summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJingyu Zhou <b02241@freescale.com>2008-04-02 13:58:21 +0800
committerDaniel Schaeffer <daniel.schaeffer@timesys.com>2008-08-25 15:20:57 -0400
commitf6c413b2f5b1b503421f4543955aeff7a40bba92 (patch)
tree0f3e59b1c9a3ba6331733cb8971a2b67425650c9
parent12573d7103e37acb79ffe972560bbb5018259089 (diff)
ENGR00058904 Add pmic source code
Add pmic source code Signed-off-by: Zhou Jingyu <Jingyu.Zhou@freescale.com>
-rw-r--r--arch/arm/configs/imx35_3stack_defconfig13
-rw-r--r--drivers/mxc/pmic/Kconfig18
-rw-r--r--drivers/mxc/pmic/Makefile2
-rw-r--r--drivers/mxc/pmic/core/Makefile10
-rw-r--r--drivers/mxc/pmic/core/max8660.c149
-rw-r--r--drivers/mxc/pmic/core/max8660.h45
-rw-r--r--drivers/mxc/pmic/core/mc9sdz60.c111
-rw-r--r--drivers/mxc/pmic/core/mc9sdz60.h69
-rw-r--r--drivers/mxc/pmic/core/mcu_pmic_core.c540
-rw-r--r--drivers/mxc/pmic/mc9sdz60/Kconfig29
-rw-r--r--drivers/mxc/pmic/mc9sdz60/Makefile12
-rw-r--r--drivers/mxc/pmic/mc9sdz60/mcu_pmic_event.c220
-rw-r--r--drivers/mxc/pmic/mc9sdz60/mcu_pmic_gpio.c111
-rw-r--r--drivers/mxc/pmic/mc9sdz60/mcu_pmic_power.c465
-rw-r--r--drivers/mxc/pmic/mc9sdz60/mcu_pmic_rtc.c599
-rw-r--r--drivers/regulator/Kconfig5
-rw-r--r--drivers/regulator/Makefile3
-rw-r--r--drivers/regulator/max8660/Makefile1
-rw-r--r--drivers/regulator/max8660/reg-max8660.c898
-rw-r--r--drivers/regulator/reg-core.c16
-rw-r--r--include/asm-arm/arch-mxc/pmic_external.h100
21 files changed, 3413 insertions, 3 deletions
diff --git a/arch/arm/configs/imx35_3stack_defconfig b/arch/arm/configs/imx35_3stack_defconfig
index d4ccd40a69f3..a20afa8a6a01 100644
--- a/arch/arm/configs/imx35_3stack_defconfig
+++ b/arch/arm/configs/imx35_3stack_defconfig
@@ -483,6 +483,7 @@ CONFIG_REGULATOR_API=y
CONFIG_REGULATOR=y
# CONFIG_REGULATOR_DEBUG is not set
# CONFIG_REGULATOR_WM8350 is not set
+CONFIG_REGULATOR_MAX8660=y
# CONFIG_PARPORT is not set
# CONFIG_PNP is not set
CONFIG_BLK_DEV=y
@@ -902,7 +903,17 @@ CONFIG_RTC_MXC=y
#
# MXC PMIC support
#
-# CONFIG_MXC_PMIC is not set
+CONFIG_MXC_I2C_MCU_PMIC_CORE=y
+CONFIG_MXC_PMIC=y
+CONFIG_MXC_PMIC_CHARDEV=y
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_PMIC_MC9SDZ60=y
+# CONFIG_MXC_MC9SDZ60_ADC is not set
+CONFIG_MXC_MC9SDZ60_RTC=y
+CONFIG_MXC_MC9SDZ60_POWER=y
#
# Advanced Power Management devices
diff --git a/drivers/mxc/pmic/Kconfig b/drivers/mxc/pmic/Kconfig
index 4a99b56202fa..f3194343e5b3 100644
--- a/drivers/mxc/pmic/Kconfig
+++ b/drivers/mxc/pmic/Kconfig
@@ -13,9 +13,16 @@ config MXC_SPI_PMIC_CORE
SPI should be providing the interface between the PMIC and the MCU.
You must select the SPI driver support to enable this option.
+config MXC_I2C_MCU_PMIC_CORE
+ tristate "MCU PMIC Protocol support (I2C interface)"
+ depends on ARCH_MXC
+ default n
+ ---help---
+ This is the PMIC core/protocol driver for the MX35 3DS MCU PMIC
+
config MXC_PMIC
boolean
- default MXC_SPI_PMIC_CORE
+ default (MXC_SPI_PMIC_CORE || MXC_I2C_MCU_PMIC_CORE)
config MXC_PMIC_CHARDEV
tristate "MXC PMIC device interface"
@@ -58,5 +65,14 @@ config MXC_PMIC_FIXARB
a hardware modification to the RF Deck that connects the Sphinx
card (with the sc55112 PMIC) to the MXC91131 EVB.
+config MXC_PMIC_MC9SDZ60
+ tristate "MC9sDZ60 Client Drivers"
+ depends on MXC_I2C_MCU_PMIC_CORE
+ depends on !ARCH_MXC91131
+ default n
+ ---help---
+ This is the MXC MC9sDZ60(MCU) PMIC client drivers support.
+
+source "drivers/mxc/pmic/mc9sdz60/Kconfig"
endmenu
diff --git a/drivers/mxc/pmic/Makefile b/drivers/mxc/pmic/Makefile
index b4312cca7080..89fb6729a663 100644
--- a/drivers/mxc/pmic/Makefile
+++ b/drivers/mxc/pmic/Makefile
@@ -4,3 +4,5 @@
obj-y += core/
obj-$(CONFIG_MXC_PMIC_MC13783) += mc13783/
+obj-$(CONFIG_MXC_PMIC_MC9SDZ60) += mc9sdz60/
+
diff --git a/drivers/mxc/pmic/core/Makefile b/drivers/mxc/pmic/core/Makefile
index 2fe13f764c61..2945dd3d411a 100644
--- a/drivers/mxc/pmic/core/Makefile
+++ b/drivers/mxc/pmic/core/Makefile
@@ -2,6 +2,11 @@
# Makefile for the PMIC core drivers.
#
obj-$(CONFIG_MXC_SPI_PMIC_CORE) += pmic_core_spi_mod.o
+
+obj-$(CONFIG_MXC_I2C_MCU_PMIC_CORE) += pmic_core_i2c_mod.o
+
+pmic_core_i2c_mod-objs := pmic_external.o pmic_event.o mcu_pmic_core.o
+
obj-$(CONFIG_MXC_PMIC_CHARDEV) += pmic-dev.o
pmic_core_spi_mod-objs := pmic_external.o pmic_event.o pmic_core_spi.o
@@ -13,3 +18,8 @@ endif
ifeq ($(CONFIG_MXC_PMIC_SC55112),y)
pmic_core_spi_mod-objs += sc55112.o
endif
+
+ifeq ($(CONFIG_MXC_PMIC_MC9SDZ60), y)
+pmic_core_i2c_mod-objs += mc9sdz60.o
+pmic_core_i2c_mod-objs += max8660.o
+endif
diff --git a/drivers/mxc/pmic/core/max8660.c b/drivers/mxc/pmic/core/max8660.c
new file mode 100644
index 000000000000..5214739d03b3
--- /dev/null
+++ b/drivers/mxc/pmic/core/max8660.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file max8660.c
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <asm/arch/clock.h>
+#include <asm/uaccess.h>
+#include <asm/arch/pmic_external.h>
+#include "max8660.h"
+
+/* I2C bus id and device address of mcu */
+#define I2C1_BUS 0
+#define MAX8660_I2C_ADDR 0x68
+
+#define DEBUG_MAX8660 1
+#if DEBUG_MAX8660
+#define DPRINTK(format, args...) printk(KERN_ERR "max8660"format"\n", ##args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+static struct i2c_client *max8660_i2c_client;
+
+ /* reg names for max8660
+ REG_MAX8660_OUTPUT_ENABLE_1,
+ REG_MAX8660_OUTPUT_ENABLE_2,
+ REG_MAX8660_VOLT__CHANGE_1,
+ REG_MAX8660_V3_TARGET_VOLT_1,
+ REG_MAX8660_V3_TARGET_VOLT_2,
+ REG_MAX8660_V4_TARGET_VOLT_1,
+ REG_MAX8660_V4_TARGET_VOLT_2,
+ REG_MAX8660_V5_TARGET_VOLT_1,
+ REG_MAX8660_V5_TARGET_VOLT_2,
+ REG_MAX8660_V6V7_TARGET_VOLT,
+ REG_MAX8660_FORCE_PWM
+ */
+
+ /* save down the reg values for the device is write only */
+static u8 max8660_reg_value_table[] =
+ { 0x0, 0x0, 0x0, 0x17, 0x17, 0x1F, 0x1F, 0x04, 0x04, 0x0, 0x0
+};
+int max8660_get_buffered_reg_val(int reg_name, u8 *value)
+{
+
+ /* outof range */
+ if (reg_name < REG_MAX8660_OUTPUT_ENABLE_1
+ || reg_name > REG_MAX8660_FORCE_PWM) {
+ DPRINTK("reg_name=%d outof range", reg_name);
+ return -1;
+ }
+ *value =
+ max8660_reg_value_table[reg_name - REG_MAX8660_OUTPUT_ENABLE_1];
+ return 0;
+}
+int max8660_save_buffered_reg_val(int reg_name, u8 value)
+{
+
+ /* outof range */
+ if (reg_name < REG_MAX8660_OUTPUT_ENABLE_1
+ || reg_name > REG_MAX8660_FORCE_PWM) {
+ DPRINTK("reg_name=%d outof range", reg_name);
+ return -1;
+ }
+ max8660_reg_value_table[reg_name - REG_MAX8660_OUTPUT_ENABLE_1] = value;
+ return 0;
+}
+
+int max8660_write_reg(u8 reg, u8 value)
+{
+ DPRINTK("max8660_i2c_client = %p", max8660_i2c_client);
+ if (i2c_smbus_write_byte_data(max8660_i2c_client, reg, value) < 0) {
+ printk(KERN_ERR "%s:write reg errorr:reg=%x,val=%x\n",
+ __func__, reg, value);
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * max8660 I2C attach function
+ *
+ * @param adapter struct i2c_client *
+ * @return Always 0 because max8660 is write-only and can not be detected
+ */
+static int max8660_probe(struct i2c_client *client)
+{
+ max8660_i2c_client = client;
+ DPRINTK("max8660_i2c_client = %p", max8660_i2c_client);
+ return 0;
+}
+
+/*!
+ * max8660 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return 0
+ */
+static int max8660_remove(struct i2c_client *client)
+{
+ return 0;
+}
+static struct i2c_driver max8660_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "max8660",},
+ .probe = max8660_probe,
+ .remove = max8660_remove,
+};
+
+/* called by pmic core when init*/
+int max8660_init(void)
+{
+ int err;
+ DPRINTK("Freescale max8660 driver loaded\n");
+ err = i2c_add_driver(&max8660_i2c_driver);
+ if (err) {
+ printk(KERN_ERR
+ "max8660: driver registration failed err = %d\n", err);
+ return err;
+ }
+ DPRINTK("max8660 inited\n");
+ return 0;
+}
+void max8660_exit(void)
+{
+ i2c_del_driver(&max8660_i2c_driver);
+}
diff --git a/drivers/mxc/pmic/core/max8660.h b/drivers/mxc/pmic/core/max8660.h
new file mode 100644
index 000000000000..54638a4ed2fa
--- /dev/null
+++ b/drivers/mxc/pmic/core/max8660.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file max8660.h
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+#ifndef _MAX8660_H_
+#define _MAX8660_H_
+
+#ifdef __KERNEL__
+
+#define MAX8660_OUTPUT_ENABLE_1 0x10
+#define MAX8660_OUTPUT_ENABLE_2 0x12
+#define MAX8660_VOLT_CHANGE_CONTROL 0x20
+#define MAX8660_V3_TARGET_VOLT_1 0x23
+#define MAX8660_V3_TARGET_VOLT_2 0x24
+#define MAX8660_V4_TARGET_VOLT_1 0x29
+#define MAX8660_V4_TARGET_VOLT_2 0x2A
+#define MAX8660_V5_TARGET_VOLT_1 0x32
+#define MAX8660_V5_TARGET_VOLT_2 0x33
+#define MAX8660_V6V7_TARGET_VOLT 0x39
+#define MAX8660_FORCE_PWM 0x80
+int max8660_write_reg(u8 reg, u8 value);
+int max8660_save_buffered_reg_val(int reg_name, u8 value);
+int max8660_get_buffered_reg_val(int reg_name, u8 *value);
+int max8660_init(void);
+
+extern int reg_max8660_probe(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _MAX8660_H_ */
diff --git a/drivers/mxc/pmic/core/mc9sdz60.c b/drivers/mxc/pmic/core/mc9sdz60.c
new file mode 100644
index 000000000000..e367cb5aee15
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc9sdz60.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+ /*!
+ * @file mc9sdz60.c
+ * @brief Driver for MC9sdz60
+ *
+ * @ingroup pmic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+
+#include <asm/arch/clock.h>
+#include <asm/uaccess.h>
+#include "mc9sdz60.h"
+
+/* I2C bus id and device address of mcu */
+#define I2C1_BUS 0
+#define MC9SDZ60_I2C_ADDR 0xD2 /* 7bits I2C address */
+static struct i2c_client *mc9sdz60_i2c_client;
+
+#define DEBUG_MC9SDZ60 1
+#if DEBUG_MC9SDZ60
+#define DPRINTK(format, args...) printk(KERN_ERR "mc9sdz60: "format"\n", ##args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+int mc9sdz60_read_reg(u8 reg, u8 *value)
+{
+ *value = (u8) i2c_smbus_read_byte_data(mc9sdz60_i2c_client, reg);
+ return 0;
+}
+
+int mc9sdz60_write_reg(u8 reg, u8 value)
+{
+ if (i2c_smbus_write_byte_data(mc9sdz60_i2c_client, reg, value) < 0) {
+ printk(KERN_ERR "%s:write reg errorr:reg=%x,val=%x\n",
+ __func__, reg, value);
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * mc9sdz60 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return 0
+ */
+static int mc9sdz60_probe(struct i2c_client *client)
+{
+ mc9sdz60_i2c_client = client;
+ DPRINTK("mc9sdz60_i2c_client = %p", mc9sdz60_i2c_client);
+ return 0;
+}
+
+/*!
+ * mc9sdz60 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return 0
+ */
+static int mc9sdz60_remove(struct i2c_client *client)
+{
+ return 0;
+}
+static struct i2c_driver mc9sdz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9sdz60",
+ },
+ .probe = mc9sdz60_probe,
+ .remove = mc9sdz60_remove,
+};
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+int mc9sdz60_init(void)
+{
+ int err;
+ DPRINTK("Freescale mc9sdz60 driver,\
+ (c) 2008 Freescale Semiconductor, Inc.\n");
+ err = i2c_add_driver(&mc9sdz60_i2c_driver);
+ if (err) {
+ printk(KERN_ERR "mc9sdz60: driver registration failed\n");
+ return err;
+ }
+ DPRINTK("mc9sdz60 inited\n");
+ return 0;
+}
+void mc9sdz60_exit(void)
+{
+ i2c_del_driver(&mc9sdz60_i2c_driver);
+}
diff --git a/drivers/mxc/pmic/core/mc9sdz60.h b/drivers/mxc/pmic/core/mc9sdz60.h
new file mode 100644
index 000000000000..c1af168d88a9
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc9sdz60.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc9sdz60.h
+ * @brief Driver for mc9sdz60
+ *
+ * @ingroup pmic
+ */
+#ifndef _MC9SDZ60_H_
+#define _MC9SDZ60_H_
+
+#define MCU_VERSION 0x00
+/*#define Reserved 0x01*/
+#define MCU_SECS 0x02
+#define MCU_MINS 0x03
+#define MCU_HRS 0x04
+#define MCU_DAY 0x05
+#define MCU_DATE 0x06
+#define MCU_MONTH 0x07
+#define MCU_YEAR 0x08
+
+#define MCU_ALARM_SECS 0x09
+#define MCU_ALARM_MINS 0x0A
+#define MCU_ALARM_HRS 0x0B
+/* #define Reserved 0x0C*/
+/* #define Reserved 0x0D*/
+#define MCU_TS_CONTROL 0x0E
+#define MCU_X_LOW 0x0F
+#define MCU_Y_LOW 0x10
+#define MCU_XY_HIGH 0x11
+#define MCU_X_LEFT_LOW 0x12
+#define MCU_X_LEFT_HIGH 0x13
+#define MCU_X_RIGHT 0x14
+#define MCU_Y_TOP_LOW 0x15
+#define MCU_Y_TOP_HIGH 0x16
+#define MCU_Y_BOTTOM 0x17
+/* #define Reserved 0x18*/
+/* #define Reserved 0x19*/
+#define MCU_RESET_1 0x1A
+#define MCU_RESET_2 0x1B
+#define MCU_POWER_CTL 0x1C
+#define MCU_DELAY_CONFIG 0x1D
+/* #define Reserved 0x1E */
+/* #define Reserved 0x1F */
+#define MCU_GPIO_1 0x20
+#define MCU_GPIO_2 0x21
+#define MCU_KPD_1 0x22
+#define MCU_KPD_2 0x23
+#define MCU_KPD_CONTROL 0x24
+#define MCU_INT_ENABLE_1 0x25
+#define MCU_INT_ENABLE_2 0x26
+#define MCU_INT_FLAG_1 0x27
+#define MCU_INT_FLAG_2 0x28
+int mc9sdz60_read_reg(u8 reg, u8 *value);
+int mc9sdz60_write_reg(u8 reg, u8 value);
+int mc9sdz60_init(void);
+
+#endif /* _MC9SDZ60_H_ */
+
diff --git a/drivers/mxc/pmic/core/mcu_pmic_core.c b/drivers/mxc/pmic/core/mcu_pmic_core.c
new file mode 100644
index 000000000000..807e45eecd64
--- /dev/null
+++ b/drivers/mxc/pmic/core/mcu_pmic_core.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc9sdz60/mcu_pmic_core.c
+ * @brief This is the main file of mc9sdz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/ioctl.h>
+#include <asm/uaccess.h>
+#include <asm/arch/gpio.h>
+
+#include <../arch/arm/mach-mx3/iomux.h>
+
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_external.h>
+#include "pmic.h"
+#include "mc9sdz60.h"
+#include "max8660.h"
+
+/* bitfield macros for mcu pmic*/
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+/*
+ * Global variables
+ */
+static pmic_version_t mxc_pmic_version;
+unsigned int active_events[11];
+
+/* default event-enable-value after reset*/
+static u8 events_enabled1 = 0x40;
+static u8 events_enabled2;
+
+/*
+ * Platform device structure for PMIC client drivers
+ */
+static struct platform_device power_ldm = {
+ .name = "pmic_power",
+ .id = 1,
+};
+static struct platform_device rtc_ldm = {
+ .name = "pmic_rtc",
+ .id = 1,
+};
+
+/* map reg names (enum pmic_reg in pmic_external.h) to real addr*/
+const static u8 mcu_pmic_reg_addr_table[] = {
+ MCU_VERSION,
+ MCU_SECS,
+ MCU_MINS,
+ MCU_HRS,
+ MCU_DAY,
+ MCU_DATE,
+ MCU_MONTH,
+ MCU_YEAR,
+ MCU_ALARM_SECS,
+ MCU_ALARM_MINS,
+ MCU_ALARM_HRS,
+ MCU_TS_CONTROL,
+ MCU_X_LOW,
+ MCU_Y_LOW,
+ MCU_XY_HIGH,
+ MCU_X_LEFT_LOW,
+ MCU_X_LEFT_HIGH,
+ MCU_X_RIGHT,
+ MCU_Y_TOP_LOW,
+ MCU_Y_TOP_HIGH,
+ MCU_Y_BOTTOM,
+ MCU_RESET_1,
+ MCU_RESET_2,
+ MCU_POWER_CTL,
+ MCU_DELAY_CONFIG,
+ MCU_GPIO_1,
+ MCU_GPIO_2,
+ MCU_KPD_1,
+ MCU_KPD_2,
+ MCU_KPD_CONTROL,
+ MCU_INT_ENABLE_1,
+ MCU_INT_ENABLE_2,
+ MCU_INT_FLAG_1,
+ MCU_INT_FLAG_2,
+ MAX8660_OUTPUT_ENABLE_1,
+ MAX8660_OUTPUT_ENABLE_2,
+ MAX8660_VOLT_CHANGE_CONTROL,
+ MAX8660_V3_TARGET_VOLT_1,
+ MAX8660_V3_TARGET_VOLT_2,
+ MAX8660_V4_TARGET_VOLT_1,
+ MAX8660_V4_TARGET_VOLT_2,
+ MAX8660_V5_TARGET_VOLT_1,
+ MAX8660_V5_TARGET_VOLT_2,
+ MAX8660_V6V7_TARGET_VOLT,
+ MAX8660_FORCE_PWM
+};
+
+#define DPRINTK(format, args...) printk(KERN_ERR "pmic-core "format"\n", ##args)
+
+int pmic_read(int reg_num, unsigned int *reg_val)
+{
+ int ret;
+ u8 value = 0;
+ /* mcu ops */
+ if (reg_num >= REG_MCU_VERSION && reg_num <= REG_MCU_INT_FLAG_2) {
+
+ ret =
+ mc9sdz60_read_reg(mcu_pmic_reg_addr_table[reg_num], &value);
+ if (ret < 0)
+ goto error1;
+ *reg_val = value;
+ } else if (reg_num >= REG_MAX8660_OUTPUT_ENABLE_1
+ && reg_num <= REG_MAX8660_FORCE_PWM) {
+ ret = max8660_get_buffered_reg_val(reg_num, &value);
+ if (ret < 0)
+ goto error1;
+ *reg_val = value;
+ } else {
+ DPRINTK("reg_num=%d out of range", reg_num);
+ goto error1;
+ }
+
+ return 0;
+
+error1:
+ return -1;
+}
+
+int pmic_write(int reg_num, const unsigned int reg_val)
+{
+ int ret;
+ u8 value = reg_val;
+ /* mcu ops */
+ if (reg_num >= REG_MCU_VERSION && reg_num <= REG_MCU_INT_FLAG_2) {
+
+ ret =
+ mc9sdz60_write_reg(mcu_pmic_reg_addr_table[reg_num], value);
+ if (ret < 0)
+ goto error1;
+ } else if (reg_num >= REG_MAX8660_OUTPUT_ENABLE_1
+ && reg_num <= REG_MAX8660_FORCE_PWM) {
+ ret =
+ max8660_write_reg(mcu_pmic_reg_addr_table[reg_num], value);
+
+ if (ret < 0)
+ goto error1;
+
+ ret = max8660_save_buffered_reg_val(reg_num, value);
+ } else {
+ DPRINTK("reg_num=%d out of range", reg_num);
+ goto error1;
+ }
+
+ return 0;
+
+error1:
+ DPRINTK(" reg_num = %d, write failed", reg_num);
+ return -1;
+
+}
+
+/* for debug*/
+void dump_pmic_reg(void)
+{
+#define REG_DUMP(reg) do {\
+ pmic_read_reg(reg, &reg_val, 0xff);\
+ DPRINTK("%s = %x", #reg, reg_val); } while (0)
+ unsigned int reg_val;
+
+ REG_DUMP(REG_MCU_VERSION);
+ REG_DUMP(REG_MCU_SECS);
+ REG_DUMP(REG_MCU_MINS);
+ REG_DUMP(REG_MCU_HRS);
+ REG_DUMP(REG_MCU_DAY);
+ REG_DUMP(REG_MCU_DATE);
+ REG_DUMP(REG_MCU_MONTH);
+ REG_DUMP(REG_MCU_YEAR);
+ REG_DUMP(REG_MCU_ALARM_SECS);
+ REG_DUMP(REG_MCU_ALARM_MINS);
+ REG_DUMP(REG_MCU_ALARM_HRS);
+ REG_DUMP(REG_MCU_TS_CONTROL);
+ REG_DUMP(REG_MCU_X_LOW);
+ REG_DUMP(REG_MCU_Y_LOW);
+ REG_DUMP(REG_MCU_XY_HIGH);
+ REG_DUMP(REG_MCU_X_LEFT_LOW);
+ REG_DUMP(REG_MCU_X_LEFT_HIGH);
+ REG_DUMP(REG_MCU_X_RIGHT);
+ REG_DUMP(REG_MCU_Y_TOP_LOW);
+ REG_DUMP(REG_MCU_Y_TOP_HIGH);
+ REG_DUMP(REG_MCU_Y_BOTTOM);
+ REG_DUMP(REG_MCU_RESET_1);
+ REG_DUMP(REG_MCU_RESET_2);
+ REG_DUMP(REG_MCU_POWER_CTL);
+ REG_DUMP(REG_MCU_DELAY_CONFIG);
+ REG_DUMP(REG_MCU_GPIO_1);
+ REG_DUMP(REG_MCU_GPIO_2);
+ REG_DUMP(REG_MCU_KPD_1);
+ REG_DUMP(REG_MCU_KPD_2);
+ REG_DUMP(REG_MCU_KPD_CONTROL);
+ REG_DUMP(REG_MCU_INT_ENABLE_1);
+ REG_DUMP(REG_MCU_INT_ENABLE_2);
+ REG_DUMP(REG_MCU_INT_FLAG_1);
+ REG_DUMP(REG_MCU_INT_FLAG_2);
+}
+
+/*!
+ * This function unsets a bit in mask register of pmic to unmask an event IT.
+ *
+ * @param event the event to be unmasked
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_unmask(type_event event)
+{
+ int reg_name;
+ u8 reg_mask = 0;
+ unsigned int event_bit = 0;
+ int ret = -1;
+
+ switch (event) {
+ case EVENT_HEADPHONE_DET:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 0;
+ break;
+ case EVENT_SD1_DET:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 2;
+ break;
+ case EVENT_SD1_WP:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 3;
+ break;
+ case EVENT_SD2_DET:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 4;
+ break;
+ case EVENT_SD2_WP:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 5;
+ break;
+ case EVENT_GPS_INT:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 1;
+ break;
+ case EVENT_POWER_KEY:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 6;
+ break;
+ case EVENT_KEYPAD:
+ reg_name = REG_MCU_INT_ENABLE_2;
+ event_bit = 2;
+ break;
+ case EVENT_RTC:
+ reg_name = REG_MCU_INT_ENABLE_2;
+ event_bit = 0;
+ break;
+ case EVENT_TS_ADC:
+ reg_name = REG_MCU_INT_ENABLE_2;
+ event_bit = 1;
+ break;
+ default:
+ return PMIC_ERROR;
+ }
+ SET_BIT_IN_BYTE(reg_mask, event_bit);
+ ret = pmic_write_reg(reg_name, reg_mask, reg_mask);
+ if (PMIC_SUCCESS == ret) {
+
+ if (REG_MCU_INT_ENABLE_2 == reg_name)
+ events_enabled2 |= reg_mask;
+ else if (REG_MCU_INT_ENABLE_1 == reg_name)
+ events_enabled1 |= reg_mask;
+ pr_debug("Enable Event : %d\n", event);
+ }
+
+ return ret;
+}
+
+/*!
+ * This function sets a bit in mask register of pmic to disable an event IT.
+ *
+ * @param event the event to be masked
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_mask(type_event event)
+{
+ int reg_name;
+ u8 reg_mask = 0;
+ unsigned int event_bit = 0;
+ int ret = -1;
+
+ switch (event) {
+ case EVENT_HEADPHONE_DET:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 0;
+ break;
+ case EVENT_SD1_DET:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 2;
+ break;
+ case EVENT_SD1_WP:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 3;
+ break;
+ case EVENT_SD2_DET:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 4;
+ break;
+ case EVENT_SD2_WP:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 5;
+ break;
+ case EVENT_GPS_INT:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 1;
+ break;
+ case EVENT_POWER_KEY:
+ reg_name = REG_MCU_INT_ENABLE_1;
+ event_bit = 6;
+ break;
+ case EVENT_KEYPAD:
+ reg_name = REG_MCU_INT_ENABLE_2;
+ event_bit = 2;
+ break;
+ case EVENT_RTC:
+ reg_name = REG_MCU_INT_ENABLE_2;
+ event_bit = 0;
+ break;
+ case EVENT_TS_ADC:
+ reg_name = REG_MCU_INT_ENABLE_2;
+ event_bit = 1;
+ break;
+ default:
+ return PMIC_ERROR;
+ }
+ SET_BIT_IN_BYTE(reg_mask, event_bit);
+
+ ret = pmic_write_reg(reg_name, 0, reg_mask);
+ if (PMIC_SUCCESS == ret) {
+
+ if (REG_MCU_INT_ENABLE_2 == reg_name)
+ events_enabled2 &= ~reg_mask;
+ else if (REG_MCU_INT_ENABLE_1 == reg_name)
+ events_enabled1 &= ~reg_mask;
+ pr_debug("Disable Event : %d\n", event);
+ }
+
+ return ret;
+}
+
+/*!
+ * This function reads the interrupt status registers of PMIC
+ * and determine the current active events.
+ *
+ * @param active_events array pointer to be used to return active
+ * event numbers.
+ *
+ * @return This function returns PMIC version.
+ */
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+ unsigned int count = 0;
+ unsigned int flag1, flag2;
+ int bit_set;
+
+ /* read int flags and ack int */
+ pmic_read(REG_MCU_INT_FLAG_1, &flag1);
+ pmic_read(REG_MCU_INT_FLAG_2, &flag2);
+ pmic_write(REG_MCU_INT_FLAG_1, 0);
+ pmic_write(REG_MCU_INT_FLAG_2, 0);
+ DPRINTK("pmic interrupt flag1=%x, flag2=%x", flag1, flag2);
+
+ flag1 &= events_enabled1;
+ flag2 &= events_enabled2;
+
+ while (flag1) {
+ bit_set = ffs(flag1) - 1;
+ *(active_events + count) = bit_set;
+ count++;
+ flag1 ^= (1 << bit_set);
+ }
+ while (flag2) {
+ bit_set = ffs(flag2) - 1;
+ *(active_events + count) = bit_set + 7;
+ count++;
+ flag2 ^= (1 << bit_set);
+ }
+
+ return count;
+}
+
+void pmic_bh_handler(struct work_struct *work);
+
+/*
+ * External functions
+ */
+extern void pmic_event_list_init(void);
+extern void pmic_event_callback(type_event event);
+extern void gpio_pmic_active(void);
+
+/*!
+ * Bottom half handler of PMIC event handling.
+ */
+DECLARE_WORK(pmic_ws, pmic_bh_handler);
+
+/*!
+ * This function registers platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_register(void)
+{
+ platform_device_register(&rtc_ldm);
+ platform_device_register(&power_ldm);
+ reg_max8660_probe();
+}
+
+/*!
+ * This function unregisters platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_unregister(void)
+{
+ platform_device_unregister(&rtc_ldm);
+}
+
+/*!
+ * This function is called when pmic interrupt occurs on the processor.
+ * It is the interrupt handler for the pmic module.
+ *
+ * @param irq the irq number
+ * @param dev_id the pointer on the device
+ *
+ * @return The function returns IRQ_HANDLED when handled.
+ */
+static irqreturn_t pmic_irq_handler(int irq, void *dev_id)
+{
+ /* prepare a task */
+ schedule_work(&pmic_ws);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is the bottom half handler of the PMIC interrupt.
+ * It checks for active events and launches callback for the
+ * active events.
+ */
+void pmic_bh_handler(struct work_struct *work)
+{
+ unsigned int loop;
+ unsigned int count = 0;
+
+ count = pmic_get_active_events(active_events);
+
+ for (loop = 0; loop < count; loop++)
+ pmic_event_callback(active_events[loop]);
+}
+
+/*!
+ * This function is used to determine the PMIC type and its revision.
+ *
+ * @return Returns the PMIC type and its revision.
+ */
+
+pmic_version_t pmic_get_version(void)
+{
+ return mxc_pmic_version;
+}
+EXPORT_SYMBOL(pmic_get_version);
+
+static int __init mcu_pmic_init(void)
+{
+ int err;
+
+ /* init chips */
+ err = max8660_init();
+ if (err)
+ goto fail1;
+
+ err = mc9sdz60_init();
+ if (err)
+ goto fail1;
+
+ /* Initialize the PMIC event handling */
+ pmic_event_list_init();
+
+ /* Set and install PMIC IRQ handler */
+ mxc_request_iomux(MX35_PIN_GPIO1_0, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_iomux_set_pad(MX35_PIN_GPIO1_0, PAD_CTL_PKE_NONE);
+ mxc_set_gpio_direction(MX35_PIN_GPIO1_0, 1); /* input */
+
+ err = set_irq_type(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0), IRQT_RISING);
+ err = request_irq(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0), pmic_irq_handler,
+ 0, "PMIC_IRQ", 0);
+ if (err) {
+ DPRINTK("mcu pmic request irq failed");
+ goto fail1;
+ }
+
+ pmic_pdev_register();
+
+ return 0;
+
+fail1:
+ return err;
+}
+
+static void __exit mcu_pmic_exit(void)
+{
+ free_irq(IOMUX_TO_IRQ(MX35_PIN_GPIO1_1), 0);
+ pmic_pdev_unregister();
+}
+
+subsys_initcall_sync(mcu_pmic_init);
+module_exit(mcu_pmic_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("mcu pmic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc9sdz60/Kconfig b/drivers/mxc/pmic/mc9sdz60/Kconfig
new file mode 100644
index 000000000000..b0e87653853e
--- /dev/null
+++ b/drivers/mxc/pmic/mc9sdz60/Kconfig
@@ -0,0 +1,29 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_MC9SDZ60_ADC
+ tristate "MC9SDZ60 ADC support"
+ depends on MXC_PMIC_MC9SDZ60
+ default n
+ ---help---
+ This is the MC9SDZ60 ADC module driver. This module provides kernel API
+ for the ADC system of MC9SDZ60.
+ It controls also the touch screen interface.
+ If you want MC9SDZ60 ADC support, you should say Y here
+
+config MXC_MC9SDZ60_RTC
+ tristate "MC9SDZ60 Real Time Clock (RTC) support"
+ depends on MXC_PMIC_MC9SDZ60
+ ---help---
+ This is the MC9SDZ60 RTC module driver. This module provides kernel API
+ for RTC part of MC9SDZ60.
+ If you want MC9SDZ60 RTC support, you should say Y here
+
+config MXC_MC9SDZ60_POWER
+ tristate "MC9SDZ60 Power API support"
+ depends on MXC_PMIC_MC9SDZ60
+ ---help---
+ This is the MC9SDZ60 power and supplies module driver. This module provides kernel API
+ for power and regulator control part of MC9SDZ60.
+ If you want MC9SDZ60 power support, you should say Y here
diff --git a/drivers/mxc/pmic/mc9sdz60/Makefile b/drivers/mxc/pmic/mc9sdz60/Makefile
new file mode 100644
index 000000000000..ab2522632a36
--- /dev/null
+++ b/drivers/mxc/pmic/mc9sdz60/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the mc9sdz60 pmic drivers.
+#
+
+obj-y += mcu_pmic_gpio.o
+obj-$(CONFIG_MXC_MC9SDZ60_ADC) += pmic_adc-mod.o
+obj-$(CONFIG_MXC_MC9SDZ60_RTC) += pmic_rtc-mod.o
+#obj-$(CONFIG_MXC_MC9SDZ60_POWER) += pmic_power-mod.o
+pmic_adc-mod-objs := mcu_pmic_adc.o
+pmic_rtc-mod-objs := mcu_pmic_rtc.o
+#pmic_power-mod-objs := mcu_pmic_power.o
+
diff --git a/drivers/mxc/pmic/mc9sdz60/mcu_pmic_event.c b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_event.c
new file mode 100644
index 000000000000..42be5656709b
--- /dev/null
+++ b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_event.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc9sdz60/mcu_pmic_event.c
+ * @brief This is the main file of mc9sdz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <asm/ioctl.h>
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_power.h>
+#include "../core/pmic_config.h"
+#include "mc9sdz60.h"
+#include "max8660.h"
+
+/*!
+ * This structure is used to keep a list of subscribed
+ * callbacks for an event.
+ */
+typedef struct {
+ /*!
+ * Keeps a list of subscribed clients to an event.
+ */
+ struct list_head list;
+
+ /*!
+ * Callback function with parameter, called when event occurs
+ */
+ pmic_event_callback_t callback;
+} pmic_event_callback_list_t;
+
+/* Create a mutex to be used to prevent concurrent access to the event list */
+static DECLARE_MUTEX(event_mutex);
+
+/* This is a pointer to the event handler array. It defines the currently
+ * active set of events and user-defined callback functions.
+ */
+static struct list_head pmic_events[PMIC_MAX_EVENTS];
+
+/*!
+ * This function initializes event list for PMIC event handling.
+ *
+ */
+void pmic_event_list_init(void)
+{
+ int i;
+
+ for (i = 0; i < PMIC_MAX_EVENTS; i++)
+ INIT_LIST_HEAD(&pmic_events[i]);
+
+ sema_init(&event_mutex, 1);
+}
+
+/*!
+ * This function is used to subscribe on an event.
+ *
+ * @param event the event number to be subscribed
+ * @param callback the callback funtion to be subscribed
+ *
+ * @return This function returns 0 on SUCCESS, error on FAILURE.
+ */
+PMIC_STATUS pmic_event_subscribe(type_event event,
+ pmic_event_callback_t callback)
+{
+ pmic_event_callback_list_t *new = NULL;
+
+ pr_debug("Event:%d Subscribe\n", event);
+
+ /* Check whether the event & callback are valid? */
+ if (event >= PMIC_MAX_EVENTS) {
+ pr_debug("Invalid Event:%d\n", event);
+ return -EINVAL;
+ }
+ if (NULL == callback.func) {
+ pr_debug("Null or Invalid Callback\n");
+ return -EINVAL;
+ }
+
+ /* Create a new linked list entry */
+ new = kmalloc(sizeof(pmic_event_callback_list_t), GFP_KERNEL);
+ if (NULL == new)
+ return -ENOMEM;
+
+ /* Initialize the list node fields */
+ new->callback.func = callback.func;
+ new->callback.param = callback.param;
+ INIT_LIST_HEAD(&new->list);
+
+ /* Obtain the lock to access the list */
+ if (down_interruptible(&event_mutex)) {
+ kfree(new);
+ return PMIC_SYSTEM_ERROR_EINTR;
+ }
+
+ /* Unmask the requested event */
+ if (list_empty(&pmic_events[event])) {
+ if (pmic_event_unmask(event) != PMIC_SUCCESS) {
+ kfree(new);
+ up(&event_mutex);
+ return PMIC_ERROR;
+ }
+ }
+
+ /* Add this entry to the event list */
+ list_add_tail(&new->list, &pmic_events[event]);
+
+ /* Release the lock */
+ up(&event_mutex);
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_event_subscribe);
+
+/*!
+ * This function is used to unsubscribe on an event.
+ *
+ * @param event the event number to be unsubscribed
+ * @param callback the callback funtion to be unsubscribed
+ *
+ * @return This function returns 0 on SUCCESS, error on FAILURE.
+ */
+PMIC_STATUS pmic_event_unsubscribe(type_event event,
+ pmic_event_callback_t callback)
+{
+ struct list_head *p;
+ struct list_head *n;
+ pmic_event_callback_list_t *temp = NULL;
+ int ret = PMIC_EVENT_NOT_SUBSCRIBED;
+
+ pr_debug("Event:%d Unsubscribe\n", event);
+
+ /* Check whether the event & callback are valid? */
+ if (event >= PMIC_MAX_EVENTS) {
+ pr_debug("Invalid Event:%d\n", event);
+ return -EINVAL;
+ }
+
+ if (NULL == callback.func) {
+ pr_debug("Null or Invalid Callback\n");
+ return -EINVAL;
+ }
+
+ /* Obtain the lock to access the list */
+ if (down_interruptible(&event_mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* Find the entry in the list */
+ list_for_each_safe(p, n, &pmic_events[event]) {
+ temp = list_entry(p, pmic_event_callback_list_t, list);
+ if (temp->callback.func == callback.func
+ && temp->callback.param == callback.param) {
+ /* Remove the entry from the list */
+ list_del(p);
+ kfree(temp);
+ ret = PMIC_SUCCESS;
+ break;
+ }
+ }
+
+ /* Unmask the requested event */
+ if (list_empty(&pmic_events[event])) {
+ if (pmic_event_mask(event) != PMIC_SUCCESS)
+ ret = PMIC_UNSUBSCRIBE_ERROR;
+ }
+
+ /* Release the lock */
+ up(&event_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(pmic_event_unsubscribe);
+
+/*!
+ * This function calls all callback of a specific event.
+ *
+ * @param event the active event number
+ *
+ * @return None
+ */
+void pmic_event_callback(type_event event)
+{
+ struct list_head *p;
+ pmic_event_callback_list_t *temp = NULL;
+
+ /* Obtain the lock to access the list */
+ if (down_interruptible(&event_mutex))
+ return;
+ if (list_empty(&pmic_events[event])) {
+ pr_debug("PMIC Event:%d detected. No callback subscribed\n",
+ event);
+ up(&event_mutex);
+ return;
+ }
+
+ list_for_each(p, &pmic_events[event]) {
+ temp = list_entry(p, pmic_event_callback_list_t, list);
+ temp->callback.func(temp->callback.param);
+ }
+
+ /* Release the lock */
+ up(&event_mutex);
+}
+
diff --git a/drivers/mxc/pmic/mc9sdz60/mcu_pmic_gpio.c b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_gpio.c
new file mode 100644
index 000000000000..cdf2711ca564
--- /dev/null
+++ b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_gpio.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc9sdz60/mcu_pmic_gpio.c
+ * @brief This is the main file of mc9sdz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <asm/ioctl.h>
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_power.h>
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+PMIC_STATUS pmic_gpio_set_bit_val(t_mcu_gpio_reg reg, unsigned int bit,
+ unsigned int val)
+{
+ int reg_name;
+ u8 reg_mask = 0;
+
+ if (bit > 7)
+ return PMIC_PARAMETER_ERROR;
+
+ switch (reg) {
+ case MCU_GPIO_REG_RESET_1:
+ reg_name = REG_MCU_RESET_1;
+ break;
+ case MCU_GPIO_REG_RESET_2:
+ reg_name = REG_MCU_RESET_2;
+ break;
+ case MCU_GPIO_REG_POWER_CONTROL:
+ reg_name = REG_MCU_POWER_CTL;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_1:
+ reg_name = REG_MCU_GPIO_1;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_2:
+ reg_name = REG_MCU_GPIO_2;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ SET_BIT_IN_BYTE(reg_mask, bit);
+ if (0 == val)
+ CHECK_ERROR(pmic_write_reg(reg_name, 0, reg_mask));
+ else
+ CHECK_ERROR(pmic_write_reg(reg_name, reg_mask, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_gpio_set_bit_val);
+
+PMIC_STATUS pmic_gpio_get_bit_val(t_mcu_gpio_reg reg, unsigned int bit,
+ unsigned int *val)
+{
+ int reg_name;
+ unsigned int reg_read_val;
+ u8 reg_mask = 0;
+
+ if (bit > 7)
+ return PMIC_PARAMETER_ERROR;
+
+ switch (reg) {
+ case MCU_GPIO_REG_RESET_1:
+ reg_name = REG_MCU_RESET_1;
+ break;
+ case MCU_GPIO_REG_RESET_2:
+ reg_name = REG_MCU_RESET_2;
+ break;
+ case MCU_GPIO_REG_POWER_CONTROL:
+ reg_name = REG_MCU_POWER_CTL;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_1:
+ reg_name = REG_MCU_GPIO_1;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_2:
+ reg_name = REG_MCU_GPIO_2;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ SET_BIT_IN_BYTE(reg_mask, bit);
+ CHECK_ERROR(pmic_read_reg(reg_name, &reg_read_val, reg_mask));
+ if (0 == reg_read_val)
+ *val = 0;
+ else
+ *val = 1;
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_gpio_get_bit_val);
diff --git a/drivers/mxc/pmic/mc9sdz60/mcu_pmic_power.c b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_power.c
new file mode 100644
index 000000000000..6072eb9bc1a0
--- /dev/null
+++ b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_power.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc9sdz60/mcu_pmic_power.c
+ * @brief This is the main file of mc9sdz60 Power Control driver.
+ * @This driver is deprecated, replaced by Wolfson regulator architecture
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <asm/ioctl.h>
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_power.h>
+#include "../core/pmic_config.h"
+
+/*
+ * PMIC Power Control API
+ */
+
+
+/*!
+ * This is the suspend of power management for Power Control
+ * API module.
+ *
+ * @param pdev the device structure used to give information on which power
+ * device (0 through 3 channels) to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_power_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+};
+
+/*!
+ * This is the resume of power management API.
+ * It suports RESTORE state.
+ *
+ * @param pdev the device structure used to give information on which power
+ * device (0 through 3 channels) to suspend
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_power_resume(struct platform_device *pdev)
+{
+ return 0;
+};
+
+/*!
+ * This function sets user power off in power control register and thus powers
+ * off the phone.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_off(void)
+{
+#define POWER_OFF_DELAY_MS 200
+ u8 mask = 0;
+ CHECK_ERROR(pmic_write_reg
+ (REG_MCU_DELAY_CONFIG, POWER_OFF_DELAY_MS, 0xff));
+ SET_BIT_IN_BYTE(mask, 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_POWER_CTL, mask, mask));
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_power_off);
+
+/*!
+ * This function turns on a regulator.
+ *
+ * @param regulator The regulator to be truned on.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_on(t_pmic_regulator regulator)
+{
+ u8 reg_mask = 0;
+ int reg_num;
+
+ if (regulator > MCU_LDO7 || regulator < MCU_SW1)
+ return PMIC_NOT_SUPPORTED;
+
+ switch (regulator) {
+ case MCU_SW1:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW2:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW3:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_SW4:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_LDO5:
+ SET_BIT_IN_BYTE(reg_mask, 3);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_LDO6:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+ case MCU_LDO7:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_num, reg_mask, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_power_regulator_on);
+
+/*!
+ * This function turns off a regulator.
+ *
+ * @param regulator The regulator to be truned off.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_off(t_pmic_regulator regulator)
+{
+ u8 reg_mask = 0;
+ unsigned int reg_read = 0;
+ int reg_num;
+
+ if (regulator > MCU_LDO7 || regulator < MCU_SW1)
+ return PMIC_NOT_SUPPORTED;
+
+ switch (regulator) {
+ case MCU_SW1:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW2:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW3:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_SW4:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_LDO5:
+ SET_BIT_IN_BYTE(reg_mask, 3);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_LDO6:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+ case MCU_LDO7:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_num, 0, reg_mask));
+
+ /* handle sw3,4 */
+ switch (regulator) {
+ case MCU_SW3:
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_read_reg
+ (REG_MCU_POWER_CTL, &reg_read, reg_mask));
+
+ /* check if hw pin enable sw34 */
+ if (0 != reg_read) {
+
+ /* keep sw4 on */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_OUTPUT_ENABLE_1, reg_mask,
+ reg_mask));
+
+ /* disable hw pin to actually turn off sw3 */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MCU_POWER_CTL, 0, reg_mask));
+ }
+ break;
+ case MCU_SW4:
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_read_reg
+ (REG_MCU_POWER_CTL, &reg_read, reg_mask));
+
+ /* check if hw pin enable sw34 */
+ if (0 != reg_read) {
+
+ /* keep sw3 on */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_OUTPUT_ENABLE_1, reg_mask,
+ reg_mask));
+
+ /* disable hw pin to actually turn off sw4 */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MCU_POWER_CTL, 0, reg_mask));
+ }
+ break;
+ default:
+ break;
+ }
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_power_regulator_off);
+
+/*!
+ * This function sets the regulator output voltage.
+ *
+ * @param regulator The regulator to be truned off.
+ * @param voltage The regulator output voltage.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_voltage(t_pmic_regulator regulator,
+ t_regulator_voltage voltage)
+{
+ u8 reg_mask = 0;
+
+ if (regulator > MCU_LDO7 || regulator < MCU_SW3)
+ return PMIC_NOT_SUPPORTED;
+
+ switch (regulator) {
+ case MCU_SW3:
+ if ((voltage.mcu_sw34 < MCU_SW34_0_725V)
+ || voltage.mcu_sw34 > MCU_SW34_1_8V)
+ return PMIC_PARAMETER_ERROR;
+
+ /* hold on */
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, 0, reg_mask));
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_V3_TARGET_VOLT_1, voltage.mcu_sw34,
+ 0xff));
+
+ /* start ramp */
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, reg_mask,
+ reg_mask));
+ break;
+ case MCU_SW4:
+ if ((voltage.mcu_sw34 < MCU_SW34_0_725V)
+ || voltage.mcu_sw34 > MCU_SW34_1_8V)
+ return PMIC_PARAMETER_ERROR;
+
+ /* hold on */
+ SET_BIT_IN_BYTE(reg_mask, 4);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, 0, reg_mask));
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_V4_TARGET_VOLT_1, voltage.mcu_sw34,
+ 0xff));
+
+ /* start ramp */
+ SET_BIT_IN_BYTE(reg_mask, 4);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, reg_mask,
+ reg_mask));
+ break;
+ case MCU_LDO5:
+ if (voltage.mcu_ldo5 < MCU_LDO5_1_725V
+ || voltage.mcu_ldo5 > MCU_LDO5_2_0V)
+ return PMIC_PARAMETER_ERROR;
+
+ /* hold on */
+ SET_BIT_IN_BYTE(reg_mask, 6);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, 0, reg_mask));
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_V5_TARGET_VOLT_1, voltage.mcu_ldo5,
+ 0xff));
+
+ /* start ramp */
+ SET_BIT_IN_BYTE(reg_mask, 6);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, reg_mask,
+ reg_mask));
+ break;
+
+ break;
+ case MCU_LDO6:
+ if (voltage.mcu_ldo67 < MCU_LDO67_1_8V
+ || voltage.mcu_ldo67 > MCU_LDO67_3_3V)
+ return PMIC_PARAMETER_ERROR;
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT, voltage.mcu_ldo67,
+ 0x0f));
+
+ break;
+ case MCU_LDO7:
+ if (voltage.mcu_ldo67 < MCU_LDO67_1_8V
+ || voltage.mcu_ldo67 > MCU_LDO67_3_3V)
+ return PMIC_PARAMETER_ERROR;
+ break;
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT,
+ (voltage.mcu_ldo67 << 4), 0xf0));
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_power_regulator_set_voltage);
+
+/*!
+ * This function retrives the regulator output voltage.
+ *
+ * @param regulator The regulator to be truned off.
+ * @param voltage Pointer to regulator output voltage.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_voltage(t_pmic_regulator regulator,
+ t_regulator_voltage *voltage)
+{
+ unsigned int reg_val = 0;
+
+ if (regulator > MCU_LDO7 || regulator < MCU_SW3)
+ return PMIC_NOT_SUPPORTED;
+
+ switch (regulator) {
+ case MCU_SW3:
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V3_TARGET_VOLT_1, &reg_val, 0xff));
+ voltage->mcu_sw34 = reg_val;
+ break;
+ case MCU_SW4:
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V4_TARGET_VOLT_1, &reg_val, 0xff));
+ voltage->mcu_sw34 = reg_val;
+ break;
+ case MCU_LDO5:
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V5_TARGET_VOLT_1, &reg_val, 0xff));
+ voltage->mcu_ldo5 = reg_val;
+ break;
+ case MCU_LDO6:
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT, &reg_val, 0x0f));
+ voltage->mcu_ldo67 = reg_val;
+ break;
+ case MCU_LDO7:
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT, &reg_val, 0xf0));
+ voltage->mcu_ldo67 = (reg_val >> 4) & 0xf;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_power_regulator_get_voltage);
+
+/*
+ * Initialization and Exit
+ */
+
+PMIC_STATUS pmic_power_regulator_init()
+{
+ t_regulator_voltage volt;
+ volt.mcu_sw34 = MCU_SW34_1_2V;
+ CHECK_ERROR(pmic_power_regulator_set_voltage(MCU_SW3, volt));
+ volt.mcu_sw34 = MCU_SW34_1_5V;
+ CHECK_ERROR(pmic_power_regulator_set_voltage(MCU_SW4, volt));
+ volt.mcu_ldo5 = MCU_LDO5_1_8V;
+ CHECK_ERROR(pmic_power_regulator_set_voltage(MCU_LDO5, volt));
+ volt.mcu_ldo67 = MCU_LDO67_2_5V;
+ CHECK_ERROR(pmic_power_regulator_set_voltage(MCU_LDO6, volt));
+ volt.mcu_ldo67 = MCU_LDO67_2_8V;
+ CHECK_ERROR(pmic_power_regulator_set_voltage(MCU_LDO7, volt));
+
+ return PMIC_SUCCESS;
+}
+
+static int pmic_power_probe(struct platform_device *pdev)
+{
+ printk(KERN_INFO "PMIC Power successfully probed\n");
+ pmic_power_regulator_init();
+ return 0;
+}
+
+static struct platform_driver pmic_power_driver_ldm = {
+ .driver = {
+ .name = "pmic_power",
+ },
+ .suspend = pmic_power_suspend,
+ .resume = pmic_power_resume,
+ .probe = pmic_power_probe,
+ .remove = NULL,
+};
+
+static int __init pmic_power_init(void)
+{
+ pr_debug("PMIC Power driver loading..\n");
+ return platform_driver_register(&pmic_power_driver_ldm);
+}
+static void __exit pmic_power_exit(void)
+{
+ platform_driver_unregister(&pmic_power_driver_ldm);
+ pr_debug("PMIC Power driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_power_init);
+module_exit(pmic_power_exit);
+
+MODULE_DESCRIPTION("PMIC Power Control device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc9sdz60/mcu_pmic_rtc.c b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_rtc.c
new file mode 100644
index 000000000000..a83d13d435a4
--- /dev/null
+++ b/drivers/mxc/pmic/mc9sdz60/mcu_pmic_rtc.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc9sdz60/mcu_pmic_rtc.c
+ * @brief This is the main file of mc9sdz60 RTC driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <asm/ioctl.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_rtc.h>
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_power.h>
+
+#define MCU_PMIC_RTC_NAME "pmic_rtc"
+/*
+ * Global variables
+ */
+static int pmic_rtc_major;
+static void callback_alarm_asynchronous(void *);
+static void callback_alarm_synchronous(void *);
+static unsigned int pmic_rtc_poll(struct file *file, poll_table *wait);
+static DECLARE_WAIT_QUEUE_HEAD(queue_alarm);
+static DECLARE_WAIT_QUEUE_HEAD(pmic_rtc_wait);
+static pmic_event_callback_t alarm_callback;
+static pmic_event_callback_t rtc_callback;
+static int pmic_rtc_detected;
+static bool pmic_rtc_done;
+static struct class *pmic_rtc_class;
+struct rtc_time alarm_greg_time;
+static DECLARE_MUTEX(mutex);
+
+#define DEBUG_PMIC_RTC 1
+#if DEBUG_PMIC_RTC
+#define DPRINTK(format, args...) printk(KERN_ERR "pmic_rtc"format"\n", ##args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+/*
+ * Real Time Clock Pmic API
+ */
+
+/*!
+ * This is the callback function called on TSI Pmic event, used in asynchronous
+ * call.
+ */
+static void callback_alarm_asynchronous(void *unused)
+{
+ pmic_rtc_done = true;
+}
+
+/*!
+ * This is the callback function is used in test code for (un)sub.
+ */
+static void callback_test_sub(void)
+{
+ printk(KERN_INFO "*****************************************\n");
+ printk(KERN_INFO "***** PMIC RTC 'Alarm IT CallBack' ******\n");
+ printk(KERN_INFO "*****************************************\n");
+}
+
+/*!
+ * This is the callback function called on TSI Pmic event, used in synchronous
+ * call.
+ */
+static void callback_alarm_synchronous(void *unused)
+{
+ printk(KERN_INFO "*** Alarm IT Pmic ***\n");
+ wake_up(&queue_alarm);
+}
+
+/*!
+ * This function wait the Alarm event
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_wait_alarm(void)
+{
+ DEFINE_WAIT(wait);
+ alarm_callback.func = callback_alarm_synchronous;
+ alarm_callback.param = NULL;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_RTC, alarm_callback));
+ prepare_to_wait(&queue_alarm, &wait, TASK_UNINTERRUPTIBLE);
+ schedule();
+ finish_wait(&queue_alarm, &wait);
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_RTC, alarm_callback));
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_wait_alarm);
+
+/*!
+ * This function set the real time clock of PMIC
+ *
+ * @param pmic_time value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time(struct timeval *pmic_time)
+{
+ u8 reg_val;
+ struct rtc_time greg_time;
+
+ rtc_time_to_tm(pmic_time->tv_sec, &greg_time);
+
+ reg_val = greg_time.tm_sec % 10;
+ reg_val |= ((greg_time.tm_sec / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_SECS, reg_val, 0x7f));
+
+ reg_val = greg_time.tm_min % 10;
+ reg_val |= ((greg_time.tm_min / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_MINS, reg_val, 0x7f));
+
+ reg_val = greg_time.tm_hour % 10;
+ reg_val |= ((greg_time.tm_hour / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_HRS, reg_val, 0x3f));
+
+ reg_val = greg_time.tm_wday;
+ CHECK_ERROR(pmic_write_reg(REG_MCU_DAY, reg_val, 0x3));
+
+ reg_val = greg_time.tm_mday % 10;
+ reg_val |= ((greg_time.tm_mday / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_DATE, reg_val, 0x3f));
+
+ reg_val = greg_time.tm_mon % 10;
+ reg_val |= ((greg_time.tm_mon / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_MONTH, reg_val, 0x1f));
+
+ reg_val = greg_time.tm_year % 10;
+ reg_val |= ((greg_time.tm_year / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_YEAR, reg_val, 0xff));
+
+ DPRINTK("set time = %d y, %d m, %d d, %d h, %d m, %d s",
+ greg_time.tm_year, greg_time.tm_mon, greg_time.tm_mday,
+ greg_time.tm_hour, greg_time.tm_min, greg_time.tm_sec);
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_set_time);
+
+/*!
+ * This function get the real time clock of PMIC
+ *
+ * @param pmic_time return value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time(struct timeval *pmic_time)
+{
+ unsigned int reg_val;
+ struct rtc_time greg_time;
+ unsigned long time = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_SECS, &reg_val, 0x7f));
+ greg_time.tm_sec = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_MINS, &reg_val, 0x7f));
+ greg_time.tm_min = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_HRS, &reg_val, 0x3f));
+ greg_time.tm_hour = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_DAY, &reg_val, 0x3));
+ greg_time.tm_wday = reg_val;
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_DATE, &reg_val, 0x3f));
+ greg_time.tm_mday = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_MONTH, &reg_val, 0x1f));
+ greg_time.tm_mon = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_YEAR, &reg_val, 0xff));
+ greg_time.tm_year = BCD2BIN(reg_val);
+
+ DPRINTK("get time = %d y, %d m, %d d, %d h, %d m, %d s",
+ greg_time.tm_year, greg_time.tm_mon, greg_time.tm_mday,
+ greg_time.tm_hour, greg_time.tm_min, greg_time.tm_sec);
+
+ rtc_tm_to_time(&greg_time, &time);
+ pmic_time->tv_sec = time;
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_get_time);
+
+/*!
+ * This function set the real time clock alarm of PMIC
+ *
+ * @param pmic_time value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time_alarm(struct timeval *pmic_time)
+{
+ u8 reg_val;
+ struct rtc_time greg_time;
+
+ down_interruptible(&mutex);
+
+ rtc_time_to_tm(pmic_time->tv_sec, &greg_time);
+ rtc_time_to_tm(pmic_time->tv_sec, &alarm_greg_time);
+
+ reg_val = greg_time.tm_sec % 10;
+ reg_val |= ((greg_time.tm_sec / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_ALARM_SECS, reg_val, 0x7f));
+
+ reg_val = greg_time.tm_min % 10;
+ reg_val |= ((greg_time.tm_min / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_ALARM_MINS, reg_val, 0x7f));
+
+ reg_val = greg_time.tm_hour % 10;
+ reg_val |= ((greg_time.tm_hour / 10) << 4);
+ CHECK_ERROR(pmic_write_reg(REG_MCU_ALARM_HRS, reg_val, 0x3f));
+
+ /* enable alarm */
+ CHECK_ERROR(pmic_write_reg(REG_MCU_ALARM_SECS, 0x80, 0x80));
+ DPRINTK("set alarm time = %dh, %dm, %ds\n",
+ greg_time.tm_hour, greg_time.tm_min, greg_time.tm_sec);
+
+ up(&mutex);
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_set_time_alarm);
+
+/*!
+ * This function get the real time clock alarm of PMIC
+ *
+ * @param pmic_time return value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time_alarm(struct timeval *pmic_time)
+{
+ unsigned int reg_val;
+ struct rtc_time greg_time;
+ unsigned long time = 0;
+
+ memzero(&greg_time, sizeof(struct rtc_time));
+ greg_time = alarm_greg_time;
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_ALARM_SECS, &reg_val, 0x7f));
+ greg_time.tm_sec = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_ALARM_MINS, &reg_val, 0x7f));
+ greg_time.tm_min = BCD2BIN(reg_val);
+
+ CHECK_ERROR(pmic_read_reg(REG_MCU_ALARM_HRS, &reg_val, 0x3f));
+ greg_time.tm_hour = BCD2BIN(reg_val);
+
+ rtc_tm_to_time(&greg_time, &time);
+ pmic_time->tv_sec = time;
+ DPRINTK("get alarm time = %dh, %dm, %ds\n",
+ greg_time.tm_hour, greg_time.tm_min, greg_time.tm_sec);
+
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_get_time_alarm);
+
+/*!
+ * This function is used to un/subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ * @param sub define if Un/subscribe event.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event(t_rtc_int event, void *callback, bool sub)
+{
+ type_event rtc_event;
+ if (callback == NULL) {
+ return PMIC_ERROR;
+ } else {
+ rtc_callback.func = callback;
+ rtc_callback.param = NULL;
+ }
+ switch (event) {
+ case RTC_IT_ALARM:
+ rtc_event = EVENT_RTC;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ if (sub)
+ CHECK_ERROR(pmic_event_subscribe(rtc_event, rtc_callback));
+ else
+ CHECK_ERROR(pmic_event_unsubscribe(rtc_event, rtc_callback));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_sub(t_rtc_int event, void *callback)
+{
+ CHECK_ERROR(pmic_rtc_event(event, callback, true));
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_event_sub);
+
+/*!
+ * This function is used to un subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_unsub(t_rtc_int event, void *callback)
+{
+ CHECK_ERROR(pmic_rtc_event(event, callback, false));
+ return PMIC_SUCCESS;
+}
+EXPORT_SYMBOL(pmic_rtc_event_unsub);
+
+/* Called without the kernel lock - fine */
+static unsigned int pmic_rtc_poll(struct file *file, poll_table *wait)
+{
+ if (pmic_rtc_done)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC RTC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int pmic_rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct timeval *pmic_time = NULL;
+
+ if (_IOC_TYPE(cmd) != 'p')
+ return -ENOTTY;
+
+ if (arg) {
+ pmic_time = kmalloc(sizeof(struct timeval), GFP_KERNEL);
+ if (pmic_time == NULL)
+ return -ENOMEM;
+
+ /* if (copy_from_user(pmic_time, (struct timeval *)arg,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ } */
+ }
+
+ switch (cmd) {
+ case PMIC_RTC_SET_TIME:
+ if (copy_from_user(pmic_time, (struct timeval *)arg,
+ sizeof(struct timeval)))
+ return -EFAULT;
+
+ pr_debug("SET RTC\n");
+ CHECK_ERROR(pmic_rtc_set_time(pmic_time));
+ break;
+ case PMIC_RTC_GET_TIME:
+ if (copy_to_user((struct timeval *)arg, pmic_time,
+ sizeof(struct timeval)))
+ return -EFAULT;
+
+ pr_debug("GET RTC\n");
+ CHECK_ERROR(pmic_rtc_get_time(pmic_time));
+ break;
+ case PMIC_RTC_SET_ALARM:
+ if (copy_from_user(pmic_time, (struct timeval *)arg,
+ sizeof(struct timeval)))
+ return -EFAULT;
+
+ pr_debug("SET RTC ALARM\n");
+ CHECK_ERROR(pmic_rtc_set_time_alarm(pmic_time));
+ break;
+ case PMIC_RTC_GET_ALARM:
+ if (copy_to_user((struct timeval *)arg, pmic_time,
+ sizeof(struct timeval)))
+ return -EFAULT;
+
+ pr_debug("GET RTC ALARM\n");
+ CHECK_ERROR(pmic_rtc_get_time_alarm(pmic_time));
+ break;
+ case PMIC_RTC_WAIT_ALARM:
+ printk(KERN_INFO "WAIT ALARM...\n");
+ CHECK_ERROR(pmic_rtc_event_sub(RTC_IT_ALARM,
+ callback_test_sub));
+ CHECK_ERROR(pmic_rtc_wait_alarm());
+ printk(KERN_INFO "ALARM DONE\n");
+ CHECK_ERROR(pmic_rtc_event_unsub(RTC_IT_ALARM,
+ callback_test_sub));
+ break;
+ case PMIC_RTC_ALARM_REGISTER:
+ printk(KERN_INFO "PMIC RTC ALARM REGISTER\n");
+ alarm_callback.func = callback_alarm_asynchronous;
+ alarm_callback.param = NULL;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_RTC, alarm_callback));
+ break;
+ case PMIC_RTC_ALARM_UNREGISTER:
+ printk(KERN_INFO "PMIC RTC ALARM UNREGISTER\n");
+ alarm_callback.func = callback_alarm_asynchronous;
+ alarm_callback.param = NULL;
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_RTC, alarm_callback));
+ pmic_rtc_done = false;
+ break;
+ default:
+ pr_debug("%d unsupported ioctl command\n", (int)cmd);
+ return -EINVAL;
+ }
+
+ if (arg) {
+ if (copy_to_user((struct timeval *)arg, pmic_time,
+ sizeof(struct timeval)))
+ return -EFAULT;
+
+ kfree(pmic_time);
+ }
+
+ return 0;
+}
+
+/*!
+ * This function implements the open method on a PMIC RTC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_rtc_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*!
+ * This function implements the release method on a PMIC RTC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_rtc_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*!
+ * This function is called to put the RTC in a low power state.
+ * There is no need for power handlers for the RTC device.
+ * The RTC cannot be suspended.
+ *
+ * @param pdev the device structure used to give information on which RTC
+ * device (0 through 3 channels) to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+/*!
+ * This function is called to resume the RTC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which RTC
+ * device (0 through 3 channels) to suspend
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_rtc_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+
+static struct file_operations pmic_rtc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = pmic_rtc_ioctl,
+ .poll = pmic_rtc_poll,
+ .open = pmic_rtc_open,
+ .release = pmic_rtc_release,
+};
+
+int pmic_rtc_loaded(void)
+{
+ return pmic_rtc_detected;
+}
+EXPORT_SYMBOL(pmic_rtc_loaded);
+
+
+static int pmic_rtc_remove(struct platform_device *pdev)
+{
+ class_device_destroy(pmic_rtc_class, MKDEV(pmic_rtc_major, 0));
+ class_destroy(pmic_rtc_class);
+ unregister_chrdev(pmic_rtc_major, MCU_PMIC_RTC_NAME);
+ return 0;
+}
+
+static int pmic_rtc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct class_device *temp_class;
+
+ pmic_rtc_major = register_chrdev(0, MCU_PMIC_RTC_NAME, &pmic_rtc_fops);
+ if (pmic_rtc_major < 0) {
+ printk(KERN_ERR "Unable to get a major for pmic_rtc\n");
+ return pmic_rtc_major;
+ }
+
+ pmic_rtc_class = class_create(THIS_MODULE, MCU_PMIC_RTC_NAME);
+ if (IS_ERR(pmic_rtc_class)) {
+ printk(KERN_ERR "Error creating pmic rtc class.\n");
+ ret = PTR_ERR(pmic_rtc_class);
+ goto err_out1;
+ }
+
+ temp_class = class_device_create(pmic_rtc_class, NULL,
+ MKDEV(pmic_rtc_major, 0),
+ NULL, MCU_PMIC_RTC_NAME);
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "Error creating pmic rtc class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ pmic_rtc_detected = 1;
+
+ /* start RTC */
+ pmic_write_reg(REG_MCU_SECS, 0x80, 0x80);
+
+ return ret;
+
+err_out2:
+ class_destroy(pmic_rtc_class);
+err_out1:
+ unregister_chrdev(pmic_rtc_major, MCU_PMIC_RTC_NAME);
+ return ret;
+}
+
+static struct platform_driver pmic_rtc_driver_ldm = {
+ .driver = {
+ .name = MCU_PMIC_RTC_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = pmic_rtc_suspend,
+ .resume = pmic_rtc_resume,
+ .probe = pmic_rtc_probe,
+ .remove = pmic_rtc_remove,
+};
+
+static int __init pmic_rtc_init(void)
+{
+ return platform_driver_register(&pmic_rtc_driver_ldm);
+}
+static void __exit pmic_rtc_exit(void)
+{
+ platform_driver_unregister(&pmic_rtc_driver_ldm);
+ pr_debug("PMIC RTC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_rtc_init);
+module_exit(pmic_rtc_exit);
+
+MODULE_DESCRIPTION("Pmic_rtc driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 3b3e8f4d2f2d..9b703aa785ac 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -50,4 +50,9 @@ endchoice
endmenu
+config REGULATOR_MAX8660
+ tristate "MAX8660 Regulator Support"
+ depends on REGULATOR
+ depends on MXC_PMIC_MC9SDZ60
+
endmenu
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index b54e710d41b4..b78ca53e1833 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -9,4 +9,5 @@ ifeq ($(CONFIG_REGULATOR_DEBUG),y)
endif
obj-$(CONFIG_REGULATOR_MC13783) += mc13783/
-obj-$(CONFIG_REGULATOR_WM8350) += wm8350/ \ No newline at end of file
+obj-$(CONFIG_REGULATOR_WM8350) += wm8350/
+obj-$(CONFIG_REGULATOR_MAX8660) += max8660/
diff --git a/drivers/regulator/max8660/Makefile b/drivers/regulator/max8660/Makefile
new file mode 100644
index 000000000000..e77279e88146
--- /dev/null
+++ b/drivers/regulator/max8660/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REGULATOR_MAX8660) += reg-max8660.o
diff --git a/drivers/regulator/max8660/reg-max8660.c b/drivers/regulator/max8660/reg-max8660.c
new file mode 100644
index 000000000000..7584fa8a544f
--- /dev/null
+++ b/drivers/regulator/max8660/reg-max8660.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#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/regulator-platform.h>
+#include <linux/regulator/regulator-drv.h>
+#include <asm/ioctl.h>
+#include <linux/platform_device.h>
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_external.h>
+
+/*!
+ * brief PMIC regulators.
+ */
+
+enum {
+ MCU_SW1,
+ MCU_SW2,
+ MCU_SW3,
+ MCU_SW4,
+ MCU_LDO5,
+ MCU_LDO6,
+ MCU_LDO7,
+ MCU_LCD,
+ MCU_WIFI,
+ MCU_HDD,
+ MCU_GPS,
+ MCU_CMOS,
+ MCU_PLL
+} MAX8660_regulator;
+
+/*!
+ * @enum t_pmic_regulator_voltage_mcu_sw34
+ * @brief MCU PMIC Switch mode regulator SW34 output voltages.
+ */
+enum {
+ MCU_SW34_0_725V = 0,
+ MCU_SW34_0_75V,
+ MCU_SW34_0_775V,
+ MCU_SW34_0_8V,
+ MCU_SW34_0_825V,
+ MCU_SW34_0_85V,
+ MCU_SW34_0_875V,
+ MCU_SW34_0_9V,
+ MCU_SW34_0_925V,
+ MCU_SW34_0_95V,
+ MCU_SW34_0_975V,
+ MCU_SW34_1_0V,
+ MCU_SW34_1_025V,
+ MCU_SW34_1_05V,
+ MCU_SW34_1_075V,
+ MCU_SW34_1_1V,
+ MCU_SW34_1_125V,
+ MCU_SW34_1_15V,
+ MCU_SW34_1_175V,
+ MCU_SW34_1_2V,
+ MCU_SW34_1_225V,
+ MCU_SW34_1_25V,
+ MCU_SW34_1_275V,
+ MCU_SW34_1_3V,
+ MCU_SW34_1_325V,
+ MCU_SW34_1_35V,
+ MCU_SW34_1_375V,
+ MCU_SW34_1_4V,
+ MCU_SW34_1_425V,
+ MCU_SW34_1_45V,
+ MCU_SW34_1_475V,
+ MCU_SW34_1_5V,
+ MCU_SW34_1_525V,
+ MCU_SW34_1_55V,
+ MCU_SW34_1_575V,
+ MCU_SW34_1_6V,
+ MCU_SW34_1_625V,
+ MCU_SW34_1_65V,
+ MCU_SW34_1_675V,
+ MCU_SW34_1_7V,
+ MCU_SW34_1_725V,
+ MCU_SW34_1_75V,
+ MCU_SW34_1_775V,
+ MCU_SW34_1_8V
+} t_pmic_regulator_voltage_mcu_sw34;
+
+enum {
+ MCU_LDO5_1_7V,
+ MCU_LDO5_1_725V,
+ MCU_LDO5_1_75V,
+ MCU_LDO5_1_775V,
+ MCU_LDO5_1_8V,
+ MCU_LDO5_1_825V,
+ MCU_LDO5_1_85V,
+ MCU_LDO5_1_875V,
+ MCU_LDO5_1_9V,
+ MCU_LDO5_1_925V,
+ MCU_LDO5_1_95V,
+ MCU_LDO5_1_975V,
+ MCU_LDO5_2_0V
+} t_pmic_regulator_voltage_mcu_ldo5;
+
+enum {
+ MCU_LDO67_1_8V,
+ MCU_LDO67_1_9V,
+ MCU_LDO67_2_0V,
+ MCU_LDO67_2_1V,
+ MCU_LDO67_2_2V,
+ MCU_LDO67_2_3V,
+ MCU_LDO67_2_4V,
+ MCU_LDO67_2_5V,
+ MCU_LDO67_2_6V,
+ MCU_LDO67_2_7V,
+ MCU_LDO67_2_8V,
+ MCU_LDO67_2_9V,
+ MCU_LDO67_3_0V,
+ MCU_LDO67_3_1V,
+ MCU_LDO67_3_2V,
+ MCU_LDO67_3_3V
+} t_pmic_regulator_voltage_mcu_ldo67;
+
+#define NUM_MAX8660_REGULATORS 7
+#define NUM_MAX8660_CHILDREN_REGULATORS 6
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+#define DEBUG_REG_MAX8660 1
+#if DEBUG_REG_MAX8660
+#define DPRINTK(format, args...) printk(KERN_ERR \
+ "reg-max8660: "format"\n", ##args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+static int max8660_regulator_on(int regulator)
+{
+ u8 reg_mask = 0;
+ int reg_num;
+
+ if (regulator > MCU_LDO7 || regulator < MCU_SW1)
+ return -1;
+
+ switch (regulator) {
+ case MCU_SW1:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW2:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW3:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_SW4:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_LDO5:
+ SET_BIT_IN_BYTE(reg_mask, 3);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_LDO6:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+ case MCU_LDO7:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_num, reg_mask, reg_mask));
+
+ return 0;
+}
+
+static int max8660_regulator_off(int regulator)
+{
+ u8 reg_mask = 0;
+ unsigned int reg_read = 0;
+ int reg_num;
+
+ if (regulator > MCU_LDO7 || regulator < MCU_SW1)
+ return PMIC_NOT_SUPPORTED;
+
+ switch (regulator) {
+ case MCU_SW1:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW2:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_SW3:
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_SW4:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_1;
+ break;
+ case MCU_LDO5:
+ SET_BIT_IN_BYTE(reg_mask, 3);
+ reg_num = REG_MCU_POWER_CTL;
+ break;
+ case MCU_LDO6:
+ SET_BIT_IN_BYTE(reg_mask, 1);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+ case MCU_LDO7:
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ reg_num = REG_MAX8660_OUTPUT_ENABLE_2;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_num, 0, reg_mask));
+
+ /* handle sw3,4 */
+ switch (regulator) {
+ case MCU_SW3:
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_read_reg
+ (REG_MCU_POWER_CTL, &reg_read, reg_mask));
+
+ /* check if hw pin enable sw34 */
+ if (0 != reg_read) {
+
+ /* keep sw4 on */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_OUTPUT_ENABLE_1, reg_mask,
+ reg_mask));
+
+ /* disable hw pin to actually turn off sw3 */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MCU_POWER_CTL, 0, reg_mask));
+ }
+ break;
+ case MCU_SW4:
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_read_reg
+ (REG_MCU_POWER_CTL, &reg_read, reg_mask));
+
+ /* check if hw pin enable sw34 */
+ if (0 != reg_read) {
+
+ /* keep sw3 on */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_OUTPUT_ENABLE_1, reg_mask,
+ reg_mask));
+
+ /* disable hw pin to actually turn off sw4 */
+ reg_mask = 0;
+ SET_BIT_IN_BYTE(reg_mask, 2);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MCU_POWER_CTL, 0, reg_mask));
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int max8660_sw1_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_SW1);
+}
+
+static int max8660_sw1_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_SW1);
+}
+
+static int max8660_sw2_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_SW2);
+}
+
+static int max8660_sw2_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_SW2);
+}
+
+static int max8660_sw3_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_SW3);
+}
+
+static int max8660_sw3_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_SW3);
+}
+
+static int max8660_sw3_set_voltage(struct regulator *reg, int uV)
+{
+ u8 reg_mask = 0;
+ int mV = uV / 1000;
+ int volt;
+
+ if (mV < 725 || mV > 1800)
+ return -1;
+
+ /* convert to reg value */
+ volt = (mV - 725) / 25;
+
+ /* hold on */
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, 0, reg_mask));
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg(REG_MAX8660_V3_TARGET_VOLT_1, volt, 0xff));
+
+ /* start ramp */
+ SET_BIT_IN_BYTE(reg_mask, 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, reg_mask, reg_mask));
+
+ return 0;
+}
+
+static int max8660_sw3_get_voltage(struct regulator *reg)
+{
+ int uV;
+ unsigned int reg_val = 0;
+
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V3_TARGET_VOLT_1, &reg_val, 0xff));
+
+ uV = 1000 * (reg_val * 25 + 725);
+
+ return uV;
+}
+
+static int max8660_sw4_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_SW4);
+}
+
+static int max8660_sw4_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_SW4);
+}
+
+static int max8660_sw4_set_voltage(struct regulator *reg, int uV)
+{
+ u8 reg_mask = 0;
+ int mV = uV / 1000;
+ int volt;
+
+ if (mV < 725 || mV > 1800)
+ return -1;
+
+ /* convert to reg value */
+ volt = (mV - 725) / 25;
+
+ /* hold on */
+ SET_BIT_IN_BYTE(reg_mask, 4);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, 0, reg_mask));
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg(REG_MAX8660_V4_TARGET_VOLT_1, volt, 0xff));
+
+ /* start ramp */
+ SET_BIT_IN_BYTE(reg_mask, 4);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, reg_mask, reg_mask));
+
+ return 0;
+}
+
+static int max8660_sw4_get_voltage(struct regulator *reg)
+{
+ int uV;
+ unsigned int reg_val = 0;
+
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V4_TARGET_VOLT_1, &reg_val, 0xff));
+
+ uV = 1000 * (reg_val * 25 + 725);
+
+ return uV;
+}
+
+static int max8660_ldo5_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_LDO5);
+}
+
+static int max8660_ldo5_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_LDO5);
+}
+
+static int max8660_ldo5_set_voltage(struct regulator *reg, int uV)
+{
+ u8 reg_mask = 0;
+ int mV = uV / 1000;
+ int volt;
+
+ if (mV < 1700 || mV > 2000)
+ return -1;
+
+ /* convert to reg value */
+ volt = (mV - 1700) / 25;
+
+ /* hold on */
+ SET_BIT_IN_BYTE(reg_mask, 6);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, 0, reg_mask));
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg(REG_MAX8660_V5_TARGET_VOLT_1, volt, 0xff));
+
+ /* start ramp */
+ SET_BIT_IN_BYTE(reg_mask, 6);
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_VOLT_CHANGE_CONTROL_1, reg_mask, reg_mask));
+
+ return 0;
+}
+
+static int max8660_ldo5_get_voltage(struct regulator *reg)
+{
+ int uV;
+ unsigned int reg_val = 0;
+
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V5_TARGET_VOLT_1, &reg_val, 0xff));
+
+ uV = 1000 * (reg_val * 25 + 1700);
+
+ return uV;
+
+}
+
+static int max8660_ldo6_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_LDO6);
+}
+
+static int max8660_ldo6_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_LDO6);
+}
+
+static int max8660_ldo6_set_voltage(struct regulator *reg, int uV)
+{
+ int mV = uV / 1000;
+ int volt;
+
+ if (mV < 1800 || mV > 3300)
+ return -1;
+
+ /* convert to reg value */
+ volt = (mV - 1800) / 100;
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg(REG_MAX8660_V6V7_TARGET_VOLT, volt, 0x0f));
+
+ return 0;
+}
+
+static int max8660_ldo6_get_voltage(struct regulator *reg)
+{
+ int uV;
+ unsigned int reg_val = 0;
+
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT, &reg_val, 0x0f));
+
+ uV = 1000 * (reg_val * 100 + 1800);
+
+ return uV;
+}
+
+static int max8660_ldo7_enable(struct regulator *reg)
+{
+ return max8660_regulator_on(MCU_LDO7);
+}
+
+static int max8660_ldo7_disable(struct regulator *reg)
+{
+ return max8660_regulator_off(MCU_LDO7);
+}
+
+static int max8660_ldo7_set_voltage(struct regulator *reg, int uV)
+{
+ int mV = uV / 1000;
+ int volt;
+
+ if (mV < 1800 || mV > 3300)
+ return -1;
+
+ /* convert to reg value */
+ volt = (mV - 1800) / 100;
+
+ /* set volt */
+ CHECK_ERROR(pmic_write_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT, (volt << 4), 0xf0));
+
+ return 0;
+}
+
+static int max8660_ldo7_get_voltage(struct regulator *reg)
+{
+ int uV;
+ unsigned int reg_val = 0;
+
+ CHECK_ERROR(pmic_read_reg
+ (REG_MAX8660_V6V7_TARGET_VOLT, &reg_val, 0x0f));
+ reg_val = (reg_val >> 4) & 0xf;
+
+ uV = 1000 * (reg_val * 100 + 1800);
+
+ return uV;
+}
+
+static struct regulator_ops max8660_sw1_ops = {
+ .enable = max8660_sw1_enable,
+ .disable = max8660_sw1_disable,
+};
+
+static struct regulator_ops max8660_sw2_ops = {
+ .enable = max8660_sw2_enable,
+ .disable = max8660_sw2_disable,
+};
+
+static struct regulator_ops max8660_sw3_ops = {
+ .set_voltage = max8660_sw3_set_voltage,
+ .get_voltage = max8660_sw3_get_voltage,
+ .enable = max8660_sw3_enable,
+ .disable = max8660_sw3_disable,
+};
+
+static struct regulator_ops max8660_sw4_ops = {
+ .set_voltage = max8660_sw4_set_voltage,
+ .get_voltage = max8660_sw4_get_voltage,
+ .enable = max8660_sw4_enable,
+ .disable = max8660_sw4_disable,
+};
+
+static struct regulator_ops max8660_ldo5_ops = {
+ .set_voltage = max8660_ldo5_set_voltage,
+ .get_voltage = max8660_ldo5_get_voltage,
+ .enable = max8660_ldo5_enable,
+ .disable = max8660_ldo5_disable,
+};
+
+static struct regulator_ops max8660_ldo6_ops = {
+ .set_voltage = max8660_ldo6_set_voltage,
+ .get_voltage = max8660_ldo6_get_voltage,
+ .enable = max8660_ldo6_enable,
+ .disable = max8660_ldo6_disable,
+};
+
+static struct regulator_ops max8660_ldo7_ops = {
+ .set_voltage = max8660_ldo7_set_voltage,
+ .get_voltage = max8660_ldo7_get_voltage,
+ .enable = max8660_ldo7_enable,
+ .disable = max8660_ldo7_disable,
+};
+
+struct regulation_constraints max8660_sw3_regulation_constraints = {
+ .min_uV = mV_to_uV(725),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+};
+
+struct regulation_constraints max8660_sw4_regulation_constraints = {
+ .min_uV = mV_to_uV(725),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+};
+
+struct regulation_constraints max8660_ldo5_regulation_constraints = {
+ .min_uV = mV_to_uV(1700),
+ .max_uV = mV_to_uV(2000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+};
+
+struct regulation_constraints max8660_ldo6_regulation_constraints = {
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(3300),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+};
+
+struct regulation_constraints max8660_ldo7_regulation_constraints = {
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(3300),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+};
+
+struct max8660_regulator {
+ struct regulator regulator;
+};
+
+static struct max8660_regulator reg_max8660[NUM_MAX8660_REGULATORS] = {
+ {
+ .regulator = {
+ .name = "SW1",
+ .id = MCU_SW1,
+ .ops = &max8660_sw1_ops,
+ },
+ },
+ {
+ .regulator = {
+ .name = "SW2",
+ .id = MCU_SW2,
+ .ops = &max8660_sw2_ops,
+ },
+ },
+ {
+ .regulator = {
+ .name = "SW3",
+ .id = MCU_SW3,
+ .ops = &max8660_sw3_ops,
+ .constraints = &max8660_sw3_regulation_constraints,
+ },
+ },
+ {
+ .regulator = {
+ .name = "SW4",
+ .id = MCU_SW4,
+ .ops = &max8660_sw4_ops,
+ .constraints = &max8660_sw4_regulation_constraints,
+ },
+ },
+ {
+ .regulator = {
+ .name = "LDO5",
+ .id = MCU_LDO5,
+ .ops = &max8660_ldo5_ops,
+ .constraints = &max8660_ldo5_regulation_constraints,
+ },
+ },
+ {
+ .regulator = {
+ .name = "LDO6",
+ .id = MCU_LDO6,
+ .ops = &max8660_ldo6_ops,
+ .constraints = &max8660_ldo6_regulation_constraints,
+ },
+ },
+ {
+ .regulator = {
+ .name = "LDO7",
+ .id = MCU_LDO7,
+ .ops = &max8660_ldo7_ops,
+ .constraints = &max8660_ldo7_regulation_constraints,
+ },
+ },
+};
+
+/* children regulator ops*/
+
+/* lcd */
+static int max8660_lcd_enable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 6, 1);
+}
+
+static int max8660_lcd_disable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 6, 0);
+}
+
+static struct regulator_ops max8660_lcd_ops = {
+ .enable = max8660_lcd_enable,
+ .disable = max8660_lcd_disable,
+};
+
+/* wifi */
+static int max8660_wifi_enable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 5, 1);
+}
+
+static int max8660_wifi_disable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 5, 0);
+}
+
+static struct regulator_ops max8660_wifi_ops = {
+ .enable = max8660_wifi_enable,
+ .disable = max8660_wifi_disable,
+};
+
+/* hdd */
+static int max8660_hdd_enable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 4, 1);
+}
+
+static int max8660_hdd_disable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 4, 0);
+}
+
+static struct regulator_ops max8660_hdd_ops = {
+ .enable = max8660_hdd_enable,
+ .disable = max8660_hdd_disable,
+};
+
+/* gps */
+static int max8660_gps_enable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0, 1);
+}
+
+static int max8660_gps_disable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0, 0);
+}
+
+static struct regulator_ops max8660_gps_ops = {
+ .enable = max8660_gps_enable,
+ .disable = max8660_gps_disable,
+};
+
+/* cmos */
+static int max8660_cmos_enable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 4, 1);
+}
+
+static int max8660_cmos_disable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 4, 0);
+}
+
+static struct regulator_ops max8660_cmos_ops = {
+ .enable = max8660_cmos_enable,
+ .disable = max8660_cmos_disable,
+};
+
+/* pll */
+static int max8660_pll_enable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 5, 1);
+}
+
+static int max8660_pll_disable(struct regulator *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 5, 0);
+}
+
+static struct regulator_ops max8660_pll_ops = {
+ .enable = max8660_pll_enable,
+ .disable = max8660_pll_disable,
+};
+
+static struct max8660_regulator
+ reg_max8660_children[NUM_MAX8660_CHILDREN_REGULATORS] = {
+ {
+ .regulator = {
+ .name = "LCD",
+ .id = MCU_LCD,
+ .ops = &max8660_lcd_ops,
+ .parent = &reg_max8660[0].regulator, /*SW1 */
+ },
+ },
+ {
+ .regulator = {
+ .name = "WIFI",
+ .id = MCU_WIFI,
+ .ops = &max8660_wifi_ops,
+ .parent = &reg_max8660[3].regulator, /*SW4 */
+ },
+ },
+ {
+ .regulator = {
+ .name = "HDD",
+ .id = MCU_HDD,
+ .ops = &max8660_hdd_ops,
+ },
+ },
+ {
+ .regulator = {
+ .name = "GPS",
+ .id = MCU_GPS,
+ .ops = &max8660_gps_ops,
+ .parent = &reg_max8660[0].regulator, /*SW1 */
+ },
+
+ },
+ /* hw not finished!! */
+ {
+ .regulator = {
+ .name = "CMOS",
+ .id = MCU_CMOS,
+ .ops = &max8660_cmos_ops,
+ },
+
+ },
+ /* hw not finished!! */
+ {
+ .regulator = {
+ .name = "PLL",
+ .id = MCU_PLL,
+ .ops = &max8660_pll_ops,
+ },
+
+ },
+
+};
+
+/*
+ * Init and Exit
+ */
+int reg_max8660_probe(void)
+{
+ int ret11 = 0;
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(reg_max8660); i++) {
+ ret11 = regulator_register(&reg_max8660[i].regulator);
+ regulator_set_platform_constraints(reg_max8660[i].regulator.
+ name,
+ reg_max8660[i].regulator.
+ constraints);
+ if (ret11 < 0) {
+ DPRINTK("%s: failed to register %s err %d\n",
+ __func__, reg_max8660[i].regulator.name, ret11);
+ i--;
+ for (; i >= 0; i--)
+ regulator_unregister(&reg_max8660[i].regulator);
+
+ return ret11;
+ }
+ DPRINTK("%s: success register %s err %d\n",
+ __func__, reg_max8660[i].regulator.name, ret11);
+
+ }
+
+ /* for child regulators */
+ for (i = 0; i < ARRAY_SIZE(reg_max8660_children); i++) {
+ ret11 = regulator_register(&reg_max8660_children[i].regulator);
+ regulator_set_platform_source(&reg_max8660_children[i].
+ regulator,
+ reg_max8660[i].regulator.parent);
+ if (ret11 < 0) {
+ DPRINTK("%s: failed to register %s err %d\n",
+ __func__,
+ reg_max8660_children[i].regulator.name, ret11);
+ i--;
+ for (; i >= 0; i--)
+ regulator_unregister(&reg_max8660_children[i].
+ regulator);
+
+ return ret11;
+ }
+ DPRINTK("%s: success register %s err %d\n",
+ __func__, reg_max8660_children[i].regulator.name,
+ ret11);
+
+ }
+ DPRINTK("max8660 regulator successfully probed\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(reg_max8660_probe);
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MAX8660 Regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/reg-core.c b/drivers/regulator/reg-core.c
index afaf68457ba7..0f0f7f82bb1d 100644
--- a/drivers/regulator/reg-core.c
+++ b/drivers/regulator/reg-core.c
@@ -224,6 +224,21 @@ static ssize_t regulator_enabled_use_count(struct class_device *cdev, char *buf)
return sprintf(buf, "%d\n", regulator->use_count);
}
+static ssize_t regulator_ctl(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct regulator *regulator = to_regulator(cdev);
+ if (buf[0] == '0') {
+ printk(KERN_WARNING"disable regulator.\n");
+ if (regulator_disable(regulator))
+ printk(KERN_ERR"disable regulator failed.\n");
+ } else {
+ printk(KERN_WARNING"enable regulator.\n");
+ if (regulator_enable(regulator))
+ printk(KERN_ERR"enable regulator failed.\n");
+ }
+ return count;
+}
static struct class_device_attribute regulator_dev_attrs[] = {
__ATTR(uV, 0444, regulator_uV_show, NULL),
@@ -235,6 +250,7 @@ static struct class_device_attribute regulator_dev_attrs[] = {
__ATTR(valid_modes, 0444, regulator_constraint_modes_show, NULL),
__ATTR(total_uA_load, 0444, regulator_total_dev_load, NULL),
__ATTR(enabled_count, 0444, regulator_enabled_use_count, NULL),
+ __ATTR(ctl, 0666, NULL, regulator_ctl),
__ATTR_NULL,
};
diff --git a/include/asm-arm/arch-mxc/pmic_external.h b/include/asm-arm/arch-mxc/pmic_external.h
index 71e1e032fafc..5679a21b495c 100644
--- a/include/asm-arm/arch-mxc/pmic_external.h
+++ b/include/asm-arm/arch-mxc/pmic_external.h
@@ -129,6 +129,8 @@ typedef struct {
#define PMIC_MAX_EVENTS 48
#define PMIC_ARBITRATION "NULL"
+
+#ifdef CONFIG_MXC_PMIC_MC13783
/*!
* This is the enumeration of register names of MC13783
*/
@@ -804,6 +806,98 @@ typedef struct {
bool sense_clks;
} t_sensor_bits;
+#endif /*CONFIG_MXC_PMIC_MC13783 */
+
+#ifdef CONFIG_MXC_PMIC_MC9SDZ60
+
+typedef enum {
+
+ /*reg names for mcu */
+ REG_MCU_VERSION = 0,
+ REG_MCU_SECS,
+ REG_MCU_MINS,
+ REG_MCU_HRS,
+ REG_MCU_DAY,
+ REG_MCU_DATE,
+ REG_MCU_MONTH,
+ REG_MCU_YEAR,
+ REG_MCU_ALARM_SECS,
+ REG_MCU_ALARM_MINS,
+ REG_MCU_ALARM_HRS,
+ REG_MCU_TS_CONTROL,
+ REG_MCU_X_LOW,
+ REG_MCU_Y_LOW,
+ REG_MCU_XY_HIGH,
+ REG_MCU_X_LEFT_LOW,
+ REG_MCU_X_LEFT_HIGH,
+ REG_MCU_X_RIGHT,
+ REG_MCU_Y_TOP_LOW,
+ REG_MCU_Y_TOP_HIGH,
+ REG_MCU_Y_BOTTOM,
+ REG_MCU_RESET_1,
+ REG_MCU_RESET_2,
+ REG_MCU_POWER_CTL,
+ REG_MCU_DELAY_CONFIG,
+ REG_MCU_GPIO_1,
+ REG_MCU_GPIO_2,
+ REG_MCU_KPD_1,
+ REG_MCU_KPD_2,
+ REG_MCU_KPD_CONTROL,
+ REG_MCU_INT_ENABLE_1,
+ REG_MCU_INT_ENABLE_2,
+ REG_MCU_INT_FLAG_1,
+ REG_MCU_INT_FLAG_2,
+
+ /* reg names for max8660 */
+ REG_MAX8660_OUTPUT_ENABLE_1,
+ REG_MAX8660_OUTPUT_ENABLE_2,
+ REG_MAX8660_VOLT_CHANGE_CONTROL_1,
+ REG_MAX8660_V3_TARGET_VOLT_1,
+ REG_MAX8660_V3_TARGET_VOLT_2,
+ REG_MAX8660_V4_TARGET_VOLT_1,
+ REG_MAX8660_V4_TARGET_VOLT_2,
+ REG_MAX8660_V5_TARGET_VOLT_1,
+ REG_MAX8660_V5_TARGET_VOLT_2,
+ REG_MAX8660_V6V7_TARGET_VOLT,
+ REG_MAX8660_FORCE_PWM
+} pmic_reg;
+
+typedef enum {
+ EVENT_HEADPHONE_DET = 0,
+ EVENT_GPS_INT = 1,
+ EVENT_SD1_DET = 2,
+ EVENT_SD1_WP = 3,
+ EVENT_SD2_DET = 4,
+ EVENT_SD2_WP = 5,
+ EVENT_POWER_KEY = 6,
+
+ /* part 2 */
+ EVENT_RTC = 7,
+ EVENT_TS_ADC = 8,
+ EVENT_KEYPAD = 9
+} type_event;
+
+typedef enum {
+
+ MCU_GPIO_REG_RESET_1,
+ MCU_GPIO_REG_RESET_2,
+ MCU_GPIO_REG_POWER_CONTROL,
+ MCU_GPIO_REG_GPIO_CONTROL_1,
+ MCU_GPIO_REG_GPIO_CONTROL_2,
+} t_mcu_gpio_reg;
+
+typedef enum {
+
+ MCU_SENSOR_NOT_SUPPORT
+} t_sensor;
+
+typedef enum {
+
+ MCU_SENSOR_BIT_NOT_SUPPORT
+} t_sensor_bits;
+
+#endif /* MXC_PMIC_MC9SDZ60 */
+
/* EXPORTED FUNCTIONS */
#ifdef __KERNEL__
@@ -875,6 +969,12 @@ bool pmic_check_sensor(t_sensor sensor);
*/
PMIC_STATUS pmic_get_sensors(t_sensor_bits * sensor_bits);
+PMIC_STATUS pmic_gpio_set_bit_val(t_mcu_gpio_reg reg, unsigned int bit,
+ unsigned int val);
+
+PMIC_STATUS pmic_gpio_get_bit_val(t_mcu_gpio_reg reg, unsigned int bit,
+ unsigned int *val);
+
#ifdef CONFIG_REGULATOR_MC13783
/*!
* This function is used to initialize the regulator for MC13783.