diff options
author | Jingyu Zhou <b02241@freescale.com> | 2008-04-02 13:58:21 +0800 |
---|---|---|
committer | Daniel Schaeffer <daniel.schaeffer@timesys.com> | 2008-08-25 15:20:57 -0400 |
commit | f6c413b2f5b1b503421f4543955aeff7a40bba92 (patch) | |
tree | 0f3e59b1c9a3ba6331733cb8971a2b67425650c9 | |
parent | 12573d7103e37acb79ffe972560bbb5018259089 (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_defconfig | 13 | ||||
-rw-r--r-- | drivers/mxc/pmic/Kconfig | 18 | ||||
-rw-r--r-- | drivers/mxc/pmic/Makefile | 2 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/Makefile | 10 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/max8660.c | 149 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/max8660.h | 45 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/mc9sdz60.c | 111 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/mc9sdz60.h | 69 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/mcu_pmic_core.c | 540 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc9sdz60/Kconfig | 29 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc9sdz60/Makefile | 12 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc9sdz60/mcu_pmic_event.c | 220 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc9sdz60/mcu_pmic_gpio.c | 111 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc9sdz60/mcu_pmic_power.c | 465 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc9sdz60/mcu_pmic_rtc.c | 599 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 5 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 3 | ||||
-rw-r--r-- | drivers/regulator/max8660/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/max8660/reg-max8660.c | 898 | ||||
-rw-r--r-- | drivers/regulator/reg-core.c | 16 | ||||
-rw-r--r-- | include/asm-arm/arch-mxc/pmic_external.h | 100 |
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, ®_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, ®_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, ®_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, ®_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, ®_val, 0xff)); + voltage->mcu_sw34 = reg_val; + break; + case MCU_SW4: + CHECK_ERROR(pmic_read_reg + (REG_MAX8660_V4_TARGET_VOLT_1, ®_val, 0xff)); + voltage->mcu_sw34 = reg_val; + break; + case MCU_LDO5: + CHECK_ERROR(pmic_read_reg + (REG_MAX8660_V5_TARGET_VOLT_1, ®_val, 0xff)); + voltage->mcu_ldo5 = reg_val; + break; + case MCU_LDO6: + CHECK_ERROR(pmic_read_reg + (REG_MAX8660_V6V7_TARGET_VOLT, ®_val, 0x0f)); + voltage->mcu_ldo67 = reg_val; + break; + case MCU_LDO7: + CHECK_ERROR(pmic_read_reg + (REG_MAX8660_V6V7_TARGET_VOLT, ®_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, ®_val, 0x7f)); + greg_time.tm_sec = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_MINS, ®_val, 0x7f)); + greg_time.tm_min = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_HRS, ®_val, 0x3f)); + greg_time.tm_hour = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_DAY, ®_val, 0x3)); + greg_time.tm_wday = reg_val; + + CHECK_ERROR(pmic_read_reg(REG_MCU_DATE, ®_val, 0x3f)); + greg_time.tm_mday = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_MONTH, ®_val, 0x1f)); + greg_time.tm_mon = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_YEAR, ®_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, ®_val, 0x7f)); + greg_time.tm_sec = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_ALARM_MINS, ®_val, 0x7f)); + greg_time.tm_min = BCD2BIN(reg_val); + + CHECK_ERROR(pmic_read_reg(REG_MCU_ALARM_HRS, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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 = ®_max8660[0].regulator, /*SW1 */ + }, + }, + { + .regulator = { + .name = "WIFI", + .id = MCU_WIFI, + .ops = &max8660_wifi_ops, + .parent = ®_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 = ®_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(®_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(®_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(®_max8660_children[i].regulator); + regulator_set_platform_source(®_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(®_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. |