aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-02-01 06:11:08 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-02-21 16:12:37 +0800
commit2a80cddf306f4486a98db08811dafe7b84f41c75 (patch)
tree4f55f0f3289fa7f569dacd40f0fa8be70220d0e1
parent6bdd0b0cd106c8e267df840ca40d1f16f6e5d74e (diff)
mfd: hi6421: add mfd core driver
Signed-off-by: Guodong Xu <guodong.xu@linaro.org>
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/hi6421-pmic-core.c163
-rw-r--r--include/linux/mfd/hi6421-pmic.h55
4 files changed, 227 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ff553babf455..6dcca6a4f465 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1146,3 +1146,11 @@ config VEXPRESS_CONFIG
help
Platform configuration infrastructure for the ARM Ltd.
Versatile Express.
+
+config MFD_HI6421_PMIC
+ tristate "Support HiSilicon Hi6421 PMU/Codec IC"
+ depends on OF
+ help
+ This driver supports HiSilicon Hi6421 power management and codec IC,
+ including regulators, codec, ADCs, Coulomb counter, etc. Memory
+ mapped I/O ports are the way of communication with it.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8b977f8045ae..952a9fd0c40d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -148,3 +148,4 @@ obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
+obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
new file mode 100644
index 000000000000..f50729a4340b
--- /dev/null
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -0,0 +1,163 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+static DEFINE_MUTEX(reg_lock_mutex);
+
+extern struct of_device_id *of_hi6421_regulator_match_tbl;
+
+/* Register Access Helpers, rmw() functions need to run locked */
+void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg,
+ u32 mask, u32 bits)
+{
+ mutex_lock(&reg_lock_mutex);
+ hi6421_pmic_write(pmic, reg, \
+ (hi6421_pmic_read(pmic, reg) & ~mask) | (mask & bits));
+ mutex_unlock(&reg_lock_mutex);
+}
+
+static int hi6421_pmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hi6421_pmic *pmic = NULL;
+ int ret = 0;
+
+ pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(dev, "cannot allocate hi6421_pmic device info\n");
+ ret = -ENOMEM;
+ goto probe_end;
+ }
+
+ /* get resources */
+ pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pmic->res) {
+ ret = -ENOMEM;
+ dev_err(dev, "hi6421_pmic_probe platform_get_resource err, \
+ ret=%d\n", ret);
+ goto error_res;
+ }
+ /* debug */
+ printk("hi6421_pmic_probe: res->start=0x%x, end=0x%x,\n name=%s, \
+ flags=0x%lx\n", pmic->res->start, pmic->res->end, \
+ pmic->res->name, pmic->res->flags);
+
+ if (!request_mem_region(pmic->res->start, resource_size(pmic->res),
+ pdev->name)) {
+ ret = -ENOMEM;
+ dev_err(dev, "cannot claim register memory\n");
+ goto error_res;
+ }
+
+ pmic->regs = ioremap(pmic->res->start, resource_size(pmic->res));
+ if (!pmic->regs) {
+ ret = -ENOMEM;
+ dev_err(dev, "cannot map register memory\n");
+ goto error_map;
+ }
+ /* debug */
+ printk("pmic->regs=0x%x\n", (u32)pmic->regs);
+
+ /* TODO: get and enable clk request */
+
+ platform_set_drvdata(pdev, pmic);
+ /* debug */
+ printk("pdev=0x%x, pmic=0x%x\n, dev=0x%x\n", (u32)pdev, (u32)pmic, \
+ (u32)dev);
+
+ /* set over-current protection debounce 8ms*/
+ hi6421_pmic_rmw(pmic, OCP_DEB_CTRL_REG, \
+ OCP_DEB_SEL_MASK | OCP_EN_DEBOUNCE_MASK | OCP_AUTO_STOP_MASK, \
+ OCP_DEB_SEL_8MS | OCP_EN_DEBOUNCE_ENABLE);
+
+ /* populate sub nodes */
+ of_platform_populate(np, of_hi6421_regulator_match_tbl, NULL, dev);
+
+ goto probe_end;
+
+/* TODO: handle clk request error */
+/* error_clk:
+ * iounmap(pmic->regs);
+ */
+error_map:
+ release_mem_region(pmic->res->start, resource_size(pmic->res));
+error_res:
+ kfree(pmic);
+probe_end:
+ return ret;
+}
+
+static int hi6421_pmic_remove(struct platform_device *pdev)
+{
+ struct hi6421_pmic *pmic = platform_get_drvdata(pdev);
+
+ iounmap(pmic->regs);
+ release_mem_region(pmic->res->start, resource_size(pmic->res));
+ kfree(pmic);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct of_device_id of_hi6421_pmic_match_tbl[] = {
+ {
+ .compatible = "hisilicon,hi6421-pmic",
+ },
+ { /* end */ }
+};
+
+static struct platform_driver hi6421_pmic_driver = {
+ .driver = {
+ .name = "hi6421_pmic",
+ .owner = THIS_MODULE,
+ .of_match_table = of_hi6421_pmic_match_tbl,
+ },
+ .probe = hi6421_pmic_probe,
+ .remove = hi6421_pmic_remove,
+};
+
+static int __init hi6421_pmic_init(void)
+{
+ return platform_driver_register(&hi6421_pmic_driver);
+}
+module_init(hi6421_pmic_init);
+
+static void __exit hi6421_pmic_exit(void)
+{
+ platform_driver_unregister(&hi6421_pmic_driver);
+}
+module_exit(hi6421_pmic_exit);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 PMIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/hi6421-pmic.h b/include/linux/mfd/hi6421-pmic.h
new file mode 100644
index 000000000000..51ddd6ac8bd9
--- /dev/null
+++ b/include/linux/mfd/hi6421-pmic.h
@@ -0,0 +1,55 @@
+/*
+ * Header file for device driver Hi6421 PMIC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (C) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define OCP_DEB_CTRL_REG (0x51)
+#define OCP_DEB_SEL_MASK (0x0C)
+#define OCP_DEB_SEL_8MS (0x00)
+#define OCP_DEB_SEL_16MS (0x04)
+#define OCP_DEB_SEL_32MS (0x08)
+#define OCP_DEB_SEL_64MS (0x0C)
+#define OCP_EN_DEBOUNCE_MASK (0x02)
+#define OCP_EN_DEBOUNCE_ENABLE (0x02)
+#define OCP_AUTO_STOP_MASK (0x01)
+#define OCP_AUTO_STOP_ENABLE (0x01)
+#define HI6421_REGS_ENA_PROTECT_TIME (100) /* in microseconds */
+#define HI6421_ECO_MODE_ENABLE (1)
+#define HI6421_ECO_MODE_DISABLE (0)
+
+struct hi6421_pmic {
+ struct resource *res;
+ struct device *dev;
+ void __iomem *regs;
+ spinlock_t lock;
+};
+
+/* Register Access Helpers */
+static inline u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg)
+{
+ return readl(pmic->regs + (reg << 2));
+}
+
+static inline void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val)
+{
+ writel(val, pmic->regs + (reg << 2));
+}
+
+void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg, u32 mask, u32 bits);