/*
 * Copyright (c) 2015 Cray Inc. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef GNIX_PRIV_H_
#define GNIX_PRIV_H_

#include "gnix.h"

/*
 * Start of code pulled from gni_priv.h
 */
inline static uint8_t gni_crc_bits(uint8_t data)
{
  uint8_t lcrc = 0;

  if(data & 1)
    lcrc ^= 0x5e;
  if(data & 2)
    lcrc ^= 0xbc;
  if(data & 4)
    lcrc ^= 0x61;
  if(data & 8)
    lcrc ^= 0xc2;
  if(data & 0x10)
    lcrc ^= 0x9d;
  if(data & 0x20)
    lcrc ^= 0x23;
  if(data & 0x40)
    lcrc ^= 0x46;
  if(data & 0x80)
    lcrc ^= 0x8c;

  return lcrc;
}

inline static uint8_t gni_memhndl_calc_crc(gni_mem_handle_t *memhndl)
{
        uint64_t qw1 = memhndl->qword1;
        uint64_t qw2 = memhndl->qword2;
        uint8_t  crc = 0;
        crc  = gni_crc_bits((qw1 ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 8) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 16) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 24) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 32) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 40) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 48) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw1 >> 56) ^ crc)&0xff);
        crc  = gni_crc_bits((qw2 ^ crc)&0xff);
        crc  = gni_crc_bits(((qw2 >> 8) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw2 >> 16) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw2 >> 24) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw2 >> 32) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw2 >> 40) ^ crc)&0xff);
        crc  = gni_crc_bits(((qw2 >> 48) ^ crc)&0xff);

        return crc;
}

typedef struct gni_mem_hndl_v1 {
        struct {
                uint64_t va: 52;
                uint64_t mdh: 12;
        };
        struct {
                uint64_t npages: 28;
                uint64_t pgsize: 6;
                uint64_t flags: 8;
                uint64_t unused: 14;
                uint64_t crc: 8;
        };
} gni_mem_hndl_v1_t;
typedef struct gni_mem_hndl_v2 {
        union {
                struct {
                        uint64_t va: 52;
                        uint64_t entropy: 12;
                };
                uint64_t id;
        };
        struct {
                uint64_t npages: 28;
                uint64_t pgsize: 6;
                uint64_t flags: 8;
                uint64_t mdh: 12;
                uint64_t unused: 2;
                uint64_t crc: 8;
        };
} gni_mem_hndl_v2_t;

/*************** Memory Handle ****************/
/* Flags (8 bits)*/
#define GNI_MEMHNDL_FLAG_READONLY       0x01UL /* Memory is not writable */
#define GNI_MEMHNDL_FLAG_VMDH           0x02UL /* Mapped via virtual MDH table */
#define GNI_MEMHNDL_FLAG_MRT            0x04UL /* MRT was used for mapping */
#define GNI_MEMHNDL_FLAG_GART           0x08UL /* GART was used for mapping */
#define GNI_MEMHNDL_FLAG_IOMMU          0x10UL /* IOMMU was used for mapping */
#define GNI_MEMHNDL_FLAG_PCI_IOMMU      0x20UL /* PCI IOMMU was used for mapping */
#define GNI_MEMHNDL_FLAG_CLONE          0x40UL /* Registration cloned from a master MDD */
#define GNI_MEMHNDL_FLAG_NEW_FRMT       0x80UL /* Used to support MDD sharing */
/* Memory Handle manipulations  */
#define GNI_MEMHNDL_INIT(memhndl) do {memhndl.qword1 = 0; memhndl.qword2 = 0;} while(0)
/* Support macros, 34 is the offset of the flags value */
#define GNI_MEMHNDL_NEW_FRMT(memhndl) ((memhndl.qword2 >> 34) & GNI_MEMHNDL_FLAG_NEW_FRMT)
#define GNI_MEMHNDL_FRMT_SET(memhndl, val, value)           \
        if (GNI_MEMHNDL_NEW_FRMT(memhndl)) {                \
                uint64_t tmp = value;                       \
                ((gni_mem_hndl_v2_t *)&memhndl)->val = tmp; \
        } else {                                            \
                uint64_t tmp = value;                       \
                ((gni_mem_hndl_v1_t *)&memhndl)->val = tmp; \
        }

#define GNI_MEMHNDL_FRMT_GET(memhndl, val) \
        ((uint64_t)(GNI_MEMHNDL_NEW_FRMT(memhndl) ? ((gni_mem_hndl_v2_t *)&memhndl)->val : ((gni_mem_hndl_v1_t *)&memhndl)->val))

/* Differing locations for V1 and V2 mem handles */
#define GNI_MEMHNDL_SET_VA(memhndl, value)  GNI_MEMHNDL_FRMT_SET(memhndl, va, (value) >> 12)
#define GNI_MEMHNDL_GET_VA(memhndl)         (GNI_MEMHNDL_FRMT_GET(memhndl, va) << 12)
#define GNI_MEMHNDL_SET_MDH(memhndl, value) GNI_MEMHNDL_FRMT_SET(memhndl, mdh, value)
#define GNI_MEMHNDL_GET_MDH(memhndl)        GNI_MEMHNDL_FRMT_GET(memhndl, mdh)


/* The MDH field size is the same, and there is no other define to
 * limit max MDHs in uGNI. */

#define GNI_MEMHNDL_MDH_MASK    0xFFFUL

/* From this point forward, there is no difference. We don't need the
 * inlined conditionals */

/* Number of Registered pages (1TB for 4kB pages): QWORD2[27:0] */
#define GNI_MEMHNDL_NPGS_MASK   0xFFFFFFFUL
#define GNI_MEMHNDL_SET_NPAGES(memhndl, value) memhndl.qword2 |= (value & GNI_MEMHNDL_NPGS_MASK)
/* Page size that was used to calculate the total number of pages : QWORD2[33:28] */
#define GNI_MEMHNDL_PSIZE_MASK  0x3FUL
#define GNI_MEMHNDL_SET_PAGESIZE(memhndl, value) memhndl.qword2 |= (((uint64_t)value & GNI_MEMHNDL_PSIZE_MASK) << 28)
/* Flags: QWORD2[41:34] */
#define GNI_MEMHNDL_FLAGS_MASK  0xFFUL
#define GNI_MEMHNDL_SET_FLAGS(memhndl, value) memhndl.qword2 |= ((value & GNI_MEMHNDL_FLAGS_MASK) << 34)
#define GNI_MEMHNDL_GET_FLAGS(memhndl) ((memhndl.qword2 >> 34) & GNI_MEMHNDL_FLAGS_MASK)
/* QWORD2[55:54] left blank */
/* CRC to verify integrity of the handle: QWORD2[63:56] ( Call this only after all other field are set!)*/
#define GNI_MEMHNDL_CRC_MASK 0xFFUL
#define GNI_MEMHNDL_SET_CRC(memhndl) (memhndl.qword2 |= ((uint64_t)gni_memhndl_calc_crc(&memhndl)<<56))

/*
 * End of code pulled from gni_priv.h
 */

#endif /* GNIX_PRIV_H_ */
