diff options
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/ccwgroup.c | 112 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 73 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 13 | ||||
-rw-r--r-- | drivers/s390/cio/device.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 47 |
5 files changed, 99 insertions, 147 deletions
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 5f1dc6fb5708..731470e68493 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -1,7 +1,7 @@ /* * bus driver for ccwgroup * - * Copyright IBM Corp. 2002, 2009 + * Copyright IBM Corp. 2002, 2012 * * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) @@ -15,10 +15,13 @@ #include <linux/ctype.h> #include <linux/dcache.h> +#include <asm/cio.h> #include <asm/ccwdev.h> #include <asm/ccwgroup.h> -#define CCW_BUS_ID_SIZE 20 +#include "device.h" + +#define CCW_BUS_ID_SIZE 10 /* In Linux 2.4, we had a channel device layer called "chandev" * that did all sorts of obscure stuff for networking devices. @@ -27,19 +30,6 @@ * to devices that use multiple subchannels. */ -/* a device matches a driver if all its slave devices match the same - * entry of the driver */ -static int ccwgroup_bus_match(struct device *dev, struct device_driver * drv) -{ - struct ccwgroup_device *gdev = to_ccwgroupdev(dev); - struct ccwgroup_driver *gdrv = to_ccwgroupdrv(drv); - - if (gdev->creator_id == gdrv->driver_id) - return 1; - - return 0; -} - static struct bus_type ccwgroup_bus_type; static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) @@ -254,9 +244,10 @@ static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev) return 0; } -static int __get_next_bus_id(const char **buf, char *bus_id) +static int __get_next_id(const char **buf, struct ccw_dev_id *id) { - int rc, len; + unsigned int cssid, ssid, devno; + int ret = 0, len; char *start, *end; start = (char *)*buf; @@ -271,49 +262,40 @@ static int __get_next_bus_id(const char **buf, char *bus_id) len = end - start + 1; end++; } - if (len < CCW_BUS_ID_SIZE) { - strlcpy(bus_id, start, len); - rc = 0; + if (len <= CCW_BUS_ID_SIZE) { + if (sscanf(start, "%2x.%1x.%04x", &cssid, &ssid, &devno) != 3) + ret = -EINVAL; } else - rc = -EINVAL; - *buf = end; - return rc; -} - -static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE]) -{ - int cssid, ssid, devno; + ret = -EINVAL; - /* Must be of form %x.%x.%04x */ - if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3) - return 0; - return 1; + if (!ret) { + id->ssid = ssid; + id->devno = devno; + } + *buf = end; + return ret; } /** - * ccwgroup_create_from_string() - create and register a ccw group device - * @root: parent device for the new device - * @creator_id: identifier of creating driver - * @cdrv: ccw driver of slave devices + * ccwgroup_create_dev() - create and register a ccw group device + * @parent: parent device for the new device + * @gdrv: driver for the new group device * @num_devices: number of slave devices * @buf: buffer containing comma separated bus ids of slave devices * - * Create and register a new ccw group device as a child of @root. Slave - * devices are obtained from the list of bus ids given in @buf and must all - * belong to @cdrv. + * Create and register a new ccw group device as a child of @parent. Slave + * devices are obtained from the list of bus ids given in @buf. * Returns: * %0 on success and an error code on failure. * Context: * non-atomic */ -int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, - struct ccw_driver *cdrv, int num_devices, - const char *buf) +int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv, + int num_devices, const char *buf) { struct ccwgroup_device *gdev; + struct ccw_dev_id dev_id; int rc, i; - char tmp_bus_id[CCW_BUS_ID_SIZE]; - const char *curr_buf; gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), GFP_KERNEL); @@ -323,29 +305,24 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, atomic_set(&gdev->onoff, 0); mutex_init(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex); - gdev->creator_id = creator_id; gdev->count = num_devices; gdev->dev.bus = &ccwgroup_bus_type; - gdev->dev.parent = root; + gdev->dev.parent = parent; gdev->dev.release = ccwgroup_release; device_initialize(&gdev->dev); - curr_buf = buf; - for (i = 0; i < num_devices && curr_buf; i++) { - rc = __get_next_bus_id(&curr_buf, tmp_bus_id); + for (i = 0; i < num_devices && buf; i++) { + rc = __get_next_id(&buf, &dev_id); if (rc != 0) goto error; - if (!__is_valid_bus_id(tmp_bus_id)) { - rc = -EINVAL; - goto error; - } - gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id); + gdev->cdev[i] = get_ccwdev_by_dev_id(&dev_id); /* * All devices have to be of the same type in * order to be grouped. */ - if (!gdev->cdev[i] - || gdev->cdev[i]->id.driver_info != + if (!gdev->cdev[i] || !gdev->cdev[i]->drv || + gdev->cdev[i]->drv != gdev->cdev[0]->drv || + gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; goto error; @@ -361,18 +338,25 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, spin_unlock_irq(gdev->cdev[i]->ccwlock); } /* Check for sufficient number of bus ids. */ - if (i < num_devices && !curr_buf) { + if (i < num_devices) { rc = -EINVAL; goto error; } /* Check for trailing stuff. */ - if (i == num_devices && strlen(curr_buf) > 0) { + if (i == num_devices && strlen(buf) > 0) { rc = -EINVAL; goto error; } dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev)); gdev->dev.groups = ccwgroup_attr_groups; + + if (gdrv) { + gdev->dev.driver = &gdrv->driver; + rc = gdrv->setup ? gdrv->setup(gdev) : 0; + if (rc) + goto error; + } rc = device_add(&gdev->dev); if (rc) goto error; @@ -397,7 +381,7 @@ error: put_device(&gdev->dev); return rc; } -EXPORT_SYMBOL(ccwgroup_create_from_string); +EXPORT_SYMBOL(ccwgroup_create_dev); static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, void *data) @@ -440,14 +424,6 @@ module_exit(cleanup_ccwgroup); /************************** driver stuff ******************************/ -static int ccwgroup_probe(struct device *dev) -{ - struct ccwgroup_device *gdev = to_ccwgroupdev(dev); - struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver); - - return gdrv->probe ? gdrv->probe(gdev) : -ENODEV; -} - static int ccwgroup_remove(struct device *dev) { struct ccwgroup_device *gdev = to_ccwgroupdev(dev); @@ -542,8 +518,6 @@ static const struct dev_pm_ops ccwgroup_pm_ops = { static struct bus_type ccwgroup_bus_type = { .name = "ccwgroup", - .match = ccwgroup_bus_match, - .probe = ccwgroup_probe, .remove = ccwgroup_remove, .shutdown = ccwgroup_shutdown, .pm = &ccwgroup_pm_ops, diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index a49c46c91983..a6ddaed8793d 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -656,51 +656,34 @@ static struct io_subchannel_private console_priv; static int console_subchannel_in_use; /* - * Use cio_tpi to get a pending interrupt and call the interrupt handler. - * Return non-zero if an interrupt was processed, zero otherwise. + * Use cio_tsch to update the subchannel status and call the interrupt handler + * if status had been pending. Called with the console_subchannel lock. */ -static int cio_tpi(void) +static void cio_tsch(struct subchannel *sch) { - struct tpi_info *tpi_info; - struct subchannel *sch; struct irb *irb; int irq_context; - tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id; - if (tpi(NULL) != 1) - return 0; - kstat_cpu(smp_processor_id()).irqs[IO_INTERRUPT]++; - if (tpi_info->adapter_IO) { - do_adapter_IO(tpi_info->isc); - return 1; - } irb = (struct irb *)&S390_lowcore.irb; /* Store interrupt response block to lowcore. */ - if (tsch(tpi_info->schid, irb) != 0) { + if (tsch(sch->schid, irb) != 0) /* Not status pending or not operational. */ - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; - return 1; - } - sch = (struct subchannel *)(unsigned long)tpi_info->intparm; - if (!sch) { - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; - return 1; - } + return; + memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); + /* Call interrupt handler with updated status. */ irq_context = in_interrupt(); - if (!irq_context) + if (!irq_context) { local_bh_disable(); - irq_enter(); - spin_lock(sch->lock); - memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); + irq_enter(); + } if (sch->driver && sch->driver->irq) sch->driver->irq(sch); else kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; - spin_unlock(sch->lock); - irq_exit(); - if (!irq_context) + if (!irq_context) { + irq_exit(); _local_bh_enable(); - return 1; + } } void *cio_get_console_priv(void) @@ -712,34 +695,16 @@ void *cio_get_console_priv(void) * busy wait for the next interrupt on the console */ void wait_cons_dev(void) - __releases(console_subchannel.lock) - __acquires(console_subchannel.lock) { - unsigned long cr6 __attribute__ ((aligned (8))); - unsigned long save_cr6 __attribute__ ((aligned (8))); - - /* - * before entering the spinlock we may already have - * processed the interrupt on a different CPU... - */ if (!console_subchannel_in_use) return; - /* disable all but the console isc */ - __ctl_store (save_cr6, 6, 6); - cr6 = 1UL << (31 - CONSOLE_ISC); - __ctl_load (cr6, 6, 6); - - do { - spin_unlock(console_subchannel.lock); - if (!cio_tpi()) - cpu_relax(); - spin_lock(console_subchannel.lock); - } while (console_subchannel.schib.scsw.cmd.actl != 0); - /* - * restore previous isc value - */ - __ctl_load (save_cr6, 6, 6); + while (1) { + cio_tsch(&console_subchannel); + if (console_subchannel.schib.scsw.cmd.actl == 0) + break; + udelay_simple(100); + } } static int diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 02d015259461..f8f952d52045 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -695,7 +695,17 @@ static int match_dev_id(struct device *dev, void *data) return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id); } -static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) +/** + * get_ccwdev_by_dev_id() - obtain device from a ccw device id + * @dev_id: id of the device to be searched + * + * This function searches all devices attached to the ccw bus for a device + * matching @dev_id. + * Returns: + * If a device is found its reference count is increased and returned; + * else %NULL is returned. + */ +struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) { struct device *dev; @@ -703,6 +713,7 @@ static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) return dev ? to_ccwdev(dev) : NULL; } +EXPORT_SYMBOL_GPL(get_ccwdev_by_dev_id); static void ccw_device_do_unbind_bind(struct ccw_device *cdev) { diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 179824b3082f..6bace6942396 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -101,6 +101,7 @@ int ccw_device_test_sense_data(struct ccw_device *); void ccw_device_schedule_sch_unregister(struct ccw_device *); int ccw_purge_blacklisted(void); void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo); +struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id); /* Function prototypes for device status and basic sense stuff. */ void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 35c685c374e9..7493efafa0d5 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -63,7 +63,7 @@ static inline int do_siga_input(unsigned long schid, unsigned int mask, " ipm %0\n" " srl %0,28\n" : "=d" (cc) - : "d" (__fc), "d" (__schid), "d" (__mask) : "cc", "memory"); + : "d" (__fc), "d" (__schid), "d" (__mask) : "cc"); return cc; } @@ -74,7 +74,7 @@ static inline int do_siga_input(unsigned long schid, unsigned int mask, * @bb: busy bit indicator, set only if SIGA-w/wt could not access a buffer * @fc: function code to perform * - * Returns cc or QDIO_ERROR_SIGA_ACCESS_EXCEPTION. + * Returns condition code. * Note: For IQDC unicast queues only the highest priority queue is processed. */ static inline int do_siga_output(unsigned long schid, unsigned long mask, @@ -85,18 +85,16 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask, register unsigned long __schid asm("1") = schid; register unsigned long __mask asm("2") = mask; register unsigned long __aob asm("3") = aob; - int cc = QDIO_ERROR_SIGA_ACCESS_EXCEPTION; + int cc; asm volatile( " siga 0\n" - "0: ipm %0\n" + " ipm %0\n" " srl %0,28\n" - "1:\n" - EX_TABLE(0b, 1b) - : "+d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask), - "+d" (__aob) - : : "cc", "memory"); - *bb = ((unsigned int) __fc) >> 31; + : "=d" (cc), "+d" (__fc), "+d" (__aob) + : "d" (__schid), "d" (__mask) + : "cc"); + *bb = __fc >> 31; return cc; } @@ -167,7 +165,7 @@ again: DBF_ERROR("%4x EQBS ERROR", SCH_NO(q)); DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); - q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, + q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE, q->nr, q->first_to_kick, count, q->irq_ptr->int_parm); return 0; } @@ -215,7 +213,7 @@ again: DBF_ERROR("%4x SQBS ERROR", SCH_NO(q)); DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); - q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, + q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE, q->nr, q->first_to_kick, count, q->irq_ptr->int_parm); return 0; } @@ -313,7 +311,7 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, cc = do_siga_sync(schid, output, input, fc); if (unlikely(cc)) DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); - return cc; + return (cc) ? -EIO : 0; } static inline int qdio_siga_sync_q(struct qdio_q *q) @@ -384,7 +382,7 @@ static inline int qdio_siga_input(struct qdio_q *q) cc = do_siga_input(schid, q->mask, fc); if (unlikely(cc)) DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); - return cc; + return (cc) ? -EIO : 0; } #define qdio_siga_sync_out(q) qdio_siga_sync(q, ~0U, 0) @@ -443,7 +441,7 @@ static void process_buffer_error(struct qdio_q *q, int count) unsigned char state = (q->is_input_q) ? SLSB_P_INPUT_NOT_INIT : SLSB_P_OUTPUT_NOT_INIT; - q->qdio_error |= QDIO_ERROR_SLSB_STATE; + q->qdio_error = QDIO_ERROR_SLSB_STATE; /* special handling for no target buffer empty */ if ((!q->is_input_q && @@ -519,7 +517,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state = 0; - q->timestamp = get_clock_fast(); + q->timestamp = get_clock(); /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved @@ -575,7 +573,7 @@ static int qdio_inbound_q_moved(struct qdio_q *q) bufnr = get_inbound_buffer_frontier(q); - if ((bufnr != q->last_move) || q->qdio_error) { + if (bufnr != q->last_move) { q->last_move = bufnr; if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR) q->u.in.timestamp = get_clock(); @@ -790,7 +788,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state = 0; - q->timestamp = get_clock_fast(); + q->timestamp = get_clock(); if (need_siga_sync(q)) if (((queue_type(q) != QDIO_IQDIO_QFMT) && @@ -863,7 +861,7 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) bufnr = get_outbound_buffer_frontier(q); - if ((bufnr != q->last_move) || q->qdio_error) { + if (bufnr != q->last_move) { q->last_move = bufnr; DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); return 1; @@ -894,13 +892,16 @@ retry: goto retry; } DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr); - cc |= QDIO_ERROR_SIGA_BUSY; - } else + cc = -EBUSY; + } else { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr); + cc = -ENOBUFS; + } break; case 1: case 3: DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc); + cc = -EIO; break; } if (retries) { @@ -1090,7 +1091,7 @@ static void qdio_handle_activate_check(struct ccw_device *cdev, } count = sub_buf(q->first_to_check, q->first_to_kick); - q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, + q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE, q->nr, q->first_to_kick, count, irq_ptr->int_parm); no_handler: qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); @@ -1691,7 +1692,7 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, "do%02x b:%02x c:%02x", callflags, bufnr, count); if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE) - return -EBUSY; + return -EIO; if (!count) return 0; if (callflags & QDIO_FLAG_SYNC_INPUT) |