[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(¤t_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(¤t_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, ®_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