18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * MEN Chameleon Bus.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 MEN Mikroelektronik GmbH (www.men.de)
68c2ecf20Sopenharmony_ci * Author: Johannes Thumshirn <johannes.thumshirn@men.de>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/idr.h>
138c2ecf20Sopenharmony_ci#include <linux/mcb.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic DEFINE_IDA(mcb_ida);
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic const struct mcb_device_id *mcb_match_id(const struct mcb_device_id *ids,
188c2ecf20Sopenharmony_ci						struct mcb_device *dev)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	if (ids) {
218c2ecf20Sopenharmony_ci		while (ids->device) {
228c2ecf20Sopenharmony_ci			if (ids->device == dev->id)
238c2ecf20Sopenharmony_ci				return ids;
248c2ecf20Sopenharmony_ci			ids++;
258c2ecf20Sopenharmony_ci		}
268c2ecf20Sopenharmony_ci	}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	return NULL;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int mcb_match(struct device *dev, struct device_driver *drv)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct mcb_driver *mdrv = to_mcb_driver(drv);
348c2ecf20Sopenharmony_ci	struct mcb_device *mdev = to_mcb_device(dev);
358c2ecf20Sopenharmony_ci	const struct mcb_device_id *found_id;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	found_id = mcb_match_id(mdrv->id_table, mdev);
388c2ecf20Sopenharmony_ci	if (found_id)
398c2ecf20Sopenharmony_ci		return 1;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int mcb_uevent(struct device *dev, struct kobj_uevent_env *env)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct mcb_device *mdev = to_mcb_device(dev);
478c2ecf20Sopenharmony_ci	int ret;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	ret = add_uevent_var(env, "MODALIAS=mcb:16z%03d", mdev->id);
508c2ecf20Sopenharmony_ci	if (ret)
518c2ecf20Sopenharmony_ci		return -ENOMEM;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return 0;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int mcb_probe(struct device *dev)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
598c2ecf20Sopenharmony_ci	struct mcb_device *mdev = to_mcb_device(dev);
608c2ecf20Sopenharmony_ci	const struct mcb_device_id *found_id;
618c2ecf20Sopenharmony_ci	struct module *carrier_mod;
628c2ecf20Sopenharmony_ci	int ret;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	found_id = mcb_match_id(mdrv->id_table, mdev);
658c2ecf20Sopenharmony_ci	if (!found_id)
668c2ecf20Sopenharmony_ci		return -ENODEV;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	carrier_mod = mdev->dev.parent->driver->owner;
698c2ecf20Sopenharmony_ci	if (!try_module_get(carrier_mod))
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	get_device(dev);
738c2ecf20Sopenharmony_ci	ret = mdrv->probe(mdev, found_id);
748c2ecf20Sopenharmony_ci	if (ret) {
758c2ecf20Sopenharmony_ci		module_put(carrier_mod);
768c2ecf20Sopenharmony_ci		put_device(dev);
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return ret;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int mcb_remove(struct device *dev)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
858c2ecf20Sopenharmony_ci	struct mcb_device *mdev = to_mcb_device(dev);
868c2ecf20Sopenharmony_ci	struct module *carrier_mod;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	mdrv->remove(mdev);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	carrier_mod = mdev->dev.parent->driver->owner;
918c2ecf20Sopenharmony_ci	module_put(carrier_mod);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	put_device(&mdev->dev);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void mcb_shutdown(struct device *dev)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
1018c2ecf20Sopenharmony_ci	struct mcb_device *mdev = to_mcb_device(dev);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (mdrv && mdrv->shutdown)
1048c2ecf20Sopenharmony_ci		mdrv->shutdown(mdev);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic ssize_t revision_show(struct device *dev, struct device_attribute *attr,
1088c2ecf20Sopenharmony_ci			 char *buf)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct mcb_bus *bus = to_mcb_bus(dev);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", bus->revision);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(revision);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic ssize_t model_show(struct device *dev, struct device_attribute *attr,
1178c2ecf20Sopenharmony_ci			 char *buf)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct mcb_bus *bus = to_mcb_bus(dev);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%c\n", bus->model);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(model);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic ssize_t minor_show(struct device *dev, struct device_attribute *attr,
1268c2ecf20Sopenharmony_ci			 char *buf)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct mcb_bus *bus = to_mcb_bus(dev);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", bus->minor);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(minor);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr,
1358c2ecf20Sopenharmony_ci			 char *buf)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct mcb_bus *bus = to_mcb_bus(dev);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%s\n", bus->name);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct attribute *mcb_bus_attrs[] = {
1448c2ecf20Sopenharmony_ci	&dev_attr_revision.attr,
1458c2ecf20Sopenharmony_ci	&dev_attr_model.attr,
1468c2ecf20Sopenharmony_ci	&dev_attr_minor.attr,
1478c2ecf20Sopenharmony_ci	&dev_attr_name.attr,
1488c2ecf20Sopenharmony_ci	NULL,
1498c2ecf20Sopenharmony_ci};
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic const struct attribute_group mcb_carrier_group = {
1528c2ecf20Sopenharmony_ci	.attrs = mcb_bus_attrs,
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic const struct attribute_group *mcb_carrier_groups[] = {
1568c2ecf20Sopenharmony_ci	&mcb_carrier_group,
1578c2ecf20Sopenharmony_ci	NULL,
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic struct bus_type mcb_bus_type = {
1628c2ecf20Sopenharmony_ci	.name = "mcb",
1638c2ecf20Sopenharmony_ci	.match = mcb_match,
1648c2ecf20Sopenharmony_ci	.uevent = mcb_uevent,
1658c2ecf20Sopenharmony_ci	.probe = mcb_probe,
1668c2ecf20Sopenharmony_ci	.remove = mcb_remove,
1678c2ecf20Sopenharmony_ci	.shutdown = mcb_shutdown,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic struct device_type mcb_carrier_device_type = {
1718c2ecf20Sopenharmony_ci	.name = "mcb-carrier",
1728c2ecf20Sopenharmony_ci	.groups = mcb_carrier_groups,
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/**
1768c2ecf20Sopenharmony_ci * __mcb_register_driver() - Register a @mcb_driver at the system
1778c2ecf20Sopenharmony_ci * @drv: The @mcb_driver
1788c2ecf20Sopenharmony_ci * @owner: The @mcb_driver's module
1798c2ecf20Sopenharmony_ci * @mod_name: The name of the @mcb_driver's module
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * Register a @mcb_driver at the system. Perform some sanity checks, if
1828c2ecf20Sopenharmony_ci * the .probe and .remove methods are provided by the driver.
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_ciint __mcb_register_driver(struct mcb_driver *drv, struct module *owner,
1858c2ecf20Sopenharmony_ci			const char *mod_name)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	if (!drv->probe || !drv->remove)
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	drv->driver.owner = owner;
1918c2ecf20Sopenharmony_ci	drv->driver.bus = &mcb_bus_type;
1928c2ecf20Sopenharmony_ci	drv->driver.mod_name = mod_name;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return driver_register(&drv->driver);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(__mcb_register_driver, MCB);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/**
1998c2ecf20Sopenharmony_ci * mcb_unregister_driver() - Unregister a @mcb_driver from the system
2008c2ecf20Sopenharmony_ci * @drv: The @mcb_driver
2018c2ecf20Sopenharmony_ci *
2028c2ecf20Sopenharmony_ci * Unregister a @mcb_driver from the system.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_civoid mcb_unregister_driver(struct mcb_driver *drv)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	driver_unregister(&drv->driver);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_unregister_driver, MCB);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic void mcb_release_dev(struct device *dev)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct mcb_device *mdev = to_mcb_device(dev);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	mcb_bus_put(mdev->bus);
2158c2ecf20Sopenharmony_ci	kfree(mdev);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/**
2198c2ecf20Sopenharmony_ci * mcb_device_register() - Register a mcb_device
2208c2ecf20Sopenharmony_ci * @bus: The @mcb_bus of the device
2218c2ecf20Sopenharmony_ci * @dev: The @mcb_device
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * Register a specific @mcb_device at a @mcb_bus and the system itself.
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_ciint mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int ret;
2288c2ecf20Sopenharmony_ci	int device_id;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	device_initialize(&dev->dev);
2318c2ecf20Sopenharmony_ci	mcb_bus_get(bus);
2328c2ecf20Sopenharmony_ci	dev->dev.bus = &mcb_bus_type;
2338c2ecf20Sopenharmony_ci	dev->dev.parent = bus->dev.parent;
2348c2ecf20Sopenharmony_ci	dev->dev.release = mcb_release_dev;
2358c2ecf20Sopenharmony_ci	dev->dma_dev = bus->carrier;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	device_id = dev->id;
2388c2ecf20Sopenharmony_ci	dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d",
2398c2ecf20Sopenharmony_ci		bus->bus_nr, device_id, dev->inst, dev->group, dev->var);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ret = device_add(&dev->dev);
2428c2ecf20Sopenharmony_ci	if (ret < 0) {
2438c2ecf20Sopenharmony_ci		pr_err("Failed registering device 16z%03d on bus mcb%d (%d)\n",
2448c2ecf20Sopenharmony_ci			device_id, bus->bus_nr, ret);
2458c2ecf20Sopenharmony_ci		goto out;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ciout:
2518c2ecf20Sopenharmony_ci	put_device(&dev->dev);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return ret;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_device_register, MCB);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void mcb_free_bus(struct device *dev)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct mcb_bus *bus = to_mcb_bus(dev);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	put_device(bus->carrier);
2628c2ecf20Sopenharmony_ci	ida_simple_remove(&mcb_ida, bus->bus_nr);
2638c2ecf20Sopenharmony_ci	kfree(bus);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/**
2678c2ecf20Sopenharmony_ci * mcb_alloc_bus() - Allocate a new @mcb_bus
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * Allocate a new @mcb_bus.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_cistruct mcb_bus *mcb_alloc_bus(struct device *carrier)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct mcb_bus *bus;
2748c2ecf20Sopenharmony_ci	int bus_nr;
2758c2ecf20Sopenharmony_ci	int rc;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
2788c2ecf20Sopenharmony_ci	if (!bus)
2798c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
2828c2ecf20Sopenharmony_ci	if (bus_nr < 0) {
2838c2ecf20Sopenharmony_ci		kfree(bus);
2848c2ecf20Sopenharmony_ci		return ERR_PTR(bus_nr);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	bus->bus_nr = bus_nr;
2888c2ecf20Sopenharmony_ci	bus->carrier = get_device(carrier);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	device_initialize(&bus->dev);
2918c2ecf20Sopenharmony_ci	bus->dev.parent = carrier;
2928c2ecf20Sopenharmony_ci	bus->dev.bus = &mcb_bus_type;
2938c2ecf20Sopenharmony_ci	bus->dev.type = &mcb_carrier_device_type;
2948c2ecf20Sopenharmony_ci	bus->dev.release = &mcb_free_bus;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	dev_set_name(&bus->dev, "mcb:%d", bus_nr);
2978c2ecf20Sopenharmony_ci	rc = device_add(&bus->dev);
2988c2ecf20Sopenharmony_ci	if (rc)
2998c2ecf20Sopenharmony_ci		goto err_put;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return bus;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cierr_put:
3048c2ecf20Sopenharmony_ci	put_device(&bus->dev);
3058c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_alloc_bus, MCB);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int __mcb_devices_unregister(struct device *dev, void *data)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	device_unregister(dev);
3128c2ecf20Sopenharmony_ci	return 0;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic void mcb_devices_unregister(struct mcb_bus *bus)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_devices_unregister);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci/**
3208c2ecf20Sopenharmony_ci * mcb_release_bus() - Free a @mcb_bus
3218c2ecf20Sopenharmony_ci * @bus: The @mcb_bus to release
3228c2ecf20Sopenharmony_ci *
3238c2ecf20Sopenharmony_ci * Release an allocated @mcb_bus from the system.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_civoid mcb_release_bus(struct mcb_bus *bus)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	mcb_devices_unregister(bus);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_release_bus, MCB);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/**
3328c2ecf20Sopenharmony_ci * mcb_bus_put() - Increment refcnt
3338c2ecf20Sopenharmony_ci * @bus: The @mcb_bus
3348c2ecf20Sopenharmony_ci *
3358c2ecf20Sopenharmony_ci * Get a @mcb_bus' ref
3368c2ecf20Sopenharmony_ci */
3378c2ecf20Sopenharmony_cistruct mcb_bus *mcb_bus_get(struct mcb_bus *bus)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	if (bus)
3408c2ecf20Sopenharmony_ci		get_device(&bus->dev);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return bus;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_bus_get, MCB);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/**
3478c2ecf20Sopenharmony_ci * mcb_bus_put() - Decrement refcnt
3488c2ecf20Sopenharmony_ci * @bus: The @mcb_bus
3498c2ecf20Sopenharmony_ci *
3508c2ecf20Sopenharmony_ci * Release a @mcb_bus' ref
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_civoid mcb_bus_put(struct mcb_bus *bus)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	if (bus)
3558c2ecf20Sopenharmony_ci		put_device(&bus->dev);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_bus_put, MCB);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/**
3608c2ecf20Sopenharmony_ci * mcb_alloc_dev() - Allocate a device
3618c2ecf20Sopenharmony_ci * @bus: The @mcb_bus the device is part of
3628c2ecf20Sopenharmony_ci *
3638c2ecf20Sopenharmony_ci * Allocate a @mcb_device and add bus.
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_cistruct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct mcb_device *dev;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(struct mcb_device), GFP_KERNEL);
3708c2ecf20Sopenharmony_ci	if (!dev)
3718c2ecf20Sopenharmony_ci		return NULL;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	dev->bus = bus;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return dev;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_alloc_dev, MCB);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/**
3808c2ecf20Sopenharmony_ci * mcb_free_dev() - Free @mcb_device
3818c2ecf20Sopenharmony_ci * @dev: The device to free
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci * Free a @mcb_device
3848c2ecf20Sopenharmony_ci */
3858c2ecf20Sopenharmony_civoid mcb_free_dev(struct mcb_device *dev)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	kfree(dev);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_free_dev, MCB);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int __mcb_bus_add_devices(struct device *dev, void *data)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	int retval;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	retval = device_attach(dev);
3968c2ecf20Sopenharmony_ci	if (retval < 0) {
3978c2ecf20Sopenharmony_ci		dev_err(dev, "Error adding device (%d)\n", retval);
3988c2ecf20Sopenharmony_ci		return retval;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/**
4058c2ecf20Sopenharmony_ci * mcb_bus_add_devices() - Add devices in the bus' internal device list
4068c2ecf20Sopenharmony_ci * @bus: The @mcb_bus we add the devices
4078c2ecf20Sopenharmony_ci *
4088c2ecf20Sopenharmony_ci * Add devices in the bus' internal device list to the system.
4098c2ecf20Sopenharmony_ci */
4108c2ecf20Sopenharmony_civoid mcb_bus_add_devices(const struct mcb_bus *bus)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_bus_add_devices, MCB);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/**
4178c2ecf20Sopenharmony_ci * mcb_get_resource() - get a resource for a mcb device
4188c2ecf20Sopenharmony_ci * @dev: the mcb device
4198c2ecf20Sopenharmony_ci * @type: the type of resource
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_cistruct resource *mcb_get_resource(struct mcb_device *dev, unsigned int type)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	if (type == IORESOURCE_MEM)
4248c2ecf20Sopenharmony_ci		return &dev->mem;
4258c2ecf20Sopenharmony_ci	else if (type == IORESOURCE_IRQ)
4268c2ecf20Sopenharmony_ci		return &dev->irq;
4278c2ecf20Sopenharmony_ci	else
4288c2ecf20Sopenharmony_ci		return NULL;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_get_resource, MCB);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/**
4338c2ecf20Sopenharmony_ci * mcb_request_mem() - Request memory
4348c2ecf20Sopenharmony_ci * @dev: The @mcb_device the memory is for
4358c2ecf20Sopenharmony_ci * @name: The name for the memory reference.
4368c2ecf20Sopenharmony_ci *
4378c2ecf20Sopenharmony_ci * Request memory for a @mcb_device. If @name is NULL the driver name will
4388c2ecf20Sopenharmony_ci * be used.
4398c2ecf20Sopenharmony_ci */
4408c2ecf20Sopenharmony_cistruct resource *mcb_request_mem(struct mcb_device *dev, const char *name)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct resource *mem;
4438c2ecf20Sopenharmony_ci	u32 size;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (!name)
4468c2ecf20Sopenharmony_ci		name = dev->dev.driver->name;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	size = resource_size(&dev->mem);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	mem = request_mem_region(dev->mem.start, size, name);
4518c2ecf20Sopenharmony_ci	if (!mem)
4528c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return mem;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_request_mem, MCB);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/**
4598c2ecf20Sopenharmony_ci * mcb_release_mem() - Release memory requested by device
4608c2ecf20Sopenharmony_ci * @dev: The @mcb_device that requested the memory
4618c2ecf20Sopenharmony_ci *
4628c2ecf20Sopenharmony_ci * Release memory that was prior requested via @mcb_request_mem().
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_civoid mcb_release_mem(struct resource *mem)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	u32 size;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	size = resource_size(mem);
4698c2ecf20Sopenharmony_ci	release_mem_region(mem->start, size);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_release_mem, MCB);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic int __mcb_get_irq(struct mcb_device *dev)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct resource *irq;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	irq = mcb_get_resource(dev, IORESOURCE_IRQ);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	return irq->start;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci/**
4838c2ecf20Sopenharmony_ci * mcb_get_irq() - Get device's IRQ number
4848c2ecf20Sopenharmony_ci * @dev: The @mcb_device the IRQ is for
4858c2ecf20Sopenharmony_ci *
4868c2ecf20Sopenharmony_ci * Get the IRQ number of a given @mcb_device.
4878c2ecf20Sopenharmony_ci */
4888c2ecf20Sopenharmony_ciint mcb_get_irq(struct mcb_device *dev)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct mcb_bus *bus = dev->bus;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (bus->get_irq)
4938c2ecf20Sopenharmony_ci		return bus->get_irq(dev);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return __mcb_get_irq(dev);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(mcb_get_irq, MCB);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic int mcb_init(void)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	return bus_register(&mcb_bus_type);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void mcb_exit(void)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	ida_destroy(&mcb_ida);
5078c2ecf20Sopenharmony_ci	bus_unregister(&mcb_bus_type);
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/* mcb must be initialized after PCI but before the chameleon drivers.
5118c2ecf20Sopenharmony_ci * That means we must use some initcall between subsys_initcall and
5128c2ecf20Sopenharmony_ci * device_initcall.
5138c2ecf20Sopenharmony_ci */
5148c2ecf20Sopenharmony_cifs_initcall(mcb_init);
5158c2ecf20Sopenharmony_cimodule_exit(mcb_exit);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MEN Chameleon Bus Driver");
5188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
5198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
520