blob: 504b3f143930b72af5b002ae496f1e97247d0d85 [file] [log] [blame]
/* 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