summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Cai <R63905@freescale.com>2011-04-02 17:46:58 +0800
committerRobby Cai <R63905@freescale.com>2011-04-06 15:59:36 +0800
commit12156df7104dba860d59b4f3729bad220cb33711 (patch)
tree0f065418de887922c389c53dc30e4cbce6632cbc
parent5cbe940f849ab7da732ac0db6f388ceb7ea01778 (diff)
ENGR00141508-1 MX50 RD3: Add PMIC Ripley driver
Support Regulator, ADC, TouchScreen, Battery, RTC. PMIC issues are tracked on http://wiki.freescale.net/display/MADPlatMX508/RD3+board+Issues+Tracking Signed-off-by: Anson Huang <b20788@freescale.com> Signed-off-by: Zhou Jingyu <Jingyu.Zhou@freescale.com> Signed-off-by: Robby Cai <R63905@freescale.com>
-rw-r--r--drivers/mxc/pmic/Kconfig9
-rw-r--r--drivers/mxc/pmic/Makefile1
-rw-r--r--drivers/mxc/pmic/core/Makefile4
-rw-r--r--drivers/mxc/pmic/core/mc34708.c121
-rw-r--r--drivers/mxc/pmic/core/pmic_core_spi.c6
-rw-r--r--drivers/mxc/pmic/mc34708/Kconfig27
-rw-r--r--drivers/mxc/pmic/mc34708/Makefile10
-rw-r--r--drivers/mxc/pmic/mc34708/mc34708_adc.c615
-rw-r--r--drivers/mxc/pmic/mc34708/mc34708_battery.c950
-rw-r--r--drivers/regulator/Kconfig5
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/reg-mc34708.c1276
-rw-r--r--include/linux/mfd/mc34708/core.h83
-rw-r--r--include/linux/mfd/mc34708/mc34708.h134
-rw-r--r--include/linux/mfd/mc34708/mc34708_adc.h88
-rw-r--r--include/linux/mfd/mc34708/mc34708_battery.h162
-rw-r--r--include/linux/pmic_external.h78
17 files changed, 3568 insertions, 2 deletions
diff --git a/drivers/mxc/pmic/Kconfig b/drivers/mxc/pmic/Kconfig
index 4ee91110fc03..6f69ab0e12e4 100644
--- a/drivers/mxc/pmic/Kconfig
+++ b/drivers/mxc/pmic/Kconfig
@@ -23,6 +23,14 @@ config MXC_PMIC_MC13892
This is the MXC MC13892(PMIC) support. It include
ADC, Battery, Connectivity, Light, Power and RTC.
+config MXC_PMIC_MC34708
+ tristate "MC34708 PMIC"
+ depends on ARCH_MXC && (I2C || SPI)
+ select MXC_PMIC
+ ---help---
+ This is the MXC MC34708(PMIC) support. It include
+ ADC, Battery, Connectivity, Light, Power and RTC.
+
config MXC_PMIC_I2C
bool "Support PMIC I2C Interface"
depends on MXC_PMIC_MC13892 && I2C
@@ -61,4 +69,5 @@ source "drivers/mxc/pmic/mc13783/Kconfig"
source "drivers/mxc/pmic/mc13892/Kconfig"
+source "drivers/mxc/pmic/mc34708/Kconfig"
endmenu
diff --git a/drivers/mxc/pmic/Makefile b/drivers/mxc/pmic/Makefile
index 385c07e8509f..1c4dcccfb882 100644
--- a/drivers/mxc/pmic/Makefile
+++ b/drivers/mxc/pmic/Makefile
@@ -5,3 +5,4 @@
obj-y += core/
obj-$(CONFIG_MXC_PMIC_MC13783) += mc13783/
obj-$(CONFIG_MXC_PMIC_MC13892) += mc13892/
+obj-$(CONFIG_MXC_PMIC_MC34708) += mc34708/
diff --git a/drivers/mxc/pmic/core/Makefile b/drivers/mxc/pmic/core/Makefile
index be06863f79f3..17b0751de988 100644
--- a/drivers/mxc/pmic/core/Makefile
+++ b/drivers/mxc/pmic/core/Makefile
@@ -11,6 +11,10 @@ ifneq ($(CONFIG_MXC_PMIC_MC13892),)
pmic_mxc_mod-objs += mc13892.o
endif
+ifneq ($(CONFIG_MXC_PMIC_MC34708),)
+pmic_mxc_mod-objs += mc34708.o
+endif
+
ifneq ($(CONFIG_MXC_PMIC_SPI),)
pmic_mxc_mod-objs += pmic_core_spi.o
endif
diff --git a/drivers/mxc/pmic/core/mc34708.c b/drivers/mxc/pmic/core/mc34708.c
new file mode 100644
index 000000000000..0f376833c7d3
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc34708.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*!
+ * @file pmic/core/mc34708.c
+ * @brief This file contains MC34708 specific PMIC code. This implementaion
+ * may differ for each PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/mfd/mc34708/core.h>
+
+#include <asm/mach-types.h>
+
+#include "pmic.h"
+
+/*
+ * Defines
+ */
+#define MC34708_I2C_RETRY_TIMES 10
+#define MXC_PMIC_FRAME_MASK 0x00FFFFFF
+#define MXC_PMIC_MAX_REG_NUM 0x3F
+#define MXC_PMIC_REG_NUM_SHIFT 0x19
+#define MXC_PMIC_WRITE_BIT_SHIFT 31
+
+void mc34708_power_off(void);
+
+void *mc34708_alloc_data(struct device *dev)
+{
+ struct mc34708 *mc34708;
+
+ mc34708 = kzalloc(sizeof(struct mc34708), GFP_KERNEL);
+ if (mc34708 == NULL)
+ return NULL;
+
+ mc34708->dev = dev;
+
+ return (void *)mc34708;
+}
+
+int mc34708_init_registers(void)
+{
+ CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF));
+ CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF));
+ CHECK_ERROR(pmic_write(REG_INT_STATUS0, 0xFFFFFF));
+ CHECK_ERROR(pmic_write(REG_INT_STATUS1, 0xFFFFFF));
+
+ pm_power_off = mc34708_power_off;
+
+ return PMIC_SUCCESS;
+}
+
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param ver pointer to the pmic_version_t structure
+ *
+ * @return This function returns PMIC version.
+ */
+void mc34708_get_revision(pmic_version_t *ver)
+{
+ int rev_id = 0;
+ int rev1 = 0;
+ int rev2 = 0;
+ int finid = 0;
+ int icid = 0;
+
+ ver->id = PMIC_MC34708;
+ pmic_read(REG_IDENTIFICATION, &rev_id);
+
+ rev1 = (rev_id & 0x018) >> 3;
+ rev2 = (rev_id & 0x007);
+ icid = (rev_id & 0x01C0) >> 6;
+ finid = (rev_id & 0x01E00) >> 9;
+
+ ver->revision = ((rev1 * 10) + rev2);
+ printk(KERN_INFO "mc34708 Rev %d.%d FinVer %x detected\n", rev1,
+ rev2, finid);
+}
+
+void mc34708_power_off(void)
+{
+ unsigned int value;
+
+ pmic_read_reg(REG_POWER_CTL0, &value, 0xffffff);
+
+ value |= 0x000008;
+
+ pmic_write_reg(REG_POWER_CTL0, value, 0xffffff);
+}
diff --git a/drivers/mxc/pmic/core/pmic_core_spi.c b/drivers/mxc/pmic/core/pmic_core_spi.c
index 7357945f3a93..156c50f0dfc3 100644
--- a/drivers/mxc/pmic/core/pmic_core_spi.c
+++ b/drivers/mxc/pmic/core/pmic_core_spi.c
@@ -42,6 +42,7 @@
#include <asm/uaccess.h>
#include <linux/mfd/mc13892/core.h>
+#include <linux/mfd/mc34708/core.h>
#include "pmic.h"
@@ -89,11 +90,13 @@ static struct platform_device bleds_ldm = {
enum pmic_id {
PMIC_ID_MC13892,
+ PMIC_ID_MC34708,
PMIC_ID_INVALID,
};
struct pmic_internal pmic_internal[] = {
[PMIC_ID_MC13892] = _PMIC_INTERNAL_INITIALIZER(mc13892),
+ [PMIC_ID_MC34708] = _PMIC_INTERNAL_INITIALIZER(mc34708),
};
/*
@@ -227,6 +230,7 @@ static int __devinit pmic_probe(struct spi_device *spi)
adc_ldm.name = get_client_device_name(name, "%s_adc");
battery_ldm.name = get_client_device_name(name, "%s_battery");
+ light_ldm.name = get_client_device_name(name, "%s_light");
/* Initialize the PMIC event handling */
pmic_event_list_init();
@@ -319,6 +323,8 @@ static const struct spi_device_id pmic_device_id[] = {
{
.name = "mc13892",
}, {
+ .name = "mc34708",
+ }, {
/* sentinel */
}
};
diff --git a/drivers/mxc/pmic/mc34708/Kconfig b/drivers/mxc/pmic/mc34708/Kconfig
new file mode 100644
index 000000000000..612ebdb0deb7
--- /dev/null
+++ b/drivers/mxc/pmic/mc34708/Kconfig
@@ -0,0 +1,27 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_MC34708_ADC
+ tristate "MC34708 ADC support"
+ depends on MXC_PMIC_MC34708
+ ---help---
+ This is the MC34708 ADC module driver. This module provides kernel API
+ for the ADC system of MC34708.
+ It controls also the touch screen interface.
+ If you want MC34708 ADC support, you should say Y here
+
+config MXC_MC34708_RTC
+ tristate "MC34708 Real Time Clock (RTC) support"
+ depends on MXC_PMIC_MC34708
+ ---help---
+ This is the MC34708 RTC module driver. This module provides kernel API
+ for RTC part of MC34708.
+ If you want MC34708 RTC support, you should say Y here
+config MXC_MC34708_BATTERY
+ tristate "MC34708 Battery API support"
+ depends on MXC_PMIC_MC34708
+ ---help---
+ This is the MC34708 battery module driver. This module provides kernel API
+ for battery control part of MC34708.
+ If you want MC34708 battery support, you should say Y here
diff --git a/drivers/mxc/pmic/mc34708/Makefile b/drivers/mxc/pmic/mc34708/Makefile
new file mode 100644
index 000000000000..6e1c907e8f35
--- /dev/null
+++ b/drivers/mxc/pmic/mc34708/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the mc13783 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_MC34708_ADC) += mc34708_adc.o
+#obj-$(CONFIG_MXC_MC34708_RTC) += mc34708_rtc.o
+obj-$(CONFIG_MXC_MC34708_LIGHT) += mc34708_light.o
+obj-$(CONFIG_MXC_MC34708_BATTERY) += mc34708_battery.o
+#obj-$(CONFIG_MXC_MC34708_CONNECTIVITY) += mc34708_convity.o
+#obj-$(CONFIG_MXC_MC34708_POWER) += mc34708_power.o
diff --git a/drivers/mxc/pmic/mc34708/mc34708_adc.c b/drivers/mxc/pmic/mc34708/mc34708_adc.c
new file mode 100644
index 000000000000..a08c21ae4bd3
--- /dev/null
+++ b/drivers/mxc/pmic/mc34708/mc34708_adc.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/device.h>
+
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+#include <linux/mfd/mc34708/mc34708.h>
+
+#include "../core/pmic.h"
+
+#define ADSEL0_LSH 0
+#define ADSEL0_WID 4
+#define ADSEL1_LSH 4
+#define ADSEL1_WID 4
+#define ADSEL2_LSH 8
+#define ADSEL2_WID 4
+#define ADSEL3_LSH 12
+#define ADSEL3_WID 4
+#define ADSEL4_LSH 16
+#define ADSEL4_WID 4
+#define ADSEL5_LSH 20
+#define ADSEL5_WID 4
+
+#define ADSEL6_LSH 0
+#define ADSEL6_WID 4
+#define ADSEL7_LSH 4
+#define ADSEL7_WID 4
+
+#define ADRESULT0_LSH 2
+#define ADRESULT0_WID 10
+
+#define ADRESULT1_LSH 14
+#define ADRESULT1_WID 10
+
+#define ADEN_LSH 0
+#define ADEN_WID 1
+
+#define ADSTART_LSH 1
+#define ADSTART_WID 1
+
+#define ADSTOP_LSH 4
+#define ADSTOP_WID 3
+
+#define TSEN (1 << 12)
+#define TSSTART (1 << 13)
+#define TSCONT (1 << 14)
+#define TSHOLD (1 << 15)
+#define TSSTOP0 (1 << 16)
+#define TSSTOP1 (1 << 17)
+#define TSSTOP2 (1 << 18)
+#define TSPENDETEN (1 << 20)
+
+#define DUMMY 0
+#define X_POS 1
+#define Y_POS 2
+#define CONTACT_RES 3
+
+#define DELTA_Y_MAX 50
+#define DELTA_X_MAX 50
+
+#define ADC_MAX_CHANNEL 7
+
+/* internal function */
+static void callback_tspendet(void *);
+static void callback_tsdone(void *);
+static void callback_adcbisdone(void *);
+
+static int suspend_flag;
+
+static DECLARE_COMPLETION(adcdone_it);
+static DECLARE_COMPLETION(tsdone_int);
+static DECLARE_COMPLETION(tspendet_int);
+static pmic_event_callback_t tspendet_event;
+static pmic_event_callback_t event_tsdone;
+static pmic_event_callback_t event_adc;
+static DECLARE_MUTEX(convert_mutex);
+
+u32 value[8];
+
+static bool pmic_adc_ready;
+
+int is_mc34708_adc_ready()
+{
+ return pmic_adc_ready;
+}
+
+EXPORT_SYMBOL(is_mc34708_adc_ready);
+
+static int mc34708_pmic_adc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ suspend_flag = 1;
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_ADC0, BITFVAL(ADEN, 0), BITFMASK(ADEN)));
+
+ return 0;
+};
+
+static int mc34708_pmic_adc_resume(struct platform_device *pdev)
+{
+ suspend_flag = 0;
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_ADC0, BITFVAL(ADEN, 1), BITFMASK(ADEN)));
+
+ return 0;
+};
+
+static void callback_tspendet(void *unused)
+{
+ pr_debug("*** TSI IT mc34708 PMIC_ADC_GET_TOUCH_SAMPLE ***\n");
+ complete(&tspendet_int);
+ pmic_event_mask(MC34708_EVENT_TSPENDET);
+}
+
+static void callback_tsdone(void *unused)
+{
+ complete(&tsdone_int);
+}
+
+static void callback_adcdone(void *unused)
+{
+ complete(&adcdone_it);
+}
+
+int mc34708_pmic_adc_init(void)
+{
+ unsigned int reg_value = 0, i = 0;
+
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ /* sub to ADCDone IT */
+ event_adc.param = NULL;
+ event_adc.func = callback_adcdone;
+ CHECK_ERROR(pmic_event_subscribe(MC34708_EVENT_ADCDONEI, event_adc));
+
+ /* sub to TSDONE INT */
+ event_tsdone.param = NULL;
+ event_tsdone.func = callback_tsdone;
+ CHECK_ERROR(pmic_event_subscribe(MC34708_EVENT_TSDONEI, event_tsdone));
+
+ /* sub to TS Pen Detect INT */
+ tspendet_event.param = NULL;
+ tspendet_event.func = callback_tspendet;
+ CHECK_ERROR(pmic_event_subscribe
+ (MC34708_EVENT_TSPENDET, tspendet_event));
+
+ /* enable adc */
+ pmic_write_reg(MC34708_REG_ADC0, 0x170000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC1, 0xFFF000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC2, 0x000000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC3, 0xF28500, PMIC_ALL_BITS);
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc34708_pmic_adc_deinit(void)
+{
+ CHECK_ERROR(pmic_event_unsubscribe
+ (MC34708_EVENT_ADCDONEI, event_tsdone));
+ CHECK_ERROR(pmic_event_unsubscribe
+ (MC34708_EVENT_TSPENDET, tspendet_event));
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc34708_pmic_adc_convert(t_channel channel, unsigned short *result)
+{
+ PMIC_STATUS ret;
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register1 = MC34708_REG_ADC2;
+
+ if (suspend_flag == 1)
+ return -EBUSY;
+ down(&convert_mutex);
+
+ if (channel <= BATTERY_CURRENT) {
+ INIT_COMPLETION(adcdone_it);
+ ret = pmic_write_reg(MC34708_REG_ADC2,
+ BITFVAL(ADSEL0, BATTERY_VOLTAGE),
+ BITFMASK(ADSEL0));
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+
+ ret = pmic_write_reg(MC34708_REG_ADC2,
+ BITFVAL(ADSEL1, BATTERY_CURRENT),
+ BITFMASK(ADSEL1));
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+
+ ret = pmic_write_reg
+ (MC34708_REG_ADC0, BITFVAL(ADEN, 1),
+ BITFMASK(ADEN));
+
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+ ret = pmic_write_reg(MC34708_REG_ADC0,
+ BITFVAL(ADSTOP, ADC_MAX_CHANNEL),
+ BITFMASK(ADSTOP));
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+ ret = pmic_write_reg(MC34708_REG_ADC0,
+ BITFVAL(ADSTART, 1), BITFMASK(ADSTART));
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+
+ pr_debug("wait adc done.\n");
+ msleep(100);
+ wait_for_completion_interruptible(&adcdone_it);
+ ret = pmic_write_reg(MC34708_REG_ADC0,
+ BITFVAL(ADSTART, 0), BITFMASK(ADSTART));
+
+ ret =
+ pmic_read_reg(MC34708_REG_ADC4, &register_val,
+ PMIC_ALL_BITS);
+
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+
+ switch (channel) {
+ case BATTERY_VOLTAGE:
+ *result = BITFEXT(register_val, ADRESULT0);
+ break;
+ case BATTERY_CURRENT:
+ *result = BITFEXT(register_val, ADRESULT1);
+ break;
+ default:
+ *result = BITFEXT(register_val, ADRESULT0);
+ break;
+ }
+
+ pmic_write_reg
+ (MC34708_REG_ADC0, BITFVAL(ADEN, 0),
+ BITFMASK(ADEN));
+ } else {
+
+ INIT_COMPLETION(tsdone_int);
+ pr_debug("wait adc done.\n");
+ wait_for_completion_interruptible(&tsdone_int);
+ ret =
+ pmic_read_reg(MC34708_REG_ADC4, &register_val,
+ PMIC_ALL_BITS);
+ if (PMIC_SUCCESS != ret)
+ goto error1;
+ *result = BITFEXT(register_val, ADRESULT0);
+ }
+error1:
+ up(&convert_mutex);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(mc34708_pmic_adc_convert);
+
+static int pmic_adc_filter(t_touch_screen *ts_curr)
+{
+ unsigned int ydiff, xdiff;
+ unsigned int sample_sumx, sample_sumy;
+
+ if (ts_curr->contact_resistance == 0) {
+ ts_curr->x_position = 0;
+ ts_curr->y_position = 0;
+ return 0;
+ }
+
+ ydiff = abs(ts_curr->y_position1 - ts_curr->y_position2);
+ if (ydiff > DELTA_Y_MAX) {
+ pr_debug("pmic_adc_filter: Ret pos y\n");
+ return -1;
+ }
+
+ xdiff = abs(ts_curr->x_position1 - ts_curr->x_position2);
+ if (xdiff > DELTA_X_MAX) {
+ pr_debug("pmic_adc_filter: Ret pos x\n");
+ return -1;
+ }
+
+ sample_sumx = ts_curr->x_position1 + ts_curr->x_position2;
+ sample_sumy = ts_curr->y_position1 + ts_curr->y_position2;
+
+ ts_curr->y_position = sample_sumy / 2;
+ ts_curr->x_position = sample_sumx / 2;
+
+ return 0;
+}
+
+PMIC_STATUS mc34708_adc_read_ts(t_touch_screen *ts_value, int wait_tsi)
+{
+ pr_debug("mc34708_adc : mc34708_adc_read_ts\n");
+
+ if (wait_tsi) {
+ INIT_COMPLETION(tspendet_int);
+
+ pmic_event_unmask(MC34708_EVENT_TSPENDET);
+ pmic_write_reg(MC34708_REG_ADC0, 0x170000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC1, 0xFFF000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC2, 0x000000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC3, 0xF28500, PMIC_ALL_BITS);
+
+ wait_for_completion_interruptible(&tspendet_int);
+ }
+
+ INIT_COMPLETION(tsdone_int);
+
+ int adc3 = X_POS << 8 | X_POS << 10 | DUMMY << 12 |
+ Y_POS << 14 | Y_POS << 16 | DUMMY << 18 |
+ CONTACT_RES << 20 | CONTACT_RES << 22;
+
+ pmic_write_reg(MC34708_REG_ADC1, 0xFFF000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC2, 0x000000, PMIC_ALL_BITS);
+ pmic_write_reg(MC34708_REG_ADC3, adc3, PMIC_ALL_BITS);
+ /* TSSTOP=0b111, TSCONT=1, TSSTART=1, TSEN=1 */
+ pmic_write_reg(MC34708_REG_ADC0, 0x177000, PMIC_ALL_BITS);
+
+ wait_for_completion_interruptible(&tsdone_int);
+
+ int i;
+ for (i = 0; i < 4; i++) {
+ int reg = MC34708_REG_ADC4 + i;
+ int result, ret;
+ ret = pmic_read_reg(reg, &result, PMIC_ALL_BITS);
+ if (ret != PMIC_SUCCESS)
+ pr_err("pmic_write_reg err\n");
+
+ value[i * 2] = (result & (0x3FF << 2)) >> 2;
+ value[i * 2 + 1] = (result & (0x3FF << 14)) >> 14;
+ }
+
+ if (value[6] < 1000) {
+ ts_value->x_position = value[0];
+ ts_value->x_position1 = value[0];
+ ts_value->x_position2 = value[1];
+
+ ts_value->y_position = value[3];
+ ts_value->y_position1 = value[3];
+ ts_value->y_position2 = value[4];
+
+ ts_value->contact_resistance = value[6];
+ } else {
+ ts_value->x_position = 0;
+ ts_value->y_position = 0;
+ ts_value->contact_resistance = 0;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc34708_adc_get_touch_sample(t_touch_screen *touch_sample, int wait)
+{
+ if (mc34708_adc_read_ts(touch_sample, wait) != 0)
+ return PMIC_ERROR;
+ if (0 == pmic_adc_filter(touch_sample))
+ return PMIC_SUCCESS;
+ else
+ return PMIC_ERROR;
+}
+
+EXPORT_SYMBOL(mc34708_adc_get_touch_sample);
+
+#ifdef DEBUG
+static t_adc_param adc_param_db;
+
+static ssize_t adc_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int *value = adc_param_db.value;
+
+ pr_debug("adc_info\n");
+
+ pr_debug("ch0\t\t%d\n", adc_param_db.channel_0);
+ pr_debug("ch1\t\t%d\n", adc_param_db.channel_1);
+ pr_debug("d5\t\t%d\n", adc_param_db.chrgraw_devide_5);
+ pr_debug("conv delay\t%d\n", adc_param_db.conv_delay);
+ pr_debug("delay\t\t%d\n", adc_param_db.delay);
+ pr_debug("read mode\t%d\n", adc_param_db.read_mode);
+ pr_debug("read ts\t\t%d\n", adc_param_db.read_ts);
+ pr_debug("single ch\t%d\n", adc_param_db.single_channel);
+ pr_debug("wait ts int\t%d\n", adc_param_db.wait_tsi);
+ pr_debug("value0-3:\t%d\t%d\t%d\t%d\n", value[0], value[1],
+ value[2], value[3]);
+ pr_debug("value4-7:\t%d\t%d\t%d\t%d\n", value[4], value[5],
+ value[6], value[7]);
+
+ return 0;
+}
+
+enum {
+ ADC_SET_CH0 = 0,
+ ADC_SET_CH1,
+ ADC_SET_DV5,
+ ADC_SET_CON_DELAY,
+ ADC_SET_DELAY,
+ ADC_SET_RM,
+ ADC_SET_RT,
+ ADC_SET_S_CH,
+ ADC_SET_WAIT_TS,
+ ADC_INIT_P,
+ ADC_START,
+ ADC_TS,
+ ADC_TS_READ,
+ ADC_TS_CAL,
+ ADC_CMD_MAX
+};
+
+static const char *const adc_cmd[ADC_CMD_MAX] = {
+ [ADC_SET_CH0] = "ch0",
+ [ADC_SET_CH1] = "ch1",
+ [ADC_SET_DV5] = "dv5",
+ [ADC_SET_CON_DELAY] = "cd",
+ [ADC_SET_DELAY] = "dl",
+ [ADC_SET_RM] = "rm",
+ [ADC_SET_RT] = "rt",
+ [ADC_SET_S_CH] = "sch",
+ [ADC_SET_WAIT_TS] = "wt",
+ [ADC_INIT_P] = "init",
+ [ADC_START] = "start",
+ [ADC_TS] = "touch",
+ [ADC_TS_READ] = "touchr",
+ [ADC_TS_CAL] = "cal"
+};
+
+static int cmd(unsigned int index, int value)
+{
+ t_touch_screen ts;
+
+ switch (index) {
+ case ADC_SET_CH0:
+ adc_param_db.channel_0 = value;
+ break;
+ case ADC_SET_CH1:
+ adc_param_db.channel_1 = value;
+ break;
+ case ADC_SET_DV5:
+ adc_param_db.chrgraw_devide_5 = value;
+ break;
+ case ADC_SET_CON_DELAY:
+ adc_param_db.conv_delay = value;
+ break;
+ case ADC_SET_RM:
+ adc_param_db.read_mode = value;
+ break;
+ case ADC_SET_RT:
+ adc_param_db.read_ts = value;
+ break;
+ case ADC_SET_S_CH:
+ adc_param_db.single_channel = value;
+ break;
+ case ADC_SET_WAIT_TS:
+ adc_param_db.wait_tsi = value;
+ break;
+ case ADC_TS:
+ mc34708_pmic_adc_get_touch_sample(&ts, 1);
+ pr_debug("x = %d\n", ts.x_position);
+ pr_debug("y = %d\n", ts.y_position);
+ pr_debug("p = %d\n", ts.contact_resistance);
+ break;
+ case ADC_TS_READ:
+ mc34708_pmic_adc_get_touch_sample(&ts, 0);
+ pr_debug("x = %d\n", ts.x_position);
+ pr_debug("y = %d\n", ts.y_position);
+ pr_debug("p = %d\n", ts.contact_resistance);
+ break;
+ case ADC_TS_CAL:
+ break;
+ default:
+ pr_debug("error command\n");
+ break;
+ }
+ return 0;
+}
+
+static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state = 0;
+ const char *const *s;
+ char *p, *q;
+ int error;
+ int len, value = 0;
+
+ pr_debug("adc_ctl\n");
+
+ q = NULL;
+ q = memchr(buf, ' ', count);
+
+ if (q != NULL) {
+ len = q - buf;
+ q += 1;
+ value = simple_strtoul(q, NULL, 10);
+ } else {
+ p = memchr(buf, '\n', count);
+ len = p ? p - buf : count;
+ }
+
+ for (s = &adc_cmd[state]; state < ADC_CMD_MAX; s++, state++) {
+ if (*s && !strncmp(buf, *s, len))
+ break;
+ }
+ if (state < ADC_CMD_MAX && *s)
+ error = cmd(state, value);
+ else
+ error = -EINVAL;
+
+ return count;
+}
+
+#else
+static ssize_t adc_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return count;
+}
+
+#endif
+
+static DEVICE_ATTR(adc, 0644, adc_info, adc_ctl);
+
+static struct pmic_adc_api pmic_adc_api = {
+ .is_pmic_adc_ready = is_mc34708_adc_ready,
+ .pmic_adc_get_touch_sample = mc34708_adc_get_touch_sample,
+};
+
+static int mc34708_pmic_adc_module_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ pr_info("PMIC ADC start probe\n");
+ ret = device_create_file(&(pdev->dev), &dev_attr_adc);
+ if (ret) {
+ pr_debug("Can't create device file!\n");
+ return -ENODEV;
+ }
+
+ ret = mc34708_pmic_adc_init();
+ if (ret != PMIC_SUCCESS) {
+ pr_info("Error in mc34708_pmic_adc_init.\n");
+ goto rm_dev_file;
+ }
+
+ pmic_adc_ready = 1;
+ register_adc_apis(&pmic_adc_api);
+ pr_debug("PMIC ADC successfully probed\n");
+ return 0;
+
+rm_dev_file:
+ device_remove_file(&(pdev->dev), &dev_attr_adc);
+ return ret;
+}
+
+static int mc34708_pmic_adc_module_remove(struct platform_device *pdev)
+{
+ mc34708_pmic_adc_deinit();
+ pmic_adc_ready = 0;
+ pr_debug("PMIC ADC successfully removed\n");
+ return 0;
+}
+
+static struct platform_driver mc34708_pmic_adc_driver_ldm = {
+ .driver = {
+ .name = "mc34708_adc",
+ },
+ .suspend = mc34708_pmic_adc_suspend,
+ .resume = mc34708_pmic_adc_resume,
+ .probe = mc34708_pmic_adc_module_probe,
+ .remove = mc34708_pmic_adc_module_remove,
+};
+
+static int __init mc34708_pmic_adc_module_init(void)
+{
+ pr_info("MC34708 PMIC ADC driver loading...\n");
+ return platform_driver_register(&mc34708_pmic_adc_driver_ldm);
+}
+
+static void __exit mc34708_pmic_adc_module_exit(void)
+{
+ platform_driver_unregister(&mc34708_pmic_adc_driver_ldm);
+ pr_debug("MC34708 PMIC ADC driver successfully unloaded\n");
+}
+
+module_init(mc34708_pmic_adc_module_init);
+module_exit(mc34708_pmic_adc_module_exit);
+
+MODULE_DESCRIPTION("MC34708 PMIC ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc34708/mc34708_battery.c b/drivers/mxc/pmic/mc34708/mc34708_battery.c
new file mode 100644
index 000000000000..55dd7d058a1a
--- /dev/null
+++ b/drivers/mxc/pmic/mc34708/mc34708_battery.c
@@ -0,0 +1,950 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <asm/mach-types.h>
+#include <linux/mfd/mc34708/mc34708_battery.h>
+#include <linux/mfd/mc34708/mc34708_adc.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+#include <linux/mfd/mc34708/mc34708.h>
+
+#define CHRCV_LSH 6
+#define CHRCV_WID 6
+
+#define CHRCC_LSH 12
+#define CHRCC_WID 4
+
+#define CHRITERM_LSH 16
+#define CHRITERM_WID 3
+
+#define USBDETS_LSH 3
+#define USBDETS_WID 1
+
+#define AUXDETS_LSH 4
+#define AUXDETS_WID 1
+
+#define VBAT_TRKL_LSH 0
+#define VBAT_TRKL_WID 2
+
+#define LOWBATT_LSH 4
+#define LOWBATT_WID 2
+
+#define BATTEMPH_LSH 21
+#define BATTEMPH_WID 2
+
+#define EOCBUCKEN_LSH 23
+#define EOCBUCKEN_WID 1
+
+#define BATTEMPL_LSH 19
+#define BATTEMPL_WID 2
+
+#define CHRITERMEN_LSH 2
+#define CHRITERMEN_WID 1
+
+#define CHREN_LSH 3
+#define CHREN_WID 1
+
+#define MUSBCHRG_LSH 13
+#define MUSBCHRG_WID 2
+
+#define MSW_LSH 1
+#define MSW_WID 1
+
+#define AUXWEAKEN_LSH 22
+#define AUXWEAKEN_WID 1
+
+#define VBUSWEAKEN_LSH 23
+#define VBUSWEAKEN_WID 1
+
+#define AUXILIM_LSH 11
+#define AUXILIM_WID 3
+
+#define ILIM1P5_LSH 20
+#define ILIM1P5_WID 1
+
+#define ACC_STARTCC_LSH 0
+#define ACC_STARTCC_WID 1
+#define ACC_RSTCC_LSH 1
+#define ACC_RSTCC_WID 1
+#define ACC_CCFAULT_LSH 7
+#define ACC_CCFAULT_WID 7
+#define ACC_CCOUT_LSH 8
+#define ACC_CCOUT_WID 16
+#define ACC1_ONEC_LSH 0
+#define ACC1_ONEC_WID 15
+
+#define ACC_CALIBRATION 0x17
+#define ACC_START_COUNTER 0x07
+#define ACC_STOP_COUNTER 0x2
+#define ACC_CONTROL_BIT_MASK 0x1f
+#define ACC_ONEC_VALUE 2621
+#define ACC_COULOMB_PER_LSB 1
+#define ACC_CALIBRATION_DURATION_MSECS 20
+
+#define BAT_VOLTAGE_UNIT_UV 4692
+#define BAT_CURRENT_UNIT_UA 7813
+#define CHG_VOLTAGE_UINT_UV 23474
+#define CHG_MIN_CURRENT_UA 3500
+
+#define COULOMB_TO_UAH(c) (10000 * c / 36)
+
+#define BATTOVP_LSH 9
+#define BATTOVP_WID 1
+
+#define USBDETM_LSH 3
+#define USBDETM_WID 1
+#define AUXDETM_LSH 4
+#define AUXDETM_WID 1
+#define ATTACHM_LSH 15
+#define ATTACHM_WID 1
+#define DETACHM_LSH 16
+#define DETACHM_WID 1
+#define ADCCHANGEM_LSH 21
+#define ADCCHANGEM_WID 1
+
+#define BATTISOEN_LSH 23
+#define BATTISOEN_WID 1
+
+#define USBHOST 0x4
+#define USBCHARGER 0x20
+#define DEDICATEDCHARGER 0x40
+
+static int suspend_flag;
+static int charging_flag;
+static int power_change_flag;
+
+/*
+usb_type = 0x4; USB host;
+usb_type = 0x20; USB charger;
+usb_type = 0x40; Dedicated charger;
+*/
+static int usb_type;
+static struct mc34708_charger_setting_point ripley_charger_setting_point[] = {
+ {
+ .microVolt = 4200000,
+ .microAmp = 1150000,
+ },
+};
+
+static struct mc34708_charger_config ripley_charge_config = {
+ .batteryTempLow = 0,
+ .batteryTempHigh = 0,
+ .hasTempSensor = 0,
+ .trickleThreshold = 3000000,
+ .vbusThresholdLow = 4600000,
+ .vbusThresholdWeak = 4800000,
+ .vbusThresholdHigh = 5000000,
+ .vauxThresholdLow = 4600000,
+ .vauxThresholdWeak = 4800000,
+ .vauxThresholdHigh = 5000000,
+ .lowBattThreshold = 3100000,
+ .toppingOffMicroAmp = 50000, /* 50mA */
+ .chargingPoints = ripley_charger_setting_point,
+ .pointsNumber = 3,
+};
+
+static int dump_ripley_register(int reg);
+
+static int enable_charger(int enable)
+{
+ charging_flag = enable ? 1 : 0;
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(CHREN, enable ? 1 : 0),
+ BITFMASK(CHREN)));
+ return 0;
+}
+
+static int ripley_get_batt_voltage(unsigned short *voltage)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ channel = BATTERY_VOLTAGE;
+ CHECK_ERROR(mc34708_pmic_adc_convert(channel, result));
+ *voltage = result[0];
+
+ return 0;
+}
+
+static int ripley_get_batt_current(unsigned short *curr)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ channel = BATTERY_CURRENT;
+ CHECK_ERROR(mc34708_pmic_adc_convert(channel, result));
+ *curr = result[0];
+
+ return 0;
+}
+
+static int coulomb_counter_calibration;
+static unsigned int coulomb_counter_start_time_msecs;
+
+static int ripley_start_coulomb_counter(void)
+{
+ /* set scaler */
+ CHECK_ERROR(pmic_write_reg(REG_ACC1,
+ ACC_COULOMB_PER_LSB * ACC_ONEC_VALUE,
+ BITFMASK(ACC1_ONEC)));
+
+ CHECK_ERROR(pmic_write_reg
+ (REG_ACC0, ACC_START_COUNTER, ACC_CONTROL_BIT_MASK));
+ coulomb_counter_start_time_msecs = jiffies_to_msecs(jiffies);
+ pr_debug("coulomb counter start time %u\n",
+ coulomb_counter_start_time_msecs);
+ return 0;
+}
+
+static int ripley_stop_coulomb_counter(void)
+{
+ CHECK_ERROR(pmic_write_reg
+ (REG_ACC0, ACC_STOP_COUNTER, ACC_CONTROL_BIT_MASK));
+ return 0;
+}
+
+static int ripley_calibrate_coulomb_counter(void)
+{
+ int ret;
+ unsigned int value;
+
+ /* set scaler */
+ CHECK_ERROR(pmic_write_reg(REG_ACC1, 0x1, BITFMASK(ACC1_ONEC)));
+
+ CHECK_ERROR(pmic_write_reg
+ (REG_ACC0, ACC_CALIBRATION, ACC_CONTROL_BIT_MASK));
+ msleep(ACC_CALIBRATION_DURATION_MSECS);
+
+ ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+ if (ret != 0)
+ return -1;
+ value = BITFEXT(value, ACC_CCOUT);
+ pr_debug("calibrate value = %x\n", value);
+ coulomb_counter_calibration = (int)((s16) ((u16) value));
+ pr_debug("coulomb_counter_calibration = %d\n",
+ coulomb_counter_calibration);
+
+ return 0;
+
+}
+
+static int ripley_get_charger_coulomb(int *coulomb)
+{
+ int ret;
+ unsigned int value;
+ int calibration;
+ unsigned int time_diff_msec;
+
+ ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+ if (ret != 0)
+ return -1;
+ value = BITFEXT(value, ACC_CCOUT);
+ pr_debug("counter value = %x\n", value);
+ *coulomb = ((s16) ((u16) value)) * ACC_COULOMB_PER_LSB;
+
+ if (abs(*coulomb) >= ACC_COULOMB_PER_LSB) {
+ /* calibrate */
+ time_diff_msec = jiffies_to_msecs(jiffies);
+ time_diff_msec =
+ (time_diff_msec > coulomb_counter_start_time_msecs) ?
+ (time_diff_msec - coulomb_counter_start_time_msecs) :
+ (0xffffffff - coulomb_counter_start_time_msecs
+ + time_diff_msec);
+ calibration = coulomb_counter_calibration * (int)time_diff_msec
+ / (ACC_ONEC_VALUE * ACC_CALIBRATION_DURATION_MSECS);
+ *coulomb -= calibration;
+ }
+
+ return 0;
+}
+
+struct ripley_dev_info {
+ struct device *dev;
+
+ unsigned short voltage_raw;
+ int voltage_uV;
+ unsigned short current_raw;
+ int current_uA;
+ int battery_status;
+ int full_counter;
+ int chargeTimeSeconds;
+ int usb_charger_online;
+ int aux_charger_online;
+ int charger_voltage_uV;
+ int accum_current_uAh;
+
+ int currChargePoint;
+
+ struct power_supply bat;
+ struct power_supply usb_charger;
+ struct power_supply aux_charger;
+
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_work;
+ struct mc34708_charger_config *chargeConfig;
+};
+
+#define ripley_SENSER 25
+#define to_ripley_dev_info(x) container_of((x), struct ripley_dev_info, \
+ bat);
+
+static enum power_supply_property ripley_battery_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_STATUS,
+};
+
+static enum power_supply_property ripley_aux_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property ripley_usb_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+#define BATTTEMPH_TO_BITS(temp) ((temp - 45) / 5)
+#define BATTTEMPL_TO_BITS(temp) (temp / 5)
+#define VBAT_TRKL_UV_TO_BITS(uv) ((uv-2800000) / 100000)
+#define LOWBATT_UV_TO_BITS(uv) ((uv - 3100000) / 100000)
+#define CHRITEM_UV_TO_BITS(uv) (((uv / 1000) - 50) / 50)
+
+static int dump_ripley_register(int reg)
+{
+ unsigned int value;
+ pmic_read_reg(reg, &value, PMIC_ALL_BITS);
+ pr_info("ripley reg %d = 0x%x\n", reg, value);
+ return 0;
+}
+
+static int init_charger(struct mc34708_charger_config *config)
+{
+ /* set charger current termination threshold */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(CHRITERM,
+ CHRITEM_UV_TO_BITS
+ (config->toppingOffMicroAmp)),
+ BITFMASK(CHRITERM)));
+ /* enable charger current termination */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(CHRITERMEN, 1),
+ BITFMASK(CHRITERMEN)));
+
+ /* enable EOC buck */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(EOCBUCKEN, 1), BITFMASK(EOCBUCKEN)));
+ /* disable manual switch */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL,
+ BITFVAL(MSW, 0), BITFMASK(MSW)));
+ /* enable 1P5 large current */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_DEBOUNCE,
+ BITFVAL(ILIM1P5, 1), BITFMASK(ILIM1P5)));
+
+ /* enable ISO */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_DEBOUNCE,
+ BITFVAL(BATTISOEN, 1), BITFMASK(BATTISOEN)));
+
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_SOURCE,
+ BITFVAL(AUXWEAKEN, 1), BITFMASK(AUXWEAKEN)));
+
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_SOURCE,
+ BITFVAL(VBUSWEAKEN, 1),
+ BITFMASK(VBUSWEAKEN)));
+
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(VBAT_TRKL,
+ VBAT_TRKL_UV_TO_BITS
+ (config->trickleThreshold)),
+ BITFMASK(VBAT_TRKL)));
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(LOWBATT,
+ LOWBATT_UV_TO_BITS(config->lowBattThreshold)),
+ BITFMASK(LOWBATT)));
+
+ if (config->hasTempSensor) {
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(BATTEMPH,
+ BATTTEMPH_TO_BITS
+ (config->batteryTempHigh)),
+ BITFMASK(BATTEMPH)));
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_BATTERY_PROFILE,
+ BITFVAL(BATTEMPL,
+ BATTTEMPL_TO_BITS(config->batteryTempLow)),
+ BITFMASK(BATTEMPL)));
+ }
+
+ return 0;
+}
+
+#define CHRCV_UV_TO_BITS(uv) ((uv - 3500000) / 20000)
+#define CHRCC_UA_TO_BITS(ua) ((ua - 250000) / 100000)
+
+static int set_charging_point(struct ripley_dev_info *di, int point)
+{
+ unsigned int val, mask;
+ if (point >= 0 && point < di->chargeConfig->pointsNumber) {
+
+ val =
+ BITFVAL(CHRCV,
+ CHRCV_UV_TO_BITS(di->
+ chargeConfig->chargingPoints
+ [point].microVolt));
+ switch (usb_type) {
+ case USBHOST:
+ /* set current limit to 500mA */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL,
+ BITFVAL(MUSBCHRG, 1),
+ BITFMASK(MUSBCHRG)));
+ val |= BITFVAL(CHRCC, CHRCC_UA_TO_BITS(250000));
+ break;
+ case USBCHARGER:
+ /* set current limit to 950mA */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL,
+ BITFVAL(MUSBCHRG, 3),
+ BITFMASK(MUSBCHRG)));
+ val |= BITFVAL(CHRCC, CHRCC_UA_TO_BITS(350000));
+ break;
+ case DEDICATEDCHARGER:
+ /* set current limit to 950mA */
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL,
+ BITFVAL(MUSBCHRG, 3),
+ BITFMASK(MUSBCHRG)));
+ val |= BITFVAL(CHRCC, CHRCC_UA_TO_BITS(350000));
+ break;
+ default:
+ val |=
+ BITFVAL(CHRCC,
+ CHRCC_UA_TO_BITS(di->
+ chargeConfig->chargingPoints
+ [point].microAmp));
+ break;
+ }
+
+ mask = BITFMASK(CHRCV) | BITFMASK(CHRCC);
+ CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE,
+ val, mask));
+
+ di->currChargePoint = point;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#define FULL_DEBOUNCE_TIME 3
+static int adjust_charging_parameter(struct mc34708_charger_config **config)
+{
+ struct ripley_dev_info *di =
+ container_of((config), struct ripley_dev_info, chargeConfig);
+
+ if (di->voltage_uV >=
+ (*config)->chargingPoints[di->currChargePoint].microVolt) {
+
+ /* for NOT the final charge point */
+ if (di->currChargePoint < (*config)->pointsNumber - 1) {
+ di->full_counter++;
+ if (di->full_counter > FULL_DEBOUNCE_TIME) {
+ set_charging_point(di, di->currChargePoint + 1);
+ pr_info
+ ("shift to charge point %d, volt=%d uV, curr=%d uA\n",
+ di->currChargePoint,
+ (*config)->
+ chargingPoints
+ [di->currChargePoint].microVolt,
+ (*config)->
+ chargingPoints
+ [di->currChargePoint].microAmp);
+ }
+ } else {
+ if (di->current_uA <= (*config)->toppingOffMicroAmp)
+ di->full_counter++;
+ if (di->full_counter > FULL_DEBOUNCE_TIME)
+ di->battery_status =
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ }
+
+ return 0;
+}
+
+static int ripley_charger_update_status(struct ripley_dev_info *di)
+{
+ int ret;
+ unsigned int value;
+ unsigned int reg_usb_type;
+ int usbOnline, auxOnline;
+ int restartCharging = 0;
+ int stopCharging = 0;
+
+ ret = pmic_read_reg(MC34708_REG_INT_SENSE0, &value, PMIC_ALL_BITS);
+
+ if (ret == 0) {
+ usbOnline = BITFEXT(value, USBDETS);
+ auxOnline = BITFEXT(value, AUXDETS);
+ if (!(di->aux_charger_online || di->usb_charger_online) &&
+ (usbOnline || auxOnline))
+ restartCharging = 1;
+ if ((di->aux_charger_online || di->usb_charger_online) &&
+ !(usbOnline || auxOnline))
+ stopCharging = 1;
+
+ if (auxOnline != di->aux_charger_online) {
+ msleep(500);
+ di->aux_charger_online = auxOnline;
+ dev_info(di->aux_charger.dev,
+ "aux charger status: %s\n",
+ auxOnline ? "online" : "offline");
+ power_supply_changed(&di->aux_charger);
+ }
+ if (usbOnline != di->usb_charger_online) {
+ /* need some delay to know the usb type */
+ msleep(800);
+ ret = pmic_read_reg(MC34708_REG_USB_DEVICE_TYPE,
+ &reg_usb_type, PMIC_ALL_BITS);
+ usb_type = 0;
+ if ((reg_usb_type & USBHOST) != 0) {
+ usb_type = USBHOST;
+ pr_info("USB host attached!!!\n");
+ }
+ if ((reg_usb_type & USBCHARGER) != 0) {
+ usb_type = USBCHARGER;
+ pr_info("USB charger attached!!!\n");
+ }
+ if ((reg_usb_type & DEDICATEDCHARGER) != 0) {
+ usb_type = DEDICATEDCHARGER;
+ pr_info("Dedicated charger attached!!!\n");
+ }
+ di->usb_charger_online = usbOnline;
+ dev_info(di->usb_charger.dev, "usb cable status: %s\n",
+ usbOnline ? "online" : "offline");
+ power_supply_changed(&di->usb_charger);
+ }
+
+ if (restartCharging) {
+ ripley_start_coulomb_counter();
+ enable_charger(1);
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue,
+ &di->monitor_work, HZ / 10);
+ } else if (stopCharging) {
+ ripley_stop_coulomb_counter();
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue,
+ &di->monitor_work, HZ / 10);
+ }
+ }
+ power_change_flag = 1;
+
+ return ret;
+}
+
+static int ripley_aux_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ripley_dev_info *di =
+ container_of((psy), struct ripley_dev_info, aux_charger);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->aux_charger_online;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ripley_usb_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ripley_dev_info *di =
+ container_of((psy), struct ripley_dev_info, usb_charger);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->usb_charger_online;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ripley_battery_read_status(struct ripley_dev_info *di)
+{
+ int retval;
+ int coulomb;
+ retval = ripley_get_batt_voltage(&(di->voltage_raw));
+ if (retval == 0)
+ di->voltage_uV = di->voltage_raw * BAT_VOLTAGE_UNIT_UV;
+
+ retval = ripley_get_batt_current(&(di->current_raw));
+ if (retval == 0) {
+ if (di->current_raw & 0x200)
+ di->current_uA =
+ (0x1FF - (di->current_raw & 0x1FF)) *
+ BAT_CURRENT_UNIT_UA * (-1);
+ else
+ di->current_uA =
+ (di->current_raw & 0x1FF) * BAT_CURRENT_UNIT_UA;
+ }
+ retval = ripley_get_charger_coulomb(&coulomb);
+ if (retval == 0)
+ di->accum_current_uAh = COULOMB_TO_UAH(coulomb);
+ return retval;
+}
+
+static void ripley_battery_update_status(struct ripley_dev_info *di)
+{
+ unsigned int point = 0;
+ struct mc34708_charger_config *config = di->chargeConfig;
+ int old_battery_status = di->battery_status;
+
+ if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN)
+ di->full_counter = 0;
+
+ ripley_battery_read_status(di);
+
+ if (di->usb_charger_online || di->aux_charger_online) {
+ if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN ||
+ di->battery_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ point = 0;
+ init_charger(config);
+ set_charging_point(di, point);
+ enable_charger(1);
+ } else if (di->battery_status == POWER_SUPPLY_STATUS_CHARGING)
+ adjust_charging_parameter(&(di->chargeConfig));
+ if (di->full_counter > FULL_DEBOUNCE_TIME &&
+ di->currChargePoint >= di->chargeConfig->pointsNumber) {
+ di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else
+ di->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ di->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ di->full_counter = 0;
+ }
+
+ if (power_change_flag) {
+ pr_info("battery status: %d, old status: %d, point: %d\n",
+ di->battery_status, old_battery_status,
+ di->currChargePoint);
+ power_change_flag = 0;
+ }
+ dev_dbg(di->bat.dev, "bat status: %d\n", di->battery_status);
+ if (old_battery_status != POWER_SUPPLY_STATUS_UNKNOWN &&
+ di->battery_status != old_battery_status)
+ power_supply_changed(&di->bat);
+}
+
+static void ripley_battery_work(struct work_struct *work)
+{
+ struct ripley_dev_info *di = container_of(work,
+ struct ripley_dev_info,
+ monitor_work.work);
+ const int interval = HZ * 15;
+
+ dev_dbg(di->dev, "%s\n", __func__);
+
+ ripley_battery_update_status(di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void usb_charger_online_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ ripley_charger_update_status(di);
+}
+
+static void aux_charger_online_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ ripley_charger_update_status(di);
+}
+
+static void usb_over_voltage_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ ripley_charger_update_status(di);
+}
+
+static void aux_over_voltage_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ ripley_charger_update_status(di);
+}
+
+static void battery_over_voltage_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ ripley_charger_update_status(di);
+}
+
+static void battery_over_temp_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ ripley_charger_update_status(di);
+}
+
+static void battery_charge_complete_event_callback(void *para)
+{
+ struct ripley_dev_info *di = (struct ripley_dev_info *)para;
+ pr_info("\n\n battery charge complete event, disable charging\n");
+}
+
+static int ripley_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ripley_dev_info *di = to_ripley_dev_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ ripley_charger_update_status(di);
+ ripley_battery_update_status(di);
+ }
+ val->intval = di->battery_status;
+ return 0;
+ default:
+ break;
+ }
+
+ ripley_battery_read_status(di);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->current_uA;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = di->accum_current_uAh;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = 3800000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = 3300000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ripley_battery_remove(struct platform_device *pdev)
+{
+ pmic_event_callback_t bat_event_callback;
+ struct ripley_dev_info *di = platform_get_drvdata(pdev);
+
+ bat_event_callback.func = usb_charger_online_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_unsubscribe(MC34708_EVENT_USBDET, bat_event_callback);
+ bat_event_callback.func = aux_charger_online_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_unsubscribe(MC34708_EVENT_AUXDET, bat_event_callback);
+
+ cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+ &di->monitor_work);
+ destroy_workqueue(di->monitor_wqueue);
+ power_supply_unregister(&di->bat);
+ power_supply_unregister(&di->usb_charger);
+ power_supply_unregister(&di->aux_charger);
+
+ kfree(di);
+
+ return 0;
+}
+
+static int ripley_battery_probe(struct platform_device *pdev)
+{
+ int retval = 0;
+ struct ripley_dev_info *di;
+ pmic_event_callback_t bat_event_callback;
+ di = kzalloc(sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ retval = -ENOMEM;
+ goto di_alloc_failed;
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ di->aux_charger.name = "ripley_aux_charger";
+ di->aux_charger.type = POWER_SUPPLY_TYPE_MAINS;
+ di->aux_charger.properties = ripley_aux_charger_props;
+ di->aux_charger.num_properties = ARRAY_SIZE(ripley_aux_charger_props);
+ di->aux_charger.get_property = ripley_aux_charger_get_property;
+ retval = power_supply_register(&pdev->dev, &di->aux_charger);
+ if (retval) {
+ dev_err(di->dev, "failed to register charger\n");
+ goto charger_failed;
+ }
+
+ di->usb_charger.name = "ripley_usb_charger";
+ di->usb_charger.type = POWER_SUPPLY_TYPE_USB;
+ di->usb_charger.properties = ripley_usb_charger_props;
+ di->usb_charger.num_properties = ARRAY_SIZE(ripley_usb_charger_props);
+ di->usb_charger.get_property = ripley_usb_charger_get_property;
+ retval = power_supply_register(&pdev->dev, &di->usb_charger);
+ if (retval) {
+ dev_err(di->dev, "failed to register charger\n");
+ goto charger_failed;
+ }
+
+ INIT_DELAYED_WORK(&di->monitor_work, ripley_battery_work);
+ di->monitor_wqueue =
+ create_singlethread_workqueue(dev_name(&pdev->dev));
+ if (!di->monitor_wqueue) {
+ retval = -ESRCH;
+ goto workqueue_failed;
+ }
+
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 10);
+
+ di->dev = &pdev->dev;
+ di->chargeConfig = &ripley_charge_config;
+ di->bat.name = "ripley_bat";
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = ripley_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(ripley_battery_props);
+ di->bat.get_property = ripley_battery_get_property;
+ di->bat.use_for_apm = 1;
+
+ di->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ retval = power_supply_register(&pdev->dev, &di->bat);
+ if (retval) {
+ dev_err(di->dev, "failed to register battery\n");
+ goto batt_failed;
+ }
+
+ bat_event_callback.func = usb_over_voltage_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_USBOVP, bat_event_callback);
+
+ bat_event_callback.func = aux_over_voltage_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_AUXOVP, bat_event_callback);
+
+ bat_event_callback.func = usb_charger_online_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_USBDET, bat_event_callback);
+
+ bat_event_callback.func = aux_charger_online_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_AUXDET, bat_event_callback);
+
+ bat_event_callback.func = battery_over_voltage_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_BATTOVP, bat_event_callback);
+
+ bat_event_callback.func = battery_over_temp_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_BATTOTP, bat_event_callback);
+
+ bat_event_callback.func = battery_charge_complete_event_callback;
+ bat_event_callback.param = (void *)di;
+ pmic_event_subscribe(MC34708_EVENT_CHRCMPL, bat_event_callback);
+
+ ripley_stop_coulomb_counter();
+ ripley_calibrate_coulomb_counter();
+
+ goto success;
+
+workqueue_failed:
+ power_supply_unregister(&di->aux_charger);
+ power_supply_unregister(&di->usb_charger);
+charger_failed:
+ power_supply_unregister(&di->bat);
+batt_failed:
+ kfree(di);
+di_alloc_failed:
+success:
+ dev_dbg(di->dev, "%s battery probed!\n", __func__);
+ return retval;
+
+ return 0;
+}
+
+static int ripley_battery_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ suspend_flag = 1;
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_INT_STATUS0, BITFVAL(BATTOVP, 0),
+ BITFMASK(BATTOVP)));
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_INT_MASK0, BITFVAL(BATTOVP, 1),
+ BITFMASK(BATTOVP)));
+
+ return 0;
+};
+
+static int ripley_battery_resume(struct platform_device *pdev)
+{
+ suspend_flag = 0;
+ CHECK_ERROR(pmic_write_reg
+ (MC34708_REG_INT_MASK0, BITFVAL(BATTOVP, 0),
+ BITFMASK(BATTOVP)));
+
+ return 0;
+};
+
+static struct platform_driver ripley_battery_driver_ldm = {
+ .driver = {
+ .name = "mc34708_battery",
+ .bus = &platform_bus_type,
+ },
+ .probe = ripley_battery_probe,
+ .remove = ripley_battery_remove,
+ .suspend = ripley_battery_suspend,
+ .resume = ripley_battery_resume,
+};
+
+static int __init ripley_battery_init(void)
+{
+ pr_debug("Ripley Battery driver loading...\n");
+ return platform_driver_register(&ripley_battery_driver_ldm);
+}
+
+static void __exit ripley_battery_exit(void)
+{
+ platform_driver_unregister(&ripley_battery_driver_ldm);
+ pr_debug("Ripley Battery driver successfully unloaded\n");
+}
+
+module_init(ripley_battery_init);
+module_exit(ripley_battery_exit);
+
+MODULE_DESCRIPTION("ripley_battery driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 694c2e6e539d..6239f3436a21 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -206,6 +206,11 @@ config REGULATOR_MC13892
depends on MXC_PMIC_MC13892
default y
+config REGULATOR_MC34708
+ tristate "MC34708 Regulator Support"
+ depends on MXC_PMIC_MC34708
+ default y
+
config REGULATOR_MC34704
tristate "MC34704 Regulator Support"
depends on MXC_PMIC_MC34704
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 850fdd8d45fa..c368da79db17 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += reg-mc13892.o
obj-$(CONFIG_REGULATOR_MC34704) += reg-mc34704.o
obj-$(CONFIG_REGULATOR_STMP3XXX) += stmp3xxx.o
obj-$(CONFIG_REGULATOR_MXS) += mxs-regulator.o
+obj-$(CONFIG_REGULATOR_MC34708) += reg-mc34708.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589-regulator.o
obj-$(CONFIG_REGULATOR_MC9S08DZ60) += reg-mc9s08dz60.o
diff --git a/drivers/regulator/reg-mc34708.c b/drivers/regulator/reg-mc34708.c
new file mode 100644
index 000000000000..64e4abd405f0
--- /dev/null
+++ b/drivers/regulator/reg-mc34708.c
@@ -0,0 +1,1276 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/mc34708/core.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_status.h>
+#include <linux/mfd/mc34708/mc34708.h>
+#include <linux/pmic_external.h>
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+/*!
+ * @enum regulator_voltage_vpll
+ * @brief PMIC regulator VPLL output voltage.
+ */
+enum {
+ VPLL_1_2V = 0,
+ VPLL_1_25V,
+ VPLL_1_5V,
+ VPLL_1_8V,
+};
+
+/*!
+ * @enum regulator_voltage_vusb2
+ * @brief PMIC regulator VUSB2 output voltage.
+ */
+enum {
+ VUSB2_2_5V = 0,
+ VUSB2_2_6V,
+ VUSB2_2_75V,
+ VUSB2_3V,
+};
+
+/*!
+ * @enum regulator_voltage_vdac
+ * @brief PMIC regulator VDAC output voltage.
+ */
+enum {
+ VDAC_2_5V = 0,
+ VDAC_2_6V,
+ VDAC_2_7V,
+ VDAC_2_775V,
+} regulator_voltage_vdac;
+
+/*!
+ * @enum regulator_voltage_vgen1
+ * @brief PMIC regulator VGEN1 output voltage.
+ */
+enum {
+ VGEN1_1_2V = 0,
+ VGEN1_1_25V,
+ VGEN1_1_3V,
+ VGEN1_1_35V,
+ VGEN1_1_4V,
+ VGEN1_1_45V,
+ VGEN1_1_5V,
+ VGEN1_1_55V,
+};
+
+/*!
+ * @enum regulator_voltage_vgen2
+ * @brief mc34708 PMIC regulator VGEN2 output voltage.
+ */
+enum {
+ VGEN2_2_5V = 0,
+ VGEN2_2_7V,
+ VGEN2_2_8V,
+ VGEN2_2_9V,
+ VGEN2_3V,
+ VGEN2_3_1V,
+ VGEN2_3_15V,
+ VGEN2_3_3V,
+};
+
+/*!
+ * The \b TPmicDVSTransitionSpeed enum defines the rate with which the
+ * voltage transition occurs.
+ */
+enum {
+ ESysDependent,
+ E25mVEach4us,
+ E25mVEach8us,
+ E25mvEach16us
+} DVS_transition_speed;
+
+/*
+ * Reg Regulator Mode 0
+ */
+
+#define VGEN1_EN_LSH 0
+#define VGEN1_EN_WID 1
+#define VGEN1_EN_ENABLE 1
+#define VGEN1_EN_DISABLE 0
+#define VDAC_EN_LSH 4
+#define VDAC_EN_WID 1
+#define VDAC_EN_ENABLE 1
+#define VDAC_EN_DISABLE 0
+#define VREFDDR_EN_LSH 10
+#define VREFDDR_EN_WID 1
+#define VREFDDR_EN_ENABLE 1
+#define VREFDDR_EN_DISABLE 0
+#define VGEN2_EN_LSH 12
+#define VGEN2_EN_WID 1
+#define VGEN2_EN_ENABLE 1
+#define VGEN2_EN_DISABLE 0
+#define VPLL_EN_LSH 15
+#define VPLL_EN_WID 1
+#define VPLL_EN_ENABLE 1
+#define VPLL_EN_DISABLE 0
+#define VUSB2_EN_LSH 18
+#define VUSB2_EN_WID 1
+#define VUSB2_EN_ENABLE 1
+#define VUSB2_EN_DISABLE 0
+#define VUSB_EN_LSH 3
+#define VUSB_EN_WID 1
+#define VUSB_EN_ENABLE 1
+#define VUSB_EN_DISABLE 0
+
+#define VUSBSEL_LSH 2
+#define VUSBSEL_WID 1
+#define VUSBSEL_ENABLE 1
+#define VUSBSEL_DISABLE 0
+
+/*
+ * Reg Regulator Setting 0
+ */
+#define VGEN1_LSH 0
+#define VGEN1_WID 3
+#define VDAC_LSH 4
+#define VDAC_WID 2
+#define VGEN2_LSH 6
+#define VGEN2_WID 3
+#define VPLL_LSH 9
+#define VPLL_WID 2
+#define VUSB2_LSH 11
+#define VUSB2_WID 2
+
+/*
+ * Reg Switcher 1 A/B
+ */
+#define SW1A_LSH 0
+#define SW1A_WID 6
+#define SW1A_STDBY_LSH 6
+#define SW1A_STDBY_WID 6
+
+#define SW1B_LSH 12
+#define SW1B_WID 6
+#define SW1B_STDBY_LSH 18
+#define SW1B_STDBY_WID 6
+
+/*
+ * Reg Switcher 2&3
+ */
+#define SW2_LSH 0
+#define SW2_WID 6
+#define SW2_STDBY_LSH 6
+#define SW2_STDBY_WID 6
+#define SW3_LSH 12
+#define SW3_WID 5
+#define SW3_STDBY_LSH 18
+#define SW3_STDBY_WID 5
+
+/*
+ * Reg Switcher 4 A/B
+ */
+#define SW4A_LSH 0
+#define SW4A_WID 5
+#define SW4A_STDBY_LSH 5
+#define SW4A_STDBY_WID 5
+#define SW4AHI_LSH 10
+#define SW4AHI_WID 2
+
+#define SW4B_LSH 12
+#define SW4B_WID 5
+#define SW4B_STDBY_LSH 17
+#define SW4B_STDBY_WID 5
+#define SW4BHI_LSH 22
+#define SW4BHI_WID 2
+
+/*
+ * Reg Switcher 5
+ */
+#define SW5_LSH 0
+#define SW5_WID 5
+#define SW5_STDBY_LSH 10
+#define SW5_STDBY_WID 5
+
+#define SWXHI_LSH 23
+#define SWXHI_WID 1
+#define SWXHI_ON 1
+#define SWXHI_OFF 0
+
+/*
+ * Switcher mode configuration
+ */
+#define SW_MODE_SYNC_RECT_EN 0
+#define SW_MODE_PULSE_NO_SKIP_EN 1
+#define SW_MODE_PULSE_SKIP_EN 2
+#define SW_MODE_LOW_POWER_EN 3
+
+#define dvs_speed E25mvEach16us
+
+enum {
+ SW1_MIN_UV = 650000,
+ SW1_MAX_UV = 1437500,
+ SW1_STEP_UV = 12500,
+};
+
+enum {
+ SW2_MIN_UV = 650000,
+ SW2_MAX_UV = 1437500,
+ SW2_STEP_UV = 12500,
+};
+
+enum {
+ SW3_MIN_MV = 650,
+ SW3_MAX_MV = 1425,
+ SW3_STEP_MV = 25,
+};
+
+enum {
+ SW4_MIN_MV = 1200,
+ SW4_MAX_MV = 1975,
+ SW4_STEP_MV = 25,
+ SW4_HI_2500_MV = 2500,
+ SW4_HI_3150_MV = 3150,
+ SW4_HI_3300_MV = 3300,
+};
+
+enum {
+ SW5_MIN_MV = 1200,
+ SW5_MAX_MV = 1975,
+ SW5_STEP_MV = 25,
+};
+
+enum {
+ VGEN1_MIN_MV = 1200,
+ VGEN1_MAX_MV = 1550,
+ VGEN1_STEP_MV = 50,
+};
+
+static unsigned int mv_to_bit_value(int mv, int min_mv, int max_mv, int step_mv)
+{
+ return ((unsigned int)(((mv < min_mv) ?
+ min_mv : (mv > max_mv ? max_mv : mv)
+ - min_mv) / step_mv));
+}
+
+static int bit_value_to_mv(unsigned int val, int min_mv, int step_mv)
+{
+ return ((unsigned int)val) * step_mv + min_mv;
+}
+
+static unsigned int uv_to_bit_value(int uv, int min_uv, int max_uv, int step_uv)
+{
+ return (unsigned int)(((uv < min_uv) ?
+ min_uv : (uv > max_uv ? max_uv : uv)
+ - min_uv) / step_uv);
+}
+
+static int bit_value_to_uv(unsigned int val, int min_uv, int step_uv)
+{
+ return ((unsigned int)val) * step_uv + min_uv;
+}
+
+static int mc34708_get_voltage_value(int sw, int mV)
+{
+ int voltage;
+
+ switch (sw) {
+ case MC34708_SW1A:
+ case MC34708_SW1B:
+ case MC34708_SW2:
+ if (mV < 650)
+ mV = 650;
+ if (mV > 1437)
+ mV = 1437;
+ voltage = (mV - 650) * 10 / 125;
+ break;
+ case MC34708_SW3:
+ if (mV < 650)
+ mV = 650;
+ if (mV > 1425)
+ mV = 1425;
+ voltage = (mV - 650) / 25;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return voltage;
+}
+
+static int mc34708_vpll_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0, register1 = 0;
+ int voltage, mV = uV / 1000;
+
+ if (mV < 1250)
+ voltage = VPLL_1_2V;
+ else if (mV < 1500)
+ voltage = VPLL_1_25V;
+ else if (mV < 1800)
+ voltage = VPLL_1_5V;
+ else
+ voltage = VPLL_1_8V;
+
+ register_val = BITFVAL(VPLL, voltage);
+ register_mask = BITFMASK(VPLL);
+ register1 = MC34708_REG_REGULATOR_SETTING0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_vpll_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_REGULATOR_SETTING0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VPLL);
+
+ switch (voltage) {
+ case VPLL_1_2V:
+ mV = 1200;
+ break;
+ case VPLL_1_25V:
+ mV = 1250;
+ break;
+ case VPLL_1_5V:
+ mV = 1500;
+ break;
+ case VPLL_1_8V:
+ mV = 1800;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc34708_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ unsigned int register1;
+ unsigned int register_mask;
+ int id = rdev_get_id(rdev);
+ unsigned int register_val = 0;
+
+ switch (id) {
+ case MC34708_SWBST:
+ register_mask = BITFMASK(VUSBSEL);
+ register1 = MC34708_REG_REGULATOR_MODE0;
+ break;
+ case MC34708_VUSB:
+ register_mask = BITFMASK(VUSB_EN);
+ register1 = MC34708_REG_REGULATOR_MODE0;
+ break;
+ default:
+ return 1;
+ }
+ CHECK_ERROR(pmic_read_reg(register1, &register_val, register_mask));
+ return (register_val != 0);
+}
+
+static int mc34708_vusb2_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if (mV < 2600)
+ voltage = VUSB2_2_5V;
+ else if (mV < 2750)
+ voltage = VUSB2_2_6V;
+ else if (mV < 3000)
+ voltage = VUSB2_2_75V;
+ else
+ voltage = VUSB2_3V;
+
+ register_val = BITFVAL(VUSB2, voltage);
+ register_mask = BITFMASK(VUSB2);
+ register1 = MC34708_REG_REGULATOR_SETTING0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_vusb2_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_REGULATOR_SETTING0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VUSB2);
+
+ switch (voltage) {
+ case VUSB2_2_5V:
+ mV = 2500;
+ break;
+ case VUSB2_2_6V:
+ mV = 2600;
+ break;
+ case VUSB2_2_75V:
+ mV = 2750;
+ break;
+ case VUSB2_3V:
+ mV = 3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc34708_vgen1_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ voltage = mv_to_bit_value(mV, VGEN1_MIN_MV,
+ VGEN1_MAX_MV, VGEN1_STEP_MV);
+ register_val = BITFVAL(VGEN1, voltage);
+ register_mask = BITFMASK(VGEN1);
+ register1 = MC34708_REG_REGULATOR_SETTING0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_vgen1_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_REGULATOR_SETTING0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VGEN1);
+ mV = bit_value_to_mv(voltage, VGEN1_MIN_MV, VGEN1_STEP_MV);
+
+ return mV * 1000;
+}
+
+static int mc34708_ldo_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int id = rdev_get_id(reg);
+
+ switch (id) {
+ case MC34708_VREFDDR:
+ register_val = BITFVAL(VREFDDR_EN, VREFDDR_EN_ENABLE);
+ register_mask = BITFMASK(VREFDDR_EN);
+ break;
+ case MC34708_VUSB:
+ register_val = BITFVAL(VUSB_EN, VUSB_EN_ENABLE);
+ register_mask = BITFMASK(VUSB_EN);
+ break;
+ case MC34708_VUSB2:
+ register_val = BITFVAL(VUSB2_EN, VUSB2_EN_ENABLE);
+ register_mask = BITFMASK(VUSB2_EN);
+ break;
+ case MC34708_VDAC:
+ register_val = BITFVAL(VDAC_EN, VDAC_EN_ENABLE);
+ register_mask = BITFMASK(VDAC_EN);
+ break;
+ case MC34708_VGEN1:
+ register_val = BITFVAL(VGEN1_EN, VGEN1_EN_ENABLE);
+ register_mask = BITFMASK(VGEN1_EN);
+ break;
+ case MC34708_VGEN2:
+ register_val = BITFVAL(VGEN2_EN, VGEN2_EN_ENABLE);
+ register_mask = BITFMASK(VGEN2_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ register1 = MC34708_REG_REGULATOR_MODE0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_ldo_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int id = rdev_get_id(reg);
+ switch (id) {
+ case MC34708_SWBST:
+ register_val = BITFVAL(VUSBSEL, VUSBSEL_DISABLE);
+ register_mask = BITFMASK(VUSBSEL);
+ break;
+ case MC34708_VREFDDR:
+ register_val = BITFVAL(VREFDDR_EN, VREFDDR_EN_DISABLE);
+ register_mask = BITFMASK(VREFDDR_EN);
+ break;
+ case MC34708_VUSB:
+ register_val = BITFVAL(VUSB_EN, VUSB_EN_DISABLE);
+ register_mask = BITFMASK(VUSB_EN);
+ break;
+ case MC34708_VUSB2:
+ register_val = BITFVAL(VUSB2_EN, VUSB2_EN_DISABLE);
+ register_mask = BITFMASK(VUSB2_EN);
+ break;
+ case MC34708_VDAC:
+ register_val = BITFVAL(VDAC_EN, VDAC_EN_DISABLE);
+ register_mask = BITFMASK(VDAC_EN);
+ break;
+ case MC34708_VGEN1:
+ register_val = BITFVAL(VGEN1_EN, VGEN1_EN_DISABLE);
+ register_mask = BITFMASK(VGEN1_EN);
+ break;
+ case MC34708_VGEN2:
+ register_val = BITFVAL(VGEN2_EN, VGEN2_EN_DISABLE);
+ register_mask = BITFMASK(VGEN2_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ register1 = MC34708_REG_REGULATOR_MODE0;
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_vdac_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if (mV < 2600)
+ voltage = VDAC_2_5V;
+ else if (mV < 2700)
+ voltage = VDAC_2_6V;
+ else if (mV < 2775)
+ voltage = VDAC_2_7V;
+ else
+ voltage = VDAC_2_775V;
+
+ register_val = BITFVAL(VDAC, voltage);
+ register_mask = BITFMASK(VDAC);
+ register1 = MC34708_REG_REGULATOR_SETTING0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_vdac_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_REGULATOR_SETTING0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VDAC);
+
+ switch (voltage) {
+ case VDAC_2_5V:
+ mV = 2500;
+ break;
+ case VDAC_2_6V:
+ mV = 2600;
+ break;
+ case VDAC_2_7V:
+ mV = 2700;
+ break;
+ case VDAC_2_775V:
+ mV = 2775;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc34708_vgen2_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if (mV < 2700)
+ voltage = VGEN2_2_5V;
+ else if (mV < 2800)
+ voltage = VGEN2_2_7V;
+ else if (mV < 2900)
+ voltage = VGEN2_2_8V;
+ else if (mV < 3000)
+ voltage = VGEN2_2_9V;
+ else if (mV < 3100)
+ voltage = VGEN2_3V;
+ else if (mV < 3150)
+ voltage = VGEN2_3_1V;
+ else if (mV < 3300)
+ voltage = VGEN2_3_15V;
+ else
+ voltage = VGEN2_3_3V;
+
+ register1 = MC34708_REG_REGULATOR_SETTING0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_vgen2_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_REGULATOR_SETTING0,
+ &register_val, PMIC_ALL_BITS));
+
+ voltage = BITFEXT(register_val, VGEN2);
+
+ switch (voltage) {
+ case VGEN2_2_5V:
+ mV = 2500;
+ break;
+ case VGEN2_2_7V:
+ mV = 2700;
+ break;
+ case VGEN2_2_8V:
+ mV = 2800;
+ break;
+ case VGEN2_2_9V:
+ mV = 2900;
+ break;
+ case VGEN2_3V:
+ mV = 3000;
+ break;
+ case VGEN2_3_1V:
+ mV = 3100;
+ break;
+ case VGEN2_3_15V:
+ mV = 3150;
+ break;
+ case VGEN2_3_3V:
+ mV = 3300;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc34708_sw_set_normal_voltage(struct regulator_dev *reg, int minuV,
+ int uV)
+{
+ unsigned int register_val = 0, register_mask = 0, register1 = 0;
+ int voltage, mV = uV / 1000;
+ int id = rdev_get_id(reg);
+
+ switch (id) {
+ case MC34708_SW1A:
+ voltage =
+ uv_to_bit_value(mV * 1000, SW1_MIN_UV, SW1_MAX_UV,
+ SW1_STEP_UV);
+ register_val = BITFVAL(SW1A, voltage);
+ register_mask = BITFMASK(SW1A);
+ register1 = MC34708_REG_SW1AB;
+ break;
+ case MC34708_SW1B:
+ voltage =
+ uv_to_bit_value(mV * 1000, SW1_MIN_UV, SW1_MAX_UV,
+ SW1_STEP_UV);
+ register_val = BITFVAL(SW1B, voltage);
+ register_mask = BITFMASK(SW1B);
+ register1 = MC34708_REG_SW1AB;
+ break;
+ case MC34708_SW2:
+ voltage =
+ uv_to_bit_value(mV, SW2_MIN_UV, SW2_MAX_UV, SW2_STEP_UV);
+ register_val = BITFVAL(SW2, voltage);
+ register_mask = BITFMASK(SW2);
+ register1 = MC34708_REG_SW2_3;
+ break;
+ case MC34708_SW3:
+ voltage =
+ mv_to_bit_value(mV, SW3_MIN_MV, SW3_MAX_MV, SW3_STEP_MV);
+ register_val = BITFVAL(SW3, voltage);
+ register_mask = BITFMASK(SW3);
+ register1 = MC34708_REG_SW2_3;
+ break;
+ case MC34708_SW4A:
+ if (mV < SW4_HI_2500_MV) {
+ voltage =
+ mv_to_bit_value(mV, SW4_MIN_MV, SW4_MAX_MV,
+ SW4_STEP_MV);
+ register_val =
+ BITFVAL(SW4A, voltage) | BITFVAL(SW4AHI, 0);
+ register_mask = BITFMASK(SW4A) | BITFMASK(SW4AHI);
+ register1 = MC34708_REG_SW4AB;
+ } else {
+ if (mV < SW4_HI_3150_MV)
+ register_val = BITFVAL(SW4AHI, 1);
+ else if (mV < SW4_HI_3300_MV)
+ register_val = BITFVAL(SW4AHI, 2);
+ else
+ register_val = BITFVAL(SW4AHI, 3);
+ register_mask = BITFMASK(SW4AHI);
+ register1 = MC34708_REG_SW4AB;
+ }
+ break;
+ case MC34708_SW4B:
+ if (mV < SW4_HI_2500_MV) {
+ voltage =
+ mv_to_bit_value(mV, SW4_MIN_MV, SW4_MAX_MV,
+ SW4_STEP_MV);
+ register_val =
+ BITFVAL(SW4B, voltage) | BITFVAL(SW4BHI, 0);
+ register_mask = BITFMASK(SW4B) | BITFMASK(SW4BHI);
+ register1 = MC34708_REG_SW4AB;
+ } else {
+ if (mV < SW4_HI_3150_MV)
+ register_val = BITFVAL(SW4BHI, 1);
+ else if (mV < SW4_HI_3300_MV)
+ register_val = BITFVAL(SW4BHI, 2);
+ else
+ register_val = BITFVAL(SW4BHI, 3);
+ register_mask = BITFMASK(SW4BHI);
+ register1 = MC34708_REG_SW4AB;
+ }
+ break;
+ case MC34708_SW5:
+ voltage =
+ mv_to_bit_value(mV, SW5_MIN_MV, SW5_MAX_MV, SW5_STEP_MV);
+ register_val = BITFVAL(SW5, voltage);
+ register_mask = BITFMASK(SW5);
+ register1 = MC34708_REG_SW5;
+ break;
+ default:
+ break;
+ }
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_sw_get_normal_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+ int id = rdev_get_id(reg);
+
+ switch (id) {
+ case MC34708_SW1A:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW1AB,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW1A);
+ mV = bit_value_to_uv(voltage, SW1_MIN_UV, SW1_STEP_UV) / 1000;
+ break;
+ case MC34708_SW1B:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW1AB,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW1B);
+ mV = bit_value_to_uv(voltage, SW1_MIN_UV, SW1_STEP_UV) / 1000;
+ break;
+ case MC34708_SW2:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW2_3,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW2);
+ mV = bit_value_to_uv(voltage, SW2_MIN_UV, SW2_STEP_UV) / 1000;
+ break;
+ case MC34708_SW3:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW2_3,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW3);
+ mV = bit_value_to_mv(voltage, SW3_MIN_MV, SW3_STEP_MV);
+ break;
+ case MC34708_SW4A:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW4AB,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW4AHI);
+ if (voltage == 0) {
+ voltage = BITFEXT(register_val, SW4A);
+ mV = bit_value_to_mv(voltage, SW4_MIN_MV, SW4_STEP_MV);
+ } else if (voltage == 1)
+ mV = SW4_HI_2500_MV;
+ else if (voltage == 2)
+ mV = SW4_HI_3150_MV;
+ else
+ mV = SW4_HI_3300_MV;
+ break;
+ case MC34708_SW4B:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW4AB,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW4BHI);
+ if (voltage == 0) {
+ voltage = BITFEXT(register_val, SW4B);
+ mV = bit_value_to_mv(voltage, SW4_MIN_MV, SW4_STEP_MV);
+ } else if (voltage == 1)
+ mV = SW4_HI_2500_MV;
+ else if (voltage == 2)
+ mV = SW4_HI_3150_MV;
+ else
+ mV = SW4_HI_3300_MV;
+ break;
+ case MC34708_SW5:
+ CHECK_ERROR(pmic_read_reg(MC34708_REG_SW5,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW5);
+ mV = bit_value_to_mv(voltage, SW5_MIN_MV, SW5_STEP_MV);
+ break;
+ default:
+ break;
+ }
+ if (mV == 0)
+ return -EINVAL;
+ else
+ return mV * 1000;
+}
+
+static int mc34708_sw_normal_enable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc34708_sw_normal_disable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc34708_sw_stby_enable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc34708_sw_stby_disable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc34708_sw_set_stby_voltage(struct regulator_dev *reg, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0, register_valtest = 0;
+ unsigned int register1 = 0;
+
+ int voltage, mV = uV / 1000;
+ int sw = rdev_get_id(reg);
+
+ voltage = mc34708_get_voltage_value(sw, mV);
+
+ switch (sw) {
+ case MC34708_SW1A:
+ register1 = REG_MC34708_SW_1_A_B;
+ register_val = BITFVAL(SW1A_STDBY, voltage);
+ register_mask = BITFMASK(SW1A_STDBY);
+ break;
+ case MC34708_SW1B:
+ register1 = REG_MC34708_SW_1_A_B;
+ register_val = BITFVAL(SW1B_STDBY, voltage);
+ register_mask = BITFMASK(SW1B_STDBY);
+ break;
+ case MC34708_SW2:
+ register1 = REG_MC34708_SW_2_3;
+ register_val = BITFVAL(SW2_STDBY, voltage);
+ register_mask = BITFMASK(SW2_STDBY);
+ break;
+ case MC34708_SW3:
+ register1 = REG_MC34708_SW_2_3;
+ register_val = BITFVAL(SW3_STDBY, voltage);
+ register_mask = BITFMASK(SW3_STDBY);
+ break;
+ case MC34708_SW4A:
+ register1 = REG_MC34708_SW_4_A_B;
+ register_val = BITFVAL(SW4A_STDBY, voltage);
+ register_mask = BITFMASK(SW4A_STDBY);
+ break;
+ case MC34708_SW4B:
+ register1 = REG_MC34708_SW_4_A_B;
+ register_val = BITFVAL(SW4B_STDBY, voltage);
+ register_mask = BITFMASK(SW4B_STDBY);
+ break;
+ case MC34708_SW5:
+ register1 = REG_MC34708_SW_5;
+ register_val = BITFVAL(SW5_STDBY, voltage);
+ register_mask = BITFMASK(SW5_STDBY);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_sw_set_normal_mode(struct regulator_dev *reg,
+ unsigned int mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int register1 = 0;
+ unsigned int l_mode;
+ int sw = rdev_get_id(reg);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ /* SYNC RECT mode */
+ l_mode = SW_MODE_SYNC_RECT_EN;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ /* PULSE SKIP mode */
+ l_mode = SW_MODE_PULSE_SKIP_EN;
+ break;
+ case REGULATOR_MODE_IDLE:
+ /* LOW POWER mode */
+ l_mode = SW_MODE_LOW_POWER_EN;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ /* NO PULSE SKIP mode */
+ l_mode = SW_MODE_PULSE_NO_SKIP_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return pmic_write_reg(register1, reg_val, reg_mask);
+}
+
+static unsigned int mc34708_sw_get_normal_mode(struct regulator_dev *reg)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int register1 = 0;
+ unsigned int l_mode = 0;
+ int sw = rdev_get_id(reg);
+ int ret = 0;
+
+ ret = pmic_read_reg(register1, &reg_val, reg_mask);
+ return ret;
+
+}
+
+static int mc34708_sw_set_stby_mode(struct regulator_dev *reg,
+ unsigned int mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int register1 = 0;
+ unsigned int l_mode;
+ int sw = rdev_get_id(reg);
+
+ return pmic_write_reg(register1, reg_val, reg_mask);
+}
+
+static int mc34708_swbst_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int id = rdev_get_id(reg);
+
+ switch (id) {
+ case MC34708_SWBST:
+ register_val = 0xC; /* SWBSTMODE=0x3 */
+ register_mask = 0xC;
+ pmic_write_reg(MC34708_REG_SWBST_CTL, register_val,
+ register_mask);
+ register_val = BITFVAL(VUSBSEL, VUSBSEL_ENABLE);
+ register_mask = BITFMASK(VUSBSEL);
+ break;
+ default:
+ return -EINVAL;
+ }
+ register1 = MC34708_REG_REGULATOR_MODE0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc34708_swbst_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int id = rdev_get_id(reg);
+
+ switch (id) {
+ case MC34708_SWBST:
+ register_val = BITFVAL(VUSBSEL, VUSBSEL_DISABLE);
+ register_mask = BITFMASK(VUSBSEL);
+ break;
+ default:
+ return -EINVAL;
+ }
+ register1 = MC34708_REG_REGULATOR_MODE0;
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static struct regulator_ops mc34708_vrefddr_ops = {
+ .enable = mc34708_ldo_enable,
+ .disable = mc34708_ldo_disable,
+};
+
+static struct regulator_ops mc34708_vpll_ops = {
+ .set_voltage = mc34708_vpll_set_voltage,
+ .get_voltage = mc34708_vpll_get_voltage,
+};
+
+static struct regulator_ops mc34708_swbst_ops = {
+ .enable = mc34708_swbst_enable,
+ .disable = mc34708_swbst_disable,
+ .is_enabled = mc34708_regulator_is_enabled,
+};
+
+static struct regulator_ops mc34708_vusb_ops = {
+ .enable = mc34708_ldo_enable,
+ .disable = mc34708_ldo_disable,
+ .is_enabled = mc34708_regulator_is_enabled,
+};
+
+static struct regulator_ops mc34708_vusb2_ops = {
+ .set_voltage = mc34708_vusb2_set_voltage,
+ .get_voltage = mc34708_vusb2_get_voltage,
+ .enable = mc34708_ldo_enable,
+ .disable = mc34708_ldo_disable,
+};
+
+static struct regulator_ops mc34708_vgen1_ops = {
+ .set_voltage = mc34708_vgen1_set_voltage,
+ .get_voltage = mc34708_vgen1_get_voltage,
+ .enable = mc34708_ldo_enable,
+ .disable = mc34708_ldo_disable,
+};
+
+static struct regulator_ops mc34708_vgen2_ops = {
+ .set_voltage = mc34708_vgen2_set_voltage,
+ .get_voltage = mc34708_vgen2_get_voltage,
+ .enable = mc34708_ldo_enable,
+ .disable = mc34708_ldo_disable,
+};
+
+static struct regulator_ops mc34708_vdac_ops = {
+ .set_voltage = mc34708_vdac_set_voltage,
+ .get_voltage = mc34708_vdac_get_voltage,
+ .enable = mc34708_ldo_enable,
+ .disable = mc34708_ldo_disable,
+};
+
+static struct regulator_ops mc34708_sw_ops = {
+ .set_voltage = mc34708_sw_set_normal_voltage,
+ .get_voltage = mc34708_sw_get_normal_voltage,
+ .get_mode = mc34708_sw_get_normal_mode,
+ .set_mode = mc34708_sw_set_normal_mode,
+ .set_suspend_voltage = mc34708_sw_set_stby_voltage,
+ .set_suspend_enable = mc34708_sw_stby_enable,
+ .set_suspend_disable = mc34708_sw_stby_disable,
+ .set_suspend_mode = mc34708_sw_set_stby_mode,
+};
+
+static struct regulator_desc reg_mc34708[] = {
+ {
+ .name = "SW1A",
+ .id = MC34708_SW1A,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SW1B",
+ .id = MC34708_SW1B,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SW2",
+ .id = MC34708_SW2,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SW3",
+ .id = MC34708_SW3,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SW4A",
+ .id = MC34708_SW4A,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SW4B",
+ .id = MC34708_SW4B,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SW5",
+ .id = MC34708_SW5,
+ .ops = &mc34708_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "SWBST",
+ .id = MC34708_SWBST,
+ .ops = &mc34708_swbst_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VPLL",
+ .id = MC34708_VPLL,
+ .ops = &mc34708_vpll_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VREFDDR",
+ .id = MC34708_VREFDDR,
+ .ops = &mc34708_vrefddr_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VUSB",
+ .id = MC34708_VUSB,
+ .ops = &mc34708_vusb_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VUSB2",
+ .id = MC34708_VUSB2,
+ .ops = &mc34708_vusb2_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VDAC",
+ .id = MC34708_VDAC,
+ .ops = &mc34708_vdac_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VGEN1",
+ .id = MC34708_VGEN1,
+ .ops = &mc34708_vgen1_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "VGEN2",
+ .id = MC34708_VGEN2,
+ .ops = &mc34708_vgen2_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+};
+
+/*
+ * Init and Exit
+ */
+
+static int reg_mc34708_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+ /* register regulator */
+ rdev = regulator_register(&reg_mc34708[pdev->id], &pdev->dev,
+ pdev->dev.platform_data,
+ dev_get_drvdata(&pdev->dev));
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ reg_mc34708[pdev->id].name);
+ return PTR_ERR(rdev);
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int mc34708_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+int mc34708_register_regulator(struct mc34708 *mc34708, int reg,
+ struct regulator_init_data *initdata)
+{
+ struct platform_device *pdev;
+ int ret;
+ if (mc34708->pmic.pdev[reg])
+ return -EBUSY;
+
+ pdev = platform_device_alloc("mc34708-regulatr", reg);
+ if (!pdev)
+ return -ENOMEM;
+
+ mc34708->pmic.pdev[reg] = pdev;
+
+ initdata->driver_data = mc34708;
+
+ pdev->dev.platform_data = initdata;
+ pdev->dev.parent = mc34708->dev;
+ ret = platform_device_add(pdev);
+
+ if (ret != 0) {
+ dev_err(mc34708->dev, "Failed to register regulator %d: %d\n",
+ reg, ret);
+ platform_device_del(pdev);
+ mc34708->pmic.pdev[reg] = NULL;
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(mc34708_register_regulator);
+
+static struct platform_driver mc34708_regulator_driver = {
+ .probe = reg_mc34708_probe,
+ .remove = mc34708_regulator_remove,
+ .driver = {
+ .name = "mc34708-regulatr",
+ /* o left out due to string length */
+ },
+};
+
+static int __init mc34708_regulator_subsys_init(void)
+{
+ return platform_driver_register(&mc34708_regulator_driver);
+}
+
+subsys_initcall(mc34708_regulator_subsys_init);
+
+static void __exit mc34708_regulator_exit(void)
+{
+ platform_driver_unregister(&mc34708_regulator_driver);
+}
+
+module_exit(mc34708_regulator_exit);
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("mc34708 Regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/mc34708/core.h b/include/linux/mfd/mc34708/core.h
new file mode 100644
index 000000000000..9da55b6bbe75
--- /dev/null
+++ b/include/linux/mfd/mc34708/core.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __LINUX_MFD_MC34708_CORE_H_
+#define __LINUX_MFD_MC34708_CORE_H_
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/pmic_external.h>
+/*!
+ * brief PMIC regulators.
+ */
+#define MC34708_SW1A 0
+#define MC34708_SW1B 1
+#define MC34708_SW2 2
+#define MC34708_SW3 3
+#define MC34708_SW4A 4
+#define MC34708_SW4B 5
+#define MC34708_SW5 6
+#define MC34708_SWBST 7
+#define MC34708_VPLL 8
+#define MC34708_VREFDDR 9
+#define MC34708_VUSB 10
+#define MC34708_VUSB2 11
+#define MC34708_VDAC 12
+#define MC34708_VGEN1 13
+#define MC34708_VGEN2 14
+#define MC34708_REG_NUM 15
+
+struct mc34708;
+struct regulator_init_data;
+
+struct mc34708_platform_data {
+ int (*init)(struct mc34708 *);
+ void *(*pmic_alloc_data)(struct device *dev);
+ int (*pmic_init_registers)(void);
+ void (*pmic_get_revision)(pmic_version_t *ver);
+};
+
+struct mc34708_pmic {
+ /* regulator devices */
+ struct platform_device *pdev[MC34708_REG_NUM];
+};
+
+struct mc34708 {
+ int rev; /* chip revision */
+
+ struct device *dev;
+
+ /* device IO */
+ union {
+ struct i2c_client *i2c_client;
+ struct spi_device *spi_device;
+ };
+ u16 *reg_cache;
+
+ /* Client devices */
+ struct mc34708_pmic pmic;
+};
+
+int mc34708_register_regulator(struct mc34708 *mc34708, int reg,
+ struct regulator_init_data *initdata);
+void *mc34708_alloc_data(struct device *dev);
+int mc34708_init_registers(void);
+void mc34708_get_revision(pmic_version_t *ver);
+
+#endif
diff --git a/include/linux/mfd/mc34708/mc34708.h b/include/linux/mfd/mc34708/mc34708.h
new file mode 100644
index 000000000000..cf56bfeb68ff
--- /dev/null
+++ b/include/linux/mfd/mc34708/mc34708.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_MXC_PMIC_MC34708_EXTERNAL_H__
+#define __ASM_ARCH_MXC_PMIC_MC34708_EXTERNAL_H__
+
+
+enum {
+ MC34708_REG_INT_STATUS0 = 0,
+ MC34708_REG_INT_MASK0,
+ MC34708_REG_INT_SENSE0,
+ MC34708_REG_INT_STATUS1,
+ MC34708_REG_INT_MASK1,
+ MC34708_REG_INT_SENSE1,
+ MC34708_REG_POWER_UP_MODE_SENSE,
+ MC34708_REG_IDENTIFICATION,
+ MC34708_REG_REGULATOR_FAULT_SENSE,
+ MC34708_REG_ACC0,
+ MC34708_REG_ACC1,
+ MC34708_REG_UNUSED0,
+ MC34708_REG_UNUSED1,
+ MC34708_REG_POWER_CTL0,
+ MC34708_REG_POWER_CTL1,
+ MC34708_REG_POWER_CTL2,
+ MC34708_REG_MEM_A,
+ MC34708_REG_MEM_B,
+ MC34708_REG_MEM_C,
+ MC34708_REG_MEM_D,
+ MC34708_REG_RTC_TIME,
+ MC34708_REG_RTC_ALARM,
+ MC34708_REG_RTC_DAY,
+ MC34708_REG_RTC_DAY_ALARM,
+ MC34708_REG_SW1AB,
+ MC34708_REG_SW2_3,
+ MC34708_REG_SW4AB,
+ MC34708_REG_SW5,
+ MC34708_REG_SW_OP_MODE_1_2,
+ MC34708_REG_SW_OP_MODE_3_4_5,
+ MC34708_REG_REGULATOR_SETTING0,
+ MC34708_REG_SWBST_CTL,
+ MC34708_REG_REGULATOR_MODE0,
+ MC34708_REG_GPIOLV0_CTL,
+ MC34708_REG_GPIOLV1_CTL,
+ MC34708_REG_GPIOLV2_CTL,
+ MC34708_REG_GPIOLV3_CTL,
+ MC34708_REG_USB_TIMING,
+ MC34708_REG_USB_BUTTON,
+ MC34708_REG_USB_CTL,
+ MC34708_REG_USB_DEVICE_TYPE,
+ MC34708_REG_UNUSED2,
+ MC34708_REG_UNUSED3,
+ MC34708_REG_ADC0,
+ MC34708_REG_ADC1,
+ MC34708_REG_ADC2,
+ MC34708_REG_ADC3,
+ MC34708_REG_ADC4,
+ MC34708_REG_ADC5,
+ MC34708_REG_ADC6,
+ MC34708_REG_ADC7,
+ MC34708_REG_BATTERY_PROFILE,
+ MC34708_REG_CHARGER_DEBOUNCE,
+ MC34708_REG_CHARGER_SOURCE,
+ MC34708_REG_CHARGER_LED_CTL,
+ MC34708_REG_PWM_CTL,
+ MC34708_REG_UNUSED4,
+ MC34708_REG_UNUSED5,
+ MC34708_REG_UNUSED6,
+ MC34708_REG_UNUSED7,
+ MC34708_REG_UNUSED8,
+ MC34708_REG_UNUSED9,
+ MC34708_REG_UNUSED10,
+ MC34708_REG_UNUSED11,
+};
+
+enum {
+ MC34708_EVENT_ADCDONEI = 0,
+ MC34708_EVENT_TSDONEI = 1,
+ MC34708_EVENT_TSPENDET = 2,
+ MC34708_EVENT_USBDET = 3,
+ MC34708_EVENT_AUXDET = 4,
+ MC34708_EVENT_USBOVP = 5,
+ MC34708_EVENT_AUXOVP = 6,
+ MC34708_EVENT_CHRTIMEEXP = 7,
+ MC34708_EVENT_BATTOTP = 8,
+ MC34708_EVENT_BATTOVP = 9,
+ MC34708_EVENT_CHRCMPL = 10,
+ MC34708_EVENT_WKVBUSDET = 11,
+ MC34708_EVENT_WKAUXDET = 12,
+ MC34708_EVENT_LOWBATT = 13,
+ MC34708_EVENT_VBUSREGMI = 14,
+ MC34708_EVENT_ATTACH = 15,
+ MC34708_EVENT_DETACH = 16,
+ MC34708_EVENT_KP = 17,
+ MC34708_EVENT_LKP = 18,
+ MC34708_EVENT_LKR = 19,
+ MC34708_EVENT_UKNOWN_ATTA = 20,
+ MC34708_EVENT_ADC_CHANGE = 21,
+ MC34708_EVENT_STUCK_KEY = 22,
+ MC34708_EVENT_STUCK_KEY_RCV = 23,
+
+ MC34708_EVENT_1HZI = 24,
+ MC34708_EVENT_TODAI = 25,
+ MC34708_EVENT_Unused1 = 26,
+ MC34708_EVENT_PWRON1I = 27,
+ MC34708_EVENT_PWRON2I = 28,
+ MC34708_EVENT_WDIRESETI = 29,
+ MC34708_EVENT_SYSRSTI = 30,
+ MC34708_EVENT_RTCRSTI = 31,
+ MC34708_EVENT_PCI = 32,
+ MC34708_EVENT_WARMI = 33,
+ MC34708_EVENT_MEMHLDI = 34,
+ MC34708_EVENT_Unused2 = 35,
+ MC34708_EVENT_THWARNLI = 36,
+ MC34708_EVENT_THWARNHI = 37,
+ MC34708_EVENT_CLKI = 38,
+ MC34708_EVENT_Unused3 = 39,
+ MC34708_EVENT_SCPI = 40,
+};
+#endif
diff --git a/include/linux/mfd/mc34708/mc34708_adc.h b/include/linux/mfd/mc34708/mc34708_adc.h
new file mode 100644
index 000000000000..721d7882ed10
--- /dev/null
+++ b/include/linux/mfd/mc34708/mc34708_adc.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_MXC_MC34708_PMIC_ADC_H__
+#define __ASM_ARCH_MXC_MC34708_PMIC_ADC_H__
+
+/*!
+ * @defgroup PMIC_ADC PMIC Digitizer Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*!
+ * This enumeration defines input channels for PMIC ADC
+ */
+
+typedef enum {
+ BATTERY_VOLTAGE,
+ BATTERY_CURRENT,
+ APPLICATION_SUPPLY_VOLTAGE,
+ DIE_TEMP,
+ AUX_CHARGER_VOLTAGE,
+ USB_VOLTAGE,
+ RESERVER0,
+ BATTERY_THEMISTOR,
+ COINCELL_VOLTAGE,
+ GEN_PURPOSE_AD9,
+ GEN_PURPOSE_AD10,
+ GEN_PURPOSE_AD11,
+ GEN_PURPOSE_AD12,
+ GEN_PURPOSE_AD13,
+ GEN_PURPOSE_AD14,
+ GEN_PURPOSE_AD15,
+} t_channel;
+
+
+/* EXPORTED FUNCTIONS */
+
+#ifdef __KERNEL__
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS mc34708_pmic_adc_init(void);
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS mc34708_pmic_adc_deinit(void);
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to the conversion result. The memory
+ * should be allocated by the caller of this function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS mc34708_pmic_adc_convert(t_channel channel, unsigned short *result);
+
+
+
+#endif /* _KERNEL */
+#endif /* __ASM_ARCH_MXC_PMIC_ADC_H__ */
diff --git a/include/linux/mfd/mc34708/mc34708_battery.h b/include/linux/mfd/mc34708/mc34708_battery.h
new file mode 100644
index 000000000000..c38c4486a5e8
--- /dev/null
+++ b/include/linux/mfd/mc34708/mc34708_battery.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _MC34708_BC_H
+#define _MC34708_BC_H
+
+#include <linux/types.h>
+
+#define RIPLIEY_BC_MAX_RESTART_CYCLES 100
+
+#define RIPLIEY_BC_LIION_CHARGING_VOLTAGE 4200
+#define RIPLIEY_BC_ALKALINE_NIMH_CHARGING_VOLTAGE 1750
+
+/* brief Defines battery charger states. */
+typedef enum _ddi_bc_State {
+ /* brief TBD */
+ RIPLIEY_BC_STATE_UNINITIALIZED = 0,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_BROKEN = 1,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_DISABLED = 2,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_WAITING_TO_CHARGE = 3,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_CONDITIONING = 4,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_CHARGING = 5,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_TOPPING_OFF = 6,
+ /* brief TBD */
+ RIPLIEY_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE = 7,
+
+} ddi_bc_State_t;
+
+typedef enum _ddi_bc_BrokenReason {
+ /* brief TBD */
+ RIPLIEY_BC_BROKEN_UNINITIALIZED = 0,
+ /* brief TBD */
+ RIPLIEY_BC_BROKEN_CHARGING_TIMEOUT = 1,
+ /* brief TBD */
+ RIPLIEY_BC_BROKEN_FORCED_BY_APPLICATION = 2,
+ /* brief TBD */
+ RIPLIEY_BC_BROKEN_EXTERNAL_BATTERY_VOLTAGE_DETECTED = 3,
+ /* brief TBD */
+ RIPLIEY_BC_BROKEN_NO_BATTERY_DETECTED = 4,
+
+} ddi_bc_BrokenReason_t;
+
+struct mc34708_charger_setting_point {
+ u32 microVolt;
+ u32 microAmp;
+};
+
+/* brief Defines the battery charger configuration. */
+typedef struct mc34708_charger_config {
+
+ u32 batteryTempLow;
+ u32 batteryTempHigh;
+ s32 hasTempSensor;
+
+ u32 trickleThreshold;
+
+ u32 vbusThresholdLow;
+ u32 vbusThresholdWeak;
+ u32 vbusThresholdHigh;
+
+ u32 vauxThresholdLow;
+ u32 vauxThresholdWeak;
+ u32 vauxThresholdHigh;
+
+ u32 lowBattThreshold;
+
+ u32 toppingOffMicroAmp;
+
+ struct mc34708_charger_setting_point *chargingPoints;
+ u32 pointsNumber;
+
+};
+
+/* Status returned by Battery Charger functions. */
+
+typedef enum _ddi_bc_Status {
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_SUCCESS = 0,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_HARDWARE_DISABLED,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_BAD_BATTERY_MODE,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_CLOCK_GATE_CLOSED,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_NOT_INITIALIZED,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_ALREADY_INITIALIZED,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_BROKEN,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_NOT_BROKEN,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_NOT_DISABLED,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_BAD_ARGUMENT,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL,
+ /* brief TBD */
+ RIPLIEY_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE,
+} ddi_bc_Status_t;
+
+
+/* BCM Event Codes */
+
+/* These are the codes that might be published to PMI Subscribers. */
+
+
+#define RIPLIEY_BC_EVENT_GROUP (11<<10)
+
+/* brief TBD */
+/* todo [PUBS] Add definition(s)... */
+typedef enum {
+ /* Use the error code group value to make events unique for the EOI */
+ /* brief TBD */
+ ddi_bc_MinEventCode = RIPLIEY_BC_EVENT_GROUP,
+ /* brief TBD */
+ ddi_bc_WaitingToChargeCode,
+ /* brief TBD */
+ ddi_bc_State_ConditioningCode,
+ /* brief TBD */
+ ddi_bc_State_Topping_OffCode,
+ /* brief TBD */
+ ddi_bc_State_BrokenCode,
+ /* brief TBD */
+ ddi_bc_SettingChargeCode,
+ /* brief TBD */
+ ddi_bc_RaisingDieTempAlarmCode,
+ /* brief TBD */
+ ddi_bc_DroppingDieTempAlarmCode,
+
+ /* brief TBD */
+ ddi_bc_MaxEventCode,
+ /* brief TBD */
+ ddi_bc_DcdcModeWaitingToChargeCode
+} ddi_bc_Event_t;
+
+
+/* End of file */
+
+#endif /* _MC34708_BC_H */
diff --git a/include/linux/pmic_external.h b/include/linux/pmic_external.h
index 0ea60e5abab2..cc66a33e1299 100644
--- a/include/linux/pmic_external.h
+++ b/include/linux/pmic_external.h
@@ -50,7 +50,8 @@ typedef enum {
PMIC_MC13783 = 1, /*!< MC13783 */
PMIC_SC55112 = 2, /*!< SC55112 */
PMIC_MC13892 = 3,
- PMIC_MC34704 = 4
+ PMIC_MC34704 = 4,
+ PMIC_MC34708 = 5
} pmic_id_t;
/*!
@@ -132,7 +133,10 @@ typedef struct {
#define PMIC_ARBITRATION "NULL"
-#if defined(CONFIG_MXC_PMIC_MC13892_MODULE) || defined(CONFIG_MXC_PMIC_MC13892)
+#if defined(CONFIG_MXC_PMIC_MC13892_MODULE) || \
+ defined(CONFIG_MXC_PMIC_MC13892) || \
+ defined(CONFIG_MXC_PMIC_MC34708_MODULE) || \
+ defined(CONFIG_MXC_PMIC_MC34708)
enum {
REG_INT_STATUS0 = 0,
REG_INT_MASK0,
@@ -292,6 +296,76 @@ void gpio_pmic_active(void);
void pmic_event_list_init(void);
void mc13892_power_off(void);
+enum {
+ REG_MC34708_INT_STATUS0 = 0,
+ REG_MC34708_INT_MASK0,
+ REG_MC34708_INT_SENSE0,
+ REG_MC34708_INT_STATUS1,
+ REG_MC34708_INT_MASK1,
+ REG_MC34708_INT_SENSE1,
+ REG_MC34708_PU_MODE_S,
+ REG_MC34708_IDENTIFICATION,
+ REG_MC34708_REGU_FAULT_S,
+ REG_MC34708_ACC0,
+ REG_MC34708_ACC1, /*10 */
+ REG_MC34708_UNUSED1,
+ REG_MC34708_UNUSED2,
+ REG_MC34708_POWER_CTL0,
+ REG_MC34708_POWER_CTL1,
+ REG_MC34708_POWER_CTL2,
+ REG_MC34708_MEM_A,
+ REG_MC34708_MEM_B,
+ REG_MC34708_MEM_C,
+ REG_MC34708_MEM_D,
+ REG_MC34708_RTC_TIME, /*20 */
+ REG_MC34708_RTC_ALARM,
+ REG_MC34708_RTC_DAY,
+ REG_MC34708_RTC_DAY_ALARM,
+ REG_MC34708_SW_1_A_B,
+ REG_MC34708_SW_2_3,
+ REG_MC34708_SW_4_A_B,
+ REG_MC34708_SW_5,
+ REG_MC34708_SW_1_2_OP,
+ REG_MC34708_SW_3_4_5_OP,
+ REG_MC34708_SETTING_0, /*30 */
+ REG_MC34708_SWBST,
+ REG_MC34708_MODE_0,
+ REG_MC34708_GPIOLV0,
+ REG_MC34708_GPIOLV1,
+ REG_MC34708_GPIOLV2,
+ REG_MC34708_GPIOLV3,
+ REG_MC34708_USB_TIMING,
+ REG_MC34708_USB_BUTTON,
+ REG_MC34708_USB_CONTROL,
+ REG_MC34708_USB_DEVICE_TYPE, /*40 */
+ REG_MC34708_UNUSED3,
+ REG_MC34708_UNUSED4,
+ REG_MC34708_ADC0,
+ REG_MC34708_ADC1,
+ REG_MC34708_ADC2,
+ REG_MC34708_ADC3,
+ REG_MC34708_ADC4,
+ REG_MC34708_ADC5,
+ REG_MC34708_ADC6,
+ REG_MC34708_ADC7, /*50 */
+ REG_MC34708_BATTERY_PRO,
+ REG_MC34708_CHARGER_DEBOUNCE,
+ REG_MC34708_CHARGER_SOURCE,
+ REG_MC34708_CHARGER_LED_CON,
+ REG_MC34708_PWM_CON,
+ REG_MC34708_UNUSED5,
+ REG_MC34708_UNUSED6,
+ REG_MC34708_UNUSED7,
+ REG_MC34708_UNUSED8,
+ REG_MC34708_UNUSED9, /*60 */
+ REG_MC34708_UNUSED10,
+ REG_MC34708_UNUSED11,
+ REG_MC34708_UNUSED12,
+};
+
+extern struct i2c_client *mc34708_client;
+void mc34708_power_off(void);
+
#elif defined(CONFIG_MXC_PMIC_MC34704_MODULE) || defined(CONFIG_MXC_PMIC_MC34704)
typedef enum {