diff options
-rw-r--r-- | drivers/mfd/ab8500-gpadc.c | 115 | ||||
-rw-r--r-- | include/linux/mfd/ab8500/ab8500-gpadc.h | 6 |
2 files changed, 75 insertions, 46 deletions
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index f60f71f4b473..178cbc55caea 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * License Terms: GNU General Public License v2 | 4 | * License Terms: GNU General Public License v2 |
5 | * Author: Arun R Murthy <arun.murthy@stericsson.com> | 5 | * Author: Arun R Murthy <arun.murthy@stericsson.com> |
6 | * Author: Daniel Willerud <daniel.willerud@stericsson.com> | ||
6 | */ | 7 | */ |
7 | #include <linux/init.h> | 8 | #include <linux/init.h> |
8 | #include <linux/module.h> | 9 | #include <linux/module.h> |
@@ -15,6 +16,7 @@ | |||
15 | #include <linux/regulator/consumer.h> | 16 | #include <linux/regulator/consumer.h> |
16 | #include <linux/err.h> | 17 | #include <linux/err.h> |
17 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/list.h> | ||
18 | #include <linux/mfd/ab8500.h> | 20 | #include <linux/mfd/ab8500.h> |
19 | #include <linux/mfd/abx500.h> | 21 | #include <linux/mfd/abx500.h> |
20 | #include <linux/mfd/ab8500/ab8500-gpadc.h> | 22 | #include <linux/mfd/ab8500/ab8500-gpadc.h> |
@@ -48,21 +50,41 @@ | |||
48 | /** | 50 | /** |
49 | * struct ab8500_gpadc - ab8500 GPADC device information | 51 | * struct ab8500_gpadc - ab8500 GPADC device information |
50 | * @dev: pointer to the struct device | 52 | * @dev: pointer to the struct device |
51 | * @parent: pointer to the parent device structure ab8500 | 53 | * @node: a list of AB8500 GPADCs, hence prepared for |
54 | reentrance | ||
52 | * @ab8500_gpadc_complete: pointer to the struct completion, to indicate | 55 | * @ab8500_gpadc_complete: pointer to the struct completion, to indicate |
53 | * the completion of gpadc conversion | 56 | * the completion of gpadc conversion |
54 | * @ab8500_gpadc_lock: structure of type mutex | 57 | * @ab8500_gpadc_lock: structure of type mutex |
55 | * @regu: pointer to the struct regulator | 58 | * @regu: pointer to the struct regulator |
56 | * @irq: interrupt number that is used by gpadc | 59 | * @irq: interrupt number that is used by gpadc |
57 | */ | 60 | */ |
58 | static struct ab8500_gpadc { | 61 | struct ab8500_gpadc { |
59 | struct device *dev; | 62 | struct device *dev; |
60 | struct ab8500 *parent; | 63 | struct list_head node; |
61 | struct completion ab8500_gpadc_complete; | 64 | struct completion ab8500_gpadc_complete; |
62 | struct mutex ab8500_gpadc_lock; | 65 | struct mutex ab8500_gpadc_lock; |
63 | struct regulator *regu; | 66 | struct regulator *regu; |
64 | int irq; | 67 | int irq; |
65 | } *di; | 68 | }; |
69 | |||
70 | static LIST_HEAD(ab8500_gpadc_list); | ||
71 | |||
72 | /** | ||
73 | * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC | ||
74 | * (i.e. the first GPADC in the instance list) | ||
75 | */ | ||
76 | struct ab8500_gpadc *ab8500_gpadc_get(char *name) | ||
77 | { | ||
78 | struct ab8500_gpadc *gpadc; | ||
79 | |||
80 | list_for_each_entry(gpadc, &ab8500_gpadc_list, node) { | ||
81 | if (!strcmp(name, dev_name(gpadc->dev))) | ||
82 | return gpadc; | ||
83 | } | ||
84 | |||
85 | return ERR_PTR(-ENOENT); | ||
86 | } | ||
87 | EXPORT_SYMBOL(ab8500_gpadc_get); | ||
66 | 88 | ||
67 | /** | 89 | /** |
68 | * ab8500_gpadc_convert() - gpadc conversion | 90 | * ab8500_gpadc_convert() - gpadc conversion |
@@ -72,24 +94,24 @@ static struct ab8500_gpadc { | |||
72 | * data. Thereafter calibration has to be made to obtain the | 94 | * data. Thereafter calibration has to be made to obtain the |
73 | * data in the required quantity measurement. | 95 | * data in the required quantity measurement. |
74 | */ | 96 | */ |
75 | int ab8500_gpadc_convert(u8 input) | 97 | int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) |
76 | { | 98 | { |
77 | int ret; | 99 | int ret; |
78 | u16 data = 0; | 100 | u16 data = 0; |
79 | int looplimit = 0; | 101 | int looplimit = 0; |
80 | u8 val, low_data, high_data; | 102 | u8 val, low_data, high_data; |
81 | 103 | ||
82 | if (!di) | 104 | if (!gpadc) |
83 | return -ENODEV; | 105 | return -ENODEV; |
84 | 106 | ||
85 | mutex_lock(&di->ab8500_gpadc_lock); | 107 | mutex_lock(&gpadc->ab8500_gpadc_lock); |
86 | /* Enable VTVout LDO this is required for GPADC */ | 108 | /* Enable VTVout LDO this is required for GPADC */ |
87 | regulator_enable(di->regu); | 109 | regulator_enable(gpadc->regu); |
88 | 110 | ||
89 | /* Check if ADC is not busy, lock and proceed */ | 111 | /* Check if ADC is not busy, lock and proceed */ |
90 | do { | 112 | do { |
91 | ret = abx500_get_register_interruptible(di->dev, AB8500_GPADC, | 113 | ret = abx500_get_register_interruptible(gpadc->dev, |
92 | AB8500_GPADC_STAT_REG, &val); | 114 | AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); |
93 | if (ret < 0) | 115 | if (ret < 0) |
94 | goto out; | 116 | goto out; |
95 | if (!(val & GPADC_BUSY)) | 117 | if (!(val & GPADC_BUSY)) |
@@ -97,75 +119,76 @@ int ab8500_gpadc_convert(u8 input) | |||
97 | msleep(10); | 119 | msleep(10); |
98 | } while (++looplimit < 10); | 120 | } while (++looplimit < 10); |
99 | if (looplimit >= 10 && (val & GPADC_BUSY)) { | 121 | if (looplimit >= 10 && (val & GPADC_BUSY)) { |
100 | dev_err(di->dev, "gpadc_conversion: GPADC busy"); | 122 | dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); |
101 | ret = -EINVAL; | 123 | ret = -EINVAL; |
102 | goto out; | 124 | goto out; |
103 | } | 125 | } |
104 | 126 | ||
105 | /* Enable GPADC */ | 127 | /* Enable GPADC */ |
106 | ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GPADC, | 128 | ret = abx500_mask_and_set_register_interruptible(gpadc->dev, |
107 | AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC); | 129 | AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC); |
108 | if (ret < 0) { | 130 | if (ret < 0) { |
109 | dev_err(di->dev, "gpadc_conversion: enable gpadc failed\n"); | 131 | dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n"); |
110 | goto out; | 132 | goto out; |
111 | } | 133 | } |
112 | /* Select the input source and set average samples to 16 */ | 134 | /* Select the input source and set average samples to 16 */ |
113 | ret = abx500_set_register_interruptible(di->dev, AB8500_GPADC, | 135 | ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, |
114 | AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); | 136 | AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); |
115 | if (ret < 0) { | 137 | if (ret < 0) { |
116 | dev_err(di->dev, | 138 | dev_err(gpadc->dev, |
117 | "gpadc_conversion: set avg samples failed\n"); | 139 | "gpadc_conversion: set avg samples failed\n"); |
118 | goto out; | 140 | goto out; |
119 | } | 141 | } |
120 | /* Enable ADC, Buffering and select rising edge, start Conversion */ | 142 | /* Enable ADC, Buffering and select rising edge, start Conversion */ |
121 | ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GPADC, | 143 | ret = abx500_mask_and_set_register_interruptible(gpadc->dev, |
122 | AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); | 144 | AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); |
123 | if (ret < 0) { | 145 | if (ret < 0) { |
124 | dev_err(di->dev, | 146 | dev_err(gpadc->dev, |
125 | "gpadc_conversion: select falling edge failed\n"); | 147 | "gpadc_conversion: select falling edge failed\n"); |
126 | goto out; | 148 | goto out; |
127 | } | 149 | } |
128 | ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GPADC, | 150 | ret = abx500_mask_and_set_register_interruptible(gpadc->dev, |
129 | AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); | 151 | AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); |
130 | if (ret < 0) { | 152 | if (ret < 0) { |
131 | dev_err(di->dev, | 153 | dev_err(gpadc->dev, |
132 | "gpadc_conversion: start s/w conversion failed\n"); | 154 | "gpadc_conversion: start s/w conversion failed\n"); |
133 | goto out; | 155 | goto out; |
134 | } | 156 | } |
135 | /* wait for completion of conversion */ | 157 | /* wait for completion of conversion */ |
136 | if (!wait_for_completion_timeout(&di->ab8500_gpadc_complete, 2*HZ)) { | 158 | if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) { |
137 | dev_err(di->dev, | 159 | dev_err(gpadc->dev, |
138 | "timeout: didnt recieve GPADC conversion interrupt\n"); | 160 | "timeout: didnt recieve GPADC conversion interrupt\n"); |
139 | ret = -EINVAL; | 161 | ret = -EINVAL; |
140 | goto out; | 162 | goto out; |
141 | } | 163 | } |
142 | 164 | ||
143 | /* Read the converted RAW data */ | 165 | /* Read the converted RAW data */ |
144 | ret = abx500_get_register_interruptible(di->dev, AB8500_GPADC, | 166 | ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, |
145 | AB8500_GPADC_MANDATAL_REG, &low_data); | 167 | AB8500_GPADC_MANDATAL_REG, &low_data); |
146 | if (ret < 0) { | 168 | if (ret < 0) { |
147 | dev_err(di->dev, "gpadc_conversion: read low data failed\n"); | 169 | dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); |
148 | goto out; | 170 | goto out; |
149 | } | 171 | } |
150 | 172 | ||
151 | ret = abx500_get_register_interruptible(di->dev, AB8500_GPADC, | 173 | ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, |
152 | AB8500_GPADC_MANDATAH_REG, &high_data); | 174 | AB8500_GPADC_MANDATAH_REG, &high_data); |
153 | if (ret < 0) { | 175 | if (ret < 0) { |
154 | dev_err(di->dev, "gpadc_conversion: read high data failed\n"); | 176 | dev_err(gpadc->dev, |
177 | "gpadc_conversion: read high data failed\n"); | ||
155 | goto out; | 178 | goto out; |
156 | } | 179 | } |
157 | 180 | ||
158 | data = (high_data << 8) | low_data; | 181 | data = (high_data << 8) | low_data; |
159 | /* Disable GPADC */ | 182 | /* Disable GPADC */ |
160 | ret = abx500_set_register_interruptible(di->dev, AB8500_GPADC, | 183 | ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, |
161 | AB8500_GPADC_CTRL1_REG, DIS_GPADC); | 184 | AB8500_GPADC_CTRL1_REG, DIS_GPADC); |
162 | if (ret < 0) { | 185 | if (ret < 0) { |
163 | dev_err(di->dev, "gpadc_conversion: disable gpadc failed\n"); | 186 | dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); |
164 | goto out; | 187 | goto out; |
165 | } | 188 | } |
166 | /* Disable VTVout LDO this is required for GPADC */ | 189 | /* Disable VTVout LDO this is required for GPADC */ |
167 | regulator_disable(di->regu); | 190 | regulator_disable(gpadc->regu); |
168 | mutex_unlock(&di->ab8500_gpadc_lock); | 191 | mutex_unlock(&gpadc->ab8500_gpadc_lock); |
169 | return data; | 192 | return data; |
170 | 193 | ||
171 | out: | 194 | out: |
@@ -175,12 +198,12 @@ out: | |||
175 | * GPADC status register to go low. In V1.1 there wait_for_completion | 198 | * GPADC status register to go low. In V1.1 there wait_for_completion |
176 | * seems to timeout when waiting for an interrupt.. Not seen in V2.0 | 199 | * seems to timeout when waiting for an interrupt.. Not seen in V2.0 |
177 | */ | 200 | */ |
178 | (void) abx500_set_register_interruptible(di->dev, AB8500_GPADC, | 201 | (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, |
179 | AB8500_GPADC_CTRL1_REG, DIS_GPADC); | 202 | AB8500_GPADC_CTRL1_REG, DIS_GPADC); |
180 | regulator_disable(di->regu); | 203 | regulator_disable(gpadc->regu); |
181 | mutex_unlock(&di->ab8500_gpadc_lock); | 204 | mutex_unlock(&gpadc->ab8500_gpadc_lock); |
182 | dev_err(di->dev, "gpadc_conversion: Failed to AD convert channel %d\n", | 205 | dev_err(gpadc->dev, |
183 | input); | 206 | "gpadc_conversion: Failed to AD convert channel %d\n", input); |
184 | return ret; | 207 | return ret; |
185 | } | 208 | } |
186 | EXPORT_SYMBOL(ab8500_gpadc_convert); | 209 | EXPORT_SYMBOL(ab8500_gpadc_convert); |
@@ -195,9 +218,9 @@ EXPORT_SYMBOL(ab8500_gpadc_convert); | |||
195 | * can be read from the registers. | 218 | * can be read from the registers. |
196 | * Returns IRQ status(IRQ_HANDLED) | 219 | * Returns IRQ status(IRQ_HANDLED) |
197 | */ | 220 | */ |
198 | static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_di) | 221 | static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc) |
199 | { | 222 | { |
200 | struct ab8500_gpadc *gpadc = _di; | 223 | struct ab8500_gpadc *gpadc = _gpadc; |
201 | 224 | ||
202 | complete(&gpadc->ab8500_gpadc_complete); | 225 | complete(&gpadc->ab8500_gpadc_complete); |
203 | 226 | ||
@@ -215,16 +238,16 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) | |||
215 | return -ENOMEM; | 238 | return -ENOMEM; |
216 | } | 239 | } |
217 | 240 | ||
218 | gpadc->parent = dev_get_drvdata(pdev->dev.parent); | ||
219 | gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); | 241 | gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); |
220 | if (gpadc->irq < 0) { | 242 | if (gpadc->irq < 0) { |
221 | dev_err(gpadc->dev, "failed to get platform irq-%d\n", di->irq); | 243 | dev_err(gpadc->dev, "failed to get platform irq-%d\n", |
244 | gpadc->irq); | ||
222 | ret = gpadc->irq; | 245 | ret = gpadc->irq; |
223 | goto fail; | 246 | goto fail; |
224 | } | 247 | } |
225 | 248 | ||
226 | gpadc->dev = &pdev->dev; | 249 | gpadc->dev = &pdev->dev; |
227 | mutex_init(&di->ab8500_gpadc_lock); | 250 | mutex_init(&gpadc->ab8500_gpadc_lock); |
228 | 251 | ||
229 | /* Initialize completion used to notify completion of conversion */ | 252 | /* Initialize completion used to notify completion of conversion */ |
230 | init_completion(&gpadc->ab8500_gpadc_complete); | 253 | init_completion(&gpadc->ab8500_gpadc_complete); |
@@ -246,7 +269,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) | |||
246 | dev_err(gpadc->dev, "failed to get vtvout LDO\n"); | 269 | dev_err(gpadc->dev, "failed to get vtvout LDO\n"); |
247 | goto fail; | 270 | goto fail; |
248 | } | 271 | } |
249 | di = gpadc; | 272 | list_add_tail(&gpadc->node, &ab8500_gpadc_list); |
250 | dev_dbg(gpadc->dev, "probe success\n"); | 273 | dev_dbg(gpadc->dev, "probe success\n"); |
251 | return 0; | 274 | return 0; |
252 | fail: | 275 | fail: |
@@ -259,8 +282,10 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) | |||
259 | { | 282 | { |
260 | struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); | 283 | struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); |
261 | 284 | ||
285 | /* remove this gpadc entry from the list */ | ||
286 | list_del(&gpadc->node); | ||
262 | /* remove interrupt - completion of Sw ADC conversion */ | 287 | /* remove interrupt - completion of Sw ADC conversion */ |
263 | free_irq(gpadc->irq, di); | 288 | free_irq(gpadc->irq, gpadc); |
264 | /* disable VTVout LDO that is being used by GPADC */ | 289 | /* disable VTVout LDO that is being used by GPADC */ |
265 | regulator_put(gpadc->regu); | 290 | regulator_put(gpadc->regu); |
266 | kfree(gpadc); | 291 | kfree(gpadc); |
@@ -291,6 +316,6 @@ subsys_initcall_sync(ab8500_gpadc_init); | |||
291 | module_exit(ab8500_gpadc_exit); | 316 | module_exit(ab8500_gpadc_exit); |
292 | 317 | ||
293 | MODULE_LICENSE("GPL v2"); | 318 | MODULE_LICENSE("GPL v2"); |
294 | MODULE_AUTHOR("Arun R Murthy"); | 319 | MODULE_AUTHOR("Arun R Murthy, Daniel Willerud"); |
295 | MODULE_ALIAS("platform:ab8500_gpadc"); | 320 | MODULE_ALIAS("platform:ab8500_gpadc"); |
296 | MODULE_DESCRIPTION("AB8500 GPADC driver"); | 321 | MODULE_DESCRIPTION("AB8500 GPADC driver"); |
diff --git a/include/linux/mfd/ab8500/ab8500-gpadc.h b/include/linux/mfd/ab8500/ab8500-gpadc.h index 74b87dde9cd0..46b954011f16 100644 --- a/include/linux/mfd/ab8500/ab8500-gpadc.h +++ b/include/linux/mfd/ab8500/ab8500-gpadc.h | |||
@@ -3,6 +3,7 @@ | |||
3 | * Licensed under GPLv2. | 3 | * Licensed under GPLv2. |
4 | * | 4 | * |
5 | * Author: Arun R Murthy <arun.murthy@stericsson.com> | 5 | * Author: Arun R Murthy <arun.murthy@stericsson.com> |
6 | * Author: Daniel Willerud <daniel.willerud@stericsson.com> | ||
6 | */ | 7 | */ |
7 | 8 | ||
8 | #ifndef _AB8500_GPADC_H | 9 | #ifndef _AB8500_GPADC_H |
@@ -23,6 +24,9 @@ | |||
23 | #define BK_BAT_V 0x0C | 24 | #define BK_BAT_V 0x0C |
24 | #define DIE_TEMP 0x0D | 25 | #define DIE_TEMP 0x0D |
25 | 26 | ||
26 | int ab8500_gpadc_convert(u8 input); | 27 | struct ab8500_gpadc; |
28 | |||
29 | struct ab8500_gpadc *ab8500_gpadc_get(char *name); | ||
30 | int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input); | ||
27 | 31 | ||
28 | #endif /* _AB8500_GPADC_H */ | 32 | #endif /* _AB8500_GPADC_H */ |