diff options
Diffstat (limited to 'drivers/s390/cio/cio.c')
| -rw-r--r-- | drivers/s390/cio/cio.c | 245 |
1 files changed, 141 insertions, 104 deletions
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 3db2c386546f..8a8df7552969 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | 9 | * Martin Schwidefsky (schwidefsky@de.ibm.com) |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #define KMSG_COMPONENT "cio" | ||
| 13 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 14 | |||
| 12 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 13 | #include <linux/init.h> | 16 | #include <linux/init.h> |
| 14 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
| @@ -104,44 +107,6 @@ cio_get_options (struct subchannel *sch) | |||
| 104 | return flags; | 107 | return flags; |
| 105 | } | 108 | } |
| 106 | 109 | ||
| 107 | /* | ||
| 108 | * Use tpi to get a pending interrupt, call the interrupt handler and | ||
| 109 | * return a pointer to the subchannel structure. | ||
| 110 | */ | ||
| 111 | static int | ||
| 112 | cio_tpi(void) | ||
| 113 | { | ||
| 114 | struct tpi_info *tpi_info; | ||
| 115 | struct subchannel *sch; | ||
| 116 | struct irb *irb; | ||
| 117 | int irq_context; | ||
| 118 | |||
| 119 | tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; | ||
| 120 | if (tpi (NULL) != 1) | ||
| 121 | return 0; | ||
| 122 | irb = (struct irb *) __LC_IRB; | ||
| 123 | /* Store interrupt response block to lowcore. */ | ||
| 124 | if (tsch (tpi_info->schid, irb) != 0) | ||
| 125 | /* Not status pending or not operational. */ | ||
| 126 | return 1; | ||
| 127 | sch = (struct subchannel *)(unsigned long)tpi_info->intparm; | ||
| 128 | if (!sch) | ||
| 129 | return 1; | ||
| 130 | irq_context = in_interrupt(); | ||
| 131 | if (!irq_context) | ||
| 132 | local_bh_disable(); | ||
| 133 | irq_enter (); | ||
| 134 | spin_lock(sch->lock); | ||
| 135 | memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); | ||
| 136 | if (sch->driver && sch->driver->irq) | ||
| 137 | sch->driver->irq(sch); | ||
| 138 | spin_unlock(sch->lock); | ||
| 139 | irq_exit (); | ||
| 140 | if (!irq_context) | ||
| 141 | _local_bh_enable(); | ||
| 142 | return 1; | ||
| 143 | } | ||
| 144 | |||
| 145 | static int | 110 | static int |
| 146 | cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) | 111 | cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) |
| 147 | { | 112 | { |
| @@ -152,11 +117,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) | |||
| 152 | else | 117 | else |
| 153 | sch->lpm = 0; | 118 | sch->lpm = 0; |
| 154 | 119 | ||
| 155 | stsch (sch->schid, &sch->schib); | ||
| 156 | |||
| 157 | CIO_MSG_EVENT(2, "cio_start: 'not oper' status for " | 120 | CIO_MSG_EVENT(2, "cio_start: 'not oper' status for " |
| 158 | "subchannel 0.%x.%04x!\n", sch->schid.ssid, | 121 | "subchannel 0.%x.%04x!\n", sch->schid.ssid, |
| 159 | sch->schid.sch_no); | 122 | sch->schid.sch_no); |
| 123 | |||
| 124 | if (cio_update_schib(sch)) | ||
| 125 | return -ENODEV; | ||
| 126 | |||
| 160 | sprintf(dbf_text, "no%s", dev_name(&sch->dev)); | 127 | sprintf(dbf_text, "no%s", dev_name(&sch->dev)); |
| 161 | CIO_TRACE_EVENT(0, dbf_text); | 128 | CIO_TRACE_EVENT(0, dbf_text); |
| 162 | CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); | 129 | CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); |
| @@ -354,7 +321,8 @@ cio_cancel (struct subchannel *sch) | |||
| 354 | switch (ccode) { | 321 | switch (ccode) { |
| 355 | case 0: /* success */ | 322 | case 0: /* success */ |
| 356 | /* Update information in scsw. */ | 323 | /* Update information in scsw. */ |
| 357 | stsch (sch->schid, &sch->schib); | 324 | if (cio_update_schib(sch)) |
| 325 | return -ENODEV; | ||
| 358 | return 0; | 326 | return 0; |
| 359 | case 1: /* status pending */ | 327 | case 1: /* status pending */ |
| 360 | return -EBUSY; | 328 | return -EBUSY; |
| @@ -365,30 +333,70 @@ cio_cancel (struct subchannel *sch) | |||
| 365 | } | 333 | } |
| 366 | } | 334 | } |
| 367 | 335 | ||
| 336 | |||
| 337 | static void cio_apply_config(struct subchannel *sch, struct schib *schib) | ||
| 338 | { | ||
| 339 | schib->pmcw.intparm = sch->config.intparm; | ||
| 340 | schib->pmcw.mbi = sch->config.mbi; | ||
| 341 | schib->pmcw.isc = sch->config.isc; | ||
| 342 | schib->pmcw.ena = sch->config.ena; | ||
| 343 | schib->pmcw.mme = sch->config.mme; | ||
| 344 | schib->pmcw.mp = sch->config.mp; | ||
| 345 | schib->pmcw.csense = sch->config.csense; | ||
| 346 | schib->pmcw.mbfc = sch->config.mbfc; | ||
| 347 | if (sch->config.mbfc) | ||
| 348 | schib->mba = sch->config.mba; | ||
| 349 | } | ||
| 350 | |||
| 351 | static int cio_check_config(struct subchannel *sch, struct schib *schib) | ||
| 352 | { | ||
| 353 | return (schib->pmcw.intparm == sch->config.intparm) && | ||
| 354 | (schib->pmcw.mbi == sch->config.mbi) && | ||
| 355 | (schib->pmcw.isc == sch->config.isc) && | ||
| 356 | (schib->pmcw.ena == sch->config.ena) && | ||
| 357 | (schib->pmcw.mme == sch->config.mme) && | ||
| 358 | (schib->pmcw.mp == sch->config.mp) && | ||
| 359 | (schib->pmcw.csense == sch->config.csense) && | ||
| 360 | (schib->pmcw.mbfc == sch->config.mbfc) && | ||
| 361 | (!sch->config.mbfc || (schib->mba == sch->config.mba)); | ||
| 362 | } | ||
| 363 | |||
| 368 | /* | 364 | /* |
| 369 | * Function: cio_modify | 365 | * cio_commit_config - apply configuration to the subchannel |
| 370 | * Issues a "Modify Subchannel" on the specified subchannel | ||
| 371 | */ | 366 | */ |
| 372 | int | 367 | int cio_commit_config(struct subchannel *sch) |
| 373 | cio_modify (struct subchannel *sch) | ||
| 374 | { | 368 | { |
| 375 | int ccode, retry, ret; | 369 | struct schib schib; |
| 370 | int ccode, retry, ret = 0; | ||
| 371 | |||
| 372 | if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib)) | ||
| 373 | return -ENODEV; | ||
| 376 | 374 | ||
| 377 | ret = 0; | ||
| 378 | for (retry = 0; retry < 5; retry++) { | 375 | for (retry = 0; retry < 5; retry++) { |
| 379 | ccode = msch_err (sch->schid, &sch->schib); | 376 | /* copy desired changes to local schib */ |
| 380 | if (ccode < 0) /* -EIO if msch gets a program check. */ | 377 | cio_apply_config(sch, &schib); |
| 378 | ccode = msch_err(sch->schid, &schib); | ||
| 379 | if (ccode < 0) /* -EIO if msch gets a program check. */ | ||
| 381 | return ccode; | 380 | return ccode; |
| 382 | switch (ccode) { | 381 | switch (ccode) { |
| 383 | case 0: /* successfull */ | 382 | case 0: /* successfull */ |
| 384 | return 0; | 383 | if (stsch(sch->schid, &schib) || |
| 385 | case 1: /* status pending */ | 384 | !css_sch_is_valid(&schib)) |
| 385 | return -ENODEV; | ||
| 386 | if (cio_check_config(sch, &schib)) { | ||
| 387 | /* commit changes from local schib */ | ||
| 388 | memcpy(&sch->schib, &schib, sizeof(schib)); | ||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | ret = -EAGAIN; | ||
| 392 | break; | ||
| 393 | case 1: /* status pending */ | ||
| 386 | return -EBUSY; | 394 | return -EBUSY; |
| 387 | case 2: /* busy */ | 395 | case 2: /* busy */ |
| 388 | udelay (100); /* allow for recovery */ | 396 | udelay(100); /* allow for recovery */ |
| 389 | ret = -EBUSY; | 397 | ret = -EBUSY; |
| 390 | break; | 398 | break; |
| 391 | case 3: /* not operational */ | 399 | case 3: /* not operational */ |
| 392 | return -ENODEV; | 400 | return -ENODEV; |
| 393 | } | 401 | } |
| 394 | } | 402 | } |
| @@ -396,6 +404,23 @@ cio_modify (struct subchannel *sch) | |||
| 396 | } | 404 | } |
| 397 | 405 | ||
| 398 | /** | 406 | /** |
| 407 | * cio_update_schib - Perform stsch and update schib if subchannel is valid. | ||
| 408 | * @sch: subchannel on which to perform stsch | ||
| 409 | * Return zero on success, -ENODEV otherwise. | ||
| 410 | */ | ||
| 411 | int cio_update_schib(struct subchannel *sch) | ||
| 412 | { | ||
| 413 | struct schib schib; | ||
| 414 | |||
| 415 | if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib)) | ||
| 416 | return -ENODEV; | ||
| 417 | |||
| 418 | memcpy(&sch->schib, &schib, sizeof(schib)); | ||
| 419 | return 0; | ||
| 420 | } | ||
| 421 | EXPORT_SYMBOL_GPL(cio_update_schib); | ||
| 422 | |||
| 423 | /** | ||
| 399 | * cio_enable_subchannel - enable a subchannel. | 424 | * cio_enable_subchannel - enable a subchannel. |
| 400 | * @sch: subchannel to be enabled | 425 | * @sch: subchannel to be enabled |
| 401 | * @intparm: interruption parameter to set | 426 | * @intparm: interruption parameter to set |
| @@ -403,7 +428,6 @@ cio_modify (struct subchannel *sch) | |||
| 403 | int cio_enable_subchannel(struct subchannel *sch, u32 intparm) | 428 | int cio_enable_subchannel(struct subchannel *sch, u32 intparm) |
| 404 | { | 429 | { |
| 405 | char dbf_txt[15]; | 430 | char dbf_txt[15]; |
| 406 | int ccode; | ||
| 407 | int retry; | 431 | int retry; |
| 408 | int ret; | 432 | int ret; |
| 409 | 433 | ||
| @@ -412,33 +436,27 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) | |||
| 412 | 436 | ||
| 413 | if (sch_is_pseudo_sch(sch)) | 437 | if (sch_is_pseudo_sch(sch)) |
| 414 | return -EINVAL; | 438 | return -EINVAL; |
| 415 | ccode = stsch (sch->schid, &sch->schib); | 439 | if (cio_update_schib(sch)) |
| 416 | if (ccode) | ||
| 417 | return -ENODEV; | 440 | return -ENODEV; |
| 418 | 441 | ||
| 419 | for (retry = 5, ret = 0; retry > 0; retry--) { | 442 | sch->config.ena = 1; |
| 420 | sch->schib.pmcw.ena = 1; | 443 | sch->config.isc = sch->isc; |
| 421 | sch->schib.pmcw.isc = sch->isc; | 444 | sch->config.intparm = intparm; |
| 422 | sch->schib.pmcw.intparm = intparm; | 445 | |
| 423 | ret = cio_modify(sch); | 446 | for (retry = 0; retry < 3; retry++) { |
| 424 | if (ret == -ENODEV) | 447 | ret = cio_commit_config(sch); |
| 425 | break; | 448 | if (ret == -EIO) { |
| 426 | if (ret == -EIO) | ||
| 427 | /* | 449 | /* |
| 428 | * Got a program check in cio_modify. Try without | 450 | * Got a program check in msch. Try without |
| 429 | * the concurrent sense bit the next time. | 451 | * the concurrent sense bit the next time. |
| 430 | */ | 452 | */ |
| 431 | sch->schib.pmcw.csense = 0; | 453 | sch->config.csense = 0; |
| 432 | if (ret == 0) { | 454 | } else if (ret == -EBUSY) { |
| 433 | stsch (sch->schid, &sch->schib); | ||
| 434 | if (sch->schib.pmcw.ena) | ||
| 435 | break; | ||
| 436 | } | ||
| 437 | if (ret == -EBUSY) { | ||
| 438 | struct irb irb; | 455 | struct irb irb; |
| 439 | if (tsch(sch->schid, &irb) != 0) | 456 | if (tsch(sch->schid, &irb) != 0) |
| 440 | break; | 457 | break; |
| 441 | } | 458 | } else |
| 459 | break; | ||
| 442 | } | 460 | } |
| 443 | sprintf (dbf_txt, "ret:%d", ret); | 461 | sprintf (dbf_txt, "ret:%d", ret); |
| 444 | CIO_TRACE_EVENT (2, dbf_txt); | 462 | CIO_TRACE_EVENT (2, dbf_txt); |
| @@ -453,8 +471,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel); | |||
| 453 | int cio_disable_subchannel(struct subchannel *sch) | 471 | int cio_disable_subchannel(struct subchannel *sch) |
| 454 | { | 472 | { |
| 455 | char dbf_txt[15]; | 473 | char dbf_txt[15]; |
| 456 | int ccode; | ||
| 457 | int retry; | ||
| 458 | int ret; | 474 | int ret; |
| 459 | 475 | ||
| 460 | CIO_TRACE_EVENT (2, "dissch"); | 476 | CIO_TRACE_EVENT (2, "dissch"); |
| @@ -462,8 +478,7 @@ int cio_disable_subchannel(struct subchannel *sch) | |||
| 462 | 478 | ||
| 463 | if (sch_is_pseudo_sch(sch)) | 479 | if (sch_is_pseudo_sch(sch)) |
| 464 | return 0; | 480 | return 0; |
| 465 | ccode = stsch (sch->schid, &sch->schib); | 481 | if (cio_update_schib(sch)) |
| 466 | if (ccode == 3) /* Not operational. */ | ||
| 467 | return -ENODEV; | 482 | return -ENODEV; |
| 468 | 483 | ||
| 469 | if (scsw_actl(&sch->schib.scsw) != 0) | 484 | if (scsw_actl(&sch->schib.scsw) != 0) |
| @@ -473,24 +488,9 @@ int cio_disable_subchannel(struct subchannel *sch) | |||
| 473 | */ | 488 | */ |
| 474 | return -EBUSY; | 489 | return -EBUSY; |
| 475 | 490 | ||
| 476 | for (retry = 5, ret = 0; retry > 0; retry--) { | 491 | sch->config.ena = 0; |
| 477 | sch->schib.pmcw.ena = 0; | 492 | ret = cio_commit_config(sch); |
| 478 | ret = cio_modify(sch); | 493 | |
| 479 | if (ret == -ENODEV) | ||
| 480 | break; | ||
| 481 | if (ret == -EBUSY) | ||
| 482 | /* | ||
| 483 | * The subchannel is busy or status pending. | ||
| 484 | * We'll disable when the next interrupt was delivered | ||
| 485 | * via the state machine. | ||
| 486 | */ | ||
| 487 | break; | ||
| 488 | if (ret == 0) { | ||
| 489 | stsch (sch->schid, &sch->schib); | ||
| 490 | if (!sch->schib.pmcw.ena) | ||
| 491 | break; | ||
| 492 | } | ||
| 493 | } | ||
| 494 | sprintf (dbf_txt, "ret:%d", ret); | 494 | sprintf (dbf_txt, "ret:%d", ret); |
| 495 | CIO_TRACE_EVENT (2, dbf_txt); | 495 | CIO_TRACE_EVENT (2, dbf_txt); |
| 496 | return ret; | 496 | return ret; |
| @@ -687,6 +687,43 @@ static char console_sch_name[10] = "0.x.xxxx"; | |||
| 687 | static struct io_subchannel_private console_priv; | 687 | static struct io_subchannel_private console_priv; |
| 688 | static int console_subchannel_in_use; | 688 | static int console_subchannel_in_use; |
| 689 | 689 | ||
| 690 | /* | ||
| 691 | * Use tpi to get a pending interrupt, call the interrupt handler and | ||
| 692 | * return a pointer to the subchannel structure. | ||
| 693 | */ | ||
| 694 | static int cio_tpi(void) | ||
| 695 | { | ||
| 696 | struct tpi_info *tpi_info; | ||
| 697 | struct subchannel *sch; | ||
| 698 | struct irb *irb; | ||
| 699 | int irq_context; | ||
| 700 | |||
| 701 | tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; | ||
| 702 | if (tpi(NULL) != 1) | ||
| 703 | return 0; | ||
| 704 | irb = (struct irb *) __LC_IRB; | ||
| 705 | /* Store interrupt response block to lowcore. */ | ||
| 706 | if (tsch(tpi_info->schid, irb) != 0) | ||
| 707 | /* Not status pending or not operational. */ | ||
| 708 | return 1; | ||
| 709 | sch = (struct subchannel *)(unsigned long)tpi_info->intparm; | ||
| 710 | if (!sch) | ||
| 711 | return 1; | ||
| 712 | irq_context = in_interrupt(); | ||
| 713 | if (!irq_context) | ||
| 714 | local_bh_disable(); | ||
| 715 | irq_enter(); | ||
| 716 | spin_lock(sch->lock); | ||
| 717 | memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); | ||
| 718 | if (sch->driver && sch->driver->irq) | ||
| 719 | sch->driver->irq(sch); | ||
| 720 | spin_unlock(sch->lock); | ||
| 721 | irq_exit(); | ||
| 722 | if (!irq_context) | ||
| 723 | _local_bh_enable(); | ||
| 724 | return 1; | ||
| 725 | } | ||
| 726 | |||
| 690 | void *cio_get_console_priv(void) | 727 | void *cio_get_console_priv(void) |
| 691 | { | 728 | { |
| 692 | return &console_priv; | 729 | return &console_priv; |
| @@ -780,7 +817,7 @@ cio_probe_console(void) | |||
| 780 | sch_no = cio_get_console_sch_no(); | 817 | sch_no = cio_get_console_sch_no(); |
| 781 | if (sch_no == -1) { | 818 | if (sch_no == -1) { |
| 782 | console_subchannel_in_use = 0; | 819 | console_subchannel_in_use = 0; |
| 783 | printk(KERN_WARNING "cio: No ccw console found!\n"); | 820 | pr_warning("No CCW console was found\n"); |
| 784 | return ERR_PTR(-ENODEV); | 821 | return ERR_PTR(-ENODEV); |
| 785 | } | 822 | } |
| 786 | memset(&console_subchannel, 0, sizeof(struct subchannel)); | 823 | memset(&console_subchannel, 0, sizeof(struct subchannel)); |
| @@ -796,10 +833,9 @@ cio_probe_console(void) | |||
| 796 | * enable console I/O-interrupt subclass | 833 | * enable console I/O-interrupt subclass |
| 797 | */ | 834 | */ |
| 798 | isc_register(CONSOLE_ISC); | 835 | isc_register(CONSOLE_ISC); |
| 799 | console_subchannel.schib.pmcw.isc = CONSOLE_ISC; | 836 | console_subchannel.config.isc = CONSOLE_ISC; |
| 800 | console_subchannel.schib.pmcw.intparm = | 837 | console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel; |
| 801 | (u32)(addr_t)&console_subchannel; | 838 | ret = cio_commit_config(&console_subchannel); |
| 802 | ret = cio_modify(&console_subchannel); | ||
| 803 | if (ret) { | 839 | if (ret) { |
| 804 | isc_unregister(CONSOLE_ISC); | 840 | isc_unregister(CONSOLE_ISC); |
| 805 | console_subchannel_in_use = 0; | 841 | console_subchannel_in_use = 0; |
| @@ -811,8 +847,8 @@ cio_probe_console(void) | |||
| 811 | void | 847 | void |
| 812 | cio_release_console(void) | 848 | cio_release_console(void) |
| 813 | { | 849 | { |
| 814 | console_subchannel.schib.pmcw.intparm = 0; | 850 | console_subchannel.config.intparm = 0; |
| 815 | cio_modify(&console_subchannel); | 851 | cio_commit_config(&console_subchannel); |
| 816 | isc_unregister(CONSOLE_ISC); | 852 | isc_unregister(CONSOLE_ISC); |
| 817 | console_subchannel_in_use = 0; | 853 | console_subchannel_in_use = 0; |
| 818 | } | 854 | } |
| @@ -852,7 +888,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib) | |||
| 852 | cc = msch(schid, schib); | 888 | cc = msch(schid, schib); |
| 853 | if (cc) | 889 | if (cc) |
| 854 | return (cc==3?-ENODEV:-EBUSY); | 890 | return (cc==3?-ENODEV:-EBUSY); |
| 855 | stsch(schid, schib); | 891 | if (stsch(schid, schib) || !css_sch_is_valid(schib)) |
| 892 | return -ENODEV; | ||
| 856 | if (!schib->pmcw.ena) | 893 | if (!schib->pmcw.ena) |
| 857 | return 0; | 894 | return 0; |
| 858 | } | 895 | } |
