]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
08c3e06a BR |
2 | /* NXP PCF50633 ADC Driver |
3 | * | |
4 | * (C) 2006-2008 by Openmoko, Inc. | |
5 | * Author: Balaji Rao <[email protected]> | |
6 | * All rights reserved. | |
7 | * | |
8 | * Broken down from monstrous PCF50633 driver mainly by | |
9 | * Harald Welte, Andy Green and Werner Almesberger | |
10 | * | |
08c3e06a BR |
11 | * NOTE: This driver does not yet support subtractive ADC mode, which means |
12 | * you can do only one measurement per read request. | |
13 | */ | |
14 | ||
15 | #include <linux/kernel.h> | |
5a0e3ad6 | 16 | #include <linux/slab.h> |
08c3e06a | 17 | #include <linux/module.h> |
08c3e06a BR |
18 | #include <linux/device.h> |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/completion.h> | |
21 | ||
22 | #include <linux/mfd/pcf50633/core.h> | |
23 | #include <linux/mfd/pcf50633/adc.h> | |
24 | ||
25 | struct pcf50633_adc_request { | |
26 | int mux; | |
27 | int avg; | |
08c3e06a BR |
28 | void (*callback)(struct pcf50633 *, void *, int); |
29 | void *callback_param; | |
6438a694 | 30 | }; |
08c3e06a | 31 | |
6438a694 LPC |
32 | struct pcf50633_adc_sync_request { |
33 | int result; | |
08c3e06a | 34 | struct completion completion; |
08c3e06a BR |
35 | }; |
36 | ||
37 | #define PCF50633_MAX_ADC_FIFO_DEPTH 8 | |
38 | ||
39 | struct pcf50633_adc { | |
40 | struct pcf50633 *pcf; | |
41 | ||
42 | /* Private stuff */ | |
43 | struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH]; | |
44 | int queue_head; | |
45 | int queue_tail; | |
46 | struct mutex queue_mutex; | |
47 | }; | |
48 | ||
49 | static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf) | |
50 | { | |
51 | return platform_get_drvdata(pcf->adc_pdev); | |
52 | } | |
53 | ||
54 | static void adc_setup(struct pcf50633 *pcf, int channel, int avg) | |
55 | { | |
56 | channel &= PCF50633_ADCC1_ADCMUX_MASK; | |
57 | ||
58 | /* kill ratiometric, but enable ACCSW biasing */ | |
59 | pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00); | |
60 | pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01); | |
61 | ||
62 | /* start ADC conversion on selected channel */ | |
63 | pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg | | |
64 | PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT); | |
65 | } | |
66 | ||
67 | static void trigger_next_adc_job_if_any(struct pcf50633 *pcf) | |
68 | { | |
69 | struct pcf50633_adc *adc = __to_adc(pcf); | |
70 | int head; | |
71 | ||
08c3e06a BR |
72 | head = adc->queue_head; |
73 | ||
bd8ef102 | 74 | if (!adc->queue[head]) |
08c3e06a | 75 | return; |
08c3e06a BR |
76 | |
77 | adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg); | |
78 | } | |
79 | ||
80 | static int | |
81 | adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) | |
82 | { | |
83 | struct pcf50633_adc *adc = __to_adc(pcf); | |
84 | int head, tail; | |
85 | ||
86 | mutex_lock(&adc->queue_mutex); | |
87 | ||
88 | head = adc->queue_head; | |
89 | tail = adc->queue_tail; | |
90 | ||
91 | if (adc->queue[tail]) { | |
92 | mutex_unlock(&adc->queue_mutex); | |
bd8ef102 | 93 | dev_err(pcf->dev, "ADC queue is full, dropping request\n"); |
08c3e06a BR |
94 | return -EBUSY; |
95 | } | |
96 | ||
97 | adc->queue[tail] = req; | |
bd8ef102 PF |
98 | if (head == tail) |
99 | trigger_next_adc_job_if_any(pcf); | |
08c3e06a BR |
100 | adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1); |
101 | ||
102 | mutex_unlock(&adc->queue_mutex); | |
103 | ||
08c3e06a BR |
104 | return 0; |
105 | } | |
106 | ||
6438a694 LPC |
107 | static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, |
108 | int result) | |
08c3e06a | 109 | { |
6438a694 | 110 | struct pcf50633_adc_sync_request *req = param; |
08c3e06a BR |
111 | |
112 | req->result = result; | |
113 | complete(&req->completion); | |
114 | } | |
115 | ||
116 | int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg) | |
117 | { | |
6438a694 LPC |
118 | struct pcf50633_adc_sync_request req; |
119 | int ret; | |
08c3e06a | 120 | |
6438a694 | 121 | init_completion(&req.completion); |
08c3e06a | 122 | |
6438a694 LPC |
123 | ret = pcf50633_adc_async_read(pcf, mux, avg, |
124 | pcf50633_adc_sync_read_callback, &req); | |
125 | if (ret) | |
126 | return ret; | |
bd8ef102 | 127 | |
6438a694 | 128 | wait_for_completion(&req.completion); |
08c3e06a | 129 | |
6438a694 | 130 | return req.result; |
08c3e06a BR |
131 | } |
132 | EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read); | |
133 | ||
134 | int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg, | |
135 | void (*callback)(struct pcf50633 *, void *, int), | |
136 | void *callback_param) | |
137 | { | |
138 | struct pcf50633_adc_request *req; | |
8b450dcf | 139 | int ret; |
08c3e06a BR |
140 | |
141 | /* req is freed when the result is ready, in interrupt handler */ | |
142 | req = kmalloc(sizeof(*req), GFP_KERNEL); | |
143 | if (!req) | |
144 | return -ENOMEM; | |
145 | ||
146 | req->mux = mux; | |
147 | req->avg = avg; | |
148 | req->callback = callback; | |
149 | req->callback_param = callback_param; | |
150 | ||
8b450dcf QL |
151 | ret = adc_enqueue_request(pcf, req); |
152 | if (ret) | |
153 | kfree(req); | |
154 | ||
155 | return ret; | |
08c3e06a BR |
156 | } |
157 | EXPORT_SYMBOL_GPL(pcf50633_adc_async_read); | |
158 | ||
159 | static int adc_result(struct pcf50633 *pcf) | |
160 | { | |
161 | u8 adcs1, adcs3; | |
162 | u16 result; | |
163 | ||
164 | adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1); | |
165 | adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3); | |
166 | result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK); | |
167 | ||
168 | dev_dbg(pcf->dev, "adc result = %d\n", result); | |
169 | ||
170 | return result; | |
171 | } | |
172 | ||
173 | static void pcf50633_adc_irq(int irq, void *data) | |
174 | { | |
175 | struct pcf50633_adc *adc = data; | |
176 | struct pcf50633 *pcf = adc->pcf; | |
177 | struct pcf50633_adc_request *req; | |
bd8ef102 | 178 | int head, res; |
08c3e06a BR |
179 | |
180 | mutex_lock(&adc->queue_mutex); | |
181 | head = adc->queue_head; | |
182 | ||
183 | req = adc->queue[head]; | |
184 | if (WARN_ON(!req)) { | |
185 | dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n"); | |
186 | mutex_unlock(&adc->queue_mutex); | |
187 | return; | |
188 | } | |
189 | adc->queue[head] = NULL; | |
190 | adc->queue_head = (head + 1) & | |
191 | (PCF50633_MAX_ADC_FIFO_DEPTH - 1); | |
192 | ||
bd8ef102 PF |
193 | res = adc_result(pcf); |
194 | trigger_next_adc_job_if_any(pcf); | |
195 | ||
08c3e06a BR |
196 | mutex_unlock(&adc->queue_mutex); |
197 | ||
bd8ef102 | 198 | req->callback(pcf, req->callback_param, res); |
08c3e06a | 199 | kfree(req); |
08c3e06a BR |
200 | } |
201 | ||
f791be49 | 202 | static int pcf50633_adc_probe(struct platform_device *pdev) |
08c3e06a | 203 | { |
08c3e06a BR |
204 | struct pcf50633_adc *adc; |
205 | ||
8a105ca2 | 206 | adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL); |
08c3e06a BR |
207 | if (!adc) |
208 | return -ENOMEM; | |
209 | ||
68d641ef | 210 | adc->pcf = dev_to_pcf50633(pdev->dev.parent); |
08c3e06a BR |
211 | platform_set_drvdata(pdev, adc); |
212 | ||
68d641ef | 213 | pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY, |
08c3e06a BR |
214 | pcf50633_adc_irq, adc); |
215 | ||
216 | mutex_init(&adc->queue_mutex); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
e999021c | 221 | static void pcf50633_adc_remove(struct platform_device *pdev) |
08c3e06a BR |
222 | { |
223 | struct pcf50633_adc *adc = platform_get_drvdata(pdev); | |
224 | int i, head; | |
225 | ||
226 | pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY); | |
227 | ||
228 | mutex_lock(&adc->queue_mutex); | |
229 | head = adc->queue_head; | |
230 | ||
231 | if (WARN_ON(adc->queue[head])) | |
232 | dev_err(adc->pcf->dev, | |
233 | "adc driver removed with request pending\n"); | |
234 | ||
235 | for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++) | |
236 | kfree(adc->queue[i]); | |
237 | ||
238 | mutex_unlock(&adc->queue_mutex); | |
08c3e06a BR |
239 | } |
240 | ||
241 | static struct platform_driver pcf50633_adc_driver = { | |
242 | .driver = { | |
243 | .name = "pcf50633-adc", | |
244 | }, | |
245 | .probe = pcf50633_adc_probe, | |
e999021c | 246 | .remove_new = pcf50633_adc_remove, |
08c3e06a BR |
247 | }; |
248 | ||
65349d60 | 249 | module_platform_driver(pcf50633_adc_driver); |
08c3e06a BR |
250 | |
251 | MODULE_AUTHOR("Balaji Rao <[email protected]>"); | |
252 | MODULE_DESCRIPTION("PCF50633 adc driver"); | |
253 | MODULE_LICENSE("GPL"); | |
254 | MODULE_ALIAS("platform:pcf50633-adc"); | |
255 |