diff options
Diffstat (limited to 'drivers/s390/cio/cio.c')
-rw-r--r-- | drivers/s390/cio/cio.c | 128 |
1 files changed, 100 insertions, 28 deletions
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 8936e460a807..20aee2783847 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <asm/irq.h> | 21 | #include <asm/irq.h> |
22 | #include <asm/irq_regs.h> | 22 | #include <asm/irq_regs.h> |
23 | #include <asm/setup.h> | 23 | #include <asm/setup.h> |
24 | #include <asm/reset.h> | ||
24 | #include "airq.h" | 25 | #include "airq.h" |
25 | #include "cio.h" | 26 | #include "cio.h" |
26 | #include "css.h" | 27 | #include "css.h" |
@@ -28,6 +29,7 @@ | |||
28 | #include "ioasm.h" | 29 | #include "ioasm.h" |
29 | #include "blacklist.h" | 30 | #include "blacklist.h" |
30 | #include "cio_debug.h" | 31 | #include "cio_debug.h" |
32 | #include "../s390mach.h" | ||
31 | 33 | ||
32 | debug_info_t *cio_debug_msg_id; | 34 | debug_info_t *cio_debug_msg_id; |
33 | debug_info_t *cio_debug_trace_id; | 35 | debug_info_t *cio_debug_trace_id; |
@@ -841,26 +843,12 @@ __clear_subchannel_easy(struct subchannel_id schid) | |||
841 | return -EBUSY; | 843 | return -EBUSY; |
842 | } | 844 | } |
843 | 845 | ||
844 | struct sch_match_id { | 846 | static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data) |
845 | struct subchannel_id schid; | ||
846 | struct ccw_dev_id devid; | ||
847 | int rc; | ||
848 | }; | ||
849 | |||
850 | static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid, | ||
851 | void *data) | ||
852 | { | 847 | { |
853 | struct schib schib; | 848 | struct schib schib; |
854 | struct sch_match_id *match_id = data; | ||
855 | 849 | ||
856 | if (stsch_err(schid, &schib)) | 850 | if (stsch_err(schid, &schib)) |
857 | return -ENXIO; | 851 | return -ENXIO; |
858 | if (match_id && schib.pmcw.dnv && | ||
859 | (schib.pmcw.dev == match_id->devid.devno) && | ||
860 | (schid.ssid == match_id->devid.ssid)) { | ||
861 | match_id->schid = schid; | ||
862 | match_id->rc = 0; | ||
863 | } | ||
864 | if (!schib.pmcw.ena) | 852 | if (!schib.pmcw.ena) |
865 | return 0; | 853 | return 0; |
866 | switch(__disable_subchannel_easy(schid, &schib)) { | 854 | switch(__disable_subchannel_easy(schid, &schib)) { |
@@ -876,27 +864,111 @@ static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid, | |||
876 | return 0; | 864 | return 0; |
877 | } | 865 | } |
878 | 866 | ||
879 | static int clear_all_subchannels_and_match(struct ccw_dev_id *devid, | 867 | static atomic_t chpid_reset_count; |
880 | struct subchannel_id *schid) | 868 | |
869 | static void s390_reset_chpids_mcck_handler(void) | ||
870 | { | ||
871 | struct crw crw; | ||
872 | struct mci *mci; | ||
873 | |||
874 | /* Check for pending channel report word. */ | ||
875 | mci = (struct mci *)&S390_lowcore.mcck_interruption_code; | ||
876 | if (!mci->cp) | ||
877 | return; | ||
878 | /* Process channel report words. */ | ||
879 | while (stcrw(&crw) == 0) { | ||
880 | /* Check for responses to RCHP. */ | ||
881 | if (crw.slct && crw.rsc == CRW_RSC_CPATH) | ||
882 | atomic_dec(&chpid_reset_count); | ||
883 | } | ||
884 | } | ||
885 | |||
886 | #define RCHP_TIMEOUT (30 * USEC_PER_SEC) | ||
887 | static void css_reset(void) | ||
888 | { | ||
889 | int i, ret; | ||
890 | unsigned long long timeout; | ||
891 | |||
892 | /* Reset subchannels. */ | ||
893 | for_each_subchannel(__shutdown_subchannel_easy, NULL); | ||
894 | /* Reset channel paths. */ | ||
895 | s390_reset_mcck_handler = s390_reset_chpids_mcck_handler; | ||
896 | /* Enable channel report machine checks. */ | ||
897 | __ctl_set_bit(14, 28); | ||
898 | /* Temporarily reenable machine checks. */ | ||
899 | local_mcck_enable(); | ||
900 | for (i = 0; i <= __MAX_CHPID; i++) { | ||
901 | ret = rchp(i); | ||
902 | if ((ret == 0) || (ret == 2)) | ||
903 | /* | ||
904 | * rchp either succeeded, or another rchp is already | ||
905 | * in progress. In either case, we'll get a crw. | ||
906 | */ | ||
907 | atomic_inc(&chpid_reset_count); | ||
908 | } | ||
909 | /* Wait for machine check for all channel paths. */ | ||
910 | timeout = get_clock() + (RCHP_TIMEOUT << 12); | ||
911 | while (atomic_read(&chpid_reset_count) != 0) { | ||
912 | if (get_clock() > timeout) | ||
913 | break; | ||
914 | cpu_relax(); | ||
915 | } | ||
916 | /* Disable machine checks again. */ | ||
917 | local_mcck_disable(); | ||
918 | /* Disable channel report machine checks. */ | ||
919 | __ctl_clear_bit(14, 28); | ||
920 | s390_reset_mcck_handler = NULL; | ||
921 | } | ||
922 | |||
923 | static struct reset_call css_reset_call = { | ||
924 | .fn = css_reset, | ||
925 | }; | ||
926 | |||
927 | static int __init init_css_reset_call(void) | ||
928 | { | ||
929 | atomic_set(&chpid_reset_count, 0); | ||
930 | register_reset_call(&css_reset_call); | ||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | arch_initcall(init_css_reset_call); | ||
935 | |||
936 | struct sch_match_id { | ||
937 | struct subchannel_id schid; | ||
938 | struct ccw_dev_id devid; | ||
939 | int rc; | ||
940 | }; | ||
941 | |||
942 | static int __reipl_subchannel_match(struct subchannel_id schid, void *data) | ||
943 | { | ||
944 | struct schib schib; | ||
945 | struct sch_match_id *match_id = data; | ||
946 | |||
947 | if (stsch_err(schid, &schib)) | ||
948 | return -ENXIO; | ||
949 | if (schib.pmcw.dnv && | ||
950 | (schib.pmcw.dev == match_id->devid.devno) && | ||
951 | (schid.ssid == match_id->devid.ssid)) { | ||
952 | match_id->schid = schid; | ||
953 | match_id->rc = 0; | ||
954 | return 1; | ||
955 | } | ||
956 | return 0; | ||
957 | } | ||
958 | |||
959 | static int reipl_find_schid(struct ccw_dev_id *devid, | ||
960 | struct subchannel_id *schid) | ||
881 | { | 961 | { |
882 | struct sch_match_id match_id; | 962 | struct sch_match_id match_id; |
883 | 963 | ||
884 | match_id.devid = *devid; | 964 | match_id.devid = *devid; |
885 | match_id.rc = -ENODEV; | 965 | match_id.rc = -ENODEV; |
886 | local_irq_disable(); | 966 | for_each_subchannel(__reipl_subchannel_match, &match_id); |
887 | for_each_subchannel(__shutdown_subchannel_easy_and_match, &match_id); | ||
888 | if (match_id.rc == 0) | 967 | if (match_id.rc == 0) |
889 | *schid = match_id.schid; | 968 | *schid = match_id.schid; |
890 | return match_id.rc; | 969 | return match_id.rc; |
891 | } | 970 | } |
892 | 971 | ||
893 | |||
894 | void clear_all_subchannels(void) | ||
895 | { | ||
896 | local_irq_disable(); | ||
897 | for_each_subchannel(__shutdown_subchannel_easy_and_match, NULL); | ||
898 | } | ||
899 | |||
900 | extern void do_reipl_asm(__u32 schid); | 972 | extern void do_reipl_asm(__u32 schid); |
901 | 973 | ||
902 | /* Make sure all subchannels are quiet before we re-ipl an lpar. */ | 974 | /* Make sure all subchannels are quiet before we re-ipl an lpar. */ |
@@ -904,9 +976,9 @@ void reipl_ccw_dev(struct ccw_dev_id *devid) | |||
904 | { | 976 | { |
905 | struct subchannel_id schid; | 977 | struct subchannel_id schid; |
906 | 978 | ||
907 | if (clear_all_subchannels_and_match(devid, &schid)) | 979 | s390_reset_system(); |
980 | if (reipl_find_schid(devid, &schid) != 0) | ||
908 | panic("IPL Device not found\n"); | 981 | panic("IPL Device not found\n"); |
909 | cio_reset_channel_paths(); | ||
910 | do_reipl_asm(*((__u32*)&schid)); | 982 | do_reipl_asm(*((__u32*)&schid)); |
911 | } | 983 | } |
912 | 984 | ||