root/drivers/s390/net/ism.h
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef S390_ISM_H
#define S390_ISM_H

#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/dibs.h>
#include <asm/pci_insn.h>

#define UTIL_STR_LEN    16
#define ISM_ERROR       0xFFFF

#define ISM_NR_DMBS     1920

/*
 * Do not use the first word of the DMB bits to ensure 8 byte aligned access.
 */
#define ISM_DMB_WORD_OFFSET     1
#define ISM_DMB_BIT_OFFSET      (ISM_DMB_WORD_OFFSET * 32)

#define ISM_REG_SBA     0x1
#define ISM_REG_IEQ     0x2
#define ISM_READ_GID    0x3
#define ISM_ADD_VLAN_ID 0x4
#define ISM_DEL_VLAN_ID 0x5
#define ISM_SET_VLAN    0x6
#define ISM_RESET_VLAN  0x7
#define ISM_QUERY_INFO  0x8
#define ISM_QUERY_RGID  0x9
#define ISM_REG_DMB     0xA
#define ISM_UNREG_DMB   0xB
#define ISM_SIGNAL_IEQ  0xE
#define ISM_UNREG_SBA   0x11
#define ISM_UNREG_IEQ   0x12

enum ism_event_type {
        ISM_EVENT_BUF = 0x00,
        ISM_EVENT_DEV = 0x01,
        ISM_EVENT_SWR = 0x02
};

enum ism_event_code {
        ISM_BUF_DMB_UNREGISTERED = 0x04,
        ISM_BUF_USING_ISM_DEV_DISABLED = 0x08,
        ISM_BUF_OWNING_ISM_DEV_IN_ERR_STATE = 0x02,
        ISM_BUF_USING_ISM_DEV_IN_ERR_STATE = 0x03,
        ISM_BUF_VLAN_MISMATCH_WITH_OWNER = 0x05,
        ISM_BUF_VLAN_MISMATCH_WITH_USER = 0x06,
        ISM_DEV_GID_DISABLED = 0x07,
        ISM_DEV_GID_ERR_STATE = 0x01
};

struct ism_req_hdr {
        u32 cmd;
        u16 : 16;
        u16 len;
};

struct ism_resp_hdr {
        u32 cmd;
        u16 ret;
        u16 len;
};

union ism_reg_sba {
        struct {
                struct ism_req_hdr hdr;
                u64 sba;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(16);

union ism_reg_ieq {
        struct {
                struct ism_req_hdr hdr;
                u64 ieq;
                u64 len;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(16);

/* ISM-vPCI devices provide 64 Bit GIDs
 * Map them to ISM UUID GIDs like this:
 *  _________________________________________
 * | 64 Bit ISM-vPCI GID | 00000000_00000000 |
 *  -----------------------------------------
 * This will be interpreted as a UIID variant, that is reserved
 * for NCS backward compatibility. So it will not collide with
 * proper UUIDs.
 */
union ism_read_gid {
        struct {
                struct ism_req_hdr hdr;
        } request;
        struct {
                struct ism_resp_hdr hdr;
                u64 gid;
        } response;
} __aligned(16);

union ism_qi {
        struct {
                struct ism_req_hdr hdr;
        } request;
        struct {
                struct ism_resp_hdr hdr;
                u32 version;
                u32 max_len;
                u64 ism_state;
                u64 my_gid;
                u64 sba;
                u64 ieq;
                u32 ieq_len;
                u32 : 32;
                u32 dmbs_owned;
                u32 dmbs_used;
                u32 vlan_required;
                u32 vlan_nr_ids;
                u16 vlan_id[64];
        } response;
} __aligned(64);

union ism_query_rgid {
        struct {
                struct ism_req_hdr hdr;
                u64 rgid;
                u32 vlan_valid;
                u32 vlan_id;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(16);

union ism_reg_dmb {
        struct {
                struct ism_req_hdr hdr;
                u64 dmb;
                u32 dmb_len;
                u32 sba_idx;
                u32 vlan_valid;
                u32 vlan_id;
                u64 rgid;
        } request;
        struct {
                struct ism_resp_hdr hdr;
                u64 dmb_tok;
        } response;
} __aligned(32);

union ism_sig_ieq {
        struct {
                struct ism_req_hdr hdr;
                u64 rgid;
                u32 trigger_irq;
                u32 event_code;
                u64 info;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(32);

union ism_unreg_dmb {
        struct {
                struct ism_req_hdr hdr;
                u64 dmb_tok;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(16);

union ism_cmd_simple {
        struct {
                struct ism_req_hdr hdr;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(8);

union ism_set_vlan_id {
        struct {
                struct ism_req_hdr hdr;
                u64 vlan_id;
        } request;
        struct {
                struct ism_resp_hdr hdr;
        } response;
} __aligned(16);

struct ism_eq_header {
        u64 idx;
        u64 ieq_len;
        u64 entry_len;
        u64 : 64;
};

struct ism_event {
        u32 type;
        u32 code;
        u64 tok;
        u64 time;
        u64 info;
};

struct ism_eq {
        struct ism_eq_header header;
        struct ism_event entry[15];
};

struct ism_sba {
        u32 s : 1;      /* summary bit */
        u32 e : 1;      /* event bit */
        u32 : 30;
        u32 dmb_bits[ISM_NR_DMBS / 32];
        u32 reserved[3];
        u16 dmbe_mask[ISM_NR_DMBS];
};

struct ism_dev {
        spinlock_t cmd_lock; /* serializes cmds */
        struct dibs_dev *dibs;
        struct pci_dev *pdev;
        struct ism_sba *sba;
        dma_addr_t sba_dma_addr;
        DECLARE_BITMAP(sba_bitmap, ISM_NR_DMBS);

        struct ism_eq *ieq;
        dma_addr_t ieq_dma_addr;
        int ieq_idx;
};

#define ISM_CREATE_REQ(dmb, idx, sf, offset)            \
        ((dmb) | (idx) << 24 | (sf) << 23 | (offset))

static inline void __ism_read_cmd(struct ism_dev *ism, void *data,
                                  unsigned long offset, unsigned long len)
{
        struct zpci_dev *zdev = to_zpci(ism->pdev);
        u64 req = ZPCI_CREATE_REQ(zdev->fh, 2, 8);

        while (len > 0) {
                __zpci_load(data, req, offset);
                offset += 8;
                data += 8;
                len -= 8;
        }
}

static inline void __ism_write_cmd(struct ism_dev *ism, void *data,
                                   unsigned long offset, unsigned long len)
{
        struct zpci_dev *zdev = to_zpci(ism->pdev);
        u64 req = ZPCI_CREATE_REQ(zdev->fh, 2, len);

        if (len)
                __zpci_store_block(data, req, offset);
}

static inline int __ism_move(struct ism_dev *ism, u64 dmb_req, void *data,
                             unsigned int size)
{
        struct zpci_dev *zdev = to_zpci(ism->pdev);
        u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, size);

        return __zpci_store_block(data, req, dmb_req);
}

#endif /* S390_ISM_H */