diff options
author | Marty Fouts <mfouts@sta.samsung.com> | 2011-06-09 17:05:49 -0700 |
---|---|---|
committer | Todd Poynor <toddpoynor@google.com> | 2011-06-21 11:19:30 -0700 |
commit | a0bbc2c0c79014a0270088c695f08c27cd614e7e (patch) | |
tree | b346f693d3d7e0ef62dd9311d13f88787323503b | |
parent | 8318049bd0d942a97bc88eb2033a3b31307799bf (diff) | |
download | kernel_samsung_tuna-a0bbc2c0c79014a0270088c695f08c27cd614e7e.zip kernel_samsung_tuna-a0bbc2c0c79014a0270088c695f08c27cd614e7e.tar.gz kernel_samsung_tuna-a0bbc2c0c79014a0270088c695f08c27cd614e7e.tar.bz2 |
mfd: TWL6030: add ADC driver
This driver takes the TWL4030 ADC driver as a template and implements
the ability to fetch a result from any channel on the mux. It does
not implement the other features in the TWL4030 ADC driver.
Change-Id: If9150b2386e2c11754f40b1de7b28f1680a5730e
Signed-off-by: Marty Fouts <mfouts@sta.samsung.com>
-rw-r--r-- | drivers/mfd/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/twl6030-madc.c | 261 | ||||
-rw-r--r-- | include/linux/i2c/twl6030-madc.h | 86 |
4 files changed, 358 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5a067aa..8206646 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -237,6 +237,16 @@ config TWL6030_POWEROFF bool "TWL6030 device poweroff" depends on TWL4030_CORE +config TWL6030_MADC + tristate "Texas Instruments TWL6030 MADC" + depends on TWL4030_CORE + help + This driver provides support for TWL6030-MADC. The + driver supports both RT and SW conversion methods. + + This driver can be built as a module. If so it will be + named twl6030-madc + config TWL6040_CODEC bool depends on TWL4030_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0b3517a..5a4688f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o +obj-$(CONFIG_TWL6030_MADC) += twl6030-madc.o obj-$(CONFIG_TWL6040_CODEC) += twl6040-codec.o twl6040-irq.o obj-$(CONFIG_TWL6030_POWEROFF) += twl6030-poweroff.o diff --git a/drivers/mfd/twl6030-madc.c b/drivers/mfd/twl6030-madc.c new file mode 100644 index 0000000..b1dcf30 --- /dev/null +++ b/drivers/mfd/twl6030-madc.c @@ -0,0 +1,261 @@ +/* + * + * TWL6030 MADC module driver-This driver only implements the ADC read + * functions + * + * Copyright (C) 2011 Samsung Telecommunications of America + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen <mikko.k.ylinen@nokia.com> + * + * Amit Kucheria <amit.kucheria@canonical.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/i2c/twl.h> +#include <linux/i2c/twl6030-madc.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/err.h> + +#define GPADCS (1 << 1) +#define REG_TOGGLE1 0x90 + +/* + * struct twl6030_madc_data - a container for madc info + * @dev - pointer to device structure for madc + * @lock - mutex protecting this data structure + */ +struct twl6030_madc_data { + struct device *dev; + struct mutex lock; +}; + +static struct twl6030_madc_data *twl6030_madc; + +static inline int twl6030_madc_start_conversion(struct twl6030_madc_data *madc) +{ + int ret; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCS, REG_TOGGLE1); + if (ret) { + dev_err(madc->dev, "unable to write register 0x%X\n", + REG_TOGGLE1); + return ret; + } + + ret = twl_i2c_write_u8(TWL_MODULE_MADC, TWL6030_MADC_SP2, + TWL6030_MADC_CTRL_P2); + if (ret) { + dev_err(madc->dev, "unable to write register 0x%X\n", + TWL6030_MADC_CTRL_P2); + return ret; + } + return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl6030_madc_wait_conversion_ready(struct twl6030_madc_data *madc, + unsigned int timeout_ms, + u8 status_reg) +{ + unsigned long timeout; + int ret; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + do { + u8 reg; + + ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, ®, status_reg); + if (ret) { + dev_err(madc->dev, + "unable to read status register 0x%X\n", + status_reg); + return ret; + } + if (!(reg & TWL6030_MADC_BUSY) && (reg & TWL6030_MADC_EOCP1)) + return 0; + usleep_range(500, 2000); + } while (!time_after(jiffies, timeout)); + dev_err(madc->dev, "conversion timeout!\n"); + + return -EAGAIN; +} + +/* + * Function to read a particular channel value. + * @madc - pointer to struct twl6030_madc_data + * @reg - lsb of ADC Channel + * If the i2c read fails it returns an error else returns 0. + */ +static int twl6030_madc_channel_raw_read(struct twl6030_madc_data *madc, + u8 reg) +{ + u8 msb, lsb; + int ret; + u8 ctrl_p1; + + mutex_lock(&madc->lock); + ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &ctrl_p1, + TWL6030_MADC_CTRL_P1); + if (ret) { + dev_err(madc->dev, "unable to read control register."); + goto unlock; + } + + if ((ctrl_p1 & TWL6030_MADC_CTRL_P1) != TWL6030_MADC_CTRL_P1) { + ctrl_p1 |= TWL6030_MADC_SP1; + ret = twl_i2c_write_u8(TWL6030_MODULE_MADC, ctrl_p1, + TWL6030_MADC_CTRL_P1); + if (ret) { + dev_err(madc->dev, + "unable to write control register."); + goto unlock; + } + } + + (void)twl6030_madc_start_conversion(twl6030_madc); + ret = twl6030_madc_wait_conversion_ready(twl6030_madc, 5, + TWL6030_MADC_CTRL_P1); + if (ret) + goto unlock; + + /* + * For each ADC channel, we have MSB and LSB register + * pair. MSB address is always LSB address+1. reg parameter is + * the address of LSB register + */ + ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &msb, reg + 1); + if (ret) { + dev_err(madc->dev, "unable to read MSB register 0x%X\n", + reg + 1); + goto unlock; + } + ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &lsb, reg); + if (ret) { + dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg); + goto unlock; + } + ret = (int)((msb << 8) | lsb); +unlock: + mutex_unlock(&madc->lock); + return ret; +} + +/* + * Return channel value + * Or < 0 on failure. + */ +int twl6030_get_madc_conversion(int channel_no) +{ + u8 reg = TWL6030_MADC_GPCH0_LSB + (2 * channel_no); + if (!twl6030_madc) { + pr_err("%s: No ADC device\n", __func__); + return -EINVAL; + } + if (channel_no >= TWL6030_MADC_MAX_CHANNELS) { + dev_err(twl6030_madc->dev, + "%s: Channel number (%d) exceeds max (%d)\n", + __func__, channel_no, TWL6030_MADC_MAX_CHANNELS); + return -EINVAL; + } + + return twl6030_madc_channel_raw_read(twl6030_madc, reg); +} +EXPORT_SYMBOL_GPL(twl6030_get_madc_conversion); + +/* + * Initialize MADC + */ +static int __devinit twl6030_madc_probe(struct platform_device *pdev) +{ + struct twl6030_madc_data *madc; + struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + madc = kzalloc(sizeof(*madc), GFP_KERNEL); + if (!madc) + return -ENOMEM; + + platform_set_drvdata(pdev, madc); + mutex_init(&madc->lock); + twl6030_madc = madc; + return 0; +} + +static int __devexit twl6030_madc_remove(struct platform_device *pdev) +{ + struct twl6030_madc_data *madc = platform_get_drvdata(pdev); + + mutex_destroy(&madc->lock); + free_irq(platform_get_irq(pdev, 0), madc); + platform_set_drvdata(pdev, NULL); + twl6030_madc = NULL; + kfree(madc); + + return 0; +} + +static struct platform_driver twl6030_madc_driver = { + .probe = twl6030_madc_probe, + .remove = __exit_p(twl6030_madc_remove), + .driver = { + .name = "twl6030_madc", + .owner = THIS_MODULE, + }, +}; + +static int __init twl6030_madc_init(void) +{ + return platform_driver_register(&twl6030_madc_driver); +} + +module_init(twl6030_madc_init); + +static void __exit twl6030_madc_exit(void) +{ + platform_driver_unregister(&twl6030_madc_driver); +} + +module_exit(twl6030_madc_exit); + +MODULE_DESCRIPTION("TWL6030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl6030_madc"); diff --git a/include/linux/i2c/twl6030-madc.h b/include/linux/i2c/twl6030-madc.h new file mode 100644 index 0000000..81b9464 --- /dev/null +++ b/include/linux/i2c/twl6030-madc.h @@ -0,0 +1,86 @@ +/* + * twl6030_madc.h - Header for TWL6030 MADC + * + * Copyright (C) 2011 Samsung Telecommunications of America + * + * Based on twl4030-madc.h + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef _TWL6030_MADC_H +#define _TWL6030_MADC_H + +#define TWL6030_MADC_MAX_CHANNELS 17 +/* + * twl6030 madc occupies the same offset in the twl6030 map that + * twl4030 madc does in the twl4030 map. + * likewise the charger + */ +#define TWL6030_MODULE_MADC TWL4030_MODULE_MADC +#define TWL6030_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE + +#define TWL6030_MADC_CTRL 0x00 +#define TWL6030_MADC_TEMP1_EN (1 << 0) +#define TWL6030_MADC_TEMP2_EN (1 << 1) +#define TWL6030_MADC_SCALER_EN_CH2 (1 << 2) +#define TWL6030_MADC_VBAT_SCALER_DIV (1 << 3) +#define TWL6030_MADC_SCALER_EN_CH11 (1 << 4) +#define TWL6030_MADC_TMP1_EN_MONITOR (1 << 5) +#define TWL6030_MADC_TMP2_EN_MONITOR (1 << 6) +#define TWL6030_MADC_ISOURCE_EN (1 << 7) + +#define TWL6030_MADC_RTSELECT_LSB 0x02 +#define TWL6030_MADC_ADCIN0 (1 << 0) +#define TWL6030_MADC_ADCIN1 (1 << 1) +#define TWL6030_MADC_ADCIN2 (1 << 2) +#define TWL6030_MADC_ADCIN3 (1 << 3) +#define TWL6030_MADC_ADCIN4 (1 << 4) +#define TWL6030_MADC_ADCIN5 (1 << 5) +#define TWL6030_MADC_ADCIN6 (1 << 6) +#define TWL6030_MADC_ADCIN7 (1 << 7) + +#define TWL6030_MADC_RTSELECT_ISB 0x03 +#define TWL6030_MADC_ADCIN8 (1 << 0) +#define TWL6030_MADC_ADCIN9 (1 << 1) +#define TWL6030_MADC_ADCIN10 (1 << 2) +#define TWL6030_MADC_ADCIN11 (1 << 3) +#define TWL6030_MADC_ADCIN12 (1 << 4) +#define TWL6030_MADC_ADCIN13 (1 << 5) +#define TWL6030_MADC_ADCIN14 (1 << 6) +#define TWL6030_MADC_ADCIN15 (1 << 7) + +#define TWL6030_MADC_RTSELECT_MSB 0x04 +#define TWL6030_MADC_ADCIN16 (1 << 0) + +#define TWL6030_MADC_CTRL_P1 0x05 +#define TWL6030_MADC_BUSY (1 << 0) +#define TWL6030_MADC_EOCP1 (1 << 1) +#define TWL6030_MADC_EOCRT (1 << 2) +#define TWL6030_MADC_SP1 (1 << 3) + +#define TWL6030_MADC_CTRL_P2 0x06 +#define TWL6030_MADC_BUSYB (1 << 0) +#define TWL6030_MADC_EOCP2 (1 << 1) +#define TWL6030_MADC_SP2 (1 << 2) + +#define TWL6030_MADC_RTCH0_LSB 0x07 +#define TWL6030_MADC_GPCH0_LSB 0x29 + +int twl6030_get_madc_conversion(int channel_no); +#endif |