aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s3c24xx
diff options
context:
space:
mode:
authorMaurus Cuelenaere <mcuelenaere@gmail.com>2010-01-13 18:30:31 -0500
committerBen Dooks <ben-linux@fluff.org>2010-01-17 19:30:49 -0500
commit3929e1e76d9116856a4c7a00fcce0539dd8507a0 (patch)
treea185da5ed8cff52e08555047b163f2b0d9c66c0f /arch/arm/plat-s3c24xx
parent501dae90b3ae4dd3d8efdacfcb072c3d65eb5a33 (diff)
ARM: SAMSUNG: Move S3C24XX ADC driver to plat-samsung
Move S3C24XX ADC driver to plat-samsung Signed-off-by: Maurus Cuelenaere <mcuelenaere@gmail.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c24xx')
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig7
-rw-r--r--arch/arm/plat-s3c24xx/Makefile1
-rw-r--r--arch/arm/plat-s3c24xx/adc.c435
3 files changed, 0 insertions, 443 deletions
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index 342647eb91d8..a806f359ceb6 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -117,13 +117,6 @@ config S3C2410_DMA_DEBUG
117 Enable debugging output for the DMA code. This option sends info 117 Enable debugging output for the DMA code. This option sends info
118 to the kernel log, at priority KERN_DEBUG. 118 to the kernel log, at priority KERN_DEBUG.
119 119
120config S3C24XX_ADC
121 bool "ADC common driver support"
122 help
123 Core support for the ADC block found in the S3C24XX SoC systems
124 for drivers such as the touchscreen and hwmon to use to share
125 this resource.
126
127# SPI default pin configuration code 120# SPI default pin configuration code
128 121
129config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 122config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile
index 5dee8c12e8b4..e0100266d039 100644
--- a/arch/arm/plat-s3c24xx/Makefile
+++ b/arch/arm/plat-s3c24xx/Makefile
@@ -38,7 +38,6 @@ obj-$(CONFIG_PM) += irq-pm.o
38obj-$(CONFIG_PM) += sleep.o 38obj-$(CONFIG_PM) += sleep.o
39obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o 39obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o
40obj-$(CONFIG_S3C2410_DMA) += dma.o 40obj-$(CONFIG_S3C2410_DMA) += dma.o
41obj-$(CONFIG_S3C24XX_ADC) += adc.o
42obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o 41obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o
43obj-$(CONFIG_S3C2412_IOTIMING) += s3c2412-iotiming.o 42obj-$(CONFIG_S3C2412_IOTIMING) += s3c2412-iotiming.o
44obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o 43obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o
diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c
deleted file mode 100644
index ce47627f3368..000000000000
--- a/arch/arm/plat-s3c24xx/adc.c
+++ /dev/null
@@ -1,435 +0,0 @@
1/* arch/arm/plat-s3c24xx/adc.c
2 *
3 * Copyright (c) 2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
6 *
7 * S3C24XX ADC device core
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License.
12*/
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/platform_device.h>
17#include <linux/sched.h>
18#include <linux/list.h>
19#include <linux/err.h>
20#include <linux/clk.h>
21#include <linux/interrupt.h>
22#include <linux/io.h>
23
24#include <plat/regs-adc.h>
25#include <plat/adc.h>
26
27/* This driver is designed to control the usage of the ADC block between
28 * the touchscreen and any other drivers that may need to use it, such as
29 * the hwmon driver.
30 *
31 * Priority will be given to the touchscreen driver, but as this itself is
32 * rate limited it should not starve other requests which are processed in
33 * order that they are received.
34 *
35 * Each user registers to get a client block which uniquely identifies it
36 * and stores information such as the necessary functions to callback when
37 * action is required.
38 */
39
40struct s3c_adc_client {
41 struct platform_device *pdev;
42 struct list_head pend;
43 wait_queue_head_t *wait;
44
45 unsigned int nr_samples;
46 int result;
47 unsigned char is_ts;
48 unsigned char channel;
49
50 void (*select_cb)(struct s3c_adc_client *c, unsigned selected);
51 void (*convert_cb)(struct s3c_adc_client *c,
52 unsigned val1, unsigned val2,
53 unsigned *samples_left);
54};
55
56struct adc_device {
57 struct platform_device *pdev;
58 struct platform_device *owner;
59 struct clk *clk;
60 struct s3c_adc_client *cur;
61 struct s3c_adc_client *ts_pend;
62 void __iomem *regs;
63
64 unsigned int prescale;
65
66 int irq;
67};
68
69static struct adc_device *adc_dev;
70
71static LIST_HEAD(adc_pending);
72
73#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
74
75static inline void s3c_adc_convert(struct adc_device *adc)
76{
77 unsigned con = readl(adc->regs + S3C2410_ADCCON);
78
79 con |= S3C2410_ADCCON_ENABLE_START;
80 writel(con, adc->regs + S3C2410_ADCCON);
81}
82
83static inline void s3c_adc_select(struct adc_device *adc,
84 struct s3c_adc_client *client)
85{
86 unsigned con = readl(adc->regs + S3C2410_ADCCON);
87
88 client->select_cb(client, 1);
89
90 con &= ~S3C2410_ADCCON_MUXMASK;
91 con &= ~S3C2410_ADCCON_STDBM;
92 con &= ~S3C2410_ADCCON_STARTMASK;
93
94 if (!client->is_ts)
95 con |= S3C2410_ADCCON_SELMUX(client->channel);
96
97 writel(con, adc->regs + S3C2410_ADCCON);
98}
99
100static void s3c_adc_dbgshow(struct adc_device *adc)
101{
102 adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n",
103 readl(adc->regs + S3C2410_ADCCON),
104 readl(adc->regs + S3C2410_ADCTSC),
105 readl(adc->regs + S3C2410_ADCDLY));
106}
107
108static void s3c_adc_try(struct adc_device *adc)
109{
110 struct s3c_adc_client *next = adc->ts_pend;
111
112 if (!next && !list_empty(&adc_pending)) {
113 next = list_first_entry(&adc_pending,
114 struct s3c_adc_client, pend);
115 list_del(&next->pend);
116 } else
117 adc->ts_pend = NULL;
118
119 if (next) {
120 adc_dbg(adc, "new client is %p\n", next);
121 adc->cur = next;
122 s3c_adc_select(adc, next);
123 s3c_adc_convert(adc);
124 s3c_adc_dbgshow(adc);
125 }
126}
127
128int s3c_adc_start(struct s3c_adc_client *client,
129 unsigned int channel, unsigned int nr_samples)
130{
131 struct adc_device *adc = adc_dev;
132 unsigned long flags;
133
134 if (!adc) {
135 printk(KERN_ERR "%s: failed to find adc\n", __func__);
136 return -EINVAL;
137 }
138
139 if (client->is_ts && adc->ts_pend)
140 return -EAGAIN;
141
142 local_irq_save(flags);
143
144 client->channel = channel;
145 client->nr_samples = nr_samples;
146
147 if (client->is_ts)
148 adc->ts_pend = client;
149 else
150 list_add_tail(&client->pend, &adc_pending);
151
152 if (!adc->cur)
153 s3c_adc_try(adc);
154 local_irq_restore(flags);
155
156 return 0;
157}
158EXPORT_SYMBOL_GPL(s3c_adc_start);
159
160static void s3c_convert_done(struct s3c_adc_client *client,
161 unsigned v, unsigned u, unsigned *left)
162{
163 client->result = v;
164 wake_up(client->wait);
165}
166
167int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch)
168{
169 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
170 int ret;
171
172 client->convert_cb = s3c_convert_done;
173 client->wait = &wake;
174 client->result = -1;
175
176 ret = s3c_adc_start(client, ch, 1);
177 if (ret < 0)
178 goto err;
179
180 ret = wait_event_timeout(wake, client->result >= 0, HZ / 2);
181 if (client->result < 0) {
182 ret = -ETIMEDOUT;
183 goto err;
184 }
185
186 client->convert_cb = NULL;
187 return client->result;
188
189err:
190 return ret;
191}
192EXPORT_SYMBOL_GPL(s3c_adc_read);
193
194static void s3c_adc_default_select(struct s3c_adc_client *client,
195 unsigned select)
196{
197}
198
199struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
200 void (*select)(struct s3c_adc_client *client,
201 unsigned int selected),
202 void (*conv)(struct s3c_adc_client *client,
203 unsigned d0, unsigned d1,
204 unsigned *samples_left),
205 unsigned int is_ts)
206{
207 struct s3c_adc_client *client;
208
209 WARN_ON(!pdev);
210
211 if (!select)
212 select = s3c_adc_default_select;
213
214 if (!pdev)
215 return ERR_PTR(-EINVAL);
216
217 client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
218 if (!client) {
219 dev_err(&pdev->dev, "no memory for adc client\n");
220 return ERR_PTR(-ENOMEM);
221 }
222
223 client->pdev = pdev;
224 client->is_ts = is_ts;
225 client->select_cb = select;
226 client->convert_cb = conv;
227
228 return client;
229}
230EXPORT_SYMBOL_GPL(s3c_adc_register);
231
232void s3c_adc_release(struct s3c_adc_client *client)
233{
234 /* We should really check that nothing is in progress. */
235 if (adc_dev->cur == client)
236 adc_dev->cur = NULL;
237 if (adc_dev->ts_pend == client)
238 adc_dev->ts_pend = NULL;
239 else {
240 struct list_head *p, *n;
241 struct s3c_adc_client *tmp;
242
243 list_for_each_safe(p, n, &adc_pending) {
244 tmp = list_entry(p, struct s3c_adc_client, pend);
245 if (tmp == client)
246 list_del(&tmp->pend);
247 }
248 }
249
250 if (adc_dev->cur == NULL)
251 s3c_adc_try(adc_dev);
252 kfree(client);
253}
254EXPORT_SYMBOL_GPL(s3c_adc_release);
255
256static irqreturn_t s3c_adc_irq(int irq, void *pw)
257{
258 struct adc_device *adc = pw;
259 struct s3c_adc_client *client = adc->cur;
260 unsigned long flags;
261 unsigned data0, data1;
262
263 if (!client) {
264 dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
265 return IRQ_HANDLED;
266 }
267
268 data0 = readl(adc->regs + S3C2410_ADCDAT0);
269 data1 = readl(adc->regs + S3C2410_ADCDAT1);
270 adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
271
272 client->nr_samples--;
273
274 if (client->convert_cb)
275 (client->convert_cb)(client, data0 & 0x3ff, data1 & 0x3ff,
276 &client->nr_samples);
277
278 if (client->nr_samples > 0) {
279 /* fire another conversion for this */
280
281 client->select_cb(client, 1);
282 s3c_adc_convert(adc);
283 } else {
284 local_irq_save(flags);
285 (client->select_cb)(client, 0);
286 adc->cur = NULL;
287
288 s3c_adc_try(adc);
289 local_irq_restore(flags);
290 }
291
292 return IRQ_HANDLED;
293}
294
295static int s3c_adc_probe(struct platform_device *pdev)
296{
297 struct device *dev = &pdev->dev;
298 struct adc_device *adc;
299 struct resource *regs;
300 int ret;
301
302 adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL);
303 if (adc == NULL) {
304 dev_err(dev, "failed to allocate adc_device\n");
305 return -ENOMEM;
306 }
307
308 adc->pdev = pdev;
309 adc->prescale = S3C2410_ADCCON_PRSCVL(49);
310
311 adc->irq = platform_get_irq(pdev, 1);
312 if (adc->irq <= 0) {
313 dev_err(dev, "failed to get adc irq\n");
314 ret = -ENOENT;
315 goto err_alloc;
316 }
317
318 ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc);
319 if (ret < 0) {
320 dev_err(dev, "failed to attach adc irq\n");
321 goto err_alloc;
322 }
323
324 adc->clk = clk_get(dev, "adc");
325 if (IS_ERR(adc->clk)) {
326 dev_err(dev, "failed to get adc clock\n");
327 ret = PTR_ERR(adc->clk);
328 goto err_irq;
329 }
330
331 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
332 if (!regs) {
333 dev_err(dev, "failed to find registers\n");
334 ret = -ENXIO;
335 goto err_clk;
336 }
337
338 adc->regs = ioremap(regs->start, resource_size(regs));
339 if (!adc->regs) {
340 dev_err(dev, "failed to map registers\n");
341 ret = -ENXIO;
342 goto err_clk;
343 }
344
345 clk_enable(adc->clk);
346
347 writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
348 adc->regs + S3C2410_ADCCON);
349
350 dev_info(dev, "attached adc driver\n");
351
352 platform_set_drvdata(pdev, adc);
353 adc_dev = adc;
354
355 return 0;
356
357 err_clk:
358 clk_put(adc->clk);
359
360 err_irq:
361 free_irq(adc->irq, adc);
362
363 err_alloc:
364 kfree(adc);
365 return ret;
366}
367
368static int __devexit s3c_adc_remove(struct platform_device *pdev)
369{
370 struct adc_device *adc = platform_get_drvdata(pdev);
371
372 iounmap(adc->regs);
373 free_irq(adc->irq, adc);
374 clk_disable(adc->clk);
375 clk_put(adc->clk);
376 kfree(adc);
377
378 return 0;
379}
380
381#ifdef CONFIG_PM
382static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
383{
384 struct adc_device *adc = platform_get_drvdata(pdev);
385 u32 con;
386
387 con = readl(adc->regs + S3C2410_ADCCON);
388 con |= S3C2410_ADCCON_STDBM;
389 writel(con, adc->regs + S3C2410_ADCCON);
390
391 clk_disable(adc->clk);
392
393 return 0;
394}
395
396static int s3c_adc_resume(struct platform_device *pdev)
397{
398 struct adc_device *adc = platform_get_drvdata(pdev);
399
400 clk_enable(adc->clk);
401
402 writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
403 adc->regs + S3C2410_ADCCON);
404
405 return 0;
406}
407
408#else
409#define s3c_adc_suspend NULL
410#define s3c_adc_resume NULL
411#endif
412
413static struct platform_driver s3c_adc_driver = {
414 .driver = {
415 .name = "s3c24xx-adc",
416 .owner = THIS_MODULE,
417 },
418 .probe = s3c_adc_probe,
419 .remove = __devexit_p(s3c_adc_remove),
420 .suspend = s3c_adc_suspend,
421 .resume = s3c_adc_resume,
422};
423
424static int __init adc_init(void)
425{
426 int ret;
427
428 ret = platform_driver_register(&s3c_adc_driver);
429 if (ret)
430 printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
431
432 return ret;
433}
434
435arch_initcall(adc_init);