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