aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s3c24xx
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-s3c24xx')
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig28
-rw-r--r--arch/arm/plat-s3c24xx/Makefile1
-rw-r--r--arch/arm/plat-s3c24xx/adc.c372
-rw-r--r--arch/arm/plat-s3c24xx/devs.c10
-rw-r--r--arch/arm/plat-s3c24xx/include/mach/pwm-clock.h55
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-clock.c1
6 files changed, 466 insertions, 1 deletions
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index f0d54fdf88d4..2c8a2f5d75ff 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -41,6 +41,27 @@ config S3C24XX_PWM
41 Support for exporting the PWM timer blocks via the pwm device 41 Support for exporting the PWM timer blocks via the pwm device
42 system. 42 system.
43 43
44
45# gpio configurations
46
47config S3C24XX_GPIO_EXTRA
48 int
49 default 128 if S3C24XX_GPIO_EXTRA128
50 default 64 if S3C24XX_GPIO_EXTRA64
51 default 0
52
53config S3C24XX_GPIO_EXTRA64
54 bool
55 help
56 Add an extra 64 gpio numbers to the available GPIO pool. This is
57 available for boards that need extra gpios for external devices.
58
59config S3C24XX_GPIO_EXTRA128
60 bool
61 help
62 Add an extra 128 gpio numbers to the available GPIO pool. This is
63 available for boards that need extra gpios for external devices.
64
44config PM_SIMTEC 65config PM_SIMTEC
45 bool 66 bool
46 help 67 help
@@ -62,6 +83,13 @@ config S3C2410_DMA_DEBUG
62 Enable debugging output for the DMA code. This option sends info 83 Enable debugging output for the DMA code. This option sends info
63 to the kernel log, at priority KERN_DEBUG. 84 to the kernel log, at priority KERN_DEBUG.
64 85
86config S3C24XX_ADC
87 bool "ADC common driver support"
88 help
89 Core support for the ADC block found in the S3C24XX SoC systems
90 for drivers such as the touchscreen and hwmon to use to share
91 this resource.
92
65# SPI default pin configuration code 93# SPI default pin configuration code
66 94
67config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 95config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile
index 763d3444048b..1e0767b266b8 100644
--- a/arch/arm/plat-s3c24xx/Makefile
+++ b/arch/arm/plat-s3c24xx/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_PM) += sleep.o
31obj-$(CONFIG_HAVE_PWM) += pwm.o 31obj-$(CONFIG_HAVE_PWM) += pwm.o
32obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o 32obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o
33obj-$(CONFIG_S3C2410_DMA) += dma.o 33obj-$(CONFIG_S3C2410_DMA) += dma.o
34obj-$(CONFIG_S3C24XX_ADC) += adc.o
34 35
35# device specific setup and/or initialisation 36# device specific setup and/or initialisation
36obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o 37obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o
diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c
new file mode 100644
index 000000000000..9a5c767e0a42
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/adc.c
@@ -0,0 +1,372 @@
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/list.h>
18#include <linux/err.h>
19#include <linux/clk.h>
20#include <linux/interrupt.h>
21#include <linux/io.h>
22
23#include <plat/regs-adc.h>
24#include <plat/adc.h>
25
26/* This driver is designed to control the usage of the ADC block between
27 * the touchscreen and any other drivers that may need to use it, such as
28 * the hwmon driver.
29 *
30 * Priority will be given to the touchscreen driver, but as this itself is
31 * rate limited it should not starve other requests which are processed in
32 * order that they are received.
33 *
34 * Each user registers to get a client block which uniquely identifies it
35 * and stores information such as the necessary functions to callback when
36 * action is required.
37 */
38
39struct s3c_adc_client {
40 struct platform_device *pdev;
41 struct list_head pend;
42
43 unsigned int nr_samples;
44 unsigned char is_ts;
45 unsigned char channel;
46
47 void (*select_cb)(unsigned selected);
48 void (*convert_cb)(unsigned val1, unsigned val2);
49};
50
51struct adc_device {
52 struct platform_device *pdev;
53 struct platform_device *owner;
54 struct clk *clk;
55 struct s3c_adc_client *cur;
56 struct s3c_adc_client *ts_pend;
57 void __iomem *regs;
58
59 unsigned int prescale;
60
61 int irq;
62};
63
64static struct adc_device *adc_dev;
65
66static LIST_HEAD(adc_pending);
67
68#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
69
70static inline void s3c_adc_convert(struct adc_device *adc)
71{
72 unsigned con = readl(adc->regs + S3C2410_ADCCON);
73
74 con |= S3C2410_ADCCON_ENABLE_START;
75 writel(con, adc->regs + S3C2410_ADCCON);
76}
77
78static inline void s3c_adc_select(struct adc_device *adc,
79 struct s3c_adc_client *client)
80{
81 unsigned con = readl(adc->regs + S3C2410_ADCCON);
82
83 client->select_cb(1);
84
85 con &= ~S3C2410_ADCCON_MUXMASK;
86 con &= ~S3C2410_ADCCON_STDBM;
87 con &= ~S3C2410_ADCCON_STARTMASK;
88
89 if (!client->is_ts)
90 con |= S3C2410_ADCCON_SELMUX(client->channel);
91
92 writel(con, adc->regs + S3C2410_ADCCON);
93}
94
95static void s3c_adc_dbgshow(struct adc_device *adc)
96{
97 adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n",
98 readl(adc->regs + S3C2410_ADCCON),
99 readl(adc->regs + S3C2410_ADCTSC),
100 readl(adc->regs + S3C2410_ADCDLY));
101}
102
103void s3c_adc_try(struct adc_device *adc)
104{
105 struct s3c_adc_client *next = adc->ts_pend;
106
107 if (!next && !list_empty(&adc_pending)) {
108 next = list_first_entry(&adc_pending,
109 struct s3c_adc_client, pend);
110 list_del(&next->pend);
111 } else
112 adc->ts_pend = NULL;
113
114 if (next) {
115 adc_dbg(adc, "new client is %p\n", next);
116 adc->cur = next;
117 s3c_adc_select(adc, next);
118 s3c_adc_convert(adc);
119 s3c_adc_dbgshow(adc);
120 }
121}
122
123int s3c_adc_start(struct s3c_adc_client *client,
124 unsigned int channel, unsigned int nr_samples)
125{
126 struct adc_device *adc = adc_dev;
127 unsigned long flags;
128
129 if (!adc) {
130 printk(KERN_ERR "%s: failed to find adc\n", __func__);
131 return -EINVAL;
132 }
133
134 if (client->is_ts && adc->ts_pend)
135 return -EAGAIN;
136
137 local_irq_save(flags);
138
139 client->channel = channel;
140 client->nr_samples = nr_samples;
141
142 if (client->is_ts)
143 adc->ts_pend = client;
144 else
145 list_add_tail(&client->pend, &adc_pending);
146
147 if (!adc->cur)
148 s3c_adc_try(adc);
149 local_irq_restore(flags);
150
151 return 0;
152}
153EXPORT_SYMBOL_GPL(s3c_adc_start);
154
155static void s3c_adc_default_select(unsigned select)
156{
157}
158
159struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
160 void (*select)(unsigned int selected),
161 void (*conv)(unsigned d0, unsigned d1),
162 unsigned int is_ts)
163{
164 struct s3c_adc_client *client;
165
166 WARN_ON(!pdev);
167 WARN_ON(!conv);
168
169 if (!select)
170 select = s3c_adc_default_select;
171
172 if (!conv || !pdev)
173 return ERR_PTR(-EINVAL);
174
175 client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
176 if (!client) {
177 dev_err(&pdev->dev, "no memory for adc client\n");
178 return ERR_PTR(-ENOMEM);
179 }
180
181 client->pdev = pdev;
182 client->is_ts = is_ts;
183 client->select_cb = select;
184 client->convert_cb = conv;
185
186 return client;
187}
188EXPORT_SYMBOL_GPL(s3c_adc_register);
189
190void s3c_adc_release(struct s3c_adc_client *client)
191{
192 /* We should really check that nothing is in progress. */
193 kfree(client);
194}
195EXPORT_SYMBOL_GPL(s3c_adc_release);
196
197static irqreturn_t s3c_adc_irq(int irq, void *pw)
198{
199 struct adc_device *adc = pw;
200 struct s3c_adc_client *client = adc->cur;
201 unsigned long flags;
202 unsigned data0, data1;
203
204 if (!client) {
205 dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
206 return IRQ_HANDLED;
207 }
208
209 data0 = readl(adc->regs + S3C2410_ADCDAT0);
210 data1 = readl(adc->regs + S3C2410_ADCDAT1);
211 adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
212
213 (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff);
214
215 if (--client->nr_samples > 0) {
216 /* fire another conversion for this */
217
218 client->select_cb(1);
219 s3c_adc_convert(adc);
220 } else {
221 local_irq_save(flags);
222 (client->select_cb)(0);
223 adc->cur = NULL;
224
225 s3c_adc_try(adc);
226 local_irq_restore(flags);
227 }
228
229 return IRQ_HANDLED;
230}
231
232static int s3c_adc_probe(struct platform_device *pdev)
233{
234 struct device *dev = &pdev->dev;
235 struct adc_device *adc;
236 struct resource *regs;
237 int ret;
238
239 adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL);
240 if (adc == NULL) {
241 dev_err(dev, "failed to allocate adc_device\n");
242 return -ENOMEM;
243 }
244
245 adc->pdev = pdev;
246 adc->prescale = S3C2410_ADCCON_PRSCVL(49);
247
248 adc->irq = platform_get_irq(pdev, 1);
249 if (adc->irq <= 0) {
250 dev_err(dev, "failed to get adc irq\n");
251 ret = -ENOENT;
252 goto err_alloc;
253 }
254
255 ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc);
256 if (ret < 0) {
257 dev_err(dev, "failed to attach adc irq\n");
258 goto err_alloc;
259 }
260
261 adc->clk = clk_get(dev, "adc");
262 if (IS_ERR(adc->clk)) {
263 dev_err(dev, "failed to get adc clock\n");
264 ret = PTR_ERR(adc->clk);
265 goto err_irq;
266 }
267
268 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
269 if (!regs) {
270 dev_err(dev, "failed to find registers\n");
271 ret = -ENXIO;
272 goto err_clk;
273 }
274
275 adc->regs = ioremap(regs->start, resource_size(regs));
276 if (!adc->regs) {
277 dev_err(dev, "failed to map registers\n");
278 ret = -ENXIO;
279 goto err_clk;
280 }
281
282 clk_enable(adc->clk);
283
284 writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
285 adc->regs + S3C2410_ADCCON);
286
287 dev_info(dev, "attached adc driver\n");
288
289 platform_set_drvdata(pdev, adc);
290 adc_dev = adc;
291
292 return 0;
293
294 err_clk:
295 clk_put(adc->clk);
296
297 err_irq:
298 free_irq(adc->irq, adc);
299
300 err_alloc:
301 kfree(adc);
302 return ret;
303}
304
305static int s3c_adc_remove(struct platform_device *pdev)
306{
307 struct adc_device *adc = platform_get_drvdata(pdev);
308
309 iounmap(adc->regs);
310 free_irq(adc->irq, adc);
311 clk_disable(adc->clk);
312 clk_put(adc->clk);
313 kfree(adc);
314
315 return 0;
316}
317
318#ifdef CONFIG_PM
319static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
320{
321 struct adc_device *adc = platform_get_drvdata(pdev);
322 u32 con;
323
324 con = readl(adc->regs + S3C2410_ADCCON);
325 con |= S3C2410_ADCCON_STDBM;
326 writel(con, adc->regs + S3C2410_ADCCON);
327
328 clk_disable(adc->clk);
329
330 return 0;
331}
332
333static int s3c_adc_resume(struct platform_device *pdev)
334{
335 struct adc_device *adc = platform_get_drvdata(pdev);
336
337 clk_enable(adc->clk);
338
339 writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
340 adc->regs + S3C2410_ADCCON);
341
342 return 0;
343}
344
345#else
346#define s3c_adc_suspend NULL
347#define s3c_adc_resume NULL
348#endif
349
350static struct platform_driver s3c_adc_driver = {
351 .driver = {
352 .name = "s3c24xx-adc",
353 .owner = THIS_MODULE,
354 },
355 .probe = s3c_adc_probe,
356 .remove = __devexit_p(s3c_adc_remove),
357 .suspend = s3c_adc_suspend,
358 .resume = s3c_adc_resume,
359};
360
361static int __init adc_init(void)
362{
363 int ret;
364
365 ret = platform_driver_register(&s3c_adc_driver);
366 if (ret)
367 printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
368
369 return ret;
370}
371
372arch_initcall(adc_init);
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 3cb07b6a0e4c..14d4f0bc1253 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -347,12 +347,20 @@ static struct resource s3c_adc_resource[] = {
347}; 347};
348 348
349struct platform_device s3c_device_adc = { 349struct platform_device s3c_device_adc = {
350 .name = "s3c2410-adc", 350 .name = "s3c24xx-adc",
351 .id = -1, 351 .id = -1,
352 .num_resources = ARRAY_SIZE(s3c_adc_resource), 352 .num_resources = ARRAY_SIZE(s3c_adc_resource),
353 .resource = s3c_adc_resource, 353 .resource = s3c_adc_resource,
354}; 354};
355 355
356/* HWMON */
357
358struct platform_device s3c_device_hwmon = {
359 .name = "s3c24xx-hwmon",
360 .id = -1,
361 .dev.parent = &s3c_device_adc.dev,
362};
363
356/* SDI */ 364/* SDI */
357 365
358static struct resource s3c_sdi_resource[] = { 366static struct resource s3c_sdi_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h b/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h
new file mode 100644
index 000000000000..a087de21bc20
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h
@@ -0,0 +1,55 @@
1/* linux/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h
2 *
3 * Copyright 2008 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
5 * http://armlinux.simtec.co.uk/
6 *
7 * S3C24xx - pwm clock and timer support
8 */
9
10/**
11 * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
12 * @cfg: The timer TCFG1 register bits shifted down to 0.
13 *
14 * Return true if the given configuration from TCFG1 is a TCLK instead
15 * any of the TDIV clocks.
16 */
17static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
18{
19 return tcfg == S3C2410_TCFG1_MUX_TCLK;
20}
21
22/**
23 * tcfg_to_divisor() - convert tcfg1 setting to a divisor
24 * @tcfg1: The tcfg1 setting, shifted down.
25 *
26 * Get the divisor value for the given tcfg1 setting. We assume the
27 * caller has already checked to see if this is not a TCLK source.
28 */
29static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
30{
31 return 1 << (1 + tcfg1);
32}
33
34/**
35 * pwm_tdiv_has_div1() - does the tdiv setting have a /1
36 *
37 * Return true if we have a /1 in the tdiv setting.
38 */
39static inline unsigned int pwm_tdiv_has_div1(void)
40{
41 return 0;
42}
43
44/**
45 * pwm_tdiv_div_bits() - calculate TCFG1 divisor value.
46 * @div: The divisor to calculate the bit information for.
47 *
48 * Turn a divisor into the necessary bit field for TCFG1.
49 */
50static inline unsigned long pwm_tdiv_div_bits(unsigned int div)
51{
52 return ilog2(div) - 1;
53}
54
55#define S3C_TCFG1_MUX_TCLK S3C2410_TCFG1_MUX_TCLK
diff --git a/arch/arm/plat-s3c24xx/s3c2410-clock.c b/arch/arm/plat-s3c24xx/s3c2410-clock.c
index 4e07943c1e29..b61bdb793734 100644
--- a/arch/arm/plat-s3c24xx/s3c2410-clock.c
+++ b/arch/arm/plat-s3c24xx/s3c2410-clock.c
@@ -272,5 +272,6 @@ int __init s3c2410_baseclk_add(void)
272 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", 272 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
273 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); 273 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
274 274
275 s3c_pwmclk_init();
275 return 0; 276 return 0;
276} 277}