diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/cio.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 69 | ||||
-rw-r--r-- | drivers/s390/cio/css.h | 2 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 47 |
4 files changed, 95 insertions, 24 deletions
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 6e933aebe013..4062748e8346 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/mutex.h> | 4 | #include <linux/mutex.h> |
5 | #include <linux/device.h> | 5 | #include <linux/device.h> |
6 | #include <linux/mod_devicetable.h> | ||
6 | #include <asm/chpid.h> | 7 | #include <asm/chpid.h> |
7 | #include "chsc.h" | 8 | #include "chsc.h" |
8 | #include "schid.h" | 9 | #include "schid.h" |
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index b7f4b52c5a9a..53e7496dc90c 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * drivers/s390/cio/css.c | 2 | * drivers/s390/cio/css.c |
3 | * driver for channel subsystem | 3 | * driver for channel subsystem |
4 | * | 4 | * |
5 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 2002,2008 |
6 | * IBM Corporation | ||
7 | * Author(s): Arnd Bergmann (arndb@de.ibm.com) | 6 | * Author(s): Arnd Bergmann (arndb@de.ibm.com) |
8 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 7 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
9 | */ | 8 | */ |
@@ -210,6 +209,41 @@ void css_update_ssd_info(struct subchannel *sch) | |||
210 | } | 209 | } |
211 | } | 210 | } |
212 | 211 | ||
212 | static ssize_t type_show(struct device *dev, struct device_attribute *attr, | ||
213 | char *buf) | ||
214 | { | ||
215 | struct subchannel *sch = to_subchannel(dev); | ||
216 | |||
217 | return sprintf(buf, "%01x\n", sch->st); | ||
218 | } | ||
219 | |||
220 | static DEVICE_ATTR(type, 0444, type_show, NULL); | ||
221 | |||
222 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | ||
223 | char *buf) | ||
224 | { | ||
225 | struct subchannel *sch = to_subchannel(dev); | ||
226 | |||
227 | return sprintf(buf, "css:t%01X\n", sch->st); | ||
228 | } | ||
229 | |||
230 | static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); | ||
231 | |||
232 | static struct attribute *subch_attrs[] = { | ||
233 | &dev_attr_type.attr, | ||
234 | &dev_attr_modalias.attr, | ||
235 | NULL, | ||
236 | }; | ||
237 | |||
238 | static struct attribute_group subch_attr_group = { | ||
239 | .attrs = subch_attrs, | ||
240 | }; | ||
241 | |||
242 | static struct attribute_group *default_subch_attr_groups[] = { | ||
243 | &subch_attr_group, | ||
244 | NULL, | ||
245 | }; | ||
246 | |||
213 | static int css_register_subchannel(struct subchannel *sch) | 247 | static int css_register_subchannel(struct subchannel *sch) |
214 | { | 248 | { |
215 | int ret; | 249 | int ret; |
@@ -218,16 +252,17 @@ static int css_register_subchannel(struct subchannel *sch) | |||
218 | sch->dev.parent = &channel_subsystems[0]->device; | 252 | sch->dev.parent = &channel_subsystems[0]->device; |
219 | sch->dev.bus = &css_bus_type; | 253 | sch->dev.bus = &css_bus_type; |
220 | sch->dev.release = &css_subchannel_release; | 254 | sch->dev.release = &css_subchannel_release; |
221 | sch->dev.groups = subch_attr_groups; | 255 | sch->dev.groups = default_subch_attr_groups; |
222 | /* | 256 | /* |
223 | * We don't want to generate uevents for I/O subchannels that don't | 257 | * We don't want to generate uevents for I/O subchannels that don't |
224 | * have a working ccw device behind them since they will be | 258 | * have a working ccw device behind them since they will be |
225 | * unregistered before they can be used anyway, so we delay the add | 259 | * unregistered before they can be used anyway, so we delay the add |
226 | * uevent until after device recognition was successful. | 260 | * uevent until after device recognition was successful. |
261 | * Note that we suppress the uevent for all subchannel types; | ||
262 | * the subchannel driver can decide itself when it wants to inform | ||
263 | * userspace of its existence. | ||
227 | */ | 264 | */ |
228 | if (!cio_is_console(sch->schid)) | 265 | sch->dev.uevent_suppress = 1; |
229 | /* Console is special, no need to suppress. */ | ||
230 | sch->dev.uevent_suppress = 1; | ||
231 | css_update_ssd_info(sch); | 266 | css_update_ssd_info(sch); |
232 | /* make it known to the system */ | 267 | /* make it known to the system */ |
233 | ret = css_sch_device_register(sch); | 268 | ret = css_sch_device_register(sch); |
@@ -236,6 +271,15 @@ static int css_register_subchannel(struct subchannel *sch) | |||
236 | sch->schid.ssid, sch->schid.sch_no, ret); | 271 | sch->schid.ssid, sch->schid.sch_no, ret); |
237 | return ret; | 272 | return ret; |
238 | } | 273 | } |
274 | if (!sch->driver) { | ||
275 | /* | ||
276 | * No driver matched. Generate the uevent now so that | ||
277 | * a fitting driver module may be loaded based on the | ||
278 | * modalias. | ||
279 | */ | ||
280 | sch->dev.uevent_suppress = 0; | ||
281 | kobject_uevent(&sch->dev.kobj, KOBJ_ADD); | ||
282 | } | ||
239 | return ret; | 283 | return ret; |
240 | } | 284 | } |
241 | 285 | ||
@@ -926,12 +970,25 @@ static void css_shutdown(struct device *dev) | |||
926 | sch->driver->shutdown(sch); | 970 | sch->driver->shutdown(sch); |
927 | } | 971 | } |
928 | 972 | ||
973 | static int css_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
974 | { | ||
975 | struct subchannel *sch = to_subchannel(dev); | ||
976 | int ret; | ||
977 | |||
978 | ret = add_uevent_var(env, "ST=%01X", sch->st); | ||
979 | if (ret) | ||
980 | return ret; | ||
981 | ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st); | ||
982 | return ret; | ||
983 | } | ||
984 | |||
929 | struct bus_type css_bus_type = { | 985 | struct bus_type css_bus_type = { |
930 | .name = "css", | 986 | .name = "css", |
931 | .match = css_bus_match, | 987 | .match = css_bus_match, |
932 | .probe = css_probe, | 988 | .probe = css_probe, |
933 | .remove = css_remove, | 989 | .remove = css_remove, |
934 | .shutdown = css_shutdown, | 990 | .shutdown = css_shutdown, |
991 | .uevent = css_uevent, | ||
935 | }; | 992 | }; |
936 | 993 | ||
937 | /** | 994 | /** |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index bfe0ada43f2c..e0fc7b499784 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
@@ -143,6 +143,4 @@ int css_sch_is_valid(struct schib *); | |||
143 | 143 | ||
144 | extern struct workqueue_struct *slow_path_wq; | 144 | extern struct workqueue_struct *slow_path_wq; |
145 | void css_wait_for_slow_path(void); | 145 | void css_wait_for_slow_path(void); |
146 | |||
147 | extern struct attribute_group *subch_attr_groups[]; | ||
148 | #endif | 146 | #endif |
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0ed5a81260bc..23b129fd4d8d 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -585,19 +585,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); | |||
585 | static DEVICE_ATTR(online, 0644, online_show, online_store); | 585 | static DEVICE_ATTR(online, 0644, online_show, online_store); |
586 | static DEVICE_ATTR(availability, 0444, available_show, NULL); | 586 | static DEVICE_ATTR(availability, 0444, available_show, NULL); |
587 | 587 | ||
588 | static struct attribute * subch_attrs[] = { | 588 | static struct attribute *io_subchannel_attrs[] = { |
589 | &dev_attr_chpids.attr, | 589 | &dev_attr_chpids.attr, |
590 | &dev_attr_pimpampom.attr, | 590 | &dev_attr_pimpampom.attr, |
591 | NULL, | 591 | NULL, |
592 | }; | 592 | }; |
593 | 593 | ||
594 | static struct attribute_group subch_attr_group = { | 594 | static struct attribute_group io_subchannel_attr_group = { |
595 | .attrs = subch_attrs, | 595 | .attrs = io_subchannel_attrs, |
596 | }; | ||
597 | |||
598 | struct attribute_group *subch_attr_groups[] = { | ||
599 | &subch_attr_group, | ||
600 | NULL, | ||
601 | }; | 596 | }; |
602 | 597 | ||
603 | static struct attribute * ccwdev_attrs[] = { | 598 | static struct attribute * ccwdev_attrs[] = { |
@@ -1157,11 +1152,21 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1157 | 1152 | ||
1158 | cdev = sch_get_cdev(sch); | 1153 | cdev = sch_get_cdev(sch); |
1159 | if (cdev) { | 1154 | if (cdev) { |
1155 | rc = sysfs_create_group(&sch->dev.kobj, | ||
1156 | &io_subchannel_attr_group); | ||
1157 | if (rc) | ||
1158 | CIO_MSG_EVENT(0, "Failed to create io subchannel " | ||
1159 | "attributes for subchannel " | ||
1160 | "0.%x.%04x (rc=%d)\n", | ||
1161 | sch->schid.ssid, sch->schid.sch_no, rc); | ||
1160 | /* | 1162 | /* |
1161 | * This subchannel already has an associated ccw_device. | 1163 | * This subchannel already has an associated ccw_device. |
1162 | * Register it and exit. This happens for all early | 1164 | * Throw the delayed uevent for the subchannel, register |
1163 | * device, e.g. the console. | 1165 | * the ccw_device and exit. This happens for all early |
1166 | * devices, e.g. the console. | ||
1164 | */ | 1167 | */ |
1168 | sch->dev.uevent_suppress = 0; | ||
1169 | kobject_uevent(&sch->dev.kobj, KOBJ_ADD); | ||
1165 | cdev->dev.groups = ccwdev_attr_groups; | 1170 | cdev->dev.groups = ccwdev_attr_groups; |
1166 | device_initialize(&cdev->dev); | 1171 | device_initialize(&cdev->dev); |
1167 | ccw_device_register(cdev); | 1172 | ccw_device_register(cdev); |
@@ -1184,11 +1189,17 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1184 | */ | 1189 | */ |
1185 | dev_id.devno = sch->schib.pmcw.dev; | 1190 | dev_id.devno = sch->schib.pmcw.dev; |
1186 | dev_id.ssid = sch->schid.ssid; | 1191 | dev_id.ssid = sch->schid.ssid; |
1192 | rc = sysfs_create_group(&sch->dev.kobj, | ||
1193 | &io_subchannel_attr_group); | ||
1194 | if (rc) | ||
1195 | return rc; | ||
1187 | /* Allocate I/O subchannel private data. */ | 1196 | /* Allocate I/O subchannel private data. */ |
1188 | sch->private = kzalloc(sizeof(struct io_subchannel_private), | 1197 | sch->private = kzalloc(sizeof(struct io_subchannel_private), |
1189 | GFP_KERNEL | GFP_DMA); | 1198 | GFP_KERNEL | GFP_DMA); |
1190 | if (!sch->private) | 1199 | if (!sch->private) { |
1191 | return -ENOMEM; | 1200 | rc = -ENOMEM; |
1201 | goto out_err; | ||
1202 | } | ||
1192 | cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); | 1203 | cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); |
1193 | if (!cdev) | 1204 | if (!cdev) |
1194 | cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), | 1205 | 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 | } | 1218 | } |
1208 | cdev = io_subchannel_create_ccwdev(sch); | 1219 | cdev = io_subchannel_create_ccwdev(sch); |
1209 | if (IS_ERR(cdev)) { | 1220 | if (IS_ERR(cdev)) { |
1210 | kfree(sch->private); | 1221 | rc = PTR_ERR(cdev); |
1211 | return PTR_ERR(cdev); | 1222 | goto out_err; |
1212 | } | 1223 | } |
1213 | rc = io_subchannel_recog(cdev, sch); | 1224 | rc = io_subchannel_recog(cdev, sch); |
1214 | if (rc) { | 1225 | if (rc) { |
@@ -1217,9 +1228,12 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1217 | spin_unlock_irqrestore(sch->lock, flags); | 1228 | spin_unlock_irqrestore(sch->lock, flags); |
1218 | if (cdev->dev.release) | 1229 | if (cdev->dev.release) |
1219 | cdev->dev.release(&cdev->dev); | 1230 | cdev->dev.release(&cdev->dev); |
1220 | kfree(sch->private); | 1231 | goto out_err; |
1221 | } | 1232 | } |
1222 | 1233 | return 0; | |
1234 | out_err: | ||
1235 | kfree(sch->private); | ||
1236 | sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); | ||
1223 | return rc; | 1237 | return rc; |
1224 | } | 1238 | } |
1225 | 1239 | ||
@@ -1240,6 +1254,7 @@ io_subchannel_remove (struct subchannel *sch) | |||
1240 | ccw_device_unregister(cdev); | 1254 | ccw_device_unregister(cdev); |
1241 | put_device(&cdev->dev); | 1255 | put_device(&cdev->dev); |
1242 | kfree(sch->private); | 1256 | kfree(sch->private); |
1257 | sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); | ||
1243 | return 0; | 1258 | return 0; |
1244 | } | 1259 | } |
1245 | 1260 | ||