[linux-yocto] [PATCH 07/65] pmu: add atc260x pmu driver

Jiang Lu lu.jiang at windriver.com
Wed Dec 21 01:16:08 PST 2016


From: wurui <wurui at actions-semi.com>

commit 1b3a6e8355da6764b8e310aa5dc520ac2252379e from
https://github.com/xapp-le/kernel.git

Change-Id: If07daab69208f7a6ec5652a89ce036ac67f96e4b
---
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-atc2603c-sgpio.c                 |  764 +++++
 drivers/gpio/gpio-atc260x.c                        |  421 +++
 drivers/hwmon/Kconfig                              |   10 +
 drivers/hwmon/Makefile                             |    1 +
 drivers/hwmon/atc260x-hwmon.c                      |  235 ++
 drivers/input/keyboard/Kconfig                     |   22 +
 drivers/input/keyboard/Makefile                    |    3 +
 drivers/input/keyboard/atc260x-adckeypad.c         |  505 ++++
 drivers/input/keyboard/atc260x-irkeypad.c          | 1153 ++++++++
 drivers/input/keyboard/atc260x-onoff.c             |  558 ++++
 drivers/mfd/Kconfig                                |   15 +
 drivers/mfd/Makefile                               |    5 +
 drivers/mfd/atc260x-auxadc.c                       | 1027 +++++++
 drivers/mfd/atc260x-cmu.c                          |  130 +
 drivers/mfd/atc260x-core.c                         | 1070 +++++++
 drivers/mfd/atc260x-core.h                         |   98 +
 drivers/mfd/atc260x-ex-api.c                       |   70 +
 drivers/mfd/atc260x-i2c.c                          |  605 ++++
 drivers/mfd/atc260x-pm.c                           | 1510 ++++++++++
 drivers/mfd/atc260x-pstore.c                       |  456 +++
 drivers/mfd/atc260x-pwm.c                          |  205 ++
 drivers/mfd/atc260x-spi.c                          |  323 ++
 drivers/mfd/atc260x-subdev.h                       |  661 +++++
 drivers/power/Kconfig                              |   20 +
 drivers/power/Makefile                             |    3 +
 drivers/power/atc260x_cap_gauge.c                  | 3074 ++++++++++++++++++++
 drivers/power/atc260x_power/Makefile               |    2 +
 drivers/power/atc260x_power/atc2603a_power_phy.c   |  618 ++++
 drivers/power/atc260x_power/atc2603c_power_phy.c   |  642 ++++
 drivers/power/atc260x_power/atc260x_power.h        |  291 ++
 .../power/atc260x_power/atc260x_power_event_log.c  |  105 +
 drivers/power/atc260x_power/atc260x_power_main.c   | 2938 +++++++++++++++++++
 drivers/power/bq27441_battery.c                    | 1891 ++++++++++++
 drivers/regulator/Kconfig                          |    7 +-
 drivers/regulator/Makefile                         |    4 +-
 drivers/regulator/atc260x-pwm-dcdc.c               |  349 +++
 drivers/regulator/atc260x-regulator.c              |  954 ++++++
 drivers/regulator/atc260x-regulator.h              | 1606 ++++++++++
 drivers/regulator/atc260x-switch-ldo.c             |  540 ++++
 drivers/rtc/Kconfig                                |    9 +-
 drivers/rtc/Makefile                               |    1 +
 drivers/rtc/rtc-atc260x.c                          |  727 +++++
 include/linux/mfd/atc260x/atc260x.h                |  258 ++
 include/linux/mfd/atc260x/regs_map_atc2603a.h      |  209 ++
 include/linux/mfd/atc260x/regs_map_atc2603c.h      |  192 ++
 include/linux/mfd/atc260x/regs_map_atc2609a.h      |  220 ++
 47 files changed, 24505 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 drivers/gpio/Makefile
 create mode 100755 drivers/gpio/gpio-atc2603c-sgpio.c
 create mode 100755 drivers/gpio/gpio-atc260x.c
 mode change 100644 => 100755 drivers/hwmon/Kconfig
 mode change 100644 => 100755 drivers/hwmon/Makefile
 create mode 100755 drivers/hwmon/atc260x-hwmon.c
 mode change 100644 => 100755 drivers/input/keyboard/Kconfig
 mode change 100644 => 100755 drivers/input/keyboard/Makefile
 create mode 100755 drivers/input/keyboard/atc260x-adckeypad.c
 create mode 100755 drivers/input/keyboard/atc260x-irkeypad.c
 create mode 100755 drivers/input/keyboard/atc260x-onoff.c
 mode change 100644 => 100755 drivers/mfd/Kconfig
 mode change 100644 => 100755 drivers/mfd/Makefile
 create mode 100755 drivers/mfd/atc260x-auxadc.c
 create mode 100755 drivers/mfd/atc260x-cmu.c
 create mode 100755 drivers/mfd/atc260x-core.c
 create mode 100755 drivers/mfd/atc260x-core.h
 create mode 100755 drivers/mfd/atc260x-ex-api.c
 create mode 100755 drivers/mfd/atc260x-i2c.c
 create mode 100755 drivers/mfd/atc260x-pm.c
 create mode 100755 drivers/mfd/atc260x-pstore.c
 create mode 100755 drivers/mfd/atc260x-pwm.c
 create mode 100755 drivers/mfd/atc260x-spi.c
 create mode 100755 drivers/mfd/atc260x-subdev.h
 mode change 100644 => 100755 drivers/power/Kconfig
 mode change 100644 => 100755 drivers/power/Makefile
 create mode 100755 drivers/power/atc260x_cap_gauge.c
 create mode 100755 drivers/power/atc260x_power/Makefile
 create mode 100755 drivers/power/atc260x_power/atc2603a_power_phy.c
 create mode 100755 drivers/power/atc260x_power/atc2603c_power_phy.c
 create mode 100755 drivers/power/atc260x_power/atc260x_power.h
 create mode 100755 drivers/power/atc260x_power/atc260x_power_event_log.c
 create mode 100755 drivers/power/atc260x_power/atc260x_power_main.c
 create mode 100755 drivers/power/bq27441_battery.c
 mode change 100644 => 100755 drivers/regulator/Kconfig
 mode change 100644 => 100755 drivers/regulator/Makefile
 create mode 100755 drivers/regulator/atc260x-pwm-dcdc.c
 create mode 100755 drivers/regulator/atc260x-regulator.c
 create mode 100755 drivers/regulator/atc260x-regulator.h
 create mode 100755 drivers/regulator/atc260x-switch-ldo.c
 mode change 100644 => 100755 drivers/rtc/Kconfig
 mode change 100644 => 100755 drivers/rtc/Makefile
 create mode 100755 drivers/rtc/rtc-atc260x.c
 create mode 100755 include/linux/mfd/atc260x/atc260x.h
 create mode 100755 include/linux/mfd/atc260x/regs_map_atc2603a.h
 create mode 100755 include/linux/mfd/atc260x/regs_map_atc2603c.h
 create mode 100755 include/linux/mfd/atc260x/regs_map_atc2609a.h

diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
old mode 100644
new mode 100755
index f71bb97..bede2bd
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -112,3 +112,4 @@ obj-$(CONFIG_GPIO_XILINX)	+= gpio-xilinx.o
 obj-$(CONFIG_GPIO_XTENSA)	+= gpio-xtensa.o
 obj-$(CONFIG_GPIO_ZEVIO)	+= gpio-zevio.o
 obj-$(CONFIG_GPIO_ZYNQ)		+= gpio-zynq.o
+obj-$(CONFIG_GPIO_ATC260X)	+= gpio-atc260x.o gpio-atc2603c-sgpio.o
diff --git a/drivers/gpio/gpio-atc2603c-sgpio.c b/drivers/gpio/gpio-atc2603c-sgpio.c
new file mode 100755
index 0000000..c35528b
--- /dev/null
+++ b/drivers/gpio/gpio-atc2603c-sgpio.c
@@ -0,0 +1,764 @@
+/*
+ * gpio-atc2603c-sgpio.c  --  gpiolib support for ATC260X
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <mach/power.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+/* SGPIO 专指atc2603c内的具有中断&唤醒功能的几个特殊GPIO,
+ * atc2603a内也有叫SGPIO的pin, 是完全不同的功能, 不是这里的支持范围. */
+
+
+#define ATC2603C_SGPIO_NR	7
+#define ATC2603C_SGPIO_OBJ_TYPE_ID 0xfc3b25ceU
+#define ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK (1U << (sizeof(uint) * 8 -1))
+
+/* for debug */
+#define ATC2603C_SGPIO_ASSERT_VALID_DEV(ADEV) \
+	BUG_ON(IS_ERR_OR_NULL(ADEV) || (ADEV)->_obj_type_id != ATC2603C_SGPIO_OBJ_TYPE_ID)
+
+
+struct atc2603c_sgpio_mfp_map {
+	u16 mask;
+	u16 value;
+};
+
+struct atc2603c_sgpio_dev {
+	struct device *dev;
+	struct atc260x_dev *atc260x;
+	struct gpio_chip gpio_chip;
+
+	/* for IRQ function */
+	struct mutex irq_bus_lock;
+	struct irq_chip irq_chip;
+	struct irq_domain *irq_domain;
+	uint root_irq;
+	uint cached_irq_en_mask;   /* MSB is the change bit */
+	uint cached_irq_trig_type; /* MSB is the change bit */
+	uint cached_irq_wake_mask; /* MSB is the change bit */
+	int wake_count;
+	uint parent_irq_no_wake;
+	uint irq_map_tbl[ATC2603C_SGPIO_NR]; /* hwirq -> virq */
+
+	/* for debug */
+	u32 _obj_type_id; /* set to ATC2603C_SGPIO_OBJ_TYPE_ID */
+};
+
+
+/* Interrupt function ------------------------------------------------------- */
+
+/* 原本这里可以直接用regmap_irq框架的, 但是因为SGPIO的中断mask和pending是同一个寄存器,
+ * 不符合regmap_irq的要求, 硬用会出问题的, 所以这里只能自己实现中断分发了. */
+
+static void _atc2603c_sgpio_irq_lock(struct irq_data *data)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+
+	atc2603c_sgpio = irq_data_get_irq_chip_data(data);
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+
+	mutex_lock(&(atc2603c_sgpio->irq_bus_lock));
+}
+
+static void _atc2603c_sgpio_sync_cached_regs(struct atc2603c_sgpio_dev *atc2603c_sgpio)
+{
+	uint changed_mask;
+	int ret;
+
+	changed_mask = ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK;
+
+	/* write back cached registers */
+	if (atc2603c_sgpio->cached_irq_wake_mask & changed_mask) {
+		atc2603c_sgpio->cached_irq_wake_mask &= ~changed_mask;
+		ret = atc260x_reg_write(atc2603c_sgpio->atc260x,
+				ATC2603C_PMU_SGPIO_CTL2,
+				atc2603c_sgpio->cached_irq_wake_mask);
+		if (ret == 0) {
+			/* setup root wakeup source */
+			ret = owl_pmic_setup_aux_wakeup_src(
+					OWL_PMIC_WAKEUP_SRC_SGPIOIRQ,
+					(atc2603c_sgpio->cached_irq_wake_mask != 0));
+			if (ret)
+				dev_err(atc2603c_sgpio->dev,
+					"%s: failed to setup SGPIOIRQ wakeup source\n", __func__);
+		} else {
+			dev_err(atc2603c_sgpio->dev,
+				"%s: failed to write back cached irq_wake_mask\n", __func__);
+		}
+	}
+	if (atc2603c_sgpio->cached_irq_trig_type & changed_mask) {
+		atc2603c_sgpio->cached_irq_trig_type &= ~changed_mask;
+		ret = atc260x_reg_write(atc2603c_sgpio->atc260x,
+				ATC2603C_PMU_SGPIO_CTL1,
+				atc2603c_sgpio->cached_irq_trig_type);
+		if (ret)
+			dev_err(atc2603c_sgpio->dev,
+				"%s: failed to write back cached irq_trig_type\n", __func__);
+	}
+	/* en_mask MUST be handled at last */
+	if (atc2603c_sgpio->cached_irq_en_mask & changed_mask) {
+		atc2603c_sgpio->cached_irq_en_mask &= ~changed_mask;
+		ret = atc260x_reg_wp_setbits(atc2603c_sgpio->atc260x,
+				ATC2603C_PMU_SGPIO_CTL0,
+				(0x7fU << 9),
+				(0x7fU << 2),
+				atc2603c_sgpio->cached_irq_en_mask);
+		if (ret)
+			dev_err(atc2603c_sgpio->dev,
+				"%s: failed to write back cached irq_en_mask\n", __func__);
+	}
+}
+
+static void _atc2603c_sgpio_irq_sync_unlock(struct irq_data *data)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int i, ret;
+
+	atc2603c_sgpio = irq_data_get_irq_chip_data(data);
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+
+	_atc2603c_sgpio_sync_cached_regs(atc2603c_sgpio);
+
+	/* If we've changed our wakeup count propagate it to the parent */
+	if (atc2603c_sgpio->wake_count) {
+		if (!atc2603c_sgpio->parent_irq_no_wake) {
+			if (atc2603c_sgpio->wake_count < 0)
+				for (i = atc2603c_sgpio->wake_count; i < 0; i++) {
+					ret = irq_set_irq_wake(atc2603c_sgpio->root_irq, 0);
+					if (ret)
+						break;
+				}
+			else
+				for (i = 0; i < atc2603c_sgpio->wake_count; i++) {
+					ret = irq_set_irq_wake(atc2603c_sgpio->root_irq, 1);
+					if (ret && i == 0) {
+						atc2603c_sgpio->parent_irq_no_wake = 1;
+						break;
+					}
+				}
+		}
+		atc2603c_sgpio->wake_count = 0;
+	}
+
+	mutex_unlock(&(atc2603c_sgpio->irq_bus_lock));
+}
+
+static void _atc2603c_sgpio_irq_enable(struct irq_data *data)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	uint changed_mask;
+
+	atc2603c_sgpio = irq_data_get_irq_chip_data(data);
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+	BUG_ON(data->hwirq >= ATC2603C_SGPIO_NR);
+
+	changed_mask = ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK;
+	atc2603c_sgpio->cached_irq_en_mask |= changed_mask | (1U << (data->hwirq + 2));
+}
+
+static void _atc2603c_sgpio_irq_disable(struct irq_data *data)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	uint changed_mask;
+
+	atc2603c_sgpio = irq_data_get_irq_chip_data(data);
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+	BUG_ON(data->hwirq >= ATC2603C_SGPIO_NR);
+
+	changed_mask = ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK;
+	atc2603c_sgpio->cached_irq_en_mask |= changed_mask;
+	atc2603c_sgpio->cached_irq_en_mask &= ~(1U << (data->hwirq + 2));
+}
+
+static int _atc2603c_sgpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	uint trig_type, changed_mask, cfg_value, shift_pos;
+
+	atc2603c_sgpio = irq_data_get_irq_chip_data(data);
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+	BUG_ON(data->hwirq >= ATC2603C_SGPIO_NR);
+
+	trig_type = flow_type & IRQ_TYPE_SENSE_MASK;
+	if ((trig_type & (trig_type - 1)) != 0) {
+		/* multi bits */
+		dev_err(atc2603c_sgpio->dev, "not support IRQ flow_type 0x%x\n", trig_type);
+		return -EINVAL;
+	}
+
+	cfg_value = 0;
+	switch (trig_type) {
+	case IRQ_TYPE_EDGE_RISING:
+		cfg_value = 2;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		cfg_value = 3;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		cfg_value = 0;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		cfg_value = 1;
+		break;
+	}
+	shift_pos = 2U + data->hwirq * 2U;
+	changed_mask = ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK;
+	atc2603c_sgpio->cached_irq_trig_type =
+		(atc2603c_sgpio->cached_irq_trig_type & ~(3U << shift_pos)) |
+		(cfg_value << shift_pos) | changed_mask;
+
+	return 0;
+}
+
+static int _atc2603c_sgpio_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	uint changed_mask;
+
+	atc2603c_sgpio = irq_data_get_irq_chip_data(data);
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+	BUG_ON(data->hwirq >= ATC2603C_SGPIO_NR);
+
+	on = !!on;
+
+	changed_mask = ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK;
+	atc2603c_sgpio->cached_irq_wake_mask =
+		(atc2603c_sgpio->cached_irq_wake_mask & ~(1U << (data->hwirq + 2))) |
+		(on << (data->hwirq + 2)) | changed_mask;
+
+	if (on)
+		atc2603c_sgpio->wake_count++;
+	else
+		atc2603c_sgpio->wake_count--;
+	return 0;
+}
+
+static const struct irq_chip sc_atc2603c_sgpio_irq_chip_template = {
+	.name                   = "atc2603c-sgpio-irqc",
+	.irq_bus_lock           = _atc2603c_sgpio_irq_lock,
+	.irq_bus_sync_unlock    = _atc2603c_sgpio_irq_sync_unlock,
+	.irq_disable            = _atc2603c_sgpio_irq_disable,
+	.irq_enable             = _atc2603c_sgpio_irq_enable,
+	.irq_set_type           = _atc2603c_sgpio_irq_set_type,
+	.irq_set_wake           = _atc2603c_sgpio_irq_set_wake,
+};
+
+static int _atc2603c_sgpio_irq_map(struct irq_domain *h, unsigned int virq,
+			  irq_hw_number_t hw)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio = h->host_data;
+
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+
+	irq_set_chip_data(virq, atc2603c_sgpio);
+	irq_set_chip(virq, &atc2603c_sgpio->irq_chip);
+	irq_set_nested_thread(virq, 1);
+
+	/* ARM needs us to explicitly flag the IRQ as valid
+	 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+	set_irq_flags(virq, IRQF_VALID);
+#else
+	irq_set_noprobe(virq);
+#endif
+
+	return 0;
+}
+
+static const struct irq_domain_ops sc_atc2603c_sgpio_domain_ops = {
+	.map	= _atc2603c_sgpio_irq_map,
+	.xlate	= irq_domain_xlate_twocell,
+};
+
+static irqreturn_t _atc2603c_sgpio_irq_thread(int irq, void *udata)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio = udata;
+	uint irq_status, irq_mask, irq_mask_real;
+	uint hwirq;
+	int ret;
+
+	ATC2603C_SGPIO_ASSERT_VALID_DEV(atc2603c_sgpio);
+
+	ret = atc260x_reg_read(atc2603c_sgpio->atc260x, ATC2603C_PMU_SGPIO_CTL0);
+	if (ret < 0) {
+		dev_err(atc2603c_sgpio->dev, "%s: read status reg err, ret=%d\n",
+			__func__, ret);
+		return IRQ_NONE;
+	}
+	irq_status = ((uint)ret >> 9) & 0x7f;
+	irq_mask_real = ((uint)ret >> 2) & 0x7f;
+
+	irq_mask = (atc2603c_sgpio->cached_irq_en_mask &
+			~ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK) >> 2;
+	BUG_ON(irq_mask_real != irq_mask);
+
+	if ((irq_status & irq_mask) == 0) {
+		dev_err(atc2603c_sgpio->dev, "%s: empty IRQ, irq_status=0x%x irq_mask=0x%x\n",
+			__func__, irq_status, irq_mask);
+		return IRQ_NONE;
+	}
+	irq_status = irq_status & irq_mask;
+
+	/* clear all pending */
+	mutex_lock(&(atc2603c_sgpio->irq_bus_lock));
+	ret = atc260x_reg_wp_clrpnd(atc2603c_sgpio->atc260x,
+			ATC2603C_PMU_SGPIO_CTL0,
+			(0x7fU << 9),
+			(irq_status << 9));
+	mutex_unlock(&(atc2603c_sgpio->irq_bus_lock));
+	if (ret) {
+		dev_err(atc2603c_sgpio->dev, "%s: failed to clr pending, ret=%d\n",
+			__func__, ret);
+		return IRQ_NONE;
+	}
+
+	do {
+		hwirq = __ffs(irq_status);
+		handle_nested_irq(irq_find_mapping(atc2603c_sgpio->irq_domain, hwirq));
+		irq_status &= ~(1U << hwirq);
+	} while (irq_status != 0);
+
+	return IRQ_HANDLED;
+}
+
+static void _atc2603c_sgpio_dispose_all_irq_mappings(struct atc2603c_sgpio_dev *atc2603c_sgpio)
+{
+	uint hwirq, virq;
+
+	for (hwirq = 0; hwirq < ATC2603C_SGPIO_NR; hwirq++) {
+		virq = atc2603c_sgpio->irq_map_tbl[hwirq];
+		if (virq > 0) {
+			atc2603c_sgpio->irq_map_tbl[hwirq] = 0;
+			irq_dispose_mapping(virq);
+		}
+	}
+}
+
+static int _atc2603c_sgpio_create_all_irq_mappings(struct atc2603c_sgpio_dev *atc2603c_sgpio)
+{
+	uint hwirq, virq;
+
+	memset(atc2603c_sgpio->irq_map_tbl, 0, sizeof(atc2603c_sgpio->irq_map_tbl));
+
+	for (hwirq = 0; hwirq < ATC2603C_SGPIO_NR; hwirq++) {
+		virq = irq_create_mapping(atc2603c_sgpio->irq_domain, hwirq);
+		if (virq == 0) {
+			_atc2603c_sgpio_dispose_all_irq_mappings(atc2603c_sgpio);
+			dev_err(atc2603c_sgpio->dev,
+				"%s: failed to create IRQ mapping for hwirq %u\n",
+				__func__, hwirq);
+			return -ENXIO;
+		}
+		atc2603c_sgpio->irq_map_tbl[hwirq] = virq;
+		dev_info(atc2603c_sgpio->dev, "new IRQ mapping: hwirq%u -> virq%u\n",
+			hwirq, virq);
+	}
+
+	return 0;
+}
+
+static int _atc2603c_sgpio_irq_init(struct atc2603c_sgpio_dev *atc2603c_sgpio)
+{
+	struct atc260x_dev *atc260x;
+	int ret;
+
+	atc260x = atc2603c_sgpio->atc260x;
+
+	/* Mask all the interrupts by default */
+	/* Wake is disabled by default */
+	ret = atc260x_reg_write(atc260x, ATC2603C_PMU_SGPIO_CTL0, (0x7fU<<9) | 0);
+	ret |= atc260x_reg_write(atc260x, ATC2603C_PMU_SGPIO_CTL1, 0);
+	ret |= atc260x_reg_write(atc260x, ATC2603C_PMU_SGPIO_CTL2, 0);
+	if (ret) {
+		dev_err(atc2603c_sgpio->dev, "failed to init interrupt hardware\n");
+		return -EIO;
+	}
+	atc2603c_sgpio->cached_irq_en_mask = 0;
+	atc2603c_sgpio->cached_irq_trig_type = 0;
+	atc2603c_sgpio->cached_irq_wake_mask = 0;
+	atc2603c_sgpio->wake_count = 0;
+	atc2603c_sgpio->parent_irq_no_wake = 0; /* assume 0, will check later */
+
+	mutex_init(&(atc2603c_sgpio->irq_bus_lock));
+
+	atc2603c_sgpio->irq_chip = sc_atc2603c_sgpio_irq_chip_template;
+
+	atc2603c_sgpio->irq_domain = irq_domain_add_linear(
+			atc2603c_sgpio->dev->of_node,
+			ATC2603C_SGPIO_NR,
+			&sc_atc2603c_sgpio_domain_ops, atc2603c_sgpio);
+	if (!atc2603c_sgpio->irq_domain) {
+		dev_err(atc2603c_sgpio->dev, "failed to create IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	ret = _atc2603c_sgpio_create_all_irq_mappings(atc2603c_sgpio);
+	if (ret) {
+		irq_domain_remove(atc2603c_sgpio->irq_domain);
+		dev_err(atc2603c_sgpio->dev, "failed to create IRQ mappings\n");
+	}
+
+	dev_info(atc2603c_sgpio->dev, "root virq num : %u\n", atc2603c_sgpio->root_irq);
+	ret = devm_request_threaded_irq(
+			atc2603c_sgpio->dev,
+			atc2603c_sgpio->root_irq,
+			NULL, _atc2603c_sgpio_irq_thread,
+			IRQF_ONESHOT,
+			atc2603c_sgpio->irq_chip.name, atc2603c_sgpio);
+	if (ret != 0) {
+		_atc2603c_sgpio_dispose_all_irq_mappings(atc2603c_sgpio);
+		irq_domain_remove(atc2603c_sgpio->irq_domain);
+		dev_err(atc2603c_sgpio->dev, "failed to request root IRQ %d, ret=%d\n",
+			atc2603c_sgpio->root_irq, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void _atc2603c_sgpio_irq_exit(struct atc2603c_sgpio_dev *atc2603c_sgpio)
+{
+	devm_free_irq(atc2603c_sgpio->dev, atc2603c_sgpio->root_irq, atc2603c_sgpio);
+	_atc2603c_sgpio_dispose_all_irq_mappings(atc2603c_sgpio);
+	irq_domain_remove(atc2603c_sgpio->irq_domain);
+}
+
+
+/* GPIO function ------------------------------------------------------------ */
+
+static const struct atc2603c_sgpio_mfp_map sc_atc2603c_sgpio_mfp_map[] = {
+	/* mask        val */
+	{0x0003,     0x0001},    /* GPIO0 */
+	{0x001c,     0x0004},    /* GPIO1 */
+	{0x00e0,     0x0020},    /* GPIO2 */
+	{0x0300,     0x0100},    /* GPIO3 */
+	{0x0c00,     0x0000},    /* GPIO4 */
+	{0x3000,     0x0000},    /* GPIO5 */
+	{0xc000,     0x0000},    /* GPIO6 */
+};
+
+static inline struct atc2603c_sgpio_dev *_get_atc2603c_sgpio_dev(struct gpio_chip *chip)
+{
+	return dev_get_drvdata(chip->dev);
+}
+
+static int _atc2603c_sgpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	struct atc2603c_sgpio_mfp_map const *mfp_map;
+	uint mfp_reg_msk, mfp_reg_val;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return -EINVAL;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+	mfp_map = sc_atc2603c_sgpio_mfp_map;
+
+	mfp_reg_msk = (mfp_map[offset]).mask;
+	mfp_reg_val = (mfp_map[offset]).value;
+
+	/* once GPIO MFP set, no way (no need) to revert */
+	ret = atc260x_reg_setbits(atc2603c_sgpio->atc260x, ATC2603C_PMU_MUX_CTL0, mfp_reg_msk, mfp_reg_val);
+	if (ret)
+		return ret;
+
+	dev_info(atc2603c_sgpio->dev, "requested SGPIO #%u\n", offset);
+	return 0;
+}
+
+static int _atc2603c_sgpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return -EINVAL;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+
+	ret = atc260x_reg_read(atc2603c_sgpio->atc260x, ATC2603C_PMU_SGPIO_CTL3);
+	if (ret < 0)
+		return ret;
+	if (ret & (1U << (offset + 2U + ATC2603C_SGPIO_NR)))
+		return GPIOF_DIR_OUT;
+	if (ret & (1U << (offset + 2U)))
+		return GPIOF_DIR_IN;
+
+	return -ENXIO;
+}
+
+static int _atc2603c_sgpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return -EINVAL;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+
+	ret = atc260x_reg_setbits(atc2603c_sgpio->atc260x,
+		ATC2603C_PMU_SGPIO_CTL3,
+		(1U << (offset + 2U + ATC2603C_SGPIO_NR)) | (1U << (offset + 2U)),
+		(1U << (offset + 2U)));
+	return ret;
+}
+
+static int _atc2603c_sgpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return -EINVAL;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+
+	ret = atc260x_reg_read(atc2603c_sgpio->atc260x, ATC2603C_PMU_SGPIO_CTL4);
+	if (ret < 0)
+		return ret;
+
+	return (((uint)ret & (1U << offset)) != 0);
+}
+
+static int _atc2603c_sgpio_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return -EINVAL;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+
+	ret = atc260x_reg_setbits(atc2603c_sgpio->atc260x,
+		ATC2603C_PMU_SGPIO_CTL4,
+		(1U << offset),
+		((value != 0) ? (1U << offset) : 0));
+	if (ret == 0) {
+		ret = atc260x_reg_setbits(atc2603c_sgpio->atc260x,
+			ATC2603C_PMU_SGPIO_CTL3,
+			(1U << (offset + 2U + ATC2603C_SGPIO_NR)) | (1U << (offset + 2U)),
+			(1U << (offset + 2U + ATC2603C_SGPIO_NR)));
+	}
+	return ret;
+}
+
+static void _atc2603c_sgpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+
+	ret = atc260x_reg_setbits(atc2603c_sgpio->atc260x,
+		ATC2603C_PMU_SGPIO_CTL4,
+		(1U << offset),
+		((value != 0) ? (1U << offset) : 0));
+	if (ret) {
+		dev_err(atc2603c_sgpio->dev, "failed to set GPIO%u state to %d, ret=%d\n",
+			offset, value, ret);
+	}
+}
+
+static int _atc2603c_sgpio_find_irq_nr(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	if (offset >= ATC2603C_SGPIO_NR)
+		return -EINVAL;
+
+	atc2603c_sgpio = _get_atc2603c_sgpio_dev(chip);
+	ret = atc2603c_sgpio->irq_map_tbl[offset]; /* hw_gpio number is the hwirq number. */
+	if (ret <= 0)
+		return -ENOENT;
+	return ret;
+}
+
+static const struct gpio_chip sc_atc2603c_sgpio_chip_template = {
+	.label              = "atc2603c-sgpio-chip",
+	.owner              = THIS_MODULE,
+	.request            = _atc2603c_sgpio_request,
+	.free               = NULL,
+	.get_direction      = _atc2603c_sgpio_get_direction,
+	.direction_input    = _atc2603c_sgpio_direction_in,
+	.get                = _atc2603c_sgpio_get,
+	.direction_output   = _atc2603c_sgpio_direction_out,
+	.set                = _atc2603c_sgpio_set,
+	.to_irq             = _atc2603c_sgpio_find_irq_nr,
+	.set_debounce       = NULL,
+	.dbg_show           = NULL,
+	.can_sleep          = 1,
+};
+
+
+/* Driver ------------------------------------------------------------------- */
+
+static int atc2603c_sgpio_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x;
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	dev_info(&pdev->dev, "Probing...\n");
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	atc2603c_sgpio = devm_kzalloc(&pdev->dev, sizeof(*atc2603c_sgpio), GFP_KERNEL);
+	if (atc2603c_sgpio == NULL) {
+		dev_err(&pdev->dev, "no mem\n");
+		return -ENOMEM;
+	}
+	atc2603c_sgpio->dev = &pdev->dev;
+	atc2603c_sgpio->atc260x = atc260x;
+	atc2603c_sgpio->_obj_type_id = ATC2603C_SGPIO_OBJ_TYPE_ID;
+	platform_set_drvdata(pdev, atc2603c_sgpio);
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "No IRQ resource\n");
+		return -ENODEV;
+	}
+	atc2603c_sgpio->root_irq = ret;
+
+	ret = _atc2603c_sgpio_irq_init(atc2603c_sgpio);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to init SGPIO interrupt controller, ret=%d\n", ret);
+		return ret;
+	}
+
+	atc2603c_sgpio->gpio_chip = sc_atc2603c_sgpio_chip_template;
+	atc2603c_sgpio->gpio_chip.dev = &pdev->dev;
+	atc2603c_sgpio->gpio_chip.base = -1; /* use a dynamic range, user get GPIO via DTS bindings */
+	atc2603c_sgpio->gpio_chip.ngpio = ATC2603C_SGPIO_NR;
+
+	ret = gpiochip_add(&atc2603c_sgpio->gpio_chip);
+	if (ret < 0) {
+		_atc2603c_sgpio_irq_exit(atc2603c_sgpio);
+		dev_err(&pdev->dev, "Failed to register gpiochip, ret=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int atc2603c_sgpio_remove(struct platform_device *pdev)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	int ret;
+
+	atc2603c_sgpio = platform_get_drvdata(pdev);
+
+	ret = gpiochip_remove(&atc2603c_sgpio->gpio_chip);
+	if (ret == 0)
+		dev_err(&pdev->dev, "Failed to unregister gpiochip, ret=%d\n", ret);
+
+	_atc2603c_sgpio_irq_exit(atc2603c_sgpio);
+
+	atc2603c_sgpio->_obj_type_id = 0;
+	return ret;
+}
+
+static int atc2603c_sgpio_suspend_late(struct device *dev)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+	uint changed_mask;
+	int ret;
+
+	atc2603c_sgpio = dev_get_drvdata(dev);
+	dev_info(atc2603c_sgpio->dev, "enter suspend\n");
+
+	mutex_lock(&(atc2603c_sgpio->irq_bus_lock));
+
+	changed_mask = ATC2603C_SGPIO_CACHE_REG_CHANGED_MSK;
+	atc2603c_sgpio->cached_irq_wake_mask |= changed_mask; /* mark as changed */
+	atc2603c_sgpio->cached_irq_trig_type |= changed_mask;
+	atc2603c_sgpio->cached_irq_en_mask   |= changed_mask;
+
+	ret = atc260x_reg_wp_setbits(atc2603c_sgpio->atc260x,
+			ATC2603C_PMU_SGPIO_CTL0,
+			(0x7fU << 9),
+			(0x7fU << 2),
+			0);
+	if (ret)
+		dev_err(atc2603c_sgpio->dev, "%s: failed to disable all IRQ\n", __func__);
+	return ret;
+}
+
+static int atc2603c_sgpio_resume_early(struct device *dev)
+{
+	struct atc2603c_sgpio_dev *atc2603c_sgpio;
+
+	atc2603c_sgpio = dev_get_drvdata(dev);
+	dev_info(atc2603c_sgpio->dev, "resume\n");
+
+	_atc2603c_sgpio_sync_cached_regs(atc2603c_sgpio);
+
+	mutex_unlock(&(atc2603c_sgpio->irq_bus_lock));
+	return 0;
+}
+
+static const struct dev_pm_ops sc_atc2603c_sgpio_pm_ops = {
+	.suspend_late  = atc2603c_sgpio_suspend_late,
+	.resume_early  = atc2603c_sgpio_resume_early,
+	.freeze_late   = atc2603c_sgpio_suspend_late,
+	.thaw_early    = atc2603c_sgpio_resume_early,
+	.poweroff_late = atc2603c_sgpio_suspend_late,
+	.restore_early = atc2603c_sgpio_resume_early,
+};
+
+static const struct of_device_id atc2603c_sgpio_match[] = {
+	{ .compatible = "actions,atc2603c-sgpio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc2603c_sgpio_match);
+
+static struct platform_driver atc2603c_sgpio_driver = {
+	.probe      = atc2603c_sgpio_probe,
+	.remove     = atc2603c_sgpio_remove,
+	.driver = {
+		.name = "atc2603c-sgpio",
+		.owner = THIS_MODULE,
+		.pm = &sc_atc2603c_sgpio_pm_ops,
+		.of_match_table = atc2603c_sgpio_match,
+	},
+};
+
+static int __init atc2603c_sgpio_init(void)
+{
+	return platform_driver_register(&atc2603c_sgpio_driver);
+}
+subsys_initcall(atc2603c_sgpio_init);
+
+static void __exit atc2603c_sgpio_exit(void)
+{
+	platform_driver_unregister(&atc2603c_sgpio_driver);
+}
+module_exit(atc2603c_sgpio_exit);
+
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("SGPIO interface for ATC260X PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc2603c-sgpio");
diff --git a/drivers/gpio/gpio-atc260x.c b/drivers/gpio/gpio-atc260x.c
new file mode 100755
index 0000000..a1bb178
--- /dev/null
+++ b/drivers/gpio/gpio-atc260x.c
@@ -0,0 +1,421 @@
+/*
+ * atc260x-gpio.c  --  gpiolib support for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+struct atc260x_gpio_mfp_map {
+	u16 mfp_reg;
+	u16 mask;
+	u16 value;
+};
+
+struct atc260x_gpio_hwinfo {
+	const struct atc260x_gpio_mfp_map *mfp_map_tbl;
+	u16 reg_outen_tbl[4];
+	u16 reg_inen_tbl[4];
+	u16 reg_data_tbl[4];
+	u16 gpio_nr_per_reg;
+	u16 gpio_nr;
+};
+
+struct atc260x_gpio_dev {
+	struct device *dev;
+	struct atc260x_dev *atc260x;
+	const struct atc260x_gpio_hwinfo *hwinfo;
+	struct gpio_chip gpio_chip;
+};
+
+
+/* HWinfo for atc2603a ------------------------------------------------------ */
+
+static const struct atc260x_gpio_mfp_map sc_atc2603a_gpio_mfp_map[] = {
+	/* reg                mask        val */
+	{ATC2603A_MFP_CTL1,   0x0c00,     0x0800},    /* GPIO0 */
+	{ATC2603A_MFP_CTL1,   0x0c00,     0x0800},    /* GPIO1 */
+	{ATC2603A_MFP_CTL1,   0x3000,     0x2000},    /* GPIO2 */
+	{ATC2603A_MFP_CTL1,   0x3000,     0x2000},    /* GPIO3 */
+	{ATC2603A_MFP_CTL1,   0xc000,     0x8000},    /* GPIO4 */
+	{ATC2603A_MFP_CTL1,   0xc000,     0x8000},    /* GPIO5 */
+	{ATC2603A_MFP_CTL0,   0x0010,     0x0010},    /* GPIO6 */
+	{ATC2603A_MFP_CTL0,   0x0010,     0x0010},    /* GPIO7 */
+	{ATC2603A_MFP_CTL0,   0x000c,     0x0004},    /* GPIO8 */
+	{ATC2603A_MFP_CTL0,   0x0020,     0x0020},    /* GPIO9 */
+	{ATC2603A_MFP_CTL0,   0x0020,     0x0020},    /* GPIO10 */
+	{ATC2603A_MFP_CTL0,   0x0020,     0x0020},    /* GPIO11 */
+	{ATC2603A_MFP_CTL1,   0x0001,     0x0001},    /* GPIO12 */
+	{ATC2603A_MFP_CTL1,   0x0002,     0x0002},    /* GPIO13 */
+	{ATC2603A_MFP_CTL1,   0x0004,     0x0004},    /* GPIO14 */
+	{ATC2603A_MFP_CTL1,   0x0008,     0x0008},    /* GPIO15 */
+	{ATC2603A_MFP_CTL1,   0x0010,     0x0010},    /* GPIO16 */
+	{ATC2603A_MFP_CTL1,   0x00e0,          0},    /* GPIO17, reserved for TP */
+	{ATC2603A_MFP_CTL1,   0x00e0,          0},    /* GPIO18, reserved for TP */
+	{ATC2603A_MFP_CTL1,   0x00e0,          0},    /* GPIO19, reserved for TP */
+	{ATC2603A_MFP_CTL1,   0x00e0,          0},    /* GPIO20, reserved for TP */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO21 */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO22 */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO23 */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO24 */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO25 */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO26 */
+	{ATC2603A_MFP_CTL0,   0x0003,     0x0001},    /* GPIO27 */
+	{ATC2603A_MFP_CTL0,   0x0040,     0x0040},    /* GPIO28 */
+	{ATC2603A_MFP_CTL0,   0x0080,     0x0080},    /* GPIO29 */
+	{                0,        0,          0},    /* GPIO30, dedicated GPIO */
+	{                0,        0,          0},    /* GPIO31, dedicated GPIO */
+};
+
+static const struct atc260x_gpio_hwinfo sc_atc2603a_gpio_hwinfo = {
+	.mfp_map_tbl = sc_atc2603a_gpio_mfp_map,
+	.reg_outen_tbl = {ATC2603A_GPIO_OUTEN0, ATC2603A_GPIO_OUTEN1},
+	.reg_inen_tbl  = {ATC2603A_GPIO_INEN0, ATC2603A_GPIO_INEN1},
+	.reg_data_tbl  = {ATC2603A_GPIO_DAT0, ATC2603A_GPIO_DAT1},
+	.gpio_nr_per_reg = 16,
+	.gpio_nr = 32,
+};
+
+/* HWinfo for atc2603c ------------------------------------------------------ */
+
+static const struct atc260x_gpio_mfp_map sc_atc2603c_gpio_mfp_map[] = {
+	/* reg                mask        val */
+	{ATC2603C_MFP_CTL,    0x0060,     0x0020},    /* GPIO0 */
+	{ATC2603C_MFP_CTL,    0x0060,     0x0020},    /* GPIO1 */
+	{ATC2603C_MFP_CTL,    0x0018,     0x0008},    /* GPIO2 */
+	{ATC2603C_MFP_CTL,    0x0180,     0x0080},    /* GPIO3 */
+	{ATC2603C_MFP_CTL,    0x0180,     0x0080},    /* GPIO4 */
+	{ATC2603C_MFP_CTL,    0x0180,     0x0080},    /* GPIO5 */
+	{               0,         0,          0},    /* GPIO6 */
+	{               0,         0,          0},    /* GPIO7 */
+};
+
+static const struct atc260x_gpio_hwinfo sc_atc2603c_gpio_hwinfo = {
+	.mfp_map_tbl = sc_atc2603c_gpio_mfp_map,
+	.reg_outen_tbl = {ATC2603C_GPIO_OUTEN, },
+	.reg_inen_tbl  = {ATC2603C_GPIO_INEN, },
+	.reg_data_tbl  = {ATC2603C_GPIO_DAT, },
+	.gpio_nr_per_reg = 8,
+	.gpio_nr = 8,
+};
+
+/* HWinfo for atc2609a ------------------------------------------------------ */
+
+static const struct atc260x_gpio_mfp_map sc_atc2609a_gpio_mfp_map[] = {
+	/* reg                mask        val */
+	{ATC2609A_MFP_CTL,    0x0060,     0x0020},    /* GPIO0 */
+	{ATC2609A_MFP_CTL,    0x0060,     0x0020},    /* GPIO1 */
+	{ATC2609A_MFP_CTL,    0x0018,     0x0008},    /* GPIO2 */
+	{ATC2609A_MFP_CTL,    0x0180,     0x0080},    /* GPIO3 */
+	{ATC2609A_MFP_CTL,    0x0180,     0x0080},    /* GPIO4 */
+	{ATC2609A_MFP_CTL,    0x0180,     0x0080},    /* GPIO5 */
+	{               0,         0,          0},    /* GPIO6 */
+	{               0,         0,          0},    /* GPIO7 */
+};
+
+static const struct atc260x_gpio_hwinfo sc_atc2609a_gpio_hwinfo = {
+	.mfp_map_tbl = sc_atc2609a_gpio_mfp_map,
+	.reg_outen_tbl = {ATC2609A_GPIO_OUTEN, },
+	.reg_inen_tbl  = {ATC2609A_GPIO_INEN, },
+	.reg_data_tbl  = {ATC2609A_GPIO_DAT, },
+	.gpio_nr_per_reg = 8,
+	.gpio_nr = 8,
+};
+
+
+/* code --------------------------------------------------------------------- */
+
+static inline struct atc260x_gpio_dev *_get_atc260x_gpio_dev(struct gpio_chip *chip)
+{
+	return dev_get_drvdata(chip->dev);
+}
+
+static int _atc260x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	struct atc260x_gpio_hwinfo const *hwinfo;
+	uint mfp_reg, mfp_reg_msk, mfp_reg_val;
+	int ret;
+
+	atc260x_gpio = _get_atc260x_gpio_dev(chip);
+	hwinfo = atc260x_gpio->hwinfo;
+
+	if (offset >= hwinfo->gpio_nr)
+		return -EINVAL;
+
+	if (hwinfo->mfp_map_tbl == NULL) { /* need to touch MFP ? */
+		mfp_reg_msk = (hwinfo->mfp_map_tbl[offset]).mask;
+		if (mfp_reg_msk != 0) { /* need to touch MFP ? */
+			mfp_reg = (hwinfo->mfp_map_tbl[offset]).mfp_reg;
+			mfp_reg_val = (hwinfo->mfp_map_tbl[offset]).value;
+
+			/* once GPIO MFP set, no way (no need) to revert */
+			ret = atc260x_reg_setbits(atc260x_gpio->atc260x, mfp_reg, mfp_reg_msk, mfp_reg_val);
+			if (ret)
+				return ret;
+		}
+	}
+
+	dev_info(atc260x_gpio->dev, "requested GPIO #%u\n", offset);
+	return 0;
+}
+
+static int _atc260x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	struct atc260x_gpio_hwinfo const *hwinfo;
+	uint reg_bank, reg_msk;
+	int ret;
+
+	atc260x_gpio = _get_atc260x_gpio_dev(chip);
+	hwinfo = atc260x_gpio->hwinfo;
+
+	if (offset >= hwinfo->gpio_nr)
+		return -EINVAL;
+
+	reg_bank = offset / hwinfo->gpio_nr_per_reg;
+	reg_msk = 1U << (offset % hwinfo->gpio_nr_per_reg);
+
+	ret = atc260x_reg_read(atc260x_gpio->atc260x, hwinfo->reg_outen_tbl[reg_bank]);
+	if (ret < 0)
+		return ret;
+	if (ret & reg_msk)
+		return GPIOF_DIR_OUT;
+
+	ret = atc260x_reg_read(atc260x_gpio->atc260x, hwinfo->reg_inen_tbl[reg_bank]);
+	if (ret < 0)
+		return ret;
+	if (ret & reg_msk)
+		return GPIOF_DIR_IN;
+
+	return -ENXIO;
+}
+
+static int _atc260x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	struct atc260x_gpio_hwinfo const *hwinfo;
+	uint reg_bank, reg_msk;
+	int ret;
+
+	atc260x_gpio = _get_atc260x_gpio_dev(chip);
+	hwinfo = atc260x_gpio->hwinfo;
+
+	if (offset >= hwinfo->gpio_nr)
+		return -EINVAL;
+
+	reg_bank = offset / hwinfo->gpio_nr_per_reg;
+	reg_msk = 1U << (offset % hwinfo->gpio_nr_per_reg);
+
+	ret = atc260x_reg_setbits(atc260x_gpio->atc260x,
+			hwinfo->reg_inen_tbl[reg_bank], reg_msk, reg_msk);
+	if (ret == 0) {
+		ret = atc260x_reg_setbits(atc260x_gpio->atc260x,
+				hwinfo->reg_outen_tbl[reg_bank], reg_msk, 0);
+	}
+	return ret;
+}
+
+static int _atc260x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	struct atc260x_gpio_hwinfo const *hwinfo;
+	uint reg_bank, reg_msk;
+	int ret;
+
+	atc260x_gpio = _get_atc260x_gpio_dev(chip);
+	hwinfo = atc260x_gpio->hwinfo;
+
+	if (offset >= hwinfo->gpio_nr)
+		return -EINVAL;
+
+	reg_bank = offset / hwinfo->gpio_nr_per_reg;
+	reg_msk = 1U << (offset % hwinfo->gpio_nr_per_reg);
+
+	ret = atc260x_reg_read(atc260x_gpio->atc260x, hwinfo->reg_data_tbl[reg_bank]);
+	if (ret < 0)
+		return ret;
+
+	return ((ret & reg_msk) != 0);
+}
+
+static int _atc260x_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	struct atc260x_gpio_hwinfo const *hwinfo;
+	uint reg_bank, reg_msk;
+	int ret;
+
+	atc260x_gpio = _get_atc260x_gpio_dev(chip);
+	hwinfo = atc260x_gpio->hwinfo;
+
+	if (offset >= hwinfo->gpio_nr)
+		return -EINVAL;
+
+	reg_bank = offset / hwinfo->gpio_nr_per_reg;
+	reg_msk = 1U << (offset % hwinfo->gpio_nr_per_reg);
+
+	ret = atc260x_reg_setbits(atc260x_gpio->atc260x,
+			hwinfo->reg_data_tbl[reg_bank],
+			reg_msk,
+			((value != 0) ? reg_msk : 0));
+	if (ret == 0) {
+		ret = atc260x_reg_setbits(atc260x_gpio->atc260x,
+				hwinfo->reg_outen_tbl[reg_bank], reg_msk, reg_msk);
+		if (ret == 0) {
+			ret = atc260x_reg_setbits(atc260x_gpio->atc260x,
+					hwinfo->reg_inen_tbl[reg_bank], reg_msk, 0);
+		}
+	}
+	return ret;
+}
+
+static void _atc260x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	struct atc260x_gpio_hwinfo const *hwinfo;
+	uint reg_bank, reg_msk;
+	int ret;
+
+	atc260x_gpio = _get_atc260x_gpio_dev(chip);
+	hwinfo = atc260x_gpio->hwinfo;
+
+	if (offset >= hwinfo->gpio_nr)
+		return;
+
+	reg_bank = offset / hwinfo->gpio_nr_per_reg;
+	reg_msk = 1U << (offset % hwinfo->gpio_nr_per_reg);
+
+	ret = atc260x_reg_setbits(atc260x_gpio->atc260x,
+			hwinfo->reg_data_tbl[reg_bank],
+			reg_msk,
+			((value != 0) ? reg_msk : 0));
+	if (ret) {
+		dev_err(atc260x_gpio->dev, "failed to set GPIO%u state to %d, ret=%d\n",
+			offset, value, ret);
+	}
+}
+
+static const struct gpio_chip sc_atc260x_gpiochip_template = {
+	.label              = "atc260x-gpio-chip",
+	.owner              = THIS_MODULE,
+	.request            = _atc260x_gpio_request,
+	.free               = NULL,
+	.get_direction      = _atc260x_gpio_get_direction,
+	.direction_input    = _atc260x_gpio_direction_in,
+	.get                = _atc260x_gpio_get,
+	.direction_output   = _atc260x_gpio_direction_out,
+	.set                = _atc260x_gpio_set,
+	.to_irq             = NULL,
+	.set_debounce       = NULL,
+	.dbg_show           = NULL,
+	.can_sleep          = 1,
+};
+
+static int atc260x_gpio_probe(struct platform_device *pdev)
+{
+	static const struct atc260x_gpio_hwinfo * const sc_hwinfo_tbl[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = &sc_atc2603a_gpio_hwinfo,
+		[ATC260X_ICTYPE_2603C] = &sc_atc2603c_gpio_hwinfo,
+		[ATC260X_ICTYPE_2609A] = &sc_atc2609a_gpio_hwinfo,
+	};
+	struct atc260x_dev *atc260x;
+	struct atc260x_gpio_dev *atc260x_gpio;
+	uint ic_type;
+	int ret;
+
+	dev_info(&pdev->dev, "Probing...\n");
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	atc260x_gpio = devm_kzalloc(&pdev->dev, sizeof(*atc260x_gpio), GFP_KERNEL);
+	if (atc260x_gpio == NULL) {
+		dev_err(&pdev->dev, "no mem\n");
+		return -ENOMEM;
+	}
+
+	atc260x_gpio->dev = &pdev->dev;
+	atc260x_gpio->atc260x = atc260x;
+
+	ic_type = atc260x_get_ic_type(atc260x);
+	BUG_ON(ic_type >= ARRAY_SIZE(sc_hwinfo_tbl));
+	atc260x_gpio->hwinfo = sc_hwinfo_tbl[ic_type];
+
+	atc260x_gpio->gpio_chip = sc_atc260x_gpiochip_template;
+	atc260x_gpio->gpio_chip.dev = &pdev->dev;
+	atc260x_gpio->gpio_chip.base = -1; /* use a dynamic range, user get GPIO via DTS bindings */
+	atc260x_gpio->gpio_chip.ngpio = atc260x_gpio->hwinfo->gpio_nr;
+
+	ret = gpiochip_add(&atc260x_gpio->gpio_chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register gpiochip, ret=%d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, atc260x_gpio);
+
+	return 0;
+}
+
+static int atc260x_gpio_remove(struct platform_device *pdev)
+{
+	struct atc260x_gpio_dev *atc260x_gpio;
+	int ret;
+
+	atc260x_gpio = platform_get_drvdata(pdev);
+
+	ret = gpiochip_remove(&atc260x_gpio->gpio_chip);
+	if (ret == 0)
+		dev_err(&pdev->dev, "Failed to unregister gpiochip, ret=%d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id atc260x_gpio_match[] = {
+	{ .compatible = "actions,atc2603a-gpio", },
+	{ .compatible = "actions,atc2603c-gpio", },
+	{ .compatible = "actions,atc2609a-gpio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_gpio_match);
+
+static struct platform_driver atc260x_gpio_driver = {
+	.probe      = atc260x_gpio_probe,
+	.remove     = atc260x_gpio_remove,
+	.driver = {
+		.name = "atc260x-gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = atc260x_gpio_match,
+	},
+};
+
+static int __init atc260x_gpio_init(void)
+{
+	return platform_driver_register(&atc260x_gpio_driver);
+}
+subsys_initcall(atc260x_gpio_init);
+
+static void __exit atc260x_gpio_exit(void)
+{
+	platform_driver_unregister(&atc260x_gpio_driver);
+}
+module_exit(atc260x_gpio_exit);
+
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("GPIO interface for ATC260X PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-gpio");
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
old mode 100644
new mode 100755
index 25d9e72..31b7d18
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1698,6 +1698,16 @@ config SENSORS_WM831X
 	  This driver can also be built as a module.  If so, the module
 	  will be called wm831x-hwmon.
 
+config SENSORS_ATC260X
+	tristate "atc260x PMICs"
+	depends on MFD_ATC260X
+	help
+	  If you say yes here you get support for the hardware
+	  monitoring functionality of the actions atc260x series of PMICs.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called atc260x_hwmon.
+
 config SENSORS_WM8350
 	tristate "Wolfson Microelectronics WM835x"
 	depends on MFD_WM8350
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
old mode 100644
new mode 100755
index b4a40f1..ee0b55a
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
 obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_ATC260X)	+= atc260x-hwmon.o
 
 obj-$(CONFIG_PMBUS)		+= pmbus/
 
diff --git a/drivers/hwmon/atc260x-hwmon.c b/drivers/hwmon/atc260x-hwmon.c
new file mode 100755
index 0000000..0ee4913
--- /dev/null
+++ b/drivers/hwmon/atc260x-hwmon.c
@@ -0,0 +1,235 @@
+/*
+ * atc260x-hwmon.c  --  hardware monitoring for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/atc260x/atc260x.h>
+
+struct atc260x_hwmon_dev {
+	struct device *dev;
+	struct atc260x_dev *atc260x;
+	struct device *classdev;
+};
+
+static const char * sc_atc260x_hwmon_alt_ch_name_tbl[][2] = {
+	{"BATV",        "bat_voltage" },
+	{"BATI",        "bat_current" },
+	{"VBUSV",       "vbus_voltage" },
+	{"VBUSI",       "vbus_current" },
+	{"SYSPWRV",     "syspower_voltage" },
+	{"WALLV",       "wall_voltage" },
+	{"WALLI",       "wall_current" },
+	{"CHGI",        "charge_current" },
+	{"IREF",        "current_ref" },
+	{"REMCON",      "remote_control" },
+	{"ICTEMP",      "ic_temperature" },
+	{"BAKBATV",     "backupbat_voltage" },
+	{"AUX0",        "aux0" },
+	{"AUX1",        "aux1" },
+	{"AUX2",        "aux2" },
+	{"AUX3",        "aux3" },
+	{"ICM",         "icm_current" },
+	{"SVCC",        "svcc_voltage" },
+};
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	strcpy(buf, "atc260x\n");
+	return strlen(buf);
+}
+
+static ssize_t show_value(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct atc260x_hwmon_dev *hwmon = dev_get_drvdata(dev);
+	struct sensor_device_attribute *sen_attr;
+	int channel, ret;
+	s32 tr_value;
+	const char *ch_unit_name;
+
+	sen_attr = to_sensor_dev_attr(attr);
+	channel = sen_attr->index;
+	if (channel < 0) {
+		const char *core_ch_name;
+		uint i;
+		for (i = 0; i < ARRAY_SIZE(sc_atc260x_hwmon_alt_ch_name_tbl); i++) {
+			if (strcmp(sen_attr->dev_attr.attr.name,
+					sc_atc260x_hwmon_alt_ch_name_tbl[i][1]) == 0) {
+				core_ch_name = sc_atc260x_hwmon_alt_ch_name_tbl[i][0];
+				channel = atc260x_auxadc_find_chan(hwmon->atc260x, core_ch_name);
+				sen_attr->index = channel; /* save it. */
+				break;
+			}
+		}
+	}
+
+	if (channel < 0) {
+		strcpy(buf, "<channel not found>\n");
+		return strlen(buf);
+	}
+
+	ret = atc260x_auxadc_get_translated(hwmon->atc260x, channel, &tr_value);
+	if (ret != 0) {
+		return scnprintf(buf, PAGE_SIZE, "<translate error, ret=%d>\n", ret);
+	} else {
+		ch_unit_name = atc260x_auxadc_channel_unit_name(hwmon->atc260x, channel);
+		return scnprintf(buf, PAGE_SIZE, "%d %s\n", tr_value, ch_unit_name);
+	}
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static SENSOR_DEVICE_ATTR(wall_voltage, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(vbus_voltage, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(bat_voltage, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(syspower_voltage, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(backupbat_voltage, S_IRUGO, show_value, NULL, -1);
+
+static SENSOR_DEVICE_ATTR(wall_current, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(vbus_current, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(bat_current, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(charge_current, S_IRUGO, show_value, NULL, -1);
+
+static SENSOR_DEVICE_ATTR(current_ref, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(ic_temperature, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(remote_control, S_IRUGO, show_value, NULL, -1);
+
+static SENSOR_DEVICE_ATTR(aux0, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(aux1, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(aux2, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(aux3, S_IRUGO, show_value, NULL, -1);
+
+static SENSOR_DEVICE_ATTR(icm_current, S_IRUGO, show_value, NULL, -1);
+static SENSOR_DEVICE_ATTR(svcc_voltage, S_IRUGO, show_value, NULL, -1);
+
+static struct attribute *atc260x_attributes[] = {
+	&dev_attr_name.attr,
+
+	&sensor_dev_attr_wall_voltage.dev_attr.attr,
+	&sensor_dev_attr_vbus_voltage.dev_attr.attr,
+	&sensor_dev_attr_bat_voltage.dev_attr.attr,
+	&sensor_dev_attr_syspower_voltage.dev_attr.attr,
+	&sensor_dev_attr_backupbat_voltage.dev_attr.attr,
+
+	&sensor_dev_attr_wall_current.dev_attr.attr,
+	&sensor_dev_attr_vbus_current.dev_attr.attr,
+	&sensor_dev_attr_bat_current.dev_attr.attr,
+	&sensor_dev_attr_charge_current.dev_attr.attr,
+
+	&sensor_dev_attr_current_ref.dev_attr.attr,
+	&sensor_dev_attr_ic_temperature.dev_attr.attr,
+	&sensor_dev_attr_remote_control.dev_attr.attr,
+
+	&sensor_dev_attr_aux0.dev_attr.attr,
+	&sensor_dev_attr_aux1.dev_attr.attr,
+	&sensor_dev_attr_aux2.dev_attr.attr,
+	&sensor_dev_attr_aux3.dev_attr.attr,
+
+	&sensor_dev_attr_icm_current.dev_attr.attr,
+	&sensor_dev_attr_svcc_voltage.dev_attr.attr,
+
+	NULL
+};
+
+static const struct attribute_group atc260x_attr_group = {
+	.attrs  = atc260x_attributes,
+};
+
+static int atc260x_hwmon_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x = dev_get_drvdata(pdev->dev.parent);
+	struct atc260x_hwmon_dev *hwmon;
+	int ret;
+
+	dev_info(&pdev->dev, "Probing %s\n", pdev->name);
+
+	hwmon = devm_kzalloc(&pdev->dev, sizeof(struct atc260x_hwmon_dev), GFP_KERNEL);
+	if (!hwmon)
+		return -ENOMEM;
+
+	hwmon->dev = &pdev->dev;
+	hwmon->atc260x = atc260x;
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &atc260x_attr_group);
+	if (ret)
+		goto err;
+
+	hwmon->classdev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon->classdev)) {
+		ret = PTR_ERR(hwmon->classdev);
+		goto err_sysfs;
+	}
+
+	platform_set_drvdata(pdev, hwmon);
+	return 0;
+
+err_sysfs:
+	sysfs_remove_group(&pdev->dev.kobj, &atc260x_attr_group);
+err:
+	return ret;
+}
+
+static int atc260x_hwmon_remove(struct platform_device *pdev)
+{
+	struct atc260x_hwmon_dev *hwmon = platform_get_drvdata(pdev);
+	hwmon_device_unregister(hwmon->classdev);
+	sysfs_remove_group(&pdev->dev.kobj, &atc260x_attr_group);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static const struct of_device_id atc260x_hwmon_match[] = {
+	{ .compatible = "actions,atc2603a-hwmon", },
+	{ .compatible = "actions,atc2603c-hwmon", },
+	{ .compatible = "actions,atc2609a-hwmon", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_hwmon_match);
+
+static struct platform_driver atc260x_hwmon_driver = {
+	.probe = atc260x_hwmon_probe,
+	.remove = atc260x_hwmon_remove,
+	.driver = {
+		.name = "atc260x-hwmon",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(atc260x_hwmon_match),
+	},
+};
+
+module_platform_driver(atc260x_hwmon_driver)
+
+/*static int __init atc260x_hwmon_init(void) */
+/*{ */
+/*    return platform_driver_register(&atc260x_hwmon_driver); */
+/*} */
+/*//subsys_initcall(atc260x_hwmon_init); */
+/*late_initcall(atc260x_hwmon_init); */
+/* */
+/*static void __exit atc260x_hwmon_exit(void) */
+/*{ */
+/*    platform_driver_unregister(&atc260x_hwmon_driver); */
+/*} */
+/*module_exit(atc260x_hwmon_exit); */
+
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("ATC260X Hardware Monitoring");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-hwmon");
+
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
old mode 100644
new mode 100755
index 106fbac..03d5355
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -638,6 +638,28 @@ config KEYBOARD_TC3589X
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called tc3589x-keypad.
+	  
+config KEYBOARD_ATC260X_ADCKEY
+	tristate "Actions atc260x ADC key support"
+	depends on MFD_ATC260X
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you want to use the adc keypad controller on
+	  atc260x PMIC.
+
+config KEYBOARD_ATC260X_ONOFF
+	tristate "Actions atc260x ON/OFF key support"
+	depends on MFD_ATC260X
+	help
+	  Say Y here if you want to use the on/off keypad on
+	  atc260x PMIC.
+
+config KEYBOARD_ATC260X_IRKEY
+        tristate "Actions atc260x IRKEY key support"
+        depends on MFD_ATC260X
+        help
+          Say Y here if you want to use the ir keypad on
+          atc260x PMIC
 
 config KEYBOARD_TWL4030
 	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
old mode 100644
new mode 100755
index df28d55..4b7205c
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -62,3 +62,6 @@ obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
 obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
+obj-$(CONFIG_KEYBOARD_ATC260X_ADCKEY)	+= atc260x-adckeypad.o
+obj-$(CONFIG_KEYBOARD_ATC260X_ONOFF)	+= atc260x-onoff.o
+obj-$(CONFIG_KEYBOARD_ATC260X_IRKEY)    += atc260x-irkeypad.o
diff --git a/drivers/input/keyboard/atc260x-adckeypad.c b/drivers/input/keyboard/atc260x-adckeypad.c
new file mode 100755
index 0000000..262b343
--- /dev/null
+++ b/drivers/input/keyboard/atc260x-adckeypad.c
@@ -0,0 +1,505 @@
+/*
+ * Asoc adc keypad driver
+ *
+ * Copyright (C) 2011 Actions Semiconductor, Inc
+ * Author:  chenbo <chenbo at actions-semi.com>
+ *
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <linux/fb.h>
+/*#include <mach/gl5203_gpio.h> */
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+#define ADCKEYPAD_DEBUG     0
+
+#define KEY_VAL_INIT            KEY_UP
+#define KEY_VAL_HOLD            SW_RADIO
+
+
+static const unsigned int left_adc[9] = {
+	0x00, 0x32, 0x97, 0xfb, 0x15f, 0x1c3, 0x24e, 0x2b3, 0x317
+};
+static const unsigned int right_adc[9] = {
+	0x00, 0x96, 0xfa, 0x15e, 0x1c2, 0x226, 0x2b2, 0x316, 0x400
+};
+static const unsigned int key_val[9] = {
+	KEY_HOME, KEY_MENU, KEY_VOLUMEUP, KEY_VOLUMEDOWN,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_UP
+};
+
+
+struct adc_key {
+unsigned int min_adc_val;   /*! min adc sample value */
+unsigned int max_adc_val;   /*! max adc sample value */
+unsigned int keyval;        /*! report key value */
+};
+
+struct atc260x_adckeypad_dev {
+	struct device *dev;
+	struct atc260x_dev *atc260x;
+	struct input_polled_dev *poll_dev;
+
+	unsigned int auxadc_channel;
+
+	unsigned int *adc_buffer;
+
+	unsigned int *left_adc_val;
+	unsigned int *right_adc_val;
+	unsigned int *key_values;
+
+	unsigned int filter_dep;
+	unsigned int variance;
+
+	unsigned int keymapsize;
+	unsigned int old_key_val;
+	unsigned int key_val;
+	unsigned int filter_index;
+};
+
+static inline unsigned int atc260x_adckeypad_convert(unsigned int adc_val,
+	struct atc260x_adckeypad_dev *atc260x_adckeypad)
+{
+	unsigned int i;
+	unsigned int key_val = KEY_RESERVED;
+
+	for (i = 0; i < atc260x_adckeypad->keymapsize; i++) {
+		if ((adc_val >= *(atc260x_adckeypad->left_adc_val + i))
+			&& (adc_val <= *(atc260x_adckeypad->right_adc_val + i))) {
+			key_val = *(atc260x_adckeypad->key_values + i);
+			break;
+		}
+	}
+	return key_val;
+}
+
+static void atc260x_adckeypad_report(struct input_dev *input_dev,
+	struct atc260x_adckeypad_dev *atc260x_adckeypad)
+{
+	unsigned int changed;
+
+	changed = atc260x_adckeypad->old_key_val ^ atc260x_adckeypad->key_val;
+
+	if (changed) {
+		if (atc260x_adckeypad->key_val != KEY_RESERVED) {
+			dev_info(atc260x_adckeypad->dev, "key_code=%d val=1\n",
+				atc260x_adckeypad->key_val);
+			input_report_key(input_dev, atc260x_adckeypad->key_val, 1);
+			input_sync(input_dev);
+		}
+		if (atc260x_adckeypad->old_key_val != KEY_RESERVED) {
+			dev_info(atc260x_adckeypad->dev, "key_code=%d val=0\n",
+				atc260x_adckeypad->old_key_val);
+			input_report_key(input_dev, atc260x_adckeypad->old_key_val, 0);
+			input_sync(input_dev);
+		}
+		atc260x_adckeypad->old_key_val = atc260x_adckeypad->key_val;
+	}
+}
+
+static int atc260x_adckeypad_scan(struct atc260x_dev *atc260x,
+	struct input_polled_dev *poll_dev)
+{
+	struct atc260x_adckeypad_dev *atc260x_adckeypad = poll_dev->private;
+	s32 tr_val;
+	int ret;
+
+	/* no need to touch the hardware,
+	 * we use the service from the parent device (ie. the core). */
+
+	ret = atc260x_auxadc_get_translated(atc260x_adckeypad->atc260x,
+		atc260x_adckeypad->auxadc_channel, &tr_val);
+	if (ret) {
+		dev_err(atc260x_adckeypad->dev,
+			"%s() failed to get raw value of auxadc channel #%u\n",
+			__func__, atc260x_adckeypad->auxadc_channel);
+		tr_val = 4095; /* use max value instead. */
+	}
+	/* tr_val is in the range [0, 4095] */
+	if (tr_val < 0 || tr_val > 4095) {
+		dev_err(atc260x_adckeypad->dev,
+			"%s() auxadc channel #%u result out of range\n",
+			__func__, atc260x_adckeypad->auxadc_channel);
+		tr_val = 4095; /* use max value instead. */
+	}
+	/* dev_info(atc260x_adckeypad->dev, "%s() adc_val=%u\n", __func__, tr_val); */
+	return tr_val;
+}
+
+static int atc260x_adckeypad_filter(struct input_polled_dev *dev)
+{
+	struct atc260x_adckeypad_dev *atc260x_adckeypad = dev->private;
+	uint tmp, sum_cnt, adc_val_sum;
+	uint i, j;
+	int diff;
+
+	if (atc260x_adckeypad->adc_buffer == NULL)
+		return -EINVAL;
+	if (atc260x_adckeypad->filter_dep == 0)
+		return -EINVAL;
+	sum_cnt = atc260x_adckeypad->filter_dep;
+
+	adc_val_sum = 0;
+	for (i = 0; i < sum_cnt; i++) {
+		tmp = atc260x_adckeypad->adc_buffer[i];
+		if (tmp == (typeof(tmp))-1)
+			return -EINVAL;
+		for (j = i + 1; j < sum_cnt; j++) {
+			diff = tmp - atc260x_adckeypad->adc_buffer[j];
+			diff = (diff >= 0) ? diff : -diff;
+			if (diff >= atc260x_adckeypad->variance)
+				return -EINVAL;
+		}
+		adc_val_sum += tmp;
+	}
+
+	return adc_val_sum / sum_cnt;
+}
+
+static void atc260x_adckeypad_poll(struct input_polled_dev *dev)
+{
+	struct atc260x_adckeypad_dev *atc260x_adckeypad = dev->private;
+	struct input_dev *input_dev = dev->input;
+	int ret;
+
+	ret = atc260x_adckeypad_scan(atc260x_adckeypad->atc260x, dev);
+	if (ret < 0)
+		return;
+
+	atc260x_adckeypad->adc_buffer[atc260x_adckeypad->filter_index] = ret;
+	atc260x_adckeypad->filter_index =
+		(atc260x_adckeypad->filter_index < atc260x_adckeypad->filter_dep) ?
+			atc260x_adckeypad->filter_index + 1 : 0;
+
+	ret = atc260x_adckeypad_filter(dev);
+	if (ret >= 0) {
+		atc260x_adckeypad->key_val =
+			atc260x_adckeypad_convert(ret, atc260x_adckeypad);
+		atc260x_adckeypad_report(input_dev, atc260x_adckeypad);
+	}
+}
+
+static int atc260x_adckeypad_config(struct atc260x_adckeypad_dev *atc260x_adckeypad)
+{
+	struct atc260x_dev *atc260x = atc260x_adckeypad->atc260x;
+	const char *default_channel_name, *channel_name, *of_prop_name;
+	int ret;
+
+	/* no need to touch the hardware,
+	 * we use the service from the parent device (ie. the core). */
+
+	default_channel_name = "REMCON";
+	of_prop_name = "adc_channel_name";
+	ret = of_property_read_string(
+		atc260x_adckeypad->dev->of_node, "adc_channel_name", &channel_name);
+	if (ret) {
+		dev_warn(atc260x_adckeypad->dev, "%s() can not get of_prop %s\n",
+			__func__, of_prop_name);
+		/* use default value */
+		channel_name = default_channel_name;
+	}
+	dev_info(atc260x_adckeypad->dev, "select AUXADC channel %s", channel_name);
+
+	ret = atc260x_auxadc_find_chan(atc260x, channel_name);
+	if (ret < 0) {
+		dev_err(atc260x_adckeypad->dev, "%s() unknown channel %s\n",
+			__func__, channel_name);
+		return ret;
+	}
+	atc260x_adckeypad->auxadc_channel = ret;
+
+	return 0;
+}
+
+static int atc260x_adckeypad_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x;
+	struct atc260x_adckeypad_dev *atc260x_adckeypad;
+	struct device_node *np;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input_dev;
+	const char *dts_status_cfg_str;
+	int ret = 0;
+	int i;
+
+	dev_info(&pdev->dev, "Probing...\n");
+
+	np = pdev->dev.of_node;
+	ret = of_property_read_string(np, "status", &dts_status_cfg_str);
+	if (ret == 0 && strcmp(dts_status_cfg_str, "okay") != 0) {
+		dev_info(&pdev->dev, "disabled by DTS\n");
+		return -ENODEV;
+	}
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	atc260x_adckeypad = devm_kzalloc(&pdev->dev,
+			sizeof(struct atc260x_adckeypad_dev), GFP_KERNEL);
+	if (!atc260x_adckeypad) {
+		dev_err(&pdev->dev, "%s() no mem\n", __func__);
+		return -ENOMEM;
+	}
+	atc260x_adckeypad->dev = &pdev->dev;
+	atc260x_adckeypad->atc260x = atc260x;
+	platform_set_drvdata(pdev, atc260x_adckeypad);
+
+	ret = atc260x_adckeypad_config(atc260x_adckeypad);
+	if (ret)
+		goto of_property_read_err;
+
+	atc260x_adckeypad->filter_index = 0;
+	atc260x_adckeypad->old_key_val = KEY_VAL_INIT;
+	/*
+	 * get configure info from xml
+	 */
+#if (ADCKEYPAD_DEBUG == 1)
+	atc260x_adckeypad->keymapsize = 9;
+	/*get left adc val*/
+	atc260x_adckeypad->left_adc_val = left_adc;
+	/*get right adc val*/
+	atc260x_adckeypad->right_adc_val = right_adc;
+	/*get key values*/
+	atc260x_adckeypad->key_values = key_val;
+
+	atc260x_adckeypad->filter_dep = 5;
+	atc260x_adckeypad->variance = 50;
+
+	atc260x_adckeypad->adc_buffer = devm_kzalloc(
+			atc260x_adckeypad->dev,
+			sizeof(unsigned int) * atc260x_adckeypad->filter_dep,
+			GFP_KERNEL);
+	if (!atc260x_adckeypad->adc_buffer)
+		goto free_buffer;
+	memset(atc260x_adckeypad->adc_buffer, 0xff,
+		sizeof(unsigned int) * atc260x_adckeypad->filter_dep);
+
+#else
+	/*get keymapsize*/
+	ret = of_property_read_u32(np, "keymapsize", &(atc260x_adckeypad->keymapsize));
+	if ((ret) || (!atc260x_adckeypad->keymapsize)) {
+		dev_err(&pdev->dev, "Get keymapsize failed ret = %d \r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "keymapsize = %d\n", atc260x_adckeypad->keymapsize);
+
+	/*get key filter depth*/
+	ret = of_property_read_u32(np, "filter_dep", &(atc260x_adckeypad->filter_dep));
+	if ((ret) || (!atc260x_adckeypad->filter_dep)) {
+		dev_err(&pdev->dev, "Get filter_dep failed ret = %d\r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "filter_dep = %d\n", atc260x_adckeypad->filter_dep);
+
+	/*get variance val */
+	ret = of_property_read_u32(np, "variance", &(atc260x_adckeypad->variance));
+	if ((ret) || (!atc260x_adckeypad->variance)) {
+		dev_err(&pdev->dev, "Get variance failed ret = %d\r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "variance = %d\n", atc260x_adckeypad->variance);
+
+	/*get left adc val*/
+	atc260x_adckeypad->left_adc_val = devm_kzalloc(&pdev->dev,
+		sizeof(unsigned int) * (atc260x_adckeypad->keymapsize), GFP_KERNEL);
+	if (!atc260x_adckeypad->left_adc_val)
+		goto free;
+
+	ret = of_property_read_u32_array(np, "left_adc_val",
+		(u32 *)atc260x_adckeypad->left_adc_val,
+		atc260x_adckeypad->keymapsize);
+	if (ret) {
+		dev_err(&pdev->dev, "Get left_adc_val failed ret = %d\r\n", ret);
+		goto free_left;
+	}
+
+	/*get right adc val*/
+	atc260x_adckeypad->right_adc_val = devm_kzalloc(&pdev->dev,
+		sizeof(unsigned int) * (atc260x_adckeypad->keymapsize), GFP_KERNEL);
+	if (!atc260x_adckeypad->right_adc_val)
+		goto free;
+
+	ret = of_property_read_u32_array(np, "right_adc_val",
+		(u32 *)atc260x_adckeypad->right_adc_val,
+		atc260x_adckeypad->keymapsize);
+	if (ret) {
+		dev_err(&pdev->dev, "Get right_adc_val failed ret = %d\r\n", ret);
+		goto free_right;
+	}
+
+	/*get key val*/
+	atc260x_adckeypad->key_values = devm_kzalloc(&pdev->dev,
+		sizeof(unsigned int) * (atc260x_adckeypad->keymapsize), GFP_KERNEL);
+	if (!atc260x_adckeypad->key_values)
+		goto free;
+
+	ret = of_property_read_u32_array(np, "key_val",
+		(u32 *)atc260x_adckeypad->key_values,
+		atc260x_adckeypad->keymapsize);
+	if (ret) {
+		dev_err(&pdev->dev, "Get key_values failed ret = %d\r\n", ret);
+		goto free_key_values;
+		}
+
+	/*Malloc adc_buffer*/
+	atc260x_adckeypad->adc_buffer = devm_kzalloc(&pdev->dev,
+		sizeof(unsigned int) * atc260x_adckeypad->filter_dep, GFP_KERNEL);
+	if (!atc260x_adckeypad->adc_buffer)
+		goto free_buffer;
+	memset(atc260x_adckeypad->adc_buffer, 0xff,
+		sizeof(unsigned int) * atc260x_adckeypad->filter_dep);
+#endif
+
+	/*
+	 * poll dev related
+	 */
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev) {
+		ret = -ENOMEM;
+		goto free_buffer;
+	}
+	atc260x_adckeypad->poll_dev = poll_dev;
+
+	poll_dev->private = atc260x_adckeypad;
+	poll_dev->poll = atc260x_adckeypad_poll;
+
+#if (ADCKEYPAD_DEBUG == 1)
+	poll_dev->poll_interval = 5;/* msec */
+#else
+	/*get poll period*/
+	ret = of_property_read_u32(np, "poll_interval", &(poll_dev->poll_interval));
+	if ((ret) || (!poll_dev->poll_interval)) {
+		dev_err(&pdev->dev, "Get poll_interval failed \r\n");
+		goto free_buffer;
+	}
+	dev_info(&pdev->dev, "poll_interval = %ums\n", poll_dev->poll_interval);
+#endif
+
+	input_dev = poll_dev->input;
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SW);
+	input_dev->name = pdev->name;
+	input_dev->phys = "atc260x_adckeypad/input3";
+	input_dev->keycode = atc260x_adckeypad->key_values;
+	input_dev->keycodesize = atc260x_adckeypad->keymapsize;
+	input_dev->keycodemax = atc260x_adckeypad->keymapsize;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->id.bustype = BUS_HOST;
+
+	for (i = 0; i < atc260x_adckeypad->keymapsize; i++)
+		__set_bit(*(atc260x_adckeypad->key_values + i), input_dev->keybit);
+
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+	__set_bit(KEY_POWER, input_dev->keybit);
+	__set_bit(KEY_POWER2, input_dev->keybit);
+	__set_bit(KEY_VAL_HOLD, input_dev->swbit);
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	ret = input_register_polled_device(poll_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() failed to register_polled_device, ret=%d\n",
+			__func__, ret);
+		goto free_polled;
+	}
+
+	input_dev->timer.data = (long) input_dev;
+	return 0;
+
+free_polled:
+	platform_set_drvdata(pdev, NULL);
+	input_free_polled_device(poll_dev);
+
+free_buffer:
+free_key_values:
+free_right:
+free_left:
+free:
+of_property_read_err:
+
+	return ret;
+}
+
+static int atc260x_adckeypad_remove(struct platform_device *pdev)
+{
+	struct atc260x_adckeypad_dev *atc260x_adckeypad =
+		platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	input_unregister_polled_device(atc260x_adckeypad->poll_dev);
+	input_free_polled_device(atc260x_adckeypad->poll_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int atc260x_adckeypad_suspend(struct platform_device *pdev,
+	pm_message_t state)
+{
+	struct atc260x_adckeypad_dev *atc260x_adckeypad = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&atc260x_adckeypad->poll_dev->work);
+	return 0;
+}
+static int atc260x_adckeypad_resume(struct platform_device *pdev)
+{
+	struct atc260x_adckeypad_dev *atc260x_adckeypad = platform_get_drvdata(pdev);
+
+	schedule_delayed_work(&atc260x_adckeypad->poll_dev->work,
+		msecs_to_jiffies(atc260x_adckeypad->poll_dev->poll_interval));
+	return 0;
+}
+#else
+	# define atc260x_adckeypad_suspend NULL
+	# define atc260x_adckeypad_resume  NULL
+#endif
+
+static const struct of_device_id atc260x_adckey_of_match[] = {
+	{.compatible = "actions,atc2603a-adckeypad",},
+	{.compatible = "actions,atc2603c-adckeypad",},
+	{.compatible = "actions,atc2609a-adckeypad",},
+	{}
+};
+
+static struct platform_driver atc260x_adckeypad_driver = {
+	.driver = {
+		.name = "atc260x-adckeypad",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(atc260x_adckey_of_match),
+	},
+	.probe = atc260x_adckeypad_probe,
+	.remove = atc260x_adckeypad_remove,
+	.suspend = atc260x_adckeypad_suspend,
+	.resume = atc260x_adckeypad_resume,
+};
+
+module_platform_driver(atc260x_adckeypad_driver)
+
+/*static int __init atc260x_adckeypad_init(void) */
+/*{ */
+/*  return platform_driver_register(&atc260x_adckeypad_driver); */
+/*} */
+/*subsys_init(atc260x_adckeypad_init); */
+/*//late_initcall(atc260x_adckeypad_init); */
+/*static void __exit atc260x_adckeypad_exit(void) */
+/*{ */
+/*  platform_driver_unregister(&atc260x_adckeypad_driver); */
+/*} */
+/*module_exit(atc260x_adckeypad_exit); */
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Asoc adckey  drvier");
+MODULE_AUTHOR("sall.xie/Actions Semi, Inc");
diff --git a/drivers/input/keyboard/atc260x-irkeypad.c b/drivers/input/keyboard/atc260x-irkeypad.c
new file mode 100755
index 0000000..4683931
--- /dev/null
+++ b/drivers/input/keyboard/atc260x-irkeypad.c
@@ -0,0 +1,1153 @@
+/*
+ * Asoc  irkeypad driver
+ *
+ * Copyright (C) 2011 Actions Semiconductor, Inc
+ * Author:	chenbo <chenbo at actions-semi.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mfd/atc260x/atc260x.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#define IR_PROTOCOL_9012    0x0
+#define IR_PROTOCOL_NEC8    0x1
+#define IR_PROTOCOL_RC5     0x2
+
+/*debug related*/
+#define IRKEYPAD_DEBUG			1
+#define IRKEYPAD_REG_PRINT		1
+#define IRKEYPAD_DEBUG_INFO		1
+#define DEBUG_IRQ_HANDLER 		1
+#define IRKEYPAD_SUPPORT_MOUSE  1
+/*IR Power Key is Suspend Func */
+#if (IRKEYPAD_DEBUG_INFO == 1)
+	#define GL5201_IRKEYPAD_INFO(fmt, args...)	\
+	printk(KERN_INFO "gl5201_irkeypad_drv: " fmt, ##args)
+#else
+	#define GL5201_IRKEYPAD_INFO(fmt, args...)
+#endif
+
+#define res_size(res)	((res)->end - (res)->start + 1)
+
+#define IRC_STAT_UCMP 		(0x1 << 6)
+#define IRC_STAT_KDCM 		(0x1 << 5)
+#define IRC_STAT_RCD 		(0x1 << 4)
+#define IRC_STAT_IIP 		(0x1 << 2)
+#define IRC_STAT_IREP 		(0x1 << 0)
+
+#define GPIO_NAME_POWER_LED "power_led"
+//static struct gpio_pre_cfg power_led_cfg;
+static int gpio_power_led_pin;
+static bool power_led_exist;
+static bool led_blink_enable = 0;
+
+
+struct atc260x_dev *atc260x_dev_global = NULL;
+static unsigned long power_timeout;
+static bool suspending = false;
+
+struct asoc_irkeypad {
+	unsigned int size;
+        unsigned int ir_ch;
+	unsigned int protocol;
+	unsigned int user_code;
+	unsigned int wk_code;
+	unsigned int period;
+
+	unsigned int *ir_code;
+	unsigned int *key_code;
+
+	struct atc260x_dev *atc260x_dev;
+
+	struct input_dev *input_dev;
+
+	struct workqueue_struct *wq;
+  	struct delayed_work work;
+  	struct tasklet_struct tasklet_pressed;
+
+	int irq;
+	unsigned int ir_val;
+	unsigned int old_key_val;
+	unsigned int new_key_val;
+	unsigned int old_mouse_move;
+	unsigned int new_mouse_move;
+	unsigned int speed_up;
+	unsigned int mouse_speed;
+	unsigned int is_mouse_mode;
+};
+
+#if 0
+static  unsigned int asoc_irkeypad_keycode[]=
+{
+	KEY_POWER,KEY_MUTE,KEY_VOLUMEUP,KEY_VOLUMEDOWN,KEY_1,
+	KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,
+	KEY_7,KEY_8,KEY_9,KEY_0,/*KEY_INPUT,*/
+	KEY_BACKSPACE,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT,
+	KEY_SELECT,KEY_HOMEPAGE,KEY_MENU,KEY_BACK,KEY_MOVE,
+	KEY_MENU,KEY_MOVE,KEY_MENU
+/*	KEY_PAGEUP,KEY_PAGEDOWN,KEY_MOVE,KEY_F2_VLC,KEY_F3_VLC,
+	KEY_SETTING,KEY_LOCAL,KEY_SEARCH,KEY_CYCLE_PLAY,KEY_F4_VLC,
+	KEY_HELP,KEY_HDMI,KEY_AV,KEY_YPBPR,KEY_COLLECTION,
+	KEY_PLAYPAUSE*/
+};
+
+static  unsigned int asoc_irkeypad_ircode[]=
+{
+	0x51,0x4D,0xBB,0xBD,0x31,
+	0x32,0x33,0x34,0x35,0x36,
+	0x37,0x38,0x39,0x30,/*0x58,*/
+	0x44,0x26,0x28,0x25,0x27,
+	0x0D,0x49,0xBA,0x1B,0x4A,
+	0x52,0x46,0x53
+	/*0x56,0x4E,0x48,0x41,0x59,
+	0x53,0x42,0x46,0x4C,0x52,
+	0x54,0x09,0x11,0x12,0x4F,
+	0x50*/
+};
+#endif
+
+void irkeypad_reg_print(struct asoc_irkeypad *irkeypad)
+{
+#if (IRKEYPAD_REG_PRINT == 1)
+	GL5201_IRKEYPAD_INFO("\n\nfollowing list all irkeypad register's value!\n");
+	GL5201_IRKEYPAD_INFO("register atc2603_INTS_PD(0x%08x) value is 0x%08x\n",
+		ATC2603C_INTS_PD, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_INTS_PD));
+	GL5201_IRKEYPAD_INFO("register atc2603_INTS_MSK(0x%08x) value is 0x%08x\n",
+		ATC2603C_INTS_MSK, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_INTS_MSK));
+	GL5201_IRKEYPAD_INFO("register atc2603_PMU_SYS_CTL3(0x%08x) value is 0x%08x\n",
+		ATC2603C_PMU_SYS_CTL3, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_PMU_SYS_CTL3));
+	GL5201_IRKEYPAD_INFO("register atc2603_PMU_SYS_CTL0(0x%08x) value is 0x%08x\n",
+		ATC2603C_PMU_SYS_CTL0, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_PMU_SYS_CTL0));
+	GL5201_IRKEYPAD_INFO("register atc2603_IRC_CTL(0x%08x) value is 0x%08x\n",
+		ATC2603C_IRC_CTL, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_IRC_CTL));
+	GL5201_IRKEYPAD_INFO("register atc2603_IRC_STAT(0x%08x) value is 0x%08x\n",
+		ATC2603C_IRC_STAT, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_IRC_STAT));
+	GL5201_IRKEYPAD_INFO("register atc2603_IRC_CC(0x%08x) value is 0x%08x\n",
+		ATC2603C_IRC_CC, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_IRC_CC));
+	GL5201_IRKEYPAD_INFO("register atc2603_IRC_KDC(0x%08x) value is 0x%08x\n",
+		ATC2603C_IRC_KDC, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_IRC_KDC));
+	GL5201_IRKEYPAD_INFO("register atc2603_IRC_WK(0x%08x) value is 0x%08x\n",
+		ATC2603C_IRC_WK, atc260x_reg_read(irkeypad->atc260x_dev,ATC2603C_IRC_WK));
+#endif
+}
+
+static void asoc_irkeypad_enable_irq(struct asoc_irkeypad *irkeypad)
+{
+	struct atc260x_dev *atc260x_dev = irkeypad->atc260x_dev;
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CTL,
+		0x0004, 0x0004);
+}
+
+static void asoc_irkeypad_disable_irq(struct asoc_irkeypad *irkeypad)
+{
+	struct atc260x_dev *atc260x_dev = irkeypad->atc260x_dev;
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CTL,
+		0x0004, 0x0000);
+}
+
+static void asoc_irkeypad_config(struct asoc_irkeypad * irkeypad)
+{
+    struct atc260x_dev *atc260x_dev = irkeypad->atc260x_dev;
+
+	GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+
+	/*IRC enable*/
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CTL,
+		0x0008, 0x0008);
+
+	/*irq*/
+	atc260x_set_bits(atc260x_dev, ATC2603C_INTS_MSK,
+		0x0100, 0x0100);    //INTS_MSK bit[8]TR
+
+	/*mfp*/
+        if(irkeypad->ir_ch == 1){
+            atc260x_set_bits(atc260x_dev, ATC2603C_PMU_MUX_CTL0,
+                  0x0003, 0x0002);
+        }else {
+	    atc260x_set_bits(atc260x_dev, ATC2603C_PMU_MUX_CTL0,
+	          0x3000, 0x1000);
+        }
+
+	/*wake up*/
+	atc260x_set_bits(atc260x_dev, ATC2603C_PMU_SYS_CTL0,
+		0x0020, 0x0020);
+
+	switch(irkeypad->protocol) {
+
+	case IR_PROTOCOL_9012:
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CTL,
+			0x0003, 0x0000);
+	/*set IRC cc,NEC default*/
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CC,
+		0xffff, irkeypad->user_code);
+
+	/*set IRC wakeup*/
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_WK,
+		0xffff, ((~irkeypad->wk_code) << 8) | (irkeypad->wk_code));
+		break;
+
+	case IR_PROTOCOL_NEC8:
+		atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CTL,
+			0x0003, 0x0001);
+		atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CC,
+			0xffff, irkeypad->user_code);
+		atc260x_set_bits(atc260x_dev, ATC2603C_IRC_WK,
+			0xffff, ((~irkeypad->wk_code) << 8) | (irkeypad->wk_code));
+		break;
+
+	case IR_PROTOCOL_RC5:
+		atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CTL,
+			0x0003, 0x0002);
+		atc260x_set_bits(atc260x_dev, ATC2603C_IRC_CC,
+			0xffff, (irkeypad->user_code) & 0x001f);
+		atc260x_set_bits(atc260x_dev, ATC2603C_IRC_WK,
+			0xffff, (irkeypad->wk_code) & 0x003f);
+		break;
+
+	default:
+		break;
+	}
+
+    /*set IRC filter*/
+    atc260x_set_bits(atc260x_dev, ATC2603C_IRC_FILTER,
+        0xffff,0x000b);
+
+	GL5201_IRKEYPAD_INFO("[%s finished]\n",__func__);
+}
+
+static void asoc_irkeypad_convert(unsigned int protocol,
+		unsigned int *val)
+{
+	switch (protocol) {
+	case IR_PROTOCOL_9012:
+		*val &= 0x00ff;
+		break;
+
+	case IR_PROTOCOL_NEC8:
+		*val &= 0x00ff;
+		break;
+
+	case IR_PROTOCOL_RC5:
+		*val &= 0x003f;
+		break;
+
+	default:
+		break;
+	}
+
+	return;
+}
+
+static void asoc_irkeypad_scan(struct asoc_irkeypad *irkeypad)
+{
+
+	struct atc260x_dev *atc260x_dev = irkeypad->atc260x_dev;
+
+	//GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+
+	irkeypad->ir_val = atc260x_reg_read(atc260x_dev, ATC2603C_IRC_KDC);
+
+	asoc_irkeypad_convert(irkeypad->protocol, &(irkeypad->ir_val));
+
+	//GL5201_IRKEYPAD_INFO("[%s finished]\n",__func__);
+
+	return;
+}
+
+static void asoc_irkeypad_map(struct asoc_irkeypad *irkeypad)
+{
+	int i;
+	unsigned int *ir_val;
+
+	for (i = 0; i < irkeypad->size; i++) {
+		ir_val = irkeypad->ir_code + i;
+		asoc_irkeypad_convert(irkeypad->protocol, ir_val);
+		if (*ir_val == irkeypad->ir_val) {
+
+			irkeypad->new_key_val = *(irkeypad->key_code + i);
+			return;
+		}
+
+	}
+
+	GL5201_IRKEYPAD_INFO("[%s]irkeypad map failed, ir_val = 0x%x\n",__func__, irkeypad->ir_val);
+
+	return;
+}
+
+static void asoc_irkeypad_report_released(struct asoc_irkeypad *irkeypad)
+{
+	struct input_dev *input_dev;
+
+#if 0
+    input_dev = irkeypad->input_dev;
+
+     if (irkeypad->old_key_val  != KEY_RESERVED)
+     {
+	    input_report_key(input_dev, irkeypad->old_key_val, 0);
+	    GL5201_IRKEYPAD_INFO("key: %d %s\n",
+		    irkeypad->old_key_val,"released");
+     }
+	 input_sync(input_dev);
+     irkeypad->new_key_val = KEY_RESERVED;
+     irkeypad->old_key_val = irkeypad->new_key_val;
+
+#else
+
+    asoc_irkeypad_disable_irq(irkeypad);
+
+#if DEBUG_IRQ_HANDLER
+	GL5201_IRKEYPAD_INFO("%s start : old_key_val = %d \n",__func__, irkeypad->old_key_val);
+#endif
+
+    input_dev = irkeypad->input_dev;
+    if (irkeypad->old_key_val  != KEY_RESERVED) {
+		input_report_key(input_dev, irkeypad->old_key_val, 0);
+		input_sync(input_dev);
+			GL5201_IRKEYPAD_INFO("key: %d %s\n",
+				irkeypad->old_key_val,"released");
+ 			if(irkeypad->old_key_val == KEY_POWER){
+				power_timeout = jiffies + HZ*5;
+			}   	
+			irkeypad->new_key_val = KEY_RESERVED;
+    		irkeypad->old_key_val = irkeypad->new_key_val;
+	}
+
+	if(power_led_exist && led_blink_enable){
+		__gpio_set_value(gpio_power_led_pin, 0);
+	}
+
+	asoc_irkeypad_enable_irq(irkeypad);
+
+#if DEBUG_IRQ_HANDLER
+	GL5201_IRKEYPAD_INFO("%s end\n",__func__);
+#endif
+#endif
+
+}
+
+static void  asoc_irkeypad_report_pressed(struct asoc_irkeypad *irkeypad)
+{
+	unsigned int changed;
+	struct input_dev *input_dev = irkeypad->input_dev;
+
+#if DEBUG_IRQ_HANDLER
+	GL5201_IRKEYPAD_INFO("%s start\n",__func__);
+#endif
+
+	asoc_irkeypad_map(irkeypad);
+
+#if (IRKEYPAD_SUPPORT_MOUSE == 1)
+    if (irkeypad->is_mouse_mode == 1)
+    {
+        if (irkeypad->speed_up == 1)
+        {
+            irkeypad->new_mouse_move = irkeypad->new_key_val;
+            if (irkeypad->old_mouse_move == irkeypad->new_mouse_move)
+            {
+                irkeypad->mouse_speed += 2;
+                if (irkeypad->mouse_speed >= 50)
+                {
+                    irkeypad->mouse_speed = 50;
+                }
+            }
+            else
+            {
+                irkeypad->mouse_speed = 10;
+            }
+        }
+        switch(irkeypad->new_key_val)
+        {
+            case KEY_UP:
+                    input_report_rel(input_dev, REL_X,0);
+                    input_report_rel(input_dev, REL_Y, -irkeypad->mouse_speed);
+                    input_sync(input_dev);
+                    irkeypad->speed_up      = 1;
+                    break;
+            case KEY_DOWN:
+                    input_report_rel(input_dev,REL_X,0);
+                    input_report_rel(input_dev,REL_Y, irkeypad->mouse_speed);
+                    input_sync(input_dev);
+                    irkeypad->speed_up      = 1;
+                    break;
+            case KEY_LEFT:
+                    input_report_rel(input_dev,REL_X,-irkeypad->mouse_speed);
+                    input_report_rel(input_dev,REL_Y,0);
+                    input_sync(input_dev);
+                    irkeypad->speed_up      = 1;
+                    break;
+            case KEY_RIGHT:
+                    input_report_rel(input_dev,REL_X,irkeypad->mouse_speed);
+                    input_report_rel(input_dev,REL_Y,0);
+                    input_sync(input_dev);
+                    irkeypad->speed_up       = 1;
+                    break;
+            default:
+                    irkeypad->speed_up       = 0;
+                    irkeypad->mouse_speed    = 10;
+                    break;
+        }
+
+        if (irkeypad->speed_up == 1)
+        {
+            irkeypad->old_mouse_move = irkeypad->new_key_val;
+            irkeypad->new_key_val = KEY_RESERVED;
+            irkeypad->old_key_val = irkeypad->new_key_val ;
+        }
+    }
+#endif
+
+    changed = irkeypad->old_key_val ^ irkeypad->new_key_val;
+    if (changed)
+    {
+#if (IRKEYPAD_SUPPORT_MOUSE == 1)
+        if (irkeypad->new_key_val == KEY_MOVE)
+        {
+              irkeypad->speed_up     = 0;
+              irkeypad->mouse_speed  = 10;
+              if (irkeypad->is_mouse_mode == 0)
+              {
+                   GL5201_IRKEYPAD_INFO("irkeypad mouse mode enable \n");
+                   irkeypad->is_mouse_mode = 1;
+                   input_report_rel(input_dev, REL_X,  1);
+                   input_report_rel(input_dev, REL_Y,  1);
+                   input_report_key(input_dev, BTN_LEFT,  0);
+                   input_report_key(input_dev, BTN_RIGHT,  0);
+                   irkeypad->old_key_val = irkeypad->new_key_val ;
+                   input_sync(input_dev);
+                   return;
+               }
+               else
+               {
+                   irkeypad->is_mouse_mode = 0;
+                   GL5201_IRKEYPAD_INFO("irkeypad key mode enable \n");
+               }
+        }
+
+        if (irkeypad->is_mouse_mode == 1)
+        {
+            switch(irkeypad->new_key_val)
+            {
+                case KEY_SELECT:
+                    irkeypad->new_key_val = BTN_LEFT;
+                    break;
+
+                //case KEY_BACK:
+                //    irkeypad->new_key_val = BTN_RIGHT;
+                //    break;
+
+                default:
+                    break;
+            }
+        }
+
+		if(!(irkeypad->old_key_val ^ irkeypad->new_key_val))
+		  return;
+#endif
+#if 0
+
+        if (irkeypad->old_key_val != KEY_RESERVED)
+        {
+              input_report_key(input_dev,irkeypad->old_key_val, 0);
+        }
+
+        if (irkeypad->new_key_val != KEY_RESERVED)
+        {
+             input_report_key(input_dev,irkeypad->new_key_val, 1);
+        }
+
+        irkeypad->old_key_val = irkeypad->new_key_val;
+        input_sync(input_dev);
+#endif		
+		
+		/*report old val released*/
+		if (irkeypad->old_key_val != KEY_RESERVED) {
+
+			//asoc_irkeypad_report_released(irkeypad);
+		}
+
+		if(irkeypad->new_key_val != KEY_RESERVED) {
+			input_report_key(input_dev, irkeypad->new_key_val, 1);
+			input_sync(input_dev);
+			GL5201_IRKEYPAD_INFO("key: %d %s\n",
+					irkeypad->new_key_val,"pressed");
+
+			if(power_led_exist && led_blink_enable){
+				__gpio_set_value(gpio_power_led_pin, 1);
+			}
+		}
+
+		irkeypad->old_key_val = irkeypad->new_key_val;
+	}
+	return;
+}
+
+static void asoc_irkeypad_tasklet_pressed(unsigned long data)
+{
+	struct asoc_irkeypad *irkeypad = (struct asoc_irkeypad *)data;
+
+	asoc_irkeypad_report_pressed(irkeypad);
+}
+
+static void asoc_irkeypad_work_released(struct work_struct *work)
+{
+	struct asoc_irkeypad *irkeypad =
+		container_of(work, struct asoc_irkeypad, work.work);
+
+	asoc_irkeypad_report_released(irkeypad);
+}
+
+static inline unsigned int asoc_irkeypad_get_pend(struct asoc_irkeypad *irkeypad)
+{
+	return (atc260x_reg_read(irkeypad->atc260x_dev,
+			ATC2603C_IRC_STAT) & IRC_STAT_IIP ? 1 : 0);
+}
+
+static inline unsigned int asoc_irkeypad_get_irep(struct asoc_irkeypad *irkeypad, unsigned int stat)
+{
+	/*return (atc260x_reg_read(irkeypad->atc260x_dev,
+			atc2603_IRC_STAT) & IRC_STAT_IREP ? 1 : 0);*/
+	return ( (stat & IRC_STAT_IREP) ? 1 : 0);
+}
+
+bool is_invalid_power(struct asoc_irkeypad *irkeypad){
+	int i;
+	unsigned int *ir_val;
+	unsigned int keycode = 0;
+
+	for (i = 0; i < irkeypad->size; i++) {
+		ir_val = irkeypad->ir_code + i;
+		asoc_irkeypad_convert(irkeypad->protocol, ir_val);
+		if (*ir_val == irkeypad->ir_val) {
+			keycode = *(irkeypad->key_code + i);
+			break;
+		}
+
+	}
+
+	if(suspending){
+            if(keycode == KEY_POWER){
+                return false;
+            }else {
+	        return true;
+            }
+	}
+
+	if(time_before(jiffies, power_timeout) && keycode == KEY_POWER){
+	  return true;
+	} else {
+	  return false;
+	}
+}
+
+static int wakeup_powerkey_check(struct asoc_irkeypad *irkeypad)
+{
+	struct atc260x_dev *atc260x_dev = irkeypad->atc260x_dev;
+        unsigned int wk_src;
+        struct input_dev *input_dev;
+
+        input_dev = irkeypad->input_dev;
+ 
+        wk_src = atc260x_reg_read(atc260x_dev, ATC2603C_PMU_SYS_CTL1);
+        GL5201_IRKEYPAD_INFO("Suspending of resuming, wk_src = 0x%x \n",wk_src);
+        wk_src &= 0x20;
+        if(wk_src != 0x20){
+                GL5201_IRKEYPAD_INFO("Suspending of resuming, no key.");
+                return -1;
+        }
+
+        asoc_irkeypad_scan(irkeypad);   
+
+        if(is_invalid_power(irkeypad)){
+                GL5201_IRKEYPAD_INFO("Suspending of resuming, ignore this power key.");
+                return -1;
+        } 
+
+        input_report_key(input_dev, KEY_POWER, 1);
+        input_report_key(input_dev, KEY_POWER, 0);
+        input_sync(input_dev);
+
+        return 0;
+
+}
+
+static irqreturn_t asoc_irkeypad_irq_handler(int irq, void *dev_id)
+{
+	struct asoc_irkeypad *irkeypad = dev_id;
+	struct atc260x_dev *atc260x_dev = irkeypad->atc260x_dev;
+	unsigned int stat;
+    unsigned int old_ir_val;
+
+#if DEBUG_IRQ_HANDLER
+	GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+#endif
+
+	stat = atc260x_reg_read(irkeypad->atc260x_dev, ATC2603C_IRC_STAT);
+	atc260x_set_bits(atc260x_dev, ATC2603C_IRC_STAT, 0xffff, 0x0114);
+
+	old_ir_val = irkeypad->ir_val;
+	asoc_irkeypad_scan(irkeypad);
+
+	if(is_invalid_power(irkeypad)){
+		GL5201_IRKEYPAD_INFO("Suspending of resuming, ignore this power key.");
+		return IRQ_HANDLED;
+	}
+
+#if DEBUG_IRQ_HANDLER
+        stat = atc260x_reg_read(irkeypad->atc260x_dev, ATC2603C_IRC_STAT);
+	GL5201_IRKEYPAD_INFO("IRC_STAT : 0x%x\n", stat);
+#endif
+
+	if(power_led_exist && led_blink_enable){
+		__gpio_set_value(gpio_power_led_pin, 0);
+	}
+
+
+	
+	if (asoc_irkeypad_get_irep(irkeypad, stat)) {
+		if ((irkeypad->ir_val == old_ir_val) || (old_ir_val == 0)) {
+			GL5201_IRKEYPAD_INFO("invalide code, old_ir_val = 0x%x, new_ir_val = 0x%x, IRC_STAT = 0x%x\n",
+					old_ir_val, irkeypad->ir_val, stat);
+#if DEBUG_IRQ_HANDLER
+			irkeypad_reg_print(irkeypad);
+#endif
+			return IRQ_HANDLED;
+		}
+	}
+
+#if DEBUG_IRQ_HANDLER
+	GL5201_IRKEYPAD_INFO("[%s] : old_key_val = %d, new_key_val = %d \n",__func__, 
+				irkeypad->old_key_val, irkeypad->new_key_val);
+#endif
+
+	if( stat & IRC_STAT_RCD ){
+		if ((irkeypad->new_key_val == KEY_RESERVED) && (irkeypad->speed_up == 0) ){
+			GL5201_IRKEYPAD_INFO("invalide repeat, IRC_STAT = 0x%x\n", stat);
+		} else {
+			cancel_delayed_work(&irkeypad->work);
+#if (IRKEYPAD_SUPPORT_MOUSE == 1)
+			tasklet_schedule(&irkeypad->tasklet_pressed);
+#endif
+			queue_delayed_work(irkeypad->wq, &irkeypad->work,
+					msecs_to_jiffies(irkeypad->period));
+		}
+
+	} else {
+		cancel_delayed_work(&irkeypad->work);
+		//if ( irkeypad->new_key_val != KEY_RESERVED ){
+		//	asoc_irkeypad_report_released(irkeypad);
+		//}
+		//asoc_irkeypad_scan(irkeypad);
+		tasklet_schedule(&irkeypad->tasklet_pressed);
+		queue_delayed_work(irkeypad->wq, &irkeypad->work,
+				msecs_to_jiffies(irkeypad->period));
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static void asoc_irkeypad_start(struct asoc_irkeypad * irkeypad)
+{
+	GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+
+	asoc_irkeypad_config(irkeypad);
+	asoc_irkeypad_enable_irq(irkeypad);
+
+	GL5201_IRKEYPAD_INFO("[%s finished]\n",__func__);
+}
+
+static void asoc_irkeypad_stop(struct asoc_irkeypad *irkeypad)
+{
+
+	GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+
+	cancel_delayed_work(&irkeypad->work);
+	asoc_irkeypad_disable_irq(irkeypad);
+
+	GL5201_IRKEYPAD_INFO("[%s finished]\n",__func__);
+}
+
+static int asoc_irkeypad_open(struct input_dev *dev)
+{
+	struct asoc_irkeypad *irkeypad = input_get_drvdata(dev);
+
+	GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+
+	asoc_irkeypad_start(irkeypad);
+
+	GL5201_IRKEYPAD_INFO("[%s finished]\n",__func__);
+	return 0;
+}
+
+static void asoc_irkeypad_close(struct input_dev *dev)
+{
+
+	struct asoc_irkeypad *irkeypad = input_get_drvdata(dev);
+
+	GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+
+	asoc_irkeypad_stop(irkeypad);
+
+	GL5201_IRKEYPAD_INFO("[%s finished]\n",__func__);
+	return;
+}
+
+static ssize_t atv5201_irkeypad_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,size_t count)
+{
+	char *endp;
+	struct asoc_irkeypad *irkeypad = dev_get_drvdata(dev);
+	int cmd = simple_strtoul(buf,&endp,0);
+	size_t size = endp - buf;
+
+	if (*endp && isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	switch (cmd) {
+	case 0:
+		irkeypad_reg_print(irkeypad);
+		break;
+
+	default:
+		break;
+	}
+
+	return count;
+
+
+}
+
+static ssize_t atv5201_irkeypad_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	ssize_t ret_size = 0;
+
+	return ret_size;
+}
+
+static ssize_t atv5201_irkeypad_mousespeed_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,size_t count)
+{
+	char *endp;
+    int __attribute__((unused)) cmd;
+    size_t size;
+	//struct asoc_irkeypad *irkeypad = dev_get_drvdata(dev);
+    
+	cmd = simple_strtoul(buf, &endp, 0);
+	size = endp - buf;
+
+	if (*endp && isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t atv5201_irkeypad_mousespeed_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+    struct asoc_irkeypad *irkeypad = dev_get_drvdata(dev);
+    unsigned int offset = 0;
+
+	offset += snprintf(&buf[offset], PAGE_SIZE - offset, "%d\n",
+					irkeypad->mouse_speed);
+	return offset;
+}
+
+
+
+static struct device_attribute irkeypad_attrs[] = {
+	__ATTR(mouseSpeed, S_IRUGO | S_IWUSR,  atv5201_irkeypad_mousespeed_show, atv5201_irkeypad_mousespeed_store),
+	__ATTR(queueSize,  S_IRUGO | S_IWUSR,	atv5201_irkeypad_show, atv5201_irkeypad_store),
+};
+
+
+static int atc260x_irkeypad_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x_dev = dev_get_drvdata(pdev->dev.parent);
+	struct asoc_irkeypad *irkeypad;
+	struct device_node *np;
+	struct input_dev *input_dev;
+	int ret = 0;
+	int i;
+
+    atc260x_dev_global = atc260x_dev;
+
+	printk("[%s start]\n",__func__);
+	np = pdev->dev.of_node;
+
+	if(led_blink_enable){/*
+		memset((void *)&power_led_cfg, 0, sizeof(struct gpio_pre_cfg));
+		if (gpio_get_pre_cfg(GPIO_NAME_POWER_LED, &power_led_cfg)) {
+			printk("\n[power] get  power led gpio failed!");
+			power_led_exist = false;
+		} else {
+			power_led_exist = true;
+			gpio_power_led_pin =  ASOC_GPIO_PORT(power_led_cfg.iogroup, power_led_cfg.pin_num);
+			gpio_request(gpio_power_led_pin, GPIO_NAME_POWER_LED);
+			gpio_direction_output(gpio_power_led_pin, 0);
+		}*/
+	}
+
+	irkeypad = kzalloc(sizeof(struct asoc_irkeypad), GFP_KERNEL);
+	if (irkeypad == NULL) {
+
+		dev_err(&pdev->dev, "failed to allocate irkeypad driver data\n");
+		ret = -ENOMEM;
+		return ret;
+
+	}
+	platform_set_drvdata(pdev, irkeypad);
+
+	irkeypad->atc260x_dev = atc260x_dev;
+	irkeypad->old_key_val = KEY_RESERVED;
+	irkeypad->old_mouse_move = KEY_RESERVED;
+	irkeypad->mouse_speed  = 10;
+#if 0 /*(IRKEYPAD_DEBUG == 1)*/
+	irkeypad->size = ARRAY_SIZE(asoc_irkeypad_keycode);
+	irkeypad->user_code = 0x80;
+	irkeypad->wk_code = 0x51;
+	irkeypad->ir_code = asoc_irkeypad_ircode;
+	irkeypad->key_code = asoc_irkeypad_keycode;
+	irkeypad->protocol = 0x01;
+	irkeypad->period   = 130;
+#endif
+	/*get size*/
+	ret = of_property_read_u32(np, "size", &(irkeypad->size));
+	if ((ret) || (!irkeypad->size)) {
+		dev_err(&pdev->dev, "Get size failed ret = %d \r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "size = %d\n", irkeypad->size);
+
+        ret = of_property_read_u32(np, "ir_ch", &(irkeypad->ir_ch));
+        if ((ret) || (!irkeypad->ir_ch)) {
+                dev_err(&pdev->dev, "Get ir_ch failed ret = %d \r\n", ret);
+                irkeypad->ir_ch = 0;
+                //goto of_property_read_err;
+        }
+        printk("atc260x_irkeypad: ir_ch = %d\n", irkeypad->ir_ch);
+
+	/*get user_code*/
+	ret = of_property_read_u32(np, "user_code", &(irkeypad->user_code));
+	if ((ret) || (!irkeypad->user_code)) {
+		dev_err(&pdev->dev, "Get user_code failed ret = %d \r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "user_code = %d\n", irkeypad->user_code);
+
+	/*get protocol*/
+	ret = of_property_read_u32(np, "protocol", &(irkeypad->protocol));
+	if ((ret) /*|| (!irkeypad->protocol)*/) {
+		dev_err(&pdev->dev, "Get protocol failed ret = %d \r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "protocol = %d\n", irkeypad->protocol);
+	
+	/*get wk_code*/
+	ret = of_property_read_u32(np, "wk_code", &(irkeypad->wk_code));
+	if ((ret) || (!irkeypad->wk_code)) {
+		dev_err(&pdev->dev, "Get wk_code failed ret = %d \r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "wk_code = %d\n", irkeypad->wk_code);
+	
+	/*get period*/
+	ret = of_property_read_u32(np, "period", &(irkeypad->period));
+	if ((ret) || (!irkeypad->period)) {
+		dev_err(&pdev->dev, "Get period failed ret = %d \r\n", ret);
+		goto of_property_read_err;
+	}
+	dev_info(&pdev->dev, "period = %d\n", irkeypad->period);
+
+
+	/*get ir_code*/
+	irkeypad->ir_code = devm_kzalloc(&pdev->dev,
+		sizeof(unsigned int) * (irkeypad->size), GFP_KERNEL);
+	if (!irkeypad->ir_code)
+		goto free;
+
+	ret = of_property_read_u32_array(np, "ir_code",
+		(u32 *)irkeypad->ir_code,
+		irkeypad->size);
+	if (ret) {
+		dev_err(&pdev->dev, "Get ir_code failed ret = %d\r\n", ret);
+		goto free_ir_code;
+	}
+
+
+	/*get key_code*/
+	irkeypad->key_code = devm_kzalloc(&pdev->dev,
+		sizeof(unsigned int) * (irkeypad->size), GFP_KERNEL);
+	if (!irkeypad->key_code)
+		goto free;
+
+	ret = of_property_read_u32_array(np, "key_code",
+		(u32 *)irkeypad->key_code,
+		irkeypad->size);
+	if (ret) {
+		dev_err(&pdev->dev, "Get key_code failed ret = %d\r\n", ret);
+		goto free_key_code;
+	}
+
+
+	/*
+	 * the WORK work is in charge of reporting key release and
+	 * the WORK work_pressed reports key pressed.
+	 */
+	tasklet_init(&irkeypad->tasklet_pressed, asoc_irkeypad_tasklet_pressed, (unsigned long)irkeypad);
+	INIT_DELAYED_WORK(&irkeypad->work, asoc_irkeypad_work_released);
+	irkeypad->wq = create_workqueue("atv5201-IRKEYPAD");
+
+	/*
+	 * irq related
+	 */
+	irkeypad->irq = platform_get_irq(pdev, 0);
+	GL5201_IRKEYPAD_INFO("[%s] get irq: %d\n", __func__, irkeypad->irq);
+	if (irkeypad->irq < 0) {
+		dev_err(&pdev->dev, "failed to get irkeypad irq\n");
+		ret = -ENXIO;
+		goto free_key_code;
+	}
+	ret = request_threaded_irq(irkeypad->irq, NULL, asoc_irkeypad_irq_handler,
+		IRQF_TRIGGER_HIGH, "atc260x-irkeypad", irkeypad);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request irkeypad IRQ\n");
+		goto free_key_code;
+	}
+        asoc_irkeypad_disable_irq(irkeypad);
+	/*
+	 * input_dev related
+	 */
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate  irkeypad input device\n");
+		ret = -ENOMEM;
+		goto free_irq;
+	}
+	input_dev->name = pdev->name;
+	input_dev->phys = "asoc-irkeypad/input5";
+	input_dev->open = asoc_irkeypad_open;
+	input_dev->close = asoc_irkeypad_close;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->keycodemax = irkeypad->size;
+	input_dev->id.bustype = BUS_HOST;
+
+	irkeypad->input_dev = input_dev;
+	input_set_drvdata(input_dev, irkeypad);
+#if (IRKEYPAD_SUPPORT_MOUSE == 1)
+	input_dev->evbit[0] 	= BIT(EV_KEY) | BIT(EV_REL);
+    input_dev->keybit[BIT_WORD(BTN_MOUSE)]  = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+    input_dev->relbit[0]    = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+    irkeypad->is_mouse_mode    = 0;
+#else
+	input_dev->evbit[0] = BIT(EV_KEY);
+#endif
+
+	for (i = 0; i < input_dev->keycodemax; i++){
+
+		if(irkeypad->key_code[i] != KEY_RESERVED)
+			__set_bit(irkeypad->key_code[i],
+				input_dev->keybit);
+
+	}
+	ret = input_register_device(input_dev);
+	if (ret) {
+
+		dev_err(&pdev->dev, "failed to register input device\n");
+		goto free_input;
+
+	}
+
+	for (i = 0; i < ARRAY_SIZE(irkeypad_attrs); i++) {
+		ret = device_create_file(&pdev->dev, &irkeypad_attrs[i]);
+		if (ret) {
+			printk("failed to create sysfs file\n");
+			break;
+		}
+	}
+	
+	power_timeout = jiffies;
+	//asoc_irkeypad_open(input_dev);
+	printk("[%s finished]\n", __func__);
+	return 0;
+
+free_input:
+	input_free_device(input_dev);
+        input_set_drvdata(input_dev, NULL);
+free_irq:
+	free_irq(irkeypad->irq, pdev);
+
+free_key_code:
+free_ir_code:
+        platform_set_drvdata(pdev, NULL);
+of_property_read_err:
+free:
+	kfree(irkeypad);
+
+	return ret;
+}
+
+static int atc260x_irkeypad_remove(struct platform_device *pdev)
+{
+        int i;
+	struct asoc_irkeypad *irkeypad = platform_get_drvdata(pdev);
+
+        GL5201_IRKEYPAD_INFO("[%s start]\n",__func__);
+	//asoc_irkeypad_close(irkeypad->input_dev);
+	free_irq(irkeypad->irq, irkeypad);
+	input_unregister_device(irkeypad->input_dev);
+	input_free_device(irkeypad->input_dev);
+	input_set_drvdata(irkeypad->input_dev, NULL);
+
+	for (i = 0; i < ARRAY_SIZE(irkeypad_attrs); i++) {
+        device_remove_file(&pdev->dev, &irkeypad_attrs[i]);
+	}
+	destroy_workqueue(irkeypad->wq);
+	platform_set_drvdata(pdev, NULL);
+ 
+        kfree(irkeypad);
+    
+	GL5201_IRKEYPAD_INFO("[%s finished]\n", __func__);
+	return 0;
+}
+
+
+void atc260x_irkeypad_reset_irc(void)
+{
+        GL5201_IRKEYPAD_INFO("%s ,reset IRC protocol!\n", __func__);
+	atc260x_set_bits(atc260x_dev_global, ATC2603C_IRC_CTL,
+		0x0003, 0x0002);
+	msleep(100);
+	atc260x_set_bits(atc260x_dev_global, ATC2603C_IRC_CTL,
+		0x0003, 0x0001);
+}
+
+static void atc260x_irkeypad_shutdown(struct platform_device *pdev){
+	atc260x_irkeypad_reset_irc();
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void irkeypad_early_suspend(struct early_suspend *h)
+{
+//	atc260x_set_bits(atc260x_dev_global, atc2603_IRC_CTL,
+//		0x0004, 0x0000);
+	suspending = true;
+        GL5201_IRKEYPAD_INFO("[%s finished]\n", __func__);
+}
+
+static void irkeypad_late_resume(struct early_suspend *h)
+{
+//	atc260x_set_bits(atc260x_dev_global, atc2603_IRC_CTL,
+//		0x0004, 0x0004);
+	suspending = false;
+	power_timeout = jiffies + 5*HZ;
+        GL5201_IRKEYPAD_INFO("[%s finished]\n", __func__);
+}
+
+static struct early_suspend irkeypad_early_suspend_desc = {
+		.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN,
+		.suspend = irkeypad_early_suspend,
+		.resume = irkeypad_late_resume,
+};
+#endif
+static int atc260x_irkeypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct asoc_irkeypad *irkeypad = platform_get_drvdata(pdev);
+	asoc_irkeypad_stop(irkeypad);
+
+	atc260x_irkeypad_reset_irc();
+        GL5201_IRKEYPAD_INFO("[%s finished]\n", __func__);
+	return 0;
+}
+
+static int atc260x_irkeypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct asoc_irkeypad *irkeypad = platform_get_drvdata(pdev);
+        wakeup_powerkey_check(irkeypad);
+	asoc_irkeypad_start(irkeypad);
+        GL5201_IRKEYPAD_INFO("[%s finished]\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops s_atc260x_irkeypad_pm_ops = {
+	.suspend        = atc260x_irkeypad_suspend,
+	.resume	        = atc260x_irkeypad_resume,
+};
+
+static const struct of_device_id atc260x_irkey_of_match[] = {
+        {.compatible = "actions,atc2603a-irkeypad",},
+        {.compatible = "actions,atc2603c-irkeypad",},
+        {.compatible = "actions,atc2609a-irkeypad",},
+        {}
+};
+MODULE_DEVICE_TABLE(of, atc260x_irkey_of_match);
+
+static struct platform_driver atc260x_irkeypad_driver = {
+        .driver         = {
+                .name   = "atc260x-irkeypad",
+                .owner  = THIS_MODULE,
+                .pm     = &s_atc260x_irkeypad_pm_ops,
+                .of_match_table = of_match_ptr(atc260x_irkey_of_match),
+        },
+	.probe		= atc260x_irkeypad_probe,
+	.remove		= atc260x_irkeypad_remove,
+        .shutdown       = atc260x_irkeypad_shutdown,
+};
+
+static int __init atc260x_irkeypad_init(void)
+{
+	int ret;
+	ret=platform_driver_register(&atc260x_irkeypad_driver);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	register_early_suspend(&irkeypad_early_suspend_desc);
+#endif
+	return ret;
+}
+
+static void __exit atc260x_irkeypad_exit(void)
+{
+	platform_driver_unregister(&atc260x_irkeypad_driver);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	/*if config for early suspen , unregister it */	
+        unregister_early_suspend(&irkeypad_early_suspend_desc);
+#endif
+}
+
+module_init(atc260x_irkeypad_init);
+module_exit(atc260x_irkeypad_exit);
+
+MODULE_DESCRIPTION("Asoc irkeypad controller drvier");
+MODULE_AUTHOR("ZhigaoNi <zhigaoni at actions-semi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:asoc-irkeypad");
diff --git a/drivers/input/keyboard/atc260x-onoff.c b/drivers/input/keyboard/atc260x-onoff.c
new file mode 100755
index 0000000..2b2c075
--- /dev/null
+++ b/drivers/input/keyboard/atc260x-onoff.c
@@ -0,0 +1,558 @@
+/*
+ * atc260x_onoff.c  --  On/Off key driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include <mach/power.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+
+extern int owl_pm_wakeup_flag(void);
+
+
+
+struct atc260x_onoff_dev;
+typedef int (*atc260x_onoff_init_func_t)(struct atc260x_onoff_dev *atc260x_onoff);
+
+struct atc260x_onoff_regdef {
+	atc260x_onoff_init_func_t init_func;
+	atc260x_onoff_init_func_t exit_func;
+	u32     reg_int_ctl;
+	u32     reg_int_pnd;
+	u32     int_reg_w1c_bm;  /* 一些"写1清0"功能位的位码. 用于应付不同的写副作用寄存器位混编的brain-damaged设计. */
+	u32     pnd_reg_w1c_bm;  /* 一些"写1清0"功能位的位码. */
+	u32     kdwn_state_bitm;
+	u32     long_int_pnd_bitm;
+	u32     short_int_pnd_bitm;
+	u32     kdwn_int_pnd_bitm;
+	u32     press_int_en_bitm;
+	u32     kdwn_int_en_bitm;
+	u32     reg_reset_pnd;
+	u32     reset_pnd_bitm;
+};
+
+/* On/Off module structure */
+struct atc260x_onoff_dev {
+	struct device *dev;
+	struct input_dev *idev;
+	struct atc260x_dev *atc260x;
+	const struct atc260x_onoff_regdef *regdef;
+	struct delayed_work work;
+	uint irq;
+	uint key_trigger;
+	uint suspend_state;
+};
+
+
+
+/* for atc2603a ------------------------------------------------------------- */
+
+static int _atc2603a_onoff_init(struct atc260x_onoff_dev *atc260x_onoff)
+{
+	uint onoff_time = 0; /* more quick response of the on/off key */
+	return atc260x_reg_setbits(atc260x_onoff->atc260x, ATC2603A_PMU_SYS_CTL2,
+		(1U << 12) | (3U << 10) | (1<<14)|(1<<13),
+		(1U << 12) | (onoff_time << 10) | (1<<14)|(1<<13));
+	/* Don't touch the INTS_MSK register here, it is own by regmap now.
+	 * Register interrupt also enable the source in INTS_MSK register. */
+}
+static int _atc2603a_onoff_exit(struct atc260x_onoff_dev *atc260x_onoff)
+{
+	/* set long_key to 2s */
+	return atc260x_reg_setbits(atc260x_onoff->atc260x, ATC2603A_PMU_SYS_CTL2,
+		(3U << 10), (2 << 10));
+}
+static const struct atc260x_onoff_regdef sc_atc2603a_onoff_regdef = {
+	.init_func          = _atc2603a_onoff_init,
+	.exit_func          = _atc2603a_onoff_exit,
+	.reg_int_ctl        = ATC2603A_PMU_SYS_CTL2,
+	.reg_int_pnd        = ATC2603A_PMU_SYS_CTL2,
+	.int_reg_w1c_bm     = (1<<14)|(1<<13),
+	.pnd_reg_w1c_bm     = (1<<14)|(1<<13),
+	.kdwn_state_bitm    = (1 << 15),
+	.long_int_pnd_bitm  = (1 << 13),
+	.short_int_pnd_bitm = (1 << 14),
+	.kdwn_int_pnd_bitm  = 0,
+	.press_int_en_bitm  = (1 << 12),
+	.kdwn_int_en_bitm   = 0,
+	.reg_reset_pnd		= 0,
+	.reset_pnd_bitm		= 0,
+};
+
+
+/* for atc2603c ------------------------------------------------------------- */
+
+static int _atc2603c_onoff_init(struct atc260x_onoff_dev *atc260x_onoff)
+{
+	uint onoff_time = 2; /* <2s for short_key, >2s for long_key */
+	return atc260x_reg_setbits(atc260x_onoff->atc260x, ATC2603A_PMU_SYS_CTL2,
+		(1U << 12) | (1 << 1) | (3U << 10) | (1<<14)|(1<<13)|(1<<2),
+		(1U << 12) | (1 << 1) | (onoff_time << 10) | (1<<14)|(1<<13)|(1<<2));
+	/* Don't touch the INTS_MSK register here, it is own by regmap now.
+	 * Register interrupt also enable the source in INTS_MSK register. */
+}
+static const struct atc260x_onoff_regdef sc_atc2603c_onoff_regdef = {
+	.init_func          = _atc2603c_onoff_init,
+	.reg_int_ctl        = ATC2603A_PMU_SYS_CTL2,
+	.reg_int_pnd        = ATC2603A_PMU_SYS_CTL2,
+	.int_reg_w1c_bm     = (1<<14)|(1<<13)|(1<<2),
+	.pnd_reg_w1c_bm     = (1<<14)|(1<<13)|(1<<2),
+	.kdwn_state_bitm    = (1 << 15),
+	.long_int_pnd_bitm  = (1 << 13),
+	.short_int_pnd_bitm = (1 << 14),
+	.kdwn_int_pnd_bitm  = (1 << 2),
+	.press_int_en_bitm  = (1 << 12),
+	.kdwn_int_en_bitm   = (1 << 1),
+	.reg_reset_pnd		= ATC2603A_PMU_SYS_CTL1,
+	.reset_pnd_bitm		= (1 << 10),
+};
+
+
+/* for atc2609a ------------------------------------------------------------- */
+
+static int _atc2609a_onoff_init(struct atc260x_onoff_dev *atc260x_onoff)
+{
+	uint onoff_time = 2; /* <2s for short_key, >2s for long_key */
+	return atc260x_reg_setbits(atc260x_onoff->atc260x, ATC2603A_PMU_SYS_CTL2,
+		(1U << 12) | (1 << 1) | (3U << 10) | (1<<14)|(1<<13)|(1<<2),
+		(1U << 12) | (1 << 1) | (onoff_time << 10) | (1<<14)|(1<<13)|(1<<2));
+	/* Don't touch the INTS_MSK register here, it is own by regmap now.
+	 * Register interrupt also enable the source in INTS_MSK register. */
+}
+static const struct atc260x_onoff_regdef sc_atc2609a_onoff_regdef = {
+	.init_func          = _atc2609a_onoff_init,
+	.reg_int_ctl        = ATC2603A_PMU_SYS_CTL2,
+	.reg_int_pnd        = ATC2603A_PMU_SYS_CTL2,
+	.int_reg_w1c_bm     = (1<<14)|(1<<13)|(1<<2),
+	.pnd_reg_w1c_bm     = (1<<14)|(1<<13)|(1<<2),
+	.kdwn_state_bitm    = (1 << 15),
+	.long_int_pnd_bitm  = (1 << 13),
+	.short_int_pnd_bitm = (1 << 14),
+	.kdwn_int_pnd_bitm  = (1 << 2),
+	.press_int_en_bitm  = (1 << 12),
+	.kdwn_int_en_bitm   = (1 << 1),
+	.reg_reset_pnd		= 0,
+	.reset_pnd_bitm		= 0,
+};
+
+static const struct atc260x_onoff_regdef * const sc_atc260x_onoff_regdef_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = &sc_atc2603a_onoff_regdef,
+	[ATC260X_ICTYPE_2603C] = &sc_atc2603c_onoff_regdef,
+	[ATC260X_ICTYPE_2609A] = &sc_atc2609a_onoff_regdef,
+};
+
+
+/* Read-Modify-Write operation */
+static void _atc260x_onoff_rmw_intreg(struct atc260x_dev *atc260x,
+	const struct atc260x_onoff_regdef *regdef, uint mask, uint value)
+{
+	int ret;
+	ret = atc260x_reg_wp_setbits(atc260x, regdef->reg_int_ctl,
+		regdef->int_reg_w1c_bm, mask, value);
+	if (ret) {
+		pr_err("%s() failed to r/w onoff int reg\n", __func__);
+	}
+}
+
+/* Clear-Pend operation */
+static void _atc260x_onoff_w1clr_pndreg(struct atc260x_dev *atc260x,
+	const struct atc260x_onoff_regdef *regdef, uint mask)
+{
+	int ret;
+	ret = atc260x_reg_wp_clrpnd(atc260x, regdef->reg_int_pnd,
+		regdef->pnd_reg_w1c_bm, mask);
+	if (ret) {
+		pr_err("%s() failed to w onoff pnd reg\n", __func__);
+	}
+}
+
+/**
+ * The chip gives us an interrupt when the ON/OFF pin is asserted but we
+ * then need to poll to see when the pin is deasserted.
+ */
+static void atc260x_poll_onoff(struct work_struct *work)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = container_of(work, struct atc260x_onoff_dev,
+						   work.work);
+	struct atc260x_dev *atc260x = atc260x_onoff->atc260x;
+	const struct atc260x_onoff_regdef *regdef = atc260x_onoff->regdef;
+	int poll, ret;
+
+	ret = atc260x_reg_read(atc260x, regdef->reg_int_pnd);
+	if (ret >= 0) {
+		poll = atc260x_onoff->key_trigger || (ret & regdef->kdwn_state_bitm);
+		atc260x_onoff->key_trigger = 0;
+		dev_dbg(atc260x_onoff->dev, "On/Off key CTL2=%x, poll=%d\n", ret, poll);
+
+		/* report key pressed */
+		input_report_key(atc260x_onoff->idev, KEY_POWER, poll);
+		input_sync(atc260x_onoff->idev);
+	} else {
+		dev_err(atc260x_onoff->dev, "Failed to read ON/OFF status: %d\n", ret);
+		poll = 1;
+	}
+
+	/* if key is pressed, check key status after 200 ms */
+	if (poll) {
+		schedule_delayed_work(&atc260x_onoff->work, msecs_to_jiffies(200));
+	} else {
+		/* cleare On/Off press pending */
+		_atc260x_onoff_w1clr_pndreg(atc260x, regdef,
+			(regdef->long_int_pnd_bitm | regdef->short_int_pnd_bitm |
+			regdef->kdwn_int_pnd_bitm));
+		/* enable the On/Off IRQ */
+		_atc260x_onoff_rmw_intreg(atc260x, regdef,
+			(regdef->press_int_en_bitm | regdef->kdwn_int_en_bitm),
+			(regdef->press_int_en_bitm | regdef->kdwn_int_en_bitm));
+	}
+}
+
+static void _atc260x_onoff_trigger_key_process(struct atc260x_onoff_dev *atc260x_onoff, uint delay)
+{
+	struct atc260x_dev *atc260x = atc260x_onoff->atc260x;
+	const struct atc260x_onoff_regdef *regdef = atc260x_onoff->regdef;
+
+	/* disable On/Off interrupt, but not clear the On/Off IRQ pending bits */
+	_atc260x_onoff_rmw_intreg(atc260x, regdef,
+		(regdef->press_int_en_bitm | regdef->kdwn_int_en_bitm), 0);
+
+	atc260x_onoff->key_trigger = 1;
+	schedule_delayed_work(&atc260x_onoff->work, delay);
+}
+
+static void atc260x_onoff_irq_handle(struct atc260x_onoff_dev *atc260x_onoff);
+
+/**
+ * On/Off IRQ hander, run in kernel thread of ATC260X core IRQ dispatcher
+ */
+static irqreturn_t atc260x_onoff_irq(int irq, void *data)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = data;
+
+	atc260x_onoff_irq_handle(atc260x_onoff);
+	/*dev_dbg(atc260x_onoff->dev, "on/off interrupt!\n"); */
+
+	_atc260x_onoff_trigger_key_process(atc260x_onoff, 0);
+	return IRQ_HANDLED;
+}
+
+static int atc260x_onoff_suspend(struct device *dev)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = dev_get_drvdata(dev);
+	struct atc260x_dev *atc260x = atc260x_onoff->atc260x;
+	const struct atc260x_onoff_regdef *regdef = atc260x_onoff->regdef;
+	dev_info(atc260x_onoff->dev, "%s() enter\n", __func__);
+
+	/* disable On/Off interrupt */
+	_atc260x_onoff_rmw_intreg(atc260x, regdef,
+		(regdef->press_int_en_bitm | regdef->kdwn_int_en_bitm), 0);
+
+	cancel_delayed_work_sync(&atc260x_onoff->work);
+
+	atc260x_onoff->suspend_state = 1;
+	/*wake up in 0.5s,not 2s*/
+	atc260x_reg_setbits(atc260x,ATC2603C_PMU_SYS_CTL2,0x0c00,0);
+
+	return 0;
+}
+
+static int atc260x_onoff_resume(struct device *dev)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = dev_get_drvdata(dev);
+	struct atc260x_dev *atc260x = atc260x_onoff->atc260x;
+	
+	int ret;
+
+	dev_info(atc260x_onoff->dev, "%s() enter\n", __func__);
+
+	ret = (atc260x_onoff->regdef->init_func)(atc260x_onoff);
+	if (ret) {
+		dev_info(atc260x_onoff->dev, "%s() hw re-init failed\n", __func__);
+		return -EIO;
+	}
+	/*long time press back to 2s*/
+	atc260x_reg_setbits(atc260x,ATC2603C_PMU_SYS_CTL2,0x0c00,(2 << 10));
+
+	return 0;
+}
+
+static int atc260x_onoff_suspend_prepare(struct device *dev)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = dev_get_drvdata(dev);
+	dev_info(atc260x_onoff->dev, "%s() enter\n", __func__);
+	atc260x_onoff->suspend_state = 0;
+	return 0;
+}
+
+static void atc260x_onoff_resume_complete(struct device *dev)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = dev_get_drvdata(dev);
+
+	dev_info(atc260x_onoff->dev, "%s() enter\n", __func__);
+
+	/* resend the long/short press key sequence, according to the active wakeup source. */
+	if (atc260x_onoff->suspend_state != 0) {
+		uint wakeup_flag = owl_pm_wakeup_flag();
+		uint sim_wake_srcs = OWL_PMIC_WAKEUP_SRC_WALL_IN | OWL_PMIC_WAKEUP_SRC_VBUS_IN;
+#if 1
+		/* distinguish long/short press, resend the corresponding sequence. */
+		if (wakeup_flag & (OWL_PMIC_WAKEUP_SRC_ONOFF_SHORT | sim_wake_srcs)) {
+			dev_info(atc260x_onoff->dev, "resend/simulate On/Off short press...\n");
+			input_report_key(atc260x_onoff->idev, KEY_POWER, 1);
+			input_report_key(atc260x_onoff->idev, KEY_POWER, 0);
+			input_sync(atc260x_onoff->idev);
+		} else if (wakeup_flag & OWL_PMIC_WAKEUP_SRC_ONOFF_LONG) {
+			dev_info(atc260x_onoff->dev, "resend/simulate On/Off long press...\n");
+			input_report_key(atc260x_onoff->idev, KEY_POWER, 1);
+			input_sync(atc260x_onoff->idev);
+			_atc260x_onoff_trigger_key_process(atc260x_onoff, msecs_to_jiffies(2500));
+		}
+#else
+		/* Only resend short press. */
+		if (wakeup_flag & (OWL_PMIC_WAKEUP_SRC_ONOFF_SHORT | OWL_PMIC_WAKEUP_SRC_ONOFF_LONG | sim_wake_srcs)) {
+			dev_info(atc260x_onoff->dev, "resend/simulate On/Off short press...\n");
+			input_report_key(atc260x_onoff->idev, KEY_POWER, 1);
+			input_report_key(atc260x_onoff->idev, KEY_POWER, 0);
+			input_sync(atc260x_onoff->idev);
+		}
+#endif
+	}
+}
+
+static const struct dev_pm_ops s_atc260x_onoff_pm_ops = {
+	.suspend       = atc260x_onoff_suspend,
+	.resume        = atc260x_onoff_resume,
+	.prepare       = atc260x_onoff_suspend_prepare,
+	.complete      = atc260x_onoff_resume_complete,
+};
+
+void atc260x_onoff_shutdown(struct platform_device *pdev)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = platform_get_drvdata(pdev);
+	struct atc260x_dev *atc260x = atc260x_onoff->atc260x;
+	const struct atc260x_onoff_regdef *regdef = atc260x_onoff->regdef;
+
+	dev_info(atc260x_onoff->dev, "%s() enter\n", __func__);
+
+	/* call exit_func */
+	if (regdef->exit_func != NULL) {
+		(regdef->exit_func)(atc260x_onoff);
+	}
+
+	/* disable On/Off interrupt */
+	_atc260x_onoff_rmw_intreg(atc260x, regdef,
+		(regdef->press_int_en_bitm | regdef->kdwn_int_en_bitm), 0);
+}
+
+static int onoff_reset_irq = 0;
+static struct irq_domain *onoff_reset_domain;
+
+static void atc260x_onoff_irq_handle(struct atc260x_onoff_dev *atc260x_onoff)
+{
+	if(atc260x_onoff->regdef->reg_reset_pnd != 0 && onoff_reset_irq > 0)
+	{
+		int ret;
+		struct atc260x_dev *atc260x = atc260x_onoff->atc260x;
+		const struct atc260x_onoff_regdef *regdef = atc260x_onoff->regdef;
+
+		ret = atc260x_reg_read(atc260x, regdef->reg_reset_pnd);
+		if(ret > 0 && (ret & regdef->reset_pnd_bitm) != 0)
+		{
+			handle_nested_irq(onoff_reset_irq);
+			pr_err("[onoff_reset_irq] %s: send irq!\n", __func__);
+		}
+	}
+}
+
+static void onoff_reset_irq_disable(struct irq_data *data)
+{
+}
+
+static void onoff_reset_irq_enable(struct irq_data *data)
+{
+}
+
+static struct irq_chip owl_sirq_irq = {
+	.name = "onoff_reset_irq",
+	.irq_ack = onoff_reset_irq_disable,
+	.irq_mask = onoff_reset_irq_disable,
+	.irq_mask_ack = onoff_reset_irq_disable,
+	.irq_unmask = onoff_reset_irq_enable,
+};
+
+static int onoff_reset_irq_map(struct irq_domain *d, unsigned int virq,
+				irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(virq, &owl_sirq_irq, handle_simple_irq);
+	set_irq_flags(virq, IRQF_VALID);
+	return 0;
+}
+
+
+static struct irq_domain_ops onoff_reset_irq_ops = {
+	.map    = onoff_reset_irq_map,
+	.xlate  = irq_domain_xlate_onecell,
+};
+
+static int atc260x_onoff_irq_chip_init(struct platform_device *pdev)
+{
+	onoff_reset_domain = irq_domain_add_linear(pdev->dev.of_node,
+					   1, &onoff_reset_irq_ops, NULL);
+
+	if (!onoff_reset_domain)
+	{
+		pr_err("[onoff_reset_irq] %s: irq_domain_add_linear failed!\n", __func__);
+		return -ENODEV;
+	}
+
+	onoff_reset_irq = irq_create_mapping(onoff_reset_domain, 0);
+	if(onoff_reset_irq <= 0)
+	{
+		pr_err("[onoff_reset_irq] %s: irq_create_mapping failed!\n", __func__);
+		return -ENODEV;
+	}
+	
+	return 0;
+}
+
+static int atc260x_onoff_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x;
+	struct atc260x_onoff_dev *atc260x_onoff;
+	uint ic_type;
+	int irq, ret;
+
+	dev_info(&pdev->dev, "Probing %s\n", pdev->name);
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	atc260x_onoff = devm_kzalloc(&pdev->dev, sizeof(struct atc260x_onoff_dev),
+				GFP_KERNEL);
+	if (!atc260x_onoff) {
+		dev_err(&pdev->dev, "Can't allocate data\n");
+		return -ENOMEM;
+	}
+	atc260x_onoff->dev = &pdev->dev;
+	atc260x_onoff->atc260x = atc260x;
+	platform_set_drvdata(pdev, atc260x_onoff);
+
+	INIT_DELAYED_WORK(&atc260x_onoff->work, atc260x_poll_onoff);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "No IRQ resource for On/Off key\n");
+		ret = -ENODEV;
+		goto err;
+	}
+	dev_info(&pdev->dev, "atc260x_onoff IRQ num : %u\n", irq);
+	atc260x_onoff->irq = irq;
+
+	atc260x_onoff->idev = input_allocate_device();
+	if (!atc260x_onoff->idev) {
+		dev_err(&pdev->dev, "Can't allocate input dev\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	atc260x_onoff->idev->evbit[0] = BIT_MASK(EV_KEY);
+	atc260x_onoff->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+	atc260x_onoff->idev->name = "atc260x_onoff";
+	atc260x_onoff->idev->phys = "atc260x_onoff/input0";
+	atc260x_onoff->idev->dev.parent = &pdev->dev;
+
+	ret = input_register_device(atc260x_onoff->idev);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret);
+		goto err_input_alloc_dev;
+	}
+
+	/* init & enable hardware. */
+	ic_type = atc260x_get_ic_type(atc260x);
+	BUG_ON(ic_type >= ARRAY_SIZE(sc_atc260x_onoff_regdef_tbl));
+	atc260x_onoff->regdef = sc_atc260x_onoff_regdef_tbl[ic_type];
+	BUG_ON(atc260x_onoff->regdef == NULL);
+	ret = (atc260x_onoff->regdef->init_func)(atc260x_onoff);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s() hardware init err, ret=%d\n", __func__, ret);
+		goto err_input_reg_dev;
+	}
+
+	/*
+	 *use default primary handle, and atc260x_onoff_irq run in ATC260X core irq kernel thread
+	 */
+	ret = devm_request_threaded_irq(&pdev->dev, atc260x_onoff->irq, NULL,
+			atc260x_onoff_irq, IRQF_TRIGGER_HIGH, "atc260x_onoff",
+			atc260x_onoff);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
+		goto err_input_reg_dev;
+	}
+
+	atc260x_onoff_irq_chip_init(pdev);
+	
+	return 0;
+
+err_input_reg_dev:
+	input_unregister_device(atc260x_onoff->idev);
+err_input_alloc_dev:
+	input_free_device(atc260x_onoff->idev);
+err:
+	return ret;
+}
+
+static int atc260x_onoff_remove(struct platform_device *pdev)
+{
+	struct atc260x_onoff_dev *atc260x_onoff = platform_get_drvdata(pdev);
+	cancel_delayed_work_sync(&atc260x_onoff->work);
+	input_unregister_device(atc260x_onoff->idev);
+	return 0;
+}
+
+static const struct of_device_id atc260x_onoff_match[] = {
+	{ .compatible = "actions,atc2603a-onoff", },
+	{ .compatible = "actions,atc2603c-onoff", },
+	{ .compatible = "actions,atc2609a-onoff", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_onoff_match);
+
+static struct platform_driver atc260x_onoff_driver = {
+	.driver     = {
+		.name   = "atc260x-onoff",
+		.owner  = THIS_MODULE,
+		.pm     = &s_atc260x_onoff_pm_ops,
+		.of_match_table = of_match_ptr(atc260x_onoff_match),
+	},
+	.probe      = atc260x_onoff_probe,
+	.remove     = atc260x_onoff_remove,
+	.shutdown   = atc260x_onoff_shutdown,
+};
+
+module_platform_driver(atc260x_onoff_driver);
+
+MODULE_ALIAS("platform:atc260x-onff");
+MODULE_DESCRIPTION("ATC260X ON/OFF pin");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Actions Semi, Inc");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
old mode 100644
new mode 100755
index adff060..59f8348
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1453,6 +1453,16 @@ config MFD_WM8994
 	  core support for the WM8994, in order to use the actual
 	  functionaltiy of the device other drivers must be enabled.
 
+config MFD_ATC260X
+	tristate "Actions atc260x multifunctions device"
+	depends on SPI_OWL
+	select MFD_CORE
+	select REGMAP_I2C
+	select REGMAP_SPI
+	select REGMAP_IRQ
+	help
+	  ATC260X PMIC core driver.
+
 config MFD_STW481X
 	tristate "Support for ST Microelectronics STw481x"
 	depends on I2C && ARCH_NOMADIK
@@ -1499,5 +1509,10 @@ config MFD_VEXPRESS_SYSREG
 	  System Registers are the platform configuration block
 	  on the ARM Ltd. Versatile Express board.
 
+config ATC260X_PWM
+	tristate "Actions atc260x PWM support"
+ 	help
+  	  Say Y here if you want to use the pwm on atc260x PMIC.
+
 endmenu
 endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
old mode 100644
new mode 100755
index ab95dac..49479f6
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,6 +8,11 @@ obj-$(CONFIG_MFD_88PM800)	+= 88pm800.o 88pm80x.o
 obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
+atc260x-objs			:= atc260x-core.o atc260x-spi.o atc260x-i2c.o
+atc260x-objs			+= atc260x-auxadc.o atc260x-cmu.o atc260x-pstore.o atc260x-ex-api.o
+obj-$(CONFIG_MFD_ATC260X)	+= atc260x.o
+obj-$(CONFIG_MFD_ATC260X)	+= atc260x-pm.o
+obj-$(CONFIG_ATC260X_PWM) += atc260x-pwm.o
 obj-$(CONFIG_MFD_BCM590XX)	+= bcm590xx.o
 obj-$(CONFIG_MFD_CROS_EC)	+= cros_ec.o
 obj-$(CONFIG_MFD_CROS_EC_I2C)	+= cros_ec_i2c.o
diff --git a/drivers/mfd/atc260x-auxadc.c b/drivers/mfd/atc260x-auxadc.c
new file mode 100755
index 0000000..528c121
--- /dev/null
+++ b/drivers/mfd/atc260x-auxadc.c
@@ -0,0 +1,1027 @@
+/* UTF-8 encoded. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include "atc260x-core.h"
+
+
+/* 为了兼容N颗IC, 这里使用了比较繁琐的数据结构. */
+
+#define ATC260x_AUXADC_MAX_CHNL 20
+
+
+struct atc260x_auxadc_chn_hwinfo;
+typedef int (*atc260x_auxadc_getraw_func_t)(uint channel, const struct atc260x_auxadc_chn_hwinfo *ch_info, struct atc260x_dev *atc260x);
+typedef int (*atc260x_auxadc_translate_func_t)(uint raw_val, const struct atc260x_auxadc_chn_hwinfo *ch_info, struct atc260x_dev *atc260x);
+
+struct atc260x_auxadc_chn_hwinfo {
+	u16     dat_reg_addr;
+	u8      dat_bit_offs;
+	u8      dat_bit_cnt;
+
+	/* get raw value function, if the above setting is not useful. */
+	atc260x_auxadc_getraw_func_t getraw_func;
+
+	/* translate function, for complex transformation */
+	atc260x_auxadc_translate_func_t tran_func;
+
+	/* simple linear transformation, fall-back method. */
+	/* y = ax + b,  a=tr_multiplier/(1<<tr_r_shift), b=tr_offset */
+	s32     tr_offset;
+	s32     tr_multiplier;
+	u8      tr_r_shift;
+};
+
+struct atc260x_auxadc_hwinfo {
+	uint            channels;
+	const char      *chn_names[ATC260x_AUXADC_MAX_CHNL];
+	const char      *chn_unit_names[ATC260x_AUXADC_MAX_CHNL]; /* 度量单位. */
+	const struct atc260x_auxadc_chn_hwinfo *chn_hwinfos[ATC260x_AUXADC_MAX_CHNL];
+};
+
+
+/* for atc2603a ------------------------------------------------------------- */
+/* no postfix means rev.D */
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_batv = {
+	.dat_reg_addr    = ATC2603A_PMU_BATVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv * 2 */
+	.tr_r_shift      = 6,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_bati = {
+	.dat_reg_addr    = ATC2603A_PMU_BATIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500, /* raw * 1500 / 1024 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_vbusv = {
+	.dat_reg_addr    = ATC2603A_PMU_VBUSVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1875, /* raw * 2.9296875mv * 2.5 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_vbusi = {
+	.dat_reg_addr    = ATC2603A_PMU_VBUSIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500, /* raw * 1500 / 1024 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_syspwrv = {
+	.dat_reg_addr    = ATC2603A_PMU_SYSPWRADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1875, /* raw * 2.9296875mv * 2.5 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_wallv = {
+	.dat_reg_addr    = ATC2603A_PMU_WALLVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1875, /* raw * 2.9296875mv * 2.5 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_walli = {
+	.dat_reg_addr    = ATC2603A_PMU_WALLIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500, /* raw * 1500 / 1024 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_chgi = {
+	.dat_reg_addr    = ATC2603A_PMU_CHGIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500, /* raw * 1500 / 1024 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_iref = {
+	.dat_reg_addr    = ATC2603A_PMU_IREFADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv */
+	.tr_r_shift      = 7,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_remcon = {
+	.dat_reg_addr    = ATC2603A_PMU_REMCONADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_ictemp = {
+	.dat_reg_addr    = ATC2603A_PMU_ICTEMPADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 399155,   /* (raw_val * 194.9) - 14899 - 30000 (mC) */
+	.tr_r_shift      = 11,
+	.tr_offset       = -14899 -30000,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_bakbatv = {
+	.dat_reg_addr    = ATC2603A_PMU_BAKBATADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv */
+	.tr_r_shift      = 7,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_aux0 = {
+	.dat_reg_addr    = ATC2603A_PMU_AUXADC0,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_aux1 = {
+	.dat_reg_addr    = ATC2603A_PMU_AUXADC1,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_aux2 = {
+	.dat_reg_addr    = ATC2603A_PMU_AUXADC2,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603a_chn_hwinfos_aux3 = {
+	.dat_reg_addr    = ATC2603A_PMU_AUXADC3,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_hwinfo sc_atc2603a_auxadc_hwinfo = {
+	.channels = 16,
+	.chn_names = {
+		"BATV", "BATI", "VBUSV", "VBUSI", "SYSPWRV", "WALLV", "WALLI", "CHGI",
+		"IREF", "REMCON", "ICTEMP", "BAKBATV", "AUX0", "AUX1", "AUX2", "AUX3"
+	},
+	.chn_unit_names = {
+		"mV", "mA", "mV", "mA", "mV", "mV", "mA", "mA",
+		"mV", "/1024", "mCel", "mV", "/1024", "/1024", "/1024", "/1024"
+	},
+	.chn_hwinfos = {
+		&sc_atc2603a_chn_hwinfos_batv,
+		&sc_atc2603a_chn_hwinfos_bati,
+		&sc_atc2603a_chn_hwinfos_vbusv,
+		&sc_atc2603a_chn_hwinfos_vbusi,
+		&sc_atc2603a_chn_hwinfos_syspwrv,
+		&sc_atc2603a_chn_hwinfos_wallv,
+		&sc_atc2603a_chn_hwinfos_walli,
+		&sc_atc2603a_chn_hwinfos_chgi,
+		&sc_atc2603a_chn_hwinfos_iref,
+		&sc_atc2603a_chn_hwinfos_remcon,
+		&sc_atc2603a_chn_hwinfos_ictemp,
+		&sc_atc2603a_chn_hwinfos_bakbatv,
+		&sc_atc2603a_chn_hwinfos_aux0,
+		&sc_atc2603a_chn_hwinfos_aux1,
+		&sc_atc2603a_chn_hwinfos_aux2,
+		&sc_atc2603a_chn_hwinfos_aux3
+	}
+};
+
+
+/* for atc2603c ------------------------------------------------------------- */
+/* no postfix means rev.A */
+
+/* software fix for gl5307.
+ *
+ 0.  CHGI = ADC_DBG0 * 1546 / I_ref(max)
+ 1.  VBUSI = ADC_DBG1 *1509 / I_ref(max)
+ 2.  WALLI = ADC_DBG2 * 1527 / I_ref(max)
+ 3.  BATI = ADC_DBG3 * 1546 / I_ref(max)
+ 4.  REMCON = ADC_DBG4 * 1024 / 2*SVCC(max)
+ */
+
+struct atc260x_auxadc_udata_3c {
+	uint    dbg_ref_valid;
+	uint    adc_iref_max;       /* for DBG channels */
+	uint    adc_svcc_min;       /* for DBG channels */
+	uint    icm_res_10ohm;      /* ICM sample resistor is 10mOhm */
+};
+
+static int _atc2603c_auxadc_getraw_icm_chn(
+		uint channel, const struct atc260x_auxadc_chn_hwinfo *ch_info,
+		struct atc260x_dev *atc260x)
+{
+	uint reg_val;
+	int ret;
+
+	/* the channel is already enabled. */
+	while (1) {
+		ret = atc260x_reg_read(atc260x, ATC2603C_PMU_ICMADC);
+		if (ret < 0) {
+			return ret;
+		}
+		reg_val = ret;
+		if (reg_val & (1U << 11)) { /* CM_DATAOK ? */
+			break;
+		}
+		udelay(200);
+	}
+	return reg_val & ((1U << 11) -1U);
+}
+static void _atc2603c_auxadc_usertran_prepare_dbg_refs(struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_3c *pud;
+	uint i, tmp, iref_max, svcc_min;
+	int ret;
+	pud = (struct atc260x_auxadc_udata_3c *)(atc260x->auxadc_udata);
+	if (! pud->dbg_ref_valid) {
+		/*get max current reference value for later current value adjusting.*/
+		iref_max = 0;
+		svcc_min = (typeof(svcc_min)) -1;
+		for (i = 0; i < 10; i++){
+			ret = atc260x_auxadc_get_raw(atc260x, 8); /* channel #8, IREF */
+			if (ret < 0)
+				goto label_err;
+			tmp = 0xFFFFU & ret;
+			if (tmp > iref_max)
+				iref_max = tmp;
+			ret = atc260x_auxadc_get_raw(atc260x, 16); /* channel #16, SVCC */
+			if (ret < 0)
+				goto label_err;
+			tmp = 0xFFFFU & ret;
+			if (tmp < svcc_min)
+				svcc_min = tmp;
+			msleep(1);
+		}
+		pud->adc_iref_max = iref_max;
+		pud->adc_svcc_min = svcc_min;
+		pud->dbg_ref_valid = 1;
+	}
+	return;
+	label_err:
+	dev_err(atc260x->dev,
+		"%s() failed to get raw value of ch-8 and ch-17\n", __func__);
+}
+static int _atc2603c_auxadc_usertran_dbg_i_mode(
+		uint raw_val, const struct atc260x_auxadc_chn_hwinfo *ch_info, struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_3c *pud;
+	uint adc_iref_max;
+
+	_atc2603c_auxadc_usertran_prepare_dbg_refs(atc260x);
+
+	pud = (struct atc260x_auxadc_udata_3c *)(atc260x->auxadc_udata);
+	adc_iref_max = pud->adc_iref_max;
+	if (adc_iref_max == 0)
+		adc_iref_max = 1;
+	return raw_val * ch_info->tr_multiplier / adc_iref_max;
+}
+static int _atc2603c_auxadc_usertran_dbg_rem_mode(
+		uint raw_val, const struct atc260x_auxadc_chn_hwinfo *ch_info, struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_3c *pud;
+	uint adc_svcc_min;
+
+	_atc2603c_auxadc_usertran_prepare_dbg_refs(atc260x);
+
+	/* remcon_uv = ADC_DBG4*(1024/2)*SVCCADC(小) , 单位是uV */
+	/* remcon_ratio = (remcon_uv * 1024) / svcc_uv
+	 * remcon_ratio = (remcon_uv *1024) / (SVCCADC * 2929.6875 * 2 )
+	 * remcon_ratio = ADC_DBG4*512*512 / 2929.6875
+	 * remcon_ratio ~= (ADC_DBG4 * 46912496) >> 19   (SVCCADC??)
+	 */
+
+	pud = (struct atc260x_auxadc_udata_3c *)(atc260x->auxadc_udata);
+	adc_svcc_min = pud->adc_svcc_min;
+	if (adc_svcc_min == 0)
+		adc_svcc_min = 1;
+	return raw_val * 512U / adc_svcc_min;
+}
+static int _atc2603c_auxadc_usertran_icm(
+		uint raw_val, const struct atc260x_auxadc_chn_hwinfo *ch_info, struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_3c *pud;
+	int tmp;
+	pud = (struct atc260x_auxadc_udata_3c *)(atc260x->auxadc_udata);
+	tmp = raw_val & 0x3ffU;
+	if (pud->icm_res_10ohm == 0) {
+		tmp = (tmp * 9375U) >> 12;   /*tmp = tmp * 2343.75U / 1024U; */
+	} else {
+		tmp = (tmp * 9375U) >> 11;  /*tmp = tmp * 4687.5U / 1024U; */
+	}
+	if (raw_val & 0x400U) {
+		tmp = -tmp;
+		/*TODO: "+1step effect" in minus direction. */
+	}
+	return tmp;
+}
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_batv = {
+	.dat_reg_addr    = ATC2603C_PMU_BATVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv * 2 */
+	.tr_r_shift      = 6,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_a_chn_hwinfos_bati = {
+	.dat_reg_addr    = ATC2603C_PMU_ADC_DBG3,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1546,
+	.tran_func       = _atc2603c_auxadc_usertran_dbg_i_mode,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_b_chn_hwinfos_bati = {
+	.dat_reg_addr    = ATC2603C_PMU_BATIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500,
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_vbusv = {
+	.dat_reg_addr    = ATC2603C_PMU_VBUSVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1875, /* raw * 2.9296875mv * 2.5 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_a_chn_hwinfos_vbusi = {
+	.dat_reg_addr    = ATC2603C_PMU_ADC_DBG1,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1509,
+	.tran_func       = _atc2603c_auxadc_usertran_dbg_i_mode,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_b_chn_hwinfos_vbusi = {
+	.dat_reg_addr    = ATC2603C_PMU_VBUSIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500,
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_syspwrv = {
+	.dat_reg_addr    = ATC2603C_PMU_SYSPWRADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1875, /* raw * 2.9296875mv * 2.5 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_wallv = {
+	.dat_reg_addr    = ATC2603C_PMU_WALLVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1875, /* raw * 2.9296875mv * 2.5 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_a_chn_hwinfos_walli = {
+	.dat_reg_addr    = ATC2603C_PMU_WALLIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500,
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_b_chn_hwinfos_walli = {
+	.dat_reg_addr    = ATC2603C_PMU_ADC_DBG2,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1500,
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_a_chn_hwinfos_chgi = {
+	.dat_reg_addr    = ATC2603C_PMU_ADC_DBG0,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1546,
+	.tran_func       = _atc2603c_auxadc_usertran_dbg_i_mode,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_ver_b_chn_hwinfos_chgi = {
+	.dat_reg_addr    = ATC2603C_PMU_CHGIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 2000,
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_iref = {
+	.dat_reg_addr    = ATC2603C_PMU_IREFADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv */
+	.tr_r_shift      = 7,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_remcon = {
+	.dat_reg_addr    = ATC2603C_PMU_ADC_DBG4,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tran_func       = _atc2603c_auxadc_usertran_dbg_rem_mode, /* output raw value without BUG */
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_ictemp = {
+	.dat_reg_addr    = ATC2603C_PMU_ICTEMPADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 399155,   /* (raw_val * 194.9) - 14899 - 30000 (mC) */
+	.tr_r_shift      = 11,
+	.tr_offset       = -14899 -30000,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_bakbatv = {
+	.dat_reg_addr    = ATC2603C_PMU_BAKBATADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv */
+	.tr_r_shift      = 7,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_aux0 = {
+	.dat_reg_addr    = ATC2603C_PMU_AUXADC0,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_aux1 = {
+	.dat_reg_addr    = ATC2603C_PMU_AUXADC1,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_aux2 = {
+	.dat_reg_addr    = ATC2603C_PMU_AUXADC2,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 1,   /* raw value (0~1023) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_icm = {
+	.dat_reg_addr    = ATC2603C_PMU_ICMADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 11,
+	.getraw_func     = _atc2603c_auxadc_getraw_icm_chn,
+	.tran_func       = _atc2603c_auxadc_usertran_icm,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2603c_chn_hwinfos_svccv = {
+	.dat_reg_addr    = ATC2603C_PMU_SVCCADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 10,
+	.tr_multiplier   = 375, /* raw * 2.9296875mv * 2 */
+	.tr_r_shift      = 6,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_hwinfo sc_atc2603c_ver_a_auxadc_hwinfo = {
+	.channels = 17,
+	.chn_names = {
+		"BATV", "BATI", "VBUSV", "VBUSI", "SYSPWRV", "WALLV", "WALLI", "CHGI",
+		"IREF", "REMCON", "ICTEMP", "BAKBATV", "AUX0", "AUX1", "AUX2", "ICM",
+		"SVCC"
+	},
+	.chn_unit_names = {
+		"mV", "mA", "mV", "mA", "mV", "mV", "mA", "mA",
+		"mV", "/1024", "mCel", "/1024", "/1024", "/1024", "mV", "mA",
+		"mV"
+	},
+	.chn_hwinfos = {
+		&sc_atc2603c_chn_hwinfos_batv,
+		&sc_atc2603c_ver_a_chn_hwinfos_bati,
+		&sc_atc2603c_chn_hwinfos_vbusv,
+		&sc_atc2603c_ver_a_chn_hwinfos_vbusi,
+		&sc_atc2603c_chn_hwinfos_syspwrv,
+		&sc_atc2603c_chn_hwinfos_wallv,
+		&sc_atc2603c_ver_a_chn_hwinfos_walli,
+		&sc_atc2603c_ver_a_chn_hwinfos_chgi,
+		&sc_atc2603c_chn_hwinfos_iref,
+		&sc_atc2603c_chn_hwinfos_remcon,
+		&sc_atc2603c_chn_hwinfos_ictemp,
+		&sc_atc2603c_chn_hwinfos_bakbatv,
+		&sc_atc2603c_chn_hwinfos_aux0,
+		&sc_atc2603c_chn_hwinfos_aux1,
+		&sc_atc2603c_chn_hwinfos_aux2,
+		&sc_atc2603c_chn_hwinfos_icm,
+		&sc_atc2603c_chn_hwinfos_svccv
+	}
+};
+
+static const struct atc260x_auxadc_hwinfo sc_atc2603c_ver_b_auxadc_hwinfo = {
+	.channels = 17,
+	.chn_names = {
+		"BATV", "BATI", "VBUSV", "VBUSI", "SYSPWRV", "WALLV", "WALLI", "CHGI",
+		"IREF", "REMCON", "ICTEMP", "BAKBATV", "AUX0", "AUX1", "AUX2", "ICM",
+		"SVCC"
+	},
+	.chn_unit_names = {
+		"mV", "mA", "mV", "mA", "mV", "mV", "mA", "mA",
+		"mV", "/1024", "mCel", "/1024", "/1024", "/1024", "mV", "mA",
+		"mV"
+	},
+	.chn_hwinfos = {
+		&sc_atc2603c_chn_hwinfos_batv,
+		&sc_atc2603c_ver_b_chn_hwinfos_bati,
+		&sc_atc2603c_chn_hwinfos_vbusv,
+		&sc_atc2603c_ver_b_chn_hwinfos_vbusi,
+		&sc_atc2603c_chn_hwinfos_syspwrv,
+		&sc_atc2603c_chn_hwinfos_wallv,
+		&sc_atc2603c_ver_b_chn_hwinfos_walli,
+		&sc_atc2603c_ver_b_chn_hwinfos_chgi,
+		&sc_atc2603c_chn_hwinfos_iref,
+		&sc_atc2603c_chn_hwinfos_remcon,
+		&sc_atc2603c_chn_hwinfos_ictemp,
+		&sc_atc2603c_chn_hwinfos_bakbatv,
+		&sc_atc2603c_chn_hwinfos_aux0,
+		&sc_atc2603c_chn_hwinfos_aux1,
+		&sc_atc2603c_chn_hwinfos_aux2,
+		&sc_atc2603c_chn_hwinfos_icm,
+		&sc_atc2603c_chn_hwinfos_svccv
+	}
+};
+
+
+/* for atc2609a ------------------------------------------------------------- */
+/* no postfix means rev.D */
+
+struct atc260x_auxadc_udata_9a {
+	int    adc_ictemp_adj;
+};
+
+static int _atc2609a_auxadc_usertran_ictemp_adj(
+		uint raw_val, const struct atc260x_auxadc_chn_hwinfo *ch_info, struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_9a *pud;
+	pud = (struct atc260x_auxadc_udata_9a *)(atc260x->auxadc_udata);
+	return  51 * ((int)raw_val + pud->adc_ictemp_adj - 1333);
+}
+
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_iref = {
+	.dat_reg_addr    = ATC2609A_PMU_IREFADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 375, /* raw * 0.732421875mv */
+	.tr_r_shift      = 9,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_chgi = {
+	.dat_reg_addr    = ATC2609A_PMU_CHGIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 4000, /* raw * 4000 / 4096 */
+	.tr_r_shift      = 12,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_vbusi = {
+	.dat_reg_addr    = ATC2609A_PMU_VBUSIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 4000, /* raw * 4000 / 4096 */
+	.tr_r_shift      = 12,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_walli = {
+	.dat_reg_addr    = ATC2609A_PMU_WALLIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 4000, /* raw * 4000 / 4096 */
+	.tr_r_shift      = 12,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_bati = {
+	.dat_reg_addr    = ATC2609A_PMU_BATIADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 4000, /* raw * 4000 / 4096 */
+	.tr_r_shift      = 12,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_remcon = {
+	.dat_reg_addr    = ATC2609A_PMU_REMCONADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1,   /* raw value (0~4095) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_ictemp = {
+	.dat_reg_addr    = ATC2609A_PMU_ICTEMPADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tran_func       = _atc2609a_auxadc_usertran_ictemp_adj,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_svccv = {
+	.dat_reg_addr    = ATC2609A_PMU_SVCCADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 375, /* raw * 0.732421875mv * 2 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_bakbatv = {
+	.dat_reg_addr    = ATC2609A_PMU_BAKBATADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 375, /* raw * 0.732421875mv * 2 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_syspwrv = {
+	.dat_reg_addr    = ATC2609A_PMU_SYSPWRADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1875, /* raw * 0.732421875mv * 2.5 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_wallv = {
+	.dat_reg_addr    = ATC2609A_PMU_WALLVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1875, /* raw * 0.732421875mv * 2.5 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_vbusv = {
+	.dat_reg_addr    = ATC2609A_PMU_VBUSVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1875, /* raw * 0.732421875mv * 2.5 */
+	.tr_r_shift      = 10,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_aux3 = {
+	.dat_reg_addr    = ATC2609A_PMU_AUXADC3,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1,   /* raw value (0~4095) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_aux2 = {
+	.dat_reg_addr    = ATC2609A_PMU_AUXADC2,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1,   /* raw value (0~4095) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_aux1 = {
+	.dat_reg_addr    = ATC2609A_PMU_AUXADC1,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1,   /* raw value (0~4095) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_aux0 = {
+	.dat_reg_addr    = ATC2609A_PMU_AUXADC0,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 1,   /* raw value (0~4095) without BUG */
+	.tr_r_shift      = 0,
+	.tr_offset       = 0,
+};
+static const struct atc260x_auxadc_chn_hwinfo sc_atc2609a_chn_hwinfos_batv = {
+	.dat_reg_addr    = ATC2609A_PMU_BATVADC,
+	.dat_bit_offs    = 0,
+	.dat_bit_cnt     = 12,
+	.tr_multiplier   = 375, /* raw * 0.732421875mv * 2 */
+	.tr_r_shift      = 8,
+	.tr_offset       = 0,
+};
+
+static const struct atc260x_auxadc_hwinfo sc_atc2609a_auxadc_hwinfo = {
+	.channels = 17,
+	.chn_names = {
+		"IREF", "CHGI", "VBUSI", "WALLI", "BATI", "REMCON", "ICTEMP", "SVCC",
+		"BAKBATV", "SYSPWRV", "WALLV", "VBUSV", "AUX3", "AUX2", "AUX1", "AUX0",
+		"BATV"
+	},
+	.chn_unit_names = {
+		"mV", "mA", "mA", "mA", "mA", "/4096", "mCel", "mV",
+		"mV", "mV", "mV", "mv", "/4096", "/4096", "/4096", "/4096",
+		"mV"
+	},
+	.chn_hwinfos = {
+		&sc_atc2609a_chn_hwinfos_iref,
+		&sc_atc2609a_chn_hwinfos_chgi,
+		&sc_atc2609a_chn_hwinfos_vbusi,
+		&sc_atc2609a_chn_hwinfos_walli,
+		&sc_atc2609a_chn_hwinfos_bati,
+		&sc_atc2609a_chn_hwinfos_remcon,
+		&sc_atc2609a_chn_hwinfos_ictemp,
+		&sc_atc2609a_chn_hwinfos_svccv,
+		&sc_atc2609a_chn_hwinfos_bakbatv,
+		&sc_atc2609a_chn_hwinfos_syspwrv,
+		&sc_atc2609a_chn_hwinfos_wallv,
+		&sc_atc2609a_chn_hwinfos_vbusv,
+		&sc_atc2609a_chn_hwinfos_aux3,
+		&sc_atc2609a_chn_hwinfos_aux2,
+		&sc_atc2609a_chn_hwinfos_aux1,
+		&sc_atc2609a_chn_hwinfos_aux0,
+		&sc_atc2609a_chn_hwinfos_batv,
+	},
+};
+
+
+
+
+int atc260x_auxadc_find_chan(struct atc260x_dev *atc260x, const char *channel_name)
+{
+	uint i;
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	if (!channel_name) {
+		return -EINVAL;
+	}
+	for (i = 0; i < atc260x->auxadc_hwinfo->channels; i++) {
+		if (0 == strcmp(channel_name, atc260x->auxadc_hwinfo->chn_names[i])) {
+			return i;
+		}
+	}
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(atc260x_auxadc_find_chan);
+
+const char *atc260x_auxadc_channel_name(struct atc260x_dev *atc260x, uint channel)
+{
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	if (channel >= atc260x->auxadc_hwinfo->channels)
+		return NULL;
+	return atc260x->auxadc_hwinfo->chn_names[channel];
+}
+EXPORT_SYMBOL_GPL(atc260x_auxadc_channel_name);
+
+const char *atc260x_auxadc_channel_unit_name(struct atc260x_dev *atc260x, uint channel)
+{
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	if (channel >= atc260x->auxadc_hwinfo->channels)
+		return NULL;
+	return atc260x->auxadc_hwinfo->chn_unit_names[channel];
+}
+EXPORT_SYMBOL_GPL(atc260x_auxadc_channel_unit_name);
+
+static int _atc260x_auxadc_get_raw_inner(struct atc260x_dev *atc260x, uint channel,
+		const struct atc260x_auxadc_chn_hwinfo *ch_info)
+{
+	uint raw_value;
+	int ret;
+
+	/* get raw value */
+	if (unlikely(ch_info->getraw_func)) {
+		/* only this branch needs lock. */
+		mutex_lock(&atc260x->auxadc_read_mutex);
+		ret = (ch_info->getraw_func)(channel, ch_info, atc260x);
+		mutex_unlock(&atc260x->auxadc_read_mutex);
+		if (ret < 0) {
+			dev_err(atc260x->dev,
+				"%s() getraw_func failed, auxadc #%u\n", __func__, channel);
+			return -EIO;
+		}
+		raw_value = ret;
+	} else {
+		ret = atc260x_reg_read(atc260x, ch_info->dat_reg_addr);
+		if (ret < 0) {
+			dev_err(atc260x->dev,
+				"%s() can not read auxadc reg 0x%x\n",
+				__func__, ch_info->dat_reg_addr);
+			return -EIO;
+		}
+		raw_value = ((uint)ret >> ch_info->dat_bit_offs) & ((1U << ch_info->dat_bit_cnt) -1U);
+	}
+	return raw_value;
+}
+
+int atc260x_auxadc_get_raw(struct atc260x_dev *atc260x, uint channel)
+{
+	const struct atc260x_auxadc_chn_hwinfo *ch_info;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	if (channel >= atc260x->auxadc_hwinfo->channels) {
+		dev_err(atc260x->dev,
+				"%s() no such channel: %u\n", __func__, channel);
+		return -ENXIO;
+	}
+	ch_info = atc260x->auxadc_hwinfo->chn_hwinfos[channel];
+	return _atc260x_auxadc_get_raw_inner(atc260x, channel, ch_info);
+}
+EXPORT_SYMBOL_GPL(atc260x_auxadc_get_raw);
+
+int atc260x_auxadc_get_translated(struct atc260x_dev *atc260x, uint channel, s32 *p_tr_value)
+{
+	const struct atc260x_auxadc_chn_hwinfo *ch_info;
+	int ret, tr_value;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	if (channel >= atc260x->auxadc_hwinfo->channels) {
+		dev_err(atc260x->dev,
+				"%s() no such channel: %u\n", __func__, channel);
+		return -EINVAL;
+	}
+	ch_info = atc260x->auxadc_hwinfo->chn_hwinfos[channel];
+
+	ret = _atc260x_auxadc_get_raw_inner(atc260x, channel, ch_info);
+	if (ret < 0) {
+		dev_err(atc260x->dev,
+			"%s() can not get raw value from auxadc #%u, ret=%d\n",
+			__func__, channel, ret);
+		return ret;
+	}
+	dev_dbg(atc260x->dev, "%s() channel #%u raw = %d\n", __func__, channel, ret);
+
+	if (ch_info->tran_func) {
+		tr_value = (ch_info->tran_func)(ret, ch_info, atc260x);
+		dev_dbg(atc260x->dev, "%s() channel #%u call 0x%lx for translate, result=%d\n",
+				__func__, channel, (ulong)(ch_info->tran_func), tr_value);
+	} else {
+		tr_value = (s32)(((s64)ret * ch_info->tr_multiplier) >> ch_info->tr_r_shift) +
+			ch_info->tr_offset;
+		dev_dbg(atc260x->dev, "%s() channel #%u linear translate, "
+				"multiplier=%d r_shift=%u offset=%d result=%d\n",
+				__func__, channel, ch_info->tr_multiplier, ch_info->tr_r_shift,
+				ch_info->tr_offset, tr_value);
+	}
+
+	if (p_tr_value) {
+		*p_tr_value = tr_value;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_auxadc_get_translated);
+
+
+/* init --------------------------------------------------------------------- */
+
+static int _atc2603a_auxadc_dev_init(struct atc260x_dev *atc260x)
+{
+	atc260x->auxadc_hwinfo = &sc_atc2603a_auxadc_hwinfo;
+	atc260x->auxadc_udata = NULL;
+
+	/* open all channels (+ ~1uA) */
+	atc260x_reg_write(atc260x, ATC2603A_PMU_AUXADC_CTL0, 0xffffU);
+
+	return 0;
+}
+static void _atc2603a_auxadc_dev_exit(struct atc260x_dev *atc260x)
+{
+}
+
+static int _atc2603c_auxadc_dev_init(struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_3c *usrdata;
+	int icm_ohm, ret = 0;
+	uint dat;
+	int ic_ver;
+
+	ic_ver = atc260x_get_ic_ver(atc260x);
+	if(ic_ver == 0) {
+		atc260x->auxadc_hwinfo = &sc_atc2603c_ver_a_auxadc_hwinfo;
+	}
+	else{
+		atc260x->auxadc_hwinfo = &sc_atc2603c_ver_b_auxadc_hwinfo;
+	}
+	usrdata = devm_kzalloc(atc260x->dev, sizeof(*usrdata), GFP_KERNEL);
+	if (!usrdata) {
+		return -ENOMEM;
+	}
+	atc260x->auxadc_udata = usrdata;
+
+	/*enable adc data output.*/
+	ret = atc260x_reg_read(atc260x, ATC2603C_PMU_AUXADC_CTL1);
+	if (ret < 0){
+		goto label_io_err;
+	}
+	dat = ret & 0xFFFF;
+	/*version B don't set bit3 0 */
+	if(ic_ver == 0){
+		if (dat & (1U << 3)) {
+			/* disable COMP OFFSET TRIMMING */
+			atc260x_reg_write(atc260x, ATC2603C_PMU_AUXADC_CTL1, dat & ~(1<<3));
+		}
+	}
+	/* open all channels (+ ~1uA) */
+	atc260x_reg_write(atc260x, ATC2603C_PMU_AUXADC_CTL0, 0xffffU);
+
+	icm_ohm = 20;
+	ret = of_property_read_u32(atc260x->dev->of_node, "icm_ohm", &icm_ohm);
+	if (ret){
+		dev_err(atc260x->dev, "icm_ohm not config, use default value\n");
+	}
+	dev_info(atc260x->dev, "auxadc icm_ohm = %u\n", icm_ohm);
+	usrdata->icm_res_10ohm = (icm_ohm == 10);
+
+	/*setup value between CMN and CMP according to the resistor on board.*/
+	ret = atc260x_reg_setbits(atc260x, ATC2603C_PMU_AUXADC_CTL1,
+		(1U << 4), (((icm_ohm == 10) ? 1 : 0) << 4));
+	if (ret) {
+		goto label_io_err;
+	}
+
+	return 0;
+
+	label_io_err:
+	dev_err(atc260x->dev, "%s() fialed to read/write register\n", __func__);
+	return -EIO;
+}
+static void _atc2603c_auxadc_dev_exit(struct atc260x_dev *atc260x)
+{
+	/* no need to do anything. */
+}
+
+static int _atc2609a_auxadc_dev_init(struct atc260x_dev *atc260x)
+{
+	struct atc260x_auxadc_udata_9a *usrdata;
+	uint ictemp_adj;
+	int ret;
+
+	atc260x->auxadc_hwinfo = &sc_atc2609a_auxadc_hwinfo;
+
+	usrdata = devm_kzalloc(atc260x->dev, sizeof(*usrdata), GFP_KERNEL);
+	if (!usrdata) {
+		return -ENOMEM;
+	}
+	atc260x->auxadc_udata = usrdata;
+
+	/* open all channels (+ ~1uA) */
+	atc260x_reg_write(atc260x, ATC2609A_PMU_AUXADC_CTL0, 0xffffU);
+
+	/* IC_TEMP ADJ */
+	/*TODO */
+	ictemp_adj = 0;
+	ret = atc260x_reg_write(atc260x, ATC2609A_PMU_ICTEMPADC_ADJ, ictemp_adj);
+	if (ret) {
+		dev_err(atc260x->dev, "%s() failed to set ic_temp adjuest value\n", __func__);
+	}
+	usrdata->adc_ictemp_adj = ictemp_adj;
+
+	return 0;
+}
+static void _atc2609a_auxadc_dev_exit(struct atc260x_dev *atc260x)
+{
+}
+
+typedef int (*atc260x_auxadc_init_func_t)(struct atc260x_dev *atc260x);
+typedef void (*atc260x_auxadc_exit_func_t)(struct atc260x_dev *atc260x);
+static const atc260x_auxadc_init_func_t sc_init_func_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = _atc2603a_auxadc_dev_init,
+	[ATC260X_ICTYPE_2603C] = _atc2603c_auxadc_dev_init,
+	[ATC260X_ICTYPE_2609A] = _atc2609a_auxadc_dev_init,
+};
+static const atc260x_auxadc_exit_func_t sc_exit_func_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = _atc2603a_auxadc_dev_exit,
+	[ATC260X_ICTYPE_2603C] = _atc2603c_auxadc_dev_exit,
+	[ATC260X_ICTYPE_2609A] = _atc2609a_auxadc_dev_exit,
+};
+
+int atc260x_auxadc_dev_init(struct atc260x_dev *atc260x)
+{
+	int ret;
+
+	mutex_init(&atc260x->auxadc_read_mutex);
+
+	BUG_ON(atc260x->ic_type >= ATC260X_ICTYPE_CNT);
+	ret = (sc_init_func_tbl[atc260x->ic_type])(atc260x);
+	if (ret) {
+		dev_err(atc260x->dev, "%s() failed, ret=%d\n", __func__, ret);
+	}
+	return ret;
+}
+void atc260x_auxadc_dev_exit(struct atc260x_dev *atc260x)
+{
+	(sc_exit_func_tbl[atc260x->ic_type])(atc260x);
+	mutex_destroy(&atc260x->auxadc_read_mutex);
+}
diff --git a/drivers/mfd/atc260x-cmu.c b/drivers/mfd/atc260x-cmu.c
new file mode 100755
index 0000000..c8f2128
--- /dev/null
+++ b/drivers/mfd/atc260x-cmu.c
@@ -0,0 +1,130 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include "atc260x-core.h"
+
+
+
+struct atc260x_cmu_reg_cfg {
+	u16         clk_ctl_reg;
+	u16         rst_ctl_reg;
+	u8          rst_bit_tbl[ATC260X_CMU_MODULE_NUM]; /* index by ATC260X_CMU_MODULE_xxx */
+	u8          clk_bit_tbl[ATC260X_CMU_MODULE_NUM]; /* index by ATC260X_CMU_MODULE_xxx */
+};
+
+static const struct atc260x_cmu_reg_cfg sc_atc2603a_cmu_reg_cfg = {
+	.clk_ctl_reg = ATC2603A_CMU_DEVRST,
+	.rst_ctl_reg = ATC2603A_CMU_DEVRST,
+				 /*   TP,  MFP, INTS, ETHPHY, AUDIO,  PWSI  */
+	.rst_bit_tbl = {   0,    1,    2,      3,     4,  0xff },
+	.clk_bit_tbl = {   8, 0xff, 0xff,      9,    10,  0xff },
+};
+static const struct atc260x_cmu_reg_cfg sc_atc2603c_cmu_reg_cfg = {
+	.clk_ctl_reg = ATC2603C_CMU_DEVRST,
+	.rst_ctl_reg = ATC2603C_CMU_DEVRST,
+				 /*   TP,  MFP, INTS, ETHPHY, AUDIO,  PWSI  */
+	.rst_bit_tbl = {0xff,    1,    2,   0xff,     4,  0xff },
+	.clk_bit_tbl = {0xff, 0xff, 0xff,   0xff,    10,  0xff },
+};
+static const struct atc260x_cmu_reg_cfg sc_atc2609a_cmu_reg_cfg = {
+	.clk_ctl_reg = ATC2609A_CMU_DEVRST,
+	.rst_ctl_reg = ATC2609A_CMU_DEVRST,
+				 /*   TP,  MFP, INTS, ETHPHY, AUDIO,  PWSI  */
+	.rst_bit_tbl = {0xff,    1,    2,   0xff,     0,     3 },
+	.clk_bit_tbl = {0xff, 0xff, 0xff,   0xff,     8,  0xff },
+};
+static const struct atc260x_cmu_reg_cfg * const sc_atc260x_cmu_reg_cfg_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = &sc_atc2603a_cmu_reg_cfg,
+	[ATC260X_ICTYPE_2603C] = &sc_atc2603c_cmu_reg_cfg,
+	[ATC260X_ICTYPE_2609A] = &sc_atc2609a_cmu_reg_cfg,
+};
+
+int atc260x_cmu_reset(struct atc260x_dev *atc260x, uint cmu_module)
+{
+	struct atc260x_cmu_reg_cfg const *regcfg;
+	uint reg_addr, reg_val, rst_bit;
+	int ret;
+
+	if (cmu_module >= ATC260X_CMU_MODULE_NUM) {
+		dev_err(atc260x->dev, "Invalid ATC260X cmu module %u\n", cmu_module);
+		return -EINVAL;
+	}
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	BUG_ON(atc260x->ic_type >= ATC260X_ICTYPE_CNT);
+	regcfg = sc_atc260x_cmu_reg_cfg_tbl[atc260x->ic_type];
+	rst_bit = regcfg->rst_bit_tbl[cmu_module];
+	if (rst_bit >= 32) {
+		dev_err(atc260x->dev, "No reset func for cmu module %u\n", cmu_module);
+		return -ENXIO; /* no such function */
+	}
+	reg_addr = regcfg->rst_ctl_reg;
+
+	ret = atc260x_reg_read(atc260x, reg_addr);
+	if (ret < 0) {
+		goto label_err;
+	}
+	reg_val = ret;
+	ret = atc260x_reg_write(atc260x, reg_addr, reg_val & ~(1U << rst_bit));
+	if (ret < 0) {
+		goto label_err;
+	}
+	udelay(50);
+	ret = atc260x_reg_write(atc260x, reg_addr, reg_val);
+	if (ret < 0) {
+		goto label_err;
+	}
+	udelay(50);
+
+	return 0;
+
+	label_err:
+	dev_err(atc260x->dev, "%s() faile to read/write cmu reset reg 0x%x\n",
+		__func__, reg_addr);
+	return -EIO;
+}
+EXPORT_SYMBOL_GPL(atc260x_cmu_reset);
+
+int atc260x_cmu_clk_ctrl(struct atc260x_dev *atc260x, uint cmu_module, uint clk_en)
+{
+	struct atc260x_cmu_reg_cfg const *regcfg;
+	uint reg_addr, clk_bit;
+	int ret;
+
+	if (cmu_module >= ATC260X_CMU_MODULE_NUM) {
+		dev_err(atc260x->dev, "Invalid ATC260X cmu module %u\n", cmu_module);
+		return -EINVAL;
+	}
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	clk_en = !!clk_en;
+
+	regcfg = sc_atc260x_cmu_reg_cfg_tbl[atc260x->ic_type];
+	clk_bit = regcfg->clk_bit_tbl[cmu_module];
+	if (clk_bit >= 32) {
+		dev_err(atc260x->dev, "No clk_ctl func for cmu module %u\n", cmu_module);
+		return -ENXIO; /* no such function */
+	}
+	reg_addr = regcfg->clk_ctl_reg;
+
+	ret = atc260x_reg_setbits(atc260x, reg_addr, (1U << clk_bit), (clk_en << clk_bit));
+	if (ret < 0) {
+		dev_err(atc260x->dev, "%s() faile to read/write cmu clock reg 0x%x\n",
+			__func__, reg_addr);
+		return -EIO;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_cmu_clk_ctrl);
+
+
diff --git a/drivers/mfd/atc260x-core.c b/drivers/mfd/atc260x-core.c
new file mode 100755
index 0000000..a5cf654
--- /dev/null
+++ b/drivers/mfd/atc260x-core.c
@@ -0,0 +1,1070 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/suspend.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include "atc260x-core.h"
+
+static const u16 sc_suspend_save_regs_atx2603a[] = {
+	ATC2603A_PMU_SYS_CTL1,    /* u-boot may change. */
+	ATC2603A_CMU_DEVRST,
+	/*ATC2603A_INTS_PD, */
+	ATC2603A_INTS_MSK,        /* regmap_irq require this */
+	ATC2603A_MFP_CTL0,
+	ATC2603A_MFP_CTL1,
+	ATC2603A_PAD_DRV0,
+	ATC2603A_PAD_DRV1,
+	ATC2603A_PAD_EN,
+	ATC2603A_PMU_AUXADC_CTL1, /* CTL1 goes first. */
+	ATC2603A_PMU_AUXADC_CTL0,
+	/*ATC2603A_TP_CTL0, */
+	/*ATC2603A_TP_CTL1, */
+	0xffff,  /* end */
+};
+static const u16 sc_suspend_save_regs_atx2603c[] = {
+	ATC2603C_PMU_SYS_CTL1,    /* u-boot may change. */
+	ATC2603C_PMU_MUX_CTL0,    /* MFP for SGPIOs */
+	ATC2603C_CMU_DEVRST,
+	ATC2603C_INTS_MSK,        /* regmap_irq require this */
+	ATC2603C_MFP_CTL,
+	ATC2603C_PAD_VSEL,
+	ATC2603C_PAD_DRV,
+	ATC2603C_PAD_EN,
+	ATC2603C_PMU_AUXADC_CTL1, /* CTL1 goes first. */
+	ATC2603C_PMU_AUXADC_CTL0,
+	0xffff,  /* end */
+};
+static const u16 sc_suspend_save_regs_atx2609a[] = {
+	ATC2609A_PMU_SYS_CTL1,    /* u-boot may change. */
+	ATC2609A_CMU_DEVRST,
+	ATC2609A_INTS_MSK,        /* regmap_irq require this */
+	ATC2609A_MFP_CTL,
+	ATC2609A_PAD_VSEL,
+	ATC2609A_PAD_DRV,
+	ATC2609A_PAD_EN,
+	ATC2609A_PMU_AUXADC_CTL1, /* CTL1 goes first. */
+	ATC2609A_PMU_AUXADC_CTL0,
+	0xffff,  /* end */
+};
+static const u16 * const sc_suspend_save_regs_tbl[ATC260X_ICTYPE_CNT] = {
+	sc_suspend_save_regs_atx2603a,
+	sc_suspend_save_regs_atx2603c,
+	sc_suspend_save_regs_atx2609a,
+};
+
+static int _atc260x_save_critical_regs(struct atc260x_dev *atc260x)
+{
+	uint i, reg;
+	int ret;
+	u16 const *p;
+	p = sc_suspend_save_regs_tbl[atc260x->ic_type];
+	for (i = 0; i < ATC260X_MAX_SAVE_REGS; i++,p++) {
+		reg = *p;
+		if (reg == 0xffffU)
+			break;
+
+		ret = atc260x_reg_read(atc260x, reg);
+		if (ret < 0)
+			return ret;
+		atc260x->reg_save_buf[i] = ret;
+
+		dev_dbg(atc260x->dev, "%s() save reg 0x%x value 0x%x\n",
+			__func__, reg, ret);
+	}
+	return 0;
+}
+
+static int _atc260x_restore_critical_regs(struct atc260x_dev *atc260x)
+{
+	uint i, reg, value;
+	int ret;
+	u16 const *p;
+	p = sc_suspend_save_regs_tbl[atc260x->ic_type];
+	for (i = 0; i < ATC260X_MAX_SAVE_REGS; i++,p++) {
+		reg = *p;
+		if (reg == 0xffffU)
+			break;
+
+		value = atc260x->reg_save_buf[i];
+		ret = atc260x_reg_write(atc260x, reg, value);
+		if (ret) {
+			return ret;
+		}
+		dev_dbg(atc260x->dev, "%s() restore reg 0x%x value 0x%x\n",
+			__func__, reg, value);
+#if defined(DEBUG)
+		ret = atc260x_reg_read(atc260x, reg); /* read back check */
+		if (ret < 0) {
+			return ret;
+		}
+		if (ret != value) {
+			/* 读回值不对有可能是正常的, 因为会有pending位和只读状态位. print下即可. */
+			dev_warn(atc260x->dev, "%s() readback cmp diff, "
+				"reg=0x%x org=0x%x readback=0x%x\n",
+				__func__, reg, value, (uint)ret);
+		}
+#endif
+	}
+	return 0;
+}
+
+/* Get atc260x parent device structure.  For sub-device used. */
+struct atc260x_dev *atc260x_get_parent_dev(struct device *sub_dev)
+{
+	struct atc260x_dev *atc260x;
+	atc260x = (struct atc260x_dev*)(dev_get_drvdata(sub_dev->parent));
+	if (ATC260X_CHK_VALID_DEV(atc260x)) {
+		return atc260x;
+	}
+	dev_err(sub_dev, "%s() not sub device of atc260x\n", __func__);
+	dump_stack();
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(atc260x_get_parent_dev);
+
+void atc260x_get_bus_info(struct atc260x_dev *atc260x, uint *bus_num, uint *bus_addr)
+{
+	*bus_num = atc260x->bus_num;
+	*bus_addr = atc260x->bus_addr;
+}
+EXPORT_SYMBOL_GPL(atc260x_get_bus_info);
+
+uint atc260x_get_ic_type(struct atc260x_dev *atc260x)
+{
+	return atc260x->ic_type;
+}
+EXPORT_SYMBOL_GPL(atc260x_get_ic_type);
+
+uint atc260x_get_ic_ver(struct atc260x_dev *atc260x)
+{
+	return atc260x->ic_ver;
+}
+EXPORT_SYMBOL_GPL(atc260x_get_ic_ver);
+
+/*
+ * Safe read, modify, write methods
+ */
+int atc260x_reg_setbits(struct atc260x_dev *atc260x, uint reg,
+			u16 mask, u16 val)
+{
+	unsigned long irq_state_save;
+	int ret;
+	uint reg_tmp_val, reg_old_val;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_dbg(atc260x->dev, "%s() reg=0x%x mask=0x%x val=0x%x\n",
+		__func__, reg, mask, val);
+
+	switch (atc260x->reg_access_mode) {
+	case ATC260X_ACCESS_MODE_NORMAL:
+		ret = regmap_update_bits(atc260x->regmap, reg, mask, val);
+		break;
+	case ATC260X_ACCESS_MODE_DIRECT:
+		spin_lock_irqsave(&atc260x->dacc_spinlock, irq_state_save);
+		ret = atc260x->direct_read_reg(atc260x, reg);
+		if (ret < 0) {
+			spin_unlock_irqrestore(&atc260x->dacc_spinlock, irq_state_save);
+			break;
+		}
+		reg_tmp_val = reg_old_val = ret;
+		ret = 0;
+		reg_tmp_val &= ~((uint)mask);
+		reg_tmp_val |= (uint)val & mask;
+		if (reg_tmp_val != reg_old_val) {
+			ret = atc260x->direct_write_reg(atc260x, reg, reg_tmp_val);
+		}
+		spin_unlock_irqrestore(&atc260x->dacc_spinlock, irq_state_save);
+		break;
+	default:
+		dev_err(atc260x->dev, "%s() unknown reg access mode %u. "
+			"it is likely that somebody still calling this API "
+			"while we are in suspended state.\n",
+			__func__, atc260x->reg_access_mode);
+		/* Keep this dump and we can see whom call us. */
+		dump_stack();
+		ret = -EIO;
+	}
+
+	if (ret) {
+		dev_err(atc260x->dev, "reg 0x%x set bits failed\n", reg);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atc260x_reg_setbits);
+
+int atc260x_reg_read(struct atc260x_dev *atc260x, uint reg)
+{
+	unsigned long irq_state_save;
+	int ret;
+	uint data;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_dbg(atc260x->dev, "%s() reg=0x%x\n", __func__, reg);
+
+	data = -1;
+	switch (atc260x->reg_access_mode) {
+	case ATC260X_ACCESS_MODE_NORMAL:
+		ret = regmap_read(atc260x->regmap, reg, &data);
+		break;
+	case ATC260X_ACCESS_MODE_DIRECT:
+		spin_lock_irqsave(&atc260x->dacc_spinlock, irq_state_save);
+		ret = atc260x->direct_read_reg(atc260x, reg);
+		if (ret >= 0) {
+			data = ret;
+		}
+		spin_unlock_irqrestore(&atc260x->dacc_spinlock, irq_state_save);
+		ret = 0;
+		break;
+	default:
+		dev_err(atc260x->dev, "%s() unknown reg access mode %u. "
+			"it is likely that somebody still calling this API "
+			"while we are in suspended state.\n",
+			__func__, atc260x->reg_access_mode);
+		/* Keep this dump and we can see whom call us. */
+		dump_stack();
+		ret = -EIO;
+	}
+
+	if (ret) {
+		dev_err(atc260x->dev, "read from reg 0x%x failed, ret=%d\n", reg, ret);
+	}
+	return data;
+}
+EXPORT_SYMBOL_GPL(atc260x_reg_read);
+
+int atc260x_reg_write(struct atc260x_dev *atc260x, uint reg, u16 val)
+{
+	unsigned long irq_state_save;
+	int ret;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_dbg(atc260x->dev, "%s() reg=0x%x val=0x%x\n", __func__, reg, val);
+
+	switch (atc260x->reg_access_mode) {
+	case ATC260X_ACCESS_MODE_NORMAL:
+		ret = regmap_write(atc260x->regmap, reg, val);
+		break;
+	case ATC260X_ACCESS_MODE_DIRECT:
+		spin_lock_irqsave(&atc260x->dacc_spinlock, irq_state_save);
+		ret = atc260x->direct_write_reg(atc260x, reg, val);
+		spin_unlock_irqrestore(&atc260x->dacc_spinlock, irq_state_save);
+		break;
+	default:
+		dev_err(atc260x->dev, "%s() unknown reg access mode %u. "
+			"it is likely that somebody still calling this API "
+			"while we are in suspended state.\n",
+			__func__, atc260x->reg_access_mode);
+		/* Keep this dump and we can see whom call us. */
+		dump_stack();
+		ret = -EIO;
+	}
+
+	if (ret) {
+		dev_err(atc260x->dev, "write to reg 0x%x failed\n", reg);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atc260x_reg_write);
+
+void atc260x_exit_reg_direct_access(struct atc260x_dev *atc260x)
+{
+	unsigned long irq_state_save;
+	uint mode;
+
+	spin_lock_irqsave(&atc260x->dacc_spinlock, irq_state_save);
+	(atc260x->direct_acc_exit)(atc260x); /* call the exit callback. */
+	mode = atc260x->reg_access_mode = ATC260X_ACCESS_MODE_NORMAL;
+	spin_unlock_irqrestore(&atc260x->dacc_spinlock, irq_state_save);
+
+	dev_info(atc260x->dev, "reg access mode set to %u\n", mode);
+}
+
+void atc260x_set_reg_direct_access(struct atc260x_dev *atc260x, bool enable)
+{
+	unsigned long irq_state_save;
+	uint mode;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	spin_lock_irqsave(&atc260x->dacc_spinlock, irq_state_save);
+	mode = atc260x->reg_access_mode;
+	if (enable) {
+		if (mode != ATC260X_ACCESS_MODE_DIRECT) {
+			(atc260x->direct_acc_init)(atc260x); /* call the init callback. */
+			mode = ATC260X_ACCESS_MODE_DIRECT;
+		}
+	} else {
+		if (mode == ATC260X_ACCESS_MODE_DIRECT) {
+			(atc260x->direct_acc_exit)(atc260x); /* call the exit callback. */
+			mode = ATC260X_ACCESS_MODE_NONE;
+		}
+	}
+	atc260x->reg_access_mode = mode;
+	spin_unlock_irqrestore(&atc260x->dacc_spinlock, irq_state_save);
+
+	dev_info(atc260x->dev, "reg access mode set to %u\n", mode);
+}
+EXPORT_SYMBOL_GPL(atc260x_set_reg_direct_access);
+
+
+static int atc260x_pm_notifier_func(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+	struct atc260x_dev *atc260x;
+
+	atc260x = container_of(nb, struct atc260x_dev, pm_notif_blk);
+
+	/* TODO */
+	/*switch(event) { */
+	/*case PM_HIBERNATION_PREPARE: ** Going to hibernate ** */
+	/*  break; */
+	/*case PM_POST_HIBERNATION:    ** Hibernation finished ** */
+	/*  break; */
+	/*case PM_SUSPEND_PREPARE:     ** Going to suspend the system ** */
+	/*  break; */
+	/*case PM_POST_SUSPEND:        ** Suspend finished ** */
+	/*  break; */
+	/*case PM_RESTORE_PREPARE:     ** Going to restore a saved image ** */
+	/*  break; */
+	/*case PM_POST_RESTORE:        ** Restore failed ** */
+	/*  break; */
+	/*} */
+	return NOTIFY_OK;
+}
+
+#if 0  /* for io debug */
+static void _atc260x_init_test_reg_access(struct atc260x_dev *atc260x)
+{
+	static const u16 sc_test_reg_tbl[ATC260X_ICTYPE_CNT][2] = {
+		[ATC260X_ICTYPE_2603A] = {ATC2603A_PMU_SYS_CTL8, ATC2603A_PMU_SYS_CTL9},
+		[ATC260X_ICTYPE_2603C] = {ATC2603C_PMU_FW_USE3, ATC2603C_PMU_FW_USE4},
+		[ATC260X_ICTYPE_2609A] = {ATC2609A_PMU_FW_USE0, ATC2609A_PMU_FW_USE1},
+	};
+	uint i, seed, tmp1, tmp2;
+	u16 reg1_addr, reg2_addr;
+	int ret;
+
+	dev_err(atc260x->dev, "start reg io test ............................\n");
+	dump_stack();
+
+	reg1_addr = sc_test_reg_tbl[atc260x->ic_type][0];
+	reg2_addr = sc_test_reg_tbl[atc260x->ic_type][1];
+
+	seed = 0x123456;
+	for (i = 0; i < 10000; i++) {
+		seed = seed * 1103515245U + 12345U; /* randomize */
+		tmp1 = ((seed & 0xffffU) ^ (seed >> 16)) & 0xffffU;
+		tmp2 = ((seed & 0xffffU) ^ ~(seed >> 16)) & 0xffffU;
+		ret = atc260x_reg_write(atc260x, reg1_addr, tmp1);
+		if (ret) {
+			dev_err(atc260x->dev, "test write err, i=%u reg=0x%x value=0x%x\n",
+				i, ATC2603C_PMU_FW_USE4, tmp1);
+			break;
+		}
+		ret = atc260x_reg_write(atc260x, reg2_addr, tmp2);
+		if (ret) {
+			dev_err(atc260x->dev, "test write err, i=%u reg=0x%x value=0x%x\n",
+				i, ATC2603C_PMU_FW_USE4, tmp2);
+			break;
+		}
+		if (i == 5000) {
+			dev_err(atc260x->dev, "test switch to direct_access mode\n");
+			atc260x_set_reg_direct_access(atc260x, true);
+		}
+		ret = atc260x_reg_read(atc260x, reg1_addr);
+		if (ret < 0 || (uint)ret != tmp1) {
+			dev_err(atc260x->dev, "test write err, i=%u reg=0x%x value=0x%x org_value=0x%x\n",
+				i, ATC2603C_PMU_FW_USE3, ret, tmp1);
+			break;
+		}
+		ret = atc260x_reg_read(atc260x, reg2_addr);
+		if (ret < 0 || (uint)ret != tmp2) {
+			dev_err(atc260x->dev, "test write err, i=%u reg=0x%x value=0x%x org_value=0x%x\n",
+				i, ATC2603C_PMU_FW_USE4, ret, tmp2);
+			break;
+		}
+		if ((i % 512) == 0) {
+			dev_err(atc260x->dev, "test cycle = %u\n", i);
+		}
+		ret = 0;
+	}
+	dev_err(atc260x->dev, "end reg io test. ret=%d\n", ret);
+	BUG();
+}
+#endif
+
+static int _atc2603a_init_hardware(struct atc260x_dev *atc260x)
+{
+	uint tmp;
+	int ret;
+
+	ret = atc260x_reg_read(atc260x, ATC2603A_CMU_HOSCCTL);
+	if (ret < 0) {
+		dev_err(atc260x->dev, "Failed to read magicnum, ret=%d\n", ret);
+		goto err;
+	}
+	if ((ret & ~(1U<<15)) != 0x2aab) {
+		dev_err(atc260x->dev, "Device is not a atc2603a: Register 0x%x value 0x%x!=0x%x\n",
+			ATC2603A_PMU_OC_INT_EN, ret, 0x2aab);
+		ret = -EINVAL;
+		goto err;
+	}
+	/*_atc260x_init_test_reg_access(atc260x); */
+
+	ret = atc260x_reg_read(atc260x, ATC2603A_PMU_BDG_CTL);
+	tmp = ret;
+	tmp |= (0x1 << 7);
+	tmp |= (0x1 << 6);
+	tmp &= ~(0x1 << 5);
+	tmp &= ~(0x1 << 11);
+	atc260x_reg_write(atc260x, ATC2603A_PMU_BDG_CTL, tmp);
+	dev_info(atc260x->dev, "set bdg ctl, ret=%d value=0x%x\n", ret, tmp);
+	/*atc260x_reg_setbits(atc260x, ATC2603A_PMU_BDG_CTL, (0x1 << 7),(0x1 << 7));
+	atc260x_reg_setbits(atc260x, ATC2603A_PMU_BDG_CTL, (0x1 << 11),(~(0x1 << 11)));
+	atc260x_reg_setbits(atc260x, ATC2603A_PMU_BDG_CTL, (0x1 << 6),(0x1 << 6));
+	atc260x_reg_setbits(atc260x, ATC2603A_PMU_BDG_CTL, (0x1 << 5),(~(0x1 << 5)));*/
+
+	/* disable TP pad (X1, Y1, X2, Y2) for external TP chip */
+	ret = atc260x_reg_setbits(atc260x, ATC2603A_MFP_CTL1, 0x00e0, 0x0);
+
+	/* init interrupt */
+	atc260x_cmu_reset(atc260x, ATC260X_CMU_MODULE_INTS); /* reset ATC260X INTC */
+	atc260x_reg_write(atc260x, ATC2603A_INTS_MSK, 0);    /* disable all sources */
+	atc260x_reg_setbits(atc260x, ATC2603A_PAD_EN, 0x1, 0x1); /* Enable P_EXTIRQ pad */
+
+	err:
+	return ret;
+}
+static int _atc2603c_init_hardware(struct atc260x_dev *atc260x)
+{
+	uint tmp;
+	int ret;
+
+	ret = atc260x_reg_read(atc260x, ATC2603C_PMU_OC_INT_EN);
+	if (ret < 0) {
+		dev_err(atc260x->dev, "Failed to read magicnum, ret=%d\n", ret);
+		goto err;
+	}
+	if (ret != 0x1bc0) {
+		dev_err(atc260x->dev, "Device is not an atc2603c: Register 0x%x value 0x%x!=0x%x\n",
+			ATC2603C_PMU_OC_INT_EN, ret, 0x1bc0);
+		ret = -EINVAL;
+		goto err;
+	}
+	/*_atc260x_init_test_reg_access(atc260x); */
+
+	ret = atc260x_reg_read(atc260x, ATC2603C_PMU_BDG_CTL);
+	tmp = ret;
+	tmp |= (0x1 << 7);      /*dbg enable */
+	tmp |= (0x1 << 6);      /*dbg filter. */
+	tmp &= ~(0x1 << 5);     /*disabel pulldown resistor. */
+	tmp &= ~(0x1 << 11);    /*efuse. */
+	ret = atc260x_reg_write(atc260x, ATC2603C_PMU_BDG_CTL, tmp);
+	dev_info(atc260x->dev, "set bdg ctl, ret=%d value=0x%x\n", ret, tmp);
+	/*atc260x_reg_setbits(atc260x, ATC2603C_PMU_BDG_CTL, (0x1 << 7),(0x1 << 7));
+	atc260x_reg_setbits(atc260x, ATC2603C_PMU_BDG_CTL, (0x1 << 11),(~(0x1 << 11)));
+	atc260x_reg_setbits(atc260x, ATC2603C_PMU_BDG_CTL, (0x1 << 6),(0x1 << 6));
+	atc260x_reg_setbits(atc260x, ATC2603C_PMU_BDG_CTL, (0x1 << 5),(~(0x1 << 5)));*/
+
+	/* init interrupt */
+	atc260x_cmu_reset(atc260x, ATC260X_CMU_MODULE_INTS); /* reset ATC260X INTC */
+	atc260x_reg_write(atc260x, ATC2603C_INTS_MSK, 0);    /* disable all sources */
+	atc260x_reg_setbits(atc260x, ATC2603C_PAD_EN, 0x1, 0x1); /* Enable P_EXTIRQ pad */
+
+	/* Output 32K clock */
+	{
+		u32 of_cfg_val;
+		int ret, vsel_31;
+
+		ret = of_property_read_u32(atc260x->dev->of_node, "losc_32k_output_enable", &of_cfg_val);
+		if (ret == 0 && of_cfg_val != 0) {
+			vsel_31 = 0;
+			ret = of_property_read_u32(atc260x->dev->of_node, "losc_32k_output_voltage", &of_cfg_val);
+			if (ret == 0)
+				vsel_31 = (of_cfg_val == 31) ? 1 : 0;
+
+			atc260x_reg_setbits(atc260x, ATC2603C_PAD_VSEL, (1U << 2), (vsel_31 << 2));
+			atc260x_reg_setbits(atc260x, ATC2603C_MFP_CTL, (3U << 7), (2U << 7));
+			atc260x_reg_setbits(atc260x, ATC2603C_PAD_EN, (1U << 2), (1U << 2));
+		}
+	}
+
+	ret = 0;
+
+	err:
+	return ret;
+}
+static int _atc2609a_init_hardware(struct atc260x_dev *atc260x)
+{
+	int ret;
+
+	ret = atc260x_reg_read(atc260x, ATC2609A_PMU_OC_INT_EN);
+	if (ret < 0) {
+		dev_err(atc260x->dev, "Failed to read magicnum, ret=%d\n", ret);
+		goto err;
+	}
+	if (ret != 0x0ff8) {
+		dev_err(atc260x->dev, "Device is not an atc2609a: Register 0x%x value 0x%x!=0x%x\n",
+			ATC2603C_PMU_OC_INT_EN, ret, 0x0ff8);
+		ret = -EINVAL;
+		goto err;
+	}
+	/*_atc260x_init_test_reg_access(atc260x); */
+
+	/* init interrupt */
+	atc260x_cmu_reset(atc260x, ATC260X_CMU_MODULE_INTS); /* reset ATC260X INTC */
+	atc260x_reg_write(atc260x, ATC2609A_INTS_MSK, 0);    /* disable all sources */
+	atc260x_reg_setbits(atc260x, ATC2609A_PAD_EN, 0x1, 0x1); /* Enable P_EXTIRQ pad */
+
+	/* Output 32K clock */
+	{
+		u32 of_cfg_val;
+		int ret;
+		ret = of_property_read_u32(atc260x->dev->of_node, "losc_32k_output_enable", &of_cfg_val);
+		if (ret == 0 && of_cfg_val != 0) {
+			atc260x_reg_setbits(atc260x, ATC2609A_PMU_SYS_CTL4, (7U << 5), (4U << 5));
+		}
+	}
+
+	ret = 0;
+
+	err:
+	return ret;
+}
+static int atc260x_init_hardware(struct atc260x_dev *atc260x)
+{
+	int ret = -ENXIO;
+	switch (atc260x->ic_type) {
+	case ATC260X_ICTYPE_2603A:
+		ret = _atc2603a_init_hardware(atc260x);
+		break;
+	case ATC260X_ICTYPE_2603C:
+		ret = _atc2603c_init_hardware(atc260x);
+		break;
+	case ATC260X_ICTYPE_2609A:
+		ret = _atc2609a_init_hardware(atc260x);
+		break;
+	}
+	return ret;
+}
+
+
+
+/* ATC2603A IRQs */
+#define ATC2603A_IRQ_AUDIO              (0)
+#define ATC2603A_IRQ_TP                 (1)
+#define ATC2603A_IRQ_ETHERNET           (2)
+#define ATC2603A_IRQ_OV                 (3)
+#define ATC2603A_IRQ_OC                 (4)
+#define ATC2603A_IRQ_OT                 (5)
+#define ATC2603A_IRQ_UV                 (6)
+#define ATC2603A_IRQ_ALARM              (7)
+#define ATC2603A_IRQ_ONOFF              (8)
+#define ATC2603A_IRQ_WKUP               (9)
+#define ATC2603A_IRQ_IR                 (10)
+
+/* ATC2603C IRQs */
+#define ATC2603C_IRQ_AUDIO              (0)
+#define ATC2603C_IRQ_OV                 (1)
+#define ATC2603C_IRQ_OC                 (2)
+#define ATC2603C_IRQ_OT                 (3)
+#define ATC2603C_IRQ_UV                 (4)
+#define ATC2603C_IRQ_ALARM              (5)
+#define ATC2603C_IRQ_ONOFF              (6)
+#define ATC2603C_IRQ_SGPIO              (7)
+#define ATC2603C_IRQ_IR                 (8)
+#define ATC2603C_IRQ_REMCON             (9)
+#define ATC2603C_IRQ_POWER_IN           (10)
+
+/* ATC2609A IRQs */
+#define ATC2609A_IRQ_AUDIO              (0)
+#define ATC2609A_IRQ_OV                 (1)
+#define ATC2609A_IRQ_OC                 (2)
+#define ATC2609A_IRQ_OT                 (3)
+#define ATC2609A_IRQ_UV                 (4)
+#define ATC2609A_IRQ_ALARM              (5)
+#define ATC2609A_IRQ_ONOFF              (6)
+#define ATC2609A_IRQ_WKUP               (7)
+#define ATC2609A_IRQ_IR                 (8)
+#define ATC2609A_IRQ_REMCON             (9)
+#define ATC2609A_IRQ_POWER_IN           (10)
+
+static const struct regmap_irq atc2603a_irqs[] = {
+	[ATC2603A_IRQ_AUDIO]    = { .reg_offset = 0, .mask = BIT(0), },
+	[ATC2603A_IRQ_TP]       = { .reg_offset = 0, .mask = BIT(1), },
+	[ATC2603A_IRQ_ETHERNET] = { .reg_offset = 0, .mask = BIT(2), },
+	[ATC2603A_IRQ_OV]       = { .reg_offset = 0, .mask = BIT(3), },
+	[ATC2603A_IRQ_OC]       = { .reg_offset = 0, .mask = BIT(4), },
+	[ATC2603A_IRQ_OT]       = { .reg_offset = 0, .mask = BIT(5), },
+	[ATC2603A_IRQ_UV]       = { .reg_offset = 0, .mask = BIT(6), },
+	[ATC2603A_IRQ_ALARM]    = { .reg_offset = 0, .mask = BIT(7), },
+	[ATC2603A_IRQ_ONOFF]    = { .reg_offset = 0, .mask = BIT(8), },
+	[ATC2603A_IRQ_WKUP]     = { .reg_offset = 0, .mask = BIT(9), },
+	[ATC2603A_IRQ_IR]       = { .reg_offset = 0, .mask = BIT(10), },
+};
+static const struct regmap_irq_chip atc2603a_irq_chip = {
+	.name = "atc2603a",
+	.irqs = atc2603a_irqs,
+	.num_irqs = ARRAY_SIZE(atc2603a_irqs),
+
+	.num_regs = 1,
+	.status_base = ATC2603A_INTS_PD,
+	.mask_base = ATC2603A_INTS_MSK,
+	.mask_invert = true,
+};
+
+static const struct regmap_irq atc2603c_irqs[] = {
+	[ATC2603C_IRQ_AUDIO]    = { .reg_offset = 0, .mask = BIT(0), },
+	[ATC2603C_IRQ_OV]       = { .reg_offset = 0, .mask = BIT(1), },
+	[ATC2603C_IRQ_OC]       = { .reg_offset = 0, .mask = BIT(2), },
+	[ATC2603C_IRQ_OT]       = { .reg_offset = 0, .mask = BIT(3), },
+	[ATC2603C_IRQ_UV]       = { .reg_offset = 0, .mask = BIT(4), },
+	[ATC2603C_IRQ_ALARM]    = { .reg_offset = 0, .mask = BIT(5), },
+	[ATC2603C_IRQ_ONOFF]    = { .reg_offset = 0, .mask = BIT(6), },
+	[ATC2603C_IRQ_SGPIO]    = { .reg_offset = 0, .mask = BIT(7), },
+	[ATC2603C_IRQ_IR]       = { .reg_offset = 0, .mask = BIT(8), },
+	[ATC2603C_IRQ_REMCON]   = { .reg_offset = 0, .mask = BIT(9), },
+	[ATC2603C_IRQ_POWER_IN] = { .reg_offset = 0, .mask = BIT(10), },
+};
+static const struct regmap_irq_chip atc2603c_irq_chip = {
+	.name = "atc2603c",
+	.irqs = atc2603c_irqs,
+	.num_irqs = ARRAY_SIZE(atc2603c_irqs),
+
+	.num_regs = 1,
+	.status_base = ATC2603C_INTS_PD,
+    .ack_base = ATC2603C_INTS_PD,
+	.mask_base = ATC2603C_INTS_MSK,
+	.mask_invert = true,
+};
+
+static const struct regmap_irq atc2609a_irqs[] = {
+	[ATC2609A_IRQ_AUDIO]    = { .reg_offset = 0, .mask = BIT(0), },
+	[ATC2609A_IRQ_OV]       = { .reg_offset = 0, .mask = BIT(1), },
+	[ATC2609A_IRQ_OC]       = { .reg_offset = 0, .mask = BIT(2), },
+	[ATC2609A_IRQ_OT]       = { .reg_offset = 0, .mask = BIT(3), },
+	[ATC2609A_IRQ_UV]       = { .reg_offset = 0, .mask = BIT(4), },
+	[ATC2609A_IRQ_ALARM]    = { .reg_offset = 0, .mask = BIT(5), },
+	[ATC2609A_IRQ_ONOFF]    = { .reg_offset = 0, .mask = BIT(6), },
+	[ATC2609A_IRQ_WKUP]     = { .reg_offset = 0, .mask = BIT(7), },
+	[ATC2609A_IRQ_IR]       = { .reg_offset = 0, .mask = BIT(8), },
+	[ATC2609A_IRQ_REMCON]   = { .reg_offset = 0, .mask = BIT(9), },
+	[ATC2609A_IRQ_POWER_IN] = { .reg_offset = 0, .mask = BIT(10), },
+};
+static const struct regmap_irq_chip atc2609a_irq_chip = {
+	.name = "atc2609a",
+	.irqs = atc2609a_irqs,
+	.num_irqs = ARRAY_SIZE(atc2609a_irqs),
+
+	.num_regs = 1,
+	.status_base = ATC2609A_INTS_PD,
+	.mask_base = ATC2609A_INTS_MSK,
+	.mask_invert = true,
+};
+
+static const struct regmap_irq_chip * const sc_atc260x_irq_chip_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = &atc2603a_irq_chip,
+	[ATC260X_ICTYPE_2603C] = &atc2603c_irq_chip,
+	[ATC260X_ICTYPE_2609A] = &atc2609a_irq_chip
+};
+
+
+/*#ifdef CONFIG_ATC260X_SYSFS_REG */
+#if 1
+static ssize_t store_atc260x_reg(struct device *dev, struct device_attribute *attr,
+								 const char *buf, size_t count)
+{
+	struct atc260x_dev *atc260x;
+	unsigned int reg, reg2, mid_token, reg_val;
+	char *end_ptr;
+	int ret, write;
+
+	atc260x = dev_get_drvdata(dev);
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	reg = simple_strtoul(buf, &end_ptr, 16);
+	if ((buf == end_ptr) || (reg > 0xffff))
+		goto out;
+	mid_token = *end_ptr++;
+
+	reg2 = reg;
+	write = reg_val = 0;
+	switch (mid_token) {
+	case '-' :
+		reg2 = simple_strtoul(end_ptr, NULL, 16);
+		if (reg2 > 0xffff || reg2 < reg)
+			goto out;
+		break;
+	case '=' :
+		reg_val = simple_strtoul(end_ptr, NULL, 16);
+		if (reg_val > 0xffff)
+			goto out;
+		write = 1;
+		break;
+	}
+
+	if (write) {
+		ret = atc260x_reg_write(atc260x, reg, reg_val);
+		if (ret < 0)
+			goto out;
+		pr_err("[ATC260x] reg [0x%04x] <- 0x%04x\n", reg, reg_val);
+	}
+	/* "read" or "read back after written" */
+	for (; reg <= reg2; reg++) {
+		ret = atc260x_reg_read(atc260x, reg);
+		if (ret < 0)
+			goto out;
+		pr_err("[ATC260x] reg [0x%04x] :  0x%04x\n", reg, ret);
+	}
+out:
+	return count;
+}
+static ssize_t show_atc260x_reg(struct device *dev,
+								struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf,
+		"echo reg_addr > reg_dbg  : dump register @reg_addr\n"
+		"echo reg_addr1-reg_addr1 > reg_dbg  : dump register @range [reg_addr1,reg_addr2]\n"
+		"echo reg_addr=value > reg_dbg  : write value to register @reg_addr\n");
+}
+static ssize_t show_atc260x_auxadc_dbg_values(
+		struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct atc260x_dev *atc260x;
+	const char *ch_name, *ch_unit_name;
+	uint channel, size_accu;
+	s32 tr_value;
+	int ret, ret2;
+
+	atc260x = dev_get_drvdata(dev);
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	size_accu = 0; ;
+	for (channel = 0; ; channel++){
+		ch_name = atc260x_auxadc_channel_name(atc260x, channel);
+		ch_unit_name = atc260x_auxadc_channel_unit_name(atc260x, channel);
+		if (ch_name == NULL || ch_unit_name == NULL) {
+			break;
+		}
+		ret = atc260x_auxadc_get_translated(atc260x, channel, &tr_value);
+		if (ret == 0) {
+			ret2 = scnprintf(buf, PAGE_SIZE-size_accu, "%-2u %-10s  %d %s\n",
+				channel, ch_name, tr_value, ch_unit_name);
+		} else {
+			ret2 = scnprintf(buf, PAGE_SIZE-size_accu, "%-2u %-10s <error, ret=%d>\n",
+				channel, ch_name, ret);
+		}
+		if (ret2 < 0) {
+			break;
+		}
+		size_accu += ret2;
+		buf += ret2;
+	}
+	return size_accu;
+}
+static ssize_t show_atc260x_pstore_dbg_values(
+		struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct atc260x_dev *atc260x;
+	int ret;
+
+	atc260x = dev_get_drvdata(dev);
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	ret = atc260x_pstore_dbg_dump(atc260x, buf, PAGE_SIZE);
+	if (ret < 0){
+		dev_err(atc260x->dev, "%s() err, ret=%d\n", __func__, ret);
+		return 0;
+	}
+	return ret;
+}
+
+static struct device_attribute atc260x_attrs[] = {
+	__ATTR(reg_dbg, 0644, show_atc260x_reg, store_atc260x_reg),
+	__ATTR(auxadc_dbg, 0444, show_atc260x_auxadc_dbg_values, NULL),
+	__ATTR(pstore_dbg, 0444, show_atc260x_pstore_dbg_values, NULL),
+};
+
+/* create sysfs register operation interface */
+static int atc260x_create_attr(struct device *dev)
+{
+	int i, ret;
+	for (i = 0; i < ARRAY_SIZE(atc260x_attrs); i++) {
+		ret = device_create_file(dev, &atc260x_attrs[i]);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+static void atc260x_remove_attr(struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(atc260x_attrs); i++) {
+		device_remove_file(dev, &atc260x_attrs[i]);
+	}
+}
+#else /* CONFIG_ATC260X_SYSFS_REG */
+static int atc260x_create_attr(struct device *dev)
+{
+	return 0;
+}
+static void atc260x_remove_attr(struct device *dev)
+{
+}
+#endif /* CONFIG_ATC260X_SYSFS_REG */
+
+
+
+static uint atc260x_get_revised_version(struct atc260x_dev *atc260x)
+{
+	static const u16 sc_ver_reg_addr_tbl[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = ATC2603A_CVER,
+		[ATC260X_ICTYPE_2603C] = ATC2603C_CHIP_VER,
+		[ATC260X_ICTYPE_2609A] = ATC2609A_CHIP_VER
+	};
+	uint val, reg;
+
+	/* 目前支持的3款IC的rev定义都是一致的, 后续有不一致的情况就要走分支. */
+
+	BUG_ON(atc260x->ic_type >= ARRAY_SIZE(sc_ver_reg_addr_tbl));
+	reg = sc_ver_reg_addr_tbl[atc260x->ic_type];
+	BUG_ON(reg == 0);
+	val = atc260x_reg_read(atc260x, reg);
+	BUG_ON((int)val < 0 || val > 31);
+	return __ffs(val + 1U);
+}
+
+/* import the sub-devices define. */
+#define __MFD_ATC260X__NEED_SUB_DEV_DEFINE__ 1
+#include "atc260x-subdev.h"
+#undef __MFD_ATC260X__NEED_SUB_DEV_DEFINE__
+
+int atc260x_core_dev_init(struct atc260x_dev *atc260x)
+{
+	struct irq_domain *p_regmap_irq_domain;
+	int ret;
+
+	dev_info(atc260x->dev, "%s() enter\n", __func__);
+
+	/* prepare atc260x structure. */
+	atc260x->reg_save_buf = devm_kzalloc(
+		atc260x->dev,
+		sizeof(*(atc260x->reg_save_buf)) * ATC260X_MAX_SAVE_REGS,
+		GFP_KERNEL);
+	if (atc260x->reg_save_buf == NULL) {
+		dev_err(atc260x->dev, "no mem\n");
+		ret = -ENOMEM;
+		goto label_err_lv0;
+	}
+	dev_set_drvdata(atc260x->dev, atc260x);
+
+	/* set type ID */
+	atc260x->_obj_type_id = ATC260x_PARENT_OBJ_TYPE_ID;
+
+	/* hardware init. */
+	ret = atc260x_init_hardware(atc260x);
+	if (ret) {
+		dev_err(atc260x->dev, "failed to init hardware, ret=%d\n", ret);
+		goto label_err_lv1;
+	}
+
+	atc260x->ic_ver = atc260x_get_revised_version(atc260x);
+	dev_info(atc260x->dev, "detect PMU chip type %u ver %c\n",
+			atc260x->ic_type, 'A' + atc260x->ic_ver);
+
+	/* init direct access */
+	spin_lock_init(&atc260x->dacc_spinlock);
+	atc260x->reg_access_mode = ATC260X_ACCESS_MODE_NORMAL;
+
+	/* irq chip */
+	dev_info(atc260x->dev, "PMU root IRQ %u\n", atc260x->irq);
+	BUG_ON(atc260x->ic_type >= ARRAY_SIZE(sc_atc260x_irq_chip_tbl));
+	/* PMU subdev IRQ use dynamic (linear) mapping (set argument irq_base=0) */
+	ret = regmap_add_irq_chip(atc260x->regmap, atc260x->irq,
+				  IRQF_ONESHOT | IRQF_SHARED, 0,
+				  sc_atc260x_irq_chip_tbl[atc260x->ic_type],
+				  &atc260x->regmap_irqc_data);
+	if (ret) {
+		dev_err(atc260x->dev, "failed to add irq chip: %d\n", ret);
+		goto label_err_lv1;
+	}
+	/* get the irq_domain, used later */
+	p_regmap_irq_domain = regmap_irq_get_domain(atc260x->regmap_irqc_data);
+	BUG_ON(p_regmap_irq_domain == NULL);
+
+	/* init auxadc sub-function */
+	ret = atc260x_auxadc_dev_init(atc260x);
+	if (ret) {
+		dev_err(atc260x->dev, "failed to init auxadc: %d\n", ret);
+		goto label_err_lv2;
+	}
+
+	/* register PM notify */
+	atc260x->pm_notif_blk.notifier_call = atc260x_pm_notifier_func;
+	ret = register_pm_notifier(&(atc260x->pm_notif_blk));
+	if (ret) {
+		dev_err(atc260x->dev, "failed to register pm_notifier, ret=%d\n", ret);
+		goto label_err_lv3;
+	}
+
+	/* sysfs */
+	ret = atc260x_create_attr(atc260x->dev);
+	if (ret) {
+		dev_err(atc260x->dev, "failed to create sysfs nodes, ret=%d\n", ret);
+		goto label_err_lv4;
+	}
+
+	/* init Global API */
+	atc260x_extapi_dev_init(atc260x);
+
+	/* The core device is up, instantiate the subdevices.
+	 * We proveide the irq_domain,
+	 * so IRQ will get mapped after mfd_add_devices. */
+	BUG_ON(atc260x->ic_type >= ARRAY_SIZE(sc_atc260x_mfd_cell_def_tbl) ||
+		atc260x->ic_type >= ARRAY_SIZE(sc_atc260x_mfd_cell_cnt_tbl) ||
+		sc_atc260x_mfd_cell_def_tbl[atc260x->ic_type] == NULL ||
+		sc_atc260x_mfd_cell_cnt_tbl[atc260x->ic_type] == 0);
+	ret = mfd_add_devices(atc260x->dev, 0,
+		sc_atc260x_mfd_cell_def_tbl[atc260x->ic_type],
+		sc_atc260x_mfd_cell_cnt_tbl[atc260x->ic_type],
+		NULL, 0, p_regmap_irq_domain);
+	if (ret) {
+		dev_err(atc260x->dev, "failed to add children devices: %d\n", ret);
+		goto label_err_lv5;
+	}
+
+	dev_info(atc260x->dev, "%s() exit\n", __func__);
+	return 0;
+
+	label_err_lv5:
+	atc260x_extapi_dev_exit(atc260x);
+	atc260x_remove_attr(atc260x->dev);
+	label_err_lv4:
+	unregister_pm_notifier(&(atc260x->pm_notif_blk));
+	label_err_lv3:
+	atc260x_auxadc_dev_exit(atc260x);
+	label_err_lv2:
+	regmap_del_irq_chip(atc260x->irq, atc260x->regmap_irqc_data);
+	label_err_lv1:
+	dev_set_drvdata(atc260x->dev, NULL);
+	label_err_lv0:
+	atc260x->_obj_type_id = 0;
+	return ret;
+}
+
+void atc260x_core_dev_exit(struct atc260x_dev *atc260x)
+{
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+
+	mfd_remove_devices(atc260x->dev); /* remove all sub devices */
+
+	atc260x_extapi_dev_exit(atc260x);
+	atc260x_remove_attr(atc260x->dev);
+	unregister_pm_notifier(&(atc260x->pm_notif_blk));
+	atc260x_auxadc_dev_exit(atc260x);
+	regmap_del_irq_chip(atc260x->irq, atc260x->regmap_irqc_data);
+	dev_set_drvdata(atc260x->dev, NULL);
+	atc260x->_obj_type_id = 0;
+}
+
+int atc260x_core_dev_suspend(struct atc260x_dev *atc260x)
+{
+	int ret;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_info(atc260x->dev, "%s() enter\n", __func__);
+
+	disable_irq(atc260x->irq);
+
+	/* save critical registers. */
+	ret = _atc260x_save_critical_regs(atc260x);
+	if (ret) {
+		dev_err(atc260x->dev, "%s() save reg err, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(atc260x->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+int atc260x_core_dev_suspend_late(struct atc260x_dev *atc260x)
+{
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_info(atc260x->dev, "%s() enter\n", __func__);
+
+	/* disable all reg access until atc260x_set_reg_direct_access is called */
+	atc260x->reg_access_mode = ATC260X_ACCESS_MODE_NONE;
+	/* 这里不立即启用direct_access模式.
+	 * 因为i2c驱动现在还没有suspend, 仍有可能有未suspend的其它设备找它传输数据, 这里立即切到
+	 * direct_access模式就会影响i2c功能(产生竟争状态). */
+
+	dev_dbg(atc260x->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+int atc260x_core_dev_resume_early(struct atc260x_dev *atc260x)
+{
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_info(atc260x->dev, "%s() enter\n", __func__);
+	atc260x_exit_reg_direct_access(atc260x);
+	dev_dbg(atc260x->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+int atc260x_core_dev_resume(struct atc260x_dev *atc260x)
+{
+	int ret;
+
+	ATC260X_ASSERT_VALID_DEV(atc260x);
+	dev_info(atc260x->dev, "%s() enter\n", __func__);
+
+	/* restore critical registers. */
+	ret = _atc260x_restore_critical_regs(atc260x);
+	if (ret) {
+		dev_err(atc260x->dev, "%s() restore reg err, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	/*enable irq MUST after restore registers */
+	enable_irq(atc260x->irq);
+
+	dev_dbg(atc260x->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+/* About the suspend order
+ * 关于 suspend 顺序的说明:
+ *
+ * kernel的suspend顺序是依赖于设备的注册顺序的(单纯的device_add()的顺序, 与驱动probe顺序几乎无关),
+ * 而i2c/spi总线上的260x设备的device_add()一定是晚于mmc_host/usb_host等由DTS注册的platform_dev的.
+ * 故260x设备的suspend一定是比mmc_host/usb_host等platform_dev调用得要早.
+ *
+ * 这里有个问题: mmc_host/usb_host等设备会用到regulator, 这就要求260x设备以及它的regulator子设备
+ * 在suspend后要仍然能工作. 这里260x旧将部分影响功能的操作放到suspend_late执行.
+ *
+ * 由于i2c驱动的suspend是放在suspend_noirq阶段, 为了防止其suspend后260x仍通过regmap操作它,
+ * 在260x的suspend_late时将reg读写锁死, 直到direct_access模式启用后才可继续访问reg.
+ * 因为这锁死之前, 所有依赖260x的设备均已经suspend, 不会造成问题.
+ *
+ * direct_access模式启用必须在i2c/spi的suspend_noirq之后, 否则两者会争抢操作硬件.
+ *
+ * 安全的suspend顺序是:
+ *   0. 非platform设备suspend (期间会调用regulator_disable关闭dcdc/ldo)
+ *   1. regulator suspend (不做任何事情)
+ *   2. 260x suspend
+ *   3. mmc_host / camera 等platform设备 suspend (期间会调用regulator_disable关闭dcdc/ldo)
+ *   4. regulator suspend_late (打印regulator状态)
+ *   5. 260x suspend_late (锁寄存器访问)
+ *   6. i2c/spi host suspend_noirq (关闭i2c/spi硬件)
+ *   7. atc260x_set_reg_direct_access() 被 platform_suspend_ops.prepare_late 调用
+ *   8. 系统进S2
+ *
+ * 安全的resume顺序则相反. */
diff --git a/drivers/mfd/atc260x-core.h b/drivers/mfd/atc260x-core.h
new file mode 100755
index 0000000..f45e2f3
--- /dev/null
+++ b/drivers/mfd/atc260x-core.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+/* This header is used by core only, other modules has no need to include this. */
+
+
+#ifndef __MFD_ATC260X_CORE_H__
+#define __MFD_ATC260X_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+#define ATC260X_MAX_SAVE_REGS       256
+
+enum {
+	ATC260X_ACCESS_MODE_NORMAL = 0,
+	ATC260X_ACCESS_MODE_DIRECT,
+	ATC260X_ACCESS_MODE_NONE
+};
+
+
+struct atc260x_auxadc_chn_hwinfo;
+
+/* ATC260X device */
+struct atc260x_dev {
+	/* all fields internal used only, not for sub devices! */
+
+	struct device               *dev;
+	int                         irq;       /* 260x_core的IRQ号, 对应SOC的某个外部中断. */
+	struct spi_device           *spi;
+	struct i2c_client           *i2c_client;
+	struct regmap               *regmap;
+	struct regmap_irq_chip_data *regmap_irqc_data;
+
+	u8                          ic_type;       /* see ATC260X_ICTYPE_2603A ... */
+	u8                          ic_ver;        /* see ATC260X_ICVER_A ... */
+	u8                          bus_num;       /* SPI / I2C bus number, used by reg direct access. */
+	u8                          bus_addr;      /* device's bus address, only for I2C, 7bit, r/w bit excluded */
+	u8                          reg_access_mode; /* see ATC260X_ACCESS_MODE_NORMAL ... */
+	u8                          direct_access_ioremap_flag;
+
+	spinlock_t                  dacc_spinlock; /* for direct access */
+	void __iomem *              dacc_iobase;     /* for bus, I2C SPI ... */
+	void __iomem *              dacc_cmu_iobase; /* for CMU */
+	int (*direct_read_reg)(struct atc260x_dev *atc260x, uint reg);
+	int (*direct_write_reg)(struct atc260x_dev *atc260x, uint reg, u16 val);
+	void (*direct_acc_init)(struct atc260x_dev *atc260x);
+	void (*direct_acc_exit)(struct atc260x_dev *atc260x);
+
+	struct mutex                auxadc_read_mutex;
+	const struct atc260x_auxadc_hwinfo *auxadc_hwinfo;
+	void                        *auxadc_udata;
+
+	struct notifier_block       pm_notif_blk;
+
+	u16                         *reg_save_buf;
+
+	u32                         _obj_type_id;
+};
+
+#define ATC260x_PARENT_OBJ_TYPE_ID 0x72f80927U
+
+/* for debug */
+#define ATC260X_ASSERT_VALID_DEV(ADEV) \
+	BUG_ON((ADEV == NULL) || IS_ERR(ADEV) || \
+	 (ADEV)->_obj_type_id != ATC260x_PARENT_OBJ_TYPE_ID)
+#define ATC260X_CHK_VALID_DEV(ADEV) \
+	(! ((ADEV == NULL) || IS_ERR(ADEV) || \
+	 (ADEV)->_obj_type_id != ATC260x_PARENT_OBJ_TYPE_ID))
+
+
+/* only for core internal. */
+extern int atc260x_core_dev_init(struct atc260x_dev *atc260x);
+extern void atc260x_core_dev_exit(struct atc260x_dev *atc260x);
+extern int atc260x_core_dev_suspend(struct atc260x_dev *atc260x);
+extern int atc260x_core_dev_suspend_late(struct atc260x_dev *atc260x);
+extern int atc260x_core_dev_resume_early(struct atc260x_dev *atc260x);
+extern int atc260x_core_dev_resume(struct atc260x_dev *atc260x);
+
+extern int atc260x_auxadc_dev_init(struct atc260x_dev *atc260x);
+extern void atc260x_auxadc_dev_exit(struct atc260x_dev *atc260x);
+
+extern void atc260x_extapi_dev_init(struct atc260x_dev *atc260x);
+extern void atc260x_extapi_dev_exit(struct atc260x_dev *atc260x);
+
+
+extern int atc260x_pstore_dbg_dump(struct atc260x_dev *atc260x, char *buf, uint bufsize);
+
+
+#endif /* __MFD_ATC260X_CORE_H__ */
diff --git a/drivers/mfd/atc260x-ex-api.c b/drivers/mfd/atc260x-ex-api.c
new file mode 100755
index 0000000..45355be
--- /dev/null
+++ b/drivers/mfd/atc260x-ex-api.c
@@ -0,0 +1,70 @@
+/* UTF-8 encoded. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include "atc260x-core.h"
+
+/* external API
+ * 这里提供一些供子设备之外的模块调用的接口.
+ * 这样做主要是为了避免内部使用的API被外部滥用, 多IC兼容时造成麻烦. */
+
+
+static struct atc260x_dev *s_current_atc260x = NULL;
+
+/* auxadc ------------------------------------------------------------------- */
+
+int atc260x_ex_auxadc_find_chan(const char *channel_name)
+{
+	ATC260X_ASSERT_VALID_DEV(s_current_atc260x); /* 设备枚举前调用则报BUG, 下同. */
+	return atc260x_auxadc_find_chan(s_current_atc260x, channel_name);
+}
+EXPORT_SYMBOL_GPL(atc260x_ex_auxadc_find_chan);
+
+int atc260x_ex_auxadc_read(uint channel, s32 *p_tr_value)
+{
+	ATC260X_ASSERT_VALID_DEV(s_current_atc260x);
+	return atc260x_auxadc_get_translated(s_current_atc260x, channel, p_tr_value);
+}
+EXPORT_SYMBOL_GPL(atc260x_ex_auxadc_read);
+
+
+/* pstore ------------------------------------------------------------------- */
+
+int atc260x_ex_pstore_set(uint tag, u32 value)
+{
+	ATC260X_ASSERT_VALID_DEV(s_current_atc260x);
+	return atc260x_pstore_set(s_current_atc260x, tag, value);
+}
+EXPORT_SYMBOL_GPL(atc260x_ex_pstore_set);
+
+int atc260x_ex_pstore_get(uint tag, u32 *p_value)
+{
+	ATC260X_ASSERT_VALID_DEV(s_current_atc260x);
+	return atc260x_pstore_get(s_current_atc260x, tag, p_value);
+}
+EXPORT_SYMBOL_GPL(atc260x_ex_pstore_get);
+
+
+/* init --------------------------------------------------------------------- */
+
+void atc260x_extapi_dev_init(struct atc260x_dev *atc260x)
+{
+	if (s_current_atc260x == NULL)
+		s_current_atc260x = atc260x;
+}
+
+void atc260x_extapi_dev_exit(struct atc260x_dev *atc260x)
+{
+	if (atc260x == s_current_atc260x)
+		s_current_atc260x = NULL;
+}
diff --git a/drivers/mfd/atc260x-i2c.c b/drivers/mfd/atc260x-i2c.c
new file mode 100755
index 0000000..bb73814
--- /dev/null
+++ b/drivers/mfd/atc260x-i2c.c
@@ -0,0 +1,605 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include <mach/regs_map-atm7039.h>
+
+#include "atc260x-core.h"
+
+
+
+/*----------------------------------------------------------------------------*/
+/* code for direct access */
+
+#define REG_I2C_CTL(_iob)                   ((_iob)+(I2C0_CTL-I2C0_BASE))
+#define REG_I2C_CLKDIV(_iob)                ((_iob)+(I2C0_CLKDIV-I2C0_BASE))
+#define REG_I2C_STAT(_iob)                  ((_iob)+(I2C0_STAT-I2C0_BASE))
+#define REG_I2C_ADDR(_iob)                  ((_iob)+(I2C0_ADDR-I2C0_BASE))
+#define REG_I2C_TXDAT(_iob)                 ((_iob)+(I2C0_TXDAT-I2C0_BASE))
+#define REG_I2C_RXDAT(_iob)                 ((_iob)+(I2C0_RXDAT-I2C0_BASE))
+#define REG_I2C_CMD(_iob)                   ((_iob)+(I2C0_CMD-I2C0_BASE))
+#define REG_I2C_FIFOCTL(_iob)               ((_iob)+(I2C0_FIFOCTL-I2C0_BASE))
+#define REG_I2C_FIFOSTAT(_iob)              ((_iob)+(I2C0_FIFOSTAT-I2C0_BASE))
+#define REG_I2C_DATCNT(_iob)                ((_iob)+(I2C0_DATCNT-I2C0_BASE))
+#define REG_I2C_RCNT(_iob)                  ((_iob)+(I2C0_RCNT-I2C0_BASE))
+
+#define REG_CMU_ETHERNETPLL(_iob)           ((_iob)+(CMU_ETHERNETPLL-CMU_BASE))
+#define REG_CMU_DEVCLKEN0(_iob)             ((_iob)+(CMU_DEVCLKEN0-CMU_BASE))
+#define REG_CMU_DEVCLKEN1(_iob)             ((_iob)+(CMU_DEVCLKEN1-CMU_BASE))
+#define REG_CMU_DEVRST0(_iob)               ((_iob)+(CMU_DEVRST0-CMU_BASE))
+#define REG_CMU_DEVRST1(_iob)               ((_iob)+(CMU_DEVRST1-CMU_BASE))
+
+#define _da_i2c_writel(_iob, _val, _reg_name) writel( (_val), REG_ ##_reg_name ((_iob)) )
+#define _da_i2c_readl(_iob, _reg_name)        readl( REG_ ##_reg_name ((_iob)) )
+
+
+
+
+/* I2Cx_CTL */
+#define I2C_CTL_GRAS                (0x1 << 0)      /* Generate ACK or NACK Signal */
+#define I2C_CTL_GRAS_ACK           0            /* generate the ACK signal at 9th clock of SCL */
+#define I2C_CTL_GRAS_NACK          I2C_CTL_GRAS /* generate the NACK signal at 9th clock of SCL */
+#define I2C_CTL_RB                  (0x1 << 1)     /* Release Bus */
+#define I2C_CTL_GBCC_MASK           (0x3 << 2)     /* Loop Back Enable */
+#define I2C_CTL_GBCC(x)             (((x) & 0x3) << 2)
+#define I2C_CTL_GBCC_NONE           I2C_CTL_GBCC(0)
+#define I2C_CTL_GBCC_START          I2C_CTL_GBCC(1)
+#define I2C_CTL_GBCC_STOP           I2C_CTL_GBCC(2)
+#define I2C_CTL_GBCC_RESTART        I2C_CTL_GBCC(3)
+#define I2C_CTL_IRQE                (0x1 << 5)     /* IRQ Enable */
+#define I2C_CTL_PUEN                (0x1 << 6)     /* Internal Pull-Up resistor (1.5k) enable. */
+#define I2C_CTL_EN                  (0x1 << 7)     /* Enable. When enable, reset the status machine to IDLE */
+#define I2C_CTL_AE                  (0x1 << 8)     /* Arbitor enable */
+
+/* I2Cx_CLKDIV */
+#define I2C_CLKDIV_DIV_MASK         (0xff << 0)     /* Clock Divider Factor (only for master mode). */
+#define I2C_CLKDIV_DIV(x)           (((x) & 0xff) << 0)
+
+/* I2Cx_STAT */
+#define I2C_STAT_RACK               (0x1 << 0)      /* Receive ACK or NACK when transmit data or address */
+#define I2C_STAT_BEB                (0x1 << 1)      /* IRQ Pending Bit, Write “1” to clear this bit */
+#define I2C_STAT_IRQP               (0x1 << 2)      /* IRQ Pending Bit, Write “1” to clear this bit */
+#define I2C_STAT_LAB                (0x1 << 3)      /* Lose arbitration bit, Write “1” to clear this bit */
+#define I2C_STAT_STPD               (0x1 << 4)      /* Stop detect bit, Write “1” to clear this bit */
+#define I2C_STAT_STAD               (0x1 << 5)      /* Start detect bit, Write “1” to clear this bit */
+#define I2C_STAT_BBB                (0x1 << 6)      /* Bus busy bit */
+#define I2C_STAT_TCB                (0x1 << 7)      /* Transfer complete bit */
+#define I2C_STAT_LBST               (0x1 << 8)      /* Last Byte Status Bit, 0: address, 1: data */
+#define I2C_STAT_SAMB               (0x1 << 9)      /* Slave address match bit */
+#define I2C_STAT_SRGC               (0x1 << 10)     /* Slave receive general call */
+
+#define I2C_BUS_ERR_MSK             ( I2C_STAT_LAB | I2C_STAT_BEB)
+
+/* I2Cx_CMD */
+#define I2C_CMD_SBE                 (0x1 << 0)      /* Start bit enable */
+#define I2C_CMD_AS_MASK             (0x7 << 1)      /* Address select */
+#define I2C_CMD_AS(x)               (((x) & 0x7) << 1)
+#define I2C_CMD_RBE                 (0x1 << 4)      /* Restart bit enable */
+#define I2C_CMD_SAS_MASK            (0x7 << 5)      /* Second Address select */
+#define I2C_CMD_SAS(x)              (((x) & 0x7) << 5)
+#define I2C_CMD_DE                  (0x1 << 8)      /* Data enable */
+#define I2C_CMD_NS                  (0x1 << 9)      /* NACK select */
+#define I2C_CMD_SE                  (0x1 << 10)     /* Stop enable */
+#define I2C_CMD_MSS                 (0x1 << 11)     /* MSS Master or slave mode select */
+#define I2C_CMD_WRS                 (0x1 << 12)     /* Write or Read select */
+#define I2C_CMD_EXEC                (0x1 << 15)     /* Start to execute the command list */
+
+/*FIFO mode write cmd 0x8d01 */
+#define I2C_CMD_X   (\
+	I2C_CMD_EXEC | I2C_CMD_MSS | \
+	I2C_CMD_SE | I2C_CMD_DE | I2C_CMD_SBE)
+
+/* I2Cx_FIFOCTL */
+#define I2C_FIFOCTL_NIB             (0x1 << 0)      /* NACK Ignore Bit */
+#define I2C_FIFOCTL_RFR             (0x1 << 1)      /* RX FIFO reset bit, Write 1 to reset RX FIFO */
+#define I2C_FIFOCTL_TFR             (0x1 << 2)      /* TX FIFO reset bit, Write 1 to reset TX FIFO */
+
+/* I2Cx_FIFOSTAT */
+#define I2C_FIFOSTAT_CECB           (0x1 << 0)      /* command Execute Complete bit */
+#define I2C_FIFOSTAT_RNB            (0x1 << 1)      /* Receive NACK Error bit */
+#define I2C_FIFOSTAT_RFE            (0x1 << 2)      /* RX FIFO empty bit */
+#define I2C_FIFOSTAT_RFF            (0x1 << 3)      /* RX FIFO full bit */
+#define I2C_FIFOSTAT_TFE            (0x1 << 4)      /* TX FIFO empty bit */
+#define I2C_FIFOSTAT_TFF            (0x1 << 5)      /* TX FIFO full bit */
+#define I2C_FIFOSTAT_RFD_MASK       (0xff << 8)     /* Rx FIFO level display */
+#define I2C_FIFOSTAT_RFD_SHIFT      (8)
+#define I2C_FIFOSTAT_TFD_MASK       (0xff << 16)    /* Tx FIFO level display */
+#define I2C_FIFOSTAT_TFD_SHIFT      (16)
+
+#define I2C_CTL_START_CMD               (  \
+		  I2C_CTL_IRQE |   \
+		  I2C_CTL_EN |     \
+		  I2C_CTL_GBCC_START |    \
+		  I2C_CTL_PUEN |     \
+		  I2C_CTL_RB   \
+		 )
+
+#define I2C_CTL_STOP_CMD                (  \
+			I2C_CTL_EN |  \
+			I2C_CTL_PUEN | \
+			I2C_CTL_GBCC_STOP | \
+			I2C_CTL_RB | \
+			I2C_CTL_IRQE \
+		)
+
+#define I2C_CTL_RESTART_CMD             ( \
+			I2C_CTL_IRQE | \
+			I2C_CTL_EN | \
+			I2C_CTL_GBCC_RESTART | \
+			I2C_CTL_PUEN | \
+			I2C_CTL_RB \
+		)
+
+
+
+#define I2C_WRITE   (0)
+#define I2C_READ    (1)
+
+#define I2C_OK          0
+#define I2C_NOK         1
+#define I2C_NACK        2
+#define I2C_NOK_TOUT    3
+#define I2C_TIMEOUT     1
+
+/*
+ * direct msg.
+ */
+struct _da_i2c_dmsg {
+	u8  type;       /*read or write */
+	u8  s_addr;     /*slave address */
+	u8  r_addr;     /*reg address */
+	u8  len;        /*data length */
+	u8 * data;      /*data buffer */
+};
+
+/*
+ * cmd_type is 0 for write, 1 for read.
+ *
+ * addr_len can take any value from 0-255, it is only limited
+ * by the char, we could make it larger if needed. If it is
+ * 0 we skip the address write cycle.
+ */
+static int _do_i2c_transfer(void __iomem *iobase, struct _da_i2c_dmsg * msg)
+{
+	u32 val = 0, i = 0, result = I2C_OK;
+	u32 i2c_cmd;
+	u8 chip, addr_len, data_len;
+	u8 *addr;
+	u8 *data;
+
+	chip = msg->s_addr;
+	addr = &(msg->r_addr);
+	addr_len = 1; /*fix */
+	data = msg->data;
+	data_len = msg->len;
+
+
+	switch (msg->type) {
+	case I2C_WRITE:
+		/*1, enable i2c ,not enable interrupt*/
+		_da_i2c_writel(iobase, 0x80, I2C_CTL);
+		/*2, write data count*/
+		_da_i2c_writel(iobase, data_len, I2C_DATCNT);
+		/*3, write slave addr*/
+		_da_i2c_writel(iobase, (chip << 1), I2C_TXDAT);
+		/*4, write register addr*/
+		for (i = 0; i < addr_len; i++)
+			_da_i2c_writel(iobase, addr[i], I2C_TXDAT);
+		/*5, write data*/
+		for (i = 0; i < data_len; i++)
+			_da_i2c_writel(iobase, data[i], I2C_TXDAT);
+		/*6, write fifo command */
+		i2c_cmd = I2C_CMD_X | I2C_CMD_AS((addr_len) + sizeof(chip));
+		_da_i2c_writel(iobase, i2c_cmd, I2C_CMD);
+
+		/* wait command complete */
+		while (1) {
+			val = _da_i2c_readl(iobase, I2C_FIFOSTAT);
+			if (val & I2C_FIFOSTAT_RNB) {
+				result = I2C_NACK;
+				goto label_err;
+			}
+			if (val & I2C_FIFOSTAT_CECB)
+				break;
+			/* 因为可能跑在关中断&关调度的场景, 不做超时处理了. */
+		}
+		result = I2C_OK;
+		break;
+
+	case I2C_READ:
+		/*1, enable i2c ,not enable interrupt*/
+		_da_i2c_writel(iobase, 0x80, I2C_CTL);
+		/*2, write data count*/
+		_da_i2c_writel(iobase, data_len, I2C_DATCNT);
+		/*3, write slave addr*/
+		_da_i2c_writel(iobase, (chip << 1), I2C_TXDAT);
+		/*4, write register addr*/
+		for (i = 0; i < addr_len; i++)
+			_da_i2c_writel(iobase, addr[i], I2C_TXDAT);
+		/*5, write slave addr | read_flag*/
+		_da_i2c_writel(iobase, (chip << 1) | I2C_READ, I2C_TXDAT);
+		/*6, write fifo command */
+		i2c_cmd = I2C_CMD_X | I2C_CMD_RBE | I2C_CMD_NS \
+			| I2C_CMD_SAS(sizeof(chip)) |\
+			I2C_CMD_AS(sizeof(chip) + addr_len);
+		_da_i2c_writel(iobase, i2c_cmd, I2C_CMD);
+
+		/* wait command complete */
+		while (1) {
+			val = _da_i2c_readl(iobase, I2C_FIFOSTAT);
+			if (val & I2C_FIFOSTAT_RNB) {
+				result = I2C_NACK;
+				goto label_err;
+			}
+			if (val & I2C_FIFOSTAT_CECB)
+				break;
+			/* 因为可能跑在关中断&关调度的场景, 不做超时处理了. */
+		}
+		result = I2C_OK;
+
+		/*8, Read data from rxdata*/
+		for (i = 0; i < data_len; i++) {
+			data[i] = _da_i2c_readl(iobase, I2C_RXDAT);
+			/*i2c_dbg("-->>Read data[%d] = 0x%02x\r\n", i, data[i]); */
+		}
+		break;
+
+	default:
+		/*i2c_dbg("i2c_transfer: bad call\n"); */
+		result = I2C_NOK;
+		break;
+	}
+	return result;
+
+	label_err:
+	/* clear err bit */
+	_da_i2c_writel(iobase, I2C_FIFOSTAT_RNB, I2C_FIFOSTAT);
+	/* reset fifo */
+	_da_i2c_writel(iobase, 0x06, I2C_FIFOCTL);
+	_da_i2c_readl(iobase, I2C_FIFOCTL);
+	return result;
+}
+
+static int _i2c_transfer(void __iomem *iobase, struct _da_i2c_dmsg * msg)
+{
+	int ret;
+	_da_i2c_writel(iobase, 0xff, I2C_STAT);/*reset all the stats */
+	_da_i2c_writel(iobase, I2C_CTL_EN | I2C_CTL_PUEN, I2C_CTL); /*disable inttrupt. */
+	ret = _do_i2c_transfer(iobase, msg);
+	/*disable adapter. */
+	_da_i2c_writel(iobase, 0, I2C_CTL);
+	return ret;
+}
+
+static int _i2c_read(void __iomem *iobase, u8 addr, u8 reg, u8 *data, u32 len)
+{
+	int ret = 0;
+	struct _da_i2c_dmsg msg;
+
+	msg.type = I2C_READ;
+	msg.s_addr = addr;
+	msg.r_addr = reg;
+	msg.len = len;
+	msg.data = data;
+	ret = _i2c_transfer(iobase, &msg);
+	if (ret != 0) {
+		pr_err("direct I2c read failed, ret=%d\n", ret);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int _i2c_write(void __iomem *iobase, u8 addr, u8 reg, u8 *data, u32 len)
+{
+	struct _da_i2c_dmsg msg;
+	int ret = 0;
+
+	msg.type = I2C_WRITE;
+	msg.s_addr = addr;
+	msg.r_addr = reg;
+	msg.len = len;
+	msg.data = data;
+
+	ret = _i2c_transfer(iobase, &msg);
+	if (ret != 0) {
+		pr_err("driect I2c write failed, ret=%d\n", ret);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void _atc260x_i2c_direct_access_init(struct atc260x_dev *atc260x)
+{
+	static const ulong sc_hw_iobase_tbl[] = {
+		I2C0_BASE, I2C1_BASE, I2C2_BASE, I2C3_BASE
+	};
+	static const u8 sc_i2c_clk_ctl_bit_tbl[] = {
+		14, 15, 30, 31,  /* for gl5206 only !!! */
+	};
+	static const u8 sc_i2c_reset_ctl_bit_tbl[] = {
+		12, 13, 18, 19,  /* for gl5206 only !!! */
+	};
+	ulong hw_iobase, hw_iosize;
+	void __iomem *hw_cmu_iobase;
+	uint mask;
+
+	/* ioremap ... */
+	if (atc260x->direct_access_ioremap_flag == 0) {
+		atc260x->direct_access_ioremap_flag = 1;
+
+		BUG_ON(atc260x->bus_num >= ARRAY_SIZE(sc_hw_iobase_tbl));
+		hw_iobase = sc_hw_iobase_tbl[atc260x->bus_num];
+		hw_iosize = SPI0_RXCR - SPI0_BASE +4U;
+		/* no need to request the IO region, it always failed because we
+		 * are now trying to steal others' stuff ;)
+		if (!request_mem_region(hw_iobase, hw_iosize, "atc260x-spi-dacc")) {
+			dev_err(atc260x->dev, "failed to request SPI region\n");
+			BUG();
+		} */
+		atc260x->dacc_iobase = devm_ioremap(atc260x->dev, hw_iobase, hw_iosize);
+		if (atc260x->dacc_iobase == NULL) {
+			dev_err(atc260x->dev, "failed to ioremap SPI region\n");
+			BUG();
+		}
+		atc260x->dacc_cmu_iobase = devm_ioremap(atc260x->dev,
+					CMU_BASE, (CMU_DIGITALDEBUG-CMU_BASE));
+		if (atc260x->dacc_cmu_iobase == NULL) {
+			dev_err(atc260x->dev, "failed to ioremap CMU region\n");
+			BUG();
+		}
+	}
+	hw_cmu_iobase = atc260x->dacc_cmu_iobase;
+
+	/* init I2C clock  (gl5206 only !!!) */
+	/* ETH PLL */
+	writel(readl(REG_CMU_ETHERNETPLL(hw_cmu_iobase)) | (1U << 0),
+			REG_CMU_ETHERNETPLL(hw_cmu_iobase));
+	udelay(600);
+	mask = 1U << sc_i2c_clk_ctl_bit_tbl[atc260x->bus_num];
+	writel(readl(REG_CMU_DEVCLKEN1(hw_cmu_iobase)) | mask,
+			REG_CMU_DEVCLKEN1(hw_cmu_iobase));
+	readl(REG_CMU_DEVCLKEN1(hw_cmu_iobase));
+
+	/* reset I2C */
+	mask = 1U << sc_i2c_reset_ctl_bit_tbl[atc260x->bus_num];
+	writel(readl(REG_CMU_DEVRST1(hw_cmu_iobase)) & ~mask,
+			REG_CMU_DEVRST1(hw_cmu_iobase));
+	readl(REG_CMU_DEVRST1(hw_cmu_iobase));
+	udelay(20);
+	writel(readl(REG_CMU_DEVRST1(hw_cmu_iobase)) | mask,
+			REG_CMU_DEVRST1(hw_cmu_iobase));
+	readl(REG_CMU_DEVRST1(hw_cmu_iobase));
+	udelay(50);
+
+	/* I2C clock divider (400kHz) */
+	writel((5U<<8)|(16U<<0), REG_I2C_CLKDIV(atc260x->dacc_iobase));
+
+	dev_info(atc260x->dev, "%s() direct_access activated\n", __func__);
+}
+
+static void _atc260x_i2c_direct_access_exit(struct atc260x_dev *atc260x)
+{
+	if (atc260x->direct_access_ioremap_flag != 0) {
+		atc260x->direct_access_ioremap_flag = 0;
+		devm_iounmap(atc260x->dev, atc260x->dacc_iobase);
+		devm_iounmap(atc260x->dev, atc260x->dacc_cmu_iobase);
+		dev_info(atc260x->dev, "%s() direct_access IO unmapped\n", __func__);
+	}
+}
+
+static int _atc260x_i2c_direct_read_reg(struct atc260x_dev *atc260x, uint reg)
+{
+	u8 buffer[4];
+	int ret;
+
+	ret = _i2c_read(atc260x->dacc_iobase, atc260x->bus_addr, reg, buffer, 2);
+	if (ret < 0) {
+		return ret;
+	}
+	ret = buffer[1] | ((uint)(buffer[0]) << 8);
+	return ret;
+}
+
+static int _atc260x_i2c_direct_write_reg(struct atc260x_dev *atc260x, uint reg, u16 val)
+{
+	u8 buffer[4];
+	int ret;
+
+	buffer[0] = (val >> 8) & 0xffU;
+	buffer[1] = val & 0xffU;
+	ret = _i2c_write(atc260x->dacc_iobase, atc260x->bus_addr, reg, buffer, 2);
+	return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+static struct regmap_config atc2603c_i2c_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+
+	/* TODO : add wr_table rd_table volatile_table precious_table */
+
+	.cache_type = REGCACHE_NONE,   /* TODO : i2c reg_rw need a fast cache, REGCACHE_FLAT  */
+	.max_register = ATC2603C_CHIP_VER,
+	.reg_format_endian = REGMAP_ENDIAN_BIG,
+	.val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static struct regmap_config atc2609a_i2c_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+
+	/* TODO : add wr_table rd_table volatile_table precious_table */
+
+	.cache_type = REGCACHE_NONE,   /* TODO : i2c reg_rw need a fast cache, REGCACHE_FLAT  */
+	.max_register = ATC2609A_CHIP_VER,
+	.reg_format_endian = REGMAP_ENDIAN_BIG,
+	.val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct of_device_id atc260x_i2c_of_match[] = {
+	{ .compatible = "actions,atc2603c", .data = (void *)ATC260X_ICTYPE_2603C },
+	{ .compatible = "actions,atc2609a", .data = (void *)ATC260X_ICTYPE_2609A },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, atc260x_i2c_of_match);
+
+static int atc260x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id_unused)
+{
+	struct atc260x_dev *atc260x;
+	struct regmap_config *p_regmap_cfg;
+	const struct of_device_id *of_id;
+	int ret;
+
+	dev_info(&i2c->dev, "Probing...\n");
+
+	ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+	if (!ret) {
+		dev_err(&i2c->dev, "I2C bus not functional\n");
+		return -EFAULT;
+	}
+
+	atc260x = devm_kzalloc(&i2c->dev, sizeof(*atc260x), GFP_KERNEL);
+	if (atc260x == NULL)
+		return -ENOMEM;
+
+	of_id = of_match_device(atc260x_i2c_of_match, &i2c->dev); /* match again, get of_id */
+	if (!of_id) {
+		dev_err(&i2c->dev, "of_match failed, unable to get device data\n");
+		return -ENODEV;
+	}
+	atc260x->ic_type = (ulong) of_id->data;
+
+	i2c_set_clientdata(i2c, atc260x);
+	atc260x->dev = &i2c->dev; /* 不要创建新设备, 复用i2c的从设备即可. */
+	atc260x->irq =i2c->irq;
+
+	/* init direct-access functions */
+	atc260x->bus_num = (typeof(atc260x->bus_num))(i2c->adapter->nr);
+	atc260x->bus_addr = i2c->addr;
+	atc260x->direct_read_reg = _atc260x_i2c_direct_read_reg;
+	atc260x->direct_write_reg = _atc260x_i2c_direct_write_reg;
+	atc260x->direct_acc_init = _atc260x_i2c_direct_access_init;
+	atc260x->direct_acc_exit = _atc260x_i2c_direct_access_exit;
+
+	/* register regmap */
+	switch (atc260x->ic_type) {
+	case ATC260X_ICTYPE_2603C:
+		p_regmap_cfg = &atc2603c_i2c_regmap_config;
+		break;
+	case ATC260X_ICTYPE_2609A:
+		p_regmap_cfg = &atc2609a_i2c_regmap_config;
+		break;
+	default:
+		BUG();
+	}
+	atc260x->regmap = devm_regmap_init_i2c(i2c, p_regmap_cfg);
+	if (IS_ERR(atc260x->regmap)) {
+		ret = PTR_ERR(atc260x->regmap);
+		dev_err(atc260x->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = atc260x_core_dev_init(atc260x);
+
+	return ret;
+}
+
+static int atc260x_i2c_remove(struct i2c_client *i2c)
+{
+	struct atc260x_dev *atc260x = i2c_get_clientdata(i2c);
+	atc260x_core_dev_exit(atc260x);
+	return 0;
+}
+
+static int atc260x_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct atc260x_dev *atc260x = i2c_get_clientdata(client);
+	return atc260x_core_dev_suspend(atc260x);
+}
+static int atc260x_i2c_suspend_late(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct atc260x_dev *atc260x = i2c_get_clientdata(client);
+	return atc260x_core_dev_suspend_late(atc260x);
+}
+static int atc260x_i2c_resume_early(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct atc260x_dev *atc260x = i2c_get_clientdata(client);
+	return atc260x_core_dev_resume_early(atc260x);
+}
+static int atc260x_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct atc260x_dev *atc260x = i2c_get_clientdata(client);
+	return atc260x_core_dev_resume(atc260x);
+}
+static const struct dev_pm_ops s_atc260x_i2c_pm_ops = {
+	.suspend       = atc260x_i2c_suspend,
+	.suspend_late  = atc260x_i2c_suspend_late,
+	.resume_early  = atc260x_i2c_resume_early,
+	.resume        = atc260x_i2c_resume,
+	.freeze        = atc260x_i2c_suspend,
+	.freeze_late   = atc260x_i2c_suspend_late,
+	.thaw_early    = atc260x_i2c_resume_early,
+	.thaw          = atc260x_i2c_resume,
+	.poweroff      = atc260x_i2c_suspend,
+	.poweroff_late = atc260x_i2c_suspend_late,
+	.restore_early = atc260x_i2c_resume_early,
+	.restore       = atc260x_i2c_resume,
+};
+
+static const struct i2c_device_id atc260x_i2c_id_tbl[] = {
+	{ "atc2603a", 0 },
+	{ "atc2603c", 0 },
+	{ "atc2609a", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, atc260x_i2c_id_tbl);
+
+static struct i2c_driver atc260x_i2c_driver = {
+	.probe      = atc260x_i2c_probe,
+	.remove     = atc260x_i2c_remove,
+	.driver = {
+		.name   = "atc260x_i2c",
+		.owner  = THIS_MODULE,
+		.pm     = &s_atc260x_i2c_pm_ops,
+		.of_match_table = of_match_ptr(atc260x_i2c_of_match),
+	},
+	.id_table   = atc260x_i2c_id_tbl,
+};
+
+static int __init atc260x_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&atc260x_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register atc260x I2C driver: %d\n", ret);
+	return ret;
+}
+subsys_initcall(atc260x_i2c_init);
+
+static void __exit atc260x_i2c_exit(void)
+{
+	i2c_del_driver(&atc260x_i2c_driver);
+}
+module_exit(atc260x_i2c_exit);
+
+MODULE_DESCRIPTION("I2C support for atc260x PMICs");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Actions Semi.");
diff --git a/drivers/mfd/atc260x-pm.c b/drivers/mfd/atc260x-pm.c
new file mode 100755
index 0000000..7d982305
--- /dev/null
+++ b/drivers/mfd/atc260x-pm.c
@@ -0,0 +1,1510 @@
+/*
+ * atc260x_pm.c  --  ATC260X power management (suspend to ram) support.
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+
+#include <asm/suspend.h>
+#include <mach/hardware.h>
+#include <mach/power.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+/* ATC2603A_PMU_SYS_CTL1 register bits */
+#define PMU_SYS_CTL1_EN_S1              (1 << 0)
+#define PMU_SYS_CTL1_LB_S4_SHIFT        (3)
+#define PMU_SYS_CTL1_LB_S4_MASK                 (0x3 << PMU_SYS_CTL1_LB_S4_SHIFT)
+#define PMU_SYS_CTL1_LB_S4_3_1V         (0x2 << PMU_SYS_CTL1_LB_S4_SHIFT)/*3.1v 低电进S4电压 */
+#define PMU_SYS_CTL1_LB_S4_3_3V         (0x3 << PMU_SYS_CTL1_LB_S4_SHIFT)/*3.3v 低电进S4电压 */
+#define PMU_SYS_CTL1_WAKE_FLAG_SHIFT    (5)
+
+/* ATC2603A_PMU_SYS_CTL2 register bits */
+#define PMU_SYS_CTL2_S2_TIMER_SHIFT     (3)
+#define PMU_SYS_CTL2_S2_TIMER_MASK      (0x7 << PMU_SYS_CTL2_S2_TIMER_SHIFT)
+#define PMU_SYS_CTL2_S2_TIMER_EN        (1 << 6)
+
+/* ATC2603A_PMU_SYS_CTL3 register bits */
+#define PMU_SYS_CTL3_S3_TIMER_SHIFT     (10)
+#define PMU_SYS_CTL3_S3_TIMER_MASK      (0x7 << PMU_SYS_CTL3_S3_TIMER_SHIFT)
+#define PMU_SYS_CTL3_S3_TIMER_EN        (1 << 13)
+#define PMU_SYS_CTL3_EN_S3              (1 << 14)
+#define PMU_SYS_CTL3_EN_S2              (1 << 15)
+
+/* ATC2603C_PMU_SYS_CTL3 register bits */
+#define ATC2603C_S2S3TOS1_TIMER_EN      (1 << 9)
+
+/* ATC2603A_PMU_SYS_CTL5 register bits */
+#define PMU_SYS_CTL5_DETECT_MASK        (0xf << 7)
+
+#define PMU_SYS_CTL5_WALLWKDTEN         (1 << 7)
+#define PMU_SYS_CTL5_VBUSWKDTEN         (1 << 8)
+#define PMU_SYS_CTL5_REMCON_DECT_EN     (1 << 9)
+#define PMU_SYS_CTL5_TP_DECT_EN         (1 << 10)
+
+
+#define to_atc260x_pm_attr(_attr) \
+	container_of(_attr, struct atc260x_pm_attribute, attr)
+
+/*s2 mode,=0 normal mode, =1 low temp mode; */
+static u32 s2_mode;
+
+/*in minutes. */
+static const unsigned long s2tos3_timeout_table[8] = {
+	6, 16, 31, 61, 91, 121, 151, 181
+};
+/*in minutes. */
+static const unsigned long s3tos4_timeout_table[8] = {
+	6, 16, 31, 61, 91, 121, 151, 181
+};
+
+struct atc260x_pm_alarm_save {
+	u16 msalm;
+	u16 halm;
+	u16 ymdalm;
+};
+
+struct atc260x_pm_dev {
+	struct device *dev;
+	struct atc260x_dev  *atc260x;
+	struct notifier_block pm_nb;
+	uint pmic_type;
+	uint pmic_ver;
+	uint active_wakeup_srcs;
+};
+
+struct atc260x_pm_attribute{
+	struct kobj_attribute attr;
+	int index;
+};
+
+static struct atc260x_pm_dev *s_current_atc260x_pm_obj = NULL;
+
+static struct atc260x_pm_dev * _get_curr_atc260x_pm_obj(void)
+{
+	struct atc260x_pm_dev *atc260x_pm = s_current_atc260x_pm_obj;
+	if (atc260x_pm == NULL) {
+		pr_err("%s() atc260x_pm not registered!\n", __func__);
+		BUG();
+	}
+	return atc260x_pm;
+}
+
+/* ---------- sys pm interface ---------------------------------------------- */
+
+struct atc260x_pm_wakeup_src_reg_desc {
+	u16 regs[4];
+	u8  bit_tbl[OWL_PMIC_WAKEUP_SRC_CNT][2];
+};
+
+static const struct atc260x_pm_wakeup_src_reg_desc sc_atc2603a_pm_wakeup_src_desc = {
+	.regs = {ATC2603A_PMU_SYS_CTL0, ATC2603A_PMU_SYS_CTL1, 0xffff, 0xffff},
+	.bit_tbl = {
+		/*  w,   r    */
+		{   5,   16+5,  }, /* OWL_PMIC_WAKEUP_SRC_IR */
+		{   6,   16+6,  }, /* OWL_PMIC_WAKEUP_SRC_RESET */
+		{   7,   16+7,  }, /* OWL_PMIC_WAKEUP_SRC_HDSW */
+		{   8,   16+8,  }, /* OWL_PMIC_WAKEUP_SRC_ALARM */
+		{   9,   16+9,  }, /* OWL_PMIC_WAKEUP_SRC_REMCON */
+		{   10,  16+10, }, /* OWL_PMIC_WAKEUP_SRC_TP */
+		{   11,  16+11, }, /* OWL_PMIC_WAKEUP_SRC_WKIRQ */
+		{   12,  16+12, }, /* OWL_PMIC_WAKEUP_SRC_ONOFF_SHORT */
+		{   13,  16+13, }, /* OWL_PMIC_WAKEUP_SRC_ONOFF_LONG */
+		{   14,  16+14, }, /* OWL_PMIC_WAKEUP_SRC_WALL_IN */
+		{   15,  16+15, }, /* OWL_PMIC_WAKEUP_SRC_VBUS_IN */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_RESTART */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_SGPIOIRQ */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_WALL_OUT */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_VBUS_OUT */
+	}
+};
+static const struct atc260x_pm_wakeup_src_reg_desc sc_atc2603c_pm_wakeup_src_desc = {
+	.regs = {ATC2603C_PMU_SYS_CTL0, ATC2603C_PMU_SYS_CTL1, ATC2603C_PMU_SYS_CTL3, 0xffff},
+	.bit_tbl = {
+		/*  w,   r    */
+		{   5,   16+5,  }, /* OWL_PMIC_WAKEUP_SRC_IR */
+		{   6,   16+6,  }, /* OWL_PMIC_WAKEUP_SRC_RESET */
+		{   7,   16+7,  }, /* OWL_PMIC_WAKEUP_SRC_HDSW */
+		{   8,   16+8,  }, /* OWL_PMIC_WAKEUP_SRC_ALARM */
+		{   9,   16+9,  }, /* OWL_PMIC_WAKEUP_SRC_REMCON */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_TP */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_WKIRQ */
+		{   12,  16+12, }, /* OWL_PMIC_WAKEUP_SRC_ONOFF_SHORT */
+		{   13,  16+13, }, /* OWL_PMIC_WAKEUP_SRC_ONOFF_LONG */
+		{   14,  16+14, }, /* OWL_PMIC_WAKEUP_SRC_WALL_IN */
+		{   15,  16+15, }, /* OWL_PMIC_WAKEUP_SRC_VBUS_IN */
+		{   10,  16+10, }, /* OWL_PMIC_WAKEUP_SRC_RESTART */
+		{   11,  16+11, }, /* OWL_PMIC_WAKEUP_SRC_SGPIOIRQ */
+		{ 32+2,  32+0,  }, /* OWL_PMIC_WAKEUP_SRC_WALL_OUT */ /* same as WALL_IN */
+		{ 32+3,  32+1,  }, /* OWL_PMIC_WAKEUP_SRC_VBUS_OUT */ /* same as VBUS_IN */
+	}
+};
+static const struct atc260x_pm_wakeup_src_reg_desc sc_atc2609a_pm_wakeup_src_desc = {
+	.regs = {ATC2609A_PMU_SYS_CTL0, ATC2609A_PMU_SYS_CTL1, 0xffff, 0xffff},
+	.bit_tbl = {
+		/*  w,   r    */
+		{   5,   16+5,  }, /* OWL_PMIC_WAKEUP_SRC_IR */
+		{   6,   16+6,  }, /* OWL_PMIC_WAKEUP_SRC_RESET */
+		{   7,   16+7,  }, /* OWL_PMIC_WAKEUP_SRC_HDSW */
+		{   8,   16+8,  }, /* OWL_PMIC_WAKEUP_SRC_ALARM */
+		{   9,   16+9,  }, /* OWL_PMIC_WAKEUP_SRC_REMCON */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_TP */
+		{   11,  16+11, }, /* OWL_PMIC_WAKEUP_SRC_WKIRQ */
+		{   12,  16+12, }, /* OWL_PMIC_WAKEUP_SRC_ONOFF_SHORT */
+		{   13,  16+13, }, /* OWL_PMIC_WAKEUP_SRC_ONOFF_LONG */
+		{   14,  16+14, }, /* OWL_PMIC_WAKEUP_SRC_WALL_IN */
+		{   15,  16+15, }, /* OWL_PMIC_WAKEUP_SRC_VBUS_IN */
+		{   10,  16+10, }, /* OWL_PMIC_WAKEUP_SRC_RESTART */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_SGPIOIRQ */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_WALL_OUT */
+		{   255, 255,   }, /* OWL_PMIC_WAKEUP_SRC_VBUS_OUT */
+	}
+};
+static const struct atc260x_pm_wakeup_src_reg_desc * const sc_atc260x_pm_wakeup_src_desc_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = &sc_atc2603a_pm_wakeup_src_desc,
+	[ATC260X_ICTYPE_2603C] = &sc_atc2603c_pm_wakeup_src_desc,
+	[ATC260X_ICTYPE_2609A] = &sc_atc2609a_pm_wakeup_src_desc,
+};
+
+static const u16 sc_atc260x_pm_regtbl_sysctl0[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_PMU_SYS_CTL0,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_PMU_SYS_CTL0,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_PMU_SYS_CTL0,
+};
+static const u16 sc_atc260x_pm_regtbl_sysctl1[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_PMU_SYS_CTL1,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_PMU_SYS_CTL1,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_PMU_SYS_CTL1,
+};
+static const u16 sc_atc260x_pm_regtbl_sysctl2[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_PMU_SYS_CTL2,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_PMU_SYS_CTL2,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_PMU_SYS_CTL2,
+};
+static const u16 sc_atc260x_pm_regtbl_sysctl3[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_PMU_SYS_CTL3,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_PMU_SYS_CTL3,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_PMU_SYS_CTL3,
+};
+static const u16 sc_atc260x_pm_regtbl_sysctl5[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_PMU_SYS_CTL5,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_PMU_SYS_CTL5,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_PMU_SYS_CTL5,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_ms[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_MS,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_MS,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_MS,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_h[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_H,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_H,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_H,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_ymd[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_YMD,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_YMD,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_YMD,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_dc[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_DC,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_DC,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_DC,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_msalm[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_MSALM,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_MSALM,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_MSALM,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_halm[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_HALM,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_HALM,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_HALM,
+};
+static const u16 sc_atc260x_pm_regtbl_rtc_ymdalm[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ATC2603A_RTC_YMDALM,
+	[ATC260X_ICTYPE_2603C] = ATC2603C_RTC_YMDALM,
+	[ATC260X_ICTYPE_2609A] = ATC2609A_RTC_YMDALM,
+};
+
+
+static int _atc260x_pm_set_wakeup_src_inner(struct atc260x_pm_dev *atc260x_pm,
+		uint wakeup_mask, uint wakeup_src)
+{
+	const struct atc260x_pm_wakeup_src_reg_desc *wakeup_src_desc;
+	uint i, reg_addr, reg_val, reg_mask, tmp_mask;
+	u16 reg_vals[4], reg_masks[4];
+	int ret;
+
+	dev_info(atc260x_pm->dev, "%s() enter, mask=0x%x src=0x%x\n",
+		__func__, wakeup_mask, wakeup_src);
+
+	if ((wakeup_mask & ~OWL_PMIC_WAKEUP_SRC_ALL) ||
+		(wakeup_src & ~OWL_PMIC_WAKEUP_SRC_ALL) ||
+		(wakeup_src & ~wakeup_mask)) {
+		dev_err(atc260x_pm->dev, "%s() invalid wakeup source\n", __func__);
+		return -EINVAL;
+	}
+	if (wakeup_mask == 0)
+		return 0;
+
+	/* update wakeup source EN bit(s) */
+	memset(reg_vals, 0, sizeof(reg_vals));
+	memset(reg_masks, 0, sizeof(reg_masks));
+	wakeup_src_desc = sc_atc260x_pm_wakeup_src_desc_tbl[atc260x_pm->pmic_type];
+	tmp_mask = wakeup_mask;
+	while (tmp_mask) {
+		uint src_index, comb_bit, reg_index, reg_bit;
+		uint tmp_mask1 = tmp_mask & ~(tmp_mask -1U); /* 最低为1的位. */
+		tmp_mask &= ~tmp_mask1;
+		src_index = __ffs(tmp_mask1);
+		BUG_ON(src_index >= OWL_PMIC_WAKEUP_SRC_CNT);
+
+		comb_bit = wakeup_src_desc->bit_tbl[src_index][0];
+		if (comb_bit == 255) {
+			if (wakeup_src & (1U<<src_index)) {
+				dev_warn(atc260x_pm->dev, "%s() src %u (mask 0x%x) not support\n",
+					__func__, src_index, (1U<<src_index));
+			}
+			continue;
+		}
+		reg_index = comb_bit / 16U;
+		reg_bit = comb_bit % 16U;
+		BUG_ON(reg_index >= ARRAY_SIZE(reg_masks));
+
+		reg_masks[reg_index] |= 1U << reg_bit;
+		if (wakeup_src & (1U<<src_index)) {
+			reg_vals[reg_index] |= 1U << reg_bit;
+		}
+	}
+	for (i = 0; i < ARRAY_SIZE(reg_masks); i++) {
+		reg_addr = wakeup_src_desc->regs[i];
+		if (reg_masks[i] != 0 && reg_addr != 0xffff) {
+			ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+					reg_addr, reg_masks[i], reg_vals[i]);
+			if (ret) {
+				dev_err(atc260x_pm->dev, "%s() io err, ret=%d\n", __func__, ret);
+				return -EIO;
+			}
+			dev_info(atc260x_pm->dev, "%s() setup reg=0x%x msk=0x%x val=0x%x readback=0x%x\n",
+					__func__, reg_addr, reg_masks[i], reg_vals[i],
+					atc260x_reg_read(atc260x_pm->atc260x, reg_addr));
+		}
+	}
+
+	/* set detect enable bit(s) */
+	reg_val = reg_mask = 0;
+	switch (atc260x_pm->pmic_type) {
+	case ATC260X_ICTYPE_2603A:
+		if (wakeup_mask & OWL_PMIC_WAKEUP_SRC_TP) {
+			reg_mask |= PMU_SYS_CTL5_TP_DECT_EN;
+			if (wakeup_src & OWL_PMIC_WAKEUP_SRC_TP){
+				reg_val |= PMU_SYS_CTL5_TP_DECT_EN;
+				/* must eable TEN if TP as wakeup source */
+			}
+		}
+
+		if (wakeup_mask & OWL_PMIC_WAKEUP_SRC_REMCON) {
+			reg_mask |= PMU_SYS_CTL5_REMCON_DECT_EN;
+			if (wakeup_src & OWL_PMIC_WAKEUP_SRC_REMCON)
+				reg_val |= PMU_SYS_CTL5_REMCON_DECT_EN;
+		}
+
+		/* for re-plugin wakeup, not disable wakeup detect for wall/usb cable */
+		#if 0
+		if (wakeup_mask & OWL_PMIC_WAKEUP_SRC_WALL_IN) {
+			reg_mask |= PMU_SYS_CTL5_WALLWKDTEN;
+			if (wakeup_src & OWL_PMIC_WAKEUP_SRC_WALL_IN)
+				reg_val |= PMU_SYS_CTL5_WALLWKDTEN;
+		}
+
+		if (wakeup_mask & OWL_PMIC_WAKEUP_SRC_VBUS_IN) {
+			reg_mask |= PMU_SYS_CTL5_VBUSWKDTEN;
+			if (wakeup_src & OWL_PMIC_WAKEUP_SRC_VBUS_IN)
+				reg_val |= PMU_SYS_CTL5_VBUSWKDTEN;
+		}
+		#endif
+		atc260x_reg_setbits(atc260x_pm->atc260x,
+			ATC2603A_PMU_SYS_CTL5, reg_mask, reg_val);
+		break;
+
+	case ATC260X_ICTYPE_2603C:
+		if (wakeup_mask & OWL_PMIC_WAKEUP_SRC_REMCON) {
+			reg_mask |= PMU_SYS_CTL5_REMCON_DECT_EN;
+			if (wakeup_src & OWL_PMIC_WAKEUP_SRC_REMCON)
+				reg_val |= PMU_SYS_CTL5_REMCON_DECT_EN;
+		}
+		atc260x_reg_setbits(atc260x_pm->atc260x,
+			ATC2603C_PMU_SYS_CTL5, reg_mask, reg_val);
+
+	case ATC260X_ICTYPE_2609A:
+		if (wakeup_mask & OWL_PMIC_WAKEUP_SRC_REMCON) {
+			reg_mask |= PMU_SYS_CTL5_REMCON_DECT_EN;
+			if (wakeup_src & OWL_PMIC_WAKEUP_SRC_REMCON)
+				reg_val |= PMU_SYS_CTL5_REMCON_DECT_EN;
+		}
+		atc260x_reg_setbits(atc260x_pm->atc260x,
+			ATC2609A_PMU_SYS_CTL5, reg_mask, reg_val);
+	}
+
+	return 0;
+}
+
+static int _atc260x_pm_get_wakeup_src_inner(struct atc260x_pm_dev *atc260x_pm, bool get_flag)
+{
+	const struct atc260x_pm_wakeup_src_reg_desc *wakeup_src_desc;
+	uint i, comb_bit, reg_index, reg_bit, reg_addr, wakeup_src_bm;
+	u16 reg_vals[4];
+	int ret;
+
+	get_flag = !!get_flag;
+	wakeup_src_desc = sc_atc260x_pm_wakeup_src_desc_tbl[atc260x_pm->pmic_type];
+
+	for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
+		reg_addr = wakeup_src_desc->regs[i];
+		if (reg_addr != 0xffff) {
+			ret = atc260x_reg_read(atc260x_pm->atc260x, wakeup_src_desc->regs[i]);
+			if (ret < 0) {
+				dev_err(atc260x_pm->dev, "%s() io err, ret=%d\n", __func__, ret);
+				return ret;
+			}
+			reg_vals[i] = ret;
+		} else {
+			reg_vals[i] = 0;
+		}
+	}
+
+	wakeup_src_bm = 0;
+	for (i = 0; i < OWL_PMIC_WAKEUP_SRC_CNT; i++) {
+		comb_bit = wakeup_src_desc->bit_tbl[i][get_flag];
+		if (comb_bit == 255) /* not support ? */
+			continue;
+		reg_index = comb_bit / 16U;
+		reg_bit = comb_bit % 16U;
+		BUG_ON(reg_index >= ARRAY_SIZE(reg_vals));
+		if (reg_vals[reg_index] & (1U << reg_bit)) {
+			wakeup_src_bm |= 1U << i;
+		}
+	}
+
+	return wakeup_src_bm;
+}
+
+static int _atc260x_pm_set_wakeup_src(uint wakeup_mask, uint wakeup_src)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	return _atc260x_pm_set_wakeup_src_inner(atc260x_pm, wakeup_mask, wakeup_src);
+}
+
+static int _atc260x_pm_get_wakeup_src(void)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	return _atc260x_pm_get_wakeup_src_inner(atc260x_pm, 0);
+}
+
+static int _atc260x_pm_get_wakeup_flag_inner(struct atc260x_pm_dev *atc260x_pm)
+{
+	int ret;
+
+	ret = _atc260x_pm_get_wakeup_src_inner(atc260x_pm, 1);
+	if (ret < 0)
+		return ret;
+	atc260x_pm->active_wakeup_srcs = ret;
+	dev_info(atc260x_pm->dev, "translated wakeup falgs: 0x%x\n", ret);
+	return 0;
+}
+
+static int _atc260x_pm_get_wakeup_flag(void)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	return atc260x_pm->active_wakeup_srcs;
+}
+
+static int _atc260x_pm_setup_rtc_alarm(struct atc260x_pm_dev *atc260x_pm,
+		uint seconds, uint safe_margin,
+		struct atc260x_pm_alarm_save *p_old_alarm)
+{
+	struct rtc_time rtc_tmp_tm;
+	uint reg_rtc_ms, reg_rtc_h, reg_rtc_ymd, reg_rtc_dc;
+	uint reg_rtc_msalm, reg_rtc_halm, reg_rtc_ymdalm;
+	uint rtc_ms, rtc_h, rtc_ymd, rtc_cen, rtc_msalm, rtc_halm, rtc_ymdalm;
+	ulong rtc_times, rtc_alm_times;
+	int ret1, ret2, ret3, ret4, ret5, ret6, ret7;
+
+	dev_info(atc260x_pm->dev, "%s() enter, seconds=%u safe_margin=%u\n",
+		__func__, seconds, safe_margin);
+
+	reg_rtc_ms      = sc_atc260x_pm_regtbl_rtc_ms[atc260x_pm->pmic_type];
+	reg_rtc_h       = sc_atc260x_pm_regtbl_rtc_h[atc260x_pm->pmic_type];
+	reg_rtc_ymd     = sc_atc260x_pm_regtbl_rtc_ymd[atc260x_pm->pmic_type];
+	reg_rtc_dc      = sc_atc260x_pm_regtbl_rtc_dc[atc260x_pm->pmic_type];
+	reg_rtc_msalm   = sc_atc260x_pm_regtbl_rtc_msalm[atc260x_pm->pmic_type];
+	reg_rtc_halm    = sc_atc260x_pm_regtbl_rtc_halm[atc260x_pm->pmic_type];
+	reg_rtc_ymdalm  = sc_atc260x_pm_regtbl_rtc_ymdalm[atc260x_pm->pmic_type];
+
+	ret1 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_ms);
+	ret2 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_h);
+	ret3 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_ymd);
+	ret4 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_dc);
+	ret5 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_msalm);
+	ret6 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_halm);
+	ret7 = atc260x_reg_read(atc260x_pm->atc260x, reg_rtc_ymdalm);
+	if (ret1 < 0 || ret2 < 0 || ret3 < 0 || ret4 < 0 || ret5 < 0 || ret6 < 0 || ret7 < 0) {
+		dev_err(atc260x_pm->dev, "%s() read IO err\n", __func__);
+		return -EIO;
+	}
+	rtc_ms  = (uint)ret1 & 0xfffU;
+	rtc_h   = (uint)ret2 & 0x1fU;
+	rtc_ymd = (uint)ret3;
+	rtc_cen = (uint)ret4 & 0x7fU;
+	rtc_msalm  = (uint)ret5 & 0xfffU;
+	rtc_halm   = (uint)ret6 & 0x1fU;
+	rtc_ymdalm = (uint)ret7;
+
+	rtc_times = mktime(
+			rtc_cen * 100 + ((rtc_ymd >> 9) & 0x7fU),
+			(rtc_ymd >> 5) & 0xfU,
+			rtc_ymd & 0x1fU,
+			rtc_h,
+			(rtc_ms >> 6) & 0x3fU,
+			rtc_ms & 0x3fU);
+	rtc_alm_times = mktime(
+			rtc_cen * 100 + ((rtc_ymdalm >> 9) & 0x7fU),
+			(rtc_ymdalm >> 5) & 0xfU,
+			rtc_ymdalm & 0x1fU,
+			rtc_halm,
+			(rtc_msalm >> 6) & 0x3fU,
+			rtc_msalm & 0x3fU);
+
+	/* save old alarm if needed. */
+	if (rtc_times < rtc_alm_times) {
+		/* Alarm already activated. */
+		if ((rtc_alm_times - rtc_times) <= safe_margin) {
+			dev_warn(atc260x_pm->dev, "%s() old alarm will be postponed (+ ~%us)\n",
+				__func__, safe_margin);
+			rtc_alm_times = rtc_times + safe_margin;
+		}
+		/* save old alarm */
+		rtc_time_to_tm(rtc_alm_times, &rtc_tmp_tm);
+		p_old_alarm->msalm =
+			((rtc_tmp_tm.tm_min) << 6) |
+			rtc_tmp_tm.tm_sec;
+		p_old_alarm->halm =
+			rtc_tmp_tm.tm_hour;
+		p_old_alarm->ymdalm =
+			((rtc_tmp_tm.tm_year+1900 - rtc_cen*100) << 9) |
+			((rtc_tmp_tm.tm_mon +1) << 5) |
+			rtc_tmp_tm.tm_mday;
+		dev_info(atc260x_pm->dev, "%s() saved old alarm %u-%u-%u %u:%u:%u, "
+			"msalm=0x%x halm=0x%x ymdalm=0x%x\n",
+			__func__,
+			rtc_tmp_tm.tm_year+1900, rtc_tmp_tm.tm_mon+1, rtc_tmp_tm.tm_mday,
+			rtc_tmp_tm.tm_hour, rtc_tmp_tm.tm_min, rtc_tmp_tm.tm_sec,
+			p_old_alarm->msalm, p_old_alarm->halm, p_old_alarm->ymdalm);
+	} else {
+		dev_info(atc260x_pm->dev, "%s() no need to save old alarm\n", __func__);
+		memset(p_old_alarm, 0, sizeof(*p_old_alarm));
+	}
+
+	/* set new alarm */
+	rtc_alm_times = rtc_times + seconds;
+	rtc_time_to_tm(rtc_alm_times, &rtc_tmp_tm);
+	rtc_msalm =
+		((rtc_tmp_tm.tm_min) << 6) |
+		rtc_tmp_tm.tm_sec;
+	rtc_halm =
+		rtc_tmp_tm.tm_hour;
+	rtc_ymdalm =
+		((rtc_tmp_tm.tm_year+1900 - rtc_cen*100) << 9) |
+		((rtc_tmp_tm.tm_mon+1) << 5) |
+		rtc_tmp_tm.tm_mday;
+	ret5 = atc260x_reg_write(atc260x_pm->atc260x, reg_rtc_msalm, rtc_msalm);
+	ret6 = atc260x_reg_write(atc260x_pm->atc260x, reg_rtc_halm, rtc_halm);
+	ret7 = atc260x_reg_write(atc260x_pm->atc260x, reg_rtc_ymdalm, rtc_ymdalm);
+	if (ret5 || ret6 || ret7) {
+		dev_err(atc260x_pm->dev, "%s() write IO err\n", __func__);
+		return -EIO;
+	}
+	dev_info(atc260x_pm->dev, "%s() new alarm set to %u-%u-%u %u:%u:%u, "
+		"msalm=0x%x halm=0x%x ymdalm=0x%x\n",
+		__func__,
+		rtc_tmp_tm.tm_year+1900, rtc_tmp_tm.tm_mon+1, rtc_tmp_tm.tm_mday,
+		rtc_tmp_tm.tm_hour, rtc_tmp_tm.tm_min, rtc_tmp_tm.tm_sec,
+		rtc_msalm, rtc_halm, rtc_ymdalm);
+
+	dev_info(atc260x_pm->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+static int _atc260x_pm_prepare_suspend(void)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+
+	dev_info(atc260x_pm->dev, "%s() enter\n", __func__);
+
+	/* avoid using standard SPI/I2C interface that maybe halt in suspend process */
+	atc260x_set_reg_direct_access(atc260x_pm->atc260x, true);
+
+	dev_info(atc260x_pm->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+static int _set_s2_mode(struct atc260x_pm_dev *atc260x_pm)
+{
+	struct device_node *of_parent_node;
+	int ret;
+
+	of_parent_node = of_get_parent(atc260x_pm->dev->of_node);
+	if (of_parent_node == NULL) {
+		dev_err(atc260x_pm->dev, "%s() can not get parent of_nade\n", __func__);
+		return -ENODEV;
+	}
+	
+	ret = of_property_read_u32(of_parent_node, "s2_mode", &s2_mode);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() can not get of_property s2_mode \n", __func__);
+		return -ENODEV;
+	}
+	dev_info(atc260x_pm->dev, "got s2_mode: %u\n", s2_mode);
+
+	return 0;	
+}
+
+/* only prepare the env, not disable S1 immediately */
+static int _atc260x_pm_enter_suspend(void)
+{
+	struct atc260x_pm_dev *atc260x_pm;
+	struct atc260x_dev *atc260x;
+	void (*p_cpu_resume)(void);
+	uint reg_sysctl2, reg_sysctl3;
+	uint reg_sysctl3_val;
+	phys_addr_t resume_phy_address;
+	int i, ret;
+
+	atc260x_pm = _get_curr_atc260x_pm_obj();
+	atc260x = atc260x_pm->atc260x;
+
+	dev_info(atc260x_pm->dev, "%s() enter\n", __func__);
+
+	reg_sysctl2 = sc_atc260x_pm_regtbl_sysctl2[atc260x_pm->pmic_type];
+	reg_sysctl3 = sc_atc260x_pm_regtbl_sysctl3[atc260x_pm->pmic_type];
+
+	reg_sysctl3_val = PMU_SYS_CTL3_EN_S2;
+
+	/* check S2->S3 timer */
+	ret = atc260x_reg_read(atc260x, reg_sysctl2);
+	if (ret < 0) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		return ret;
+	}
+	if ((uint)ret & PMU_SYS_CTL2_S2_TIMER_EN) {
+		reg_sysctl3_val |= PMU_SYS_CTL3_EN_S3;
+	}
+
+	/* save resume address to pstore */
+	p_cpu_resume = symbol_get(owl_cpu_resume);
+	if (!p_cpu_resume || IS_ERR(p_cpu_resume))
+	{
+		dev_err(atc260x_pm->dev, "%s() symbol owl_cpu_resume not found", __func__);
+		return -EINVAL;
+	}
+	resume_phy_address = (phys_addr_t)virt_to_phys(p_cpu_resume);
+	dev_info(atc260x_pm->dev, "%s() owl_cpu_resume @ 0x%lx (phy 0x%lx)\n",
+		__func__, (ulong)p_cpu_resume, (ulong)resume_phy_address);
+		
+	if(s2_mode)
+		resume_phy_address |= (1UL << (sizeof(resume_phy_address)*8-1)); /* set highest bit */
+	
+	ret = atc260x_pstore_set(atc260x,
+		ATC260X_PSTORE_TAG_RESUME_ADDR, resume_phy_address);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		return ret;
+	}
+	#ifdef CONFIG_64BIT
+	ret = atc260x_pstore_set(atc260x,
+		ATC260X_PSTORE_TAG_RESUME_ADDR_H, resume_phy_address >> 32);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		return ret;
+	}
+	#endif
+
+	/* set FW suspend flag */
+	ret = atc260x_pstore_set(atc260x_pm->atc260x, ATC260X_PSTORE_TAG_FW_S2, 1);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		return ret;
+	}
+
+	if(s2_mode) {
+		/* suspend 分两个阶段进行:
+		 * 1.是kernel这边初进S2,
+		 * 2.是alarm唤醒进u-boot, u-boot做一些省电配置, 再进S2. */
+
+		/* set phase #1 wakeup alarm */
+		
+		ret = _atc260x_pm_setup_rtc_alarm(atc260x_pm, 5, 15,
+				(struct atc260x_pm_alarm_save *)0xc0000400);
+		if (ret) {
+			dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+			return ret;
+		}
+	}
+
+	/* set S2 S3  */
+	for (i = 0; i < 10; i++) {
+		ret = atc260x_reg_setbits(atc260x, reg_sysctl3,
+				PMU_SYS_CTL3_EN_S2 | PMU_SYS_CTL3_EN_S3, reg_sysctl3_val);
+		if (ret == 0) {
+			break;
+		}
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		mdelay(1);
+	}
+	if (ret) {
+		return ret;
+	}
+
+	/* clean old wakeup_srcs record. */
+	atc260x_pm->active_wakeup_srcs = 0;
+
+	dev_info(atc260x_pm->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+static int _atc260x_pm_wakeup(void)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	int ret;
+
+	dev_info(atc260x_pm->dev, "%s() enter\n", __func__);
+
+	atc260x_set_reg_direct_access(atc260x_pm->atc260x, true);
+
+	/* get get_wakeup_flag */
+	ret = _atc260x_pm_get_wakeup_flag_inner(atc260x_pm);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() get_wakeup_flag failed\n", __func__);
+	}
+
+	/* clear FW suspend flag */
+	ret = atc260x_pstore_set(atc260x_pm->atc260x, ATC260X_PSTORE_TAG_FW_S2, 0);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() pstore access failed\n", __func__);
+	}
+
+	dev_info(atc260x_pm->dev, "%s() exit\n", __func__);
+	return 0;
+}
+
+static int _atc260x_pm_finsh_wakeup(void)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	dev_info(atc260x_pm->dev, "%s() enter\n", __func__);
+	atc260x_set_reg_direct_access(atc260x_pm->atc260x, false);
+	return 0;
+}
+
+static int _atc260x_pm_prepare_powerdown(void)
+{
+	/* switch to direct access,
+	 * avoid using standard SPI/I2C interface that maybe halt in pwrdwn process */
+	atc260x_set_reg_direct_access(_get_curr_atc260x_pm_obj()->atc260x, true);
+	return 0;
+}
+
+static int _atc260x_pm_powerdown(uint deep_pwrdn, uint for_upgrade)
+{
+	struct atc260x_pm_dev *atc260x_pm;
+	struct atc260x_dev *atc260x;
+	uint reg_sysctl1, reg_sysctl3, reg_mask, reg_val;
+	int ret;
+
+	atc260x_pm = _get_curr_atc260x_pm_obj();
+	atc260x = atc260x_pm->atc260x;
+
+	dev_info(atc260x_pm->dev, "%s() enter, deep_pwrdn=%u for_upgrade=%u\n",
+		__func__, deep_pwrdn, for_upgrade);
+
+	if (for_upgrade) {
+		/* clear pstore */
+		ret = atc260x_pstore_reset_all(atc260x);
+		if (ret) {
+			dev_err(atc260x_pm->dev, "%s() failed to reset pstore\n", __func__);
+		}
+	}
+
+	reg_sysctl1 = sc_atc260x_pm_regtbl_sysctl1[atc260x_pm->pmic_type];
+	reg_sysctl3 = sc_atc260x_pm_regtbl_sysctl3[atc260x_pm->pmic_type];
+
+	reg_mask = PMU_SYS_CTL3_EN_S2 | PMU_SYS_CTL3_EN_S3;
+	reg_val = deep_pwrdn ? 0 : PMU_SYS_CTL3_EN_S3;
+	ret = atc260x_reg_setbits(atc260x, reg_sysctl3, reg_mask, reg_val);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		return ret;
+	}
+
+	pr_alert("%s() : powerdown.......................\n", __func__);
+
+	ret = atc260x_reg_setbits(atc260x, reg_sysctl1, PMU_SYS_CTL1_EN_S1, 0);
+	while (ret == 0) { /* wait for powerdown if success. */
+		mdelay(200); /* DO NOT use msleep() here! non-schedulable contex! */
+		dev_warn(atc260x_pm->dev, "%s() wait for powerdown...\n", __func__);
+	}
+	return ret;
+}
+
+static int _atc2603a_pm_do_reboot(struct atc260x_pm_dev *atc260x_pm)
+{
+	struct atc260x_pm_alarm_save old_alarm;
+	int ret, ret1, ret2;
+
+	/* 旧的5302没有软件触发的reset功能, 只能用PowerDown+Alarm来模拟. */
+
+	/* disable WALL/VBUS wakeup, enable alarm wakeup. */
+	ret = _atc260x_pm_set_wakeup_src_inner(atc260x_pm,
+		(OWL_PMIC_WAKEUP_SRC_WALL_IN | OWL_PMIC_WAKEUP_SRC_VBUS_IN |
+		OWL_PMIC_WAKEUP_SRC_ALARM),
+		OWL_PMIC_WAKEUP_SRC_ALARM);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() clear VBUS&WALL wakeup err, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	/* set alarm */
+	ret = _atc260x_pm_setup_rtc_alarm(atc260x_pm, 3, 8, &old_alarm);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() setup alarm err, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	/* backup old alarm data to pstore */
+	ret = atc260x_pstore_set(atc260x_pm->atc260x,
+		ATC260X_PSTORE_TAG_RTC_MSALM, old_alarm.msalm);
+	ret1 = atc260x_pstore_set(atc260x_pm->atc260x,
+		ATC260X_PSTORE_TAG_RTC_HALM, old_alarm.halm);
+	ret2 = atc260x_pstore_set(atc260x_pm->atc260x,
+		ATC260X_PSTORE_TAG_RTC_YMDALM, old_alarm.ymdalm);
+	if (ret || ret1 || ret2) {
+		dev_err(atc260x_pm->dev, "%s() faile to save old alarm\n", __func__);
+		return -EIO;
+	}
+
+	/* enter S3 */
+	ret = _atc260x_pm_powerdown(false, false);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() faile to powerdown, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+extern void owl_switch_jtag(void);
+
+static int _atc2603c_9a_pm_do_reboot(struct atc260x_pm_dev *atc260x_pm)
+{
+	/* 新的IC带软件触发的reset功能, 直接使用即可. */
+	struct atc260x_dev *atc260x;
+	uint reg_sysctl0, reg_sysctl3;
+	int ret;
+
+	atc260x = atc260x_pm->atc260x;
+
+	/* clear all wakeup source except on/off and p_reset */
+	ret = _atc260x_pm_set_wakeup_src_inner(atc260x_pm,
+		OWL_PMIC_WAKEUP_SRC_ALL,
+		OWL_PMIC_WAKEUP_SRC_ONOFF_LONG | OWL_PMIC_WAKEUP_SRC_RESET);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() clear setup wakeup err, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	/* set S3, clear S2 */
+	reg_sysctl3 = sc_atc260x_pm_regtbl_sysctl3[atc260x_pm->pmic_type];
+	ret = atc260x_reg_setbits(atc260x, reg_sysctl3,
+			PMU_SYS_CTL3_EN_S2 | PMU_SYS_CTL3_EN_S3, PMU_SYS_CTL3_EN_S3);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		return ret;
+	}
+
+	reg_sysctl0 = sc_atc260x_pm_regtbl_sysctl0[atc260x_pm->pmic_type];
+
+	owl_switch_jtag();
+	pr_alert("%s() : reboot.......................\n", __func__);
+
+	ret = atc260x_reg_setbits(atc260x, reg_sysctl0, (1U << 10), (1U << 10));
+	while (ret == 0) { /* wait for reboot if success. */
+		mdelay(200); /* DO NOT use msleep() here! non-schedulable contex! */
+		dev_warn(atc260x_pm->dev, "%s() wait for reboot...\n", __func__);
+	}
+	return ret;
+}
+
+static int _atc260x_pm_reboot(uint tgt)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	struct atc260x_dev *atc260x = atc260x_pm->atc260x;
+	uint flag_adfu, flag_recovery, flag_dis_mchrg;
+	int ret;
+
+	dev_info(atc260x_pm->dev, "%s() enter, tgt=%u\n", __func__, tgt);
+
+	flag_adfu = flag_recovery = flag_dis_mchrg = 0;
+	switch (tgt) {
+	case OWL_PMIC_REBOOT_TGT_NORMAL:
+		flag_adfu = flag_recovery = flag_dis_mchrg = 0;
+		break;
+	case OWL_PMIC_REBOOT_TGT_SYS:
+		flag_adfu = flag_recovery = 0;
+		flag_dis_mchrg = 1;
+		break;
+	case OWL_PMIC_REBOOT_TGT_ADFU:
+		flag_adfu = flag_dis_mchrg = 1;
+		flag_recovery = 0;
+		break;
+	case OWL_PMIC_REBOOT_TGT_RECOVERY:
+		flag_recovery = flag_dis_mchrg = 1;
+		flag_adfu = 0;
+		break;
+	case OWL_PMIC_REBOOT_TGT_BOOTLOADER:
+		flag_recovery = 2;
+		flag_dis_mchrg = 0;
+		flag_adfu = 0;
+		break;	
+	case OWL_PMIC_REBOOT_TGT_FASTBOOT:
+		flag_recovery = 3;
+		flag_dis_mchrg = 0;
+		flag_adfu = 0;
+		break;		
+		
+	}
+	ret = atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_REBOOT_ADFU, flag_adfu);
+	ret |= atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_REBOOT_RECOVERY, flag_recovery);
+	ret |= atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_DIS_MCHRG, flag_dis_mchrg);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() failed to update pstore\n", __func__);
+		return -EIO;
+	}
+
+	ret = -ENODEV;
+	switch (atc260x_pm->pmic_type) {
+	case ATC260X_ICTYPE_2603A:
+		ret = _atc2603a_pm_do_reboot(atc260x_pm);
+		break;
+	case ATC260X_ICTYPE_2603C:
+	case ATC260X_ICTYPE_2609A:
+		ret = _atc2603c_9a_pm_do_reboot(atc260x_pm);
+		break;
+	}
+	dev_err(atc260x_pm->dev, "%s() failed, ret=%d\n", __func__, ret);
+	return ret;
+}
+
+static int _atc260x_pm_get_bus_info(uint *bus_num, uint *addr, uint *ic_type)
+{
+	/* sys pm 那边进suspend的汇编代码要用到这些信息. */
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	struct atc260x_dev *atc260x = atc260x_pm->atc260x;
+	atc260x_get_bus_info(atc260x, bus_num, addr);
+	*ic_type = atc260x_pm->pmic_type;
+	return 0;
+}
+
+static struct owl_pmic_pm_ops atc260x_pm_pmic_ops = {
+	.set_wakeup_src = _atc260x_pm_set_wakeup_src,
+	.get_wakeup_src = _atc260x_pm_get_wakeup_src,
+	.get_wakeup_flag = _atc260x_pm_get_wakeup_flag,
+
+	.shutdown_prepare = _atc260x_pm_prepare_powerdown,
+	.powerdown = _atc260x_pm_powerdown,
+	.reboot = _atc260x_pm_reboot,
+
+	.suspend_prepare = _atc260x_pm_prepare_suspend,
+	.suspend_enter = _atc260x_pm_enter_suspend,
+	.suspend_wake = _atc260x_pm_wakeup,
+	.suspend_finish = _atc260x_pm_finsh_wakeup,
+
+	.get_bus_info = _atc260x_pm_get_bus_info,
+};
+
+/* ---------- sysfs interface ----------------------------------------------- */
+
+static ssize_t atc260x_wakeup_attr_shown(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int index = to_atc260x_pm_attr(attr)->index;
+	int wakeup_src;
+
+	pr_debug("%s %d: index %d\n", __FUNCTION__, __LINE__, index);
+
+	wakeup_src = _atc260x_pm_get_wakeup_src();
+
+	wakeup_src = (wakeup_src & (1 << index)) ? 1 : 0;
+
+	return sprintf(buf, "%d\n", wakeup_src);
+}
+
+static ssize_t atc260x_wakeup_attr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int index = to_atc260x_pm_attr(attr)->index;
+	unsigned long val = 0;
+	int ret;
+
+	pr_debug("%s %d: index %d\n", __FUNCTION__, __LINE__, index);
+
+	ret = kstrtoul(buf, 0, &val);
+	if (ret < 0)
+		return ret;
+
+	_atc260x_pm_set_wakeup_src(1 << index, (!!val) << index);
+
+	return count;
+}
+
+
+static ssize_t atc260x_wakeup_srcs_attr_shown(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	int wakeup_src;
+
+	pr_debug("%s %d:\n", __FUNCTION__, __LINE__);
+
+	wakeup_src = _atc260x_pm_get_wakeup_src();
+
+	return sprintf(buf, "0x%x\n", wakeup_src);
+}
+
+static ssize_t atc260x_wakeup_srcs_attr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	unsigned long wakeup_src = 0;
+	int ret;
+
+	pr_debug("%s %d:\n", __FUNCTION__, __LINE__);
+
+	ret = kstrtoul(buf, 0, &wakeup_src);
+	if (ret < 0)
+		return ret;
+
+	if (wakeup_src & ~OWL_PMIC_WAKEUP_SRC_ALL)
+		return -EINVAL;
+
+	_atc260x_pm_set_wakeup_src(OWL_PMIC_WAKEUP_SRC_ALL, wakeup_src);
+
+	return count;
+}
+
+static ssize_t atc260x_s2tos3_timeout_shown(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct atc260x_pm_dev *atc260x_pm;
+	int ret, i;
+	int len = 0;
+
+	atc260x_pm = _get_curr_atc260x_pm_obj();
+
+	pr_debug("%s %d:\n", __FUNCTION__, __LINE__);
+
+	ret = atc260x_reg_read(atc260x_pm->atc260x,
+			sc_atc260x_pm_regtbl_sysctl2[atc260x_pm->pmic_type]);
+
+	if (ret & PMU_SYS_CTL2_S2_TIMER_EN) {
+		i = (ret & PMU_SYS_CTL2_S2_TIMER_MASK) >> PMU_SYS_CTL2_S2_TIMER_SHIFT;
+		len = sprintf(buf, "[%lu] ", s2tos3_timeout_table[i]);
+	} else {
+		len = sprintf(buf, "[%d] ", 0);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(s2tos3_timeout_table); i++) {
+		len += sprintf(buf + len, "%lu ", s2tos3_timeout_table[i]);
+	}
+
+	len += sprintf(buf + len, " (min)\n");
+
+	return len;
+}
+
+static ssize_t atc260x_s2tos3_timeout_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *instr, size_t bytes)
+{
+	struct atc260x_pm_dev *atc260x_pm;
+	unsigned long timeout = 0;
+	int ret, i, tbl_size;
+
+	atc260x_pm = _get_curr_atc260x_pm_obj();
+
+	pr_debug("%s %d:\n", __FUNCTION__, __LINE__);
+
+	tbl_size = ARRAY_SIZE(s2tos3_timeout_table);
+
+	ret = kstrtoul(instr, 0, &timeout);
+	if (ret)
+		return ret;
+
+	if (timeout == 0) {
+		/* disable S2->S3 timer */
+		ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+			ATC2603A_PMU_SYS_CTL2,
+			PMU_SYS_CTL2_S2_TIMER_EN,
+			0);
+
+		return ret;
+	}
+
+	for (i = 0; i < tbl_size; i++) {
+		if (timeout == s2tos3_timeout_table[i])
+			break;
+	}
+
+	if (i == tbl_size)
+		return -EINVAL;
+
+	/* enable S2->S3 timer */
+	ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+		sc_atc260x_pm_regtbl_sysctl2[atc260x_pm->pmic_type],
+		PMU_SYS_CTL2_S2_TIMER_MASK | PMU_SYS_CTL2_S2_TIMER_EN,
+		(i << PMU_SYS_CTL2_S2_TIMER_SHIFT) | PMU_SYS_CTL2_S2_TIMER_EN);
+
+	atc260x_reg_read(atc260x_pm->atc260x,
+		sc_atc260x_pm_regtbl_sysctl2[atc260x_pm->pmic_type]);
+
+	return bytes;
+}
+
+
+static ssize_t atc260x_s3tos4_timeout_shown(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct atc260x_pm_dev *atc260x_pm;
+	int ret, i;
+	int len = 0;
+
+	atc260x_pm = _get_curr_atc260x_pm_obj();
+
+	pr_debug("%s %d:\n", __FUNCTION__, __LINE__);
+
+	ret = atc260x_reg_read(atc260x_pm->atc260x,
+			sc_atc260x_pm_regtbl_sysctl3[atc260x_pm->pmic_type]);
+
+	if (ret & PMU_SYS_CTL3_S3_TIMER_EN) {
+		i = (ret & PMU_SYS_CTL3_S3_TIMER_MASK) >> PMU_SYS_CTL3_S3_TIMER_SHIFT;
+		len = sprintf(buf, "[%lu] ", s2tos3_timeout_table[i]);
+	} else {
+		len = sprintf(buf, "[%d] ", 0);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(s3tos4_timeout_table); i++) {
+		len += sprintf(buf + len, "%lu ", s3tos4_timeout_table[i]);
+	}
+
+	len += sprintf(buf + len, " (min)\n");
+
+	return len;
+}
+
+static ssize_t atc260x_s3tos4_timeout_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *instr, size_t bytes)
+{
+	struct atc260x_pm_dev *atc260x_pm;
+	unsigned long timeout = 0;
+	int ret, i, tbl_size;
+
+	atc260x_pm = _get_curr_atc260x_pm_obj();
+
+	pr_debug("%s %d:\n", __FUNCTION__, __LINE__);
+
+	tbl_size = ARRAY_SIZE(s3tos4_timeout_table);
+
+	ret = kstrtoul(instr, 0, &timeout);
+	if (ret)
+		return ret;
+
+	if (timeout == 0) {
+		/* disable S3->S4 timer */
+		ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+			ATC2603A_PMU_SYS_CTL3,
+			PMU_SYS_CTL3_S3_TIMER_EN,
+			0);
+
+		return ret;
+	}
+
+	for (i = 0; i < tbl_size; i++) {
+		if (timeout == s3tos4_timeout_table[i])
+			break;
+	}
+
+	if (i == tbl_size)
+		return -EINVAL;
+
+	ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+		sc_atc260x_pm_regtbl_sysctl3[atc260x_pm->pmic_type],
+		PMU_SYS_CTL3_S3_TIMER_MASK | PMU_SYS_CTL3_S3_TIMER_EN,
+		(i << PMU_SYS_CTL3_S3_TIMER_SHIFT) | PMU_SYS_CTL3_S3_TIMER_EN);
+
+	return bytes;
+}
+
+#define ATC260X_PM_ATTR(_name, _mode, _show, _store, _index)    \
+	{ .attr = __ATTR(_name, _mode, _show, _store),  \
+	  .index = _index }
+
+#define WAKEUP_ATTR(_name, _index)  \
+static struct atc260x_pm_attribute atc260x_wakeup_attr_##_name      \
+	= ATC260X_PM_ATTR(_name, S_IRUGO | S_IWUSR, atc260x_wakeup_attr_shown, atc260x_wakeup_attr_store, _index)
+
+#define WAKEUP_ATTR_PTR(_name) \
+	&atc260x_wakeup_attr_##_name.attr.attr
+
+#define PM_ATTR(_name, _mode, _show, _store)    \
+static struct atc260x_pm_attribute atc260x_pm_attr_##_name      \
+	= ATC260X_PM_ATTR(_name, _mode, _show, _store, 0)
+
+#define PM_ATTR_PTR(_name)  \
+	&atc260x_pm_attr_##_name.attr.attr
+
+WAKEUP_ATTR(wken_ir, 0);
+WAKEUP_ATTR(wken_reset, 1);
+WAKEUP_ATTR(wken_hdsw, 2);
+WAKEUP_ATTR(wken_alarm, 3);
+WAKEUP_ATTR(wken_remcon, 4);
+WAKEUP_ATTR(wken_tp, 5);
+WAKEUP_ATTR(wken_wkirq, 6);
+WAKEUP_ATTR(wken_onoff_short, 7);
+WAKEUP_ATTR(wken_onoff_long, 8);
+WAKEUP_ATTR(wken_wall, 9);
+WAKEUP_ATTR(wken_vbus, 10);
+WAKEUP_ATTR(wken_restart, 11);
+WAKEUP_ATTR(wken_sgpio, 12);
+
+PM_ATTR(wakeup_srcs, S_IRUGO | S_IWUSR, atc260x_wakeup_srcs_attr_shown, \
+	atc260x_wakeup_srcs_attr_store);
+
+PM_ATTR(s2tos3_timeout, S_IRUGO | S_IWUSR, atc260x_s2tos3_timeout_shown, \
+	atc260x_s2tos3_timeout_store);
+PM_ATTR(s3tos4_timeout, S_IRUGO | S_IWUSR, atc260x_s3tos4_timeout_shown, \
+	atc260x_s3tos4_timeout_store);
+
+
+static struct attribute *atc260x_pm_attrs[] = {
+	WAKEUP_ATTR_PTR(wken_ir),
+	WAKEUP_ATTR_PTR(wken_reset),
+	WAKEUP_ATTR_PTR(wken_hdsw),
+	WAKEUP_ATTR_PTR(wken_alarm),
+	WAKEUP_ATTR_PTR(wken_remcon),
+	WAKEUP_ATTR_PTR(wken_tp),
+	WAKEUP_ATTR_PTR(wken_wkirq),
+	WAKEUP_ATTR_PTR(wken_onoff_short),
+	WAKEUP_ATTR_PTR(wken_onoff_long),
+	WAKEUP_ATTR_PTR(wken_wall),
+	WAKEUP_ATTR_PTR(wken_vbus),
+	WAKEUP_ATTR_PTR(wken_restart),
+	WAKEUP_ATTR_PTR(wken_sgpio),
+
+	PM_ATTR_PTR(wakeup_srcs),
+
+	PM_ATTR_PTR(s2tos3_timeout),
+	PM_ATTR_PTR(s3tos4_timeout),
+
+	NULL,
+};
+
+static struct attribute_group asoc_pmattr_group = {
+	.name = "wakeups",
+	.attrs = atc260x_pm_attrs,
+};
+
+static void _clear_status(struct atc260x_pm_dev *atc260x_pm)
+{
+	struct atc260x_dev *atc260x;
+	u32 rtc_msalm, rtc_halm, rtc_ymdalm;
+	int ret;
+
+	atc260x = atc260x_pm->atc260x;
+
+	/* disable alarm wake */
+	ret = _atc260x_pm_set_wakeup_src_inner(
+		atc260x_pm, OWL_PMIC_WAKEUP_SRC_ALARM, 0);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+	}
+
+	/* restore old alarm */
+	ret = atc260x_pstore_get(atc260x, ATC260X_PSTORE_TAG_RTC_MSALM, &rtc_msalm);
+	ret |= atc260x_pstore_get(atc260x, ATC260X_PSTORE_TAG_RTC_HALM, &rtc_halm);
+	ret |= atc260x_pstore_get(atc260x, ATC260X_PSTORE_TAG_RTC_YMDALM, &rtc_ymdalm);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+	}
+	if (rtc_msalm != 0 && rtc_halm != 0 && rtc_ymdalm != 0) {
+		ret = atc260x_reg_write(atc260x,
+			sc_atc260x_pm_regtbl_rtc_msalm[atc260x_pm->pmic_type], rtc_msalm);
+		ret |= atc260x_reg_write(atc260x,
+			sc_atc260x_pm_regtbl_rtc_halm[atc260x_pm->pmic_type], rtc_halm);
+		ret |= atc260x_reg_write(atc260x,
+			sc_atc260x_pm_regtbl_rtc_ymdalm[atc260x_pm->pmic_type], rtc_ymdalm);
+		if (ret) {
+			dev_err(atc260x_pm->dev, "%s() restore old alarm err\n", __func__);
+		}
+		dev_info(atc260x_pm->dev, "%s() restore old alarm, "
+				"msalm=0x%x halm=0x%x ymdalm=0x%x\n",
+				__func__, rtc_msalm, rtc_halm, rtc_ymdalm);
+	} else {
+		dev_info(atc260x_pm->dev, "%s() no need to restore old alarm\n", __func__);
+	}
+
+	/* clear pstore */
+	ret = atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_REBOOT_ADFU, 0);
+	ret |= atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_REBOOT_RECOVERY, 0);
+	ret |= atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_DIS_MCHRG, 0);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+	}
+	ret = atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_RTC_MSALM, 0);
+	ret |= atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_RTC_HALM, 0);
+	ret |= atc260x_pstore_set(atc260x, ATC260X_PSTORE_TAG_RTC_YMDALM, 0);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+	}
+}
+
+static int atc260x_pm_setup_wall_vbus_wakup_function(struct atc260x_pm_dev *atc260x_pm)
+{
+	struct device_node *of_parent_node, *of_power_node;
+	u32 support_adaptor_type;
+	uint reg_mask, reg_val;
+	int ret;
+
+	of_parent_node = of_get_parent(atc260x_pm->dev->of_node);
+	if (of_parent_node == NULL) {
+		dev_err(atc260x_pm->dev, "%s() can not get parent of_nade\n", __func__);
+		return -ENODEV;
+	}
+	of_power_node = of_get_child_by_name(of_parent_node, "atc260x-power");
+	if (of_parent_node == NULL) {
+		dev_err(atc260x_pm->dev, "%s() can not find power of_nade\n", __func__);
+		return -ENODEV;
+	}
+	ret = of_property_read_u32(of_power_node, "support_adaptor_type", &support_adaptor_type);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() can not get of_property support_adaptor_type \n", __func__);
+		return -ENODEV;
+	}
+	dev_info(atc260x_pm->dev, "got support_adaptor_type: %u\n", support_adaptor_type);
+
+	reg_mask = (3U << 7);
+	switch (support_adaptor_type) {
+	case 1:
+		/* only support WALL */
+		reg_val = (1U << 7);
+		break;
+	case 2:
+		/* only support VBUS */
+		reg_val = (1U << 8);
+		break;
+	default :
+		reg_val = (3U << 7);
+	}
+	ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+			sc_atc260x_pm_regtbl_sysctl5[atc260x_pm->pmic_type],
+			reg_mask, reg_val);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u IO err\n", __func__, __LINE__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int atc260x_pm_notifier_callback(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+	struct atc260x_pm_dev *atc260x_pm = _get_curr_atc260x_pm_obj();
+	switch (event) {
+	case PM_POST_HIBERNATION:    /* Hibernation finished */
+		_clear_status(atc260x_pm);
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static int atc260x_pm_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x;
+	struct atc260x_pm_dev *atc260x_pm;
+	int ret;
+
+	dev_info(&pdev->dev, "Probing...\n");
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	atc260x_pm = devm_kzalloc(&pdev->dev, sizeof(struct atc260x_pm_dev),
+				GFP_KERNEL);
+	if (!atc260x_pm) {
+		dev_err(&pdev->dev, "%s() no mem\n", __func__);
+		return -ENOMEM;
+	}
+	atc260x_pm->dev = &pdev->dev;
+	atc260x_pm->atc260x = atc260x;
+	atc260x_pm->pmic_type = atc260x_get_ic_type(atc260x);
+	atc260x_pm->pmic_ver = atc260x_get_ic_ver(atc260x);
+
+	platform_set_drvdata(pdev, atc260x_pm);
+
+	if (s_current_atc260x_pm_obj == NULL) {
+		s_current_atc260x_pm_obj = atc260x_pm;
+	}
+
+	ret = atc260x_reg_setbits(atc260x,
+		sc_atc260x_pm_regtbl_sysctl1[atc260x_pm->pmic_type],
+		PMU_SYS_CTL1_LB_S4_MASK, PMU_SYS_CTL1_LB_S4_3_1V);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+	}
+	
+	_set_s2_mode(atc260x_pm);
+	
+	_atc260x_pm_set_wakeup_src_inner(atc260x_pm, OWL_PMIC_WAKEUP_SRC_ALL,
+			OWL_PMIC_WAKEUP_SRC_RESET | OWL_PMIC_WAKEUP_SRC_HDSW |
+			OWL_PMIC_WAKEUP_SRC_ONOFF_LONG | OWL_PMIC_WAKEUP_SRC_WALL_IN |
+			OWL_PMIC_WAKEUP_SRC_VBUS_IN);
+	_clear_status(atc260x_pm);
+
+	/* delay some time from s2/s3 to s1 to avoid ddr init fail in bootloader */
+	if (atc260x_pm->pmic_type == ATC260X_ICTYPE_2603C) {
+		ret = atc260x_reg_setbits(atc260x_pm->atc260x,
+			sc_atc260x_pm_regtbl_sysctl3[atc260x_pm->pmic_type],
+			ATC2603C_S2S3TOS1_TIMER_EN, ATC2603C_S2S3TOS1_TIMER_EN);
+		if (ret) {
+			dev_err(atc260x_pm->dev, "%s() %u err\n", __func__, __LINE__);
+		}
+	}
+
+	ret = atc260x_pm_setup_wall_vbus_wakup_function(atc260x_pm);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() setup_wall_vbus_wakup_function failed\n", __func__);
+		/* not serious error, keep going. */
+	}
+
+	/* get get_wakeup_flag */
+	ret = _atc260x_pm_get_wakeup_flag_inner(atc260x_pm);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "%s() get_wakeup_flag failed\n", __func__);
+		/* not serious error, keep going. */
+	}
+
+	/* register PM notify */
+	atc260x_pm->pm_nb.notifier_call = atc260x_pm_notifier_callback;
+	ret = register_pm_notifier(&(atc260x_pm->pm_nb));
+	if (ret) {
+		dev_err(atc260x_pm->dev, "failed to register pm_notifier, ret=%d\n", ret);
+		goto label_err_lv2;
+	}
+
+	ret = sysfs_create_group(power_kobj, &asoc_pmattr_group);
+	if (ret) {
+		dev_err(atc260x_pm->dev, "failed to create sysfs nodes for atc260x_pm, ret=%d\n", ret);
+		goto label_err_lv3;
+	}
+
+	owl_pmic_set_pm_ops(&atc260x_pm_pmic_ops);
+
+	dev_info(atc260x_pm->dev, "#####%s PMU_SYS_CTL0:0x%x ####\n", __func__,
+		atc260x_reg_read(atc260x, sc_atc260x_pm_regtbl_sysctl0[atc260x_pm->pmic_type]));
+
+	return 0;
+
+	label_err_lv3:
+	unregister_pm_notifier(&(atc260x_pm->pm_nb));
+	label_err_lv2:
+	owl_pmic_set_pm_ops(NULL);
+	/*label_err_lv1: */
+	platform_set_drvdata(pdev, NULL);
+	if (s_current_atc260x_pm_obj == atc260x_pm) {
+		s_current_atc260x_pm_obj = NULL;
+	}
+	return ret;
+}
+
+static int atc260x_pm_remove(struct platform_device *pdev)
+{
+	struct atc260x_pm_dev *atc260x_pm = platform_get_drvdata(pdev);
+	sysfs_remove_group(power_kobj, &asoc_pmattr_group);
+	unregister_pm_notifier(&(atc260x_pm->pm_nb));
+	owl_pmic_set_pm_ops(NULL);
+	platform_set_drvdata(pdev, NULL);
+	if (s_current_atc260x_pm_obj == atc260x_pm) {
+		s_current_atc260x_pm_obj = NULL;
+	}
+	return 0;
+}
+
+
+static const struct of_device_id atc260x_pm_match[] = {
+	{ .compatible = "actions,atc2603a-pm", },
+	{ .compatible = "actions,atc2603c-pm", },
+	{ .compatible = "actions,atc2609a-pm", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_pm_match);
+
+static struct platform_driver atc260x_pm_driver = {
+	.probe = atc260x_pm_probe,
+	.remove = atc260x_pm_remove,
+	.driver = {
+		.name   = "atc260x-pm",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(atc260x_pm_match),
+	},
+};
+
+static int __init atc260x_pm_init(void)
+{
+	int ret;
+	ret = platform_driver_register(&atc260x_pm_driver);
+	if (ret != 0)
+		pr_err("Failed to register ATC260X PM driver: %d\n", ret);
+	return 0;
+}
+subsys_initcall(atc260x_pm_init);
+
+static void __exit atc260x_pm_exit(void)
+{
+	platform_driver_unregister(&atc260x_pm_driver);
+}
+module_exit(atc260x_pm_exit);
+
+/* Module information */
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("ATC260X PM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-pm");
+
+
diff --git a/drivers/mfd/atc260x-pstore.c b/drivers/mfd/atc260x-pstore.c
new file mode 100755
index 0000000..4d76256
--- /dev/null
+++ b/drivers/mfd/atc260x-pstore.c
@@ -0,0 +1,456 @@
+/* UTF-8 encoded. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include "atc260x-core.h"
+
+/* persistent storage
+ * 本模块负责管理260x中几个不掉电/不复位的寄存器.
+ * 因为要考虑N颗相似IC的兼容, 使用了比较繁琐的数据结构. */
+
+
+struct atc260x_pstore_fmt {
+	const char *name;
+	u16     regs[4];   /* destination register of each slice */
+	u16     reg_bm[4]; /* register bitmap of each slice */
+	u8      src_bp[4]; /* source start bit position of each slice */
+};
+
+static const struct atc260x_pstore_fmt sc_atc2603a_pstore_fmt_tbl[] = {
+	[ATC260X_PSTORE_TAG_REBOOT_ADFU] = {
+		.name = "REBOOT_ADFU",
+		.regs   = {ATC2603A_PMU_UV_STATUS },
+		.reg_bm = {(1U << 1)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_REBOOT_RECOVERY] = {
+		.name = "REBOOT_RECOVERY",
+		.regs   = {ATC2603A_PMU_SYS_CTL2 },
+		.reg_bm = {(3U << 1)             },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_FW_S2] = {
+		.name = "FW_S2",
+		.regs   = {ATC2603A_PMU_SYS_CTL3 },
+		.reg_bm = {(1U << 4)             },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_DIS_MCHRG] = {
+		.name = "DIS_MCHRG",
+		.regs   = {ATC2603A_PMU_UV_INT_EN },
+		.reg_bm = {(1U << 0)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_MSALM] = {
+		.name = "RTC_MSALM",
+		.regs   = {ATC2603A_PMU_SYS_CTL3, ATC2603A_PMU_SYS_CTL9},
+		.reg_bm = {(0xfU << 6),           (0xff << 0)          },
+		.src_bp = {8,                     0                    },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_HALM] = {
+		.name = "RTC_HALM",
+		.regs   = {ATC2603A_PMU_VBUS_CTL1 },
+		.reg_bm = {(0x1fU << 0)           },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_YMDALM] = {
+		.name = "RTC_YMDALM",
+		.regs   = {ATC2603A_PMU_SYS_CTL8 },
+		.reg_bm = {(0xffffU << 0)        },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_CAP] = {
+		.name = "GAUGE_CAP",
+		.regs   = {ATC2603A_PMU_SYS_CTL9 },
+		.reg_bm = {(0xffU << 8)          },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_BAT_RES] = {
+		.name = "GAUGE_BAT_RES",
+		.regs   = {ATC2603A_PMU_OC_INT_EN, ATC2603A_PMU_OC_INT_EN, ATC2603A_PMU_OC_INT_EN, ATC2603A_PMU_SYS_PENDING },
+		.reg_bm = {(0x3fU << 0),           (1U << 10),             (7U<<13),               (0x3fU << 1)             },
+		.src_bp = {0,                      6,                      7,                      10                       },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST] = {
+		.name = "GAUGE_ICM_EXIST",
+		.regs   = {ATC2603A_PMU_SYS_CTL3 },
+		.reg_bm = {(1U << 5)             },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_SHDWN_TIME] = {
+		.name = "GAUGE_SHDWN_TIME",
+		.regs   = {0, }, /* not need for 2603a */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_S2_CONSUMP] = {
+		.name = "GAUGE_S2_CONSUMP",
+		.regs   = {0, }, /* not need for 2603a */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_CLMT_RESET] = {
+		.name = "GAUGE_CLMT_RESET",
+		.regs   = {0, }, /* not need for 2603a */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_RESUME_ADDR] = {
+		.name = "RESUME_ADDR",
+		.regs   = {ATC2603A_PMU_OC_STATUS, ATC2603A_PMU_SYS_CTL3, ATC2603A_PMU_SYS_CTL9, ATC2603A_PMU_SYS_CTL8},
+		.reg_bm = {(0xfU << 2),            (0xfU << 6),           (0xff << 0),           (0xffffU << 0)       },
+		.src_bp = {28,                     24,                     16,                   0                    },
+	},
+};
+
+static const struct atc260x_pstore_fmt sc_atc2603c_pstore_fmt_tbl[] = {
+	[ATC260X_PSTORE_TAG_REBOOT_ADFU] = {
+		.name = "REBOOT_ADFU",
+		.regs   = {ATC2603C_PMU_UV_STATUS },
+		.reg_bm = {(1U << 1)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_REBOOT_RECOVERY] = {
+		.name = "REBOOT_RECOVERY",
+		.regs   = {ATC2603C_PMU_OV_INT_EN },
+		.reg_bm = {(3U << 0)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_FW_S2] = {
+		.name = "FW_S2",
+		.regs   = {ATC2603C_PMU_SYS_CTL3 },
+		.reg_bm = {(1U << 4)             },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_DIS_MCHRG] = {
+		.name = "DIS_MCHRG",
+		.regs   = {ATC2603C_PMU_UV_INT_EN },
+		.reg_bm = {(1U << 0)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_MSALM] = {
+		.name = "RTC_MSALM",
+		.regs   = {ATC2603C_PMU_FW_USE0 },
+		.reg_bm = {(0xfffU << 0)        },
+		.src_bp = {0                    },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_HALM] = {
+		.name = "RTC_HALM",
+		.regs   = {ATC2603C_PMU_VBUS_CTL1 },
+		.reg_bm = {(0x1fU << 0)           },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_YMDALM] = {
+		.name = "RTC_YMDALM",
+		.regs   = {ATC2603C_PMU_SYS_CTL8 },
+		.reg_bm = {(0xffffU << 0)        },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_CAP] = {
+		.name = "GAUGE_CAP",
+		.regs   = {ATC2603C_PMU_SYS_CTL9 },
+		.reg_bm = {(0xffU << 8)          },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_BAT_RES] = {
+		.name = "GAUGE_BAT_RES",
+		.regs   = {ATC2603C_PMU_OC_INT_EN, ATC2603C_PMU_OC_INT_EN, ATC2603C_PMU_OC_INT_EN, ATC2603A_PMU_SYS_CTL9 },
+		.reg_bm = {(0x3fU << 0),           (1U << 10),             (7U<<13),               (0x3fU << 2)          },
+		.src_bp = {0,                      6,                      7,                      10                    },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST] = {
+		.name = "GAUGE_ICM_EXIST",
+		.regs   = {ATC2603C_PMU_SYS_CTL3 },
+		.reg_bm = {(1U << 5)             },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_SHDWN_TIME] = {
+		.name = "GAUGE_SHDWN_TIME",
+		.regs   = {0, }, /* not need for 2603c */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_S2_CONSUMP] = {
+		.name = "GAUGE_S2_CONSUMP",
+		.regs   = {0, }, /* not need for 2603c */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_CLMT_RESET] = {
+		.name = "GAUGE_CLMT_RESET",
+		.regs   = {0, }, /* not need for 2603c */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_RESUME_ADDR] = {
+		.name = "RESUME_ADDR",
+		.regs   = {ATC2603C_PMU_FW_USE0, ATC2603C_PMU_SYS_CTL8},
+		.reg_bm = {(0xffffU << 0),       (0xffffU << 0)       },
+		.src_bp = {16,                   0                    },
+	},
+};
+
+static const struct atc260x_pstore_fmt sc_atc2609a_pstore_fmt_tbl[] = {
+	[ATC260X_PSTORE_TAG_REBOOT_ADFU] = {
+		.name = "REBOOT_ADFU",
+		.regs   = {ATC2609A_PMU_UV_STATUS },
+		.reg_bm = {(1U << 1)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_REBOOT_RECOVERY] = {
+		.name = "REBOOT_RECOVERY",
+		.regs   = {ATC2609A_PMU_OV_INT_EN },
+		.reg_bm = {(3U << 0)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_FW_S2] = {
+		.name = "FW_S2",
+		.regs   = {ATC2609A_PMU_SYS_CTL3 },
+		.reg_bm = {(1U << 4)             },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_DIS_MCHRG] = {
+		.name = "DIS_MCHRG",
+		.regs   = {ATC2609A_PMU_UV_INT_EN },
+		.reg_bm = {(1U << 0)              },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_MSALM] = {
+		.name = "RTC_MSALM",
+		.regs   = {ATC2609A_PMU_SYS_CTL7 },
+		.reg_bm = {(0xfffU << 0)         },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_HALM] = {
+		.name = "RTC_HALM",
+		.regs   = {ATC2609A_PMU_VBUS_CTL1 },
+		.reg_bm = {(0x1fU << 0)           },
+		.src_bp = {0                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RTC_YMDALM] = {
+		.name = "RTC_YMDALM",
+		.regs   = {ATC2609A_PMU_SYS_CTL8 },
+		.reg_bm = {(0xffffU << 0)        },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_CAP] = {
+		.name = "GAUGE_CAP",
+		.regs   = {ATC2609A_PMU_SYS_CTL9 },
+		.reg_bm = {(0xffU << 8)          },
+		.src_bp = {0                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_BAT_RES] = {
+		.name = "GAUGE_BAT_RES",
+		.regs   = {0, }, /* not need for 2609a */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST] = {
+		.name = "GAUGE_ICM_EXIST",
+		.regs   = {0, }, /* not need for 2609a */
+		.reg_bm = {0, },
+		.src_bp = {0, },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_SHDWN_TIME] = {
+		.name = "GAUGE_SHDWN_TIME",
+		.regs   = {ATC2609A_PMU_SYS_CTL9, ATC2609A_PMU_BAT_CTL0, ATC2609A_PMU_BAT_CTL1, ATC2609A_PMU_WALL_CTL1 },
+		.reg_bm = {(0xffU << 0),          (0x3fU << 0),          (0x1ffU << 0),         (0xffU << 0)           },
+		.src_bp = {0,                     8,                     14,                    23                     },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_S2_CONSUMP] = {
+		.name = "GAUGE_S2_CONSUMP",
+		.regs   = {ATC2609A_PMU_VBUS_CTL0, },
+		.reg_bm = {(0x3fU << 0),           },
+		.src_bp = {0,                      },
+	},
+
+	[ATC260X_PSTORE_TAG_GAUGE_CLMT_RESET] = {
+		.name = "GAUGE_CLMT_RESET",
+		.regs   = {ATC2609A_PMU_OV_STATUS, },
+		.reg_bm = {(1U << 1),              },
+		.src_bp = {0,                      },
+	},
+
+	[ATC260X_PSTORE_TAG_RESUME_ADDR] = {
+		.name = "RESUME_ADDR",
+		.regs   = {ATC2609A_PMU_SYS_CTL7, ATC2609A_PMU_SYS_CTL8},
+		.reg_bm = {(0xffffU << 0),        (0xffffU << 0)       },
+		.src_bp = {16,                    0                    },
+	},
+};
+
+static const struct atc260x_pstore_fmt * const sc_atc2603a_pstore_fmt_tbls[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = sc_atc2603a_pstore_fmt_tbl,
+	[ATC260X_ICTYPE_2603C] = sc_atc2603c_pstore_fmt_tbl,
+	[ATC260X_ICTYPE_2609A] = sc_atc2609a_pstore_fmt_tbl,
+};
+
+
+int atc260x_pstore_set(struct atc260x_dev *atc260x, uint tag, u32 value)
+{
+	const struct atc260x_pstore_fmt *fmt;
+	uint i, reg_shift;
+	u32 src_bm, reg_bm, reg_val;
+	int ret;
+
+	if (tag >= ATC260X_PSTORE_TAG_NUM)
+		return -EINVAL;
+
+	BUG_ON(atc260x->ic_type >= ATC260X_ICTYPE_CNT);
+	fmt = &((sc_atc2603a_pstore_fmt_tbls[atc260x->ic_type])[tag]);
+
+	if (fmt->reg_bm[0] == 0 && value != 0) {
+		dev_err(atc260x->dev, "%s(): pstore tag %s not available (ic_type=%u)\n",
+			__func__, fmt->name, atc260x->ic_type);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fmt->regs); i++) {
+		if (fmt->reg_bm[i] == 0)
+			break;
+
+		reg_bm = fmt->reg_bm[i];
+		reg_shift = __ffs(reg_bm);
+		src_bm = (reg_bm >> reg_shift) << fmt->src_bp[i];
+		reg_val = ((value & src_bm) >> fmt->src_bp[i]) << reg_shift;
+
+		ret = atc260x_reg_setbits(atc260x, fmt->regs[i], reg_bm, reg_val);
+		if (ret) {
+			dev_err(atc260x->dev, "%s() io failed, ret=%d", __func__, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_pstore_set);
+
+int atc260x_pstore_get(struct atc260x_dev *atc260x, uint tag, u32 *p_value)
+{
+	const struct atc260x_pstore_fmt *fmt;
+	uint i, reg_shift;
+	u32 result, reg_bm;
+	int ret;
+
+	if (tag >= ATC260X_PSTORE_TAG_NUM)
+		return -EINVAL;
+
+	BUG_ON(atc260x->ic_type >= ATC260X_ICTYPE_CNT);
+	fmt = &((sc_atc2603a_pstore_fmt_tbls[atc260x->ic_type])[tag]);
+
+	result = 0;
+	for (i = 0; i < ARRAY_SIZE(fmt->regs); i++) {
+		if (fmt->reg_bm[i] == 0)
+			break;
+
+		reg_bm = fmt->reg_bm[i];
+		reg_shift = __ffs(reg_bm);
+
+		ret = atc260x_reg_read(atc260x, fmt->regs[i]);
+		if (ret < 0) {
+			dev_err(atc260x->dev, "%s() io failed, ret=%d", __func__, ret);
+			return ret;
+		}
+		result |= (((u32)ret & reg_bm) >> reg_shift) << fmt->src_bp[i];
+	}
+
+	*p_value = result;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_pstore_get);
+
+int atc260x_pstore_reset_all(struct atc260x_dev *atc260x)
+{
+	uint t;
+	int ret;
+	for (t = 0; t < ATC260X_PSTORE_TAG_NUM; t++) {
+		ret = atc260x_pstore_set(atc260x, t, 0);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_pstore_reset_all);
+
+/* return filled size, or error code. */
+int atc260x_pstore_dbg_dump(struct atc260x_dev *atc260x, char *buf, uint bufsize)
+{
+	const struct atc260x_pstore_fmt *fmt;
+	uint t, size_remain, filled_size;
+	u32 result;
+	int ret;
+
+	if (bufsize <= 2) {
+		return -EINVAL;
+	}
+	size_remain = bufsize - 2;
+	filled_size = 0;
+
+	BUG_ON(atc260x->ic_type >= ATC260X_ICTYPE_CNT);
+	fmt = sc_atc2603a_pstore_fmt_tbls[atc260x->ic_type];
+
+	for (t = 0; t < ATC260X_PSTORE_TAG_NUM; t++,fmt++) {
+		ret = atc260x_pstore_get(atc260x, t, &result);
+		if (ret < 0)
+			return ret;
+		ret = scnprintf(buf, size_remain, "%-2u %-24s 0x%x\n",
+				t, fmt->name, result);
+		if (ret < 0)
+			return ret;
+		buf += (uint)ret;
+		filled_size += (uint)ret;
+		size_remain -= (uint)ret;
+		if (size_remain == 0)
+			break;
+	}
+
+	return filled_size;
+}
+
diff --git a/drivers/mfd/atc260x-pwm.c b/drivers/mfd/atc260x-pwm.c
new file mode 100755
index 0000000..5783acf
--- /dev/null
+++ b/drivers/mfd/atc260x-pwm.c
@@ -0,0 +1,205 @@
+/*
+ * Asoc  ir driver
+ *
+ * Copyright (C) 2011 Actions Semiconductor, Inc
+ * Author:	chenbo <chenbo at actions-semi.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mfd/atc260x/atc260x.h>
+#include <linux/time.h>
+
+
+#define ATC260X_PWM_INFO(fmt, args...)	printk(KERN_INFO "atc260x_pwm_drv: " fmt, ##args)
+
+struct atc260x_pwm_dev {
+	struct atc260x_dev *atc260x_dev;
+};
+
+void atc260x_pwm_reg_print(struct atc260x_pwm_dev *atc260x_pwm)
+{
+	ATC260X_PWM_INFO("\n\nfollowing list all atc260x pwm register's value!\n");
+	ATC260X_PWM_INFO("register ATC2603C_PMU_MUX_CTL0(0x%08x) value is 0x%08x\n",
+		ATC2603C_PMU_MUX_CTL0, atc260x_reg_read(atc260x_pwm->atc260x_dev,ATC2603C_PMU_MUX_CTL0));
+	ATC260X_PWM_INFO("register ATC2603C_PWMCLK_CTL(0x%08x) value is 0x%08x\n",
+		ATC2603C_PWMCLK_CTL, atc260x_reg_read(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL));
+	ATC260X_PWM_INFO("register ATC2603C_PWM0_CTL(0x%08x) value is 0x%08x\n",
+		ATC2603C_PWM0_CTL, atc260x_reg_read(atc260x_pwm->atc260x_dev,ATC2603C_PWM0_CTL));
+	ATC260X_PWM_INFO("register ATC2603C_PWM1_CTL(0x%08x) value is 0x%08x\n",
+		ATC2603C_PWM1_CTL, atc260x_reg_read(atc260x_pwm->atc260x_dev,ATC2603C_PWM1_CTL));
+}
+
+static void atc260x_pwm_on(struct atc260x_pwm_dev *atc260x_pwm)
+{
+	ATC260X_PWM_INFO("[%s start]\n",__func__);
+
+	//set SGPIO4 sgpio out
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PMU_MUX_CTL0,0x0c00,0x0000);
+	//atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0x3000,0x0010);
+	//atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0x0fff,0x0010);
+  //atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWM0_CTL,0xffff,0xff00);
+  //atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0x3000,0x3010);	
+  atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PMU_SGPIO_CTL3,0x2000,0x2000);
+  atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PMU_SGPIO_CTL4,0x0010,0x0010);  
+
+	ATC260X_PWM_INFO("[%s finished]\n",__func__);
+}
+
+static void atc260x_pwm_hold(struct atc260x_pwm_dev *atc260x_pwm)
+{
+
+	ATC260X_PWM_INFO("[%s start]\n",__func__);
+
+	//set SGPIO4 pwm0
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PMU_MUX_CTL0,0x0c00,0x0C00);
+	
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0x3000,0x0010);
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0x0fff,0x0010);
+  atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWM0_CTL,0xffff,0xffff);
+  atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0x3000,0x3010);	
+
+	ATC260X_PWM_INFO("[%s finished]\n",__func__);
+}
+
+static void atc260x_pwm_stop(struct atc260x_pwm_dev *atc260x_pwm)
+{
+
+	ATC260X_PWM_INFO("[%s start]\n",__func__);
+
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PMU_MUX_CTL0,0x0c00,0x0000);
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PMU_SGPIO_CTL3,0x2000,0x0000);
+
+	atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWMCLK_CTL,0xffff,0x0000);
+  atc260x_set_bits(atc260x_pwm->atc260x_dev,ATC2603C_PWM0_CTL,0xffff,0x0000);
+
+	ATC260X_PWM_INFO("[%s finished]\n",__func__);
+}
+
+static int atc260x_pwm_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x_dev = dev_get_drvdata(pdev->dev.parent);
+	struct atc260x_pwm_dev *atc260x_pwm;
+	struct device_node *np;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "atc260x_pwm Probing...\n");
+	np = pdev->dev.of_node;
+
+	atc260x_pwm = kzalloc(sizeof(struct atc260x_pwm_dev), GFP_KERNEL);
+	if (atc260x_pwm == NULL) {
+		dev_err(&pdev->dev, "failed to allocate atc260x_pwm driver data\n");
+		ret = -ENOMEM;
+		return ret;
+	}
+	platform_set_drvdata(pdev, atc260x_pwm);
+
+	atc260x_pwm->atc260x_dev = atc260x_dev;
+
+	/*undo for dts*/
+	
+	atc260x_pwm_on(atc260x_pwm);
+	return 0;
+}
+
+static int atc260x_pwm_remove(struct platform_device *pdev)
+{
+  struct atc260x_pwm_dev *atc260x_pwm = platform_get_drvdata(pdev);
+  ATC260X_PWM_INFO("[%s start]\n",__func__);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(atc260x_pwm);
+    
+	ATC260X_PWM_INFO("[%s finished]\n", __func__);
+	return 0;
+}
+
+static void atc260x_pwm_shutdown(struct platform_device *pdev){
+	struct atc260x_pwm_dev *atc260x_pwm = platform_get_drvdata(pdev);
+	ATC260X_PWM_INFO("[%s start]\n",__func__);
+
+	atc260x_pwm_stop(atc260x_pwm);
+    
+	ATC260X_PWM_INFO("[%s finished]\n", __func__);
+}
+
+static int atc260x_pwm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct atc260x_pwm_dev *atc260x_pwm = platform_get_drvdata(pdev);
+	atc260x_pwm_hold(atc260x_pwm);
+
+  ATC260X_PWM_INFO("[%s finished]\n", __func__);
+	return 0;
+}
+
+static int atc260x_pwm_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct atc260x_pwm_dev *atc260x_pwm = platform_get_drvdata(pdev);
+	atc260x_pwm_on(atc260x_pwm);
+	
+  ATC260X_PWM_INFO("[%s finished]\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops s_atc260x_pwm_pm_ops = {
+	.suspend        = atc260x_pwm_suspend,
+	.resume	        = atc260x_pwm_resume,
+};
+
+static const struct of_device_id atc260x_pwm_of_match[] = {
+        {.compatible = "actions,atc2603a-pwm",},
+        {.compatible = "actions,atc2603c-pwm",},
+        {.compatible = "actions,atc2609a-pwm",},
+        {}
+};
+MODULE_DEVICE_TABLE(of, atc260x_pwm_of_match);
+
+static struct platform_driver atc260x_pwm_driver = {
+        .driver         = {
+                .name   = "atc260x-pwm",
+                .owner  = THIS_MODULE,
+                .pm     = &s_atc260x_pwm_pm_ops,
+                .of_match_table = of_match_ptr(atc260x_pwm_of_match),
+        },
+	.probe		= atc260x_pwm_probe,
+	.remove		= atc260x_pwm_remove,
+  .shutdown = atc260x_pwm_shutdown,
+};
+
+static int __init atc260x_pwm_init(void)
+{
+	int ret;
+	ret=platform_driver_register(&atc260x_pwm_driver);
+	return ret;
+}
+
+static void __exit atc260x_pwm_exit(void)
+{
+	platform_driver_unregister(&atc260x_pwm_driver);
+}
+
+module_init(atc260x_pwm_init);
+module_exit(atc260x_pwm_exit);
+
+MODULE_DESCRIPTION("ATC260X PWM driver");
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-pwm");
diff --git a/drivers/mfd/atc260x-spi.c b/drivers/mfd/atc260x-spi.c
new file mode 100755
index 0000000..1f9a9b4
--- /dev/null
+++ b/drivers/mfd/atc260x-spi.c
@@ -0,0 +1,323 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#include <mach/regs_map-atm7039.h>
+
+#include "atc260x-core.h"
+
+
+
+/*----------------------------------------------------------------------------*/
+/* code for direct access */
+
+#define REG_SPI_CTL(_iob)                   ((_iob)+(SPI0_CTL-SPI0_BASE))
+#define REG_SPI_CLKDIV(_iob)                ((_iob)+(SPI0_CLKDIV-SPI0_BASE))
+#define REG_SPI_STAT(_iob)                  ((_iob)+(SPI0_STAT-SPI0_BASE))
+#define REG_SPI_RXDAT(_iob)                 ((_iob)+(SPI0_RXDAT-SPI0_BASE))
+#define REG_SPI_TXDAT(_iob)                 ((_iob)+(SPI0_TXDAT-SPI0_BASE))
+#define REG_SPI_TCNT(_iob)                  ((_iob)+(SPI0_TCNT-SPI0_BASE))
+#define REG_SPI_SEED(_iob)                  ((_iob)+(SPI0_SEED-SPI0_BASE))
+#define REG_SPI_TXCR(_iob)                  ((_iob)+(SPI0_TXCR-SPI0_BASE))
+#define REG_SPI_RXCR(_iob)                  ((_iob)+(SPI0_TXCR-SPI0_BASE))
+
+#define REG_CMU_ETHERNETPLL(_iob)           ((_iob)+(CMU_ETHERNETPLL-CMU_BASE))
+#define REG_CMU_DEVCLKEN0(_iob)             ((_iob)+(CMU_DEVCLKEN0-CMU_BASE))
+#define REG_CMU_DEVCLKEN1(_iob)             ((_iob)+(CMU_DEVCLKEN1-CMU_BASE))
+#define REG_CMU_DEVRST0(_iob)               ((_iob)+(CMU_DEVRST0-CMU_BASE))
+#define REG_CMU_DEVRST1(_iob)               ((_iob)+(CMU_DEVRST1-CMU_BASE))
+
+
+static int spi_shift_out(void __iomem *iobase, unsigned int var)
+{
+	int temp;
+	int wait_times = 0x500000;
+
+	writel(var, REG_SPI_TXDAT(iobase));
+	do
+	{
+		temp =readl(REG_SPI_STAT(iobase));
+		temp =temp & 0x04;
+	}
+	while ((temp == 0)&&(--wait_times > 0));
+	writel(temp, REG_SPI_STAT(iobase));
+
+	return(temp);
+}
+static void spi_direct_write(void __iomem *iobase, u16 addr, u16 data)
+{
+	u32 delay_cell = 0x00000000; /*0x1000000:delay 1 hclk cycle;0x2000000:delay 2 hclk cycle */
+	unsigned short temp;
+
+	writel(0x401c0+delay_cell, REG_SPI_CTL(iobase));    /*16bit mode,pull down spi_ss line */
+	temp=(addr<<3)|0x8000;          /*bit15=1 is write flag,low 15 bit is the register address */
+	spi_shift_out(iobase, temp);            /*send out the write flag and address */
+	spi_shift_out(iobase, data);            /*send out the write data */
+	writel(0x401d0+delay_cell, REG_SPI_CTL(iobase));  /*16bit mode,pull up spi_ss line */
+}
+static u16 spi_direct_read(void __iomem *iobase, u16 addr)
+{
+	u32 delay_cell = 0x00000000; /*0x1000000:delay 1 hclk cycle;0x2000000:delay 2 hclk cycle */
+	unsigned short temp;
+
+	writel(0x401c0+delay_cell, REG_SPI_CTL(iobase));    /*16bit mode,pull down spi_ss line */
+
+	temp=(addr<<3)&0x7fff;          /*bit15=0 is read flag,low 15 bit is the register address */
+	spi_shift_out(iobase, temp);            /*send out the read flag and address */
+
+	writel(0x30, REG_SPI_STAT(iobase));         /*reset the fifo */
+
+	spi_shift_out(iobase, 0x0);         /*send out read clock */
+	temp = readl(REG_SPI_RXDAT(iobase));          /*read the register value */
+	writel(0x401d0+delay_cell, REG_SPI_CTL(iobase));    /*16bit mode,pull up spi_ss line */
+
+	return temp;
+}
+
+static void _atc260x_spi_direct_access_init(struct atc260x_dev *atc260x)
+{
+	static const ulong sc_hw_iobase_tbl[] = {
+		SPI0_BASE, SPI1_BASE, SPI2_BASE, SPI3_BASE
+	};
+	static const u8 sc_spi_clk_ctl_bit_tbl[] = {
+		10, 11, 12, 13,  /* for gl5206 only !!! */
+	};
+	static const u8 sc_spi_reset_ctl_bit_tbl[] = {
+		8, 9, 10, 11,  /* for gl5206 only !!! */
+	};
+	ulong hw_iobase, hw_iosize;
+	void __iomem *hw_cmu_iobase;
+	uint mask;
+
+	/* ioremap ... */
+	if (atc260x->direct_access_ioremap_flag == 0) {
+		atc260x->direct_access_ioremap_flag = 1;
+
+		BUG_ON(atc260x->bus_num >= ARRAY_SIZE(sc_hw_iobase_tbl));
+		hw_iobase = sc_hw_iobase_tbl[atc260x->bus_num];
+		hw_iosize = SPI0_RXCR - SPI0_BASE +4U;
+		/* no need to request the IO region, it always failed because we
+		 * are now trying to steal others' stuff ;)
+		if (!request_mem_region(hw_iobase, hw_iosize, "atc260x-spi-dacc")) {
+			dev_err(atc260x->dev, "failed to request SPI region\n");
+			BUG();
+		} */
+		atc260x->dacc_iobase = devm_ioremap(atc260x->dev, hw_iobase, hw_iosize);
+		if (atc260x->dacc_iobase == NULL) {
+			dev_err(atc260x->dev, "failed to ioremap SPI region\n");
+			BUG();
+		}
+		atc260x->dacc_cmu_iobase = devm_ioremap(atc260x->dev,
+					CMU_BASE, (CMU_DIGITALDEBUG-CMU_BASE));
+		if (atc260x->dacc_cmu_iobase == NULL) {
+			dev_err(atc260x->dev, "failed to ioremap CMU region\n");
+			BUG();
+		}
+	}
+	hw_cmu_iobase = atc260x->dacc_cmu_iobase;
+
+	/* init SPI clock */
+	mask = 1U << sc_spi_clk_ctl_bit_tbl[atc260x->bus_num];
+	writel(readl(REG_CMU_DEVCLKEN1(hw_cmu_iobase)) | mask,
+			REG_CMU_DEVCLKEN1(hw_cmu_iobase));
+	readl(REG_CMU_DEVCLKEN1(hw_cmu_iobase));
+
+	/* reset SPI */
+	mask = 1U << sc_spi_reset_ctl_bit_tbl[atc260x->bus_num];
+	writel(readl(REG_CMU_DEVRST1(hw_cmu_iobase)) & ~mask,
+			REG_CMU_DEVRST1(hw_cmu_iobase));
+	readl(REG_CMU_DEVRST1(hw_cmu_iobase));
+	udelay(20);
+	writel(readl(REG_CMU_DEVRST1(hw_cmu_iobase)) | mask,
+			REG_CMU_DEVRST1(hw_cmu_iobase));
+	readl(REG_CMU_DEVRST1(hw_cmu_iobase));
+	udelay(50);
+
+	/* SPI clock divider */
+	/* assume HCLK <= 1GHz */
+	writel((250U<<0), REG_SPI_CLKDIV(atc260x->dacc_iobase));
+
+	dev_info(atc260x->dev, "%s() direct_access activated\n", __func__);
+}
+
+static void _atc260x_spi_direct_access_exit(struct atc260x_dev *atc260x)
+{
+	if (atc260x->direct_access_ioremap_flag != 0) {
+		atc260x->direct_access_ioremap_flag = 0;
+		devm_iounmap(atc260x->dev, atc260x->dacc_iobase);
+		devm_iounmap(atc260x->dev, atc260x->dacc_cmu_iobase);
+		dev_info(atc260x->dev, "%s() direct_access IO unmapped\n", __func__);
+	}
+}
+
+static int _atc260x_spi_direct_read_reg(struct atc260x_dev *atc260x, uint reg)
+{
+	return spi_direct_read(atc260x->dacc_iobase, reg);
+}
+
+static int _atc260x_spi_direct_write_reg(struct atc260x_dev *atc260x, uint reg, u16 val)
+{
+	spi_direct_write(atc260x->dacc_iobase, reg, val);
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static struct regmap_config atc2603a_regmap_config = {
+	.reg_bits = 13,
+	.pad_bits = 3,
+	.val_bits = 16,
+
+	.read_flag_mask = 0x0,
+	.write_flag_mask = 0x80,
+
+	.cache_type = REGCACHE_NONE,  /* SPI的速率比较高, 不用cache都没有问题了. */
+	.max_register = ATC2603A_CVER,
+	.reg_format_endian = REGMAP_ENDIAN_BIG,
+	.val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct of_device_id atc2603_spi_of_match[] = {
+	{ .compatible = "actions,atc2603a", .data = (void *)ATC260X_ICTYPE_2603A },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, atc260x_of_match);
+
+static int atc260x_spi_probe(struct spi_device *spi)
+{
+	struct atc260x_dev *atc260x;
+	const struct of_device_id *of_id;
+	int ret;
+
+	dev_info(&spi->dev, "Probing...\n");
+
+	atc260x = devm_kzalloc(&spi->dev, sizeof(*atc260x), GFP_KERNEL);
+	if (atc260x == NULL)
+		return -ENOMEM;
+
+	of_id = of_match_device(atc2603_spi_of_match, &spi->dev); /* match again, get of_id */
+	if (!of_id) {
+		dev_err(&spi->dev, "of_match failed, unable to get device data\n");
+		return -ENODEV;
+	}
+	atc260x->ic_type = (ulong) of_id->data;
+
+	/* 当bits_per_word>8时, word内使用native_endian(little)的字节序,
+	 * 这是 OWL spi master驱动那边的实现方式.
+	 * 而这边regmap内部已经有endian的处理了,
+	 * 提交到spi的都是按big_endian处理好的字节流, 故这里bits_per_word不能配置为16,
+	 * 否则寄存器的地址/数据的字节序又反过来了. */
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_3;
+
+	spi_set_drvdata(spi, atc260x);
+	atc260x->dev = &spi->dev;
+	atc260x->irq = spi->irq;
+
+	/* init direct-access functions */
+	atc260x->bus_num = (typeof(atc260x->bus_num))(spi->master->bus_num);
+	atc260x->bus_addr = 0;
+	atc260x->direct_read_reg = _atc260x_spi_direct_read_reg;
+	atc260x->direct_write_reg = _atc260x_spi_direct_write_reg;
+	atc260x->direct_acc_init = _atc260x_spi_direct_access_init;
+	atc260x->direct_acc_exit = _atc260x_spi_direct_access_exit;
+
+	/* register regmap */
+	atc260x->regmap = devm_regmap_init_spi(spi, &atc2603a_regmap_config);
+	if (IS_ERR(atc260x->regmap)) {
+		ret = PTR_ERR(atc260x->regmap);
+		dev_err(atc260x->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = atc260x_core_dev_init(atc260x);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to init atc260x device");
+	}
+
+	return ret;
+}
+
+static int atc260x_spi_remove(struct spi_device *spi)
+{
+	struct atc260x_dev *atc260x = spi_get_drvdata(spi);
+	atc260x_core_dev_exit(atc260x);
+	return 0;
+}
+
+static int atc260x_spi_suspend(struct device *dev)
+{
+	struct atc260x_dev *atc260x = dev_get_drvdata(dev);
+	return atc260x_core_dev_suspend(atc260x);
+}
+static int atc260x_spi_suspend_late(struct device *dev)
+{
+	struct atc260x_dev *atc260x = dev_get_drvdata(dev);
+	return atc260x_core_dev_suspend_late(atc260x);
+}
+static int atc260x_spi_resume_early(struct device *dev)
+{
+	struct atc260x_dev *atc260x = dev_get_drvdata(dev);
+	return atc260x_core_dev_resume_early(atc260x);
+}
+static int atc260x_spi_resume(struct device *dev)
+{
+	struct atc260x_dev *atc260x = dev_get_drvdata(dev);
+	return atc260x_core_dev_resume(atc260x);
+}
+static const struct dev_pm_ops s_atc260x_spi_pm_ops = {
+	.suspend       = atc260x_spi_suspend,
+	.suspend_late  = atc260x_spi_suspend_late,
+	.resume_early  = atc260x_spi_resume_early,
+	.resume        = atc260x_spi_resume,
+	.freeze        = atc260x_spi_suspend,
+	.freeze_late   = atc260x_spi_suspend_late,
+	.thaw_early    = atc260x_spi_resume_early,
+	.thaw          = atc260x_spi_resume,
+	.poweroff      = atc260x_spi_suspend,
+	.poweroff_late = atc260x_spi_suspend_late,
+	.restore_early = atc260x_spi_resume_early,
+	.restore       = atc260x_spi_resume,
+};
+
+static struct spi_driver atc260x_spi_driver = {
+	.probe      = atc260x_spi_probe,
+	.remove     = atc260x_spi_remove,
+	.driver = {
+		.name   = "atc260x_spi",
+		.owner  = THIS_MODULE,
+		.pm     = &s_atc260x_spi_pm_ops,
+		.of_match_table = of_match_ptr(atc2603_spi_of_match),
+	},
+};
+
+static int __init atc260x_spi_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&atc260x_spi_driver);
+	if (ret != 0)
+		pr_err("Failed to register atc260x SPI driver: %d\n", ret);
+	return ret;
+}
+subsys_initcall(atc260x_spi_init);
+
+static void __exit atc260x_spi_exit(void)
+{
+	spi_unregister_driver(&atc260x_spi_driver);
+}
+module_exit(atc260x_spi_exit);
+
+MODULE_DESCRIPTION("SPI support for atc260x PMICs");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Actions Semi.");
diff --git a/drivers/mfd/atc260x-subdev.h b/drivers/mfd/atc260x-subdev.h
new file mode 100755
index 0000000..adcdbde
--- /dev/null
+++ b/drivers/mfd/atc260x-subdev.h
@@ -0,0 +1,661 @@
+/* This is ugly,
+ * but we need to move these long list away from the core,
+ * to make code clean and readable. */
+
+#ifdef __MFD_ATC260X__NEED_SUB_DEV_DEFINE__
+
+#ifndef __MFD_ATC260X_SUB_DEV_DEF_H__
+#define __MFD_ATC260X_SUB_DEV_DEF_H__
+
+
+/* --------------  ATC2603A ------------------------------------------------- */
+static const struct resource atc2603a_onoff_resources[] = {
+	{
+		.start = ATC2603A_IRQ_ONOFF,
+		.end   = ATC2603A_IRQ_ONOFF,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2603a_irkey_resources[] = {
+        {
+                .start = ATC2603A_IRQ_IR,
+                .end   = ATC2603A_IRQ_IR,
+                .flags = IORESOURCE_IRQ,
+        },
+};
+static const struct resource atc2603a_ethernet_resources[] = {
+	{
+		.start = ATC2603A_IRQ_ETHERNET,
+		.end   = ATC2603A_IRQ_ETHERNET,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2603a_rtc_resources[] = {
+	{
+		.start = ATC2603A_IRQ_ALARM,
+		.end   = ATC2603A_IRQ_ALARM,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2603a_audio_resources[] = {
+	{
+		.start = ATC2603A_IRQ_AUDIO,
+		.end   = ATC2603A_IRQ_AUDIO,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+/* remember to update the declaration in the core.c !!! */
+static const struct mfd_cell sc_atc2603a_cells[] = {
+	/* DCDC */
+	{
+		.id = 1,
+		.name = "atc2603a-dcdc1",
+		.of_compatible = "actions,atc2603a-dcdc1",
+		/* 注意这里的of_compatible是去match DTS的(不是match driver中的那个list),
+		 * 找到的device_node 将赋值到dev->of_node中.
+		 * 驱动probe的时候的of_match还是dev->of_node和driver中的那个of_compatible list
+		 * 两者进行match. */
+	},
+	{
+		.id = 2,
+		.name = "atc2603a-dcdc2",
+		.of_compatible = "actions,atc2603a-dcdc2",
+	},
+	{
+		.id = 3,
+		.name = "atc2603a-dcdc3",
+		.of_compatible = "actions,atc2603a-dcdc3",
+	},
+	{
+		.id = 4,
+		.name = "atc2603a-dcdc4",
+		.of_compatible = "actions,atc2603a-dcdc4",
+	},
+
+	/* LDO */
+	{
+		.id = 1,
+		.name = "atc2603a-ldo1",
+		.of_compatible = "actions,atc2603a-ldo1",
+	},
+	{
+		.id = 2,
+		.name = "atc2603a-ldo2",
+		.of_compatible = "actions,atc2603a-ldo2",
+	},
+	{
+		.id = 3,
+		.name = "atc2603a-ldo3",
+		.of_compatible = "actions,atc2603a-ldo3",
+	},
+	{
+		.id = 4,
+		.name = "atc2603a-ldo4",
+		.of_compatible = "actions,atc2603a-ldo4",
+	},
+	{
+		.id = 5,
+		.name = "atc2603a-ldo5",
+		.of_compatible = "actions,atc2603a-ldo5",
+	},
+	{
+		.id = 6,
+		.name = "atc2603a-ldo6",
+		.of_compatible = "actions,atc2603a-ldo6",
+	},
+	{
+		.id = 7,
+		.name = "atc2603a-ldo7",
+		.of_compatible = "actions,atc2603a-ldo7",
+	},
+	{
+		.id = 8,
+		.name = "atc2603a-ldo8",
+		.of_compatible = "actions,atc2603a-ldo8",
+	},
+	{
+		.id = 9,
+		.name = "atc2603a-ldo9",
+		.of_compatible = "actions,atc2603a-ldo9",
+	},
+	{
+		.id = 10,
+		.name = "atc2603a-ldo10",
+		.of_compatible = "actions,atc2603a-ldo10",
+	},
+	{
+		.id = 11,
+		.name = "atc2603a-ldo11",
+		.of_compatible = "actions,atc2603a-ldo11",
+	},
+
+	/* switches-ldo */
+	{
+		.id = 1,
+		.name = "atc2603a-switch1",
+		.of_compatible = "actions,atc2603a-switch1",
+	},
+	{
+		.id = 2,
+		.name = "atc2603a-switch2",
+		.of_compatible = "actions,atc2603a-switch2",
+	},
+
+	/* External PWM emulated DC-DC */
+	{
+		.id = 1,
+		.name = "atc2603a-ext-pwm-dcdc1",
+		.of_compatible = "actions,atc2603a-ext-pwm-dcdc1",
+	},
+	{
+		.id = 2,
+		.name = "atc2603a-ext-pwm-dcdc2",
+		.of_compatible = "actions,atc2603a-ext-pwm-dcdc2",
+	},
+
+	/* On/Off key */
+	{
+		.name = "atc2603a-onoff",
+		.of_compatible = "actions,atc2603a-onoff",
+		.num_resources = ARRAY_SIZE(atc2603a_onoff_resources),
+		.resources = atc2603a_onoff_resources,
+	},
+
+	/* Ethernet phy */
+	{
+		.name = "atc2603a-ethernet",
+		.of_compatible = "actions,atc2603a-ethernet",
+		.num_resources = ARRAY_SIZE(atc2603a_ethernet_resources),
+		.resources = atc2603a_ethernet_resources,
+	},
+
+	/* RTC */
+	{
+		.name = "atc2603a-rtc",
+		.of_compatible = "actions,atc2603a-rtc",
+		.num_resources = ARRAY_SIZE(atc2603a_rtc_resources),
+		.resources = atc2603a_rtc_resources,
+	},
+
+	/* GPIO */
+	{
+		.name = "atc2603a-gpio",
+		.of_compatible = "actions,atc2603a-gpio",
+	},
+
+	/* ADC keypad */
+	{
+		.name = "atc2603a-adckeypad",
+		.of_compatible = "actions,atc2603a-adckeypad",
+	},
+
+    /* IR keypad */
+    {
+        .name = "atc2603a-irkeypad",
+        .of_compatible = "actions,atc2603a-irkeypad",
+        .num_resources = ARRAY_SIZE(atc2603a_irkey_resources),
+        .resources = atc2603a_irkey_resources,
+    },
+    
+  /* pwm */
+	{
+		.name = "atc2603a-pwm",
+		.of_compatible = "actions,atc2603a-pwm",
+	},
+
+	/* Audio */
+	{
+		.name = "atc2603a-audio",
+		.of_compatible = "actions,atc2603a-audio",
+		.num_resources = ARRAY_SIZE(atc2603a_audio_resources),
+		.resources = atc2603a_audio_resources,
+	},
+
+	/* Hardware monitor */
+	{
+		.name = "atc2603a-hwmon",
+		.of_compatible = "actions,atc2603a-hwmon",
+	},
+
+	/* Power */
+	{
+		.name = "atc2603a-power",
+		.of_compatible = "actions,atc2603a-power",
+	},
+
+	/* Power Cap Gauge */
+	{
+		.name = "atc2603a-cap-gauge",
+		.of_compatible = "actions,atc2603a-cap-gauge",
+	},
+
+	/* Suspend / Wakeup */
+	{
+		.name = "atc2603a-pm",
+		.of_compatible = "actions,atc2603a-pm",
+	},
+};
+
+
+/* --------------  ATC2603C ------------------------------------------------- */
+static const struct resource atc2603c_onoff_resources[] = {
+	{
+		.start = ATC2603C_IRQ_ONOFF,
+		.end   = ATC2603C_IRQ_ONOFF,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2603c_irkey_resources[] = {
+    {
+        .start = ATC2603C_IRQ_IR,
+        .end   = ATC2603C_IRQ_IR,
+        .flags = IORESOURCE_IRQ,
+    },
+};
+static const struct resource atc2603c_rtc_resources[] = {
+	{
+		.start = ATC2603C_IRQ_ALARM,
+		.end   = ATC2603C_IRQ_ALARM,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2603c_sgpio_resources[] = {
+	{
+		.start = ATC2603C_IRQ_SGPIO,
+		.end   = ATC2603C_IRQ_SGPIO,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2603c_audio_resources[] = {
+	{
+		.start = ATC2603C_IRQ_AUDIO,
+		.end   = ATC2603C_IRQ_AUDIO,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static const struct mfd_cell sc_atc2603c_cells[] = {
+	/* DCDC */
+	{
+		.id = 1,
+		.name = "atc2603c-dcdc1",
+		.of_compatible = "actions,atc2603c-dcdc1",
+	},
+	{
+		.id = 2,
+		.name = "atc2603c-dcdc2",
+		.of_compatible = "actions,atc2603c-dcdc2",
+	},
+	{
+		.id = 3,
+		.name = "atc2603c-dcdc3",
+		.of_compatible = "actions,atc2603c-dcdc3",
+	},
+
+	/* LDO */
+	{
+		.id = 1,
+		.name = "atc2603c-ldo1",
+		.of_compatible = "actions,atc2603c-ldo1",
+	},
+	{
+		.id = 2,
+		.name = "atc2603c-ldo2",
+		.of_compatible = "actions,atc2603c-ldo2",
+	},
+	{
+		.id = 3,
+		.name = "atc2603c-ldo3",
+		.of_compatible = "actions,atc2603c-ldo3",
+	},
+	{
+		.id = 5,
+		.name = "atc2603c-ldo5",
+		.of_compatible = "actions,atc2603c-ldo5",
+	},
+	{
+		.id = 6,
+		.name = "atc2603c-ldo6",
+		.of_compatible = "actions,atc2603c-ldo6",
+	},
+	{
+		.id = 7,
+		.name = "atc2603c-ldo7",
+		.of_compatible = "actions,atc2603c-ldo7",
+	},
+	{
+		.id = 8,
+		.name = "atc2603c-ldo8",
+		.of_compatible = "actions,atc2603c-ldo8",
+	},
+	{
+		.id = 11,
+		.name = "atc2603c-ldo11",
+		.of_compatible = "actions,atc2603c-ldo11",
+	},
+
+	/* switches-ldo */
+	{
+		.id = 1,
+		.name = "atc2603c-switch1",
+		.of_compatible = "actions,atc2603c-switch1",
+	},
+
+	/* External PWM emulated DC-DC */
+	{
+		.id = 1,
+		.name = "atc2603c-ext-pwm-dcdc1",
+		.of_compatible = "actions,atc2603c-ext-pwm-dcdc1",
+	},
+	{
+		.id = 2,
+		.name = "atc2603c-ext-pwm-dcdc2",
+		.of_compatible = "actions,atc2603c-ext-pwm-dcdc2",
+	},
+
+	/* On/Off key */
+	{
+		.name = "atc2603c-onoff",
+		.of_compatible = "actions,atc2603c-onoff",
+		.num_resources = ARRAY_SIZE(atc2603c_onoff_resources),
+		.resources = atc2603c_onoff_resources,
+	},
+
+	/* RTC */
+	{
+		.name = "atc2603c-rtc",
+		.of_compatible = "actions,atc2603c-rtc",
+		.num_resources = ARRAY_SIZE(atc2603c_rtc_resources),
+		.resources = atc2603c_rtc_resources,
+	},
+
+	/* GPIO */
+	{
+		.name = "atc2603c-gpio",
+		.of_compatible = "actions,atc2603c-gpio",
+	},
+
+	/* SGPIO */
+	{
+		.name = "atc2603c-sgpio",
+		.of_compatible = "actions,atc2603c-sgpio",
+		.num_resources = ARRAY_SIZE(atc2603c_sgpio_resources),
+		.resources = atc2603c_sgpio_resources,
+	},
+
+	/* ADC keypad */
+	{
+		.name = "atc2603c-adckeypad",
+		.of_compatible = "actions,atc2603c-adckeypad",
+	},
+
+    /* IR keypad */
+    {
+        .name = "atc2603c-irkeypad",
+        .of_compatible = "actions,atc2603c-irkeypad",
+        .num_resources = ARRAY_SIZE(atc2603c_irkey_resources),
+        .resources = atc2603c_irkey_resources,
+    },
+    
+  /* pwm */
+	{
+		.name = "atc2603c-pwm",
+		.of_compatible = "actions,atc2603c-pwm",
+	},
+
+	/* Audio */
+	{
+		.name = "atc2603c-audio",
+		.of_compatible = "actions,atc2603c-audio",
+		.num_resources = ARRAY_SIZE(atc2603c_audio_resources),
+		.resources = atc2603c_audio_resources,
+	},
+
+	/* Hardware monitor */
+	{
+		.name = "atc2603c-hwmon",
+		.of_compatible = "actions,atc2603c-hwmon",
+	},
+
+	/* Power */
+	{
+		.name = "atc2603c-power",
+		.of_compatible = "actions,atc2603c-power",
+	},
+
+	/* Power Cap Gauge */
+	{
+		.name = "atc2603c-cap-gauge",
+		.of_compatible = "actions,atc2603c-cap-gauge",
+	},
+
+	/* Suspend / Wakeup */
+	{
+		.name = "atc2603c-pm",
+		.of_compatible = "actions,atc2603c-pm",
+	},
+};
+
+
+
+
+
+/* --------------  ATC2609A ------------------------------------------------- */
+static const struct resource atc2609a_onoff_resources[] = {
+	{
+		.start = ATC2609A_IRQ_ONOFF,
+		.end   = ATC2609A_IRQ_ONOFF,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2609a_irkey_resources[] = {
+    {
+        .start = ATC2609A_IRQ_IR,
+        .end   = ATC2609A_IRQ_IR,
+        .flags = IORESOURCE_IRQ,
+    },
+};
+static const struct resource atc2609a_rtc_resources[] = {
+	{
+		.start = ATC2609A_IRQ_ALARM,
+		.end   = ATC2609A_IRQ_ALARM,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+static const struct resource atc2609a_audio_resources[] = {
+	{
+		.start = ATC2609A_IRQ_AUDIO,
+		.end   = ATC2609A_IRQ_AUDIO,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static const struct mfd_cell sc_atc2609a_cells[] = {
+	/* DCDC */
+	{
+		.id = 0,
+		.name = "atc2609a-dcdc0",
+		.of_compatible = "actions,atc2609a-dcdc0",
+	},
+	{
+		.id = 1,
+		.name = "atc2609a-dcdc1",
+		.of_compatible = "actions,atc2609a-dcdc1",
+	},
+	{
+		.id = 2,
+		.name = "atc2609a-dcdc2",
+		.of_compatible = "actions,atc2609a-dcdc2",
+	},
+	{
+		.id = 3,
+		.name = "atc2609a-dcdc3",
+		.of_compatible = "actions,atc2609a-dcdc3",
+	},
+	{
+		.id = 4,
+		.name = "atc2609a-dcdc4",
+		.of_compatible = "actions,atc2609a-dcdc4",
+	},
+
+	/* LDO */
+	{
+		.id = 0,
+		.name = "atc2609a-ldo0",
+		.of_compatible = "actions,atc2609a-ldo0",
+	},
+	{
+		.id = 1,
+		.name = "atc2609a-ldo1",
+		.of_compatible = "actions,atc2609a-ldo1",
+	},
+	{
+		.id = 2,
+		.name = "atc2609a-ldo2",
+		.of_compatible = "actions,atc2609a-ldo2",
+	},
+	{
+		.id = 3,
+		.name = "atc2609a-ldo3",
+		.of_compatible = "actions,atc2609a-ldo3",
+	},
+	{
+		.id = 4,
+		.name = "atc2609a-ldo4",
+		.of_compatible = "actions,atc2609a-ldo4",
+	},
+	{
+		.id = 5,
+		.name = "atc2609a-ldo5",
+		.of_compatible = "actions,atc2609a-ldo5",
+	},
+	{
+		.id = 6,
+		.name = "atc2609a-ldo6",
+		.of_compatible = "actions,atc2609a-ldo6",
+	},
+	{
+		.id = 7,
+		.name = "atc2609a-ldo7",
+		.of_compatible = "actions,atc2609a-ldo7",
+	},
+	{
+		.id = 8,
+		.name = "atc2609a-ldo8",
+		.of_compatible = "actions,atc2609a-ldo8",
+	},
+	{
+		.id = 9,
+		.name = "atc2609a-ldo9",
+		.of_compatible = "actions,atc2609a-ldo9",
+	},
+
+	/* 2609a has no swtich-ldo */
+
+	/* External PWM emulated DC-DC */
+	{
+		.id = 1,
+		.name = "atc2609a-ext-pwm-dcdc1",
+		.of_compatible = "actions,atc2609a-ext-pwm-dcdc1",
+	},
+	{
+		.id = 2,
+		.name = "atc2609a-ext-pwm-dcdc2",
+		.of_compatible = "actions,atc2609a-ext-pwm-dcdc2",
+	},
+
+	/* On/Off key */
+	{
+		.name = "atc2609a-onoff",
+		.of_compatible = "actions,atc2609a-onoff",
+		.num_resources = ARRAY_SIZE(atc2609a_onoff_resources),
+		.resources = atc2609a_onoff_resources,
+	},
+
+	/* RTC */
+	{
+		.name = "atc2609a-rtc",
+		.of_compatible = "actions,atc2609a-rtc",
+		.num_resources = ARRAY_SIZE(atc2609a_rtc_resources),
+		.resources = atc2609a_rtc_resources,
+	},
+
+	/* GPIO */
+	{
+		.name = "atc2609a-gpio",
+		.of_compatible = "actions,atc2609a-gpio",
+	},
+
+	/* ADC keypad */
+	{
+		.name = "atc2609a-adckeypad",
+		.of_compatible = "actions,atc2609a-adckeypad",
+	},
+
+    /* IR keypad */
+    {
+        .name = "atc2609a-irkeypad",
+        .of_compatible = "actions,atc2609a-irkeypad",
+        .num_resources = ARRAY_SIZE(atc2609a_irkey_resources),
+        .resources = atc2609a_irkey_resources,
+    },
+    
+  /* pwm */
+	{
+		.name = "atc2609a-pwm",
+		.of_compatible = "actions,atc2609a-pwm",
+	},
+
+	/* Audio */
+	{
+		.name = "atc2609a-audio",
+		.of_compatible = "actions,atc2609a-audio",
+		.num_resources = ARRAY_SIZE(atc2609a_audio_resources),
+		.resources = atc2609a_audio_resources,
+	},
+
+	/* Hardware monitor */
+	{
+		.name = "atc2609a-hwmon",
+		.of_compatible = "actions,atc2609a-hwmon",
+	},
+
+	/* Power */
+	{
+		.name = "atc2609a-power",
+		.of_compatible = "actions,atc2609a-power",
+	},
+
+	/* Power Cap Gauge */
+	{
+		.name = "atc2609a-cap-gauge",
+		.of_compatible = "actions,atc2609a-cap-gauge",
+	},
+
+	/* Suspend / Wakeup */
+	{
+		.name = "atc2609a-pm",
+		.of_compatible = "actions,atc2609a-pm",
+	},
+};
+
+
+
+
+static const struct mfd_cell * const sc_atc260x_mfd_cell_def_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = sc_atc2603a_cells,
+	[ATC260X_ICTYPE_2603C] = sc_atc2603c_cells,
+	[ATC260X_ICTYPE_2609A] = sc_atc2609a_cells,
+};
+
+static const u16 sc_atc260x_mfd_cell_cnt_tbl[ATC260X_ICTYPE_CNT] = {
+	[ATC260X_ICTYPE_2603A] = ARRAY_SIZE(sc_atc2603a_cells),
+	[ATC260X_ICTYPE_2603C] = ARRAY_SIZE(sc_atc2603c_cells),
+	[ATC260X_ICTYPE_2609A] = ARRAY_SIZE(sc_atc2609a_cells),
+};
+
+
+#endif /* __MFD_ATC260X_SUB_DEV_DEF_H__ */
+
+#endif
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
old mode 100644
new mode 100755
index 4091fb0..40f331a
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -162,6 +162,13 @@ config BATTERY_BQ27x00
 	depends on I2C || I2C=n
 	help
 	  Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips.
+	  
+config BATTERY_BQ27441
+	tristate "BQ27441 battery driver for Actions"
+	depends on I2C || I2C=n
+	depends on CHARGER_DRV_ATC206X
+	help
+	  Say Y here to enable support for batteries with BQ27441 (I2C) chips.
 
 config BATTERY_BQ27X00_I2C
 	bool "BQ27200/BQ27500 support"
@@ -431,6 +438,19 @@ config BATTERY_GOLDFISH
 	  Say Y to enable support for the battery and AC power in the
 	  Goldfish emulator.
 
+config CHARGER_DRV_ATC206X
+	tristate "actions ATC260x PMIC CHARGER"
+	depends on MFD_ATC260X
+	help
+	  If you say yes here you will get support for the CHARGER module
+	  of the actions atc260x series PMICs.
+config ATC260X_CAP_GAUGE
+	tristate "actions ATC260x GAUGE"
+	depends on MFD_ATC260X
+	help
+	  If you say yes here you will get support for the CHARGER module
+	  of the actions atc260x series PMICs.
+
 config BATTERY_RT5033
 	tristate "RT5033 fuel gauge support"
 	depends on MFD_RT5033
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
old mode 100644
new mode 100755
index b7b0181..7b9a44b
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)	+= wm97xx_battery.o
 obj-$(CONFIG_BATTERY_SBS)	+= sbs-battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)	+= bq27x00_battery.o
+obj-$(CONFIG_BATTERY_BQ27441)	+= bq27441_battery.o
 obj-$(CONFIG_BATTERY_DA9030)	+= da9030_battery.o
 obj-$(CONFIG_BATTERY_DA9052)	+= da9052-battery.o
 obj-$(CONFIG_CHARGER_DA9150)	+= da9150-charger.o
@@ -64,3 +65,5 @@ obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090)	+= tps65090-charger.o
 obj-$(CONFIG_POWER_RESET)	+= reset/
 obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
+obj-$(CONFIG_CHARGER_DRV_ATC206X)	+= atc260x_power/
+obj-$(CONFIG_ATC260X_CAP_GAUGE) += atc260x_cap_gauge.o
\ No newline at end of file
diff --git a/drivers/power/atc260x_cap_gauge.c b/drivers/power/atc260x_cap_gauge.c
new file mode 100755
index 0000000..fe5f7cb
--- /dev/null
+++ b/drivers/power/atc260x_cap_gauge.c
@@ -0,0 +1,3074 @@
+/*
+ * atc260x_cap_gauge.c  --  fuel gauge  driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
+#include <linux/kfifo.h>
+#include <linux/rtc.h>
+
+#include <linux/inotify.h>  
+#include <linux/suspend.h>
+#include <linux/mutex.h>
+#include <linux/reboot.h>
+#include <asm/div64.h>
+
+#include <linux/list.h>
+#include <mach/gpio.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+#include <mach/power.h>
+#include <linux/earlysuspend.h>
+#include <linux/mfd/atc260x/atc260x.h>
+/* If defined, enable printk. */
+//#define DEBUG
+#ifdef DEBUG
+#define GAUGE_DBG(format, arg...)       \
+	 printk(format , ## arg)
+#else
+#define GAUGE_DBG(format, arg...)       \
+	 do {} while (0)
+#endif
+
+#define GAUGE_ERR(...)				printk(KERN_ERR "ATC260X_GAUGE: " __VA_ARGS__);
+#define GAUGE_WARNING(...)		printk(KERN_WARNING "ATC260X_GAUGE: " __VA_ARGS__);
+#define GAUGE_NOTICE(...)			printk(KERN_NOTICE "ATC260X_GAUGE: " __VA_ARGS__);
+#define GAUGE_INFO(...)			printk(KERN_INFO "ATC260X_GAUGE: " __VA_ARGS__);
+
+/*rtc*/
+#define     RTC_H_H_SHIFT               		(0)
+#define     RTC_H_H_MASK                		(0x1f << RTC_H_H_SHIFT)
+#define     RTC_MS_M_SHIFT             		(6)
+#define     RTC_MS_M_MASK               		(0x3f << RTC_MS_M_SHIFT)
+#define     RTC_MS_S_SHIFT             		(0)
+#define     RTC_MS_S_MASK               		(0x3f << RTC_MS_S_SHIFT)
+#define     RTC_H_H(h)                  			(((h) & RTC_H_H_MASK) >> RTC_H_H_SHIFT)
+#define     RTC_H_VAL(h)                			(((h) << RTC_H_H_SHIFT))
+#define     RTC_MS_M(ms)                		(((ms) & RTC_MS_M_MASK) >> RTC_MS_M_SHIFT)
+#define     RTC_MS_S(ms)                		(((ms) & RTC_MS_S_MASK) >> RTC_MS_S_SHIFT)
+#define     RTC_MS_VAL(m, s)            		(((m) << RTC_MS_M_SHIFT) | ((s) << RTC_MS_S_SHIFT))
+
+/*ATC2603C_PMU_ICMADC*/
+#define PMU_ICMADC_MASK (0x7ff )
+#define PMU_ICMADC_SIGN_BIT (1 << 10 )
+#define ADC_LSB_FOR_10mohm (1144)/*mA*/
+#define ADC_LSB_FOR_20mohm (572)/*mA*/
+
+#define PMU_VER_ABC_RATIO			3
+#define PMU_VER_D_RATIO				4
+#define PMU_CUR_RATIO_BASE			3
+
+
+/*current threshold*/
+#define CHARGE_CURRENT_THRESHOLD			(60)/*ma*/
+#define DISCHARGE_CURRENT_THRESHOLD		(30)/*ma*/
+
+/*full charge, full discharge*/
+#define FULL_CHARGE_SOC						(100000)
+#define EMPTY_DISCHARGE_SOC				(0)
+
+/*adc up and down float value*/
+#define BAT_VOL_VARIANCE_THRESHOLD		(20)/*mv*/
+#define BAT_CUR_VARIANCE_THRESHOLD		(100)/*ma*/
+
+/*update resistor, batv range */
+#define BATV_RESISTOR_MIN					(3600)/*mv*/
+#define BATV_RESISTOR_MAX					(4000)/*mv*/
+
+/*update resistor batv threshold value, when discharging*/
+#define UPDATE_RESISTOR_VOL_DIFF_THRESHOLD		(70)/*mv*/
+
+#define FULL_CHARGE_OCV						(4180)/*mv*/
+#define CONST_ROUNDING_500					(500)
+#define CONST_ROUNDING						(1000)
+
+/*added by cxj at 2014-11-26:the max number of current or voltage samples*/
+#define SAMPLES_COUNT                20
+
+#define SOC_THRESHOLD 				1000
+#define NOT_BEYOND_SOC_THRESHOLD_TIME	 30
+
+#define SAMPLE_OUT 1
+#define SAMPLE_TIME 120
+
+
+#define TERMINAL_VOL_ADD 50
+
+enum REG_TYPE
+{
+	PMU_SYS_CTL9,
+	PMU_OV_INT_EN,
+	PMU_BATIADC,
+	PMU_IREFADC,
+	PMU_ICMADC,
+	RTC_MS,
+	RTC_H,
+};
+
+enum INFO_TYPE
+{
+	CURRENT_TYPE,
+	VOLTAGE_TYPE,
+};
+
+enum RSENSE_VALUE
+{
+	RSENSE_10mohm = 10,
+	RSENSE_20mohm = 20
+};
+
+
+/**
+ * dts_config_items - dts config items information.user can change these items to meet their
+ *                            need.
+ * @ capacity : nominal chemical capacity.
+ * @ rsense : rsense resistor value(mohm).
+ * @ taper_vol : the voltage that is colsed to full charge.
+ * @ taper_cur : the current that is colsed to full charge.
+ * @ stop_vol : the minimum voltage that tablet works normally.
+ * @ print_switch : whether if turn on print switch or not, if true, then print to screen every interval.
+ * @ log_switch : whether if turn on log switch or not, if true the log will save in sdcard.
+ */
+struct dts_config_items
+{
+	int capacity;
+	int icm_available;
+	int rsense;
+	int taper_vol;
+	int taper_current;
+	int terminal_vol;
+	int min_over_chg_protect_voltage;
+	int suspend_current;
+	int shutdown_current;
+	int print_switch;
+	int log_switch;
+	
+	int ocv_soc_config;
+};
+
+/**
+ * data_group -  divide into  groups for gathered battery info, current/voltage.
+ * @ index : the head index of every group.
+ * @ num : the number of every group member.
+ * @ count : the number of valid data within the group.
+ * @ sum : the sum of all the data within the group.
+ * @ avr_data : the average value of this group.
+ *
+ * data of every group have similar range, not excessive shaking.
+ */
+struct data_group
+{
+	int index;
+	int num;
+	int count;
+	int sum;
+	int avr_data;
+};
+
+/**
+  * kfifo_data - the data will store into kfifo.
+  * @ bat_vol : battery voltage.
+  * @ bat_curr : battery current.
+  * @ timestamp : current time stamp.
+  */
+struct kfifo_data
+{
+	int bat_vol;
+	int bat_curr;
+	struct timeval timestamp;
+};
+
+/**
+ * atc260x_gauge_info - atc260x soft fuel gauge information
+ *
+ * @ atc260x : come from parent device, atc260x.
+ * @ node : device node, in order to get property from dts file.
+ * @ cfg_items : config items from dts file.
+ * @ lock : prevent the concurrence for soc reading and soc writting.
+ * @ firtst_product : whether if the first product.
+ * @ current_ratio : current ratio depend on pmu version, if atc2603a abc, current ratio is 4, 
+ *     othes version current ratio is 3.
+ * @ ch_resistor : battery impedence, dc resistor mainly, under charging. 
+ * @ disch_resistor : battery impedence, dc resistor mainly, under discharging.
+ * @ curr_buf : save gathered battery current, charge/discharge.
+ * @ vol_buf : save gathered battery voltage, charge/discharge.
+ * @ ibatt_avg : the average value of curr_buf.
+ * @ vbatt_avg : the average value of vol_buf.
+ * @ status : there are 3 charge status:CHARGING, DISCHARGING, 
+ * 			NO_CHARGEING_NO_DISCHARGING.
+ * @ ocv : the open circut voltage of battery.
+ * @ ocv_stop : the open circut voltage of battery, corresponding with terminal voltage.  
+ * @ soc_last : save last soc.
+ * @ soc_now : current  state of charge.
+ * @ soc_real : the calclated state of charge really.
+ * @ soc_filter : the state of charge filtered by soc_now and soc_real.
+ * @ soc_show : the state of charge showing in UI, substracting down_step by soc_now.
+ * @ soc_ref : the state of charge corresponding with batv.
+ * @ index : indicate the position of soc_queue.
+ * @ wq : create work queue for atc260x fuel gauge only.
+ * @ work : be responsibe to update battery capacity.
+ * @ interval : delayed work poll time interval.
+ */
+struct atc260x_gauge_info 
+{
+	struct atc260x_dev *atc260x;
+	struct device_node *node;
+	struct dts_config_items cfg_items;
+
+	struct mutex lock;
+	int store_gauge_info_switch_sav;
+	bool first_product;
+	int current_ratio;
+	int ch_resistor;
+	int disch_resistor;
+	bool dich_r_change;
+	
+	int curr_buf[SAMPLES_COUNT];
+	int vol_buf[SAMPLES_COUNT];
+	int ibatt_avg;
+	int vbatt_avg;
+
+	int status;
+
+	int ocv;
+	int ocv_stop; 
+	
+	struct kfifo fifo; 
+	struct kfifo down_curve;
+	struct kfifo temp_down_curve;
+	
+	int soc_last;
+	int soc_now;
+	int soc_real;
+	int soc_filter;
+	int soc_ref;
+	int soc_show;
+
+#ifdef GAUGE_SOC_AVERAGE	
+	int index;
+	soc_queue[5];
+#endif
+	struct workqueue_struct *wq;
+	struct delayed_work work;
+	int interval;
+	
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend	early_suspend;
+#endif
+
+	 int (*filter_algorithm)(int *buf,  int len, int type);
+
+};
+
+extern void act260x_set_get_cap_point(void *ptr);
+extern void act260x_set_get_volt_point(void *ptr);
+extern void act260x_set_get_cur_point(void *ptr);
+
+extern int atc260x_set_charger_current(int new, int *old);
+extern void atc260x_charger_turn_off_force(void);
+extern void atc260x_charger_turn_on_force(void);
+extern bool atc260x_get_charge_status(void);
+extern int get_chg_current_now(void);
+
+static struct atc260x_gauge_info *global_gauge_info_ptr = NULL;
+static int first_store_gauge_info;
+static int taper_interval;
+static int ic_type;
+static struct timeval current_tick;
+static int ch_resistor_calced = 0;
+/*whether if board layout preserved cm related or not*/
+static bool board_has_cm = false;
+static bool icm_detect_loop;
+static int icm_detect_cycle = 0;
+static bool test_icm_enable = false;
+/*added by cxj @2014-12-04*/
+static int cost_time = 0;
+/*added by cxj @2014-12-10*/
+static bool resume_flag = false;
+/* for full power schedule*/
+bool full_power_dealing = false;
+int next_do_count = 0;
+int cycle_count = 0;
+/*for suspend consume*/
+int pre_charge_status;
+static int first_calc_resistor = -1;
+
+
+static int reg_offset_atc2603a[] =
+{	
+	0x09, /*0:PMU_SYS_CTL9*/
+	0x2e, /*1:PMU_OV_INT_EN*/
+	0x41, /*2:PMU_BATIADC*/
+	0x4a, /*3:PMU_IREFADC*/
+	0xff, /*4:NULL*/
+	0x56, /*5:RTC_MS*/
+	0x57, /*6:RTC_H*/
+};
+
+static int reg_offset_atc2603c[] =
+{
+	0x09, /*0:PMU_SYS_CTL9*/
+	0x2e, /*1:PMU_OV_INT_EN*/
+	0x41, /*2:PMU_BATIADC*/
+	0x4a, /*3:PMU_IREFADC*/
+	0x50, /*4:PMU_ICMADC*/
+	0x56, /*5:RTC_MS*/    /*modified by cxj @20141118:wrong address*/
+	0x57, /*6:RTC_H*/
+};
+
+
+/**
+ *  ocv_soc_table : ocv soc mapping table 
+ */
+static  int ocv_soc_table[][2] =
+{
+	{3477, 1}, {3534, 2}, {3591, 3}, {3624, 4}, {3637, 5}, 
+	{3649, 6}, {3661, 7},{3667, 8}, {3673, 9},{3677, 10}, 
+	{3682, 11}, {3685, 12}, {3690, 13}, {3693, 14}, {3700, 15}, 
+	{3706, 16}, {3712, 17}, {3716, 18}, {3722, 19}, {3728, 20},
+	{3732, 21}, {3736, 22}, {3739, 23}, {3744, 24}, {3747, 25}, 
+	{3751, 26}, {3755, 27}, {3758, 28}, {3761, 29}, {3765, 30},
+	{3768, 31}, {3771, 32}, {3775, 33}, {3777, 34}, {3782, 35}, 
+	{3784, 36}, {3788, 37}, {3791, 38}, {3793, 39}, {3794, 40},
+	{3800, 41}, {3801, 42}, {3804, 43}, {3807, 44}, {3812, 45}, 
+	{3815, 46}, {3819, 47}, {3823, 48}, {3825, 49}, {3830, 50},
+	{3834, 51}, {3838, 52}, {3841, 53}, {3845, 54}, {3850, 55}, 
+	{3854, 56}, {3858, 57}, {3864, 58}, {3870, 59}, {3874, 60},
+	{3880, 61}, {3889, 62}, {3895, 63}, {3902, 64}, {3908, 65}, 
+	{3916, 66}, {3926, 67}, {3933, 68}, {3940, 69}, {3947, 70},
+	{3954, 71}, {3961, 72}, {3968, 73}, {3972, 74}, {3979, 75}, 
+	{3985, 76}, {3992, 77}, {3997, 78}, {4005, 79}, {4012, 80},
+	{4019, 81}, {4028, 82}, {4036, 83}, {4046, 84}, {4054, 85}, 
+	{4061, 86}, {4068, 87}, {4075, 88}, {4084, 89}, {4090, 90},
+	{4099, 91}, {4107, 92}, {4115, 93}, {4126, 94}, {4132, 95}, 
+	{4141, 96}, {4152, 97}, {4160, 98}, {4170, 99}, {4180, 100},
+};  
+
+#define TABLE_LEN 10
+static int ocv_soc_table_config[100][2];
+static int ocv_soc_table_init(struct atc260x_gauge_info * info)
+{
+	int i,j;
+	int ret;
+	u32 ocv[TABLE_LEN];
+	int config_items_count,item_number;
+	char *config_node_name;
+	char temp_item_number[3];
+	char *src_item_number;
+	temp_item_number[2] = '\0';
+	
+	for(config_items_count = 0; config_items_count < 10; config_items_count++)
+	{
+		char start_item_name[11]= "ocv_soc_";
+		config_node_name = start_item_name;	
+		
+		item_number = config_items_count*10;
+		temp_item_number[0] = item_number/10+'0';
+		temp_item_number[1] = item_number%10+'0';
+		src_item_number = temp_item_number;
+		
+		config_node_name = strcat(start_item_name, src_item_number);
+		
+		ret = of_property_read_u32_array(info->node,config_node_name,ocv,TABLE_LEN);
+		if(ret)
+		{
+			GAUGE_ERR("get ocv from dts fail!!\n");
+			return 0;
+		}
+		for(i = config_items_count*10, j = 0; i < config_items_count*10+10; i++,j++)
+		{
+			ocv_soc_table_config[i][0] = ocv[j];
+			ocv_soc_table_config[i][1] = i+1;
+			GAUGE_DBG("%d  %d\n",ocv_soc_table_config[i][0],ocv_soc_table_config[i][1]);
+		}
+	}
+	info->cfg_items.ocv_soc_config = 1;
+	return 1;
+}
+
+
+/* added by cxj @20141010 */
+static int get_pmu_ic_type(struct atc260x_gauge_info *gauge)
+{
+	int pmu_type;
+	pmu_type = atc260x_get_ic_type(gauge->atc260x);
+	GAUGE_INFO("[%s]pmu type is %d\n", __func__,pmu_type);
+	
+	return pmu_type;
+}
+
+/* added by cxj @20141010 */
+static int atc260x_get_version(struct atc260x_gauge_info *gauge)
+{
+	int pmu_version;
+	pmu_version = atc260x_get_ic_ver(gauge->atc260x);
+	GAUGE_INFO("[%s]pmu version is %d\n",__func__,pmu_version);
+	return pmu_version;
+}
+static int gauge_reg_read(struct atc260x_dev *atc260x, unsigned short reg)
+{
+	int value = -EINVAL;
+
+	if (reg == 0xff)
+	{
+		GAUGE_ERR("[%s]register reading err!\n",__func__);
+		return value;
+	}	
+	
+	if (ic_type == ATC260X_ICTYPE_2603A)
+		value = atc260x_reg_read(atc260x, reg_offset_atc2603a[reg]);
+	else if (ic_type == ATC260X_ICTYPE_2603C)
+		value = atc260x_reg_read(atc260x, reg_offset_atc2603c[reg]);
+	else
+		GAUGE_WARNING("we do not support this ic type!\n");
+		
+	return value ;
+}
+
+static int gauge_reg_write(struct atc260x_dev *atc260x, unsigned short reg,
+             unsigned short val)
+{
+	int ret = -EINVAL;
+
+	if (reg == 0xff)
+		return ret;
+	
+	if (ic_type == ATC260X_ICTYPE_2603A)
+		ret = atc260x_reg_write(atc260x, reg_offset_atc2603a[reg], val);
+	else if (ic_type == ATC260X_ICTYPE_2603C)
+		ret = atc260x_reg_write(atc260x, reg_offset_atc2603c[reg], val);
+
+	return ret;
+
+}
+
+static int store_batt_info(struct atc260x_gauge_info * info)
+{
+	u8 buf[200];
+	struct file *filp;
+	mm_segment_t fs;
+	int offset = 0;
+	int h, ms;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);	
+
+	filp = filp_open("/mnt/sdcard/cap_gauge_info.log", O_CREAT | O_RDWR, 0644);	  
+	if (IS_ERR(filp)) 
+	{
+		GAUGE_ERR("\n[cap_gauge] can't accessed sd card cap_gauge_info.log, exiting");
+		return 0;
+	}
+
+	if (first_store_gauge_info == 1) 
+	{
+		memset(buf,0,200);
+		offset = sprintf(buf, "time,status,bat_v,bat_i,r_ch,r_disch,bat_ocv,soc_real,soc_now,soc_filter,soc_show,soc_ref,board_has_cm\t\n");
+		filp->f_op->llseek(filp, 0, SEEK_END);
+		filp->f_op->write(filp, (char *)buf, offset + 1, &filp->f_pos);
+		first_store_gauge_info = 0;
+	}
+
+	h = gauge_reg_read(info->atc260x, RTC_H);
+	if (h < 0) 
+	{
+		GAUGE_ERR("\n[cap_gauge] Failed to read reg RTC_H: %d", h);
+		return h;
+	}
+
+	ms = gauge_reg_read(info->atc260x, RTC_MS);
+	if (ms < 0) 
+	{
+		GAUGE_ERR("\n[cap_gauge] Failed to read reg RTC_MS: %d", ms);
+		return ms;
+	}
+
+	memset(buf,0,200);
+
+	offset = sprintf(buf, "%02d:%02d:%02d,%d,%04d,%04d,%04d,%d,%d,%d,%d,%d,%d,%d,%d\t\n",
+	RTC_H_H(h),RTC_MS_M(ms),RTC_MS_S(ms),
+	info->status, 
+	info->vbatt_avg,
+	info->ibatt_avg,
+	info->ch_resistor,
+	info->disch_resistor,
+	info->ocv,
+	info->soc_real,
+	info->soc_now,
+	info->soc_filter,
+	info->soc_show,
+	info->soc_ref,
+	board_has_cm);
+
+	filp->f_op->llseek(filp, 0, SEEK_END);
+	filp->f_op->write(filp, (char *)buf, offset + 1, &filp->f_pos);
+	set_fs(fs);
+	filp_close(filp, NULL);
+
+	return 0;
+}
+
+static int  get_cfg_items(struct atc260x_gauge_info *info)
+{
+	const __be32 *property;
+	int len;
+
+	/*capacity*/
+	property = of_get_property(info->node, "capacity", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.capacity = be32_to_cpup(property);
+		if (info->cfg_items.capacity < 0)
+		{
+			GAUGE_ERR("[%s] cfg_items.capacity =%d \n", __func__, info->cfg_items.capacity);
+			return -EINVAL;
+		}
+	}
+	else
+	{
+		GAUGE_ERR("[%s] cfg_items.capacity not config\n", __func__);
+		return -EINVAL; 
+	}
+	
+	/*icm available*/
+	property = of_get_property(info->node, "icm_available", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.icm_available = be32_to_cpup(property);
+		GAUGE_DBG("[%s]cfg_items.icm_available =%d \n", __func__,  info->cfg_items.icm_available);
+		if(info->cfg_items.icm_available < 0)
+		{
+			GAUGE_ERR("[%s]	cfg_items.icm_available =%d \n", __func__,  info->cfg_items.icm_available);
+			info->cfg_items.icm_available = 0;
+			return -EINVAL;
+		}
+	}
+	else
+	{
+		GAUGE_ERR("[%s] cfg_items.icm_available not config\n", __func__);
+		info->cfg_items.icm_available = 0;
+		return -EINVAL;
+	}
+	
+	/*rsense*/
+	property = of_get_property(info->node, "icm_ohm_val", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.rsense = be32_to_cpup(property);
+		if(info->cfg_items.rsense < 0)
+		{
+			GAUGE_ERR("[%s]	cfg_items.rsense =%d \n", __func__,  info->cfg_items.rsense);
+			return -EINVAL;
+		}
+	}
+	else
+	{
+		GAUGE_ERR("[%s] cfg_items.rsense not config\n", __func__);
+		return -EINVAL;
+	}
+
+	/*taper_vol*/
+	property = of_get_property(info->node, "taper_voltage", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.taper_vol = be32_to_cpup(property);
+		if(info->cfg_items.taper_vol < 0)
+		{
+			GAUGE_WARNING("cfg_items.taper_vol =%d \n", info->cfg_items.taper_vol);
+			info->cfg_items.taper_vol = 4180;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.taper_vol not config\n");
+	}
+	/*taper_current*/
+	property = of_get_property(info->node, "taper_current", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.taper_current = be32_to_cpup(property);
+		if(info->cfg_items.taper_current < 0)
+		{
+			GAUGE_WARNING("cfg_items.taper_current =%d \n", info->cfg_items.taper_current);
+			info->cfg_items.taper_current = info->cfg_items.capacity * 5 / 100;//0.05C
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.taper_current not config\n");
+	}
+	/*terminal_vol*/
+	property = of_get_property(info->node, "terminal_voltage", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.terminal_vol = be32_to_cpup(property);
+		if(info->cfg_items.terminal_vol < 0)
+		{
+			GAUGE_WARNING("cfg_items.terminal_vol =%d \n", info->cfg_items.terminal_vol);
+			info->cfg_items.terminal_vol = 3450;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.terminal_vol not config \n");
+	}
+
+	/* min_over_chg_protect_voltage */
+	property = of_get_property(info->node, "min_over_chg_protect_voltage", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.min_over_chg_protect_voltage = be32_to_cpup(property);
+		if(info->cfg_items.min_over_chg_protect_voltage < 0)
+		{
+			GAUGE_WARNING("cfg_items.min_over_chg_protect_voltage =%d \n", info->cfg_items.min_over_chg_protect_voltage);
+			info->cfg_items.min_over_chg_protect_voltage = 4275;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.min_over_chg_protect_voltage not config \n");
+	}
+	/* shutdown_current */
+	property = of_get_property(info->node, "shutdown_current", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.shutdown_current = be32_to_cpup(property);
+		if(info->cfg_items.shutdown_current < 0)
+		{
+			GAUGE_WARNING("cfg_items.shutdown_current =%d \n", info->cfg_items.shutdown_current);
+			info->cfg_items.shutdown_current = 50;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.shutdown_current not config \n");
+	}
+	/* suspend_current */
+	property = of_get_property(info->node, "suspend_current", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.suspend_current = be32_to_cpup(property);
+		if(info->cfg_items.suspend_current < 0)
+		{
+			GAUGE_WARNING("cfg_items.suspend_current =%d \n", info->cfg_items.suspend_current);
+			info->cfg_items.suspend_current = 50;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.suspend_current not config \n");
+	}
+	/*print_switch*/
+	property = of_get_property(info->node, "print_switch", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.print_switch = be32_to_cpup(property);
+		if(info->cfg_items.print_switch < 0)
+		{
+			GAUGE_WARNING("cfg_items.print_switch =%d \n", info->cfg_items.print_switch);
+			info->cfg_items.print_switch = 0;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.print_switch not config\n");
+	}
+
+	/*log_switch*/
+	property = of_get_property(info->node, "log_switch", &len);
+	if (property && len == sizeof(int))
+	{
+		info->cfg_items.log_switch = be32_to_cpup(property);
+		if(info->cfg_items.log_switch < 0)
+		{
+			GAUGE_WARNING("cfg_items.log_switch =%d \n", info->cfg_items.log_switch);
+			info->cfg_items.log_switch = 0;
+		}
+	}
+	else
+	{
+		GAUGE_WARNING("cfg_items.log_switch not config\n");
+	}
+		
+	return 0;
+}
+
+static int  get_stored_soc(struct atc260x_gauge_info *info)
+{
+	int data; 
+	
+	data = gauge_reg_read(info->atc260x, PMU_SYS_CTL9);
+	if (data & 0x8000)	
+	{
+		data = (data >> 8) & 0x7f;
+		GAUGE_DBG("[%s] get sotred soc:%d\n", __func__, data);
+		return data;
+	}
+	else 
+	{
+		GAUGE_ERR("[%s] not store soc\n", __func__);
+		return -EIO;
+	}
+}
+
+static void store_soc(struct atc260x_gauge_info *info)
+{
+	int data;
+	
+	data = gauge_reg_read(info->atc260x, PMU_SYS_CTL9);
+	data &= 0xff;
+	data |= info->soc_show << 8;
+	data |= 0x8000;
+	gauge_reg_write(info->atc260x, PMU_SYS_CTL9, data);
+}
+/* get the time from year of setting time now (minus 1970)
+*/
+#define GREENWICH_YEAR 1970
+#define ATC260X_RTC_NAME			("rtc0")
+static unsigned long get_time_hour(struct atc260x_gauge_info *info) 
+{
+	struct timeval current_tick;
+	unsigned long tick;
+	struct rtc_time tm;
+	struct rtc_device *rtc;
+	int year,year_gap;
+	
+	rtc = rtc_class_open(ATC260X_RTC_NAME);
+	if (rtc == NULL) {
+		pr_err("%s: unable to open rtc device atc260x\n", __func__);
+		return 0;
+	}
+	rtc_read_time(rtc, &tm);
+	rtc_class_close(rtc);
+	
+	year = tm.tm_year + 1900;
+	GAUGE_DBG("current year = %d\n",year);
+	year_gap = year - GREENWICH_YEAR - 1;
+	
+	do_gettimeofday(&current_tick);
+	GAUGE_DBG("[%s]timeofday =%lu hours\n",__func__,current_tick.tv_sec/3600);
+	tick = current_tick.tv_sec / 3600 - (year_gap * 365 * 24);
+	
+	return tick;
+}
+static void store_time_pre_shutdown(struct atc260x_gauge_info *info)
+{
+	unsigned long systime_hour;
+	systime_hour = get_time_hour(info);
+	GAUGE_DBG("[%s]hours=%lu\n",__func__,systime_hour);
+	/*bit 2~bit15*/
+	systime_hour = (systime_hour << 2) | (gauge_reg_read(info->atc260x, PMU_OV_INT_EN)&(0x3));
+	GAUGE_DBG("[%s]storing time =%lu\n",__func__,systime_hour);
+	gauge_reg_write(info->atc260x, PMU_OV_INT_EN, systime_hour);
+}
+
+static int get_stored_time(struct atc260x_gauge_info *info)
+{
+	int stored_time;
+	
+	stored_time = gauge_reg_read(info->atc260x, PMU_OV_INT_EN);
+	stored_time >>=  2;
+	GAUGE_DBG("[%s]sotred time= %d hours\n",__func__,stored_time);
+	return stored_time;
+}
+
+static bool  first_product(struct atc260x_dev *atc260x)
+{
+	int pmu_sys_ctl9 = gauge_reg_read(atc260x, PMU_SYS_CTL9);
+	GAUGE_DBG("[%s]:pmu_sys_ctl9:0x%x\n", __func__, pmu_sys_ctl9);
+	if (pmu_sys_ctl9 & 0x8000)	
+	{
+		return false;
+	}
+	else 
+	{
+		return true;
+	}
+}
+
+
+/* modified by cxj @2014-10-31 */
+static int  measure_atc2603c_current(struct atc260x_gauge_info *info)
+{
+	int bat_cur = 0;
+	int adc_value;
+
+	adc_value = gauge_reg_read(info->atc260x, PMU_ICMADC) & PMU_ICMADC_MASK;
+	
+	if(info->cfg_items.rsense == RSENSE_10mohm)
+		bat_cur = ((adc_value & 0x3ff) * 4578 / 1024 )/1000;
+	else if(info->cfg_items.rsense == RSENSE_20mohm)
+		bat_cur = (adc_value & 0x3ff) * 2343 / 1024;
+	else
+		GAUGE_ERR("cannot find the responding config of resistor!\n");
+		
+	if(PMU_ICMADC_SIGN_BIT & adc_value)
+		bat_cur = -bat_cur;
+	else
+		bat_cur = bat_cur;
+	
+	if (((bat_cur >= 0) && (bat_cur <= CHARGE_CURRENT_THRESHOLD)) ||
+		((bat_cur <= 0) && (abs(bat_cur) <= DISCHARGE_CURRENT_THRESHOLD)))
+	{
+		return 0;
+	}
+	return bat_cur;
+}
+
+/**
+ * measure_atc2603a_current - mesure atc2603a current, when discharge/charge.
+ * 
+ * when discharge, there are 2 pathes,  inner mos path and extern mos path, 
+ * when battery servers as power supply for syspower,
+ * inner_discharge_current / extern_discharge_current as follows table:
+ * ---------------------------------------------------------------
+ *| inner_current(mA) | inner_current/extern_current | inner_mos/extern_mos |
+ *|---------------------------------------------------------------
+ *|42                        |42/8                                   |8/42                           |
+ *|---------------------------------------------------------------
+ *|60                        |60/40                                 |40/60                          |
+ *|----------------------------------------------------------------
+ *|75                        |80/120                               |80/120                         |
+ *|----------------------------------------------------------------
+ *|116                      |120/380                              |380/120                       |
+ *|----------------------------------------------------------------
+ *|165                      |165/635                              |635/165                       |
+ *|----------------------------------------------------------------
+ *|200                      |200/800                              |800/200                       |
+ *|----------------------------------------------------------------
+ *
+ */
+ static int atc260x_read_adc(struct atc260x_dev *atc260x, const char *channel_name)
+{
+	int ret;
+	int translate_data;
+	unsigned int channel_num;
+	
+	channel_num = atc260x_auxadc_find_chan(atc260x,channel_name);
+	if(channel_num < 0 )
+	{
+		GAUGE_ERR("[%s]not support this channel or the channel name is error!\n",__func__);
+		return channel_num;
+	}
+	ret = atc260x_auxadc_get_translated(atc260x,channel_num, &translate_data);
+	if(ret < 0)
+		GAUGE_ERR("[%s]cannot get the correct translation data!\n",__func__);
+	
+	return translate_data;
+}
+static int  measure_atc2603a_current(struct atc260x_gauge_info *info)
+{
+	int ch_current;
+	int disch_current;
+	
+	ch_current =  atc260x_read_adc(info->atc260x, "CHGI");
+	ch_current = ch_current * info->current_ratio /PMU_CUR_RATIO_BASE;
+
+	if (ch_current > CHARGE_CURRENT_THRESHOLD)  /*60ma*/
+		return ch_current;
+	
+	disch_current  =  atc260x_read_adc(info->atc260x, "BATI");
+	if (disch_current <= 50)
+	{
+		goto out;
+	}
+	else if ((disch_current > 50) &&
+		(disch_current <= 60))
+	{	
+		disch_current = disch_current + disch_current * 40 /60;
+	}
+	else if ((disch_current > 60) &&
+		(disch_current <= 75))
+	{	
+		disch_current = disch_current + disch_current * 120 /80;
+	}
+	else if ((disch_current > 75) &&
+		(disch_current <= 116))
+	{	
+		disch_current = disch_current + disch_current * 380 /120;
+	}
+	else if ((disch_current > 116) &&
+		(disch_current <= 165))
+	{	
+		disch_current = disch_current + disch_current * 635 /165;
+	}
+	else if ((disch_current > 165) &&
+		(disch_current <= 200))
+	{	
+		disch_current = disch_current + disch_current * 800 /200;
+	}
+	else
+	{
+		disch_current = disch_current + disch_current * 800 /200;
+	}
+	
+out:
+	if (disch_current > DISCHARGE_CURRENT_THRESHOLD)  /*30ma*/
+		return -disch_current;
+
+	return 0;
+	
+}
+
+int  measure_current(void)
+{	
+	int bat_curr = -EINVAL;
+	if ((ic_type == ATC260X_ICTYPE_2603C) && board_has_cm)
+	{
+		bat_curr = measure_atc2603c_current(global_gauge_info_ptr);
+	}
+	else if ((ic_type == ATC260X_ICTYPE_2603A) ||
+		((ic_type == ATC260X_ICTYPE_2603C) && (!board_has_cm)))
+	{
+		bat_curr = measure_atc2603a_current(global_gauge_info_ptr);
+	}
+	
+	return bat_curr;
+}
+
+int  measure_iref_adc(void)
+{	
+	int iref_adc;
+
+	iref_adc = gauge_reg_read(global_gauge_info_ptr->atc260x, 
+		PMU_IREFADC);
+	
+	return   iref_adc;
+}
+
+
+int  measure_vbatt(void)
+{
+	return   atc260x_read_adc(global_gauge_info_ptr->atc260x, "BATV");
+}
+
+static int measure_vbatt_average(void)
+{
+	int vol_buf[SAMPLES_COUNT];
+	int sum = 0;
+	int i;
+	
+	for (i = 0; i < SAMPLES_COUNT; i++ )
+	{
+		vol_buf[i] = measure_vbatt();
+		sum += vol_buf[i];
+		usleep_range(2000,2200);
+	}
+
+	return sum / SAMPLES_COUNT;
+}
+
+static int measure_current_avr(void)
+{
+	int curr_buf[SAMPLES_COUNT];
+	int sum = 0;
+	int i;
+	
+	for (i = 0; i < SAMPLES_COUNT; i++ )
+	{
+		curr_buf[i] = measure_current();
+		sum += curr_buf[i];
+		usleep_range(2000,2200);
+	}
+	return sum / SAMPLES_COUNT;
+}
+
+static void get_charge_status(int *status)
+{
+	int data;
+	
+	data = measure_current();
+	//data = measure_atc2603a_current(global_gauge_info_ptr);
+
+	if (data < 0) 
+	{
+		*status =  POWER_SUPPLY_STATUS_DISCHARGING;
+	}
+	else if (data > 0)
+	{
+		*status = POWER_SUPPLY_STATUS_CHARGING;
+	}
+	else
+	{
+		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+
+	GAUGE_DBG("[%s] charge status:%d\n", __func__, *status);
+
+}
+
+
+void batt_info_dump(struct atc260x_gauge_info *info)
+{
+	printk("\n======================debug information========================\n");
+	/* dts config */
+	printk("[%s] capacity:%dmAh\n",__func__,info->cfg_items.capacity);
+	printk("[%s] icm_available:%d\n",__func__,info->cfg_items.icm_available);
+	printk("[%s] rsense:%dmohm\n",__func__,info->cfg_items.rsense);
+	printk("[%s] taper_vol:%dmV\n",__func__,info->cfg_items.taper_vol);
+	printk("[%s] taper_current:%dmA\n",__func__,info->cfg_items.taper_current);
+	printk("[%s] terminal_vol:%dmV\n",__func__,info->cfg_items.terminal_vol);
+	printk("[%s] min_over_chg_protect_voltage:%dmV\n",__func__,info->cfg_items.min_over_chg_protect_voltage);
+	printk("[%s] suspend_current:%duA\n",__func__,info->cfg_items.suspend_current);
+	printk("[%s] shutdown_current:%duA\n\n",__func__,info->cfg_items.shutdown_current);
+	/* interval */
+	printk("[%s] interval:%ds\n",__func__,info->interval);
+	/* charge status */
+	if (info->status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+		printk("[%s] charger status:POWER_SUPPLY_STATUS_NOT_DISCHARGING\n", __func__);
+	else if (info->status == POWER_SUPPLY_STATUS_CHARGING)
+		printk("[%s] charger status:POWER_SUPPLY_STATUS_CHARGING\n", __func__);
+	else if (info->status == POWER_SUPPLY_STATUS_DISCHARGING)
+		printk("[%s] charger status:POWER_SUPPLY_STATUS_DISCHARGING\n", __func__);
+	/* battery */
+	printk("[%s] measure bat voltage:%dmv\n", __func__, measure_vbatt());
+	printk("[%s] measure charger/discharge current:%dmA\n", __func__, measure_current());
+	printk("[%s] charge resistor:%dmohm\n", __func__, info->ch_resistor);
+	printk("[%s] discharge resistor:%dmohm\n", __func__, info->disch_resistor);
+	printk("[%s] ocv:%dmv\n", __func__, info->ocv);
+	printk("[%s] ocv stop:%dmv\n", __func__, info->ocv_stop);
+	printk("[%s] real soc:%d\n", __func__, info->soc_real);
+	printk("[%s] now soc:%d\n", __func__, info->soc_now);
+	printk("[%s] filter soc:%d\n", __func__, info->soc_filter);
+	printk("[%s] show soc:%d\n", __func__, info->soc_show);
+	printk("[%s] stopped soc:%d\n", __func__, info->soc_ref);
+	printk("[%s] board_has_cm:%d\n",__func__,board_has_cm);
+	printk("\n===============================================================\n");
+}
+static int get_responding_ocv(struct atc260x_gauge_info *info,int soc)
+{
+	int i;
+	int count;
+	int ocv_finded;
+	int soc_finded;
+	int ocv_add;
+	int (*ocv_soc_table_p)[2];
+	
+	if(info->cfg_items.ocv_soc_config)
+		ocv_soc_table_p = ocv_soc_table_config;
+	else
+		ocv_soc_table_p = ocv_soc_table;
+
+	count = ARRAY_SIZE(ocv_soc_table);
+	for (i = count -1; i >= 0; i--)
+	{
+		soc_finded = (*(ocv_soc_table_p + i))[1];
+
+		if (soc >= soc_finded*1000)
+		{
+			if (i == count - 1)
+			{
+				ocv_finded = FULL_CHARGE_OCV;
+				break;
+			}
+			ocv_finded = (*(ocv_soc_table_p + i))[0];
+			ocv_add = (soc - soc_finded*1000) * ((*(ocv_soc_table_p + i + 1))[0] - ocv_finded)/1000;
+			ocv_finded += ocv_add;
+			GAUGE_DBG("ocv_finded=%d,ocv_add=%d\n",ocv_finded,ocv_add);
+			break;
+		}
+	}
+	
+	return ocv_finded;
+}
+static void update_discharge_resistor(struct atc260x_gauge_info *info,int soc)
+{
+	int batv = measure_vbatt_average();
+	int bati = -measure_current_avr();
+	int resp_ocv = get_responding_ocv(info,soc);
+	GAUGE_DBG("resp_ocv=%d,batv=%d,bati=%d\n",resp_ocv,batv,bati);
+	if (resp_ocv > batv)
+	{
+		info->disch_resistor = (resp_ocv - batv)*1000/bati;
+		if (info->disch_resistor > 500)
+			info->disch_resistor = 500;
+		else if (info->disch_resistor < 80)
+			info->disch_resistor = 80;
+	}
+	else
+		GAUGE_ERR("[%s]err:resp_ocv=%d,batv=%d!!",__func__,resp_ocv,batv);
+	GAUGE_DBG("[%s]disch_resistor=%d\n",__func__,info->disch_resistor);
+}
+
+#define SLIP_DOWN_TIME  30
+#define TERMINAL_VOLTAGE_ADD 0
+/*smooth for discharge curve*/
+static void down_curve_smooth(struct atc260x_gauge_info *info, int *soc)
+{
+	int down_step = 0;
+	int down_step_diff;
+	int weight;
+	int vbatt_avg;
+	int bati_avg;
+	int bat_full;
+	int data;
+
+	/*calc down step, when battery voltage is low*/
+	vbatt_avg = measure_vbatt_average();
+	bati_avg = -measure_current_avr();
+	if (bati_avg < 0)
+	{
+		down_step = 0;
+		goto out;
+	}
+	
+	if (vbatt_avg <= info->cfg_items.terminal_vol - 50)
+	{
+		GAUGE_INFO("[%s] battery voltage is %dmv\n", __func__, vbatt_avg);
+		down_step = *soc;
+		goto out;
+	}
+		
+	bat_full = FULL_CHARGE_OCV - bati_avg * 250 / 1000;
+	if (vbatt_avg >= bat_full)
+		weight = 1000;
+	else if (vbatt_avg > info->cfg_items.terminal_vol-TERMINAL_VOLTAGE_ADD)
+	{
+		weight = 1000 * ( vbatt_avg - info->cfg_items.terminal_vol+TERMINAL_VOLTAGE_ADD) /(bat_full - info->cfg_items.terminal_vol+TERMINAL_VOLTAGE_ADD);
+	}
+	else
+		weight = 0;
+	info->soc_ref = FULL_CHARGE_SOC / 1000 * weight; 
+	info->soc_filter = (info->soc_real * weight  + info->soc_ref * (1000 - weight)) / 1000 ;
+	GAUGE_DBG("weight=%d,soc_ref=%d,soc_filter=%d,soc_real=%d\n",
+				weight,info->soc_ref,info->soc_filter,info->soc_real);
+	
+	if ((info->ocv >= FULL_CHARGE_OCV || info->soc_ref == FULL_CHARGE_SOC) && info->soc_now == FULL_CHARGE_SOC)
+	{
+		down_step = 0;
+		goto out;
+	}
+	
+	if (info->ocv <= 3200 + info->disch_resistor * 2000 /1000)
+	{
+		data = -measure_current();
+		GAUGE_DBG("[%s] discharge current is %dmA\n", __func__, data);
+		if (data >= 2000)
+		{
+			down_step = 1000;
+			goto out;
+		}	
+	}
+
+	/*calc down step, when battery voltage is high*/
+	/*calculate the entire battery duration time(s) according to current system consumption*/
+	
+	if (!bati_avg) {
+		down_step = 0;
+		goto out;
+	}
+	down_step = (info->cfg_items.capacity * 3600) / bati_avg;
+	GAUGE_DBG("[%s] all_time=capacity *3600/bat_cur=%d*3600/%d=%d\n", 
+			__func__, info->cfg_items.capacity, bati_avg, down_step);
+	/*calc reduced soc per second*/
+	down_step = FULL_CHARGE_SOC /down_step;
+	GAUGE_DBG("[%s] soc_per_sec=FULL_CHARGE_SOC/all_time=%d/all_time=%d\n", 
+			__func__, FULL_CHARGE_SOC, down_step);
+	/*calc the down step during interval*/
+	down_step = down_step * info->interval;
+	GAUGE_DBG("[%s] down_step=soc_per_sec*interval=soc_per_sec*%d=%d\n", 
+			__func__,info->interval, down_step);
+	
+	if (info->soc_filter < info->soc_now)
+	{
+		if (info->soc_filter > 10000)
+		{
+			down_step_diff = (info->soc_now - info->soc_filter)/ (60 /info->interval);
+			down_step = (down_step > down_step_diff) ? down_step:down_step_diff;
+			if (info->soc_now - info->soc_filter > 10000)
+			{
+				GAUGE_DBG("soc_now>soc_filter+10%:down_step=0.5%\n");
+				down_step = 500;
+			}
+			else
+			{
+				if (down_step >= 250)
+					down_step = 250;
+			}
+		}
+		else
+		{
+			down_step_diff = (info->soc_now - info->soc_filter)/ (30 /info->interval);
+			down_step = (down_step > down_step_diff) ? down_step:down_step_diff;
+			if (down_step >= 500)
+				down_step = 500;
+		}
+			
+		GAUGE_DBG("[%s]soc_filter(%d) < soc_now(%d),down_step=%d\n",__func__,info->soc_filter,info->soc_now,down_step);
+	}
+	else
+	{
+		if (info->soc_filter - info->soc_now > 2000)
+		{
+			GAUGE_DBG("soc_filter>soc_now+2000:R decrease!!");
+			update_discharge_resistor(info,info->soc_now );
+			down_step = 0;
+		}
+	}
+	if (info->soc_real > info->soc_filter + 250)
+	{
+		GAUGE_DBG("soc_real>soc_filter:R decrease!!");
+		update_discharge_resistor(info,info->soc_filter);
+	}
+	if (info->soc_now > info->soc_real + 250)
+	{
+		GAUGE_DBG("soc_now>soc_real:R increase!!");
+		update_discharge_resistor(info,info->soc_now);
+		info->dich_r_change = true;
+	}
+
+	if (down_step < 0)
+		down_step = 0;
+	else
+	{
+		if (info->soc_now - down_step < 1000)
+			down_step = 0;
+	}
+out:
+	*soc = *soc - down_step;
+	GAUGE_DBG("[%s] down_step=%d, soc:%d\n", __func__, down_step, *soc);
+}
+
+static void soc_post_process(struct atc260x_gauge_info *info)
+{
+	int soc_last;
+	
+	if (info->soc_now  > FULL_CHARGE_SOC) 
+	{
+		info->soc_now = FULL_CHARGE_SOC;
+	} 
+	else if (info->soc_now < EMPTY_DISCHARGE_SOC) 
+	{
+		info->soc_now = EMPTY_DISCHARGE_SOC;
+	}
+	
+	mutex_lock(&info->lock);
+	info->soc_show = info->soc_now / 1000;	
+	mutex_unlock(&info->lock);
+
+	/*
+	 * chenbo at 20150514 
+	 * save last soc
+	 */
+	soc_last = get_stored_soc(info);
+	if ((soc_last >= 0) && 
+		soc_last - info->soc_show > 1) {
+		info->soc_last = soc_last;
+	}
+	
+	store_soc(info);
+
+	if (info->soc_show % 5)
+		ch_resistor_calced = 0;
+		
+	if (info->cfg_items.print_switch)
+	{
+		batt_info_dump(info);
+	}
+	
+	if (info->cfg_items.log_switch)
+	{
+		store_batt_info(info);
+	}
+	
+}
+
+static int get_threshold(int type)
+{
+	int threshold = 0 ;
+	
+	if (type == CURRENT_TYPE)
+	{
+		threshold = BAT_CUR_VARIANCE_THRESHOLD;
+	}
+	else if (type == VOLTAGE_TYPE)
+	{
+		threshold = BAT_VOL_VARIANCE_THRESHOLD;
+	}
+	else
+		GAUGE_INFO("the parameter of 'type' do not support!\n");
+
+	return threshold;
+}
+
+/*
+ * filter for gathered batt current and batt voltage, including 2 steps:
+ * step1 : divide the buf which lengh is len into several groups, 
+ *            every group have similar data range;
+ * step2: filterd for ervery group.
+ */
+static int filter_algorithm1(int *buf,  int len, int type)
+{
+	struct data_group group[10];
+	int threshold = 0;
+	int i;
+	int j;
+	int k;
+	
+    	if (!buf) 
+	{
+        		return -EINVAL;
+    	}
+
+	threshold = get_threshold(type);
+		
+	/*divided the data into several group*/
+	group[0].index = 0;
+	group[0].num = 1;
+    	for (i = 1, j = 0;  i < len; i++) 
+    	{
+		if (abs(buf[i] - buf[i - 1]) > threshold)
+		{
+			group[++j].index = i;
+		}
+
+		group[j].num = (i + 1) - group[j].index;
+    	}
+
+	/*handle  every group*/
+	for (i = 0; i < sizeof(group) / sizeof(struct data_group); i++)
+	{
+		GAUGE_DBG("group[%d].index=%d, group[%d].num=%d\n", i, group[i].index, i, group[i].num);
+		
+		if (group[i].num >= 5)
+		{
+			for (j = group[i].index; j <= (group[i].index + group[i].num + 1) /2; j++)
+			{
+				group[i].sum = buf[j];
+				group[i].count = 1;
+				for (k = j; k < group[i].index + group[i].num; k++)
+				{
+					if (abs(buf[k + 1] - buf[j]) < threshold)
+					{
+						group[i].sum+= buf[k + 1];
+						group[i].count++;
+						GAUGE_DBG(" buf[%d]:%d\n", k + 1,  buf[k + 1]);
+					}
+				}
+
+				if (group[i].count >= (group[i].num + 1) / 2)
+				{
+					group[i].avr_data = group[i].sum  / group[i].count ;
+					GAUGE_DBG("[%s] Average cur/vol=%d/%d=%d\n", 
+						__func__, group[i].sum, group[i].count, group[i].avr_data);
+
+					return group[i].avr_data;
+				}
+			}
+
+			
+		}
+
+	}
+
+	return -EINVAL;
+}
+
+/*filter for gathered batt current and batt voltage.
+ * note : the process of filter_algorithm2 is looser than filter_algorithm1.
+ 
+static int filter_algorithm2(int *buf,  int len, int type)
+{
+	int threshold = 0;
+	int avr_data;
+	int count;
+	int sum;
+	int j;
+	int k;
+
+	if (!buf) 
+	{
+        		return -EINVAL;
+    	}
+	
+	threshold = get_threshold(type);
+	
+	for (j = 0; j <= (len + 1) /2; j++)
+	{
+		sum = buf[j];
+		count = 1;
+		for (k = j; k < len; k++)
+		{
+			if (abs(buf[k + 1] - buf[k]) < threshold)
+			{
+				sum+= buf[k + 1];
+				count++;
+			}
+		}
+
+		if (count >= (len + 1) / 2)
+		{
+			avr_data = sum  / count ;
+			GAUGE_DBG("[%s] Average cur/vol=%d/%d=%d\n", 
+				__func__, sum, count, avr_data);
+
+			return avr_data;
+		}
+	}
+
+	return -EINVAL;
+}
+*/
+/*by testing ,we know that the current change depend the scene,but always not hardly
+ *we just calculate the data near the average value
+ *the one apart from the average by 100 will be discarded 
+ */ 
+ 
+ static int filter_algorithm3(int *buf,  int len, int type)
+ {
+	int threshold = 0;
+	int avr_data;
+	int count = 0;
+	int sum = 0;
+	int j;
+	int k;
+	
+	if (!buf) 
+        return -EINVAL;
+	
+	threshold = get_threshold(type);
+	
+	for(j = 0;j < len;j++)
+		sum += buf[j];
+	avr_data = sum / len;
+	for(k = 0;k < len;k ++)
+	{
+		if(abs(buf[k] - avr_data) < threshold)
+			count ++;
+		else
+			sum -= buf[k];
+	}
+	GAUGE_DBG("available data count:%d\n",count);
+	if(count > len * 2 / 3)
+		avr_data = sum / count;
+	else
+		return -EINVAL;
+	
+	return avr_data;
+ }
+
+static int filter_process(struct atc260x_gauge_info *info)
+{
+	struct kfifo_data fifo_data;
+	
+	fifo_data.bat_vol = 
+		info->filter_algorithm(info->vol_buf, SAMPLES_COUNT, VOLTAGE_TYPE);
+	fifo_data.bat_curr = 
+		info->filter_algorithm(info->curr_buf, SAMPLES_COUNT, CURRENT_TYPE);
+	fifo_data.timestamp.tv_sec = current_tick.tv_sec;
+	fifo_data.timestamp.tv_usec = current_tick.tv_usec;
+	GAUGE_DBG("[%s] the latest value: %d(vol), %d(cur)\n",
+		__func__, fifo_data.bat_vol, fifo_data.bat_curr);
+	
+	/* modified by cxj at 20141029 */
+	if ((fifo_data.bat_vol == -EINVAL) ||
+		(fifo_data.bat_curr == -EINVAL))
+		return -EINVAL;
+	
+	kfifo_in(&info->fifo, &fifo_data, sizeof(struct kfifo_data));
+
+	return 0;
+}
+
+/*
+ * gather battery info, including voltage and current.
+ */
+static void  gather_battery_info(struct atc260x_gauge_info *info)
+{
+	int i;
+
+	do_gettimeofday(&current_tick);
+	
+	for (i = 0; i < SAMPLES_COUNT; i++ )
+	{
+		info->vol_buf[i] = measure_vbatt();
+		info->curr_buf[i] = measure_current();
+		usleep_range(2000,2200);
+	}
+}
+
+
+/* calculate resistor in charging case */
+static int calc_charge_resistor(struct atc260x_gauge_info *info)
+{
+	struct kfifo_data fifo_data[2];
+	int reg_chg_current;
+	int data;
+	int chg_current;
+	int ret;
+
+	chg_current = measure_current_avr();
+	GAUGE_DBG("chg_current=%d\n",chg_current);
+	if (chg_current < 0)
+	{
+		GAUGE_ERR("measure_current :data <0");
+		goto out;
+	}
+	
+	if (chg_current >= 500)
+	{
+		atc260x_set_charger_current(500, &reg_chg_current);
+		msleep(2000);
+		gather_battery_info(info);
+		ret = filter_process(info);
+		
+		if (ret)
+		{
+			GAUGE_ERR("[%s]filter_process failed!\n",__func__);
+			goto out;
+		}
+		
+		atc260x_set_charger_current(100, &ret);
+		msleep(1500);
+		
+		gather_battery_info(info);
+		ret = filter_process(info);
+		
+		if (ret)
+		{
+			GAUGE_ERR("[%s]filter_process failed!\n",__func__);
+			goto out;
+		}
+		
+		atc260x_set_charger_current(reg_chg_current, &ret);
+		msleep(500);
+	}
+	else
+	{
+		reg_chg_current = get_chg_current_now();
+		gather_battery_info(info);
+		ret = filter_process(info);
+		GAUGE_DBG("[%s]turn off charger!\n",__func__);
+		atc260x_charger_turn_off_force();
+		msleep(500);
+		
+		gather_battery_info(info);
+		ret = filter_process(info);
+		
+		if (ret)
+		{
+			GAUGE_ERR("[%s]filter_process failed!\n",__func__);
+			goto out;
+		}
+		
+		atc260x_set_charger_current(reg_chg_current, &ret);
+		GAUGE_DBG("[%s]turn on charger!\n",__func__);
+		atc260x_charger_turn_on_force();
+		msleep(500);
+	}
+
+	if (kfifo_is_full(&info->fifo))
+	{
+		ret = kfifo_out(&info->fifo, &fifo_data[0], sizeof(struct kfifo_data));
+		GAUGE_DBG("[%s] %d(vol), %d(cur), dequeue len:%d\n", 
+			__func__, fifo_data[0].bat_vol, fifo_data[0].bat_curr, ret);
+		ret = kfifo_out(&info->fifo, &fifo_data[1], sizeof(struct kfifo_data));
+		GAUGE_DBG("[%s] %d(vol), %d(cur), dequeue len:%d\n", 
+			__func__, fifo_data[1].bat_vol, fifo_data[1].bat_curr, ret);
+
+		if ((fifo_data[0].bat_vol > fifo_data[1].bat_vol) && 
+			(fifo_data[0].bat_curr > fifo_data[1].bat_curr)) 
+		{
+			/* calculate resistor :mohm*/
+			data = 1000 * (fifo_data[0].bat_vol  - fifo_data[1].bat_vol)
+			    / (fifo_data[0].bat_curr - fifo_data[1].bat_curr);
+				
+			GAUGE_DBG("fifo_data[0].bat_vol = %d, fifo_data[1].bat_vol = %d\n",fifo_data[0].bat_vol,fifo_data[1].bat_vol);
+			GAUGE_DBG("fifo_data[0].bat_curr = %d, fifo_data[1].bat_curr = %d\n",fifo_data[0].bat_curr,fifo_data[1].bat_curr);
+			GAUGE_DBG("here ch_resistor = %d\n",data);
+			
+			if (data <= 500)
+				info->ch_resistor = data;
+			else
+				info->ch_resistor = 500;
+			GAUGE_DBG("[%s] the latest charge resistor is %d\n", __func__, info->ch_resistor);
+
+			return 0;
+		}
+	}
+
+out:
+	kfifo_reset(&info->fifo);
+	return -EINVAL;
+}
+
+/* Calculate Open Circuit Voltage */
+static int calc_ocv(struct atc260x_gauge_info *info)
+{
+	int i;
+	int vbatt_sum;
+	int ibatt_sum;
+	int count = 0;
+
+	switch (info->status)
+	{
+	case POWER_SUPPLY_STATUS_NOT_CHARGING:
+		for (i = 0, vbatt_sum = 0; i < SAMPLES_COUNT; i++) 
+		{
+			vbatt_sum += info->vol_buf[i];
+		}
+		
+		info->vbatt_avg = vbatt_sum / SAMPLES_COUNT;
+		info->ocv = info->vbatt_avg;
+		info->ocv_stop = info->cfg_items.terminal_vol+TERMINAL_VOL_ADD;
+		info->ibatt_avg = 0;
+		break;
+	case POWER_SUPPLY_STATUS_CHARGING:
+	case POWER_SUPPLY_STATUS_DISCHARGING:
+		for (i = 0, ibatt_sum = 0, vbatt_sum = 0; i < SAMPLES_COUNT; i++) 
+		{
+			vbatt_sum += info->vol_buf[i];
+			if (info->status == POWER_SUPPLY_STATUS_CHARGING)
+			{
+				if (info->curr_buf[i] > 0)
+				{
+					ibatt_sum += info->curr_buf[i];
+					count ++;
+				}
+			}
+			else if (info->status == POWER_SUPPLY_STATUS_DISCHARGING)
+			{
+				if (info->curr_buf[i] < 0)
+				{
+					ibatt_sum += -info->curr_buf[i];
+					count ++;
+				}
+			} 
+			
+		}
+		info->vbatt_avg = vbatt_sum / SAMPLES_COUNT;
+		/*added by cxj at 20141101:division cannot be zero*/
+		if(count != 0)
+			info->ibatt_avg = ibatt_sum / count;
+		else      /*if zero ,battery is of full power*/
+		{
+	      /*modified by cxj at 20141113:cannot return -EINVAL,because of full capacity*/
+			info->ocv = info->vbatt_avg;
+			info->ibatt_avg = 0;
+			return 0; 
+		}
+		
+		if (info->status == POWER_SUPPLY_STATUS_CHARGING)
+		{
+			info->ocv = info->vbatt_avg - info->ibatt_avg * info->ch_resistor / 1000;
+			GAUGE_DBG("[%s] ocv:%d\n", __func__, info->ocv);
+		}
+		else if (info->status == POWER_SUPPLY_STATUS_DISCHARGING)
+		{	
+			info->ocv = info->vbatt_avg + info->ibatt_avg * info->disch_resistor / 1000;
+			info->ocv_stop = info->cfg_items.terminal_vol + TERMINAL_VOL_ADD + info->ibatt_avg * info->disch_resistor / 1000;
+			GAUGE_DBG("[%s] ocv:%d, ocv stop:%d\n", 
+				__func__, info->ocv, info->ocv_stop);
+		}
+		break;
+	default:
+		GAUGE_WARNING("[%s] charging status err!\n" ,__func__);
+		return -EINVAL;
+	}
+	
+	return 0;
+	
+}
+
+/* Calculate State of Charge (percent points) */
+static void calc_soc(struct atc260x_gauge_info *info, int ocv, int *soc)
+{
+	int i;
+	int count;
+	int soc_finded;
+	int (*ocv_soc_table_p)[2];
+	
+	if(info->cfg_items.ocv_soc_config)
+		ocv_soc_table_p = ocv_soc_table_config;
+	else
+		ocv_soc_table_p = ocv_soc_table;
+		
+	if(ocv < (*ocv_soc_table_p)[0])
+	{
+		*soc = 0;
+		GAUGE_DBG("[%s] ocv:%d, soc:0(ocv is less than the minimum value, set soc zero)\n", 
+			__func__, ocv);
+		return;
+	}
+	count = ARRAY_SIZE(ocv_soc_table);
+	
+	for (i = count -1; i >= 0; i--) 
+	{
+		if (ocv >= (*(ocv_soc_table_p + i))[0])
+		{
+			if (i == count - 1)
+			{
+				*soc = FULL_CHARGE_SOC;
+				break;
+			}
+			soc_finded = (*(ocv_soc_table_p + i))[1];
+			GAUGE_DBG("soc_finded=%d\n",soc_finded);
+			*soc = soc_finded*1000 + (ocv - (*(ocv_soc_table_p + i))[0])*
+					((*(ocv_soc_table_p + i + 1))[1]-soc_finded)*1000/
+					(((*(ocv_soc_table_p + i + 1))[0]) - (*(ocv_soc_table_p + i))[0]);
+			GAUGE_DBG("[%s]ocv:%d, calc soc is %d\n", __func__, ocv, *soc);
+			break;
+		}
+	}
+
+}
+
+#ifdef GAUGE_SOC_AVERAGE
+static int soc_average(struct atc260x_gauge_info *info,  int soc)
+{
+	int soc_sum = 0;
+	int soc_avr;
+	int i;
+	int size;
+	
+	size = sizeof(info->soc_queue) / sizeof(int);
+	
+	info->soc_queue[info->index++ % size] = soc;
+
+	if (info->index < size -1)
+	{
+		return soc;
+	}
+
+	for (i = 0; i < size; i++)
+	{
+		soc_sum += info->soc_queue[i];
+		GAUGE_DBG("[%s] info->soc_queue[%d] = %d\n", __func__, i,  info->soc_queue[i]);	
+	}
+
+	info->index = 0;
+	soc_avr = soc_sum / size;
+	GAUGE_DBG("[%s] average soc = %d /%d = %d\n", __func__, soc_sum, size, soc_avr);	
+
+	return soc_avr;
+}
+#endif
+
+static int generate_poll_interval(struct atc260x_gauge_info *info)
+{
+	int data;
+	
+	data = measure_vbatt_average();
+	
+	if (data >= info->cfg_items.terminal_vol + 100)
+	{
+		if (info->soc_show == 99 || info->soc_real == FULL_CHARGE_SOC || full_power_dealing)
+		{
+			info->interval = 5;
+		}
+		else
+			info->interval = 10;
+	}
+	else if (data >= info->cfg_items.terminal_vol + 50)
+	{
+		info->interval = 5;
+	}
+	else
+	{
+		info->interval = 2;
+	}
+
+	GAUGE_DBG("[%s] bat_vol :%d, interval = %d\n", __func__, data, info->interval);
+	
+	return info->interval;
+}
+
+static void start_anew(struct atc260x_gauge_info *info)
+{
+	kfifo_reset(&info->fifo);
+	ch_resistor_calced = 0;
+	first_calc_resistor = -1;
+	taper_interval = 0;
+
+	kfifo_reset(&info->down_curve);
+}
+static void soc_grow_up(struct atc260x_gauge_info *info,int grow_step)
+{
+	if (info->soc_now == FULL_CHARGE_SOC)
+		return ;
+		
+	if (info->soc_real > info->soc_now)
+	{
+		info->soc_now +=  grow_step;
+		GAUGE_DBG("soc_now add up %d\n",grow_step);
+	}
+	if (info->soc_now > FULL_CHARGE_SOC)
+		info->soc_now = FULL_CHARGE_SOC;
+	
+}
+
+#define CLIMB_UP_TIME  30
+static void soc_now_compensation(struct atc260x_gauge_info *info)
+{
+	int chg_current;
+	int batv;
+	int soc_up_step;
+	int soc_compensate;
+	int soc_to_show;
+	
+	int ratio = 1;
+	chg_current = measure_current_avr();
+	batv = measure_vbatt_average();
+	/*interval * FULL_CHARGE_SOC / (info->cfg_items.capacity * 3600 / chg_current)*/
+	soc_up_step = info->interval * FULL_CHARGE_SOC / 100 * chg_current / (info->cfg_items.capacity * 36);
+	
+	/*for test*/
+	if (info->soc_real >= 90000 && info->soc_real <= 98000)
+		ratio = 2;
+	soc_up_step = soc_up_step / ratio;
+	soc_to_show = info->soc_now + soc_up_step;
+	GAUGE_DBG("soc_now = %d,soc_up_step = %d,soc_to_show=%d\n",info->soc_now,soc_up_step,soc_to_show);
+	
+	if ( info->soc_real < 90000)
+	{
+		if (info->soc_real >= soc_to_show + 2000)
+		{
+			soc_compensate = 800 % ((info->soc_real - soc_to_show)/(CLIMB_UP_TIME / info->interval));
+			if (soc_up_step + soc_compensate > 1000)
+				soc_compensate = 1000 - soc_up_step;
+			GAUGE_DBG("soc_real >soc_to_show +2000:soc_compensate = %d\n",soc_compensate);
+			soc_to_show += soc_compensate;
+			GAUGE_DBG("finally:soc_now : %d\n",soc_to_show);		
+		}
+	}
+	
+	if (soc_to_show >= info->soc_real + 1000 )
+	{
+		soc_compensate = soc_up_step;
+		GAUGE_DBG("soc_to_show > soc_real + 1000:soc_compensate = -%d\n",soc_compensate);	
+		soc_to_show -= soc_compensate;
+		GAUGE_DBG("finally:soc_now : %d\n",soc_to_show);
+	}
+	
+	if (soc_to_show >= FULL_CHARGE_SOC - 1)
+		info->soc_now = FULL_CHARGE_SOC - 1;
+	else
+		info->soc_now = soc_to_show;
+}
+
+static void full_power_schedule(struct atc260x_gauge_info *info)
+{
+	if(atc260x_get_charge_status())
+	{
+		atc260x_charger_turn_off_force();
+		GAUGE_DBG("[%s]now turn off charger...\n",__func__);
+		msleep(5000);
+	}
+	/* detecting battery voltage as ocv*/
+	get_charge_status(&info->status);/* added by cxj @2015-01-28*/
+	info->vbatt_avg = measure_vbatt_average();
+	info->ocv = info->vbatt_avg;
+	calc_soc(info, info->ocv, &info->soc_real);
+	GAUGE_DBG("after 5 secs,bat_vol(ocv)= %d\n",info->vbatt_avg);	
+	
+	if(info->soc_real == FULL_CHARGE_SOC)
+	{
+		GAUGE_DBG("[%s]soc_real has been FULL_CHARGE_SOC!now minus 1000\n",__func__);
+		info->soc_real = FULL_CHARGE_SOC - 1000;
+	}
+	
+	if (info->vbatt_avg >= (info->cfg_items.taper_vol))
+	{
+		taper_interval += info->interval;
+		GAUGE_DBG("[%s]taper_interval = %d\n",__func__,taper_interval);
+		if (taper_interval >= 60)
+		{
+			GAUGE_DBG("Time's up,power is full!\n");
+			info->soc_real = FULL_CHARGE_SOC;
+
+			/* the right of finally turning off the charger is owned by charger driver
+			 * now the soc_real is just up to FULL_CHARGE_SOC,so the it must be more than
+			 * soc_now.meanwhile,calling the following function is needed,as the operation 
+			 * to recover the flag of turn_off_force.
+			 */
+			GAUGE_DBG("turn on charger finally!\n");
+			atc260x_charger_turn_on_force();
+			
+			full_power_dealing = false;
+			taper_interval = 0;
+		}	
+	}
+	else
+	{
+		cycle_count = taper_interval / info->interval + 1; 
+		GAUGE_DBG("info->interval=%d,taper_interval=%d,cycle_count = %d\n",info->interval,taper_interval,cycle_count);
+		atc260x_charger_turn_on_force();
+	}
+	return;
+}
+static bool emphasize_dealing(struct atc260x_gauge_info *info)
+{
+	GAUGE_DBG("next_do_count = %d,cycle_count = %d\n",next_do_count,cycle_count);
+	if (cycle_count == 0)
+		return true;
+	else
+	{
+		if(next_do_count++ == (60 / info->interval - cycle_count))
+		{
+			next_do_count = 0;
+			cycle_count = 0;
+			return true;
+		}
+		else
+			return false;
+	}
+}
+static bool pre_full_power_schedule(struct atc260x_gauge_info *info)
+{
+	int bat_curr;
+	int bat_vol;
+	int chg_current;
+	int current_set_now;
+	bool full_power_test = false;
+	
+	bat_curr = measure_current_avr();	
+	bat_vol = measure_vbatt_average();
+	GAUGE_DBG("bat_curr=%d,bat_vol=%d\n",bat_curr,bat_vol);
+	
+	current_set_now = get_chg_current_now();
+	GAUGE_DBG("current_set_now = %d\n",current_set_now);
+	
+	if(info->cfg_items.min_over_chg_protect_voltage >= 4275)
+	{
+		if (bat_vol > 4200)
+		{
+			if (current_set_now > 400)
+			{
+				if (bat_curr < info->cfg_items.taper_current)
+				{
+					GAUGE_DBG("current_set_now > 300 && bat_cur < %d:enter full power dealing\n",
+								info->cfg_items.taper_current);
+					full_power_test = true;
+					taper_interval = 0;
+				}
+			}
+			else
+			{
+				if (info->ocv >= info->cfg_items.taper_vol)
+				{
+					GAUGE_DBG("current_set_now<300 && ocv >= %d:enter full power dealing\n",
+								info->cfg_items.taper_vol);
+					full_power_test = true;
+					taper_interval = 0;
+				}
+			}
+		}
+	}
+	else
+	{
+		if (bat_vol > 4200)
+		{
+			if (current_set_now > 400)
+			{
+				if (bat_curr < 700)
+				{
+					GAUGE_DBG("now set charge current to be 400mA");
+					atc260x_set_charger_current(400, &chg_current);
+				}
+			}
+			else if (current_set_now == 400)
+			{
+				if (bat_curr < info->cfg_items.taper_current)
+				{
+					GAUGE_DBG("current_set_now = 400 && bat_curr < %d:enter full power\n",
+								info->cfg_items.taper_current);
+					full_power_test = true;
+					taper_interval = 0;
+				}
+			}
+			else
+			{
+				if (info->ocv >= info->cfg_items.taper_vol)
+				{
+					GAUGE_DBG("current_set_now < 400 && ocv >= %d:enter full power\n",
+								info->cfg_items.taper_vol);
+					full_power_test = true;
+					taper_interval = 0;
+				}
+			}
+		}
+			
+	}
+	
+	/*for test*/
+	if (info->ocv >= info->cfg_items.taper_vol && info->soc_now == (FULL_CHARGE_SOC-1))
+	{
+		full_power_test = true;
+		taper_interval = 0;
+	}
+	
+	GAUGE_DBG("[%s]full_power_test =%d\n",__func__,full_power_test);
+	full_power_dealing = full_power_test;
+	return full_power_test;
+}
+
+static int  charge_process(struct atc260x_gauge_info *info)
+{
+	int ret;
+	int current_set_now;
+	int chg_current;
+	
+	if (info->soc_real == FULL_CHARGE_SOC)
+	{
+		if (info->cfg_items.log_switch)
+		{
+			gather_battery_info(info);
+			calc_ocv(info);//calc batv,bati,ocv for log
+		}
+		current_set_now = get_chg_current_now();
+		if (current_set_now != 100)
+			atc260x_set_charger_current(100,&chg_current);
+		soc_grow_up(info,500);
+		return 0;
+	}	
+	/* added by cxj @2014-12-31
+	 * NEW FEATURE\A3\BAfull power dealing
+	 */
+	if(pre_full_power_schedule(info))
+	{	
+		info-> interval = 5;
+		if (emphasize_dealing(info))
+			full_power_schedule(info);
+		return 0;
+	}
+
+	/*calc ch_resistor*/
+	if ((!(info->soc_show % 5) && !ch_resistor_calced) || first_calc_resistor == -1)
+	{
+		ret = calc_charge_resistor(info);
+		if (ret)
+			GAUGE_ERR("[%s] calc charge resistor err\n", __func__);
+		if (first_calc_resistor == -1)
+		{
+			GAUGE_DBG("calc resistor over!\n");
+			first_calc_resistor = 0;
+		}
+		ch_resistor_calced = 1;
+	}
+
+	/*calc ocv*/
+	gather_battery_info(info);
+	ret = calc_ocv(info);
+	if (ret)
+	{
+		GAUGE_ERR("[%s]calc ocv fail!\n",__func__);
+		return ret;
+	}
+	
+	/*calc soc*/
+	calc_soc(info, info->ocv, &info->soc_real);
+	
+	soc_now_compensation(info);
+	
+	/*if the calc_soc has been FULL_CHARGE_SOC, we do not make it as it should be
+	 * because the full power schedule will do it
+	*/
+	if(info->soc_real == FULL_CHARGE_SOC)
+	{
+		GAUGE_DBG("[%s]soc_real has been FULL_CHARGE_SOC!now minus 1000\n",__func__);
+		info->soc_real = FULL_CHARGE_SOC - 1000;
+	}
+	
+	return 0;
+}
+
+
+static int  discharge_process(struct atc260x_gauge_info *info)
+{
+	int ret;
+	int soc_real_test;
+	static int first_flag = 1;
+	gather_battery_info(info);
+
+	ret = calc_ocv(info);
+	if (ret)
+	{
+		GAUGE_DBG("[%s]calc_ocv failed!\n",__func__);
+		return ret;
+	}
+	
+	if (first_flag)
+	{
+		calc_soc(info, info->ocv, &info->soc_real);
+		soc_real_test = info->soc_real;
+		first_flag = 0;
+	}
+	else
+		calc_soc(info, info->ocv, &soc_real_test);/*not allow soc_real go up*/
+	
+	if (soc_real_test < info->soc_real)
+	{
+		info->soc_real = soc_real_test;
+		
+	}
+	else
+	{
+		if (info->dich_r_change)
+		{
+			info->soc_real = soc_real_test;
+			info->dich_r_change = false;
+		}
+	}
+	GAUGE_DBG("soc_real:%d\n",info->soc_real);
+
+	down_curve_smooth(info, &info->soc_now);
+
+	return 0;
+}
+
+/*added by cxj @20141117*/
+static int notcharging_process(struct atc260x_gauge_info *info)
+{
+	/* when step into full_power_schedule(this time,bat_vol>=4180mV),charger may be turned off,and
+	 * status of charge will turn to be NOTDISCHARGING,and cannot dealing
+	 * full power status in charge_process
+	 * soc_now >0:avoid poor healthy status of battery,such as no battery ,short circuit.
+	 * soc_now <FULL_CHARGE_SOC:avoid still stepping into full power dealing when capacity is 100%,
+	*/
+	if(info->soc_now == FULL_CHARGE_SOC || info->soc_now <= 0)
+	{
+		if (info->cfg_items.log_switch)
+		{
+			gather_battery_info(info);
+			calc_ocv(info);//calc batv,bati,ocv for log
+		}
+		return 0;
+	}
+	
+	gather_battery_info(info);
+	calc_ocv(info);
+	calc_soc(info, info->ocv, &info->soc_real);  
+	/* 1.when the load capacity of adapter cannot support charging with more than 60mA,
+	 * 2.when full_power_dealing finished,and then turn on charger ,but also,the load capacity is not enough,
+	 * 3.when battery is being full,but did not ever step into full_power_dealing,and charging current<60mA .
+	 * all above,contribute to going into this function,meanwhile charger is on.
+	 */
+	if (atc260x_get_charge_status())
+	{
+		GAUGE_DBG("charger is on,but current<60mA\n");
+		if (info->ocv >= (info->cfg_items.taper_vol))
+		{
+			info->soc_now += 500;/* for 2&3*/
+		}
+		else
+		{
+			if (info->soc_real > info->soc_now + 500)
+			{
+				if (info->soc_now + 500 < FULL_CHARGE_SOC)  /*for 1*/
+					info->soc_now += 500;
+				
+			}
+			else if (info->soc_real < info->soc_now - 500)
+			{
+				if (info->soc_now - 500 >= 1000)
+					info->soc_now -= 500;
+			}
+		}
+		return 0;
+	}
+	
+	if(info->soc_real == FULL_CHARGE_SOC)
+	{
+		GAUGE_DBG("[%s]soc_real has been FULL_CHARGE_SOC!now minus 1000\n",__func__);
+		info->soc_real = FULL_CHARGE_SOC - 1000;
+	}
+	/* here,if charger is not on,it should be:
+	 * 1.being in full_power_dealing
+	 * 2.just plug in adapter a moment
+	*/
+	if(info->ocv >= (info->cfg_items.taper_vol))
+	{
+		soc_grow_up(info,500);
+		GAUGE_DBG("[%s]interval=%d\n",__func__,info->interval);
+		full_power_schedule(info);
+	}
+	else
+	{
+		if(!atc260x_get_charge_status())
+		{
+			GAUGE_DBG("[%s]now turn on charger...\n",__func__);
+			atc260x_charger_turn_on_force();
+		}
+		cycle_count = taper_interval / info->interval + 1;
+		GAUGE_DBG("[%s]taper_interval=%d,cycle_count=%d",__func__,taper_interval,cycle_count);
+	}
+	return 0;
+}
+
+/*detect if board has preserved cm layout*/
+static void board_cm_pre_detect(struct atc260x_gauge_info *info)
+{
+	int bat_curr_icm;
+	int bat_curr_normal;
+	int bat_vol;
+	int icm_ok;
+	int ret;
+	
+	if (!info->cfg_items.icm_available)
+		goto out;
+	ret = atc260x_pstore_get(info->atc260x,ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST,&icm_ok);
+	GAUGE_DBG("[%s]icm_ok = %d\n",__func__,icm_ok);
+	if (ret)
+	{
+		GAUGE_ERR("get icm_ok error!\n");
+		goto out;
+	}
+	if (icm_ok == 1)
+	{
+		GAUGE_INFO("icm does not work normally!\n");
+		goto out;
+	}
+	
+	bat_vol = measure_vbatt();
+	bat_curr_normal = measure_atc2603a_current(info);
+	
+	if (bat_curr_normal == 0)	
+	{
+		icm_detect_loop = true;
+		return;	
+	}
+	bat_curr_icm = measure_atc2603c_current(info);	
+	if(bat_curr_icm == 0)
+	{
+		board_has_cm = false;
+		GAUGE_INFO("[%s]ICM do not exist or work normally\n",__func__);
+	}
+	else
+	{	
+		board_has_cm = true;
+		GAUGE_DBG("[%s]ICM exist and work!\n",__func__);
+	}	
+	GAUGE_DBG("enter loop\n");
+	icm_detect_loop = true;
+	return;
+
+out:
+	GAUGE_DBG("never enter loop\n");
+	icm_detect_loop = false;
+	return;
+}
+static void board_cm_detect(struct atc260x_gauge_info *info)
+{
+	int normal_cur;
+	int bat_vol;
+	int icm_cur;
+	int ret;
+
+	normal_cur = measure_atc2603a_current(info);
+	bat_vol = measure_vbatt();
+	/* when it's in NOTDISCHARGING status,it's unnecessary to detect ICM */
+	if(normal_cur == 0)
+	{
+		icm_detect_loop = true;
+		return;
+	}
+
+	/*when not full ,we begin to detect icm
+	 * the current detected was not real,just for tester to use,
+	 * when test_icm_enable is true
+	 */
+	
+	if (!test_icm_enable)
+		icm_cur = measure_atc2603c_current(info);
+	else
+		icm_cur = 0;
+	GAUGE_DBG("normal_cur =%d,icm_cur = %d\n",normal_cur,icm_cur);
+	
+	if(icm_cur == 0)
+	{		
+		if(board_has_cm)
+		{	
+			board_has_cm = false;/*change to ICH&IBAT*/
+			icm_detect_cycle = 0;/*if not seriously zero,cancel accumulation*/
+		}
+		if(++icm_detect_cycle == 3)
+		{
+			GAUGE_DBG("icm_detect_cycle=%d\n",icm_detect_cycle);
+			ret = atc260x_pstore_set(info->atc260x,ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST,1);
+			if(ret)
+				GAUGE_ERR("Cann't write to the ICM_EXIST bit\n");
+
+			icm_detect_loop = false;
+		}
+	}
+	else
+	{
+		board_has_cm = true;
+		icm_detect_loop = true;
+	}
+}
+
+/* uA */
+/*record the time stamp of suspend*/
+static struct timespec suspend_ts; 
+static void soc_update_after_resume(struct atc260x_gauge_info *info)
+{
+	struct timespec resume_ts;
+	struct timespec sleeping_ts;
+
+	unsigned int soc_consume;
+	unsigned int soc_to_show;
+	
+	getnstimeofday(&resume_ts);
+	sleeping_ts = timespec_sub(resume_ts, suspend_ts);
+	
+	soc_consume = (unsigned int)sleeping_ts.tv_sec * info->cfg_items.suspend_current /(info->cfg_items.capacity * 36);
+	GAUGE_DBG("sleeping_ts.tv_sec(%ld)*FULL_CHARGE_SOC(%d)*SUSPEND_CURRENT(%d)/1000/(capacity(%d)*3600)=%u\n",
+				sleeping_ts.tv_sec,FULL_CHARGE_SOC,info->cfg_items.suspend_current,info->cfg_items.capacity,soc_consume);
+
+	if((soc_consume < 1000 && info->soc_now == FULL_CHARGE_SOC) || pre_charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+		soc_consume = 0;
+	
+	soc_to_show = info->soc_now - soc_consume;
+	if (soc_to_show > 0)
+		info->soc_now = soc_to_show;
+	else
+		info->soc_now = 0;
+	GAUGE_DBG("*******soc_now = %d\n",info->soc_now);
+}
+#define SHUTDOWN_TIME_THRESHOLD (30 * 24 * 3600)
+static int soc_update_after_reboot(struct atc260x_gauge_info *info)
+{
+	unsigned long time_stored;
+	unsigned long time_now;
+	unsigned long time_space;
+
+	unsigned int soc_consume;
+	unsigned int soc_stored;
+	unsigned int soc_to_show;
+	int ret;
+	int batv_avr;
+	
+	time_now = get_time_hour(info);
+	time_stored = get_stored_time(info);
+	time_space = (time_now - time_stored) * 3600;
+	GAUGE_DBG("the time of shutdown is %lus\n",time_space);
+	soc_stored = get_stored_soc(info) * 1000;
+	
+	if (time_space < SHUTDOWN_TIME_THRESHOLD)
+	{
+		soc_consume = time_space * info->cfg_items.shutdown_current /(info->cfg_items.capacity * 36);
+		GAUGE_DBG("[%s]soc_consume = %d\n",__func__,soc_consume);
+
+		soc_to_show = soc_stored - soc_consume;
+		if(soc_to_show > 0)
+			info->soc_now = soc_to_show + 500; /*500:avoid soc to be show being cut 1% suddenly*/
+		else
+			info->soc_now = 0;
+	}
+	else
+	{
+		ret = calc_ocv(info);
+		if (ret)
+		{
+			GAUGE_ERR("[%s]calculate ocv failed!!\n",__func__);
+			return ret;
+		}
+		calc_soc(info, info->ocv, &info->soc_real);
+
+		/* 
+		 * if bat voltage is equal with or less than stop voltage by 50mv, 
+		 * use real soc to prevent abnormal  shutdown.
+		 */
+		batv_avr = measure_vbatt_average();
+		if ((batv_avr < info->cfg_items.terminal_vol + 50) &&
+			info->soc_real <= info->soc_now)
+		{
+			info->soc_now = info->soc_real;
+			return 0;
+		}
+
+		if(info->soc_real < 15000)
+		{
+			if(soc_stored < info->soc_real)
+				info->soc_now = soc_stored;
+			else
+				info->soc_now =  info->soc_real;
+		}
+		else
+		{
+			if(abs(soc_stored-info->soc_real) > 10000)
+			{
+				if(soc_stored < info->soc_real)
+					info->soc_now = soc_stored;
+				else
+					info->soc_now =  info->soc_real;				
+			}
+			else
+				info->soc_now = soc_stored;
+		}
+
+	}
+	return 0;
+}
+static int  init_capacity(struct atc260x_gauge_info *info)
+{
+	int ret;
+	int wakeup_flag;
+	int soc_stored;
+	
+	/*added by cxj @2014-12-04
+	 *if resume,we let cost_time to be zero,as from suspend to resume,it always not costs more than 30sec
+	*/
+	cost_time = 0;
+
+	gather_battery_info(info);
+	get_charge_status(&info->status);
+	wakeup_flag = owl_pm_wakeup_flag();
+	
+	/* chenbo at 20150608, 
+	 * recalclate soc when boot due to  upgrade or onoff 8s or reset 
+	 */
+	if (info->first_product || 
+		wakeup_flag == OWL_PMIC_WAKEUP_SRC_RESET)
+	{
+		if (wakeup_flag == OWL_PMIC_WAKEUP_SRC_RESET)
+			GAUGE_INFO("%s onoff 8s wakeup or reset\n", __func__);
+		ret = calc_ocv(info);
+		if (ret)
+		{
+			GAUGE_ERR("[%s]calculate ocv failed!!\n",__func__);
+			return ret;
+		}
+		calc_soc(info, info->ocv, &info->soc_real);
+		
+		info->soc_now = info->soc_real;
+		
+		if (info->first_product)
+			info->first_product = false;
+		GAUGE_INFO("[%s] usable soc:%d, current soc:%d\n",
+		__func__, info->soc_real, info->soc_now);	
+		/* chenbo at 20150608, 
+	 	*  recalclate soc when boot due to onoff 8s or reset 
+	 	*/
+		if (wakeup_flag == OWL_PMIC_WAKEUP_SRC_RESET)
+		{
+			soc_stored = get_stored_soc(info) * 1000;
+			if (soc_stored < 0)
+				return 0;
+				
+			if(info->soc_real < 15000 || 
+				abs(soc_stored - info->soc_real) > 10000)
+				info->soc_now = min(soc_stored, info->soc_real);
+			else
+				info->soc_now = soc_stored;
+			GAUGE_INFO("[%s] soc_stored:%d, soc_real:%d\n",
+				__func__, soc_stored, info->soc_real);
+				
+		}
+		
+	}
+	else
+	{
+		if (resume_flag)
+		{
+			soc_update_after_resume(info);
+			return 0;
+		}
+		ret = soc_update_after_reboot(info);
+		if (ret)
+		{
+			GAUGE_INFO("soc update after reboot failed!\n");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+
+static void gauge_update(struct work_struct *work)
+{
+	int charge_status;
+	int ret;
+	struct atc260x_gauge_info *info;
+	info = container_of(work, struct atc260x_gauge_info, work.work);
+	charge_status = info->status;
+	/* added by cxj @2014-12-10
+	 * when resuming, init_capacity cost much time,take it here
+	*/
+	if(resume_flag)
+	{
+		board_cm_pre_detect(info);
+		
+		ret = init_capacity(info);
+		if (ret)
+		{
+			GAUGE_ERR("[%s] init_gauge failed\n",__func__);
+			return;
+		}	
+		resume_flag = false;
+		GAUGE_INFO("[%s]resume,init_capacity done!",__func__);
+	}
+	
+	if (icm_detect_loop)
+		board_cm_detect(info);
+	
+	/* if the status has been changed,we discard the kfifo data*/
+	get_charge_status(&info->status);
+	if (charge_status ^ info->status)
+	{
+		GAUGE_INFO("charge status changed!\n");
+		start_anew(info);  
+		 if (info->status == POWER_SUPPLY_STATUS_DISCHARGING)
+			 update_discharge_resistor(info,info->soc_now);
+	}
+	
+	/*modified by cxj at 20141117*/
+	
+	if (info->status == POWER_SUPPLY_STATUS_CHARGING)
+	{
+		charge_process(info);
+	}
+	else if (info->status == POWER_SUPPLY_STATUS_DISCHARGING)
+	{
+		discharge_process(info);
+	}
+	else    /* POWER_SUPPLY_STATUS_NOT_CHARGING */
+	{
+		notcharging_process(info);
+	}
+	
+	soc_post_process(info);
+	generate_poll_interval(info);
+	queue_delayed_work(info->wq, &info->work, info->interval * HZ);
+}
+
+static void get_current_ratio(struct atc260x_gauge_info *info)
+{
+	if (ic_type == ATC260X_ICTYPE_2603A)
+	{
+		if (atc260x_get_version(info) == ATC260X_ICVER_D) 
+		{
+			info->current_ratio = PMU_VER_D_RATIO;
+		} 
+		else 
+		{
+			info->current_ratio = PMU_VER_ABC_RATIO;
+		} 
+	}
+	else if (ic_type == ATC260X_ICTYPE_2603C)
+	{
+		info->current_ratio = PMU_VER_ABC_RATIO;
+	}
+}
+static void resistor_init(struct atc260x_gauge_info *info)
+{
+	info->ch_resistor = 150;
+	info->disch_resistor = 250;
+	GAUGE_INFO("[%s] inited ch_resistor : %d, inited disch_resistor:%d\n",
+		__func__, info->ch_resistor, info->disch_resistor); 
+}
+static int  init_gauge(struct atc260x_gauge_info *info)
+{
+	int ret;
+	/*added by cxj @20141009*/
+	ic_type = get_pmu_ic_type(info);
+	/*get pmu current ratio according to pmu version*/
+	get_current_ratio(info);
+
+	GAUGE_INFO("[%s] PMU current ratio:%d\n", __func__, info->current_ratio);
+
+	start_anew(info);
+	info->interval = 5;
+	
+	info->first_product = first_product(info->atc260x);
+	GAUGE_INFO("[%s] first_product:%d\n", __func__, info->first_product);
+
+	resistor_init(info);
+	info->filter_algorithm = filter_algorithm3;
+	
+	/* detect cm before init_capacity */
+	if(ic_type == ATC260X_ICTYPE_2603C)
+		board_cm_pre_detect(info);
+	
+	ret = init_capacity(info);
+	if (ret)
+	{
+		GAUGE_ERR("[%s]init_capacity failed!\n",__func__);
+		return ret;
+	}
+	
+	return 0;
+}
+
+int get_cap_gauge(void)
+{
+	return global_gauge_info_ptr->soc_show;
+}
+
+static void set_callback(void)
+{
+	act260x_set_get_cap_point((void *)get_cap_gauge);
+	act260x_set_get_volt_point((void *)measure_vbatt);
+	act260x_set_get_cur_point((void *)measure_current);
+}
+
+void gauge_reset(struct atc260x_gauge_info *info)
+{
+	int resistor;
+	int data;
+	
+	resistor = gauge_reg_read(info->atc260x, PMU_OV_INT_EN);
+	resistor = resistor | (0x3fff << 2);
+	gauge_reg_write(info->atc260x, PMU_OV_INT_EN, resistor);
+
+	data = gauge_reg_read(info->atc260x, PMU_SYS_CTL9);
+	data = data & ~(0xff << 8);
+	gauge_reg_write(info->atc260x, PMU_SYS_CTL9, data);
+}
+
+static ssize_t show_reset(struct device *dev,struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+static ssize_t store_reset(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct platform_device *pdev = (struct platform_device *)container_of(dev,struct platform_device,dev);
+	struct atc260x_gauge_info *info = (struct atc260x_gauge_info *)platform_get_drvdata(pdev);
+
+	gauge_reset(info);
+	get_stored_time(info);
+	get_stored_soc(info);
+	first_product(info->atc260x);
+			
+	return count;
+}
+
+static ssize_t show_dump(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = (struct platform_device *)container_of(dev,struct platform_device,dev);
+	struct atc260x_gauge_info *info = (struct atc260x_gauge_info *)platform_get_drvdata(pdev);
+	batt_info_dump(info);
+	return 0;
+}
+static ssize_t store_dump(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return count;
+}
+
+static ssize_t show_test_kfifo(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = (struct platform_device *)container_of(dev,struct platform_device,dev);
+	struct atc260x_gauge_info *info = (struct atc260x_gauge_info *)platform_get_drvdata(pdev);
+	struct kfifo_data kfifo_data;
+	struct kfifo_data test;
+	int ret;
+	
+	kfifo_data.bat_curr = 1000;
+	kfifo_data.bat_vol = 3800;
+	kfifo_in(&info->fifo, &kfifo_data, sizeof(struct kfifo_data));
+	kfifo_data.bat_curr = 1500;
+	kfifo_data.bat_vol = 4000;
+	kfifo_in(&info->fifo, &kfifo_data, sizeof(struct kfifo_data));
+	ret = kfifo_out(&info->fifo, &test, sizeof(struct kfifo_data));
+	printk("%s : test.current :%d\n", __func__, test.bat_curr);
+	printk("%s : test.voltage :%d\n", __func__, test.bat_vol);
+	ret = kfifo_out_peek(&info->fifo, &test, sizeof(struct kfifo_data));
+	printk("%s : peer,test.current :%d\n", __func__, test.bat_curr);
+	printk("%s : peer,test.voltage :%d\n", __func__, test.bat_vol);
+	return 0;
+}
+static ssize_t store_test_kfifo(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return count;
+}
+
+static ssize_t show_test_filter(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	int test_buf[10] = {550,560,    600,610,619,605, 608,    700,710,720};
+	ret = filter_algorithm1(test_buf, 10, CURRENT_TYPE);
+	printk("ret = %d\n", ret);
+	
+	return 0;
+}
+static ssize_t store_test_filter(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return count;
+}
+
+static ssize_t show_test_icm(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret,icm_ok;
+	ret = atc260x_pstore_get(global_gauge_info_ptr->atc260x,ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST,&icm_ok);
+	printk("\n*********************************************\n");
+	printk("  test_icm_enable:%d [1:enable 0:disable]\n",test_icm_enable);
+	printk("  icm_detect_loop:%d [1:in loop 0:not in loop]\n",icm_detect_loop);
+	printk("  board_has_cm   :%d [1:icm works 0:icm fails]\n",board_has_cm);
+	printk("   ICM_EXIST     :%d [1:reg = 1 0:reg = 0]\n",icm_ok);
+	printk("\n**********************************************\n");
+	return sprintf(buf,"%d\n",board_has_cm);
+}
+static ssize_t store_test_icm(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int icm_status_to_fail;
+	int ret;
+	sscanf(buf,"%d\n",&icm_status_to_fail);
+	if(icm_status_to_fail == 1)
+	{
+		test_icm_enable = true;
+		printk("********icm is set to fail :%d********\n",icm_status_to_fail);
+	}
+	else if(icm_status_to_fail == 0)
+	{
+		test_icm_enable = false;
+		icm_detect_loop = true;
+		board_has_cm = true;
+		icm_detect_cycle = 0;
+		ret = atc260x_pstore_set(global_gauge_info_ptr->atc260x,ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST,0);
+		if(ret)
+		{
+			printk("Cann't write to the ICM_EXIST bit\n");
+		}
+		else
+		{
+			printk("write 0 to ICM_EXIST successfully\n");
+		}
+		printk("recover to work normally:%d\n",icm_status_to_fail);
+		
+	}
+	else
+		printk("please enter 0 or 1:0-normal work 1-icm fail\n");
+		
+	return count;
+		
+}
+static ssize_t store_log_switch(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int log_switch;
+
+	sscanf(buf,"%d\n",&log_switch);
+	if (log_switch == 1 || log_switch == 0)
+	{
+		global_gauge_info_ptr->cfg_items.log_switch = log_switch;
+		printk("now log_switch=%d\n",log_switch);
+	}
+	else
+		printk("wrong parameter!\n");
+	return count;
+}
+static ssize_t show_log_switch(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf,"%d\n",global_gauge_info_ptr->cfg_items.log_switch);
+}
+
+static ssize_t store_print_switch(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int print_switch;
+	
+	sscanf(buf,"%d\n",&print_switch);
+	if (print_switch == 1 || print_switch == 0)
+	{
+		global_gauge_info_ptr->cfg_items.print_switch = print_switch;
+		printk("now print_switch=%d\n",print_switch);
+	}
+	else
+		printk("wrong parameter!\n");
+	return count;
+}
+static ssize_t show_print_switch(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf,"%d\n",global_gauge_info_ptr->cfg_items.print_switch);
+}
+
+static struct device_attribute gauge_attrs[] = 
+{
+	__ATTR(test_filter, S_IRUGO | S_IWUSR, show_test_filter, store_test_filter),
+	__ATTR(test_kfifo, S_IRUGO | S_IWUSR, show_test_kfifo, store_test_kfifo),
+	__ATTR(reset, S_IRUGO | S_IWUSR, show_reset, store_reset),
+	__ATTR(dump, S_IRUGO | S_IWUSR, show_dump, store_dump),
+	__ATTR(test_icm, S_IRUGO | S_IWUSR, show_test_icm, store_test_icm),
+	__ATTR(log_switch, S_IRUGO | S_IWUSR,show_log_switch,store_log_switch),
+	__ATTR(print_switch, S_IRUGO | S_IWUSR,show_print_switch,store_print_switch),
+
+};
+
+int gauge_create_sysfs(struct device *dev)
+{
+	int r, t;
+	
+	GAUGE_INFO("[%s] create sysfs for gauge\n", __func__);
+	
+	for (t = 0; t < ARRAY_SIZE(gauge_attrs); t++) 
+	{
+		r = device_create_file(dev, &gauge_attrs[t]);
+	
+		if (r)
+		{
+			dev_err(dev, "failed to create sysfs file\n");
+			return r;
+		}
+	}
+	
+	return 0;
+}
+
+void gauge_remove_sysfs(struct device *dev)
+{
+	int  t;
+
+	GAUGE_INFO("[%s]remove sysfs for gauge\n", __func__);
+	for (t = 0; t < ARRAY_SIZE(gauge_attrs); t++) 
+	{
+		device_remove_file(dev, &gauge_attrs[t]);
+	}
+}
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void atc260x_gauge_early_suspend(struct early_suspend *handler)
+{
+    printk("[%s-%d] enter early_suspend\n", __FUNCTION__, __LINE__);
+}
+
+static void atc260x_gauge_later_resume(struct early_suspend *handler)
+{
+    printk("[%s-%d] enter later_resume\n", __FUNCTION__, __LINE__);
+
+}
+#endif  //CONFIG_HAS_EARLYSUSPEND
+
+static int atc260x_change_log_switch_pre_suspend(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+	switch (event) {
+	case PM_SUSPEND_PREPARE:
+		if(global_gauge_info_ptr->cfg_items.log_switch == 1)
+		{
+			global_gauge_info_ptr->store_gauge_info_switch_sav = global_gauge_info_ptr->cfg_items.log_switch;
+			global_gauge_info_ptr->cfg_items.log_switch = 0;
+		}
+		GAUGE_INFO("log_switch = %d\n",global_gauge_info_ptr->cfg_items.log_switch);
+		return NOTIFY_OK;
+	default:
+		return NOTIFY_DONE;
+	}
+}
+static int atc260x_change_log_switch_pre_shutdown(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+	cancel_delayed_work_sync(&global_gauge_info_ptr->work);
+	switch (event) {
+	case SYS_POWER_OFF:
+		if(global_gauge_info_ptr->cfg_items.log_switch == 1)
+		{
+			global_gauge_info_ptr->store_gauge_info_switch_sav = global_gauge_info_ptr->cfg_items.log_switch;
+			global_gauge_info_ptr->cfg_items.log_switch = 0;
+		}
+		GAUGE_INFO("log_switch = %d\n",global_gauge_info_ptr->cfg_items.log_switch);
+		/*
+		 * chenbo at 20150514
+		 * restore last soc during discharging,
+		 * if soc jump from some soc(>1%) to zero.
+		 */
+		if (global_gauge_info_ptr->soc_show == 0 &&
+			global_gauge_info_ptr->soc_last > 0) {
+			global_gauge_info_ptr->soc_show = global_gauge_info_ptr->soc_last;
+			store_soc(global_gauge_info_ptr);
+		}
+		return NOTIFY_OK;
+	default:
+		return NOTIFY_DONE;
+	}
+}
+static struct notifier_block atc260x_suspend_notify = {
+	.notifier_call = atc260x_change_log_switch_pre_suspend,
+};
+static struct notifier_block atc260x_shutdown_notify = {
+	.notifier_call = atc260x_change_log_switch_pre_shutdown,
+};
+
+static  int atc260x_gauge_probe(struct platform_device *pdev)
+{
+
+	struct atc260x_dev *atc260x = dev_get_drvdata(pdev->dev.parent);
+	struct atc260x_gauge_info *info;
+	int ret;
+	
+	info = kzalloc(sizeof(struct atc260x_gauge_info), GFP_KERNEL);
+	if (info == NULL)
+		return -ENOMEM;	
+
+	ret =  kfifo_alloc(&info->fifo, 2 * sizeof(struct kfifo_data), GFP_KERNEL);
+	if (ret)
+	{
+		GAUGE_ERR("[%s] error kfifo_alloc\n", __func__);
+		goto free;
+	}
+	
+	info->atc260x = atc260x;
+	info->node = pdev->dev.of_node;
+	global_gauge_info_ptr = info;
+	first_store_gauge_info = 1;
+
+	mutex_init(&info->lock);
+	
+	platform_set_drvdata(pdev, info);
+	
+	ret = gauge_create_sysfs(&pdev->dev);
+	if (ret)
+	{
+		GAUGE_ERR("gauge_create_sysfs failed!\n");
+		goto free_fifo; 
+	}
+
+	if (get_cfg_items(info))
+	{
+		GAUGE_ERR("get_cfg_items failed!\n");
+		goto remove_sysfs;
+	}
+	/*modified @2014-12-20*/
+	ocv_soc_table_init(info);
+	
+   	if (init_gauge(info))
+	{
+		GAUGE_ERR("init_gauge failed!\n");
+	 	goto free_fifo;
+	}
+
+	set_callback();
+
+	register_pm_notifier(&atc260x_suspend_notify);
+	register_reboot_notifier(&atc260x_shutdown_notify);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	info->early_suspend.suspend = atc260x_gauge_early_suspend;
+	info->early_suspend.resume = atc260x_gauge_later_resume;
+	register_early_suspend(&info->early_suspend);
+#endif
+
+	/*Create a work queue for fuel gauge*/
+	info->wq =
+		create_singlethread_workqueue("atc260x_gauge_wq");
+	if (info->wq == NULL) 
+	{
+		GAUGE_ERR("[%s] failed to create work queue\n", __func__);
+		goto remove_sysfs;
+	}
+	
+	INIT_DELAYED_WORK(&info->work, gauge_update);
+	queue_delayed_work(info->wq, &info->work, 0 * HZ);
+
+	return 0;
+	
+remove_sysfs:
+	gauge_remove_sysfs(&pdev->dev);
+free_fifo:
+	kfifo_free(&info->fifo);
+free:
+	kfree(info);
+
+	 return ret;
+}
+
+static  int atc260x_gauge_remove(struct platform_device *pdev)
+{
+
+	struct atc260x_gauge_info *info = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&info->early_suspend);
+#endif
+	kfifo_free(&info->fifo);
+	kfree(info);
+
+	return 0;
+}
+
+
+static int atc260x_gauge_suspend(struct platform_device *pdev, pm_message_t m)
+{
+	struct atc260x_gauge_info *info = platform_get_drvdata(pdev);
+	get_charge_status(&pre_charge_status);
+	GAUGE_DBG("pre_charge_status:%d[0:NOT_CHARGE,else:DISCHARGE]",pre_charge_status^POWER_SUPPLY_STATUS_NOT_CHARGING);
+	cancel_delayed_work_sync(&info->work); 
+	start_anew(info);
+	getnstimeofday(&suspend_ts);
+	return 0;
+}
+
+static int atc260x_gauge_resume(struct platform_device *pdev)
+{
+	struct atc260x_gauge_info *info = platform_get_drvdata(pdev);
+
+	resume_flag = true;
+
+	info->cfg_items.log_switch = info->store_gauge_info_switch_sav;
+	GAUGE_DBG("=====resume:switch= %d\n",info->cfg_items.log_switch);
+	
+ 	queue_delayed_work(info->wq, &info->work, 0 * HZ);
+	
+ 	return 0;
+}
+static void atc260x_gauge_shutdown(struct platform_device *pdev)
+{
+	struct atc260x_gauge_info *info = platform_get_drvdata(pdev);
+	store_time_pre_shutdown(info);
+}
+/* added by cxj @20141009 */
+static const struct of_device_id atc260x_cap_gauge_match[] = {
+	{ .compatible = "actions,atc2603a-cap-gauge", },
+	{ .compatible = "actions,atc2603c-cap-gauge", },
+	{ .compatible = "actions,atc2609a-cap-gauge", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_cap_gauge_match);
+static struct platform_driver atc260x_gauge_driver = {
+	.probe      = atc260x_gauge_probe,
+	.remove     = atc260x_gauge_remove,
+	.driver     = 
+	{
+		.name = "atc260x-cap-gauge",
+		.of_match_table = of_match_ptr(atc260x_cap_gauge_match), /* by cxj */
+	},
+	.suspend    = atc260x_gauge_suspend,
+	.resume     = atc260x_gauge_resume,
+	.shutdown   = atc260x_gauge_shutdown,
+};
+
+static int __init atc260x_gauge_init(void)
+{
+	GAUGE_INFO("========This is a new reconstructed gauge by actions==========\n");
+	GAUGE_INFO("[%s] drv name:%s, drv version:%s\n", __func__, THIS_MODULE->name, THIS_MODULE->version);
+	return platform_driver_register(&atc260x_gauge_driver);
+}
+module_init(atc260x_gauge_init);
+
+static void __exit atc260x_gauge_exit(void)
+{
+	platform_driver_unregister(&atc260x_gauge_driver);    
+}
+module_exit(atc260x_gauge_exit);
+
+/* Module information */
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("ATC260X gauge drv");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-cap-gauge");
+MODULE_VERSION("Actions-v1-20150720151405");
diff --git a/drivers/power/atc260x_power/Makefile b/drivers/power/atc260x_power/Makefile
new file mode 100755
index 0000000..aee57d3
--- /dev/null
+++ b/drivers/power/atc260x_power/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CHARGER_DRV_ATC206X) += atc260x_power.o
+atc260x_power-y := atc260x_power_main.o atc2603a_power_phy.o atc2603c_power_phy.o atc260x_power_event_log.o
\ No newline at end of file
diff --git a/drivers/power/atc260x_power/atc2603a_power_phy.c b/drivers/power/atc260x_power/atc2603a_power_phy.c
new file mode 100755
index 0000000..f9df351
--- /dev/null
+++ b/drivers/power/atc260x_power/atc2603a_power_phy.c
@@ -0,0 +1,618 @@
+/*
+ * atc260x_power_2603A_phy.c  --  Power driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This file include reg write and read opts mainly.
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/mfd/atc260x/atc260x.h>
+#include <linux/power_supply.h>
+#include "atc260x_power.h"
+
+/* ATC2603A_PMU_CHARGER_CTL0 */
+#define     PMU_CHARGER_CTL0_CHGAUTO_DETECT_EN      (1 << 0)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT       (1)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_MASK        (0x3 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_100MV       (0 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_200MV       (1 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_300MV       (2 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_400MV       (3 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_CURRENT_TEMP       (1 << 3)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT   (4)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET         (0x3 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_3810MV      (0 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_3960MV      (1 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_4250MV      (2 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_4400MV      (3 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR             (1 << 6)
+#define     PMU_CHARGER_CTL0_DTSEL_SHIFT            (7)
+#define     PMU_CHARGER_CTL0_DTSEL_MASK             (0x1 << PMU_CHARGER_CTL0_DTSEL_SHIFT)
+#define     PMU_CHARGER_CTL0_DTSEL_12MIN            (0 << PMU_CHARGER_CTL0_DTSEL_SHIFT)
+#define     PMU_CHARGER_CTL0_DTSEL_20S              (1 << PMU_CHARGER_CTL0_DTSEL_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_FORCE_OFF          (1 << 8)
+#define     PMU_CHARGER_CTL0_TRICKLEEN              (1 << 9)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT    (10)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_MASK     (0x3 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_30MIN    (0 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_40MIN    (1 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_50MIN    (2 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_60MIN    (3 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT    (12)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_MASK     (0x3 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_4H       (0 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_6H       (1 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_8H       (2 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_12H      (3 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGTIME                (1 << 14)
+#define     PMU_CHARGER_CTL0_ENCH                   (1 << 15)
+
+/* ATC2603A_PMU_CHARGER_CTL1 */
+#define     PMU_CHARGER_CTL1_ICHG_REG_CC_SHIFT      (0)
+#define     PMU_CHARGER_CTL1_ICHG_REG_CC_MASK       (0xf << PMU_CHARGER_CTL1_ICHG_REG_CC_SHIFT)
+#define     PMU_CHARGER_CTL1_ICHG_REG_CC(i)         \
+                    ((((i) / 100) << PMU_CHARGER_CTL1_ICHG_REG_CC_SHIFT) & PMU_CHARGER_CTL1_ICHG_REG_CC_MASK)
+
+#define     PMU_CHARGER_CTL1_BAT_EXIST_EN           (1 << 5)
+#define     PMU_CHARGER_CTL1_CURRENT_SOFT_START     (1 << 6)
+#define     PMU_CHARGER_CTL1_STOPV_SHIFT            (7)
+#define     PMU_CHARGER_CTL1_STOPV_MASK             (0x1 << PMU_CHARGER_CTL1_STOPV_SHIFT)
+#define     PMU_CHARGER_CTL1_STOPV_4180MV           (0 << PMU_CHARGER_CTL1_STOPV_SHIFT)
+#define     PMU_CHARGER_CTL1_STOPV_4160MV           (1 << PMU_CHARGER_CTL1_STOPV_SHIFT)
+#define     PMU_CHARGER_CTL1_CHARGER_TIMER_END      (1 << 8)
+#define     PMU_CHARGER_CTL1_BAT_DT_OVER            (1 << 9)
+#define     PMU_CHARGER_CTL1_BAT_EXIST              (1 << 10)
+#define     PMU_CHARGER_CTL1_CUR_ZERO               (1 << 11)
+#define     PMU_CHARGER_CTL1_CHGPWROK               (1 << 12)
+#define     PMU_CHARGER_CTL1_PHASE_SHIFT            (13)
+#define     PMU_CHARGER_CTL1_PHASE_MASK             (0x3 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_PHASE_PRECHARGE        (1 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_PHASE_CONSTANT_CURRENT (2 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_PHASE_CONSTANT_VOLTAGE (3 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_CHGEND                 (1 << 15)
+
+/* ATC2603A_PMU_CHARGER_CTL2 */
+#define     PMU_CHARGER_CTL2_ICHG_REG_T_SHIFT       (4) /*jlingzhang*/
+#define     PMU_CHARGER_CTL2_ICHG_REG_T_MASK        (0x3 << PMU_CHARGER_CTL2_ICHG_REG_T_SHIFT)
+#define     PMU_CHARGER_CTL2_ICHG_REG_T(i)          \
+                    ((((i) / 100) << PMU_CHARGER_CTL2_ICHG_REG_T_SHIFT) & PMU_CHARGER_CTL2_ICHG_REG_T_MASK)
+
+#define     PMU_CHARGER_CTL2_CV_SET                 (1 << 6)
+
+#define		PMU_CHARGER_CTL2_TEMPTH1_SHIFT             (13) 
+#define		PMU_CHARGER_CTL2_TEMPTH1_MASK 		(0x3 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH1_75			(0 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH1_90			(1 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH1_105			(2 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH1_115			(3 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define		PMU_CHARGER_CTL2_TEMPTH2_SHIFT             (11) 
+#define		PMU_CHARGER_CTL2_TEMPTH2_MASK             (0x3 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT) 
+#define 		PMU_CHARGER_CTL2_TEMPTH2_90			(0 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH2_105			(1 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH2_120			(2 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH2_135			(3 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define		PMU_CHARGER_CTL2_TEMPTH3_SHIFT             (9)
+#define		PMU_CHARGER_CTL2_TEMPTH3_MASK		 (0x3 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH3_100			(0 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH3_120			(1 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 		PMU_CHARGER_CTL2_TEMPTH3_130			(2 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+/* ATC2603A_PMU_SYS_Pending */
+#define     PMU_SYS_PENDING_BAT_OV                  (1 << 15)
+
+/* ATC2603A_PMU_OT_CTL */
+#define     PMU_OT_CTL_OT                           (1 << 15)
+#define 	PMU_OT_CTL_OT_INT_EN 			(1 << 12)
+#define 	PMU_OT_CTL_OT_SHUTOFF_EN		(1 << 11)
+#define	PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT        (9) 
+#define	PMU_OT_CTL_OT_SHUTOFF_SET_MASK        (0x3 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT) 
+#define	PMU_OT_CTL_OT_SHUTOFF_SET_100        (0 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define	PMU_OT_CTL_OT_SHUTOFF_SET_120        (1 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define	PMU_OT_CTL_OT_SHUTOFF_SET_130        (2 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define	PMU_OT_CTL_OT_SHUTOFF_SET_140        (3 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define 	PMU_OT_CTL_OT_EN					(1 << 8)
+ 
+ /* ATC2603A_APDS_CTL */
+#define	VBUS_CONTROL_EN 	(1 << 15)
+#define     VBUS_CONTROL_SEL_SHIFT                (14)
+#define     VBUS_CONTROL_SEL_MASK                (1 << VBUS_CONTROL_SEL_SHIFT)
+#define     VBUS_CONTROL_SEL_VOL                (0x0 << VBUS_CONTROL_SEL_SHIFT)
+#define     VBUS_CONTROL_SEL_CUR                (0x1 << VBUS_CONTROL_SEL_SHIFT)
+#define	VBUS_CUR_LIMITED_SHIFT		(12)
+#define	VBUS_CUR_LIMITED_MASK		(0x3 << VBUS_CUR_LIMITED_SHIFT)
+#define	VBUS_CUR_LIMITED_100MA	(0x0 << VBUS_CUR_LIMITED_SHIFT)
+#define	VBUS_CUR_LIMITED_300MA	(0x1 << VBUS_CUR_LIMITED_SHIFT)
+#define	VBUS_CUR_LIMITED_500MA	(0x2 << VBUS_CUR_LIMITED_SHIFT)
+#define	VBUS_CUR_LIMITED_800MA	(0x3 << VBUS_CUR_LIMITED_SHIFT)
+#define     APDS_CTL_VBUS_VOL_LIMITED_SHIFT (10)
+#define     APDS_CTL_VBUS_VOL_LIMITED_MASK (0x3 << APDS_CTL_VBUS_VOL_LIMITED_SHIFT)
+#define     APDS_CTL_VBUSOTG                (1 << 9)
+#define     APDS_CTL_VBUS_PD                 (1 << 2)
+#define     APDS_CTL_WALL_PD			(1 << 1)
+#define     ATC260X_SUPPLY_WALL          (0)
+#define     ATC260X_SUPPLY_VBUS          (1)
+#define     ATC260X_SUPPLY_BAT             (2)
+
+#define    CV_SET_VAL                             4300
+ int atc2603a_read_adc(struct atc260x_dev *atc260x, const char *channel_name)
+{
+	int ret;
+	int translate_data;
+	unsigned int channel_num;
+	
+	channel_num = atc260x_auxadc_find_chan(atc260x,channel_name);
+	if(channel_num < 0 )
+	{
+		printk("not support this channel or the channel name is error!\n");
+		return channel_num;
+	}
+	ret = atc260x_auxadc_get_translated(atc260x,channel_num, &translate_data);
+	if(ret < 0)
+		printk("cannot get the correct translation data!\n");
+	
+	return translate_data;
+}
+ 
+ int atc2603a_read_adc_supply(struct atc260x_dev *atc260x,
+                     const char *channel_name,
+                     union power_supply_propval *val)
+{
+	int translate_data;
+
+	translate_data = atc2603a_read_adc(atc260x,channel_name);
+	val->intval = translate_data;
+	
+	return translate_data;
+}
+
+ int atc2603a_check_bat_online(struct atc260x_dev *atc260x)
+{
+	int ret;
+	int exist;
+
+	/* dectect bit 0 > 1 to start dectecting */
+	ret = atc260x_set_bits(atc260x, ATC2603A_PMU_CHARGER_CTL1, 
+	PMU_CHARGER_CTL1_BAT_EXIST_EN, PMU_CHARGER_CTL1_BAT_EXIST_EN);
+	if (ret < 0)
+		return ret;
+
+	/* wait bat detect over */
+	msleep(ATC260X_BAT_DETECT_DELAY_MS);
+
+	ret = atc260x_reg_read(atc260x, ATC2603A_PMU_CHARGER_CTL1);
+	if (ret < 0)
+		return ret;
+
+	exist = ret & PMU_CHARGER_CTL1_BAT_EXIST;
+
+	/* cleare battery detect bit, otherwise cannot changer */
+	ret = atc260x_set_bits(atc260x, ATC2603A_PMU_CHARGER_CTL1, 
+	PMU_CHARGER_CTL1_BAT_EXIST_EN, 0);
+	if (ret < 0)
+		return ret;
+
+	if (exist) {
+		int bat_v = atc2603a_read_adc(atc260x, "BATV");
+		printk("%s bat_v:%d\n",__func__, bat_v);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int atc2603a_check_wall_online(struct atc260x_charger *charger,  
+	union power_supply_propval *val)
+{
+	int ret;
+	
+		ret = atc2603a_read_adc_supply(charger->atc260x, "WALLV", val);
+		if (ret < 0)
+			return ret;
+
+
+        if ((ret > charger->wall_v_thresh) &&
+        	(ret > charger->bat_mv))
+		val->intval = 1;
+	else
+		val->intval = 0;
+
+	return ret;
+}
+
+int atc2603a_check_vbus_online(struct atc260x_charger *charger, 
+	union power_supply_propval *val)
+{
+	int ret = 0;
+	
+	if (charger->g_vbus_is_otg) {
+		/* no usb-charger while otg is enabled */
+		val->intval = 0;
+	} else {
+		ret = atc2603a_read_adc_supply(charger->atc260x, "VBUSV", val);
+		if (ret < 0)
+			return ret;
+
+		if (ret > ATC260X_VBUS_VOLTAGE_THRESHOLD)
+			val->intval = 1;
+		else
+			val->intval = 0;
+	}
+
+	return ret;
+}
+
+void atc2603a_charger_update_phrase_type(struct atc260x_charger *charger)
+{
+	int ret = atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1);
+	charger->chg_type = ret & PMU_CHARGER_CTL1_PHASE_MASK;
+}
+
+void atc2603a_set_charge(struct atc260x_dev *atc260x, int on)
+{
+	int val;
+
+	if (on) {
+		val = PMU_CHARGER_CTL0_ENCH;
+	} else {    
+		val = 0;
+	}
+	
+	atc260x_set_bits(atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+		PMU_CHARGER_CTL0_ENCH, val);
+}
+
+/**
+ * @brief atc260x_check_bat_status
+ * check if battery is healthy
+ * @return BATTERY_HEALTHY_STATE
+ */
+static int atc2603a_bat_check_health_pre(struct atc260x_dev *atc260x)
+{
+	int batv = atc2603a_read_adc(atc260x, "BATV");
+	if (batv <= 200)
+	{
+		int pmu_charger_ctl1 =  atc260x_reg_read(atc260x, ATC2603A_PMU_CHARGER_CTL1);
+		pmu_charger_ctl1 &= ~0xf;
+		atc260x_reg_write(atc260x, ATC2603A_PMU_CHARGER_CTL1, pmu_charger_ctl1); 
+		atc2603a_set_charge(atc260x, 1);
+		msleep(64);
+		batv = atc2603a_read_adc(atc260x, "BATV");
+		atc2603a_set_charge(atc260x, 0);
+		if (batv <= 200)
+		{
+			return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+		} 
+	}
+
+	return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+/*detect if the bat is OVERVOLTAGE(or too heat)*/
+int atc2603a_bat_check_health(struct atc260x_charger *charger, int *health)
+{
+	struct atc260x_dev *atc260x = charger->atc260x;
+	int ret;
+
+	ret = atc260x_reg_read(atc260x, ATC2603A_PMU_SYS_PENDING);
+	if (ret < 0)
+		return ret;
+
+	if (ret & PMU_SYS_PENDING_BAT_OV) 
+	{
+		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+		return 0;
+	}
+
+	*health = POWER_SUPPLY_HEALTH_GOOD;
+
+	return 0;
+}
+
+void atc2603a_set_trick_current(struct atc260x_charger *charger)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2, 
+                    atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2) |
+                    PMU_CHARGER_CTL2_ICHG_REG_T(charger->thresholds.trickle_charge_current));
+	
+	 atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+		atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) |
+		PMU_CHARGER_CTL0_TRICKLEEN |
+		PMU_CHARGER_CTL0_CHARGE_TIMER2_30MIN); 
+}
+
+int atc2603a_get_constant_current(struct atc260x_charger *charger)
+{
+	 return atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1) & 0xf;
+}
+
+
+void atc2603a_set_constant_current(struct atc260x_charger *charger, int set_current)
+{
+	/* 
+	* constant charge current: 1000ma 
+	* charge stop voltage (OCV): 4.16v
+	*/
+	unsigned int current_now = atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1);
+	
+	if ((current_now & 0xf) != set_current)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1, 
+			(current_now & (~0xf)) | set_current);
+	}
+	
+	 atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+		atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) |
+		PMU_CHARGER_CTL0_CHARGE_TIMER1_12H);
+}
+
+static void set_vbus_control_mode(struct atc260x_charger *charger, int vbus_control_mode)
+{
+	if (vbus_control_mode == VOLTAGE_LIMITED)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+						(atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL) &
+						(~VBUS_CONTROL_SEL_MASK )) |
+						VBUS_CONTROL_EN |
+						VBUS_CONTROL_SEL_VOL);
+		power_dbg("vubs control mode:***VOLTAGE_LIMITED***, ATC2603A_PMU_APDS_CTL:%x\n",
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL));
+	}
+	else if (vbus_control_mode == CURRENT_LIMITED)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+						(atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL) &
+						(~VBUS_CUR_LIMITED_MASK ) &
+						(~VBUS_CONTROL_SEL_MASK)) |
+						VBUS_CONTROL_EN |
+						VBUS_CUR_LIMITED_500MA |
+						VBUS_CONTROL_SEL_CUR);
+		power_dbg("vubs control mode:***CURRENT_LIMITED***, ATC2603A_PMU_APDS_CTL:%x\n",
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL));
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+						(atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL) &
+						(~VBUS_CONTROL_EN)));
+	}
+}
+
+
+void atc2603a_set_vbus_path(struct atc260x_charger * charger, bool enable)
+{
+	if (enable)
+	{
+		/* connect the path */
+		atc260x_set_bits(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+		APDS_CTL_VBUSOTG, 0);
+	}
+	else 
+	{
+		/* cut off the path */
+		atc260x_set_bits(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+		APDS_CTL_VBUSOTG, APDS_CTL_VBUSOTG);
+	}
+}
+
+int atc2603a_get_vbus_path(struct atc260x_charger * charger)
+{
+	/*if vbus is cut,return 0,else return 1 */
+	return !(atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL) & APDS_CTL_VBUSOTG);
+}
+
+/*5k resistor of wall enable*/
+void atc2603a_set_apds_wall_pd(struct atc260x_charger *charger, bool enable)
+{
+	if (enable)
+	{
+		atc260x_set_bits(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+			APDS_CTL_WALL_PD, APDS_CTL_WALL_PD);
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL) & ~APDS_CTL_WALL_PD);
+	}
+}
+
+void atc2603a_set_apds_vbus_pd(struct atc260x_charger *charger, bool enable)
+{
+	if (enable)
+	{
+		atc260x_set_bits(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+			APDS_CTL_VBUS_PD, APDS_CTL_VBUS_PD);
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_APDS_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_APDS_CTL)  & ~APDS_CTL_VBUS_PD);
+	}
+}
+
+
+void atc2603a_set_syspwr(struct atc260x_charger *charger)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+		atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) |
+		PMU_CHARGER_CTL0_CHGPWR_SET_100MV | 
+		PMU_CHARGER_CTL0_CHG_SYSPWR_SET_4250MV);
+
+	if (charger->cfg_items.ext_dcdc_exist == 1) 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) & ~PMU_CHARGER_CTL0_CHG_SYSPWR);  
+	}
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) | PMU_CHARGER_CTL0_CHG_SYSPWR);  
+	}
+}
+
+void atc2603a_set_current_soft_start(struct atc260x_charger *charger, bool enable)
+{
+	if (enable)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1, 
+				atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1) |
+				PMU_CHARGER_CTL1_CURRENT_SOFT_START );
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1, 
+				atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1) &
+				(~PMU_CHARGER_CTL1_CURRENT_SOFT_START) );
+	}
+} 
+
+void atc2603a_cv_set(struct atc260x_charger *charger, int cv_val_mv)
+{
+	if (cv_val_mv == 4300)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2, 
+	                    atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2) |
+	                    PMU_CHARGER_CTL2_CV_SET);
+	}
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2, 
+	                    atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2) &
+	                    ~PMU_CHARGER_CTL2_CV_SET);
+	}
+}
+
+void atc2603a_set_ot_shutoff(struct atc260x_charger *charger, int ot_shutoff_enable)
+{
+	if (!ot_shutoff_enable) //disable over temperature shutoff bit
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_OT_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_OT_CTL) & ~PMU_OT_CTL_OT_SHUTOFF_EN);
+	} 
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_OT_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_OT_CTL) | PMU_OT_CTL_OT_SHUTOFF_EN);
+	}
+}
+
+void atc2603a_set_change_current_temp(struct atc260x_charger *charger, int  change_current_temp)
+{
+	 if (change_current_temp) {
+	      atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+	          atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) | PMU_CHARGER_CTL0_CHG_CURRENT_TEMP);
+	 } else {
+	     atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+	          atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) & (~PMU_CHARGER_CTL0_CHG_CURRENT_TEMP));
+	 }
+}
+
+void atc2603a_set_charger_stop_voltage(struct atc260x_charger *charger, int stopv)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1, 
+			(atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL1) &
+			(~ PMU_CHARGER_CTL1_STOPV_MASK)) | 
+			stopv);
+}
+
+
+
+void atc2603a_set_temp_threshold(struct atc260x_charger *charger)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2, 
+                    (atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL2)  & 
+                    (~PMU_CHARGER_CTL2_TEMPTH1_MASK) &
+                    (~PMU_CHARGER_CTL2_TEMPTH2_MASK) &
+                    (~PMU_CHARGER_CTL2_TEMPTH3_MASK)) |
+                    PMU_CHARGER_CTL2_TEMPTH1_90 | PMU_CHARGER_CTL2_TEMPTH2_105 | PMU_CHARGER_CTL2_TEMPTH3_120);
+}
+
+void atc2603a_set_auto_detect_charging(struct atc260x_charger *charger, bool enable)
+{
+	if (!enable)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0,
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) & 
+			~PMU_CHARGER_CTL0_CHGAUTO_DETECT_EN &
+			~PMU_CHARGER_CTL0_CHG_FORCE_OFF);
+	}
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0,
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) | 
+			PMU_CHARGER_CTL0_CHGAUTO_DETECT_EN |
+			PMU_CHARGER_CTL0_CHG_FORCE_OFF);
+
+		atc260x_reg_write(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0, 
+			atc260x_reg_read(charger->atc260x, ATC2603A_PMU_CHARGER_CTL0) |
+			PMU_CHARGER_CTL0_DTSEL_12MIN |
+			PMU_CHARGER_CTL0_CHGTIME);
+
+	}
+}
+
+void atc2603a_charger_phy_init(struct atc260x_charger *charger)
+{
+	/*
+	  * charger init
+	  */
+	atc2603a_set_trick_current(charger);// trickle charge current: 100mA
+	atc2603a_set_constant_current(charger, PMU_CHARGER_CTL1_ICHG_REG_CC(charger->thresholds.constant_charge_current));//set constant current value
+	atc2603a_set_current_soft_start(charger, true);
+	atc2603a_set_charger_stop_voltage(charger, PMU_CHARGER_CTL1_STOPV_4160MV);
+	atc2603a_cv_set(charger, CV_SET_VAL);//enable fast charge
+	atc2603a_set_ot_shutoff(charger, charger->cfg_items.ot_shutoff_enable);//shutoff charger when ov according to cfg xml
+	 atc2603a_set_temp_threshold(charger);// set temp protect threshold
+	atc2603a_set_change_current_temp(charger, charger->cfg_items.change_current_temp);
+	atc2603a_set_auto_detect_charging(charger, false);
+
+	/*
+	  * syspwr init
+	  */
+	atc2603a_set_syspwr(charger);
+
+	/*
+	  * wall init
+	  */
+	atc2603a_set_apds_wall_pd(charger, true);
+}
+
+void atc2603a_dump_regs(struct atc260x_charger *charger)
+{
+	int i;
+	for (i = 0x00; i <= 0x64; i++ )
+	{
+		//printk("0x%x:0x%x\n", i, atc260x_reg_read(charger->atc260x, ATC2603A_PMU_BASE + i));
+	}
+}
+
+
+void atc2603a_get_info(struct atc260x_charger *charger)
+{
+	charger->name = "power-2603A";
+	//charger->read_adc = atc2603a_read_adc_supply;
+	charger->read_adc = atc2603a_read_adc;
+	charger->check_bat_online = atc2603a_check_bat_online;
+	charger->check_wall_online = atc2603a_check_wall_online;
+	charger->check_vbus_online = atc2603a_check_vbus_online;
+	charger->update_phrase_type = atc2603a_charger_update_phrase_type;
+	charger->update_charger_mode = NULL;
+	charger->set_charge = atc2603a_set_charge;
+	charger->set_vbus_ctl_mode = set_vbus_control_mode;
+	charger->check_health_pre = atc2603a_bat_check_health_pre;
+	charger->check_health = atc2603a_bat_check_health;
+	charger->set_constant_current = atc2603a_set_constant_current;
+	charger->get_constant_current = atc2603a_get_constant_current;
+	charger->cv_set = atc2603a_cv_set;
+	charger->set_apds_wall_pd = atc2603a_set_apds_wall_pd;
+	charger->set_vbus_path = atc2603a_set_vbus_path;
+	charger->get_vbus_path = atc2603a_get_vbus_path;
+	charger->set_apds_vbus_pd = atc2603a_set_apds_vbus_pd;
+	charger->charger_phy_init = atc2603a_charger_phy_init;
+	charger->dump_regs = atc2603a_dump_regs;
+}
diff --git a/drivers/power/atc260x_power/atc2603c_power_phy.c b/drivers/power/atc260x_power/atc2603c_power_phy.c
new file mode 100755
index 0000000..6d9668a
--- /dev/null
+++ b/drivers/power/atc260x_power/atc2603c_power_phy.c
@@ -0,0 +1,642 @@
+/*
+ * atc260x_power_2603C_phy.c  --  Power driver for ATC260X
+ *
+ * Copyright 2014 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This file include reg write and read opts mainly.
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+#include <linux/delay.h>
+#include <mach/hardware.h>
+
+//#include "../../mfd/atc260x_dev.h"
+#include <linux/mfd/atc260x/atc260x.h>
+#include <linux/power_supply.h>
+#include "atc260x_power.h"
+
+/* ATC2603C_PMU_CHARGER_CTL0 */
+#define     PMU_CHARGER_CTL0_CHGAUTO_DETECT_EN      (1 << 0)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT       (1)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_MASK        (0x3 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_100MV       (0 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_200MV       (1 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_300MV       (2 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGPWR_SET_400MV       (3 << PMU_CHARGER_CTL0_CHGPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_CURRENT_TEMP       (1 << 3)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT   (4)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET         (0x3 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_3810MV      (0 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_3960MV      (1 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_4250MV      (2 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR_SET_4400MV      (3 << PMU_CHARGER_CTL0_CHG_SYSPWR_SET_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_SYSPWR             (1 << 6)
+#define     PMU_CHARGER_CTL0_DTSEL_SHIFT            (7)
+#define     PMU_CHARGER_CTL0_DTSEL_MASK             (0x1 << PMU_CHARGER_CTL0_DTSEL_SHIFT)
+#define     PMU_CHARGER_CTL0_DTSEL_12MIN            (0 << PMU_CHARGER_CTL0_DTSEL_SHIFT)
+#define     PMU_CHARGER_CTL0_DTSEL_20S              (1 << PMU_CHARGER_CTL0_DTSEL_SHIFT)
+#define     PMU_CHARGER_CTL0_CHG_FORCE_OFF          (1 << 8)
+#define     PMU_CHARGER_CTL0_TRICKLEEN              (1 << 9)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT    (10)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_MASK     (0x3 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_30MIN    (0 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_40MIN    (1 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_50MIN    (2 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER2_60MIN    (3 << PMU_CHARGER_CTL0_CHARGE_TIMER2_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT    (12)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_MASK     (0x3 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_4H       (0 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_6H       (1 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_8H       (2 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHARGE_TIMER1_12H      (3 << PMU_CHARGER_CTL0_CHARGE_TIMER1_SHIFT)
+#define     PMU_CHARGER_CTL0_CHGTIME                (1 << 14)
+#define     PMU_CHARGER_CTL0_ENCH                   (1 << 15)
+
+/* ATC2603C_PMU_CHARGER_CTL1 */
+#define     PMU_CHARGER_CTL1_ICHG_REG_CC_SHIFT      (0)
+#define     PMU_CHARGER_CTL1_ICHG_REG_CC_MASK       (0xf << PMU_CHARGER_CTL1_ICHG_REG_CC_SHIFT)
+#define     PMU_CHARGER_CTL1_ICHG_REG_CC(i)         \
+                    ((((i) / 100) << PMU_CHARGER_CTL1_ICHG_REG_CC_SHIFT) & PMU_CHARGER_CTL1_ICHG_REG_CC_MASK)
+
+#define     PMU_CHARGER_CTL1_BAT_EXIST_EN           (1 << 5)
+#define     PMU_CHARGER_CTL1_CURRENT_SOFT_START     (1 << 6)
+#define     PMU_CHARGER_CTL1_STOPV_SHIFT            (7)
+#define     PMU_CHARGER_CTL1_STOPV_MASK             (0x1 << PMU_CHARGER_CTL1_STOPV_SHIFT)
+#define     PMU_CHARGER_CTL1_STOPV_4180MV           (0 << PMU_CHARGER_CTL1_STOPV_SHIFT)
+#define     PMU_CHARGER_CTL1_STOPV_4160MV           (1 << PMU_CHARGER_CTL1_STOPV_SHIFT)
+#define     PMU_CHARGER_CTL1_CHARGER_TIMER_END      (1 << 8)
+#define     PMU_CHARGER_CTL1_BAT_DT_OVER            (1 << 9)
+#define     PMU_CHARGER_CTL1_BAT_EXIST              (1 << 10)
+#define     PMU_CHARGER_CTL1_CUR_ZERO               (1 << 11)
+#define     PMU_CHARGER_CTL1_CHGPWROK               (1 << 12)
+#define     PMU_CHARGER_CTL1_PHASE_SHIFT            (13)
+#define     PMU_CHARGER_CTL1_PHASE_MASK             (0x3 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_PHASE_PRECHARGE        (1 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_PHASE_CONSTANT_CURRENT (2 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_PHASE_CONSTANT_VOLTAGE (3 << PMU_CHARGER_CTL1_PHASE_SHIFT)
+#define     PMU_CHARGER_CTL1_CHGEND                 (1 << 15)
+
+/*ATC2603C_PMU_CHARGER_CTL2 */
+#define     PMU_CHARGER_CTL2_CV_SET_SHIFT           (2)  /*chenxijian*/
+#define     PMU_CHARGER_CTL2_CV_SET_4200MV          (0 << PMU_CHARGER_CTL2_CV_SET_SHIFT)
+#define     PMU_CHARGER_CTL2_CV_SET_4300MV          (1 << PMU_CHARGER_CTL2_CV_SET_SHIFT)
+#define     PMU_CHARGER_CTL2_CV_SET_4350MV          (2 << PMU_CHARGER_CTL2_CV_SET_SHIFT)
+#define     PMU_CHARGER_CTL2_CV_SET_4400MV          (3 << PMU_CHARGER_CTL2_CV_SET_SHIFT)
+/*ATC2603C verB*/
+#define		PMU_CHARGER_CTL2_CV_SET_B_SHIFT			(1)
+#define		PMU_CHARGER_CTL2_CV_SET_4250MV          (1 << PMU_CHARGER_CTL2_CV_SET_B_SHIFT)
+
+#define     PMU_CHARGER_CTL2_ICHG_REG_T_SHIFT       (4) /*jlingzhang*/
+#define     PMU_CHARGER_CTL2_ICHG_REG_T_MASK        (0x3 << PMU_CHARGER_CTL2_ICHG_REG_T_SHIFT)
+#define     PMU_CHARGER_CTL2_ICHG_REG_T(i)          \
+                    ((((i) / 100) << PMU_CHARGER_CTL2_ICHG_REG_T_SHIFT) & PMU_CHARGER_CTL2_ICHG_REG_T_MASK)
+					
+//bit6 reserved ??
+#define		PMU_CHARGER_CTL2_TEMPTH3_SHIFT          (9)
+#define		PMU_CHARGER_CTL2_TEMPTH3_MASK		    (0x3 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH3_100			(0 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH3_120			(1 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH3_130			(2 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH3_140			(3 << PMU_CHARGER_CTL2_TEMPTH3_SHIFT)
+#define		PMU_CHARGER_CTL2_TEMPTH2_SHIFT          (11) 
+#define		PMU_CHARGER_CTL2_TEMPTH2_MASK           (0x3 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT) 
+#define 	PMU_CHARGER_CTL2_TEMPTH2_90			    (0 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH2_105			(1 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH2_120			(2 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH2_135			(3 << PMU_CHARGER_CTL2_TEMPTH2_SHIFT)
+#define		PMU_CHARGER_CTL2_TEMPTH1_SHIFT          (13) 
+#define		PMU_CHARGER_CTL2_TEMPTH1_MASK 		    (0x3 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH1_75			    (0 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH1_90			    (1 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH1_105			(2 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+#define 	PMU_CHARGER_CTL2_TEMPTH1_115			(3 << PMU_CHARGER_CTL2_TEMPTH1_SHIFT)
+
+/* ATC2603C_PMU_SYS_PEDNDING */
+/*bit6~bit3 has been defined in atc2603 spec*/
+#define     PMU_SYS_PENDING_BAT_OV                  (1 << 15)
+
+/*ATC2603A_PMU_OT_CTL */
+#define     PMU_OT_CTL_OT                           (1 << 15)
+#define 	PMU_OT_CTL_OT_INT_EN 			        (1 << 12)
+#define 	PMU_OT_CTL_OT_SHUTOFF_EN		        (1 << 11)
+#define	    PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT         (9) 
+#define	    PMU_OT_CTL_OT_SHUTOFF_SET_MASK          (0x3 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT) 
+#define	    PMU_OT_CTL_OT_SHUTOFF_SET_100           (0 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define	    PMU_OT_CTL_OT_SHUTOFF_SET_120           (1 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define	    PMU_OT_CTL_OT_SHUTOFF_SET_130           (2 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define	    PMU_OT_CTL_OT_SHUTOFF_SET_140           (3 << PMU_OT_CTL_OT_SHUTOFF_SET_SHIFT)
+#define 	PMU_OT_CTL_OT_EN					    (1 << 8)
+
+ /* ATC2603A_APDS_CTL */
+#define	VBUS_CONTROL_EN 	(1 << 15)
+#define     VBUS_CONTROL_SEL_SHIFT                (14)
+#define     VBUS_CONTROL_SEL_MASK                 (1 << VBUS_CONTROL_SEL_SHIFT)
+#define     VBUS_CONTROL_SEL_VOL                  (0x0 << VBUS_CONTROL_SEL_SHIFT)
+#define     VBUS_CONTROL_SEL_CUR                  (0x1 << VBUS_CONTROL_SEL_SHIFT)
+#define	    VBUS_CUR_LIMITED_SHIFT		          (12)
+#define	    VBUS_CUR_LIMITED_MASK		          (0x3 << VBUS_CUR_LIMITED_SHIFT)
+#define	    VBUS_CUR_LIMITED_1000MA	              (0x0 << VBUS_CUR_LIMITED_SHIFT)    //here attention:5302-100MA-->5307-1000MA
+#define	    VBUS_CUR_LIMITED_300MA	              (0x1 << VBUS_CUR_LIMITED_SHIFT)
+#define	    VBUS_CUR_LIMITED_500MA	              (0x2 << VBUS_CUR_LIMITED_SHIFT)
+#define	    VBUS_CUR_LIMITED_800MA	              (0x3 << VBUS_CUR_LIMITED_SHIFT)
+#define     APDS_CTL_VBUS_VOL_LIMITED_SHIFT       (10)
+#define     APDS_CTL_VBUS_VOL_LIMITED_MASK        (0x3 << APDS_CTL_VBUS_VOL_LIMITED_SHIFT)
+#define     APDS_CTL_VBUSOTG                      (1 << 9)
+#define     APDS_CTL_VBUS_PD                      (1 << 2)
+#define     APDS_CTL_WALL_PD			          (1 << 1)
+#define     ATC260X_SUPPLY_WALL                   (0)
+#define     ATC260X_SUPPLY_VBUS                   (1)
+#define     ATC260X_SUPPLY_BAT                    (2)
+
+#define    CV_SET_VAL                             4300
+ int atc2603c_read_adc(struct atc260x_dev *atc260x, const char *channel_name)
+{
+	int ret;
+	int translate_data;
+	unsigned int channel_num;
+	
+	channel_num = atc260x_auxadc_find_chan(atc260x,channel_name);
+	if(channel_num < 0 )
+	{
+		printk("not support this channel or the channel name is error!\n");
+		return channel_num;
+	}
+	ret = atc260x_auxadc_get_translated(atc260x,channel_num, &translate_data);
+	if(ret < 0)
+		printk("cannot get the correct translation data!\n");
+	
+	return translate_data;
+}
+ 
+ int atc2603c_read_adc_supply(struct atc260x_dev *atc260x,
+                     const char *channel_name,
+                     union power_supply_propval *val)
+{
+	int translate_data;
+
+	translate_data = atc2603c_read_adc(atc260x,channel_name);
+	val->intval = translate_data;
+	
+	return translate_data;
+}
+
+ int atc2603c_check_bat_online(struct atc260x_dev *atc260x)
+ {
+ 	int ret;
+	int exist;
+	
+	/* dectect bit 0 > 1 to start dectecting */
+	ret = atc260x_set_bits(atc260x, ATC2603C_PMU_CHARGER_CTL1, 
+	PMU_CHARGER_CTL1_BAT_EXIST_EN, PMU_CHARGER_CTL1_BAT_EXIST_EN);
+	if (ret < 0)
+		return ret;	
+		
+	/* wait bat detect over */
+	msleep(ATC260X_BAT_DETECT_DELAY_MS);/*300ms*/
+
+	ret = atc260x_reg_read(atc260x, ATC2603C_PMU_CHARGER_CTL1);
+	if (ret < 0)
+		return ret;
+		
+	exist = ret & PMU_CHARGER_CTL1_BAT_EXIST;
+	
+	/* cleare battery detect bit, otherwise cannot changer */
+	ret = atc260x_set_bits(atc260x, ATC2603C_PMU_CHARGER_CTL1, 
+	PMU_CHARGER_CTL1_BAT_EXIST_EN, 0);
+	if (ret < 0)
+		return ret;
+	if (exist) {
+		int bat_v = atc2603c_read_adc(atc260x, "BATV");
+		printk("%s bat_v:%d\n",__func__, bat_v);
+		return 1;
+	}
+	return 0;
+ }
+
+ int atc2603c_check_wall_online(struct atc260x_charger *charger,  
+	union power_supply_propval *val)
+{
+	int ret;
+	
+		ret = atc2603c_read_adc_supply(charger->atc260x, "WALLV", val);
+		if (ret < 0)
+			return ret;
+
+
+        if ((ret > charger->wall_v_thresh) &&
+        	(ret > charger->bat_mv))
+		val->intval = 1;
+	else
+		val->intval = 0;
+
+	return ret;
+}
+
+int atc2603c_check_vbus_online(struct atc260x_charger *charger, 
+	union power_supply_propval *val)
+{
+    int ret = 0;
+	
+	if (charger->g_vbus_is_otg) {
+		/* no usb-charger while otg is enabled */
+		val->intval = 0;
+	} else {
+		ret = atc2603c_read_adc_supply(charger->atc260x, "VBUSV", val);
+		if (ret < 0)
+			return ret;
+
+		if (ret > ATC260X_VBUS_VOLTAGE_THRESHOLD)
+			val->intval = 1;
+		else
+			val->intval = 0;
+	}
+
+	return ret;
+}
+
+void atc2603c_charger_update_phrase_type(struct atc260x_charger *charger)
+{
+	int ret = atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1);
+	charger->chg_type = ret & PMU_CHARGER_CTL1_PHASE_MASK;
+}
+
+/*enable the charger*/
+void atc2603c_set_charge(struct atc260x_dev *atc260x, int on)
+{
+	int val;
+
+	if (on) {
+		val = PMU_CHARGER_CTL0_ENCH;
+	} else {    
+		val = 0;
+	}
+	
+	atc260x_set_bits(atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+		PMU_CHARGER_CTL0_ENCH, val);
+}
+
+/**
+ * @brief atc260x_check_bat_status
+ * check if battery is healthy
+ * @return BATTERY_HEALTHY_STATE
+ */
+static int atc2603c_bat_check_health_pre(struct atc260x_dev *atc260x)
+{
+	int batv = atc2603c_read_adc(atc260x, "BATV");
+	if (batv <= 200)
+	{
+		int pmu_charger_ctl1 =  atc260x_reg_read(atc260x, ATC2603C_PMU_CHARGER_CTL1);
+		pmu_charger_ctl1 &= ~0xf;
+		atc260x_reg_write(atc260x, ATC2603C_PMU_CHARGER_CTL1, pmu_charger_ctl1); 
+		atc2603c_set_charge(atc260x, 1);
+		msleep(64);
+		batv = atc2603c_read_adc(atc260x, "BATV");
+		atc2603c_set_charge(atc260x, 0);
+		if (batv <= 200)
+		{
+			return POWER_SUPPLY_HEALTH_UNKNOWN; /*modified by cxj at 2014-11-06*/
+		} 
+	}
+
+	return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+/*detect if the bat is OVERVOLTAGE(or too heat)*/
+int atc2603c_bat_check_health(struct atc260x_charger *charger, int *health)
+{
+	struct atc260x_dev *atc260x = charger->atc260x;
+	int ret;
+
+	ret = atc260x_reg_read(atc260x, ATC2603C_PMU_SYS_PENDING);
+	if (ret < 0)
+		return ret;
+
+	if (ret & PMU_SYS_PENDING_BAT_OV) 
+	{
+		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+		return 0;
+	}
+
+	*health = POWER_SUPPLY_HEALTH_GOOD;
+
+	return 0;
+}
+
+void atc2603c_set_trick_current(struct atc260x_charger *charger)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+                    (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2) & 0xffcf) |
+                    PMU_CHARGER_CTL2_ICHG_REG_T(200));
+	 atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+		atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) |
+		PMU_CHARGER_CTL0_TRICKLEEN |
+		PMU_CHARGER_CTL0_CHARGE_TIMER2_30MIN); 
+}
+
+int atc2603c_get_constant_current(struct atc260x_charger *charger)
+{
+	 return atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1) & 0xf;
+}
+
+void atc2603c_set_constant_current(struct atc260x_charger *charger, int set_current)
+{
+	/* 
+	* constant charge current: 1000ma 
+	* charge stop voltage (OCV): 4.16v
+	*/
+	unsigned int current_now = atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1);
+	
+	if ((current_now & 0xf) != set_current)
+	{	
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1, 
+							(current_now &(~0xf))|set_current);
+
+	}
+
+	 atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+		atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) |
+		PMU_CHARGER_CTL0_CHARGE_TIMER1_12H);
+}
+
+static void set_vbus_control_mode(struct atc260x_charger *charger, int vbus_control_mode)
+{
+	if (vbus_control_mode == VOLTAGE_LIMITED)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+						(atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL) &
+						(~VBUS_CONTROL_SEL_MASK )) |
+						VBUS_CONTROL_EN |
+						VBUS_CONTROL_SEL_VOL);
+		power_dbg("vubs control mode:***VOLTAGE_LIMITED***, ATC2603C_PMU_APDS_CTL:%x\n",
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL));
+	}
+	else if (vbus_control_mode == CURRENT_LIMITED)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+						(atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL) &
+						(~VBUS_CUR_LIMITED_MASK ) &
+						(~VBUS_CONTROL_SEL_MASK)) |
+						VBUS_CONTROL_EN |
+						VBUS_CUR_LIMITED_500MA |
+						VBUS_CONTROL_SEL_CUR);
+		power_dbg("vubs control mode:***CURRENT_LIMITED***, ATC2603C_PMU_APDS_CTL:%x\n",
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL));
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+						(atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL) &
+						(~VBUS_CONTROL_EN)));
+	}
+
+}
+
+void atc2603c_set_vbus_path(struct atc260x_charger * charger, bool enable)
+{
+	if (enable)
+	{
+		// shut off the path from vbus to vbat,
+		// when support usb adaptor only.
+		atc260x_set_bits(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+		APDS_CTL_VBUSOTG, 0);
+	}
+	else 
+	{
+		atc260x_set_bits(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+		APDS_CTL_VBUSOTG, APDS_CTL_VBUSOTG);
+	}
+}
+
+int atc2603c_get_vbus_path(struct atc260x_charger * charger)
+{
+	return !(atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL) & APDS_CTL_VBUSOTG);
+}
+
+/*5k resistor of wall enable*/
+void atc2603c_set_apds_wall_pd(struct atc260x_charger *charger, bool enable)
+{
+	if (enable)
+	{
+		atc260x_set_bits(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+			APDS_CTL_WALL_PD, APDS_CTL_WALL_PD);
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL) & ~APDS_CTL_WALL_PD);
+	}
+}
+
+void atc2603c_set_apds_vbus_pd(struct atc260x_charger *charger, bool enable)
+{
+	if (enable)
+	{
+		atc260x_set_bits(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+			APDS_CTL_VBUS_PD, APDS_CTL_VBUS_PD);
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_APDS_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_APDS_CTL)  & ~APDS_CTL_VBUS_PD);
+	}
+}
+
+void atc2603c_set_syspwr(struct atc260x_charger *charger)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+		(atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) & 
+		(~(PMU_CHARGER_CTL0_CHG_SYSPWR_SET | PMU_CHARGER_CTL0_CHGPWR_SET_MASK))) |
+		PMU_CHARGER_CTL0_CHGPWR_SET_100MV | 
+		PMU_CHARGER_CTL0_CHG_SYSPWR_SET_4250MV);
+
+	if (charger->cfg_items.ext_dcdc_exist == 1) 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) & ~PMU_CHARGER_CTL0_CHG_SYSPWR);  
+	}
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) | PMU_CHARGER_CTL0_CHG_SYSPWR);  
+	}
+}
+
+void atc2603c_set_current_soft_start(struct atc260x_charger *charger, bool enable)
+{
+	if (enable)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1, 
+				atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1) |
+				PMU_CHARGER_CTL1_CURRENT_SOFT_START );
+	}
+	else
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1, 
+				atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1) &
+				(~PMU_CHARGER_CTL1_CURRENT_SOFT_START) );
+	}
+} 
+
+/*by chenxijian*/
+void atc2603c_cv_set(struct atc260x_charger *charger, int cv_val_mv)
+{
+	if (cv_val_mv == 4250)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+		        (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2) & 0xfff1) |
+				PMU_CHARGER_CTL2_CV_SET_4250MV);
+	}
+	else if(cv_val_mv == 4300)
+	{
+	    atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+		        (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2) & 0xfff3) |
+				PMU_CHARGER_CTL2_CV_SET_4300MV);
+	}
+	else if(cv_val_mv == 4350)
+	{
+	    atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+		        (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2) & 0xfff3) |
+				PMU_CHARGER_CTL2_CV_SET_4350MV);
+	}
+	else if(cv_val_mv == 4400)
+	{
+	    atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+		        (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2) & 0xfff3) |
+				PMU_CHARGER_CTL2_CV_SET_4400MV);
+	}
+	else
+	{
+	    atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+		        (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2) & 0xfff3) |
+				PMU_CHARGER_CTL2_CV_SET_4200MV);
+	}
+}
+
+void atc2603c_set_ot_shutoff(struct atc260x_charger *charger, int ot_shutoff_enable)
+{
+	if (!ot_shutoff_enable) //disable over temperature shutoff bit
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_OT_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_OT_CTL) & ~PMU_OT_CTL_OT_SHUTOFF_EN);
+	} 
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_OT_CTL, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_OT_CTL) | PMU_OT_CTL_OT_SHUTOFF_EN);
+	}
+}
+
+void atc2603c_set_change_current_temp(struct atc260x_charger *charger, int  change_current_temp)
+{
+	 if (change_current_temp) {
+	      atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+	          atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) | PMU_CHARGER_CTL0_CHG_CURRENT_TEMP);
+	 } else {
+	     atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+	          atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) & (~PMU_CHARGER_CTL0_CHG_CURRENT_TEMP));
+	 }
+}
+
+void atc2603c_set_charger_stop_voltage(struct atc260x_charger *charger, int stopv)
+{
+	atc260x_reg_write(charger->atc260x,ATC2603C_PMU_CHARGER_CTL1, 
+			(atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL1) &
+			(~ PMU_CHARGER_CTL1_STOPV_MASK)) | 
+			stopv);
+}
+
+void atc2603c_set_temp_threshold(struct atc260x_charger *charger)
+{
+	atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2, 
+                    (atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL2)  & 
+                    (~PMU_CHARGER_CTL2_TEMPTH1_MASK) &
+                    (~PMU_CHARGER_CTL2_TEMPTH2_MASK) &
+                    (~PMU_CHARGER_CTL2_TEMPTH3_MASK)) |
+                    PMU_CHARGER_CTL2_TEMPTH1_90 | PMU_CHARGER_CTL2_TEMPTH2_105 | PMU_CHARGER_CTL2_TEMPTH3_120);
+}
+
+void atc2603c_set_auto_detect_charging(struct atc260x_charger *charger, bool enable)
+{
+	if (!enable)
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0,
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) & 
+			~PMU_CHARGER_CTL0_CHGAUTO_DETECT_EN &
+			~PMU_CHARGER_CTL0_CHG_FORCE_OFF);
+	}
+	else 
+	{
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0,
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) | 
+			PMU_CHARGER_CTL0_CHGAUTO_DETECT_EN |
+			PMU_CHARGER_CTL0_CHG_FORCE_OFF);
+
+		atc260x_reg_write(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0, 
+			atc260x_reg_read(charger->atc260x, ATC2603C_PMU_CHARGER_CTL0) |
+			PMU_CHARGER_CTL0_DTSEL_12MIN |
+			PMU_CHARGER_CTL0_CHGTIME);
+
+	}
+}
+
+void atc2603c_charger_phy_init(struct atc260x_charger *charger)
+{
+	/*
+	 * charger init
+	 */    
+	atc2603c_set_trick_current(charger);// trickle charge current: 100mA
+	atc2603c_set_constant_current(charger, PMU_CHARGER_CTL1_ICHG_REG_CC(charger->thresholds.constant_charge_current));//set constant current value
+	atc2603c_set_current_soft_start(charger, true);
+	atc2603c_set_charger_stop_voltage(charger, PMU_CHARGER_CTL1_STOPV_4160MV);
+	atc2603c_cv_set(charger, CV_SET_VAL);//4.3v,fast charge
+	atc2603c_set_ot_shutoff(charger, charger->cfg_items.ot_shutoff_enable);//shutoff charger when ov according to cfg xml
+	atc2603c_set_temp_threshold(charger);// set temp protect threshold
+	atc2603c_set_change_current_temp(charger, charger->cfg_items.change_current_temp);
+	atc2603c_set_auto_detect_charging(charger, false);
+	
+	/*
+	 * syspwr init
+	 */
+	atc2603c_set_syspwr(charger);
+
+	/*
+	 * wall init
+	 */
+	atc2603c_set_apds_wall_pd(charger, true);
+}
+
+void atc2603c_dump_regs(struct atc260x_charger *charger)
+{
+	int i;
+	for (i = 0x00; i <= 0x64; i++ )
+	{
+		//printk("0x%x:0x%x\n", i, atc260x_reg_read(charger->atc260x, gl5307_PMU_BASE + i));
+	}
+}
+void atc2603c_get_info(struct atc260x_charger *charger)
+{
+	charger->name = "power-2603C";
+	//charger->read_adc = atc2603c_read_adc_supply;
+	charger->read_adc = atc2603c_read_adc;
+	charger->check_bat_online = atc2603c_check_bat_online;
+	charger->check_wall_online = atc2603c_check_wall_online;
+	charger->check_vbus_online = atc2603c_check_vbus_online;
+	charger->update_phrase_type = atc2603c_charger_update_phrase_type;
+	charger->update_charger_mode = NULL;
+	charger->set_charge = atc2603c_set_charge;
+	charger->set_vbus_ctl_mode = set_vbus_control_mode;
+	charger->check_health_pre = atc2603c_bat_check_health_pre;
+	charger->check_health = atc2603c_bat_check_health;
+	charger->set_constant_current = atc2603c_set_constant_current;
+	charger->get_constant_current = atc2603c_get_constant_current;
+	charger->cv_set = atc2603c_cv_set;
+	charger->set_apds_wall_pd = atc2603c_set_apds_wall_pd;
+	charger->set_vbus_path = atc2603c_set_vbus_path;
+	charger->get_vbus_path = atc2603c_get_vbus_path;
+	charger->set_apds_vbus_pd = atc2603c_set_apds_vbus_pd;
+	charger->charger_phy_init = atc2603c_charger_phy_init;
+	charger->dump_regs = atc2603c_dump_regs;
+}
diff --git a/drivers/power/atc260x_power/atc260x_power.h b/drivers/power/atc260x_power/atc260x_power.h
new file mode 100755
index 0000000..2fdcacb
--- /dev/null
+++ b/drivers/power/atc260x_power/atc260x_power.h
@@ -0,0 +1,291 @@
+#ifndef __ATC260X_POWER_H__
+#define __ATC260X_POWER_H__
+
+#include <linux/gpio.h>
+#include <mach/gpio.h>
+#include <linux/completion.h>
+#include <linux/earlysuspend.h>
+#include <linux/timer.h>
+#include <linux/mutex.h>
+#include <linux/wakelock.h>
+
+/* If defined, enable printk. */
+//#define DEBUG
+#ifdef DEBUG
+#define power_dbg(format, arg...)       \
+	pr_info(format , ## arg)
+#else
+#define power_dbg(format, arg...)       \
+	do {} while (0)
+#endif
+
+#define power_err(...)        printk(KERN_ERR "ATC260X_POWER: " __VA_ARGS__);
+#define power_info(...)       printk(KERN_INFO "ATC260X_POWER: " __VA_ARGS__);
+
+#define     ATC260X_VBUS_VOLTAGE_THRESHOLD      3900        /* 3.9v */
+#define     ATC260X_BAT_DETECT_DELAY_MS         300         /* wait 300ms for bat detect over */
+
+struct gpio_pre_cfg;
+
+enum BL_STATUS {
+	BL_ON = 0,
+	BL_OFF
+};
+
+typedef enum  {
+	NO_PLUGED = 0,
+	WALL_PLUGED,
+	USB_PLUGED
+} charger_state_t;
+
+enum USB_PLUGED_TYPE {
+	USB_NO_PLUGED = 0,
+	USB_PLUGED_PC,
+	USB_PLUGED_ADP
+};
+
+/*
+* There are :
+* 1.two interface:wall plugged only or wall and usb plugged or usb plugged only;
+* 2.single usb:usb plugged and wall plugged.
+* 3.single dcin:wall plugged only.
+*/
+enum SUPPORT_ADAPTOR_TYPE {
+	SUPPORT_DCIN = 0x1,
+	SUPPORT_USB = 0x2,
+	SUPPORT_DCIN_USB = 0x3
+};
+
+enum VBUS_CONTROL_MODE {
+	CURRENT_LIMITED,
+	VOLTAGE_LIMITED,
+	CANCEL_LIMITED
+};
+
+//keep sync with   arch/arm/mach-liger/pm.c ADAPTER_TYPE_NO_PLUGIN
+#define ADAPTER_TYPE_NO_PLUGIN             0
+#define ADAPTER_TYPE_WALL_PLUGIN           1 
+#define ADAPTER_TYPE_PC_USB_PLUGIN         2
+#define ADAPTER_TYPE_USB_ADAPTER_PLUGIN    3
+#define ADAPTER_TYPE_USB_WALL_PLUGIN       4
+
+struct atc260x_battery_thresholds {
+	int vbat_charge_start;
+	int vbat_charge_stop;
+	int vbat_low;
+	int vbat_crit;
+	int trickle_charge_current;
+	int constant_charge_current;    
+};
+
+struct atc260x_cfg_items {
+	unsigned int bl_on_current_usb_pc;
+	unsigned int bl_off_current_usb_pc;
+	unsigned int bl_on_current_usb_adp;
+	unsigned int bl_off_current_usb_adp;
+	unsigned int bl_on_current_wall_adp;
+	unsigned int bl_off_current_wall_adp;
+
+	unsigned int backlight_on_vol_diff;
+	unsigned int backlight_off_vol_diff;
+	/*external DCDC flag */
+	unsigned int ext_dcdc_exist;                		
+	unsigned int support_adaptor_type;
+	unsigned int enable_vbus_current_limited;
+	unsigned int usb_adapter_as_ac;
+	int boot_cap_threshold;
+	int bl_on_voltage;
+	
+	//struct gpio_pre_cfg charger_led_cfg;
+	//struct gpio_pre_cfg ext_chg_ctrl_cfg;
+
+	int change_current_temp;
+	int ot_shutoff_enable;
+
+	int gpio_led_inner_pin;
+	int gpio_led_active_low;
+	bool charger_led_exist;
+	int gpio_ext_chg_ctrl_pin;
+	int gpio_ext_chg_ctrl_active_low;
+	bool ext_charger_exist;
+};
+
+struct atc260x_charger {
+	const char *name;
+	struct power_supply *psy;
+	struct power_supply_desc charger_desc;
+	struct atc260x_dev *atc260x;
+	struct device_node *node;
+	struct workqueue_struct *charger_wq;
+	struct delayed_work work;
+	struct pwm_device *pwm;
+	struct timer_list adjust_current_timer;
+	struct atc260x_battery_thresholds thresholds;
+	unsigned int interval;
+	
+	struct mutex lock;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		struct early_suspend early_suspend;
+#endif
+
+	struct wake_lock charger_wake_lock;
+	struct wake_lock delay_lock;
+	 int wakelock_acquired;
+
+	struct atc260x_cfg_items cfg_items;
+
+	struct completion check_complete;
+
+	/* battery status */
+	bool bat_is_exist;	
+	int chg_type;
+	int bat_mv;
+	int bat_ma;
+	int bat_temp;
+	int chg_ma;
+	int health;
+	int battery_status;
+	int tricle_current;
+	int pre_bat_cap;
+	int cur_bat_cap; 
+	int bat_counter;   /* jiffies from last update the real battery voltage */
+	int info_counter;  /*jiffies from last updat the battery info */
+	
+	/*charger status*/
+	bool extern_power_online;  
+	bool charge_on;
+	bool charge_force_off;/*added by cxj @2014-1-5*/
+	bool cv_enabled;
+	int extra_chg_count;
+	int vbus_mv;
+	int wall_mv;
+	int wall_v_thresh;
+	int g_vbus_is_otg;
+	int vbus_control_mode;
+	
+	int full_vol;
+	int charger_cur_status;		// currently detected charger status, such as usb or wall whether online;
+	int charger_pre_status;		// saved charger status of previous detectation.
+
+	int cur_bl_status;
+	int pre_bl_status;
+	//charger_state_t chg_mode;
+	enum USB_PLUGED_TYPE usb_pluged_type;
+	bool usb_pluged_type_changed; 
+	/*0, off; 1, on*/
+	int led_status; 
+
+	/*for hard gauge */
+	int (*get_hw_gauge_cap)(void);
+	int (*get_hw_gauge_volt)(void);
+	int (*get_hw_gauge_cur)(void);
+	int (*get_hw_gauge_temp)(void);
+
+	/*for soft gauge*/
+	int (*get_gauge_cap)(void);
+	int (*get_gauge_volt)(void);
+	int (*get_gauge_cur)(void);
+	int (*get_gauge_temp)(void);
+	int (*adjust_tp_para)(int);
+
+	int (*read_adc)(struct atc260x_dev *atc260x, const char *channel_name);/*cxj*/
+	int (*check_bat_online)(struct atc260x_dev *atc260x);
+	int (*check_wall_online)(struct atc260x_charger *charger,  
+		union power_supply_propval *val);
+	int (*check_vbus_online)(struct atc260x_charger *charger, 
+		union power_supply_propval *val);
+	void (*update_phrase_type)(struct atc260x_charger *charger);
+	void (*set_charge)(struct atc260x_dev *atc260x, int on);
+	void (*set_vbus_ctl_mode)(struct atc260x_charger *charger, int mode);
+	void (*update_charger_mode)(struct atc260x_charger *charger);
+	int (*check_health_pre)(struct atc260x_dev *atc260x);
+	int (*check_health)(struct atc260x_charger *charger, int *health);
+	void (*set_constant_current)(struct atc260x_charger *charger, int set_current);
+	int (*get_constant_current)(struct atc260x_charger *charger);
+	void (*cv_set)(struct atc260x_charger *charger, int cv_val_mv);
+	void (*set_apds_wall_pd)(struct atc260x_charger *charger, bool enable);
+	void (*set_vbus_path)(struct atc260x_charger * charger, bool enable);
+	int (*get_vbus_path)(struct atc260x_charger * charger);
+	void (*set_apds_vbus_pd)(struct atc260x_charger *charger, bool enable);
+	void (*charger_phy_init)(struct atc260x_charger *charger);
+	void (*dump_regs)(struct atc260x_charger *charger);
+
+	struct dentry *debug_file;
+};
+
+struct atc260x_battery
+{
+	struct power_supply psy;	
+	struct atc260x_dev *atc260x;
+
+	/*battery status*/
+	bool bat_is_exist;  
+	int chg_type;
+	int bat_mv;
+	int bat_ma;
+	int bat_temp;
+	int chg_ma;
+	int health;
+
+	int full_vol;
+         int battery_status;
+	int boot_cap_threshold;
+	int pre_bat_cap;
+	int cur_bat_cap;
+
+	int bat_counter;   /* jiffies from last update the real battery voltage */
+	int info_counter;  /*jiffies from last updat the battery info */
+
+	/* function pointer of getting capacity */
+	int (*get_gauge_cap)(void);
+	/* function pointer of getting voltage */
+	int (*get_gauge_volt)(void);
+	/* function pointer of getting current */
+	int (*get_gauge_cur)(void);
+	/* function pointer of getting temperature */
+	int (*get_gauge_temp)(void);
+
+	int (*check_bat_online)(struct atc260x_dev *atc260x);
+	void (*update_phrase_type)(struct atc260x_charger *charger);
+	int (*check_health_pre)(struct atc260x_dev *atc260x);
+	int (*check_health)(struct atc260x_charger *charger, int *health);
+};
+
+struct atc260x_power {
+	struct atc260x_dev *atc260x;
+	struct power_supply *wall;
+	struct power_supply *usb;
+	struct atc260x_charger charger;
+};
+
+#ifdef STORE_POWER_INFO
+struct pmu_ic_info_test {
+	int bat_v;			/*battery voltage*/
+	int bat_i; 			/*battery current*/
+	int diff_v;			/*different voltage*/
+	int wall_v;    		/*wall voltage*/
+	int pwm_level;    	/*pwm level*/
+};
+#endif
+
+void atc2603a_get_info(struct atc260x_charger *charger);
+//void atc2603b_get_info(struct atc260x_charger *charger) ;
+void atc2603c_get_info(struct atc260x_charger *charger);
+
+#define LOG_HEADER_BATTERY "bat_update" 
+#define LOG_HEADER_HOTPLUG "hotplug"
+#define LOG_HEADER_AWAKE "awake_timeout"
+#define LOG_HEADER_SUSPEND "suspend"
+#define LOG_HEADER_RESUME "resume"
+#define LOG_HEADER_LOCK "lock"
+#define LOG_HEADER_UNLOCK "unlock"
+#define LOG_HEADER_CHARGER_ON "charge_on"
+#define LOG_HEADER_CHARGER_OFF "charge_off"
+
+extern int log_event_none(const char *header);
+extern int log_event_int(const char *header, int intval);
+extern int log_event_int_int(const char *header, int intval, int newval);
+extern int log_event_string(const char *header, const char *content);
+extern void log_event_dump(void);
+extern void log_event_init(void);
+#endif
diff --git a/drivers/power/atc260x_power/atc260x_power_event_log.c b/drivers/power/atc260x_power/atc260x_power_event_log.c
new file mode 100755
index 0000000..3fb3787
--- /dev/null
+++ b/drivers/power/atc260x_power/atc260x_power_event_log.c
@@ -0,0 +1,105 @@
+#include <linux/kfifo.h>
+#include <linux/time.h>
+
+#define LOG_LINE_MAX_SIZE 48
+#define LOG_BUF_SIZE (30*48*512)
+static char logbuf[LOG_BUF_SIZE];
+static int cur=0;
+
+static int log_event(const char *content, unsigned int len){
+    int nextfreepos;
+    if(!content){
+        return -1;
+    }
+    nextfreepos = cur+len;
+    if(nextfreepos<=LOG_BUF_SIZE){
+        memcpy(logbuf+cur, content, len);
+        cur=nextfreepos%LOG_BUF_SIZE;
+        return 0;
+    }
+    pr_warn("log_event buffer full");
+    memcpy(logbuf+cur, content, LOG_BUF_SIZE-cur);
+    nextfreepos= len-(LOG_BUF_SIZE-cur);
+	nextfreepos = nextfreepos % LOG_BUF_SIZE;
+    memcpy(logbuf, content+LOG_BUF_SIZE-cur, nextfreepos);
+    cur=nextfreepos;
+    return 0;
+} 
+
+
+int log_event_none(const char *header){
+    char linebuf[LOG_LINE_MAX_SIZE];
+    struct timeval time;
+    struct tm tm;
+    int reallen;
+    memset(linebuf, 0, sizeof(linebuf));
+    do_gettimeofday(&time);
+    time_to_tm(time.tv_sec, 0, &tm);
+    reallen=snprintf(linebuf, LOG_LINE_MAX_SIZE, "%d.%d-%d:%d:%d %s\n",
+        tm.tm_mon, tm.tm_mday, tm.tm_hour,
+        tm.tm_min, tm.tm_sec, header);
+    return log_event(linebuf, LOG_LINE_MAX_SIZE);
+} 
+
+int log_event_int(const char *header, int intval){
+    char linebuf[LOG_LINE_MAX_SIZE];
+    struct timeval time;
+    struct tm tm;
+    int reallen;
+    memset(linebuf, 0, sizeof(linebuf));
+    do_gettimeofday(&time);
+    time_to_tm(time.tv_sec, 0, &tm);
+    reallen=snprintf(linebuf, LOG_LINE_MAX_SIZE, "%d.%d-%d:%d:%d %s %d \n",
+        tm.tm_mon, tm.tm_mday, tm.tm_hour,
+        tm.tm_min, tm.tm_sec, header, intval);
+    return log_event(linebuf, LOG_LINE_MAX_SIZE);
+} 
+
+int log_event_int_int(const char *header, int intval, int newval){
+    char linebuf[LOG_LINE_MAX_SIZE];
+    struct timeval time;
+    struct tm tm;
+    int reallen;
+    memset(linebuf, 0, sizeof(linebuf));
+    do_gettimeofday(&time);
+    time_to_tm(time.tv_sec, 0, &tm);
+    reallen=snprintf(linebuf, LOG_LINE_MAX_SIZE, "%d.%d-%d:%d:%d %s %d %d\n",
+        tm.tm_mon, tm.tm_mday, tm.tm_hour,
+        tm.tm_min, tm.tm_sec, header, intval,newval);
+    return log_event(linebuf, LOG_LINE_MAX_SIZE);
+} 
+
+
+int log_event_string(const char *header, const char *content){
+    char linebuf[LOG_LINE_MAX_SIZE];
+    int reallen;
+    struct timeval time;
+    struct tm tm;
+    memset(linebuf, 0, sizeof(linebuf));
+    do_gettimeofday(&time);
+    time_to_tm(time.tv_sec, 0, &tm);
+    reallen=snprintf(linebuf, LOG_LINE_MAX_SIZE, "%d.%d-%d:%d:%d %s %s \n",
+        tm.tm_mon, tm.tm_mday, tm.tm_hour,
+        tm.tm_min, tm.tm_sec, header, content);
+    return log_event(linebuf, LOG_LINE_MAX_SIZE);
+}
+
+void log_event_dump(void){
+    char linebuf[LOG_LINE_MAX_SIZE];
+    int i;
+    for(i=0; i<LOG_BUF_SIZE/LOG_LINE_MAX_SIZE; i++){
+        memcpy(linebuf, logbuf+i*LOG_LINE_MAX_SIZE, LOG_LINE_MAX_SIZE);
+        pr_info("%s", linebuf);
+    }
+}
+
+void log_event_init(void){
+
+    cur=0; 
+    memset(logbuf, 0, LOG_BUF_SIZE);
+}
+
+void log_event_exit(void){
+}
+
+    
diff --git a/drivers/power/atc260x_power/atc260x_power_main.c b/drivers/power/atc260x_power/atc260x_power_main.c
new file mode 100755
index 0000000..823bc86
--- /dev/null
+++ b/drivers/power/atc260x_power/atc260x_power_main.c
@@ -0,0 +1,2938 @@
+/*
+ * atc260x_power_main.c  --  Power driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <mach/gpio.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+#include <linux/pwm.h>
+#include <linux/earlysuspend.h>
+#include <linux/fb.h>
+#include <linux/earlysuspend.h>
+#include <linux/fb.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/mfd/atc260x/atc260x.h>
+#include <mach/bootdev.h>
+#include "atc260x_power.h"
+
+#define GPIO_NAME_LED_INNER_CHARGER "inner_charger_led_ctl"
+#define GPIO_NAME_EXT_CTRL_CHARGER "ext_charger_ctl"
+
+#define PMU_CHARGER_PHASE_PRECHARGE	(0)
+#define PMU_CHARGER_PHASE_CONSTANT_CURRENT	(1)
+#define PMU_CHARGER_PHASE_CONSTANT_VOLTAGE	(2)     
+
+#define     ATC260X_SUPPLY_WALL          (0)
+#define     ATC260X_SUPPLY_VBUS          (1)
+#define     ATC260X_SUPPLY_BAT             (2)
+
+/*pmu version, including a,b,c and d*/
+#define	PMU_VERSION_A	0
+#define	PMU_VERSION_B	1
+#define	PMU_VERSION_C	2
+#define	PMU_VERSION_D	3
+/*different pmu version, different ratio */
+#define 	PMU_VERSION_ABC_CC_RATIO	4
+#define 	PMU_VERSION_D_CC_RATIO	3
+
+/* update real battery voltage per 1min */
+#define     ATC260X_INFO_UPDATE_INTERVAL         (1 * 60 * HZ)             			
+#define     PWM_MAX_LEVEL                               63
+#define     BL_POWER_PATH                             "/sys/class/backlight/backlight.3/bl_power"
+/* when battery is full, need charge more than EXTRA_CHARGE_TIME*2 seconds.*/
+#define 	EXTRA_CHARGE_TIME 900	
+
+/*extern buck adjust coefficient*/ 
+#define CONFIG_CURVE_ARG0	416000
+#define CONFIG_CURVE_ARG1	3727
+#define NO_CHARGER_PWM_LEVEL	32
+
+#define PSY_NAME_WALL  "atc260x-wall"
+#define PSY_NAME_USB   "atc260x-usb"
+
+enum BACKLIGHT_CHANGE
+{
+	PRE_BL_ON,
+	POST_BL_ON,
+	ABORT_BL_ON
+};
+
+static enum power_supply_property atc260x_wall_props[] = 
+{
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static enum power_supply_property atc260x_usb_props[] = 
+{
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static struct atc260x_power *global_power_dev = NULL;
+
+static  struct atc260x_charger * get_atc260x_charger(void)
+{
+    return global_power_dev ?  &global_power_dev->charger : NULL;
+}
+static struct atc260x_dev *get_atc260x_dev(void)
+{
+    return global_power_dev ? global_power_dev->atc260x: NULL;
+}
+
+int mini_chg = 0;
+module_param(mini_chg, int, S_IRUSR);
+
+static bool first_power_on = false;
+static int count_completion = 0;
+/*bat arv voltage before check bat online*/
+int batv_before_check;
+/*bat arv voltage after check bat online*/
+int batv_after_check;
+
+/*How long befor enable charger, because gas gauge need  0A correction*/
+static  int charger_delay_counter = -1;
+static int charger_once_on = false;
+atomic_t adapter_type = ATOMIC_INIT(0);
+/*record later resume state, if enter later resume cancel update work, otherwise schedule the work*/
+static bool enter_later_resume = false;
+
+static int atc260x_bat_check_status(struct atc260x_charger *charger, int *status);
+static int atc260x_charger_check_online(struct atc260x_charger *charger);
+extern int get_config(const char *key, char *buff, int len);
+extern int set_judge_adapter_type_handle(void* handle);
+static int get_pmu_ic_type(struct atc260x_charger *charger);
+extern int owl_backlight_is_on(void);
+
+
+int distinguish_adapter_type(void) 
+{
+	return atomic_read(&adapter_type);
+}
+/* modified by cxj @20141009 */
+static int get_pmu_ic_type(struct atc260x_charger *charger)
+{
+	int pmu_type;
+	pmu_type = atc260x_get_ic_type(charger->atc260x);
+	power_dbg("[%s]pmu type is %d\n", __func__,pmu_type);
+	
+	return pmu_type;
+}
+/* added by cxj @20141009 */
+static int atc260x_get_version(struct atc260x_charger *charger)
+{
+	int pmu_version;
+	pmu_version = atc260x_get_ic_ver(charger->atc260x);
+	power_dbg("[%s]pmu version is %d\n",__func__,pmu_version);
+	return pmu_version;
+} 
+/* added functions:*_get_cap,*_get_vol,*_get_cur,*_get_temp
+ * by tuhaoming @20150305 
+ * support hard gauge as well as the soft one
+*/
+static int atc260x_power_get_cap(struct atc260x_charger *charger , int *data)
+{
+	int ret = -1;
+	
+   	if(charger->get_hw_gauge_cap)
+    {
+		*data = charger->get_hw_gauge_cap();
+		ret = 0;
+	}
+	else if(charger->get_gauge_cap)
+	{
+		*data = charger->get_gauge_cap();
+		ret = 1;
+	}
+	else
+	{
+		ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+static int atc260x_power_get_vol(struct atc260x_charger *charger , int *data)
+{
+	int ret = -1;
+
+	if (charger->get_hw_gauge_volt)
+	{
+		*data = charger->get_hw_gauge_volt();
+		ret = 0;
+	}
+	else if (charger->get_gauge_volt)
+	{
+		*data = charger->get_gauge_volt();
+		ret = 1;
+	}
+	else
+	{
+		ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+static int atc260x_power_get_cur(struct atc260x_charger *charger , int *data)
+{
+	int ret = 0;
+	
+	if (charger->get_hw_gauge_cur)
+	{
+		*data = charger->get_hw_gauge_cur();
+		ret = 0;
+
+	}
+	else if (charger->get_gauge_cur) 
+	{
+		*data = charger->get_gauge_cur();
+		ret = 1;
+	}
+	else
+	{
+		ret = -EFAULT;
+	}
+
+	return ret;	
+}
+
+static int atc260x_power_get_temp(struct atc260x_charger *charger , int *data){
+	int ret = 0;
+	if (charger->get_hw_gauge_temp)
+	{
+		*data = charger->get_hw_gauge_temp();
+		ret = 0;
+	}
+	else if (charger->get_gauge_temp) 
+	{
+		*data = charger->get_gauge_temp();
+		ret = 1;
+	}
+	else
+	{
+		ret = -EFAULT;
+	}
+	
+	return ret;
+}
+/**
+ * get_pwm_level - calculate the pwm level according to the bat voltage.
+ * @curve_arg0:	refer to the following fomula.
+ * @curve_arg1: refer to the following fomula.
+ * @vbat:	battery voltage
+ * @bat_charger_diff:	the vol diff between bat and charger.
+ * @return:	the pwm level.
+ *
+ * pwm_level = curve_arg0 * (batv + bat_charger_diff - curve_arg1) / 1000
+ */
+static unsigned int get_pwm_level(unsigned int curve_arg0, unsigned int curve_arg1, unsigned int vbat, unsigned int bat_charger_diff)
+{
+	unsigned int level = 0;
+
+	if (vbat <= (curve_arg1 - bat_charger_diff)) 
+	{
+		return 0;
+	}
+
+	level = (curve_arg0 * (vbat + bat_charger_diff -curve_arg1)) / (1000 * 10000);
+	
+	if (level  > PWM_MAX_LEVEL) 
+	{
+		level = PWM_MAX_LEVEL;
+	}
+	
+	return level;
+}   
+
+#ifdef CONFIG_DEBUG_FS
+static int bat_debug_show(struct seq_file *s, void *data)
+{
+	struct atc260x_charger *charger = s->private;
+
+	seq_printf(s, "charger is %s\n", charger->charge_on ? "on" : "off");
+	if (charger->extern_power_online) 
+	{
+		seq_printf(s, "charge current = %dmA\n",
+	   		charger->chg_ma);
+	}
+
+	seq_printf(s, "wall voltage = %d (mV)\n",
+		charger->wall_mv);
+	seq_printf(s, "vbus voltage = %d (mV)\n",
+		charger->vbus_mv);
+	seq_printf(s, "bat voltage = %d (mV)\n",
+		charger->bat_mv);
+	seq_printf(s, "bat current = %d (mA)\n",
+		charger->bat_ma);
+
+	return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, bat_debug_show, inode->i_private);
+}
+
+static const struct file_operations bat_debug_fops = {
+	.open       = debug_open,
+	.read       = seq_read,
+	.llseek     = seq_lseek,
+	.release    = single_release,
+};
+
+static struct dentry *atc260x_bat_create_debugfs(struct atc260x_charger *charger)
+{
+	charger->debug_file = debugfs_create_file("charger", 0660, 0, charger,
+	                     &bat_debug_fops);
+	return charger->debug_file;
+}
+
+static void atc260x_bat_remove_debugfs(struct atc260x_charger *charger)
+{
+	debugfs_remove(charger->debug_file);
+}
+#else
+static inline struct dentry *atc260x_bat_create_debugfs(struct atc260x_charger *charger)
+{
+    return NULL;
+}
+static inline void atc260x_bat_remove_debugfs(struct atc260x_charger *charger)
+{
+}
+#endif
+
+static int atc260x_power_check_online(struct atc260x_charger *charger, int supply,
+                     union power_supply_propval *val)
+{
+	int ret = 0;
+
+	switch (supply) {
+	case ATC260X_SUPPLY_WALL:
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_USB)
+	{
+		val->intval = 0;
+	}
+	else
+	{
+		ret = charger->check_wall_online(charger, val);
+	}
+	
+	if (ret < 0)
+		return ret;
+	break;
+
+	case ATC260X_SUPPLY_VBUS:
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN)
+	{
+		val->intval = 0;
+		break;	
+	}
+	ret = charger->check_vbus_online(charger, val);
+	if (ret < 0)
+		return ret;
+	break;  
+	
+	case ATC260X_SUPPLY_BAT:
+	val->intval = charger->check_bat_online(charger->atc260x);
+	break;              
+
+	default:
+	break;
+
+}
+
+return 0;
+}
+
+static int atc260x_wall_get_prop(struct power_supply *psy,
+                enum power_supply_property psp,
+                union power_supply_propval *val)
+{
+	struct atc260x_power *atc260x_power = dev_get_drvdata(psy->dev.parent);
+	struct atc260x_charger *charger = &atc260x_power->charger;
+	
+	int ret = 0;
+
+	if (first_power_on)
+	{	
+		wait_for_completion(&charger->check_complete);
+		mutex_lock(&charger->lock);
+		if(++count_completion == 3)
+		{
+			first_power_on = false;
+			pr_info("[%s] first_power_on = false!\n",__func__);
+		}	
+		mutex_unlock(&charger->lock);		
+	}
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (atc260x_power->charger.charger_cur_status & WALL_PLUGED)
+			val->intval = 1;
+		else if ((atomic_read(&adapter_type) == ADAPTER_TYPE_USB_ADAPTER_PLUGIN) &&
+					charger->cfg_items.usb_adapter_as_ac)
+			val->intval = 1;
+		else
+			val->intval = 0;
+		break;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = atc260x_power->charger.wall_mv;
+		break;
+	
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+
+
+/**
+ * atc260x_disable_vbus_id_if_needed - disable vbus path if needed
+ *
+ * disable vbus path in order to prevent rob large current from vbus, 
+ * when dc5v and usb plugged in.
+ */
+static void atc260x_disable_vbus_id_if_needed(struct atc260x_charger *charger)
+{
+	/*if vbus is already cut(return 0) ,we do not need to set again*/
+	if(!charger->get_vbus_path)
+		return;
+	/* scene 1 single charge mode*/
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN ||
+		charger->cfg_items.support_adaptor_type == SUPPORT_USB)
+	{
+		charger->set_apds_vbus_pd(charger,true);
+		goto disable_path;
+	}
+	/* scene 2 otg */
+	if (charger->g_vbus_is_otg) {
+		charger->set_apds_vbus_pd(charger, false);
+		goto disable_path;
+	}
+	/* scene 3 USB+DCIN */
+	if ((charger->charger_cur_status & WALL_PLUGED) && 
+			(charger->charger_cur_status & USB_PLUGED)) {
+		charger->set_apds_vbus_pd(charger, true);
+		
+		/*added by cxj @2014-12-27*/
+		if (charger->cfg_items.ext_dcdc_exist)
+			goto disable_path;
+		else
+		{
+			if (charger->bat_is_exist)
+			{
+				if(charger->bat_mv > 3500 && charger->cur_bat_cap > 7)
+					goto disable_path;
+			}
+		}
+	}
+	/* if all above is false,we make path connected default */
+	charger->set_vbus_path(charger, true);
+	charger->set_apds_vbus_pd(charger, true);
+	return;
+	
+disable_path:
+	charger->set_vbus_path(charger, false);
+	return;
+}
+
+/**
+ * atc260x_enable_vbusotg - enable vbus otg function or not.
+ * @on : if true, disable the path between vbus and syspwr, otherwise enable the path. 
+ *  
+ * note: when enable the otg function,must shutdown the diode between vbusotg and syspwr, to avoiding loop circuit.
+ */
+int atc260x_enable_vbusotg(int on)
+{
+	struct atc260x_charger *charger = get_atc260x_charger();
+
+	WARN_ON(charger->atc260x == NULL ||charger== NULL);
+	
+	if (charger == NULL)
+		return -ENODEV;  	
+	charger->g_vbus_is_otg = on;
+	/* added by cxj @2015-01-04
+	 * when usb disk plugging,we must set vbus off immediately
+	 * or the current circuit may cause damage 
+	 */
+	atc260x_disable_vbus_id_if_needed(charger);
+	power_dbg("\n[power] %s, %d, on: %d, g_vbus_is_otg: %d", __FUNCTION__, __LINE__, on, charger->g_vbus_is_otg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_enable_vbusotg);
+static int atc260x_usb_get_prop(struct power_supply *psy,
+                   enum power_supply_property psp,
+                   union power_supply_propval *val)
+{
+	struct atc260x_power *atc260x_power = dev_get_drvdata(psy->dev.parent);
+	struct atc260x_charger * charger = &atc260x_power->charger;
+	int ret = 0;
+
+	if (first_power_on)
+	{	
+
+		wait_for_completion(&charger->check_complete);
+		mutex_lock(&charger->lock);
+		if(++count_completion == 3)
+		{
+			first_power_on = false;
+			pr_info("[%s] first_power_on = false!\n",__func__);
+		}	
+		mutex_unlock(&charger->lock);		
+	}
+	
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (atc260x_power->charger.charger_cur_status & USB_PLUGED) 
+		{
+			if (atomic_read(&adapter_type) == ADAPTER_TYPE_USB_ADAPTER_PLUGIN && 
+				charger->cfg_items.usb_adapter_as_ac)
+				val->intval = 0;
+			else
+				val->intval = 1;
+		}
+		else 
+			val->intval = 0;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		if (atc260x_power->charger.g_vbus_is_otg)
+			val->intval = 0;
+		else
+			val->intval = atc260x_power->charger.vbus_mv;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+
+static void system_stayawake(struct atc260x_charger *charger)
+{
+
+	if(!charger->wakelock_acquired)
+	{
+		wake_lock(&charger->charger_wake_lock);
+		charger->wakelock_acquired=1;
+		log_event_none(LOG_HEADER_LOCK);
+	}
+}
+static void system_maysleep(struct atc260x_charger *charger)
+{
+
+	if(charger->wakelock_acquired)
+	{
+		wake_unlock(&charger->charger_wake_lock);
+		charger->wakelock_acquired=0;
+		log_event_none(LOG_HEADER_UNLOCK);
+	}
+}
+
+/**
+ * request_system_state - request to run continuously or sleep
+ *
+ * stay awake if bat is not full and adapter is online, also bat is online;
+ * may sleep including: 1.bat is full although adapter is online; 
+ *					  2.adapter is offline;
+ *					  3.battery is offline.
+ */
+static int request_system_state(struct atc260x_charger *charger)
+{
+
+	int battery_full = 0;
+
+	int charger_status_changed = charger->charger_cur_status!=charger->charger_pre_status;
+	if ((charger->cur_bat_cap == 100))
+	{
+		battery_full=1;
+	}
+
+	/*awake 5s when adapter plug in/out in order to light on screen*/
+	if (charger_status_changed)
+	{
+		log_event_none(LOG_HEADER_AWAKE);
+		wake_lock_timeout(&charger->delay_lock, 5 * HZ);
+	}
+	
+	/*no battery , we may wake unlock to sleep for CE certification*/
+	if (!charger->bat_is_exist)
+	{
+		system_maysleep(charger);
+		return 0;
+	}
+	
+	/*not any adapter is connected, we should wake unlock to sleep*/
+	if(!charger->extern_power_online)
+	{
+		system_maysleep(charger);
+		return 0;
+	}
+
+	/*battery is full, we should wake unlock to sleep for CE certification*/
+	if(battery_full)
+	{
+		system_maysleep(charger);
+	}
+	else
+	{
+		system_stayawake(charger);
+	}  
+	
+	return 0;
+}
+
+
+void atc260x_charger_turn_on(void)
+{
+	struct atc260x_charger *charger = get_atc260x_charger();
+	int wall_connected=charger->charger_cur_status &WALL_PLUGED;
+	
+	/*added by cxj @2015-1-5*/
+	if (charger->charge_force_off)
+		return;
+		
+	if (charger->charge_on)
+		return;
+	
+	if (charger->cfg_items.ext_charger_exist) 
+	{
+		/*use external charger, such as tp5000*/
+		if (wall_connected == WALL_PLUGED) 
+		{
+			/*WALL pluged, no matter USB pluged or not*/
+			if (charger->cur_bl_status) {
+				/* backlight is on, turn off external charger to prevent temprature too high*/
+				__gpio_set_value(charger->cfg_items.gpio_ext_chg_ctrl_pin, 1);
+			} 
+			else 
+			{
+			      /*  backlight is off, First, turn off internal charger.	
+				* Then, turn on external charger
+				*/
+				charger->set_charge(charger->atc260x, 0);
+				msleep(5000);
+				__gpio_set_value(charger->cfg_items.gpio_ext_chg_ctrl_pin, 0);
+				msleep(5000);
+			}               
+
+			/* then turn on internal charger*/
+			charger->set_charge(charger->atc260x, 1);
+		} 
+		else if (charger->charger_cur_status == USB_PLUGED) 
+		{
+			/*Only USB pluged.turn off external charger, no matter charged or not previously. */
+			__gpio_set_value(charger->cfg_items.gpio_ext_chg_ctrl_pin, 1);
+
+			/*then turn on internal charger.*/
+			charger->set_charge(charger->atc260x, 1);
+		}
+	} 
+	else 
+	{
+		/*use internal charger, such as 2603A.*/
+		charger->set_charge(charger->atc260x, 1);
+		charger->charge_on = true;
+		/*set pwm level to  NO_CHARGER_PWM_LEVEL to improve power efficiency*/
+		
+		if (charger->cfg_items.ext_dcdc_exist)
+		{
+			if (IS_ERR_OR_NULL(charger->pwm))
+			{
+				pr_err("charger->pwm is NULL or ERR!!\n");
+				return;
+			}
+			pwm_config(charger->pwm,
+				((charger->pwm->period) >> 6) * NO_CHARGER_PWM_LEVEL,
+				charger->pwm->period);
+		}
+	}
+	
+	log_event_none(LOG_HEADER_CHARGER_ON);
+}
+void atc260x_charger_turn_on_force(void)
+{
+	struct atc260x_charger *charger = get_atc260x_charger();
+	charger->charge_force_off = false;
+	if(charger_once_on)
+		atc260x_charger_turn_on();
+}
+EXPORT_SYMBOL_GPL(atc260x_charger_turn_on_force);
+
+void atc260x_charger_turn_off(void)
+{	
+	struct atc260x_charger *charger = get_atc260x_charger();
+	
+	if (!charger->charge_on)
+		return;
+	
+	if (charger->cfg_items.ext_charger_exist) 
+	{
+		/*turn off external charger.*/
+		__gpio_set_value(charger->cfg_items.gpio_ext_chg_ctrl_pin, 1);
+	} 
+
+	// turn off internal charger, no matter charged or not.
+	charger->set_charge(charger->atc260x, 0);
+	charger->charge_on = false;
+	if (!charger->cfg_items.ext_charger_exist) 
+	{
+		/*charging is stopped, set pwm level = NO_CHARGER_PWM_LEVEL*/
+		if (charger->cfg_items.ext_dcdc_exist)  
+		{
+			if (IS_ERR_OR_NULL(charger->pwm))
+			{
+				pr_err("charger->pwm is NULL or ERR!!\n");
+				return;
+			}
+			pwm_config(charger->pwm,
+				((charger->pwm->period) >> 6) * NO_CHARGER_PWM_LEVEL,
+				charger->pwm->period);
+		}
+	}
+
+	log_event_none(LOG_HEADER_CHARGER_OFF);
+}
+void atc260x_charger_turn_off_force(void)
+{
+	struct atc260x_charger *charger = get_atc260x_charger();
+	charger->charge_force_off = true;
+	atc260x_charger_turn_off();
+	return;
+}
+EXPORT_SYMBOL_GPL(atc260x_charger_turn_off_force);
+
+
+static void led_state_charge(struct atc260x_charger *charger){
+
+    if(charger->led_status)
+        return ;
+    if (charger->cfg_items.charger_led_exist) {
+      __gpio_set_value(charger->cfg_items.gpio_led_inner_pin, 1);
+    }
+    charger->led_status=1;
+}
+static void led_state_normal(struct atc260x_charger *charger){
+	if(!charger->led_status)
+		return ;   
+	
+	if (charger->cfg_items.charger_led_exist) 
+	{
+		__gpio_set_value(charger->cfg_items.gpio_led_inner_pin, 0);
+	}    
+
+	charger->led_status=0;
+}
+
+#define PWM_LEVEL_CHANGE_STEP    5
+#define VOLT_DIFF_THRESHOLD      50
+static int pwm_batv_count = 0;		
+static int pwm_batv_sum = 0;	
+static int pre_pwm_level = PWM_MAX_LEVEL;		
+static void atc260x_power_update_pwm(struct atc260x_charger *charger)
+{
+	int data = 0;
+	int ret = -1;
+
+	int voltage_diff;
+	int bat_avr_vol;
+	int pwm_level;
+	int wall_vol;
+	int bat_vol;
+
+	if (!charger->bat_is_exist)
+	{
+		if (charger->cfg_items.ext_dcdc_exist== 1) 
+		{
+			pwm_config(charger->pwm,
+						((charger->pwm->period) >> 6) * PWM_MAX_LEVEL,
+						charger->pwm->period);
+			power_dbg("[%s]duty_ns = %d,priod_ns= %d\n",
+						__func__,((charger->pwm->period) >> 6) * PWM_MAX_LEVEL,charger->pwm->period);
+		}
+		return ;
+	}
+	
+	if (charger->cfg_items.ext_dcdc_exist== 1) 
+	{
+		if(charger->charge_on)
+		{	
+			ret = atc260x_power_get_vol(charger, &data);
+			if(ret >= 0){
+				pwm_batv_sum += data;
+			}else{
+				pwm_batv_sum += charger->read_adc(charger->atc260x, "BATV");
+			}
+			pwm_batv_count++;
+		} 
+		else 
+		{
+			pwm_batv_sum = 0;
+			pwm_batv_count = 0;
+		}
+
+		if (!charger->cfg_items.ext_charger_exist) {
+			/* if external charger not exist, then adjust pwm level every 20s during charging state.*/
+			if (pwm_batv_count == 10)
+			{	
+				power_dbg("******time to config pwm*******\n");
+				// adjust different voltage depend on backlight.
+				if (charger->cur_bl_status == BL_OFF)
+				{
+					voltage_diff = charger->cfg_items.backlight_off_vol_diff;
+				} 
+				else
+				{
+					voltage_diff = charger->cfg_items.backlight_on_vol_diff;
+				}
+
+				bat_avr_vol = pwm_batv_sum /pwm_batv_count ;
+
+				wall_vol = charger->read_adc(charger->atc260x, "WALLV");
+				
+				pwm_level = get_pwm_level(CONFIG_CURVE_ARG0, CONFIG_CURVE_ARG1, bat_avr_vol, voltage_diff);  
+				
+				power_dbg("wall_vol = %d,bat_vol =%d,pwm_level = %d\n",wall_vol,bat_avr_vol,pwm_level);
+				power_dbg("wall_vol - bat_avr_vol = %d\n",wall_vol - bat_avr_vol);
+				if (pre_pwm_level == PWM_MAX_LEVEL)
+				{
+					pre_pwm_level = pwm_level;//init
+				}
+				if(wall_vol - bat_avr_vol < voltage_diff)
+				{
+					if (pwm_level <= pre_pwm_level)
+					{
+						if (pre_pwm_level < PWM_MAX_LEVEL)
+						{
+							pre_pwm_level += PWM_LEVEL_CHANGE_STEP;
+							if (pre_pwm_level >= PWM_MAX_LEVEL)
+								pre_pwm_level = PWM_MAX_LEVEL-1;//avoid to be same as PWM_MAX_LEVEL
+						}
+							
+						pwm_level = pre_pwm_level;
+						power_dbg("wall+:the reconfig pwm level is:%d\n",pwm_level);
+					}
+				}
+				else
+				{
+					pwm_level = pre_pwm_level;
+				}
+				
+				if (wall_vol - bat_avr_vol > voltage_diff + VOLT_DIFF_THRESHOLD)
+				{
+					if (pwm_level >= pre_pwm_level)
+					{
+						if (pre_pwm_level < PWM_MAX_LEVEL)
+						{
+							pre_pwm_level -= PWM_LEVEL_CHANGE_STEP;
+							if (pre_pwm_level < 0)
+								pre_pwm_level = 0;
+							pwm_level = pre_pwm_level;
+						}
+						else
+							pre_pwm_level = pwm_level;
+						power_dbg("wall-:the reconfig pwm level is:%d\n",pwm_level);
+					}
+				}
+
+				pwm_config(charger->pwm,
+					((charger->pwm->period) >> 6) * pwm_level,
+					charger->pwm->period);
+				
+				wall_vol = charger->read_adc(charger->atc260x, "WALLV");
+				bat_vol = charger->read_adc(charger->atc260x, "BATV");
+				
+				power_dbg("after pwm config,pwm_level=%d,wall_vol=%d,bat_vol=%d,wall_vol-bat_vol=%d\n",
+					pwm_level,wall_vol,bat_vol,wall_vol-bat_vol);
+				
+				pre_pwm_level = pwm_level;
+				
+				pwm_batv_sum = 0;
+				pwm_batv_count = 0;			    
+			}
+		}
+
+	}    
+}
+
+/**
+  * update_fast_charge_state - enalbe/disable fast charge
+  * 
+  * disable fast charge if bat is full. 
+  */
+static void update_fast_charge_state(struct atc260x_charger *charger)
+{
+	if (!charger->bat_is_exist)
+	{
+		return ;
+	}
+	
+	if (charger->cur_bat_cap == 100) 
+	{
+		if (charger->cv_enabled) 
+		{
+			charger->cv_set(charger, 4200);
+			charger->cv_enabled = false;
+		}
+	} 
+	else 
+	{
+		if (charger->cv_enabled == false) 
+		{
+			charger->cv_set(charger, 4300);
+			charger->cv_enabled = true;         
+		}
+	}
+}
+
+
+static void atc260x_update_voltage(struct atc260x_charger *charger){
+    
+    struct atc260x_power* power = container_of(charger, struct atc260x_power, charger);
+    power->charger.wall_mv = charger->read_adc(power->atc260x, 
+            "WALLV");
+    power->charger.vbus_mv =  charger->read_adc(power->atc260x, 
+            "VBUSV");
+
+}
+
+/*
+ * update adatper type
+ * pm driver will query adapter_type
+ *
+ */
+static void update_adapter_type(struct atc260x_charger *charger){
+	int chg_mode=charger->charger_cur_status;
+	if (chg_mode == NO_PLUGED)
+		atomic_set(&adapter_type, ADAPTER_TYPE_NO_PLUGIN);
+
+	else if (chg_mode == WALL_PLUGED)
+		atomic_set(&adapter_type, ADAPTER_TYPE_WALL_PLUGIN);
+
+	else if(chg_mode == USB_PLUGED)
+	{
+		if((chg_mode & USB_PLUGED) 
+			&& (charger->usb_pluged_type ==USB_PLUGED_PC)) 
+		{
+			atomic_set(&adapter_type, ADAPTER_TYPE_PC_USB_PLUGIN);
+		}
+
+		if ((chg_mode & USB_PLUGED) 
+			&& (charger->usb_pluged_type == USB_PLUGED_ADP))
+		{
+			atomic_set(&adapter_type, ADAPTER_TYPE_USB_ADAPTER_PLUGIN);
+		}
+	}
+	else if(chg_mode == (WALL_PLUGED | USB_PLUGED))
+		atomic_set(&adapter_type, ADAPTER_TYPE_USB_WALL_PLUGIN); 
+	else
+		pr_err("do not support this adapter type!\n");
+}
+
+static int get_batv_avr(struct atc260x_charger *charger)
+{
+	int data = 0;
+	int ret = -1;
+
+	int sum = 0;
+	int count = 0;
+	for (; count < 3; count++)
+	{
+		ret = atc260x_power_get_vol(charger, &data);
+		if(ret >= 0){
+			sum += data;
+		}else{
+			sum += charger->read_adc(charger->atc260x, "BATV");
+		}
+		msleep(5);
+	}
+
+	return sum /count;
+}
+
+static void atc260x_check_bat_online(struct atc260x_charger *charger)
+{
+	int ret;
+	
+	if (first_power_on)
+	{
+		/*check if battery is online*/
+		batv_before_check = get_batv_avr(charger);
+		ret = charger->check_bat_online(charger->atc260x);
+		if (ret <= 0) 
+		{
+			pr_warn("\n[power] No battery detected\n");
+			charger->bat_is_exist = false;
+		} 
+		else 
+		{
+			pr_info("\n[power] Battery detected\n");
+			charger->bat_is_exist = true;
+		}
+		batv_after_check = get_batv_avr(charger);
+	}
+}
+
+/**
+  * udpate battery capacity
+  *
+  */
+static int update_battery_level(struct atc260x_charger *charger)
+{
+	int data = 0;
+	int ret = -1;
+
+	if (!charger->bat_is_exist)
+		return -ENXIO;
+
+	ret = atc260x_power_get_cap(charger , &data);
+	if(ret >= 0) {
+		charger->cur_bat_cap = data;
+	} else {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+
+static void update_battery_state(struct atc260x_charger *charger)
+{
+	int data = 0;
+	int ret = -1;
+
+	int status;
+	
+	atc260x_check_bat_online(charger);
+	
+	/*phrase update*/
+	charger->update_phrase_type(charger);
+
+
+	/* update current*/
+	ret = atc260x_power_get_cur(charger , &data);
+	if(ret >= 0){
+		charger->bat_ma = data;
+	}else{
+		atc260x_bat_check_status(charger, &status);
+		if ((status == POWER_SUPPLY_STATUS_CHARGING) ||
+			(status == POWER_SUPPLY_STATUS_FULL))
+			charger->bat_ma = charger->read_adc(charger->atc260x, "CHGI");
+	}
+	
+
+	/*update voltate*/
+	ret = atc260x_power_get_vol(charger, &data);
+	if(ret >= 0){
+		charger->bat_mv = data;
+	}else{
+		/*pr_info("we do not get bat_mv from gauge\n");*/
+		charger->bat_mv = charger->read_adc(charger->atc260x, "BATV");
+	}
+
+	ret = atc260x_power_get_temp(charger, &data);
+	if(ret >= 0){
+		charger->bat_temp = data;
+	}else{
+		//printk("temp has not rigest\n");
+	}
+	
+	update_battery_level(charger);
+}
+
+/**
+ *  update_charge_state - update charger current status
+ *
+ */
+static void update_charge_state( struct atc260x_charger *charger)
+{
+	charger->charger_cur_status = atc260x_charger_check_online(charger);
+	//ywwang todo
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN)
+	{
+		if (charger->charger_cur_status & WALL_PLUGED)  
+		{
+			charger->extern_power_online = true;
+		}
+		else
+		{
+			charger->extern_power_online = false;
+		}
+	}
+	else  /* SUPPORT_DCIN_USB or SUPPORT_USB */
+	{
+		charger->extern_power_online = charger->charger_cur_status!= NO_PLUGED;
+	}
+
+	/*vbus/wall voltage*/ 
+	atc260x_update_voltage(charger);
+
+	if (owl_backlight_is_on()) 
+	{
+		charger->cur_bl_status = BL_ON;
+	} 
+	else 
+	{
+		charger->cur_bl_status = BL_OFF;
+	}
+
+	update_adapter_type(charger);
+     
+}
+
+
+static void commit_charge_state(struct atc260x_charger *charger)
+{
+	charger->charger_pre_status = charger->charger_cur_status;
+	charger->pre_bl_status = charger->cur_bl_status;
+	charger->pre_bat_cap = charger->cur_bat_cap;
+	
+	charger->usb_pluged_type_changed = false;
+	enter_later_resume = false;	
+}
+
+static void handle_tp_calibration( struct atc260x_charger *charger)
+{
+	int charger_changed= charger->charger_cur_status!=charger->charger_pre_status;
+	int charger_on = charger->charger_cur_status !=NO_PLUGED;
+
+	if (charger_changed &&charger->adjust_tp_para) 
+	{ 
+		charger->adjust_tp_para(charger_on);
+	}
+}
+
+/**
+ * turn_off_charger_if_needed - turn off charger under some conditions
+ *
+ * turn off charger under following conditions:
+ * 1.battery is offline;
+ * 2.all adapters are offline;
+ * 3.bat capacity is full.
+ */
+static void turn_off_charger_if_needed(struct atc260x_charger *charger)
+{
+	/*battery is removed*/
+	if (!charger->bat_is_exist) 
+	{
+		/* disable charger if battery is removed */
+		if (charger->charge_on) 
+		{
+			led_state_normal(charger);
+			atc260x_charger_turn_off();
+			power_info("%s:battery doesn't exist,charger turn off\n", __func__);
+			charger_once_on = false;
+		}
+		
+		return;
+	}
+
+	/*charger hotplug*/
+	if(!charger->extern_power_online && charger->charge_on)
+	{
+		led_state_normal(charger);
+		atc260x_charger_turn_off();
+		power_info("%s:hotplug-out, charger turn off\n", __func__);
+		charger_once_on = false;
+	}
+
+	/*charger is on , but battery is full now,  we close led*/
+	// ywwang  todo
+	if (charger->extern_power_online && (charger->cur_bat_cap < 100)) 
+	{
+		led_state_charge(charger);
+	} 
+	else 
+	{
+		if(charger->charge_on)
+		{
+			led_state_normal(charger);
+			atc260x_charger_turn_off();
+			power_info("%s:power full,charger turn off\n", __func__);
+			charger_once_on = false;
+		}
+	}
+
+}
+
+/**
+ * full_vol_is_low - compare the bat vol that bat is full with current bat voltage
+ * @return : if the diff less than 100mv return false, otherwise return true.
+
+static bool bat_vol_is_too_high(struct atc260x_charger *charger)
+{
+#define BAT_VOL_DIFF (100)
+
+	if (charger->cur_bat_cap == 100)
+	{
+		if (charger->full_vol - charger->bat_mv < BAT_VOL_DIFF)
+			return true;
+		else
+			return false;
+	}
+	else
+		return false;
+}
+ */
+static void turn_on_charger_if_needed(struct atc260x_charger *charger)
+{
+	if (owl_get_boot_mode() == OWL_BOOT_MODE_UPGRADE) {
+		//printk("turn_on_charger_if_needed: :upgrade exit charger\n");
+		return;
+	}
+
+	if(!charger->bat_is_exist || 
+		charger->charge_on ||
+		!charger->extern_power_online || 
+		charger->cur_bat_cap == 100 ||
+		charger->battery_status != POWER_SUPPLY_HEALTH_GOOD)
+	{
+		charger_delay_counter=-1;
+		return ;
+	}
+
+	WARN_ON(charger_delay_counter > 7);
+	charger_delay_counter++;
+	if (!charger->charge_on && charger_delay_counter == 7)
+	{
+		atc260x_charger_turn_on();
+		if(charger->charge_on)
+		{
+			power_info("%s:charger turn on\n", __func__);
+			charger_once_on = true;
+			led_state_charge(charger);
+		}
+
+		charger_delay_counter = -1;
+	}
+}
+
+/**
+ * atc260x_set_future_current - set future constant current
+ *
+ * we need write the constant current  into reg.
+ */
+static unsigned int  atc260x_get_future_current(struct atc260x_charger *charger, int mode)
+{
+	unsigned int set_current = 0;
+	
+	switch (mode)
+	{
+		case WALL_PLUGED:
+		if (charger->cur_bl_status == BL_ON) 
+		{
+			set_current = charger->cfg_items.bl_on_current_wall_adp;	
+		} 
+		else 
+		{
+			set_current = charger->cfg_items.bl_off_current_wall_adp;
+		}
+		break;
+
+		case USB_PLUGED:
+		if (charger->usb_pluged_type == USB_PLUGED_PC) 
+		{
+			// usb_pc pluged
+			if (charger->cur_bl_status == BL_ON) 
+			{
+				set_current = charger->cfg_items.bl_on_current_usb_pc;	
+			} 
+			else 
+			{
+				set_current = charger->cfg_items.bl_off_current_usb_pc;
+			}
+		} 
+		else if (charger->usb_pluged_type == USB_PLUGED_ADP)
+		{
+			// usb_adaptor pluged
+			if (charger->cur_bl_status == BL_ON) 
+			{
+				set_current = charger->cfg_items.bl_on_current_usb_adp;	
+			} 
+			else 
+			{
+				set_current = charger->cfg_items.bl_off_current_usb_adp;
+			}
+		} 
+		else 
+		{
+			// not sure, default as usb_pc pluged
+			if (charger->cur_bl_status == BL_ON) 
+			{
+				set_current = charger->cfg_items.bl_on_current_usb_pc;	
+			} 
+			else 
+			{
+				set_current = charger->cfg_items.bl_off_current_usb_pc;
+			}
+		}	
+		
+		break;
+
+		default:
+			set_current = charger->cfg_items.bl_on_current_usb_pc;
+			break;
+			
+	}
+
+	return set_current;	
+}
+
+static int atc260x_real_current_to_binary(struct atc260x_charger *charger,int set_current)
+{
+	int ic_type;
+	int ic_version;
+	int binary_current;
+	
+	ic_type = atc260x_get_ic_type(charger->atc260x);
+	ic_version = atc260x_get_version(charger);
+	
+	if(ic_type == ATC260X_ICTYPE_2603C)
+	{
+		if(set_current >=1 && set_current <= 2)
+			binary_current = set_current;
+		else if(set_current >= 4 && set_current <= 6)
+			binary_current = set_current - 1;
+		else if(set_current >= 8 && set_current <= 10)
+			binary_current = set_current - 2;
+		else if(set_current >= 12 && set_current <= 14)
+			binary_current = set_current - 3;
+		else if(set_current >= 16 && set_current <= 18)
+			binary_current = set_current - 4;
+		else if(set_current == 20)
+			binary_current = set_current - 5;
+		else
+		{
+			binary_current = 2;  /*200mA*/
+			pr_warn("have not the responding value you are setting ,now set it to be 200mA!\n");
+		}
+	}
+	else if (ic_type == ATC260X_ICTYPE_2603A)
+	{
+		if( ic_version == ATC260X_ICVER_A || 
+			ic_version == ATC260X_ICVER_B || 
+			ic_version == ATC260X_ICVER_C)
+			binary_current = set_current;
+		else
+			binary_current = set_current * PMU_VERSION_D_CC_RATIO / 4;
+	}
+	else
+	{
+		pr_err("we do not support this ic_type!!\n");
+		return -EINVAL;
+	}
+	return binary_current;
+}
+
+static void atc260x_charger_adjust_current(struct atc260x_charger *charger)
+{
+	unsigned int set_current = 0;
+	unsigned int current_binary;
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN) 
+	{
+		if (charger->charger_cur_status & WALL_PLUGED) 
+		{
+			set_current = atc260x_get_future_current(charger, WALL_PLUGED);
+		}
+	}
+	else if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN_USB)
+	{
+		if (charger->charger_cur_status & WALL_PLUGED) 
+		{
+			set_current = atc260x_get_future_current(charger, WALL_PLUGED);
+		}
+		else if (charger->charger_cur_status & USB_PLUGED)
+		{
+			set_current = atc260x_get_future_current(charger, USB_PLUGED);
+		}
+	}
+	else if (charger->cfg_items.support_adaptor_type == SUPPORT_USB)
+	{
+		set_current = atc260x_get_future_current(charger, WALL_PLUGED);
+	}
+	else
+	{
+		pr_warn("%s: dont support adapter type? \n", __func__);
+	}
+	
+	/*added by cxj @2014-12-24*/
+	current_binary = atc260x_real_current_to_binary(charger, set_current);
+
+	charger->set_constant_current(charger, current_binary);
+	power_info("%s:set constant current:%dmA(binary:%x)\n", 
+				__func__,  set_current * 100,current_binary);
+}
+
+
+static void change_charge_current_if_needed(struct atc260x_charger *charger)
+{
+	bool  bl_changed = (charger->cur_bl_status == charger->pre_bl_status) ? false : true;
+	bool mode_changed = (charger->charger_cur_status == charger->charger_pre_status) ? false : true;
+		
+	if(charger->charger_cur_status == NO_PLUGED || !charger->bat_is_exist)
+	{
+		return ;
+	}
+	
+	if(bl_changed || mode_changed || charger->usb_pluged_type_changed || enter_later_resume)
+	{
+		atc260x_charger_adjust_current(charger);  
+		
+	}
+}
+
+
+static void  report_power_supply_change(struct atc260x_charger *charger)
+{
+	struct atc260x_power *power;
+	int battery_level_changed = charger->pre_bat_cap != charger->cur_bat_cap;
+	int supply_changed = charger->charger_pre_status != charger->charger_cur_status;
+	power = container_of(charger, struct atc260x_power, charger);
+
+	if(battery_level_changed || (charger->cur_bat_cap == 0 && charger->bat_is_exist))
+	{
+		log_event_int(LOG_HEADER_BATTERY, charger->cur_bat_cap);
+		power_supply_changed(charger->psy);
+	}
+	else if (charger->info_counter > ATC260X_INFO_UPDATE_INTERVAL)
+	{
+		log_event_int(LOG_HEADER_BATTERY, charger->cur_bat_cap);
+
+		power_supply_changed(charger->psy);   
+		charger->info_counter = 0;   
+	}
+	else
+	{
+		charger->info_counter += charger->interval;
+	}
+
+	if (first_power_on && (charger->info_counter == (8 * HZ))) 
+	{
+		power_supply_changed(charger->psy);
+	}
+	
+	// avoid system unstable when temperature is too high.
+	if (charger->bat_temp > 55) 
+	{
+		// ywwang: todo
+		power_supply_changed(charger->psy);
+	}
+
+	if(supply_changed)
+	{
+		log_event_int_int(LOG_HEADER_HOTPLUG, charger->charger_pre_status, charger->charger_cur_status);
+
+		power_supply_changed(power->wall); 
+		power_supply_changed(power->usb);
+	}
+    
+    if (first_power_on)
+	{
+
+		complete_all(&charger->check_complete);
+
+		//complete(&charger->check_usb_complete);
+
+		//complete(&charger->check_bat_complete);
+
+		power_dbg("%s:complete\n", __func__);
+		//first_power_on = false;
+	}
+}
+
+/**
+ *new featrue:chenbo,20140506, set vbus control mode depend on usb type. 
+ */
+static void update_vbus_control_mode(struct atc260x_charger *charger)
+{
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN_USB)
+	{
+		if (charger->charger_cur_status & USB_PLUGED)
+		{
+			if (charger->usb_pluged_type_changed)
+			{
+				if  (charger->usb_pluged_type == USB_PLUGED_PC)
+				{
+					if (charger->cfg_items.enable_vbus_current_limited)
+					{
+						charger->set_vbus_ctl_mode(charger,  CURRENT_LIMITED);
+						charger->vbus_control_mode = CURRENT_LIMITED;
+					}
+					else
+					{
+						charger->set_vbus_ctl_mode(charger,  CANCEL_LIMITED);
+						charger->vbus_control_mode = CANCEL_LIMITED;
+					}
+					
+				}
+				else if  (charger->usb_pluged_type == USB_PLUGED_ADP)
+				{
+					charger->set_vbus_ctl_mode(charger, VOLTAGE_LIMITED);
+					charger->vbus_control_mode = VOLTAGE_LIMITED;
+				}  
+			}
+		}
+		else if (charger->charger_pre_status != charger->charger_cur_status)
+		{
+			charger->set_vbus_ctl_mode(charger, CURRENT_LIMITED);
+			charger->vbus_control_mode = CURRENT_LIMITED;
+		}
+	}
+}
+
+
+
+static void atc260x_charging_monitor(struct work_struct *work)
+{
+	struct atc260x_charger *charger;
+	struct atc260x_power *power;
+	
+	charger = container_of(work, struct atc260x_charger, work.work);
+	power = container_of(charger, struct atc260x_power, charger);
+	/* step 1 : check if any charge cable is connected*/
+	update_battery_state(charger);
+	update_charge_state(charger);
+	
+	atc260x_disable_vbus_id_if_needed(charger);
+
+	update_fast_charge_state(charger);
+	atc260x_power_update_pwm(charger);
+
+	/*step 1.1  stay awak or allow sleep ?*/
+	request_system_state(charger);
+
+	/* step 2: handle tp calibration*/
+	handle_tp_calibration(charger);
+
+	/* step 3  turn off charger if there is no adapter connected with charger */
+	turn_off_charger_if_needed(charger);
+
+	/* step 4 turn on charger if any adapter  is dected */
+	turn_on_charger_if_needed(charger);
+	
+	/* step 6 update vbus control mode if needed.*/
+	update_vbus_control_mode(charger);
+
+	/* step 5 dynamic change charge current : not to be too hot....*/
+	change_charge_current_if_needed(charger);
+
+	if ((charger->update_charger_mode != NULL) && charger->charge_on)
+		charger->update_charger_mode(charger);
+
+	/* step 7 monitor battery health status.*/
+	if (charger->check_health)
+		charger->check_health(charger, &charger->health);
+
+	/* step 8 report battery info, including bat capacity, charger hotplug event, etc*/
+	report_power_supply_change(charger);
+
+	/* step 9 here we commit all status:  pre = cur*/
+	commit_charge_state(charger);
+
+	/* reschedule for the next time */
+	queue_delayed_work(charger->charger_wq, &charger->work, msecs_to_jiffies(charger->interval * 1000));
+}
+
+static int atc260x_bat_check_status(struct atc260x_charger *charger, int *status)
+{
+	if (!charger->bat_is_exist ||
+		(charger->battery_status != POWER_SUPPLY_HEALTH_GOOD)) 
+	{
+		*status = POWER_SUPPLY_STATUS_UNKNOWN;	
+	} 
+	else if (charger->extern_power_online) 
+	{
+		if (charger->cur_bat_cap == 100) 
+		{
+			*status = POWER_SUPPLY_STATUS_FULL;
+		} 
+		else 
+		{
+			*status = POWER_SUPPLY_STATUS_CHARGING;
+		}
+	}
+	else 
+	{
+		*status = POWER_SUPPLY_STATUS_DISCHARGING;
+	}
+
+	return 0;
+}
+
+int atc260x_bat_check_type(struct atc260x_charger *charger, int *type)
+{
+	switch (charger->chg_type) 
+	{
+	case PMU_CHARGER_PHASE_PRECHARGE:
+	    *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+	    break;
+		
+	case PMU_CHARGER_PHASE_CONSTANT_CURRENT:
+	case PMU_CHARGER_PHASE_CONSTANT_VOLTAGE:       
+	    *type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	    break;
+		
+	default:
+	    *type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+	    break;
+	}
+
+	return 0;
+}
+
+
+static int atc260x_bat_get_props(struct power_supply *psy,
+                   enum power_supply_property psp,
+                   union power_supply_propval *val)
+{
+	struct atc260x_power* power = dev_get_drvdata(psy->dev.parent);
+	struct atc260x_charger* charger = &power->charger;
+	int ret = 0;
+	
+	if (first_power_on)
+	{	
+
+		wait_for_completion(&charger->check_complete);
+		mutex_lock(&charger->lock);
+		if(++count_completion == 3)
+		{
+			first_power_on = false;
+			pr_info("[%s] first_power_on = false!\n",__func__);
+		}	
+		mutex_unlock(&charger->lock);
+	}
+	
+	switch (psp) 
+	{
+		case POWER_SUPPLY_PROP_STATUS:
+			ret = atc260x_bat_check_status(charger, &val->intval);
+			break;
+			
+		case POWER_SUPPLY_PROP_PRESENT:
+			val->intval = charger->bat_is_exist;
+			break;
+			
+		case POWER_SUPPLY_PROP_ONLINE:
+			val->intval = charger->bat_is_exist;
+			break;
+			
+		case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+			val->intval = charger->bat_mv * 1000;/* mV -> uV */  
+			break;
+			
+		case POWER_SUPPLY_PROP_CURRENT_NOW:
+			val->intval = charger->bat_ma;
+			val->intval *= 1000;                /* mA -> uA */
+			break;
+			
+		case POWER_SUPPLY_PROP_HEALTH:
+			val->intval = charger->health;
+			break;
+			
+		case POWER_SUPPLY_PROP_CHARGE_TYPE:
+			ret = atc260x_bat_check_type(charger, &val->intval);
+			break;
+			
+		case POWER_SUPPLY_PROP_CAPACITY:
+			if (charger->battery_status != POWER_SUPPLY_HEALTH_GOOD)
+				val->intval = -99;
+			else
+				val->intval = charger->cur_bat_cap;
+			break;
+			
+		case POWER_SUPPLY_PROP_TECHNOLOGY:
+			val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+			break;
+
+		case POWER_SUPPLY_PROP_TEMP:
+			val->intval = charger->bat_temp * 10;
+			break;
+
+		default:
+			ret = -EINVAL;
+			break;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property atc260x_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_TEMP,
+};
+
+/*static void atc260x_bat_convert_thresholds(struct atc260x_charger *charger,
+                          struct atc260x_battery_pdata *pdata)
+{
+	charger->thresholds.vbat_charge_start = pdata->vbat_charge_start;
+	charger->thresholds.vbat_charge_stop = pdata->vbat_charge_stop;
+	charger->thresholds.vbat_low = pdata->vbat_low;
+	charger->thresholds.vbat_crit = pdata->vbat_crit;
+
+	charger->thresholds.trickle_charge_current = 
+		(pdata->trickle_charge_current ? : 200);
+	charger->thresholds.constant_charge_current = 
+		(pdata->constant_charge_current ? : 1000);
+}*/
+/* added by cxj at 2014/10/14 */
+static void atc260x_vbus_init(struct atc260x_charger *charger)
+{
+	//charger->set_apds_vbus_pd(charger, true);
+	if(!charger->g_vbus_is_otg)
+	{
+		if (charger->cfg_items.support_adaptor_type == SUPPORT_USB) 
+		{
+			charger->set_vbus_path(charger, false);/* always cut */
+			charger->set_apds_vbus_pd(charger, false);	
+		}
+		else
+		{
+			charger->set_vbus_path(charger, true);
+			charger->set_apds_vbus_pd(charger, true);
+		}	
+	}
+}
+/* added by cxj at 2014/10/14 */
+static void atc260x_dcdc_init(struct atc260x_charger *charger)
+{
+	int pwm_level = PWM_MAX_LEVEL;
+	if (charger->cfg_items.ext_dcdc_exist == 1) 
+	{
+		if (charger->cfg_items.ext_charger_exist) 
+		{
+			// if external charger exist, then set max value to pwm3. 
+			pwm_config(charger->pwm,
+					((charger->pwm->period) >> 6) * pwm_level,
+					charger->pwm->period);
+		} 
+		else
+		{
+			pwm_level = get_pwm_level(CONFIG_CURVE_ARG0, CONFIG_CURVE_ARG1, 4500, 350);  
+			pwm_config(charger->pwm,
+					((charger->pwm->period) >> 6) * pwm_level,
+					charger->pwm->period);
+			pwm_enable(charger->pwm);
+			power_dbg("[%s]duty_ns = %d,priod_ns= %d\n",
+					__func__,((charger->pwm->period) >> 6) * pwm_level,charger->pwm->period);
+		}
+	}
+}
+
+static int atc260x_charger_init(struct atc260x_charger *charger)
+{
+	power_dbg("\n[power]  vbat_charge_start:%d mV, vbat_charge_stop:%d mV, "
+	"\nvbat_low:%d mV, vbat_crit:%d mV, trickle_charge_current:%d mA, constant_charge_current:%d mA\n", 
+	charger->thresholds.vbat_charge_start,
+	charger->thresholds.vbat_charge_stop,
+	charger->thresholds.vbat_low,
+	charger->thresholds.vbat_crit,
+	charger->thresholds.trickle_charge_current,
+	charger->thresholds.constant_charge_current);
+
+	charger->charger_phy_init(charger);
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_USB)
+	{
+		charger->set_vbus_ctl_mode(charger, VOLTAGE_LIMITED);
+	}
+	else
+	{
+		if (charger->cfg_items.enable_vbus_current_limited)
+			charger->set_vbus_ctl_mode(charger, CURRENT_LIMITED);
+		else
+			charger->set_vbus_ctl_mode(charger, CANCEL_LIMITED);
+	}
+
+	charger->bat_mv = charger->read_adc(charger->atc260x, "BATV") * 1000;
+	charger->bat_counter = 0;
+	charger->info_counter = 0;
+	power_dbg("\n[power] volatge(mV): wall %d  vbus %d  bat %d",
+	charger->wall_mv, charger->vbus_mv, charger->bat_mv);
+
+	charger->charge_on = false;
+	charger->cur_bl_status = BL_ON;
+	charger->pre_bl_status = -1;
+	//charger->charger_pre_status = 0;
+	charger->extra_chg_count = 0;
+	charger->usb_pluged_type = USB_NO_PLUGED;
+	charger->usb_pluged_type_changed = false;
+	charger->led_status=0;
+	charger->wakelock_acquired=0;
+	
+	charger->cv_enabled = true;/* added by cxj */
+	charger->cv_set(charger, 4300);
+	/*
+	* vbus init
+	*/
+	atc260x_vbus_init(charger);
+	/*
+	* external dcdc init
+	*/
+	atc260x_dcdc_init(charger);
+
+	return 0;
+}
+
+void act260x_set_get_cap_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+	    global_power_dev->charger.get_gauge_cap = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_cap_point);
+
+void act260x_set_get_volt_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.get_gauge_volt = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_volt_point);
+
+void act260x_set_get_cur_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.get_gauge_cur = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_cur_point);
+
+void act260x_set_get_temp_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.get_gauge_temp = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_temp_point);
+
+
+
+void act260x_set_get_hw_cap_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+	    global_power_dev->charger.get_hw_gauge_cap = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_hw_cap_point);
+
+void act260x_set_get_hw_volt_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.get_hw_gauge_volt = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_hw_volt_point);
+
+void act260x_set_get_hw_cur_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.get_hw_gauge_cur = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_hw_cur_point);
+
+void act260x_set_get_hw_temp_point(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.get_hw_gauge_temp = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(act260x_set_get_hw_temp_point);
+
+void atc260x_set_adjust_tp_para(void *ptr)
+{
+	if (global_power_dev != NULL) 
+	{ 
+		global_power_dev->charger.adjust_tp_para = ptr;
+	}
+}
+EXPORT_SYMBOL_GPL(atc260x_set_adjust_tp_para);
+/*modified by cxj at 2015/1/12*/
+int atc260x_get_charge_status(void)
+{
+	if (global_power_dev != NULL) 
+	{
+		return global_power_dev->charger.charge_on;
+	}
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(atc260x_get_charge_status);
+
+void atc260x_get_batv_from_charger(int *batv1, int *batv2)
+{
+	*batv1 = batv_before_check;
+	*batv2 = batv_after_check;
+}
+EXPORT_SYMBOL_GPL(atc260x_get_batv_from_charger);
+
+/* export to cap_gauge driver */
+int pmu_reg_read(unsigned short reg)
+{
+    struct atc260x_dev *atc260x = get_atc260x_dev();
+    int ret;
+    
+    ret = atc260x_reg_read(atc260x, reg);
+    if (ret < 0)
+    	return -1;
+    	
+    return ret;
+}
+EXPORT_SYMBOL_GPL(pmu_reg_read);
+
+int pmu_reg_write(unsigned short reg, unsigned short val)
+{
+    struct atc260x_dev *atc260x = get_atc260x_dev();
+    int ret;
+    
+    ret = atc260x_reg_write(atc260x, reg, val);
+    if (ret < 0)
+    	return -1;
+    	
+    return ret;
+}
+EXPORT_SYMBOL_GPL(pmu_reg_write);
+
+
+
+void atc260x_set_usb_plugin_type(int type)
+{
+	if (global_power_dev != NULL) 
+	{	
+		global_power_dev->charger.usb_pluged_type = type;
+		global_power_dev->charger.usb_pluged_type_changed = true;
+		pr_info("[%s]usb_pluged_type = %d\n",__func__,global_power_dev->charger.usb_pluged_type);
+	}
+}
+EXPORT_SYMBOL_GPL(atc260x_set_usb_plugin_type);
+
+int get_chg_current_now(void)
+{
+	int ic_type;
+	int ic_version;
+	int chg_current_now;
+
+	struct atc260x_charger *charger = get_atc260x_charger();
+	ic_type = get_pmu_ic_type(charger);
+	ic_version = atc260x_get_version(charger);
+	chg_current_now = charger->get_constant_current(charger);
+	if(ic_type == ATC260X_ICTYPE_2603C)
+	{
+		if (chg_current_now >= 1 && chg_current_now <= 2)
+			chg_current_now += 0;
+		else if (chg_current_now >= 3 && chg_current_now <= 5)
+			chg_current_now += 1;
+		else if (chg_current_now >= 6 && chg_current_now <= 8)
+			chg_current_now += 2;
+		else if (chg_current_now >= 9 && chg_current_now <= 11)
+			chg_current_now += 3;
+		else if (chg_current_now >= 12 && chg_current_now <= 14)
+			chg_current_now += 4;
+		else
+			chg_current_now += 5;
+	}
+	else if (ic_type == ATC260X_ICTYPE_2603A)
+	{
+		if (ic_version == ATC260X_ICVER_A || 
+			ic_version == ATC260X_ICVER_B ||
+			ic_version == ATC260X_ICVER_C)
+			chg_current_now *= 4 / PMU_VERSION_ABC_CC_RATIO;
+		else
+			chg_current_now *= 4 / PMU_VERSION_D_CC_RATIO +1;
+	}
+		
+	chg_current_now *= 100;
+	
+	return chg_current_now;
+}
+EXPORT_SYMBOL_GPL(get_chg_current_now);
+int atc260x_set_charger_current(int new, int *old)
+{
+	struct atc260x_charger *charger = get_atc260x_charger();
+	int current_binary;
+
+	*old = get_chg_current_now();
+	current_binary = atc260x_real_current_to_binary(charger,new / 100);	
+	charger->set_constant_current(charger, current_binary);
+	pr_info("[%s]the current set to be:%d(%d)\n",__func__,current_binary,new);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(atc260x_set_charger_current);
+/**
+ * enable_adjust_current_switch - enable adjust current switch for monitor work.
+ * 
+ */
+ /*do not need timer to queue_delayed_work,but in later resume directly
+  *by cxj
+static void enable_adjust_current_switch(unsigned long data)
+{
+	struct atc260x_charger *charger = (struct atc260x_charger *)data;
+	printk("%s, enable monitor current adjust\n", __func__);
+	
+	queue_delayed_work(charger->charger_wq, &charger->work, 0 * HZ);
+}
+ */
+
+static int atc260x_charger_check_wall_online(struct atc260x_charger *charger)
+{
+	struct atc260x_dev *atc260x = charger->atc260x;
+	if ((charger->read_adc(atc260x, "WALLV") > charger->wall_v_thresh))
+		return WALL_PLUGED;
+	else 
+		return NO_PLUGED;
+}
+
+static int atc260x_charger_check_usb_online(struct atc260x_charger *charger)
+{
+	struct atc260x_dev *atc260x = charger->atc260x;
+	
+	if (charger->cfg_items.support_adaptor_type == SUPPORT_DCIN)
+		return NO_PLUGED;
+	
+	if (!charger->g_vbus_is_otg) 
+	{
+		if (charger->read_adc(atc260x, "VBUSV") > 
+			ATC260X_VBUS_VOLTAGE_THRESHOLD) 
+		{
+			if (charger->usb_pluged_type == USB_NO_PLUGED)
+			{
+				charger->usb_pluged_type = USB_PLUGED_PC;
+			} 
+
+			return USB_PLUGED;
+		} 
+		else 
+		{
+			charger->usb_pluged_type = USB_NO_PLUGED;
+
+			return NO_PLUGED;
+		}
+	}
+
+	return NO_PLUGED;
+}
+
+
+static int atc260x_charger_check_online(struct atc260x_charger *charger)
+{
+	int  chg_mode = NO_PLUGED;
+
+	chg_mode |= atc260x_charger_check_wall_online(charger);
+	chg_mode |= atc260x_charger_check_usb_online(charger);
+	
+	return chg_mode;
+}
+
+static ssize_t show_boot_cap_threshold(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct atc260x_charger *charger = power_supply_get_drvdata(psy);
+	return snprintf(buf, PAGE_SIZE, "%d\n", charger->cfg_items.boot_cap_threshold);	
+}
+
+static ssize_t store_boot_cap_threshold(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return 0;	
+	
+}
+
+static ssize_t show_charger_online(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct atc260x_charger *charger = power_supply_get_drvdata(psy);
+	union power_supply_propval wall_online, usb_online;
+
+	atc260x_power_check_online(charger, ATC260X_SUPPLY_WALL, &wall_online);
+	atc260x_power_check_online(charger, ATC260X_SUPPLY_VBUS, &usb_online);
+	return snprintf(buf, PAGE_SIZE, "%d\n", usb_online.intval | wall_online.intval);	
+}
+
+static ssize_t store_charger_online(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return 0;	
+	
+}
+
+
+static ssize_t show_dump(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct atc260x_charger *charger = power_supply_get_drvdata(psy);
+
+	//log_event_dump();
+	printk("\n=========================debug information====================\n");
+	/*base config*/
+	printk("support_adaptor_type:%d\n",charger->cfg_items.support_adaptor_type);
+	printk("usb_adapter_as_ac:%d\n",charger->cfg_items.usb_adapter_as_ac);
+	printk("bl_on_current_usb_pc:%d\n",charger->cfg_items.bl_on_current_usb_pc);
+	printk("bl_off_current_usb_pc:%d\n",charger->cfg_items.bl_off_current_usb_pc);
+	printk("bl_on_current_usb_adp:%d\n",charger->cfg_items.bl_on_current_usb_adp);
+	printk("bl_off_current_usb_adp:%d\n",charger->cfg_items.bl_off_current_usb_adp);
+	printk("bl_on_current_wall_adp:%d\n",charger->cfg_items.bl_on_current_wall_adp);
+	printk("bl_off_current_wall_adp:%d\n",charger->cfg_items.bl_off_current_wall_adp);
+	printk("ext_dcdc_exist:%d\n",charger->cfg_items.ext_dcdc_exist);
+	printk("enable_vbus_current_limited:%d\n",charger->cfg_items.enable_vbus_current_limited);
+	/*PMU info*/
+	switch(get_pmu_ic_type(charger))
+	{
+		case ATC260X_ICTYPE_2603A:printk("pmu type:ATC2603A\n");break;
+		case ATC260X_ICTYPE_2603C:printk("pmu type:ATC2603C\n");break;
+		default:printk("pmu:unknown\n");
+	}
+	switch(atc260x_get_version(charger))
+	{
+		case ATC260X_ICVER_A:printk("pmu version : A\n");break;
+		case ATC260X_ICVER_B:printk("pmu version : B\n");break;
+		case ATC260X_ICVER_C:printk("pmu version : C\n");break;
+		case ATC260X_ICVER_D:printk("pmu version : D\n");break;
+		default:printk("pmu version : unknown\n");
+	}
+
+	/* interval */
+	printk("poll interval:%ds\n", charger->interval);
+	printk("wakelock_acquired:%d\n", charger->wakelock_acquired);
+	/*adapter*/
+	if (charger->charger_cur_status & WALL_PLUGED)
+	{	
+		printk("chg_mode:WALL\n");
+		if (charger->cfg_items.ext_dcdc_exist== 1) 
+		{
+			printk("PWM:duty_ns = %d,priod_ns= %d\n",
+						((charger->pwm->period) >> 6) * PWM_MAX_LEVEL,charger->pwm->period);
+			printk("wallv-batv:%d\n",charger->wall_mv-charger->bat_mv);
+		}
+	}
+
+	if (charger->charger_cur_status & USB_PLUGED)
+	{
+		printk("chg_mode:USB\n");
+	}
+
+	if (charger->charger_cur_status & USB_PLUGED)
+	{
+		if (charger->usb_pluged_type == USB_PLUGED_PC)
+			printk("usb pluged type:USB PC\n");
+		else if (charger->usb_pluged_type == USB_PLUGED_ADP)
+			printk("usb pluged type:USB ADAPTER\n");
+	}
+	/*vbus&wall*/
+	printk("vbus_mv:%dmv\n", charger->vbus_mv);
+	printk("wall_mv:%dmv\n", charger->wall_mv);
+
+	/*vbus path*/
+	printk("vbus path on/off:%d\n", charger->get_vbus_path(charger));
+	printk("g_vbus_is_otg:%d\n", charger->g_vbus_is_otg);
+	/*vbus control mode*/
+	switch(charger->vbus_control_mode)
+	{
+		case CURRENT_LIMITED:printk("vbus_control_mode:CURRENT_LIMITED\n");break;
+		case VOLTAGE_LIMITED:printk("vbus_control_mode:VOLTAGE_LIMITED\n");break;
+		default:printk("vbus_control_mode:CANCEL_LIMITED\n");break;
+	}
+	/*fast charge*/
+	printk("cv_enabled:%d\n", charger->cv_enabled);
+	/*charger onoff */
+	printk("charge_on:%d\n", charger->charge_on);
+	/* battery */
+	printk("bat_is_exist:%d\n", charger->bat_is_exist);
+	printk("bat_mv:%dmv\n", charger->bat_mv);
+	printk("bat_ma:%dmA\n", charger->bat_ma);
+	printk("cur_bat_cap:%d%%\n", charger->cur_bat_cap);
+	printk("bat_temp:%dC\n", charger->bat_temp);
+	if (charger->health == POWER_SUPPLY_HEALTH_GOOD)
+		printk("health:GOOD\n");
+	else
+		printk("health:BAD\n");
+	printk("tricle_current:%dmA\n", charger->tricle_current);
+	printk("constant charge current:%dmA\n",get_chg_current_now());
+	/*backlight*/
+	if (charger->cur_bl_status == BL_ON)
+		printk("cur_bl_status: ON\n");
+	else if (charger->cur_bl_status == BL_OFF)
+		printk("cur_bl_status: OFF\n");
+
+	printk("led_status:%d\n", charger->led_status);
+	printk("\n=============================================================\n");
+	return 0;	
+}
+
+static ssize_t store_dump(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return 0;	
+}
+
+static ssize_t show_bl_on_voltage(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct atc260x_charger *charger = power_supply_get_drvdata(psy);
+	return snprintf(buf, PAGE_SIZE, "%d\n", charger->cfg_items.bl_on_voltage * 1000);	
+}
+
+static ssize_t store_bl_on_voltage(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return 0;	
+	
+}
+
+static struct device_attribute atc260x_power_attrs[] = {
+	__ATTR(boot_cap_threshold, S_IRUGO | S_IWUSR, show_boot_cap_threshold, store_boot_cap_threshold),
+	__ATTR(dump, S_IRUGO | S_IWUSR, show_dump, store_dump),
+	 __ATTR(charger_online, S_IRUGO | S_IWUSR, show_charger_online, store_charger_online),
+	__ATTR(bl_on_voltage, S_IRUGO | S_IWUSR, show_bl_on_voltage, store_bl_on_voltage),
+};
+
+int atc260x_power_create_sysfs(struct atc260x_charger *charger)
+{
+	int r, t;
+	
+	power_info("create sysfs for atc260x power\n");
+	
+	for (t = 0; t < ARRAY_SIZE(atc260x_power_attrs); t++) {
+		r = device_create_file(&(charger->psy->dev), &atc260x_power_attrs[t]);
+	
+		if (r) {
+			dev_err(&(charger->psy->dev), "failed to create sysfs file for atc260x power\n");
+			return r;
+		}
+	}
+	
+	return 0;
+}
+void atc260x_power_remove_sysfs(struct atc260x_charger *charger)
+{
+	int  t;
+
+	power_info("[%s]remove sysfs for atc260x charger\n", __func__);
+	for (t = 0; t < ARRAY_SIZE(atc260x_power_attrs); t++) {
+		device_remove_file(&(charger->psy->dev), &atc260x_power_attrs[t]);
+	}
+}
+
+static int atc260x_get_cfg_item(struct atc260x_charger *charger)
+{
+	const __be32 *property;
+	enum of_gpio_flags flags;
+	int len;
+	int ret = 0;
+	int cur_ratio;
+	
+	/* adjust current depend on charger version.*/
+	int chg_version = 0;
+	int bl_on_current_usb_pc=0;
+	int bl_off_current_usb_pc=0;
+	int bl_on_current_usb_adp=0;
+	int bl_off_current_usb_adp=0;
+	int bl_on_current_wall_adp=0;
+	int bl_off_current_wall_adp=0;
+	
+	/*added by cxj @2014/8/17:start here*/
+	chg_version = atc260x_get_version(charger);
+	BUG_ON(chg_version<0);
+	if (get_pmu_ic_type(charger) == ATC260X_ICTYPE_2603A)
+	{
+	    if (chg_version == ATC260X_ICVER_A || 
+		    chg_version == ATC260X_ICVER_B || 
+		    chg_version == ATC260X_ICVER_C) 
+		    cur_ratio = PMU_VERSION_ABC_CC_RATIO;
+	    else
+		    cur_ratio = PMU_VERSION_D_CC_RATIO;
+	}
+	else
+	{
+	    cur_ratio = PMU_VERSION_ABC_CC_RATIO;
+	}
+	/*end here */
+	
+	// step 1: get max charger current from dts
+	property = of_get_property(charger->node, "bl_on_usb_pc_max_charge_current", &len);
+	if (property && len == sizeof(int))
+	{
+		bl_on_current_usb_pc = (be32_to_cpup(property)) / 100;
+		if(bl_on_current_usb_pc < 0 ||bl_on_current_usb_pc*cur_ratio > 4 * 15)
+		{
+			pr_warn("bl_on_current_usb_pc =%d \n", bl_on_current_usb_pc);
+			bl_on_current_usb_pc = 3;
+		}
+	}
+	else
+	{
+	     bl_on_current_usb_pc = 3;
+	}
+
+	property = of_get_property(charger->node, "bl_off_usb_pc_max_charge_current", &len);
+	if (property && len == sizeof(int))
+	{
+	    bl_off_current_usb_pc = (be32_to_cpup(property))  / 100;
+	    if(bl_off_current_usb_pc < 0 ||bl_off_current_usb_pc*cur_ratio > 4 * 15)
+	    {
+	         pr_warn("bl_off_current_usb_pc =%d \n", bl_off_current_usb_pc);
+	         bl_off_current_usb_pc = 5;
+	    }
+	}
+	else
+	{
+	     bl_on_current_usb_pc = 5;
+	}
+
+	property = of_get_property(charger->node, "bl_on_usb_adp_max_charge_current", &len);
+	if (property && len == sizeof(int))
+	{
+	    bl_on_current_usb_adp = (be32_to_cpup(property))  / 100;
+	    if(bl_on_current_usb_adp < 0 ||bl_on_current_usb_adp*cur_ratio > 4*15)
+	    {
+	         pr_warn("bl_on_current_usb_adp =%d \n", bl_on_current_usb_adp);
+	         bl_on_current_usb_adp = 3;
+	    }
+	}
+	else
+	{
+	     bl_on_current_usb_adp = 3;
+	}
+
+	property = of_get_property(charger->node, "bl_off_usb_adp_max_charge_current", &len);
+	if (property && len == sizeof(int))
+	{
+	    bl_off_current_usb_adp = (be32_to_cpup(property))  / 100;
+	    if(bl_off_current_usb_adp < 0 ||bl_off_current_usb_adp*cur_ratio > 4*15)
+	    {
+	         pr_warn("bl_off_current_usb_adp =%d \n", bl_off_current_usb_adp);
+	         bl_off_current_usb_adp = 8;
+	    }
+	}
+	else
+	{
+	     bl_off_current_usb_adp = 8;
+	}
+
+	property = of_get_property(charger->node, "bl_on_wall_adp_max_charge_current", &len);
+	if (property && len == sizeof(int))
+	{
+	bl_on_current_wall_adp = (be32_to_cpup(property))  / 100;
+	if(bl_on_current_wall_adp < 0 ||bl_on_current_wall_adp*cur_ratio > 4 * 15)
+	{
+		 pr_warn("bl_on_current_wall_adp =%d \n", bl_on_current_wall_adp);
+		 bl_on_current_wall_adp = 3;
+	}
+	}
+	else
+	{
+	 bl_on_current_wall_adp = 3;
+	}
+
+	property = of_get_property(charger->node, "bl_off_wall_adp_max_charge_current", &len);
+	if (property && len == sizeof(int))
+	{
+	bl_off_current_wall_adp = (be32_to_cpup(property))  / 100;
+	if(bl_off_current_wall_adp < 0 ||bl_off_current_wall_adp*cur_ratio > 4 * 15)
+	{
+		 pr_warn("bl_off_current_wall_adp =%d \n", bl_off_current_wall_adp);
+		 bl_off_current_wall_adp = 15;
+	}
+	}
+	else
+	{
+		bl_off_current_wall_adp = 15;
+	}
+
+	charger->cfg_items.bl_on_current_usb_pc = bl_on_current_usb_pc * cur_ratio / 4;
+	charger->cfg_items.bl_off_current_usb_pc = bl_off_current_usb_pc * cur_ratio / 4;
+	charger->cfg_items.bl_on_current_usb_adp = bl_on_current_usb_adp * cur_ratio / 4;
+	charger->cfg_items.bl_off_current_usb_adp = bl_off_current_usb_adp * cur_ratio / 4;
+	charger->cfg_items.bl_on_current_wall_adp = bl_on_current_wall_adp * cur_ratio / 4;
+	charger->cfg_items.bl_off_current_wall_adp = bl_off_current_wall_adp * cur_ratio / 4;
+	power_dbg("bl_on_current_usb_pc: %d \n", charger->cfg_items.bl_on_current_usb_pc);  
+	power_dbg("bl_off_current_usb_pc: %d \n", charger->cfg_items.bl_off_current_usb_pc);    
+	power_dbg("bl_on_current_usb_adp: %d \n",charger->cfg_items.bl_on_current_usb_adp); 
+	power_dbg("bl_off_current_usb_adp: %d \n", charger->cfg_items.bl_off_current_usb_adp);  
+	power_dbg("bl_on_current_wall_adp: %d \n", charger->cfg_items.bl_on_current_wall_adp);  
+	power_dbg("bl_off_current_wall_adp: %d \n", charger->cfg_items.bl_off_current_wall_adp);    
+
+
+	property = of_get_property(charger->node, "bl_on_voltage", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.bl_on_voltage = (be32_to_cpup(property));
+	}
+	else
+	{
+		charger->cfg_items.bl_on_voltage = 3300;
+	}
+
+	property = of_get_property(charger->node, "bl_on_voltage_diff", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.backlight_on_vol_diff = (be32_to_cpup(property));
+		power_dbg("bl_on_voltage_diff: %d \n", charger->cfg_items.backlight_on_vol_diff); 
+	}
+	else
+	{
+		charger->cfg_items.backlight_on_vol_diff = 350;
+	}
+
+	property = of_get_property(charger->node, "bl_off_voltage_diff", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.backlight_off_vol_diff = (be32_to_cpup(property));
+		power_dbg("bl_off_voltage_diff: %d \n", charger->cfg_items.backlight_off_vol_diff); 
+	}
+	else
+	{
+		charger->cfg_items.backlight_off_vol_diff = 400;
+	}
+	
+	// adjust voltag diff depend on charger version.
+	if (chg_version == ATC260X_ICVER_D) 
+	{
+		charger->cfg_items.backlight_off_vol_diff += 100;
+	}
+
+	property = of_get_property(charger->node, "ext_dcdc_exist", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.ext_dcdc_exist = (be32_to_cpup(property));
+		power_dbg("ext_dcdc_exist: %d \n", charger->cfg_items.ext_dcdc_exist); 
+	}
+	else
+	{
+		charger->cfg_items.ext_dcdc_exist = 1;
+	}
+	if (charger->cfg_items.ext_dcdc_exist == 1) {
+		charger->wall_v_thresh = 3400;
+	} 
+	else 
+	{
+		charger->wall_v_thresh = 4300;   /*modified by cxj @2015-01-16:4200-->4300*/
+	}
+    
+	property = of_get_property(charger->node, "enable_vbus_current_limited", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.enable_vbus_current_limited = (be32_to_cpup(property));
+		power_dbg("enable_vbus_current_limited: %d \n", charger->cfg_items.enable_vbus_current_limited); 
+	}
+	else
+	{
+		charger->cfg_items.enable_vbus_current_limited = 1;
+	}
+	
+	property = of_get_property(charger->node, "usb_adapter_as_ac", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.usb_adapter_as_ac = (be32_to_cpup(property));
+		power_dbg("usb_adapter_as_ac: %d \n", charger->cfg_items.usb_adapter_as_ac); 
+	}
+	else
+	{
+		charger->cfg_items.usb_adapter_as_ac = 1;
+	}
+
+
+
+	// get support adaptor type.
+	property = of_get_property(charger->node, "support_adaptor_type", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.support_adaptor_type = (be32_to_cpup(property));
+	}
+	else
+	{
+		charger->cfg_items.support_adaptor_type = 3;
+	}
+	power_dbg("[power]support adaptor type: %d\n", charger->cfg_items.support_adaptor_type);
+    
+	//get low capacity boot threshold.don't boot to android ,if lower than  this value and usb plugged  
+	property = of_get_property(charger->node, "boot_cap_threshold", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.boot_cap_threshold = (be32_to_cpup(property));
+	}
+	else
+	{
+		charger->cfg_items.boot_cap_threshold = 7;
+	}
+	power_dbg("[power]boot_cap_threshold: %d\n", charger->cfg_items.boot_cap_threshold);
+
+	property = of_get_property(charger->node, "change_current_temp", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.change_current_temp = (be32_to_cpup(property));
+	}
+	else
+	{
+		charger->cfg_items.change_current_temp = 1;
+	}
+	power_dbg("[power]change_current_temp: %d\n", charger->cfg_items.change_current_temp);
+
+	property = of_get_property(charger->node, "ot_shutoff_enable", &len);
+	if (property && len == sizeof(int))
+	{
+		charger->cfg_items.ot_shutoff_enable = (be32_to_cpup(property));
+	}
+	else
+	{
+		charger->cfg_items.ot_shutoff_enable = 1;
+	}
+	power_dbg("[power]ot_shutoff_enable: %d\n", charger->cfg_items.ot_shutoff_enable);
+
+	/*get gpio config for charge led*/
+	charger->cfg_items.gpio_led_inner_pin = of_get_named_gpio_flags(charger->node, GPIO_NAME_LED_INNER_CHARGER, 0, &flags);
+	if (charger->cfg_items.gpio_led_inner_pin >= 0)
+	{
+		charger->cfg_items.gpio_led_active_low = flags & OF_GPIO_ACTIVE_LOW;
+		if (gpio_request(charger->cfg_items.gpio_led_inner_pin, GPIO_NAME_LED_INNER_CHARGER) < 0) 
+		{
+			power_err("[ATC260X] gpio_led_inner_pin =%d request failed!\n", charger->cfg_items.gpio_led_inner_pin);
+		}
+		else
+		{
+			charger->cfg_items.charger_led_exist = true;
+			gpio_direction_output(charger->cfg_items.gpio_led_inner_pin, !charger->cfg_items.gpio_led_active_low);
+
+		}
+	}
+	/* get gpio config for external charge control */
+	charger->cfg_items.gpio_ext_chg_ctrl_pin = of_get_named_gpio_flags(charger->node, GPIO_NAME_EXT_CTRL_CHARGER, 0, &flags);
+	if (charger->cfg_items.gpio_ext_chg_ctrl_pin >= 0)
+	{
+		charger->cfg_items.gpio_ext_chg_ctrl_active_low = flags & OF_GPIO_ACTIVE_LOW;
+		if (gpio_request(charger->cfg_items.gpio_ext_chg_ctrl_pin, GPIO_NAME_EXT_CTRL_CHARGER) < 0) 
+		{
+			power_err("[ATC260X] gpio_ext_chg_ctrl_pin =%d request failed!\n", charger->cfg_items.gpio_ext_chg_ctrl_pin);
+		}
+		else
+		{
+			charger->cfg_items.ext_charger_exist = true;
+			gpio_direction_output(charger->cfg_items.gpio_ext_chg_ctrl_pin, !charger->cfg_items.gpio_ext_chg_ctrl_active_low);
+
+		}
+	}
+	
+	return ret;
+	
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void atc260x_power_early_suspend(struct early_suspend *handler)
+{
+	power_dbg("%s, %d, enter early_suspend\n", __FUNCTION__, __LINE__);
+}
+
+static void atc260x_power_later_resume(struct early_suspend *handler)
+{
+	struct atc260x_charger *charger = container_of(handler, struct atc260x_charger, early_suspend);
+	wake_lock_timeout(&charger->delay_lock, 5 * HZ);
+	power_dbg("%s, %d, enter later_resume\n", __FUNCTION__, __LINE__);
+	cancel_delayed_work_sync(&charger->work);
+	enter_later_resume = true;
+	charger->set_constant_current(charger, charger->cfg_items.bl_on_current_usb_pc);
+	/*mod_timer(&charger->adjust_current_timer, jiffies + msecs_to_jiffies(3000));*/
+	queue_delayed_work(charger->charger_wq, &charger->work,msecs_to_jiffies(3000));
+	
+}
+#endif  //CONFIG_HAS_EARLYSUSPEND
+
+static struct power_supply_desc usb_desc;
+static struct power_supply_desc wall_desc;
+
+static  int atc260x_power_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x = dev_get_drvdata(pdev->dev.parent);
+	struct atc260x_power *power;
+	struct atc260x_charger *charger;    
+	int ret;
+	struct power_supply_config charger_psy_cfg = {};
+	struct power_supply_config wall_psy_cfg = {};
+	struct power_supply_config usb_psy_cfg = {};
+
+	power = kzalloc(sizeof(struct atc260x_power), GFP_KERNEL);
+	if (power == NULL)
+		return -ENOMEM;
+
+	global_power_dev = power;
+	power->atc260x = atc260x;
+	
+	charger = &power->charger;
+	charger->atc260x = atc260x;
+	charger->node = pdev->dev.of_node;
+	mutex_init(&charger->lock);
+	init_completion(&charger->check_complete);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	charger->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	charger->early_suspend.suspend = atc260x_power_early_suspend;
+	charger->early_suspend.resume = atc260x_power_later_resume;
+	register_early_suspend(&charger->early_suspend);
+#endif
+	
+	platform_set_drvdata(pdev, power);
+	/* monitor interval is 2 seconds */
+	charger->interval = 2;
+
+	charger->debug_file = atc260x_bat_create_debugfs(charger);
+	
+	/*modified by cxj @20141009 */
+	if (get_pmu_ic_type(charger) == ATC260X_ICTYPE_2603A)
+	{
+		atc2603a_get_info(charger);
+	}
+	else if (get_pmu_ic_type(charger) == ATC260X_ICTYPE_2603C)
+	{
+		atc2603c_get_info(charger);
+	}
+	else
+	{
+		//atc260x_get_2609A_info(charger);
+		pr_err("we do not support the ATC2603C\n");
+	}
+
+	/*init battery power supply*/
+	charger->charger_desc.name = "battery";
+	charger->charger_desc.use_for_apm = 1;
+	charger->charger_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+	charger->charger_desc.properties = atc260x_bat_props;
+	charger->charger_desc.num_properties = ARRAY_SIZE(atc260x_bat_props);        
+	charger->charger_desc.get_property = atc260x_bat_get_props;
+	charger_psy_cfg.drv_data = charger;
+	charger->psy = power_supply_register(&pdev->dev, &charger->charger_desc, &charger_psy_cfg);
+	if (IS_ERR(charger->psy)) {
+		ret = PTR_ERR(charger->psy);
+		goto err_kmalloc;
+	}
+	power_dbg("%s:power_supply_register for bat success\n", __func__);
+
+	/*init wall power supply*/
+	wall_desc.name = PSY_NAME_WALL;
+	wall_desc.type = POWER_SUPPLY_TYPE_MAINS;
+	wall_desc.properties = atc260x_wall_props;
+	wall_desc.num_properties = ARRAY_SIZE(atc260x_wall_props);
+	wall_desc.get_property = atc260x_wall_get_prop;
+	wall_psy_cfg.drv_data = power;
+	power->wall = power_supply_register(&pdev->dev, &wall_desc, &wall_psy_cfg);
+	if (IS_ERR(power->wall)) {
+		ret = PTR_ERR(power->wall);
+		goto err_battery;
+	}
+
+	/*init usb power supply*/
+	usb_desc.name = PSY_NAME_USB;
+	usb_desc.type = POWER_SUPPLY_TYPE_USB;
+	usb_desc.properties = atc260x_usb_props;
+	usb_desc.num_properties = ARRAY_SIZE(atc260x_usb_props);
+	usb_desc.get_property = atc260x_usb_get_prop;
+	usb_psy_cfg.drv_data = power;
+	power->usb = power_supply_register(&pdev->dev, &usb_desc, &usb_psy_cfg);
+	if (IS_ERR(power->usb)) {
+		ret = PTR_ERR(power->usb);
+		goto err_wall;
+	}      
+
+	first_power_on = true;
+	
+	/*tricle value init 200mA*/
+	charger->tricle_current = 200; 
+
+	ret = atc260x_power_create_sysfs(charger);
+	if (ret)
+		goto err_usb;
+	
+	if (atc260x_get_cfg_item(charger) != 0)
+		goto err_create_sysfs;
+
+	if (charger->cfg_items.ext_dcdc_exist)
+	{
+		charger->pwm = pwm_get(&pdev->dev, NULL);
+		if (IS_ERR(charger->pwm)) 
+		{
+			pr_err("%s, unable to request PWM!\n", __FUNCTION__);
+			goto err_usb;
+		}
+
+		if (0 == charger->pwm->period) 
+		{
+			pr_err("%s, ext_dcdc_pwm->period is zero!\n", __FUNCTION__);
+			goto err_usb;
+		}
+	}
+
+	atc260x_charger_init(charger);
+	
+//	atc260x_check_bat_online(charger);
+//	charger->charger_cur_status = atc260x_charger_check_online(charger);
+	
+
+	/*check if battery is healthy*/
+	charger->battery_status = charger->check_health_pre(charger->atc260x);
+
+	pr_info("charger=%p %d\n", charger, __LINE__);
+	pr_info("set_vbus_path=%p set_apds_vbus_pd=%p\n", charger->set_vbus_path, 
+	     charger->set_apds_vbus_pd);
+	
+	set_judge_adapter_type_handle((void *)distinguish_adapter_type);
+	
+	/*init wake_lock*/     
+	wake_lock_init(&charger->charger_wake_lock, WAKE_LOCK_SUSPEND, "charger_lock");        
+	wake_lock_init(&charger->delay_lock, WAKE_LOCK_SUSPEND, "delay_lock");     
+
+	/*Create a work queue for the clmt*/
+	charger->charger_wq =
+		create_singlethread_workqueue("atc260x_charger_wq");
+	if (charger->charger_wq == NULL) 
+	{
+		power_dbg("[%s]:failed to create work queue\n", __func__);
+		goto err_destroy_wakelock;
+	}
+	INIT_DELAYED_WORK(&charger->work, atc260x_charging_monitor);
+	queue_delayed_work(charger->charger_wq, &charger->work, 0 * HZ);
+
+	return 0;
+	
+err_destroy_wakelock:
+	wake_lock_destroy(&charger->charger_wake_lock);
+	wake_lock_destroy(&charger->delay_lock);
+err_create_sysfs:
+	atc260x_power_remove_sysfs(charger);
+err_usb:
+	 power_supply_unregister(power->usb);
+err_wall:
+	//ywwang: todo:  free gpio already requested    
+	power_supply_unregister(power->wall);
+err_battery:
+	power_supply_unregister(charger->psy); 
+err_kmalloc:
+	kfree(power);
+	platform_set_drvdata(pdev, NULL);
+	global_power_dev=0;
+	return ret;
+}
+
+static  int atc260x_power_remove(struct platform_device *pdev)
+{
+	struct atc260x_power *atc260x_power = platform_get_drvdata(pdev);
+
+	if(!atc260x_power)
+	{
+		pr_warn("strange  atc260x_power is null ,probe maybe failed\n");
+		return 0;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+		unregister_early_suspend(&atc260x_power->charger.early_suspend);
+#endif
+
+	cancel_delayed_work_sync(&atc260x_power->charger.work);
+	atc260x_bat_remove_debugfs(&atc260x_power->charger);
+
+	power_supply_unregister(atc260x_power->charger.psy);
+	power_supply_unregister(atc260x_power->wall);
+	power_supply_unregister(atc260x_power->usb);
+
+	kfree(atc260x_power);
+
+	return 0;
+}
+
+/*record the time stamp of suspend*/
+static struct timespec suspend_ts;      
+static int atc260x_power_suspend(struct platform_device *pdev, pm_message_t m)
+{
+	struct atc260x_power *atc260x_power = platform_get_drvdata(pdev);
+	struct atc260x_charger *charger = &atc260x_power->charger;
+
+	dev_info(&pdev->dev, "atc260x_power_suspend()\n");
+	pr_info("%s,cur_bat_cap:%d, bat_mv:%d\n", __func__, charger->cur_bat_cap, charger->bat_mv);
+	cancel_delayed_work_sync(&atc260x_power->charger.work); 
+
+	if (charger->cfg_items.ext_dcdc_exist)
+		pwm_config(charger->pwm,
+				((charger->pwm->period) >> 6) * PWM_MAX_LEVEL,
+					charger->pwm->period);
+	if (charger->cfg_items.charger_led_exist) 
+	{
+		gpio_free(charger->cfg_items.gpio_led_inner_pin);	
+	}
+
+	if (charger->cfg_items.ext_charger_exist) 
+	{
+		gpio_free(charger->cfg_items.gpio_ext_chg_ctrl_pin);	
+	}
+	log_event_none(LOG_HEADER_SUSPEND);
+	getnstimeofday(&suspend_ts);
+
+	return 0;
+}
+
+ /*during supend, consumption by  1% for every 10800 seconds*/
+#define SUSPEND_SECONDS_1_PERCRNT   10800  
+static int atc260x_power_resume(struct platform_device *pdev)
+{
+	struct timespec resume_ts;
+	struct timespec sleeping_ts;
+	unsigned int cap_percent = 0;
+	struct atc260x_power *atc260x_power = platform_get_drvdata(pdev);
+	struct atc260x_charger *charger = &atc260x_power->charger;
+	
+	wake_lock_timeout(&charger->delay_lock, 5 * HZ);/*added by cxj at 2014/11/14*/
+	
+	dev_info(&pdev->dev, "atc260x_power_resume()\n");
+	pr_info("%s,cur_bat_cap:%d, bat_mv:%d\n", __func__, charger->cur_bat_cap, charger->bat_mv);
+	log_event_none(LOG_HEADER_RESUME);
+	
+	if (charger->cfg_items.charger_led_exist) 
+	{
+		gpio_request(charger->cfg_items.gpio_led_inner_pin, GPIO_NAME_LED_INNER_CHARGER);
+		gpio_direction_output(charger->cfg_items.gpio_led_inner_pin, 0);
+	}
+
+	if (charger->cfg_items.ext_charger_exist) 
+	{
+		gpio_request(charger->cfg_items.gpio_ext_chg_ctrl_pin, GPIO_NAME_EXT_CTRL_CHARGER);
+		gpio_direction_output(charger->cfg_items.gpio_ext_chg_ctrl_pin, 1);
+	}
+
+	//atc260x_charger_init(charger);
+	atc260x_dcdc_init(charger);
+	getnstimeofday(&resume_ts);
+	sleeping_ts = timespec_sub(resume_ts, suspend_ts);
+	cap_percent = (sleeping_ts.tv_sec / SUSPEND_SECONDS_1_PERCRNT);
+
+
+	if (charger->cur_bat_cap <= cap_percent) 
+	{
+		charger->cur_bat_cap = 0;
+		power_supply_changed(charger->psy);
+	} 
+	else 
+	{
+		charger->cur_bat_cap -= cap_percent;
+	}
+	
+	/*schedule_delayed_work(&charger->work, msecs_to_jiffies(0));*/
+	queue_delayed_work(charger->charger_wq, &charger->work, msecs_to_jiffies(0));
+	return 0;
+}
+
+static void atc260x_power_shutdown(struct platform_device *pdev)
+{
+	struct atc260x_power *atc260x_power = platform_get_drvdata(pdev);
+	struct atc260x_charger *charger = &atc260x_power->charger;
+	cancel_delayed_work_sync(&charger->work); 
+
+	if (charger->cfg_items.charger_led_exist) 
+	{
+		gpio_free(charger->cfg_items.gpio_led_inner_pin);	
+	}
+
+	if (charger->cfg_items.ext_charger_exist) 
+	{
+		gpio_free(charger->cfg_items.gpio_ext_chg_ctrl_pin);	
+	}
+
+	if (charger->cfg_items.ext_dcdc_exist == 1) 
+	{
+		pwm_disable(charger->pwm);
+		pwm_free(charger->pwm);
+		charger->pwm = NULL;
+	}
+	if (charger->atc260x == NULL)
+		return ;
+
+	dev_info(&pdev->dev, "atc260x_power_shutdown()\n");
+	pr_info("%s,cur_bat_cap:%d, bat_mv:%d\n", __func__, charger->cur_bat_cap, charger->bat_mv);
+	
+	if (charger->set_apds_wall_pd)
+		charger->set_apds_wall_pd(charger, true);
+	
+	return;
+}
+/* added by cxj @20141009 */
+static const struct of_device_id atc260x_power_match[] = {
+	{ .compatible = "actions,atc2603a-power", },
+	{ .compatible = "actions,atc2603c-power", },
+	{ .compatible = "actions,atc2609a-power", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_power_match);
+
+static struct platform_driver atc260x_power_driver = 
+{
+	.probe      = atc260x_power_probe,
+	.remove     = atc260x_power_remove,
+	.driver     = 
+	{
+	    .name = "atc260x-power",
+		.of_match_table = of_match_ptr(atc260x_power_match), /* by cxj */
+	},
+	.suspend    = atc260x_power_suspend,
+	.resume     = atc260x_power_resume,
+	.shutdown = atc260x_power_shutdown,
+};
+
+static int __init atc260x_power_init(void)
+{
+	//pr_info(" %s version: %s\n", THIS_MODULE->name, THIS_MODULE->version);
+	log_event_init();
+	return platform_driver_register(&atc260x_power_driver);
+}
+late_initcall(atc260x_power_init);
+
+static void __exit atc260x_power_exit(void)
+{
+	wake_lock_destroy(&global_power_dev->charger.charger_wake_lock);
+	wake_lock_destroy(&global_power_dev->charger.delay_lock);
+	platform_driver_unregister(&atc260x_power_driver);    
+}
+module_exit(atc260x_power_exit);
+
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("atc260x charger driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-power");
+MODULE_VERSION("Actions-v2-20140829102330");
diff --git a/drivers/power/bq27441_battery.c b/drivers/power/bq27441_battery.c
new file mode 100755
index 0000000..5cbb638
--- /dev/null
+++ b/drivers/power/bq27441_battery.c
@@ -0,0 +1,1891 @@
+
+#if 1
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <linux/moduleparam.h>
+
+#if 1
+#define DEPEND
+#define STORE_BQ27441_INFO 	1
+#else
+#define STORE_BQ27441_INFO 	0
+#endif
+
+
+//#define USED_EXT_TEMP
+
+//config for bq27441
+#define TI_CONFIG_STATUS "okay"
+#define TI_CONFIG_REG 0x55
+#define TI_CONFIG_CAP 3500
+#define TI_CONFIG_TERMVOL 3400
+#define TI_CONFIG_TERMCUR 250
+#define TI_CONFIG_TAPERVOL 4200
+#define TI_CONFIG_QMAXCELL 10369
+#define TI_CONFIG_LOADSEL 0x81
+#define TI_CONFIG_DESIGNENERGY 12950
+#define TI_CONFIG_DSGCUR 333
+#define TI_CONFIG_CHGCUR 267
+#define TI_CONFIG_QUITCUR 500
+//#define TI_CONFIG_UPDATAFLAG 0
+
+int TI_CONFIG_RATABLE[] = {106, 106, 107, 119, 85, 72, 79, 85, 77, 73, 94, 116, 116, 291, 467};
+
+
+#ifdef TI_DEBUG 
+#define print_info(fmt, args...)   \
+        do{                              \
+                printk(fmt, ##args);     \
+        }while(0)
+#else
+#define print_info(fmt, args...)
+#endif
+
+#define CONTROL_STATUS	0x0
+#define DEVICE_TYPE 		0x1
+#define FW_VERSION		0x2
+#define DM_CODE               0x4
+#define SET_CFGUPDATE	0x13
+#define SET_SOFT_RESET	0x42
+#define SEALED_CMD		0x20
+#define BAT_INSERT		0x0C
+#define RESET_CMD		0x41
+#define ITPOR				(1 << 5)
+#define DRIVER_VESION	1
+#define CAP_STEP	1
+#define INCREASE_CAP_COUNT		25
+#define DEDUCE_CAP_COUNT		30
+#define DEDUCE_CAP_COUNT_QUICK	15
+#define CAP_HOLD_COUNT			25
+
+
+#define OP_CONFIG_SUBID 0x40
+
+enum REGISTER_TYPE {
+    TYPE_INVALID,
+    TYPE_8Bit,
+    TYPE_16Bit,
+};
+
+enum REG_TYPE
+{
+	PMU_SYS_CTL9,
+};
+
+static int register_atc2603c[] =
+{
+	0x09, /*0:PMU_SYS_CTL9*/
+};
+
+#if STORE_BQ27441_INFO
+struct bq27441_info {
+	int act_vol;		/* battery voltage */
+	int act_cur; 		/* battery current */
+	int act_cap;    		/* actually capacity read from eg2801*/
+	int remain;    		/* remain capacity */
+	int full;    			/* full capacity */
+	int flag;
+	int health;
+	int temp;
+	int temp_in;
+	int status;
+	int control;
+	int vir_cap;
+};
+
+static struct bq27441_info bq27441_infos;
+static struct kobject *hw_gauge_kobj; 
+static int first_store_pmu_info_flag = 1;
+static bool ti_updataflag_config = false;
+
+static int bq27441_info_store_usb(struct bq27441_info* p_info);
+#endif
+
+static struct i2c_client *gauge_client;
+static int open_debug;
+static int open_save;
+static int cap_last_time;
+static int cap_chg_count;
+static int cap_hold_count = 0;
+int gauge = 0;
+static int deduce_vir_cap_count = 0;
+static int increase_vir_cap_count = 0;
+static int duringconfig = 0;
+
+
+extern void act260x_set_get_hw_cap_point(void *ptr);
+extern void act260x_set_get_hw_volt_point(void *ptr);
+extern void act260x_set_get_hw_cur_point(void *ptr);
+extern void act260x_set_get_hw_temp_point(void *ptr);
+
+extern int pmu_reg_read(unsigned short reg);
+extern int pmu_reg_write(unsigned short reg, unsigned short val);
+
+static int bq27441_config(struct i2c_client *client);
+static int bq27441_config_check(void);
+//extern void system_powerdown_withgpio(void);
+extern int pmu_reg_read(unsigned short);
+
+static int isDuringConfig(){
+	return duringconfig;
+}
+
+static void SetDuringConfig(int val){
+	duringconfig = val;
+}
+
+#if STORE_BQ27441_INFO
+/*
+	store log into /data/
+*/
+static int bq27441_info_store_usb(struct bq27441_info* p_info)
+{
+	u8 buf[200];
+	struct file *filp;
+	mm_segment_t fs;
+	int offset = 0;
+	int h, ms, ms_m, ms_s;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);	
+		
+	filp = filp_open("/data/bq27441_info.log", O_CREAT | O_RDWR, 0644); //  /mnt/sd-ext/ /mnt/uhost/
+	if (IS_ERR(filp)) {
+		print_info("[bq27441] can't accessed USB bq27441_info.log.\n");
+		return 0;
+	}
+
+    if (first_store_pmu_info_flag == 1) {
+        memset(buf,0,200);
+        offset = sprintf(buf, "time,act_vol, act_cur, act_cap, remain, full, flag, health, temp, temp_in,status, control\t\n");
+        filp->f_op->llseek(filp, 0, SEEK_END);
+        filp->f_op->write(filp, (char *)buf, offset + 1, &filp->f_pos);
+        first_store_pmu_info_flag = 0;
+    }
+    	
+    filp->f_op->llseek(filp, 0, SEEK_END);
+
+    memset(buf,0,200);
+    offset = sprintf(buf, "%02d:%02d:%02d,%04d,%04d,%d,%d,%d,0x%04x,0x%04x,%02d, %02d, 0x%04x, 0x%04x, %d\t\n",
+                h, ms_m, ms_s,
+                p_info->act_vol,
+                p_info->act_cur,
+                p_info->act_cap,
+                p_info->remain,
+		p_info->full,
+		p_info->flag ,
+		p_info->health,
+		p_info->temp,
+		p_info->temp_in,
+		p_info->status,
+		p_info->control,
+		p_info->vir_cap);
+    
+    filp->f_op->write(filp, (char *)buf, offset + 1, &filp->f_pos);
+    
+    set_fs(fs);
+    filp_close(filp, NULL);
+    return 0;
+}
+#endif
+
+static int bq27441_get_regs(struct i2c_client *client, 
+                int addr, u8 *buff, int count)
+{
+	struct i2c_msg msg[2];
+	int err;
+	u8 address;
+
+	address = addr & 0xff;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].buf = &address;
+	msg[0].len = 1;
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].buf = buff;
+	msg[1].len = count;
+
+	err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+	if ( err != 2 ) {
+		print_info("[bq27441] transfer failed(%d)\n", err);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int bq27441_set_reg(struct i2c_client *client, 
+    enum REGISTER_TYPE type, int addr, u16 data)
+{
+	unsigned char buffer[3];
+	int ret, count;
+
+	switch( type ) {
+	    case TYPE_8Bit:
+	        buffer[0] = addr & 0xff;
+	        buffer[1] = data & 0xff;
+	        count = 2;
+	        break;
+	    case TYPE_16Bit:
+	        buffer[0] = addr & 0xff;
+	        buffer[1] = (data >> 8) & 0xff;
+	        buffer[2] = data & 0xff;
+	        count = 3;
+	        break;
+	    default:
+	        return -EFAULT;
+	}
+    
+	ret = i2c_master_send(client, buffer, count);
+	if ( ret != count) {
+		print_info("[bq27441] set reg failed.\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int bq27441_set_subcmd(struct i2c_client *client, u8 sub_cmd)
+{
+	unsigned char buffer[3];
+	int ret, count;
+
+	buffer[0] = 0x00;
+	buffer[1] = sub_cmd & 0xff;
+	buffer[2] = 0x00;
+	count = 3;
+
+	ret = i2c_master_send(client, buffer, count);
+	if ( ret != count) {
+		pr_err("[bq27441] set reg failed.\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void store_first_product(void)
+{
+	int data;	
+	data = pmu_reg_read(register_atc2603c[PMU_SYS_CTL9]);
+	data |= 0x8000;
+	pmu_reg_write(register_atc2603c[PMU_SYS_CTL9], data);
+}
+
+/*
+	get first product flag
+*/
+static bool get_first_product(void)
+{
+	int pmu_sys_ctl9 = pmu_reg_read(register_atc2603c[PMU_SYS_CTL9]);
+	printk(KERN_ERR "[%s]:pmu_sys_ctl9:0x%x\n", __func__, pmu_sys_ctl9);
+	if (pmu_sys_ctl9 & 0x8000)	
+	{
+		return false;
+	}
+	else 
+	{
+		return true;
+	}
+}
+
+/*
+	get_pmu_battery_volatge
+	get voltage from pmu
+*/
+static int get_pmu_battery_volatge()
+{
+	int tmp = 0 ;	
+	int pmu_bat_v = 0;
+	tmp = pmu_reg_read(0x40);
+	pmu_bat_v = tmp * 2930 * 2 / 1000;
+	//print_info("get_pmu_battery_volatge %d\n",tmp);
+	return pmu_bat_v;
+}
+
+/*
+get_battery_healty
+if battery voltage < 3200 return 0 else return 1
+*/
+static int get_battery_healty()
+{
+	int pmu_bat_v = 0;
+
+	pmu_bat_v = get_pmu_battery_volatge();
+	if(pmu_bat_v > 3200)
+		return 1;
+	else
+		return 0;
+}
+
+/*
+	check battery error
+*/
+static int setError = 0;
+static int bq27441_check_error()
+{
+	int err = 0;
+	int temperature = 0;
+	int volatge = 0;
+	u8 vbuffer[2];
+	u8 tbuffer[2];
+	u8 ebuffer[2];
+	if(setError == 1)
+		return -1;
+	
+	if(get_battery_healty() == 0 || isDuringConfig() == 1)
+		return -1;
+
+	err = bq27441_set_subcmd(gauge_client, DM_CODE);
+	err = bq27441_get_regs(gauge_client, 0x0, ebuffer, 2);
+
+	//print_info("version[%x][%x][%x] \n", ((ebuffer[0]) | ebuffer[1]<<8),ebuffer[0],ebuffer[1]);
+
+	memset(vbuffer,sizeof(vbuffer),0);
+	err = bq27441_get_regs(gauge_client, 0x04, vbuffer, 2);
+	if ( err ) 
+	{ 
+		//print_info("[bq27441] check err : i2c error\n"); 
+		//print_info("volatge[%x][%x][%x] \n", ((vbuffer[0]) | vbuffer[1]<<8),vbuffer[0],vbuffer[1]);
+		return -1;
+	} 
+	volatge = ((vbuffer[0]) | vbuffer[1]<<8);
+	
+	// 273.16K = 0C
+	memset(tbuffer,sizeof(tbuffer),0);
+	err = bq27441_get_regs(gauge_client, 0x1e, tbuffer, 2);
+	if ( err ) 
+	{ 
+		//print_info("[eg2801] check err : i2c error\n"); 
+		//print_info("temperature [%x][%x][%x]  \n", ((tbuffer[0]) | tbuffer[1]<<8),tbuffer[0],tbuffer[1]);
+
+		return -1;
+	} 
+	temperature = ((tbuffer[0]) | tbuffer[1]<<8);
+
+	if(volatge < 2048 || temperature	 < 2048 ||  ((ebuffer[0]) | ebuffer[1]<<8) != 0x48)
+	{
+		//print_info("[eg2801] check err : date error\n");
+		//print_info("temperature [%x][%x][%x] volatge[%x][%x][%x] \n",temperature,tbuffer[0],tbuffer[1],volatge,vbuffer[0],vbuffer[1]);
+		return -2;
+	}
+	
+	//print_info("temperature [%x][%x][%x] volatge[%x][%x][%x] \n",volatge,tbuffer[0],tbuffer[1],volatge,vbuffer[0],vbuffer[1]);
+	return 0;
+}
+
+
+static int bq27441_get_real_capacity(void)
+{
+	u8 data[2];
+	int ret = 0;
+	int capacity = 0;
+	
+	if(get_battery_healty()==0 || isDuringConfig() == 1)
+		return 0;
+	
+	ret = bq27441_get_regs(gauge_client, 0x1c, data, 2);
+	if (ret) {
+		print_info("[bq27441] bq27441_get_real_voltage failed\n"); 
+		return -1; 
+	} 
+
+	capacity =((data[0]) | data[1]<<8);
+	return capacity;
+}
+
+
+static int bq27441_get_Flags_FC(void){
+	u8 data[2];
+	int ret = 0;
+	int Flags = 0;
+	int mask = 0xFDFF;
+	if(get_battery_healty()==0 || isDuringConfig() == 1)
+		return 0;
+	
+	ret = bq27441_get_regs(gauge_client, 0x06, data, 2);
+	if (ret) {
+		print_info("[bq27441] bq27441_get_Flag_FC failed\n"); 
+		return -1; 
+	} 
+
+	Flags = ((data[0]) | data[1]<<8);
+
+	if(Flags&(~mask) != 0){
+		return 1;
+	}
+	
+	print_info("Flags = %x , [%x][%x] [%x]",Flags,data[1],data[0],Flags&(~mask));
+		
+	return 0;
+
+}
+
+static int testTemp = 0;
+static int bq27441_get_real_temperature(void)
+{
+	u8 data[2];
+	int ret = 0;
+	int temperature = 0;
+
+	if(testTemp != 0){
+		return testTemp;
+	}
+
+	if(get_battery_healty()==0 || isDuringConfig() == 1)
+		return 0;
+	
+	ret = bq27441_get_regs(gauge_client, 0x1e, data, 2);
+
+	if (ret) {
+		print_info("[bq27441] bq27441_get_real_temperature failed\n"); 
+		return -1; 
+	} 
+	temperature = ((data[0]) | data[1]<<8);
+	temperature = temperature / 10 - 273;
+	print_info("[bq27441] temp = %d data[0]=%d data[1]=%d\n",temperature,data[0],data[1]); 
+	return temperature;
+}
+
+static int bq27441_get_real_voltage(void)
+{
+	u8 data[2];
+	int ret = 0;
+	int voltage = 0;
+	
+	if(get_battery_healty()==0 || isDuringConfig() == 1)
+		return 0;
+	
+	ret = bq27441_get_regs(gauge_client, 0x4, data, 2);
+	if (ret) {
+		print_info("[bq27441] bq27441_get_real_voltage failed\n"); 
+		return -1; 
+	} 
+
+	voltage = ((data[0]) | data[1]<<8);
+	
+	return voltage;
+}
+
+static int bq27441_get_real_current(void)
+{
+	u8 data[2];
+	int ret = 0, bat_current;
+	
+	if(get_battery_healty()==0 || isDuringConfig() == 1)
+		return 0;
+	
+	ret = bq27441_get_regs(gauge_client, 0x10, data, 2);
+	if (ret) { 
+		print_info("[bq27441] bq27441_get_real_current failed\n"); 
+		return -1; 
+	} 
+
+	bat_current = ((data[0]) | data[1]<<8);
+	if (data[1] & 0x80)
+		bat_current |= 0xFFFF0000;
+
+	return bat_current;
+}
+
+static int bq27441_get_type(void)
+{
+	return 1;
+}
+
+static int bq27441_get_capacity(void)
+{
+	int act_cap = 0, vir_cap, real_cap, i, deduce_count;
+	int act_vol = 0;
+	int act_cur = 0;
+	int ret;
+	u8 data[2];
+	int remain, full, health, temp, temp_in, flag, status, control;
+	//return 0;
+	if(get_battery_healty()==0 || isDuringConfig() == 1)
+		return 0;
+	
+#if STORE_BQ27441_INFO
+	struct bq27441_info* p_info = &bq27441_infos;
+#endif
+
+	for (i = 0; i < 16; i++) {
+		ret = bq27441_get_regs(gauge_client, 0x06, data, 2);
+		if (!ret) 
+			break;
+		if (i == 15) {
+			pr_err("[bq27441]get_caoacity flags failed,cap_last_time=%d , system_powerdown_withgpio\n", cap_last_time);
+			//system_powerdown_withgpio();
+			return cap_last_time;
+		}
+	}
+
+	ret = ((data[0]) | (data[1]<<8));
+	if (ret & ITPOR) {
+		ret = bq27441_get_regs(gauge_client, 0x06, data, 2);
+		if (ret) {
+			pr_err("[bq27441]get_caoacity flags failed,cap_last_time=%d \n", cap_last_time);
+			return cap_last_time;
+		}
+		ret = ((data[0]) | data[1]<<8);
+		if (ret & ITPOR) {
+			print_info("[bq27441][RECONFIG] WATCH DOG HAS DEAD,RECONFIG \n");
+			pr_err("[bq27441]reconfig, flags=0x%x\n", ret);
+			ret = bq27441_config(gauge_client);
+			if ( ret < 0) {
+				pr_err("[bq27441] First config failed\n");
+				msleep(1000);
+				ret = bq27441_config(gauge_client);
+				if ( ret < 0) {
+					pr_err("[bq27441] Second config failed\n");
+					msleep(5000);
+					ret = bq27441_config(gauge_client);
+					if ( ret < 0) {
+						pr_err("[bq27441] Third config failed\n");
+						return cap_last_time;
+					}
+				}
+			}
+		}
+	}
+
+	act_cap = bq27441_get_real_capacity();
+	act_vol = bq27441_get_real_voltage();
+	act_cur = bq27441_get_real_current();
+
+	bq27441_get_regs(gauge_client, 0x0, data, 2);
+	control = ((data[0]) | data[1]<<8);
+
+	bq27441_get_regs(gauge_client, 0x2, data, 2);
+	temp = ((data[0]) | data[1]<<8) - 2731;
+
+	bq27441_get_regs(gauge_client, 0x06, data, 2);
+	flag = ((data[0]) | data[1]<<8);
+
+	bq27441_get_regs(gauge_client, 0x0C, data, 2);
+	remain = ((data[0]) | data[1]<<8);
+
+	bq27441_get_regs(gauge_client, 0x0E, data, 2);
+	full = ((data[0]) | data[1]<<8);
+
+	bq27441_get_regs(gauge_client, 0x1E, data, 2);
+	temp_in = ((data[0]) | data[1]<<8) - 2731;
+
+	bq27441_get_regs(gauge_client, 0x20, data, 2);
+	health = ((data[0]) | data[1]<<8);
+
+	bq27441_set_subcmd(gauge_client, CONTROL_STATUS);
+	bq27441_get_regs(gauge_client, 0x0, data, 2);
+	status = ((data[0]) | data[1]<<8);
+
+	vir_cap = act_cap;
+
+CAP_REORT:
+
+	if (open_debug) {
+		print_info(KERN_ERR"[cap]  %d\t%d\t%d\t%d\t%d\t0x%x\t%d\t%d\t%d\t0x%x\t0x%x\t%d\n",
+			act_vol, act_cur, act_cap, remain, full, flag, health, temp, temp_in,status, control, vir_cap);
+	}
+
+#if STORE_BQ27441_INFO
+		if (open_save) {
+			memset(&bq27441_infos, 0, sizeof(bq27441_infos));
+			p_info->act_vol = act_vol;
+			p_info->act_cur = act_cur;
+			p_info->act_cap = act_cap;
+			p_info->remain = remain;
+			p_info->full = full;
+			p_info->flag = flag;
+			p_info->health = health;
+			p_info->temp = temp;
+			p_info->temp_in = temp_in;
+			p_info->status = status;
+			p_info->control = control;
+			p_info->vir_cap = vir_cap;
+			bq27441_info_store_usb(p_info);
+		}
+#endif
+#if 0
+	if(vir_cap == 100){
+		if(bq27441_get_Flags_FC()||(act_vol > 4250 && act_cur < 200)){
+			vir_cap = 100;
+			//print_info("FC Hase Set UP  return 100\n");
+		}
+		else{
+			vir_cap = 99;
+			//print_info("FC Hase Not Set UP  return 99 (vol %d | cur %d )\n",act_vol,act_cur);
+		}
+	}
+#endif
+	return vir_cap;
+}
+
+//1860302360\C5\CB
+/*
+	bq27441_config
+	init bq27441
+*/
+static int bq27441_config(struct i2c_client *client)
+{
+	int ret, len, tmp, new_tmp, flag, index, addr,reg_value, i;
+	u8 old_csum ,new_csum_first, new_csum_sec, new_csum_third, data[2];
+	u16 old_capacity, old_term_vol, old_taper_rate, old_load_sel, dsg_cur, chg_cur, quit_cur, old_taper_vol, old_design_energy, old_reserve_cap, old_term_vdatle;
+	u16 new_capacity, new_term_vol, new_term_cur, new_taper_rate, new_load_sel,new_qmax_cell, new_taper_vol, new_design_energy, new_reserve_cap;
+	u16 new_dsg_cur, new_chg_cur, new_quit_cur, new_term_vdatle, old_qmax_cell;
+	int a_x = 0;
+	struct device_node *node;
+	const __be32 *property;
+	int new_version = DRIVER_VESION;
+	node = client->dev.of_node;
+	
+	SetDuringConfig(1);
+
+	ret = bq27441_set_subcmd(client, CONTROL_STATUS);
+	ret |= bq27441_get_regs(client, 0x0, data, 2);
+	if (ret) {
+		pr_err("[bq27441] get control status register failed\n");
+		goto ERROR; 
+      }
+	print_info("[bq27441] CONTROL_STATUS: 0x%x%x\n", data[1],data[0]);
+
+	if (data[1] & 0x20) {
+		ret   = bq27441_set_reg(client, TYPE_8Bit, 0x00, 0x00);
+		ret   |= bq27441_set_reg(client, TYPE_8Bit, 0x01, 0x80);
+		ret   |= bq27441_set_reg(client, TYPE_8Bit, 0x00, 0x00);
+		ret   |= bq27441_set_reg(client, TYPE_8Bit, 0x01, 0x80);
+		if (ret) {
+			pr_err("[bq27441] set device usealed failed\n");
+			goto ERROR; 
+	        }
+	}
+
+	bq27441_set_subcmd(client, RESET_CMD);
+	msleep(5000);
+
+	ret = bq27441_set_subcmd(client, SET_CFGUPDATE);
+	msleep(1200);
+	ret |= bq27441_get_regs(client, 0x6, data, 2);
+
+	flag = data[0] & 0x10;
+	if (!flag) {
+		print_info("[bq27441] CFGUPDATE mode failed! 0x%x%x,\n", ret ,data[0],data[1]);
+		goto ERROR; 
+	}
+
+SET_BLOCK_FIRST:
+	
+#if USED_EXT_TEMP
+	ret   = bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x40);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x40 failed\n");
+		goto ERROR; 
+	}
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		old_csum = data[0];
+		print_info("[bq27441] old csum_blk(40) = 0x%x\n", old_csum);
+	} else {
+		pr_err("[bq27441] get old csum_blk(40) failed\n");
+		goto ERROR; 
+	}
+
+	ret = bq27441_get_regs(client, 0x40, data,2);
+	print_info("[bq27441][OP_CONFIG] 0x40 is:[%x] [%x]\n",data[0],data[1]);
+	ret = bq27441_get_regs(client, 0x3a, data,2);
+	print_info("[bq27441][OP_CONFIG] 0x3a is:[%x] [%x]\n",data[0],data[1]);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		print_info("[bq27441] old csum_blk(40) = 0x%x\n", data[0]);
+	} else {
+		pr_err("[bq27441] get old csum_blk(40) failed\n");
+		goto ERROR; 
+	}
+
+	ret  =  bq27441_set_reg(client, TYPE_16Bit, 0x40, 0x25f8);
+	if (ret) {
+		pr_err("[bq27441] set opConfig failed\n");
+		goto ERROR; 
+	}
+	
+	ret = bq27441_set_reg(client, TYPE_8Bit, 0x60, 0x6A);
+	print_info("[bq27441] count new csum_blk(40) = 0x%x data[0]=%x data[1]=%x \n", new_csum_first,data[0] ,data[1] );
+
+	ret = bq27441_get_regs(client, 0x40, data,2);
+	print_info("[bq27441][OP_CONFIG] 0x40 is:[%x] [%x]\n",data[0],data[1]);
+	ret = bq27441_get_regs(client, 0x3a, data,2);
+	print_info("[bq27441][OP_CONFIG] 0x3a is:[%x] [%x]\n",data[0],data[1]);
+
+#endif
+
+	ret   = bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x24);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x24 failed\n");
+		goto ERROR; 
+	}
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		old_csum = data[0];
+		print_info("[bq27441] old csum_blk(36) = 0x%x\n", old_csum);
+	} else {
+		pr_err("[bq27441] get old csum_blk(36) failed\n");
+		goto ERROR; 
+	}
+
+	if (0xD3 != old_csum) {
+		ret  =  bq27441_set_reg(client, TYPE_16Bit, 0x47, 0xc8);
+		if (ret) {
+			pr_err("[bq27441] set DODatEOC Delta T failed\n");
+			goto ERROR; 
+		}
+		ret = bq27441_set_reg(client, TYPE_8Bit, 0x60, 0xD3);
+		mdelay(50);
+	}
+
+SET_BLOCK_SECOND:
+
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x52);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x52  failed\n");
+		goto ERROR; 
+        }
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		old_csum = data[0];
+		print_info("[bq27441] old csum_blk(82) = 0x%x\n", old_csum);
+        } else {
+		pr_err("[bq27441] get check sum failed\n");
+		goto ERROR; 
+        }
+
+	ret |= bq27441_get_regs(client, 0x40, data, 2);
+	old_qmax_cell = (data[0]<<8) |data[1];
+	tmp = data[0] + data[1];
+
+	ret |= bq27441_get_regs(client, 0x45, data, 1);
+	old_load_sel = data[0];
+	tmp += data[0];
+
+	ret |= bq27441_get_regs(client, 0x4A, data, 2);
+	old_capacity = (data[0]<<8) |data[1];
+	tmp += data[0] + data[1];
+	
+	#ifdef TI_CONFIG_DESIGNENERGY
+		property = TI_CONFIG_DESIGNENERGY;
+		new_design_energy = property;
+		ret |= bq27441_get_regs(client, 0x4C, data, 2);
+		old_design_energy = (data[0]<<8) |data[1];
+		tmp += data[0] + data[1];
+	#else
+		new_design_energy = 0x00;
+	#endif 
+	
+	ret |= bq27441_get_regs(client, 0x50, data, 2);
+	old_term_vol = (data[0]<<8) |data[1];
+	tmp += data[0] + data[1];
+
+	ret |= bq27441_get_regs(client, 0x52, data, 2);
+	old_term_vdatle = (data[0]<<8) |data[1];
+	tmp += data[0] + data[1];
+
+	ret |= bq27441_get_regs(client, 0x5B, data, 2);
+	old_taper_rate = (data[0]<<8) |data[1];
+	tmp += data[0] + data[1];
+
+	ret |= bq27441_get_regs(client, 0x5D, data, 2);
+	old_taper_vol = (data[0]<<8) |data[1];
+	tmp += data[0] + data[1];
+
+	#ifdef TI_CONFIG_RESERVECAP
+		new_reserve_cap = TI_CONFIG_RESERVECAP;
+		ret |= bq27441_get_regs(client, 0x43, data, 2);
+		old_reserve_cap = (data[1]<<8) |data[0];
+		tmp += data[0] + data[1];
+	#else
+		new_reserve_cap = 0x00;
+	#endif
+	
+	tmp += old_csum;
+
+	if (ret) {
+		pr_err("[bq27441] get blk(82) old config failed\n");
+		goto ERROR; 
+	} else {
+		print_info("[bq27441] old config: cap=%dmAh, term_vol=%dmV, taper_rate=%d, old_qmax_cell=%d, old_taper_vol=%d, \
+old_design_energy=%d, old_term_vdatle=%d, old_load_sel=%d\n",
+			old_capacity, old_term_vol, old_taper_rate, old_qmax_cell, old_taper_vol, old_design_energy, old_term_vdatle, old_load_sel);
+	}
+	
+	#ifdef TI_CONFIG_CAP
+		new_capacity = TI_CONFIG_CAP;
+	#else
+		new_capacity = 5700;
+	#endif
+
+	#ifdef TI_CONFIG_TERMVOL
+		new_term_vol = TI_CONFIG_TERMVOL;
+	#else
+		new_term_vol = 3400;
+	#endif
+
+	#ifdef TI_CONFIG_TERMCUR
+		new_term_cur = TI_CONFIG_TERMCUR;
+	#else
+		new_term_cur = 350;
+	#endif
+	
+	#ifdef TI_CONFIG_QMAXCELL
+		new_qmax_cell = TI_CONFIG_QMAXCELL;
+	#else
+		pr_err("[bq27441] get qmax_cell from dts failed\n");
+		goto ERROR; 
+	#endif
+
+	#ifdef TI_CONFIG_TAPERVOL
+		new_taper_vol = TI_CONFIG_TAPERVOL;
+	#else
+		new_taper_vol = 4140;
+	#endif
+
+	#ifdef TI_CONFIG_LOADSEL
+		new_load_sel = TI_CONFIG_LOADSEL;
+	#else
+		new_load_sel = 0x81;
+	#endif
+	
+	new_taper_rate = (new_capacity*10)/new_term_cur;
+	new_term_vdatle = 100;
+
+	new_tmp = ((new_capacity >> 8)&0xFF) + (new_capacity&0xFF);
+	new_tmp += ((new_term_vol >> 8)&0xFF) + (new_term_vol&0xFF);
+	new_tmp += ((new_taper_rate >> 8)&0xFF) + (new_taper_rate&0xFF);
+	new_tmp += ((new_qmax_cell >> 8)&0xFF) + (new_qmax_cell&0xFF);
+	new_tmp += ((new_taper_vol >> 8)&0xFF) + (new_taper_vol&0xFF);
+	new_tmp += new_term_vdatle;
+	new_tmp += new_load_sel;
+	if (new_reserve_cap)
+		new_tmp += ((new_reserve_cap >> 8)&0xFF) + (new_reserve_cap&0xFF);
+	
+	if (new_design_energy)
+		new_tmp += ((new_design_energy >> 8)&0xFF) + (new_design_energy&0xFF);
+
+	print_info("[bq27441] new config: cap=%dmAh, term_vol=%dmV, new_taper_rate=%d, qmax_cell=%d, \
+new_taper_vol=%d, new_design_energy=%d, new_term_vdatle=%d\n",
+		new_capacity, new_term_vol, new_taper_rate, new_qmax_cell, new_taper_vol, new_design_energy, new_term_vdatle);
+
+	print_info("[bq27441]tmp: 0x%x, new_tmp=%x\n", tmp, new_tmp);
+	new_csum_first= ~((~(tmp&0xFF) + new_tmp)&0xFF);
+	print_info("[bq27441] new csum_blk(82) =0x%x !\n", new_csum_first);
+
+	if (new_csum_first == old_csum) {
+		print_info("[bq27441] FW blk(82) have already configed!\n");
+	} else {
+		ret  =  bq27441_set_reg(client, TYPE_16Bit, 0x40, new_qmax_cell);
+		ret |= bq27441_set_reg(client, TYPE_8Bit, 0x45, new_load_sel);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x4A, new_capacity);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x50, new_term_vol);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x52, new_term_vdatle);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x5B, new_taper_rate);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x5D, new_taper_vol);
+		if (new_reserve_cap) {
+			ret |= bq27441_set_reg(client, TYPE_16Bit, 0x43, new_reserve_cap);
+		}
+
+		if (new_design_energy) {
+			ret |= bq27441_set_reg(client, TYPE_16Bit, 0x4C, new_design_energy);
+		}
+
+		if (ret) {
+			pr_err("[bq27441] set new config failed\n");
+			goto ERROR; 
+		}
+
+		ret = bq27441_set_reg(client, TYPE_8Bit, 0x60, new_csum_first);
+		mdelay(50);
+	}
+
+SET_BLOCK_THIRD:
+	ret   = bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x51);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x51  failed\n");
+		goto ERROR; 
+        }
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		old_csum = data[0];
+		print_info("[bq27441] old csum_blk(81) = 0x%x\n", old_csum);
+        } else {
+		pr_err("[bq27441] get csum_blk(81) failed\n");
+		goto ERROR; 
+        }
+
+	ret = bq27441_get_regs(client, 0x40, data, 2);
+	dsg_cur = (data[1]<<8) |data[0];
+	tmp = data[0] + data[1];
+
+	ret |= bq27441_get_regs(client, 0x42, data, 2);
+	chg_cur = (data[1]<<8) |data[0];
+	tmp += data[0] + data[1];
+
+	ret |= bq27441_get_regs(client, 0x44, data, 2);
+	quit_cur = (data[1]<<8) |data[0];
+	tmp += data[0] + data[1];
+
+	tmp += old_csum;
+
+	if (ret) {
+		pr_err("[bq27441] get blk(36) old config failed\n");
+		goto ERROR; 
+	} else {
+		pr_err("[bq27441] old config: Dsg_cur=%dmA, Chg_cur=%dmA, Quit_cur=%dmA\n",
+			dsg_cur, chg_cur, quit_cur);
+	}
+
+	#ifdef TI_CONFIG_DSGCUR
+		new_dsg_cur = TI_CONFIG_DSGCUR;
+	#else
+		new_dsg_cur = 167;
+	#endif
+
+	#ifdef TI_CONFIG_CHGCUR
+		new_chg_cur = TI_CONFIG_CHGCUR;
+	#else
+		new_chg_cur = 100;
+	#endif
+
+	#ifdef TI_CONFIG_QUITCUR
+		new_quit_cur = TI_CONFIG_QUITCUR;
+	#else
+		new_quit_cur = 250;
+	#endif
+	
+	print_info("[bq27441] new_dsg_cur: %d, new_chg_cur=%d, new_quit_cur=%d\n", new_dsg_cur, new_chg_cur, new_quit_cur);
+	new_tmp = ((new_dsg_cur >> 8)&0xFF) + (new_dsg_cur&0xFF);
+	new_tmp += ((new_chg_cur >> 8)&0xFF) + (new_chg_cur&0xFF);
+	new_tmp += ((new_quit_cur >> 8)&0xFF) + (new_quit_cur&0xFF);
+	
+	//print_info("[bq27441]tmp: 0x%x, new_tmp=%x\n", tmp, new_tmp);
+	new_csum_sec= ~((~(tmp&0xFF) + new_tmp)&0xFF);
+	print_info("[bq27441] new csum_blk(81)=0x%x !(0x8D)\n", new_csum_sec);
+
+	if (new_csum_sec == old_csum) {
+		print_info("[bq27441] FW blk(81) have already configed!\n");
+	} else {
+		ret  =  bq27441_set_reg(client, TYPE_16Bit, 0x40, new_dsg_cur);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x42, new_chg_cur);
+		ret |= bq27441_set_reg(client, TYPE_16Bit, 0x44, new_quit_cur);
+
+		if (ret) {
+			pr_err("[bq27441] set new config failed\n");
+			goto ERROR; 
+		}
+			
+		ret = bq27441_set_reg(client, TYPE_8Bit, 0x60, new_csum_sec);
+		mdelay(50);
+	}
+
+SET_BLOCK_FOUTH:
+	//TI_CONFIG_RATABLE
+		//of_property_read_u32_array(node, "ra_table", ra_table, 15);
+		print_info("GET NEW R_TABLE:\n[bq27441] ");
+		for (index = 0; index < 15; index++) {
+			print_info("%d\t", TI_CONFIG_RATABLE[index]);
+		}
+
+		ret   = bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+		ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x59);
+		ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+		if (ret) {
+			pr_err("[bq27441] set first 32 bytes in block 0x59 failed\n");
+			goto ERROR; 
+		}
+
+		mdelay(50);
+
+		ret = bq27441_get_regs(client, 0x60, data, 1);
+		if (!ret) {
+			old_csum = data[0];
+			printk(KERN_ERR "\n[bq27441] old csum_blk(89)= 0x%x\n", old_csum);
+		} else {
+			pr_err("\n[bq27441] get csum_blk(89) failed\n");
+			goto ERROR; 
+		}
+
+		print_info("OLD R_TABLE:\n[bq27441] ");
+		for (index = 0, addr = 0x40, tmp = 0; index < 15; index++, addr+=2) {
+			ret = bq27441_get_regs(client, addr, data, 2);
+			if (ret) {
+				pr_err("[bq27441] get old config blk(89) failed\n");
+				goto ERROR; 
+			}
+			tmp += data[0] + data[1];
+			reg_value = (data[0]<<8) + data[1];
+			print_info("%d\t", reg_value);
+		}
+
+		tmp += old_csum;
+		
+		for (index = 0, new_tmp = 0; index < 15; index++) {
+			new_tmp += ((TI_CONFIG_RATABLE[index]>> 8)&0xFF) + (TI_CONFIG_RATABLE[index]&0xFF);
+		}
+		
+		print_info("\n[bq27441]tmp: 0x%x, new_tmp=%x\n", tmp, new_tmp);
+		new_csum_third= ~((~(tmp&0xFF) + new_tmp)&0xFF);
+		print_info("[bq27441] new csum_blk(89)=0x%x !\n", new_csum_third);
+
+		if (new_csum_third == old_csum) {
+			printk(KERN_ERR "[bq27441] FW blk(89) have already configed!\n");
+		} else {
+			printk(KERN_ERR "[bq27441] FW blk(89) not have configed!\n");
+			for (index = 0, addr = 0x40; index < 15; index++, addr+=2) {
+				print_info("[bq27441] set r_table index =%d\n", index);
+				mdelay(100);
+				ret = bq27441_set_reg(client, TYPE_16Bit, addr, TI_CONFIG_RATABLE[index]);
+				if (ret) {
+					pr_err("[bq27441] set old config blk(89) failed\n");
+					goto ERROR; 
+				}
+			}
+
+			ret = bq27441_set_reg(client, TYPE_8Bit, 0x60, new_csum_third);
+			if (ret) {
+				pr_err("[bq27441] set new csum_blk(89) failed\n");
+				goto ERROR; 
+			}
+			mdelay(50);
+		}
+			
+		ret |= bq27441_set_subcmd(client, SET_SOFT_RESET);
+		if (ret) {
+			pr_err("[bq27441] SET_SOFT_RESET failed\n");
+			goto ERROR; 
+		}
+
+		msleep(2000);
+		
+		ret = bq27441_get_regs(client, 0x6, data, 2);
+		flag = data[0] & 0x10;
+		if (flag) {
+			print_info("[bq27441] CFGUPDATE mode uclear! 0x%x%x,\n", ret ,data[0],data[1]);
+			goto ERROR; 
+		}
+
+	bq27441_config_check();
+
+	bq27441_set_subcmd(client, SEALED_CMD);
+	msleep(1000);
+	ERROR:
+	SetDuringConfig(0);
+	
+	return 0;
+}
+
+/*
+	check bq27441 init whether success
+*/
+static int bq27441_config_check(void)
+{
+	int ret, i, addr, tmp;
+	u8 data[2];
+	u16 dsg_cur, chg_cur, quit_cur;
+	struct i2c_client *client = gauge_client;
+	SetDuringConfig(1);
+	ret = bq27441_set_subcmd(client, CONTROL_STATUS);
+	ret |= bq27441_get_regs(client, 0x0, data, 2);
+	if (ret) {
+		pr_err("[bq27441] get control status register failed\n");
+		goto ERROR; 
+        }
+	print_info("[bq27441] CONTROL_STATUS: 0x%x%x\n", data[1],data[0]);
+
+	if (data[1] & 0x20) {
+		ret   = bq27441_set_reg(client, TYPE_8Bit, 0x00, 0x00);
+		ret   |= bq27441_set_reg(client, TYPE_8Bit, 0x01, 0x80);
+		ret   |= bq27441_set_reg(client, TYPE_8Bit, 0x00, 0x00);
+		ret   |= bq27441_set_reg(client, TYPE_8Bit, 0x01, 0x80);
+		if (ret) {
+			pr_err("[bq27441] set device usealed failed\n");
+			goto ERROR; 
+	        }
+	}
+
+	ret   = bq27441_set_reg(client, TYPE_8Bit, 0x00, 0x13);
+	msleep(1200);
+
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x24);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x59 failed\n");
+		goto ERROR; 
+	}
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		print_info("[GETSUM bq27441] Now, csum_blk(36)=0x%x\n", data[0]);
+	} else {
+		pr_err("[bq27441] get csum_blk(36) failed\n");
+		goto ERROR; 
+	}
+
+	ret = bq27441_get_regs(client, 0x47, data, 2);
+	if (!ret) {
+		print_info("[bq27441] Now, DODatEOC Delta T=0x%x\n", ((data[1]<<8) |data[0]));
+	} else {
+		pr_err("[bq27441] get DODatEOC Delta T failed\n");
+	}
+
+	mdelay(50);
+
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x52);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x59 failed\n");
+		goto ERROR; 
+	}
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		print_info("[GETSUM bq27441] Now, csum_blk(82) = 0x%x\n", data[0]);
+	} else {
+		pr_err("[bq27441] get new csum_blk(82) failed\n");
+		goto ERROR; 
+	}
+
+	ret |= bq27441_get_regs(client, 0x40, data, 2);
+	pr_err("qmax_cell =%d", ((data[0]<<8) |data[1]));
+
+	ret |= bq27441_get_regs(client, 0x45, data, 1);
+	pr_err("load_sel =0x%x", data[0]);
+
+	ret |= bq27441_get_regs(client, 0x4A, data, 2);
+	pr_err("capacity =%d", ((data[0]<<8) |data[1]));
+
+	ret |= bq27441_get_regs(client, 0x4C, data, 2);
+	pr_err("design_energy =%d", ((data[0]<<8) |data[1]));
+
+	ret |= bq27441_get_regs(client, 0x50, data, 2);
+	pr_err("term_vol =%d", ((data[0]<<8) |data[1]));
+
+	ret |= bq27441_get_regs(client, 0x52, data, 2);
+	pr_err("term_vdatle =%d", ((data[0]<<8) |data[1]));
+
+	ret |= bq27441_get_regs(client, 0x5B, data, 2);
+	pr_err("taper_rate =%d", ((data[0]<<8) |data[1]));
+
+	ret |= bq27441_get_regs(client, 0x5D, data, 2);
+	pr_err("taper_vol =%d\n", ((data[0]<<8) |data[1]));
+
+	print_info("[bq27441] "); 
+	for (i=0; i <32;i++) {
+		bq27441_get_regs(client, (0x40+i), data, 1);
+		print_info("0x%x\t",data[0]);
+	}
+	print_info("\n");
+
+	mdelay(50);
+
+	ret   = bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x51);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x52 failed\n");
+		goto ERROR; 
+	}
+
+	mdelay(50);
+
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		print_info("[GETSUM bq27441] Now, csum_blk(81)= 0x%x\n", data[0]);
+	} else {
+		pr_err("[bq27441] get csum_blk(81) failed\n");
+		goto ERROR; 
+	}
+
+	ret = bq27441_get_regs(client, 0x40, data, 2);
+	dsg_cur = (data[1]<<8) |data[0];
+	
+	ret |= bq27441_get_regs(client, 0x42, data, 2);
+	chg_cur = (data[1]<<8) |data[0];
+	
+	ret |= bq27441_get_regs(client, 0x44, data, 2);
+	quit_cur = (data[1]<<8) |data[0];
+
+	pr_err("[bq27441] check: csum_blk(81): Dsg_cur=0x%x, Chg_cur=0x%x, Quit_cur=0x%x\n",
+		dsg_cur, chg_cur, quit_cur);
+
+	mdelay(50);
+
+	ret  = bq27441_set_reg(client, TYPE_8Bit, 0x61, 0x00);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3E, 0x59);
+	ret |= bq27441_set_reg(client, TYPE_8Bit, 0x3F, 0x00);
+	if (ret) {
+		pr_err("[bq27441] set first 32 bytes in block 0x51 failed\n");
+		goto ERROR; 
+	}
+
+	mdelay(50);
+	
+	ret = bq27441_get_regs(client, 0x60, data, 1);
+	if (!ret) {
+		print_info("[GETSUM bq27441] Now, csum_blk(89) = 0x%x\n", data[0]);
+	} else {
+		pr_err("[bq27441] get csum_third failed\n");
+		goto ERROR; 
+	}
+
+	print_info("NOW R_TABLE:\n[bq27441] ");
+	for (i = 0, addr = 0x40, tmp = 0; i < 15; i++, addr+=2) {
+		ret = bq27441_get_regs(client, addr, data, 2);
+		if (ret) {
+			pr_err("[bq27441] get old config blk(89) failed\n");
+			goto ERROR; 
+		}
+		tmp += data[0] + data[1];
+		ret = (data[0]<<8) + data[1];
+		print_info("%d\t", ret);
+	}
+	print_info("\n");
+	
+	ERROR:
+	
+	SetDuringConfig(0);
+
+	return -EAGAIN;
+}
+
+//check bq27441 init whether success
+static ssize_t bq27441_csum_check(struct device *dev, struct device_attribute *attr,
+									 const char *buf, size_t count)
+{
+	bq27441_config_check();
+
+	bq27441_set_subcmd(gauge_client, SEALED_CMD);
+
+	return count;
+}
+
+static ssize_t store_debug_open(struct device *dev, struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+	unsigned int status=0;
+	unsigned short tmp = 0;
+	char *end_ptr;
+	 
+	status = simple_strtoul(buf, &end_ptr, 16);
+	if ((buf == end_ptr) || (status > 0x1))
+	{
+	    print_info("\n error at %s %d", __FUNCTION__, __LINE__);
+	    goto out;
+	}
+	
+	if(status != 0)
+		open_debug = 1;
+	else
+		open_debug = 0;
+
+	print_info("\n %s status:0x%x, open_debug = 0x%x\n", __FUNCTION__, status, open_debug);
+
+out:
+	return count;    
+}
+
+static ssize_t store_save_open(struct device *dev, struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+	unsigned int status=0;
+	unsigned short tmp = 0;
+	char *end_ptr;
+
+	status = simple_strtoul(buf, &end_ptr, 16);
+	if ((buf == end_ptr) || (status > 0x1))
+	{
+	    print_info("\n error at %s %d", __FUNCTION__, __LINE__);
+	    goto out;
+	}
+	
+	if(status != 0)
+		open_save = 1;
+	else
+		open_save = 0;
+
+	print_info("\n %s status:0x%x, open_save = 0x%x\n", __FUNCTION__, status, open_save);
+
+out:
+	return count;    
+}
+
+/*
+static struct device_attribute bq27441_attrs[] = {
+	__ATTR(debug_open, 0644, NULL, store_debug_open),
+	__ATTR(save_open, 0644, NULL, store_save_open),
+	__ATTR(check_config, 0644, NULL, bq27441_csum_check),
+};
+*/ 
+static int bq27441_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int flags, ret, i, device_version, updata_flag, len, act_cap;
+	u8 data[2];
+	struct device_node *node;
+
+	printk("[bq27441] %s is probing .......  \n", __func__);
+	
+	gauge_client = client;
+	node = client->dev.of_node;
+	print_info("[bq27441] probe in, address=0x%0x\n",  client->addr);
+	ret = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA  | I2C_FUNC_SMBUS_READ_BYTE);
+	if ( !ret ) {
+		ret = -EFAULT;
+		print_info("i2c_check_functionality err\n");
+		goto error;
+	}
+	
+	ti_updataflag_config = get_first_product();
+	store_first_product();
+	
+	/*check bq27441 device type*/
+	ret = bq27441_set_subcmd(client, DEVICE_TYPE);
+	
+	ret |= bq27441_get_regs(client, 0x0, data, 2);
+	if (ret) {
+		pr_err("[bq27441] get device type register failed\n");
+		goto error;
+      }
+		
+	if ((data[0] != 0x21) || (data[1] != 0x4)) {
+		pr_err("[bq27441] ID error! ID= 0x%x%x\n",data[1],data[0]);
+		//goto error;
+	} else {
+		print_info("[bq27441] dect success!\n");
+	}
+
+#if 0
+	#ifdef TI_CONFIG_UPDATAFLAG
+		updata_flag = TI_CONFIG_UPDATAFLAG;
+	#else
+		updata_flag = 0;
+	#endif
+#endif
+
+	updata_flag = ti_updataflag_config;
+	
+	pr_err("updata_flag = %d\n", updata_flag);
+
+
+	ret = bq27441_get_regs(gauge_client, 0x06, data, 2);
+	if (ret) {
+		pr_err("[bq27441] probe get flags failed\n");
+		goto error;
+        }	
+	
+     //ret = bq27441_check_updata(gauge_client,0xd3,0xb9,0x5e,0x33);
+	//if(ret == 1) {
+		//updata_flag = 1;
+	//	print_info("need to updata \n");
+	//}
+
+	flags = ((data[0]) | data[1]<<8);
+	 
+	if ((flags & ITPOR) || (1 == updata_flag)) {
+		if(flags & ITPOR)
+			printk(KERN_ERR "[bq27441][RECONFIG] TI REG ITOP NEED TO RESET \n");
+		if(updata_flag == 1 )
+			printk(KERN_ERR "[bq27441][RECONFIG] SYSTEM SET TO RESET \n");
+		
+		printk(KERN_ERR "[bq27441] first config!\n");
+		ret = bq27441_config(client);
+		if ( ret < 0) {
+			printk("[bq27441] first config failed\n");
+			ret = bq27441_config(client);
+			if ( ret < 0) {
+				printk("[bq27441] second config failed\n");
+				ret = bq27441_config(client);
+				if ( ret < 0) {
+					printk("[bq27441] third config failed\n");
+					goto error;
+				}
+			}
+		}
+
+	} else {
+		printk("[bq27441] ITPOR = 0\n");
+	}
+	
+	PASS:
+
+	
+#ifdef DEPEND
+	/*ע\B2\E1\B5\E7Դģ\BF\E9\BDӿ\DA*/
+	print_info("[bq27441] call back function register. \n");
+	act260x_set_get_hw_cap_point((void *)bq27441_get_capacity);
+	act260x_set_get_hw_volt_point((void *)bq27441_get_real_voltage);
+	act260x_set_get_hw_cur_point((void *)bq27441_get_real_current);
+	act260x_set_get_hw_temp_point((void *)bq27441_get_real_temperature);
+	//act260x_set_get_detect_for_hd_point((void *)bq27441_check_error);
+#endif
+/*
+	for (i = 0; i < ARRAY_SIZE(bq27441_attrs); i++) {
+		ret = device_create_file(&(client->dev), &bq27441_attrs[i]);
+		if (ret)
+			return ret;
+	}*/
+
+	do {
+		act_cap = bq27441_get_real_capacity();
+		if (act_cap < 0 || act_cap >100) {
+			pr_err("[bq27441] get init cap failed!\n");
+			continue;
+		} else
+			break;
+	} while(1);
+#if 0
+	if (act_cap > 85) {
+                cap_last_time = 90 + (act_cap - 85)*2/3;
+        } else if (act_cap > 60) {
+                cap_last_time = 60 + (act_cap - 60)*6/5;
+        } /*else if (act_cur < 0 && bat_vol <= 3650 && bat_low_cap) {
+			vir_cap = (bat_vol -3500) * bat_low_cap/(bat_low_vol - 3500);
+	} */ else {
+		cap_last_time = act_cap;
+	}
+#endif
+	//pr_err("[bq27441] cap_last_time = %d\n", cap_last_time);
+
+	return 0;
+
+error:
+	return ret;
+}
+
+static int bq27441_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	return 0;
+}
+
+static int bq27441_resume(struct i2c_client *client)
+{
+	int act_cap;
+
+	do {
+		act_cap = bq27441_get_real_capacity();
+		if (act_cap < 0 || act_cap >100) {
+			pr_err("[bq27441] get init cap failed!\n");
+			continue;
+		} else
+			break;
+	} while(1);
+#if 0
+	if (act_cap > 85) {
+                cap_last_time = 90 + (act_cap - 85)*2/3;
+        } else if (act_cap > 60) {
+                cap_last_time = 60 + (act_cap - 60)*6/5;
+        } /*else if (act_cur < 0 && bat_vol <= 3650 && bat_low_cap) {
+			vir_cap = (bat_vol -3500) * bat_low_cap/(bat_low_vol - 3500);
+	} */ else {
+		cap_last_time = act_cap;
+	}
+#endif
+	//pr_err("[bq27441] return from str, cap_last_time = %d,  act_cap =%d\n", cap_last_time, act_cap);
+	return 0;
+}
+
+static struct i2c_device_id bq27441_id[] = {
+    {"bq27441", 0},
+    {},
+};
+MODULE_DEVICE_TABLE(i2c, bq27441_id);
+
+static const struct of_device_id atc260x_bq27441_match[] = {
+	{ .compatible = "actions,bq27441", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_bq27441_match);
+
+static struct i2c_client * pClient = NULL;
+
+static struct i2c_board_info bq27441_device = {
+    .type	= "bq27441",
+	.addr   = 0x55,
+};
+
+//show volt from ba27441 @/sys/HW_GAUGE/
+static ssize_t show_VoltageHW(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	ret=bq27441_get_real_voltage();
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_VoltageHW(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//show volt from atc260x @/sys/HW_GAUGE/
+static ssize_t show_VoltagePMU(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	ret=get_pmu_battery_volatge();
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_VoltagePMU(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//show cap from bq27441 @/sys/HW_GAUGE/
+static ssize_t show_Capacity(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	ret=bq27441_get_real_capacity();
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_Capacity(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//show current temp  @/sys/HW_GAUGE/
+static ssize_t show_Temperature(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	ret=bq27441_get_real_temperature();
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_Temperature(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//show i2c transfer error @/sys/HW_GAUGE/
+static ssize_t show_ErrorCheck(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	ret=bq27441_check_error();
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_ErrorCheck(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//dump all regiest for bq27441 @/sys/HW_GAUGE/
+static ssize_t show_DumpReg(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	int data[30];
+	memset(data,0,30*4);
+	bq27441_get_regs(gauge_client, 0x00, data, 30);
+
+	return sprintf(buf, "Ctl:%x Tem:%x Tem:%x Vol:%x Flg:%x NACap:%x FACap:%x RCap:%x FCap:%x ACur:%x SCur:%x MLCur:%x APow:%x SOChg:%x ITem:%x SOH:%x \n"
+										, (data[0]|data[1]<<8),(data[2]|data[3]<<8),(data[4]|data[5]<<8)
+										,(data[6]|data[7]<<8),(data[8]|data[9]<<8),(data[10]|data[11]<<8)
+										,(data[12]|data[13]<<8),(data[14]|data[15]<<8),(data[16]|data[17]<<8)
+										,(data[18]|data[19]<<8),(data[20]|data[21]<<8),(data[22]|data[23]<<8)
+										,(data[24]|data[25]<<8),(data[26]|data[27]<<8),(data[28]|data[29]<<8)
+										); 
+}
+
+static ssize_t store_DumpReg(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//reinit bq27441 @/sys/HW_GAUGE/
+static ssize_t show_ReConfig(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_ReConfig(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+	unsigned int status=0;
+	unsigned short tmp = 0;
+	char *end_ptr;
+
+	status = simple_strtoul(buf, &end_ptr, 16);
+	if ((buf == end_ptr) || (status > 0x1))
+	{
+	    print_info("\n error at %s %d", __FUNCTION__, __LINE__);
+	    goto out;
+	}
+	
+	if(status != 0)
+		bq27441_config(gauge_client);
+		
+	out:
+
+    	return count;
+	
+}
+
+//show current @/sys/HW_GAUGE/
+static ssize_t show_Current(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=0;
+	ret=bq27441_get_real_current();
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_Current(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+    return count;
+}
+
+//enable save log to /data/bq27441_info.log @/sys/HW_GAUGE/
+static ssize_t show_SetDebugLog(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=open_save;
+	
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_SetDebugLog(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+
+	unsigned int status=0;
+	unsigned short tmp = 0;
+	char *end_ptr;
+
+	status = simple_strtoul(buf, &end_ptr, 16);
+	if ((buf == end_ptr) || (status > 0x1))
+	{
+	    print_info("\n error at %s %d", __FUNCTION__, __LINE__);
+	    goto out;
+	}
+	
+	if(status != 0)
+		open_save = 1;
+	else
+		open_save = 0;
+	
+	out:
+    	return count;
+}
+
+//for test @/sys/HW_GAUGE/
+static ssize_t show_SetError(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=setError;
+	
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_SetError(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+	unsigned int status=0;
+	unsigned short tmp = 0;
+	char *end_ptr;
+
+	status = simple_strtoul(buf, &end_ptr, 16);
+	if ((buf == end_ptr) || (status > 0x1))
+	{
+	    print_info("\n error at %s %d", __FUNCTION__, __LINE__);
+	    goto out;
+	}
+	
+	if(status != 0)
+		setError = 1;
+	else
+		setError = 0;
+	
+	out:
+    	return count;
+}
+
+//for test @/sys/HW_GAUGE/
+static ssize_t show_SetTemp(struct device *dev, struct device_attribute *attr, char *buf, size_t count) 
+{
+	int ret=testTemp;
+	
+	return sprintf(buf, "%d\n", ret); 
+}
+
+static ssize_t store_SetTemp(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+
+	unsigned int status=0;
+	unsigned short tmp = 0;
+	char *end_ptr;
+
+	status = simple_strtoul(buf, end_ptr, 10);
+	if ((buf == end_ptr)/* || (status > 0x1)*/)
+	{
+	    print_info("\n error at %s %d", __FUNCTION__, __LINE__);
+	    goto out;
+	}
+	
+	testTemp = status;
+
+	out:
+    	return count;
+}
+
+
+
+static DEVICE_ATTR(ErrorCheck, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+       show_ErrorCheck, store_ErrorCheck);
+static DEVICE_ATTR(Capacity, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_Capacity,store_Capacity);
+static DEVICE_ATTR(VoltageHW, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_VoltageHW, store_VoltageHW);
+static DEVICE_ATTR(VoltagePMU, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_VoltagePMU, store_VoltagePMU);
+static DEVICE_ATTR(DumpReg, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_DumpReg, store_DumpReg);
+static DEVICE_ATTR(ReConfig, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_ReConfig, store_ReConfig);
+static DEVICE_ATTR(SetDebugLog, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_SetDebugLog, store_SetDebugLog);
+static DEVICE_ATTR(Temperature, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_Temperature, store_Temperature);
+static DEVICE_ATTR(SetError, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_SetError, store_SetError);
+static DEVICE_ATTR(SetTemp, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_SetTemp, store_SetTemp);
+static DEVICE_ATTR(Current, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+        show_Current, store_Current);
+
+static struct attribute *hd_gauge_attributes[] = {
+    &dev_attr_ErrorCheck.attr,
+    &dev_attr_VoltageHW.attr,
+    &dev_attr_Capacity.attr,
+    &dev_attr_Temperature.attr,
+    &dev_attr_VoltagePMU.attr,
+    &dev_attr_DumpReg.attr,
+    &dev_attr_ReConfig.attr,
+    &dev_attr_SetDebugLog.attr,
+    &dev_attr_SetError.attr,
+    &dev_attr_SetTemp.attr,
+    &dev_attr_Current.attr,
+    NULL
+};
+
+static struct attribute_group hd_gauge_attribute_group = {
+    .attrs = hd_gauge_attributes
+};
+
+static int bq27441_remove(struct i2c_client *client)
+{
+	int i;
+/*
+	for (i = 0; i < ARRAY_SIZE(bq27441_attrs); i++) {
+		device_remove_file(&(client->dev), &bq27441_attrs[i]);
+	}
+*/
+#ifdef DEPEND
+	act260x_set_get_hw_cap_point((void *)NULL);
+	act260x_set_get_hw_volt_point((void *)NULL);
+	act260x_set_get_hw_cur_point((void *)NULL); 
+	act260x_set_get_hw_temp_point((void *)NULL);
+#endif
+
+	sysfs_remove_group(hw_gauge_kobj,&hd_gauge_attribute_group);
+	return 0;
+}
+
+static struct i2c_driver bq27441_driver = {
+	.driver = {
+	    .owner = THIS_MODULE,
+	    .name = "bq27441",
+	},
+
+	.class = I2C_CLASS_HWMON,
+	.probe = bq27441_probe,
+	.remove = bq27441_remove,
+	.suspend = bq27441_suspend,
+	.resume = bq27441_resume,
+	.id_table = bq27441_id,
+};
+
+static int bq27441_driver_init(void)
+{
+	int ret;
+	struct i2c_client *client = NULL;
+    struct i2c_adapter *adap;
+	printk("[bq27441] %s is start .......  \n", __func__);
+	adap = i2c_get_adapter(2);
+	hw_gauge_kobj = kobject_create_and_add("HW_GAUGE", NULL);  
+	if(hw_gauge_kobj == NULL){  
+		ret = -ENOMEM;  
+		return ret;
+	}  
+
+    	ret = sysfs_create_group(hw_gauge_kobj,&hd_gauge_attribute_group);
+	
+	print_info("[bq27441] %s version: %s, 2013-8-17\n", THIS_MODULE->name, THIS_MODULE->version);	
+   	pClient = i2c_new_device(adap, &bq27441_device); 
+	ret = i2c_add_driver(&bq27441_driver);
+	if (ret) {
+		print_info("i2c_add_driver err %d \n",ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void  bq27441_driver_exit(void)
+{
+	struct i2c_client *client = NULL;
+	printk("[bq27441] %s is exit .......  \n", __func__);
+	i2c_del_driver(&bq27441_driver);
+      i2c_unregister_device(pClient);
+}
+
+module_param(gauge, int, S_IRUGO);
+module_init(bq27441_driver_init);
+module_exit(bq27441_driver_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("semi-actions co.");
+MODULE_VERSION("1.0.0");
+#endif
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
old mode 100644
new mode 100755
index a6f116a..79b7880
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -728,7 +728,12 @@ config REGULATOR_WM831X
 	help
 	  Support the voltage and current regulators of the WM831x series
 	  of PMIC devices.
-
+config REGULATOR_ATC260X
+tristate "Actions Atc260x PMIC regulators"
+	depends on MFD_ATC260X
+	help
+	  Support the voltage and current regulators of the act260x series
+	  of PMIC devices.
 config REGULATOR_WM8350
 	tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC"
 	depends on MFD_WM8350
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
old mode 100644
new mode 100755
index 2c4da15..3be05a1
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -98,6 +98,8 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
 obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
 obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
 obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
-
+obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-pwm-dcdc.o
+obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o
+obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-switch-ldo.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/atc260x-pwm-dcdc.c b/drivers/regulator/atc260x-pwm-dcdc.c
new file mode 100755
index 0000000..ed50533
--- /dev/null
+++ b/drivers/regulator/atc260x-pwm-dcdc.c
@@ -0,0 +1,349 @@
+/*
+ * atc260x_dcdc.c  --  DCDC driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>    /* for udelay() */
+
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+#define CONFIG_ATC260x_PWM_DCDC_FULL_DBG 0
+#if CONFIG_ATC260x_PWM_DCDC_FULL_DBG
+#define dev_dbgl(DEV, FMT, ARGS...) dev_info(DEV, FMT, ## ARGS)
+#else
+#define dev_dbgl(DEV, FMT, ARGS...) do{}while(0)
+#endif
+
+#define TABLE_LEN_SIZE 16
+
+struct ext_pwm_dcdc_dev {
+	struct device *dev;
+	struct atc260x_dev *atc260x;
+	const char *name;
+	struct pwm_device *pwm_dev;
+	struct regulator_desc desc;
+	struct regulator_dev *regulator;
+
+	u32 pwm_config_table[TABLE_LEN_SIZE];
+	u32 vdd_cpu_table[TABLE_LEN_SIZE];
+	u32 table_len;
+	u32 cur_vdd_index;
+	uint auxadc_ch;
+};
+
+static int ext_pwm_dcdc_reconfig_pwm(struct ext_pwm_dcdc_dev *dcdc, uint voltage_index)
+{
+	uint pwm_level, pwm_active_time, pwm_period;
+	int ret;
+
+	pwm_period = pwm_get_period(dcdc->pwm_dev);
+	pwm_level = dcdc->pwm_config_table[voltage_index] +1;  /* DTS那边的表是预先减了1的. */
+	pwm_active_time = ((u64)pwm_period * pwm_level) / 64U; /* 取下整, 因为duty越低电压越高. */
+	ret = pwm_config(dcdc->pwm_dev, pwm_active_time, pwm_period);
+
+#if CONFIG_ATC260x_PWM_DCDC_FULL_DBG
+	dev_info(dcdc->dev, "%s() set pwm, pwm_level=%u active_time=%u period=%u ret=%d\n",
+			__func__, pwm_level, pwm_active_time, pwm_period, ret);
+#endif
+	return ret;
+}
+
+static int ext_pwm_dcdc_set_voltage(struct regulator_dev *rdev,
+					 int min_uV, int max_uV, unsigned *selector)
+{
+	struct ext_pwm_dcdc_dev *dcdc;
+	uint i;
+	int ret;
+
+	dcdc = rdev_get_drvdata(rdev);
+
+	if (min_uV < dcdc->vdd_cpu_table[0]) {
+		dev_err(dcdc->dev, "%s() set volt too low! volt=%d\n", __func__, min_uV);
+		min_uV = dcdc->vdd_cpu_table[0];
+	}
+	if (min_uV > dcdc->vdd_cpu_table[dcdc->table_len-1]) {
+		dev_err(dcdc->dev, "%s() set volt too high! volt=%d\n", __func__, min_uV);
+		min_uV = dcdc->vdd_cpu_table[dcdc->table_len-1];
+	}
+
+	for (i = 0; i < dcdc->table_len; i++) {
+		if (min_uV <= dcdc->vdd_cpu_table[i])
+			break;
+	}
+	dcdc->cur_vdd_index = i;
+	*selector = i;
+
+	ret = ext_pwm_dcdc_reconfig_pwm(dcdc, i);
+	if (ret == 0)
+		mdelay(3);  /*extern dc-dc need ~1ms stable time, wait for 3ms here. */
+
+	return ret;
+}
+
+static int ext_pwm_dcdc_get_voltage(struct regulator_dev *rdev)
+{
+	struct ext_pwm_dcdc_dev *dcdc;
+
+	dcdc = rdev_get_drvdata(rdev);
+
+#if 0
+	s32 real_volt;
+	int ret;
+	ret = atc260x_auxadc_get_translated(dcdc->atc260x, dcdc->auxadc_ch, &real_volt);
+	if (ret != 0) {
+		dev_err(dcdc->dev, "failed to convert real output voltage\n");
+		real_volt = 0;
+	}
+	return real_volt * 1000U;
+#endif
+
+	/* The regulator framework require to return the controll-value,
+	 * not the real value. */
+	return dcdc->vdd_cpu_table[dcdc->cur_vdd_index];
+}
+
+extern int owl_pwm_get_duty_cfg(struct pwm_device *pwm, uint *comparator_steps, uint *counter_steps);
+static int ext_pwm_dcdc_get_init_voltage_selector(struct ext_pwm_dcdc_dev *dcdc)
+{
+	uint i, comparator_steps, counter_steps, pwm_level;
+	int ret;
+
+	comparator_steps = 0;
+	counter_steps = 0;
+	ret = owl_pwm_get_duty_cfg(dcdc->pwm_dev, &comparator_steps, &counter_steps);
+	if (ret)
+		return ret;
+	if (counter_steps == 0)
+		return -EINVAL;
+
+	pwm_level = (64U * comparator_steps) / counter_steps;
+	pwm_level--; /* DTS那边的表是预先减了1的. */
+
+	for (i = 0; i < dcdc->table_len; i++) {
+		if (pwm_level == dcdc->pwm_config_table[i])
+			return i;
+	}
+
+	return -ENXIO;
+}
+
+static struct regulator_ops ext_pwm_dcdc_ops = {
+	.get_voltage = ext_pwm_dcdc_get_voltage,
+	.set_voltage = ext_pwm_dcdc_set_voltage,
+};
+
+static int ext_pwm_dcdc_probe(struct platform_device *pdev)
+{
+	struct ext_pwm_dcdc_dev *dcdc;
+	struct regulator_init_data  *init_data;
+	struct regulator_config config = { };
+	int ret, len;
+	uint __maybe_unused i;
+	struct device_node *node;
+	const __be32 *property;
+
+	dev_info(&pdev->dev, "Probing %s\n", pdev->name);
+
+	dcdc = devm_kzalloc(&pdev->dev, sizeof(struct ext_pwm_dcdc_dev), GFP_KERNEL);
+	if (dcdc == NULL) {
+		dev_err(&pdev->dev, "Unable to allocate private data\n");
+		return -ENOMEM;
+	}
+	dcdc->dev = &pdev->dev;
+	dcdc->atc260x = atc260x_get_parent_dev(&pdev->dev);
+	dcdc->name = "unknown";
+
+	platform_set_drvdata(pdev, dcdc);
+
+	node = pdev->dev.of_node;
+	property = of_get_property(node, "table_len", &len);
+	if (property && len == sizeof(int)) {
+		dcdc->table_len = (be32_to_cpup(property));
+		if (dcdc->table_len > TABLE_LEN_SIZE) {
+			dev_err(&pdev->dev, "table_len too leng \n");
+			dcdc->table_len = 3;
+		}
+	} else {
+		dev_err(&pdev->dev, "get table_len failed \n");
+		dcdc->table_len = 3;
+	}
+
+	dev_info(&pdev->dev, "table_len = %d\n", dcdc->table_len);
+	of_property_read_u32_array(node, "pwm_config_table", dcdc->pwm_config_table, dcdc->table_len);
+	of_property_read_u32_array(node, "vdd_cpu_table", dcdc->vdd_cpu_table, dcdc->table_len);
+#if CONFIG_ATC260x_PWM_DCDC_FULL_DBG
+	for (i = 0; i < dcdc->table_len; i++) {
+		dev_dbgl(&pdev->dev, "get pwm_config_table[%d]=%d\n", i, dcdc->pwm_config_table[i]);
+	}
+	for (i = 0; i < dcdc->table_len; i++) {
+		dev_dbgl(&pdev->dev, "get vdd_cpu_table[%d]=%d\n", i, dcdc->vdd_cpu_table[i]);
+	}
+#endif
+
+	dcdc->desc.name = dcdc->name;
+	dcdc->desc.type = REGULATOR_VOLTAGE;
+	dcdc->desc.n_voltages = dcdc->table_len;
+	dcdc->desc.ops = &ext_pwm_dcdc_ops;
+	dcdc->desc.owner = THIS_MODULE;
+	init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, &dcdc->desc);
+	if (!init_data)
+		return -ENOMEM;
+	init_data->constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
+	dcdc->name = init_data->constraints.name;
+
+	ret = atc260x_auxadc_find_chan(dcdc->atc260x, "AUX0");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to find auxadc channel: AUX0\n");
+		return -ENXIO;
+	}
+	dcdc->auxadc_ch = ret;
+
+	dcdc->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(dcdc->pwm_dev)) {
+		dev_err(&pdev->dev, "%s, unable to request pwm_device\n", __func__);
+		return -EAGAIN;
+	}
+	if (0 == dcdc->pwm_dev->period) {
+		dev_err(&pdev->dev, "%s, ext_dcdc_pwm->period is zero!\n", __func__);
+		return -EINVAL;
+	}
+	pwm_enable(dcdc->pwm_dev);
+
+	ret = ext_pwm_dcdc_get_init_voltage_selector(dcdc);
+	if (ret >= 0) {
+		dcdc->cur_vdd_index = ret;
+		dev_info(&pdev->dev, "got init voltage selector %d\n", ret);
+	} else {
+		dcdc->cur_vdd_index = 0;
+		dev_info(&pdev->dev, "failed to get init voltage selector, assum 0\n");
+	}
+
+	config.dev = &pdev->dev;
+	config.init_data = init_data;
+	config.driver_data = dcdc;
+	config.of_node = pdev->dev.of_node;
+
+	dcdc->regulator = regulator_register(&dcdc->desc, &config);
+	if (IS_ERR(dcdc->regulator)) {
+		ret = PTR_ERR(dcdc->regulator);
+		dev_err(&pdev->dev, "Failed to register VDD_CPU_DCDC: %d\n", ret);
+		return -EAGAIN;
+	}
+
+#if CONFIG_ATC260x_PWM_DCDC_FULL_DBG && defined(CONFIG_REGULATOR_VIRTUAL_CONSUMER)
+	/* for debug */
+	platform_device_register_resndata(NULL, "reg-virt-consumer",
+		40 + pdev->id,
+		NULL, 0,
+		dcdc->name, strlen(dcdc->name) + 1);
+#endif
+	return 0;
+}
+
+static int ext_pwm_dcdc_remove(struct platform_device *pdev)
+{
+	struct ext_pwm_dcdc_dev *dcdc = platform_get_drvdata(pdev);
+
+	pwm_disable(dcdc->pwm_dev);
+	devm_pwm_put(&pdev->dev, dcdc->pwm_dev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	regulator_unregister(dcdc->regulator);
+
+	return 0;
+}
+
+static int ext_pwm_dcdc_suspend_late(struct device *dev)
+{
+	struct ext_pwm_dcdc_dev * __maybe_unused dcdc = dev_get_drvdata(dev);
+	dev_dbgl(dcdc->dev, "%s() selector=%d cur_voltage=%uuV\n",
+		__func__,
+		dcdc->cur_vdd_index, dcdc->vdd_cpu_table[dcdc->cur_vdd_index]);
+	return 0;
+}
+
+static int ext_pwm_dcdc_resume_early(struct device *dev)
+{
+	struct ext_pwm_dcdc_dev *dcdc = dev_get_drvdata(dev);
+	int ret = ext_pwm_dcdc_reconfig_pwm(dcdc, dcdc->cur_vdd_index);
+	if (ret) {
+		dev_err(dcdc->dev, "%s() failed to restore seletor %u, ret=%d\n",
+			__func__, dcdc->cur_vdd_index, ret);
+		return ret;
+	}
+	dev_dbgl(dcdc->dev, "restore seletor: %u\n", dcdc->cur_vdd_index);
+	return 0;
+}
+
+static const struct dev_pm_ops s_ext_pwm_dcdc_pm_ops = {
+	.suspend_late  = ext_pwm_dcdc_suspend_late,
+	.resume_early  = ext_pwm_dcdc_resume_early,
+	.freeze_late   = ext_pwm_dcdc_suspend_late,
+	.thaw_early    = ext_pwm_dcdc_resume_early,
+	.poweroff_late = ext_pwm_dcdc_suspend_late,
+	.restore_early = ext_pwm_dcdc_resume_early,
+};
+
+static const struct of_device_id ext_pwm_dcdc_match[] = {
+	{ .compatible = "actions,atc2603a-ext-pwm-dcdc", },
+	{ .compatible = "actions,atc2603c-ext-pwm-dcdc", },
+	{ .compatible = "actions,atc2609a-ext-pwm-dcdc", },
+	{ .compatible = "actions,atc260x-ext-pwm-dcdc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ext_pwm_dcdc_match);
+
+static struct platform_driver ext_pwm_dcdc_driver = {
+	.driver     = {
+		.name   = "atc260x-ext-pwm-dcdc",
+		.owner  = THIS_MODULE,
+		.pm     = &s_ext_pwm_dcdc_pm_ops,
+		.of_match_table = of_match_ptr(ext_pwm_dcdc_match),
+	},
+	.probe =  ext_pwm_dcdc_probe,
+	.remove = ext_pwm_dcdc_remove,
+};
+
+static int __init atc260x_ext_pwm_dcdc_init(void)
+{
+	int ret;
+	ret = platform_driver_register(&ext_pwm_dcdc_driver);
+	if (ret != 0)
+		pr_err("%s() Failed to register driver: %d\n", __func__, ret);
+	return ret;
+}
+subsys_initcall(atc260x_ext_pwm_dcdc_init);
+
+static void __exit atc260x_ext_pwm_dcdc_exit(void)
+{
+	platform_driver_unregister(&ext_pwm_dcdc_driver);
+}
+module_exit(atc260x_ext_pwm_dcdc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("External PWM DCDC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-pwm-dcdc");
diff --git a/drivers/regulator/atc260x-regulator.c b/drivers/regulator/atc260x-regulator.c
new file mode 100755
index 0000000..93ee68b
--- /dev/null
+++ b/drivers/regulator/atc260x-regulator.c
@@ -0,0 +1,954 @@
+/*
+ * atc260x_regulator.c  --  regulator driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#define CONFIG_ATC260x_REGU_FULL_DBG 0
+#if CONFIG_ATC260x_REGU_FULL_DBG
+#define dev_dbgl(DEV, FMT, ARGS...) dev_info(DEV, FMT, ## ARGS)
+#else
+#define dev_dbgl(DEV, FMT, ARGS...) do{}while(0)
+#endif
+
+#define REGULATOR_MEM_STATE_MODE_NO_RESTORE     BIT(0)
+
+struct atc260x_regu_volt_band {
+	u32 volt_max_uv;
+	u32 volt_min_uv;
+	u32 volt_step_uv;
+	u16 vsel_start;
+	u16 vsel_end;
+};
+
+struct atc260x_regulator_dev;
+struct atc260x_regu_hwinfo;
+typedef int (*atc260x_regu_volt_lookup_func_t)(struct atc260x_regulator_dev *atc260x_regu,
+		const struct atc260x_regu_hwinfo *hwinfo, uint band, uint index);
+
+struct atc260x_regu_hwinfo {
+	atc260x_regu_volt_lookup_func_t nonlinear_lookup;
+
+	struct atc260x_regu_volt_band volt_bands[2];
+	uint    volt_band_exclusive;
+
+	u16     ctrl_reg_addr;
+	u16     ctrl_en_bm;
+	u16     ctrl_bypass_bm;
+
+	u16     mode_reg_addr;
+	u16     mode_bm;
+	u16     mode_bv_auto;
+	u16     mode_bv_pwm;
+	u16     mode_bv_pfm;
+
+	u16     vsel_reg_addr;
+	u16     vsel_volt_bm;
+
+	u16     vsel_band_reg_addr;
+	u16     vsel_band_bm;
+
+	u16     state_ov_reg_addr;
+	u16     state_uv_reg_addr;
+	u16     state_oc_reg_addr;
+	u16     state_ov_bm;
+	u16     state_uv_bm;
+	u16     state_oc_bm;
+
+	u32     stable_time_on;
+	u32     stable_time_chng_volt;
+
+	u32     valid_modes_mask;       /* REGULATOR_MODE_NORMAL ... */
+	u32     valid_ops_mask;         /* REGULATOR_CHANGE_VOLTAGE ... */
+};
+
+struct atc260x_regulator_dev {
+	struct device *dev;
+	struct atc260x_dev *atc260x;
+
+	const struct atc260x_regu_hwinfo *regu_hwinfo;
+	struct regulator_desc regulator_desc;
+	struct regulator_dev *regulator;
+	const char *name;
+	uint regu_id;
+	uint selected_volt_band;
+	uint suspend_save_state;    /* state before suspend */
+	uint suspend_save_selector; /* selector before suspend */
+};
+
+
+/* bring in the hwinfo structures */
+#define __MFD_ATC260X_REGULATOR__NEED_HWINFO_DEFINE__ 1
+#include "atc260x-regulator.h"
+#undef __MFD_ATC260X_REGULATOR__NEED_HWINFO_DEFINE__
+
+
+
+static int _atc260x_regulator_set_volt_band(struct atc260x_regulator_dev *atc260x_regu,
+		uint sel_band)
+{
+	struct atc260x_dev *atc260x = atc260x_regu->atc260x;
+	const struct atc260x_regu_hwinfo *hwinfo = atc260x_regu->regu_hwinfo;
+	int ret;
+
+	BUG_ON(hwinfo->vsel_band_reg_addr == 0 || hwinfo->vsel_band_bm == 0);
+	dev_info(atc260x_regu->dev, "attemp to change volt band to #%u\n", sel_band);
+
+	ret = atc260x_reg_setbits(atc260x,
+		hwinfo->vsel_band_reg_addr, hwinfo->vsel_band_bm,
+		(sel_band << __ffs(hwinfo->vsel_band_bm)) & hwinfo->vsel_band_bm);
+	if (ret) {
+		dev_err(atc260x_regu->dev, "%s() failed, reg=0x%x bm=0x%x ret=%d\n",
+			__func__, hwinfo->vsel_band_reg_addr, hwinfo->vsel_band_bm, ret);
+	}
+	return ret;
+}
+
+static int _atc260x_regulator_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	const struct atc260x_regu_volt_band *volt_band;
+	uint i, result;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+
+	/* check hwinfo valid */
+	if (hwinfo->ctrl_reg_addr == 0) {
+		dev_err(atc260x_regu->dev,
+			"You are going to use a regulator (%s) w/o valid hwinfo structure, "
+			"this is a BUG in the dts file!\n",
+			atc260x_regu->name);
+		BUG();
+	}
+
+	/* try lookup func first */
+	if (hwinfo->nonlinear_lookup != NULL) {
+		ret = (hwinfo->nonlinear_lookup)(
+			atc260x_regu, hwinfo,
+			atc260x_regu->selected_volt_band, selector);
+		if (ret < 0) {
+			dev_err(atc260x_regu->dev, "%s() call nonlinear_lookup err, ret=%d\n",
+				__func__, ret);
+			return ret;
+		}
+		result = ret;
+	}
+	else {
+		/* try linear band. */
+		if (hwinfo->volt_band_exclusive) {
+			BUG_ON(hwinfo->volt_band_exclusive >= ARRAY_SIZE(hwinfo->volt_bands));
+			volt_band = &(hwinfo->volt_bands[atc260x_regu->selected_volt_band]);
+		} else {
+			volt_band = NULL;
+			for (i = 0; i < ARRAY_SIZE(hwinfo->volt_bands); i++) {
+				if (hwinfo->volt_bands[i].volt_max_uv == 0) /* not used? */
+					break;
+				if (selector >= hwinfo->volt_bands[i].vsel_start &&
+					selector <= hwinfo->volt_bands[i].vsel_end) {
+					volt_band = &(hwinfo->volt_bands[i]);
+					break;
+				}
+			}
+		}
+		if (volt_band == NULL) {
+			dev_err(atc260x_regu->dev, "%s() selector %u not match\n",
+				__func__, selector);
+			return -EINVAL;
+		}
+
+		result = volt_band->volt_min_uv +
+				(selector - volt_band->vsel_start) * volt_band->volt_step_uv;
+	}
+
+	dev_dbgl(atc260x_regu->dev, "%s() selector %u --> voltage %uuV\n",
+		__func__, selector, result);
+	return result;
+}
+
+static int _atc260x_regulator_get_vchng_stable_time(struct regulator_dev *rdev,
+					 unsigned int old_selector, unsigned int new_selector)
+{
+	struct atc260x_regulator_dev *atc260x_regu = rdev_get_drvdata(rdev);
+	int old_volt, new_volt;
+
+	old_volt = (atc260x_regu->regulator_desc.ops->list_voltage)(rdev, old_selector);
+	if (old_volt < 0)
+		return old_volt; /* error code */
+
+	new_volt = (atc260x_regu->regulator_desc.ops->list_voltage)(rdev, new_selector);
+	if (new_volt < 0)
+		return new_volt; /* error code */
+
+	return (new_volt > old_volt) ?
+		atc260x_regu->regu_hwinfo->stable_time_chng_volt : 0;
+}
+
+static int _atc260x_regulator_get_hw_status(struct atc260x_regulator_dev *atc260x_regu,
+	u8 *p_on, u8 *p_bypass, u8 *p_oc, u8 *p_ov, u8 *p_uv)
+{
+	const struct atc260x_regu_hwinfo *hwinfo;
+	u8  *state_result_list[3];
+	uint state_reg_list[3];
+	u16  state_reg_bm_list[3];
+	uint i, ctrl_reg_val;
+	int ret;
+
+	hwinfo = atc260x_regu->regu_hwinfo;
+
+	if (p_on || p_bypass) {
+		ret = atc260x_reg_read(atc260x_regu->atc260x, hwinfo->ctrl_reg_addr);
+		if (ret < 0) {
+			dev_err(atc260x_regu->dev, "%s() read act260x reg %u err, ret=%u\n",
+					__func__, hwinfo->ctrl_reg_addr, ret);
+			return -EIO;
+		}
+		ctrl_reg_val = ret;
+
+		if (p_on) {
+			if (hwinfo->ctrl_en_bm == 0) {
+				*p_on = -1; /* always_on */
+			} else if (ctrl_reg_val & hwinfo->ctrl_en_bm) {
+				*p_on = 1; /* on */
+			} else {
+				*p_on = 0;
+			}
+		}
+		if (p_bypass) {
+			if (ctrl_reg_val & hwinfo->ctrl_bypass_bm) {
+				*p_bypass = 1;
+			} else {
+				*p_bypass = 0;
+			}
+		}
+	}
+
+	state_result_list[0]     = p_ov;
+	state_reg_list[0]        = hwinfo->state_ov_reg_addr;
+	state_reg_bm_list[0]     = hwinfo->state_ov_bm;
+	state_result_list[1]     = p_uv;
+	state_reg_list[1]        = hwinfo->state_uv_reg_addr;
+	state_reg_bm_list[1]     = hwinfo->state_uv_bm;
+	state_result_list[2]     = p_oc;
+	state_reg_list[2]        = hwinfo->state_oc_reg_addr;
+	state_reg_bm_list[2]     = hwinfo->state_oc_bm;
+	for (i = 0; i < ARRAY_SIZE(state_reg_bm_list); i++) {
+		if (state_result_list[i] == NULL)
+			continue;
+		if (state_reg_bm_list[i] != 0) {
+			ret = atc260x_reg_read(atc260x_regu->atc260x, state_reg_list[i]);
+			if (ret < 0) {
+				dev_err(atc260x_regu->dev, "%s() read act260x reg %u err, ret=%u\n",
+						__func__, state_reg_list[i], ret);
+				return -EIO;
+			}
+			*(state_result_list[i]) = ((uint)ret & state_reg_bm_list[i]) ? 1 : 0;
+		} else {
+			*(state_result_list[i]) = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int _atc260x_regulator_get_actual_status(struct regulator_dev *rdev)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	u8 state_on, state_bypass;
+	u8 state_oc, state_ov, state_uv;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+
+	ret = _atc260x_regulator_get_hw_status(atc260x_regu,
+		&state_on, &state_bypass, &state_oc, &state_ov, &state_uv);
+	if (ret) {
+		dev_err(atc260x_regu->dev, "%s() get HW state err, ret=%u\n",
+				__func__,ret);
+		return -EIO;
+	}
+	if (state_oc | state_ov | state_uv) {
+		return REGULATOR_STATUS_ERROR;
+	}
+	if (state_bypass) {
+		return REGULATOR_STATUS_BYPASS;
+	}
+	if (state_on) {
+		return REGULATOR_STATUS_ON;
+	}
+	return REGULATOR_STATUS_OFF;
+}
+
+static int _atc260x_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	uint reg_mask;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+	reg_mask = hwinfo->vsel_volt_bm;
+	if (reg_mask) {
+		ret = atc260x_reg_setbits(atc260x_regu->atc260x,
+			hwinfo->vsel_reg_addr,
+			reg_mask, (selector << __ffs(reg_mask)) & reg_mask);
+	} else {
+		/* fixed voltage, selector should be 0 */
+		ret = (selector == 0) ? 0 : -ENXIO;
+	}
+	dev_dbgl(atc260x_regu->dev, "set voltage_sel=%u, ret=%d\n", selector, ret);
+	return ret;
+}
+
+static int _atc260x_regulator_get_voltage_sel(struct regulator_dev *rdev)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	uint reg_mask;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+	reg_mask = hwinfo->vsel_volt_bm;
+	if (reg_mask) {
+		ret = atc260x_reg_read(atc260x_regu->atc260x, hwinfo->vsel_reg_addr);
+		if (ret >= 0) {
+			ret = ((uint)ret & reg_mask) >> __ffs(reg_mask);
+		}
+	} else {
+		/* fixed voltage, selector=0 */
+		ret = 0;
+	}
+	return ret;
+}
+
+static int _atc260x_regulator_enable_inner(struct regulator_dev *rdev, uint enable)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	uint reg_mask;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+	reg_mask = hwinfo->ctrl_en_bm;
+	if (reg_mask) {
+		ret = atc260x_reg_setbits(atc260x_regu->atc260x,
+			hwinfo->ctrl_reg_addr, reg_mask, (enable ? reg_mask : 0));
+	} else {
+		/* always on. */
+		ret = enable ? 0 : -ENXIO;
+	}
+	dev_dbgl(atc260x_regu->dev, "set enable=%u, ret=%d\n", enable, ret);
+	return ret;
+}
+static int _atc260x_regulator_enable(struct regulator_dev *rdev)
+{
+	return _atc260x_regulator_enable_inner(rdev, 1);
+}
+static int _atc260x_regulator_disable(struct regulator_dev *rdev)
+{
+	return _atc260x_regulator_enable_inner(rdev, 0);
+}
+
+static int _atc260x_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	uint reg_mask;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+	reg_mask = hwinfo->ctrl_en_bm;
+	if (reg_mask) {
+		ret = atc260x_reg_read(atc260x_regu->atc260x, hwinfo->ctrl_reg_addr);
+		if (ret >= 0) {
+			ret = (((uint)ret & reg_mask) != 0);
+		}
+	} else {
+		/* always on. */
+		ret = 1;
+	}
+	return ret;
+}
+
+static int _atc260x_regulator_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	uint reg_mask;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+	reg_mask = hwinfo->ctrl_bypass_bm;
+	if (reg_mask) {
+		ret = atc260x_reg_setbits(atc260x_regu->atc260x,
+			hwinfo->ctrl_reg_addr, reg_mask, (enable ? reg_mask : 0));
+	} else {
+		/* no such function. */
+		ret = enable ? -ENXIO : 0;
+	}
+	dev_dbgl(atc260x_regu->dev, "set bypass=%u, ret=%d\n", enable, ret);
+	return ret;
+}
+
+static int _atc260x_regulator_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+	struct atc260x_regulator_dev *atc260x_regu;
+	const struct atc260x_regu_hwinfo *hwinfo;
+	uint reg_mask;
+	int ret;
+
+	atc260x_regu = rdev_get_drvdata(rdev);
+	hwinfo = atc260x_regu->regu_hwinfo;
+	reg_mask = hwinfo->ctrl_bypass_bm;
+	if (reg_mask) {
+		ret = atc260x_reg_read(atc260x_regu->atc260x, hwinfo->ctrl_reg_addr);
+		if (ret >= 0) {
+			*enable = (((uint)ret & reg_mask) != 0);
+		}
+	} else {
+		/* no such function. */
+		*enable = 0;
+		ret = -ENXIO;
+	}
+	return ret;
+}
+
+static struct regulator_ops atc260x_regulator_linear_ops = {
+	/* use ourselfs' function */
+	.list_voltage       = regulator_list_voltage_linear,
+	/*.enable_time        = NULL, ** no need ** */
+	.set_voltage_time_sel = _atc260x_regulator_get_vchng_stable_time,
+
+	.set_mode           = NULL,
+	.get_mode           = NULL,
+	.get_optimum_mode   = NULL,
+	.get_status         = _atc260x_regulator_get_actual_status,
+
+	/* basic ops */
+	.set_voltage_sel    = _atc260x_regulator_set_voltage_sel,
+	.get_voltage_sel    = _atc260x_regulator_get_voltage_sel,
+	.enable             = _atc260x_regulator_enable,
+	.disable            = _atc260x_regulator_disable,
+	.is_enabled         = _atc260x_regulator_is_enabled,
+	.set_bypass         = _atc260x_regulator_set_bypass,
+	.get_bypass         = _atc260x_regulator_get_bypass,
+};
+
+static struct regulator_ops atc260x_regulator_banded_ops = {
+	/* use ourselfs' function */
+	.list_voltage       = _atc260x_regulator_list_voltage,
+	/*.enable_time        = NULL, ** no need ** */
+	.set_voltage_time_sel = _atc260x_regulator_get_vchng_stable_time,
+
+	.set_mode           = NULL,
+	.get_mode           = NULL,
+	.get_optimum_mode   = NULL,
+	.get_status         = _atc260x_regulator_get_actual_status,
+
+	/* basic ops */
+	.set_voltage_sel    = _atc260x_regulator_set_voltage_sel,
+	.get_voltage_sel    = _atc260x_regulator_get_voltage_sel,
+	.enable             = _atc260x_regulator_enable,
+	.disable            = _atc260x_regulator_disable,
+	.is_enabled         = _atc260x_regulator_is_enabled,
+	.set_bypass         = _atc260x_regulator_set_bypass,
+	.get_bypass         = _atc260x_regulator_get_bypass,
+};
+
+/* return : true / false */
+static int _atc260x_chk_constraints_vrang(struct regulation_constraints *cs,
+		uint v_min, uint v_max, uint correct)
+{
+	uint ret = 1;
+	if (correct) {
+		if (cs->min_uV < v_min) {
+			cs->min_uV = v_min;
+			ret = 0;
+		}
+		if (cs->min_uV > v_max) {
+			cs->min_uV = v_max;
+			ret = 0;
+		}
+		if (cs->max_uV < v_min) {
+			cs->max_uV = v_min;
+			ret = 0;
+		}
+		if (cs->max_uV > v_max) {
+			cs->max_uV = v_max;
+			ret = 0;
+		}
+	} else {
+		ret = (cs->min_uV < v_min) ? 0 : ret;
+		ret = (cs->min_uV > v_max) ? 0 : ret;
+		ret = (cs->max_uV < v_min) ? 0 : ret;
+		ret = (cs->max_uV > v_max) ? 0 : ret;
+	}
+	return ret;
+}
+
+static int _atc260x_init_regulator_desc(struct atc260x_regulator_dev *atc260x_regu,
+		struct regulator_desc *r_desc, struct regulator_config *r_cfg,
+		struct regulator_init_data  *init_data)
+{
+	static const struct atc260x_regu_hwinfo *  sc_atc260x_dcdc_hwinfo_tbl_pool[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = sc_atc2603a_dcdc_hwinfo_tbl,
+		[ATC260X_ICTYPE_2603C] = sc_atc2603c_ver_a_dcdc_hwinfo_tbl,
+		[ATC260X_ICTYPE_2609A] = sc_atc2609a_dcdc_hwinfo_tbl,
+	};
+	static  uint sc_atc260x_dcdc_hwinfo_tbl_len[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = ARRAY_SIZE(sc_atc2603a_dcdc_hwinfo_tbl),
+		[ATC260X_ICTYPE_2603C] = ARRAY_SIZE(sc_atc2603c_ver_a_dcdc_hwinfo_tbl),
+		[ATC260X_ICTYPE_2609A] = ARRAY_SIZE(sc_atc2609a_dcdc_hwinfo_tbl),
+	};
+	static const struct atc260x_regu_hwinfo * const sc_atc260x_ldo_hwinfo_tbl_pool[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = sc_atc2603a_ldo_hwinfo_tbl,
+		[ATC260X_ICTYPE_2603C] = sc_atc2603c_ldo_hwinfo_tbl,
+		[ATC260X_ICTYPE_2609A] = sc_atc2609a_ldo_hwinfo_tbl,
+	};
+	static const uint sc_atc260x_ldo_hwinfo_tbl_len[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = ARRAY_SIZE(sc_atc2603a_ldo_hwinfo_tbl),
+		[ATC260X_ICTYPE_2603C] = ARRAY_SIZE(sc_atc2603c_ldo_hwinfo_tbl),
+		[ATC260X_ICTYPE_2609A] = ARRAY_SIZE(sc_atc2609a_ldo_hwinfo_tbl),
+	};
+	struct atc260x_dev *atc260x;
+	const struct atc260x_regu_hwinfo *hwinfo_tbl, *hwinfo;
+	const struct atc260x_regu_volt_band *linear_volt_band;
+	uint hwinfo_tbl_len, selected_volt_band, total_n_volt;
+	uint ic_type;
+	int ret;
+
+	atc260x = atc260x_regu->atc260x;
+	ic_type = atc260x_get_ic_type(atc260x);
+	if((ic_type == ATC260X_ICTYPE_2603C) && (atc260x_get_ic_ver(atc260x) > 0) &&(atc260x_regu->regu_id == 2)) {	
+		init_data->constraints.min_uV = 1000000;
+		sc_atc260x_dcdc_hwinfo_tbl_pool[ATC260X_ICTYPE_2603C] = sc_atc2603c_ver_b_dcdc_hwinfo_tbl;
+		sc_atc260x_dcdc_hwinfo_tbl_len[ATC260X_ICTYPE_2603C] = ARRAY_SIZE(sc_atc2603c_ver_b_dcdc_hwinfo_tbl);
+	}
+	BUG_ON(ic_type >= ATC260X_ICTYPE_CNT);
+
+	if (strstr(atc260x_regu->name, "dcdc") != NULL) {
+		hwinfo_tbl = sc_atc260x_dcdc_hwinfo_tbl_pool[ic_type];
+		hwinfo_tbl_len = sc_atc260x_dcdc_hwinfo_tbl_len[ic_type];
+	}
+	else if (strstr(atc260x_regu->name, "ldo") != NULL) {
+		hwinfo_tbl = sc_atc260x_ldo_hwinfo_tbl_pool[ic_type];
+		hwinfo_tbl_len = sc_atc260x_ldo_hwinfo_tbl_len[ic_type];
+	}
+	else {
+		dev_err(atc260x_regu->dev, "%s() unknown type of regulator dev %s\n",
+			__func__, atc260x_regu->name);
+		return -ENXIO;
+	}
+	if (atc260x_regu->regu_id >= hwinfo_tbl_len) {
+		dev_err(atc260x_regu->dev, "%s() unknown regulator (%s) id %d\n",
+			__func__, atc260x_regu->name, atc260x_regu->regu_id);
+		return -ENXIO;
+	}
+	hwinfo = &(hwinfo_tbl[atc260x_regu->regu_id]);
+
+	atc260x_regu->regu_hwinfo = hwinfo;
+
+	/* handle voltage band */
+	if (hwinfo->volt_bands[1].volt_max_uv == 0) {
+		/* only 1 band */
+		/* apply a smaller range */
+		_atc260x_chk_constraints_vrang(&init_data->constraints,
+			hwinfo->volt_bands[0].volt_min_uv,
+			hwinfo->volt_bands[0].volt_max_uv, true);
+		linear_volt_band = &(hwinfo->volt_bands[0]);
+		selected_volt_band = 0;
+		total_n_volt = hwinfo->volt_bands[0].vsel_end -
+				hwinfo->volt_bands[0].vsel_start + 1U;
+	} else {
+		if (hwinfo->volt_band_exclusive == 0) {
+			_atc260x_chk_constraints_vrang(&init_data->constraints,
+				hwinfo->volt_bands[0].volt_min_uv,
+				hwinfo->volt_bands[1].volt_max_uv, true);
+			linear_volt_band = NULL;
+			selected_volt_band = 0;
+			total_n_volt = hwinfo->volt_bands[1].vsel_end -
+					hwinfo->volt_bands[0].vsel_start + 1U;
+		} else {
+			if (_atc260x_chk_constraints_vrang(&init_data->constraints,
+				hwinfo->volt_bands[0].volt_min_uv,
+				hwinfo->volt_bands[0].volt_max_uv, false))
+			{
+				linear_volt_band = &(hwinfo->volt_bands[0]);
+				selected_volt_band = 0;
+				total_n_volt = hwinfo->volt_bands[0].vsel_end -
+						hwinfo->volt_bands[0].vsel_start + 1U;
+			}
+			else if (_atc260x_chk_constraints_vrang(&init_data->constraints,
+				hwinfo->volt_bands[1].volt_min_uv,
+				hwinfo->volt_bands[1].volt_max_uv, false))
+			{
+				linear_volt_band = &(hwinfo->volt_bands[1]);
+				selected_volt_band = 1;
+				total_n_volt = hwinfo->volt_bands[1].vsel_end -
+						hwinfo->volt_bands[1].vsel_start + 1U;
+			}
+			else {
+				dev_err(atc260x_regu->dev, "%s() unmatched voltage rang [%u,%u]\n",
+					__func__,
+					init_data->constraints.min_uV, init_data->constraints.max_uV);
+				return -ENXIO;
+			}
+			ret = _atc260x_regulator_set_volt_band(atc260x_regu, selected_volt_band);
+			if (ret) {
+				return ret;
+			}
+		}
+	}
+
+	atc260x_regu->selected_volt_band = selected_volt_band;
+
+	init_data->constraints.valid_modes_mask |= hwinfo->valid_modes_mask;
+	init_data->constraints.valid_ops_mask |= hwinfo->valid_ops_mask;
+	if (hwinfo->ctrl_en_bm == 0) {
+		init_data->constraints.always_on = 1;
+		init_data->constraints.valid_ops_mask &= ~REGULATOR_CHANGE_STATUS;
+	}
+	if (init_data->constraints.min_uV == init_data->constraints.max_uV) {
+		init_data->constraints.apply_uV = 1;
+	}
+
+	r_cfg->dev = atc260x_regu->dev;
+	r_cfg->init_data = init_data;
+	r_cfg->driver_data = atc260x_regu;
+	r_cfg->of_node = atc260x_regu->dev->of_node;
+
+	r_desc->name = init_data->constraints.name;
+	r_desc->id = atc260x_regu->regu_id;
+	r_desc->type = REGULATOR_VOLTAGE;
+	r_desc->n_voltages = total_n_volt;
+	r_desc->ops = &atc260x_regulator_banded_ops;
+	r_desc->owner = THIS_MODULE;
+
+	if (linear_volt_band != NULL && hwinfo->nonlinear_lookup == NULL) {
+		r_desc->min_uV = linear_volt_band->volt_min_uv;
+		r_desc->uV_step = linear_volt_band->volt_step_uv;
+		r_desc->linear_min_sel = linear_volt_band->vsel_start;
+		r_desc->ops = &atc260x_regulator_linear_ops;
+	}
+
+	r_desc->enable_time = hwinfo->stable_time_on;
+
+	return 0;
+}
+
+static void _atc260x_dump_regulator_info(struct atc260x_regulator_dev *atc260x_regu,
+		struct regulator_init_data  *init_data)
+{
+#if CONFIG_ATC260x_REGU_FULL_DBG
+	uint i;
+	const struct atc260x_regu_hwinfo *hwinfo;
+
+	dev_info(atc260x_regu->dev, "dump regulator ----------------------------\n");
+	pr_info("  id: %u  name: %s\n", atc260x_regu->regu_id, atc260x_regu->name);
+	pr_info("  selected_volt_band: %u\n", atc260x_regu->selected_volt_band);
+
+	hwinfo = atc260x_regu->regu_hwinfo;
+	pr_info("  hwinfo :\n");
+	pr_info("    nonlinear_lookup : %lx\n", (ulong)(hwinfo->nonlinear_lookup));
+	for (i = 0 ;i < ARRAY_SIZE(hwinfo->volt_bands); i++) {
+		pr_info("    volt_bands_%u     : min %uuV  max %uuV  step %uuV  vsel_start %u  vsel_end %u\n",
+			i,
+			hwinfo->volt_bands[i].volt_min_uv, hwinfo->volt_bands[i].volt_max_uv,
+			hwinfo->volt_bands[i].volt_step_uv,
+			hwinfo->volt_bands[i].vsel_start, hwinfo->volt_bands[i].vsel_end);
+	}
+	pr_info("    volt_band_exclusive : %u\n", hwinfo->volt_band_exclusive);
+	pr_info("    ctrl_reg_addr    : 0x%x\n", hwinfo->ctrl_reg_addr);
+	pr_info("    ctrl_en_bm       : 0x%x\n", hwinfo->ctrl_en_bm);
+	pr_info("    ctrl_bypass_bm   : 0x%x\n", hwinfo->ctrl_bypass_bm);
+
+	pr_info("    mode_reg_addr    : 0x%x\n", hwinfo->mode_reg_addr);
+	pr_info("    mode_bm          : 0x%x\n", hwinfo->mode_bm);
+	pr_info("    mode_bv_auto     : 0x%x\n", hwinfo->mode_bv_auto);
+	pr_info("    mode_bv_pwm      : 0x%x\n", hwinfo->mode_bv_pwm);
+	pr_info("    mode_bv_pfm      : 0x%x\n", hwinfo->mode_bv_pfm);
+
+	pr_info("    vsel_reg_addr    : 0x%x\n", hwinfo->vsel_reg_addr);
+	pr_info("    vsel_volt_bm     : 0x%x\n", hwinfo->vsel_volt_bm);
+
+	pr_info("    vsel_band_reg_addr : 0x%x\n", hwinfo->vsel_band_reg_addr);
+	pr_info("    vsel_band_bm     : 0x%x\n", hwinfo->vsel_band_bm);
+
+	pr_info("    state_ov_reg_addr  : 0x%x\n", hwinfo->state_ov_reg_addr);
+	pr_info("    state_uv_reg_addr  : 0x%x\n", hwinfo->state_uv_reg_addr);
+	pr_info("    state_oc_reg_addr  : 0x%x\n", hwinfo->state_oc_reg_addr);
+	pr_info("    state_ov_bm        : 0x%x\n", hwinfo->state_ov_bm);
+	pr_info("    state_uv_bm        : 0x%x\n", hwinfo->state_uv_bm);
+	pr_info("    state_oc_bm        : 0x%x\n", hwinfo->state_oc_bm);
+
+	pr_info("    stable_time_on        : %uuS\n", hwinfo->stable_time_on);
+	pr_info("    stable_time_chng_volt : %uuS\n", hwinfo->stable_time_chng_volt);
+
+	pr_info("    valid_modes_mask : 0x%x\n", hwinfo->valid_modes_mask);
+	pr_info("    valid_ops_mask   : 0x%x\n", hwinfo->valid_ops_mask);
+
+	pr_info("  constraints :\n");
+	pr_info("    min_uV           : 0x%x\n", init_data->constraints.min_uV);
+	pr_info("    max_uV           : 0x%x\n", init_data->constraints.max_uV);
+	pr_info("    initial_mode     : 0x%x\n", init_data->constraints.initial_mode);
+	pr_info("    ramp_delay       : %u\n", init_data->constraints.ramp_delay);
+	pr_info("    always_on        : %u\n", init_data->constraints.always_on);
+	pr_info("    boot_on          : %u\n", init_data->constraints.boot_on);
+	pr_info("    apply_uV         : %u\n", init_data->constraints.apply_uV);
+	pr_info("    valid_modes_mask : 0x%x\n", init_data->constraints.valid_modes_mask);
+	pr_info("    valid_ops_mask   : 0x%x\n", init_data->constraints.valid_ops_mask);
+#endif
+}
+
+static int atc260x_regulator_probe(struct platform_device *pdev)
+{
+	struct regulator_config config = { };
+	struct regulator_init_data  *init_data;
+	struct atc260x_dev *atc260x;
+	struct atc260x_regulator_dev *atc260x_regu;
+	uint regu_id;
+	int ret;
+	u32 val;
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+	regu_id = pdev->id;
+
+	dev_info(&pdev->dev, "Probing %s, id=%u\n", pdev->name, regu_id);
+
+	init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, NULL);
+	if (!init_data)
+		return -ENOMEM;
+	/* get other stuff from DTS */
+	if (of_find_property(pdev->dev.of_node, "regulator-suspend-off", NULL)) {
+		init_data->constraints.state_standby.disabled = 1;
+		init_data->constraints.state_mem.disabled = 1;
+		init_data->constraints.state_disk.disabled = 1;
+	}
+
+	if (!of_property_read_u32(pdev->dev.of_node, "regulator-state-mode", &val)) {
+		init_data->constraints.state_standby.mode  = val;
+		init_data->constraints.state_mem.mode  = val;
+		init_data->constraints.state_disk.mode  = val;
+	}
+
+	atc260x_regu = devm_kzalloc(
+			&pdev->dev, sizeof(struct atc260x_regulator_dev), GFP_KERNEL);
+	if (atc260x_regu == NULL) {
+		dev_err(&pdev->dev, "Unable to allocate private data\n");
+		return -ENOMEM;
+	}
+	atc260x_regu->dev = &pdev->dev;
+	atc260x_regu->atc260x = atc260x;
+	atc260x_regu->regu_id = regu_id;
+	atc260x_regu->name = init_data->constraints.name;
+
+	/* init desc */
+	ret = _atc260x_init_regulator_desc(atc260x_regu,
+			&(atc260x_regu->regulator_desc), &config, init_data);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() failed to init regulator_desc, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+	_atc260x_dump_regulator_info(atc260x_regu, init_data);
+
+	atc260x_regu->regulator = regulator_register(
+			&(atc260x_regu->regulator_desc), &config);
+	if (IS_ERR(atc260x_regu->regulator)) {
+		ret = PTR_ERR(atc260x_regu->regulator);
+		dev_err(&pdev->dev, "%s() Failed to register regulator, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, atc260x_regu);
+
+#if CONFIG_ATC260x_REGU_FULL_DBG && defined(CONFIG_REGULATOR_VIRTUAL_CONSUMER)
+	/* for debug */
+	platform_device_register_resndata(NULL, "reg-virt-consumer",
+		(strstr(atc260x_regu->name, "dcdc") ? 0 : 10) + atc260x_regu->regu_id,
+		NULL, 0,
+		atc260x_regu->name, strlen(atc260x_regu->name) + 1);
+#endif
+	return 0;
+}
+
+static int atc260x_regulator_remove(struct platform_device *pdev)
+{
+	struct atc260x_regulator_dev *atc260x_regu = platform_get_drvdata(pdev);
+	platform_set_drvdata(pdev, NULL);
+	regulator_unregister(atc260x_regu->regulator);
+	return 0;
+}
+
+static int atc260x_regulator_print_suspend_state(
+		struct atc260x_regulator_dev *atc260x_regu, const char *prompt)
+{
+	u8 state_on, state_bypass;
+	u8 state_oc, state_ov, state_uv;
+	int ret;
+
+	ret = _atc260x_regulator_get_hw_status(atc260x_regu,
+		&state_on, &state_bypass, &state_oc, &state_ov, &state_uv);
+	if (ret) {
+		dev_err(atc260x_regu->dev, "state %s : <sw failed, ret=%d>\n",
+			prompt, ret);
+		return ret;
+	}
+	if (((state_on == 1) && (atc260x_regu->regulator->constraints->always_on == 0)) ||
+		(state_bypass|state_oc|state_ov|state_uv)) {
+		dev_warn(atc260x_regu->dev, "state %s : on=%u bypass=%u oc=%u ov=%u uv=%u\n",
+			prompt,
+			state_on, state_bypass, state_oc, state_ov, state_uv);
+	} else {
+		dev_info(atc260x_regu->dev, "state %s : on=%u bypass=%u oc=%u ov=%u uv=%u\n",
+			prompt,
+			state_on, state_bypass, state_oc, state_ov, state_uv);
+	}
+	return 0;
+}
+
+static int atc260x_regulator_suspend_late(struct device *dev)
+{
+	struct atc260x_regulator_dev *atc260x_regu = dev_get_drvdata(dev);
+	int ret;
+
+	ret = _atc260x_regulator_get_voltage_sel(atc260x_regu->regulator);
+	if (ret < 0) {
+		dev_err(dev, "%s() failed to save selector, ret=%d\n", __func__, ret);
+		return ret;
+	}
+	atc260x_regu->suspend_save_selector = ret;
+	dev_dbgl(dev, "save selector: %u\n", ret);
+
+	atc260x_regu->suspend_save_state = 0;
+	if (atc260x_regu->regulator->constraints->state_mem.disabled) {
+		if (_atc260x_regulator_is_enabled(atc260x_regu->regulator)) {
+			atc260x_regu->suspend_save_state = 1;
+			ret = _atc260x_regulator_disable(atc260x_regu->regulator);
+			if (ret) {
+				dev_err(dev, "%s() failed to disable, ret=%d\n", __func__, ret);
+				/* do not return error, keep going. */
+			}
+		}
+	}
+
+	return atc260x_regulator_print_suspend_state(atc260x_regu, "@suspend_late");
+}
+
+static int atc260x_regulator_resume_early(struct device *dev)
+{
+	struct atc260x_regulator_dev *atc260x_regu = dev_get_drvdata(dev);
+	int ret;
+
+	if ((atc260x_regu->regulator->constraints->state_mem.mode & REGULATOR_MEM_STATE_MODE_NO_RESTORE) == 0) {
+		ret = _atc260x_regulator_set_voltage_sel(
+			atc260x_regu->regulator, atc260x_regu->suspend_save_selector);
+		if (ret) {
+			dev_err(dev, "%s() failed to restore selector, ret=%d\n", __func__, ret);
+			return ret;
+		}
+		dev_dbgl(dev, "restore selector: %u\n", atc260x_regu->suspend_save_selector);
+	}
+
+	if (atc260x_regu->regulator->constraints->state_mem.disabled) {
+		if (atc260x_regu->suspend_save_state != 0) {
+			ret = _atc260x_regulator_enable(atc260x_regu->regulator);
+			if (ret) {
+				dev_err(dev, "%s() failed to enable, ret=%d\n", __func__, ret);
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+static void atc260x_regulator_shutdown(struct platform_device *pdev)
+{
+	atc260x_regulator_print_suspend_state(
+		platform_get_drvdata(pdev), "before shutdown");
+}
+
+static const struct dev_pm_ops s_atc260x_regulator_pm_ops = {
+	.suspend_late  = atc260x_regulator_suspend_late,
+	.resume_early  = atc260x_regulator_resume_early,
+	.freeze_late   = atc260x_regulator_suspend_late,
+	.thaw_early    = atc260x_regulator_resume_early,
+	.poweroff_late = atc260x_regulator_suspend_late,
+	.restore_early = atc260x_regulator_resume_early,
+};
+
+static const struct of_device_id atc260x_regulator_match[] = {
+	/* only for voltage ragulators. (DC-DC & LDO) */
+	{ .compatible = "actions,atc2603a-dcdc", },
+	{ .compatible = "actions,atc2603a-ldo", },
+	{ .compatible = "actions,atc2603c-dcdc", },
+	{ .compatible = "actions,atc2603c-ldo", },
+	{ .compatible = "actions,atc2609a-dcdc", },
+	{ .compatible = "actions,atc2609a-ldo", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_regulator_match);
+
+static struct platform_driver atc260x_regulator_driver = {
+	.driver     = {
+		.name   = "atc260x-regulator",
+		.owner  = THIS_MODULE,
+		.pm     = &s_atc260x_regulator_pm_ops,
+		.of_match_table = of_match_ptr(atc260x_regulator_match),
+	},
+	.probe = atc260x_regulator_probe,
+	.remove = atc260x_regulator_remove,
+	.shutdown = atc260x_regulator_shutdown,
+};
+
+static int __init atc260x_regulator_init(void)
+{
+	int ret;
+	ret = platform_driver_register(&atc260x_regulator_driver);
+	if (ret != 0) {
+		pr_err("%s(): failed to register ATC260X regulator driver, ret=%d\n",
+			__func__, ret);
+	}
+	return ret;
+}
+subsys_initcall(atc260x_regulator_init);
+
+static void __exit atc260x_regulator_exit(void)
+{
+	platform_driver_unregister(&atc260x_regulator_driver);
+}
+module_exit(atc260x_regulator_exit);
+
+/* Module information */
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("ATC260X regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-regulator");
diff --git a/drivers/regulator/atc260x-regulator.h b/drivers/regulator/atc260x-regulator.h
new file mode 100755
index 0000000..20b214d
--- /dev/null
+++ b/drivers/regulator/atc260x-regulator.h
@@ -0,0 +1,1606 @@
+/* This is ugly,
+ * but we need to move these long list away to make code clean and readable. */
+
+#ifdef __MFD_ATC260X_REGULATOR__NEED_HWINFO_DEFINE__
+
+#ifndef __MFD_ATC260X_REGULATOR_HWINFO_DEF_H__
+#define __MFD_ATC260X_REGULATOR_HWINFO_DEF_H__
+
+
+/* for atc2603a ------------------------------------------------------------- */
+
+static const struct atc260x_regu_hwinfo sc_atc2603a_dcdc_hwinfo_tbl[] = {
+	/* keep space for DCDC0 (there's no DCDC0 here) */
+	{ },
+
+	/* DCDC 1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1400000,
+				.volt_min_uv =  700000,
+				.volt_step_uv =  25000,
+				.vsel_start = 0,
+				.vsel_end   = 28,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_DC1_CTL0,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_DC1_CTL0,
+		.vsel_volt_bm = 0x1fU << 7,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 15,
+		.state_uv_bm = 1 << 15,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* DCDC 2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1950000,
+				.volt_min_uv = 1300000,
+				.volt_step_uv =  50000,
+				.vsel_start = 0,
+				.vsel_end   = 13,
+			},
+			{
+				.volt_max_uv = 2150000,
+				.volt_min_uv = 1950000,
+				.volt_step_uv = 100000,
+				.vsel_start = 13,
+				.vsel_end   = 15,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_DC2_CTL0,
+		.ctrl_en_bm = 1 << 15,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_DC2_CTL0,
+		.vsel_volt_bm = 0xfU << 8,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 14,
+		.state_uv_bm = 1 << 14,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_DC3_CTL0,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_DC3_CTL0,
+		.vsel_volt_bm = 0x7U << 9,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 13,
+		.state_uv_bm = 1 << 13,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* DCDC 4 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 5000000,
+				.volt_min_uv = 5000000,
+				.volt_step_uv = 0,
+				.vsel_start = 0,
+				.vsel_end   = 0,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_DC4_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_DC4_CTL0,
+		.vsel_volt_bm = 0,
+
+		.stable_time_on = 3000,
+
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+	},
+};
+
+static const struct atc260x_regu_hwinfo sc_atc2603a_ldo_hwinfo_tbl[] = {
+	/* keep space for LDO0 (there's no LDO0 here) */
+	{ },
+
+	/* LDO1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO1_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO1_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 11,
+		.state_uv_bm = 1 << 11,
+		.state_oc_bm = 1 << 15,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO2_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO2_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 10,
+		.state_uv_bm = 1 << 10,
+		.state_oc_bm = 1 << 14,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2000000,
+				.volt_min_uv = 1500000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 5,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO3_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO3_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 9,
+		.state_uv_bm = 1 << 9,
+		.state_oc_bm = 1 << 13,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO4 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3500000,
+				.volt_min_uv = 2800000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO4_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO4_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 8,
+		.state_uv_bm = 1 << 8,
+		.state_oc_bm = 1 << 12,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO5 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO5_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO5_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 7,
+		.state_uv_bm = 1 << 7,
+		.state_oc_bm = 1 << 11,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO6 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1400000,
+				.volt_min_uv =  700000,
+				.volt_step_uv =  25000,
+				.vsel_start = 0,
+				.vsel_end   = 28,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO6_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO6_CTL,
+		.vsel_volt_bm = 0x1fU << 11,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 6,
+		.state_uv_bm = 1 << 6,
+		.state_oc_bm = 1 << 10,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO7 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2000000,
+				.volt_min_uv = 1500000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 5,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO7_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO7_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 5,
+		.state_uv_bm = 1 << 5,
+		.state_oc_bm = 1 << 9,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO8 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2300000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 10,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO8_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO8_CTL,
+		.vsel_volt_bm = 0xfU << 12,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 4,
+		.state_uv_bm = 1 << 4,
+		.state_oc_bm = 1 << 8,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO9 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1500000,
+				.volt_min_uv = 1000000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 5,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO9_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO9_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 3,
+		.state_uv_bm = 1 << 3,
+		.state_oc_bm = 1 << 7,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO10 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2300000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 10,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO10_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO10_CTL,
+		.vsel_volt_bm = 0xfU << 12,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 2,
+		.state_uv_bm = 1 << 2,
+		.state_oc_bm = 1 << 6,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO11 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603A_PMU_LDO11_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603A_PMU_LDO11_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603A_PMU_OC_STATUS,
+		.state_ov_bm = 0,
+		.state_uv_bm = 0,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+};
+
+/* for atc2603c ------------------------------------------------------------- */
+
+static const struct atc260x_regu_hwinfo sc_atc2603c_ver_a_dcdc_hwinfo_tbl[] = {
+	/* keep space for DCDC0 (there's no DCDC0 here) */
+	{ },
+
+	/* DCDC 1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1400000,
+				.volt_min_uv =  700000,
+				.volt_step_uv =  25000,
+				.vsel_start = 0,
+				.vsel_end   = 28,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_DC1_CTL0,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_DC1_CTL0,
+		.vsel_volt_bm = 0x1fU << 7,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 15,
+		.state_uv_bm = 1 << 15,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* DCDC 2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1950000,
+				.volt_min_uv = 1300000,
+				.volt_step_uv =  50000,
+				.vsel_start = 0,
+				.vsel_end   = 13,
+			},
+			{
+				.volt_max_uv = 2150000,
+				.volt_min_uv = 1950000,
+				.volt_step_uv = 100000,
+				.vsel_start = 13,
+				.vsel_end   = 15,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_DC2_CTL0,
+		.ctrl_en_bm = 1 << 15,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_DC2_CTL0,
+		.vsel_volt_bm = 0xfU << 8,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 14,
+		.state_uv_bm = 1 << 14,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_DC3_CTL0,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_DC3_CTL0,
+		.vsel_volt_bm = 0x7U << 9,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 13,
+		.state_uv_bm = 1 << 13,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+};
+
+static const struct atc260x_regu_hwinfo sc_atc2603c_ver_b_dcdc_hwinfo_tbl[] = {
+	/* keep space for DCDC0 (there's no DCDC0 here) */
+	{ },
+
+	/* DCDC 1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1400000,
+				.volt_min_uv =  700000,
+				.volt_step_uv =  25000,
+				.vsel_start = 0,
+				.vsel_end   = 28,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_DC1_CTL0,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_DC1_CTL0,
+		.vsel_volt_bm = 0x1fU << 7,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 15,
+		.state_uv_bm = 1 << 15,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* DCDC 2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1850000,
+				.volt_min_uv = 1000000,
+				.volt_step_uv =  50000,
+				.vsel_start = 0,
+				.vsel_end   = 17,
+			},
+			#if 0
+			{
+				.volt_max_uv = 2150000,
+				.volt_min_uv = 1950000,
+				.volt_step_uv = 100000,
+				.vsel_start = 13,
+				.vsel_end   = 15,
+			},
+			#endif
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_DC2_CTL0,
+		.ctrl_en_bm = 1 << 15,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_DC2_CTL0,
+		.vsel_volt_bm = 0x1fU << 8,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 14,
+		.state_uv_bm = 1 << 14,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_DC3_CTL0,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_DC3_CTL0,
+		.vsel_volt_bm = 0x7U << 9,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 13,
+		.state_uv_bm = 1 << 13,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 350,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+};
+static const struct atc260x_regu_hwinfo sc_atc2603c_ldo_hwinfo_tbl[] = {
+	/* keep space for LDO0 (there's no LDO0 here) */
+	{ },
+
+	/* LDO1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO1_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO1_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 11,
+		.state_uv_bm = 1 << 11,
+		.state_oc_bm = 1 << 15,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO2_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO2_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 10,
+		.state_uv_bm = 1 << 10,
+		.state_oc_bm = 1 << 14,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2000000,
+				.volt_min_uv = 1500000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 5,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO3_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO3_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 9,
+		.state_uv_bm = 1 << 9,
+		.state_oc_bm = 1 << 13,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* space for LDO4 */
+	{ },
+
+	/* LDO5 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO5_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO5_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 7,
+		.state_uv_bm = 1 << 7,
+		.state_oc_bm = 1 << 11,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO6 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1400000,
+				.volt_min_uv =  700000,
+				.volt_step_uv =  25000,
+				.vsel_start = 0,
+				.vsel_end   = 28,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO6_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO6_CTL,
+		.vsel_volt_bm = 0x1fU << 11,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 6,
+		.state_uv_bm = 1 << 6,
+		.state_oc_bm = 1 << 10,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+
+	/* LDO7 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2000000,
+				.volt_min_uv = 1500000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 5,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO7_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO7_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 5,
+		.state_uv_bm = 1 << 5,
+		.state_oc_bm = 1 << 9,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO8 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2300000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 10,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO8_CTL,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO8_CTL,
+		.vsel_volt_bm = 0xfU << 12,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 4,
+		.state_uv_bm = 1 << 4,
+		.state_oc_bm = 1 << 8,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* space for LDO9 */
+	{ },
+	/* space for LDO10 */
+	{ },
+
+	/* LDO11 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2603C_PMU_LDO11_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2603C_PMU_LDO11_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2603C_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2603C_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2603C_PMU_OC_STATUS,
+		.state_ov_bm = 0,
+		.state_uv_bm = 0,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+};
+
+
+/* for atc2609a ------------------------------------------------------------- */
+
+static const struct atc260x_regu_hwinfo sc_atc2609a_dcdc_hwinfo_tbl[] = {
+	/* DCDC 0 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2193750,
+				.volt_min_uv =  600000,
+				.volt_step_uv =   6250,
+				.vsel_start = 0,
+				.vsel_end   = 255,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_DC_OSC,
+		.ctrl_en_bm = 1 << 4,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_DC0_CTL0,
+		.vsel_volt_bm = 0xffU << 8,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 15,
+		.state_uv_bm = 1 << 15,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 250,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2193750,
+				.volt_min_uv =  600000,
+				.volt_step_uv =   6250,
+				.vsel_start = 0,
+				.vsel_end   = 255,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_DC_OSC,
+		.ctrl_en_bm = 1 << 5,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_DC1_CTL0,
+		.vsel_volt_bm = 0xffU << 8,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 14,
+		.state_uv_bm = 1 << 14,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 250,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2193750,
+				.volt_min_uv =  600000,
+				.volt_step_uv =   6250,
+				.vsel_start = 0,
+				.vsel_end   = 255,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_DC_OSC,
+		.ctrl_en_bm = 1 << 6,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_DC2_CTL0,
+		.vsel_volt_bm = 0xffU << 8,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 13,
+		.state_uv_bm = 1 << 13,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 250,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 1400000,
+				.volt_min_uv =  600000,
+				.volt_step_uv =   6250,
+				.vsel_start = 0,
+				.vsel_end   = 128,
+			},
+			{
+				.volt_max_uv = 4000000,
+				.volt_min_uv = 1400000,
+				.volt_step_uv =  25000,
+				.vsel_start = 128,
+				.vsel_end   = 232,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_DC_OSC,
+		.ctrl_en_bm = 1 << 7,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_DC3_CTL0,
+		.vsel_volt_bm = 0xffU << 8,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 12,
+		.state_uv_bm = 1 << 12,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 250,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* DCDC 4 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2193750,
+				.volt_min_uv =  600000,
+				.volt_step_uv =   6250,
+				.vsel_start = 0,
+				.vsel_end   = 255,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_DC_OSC,
+		.ctrl_en_bm = 1 << 8,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_DC4_CTL0,
+		.vsel_volt_bm = 0xffU << 8,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 2,
+		.state_uv_bm = 1 << 2,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 800,
+		.stable_time_chng_volt = 250,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+};
+
+/* for LDO-6 bugfix */
+static int _atc2609a_ldo6_volt_lookup(struct atc260x_regulator_dev *atc260x_regu,
+		const struct atc260x_regu_hwinfo *hwinfo, uint band, uint index)
+{
+	static const u16 sc_revd_buggy_volt_tbl[16] = {
+		/* in mV */
+		850, 900, 950, 1000, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
+		1100, 1150, 1200, 1250, 2200
+	};
+	BUG_ON(index >= 16);
+	if (band == 0) {
+		/* check rev */
+		if (atc260x_get_ic_ver(atc260x_regu->atc260x) > ATC260X_ICVER_D) {
+			/* > revD, no bug */
+			BUG(); /* TODO : fill this when revE ready. */
+			return 0;
+		} else {
+			return sc_revd_buggy_volt_tbl[index] * 1000U; /* in uV */
+		}
+	} else {
+		/* band 1 has no bug */
+		return 2100000U + 100000U * index;
+	}
+}
+
+static const struct atc260x_regu_hwinfo sc_atc2609a_ldo_hwinfo_tbl[] = {
+	/* LDO 0 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3400000,
+				.volt_min_uv = 2300000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 11,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO0_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 1 << 1,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO0_CTL0,
+		.vsel_volt_bm = 0xfU << 2,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 11,
+		.state_uv_bm = 1 << 11,
+		.state_oc_bm = 1 << 11,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_BYPASS,
+	},
+
+	/* LDO 1 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3400000,
+				.volt_min_uv = 2300000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 11,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO1_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 1 << 1,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO1_CTL0,
+		.vsel_volt_bm = 0xfU << 2,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 10,
+		.state_uv_bm = 1 << 10,
+		.state_oc_bm = 1 << 10,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_BYPASS,
+	},
+
+	/* LDO 2 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3400000,
+				.volt_min_uv = 2300000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 11,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO2_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 1 << 1,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO2_CTL0,
+		.vsel_volt_bm = 0xfU << 2,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 9,
+		.state_uv_bm = 1 << 9,
+		.state_oc_bm = 1 << 9,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_BYPASS,
+	},
+
+	/* LDO 3 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2200000,
+				.volt_min_uv =  700000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 15,
+			},
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2100000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 12,
+			},
+		},
+		.volt_band_exclusive = 1,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO3_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO3_CTL0,
+		.vsel_volt_bm = 0xfU << 1,
+		.vsel_band_reg_addr = ATC2609A_PMU_LDO3_CTL0,
+		.vsel_band_bm = 1 << 5,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 8,
+		.state_uv_bm = 1 << 8,
+		.state_oc_bm = 1 << 8,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO 4 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2200000,
+				.volt_min_uv =  700000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 15,
+			},
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2100000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 12,
+			},
+		},
+		.volt_band_exclusive = 1,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO4_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO4_CTL0,
+		.vsel_volt_bm = 0xfU << 1,
+		.vsel_band_reg_addr = ATC2609A_PMU_LDO4_CTL0,
+		.vsel_band_bm = 1 << 5,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 7,
+		.state_uv_bm = 1 << 7,
+		.state_oc_bm = 1 << 7,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO 5 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2200000,
+				.volt_min_uv =  700000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 15,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO5_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO5_CTL0,
+		.vsel_volt_bm = 0xfU << 1,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 6,
+		.state_uv_bm = 1 << 6,
+		.state_oc_bm = 1 << 6,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO 6 */
+	{
+		.nonlinear_lookup = _atc2609a_ldo6_volt_lookup, /* for bugfix */
+		.volt_bands = {
+			{
+				.volt_max_uv = 2200000,
+				.volt_min_uv =  850000,
+				.volt_step_uv =  50000,
+				.vsel_start = 0,
+				.vsel_end   = 15,
+			},
+			{
+				.volt_max_uv = 3200000,
+				.volt_min_uv = 2100000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 11,
+			},
+		},
+		.volt_band_exclusive = 1,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO6_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO6_CTL0,
+		.vsel_volt_bm = 0xfU << 1,
+		.vsel_band_reg_addr = ATC2609A_PMU_LDO6_CTL0,
+		.vsel_band_bm = 1 << 5,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 5,
+		.state_uv_bm = 1 << 5,
+		.state_oc_bm = 1 << 5,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO 7 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2200000,
+				.volt_min_uv =  700000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 15,
+			},
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2100000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 12,
+			},
+		},
+		.volt_band_exclusive = 1,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO7_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO7_CTL0,
+		.vsel_volt_bm = 0xfU << 1,
+		.vsel_band_reg_addr = ATC2609A_PMU_LDO7_CTL0,
+		.vsel_band_bm = 1 << 5,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 4,
+		.state_uv_bm = 1 << 4,
+		.state_oc_bm = 1 << 4,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO 8 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 2200000,
+				.volt_min_uv =  700000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 15,
+			},
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2100000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 12,
+			},
+		},
+		.volt_band_exclusive = 1,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO8_CTL0,
+		.ctrl_en_bm = 1 << 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO8_CTL0,
+		.vsel_volt_bm = 0xfU << 1,
+		.vsel_band_reg_addr = ATC2609A_PMU_LDO8_CTL0,
+		.vsel_band_bm = 1 << 5,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 1 << 3,
+		.state_uv_bm = 1 << 3,
+		.state_oc_bm = 1 << 3,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS,
+	},
+
+	/* LDO 9 */
+	{
+		.volt_bands = {
+			{
+				.volt_max_uv = 3300000,
+				.volt_min_uv = 2600000,
+				.volt_step_uv = 100000,
+				.vsel_start = 0,
+				.vsel_end   = 7,
+			},
+		},
+		.volt_band_exclusive = 0,
+
+		.ctrl_reg_addr = ATC2609A_PMU_LDO9_CTL,
+		.ctrl_en_bm = 0,
+		.ctrl_bypass_bm = 0,
+
+		.vsel_reg_addr = ATC2609A_PMU_LDO9_CTL,
+		.vsel_volt_bm = 0x7U << 13,
+
+		.state_ov_reg_addr = ATC2609A_PMU_OV_STATUS,
+		.state_uv_reg_addr = ATC2609A_PMU_UV_STATUS,
+		.state_oc_reg_addr = ATC2609A_PMU_OC_STATUS,
+		.state_ov_bm = 0,
+		.state_uv_bm = 0,
+		.state_oc_bm = 0,
+
+		.stable_time_on = 2000,
+		.stable_time_chng_volt = 800,
+
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+	},
+};
+
+
+
+
+#endif /* __MFD_ATC260X_REGULATOR_HWINFO_DEF_H__ */
+
+#endif
diff --git a/drivers/regulator/atc260x-switch-ldo.c b/drivers/regulator/atc260x-switch-ldo.c
new file mode 100755
index 0000000..c72f5ce
--- /dev/null
+++ b/drivers/regulator/atc260x-switch-ldo.c
@@ -0,0 +1,540 @@
+/*
+ * atc260x_switch_ldo.c  --  Switch LDO driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+
+#define PMU_SWITCH_CTL_SWITCH1_LDO_BIAS         (1 << 0)
+#define PMU_SWITCH_CTL_SWITCH1_DISCHARGE_EN (1 << 1)
+#define PMU_SWITCH_CTL_SWITCH1_LDO_SET(x)       (((x) & 0x3) << 3)
+#define PMU_SWITCH_CTL_SWITCH1_LDO_GET(x)       (((x) & 0x18) >> 3)
+#define PMU_SWITCH_CTL_SWITCH1_LDO_MASK         (0x3 << 3)
+#define PMU_SWITCH_CTL_SWITCH1_MODE_MASK      (1 << 5)
+#define PMU_SWITCH_CTL_SWITCH1_MODE_SWITCH  (0 << 5)
+#define PMU_SWITCH_CTL_SWITCH1_MODE_LDO         (1 << 5)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_BIAS     (1 << 6)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_SET(x)       (((x) & 0xf) << 8)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_GET(x)       (((x) & 0xf00) >> 8)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_MASK         ( 0xf << 8)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_MASK (1 << 12)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_10_20V   (0 << 12)
+#define PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_23_33V   (1 << 12)
+#define PMU_SWITCH_CTL_SWITCH2_MODE_SWITCH      (0 << 13)
+#define PMU_SWITCH_CTL_SWITCH2_MODE_LDO         (1 << 13)
+#define PMU_SWITCH_CTL_SWITCH2_EN               (1 << 14)
+#define PMU_SWITCH_CTL_SWITCH1_EN               (1 << 15)
+
+#define ATC260X_SWITCH_LDO_MAX_NUM  2
+
+static unsigned short switch2_SEL;
+
+/* Supported voltage values for regulators (in milliVolts) */
+static const unsigned int switch2_ldo_volcfg1_table[] = {
+	1000000, 1100000, 1200000, 1300000,
+	1400000, 1500000, 1600000, 1700000,
+	1750000, 1800000, 1850000, 1900000,
+	1950000, 2000000, 2000000, 2000000,
+};
+
+static const unsigned int switch2_ldo_volcfg2_table[] = {
+	2300000, 2400000, 2500000, 2600000,
+	2700000, 2800000, 2900000, 3000000,
+	3050000, 3100000, 3150000, 3200000,
+	3250000, 3300000, 3300000, 3300000,
+};
+
+static const unsigned int switch1_ldo_volcfg_table[] = {
+	3000000, 3100000, 3200000, 3300000,
+};
+
+struct atc260x_switch_ldo_property
+{
+	int table_len;
+	const unsigned int *table;
+};
+
+struct atc260x_swldo_dev {
+	const char *name;
+	struct regulator_desc desc;
+	struct atc260x_dev *atc260x;
+	struct regulator_dev *regulator;
+	struct atc260x_switch_ldo_property property;
+	uint ctrl_reg;
+	uint pmic_type;
+	uint pmic_ver;
+	uint vsel_now;
+};
+
+static int atc260x_switch_ldo_list_voltage(struct regulator_dev *rdev,
+					  unsigned int selector)
+{
+	struct atc260x_swldo_dev *ldo = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+
+	if (id > ATC260X_SWITCH_LDO_MAX_NUM ||
+					selector >= (ldo->property.table_len))
+		return -EINVAL;
+
+	return ldo->property.table[selector];
+}
+
+static int atc260x_switch_ldo_is_enabled(struct regulator_dev *rdev)
+{
+	struct atc260x_swldo_dev *ldo = rdev_get_drvdata(rdev);
+	struct atc260x_dev *atc260x = ldo->atc260x;
+	int id, ret, mask_bit;
+
+	id = rdev_get_id(rdev);
+	if (id > ATC260X_SWITCH_LDO_MAX_NUM)
+		return -EINVAL;
+
+	ret = atc260x_reg_read(atc260x, ldo->ctrl_reg);
+	if (ret < 0)
+		return ret;
+
+	if (1 == id) {
+		mask_bit = PMU_SWITCH_CTL_SWITCH1_EN | PMU_SWITCH_CTL_SWITCH1_MODE_LDO;
+		ret &= mask_bit;
+		if (ldo->pmic_type == ATC260X_ICTYPE_2603A &&
+			ldo->pmic_ver <= ATC260X_ICVER_C) {
+			/*ver A B C*/
+			return (ret == mask_bit);
+		} else {
+			/*ver D*/
+			return (ret == 0);
+		}
+	} else {
+		mask_bit = PMU_SWITCH_CTL_SWITCH2_EN | PMU_SWITCH_CTL_SWITCH2_MODE_LDO;
+		ret &= mask_bit;
+		return (ret == mask_bit);
+	}
+}
+
+static int atc260x_switch_ldo_enable(struct regulator_dev *rdev)
+{
+	struct atc260x_swldo_dev *ldo = rdev_get_drvdata(rdev);
+	struct atc260x_dev *atc260x = ldo->atc260x;
+	int id, ret;
+
+	id = rdev_get_id(rdev);
+	if (id > ATC260X_SWITCH_LDO_MAX_NUM)
+		return -EINVAL;
+
+	if (1 == id) {
+		if (ldo->pmic_type == ATC260X_ICTYPE_2603A &&
+			ldo->pmic_ver <= ATC260X_ICVER_C) {
+			ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+						PMU_SWITCH_CTL_SWITCH1_EN, PMU_SWITCH_CTL_SWITCH1_EN);
+			pr_info("enable switch 1, version A~C!\n");
+		} else {
+			/*ver D, enable is 0, disable is 1*/
+			ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+						PMU_SWITCH_CTL_SWITCH1_EN, 0);
+			pr_info("enable switch 1, version D!\n");
+		}
+	} else {
+		ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_EN, PMU_SWITCH_CTL_SWITCH2_EN);
+	}
+
+	mdelay(2);
+
+	return ret;
+}
+
+static int atc260x_switch_ldo_disable(struct regulator_dev *rdev)
+{
+	struct atc260x_swldo_dev *ldo = rdev_get_drvdata(rdev);
+	struct atc260x_dev *atc260x = ldo->atc260x;
+	int id, ret;
+
+	id = rdev_get_id(rdev);
+	if (id > ATC260X_SWITCH_LDO_MAX_NUM)
+		return -EINVAL;
+
+	if (1 == id) {
+		if (ldo->pmic_type == ATC260X_ICTYPE_2603A &&
+			ldo->pmic_ver <= ATC260X_ICVER_C) {
+			/*ver A~C, enable is 1, disable is 0*/
+			ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+					PMU_SWITCH_CTL_SWITCH1_EN, 0);
+			pr_info("disable switch 1, version A~C!\n");
+		} else {
+			/*ver D, enable is 0, disable is 1*/
+			ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+					PMU_SWITCH_CTL_SWITCH1_EN, PMU_SWITCH_CTL_SWITCH1_EN);
+			pr_info("disable switch 1, version D!\n");
+		}
+	} else {
+			ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_EN, 0);
+	}
+
+	return ret;
+}
+
+static int atc260x_switch_ldo_set_voltage(struct regulator_dev *rdev,
+					 int min_uV, int max_uV, unsigned *selector)
+{
+	struct atc260x_swldo_dev *ldo = rdev_get_drvdata(rdev);
+	struct atc260x_dev *atc260x = ldo->atc260x;
+	int id, ret, vsel;
+	unsigned int uV;
+
+	id = rdev_get_id(rdev);
+	if (id > ATC260X_SWITCH_LDO_MAX_NUM)
+		return -EINVAL;
+
+	if (min_uV < (rdev->constraints->min_uV) ||
+				min_uV > (rdev->constraints->max_uV))
+		return -EINVAL;
+	if (max_uV < (rdev->constraints->min_uV) ||
+				max_uV >(rdev->constraints->max_uV))
+		return -EINVAL;
+
+	for (vsel = 0; vsel < ldo->property.table_len; vsel++) {
+		uV = ldo->property.table[vsel];
+
+		/* Break at the first in-range value */
+		if (min_uV <= uV && uV <= max_uV)
+			break;
+	}
+
+	if (vsel == ldo->property.table_len)
+		return -EINVAL;
+
+	if (1 ==id) {
+		ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH1_LDO_MASK,
+				PMU_SWITCH_CTL_SWITCH1_LDO_SET(vsel));
+	} else {
+		ret = atc260x_reg_setbits(atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_LDO_MASK,
+				PMU_SWITCH_CTL_SWITCH2_LDO_SET(vsel));
+	}
+
+	if (0 == ret) {
+		ldo->vsel_now = *selector = vsel;
+		mdelay(1);
+	}
+
+	return ret;
+}
+
+static int atc260x_switch_ldo_get_voltage(struct regulator_dev *rdev)
+{
+	struct atc260x_swldo_dev *ldo = rdev_get_drvdata(rdev);
+	struct atc260x_dev *atc260x = ldo->atc260x;
+	int id, ret, vsel;
+
+	id = rdev_get_id(rdev);
+	if (id > ATC260X_SWITCH_LDO_MAX_NUM)
+		return -EINVAL;
+
+	ret = atc260x_reg_read(atc260x, ldo->ctrl_reg);
+	if (ret < 0)
+		return ret;
+
+	if (1 == id)
+		vsel = PMU_SWITCH_CTL_SWITCH1_LDO_GET(ret);
+	else
+		vsel = PMU_SWITCH_CTL_SWITCH2_LDO_GET(ret);
+
+	ret = ldo->property.table[vsel];
+
+	return ret;
+}
+
+
+static struct regulator_ops atc260x_switch_ldo_ops = {
+	.list_voltage    = atc260x_switch_ldo_list_voltage,
+	.get_voltage   = atc260x_switch_ldo_get_voltage,
+	.set_voltage   = atc260x_switch_ldo_set_voltage,
+	.is_enabled     = atc260x_switch_ldo_is_enabled,
+	.enable           = atc260x_switch_ldo_enable,
+	.disable          = atc260x_switch_ldo_disable,
+};
+
+static int ldo_init(struct atc260x_swldo_dev *ldo, struct regulator_init_data *init_data)
+{
+	int ret = 0, id;
+	int min_uv =  init_data->constraints.min_uV;
+	int max_uv = init_data->constraints.max_uV;
+
+	id = ldo->desc.id;
+	if (id == 1) {
+		/* Switch LDO 1, diable  discharge*/
+		atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+			PMU_SWITCH_CTL_SWITCH1_DISCHARGE_EN, 0);
+
+		if ((ldo->pmic_type == ATC260X_ICTYPE_2603A) &&
+			(ldo->pmic_ver <= ATC260X_ICVER_C)) {
+			/* 5302 ver A,B,C,  1- LDO, 0-- switch*/
+			pr_warn("[SWITCH1] set switch 1 to ldo mode (2603a Version A~C!)\n");
+			atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH1_MODE_MASK,
+				PMU_SWITCH_CTL_SWITCH1_MODE_LDO);
+		} else {
+			/* ver D, 0-- LDO, 1-- switch*/
+			pr_warn("[SWITCH1] set switch 1 to ldo mode Version D!\n");
+			atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH1_MODE_MASK,
+				0);
+		}
+		ldo->property.table = switch1_ldo_volcfg_table;
+		ldo->property.table_len = ARRAY_SIZE(switch1_ldo_volcfg_table);;
+	} else if (id == 2) {
+		/* Switch LDO 2 */
+		int table1_len = ARRAY_SIZE(switch2_ldo_volcfg1_table);
+		int table2_len = ARRAY_SIZE(switch2_ldo_volcfg2_table);
+
+		atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_EN,
+				0);
+
+		if ((switch2_ldo_volcfg1_table[0] <= min_uv) &&
+				(switch2_ldo_volcfg1_table[table1_len - 1] >= max_uv)) {
+			atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_MASK,
+				PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_10_20V);
+			ldo->property.table = switch2_ldo_volcfg1_table;
+			ldo->property.table_len = table1_len;
+			switch2_SEL = PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_10_20V;
+		} else if ((switch2_ldo_volcfg2_table[0] <= min_uv) &&
+				(switch2_ldo_volcfg2_table[table2_len - 1] >= max_uv)) {
+			atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_MASK,
+				PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_23_33V);
+			ldo->property.table = switch2_ldo_volcfg2_table;
+			ldo->property.table_len = table2_len;
+			switch2_SEL = PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_23_33V;
+			} else {
+				ret = -EINVAL;
+			}
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int atc260x_switch_ldo_probe(struct platform_device *pdev)
+{
+	static const u16 sc_ctrl_reg_tbl[ATC260X_ICTYPE_CNT] = {
+		[ATC260X_ICTYPE_2603A] = ATC2603A_PMU_SWITCH_CTL,
+		[ATC260X_ICTYPE_2603C] = ATC2603C_PMU_SWITCH_CTL,
+		[ATC260X_ICTYPE_2609A] = 0,
+	};
+	struct regulator_config config = { };
+	struct regulator_init_data  *init_data;
+	struct atc260x_dev *atc260x;
+	struct atc260x_swldo_dev *ldo;
+	uint ic_type;
+	int id, ret;
+
+	id = pdev->id;
+	dev_info(&pdev->dev, "Probing %s, id=%u\n", pdev->name, id);
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, NULL);
+	if (!init_data) {
+		return -ENOMEM;
+	}
+
+	ldo = devm_kzalloc(&pdev->dev, sizeof(struct atc260x_swldo_dev), GFP_KERNEL);
+	if (ldo == NULL) {
+		dev_err(&pdev->dev, "Unable to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	ldo->atc260x = atc260x;
+	ldo->desc.id = id;
+	ldo->name = init_data->constraints.name;
+	ldo->desc.name = ldo->name;
+
+	ic_type = atc260x_get_ic_type(atc260x);
+	BUG_ON(ic_type >= ARRAY_SIZE(sc_ctrl_reg_tbl));
+	ldo->ctrl_reg = sc_ctrl_reg_tbl[ic_type];
+	if (ldo->ctrl_reg == 0) {
+		dev_err(&pdev->dev, "device not support\n");
+		return -ENXIO;
+	}
+	ldo->pmic_type = atc260x_get_ic_type(atc260x);
+	ldo->pmic_ver = atc260x_get_ic_ver(atc260x);
+	dev_err(&pdev->dev, "pmic_ver=%u\n", ldo->pmic_ver);
+
+	ret = ldo_init(ldo, init_data);
+	if (ret) {
+		pr_err("Faild to init Switch LDO%d", id);
+		goto err;
+	}
+
+	ldo->desc.type = REGULATOR_VOLTAGE;
+	ldo->desc.n_voltages = ldo->property.table_len;
+	ldo->desc.ops = &atc260x_switch_ldo_ops;
+	ldo->desc.owner = THIS_MODULE;
+
+	config.dev = &pdev->dev;
+	config.init_data = init_data;
+	config.driver_data = ldo;
+	config.of_node = pdev->dev.of_node;
+
+	ldo->regulator = regulator_register(&ldo->desc, &config);
+	if (IS_ERR(ldo->regulator)) {
+		ret = PTR_ERR(ldo->regulator);
+		dev_err(&pdev->dev, "Failed to register LDO%d: %d\n",
+			id + 1, ret);
+		goto err;
+	}
+
+	ret = atc260x_reg_read(atc260x, ldo->ctrl_reg);
+	if (ret < 0)
+		goto err2;
+	if (1 == id)
+		ldo->vsel_now = PMU_SWITCH_CTL_SWITCH1_LDO_GET(ret);
+	else
+		ldo->vsel_now = PMU_SWITCH_CTL_SWITCH2_LDO_GET(ret);
+
+	platform_set_drvdata(pdev, ldo);
+
+#if 0 && defined(CONFIG_REGULATOR_VIRTUAL_CONSUMER)
+	/* for debug */
+	platform_device_register_resndata(NULL, "reg-virt-consumer",
+		30 + id,
+		NULL, 0,
+		ldo->name, strlen(ldo->name) + 1);
+#endif
+	return 0;
+
+err2:
+	regulator_unregister(ldo->regulator);
+err:
+	return ret;
+}
+
+static int atc260x_switch_ldo_remove(struct platform_device *pdev)
+{
+	struct atc260x_swldo_dev *ldo = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	regulator_unregister(ldo->regulator);
+
+	return 0;
+}
+
+static int atc260x_switch_ldo_suspend_late(struct device *dev)
+{
+	struct atc260x_swldo_dev *ldo = dev_get_drvdata(dev);
+	uint state = atc260x_switch_ldo_is_enabled(ldo->regulator);
+	if (state) {
+		dev_warn(dev, "%s() switch%d state : %d\n",
+			__func__, ldo->desc.id, state);
+	} else {
+		dev_info(dev, "%s() switch%d state : %d\n",
+			__func__, ldo->desc.id, state);
+	}
+	return 0;
+}
+
+static int atc260x_switch_ldo_resume_early(struct device *dev)
+{
+	struct atc260x_swldo_dev *ldo = dev_get_drvdata(dev);
+	int id, ret;
+
+	id = ldo->desc.id;
+
+	dev_info(dev, "%s() ldo%d\n", __func__, ldo->desc.id);
+
+	if ((ldo->vsel_now) >= (ldo->property.table_len))
+		return -EINVAL;
+
+	if (1 ==id) {
+		ret = atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH1_LDO_MASK,
+				PMU_SWITCH_CTL_SWITCH1_LDO_SET(ldo->vsel_now));
+	} else {
+		ret = atc260x_reg_setbits(ldo->atc260x, ldo->ctrl_reg,
+				PMU_SWITCH_CTL_SWITCH2_LDO_MASK |
+					PMU_SWITCH_CTL_SWITCH2_LDO_VOL_SEL_MASK,
+				PMU_SWITCH_CTL_SWITCH2_LDO_SET(ldo->vsel_now)) |
+					switch2_SEL;
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops s_atc260x_switch_ldo_pm_ops = {
+	.suspend_late  = atc260x_switch_ldo_suspend_late,
+	.resume_early  = atc260x_switch_ldo_resume_early,
+	.freeze_late   = atc260x_switch_ldo_suspend_late,
+	.thaw_early    = atc260x_switch_ldo_resume_early,
+	.poweroff_late = atc260x_switch_ldo_suspend_late,
+	.restore_early = atc260x_switch_ldo_resume_early,
+};
+
+static const struct of_device_id atc260x_switch_ldo_match[] = {
+	/* only 2603a & 2603c have switchs */
+	{ .compatible = "actions,atc2603a-switch", },
+	{ .compatible = "actions,atc2603c-switch", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_switch_ldo_match);
+
+static struct platform_driver atc260x_switch_ldo_driver = {
+	.driver     = {
+		.name   = "atc260x-switch-ldo",
+		.owner  = THIS_MODULE,
+		.pm     = &s_atc260x_switch_ldo_pm_ops,
+		.of_match_table = of_match_ptr(atc260x_switch_ldo_match),
+	},
+	.probe = atc260x_switch_ldo_probe,
+	.remove = atc260x_switch_ldo_remove,
+};
+
+static int __init atc260x_switch_ldo_init(void)
+{
+	int ret;
+	ret = platform_driver_register(&atc260x_switch_ldo_driver);
+	if (ret != 0)
+		pr_err("Failed to register ATC260X Switch LDO driver: %d\n", ret);
+
+	return 0;
+}
+subsys_initcall(atc260x_switch_ldo_init);
+
+static void __exit atc260x_switch_ldo_exit(void)
+{
+	platform_driver_unregister(&atc260x_switch_ldo_driver);
+}
+module_exit(atc260x_switch_ldo_exit);
+
+/* Module information */
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("ATC260X Switch LDO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-switch-ldo");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
old mode 100644
new mode 100755
index 0fe4ad8..a2281da
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1048,7 +1048,14 @@ config RTC_DRV_WM831X
 
 	  This driver can also be built as a module. If so, the module
 	  will be called "rtc-wm831x".
-
+	  
+config RTC_DRV_ATC206X
+	tristate "actions ATC260x PMIC RTC"
+	depends on MFD_ATC260X
+	help
+	  If you say yes here you will get support for the RTC subsystem
+	  of the actions atc260x series PMICs.
+	  
 config RTC_DRV_WM8350
 	tristate "Wolfson Microelectronics WM8350 RTC"
 	depends on MFD_WM8350
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
old mode 100644
new mode 100755
index 2b82e2b..0f338f9
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -155,3 +155,4 @@ obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
+obj-$(CONFIG_RTC_DRV_ATC206X)	+= rtc-atc260x.o
diff --git a/drivers/rtc/rtc-atc260x.c b/drivers/rtc/rtc-atc260x.c
new file mode 100755
index 0000000..128da117
--- /dev/null
+++ b/drivers/rtc/rtc-atc260x.c
@@ -0,0 +1,727 @@
+/*
+ * atc260x_rtc.c  --  RTC driver for ATC260X
+ *
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+/* UTF-8 encoded.
+ * 因为N颗IC的RTC寄存器位定义都是一致的, 仅仅是寄存器的地址有差别, regdef仅仅覆盖到寄存器地址即可. */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mfd/atc260x/atc260x.h>
+
+#define RTC_DEFAULT_START_YEAR      (2011)
+
+/* RTC Control Register */
+#define RTC_CTL_ALIP                (1 << 0)
+#define RTC_CTL_ALIE                (1 << 1)
+#define RTC_CTL_EXT_LOSC_STATUS     (1 << 3)
+#define RTC_CTL_RTCE                (1 << 4)
+#define RTC_CTL_CKSS0               (1 << 5)
+#define RTC_CTL_EOSC                (1 << 6)
+#define RTC_CTL_LEAP                (1 << 9)
+#define RTC_CTL_VER1                (1 << 10)
+#define RTC_CTL_RST                 (1 << 11)
+#define RTC_CTL_LGS                 (1 << 11)
+
+
+#define RTC_YMD_Y_SHIFT             (9)
+#define RTC_YMD_Y_MASK              (0x7f << RTC_YMD_Y_SHIFT)
+#define RTC_YMD_M_SHIFT             (5)
+#define RTC_YMD_M_MASK              (0xf << RTC_YMD_M_SHIFT)
+#define RTC_YMD_D_SHIFT             (0)
+#define RTC_YMD_D_MASK              (0x1f << RTC_YMD_D_SHIFT)
+
+#define RTC_H_H_SHIFT               (0)
+#define RTC_H_H_MASK                (0x1f << RTC_H_H_SHIFT)
+
+#define RTC_MS_M_SHIFT              (6)
+#define RTC_MS_M_MASK               (0x3f << RTC_MS_M_SHIFT)
+#define RTC_MS_S_SHIFT              (0)
+#define RTC_MS_S_MASK               (0x3f << RTC_MS_S_SHIFT)
+
+#define RTC_DC_D_SHIFT              (7)
+#define RTC_DC_D_MASK               (0x7 << RTC_DC_D_SHIFT)
+#define RTC_DC_C_SHIFT              (0)
+#define RTC_DC_C_MASK               (0x7f << RTC_DC_C_SHIFT)
+
+#define RTC_YMD_Y(ymd)              (((ymd) & RTC_YMD_Y_MASK) >> RTC_YMD_Y_SHIFT)
+#define RTC_YMD_M(ymd)              (((ymd) & RTC_YMD_M_MASK) >> RTC_YMD_M_SHIFT)
+#define RTC_YMD_D(ymd)              (((ymd) & RTC_YMD_D_MASK) >> RTC_YMD_D_SHIFT)
+#define RTC_YMD_VAL(y, m, d)        (((y) << RTC_YMD_Y_SHIFT) | ((m) << RTC_YMD_M_SHIFT) | ((d) << RTC_YMD_D_SHIFT))
+
+#define RTC_H_H(h)                  (((h) & RTC_H_H_MASK) >> RTC_H_H_SHIFT)
+#define RTC_H_VAL(h)                (((h) << RTC_H_H_SHIFT))
+
+#define RTC_MS_M(ms)                (((ms) & RTC_MS_M_MASK) >> RTC_MS_M_SHIFT)
+#define RTC_MS_S(ms)                (((ms) & RTC_MS_S_MASK) >> RTC_MS_S_SHIFT)
+#define RTC_MS_VAL(m, s)            (((m) << RTC_MS_M_SHIFT) | ((s) << RTC_MS_S_SHIFT))
+
+#define RTC_DC_D(dc)                (((dc) & RTC_DC_D_MASK) >> RTC_DC_D_SHIFT)
+#define RTC_DC_C(dc)                (((dc) & RTC_DC_C_MASK) >> RTC_DC_C_SHIFT)
+#define RTC_DC_VAL(d, c)            (((d) << RTC_DC_D_SHIFT) | ((c) << RTC_DC_C_SHIFT))
+
+
+#define REG_RTC_CTL                 0
+#define REG_RTC_MSALM               1
+#define REG_RTC_HALM                2
+#define REG_RTC_YMDALM              3
+#define REG_RTC_MS                  4
+#define REG_RTC_H                   5
+#define REG_RTC_DC                  6
+#define REG_RTC_YMD                 7
+
+struct atc260x_rtc_dev {
+	struct atc260x_dev *atc260x;
+	struct rtc_device *rtc;
+	uint regbase;
+	uint alarm_enabled;
+	int  alm_irq;
+};
+
+/*
+ * Read current time and date in RTC
+ */
+static int atc260x_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = dev_get_drvdata(dev);
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	int ymd, h, ms, dc, century;
+
+	ymd = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_YMD);
+	if (ymd < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, ymd);
+		return ymd;
+	}
+
+	h = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_H);
+	if (h < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, h);
+		return h;
+	}
+
+	ms = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_MS);
+	if (ms < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, ms);
+		return ms;
+	}
+
+	dc = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_DC);
+	if (dc < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, dc);
+		return dc;
+	}
+
+	century = RTC_DC_C(dc);
+	if (century < 19) {
+		/* default: 2011 */
+		tm->tm_year = RTC_YMD_Y(ymd) +  RTC_DEFAULT_START_YEAR - 1900;
+	} else {
+		tm->tm_year = RTC_YMD_Y(ymd) +  century * 100 - 1900;
+	}
+
+	tm->tm_mon = RTC_YMD_M(ymd) - 1;
+	tm->tm_mday = RTC_YMD_D(ymd);
+	tm->tm_hour = RTC_H_H(h);
+	tm->tm_min = RTC_MS_M(ms);
+	tm->tm_sec = RTC_MS_S(ms);
+	tm->tm_wday = RTC_DC_D(dc);
+
+	dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
+		1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	return rtc_valid_tm(tm);
+}
+
+
+static int atc260x_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = dev_get_drvdata(dev);
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	int ret, century, year;
+
+	dev_info(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
+		1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	century = (tm->tm_year + 1900) / 100;
+	year = tm->tm_year % 100;
+
+	/* Stop RTC while updating the TC registers */
+	ret = atc260x_reg_wp_setbits(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_RTCE, 0);
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	/* update all the time registers in one shot */
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_YMD,
+				   RTC_YMD_VAL(year, tm->tm_mon + 1, tm->tm_mday));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_H,
+		RTC_H_VAL(tm->tm_hour));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_MS,
+		RTC_MS_VAL(tm->tm_min, tm->tm_sec));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_DC,
+		RTC_DC_VAL(tm->tm_wday, century));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	/* Start RTC */
+	ret = atc260x_reg_wp_clrpnd(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_ALIP);
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+	ret = atc260x_reg_wp_setbits(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_RTCE, RTC_CTL_RTCE);
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int atc260x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = dev_get_drvdata(dev);
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	int ret, ymd, h, ms, dc, century;
+
+	ymd = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_YMDALM);
+	if (ymd < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, ymd);
+		return ymd;
+	}
+
+	h = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_HALM);
+	if (h < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, h);
+		return h;
+	}
+
+	ms = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_MSALM);
+	if (ms < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, ms);
+		return ms;
+	}
+
+	dc = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_DC);
+	if (dc < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, dc);
+		return dc;
+	}
+
+	century = RTC_DC_C(dc);
+	if (century < 19) {
+		/* default: 2011 */
+		alrm->time.tm_year = RTC_YMD_Y(ymd) +  RTC_DEFAULT_START_YEAR - 1900;
+	} else {
+		alrm->time.tm_year = RTC_YMD_Y(ymd) +  century * 100 - 1900;
+	}
+
+	alrm->time.tm_mon = RTC_YMD_M(ymd) - 1;
+	alrm->time.tm_mday = RTC_YMD_D(ymd);
+	alrm->time.tm_hour = RTC_H_H(h);
+	alrm->time.tm_min = RTC_MS_M(ms);
+	alrm->time.tm_sec = RTC_MS_S(ms);
+
+	ret = atc260x_reg_read(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL);
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: read reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	if (ret & RTC_CTL_ALIE)
+		alrm->enabled = 1;
+	else
+		alrm->enabled = 0;
+
+	return 0;
+}
+
+static int atc260x_rtc_stop_alarm(struct atc260x_rtc_dev *atc260x_rtc)
+{
+	atc260x_rtc->alarm_enabled = 0;
+
+	return atc260x_reg_wp_setbits(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+				   RTC_CTL_ALIP, RTC_CTL_ALIE, 0);
+}
+
+static int atc260x_rtc_start_alarm(struct atc260x_rtc_dev *atc260x_rtc)
+{
+	int ret;
+
+	atc260x_rtc->alarm_enabled = 1;
+
+	ret = atc260x_reg_wp_clrpnd(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+				   RTC_CTL_ALIP, RTC_CTL_ALIP);
+	if (ret)
+		return ret;
+	return atc260x_reg_wp_setbits(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+				   RTC_CTL_ALIP, RTC_CTL_ALIE, RTC_CTL_ALIE);
+}
+
+static int atc260x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = dev_get_drvdata(dev);
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	int ret, year;
+
+	struct rtc_time *tm = &alrm->time;
+	if (tm != NULL) {
+		dev_info(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
+			1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+			tm->tm_hour, tm->tm_min, tm->tm_sec);
+	}
+
+	ret = atc260x_rtc_stop_alarm(atc260x_rtc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to stop alarm: %d\n", ret);
+		return ret;
+	}
+
+	year = alrm->time.tm_year % 100;
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_YMDALM,
+		RTC_YMD_VAL(year, alrm->time.tm_mon + 1, alrm->time.tm_mday));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_HALM,
+		RTC_H_VAL(alrm->time.tm_hour));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	ret = atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_MSALM,
+		RTC_MS_VAL(alrm->time.tm_min, alrm->time.tm_sec));
+	if (ret < 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	if (alrm->enabled) {
+		ret = atc260x_rtc_start_alarm(atc260x_rtc);
+		if (ret < 0) {
+			dev_err(dev, "Failed to start alarm: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int atc260x_rtc_alarm_irq_enable(struct device *dev,
+					   unsigned int enabled)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = dev_get_drvdata(dev);
+
+	if (enabled)
+		return atc260x_rtc_start_alarm(atc260x_rtc);
+	else
+		return atc260x_rtc_stop_alarm(atc260x_rtc);
+}
+
+
+static irqreturn_t atc260x_alm_irq(int irq, void *data)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = data;
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	struct device *dev = atc260x_rtc->rtc->dev.parent;
+	int ret;
+
+	dev_info(dev, "alarm irq isr\n");
+
+	/* clear alarm IRQ pending */
+	ret = atc260x_reg_wp_clrpnd(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_ALIP);
+	if (ret != 0) {
+		dev_err(dev, "%s() %u: write reg err, ret=%d\n", __func__, __LINE__, ret);
+	}
+
+	rtc_update_irq(atc260x_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops atc260x_rtc_ops = {
+	.read_time          = atc260x_rtc_readtime,
+	.set_time           = atc260x_rtc_settime,
+	.read_alarm         = atc260x_rtc_readalarm,
+	.set_alarm          = atc260x_rtc_setalarm,
+	.alarm_irq_enable   = atc260x_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM
+/* Turn off the alarm if it should not be a wake source. */
+static int atc260x_rtc_suspend(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct atc260x_rtc_dev *atc260x_rtc;
+	int ret, enable;
+
+	dev_info(dev, "%s() enter\n", __func__);
+
+	pdev = to_platform_device(dev);
+	atc260x_rtc = dev_get_drvdata(&pdev->dev);
+
+	if (atc260x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev))
+		enable = RTC_CTL_ALIE;
+	else
+		enable = 0;
+
+	ret = atc260x_reg_wp_setbits(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+				  RTC_CTL_ALIP, RTC_CTL_ALIE, enable);
+	if (ret != 0)
+		dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret);
+
+	return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int atc260x_rtc_resume(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct atc260x_rtc_dev *atc260x_rtc;
+	int ret;
+
+	dev_info(dev, "%s() enter\n", __func__);
+
+	pdev = to_platform_device(dev);
+	atc260x_rtc = dev_get_drvdata(&pdev->dev);
+
+	if (atc260x_rtc->alarm_enabled) {
+		ret = atc260x_rtc_start_alarm(atc260x_rtc);
+		if (ret != 0)
+			dev_err(&pdev->dev, "Failed to restart RTC alarm: %d\n", ret);
+	}
+
+	return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int atc260x_rtc_freeze(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct atc260x_rtc_dev *atc260x_rtc;
+	int ret;
+
+	dev_info(dev, "%s() enter\n", __func__);
+
+	pdev = to_platform_device(dev);
+	atc260x_rtc = dev_get_drvdata(&pdev->dev);
+
+	ret = atc260x_reg_wp_setbits(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+				  RTC_CTL_ALIP, RTC_CTL_ALIE, 0);
+	if (ret != 0)
+		dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
+
+	return 0;
+}
+#else
+#define atc260x_rtc_suspend NULL
+#define atc260x_rtc_resume NULL
+#define atc260x_rtc_freeze NULL
+#endif
+
+#if 0
+static int check_default_year(struct atc260x_rtc_dev *atc260x_rtc, int default_year)
+{
+	struct device *dev = atc260x_rtc->rtc->dev.parent;
+	struct rtc_time tm;
+	int ret;
+
+	if (default_year < 1900)
+		return -EINVAL;
+
+	ret = atc260x_rtc_readtime(dev, &tm);
+	if (ret)
+		return ret;
+
+	/* atc260x_rtc_readtime() doesn't return real year if the year < 1900,
+	 * so the following code is dead code.
+	 */
+	if (tm.tm_year < (default_year - 1900)) {
+		dev_err(dev, "invalid year(%d), reset to year %d\n",
+			tm.tm_year + 1900, default_year);
+
+		tm.tm_year = default_year - 1900;
+		ret = atc260x_rtc_settime(dev, &tm);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+#endif
+
+/* take effect after 0.5s if reset time */
+static int check_default_year(struct atc260x_rtc_dev *atc260x_rtc, int default_year)
+{
+	struct device *dev = atc260x_rtc->rtc->dev.parent;
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	int ymd, dc, century, year_in_century;
+
+	if (default_year < 1900)
+		return -EINVAL;
+
+	ymd = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_YMD);
+	dc = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_DC);
+	century = RTC_DC_C(dc);
+	if (century >= 19)
+		return 0;
+
+	dev_err(dev, "invalid year(%d), reset to year %d\n",
+		century * 100 + RTC_YMD_Y(ymd), default_year);
+
+	century = default_year / 100;
+	year_in_century = default_year % 100;
+
+	/* Stop RTC while updating the TC registers */
+	atc260x_reg_wp_setbits(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_RTCE, 0);
+
+	atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_DC,
+		RTC_DC_VAL(RTC_DC_D(dc), century));
+
+	atc260x_reg_write(atc260x, atc260x_rtc->regbase+REG_RTC_YMD,
+		RTC_YMD_VAL(year_in_century, RTC_YMD_M(ymd), RTC_YMD_D(ymd)));
+
+	/* Start RTC */
+	atc260x_reg_wp_clrpnd(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_ALIP);
+	atc260x_reg_wp_setbits(atc260x, atc260x_rtc->regbase+REG_RTC_CTL, RTC_CTL_ALIP, RTC_CTL_RTCE, RTC_CTL_RTCE);
+
+	return 0;
+}
+
+/* init RTC hardware */
+static int atc260x_rtc_hw_init(struct atc260x_rtc_dev *atc260x_rtc)
+{
+	struct atc260x_dev *atc260x = atc260x_rtc->atc260x;
+	int ctl;
+
+	/* check if external osc is available  */
+	ctl = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_CTL);
+	if (!(ctl & RTC_CTL_EXT_LOSC_STATUS)) {
+		atc260x_reg_wp_setbits(atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+			RTC_CTL_ALIP, RTC_CTL_RTCE | RTC_CTL_EOSC, RTC_CTL_RTCE | RTC_CTL_EOSC);
+
+		/* wait external osc ready */
+		udelay(100);
+		ctl = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_CTL);
+	}
+
+	if (ctl & RTC_CTL_EXT_LOSC_STATUS) {
+		/* choose external osc */
+		atc260x_reg_wp_setbits(atc260x, atc260x_rtc->regbase+REG_RTC_CTL,
+			RTC_CTL_ALIP, RTC_CTL_RTCE | RTC_CTL_CKSS0, RTC_CTL_RTCE | RTC_CTL_CKSS0);
+		udelay(100);
+	}
+
+	return 0;
+}
+
+static ssize_t atc260x_rtc_sysfs_show_ext_osc(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct rtc_device *rtc = to_rtc_device(dev);
+	struct atc260x_rtc_dev *atc260x_rtc = dev_get_drvdata(rtc->dev.parent);
+	int ret;
+
+	ret = atc260x_reg_read(atc260x_rtc->atc260x, atc260x_rtc->regbase+REG_RTC_CTL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read RTC control: ret=%d\n", ret);
+		return ret;
+	}
+
+	if (ret & RTC_CTL_CKSS0)
+		buf[0] = '1';
+	else
+		buf[0] = '0';
+	buf[1] = '\n';
+	buf[2] = 0;
+	return 2;
+}
+static DEVICE_ATTR(ext_osc, S_IRUGO, atc260x_rtc_sysfs_show_ext_osc, NULL);
+
+static int atc260x_rtc_probe(struct platform_device *pdev)
+{
+	struct atc260x_dev *atc260x;
+	struct atc260x_rtc_dev *atc260x_rtc;
+	uint ic_type;
+	int alm_irq, ret = 0;
+
+	dev_info(&pdev->dev, "Probing...\n");
+
+	atc260x = atc260x_get_parent_dev(&pdev->dev);
+
+	atc260x_rtc = devm_kzalloc(&pdev->dev, sizeof(*atc260x_rtc), GFP_KERNEL);
+	if (atc260x_rtc == NULL)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, atc260x_rtc);
+	atc260x_rtc->atc260x = atc260x;
+
+	/* select IC type. */
+	ic_type = atc260x_get_ic_type(atc260x);
+	switch (ic_type) {
+	case ATC260X_ICTYPE_2603A:
+		atc260x_rtc->regbase = ATC2603A_RTC_CTL;
+		break;
+	case ATC260X_ICTYPE_2603C:
+		atc260x_rtc->regbase = ATC2603C_RTC_CTL;
+		break;
+	case ATC260X_ICTYPE_2609A:
+		atc260x_rtc->regbase = ATC2609A_RTC_CTL;
+		break;
+	default :
+		dev_dbg(&pdev->dev, "%s() unsupport IC_TYPE: 0x%x\n",
+			__func__, ic_type);
+		goto err;
+	}
+
+	ret = atc260x_reg_read(atc260x, atc260x_rtc->regbase+REG_RTC_CTL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
+		goto err;
+	}
+	if (ret & RTC_CTL_ALIE)
+		atc260x_rtc->alarm_enabled = 1;
+
+	atc260x_rtc_hw_init(atc260x_rtc);
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	atc260x_rtc->rtc = rtc_device_register("atc260x-RTC", &pdev->dev,
+						  &atc260x_rtc_ops, THIS_MODULE);
+	if (IS_ERR(atc260x_rtc->rtc)) {
+		ret = PTR_ERR(atc260x_rtc->rtc);
+		goto err;
+	}
+
+	/* check default start year */
+	check_default_year(atc260x_rtc, RTC_DEFAULT_START_YEAR);
+
+	alm_irq = platform_get_irq(pdev, 0);
+	dev_info(&pdev->dev, "RTC alarm IRQ num : %u\n", alm_irq);
+	ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL, atc260x_alm_irq,
+				   IRQF_TRIGGER_HIGH, "RTC alarm",
+				   atc260x_rtc);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+			alm_irq, ret);
+	}
+	atc260x_rtc->alm_irq = alm_irq;
+
+	ret = device_create_file(&atc260x_rtc->rtc->dev, &dev_attr_ext_osc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to create ext_osc attribute, ret=%d\n", ret);
+	}
+
+	return 0;
+
+err:
+	platform_set_drvdata(pdev, NULL);
+	return ret;
+}
+
+static int atc260x_rtc_remove(struct platform_device *pdev)
+{
+	struct atc260x_rtc_dev *atc260x_rtc = platform_get_drvdata(pdev);
+
+	device_remove_file(&atc260x_rtc->rtc->dev, &dev_attr_ext_osc);
+	devm_free_irq(&pdev->dev, atc260x_rtc->alm_irq, atc260x_rtc);
+	rtc_device_unregister(atc260x_rtc->rtc);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct dev_pm_ops atc260x_rtc_pm_ops = {
+	.suspend = atc260x_rtc_suspend,
+	.resume = atc260x_rtc_resume,
+
+	.freeze = atc260x_rtc_freeze,
+	.thaw = atc260x_rtc_resume,
+	.restore = atc260x_rtc_resume,
+
+	.poweroff = atc260x_rtc_suspend,
+};
+
+
+static const struct of_device_id atc260x_rtc_match[] = {
+	{ .compatible = "actions,atc2603a-rtc", },
+	{ .compatible = "actions,atc2603c-rtc", },
+	{ .compatible = "actions,atc2609a-rtc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, atc260x_rtc_match);
+
+static struct platform_driver atc260x_rtc_driver = {
+	.driver = {
+		.name = "atc260x-rtc",
+		.pm = &atc260x_rtc_pm_ops,
+		.of_match_table = of_match_ptr(atc260x_rtc_match),
+	},
+	.probe = atc260x_rtc_probe,
+	.remove = atc260x_rtc_remove,
+};
+
+module_platform_driver(atc260x_rtc_driver)
+
+/*static int __init atc260x_rtc_init(void) */
+/*{ */
+/*  printk("%s() %d\n", __FUNCTION__, __LINE__); */
+/*    return platform_driver_register(&atc260x_rtc_driver); */
+/*} */
+/*subsys_initcall(atc260x_rtc_init); */
+/*static void __exit atc260x_rtc_exit(void) */
+/*{ */
+/*    platform_driver_unregister(&atc260x_rtc_driver); */
+/*} */
+/*module_exit(atc260x_rtc_exit); */
+
+/* Module information */
+MODULE_AUTHOR("Actions Semi, Inc");
+MODULE_DESCRIPTION("RTC driver for ATC260X");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atc260x-rtc");
diff --git a/include/linux/mfd/atc260x/atc260x.h b/include/linux/mfd/atc260x/atc260x.h
new file mode 100755
index 0000000..85a0ba5
--- /dev/null
+++ b/include/linux/mfd/atc260x/atc260x.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2011 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+/*  UTF-8 encoded.  */
+
+/* 这里给出的大部分API都是给子设备使用的, 名字均以 atc260x_ 开头, 均需要提供父设备device指针,
+ * 另外有少量API供外部其它模块使用, 名字以 atc260x_ex_ 开头,
+ * 这样做主要是为了避免原本内部使用的API被外部滥用, 多IC兼容时造成麻烦.
+ * 设计上要做到: 子设备之外的其它模块很难拿到atc260x_dev的指针. */
+
+
+/* atc260x 寄存器归属说明:
+ * 1. 各个子模块的寄存器归各自所有
+ * 2. CMU模块的寄存器归core所有, 各个子设备不能直接操作, core有接口
+ * 3. INT模块的寄存器归core/regmap所有, 各个子设备不能直接操作, 注册ISR(request_irq)时会自动配置
+ * 4. PMU-AUXADC的寄存器归core所有, ..., core有接口
+ * 5. PMU中不掉电不reset的FW用寄存器归core所有, ..., core有接口
+ * 6. MFP 原本是公有的, 但是由于配置甚少, 各个子设备自行维护. */
+
+
+
+#ifndef __MFD_ATC260X_H__
+#define __MFD_ATC260X_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <linux/mfd/atc260x/regs_map_atc2603a.h>
+#include <linux/mfd/atc260x/regs_map_atc2603c.h>
+#include <linux/mfd/atc260x/regs_map_atc2609a.h>
+
+enum {
+	/* DO NOT change the order!
+	 * 进suspend的最后一段汇编代码依赖于这里的顺序来区分IC. */
+	ATC260X_ICTYPE_2603A = 0,
+	ATC260X_ICTYPE_2603C,
+	ATC260X_ICTYPE_2609A,
+	ATC260X_ICTYPE_CNT
+};
+
+enum {
+	ATC260X_ICVER_A = 0,
+	ATC260X_ICVER_B,
+	ATC260X_ICVER_C,
+	ATC260X_ICVER_D,
+	ATC260X_ICVER_E,
+	ATC260X_ICVER_F,
+	ATC260X_ICVER_G,
+	ATC260X_ICVER_H,
+};
+
+struct atc260x_dev;
+
+/* Get atc260x parent device structure. For sub-device used only ! */
+extern struct atc260x_dev *atc260x_get_parent_dev(struct device *sub_dev);
+
+extern void atc260x_get_bus_info(struct atc260x_dev *atc260x, uint *bus_num, uint *bus_addr); /* for atc260x-pm only! */
+extern uint atc260x_get_ic_type(struct atc260x_dev *atc260x); /* see ATC260X_ICTYPE_2603A ... */
+extern uint atc260x_get_ic_ver(struct atc260x_dev *atc260x);  /* see ATC260X_ICVER_A ... */
+
+
+
+/* register read/ write ----------------------------------------------------- */
+
+/* ATC260X register interface.  For sub-device used only ! */
+
+/* 注意:
+ * 1. 尽量使用atc260x_reg_setbits接口, 这样可以保证寄存器的读-改-写操作是原子的.
+ *    单独使用atc260x_reg_read和atc260x_reg_write来实现读-改-写操作, 若寄存器是共用的,
+ *    就会有竞争问题.
+ * 2. atc260x_reg_setbits 只有内容有更改时才写入, 所以不能用来清pending位(写1清0的位),
+ *    再者, 用setbits接口来清pending也会意外将mask之外的别的已经为1的pending位清掉.
+ * 3. IC有时会有正常的bit与写1清0的bit混放到一个寄存器里边的情况(设计失误?),
+ *    操作这类寄存器需要特别注意, 因为改正常bits的read-modify-write操作会意外地将其它的
+ *    已经置1的pending位清掉,
+ *    操作这样的寄存器请使用 atc260x_reg_wp_xxx 的接口. */
+
+extern int atc260x_reg_read(struct atc260x_dev *atc260x, uint reg);
+extern int atc260x_reg_write(struct atc260x_dev *atc260x, uint reg, u16 val);
+extern int atc260x_reg_setbits(struct atc260x_dev *atc260x, uint reg, u16 mask, u16 val);
+
+/* 兼容旧版本的接口. */
+static inline int atc260x_set_bits(struct atc260x_dev *atc260x, uint reg, u16 mask, u16 val)
+{
+	return atc260x_reg_setbits(atc260x, reg, mask, val);
+}
+
+/* 读写 "正常的bit与写1清0的bit混放" 的寄存器的接口. */
+/* reg_wp_setbits 用于修改其中的"正常"的bits */
+static inline int atc260x_reg_wp_setbits(struct atc260x_dev *atc260x,
+	uint reg, u16 all_pnd_mask, u16 mask, u16 val)
+{
+	/* 保证所有pending都写0 */
+	return atc260x_reg_setbits(atc260x, reg, (all_pnd_mask | mask), (val & mask & ~all_pnd_mask));
+}
+/* reg_wp_clrpnd 用于清pending */
+static inline int atc260x_reg_wp_clrpnd(struct atc260x_dev *atc260x,
+	uint reg, u16 all_pnd_mask, u16 clr_mask)
+{
+	uint val;
+	int ret = atc260x_reg_read(atc260x, reg);
+	if (ret < 0)
+		return ret;
+	val = ((uint)ret & ~all_pnd_mask) | clr_mask;
+	return atc260x_reg_write(atc260x, reg, val);
+}
+
+/* 切换到direct access模式的接口, for atc260x-pm only! */
+extern void atc260x_set_reg_direct_access(struct atc260x_dev *atc260x, bool enable);
+
+
+
+/* for module clock & reset ------------------------------------------------- */
+
+/* 模块clock开关 & reset API */
+
+/* DO NOT change the order!!! */
+#define ATC260X_CMU_MODULE_NUM           (6) /* CMU module count */
+#define ATC260X_CMU_MODULE_TP            (0)
+#define ATC260X_CMU_MODULE_MFP           (1)
+#define ATC260X_CMU_MODULE_INTS          (2)
+#define ATC260X_CMU_MODULE_ETHPHY        (3)
+#define ATC260X_CMU_MODULE_AUDIO         (4)
+#define ATC260X_CMU_MODULE_PWSI          (5)
+
+/* module reset controll */
+extern int atc260x_cmu_reset(struct atc260x_dev *atc260x, uint cmu_module);
+/* module clock enable/disable controll */
+extern int atc260x_cmu_clk_ctrl(struct atc260x_dev *atc260x, uint cmu_module, uint clk_en);
+
+static inline int atc260x_cmu_enable(struct atc260x_dev *atc260x, uint cmu_module)
+{
+	return atc260x_cmu_clk_ctrl(atc260x, cmu_module, 1);
+}
+static inline int atc260x_cmu_disable(struct atc260x_dev *atc260x, uint cmu_module)
+{
+	return atc260x_cmu_clk_ctrl(atc260x, cmu_module, 0);
+}
+
+
+
+/* for auxadc --------------------------------------------------------------- */
+
+/* 从名字获得通道号. N个IC兼容的缘故,通道号不是固定的.  返回 <0 表示错误. */
+extern int atc260x_auxadc_find_chan(struct atc260x_dev *atc260x, const char *channel_name);
+/* 返回通道的名字, 错误时返回NULL */
+extern const char *atc260x_auxadc_channel_name(struct atc260x_dev *atc260x, uint channel);
+/* 返回通道的计量单位(如"mV"), 错误时返回NULL */
+extern const char *atc260x_auxadc_channel_unit_name(struct atc260x_dev *atc260x, uint channel);
+/* 获取ADC原始值(从ADC数据寄存器上读到的值), 返回 <0 表示错误. */
+extern int atc260x_auxadc_get_raw(struct atc260x_dev *atc260x, uint channel);
+/* 获取ADC转换后的值, 返回 0表示OK, 返回 <0 表示错误.
+ * 结果放在 *p_tr_value, 是带符号的. 度量一般是标准单位的千分之一, 如BATV的单位是mV. */
+extern int atc260x_auxadc_get_translated(struct atc260x_dev *atc260x, uint channel, s32 *p_tr_value);
+
+/* N个IC兼容的缘故, 通道数目&编号不是固定的, 这里的通道号与IC spec上的AuxADC通道编号一致.
+ * 各个子设备应该自己判断不同IC对应的通道号,
+ * 或在驱动probe时使用atc260x_auxadc_find_chan查找所需通道号, 而不应该继续使用宏定义.
+ *
+ * 各个IC的可用 channel_name :
+ * atc2603a :
+ *    "BATV", "BATI", "VBUSV", "VBUSI", "SYSPWRV", "WALLV", "WALLI", "CHGI",
+ *    "IREF", "REMCON", "ICTEMP", "BAKBATV", "AUX0", "AUX1", "AUX2", "AUX3"
+ * atc2603c :
+ *    "BATV", "BATI", "VBUSV", "VBUSI", "SYSPWRV", "WALLV", "WALLI", "CHGI",
+ *    "IREF", "REMCON", "ICTEMP", "BAKBATV", "AUX0", "AUX1", "AUX2", "ICM",
+ *    "SVCC"
+ * atc2609a :
+ *    "IREF", "CHGI", "VBUSI", "WALLI", "BATI", "REMCON", "ICTEMP", "SVCC",
+ *    "BAKBATV", "SYSPWRV", "WALLV", "VBUSV", "AUX3", "AUX2", "AUX1", "AUX0"
+ * */
+
+
+
+/* for persistent storage --------------------------------------------------- */
+
+/* 统一管理260x中几个不掉电/不复位的FW用寄存器, 避免过度自由分配造成问题. */
+/* 不同的PMIC中各个域的分配位置是不同的, 建议统一使用这套接口, 各自处理会带来不必要的麻烦. */
+/* pstore保证在量产后, 其管理的bits初值为0. */
+/* 这里的API是给子设备用的, 外部的ko请用 atc260x_ex_ 的版本. */
+
+enum {
+	ATC260X_PSTORE_TAG_REBOOT_ADFU = 0,     /* 重启进ADFU标志. */
+	ATC260X_PSTORE_TAG_REBOOT_RECOVERY,     /* 重启进recovery标志. */
+	ATC260X_PSTORE_TAG_FW_S2,               /* 软件S2标志 */
+	ATC260X_PSTORE_TAG_DIS_MCHRG,           /* 重启不进mini_charger */
+	ATC260X_PSTORE_TAG_RTC_MSALM,           /* RTC Alarm 备份区, reboot/suspend会用到. */
+	ATC260X_PSTORE_TAG_RTC_HALM,            /* RTC Alarm 备份区 */
+	ATC260X_PSTORE_TAG_RTC_YMDALM,          /* RTC Alarm 备份区 */
+	ATC260X_PSTORE_TAG_GAUGE_CAP,           /* 保存软电量计电量 (8bits) */
+	ATC260X_PSTORE_TAG_GAUGE_BAT_RES,       /* 保存软电量计推算的电池内阻 (16bits) */
+	ATC260X_PSTORE_TAG_GAUGE_ICM_EXIST,     /* 电量计是否使用ICM功能 (1bit) */
+	ATC260X_PSTORE_TAG_GAUGE_SHDWN_TIME,    /* 保存关机时间,电量计内部使用 (31bits) */
+	ATC260X_PSTORE_TAG_GAUGE_S2_CONSUMP,    /* 电量计用, 记录S2期间的功耗. (6bits) */
+	ATC260X_PSTORE_TAG_GAUGE_CLMT_RESET,    /* 电量计用, coulomb_meter复位标记 (1bit) */
+	ATC260X_PSTORE_TAG_RESUME_ADDR,         /* S2 resume address (32bit) */
+	ATC260X_PSTORE_TAG_NUM
+};
+
+/* write pstore TAG */
+extern int atc260x_pstore_set(struct atc260x_dev *atc260x, uint tag, u32 value);
+/* read pstore TAG */
+extern int atc260x_pstore_get(struct atc260x_dev *atc260x, uint tag, u32 *p_value);
+/* clear all pstore TAG (set to zero), call after upgrade process */
+extern int atc260x_pstore_reset_all(struct atc260x_dev *atc260x);
+
+
+
+/* External API ------------------------------------------------------------- */
+
+/* 提供外部使用的API */
+
+extern int atc260x_ex_auxadc_find_chan(const char *channel_name);
+extern int atc260x_ex_auxadc_read(uint channel, s32 *p_tr_value); /* return error code! */
+static inline int atc260x_ex_auxadc_read_by_name(const char *channel_name, s32 *p_tr_value) /* return error code! */
+{
+	int channel = atc260x_ex_auxadc_find_chan(channel_name);
+	if (channel < 0)
+		return channel;
+	return atc260x_ex_auxadc_read(channel, p_tr_value);
+}
+
+extern int atc260x_ex_pstore_set(uint tag, u32 value);
+extern int atc260x_ex_pstore_get(uint tag, u32 *p_value);
+
+
+
+/* misc --------------------------------------------------------------------- */
+
+/* 主要是power那边导出的接口, 临时放置于此. */
+
+int atc260x_enable_vbusotg(int on);
+
+enum {
+	DEV_CHARGER_CURRENT_LCD = 0,
+	DEV_CHARGER_CURRENT_CAMERA,
+	DEV_CHARGER_CURRENT_WIFI,
+	DEV_CHARGER_CURRENT_MAX,
+};
+
+enum {
+	DEV_CHARGER_PRE_CONFIG = 0,
+	DEV_CHARGER_POST_CONFIG,
+};
+
+extern void config_inner_charger_current(int pre_post, int dev_type, int param);
+extern void pmic_charger_set_fun(void (*funp)(int, int, int));
+
+
+#endif /* __MFD_ATC260X_H__ */
diff --git a/include/linux/mfd/atc260x/regs_map_atc2603a.h b/include/linux/mfd/atc260x/regs_map_atc2603a.h
new file mode 100755
index 0000000..3f5358b
--- /dev/null
+++ b/include/linux/mfd/atc260x/regs_map_atc2603a.h
@@ -0,0 +1,209 @@
+/*
+ * ATC2603A Spec Version_V1.0
+ */
+
+#ifndef __ATC2603A_REG_DEFINITION_H___
+#define __ATC2603A_REG_DEFINITION_H___
+
+/* PMU Register Address */
+#define ATC2603A_PMU_BASE			(0x0)
+#define ATC2603A_PMU_SYS_CTL0			(ATC2603A_PMU_BASE + 0x00)
+#define ATC2603A_PMU_SYS_CTL1			(ATC2603A_PMU_BASE + 0x01)
+#define ATC2603A_PMU_SYS_CTL2			(ATC2603A_PMU_BASE + 0x02)
+#define ATC2603A_PMU_SYS_CTL3			(ATC2603A_PMU_BASE + 0x03)
+#define ATC2603A_PMU_SYS_CTL4			(ATC2603A_PMU_BASE + 0x04)
+#define ATC2603A_PMU_SYS_CTL5			(ATC2603A_PMU_BASE + 0x05)
+#define ATC2603A_PMU_SYS_CTL6			(ATC2603A_PMU_BASE + 0x06)
+#define ATC2603A_PMU_SYS_CTL7			(ATC2603A_PMU_BASE + 0x07)
+#define ATC2603A_PMU_SYS_CTL8			(ATC2603A_PMU_BASE + 0x08)
+#define ATC2603A_PMU_SYS_CTL9			(ATC2603A_PMU_BASE + 0x09)
+#define ATC2603A_PMU_BAT_CTL0			(ATC2603A_PMU_BASE + 0x0A)
+#define ATC2603A_PMU_BAT_CTL1			(ATC2603A_PMU_BASE + 0x0B)
+#define ATC2603A_PMU_VBUS_CTL0			(ATC2603A_PMU_BASE + 0x0C)
+#define ATC2603A_PMU_VBUS_CTL1			(ATC2603A_PMU_BASE + 0x0D)
+#define ATC2603A_PMU_WALL_CTL0			(ATC2603A_PMU_BASE + 0x0E)
+#define ATC2603A_PMU_WALL_CTL1			(ATC2603A_PMU_BASE + 0x0F)
+#define ATC2603A_PMU_SYS_PENDING		(ATC2603A_PMU_BASE + 0x10)
+#define ATC2603A_PMU_DC1_CTL0			(ATC2603A_PMU_BASE + 0x11)
+#define ATC2603A_PMU_DC1_CTL1			(ATC2603A_PMU_BASE + 0x12)
+#define ATC2603A_PMU_DC1_CTL2			(ATC2603A_PMU_BASE + 0x13)
+#define ATC2603A_PMU_DC2_CTL0			(ATC2603A_PMU_BASE + 0x14)
+#define ATC2603A_PMU_DC2_CTL1			(ATC2603A_PMU_BASE + 0x15)
+#define ATC2603A_PMU_DC2_CTL2			(ATC2603A_PMU_BASE + 0x16)
+#define ATC2603A_PMU_DC3_CTL0			(ATC2603A_PMU_BASE + 0x17)
+#define ATC2603A_PMU_DC3_CTL1			(ATC2603A_PMU_BASE + 0x18)
+#define ATC2603A_PMU_DC3_CTL2			(ATC2603A_PMU_BASE + 0x19)
+#define ATC2603A_PMU_DC4_CTL0			(ATC2603A_PMU_BASE + 0x1A)
+#define ATC2603A_PMU_DC4_CTL1			(ATC2603A_PMU_BASE + 0x1B)
+#define ATC2603A_PMU_DC5_CTL0			(ATC2603A_PMU_BASE + 0x1C)
+#define ATC2603A_PMU_DC5_CTL1			(ATC2603A_PMU_BASE + 0x1D)
+#define ATC2603A_PMU_LDO1_CTL			(ATC2603A_PMU_BASE + 0x1E)
+#define ATC2603A_PMU_LDO2_CTL			(ATC2603A_PMU_BASE + 0x1F)
+#define ATC2603A_PMU_LDO3_CTL			(ATC2603A_PMU_BASE + 0x20)
+#define ATC2603A_PMU_LDO4_CTL			(ATC2603A_PMU_BASE + 0x21)
+#define ATC2603A_PMU_LDO5_CTL			(ATC2603A_PMU_BASE + 0x22)
+#define ATC2603A_PMU_LDO6_CTL			(ATC2603A_PMU_BASE + 0x23)
+#define ATC2603A_PMU_LDO7_CTL			(ATC2603A_PMU_BASE + 0x24)
+#define ATC2603A_PMU_LDO8_CTL			(ATC2603A_PMU_BASE + 0x25)
+#define ATC2603A_PMU_LDO9_CTL			(ATC2603A_PMU_BASE + 0x26)
+#define ATC2603A_PMU_LDO10_CTL			(ATC2603A_PMU_BASE + 0x27)
+#define ATC2603A_PMU_LDO11_CTL			(ATC2603A_PMU_BASE + 0x28)
+#define ATC2603A_PMU_SWITCH_CTL			(ATC2603A_PMU_BASE + 0x29)
+#define ATC2603A_PMU_OV_CTL0			(ATC2603A_PMU_BASE + 0x2A)
+#define ATC2603A_PMU_OV_CTL1			(ATC2603A_PMU_BASE + 0x2B)
+#define ATC2603A_PMU_OV_STATUS			(ATC2603A_PMU_BASE + 0x2C)
+#define ATC2603A_PMU_OV_EN			(ATC2603A_PMU_BASE + 0x2D)
+#define ATC2603A_PMU_OV_INT_EN			(ATC2603A_PMU_BASE + 0x2E)
+#define ATC2603A_PMU_OC_CTL			(ATC2603A_PMU_BASE + 0x2F)
+#define ATC2603A_PMU_OC_STATUS			(ATC2603A_PMU_BASE + 0x30)
+#define ATC2603A_PMU_OC_EN			(ATC2603A_PMU_BASE + 0x31)
+#define ATC2603A_PMU_OC_INT_EN			(ATC2603A_PMU_BASE + 0x32)
+#define ATC2603A_PMU_UV_CTL0			(ATC2603A_PMU_BASE + 0x33)
+#define ATC2603A_PMU_UV_CTL1			(ATC2603A_PMU_BASE + 0x34)
+#define ATC2603A_PMU_UV_STATUS			(ATC2603A_PMU_BASE + 0x35)
+#define ATC2603A_PMU_UV_EN			(ATC2603A_PMU_BASE + 0x36)
+#define ATC2603A_PMU_UV_INT_EN			(ATC2603A_PMU_BASE + 0x37)
+#define ATC2603A_PMU_OT_CTL			(ATC2603A_PMU_BASE + 0x38)
+#define ATC2603A_PMU_CHARGER_CTL0		(ATC2603A_PMU_BASE + 0x39)
+#define ATC2603A_PMU_CHARGER_CTL1		(ATC2603A_PMU_BASE + 0x3A)
+#define ATC2603A_PMU_CHARGER_CTL2		(ATC2603A_PMU_BASE + 0x3B)
+#define ATC2603A_PMU_BAKCHARGER_CTL		(ATC2603A_PMU_BASE + 0x3C)
+#define ATC2603A_PMU_APDS_CTL			(ATC2603A_PMU_BASE + 0x3D)
+#define ATC2603A_PMU_AUXADC_CTL0		(ATC2603A_PMU_BASE + 0x3E)
+#define ATC2603A_PMU_AUXADC_CTL1		(ATC2603A_PMU_BASE + 0x3F)
+#define ATC2603A_PMU_BATVADC			(ATC2603A_PMU_BASE + 0x40)
+#define ATC2603A_PMU_BATIADC			(ATC2603A_PMU_BASE + 0x41)
+#define ATC2603A_PMU_WALLVADC			(ATC2603A_PMU_BASE + 0x42)
+#define ATC2603A_PMU_WALLIADC			(ATC2603A_PMU_BASE + 0x43)
+#define ATC2603A_PMU_VBUSVADC			(ATC2603A_PMU_BASE + 0x44)
+#define ATC2603A_PMU_VBUSIADC			(ATC2603A_PMU_BASE + 0x45)
+#define ATC2603A_PMU_SYSPWRADC			(ATC2603A_PMU_BASE + 0x46)
+#define ATC2603A_PMU_REMCONADC			(ATC2603A_PMU_BASE + 0x47)
+#define ATC2603A_PMU_SVCCADC			(ATC2603A_PMU_BASE + 0x48)
+#define ATC2603A_PMU_CHGIADC			(ATC2603A_PMU_BASE + 0x49)
+#define ATC2603A_PMU_IREFADC			(ATC2603A_PMU_BASE + 0x4A)
+#define ATC2603A_PMU_BAKBATADC			(ATC2603A_PMU_BASE + 0x4B)
+#define ATC2603A_PMU_ICTEMPADC			(ATC2603A_PMU_BASE + 0x4C)
+#define ATC2603A_PMU_AUXADC0			(ATC2603A_PMU_BASE + 0x4D)
+#define ATC2603A_PMU_AUXADC1			(ATC2603A_PMU_BASE + 0x4E)
+#define ATC2603A_PMU_AUXADC2			(ATC2603A_PMU_BASE + 0x4F)
+#define ATC2603A_PMU_AUXADC3			(ATC2603A_PMU_BASE + 0x50)
+#define ATC2603A_PMU_BDG_CTL			(ATC2603A_PMU_BASE + 0x51)
+#define ATC2603A_RTC_CTL			(ATC2603A_PMU_BASE + 0x52)
+#define ATC2603A_RTC_MSALM			(ATC2603A_PMU_BASE + 0x53)
+#define ATC2603A_RTC_HALM			(ATC2603A_PMU_BASE + 0x54)
+#define ATC2603A_RTC_YMDALM			(ATC2603A_PMU_BASE + 0x55)
+#define ATC2603A_RTC_MS				(ATC2603A_PMU_BASE + 0x56)
+#define ATC2603A_RTC_H				(ATC2603A_PMU_BASE + 0x57)
+#define ATC2603A_RTC_DC				(ATC2603A_PMU_BASE + 0x58)
+#define ATC2603A_RTC_YMD			(ATC2603A_PMU_BASE + 0x59)
+#define ATC2603A_EFUSE_DAT			(ATC2603A_PMU_BASE + 0x5A)
+#define ATC2603A_EFUSECRTL1			(ATC2603A_PMU_BASE + 0x5B)
+#define ATC2603A_EFUSECRTL2			(ATC2603A_PMU_BASE + 0x5C)
+#define ATC2603A_IRC_CTL			(ATC2603A_PMU_BASE + 0x60)
+#define ATC2603A_IRC_STAT			(ATC2603A_PMU_BASE + 0x61)
+#define ATC2603A_IRC_CC				(ATC2603A_PMU_BASE + 0x62)
+#define ATC2603A_IRC_KDC			(ATC2603A_PMU_BASE + 0x63)
+#define ATC2603A_IRC_WK				(ATC2603A_PMU_BASE + 0x64)
+
+/* CMU_CONTROL Register Address */
+#define ATC2603A_CMU_CONTROL_BASE		(0x100)
+#define ATC2603A_CMU_HOSCCTL			(ATC2603A_CMU_CONTROL_BASE + 0x00)
+#define ATC2603A_CMU_DEVRST			(ATC2603A_CMU_CONTROL_BASE + 0x01)
+
+/* INTS Register Address */
+#define ATC2603A_INTS_BASE			(0x200)
+#define ATC2603A_INTS_PD			(ATC2603A_INTS_BASE + 0x00)
+#define ATC2603A_INTS_MSK			(ATC2603A_INTS_BASE + 0x01)
+
+/* MFP Register Address */
+#define ATC2603A_MFP_BASE			(0x300)
+#define ATC2603A_MFP_CTL0			(ATC2603A_MFP_BASE + 0x00)
+#define ATC2603A_MFP_CTL1			(ATC2603A_MFP_BASE + 0x01)
+#define ATC2603A_GPIO_OUTEN0			(ATC2603A_MFP_BASE + 0x10)
+#define ATC2603A_GPIO_OUTEN1			(ATC2603A_MFP_BASE + 0x11)
+#define ATC2603A_GPIO_INEN0			(ATC2603A_MFP_BASE + 0x12)
+#define ATC2603A_GPIO_INEN1			(ATC2603A_MFP_BASE + 0x13)
+#define ATC2603A_GPIO_DAT0			(ATC2603A_MFP_BASE + 0x14)
+#define ATC2603A_GPIO_DAT1			(ATC2603A_MFP_BASE + 0x15)
+#define ATC2603A_PAD_DRV0			(ATC2603A_MFP_BASE + 0x20)
+#define ATC2603A_PAD_DRV1			(ATC2603A_MFP_BASE + 0x21)
+#define ATC2603A_PAD_EN				(ATC2603A_MFP_BASE + 0x22)
+#define ATC2603A_DEBUG_SEL			(ATC2603A_MFP_BASE + 0x30)
+#define ATC2603A_DEBUG_IE			(ATC2603A_MFP_BASE + 0x31)
+#define ATC2603A_DEBUG_OE			(ATC2603A_MFP_BASE + 0x32)
+
+/* AUDIO_IN_OUT Register Address */
+#define ATC2603A_AUDIO_IN_OUT_BASE		(0x400)
+#define ATC2603A_AUDIOINOUT_CTL			(ATC2603A_AUDIO_IN_OUT_BASE + 0x00)
+#define ATC2603A_AUDIO_DEBUGOUTCTL		(ATC2603A_AUDIO_IN_OUT_BASE + 0x01)
+#define ATC2603A_DAC_FILTERCTL0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x02)
+#define ATC2603A_DAC_FILTERCTL1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x03)
+#define ATC2603A_DAC_DIGITALCTL			(ATC2603A_AUDIO_IN_OUT_BASE + 0x04)
+#define ATC2603A_DAC_VOLUMECTL0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x05)
+#define ATC2603A_DAC_VOLUMECTL1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x06)
+#define ATC2603A_DAC_VOLUMECTL2			(ATC2603A_AUDIO_IN_OUT_BASE + 0x07)
+#define ATC2603A_DAC_VOLUMECTL3			(ATC2603A_AUDIO_IN_OUT_BASE + 0x08)
+#define ATC2603A_DAC_ANANLOG0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x09)
+#define ATC2603A_DAC_ANANLOG1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x0A)
+#define ATC2603A_DAC_ANANLOG2			(ATC2603A_AUDIO_IN_OUT_BASE + 0x0B)
+#define ATC2603A_DAC_ANANLOG3			(ATC2603A_AUDIO_IN_OUT_BASE + 0x0C)
+#define ATC2603A_DAC_ANANLOG4			(ATC2603A_AUDIO_IN_OUT_BASE + 0x0D)
+#define ATC2603A_CLASSD_CTL0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x0E)
+#define ATC2603A_CLASSD_CTL1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x0F)
+#define ATC2603A_CLASSD_CTL2			(ATC2603A_AUDIO_IN_OUT_BASE + 0x10)
+#define ATC2603A_ADC0_DIGITALCTL		(ATC2603A_AUDIO_IN_OUT_BASE + 0x11)
+#define ATC2603A_ADC0_HPFCTL			(ATC2603A_AUDIO_IN_OUT_BASE + 0x12)
+#define ATC2603A_ADC0_CTL			(ATC2603A_AUDIO_IN_OUT_BASE + 0x13)
+#define ATC2603A_AGC0_CTL0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x14)
+#define ATC2603A_AGC0_CTL1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x15)
+#define ATC2603A_AGC0_CTL2			(ATC2603A_AUDIO_IN_OUT_BASE + 0x16)
+#define ATC2603A_ADC_ANANLOG0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x17)
+#define ATC2603A_ADC_ANANLOG1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x18)
+#define ATC2603A_ADC1_DIGITALCTL		(ATC2603A_AUDIO_IN_OUT_BASE + 0x19)
+#define ATC2603A_ADC1_CTL			(ATC2603A_AUDIO_IN_OUT_BASE + 0x1A)
+#define ATC2603A_AGC1_CTL0			(ATC2603A_AUDIO_IN_OUT_BASE + 0x1B)
+#define ATC2603A_AGC1_CTL1			(ATC2603A_AUDIO_IN_OUT_BASE + 0x1C)
+#define ATC2603A_AGC1_CTL2			(ATC2603A_AUDIO_IN_OUT_BASE + 0x1D)
+
+/* ETHERNET_PHY Register Address */
+#define ATC2603A_ETHERNET_PHY_BASE		(0x500)
+#define ATC2603A_PHY_SMI_STAT			(ATC2603A_ETHERNET_PHY_BASE + 0x00)
+#define ATC2603A_PHY_SMI_CONFIG			(ATC2603A_ETHERNET_PHY_BASE + 0x01)
+#define ATC2603A_PHY_SMI_DATA			(ATC2603A_ETHERNET_PHY_BASE + 0x02)
+#define ATC2603A_PHY_CTRL			(ATC2603A_ETHERNET_PHY_BASE + 0x03)
+#define ATC2603A_PHY_ID1			(ATC2603A_ETHERNET_PHY_BASE + 0x04)
+#define ATC2603A_PHY_ID2			(ATC2603A_ETHERNET_PHY_BASE + 0x05)
+#define ATC2603A_PHY_ADDR			(ATC2603A_ETHERNET_PHY_BASE + 0x06)
+#define ATC2603A_PHY_LED			(ATC2603A_ETHERNET_PHY_BASE + 0x07)
+#define ATC2603A_PHY_INT_CTRL			(ATC2603A_ETHERNET_PHY_BASE + 0x08)
+#define ATC2603A_PHY_INT_STAT			(ATC2603A_ETHERNET_PHY_BASE + 0x09)
+#define ATC2603A_PHY_HW_RST			(ATC2603A_ETHERNET_PHY_BASE + 0x0A)
+#define ATC2603A_PHY_CONFIG			(ATC2603A_ETHERNET_PHY_BASE + 0x0B)
+#define ATC2603A_PHY_PLL_CTL0			(ATC2603A_ETHERNET_PHY_BASE + 0x0C)
+#define ATC2603A_PHY_PLL_CTL1			(ATC2603A_ETHERNET_PHY_BASE + 0x0D)
+#define ATC2603A_PHY_DBG0			(ATC2603A_ETHERNET_PHY_BASE + 0x0E)
+#define ATC2603A_PHY_DBG1			(ATC2603A_ETHERNET_PHY_BASE + 0x0F)
+#define ATC2603A_PHY_DBG2			(ATC2603A_ETHERNET_PHY_BASE + 0x10)
+#define ATC2603A_PHY_DBG3			(ATC2603A_ETHERNET_PHY_BASE + 0x11)
+#define ATC2603A_PHY_DBG4			(ATC2603A_ETHERNET_PHY_BASE + 0x12)
+
+/* TP Register Address */
+#define ATC2603A_TP_CONTROLLER_BASE		(0x600)
+#define ATC2603A_TP_CTL0			(ATC2603A_TP_CONTROLLER_BASE + 0x00)
+#define ATC2603A_TP_CTL1			(ATC2603A_TP_CONTROLLER_BASE + 0x01)
+#define ATC2603A_TP_STATUS			(ATC2603A_TP_CONTROLLER_BASE + 0x02)
+#define ATC2603A_TP_XDAT			(ATC2603A_TP_CONTROLLER_BASE + 0x03)
+#define ATC2603A_TP_YDAT			(ATC2603A_TP_CONTROLLER_BASE + 0x04)
+#define ATC2603A_TP_Z1DAT			(ATC2603A_TP_CONTROLLER_BASE + 0x05)
+#define ATC2603A_TP_Z2DAT			(ATC2603A_TP_CONTROLLER_BASE + 0x06)
+
+/* TEST_CONTROL Register Address */
+#define ATC2603A_TEST_CONTROL_BASE		(0x700)
+#define ATC2603A_TEST_MODE_CFG			(ATC2603A_TEST_CONTROL_BASE + 0x00)
+#define ATC2603A_TEST_CFG_EN			(ATC2603A_TEST_CONTROL_BASE + 0x01)
+#define ATC2603A_AUDIO_MEM_BIST_CTL		(ATC2603A_TEST_CONTROL_BASE + 0x02)
+#define ATC2603A_AUDIO_MEM_BIST_RESULT		(ATC2603A_TEST_CONTROL_BASE + 0x03)
+#define ATC2603A_CVER				(ATC2603A_TEST_CONTROL_BASE + 0x04)
+
+#endif
diff --git a/include/linux/mfd/atc260x/regs_map_atc2603c.h b/include/linux/mfd/atc260x/regs_map_atc2603c.h
new file mode 100755
index 0000000..eb8fc70
--- /dev/null
+++ b/include/linux/mfd/atc260x/regs_map_atc2603c.h
@@ -0,0 +1,192 @@
+/*
+ * ATC2603C Spec Version_V1.03
+ */
+
+#ifndef __ATC2603C_REG_DEFINITION_H___
+#define __ATC2603C_REG_DEFINITION_H___
+
+/* PMU Register Address */
+#define ATC2603C_PMU_BASE			(0X00)
+#define ATC2603C_PMU_SYS_CTL0			(ATC2603C_PMU_BASE + 0x00)
+#define ATC2603C_PMU_SYS_CTL1			(ATC2603C_PMU_BASE + 0x01)
+#define ATC2603C_PMU_SYS_CTL2			(ATC2603C_PMU_BASE + 0x02)
+#define ATC2603C_PMU_SYS_CTL3			(ATC2603C_PMU_BASE + 0x03)
+#define ATC2603C_PMU_SYS_CTL4			(ATC2603C_PMU_BASE + 0x04)
+#define ATC2603C_PMU_SYS_CTL5			(ATC2603C_PMU_BASE + 0x05)
+#define ATC2603C_PMU_SYS_CTL6			(ATC2603C_PMU_BASE + 0x06)
+#define ATC2603C_PMU_SYS_CTL7			(ATC2603C_PMU_BASE + 0x07)
+#define ATC2603C_PMU_SYS_CTL8			(ATC2603C_PMU_BASE + 0x08)
+#define ATC2603C_PMU_SYS_CTL9			(ATC2603C_PMU_BASE + 0x09)
+#define ATC2603C_PMU_BAT_CTL0			(ATC2603C_PMU_BASE + 0x0A)
+#define ATC2603C_PMU_BAT_CTL1			(ATC2603C_PMU_BASE + 0x0B)
+#define ATC2603C_PMU_VBUS_CTL0			(ATC2603C_PMU_BASE + 0x0C)
+#define ATC2603C_PMU_VBUS_CTL1			(ATC2603C_PMU_BASE + 0x0D)
+#define ATC2603C_PMU_WALL_CTL0			(ATC2603C_PMU_BASE + 0x0E)
+#define ATC2603C_PMU_WALL_CTL1			(ATC2603C_PMU_BASE + 0x0F)
+#define ATC2603C_PMU_SYS_PENDING		(ATC2603C_PMU_BASE + 0x10)
+#define ATC2603C_PMU_DC1_CTL0			(ATC2603C_PMU_BASE + 0x11)
+#define ATC2603C_PMU_DC1_CTL1			(ATC2603C_PMU_BASE + 0x12)
+#define ATC2603C_PMU_DC1_CTL2			(ATC2603C_PMU_BASE + 0x13)
+#define ATC2603C_PMU_DC2_CTL0			(ATC2603C_PMU_BASE + 0x14)
+#define ATC2603C_PMU_DC2_CTL1			(ATC2603C_PMU_BASE + 0x15)
+#define ATC2603C_PMU_DC2_CTL2			(ATC2603C_PMU_BASE + 0x16)
+#define ATC2603C_PMU_DC3_CTL0			(ATC2603C_PMU_BASE + 0x17)
+#define ATC2603C_PMU_DC3_CTL1			(ATC2603C_PMU_BASE + 0x18)
+#define ATC2603C_PMU_DC3_CTL2			(ATC2603C_PMU_BASE + 0x19)
+#define ATC2603C_PMU_DC4_CTL0			(ATC2603C_PMU_BASE + 0x1A)
+#define ATC2603C_PMU_DC4_CTL1			(ATC2603C_PMU_BASE + 0x1B)
+#define ATC2603C_PMU_DC5_CTL0			(ATC2603C_PMU_BASE + 0x1C)
+#define ATC2603C_PMU_DC5_CTL1			(ATC2603C_PMU_BASE + 0x1D)
+#define ATC2603C_PMU_LDO1_CTL			(ATC2603C_PMU_BASE + 0x1E)
+#define ATC2603C_PMU_LDO2_CTL			(ATC2603C_PMU_BASE + 0x1F)
+#define ATC2603C_PMU_LDO3_CTL			(ATC2603C_PMU_BASE + 0x20)
+#define ATC2603C_PMU_LDO4_CTL			(ATC2603C_PMU_BASE + 0x21)
+#define ATC2603C_PMU_LDO5_CTL			(ATC2603C_PMU_BASE + 0x22)
+#define ATC2603C_PMU_LDO6_CTL			(ATC2603C_PMU_BASE + 0x23)
+#define ATC2603C_PMU_LDO7_CTL			(ATC2603C_PMU_BASE + 0x24)
+#define ATC2603C_PMU_LDO8_CTL			(ATC2603C_PMU_BASE + 0x25)
+#define ATC2603C_PMU_LDO9_CTL			(ATC2603C_PMU_BASE + 0x26)
+#define ATC2603C_PMU_LDO10_CTL			(ATC2603C_PMU_BASE + 0x27)
+#define ATC2603C_PMU_LDO11_CTL			(ATC2603C_PMU_BASE + 0x28)
+#define ATC2603C_PMU_SWITCH_CTL			(ATC2603C_PMU_BASE + 0x29)
+#define ATC2603C_PMU_OV_CTL0			(ATC2603C_PMU_BASE + 0x2A)
+#define ATC2603C_PMU_OV_CTL1			(ATC2603C_PMU_BASE + 0x2B)
+#define ATC2603C_PMU_OV_STATUS			(ATC2603C_PMU_BASE + 0x2C)
+#define ATC2603C_PMU_OV_EN			(ATC2603C_PMU_BASE + 0x2D)
+#define ATC2603C_PMU_OV_INT_EN			(ATC2603C_PMU_BASE + 0x2E)
+#define ATC2603C_PMU_OC_CTL			(ATC2603C_PMU_BASE + 0x2F)
+#define ATC2603C_PMU_OC_STATUS			(ATC2603C_PMU_BASE + 0x30)
+#define ATC2603C_PMU_OC_EN			(ATC2603C_PMU_BASE + 0x31)
+#define ATC2603C_PMU_OC_INT_EN			(ATC2603C_PMU_BASE + 0x32)
+#define ATC2603C_PMU_UV_CTL0			(ATC2603C_PMU_BASE + 0x33)
+#define ATC2603C_PMU_UV_CTL1			(ATC2603C_PMU_BASE + 0x34)
+#define ATC2603C_PMU_UV_STATUS			(ATC2603C_PMU_BASE + 0x35)
+#define ATC2603C_PMU_UV_EN			(ATC2603C_PMU_BASE + 0x36)
+#define ATC2603C_PMU_UV_INT_EN			(ATC2603C_PMU_BASE + 0x37)
+#define ATC2603C_PMU_OT_CTL			(ATC2603C_PMU_BASE + 0x38)
+#define ATC2603C_PMU_CHARGER_CTL0		(ATC2603C_PMU_BASE + 0x39)
+#define ATC2603C_PMU_CHARGER_CTL1		(ATC2603C_PMU_BASE + 0x3A)
+#define ATC2603C_PMU_CHARGER_CTL2		(ATC2603C_PMU_BASE + 0x3B)
+#define ATC2603C_PMU_BAKCHARGER_CTL		(ATC2603C_PMU_BASE + 0x3C)
+#define ATC2603C_PMU_APDS_CTL			(ATC2603C_PMU_BASE + 0x3D)
+#define ATC2603C_PMU_AUXADC_CTL0		(ATC2603C_PMU_BASE + 0x3E)
+#define ATC2603C_PMU_AUXADC_CTL1		(ATC2603C_PMU_BASE + 0x3F)
+#define ATC2603C_PMU_BATVADC			(ATC2603C_PMU_BASE + 0x40)
+#define ATC2603C_PMU_BATIADC			(ATC2603C_PMU_BASE + 0x41)
+#define ATC2603C_PMU_WALLVADC			(ATC2603C_PMU_BASE + 0x42)
+#define ATC2603C_PMU_WALLIADC			(ATC2603C_PMU_BASE + 0x43)
+#define ATC2603C_PMU_VBUSVADC			(ATC2603C_PMU_BASE + 0x44)
+#define ATC2603C_PMU_VBUSIADC			(ATC2603C_PMU_BASE + 0x45)
+#define ATC2603C_PMU_SYSPWRADC			(ATC2603C_PMU_BASE + 0x46)
+#define ATC2603C_PMU_REMCONADC			(ATC2603C_PMU_BASE + 0x47)
+#define ATC2603C_PMU_SVCCADC			(ATC2603C_PMU_BASE + 0x48)
+#define ATC2603C_PMU_CHGIADC			(ATC2603C_PMU_BASE + 0x49)
+#define ATC2603C_PMU_IREFADC			(ATC2603C_PMU_BASE + 0x4A)
+#define ATC2603C_PMU_BAKBATADC			(ATC2603C_PMU_BASE + 0x4B)
+#define ATC2603C_PMU_ICTEMPADC			(ATC2603C_PMU_BASE + 0x4C)
+#define ATC2603C_PMU_AUXADC0			(ATC2603C_PMU_BASE + 0x4D)
+#define ATC2603C_PMU_AUXADC1			(ATC2603C_PMU_BASE + 0x4E)
+#define ATC2603C_PMU_AUXADC2			(ATC2603C_PMU_BASE + 0x4F)
+#define	ATC2603C_PMU_ICMADC			(ATC2603C_PMU_BASE + 0x50)
+#define ATC2603C_PMU_BDG_CTL			(ATC2603C_PMU_BASE + 0x51)
+#define ATC2603C_RTC_CTL			(ATC2603C_PMU_BASE + 0x52)
+#define ATC2603C_RTC_MSALM			(ATC2603C_PMU_BASE + 0x53)
+#define ATC2603C_RTC_HALM			(ATC2603C_PMU_BASE + 0x54)
+#define ATC2603C_RTC_YMDALM			(ATC2603C_PMU_BASE + 0x55)
+#define ATC2603C_RTC_MS				(ATC2603C_PMU_BASE + 0x56)
+#define ATC2603C_RTC_H				(ATC2603C_PMU_BASE + 0x57)
+#define ATC2603C_RTC_DC				(ATC2603C_PMU_BASE + 0x58)
+#define ATC2603C_RTC_YMD			(ATC2603C_PMU_BASE + 0x59)
+#define ATC2603C_EFUSE_DAT			(ATC2603C_PMU_BASE + 0x5A)
+#define ATC2603C_EFUSECRTL1			(ATC2603C_PMU_BASE + 0x5B)
+#define ATC2603C_EFUSECRTL2			(ATC2603C_PMU_BASE + 0x5C)
+#define ATC2603C_PMU_FW_USE0			(ATC2603C_PMU_BASE + 0x5D)
+#define ATC2603C_PMU_FW_USE1			(ATC2603C_PMU_BASE + 0x5E)
+#define ATC2603C_PMU_FW_USE2			(ATC2603C_PMU_BASE + 0x5F)
+#define ATC2603C_PMU_FW_USE3			(ATC2603C_PMU_BASE + 0x60)
+#define ATC2603C_PMU_FW_USE4			(ATC2603C_PMU_BASE + 0x61)
+#define ATC2603C_PMU_ABNORMAL_STATUS		(ATC2603C_PMU_BASE + 0x62)
+#define ATC2603C_PMU_WALL_APDS_CTL		(ATC2603C_PMU_BASE + 0x63)
+#define ATC2603C_PMU_REMCON_CTL0		(ATC2603C_PMU_BASE + 0x64)
+#define ATC2603C_PMU_REMCON_CTL1		(ATC2603C_PMU_BASE + 0x65)
+#define ATC2603C_PMU_MUX_CTL0			(ATC2603C_PMU_BASE + 0x66)
+#define ATC2603C_PMU_SGPIO_CTL0			(ATC2603C_PMU_BASE + 0x67)
+#define ATC2603C_PMU_SGPIO_CTL1			(ATC2603C_PMU_BASE + 0x68)
+#define ATC2603C_PMU_SGPIO_CTL2			(ATC2603C_PMU_BASE + 0x69)
+#define ATC2603C_PMU_SGPIO_CTL3			(ATC2603C_PMU_BASE + 0x6A)
+#define ATC2603C_PMU_SGPIO_CTL4			(ATC2603C_PMU_BASE + 0x6B)
+#define ATC2603C_PWMCLK_CTL			(ATC2603C_PMU_BASE + 0x6C)
+#define ATC2603C_PWM0_CTL			(ATC2603C_PMU_BASE + 0x6D)
+#define ATC2603C_PWM1_CTL			(ATC2603C_PMU_BASE + 0x6E)
+#define ATC2603C_PMU_ADC_DBG0			(ATC2603C_PMU_BASE + 0x70)
+#define ATC2603C_PMU_ADC_DBG1			(ATC2603C_PMU_BASE + 0x71)
+#define ATC2603C_PMU_ADC_DBG2			(ATC2603C_PMU_BASE + 0x72)
+#define ATC2603C_PMU_ADC_DBG3			(ATC2603C_PMU_BASE + 0x73)
+#define ATC2603C_PMU_ADC_DBG4			(ATC2603C_PMU_BASE + 0x74)
+#define ATC2603C_IRC_CTL			(ATC2603C_PMU_BASE + 0x80)
+#define ATC2603C_IRC_STAT			(ATC2603C_PMU_BASE + 0x81)
+#define ATC2603C_IRC_CC				(ATC2603C_PMU_BASE + 0x82)
+#define ATC2603C_IRC_KDC			(ATC2603C_PMU_BASE + 0x83)
+#define ATC2603C_IRC_WK				(ATC2603C_PMU_BASE + 0x84)
+#define ATC2603C_IRC_RCC			(ATC2603C_PMU_BASE + 0x85)
+#define ATC2603C_IRC_FILTER			(ATC2603C_PMU_BASE + 0x86)
+
+/* AUDIO_OUT Register Address */
+#define ATC2603C_AUDIO_OUT_BASE			(0xA0)
+#define ATC2603C_AUDIOINOUT_CTL			(ATC2603C_AUDIO_OUT_BASE + 0x0)
+#define ATC2603C_AUDIO_DEBUGOUTCTL		(ATC2603C_AUDIO_OUT_BASE + 0x1)
+#define ATC2603C_DAC_DIGITALCTL			(ATC2603C_AUDIO_OUT_BASE + 0x2)
+#define ATC2603C_DAC_VOLUMECTL0			(ATC2603C_AUDIO_OUT_BASE + 0x3)
+#define ATC2603C_DAC_ANALOG0			(ATC2603C_AUDIO_OUT_BASE + 0x4)
+#define ATC2603C_DAC_ANALOG1			(ATC2603C_AUDIO_OUT_BASE + 0x5)
+#define ATC2603C_DAC_ANALOG2			(ATC2603C_AUDIO_OUT_BASE + 0x6)
+#define ATC2603C_DAC_ANALOG3			(ATC2603C_AUDIO_OUT_BASE + 0x7)
+
+/* AUDIO_IN Register Address */
+#define ATC2603C_AUDIO_IN_BASE			(0xA0)
+#define ATC2603C_ADC_DIGITALCTL			(ATC2603C_AUDIO_IN_BASE + 0x8)
+#define ATC2603C_ADC_HPFCTL			(ATC2603C_AUDIO_IN_BASE + 0x9)
+#define ATC2603C_ADC_CTL			(ATC2603C_AUDIO_IN_BASE + 0xA)
+#define ATC2603C_AGC_CTL0			(ATC2603C_AUDIO_IN_BASE + 0xB)
+#define ATC2603C_AGC_CTL1			(ATC2603C_AUDIO_IN_BASE + 0xC)
+#define ATC2603C_AGC_CTL2			(ATC2603C_AUDIO_IN_BASE + 0xD)
+#define ATC2603C_ADC_ANALOG0			(ATC2603C_AUDIO_IN_BASE + 0xE)
+#define ATC2603C_ADC_ANALOG1			(ATC2603C_AUDIO_IN_BASE + 0xF)
+
+/* PCM_IF Register Address */
+#define ATC2603C_PCM_IF_BASE			(0xA0)
+#define ATC2603C_PCM0_CTL			(ATC2603C_PCM_IF_BASE + 0x10)
+#define ATC2603C_PCM1_CTL			(ATC2603C_PCM_IF_BASE + 0x11)
+#define ATC2603C_PCM2_CTL			(ATC2603C_PCM_IF_BASE + 0x12)
+#define ATC2603C_PCMIF_CTL			(ATC2603C_PCM_IF_BASE + 0x13)
+
+/* CMU_CONTROL Register Address */
+#define ATC2603C_CMU_CONTROL_BASE		(0xC0)
+#define ATC2603C_CMU_DEVRST			(ATC2603C_CMU_CONTROL_BASE + 0x1)
+
+/* INTS Register Address */
+#define ATC2603C_INTS_BASE			(0xC8)
+#define ATC2603C_INTS_PD			(ATC2603C_INTS_BASE + 0x0)
+#define ATC2603C_INTS_MSK			(ATC2603C_INTS_BASE + 0x1)
+
+/* MFP Register Address */
+#define ATC2603C_MFP_BASE			(0xD0)
+#define ATC2603C_MFP_CTL			(ATC2603C_MFP_BASE + 0x00)
+#define ATC2603C_PAD_VSEL			(ATC2603C_MFP_BASE + 0x01)
+#define ATC2603C_GPIO_OUTEN			(ATC2603C_MFP_BASE + 0x02)
+#define ATC2603C_GPIO_INEN			(ATC2603C_MFP_BASE + 0x03)
+#define ATC2603C_GPIO_DAT			(ATC2603C_MFP_BASE + 0x04)
+#define ATC2603C_PAD_DRV			(ATC2603C_MFP_BASE + 0x05)
+#define ATC2603C_PAD_EN				(ATC2603C_MFP_BASE + 0x06)
+#define ATC2603C_DEBUG_SEL			(ATC2603C_MFP_BASE + 0x07)
+#define ATC2603C_DEBUG_IE			(ATC2603C_MFP_BASE + 0x08)
+#define ATC2603C_DEBUG_OE			(ATC2603C_MFP_BASE + 0x09)
+#define ATC2603C_BIST_START			(ATC2603C_MFP_BASE + 0x0A)
+#define ATC2603C_BIST_RESULT			(ATC2603C_MFP_BASE + 0x0B)
+#define ATC2603C_CHIP_VER			(ATC2603C_MFP_BASE + 0x0C)
+
+/* TWSI Register Address */
+#define ATC2603C_TWI_BASE			(0xF8)
+#define ATC2603C_SADDR				(ATC2603C_TWI_BASE + 0x7)
+
+#endif
diff --git a/include/linux/mfd/atc260x/regs_map_atc2609a.h b/include/linux/mfd/atc260x/regs_map_atc2609a.h
new file mode 100755
index 0000000..6f52081
--- /dev/null
+++ b/include/linux/mfd/atc260x/regs_map_atc2609a.h
@@ -0,0 +1,220 @@
+/*
+ * ATC2609A Spec Version_V1.05
+ */
+
+#ifndef __ATC2609A_REG_DEFINITION_H___
+#define __ATC2609A_REG_DEFINITION_H___
+
+/* PMU Register Address */
+#define ATC2609A_PMU_BASE			(0x00)
+#define ATC2609A_PMU_SYS_CTL0			(ATC2609A_PMU_BASE + 0x00)
+#define ATC2609A_PMU_SYS_CTL1			(ATC2609A_PMU_BASE + 0x01)
+#define ATC2609A_PMU_SYS_CTL2			(ATC2609A_PMU_BASE + 0x02)
+#define ATC2609A_PMU_SYS_CTL3			(ATC2609A_PMU_BASE + 0x03)
+#define ATC2609A_PMU_SYS_CTL4			(ATC2609A_PMU_BASE + 0x04)
+#define ATC2609A_PMU_SYS_CTL5			(ATC2609A_PMU_BASE + 0x05)
+#define ATC2609A_PMU_SYS_CTL6			(ATC2609A_PMU_BASE + 0x06)
+#define ATC2609A_PMU_SYS_CTL7			(ATC2609A_PMU_BASE + 0x07)
+#define ATC2609A_PMU_SYS_CTL8			(ATC2609A_PMU_BASE + 0x08)
+#define ATC2609A_PMU_SYS_CTL9			(ATC2609A_PMU_BASE + 0x09)
+#define ATC2609A_PMU_BAT_CTL0			(ATC2609A_PMU_BASE + 0x0A)
+#define ATC2609A_PMU_BAT_CTL1			(ATC2609A_PMU_BASE + 0x0B)
+#define ATC2609A_PMU_VBUS_CTL0			(ATC2609A_PMU_BASE + 0x0C)
+#define ATC2609A_PMU_VBUS_CTL1			(ATC2609A_PMU_BASE + 0x0D)
+#define ATC2609A_PMU_WALL_CTL0			(ATC2609A_PMU_BASE + 0x0E)
+#define ATC2609A_PMU_WALL_CTL1			(ATC2609A_PMU_BASE + 0x0F)
+#define ATC2609A_PMU_SYS_PENDING		(ATC2609A_PMU_BASE + 0x10)
+#define ATC2609A_PMU_APDS_CTL0			(ATC2609A_PMU_BASE + 0x11)
+#define ATC2609A_PMU_APDS_CTL1			(ATC2609A_PMU_BASE + 0x12)
+#define ATC2609A_PMU_APDS_CTL2			(ATC2609A_PMU_BASE + 0x13)
+#define ATC2609A_PMU_CHARGER_CTL		(ATC2609A_PMU_BASE + 0x14)
+#define ATC2609A_PMU_BAKCHARGER_CTL		(ATC2609A_PMU_BASE + 0x15)
+#define ATC2609A_PMU_SWCHG_CTL0			(ATC2609A_PMU_BASE + 0x16)
+#define ATC2609A_PMU_SWCHG_CTL1			(ATC2609A_PMU_BASE + 0x17)
+#define ATC2609A_PMU_SWCHG_CTL2			(ATC2609A_PMU_BASE + 0x18)
+#define ATC2609A_PMU_SWCHG_CTL3			(ATC2609A_PMU_BASE + 0x19)
+#define ATC2609A_PMU_SWCHG_CTL4			(ATC2609A_PMU_BASE + 0x1A)
+#define ATC2609A_PMU_DC_OSC			(ATC2609A_PMU_BASE + 0x1B)
+#define ATC2609A_PMU_DC0_CTL0			(ATC2609A_PMU_BASE + 0x1C)
+#define ATC2609A_PMU_DC0_CTL1			(ATC2609A_PMU_BASE + 0x1D)
+#define ATC2609A_PMU_DC0_CTL2			(ATC2609A_PMU_BASE + 0x1E)
+#define ATC2609A_PMU_DC0_CTL3			(ATC2609A_PMU_BASE + 0x1F)
+#define ATC2609A_PMU_DC0_CTL4			(ATC2609A_PMU_BASE + 0x20)
+#define ATC2609A_PMU_DC0_CTL5			(ATC2609A_PMU_BASE + 0x21)
+#define ATC2609A_PMU_DC0_CTL6			(ATC2609A_PMU_BASE + 0x22)
+#define ATC2609A_PMU_DC1_CTL0			(ATC2609A_PMU_BASE + 0x23)
+#define ATC2609A_PMU_DC1_CTL1			(ATC2609A_PMU_BASE + 0x24)
+#define ATC2609A_PMU_DC1_CTL2			(ATC2609A_PMU_BASE + 0x25)
+#define ATC2609A_PMU_DC1_CTL3			(ATC2609A_PMU_BASE + 0x26)
+#define ATC2609A_PMU_DC1_CTL4			(ATC2609A_PMU_BASE + 0x27)
+#define ATC2609A_PMU_DC1_CTL5			(ATC2609A_PMU_BASE + 0x28)
+#define ATC2609A_PMU_DC1_CTL6			(ATC2609A_PMU_BASE + 0x29)
+#define ATC2609A_PMU_DC2_CTL0			(ATC2609A_PMU_BASE + 0x2A)
+#define ATC2609A_PMU_DC2_CTL1			(ATC2609A_PMU_BASE + 0x2B)
+#define ATC2609A_PMU_DC2_CTL2			(ATC2609A_PMU_BASE + 0x2C)
+#define ATC2609A_PMU_DC2_CTL3			(ATC2609A_PMU_BASE + 0x2D)
+#define ATC2609A_PMU_DC2_CTL4			(ATC2609A_PMU_BASE + 0x2E)
+#define ATC2609A_PMU_DC2_CTL5			(ATC2609A_PMU_BASE + 0x2F)
+#define ATC2609A_PMU_DC2_CTL6			(ATC2609A_PMU_BASE + 0x30)
+#define ATC2609A_PMU_DC3_CTL0			(ATC2609A_PMU_BASE + 0x31)
+#define ATC2609A_PMU_DC3_CTL1			(ATC2609A_PMU_BASE + 0x32)
+#define ATC2609A_PMU_DC3_CTL2			(ATC2609A_PMU_BASE + 0x33)
+#define ATC2609A_PMU_DC3_CTL3			(ATC2609A_PMU_BASE + 0x34)
+#define ATC2609A_PMU_DC3_CTL4			(ATC2609A_PMU_BASE + 0x35)
+#define ATC2609A_PMU_DC3_CTL5			(ATC2609A_PMU_BASE + 0x36)
+#define ATC2609A_PMU_DC3_CTL6			(ATC2609A_PMU_BASE + 0x37)
+#define ATC2609A_PMU_DC_ZR			(ATC2609A_PMU_BASE + 0x38)
+#define ATC2609A_PMU_LDO0_CTL0			(ATC2609A_PMU_BASE + 0x39)
+#define ATC2609A_PMU_LDO0_CTL1			(ATC2609A_PMU_BASE + 0x3A)
+#define ATC2609A_PMU_LDO1_CTL0			(ATC2609A_PMU_BASE + 0x3B)
+#define ATC2609A_PMU_LDO1_CTL1			(ATC2609A_PMU_BASE + 0x3C)
+#define ATC2609A_PMU_LDO2_CTL0			(ATC2609A_PMU_BASE + 0x3D)
+#define ATC2609A_PMU_LDO2_CTL1			(ATC2609A_PMU_BASE + 0x3E)
+#define ATC2609A_PMU_LDO3_CTL0			(ATC2609A_PMU_BASE + 0x3F)
+#define ATC2609A_PMU_LDO3_CTL1			(ATC2609A_PMU_BASE + 0x40)
+#define ATC2609A_PMU_LDO4_CTL0			(ATC2609A_PMU_BASE + 0x41)
+#define ATC2609A_PMU_LDO4_CTL1			(ATC2609A_PMU_BASE + 0x42)
+#define ATC2609A_PMU_LDO5_CTL0			(ATC2609A_PMU_BASE + 0x43)
+#define ATC2609A_PMU_LDO5_CTL1			(ATC2609A_PMU_BASE + 0x44)
+#define ATC2609A_PMU_LDO6_CTL0			(ATC2609A_PMU_BASE + 0x45)
+#define ATC2609A_PMU_LDO6_CTL1			(ATC2609A_PMU_BASE + 0x46)
+#define ATC2609A_PMU_LDO7_CTL0			(ATC2609A_PMU_BASE + 0x47)
+#define ATC2609A_PMU_LDO7_CTL1			(ATC2609A_PMU_BASE + 0x48)
+#define ATC2609A_PMU_LDO8_CTL0			(ATC2609A_PMU_BASE + 0x49)
+#define ATC2609A_PMU_LDO8_CTL1			(ATC2609A_PMU_BASE + 0x4A)
+#define ATC2609A_PMU_LDO9_CTL			(ATC2609A_PMU_BASE + 0x4B)
+#define ATC2609A_PMU_OV_INT_EN			(ATC2609A_PMU_BASE + 0x4C)
+#define ATC2609A_PMU_OV_STATUS			(ATC2609A_PMU_BASE + 0x4D)
+#define ATC2609A_PMU_UV_INT_EN			(ATC2609A_PMU_BASE + 0x4E)
+#define ATC2609A_PMU_UV_STATUS			(ATC2609A_PMU_BASE + 0x4F)
+#define ATC2609A_PMU_OC_INT_EN			(ATC2609A_PMU_BASE + 0x50)
+#define ATC2609A_PMU_OC_STATUS			(ATC2609A_PMU_BASE + 0x51)
+#define ATC2609A_PMU_OT_CTL			(ATC2609A_PMU_BASE + 0x52)
+#define ATC2609A_PMU_CM_CTL0			(ATC2609A_PMU_BASE + 0x53)
+#define ATC2609A_PMU_FW_USE0			(ATC2609A_PMU_BASE + 0x54)
+#define ATC2609A_PMU_FW_USE1			(ATC2609A_PMU_BASE + 0x55)
+#define ATC2609A_PMU_ADC12B_I			(ATC2609A_PMU_BASE + 0x56)
+#define ATC2609A_PMU_ADC12B_V			(ATC2609A_PMU_BASE + 0x57)
+#define ATC2609A_PMU_ADC12B_DUMMY		(ATC2609A_PMU_BASE + 0x58)
+#define ATC2609A_PMU_AUXADC_CTL0		(ATC2609A_PMU_BASE + 0x59)
+#define ATC2609A_PMU_AUXADC_CTL1		(ATC2609A_PMU_BASE + 0x5A)
+#define ATC2609A_PMU_BATVADC			(ATC2609A_PMU_BASE + 0x5B)
+#define ATC2609A_PMU_BATIADC			(ATC2609A_PMU_BASE + 0x5C)
+#define ATC2609A_PMU_WALLVADC			(ATC2609A_PMU_BASE + 0x5D)
+#define ATC2609A_PMU_WALLIADC			(ATC2609A_PMU_BASE + 0x5E)
+#define ATC2609A_PMU_VBUSVADC			(ATC2609A_PMU_BASE + 0x5F)
+#define ATC2609A_PMU_VBUSIADC			(ATC2609A_PMU_BASE + 0x60)
+#define ATC2609A_PMU_SYSPWRADC			(ATC2609A_PMU_BASE + 0x61)
+#define ATC2609A_PMU_REMCONADC			(ATC2609A_PMU_BASE + 0x62)
+#define ATC2609A_PMU_SVCCADC			(ATC2609A_PMU_BASE + 0x63)
+#define ATC2609A_PMU_CHGIADC			(ATC2609A_PMU_BASE + 0x64)
+#define ATC2609A_PMU_IREFADC			(ATC2609A_PMU_BASE + 0x65)
+#define ATC2609A_PMU_BAKBATADC			(ATC2609A_PMU_BASE + 0x66)
+#define ATC2609A_PMU_ICTEMPADC			(ATC2609A_PMU_BASE + 0x67)
+#define ATC2609A_PMU_AUXADC0			(ATC2609A_PMU_BASE + 0x68)
+#define ATC2609A_PMU_AUXADC1			(ATC2609A_PMU_BASE + 0x69)
+#define ATC2609A_PMU_AUXADC2			(ATC2609A_PMU_BASE + 0x6A)
+#define ATC2609A_PMU_AUXADC3			(ATC2609A_PMU_BASE + 0x6B)
+#define ATC2609A_PMU_ICTEMPADC_ADJ		(ATC2609A_PMU_BASE + 0x6C)
+#define ATC2609A_PMU_BDG_CTL			(ATC2609A_PMU_BASE + 0x6D)
+#define ATC2609A_RTC_CTL			(ATC2609A_PMU_BASE + 0x6E)
+#define ATC2609A_RTC_MSALM			(ATC2609A_PMU_BASE + 0x6F)
+#define ATC2609A_RTC_HALM			(ATC2609A_PMU_BASE + 0x70)
+#define ATC2609A_RTC_YMDALM			(ATC2609A_PMU_BASE + 0x71)
+#define ATC2609A_RTC_MS				(ATC2609A_PMU_BASE + 0x72)
+#define ATC2609A_RTC_H				(ATC2609A_PMU_BASE + 0x73)
+#define ATC2609A_RTC_DC				(ATC2609A_PMU_BASE + 0x74)
+#define ATC2609A_RTC_YMD			(ATC2609A_PMU_BASE + 0x75)
+#define ATC2609A_EFUSE_DAT			(ATC2609A_PMU_BASE + 0x76)
+#define ATC2609A_EFUSECRTL1			(ATC2609A_PMU_BASE + 0x77)
+#define ATC2609A_EFUSECRTL2			(ATC2609A_PMU_BASE + 0x78)
+#define ATC2609A_PMU_DC4_CTL0			(ATC2609A_PMU_BASE + 0x79)
+#define ATC2609A_PMU_DC4_CTL1			(ATC2609A_PMU_BASE + 0x7A)
+#define ATC2609A_PMU_DC4_CTL2			(ATC2609A_PMU_BASE + 0x7B)
+#define ATC2609A_PMU_DC4_CTL3			(ATC2609A_PMU_BASE + 0x7C)
+#define ATC2609A_PMU_DC4_CTL4			(ATC2609A_PMU_BASE + 0x7D)
+#define ATC2609A_PMU_DC4_CTL5			(ATC2609A_PMU_BASE + 0x7E)
+#define ATC2609A_PMU_DC4_CTL6			(ATC2609A_PMU_BASE + 0x7F)
+#define ATC2609A_PMU_PWR_STATUS			(ATC2609A_PMU_BASE + 0x80)
+#define ATC2609A_PMU_S2_PWR			(ATC2609A_PMU_BASE + 0x81)
+#define ATC2609A_CLMT_CTL0			(ATC2609A_PMU_BASE + 0x82)
+#define ATC2609A_CLMT_DATA0			(ATC2609A_PMU_BASE + 0x83)
+#define ATC2609A_CLMT_DATA1			(ATC2609A_PMU_BASE + 0x84)
+#define ATC2609A_CLMT_DATA2			(ATC2609A_PMU_BASE + 0x85)
+#define ATC2609A_CLMT_DATA3			(ATC2609A_PMU_BASE + 0x86)
+#define ATC2609A_CLMT_ADD0			(ATC2609A_PMU_BASE + 0x87)
+#define ATC2609A_CLMT_ADD1			(ATC2609A_PMU_BASE + 0x88)
+#define ATC2609A_CLMT_OCV_TABLE			(ATC2609A_PMU_BASE + 0x89)
+#define ATC2609A_CLMT_R_TABLE			(ATC2609A_PMU_BASE + 0x8A)
+#define ATC2609A_PMU_PWRON_CTL0			(ATC2609A_PMU_BASE + 0x8D)
+#define ATC2609A_PMU_PWRON_CTL1			(ATC2609A_PMU_BASE + 0x8E)
+#define ATC2609A_PMU_PWRON_CTL2			(ATC2609A_PMU_BASE + 0x8F)
+#define ATC2609A_IRC_CTL			(ATC2609A_PMU_BASE + 0x90)
+#define ATC2609A_IRC_STAT			(ATC2609A_PMU_BASE + 0x91)
+#define ATC2609A_IRC_CC				(ATC2609A_PMU_BASE + 0x92)
+#define ATC2609A_IRC_KDC			(ATC2609A_PMU_BASE + 0x93)
+#define ATC2609A_IRC_WK				(ATC2609A_PMU_BASE + 0x94)
+#define ATC2609A_IRC_RCC			(ATC2609A_PMU_BASE + 0x95)
+
+/* AUDIO_OUT Register Address */
+#define ATC2609A_AUDIO_OUT_BASE			(0xA0)
+#define ATC2609A_AUDIOINOUT_CTL			(ATC2609A_AUDIO_OUT_BASE + 0x00)
+#define ATC2609A_AUDIO_DEBUGOUTCTL		(ATC2609A_AUDIO_OUT_BASE + 0x01)
+#define ATC2609A_DAC_DIGITALCTL			(ATC2609A_AUDIO_OUT_BASE + 0x02)
+#define ATC2609A_DAC_VOLUMECTL0			(ATC2609A_AUDIO_OUT_BASE + 0x03)
+#define ATC2609A_DAC_ANALOG0			(ATC2609A_AUDIO_OUT_BASE + 0x04)
+#define ATC2609A_DAC_ANALOG1			(ATC2609A_AUDIO_OUT_BASE + 0x05)
+#define ATC2609A_DAC_ANALOG2			(ATC2609A_AUDIO_OUT_BASE + 0x06)
+#define ATC2609A_DAC_ANALOG3			(ATC2609A_AUDIO_OUT_BASE + 0x07)
+
+/* AUDIO_IN Register Address */
+#define ATC2609A_AUDIO_IN_BASE			(0xA0)
+#define ATC2609A_ADC_DIGITALCTL			(ATC2609A_AUDIO_IN_BASE + 0x08)
+#define ATC2609A_ADC_HPFCTL			(ATC2609A_AUDIO_IN_BASE + 0x09)
+#define ATC2609A_ADC_CTL			(ATC2609A_AUDIO_IN_BASE + 0x0A)
+#define ATC2609A_AGC_CTL0			(ATC2609A_AUDIO_IN_BASE + 0x0B)
+#define ATC2609A_AGC_CTL1			(ATC2609A_AUDIO_IN_BASE + 0x0C)
+#define ATC2609A_AGC_CTL2			(ATC2609A_AUDIO_IN_BASE + 0x0D)
+#define ATC2609A_ADC_ANALOG0			(ATC2609A_AUDIO_IN_BASE + 0x0E)
+#define ATC2609A_ADC_ANALOG1			(ATC2609A_AUDIO_IN_BASE + 0x0F)
+
+/* PCM_IF Register Address */
+#define ATC2609A_PCM_IF_BASE			(0xA0)
+#define ATC2609A_PCM0_CTL			(ATC2609A_PCM_IF_BASE + 0x10)
+#define ATC2609A_PCM1_CTL			(ATC2609A_PCM_IF_BASE + 0x11)
+#define ATC2609A_PCM2_CTL			(ATC2609A_PCM_IF_BASE + 0x12)
+#define ATC2609A_PCMIF_CTL			(ATC2609A_PCM_IF_BASE + 0x13)
+
+/* CMU_CONTROL Register Address */
+#define ATC2609A_CMU_CONTROL_BASE		(0xC0)
+#define ATC2609A_CMU_DEVRST			(ATC2609A_CMU_CONTROL_BASE + 0x01)
+
+/* INTS Register Address */
+#define ATC2609A_INTS_BASE			(0xC8)
+#define ATC2609A_INTS_PD			(ATC2609A_INTS_BASE + 0x00)
+#define ATC2609A_INTS_MSK			(ATC2609A_INTS_BASE + 0x01)
+
+/* MFP Register Address */
+#define ATC2609A_MFP_BASE			(0xD0)
+#define ATC2609A_MFP_CTL			(ATC2609A_MFP_BASE + 0x00)
+#define ATC2609A_PAD_VSEL			(ATC2609A_MFP_BASE + 0x01)
+#define ATC2609A_GPIO_OUTEN			(ATC2609A_MFP_BASE + 0x02)
+#define ATC2609A_GPIO_INEN			(ATC2609A_MFP_BASE + 0x03)
+#define ATC2609A_GPIO_DAT			(ATC2609A_MFP_BASE + 0x04)
+#define ATC2609A_PAD_DRV			(ATC2609A_MFP_BASE + 0x05)
+#define ATC2609A_PAD_EN				(ATC2609A_MFP_BASE + 0x06)
+#define ATC2609A_DEBUG_SEL			(ATC2609A_MFP_BASE + 0x07)
+#define ATC2609A_DEBUG_IE			(ATC2609A_MFP_BASE + 0x08)
+#define ATC2609A_DEBUG_OE			(ATC2609A_MFP_BASE + 0x09)
+#define ATC2609A_CHIP_VER			(ATC2609A_MFP_BASE + 0x0C)
+
+/* PWSI Register Address */
+#define ATC2609A_PWSI_BASE			(0xF0)
+#define ATC2609A_PWSI_CTL			(ATC2609A_PWSI_BASE + 0x00)
+#define ATC2609A_PWSI_STATUS			(ATC2609A_PWSI_BASE + 0x01)
+
+/* TWSI Register Address */
+#define ATC2609A_TWSI_BASE			(0xF8)
+#define ATC2609A_SADDR				(ATC2609A_TWSI_BASE + 0x07)
+
+#endif
-- 
2.7.4



More information about the linux-yocto mailing list