@@ -176,4 +176,6 @@ source "drivers/powercap/Kconfig"
source "drivers/mcb/Kconfig"
+source "drivers/qpnp/Kconfig"
+
endmenu
@@ -158,3 +158,4 @@ obj-$(CONFIG_NTB) += ntb/
obj-$(CONFIG_FMC) += fmc/
obj-$(CONFIG_POWERCAP) += powercap/
obj-$(CONFIG_MCB) += mcb/
+obj-y += qpnp/
new file mode 100644
@@ -0,0 +1,12 @@
+config QPNP_SPMI
+ tristate "Qualcomm PMIC QPNP driver"
+ depends on ARCH_QCOM
+ select REGMAP_SPMI
+ help
+ This enables basic support for the Qualcomm QPNP PMICs.
+ These PMICs are currently used with the Snapdragon 800 series of
+ SoCs. Note, that this will only be useful paired with descriptions
+ of the independent functions as children nodes in the device tree.
+
+ Say M here if you want to include support for the PM8x41 series as a
+ module. The module will be called "qpnp-spmi".
new file mode 100644
@@ -0,0 +1 @@
+obj-$(CONFIG_QPNP_SPMI) += qpnp-bus.o qpnp-spmi.o
new file mode 100644
@@ -0,0 +1,475 @@
+/* Copyright (c) 2014, The Linux Foundation. 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/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/qpnp.h>
+#include <linux/slab.h>
+
+#define QPNP_RESOURCE_SIZE 256
+
+static inline struct qpnp_driver *to_qpnp_driver(struct device_driver *dd)
+{
+ return container_of(dd, struct qpnp_driver, driver);
+}
+
+static void qpnp_device_release(struct device *dev)
+{
+ struct qpnp_device *qdev = to_qpnp_device(dev);
+
+ of_device_node_put(dev);
+ kfree(qdev->resource);
+ kfree(qdev);
+}
+
+static int qpnp_device_match(struct device *dev, struct device_driver *dd)
+{
+ if (of_driver_match_device(dev, dd))
+ return 1;
+
+ return 0;
+}
+
+static int qpnp_driver_probe(struct device *dev)
+{
+ const struct qpnp_driver *qdrv = to_qpnp_driver(dev->driver);
+ struct qpnp_device *qdev = to_qpnp_device(dev);
+
+ return qdrv->probe(qdev);
+}
+
+static int qpnp_driver_remove(struct device *dev)
+{
+ const struct qpnp_driver *qdrv = to_qpnp_driver(dev->driver);
+ struct qpnp_device *qdev = to_qpnp_device(dev);
+
+ qdrv->remove(qdev);
+ return 0;
+}
+
+static struct bus_type qpnp_bus_type = {
+ .name = "qpnp",
+ .match = qpnp_device_match,
+ .probe = qpnp_driver_probe,
+ .remove = qpnp_driver_remove,
+};
+
+/**
+ * qpnp_set_device_name - Use the device node data to assign a unique name
+ * @dev: pointer to device structure that is linked to a device tree node
+ *
+ * This routine will first try using the reg property value to derive a
+ * unique name. As a last resort it will use the node name followed by
+ * a unique number.
+ */
+static void qpnp_set_device_name(struct device *dev, u8 sid)
+{
+ static atomic_t bus_no_reg_magic;
+ struct device_node *np = dev->of_node;
+ const __be32 *reg;
+ const __be32 *addrp = NULL;
+ u64 addr;
+
+ reg = of_get_property(np, "reg", NULL);
+ if (reg) {
+ addrp = of_get_address(np, 0, NULL, NULL);
+ if (addrp)
+ addr = of_read_number(addrp, 1);
+ }
+
+ if (!addrp)
+ /*
+ * No BusID, use the np name and add a globally
+ * incremented counter (and pray...)
+ */
+ addr = atomic_add_return(1, &bus_no_reg_magic);
+
+ dev_set_name(dev, "%x.%02x.%s", sid, (u8) addr, np->name);
+}
+
+/**
+ * qpnp_device_alloc() - Allocate a new QPNP device
+ *
+ * Allocate a new QPNP device, fills QPNP device members and set device name.
+ */
+static struct qpnp_device *qpnp_device_alloc(struct device_node *np,
+ struct device *parent, u8 sid,
+ struct regmap *map)
+{
+ struct qpnp_device *qdev;
+
+ qdev = kzalloc(sizeof(*qdev), GFP_KERNEL);
+ if (!qdev)
+ return NULL;
+
+ qdev->sid = sid;
+ qdev->map = map;
+ qdev->dev.of_node = of_node_get(np);
+ qdev->dev.parent = parent;
+ qdev->dev.release = qpnp_device_release;
+ qdev->dev.bus = &qpnp_bus_type;
+
+ qpnp_set_device_name(&qdev->dev, sid);
+
+ return qdev;
+}
+
+/**
+ * qpnp_index_to_resource - Parse device tree address and return as resource
+ */
+static int
+qpnp_index_to_resource(struct device *dev, int index, struct resource *res)
+{
+ struct device_node *np = dev->of_node;
+ const __be32 *addrp;
+ u64 addr;
+ const char *name = NULL;
+
+ addrp = of_get_address(np, index, NULL, NULL);
+ if (addrp == NULL)
+ return -EINVAL;
+
+ addr = of_read_number(addrp, 1);
+
+ if (addr == OF_BAD_ADDR)
+ return -EINVAL;
+
+ /* Get optional "reg-names" property to add a name to a resource */
+ of_property_read_string_index(np, "reg-names", index, &name);
+
+ /* peripheral id is 8bits */
+ addr &= 0xff;
+ addr <<= 8;
+
+ res->start = addr;
+ res->end = addr + QPNP_RESOURCE_SIZE - 1;
+ res->flags = IORESOURCE_REG;
+ res->name = name ? name : np->full_name;
+
+ return 0;
+}
+
+/**
+ * qpnp_device_add_resources() - Allocate and add resources for QPNP device
+ * @qdev: qpnp device pointer
+ */
+static int qpnp_device_add_resources(struct qpnp_device *qdev)
+{
+ struct device_node *np = qdev->dev.of_node;
+ struct resource *res;
+ int ret, idx, num_resources = 0;
+
+ while (of_get_address(np, num_resources, NULL, NULL) != NULL)
+ num_resources++;
+
+ if (!num_resources)
+ return 0;
+
+ res = kcalloc(num_resources, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ qdev->num_resources = num_resources;
+ qdev->resource = res;
+
+ /* Populate the resource table */
+ for (idx = 0; idx < num_resources; idx++, res++) {
+ ret = qpnp_index_to_resource(&qdev->dev, idx, res);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ kfree(qdev->resource);
+ return ret;
+}
+
+/**
+ * qpnp_device_add - Alloc, initialize and register an device
+ * @np: pointer to node to create device for
+ * @parent: Linux device model parent device.
+ * @sid: Slave ID
+ *
+ * Returns pointer to created qpnp device, or NULL if a device was not
+ * registered.
+ */
+static struct qpnp_device *qpnp_device_add(struct device_node *np,
+ struct device *parent, u8 sid,
+ struct regmap *map)
+{
+ struct qpnp_device *qdev;
+ int ret;
+
+ qdev = qpnp_device_alloc(np, parent, sid, map);
+ if (!qdev)
+ return NULL;
+
+ ret = qpnp_device_add_resources(qdev);
+ if (ret)
+ goto fail;
+
+ ret = device_register(&qdev->dev);
+ if (ret)
+ goto fail;
+
+ return qdev;
+
+fail:
+ put_device(&qdev->dev);
+ return NULL;
+}
+
+static int qpnp_device_remove(struct device *dev, void *unused)
+{
+ device_unregister(dev);
+
+ return 0;
+}
+
+/**
+ * qpnp_node_create() - Create a device for a node and its children.
+ * @np: device np of the node to instantiate
+ * @parent: parent for new device
+ * @sid: Slave ID
+ *
+ * Creates a qpnp_device for the provided device_node, and
+ * recursively create devices for all the child nodes.
+ */
+static int qpnp_node_create(struct device_node *np, struct device *parent,
+ u8 sid, struct regmap *map)
+{
+ struct device_node *child;
+ struct qpnp_device *qdev;
+ int ret = 0;
+
+ /* Make sure it has a compatible property */
+ if (!of_get_property(np, "compatible", NULL)) {
+ pr_debug("skipping %s, no compatible prop\n", np->full_name);
+ return 0;
+ }
+
+ qdev = qpnp_device_add(np, parent, sid, map);
+ if (!qdev)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(np, child) {
+ pr_debug(" create child: %s\n", child->full_name);
+ ret = qpnp_node_create(child, &qdev->dev, sid, map);
+ if (ret) {
+ of_node_put(child);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * qpnp_populate_devices() - Populate qpnp_devices from device tree data
+ * @root: parent of the first level to probe
+ * @parent: parent to hook devices from
+ * @sid: Slave ID
+ *
+ * This function walks the device tree and creates devices from nodes.
+ * It follows convention of requiring all device nodes to have a 'compatible'
+ * property, and it is suitable for creating devices which are children
+ * of the root.
+ *
+ * Returns 0 on success, < 0 on failure.
+ */
+int qpnp_populate_devices(struct device_node *root, struct device *parent,
+ u8 sid, struct regmap *map)
+{
+ struct device_node *child;
+ int ret = 0;
+
+ root = of_node_get(root);
+ if (!root)
+ return -EINVAL;
+
+ for_each_available_child_of_node(root, child) {
+ ret = qpnp_node_create(child, parent, sid, map);
+ if (ret)
+ break;
+ }
+
+ of_node_put(root);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qpnp_populate_devices);
+
+/**
+ * qpnp_remove_devices(): remove a QPNP devices
+ * @root: root to devices to be removed
+ */
+void qpnp_remove_devices(struct device *root)
+{
+ device_for_each_child(root, NULL, qpnp_device_remove);
+}
+EXPORT_SYMBOL_GPL(qpnp_remove_devices);
+
+/**
+ * __of_irq_get - Decode a node's IRQ and return it as a Linux irq number
+ * @dev: pointer to device tree node
+ * @index: zero-based index of the irq
+ *
+ * Returns Linux irq number on success, -EPROBE_DEFER if the irq domain
+ * is not yet created.
+ */
+static int __of_irq_get(struct device_node *dev, int index)
+{
+ struct of_phandle_args oirq;
+ struct irq_domain *domain;
+ int ret;
+
+ ret = of_irq_parse_one(dev, index, &oirq);
+ if (ret)
+ return ret;
+
+ domain = irq_find_host(oirq.np);
+ if (!domain)
+ return -EPROBE_DEFER;
+
+ return irq_create_of_mapping(&oirq);
+}
+
+/**
+ * qpnp_get_irq - get an IRQ for a device
+ */
+int qpnp_get_irq(struct qpnp_device *qdev, unsigned int num)
+{
+ return __of_irq_get(qdev->dev.of_node, num);
+}
+EXPORT_SYMBOL_GPL(qpnp_get_irq);
+
+/**
+ * qpnp_get_irq_byname - get an IRQ for a device by name
+ */
+int qpnp_get_irq_byname(struct qpnp_device *qdev, const char *name)
+{
+ struct device_node *np = qdev->dev.of_node;
+ int index;
+
+ if (!name)
+ return -EINVAL;
+
+ index = of_property_match_string(np, "interrupt-names", name);
+ if (index < 0)
+ return index;
+
+ return __of_irq_get(np, index);
+}
+EXPORT_SYMBOL_GPL(qpnp_get_irq_byname);
+
+/**
+ * qpnp_get_resource - get a resource for a device
+ * @dev: qpnp device
+ * @type: resource type
+ * @num: resource index
+ */
+struct resource *qpnp_get_resource(struct qpnp_device *dev,
+ unsigned int type, unsigned int num)
+{
+ struct resource *res;
+ int idx;
+
+ if (type != IORESOURCE_REG)
+ return NULL;
+
+ for (idx = 0; idx < dev->num_resources; idx++) {
+ res = &dev->resource[idx];
+
+ if (type == resource_type(res) && num-- == 0)
+ return res;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(qpnp_get_resource);
+
+/**
+ * qqpnp_get_region_byname - get a resource for a device by name
+ * @dev: qpnp device
+ * @type: resource type
+ * @name: resource name
+ */
+struct resource *qpnp_get_resource_byname(struct qpnp_device *dev,
+ unsigned int type, const char *name)
+{
+ struct resource *res;
+ int idx;
+
+ if (type != IORESOURCE_REG)
+ return NULL;
+
+ for (idx = 0; idx < dev->num_resources; idx++) {
+ res = &dev->resource[idx];
+
+ if (!res->name)
+ continue;
+
+ if (type == resource_type(res) && !strcmp(res->name, name))
+ return res;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(qpnp_get_resource_byname);
+
+/**
+ * qpnp_driver_register() - Register client driver with QPNP bus
+ * @qdrv: client driver to be associated with client-device.
+ *
+ * This API will register the client driver with the QPNP framework.
+ * It is typically called from the driver's module-init function.
+ */
+int qpnp_driver_register(struct qpnp_driver *qdrv)
+{
+ qdrv->driver.bus = &qpnp_bus_type;
+
+ return driver_register(&qdrv->driver);
+}
+EXPORT_SYMBOL_GPL(qpnp_driver_register);
+
+/**
+ * qpnp_driver_unregister() - unregister a QPNP client driver
+ * @qdrv: the driver to unregister
+ */
+void qpnp_driver_unregister(struct qpnp_driver *qdrv)
+{
+ if (qdrv)
+ driver_unregister(&qdrv->driver);
+}
+EXPORT_SYMBOL_GPL(qpnp_driver_unregister);
+
+static void __exit qpnp_exit(void)
+{
+ bus_unregister(&qpnp_bus_type);
+}
+
+module_exit(qpnp_exit);
+
+static int __init qpnp_init(void)
+{
+ return bus_register(&qpnp_bus_type);
+}
+
+postcore_initcall(qpnp_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("QPNP bus module");
+MODULE_ALIAS("qpnp");
new file mode 100644
@@ -0,0 +1,63 @@
+/* Copyright (c) 2014, The Linux Foundation. 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/kernel.h>
+#include <linux/module.h>
+#include <linux/qpnp.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+
+static const struct regmap_config qpnp_spmi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0xffff,
+};
+
+static int qpnp_spmi_probe(struct spmi_device *sdev)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spmi_ext(sdev, &qpnp_spmi_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&sdev->dev, "regmap creation failed.\n");
+ return PTR_ERR(regmap);
+ }
+
+ return qpnp_populate_devices(sdev->dev.of_node, &sdev->dev,
+ sdev->usid, regmap);
+}
+
+static void qpnp_spmi_remove(struct spmi_device *sdev)
+{
+ qpnp_remove_devices(&sdev->dev);
+}
+
+static const struct of_device_id qpnp_spmi_id_table[] = {
+ { .compatible = "qcom,qpnp-spmi" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, qpnp_spmi_id_table);
+
+static struct spmi_driver qpnp_spmi_driver = {
+ .probe = qpnp_spmi_probe,
+ .remove = qpnp_spmi_remove,
+ .driver = {
+ .name = "qpnp-spmi",
+ .of_match_table = qpnp_spmi_id_table,
+ },
+};
+
+module_spmi_driver(qpnp_spmi_driver);
+
+MODULE_DESCRIPTION("Qualcomm PMIC QPNP driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spmi:qpnp-spmi");
new file mode 100644
@@ -0,0 +1,83 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. 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_QPNP_H
+#define _LINUX_QPNP_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+/**
+ * struct qpnp_device - Basic representation of an QPNP device
+ * @map: regmap to be used by driver to access device registers
+ * @dev: driver model representation of the device
+ * @sid: this devices' Unique Slave IDentifier
+ * @num_resources: number of memory resources associated to device
+ * @resource: memory resources asocuated to device
+ */
+struct qpnp_device {
+ struct device dev;
+ struct regmap *map;
+ u8 sid;
+ u32 num_resources;
+ struct resource *resource;
+};
+
+
+static inline struct qpnp_device *to_qpnp_device(struct device *d)
+{
+ return container_of(d, struct qpnp_device, dev);
+}
+
+static inline void *qpnp_get_drvdata(const struct qpnp_device *qdev)
+{
+ return dev_get_drvdata(&qdev->dev);
+}
+
+static inline void qpnp_set_drvdata(struct qpnp_device *qdev, void *data)
+{
+ dev_set_drvdata(&qdev->dev, data);
+}
+
+int qpnp_populate_devices(struct device_node *root, struct device *parent,
+ u8 sid, struct regmap *map);
+void qpnp_remove_devices(struct device *root);
+
+struct resource *qpnp_get_resource(struct qpnp_device *dev, unsigned int type,
+ unsigned int num);
+struct resource *qpnp_get_resource_byname(struct qpnp_device *dev,
+ unsigned int type, const char *name);
+
+int qpnp_get_irq(struct qpnp_device *qdev, unsigned int num);
+int qpnp_get_irq_byname(struct qpnp_device *qdev, const char *name);
+
+/**
+ * struct qpnp_driver - QPNP slave device driver
+ * @driver: QPNP device drivers should initialize name and owner field of
+ * this structure
+ * @probe: binds this driver to a QPNP device
+ * @remove: unbinds this driver from the QPNP device
+ */
+struct qpnp_driver {
+ struct device_driver driver;
+ int (*probe)(struct qpnp_device *qdev);
+ void (*remove)(struct qpnp_device *qdev);
+};
+
+int qpnp_driver_register(struct qpnp_driver *qdrv);
+void qpnp_driver_unregister(struct qpnp_driver *qdrv);
+
+#define module_qpnp_driver(__qpnp_driver) \
+ module_driver(__qpnp_driver, qpnp_driver_register, \
+ qpnp_driver_unregister)
+
+#endif