Commit 7e9db9ea authored by Cornelia Huck's avatar Cornelia Huck Committed by Heiko Carstens

[S390] cio: Introduce modalias for css bus.

Add modalias and subchannel type attributes for all subchannels.
I/O subchannel specific attributes are now created in
io_subchannel_probe(). modalias and subchannel type are also
added to the uevent for the css bus. Also make the css modalias
known.
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
parent 0ae7a7b2
What: /sys/bus/css/devices/.../type
Date: March 2008
Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
linux-s390@vger.kernel.org
Description: Contains the subchannel type, as reported by the hardware.
This attribute is present for all subchannel types.
What: /sys/bus/css/devices/.../modalias
Date: March 2008
Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
linux-s390@vger.kernel.org
Description: Contains the module alias as reported with uevents.
It is of the format css:t<type> and present for all
subchannel types.
What: /sys/bus/css/drivers/io_subchannel/.../chpids
Date: December 2002
Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
linux-s390@vger.kernel.org
Description: Contains the ids of the channel paths used by this
subchannel, as reported by the channel subsystem
during subchannel recognition.
Note: This is an I/O-subchannel specific attribute.
Users: s390-tools, HAL
What: /sys/bus/css/drivers/io_subchannel/.../pimpampom
Date: December 2002
Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
linux-s390@vger.kernel.org
Description: Contains the PIM/PAM/POM values, as reported by the
channel subsystem when last queried by the common I/O
layer (this implies that this attribute is not neccessarily
in sync with the values current in the channel subsystem).
Note: This is an I/O-subchannel specific attribute.
Users: s390-tools, HAL
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <asm/chpid.h> #include <asm/chpid.h>
#include "chsc.h" #include "chsc.h"
#include "schid.h" #include "schid.h"
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
* drivers/s390/cio/css.c * drivers/s390/cio/css.c
* driver for channel subsystem * driver for channel subsystem
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright IBM Corp. 2002,2008
* IBM Corporation
* Author(s): Arnd Bergmann (arndb@de.ibm.com) * Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com)
*/ */
...@@ -210,6 +209,41 @@ void css_update_ssd_info(struct subchannel *sch) ...@@ -210,6 +209,41 @@ void css_update_ssd_info(struct subchannel *sch)
} }
} }
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct subchannel *sch = to_subchannel(dev);
return sprintf(buf, "%01x\n", sch->st);
}
static DEVICE_ATTR(type, 0444, type_show, NULL);
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct subchannel *sch = to_subchannel(dev);
return sprintf(buf, "css:t%01X\n", sch->st);
}
static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static struct attribute *subch_attrs[] = {
&dev_attr_type.attr,
&dev_attr_modalias.attr,
NULL,
};
static struct attribute_group subch_attr_group = {
.attrs = subch_attrs,
};
static struct attribute_group *default_subch_attr_groups[] = {
&subch_attr_group,
NULL,
};
static int css_register_subchannel(struct subchannel *sch) static int css_register_subchannel(struct subchannel *sch)
{ {
int ret; int ret;
...@@ -218,15 +252,16 @@ static int css_register_subchannel(struct subchannel *sch) ...@@ -218,15 +252,16 @@ static int css_register_subchannel(struct subchannel *sch)
sch->dev.parent = &channel_subsystems[0]->device; sch->dev.parent = &channel_subsystems[0]->device;
sch->dev.bus = &css_bus_type; sch->dev.bus = &css_bus_type;
sch->dev.release = &css_subchannel_release; sch->dev.release = &css_subchannel_release;
sch->dev.groups = subch_attr_groups; sch->dev.groups = default_subch_attr_groups;
/* /*
* We don't want to generate uevents for I/O subchannels that don't * We don't want to generate uevents for I/O subchannels that don't
* have a working ccw device behind them since they will be * have a working ccw device behind them since they will be
* unregistered before they can be used anyway, so we delay the add * unregistered before they can be used anyway, so we delay the add
* uevent until after device recognition was successful. * uevent until after device recognition was successful.
* Note that we suppress the uevent for all subchannel types;
* the subchannel driver can decide itself when it wants to inform
* userspace of its existence.
*/ */
if (!cio_is_console(sch->schid))
/* Console is special, no need to suppress. */
sch->dev.uevent_suppress = 1; sch->dev.uevent_suppress = 1;
css_update_ssd_info(sch); css_update_ssd_info(sch);
/* make it known to the system */ /* make it known to the system */
...@@ -236,6 +271,15 @@ static int css_register_subchannel(struct subchannel *sch) ...@@ -236,6 +271,15 @@ static int css_register_subchannel(struct subchannel *sch)
sch->schid.ssid, sch->schid.sch_no, ret); sch->schid.ssid, sch->schid.sch_no, ret);
return ret; return ret;
} }
if (!sch->driver) {
/*
* No driver matched. Generate the uevent now so that
* a fitting driver module may be loaded based on the
* modalias.
*/
sch->dev.uevent_suppress = 0;
kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
}
return ret; return ret;
} }
...@@ -926,12 +970,25 @@ static void css_shutdown(struct device *dev) ...@@ -926,12 +970,25 @@ static void css_shutdown(struct device *dev)
sch->driver->shutdown(sch); sch->driver->shutdown(sch);
} }
static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct subchannel *sch = to_subchannel(dev);
int ret;
ret = add_uevent_var(env, "ST=%01X", sch->st);
if (ret)
return ret;
ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st);
return ret;
}
struct bus_type css_bus_type = { struct bus_type css_bus_type = {
.name = "css", .name = "css",
.match = css_bus_match, .match = css_bus_match,
.probe = css_probe, .probe = css_probe,
.remove = css_remove, .remove = css_remove,
.shutdown = css_shutdown, .shutdown = css_shutdown,
.uevent = css_uevent,
}; };
/** /**
......
...@@ -143,6 +143,4 @@ int css_sch_is_valid(struct schib *); ...@@ -143,6 +143,4 @@ int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq; extern struct workqueue_struct *slow_path_wq;
void css_wait_for_slow_path(void); void css_wait_for_slow_path(void);
extern struct attribute_group *subch_attr_groups[];
#endif #endif
...@@ -585,19 +585,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); ...@@ -585,19 +585,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL); static DEVICE_ATTR(availability, 0444, available_show, NULL);
static struct attribute * subch_attrs[] = { static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr, &dev_attr_chpids.attr,
&dev_attr_pimpampom.attr, &dev_attr_pimpampom.attr,
NULL, NULL,
}; };
static struct attribute_group subch_attr_group = { static struct attribute_group io_subchannel_attr_group = {
.attrs = subch_attrs, .attrs = io_subchannel_attrs,
};
struct attribute_group *subch_attr_groups[] = {
&subch_attr_group,
NULL,
}; };
static struct attribute * ccwdev_attrs[] = { static struct attribute * ccwdev_attrs[] = {
...@@ -1157,11 +1152,21 @@ static int io_subchannel_probe(struct subchannel *sch) ...@@ -1157,11 +1152,21 @@ static int io_subchannel_probe(struct subchannel *sch)
cdev = sch_get_cdev(sch); cdev = sch_get_cdev(sch);
if (cdev) { if (cdev) {
rc = sysfs_create_group(&sch->dev.kobj,
&io_subchannel_attr_group);
if (rc)
CIO_MSG_EVENT(0, "Failed to create io subchannel "
"attributes for subchannel "
"0.%x.%04x (rc=%d)\n",
sch->schid.ssid, sch->schid.sch_no, rc);
/* /*
* This subchannel already has an associated ccw_device. * This subchannel already has an associated ccw_device.
* Register it and exit. This happens for all early * Throw the delayed uevent for the subchannel, register
* device, e.g. the console. * the ccw_device and exit. This happens for all early
* devices, e.g. the console.
*/ */
sch->dev.uevent_suppress = 0;
kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
cdev->dev.groups = ccwdev_attr_groups; cdev->dev.groups = ccwdev_attr_groups;
device_initialize(&cdev->dev); device_initialize(&cdev->dev);
ccw_device_register(cdev); ccw_device_register(cdev);
...@@ -1184,11 +1189,17 @@ static int io_subchannel_probe(struct subchannel *sch) ...@@ -1184,11 +1189,17 @@ static int io_subchannel_probe(struct subchannel *sch)
*/ */
dev_id.devno = sch->schib.pmcw.dev; dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid; dev_id.ssid = sch->schid.ssid;
rc = sysfs_create_group(&sch->dev.kobj,
&io_subchannel_attr_group);
if (rc)
return rc;
/* Allocate I/O subchannel private data. */ /* Allocate I/O subchannel private data. */
sch->private = kzalloc(sizeof(struct io_subchannel_private), sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA); GFP_KERNEL | GFP_DMA);
if (!sch->private) if (!sch->private) {
return -ENOMEM; rc = -ENOMEM;
goto out_err;
}
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev) if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
...@@ -1207,8 +1218,8 @@ static int io_subchannel_probe(struct subchannel *sch) ...@@ -1207,8 +1218,8 @@ static int io_subchannel_probe(struct subchannel *sch)
} }
cdev = io_subchannel_create_ccwdev(sch); cdev = io_subchannel_create_ccwdev(sch);
if (IS_ERR(cdev)) { if (IS_ERR(cdev)) {
kfree(sch->private); rc = PTR_ERR(cdev);
return PTR_ERR(cdev); goto out_err;
} }
rc = io_subchannel_recog(cdev, sch); rc = io_subchannel_recog(cdev, sch);
if (rc) { if (rc) {
...@@ -1217,9 +1228,12 @@ static int io_subchannel_probe(struct subchannel *sch) ...@@ -1217,9 +1228,12 @@ static int io_subchannel_probe(struct subchannel *sch)
spin_unlock_irqrestore(sch->lock, flags); spin_unlock_irqrestore(sch->lock, flags);
if (cdev->dev.release) if (cdev->dev.release)
cdev->dev.release(&cdev->dev); cdev->dev.release(&cdev->dev);
kfree(sch->private); goto out_err;
} }
return 0;
out_err:
kfree(sch->private);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return rc; return rc;
} }
...@@ -1240,6 +1254,7 @@ io_subchannel_remove (struct subchannel *sch) ...@@ -1240,6 +1254,7 @@ io_subchannel_remove (struct subchannel *sch)
ccw_device_unregister(cdev); ccw_device_unregister(cdev);
put_device(&cdev->dev); put_device(&cdev->dev);
kfree(sch->private); kfree(sch->private);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return 0; return 0;
} }
......
...@@ -159,6 +159,15 @@ struct ap_device_id { ...@@ -159,6 +159,15 @@ struct ap_device_id {
#define AP_DEVICE_ID_MATCH_DEVICE_TYPE 0x01 #define AP_DEVICE_ID_MATCH_DEVICE_TYPE 0x01
/* s390 css bus devices (subchannels) */
struct css_device_id {
__u8 type; /* subchannel type */
__u8 pad1;
__u16 pad2;
__u32 pad3;
kernel_ulong_t driver_data;
};
#define ACPI_ID_LEN 16 /* only 9 bytes needed here, 16 bytes are used */ #define ACPI_ID_LEN 16 /* only 9 bytes needed here, 16 bytes are used */
/* to workaround crosscompile issues */ /* to workaround crosscompile issues */
......
...@@ -304,6 +304,14 @@ static int do_ap_entry(const char *filename, ...@@ -304,6 +304,14 @@ static int do_ap_entry(const char *filename,
return 1; return 1;
} }
/* looks like: "css:tN" */
static int do_css_entry(const char *filename,
struct css_device_id *id, char *alias)
{
sprintf(alias, "css:t%01X", id->type);
return 1;
}
/* Looks like: "serio:tyNprNidNexN" */ /* Looks like: "serio:tyNprNidNexN" */
static int do_serio_entry(const char *filename, static int do_serio_entry(const char *filename,
struct serio_device_id *id, char *alias) struct serio_device_id *id, char *alias)
...@@ -680,6 +688,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, ...@@ -680,6 +688,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
do_table(symval, sym->st_size, do_table(symval, sym->st_size,
sizeof(struct ap_device_id), "ap", sizeof(struct ap_device_id), "ap",
do_ap_entry, mod); do_ap_entry, mod);
else if (sym_is(symname, "__mod_css_device_table"))
do_table(symval, sym->st_size,
sizeof(struct css_device_id), "css",
do_css_entry, mod);
else if (sym_is(symname, "__mod_serio_device_table")) else if (sym_is(symname, "__mod_serio_device_table"))
do_table(symval, sym->st_size, do_table(symval, sym->st_size,
sizeof(struct serio_device_id), "serio", sizeof(struct serio_device_id), "serio",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment