diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 139 |
1 files changed, 73 insertions, 66 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c6767f5a58b2..1ab5f6c36d9b 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/list.h> | 19 | #include <linux/list.h> |
20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
21 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
22 | #include <linux/delay.h> | ||
22 | #include <linux/timer.h> | 23 | #include <linux/timer.h> |
23 | #include <linux/kernel_stat.h> | 24 | #include <linux/kernel_stat.h> |
24 | 25 | ||
@@ -43,6 +44,10 @@ static DEFINE_SPINLOCK(recovery_lock); | |||
43 | static int recovery_phase; | 44 | static int recovery_phase; |
44 | static const unsigned long recovery_delay[] = { 3, 30, 300 }; | 45 | static const unsigned long recovery_delay[] = { 3, 30, 300 }; |
45 | 46 | ||
47 | static atomic_t ccw_device_init_count = ATOMIC_INIT(0); | ||
48 | static DECLARE_WAIT_QUEUE_HEAD(ccw_device_init_wq); | ||
49 | static struct bus_type ccw_bus_type; | ||
50 | |||
46 | /******************* bus type handling ***********************/ | 51 | /******************* bus type handling ***********************/ |
47 | 52 | ||
48 | /* The Linux driver model distinguishes between a bus type and | 53 | /* The Linux driver model distinguishes between a bus type and |
@@ -127,8 +132,6 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
127 | return ret; | 132 | return ret; |
128 | } | 133 | } |
129 | 134 | ||
130 | static struct bus_type ccw_bus_type; | ||
131 | |||
132 | static void io_subchannel_irq(struct subchannel *); | 135 | static void io_subchannel_irq(struct subchannel *); |
133 | static int io_subchannel_probe(struct subchannel *); | 136 | static int io_subchannel_probe(struct subchannel *); |
134 | static int io_subchannel_remove(struct subchannel *); | 137 | static int io_subchannel_remove(struct subchannel *); |
@@ -137,8 +140,6 @@ static int io_subchannel_sch_event(struct subchannel *, int); | |||
137 | static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, | 140 | static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, |
138 | int); | 141 | int); |
139 | static void recovery_func(unsigned long data); | 142 | static void recovery_func(unsigned long data); |
140 | wait_queue_head_t ccw_device_init_wq; | ||
141 | atomic_t ccw_device_init_count; | ||
142 | 143 | ||
143 | static struct css_device_id io_subchannel_ids[] = { | 144 | static struct css_device_id io_subchannel_ids[] = { |
144 | { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, | 145 | { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, |
@@ -191,10 +192,7 @@ int __init io_subchannel_init(void) | |||
191 | { | 192 | { |
192 | int ret; | 193 | int ret; |
193 | 194 | ||
194 | init_waitqueue_head(&ccw_device_init_wq); | ||
195 | atomic_set(&ccw_device_init_count, 0); | ||
196 | setup_timer(&recovery_timer, recovery_func, 0); | 195 | setup_timer(&recovery_timer, recovery_func, 0); |
197 | |||
198 | ret = bus_register(&ccw_bus_type); | 196 | ret = bus_register(&ccw_bus_type); |
199 | if (ret) | 197 | if (ret) |
200 | return ret; | 198 | return ret; |
@@ -1086,19 +1084,14 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1086 | dev_set_uevent_suppress(&sch->dev, 0); | 1084 | dev_set_uevent_suppress(&sch->dev, 0); |
1087 | kobject_uevent(&sch->dev.kobj, KOBJ_ADD); | 1085 | kobject_uevent(&sch->dev.kobj, KOBJ_ADD); |
1088 | cdev = sch_get_cdev(sch); | 1086 | cdev = sch_get_cdev(sch); |
1089 | cdev->dev.groups = ccwdev_attr_groups; | 1087 | rc = ccw_device_register(cdev); |
1090 | device_initialize(&cdev->dev); | 1088 | if (rc) { |
1091 | cdev->private->flags.initialized = 1; | 1089 | /* Release online reference. */ |
1092 | ccw_device_register(cdev); | 1090 | put_device(&cdev->dev); |
1093 | /* | 1091 | goto out_schedule; |
1094 | * Check if the device is already online. If it is | 1092 | } |
1095 | * the reference count needs to be corrected since we | 1093 | if (atomic_dec_and_test(&ccw_device_init_count)) |
1096 | * didn't obtain a reference in ccw_device_set_online. | 1094 | wake_up(&ccw_device_init_wq); |
1097 | */ | ||
1098 | if (cdev->private->state != DEV_STATE_NOT_OPER && | ||
1099 | cdev->private->state != DEV_STATE_OFFLINE && | ||
1100 | cdev->private->state != DEV_STATE_BOXED) | ||
1101 | get_device(&cdev->dev); | ||
1102 | return 0; | 1095 | return 0; |
1103 | } | 1096 | } |
1104 | io_subchannel_init_fields(sch); | 1097 | io_subchannel_init_fields(sch); |
@@ -1580,88 +1573,102 @@ out: | |||
1580 | } | 1573 | } |
1581 | 1574 | ||
1582 | #ifdef CONFIG_CCW_CONSOLE | 1575 | #ifdef CONFIG_CCW_CONSOLE |
1583 | static struct ccw_device console_cdev; | ||
1584 | static struct ccw_device_private console_private; | ||
1585 | static int console_cdev_in_use; | ||
1586 | |||
1587 | static DEFINE_SPINLOCK(ccw_console_lock); | ||
1588 | |||
1589 | spinlock_t * cio_get_console_lock(void) | ||
1590 | { | ||
1591 | return &ccw_console_lock; | ||
1592 | } | ||
1593 | |||
1594 | static int ccw_device_console_enable(struct ccw_device *cdev, | 1576 | static int ccw_device_console_enable(struct ccw_device *cdev, |
1595 | struct subchannel *sch) | 1577 | struct subchannel *sch) |
1596 | { | 1578 | { |
1597 | struct io_subchannel_private *io_priv = cio_get_console_priv(); | ||
1598 | int rc; | 1579 | int rc; |
1599 | 1580 | ||
1600 | /* Attach subchannel private data. */ | ||
1601 | memset(io_priv, 0, sizeof(*io_priv)); | ||
1602 | set_io_private(sch, io_priv); | ||
1603 | io_subchannel_init_fields(sch); | 1581 | io_subchannel_init_fields(sch); |
1604 | rc = cio_commit_config(sch); | 1582 | rc = cio_commit_config(sch); |
1605 | if (rc) | 1583 | if (rc) |
1606 | return rc; | 1584 | return rc; |
1607 | sch->driver = &io_subchannel_driver; | 1585 | sch->driver = &io_subchannel_driver; |
1608 | /* Initialize the ccw_device structure. */ | ||
1609 | cdev->dev.parent= &sch->dev; | ||
1610 | sch_set_cdev(sch, cdev); | 1586 | sch_set_cdev(sch, cdev); |
1611 | io_subchannel_recog(cdev, sch); | 1587 | io_subchannel_recog(cdev, sch); |
1612 | /* Now wait for the async. recognition to come to an end. */ | 1588 | /* Now wait for the async. recognition to come to an end. */ |
1613 | spin_lock_irq(cdev->ccwlock); | 1589 | spin_lock_irq(cdev->ccwlock); |
1614 | while (!dev_fsm_final_state(cdev)) | 1590 | while (!dev_fsm_final_state(cdev)) |
1615 | wait_cons_dev(); | 1591 | ccw_device_wait_idle(cdev); |
1616 | rc = -EIO; | 1592 | |
1617 | if (cdev->private->state != DEV_STATE_OFFLINE) | 1593 | /* Hold on to an extra reference while device is online. */ |
1594 | get_device(&cdev->dev); | ||
1595 | rc = ccw_device_online(cdev); | ||
1596 | if (rc) | ||
1618 | goto out_unlock; | 1597 | goto out_unlock; |
1619 | ccw_device_online(cdev); | 1598 | |
1620 | while (!dev_fsm_final_state(cdev)) | 1599 | while (!dev_fsm_final_state(cdev)) |
1621 | wait_cons_dev(); | 1600 | ccw_device_wait_idle(cdev); |
1622 | if (cdev->private->state != DEV_STATE_ONLINE) | 1601 | |
1623 | goto out_unlock; | 1602 | if (cdev->private->state == DEV_STATE_ONLINE) |
1624 | rc = 0; | 1603 | cdev->online = 1; |
1604 | else | ||
1605 | rc = -EIO; | ||
1625 | out_unlock: | 1606 | out_unlock: |
1626 | spin_unlock_irq(cdev->ccwlock); | 1607 | spin_unlock_irq(cdev->ccwlock); |
1608 | if (rc) /* Give up online reference since onlining failed. */ | ||
1609 | put_device(&cdev->dev); | ||
1627 | return rc; | 1610 | return rc; |
1628 | } | 1611 | } |
1629 | 1612 | ||
1630 | struct ccw_device * | 1613 | struct ccw_device *ccw_device_probe_console(void) |
1631 | ccw_device_probe_console(void) | ||
1632 | { | 1614 | { |
1615 | struct io_subchannel_private *io_priv; | ||
1616 | struct ccw_device *cdev; | ||
1633 | struct subchannel *sch; | 1617 | struct subchannel *sch; |
1634 | int ret; | 1618 | int ret; |
1635 | 1619 | ||
1636 | if (xchg(&console_cdev_in_use, 1) != 0) | ||
1637 | return ERR_PTR(-EBUSY); | ||
1638 | sch = cio_probe_console(); | 1620 | sch = cio_probe_console(); |
1639 | if (IS_ERR(sch)) { | 1621 | if (IS_ERR(sch)) |
1640 | console_cdev_in_use = 0; | 1622 | return ERR_CAST(sch); |
1641 | return (void *) sch; | 1623 | |
1624 | io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA); | ||
1625 | if (!io_priv) { | ||
1626 | put_device(&sch->dev); | ||
1627 | return ERR_PTR(-ENOMEM); | ||
1642 | } | 1628 | } |
1643 | memset(&console_cdev, 0, sizeof(struct ccw_device)); | 1629 | cdev = io_subchannel_create_ccwdev(sch); |
1644 | memset(&console_private, 0, sizeof(struct ccw_device_private)); | 1630 | if (IS_ERR(cdev)) { |
1645 | console_cdev.private = &console_private; | 1631 | put_device(&sch->dev); |
1646 | console_private.cdev = &console_cdev; | 1632 | kfree(io_priv); |
1647 | console_private.int_class = IRQIO_CIO; | 1633 | return cdev; |
1648 | ret = ccw_device_console_enable(&console_cdev, sch); | 1634 | } |
1635 | set_io_private(sch, io_priv); | ||
1636 | ret = ccw_device_console_enable(cdev, sch); | ||
1649 | if (ret) { | 1637 | if (ret) { |
1650 | cio_release_console(); | 1638 | set_io_private(sch, NULL); |
1651 | console_cdev_in_use = 0; | 1639 | put_device(&sch->dev); |
1640 | put_device(&cdev->dev); | ||
1641 | kfree(io_priv); | ||
1652 | return ERR_PTR(ret); | 1642 | return ERR_PTR(ret); |
1653 | } | 1643 | } |
1654 | console_cdev.online = 1; | 1644 | return cdev; |
1655 | return &console_cdev; | 1645 | } |
1646 | |||
1647 | /** | ||
1648 | * ccw_device_wait_idle() - busy wait for device to become idle | ||
1649 | * @cdev: ccw device | ||
1650 | * | ||
1651 | * Poll until activity control is zero, that is, no function or data | ||
1652 | * transfer is pending/active. | ||
1653 | * Called with device lock being held. | ||
1654 | */ | ||
1655 | void ccw_device_wait_idle(struct ccw_device *cdev) | ||
1656 | { | ||
1657 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
1658 | |||
1659 | while (1) { | ||
1660 | cio_tsch(sch); | ||
1661 | if (sch->schib.scsw.cmd.actl == 0) | ||
1662 | break; | ||
1663 | udelay_simple(100); | ||
1664 | } | ||
1656 | } | 1665 | } |
1657 | 1666 | ||
1658 | static int ccw_device_pm_restore(struct device *dev); | 1667 | static int ccw_device_pm_restore(struct device *dev); |
1659 | 1668 | ||
1660 | int ccw_device_force_console(void) | 1669 | int ccw_device_force_console(struct ccw_device *cdev) |
1661 | { | 1670 | { |
1662 | if (!console_cdev_in_use) | 1671 | return ccw_device_pm_restore(&cdev->dev); |
1663 | return -ENODEV; | ||
1664 | return ccw_device_pm_restore(&console_cdev.dev); | ||
1665 | } | 1672 | } |
1666 | EXPORT_SYMBOL_GPL(ccw_device_force_console); | 1673 | EXPORT_SYMBOL_GPL(ccw_device_force_console); |
1667 | #endif | 1674 | #endif |