1 /* 2 * drivers/s390/cio/device_pgid.c 3 * 4 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, 5 * IBM Corporation 6 * Author(s): Cornelia Huck(cohuck@de.ibm.com) 7 * Martin Schwidefsky (schwidefsky@de.ibm.com) 8 * 9 * Path Group ID functions. 10 */ 11 12 #include <linux/config.h> 13 #include <linux/module.h> 14 #include <linux/init.h> 15 16 #include <asm/ccwdev.h> 17 #include <asm/cio.h> 18 #include <asm/delay.h> 19 #include <asm/lowcore.h> 20 21 #include "cio.h" 22 #include "cio_debug.h" 23 #include "css.h" 24 #include "device.h" 25 #include "ioasm.h" 26 27 /* 28 * Start Sense Path Group ID helper function. Used in ccw_device_recog 29 * and ccw_device_sense_pgid. 30 */ 31 static int 32 __ccw_device_sense_pgid_start(struct ccw_device *cdev) 33 { 34 struct subchannel *sch; 35 struct ccw1 *ccw; 36 int ret; 37 38 sch = to_subchannel(cdev->dev.parent); 39 /* Setup sense path group id channel program. */ 40 ccw = cdev->private->iccws; 41 ccw->cmd_code = CCW_CMD_SENSE_PGID; 42 ccw->cda = (__u32) __pa (&cdev->private->pgid); 43 ccw->count = sizeof (struct pgid); 44 ccw->flags = CCW_FLAG_SLI; 45 46 /* Reset device status. */ 47 memset(&cdev->private->irb, 0, sizeof(struct irb)); 48 /* Try on every path. */ 49 ret = -ENODEV; 50 while (cdev->private->imask != 0) { 51 /* Try every path multiple times. */ 52 if (cdev->private->iretry > 0) { 53 cdev->private->iretry--; 54 ret = cio_start (sch, cdev->private->iccws, 55 cdev->private->imask); 56 /* ret is 0, -EBUSY, -EACCES or -ENODEV */ 57 if (ret != -EACCES) 58 return ret; 59 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel " 60 "0.%x.%04x, lpm %02X, became 'not " 61 "operational'\n", 62 cdev->private->devno, sch->schid.ssid, 63 sch->schid.sch_no, cdev->private->imask); 64 65 } 66 cdev->private->imask >>= 1; 67 cdev->private->iretry = 5; 68 } 69 return ret; 70 } 71 72 void 73 ccw_device_sense_pgid_start(struct ccw_device *cdev) 74 { 75 int ret; 76 77 cdev->private->state = DEV_STATE_SENSE_PGID; 78 cdev->private->imask = 0x80; 79 cdev->private->iretry = 5; 80 memset (&cdev->private->pgid, 0, sizeof (struct pgid)); 81 ret = __ccw_device_sense_pgid_start(cdev); 82 if (ret && ret != -EBUSY) 83 ccw_device_sense_pgid_done(cdev, ret); 84 } 85 86 /* 87 * Called from interrupt context to check if a valid answer 88 * to Sense Path Group ID was received. 89 */ 90 static int 91 __ccw_device_check_sense_pgid(struct ccw_device *cdev) 92 { 93 struct subchannel *sch; 94 struct irb *irb; 95 96 sch = to_subchannel(cdev->dev.parent); 97 irb = &cdev->private->irb; 98 if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) 99 return -ETIME; 100 if (irb->esw.esw0.erw.cons && 101 (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { 102 /* 103 * If the device doesn't support the Sense Path Group ID 104 * command further retries wouldn't help ... 105 */ 106 return -EOPNOTSUPP; 107 } 108 if (irb->esw.esw0.erw.cons) { 109 CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " 110 "lpum %02X, cnt %02d, sns : " 111 "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", 112 cdev->private->ssid, cdev->private->devno, 113 irb->esw.esw0.sublog.lpum, 114 irb->esw.esw0.erw.scnt, 115 irb->ecw[0], irb->ecw[1], 116 irb->ecw[2], irb->ecw[3], 117 irb->ecw[4], irb->ecw[5], 118 irb->ecw[6], irb->ecw[7]); 119 return -EAGAIN; 120 } 121 if (irb->scsw.cc == 3) { 122 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x," 123 " lpm %02X, became 'not operational'\n", 124 cdev->private->devno, sch->schid.ssid, 125 sch->schid.sch_no, sch->orb.lpm); 126 return -EACCES; 127 } 128 if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { 129 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " 130 "is reserved by someone else\n", 131 cdev->private->devno, sch->schid.ssid, 132 sch->schid.sch_no); 133 return -EUSERS; 134 } 135 return 0; 136 } 137 138 /* 139 * Got interrupt for Sense Path Group ID. 140 */ 141 void 142 ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) 143 { 144 struct subchannel *sch; 145 struct irb *irb; 146 int ret; 147 148 irb = (struct irb *) __LC_IRB; 149 /* Retry sense pgid for cc=1. */ 150 if (irb->scsw.stctl == 151 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 152 if (irb->scsw.cc == 1) { 153 ret = __ccw_device_sense_pgid_start(cdev); 154 if (ret && ret != -EBUSY) 155 ccw_device_sense_pgid_done(cdev, ret); 156 } 157 return; 158 } 159 if (ccw_device_accumulate_and_sense(cdev, irb) != 0) 160 return; 161 sch = to_subchannel(cdev->dev.parent); 162 ret = __ccw_device_check_sense_pgid(cdev); 163 memset(&cdev->private->irb, 0, sizeof(struct irb)); 164 switch (ret) { 165 /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ 166 case 0: /* Sense Path Group ID successful. */ 167 if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET) 168 memcpy(&cdev->private->pgid, &css[0]->global_pgid, 169 sizeof(struct pgid)); 170 ccw_device_sense_pgid_done(cdev, 0); 171 break; 172 case -EOPNOTSUPP: /* Sense Path Group ID not supported */ 173 ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); 174 break; 175 case -ETIME: /* Sense path group id stopped by timeout. */ 176 ccw_device_sense_pgid_done(cdev, -ETIME); 177 break; 178 case -EACCES: /* channel is not operational. */ 179 sch->lpm &= ~cdev->private->imask; 180 cdev->private->imask >>= 1; 181 cdev->private->iretry = 5; 182 /* Fall through. */ 183 case -EAGAIN: /* Try again. */ 184 ret = __ccw_device_sense_pgid_start(cdev); 185 if (ret != 0 && ret != -EBUSY) 186 ccw_device_sense_pgid_done(cdev, -ENODEV); 187 break; 188 case -EUSERS: /* device is reserved for someone else. */ 189 ccw_device_sense_pgid_done(cdev, -EUSERS); 190 break; 191 } 192 } 193 194 /* 195 * Path Group ID helper function. 196 */ 197 static int 198 __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) 199 { 200 struct subchannel *sch; 201 struct ccw1 *ccw; 202 int ret; 203 204 sch = to_subchannel(cdev->dev.parent); 205 206 /* Setup sense path group id channel program. */ 207 cdev->private->pgid.inf.fc = func; 208 ccw = cdev->private->iccws; 209 if (!cdev->private->flags.pgid_single) { 210 cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH; 211 ccw->cmd_code = CCW_CMD_SUSPEND_RECONN; 212 ccw->cda = 0; 213 ccw->count = 0; 214 ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC; 215 ccw++; 216 } else 217 cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH; 218 219 ccw->cmd_code = CCW_CMD_SET_PGID; 220 ccw->cda = (__u32) __pa (&cdev->private->pgid); 221 ccw->count = sizeof (struct pgid); 222 ccw->flags = CCW_FLAG_SLI; 223 224 /* Reset device status. */ 225 memset(&cdev->private->irb, 0, sizeof(struct irb)); 226 227 /* Try multiple times. */ 228 ret = -ENODEV; 229 if (cdev->private->iretry > 0) { 230 cdev->private->iretry--; 231 ret = cio_start (sch, cdev->private->iccws, 232 cdev->private->imask); 233 /* ret is 0, -EBUSY, -EACCES or -ENODEV */ 234 if ((ret != -EACCES) && (ret != -ENODEV)) 235 return ret; 236 } 237 /* PGID command failed on this path. Switch it off. */ 238 sch->lpm &= ~cdev->private->imask; 239 sch->vpm &= ~cdev->private->imask; 240 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel " 241 "0.%x.%04x, lpm %02X, became 'not operational'\n", 242 cdev->private->devno, sch->schid.ssid, 243 sch->schid.sch_no, cdev->private->imask); 244 return ret; 245 } 246 247 /* 248 * Called from interrupt context to check if a valid answer 249 * to Set Path Group ID was received. 250 */ 251 static int 252 __ccw_device_check_pgid(struct ccw_device *cdev) 253 { 254 struct subchannel *sch; 255 struct irb *irb; 256 257 sch = to_subchannel(cdev->dev.parent); 258 irb = &cdev->private->irb; 259 if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) 260 return -ETIME; 261 if (irb->esw.esw0.erw.cons) { 262 if (irb->ecw[0] & SNS0_CMD_REJECT) 263 return -EOPNOTSUPP; 264 /* Hmm, whatever happened, try again. */ 265 CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " 266 "cnt %02d, " 267 "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", 268 cdev->private->ssid, 269 cdev->private->devno, irb->esw.esw0.erw.scnt, 270 irb->ecw[0], irb->ecw[1], 271 irb->ecw[2], irb->ecw[3], 272 irb->ecw[4], irb->ecw[5], 273 irb->ecw[6], irb->ecw[7]); 274 return -EAGAIN; 275 } 276 if (irb->scsw.cc == 3) { 277 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x," 278 " lpm %02X, became 'not operational'\n", 279 cdev->private->devno, sch->schid.ssid, 280 sch->schid.sch_no, cdev->private->imask); 281 return -EACCES; 282 } 283 return 0; 284 } 285 286 static void 287 __ccw_device_verify_start(struct ccw_device *cdev) 288 { 289 struct subchannel *sch; 290 __u8 imask, func; 291 int ret; 292 293 sch = to_subchannel(cdev->dev.parent); 294 while (sch->vpm != sch->lpm) { 295 /* Find first unequal bit in vpm vs. lpm */ 296 for (imask = 0x80; imask != 0; imask >>= 1) 297 if ((sch->vpm & imask) != (sch->lpm & imask)) 298 break; 299 cdev->private->imask = imask; 300 func = (sch->vpm & imask) ? 301 SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH; 302 ret = __ccw_device_do_pgid(cdev, func); 303 if (ret == 0 || ret == -EBUSY) 304 return; 305 cdev->private->iretry = 5; 306 } 307 ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); 308 } 309 310 /* 311 * Got interrupt for Set Path Group ID. 312 */ 313 void 314 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) 315 { 316 struct subchannel *sch; 317 struct irb *irb; 318 int ret; 319 320 irb = (struct irb *) __LC_IRB; 321 /* Retry set pgid for cc=1. */ 322 if (irb->scsw.stctl == 323 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 324 if (irb->scsw.cc == 1) 325 __ccw_device_verify_start(cdev); 326 return; 327 } 328 if (ccw_device_accumulate_and_sense(cdev, irb) != 0) 329 return; 330 sch = to_subchannel(cdev->dev.parent); 331 ret = __ccw_device_check_pgid(cdev); 332 memset(&cdev->private->irb, 0, sizeof(struct irb)); 333 switch (ret) { 334 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ 335 case 0: 336 /* Establish or Resign Path Group done. Update vpm. */ 337 if ((sch->lpm & cdev->private->imask) != 0) 338 sch->vpm |= cdev->private->imask; 339 else 340 sch->vpm &= ~cdev->private->imask; 341 cdev->private->iretry = 5; 342 __ccw_device_verify_start(cdev); 343 break; 344 case -EOPNOTSUPP: 345 /* 346 * One of those strange devices which claim to be able 347 * to do multipathing but not for Set Path Group ID. 348 */ 349 if (cdev->private->flags.pgid_single) { 350 ccw_device_verify_done(cdev, -EOPNOTSUPP); 351 break; 352 } 353 cdev->private->flags.pgid_single = 1; 354 /* fall through. */ 355 case -EAGAIN: /* Try again. */ 356 __ccw_device_verify_start(cdev); 357 break; 358 case -ETIME: /* Set path group id stopped by timeout. */ 359 ccw_device_verify_done(cdev, -ETIME); 360 break; 361 case -EACCES: /* channel is not operational. */ 362 sch->lpm &= ~cdev->private->imask; 363 sch->vpm &= ~cdev->private->imask; 364 cdev->private->iretry = 5; 365 __ccw_device_verify_start(cdev); 366 break; 367 } 368 } 369 370 void 371 ccw_device_verify_start(struct ccw_device *cdev) 372 { 373 struct subchannel *sch = to_subchannel(cdev->dev.parent); 374 375 cdev->private->flags.pgid_single = 0; 376 cdev->private->iretry = 5; 377 /* 378 * Update sch->lpm with current values to catch paths becoming 379 * available again. 380 */ 381 if (stsch(sch->schid, &sch->schib)) { 382 ccw_device_verify_done(cdev, -ENODEV); 383 return; 384 } 385 sch->lpm = sch->schib.pmcw.pim & 386 sch->schib.pmcw.pam & 387 sch->schib.pmcw.pom & 388 sch->opm; 389 __ccw_device_verify_start(cdev); 390 } 391 392 static void 393 __ccw_device_disband_start(struct ccw_device *cdev) 394 { 395 struct subchannel *sch; 396 int ret; 397 398 sch = to_subchannel(cdev->dev.parent); 399 while (cdev->private->imask != 0) { 400 if (sch->lpm & cdev->private->imask) { 401 ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); 402 if (ret == 0) 403 return; 404 } 405 cdev->private->iretry = 5; 406 cdev->private->imask >>= 1; 407 } 408 ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); 409 } 410 411 /* 412 * Got interrupt for Unset Path Group ID. 413 */ 414 void 415 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) 416 { 417 struct subchannel *sch; 418 struct irb *irb; 419 int ret; 420 421 irb = (struct irb *) __LC_IRB; 422 /* Retry set pgid for cc=1. */ 423 if (irb->scsw.stctl == 424 (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { 425 if (irb->scsw.cc == 1) 426 __ccw_device_disband_start(cdev); 427 return; 428 } 429 if (ccw_device_accumulate_and_sense(cdev, irb) != 0) 430 return; 431 sch = to_subchannel(cdev->dev.parent); 432 ret = __ccw_device_check_pgid(cdev); 433 memset(&cdev->private->irb, 0, sizeof(struct irb)); 434 switch (ret) { 435 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ 436 case 0: /* disband successful. */ 437 sch->vpm = 0; 438 ccw_device_disband_done(cdev, ret); 439 break; 440 case -EOPNOTSUPP: 441 /* 442 * One of those strange devices which claim to be able 443 * to do multipathing but not for Unset Path Group ID. 444 */ 445 cdev->private->flags.pgid_single = 1; 446 /* fall through. */ 447 case -EAGAIN: /* Try again. */ 448 __ccw_device_disband_start(cdev); 449 break; 450 case -ETIME: /* Set path group id stopped by timeout. */ 451 ccw_device_disband_done(cdev, -ETIME); 452 break; 453 case -EACCES: /* channel is not operational. */ 454 cdev->private->imask >>= 1; 455 cdev->private->iretry = 5; 456 __ccw_device_disband_start(cdev); 457 break; 458 } 459 } 460 461 void 462 ccw_device_disband_start(struct ccw_device *cdev) 463 { 464 cdev->private->flags.pgid_single = 0; 465 cdev->private->iretry = 5; 466 cdev->private->imask = 0x80; 467 __ccw_device_disband_start(cdev); 468 } 469