/*
 * Channel IO definitions
 *
 * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
 *
 * Inspired by various s390 headers in Linux 3.9.
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or (at
 * your option) any later version. See the COPYING file in the top-level
 * directory.
 */

#ifndef CIO_H
#define CIO_H

/*
 * path management control word
 */
struct pmcw {
    u32 intparm;        /* interruption parameter */
    u32 qf:1;           /* qdio facility */
    u32 w:1;
    u32 isc:3;          /* interruption subclass */
    u32 res5:3;         /* reserved zeros */
    u32 ena:1;          /* enabled */
    u32 lm:2;           /* limit mode */
    u32 mme:2;          /* measurement-mode enable */
    u32 mp:1;           /* multipath mode */
    u32 tf:1;           /* timing facility */
    u32 dnv:1;          /* device number valid */
    u32 dev:16;         /* device number */
    u8  lpm;            /* logical path mask */
    u8  pnom;           /* path not operational mask */
    u8  lpum;           /* last path used mask */
    u8  pim;            /* path installed mask */
    u16 mbi;            /* measurement-block index */
    u8  pom;            /* path operational mask */
    u8  pam;            /* path available mask */
    u8  chpid[8];       /* CHPID 0-7 (if available) */
    u32 unused1:8;      /* reserved zeros */
    u32 st:3;           /* subchannel type */
    u32 unused2:18;     /* reserved zeros */
    u32 mbfc:1;         /* measurement block format control */
    u32 xmwme:1;        /* extended measurement word mode enable */
    u32 csense:1;       /* concurrent sense; can be enabled ...*/
                        /*  ... per MSCH, however, if facility */
                        /*  ... is not installed, this results */
                        /*  ... in an operand exception.       */
} __attribute__ ((packed));

/* Target SCHIB configuration. */
struct schib_config {
    u64 mba;
    u32 intparm;
    u16 mbi;
    u32 isc:3;
    u32 ena:1;
    u32 mme:2;
    u32 mp:1;
    u32 csense:1;
    u32 mbfc:1;
} __attribute__ ((packed));

struct scsw {
    u16 flags;
    u16 ctrl;
    u32 cpa;
    u8 dstat;
    u8 cstat;
    u16 count;
} __attribute__ ((packed));

/* Function Control */
#define SCSW_FCTL_START_FUNC 0x4000
#define SCSW_FCTL_HALT_FUNC 0x2000
#define SCSW_FCTL_CLEAR_FUNC 0x1000

/* Activity Control */
#define SCSW_ACTL_RESUME_PEND   0x0800
#define SCSW_ACTL_START_PEND    0x0400
#define SCSW_ACTL_HALT_PEND     0x0200
#define SCSW_ACTL_CLEAR_PEND    0x0100
#define SCSW_ACTL_CH_ACTIVE     0x0080
#define SCSW_ACTL_DEV_ACTIVE    0x0040
#define SCSW_ACTL_SUSPENDED     0x0020

/* Status Control */
#define SCSW_SCTL_ALERT         0x0010
#define SCSW_SCTL_INTERMED      0x0008
#define SCSW_SCTL_PRIMARY       0x0004
#define SCSW_SCTL_SECONDARY     0x0002
#define SCSW_SCTL_STATUS_PEND   0x0001

/* SCSW Device Status Flags */
#define SCSW_DSTAT_ATTN     0x80
#define SCSW_DSTAT_STATMOD  0x40
#define SCSW_DSTAT_CUEND    0x20
#define SCSW_DSTAT_BUSY     0x10
#define SCSW_DSTAT_CHEND    0x08
#define SCSW_DSTAT_DEVEND   0x04
#define SCSW_DSTAT_UCHK     0x02
#define SCSW_DSTAT_UEXCP    0x01

/* SCSW Subchannel Status Flags */
#define SCSW_CSTAT_PCINT    0x80
#define SCSW_CSTAT_BADLEN   0x40
#define SCSW_CSTAT_PROGCHK  0x20
#define SCSW_CSTAT_PROTCHK  0x10
#define SCSW_CSTAT_CHDCHK   0x08
#define SCSW_CSTAT_CHCCHK   0x04
#define SCSW_CSTAT_ICCHK    0x02
#define SCSW_CSTAT_CHAINCHK 0x01

/*
 * subchannel information block
 */
typedef struct schib {
    struct pmcw pmcw;     /* path management control word */
    struct scsw scsw;     /* subchannel status word */
    u64 mba;              /* measurement block address */
    u8 mda[4];            /* model dependent area */
} __attribute__ ((packed, aligned(4))) Schib;

typedef struct subchannel_id {
    union {
        struct {
            u16 cssid:8;
            u16 reserved:4;
            u16 m:1;
            u16 ssid:2;
            u16 one:1;
        };
        u16 sch_id;
    };
    u16 sch_no;
} __attribute__ ((packed, aligned(4))) SubChannelId;

struct chsc_header {
    u16 length;
    u16 code;
} __attribute__((packed));

typedef struct chsc_area_sda {
    struct chsc_header request;
    u8 reserved1:4;
    u8 format:4;
    u8 reserved2;
    u16 operation_code;
    u32 reserved3;
    u32 reserved4;
    u32 operation_data_area[252];
    struct chsc_header response;
    u32 reserved5:4;
    u32 format2:4;
    u32 reserved6:24;
} __attribute__((packed)) ChscAreaSda;

/*
 * TPI info structure
 */
struct tpi_info {
    struct subchannel_id schid;
    u32 intparm;      /* interruption parameter */
    u32 adapter_IO:1;
    u32 reserved2:1;
    u32 isc:3;
    u32 reserved3:12;
    u32 int_type:3;
    u32 reserved4:12;
} __attribute__ ((packed, aligned(4)));

/* channel command word (format 0) */
typedef struct ccw0 {
    u8 cmd_code;
    u32 cda:24;
    u32 chainData:1;
    u32 chain:1;
    u32 sli:1;
    u32 skip:1;
    u32 pci:1;
    u32 ida:1;
    u32 suspend:1;
    u32 mida:1;
    u8 reserved;
    u16 count;
} __attribute__ ((packed, aligned(8))) Ccw0;

/* channel command word (format 1) */
typedef struct ccw1 {
    u8 cmd_code;
    u8 flags;
    u16 count;
    u32 cda;
} __attribute__ ((packed, aligned(8))) Ccw1;

/* do_cio() CCW formats */
#define CCW_FMT0                 0x00
#define CCW_FMT1                 0x01

#define CCW_FLAG_DC              0x80
#define CCW_FLAG_CC              0x40
#define CCW_FLAG_SLI             0x20
#define CCW_FLAG_SKIP            0x10
#define CCW_FLAG_PCI             0x08
#define CCW_FLAG_IDA             0x04
#define CCW_FLAG_SUSPEND         0x02

/* Common CCW commands */
#define CCW_CMD_READ_IPL         0x02
#define CCW_CMD_NOOP             0x03
#define CCW_CMD_BASIC_SENSE      0x04
#define CCW_CMD_TIC              0x08
#define CCW_CMD_SENSE_ID         0xe4

/* Virtio CCW commands */
#define CCW_CMD_SET_VQ           0x13
#define CCW_CMD_VDEV_RESET       0x33
#define CCW_CMD_READ_FEAT        0x12
#define CCW_CMD_WRITE_FEAT       0x11
#define CCW_CMD_READ_CONF        0x22
#define CCW_CMD_WRITE_CONF       0x21
#define CCW_CMD_WRITE_STATUS     0x31
#define CCW_CMD_SET_IND          0x43
#define CCW_CMD_SET_CONF_IND     0x53
#define CCW_CMD_READ_VQ_CONF     0x32

/* DASD CCW commands */
#define CCW_CMD_DASD_READ             0x06
#define CCW_CMD_DASD_SEEK             0x07
#define CCW_CMD_DASD_SEARCH_ID_EQ     0x31
#define CCW_CMD_DASD_READ_MT          0x86

/*
 * Command-mode operation request block
 */
typedef struct cmd_orb {
    u32 intparm;    /* interruption parameter */
    u32 key:4;      /* flags, like key, suspend control, etc. */
    u32 spnd:1;     /* suspend control */
    u32 res1:1;     /* reserved */
    u32 mod:1;      /* modification control */
    u32 sync:1;     /* synchronize control */
    u32 fmt:1;      /* format control */
    u32 pfch:1;     /* prefetch control */
    u32 isic:1;     /* initial-status-interruption control */
    u32 alcc:1;     /* address-limit-checking control */
    u32 ssic:1;     /* suppress-suspended-interr. control */
    u32 res2:1;     /* reserved */
    u32 c64:1;      /* IDAW/QDIO 64 bit control  */
    u32 i2k:1;      /* IDAW 2/4kB block size control */
    u32 lpm:8;      /* logical path mask */
    u32 ils:1;      /* incorrect length */
    u32 zero:6;     /* reserved zeros */
    u32 orbx:1;     /* ORB extension control */
    u32 cpa;        /* channel program address */
}  __attribute__ ((packed, aligned(4))) CmdOrb;

struct ciw {
    u8 type;
    u8 command;
    u16 count;
};

#define CU_TYPE_UNKNOWN         0x0000
#define CU_TYPE_DASD_2107       0x2107
#define CU_TYPE_VIRTIO          0x3832
#define CU_TYPE_DASD_3990       0x3990

/*
 * sense-id response buffer layout
 */
typedef struct senseid {
    /* common part */
    u8  reserved;   /* always 0x'FF' */
    u16 cu_type;    /* control unit type */
    u8  cu_model;   /* control unit model */
    u16 dev_type;   /* device type */
    u8  dev_model;  /* device model */
    u8  unused;     /* padding byte */
    /* extended part */
    struct ciw ciw[62];
}  __attribute__ ((packed, aligned(4))) SenseId;

/*
 * architected values for first sense byte - common_status. Bits 0-5 of this
 * field are common to all device types.
 */
#define SNS_STAT0_CMD_REJECT         0x80
#define SNS_STAT0_INTERVENTION_REQ   0x40
#define SNS_STAT0_BUS_OUT_CHECK      0x20
#define SNS_STAT0_EQUIPMENT_CHECK    0x10
#define SNS_STAT0_DATA_CHECK         0x08
#define SNS_STAT0_OVERRUN            0x04
#define SNS_STAT0_INCOMPL_DOMAIN     0x01

/* ECKD DASD status[0] byte */
#define SNS_STAT1_PERM_ERR           0x80
#define SNS_STAT1_INV_TRACK_FORMAT   0x40
#define SNS_STAT1_EOC                0x20
#define SNS_STAT1_MESSAGE_TO_OPER    0x10
#define SNS_STAT1_NO_REC_FOUND       0x08
#define SNS_STAT1_FILE_PROTECTED     0x04
#define SNS_STAT1_WRITE_INHIBITED    0x02
#define SNS_STAT1_IMPRECISE_END      0x01

/* ECKD DASD status[1] byte */
#define SNS_STAT2_REQ_INH_WRITE      0x80
#define SNS_STAT2_CORRECTABLE        0x40
#define SNS_STAT2_FIRST_LOG_ERR      0x20
#define SNS_STAT2_ENV_DATA_PRESENT   0x10
#define SNS_STAT2_IMPRECISE_END      0x04

/* ECKD DASD 24-byte Sense fmt_msg codes */
#define SENSE24_FMT_PROG_SYS    0x0
#define SENSE24_FMT_EQUIPMENT   0x2
#define SENSE24_FMT_CONTROLLER  0x3
#define SENSE24_FMT_MISC        0xF

/* basic sense response buffer layout */
typedef struct SenseDataEckdDasd {
    uint8_t common_status;
    uint8_t status[2];
    uint8_t res_count;
    uint8_t phys_drive_id;
    uint8_t low_cyl_addr;
    uint8_t head_high_cyl_addr;
    uint8_t fmt_msg;
    uint64_t fmt_dependent_info[2];
    uint8_t reserved;
    uint8_t program_action_code;
    uint16_t config_info;
    uint8_t mcode_hicyl;
    uint8_t cyl_head_addr[3];
}  __attribute__ ((packed, aligned(4))) SenseDataEckdDasd;

#define ECKD_SENSE24_GET_FMT(sd)     (sd->fmt_msg & 0xF0 >> 4)
#define ECKD_SENSE24_GET_MSG(sd)     (sd->fmt_msg & 0x0F)

#define unit_check(irb)         ((irb)->scsw.dstat & SCSW_DSTAT_UCHK)
#define iface_ctrl_check(irb)   ((irb)->scsw.cstat & SCSW_CSTAT_ICCHK)

/* interruption response block */
typedef struct irb {
    struct scsw scsw;
    u32 esw[5];
    u32 ecw[8];
    u32 emw[8];
}  __attribute__ ((packed, aligned(4))) Irb;

/* Used for SEEK ccw commands */
typedef struct CcwSeekData {
    uint16_t reserved;
    uint16_t cyl;
    uint16_t head;
} __attribute__((packed)) CcwSeekData;

/* Used for SEARCH ID ccw commands */
typedef struct CcwSearchIdData {
    uint16_t cyl;
    uint16_t head;
    uint8_t record;
} __attribute__((packed)) CcwSearchIdData;

int enable_mss_facility(void);
void enable_subchannel(SubChannelId schid);
uint16_t cu_type(SubChannelId schid);
int basic_sense(SubChannelId schid, uint16_t cutype, void *sense_data,
                 uint16_t data_size);
int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt);

/*
 * Some S390 specific IO instructions as inline
 */

static inline int stsch_err(struct subchannel_id schid, struct schib *addr)
{
    register struct subchannel_id reg1 asm ("1") = schid;
    int ccode = -EIO;

    asm volatile(
        "    stsch    0(%3)\n"
        "0:  ipm    %0\n"
        "    srl    %0,28\n"
        "1:\n"
        : "+d" (ccode), "=m" (*addr)
        : "d" (reg1), "a" (addr)
        : "cc");
    return ccode;
}

static inline int msch(struct subchannel_id schid, struct schib *addr)
{
    register struct subchannel_id reg1 asm ("1") = schid;
    int ccode;

    asm volatile(
        "    msch    0(%2)\n"
        "    ipm    %0\n"
        "    srl    %0,28"
        : "=d" (ccode)
        : "d" (reg1), "a" (addr), "m" (*addr)
        : "cc");
    return ccode;
}

static inline int msch_err(struct subchannel_id schid, struct schib *addr)
{
    register struct subchannel_id reg1 asm ("1") = schid;
    int ccode = -EIO;

    asm volatile(
        "    msch    0(%2)\n"
        "0:  ipm    %0\n"
        "    srl    %0,28\n"
        "1:\n"
        : "+d" (ccode)
        : "d" (reg1), "a" (addr), "m" (*addr)
        : "cc");
    return ccode;
}

static inline int tsch(struct subchannel_id schid, struct irb *addr)
{
    register struct subchannel_id reg1 asm ("1") = schid;
    int ccode;

    asm volatile(
        "    tsch    0(%3)\n"
        "    ipm    %0\n"
        "    srl    %0,28"
        : "=d" (ccode), "=m" (*addr)
        : "d" (reg1), "a" (addr)
        : "cc");
    return ccode;
}

static inline int ssch(struct subchannel_id schid, struct cmd_orb *addr)
{
    register struct subchannel_id reg1 asm("1") = schid;
    int ccode = -EIO;

    asm volatile(
        "    ssch    0(%2)\n"
        "0:  ipm    %0\n"
        "    srl    %0,28\n"
        "1:\n"
        : "+d" (ccode)
        : "d" (reg1), "a" (addr), "m" (*addr)
        : "cc", "memory");
    return ccode;
}

static inline int csch(struct subchannel_id schid)
{
    register struct subchannel_id reg1 asm("1") = schid;
    int ccode;

    asm volatile(
        "    csch\n"
        "    ipm    %0\n"
        "    srl    %0,28"
        : "=d" (ccode)
        : "d" (reg1)
        : "cc");
    return ccode;
}

static inline int tpi(struct tpi_info *addr)
{
    int ccode;

    asm volatile(
        "    tpi    0(%2)\n"
        "    ipm    %0\n"
        "    srl    %0,28"
        : "=d" (ccode), "=m" (*addr)
        : "a" (addr)
        : "cc");
    return ccode;
}

static inline int chsc(void *chsc_area)
{
    typedef struct { char _[4096]; } addr_type;
    int cc;

    asm volatile(
        "    .insn    rre,0xb25f0000,%2,0\n"
        "    ipm    %0\n"
        "    srl    %0,28\n"
        : "=d" (cc), "=m" (*(addr_type *) chsc_area)
        : "d" (chsc_area), "m" (*(addr_type *) chsc_area)
        : "cc");
    return cc;
}

#endif /* CIO_H */
