of: Add Device Tree support for SPMI

This change adds SPMI Device Tree parsing. The
of_spmi_register_devices() API should be called from the probe()
routine of each SPMI controller to parse the subtree and add the
respective SPMI devices.

The SPMI subtree is nested up to two levels deep. The first level
is the most basic and treats the address as the SPMI slave ID.
This should be used for simple devices that has no notion of
segmented SPMI address spaces.

An optional second level specifies the address as an offset
within the outer layer's slave ID. This is used to specify
multiple devices on the same slave ID that have different address
ranges. In fact, it's reasonable to specify any number of address
ranges at this level.

Devices can also specify any number of interrupts that's decoding
is done by an external interrupt device.

Sections of this code were taken from drivers/of/platform.c.

Change-Id: Ib9f06764a9bd85e3b2aab43b72aa7132885aa044
Signed-off-by: Michael Bohan <mbohan@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/spmi/msm-spmi.txt b/Documentation/devicetree/bindings/spmi/msm-spmi.txt
new file mode 100644
index 0000000..fa91514
--- /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 : <a b c> 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: <a b> 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 : <a b c> 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 d06a637..a306357 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -75,4 +75,10 @@
 	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 f7861ed..2087c5e 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -10,3 +10,4 @@
 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 0000000..9f2a396
--- /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 <linux/spmi.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_spmi.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+/**
+ * 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 0000000..d07ce63
--- /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 <linux/spmi.h>
+#include <linux/of_irq.h>
+
+#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 */