spmi: Linux driver framework for SPMI

SPMI (System Power Management Interface) is a specification
developed by the MIPI (Mobile Industry Process Interface) Alliance
optimized for the real time control of Power Management ICs (PMIC).

SPMI is a two-wire serial interface that supports up to 4 master
devices and up to 16 logical slaves.  A physical PMIC device may
consist of multiple logical slave interfaces.

The framework supports message APIs, multiple busses (1 controller
per bus) and multiple clients/slave devices per controller.

Change-Id: Ib6319047b4ab9cea6dfb0879312c4c1f63462439
Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 851189d..58bf45d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -52,6 +52,8 @@
 
 source "drivers/spi/Kconfig"
 
+source "drivers/spmi/Kconfig"
+
 source "drivers/slimbus/Kconfig"
 
 source "drivers/pps/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index e18822cd..82008ed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -51,6 +51,7 @@
 obj-$(CONFIG_TARGET_CORE)	+= target/
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
+obj-$(CONFIG_SPMI)		+= spmi/
 obj-$(CONFIG_SLIMBUS)		+= slimbus/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
new file mode 100644
index 0000000..a03835f
--- /dev/null
+++ b/drivers/spmi/Kconfig
@@ -0,0 +1,9 @@
+#
+# SPMI driver configuration
+#
+menuconfig SPMI
+	bool "SPMI support"
+	help
+	  SPMI (System Power Management Interface) is a two-wire
+	  serial interface between baseband and application processors
+	  and Power Management Integrated Circuits (PMIC).
diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
new file mode 100644
index 0000000..7bfc6db
--- /dev/null
+++ b/drivers/spmi/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for kernel SPMI framework.
+#
+obj-$(CONFIG_SPMI)			+= spmi.o
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
new file mode 100644
index 0000000..2842cd8
--- /dev/null
+++ b/drivers/spmi/spmi.c
@@ -0,0 +1,789 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spmi.h>
+
+struct spmii_boardinfo {
+	struct list_head	list;
+	struct spmi_boardinfo	board_info;
+};
+
+static DEFINE_MUTEX(board_lock);
+static LIST_HEAD(board_list);
+static LIST_HEAD(spmi_ctrl_list);
+static DEFINE_IDR(ctrl_idr);
+static struct device_type spmi_ctrl_type = { 0 };
+
+#define to_spmi(dev)	platform_get_drvdata(to_platform_device(dev))
+
+/* Forward declarations */
+struct bus_type spmi_bus_type;
+static int spmi_register_controller(struct spmi_controller *ctrl);
+
+/**
+ * spmi_busnum_to_ctrl: Map bus number to controller
+ * @busnum: bus number
+ * Returns controller representing this bus number
+ */
+struct spmi_controller *spmi_busnum_to_ctrl(u32 bus_num)
+{
+	struct spmi_controller *ctrl;
+
+	mutex_lock(&board_lock);
+	list_for_each_entry(ctrl, &spmi_ctrl_list, list) {
+		if (bus_num == ctrl->nr) {
+			mutex_unlock(&board_lock);
+			return ctrl;
+		}
+	}
+	mutex_unlock(&board_lock);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(spmi_busnum_to_ctrl);
+
+/**
+ * spmi_add_controller: Controller bring-up.
+ * @ctrl: controller to be registered.
+ * A controller is registered with the framework using this API. ctrl->nr is the
+ * desired number with which SPMI framework registers the controller.
+ * Function will return -EBUSY if the number is in use.
+ */
+int spmi_add_controller(struct spmi_controller *ctrl)
+{
+	int	id;
+	int	status;
+
+	pr_debug("adding controller for bus %d (0x%p)\n", ctrl->nr, ctrl);
+
+	if (ctrl->nr & ~MAX_ID_MASK) {
+		pr_err("invalid bus identifier %d\n", ctrl->nr);
+		return -EINVAL;
+	}
+
+retry:
+	if (idr_pre_get(&ctrl_idr, GFP_KERNEL) == 0) {
+		pr_err("no free memory for idr\n");
+		return -ENOMEM;
+	}
+
+	mutex_lock(&board_lock);
+	status = idr_get_new_above(&ctrl_idr, ctrl, ctrl->nr, &id);
+	if (status == 0 && id != ctrl->nr) {
+		status = -EAGAIN;
+		idr_remove(&ctrl_idr, id);
+	}
+	mutex_unlock(&board_lock);
+	if (status == -EAGAIN)
+		goto retry;
+
+	if (status == 0)
+		status = spmi_register_controller(ctrl);
+	return status;
+}
+EXPORT_SYMBOL_GPL(spmi_add_controller);
+
+/**
+ * spmi_del_controller: Controller tear-down.
+ * @ctrl: controller to which this device is to be added to.
+ *
+ * Controller added with the above API is torn down using this API.
+ */
+int spmi_del_controller(struct spmi_controller *ctrl)
+{
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(spmi_del_controller);
+
+#define spmi_device_attr_gr NULL
+#define spmi_device_uevent NULL
+static void spmi_dev_release(struct device *dev)
+{
+	struct spmi_device *spmidev = to_spmi_device(dev);
+	kfree(spmidev);
+}
+
+static struct device_type spmi_dev_type = {
+	.groups		= spmi_device_attr_gr,
+	.uevent		= spmi_device_uevent,
+	.release	= spmi_dev_release,
+};
+
+/**
+ * spmi_alloc_device: Allocate a new SPMI devices.
+ * @ctrl: controller to which this device is to be added to.
+ * Context: can sleep
+ *
+ * Allows a driver to allocate and initialize a SPMI device without
+ * registering it immediately.  This allows a driver to directly fill
+ * the spmi_device structure before calling spmi_add_device().
+ *
+ * Caller is responsible to call spmi_add_device() on the returned
+ * spmi_device.  If the caller needs to discard the spmi_device without
+ * adding it, then spmi_dev_put() should be called.
+ */
+struct spmi_device *spmi_alloc_device(struct spmi_controller *ctrl)
+{
+	struct spmi_device *spmidev;
+
+	if (!ctrl) {
+		pr_err("Missing SPMI controller\n");
+		return NULL;
+	}
+
+	spmidev = kzalloc(sizeof(*spmidev), GFP_KERNEL);
+	if (!spmidev) {
+		dev_err(&ctrl->dev, "unable to allocate spmi_device\n");
+		return NULL;
+	}
+
+	spmidev->ctrl = ctrl;
+	spmidev->dev.parent = ctrl->dev.parent;
+	spmidev->dev.bus = &spmi_bus_type;
+	spmidev->dev.type = &spmi_dev_type;
+	device_initialize(&spmidev->dev);
+
+	return spmidev;
+}
+EXPORT_SYMBOL_GPL(spmi_alloc_device);
+
+/* Validate the SPMI device structure */
+static struct device *get_valid_device(struct spmi_device *spmidev)
+{
+	struct device *dev;
+
+	if (!spmidev)
+		return NULL;
+
+	dev = &spmidev->dev;
+	if (dev->bus != &spmi_bus_type || dev->type != &spmi_dev_type)
+		return NULL;
+
+	return dev;
+}
+
+/**
+ * spmi_add_device: Add a new device without register board info.
+ * @ctrl: controller to which this device is to be added to.
+ *
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+int spmi_add_device(struct spmi_device *spmidev)
+{
+	int rc;
+	struct device *dev = get_valid_device(spmidev);
+
+	if (!dev) {
+		pr_err("%s: invalid SPMI device\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Set the device name */
+	dev_set_name(dev, "%s-%p", spmidev->name, spmidev);
+
+	/* Device may be bound to an active driver when this returns */
+	rc = device_add(dev);
+
+	if (rc < 0)
+		dev_err(dev, "Can't add %s, status %d\n", dev_name(dev), rc);
+	else
+		dev_dbg(dev, "device %s registered\n", dev_name(dev));
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(spmi_add_device);
+
+/**
+ * spmi_new_device: Instantiates a new SPMI device
+ * @ctrl: controller to which this device is to be added to.
+ * @info: board information for this device.
+ *
+ * Returns the new device or NULL.
+ */
+struct spmi_device *spmi_new_device(struct spmi_controller *ctrl,
+					struct spmi_boardinfo const *info)
+{
+	struct spmi_device *spmidev;
+	int rc;
+
+	if (!ctrl || !info)
+		return NULL;
+
+	spmidev = spmi_alloc_device(ctrl);
+	if (!spmidev)
+		return NULL;
+
+	spmidev->name = info->name;
+	spmidev->sid  = info->slave_id;
+	spmidev->dev.of_node = info->of_node;
+	spmidev->resource = info->resource;
+	spmidev->num_resources = info->num_resources;
+	spmidev->dev.platform_data = (void *)info->platform_data;
+
+	rc = spmi_add_device(spmidev);
+	if (rc < 0) {
+		spmi_dev_put(spmidev);
+		return NULL;
+	}
+
+	return spmidev;
+}
+EXPORT_SYMBOL_GPL(spmi_new_device);
+
+/* spmi_remove_device: Remove the effect of spmi_add_device() */
+void spmi_remove_device(struct spmi_device *spmi_dev)
+{
+	device_unregister(&spmi_dev->dev);
+}
+EXPORT_SYMBOL_GPL(spmi_remove_device);
+
+/* If controller is not present, only add to boards list */
+static void spmi_match_ctrl_to_boardinfo(struct spmi_controller *ctrl,
+				struct spmi_boardinfo *bi)
+{
+	struct spmi_device *spmidev;
+
+	spmidev = spmi_new_device(ctrl, bi);
+	if (!spmidev)
+		dev_err(ctrl->dev.parent, "can't create new device for %s\n",
+			bi->name);
+}
+
+/**
+ * spmi_register_board_info: Board-initialization routine.
+ * @bus_num: controller number (bus) on which this device will sit.
+ * @info: list of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+int spmi_register_board_info(int busnum,
+			struct spmi_boardinfo const *info, unsigned n)
+{
+	int i;
+	struct spmii_boardinfo *bi;
+
+	bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
+	if (!bi)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++, bi++, info++) {
+		struct spmi_controller *ctrl;
+
+		memcpy(&bi->board_info, info, sizeof(*info));
+		mutex_lock(&board_lock);
+		list_add_tail(&bi->list, &board_list);
+		list_for_each_entry(ctrl, &spmi_ctrl_list, list)
+			if (ctrl->nr == busnum)
+				spmi_match_ctrl_to_boardinfo(ctrl,
+							&bi->board_info);
+		mutex_unlock(&board_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spmi_register_board_info);
+
+/* ------------------------------------------------------------------------- */
+
+static inline int
+spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid)
+{
+	BUG_ON(!ctrl || !ctrl->cmd);
+	return ctrl->cmd(ctrl, opcode, sid);
+}
+
+static inline int spmi_read_cmd(struct spmi_controller *ctrl,
+				u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	BUG_ON(!ctrl || !ctrl->read_cmd);
+	return ctrl->read_cmd(ctrl, opcode, sid, addr, bc, buf);
+}
+
+static inline int spmi_write_cmd(struct spmi_controller *ctrl,
+				u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	BUG_ON(!ctrl || !ctrl->write_cmd);
+	return ctrl->write_cmd(ctrl, opcode, sid, addr, bc, buf);
+}
+
+/*
+ * register read/write: 5-bit address, 1 byte of data
+ * extended register read/write: 8-bit address, up to 16 bytes of data
+ * extended register read/write long: 16-bit address, up to 8 bytes of data
+ */
+
+/**
+ * spmi_register_read() - register read
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (5-bit address).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads 1 byte of data from a Slave device register.
+ */
+int spmi_register_read(struct spmi_controller *ctrl, u8 sid, u8 addr, u8 *buf)
+{
+	/* 4-bit Slave Identifier, 5-bit register address */
+	if (sid > SPMI_MAX_SLAVE_ID || addr > 0x1F)
+		return -EINVAL;
+
+	return spmi_read_cmd(ctrl, SPMI_CMD_READ, sid, addr, 0, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_register_read);
+
+/**
+ * spmi_ext_register_read() - extended register read
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (8-bit address).
+ * @len: the request number of bytes to read (up to 16 bytes).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads up to 16 bytes of data from the extended register space on a
+ * Slave device.
+ */
+int spmi_ext_register_read(struct spmi_controller *ctrl,
+				u8 sid, u8 addr, u8 *buf, int len)
+{
+	/* 4-bit Slave Identifier, 8-bit register address, up to 16 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 16)
+		return -EINVAL;
+
+	return spmi_read_cmd(ctrl, SPMI_CMD_EXT_READ, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_read);
+
+/**
+ * spmi_ext_register_readl() - extended register read long
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (16-bit address).
+ * @len: the request number of bytes to read (up to 8 bytes).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads up to 8 bytes of data from the extended register space on a
+ * Slave device using 16-bit address.
+ */
+int spmi_ext_register_readl(struct spmi_controller *ctrl,
+				u8 sid, u16 addr, u8 *buf, int len)
+{
+	/* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 8)
+		return -EINVAL;
+
+	return spmi_read_cmd(ctrl, SPMI_CMD_EXT_READL, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_readl);
+
+/**
+ * spmi_register_write() - register write
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (5-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ *
+ * Writes 1 byte of data to a Slave device register.
+ */
+int spmi_register_write(struct spmi_controller *ctrl, u8 sid, u8 addr, u8 *buf)
+{
+	u8 op = SPMI_CMD_WRITE;
+
+	/* 4-bit Slave Identifier, 5-bit register address */
+	if (sid > SPMI_MAX_SLAVE_ID || addr > 0x1F)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, addr, 0, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_register_write);
+
+/**
+ * spmi_register_zero_write() - register zero write
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @data: the data to be written to register 0 (7-bits).
+ *
+ * Writes data to register 0 of the Slave device.
+ */
+int spmi_register_zero_write(struct spmi_controller *ctrl, u8 sid, u8 data)
+{
+	u8 op = SPMI_CMD_ZERO_WRITE;
+
+	/* 4-bit Slave Identifier, 5-bit register address */
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, 0, 0, &data);
+}
+EXPORT_SYMBOL_GPL(spmi_register_zero_write);
+
+/**
+ * spmi_ext_register_write() - extended register write
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (8-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ * @len: the request number of bytes to read (up to 16 bytes).
+ *
+ * Writes up to 16 bytes of data to the extended register space of a
+ * Slave device.
+ */
+int spmi_ext_register_write(struct spmi_controller *ctrl,
+				u8 sid, u8 addr, u8 *buf, int len)
+{
+	u8 op = SPMI_CMD_EXT_WRITE;
+
+	/* 4-bit Slave Identifier, 8-bit register address, up to 16 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 16)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_write);
+
+/**
+ * spmi_ext_register_writel() - extended register write long
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ * @ad: slave register address (16-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ * @len: the request number of bytes to read (up to 8 bytes).
+ *
+ * Writes up to 8 bytes of data to the extended register space of a
+ * Slave device using 16-bit address.
+ */
+int spmi_ext_register_writel(struct spmi_controller *ctrl,
+				u8 sid, u16 addr, u8 *buf, int len)
+{
+	u8 op = SPMI_CMD_EXT_WRITEL;
+
+	/* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
+	if (sid > SPMI_MAX_SLAVE_ID || len <= 0 || len > 8)
+		return -EINVAL;
+
+	return spmi_write_cmd(ctrl, op, sid, addr, len - 1, buf);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_writel);
+
+/**
+ * spmi_command_reset() - sends RESET command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Reset command initializes the Slave and forces all registers to
+ * their reset values. The Slave shall enter the STARTUP state after
+ * receiving a Reset command.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_reset(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_RESET, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_reset);
+
+/**
+ * spmi_command_sleep() - sends SLEEP command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Sleep command causes the Slave to enter the user defined SLEEP state.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_sleep(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_SLEEP, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_sleep);
+
+/**
+ * spmi_command_wakeup() - sends WAKEUP command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Wakeup command causes the Slave to move from the SLEEP state to
+ * the ACTIVE state.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_wakeup(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_WAKEUP, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_wakeup);
+
+/**
+ * spmi_command_shutdown() - sends SHUTDOWN command to the specified slave
+ * @dev: SPMI device.
+ * @sid: slave identifier.
+ *
+ * The Shutdown command causes the Slave to enter the SHUTDOWN state.
+ *
+ * Returns
+ * -EINVAL for invalid Slave Identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+int spmi_command_shutdown(struct spmi_controller *ctrl, u8 sid)
+{
+	if (sid > SPMI_MAX_SLAVE_ID)
+		return -EINVAL;
+	return spmi_cmd(ctrl, SPMI_CMD_SHUTDOWN, sid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_shutdown);
+
+/* ------------------------------------------------------------------------- */
+
+static const struct spmi_device_id *spmi_match(const struct spmi_device_id *id,
+		const struct spmi_device *spmi_dev)
+{
+	while (id->name[0]) {
+		if (strncmp(spmi_dev->name, id->name, SPMI_NAME_SIZE) == 0)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int spmi_device_match(struct device *dev, struct device_driver *drv)
+{
+	struct spmi_device *spmi_dev;
+	struct spmi_driver *sdrv = to_spmi_driver(drv);
+
+	if (dev->type == &spmi_dev_type)
+		spmi_dev = to_spmi_device(dev);
+	else
+		return 0;
+
+	/* Attempt an OF style match */
+	if (of_driver_match_device(dev, drv))
+		return 1;
+
+	if (sdrv->id_table)
+		return spmi_match(sdrv->id_table, spmi_dev) != NULL;
+
+	if (drv->name)
+		return strncmp(spmi_dev->name, drv->name, SPMI_NAME_SIZE) == 0;
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int spmi_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct spmi_device *spmi_dev = NULL;
+	struct spmi_driver *driver;
+	if (dev->type == &spmi_dev_type)
+		spmi_dev = to_spmi_device(dev);
+
+	if (!spmi_dev || !dev->driver)
+		return 0;
+
+	driver = to_spmi_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+
+	return driver->suspend(spmi_dev, mesg);
+}
+
+static int spmi_legacy_resume(struct device *dev)
+{
+	struct spmi_device *spmi_dev = NULL;
+	struct spmi_driver *driver;
+	if (dev->type == &spmi_dev_type)
+		spmi_dev = to_spmi_device(dev);
+
+	if (!spmi_dev || !dev->driver)
+		return 0;
+
+	driver = to_spmi_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+
+	return driver->resume(spmi_dev);
+}
+
+static int spmi_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 spmi_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int spmi_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 spmi_legacy_resume(dev);
+}
+
+#else
+#define spmi_pm_suspend		NULL
+#define spmi_pm_resume		NULL
+#endif
+
+static const struct dev_pm_ops spmi_pm_ops = {
+	.suspend = spmi_pm_suspend,
+	.resume = spmi_pm_resume,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_suspend,
+		pm_generic_resume,
+		pm_generic_runtime_idle
+		)
+};
+struct bus_type spmi_bus_type = {
+	.name		= "spmi",
+	.match		= spmi_device_match,
+	.pm		= &spmi_pm_ops,
+};
+EXPORT_SYMBOL_GPL(spmi_bus_type);
+
+struct device spmi_dev = {
+	.init_name = "spmi",
+};
+
+static int spmi_drv_probe(struct device *dev)
+{
+	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+	return sdrv->probe(to_spmi_device(dev));
+}
+
+static int spmi_drv_remove(struct device *dev)
+{
+	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+	return sdrv->remove(to_spmi_device(dev));
+}
+
+static void spmi_drv_shutdown(struct device *dev)
+{
+	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+	sdrv->shutdown(to_spmi_device(dev));
+}
+
+/**
+ * spmi_driver_register: Client driver registration with SPMI framework.
+ * @drv: client driver to be associated with client-device.
+ *
+ * This API will register the client driver with the SPMI framework.
+ * It is called from the driver's module-init function.
+ */
+int spmi_driver_register(struct spmi_driver *drv)
+{
+	drv->driver.bus = &spmi_bus_type;
+
+	if (drv->probe)
+		drv->driver.probe = spmi_drv_probe;
+
+	if (drv->remove)
+		drv->driver.remove = spmi_drv_remove;
+
+	if (drv->shutdown)
+		drv->driver.shutdown = spmi_drv_shutdown;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(spmi_driver_register);
+
+static int spmi_register_controller(struct spmi_controller *ctrl)
+{
+	int ret = 0;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!spmi_bus_type.p)) {
+		ret = -EAGAIN;
+		goto exit;
+	}
+
+	dev_set_name(&ctrl->dev, "spmi-%d", ctrl->nr);
+	ctrl->dev.bus = &spmi_bus_type;
+	ctrl->dev.type = &spmi_ctrl_type;
+	ret = device_register(&ctrl->dev);
+	if (ret)
+		goto exit;
+
+	dev_dbg(&ctrl->dev, "Bus spmi-%d registered: dev:%x\n",
+					ctrl->nr, (u32)&ctrl->dev);
+
+	mutex_lock(&board_lock);
+	list_add_tail(&ctrl->list, &spmi_ctrl_list);
+	mutex_unlock(&board_lock);
+
+	return 0;
+
+exit:
+	mutex_lock(&board_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&board_lock);
+	return ret;
+}
+
+static void __exit spmi_exit(void)
+{
+	device_unregister(&spmi_dev);
+	bus_unregister(&spmi_bus_type);
+}
+
+static int __init spmi_init(void)
+{
+	int retval;
+
+	retval = bus_register(&spmi_bus_type);
+	if (!retval)
+		retval = device_register(&spmi_dev);
+
+	if (retval)
+		bus_unregister(&spmi_bus_type);
+
+	return retval;
+}
+postcore_initcall(spmi_init);
+module_exit(spmi_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("SPMI module");
+MODULE_ALIAS("platform:spmi");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 18997d7..cb394e8 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -436,6 +436,15 @@
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+#define SPMI_NAME_SIZE	32
+#define SPMI_MODULE_PREFIX "spmi:"
+
+struct spmi_device_id {
+	char name[SPMI_NAME_SIZE];
+	kernel_ulong_t driver_data	/* Data private to the driver */
+			__attribute__((aligned(sizeof(kernel_ulong_t))));
+};
+
 /* dmi */
 enum dmi_field {
 	DMI_NONE,
diff --git a/include/linux/spmi.h b/include/linux/spmi.h
new file mode 100644
index 0000000..ffcb24e
--- /dev/null
+++ b/include/linux/spmi.h
@@ -0,0 +1,408 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#ifndef _LINUX_SPMI_H
+#define _LINUX_SPMI_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+/* Maximum slave identifier */
+#define SPMI_MAX_SLAVE_ID		16
+
+/* SPMI Commands */
+enum spmi_commands {
+	SPMI_CMD_EXT_WRITE = 0x00,
+	SPMI_CMD_RESET = 0x10,
+	SPMI_CMD_SLEEP = 0x11,
+	SPMI_CMD_SHUTDOWN = 0x12,
+	SPMI_CMD_WAKEUP = 0x13,
+	SPMI_CMD_AUTHENTICATE = 0x14,
+	SPMI_CMD_MSTR_READ = 0x15,
+	SPMI_CMD_MSTR_WRITE = 0x16,
+	SPMI_CMD_TRANSFER_BUS_OWNERSHIP = 0x1A,
+	SPMI_CMD_DDB_MASTER_READ = 0x1B,
+	SPMI_CMD_DDB_SLAVE_READ = 0x1C,
+	SPMI_CMD_EXT_READ = 0x20,
+	SPMI_CMD_EXT_WRITEL = 0x30,
+	SPMI_CMD_EXT_READL = 0x38,
+	SPMI_CMD_WRITE = 0x40,
+	SPMI_CMD_READ = 0x60,
+	SPMI_CMD_ZERO_WRITE = 0x80,
+};
+
+struct spmi_device;
+
+/**
+ * struct spmi_controller: interface to the SPMI master controller
+ * @nr: board-specific number identifier for this controller/bus
+ * @name: name for this controller
+ * @cmd: sends a non-data command sequence on the SPMI bus.
+ * @read_cmd: sends a register read command sequence on the SPMI bus.
+ * @write_cmd: sends a register write command sequence on the SPMI bus.
+ */
+struct spmi_controller {
+	struct device		dev;
+	unsigned int		nr;
+	struct list_head	list;
+	int		(*cmd)(struct spmi_controller *, u8 opcode, u8 sid);
+	int		(*read_cmd)(struct spmi_controller *,
+				u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf);
+	int		(*write_cmd)(struct spmi_controller *,
+				u8 opcode, u8 sid, u16 addr, u8 bc, u8 *buf);
+};
+#define to_spmi_controller(d) container_of(d, struct spmi_controller, dev)
+
+/**
+ * struct spmi_driver: Manage SPMI generic/slave device driver
+ * @probe: binds this driver to a SPMI device.
+ * @remove: unbinds this driver from the SPMI device.
+ * @shutdown: standard shutdown callback used during powerdown/halt.
+ * @suspend: standard suspend callback used during system suspend
+ * @resume: standard resume callback used during system resume
+ * @driver: SPMI device drivers should initialize name and owner field of
+ *	    this structure
+ * @id_table: list of SPMI devices supported by this driver
+ */
+struct spmi_driver {
+	int				(*probe)(struct spmi_device *dev);
+	int				(*remove)(struct spmi_device *dev);
+	void				(*shutdown)(struct spmi_device *dev);
+	int				(*suspend)(struct spmi_device *dev,
+					pm_message_t pmesg);
+	int				(*resume)(struct spmi_device *dev);
+
+	struct device_driver		driver;
+	const struct spmi_device_id	*id_table;
+};
+#define to_spmi_driver(d) container_of(d, struct spmi_driver, driver)
+
+/**
+ * Client/device handle (struct spmi_device):
+ * ------------------------------------------
+ * This is the client/device handle returned when a SPMI device
+ * is registered with a controller.
+ * Pointer to this structure is used by client-driver as a handle.
+ * @dev: driver model representation of the device.
+ * @name: name of driver to use with this device.
+ * @ctrl: SPMI controller managing the bus hosting this device.
+ * @resource: array of resources for this device_node
+ * @num_resources: number of resources for this device node
+ * @sid: slave identifier.
+ */
+struct spmi_device {
+	struct device		dev;
+	const char		*name;
+	struct spmi_controller	*ctrl;
+	struct resource		*resource;
+	u32			num_resources;
+	u8			sid;
+};
+#define to_spmi_device(d) container_of(d, struct spmi_device, dev)
+
+/**
+ * struct spmi_boardinfo: Declare board info for SPMI device bringup.
+ * @slave_id: slave identifier.
+ * @spmi_device: device to be registered with the SPMI framework.
+ * @of_node: pointer to the OpenFirmware device node.
+ * @num_resources: number of resources for this device node
+ * @resource: array of resources for this device_node
+ * @platform_data: goes to spmi_device.dev.platform_data
+ */
+struct spmi_boardinfo {
+	char			name[SPMI_NAME_SIZE];
+	uint8_t			slave_id;
+	struct device_node	*of_node;
+	u32			num_resources;
+	struct resource		*resource;
+	const void		*platform_data;
+};
+
+/**
+ * spmi_driver_register: Client driver registration with SPMI framework.
+ * @drv: client driver to be associated with client-device.
+ *
+ * This API will register the client driver with the SPMI framework.
+ * It is called from the driver's module-init function.
+ */
+extern int spmi_driver_register(struct spmi_driver *drv);
+
+/**
+ * spmi_driver_unregister - reverse effect of spmi_driver_register
+ * @sdrv: the driver to unregister
+ * Context: can sleep
+ */
+static inline void spmi_driver_unregister(struct spmi_driver *sdrv)
+{
+	if (sdrv)
+		driver_unregister(&sdrv->driver);
+}
+
+/**
+ * spmi_add_controller: Controller bring-up.
+ * @ctrl: controller to be registered.
+ *
+ * A controller is registered with the framework using this API. ctrl->nr is the
+ * desired number with which SPMI framework registers the controller.
+ * Function will return -EBUSY if the number is in use.
+ */
+extern int spmi_add_controller(struct spmi_controller *ctrl);
+
+/**
+ * spmi_del_controller: Controller tear-down.
+ * Controller added with the above API is teared down using this API.
+ */
+extern int spmi_del_controller(struct spmi_controller *ctrl);
+
+/**
+ * spmi_busnum_to_ctrl: Map bus number to controller
+ * @busnum: bus number
+ *
+ * Returns controller device representing this bus number
+ */
+extern struct spmi_controller *spmi_busnum_to_ctrl(u32 bus_num);
+
+/**
+ * spmi_alloc_device: Allocate a new SPMI devices.
+ * @ctrl: controller to which this device is to be added to.
+ * Context: can sleep
+ *
+ * Allows a driver to allocate and initialize a SPMI device without
+ * registering it immediately.  This allows a driver to directly fill
+ * the spmi_device structure before calling spmi_add_device().
+ *
+ * Caller is responsible to call spmi_add_device() on the returned
+ * spmi_device.  If the caller needs to discard the spmi_device without
+ * adding it, then spmi_dev_put() should be called.
+ */
+extern struct spmi_device *spmi_alloc_device(struct spmi_controller *ctrl);
+
+/**
+ * spmi_add_device: Add spmi_device allocated with spmi_alloc_device().
+ * @spmi_dev: spmi_device to be added (registered).
+ */
+extern int spmi_add_device(struct spmi_device *spmi_dev);
+
+/**
+ * spmi_new_device: Instantiates a new SPMI device
+ * @ctrl: controller to which this device is to be added to.
+ * @info: board information for this device.
+ *
+ * Returns the new device or NULL.
+ */
+extern struct spmi_device *spmi_new_device(struct spmi_controller *ctrl,
+					struct spmi_boardinfo const *info);
+
+/* spmi_remove_device: Remove the effect of spmi_add_device() */
+extern void spmi_remove_device(struct spmi_device *spmi_dev);
+
+#ifdef CONFIG_SPMI
+/**
+ * spmi_register_board_info: Board-initialization routine.
+ * @bus_num: controller number (bus) on which this device will sit.
+ * @info: list of all devices on all controllers present on the board.
+ * @n: number of entries.
+ *
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+extern int spmi_register_board_info(int busnum,
+			struct spmi_boardinfo const *info, unsigned n);
+#else
+static inline int spmi_register_board_info(int busnum,
+			struct spmi_boardinfo const *info, unsigned n)
+{
+	return 0;
+}
+#endif
+
+static inline void *spmi_get_ctrldata(const struct spmi_controller *ctrl)
+{
+	return dev_get_drvdata(&ctrl->dev);
+}
+
+static inline void spmi_set_ctrldata(struct spmi_controller *ctrl, void *data)
+{
+	dev_set_drvdata(&ctrl->dev, data);
+}
+
+static inline void *spmi_get_devicedata(const struct spmi_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void spmi_set_devicedata(struct spmi_device *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+static inline void spmi_dev_put(struct spmi_device *spmidev)
+{
+	if (spmidev)
+		put_device(&spmidev->dev);
+}
+
+/**
+ * spmi_register_read() - register read
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @ad: slave register address (5-bit address).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads 1 byte of data from a Slave device register.
+ */
+extern int spmi_register_read(struct spmi_controller *ctrl,
+					u8 sid, u8 ad, u8 *buf);
+
+/**
+ * spmi_ext_register_read() - extended register read
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @ad: slave register address (8-bit address).
+ * @len: the request number of bytes to read (up to 16 bytes).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads up to 16 bytes of data from the extended register space on a
+ * Slave device.
+ */
+extern int spmi_ext_register_read(struct spmi_controller *ctrl,
+					u8 sid, u8 ad, u8 *buf, int len);
+
+/**
+ * spmi_ext_register_readl() - extended register read long
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @ad: slave register address (16-bit address).
+ * @len: the request number of bytes to read (up to 8 bytes).
+ * @buf: buffer to be populated with data from the Slave.
+ *
+ * Reads up to 8 bytes of data from the extended register space on a
+ * Slave device using 16-bit address.
+ */
+extern int spmi_ext_register_readl(struct spmi_controller *ctrl,
+					u8 sid, u16 ad, u8 *buf, int len);
+
+/**
+ * spmi_register_write() - register write
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @ad: slave register address (5-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ *
+ * Writes 1 byte of data to a Slave device register.
+ */
+extern int spmi_register_write(struct spmi_controller *ctrl,
+					u8 sid, u8 ad, u8 *buf);
+
+/**
+ * spmi_register_zero_write() - register zero write
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @data: the data to be written to register 0 (7-bits).
+ *
+ * Writes data to register 0 of the Slave device.
+ */
+extern int spmi_register_zero_write(struct spmi_controller *ctrl,
+					u8 sid, u8 data);
+
+/**
+ * spmi_ext_register_write() - extended register write
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @ad: slave register address (8-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ * @len: the request number of bytes to read (up to 16 bytes).
+ *
+ * Writes up to 16 bytes of data to the extended register space of a
+ * Slave device.
+ */
+extern int spmi_ext_register_write(struct spmi_controller *ctrl,
+					u8 sid, u8 ad, u8 *buf, int len);
+
+/**
+ * spmi_ext_register_writel() - extended register write long
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ * @ad: slave register address (16-bit address).
+ * @buf: buffer containing the data to be transferred to the Slave.
+ * @len: the request number of bytes to read (up to 8 bytes).
+ *
+ * Writes up to 8 bytes of data to the extended register space of a
+ * Slave device using 16-bit address.
+ */
+extern int spmi_ext_register_writel(struct spmi_controller *ctrl,
+					u8 sid, u16 ad, u8 *buf, int len);
+
+/**
+ * spmi_command_reset() - sends RESET command to the specified slave
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ *
+ * The Reset command initializes the Slave and forces all registers to
+ * their reset values. The Slave shall enter the STARTUP state after
+ * receiving a Reset command.
+ *
+ * Returns
+ * -EINVAL for invalid slave identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+extern int spmi_command_reset(struct spmi_controller *ctrl, u8 sid);
+
+/**
+ * spmi_command_sleep() - sends SLEEP command to the specified slave
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ *
+ * The Sleep command causes the Slave to enter the user defined SLEEP state.
+ *
+ * Returns
+ * -EINVAL for invalid slave identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+extern int spmi_command_sleep(struct spmi_controller *ctrl, u8 sid);
+
+/**
+ * spmi_command_wakeup() - sends WAKEUP command to the specified slave
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ *
+ * The Wakeup command causes the Slave to move from the SLEEP state to
+ * the ACTIVE state.
+ *
+ * Returns
+ * -EINVAL for invalid slave identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+extern int spmi_command_wakeup(struct spmi_controller *ctrl, u8 sid);
+
+/**
+ * spmi_command_shutdown() - sends SHUTDOWN command to the specified slave
+ * @ctrl: SPMI controller.
+ * @sid: slave identifier.
+ *
+ * The Shutdown command causes the Slave to enter the SHUTDOWN state.
+ *
+ * Returns
+ * -EINVAL for invalid slave identifier.
+ * -EPERM if the SPMI transaction is denied due to permission issues.
+ * -EIO if the SPMI transaction fails (parity errors, etc).
+ * -ETIMEDOUT if the SPMI transaction times out.
+ */
+extern int spmi_command_shutdown(struct spmi_controller *ctrl, u8 sid);
+#endif