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/cio/device.c | |
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/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 237 |
1 files changed, 237 insertions, 0 deletions
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 | /** |