diff --git a/Documentation/devicetree/bindings/spmi/msm-spmi.txt b/Documentation/devicetree/bindings/spmi/msm-spmi.txt
new file mode 100644
index 00000000000..fa9151484ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/spmi/msm-spmi.txt
@@ -0,0 +1,72 @@
+* SPMI
+
+The spmi Device Tree support interprets up to two levels of Device Tree
+topology. The first level is required and specifies only a slave address.
+The second level is optional and allows for the specification of different
+offsets within the same 16-bit address space underneath a particular SPMI
+slave ID. Within the second level, any number of address ranges can be
+associated with a particular device within that 16-bit range.
+
+First level
+
+Required properites :
+
+ - reg: SPMI Slave ID (0-15) with no size cell.
+ - compatible : "qcom," prefixed string to match against the driver.
+
+Recommended properties :
+
+ - interrupts : where a is the slave ID, b is the peripheral ID,
+ c is the device interrupt number (0-7). Each device supports any arbitrary
+ number of interrupts.
+ - interrupt-parent : the phandle for the interrupt controller that
+ services interrupts for this device.
+
+Second level
+
+Required properties :
+ - spmi-dev-container: Used by the parser to understand that this is the second
+ level of the tree.
+ - reg: where a is < 65536 and b is a size. Each device supports an
+ arbitrary number of address ranges.
+ - compatible : "qcom," prefixed string to match against the driver.
+
+Recommended properties :
+
+ - interrupts : where a is the slave ID, b is is the peripheral ID,
+ c is the device interrupt number (0-7). Each device supports any arbitrary
+ number of interrupts.
+ - interrupt-parent : the phandle for the interrupt controller that
+ services interrupts for this device.
+
+Example:
+
+/ {
+ qcom,spmi@fc4c0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&qpnpint>;
+ pmic8941@d {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xd>;
+ spmi-dev-container;
+
+ coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x4000>;
+ interrupts = <0xd 0x28 0x6 0xd 0x28 0x3>;
+
+ };
+ pon@800 {
+ compatible = "qcom,qpnp-pon";
+ reg = <0x800 0x4000>;
+ };
+ };
+ customer_dev@2 {
+ compatible = "qcom,qpnp-pon";
+ reg = <0x2>;
+ interrupts = <0x2 0x08 0x1 0x2 0x8 0x3>;
+ };
+ };
+};
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index d06a6374ed6..a306357c78f 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -75,4 +75,10 @@ config OF_PCI
help
OpenFirmware PCI bus accessors
+config OF_SPMI
+ def_tristate SPMI
+ depends on SPMI
+ help
+ OpenFirmware SPMI bus accessors
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index f7861ed2f28..2087c5e25e9 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_OF_NET) += of_net.o
obj-$(CONFIG_OF_SPI) += of_spi.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
+obj-$(CONFIG_OF_SPMI) += of_spmi.o
diff --git a/drivers/of/of_spmi.c b/drivers/of/of_spmi.c
new file mode 100644
index 00000000000..9f2a396cdc4
--- /dev/null
+++ b/drivers/of/of_spmi.c
@@ -0,0 +1,160 @@
+/* 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * Allocate resources for a child of a spmi-container node.
+ */
+static int of_spmi_allocate_resources(struct spmi_controller *ctrl,
+ struct spmi_boardinfo *info,
+ struct device_node *node,
+ uint32_t num_reg)
+{
+ int i, num_irq = 0;
+ uint64_t size;
+ uint32_t flags;
+ struct resource *res;
+ const __be32 *addrp;
+ struct of_irq oirq;
+
+ while (of_irq_map_one(node, num_irq, &oirq) == 0)
+ num_irq++;
+
+ if (num_irq || num_reg) {
+ res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ info->num_resources = num_reg + num_irq;
+ info->resource = res;
+ for (i = 0; i < num_reg; i++, res++) {
+ /* Addresses are always 16 bits */
+ addrp = of_get_address(node, i, &size, &flags);
+ BUG_ON(!addrp);
+ res->start = be32_to_cpup(addrp);
+ res->end = res->start + size - 1;
+ res->flags = flags;
+ }
+ WARN_ON(of_irq_to_resource_table(node, res, num_irq) !=
+ num_irq);
+ }
+
+ return 0;
+}
+
+static int of_spmi_create_device(struct spmi_controller *ctrl,
+ struct spmi_boardinfo *info,
+ struct device_node *node)
+{
+ void *result;
+ int rc;
+
+ rc = of_modalias_node(node, info->name, sizeof(info->name));
+ if (rc < 0) {
+ dev_err(&ctrl->dev, "of_spmi modalias failure on %s\n",
+ node->full_name);
+ return rc;
+ }
+
+ info->of_node = of_node_get(node);
+ result = spmi_new_device(ctrl, info);
+
+ if (result == NULL) {
+ dev_err(&ctrl->dev, "of_spmi: Failure registering %s\n",
+ node->full_name);
+ of_node_put(node);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void of_spmi_walk_container_children(struct spmi_controller *ctrl,
+ struct spmi_boardinfo *info,
+ struct device_node *container)
+{
+ struct device_node *node;
+ uint64_t size;
+ uint32_t flags, num_reg = 0;
+ int rc;
+
+ for_each_child_of_node(container, node) {
+ /*
+ * We can't use of_address_to_resource here since it includes
+ * address translation; and address translation assumes that no
+ * parent buses have a size-cell of 0. But SPMI does have a
+ * size-cell of 0.
+ */
+ while (of_get_address(node, num_reg, &size, &flags) != NULL)
+ num_reg++;
+
+ rc = of_spmi_allocate_resources(ctrl, info, node, num_reg);
+ if (rc) {
+ dev_err(&ctrl->dev, "%s: unable to allocate"
+ " resources\n", __func__);
+ return;
+ }
+ rc = of_spmi_create_device(ctrl, info, node);
+ if (rc) {
+ dev_err(&ctrl->dev, "%s: unable to create device for"
+ " node %s\n", __func__, node->full_name);
+ return;
+ }
+ }
+}
+
+int of_spmi_register_devices(struct spmi_controller *ctrl)
+{
+ struct device_node *node;
+
+ /* Only register child devices if the ctrl has a node pointer set */
+ if (!ctrl->dev.of_node)
+ return -ENODEV;
+
+ for_each_child_of_node(ctrl->dev.of_node, node) {
+ struct spmi_boardinfo info = {};
+ const __be32 *slave_id;
+ int len, rc;
+
+ slave_id = of_get_property(node, "reg", &len);
+ if (!slave_id) {
+ dev_err(&ctrl->dev, "of_spmi: invalid sid "
+ "on %s\n", node->full_name);
+ continue;
+ }
+
+ info.slave_id = be32_to_cpup(slave_id);
+
+ if (of_get_property(node, "spmi-dev-container", NULL)) {
+ of_spmi_walk_container_children(ctrl, &info, node);
+ continue;
+ } else {
+ rc = of_spmi_allocate_resources(ctrl, &info, node, 0);
+ if (rc)
+ continue;
+ of_spmi_create_device(ctrl, &info, node);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(of_spmi_register_devices);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/of_spmi.h b/include/linux/of_spmi.h
new file mode 100644
index 00000000000..d07ce635adc
--- /dev/null
+++ b/include/linux/of_spmi.h
@@ -0,0 +1,23 @@
+/* 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.
+ */
+
+#include
+#include
+
+#ifdef CONFIG_OF_SPMI
+int of_spmi_register_devices(struct spmi_controller *ctrl);
+#else
+static int of_spmi_register_devices(struct spmi_controller *ctrl)
+{
+ return -ENXIO;
+}
+#endif /* CONFIG_OF_SPMI */