summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuotao Fu <l.fu@pengutronix.de>2009-07-20 10:31:20 +0200
committerLuotao Fu <l.fu@pengutronix.de>2009-07-28 15:36:52 +0200
commit195be5827eb519cb232e14ec96a15205d284300f (patch)
tree0db43aa1900ffae501b4832c9aca922bdca45899
parent16f62c9b7d91c52be9d72c74e9585c56e271641c (diff)
downloadlinux-2.6-195be5827eb519cb232e14ec96a15205d284300f.tar.gz
linux-2.6-195be5827eb519cb232e14ec96a15205d284300f.tar.xz
mc13783: add adc driver
The AD Converter Unit on pmic mc13783 supports also the touchscreen interface besides "normal" conversion. To add the ADC functionality, we have to change the touchscreen code also to avoid conflicts. Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
-rw-r--r--drivers/hwmon/Kconfig6
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/mc13783-adc.c314
-rw-r--r--drivers/input/touchscreen/Kconfig1
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c112
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/mc13783-core.c14
-rw-r--r--include/linux/mfd/mc13783-private.h12
-rw-r--r--include/linux/mfd/mc13783.h5
9 files changed, 405 insertions, 62 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d73f5f473e3..2463bd2cbb1 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1005,6 +1005,12 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
+config SENSORS_MC13783_ADC
+ tristate "Freescale MC13783 ADC"
+ depends on MFD_MC13783
+ help
+ Support for the ad converter on mc13783 pmic.
+
config HWMON_DEBUG_CHIP
bool "Hardware Monitoring Chip debugging messages"
default n
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 0ae26984ba4..300cd19b367 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -88,6 +88,7 @@ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
+obj-$(CONFIG_SENSORS_MC13783_ADC) += mc13783-adc.o
ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
new file mode 100644
index 00000000000..2f4d1890772
--- /dev/null
+++ b/drivers/hwmon/mc13783-adc.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/mc13783-private.h>
+
+#define MC13783_ADC_NAME "mc13783-adc"
+
+struct mc13783_adc_priv {
+ struct mc13783 *mc13783;
+ struct mutex adc_conv_lock;
+ unsigned int sample[4];
+ struct device *hwmon_dev;
+ unsigned int conv_mode;
+ unsigned int ts_active;
+};
+
+/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
+static void inline mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+{
+ unsigned int reg_adc0, reg_adc1;
+
+ reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+ | MC13783_ADC0_TSMOD0;
+ reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+}
+
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample)
+{
+ unsigned int reg_adc0, reg_adc1;
+ int i;
+ struct mc13783_adc_priv *priv = platform_get_drvdata(mc13783->adc);
+
+ mutex_lock(&priv->adc_conv_lock);
+
+ /* set up auto incrementing anyway to make quick read */
+ reg_adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+ /* enable the adc, ignore external triggering and set ASC to trigger
+ * conversion */
+ reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
+ | MC13783_ADC1_ASC;
+
+ /* setup channel number */
+ if (channel > 7)
+ reg_adc1 |= MC13783_ADC1_ADSEL;
+
+ switch (mode) {
+ case MC13783_ADC_MODE_TS:
+ /* enables touch screen reference mode and set touchscreen mode
+ * to position mode */
+ reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+ | MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
+ reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+ case MC13783_ADC_MODE_SINGLE_CHAN:
+ reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+ reg_adc1 |= MC13783_ADC1_RAND;
+ break;
+ case MC13783_ADC_MODE_MULT_CHAN:
+ reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+ default:
+ pr_err("Unknow ADC mode\n");
+ return -EINVAL;
+ }
+
+ pr_debug("writing reg0: 0x%x reg1: 0x%x\n", reg_adc0, reg_adc1);
+
+ mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_0, reg_adc0);
+ mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_1, reg_adc1);
+
+ wait_for_completion_interruptible(&mc13783->adc_done);
+
+ for (i = 0; i < 4; i++)
+ mc13783_reg_read(mc13783, MC13783_REG_ADC_2,
+ &sample[i]);
+
+ if (priv->ts_active)
+ mc13783_adc_set_ts_irq_mode(mc13783);
+
+ mutex_unlock(&priv->adc_conv_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13783_adc_do_conversion);
+
+void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(mc13783->adc);
+
+ priv->ts_active = status;
+}
+EXPORT_SYMBOL(mc13783_adc_set_ts_status);
+
+static ssize_t mc13783_adc_show_read_mode(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ unsigned int mode;
+ const char *mode_name;
+
+ mode = priv->conv_mode;
+
+ if (mode == MC13783_ADC_MODE_SINGLE_CHAN)
+ mode_name = "Single Channel Conversion, 8 X sample / channel";
+ if (mode == MC13783_ADC_MODE_MULT_CHAN)
+ mode_name = "Multi Channel Conversion 1 x sample / channel ";
+
+ return sprintf(buf, "Mode: %s\n", mode_name);
+}
+
+static ssize_t mc13783_adc_set_read_mode(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+
+ unsigned long mode = 0;
+
+ if (!strncmp(buf, "Single", 6) || !strncmp(buf, "single", 6))
+ mode = MC13783_ADC_MODE_SINGLE_CHAN;
+
+ if (!strncmp(buf, "Multi", 5) || !strncmp(buf, "multi", 5))
+ mode = MC13783_ADC_MODE_MULT_CHAN;
+
+ if (!mode) {
+ pr_err("unknown or reserved mode\n");
+ return -EINVAL;
+ }
+
+ priv->conv_mode = mode;
+
+ return count;
+}
+
+static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ return sprintf(buf, "mc13783_adc\n");
+}
+
+static ssize_t mc13783_adc_read(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ unsigned int channel = attr -> index;
+ unsigned int mode, res[8];
+ int i, c, res_index;
+
+ mode = priv->conv_mode;
+
+ mc13783_adc_do_conversion(priv->mc13783, mode, channel, priv->sample);
+
+ for (i = 0; i < 4; i++) {
+ res[i] = (priv->sample[i] >> 2) & 0x3ff;
+ res[i + 4] = (priv->sample[i] >> 14) & 0x3ff;
+ }
+
+ if (mode == MC13783_ADC_MODE_SINGLE_CHAN) {
+ for (i = 0; i < 8; i++) {
+ c += sprintf(buf, "%u%s", res[i], i < 7 ? ", " : "\n");
+ buf += c;
+ }
+ }
+
+ if (mode == MC13783_ADC_MODE_MULT_CHAN)
+ c = sprintf(res_print, "%u\n", res[channel & 0x7]);
+
+ return c;
+}
+
+static struct sensor_device_attribute mc13783_adc_ctl[] = {
+ SENSOR_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL, 0),
+ SENSOR_ATTR(read_mode, S_IWUSR | S_IRUGO,
+ mc13783_adc_show_read_mode, mc13783_adc_set_read_mode, 0),
+
+ SENSOR_ATTR(in0_input, S_IRUGO, mc13783_adc_read, NULL, 0),
+ SENSOR_ATTR(in1_input, S_IRUGO, mc13783_adc_read, NULL, 1),
+ SENSOR_ATTR(in2_input, S_IRUGO, mc13783_adc_read, NULL, 2),
+ SENSOR_ATTR(in3_input, S_IRUGO, mc13783_adc_read, NULL, 3),
+ SENSOR_ATTR(in4_input, S_IRUGO, mc13783_adc_read, NULL, 4),
+ SENSOR_ATTR(in5_input, S_IRUGO, mc13783_adc_read, NULL, 5),
+ SENSOR_ATTR(in6_input, S_IRUGO, mc13783_adc_read, NULL, 6),
+ SENSOR_ATTR(in7_input, S_IRUGO, mc13783_adc_read, NULL, 7),
+ SENSOR_ATTR(in8_input, S_IRUGO, mc13783_adc_read, NULL, 8),
+ SENSOR_ATTR(in9_input, S_IRUGO, mc13783_adc_read, NULL, 9),
+ SENSOR_ATTR(in10_input, S_IRUGO, mc13783_adc_read, NULL, 10),
+ SENSOR_ATTR(in11_input, S_IRUGO, mc13783_adc_read, NULL, 11),
+ SENSOR_ATTR(in12_input, S_IRUGO, mc13783_adc_read, NULL, 12),
+ SENSOR_ATTR(in13_input, S_IRUGO, mc13783_adc_read, NULL, 13),
+ SENSOR_ATTR(in14_input, S_IRUGO, mc13783_adc_read, NULL, 14),
+ SENSOR_ATTR(in15_input, S_IRUGO, mc13783_adc_read, NULL, 15),
+};
+
+static int __init mc13783_adc_probe(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv;
+ int ret, i;
+ int entries = sizeof(mc13783_adc_ctl)
+ / sizeof(struct sensor_device_attribute);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13783 = pdev->dev.platform_data;
+ priv->conv_mode = MC13783_ADC_MODE_MULT_CHAN;
+
+ for (i = 0; i < entries; i++) {
+ ret = device_create_file(&pdev->dev,
+ &mc13783_adc_ctl[i].dev_attr);
+ if (ret) {
+ dev_err(&pdev->dev, "device_create_file failed.\n");
+ goto out_err;
+ }
+ }
+
+ priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(priv->hwmon_dev)) {
+ dev_err(&pdev->dev, "hwmon_device_register failed.\n");
+ ret = PTR_ERR(priv->hwmon_dev);
+ goto out_err;
+ }
+
+ mutex_init(&priv->adc_conv_lock);
+
+ /* unmask adcdone interrupts */
+ mc13783_set_bits(priv->mc13783, MC13783_REG_INTERRUPT_MASK_0,
+ MC13783_INT_MASK_ADCDONEM, 0);
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+out_err:
+ for (i--; i >= 0; i--)
+ device_remove_file(&pdev->dev, &mc13783_adc_ctl[i].dev_attr);
+
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit mc13783_adc_remove(struct platform_device *pdev)
+{
+ struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
+ int entries = sizeof(mc13783_adc_ctl)
+ / sizeof(struct sensor_device_attribute);
+ int i;
+
+ hwmon_device_unregister(&pdev->dev);
+
+ for (i = 0; i < entries; i++)
+ device_remove_file(&pdev->dev, &mc13783_adc_ctl[i].dev_attr);
+
+ mutex_destroy(&priv->adc_conv_lock);
+
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_adc_driver = {
+ .probe = mc13783_adc_probe,
+ .remove = __devexit_p(mc13783_adc_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MC13783_ADC_NAME,
+ },
+};
+
+static int __init mc13783_adc_init(void)
+{
+ return platform_driver_register(&mc13783_adc_driver);
+}
+
+static void __exit mc13783_adc_exit(void)
+{
+ platform_driver_unregister(&mc13783_adc_driver);
+}
+
+module_init(mc13783_adc_init);
+module_exit(mc13783_adc_exit);
+
+MODULE_DESCRIPTION("MC13783 input touchscreen driver");
+MODULE_AUTHOR("Luotao Fu, <l.fu@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3a8c6bbb679..fe35b54711a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -392,6 +392,7 @@ config TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_MC13783
tristate "Freescale MC13783 touchscreen input driver"
depends on MFD_MC13783
+ depends on SENSORS_MC13783_ADC
help
Say Y here if you have an Freescale MC13783 PMIC on your
board and want to use its touchscreen
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index e25bd3f63de..c7690f6b8b2 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -22,91 +22,71 @@
#include <linux/input.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/mfd/mc13783-private.h>
#include <linux/delay.h>
+#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
-#define MC13783_NAME "mc13783-ts"
+#define MC13783_TS_NAME "mc13783-ts"
struct mc13783_ts_priv {
struct input_dev *idev;
struct mc13783 *mc13783;
struct delayed_work work;
+ struct workqueue_struct *workq;
+ unsigned int sample[4];
};
-static inline void mc13783_ts_start_conversion(struct mc13783_ts_priv *priv)
-{
- unsigned int reg_adc0, reg_adc1;
-
- reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
- | MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1
- | MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
- reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADSEL | MC13783_ADC1_ADA22
- | MC13783_ADC1_ASC | MC13783_ADC1_ADTRIGIGN;
-
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_0, reg_adc0);
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_1, reg_adc1);
-}
-
-static inline void mc13783_ts_set_irq_mode(struct mc13783_ts_priv *priv)
-{
- unsigned int reg_adc0, reg_adc1;
-
- reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
- | MC13783_ADC0_TSMOD0;
- reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
-
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_0, reg_adc0);
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_1, reg_adc1);
-}
-#define TS_MIN 80
+#define TS_MIN 1
#define TS_MAX 1000
static void mc13783_ts_handler(int irq, void *data)
{
struct mc13783_ts_priv *priv = data;
unsigned int sts;
- unsigned int sample[4];
- int i;
mc13783_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_STATUS_0, &sts);
mc13783_reg_write(priv->mc13783, MC13783_REG_INTERRUPT_STATUS_0,
- sts & 0xf);
+ sts & MC13783_INT_STAT_TSI);
- if (sts & 4)
- mc13783_ts_start_conversion(priv);
+ if (sts & MC13783_INT_STAT_TSI)
+ queue_work(priv->workq, &priv->work.work);
+}
- if (sts & 1) {
- int x, y, press = 0;
- for (i = 0; i < 4; i++)
- mc13783_reg_read(priv->mc13783, MC13783_REG_ADC_2,
- &sample[i]);
+static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
+{
+ int x, y, press = 0;
- x = (sample[2] >> 2) & 0x3ff;
- y = (sample[1] >> 14) & 0x3ff;
+ x = (priv->sample[2] >> 2) & 0x3ff;
+ y = (priv->sample[3] >> 2) & 0x3ff;
- pr_debug("mc13783_ts: x: %d y: %d\n", x, y);
+ pr_debug("mc13783_ts: x: %d y: %d\n", x, y);
- if (x > TS_MIN && x < TS_MAX && y > TS_MIN && y < TS_MAX) {
- press = 1;
- input_report_abs(priv->idev, ABS_X, x);
- input_report_abs(priv->idev, ABS_Y, y);
- schedule_delayed_work(&priv->work, HZ / 50);
- } else
- mc13783_ts_set_irq_mode(priv);
+ if (x > TS_MIN && x < TS_MAX && y > TS_MIN && y < TS_MAX) {
+ press = 1;
+ input_report_abs(priv->idev, ABS_X, x);
+ input_report_abs(priv->idev, ABS_Y, y);
- input_report_abs(priv->idev, ABS_PRESSURE, press);
- input_sync(priv->idev);
+ queue_delayed_work(priv->workq, &priv->work, HZ / 50);
}
+ input_report_abs(priv->idev, ABS_PRESSURE, press);
+ input_sync(priv->idev);
}
-static void mc13783_work(struct work_struct *work)
+static void mc13783_ts_work(struct work_struct *work)
{
struct mc13783_ts_priv *priv =
container_of(work, struct mc13783_ts_priv, work.work);
+ unsigned int mode = MC13783_ADC_MODE_TS;
+ unsigned int channel = 12;
+ int ret;
+
+ ret = mc13783_adc_do_conversion
+ (priv->mc13783, mode, channel, priv->sample);
- mc13783_ts_start_conversion(priv);
+ if (! ret)
+ mc13783_ts_report_sample(priv);
}
static int __init mc13783_ts_probe(struct platform_device *pdev)
@@ -128,7 +108,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
}
priv->idev = idev;
- idev->name = MC13783_NAME;
+ idev->name = MC13783_TS_NAME;
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
idev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
@@ -136,7 +116,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- ret = mc13783_register_irq(priv->mc13783, MC13783_IRQ_ADC,
+ ret = mc13783_register_irq(priv->mc13783, MC13783_IRQ_TS,
mc13783_ts_handler, priv);
if (ret)
goto err_failed_irq;
@@ -148,15 +128,21 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
goto err_failed_register;
}
- mc13783_set_bits(priv->mc13783, MC13783_REG_INTERRUPT_MASK_0, 0xf, 0);
+ /* unmask the ts wakeup interrupt */
+ mc13783_set_bits(priv->mc13783, MC13783_REG_INTERRUPT_MASK_0,
+ MC13783_INT_MASK_TSM, 0);
+
+ mc13783_adc_set_ts_status(priv->mc13783, 1);
- INIT_DELAYED_WORK(&priv->work, mc13783_work);
- schedule_delayed_work(&priv->work, HZ / 20);
+ INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
+
+ priv->workq = create_singlethread_workqueue("mc13783_ts");
+ queue_delayed_work(priv->workq, &priv->work, HZ / 20);
return 0;
err_failed_register:
- mc13783_free_irq(priv->mc13783, MC13783_IRQ_ADC);
+ mc13783_free_irq(priv->mc13783, MC13783_IRQ_TS);
err_failed_irq:
input_free_device(priv->idev);
err_input_alloc:
@@ -169,10 +155,16 @@ static int __devexit mc13783_ts_remove(struct platform_device *pdev)
{
struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
- mc13783_free_irq(priv->mc13783, MC13783_IRQ_ADC);
+ mc13783_adc_set_ts_status(priv->mc13783, 0);
+
+ mc13783_free_irq(priv->mc13783, MC13783_IRQ_TS);
+
cancel_delayed_work(&priv->work);
+ destroy_workqueue(priv->workq);
+
input_unregister_device(priv->idev);
input_free_device(priv->idev);
+
kfree(priv);
return 0;
@@ -183,7 +175,7 @@ static struct platform_driver mc13783_ts_driver = {
.remove = __devexit_p(mc13783_ts_remove),
.driver = {
.owner = THIS_MODULE,
- .name = MC13783_NAME,
+ .name = MC13783_TS_NAME,
},
};
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 30b7323a0d0..51fff8ebb5b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -42,4 +42,4 @@ obj-$(CONFIG_PMIC_DA903X) += da903x.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
-obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o \ No newline at end of file
+obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index 54e646b4abe..380c5ab9bc1 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/completion.h>
#include <linux/spi/spi.h>
#include <linux/mfd/mc13783.h>
#include <linux/mfd/mc13783-private.h>
@@ -168,6 +169,15 @@ static void mc13783_irq_work(struct work_struct *work)
{
struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
int i;
+ unsigned int adc_sts;
+
+ /* check if the adc has finished any completion */
+ mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
+ adc_sts & MC13783_INT_STAT_ADCDONEI);
+
+ if (adc_sts & MC13783_INT_STAT_ADCDONEI)
+ complete_all(&mc13783->adc_done);
for (i = 0; i < MC13783_NUM_IRQ; i++)
if (mc13783->irq_handler[i].handler)
@@ -261,6 +271,7 @@ static int __devinit mc13783_probe(struct spi_device *spi)
INIT_WORK(&mc13783->work, mc13783_irq_work);
mutex_init(&mc13783->io_lock);
+ init_completion(&mc13783->adc_done);
if (pdata && pdata->init)
pdata->init(mc13783);
@@ -286,6 +297,8 @@ static int __devinit mc13783_probe(struct spi_device *spi)
&mc13783->codec);
mc13783_client_dev_register(mc13783, "mc13783-ts",
&mc13783->ts);
+ mc13783_client_dev_register(mc13783, "mc13783-adc",
+ &mc13783->adc);
return 0;
@@ -305,6 +318,7 @@ static int __devexit mc13783_remove(struct spi_device *spi)
platform_device_unregister(mc13783->ts);
platform_device_unregister(mc13783->codec);
+ platform_device_unregister(mc13783->adc);
if (pdata && pdata->exit)
pdata->exit();
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
index 457ea32dd9c..b307ee82e81 100644
--- a/include/linux/mfd/mc13783-private.h
+++ b/include/linux/mfd/mc13783-private.h
@@ -33,9 +33,13 @@ struct mc13783_irq {
};
#define MC13783_NUM_IRQ 2
-#define MC13783_IRQ_ADC 0
+#define MC13783_IRQ_TS 0
#define MC13783_IRQ_REGULATOR 1
+#define MC13783_ADC_MODE_TS 1
+#define MC13783_ADC_MODE_SINGLE_CHAN 2
+#define MC13783_ADC_MODE_MULT_CHAN 3
+
struct mc13783 {
int revision;
struct device *dev;
@@ -51,9 +55,12 @@ struct mc13783 {
struct platform_device regulators[30];
struct platform_device *codec;
struct platform_device *ts;
+ struct platform_device *rtc;
+ struct platform_device *adc;
struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
struct work_struct work;
+ struct completion adc_done;
};
int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
@@ -325,6 +332,9 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
#define MC13783_ADC1_ADONESHOT (1 << 22)
#define MC13783_ADC1_ADCBIS1 (1 << 23)
+#define MC13783_ADC1_CHAN0_SHIFT 5
+#define MC13783_ADC1_CHAN1_SHIFT 8
+
#define MC13783_ADC2_ADD10 (1 << 2)
#define MC13783_ADC2_ADD11 (1 << 3)
#define MC13783_ADC2_ADD12 (1 << 4)
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index db4c0e8304c..968733383f1 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -11,6 +11,11 @@ struct mc13783_platform_data {
int mc13783_register_regulator(struct device *, int, struct regulator_init_data *);
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample);
+
+void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
+
#define SW_SW1A 0
#define SW_SW1B 1
#define SW_SW2A 2