aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device.c
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2009-06-16 04:30:20 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-06-16 04:31:08 -0400
commit823d494ac11111064cf39abd4178ce299414c771 (patch)
treeede15540ad89c3412f16c24770408a4c34cc032c /drivers/s390/cio/device.c
parent03347e2592078a90df818670fddf97a33eec70fb (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.c237
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
1898static 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
1914static 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
1922static 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
1956static 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
1985static 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
2012out:
2013 cdev->private->flags.resuming = 0;
2014}
2015
2016static 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
2025static 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
2034static 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
2105out_restore:
2106 if (cdev->online && cdev->drv && cdev->drv->restore)
2107 ret = cdev->drv->restore(cdev);
2108out:
2109 return ret;
2110
2111out_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
2118out_unreg_unlock:
2119 ccw_device_schedule_sch_unregister(cdev);
2120 ret = -ENODEV;
2121out_unlock:
2122 spin_unlock_irq(sch->lock);
2123 return ret;
2124}
2125
2126static 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
1898struct bus_type ccw_bus_type = { 2134struct 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/**