| /* |
| * hsi_driver_bus.c |
| * |
| * Implements an HSI bus, device and driver interface. |
| * |
| * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved. |
| * Copyright (C) 2009 Texas Instruments, Inc. |
| * |
| * Author: Carlos Chinea <carlos.chinea@nokia.com> |
| * Author: Sebastien JAN <s-jan@ti.com> |
| * |
| * This package is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #include <linux/device.h> |
| #include "hsi_driver.h" |
| |
| #define HSI_PREFIX "hsi:" |
| |
| struct bus_type hsi_bus_type; |
| |
| static ssize_t modalias_show(struct device *dev, struct device_attribute *a, |
| char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE + 1, "%s%s\n", HSI_PREFIX, |
| dev_name(dev)); |
| } |
| |
| static struct device_attribute hsi_dev_attrs[] = { |
| __ATTR_RO(modalias), |
| __ATTR_NULL, |
| }; |
| |
| static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) |
| { |
| add_uevent_var(env, "MODALIAS=%s%s", HSI_PREFIX, dev_name(dev)); |
| return 0; |
| } |
| |
| static int hsi_bus_match(struct device *device, struct device_driver *driver) |
| { |
| struct hsi_device *dev = to_hsi_device(device); |
| struct hsi_device_driver *drv = to_hsi_device_driver(driver); |
| |
| pr_debug("HSI DRIVER BUS : hsi_bus_match for ctrl:%d, port:%d, ch%d\n", |
| dev->n_ctrl, dev->n_p, dev->n_ch); |
| |
| if (!test_bit(dev->n_ctrl, &drv->ctrl_mask)) |
| return 0; |
| |
| if (!test_bit(dev->n_ch, &drv->ch_mask[dev->n_p])) |
| return 0; |
| |
| pr_info |
| ("HSI DRIVER BUS : hsi_bus_match SUCCESS : ctrl:%d (mask:%x)," |
| " port:%d, ch:%d (mask:%x)\n", |
| dev->n_ctrl, (u32) drv->ctrl_mask, dev->n_p, dev->n_ch, |
| (u32) drv->ch_mask[dev->n_p]); |
| |
| return 1; |
| } |
| |
| int hsi_bus_unreg_dev(struct device *device, void *p) |
| { |
| device->release(device); |
| device_unregister(device); |
| |
| return 0; |
| } |
| |
| int __init hsi_bus_init(void) |
| { |
| return bus_register(&hsi_bus_type); |
| } |
| |
| void hsi_bus_exit(void) |
| { |
| bus_for_each_dev(&hsi_bus_type, NULL, NULL, hsi_bus_unreg_dev); |
| bus_unregister(&hsi_bus_type); |
| } |
| |
| static int hsi_bus_probe(struct device *dev) |
| { |
| struct hsi_device_driver *drv; |
| int rc; |
| |
| pr_debug("HSI DRIVER BUS : hsi_bus_probe\n"); |
| |
| if (!dev->driver) |
| return 0; |
| |
| drv = to_hsi_device_driver(dev->driver); |
| |
| if (!drv->probe) |
| return -ENODEV; |
| |
| rc = drv->probe(to_hsi_device(dev)); |
| |
| return rc; |
| } |
| |
| static int hsi_bus_remove(struct device *dev) |
| { |
| struct hsi_device_driver *drv; |
| int ret; |
| |
| pr_debug("HSI DRIVER BUS : hsi_bus_remove\n"); |
| |
| if (!dev->driver) |
| return 0; |
| |
| drv = to_hsi_device_driver(dev->driver); |
| if (drv->remove) { |
| ret = drv->remove(to_hsi_device(dev)); |
| } else { |
| dev->driver = NULL; |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| static int hsi_bus_suspend(struct device *dev, pm_message_t mesg) |
| { |
| struct hsi_device_driver *drv; |
| |
| if (!dev->driver) |
| return 0; |
| |
| drv = to_hsi_device_driver(dev->driver); |
| if (!drv->suspend) |
| return 0; |
| |
| return drv->suspend(to_hsi_device(dev), mesg); |
| } |
| |
| static int hsi_bus_resume(struct device *dev) |
| { |
| struct hsi_device_driver *drv; |
| |
| if (!dev->driver) |
| return 0; |
| |
| drv = to_hsi_device_driver(dev->driver); |
| if (!drv->resume) |
| return 0; |
| |
| return drv->resume(to_hsi_device(dev)); |
| } |
| |
| struct bus_type hsi_bus_type = { |
| .name = "hsi", |
| .dev_attrs = hsi_dev_attrs, |
| .match = hsi_bus_match, |
| .uevent = hsi_bus_uevent, |
| .probe = hsi_bus_probe, |
| .remove = hsi_bus_remove, |
| .suspend = hsi_bus_suspend, |
| .resume = hsi_bus_resume, |
| }; |
| |
| /** |
| * hsi_register_driver - Register HSI device driver |
| * @driver - reference to the HSI device driver. |
| */ |
| int hsi_register_driver(struct hsi_device_driver *driver) |
| { |
| int ret = 0; |
| |
| if (driver == NULL) |
| return -EINVAL; |
| |
| driver->driver.bus = &hsi_bus_type; |
| |
| ret = driver_register(&driver->driver); |
| |
| if (ret == 0) |
| pr_debug("hsi: driver %s registered\n", driver->driver.name); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(hsi_register_driver); |
| |
| /** |
| * hsi_unregister_driver - Unregister HSI device driver |
| * @driver - reference to the HSI device driver. |
| */ |
| void hsi_unregister_driver(struct hsi_device_driver *driver) |
| { |
| if (driver == NULL) |
| return; |
| |
| driver_unregister(&driver->driver); |
| |
| pr_debug("hsi: driver %s unregistered\n", driver->driver.name); |
| } |
| EXPORT_SYMBOL(hsi_unregister_driver); |