11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  bus driver for ccwgroup
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
47e597a21a1470b12428cb0edd03c40986026451fSebastian Ott *  Copyright IBM Corp. 2002, 2009
57e597a21a1470b12428cb0edd03c40986026451fSebastian Ott *
67e597a21a1470b12428cb0edd03c40986026451fSebastian Ott *  Author(s): Arnd Bergmann (arndb@de.ibm.com)
77e597a21a1470b12428cb0edd03c40986026451fSebastian Ott *	       Cornelia Huck (cornelia.huck@de.ibm.com)
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/list.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ctype.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/dcache.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/ccwdev.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/ccwgroup.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2198df67b324a2a986987ce29986e44ae9156b6698Kay Sievers#define CCW_BUS_ID_SIZE		20
2298df67b324a2a986987ce29986e44ae9156b6698Kay Sievers
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* In Linux 2.4, we had a channel device layer called "chandev"
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that did all sorts of obscure stuff for networking devices.
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is another driver that serves as a replacement for just
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * one of its functions, namely the translation of single subchannels
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to devices that use multiple subchannels.
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* a device matches a driver if all its slave devices match the same
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * entry of the driver */
32dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int ccwgroup_bus_match(struct device *dev, struct device_driver * drv)
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
34dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
35dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(drv);
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (gdev->creator_id == gdrv->driver_id)
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
43f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell Kingstatic struct bus_type ccwgroup_bus_type;
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
45dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char str[8];
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < gdev->count; i++) {
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sprintf(str, "cdev%d", i);
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sysfs_remove_link(&gdev->dev.kobj, str);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
58c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter * Remove references from ccw devices to ccw group device and from
59c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter * ccw group device to ccw devices.
60c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter */
61c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiterstatic void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
62c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter{
63c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	struct ccw_device *cdev;
64c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	int i;
65c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter
66c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	for (i = 0; i < gdev->count; i++) {
67c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		cdev = gdev->cdev[i];
68c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		if (!cdev)
69c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter			continue;
70c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		spin_lock_irq(cdev->ccwlock);
71c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		dev_set_drvdata(&cdev->dev, NULL);
72c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		spin_unlock_irq(cdev->ccwlock);
73c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		gdev->cdev[i] = NULL;
74c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		put_device(&cdev->dev);
75c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	}
76c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter}
77c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter
78dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int ccwgroup_set_online(struct ccwgroup_device *gdev)
79dad572e370138539ea45be9b53d168568e562565Sebastian Ott{
80dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
81dad572e370138539ea45be9b53d168568e562565Sebastian Ott	int ret = 0;
82dad572e370138539ea45be9b53d168568e562565Sebastian Ott
83dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
84dad572e370138539ea45be9b53d168568e562565Sebastian Ott		return -EAGAIN;
85dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (gdev->state == CCWGROUP_ONLINE)
86dad572e370138539ea45be9b53d168568e562565Sebastian Ott		goto out;
87dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (gdrv->set_online)
88dad572e370138539ea45be9b53d168568e562565Sebastian Ott		ret = gdrv->set_online(gdev);
89dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (ret)
90dad572e370138539ea45be9b53d168568e562565Sebastian Ott		goto out;
91dad572e370138539ea45be9b53d168568e562565Sebastian Ott
92dad572e370138539ea45be9b53d168568e562565Sebastian Ott	gdev->state = CCWGROUP_ONLINE;
93dad572e370138539ea45be9b53d168568e562565Sebastian Ottout:
94dad572e370138539ea45be9b53d168568e562565Sebastian Ott	atomic_set(&gdev->onoff, 0);
95dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return ret;
96dad572e370138539ea45be9b53d168568e562565Sebastian Ott}
97dad572e370138539ea45be9b53d168568e562565Sebastian Ott
98dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int ccwgroup_set_offline(struct ccwgroup_device *gdev)
99dad572e370138539ea45be9b53d168568e562565Sebastian Ott{
100dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
101dad572e370138539ea45be9b53d168568e562565Sebastian Ott	int ret = 0;
102dad572e370138539ea45be9b53d168568e562565Sebastian Ott
103dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
104dad572e370138539ea45be9b53d168568e562565Sebastian Ott		return -EAGAIN;
105dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (gdev->state == CCWGROUP_OFFLINE)
106dad572e370138539ea45be9b53d168568e562565Sebastian Ott		goto out;
107dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (gdrv->set_offline)
108dad572e370138539ea45be9b53d168568e562565Sebastian Ott		ret = gdrv->set_offline(gdev);
109dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (ret)
110dad572e370138539ea45be9b53d168568e562565Sebastian Ott		goto out;
111dad572e370138539ea45be9b53d168568e562565Sebastian Ott
112dad572e370138539ea45be9b53d168568e562565Sebastian Ott	gdev->state = CCWGROUP_OFFLINE;
113dad572e370138539ea45be9b53d168568e562565Sebastian Ottout:
114dad572e370138539ea45be9b53d168568e562565Sebastian Ott	atomic_set(&gdev->onoff, 0);
115dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return ret;
116dad572e370138539ea45be9b53d168568e562565Sebastian Ott}
117dad572e370138539ea45be9b53d168568e562565Sebastian Ott
118dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ottstatic ssize_t ccwgroup_online_store(struct device *dev,
119dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott				     struct device_attribute *attr,
120dad572e370138539ea45be9b53d168568e562565Sebastian Ott				     const char *buf, size_t count)
121dad572e370138539ea45be9b53d168568e562565Sebastian Ott{
122dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
123dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
124dad572e370138539ea45be9b53d168568e562565Sebastian Ott	unsigned long value;
125dad572e370138539ea45be9b53d168568e562565Sebastian Ott	int ret;
126dad572e370138539ea45be9b53d168568e562565Sebastian Ott
127dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (!dev->driver)
128dad572e370138539ea45be9b53d168568e562565Sebastian Ott		return -EINVAL;
129dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (!try_module_get(gdrv->driver.owner))
130dad572e370138539ea45be9b53d168568e562565Sebastian Ott		return -EINVAL;
131dad572e370138539ea45be9b53d168568e562565Sebastian Ott
132dad572e370138539ea45be9b53d168568e562565Sebastian Ott	ret = strict_strtoul(buf, 0, &value);
133dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (ret)
134dad572e370138539ea45be9b53d168568e562565Sebastian Ott		goto out;
135dad572e370138539ea45be9b53d168568e562565Sebastian Ott
136dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (value == 1)
137dad572e370138539ea45be9b53d168568e562565Sebastian Ott		ret = ccwgroup_set_online(gdev);
138dad572e370138539ea45be9b53d168568e562565Sebastian Ott	else if (value == 0)
139dad572e370138539ea45be9b53d168568e562565Sebastian Ott		ret = ccwgroup_set_offline(gdev);
140dad572e370138539ea45be9b53d168568e562565Sebastian Ott	else
141dad572e370138539ea45be9b53d168568e562565Sebastian Ott		ret = -EINVAL;
142dad572e370138539ea45be9b53d168568e562565Sebastian Ottout:
143dad572e370138539ea45be9b53d168568e562565Sebastian Ott	module_put(gdrv->driver.owner);
144dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return (ret == 0) ? count : ret;
145dad572e370138539ea45be9b53d168568e562565Sebastian Ott}
146dad572e370138539ea45be9b53d168568e562565Sebastian Ott
147dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ottstatic ssize_t ccwgroup_online_show(struct device *dev,
148dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott				    struct device_attribute *attr,
149dad572e370138539ea45be9b53d168568e562565Sebastian Ott				    char *buf)
150dad572e370138539ea45be9b53d168568e562565Sebastian Ott{
151dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
152dad572e370138539ea45be9b53d168568e562565Sebastian Ott	int online;
153dad572e370138539ea45be9b53d168568e562565Sebastian Ott
154dad572e370138539ea45be9b53d168568e562565Sebastian Ott	online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0;
155dad572e370138539ea45be9b53d168568e562565Sebastian Ott
156dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return scnprintf(buf, PAGE_SIZE, "%d\n", online);
157dad572e370138539ea45be9b53d168568e562565Sebastian Ott}
158dad572e370138539ea45be9b53d168568e562565Sebastian Ott
159c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter/*
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Provide an 'ungroup' attribute so the user can remove group devices no
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * longer needed or accidentially created. Saves memory :)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
163d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Sternstatic void ccwgroup_ungroup_callback(struct device *dev)
164d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern{
165d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
166d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern
167d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck	mutex_lock(&gdev->reg_mutex);
1681a908c735aed44c8bbed303371202e416813b271Cornelia Huck	if (device_is_registered(&gdev->dev)) {
1691a908c735aed44c8bbed303371202e416813b271Cornelia Huck		__ccwgroup_remove_symlinks(gdev);
1701a908c735aed44c8bbed303371202e416813b271Cornelia Huck		device_unregister(dev);
171c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		__ccwgroup_remove_cdev_refs(gdev);
1721a908c735aed44c8bbed303371202e416813b271Cornelia Huck	}
173d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck	mutex_unlock(&gdev->reg_mutex);
174d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern}
175d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern
176dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic ssize_t ccwgroup_ungroup_store(struct device *dev,
177dad572e370138539ea45be9b53d168568e562565Sebastian Ott				      struct device_attribute *attr,
178dad572e370138539ea45be9b53d168568e562565Sebastian Ott				      const char *buf, size_t count)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
180dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
181d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern	int rc;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
183c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter	/* Prevent concurrent online/offline processing and ungrouping. */
184c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
185c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter		return -EAGAIN;
186c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter	if (gdev->state != CCWGROUP_OFFLINE) {
187c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter		rc = -EINVAL;
188c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter		goto out;
189c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter	}
190d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern	/* Note that we cannot unregister the device from one of its
191d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern	 * attribute methods, so we have to use this roundabout approach.
192d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern	 */
193d9a9cdfb078d755e648d53ec25b7370f84ee5729Alan Stern	rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
194c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiterout:
195c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter	if (rc) {
196669420644c79c207f83fdf9105ae782867e2991fAlex Chiang		if (rc != -EAGAIN)
197669420644c79c207f83fdf9105ae782867e2991fAlex Chiang			/* Release onoff "lock" when ungrouping failed. */
198669420644c79c207f83fdf9105ae782867e2991fAlex Chiang			atomic_set(&gdev->onoff, 0);
199c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter		return rc;
200c619d4223eaa063dd15ce44235b04487235f8cb7Peter Oberparleiter	}
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
204dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ottstatic DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
205dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott
206dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ottstatic struct attribute *ccwgroup_attrs[] = {
207dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	&dev_attr_online.attr,
208dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	&dev_attr_ungroup.attr,
209dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	NULL,
210dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott};
211dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ottstatic struct attribute_group ccwgroup_attr_group = {
212dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	.attrs = ccwgroup_attrs,
213dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott};
214dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ottstatic const struct attribute_group *ccwgroup_attr_groups[] = {
215dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	&ccwgroup_attr_group,
216dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	NULL,
217dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott};
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
219dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic void ccwgroup_release(struct device *dev)
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
221c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	kfree(to_ccwgroupdev(dev));
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
224dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char str[8];
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, rc;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < gdev->count; i++) {
230dad572e370138539ea45be9b53d168568e562565Sebastian Ott		rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj,
231dad572e370138539ea45be9b53d168568e562565Sebastian Ott				       &gdev->dev.kobj, "group_device");
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc) {
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (--i; i >= 0; i--)
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  "group_device");
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return rc;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < gdev->count; i++) {
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sprintf(str, "cdev%d", i);
241dad572e370138539ea45be9b53d168568e562565Sebastian Ott		rc = sysfs_create_link(&gdev->dev.kobj,
242dad572e370138539ea45be9b53d168568e562565Sebastian Ott				       &gdev->cdev[i]->dev.kobj, str);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc) {
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (--i; i >= 0; i--) {
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sprintf(str, "cdev%d", i);
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sysfs_remove_link(&gdev->dev.kobj, str);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (i = 0; i < gdev->count; i++)
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  "group_device");
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return rc;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
257022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braunstatic int __get_next_bus_id(const char **buf, char *bus_id)
258022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun{
259022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	int rc, len;
260022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	char *start, *end;
261022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun
262022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	start = (char *)*buf;
263022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	end = strchr(start, ',');
264022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	if (!end) {
265022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		/* Last entry. Strip trailing newline, if applicable. */
266022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		end = strchr(start, '\n');
267022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		if (end)
268022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun			*end = '\0';
269022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		len = strlen(start) + 1;
270022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	} else {
271022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		len = end - start + 1;
272022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		end++;
273022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	}
27498df67b324a2a986987ce29986e44ae9156b6698Kay Sievers	if (len < CCW_BUS_ID_SIZE) {
275022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		strlcpy(bus_id, start, len);
276022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		rc = 0;
277022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	} else
278022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		rc = -EINVAL;
279022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	*buf = end;
280022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	return rc;
281022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun}
282022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun
28398df67b324a2a986987ce29986e44ae9156b6698Kay Sieversstatic int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
284022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun{
285022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	int cssid, ssid, devno;
286022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun
287022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	/* Must be of form %x.%x.%04x */
288022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
289022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		return 0;
290022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	return 1;
291022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun}
292022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun
293b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck/**
294022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun * ccwgroup_create_from_string() - create and register a ccw group device
295b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @root: parent device for the new device
296b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @creator_id: identifier of creating driver
297b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @cdrv: ccw driver of slave devices
298022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun * @num_devices: number of slave devices
299022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun * @buf: buffer containing comma separated bus ids of slave devices
300b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *
301b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * Create and register a new ccw group device as a child of @root. Slave
302022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun * devices are obtained from the list of bus ids given in @buf and must all
303b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * belong to @cdrv.
304b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * Returns:
305b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *  %0 on success and an error code on failure.
306b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * Context:
307b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *  non-atomic
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
309022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braunint ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
310022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun				struct ccw_driver *cdrv, int num_devices,
311022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun				const char *buf)
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ccwgroup_device *gdev;
314022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	int rc, i;
31598df67b324a2a986987ce29986e44ae9156b6698Kay Sievers	char tmp_bus_id[CCW_BUS_ID_SIZE];
316022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	const char *curr_buf;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
318022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
319022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		       GFP_KERNEL);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!gdev)
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_set(&gdev->onoff, 0);
324d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck	mutex_init(&gdev->reg_mutex);
325d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck	mutex_lock(&gdev->reg_mutex);
32616f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	gdev->creator_id = creator_id;
32716f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	gdev->count = num_devices;
32816f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	gdev->dev.bus = &ccwgroup_bus_type;
32916f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	gdev->dev.parent = root;
33016f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	gdev->dev.release = ccwgroup_release;
33116f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	device_initialize(&gdev->dev);
33216f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter
333022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	curr_buf = buf;
334022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	for (i = 0; i < num_devices && curr_buf; i++) {
335022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
336022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		if (rc != 0)
337022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun			goto error;
338022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		if (!__is_valid_bus_id(tmp_bus_id)) {
339022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun			rc = -EINVAL;
340022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun			goto error;
341022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		}
342022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id);
343022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		/*
344022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		 * All devices have to be of the same type in
345022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		 * order to be grouped.
346022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		 */
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!gdev->cdev[i]
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    || gdev->cdev[i]->id.driver_info !=
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    gdev->cdev[0]->id.driver_info) {
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = -EINVAL;
351d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck			goto error;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Don't allow a device to belong to more than one group. */
354c560d105a197464603247bf55962fc7f23c8cb62Sebastian Ott		spin_lock_irq(gdev->cdev[i]->ccwlock);
355db6a64238a927777e6e7b251927313f186455b1cCornelia Huck		if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
356c560d105a197464603247bf55962fc7f23c8cb62Sebastian Ott			spin_unlock_irq(gdev->cdev[i]->ccwlock);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = -EINVAL;
358d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck			goto error;
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
360db6a64238a927777e6e7b251927313f186455b1cCornelia Huck		dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
361c560d105a197464603247bf55962fc7f23c8cb62Sebastian Ott		spin_unlock_irq(gdev->cdev[i]->ccwlock);
36217088229846c078aa936ca64912ab221d083aca1Cornelia Huck	}
363022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	/* Check for sufficient number of bus ids. */
364022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	if (i < num_devices && !curr_buf) {
365022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		rc = -EINVAL;
366022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		goto error;
367022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	}
368022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	/* Check for trailing stuff. */
369022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	if (i == num_devices && strlen(curr_buf) > 0) {
370022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		rc = -EINVAL;
371022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun		goto error;
372022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741bf5b2853925cf92bfc5f0eddb68a8ed18782845Cornelia Huck	dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
375dbdf1afcaaabe83dea15a3cb9b9013e73ae3b1adSebastian Ott	gdev->dev.groups = ccwgroup_attr_groups;
37616f7f9564c3ae190954f2ec55f385a268b93ac4dPeter Oberparleiter	rc = device_add(&gdev->dev);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
378d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck		goto error;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = __ccwgroup_create_symlinks(gdev);
380dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (rc) {
381dad572e370138539ea45be9b53d168568e562565Sebastian Ott		device_del(&gdev->dev);
382dad572e370138539ea45be9b53d168568e562565Sebastian Ott		goto error;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
384dad572e370138539ea45be9b53d168568e562565Sebastian Ott	mutex_unlock(&gdev->reg_mutex);
385dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return 0;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
387022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula Braun	for (i = 0; i < num_devices; i++)
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (gdev->cdev[i]) {
389c560d105a197464603247bf55962fc7f23c8cb62Sebastian Ott			spin_lock_irq(gdev->cdev[i]->ccwlock);
390db6a64238a927777e6e7b251927313f186455b1cCornelia Huck			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
391db6a64238a927777e6e7b251927313f186455b1cCornelia Huck				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
392c560d105a197464603247bf55962fc7f23c8cb62Sebastian Ott			spin_unlock_irq(gdev->cdev[i]->ccwlock);
39317088229846c078aa936ca64912ab221d083aca1Cornelia Huck			put_device(&gdev->cdev[i]->dev);
394f26fd5d6e5006eae75a389c7ce74ed16761d094bCornelia Huck			gdev->cdev[i] = NULL;
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
396d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck	mutex_unlock(&gdev->reg_mutex);
397d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck	put_device(&gdev->dev);
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
400022b660ae5d075ed9eaddef6f6fb7abb48bdf63bUrsula BraunEXPORT_SYMBOL(ccwgroup_create_from_string);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
402e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ottstatic int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
403dad572e370138539ea45be9b53d168568e562565Sebastian Ott			     void *data)
404dad572e370138539ea45be9b53d168568e562565Sebastian Ott{
405dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct device *dev = data;
406dad572e370138539ea45be9b53d168568e562565Sebastian Ott
407dad572e370138539ea45be9b53d168568e562565Sebastian Ott	if (action == BUS_NOTIFY_UNBIND_DRIVER)
408dad572e370138539ea45be9b53d168568e562565Sebastian Ott		device_schedule_callback(dev, ccwgroup_ungroup_callback);
409dad572e370138539ea45be9b53d168568e562565Sebastian Ott
410dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return NOTIFY_OK;
411dad572e370138539ea45be9b53d168568e562565Sebastian Ott}
412e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott
413e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ottstatic struct notifier_block ccwgroup_nb = {
414e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	.notifier_call = ccwgroup_notifier
415e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott};
416e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott
417e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ottstatic int __init init_ccwgroup(void)
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
419e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	int ret;
420e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott
421e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	ret = bus_register(&ccwgroup_bus_type);
422e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	if (ret)
423e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott		return ret;
424e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott
425e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
426e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	if (ret)
427e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott		bus_unregister(&ccwgroup_bus_type);
428e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott
429e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	return ret;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
432e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ottstatic void __exit cleanup_ccwgroup(void)
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
434e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
435e909074bb91773680c0b2e49ea8af9f85c6f59bdSebastian Ott	bus_unregister(&ccwgroup_bus_type);
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_ccwgroup);
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_ccwgroup);
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/************************** driver stuff ******************************/
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
443dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int ccwgroup_probe(struct device *dev)
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
445dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
446dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
448dad572e370138539ea45be9b53d168568e562565Sebastian Ott	return gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int ccwgroup_remove(struct device *dev)
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
453dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
454dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
45650f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott	if (!dev->driver)
45750f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott		return 0;
45850f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott	if (gdrv->remove)
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		gdrv->remove(gdev);
46050f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
46401bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huckstatic void ccwgroup_shutdown(struct device *dev)
46501bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huck{
466dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
467dad572e370138539ea45be9b53d168568e562565Sebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
46801bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huck
46950f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott	if (!dev->driver)
47050f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott		return;
47150f1548399b7bd00ceb38c84a84463a89c82afe8Sebastian Ott	if (gdrv->shutdown)
47201bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huck		gdrv->shutdown(gdev);
47301bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huck}
47401bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huck
4757e597a21a1470b12428cb0edd03c40986026451fSebastian Ottstatic int ccwgroup_pm_prepare(struct device *dev)
4767e597a21a1470b12428cb0edd03c40986026451fSebastian Ott{
4777e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
4787e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
4797e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
4807e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	/* Fail while device is being set online/offline. */
4817e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (atomic_read(&gdev->onoff))
4827e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		return -EAGAIN;
4837e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
4847e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
4857e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		return 0;
4867e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
4877e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	return gdrv->prepare ? gdrv->prepare(gdev) : 0;
4887e597a21a1470b12428cb0edd03c40986026451fSebastian Ott}
4897e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
4907e597a21a1470b12428cb0edd03c40986026451fSebastian Ottstatic void ccwgroup_pm_complete(struct device *dev)
4917e597a21a1470b12428cb0edd03c40986026451fSebastian Ott{
4927e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
4937e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
4947e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
4957e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
4967e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		return;
4977e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
4987e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (gdrv->complete)
4997e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		gdrv->complete(gdev);
5007e597a21a1470b12428cb0edd03c40986026451fSebastian Ott}
5017e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5027e597a21a1470b12428cb0edd03c40986026451fSebastian Ottstatic int ccwgroup_pm_freeze(struct device *dev)
5037e597a21a1470b12428cb0edd03c40986026451fSebastian Ott{
5047e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
5057e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
5067e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5077e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
5087e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		return 0;
5097e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5107e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	return gdrv->freeze ? gdrv->freeze(gdev) : 0;
5117e597a21a1470b12428cb0edd03c40986026451fSebastian Ott}
5127e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5137e597a21a1470b12428cb0edd03c40986026451fSebastian Ottstatic int ccwgroup_pm_thaw(struct device *dev)
5147e597a21a1470b12428cb0edd03c40986026451fSebastian Ott{
5157e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
5167e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
5177e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5187e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
5197e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		return 0;
5207e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5217e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	return gdrv->thaw ? gdrv->thaw(gdev) : 0;
5227e597a21a1470b12428cb0edd03c40986026451fSebastian Ott}
5237e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5247e597a21a1470b12428cb0edd03c40986026451fSebastian Ottstatic int ccwgroup_pm_restore(struct device *dev)
5257e597a21a1470b12428cb0edd03c40986026451fSebastian Ott{
5267e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
5277e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
5287e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5297e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
5307e597a21a1470b12428cb0edd03c40986026451fSebastian Ott		return 0;
5317e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
5327e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	return gdrv->restore ? gdrv->restore(gdev) : 0;
5337e597a21a1470b12428cb0edd03c40986026451fSebastian Ott}
5347e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
535471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops ccwgroup_pm_ops = {
5367e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	.prepare = ccwgroup_pm_prepare,
5377e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	.complete = ccwgroup_pm_complete,
5387e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	.freeze = ccwgroup_pm_freeze,
5397e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	.thaw = ccwgroup_pm_thaw,
5407e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	.restore = ccwgroup_pm_restore,
5417e597a21a1470b12428cb0edd03c40986026451fSebastian Ott};
5427e597a21a1470b12428cb0edd03c40986026451fSebastian Ott
543f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell Kingstatic struct bus_type ccwgroup_bus_type = {
544f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell King	.name   = "ccwgroup",
545f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell King	.match  = ccwgroup_bus_match,
546f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell King	.probe  = ccwgroup_probe,
547f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell King	.remove = ccwgroup_remove,
54801bc8ad165490458a8feb744c8f401c1a7098e3aCornelia Huck	.shutdown = ccwgroup_shutdown,
5497e597a21a1470b12428cb0edd03c40986026451fSebastian Ott	.pm = &ccwgroup_pm_ops,
550f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell King};
551f9ccf4569ac4597e9e09d301ca362d90b4a1046dRussell King
552b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck/**
553b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * ccwgroup_driver_register() - register a ccw group driver
554b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @cdriver: driver to be registered
555b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *
556b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * This function is mainly a wrapper around driver_register().
557b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck */
558b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huckint ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* register our new driver with the core */
561292888c81e74115db5e5a4a838f730a7c3662982Heiko Carstens	cdriver->driver.bus = &ccwgroup_bus_type;
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return driver_register(&cdriver->driver);
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
565dad572e370138539ea45be9b53d168568e562565Sebastian OttEXPORT_SYMBOL(ccwgroup_driver_register);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
567dad572e370138539ea45be9b53d168568e562565Sebastian Ottstatic int __ccwgroup_match_all(struct device *dev, void *data)
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
569887ab5992925736ab23985c35f8149739e9de354Cornelia Huck	return 1;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
572b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck/**
573b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * ccwgroup_driver_unregister() - deregister a ccw group driver
574b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @cdriver: driver to be deregistered
575b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *
576b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * This function is mainly a wrapper around driver_unregister().
577b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck */
578b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huckvoid ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
580887ab5992925736ab23985c35f8149739e9de354Cornelia Huck	struct device *dev;
581887ab5992925736ab23985c35f8149739e9de354Cornelia Huck
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We don't want ccwgroup devices to live longer than their driver. */
583887ab5992925736ab23985c35f8149739e9de354Cornelia Huck	while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
584887ab5992925736ab23985c35f8149739e9de354Cornelia Huck					 __ccwgroup_match_all))) {
585d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck		struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
586d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck
587d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck		mutex_lock(&gdev->reg_mutex);
588d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck		__ccwgroup_remove_symlinks(gdev);
589887ab5992925736ab23985c35f8149739e9de354Cornelia Huck		device_unregister(dev);
590c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		__ccwgroup_remove_cdev_refs(gdev);
591d76123eb357a4baa653714183df286c1bb99f707Cornelia Huck		mutex_unlock(&gdev->reg_mutex);
592887ab5992925736ab23985c35f8149739e9de354Cornelia Huck		put_device(dev);
593887ab5992925736ab23985c35f8149739e9de354Cornelia Huck	}
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister(&cdriver->driver);
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
596dad572e370138539ea45be9b53d168568e562565Sebastian OttEXPORT_SYMBOL(ccwgroup_driver_unregister);
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
598b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck/**
599b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * ccwgroup_probe_ccwdev() - probe function for slave devices
600b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @cdev: ccw device to be probed
601b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *
602b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * This is a dummy probe function for ccw devices that are slave devices in
603b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * a ccw group device.
604b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * Returns:
605b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *  always %0
606b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck */
607b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huckint ccwgroup_probe_ccwdev(struct ccw_device *cdev)
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
611dad572e370138539ea45be9b53d168568e562565Sebastian OttEXPORT_SYMBOL(ccwgroup_probe_ccwdev);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
613b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck/**
614b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * ccwgroup_remove_ccwdev() - remove function for slave devices
615b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * @cdev: ccw device to be removed
616b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck *
617b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * This is a remove function for ccw devices that are slave devices in a ccw
618b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * group device. It sets the ccw device offline and also deregisters the
619b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck * embedding ccw group device.
620b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huck */
621b2ffd8e9a76ec90bd4a509f3d092e35978c568a3Cornelia Huckvoid ccwgroup_remove_ccwdev(struct ccw_device *cdev)
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ccwgroup_device *gdev;
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Ignore offlining errors, device is gone anyway. */
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ccw_device_set_offline(cdev);
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If one of its devices is gone, the whole group is done for. */
628c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	spin_lock_irq(cdev->ccwlock);
629c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	gdev = dev_get_drvdata(&cdev->dev);
630c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	if (!gdev) {
631c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		spin_unlock_irq(cdev->ccwlock);
632c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		return;
633c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	}
634c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	/* Get ccwgroup device reference for local processing. */
635c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	get_device(&gdev->dev);
636c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	spin_unlock_irq(cdev->ccwlock);
637c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	/* Unregister group device. */
638c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	mutex_lock(&gdev->reg_mutex);
639c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	if (device_is_registered(&gdev->dev)) {
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__ccwgroup_remove_symlinks(gdev);
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_unregister(&gdev->dev);
642c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter		__ccwgroup_remove_cdev_refs(gdev);
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
644c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	mutex_unlock(&gdev->reg_mutex);
645c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	/* Release ccwgroup device reference for local processing. */
646c03017544e3b2e60aa3c8ae451fac01595f1bf11Peter Oberparleiter	put_device(&gdev->dev);
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(ccwgroup_remove_ccwdev);
649dad572e370138539ea45be9b53d168568e562565Sebastian OttMODULE_LICENSE("GPL");
650