diff options
Diffstat (limited to 'drivers/mxc/pmic/core')
-rw-r--r-- | drivers/mxc/pmic/core/Makefile | 21 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/mc13783.c | 380 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/mc13892.c | 335 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/mc34704.c | 329 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic-dev.c | 316 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic.h | 138 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic_common.c | 131 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic_core_i2c.c | 348 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic_core_spi.c | 303 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic_event.c | 236 | ||||
-rw-r--r-- | drivers/mxc/pmic/core/pmic_external.c | 100 |
11 files changed, 2637 insertions, 0 deletions
diff --git a/drivers/mxc/pmic/core/Makefile b/drivers/mxc/pmic/core/Makefile new file mode 100644 index 000000000000..bb42231e3aa8 --- /dev/null +++ b/drivers/mxc/pmic/core/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for the PMIC core drivers. +# +obj-$(CONFIG_MXC_PMIC_MC13783) += pmic_mc13783_mod.o +pmic_mc13783_mod-objs := pmic_external.o pmic_event.o pmic_common.o pmic_core_spi.o mc13783.o + +obj-$(CONFIG_MXC_PMIC_MC13892) += pmic_mc13892_mod.o +pmic_mc13892_mod-objs := pmic_external.o pmic_event.o pmic_common.o mc13892.o + +ifneq ($(CONFIG_MXC_PMIC_SPI),) +pmic_mc13892_mod-objs += pmic_core_spi.o +endif + +ifneq ($(CONFIG_MXC_PMIC_I2C),) +pmic_mc13892_mod-objs += pmic_core_i2c.o +endif + +obj-$(CONFIG_MXC_PMIC_MC34704) += pmic_mc34704_mod.o +pmic_mc34704_mod-objs := pmic_external.o pmic_event.o mc34704.o + +obj-$(CONFIG_MXC_PMIC_CHARDEV) += pmic-dev.o diff --git a/drivers/mxc/pmic/core/mc13783.c b/drivers/mxc/pmic/core/mc13783.c new file mode 100644 index 000000000000..812de6e146aa --- /dev/null +++ b/drivers/mxc/pmic/core/mc13783.c @@ -0,0 +1,380 @@ +/* + * Copyright 2004-2010 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 pmic/core/mc13783.c + * @brief This file contains MC13783 specific PMIC code. This implementaion + * may differ for each PMIC chip. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> +#include <linux/spi/spi.h> +#include <linux/mfd/mc13783/core.h> + +#include <asm/uaccess.h> + +#include "pmic.h" + +/* + * Defines + */ +#define EVENT_MASK_0 0x697fdf +#define EVENT_MASK_1 0x3efffb +#define MXC_PMIC_FRAME_MASK 0x00FFFFFF +#define MXC_PMIC_MAX_REG_NUM 0x3F +#define MXC_PMIC_REG_NUM_SHIFT 0x19 +#define MXC_PMIC_WRITE_BIT_SHIFT 31 + +static unsigned int events_enabled0; +static unsigned int events_enabled1; +struct mxc_pmic pmic_drv_data; + +/*! + * This function is called to read a register on PMIC. + * + * @param reg_num number of the pmic register to be read + * @param reg_val return value of register + * + * @return Returns 0 on success -1 on failure. + */ +int pmic_read(unsigned int reg_num, unsigned int *reg_val) +{ + unsigned int frame = 0; + int ret = 0; + + if (reg_num > MXC_PMIC_MAX_REG_NUM) + return PMIC_ERROR; + + frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT; + + ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1); + + *reg_val = frame & MXC_PMIC_FRAME_MASK; + + return ret; +} + +/*! + * This function is called to write a value to the register on PMIC. + * + * @param reg_num number of the pmic register to be written + * @param reg_val value to be written + * + * @return Returns 0 on success -1 on failure. + */ +int pmic_write(int reg_num, const unsigned int reg_val) +{ + unsigned int frame = 0; + int ret = 0; + + if (reg_num > MXC_PMIC_MAX_REG_NUM) + return PMIC_ERROR; + + frame |= (1 << MXC_PMIC_WRITE_BIT_SHIFT); + + frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT; + + frame |= reg_val & MXC_PMIC_FRAME_MASK; + + ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1); + + return ret; +} + +void *pmic_alloc_data(struct device *dev) +{ + struct mc13783 *mc13783; + + mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL); + if (mc13783 == NULL) + return NULL; + + mc13783->dev = dev; + + return (void *)mc13783; +} + +/*! + * This function initializes the SPI device parameters for this PMIC. + * + * @param spi the SPI slave device(PMIC) + * + * @return None + */ +int pmic_spi_setup(struct spi_device *spi) +{ + /* Setup the SPI slave i.e.PMIC */ + pmic_drv_data.spi = spi; + + spi->mode = SPI_MODE_2 | SPI_CS_HIGH; + spi->bits_per_word = 32; + + return spi_setup(spi); +} + +/*! + * This function initializes the PMIC registers. + * + * @return None + */ +int pmic_init_registers(void) +{ + CHECK_ERROR(pmic_write(REG_INTERRUPT_MASK_0, MXC_PMIC_FRAME_MASK)); + CHECK_ERROR(pmic_write(REG_INTERRUPT_MASK_1, MXC_PMIC_FRAME_MASK)); + CHECK_ERROR(pmic_write(REG_INTERRUPT_STATUS_0, MXC_PMIC_FRAME_MASK)); + CHECK_ERROR(pmic_write(REG_INTERRUPT_STATUS_1, MXC_PMIC_FRAME_MASK)); + return PMIC_SUCCESS; +} + +/*! + * This function returns the PMIC version in system. + * + * @param ver pointer to the pmic_version_t structure + * + * @return This function returns PMIC version. + */ +void pmic_get_revision(pmic_version_t *ver) +{ + int rev_id = 0; + int rev1 = 0; + int rev2 = 0; + int finid = 0; + int icid = 0; + + ver->id = PMIC_MC13783; + pmic_read(REG_REVISION, &rev_id); + + rev1 = (rev_id & 0x018) >> 3; + rev2 = (rev_id & 0x007); + icid = (rev_id & 0x01C0) >> 6; + finid = (rev_id & 0x01E00) >> 9; + + /* Ver 0.2 is actually 3.2a. Report as 3.2 */ + if ((rev1 == 0) && (rev2 == 2)) { + rev1 = 3; + } + + if (rev1 == 0 || icid != 2) { + ver->revision = -1; + printk(KERN_NOTICE + "mc13783: Not detected.\tAccess failed\t!!!\n"); + } else { + ver->revision = ((rev1 * 10) + rev2); + printk(KERN_INFO "mc13783 Rev %d.%d FinVer %x detected\n", rev1, + rev2, finid); + } + + return; + +} + +/*! + * 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 status0, status1; + int bit_set; + + pmic_read(REG_INTERRUPT_STATUS_0, &status0); + pmic_read(REG_INTERRUPT_STATUS_1, &status1); + pmic_write(REG_INTERRUPT_STATUS_0, status0); + pmic_write(REG_INTERRUPT_STATUS_1, status1); + status0 &= events_enabled0; + status1 &= events_enabled1; + + while (status0) { + bit_set = ffs(status0) - 1; + *(active_events + count) = bit_set; + count++; + status0 ^= (1 << bit_set); + } + while (status1) { + bit_set = ffs(status1) - 1; + *(active_events + count) = bit_set + 24; + count++; + status1 ^= (1 << bit_set); + } + + return count; +} + +/*! + * 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) +{ + unsigned int event_mask = 0; + unsigned int mask_reg = 0; + unsigned int event_bit = 0; + int ret; + + if (event < EVENT_E1HZI) { + mask_reg = REG_INTERRUPT_MASK_0; + event_mask = EVENT_MASK_0; + event_bit = (1 << event); + events_enabled0 |= event_bit; + } else { + event -= 24; + mask_reg = REG_INTERRUPT_MASK_1; + event_mask = EVENT_MASK_1; + event_bit = (1 << event); + events_enabled1 |= event_bit; + } + + if ((event_bit & event_mask) == 0) { + pr_debug("Error: unmasking a reserved/unused event\n"); + return PMIC_ERROR; + } + + ret = pmic_write_reg(mask_reg, 0, event_bit); + + 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) +{ + unsigned int event_mask = 0; + unsigned int mask_reg = 0; + unsigned int event_bit = 0; + int ret; + + if (event < EVENT_E1HZI) { + mask_reg = REG_INTERRUPT_MASK_0; + event_mask = EVENT_MASK_0; + event_bit = (1 << event); + events_enabled0 &= ~event_bit; + } else { + event -= 24; + mask_reg = REG_INTERRUPT_MASK_1; + event_mask = EVENT_MASK_1; + event_bit = (1 << event); + events_enabled1 &= ~event_bit; + } + + if ((event_bit & event_mask) == 0) { + pr_debug("Error: masking a reserved/unused event\n"); + return PMIC_ERROR; + } + + ret = pmic_write_reg(mask_reg, event_bit, event_bit); + + pr_debug("Disable Event : %d\n", event); + + return ret; +} + +/*! + * This function is called to read all sensor bits of PMIC. + * + * @param sensor Sensor to be checked. + * + * @return This function returns true if the sensor bit is high; + * or returns false if the sensor bit is low. + */ +bool pmic_check_sensor(t_sensor sensor) +{ + unsigned int reg_val = 0; + + CHECK_ERROR(pmic_read_reg + (REG_INTERRUPT_SENSE_0, ®_val, PMIC_ALL_BITS)); + + if ((1 << sensor) & reg_val) + return true; + else + return false; +} + +/*! + * This function checks one sensor of PMIC. + * + * @param sensor_bits structure of all sensor bits. + * + * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE. + */ + +PMIC_STATUS pmic_get_sensors(t_sensor_bits *sensor_bits) +{ + int sense_0 = 0; + int sense_1 = 0; + + memset(sensor_bits, 0, sizeof(t_sensor_bits)); + + pmic_read_reg(REG_INTERRUPT_SENSE_0, &sense_0, 0xffffff); + pmic_read_reg(REG_INTERRUPT_SENSE_1, &sense_1, 0xffffff); + + sensor_bits->sense_chgdets = (sense_0 & (1 << 6)) ? true : false; + sensor_bits->sense_chgovs = (sense_0 & (1 << 7)) ? true : false; + sensor_bits->sense_chgrevs = (sense_0 & (1 << 8)) ? true : false; + sensor_bits->sense_chgshorts = (sense_0 & (1 << 9)) ? true : false; + sensor_bits->sense_cccvs = (sense_0 & (1 << 10)) ? true : false; + sensor_bits->sense_chgcurrs = (sense_0 & (1 << 11)) ? true : false; + sensor_bits->sense_bpons = (sense_0 & (1 << 12)) ? true : false; + sensor_bits->sense_lobatls = (sense_0 & (1 << 13)) ? true : false; + sensor_bits->sense_lobaths = (sense_0 & (1 << 14)) ? true : false; + sensor_bits->sense_usb4v4s = (sense_0 & (1 << 16)) ? true : false; + sensor_bits->sense_usb2v0s = (sense_0 & (1 << 17)) ? true : false; + sensor_bits->sense_usb0v8s = (sense_0 & (1 << 18)) ? true : false; + sensor_bits->sense_id_floats = (sense_0 & (1 << 19)) ? true : false; + sensor_bits->sense_id_gnds = (sense_0 & (1 << 20)) ? true : false; + sensor_bits->sense_se1s = (sense_0 & (1 << 21)) ? true : false; + sensor_bits->sense_ckdets = (sense_0 & (1 << 22)) ? true : false; + + sensor_bits->sense_onofd1s = (sense_1 & (1 << 3)) ? true : false; + sensor_bits->sense_onofd2s = (sense_1 & (1 << 4)) ? true : false; + sensor_bits->sense_onofd3s = (sense_1 & (1 << 5)) ? true : false; + sensor_bits->sense_pwrrdys = (sense_1 & (1 << 11)) ? true : false; + sensor_bits->sense_thwarnhs = (sense_1 & (1 << 12)) ? true : false; + sensor_bits->sense_thwarnls = (sense_1 & (1 << 13)) ? true : false; + sensor_bits->sense_clks = (sense_1 & (1 << 14)) ? true : false; + sensor_bits->sense_mc2bs = (sense_1 & (1 << 17)) ? true : false; + sensor_bits->sense_hsdets = (sense_1 & (1 << 18)) ? true : false; + sensor_bits->sense_hsls = (sense_1 & (1 << 19)) ? true : false; + sensor_bits->sense_alspths = (sense_1 & (1 << 20)) ? true : false; + sensor_bits->sense_ahsshorts = (sense_1 & (1 << 21)) ? true : false; + return PMIC_SUCCESS; +} + +EXPORT_SYMBOL(pmic_check_sensor); +EXPORT_SYMBOL(pmic_get_sensors); diff --git a/drivers/mxc/pmic/core/mc13892.c b/drivers/mxc/pmic/core/mc13892.c new file mode 100644 index 000000000000..a2cbb1645901 --- /dev/null +++ b/drivers/mxc/pmic/core/mc13892.c @@ -0,0 +1,335 @@ +/* + * Copyright 2008-2010 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 pmic/core/mc13892.c + * @brief This file contains MC13892 specific PMIC code. This implementaion + * may differ for each PMIC chip. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> +#include <linux/mfd/mc13892/core.h> + +#include <asm/mach-types.h> +#include <asm/uaccess.h> + +#include "pmic.h" + +/* + * Defines + */ +#define MC13892_I2C_RETRY_TIMES 10 +#define MXC_PMIC_FRAME_MASK 0x00FFFFFF +#define MXC_PMIC_MAX_REG_NUM 0x3F +#define MXC_PMIC_REG_NUM_SHIFT 0x19 +#define MXC_PMIC_WRITE_BIT_SHIFT 31 + +static unsigned int events_enabled0; +static unsigned int events_enabled1; +static struct mxc_pmic pmic_drv_data; +#ifndef CONFIG_MXC_PMIC_I2C +struct i2c_client *mc13892_client; +#endif + +int pmic_i2c_24bit_read(struct i2c_client *client, unsigned int reg_num, + unsigned int *value) +{ + unsigned char buf[3]; + int ret; + int i; + + memset(buf, 0, 3); + for (i = 0; i < MC13892_I2C_RETRY_TIMES; i++) { + ret = i2c_smbus_read_i2c_block_data(client, reg_num, 3, buf); + if (ret == 3) + break; + msleep(1); + } + + if (ret == 3) { + *value = buf[0] << 16 | buf[1] << 8 | buf[2]; + return ret; + } else { + pr_err("24bit read error, ret = %d\n", ret); + return -1; /* return -1 on failure */ + } +} + +int pmic_i2c_24bit_write(struct i2c_client *client, + unsigned int reg_num, unsigned int reg_val) +{ + char buf[3]; + int ret; + int i; + + buf[0] = (reg_val >> 16) & 0xff; + buf[1] = (reg_val >> 8) & 0xff; + buf[2] = (reg_val) & 0xff; + + for (i = 0; i < MC13892_I2C_RETRY_TIMES; i++) { + ret = i2c_smbus_write_i2c_block_data(client, reg_num, 3, buf); + if (ret == 0) + break; + msleep(1); + } + if (i == MC13892_I2C_RETRY_TIMES) + pr_err("24bit write error, ret = %d\n", ret); + + return ret; +} + +int pmic_read(int reg_num, unsigned int *reg_val) +{ + unsigned int frame = 0; + int ret = 0; + + if (pmic_drv_data.spi != NULL) { + if (reg_num > MXC_PMIC_MAX_REG_NUM) + return PMIC_ERROR; + + frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT; + + ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1); + + *reg_val = frame & MXC_PMIC_FRAME_MASK; + } else { + if (mc13892_client == NULL) + return PMIC_ERROR; + + if (pmic_i2c_24bit_read(mc13892_client, reg_num, reg_val) == -1) + return PMIC_ERROR; + } + + return PMIC_SUCCESS; +} + +int pmic_write(int reg_num, const unsigned int reg_val) +{ + unsigned int frame = 0; + int ret = 0; + + if (pmic_drv_data.spi != NULL) { + if (reg_num > MXC_PMIC_MAX_REG_NUM) + return PMIC_ERROR; + + frame |= (1 << MXC_PMIC_WRITE_BIT_SHIFT); + + frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT; + + frame |= reg_val & MXC_PMIC_FRAME_MASK; + + ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1); + + return ret; + } else { + if (mc13892_client == NULL) + return PMIC_ERROR; + + return pmic_i2c_24bit_write(mc13892_client, reg_num, reg_val); + } +} + +void *pmic_alloc_data(struct device *dev) +{ + struct mc13892 *mc13892; + + mc13892 = kzalloc(sizeof(struct mc13892), GFP_KERNEL); + if (mc13892 == NULL) + return NULL; + + mc13892->dev = dev; + + return (void *)mc13892; +} + +/*! + * This function initializes the SPI device parameters for this PMIC. + * + * @param spi the SPI slave device(PMIC) + * + * @return None + */ +int pmic_spi_setup(struct spi_device *spi) +{ + /* Setup the SPI slave i.e.PMIC */ + pmic_drv_data.spi = spi; + + spi->mode = SPI_MODE_0 | SPI_CS_HIGH; + spi->bits_per_word = 32; + + return spi_setup(spi); +} + +int pmic_init_registers(void) +{ + CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF)); + CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF)); + CHECK_ERROR(pmic_write(REG_INT_STATUS0, 0xFFFFFF)); + CHECK_ERROR(pmic_write(REG_INT_STATUS1, 0xFFFFFF)); + /* disable auto charge */ + if (machine_is_mx51_3ds()) + CHECK_ERROR(pmic_write(REG_CHARGE, 0xB40003)); + + pm_power_off = mc13892_power_off; + + return PMIC_SUCCESS; +} + +unsigned int pmic_get_active_events(unsigned int *active_events) +{ + unsigned int count = 0; + unsigned int status0, status1; + int bit_set; + + pmic_read(REG_INT_STATUS0, &status0); + pmic_read(REG_INT_STATUS1, &status1); + pmic_write(REG_INT_STATUS0, status0); + pmic_write(REG_INT_STATUS1, status1); + status0 &= events_enabled0; + status1 &= events_enabled1; + + while (status0) { + bit_set = ffs(status0) - 1; + *(active_events + count) = bit_set; + count++; + status0 ^= (1 << bit_set); + } + while (status1) { + bit_set = ffs(status1) - 1; + *(active_events + count) = bit_set + 24; + count++; + status1 ^= (1 << bit_set); + } + + return count; +} + +#define EVENT_MASK_0 0x387fff +#define EVENT_MASK_1 0x1177ef + +int pmic_event_unmask(type_event event) +{ + unsigned int event_mask = 0; + unsigned int mask_reg = 0; + unsigned int event_bit = 0; + int ret; + + if (event < EVENT_1HZI) { + mask_reg = REG_INT_MASK0; + event_mask = EVENT_MASK_0; + event_bit = (1 << event); + events_enabled0 |= event_bit; + } else { + event -= 24; + mask_reg = REG_INT_MASK1; + event_mask = EVENT_MASK_1; + event_bit = (1 << event); + events_enabled1 |= event_bit; + } + + if ((event_bit & event_mask) == 0) { + pr_debug("Error: unmasking a reserved/unused event\n"); + return PMIC_ERROR; + } + + ret = pmic_write_reg(mask_reg, 0, event_bit); + + pr_debug("Enable Event : %d\n", event); + + return ret; +} + +int pmic_event_mask(type_event event) +{ + unsigned int event_mask = 0; + unsigned int mask_reg = 0; + unsigned int event_bit = 0; + int ret; + + if (event < EVENT_1HZI) { + mask_reg = REG_INT_MASK0; + event_mask = EVENT_MASK_0; + event_bit = (1 << event); + events_enabled0 &= ~event_bit; + } else { + event -= 24; + mask_reg = REG_INT_MASK1; + event_mask = EVENT_MASK_1; + event_bit = (1 << event); + events_enabled1 &= ~event_bit; + } + + if ((event_bit & event_mask) == 0) { + pr_debug("Error: masking a reserved/unused event\n"); + return PMIC_ERROR; + } + + ret = pmic_write_reg(mask_reg, event_bit, event_bit); + + pr_debug("Disable Event : %d\n", event); + + return ret; +} + +/*! + * This function returns the PMIC version in system. + * + * @param ver pointer to the pmic_version_t structure + * + * @return This function returns PMIC version. + */ +void pmic_get_revision(pmic_version_t *ver) +{ + int rev_id = 0; + int rev1 = 0; + int rev2 = 0; + int finid = 0; + int icid = 0; + + ver->id = PMIC_MC13892; + pmic_read(REG_IDENTIFICATION, &rev_id); + + rev1 = (rev_id & 0x018) >> 3; + rev2 = (rev_id & 0x007); + icid = (rev_id & 0x01C0) >> 6; + finid = (rev_id & 0x01E00) >> 9; + + ver->revision = ((rev1 * 10) + rev2); + printk(KERN_INFO "mc13892 Rev %d.%d FinVer %x detected\n", rev1, + rev2, finid); +} + +void mc13892_power_off(void) +{ + unsigned int value; + + pmic_read_reg(REG_POWER_CTL0, &value, 0xffffff); + + value |= 0x000008; + + pmic_write_reg(REG_POWER_CTL0, value, 0xffffff); +} diff --git a/drivers/mxc/pmic/core/mc34704.c b/drivers/mxc/pmic/core/mc34704.c new file mode 100644 index 000000000000..e70a45134c6a --- /dev/null +++ b/drivers/mxc/pmic/core/mc34704.c @@ -0,0 +1,329 @@ +/* + * Copyright 2008-2010 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 pmic/core/mc34704.c + * @brief This file contains MC34704 specific PMIC code. + * + * @ingroup PMIC_CORE + */ + +/* + * 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/i2c.h> +#include <linux/uaccess.h> +#include <linux/mfd/mc34704/core.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> + +#include "pmic.h" + +/* + * Globals + */ +static pmic_version_t mxc_pmic_version = { + .id = PMIC_MC34704, + .revision = 0, +}; +static unsigned int events_enabled; +unsigned int active_events[MAX_ACTIVE_EVENTS]; +struct i2c_client *mc34704_client; +static void pmic_trigger_poll(void); + +#define MAX_MC34704_REG 0x59 +static unsigned int mc34704_reg_readonly[MAX_MC34704_REG / 32 + 1] = { + (1 << 0x03) || (1 << 0x05) || (1 << 0x07) || (1 << 0x09) || + (1 << 0x0B) || (1 << 0x0E) || (1 << 0x11) || (1 << 0x14) || + (1 << 0x17) || (1 << 0x18), + 0, +}; +static unsigned int mc34704_reg_written[MAX_MC34704_REG / 32 + 1]; +static unsigned char mc34704_shadow_regs[MAX_MC34704_REG - 1]; +#define IS_READONLY(r) ((1 << ((r) % 32)) & mc34704_reg_readonly[(r) / 32]) +#define WAS_WRITTEN(r) ((1 << ((r) % 32)) & mc34704_reg_written[(r) / 32]) +#define MARK_WRITTEN(r) do { \ + mc34704_reg_written[(r) / 32] |= (1 << ((r) % 32)); \ +} while (0) + +int pmic_read(int reg_nr, unsigned int *reg_val) +{ + int c; + + /* + * Use the shadow register if we've written to it + */ + if (WAS_WRITTEN(reg_nr)) { + *reg_val = mc34704_shadow_regs[reg_nr]; + return PMIC_SUCCESS; + } + + /* + * Otherwise, actually read the real register. + * Write-only registers will read as zero. + */ + c = i2c_smbus_read_byte_data(mc34704_client, reg_nr); + if (c == -1) { + pr_debug("mc34704: error reading register 0x%02x\n", reg_nr); + return PMIC_ERROR; + } else { + *reg_val = c; + return PMIC_SUCCESS; + } +} + +int pmic_write(int reg_nr, const unsigned int reg_val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(mc34704_client, reg_nr, reg_val); + if (ret == -1) { + return PMIC_ERROR; + } else { + /* + * Update our software copy of the register since you + * can't read what you wrote. + */ + if (!IS_READONLY(reg_nr)) { + mc34704_shadow_regs[reg_nr] = reg_val; + MARK_WRITTEN(reg_nr); + } + return PMIC_SUCCESS; + } +} + +unsigned int pmic_get_active_events(unsigned int *active_events) +{ + unsigned int count = 0; + unsigned int faults; + int bit_set; + + /* Check for any relevant PMIC faults */ + pmic_read(REG_MC34704_FAULTS, &faults); + faults &= events_enabled; + + /* + * Mask all active events, because there is no way to acknowledge + * or dismiss them in the PMIC -- they're sticky. + */ + events_enabled &= ~faults; + + /* Account for all unmasked faults */ + while (faults) { + bit_set = ffs(faults) - 1; + *(active_events + count) = bit_set; + count++; + faults ^= (1 << bit_set); + } + return count; +} + +int pmic_event_unmask(type_event event) +{ + unsigned int event_bit = 0; + unsigned int prior_events = events_enabled; + + event_bit = (1 << event); + events_enabled |= event_bit; + + pr_debug("Enable Event : %d\n", event); + + /* start the polling task as needed */ + if (events_enabled && prior_events == 0) + pmic_trigger_poll(); + + return 0; +} + +int pmic_event_mask(type_event event) +{ + unsigned int event_bit = 0; + + event_bit = (1 << event); + events_enabled &= ~event_bit; + + pr_debug("Disable Event : %d\n", event); + + return 0; +} + +/*! + * PMIC event polling task. This task is called periodically to poll + * for possible MC34704 events (No interrupt supplied by the hardware). + */ +static void pmic_event_task(struct work_struct *work); +DECLARE_DELAYED_WORK(pmic_ws, pmic_event_task); + +static void pmic_trigger_poll(void) +{ + schedule_delayed_work(&pmic_ws, HZ / 10); +} + +static void pmic_event_task(struct work_struct *work) +{ + unsigned int count = 0; + int i; + + count = pmic_get_active_events(active_events); + pr_debug("active events number %d\n", count); + + /* call handlers for all active events */ + for (i = 0; i < count; i++) + pmic_event_callback(active_events[i]); + + /* re-trigger this task, but only if somebody is watching */ + if (events_enabled) + pmic_trigger_poll(); + + return; +} + +pmic_version_t pmic_get_version(void) +{ + return mxc_pmic_version; +} +EXPORT_SYMBOL(pmic_get_version); + +int __devinit pmic_init_registers(void) +{ + /* + * Set some registers to what they should be, + * if for no other reason than to initialize our + * software register copies. + */ + CHECK_ERROR(pmic_write(REG_MC34704_GENERAL2, 0x09)); + CHECK_ERROR(pmic_write(REG_MC34704_VGSET1, 0)); + CHECK_ERROR(pmic_write(REG_MC34704_REG2SET1, 0)); + CHECK_ERROR(pmic_write(REG_MC34704_REG3SET1, 0)); + CHECK_ERROR(pmic_write(REG_MC34704_REG4SET1, 0)); + CHECK_ERROR(pmic_write(REG_MC34704_REG5SET1, 0)); + + return PMIC_SUCCESS; +} + +static int __devinit is_chip_onboard(struct i2c_client *client) +{ + int val; + + /* + * This PMIC has no version or ID register, so just see + * if it ACK's and returns 0 on some write-only register as + * evidence of its presence. + */ + val = i2c_smbus_read_byte_data(client, REG_MC34704_GENERAL2); + if (val != 0) + return -1; + + return 0; +} + +static int __devinit pmic_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct mc34704 *mc34704; + struct mc34704_platform_data *plat_data = client->dev.platform_data; + + if (!plat_data || !plat_data->init) + return -ENODEV; + + ret = is_chip_onboard(client); + + if (ret == -1) + return -ENODEV; + + mc34704 = kzalloc(sizeof(struct mc34704), GFP_KERNEL); + if (mc34704 == NULL) + return -ENOMEM; + + i2c_set_clientdata(client, mc34704); + mc34704->dev = &client->dev; + mc34704->i2c_client = client; + + mc34704_client = client; + + /* Initialize the PMIC event handling */ + pmic_event_list_init(); + + /* Initialize PMI registers */ + if (pmic_init_registers() != PMIC_SUCCESS) + return PMIC_ERROR; + + ret = plat_data->init(mc34704); + if (ret != 0) + return PMIC_ERROR; + + dev_info(&client->dev, "Loaded\n"); + + return PMIC_SUCCESS; +} + +static int pmic_remove(struct i2c_client *client) +{ + return 0; +} + +static int pmic_suspend(struct i2c_client *client, pm_message_t state) +{ + return 0; +} + +static int pmic_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id mc34704_id[] = { + {"mc34704", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, mc34704_id); + +static struct i2c_driver pmic_driver = { + .driver = { + .name = "mc34704", + .bus = NULL, + }, + .probe = pmic_probe, + .remove = pmic_remove, + .suspend = pmic_suspend, + .resume = pmic_resume, + .id_table = mc34704_id, +}; + +static int __init pmic_init(void) +{ + return i2c_add_driver(&pmic_driver); +} + +static void __exit pmic_exit(void) +{ + i2c_del_driver(&pmic_driver); +} + +/* + * Module entry points + */ +subsys_initcall_sync(pmic_init); +module_exit(pmic_exit); + +MODULE_DESCRIPTION("MC34704 PMIC driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/mxc/pmic/core/pmic-dev.c b/drivers/mxc/pmic/core/pmic-dev.c new file mode 100644 index 000000000000..7c457db47b4d --- /dev/null +++ b/drivers/mxc/pmic/core/pmic-dev.c @@ -0,0 +1,316 @@ +/* + * Copyright 2005-2010 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 pmic-dev.c + * @brief This provides /dev interface to the user program. They make it + * possible to have user-space programs use or control PMIC. Mainly its + * useful for notifying PMIC events to user-space programs. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kdev_t.h> +#include <linux/circ_buf.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/pmic_external.h> + +#include <asm/uaccess.h> + +#define PMIC_NAME "pmic" +#define CIRC_BUF_MAX 16 + +static int pmic_major; +static struct class *pmic_class; +static struct fasync_struct *pmic_dev_queue; + +static DECLARE_MUTEX(event_mutex); +static struct circ_buf pmic_events; + +static void callbackfn(void *event) +{ + printk(KERN_INFO "\n\n DETECTED PMIC EVENT : %d\n\n", + (unsigned int)event); +} + +static void user_notify_callback(void *event) +{ + down(&event_mutex); + if (CIRC_SPACE(pmic_events.head, pmic_events.tail, CIRC_BUF_MAX)) { + pmic_events.buf[pmic_events.head] = (int)event; + pmic_events.head = (pmic_events.head + 1) & (CIRC_BUF_MAX - 1); + } else { + pr_info("Failed to notify event to the user\n"); + } + up(&event_mutex); + + kill_fasync(&pmic_dev_queue, SIGIO, POLL_IN); +} + +/*! + * This function implements IOCTL controls on a PMIC 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_dev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + register_info reg_info; + pmic_event_callback_t event_sub; + type_event event = EVENT_NB; + int ret = 0; + + if (_IOC_TYPE(cmd) != 'P') + return -ENOTTY; + + switch (cmd) { + case PMIC_READ_REG: + if (copy_from_user(®_info, (register_info *) arg, + sizeof(register_info))) { + return -EFAULT; + } + ret = + pmic_read_reg(reg_info.reg, &(reg_info.reg_value), + 0x00ffffff); + pr_debug("read reg %d %x\n", reg_info.reg, reg_info.reg_value); + if (copy_to_user((register_info *) arg, ®_info, + sizeof(register_info))) { + return -EFAULT; + } + break; + + case PMIC_WRITE_REG: + if (copy_from_user(®_info, (register_info *) arg, + sizeof(register_info))) { + return -EFAULT; + } + ret = + pmic_write_reg(reg_info.reg, reg_info.reg_value, + 0x00ffffff); + pr_debug("write reg %d %x\n", reg_info.reg, reg_info.reg_value); + if (copy_to_user((register_info *) arg, ®_info, + sizeof(register_info))) { + return -EFAULT; + } + break; + + case PMIC_SUBSCRIBE: + if (get_user(event, (int __user *)arg)) { + return -EFAULT; + } + event_sub.func = callbackfn; + event_sub.param = (void *)event; + ret = pmic_event_subscribe(event, event_sub); + pr_debug("subscribe done\n"); + break; + + case PMIC_UNSUBSCRIBE: + if (get_user(event, (int __user *)arg)) { + return -EFAULT; + } + event_sub.func = callbackfn; + event_sub.param = (void *)event; + ret = pmic_event_unsubscribe(event, event_sub); + pr_debug("unsubscribe done\n"); + break; + + case PMIC_NOTIFY_USER: + if (get_user(event, (int __user *)arg)) { + return -EFAULT; + } + event_sub.func = user_notify_callback; + event_sub.param = (void *)event; + ret = pmic_event_subscribe(event, event_sub); + break; + + case PMIC_GET_NOTIFY: + down(&event_mutex); + if (CIRC_CNT(pmic_events.head, pmic_events.tail, CIRC_BUF_MAX)) { + event = (int)pmic_events.buf[pmic_events.tail]; + pmic_events.tail = (pmic_events.tail + 1) & (CIRC_BUF_MAX - 1); + } else { + pr_info("No valid notified event\n"); + } + up(&event_mutex); + + if (put_user(event, (int __user *)arg)) { + return -EFAULT; + } + break; + + default: + printk(KERN_ERR "%d unsupported ioctl command\n", (int)cmd); + return -EINVAL; + } + + return ret; +} + +/*! + * This function implements the open method on a PMIC device. + * + * @param inode pointer on the node + * @param file pointer on the file + * @return This function returns 0. + */ +static int pmic_dev_open(struct inode *inode, struct file *file) +{ + pr_debug("open\n"); + return PMIC_SUCCESS; +} + +/*! + * This function implements the release method on a PMIC device. + * + * @param inode pointer on the node + * @param file pointer on the file + * + * @return This function returns 0. + */ +static int pmic_dev_free(struct inode *inode, struct file *file) +{ + pr_debug("free\n"); + return PMIC_SUCCESS; +} + +static int pmic_dev_fasync(int fd, struct file *filp, int mode) +{ + return fasync_helper(fd, filp, mode, &pmic_dev_queue); +} + +/*! + * This structure defines file operations for a PMIC device. + */ +static struct file_operations pmic_fops = { + /*! + * the owner + */ + .owner = THIS_MODULE, + /*! + * the ioctl operation + */ + .ioctl = pmic_dev_ioctl, + /*! + * the open operation + */ + .open = pmic_dev_open, + /*! + * the release operation + */ + .release = pmic_dev_free, + /*! + * the release operation + */ + .fasync = pmic_dev_fasync, +}; + +/*! + * This function implements the init function of the PMIC char device. + * This function is called when the module is loaded. It registers + * the character device for PMIC to be used by user-space programs. + * + * @return This function returns 0. + */ +static int __init pmic_dev_init(void) +{ + int ret = 0; + struct device *pmic_device; + pmic_version_t pmic_ver; + + pmic_ver = pmic_get_version(); + if (pmic_ver.revision < 0) { + printk(KERN_ERR "No PMIC device found\n"); + return -ENODEV; + } + + pmic_major = register_chrdev(0, PMIC_NAME, &pmic_fops); + if (pmic_major < 0) { + printk(KERN_ERR "unable to get a major for pmic\n"); + return pmic_major; + } + + pmic_class = class_create(THIS_MODULE, PMIC_NAME); + if (IS_ERR(pmic_class)) { + printk(KERN_ERR "Error creating pmic class.\n"); + ret = PMIC_ERROR; + goto err; + } + + pmic_device = device_create(pmic_class, NULL, MKDEV(pmic_major, 0), NULL, + PMIC_NAME); + if (IS_ERR(pmic_device)) { + printk(KERN_ERR "Error creating pmic class device.\n"); + ret = PMIC_ERROR; + goto err1; + } + + pmic_events.buf = kmalloc(CIRC_BUF_MAX * sizeof(char), GFP_KERNEL); + if (NULL == pmic_events.buf) { + ret = -ENOMEM; + goto err2; + } + pmic_events.head = pmic_events.tail = 0; + + printk(KERN_INFO "PMIC Character device: successfully loaded\n"); + return ret; + err2: + device_destroy(pmic_class, MKDEV(pmic_major, 0)); + err1: + class_destroy(pmic_class); + err: + unregister_chrdev(pmic_major, PMIC_NAME); + return ret; + +} + +/*! + * This function implements the exit function of the PMIC character device. + * This function is called when the module is unloaded. It unregisters + * the PMIC character device. + * + */ +static void __exit pmic_dev_exit(void) +{ + device_destroy(pmic_class, MKDEV(pmic_major, 0)); + class_destroy(pmic_class); + + unregister_chrdev(pmic_major, PMIC_NAME); + + printk(KERN_INFO "PMIC Character device: successfully unloaded\n"); +} + +/* + * Module entry points + */ + +module_init(pmic_dev_init); +module_exit(pmic_dev_exit); + +MODULE_DESCRIPTION("PMIC Protocol /dev entries driver"); +MODULE_AUTHOR("FreeScale"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mxc/pmic/core/pmic.h b/drivers/mxc/pmic/core/pmic.h new file mode 100644 index 000000000000..d4ba34786888 --- /dev/null +++ b/drivers/mxc/pmic/core/pmic.h @@ -0,0 +1,138 @@ +/* + * Copyright 2004-2010 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 + */ +#ifndef __PMIC_H__ +#define __PMIC_H__ + + /*! + * @file pmic.h + * @brief This file contains prototypes of all the functions to be + * defined for each PMIC chip. The implementation of these may differ + * from PMIC chip to PMIC chip. + * + * @ingroup PMIC_CORE + */ + +#include <linux/spi/spi.h> + +#define MAX_ACTIVE_EVENTS 10 + +/*! + * This structure is a way for the PMIC core driver to define their own + * \b spi_device structure. This structure includes the core \b spi_device + * structure that is provided by Linux SPI Framework/driver as an + * element and may contain other elements that are required by core driver. + */ +struct mxc_pmic { + /*! + * Master side proxy for an SPI slave device(PMIC) + */ + struct spi_device *spi; +}; + +/*! + * This function is called to transfer data to PMIC on SPI. + * + * @param spi the SPI slave device(PMIC) + * @param buf the pointer to the data buffer + * @param len the length of the data to be transferred + * + * @return Returns 0 on success -1 on failure. + */ +static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = (const void *)buf, + .rx_buf = buf, + .len = len, + .cs_change = 0, + .delay_usecs = 0, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + if (spi_sync(spi, &m) != 0 || m.status != 0) + return PMIC_ERROR; + return len - m.actual_length; +} + +/*! + * This function returns the PMIC version in system. + * + * @param ver pointer to the pmic_version_t structure + * + * @return This function returns PMIC version. + */ +void pmic_get_revision(pmic_version_t *ver); + +/*! + * This function initializes the SPI device parameters for this PMIC. + * + * @param spi the SPI slave device(PMIC) + * + * @return None + */ +int pmic_spi_setup(struct spi_device *spi); + +/*! + * This function initializes the PMIC registers. + * + * @return None + */ +int pmic_init_registers(void); + +/*! + * 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); + +/*! + * 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); + +/*! + * 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); + +#ifdef CONFIG_MXC_PMIC_FIXARB +extern PMIC_STATUS pmic_fix_arbitration(struct spi_device *spi); +#else +static inline PMIC_STATUS pmic_fix_arbitration(struct spi_device *spi) +{ + return PMIC_SUCCESS; +} +#endif + +void *pmic_alloc_data(struct device *dev); + +int pmic_start_event_thread(int irq_num); + +void pmic_stop_event_thread(void); + +#endif /* __PMIC_H__ */ diff --git a/drivers/mxc/pmic/core/pmic_common.c b/drivers/mxc/pmic/core/pmic_common.c new file mode 100644 index 000000000000..7e53121f12b8 --- /dev/null +++ b/drivers/mxc/pmic/core/pmic_common.c @@ -0,0 +1,131 @@ +/* + * Copyright 2009-2010 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 pmic_common.c + * @brief This is the common file for the PMIC Core/Protocol driver. + * + * @ingroup PMIC_CORE + */ + +/* + * 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 <linux/kthread.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> + +#include <asm/uaccess.h> + +#include "pmic.h" + +/* + * Global variables + */ +pmic_version_t mxc_pmic_version; +unsigned int active_events[MAX_ACTIVE_EVENTS]; + + +static struct completion event_completion; +static struct task_struct *tstask; + +static int pmic_event_thread_func(void *v) +{ + unsigned int loop; + unsigned int count = 0; + unsigned int irq = (int)v; + + while (1) { + wait_for_completion_interruptible( + &event_completion); + if (kthread_should_stop()) + break; + + count = pmic_get_active_events( + active_events); + pr_debug("active events number %d\n", count); + + do { + for (loop = 0; loop < count; loop++) + pmic_event_callback(active_events[loop]); + + count = pmic_get_active_events(active_events); + + } while (count != 0); + enable_irq(irq); + } + + return 0; +} + +int pmic_start_event_thread(int irq_num) +{ + int ret = 0; + + if (tstask) + return ret; + + init_completion(&event_completion); + + tstask = kthread_run(pmic_event_thread_func, + (void *)irq_num, "pmic-event-thread"); + ret = IS_ERR(tstask) ? -1 : 0; + if (IS_ERR(tstask)) + tstask = NULL; + return ret; +} + +void pmic_stop_event_thread(void) +{ + if (tstask) { + complete(&event_completion); + kthread_stop(tstask); + } +} + +/*! + * 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. + */ +irqreturn_t pmic_irq_handler(int irq, void *dev_id) +{ + disable_irq_nosync(irq); + complete(&event_completion); + + return IRQ_HANDLED; +} + +/*! + * 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); diff --git a/drivers/mxc/pmic/core/pmic_core_i2c.c b/drivers/mxc/pmic/core/pmic_core_i2c.c new file mode 100644 index 000000000000..8dfec5ad4976 --- /dev/null +++ b/drivers/mxc/pmic/core/pmic_core_i2c.c @@ -0,0 +1,348 @@ +/* + * Copyright 2008-2010 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 pmic_core_i2c.c + * @brief This is the main file for the PMIC Core/Protocol driver. i2c + * should be providing the interface between the PMIC and the MCU. + * + * @ingroup PMIC_CORE + */ + +/* + * 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 <linux/i2c.h> +#include <linux/mfd/mc13892/core.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> +#include <linux/uaccess.h> +#include <mach/hardware.h> + +#include "pmic.h" + +#define MC13892_GENERATION_ID_LSH 6 +#define MC13892_IC_ID_LSH 13 + +#define MC13892_GENERATION_ID_WID 3 +#define MC13892_IC_ID_WID 6 + +#define MC13892_GEN_ID_VALUE 0x7 +#define MC13892_IC_ID_VALUE 1 + +/* + * Global variables + */ +struct i2c_client *mc13892_client; + +extern pmic_version_t mxc_pmic_version; +extern irqreturn_t pmic_irq_handler(int irq, void *dev_id); +/* + * Platform device structure for PMIC client drivers + */ +static struct platform_device adc_ldm = { + .name = "pmic_adc", + .id = 1, +}; +static struct platform_device battery_ldm = { + .name = "pmic_battery", + .id = 1, +}; +static struct platform_device power_ldm = { + .name = "pmic_power", + .id = 1, +}; +static struct platform_device rtc_ldm = { + .name = "pmic_rtc", + .id = 1, +}; +static struct platform_device light_ldm = { + .name = "pmic_light", + .id = 1, +}; +static struct platform_device rleds_ldm = { + .name = "pmic_leds", + .id = 'r', +}; +static struct platform_device gleds_ldm = { + .name = "pmic_leds", + .id = 'g', +}; +static struct platform_device bleds_ldm = { + .name = "pmic_leds", + .id = 'b', +}; + +static void pmic_pdev_register(struct device *dev) +{ + platform_device_register(&adc_ldm); + + if (!cpu_is_mx53()) + platform_device_register(&battery_ldm); + + platform_device_register(&rtc_ldm); + platform_device_register(&power_ldm); + platform_device_register(&light_ldm); + platform_device_register(&rleds_ldm); + platform_device_register(&gleds_ldm); + platform_device_register(&bleds_ldm); +} + +/*! + * This function unregisters platform device structures for + * PMIC client drivers. + */ +static void pmic_pdev_unregister(void) +{ + platform_device_unregister(&adc_ldm); + platform_device_unregister(&battery_ldm); + platform_device_unregister(&rtc_ldm); + platform_device_unregister(&power_ldm); + platform_device_unregister(&light_ldm); +} + +static int __devinit is_chip_onboard(struct i2c_client *client) +{ + unsigned int ret = 0; + + /*bind the right device to the driver */ + if (pmic_i2c_24bit_read(client, REG_IDENTIFICATION, &ret) == -1) + return -1; + + if (MC13892_GEN_ID_VALUE != BITFEXT(ret, MC13892_GENERATION_ID)) { + /*compare the address value */ + dev_err(&client->dev, + "read generation ID 0x%x is not equal to 0x%x!\n", + BITFEXT(ret, MC13892_GENERATION_ID), + MC13892_GEN_ID_VALUE); + return -1; + } + + return 0; +} + +static ssize_t mc13892_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, value; + int offset = (REG_TEST4 + 1) / 4; + + for (i = 0; i < offset; i++) { + pmic_read(i, &value); + pr_info("reg%02d: %06x\t\t", i, value); + pmic_read(i + offset, &value); + pr_info("reg%02d: %06x\t\t", i + offset, value); + pmic_read(i + offset * 2, &value); + pr_info("reg%02d: %06x\t\t", i + offset * 2, value); + pmic_read(i + offset * 3, &value); + pr_info("reg%02d: %06x\n", i + offset * 3, value); + } + + return 0; +} + +static ssize_t mc13892_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int reg, value, ret; + char *p; + + reg = simple_strtoul(buf, NULL, 10); + + p = NULL; + p = memchr(buf, ' ', count); + + if (p == NULL) { + pmic_read(reg, &value); + pr_debug("reg%02d: %06x\n", reg, value); + return count; + } + + p += 1; + + value = simple_strtoul(p, NULL, 16); + + ret = pmic_write(reg, value); + if (ret == 0) + pr_debug("write reg%02d: %06x\n", reg, value); + else + pr_debug("register update failed\n"); + + return count; +} + +static struct device_attribute mc13892_dev_attr = { + .attr = { + .name = "mc13892_ctl", + .mode = S_IRUSR | S_IWUSR, + }, + .show = mc13892_show, + .store = mc13892_store, +}; + +static int __devinit pmic_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + int pmic_irq; + struct mc13892 *mc13892; + struct mc13892_platform_data *plat_data = client->dev.platform_data; + + ret = is_chip_onboard(client); + if (ret == -1) + return -ENODEV; + + mc13892 = kzalloc(sizeof(struct mc13892), GFP_KERNEL); + if (mc13892 == NULL) + return -ENOMEM; + + i2c_set_clientdata(client, mc13892); + mc13892->dev = &client->dev; + mc13892->i2c_client = client; + + /* so far, we got matched chip on board */ + + mc13892_client = client; + + /* Initialize the PMIC event handling */ + pmic_event_list_init(); + + /* Initialize GPIO for PMIC Interrupt */ + gpio_pmic_active(); + + /* Get the PMIC Version */ + pmic_get_revision(&mxc_pmic_version); + if (mxc_pmic_version.revision < 0) { + dev_err((struct device *)client, + "PMIC not detected!!! Access Failed\n"); + return -ENODEV; + } else { + dev_dbg((struct device *)client, + "Detected pmic core IC version number is %d\n", + mxc_pmic_version.revision); + } + + /* Initialize the PMIC parameters */ + ret = pmic_init_registers(); + if (ret != PMIC_SUCCESS) + return PMIC_ERROR; + + pmic_irq = (int)(client->irq); + if (pmic_irq == 0) + return PMIC_ERROR; + + ret = pmic_start_event_thread(pmic_irq); + if (ret) { + pr_err("mc13892 pmic driver init: \ + fail to start event thread\n"); + return PMIC_ERROR; + } + + /* Set and install PMIC IRQ handler */ + + set_irq_type(pmic_irq, IRQF_TRIGGER_HIGH); + + ret = + request_irq(pmic_irq, pmic_irq_handler, 0, "PMIC_IRQ", + 0); + + if (ret) { + dev_err(&client->dev, "request irq %d error!\n", pmic_irq); + return ret; + } + enable_irq_wake(pmic_irq); + + if (plat_data && plat_data->init) { + ret = plat_data->init(mc13892); + if (ret != 0) + return PMIC_ERROR; + } + + ret = device_create_file(&client->dev, &mc13892_dev_attr); + if (ret) + dev_err(&client->dev, "create device file failed!\n"); + + pmic_pdev_register(&client->dev); + + dev_info(&client->dev, "Loaded\n"); + + return PMIC_SUCCESS; +} + +static int pmic_remove(struct i2c_client *client) +{ + int pmic_irq = (int)(client->irq); + + pmic_stop_event_thread(); + free_irq(pmic_irq, 0); + pmic_pdev_unregister(); + return 0; +} + +static int pmic_suspend(struct i2c_client *client, pm_message_t state) +{ + return 0; +} + +static int pmic_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id mc13892_id[] = { + {"mc13892", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, mc13892_id); + +static struct i2c_driver pmic_driver = { + .driver = { + .name = "mc13892", + .bus = NULL, + }, + .probe = pmic_probe, + .remove = pmic_remove, + .suspend = pmic_suspend, + .resume = pmic_resume, + .id_table = mc13892_id, +}; + +static int __init pmic_init(void) +{ + return i2c_add_driver(&pmic_driver); +} + +static void __exit pmic_exit(void) +{ + i2c_del_driver(&pmic_driver); +} + +/* + * Module entry points + */ +subsys_initcall_sync(pmic_init); +module_exit(pmic_exit); + +MODULE_DESCRIPTION("Core/Protocol driver for PMIC"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/mxc/pmic/core/pmic_core_spi.c b/drivers/mxc/pmic/core/pmic_core_spi.c new file mode 100644 index 000000000000..5461dbe3f0f0 --- /dev/null +++ b/drivers/mxc/pmic/core/pmic_core_spi.c @@ -0,0 +1,303 @@ +/* + * Copyright 2004-2010 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 pmic_core_spi.c + * @brief This is the main file for the PMIC Core/Protocol driver. SPI + * should be providing the interface between the PMIC and the MCU. + * + * @ingroup PMIC_CORE + */ + +/* + * 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 <linux/spi/spi.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> + +#include <asm/uaccess.h> + +#include "pmic.h" + +/* + * Static functions + */ +static void pmic_pdev_register(void); +static void pmic_pdev_unregister(void); + +/* + * Platform device structure for PMIC client drivers + */ +static struct platform_device adc_ldm = { + .name = "pmic_adc", + .id = 1, +}; +static struct platform_device battery_ldm = { + .name = "pmic_battery", + .id = 1, +}; +static struct platform_device power_ldm = { + .name = "pmic_power", + .id = 1, +}; +static struct platform_device rtc_ldm = { + .name = "pmic_rtc", + .id = 1, +}; +static struct platform_device light_ldm = { + .name = "pmic_light", + .id = 1, +}; +static struct platform_device rleds_ldm = { + .name = "pmic_leds", + .id = 'r', +}; +static struct platform_device gleds_ldm = { + .name = "pmic_leds", + .id = 'g', +}; +static struct platform_device bleds_ldm = { + .name = "pmic_leds", + .id = 'b', +}; + +/* + * External functions + */ +extern void pmic_event_list_init(void); +extern void pmic_event_callback(type_event event); +extern void gpio_pmic_active(void); +extern irqreturn_t pmic_irq_handler(int irq, void *dev_id); +extern pmic_version_t mxc_pmic_version; + +/*! + * This function registers platform device structures for + * PMIC client drivers. + */ +static void pmic_pdev_register(void) +{ + platform_device_register(&adc_ldm); + platform_device_register(&battery_ldm); + platform_device_register(&rtc_ldm); + platform_device_register(&power_ldm); + platform_device_register(&light_ldm); + platform_device_register(&rleds_ldm); + platform_device_register(&gleds_ldm); + platform_device_register(&bleds_ldm); +} + +/*! + * This function unregisters platform device structures for + * PMIC client drivers. + */ +static void pmic_pdev_unregister(void) +{ + platform_device_unregister(&adc_ldm); + platform_device_unregister(&battery_ldm); + platform_device_unregister(&rtc_ldm); + platform_device_unregister(&power_ldm); + platform_device_unregister(&light_ldm); +} + +/*! + * This function puts the SPI slave device in low-power mode/state. + * + * @param spi the SPI slave device + * @param message the power state to enter + * + * @return Returns 0 on SUCCESS and error on FAILURE. + */ +static int pmic_suspend(struct spi_device *spi, pm_message_t message) +{ + return PMIC_SUCCESS; +} + +/*! + * This function brings the SPI slave device back from low-power mode/state. + * + * @param spi the SPI slave device + * + * @return Returns 0 on SUCCESS and error on FAILURE. + */ +static int pmic_resume(struct spi_device *spi) +{ + return PMIC_SUCCESS; +} + +static struct spi_driver pmic_driver; + +/*! + * This function is called whenever the SPI slave device is detected. + * + * @param spi the SPI slave device + * + * @return Returns 0 on SUCCESS and error on FAILURE. + */ +static int __devinit pmic_probe(struct spi_device *spi) +{ + int ret = 0; + struct pmic_platform_data *plat_data = spi->dev.platform_data; + + /* Initialize the PMIC parameters */ + ret = pmic_spi_setup(spi); + if (ret != PMIC_SUCCESS) { + return PMIC_ERROR; + } + + /* Initialize the PMIC event handling */ + pmic_event_list_init(); + + /* Initialize GPIO for PMIC Interrupt */ + gpio_pmic_active(); + + /* Get the PMIC Version */ + pmic_get_revision(&mxc_pmic_version); + if (mxc_pmic_version.revision < 0) { + dev_err((struct device *)spi, + "PMIC not detected!!! Access Failed\n"); + return -ENODEV; + } else { + dev_dbg((struct device *)spi, + "Detected pmic core IC version number is %d\n", + mxc_pmic_version.revision); + } + + spi_set_drvdata(spi, pmic_alloc_data(&(spi->dev))); + + /* Initialize the PMIC parameters */ + ret = pmic_init_registers(); + if (ret != PMIC_SUCCESS) { + kfree(spi_get_drvdata(spi)); + spi_set_drvdata(spi, NULL); + return PMIC_ERROR; + } + + ret = pmic_start_event_thread(spi->irq); + if (ret) { + pr_err("mc13892 pmic driver init: \ + fail to start event thread\n"); + kfree(spi_get_drvdata(spi)); + spi_set_drvdata(spi, NULL); + return PMIC_ERROR; + } + + /* Set and install PMIC IRQ handler */ + set_irq_type(spi->irq, IRQF_TRIGGER_HIGH); + ret = request_irq(spi->irq, pmic_irq_handler, 0, "PMIC_IRQ", 0); + if (ret) { + kfree(spi_get_drvdata(spi)); + spi_set_drvdata(spi, NULL); + dev_err((struct device *)spi, "gpio1: irq%d error.", spi->irq); + return ret; + } + + enable_irq_wake(spi->irq); + + if (plat_data && plat_data->init) { + ret = plat_data->init(spi_get_drvdata(spi)); + if (ret != 0) { + kfree(spi_get_drvdata(spi)); + spi_set_drvdata(spi, NULL); + return PMIC_ERROR; + } + } + + power_ldm.dev.platform_data = spi->dev.platform_data; + + pmic_pdev_register(); + + printk(KERN_INFO "Device %s probed\n", dev_name(&spi->dev)); + + return PMIC_SUCCESS; +} + +/*! + * This function is called whenever the SPI slave device is removed. + * + * @param spi the SPI slave device + * + * @return Returns 0 on SUCCESS and error on FAILURE. + */ +static int __devexit pmic_remove(struct spi_device *spi) +{ + pmic_stop_event_thread(); + free_irq(spi->irq, 0); + + pmic_pdev_unregister(); + + printk(KERN_INFO "Device %s removed\n", dev_name(&spi->dev)); + + return PMIC_SUCCESS; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct spi_driver pmic_driver = { + .driver = { + .name = "pmic_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = pmic_probe, + .remove = __devexit_p(pmic_remove), + .suspend = pmic_suspend, + .resume = pmic_resume, +}; + +/* + * Initialization and Exit + */ + +/*! + * This function implements the init function of the PMIC device. + * This function is called when the module is loaded. It registers + * the PMIC Protocol driver. + * + * @return This function returns 0. + */ +static int __init pmic_init(void) +{ + return spi_register_driver(&pmic_driver); +} + +/*! + * This function implements the exit function of the PMIC device. + * This function is called when the module is unloaded. It unregisters + * the PMIC Protocol driver. + * + */ +static void __exit pmic_exit(void) +{ + pr_debug("Unregistering the PMIC Protocol Driver\n"); + spi_unregister_driver(&pmic_driver); +} + +/* + * Module entry points + */ +subsys_initcall_sync(pmic_init); +module_exit(pmic_exit); + +MODULE_DESCRIPTION("Core/Protocol driver for PMIC"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/mxc/pmic/core/pmic_event.c b/drivers/mxc/pmic/core/pmic_event.c new file mode 100644 index 000000000000..c8ffc63a4973 --- /dev/null +++ b/drivers/mxc/pmic/core/pmic_event.c @@ -0,0 +1,236 @@ +/* + * Copyright 2004-2010 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 pmic_event.c + * @brief This file manage all event of PMIC component. + * + * It contains event subscription, unsubscription and callback + * launch methods implemeted. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> +#include "pmic.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 DEFINE_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]); + } + + mutex_init(&event_mutex); + return; +} + +/*! + * 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 (mutex_lock_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); + mutex_unlock(&event_mutex); + return PMIC_ERROR; + } + } + + /* Add this entry to the event list */ + list_add_tail(&new->list, &pmic_events[event]); + + /* Release the lock */ + mutex_unlock(&event_mutex); + + return PMIC_SUCCESS; +} + +/*! + * 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 (mutex_lock_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 */ + mutex_unlock(&event_mutex); + + return ret; +} + +/*! + * 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 (mutex_lock_interruptible(&event_mutex)) { + return; + } + + if (list_empty(&pmic_events[event])) { + pr_debug("PMIC Event:%d detected. No callback subscribed\n", + event); + mutex_unlock(&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 */ + mutex_unlock(&event_mutex); + + return; + +} + +EXPORT_SYMBOL(pmic_event_subscribe); +EXPORT_SYMBOL(pmic_event_unsubscribe); diff --git a/drivers/mxc/pmic/core/pmic_external.c b/drivers/mxc/pmic/core/pmic_external.c new file mode 100644 index 000000000000..02318ed959bb --- /dev/null +++ b/drivers/mxc/pmic/core/pmic_external.c @@ -0,0 +1,100 @@ +/* + * Copyright 2004-2010 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 pmic_external.c + * @brief This file contains all external functions of PMIC drivers. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/init.h> +#include <linux/errno.h> + +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> + +/* + * External Functions + */ +extern int pmic_read(int reg_num, unsigned int *reg_val); +extern int pmic_write(int reg_num, const unsigned int reg_val); + +/*! + * This function is called by PMIC clients to read a register on PMIC. + * + * @param reg number of register + * @param reg_value return value of register + * @param reg_mask Bitmap mask indicating which bits to modify + * + * @return This function returns PMIC_SUCCESS if successful. + */ +PMIC_STATUS pmic_read_reg(int reg, unsigned int *reg_value, + unsigned int reg_mask) +{ + int ret = 0; + unsigned int temp = 0; + + ret = pmic_read(reg, &temp); + if (ret != PMIC_SUCCESS) { + return PMIC_ERROR; + } + *reg_value = (temp & reg_mask); + + pr_debug("Read REG[ %d ] = 0x%x\n", reg, *reg_value); + + return ret; +} + +/*! + * This function is called by PMIC clients to write a register on PMIC. + * + * @param reg number of register + * @param reg_value New value of register + * @param reg_mask Bitmap mask indicating which bits to modify + * + * @return This function returns PMIC_SUCCESS if successful. + */ +PMIC_STATUS pmic_write_reg(int reg, unsigned int reg_value, + unsigned int reg_mask) +{ + int ret = 0; + unsigned int temp = 0; + + ret = pmic_read(reg, &temp); + if (ret != PMIC_SUCCESS) { + return PMIC_ERROR; + } + temp = (temp & (~reg_mask)) | reg_value; +#ifdef CONFIG_MXC_PMIC_MC13783 + if (reg == REG_POWER_MISCELLANEOUS) + temp &= 0xFFFE7FFF; +#endif + ret = pmic_write(reg, temp); + if (ret != PMIC_SUCCESS) { + return PMIC_ERROR; + } + + pr_debug("Write REG[ %d ] = 0x%x\n", reg, reg_value); + + return ret; +} + +EXPORT_SYMBOL(pmic_read_reg); +EXPORT_SYMBOL(pmic_write_reg); |