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