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