summaryrefslogtreecommitdiff
path: root/drivers/mipi_bif
diff options
context:
space:
mode:
authorChaitanya Bandi <bandik@nvidia.com>2012-10-17 14:58:30 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:48:49 -0700
commita11f86eed184c84f0d09065c3ef5b751fa8ac919 (patch)
treea57ddeb93aa0c466cd32255a32c667d0343d4c60 /drivers/mipi_bif
parent6ccfde531faf507edf1d9d280efbe2fe8b0196f6 (diff)
mipi_bif: Add mipi_bif core driver
Bug 1022139 Change-Id: I8627ae0f3412b22136f27faf70018d8ebaf04172 Signed-off-by: Chaitanya Bandi <bandik@nvidia.com> Reviewed-on: http://git-master/r/174393 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Diffstat (limited to 'drivers/mipi_bif')
-rw-r--r--drivers/mipi_bif/Kconfig7
-rw-r--r--drivers/mipi_bif/Makefile3
-rw-r--r--drivers/mipi_bif/mipi-bif-core.c742
3 files changed, 752 insertions, 0 deletions
diff --git a/drivers/mipi_bif/Kconfig b/drivers/mipi_bif/Kconfig
new file mode 100644
index 000000000000..581e24f80389
--- /dev/null
+++ b/drivers/mipi_bif/Kconfig
@@ -0,0 +1,7 @@
+menuconfig MIPI_BIF
+ tristate "MIPI_BIF support"
+ depends on HAS_IOMEM
+ select RT_MUTEXES
+ ---help---
+ Say y here if you want MIPI_BIF support to be
+ enabled.
diff --git a/drivers/mipi_bif/Makefile b/drivers/mipi_bif/Makefile
new file mode 100644
index 000000000000..616fd2c4eb3f
--- /dev/null
+++ b/drivers/mipi_bif/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MIPI_BIF) += mipi-bif-core.o
+
+CFLAGS_mipi-bif-core.o := -Wno-deprecated-declarations
diff --git a/drivers/mipi_bif/mipi-bif-core.c b/drivers/mipi_bif/mipi-bif-core.c
new file mode 100644
index 000000000000..a7efb53b8716
--- /dev/null
+++ b/drivers/mipi_bif/mipi-bif-core.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/hardirq.h>
+#include <linux/irqflags.h>
+#include <linux/mipi-bif.h>
+
+static DEFINE_MUTEX(core_lock);
+static DEFINE_IDR(mipi_bif_adapter_idr);
+
+int __mipi_bif_first_dynamic_bus_num;
+
+#ifdef CONFIG_MIPI_BIF_COMPAT
+static struct class_compat *mipi_bif_adapter_compat_class;
+#endif
+
+static struct device_type mipi_bif_client_type;
+
+static ssize_t
+show_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", dev->type == &mipi_bif_client_type ?
+ to_mipi_bif_client(dev)->name : to_mipi_bif_adapter(dev)->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *mipi_bif_adapter_attrs[] = {
+ &dev_attr_name.attr,
+ NULL
+};
+
+static struct attribute_group mipi_bif_adapter_attr_group = {
+ .attrs = mipi_bif_adapter_attrs,
+};
+
+static const struct attribute_group *mipi_bif_adapter_attr_groups[] = {
+ &mipi_bif_adapter_attr_group,
+ NULL
+};
+
+static void mipi_bif_adapter_dev_release(struct device *dev)
+{
+ struct mipi_bif_adapter *adap = to_mipi_bif_adapter(dev);
+ complete(&adap->dev_released);
+}
+
+struct device_type mipi_bif_adapter_type = {
+ .groups = mipi_bif_adapter_attr_groups,
+ .release = mipi_bif_adapter_dev_release,
+};
+
+static struct attribute *mipi_bif_client_attrs[] = {
+ &dev_attr_name.attr,
+ NULL
+};
+
+static struct attribute_group mipi_bif_client_attr_group = {
+ .attrs = mipi_bif_client_attrs,
+};
+
+static const struct attribute_group *mipi_bif_client_attr_groups[] = {
+ &mipi_bif_client_attr_group,
+ NULL
+};
+
+static void mipi_bif_client_dev_release(struct device *dev)
+{
+ kfree(to_mipi_bif_client(dev));
+}
+
+static struct device_type mipi_bif_client_type = {
+ .groups = mipi_bif_client_attr_groups,
+ .release = mipi_bif_client_dev_release,
+};
+
+static const struct mipi_bif_device_id *mipi_bif_match_id(
+ const struct mipi_bif_device_id *id,
+ const struct mipi_bif_client *client)
+{
+ while (id->name[0]) {
+ if (strcmp(client->name, id->name) == 0)
+ return id;
+ id++;
+ }
+ return NULL;
+}
+
+static int mipi_bif_device_match(struct device *dev, struct device_driver *drv)
+{
+ if (dev->type == &mipi_bif_client_type) {
+
+ struct mipi_bif_client *client = to_mipi_bif_client(dev);
+ struct mipi_bif_driver *driver = to_mipi_bif_driver(drv);
+ if (driver->id_table)
+ return mipi_bif_match_id(driver->id_table,
+ client) != NULL;
+ }
+ return 0;
+}
+
+static int mipi_bif_device_probe(struct device *dev)
+{
+ int status = 0;
+ if (dev->type == &mipi_bif_client_type) {
+
+ struct mipi_bif_client *client = to_mipi_bif_client(dev);
+ struct mipi_bif_driver *driver;
+
+ if (!client)
+ return 0;
+
+ driver = to_mipi_bif_driver(dev->driver);
+ if (!driver->probe || !driver->id_table)
+ return -ENODEV;
+ client->driver = driver;
+
+ dev_dbg(dev, "probe\n");
+
+ status = driver->probe(client,
+ mipi_bif_match_id(driver->id_table, client));
+ if (status) {
+ client->driver = NULL;
+ mipi_bif_set_clientdata(client, NULL);
+ }
+ }
+ return status;
+}
+
+static int mipi_bif_device_remove(struct device *dev)
+{
+ int status = 0;
+ if (dev->type == &mipi_bif_client_type) {
+ struct mipi_bif_client *client = to_mipi_bif_client(dev);
+ struct mipi_bif_driver *driver;
+
+ if (!client || !dev->driver)
+ return 0;
+
+ driver = to_mipi_bif_driver(dev->driver);
+ if (driver->remove) {
+ dev_dbg(dev, "remove\n");
+ status = driver->remove(client);
+ } else {
+ dev->driver = NULL;
+ status = 0;
+ }
+ if (status == 0) {
+ client->driver = NULL;
+ mipi_bif_set_clientdata(client, NULL);
+ }
+ }
+ return status;
+}
+
+static void mipi_bif_device_shutdown(struct device *dev)
+{
+ if (dev->type == &mipi_bif_client_type) {
+
+ struct mipi_bif_client *client = to_mipi_bif_client(dev);
+ struct mipi_bif_driver *driver;
+
+ if (!client || !dev->driver)
+ return;
+ driver = to_mipi_bif_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(client);
+
+ }
+}
+#ifdef CONFIG_PM_SLEEP
+static int mipi_bif_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+ if (dev->type == &mipi_bif_client_type) {
+
+ struct mipi_bif_client *client = to_mipi_bif_client(dev);
+
+ struct mipi_bif_driver *driver;
+
+ if (!client || !dev->driver)
+ return 0;
+ driver = to_mipi_bif_driver(dev->driver);
+ if (!driver->suspend)
+ return 0;
+ return driver->suspend(client, mesg);
+
+ }
+ return 0;
+}
+
+static int mipi_bif_legacy_resume(struct device *dev)
+{
+ if (dev->type == &mipi_bif_client_type) {
+
+ struct mipi_bif_client *client = to_mipi_bif_client(dev);
+ struct mipi_bif_driver *driver;
+
+ if (!client || !dev->driver)
+ return 0;
+ driver = to_mipi_bif_driver(dev->driver);
+ if (!driver->resume)
+ return 0;
+ return driver->resume(client);
+ }
+ return 0;
+}
+
+static int mipi_bif_device_pm_suspend(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm)
+ return pm_generic_suspend(dev);
+ else
+ return mipi_bif_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int mipi_bif_device_pm_resume(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm)
+ return pm_generic_resume(dev);
+ else
+ return mipi_bif_legacy_resume(dev);
+}
+
+static int mipi_bif_device_pm_freeze(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm)
+ return pm_generic_freeze(dev);
+ else
+ return mipi_bif_legacy_suspend(dev, PMSG_FREEZE);
+}
+
+static int mipi_bif_device_pm_thaw(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm)
+ return pm_generic_thaw(dev);
+ else
+ return mipi_bif_legacy_resume(dev);
+}
+
+static int mipi_bif_device_pm_poweroff(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm)
+ return pm_generic_poweroff(dev);
+ else
+ return mipi_bif_legacy_suspend(dev, PMSG_HIBERNATE);
+}
+
+static int mipi_bif_device_pm_restore(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm)
+ return pm_generic_restore(dev);
+ else
+ return mipi_bif_legacy_resume(dev);
+}
+#else /* !CONFIG_PM_SLEEP */
+#define mipi_bif_device_pm_suspend NULL
+#define mipi_bif_device_pm_resume NULL
+#define mipi_bif_device_pm_freeze NULL
+#define mipi_bif_device_pm_thaw NULL
+#define mipi_bif_device_pm_poweroff NULL
+#define mipi_bif_device_pm_restore NULL
+#endif /* !CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops mipi_bif_device_pm_ops = {
+ .suspend = mipi_bif_device_pm_suspend,
+ .resume = mipi_bif_device_pm_resume,
+ .freeze = mipi_bif_device_pm_freeze,
+ .thaw = mipi_bif_device_pm_thaw,
+ .poweroff = mipi_bif_device_pm_poweroff,
+ .restore = mipi_bif_device_pm_restore,
+ SET_RUNTIME_PM_OPS(
+ pm_generic_runtime_suspend,
+ pm_generic_runtime_resume,
+ pm_generic_runtime_idle
+ )
+};
+
+struct bus_type mipi_bif_bus_type = {
+ .name = "mipi_bif",
+ .match = mipi_bif_device_match,
+ .probe = mipi_bif_device_probe,
+ .remove = mipi_bif_device_remove,
+ .shutdown = mipi_bif_device_shutdown,
+ .pm = &mipi_bif_device_pm_ops,
+};
+EXPORT_SYMBOL_GPL(mipi_bif_bus_type);
+
+void mipi_bif_lock_adapter(struct mipi_bif_adapter *adapter)
+{
+ struct mipi_bif_adapter *parent =
+ mipi_bif_parent_is_mipi_bif_adapter(adapter);
+
+ if (parent)
+ mipi_bif_lock_adapter(parent);
+ else
+ rt_mutex_lock(&adapter->bus_lock);
+}
+EXPORT_SYMBOL_GPL(mipi_bif_lock_adapter);
+
+static int mipi_bif_trylock_adapter(struct mipi_bif_adapter *adapter)
+{
+ struct mipi_bif_adapter *parent =
+ mipi_bif_parent_is_mipi_bif_adapter(adapter);
+
+ if (parent)
+ return mipi_bif_trylock_adapter(parent);
+ else
+ return rt_mutex_trylock(&adapter->bus_lock);
+}
+
+void mipi_bif_unlock_adapter(struct mipi_bif_adapter *adapter)
+{
+ struct mipi_bif_adapter *parent =
+ mipi_bif_parent_is_mipi_bif_adapter(adapter);
+
+ if (parent)
+ mipi_bif_unlock_adapter(parent);
+ else
+ rt_mutex_unlock(&adapter->bus_lock);
+}
+EXPORT_SYMBOL_GPL(mipi_bif_unlock_adapter);
+
+static int mipi_bif_register_adapter(struct mipi_bif_adapter *adap)
+{
+ int res = 0;
+
+ /* Can't register until after driver model init */
+ if (unlikely(WARN_ON(!mipi_bif_bus_type.p))) {
+ res = -EAGAIN;
+ goto out_list;
+ }
+
+ if (unlikely(adap->name[0] == '\0')) {
+ pr_err("mipi-bif-core:Attempt to add adapter without name\n");
+ return -EINVAL;
+ }
+ if (unlikely(!adap->algo)) {
+ pr_err("mipi-bif-core:Attempt to add adapter %s without algo\n",
+ adap->name);
+ return -EINVAL;
+ }
+
+ rt_mutex_init(&adap->bus_lock);
+
+ if (adap->timeout == 0)
+ adap->timeout = HZ;
+
+ dev_set_name(&adap->dev, "mipi-bif.%d", adap->nr);
+ adap->dev.bus = &mipi_bif_bus_type;
+ adap->dev.type = &mipi_bif_adapter_type;
+ res = device_register(&adap->dev);
+ if (res)
+ goto out_list;
+
+ dev_dbg(&adap->dev, "adapter %s registered\n", adap->name);
+
+#ifdef CONFIG_MIPI_BIF_COMPAT
+ res = class_compat_create_link(mipi_bif_adapter_compat_class,
+ &adap->dev, adap->dev.parent);
+ if (res)
+ dev_warn(&adap->dev,
+ "Failed to create compatibility class link\n");
+#endif
+
+ return 0;
+
+out_list:
+ mutex_lock(&core_lock);
+ idr_remove(&mipi_bif_adapter_idr, adap->nr);
+ mutex_unlock(&core_lock);
+ return res;
+}
+
+/**
+ * mipi_bif_add_adapter - declare mipi_bif adapter, use dynamic bus number
+ * @adapter: the adapter to add
+ * Context: can sleep
+ *
+ * When this returns zero, a new bus number was allocated and stored
+ * in adap->nr, and the specified adapter became available for clients.
+ * Otherwise, a negative errno value is returned.
+ */
+int mipi_bif_add_adapter(struct mipi_bif_adapter *adapter)
+{
+ int id, res = 0;
+
+retry:
+ if (idr_pre_get(&mipi_bif_adapter_idr, GFP_KERNEL) == 0)
+ return -ENOMEM;
+
+ mutex_lock(&core_lock);
+ /* "above" here means "above or equal to", sigh */
+ res = idr_get_new_above(&mipi_bif_adapter_idr, adapter,
+ __mipi_bif_first_dynamic_bus_num, &id);
+ mutex_unlock(&core_lock);
+
+ if (res < 0) {
+ if (res == -EAGAIN)
+ goto retry;
+ return res;
+ }
+
+ adapter->nr = id;
+ return mipi_bif_register_adapter(adapter);
+}
+EXPORT_SYMBOL_GPL(mipi_bif_add_adapter);
+
+/*
+ * mipi_bif_add_numbered_adapter - declare mipi_bif adapter with static bus num
+ * @adap: the adapter to register (with adap->nr initialized)
+ * Context: can sleep
+ */
+int mipi_bif_add_numbered_adapter(struct mipi_bif_adapter *adap)
+{
+ int id;
+ int status;
+
+ if (adap->nr == -1) /* -1 means dynamically assign bus id */
+ return mipi_bif_add_adapter(adap);
+ if (adap->nr & ~MAX_IDR_MASK)
+ return -EINVAL;
+
+retry:
+ if (idr_pre_get(&mipi_bif_adapter_idr, GFP_KERNEL) == 0)
+ return -ENOMEM;
+
+ mutex_lock(&core_lock);
+
+ status = idr_get_new_above(&mipi_bif_adapter_idr, adap, adap->nr, &id);
+ if (status == 0 && id != adap->nr) {
+ status = -EBUSY;
+ idr_remove(&mipi_bif_adapter_idr, id);
+ }
+ mutex_unlock(&core_lock);
+ if (status == -EAGAIN)
+ goto retry;
+
+ if (status == 0)
+ status = mipi_bif_register_adapter(adap);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(mipi_bif_add_numbered_adapter);
+
+void mipi_bif_unregister_device(struct mipi_bif_client *client)
+{
+ device_unregister(&client->dev);
+}
+
+static int mipi_bif_do_del_adapter(struct mipi_bif_driver *driver,
+ struct mipi_bif_adapter *adapter)
+{
+ int res;
+ if (!driver->detach_adapter)
+ return 0;
+ dev_warn(&adapter->dev, "%s: detach_adapter method is deprecated\n",
+ driver->driver.name);
+
+ res = driver->detach_adapter(adapter);
+ if (res)
+ dev_err(&adapter->dev, "detach_adapter failed (%d)"\
+ "for driver %s\n", res, driver->driver.name);
+ return res;
+}
+
+static int __process_removed_adapter(struct device_driver *d, void *data)
+{
+ return mipi_bif_do_del_adapter(to_mipi_bif_driver(d), data);
+}
+
+int mipi_bif_del_adapter(struct mipi_bif_adapter *adap)
+{
+ int res = 0;
+ struct mipi_bif_adapter *found;
+
+ /* First make sure that this adapter was ever added */
+ mutex_lock(&core_lock);
+ found = idr_find(&mipi_bif_adapter_idr, adap->nr);
+ mutex_unlock(&core_lock);
+ if (found != adap) {
+ pr_debug("mipi-bif-core: attempting to delete unregistered "\
+ "adapter %s\n", adap->name);
+ return -EINVAL;
+ }
+
+ /* Tell drivers about this removal */
+ mutex_lock(&core_lock);
+ res = bus_for_each_drv(&mipi_bif_bus_type, NULL, adap,
+ __process_removed_adapter);
+ mutex_unlock(&core_lock);
+ if (res)
+ return res;
+
+#ifdef CONFIG_MIPI_BIF_COMPAT
+ class_compat_remove_link(mipi_bif_adapter_compat_class, &adap->dev,
+ adap->dev.parent);
+#endif
+ dev_dbg(&adap->dev, "adapter %s unregistered\n", adap->name);
+
+ init_completion(&adap->dev_released);
+ device_unregister(&adap->dev);
+ /* wait for sysfs to drop all references */
+ wait_for_completion(&adap->dev_released);
+
+ /* free bus id */
+ mutex_lock(&core_lock);
+ idr_remove(&mipi_bif_adapter_idr, adap->nr);
+ mutex_unlock(&core_lock);
+
+ memset(&adap->dev, 0, sizeof(adap->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mipi_bif_del_adapter);
+
+struct mipi_bif_client *
+ mipi_bif_new_device(struct mipi_bif_adapter *adap,
+ struct mipi_bif_board_info const *info)
+{
+ struct mipi_bif_client *client;
+ int status;
+
+ client = kzalloc(sizeof *client, GFP_KERNEL);
+ if (!client)
+ return NULL;
+
+ client->adapter = adap;
+ client->addr = info->addr;
+ client->dev.platform_data = info->platform_data;
+
+ if (info->archdata)
+ client->dev.archdata = *info->archdata;
+
+ strlcpy(client->name, info->type, sizeof(client->name));
+
+ client->dev.parent = &client->adapter->dev;
+ client->dev.bus = &mipi_bif_bus_type;
+ client->dev.type = &mipi_bif_client_type;
+
+ dev_set_name(&client->dev, "%d-%04x",
+ mipi_bif_adapter_id(adap), client->addr);
+
+ status = device_register(&client->dev);
+ if (status)
+ goto out_err;
+
+ dev_dbg(&adap->dev, "client %s registered with bus id %s\n",
+ client->name, dev_name(&client->dev));
+
+ return client;
+
+out_err:
+ dev_err(&adap->dev, "Failed to register mipi_bif client %s at 0x%02x "\
+ "(%d)\n", client->name, client->addr, status);
+ kfree(client);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mipi_bif_new_device);
+
+int mipi_bif_register_driver(struct module *owner,
+ struct mipi_bif_driver *driver)
+{
+ int res;
+
+ /* Can't register until after driver model init */
+ if (unlikely(WARN_ON(!mipi_bif_bus_type.p)))
+ return -EAGAIN;
+
+ /* add the driver to the list of mipi_bif drivers in the driver core */
+ driver->driver.owner = owner;
+ driver->driver.bus = &mipi_bif_bus_type;
+
+ /* When registration returns, the driver core
+ * will have called probe() for all matching-but-unbound devices.
+ */
+ res = driver_register(&driver->driver);
+ if (res)
+ return res;
+
+ /* Drivers should switch to dev_pm_ops instead. */
+ if (driver->suspend)
+ pr_warn("mipi-bif-core: driver %s using legacy suspend\n",
+ driver->driver.name);
+ if (driver->resume)
+ pr_warn("mipi-bif-core: driver %s using legacy resume\n",
+ driver->driver.name);
+
+ pr_debug("mipi-bif-core: driver %s registered\n", driver->driver.name);
+
+ INIT_LIST_HEAD(&driver->clients);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mipi_bif_register_driver);
+
+int mipi_bif_for_each_dev(void *data, int (*fn)(struct device *, void *))
+{
+ int res;
+
+ mutex_lock(&core_lock);
+ res = bus_for_each_dev(&mipi_bif_bus_type, NULL, data, fn);
+ mutex_unlock(&core_lock);
+
+ return res;
+}
+
+static int __process_removed_driver(struct device *dev, void *data)
+{
+ if (dev->type != &mipi_bif_adapter_type)
+ return 0;
+ return mipi_bif_do_del_adapter(data, to_mipi_bif_adapter(dev));
+}
+
+void mipi_bif_del_driver(struct mipi_bif_driver *driver)
+{
+ mipi_bif_for_each_dev(driver, __process_removed_driver);
+
+ driver_unregister(&driver->driver);
+ pr_debug("mipi-bif-core:driver %s unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL_GPL(mipi_bif_del_driver);
+
+struct mipi_bif_adapter *mipi_bif_get_adapter(int nr)
+{
+ struct mipi_bif_adapter *adapter;
+
+ mutex_lock(&core_lock);
+ adapter = idr_find(&mipi_bif_adapter_idr, nr);
+ if (adapter && !try_module_get(adapter->owner))
+ adapter = NULL;
+
+ mutex_unlock(&core_lock);
+ return adapter;
+}
+EXPORT_SYMBOL_GPL(mipi_bif_get_adapter);
+
+int mipi_bif_transfer(struct mipi_bif_adapter *adap, struct mipi_bif_msg *msg)
+{
+ unsigned long orig_jiffies;
+ int ret, try;
+
+ if (adap->algo->master_xfer) {
+
+ if (in_atomic() || irqs_disabled()) {
+ ret = mipi_bif_trylock_adapter(adap);
+ if (!ret)
+ /* mipi_bif activity is ongoing. */
+ return -EAGAIN;
+ } else {
+ mipi_bif_lock_adapter(adap);
+ }
+
+ /* Retry automatically on arbitration loss */
+ orig_jiffies = jiffies;
+ for (ret = 0, try = 0; try <= adap->retries; try++) {
+ ret = adap->algo->master_xfer(adap, msg);
+ if (ret != -EAGAIN)
+ break;
+ if (time_after(jiffies, orig_jiffies + adap->timeout))
+ break;
+ }
+ mipi_bif_unlock_adapter(adap);
+
+ return ret;
+ } else {
+ dev_dbg(&adap->dev, "mipi_bif level transfers not supported\n");
+ return -EOPNOTSUPP;
+ }
+}
+EXPORT_SYMBOL_GPL(mipi_bif_transfer);
+
+static int __init mipi_bif_init(void)
+{
+ int retval;
+
+ retval = bus_register(&mipi_bif_bus_type);
+ if (retval)
+ return retval;
+#ifdef CONFIG_MIPI_BIF_COMPAT
+ mipi_bif_adapter_compat_class =
+ class_compat_register("mipi_bif_adapter");
+ if (!mipi_bif_adapter_compat_class) {
+ retval = -ENOMEM;
+ goto bus_err;
+ }
+#endif
+ return 0;
+
+#ifdef CONFIG_MIPI_BIF_COMPAT
+ class_compat_unregister(mipi_bif_adapter_compat_class);
+bus_err:
+#endif
+ bus_unregister(&mipi_bif_bus_type);
+ return retval;
+}
+
+static void __exit mipi_bif_exit(void)
+{
+#ifdef CONFIG_MIPI_BIF_COMPAT
+ class_compat_unregister(mipi_bif_adapter_compat_class);
+#endif
+ bus_unregister(&mipi_bif_bus_type);
+}
+
+postcore_initcall(mipi_bif_init);
+module_exit(mipi_bif_exit);
+
+MODULE_AUTHOR("Chaitanya Bandi<bandik@nvidia.com>");
+MODULE_DESCRIPTION("MIPI BIF core driver module");
+MODULE_LICENSE("GPL");