Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * MEN Chameleon Bus.
  3 *
  4 * Copyright (C) 2013 MEN Mikroelektronik GmbH (www.men.de)
  5 * Author: Johannes Thumshirn <johannes.thumshirn@men.de>
  6 *
  7 * This program is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU General Public License as published by the Free
  9 * Software Foundation; version 2 of the License.
 10 */
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <linux/slab.h>
 14#include <linux/types.h>
 15#include <linux/idr.h>
 16#include <linux/mcb.h>
 17
 18static DEFINE_IDA(mcb_ida);
 19
 20static const struct mcb_device_id *mcb_match_id(const struct mcb_device_id *ids,
 21						struct mcb_device *dev)
 22{
 23	if (ids) {
 24		while (ids->device) {
 25			if (ids->device == dev->id)
 26				return ids;
 27			ids++;
 28		}
 29	}
 30
 31	return NULL;
 32}
 33
 34static int mcb_match(struct device *dev, struct device_driver *drv)
 35{
 36	struct mcb_driver *mdrv = to_mcb_driver(drv);
 37	struct mcb_device *mdev = to_mcb_device(dev);
 38	const struct mcb_device_id *found_id;
 39
 40	found_id = mcb_match_id(mdrv->id_table, mdev);
 41	if (found_id)
 42		return 1;
 43
 44	return 0;
 45}
 46
 47static int mcb_uevent(struct device *dev, struct kobj_uevent_env *env)
 48{
 49	struct mcb_device *mdev = to_mcb_device(dev);
 50	int ret;
 51
 52	ret = add_uevent_var(env, "MODALIAS=mcb:16z%03d", mdev->id);
 53	if (ret)
 54		return -ENOMEM;
 55
 56	return 0;
 57}
 58
 59static int mcb_probe(struct device *dev)
 60{
 61	struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
 62	struct mcb_device *mdev = to_mcb_device(dev);
 63	const struct mcb_device_id *found_id;
 64	struct module *carrier_mod;
 65	int ret;
 66
 67	found_id = mcb_match_id(mdrv->id_table, mdev);
 68	if (!found_id)
 69		return -ENODEV;
 70
 71	carrier_mod = mdev->dev.parent->driver->owner;
 72	if (!try_module_get(carrier_mod))
 73		return -EINVAL;
 74
 75	get_device(dev);
 76	ret = mdrv->probe(mdev, found_id);
 77	if (ret)
 78		module_put(carrier_mod);
 79
 80	return ret;
 81}
 82
 83static int mcb_remove(struct device *dev)
 84{
 85	struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
 86	struct mcb_device *mdev = to_mcb_device(dev);
 87	struct module *carrier_mod;
 88
 89	mdrv->remove(mdev);
 90
 91	carrier_mod = mdev->dev.parent->driver->owner;
 92	module_put(carrier_mod);
 93
 94	put_device(&mdev->dev);
 95
 96	return 0;
 97}
 98
 99static void mcb_shutdown(struct device *dev)
100{
101	struct mcb_driver *mdrv = to_mcb_driver(dev->driver);
102	struct mcb_device *mdev = to_mcb_device(dev);
103
104	if (mdrv && mdrv->shutdown)
105		mdrv->shutdown(mdev);
106}
107
108static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
109			 char *buf)
110{
111	struct mcb_bus *bus = to_mcb_bus(dev);
112
113	return scnprintf(buf, PAGE_SIZE, "%d\n", bus->revision);
114}
115static DEVICE_ATTR_RO(revision);
116
117static ssize_t model_show(struct device *dev, struct device_attribute *attr,
118			 char *buf)
119{
120	struct mcb_bus *bus = to_mcb_bus(dev);
121
122	return scnprintf(buf, PAGE_SIZE, "%c\n", bus->model);
123}
124static DEVICE_ATTR_RO(model);
125
126static ssize_t minor_show(struct device *dev, struct device_attribute *attr,
127			 char *buf)
128{
129	struct mcb_bus *bus = to_mcb_bus(dev);
130
131	return scnprintf(buf, PAGE_SIZE, "%d\n", bus->minor);
132}
133static DEVICE_ATTR_RO(minor);
134
135static ssize_t name_show(struct device *dev, struct device_attribute *attr,
136			 char *buf)
137{
138	struct mcb_bus *bus = to_mcb_bus(dev);
139
140	return scnprintf(buf, PAGE_SIZE, "%s\n", bus->name);
141}
142static DEVICE_ATTR_RO(name);
143
144static struct attribute *mcb_bus_attrs[] = {
145	&dev_attr_revision.attr,
146	&dev_attr_model.attr,
147	&dev_attr_minor.attr,
148	&dev_attr_name.attr,
149	NULL,
150};
151
152static const struct attribute_group mcb_carrier_group = {
153	.attrs = mcb_bus_attrs,
154};
155
156static const struct attribute_group *mcb_carrier_groups[] = {
157	&mcb_carrier_group,
158	NULL,
159};
160
161
162static struct bus_type mcb_bus_type = {
163	.name = "mcb",
164	.match = mcb_match,
165	.uevent = mcb_uevent,
166	.probe = mcb_probe,
167	.remove = mcb_remove,
168	.shutdown = mcb_shutdown,
169};
170
171static struct device_type mcb_carrier_device_type = {
172	.name = "mcb-carrier",
173	.groups = mcb_carrier_groups,
174};
175
176/**
177 * __mcb_register_driver() - Register a @mcb_driver at the system
178 * @drv: The @mcb_driver
179 * @owner: The @mcb_driver's module
180 * @mod_name: The name of the @mcb_driver's module
181 *
182 * Register a @mcb_driver at the system. Perform some sanity checks, if
183 * the .probe and .remove methods are provided by the driver.
184 */
185int __mcb_register_driver(struct mcb_driver *drv, struct module *owner,
186			const char *mod_name)
187{
188	if (!drv->probe || !drv->remove)
189		return -EINVAL;
190
191	drv->driver.owner = owner;
192	drv->driver.bus = &mcb_bus_type;
193	drv->driver.mod_name = mod_name;
194
195	return driver_register(&drv->driver);
196}
197EXPORT_SYMBOL_GPL(__mcb_register_driver);
198
199/**
200 * mcb_unregister_driver() - Unregister a @mcb_driver from the system
201 * @drv: The @mcb_driver
202 *
203 * Unregister a @mcb_driver from the system.
204 */
205void mcb_unregister_driver(struct mcb_driver *drv)
206{
207	driver_unregister(&drv->driver);
208}
209EXPORT_SYMBOL_GPL(mcb_unregister_driver);
210
211static void mcb_release_dev(struct device *dev)
212{
213	struct mcb_device *mdev = to_mcb_device(dev);
214
215	mcb_bus_put(mdev->bus);
216	kfree(mdev);
217}
218
219/**
220 * mcb_device_register() - Register a mcb_device
221 * @bus: The @mcb_bus of the device
222 * @dev: The @mcb_device
223 *
224 * Register a specific @mcb_device at a @mcb_bus and the system itself.
225 */
226int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
227{
228	int ret;
229	int device_id;
230
231	device_initialize(&dev->dev);
232	mcb_bus_get(bus);
233	dev->dev.bus = &mcb_bus_type;
234	dev->dev.parent = bus->dev.parent;
235	dev->dev.release = mcb_release_dev;
236	dev->dma_dev = bus->carrier;
237
238	device_id = dev->id;
239	dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d",
240		bus->bus_nr, device_id, dev->inst, dev->group, dev->var);
241
242	ret = device_add(&dev->dev);
243	if (ret < 0) {
244		pr_err("Failed registering device 16z%03d on bus mcb%d (%d)\n",
245			device_id, bus->bus_nr, ret);
246		goto out;
247	}
248
249	return 0;
250
251out:
252
253	return ret;
254}
255EXPORT_SYMBOL_GPL(mcb_device_register);
256
257static void mcb_free_bus(struct device *dev)
258{
259	struct mcb_bus *bus = to_mcb_bus(dev);
260
261	put_device(bus->carrier);
262	ida_simple_remove(&mcb_ida, bus->bus_nr);
263	kfree(bus);
264}
265
266/**
267 * mcb_alloc_bus() - Allocate a new @mcb_bus
268 *
269 * Allocate a new @mcb_bus.
270 */
271struct mcb_bus *mcb_alloc_bus(struct device *carrier)
272{
273	struct mcb_bus *bus;
274	int bus_nr;
275	int rc;
276
277	bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
278	if (!bus)
279		return ERR_PTR(-ENOMEM);
280
281	bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
282	if (bus_nr < 0) {
283		rc = bus_nr;
284		goto err_free;
285	}
286
287	bus->bus_nr = bus_nr;
288	bus->carrier = get_device(carrier);
289
290	device_initialize(&bus->dev);
291	bus->dev.parent = carrier;
292	bus->dev.bus = &mcb_bus_type;
293	bus->dev.type = &mcb_carrier_device_type;
294	bus->dev.release = &mcb_free_bus;
295
296	dev_set_name(&bus->dev, "mcb:%d", bus_nr);
297	rc = device_add(&bus->dev);
298	if (rc)
299		goto err_free;
300
301	return bus;
302err_free:
303	put_device(carrier);
304	kfree(bus);
305	return ERR_PTR(rc);
306}
307EXPORT_SYMBOL_GPL(mcb_alloc_bus);
308
309static int __mcb_devices_unregister(struct device *dev, void *data)
310{
311	device_unregister(dev);
312	return 0;
313}
314
315static void mcb_devices_unregister(struct mcb_bus *bus)
316{
317	bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_devices_unregister);
318}
319/**
320 * mcb_release_bus() - Free a @mcb_bus
321 * @bus: The @mcb_bus to release
322 *
323 * Release an allocated @mcb_bus from the system.
324 */
325void mcb_release_bus(struct mcb_bus *bus)
326{
327	mcb_devices_unregister(bus);
328}
329EXPORT_SYMBOL_GPL(mcb_release_bus);
330
331/**
332 * mcb_bus_put() - Increment refcnt
333 * @bus: The @mcb_bus
334 *
335 * Get a @mcb_bus' ref
336 */
337struct mcb_bus *mcb_bus_get(struct mcb_bus *bus)
338{
339	if (bus)
340		get_device(&bus->dev);
341
342	return bus;
343}
344EXPORT_SYMBOL_GPL(mcb_bus_get);
345
346/**
347 * mcb_bus_put() - Decrement refcnt
348 * @bus: The @mcb_bus
349 *
350 * Release a @mcb_bus' ref
351 */
352void mcb_bus_put(struct mcb_bus *bus)
353{
354	if (bus)
355		put_device(&bus->dev);
356}
357EXPORT_SYMBOL_GPL(mcb_bus_put);
358
359/**
360 * mcb_alloc_dev() - Allocate a device
361 * @bus: The @mcb_bus the device is part of
362 *
363 * Allocate a @mcb_device and add bus.
364 */
365struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
366{
367	struct mcb_device *dev;
368
369	dev = kzalloc(sizeof(struct mcb_device), GFP_KERNEL);
370	if (!dev)
371		return NULL;
372
373	dev->bus = bus;
374
375	return dev;
376}
377EXPORT_SYMBOL_GPL(mcb_alloc_dev);
378
379/**
380 * mcb_free_dev() - Free @mcb_device
381 * @dev: The device to free
382 *
383 * Free a @mcb_device
384 */
385void mcb_free_dev(struct mcb_device *dev)
386{
387	kfree(dev);
388}
389EXPORT_SYMBOL_GPL(mcb_free_dev);
390
391static int __mcb_bus_add_devices(struct device *dev, void *data)
392{
393	struct mcb_device *mdev = to_mcb_device(dev);
394	int retval;
395
396	if (mdev->is_added)
397		return 0;
398
399	retval = device_attach(dev);
400	if (retval < 0)
401		dev_err(dev, "Error adding device (%d)\n", retval);
402
403	mdev->is_added = true;
404
405	return 0;
406}
407
408/**
409 * mcb_bus_add_devices() - Add devices in the bus' internal device list
410 * @bus: The @mcb_bus we add the devices
411 *
412 * Add devices in the bus' internal device list to the system.
413 */
414void mcb_bus_add_devices(const struct mcb_bus *bus)
415{
416	bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
417}
418EXPORT_SYMBOL_GPL(mcb_bus_add_devices);
419
420/**
421 * mcb_request_mem() - Request memory
422 * @dev: The @mcb_device the memory is for
423 * @name: The name for the memory reference.
424 *
425 * Request memory for a @mcb_device. If @name is NULL the driver name will
426 * be used.
427 */
428struct resource *mcb_request_mem(struct mcb_device *dev, const char *name)
429{
430	struct resource *mem;
431	u32 size;
432
433	if (!name)
434		name = dev->dev.driver->name;
435
436	size = resource_size(&dev->mem);
437
438	mem = request_mem_region(dev->mem.start, size, name);
439	if (!mem)
440		return ERR_PTR(-EBUSY);
441
442	return mem;
443}
444EXPORT_SYMBOL_GPL(mcb_request_mem);
445
446/**
447 * mcb_release_mem() - Release memory requested by device
448 * @dev: The @mcb_device that requested the memory
449 *
450 * Release memory that was prior requested via @mcb_request_mem().
451 */
452void mcb_release_mem(struct resource *mem)
453{
454	u32 size;
455
456	size = resource_size(mem);
457	release_mem_region(mem->start, size);
458}
459EXPORT_SYMBOL_GPL(mcb_release_mem);
460
461static int __mcb_get_irq(struct mcb_device *dev)
462{
463	struct resource *irq = &dev->irq;
464
465	return irq->start;
466}
467
468/**
469 * mcb_get_irq() - Get device's IRQ number
470 * @dev: The @mcb_device the IRQ is for
471 *
472 * Get the IRQ number of a given @mcb_device.
473 */
474int mcb_get_irq(struct mcb_device *dev)
475{
476	struct mcb_bus *bus = dev->bus;
477
478	if (bus->get_irq)
479		return bus->get_irq(dev);
480
481	return __mcb_get_irq(dev);
482}
483EXPORT_SYMBOL_GPL(mcb_get_irq);
484
485static int mcb_init(void)
486{
487	return bus_register(&mcb_bus_type);
488}
489
490static void mcb_exit(void)
491{
492	ida_destroy(&mcb_ida);
493	bus_unregister(&mcb_bus_type);
494}
495
496/* mcb must be initialized after PCI but before the chameleon drivers.
497 * That means we must use some initcall between subsys_initcall and
498 * device_initcall.
499 */
500fs_initcall(mcb_init);
501module_exit(mcb_exit);
502
503MODULE_DESCRIPTION("MEN Chameleon Bus Driver");
504MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
505MODULE_LICENSE("GPL v2");