diff options
-rw-r--r-- | Documentation/devicetree/bindings/arm/atmel-adc.txt | 65 | ||||
-rw-r--r-- | drivers/iio/adc/at91_adc.c | 132 |
2 files changed, 196 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt new file mode 100644 index 000000000000..c63097d6afeb --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt | |||
@@ -0,0 +1,65 @@ | |||
1 | * AT91's Analog to Digital Converter (ADC) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: Should be "atmel,at91sam9260-adc" | ||
5 | - reg: Should contain ADC registers location and length | ||
6 | - interrupts: Should contain the IRQ line for the ADC | ||
7 | - atmel,adc-channel-base: Offset of the first channel data register | ||
8 | - atmel,adc-channels-used: Bitmask of the channels muxed and enable for this | ||
9 | device | ||
10 | - atmel,adc-drdy-mask: Mask of the DRDY interruption in the ADC | ||
11 | - atmel,adc-num-channels: Number of channels available in the ADC | ||
12 | - atmel,adc-startup-time: Startup Time of the ADC in microseconds as | ||
13 | defined in the datasheet | ||
14 | - atmel,adc-status-register: Offset of the Interrupt Status Register | ||
15 | - atmel,adc-trigger-register: Offset of the Trigger Register | ||
16 | - atmel,adc-vref: Reference voltage in millivolts for the conversions | ||
17 | |||
18 | Optional properties: | ||
19 | - atmel,adc-use-external: Boolean to enable of external triggers | ||
20 | |||
21 | Optional trigger Nodes: | ||
22 | - Required properties: | ||
23 | * trigger-name: Name of the trigger exposed to the user | ||
24 | * trigger-value: Value to put in the Trigger register | ||
25 | to activate this trigger | ||
26 | - Optional properties: | ||
27 | * trigger-external: Is the trigger an external trigger? | ||
28 | |||
29 | Examples: | ||
30 | adc0: adc@fffb0000 { | ||
31 | compatible = "atmel,at91sam9260-adc"; | ||
32 | reg = <0xfffb0000 0x100>; | ||
33 | interrupts = <20 4>; | ||
34 | atmel,adc-channel-base = <0x30>; | ||
35 | atmel,adc-channels-used = <0xff>; | ||
36 | atmel,adc-drdy-mask = <0x10000>; | ||
37 | atmel,adc-num-channels = <8>; | ||
38 | atmel,adc-startup-time = <40>; | ||
39 | atmel,adc-status-register = <0x1c>; | ||
40 | atmel,adc-trigger-register = <0x08>; | ||
41 | atmel,adc-use-external; | ||
42 | atmel,adc-vref = <3300>; | ||
43 | |||
44 | trigger@0 { | ||
45 | trigger-name = "external-rising"; | ||
46 | trigger-value = <0x1>; | ||
47 | trigger-external; | ||
48 | }; | ||
49 | trigger@1 { | ||
50 | trigger-name = "external-falling"; | ||
51 | trigger-value = <0x2>; | ||
52 | trigger-external; | ||
53 | }; | ||
54 | |||
55 | trigger@2 { | ||
56 | trigger-name = "external-any"; | ||
57 | trigger-value = <0x3>; | ||
58 | trigger-external; | ||
59 | }; | ||
60 | |||
61 | trigger@3 { | ||
62 | trigger-name = "continuous"; | ||
63 | trigger-value = <0x6>; | ||
64 | }; | ||
65 | }; | ||
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index e2eb6139daf7..f18a95d80255 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/jiffies.h> | 15 | #include <linux/jiffies.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/of.h> | ||
19 | #include <linux/of_device.h> | ||
18 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
19 | #include <linux/sched.h> | 21 | #include <linux/sched.h> |
20 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
@@ -415,6 +417,123 @@ static int at91_adc_read_raw(struct iio_dev *idev, | |||
415 | return -EINVAL; | 417 | return -EINVAL; |
416 | } | 418 | } |
417 | 419 | ||
420 | static int at91_adc_probe_dt(struct at91_adc_state *st, | ||
421 | struct platform_device *pdev) | ||
422 | { | ||
423 | struct iio_dev *idev = iio_priv_to_dev(st); | ||
424 | struct device_node *node = pdev->dev.of_node; | ||
425 | struct device_node *trig_node; | ||
426 | int i = 0, ret; | ||
427 | u32 prop; | ||
428 | |||
429 | if (!node) | ||
430 | return -EINVAL; | ||
431 | |||
432 | st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers"); | ||
433 | |||
434 | if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) { | ||
435 | dev_err(&idev->dev, "Missing adc-channels-used property in the DT.\n"); | ||
436 | ret = -EINVAL; | ||
437 | goto error_ret; | ||
438 | } | ||
439 | st->channels_mask = prop; | ||
440 | |||
441 | if (of_property_read_u32(node, "atmel,adc-num-channels", &prop)) { | ||
442 | dev_err(&idev->dev, "Missing adc-num-channels property in the DT.\n"); | ||
443 | ret = -EINVAL; | ||
444 | goto error_ret; | ||
445 | } | ||
446 | st->num_channels = prop; | ||
447 | |||
448 | if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) { | ||
449 | dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n"); | ||
450 | ret = -EINVAL; | ||
451 | goto error_ret; | ||
452 | } | ||
453 | st->startup_time = prop; | ||
454 | |||
455 | |||
456 | if (of_property_read_u32(node, "atmel,adc-vref", &prop)) { | ||
457 | dev_err(&idev->dev, "Missing adc-vref property in the DT.\n"); | ||
458 | ret = -EINVAL; | ||
459 | goto error_ret; | ||
460 | } | ||
461 | st->vref_mv = prop; | ||
462 | |||
463 | st->registers = devm_kzalloc(&idev->dev, | ||
464 | sizeof(struct at91_adc_reg_desc), | ||
465 | GFP_KERNEL); | ||
466 | if (!st->registers) { | ||
467 | dev_err(&idev->dev, "Could not allocate register memory.\n"); | ||
468 | ret = -ENOMEM; | ||
469 | goto error_ret; | ||
470 | } | ||
471 | |||
472 | if (of_property_read_u32(node, "atmel,adc-channel-base", &prop)) { | ||
473 | dev_err(&idev->dev, "Missing adc-channel-base property in the DT.\n"); | ||
474 | ret = -EINVAL; | ||
475 | goto error_ret; | ||
476 | } | ||
477 | st->registers->channel_base = prop; | ||
478 | |||
479 | if (of_property_read_u32(node, "atmel,adc-drdy-mask", &prop)) { | ||
480 | dev_err(&idev->dev, "Missing adc-drdy-mask property in the DT.\n"); | ||
481 | ret = -EINVAL; | ||
482 | goto error_ret; | ||
483 | } | ||
484 | st->registers->drdy_mask = prop; | ||
485 | |||
486 | if (of_property_read_u32(node, "atmel,adc-status-register", &prop)) { | ||
487 | dev_err(&idev->dev, "Missing adc-status-register property in the DT.\n"); | ||
488 | ret = -EINVAL; | ||
489 | goto error_ret; | ||
490 | } | ||
491 | st->registers->status_register = prop; | ||
492 | |||
493 | if (of_property_read_u32(node, "atmel,adc-trigger-register", &prop)) { | ||
494 | dev_err(&idev->dev, "Missing adc-trigger-register property in the DT.\n"); | ||
495 | ret = -EINVAL; | ||
496 | goto error_ret; | ||
497 | } | ||
498 | st->registers->trigger_register = prop; | ||
499 | |||
500 | st->trigger_number = of_get_child_count(node); | ||
501 | st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number * | ||
502 | sizeof(struct at91_adc_trigger), | ||
503 | GFP_KERNEL); | ||
504 | if (!st->trigger_list) { | ||
505 | dev_err(&idev->dev, "Could not allocate trigger list memory.\n"); | ||
506 | ret = -ENOMEM; | ||
507 | goto error_ret; | ||
508 | } | ||
509 | |||
510 | for_each_child_of_node(node, trig_node) { | ||
511 | struct at91_adc_trigger *trig = st->trigger_list + i; | ||
512 | const char *name; | ||
513 | |||
514 | if (of_property_read_string(trig_node, "trigger-name", &name)) { | ||
515 | dev_err(&idev->dev, "Missing trigger-name property in the DT.\n"); | ||
516 | ret = -EINVAL; | ||
517 | goto error_ret; | ||
518 | } | ||
519 | trig->name = name; | ||
520 | |||
521 | if (of_property_read_u32(trig_node, "trigger-value", &prop)) { | ||
522 | dev_err(&idev->dev, "Missing trigger-value property in the DT.\n"); | ||
523 | ret = -EINVAL; | ||
524 | goto error_ret; | ||
525 | } | ||
526 | trig->value = prop; | ||
527 | trig->is_external = of_property_read_bool(trig_node, "trigger-external"); | ||
528 | i++; | ||
529 | } | ||
530 | |||
531 | return 0; | ||
532 | |||
533 | error_ret: | ||
534 | return ret; | ||
535 | } | ||
536 | |||
418 | static int at91_adc_probe_pdata(struct at91_adc_state *st, | 537 | static int at91_adc_probe_pdata(struct at91_adc_state *st, |
419 | struct platform_device *pdev) | 538 | struct platform_device *pdev) |
420 | { | 539 | { |
@@ -456,7 +575,11 @@ static int __devinit at91_adc_probe(struct platform_device *pdev) | |||
456 | 575 | ||
457 | st = iio_priv(idev); | 576 | st = iio_priv(idev); |
458 | 577 | ||
459 | ret = at91_adc_probe_pdata(st, pdev); | 578 | if (pdev->dev.of_node) |
579 | ret = at91_adc_probe_dt(st, pdev); | ||
580 | else | ||
581 | ret = at91_adc_probe_pdata(st, pdev); | ||
582 | |||
460 | if (ret) { | 583 | if (ret) { |
461 | dev_err(&pdev->dev, "No platform data available.\n"); | 584 | dev_err(&pdev->dev, "No platform data available.\n"); |
462 | ret = -EINVAL; | 585 | ret = -EINVAL; |
@@ -657,11 +780,18 @@ static int __devexit at91_adc_remove(struct platform_device *pdev) | |||
657 | return 0; | 780 | return 0; |
658 | } | 781 | } |
659 | 782 | ||
783 | static const struct of_device_id at91_adc_dt_ids[] = { | ||
784 | { .compatible = "atmel,at91sam9260-adc" }, | ||
785 | {}, | ||
786 | }; | ||
787 | MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); | ||
788 | |||
660 | static struct platform_driver at91_adc_driver = { | 789 | static struct platform_driver at91_adc_driver = { |
661 | .probe = at91_adc_probe, | 790 | .probe = at91_adc_probe, |
662 | .remove = __devexit_p(at91_adc_remove), | 791 | .remove = __devexit_p(at91_adc_remove), |
663 | .driver = { | 792 | .driver = { |
664 | .name = "at91_adc", | 793 | .name = "at91_adc", |
794 | .of_match_table = of_match_ptr(at91_adc_dt_ids), | ||
665 | }, | 795 | }, |
666 | }; | 796 | }; |
667 | 797 | ||