diff options
Diffstat (limited to 'drivers/mxc/pmic/core')
-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 |
6 files changed, 924 insertions, 0 deletions
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"); |