| /* SPDX-License-Identifier: GPL-2.0-only */ | |
| /* | |
| * CSS definitions | |
| * | |
| * Copyright IBM Corp. 2020 | |
| * Author: Pierre Morel <pmorel@linux.ibm.com> | |
| */ | |
| #ifndef _S390X_CSS_H_ | |
| #define _S390X_CSS_H_ | |
| /* subchannel ID bit 16 must always be one */ | |
| #define SCHID_ONE 0x00010000 | |
| #define CCW_F_CD 0x80 | |
| #define CCW_F_CC 0x40 | |
| #define CCW_F_SLI 0x20 | |
| #define CCW_F_SKP 0x10 | |
| #define CCW_F_PCI 0x08 | |
| #define CCW_F_IDA 0x04 | |
| #define CCW_F_S 0x02 | |
| #define CCW_F_MIDA 0x01 | |
| #define CCW_C_NOP 0x03 | |
| #define CCW_C_TIC 0x08 | |
| struct ccw1 { | |
| uint8_t code; | |
| uint8_t flags; | |
| uint16_t count; | |
| uint32_t data_address; | |
| } __attribute__ ((aligned(8))); | |
| #define ORB_CTRL_KEY 0xf0000000 | |
| #define ORB_CTRL_SPND 0x08000000 | |
| #define ORB_CTRL_STR 0x04000000 | |
| #define ORB_CTRL_MOD 0x02000000 | |
| #define ORB_CTRL_SYNC 0x01000000 | |
| #define ORB_CTRL_FMT 0x00800000 | |
| #define ORB_CTRL_PFCH 0x00400000 | |
| #define ORB_CTRL_ISIC 0x00200000 | |
| #define ORB_CTRL_ALCC 0x00100000 | |
| #define ORB_CTRL_SSIC 0x00080000 | |
| #define ORB_CTRL_CPTC 0x00040000 | |
| #define ORB_CTRL_C64 0x00020000 | |
| #define ORB_CTRL_I2K 0x00010000 | |
| #define ORB_CTRL_LPM 0x0000ff00 | |
| #define ORB_CTRL_ILS 0x00000080 | |
| #define ORB_CTRL_MIDAW 0x00000040 | |
| #define ORB_CTRL_ORBX 0x00000001 | |
| #define ORB_LPM_DFLT 0x00008000 | |
| struct orb { | |
| uint32_t intparm; | |
| uint32_t ctrl; | |
| uint32_t cpa; | |
| uint32_t prio; | |
| uint32_t reserved[4]; | |
| } __attribute__ ((aligned(4))); | |
| struct scsw { | |
| #define SCSW_SC_PENDING 0x00000001 | |
| #define SCSW_SC_SECONDARY 0x00000002 | |
| #define SCSW_SC_PRIMARY 0x00000004 | |
| #define SCSW_SC_INTERMEDIATE 0x00000008 | |
| #define SCSW_SC_ALERT 0x00000010 | |
| uint32_t ctrl; | |
| uint32_t ccw_addr; | |
| #define SCSW_DEVS_DEV_END 0x04 | |
| #define SCSW_DEVS_SCH_END 0x08 | |
| uint8_t dev_stat; | |
| #define SCSW_SCHS_PCI 0x80 | |
| #define SCSW_SCHS_IL 0x40 | |
| uint8_t sch_stat; | |
| uint16_t count; | |
| }; | |
| struct pmcw { | |
| uint32_t intparm; | |
| #define PMCW_DNV 0x0001 | |
| #define PMCW_ENABLE 0x0080 | |
| #define PMCW_MBUE 0x0010 | |
| #define PMCW_DCTME 0x0008 | |
| #define PMCW_ISC_MASK 0x3800 | |
| #define PMCW_ISC_SHIFT 11 | |
| uint16_t flags; | |
| uint16_t devnum; | |
| uint8_t lpm; | |
| uint8_t pnom; | |
| uint8_t lpum; | |
| uint8_t pim; | |
| uint16_t mbi; | |
| uint8_t pom; | |
| uint8_t pam; | |
| uint8_t chpid[8]; | |
| #define PMCW_MBF1 0x0004 | |
| uint32_t flags2; | |
| }; | |
| #define PMCW_CHANNEL_TYPE(pmcw) (pmcw->flags2 >> 21) | |
| struct schib { | |
| struct pmcw pmcw; | |
| struct scsw scsw; | |
| uint64_t mbo; | |
| uint8_t md[4]; | |
| } __attribute__ ((aligned(4))); | |
| extern struct schib schib; | |
| struct irb { | |
| struct scsw scsw; | |
| uint32_t esw[5]; | |
| uint32_t ecw[8]; | |
| uint32_t emw[8]; | |
| } __attribute__ ((aligned(4))); | |
| #define CCW_CMD_SENSE_ID 0xe4 | |
| #define CSS_SENSEID_COMMON_LEN 8 | |
| struct senseid { | |
| /* common part */ | |
| uint8_t reserved; /* always 0x'FF' */ | |
| uint16_t cu_type; /* control unit type */ | |
| uint8_t cu_model; /* control unit model */ | |
| uint16_t dev_type; /* device type */ | |
| uint8_t dev_model; /* device model */ | |
| uint8_t unused; /* padding byte */ | |
| uint8_t padding[256 - 8]; /* Extended part */ | |
| } __attribute__ ((aligned(4))) __attribute__ ((packed)); | |
| /* CSS low level access functions */ | |
| static inline int ssch(unsigned long schid, struct orb *addr) | |
| { | |
| register long long reg1 asm("1") = schid; | |
| int cc; | |
| asm volatile( | |
| " ssch 0(%2)\n" | |
| " ipm %0\n" | |
| " srl %0,28\n" | |
| : "=d" (cc) | |
| : "d" (reg1), "a" (addr), "m" (*addr) | |
| : "cc", "memory"); | |
| return cc; | |
| } | |
| static inline int stsch(unsigned long schid, struct schib *addr) | |
| { | |
| register unsigned long reg1 asm ("1") = schid; | |
| uint64_t bogus_cc = 1; | |
| int cc; | |
| asm volatile( | |
| " tmll %[bogus_cc],3\n" | |
| " stsch 0(%3)\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc), "=m" (*addr) | |
| : "d" (reg1), "a" (addr), [bogus_cc] "d" (bogus_cc) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int msch(unsigned long schid, struct schib *addr) | |
| { | |
| register unsigned long reg1 asm ("1") = schid; | |
| int cc; | |
| asm volatile( | |
| " msch 0(%3)\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc) | |
| : "d" (reg1), "m" (*addr), "a" (addr) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int tsch(unsigned long schid, struct irb *addr) | |
| { | |
| register unsigned long reg1 asm ("1") = schid; | |
| uint64_t bogus_cc = 2; | |
| int cc; | |
| asm volatile( | |
| " tmll %[bogus_cc],3\n" | |
| " tsch 0(%3)\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc), "=m" (*addr) | |
| : "d" (reg1), "a" (addr), [bogus_cc] "d" (bogus_cc) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int hsch(unsigned long schid) | |
| { | |
| register unsigned long reg1 asm("1") = schid; | |
| int cc; | |
| asm volatile( | |
| " hsch\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc) | |
| : "d" (reg1) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int xsch(unsigned long schid) | |
| { | |
| register unsigned long reg1 asm("1") = schid; | |
| int cc; | |
| asm volatile( | |
| " xsch\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc) | |
| : "d" (reg1) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int csch(unsigned long schid) | |
| { | |
| register unsigned long reg1 asm("1") = schid; | |
| int cc; | |
| asm volatile( | |
| " csch\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc) | |
| : "d" (reg1) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int rsch(unsigned long schid) | |
| { | |
| register unsigned long reg1 asm("1") = schid; | |
| int cc; | |
| asm volatile( | |
| " rsch\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc) | |
| : "d" (reg1) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int rchp(unsigned long chpid) | |
| { | |
| register unsigned long reg1 asm("1") = chpid; | |
| uint64_t bogus_cc = 1; | |
| int cc; | |
| asm volatile( | |
| " tmll %[bogus_cc],3\n" | |
| " rchp\n" | |
| " ipm %0\n" | |
| " srl %0,28" | |
| : "=d" (cc) | |
| : "d" (reg1), [bogus_cc] "d" (bogus_cc) | |
| : "cc"); | |
| return cc; | |
| } | |
| static inline int stcrw(uint32_t *crw) | |
| { | |
| uint64_t bogus_cc = 1; | |
| int cc; | |
| asm volatile( | |
| " tmll %[bogus_cc],3\n" | |
| " stcrw %[crw]\n" | |
| " ipm %[cc]\n" | |
| " srl %[cc],28" | |
| : [cc] "=d" (cc) | |
| : [crw] "Q" (*crw), [bogus_cc] "d" (bogus_cc) | |
| : "cc", "memory"); | |
| return cc; | |
| } | |
| /* Debug functions */ | |
| char *dump_pmcw_flags(uint16_t f); | |
| char *dump_scsw_flags(uint32_t f); | |
| void dump_scsw(struct scsw *scsw); | |
| void dump_irb(struct irb *irbp); | |
| void dump_schib(struct schib *sch); | |
| struct ccw1 *dump_ccw(struct ccw1 *cp); | |
| void dump_irb(struct irb *irbp); | |
| void dump_pmcw(struct pmcw *p); | |
| void dump_orb(struct orb *op); | |
| int css_enumerate(void); | |
| #define MAX_ENABLE_RETRIES 5 | |
| #define IO_SCH_ISC 3 | |
| int css_enable(int schid, int isc); | |
| bool css_enabled(int schid); | |
| /* Library functions */ | |
| int start_ccw1_chain(unsigned int sid, struct ccw1 *ccw); | |
| struct ccw1 *ccw_alloc(int code, void *data, int count, unsigned char flags); | |
| void css_irq_io(void); | |
| int css_residual_count(unsigned int schid); | |
| void enable_io_isc(uint8_t isc); | |
| int wait_and_check_io_completion(int schid); | |
| int css_find_installed_chpid(int sid, uint8_t *chpid_out); | |
| int css_generate_crw(int sid); | |
| /* | |
| * CHSC definitions | |
| */ | |
| struct chsc_header { | |
| uint16_t len; | |
| uint16_t code; | |
| }; | |
| /* Store Channel Subsystem Characteristics */ | |
| struct chsc_scsc { | |
| struct chsc_header req; | |
| uint16_t req_fmt; | |
| uint8_t cssid; | |
| uint8_t reserved[9]; | |
| struct chsc_header res; | |
| uint32_t res_fmt; | |
| #define CSSC_EXTENDED_MEASUREMENT_BLOCK 48 | |
| uint64_t general_char[255]; | |
| uint64_t chsc_char[254]; | |
| }; | |
| extern struct chsc_scsc *chsc_scsc; | |
| #define CHSC_SCSC 0x0010 | |
| #define CHSC_SCSC_LEN 0x0010 | |
| bool get_chsc_scsc(void); | |
| #define CSS_GENERAL_FEAT_BITLEN (255 * 64) | |
| #define CSS_CHSC_FEAT_BITLEN (254 * 64) | |
| #define CHSC_SCSC 0x0010 | |
| #define CHSC_SCSC_LEN 0x0010 | |
| #define CHSC_ERROR 0x0000 | |
| #define CHSC_RSP_OK 0x0001 | |
| #define CHSC_RSP_INVAL 0x0002 | |
| #define CHSC_RSP_REQERR 0x0003 | |
| #define CHSC_RSP_ENOCMD 0x0004 | |
| #define CHSC_RSP_NODATA 0x0005 | |
| #define CHSC_RSP_SUP31B 0x0006 | |
| #define CHSC_RSP_EFRMT 0x0007 | |
| #define CHSC_RSP_ECSSID 0x0008 | |
| #define CHSC_RSP_ERFRMT 0x0009 | |
| #define CHSC_RSP_ESSID 0x000A | |
| #define CHSC_RSP_EBUSY 0x000B | |
| #define CHSC_RSP_MAX 0x000B | |
| static inline int _chsc(void *p) | |
| { | |
| int cc; | |
| asm volatile(" .insn rre,0xb25f0000,%2,0\n" | |
| " ipm %0\n" | |
| " srl %0,28\n" | |
| : "=d" (cc), "=m" (p) | |
| : "d" (p), "m" (p) | |
| : "cc"); | |
| return cc; | |
| } | |
| bool chsc(void *p, uint16_t code, uint16_t len); | |
| #include <bitops.h> | |
| #define css_test_general_feature(bit) test_bit_inv(bit, chsc_scsc->general_char) | |
| #define css_test_chsc_feature(bit) test_bit_inv(bit, chsc_scsc->chsc_char) | |
| #define SCHM_DCTM 1 /* activate Device Connection TiMe */ | |
| #define SCHM_MBU 2 /* activate Measurement Block Update */ | |
| static inline void schm(void *mbo, unsigned int flags) | |
| { | |
| register void *__gpr2 asm("2") = mbo; | |
| register long __gpr1 asm("1") = flags; | |
| asm("schm" : : "d" (__gpr2), "d" (__gpr1)); | |
| } | |
| bool css_enable_mb(int sid, uint64_t mb, uint16_t mbi, uint16_t flg, bool fmt1); | |
| bool css_disable_mb(int schid); | |
| struct measurement_block_format0 { | |
| uint16_t ssch_rsch_count; | |
| uint16_t sample_count; | |
| uint32_t device_connect_time; | |
| uint32_t function_pending_time; | |
| uint32_t device_disconnect_time; | |
| uint32_t cu_queuing_time; | |
| uint32_t device_active_only_time; | |
| uint32_t device_busy_time; | |
| uint32_t initial_cmd_resp_time; | |
| }; | |
| struct measurement_block_format1 { | |
| uint32_t ssch_rsch_count; | |
| uint32_t sample_count; | |
| uint32_t device_connect_time; | |
| uint32_t function_pending_time; | |
| uint32_t device_disconnect_time; | |
| uint32_t cu_queuing_time; | |
| uint32_t device_active_only_time; | |
| uint32_t device_busy_time; | |
| uint32_t initial_cmd_resp_time; | |
| uint32_t irq_delay_time; | |
| uint32_t irq_prio_delay_time; | |
| }; | |
| #endif |