diff options
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 140 |
1 files changed, 110 insertions, 30 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 12257776e79b..0dfab30e8089 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <asm/io.h> | 24 | #include <asm/io.h> |
25 | #include <asm/todclk.h> | 25 | #include <asm/todclk.h> |
26 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
27 | #include <asm/cio.h> | ||
27 | #include <asm/ccwdev.h> | 28 | #include <asm/ccwdev.h> |
28 | 29 | ||
29 | #include "dasd_int.h" | 30 | #include "dasd_int.h" |
@@ -89,17 +90,22 @@ dasd_eckd_probe (struct ccw_device *cdev) | |||
89 | { | 90 | { |
90 | int ret; | 91 | int ret; |
91 | 92 | ||
92 | ret = dasd_generic_probe (cdev, &dasd_eckd_discipline); | 93 | /* set ECKD specific ccw-device options */ |
93 | if (ret) | 94 | ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE); |
95 | if (ret) { | ||
96 | printk(KERN_WARNING | ||
97 | "dasd_eckd_probe: could not set ccw-device options " | ||
98 | "for %s\n", cdev->dev.bus_id); | ||
94 | return ret; | 99 | return ret; |
95 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE); | 100 | } |
96 | return 0; | 101 | ret = dasd_generic_probe(cdev, &dasd_eckd_discipline); |
102 | return ret; | ||
97 | } | 103 | } |
98 | 104 | ||
99 | static int | 105 | static int |
100 | dasd_eckd_set_online(struct ccw_device *cdev) | 106 | dasd_eckd_set_online(struct ccw_device *cdev) |
101 | { | 107 | { |
102 | return dasd_generic_set_online (cdev, &dasd_eckd_discipline); | 108 | return dasd_generic_set_online(cdev, &dasd_eckd_discipline); |
103 | } | 109 | } |
104 | 110 | ||
105 | static struct ccw_driver dasd_eckd_driver = { | 111 | static struct ccw_driver dasd_eckd_driver = { |
@@ -541,6 +547,86 @@ dasd_eckd_read_conf(struct dasd_device *device) | |||
541 | } | 547 | } |
542 | 548 | ||
543 | /* | 549 | /* |
550 | * Build CP for Perform Subsystem Function - SSC. | ||
551 | */ | ||
552 | struct dasd_ccw_req * | ||
553 | dasd_eckd_build_psf_ssc(struct dasd_device *device) | ||
554 | { | ||
555 | struct dasd_ccw_req *cqr; | ||
556 | struct dasd_psf_ssc_data *psf_ssc_data; | ||
557 | struct ccw1 *ccw; | ||
558 | |||
559 | cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , | ||
560 | sizeof(struct dasd_psf_ssc_data), | ||
561 | device); | ||
562 | |||
563 | if (IS_ERR(cqr)) { | ||
564 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
565 | "Could not allocate PSF-SSC request"); | ||
566 | return cqr; | ||
567 | } | ||
568 | psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; | ||
569 | psf_ssc_data->order = PSF_ORDER_SSC; | ||
570 | psf_ssc_data->suborder = 0x08; | ||
571 | |||
572 | ccw = cqr->cpaddr; | ||
573 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
574 | ccw->cda = (__u32)(addr_t)psf_ssc_data; | ||
575 | ccw->count = 66; | ||
576 | |||
577 | cqr->device = device; | ||
578 | cqr->expires = 10*HZ; | ||
579 | cqr->buildclk = get_clock(); | ||
580 | cqr->status = DASD_CQR_FILLED; | ||
581 | return cqr; | ||
582 | } | ||
583 | |||
584 | /* | ||
585 | * Perform Subsystem Function. | ||
586 | * It is necessary to trigger CIO for channel revalidation since this | ||
587 | * call might change behaviour of DASD devices. | ||
588 | */ | ||
589 | static int | ||
590 | dasd_eckd_psf_ssc(struct dasd_device *device) | ||
591 | { | ||
592 | struct dasd_ccw_req *cqr; | ||
593 | int rc; | ||
594 | |||
595 | cqr = dasd_eckd_build_psf_ssc(device); | ||
596 | if (IS_ERR(cqr)) | ||
597 | return PTR_ERR(cqr); | ||
598 | |||
599 | rc = dasd_sleep_on(cqr); | ||
600 | if (!rc) | ||
601 | /* trigger CIO to reprobe devices */ | ||
602 | css_schedule_reprobe(); | ||
603 | dasd_sfree_request(cqr, cqr->device); | ||
604 | return rc; | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * Valide storage server of current device. | ||
609 | */ | ||
610 | static int | ||
611 | dasd_eckd_validate_server(struct dasd_device *device) | ||
612 | { | ||
613 | int rc; | ||
614 | |||
615 | /* Currently PAV is the only reason to 'validate' server on LPAR */ | ||
616 | if (dasd_nopav || MACHINE_IS_VM) | ||
617 | return 0; | ||
618 | |||
619 | rc = dasd_eckd_psf_ssc(device); | ||
620 | if (rc) | ||
621 | /* may be requested feature is not available on server, | ||
622 | * therefore just report error and go ahead */ | ||
623 | DEV_MESSAGE(KERN_INFO, device, | ||
624 | "Perform Subsystem Function returned rc=%d", rc); | ||
625 | /* RE-Read Configuration Data */ | ||
626 | return dasd_eckd_read_conf(device); | ||
627 | } | ||
628 | |||
629 | /* | ||
544 | * Check device characteristics. | 630 | * Check device characteristics. |
545 | * If the device is accessible using ECKD discipline, the device is enabled. | 631 | * If the device is accessible using ECKD discipline, the device is enabled. |
546 | */ | 632 | */ |
@@ -570,16 +656,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
570 | private->attrib.operation = DASD_NORMAL_CACHE; | 656 | private->attrib.operation = DASD_NORMAL_CACHE; |
571 | private->attrib.nr_cyl = 0; | 657 | private->attrib.nr_cyl = 0; |
572 | 658 | ||
659 | /* Read Configuration Data */ | ||
660 | rc = dasd_eckd_read_conf(device); | ||
661 | if (rc) | ||
662 | return rc; | ||
663 | |||
664 | /* Generate device unique id and register in devmap */ | ||
665 | rc = dasd_eckd_generate_uid(device, &uid); | ||
666 | if (rc) | ||
667 | return rc; | ||
668 | rc = dasd_set_uid(device->cdev, &uid); | ||
669 | if (rc == 1) /* new server found */ | ||
670 | rc = dasd_eckd_validate_server(device); | ||
671 | if (rc) | ||
672 | return rc; | ||
673 | |||
573 | /* Read Device Characteristics */ | 674 | /* Read Device Characteristics */ |
574 | rdc_data = (void *) &(private->rdc_data); | 675 | rdc_data = (void *) &(private->rdc_data); |
575 | memset(rdc_data, 0, sizeof(rdc_data)); | 676 | memset(rdc_data, 0, sizeof(rdc_data)); |
576 | rc = read_dev_chars(device->cdev, &rdc_data, 64); | 677 | rc = read_dev_chars(device->cdev, &rdc_data, 64); |
577 | if (rc) { | 678 | if (rc) |
578 | DEV_MESSAGE(KERN_WARNING, device, | 679 | DEV_MESSAGE(KERN_WARNING, device, |
579 | "Read device characteristics returned error %d", | 680 | "Read device characteristics returned " |
580 | rc); | 681 | "rc=%d", rc); |
581 | return rc; | ||
582 | } | ||
583 | 682 | ||
584 | DEV_MESSAGE(KERN_INFO, device, | 683 | DEV_MESSAGE(KERN_INFO, device, |
585 | "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", | 684 | "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", |
@@ -590,19 +689,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
590 | private->rdc_data.no_cyl, | 689 | private->rdc_data.no_cyl, |
591 | private->rdc_data.trk_per_cyl, | 690 | private->rdc_data.trk_per_cyl, |
592 | private->rdc_data.sec_per_trk); | 691 | private->rdc_data.sec_per_trk); |
593 | |||
594 | /* Read Configuration Data */ | ||
595 | rc = dasd_eckd_read_conf (device); | ||
596 | if (rc) | ||
597 | return rc; | ||
598 | |||
599 | /* Generate device unique id and register in devmap */ | ||
600 | rc = dasd_eckd_generate_uid(device, &uid); | ||
601 | if (rc) | ||
602 | return rc; | ||
603 | |||
604 | rc = dasd_set_uid(device->cdev, &uid); | ||
605 | |||
606 | return rc; | 692 | return rc; |
607 | } | 693 | } |
608 | 694 | ||
@@ -1687,14 +1773,8 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
1687 | static int __init | 1773 | static int __init |
1688 | dasd_eckd_init(void) | 1774 | dasd_eckd_init(void) |
1689 | { | 1775 | { |
1690 | int ret; | ||
1691 | |||
1692 | ASCEBC(dasd_eckd_discipline.ebcname, 4); | 1776 | ASCEBC(dasd_eckd_discipline.ebcname, 4); |
1693 | 1777 | return ccw_driver_register(&dasd_eckd_driver); | |
1694 | ret = ccw_driver_register(&dasd_eckd_driver); | ||
1695 | if (!ret) | ||
1696 | dasd_generic_auto_online(&dasd_eckd_driver); | ||
1697 | return ret; | ||
1698 | } | 1778 | } |
1699 | 1779 | ||
1700 | static void __exit | 1780 | static void __exit |