1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Channel Subsystem tests library 4 * 5 * Copyright (c) 2020 IBM Corp 6 * 7 * Authors: 8 * Pierre Morel <pmorel@linux.ibm.com> 9 */ 10 #include <libcflat.h> 11 #include <alloc_phys.h> 12 #include <asm/page.h> 13 #include <string.h> 14 #include <interrupt.h> 15 #include <asm/arch_def.h> 16 #include <asm/time.h> 17 #include <asm/arch_def.h> 18 19 #include <malloc_io.h> 20 #include <css.h> 21 22 static struct schib schib; 23 24 /* 25 * css_enumerate: 26 * On success return the first subchannel ID found. 27 * On error return an invalid subchannel ID containing cc 28 */ 29 int css_enumerate(void) 30 { 31 struct pmcw *pmcw = &schib.pmcw; 32 int scn_found = 0; 33 int dev_found = 0; 34 int schid = 0; 35 int cc; 36 int scn; 37 38 for (scn = 0; scn < 0xffff; scn++) { 39 cc = stsch(scn | SCHID_ONE, &schib); 40 switch (cc) { 41 case 0: /* 0 means SCHIB stored */ 42 break; 43 case 3: /* 3 means no more channels */ 44 goto out; 45 default: /* 1 or 2 should never happen for STSCH */ 46 report_abort("Unexpected error %d on subchannel %08x", 47 cc, scn | SCHID_ONE); 48 return cc; 49 } 50 51 /* We currently only support type 0, a.k.a. I/O channels */ 52 if (PMCW_CHANNEL_TYPE(pmcw) != 0) 53 continue; 54 55 /* We ignore I/O channels without valid devices */ 56 scn_found++; 57 if (!(pmcw->flags & PMCW_DNV)) 58 continue; 59 60 /* We keep track of the first device as our test device */ 61 if (!schid) 62 schid = scn | SCHID_ONE; 63 report_info("Found subchannel %08x", scn | SCHID_ONE); 64 dev_found++; 65 } 66 67 out: 68 report_info("Tested subchannels: %d, I/O subchannels: %d, I/O devices: %d", 69 scn, scn_found, dev_found); 70 return schid; 71 } 72 73 /* 74 * css_enable: enable the subchannel with the specified ISC 75 * @schid: Subchannel Identifier 76 * @isc : number of the interruption subclass to use 77 * Return value: 78 * On success: 0 79 * On error the CC of the faulty instruction 80 * or -1 if the retry count is exceeded. 81 */ 82 int css_enable(int schid, int isc) 83 { 84 struct pmcw *pmcw = &schib.pmcw; 85 int retry_count = 0; 86 uint16_t flags; 87 int cc; 88 89 /* Read the SCHIB for this subchannel */ 90 cc = stsch(schid, &schib); 91 if (cc) { 92 report_info("stsch: sch %08x failed with cc=%d", schid, cc); 93 return cc; 94 } 95 96 flags = PMCW_ENABLE | (isc << PMCW_ISC_SHIFT); 97 if ((pmcw->flags & (PMCW_ISC_MASK | PMCW_ENABLE)) == flags) { 98 report_info("stsch: sch %08x already enabled", schid); 99 return 0; 100 } 101 102 retry: 103 /* Update the SCHIB to enable the channel and set the ISC */ 104 pmcw->flags &= ~PMCW_ISC_MASK; 105 pmcw->flags |= flags; 106 107 /* Tell the CSS we want to modify the subchannel */ 108 cc = msch(schid, &schib); 109 if (cc) { 110 /* 111 * If the subchannel is status pending or 112 * if a function is in progress, 113 * we consider both cases as errors. 114 */ 115 report_info("msch: sch %08x failed with cc=%d", schid, cc); 116 return cc; 117 } 118 119 /* 120 * Read the SCHIB again to verify the enablement 121 */ 122 cc = stsch(schid, &schib); 123 if (cc) { 124 report_info("stsch: updating sch %08x failed with cc=%d", 125 schid, cc); 126 return cc; 127 } 128 129 if ((pmcw->flags & flags) == flags) { 130 report_info("stsch: sch %08x successfully modified after %d retries", 131 schid, retry_count); 132 return 0; 133 } 134 135 if (retry_count++ < MAX_ENABLE_RETRIES) { 136 mdelay(10); /* the hardware was not ready, give it some time */ 137 goto retry; 138 } 139 140 report_info("msch: modifying sch %08x failed after %d retries. pmcw flags: %04x", 141 schid, retry_count, pmcw->flags); 142 return -1; 143 } 144 145 static struct irb irb; 146 147 void css_irq_io(void) 148 { 149 int ret = 0; 150 char *flags; 151 int sid; 152 153 report_prefix_push("Interrupt"); 154 sid = lowcore_ptr->subsys_id_word; 155 /* Lowlevel set the SID as interrupt parameter. */ 156 if (lowcore_ptr->io_int_param != sid) { 157 report(0, 158 "io_int_param: %x differs from subsys_id_word: %x", 159 lowcore_ptr->io_int_param, sid); 160 goto pop; 161 } 162 report_info("subsys_id_word: %08x io_int_param %08x io_int_word %08x", 163 lowcore_ptr->subsys_id_word, 164 lowcore_ptr->io_int_param, 165 lowcore_ptr->io_int_word); 166 report_prefix_pop(); 167 168 report_prefix_push("tsch"); 169 ret = tsch(sid, &irb); 170 switch (ret) { 171 case 1: 172 dump_irb(&irb); 173 flags = dump_scsw_flags(irb.scsw.ctrl); 174 report(0, 175 "I/O interrupt, but tsch returns CC 1 for subchannel %08x. SCSW flags: %s", 176 sid, flags); 177 break; 178 case 2: 179 report(0, "tsch returns unexpected CC 2"); 180 break; 181 case 3: 182 report(0, "tsch reporting sch %08x as not operational", sid); 183 break; 184 case 0: 185 /* Stay humble on success */ 186 break; 187 } 188 pop: 189 report_prefix_pop(); 190 lowcore_ptr->io_old_psw.mask &= ~PSW_MASK_WAIT; 191 } 192 193 int start_ccw1_chain(unsigned int sid, struct ccw1 *ccw) 194 { 195 struct orb orb = { 196 .intparm = sid, 197 .ctrl = ORB_CTRL_ISIC|ORB_CTRL_FMT|ORB_LPM_DFLT, 198 .cpa = (unsigned int) (unsigned long)ccw, 199 }; 200 201 return ssch(sid, &orb); 202 } 203 204 struct ccw1 *ccw_alloc(int code, void *data, int count, unsigned char flags) 205 { 206 struct ccw1 *ccw; 207 208 ccw = alloc_io_mem(sizeof(*ccw), 0); 209 if (!ccw) 210 return NULL; 211 212 ccw->code = code; 213 ccw->flags = flags; 214 ccw->count = count; 215 ccw->data_address = (int)(unsigned long)data; 216 217 return ccw; 218 } 219 220 /* wait_and_check_io_completion: 221 * @schid: the subchannel ID 222 * 223 * Makes the most common check to validate a successful I/O 224 * completion. 225 * Only report failures. 226 */ 227 int wait_and_check_io_completion(int schid) 228 { 229 int ret = 0; 230 231 wait_for_interrupt(PSW_MASK_IO); 232 233 report_prefix_push("check I/O completion"); 234 235 if (lowcore_ptr->io_int_param != schid) { 236 report(0, "interrupt parameter: expected %08x got %08x", 237 schid, lowcore_ptr->io_int_param); 238 ret = -1; 239 goto end; 240 } 241 242 /* Verify that device status is valid */ 243 if (!(irb.scsw.ctrl & SCSW_SC_PENDING)) { 244 report(0, "No status pending after interrupt. Subch Ctrl: %08x", 245 irb.scsw.ctrl); 246 ret = -1; 247 goto end; 248 } 249 250 if (!(irb.scsw.ctrl & (SCSW_SC_SECONDARY | SCSW_SC_PRIMARY))) { 251 report(0, "Primary or secondary status missing. Subch Ctrl: %08x", 252 irb.scsw.ctrl); 253 ret = -1; 254 goto end; 255 } 256 257 if (!(irb.scsw.dev_stat & (SCSW_DEVS_DEV_END | SCSW_DEVS_SCH_END))) { 258 report(0, "No device end or sch end. Dev. status: %02x", 259 irb.scsw.dev_stat); 260 ret = -1; 261 goto end; 262 } 263 264 if (irb.scsw.sch_stat & ~SCSW_SCHS_IL) { 265 report_info("Unexpected Subch. status %02x", irb.scsw.sch_stat); 266 ret = -1; 267 goto end; 268 } 269 270 end: 271 report_prefix_pop(); 272 return ret; 273 } 274 275 /* 276 * css_residual_count 277 * Return the residual count, if it is valid. 278 * 279 * Return value: 280 * Success: the residual count 281 * Not meaningful: -1 (-1 can not be a valid count) 282 */ 283 int css_residual_count(unsigned int schid) 284 { 285 286 if (!(irb.scsw.ctrl & (SCSW_SC_PENDING | SCSW_SC_PRIMARY))) 287 return -1; 288 289 if (irb.scsw.dev_stat) 290 if (irb.scsw.sch_stat & ~(SCSW_SCHS_PCI | SCSW_SCHS_IL)) 291 return -1; 292 293 return irb.scsw.count; 294 } 295 296 /* 297 * enable_io_isc: setup ISC in Control Register 6 298 * @isc: The interruption Sub Class as a bitfield 299 */ 300 void enable_io_isc(uint8_t isc) 301 { 302 uint64_t value; 303 304 value = (uint64_t)isc << 24; 305 lctlg(6, value); 306 } 307