From: Mayulong <mayulo...@huawei.com>

Add the SPMI controller code and the MFD/regulator drivers needed
to support the PMIC used at the Hikey970 board.

[mchehab+hua...@kernel.org: keep just the sources, removing Kbuild changes]

Signed-off-by: Mayulong <mayulo...@huawei.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+hua...@kernel.org>
---
 drivers/mfd/hisi_pmic_spmi.c            | 759 ++++++++++++++++++++++++
 drivers/regulator/hisi_regulator_spmi.c | 741 +++++++++++++++++++++++
 drivers/spmi/hisi-spmi-controller.c     | 390 ++++++++++++
 include/linux/mfd/hisi_pmic.h           | 165 ++++++
 4 files changed, 2055 insertions(+)
 create mode 100644 drivers/mfd/hisi_pmic_spmi.c
 create mode 100644 drivers/regulator/hisi_regulator_spmi.c
 create mode 100644 drivers/spmi/hisi-spmi-controller.c
 create mode 100644 include/linux/mfd/hisi_pmic.h

diff --git a/drivers/mfd/hisi_pmic_spmi.c b/drivers/mfd/hisi_pmic_spmi.c
new file mode 100644
index 000000000000..6bb0bc4b203b
--- /dev/null
+++ b/drivers/mfd/hisi_pmic_spmi.c
@@ -0,0 +1,759 @@
+/*
+ * Device driver for regulators in HISI PMIC IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ *
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/hisi_pmic.h>
+#include <linux/irq.h>
+#include <linux/spmi.h>
+#ifndef NO_IRQ
+#define NO_IRQ       0
+#endif
+
+/* 8-bit register offset in PMIC */
+#define HISI_MASK_STATE                        0xff
+
+#define HISI_IRQ_KEY_NUM               0
+#define HISI_IRQ_KEY_VALUE             0xc0
+#define HISI_IRQ_KEY_DOWN              7
+#define HISI_IRQ_KEY_UP                        6
+
+/*#define HISI_NR_IRQ                  25*/
+#define HISI_MASK_FIELD                0xFF
+#define HISI_BITS                      8
+#define PMIC_FPGA_FLAG          1
+
+/*define the first group interrupt register number*/
+#define HISI_PMIC_FIRST_GROUP_INT_NUM        2
+
+static struct bit_info g_pmic_vbus = {0};
+#ifndef BIT
+#define BIT(x)         (0x1U << (x))
+#endif
+
+static struct hisi_pmic *g_pmic;
+static unsigned int g_extinterrupt_flag  = 0;
+static struct of_device_id of_hisi_pmic_match_tbl[] = {
+       {
+               .compatible = "hisilicon-hisi-pmic-spmi",
+       },
+       { /* end */ }
+};
+
+/*
+ * The PMIC register is only 8-bit.
+ * Hisilicon SoC use hardware to map PMIC register into SoC mapping.
+ * At here, we are accessing SoC register with 32-bit.
+ */
+u32 hisi_pmic_read(struct hisi_pmic *pmic, int reg)
+{
+       u32 ret;
+       u8 read_value = 0;
+       struct spmi_device *pdev;
+
+       if (NULL == g_pmic) {
+               pr_err(" g_pmic  is NULL\n");
+               return 0;
+       }
+
+       pdev = to_spmi_device(g_pmic->dev);
+       if (NULL == pdev) {
+               pr_err("%s:pdev get failed!\n", __func__);
+               return 0;
+       }
+
+       ret = spmi_ext_register_readl(pdev, reg, (unsigned char*)&read_value, 
1);/*lint !e734 !e732 */
+       if (ret) {
+               pr_err("%s:spmi_ext_register_readl failed!\n", __func__);
+               return ret;
+       }
+       return (u32)read_value;
+}
+EXPORT_SYMBOL(hisi_pmic_read);
+
+void hisi_pmic_write(struct hisi_pmic *pmic, int reg, u32 val)
+{
+       u32 ret;
+       struct spmi_device *pdev;
+
+       if (NULL == g_pmic) {
+               pr_err(" g_pmic  is NULL\n");
+               return;
+       }
+
+       pdev = to_spmi_device(g_pmic->dev);
+       if (NULL == pdev) {
+               pr_err("%s:pdev get failed!\n", __func__);
+               return;
+       }
+
+       ret = spmi_ext_register_writel(pdev, reg, (unsigned char*)&val, 
1);/*lint !e734 !e732 */
+       if (ret) {
+               pr_err("%s:spmi_ext_register_writel failed!\n", __func__);
+               return ;
+       }
+}
+EXPORT_SYMBOL(hisi_pmic_write);
+
+#ifdef CONFIG_HISI_DIEID
+u32 hisi_pmic_read_sub_pmu(u8 sid, int reg)
+{
+       u32 ret;
+       u8 read_value = 0;
+       struct spmi_device *pdev;
+
+       if(strstr(saved_command_line, "androidboot.swtype=factory"))
+       {
+               if (NULL == g_pmic) {
+                       pr_err(" g_pmic  is NULL\n");
+                       return -1;/*lint !e570 */
+               }
+
+               pdev = to_spmi_device(g_pmic->dev);
+               if (NULL == pdev) {
+                       pr_err("%s:pdev get failed!\n", __func__);
+                       return -1;/*lint !e570 */
+               }
+
+               ret = spmi_ext_register_readl(pdev->ctrl, sid, reg, (unsigned 
char*)&read_value, 1);/*lint !e734 !e732 */
+               if (ret) {
+                       pr_err("%s:spmi_ext_register_readl failed!\n", 
__func__);
+                       return ret;
+               }
+               return (u32)read_value;
+       }
+       return  0;
+}
+EXPORT_SYMBOL(hisi_pmic_read_sub_pmu);
+
+void hisi_pmic_write_sub_pmu(u8 sid, int reg, u32 val)
+{
+       u32 ret;
+       struct spmi_device *pdev;
+       if(strstr(saved_command_line, "androidboot.swtype=factory"))
+       {
+               if (NULL == g_pmic) {
+                       pr_err(" g_pmic  is NULL\n");
+                       return;
+               }
+
+               pdev = to_spmi_device(g_pmic->dev);
+               if (NULL == pdev) {
+                       pr_err("%s:pdev get failed!\n", __func__);
+                       return;
+               }
+
+               ret = spmi_ext_register_writel(pdev->ctrl, sid, reg, (unsigned 
char*)&val, 1);/*lint !e734 !e732 */
+               if (ret) {
+                       pr_err("%s:spmi_ext_register_writel failed!\n", 
__func__);
+                       return ;
+               }
+       }
+
+       return ;
+}
+EXPORT_SYMBOL(hisi_pmic_write_sub_pmu);
+#endif
+
+void hisi_pmic_rmw(struct hisi_pmic *pmic, int reg,
+                    u32 mask, u32 bits)
+{
+       u32 data;
+       unsigned long flags;
+
+       if (NULL == g_pmic) {
+               pr_err(" g_pmic  is NULL\n");
+               return;
+       }
+
+       spin_lock_irqsave(&g_pmic->lock, flags);
+       data = hisi_pmic_read(pmic, reg) & ~mask;
+       data |= mask & bits;
+       hisi_pmic_write(pmic, reg, data);
+       spin_unlock_irqrestore(&g_pmic->lock, flags);
+}
+EXPORT_SYMBOL(hisi_pmic_rmw);
+
+unsigned int hisi_pmic_reg_read(int addr)
+{
+       return (unsigned int)hisi_pmic_read(g_pmic, addr);
+}
+EXPORT_SYMBOL(hisi_pmic_reg_read);
+
+void hisi_pmic_reg_write(int addr, int val)
+{
+       hisi_pmic_write(g_pmic, addr, val);
+}
+EXPORT_SYMBOL(hisi_pmic_reg_write);
+
+void hisi_pmic_reg_write_lock(int addr, int val)
+{
+       unsigned long flags;
+
+       if (NULL == g_pmic) {
+               pr_err(" g_pmic  is NULL\n");
+               return;
+       }
+
+       spin_lock_irqsave(&g_pmic->lock, flags);
+       hisi_pmic_write(g_pmic, g_pmic->normal_lock.addr, 
g_pmic->normal_lock.val);
+       hisi_pmic_write(g_pmic, g_pmic->debug_lock.addr, 
g_pmic->debug_lock.val);
+       hisi_pmic_write(g_pmic, addr, val);
+       hisi_pmic_write(g_pmic, g_pmic->normal_lock.addr, 0);
+       hisi_pmic_write(g_pmic, g_pmic->debug_lock.addr, 0);
+       spin_unlock_irqrestore(&g_pmic->lock, flags);
+}
+
+int hisi_pmic_array_read(int addr, char *buff, unsigned int len)
+{
+       unsigned int i;
+
+       if ((len > 32) || (NULL == buff)) {
+               return -EINVAL;
+       }
+
+       /*
+        * Here is a bug in the pmu die.
+        * the coul driver will read 4 bytes,
+        * but the ssi bus only read 1 byte, and the pmu die
+        * will make sampling 1/10669us about vol cur,so the driver
+        * read the data is not the same sampling
+        */
+       for (i = 0; i < len; i++)
+       {
+               *(buff + i) = hisi_pmic_reg_read(addr+i);
+       }
+
+       return 0;
+}
+
+int hisi_pmic_array_write(int addr, char *buff, unsigned int len)
+{
+    unsigned int i;
+
+       if ((len > 32) || (NULL == buff)) {
+               return -EINVAL;
+       }
+
+       for (i = 0; i < len; i++)
+       {
+               hisi_pmic_reg_write(addr+i, *(buff + i));
+       }
+
+       return 0;
+}
+
+static irqreturn_t hisi_irq_handler(int irq, void *data)
+{
+       struct hisi_pmic *pmic = (struct hisi_pmic *)data;
+       unsigned long pending;
+       int i, offset;
+
+       for (i = 0; i < pmic->irqarray; i++) {
+               pending = hisi_pmic_reg_read((i + pmic->irq_addr.start_addr));
+               pending &= HISI_MASK_FIELD;
+               if (pending != 0) {
+                       pr_info("pending[%d]=0x%lx\n\r", i, pending);
+               }
+
+               hisi_pmic_reg_write((i + pmic->irq_addr.start_addr), pending);
+
+               /*solve powerkey order*/
+               if ((HISI_IRQ_KEY_NUM == i) && ((pending & HISI_IRQ_KEY_VALUE) 
== HISI_IRQ_KEY_VALUE)) {
+                       generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_DOWN]);
+                       generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_UP]);
+                       pending &= (~HISI_IRQ_KEY_VALUE);
+               }
+
+               if (pending) {
+                       for_each_set_bit(offset, &pending, HISI_BITS)
+                               generic_handle_irq(pmic->irqs[offset + i * 
HISI_BITS]);/*lint !e679 */
+               }
+       }
+
+       /*Handle the second group irq if analysis the second group irq from 
dtsi*/
+       if (1 == g_extinterrupt_flag){
+               for (i = 0; i < pmic->irqarray1; i++) {
+                       pending = hisi_pmic_reg_read((i + 
pmic->irq_addr1.start_addr));
+                       pending &= HISI_MASK_FIELD;
+                       if (pending != 0) {
+                               pr_info("pending[%d]=0x%lx\n\r", i, pending);
+                       }
+
+                       hisi_pmic_reg_write((i + pmic->irq_addr1.start_addr), 
pending);
+
+                       if (pending) {
+                               for_each_set_bit(offset, &pending, HISI_BITS)
+                                       generic_handle_irq(pmic->irqs[offset + 
(i+HISI_PMIC_FIRST_GROUP_INT_NUM) * HISI_BITS]);/*lint !e679 */
+                       }
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void hisi_irq_mask(struct irq_data *d)
+{
+       struct hisi_pmic *pmic = irq_data_get_irq_chip_data(d);
+       u32 data, offset;
+       unsigned long flags;
+
+       if (NULL == g_pmic) {
+               pr_err(" g_pmic  is NULL\n");
+               return;
+       }
+
+       offset = (irqd_to_hwirq(d) >> 3);
+       if (1==g_extinterrupt_flag){
+               if ( offset < HISI_PMIC_FIRST_GROUP_INT_NUM)
+                       offset += pmic->irq_mask_addr.start_addr;
+               else/*Change addr when irq num larger than 16 because interrupt 
addr is nonsequence*/
+                       offset = 
offset+(pmic->irq_mask_addr1.start_addr)-HISI_PMIC_FIRST_GROUP_INT_NUM;
+       }else{
+               offset += pmic->irq_mask_addr.start_addr;
+       }
+       spin_lock_irqsave(&g_pmic->lock, flags);
+       data = hisi_pmic_reg_read(offset);
+       data |= (1 << (irqd_to_hwirq(d) & 0x07));
+       hisi_pmic_reg_write(offset, data);
+       spin_unlock_irqrestore(&g_pmic->lock, flags);
+}
+
+static void hisi_irq_unmask(struct irq_data *d)
+{
+       struct hisi_pmic *pmic = irq_data_get_irq_chip_data(d);
+       u32 data, offset;
+       unsigned long flags;
+
+       if (NULL == g_pmic) {
+               pr_err(" g_pmic  is NULL\n");
+               return;
+       }
+
+       offset = (irqd_to_hwirq(d) >> 3);
+       if (1==g_extinterrupt_flag){
+               if ( offset < HISI_PMIC_FIRST_GROUP_INT_NUM)
+                       offset += pmic->irq_mask_addr.start_addr;
+               else
+                       offset = 
offset+(pmic->irq_mask_addr1.start_addr)-HISI_PMIC_FIRST_GROUP_INT_NUM;
+       }else{
+               offset += pmic->irq_mask_addr.start_addr;
+       }
+       spin_lock_irqsave(&g_pmic->lock, flags);
+       data = hisi_pmic_reg_read(offset);
+       data &= ~(1 << (irqd_to_hwirq(d) & 0x07)); /*lint !e502 */
+       hisi_pmic_reg_write(offset, data);
+       spin_unlock_irqrestore(&g_pmic->lock, flags);
+}
+
+static struct irq_chip hisi_pmu_irqchip = {
+       .name           = "hisi-irq",
+       .irq_mask       = hisi_irq_mask,
+       .irq_unmask     = hisi_irq_unmask,
+       .irq_disable    = hisi_irq_mask,
+       .irq_enable     = hisi_irq_unmask,
+};
+
+static int hisi_irq_map(struct irq_domain *d, unsigned int virq,
+                         irq_hw_number_t hw)
+{
+       struct hisi_pmic *pmic = d->host_data;
+
+       irq_set_chip_and_handler_name(virq, &hisi_pmu_irqchip,
+                                     handle_simple_irq, "hisi");
+       irq_set_chip_data(virq, pmic);
+       irq_set_irq_type(virq, IRQ_TYPE_NONE);
+
+       return 0;
+}
+
+static struct irq_domain_ops hisi_domain_ops = {
+       .map    = hisi_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
+/*lint -e570 -e64*/
+static int get_pmic_device_tree_data(struct device_node *np, struct hisi_pmic 
*pmic)
+{
+       int ret = 0;
+
+       /*get pmic irq num*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-num",
+                                               &(pmic->irqnum), 1);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-num property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*get pmic irq array number*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-array",
+                                               &(pmic->irqarray), 1);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-array property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*SOC_PMIC_IRQ_MASK_0_ADDR*/
+       ret = of_property_read_u32_array(np, 
"hisilicon,hisi-pmic-irq-mask-addr",
+                                               (int *)&pmic->irq_mask_addr, 2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-mask-addr property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*SOC_PMIC_IRQ0_ADDR*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-addr",
+                                               (int *)&pmic->irq_addr, 2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-addr property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-vbus",
+                                               (u32 *)&g_pmic_vbus, 2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-vbus property\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*pmic lock*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-lock",
+                                               (int *)&pmic->normal_lock, 2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-lock property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*pmic debug lock*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-debug-lock",
+                                               (int *)&pmic->debug_lock, 2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-debug-lock property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       return ret;
+}/*lint -restore*/
+
+
+/*lint -e570 -e64*/
+static int get_pmic_device_tree_data1(struct device_node *np, struct hisi_pmic 
*pmic)
+{
+       int ret = 0;
+
+       /*get pmic irq num*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-num1",
+                                               &(pmic->irqnum1), 1);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-num1 property set\n");
+               ret = -ENODEV;
+               pmic->irqnum1 = 0;
+               return ret;
+       }
+
+       /*get pmic irq array number*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-array1",
+                                               &(pmic->irqarray1), 1);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-array1 property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*SOC_PMIC_IRQ_MASK_0_ADDR*/
+       ret = of_property_read_u32_array(np, 
"hisilicon,hisi-pmic-irq-mask-addr1",
+                                               (int *)&pmic->irq_mask_addr1, 
2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-mask-addr1 property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       /*SOC_PMIC_IRQ0_ADDR*/
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-pmic-irq-addr1",
+                                               (int *)&pmic->irq_addr1, 2);
+       if (ret) {
+               pr_err("no hisilicon,hisi-pmic-irq-addr1 property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+
+       g_extinterrupt_flag = 1;
+       return ret;
+}/*lint -restore*/
+
+int hisi_get_pmic_irq_byname(unsigned int pmic_irq_list)
+{
+       if ( NULL == g_pmic ) {
+               pr_err("[%s]g_pmic is NULL\n", __func__);
+               return -1;
+       }
+
+       if (pmic_irq_list > (unsigned int)g_pmic->irqnum) {
+               pr_err("[%s]input pmic irq number is error.\n", __func__);
+               return -1;
+       }
+       pr_info("%s:g_pmic->irqs[%d]=%d\n", __func__, pmic_irq_list, 
g_pmic->irqs[pmic_irq_list]);
+       return (int)g_pmic->irqs[pmic_irq_list];
+}
+EXPORT_SYMBOL(hisi_get_pmic_irq_byname);
+
+int hisi_pmic_get_vbus_status(void)
+{
+       if (0 == g_pmic_vbus.addr)
+               return -1;
+
+       if (hisi_pmic_reg_read(g_pmic_vbus.addr) & BIT(g_pmic_vbus.bit))
+               return 1;
+
+       return 0;
+}
+EXPORT_SYMBOL(hisi_pmic_get_vbus_status);
+
+static void hisi_pmic_irq_prc(struct hisi_pmic *pmic)
+{
+       int i;
+       for (i = 0 ; i < pmic->irq_mask_addr.array; i++) {
+               hisi_pmic_write(pmic, pmic->irq_mask_addr.start_addr + i, 
HISI_MASK_STATE);
+       }
+
+       for (i = 0 ; i < pmic->irq_addr.array; i++) {
+               unsigned int pending = hisi_pmic_read(pmic, 
pmic->irq_addr.start_addr + i);
+               pr_debug("PMU IRQ address value:irq[0x%x] = 0x%x\n", 
pmic->irq_addr.start_addr + i, pending);
+               hisi_pmic_write(pmic, pmic->irq_addr.start_addr + i, 
HISI_MASK_STATE);
+       }
+
+}
+
+static void hisi_pmic_irq1_prc(struct hisi_pmic *pmic)
+{
+       int i;
+       if(1 == g_extinterrupt_flag){
+               for (i = 0 ; i < pmic->irq_mask_addr1.array; i++) {
+                       hisi_pmic_write(pmic, pmic->irq_mask_addr1.start_addr + 
i, HISI_MASK_STATE);
+               }
+
+               for (i = 0 ; i < pmic->irq_addr1.array; i++) {
+                       unsigned int pending1 = hisi_pmic_read(pmic, 
pmic->irq_addr1.start_addr + i);
+                       pr_debug("PMU IRQ address1 value:irq[0x%x] = 0x%x\n", 
pmic->irq_addr1.start_addr + i, pending1);
+                       hisi_pmic_write(pmic, pmic->irq_addr1.start_addr + i, 
HISI_MASK_STATE);
+               }
+       }
+}
+
+static int hisi_pmic_probe(struct spmi_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct hisi_pmic *pmic = NULL;
+       enum of_gpio_flags flags;
+       int ret = 0;
+       int i;
+       unsigned int fpga_flag = 0;
+       unsigned int virq;
+
+       pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+       if (!pmic) {
+               dev_err(dev, "cannot allocate hisi_pmic device info\n");
+               return -ENOMEM;
+       }
+
+       /*TODO: get pmic dts info*/
+       ret = get_pmic_device_tree_data(np, pmic);
+       if (ret) {
+               dev_err(&pdev->dev, "Error reading hisi pmic dts \n");
+               return ret;
+       }
+
+       /*get pmic dts the second group irq*/
+       ret = get_pmic_device_tree_data1(np, pmic);
+       if (ret) {
+               dev_err(&pdev->dev, "the platform don't support 
ext-interrupt.\n");
+       }
+
+       /* TODO: get and enable clk request */
+       spin_lock_init(&pmic->lock);
+
+       pmic->dev = dev;
+       g_pmic = pmic;
+       ret = of_property_read_u32_array(np, "hisilicon,pmic_fpga_flag", 
&fpga_flag, 1);
+       if (ret) {
+               pr_err("no hisilicon,pmic_fpga_flag property set\n");
+       }
+       if (PMIC_FPGA_FLAG == fpga_flag) {
+               goto after_irq_register;
+       }
+
+       pmic->gpio = of_get_gpio_flags(np, 0, &flags);
+       if (pmic->gpio < 0)
+               return pmic->gpio;
+
+       if (!gpio_is_valid(pmic->gpio))
+               return -EINVAL;
+
+       ret = gpio_request_one(pmic->gpio, GPIOF_IN, "pmic");
+       if (ret < 0) {
+               dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
+               return ret;
+       }
+
+       pmic->irq = gpio_to_irq(pmic->gpio);
+
+       /* mask && clear IRQ status */
+       hisi_pmic_irq_prc(pmic);
+       /*clear && mask the new adding irq*/
+       hisi_pmic_irq1_prc(pmic);
+
+       pmic->irqnum += pmic->irqnum1;
+
+       pmic->irqs = (unsigned int *)devm_kmalloc(dev, pmic->irqnum * 
sizeof(int), GFP_KERNEL);
+       if (!pmic->irqs) {
+               pr_err("%s:Failed to alloc memory for pmic irq number!\n", 
__func__);
+               goto irq_malloc;
+       }
+       memset(pmic->irqs, 0, pmic->irqnum);
+
+       pmic->domain = irq_domain_add_simple(np, pmic->irqnum, 0,
+                                            &hisi_domain_ops, pmic);
+       if (!pmic->domain) {
+               dev_err(dev, "failed irq domain add simple!\n");
+               ret = -ENODEV;
+               goto irq_domain;
+       }
+
+       for (i = 0; i < pmic->irqnum; i++) {
+               virq = irq_create_mapping(pmic->domain, i);
+               if (virq == NO_IRQ) {
+                       pr_debug("Failed mapping hwirq\n");
+                       ret = -ENOSPC;
+                       goto irq_create_mapping;
+               }
+               pmic->irqs[i] = virq;
+               pr_info("[%s]. pmic->irqs[%d] = %d\n", __func__, i, 
pmic->irqs[i]);
+       }
+
+       ret = request_threaded_irq(pmic->irq, hisi_irq_handler, NULL,
+                               IRQF_TRIGGER_LOW | IRQF_SHARED | 
IRQF_NO_SUSPEND,
+                                  "pmic", pmic);
+       if (ret < 0) {
+               dev_err(dev, "could not claim pmic %d\n", ret);
+               ret = -ENODEV;
+               goto request_theaded_irq;
+       }
+
+after_irq_register:
+       return 0;
+
+
+request_theaded_irq:
+irq_create_mapping:
+irq_domain:
+irq_malloc:
+       gpio_free(pmic->gpio);
+       g_pmic = NULL;
+       return ret;
+}
+
+static void hisi_pmic_remove(struct spmi_device *pdev)
+{
+
+       struct hisi_pmic *pmic = dev_get_drvdata(&pdev->dev);
+
+       free_irq(pmic->irq, pmic);
+       gpio_free(pmic->gpio);
+       devm_kfree(&pdev->dev, pmic);
+
+}
+static int hisi_pmic_suspend(struct device *dev, pm_message_t state)
+{
+       struct hisi_pmic *pmic = dev_get_drvdata(dev);
+
+       if (NULL == pmic) {
+               pr_err("%s:pmic is NULL\n", __func__);
+               return -ENOMEM;
+       }
+
+       pr_info("%s:+\n", __func__);
+       pr_info("%s:-\n", __func__);
+
+       return 0;
+}/*lint !e715 */
+
+static int hisi_pmic_resume(struct device *dev)
+{
+       struct hisi_pmic *pmic = dev_get_drvdata(dev);
+
+       if (NULL == pmic) {
+               pr_err("%s:pmic is NULL\n", __func__);
+               return -ENOMEM;
+       }
+
+       pr_info("%s:+\n", __func__);
+       pr_info("%s:-\n", __func__);
+
+       return 0;
+}
+
+MODULE_DEVICE_TABLE(spmi, pmic_spmi_id);
+static struct spmi_driver hisi_pmic_driver = {
+       .driver = {
+               .name   = "hisi_pmic",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_hisi_pmic_match_tbl,
+               .suspend = hisi_pmic_suspend,
+               .resume = hisi_pmic_resume,
+       },
+       .probe  = hisi_pmic_probe,
+       .remove = hisi_pmic_remove,
+};
+
+static int __init hisi_pmic_init(void)
+{
+       return spmi_driver_register(&hisi_pmic_driver);
+}
+
+static void __exit hisi_pmic_exit(void)
+{
+       spmi_driver_unregister(&hisi_pmic_driver);
+}
+
+
+subsys_initcall_sync(hisi_pmic_init);
+module_exit(hisi_pmic_exit);
+
+MODULE_DESCRIPTION("PMIC driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/regulator/hisi_regulator_spmi.c 
b/drivers/regulator/hisi_regulator_spmi.c
new file mode 100644
index 000000000000..941bfe32bf5b
--- /dev/null
+++ b/drivers/regulator/hisi_regulator_spmi.c
@@ -0,0 +1,741 @@
+/*
+ * Device driver for regulators in Hisi IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong...@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.
+ *
+ */
+
+#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/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hisi_pmic.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/version.h>
+#ifdef CONFIG_HISI_PMIC_DEBUG
+#include <linux/debugfs.h>
+#endif
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/spmi.h>
+
+#if 1
+#define BRAND_DEBUG(args...) pr_debug(args);
+#else
+#define BRAND_DEBUG(args...)
+#endif
+
+struct hisi_regulator_register_info {
+       u32 ctrl_reg;
+       u32 enable_mask;
+       u32 eco_mode_mask;
+       u32 vset_reg;
+       u32 vset_mask;
+};
+
+struct hisi_regulator {
+       const char *name;
+       struct hisi_regulator_register_info register_info;
+       struct timeval last_off_time;
+       u32 off_on_delay;
+       u32 eco_uA;
+       struct regulator_desc rdesc;
+       int (*dt_parse)(struct hisi_regulator *, struct spmi_device *);
+};
+
+static DEFINE_MUTEX(enable_mutex);
+struct timeval last_enabled;
+
+
+static inline struct hisi_pmic *rdev_to_pmic(struct regulator_dev *dev)
+{
+       /* regulator_dev parent to->
+        * hisi regulator platform device_dev parent to->
+        * hisi pmic platform device_dev
+        */
+       return dev_get_drvdata(rdev_get_dev(dev)->parent->parent);
+}
+
+/* helper function to ensure when it returns it is at least 'delay_us'
+ * microseconds after 'since'.
+ */
+static void ensured_time_after(struct timeval since, u32 delay_us)
+{
+       struct timeval now;
+       u64 elapsed_ns64, delay_ns64;
+       u32 actual_us32;
+
+       delay_ns64 = delay_us * NSEC_PER_USEC;
+       do_gettimeofday(&now);
+       elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since);
+       if (delay_ns64 > elapsed_ns64) {
+               actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) /
+                                                       NSEC_PER_USEC);
+               if (actual_us32 >= 1000) {
+                       mdelay(actual_us32 / 1000); /*lint !e647 */
+                       udelay(actual_us32 % 1000);
+               } else if (actual_us32 > 0) {
+                       udelay(actual_us32);
+               }
+       }
+       return;
+}
+
+static int hisi_regulator_is_enabled(struct regulator_dev *dev)
+{
+       u32 reg_val;
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+
+       reg_val = hisi_pmic_read(pmic, sreg->register_info.ctrl_reg);
+       BRAND_DEBUG("<[%s]: ctrl_reg=0x%x,enable_state=%d>\n", __func__, 
sreg->register_info.ctrl_reg,\
+                       (reg_val & sreg->register_info.enable_mask));
+
+       return ((reg_val & sreg->register_info.enable_mask) != 0);
+}
+
+static int hisi_regulator_enable(struct regulator_dev *dev)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+
+       /* keep a distance of off_on_delay from last time disabled */
+       ensured_time_after(sreg->last_off_time, sreg->off_on_delay);
+
+       BRAND_DEBUG("<[%s]: off_on_delay=%dus>\n", __func__, 
sreg->off_on_delay);
+
+       /* cannot enable more than one regulator at one time */
+       mutex_lock(&enable_mutex);
+       ensured_time_after(last_enabled, HISI_REGS_ENA_PROTECT_TIME);
+
+       /* set enable register */
+       hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+                               sreg->register_info.enable_mask,
+                               sreg->register_info.enable_mask);
+       BRAND_DEBUG("<[%s]: ctrl_reg=0x%x,enable_mask=0x%x>\n", __func__, 
sreg->register_info.ctrl_reg,\
+                       sreg->register_info.enable_mask);
+
+       do_gettimeofday(&last_enabled);
+       mutex_unlock(&enable_mutex);
+
+       return 0;
+}
+
+static int hisi_regulator_disable(struct regulator_dev *dev)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+
+       /* set enable register to 0 */
+       hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+                               sreg->register_info.enable_mask, 0);
+
+       do_gettimeofday(&sreg->last_off_time);
+
+       return 0;
+}
+
+static int hisi_regulator_get_voltage(struct regulator_dev *dev)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+       u32 reg_val, selector;
+
+       /* get voltage selector */
+       reg_val = hisi_pmic_read(pmic, sreg->register_info.vset_reg);
+       BRAND_DEBUG("<[%s]: vset_reg=0x%x>\n", __func__, 
sreg->register_info.vset_reg);
+
+       selector = (reg_val & sreg->register_info.vset_mask) >>
+                               (ffs(sreg->register_info.vset_mask) - 1);
+
+       return sreg->rdesc.ops->list_voltage(dev, selector);
+}
+
+static int hisi_regulator_set_voltage(struct regulator_dev *dev,
+                               int min_uV, int max_uV, unsigned *selector)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+       u32 vsel;
+       int ret = 0;
+
+       for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) {
+               int uV = sreg->rdesc.volt_table[vsel];
+               /* Break at the first in-range value */
+               if (min_uV <= uV && uV <= max_uV)
+                       break;
+       }
+
+       /* unlikely to happen. sanity test done by regulator core */
+       if (unlikely(vsel == sreg->rdesc.n_voltages))
+               return -EINVAL;
+
+       *selector = vsel;
+       /* set voltage selector */
+       hisi_pmic_rmw(pmic, sreg->register_info.vset_reg,
+               sreg->register_info.vset_mask,
+               vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+       BRAND_DEBUG("<[%s]: vset_reg=0x%x, vset_mask=0x%x, value=0x%x>\n", 
__func__,\
+                       sreg->register_info.vset_reg,\
+                       sreg->register_info.vset_mask,\
+                       vsel << (ffs(sreg->register_info.vset_mask) - 1)\
+                       );
+
+       return ret;
+}
+
+static unsigned int hisi_regulator_get_mode(struct regulator_dev *dev)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+       u32 reg_val;
+
+       reg_val = hisi_pmic_read(pmic, sreg->register_info.ctrl_reg);
+       BRAND_DEBUG("<[%s]: reg_val=%d, ctrl_reg=0x%x, eco_mode_mask=0x%x>\n", 
__func__, reg_val,\
+                       sreg->register_info.ctrl_reg,\
+                       sreg->register_info.eco_mode_mask\
+                  );
+
+       if (reg_val & sreg->register_info.eco_mode_mask)
+               return REGULATOR_MODE_IDLE;
+       else
+               return REGULATOR_MODE_NORMAL;
+}
+
+static int hisi_regulator_set_mode(struct regulator_dev *dev,
+                                               unsigned int mode)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+       struct hisi_pmic *pmic = rdev_to_pmic(dev);
+       u32 eco_mode;
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               eco_mode = HISI_ECO_MODE_DISABLE;
+               break;
+       case REGULATOR_MODE_IDLE:
+               eco_mode = HISI_ECO_MODE_ENABLE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set mode */
+       hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+               sreg->register_info.eco_mode_mask,
+               eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1));
+
+       BRAND_DEBUG("<[%s]: ctrl_reg=0x%x, eco_mode_mask=0x%x, value=0x%x>\n", 
__func__,\
+                       sreg->register_info.ctrl_reg,\
+                       sreg->register_info.eco_mode_mask,\
+                       eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 
1)\
+                  );
+       return 0;
+}
+
+
+unsigned int hisi_regulator_get_optimum_mode(struct regulator_dev *dev,
+                       int input_uV, int output_uV, int load_uA)
+{
+       struct hisi_regulator *sreg = rdev_get_drvdata(dev);
+
+       if ((load_uA == 0) || ((unsigned int)load_uA > sreg->eco_uA))
+               return REGULATOR_MODE_NORMAL;
+       else
+               return REGULATOR_MODE_IDLE;
+}
+
+static int hisi_dt_parse_common(struct hisi_regulator *sreg,
+                                       struct spmi_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct regulator_desc *rdesc = &sreg->rdesc;
+       unsigned int register_info[3] = {0};
+       int ret = 0;
+
+       /* parse .register_info.ctrl_reg */
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-ctrl",
+                                               register_info, 3);
+       if (ret) {
+               dev_err(dev, "no hisilicon,hisi-ctrl property set\n");
+               goto dt_parse_common_end;
+       }
+       sreg->register_info.ctrl_reg = register_info[0];
+       sreg->register_info.enable_mask = register_info[1];
+       sreg->register_info.eco_mode_mask = register_info[2];
+
+       /* parse .register_info.vset_reg */
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-vset",
+                                               register_info, 2);
+       if (ret) {
+               dev_err(dev, "no hisilicon,hisi-vset property set\n");
+               goto dt_parse_common_end;
+       }
+       sreg->register_info.vset_reg = register_info[0];
+       sreg->register_info.vset_mask = register_info[1];
+
+       /* parse .off-on-delay */
+       ret = of_property_read_u32(np, "hisilicon,hisi-off-on-delay-us",
+                                               &sreg->off_on_delay);
+       if (ret) {
+               dev_err(dev, "no hisilicon,hisi-off-on-delay-us property 
set\n");
+               goto dt_parse_common_end;
+       }
+
+       /* parse .enable_time */
+       ret = of_property_read_u32(np, "hisilicon,hisi-enable-time-us",
+                                  &rdesc->enable_time);
+       if (ret) {
+               dev_err(dev, "no hisilicon,hisi-enable-time-us property set\n");
+               goto dt_parse_common_end;
+       }
+
+       /* parse .eco_uA */
+       ret = of_property_read_u32(np, "hisilicon,hisi-eco-microamp",
+                                  &sreg->eco_uA);
+       if (ret) {
+               sreg->eco_uA = 0;
+               ret = 0;
+       }
+
+dt_parse_common_end:
+       return ret;
+}
+
+static int hisi_dt_parse_ldo(struct hisi_regulator *sreg,
+                               struct spmi_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct regulator_desc *rdesc = &sreg->rdesc;
+       unsigned int *v_table;
+       int ret = 0;
+
+       /* parse .n_voltages, and .volt_table */
+       ret = of_property_read_u32(np, "hisilicon,hisi-n-voltages",
+                                  &rdesc->n_voltages);
+       if (ret) {
+               dev_err(dev, "no hisilicon,hisi-n-voltages property set\n");
+               goto dt_parse_ldo_end;
+       }
+
+       /* alloc space for .volt_table */
+       v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
+                                                               GFP_KERNEL);
+       if (unlikely(!v_table)) {
+               ret = -ENOMEM;
+               dev_err(dev, "no memory for .volt_table\n");
+               goto dt_parse_ldo_end;
+       }
+
+       ret = of_property_read_u32_array(np, "hisilicon,hisi-vset-table",
+                                               v_table, rdesc->n_voltages);
+       if (ret) {
+               dev_err(dev, "no hisilicon,hisi-vset-table property set\n");
+               goto dt_parse_ldo_end1;
+       }
+       rdesc->volt_table = v_table;
+
+       /* parse hisi regulator's dt common part */
+       ret = hisi_dt_parse_common(sreg, pdev);
+       if (ret) {
+               dev_err(dev, "failure in hisi_dt_parse_common\n");
+               goto dt_parse_ldo_end1;
+       }
+
+       return ret;
+
+dt_parse_ldo_end1:
+dt_parse_ldo_end:
+       return ret;
+}
+
+static struct regulator_ops hisi_ldo_rops = {
+       .is_enabled = hisi_regulator_is_enabled,
+       .enable = hisi_regulator_enable,
+       .disable = hisi_regulator_disable,
+       .list_voltage = regulator_list_voltage_table,
+       .get_voltage = hisi_regulator_get_voltage,
+       .set_voltage = hisi_regulator_set_voltage,
+       .get_mode = hisi_regulator_get_mode,
+       .set_mode = hisi_regulator_set_mode,
+       .get_optimum_mode = hisi_regulator_get_optimum_mode,
+};
+
+static const struct hisi_regulator hisi_regulator_ldo = {
+       .rdesc = {
+       .ops = &hisi_ldo_rops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+               },
+       .dt_parse = hisi_dt_parse_ldo,
+};
+
+static struct of_device_id of_hisi_regulator_match_tbl[] = {
+       {
+               .compatible = "hisilicon-hisi-ldo",
+               .data = &hisi_regulator_ldo,
+       },
+       { /* end */ }
+};
+
+#ifdef CONFIG_HISI_PMIC_DEBUG
+extern void get_current_regulator_dev(struct seq_file *s);
+extern void set_regulator_state(char *ldo_name, int value);
+extern void get_regulator_state(char *ldo_name);
+extern int set_regulator_voltage(char *ldo_name, unsigned int vol_value);
+
+u32 pmu_atoi(char *s)
+{
+       char *p = s;
+       char c;
+       u64 ret = 0;
+       if (s == NULL)
+               return 0;
+       while ((c = *p++) != '\0') {
+               if ('0' <= c && c <= '9') {
+                       ret *= 10;
+                       ret += (u64)((unsigned char)c - '0');
+                       if (ret > U32_MAX)
+                               return 0;
+               } else {
+                       break;
+               }
+       }
+       return (u32)ret;
+}
+static int dbg_hisi_regulator_show(struct seq_file *s, void *data)
+{
+       seq_printf(s, "\n\r");
+       seq_printf(s, "%-13s %-15s %-15s %-15s %-15s\n\r",
+                       "LDO_NAME", "ON/OFF", "Use_count", "Open_count", 
"Always_on");
+       seq_printf(s, "-----------------------------------------"
+                       "-----------------------------------------------\n\r");
+       get_current_regulator_dev(s);
+       return 0;
+}
+
+static int dbg_hisi_regulator_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_hisi_regulator_show, inode->i_private);
+}
+
+static const struct file_operations debug_regulator_state_fops = {
+       .open           = dbg_hisi_regulator_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int dbg_control_regulator_show(struct seq_file *s, void *data)
+{
+       printk("                                                                
             \n\r \
+               
---------------------------------------------------------------------------------\n\r
 \
+               |usage:                                                         
                |\n\r \
+               |       S = state       R = read        V = voltage             
                            |\n\r \
+               |       set ldo state and voltage                               
                    |\n\r \
+               |       get ldo state and current voltage                       
                    |\n\r \
+               |example:                                                       
                |\n\r \
+               |       echo S ldo16 0   > control_regulator    :disable ldo16  
                    |\n\r \
+               |       echo S ldo16 1   > control_regulator    :enable ldo16   
                    |\n\r \
+               |       echo R ldo16     > control_regulator    :get ldo16 
state and voltage        |\n\r \
+               |       echo V ldo16 xxx > control_regulator    :set ldo16 
voltage                  |\n\r \
+               
---------------------------------------------------------------------------------\n\r");
+       return 0;
+}
+static ssize_t dbg_control_regulator_set_value(struct file *filp, const char 
__user *buffer,
+       size_t count, loff_t *ppos)
+{
+       char tmp[128] = {0};
+       char ptr[128] = {0};
+       char *vol = NULL;
+       char num = 0;
+       unsigned int i;
+       int next_flag = 1;
+
+       if (count >= 128) {
+               pr_info("error! buffer size big than internal buffer\n");
+               return -EFAULT;
+       }
+
+       if (copy_from_user(tmp, buffer, count)) {
+               pr_info("error!\n");
+               return -EFAULT;
+       }
+
+       if (tmp[0] == 'R' || tmp[0] == 'r') {
+               for (i = 2; i < (count - 1); i++) {
+                       ptr[i - 2] = tmp[i];
+               }
+               ptr[i - 2] = '\0';
+               get_regulator_state(ptr);
+       } else if (tmp[0] == 'S' || tmp[0] == 's') {
+               for (i = 2; i < (count - 1); i++) {
+                       if (tmp[i] == ' ') {
+                               next_flag = 0;
+                               ptr[i - 2] = '\0';
+                               continue;
+                       }
+                       if (next_flag) {
+                               ptr[i - 2] = tmp[i];
+                       } else {
+                               num = tmp[i] - 48;
+                       }
+               }
+               set_regulator_state(ptr, num);
+       } else if (tmp[0] == 'V' || tmp[0] == 'v') {
+               for (i = 2; i < (count - 1); i++) {
+                       if (tmp[i] == ' ') {
+                               next_flag = 0;
+                               ptr[i - 2] = '\0';
+                               continue;
+                       }
+                       if (next_flag) {
+                               ptr[i - 2] = tmp[i];
+                       } else {
+                               vol = &tmp[i];
+                               break;
+                       }
+               }
+               set_regulator_voltage(ptr, pmu_atoi(vol));
+       }
+
+       *ppos += count;
+
+       return count;
+}
+
+static int dbg_control_regulator_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return single_open(file, dbg_control_regulator_show, &inode->i_private);
+}
+
+static const struct file_operations set_control_regulator_fops = {
+       .open           = dbg_control_regulator_open,
+       .read           = seq_read,
+       .write          = dbg_control_regulator_set_value,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+#endif
+
+static int hisi_regulator_probe(struct spmi_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct regulator_desc *rdesc;
+       struct regulator_dev *rdev;
+       struct hisi_regulator *sreg = NULL;
+       struct regulator_init_data *initdata;
+       struct regulator_config config = { };
+       const struct of_device_id *match;
+       struct regulation_constraints *constraint;
+       const char *supplyname = NULL;
+#ifdef CONFIG_HISI_PMIC_DEBUG
+       struct dentry *d;
+       static int debugfs_flag;
+#endif
+       unsigned int temp_modes;
+
+       const struct hisi_regulator *template = NULL;
+       int ret = 0;
+       /* to check which type of regulator this is */
+       match = of_match_device(of_hisi_regulator_match_tbl, &pdev->dev);
+       if (NULL == match) {
+               pr_err("get hisi regulator fail!\n\r");
+               return -EINVAL;
+       }
+
+       template = match->data;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0))
+       initdata = of_get_regulator_init_data(dev, np, NULL);
+#else
+       initdata = of_get_regulator_init_data(dev, np);
+#endif
+       if (NULL == initdata) {
+               pr_err("get regulator init data error !\n");
+               return -EINVAL;
+       }
+
+       /* hisi regulator supports two modes */
+       constraint = &initdata->constraints;
+
+       ret = of_property_read_u32_array(np, "hisilicon,valid-modes-mask",
+                                               
&(constraint->valid_modes_mask), 1);
+       if (ret) {
+               pr_err("no hisilicon,valid-modes-mask property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+       ret = of_property_read_u32_array(np, "hisilicon,valid-idle-mask",
+                                               &temp_modes, 1);
+       if (ret) {
+               pr_err("no hisilicon,valid-modes-mask property set\n");
+               ret = -ENODEV;
+               return ret;
+       }
+       constraint->valid_ops_mask |= temp_modes;
+
+       sreg = kmemdup(template, sizeof(*sreg), GFP_KERNEL);
+       if (!sreg) {
+               pr_err("template kememdup is fail. \n");
+               return -ENOMEM;
+       }
+       sreg->name = initdata->constraints.name;
+       rdesc = &sreg->rdesc;
+       rdesc->name = sreg->name;
+       rdesc->min_uV = initdata->constraints.min_uV;
+       supplyname = of_get_property(np, "hisilicon,supply_name", NULL);
+       if (supplyname != NULL) {
+               initdata->supply_regulator = supplyname;
+       }
+
+       /* to parse device tree data for regulator specific */
+       ret = sreg->dt_parse(sreg, pdev);
+       if (ret) {
+               dev_err(dev, "device tree parameter parse error!\n");
+               goto hisi_probe_end;
+       }
+
+       config.dev = &pdev->dev;
+       config.init_data = initdata;
+       config.driver_data = sreg;
+       config.of_node = pdev->dev.of_node;
+
+       /* register regulator */
+       rdev = regulator_register(rdesc, &config);
+       if (IS_ERR(rdev)) {
+               dev_err(dev, "failed to register %s\n",
+                       rdesc->name);
+               ret = PTR_ERR(rdev);
+               goto hisi_probe_end;
+       }
+
+       BRAND_DEBUG("[%s]:valid_modes_mask[0x%x], valid_ops_mask[0x%x]\n", 
rdesc->name,\
+                       constraint->valid_modes_mask, 
constraint->valid_ops_mask);
+
+       dev_set_drvdata(dev, rdev);
+#ifdef CONFIG_HISI_PMIC_DEBUG
+       if (debugfs_flag == 0) {
+               d = debugfs_create_dir("hisi_regulator_debugfs", NULL);
+               if (!d) {
+                       dev_err(dev, "failed to create hisi regulator debugfs 
dir !\n");
+                       ret = -ENOMEM;
+                       goto hisi_probe_fail;
+               }
+               (void) debugfs_create_file("regulator_state", S_IRUSR,
+                                               d, NULL, 
&debug_regulator_state_fops);
+
+               (void) debugfs_create_file("control_regulator", S_IRUSR,
+                                               d, NULL, 
&set_control_regulator_fops);
+               debugfs_flag = 1;
+       }
+#endif
+
+#ifdef CONFIG_HISI_PMIC_DEBUG
+hisi_probe_fail:
+       if (ret)
+               regulator_unregister(rdev);
+#endif
+hisi_probe_end:
+       if (ret)
+               kfree(sreg);
+       return ret;
+}
+
+static void hisi_regulator_remove(struct spmi_device *pdev)
+{
+       struct regulator_dev *rdev = dev_get_drvdata(&pdev->dev);
+       struct hisi_regulator *sreg = rdev_get_drvdata(rdev);
+
+       regulator_unregister(rdev);
+
+       /* TODO: should i worry about that? devm_kzalloc */
+       if (sreg->rdesc.volt_table)
+               devm_kfree(&pdev->dev, (unsigned int *)sreg->rdesc.volt_table);
+
+       kfree(sreg);
+}
+static int hisi_regulator_suspend(struct device *dev, pm_message_t state)
+{
+       struct hisi_regulator *hisi_regulator = dev_get_drvdata(dev);
+
+       if (NULL == hisi_regulator) {
+               pr_err("%s:regulator is NULL\n", __func__);
+               return -ENOMEM;
+       }
+
+       pr_info("%s:+\n", __func__);
+       pr_info("%s:-\n", __func__);
+
+       return 0;
+}/*lint !e715 */
+
+static int hisi_regulator_resume(struct device *dev)
+{
+       struct hisi_regulator *hisi_regulator = dev_get_drvdata(dev);
+
+       if (NULL == hisi_regulator) {
+               pr_err("%s:regulator is NULL\n", __func__);
+               return -ENOMEM;
+       }
+
+       pr_info("%s:+\n", __func__);
+       pr_info("%s:-\n", __func__);
+
+       return 0;
+}
+
+static struct spmi_driver hisi_pmic_driver = {
+       .driver = {
+               .name   = "hisi_regulator",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_hisi_regulator_match_tbl,
+               .suspend = hisi_regulator_suspend,
+               .resume = hisi_regulator_resume,
+       },
+       .probe  = hisi_regulator_probe,
+       .remove = hisi_regulator_remove,
+};
+
+static int __init hisi_regulator_init(void)
+{
+       return spmi_driver_register(&hisi_pmic_driver);
+}
+
+static void __exit hisi_regulator_exit(void)
+{
+       spmi_driver_unregister(&hisi_pmic_driver);
+}
+
+fs_initcall(hisi_regulator_init);
+module_exit(hisi_regulator_exit);
+
+MODULE_DESCRIPTION("Hisi regulator driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/spmi/hisi-spmi-controller.c 
b/drivers/spmi/hisi-spmi-controller.c
new file mode 100644
index 000000000000..987526c8b49f
--- /dev/null
+++ b/drivers/spmi/hisi-spmi-controller.c
@@ -0,0 +1,390 @@
+
+#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/of.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/spmi.h>
+#include <linux/spmi.h>
+
+#define SPMI_CONTROLLER_NAME           "spmi_controller"
+
+/*
+ * SPMI register addr
+ */
+#define SPMI_CHANNEL_OFFSET                                    0x0300
+#define SPMI_SLAVE_OFFSET                                              0x20
+
+#define SPMI_APB_SPMI_CMD_BASE_ADDR                            0x0100
+/*lint -e750 -esym(750,*)*/
+#define SPMI_APB_SPMI_WDATA0_BASE_ADDR                 0x0104
+#define SPMI_APB_SPMI_WDATA1_BASE_ADDR                 0x0108
+#define SPMI_APB_SPMI_WDATA2_BASE_ADDR                 0x010c
+#define SPMI_APB_SPMI_WDATA3_BASE_ADDR                 0x0110
+
+#define SPMI_APB_SPMI_STATUS_BASE_ADDR                 0x0200
+
+#define SPMI_APB_SPMI_RDATA0_BASE_ADDR                 0x0204
+#define SPMI_APB_SPMI_RDATA1_BASE_ADDR                 0x0208
+#define SPMI_APB_SPMI_RDATA2_BASE_ADDR                 0x020c
+#define SPMI_APB_SPMI_RDATA3_BASE_ADDR                 0x0210
+/*lint +e750 -esym(750,*)*/
+
+#define SPMI_PER_DATAREG_BYTE                                  4
+/*
+ * SPMI cmd register
+ */
+#define SPMI_APB_SPMI_CMD_EN                                           (1 << 
31)
+#define SPMI_APB_SPMI_CMD_TYPE_OFFSET                  24
+#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET                        20
+#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET                       16
+#define SPMI_APB_SPMI_CMD_ADDR_OFFSET                          0
+
+#define Tranverse32(X)                 ((((u32)(X) & 0xff000000) >> 24) | \
+                                                          (((u32)(X) & 
0x00ff0000) >> 8) | \
+                                                          (((u32)(X) & 
0x0000ff00) << 8) | \
+                                                          (((u32)(X) & 
0x000000ff) << 24))
+
+/* Command Opcodes */
+/*lint -e749 -esym(749,*)*/
+enum spmi_controller_cmd_op_code {
+       SPMI_CMD_REG_ZERO_WRITE = 0,
+       SPMI_CMD_REG_WRITE = 1,
+       SPMI_CMD_REG_READ = 2,
+       SPMI_CMD_EXT_REG_WRITE = 3,
+       SPMI_CMD_EXT_REG_READ = 4,
+       SPMI_CMD_EXT_REG_WRITE_L = 5,
+       SPMI_CMD_EXT_REG_READ_L = 6,
+       SPMI_CMD_REG_RESET = 7,
+       SPMI_CMD_REG_SLEEP = 8,
+       SPMI_CMD_REG_SHUTDOWN = 9,
+       SPMI_CMD_REG_WAKEUP = 10,
+};
+/*lint +e749 -esym(749,*)*/
+
+/*
+ * SPMI status register
+ */
+#define SPMI_APB_TRANS_DONE                                            (1 << 0)
+#define SPMI_APB_TRANS_FAIL                                            (1 << 2)
+
+/* Command register fields */
+#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT     16
+
+/* Maximum number of support PMIC peripherals */
+#define SPMI_CONTROLLER_TIMEOUT_US             1000
+#define SPMI_CONTROLLER_MAX_TRANS_BYTES        (16)
+
+#define SPMI_WRITEL( dev, reg, addr )  \
+       do { \
+               writel( ( reg ), ( addr ) ); \
+       } while (0)
+
+#define  SPMI_READL( dev, reg, addr )  \
+       do { \
+               reg = readl( addr ); \
+       } while (0)
+
+/*
+ * @base base address of the PMIC Arbiter core registers.
+ * @rdbase, @wrbase base address of the PMIC Arbiter read core registers.
+ *     For HW-v1 these are equal to base.
+ *     For HW-v2, the value is the same in eeraly probing, in order to read
+ *     PMIC_ARB_CORE registers, then chnls, and obsrvr are set to
+ *     PMIC_ARB_CORE_REGISTERS and PMIC_ARB_CORE_REGISTERS_OBS respectivly.
+ * @intr base address of the SPMI interrupt control registers
+ * @ppid_2_chnl_tbl lookup table f(SID, Periph-ID) -> channel num
+ *      entry is only valid if corresponding bit is set in valid_ppid_bitmap.
+ * @valid_ppid_bitmap bit is set only for valid ppids.
+ * @fmt_cmd formats a command to be set into PMIC_ARBq_CHNLn_CMD
+ * @chnl_ofst calculates offset of the base of a channel reg space
+ * @ee execution environment id
+ * @irq_acc0_init_val initial value of the interrupt accumulator at probe time.
+ *      Use for an HW workaround. On handling interrupts, the first accumulator
+ *      register will be compared against this value, and bits which are set at
+ *      boot will be ignored.
+ * @reserved_chnl entry of ppid_2_chnl_tbl that this driver should never touch.
+ *      value is positive channel number or negative to mark it unused.
+ */
+struct spmi_controller_dev {
+       struct spmi_controller  *controller;
+       struct device           *dev;
+       void __iomem            *base;
+       spinlock_t              lock;
+       u32                     channel;
+};
+
+static int spmi_controller_wait_for_done(struct spmi_controller_dev *ctrl_dev,
+                                 void __iomem *base, u8 sid, u16 addr)
+{
+       u32 status = 0;
+       u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
+       u32 offset = SPMI_APB_SPMI_STATUS_BASE_ADDR + SPMI_CHANNEL_OFFSET * 
ctrl_dev->channel
+               + SPMI_SLAVE_OFFSET * sid;
+
+       while (timeout--) {
+               SPMI_READL(ctrl_dev->dev, status, base + offset);/*lint !e732 */
+
+               if (status & SPMI_APB_TRANS_DONE) {
+                       if (status & SPMI_APB_TRANS_FAIL) {
+                               dev_err(ctrl_dev->dev,
+                                       "%s: transaction failed (0x%x)\n",
+                                       __func__, status);
+                               return -EIO;
+                       }
+                       return 0;
+               }
+               udelay(1);/*lint !e778 !e774 !e747*/
+       }
+
+       dev_err(ctrl_dev->dev,
+               "%s: timeout, status 0x%x\n",
+               __func__, status);
+       return -ETIMEDOUT;/*lint !e438*/
+}/*lint !e715 !e529*/
+
+static int spmi_read_cmd(struct spmi_controller *ctrl,
+                               u8 opc, u8 sid, u16 addr, u8 *buf, size_t bc)
+{
+       struct spmi_controller_dev *spmi_controller = 
dev_get_drvdata(&ctrl->dev);
+       unsigned long flags;
+       u32 cmd, data;
+       int rc;
+       u32 chnl_ofst = SPMI_CHANNEL_OFFSET*spmi_controller->channel;
+       u8 op_code, i;
+
+       if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
+               dev_err(spmi_controller->dev
+               , "spmi_controller supports 1..%d bytes per trans, but:%ld 
requested"
+                                       , SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
+               return  -EINVAL;
+       }
+
+       /* Check the opcode */
+       if (SPMI_CMD_READ == opc)
+               op_code = SPMI_CMD_REG_READ;
+       else if (SPMI_CMD_EXT_READ == opc)
+               op_code = SPMI_CMD_EXT_REG_READ;
+       else if (SPMI_CMD_EXT_READL == opc)
+               op_code = SPMI_CMD_EXT_REG_READ_L;
+       else {
+               dev_err(spmi_controller->dev, "invalid read cmd 0x%x", opc);
+               return -EINVAL;
+       }
+
+       cmd = SPMI_APB_SPMI_CMD_EN |/*lint !e648 !e701 */                       
                                        /* cmd_en */
+                (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |/*lint !e648 !e701 
*/                      /* cmd_type */
+                ((bc-1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |/*lint !e648 
!e701 */             /* byte_cnt */
+                ((sid & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |            
                                /* slvid */
+                ((addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);           
                        /* slave_addr */
+
+       spin_lock_irqsave(&spmi_controller->lock, flags);/*lint !e550 */
+
+       SPMI_WRITEL(spmi_controller->dev, cmd, spmi_controller->base + 
chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
+
+
+       rc = spmi_controller_wait_for_done(spmi_controller, 
spmi_controller->base, sid, addr);
+       if (rc)
+               goto done;
+
+       i = 0;
+       do {
+               SPMI_READL(spmi_controller->dev, data, spmi_controller->base + 
chnl_ofst + SPMI_SLAVE_OFFSET*sid + SPMI_APB_SPMI_RDATA0_BASE_ADDR + 
i*SPMI_PER_DATAREG_BYTE);/*lint !e732 */
+               data = Tranverse32(data);
+               if ((bc - i*SPMI_PER_DATAREG_BYTE ) >> 2) {/*lint !e702 */
+                       memcpy(buf, &data, sizeof(data));
+                       buf += sizeof(data);
+               } else {
+                       memcpy(buf, &data, bc%SPMI_PER_DATAREG_BYTE);/*lint 
!e747 */
+                       buf += (bc%SPMI_PER_DATAREG_BYTE);
+               }
+               i++;
+       } while (bc > i*SPMI_PER_DATAREG_BYTE);
+
+done:
+       spin_unlock_irqrestore(&spmi_controller->lock, flags);
+       if (rc)
+               dev_err(spmi_controller->dev, "spmi read wait timeout op:0x%x 
sid:%d addr:0x%x bc:%ld\n",
+                                                       opc, sid, addr, bc + 1);
+       return rc;
+}/*lint !e550 !e529*/
+
+/*lint -e438 -esym(438,*)*/
+static int spmi_write_cmd(struct spmi_controller *ctrl,
+                               u8 opc, u8 sid, u16 addr, const u8 *buf, size_t 
bc)
+{
+       struct spmi_controller_dev *spmi_controller = 
dev_get_drvdata(&ctrl->dev);
+       unsigned long flags;
+       u32 cmd;
+       u32 data = 0;
+       int rc;
+       u32 chnl_ofst = SPMI_CHANNEL_OFFSET*spmi_controller->channel;
+       u8 op_code, i;
+
+
+       if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
+               dev_err(spmi_controller->dev
+               , "spmi_controller supports 1..%d bytes per trans, but:%ld 
requested"
+                                       , SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
+               return  -EINVAL;
+       }
+
+       /* Check the opcode */
+       if (SPMI_CMD_WRITE == opc)
+               op_code = SPMI_CMD_REG_WRITE;
+       else if (SPMI_CMD_EXT_WRITE == opc)
+               op_code = SPMI_CMD_EXT_REG_WRITE;
+       else if (SPMI_CMD_EXT_WRITEL == opc)
+               op_code = SPMI_CMD_EXT_REG_WRITE_L;
+       else {
+               dev_err(spmi_controller->dev, "invalid write cmd 0x%x", opc);
+               return -EINVAL;
+       }
+
+       cmd = SPMI_APB_SPMI_CMD_EN |/*lint !e648 !e701 */                       
                                        /* cmd_en */
+                (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |/*lint !e648 !e701 
*/                      /* cmd_type */
+                ((bc-1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |/*lint !e648 
!e701 */             /* byte_cnt */
+                ((sid & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |            
                                /* slvid */
+                ((addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);           
                        /* slave_addr */
+
+       /* Write data to FIFOs */
+       spin_lock_irqsave(&spmi_controller->lock, flags);/*lint !e550 */
+
+       i = 0;
+       do {
+               memset(&data, 0, sizeof(data));
+               if ((bc - i*SPMI_PER_DATAREG_BYTE ) >> 2) {/*lint !e702 */
+                       memcpy(&data, buf, sizeof(data));
+                       buf +=sizeof(data);
+               } else {
+                       memcpy(&data, buf, bc%SPMI_PER_DATAREG_BYTE);/*lint 
!e747 */
+                       buf +=(bc%SPMI_PER_DATAREG_BYTE);
+               }
+
+               data = Tranverse32(data);
+               SPMI_WRITEL(spmi_controller->dev, data, spmi_controller->base + 
chnl_ofst + SPMI_APB_SPMI_WDATA0_BASE_ADDR+SPMI_PER_DATAREG_BYTE*i);
+               i++;
+       } while (bc > i*SPMI_PER_DATAREG_BYTE);
+
+       /* Start the transaction */
+       SPMI_WRITEL(spmi_controller->dev, cmd, spmi_controller->base + 
chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
+
+       rc = spmi_controller_wait_for_done(spmi_controller, 
spmi_controller->base, sid, addr);
+       spin_unlock_irqrestore(&spmi_controller->lock, flags);
+
+       if (rc)
+               dev_err(spmi_controller->dev, "spmi write wait timeout op:0x%x 
sid:%d addr:0x%x bc:%ld\n",
+                                                       opc, sid, addr, bc);
+
+       return rc;
+}/*lint !e438 !e550 !e529*/
+/*lint +e438 -esym(438,*)*/
+static int spmi_controller_probe(struct platform_device *pdev)
+{
+       struct spmi_controller_dev *spmi_controller;
+       struct spmi_controller *ctrl;
+       struct resource *iores;
+       int ret = 0;
+
+       printk(KERN_INFO "HISI SPMI probe\n");
+       ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
+       if (!ctrl) {
+               dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
+               return -ENOMEM;  /*lint !e429*/
+       }
+       spmi_controller = spmi_controller_get_drvdata(ctrl);
+       spmi_controller->controller = ctrl;
+
+       /* NOTE: driver uses the static register mapping */
+       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!iores) {
+               dev_err(&pdev->dev, "can not get resource! \n");
+               return -EINVAL; /*lint !e429*/
+       }
+
+       spmi_controller->base = ioremap(iores->start, resource_size(iores));
+       if (!spmi_controller->base) {
+               dev_err(&pdev->dev, "can not remap base addr! \n");
+               return -EADDRNOTAVAIL; /*lint !e429*/
+       }
+       dev_dbg(&pdev->dev, "spmi_add_controller base addr=0x%lx!\n", (long 
unsigned int)spmi_controller->base);/*lint !e774*/
+
+       /* Get properties from the device tree */
+       ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel",
+                       &spmi_controller->channel);/*lint !e838*/
+       if (ret) {
+               dev_err(&pdev->dev, "can not get chanel \n");
+               return -ENODEV; /*lint !e429*/
+       }
+
+       platform_set_drvdata(pdev, spmi_controller);
+       dev_set_drvdata(&ctrl->dev, spmi_controller);
+
+       spin_lock_init(&spmi_controller->lock);
+
+       ctrl->nr = spmi_controller->channel;
+       ctrl->dev.parent = pdev->dev.parent;
+       ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
+
+       /* Callbacks */
+       ctrl->read_cmd = spmi_read_cmd;
+       ctrl->write_cmd = spmi_write_cmd;
+
+       ret = spmi_controller_add(ctrl);
+       if (ret) {
+               dev_err(&pdev->dev, "spmi_add_controller failed!\n");
+               goto err_add_controller;
+       }
+err_add_controller:
+       platform_set_drvdata(pdev, NULL);
+       return ret; /*lint !e429*/
+}
+
+static int spmi_del_controller(struct platform_device *pdev)
+{
+       struct spmi_controller *ctrl = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+       spmi_controller_remove(ctrl);
+       return 0;
+}
+
+static struct of_device_id spmi_controller_match_table[] = {
+       {       .compatible = "hisilicon,spmi-controller",
+       },/*lint !e785*/
+       {}/*lint !e785*/
+};
+
+static struct platform_driver spmi_controller_driver = {
+       .probe          = spmi_controller_probe,
+       .remove         = spmi_del_controller,
+       .driver         = {
+               .name   = SPMI_CONTROLLER_NAME,
+               .owner  = THIS_MODULE,/*lint !e64*/
+               .of_match_table = spmi_controller_match_table,
+       },/*lint !e785*/
+};/*lint !e785*/
+/*lint -e528 -esym(528,*)*/
+static int __init spmi_controller_init(void)
+{
+       return platform_driver_register(&spmi_controller_driver);/*lint !e64*/
+}
+postcore_initcall(spmi_controller_init);
+
+static void __exit spmi_controller_exit(void)
+{
+       platform_driver_unregister(&spmi_controller_driver);
+}
+module_exit(spmi_controller_exit);
+/*lint -e753 -esym(753,*)*/
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");/*lint !e785 !e64 !e528*/
+MODULE_ALIAS("platform:spmi_controlller");
+/*lint -e753 +esym(753,*)*/
+/*lint -e528 +esym(528,*)*/
+
diff --git a/include/linux/mfd/hisi_pmic.h b/include/linux/mfd/hisi_pmic.h
new file mode 100644
index 000000000000..939b36f617c1
--- /dev/null
+++ b/include/linux/mfd/hisi_pmic.h
@@ -0,0 +1,165 @@
+/*
+ * Header file for device driver Hi6421 PMIC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (C) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong...@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.
+ */
+
+#ifndef        __HISI_PMIC_H
+#define        __HISI_PMIC_H
+
+#include <linux/irqdomain.h>
+
+#define HISI_REGS_ENA_PROTECT_TIME     (0)     /* in microseconds */
+#define HISI_ECO_MODE_ENABLE           (1)
+#define HISI_ECO_MODE_DISABLE          (0)
+
+typedef int (*pmic_ocp_callback)(char *);
+extern int hisi_pmic_special_ocp_register(char *power_name, pmic_ocp_callback 
handler);
+
+struct irq_mask_info {
+       int start_addr;
+       int array;
+};
+
+struct irq_info {
+       int start_addr;
+       int array;
+};
+
+struct bit_info {
+       int addr;
+       int bit;
+};
+
+struct write_lock {
+       int addr;
+       int val;
+};
+
+struct hisi_pmic {
+       struct resource         *res;
+       struct device           *dev;
+       void __iomem            *regs;
+       spinlock_t              lock;
+       struct irq_domain       *domain;
+       int                     irq;
+       int                     gpio;
+       unsigned int    *irqs;
+       int                     irqnum;
+       int                     irqarray;
+       struct irq_mask_info irq_mask_addr;
+       struct irq_info irq_addr;
+       int                     irqnum1;
+       int                     irqarray1;
+       struct irq_mask_info irq_mask_addr1;
+       struct irq_info irq_addr1;
+       struct write_lock normal_lock;
+       struct write_lock debug_lock;
+};
+
+/* 0:disable; 1:enable */
+unsigned int get_uv_mntn_status(void);
+void clear_uv_mntn_resered_reg_bit(void);
+void set_uv_mntn_resered_reg_bit(void);
+
+#if defined(CONFIG_HISI_PMIC) || defined(CONFIG_HISI_PMIC_PMU_SPMI)
+/* Register Access Helpers */
+u32 hisi_pmic_read(struct hisi_pmic *pmic, int reg);
+void hisi_pmic_write(struct hisi_pmic *pmic, int reg, u32 val);
+void hisi_pmic_rmw(struct hisi_pmic *pmic, int reg, u32 mask, u32 bits);
+unsigned int hisi_pmic_reg_read(int addr);
+void hisi_pmic_reg_write(int addr, int val);
+void hisi_pmic_reg_write_lock(int addr, int val);
+int hisi_pmic_array_read(int addr, char *buff, unsigned int len);
+int hisi_pmic_array_write(int addr, char *buff, unsigned int len);
+extern int hisi_get_pmic_irq_byname(unsigned int pmic_irq_list);
+extern int hisi_pmic_get_vbus_status(void);
+#if defined(CONFIG_HISI_DIEID)
+u32 hisi_pmic_read_sub_pmu(u8 sid ,int reg);
+void hisi_pmic_write_sub_pmu(u8 sid ,int reg, u32 val);
+#endif
+#else
+static inline u32 hisi_pmic_read(struct hisi_pmic *pmic, int reg) { return 0; }
+static inline void hisi_pmic_write(struct hisi_pmic *pmic, int reg, u32 val) {}
+static inline void hisi_pmic_rmw(struct hisi_pmic *pmic, int reg, u32 mask, 
u32 bits) {}
+static inline unsigned int hisi_pmic_reg_read(int addr) { return 0; }
+static inline void hisi_pmic_reg_write(int addr, int val) {}
+static inline void hisi_pmic_reg_write_lock(int addr, int val) {}
+static inline int hisi_pmic_array_read(int addr, char *buff, unsigned int len) 
{ return 0; }
+static inline int hisi_pmic_array_write(int addr, char *buff, unsigned int 
len) { return 0; }
+static inline int hisi_get_pmic_irq_byname(unsigned int pmic_irq_list) { 
return -1; }
+static inline int hisi_pmic_get_vbus_status(void) { return 1; }
+static inline u32 hisi_pmic_read_sub_pmu(u8 sid ,int reg) { return 0; }
+static inline void hisi_pmic_write_sub_pmu(u8 sid ,int reg, u32 val) {}
+#endif
+
+#ifdef CONFIG_HISI_HI6421V500_PMU
+enum pmic_irq_list {
+       POR_D45MR = 0,
+       VBUS_CONNECT,
+       VBUS_DISCONNECT,
+       ALARMON_R,
+       HOLD_6S,
+       HOLD_1S,
+       POWERKEY_UP,
+       POWERKEY_DOWN,
+       OCP_SCP_R,
+       COUL_R,
+       VSYS_OV,
+       VSYS_UV,
+       VSYS_PWROFF_ABS,
+       VSYS_PWROFF_DEB,
+       THSD_OTMP140,
+       THSD_OTMP125,
+       HRESETN,
+       SIM0_HPD_R = 24,
+       SIM0_HPD_F,
+       SIM0_HPD_H,
+       SIM0_HPD_L,
+       SIM1_HPD_R,
+       SIM1_HPD_F,
+       SIM1_HPD_H,
+       SIM1_HPD_L,
+       PMIC_IRQ_LIST_MAX,
+};
+#else
+enum pmic_irq_list {
+       OTMP = 0,
+       VBUS_CONNECT,
+       VBUS_DISCONNECT,
+       ALARMON_R,
+       HOLD_6S,
+       HOLD_1S,
+       POWERKEY_UP,
+       POWERKEY_DOWN,
+       OCP_SCP_R,
+       COUL_R,
+       SIM0_HPD_R,
+       SIM0_HPD_F,
+       SIM1_HPD_R,
+       SIM1_HPD_F,
+       PMIC_IRQ_LIST_MAX,
+};
+#endif
+
+#ifdef CONFIG_HISI_SR_DEBUG
+extern void get_ip_regulator_state(void);
+#endif
+#endif         /* __HISI_PMIC_H */
+
-- 
2.26.2

Reply via email to