Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
new file mode 100644
index 0000000..84fd462
--- /dev/null
+++ b/drivers/spmi/Kconfig
@@ -0,0 +1,38 @@
+#
+# 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).
+
+if SPMI
+config SPMI_MSM_PMIC_ARB
+	tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in SPMI PMIC Arbiter interface on Qualcomm MSM family
+	  processors.
+
+	  This is required for communicating with Qualcomm PMICs and
+	  other devices that have the SPMI interface.
+
+config MSM_QPNP
+	depends on ARCH_MSMCOPPER
+	depends on OF_SPMI
+	bool "MSM QPNP"
+	help
+	  Say 'y' here to include support for the Qualcomm QPNP
+
+config MSM_QPNP_INT
+	depends on SPARSE_IRQ
+	depends on ARCH_MSMCOPPER
+	depends on OF_SPMI
+	depends on MSM_QPNP
+	bool "MSM QPNP INT"
+	help
+	  Say 'y' here to include support for the Qualcomm QPNP interrupt
+	  support. QPNP is a SPMI based PMIC implementation.
+endif
diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
new file mode 100644
index 0000000..d59a610
--- /dev/null
+++ b/drivers/spmi/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for kernel SPMI framework.
+#
+obj-$(CONFIG_SPMI)			+= spmi.o
+obj-$(CONFIG_SPMI_MSM_PMIC_ARB)		+= spmi-pmic-arb.o
+obj-$(CONFIG_MSM_QPNP)                  += qpnp.o
+obj-$(CONFIG_MSM_QPNP_INT)		+= qpnp-int.o
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
new file mode 100644
index 0000000..2998c01
--- /dev/null
+++ b/drivers/spmi/qpnp-int.c
@@ -0,0 +1,510 @@
+/* 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/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/spmi.h>
+#include <linux/radix-tree.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <mach/qpnp-int.h>
+
+#define QPNPINT_MAX_BUSSES 1
+
+/* 16 slave_ids, 256 per_ids per slave, and 8 ints per per_id */
+#define QPNPINT_NR_IRQS (16 * 256 * 8)
+
+enum qpnpint_regs {
+	QPNPINT_REG_RT_STS		= 0x10,
+	QPNPINT_REG_SET_TYPE		= 0x11,
+	QPNPINT_REG_POLARITY_HIGH	= 0x12,
+	QPNPINT_REG_POLARITY_LOW	= 0x13,
+	QPNPINT_REG_LATCHED_CLR		= 0x14,
+	QPNPINT_REG_EN_SET		= 0x15,
+	QPNPINT_REG_EN_CLR		= 0x16,
+	QPNPINT_REG_LATCHED_STS		= 0x18,
+};
+
+struct q_perip_data {
+	uint8_t type;	    /* bitmap */
+	uint8_t pol_high;   /* bitmap */
+	uint8_t pol_low;    /* bitmap */
+	uint8_t int_en;     /* bitmap */
+	uint8_t use_count;
+};
+
+struct q_irq_data {
+	uint32_t priv_d; /* data to optimize arbiter interactions */
+	struct q_chip_data *chip_d;
+	struct q_perip_data *per_d;
+	uint8_t mask_shift;
+	uint8_t spmi_slave;
+	uint16_t spmi_offset;
+};
+
+struct q_chip_data {
+	int bus_nr;
+	struct irq_domain domain;
+	struct qpnp_local_int cb;
+	struct spmi_controller *spmi_ctrl;
+	struct radix_tree_root per_tree;
+};
+
+static struct q_chip_data chip_data[QPNPINT_MAX_BUSSES] __read_mostly;
+
+/**
+ * qpnpint_encode_hwirq - translate between qpnp_irq_spec and
+ *			  hwirq representation.
+ *
+ * slave_offset = (addr->slave * 256 * 8);
+ * perip_offset = slave_offset + (addr->perip * 8);
+ * return perip_offset + addr->irq;
+ */
+static inline int qpnpint_encode_hwirq(struct qpnp_irq_spec *spec)
+{
+	uint32_t hwirq;
+
+	if (spec->slave > 15 || spec->irq > 7)
+		return -EINVAL;
+
+	hwirq = (spec->slave << 11);
+	hwirq |= (spec->per << 3);
+	hwirq |= spec->irq;
+
+	return hwirq;
+}
+/**
+ * qpnpint_decode_hwirq - translate between hwirq and
+ *			  qpnp_irq_spec representation.
+ */
+static inline int qpnpint_decode_hwirq(unsigned long hwirq,
+					struct qpnp_irq_spec *spec)
+{
+	if (hwirq > 65535)
+		return -EINVAL;
+
+	spec->slave = (hwirq >> 11) & 0xF;
+	spec->per = (hwirq >> 3) & 0xFF;
+	spec->irq = hwirq & 0x7;
+	return 0;
+}
+
+static int qpnpint_spmi_write(struct q_irq_data *irq_d, uint8_t reg,
+			      void *buf, uint32_t len)
+{
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	int rc;
+
+	if (!chip_d->spmi_ctrl)
+		return -ENODEV;
+
+	rc = spmi_ext_register_writel(chip_d->spmi_ctrl, irq_d->spmi_slave,
+				      irq_d->spmi_offset + reg, buf, len);
+	return rc;
+}
+
+static void qpnpint_irq_mask(struct irq_data *d)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	struct q_perip_data *per_d = irq_d->per_d;
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	if (chip_d->cb.mask) {
+		rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
+		if (rc)
+			pr_err("%s: decode failed on hwirq %lu\n",
+						 __func__, d->hwirq);
+		else
+			chip_d->cb.mask(chip_d->spmi_ctrl, &q_spec,
+								irq_d->priv_d);
+	}
+
+	per_d->int_en &= ~irq_d->mask_shift;
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
+					(u8 *)&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+}
+
+static void qpnpint_irq_mask_ack(struct irq_data *d)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	struct q_perip_data *per_d = irq_d->per_d;
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	pr_debug("hwirq %lu irq: %d mask: 0x%x\n", d->hwirq, d->irq,
+							irq_d->mask_shift);
+
+	if (chip_d->cb.mask) {
+		rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
+		if (rc)
+			pr_err("%s: decode failed on hwirq %lu\n",
+						 __func__, d->hwirq);
+		else
+			chip_d->cb.mask(chip_d->spmi_ctrl, &q_spec,
+								irq_d->priv_d);
+	}
+
+	per_d->int_en &= ~irq_d->mask_shift;
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
+							&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
+							&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+}
+
+static void qpnpint_irq_unmask(struct irq_data *d)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_chip_data *chip_d = irq_d->chip_d;
+	struct q_perip_data *per_d = irq_d->per_d;
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	if (chip_d->cb.unmask) {
+		rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
+		if (rc)
+			pr_err("%s: decode failed on hwirq %lu\n",
+						 __func__, d->hwirq);
+		else
+			chip_d->cb.unmask(chip_d->spmi_ctrl, &q_spec,
+								irq_d->priv_d);
+	}
+
+	per_d->int_en |= irq_d->mask_shift;
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_SET,
+					&irq_d->mask_shift, 1);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+}
+
+static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+	struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+	struct q_perip_data *per_d = irq_d->per_d;
+	int rc;
+	u8 buf[3];
+
+	pr_debug("hwirq %lu irq: %d flow: 0x%x\n", d->hwirq,
+							d->irq, flow_type);
+
+	per_d->pol_high &= ~irq_d->mask_shift;
+	per_d->pol_low &= ~irq_d->mask_shift;
+	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+		per_d->type |= irq_d->mask_shift; /* edge trig */
+		if (flow_type & IRQF_TRIGGER_RISING)
+			per_d->pol_high |= irq_d->mask_shift;
+		if (flow_type & IRQF_TRIGGER_FALLING)
+			per_d->pol_low |= irq_d->mask_shift;
+	} else {
+		if ((flow_type & IRQF_TRIGGER_HIGH) &&
+		    (flow_type & IRQF_TRIGGER_LOW))
+			return -EINVAL;
+		per_d->type &= ~irq_d->mask_shift; /* level trig */
+		if (flow_type & IRQF_TRIGGER_HIGH)
+			per_d->pol_high |= irq_d->mask_shift;
+		else
+			per_d->pol_high &= ~irq_d->mask_shift;
+	}
+
+	buf[0] = per_d->type;
+	buf[1] = per_d->pol_high;
+	buf[2] = per_d->pol_low;
+
+	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_SET_TYPE, &buf, 3);
+	if (rc)
+		pr_err("%s: spmi failure on irq %d\n",
+						 __func__, d->irq);
+	return rc;
+}
+
+static struct irq_chip qpnpint_chip = {
+	.name		= "qpnp-int",
+	.irq_mask	= qpnpint_irq_mask,
+	.irq_mask_ack	= qpnpint_irq_mask_ack,
+	.irq_unmask	= qpnpint_irq_unmask,
+	.irq_set_type	= qpnpint_irq_set_type,
+};
+
+static int qpnpint_init_irq_data(struct q_chip_data *chip_d,
+				 struct q_irq_data *irq_d,
+				 unsigned long hwirq)
+{
+	struct qpnp_irq_spec q_spec;
+	int rc;
+
+	irq_d->mask_shift = 1 << (hwirq & 0x7);
+	rc = qpnpint_decode_hwirq(hwirq, &q_spec);
+	if (rc < 0)
+		return rc;
+	irq_d->spmi_slave = q_spec.slave;
+	irq_d->spmi_offset = q_spec.per << 8;
+	irq_d->per_d->use_count++;
+	irq_d->chip_d = chip_d;
+
+	if (chip_d->cb.register_priv_data)
+		rc = chip_d->cb.register_priv_data(chip_d->spmi_ctrl, &q_spec,
+							&irq_d->priv_d);
+	return rc;
+}
+
+static struct q_irq_data *qpnpint_alloc_irq_data(
+					struct q_chip_data *chip_d,
+					unsigned long hwirq)
+{
+	struct q_irq_data *irq_d;
+	struct q_perip_data *per_d;
+
+	irq_d = kzalloc(sizeof(struct q_irq_data), GFP_KERNEL);
+	if (!irq_d)
+		return ERR_PTR(-ENOMEM);
+
+	/**
+	 * The Peripheral Tree is keyed from the slave + per_id. We're
+	 * ignoring the irq bits here since this peripheral structure
+	 * should be common for all irqs on the same peripheral.
+	 */
+	per_d = radix_tree_lookup(&chip_d->per_tree, (hwirq & ~0x7));
+	if (!per_d) {
+		per_d = kzalloc(sizeof(struct q_perip_data), GFP_KERNEL);
+		if (!per_d)
+			return ERR_PTR(-ENOMEM);
+		radix_tree_insert(&chip_d->per_tree,
+				  (hwirq & ~0x7), per_d);
+	}
+	irq_d->per_d = per_d;
+
+	return irq_d;
+}
+
+static int qpnpint_register_int(uint32_t busno, unsigned long hwirq)
+{
+	int irq, rc;
+	struct irq_domain *domain;
+	struct q_irq_data *irq_d;
+
+	pr_debug("busno = %u hwirq = %lu\n", busno, hwirq);
+
+	if (hwirq < 0 || hwirq >= 32768) {
+		pr_err("%s: hwirq %lu out of qpnp interrupt bounds\n",
+							__func__, hwirq);
+		return -EINVAL;
+	}
+
+	if (busno < 0 || busno > QPNPINT_MAX_BUSSES) {
+		pr_err("%s: invalid bus number %d\n", __func__, busno);
+		return -EINVAL;
+	}
+
+	domain = &chip_data[busno].domain;
+	irq = irq_domain_to_irq(domain, hwirq);
+
+	rc = irq_alloc_desc_at(irq, numa_node_id());
+	if (rc < 0) {
+		if (rc != -EEXIST)
+			pr_err("%s: failed to alloc irq at %d with "
+					"rc %d\n", __func__, irq, rc);
+		return rc;
+	}
+	irq_d = qpnpint_alloc_irq_data(&chip_data[busno], hwirq);
+	if (IS_ERR(irq_d)) {
+		pr_err("%s: failed to alloc irq data %d with "
+					"rc %d\n", __func__, irq, rc);
+		rc = PTR_ERR(irq_d);
+		goto register_err_cleanup;
+	}
+	rc = qpnpint_init_irq_data(&chip_data[busno], irq_d, hwirq);
+	if (rc) {
+		pr_err("%s: failed to init irq data %d with "
+					"rc %d\n", __func__, irq, rc);
+		goto register_err_cleanup;
+	}
+
+	irq_domain_register_irq(domain, hwirq);
+
+	irq_set_chip_and_handler(irq,
+			&qpnpint_chip,
+			handle_level_irq);
+	irq_set_chip_data(irq, irq_d);
+#ifdef CONFIG_ARM
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	irq_set_noprobe(irq);
+#endif
+	return 0;
+
+register_err_cleanup:
+	irq_free_desc(irq);
+	if (!IS_ERR(irq_d)) {
+		if (irq_d->per_d->use_count == 1)
+			kfree(irq_d->per_d);
+		else
+			irq_d->per_d->use_count--;
+		kfree(irq_d);
+	}
+	return rc;
+}
+
+static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
+				       struct device_node *controller,
+				       const u32 *intspec, unsigned int intsize,
+				       unsigned long *out_hwirq,
+				       unsigned int *out_type)
+{
+	struct qpnp_irq_spec addr;
+	struct q_chip_data *chip_d = d->priv;
+	int ret;
+
+	pr_debug("%s: intspec[0] 0x%x intspec[1] 0x%x intspec[2] 0x%x\n",
+				__func__, intspec[0], intspec[1], intspec[2]);
+
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize != 3)
+		return -EINVAL;
+
+	addr.irq = intspec[2] & 0x7;
+	addr.per = intspec[1] & 0xFF;
+	addr.slave = intspec[0] & 0xF;
+
+	ret = qpnpint_encode_hwirq(&addr);
+	if (ret < 0) {
+		pr_err("%s: invalid intspec\n", __func__);
+		return ret;
+	}
+	*out_hwirq = ret;
+	*out_type = IRQ_TYPE_NONE;
+
+	/**
+	 * Register the interrupt if it's not already registered.
+	 * This implies that mapping a qpnp interrupt allocates
+	 * resources.
+	 */
+	ret = qpnpint_register_int(chip_d->bus_nr, *out_hwirq);
+	if (ret && ret != -EEXIST) {
+		pr_err("%s: Cannot register hwirq %lu\n", __func__, *out_hwirq);
+		return ret;
+	}
+
+	return 0;
+}
+
+const struct irq_domain_ops qpnpint_irq_domain_ops = {
+	.dt_translate = qpnpint_irq_domain_dt_translate,
+};
+
+int qpnpint_register_controller(unsigned int busno,
+				struct qpnp_local_int *li_cb)
+{
+	if (busno >= QPNPINT_MAX_BUSSES)
+		return -EINVAL;
+	chip_data[busno].cb = *li_cb;
+	chip_data[busno].spmi_ctrl = spmi_busnum_to_ctrl(busno);
+	if (!chip_data[busno].spmi_ctrl)
+		return -ENOENT;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnpint_register_controller);
+
+int qpnpint_handle_irq(struct spmi_controller *spmi_ctrl,
+		       struct qpnp_irq_spec *spec)
+{
+	struct irq_domain *domain;
+	unsigned long hwirq, busno;
+	int irq;
+
+	pr_debug("spec slave = %u per = %u irq = %u\n",
+					spec->slave, spec->per, spec->irq);
+
+	if (!spec || !spmi_ctrl)
+		return -EINVAL;
+
+	busno = spmi_ctrl->nr;
+	if (busno >= QPNPINT_MAX_BUSSES)
+		return -EINVAL;
+
+	hwirq = qpnpint_encode_hwirq(spec);
+	if (hwirq < 0) {
+		pr_err("%s: invalid irq spec passed\n", __func__);
+		return -EINVAL;
+	}
+
+	domain = &chip_data[busno].domain;
+	irq = irq_domain_to_irq(domain, hwirq);
+
+	generic_handle_irq(irq);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnpint_handle_irq);
+
+/**
+ * This assumes that there's a relationship between the order of the interrupt
+ * controllers specified to of_irq_match() is the SPMI device topology. If
+ * this ever turns out to be a bad assumption, then of_irq_init_cb_t should
+ * be modified to pass a parameter to this function.
+ */
+static int qpnpint_cnt __initdata;
+
+int __init qpnpint_of_init(struct device_node *node, struct device_node *parent)
+{
+	struct q_chip_data *chip_d = &chip_data[qpnpint_cnt];
+	struct irq_domain *domain = &chip_d->domain;
+
+	INIT_RADIX_TREE(&chip_d->per_tree, GFP_ATOMIC);
+
+	domain->irq_base = irq_domain_find_free_range(0, QPNPINT_NR_IRQS);
+	domain->nr_irq = QPNPINT_NR_IRQS;
+	domain->of_node = of_node_get(node);
+	domain->priv = chip_d;
+	domain->ops = &qpnpint_irq_domain_ops;
+	irq_domain_add(domain);
+
+	pr_info("irq_base = %d\n", domain->irq_base);
+
+	qpnpint_cnt++;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnpint_of_init);
diff --git a/drivers/spmi/qpnp.c b/drivers/spmi/qpnp.c
new file mode 100644
index 0000000..a164efb
--- /dev/null
+++ b/drivers/spmi/qpnp.c
@@ -0,0 +1,56 @@
+/* Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * 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.
+ *
+ * Resource handling based on platform.c.
+ */
+
+#include <linux/export.h>
+#include <mach/qpnp.h>
+
+/**
+ * qpnp_get_resource - get a resource for a device
+ * @dev: qpnp device
+ * @type: resource type
+ * @num: resource index
+ */
+struct resource *qpnp_get_resource(struct spmi_device *dev,
+				   unsigned int node_idx, unsigned int type,
+				   unsigned int res_num)
+{
+	int i;
+
+	for (i = 0; i < dev->dev_node[node_idx].num_resources; i++) {
+		struct resource *r = &dev->dev_node[node_idx].resource[i];
+
+		if (type == resource_type(r) && res_num-- == 0)
+			return r;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(qpnp_get_resource);
+
+/**
+ * qpnp_get_irq - get an IRQ for a device
+ * @dev: qpnp device
+ * @num: IRQ number index
+ */
+int qpnp_get_irq(struct spmi_device *dev, unsigned int node_idx,
+					  unsigned int res_num)
+{
+	struct resource *r = qpnp_get_resource(dev, node_idx,
+						IORESOURCE_IRQ, res_num);
+
+	return r ? r->start : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(qpnp_get_irq);
+
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
new file mode 100644
index 0000000..f22b900
--- /dev/null
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -0,0 +1,722 @@
+/* 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/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/of_spmi.h>
+#include <linux/module.h>
+#include <mach/qpnp-int.h>
+
+#define SPMI_PMIC_ARB_NAME		"spmi_pmic_arb"
+
+/* PMIC Arbiter configuration registers */
+#define PMIC_ARB_VERSION		0x0000
+#define PMIC_ARB_INT_EN			0x0004
+
+/* PMIC Arbiter channel registers */
+#define PMIC_ARB_CMD(N)			(0x0800 + (0x80 * (N)))
+#define PMIC_ARB_CONFIG(N)		(0x0804 + (0x80 * (N)))
+#define PMIC_ARB_STATUS(N)		(0x0808 + (0x80 * (N)))
+#define PMIC_ARB_WDATA0(N)		(0x0810 + (0x80 * (N)))
+#define PMIC_ARB_WDATA1(N)		(0x0814 + (0x80 * (N)))
+#define PMIC_ARB_RDATA0(N)		(0x0818 + (0x80 * (N)))
+#define PMIC_ARB_RDATA1(N)		(0x081C + (0x80 * (N)))
+
+/* Interrupt Controller */
+#define SPMI_PIC_OWNER_ACC_STATUS(M, N)	(0x0000 + ((32 * (M)) + (4 * (N))))
+#define SPMI_PIC_ACC_ENABLE(N)		(0x0200 + (4 * (N)))
+#define SPMI_PIC_IRQ_STATUS(N)		(0x0600 + (4 * (N)))
+#define SPMI_PIC_IRQ_CLEAR(N)		(0x0A00 + (4 * (N)))
+
+/* Channel Status fields */
+enum pmic_arb_chnl_status {
+	PMIC_ARB_STATUS_DONE	= (1 << 0),
+	PMIC_ARB_STATUS_FAILURE	= (1 << 1),
+	PMIC_ARB_STATUS_DENIED	= (1 << 2),
+	PMIC_ARB_STATUS_DROPPED	= (1 << 3),
+};
+
+/* Command register fields */
+#define PMIC_ARB_CMD_MAX_BYTE_COUNT	8
+
+/* Command Opcodes */
+enum pmic_arb_cmd_op_code {
+	PMIC_ARB_OP_EXT_WRITEL = 0,
+	PMIC_ARB_OP_EXT_READL = 1,
+	PMIC_ARB_OP_EXT_WRITE = 2,
+	PMIC_ARB_OP_RESET = 3,
+	PMIC_ARB_OP_SLEEP = 4,
+	PMIC_ARB_OP_SHUTDOWN = 5,
+	PMIC_ARB_OP_WAKEUP = 6,
+	PMIC_ARB_OP_AUTHENTICATE = 7,
+	PMIC_ARB_OP_MSTR_READ = 8,
+	PMIC_ARB_OP_MSTR_WRITE = 9,
+	PMIC_ARB_OP_EXT_READ = 13,
+	PMIC_ARB_OP_WRITE = 14,
+	PMIC_ARB_OP_READ = 15,
+	PMIC_ARB_OP_ZERO_WRITE = 16,
+};
+
+/* Maximum number of support PMIC peripherals */
+#define PMIC_ARB_MAX_PERIPHS		256
+#define PMIC_ARB_PERIPH_ID_VALID	(1 << 15)
+#define PMIC_ARB_TIMEOUT_US		100
+
+#define PMIC_ARB_APID_MASK				0xFF
+#define PMIC_ARB_PPID_MASK				0xFFF
+/* extract PPID and APID from interrupt map in .dts config file format */
+#define PMIC_ARB_DEV_TRE_2_PPID(MAP_COMPRS_VAL)		\
+			((MAP_COMPRS_VAL) >> (20))
+#define PMIC_ARB_DEV_TRE_2_APID(MAP_COMPRS_VAL)		\
+			((MAP_COMPRS_VAL) &  PMIC_ARB_APID_MASK)
+
+/**
+ * base - base address of the PMIC Arbiter core registers.
+ * intr - base address of the SPMI interrupt control registers
+ */
+struct spmi_pmic_arb_dev {
+	struct spmi_controller	controller;
+	struct device		*dev;
+	struct device		*slave;
+	void __iomem		*base;
+	void __iomem		*intr;
+	int			pic_irq;
+	spinlock_t		lock;
+	u8			owner;
+	u8			channel;
+	u8			min_apid;
+	u8			max_apid;
+	u16			periph_id_map[PMIC_ARB_MAX_PERIPHS];
+};
+
+static u32 pmic_arb_read(struct spmi_pmic_arb_dev *dev, u32 offset)
+{
+	u32 val = readl_relaxed(dev->base + offset);
+	pr_debug("address 0x%p, val 0x%x\n", dev->base + offset, val);
+	return val;
+}
+
+static void pmic_arb_write(struct spmi_pmic_arb_dev *dev, u32 offset, u32 val)
+{
+	pr_debug("address 0x%p, val 0x%x\n", dev->base + offset, val);
+	writel_relaxed(val, dev->base + offset);
+}
+
+static int pmic_arb_wait_for_done(struct spmi_pmic_arb_dev *dev)
+{
+	u32 status = 0;
+	u32 timeout = PMIC_ARB_TIMEOUT_US;
+	u32 offset = PMIC_ARB_STATUS(dev->channel);
+
+	while (timeout--) {
+		status = pmic_arb_read(dev, offset);
+
+		if (status & PMIC_ARB_STATUS_DONE) {
+			if (status & PMIC_ARB_STATUS_DENIED) {
+				dev_err(dev->dev,
+					"%s: transaction denied (0x%x)\n",
+					__func__, status);
+				return -EPERM;
+			}
+
+			if (status & PMIC_ARB_STATUS_FAILURE) {
+				dev_err(dev->dev,
+					"%s: transaction failed (0x%x)\n",
+					__func__, status);
+				return -EIO;
+			}
+
+			if (status & PMIC_ARB_STATUS_DROPPED) {
+				dev_err(dev->dev,
+					"%s: transaction dropped (0x%x)\n",
+					__func__, status);
+				return -EIO;
+			}
+
+			return 0;
+		}
+		udelay(1);
+	}
+
+	dev_err(dev->dev, "%s: timeout, status 0x%x\n", __func__, status);
+	return -ETIMEDOUT;
+}
+
+static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
+{
+	u32 data = pmic_arb_read(dev, reg);
+
+	switch (bc & 0x3) {
+	case 3:
+		*buf++ = data & 0xff;
+		data >>= 8;
+	case 2:
+		*buf++ = data & 0xff;
+		data >>= 8;
+	case 1:
+		*buf++ = data & 0xff;
+		data >>= 8;
+	case 0:
+		*buf++ = data & 0xff;
+	default:
+		break;
+	}
+}
+
+static void
+pa_write_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
+{
+	u32 data = 0;
+
+	switch (bc & 0x3) {
+	case 3:
+		data = (buf[0]|buf[1]<<8|buf[2]<<16|buf[3]<<24);
+		break;
+	case 2:
+		data = (buf[0]|buf[1]<<8|buf[2]<<16);
+		break;
+	case 1:
+		data = (buf[0]|buf[1]<<8);
+		break;
+	case 0:
+		data = (buf[0]);
+		break;
+	default:
+		break;
+	}
+
+	pmic_arb_write(dev, reg, data);
+}
+
+/* Non-data command */
+static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	unsigned long flags;
+	u32 cmd;
+	int rc;
+
+	pr_debug("op:0x%x sid:%d\n", opc, sid);
+
+	/* Check for valid non-data command */
+	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
+		return -EINVAL;
+
+	cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	pmic_arb_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+	rc = pmic_arb_wait_for_done(pmic_arb);
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+
+	return rc;
+}
+
+static int pmic_arb_read_cmd(struct spmi_controller *ctrl,
+				u8 opc, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	unsigned long flags;
+	u32 cmd;
+	int rc;
+
+	pr_debug("op:0x%x sid:%d bc:%d addr:0x%x\n", opc, sid, bc, addr);
+
+	/* Check the opcode */
+	if (opc >= 0x60 && opc <= 0x7F)
+		opc = PMIC_ARB_OP_READ;
+	else if (opc >= 0x20 && opc <= 0x2F)
+		opc = PMIC_ARB_OP_EXT_READ;
+	else if (opc >= 0x38 && opc <= 0x3F)
+		opc = PMIC_ARB_OP_EXT_READL;
+	else
+		return -EINVAL;
+
+	cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	pmic_arb_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+	rc = pmic_arb_wait_for_done(pmic_arb);
+	if (rc)
+		goto done;
+
+	/* Read from FIFO, note 'bc' is actually number of bytes minus 1 */
+	pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), bc);
+
+	if (bc > 3)
+		pa_read_data(pmic_arb, buf + 4,
+				PMIC_ARB_RDATA1(pmic_arb->channel), bc);
+
+done:
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+	return rc;
+}
+
+static int pmic_arb_write_cmd(struct spmi_controller *ctrl,
+				u8 opc, u8 sid, u16 addr, u8 bc, u8 *buf)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	unsigned long flags;
+	u32 cmd;
+	int rc;
+
+	pr_debug("op:0x%x sid:%d bc:%d addr:0x%x\n", opc, sid, bc, addr);
+
+	/* Check the opcode */
+	if (opc >= 0x40 && opc <= 0x5F)
+		opc = PMIC_ARB_OP_WRITE;
+	else if (opc >= 0x00 && opc <= 0x0F)
+		opc = PMIC_ARB_OP_EXT_WRITE;
+	else if (opc >= 0x30 && opc <= 0x37)
+		opc = PMIC_ARB_OP_EXT_WRITEL;
+	else if (opc >= 0x80 && opc <= 0xFF)
+		opc = PMIC_ARB_OP_ZERO_WRITE;
+	else
+		return -EINVAL;
+
+	cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+
+	/* Write data to FIFOs */
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel), bc);
+
+	if (bc > 3)
+		pa_write_data(pmic_arb, buf + 4,
+				PMIC_ARB_WDATA1(pmic_arb->channel), bc);
+
+	/* Start the transaction */
+	pmic_arb_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+	rc = pmic_arb_wait_for_done(pmic_arb);
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+
+	return rc;
+}
+
+/* APID to PPID */
+static u16 get_peripheral_id(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
+{
+	return pmic_arb->periph_id_map[apid] & PMIC_ARB_PPID_MASK;
+}
+
+/* APID to PPID, returns valid flag */
+static int is_apid_valid(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
+{
+	return pmic_arb->periph_id_map[apid] & PMIC_ARB_PERIPH_ID_VALID;
+}
+
+/* PPID to APID */
+static uint32_t map_peripheral_id(struct spmi_pmic_arb_dev *pmic_arb, u16 ppid)
+{
+	int first = pmic_arb->min_apid;
+	int last = pmic_arb->max_apid;
+	int i;
+
+	/* Search table for a matching PPID */
+	for (i = first; i <= last; ++i) {
+		if ((pmic_arb->periph_id_map[i] & PMIC_ARB_PPID_MASK) == ppid)
+			return i;
+	}
+
+	dev_err(pmic_arb->dev, "Unknown ppid 0x%x\n", ppid);
+	return PMIC_ARB_MAX_PERIPHS;
+}
+
+/* Enable interrupt at the PMIC Arbiter PIC */
+static int pmic_arb_pic_enable(struct spmi_controller *ctrl,
+				struct qpnp_irq_spec *spec, uint32_t data)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	u8 apid = data & PMIC_ARB_APID_MASK;
+	unsigned long flags;
+	u32 status;
+
+	dev_dbg(pmic_arb->dev, "PIC enable, apid:0x%x, sid:0x%x, pid:0x%x\n",
+				apid, spec->slave, spec->per);
+
+	if (data < pmic_arb->min_apid || data > pmic_arb->max_apid) {
+		dev_err(pmic_arb->dev, "int enable: invalid APID %d\n", data);
+		return -EINVAL;
+	}
+
+	if (!is_apid_valid(pmic_arb, apid)) {
+		dev_err(pmic_arb->dev, "int enable: int not supported\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	status = readl_relaxed(pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (!status) {
+		writel_relaxed(0x1, pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+		/* Interrupt needs to be enabled before returning to caller */
+		wmb();
+	}
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+	return 0;
+}
+
+/* Disable interrupt at the PMIC Arbiter PIC */
+static int pmic_arb_pic_disable(struct spmi_controller *ctrl,
+				struct qpnp_irq_spec *spec, uint32_t data)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	u8 apid = data & PMIC_ARB_APID_MASK;
+	unsigned long flags;
+	u32 status;
+
+	dev_dbg(pmic_arb->dev, "PIC disable, apid:0x%x, sid:0x%x, pid:0x%x\n",
+				apid, spec->slave, spec->per);
+
+	if (data < pmic_arb->min_apid || data > pmic_arb->max_apid) {
+		dev_err(pmic_arb->dev, "int disable: invalid APID %d\n", data);
+		return -EINVAL;
+	}
+
+	if (!is_apid_valid(pmic_arb, apid)) {
+		dev_err(pmic_arb->dev, "int disable: int not supported\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pmic_arb->lock, flags);
+	status = readl_relaxed(pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (status) {
+		writel_relaxed(0x0, pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+		/* Interrupt needs to be disabled before returning to caller */
+		wmb();
+	}
+	spin_unlock_irqrestore(&pmic_arb->lock, flags);
+	return 0;
+}
+
+static irqreturn_t
+periph_interrupt(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
+{
+	u16 ppid = get_peripheral_id(pmic_arb, apid);
+	void __iomem *base = pmic_arb->intr;
+	u8 sid = (ppid >> 8) & 0x0F;
+	u8 pid = ppid & 0xFF;
+	u32 status;
+	int i;
+
+	if (!is_apid_valid(pmic_arb, apid)) {
+		dev_err(pmic_arb->dev, "unknown peripheral id 0x%x\n", ppid);
+		/* return IRQ_NONE; */
+	}
+
+	/* Read the peripheral specific interrupt bits */
+	status = readl_relaxed(base + SPMI_PIC_IRQ_STATUS(apid));
+
+	/* Clear the peripheral interrupts */
+	writel_relaxed(status, base + SPMI_PIC_IRQ_CLEAR(apid));
+	/* Interrupt needs to be cleared/acknowledged before exiting ISR */
+	mb();
+
+	dev_dbg(pmic_arb->dev,
+		"interrupt, apid:0x%x, sid:0x%x, pid:0x%x, intr:0x%x\n",
+						apid, sid, pid, status);
+
+	/* Send interrupt notification */
+	for (i = 0; status && i < 8; ++i, status >>= 1) {
+		if (status & 0x1) {
+			struct qpnp_irq_spec irq_spec = {
+				.slave = sid,
+				.per = pid,
+				.irq = i,
+			};
+			qpnpint_handle_irq(&pmic_arb->controller, &irq_spec);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+/* Peripheral interrupt handler */
+static irqreturn_t pmic_arb_periph_irq(int irq, void *dev_id)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = dev_id;
+	void __iomem *intr = pmic_arb->intr;
+	u8 ee = pmic_arb->owner;
+	u32 ret = IRQ_NONE;
+	u32 status;
+
+	int first = pmic_arb->min_apid >> 5;
+	int last = pmic_arb->max_apid >> 5;
+	int i, j;
+
+	dev_dbg(pmic_arb->dev, "Peripheral interrupt detected\n");
+
+	/* Check the accumulated interrupt status */
+	for (i = first; i <= last; ++i) {
+		status = readl_relaxed(intr + SPMI_PIC_OWNER_ACC_STATUS(ee, i));
+
+		for (j = 0; status && j < 32; ++j, status >>= 1) {
+			if (status & 0x1) {
+				u8 id = (i * 32) + j;
+				ret |= periph_interrupt(pmic_arb, id);
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* Callback to register an APID for specific slave/peripheral */
+static int pmic_arb_intr_priv_data(struct spmi_controller *ctrl,
+				struct qpnp_irq_spec *spec, uint32_t *data)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = spmi_get_ctrldata(ctrl);
+	u16 ppid = ((spec->slave & 0x0F) << 8) | (spec->per & 0xFF);
+	*data = map_peripheral_id(pmic_arb, ppid);
+	return 0;
+}
+
+static int __devinit
+spmi_pmic_arb_get_property(struct platform_device *pdev, char *pname, u32 *prop)
+{
+	int ret = of_property_read_u32(pdev->dev.of_node, pname, prop);
+
+	if (ret)
+		dev_err(&pdev->dev, "missing property: %s\n", pname);
+	else
+		pr_debug("%s = 0x%x\n", pname, *prop);
+
+	return ret;
+}
+
+static int __devinit spmi_pmic_arb_get_map_data(struct platform_device *pdev,
+					struct spmi_pmic_arb_dev *pmic_arb)
+{
+	int i;
+	int ret;
+	int map_size;
+	u32 *map_data;
+	const int map_width = sizeof(*map_data);
+	const struct device_node *of_node = pdev->dev.of_node;
+
+	/* Get size of the mapping table (in bytes) */
+	if (!of_get_property(of_node, "qcom,pmic-arb-ppid-map", &map_size)) {
+		dev_err(&pdev->dev, "missing ppid mapping table\n");
+		return -ENODEV;
+	}
+
+	/* Map size can't exceed the maximum number of peripherals */
+	if (map_size == 0 || map_size > map_width * PMIC_ARB_MAX_PERIPHS) {
+		dev_err(&pdev->dev, "map size of %d is not valid\n", map_size);
+		return -ENODEV;
+	}
+
+	map_data = kzalloc(map_size, GFP_KERNEL);
+	if (!map_data) {
+		dev_err(&pdev->dev, "can not allocate map data\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_u32_array(of_node,
+		"qcom,pmic-arb-ppid-map", map_data, map_size/sizeof(u32));
+	if (ret) {
+		dev_err(&pdev->dev, "invalid or missing property: ppid-map\n");
+		goto err;
+	};
+
+	pmic_arb->max_apid = 0;
+	pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
+
+	/* Build the mapping table from the data */
+	for (i = 0; i < map_size/sizeof(u32);) {
+		u32 map_compressed_val = map_data[i++];
+		u32 ppid = PMIC_ARB_DEV_TRE_2_PPID(map_compressed_val) ;
+		u32 apid = PMIC_ARB_DEV_TRE_2_APID(map_compressed_val) ;
+
+		if (pmic_arb->periph_id_map[apid] & PMIC_ARB_PERIPH_ID_VALID)
+			dev_warn(&pdev->dev, "duplicate APID 0x%x\n", apid);
+
+		pmic_arb->periph_id_map[apid] = ppid | PMIC_ARB_PERIPH_ID_VALID;
+
+		if (apid > pmic_arb->max_apid)
+			pmic_arb->max_apid = apid;
+
+		if (apid < pmic_arb->min_apid)
+			pmic_arb->min_apid = apid;
+	}
+
+	pr_debug("%d value(s) mapped, min:%d, max:%d\n",
+		map_size/map_width, pmic_arb->min_apid, pmic_arb->max_apid);
+
+err:
+	kfree(map_data);
+	return ret;
+}
+
+static struct qpnp_local_int spmi_pmic_arb_intr_cb = {
+	.mask = pmic_arb_pic_disable,
+	.unmask = pmic_arb_pic_enable,
+	.register_priv_data = pmic_arb_intr_priv_data,
+};
+
+static int __devinit spmi_pmic_arb_probe(struct platform_device *pdev)
+{
+	struct spmi_pmic_arb_dev *pmic_arb;
+	struct resource *mem_res;
+	u32 cell_index;
+	u32 prop;
+	int ret = 0;
+
+	pr_debug("SPMI PMIC Arbiter\n");
+
+	pmic_arb = devm_kzalloc(&pdev->dev,
+				sizeof(struct spmi_pmic_arb_dev), GFP_KERNEL);
+	if (!pmic_arb) {
+		dev_err(&pdev->dev, "can not allocate pmic_arb data\n");
+		return -ENOMEM;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "missing base memory resource\n");
+		return -ENODEV;
+	}
+
+	pmic_arb->base = devm_ioremap(&pdev->dev,
+					mem_res->start, resource_size(mem_res));
+	if (!pmic_arb->base) {
+		dev_err(&pdev->dev, "ioremap of 'base' failed\n");
+		return -ENOMEM;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "missing mem resource (interrupts)\n");
+		return -ENODEV;
+	}
+
+	pmic_arb->intr = devm_ioremap(&pdev->dev,
+					mem_res->start, resource_size(mem_res));
+	if (!pmic_arb->intr) {
+		dev_err(&pdev->dev, "ioremap of 'intr' failed\n");
+		return -ENOMEM;
+	}
+
+	pmic_arb->pic_irq = platform_get_irq(pdev, 0);
+	if (!pmic_arb->pic_irq) {
+		dev_err(&pdev->dev, "missing IRQ resource\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_irq(&pdev->dev, pmic_arb->pic_irq,
+		pmic_arb_periph_irq, IRQF_TRIGGER_HIGH, pdev->name, pmic_arb);
+	if (ret) {
+		dev_err(&pdev->dev, "request IRQ failed\n");
+		return ret;
+	}
+
+	/* Get properties from the device tree */
+	ret = spmi_pmic_arb_get_property(pdev, "cell-index", &cell_index);
+	if (ret)
+		return -ENODEV;
+
+	ret = spmi_pmic_arb_get_map_data(pdev, pmic_arb);
+	if (ret)
+		return ret;
+
+	ret = spmi_pmic_arb_get_property(pdev, "qcom,pmic-arb-ee", &prop);
+	if (ret)
+		return -ENODEV;
+	pmic_arb->owner = (u8)prop;
+
+	ret = spmi_pmic_arb_get_property(pdev, "qcom,pmic-arb-channel", &prop);
+	if (ret)
+		return -ENODEV;
+	pmic_arb->channel = (u8)prop;
+
+	pmic_arb->dev = &pdev->dev;
+	platform_set_drvdata(pdev, pmic_arb);
+	spmi_set_ctrldata(&pmic_arb->controller, pmic_arb);
+
+	spin_lock_init(&pmic_arb->lock);
+
+	pmic_arb->controller.nr = cell_index;
+	pmic_arb->controller.dev.parent = pdev->dev.parent;
+	pmic_arb->controller.dev.of_node = of_node_get(pdev->dev.of_node);
+
+	/* Callbacks */
+	pmic_arb->controller.cmd = pmic_arb_cmd;
+	pmic_arb->controller.read_cmd = pmic_arb_read_cmd;
+	pmic_arb->controller.write_cmd =  pmic_arb_write_cmd;
+
+	ret = spmi_add_controller(&pmic_arb->controller);
+	if (ret)
+		goto err_add_controller;
+
+	/* Register the interrupt enable/disable functions */
+	qpnpint_register_controller(cell_index, &spmi_pmic_arb_intr_cb);
+
+	/* Register device(s) from the device tree */
+	of_spmi_register_devices(&pmic_arb->controller);
+
+	pr_debug("PMIC Arb Version 0x%x\n",
+			pmic_arb_read(pmic_arb, PMIC_ARB_VERSION));
+
+	return 0;
+
+err_add_controller:
+	platform_set_drvdata(pdev, NULL);
+	return ret;
+}
+
+static int __devexit spmi_pmic_arb_remove(struct platform_device *pdev)
+{
+	struct spmi_pmic_arb_dev *pmic_arb = platform_get_drvdata(pdev);
+
+	free_irq(pmic_arb->pic_irq, pmic_arb);
+	platform_set_drvdata(pdev, NULL);
+	spmi_del_controller(&pmic_arb->controller);
+	return 0;
+}
+
+static struct of_device_id spmi_pmic_arb_match_table[] = {
+	{	.compatible = "qcom,spmi-pmic-arb",
+	},
+	{}
+};
+
+static struct platform_driver spmi_pmic_arb_driver = {
+	.probe		= spmi_pmic_arb_probe,
+	.remove		= __exit_p(spmi_pmic_arb_remove),
+	.driver		= {
+		.name	= SPMI_PMIC_ARB_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = spmi_pmic_arb_match_table,
+	},
+};
+
+static int __init spmi_pmic_arb_init(void)
+{
+	return platform_driver_register(&spmi_pmic_arb_driver);
+}
+postcore_initcall(spmi_pmic_arb_init);
+
+static void __exit spmi_pmic_arb_exit(void)
+{
+	platform_driver_unregister(&spmi_pmic_arb_driver);
+}
+module_exit(spmi_pmic_arb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:spmi_pmic_arb");
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
new file mode 100644
index 0000000..3f63532
--- /dev/null
+++ b/drivers/spmi/spmi.c
@@ -0,0 +1,790 @@
+/* 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>
+#include <linux/module.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->dev.platform_data = (void *)info->platform_data;
+	spmidev->num_dev_node = info->num_dev_node;
+	spmidev->dev_node = info->dev_node;
+
+	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");