diff options
Diffstat (limited to 'drivers/s390/cio/cio.c')
-rw-r--r-- | drivers/s390/cio/cio.c | 95 |
1 files changed, 78 insertions, 17 deletions
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 89320c1ad825..2e2882daefbb 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -16,11 +16,10 @@ | |||
16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/kernel_stat.h> | 17 | #include <linux/kernel_stat.h> |
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | |||
20 | #include <asm/cio.h> | 19 | #include <asm/cio.h> |
21 | #include <asm/delay.h> | 20 | #include <asm/delay.h> |
22 | #include <asm/irq.h> | 21 | #include <asm/irq.h> |
23 | 22 | #include <asm/setup.h> | |
24 | #include "airq.h" | 23 | #include "airq.h" |
25 | #include "cio.h" | 24 | #include "cio.h" |
26 | #include "css.h" | 25 | #include "css.h" |
@@ -192,7 +191,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ | |||
192 | sch->orb.pfch = sch->options.prefetch == 0; | 191 | sch->orb.pfch = sch->options.prefetch == 0; |
193 | sch->orb.spnd = sch->options.suspend; | 192 | sch->orb.spnd = sch->options.suspend; |
194 | sch->orb.ssic = sch->options.suspend && sch->options.inter; | 193 | sch->orb.ssic = sch->options.suspend && sch->options.inter; |
195 | sch->orb.lpm = (lpm != 0) ? (lpm & sch->opm) : sch->lpm; | 194 | sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm; |
196 | #ifdef CONFIG_64BIT | 195 | #ifdef CONFIG_64BIT |
197 | /* | 196 | /* |
198 | * for 64 bit we always support 64 bit IDAWs with 4k page size only | 197 | * for 64 bit we always support 64 bit IDAWs with 4k page size only |
@@ -570,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) | |||
570 | sch->opm = 0xff; | 569 | sch->opm = 0xff; |
571 | if (!cio_is_console(sch->schid)) | 570 | if (!cio_is_console(sch->schid)) |
572 | chsc_validate_chpids(sch); | 571 | chsc_validate_chpids(sch); |
573 | sch->lpm = sch->schib.pmcw.pim & | 572 | sch->lpm = sch->schib.pmcw.pam & sch->opm; |
574 | sch->schib.pmcw.pam & | ||
575 | sch->schib.pmcw.pom & | ||
576 | sch->opm; | ||
577 | 573 | ||
578 | CIO_DEBUG(KERN_INFO, 0, | 574 | CIO_DEBUG(KERN_INFO, 0, |
579 | "Detected device %04x on subchannel 0.%x.%04X" | 575 | "Detected device %04x on subchannel 0.%x.%04X" |
@@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid) | |||
841 | return -EBUSY; | 837 | return -EBUSY; |
842 | } | 838 | } |
843 | 839 | ||
844 | extern void do_reipl(unsigned long devno); | 840 | struct sch_match_id { |
845 | static int | 841 | struct subchannel_id schid; |
846 | __shutdown_subchannel_easy(struct subchannel_id schid, void *data) | 842 | struct ccw_dev_id devid; |
843 | int rc; | ||
844 | }; | ||
845 | |||
846 | static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid, | ||
847 | void *data) | ||
847 | { | 848 | { |
848 | struct schib schib; | 849 | struct schib schib; |
850 | struct sch_match_id *match_id = data; | ||
849 | 851 | ||
850 | if (stsch_err(schid, &schib)) | 852 | if (stsch_err(schid, &schib)) |
851 | return -ENXIO; | 853 | return -ENXIO; |
854 | if (match_id && schib.pmcw.dnv && | ||
855 | (schib.pmcw.dev == match_id->devid.devno) && | ||
856 | (schid.ssid == match_id->devid.ssid)) { | ||
857 | match_id->schid = schid; | ||
858 | match_id->rc = 0; | ||
859 | } | ||
852 | if (!schib.pmcw.ena) | 860 | if (!schib.pmcw.ena) |
853 | return 0; | 861 | return 0; |
854 | switch(__disable_subchannel_easy(schid, &schib)) { | 862 | switch(__disable_subchannel_easy(schid, &schib)) { |
@@ -864,18 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data) | |||
864 | return 0; | 872 | return 0; |
865 | } | 873 | } |
866 | 874 | ||
867 | void | 875 | static int clear_all_subchannels_and_match(struct ccw_dev_id *devid, |
868 | clear_all_subchannels(void) | 876 | struct subchannel_id *schid) |
869 | { | 877 | { |
878 | struct sch_match_id match_id; | ||
879 | |||
880 | match_id.devid = *devid; | ||
881 | match_id.rc = -ENODEV; | ||
870 | local_irq_disable(); | 882 | local_irq_disable(); |
871 | for_each_subchannel(__shutdown_subchannel_easy, NULL); | 883 | for_each_subchannel(__shutdown_subchannel_easy_and_match, &match_id); |
884 | if (match_id.rc == 0) | ||
885 | *schid = match_id.schid; | ||
886 | return match_id.rc; | ||
872 | } | 887 | } |
873 | 888 | ||
889 | |||
890 | void clear_all_subchannels(void) | ||
891 | { | ||
892 | local_irq_disable(); | ||
893 | for_each_subchannel(__shutdown_subchannel_easy_and_match, NULL); | ||
894 | } | ||
895 | |||
896 | extern void do_reipl_asm(__u32 schid); | ||
897 | |||
874 | /* Make sure all subchannels are quiet before we re-ipl an lpar. */ | 898 | /* Make sure all subchannels are quiet before we re-ipl an lpar. */ |
875 | void | 899 | void reipl_ccw_dev(struct ccw_dev_id *devid) |
876 | reipl(unsigned long devno) | ||
877 | { | 900 | { |
878 | clear_all_subchannels(); | 901 | struct subchannel_id schid; |
902 | |||
903 | if (clear_all_subchannels_and_match(devid, &schid)) | ||
904 | panic("IPL Device not found\n"); | ||
879 | cio_reset_channel_paths(); | 905 | cio_reset_channel_paths(); |
880 | do_reipl(devno); | 906 | do_reipl_asm(*((__u32*)&schid)); |
907 | } | ||
908 | |||
909 | extern struct schib ipl_schib; | ||
910 | |||
911 | /* | ||
912 | * ipl_save_parameters gets called very early. It is not allowed to access | ||
913 | * anything in the bss section at all. The bss section is not cleared yet, | ||
914 | * but may contain some ipl parameters written by the firmware. | ||
915 | * These parameters (if present) are copied to 0x2000. | ||
916 | * To avoid corruption of the ipl parameters, all variables used by this | ||
917 | * function must reside on the stack or in the data section. | ||
918 | */ | ||
919 | void ipl_save_parameters(void) | ||
920 | { | ||
921 | struct subchannel_id schid; | ||
922 | unsigned int *ipl_ptr; | ||
923 | void *src, *dst; | ||
924 | |||
925 | schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID; | ||
926 | if (!schid.one) | ||
927 | return; | ||
928 | if (stsch(schid, &ipl_schib)) | ||
929 | return; | ||
930 | if (!ipl_schib.pmcw.dnv) | ||
931 | return; | ||
932 | ipl_devno = ipl_schib.pmcw.dev; | ||
933 | ipl_flags |= IPL_DEVNO_VALID; | ||
934 | if (!ipl_schib.pmcw.qf) | ||
935 | return; | ||
936 | ipl_flags |= IPL_PARMBLOCK_VALID; | ||
937 | ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR; | ||
938 | src = (void *)(unsigned long)*ipl_ptr; | ||
939 | dst = (void *)IPL_PARMBLOCK_ORIGIN; | ||
940 | memmove(dst, src, PAGE_SIZE); | ||
941 | *ipl_ptr = IPL_PARMBLOCK_ORIGIN; | ||
881 | } | 942 | } |