/*
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
- * $Revision: 1.115 $
+ * $Revision: 1.119 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
sch->schib.pmcw.pam &
sch->schib.pmcw.pom)
| chp_mask) & sch->opm;
- spin_unlock_irq(&sch->lock);
if (!old_lpm && sch->lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
+ spin_unlock_irq(&sch->lock);
put_device(&sch->dev);
if (fla_mask != 0)
break;
{
int cc;
+ if (!device_is_online(sch))
+ /* cio could be doing I/O. */
+ return 0;
cc = stsch(sch->irq, &sch->schib);
if (cc)
return 0;
__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
{
int chp, old_lpm;
+ unsigned long flags;
if (!sch->ssd_info.valid)
return;
+ spin_lock_irqsave(&sch->lock, flags);
old_lpm = sch->lpm;
for (chp = 0; chp < 8; chp++) {
if (sch->ssd_info.chpid[chp] != chpid)
}
break;
}
+ spin_unlock_irqrestore(&sch->lock, flags);
}
static int
static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
+static ssize_t
+chp_type_show(struct device *dev, char *buf)
+{
+ struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+ if (!chp)
+ return 0;
+ return sprintf(buf, "%x\n", chp->desc.desc);
+}
+
+static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
+
+static struct attribute * chp_attrs[] = {
+ &dev_attr_status.attr,
+ &dev_attr_type.attr,
+ NULL,
+};
+
+static struct attribute_group chp_attr_group = {
+ .attrs = chp_attrs,
+};
static void
chp_release(struct device *dev)
kfree(cp);
}
+static int
+chsc_determine_channel_path_description(int chpid,
+ struct channel_path_desc *desc)
+{
+ int ccode, ret;
+
+ struct {
+ struct chsc_header request;
+ u32 : 24;
+ u32 first_chpid : 8;
+ u32 : 24;
+ u32 last_chpid : 8;
+ u32 zeroes1;
+ struct chsc_header response;
+ u32 zeroes2;
+ struct channel_path_desc desc;
+ } *scpd_area;
+
+ scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!scpd_area)
+ return -ENOMEM;
+
+ scpd_area->request = (struct chsc_header) {
+ .length = 0x0010,
+ .code = 0x0002,
+ };
+
+ scpd_area->first_chpid = chpid;
+ scpd_area->last_chpid = chpid;
+
+ ccode = chsc(scpd_area);
+ if (ccode > 0) {
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out;
+ }
+
+ switch (scpd_area->response.code) {
+ case 0x0001: /* Success. */
+ memcpy(desc, &scpd_area->desc,
+ sizeof(struct channel_path_desc));
+ ret = 0;
+ break;
+ case 0x0003: /* Invalid block. */
+ case 0x0007: /* Invalid format. */
+ case 0x0008: /* Other invalid block. */
+ CIO_CRW_EVENT(2, "Error in chsc request block!\n");
+ ret = -EINVAL;
+ break;
+ case 0x0004: /* Command not provided in model. */
+ CIO_CRW_EVENT(2, "Model does not provide scpd\n");
+ ret = -EOPNOTSUPP;
+ break;
+ default:
+ CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
+ scpd_area->response.code);
+ ret = -EIO;
+ }
+out:
+ free_page((unsigned long)scpd_area);
+ return ret;
+}
+
/*
* Entries for chpids on the system bus.
* This replaces /proc/chpids.
};
snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
+ /* Obtain channel path description and fill it in. */
+ ret = chsc_determine_channel_path_description(chpid, &chp->desc);
+ if (ret)
+ goto out_free;
+
/* make it known to the system */
ret = device_register(&chp->dev);
if (ret) {
__func__, chpid);
goto out_free;
}
- ret = device_create_file(&chp->dev, &dev_attr_status);
+ ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
if (ret) {
device_unregister(&chp->dev);
goto out_free;
return ret;
}
+void *
+chsc_get_chp_desc(struct subchannel *sch, int chp_no)
+{
+ struct channel_path *chp;
+ struct channel_path_desc *desc;
+
+ chp = chps[sch->schib.pmcw.chpid[chp_no]];
+ if (!chp)
+ return NULL;
+ desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+ if (!desc)
+ return NULL;
+ memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ return desc;
+}
+
+
static int __init
chsc_alloc_sei_area(void)
{