1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23764e82eSJohannes Thumshirn /*
33764e82eSJohannes Thumshirn * MEN Chameleon Bus.
43764e82eSJohannes Thumshirn *
53764e82eSJohannes Thumshirn * Copyright (C) 2013 MEN Mikroelektronik GmbH (www.men.de)
63764e82eSJohannes Thumshirn * Author: Johannes Thumshirn <johannes.thumshirn@men.de>
73764e82eSJohannes Thumshirn */
83764e82eSJohannes Thumshirn #include <linux/kernel.h>
93764e82eSJohannes Thumshirn #include <linux/module.h>
103764e82eSJohannes Thumshirn #include <linux/slab.h>
113764e82eSJohannes Thumshirn #include <linux/types.h>
123764e82eSJohannes Thumshirn #include <linux/idr.h>
133764e82eSJohannes Thumshirn #include <linux/mcb.h>
143764e82eSJohannes Thumshirn
153764e82eSJohannes Thumshirn static DEFINE_IDA(mcb_ida);
163764e82eSJohannes Thumshirn
mcb_match_id(const struct mcb_device_id * ids,struct mcb_device * dev)173764e82eSJohannes Thumshirn static const struct mcb_device_id *mcb_match_id(const struct mcb_device_id *ids,
183764e82eSJohannes Thumshirn struct mcb_device *dev)
193764e82eSJohannes Thumshirn {
203764e82eSJohannes Thumshirn if (ids) {
213764e82eSJohannes Thumshirn while (ids->device) {
223764e82eSJohannes Thumshirn if (ids->device == dev->id)
233764e82eSJohannes Thumshirn return ids;
243764e82eSJohannes Thumshirn ids++;
253764e82eSJohannes Thumshirn }
263764e82eSJohannes Thumshirn }
273764e82eSJohannes Thumshirn
283764e82eSJohannes Thumshirn return NULL;
293764e82eSJohannes Thumshirn }
303764e82eSJohannes Thumshirn
mcb_match(struct device * dev,struct device_driver * drv)313764e82eSJohannes Thumshirn static int mcb_match(struct device *dev, struct device_driver *drv)
323764e82eSJohannes Thumshirn {
333764e82eSJohannes Thumshirn struct mcb_driver *mdrv = to_mcb_driver(drv);
343764e82eSJohannes Thumshirn struct mcb_device *mdev = to_mcb_device(dev);
353764e82eSJohannes Thumshirn const struct mcb_device_id *found_id;
363764e82eSJohannes Thumshirn
373764e82eSJohannes Thumshirn found_id = mcb_match_id(mdrv->id_table, mdev);
383764e82eSJohannes Thumshirn if (found_id)
393764e82eSJohannes Thumshirn return 1;
403764e82eSJohannes Thumshirn
413764e82eSJohannes Thumshirn return 0;
423764e82eSJohannes Thumshirn }
433764e82eSJohannes Thumshirn
mcb_uevent(const struct device * dev,struct kobj_uevent_env * env)442a81ada3SGreg Kroah-Hartman static int mcb_uevent(const struct device *dev, struct kobj_uevent_env *env)
453764e82eSJohannes Thumshirn {
462a81ada3SGreg Kroah-Hartman const struct mcb_device *mdev = to_mcb_device(dev);
473764e82eSJohannes Thumshirn int ret;
483764e82eSJohannes Thumshirn
493764e82eSJohannes Thumshirn ret = add_uevent_var(env, "MODALIAS=mcb:16z%03d", mdev->id);
503764e82eSJohannes Thumshirn if (ret)
513764e82eSJohannes Thumshirn return -ENOMEM;
523764e82eSJohannes Thumshirn
533764e82eSJohannes Thumshirn return 0;
543764e82eSJohannes Thumshirn }
553764e82eSJohannes Thumshirn
mcb_probe(struct device * dev)563764e82eSJohannes Thumshirn static int mcb_probe(struct device *dev)
573764e82eSJohannes Thumshirn {
583764e82eSJohannes Thumshirn struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
593764e82eSJohannes Thumshirn struct mcb_device *mdev = to_mcb_device(dev);
603764e82eSJohannes Thumshirn const struct mcb_device_id *found_id;
614d2ec857SJohannes Thumshirn struct module *carrier_mod;
624d2ec857SJohannes Thumshirn int ret;
633764e82eSJohannes Thumshirn
643764e82eSJohannes Thumshirn found_id = mcb_match_id(mdrv->id_table, mdev);
653764e82eSJohannes Thumshirn if (!found_id)
663764e82eSJohannes Thumshirn return -ENODEV;
673764e82eSJohannes Thumshirn
684d2ec857SJohannes Thumshirn carrier_mod = mdev->dev.parent->driver->owner;
694d2ec857SJohannes Thumshirn if (!try_module_get(carrier_mod))
704d2ec857SJohannes Thumshirn return -EINVAL;
714d2ec857SJohannes Thumshirn
727bc36409SJohannes Thumshirn get_device(dev);
734d2ec857SJohannes Thumshirn ret = mdrv->probe(mdev, found_id);
74d7237462SZhengchao Shao if (ret) {
754d2ec857SJohannes Thumshirn module_put(carrier_mod);
76d7237462SZhengchao Shao put_device(dev);
77d7237462SZhengchao Shao }
784d2ec857SJohannes Thumshirn
794d2ec857SJohannes Thumshirn return ret;
803764e82eSJohannes Thumshirn }
813764e82eSJohannes Thumshirn
mcb_remove(struct device * dev)82fc7a6209SUwe Kleine-König static void mcb_remove(struct device *dev)
833764e82eSJohannes Thumshirn {
843764e82eSJohannes Thumshirn struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
853764e82eSJohannes Thumshirn struct mcb_device *mdev = to_mcb_device(dev);
864d2ec857SJohannes Thumshirn struct module *carrier_mod;
873764e82eSJohannes Thumshirn
883764e82eSJohannes Thumshirn mdrv->remove(mdev);
893764e82eSJohannes Thumshirn
904d2ec857SJohannes Thumshirn carrier_mod = mdev->dev.parent->driver->owner;
914d2ec857SJohannes Thumshirn module_put(carrier_mod);
924d2ec857SJohannes Thumshirn
933764e82eSJohannes Thumshirn put_device(&mdev->dev);
943764e82eSJohannes Thumshirn }
953764e82eSJohannes Thumshirn
mcb_shutdown(struct device * dev)963764e82eSJohannes Thumshirn static void mcb_shutdown(struct device *dev)
973764e82eSJohannes Thumshirn {
985d9e2ab9SJohannes Thumshirn struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
993764e82eSJohannes Thumshirn struct mcb_device *mdev = to_mcb_device(dev);
1003764e82eSJohannes Thumshirn
1013764e82eSJohannes Thumshirn if (mdrv && mdrv->shutdown)
1023764e82eSJohannes Thumshirn mdrv->shutdown(mdev);
1033764e82eSJohannes Thumshirn }
1043764e82eSJohannes Thumshirn
revision_show(struct device * dev,struct device_attribute * attr,char * buf)105803f1ca6SJohannes Thumshirn static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
106803f1ca6SJohannes Thumshirn char *buf)
107803f1ca6SJohannes Thumshirn {
108803f1ca6SJohannes Thumshirn struct mcb_bus *bus = to_mcb_bus(dev);
109803f1ca6SJohannes Thumshirn
110803f1ca6SJohannes Thumshirn return scnprintf(buf, PAGE_SIZE, "%d\n", bus->revision);
111803f1ca6SJohannes Thumshirn }
112803f1ca6SJohannes Thumshirn static DEVICE_ATTR_RO(revision);
113803f1ca6SJohannes Thumshirn
model_show(struct device * dev,struct device_attribute * attr,char * buf)114803f1ca6SJohannes Thumshirn static ssize_t model_show(struct device *dev, struct device_attribute *attr,
115803f1ca6SJohannes Thumshirn char *buf)
116803f1ca6SJohannes Thumshirn {
117803f1ca6SJohannes Thumshirn struct mcb_bus *bus = to_mcb_bus(dev);
118803f1ca6SJohannes Thumshirn
119803f1ca6SJohannes Thumshirn return scnprintf(buf, PAGE_SIZE, "%c\n", bus->model);
120803f1ca6SJohannes Thumshirn }
121803f1ca6SJohannes Thumshirn static DEVICE_ATTR_RO(model);
122803f1ca6SJohannes Thumshirn
minor_show(struct device * dev,struct device_attribute * attr,char * buf)123803f1ca6SJohannes Thumshirn static ssize_t minor_show(struct device *dev, struct device_attribute *attr,
124803f1ca6SJohannes Thumshirn char *buf)
125803f1ca6SJohannes Thumshirn {
126803f1ca6SJohannes Thumshirn struct mcb_bus *bus = to_mcb_bus(dev);
127803f1ca6SJohannes Thumshirn
128803f1ca6SJohannes Thumshirn return scnprintf(buf, PAGE_SIZE, "%d\n", bus->minor);
129803f1ca6SJohannes Thumshirn }
130803f1ca6SJohannes Thumshirn static DEVICE_ATTR_RO(minor);
131803f1ca6SJohannes Thumshirn
name_show(struct device * dev,struct device_attribute * attr,char * buf)132803f1ca6SJohannes Thumshirn static ssize_t name_show(struct device *dev, struct device_attribute *attr,
133803f1ca6SJohannes Thumshirn char *buf)
134803f1ca6SJohannes Thumshirn {
135803f1ca6SJohannes Thumshirn struct mcb_bus *bus = to_mcb_bus(dev);
136803f1ca6SJohannes Thumshirn
137803f1ca6SJohannes Thumshirn return scnprintf(buf, PAGE_SIZE, "%s\n", bus->name);
138803f1ca6SJohannes Thumshirn }
139803f1ca6SJohannes Thumshirn static DEVICE_ATTR_RO(name);
140803f1ca6SJohannes Thumshirn
141803f1ca6SJohannes Thumshirn static struct attribute *mcb_bus_attrs[] = {
142803f1ca6SJohannes Thumshirn &dev_attr_revision.attr,
143803f1ca6SJohannes Thumshirn &dev_attr_model.attr,
144803f1ca6SJohannes Thumshirn &dev_attr_minor.attr,
145803f1ca6SJohannes Thumshirn &dev_attr_name.attr,
146803f1ca6SJohannes Thumshirn NULL,
147803f1ca6SJohannes Thumshirn };
148803f1ca6SJohannes Thumshirn
149803f1ca6SJohannes Thumshirn static const struct attribute_group mcb_carrier_group = {
150803f1ca6SJohannes Thumshirn .attrs = mcb_bus_attrs,
151803f1ca6SJohannes Thumshirn };
152803f1ca6SJohannes Thumshirn
153803f1ca6SJohannes Thumshirn static const struct attribute_group *mcb_carrier_groups[] = {
154803f1ca6SJohannes Thumshirn &mcb_carrier_group,
155803f1ca6SJohannes Thumshirn NULL,
156803f1ca6SJohannes Thumshirn };
157803f1ca6SJohannes Thumshirn
158803f1ca6SJohannes Thumshirn
1593764e82eSJohannes Thumshirn static struct bus_type mcb_bus_type = {
1603764e82eSJohannes Thumshirn .name = "mcb",
1613764e82eSJohannes Thumshirn .match = mcb_match,
1623764e82eSJohannes Thumshirn .uevent = mcb_uevent,
1633764e82eSJohannes Thumshirn .probe = mcb_probe,
1643764e82eSJohannes Thumshirn .remove = mcb_remove,
1653764e82eSJohannes Thumshirn .shutdown = mcb_shutdown,
1663764e82eSJohannes Thumshirn };
1673764e82eSJohannes Thumshirn
168803f1ca6SJohannes Thumshirn static struct device_type mcb_carrier_device_type = {
169803f1ca6SJohannes Thumshirn .name = "mcb-carrier",
170803f1ca6SJohannes Thumshirn .groups = mcb_carrier_groups,
171803f1ca6SJohannes Thumshirn };
172803f1ca6SJohannes Thumshirn
1733764e82eSJohannes Thumshirn /**
1743764e82eSJohannes Thumshirn * __mcb_register_driver() - Register a @mcb_driver at the system
1753764e82eSJohannes Thumshirn * @drv: The @mcb_driver
1763764e82eSJohannes Thumshirn * @owner: The @mcb_driver's module
1773764e82eSJohannes Thumshirn * @mod_name: The name of the @mcb_driver's module
1783764e82eSJohannes Thumshirn *
1793764e82eSJohannes Thumshirn * Register a @mcb_driver at the system. Perform some sanity checks, if
1803764e82eSJohannes Thumshirn * the .probe and .remove methods are provided by the driver.
1813764e82eSJohannes Thumshirn */
__mcb_register_driver(struct mcb_driver * drv,struct module * owner,const char * mod_name)1823764e82eSJohannes Thumshirn int __mcb_register_driver(struct mcb_driver *drv, struct module *owner,
1833764e82eSJohannes Thumshirn const char *mod_name)
1843764e82eSJohannes Thumshirn {
1853764e82eSJohannes Thumshirn if (!drv->probe || !drv->remove)
1863764e82eSJohannes Thumshirn return -EINVAL;
1873764e82eSJohannes Thumshirn
1883764e82eSJohannes Thumshirn drv->driver.owner = owner;
1893764e82eSJohannes Thumshirn drv->driver.bus = &mcb_bus_type;
1903764e82eSJohannes Thumshirn drv->driver.mod_name = mod_name;
1913764e82eSJohannes Thumshirn
1923764e82eSJohannes Thumshirn return driver_register(&drv->driver);
1933764e82eSJohannes Thumshirn }
194891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(__mcb_register_driver, MCB);
1953764e82eSJohannes Thumshirn
1963764e82eSJohannes Thumshirn /**
1973764e82eSJohannes Thumshirn * mcb_unregister_driver() - Unregister a @mcb_driver from the system
1983764e82eSJohannes Thumshirn * @drv: The @mcb_driver
1993764e82eSJohannes Thumshirn *
2003764e82eSJohannes Thumshirn * Unregister a @mcb_driver from the system.
2013764e82eSJohannes Thumshirn */
mcb_unregister_driver(struct mcb_driver * drv)2023764e82eSJohannes Thumshirn void mcb_unregister_driver(struct mcb_driver *drv)
2033764e82eSJohannes Thumshirn {
2043764e82eSJohannes Thumshirn driver_unregister(&drv->driver);
2053764e82eSJohannes Thumshirn }
206891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_unregister_driver, MCB);
2073764e82eSJohannes Thumshirn
mcb_release_dev(struct device * dev)2083764e82eSJohannes Thumshirn static void mcb_release_dev(struct device *dev)
2093764e82eSJohannes Thumshirn {
2103764e82eSJohannes Thumshirn struct mcb_device *mdev = to_mcb_device(dev);
2113764e82eSJohannes Thumshirn
2123764e82eSJohannes Thumshirn mcb_bus_put(mdev->bus);
2133764e82eSJohannes Thumshirn kfree(mdev);
2143764e82eSJohannes Thumshirn }
2153764e82eSJohannes Thumshirn
2163764e82eSJohannes Thumshirn /**
2173764e82eSJohannes Thumshirn * mcb_device_register() - Register a mcb_device
2183764e82eSJohannes Thumshirn * @bus: The @mcb_bus of the device
2193764e82eSJohannes Thumshirn * @dev: The @mcb_device
2203764e82eSJohannes Thumshirn *
2213764e82eSJohannes Thumshirn * Register a specific @mcb_device at a @mcb_bus and the system itself.
2223764e82eSJohannes Thumshirn */
mcb_device_register(struct mcb_bus * bus,struct mcb_device * dev)2233764e82eSJohannes Thumshirn int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
2243764e82eSJohannes Thumshirn {
2253764e82eSJohannes Thumshirn int ret;
2263764e82eSJohannes Thumshirn int device_id;
2273764e82eSJohannes Thumshirn
2283764e82eSJohannes Thumshirn device_initialize(&dev->dev);
2295d9e2ab9SJohannes Thumshirn mcb_bus_get(bus);
2303764e82eSJohannes Thumshirn dev->dev.bus = &mcb_bus_type;
2313764e82eSJohannes Thumshirn dev->dev.parent = bus->dev.parent;
2323764e82eSJohannes Thumshirn dev->dev.release = mcb_release_dev;
2332d8784dfSMichael Moese dev->dma_dev = bus->carrier;
2343764e82eSJohannes Thumshirn
2353764e82eSJohannes Thumshirn device_id = dev->id;
2363764e82eSJohannes Thumshirn dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d",
2373764e82eSJohannes Thumshirn bus->bus_nr, device_id, dev->inst, dev->group, dev->var);
2383764e82eSJohannes Thumshirn
2393764e82eSJohannes Thumshirn ret = device_add(&dev->dev);
2403764e82eSJohannes Thumshirn if (ret < 0) {
2413764e82eSJohannes Thumshirn pr_err("Failed registering device 16z%03d on bus mcb%d (%d)\n",
2423764e82eSJohannes Thumshirn device_id, bus->bus_nr, ret);
2433764e82eSJohannes Thumshirn goto out;
2443764e82eSJohannes Thumshirn }
2453764e82eSJohannes Thumshirn
2463764e82eSJohannes Thumshirn return 0;
2473764e82eSJohannes Thumshirn
2483764e82eSJohannes Thumshirn out:
249*07f2f69fSSanjuán García, Jorge put_device(&dev->dev);
2503764e82eSJohannes Thumshirn
2513764e82eSJohannes Thumshirn return ret;
2523764e82eSJohannes Thumshirn }
253891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_device_register, MCB);
2543764e82eSJohannes Thumshirn
mcb_free_bus(struct device * dev)2555d9e2ab9SJohannes Thumshirn static void mcb_free_bus(struct device *dev)
2565d9e2ab9SJohannes Thumshirn {
2575d9e2ab9SJohannes Thumshirn struct mcb_bus *bus = to_mcb_bus(dev);
2585d9e2ab9SJohannes Thumshirn
2595d9e2ab9SJohannes Thumshirn put_device(bus->carrier);
2603af0b1d3Skeliu ida_free(&mcb_ida, bus->bus_nr);
2615d9e2ab9SJohannes Thumshirn kfree(bus);
2625d9e2ab9SJohannes Thumshirn }
2635d9e2ab9SJohannes Thumshirn
2643764e82eSJohannes Thumshirn /**
2653764e82eSJohannes Thumshirn * mcb_alloc_bus() - Allocate a new @mcb_bus
2663764e82eSJohannes Thumshirn *
2673764e82eSJohannes Thumshirn * Allocate a new @mcb_bus.
2683764e82eSJohannes Thumshirn */
mcb_alloc_bus(struct device * carrier)2694ec65b77SJohannes Thumshirn struct mcb_bus *mcb_alloc_bus(struct device *carrier)
2703764e82eSJohannes Thumshirn {
2713764e82eSJohannes Thumshirn struct mcb_bus *bus;
2723764e82eSJohannes Thumshirn int bus_nr;
27318d28819SJohannes Thumshirn int rc;
2743764e82eSJohannes Thumshirn
2753764e82eSJohannes Thumshirn bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
2763764e82eSJohannes Thumshirn if (!bus)
2774ec65b77SJohannes Thumshirn return ERR_PTR(-ENOMEM);
2783764e82eSJohannes Thumshirn
2793af0b1d3Skeliu bus_nr = ida_alloc(&mcb_ida, GFP_KERNEL);
2803764e82eSJohannes Thumshirn if (bus_nr < 0) {
28125a14332SDan Carpenter kfree(bus);
28225a14332SDan Carpenter return ERR_PTR(bus_nr);
2833764e82eSJohannes Thumshirn }
2843764e82eSJohannes Thumshirn
2853764e82eSJohannes Thumshirn bus->bus_nr = bus_nr;
2865d9e2ab9SJohannes Thumshirn bus->carrier = get_device(carrier);
28718d28819SJohannes Thumshirn
28818d28819SJohannes Thumshirn device_initialize(&bus->dev);
28918d28819SJohannes Thumshirn bus->dev.parent = carrier;
29018d28819SJohannes Thumshirn bus->dev.bus = &mcb_bus_type;
291803f1ca6SJohannes Thumshirn bus->dev.type = &mcb_carrier_device_type;
2925d9e2ab9SJohannes Thumshirn bus->dev.release = &mcb_free_bus;
29318d28819SJohannes Thumshirn
29418d28819SJohannes Thumshirn dev_set_name(&bus->dev, "mcb:%d", bus_nr);
29518d28819SJohannes Thumshirn rc = device_add(&bus->dev);
29618d28819SJohannes Thumshirn if (rc)
29725a14332SDan Carpenter goto err_put;
29818d28819SJohannes Thumshirn
2993764e82eSJohannes Thumshirn return bus;
30025a14332SDan Carpenter
30125a14332SDan Carpenter err_put:
30225a14332SDan Carpenter put_device(&bus->dev);
30318d28819SJohannes Thumshirn return ERR_PTR(rc);
3043764e82eSJohannes Thumshirn }
305891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_alloc_bus, MCB);
3063764e82eSJohannes Thumshirn
__mcb_devices_unregister(struct device * dev,void * data)3073764e82eSJohannes Thumshirn static int __mcb_devices_unregister(struct device *dev, void *data)
3083764e82eSJohannes Thumshirn {
3093764e82eSJohannes Thumshirn device_unregister(dev);
3103764e82eSJohannes Thumshirn return 0;
3113764e82eSJohannes Thumshirn }
3123764e82eSJohannes Thumshirn
mcb_devices_unregister(struct mcb_bus * bus)3133764e82eSJohannes Thumshirn static void mcb_devices_unregister(struct mcb_bus *bus)
3143764e82eSJohannes Thumshirn {
3153764e82eSJohannes Thumshirn bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_devices_unregister);
3163764e82eSJohannes Thumshirn }
3173764e82eSJohannes Thumshirn /**
3183764e82eSJohannes Thumshirn * mcb_release_bus() - Free a @mcb_bus
3193764e82eSJohannes Thumshirn * @bus: The @mcb_bus to release
3203764e82eSJohannes Thumshirn *
3213764e82eSJohannes Thumshirn * Release an allocated @mcb_bus from the system.
3223764e82eSJohannes Thumshirn */
mcb_release_bus(struct mcb_bus * bus)3233764e82eSJohannes Thumshirn void mcb_release_bus(struct mcb_bus *bus)
3243764e82eSJohannes Thumshirn {
3253764e82eSJohannes Thumshirn mcb_devices_unregister(bus);
3263764e82eSJohannes Thumshirn }
327891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_release_bus, MCB);
3283764e82eSJohannes Thumshirn
3293764e82eSJohannes Thumshirn /**
3303764e82eSJohannes Thumshirn * mcb_bus_put() - Increment refcnt
3313764e82eSJohannes Thumshirn * @bus: The @mcb_bus
3323764e82eSJohannes Thumshirn *
3333764e82eSJohannes Thumshirn * Get a @mcb_bus' ref
3343764e82eSJohannes Thumshirn */
mcb_bus_get(struct mcb_bus * bus)3353764e82eSJohannes Thumshirn struct mcb_bus *mcb_bus_get(struct mcb_bus *bus)
3363764e82eSJohannes Thumshirn {
3373764e82eSJohannes Thumshirn if (bus)
3383764e82eSJohannes Thumshirn get_device(&bus->dev);
3393764e82eSJohannes Thumshirn
3403764e82eSJohannes Thumshirn return bus;
3413764e82eSJohannes Thumshirn }
342891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_bus_get, MCB);
3433764e82eSJohannes Thumshirn
3443764e82eSJohannes Thumshirn /**
3453764e82eSJohannes Thumshirn * mcb_bus_put() - Decrement refcnt
3463764e82eSJohannes Thumshirn * @bus: The @mcb_bus
3473764e82eSJohannes Thumshirn *
3483764e82eSJohannes Thumshirn * Release a @mcb_bus' ref
3493764e82eSJohannes Thumshirn */
mcb_bus_put(struct mcb_bus * bus)3503764e82eSJohannes Thumshirn void mcb_bus_put(struct mcb_bus *bus)
3513764e82eSJohannes Thumshirn {
3523764e82eSJohannes Thumshirn if (bus)
3533764e82eSJohannes Thumshirn put_device(&bus->dev);
3543764e82eSJohannes Thumshirn }
355891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_bus_put, MCB);
3563764e82eSJohannes Thumshirn
3573764e82eSJohannes Thumshirn /**
3583764e82eSJohannes Thumshirn * mcb_alloc_dev() - Allocate a device
3593764e82eSJohannes Thumshirn * @bus: The @mcb_bus the device is part of
3603764e82eSJohannes Thumshirn *
3613764e82eSJohannes Thumshirn * Allocate a @mcb_device and add bus.
3623764e82eSJohannes Thumshirn */
mcb_alloc_dev(struct mcb_bus * bus)3633764e82eSJohannes Thumshirn struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
3643764e82eSJohannes Thumshirn {
3653764e82eSJohannes Thumshirn struct mcb_device *dev;
3663764e82eSJohannes Thumshirn
3673764e82eSJohannes Thumshirn dev = kzalloc(sizeof(struct mcb_device), GFP_KERNEL);
3683764e82eSJohannes Thumshirn if (!dev)
3693764e82eSJohannes Thumshirn return NULL;
3703764e82eSJohannes Thumshirn
3713764e82eSJohannes Thumshirn dev->bus = bus;
3723764e82eSJohannes Thumshirn
3733764e82eSJohannes Thumshirn return dev;
3743764e82eSJohannes Thumshirn }
375891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_alloc_dev, MCB);
3763764e82eSJohannes Thumshirn
3773764e82eSJohannes Thumshirn /**
3783764e82eSJohannes Thumshirn * mcb_free_dev() - Free @mcb_device
3793764e82eSJohannes Thumshirn * @dev: The device to free
3803764e82eSJohannes Thumshirn *
3813764e82eSJohannes Thumshirn * Free a @mcb_device
3823764e82eSJohannes Thumshirn */
mcb_free_dev(struct mcb_device * dev)3833764e82eSJohannes Thumshirn void mcb_free_dev(struct mcb_device *dev)
3843764e82eSJohannes Thumshirn {
3853764e82eSJohannes Thumshirn kfree(dev);
3863764e82eSJohannes Thumshirn }
387891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_free_dev, MCB);
3883764e82eSJohannes Thumshirn
__mcb_bus_add_devices(struct device * dev,void * data)3893764e82eSJohannes Thumshirn static int __mcb_bus_add_devices(struct device *dev, void *data)
3903764e82eSJohannes Thumshirn {
3913764e82eSJohannes Thumshirn int retval;
3923764e82eSJohannes Thumshirn
3933764e82eSJohannes Thumshirn retval = device_attach(dev);
3940f28ada1SJorge Sanjuan Garcia if (retval < 0) {
3953764e82eSJohannes Thumshirn dev_err(dev, "Error adding device (%d)\n", retval);
3960f28ada1SJorge Sanjuan Garcia return retval;
3970f28ada1SJorge Sanjuan Garcia }
3983764e82eSJohannes Thumshirn
3993764e82eSJohannes Thumshirn return 0;
4003764e82eSJohannes Thumshirn }
4013764e82eSJohannes Thumshirn
4023764e82eSJohannes Thumshirn /**
4033764e82eSJohannes Thumshirn * mcb_bus_add_devices() - Add devices in the bus' internal device list
4043764e82eSJohannes Thumshirn * @bus: The @mcb_bus we add the devices
4053764e82eSJohannes Thumshirn *
4063764e82eSJohannes Thumshirn * Add devices in the bus' internal device list to the system.
4073764e82eSJohannes Thumshirn */
mcb_bus_add_devices(const struct mcb_bus * bus)4083764e82eSJohannes Thumshirn void mcb_bus_add_devices(const struct mcb_bus *bus)
4093764e82eSJohannes Thumshirn {
4103764e82eSJohannes Thumshirn bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
4113764e82eSJohannes Thumshirn }
412891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_bus_add_devices, MCB);
4133764e82eSJohannes Thumshirn
4143764e82eSJohannes Thumshirn /**
4152ce80087SJohannes Thumshirn * mcb_get_resource() - get a resource for a mcb device
4162ce80087SJohannes Thumshirn * @dev: the mcb device
4172ce80087SJohannes Thumshirn * @type: the type of resource
4182ce80087SJohannes Thumshirn */
mcb_get_resource(struct mcb_device * dev,unsigned int type)4192ce80087SJohannes Thumshirn struct resource *mcb_get_resource(struct mcb_device *dev, unsigned int type)
4202ce80087SJohannes Thumshirn {
4212ce80087SJohannes Thumshirn if (type == IORESOURCE_MEM)
4222ce80087SJohannes Thumshirn return &dev->mem;
4232ce80087SJohannes Thumshirn else if (type == IORESOURCE_IRQ)
4242ce80087SJohannes Thumshirn return &dev->irq;
4252ce80087SJohannes Thumshirn else
4262ce80087SJohannes Thumshirn return NULL;
4272ce80087SJohannes Thumshirn }
428891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_get_resource, MCB);
4292ce80087SJohannes Thumshirn
4302ce80087SJohannes Thumshirn /**
4313764e82eSJohannes Thumshirn * mcb_request_mem() - Request memory
4323764e82eSJohannes Thumshirn * @dev: The @mcb_device the memory is for
4333764e82eSJohannes Thumshirn * @name: The name for the memory reference.
4343764e82eSJohannes Thumshirn *
4353764e82eSJohannes Thumshirn * Request memory for a @mcb_device. If @name is NULL the driver name will
4363764e82eSJohannes Thumshirn * be used.
4373764e82eSJohannes Thumshirn */
mcb_request_mem(struct mcb_device * dev,const char * name)4383764e82eSJohannes Thumshirn struct resource *mcb_request_mem(struct mcb_device *dev, const char *name)
4393764e82eSJohannes Thumshirn {
4403764e82eSJohannes Thumshirn struct resource *mem;
4413764e82eSJohannes Thumshirn u32 size;
4423764e82eSJohannes Thumshirn
4433764e82eSJohannes Thumshirn if (!name)
4443764e82eSJohannes Thumshirn name = dev->dev.driver->name;
4453764e82eSJohannes Thumshirn
4463764e82eSJohannes Thumshirn size = resource_size(&dev->mem);
4473764e82eSJohannes Thumshirn
4483764e82eSJohannes Thumshirn mem = request_mem_region(dev->mem.start, size, name);
4493764e82eSJohannes Thumshirn if (!mem)
4503764e82eSJohannes Thumshirn return ERR_PTR(-EBUSY);
4513764e82eSJohannes Thumshirn
4523764e82eSJohannes Thumshirn return mem;
4533764e82eSJohannes Thumshirn }
454891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_request_mem, MCB);
4553764e82eSJohannes Thumshirn
4563764e82eSJohannes Thumshirn /**
4573764e82eSJohannes Thumshirn * mcb_release_mem() - Release memory requested by device
4583764e82eSJohannes Thumshirn * @dev: The @mcb_device that requested the memory
4593764e82eSJohannes Thumshirn *
4603764e82eSJohannes Thumshirn * Release memory that was prior requested via @mcb_request_mem().
4613764e82eSJohannes Thumshirn */
mcb_release_mem(struct resource * mem)4623764e82eSJohannes Thumshirn void mcb_release_mem(struct resource *mem)
4633764e82eSJohannes Thumshirn {
4643764e82eSJohannes Thumshirn u32 size;
4653764e82eSJohannes Thumshirn
4663764e82eSJohannes Thumshirn size = resource_size(mem);
4673764e82eSJohannes Thumshirn release_mem_region(mem->start, size);
4683764e82eSJohannes Thumshirn }
469891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_release_mem, MCB);
4703764e82eSJohannes Thumshirn
__mcb_get_irq(struct mcb_device * dev)4714ec65b77SJohannes Thumshirn static int __mcb_get_irq(struct mcb_device *dev)
4724ec65b77SJohannes Thumshirn {
4732ce80087SJohannes Thumshirn struct resource *irq;
4742ce80087SJohannes Thumshirn
4752ce80087SJohannes Thumshirn irq = mcb_get_resource(dev, IORESOURCE_IRQ);
4764ec65b77SJohannes Thumshirn
4774ec65b77SJohannes Thumshirn return irq->start;
4784ec65b77SJohannes Thumshirn }
4794ec65b77SJohannes Thumshirn
4803764e82eSJohannes Thumshirn /**
4813764e82eSJohannes Thumshirn * mcb_get_irq() - Get device's IRQ number
4823764e82eSJohannes Thumshirn * @dev: The @mcb_device the IRQ is for
4833764e82eSJohannes Thumshirn *
4843764e82eSJohannes Thumshirn * Get the IRQ number of a given @mcb_device.
4853764e82eSJohannes Thumshirn */
mcb_get_irq(struct mcb_device * dev)4863764e82eSJohannes Thumshirn int mcb_get_irq(struct mcb_device *dev)
4873764e82eSJohannes Thumshirn {
4884ec65b77SJohannes Thumshirn struct mcb_bus *bus = dev->bus;
4893764e82eSJohannes Thumshirn
4904ec65b77SJohannes Thumshirn if (bus->get_irq)
4914ec65b77SJohannes Thumshirn return bus->get_irq(dev);
4924ec65b77SJohannes Thumshirn
4934ec65b77SJohannes Thumshirn return __mcb_get_irq(dev);
4943764e82eSJohannes Thumshirn }
495891e6036SJohannes Thumshirn EXPORT_SYMBOL_NS_GPL(mcb_get_irq, MCB);
4963764e82eSJohannes Thumshirn
mcb_init(void)4973764e82eSJohannes Thumshirn static int mcb_init(void)
4983764e82eSJohannes Thumshirn {
4993764e82eSJohannes Thumshirn return bus_register(&mcb_bus_type);
5003764e82eSJohannes Thumshirn }
5013764e82eSJohannes Thumshirn
mcb_exit(void)5023764e82eSJohannes Thumshirn static void mcb_exit(void)
5033764e82eSJohannes Thumshirn {
504169883a6SJohannes Thumshirn ida_destroy(&mcb_ida);
5053764e82eSJohannes Thumshirn bus_unregister(&mcb_bus_type);
5063764e82eSJohannes Thumshirn }
5073764e82eSJohannes Thumshirn
5083764e82eSJohannes Thumshirn /* mcb must be initialized after PCI but before the chameleon drivers.
5093764e82eSJohannes Thumshirn * That means we must use some initcall between subsys_initcall and
5103764e82eSJohannes Thumshirn * device_initcall.
5113764e82eSJohannes Thumshirn */
5123764e82eSJohannes Thumshirn fs_initcall(mcb_init);
5133764e82eSJohannes Thumshirn module_exit(mcb_exit);
5143764e82eSJohannes Thumshirn
5153764e82eSJohannes Thumshirn MODULE_DESCRIPTION("MEN Chameleon Bus Driver");
5163764e82eSJohannes Thumshirn MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
5173764e82eSJohannes Thumshirn MODULE_LICENSE("GPL v2");
518