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