X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fcio%2Fdevice.c;h=8e3053c2a451b4f5dc84d5b7fb77df52119d124c;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=7b6f85b3e42cbf7388dd3469ea8ba9028f262497;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7b6f85b3e..8e3053c2a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1,12 +1,11 @@ /* * drivers/s390/cio/device.c * bus driver for ccw devices - * $Revision: 1.129 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Arnd Bergmann (arndb@de.ibm.com) - * Cornelia Huck (cohuck@de.ibm.com) + * Cornelia Huck (cornelia.huck@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) */ #include @@ -22,6 +21,7 @@ #include #include +#include /* HZ */ #include "cio.h" #include "css.h" @@ -58,7 +58,7 @@ ccw_bus_match (struct device * dev, struct device_driver * drv) * Heavily modeled on pci and usb hotplug. */ static int -ccw_hotplug (struct device *dev, char **envp, int num_envp, +ccw_uevent (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { struct ccw_device *cdev = to_ccwdev(dev); @@ -106,33 +106,29 @@ ccw_hotplug (struct device *dev, char **envp, int num_envp, return 0; } -struct bus_type ccw_bus_type = { - .name = "ccw", - .match = &ccw_bus_match, - .hotplug = &ccw_hotplug, -}; +struct bus_type ccw_bus_type; -static int io_subchannel_probe (struct device *); -static int io_subchannel_remove (struct device *); +static int io_subchannel_probe (struct subchannel *); +static int io_subchannel_remove (struct subchannel *); void io_subchannel_irq (struct device *); static int io_subchannel_notify(struct device *, int); static void io_subchannel_verify(struct device *); static void io_subchannel_ioterm(struct device *); -static void io_subchannel_shutdown(struct device *); +static void io_subchannel_shutdown(struct subchannel *); struct css_driver io_subchannel_driver = { .subchannel_type = SUBCHANNEL_TYPE_IO, .drv = { .name = "io_subchannel", .bus = &css_bus_type, - .probe = &io_subchannel_probe, - .remove = &io_subchannel_remove, - .shutdown = &io_subchannel_shutdown, }, .irq = io_subchannel_irq, .notify = io_subchannel_notify, .verify = io_subchannel_verify, .termination = io_subchannel_ioterm, + .probe = io_subchannel_probe, + .remove = io_subchannel_remove, + .shutdown = io_subchannel_shutdown, }; struct workqueue_struct *ccw_device_work; @@ -204,7 +200,7 @@ module_exit(cleanup_ccw_bus_type); * TODO: Split chpids and pimpampom up? Where is "in use" in the tree? */ static ssize_t -chpids_show (struct device * dev, char * buf) +chpids_show (struct device * dev, struct device_attribute *attr, char * buf) { struct subchannel *sch = to_subchannel(dev); struct ssd_info *ssd = &sch->ssd_info; @@ -219,7 +215,7 @@ chpids_show (struct device * dev, char * buf) } static ssize_t -pimpampom_show (struct device * dev, char * buf) +pimpampom_show (struct device * dev, struct device_attribute *attr, char * buf) { struct subchannel *sch = to_subchannel(dev); struct pmcw *pmcw = &sch->schib.pmcw; @@ -229,7 +225,7 @@ pimpampom_show (struct device * dev, char * buf) } static ssize_t -devtype_show (struct device *dev, char *buf) +devtype_show (struct device *dev, struct device_attribute *attr, char *buf) { struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); @@ -242,7 +238,7 @@ devtype_show (struct device *dev, char *buf) } static ssize_t -cutype_show (struct device *dev, char *buf) +cutype_show (struct device *dev, struct device_attribute *attr, char *buf) { struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); @@ -252,7 +248,24 @@ cutype_show (struct device *dev, char *buf) } static ssize_t -online_show (struct device *dev, char *buf) +modalias_show (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct ccw_device_id *id = &(cdev->id); + int ret; + + ret = sprintf(buf, "ccw:t%04Xm%02X", + id->cu_type, id->cu_model); + if (id->dev_type != 0) + ret += sprintf(buf + ret, "dt%04Xdm%02X\n", + id->dev_type, id->dev_model); + else + ret += sprintf(buf + ret, "dtdm\n"); + return ret; +} + +static ssize_t +online_show (struct device *dev, struct device_attribute *attr, char *buf) { struct ccw_device *cdev = to_ccwdev(dev); @@ -293,6 +306,14 @@ ccw_device_set_offline(struct ccw_device *cdev) cdev->online = 0; spin_lock_irq(cdev->ccwlock); ret = ccw_device_offline(cdev); + if (ret == -ENODEV) { + if (cdev->private->state != DEV_STATE_NOT_OPER) { + cdev->private->state = DEV_STATE_OFFLINE; + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); + } + spin_unlock_irq(cdev->ccwlock); + return ret; + } spin_unlock_irq(cdev->ccwlock); if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); @@ -338,17 +359,17 @@ ccw_device_set_online(struct ccw_device *cdev) else pr_debug("ccw_device_offline returned %d, device %s\n", ret, cdev->dev.bus_id); - return (ret = 0) ? -ENODEV : ret; + return (ret == 0) ? -ENODEV : ret; } static ssize_t -online_store (struct device *dev, const char *buf, size_t count) +online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ccw_device *cdev = to_ccwdev(dev); int i, force, ret; char *tmp; - if (atomic_compare_and_swap(0, 1, &cdev->private->onoff)) + if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; if (cdev->drv && !try_module_get(cdev->drv->owner)) { @@ -414,7 +435,7 @@ online_store (struct device *dev, const char *buf, size_t count) } static ssize_t -available_show (struct device *dev, char *buf) +available_show (struct device *dev, struct device_attribute *attr, char *buf) { struct ccw_device *cdev = to_ccwdev(dev); struct subchannel *sch; @@ -440,6 +461,7 @@ static DEVICE_ATTR(chpids, 0444, chpids_show, NULL); static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL); static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); static DEVICE_ATTR(cutype, 0444, cutype_show, NULL); +static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); extern struct device_attribute dev_attr_cmb_enable; static DEVICE_ATTR(availability, 0444, available_show, NULL); @@ -463,6 +485,7 @@ subchannel_add_files (struct device *dev) static struct attribute * ccwdev_attrs[] = { &dev_attr_devtype.attr, &dev_attr_cutype.attr, + &dev_attr_modalias.attr, &dev_attr_online.attr, &dev_attr_cmb_enable.attr, &dev_attr_availability.attr, @@ -506,36 +529,43 @@ ccw_device_register(struct ccw_device *cdev) return ret; } +struct match_data { + unsigned int devno; + unsigned int ssid; + struct ccw_device * sibling; +}; + +static int +match_devno(struct device * dev, void * data) +{ + struct match_data * d = (struct match_data *)data; + struct ccw_device * cdev; + + cdev = to_ccwdev(dev); + if ((cdev->private->state == DEV_STATE_DISCONNECTED) && + (cdev->private->devno == d->devno) && + (cdev->private->ssid == d->ssid) && + (cdev != d->sibling)) { + cdev->private->state = DEV_STATE_NOT_OPER; + return 1; + } + return 0; +} + static struct ccw_device * -get_disc_ccwdev_by_devno(unsigned int devno, struct ccw_device *sibling) +get_disc_ccwdev_by_devno(unsigned int devno, unsigned int ssid, + struct ccw_device *sibling) { - struct ccw_device *cdev; - struct list_head *entry; struct device *dev; + struct match_data data = { + .devno = devno, + .ssid = ssid, + .sibling = sibling, + }; - if (!get_bus(&ccw_bus_type)) - return NULL; - down_read(&ccw_bus_type.subsys.rwsem); - cdev = NULL; - list_for_each(entry, &ccw_bus_type.devices.list) { - dev = get_device(container_of(entry, - struct device, bus_list)); - if (!dev) - continue; - cdev = to_ccwdev(dev); - if ((cdev->private->state == DEV_STATE_DISCONNECTED) && - (cdev->private->devno == devno) && - (cdev != sibling)) { - cdev->private->state = DEV_STATE_NOT_OPER; - break; - } - put_device(dev); - cdev = NULL; - } - up_read(&ccw_bus_type.subsys.rwsem); - put_bus(&ccw_bus_type); + dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno); - return cdev; + return dev ? to_ccwdev(dev) : NULL; } static void @@ -585,13 +615,13 @@ ccw_device_do_unreg_rereg(void *data) need_rename = 1; other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev, - cdev); + sch->schid.ssid, cdev); if (other_cdev) { struct subchannel *other_sch; other_sch = to_subchannel(other_cdev->dev.parent); if (get_device(&other_sch->dev)) { - stsch(other_sch->irq, &other_sch->schib); + stsch(other_sch->schid, &other_sch->schib); if (other_sch->schib.pmcw.dnv) { other_sch->schib.pmcw.intparm = 0; cio_modify(other_sch); @@ -608,8 +638,8 @@ ccw_device_do_unreg_rereg(void *data) if (test_and_clear_bit(1, &cdev->private->registered)) device_del(&cdev->dev); if (need_rename) - snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x", - sch->schib.pmcw.dev); + snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", + sch->schid.ssid, sch->schib.pmcw.dev); PREPARE_WORK(&cdev->private->kick_work, ccw_device_add_changed, (void *)cdev); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -639,7 +669,7 @@ io_subchannel_register(void *data) cdev = (struct ccw_device *) data; sch = to_subchannel(cdev->dev.parent); - if (!list_empty(&sch->dev.children)) { + if (klist_node_attached(&cdev->dev.knode_parent)) { bus_rescan_devices(&ccw_bus_type); goto out; } @@ -738,18 +768,20 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) sch->dev.driver_data = cdev; sch->driver = &io_subchannel_driver; cdev->ccwlock = &sch->lock; + /* Init private data. */ priv = cdev->private; priv->devno = sch->schib.pmcw.dev; - priv->irq = sch->irq; + priv->ssid = sch->schid.ssid; + priv->sch_no = sch->schid.sch_no; priv->state = DEV_STATE_NOT_OPER; INIT_LIST_HEAD(&priv->cmb_list); init_waitqueue_head(&priv->wait_q); init_timer(&priv->timer); /* Set an initial name for the device. */ - snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x", - sch->schib.pmcw.dev); + snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", + sch->schid.ssid, sch->schib.pmcw.dev); /* Increase counter of devices currently in recognition. */ atomic_inc(&ccw_device_init_count); @@ -766,14 +798,12 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) } static int -io_subchannel_probe (struct device *pdev) +io_subchannel_probe (struct subchannel *sch) { - struct subchannel *sch; struct ccw_device *cdev; int rc; unsigned long flags; - sch = to_subchannel(pdev); if (sch->dev.driver_data) { /* * This subchannel already has an associated ccw_device. @@ -796,20 +826,18 @@ io_subchannel_probe (struct device *pdev) get_device(&cdev->dev); return 0; } - cdev = kmalloc (sizeof(*cdev), GFP_KERNEL); + cdev = kzalloc (sizeof(*cdev), GFP_KERNEL); if (!cdev) return -ENOMEM; - memset(cdev, 0, sizeof(struct ccw_device)); - cdev->private = kmalloc(sizeof(struct ccw_device_private), + cdev->private = kzalloc(sizeof(struct ccw_device_private), GFP_KERNEL | GFP_DMA); if (!cdev->private) { kfree(cdev); return -ENOMEM; } - memset(cdev->private, 0, sizeof(struct ccw_device_private)); atomic_set(&cdev->private->onoff, 0); cdev->dev = (struct device) { - .parent = pdev, + .parent = &sch->dev, .release = ccw_device_release, }; INIT_LIST_HEAD(&cdev->private->kick_work.entry); @@ -822,7 +850,7 @@ io_subchannel_probe (struct device *pdev) return -ENODEV; } - rc = io_subchannel_recog(cdev, to_subchannel(pdev)); + rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(&sch->lock, flags); sch->dev.driver_data = NULL; @@ -846,17 +874,17 @@ ccw_device_unregister(void *data) } static int -io_subchannel_remove (struct device *dev) +io_subchannel_remove (struct subchannel *sch) { struct ccw_device *cdev; unsigned long flags; - if (!dev->driver_data) + if (!sch->dev.driver_data) return 0; - cdev = dev->driver_data; + cdev = sch->dev.driver_data; /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); - dev->driver_data = NULL; + sch->dev.driver_data = NULL; cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); /* @@ -911,16 +939,14 @@ io_subchannel_ioterm(struct device *dev) } static void -io_subchannel_shutdown(struct device *dev) +io_subchannel_shutdown(struct subchannel *sch) { - struct subchannel *sch; struct ccw_device *cdev; int ret; - sch = to_subchannel(dev); - cdev = dev->driver_data; + cdev = sch->dev.driver_data; - if (cio_is_console(sch->irq)) + if (cio_is_console(sch->schid)) return; if (!sch->schib.pmcw.ena) /* Nothing to do. */ @@ -955,10 +981,6 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch) cdev->dev = (struct device) { .parent = &sch->dev, }; - /* Initialize the subchannel structure */ - sch->dev.parent = &css_bus_device; - sch->dev.bus = &css_bus_type; - rc = io_subchannel_recog(cdev, sch); if (rc) return rc; @@ -988,7 +1010,7 @@ ccw_device_probe_console(void) int ret; if (xchg(&console_cdev_in_use, 1) != 0) - return NULL; + return ERR_PTR(-EBUSY); sch = cio_probe_console(); if (IS_ERR(sch)) { console_cdev_in_use = 0; @@ -1011,30 +1033,29 @@ ccw_device_probe_console(void) /* * get ccw_device matching the busid, but only if owned by cdrv */ +static int +__ccwdev_check_busid(struct device *dev, void *id) +{ + char *bus_id; + + bus_id = (char *)id; + + return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0); +} + + struct ccw_device * get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id) { - struct device *d, *dev; + struct device *dev; struct device_driver *drv; drv = get_driver(&cdrv->driver); if (!drv) - return 0; - - down_read(&drv->bus->subsys.rwsem); - - dev = NULL; - list_for_each_entry(d, &drv->devices, driver_list) { - dev = get_device(d); + return NULL; - if (dev && !strncmp(bus_id, dev->bus_id, BUS_ID_SIZE)) - break; - else if (dev) { - put_device(dev); - dev = NULL; - } - } - up_read(&drv->bus->subsys.rwsem); + dev = driver_find_device(drv, NULL, (void *)bus_id, + __ccwdev_check_busid); put_driver(drv); return dev ? to_ccwdev(dev) : 0; @@ -1097,6 +1118,14 @@ ccw_device_remove (struct device *dev) return 0; } +struct bus_type ccw_bus_type = { + .name = "ccw", + .match = ccw_bus_match, + .uevent = ccw_uevent, + .probe = ccw_device_probe, + .remove = ccw_device_remove, +}; + int ccw_driver_register (struct ccw_driver *cdriver) { @@ -1104,8 +1133,6 @@ ccw_driver_register (struct ccw_driver *cdriver) drv->bus = &ccw_bus_type; drv->name = cdriver->name; - drv->probe = ccw_device_probe; - drv->remove = ccw_device_remove; return driver_register(drv); } @@ -1116,6 +1143,16 @@ ccw_driver_unregister (struct ccw_driver *cdriver) driver_unregister(&cdriver->driver); } +/* Helper func for qdio. */ +struct subchannel_id +ccw_device_get_subchannel_id(struct ccw_device *cdev) +{ + struct subchannel *sch; + + sch = to_subchannel(cdev->dev.parent); + return sch->schid; +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); @@ -1125,3 +1162,4 @@ EXPORT_SYMBOL(get_ccwdev_by_busid); EXPORT_SYMBOL(ccw_bus_type); EXPORT_SYMBOL(ccw_device_work); EXPORT_SYMBOL(ccw_device_notify_work); +EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);