aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-24 20:18:58 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-24 20:18:58 -0500
commita26033a1f56b7b1f8a56050c0a9095694aecae11 (patch)
tree8bb03cc2c30770f1e795ea9f7ebeceb6a6ba3fff /drivers/iio
parent5d01410fe4d92081f349b013a2e7a95429e4f2c9 (diff)
parent8019f6962708985782b65bd97be88046a55e1e4d (diff)
Merge branch 'ib-mfd-iio-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into acpi-pmic
Pull MFD changes that the ACPI PMIC changes depend on from Lee Jones.
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/adc/Kconfig8
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/axp288_adc.c261
3 files changed, 270 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 88bdc8f612e2..bc4e787096e8 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -127,6 +127,14 @@ config AT91_ADC
127 help 127 help
128 Say yes here to build support for Atmel AT91 ADC. 128 Say yes here to build support for Atmel AT91 ADC.
129 129
130config AXP288_ADC
131 tristate "X-Powers AXP288 ADC driver"
132 depends on MFD_AXP20X
133 help
134 Say yes here to have support for X-Powers power management IC (PMIC) ADC
135 device. Depending on platform configuration, this general purpose ADC can
136 be used for sampling sensors such as thermal resistors.
137
130config EXYNOS_ADC 138config EXYNOS_ADC
131 tristate "Exynos ADC driver support" 139 tristate "Exynos ADC driver support"
132 depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) 140 depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index cb88a6a23b8f..f30093f5b67a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
14obj-$(CONFIG_AD7887) += ad7887.o 14obj-$(CONFIG_AD7887) += ad7887.o
15obj-$(CONFIG_AD799X) += ad799x.o 15obj-$(CONFIG_AD799X) += ad799x.o
16obj-$(CONFIG_AT91_ADC) += at91_adc.o 16obj-$(CONFIG_AT91_ADC) += at91_adc.o
17obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
17obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o 18obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
18obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o 19obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
19obj-$(CONFIG_MAX1027) += max1027.o 20obj-$(CONFIG_MAX1027) += max1027.o
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
new file mode 100644
index 000000000000..08bcfb061ca5
--- /dev/null
+++ b/drivers/iio/adc/axp288_adc.c
@@ -0,0 +1,261 @@
1/*
2 * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
3 *
4 * Copyright (C) 2014 Intel Corporation
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/device.h>
22#include <linux/regmap.h>
23#include <linux/mfd/axp20x.h>
24#include <linux/platform_device.h>
25
26#include <linux/iio/iio.h>
27#include <linux/iio/machine.h>
28#include <linux/iio/driver.h>
29
30#define AXP288_ADC_EN_MASK 0xF1
31#define AXP288_ADC_TS_PIN_GPADC 0xF2
32#define AXP288_ADC_TS_PIN_ON 0xF3
33
34enum axp288_adc_id {
35 AXP288_ADC_TS,
36 AXP288_ADC_PMIC,
37 AXP288_ADC_GP,
38 AXP288_ADC_BATT_CHRG_I,
39 AXP288_ADC_BATT_DISCHRG_I,
40 AXP288_ADC_BATT_V,
41 AXP288_ADC_NR_CHAN,
42};
43
44struct axp288_adc_info {
45 int irq;
46 struct regmap *regmap;
47};
48
49static const struct iio_chan_spec const axp288_adc_channels[] = {
50 {
51 .indexed = 1,
52 .type = IIO_TEMP,
53 .channel = 0,
54 .address = AXP288_TS_ADC_H,
55 .datasheet_name = "TS_PIN",
56 }, {
57 .indexed = 1,
58 .type = IIO_TEMP,
59 .channel = 1,
60 .address = AXP288_PMIC_ADC_H,
61 .datasheet_name = "PMIC_TEMP",
62 }, {
63 .indexed = 1,
64 .type = IIO_TEMP,
65 .channel = 2,
66 .address = AXP288_GP_ADC_H,
67 .datasheet_name = "GPADC",
68 }, {
69 .indexed = 1,
70 .type = IIO_CURRENT,
71 .channel = 3,
72 .address = AXP20X_BATT_CHRG_I_H,
73 .datasheet_name = "BATT_CHG_I",
74 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
75 }, {
76 .indexed = 1,
77 .type = IIO_CURRENT,
78 .channel = 4,
79 .address = AXP20X_BATT_DISCHRG_I_H,
80 .datasheet_name = "BATT_DISCHRG_I",
81 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
82 }, {
83 .indexed = 1,
84 .type = IIO_VOLTAGE,
85 .channel = 5,
86 .address = AXP20X_BATT_V_H,
87 .datasheet_name = "BATT_V",
88 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
89 },
90};
91
92#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \
93 _consumer_channel) \
94 { \
95 .adc_channel_label = _adc_channel_label, \
96 .consumer_dev_name = _consumer_dev_name, \
97 .consumer_channel = _consumer_channel, \
98 }
99
100/* for consumer drivers */
101static struct iio_map axp288_adc_default_maps[] = {
102 AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
103 AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
104 AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
105 AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
106 AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
107 AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
108 {},
109};
110
111static int axp288_adc_read_channel(int *val, unsigned long address,
112 struct regmap *regmap)
113{
114 u8 buf[2];
115
116 if (regmap_bulk_read(regmap, address, buf, 2))
117 return -EIO;
118 *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
119
120 return IIO_VAL_INT;
121}
122
123static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
124 unsigned long address)
125{
126 /* channels other than GPADC do not need to switch TS pin */
127 if (address != AXP288_GP_ADC_H)
128 return 0;
129
130 return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
131}
132
133static int axp288_adc_read_raw(struct iio_dev *indio_dev,
134 struct iio_chan_spec const *chan,
135 int *val, int *val2, long mask)
136{
137 int ret;
138 struct axp288_adc_info *info = iio_priv(indio_dev);
139
140 mutex_lock(&indio_dev->mlock);
141 switch (mask) {
142 case IIO_CHAN_INFO_RAW:
143 if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
144 chan->address)) {
145 dev_err(&indio_dev->dev, "GPADC mode\n");
146 ret = -EINVAL;
147 break;
148 }
149 ret = axp288_adc_read_channel(val, chan->address, info->regmap);
150 if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
151 chan->address))
152 dev_err(&indio_dev->dev, "TS pin restore\n");
153 break;
154 case IIO_CHAN_INFO_PROCESSED:
155 ret = axp288_adc_read_channel(val, chan->address, info->regmap);
156 break;
157 default:
158 ret = -EINVAL;
159 }
160 mutex_unlock(&indio_dev->mlock);
161
162 return ret;
163}
164
165static int axp288_adc_set_state(struct regmap *regmap)
166{
167 /* ADC should be always enabled for internal FG to function */
168 if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
169 return -EIO;
170
171 return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
172}
173
174static const struct iio_info axp288_adc_iio_info = {
175 .read_raw = &axp288_adc_read_raw,
176 .driver_module = THIS_MODULE,
177};
178
179static int axp288_adc_probe(struct platform_device *pdev)
180{
181 int ret;
182 struct axp288_adc_info *info;
183 struct iio_dev *indio_dev;
184 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
185
186 indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
187 if (!indio_dev)
188 return -ENOMEM;
189
190 info = iio_priv(indio_dev);
191 info->irq = platform_get_irq(pdev, 0);
192 if (info->irq < 0) {
193 dev_err(&pdev->dev, "no irq resource?\n");
194 return info->irq;
195 }
196 platform_set_drvdata(pdev, indio_dev);
197 info->regmap = axp20x->regmap;
198 /*
199 * Set ADC to enabled state at all time, including system suspend.
200 * otherwise internal fuel gauge functionality may be affected.
201 */
202 ret = axp288_adc_set_state(axp20x->regmap);
203 if (ret) {
204 dev_err(&pdev->dev, "unable to enable ADC device\n");
205 return ret;
206 }
207
208 indio_dev->dev.parent = &pdev->dev;
209 indio_dev->name = pdev->name;
210 indio_dev->channels = axp288_adc_channels;
211 indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
212 indio_dev->info = &axp288_adc_iio_info;
213 indio_dev->modes = INDIO_DIRECT_MODE;
214 ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
215 if (ret < 0)
216 return ret;
217
218 ret = iio_device_register(indio_dev);
219 if (ret < 0) {
220 dev_err(&pdev->dev, "unable to register iio device\n");
221 goto err_array_unregister;
222 }
223 return 0;
224
225err_array_unregister:
226 iio_map_array_unregister(indio_dev);
227
228 return ret;
229}
230
231static int axp288_adc_remove(struct platform_device *pdev)
232{
233 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
234
235 iio_device_unregister(indio_dev);
236 iio_map_array_unregister(indio_dev);
237
238 return 0;
239}
240
241static struct platform_device_id axp288_adc_id_table[] = {
242 { .name = "axp288_adc" },
243 {},
244};
245
246static struct platform_driver axp288_adc_driver = {
247 .probe = axp288_adc_probe,
248 .remove = axp288_adc_remove,
249 .id_table = axp288_adc_id_table,
250 .driver = {
251 .name = "axp288_adc",
252 },
253};
254
255MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
256
257module_platform_driver(axp288_adc_driver);
258
259MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
260MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
261MODULE_LICENSE("GPL");