diff options
Diffstat (limited to 'arch/arm/plat-samsung/adc.c')
-rw-r--r-- | arch/arm/plat-samsung/adc.c | 84 |
1 files changed, 64 insertions, 20 deletions
diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index e8f2be2d67f..ee8deef1948 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/regulator/consumer.h> | ||
24 | 25 | ||
25 | #include <plat/regs-adc.h> | 26 | #include <plat/regs-adc.h> |
26 | #include <plat/adc.h> | 27 | #include <plat/adc.h> |
@@ -39,8 +40,9 @@ | |||
39 | */ | 40 | */ |
40 | 41 | ||
41 | enum s3c_cpu_type { | 42 | enum s3c_cpu_type { |
42 | TYPE_S3C24XX, | 43 | TYPE_ADCV1, /* S3C24XX */ |
43 | TYPE_S3C64XX | 44 | TYPE_ADCV2, /* S3C64XX, S5P64X0, S5PC100 */ |
45 | TYPE_ADCV3, /* S5PV210, S5PC110, EXYNOS4210 */ | ||
44 | }; | 46 | }; |
45 | 47 | ||
46 | struct s3c_adc_client { | 48 | struct s3c_adc_client { |
@@ -71,6 +73,7 @@ struct adc_device { | |||
71 | unsigned int prescale; | 73 | unsigned int prescale; |
72 | 74 | ||
73 | int irq; | 75 | int irq; |
76 | struct regulator *vdd; | ||
74 | }; | 77 | }; |
75 | 78 | ||
76 | static struct adc_device *adc_dev; | 79 | static struct adc_device *adc_dev; |
@@ -91,6 +94,7 @@ static inline void s3c_adc_select(struct adc_device *adc, | |||
91 | struct s3c_adc_client *client) | 94 | struct s3c_adc_client *client) |
92 | { | 95 | { |
93 | unsigned con = readl(adc->regs + S3C2410_ADCCON); | 96 | unsigned con = readl(adc->regs + S3C2410_ADCCON); |
97 | enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data; | ||
94 | 98 | ||
95 | client->select_cb(client, 1); | 99 | client->select_cb(client, 1); |
96 | 100 | ||
@@ -98,8 +102,12 @@ static inline void s3c_adc_select(struct adc_device *adc, | |||
98 | con &= ~S3C2410_ADCCON_STDBM; | 102 | con &= ~S3C2410_ADCCON_STDBM; |
99 | con &= ~S3C2410_ADCCON_STARTMASK; | 103 | con &= ~S3C2410_ADCCON_STARTMASK; |
100 | 104 | ||
101 | if (!client->is_ts) | 105 | if (!client->is_ts) { |
102 | con |= S3C2410_ADCCON_SELMUX(client->channel); | 106 | if (cpu == TYPE_ADCV3) |
107 | writel(client->channel & 0xf, adc->regs + S5P_ADCMUX); | ||
108 | else | ||
109 | con |= S3C2410_ADCCON_SELMUX(client->channel); | ||
110 | } | ||
103 | 111 | ||
104 | writel(con, adc->regs + S3C2410_ADCCON); | 112 | writel(con, adc->regs + S3C2410_ADCCON); |
105 | } | 113 | } |
@@ -285,8 +293,8 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) | |||
285 | 293 | ||
286 | client->nr_samples--; | 294 | client->nr_samples--; |
287 | 295 | ||
288 | if (cpu == TYPE_S3C64XX) { | 296 | if (cpu != TYPE_ADCV1) { |
289 | /* S3C64XX ADC resolution is 12-bit */ | 297 | /* S3C64XX/S5P ADC resolution is 12-bit */ |
290 | data0 &= 0xfff; | 298 | data0 &= 0xfff; |
291 | data1 &= 0xfff; | 299 | data1 &= 0xfff; |
292 | } else { | 300 | } else { |
@@ -312,7 +320,7 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) | |||
312 | } | 320 | } |
313 | 321 | ||
314 | exit: | 322 | exit: |
315 | if (cpu == TYPE_S3C64XX) { | 323 | if (cpu != TYPE_ADCV1) { |
316 | /* Clear ADC interrupt */ | 324 | /* Clear ADC interrupt */ |
317 | writel(0, adc->regs + S3C64XX_ADCCLRINT); | 325 | writel(0, adc->regs + S3C64XX_ADCCLRINT); |
318 | } | 326 | } |
@@ -338,17 +346,24 @@ static int s3c_adc_probe(struct platform_device *pdev) | |||
338 | adc->pdev = pdev; | 346 | adc->pdev = pdev; |
339 | adc->prescale = S3C2410_ADCCON_PRSCVL(49); | 347 | adc->prescale = S3C2410_ADCCON_PRSCVL(49); |
340 | 348 | ||
349 | adc->vdd = regulator_get(dev, "vdd"); | ||
350 | if (IS_ERR(adc->vdd)) { | ||
351 | dev_err(dev, "operating without regulator \"vdd\" .\n"); | ||
352 | ret = PTR_ERR(adc->vdd); | ||
353 | goto err_alloc; | ||
354 | } | ||
355 | |||
341 | adc->irq = platform_get_irq(pdev, 1); | 356 | adc->irq = platform_get_irq(pdev, 1); |
342 | if (adc->irq <= 0) { | 357 | if (adc->irq <= 0) { |
343 | dev_err(dev, "failed to get adc irq\n"); | 358 | dev_err(dev, "failed to get adc irq\n"); |
344 | ret = -ENOENT; | 359 | ret = -ENOENT; |
345 | goto err_alloc; | 360 | goto err_reg; |
346 | } | 361 | } |
347 | 362 | ||
348 | ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); | 363 | ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); |
349 | if (ret < 0) { | 364 | if (ret < 0) { |
350 | dev_err(dev, "failed to attach adc irq\n"); | 365 | dev_err(dev, "failed to attach adc irq\n"); |
351 | goto err_alloc; | 366 | goto err_reg; |
352 | } | 367 | } |
353 | 368 | ||
354 | adc->clk = clk_get(dev, "adc"); | 369 | adc->clk = clk_get(dev, "adc"); |
@@ -372,10 +387,14 @@ static int s3c_adc_probe(struct platform_device *pdev) | |||
372 | goto err_clk; | 387 | goto err_clk; |
373 | } | 388 | } |
374 | 389 | ||
390 | ret = regulator_enable(adc->vdd); | ||
391 | if (ret) | ||
392 | goto err_ioremap; | ||
393 | |||
375 | clk_enable(adc->clk); | 394 | clk_enable(adc->clk); |
376 | 395 | ||
377 | tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; | 396 | tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; |
378 | if (platform_get_device_id(pdev)->driver_data == TYPE_S3C64XX) { | 397 | if (platform_get_device_id(pdev)->driver_data != TYPE_ADCV1) { |
379 | /* Enable 12-bit ADC resolution */ | 398 | /* Enable 12-bit ADC resolution */ |
380 | tmp |= S3C64XX_ADCCON_RESSEL; | 399 | tmp |= S3C64XX_ADCCON_RESSEL; |
381 | } | 400 | } |
@@ -388,12 +407,15 @@ static int s3c_adc_probe(struct platform_device *pdev) | |||
388 | 407 | ||
389 | return 0; | 408 | return 0; |
390 | 409 | ||
410 | err_ioremap: | ||
411 | iounmap(adc->regs); | ||
391 | err_clk: | 412 | err_clk: |
392 | clk_put(adc->clk); | 413 | clk_put(adc->clk); |
393 | 414 | ||
394 | err_irq: | 415 | err_irq: |
395 | free_irq(adc->irq, adc); | 416 | free_irq(adc->irq, adc); |
396 | 417 | err_reg: | |
418 | regulator_put(adc->vdd); | ||
397 | err_alloc: | 419 | err_alloc: |
398 | kfree(adc); | 420 | kfree(adc); |
399 | return ret; | 421 | return ret; |
@@ -406,6 +428,8 @@ static int __devexit s3c_adc_remove(struct platform_device *pdev) | |||
406 | iounmap(adc->regs); | 428 | iounmap(adc->regs); |
407 | free_irq(adc->irq, adc); | 429 | free_irq(adc->irq, adc); |
408 | clk_disable(adc->clk); | 430 | clk_disable(adc->clk); |
431 | regulator_disable(adc->vdd); | ||
432 | regulator_put(adc->vdd); | ||
409 | clk_put(adc->clk); | 433 | clk_put(adc->clk); |
410 | kfree(adc); | 434 | kfree(adc); |
411 | 435 | ||
@@ -413,8 +437,10 @@ static int __devexit s3c_adc_remove(struct platform_device *pdev) | |||
413 | } | 437 | } |
414 | 438 | ||
415 | #ifdef CONFIG_PM | 439 | #ifdef CONFIG_PM |
416 | static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) | 440 | static int s3c_adc_suspend(struct device *dev) |
417 | { | 441 | { |
442 | struct platform_device *pdev = container_of(dev, | ||
443 | struct platform_device, dev); | ||
418 | struct adc_device *adc = platform_get_drvdata(pdev); | 444 | struct adc_device *adc = platform_get_drvdata(pdev); |
419 | unsigned long flags; | 445 | unsigned long flags; |
420 | u32 con; | 446 | u32 con; |
@@ -428,19 +454,30 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) | |||
428 | disable_irq(adc->irq); | 454 | disable_irq(adc->irq); |
429 | spin_unlock_irqrestore(&adc->lock, flags); | 455 | spin_unlock_irqrestore(&adc->lock, flags); |
430 | clk_disable(adc->clk); | 456 | clk_disable(adc->clk); |
457 | regulator_disable(adc->vdd); | ||
431 | 458 | ||
432 | return 0; | 459 | return 0; |
433 | } | 460 | } |
434 | 461 | ||
435 | static int s3c_adc_resume(struct platform_device *pdev) | 462 | static int s3c_adc_resume(struct device *dev) |
436 | { | 463 | { |
464 | struct platform_device *pdev = container_of(dev, | ||
465 | struct platform_device, dev); | ||
437 | struct adc_device *adc = platform_get_drvdata(pdev); | 466 | struct adc_device *adc = platform_get_drvdata(pdev); |
467 | int ret; | ||
468 | unsigned long tmp; | ||
438 | 469 | ||
470 | ret = regulator_enable(adc->vdd); | ||
471 | if (ret) | ||
472 | return ret; | ||
439 | clk_enable(adc->clk); | 473 | clk_enable(adc->clk); |
440 | enable_irq(adc->irq); | 474 | enable_irq(adc->irq); |
441 | 475 | ||
442 | writel(adc->prescale | S3C2410_ADCCON_PRSCEN, | 476 | tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; |
443 | adc->regs + S3C2410_ADCCON); | 477 | /* Enable 12-bit ADC resolution */ |
478 | if (platform_get_device_id(pdev)->driver_data != TYPE_ADCV1) | ||
479 | tmp |= S3C64XX_ADCCON_RESSEL; | ||
480 | writel(tmp, adc->regs + S3C2410_ADCCON); | ||
444 | 481 | ||
445 | return 0; | 482 | return 0; |
446 | } | 483 | } |
@@ -453,25 +490,32 @@ static int s3c_adc_resume(struct platform_device *pdev) | |||
453 | static struct platform_device_id s3c_adc_driver_ids[] = { | 490 | static struct platform_device_id s3c_adc_driver_ids[] = { |
454 | { | 491 | { |
455 | .name = "s3c24xx-adc", | 492 | .name = "s3c24xx-adc", |
456 | .driver_data = TYPE_S3C24XX, | 493 | .driver_data = TYPE_ADCV1, |
457 | }, { | 494 | }, { |
458 | .name = "s3c64xx-adc", | 495 | .name = "s3c64xx-adc", |
459 | .driver_data = TYPE_S3C64XX, | 496 | .driver_data = TYPE_ADCV2, |
497 | }, { | ||
498 | .name = "samsung-adc-v3", | ||
499 | .driver_data = TYPE_ADCV3, | ||
460 | }, | 500 | }, |
461 | { } | 501 | { } |
462 | }; | 502 | }; |
463 | MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids); | 503 | MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids); |
464 | 504 | ||
505 | static const struct dev_pm_ops adc_pm_ops = { | ||
506 | .suspend = s3c_adc_suspend, | ||
507 | .resume = s3c_adc_resume, | ||
508 | }; | ||
509 | |||
465 | static struct platform_driver s3c_adc_driver = { | 510 | static struct platform_driver s3c_adc_driver = { |
466 | .id_table = s3c_adc_driver_ids, | 511 | .id_table = s3c_adc_driver_ids, |
467 | .driver = { | 512 | .driver = { |
468 | .name = "s3c-adc", | 513 | .name = "s3c-adc", |
469 | .owner = THIS_MODULE, | 514 | .owner = THIS_MODULE, |
515 | .pm = &adc_pm_ops, | ||
470 | }, | 516 | }, |
471 | .probe = s3c_adc_probe, | 517 | .probe = s3c_adc_probe, |
472 | .remove = __devexit_p(s3c_adc_remove), | 518 | .remove = __devexit_p(s3c_adc_remove), |
473 | .suspend = s3c_adc_suspend, | ||
474 | .resume = s3c_adc_resume, | ||
475 | }; | 519 | }; |
476 | 520 | ||
477 | static int __init adc_init(void) | 521 | static int __init adc_init(void) |
@@ -485,4 +529,4 @@ static int __init adc_init(void) | |||
485 | return ret; | 529 | return ret; |
486 | } | 530 | } |
487 | 531 | ||
488 | arch_initcall(adc_init); | 532 | module_init(adc_init); |