diff options
author | Adriana Reus <adriana.reus@intel.com> | 2015-12-14 07:24:46 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2015-12-22 13:07:05 -0500 |
commit | b6695254f800698faee4f30a8f0b199459ebeafe (patch) | |
tree | e91bfc8c5cc24a734f1899c53a2a7576a9f8f5d6 | |
parent | 58e9042f5f130bf3f9043cf75aa2632fc12140e6 (diff) |
iio: light: us5182d: Add interrupt support and events
Add interrupt support for proximity.
Add two threshold events to signal rising and falling directions.
Signed-off-by: Adriana Reus <adriana.reus@intel.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/light/us5182d.c | 271 |
1 files changed, 270 insertions, 1 deletions
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index f24b687bd152..213f7785095f 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c | |||
@@ -20,7 +20,10 @@ | |||
20 | #include <linux/acpi.h> | 20 | #include <linux/acpi.h> |
21 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
22 | #include <linux/i2c.h> | 22 | #include <linux/i2c.h> |
23 | #include <linux/iio/events.h> | ||
23 | #include <linux/iio/iio.h> | 24 | #include <linux/iio/iio.h> |
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/irq.h> | ||
24 | #include <linux/iio/sysfs.h> | 27 | #include <linux/iio/sysfs.h> |
25 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
26 | #include <linux/pm.h> | 29 | #include <linux/pm.h> |
@@ -30,6 +33,8 @@ | |||
30 | #define US5182D_CFG0_ONESHOT_EN BIT(6) | 33 | #define US5182D_CFG0_ONESHOT_EN BIT(6) |
31 | #define US5182D_CFG0_SHUTDOWN_EN BIT(7) | 34 | #define US5182D_CFG0_SHUTDOWN_EN BIT(7) |
32 | #define US5182D_CFG0_WORD_ENABLE BIT(0) | 35 | #define US5182D_CFG0_WORD_ENABLE BIT(0) |
36 | #define US5182D_CFG0_PROX BIT(3) | ||
37 | #define US5182D_CFG0_PX_IRQ BIT(2) | ||
33 | 38 | ||
34 | #define US5182D_REG_CFG1 0x01 | 39 | #define US5182D_REG_CFG1 0x01 |
35 | #define US5182D_CFG1_ALS_RES16 BIT(4) | 40 | #define US5182D_CFG1_ALS_RES16 BIT(4) |
@@ -41,6 +46,7 @@ | |||
41 | 46 | ||
42 | #define US5182D_REG_CFG3 0x03 | 47 | #define US5182D_REG_CFG3 0x03 |
43 | #define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5)) | 48 | #define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5)) |
49 | #define US5182D_CFG3_INT_SOURCE_PX BIT(3) | ||
44 | 50 | ||
45 | #define US5182D_REG_CFG4 0x10 | 51 | #define US5182D_REG_CFG4 0x10 |
46 | 52 | ||
@@ -55,6 +61,13 @@ | |||
55 | #define US5182D_REG_AUTO_LDARK_GAIN 0x29 | 61 | #define US5182D_REG_AUTO_LDARK_GAIN 0x29 |
56 | #define US5182D_REG_AUTO_HDARK_GAIN 0x2a | 62 | #define US5182D_REG_AUTO_HDARK_GAIN 0x2a |
57 | 63 | ||
64 | /* Thresholds for events: px low (0x08-l, 0x09-h), px high (0x0a-l 0x0b-h) */ | ||
65 | #define US5182D_REG_PXL_TH 0x08 | ||
66 | #define US5182D_REG_PXH_TH 0x0a | ||
67 | |||
68 | #define US5182D_REG_PXL_TH_DEFAULT 1000 | ||
69 | #define US5182D_REG_PXH_TH_DEFAULT 30000 | ||
70 | |||
58 | #define US5182D_OPMODE_ALS 0x01 | 71 | #define US5182D_OPMODE_ALS 0x01 |
59 | #define US5182D_OPMODE_PX 0x02 | 72 | #define US5182D_OPMODE_PX 0x02 |
60 | #define US5182D_OPMODE_SHIFT 4 | 73 | #define US5182D_OPMODE_SHIFT 4 |
@@ -84,6 +97,8 @@ | |||
84 | #define US5182D_READ_WORD 2 | 97 | #define US5182D_READ_WORD 2 |
85 | #define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */ | 98 | #define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */ |
86 | #define US5182D_SLEEP_MS 3000 /* ms */ | 99 | #define US5182D_SLEEP_MS 3000 /* ms */ |
100 | #define US5182D_PXH_TH_DISABLE 0xffff | ||
101 | #define US5182D_PXL_TH_DISABLE 0x0000 | ||
87 | 102 | ||
88 | /* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */ | 103 | /* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */ |
89 | static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600, | 104 | static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600, |
@@ -119,6 +134,12 @@ struct us5182d_data { | |||
119 | u8 upper_dark_gain; | 134 | u8 upper_dark_gain; |
120 | u16 *us5182d_dark_ths; | 135 | u16 *us5182d_dark_ths; |
121 | 136 | ||
137 | u16 px_low_th; | ||
138 | u16 px_high_th; | ||
139 | |||
140 | int rising_en; | ||
141 | int falling_en; | ||
142 | |||
122 | u8 opmode; | 143 | u8 opmode; |
123 | u8 power_mode; | 144 | u8 power_mode; |
124 | 145 | ||
@@ -148,10 +169,26 @@ static const struct { | |||
148 | {US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16}, | 169 | {US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16}, |
149 | {US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 | | 170 | {US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 | |
150 | US5182D_CFG2_PXGAIN_DEFAULT)}, | 171 | US5182D_CFG2_PXGAIN_DEFAULT)}, |
151 | {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100}, | 172 | {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100 | |
173 | US5182D_CFG3_INT_SOURCE_PX}, | ||
152 | {US5182D_REG_CFG4, 0x00}, | 174 | {US5182D_REG_CFG4, 0x00}, |
153 | }; | 175 | }; |
154 | 176 | ||
177 | static const struct iio_event_spec us5182d_events[] = { | ||
178 | { | ||
179 | .type = IIO_EV_TYPE_THRESH, | ||
180 | .dir = IIO_EV_DIR_RISING, | ||
181 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
182 | BIT(IIO_EV_INFO_ENABLE), | ||
183 | }, | ||
184 | { | ||
185 | .type = IIO_EV_TYPE_THRESH, | ||
186 | .dir = IIO_EV_DIR_FALLING, | ||
187 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
188 | BIT(IIO_EV_INFO_ENABLE), | ||
189 | }, | ||
190 | }; | ||
191 | |||
155 | static const struct iio_chan_spec us5182d_channels[] = { | 192 | static const struct iio_chan_spec us5182d_channels[] = { |
156 | { | 193 | { |
157 | .type = IIO_LIGHT, | 194 | .type = IIO_LIGHT, |
@@ -161,6 +198,8 @@ static const struct iio_chan_spec us5182d_channels[] = { | |||
161 | { | 198 | { |
162 | .type = IIO_PROXIMITY, | 199 | .type = IIO_PROXIMITY, |
163 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | 200 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
201 | .event_spec = us5182d_events, | ||
202 | .num_event_specs = ARRAY_SIZE(us5182d_events), | ||
164 | } | 203 | } |
165 | }; | 204 | }; |
166 | 205 | ||
@@ -487,11 +526,201 @@ static int us5182d_write_raw(struct iio_dev *indio_dev, | |||
487 | return -EINVAL; | 526 | return -EINVAL; |
488 | } | 527 | } |
489 | 528 | ||
529 | static int us5182d_setup_prox(struct iio_dev *indio_dev, | ||
530 | enum iio_event_direction dir, u16 val) | ||
531 | { | ||
532 | struct us5182d_data *data = iio_priv(indio_dev); | ||
533 | |||
534 | if (dir == IIO_EV_DIR_FALLING) | ||
535 | return i2c_smbus_write_word_data(data->client, | ||
536 | US5182D_REG_PXL_TH, val); | ||
537 | else if (dir == IIO_EV_DIR_RISING) | ||
538 | return i2c_smbus_write_word_data(data->client, | ||
539 | US5182D_REG_PXH_TH, val); | ||
540 | |||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | static int us5182d_read_thresh(struct iio_dev *indio_dev, | ||
545 | const struct iio_chan_spec *chan, enum iio_event_type type, | ||
546 | enum iio_event_direction dir, enum iio_event_info info, int *val, | ||
547 | int *val2) | ||
548 | { | ||
549 | struct us5182d_data *data = iio_priv(indio_dev); | ||
550 | |||
551 | switch (dir) { | ||
552 | case IIO_EV_DIR_RISING: | ||
553 | mutex_lock(&data->lock); | ||
554 | *val = data->px_high_th; | ||
555 | mutex_unlock(&data->lock); | ||
556 | break; | ||
557 | case IIO_EV_DIR_FALLING: | ||
558 | mutex_lock(&data->lock); | ||
559 | *val = data->px_low_th; | ||
560 | mutex_unlock(&data->lock); | ||
561 | break; | ||
562 | default: | ||
563 | return -EINVAL; | ||
564 | } | ||
565 | |||
566 | return IIO_VAL_INT; | ||
567 | } | ||
568 | |||
569 | static int us5182d_write_thresh(struct iio_dev *indio_dev, | ||
570 | const struct iio_chan_spec *chan, enum iio_event_type type, | ||
571 | enum iio_event_direction dir, enum iio_event_info info, int val, | ||
572 | int val2) | ||
573 | { | ||
574 | struct us5182d_data *data = iio_priv(indio_dev); | ||
575 | int ret; | ||
576 | |||
577 | if (val < 0 || val > USHRT_MAX || val2 != 0) | ||
578 | return -EINVAL; | ||
579 | |||
580 | switch (dir) { | ||
581 | case IIO_EV_DIR_RISING: | ||
582 | mutex_lock(&data->lock); | ||
583 | if (data->rising_en) { | ||
584 | ret = us5182d_setup_prox(indio_dev, dir, val); | ||
585 | if (ret < 0) | ||
586 | goto err; | ||
587 | } | ||
588 | data->px_high_th = val; | ||
589 | mutex_unlock(&data->lock); | ||
590 | break; | ||
591 | case IIO_EV_DIR_FALLING: | ||
592 | mutex_lock(&data->lock); | ||
593 | if (data->falling_en) { | ||
594 | ret = us5182d_setup_prox(indio_dev, dir, val); | ||
595 | if (ret < 0) | ||
596 | goto err; | ||
597 | } | ||
598 | data->px_low_th = val; | ||
599 | mutex_unlock(&data->lock); | ||
600 | break; | ||
601 | default: | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | |||
605 | return 0; | ||
606 | err: | ||
607 | mutex_unlock(&data->lock); | ||
608 | return ret; | ||
609 | } | ||
610 | |||
611 | static int us5182d_read_event_config(struct iio_dev *indio_dev, | ||
612 | const struct iio_chan_spec *chan, enum iio_event_type type, | ||
613 | enum iio_event_direction dir) | ||
614 | { | ||
615 | struct us5182d_data *data = iio_priv(indio_dev); | ||
616 | int ret; | ||
617 | |||
618 | switch (dir) { | ||
619 | case IIO_EV_DIR_RISING: | ||
620 | mutex_lock(&data->lock); | ||
621 | ret = data->rising_en; | ||
622 | mutex_unlock(&data->lock); | ||
623 | break; | ||
624 | case IIO_EV_DIR_FALLING: | ||
625 | mutex_lock(&data->lock); | ||
626 | ret = data->falling_en; | ||
627 | mutex_unlock(&data->lock); | ||
628 | break; | ||
629 | default: | ||
630 | ret = -EINVAL; | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | static int us5182d_write_event_config(struct iio_dev *indio_dev, | ||
638 | const struct iio_chan_spec *chan, enum iio_event_type type, | ||
639 | enum iio_event_direction dir, int state) | ||
640 | { | ||
641 | struct us5182d_data *data = iio_priv(indio_dev); | ||
642 | int ret; | ||
643 | u16 new_th; | ||
644 | |||
645 | mutex_lock(&data->lock); | ||
646 | |||
647 | switch (dir) { | ||
648 | case IIO_EV_DIR_RISING: | ||
649 | if (data->rising_en == state) { | ||
650 | mutex_unlock(&data->lock); | ||
651 | return 0; | ||
652 | } | ||
653 | new_th = US5182D_PXH_TH_DISABLE; | ||
654 | if (state) { | ||
655 | data->power_mode = US5182D_CONTINUOUS; | ||
656 | ret = us5182d_set_power_state(data, true); | ||
657 | if (ret < 0) | ||
658 | goto err; | ||
659 | ret = us5182d_px_enable(data); | ||
660 | if (ret < 0) | ||
661 | goto err_poweroff; | ||
662 | new_th = data->px_high_th; | ||
663 | } | ||
664 | ret = us5182d_setup_prox(indio_dev, dir, new_th); | ||
665 | if (ret < 0) | ||
666 | goto err_poweroff; | ||
667 | data->rising_en = state; | ||
668 | break; | ||
669 | case IIO_EV_DIR_FALLING: | ||
670 | if (data->falling_en == state) { | ||
671 | mutex_unlock(&data->lock); | ||
672 | return 0; | ||
673 | } | ||
674 | new_th = US5182D_PXL_TH_DISABLE; | ||
675 | if (state) { | ||
676 | data->power_mode = US5182D_CONTINUOUS; | ||
677 | ret = us5182d_set_power_state(data, true); | ||
678 | if (ret < 0) | ||
679 | goto err; | ||
680 | ret = us5182d_px_enable(data); | ||
681 | if (ret < 0) | ||
682 | goto err_poweroff; | ||
683 | new_th = data->px_low_th; | ||
684 | } | ||
685 | ret = us5182d_setup_prox(indio_dev, dir, new_th); | ||
686 | if (ret < 0) | ||
687 | goto err_poweroff; | ||
688 | data->falling_en = state; | ||
689 | break; | ||
690 | default: | ||
691 | ret = -EINVAL; | ||
692 | goto err; | ||
693 | } | ||
694 | |||
695 | if (!state) { | ||
696 | ret = us5182d_set_power_state(data, false); | ||
697 | if (ret < 0) | ||
698 | goto err; | ||
699 | } | ||
700 | |||
701 | if (!data->falling_en && !data->rising_en && !data->default_continuous) | ||
702 | data->power_mode = US5182D_ONESHOT; | ||
703 | |||
704 | mutex_unlock(&data->lock); | ||
705 | return 0; | ||
706 | |||
707 | err_poweroff: | ||
708 | if (state) | ||
709 | us5182d_set_power_state(data, false); | ||
710 | err: | ||
711 | mutex_unlock(&data->lock); | ||
712 | return ret; | ||
713 | } | ||
714 | |||
490 | static const struct iio_info us5182d_info = { | 715 | static const struct iio_info us5182d_info = { |
491 | .driver_module = THIS_MODULE, | 716 | .driver_module = THIS_MODULE, |
492 | .read_raw = us5182d_read_raw, | 717 | .read_raw = us5182d_read_raw, |
493 | .write_raw = us5182d_write_raw, | 718 | .write_raw = us5182d_write_raw, |
494 | .attrs = &us5182d_attr_group, | 719 | .attrs = &us5182d_attr_group, |
720 | .read_event_value = &us5182d_read_thresh, | ||
721 | .write_event_value = &us5182d_write_thresh, | ||
722 | .read_event_config = &us5182d_read_event_config, | ||
723 | .write_event_config = &us5182d_write_event_config, | ||
495 | }; | 724 | }; |
496 | 725 | ||
497 | static int us5182d_reset(struct iio_dev *indio_dev) | 726 | static int us5182d_reset(struct iio_dev *indio_dev) |
@@ -513,6 +742,9 @@ static int us5182d_init(struct iio_dev *indio_dev) | |||
513 | 742 | ||
514 | data->opmode = 0; | 743 | data->opmode = 0; |
515 | data->power_mode = US5182D_CONTINUOUS; | 744 | data->power_mode = US5182D_CONTINUOUS; |
745 | data->px_low_th = US5182D_REG_PXL_TH_DEFAULT; | ||
746 | data->px_high_th = US5182D_REG_PXH_TH_DEFAULT; | ||
747 | |||
516 | for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) { | 748 | for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) { |
517 | ret = i2c_smbus_write_byte_data(data->client, | 749 | ret = i2c_smbus_write_byte_data(data->client, |
518 | us5182d_regvals[i].reg, | 750 | us5182d_regvals[i].reg, |
@@ -583,6 +815,33 @@ static int us5182d_dark_gain_config(struct iio_dev *indio_dev) | |||
583 | US5182D_REG_DARK_AUTO_EN_DEFAULT); | 815 | US5182D_REG_DARK_AUTO_EN_DEFAULT); |
584 | } | 816 | } |
585 | 817 | ||
818 | static irqreturn_t us5182d_irq_thread_handler(int irq, void *private) | ||
819 | { | ||
820 | struct iio_dev *indio_dev = private; | ||
821 | struct us5182d_data *data = iio_priv(indio_dev); | ||
822 | enum iio_event_direction dir; | ||
823 | int ret; | ||
824 | u64 ev; | ||
825 | |||
826 | ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0); | ||
827 | if (ret < 0) { | ||
828 | dev_err(&data->client->dev, "i2c transfer error in irq\n"); | ||
829 | return IRQ_HANDLED; | ||
830 | } | ||
831 | |||
832 | dir = ret & US5182D_CFG0_PROX ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; | ||
833 | ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, IIO_EV_TYPE_THRESH, dir); | ||
834 | |||
835 | iio_push_event(indio_dev, ev, iio_get_time_ns()); | ||
836 | |||
837 | ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, | ||
838 | ret & ~US5182D_CFG0_PX_IRQ); | ||
839 | if (ret < 0) | ||
840 | dev_err(&data->client->dev, "i2c transfer error in irq\n"); | ||
841 | |||
842 | return IRQ_HANDLED; | ||
843 | } | ||
844 | |||
586 | static int us5182d_probe(struct i2c_client *client, | 845 | static int us5182d_probe(struct i2c_client *client, |
587 | const struct i2c_device_id *id) | 846 | const struct i2c_device_id *id) |
588 | { | 847 | { |
@@ -614,6 +873,16 @@ static int us5182d_probe(struct i2c_client *client, | |||
614 | return (ret < 0) ? ret : -ENODEV; | 873 | return (ret < 0) ? ret : -ENODEV; |
615 | } | 874 | } |
616 | 875 | ||
876 | if (client->irq > 0) { | ||
877 | ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, | ||
878 | us5182d_irq_thread_handler, | ||
879 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
880 | "us5182d-irq", indio_dev); | ||
881 | if (ret < 0) | ||
882 | return ret; | ||
883 | } else | ||
884 | dev_warn(&client->dev, "no valid irq found\n"); | ||
885 | |||
617 | us5182d_get_platform_data(indio_dev); | 886 | us5182d_get_platform_data(indio_dev); |
618 | ret = us5182d_init(indio_dev); | 887 | ret = us5182d_init(indio_dev); |
619 | if (ret < 0) | 888 | if (ret < 0) |