diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-06-16 04:30:20 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-06-16 04:31:08 -0400 |
commit | 823d494ac11111064cf39abd4178ce299414c771 (patch) | |
tree | ede15540ad89c3412f16c24770408a4c34cc032c /drivers/s390 | |
parent | 03347e2592078a90df818670fddf97a33eec70fb (diff) |
[S390] pm: ccw bus power management callbacks
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/cmf.c | 5 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 237 | ||||
-rw-r--r-- | drivers/s390/cio/device.h | 3 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 96 | ||||
-rw-r--r-- | drivers/s390/cio/device_ops.c | 26 | ||||
-rw-r--r-- | drivers/s390/cio/io_sch.h | 1 |
6 files changed, 308 insertions, 60 deletions
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index dc98b2c63862..30f516111307 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c | |||
@@ -1204,6 +1204,11 @@ static ssize_t cmb_enable_store(struct device *dev, | |||
1204 | 1204 | ||
1205 | DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store); | 1205 | DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store); |
1206 | 1206 | ||
1207 | int ccw_set_cmf(struct ccw_device *cdev, int enable) | ||
1208 | { | ||
1209 | return cmbops->set(cdev, enable ? 2 : 0); | ||
1210 | } | ||
1211 | |||
1207 | /** | 1212 | /** |
1208 | * enable_cmf() - switch on the channel measurement for a specific device | 1213 | * enable_cmf() - switch on the channel measurement for a specific device |
1209 | * @cdev: The ccw device to be enabled | 1214 | * @cdev: The ccw device to be enabled |
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 35441fa16be1..228a6c314d84 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -1895,6 +1895,242 @@ static void ccw_device_shutdown(struct device *dev) | |||
1895 | disable_cmf(cdev); | 1895 | disable_cmf(cdev); |
1896 | } | 1896 | } |
1897 | 1897 | ||
1898 | static int ccw_device_pm_prepare(struct device *dev) | ||
1899 | { | ||
1900 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1901 | |||
1902 | if (work_pending(&cdev->private->kick_work)) | ||
1903 | return -EAGAIN; | ||
1904 | /* Fail while device is being set online/offline. */ | ||
1905 | if (atomic_read(&cdev->private->onoff)) | ||
1906 | return -EAGAIN; | ||
1907 | |||
1908 | if (cdev->online && cdev->drv && cdev->drv->prepare) | ||
1909 | return cdev->drv->prepare(cdev); | ||
1910 | |||
1911 | return 0; | ||
1912 | } | ||
1913 | |||
1914 | static void ccw_device_pm_complete(struct device *dev) | ||
1915 | { | ||
1916 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1917 | |||
1918 | if (cdev->online && cdev->drv && cdev->drv->complete) | ||
1919 | cdev->drv->complete(cdev); | ||
1920 | } | ||
1921 | |||
1922 | static int ccw_device_pm_freeze(struct device *dev) | ||
1923 | { | ||
1924 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1925 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
1926 | int ret, cm_enabled; | ||
1927 | |||
1928 | /* Fail suspend while device is in transistional state. */ | ||
1929 | if (!dev_fsm_final_state(cdev)) | ||
1930 | return -EAGAIN; | ||
1931 | if (!cdev->online) | ||
1932 | return 0; | ||
1933 | if (cdev->drv && cdev->drv->freeze) { | ||
1934 | ret = cdev->drv->freeze(cdev); | ||
1935 | if (ret) | ||
1936 | return ret; | ||
1937 | } | ||
1938 | |||
1939 | spin_lock_irq(sch->lock); | ||
1940 | cm_enabled = cdev->private->cmb != NULL; | ||
1941 | spin_unlock_irq(sch->lock); | ||
1942 | if (cm_enabled) { | ||
1943 | /* Don't have the css write on memory. */ | ||
1944 | ret = ccw_set_cmf(cdev, 0); | ||
1945 | if (ret) | ||
1946 | return ret; | ||
1947 | } | ||
1948 | /* From here on, disallow device driver I/O. */ | ||
1949 | spin_lock_irq(sch->lock); | ||
1950 | ret = cio_disable_subchannel(sch); | ||
1951 | spin_unlock_irq(sch->lock); | ||
1952 | |||
1953 | return ret; | ||
1954 | } | ||
1955 | |||
1956 | static int ccw_device_pm_thaw(struct device *dev) | ||
1957 | { | ||
1958 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1959 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
1960 | int ret, cm_enabled; | ||
1961 | |||
1962 | if (!cdev->online) | ||
1963 | return 0; | ||
1964 | |||
1965 | spin_lock_irq(sch->lock); | ||
1966 | /* Allow device driver I/O again. */ | ||
1967 | ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); | ||
1968 | cm_enabled = cdev->private->cmb != NULL; | ||
1969 | spin_unlock_irq(sch->lock); | ||
1970 | if (ret) | ||
1971 | return ret; | ||
1972 | |||
1973 | if (cm_enabled) { | ||
1974 | ret = ccw_set_cmf(cdev, 1); | ||
1975 | if (ret) | ||
1976 | return ret; | ||
1977 | } | ||
1978 | |||
1979 | if (cdev->drv && cdev->drv->thaw) | ||
1980 | ret = cdev->drv->thaw(cdev); | ||
1981 | |||
1982 | return ret; | ||
1983 | } | ||
1984 | |||
1985 | static void __ccw_device_pm_restore(struct ccw_device *cdev) | ||
1986 | { | ||
1987 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
1988 | int ret; | ||
1989 | |||
1990 | if (cio_is_console(sch->schid)) | ||
1991 | goto out; | ||
1992 | /* | ||
1993 | * While we were sleeping, devices may have gone or become | ||
1994 | * available again. Kick re-detection. | ||
1995 | */ | ||
1996 | spin_lock_irq(sch->lock); | ||
1997 | cdev->private->flags.resuming = 1; | ||
1998 | ret = ccw_device_recognition(cdev); | ||
1999 | spin_unlock_irq(sch->lock); | ||
2000 | if (ret) { | ||
2001 | CIO_MSG_EVENT(0, "Couldn't start recognition for device " | ||
2002 | "%s (ret=%d)\n", dev_name(&cdev->dev), ret); | ||
2003 | spin_lock_irq(sch->lock); | ||
2004 | cdev->private->state = DEV_STATE_DISCONNECTED; | ||
2005 | spin_unlock_irq(sch->lock); | ||
2006 | /* notify driver after the resume cb */ | ||
2007 | goto out; | ||
2008 | } | ||
2009 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || | ||
2010 | cdev->private->state == DEV_STATE_DISCONNECTED); | ||
2011 | |||
2012 | out: | ||
2013 | cdev->private->flags.resuming = 0; | ||
2014 | } | ||
2015 | |||
2016 | static int resume_handle_boxed(struct ccw_device *cdev) | ||
2017 | { | ||
2018 | cdev->private->state = DEV_STATE_BOXED; | ||
2019 | if (ccw_device_notify(cdev, CIO_BOXED)) | ||
2020 | return 0; | ||
2021 | ccw_device_schedule_sch_unregister(cdev); | ||
2022 | return -ENODEV; | ||
2023 | } | ||
2024 | |||
2025 | static int resume_handle_disc(struct ccw_device *cdev) | ||
2026 | { | ||
2027 | cdev->private->state = DEV_STATE_DISCONNECTED; | ||
2028 | if (ccw_device_notify(cdev, CIO_GONE)) | ||
2029 | return 0; | ||
2030 | ccw_device_schedule_sch_unregister(cdev); | ||
2031 | return -ENODEV; | ||
2032 | } | ||
2033 | |||
2034 | static int ccw_device_pm_restore(struct device *dev) | ||
2035 | { | ||
2036 | struct ccw_device *cdev = to_ccwdev(dev); | ||
2037 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
2038 | int ret = 0, cm_enabled; | ||
2039 | |||
2040 | __ccw_device_pm_restore(cdev); | ||
2041 | spin_lock_irq(sch->lock); | ||
2042 | if (cio_is_console(sch->schid)) { | ||
2043 | cio_enable_subchannel(sch, (u32)(addr_t)sch); | ||
2044 | spin_unlock_irq(sch->lock); | ||
2045 | goto out_restore; | ||
2046 | } | ||
2047 | cdev->private->flags.donotify = 0; | ||
2048 | /* check recognition results */ | ||
2049 | switch (cdev->private->state) { | ||
2050 | case DEV_STATE_OFFLINE: | ||
2051 | break; | ||
2052 | case DEV_STATE_BOXED: | ||
2053 | ret = resume_handle_boxed(cdev); | ||
2054 | spin_unlock_irq(sch->lock); | ||
2055 | if (ret) | ||
2056 | goto out; | ||
2057 | goto out_restore; | ||
2058 | case DEV_STATE_DISCONNECTED: | ||
2059 | goto out_disc_unlock; | ||
2060 | default: | ||
2061 | goto out_unreg_unlock; | ||
2062 | } | ||
2063 | /* check if the device id has changed */ | ||
2064 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { | ||
2065 | CIO_MSG_EVENT(0, "resume: sch %s: failed (devno changed from " | ||
2066 | "%04x to %04x)\n", dev_name(&sch->dev), | ||
2067 | cdev->private->dev_id.devno, | ||
2068 | sch->schib.pmcw.dev); | ||
2069 | goto out_unreg_unlock; | ||
2070 | } | ||
2071 | /* check if the device type has changed */ | ||
2072 | if (!ccw_device_test_sense_data(cdev)) { | ||
2073 | ccw_device_update_sense_data(cdev); | ||
2074 | PREPARE_WORK(&cdev->private->kick_work, | ||
2075 | ccw_device_do_unbind_bind); | ||
2076 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
2077 | ret = -ENODEV; | ||
2078 | goto out_unlock; | ||
2079 | } | ||
2080 | if (!cdev->online) { | ||
2081 | ret = 0; | ||
2082 | goto out_unlock; | ||
2083 | } | ||
2084 | ret = ccw_device_online(cdev); | ||
2085 | if (ret) | ||
2086 | goto out_disc_unlock; | ||
2087 | |||
2088 | cm_enabled = cdev->private->cmb != NULL; | ||
2089 | spin_unlock_irq(sch->lock); | ||
2090 | |||
2091 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); | ||
2092 | if (cdev->private->state != DEV_STATE_ONLINE) { | ||
2093 | spin_lock_irq(sch->lock); | ||
2094 | goto out_disc_unlock; | ||
2095 | } | ||
2096 | if (cm_enabled) { | ||
2097 | ret = ccw_set_cmf(cdev, 1); | ||
2098 | if (ret) { | ||
2099 | CIO_MSG_EVENT(2, "resume: cdev %s: cmf failed " | ||
2100 | "(rc=%d)\n", dev_name(&cdev->dev), ret); | ||
2101 | ret = 0; | ||
2102 | } | ||
2103 | } | ||
2104 | |||
2105 | out_restore: | ||
2106 | if (cdev->online && cdev->drv && cdev->drv->restore) | ||
2107 | ret = cdev->drv->restore(cdev); | ||
2108 | out: | ||
2109 | return ret; | ||
2110 | |||
2111 | out_disc_unlock: | ||
2112 | ret = resume_handle_disc(cdev); | ||
2113 | spin_unlock_irq(sch->lock); | ||
2114 | if (ret) | ||
2115 | return ret; | ||
2116 | goto out_restore; | ||
2117 | |||
2118 | out_unreg_unlock: | ||
2119 | ccw_device_schedule_sch_unregister(cdev); | ||
2120 | ret = -ENODEV; | ||
2121 | out_unlock: | ||
2122 | spin_unlock_irq(sch->lock); | ||
2123 | return ret; | ||
2124 | } | ||
2125 | |||
2126 | static struct dev_pm_ops ccw_pm_ops = { | ||
2127 | .prepare = ccw_device_pm_prepare, | ||
2128 | .complete = ccw_device_pm_complete, | ||
2129 | .freeze = ccw_device_pm_freeze, | ||
2130 | .thaw = ccw_device_pm_thaw, | ||
2131 | .restore = ccw_device_pm_restore, | ||
2132 | }; | ||
2133 | |||
1898 | struct bus_type ccw_bus_type = { | 2134 | struct bus_type ccw_bus_type = { |
1899 | .name = "ccw", | 2135 | .name = "ccw", |
1900 | .match = ccw_bus_match, | 2136 | .match = ccw_bus_match, |
@@ -1902,6 +2138,7 @@ struct bus_type ccw_bus_type = { | |||
1902 | .probe = ccw_device_probe, | 2138 | .probe = ccw_device_probe, |
1903 | .remove = ccw_device_remove, | 2139 | .remove = ccw_device_remove, |
1904 | .shutdown = ccw_device_shutdown, | 2140 | .shutdown = ccw_device_shutdown, |
2141 | .pm = &ccw_pm_ops, | ||
1905 | }; | 2142 | }; |
1906 | 2143 | ||
1907 | /** | 2144 | /** |
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index f1cbbd94ad4e..e3975107a578 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h | |||
@@ -87,6 +87,8 @@ int ccw_device_is_orphan(struct ccw_device *); | |||
87 | int ccw_device_recognition(struct ccw_device *); | 87 | int ccw_device_recognition(struct ccw_device *); |
88 | int ccw_device_online(struct ccw_device *); | 88 | int ccw_device_online(struct ccw_device *); |
89 | int ccw_device_offline(struct ccw_device *); | 89 | int ccw_device_offline(struct ccw_device *); |
90 | void ccw_device_update_sense_data(struct ccw_device *); | ||
91 | int ccw_device_test_sense_data(struct ccw_device *); | ||
90 | void ccw_device_schedule_sch_unregister(struct ccw_device *); | 92 | void ccw_device_schedule_sch_unregister(struct ccw_device *); |
91 | int ccw_purge_blacklisted(void); | 93 | int ccw_purge_blacklisted(void); |
92 | 94 | ||
@@ -133,5 +135,6 @@ extern struct bus_type ccw_bus_type; | |||
133 | void retry_set_schib(struct ccw_device *cdev); | 135 | void retry_set_schib(struct ccw_device *cdev); |
134 | void cmf_retry_copy_block(struct ccw_device *); | 136 | void cmf_retry_copy_block(struct ccw_device *); |
135 | int cmf_reenable(struct ccw_device *); | 137 | int cmf_reenable(struct ccw_device *); |
138 | int ccw_set_cmf(struct ccw_device *cdev, int enable); | ||
136 | extern struct device_attribute dev_attr_cmb_enable; | 139 | extern struct device_attribute dev_attr_cmb_enable; |
137 | #endif | 140 | #endif |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index e46049261561..3db88c52d287 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -177,29 +177,21 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) | |||
177 | panic("Can't stop i/o on subchannel.\n"); | 177 | panic("Can't stop i/o on subchannel.\n"); |
178 | } | 178 | } |
179 | 179 | ||
180 | static int | 180 | void ccw_device_update_sense_data(struct ccw_device *cdev) |
181 | ccw_device_handle_oper(struct ccw_device *cdev) | ||
182 | { | 181 | { |
183 | struct subchannel *sch; | 182 | memset(&cdev->id, 0, sizeof(cdev->id)); |
183 | cdev->id.cu_type = cdev->private->senseid.cu_type; | ||
184 | cdev->id.cu_model = cdev->private->senseid.cu_model; | ||
185 | cdev->id.dev_type = cdev->private->senseid.dev_type; | ||
186 | cdev->id.dev_model = cdev->private->senseid.dev_model; | ||
187 | } | ||
184 | 188 | ||
185 | sch = to_subchannel(cdev->dev.parent); | 189 | int ccw_device_test_sense_data(struct ccw_device *cdev) |
186 | cdev->private->flags.recog_done = 1; | 190 | { |
187 | /* | 191 | return cdev->id.cu_type == cdev->private->senseid.cu_type && |
188 | * Check if cu type and device type still match. If | 192 | cdev->id.cu_model == cdev->private->senseid.cu_model && |
189 | * not, it is certainly another device and we have to | 193 | cdev->id.dev_type == cdev->private->senseid.dev_type && |
190 | * de- and re-register. | 194 | cdev->id.dev_model == cdev->private->senseid.dev_model; |
191 | */ | ||
192 | if (cdev->id.cu_type != cdev->private->senseid.cu_type || | ||
193 | cdev->id.cu_model != cdev->private->senseid.cu_model || | ||
194 | cdev->id.dev_type != cdev->private->senseid.dev_type || | ||
195 | cdev->id.dev_model != cdev->private->senseid.dev_model) { | ||
196 | PREPARE_WORK(&cdev->private->kick_work, | ||
197 | ccw_device_do_unbind_bind); | ||
198 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
199 | return 0; | ||
200 | } | ||
201 | cdev->private->flags.donotify = 1; | ||
202 | return 1; | ||
203 | } | 195 | } |
204 | 196 | ||
205 | /* | 197 | /* |
@@ -233,7 +225,7 @@ static void | |||
233 | ccw_device_recog_done(struct ccw_device *cdev, int state) | 225 | ccw_device_recog_done(struct ccw_device *cdev, int state) |
234 | { | 226 | { |
235 | struct subchannel *sch; | 227 | struct subchannel *sch; |
236 | int notify, old_lpm, same_dev; | 228 | int old_lpm; |
237 | 229 | ||
238 | sch = to_subchannel(cdev->dev.parent); | 230 | sch = to_subchannel(cdev->dev.parent); |
239 | 231 | ||
@@ -263,8 +255,12 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) | |||
263 | wake_up(&cdev->private->wait_q); | 255 | wake_up(&cdev->private->wait_q); |
264 | return; | 256 | return; |
265 | } | 257 | } |
266 | notify = 0; | 258 | if (cdev->private->flags.resuming) { |
267 | same_dev = 0; /* Keep the compiler quiet... */ | 259 | cdev->private->state = state; |
260 | cdev->private->flags.recog_done = 1; | ||
261 | wake_up(&cdev->private->wait_q); | ||
262 | return; | ||
263 | } | ||
268 | switch (state) { | 264 | switch (state) { |
269 | case DEV_STATE_NOT_OPER: | 265 | case DEV_STATE_NOT_OPER: |
270 | CIO_MSG_EVENT(2, "SenseID : unknown device %04x on " | 266 | CIO_MSG_EVENT(2, "SenseID : unknown device %04x on " |
@@ -273,34 +269,31 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) | |||
273 | sch->schid.ssid, sch->schid.sch_no); | 269 | sch->schid.ssid, sch->schid.sch_no); |
274 | break; | 270 | break; |
275 | case DEV_STATE_OFFLINE: | 271 | case DEV_STATE_OFFLINE: |
276 | if (cdev->online) { | 272 | if (!cdev->online) { |
277 | same_dev = ccw_device_handle_oper(cdev); | 273 | ccw_device_update_sense_data(cdev); |
278 | notify = 1; | 274 | /* Issue device info message. */ |
275 | CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: " | ||
276 | "CU Type/Mod = %04X/%02X, Dev Type/Mod " | ||
277 | "= %04X/%02X\n", | ||
278 | cdev->private->dev_id.ssid, | ||
279 | cdev->private->dev_id.devno, | ||
280 | cdev->id.cu_type, cdev->id.cu_model, | ||
281 | cdev->id.dev_type, cdev->id.dev_model); | ||
282 | break; | ||
279 | } | 283 | } |
280 | /* fill out sense information */ | 284 | cdev->private->state = DEV_STATE_OFFLINE; |
281 | memset(&cdev->id, 0, sizeof(cdev->id)); | 285 | cdev->private->flags.recog_done = 1; |
282 | cdev->id.cu_type = cdev->private->senseid.cu_type; | 286 | if (ccw_device_test_sense_data(cdev)) { |
283 | cdev->id.cu_model = cdev->private->senseid.cu_model; | 287 | cdev->private->flags.donotify = 1; |
284 | cdev->id.dev_type = cdev->private->senseid.dev_type; | 288 | ccw_device_online(cdev); |
285 | cdev->id.dev_model = cdev->private->senseid.dev_model; | 289 | wake_up(&cdev->private->wait_q); |
286 | if (notify) { | 290 | } else { |
287 | cdev->private->state = DEV_STATE_OFFLINE; | 291 | ccw_device_update_sense_data(cdev); |
288 | if (same_dev) { | 292 | PREPARE_WORK(&cdev->private->kick_work, |
289 | /* Get device online again. */ | 293 | ccw_device_do_unbind_bind); |
290 | ccw_device_online(cdev); | 294 | queue_work(ccw_device_work, &cdev->private->kick_work); |
291 | wake_up(&cdev->private->wait_q); | ||
292 | } | ||
293 | return; | ||
294 | } | 295 | } |
295 | /* Issue device info message. */ | 296 | return; |
296 | CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: " | ||
297 | "CU Type/Mod = %04X/%02X, Dev Type/Mod = " | ||
298 | "%04X/%02X\n", | ||
299 | cdev->private->dev_id.ssid, | ||
300 | cdev->private->dev_id.devno, | ||
301 | cdev->id.cu_type, cdev->id.cu_model, | ||
302 | cdev->id.dev_type, cdev->id.dev_model); | ||
303 | break; | ||
304 | case DEV_STATE_BOXED: | 297 | case DEV_STATE_BOXED: |
305 | CIO_MSG_EVENT(0, "SenseID : boxed device %04x on " | 298 | CIO_MSG_EVENT(0, "SenseID : boxed device %04x on " |
306 | " subchannel 0.%x.%04x\n", | 299 | " subchannel 0.%x.%04x\n", |
@@ -502,9 +495,6 @@ ccw_device_recognition(struct ccw_device *cdev) | |||
502 | struct subchannel *sch; | 495 | struct subchannel *sch; |
503 | int ret; | 496 | int ret; |
504 | 497 | ||
505 | if ((cdev->private->state != DEV_STATE_NOT_OPER) && | ||
506 | (cdev->private->state != DEV_STATE_BOXED)) | ||
507 | return -EINVAL; | ||
508 | sch = to_subchannel(cdev->dev.parent); | 498 | sch = to_subchannel(cdev->dev.parent); |
509 | ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); | 499 | ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); |
510 | if (ret != 0) | 500 | if (ret != 0) |
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index bf0a24af39a0..2d0efee8a290 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c | |||
@@ -1,10 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/cio/device_ops.c | 2 | * Copyright IBM Corp. 2002, 2009 |
3 | * | 3 | * |
4 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | 4 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) |
5 | * IBM Corporation | 5 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
6 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) | ||
7 | * Cornelia Huck (cornelia.huck@de.ibm.com) | ||
8 | */ | 6 | */ |
9 | #include <linux/module.h> | 7 | #include <linux/module.h> |
10 | #include <linux/init.h> | 8 | #include <linux/init.h> |
@@ -116,12 +114,15 @@ int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) | |||
116 | 114 | ||
117 | if (!cdev || !cdev->dev.parent) | 115 | if (!cdev || !cdev->dev.parent) |
118 | return -ENODEV; | 116 | return -ENODEV; |
117 | sch = to_subchannel(cdev->dev.parent); | ||
118 | if (!sch->schib.pmcw.ena) | ||
119 | return -EINVAL; | ||
119 | if (cdev->private->state == DEV_STATE_NOT_OPER) | 120 | if (cdev->private->state == DEV_STATE_NOT_OPER) |
120 | return -ENODEV; | 121 | return -ENODEV; |
121 | if (cdev->private->state != DEV_STATE_ONLINE && | 122 | if (cdev->private->state != DEV_STATE_ONLINE && |
122 | cdev->private->state != DEV_STATE_W4SENSE) | 123 | cdev->private->state != DEV_STATE_W4SENSE) |
123 | return -EINVAL; | 124 | return -EINVAL; |
124 | sch = to_subchannel(cdev->dev.parent); | 125 | |
125 | ret = cio_clear(sch); | 126 | ret = cio_clear(sch); |
126 | if (ret == 0) | 127 | if (ret == 0) |
127 | cdev->private->intparm = intparm; | 128 | cdev->private->intparm = intparm; |
@@ -162,6 +163,8 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, | |||
162 | if (!cdev || !cdev->dev.parent) | 163 | if (!cdev || !cdev->dev.parent) |
163 | return -ENODEV; | 164 | return -ENODEV; |
164 | sch = to_subchannel(cdev->dev.parent); | 165 | sch = to_subchannel(cdev->dev.parent); |
166 | if (!sch->schib.pmcw.ena) | ||
167 | return -EINVAL; | ||
165 | if (cdev->private->state == DEV_STATE_NOT_OPER) | 168 | if (cdev->private->state == DEV_STATE_NOT_OPER) |
166 | return -ENODEV; | 169 | return -ENODEV; |
167 | if (cdev->private->state == DEV_STATE_VERIFY || | 170 | if (cdev->private->state == DEV_STATE_VERIFY || |
@@ -337,12 +340,15 @@ int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) | |||
337 | 340 | ||
338 | if (!cdev || !cdev->dev.parent) | 341 | if (!cdev || !cdev->dev.parent) |
339 | return -ENODEV; | 342 | return -ENODEV; |
343 | sch = to_subchannel(cdev->dev.parent); | ||
344 | if (!sch->schib.pmcw.ena) | ||
345 | return -EINVAL; | ||
340 | if (cdev->private->state == DEV_STATE_NOT_OPER) | 346 | if (cdev->private->state == DEV_STATE_NOT_OPER) |
341 | return -ENODEV; | 347 | return -ENODEV; |
342 | if (cdev->private->state != DEV_STATE_ONLINE && | 348 | if (cdev->private->state != DEV_STATE_ONLINE && |
343 | cdev->private->state != DEV_STATE_W4SENSE) | 349 | cdev->private->state != DEV_STATE_W4SENSE) |
344 | return -EINVAL; | 350 | return -EINVAL; |
345 | sch = to_subchannel(cdev->dev.parent); | 351 | |
346 | ret = cio_halt(sch); | 352 | ret = cio_halt(sch); |
347 | if (ret == 0) | 353 | if (ret == 0) |
348 | cdev->private->intparm = intparm; | 354 | cdev->private->intparm = intparm; |
@@ -369,6 +375,8 @@ int ccw_device_resume(struct ccw_device *cdev) | |||
369 | if (!cdev || !cdev->dev.parent) | 375 | if (!cdev || !cdev->dev.parent) |
370 | return -ENODEV; | 376 | return -ENODEV; |
371 | sch = to_subchannel(cdev->dev.parent); | 377 | sch = to_subchannel(cdev->dev.parent); |
378 | if (!sch->schib.pmcw.ena) | ||
379 | return -EINVAL; | ||
372 | if (cdev->private->state == DEV_STATE_NOT_OPER) | 380 | if (cdev->private->state == DEV_STATE_NOT_OPER) |
373 | return -ENODEV; | 381 | return -ENODEV; |
374 | if (cdev->private->state != DEV_STATE_ONLINE || | 382 | if (cdev->private->state != DEV_STATE_ONLINE || |
@@ -580,6 +588,8 @@ int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw, | |||
580 | int rc; | 588 | int rc; |
581 | 589 | ||
582 | sch = to_subchannel(cdev->dev.parent); | 590 | sch = to_subchannel(cdev->dev.parent); |
591 | if (!sch->schib.pmcw.ena) | ||
592 | return -EINVAL; | ||
583 | if (cdev->private->state != DEV_STATE_ONLINE) | 593 | if (cdev->private->state != DEV_STATE_ONLINE) |
584 | return -EIO; | 594 | return -EIO; |
585 | /* Adjust requested path mask to excluded varied off paths. */ | 595 | /* Adjust requested path mask to excluded varied off paths. */ |
@@ -669,6 +679,8 @@ int ccw_device_tm_intrg(struct ccw_device *cdev) | |||
669 | { | 679 | { |
670 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 680 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
671 | 681 | ||
682 | if (!sch->schib.pmcw.ena) | ||
683 | return -EINVAL; | ||
672 | if (cdev->private->state != DEV_STATE_ONLINE) | 684 | if (cdev->private->state != DEV_STATE_ONLINE) |
673 | return -EIO; | 685 | return -EIO; |
674 | if (!scsw_is_tm(&sch->schib.scsw) || | 686 | if (!scsw_is_tm(&sch->schib.scsw) || |
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index c4f3e7c9a854..0b8f381bd20e 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h | |||
@@ -107,6 +107,7 @@ struct ccw_device_private { | |||
107 | unsigned int recog_done:1; /* dev. recog. complete */ | 107 | unsigned int recog_done:1; /* dev. recog. complete */ |
108 | unsigned int fake_irb:1; /* deliver faked irb */ | 108 | unsigned int fake_irb:1; /* deliver faked irb */ |
109 | unsigned int intretry:1; /* retry internal operation */ | 109 | unsigned int intretry:1; /* retry internal operation */ |
110 | unsigned int resuming:1; /* recognition while resume */ | ||
110 | } __attribute__((packed)) flags; | 111 | } __attribute__((packed)) flags; |
111 | unsigned long intparm; /* user interruption parameter */ | 112 | unsigned long intparm; /* user interruption parameter */ |
112 | struct qdio_irq *qdio_data; | 113 | struct qdio_irq *qdio_data; |