Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: light: vcnl4000: Add interrupt support for vcnl4040

Add support to configure proximity sensor interrupts and threshold
limits for vcnl4040. If an interrupt is detected an event will be
pushed to the event interface.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20230117190017.3789181-4-marten.lindahl@axis.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Mårten Lindahl and committed by
Jonathan Cameron
54667612 bfb6cfee

+169
+169
drivers/iio/light/vcnl4000.c
··· 60 60 61 61 #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 62 62 #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 63 + #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ 64 + #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ 63 65 #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 64 66 #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 67 + #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ 65 68 #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 66 69 67 70 #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ ··· 81 78 #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 82 79 #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 83 80 #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 81 + #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 82 + #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 83 + #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 84 84 85 85 /* Bit masks for interrupt registers. */ 86 86 #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ ··· 144 138 enum vcnl4000_device_ids id; 145 139 int rev; 146 140 int al_scale; 141 + u8 ps_int; /* proximity interrupt mode */ 147 142 const struct vcnl4000_chip_spec *chip_spec; 148 143 struct mutex vcnl4000_lock; 149 144 struct vcnl4200_channel vcnl4200_al; ··· 263 256 { 264 257 int ret; 265 258 259 + /* Do not power down if interrupts are enabled */ 260 + if (!on && data->ps_int) 261 + return 0; 262 + 266 263 ret = vcnl4000_write_als_enable(data, on); 267 264 if (ret < 0) 268 265 return ret; ··· 308 297 dev_dbg(&data->client->dev, "device id 0x%x", id); 309 298 310 299 data->rev = (ret >> 8) & 0xf; 300 + data->ps_int = 0; 311 301 312 302 data->vcnl4200_al.reg = VCNL4200_AL_DATA; 313 303 data->vcnl4200_ps.reg = VCNL4200_PS_DATA; ··· 809 797 } 810 798 } 811 799 800 + static int vcnl4040_read_event(struct iio_dev *indio_dev, 801 + const struct iio_chan_spec *chan, 802 + enum iio_event_type type, 803 + enum iio_event_direction dir, 804 + enum iio_event_info info, 805 + int *val, int *val2) 806 + { 807 + int ret; 808 + struct vcnl4000_data *data = iio_priv(indio_dev); 809 + 810 + switch (dir) { 811 + case IIO_EV_DIR_RISING: 812 + ret = i2c_smbus_read_word_data(data->client, 813 + VCNL4040_PS_THDH_LM); 814 + if (ret < 0) 815 + return ret; 816 + *val = ret; 817 + return IIO_VAL_INT; 818 + case IIO_EV_DIR_FALLING: 819 + ret = i2c_smbus_read_word_data(data->client, 820 + VCNL4040_PS_THDL_LM); 821 + if (ret < 0) 822 + return ret; 823 + *val = ret; 824 + return IIO_VAL_INT; 825 + default: 826 + return -EINVAL; 827 + } 828 + } 829 + 830 + static int vcnl4040_write_event(struct iio_dev *indio_dev, 831 + const struct iio_chan_spec *chan, 832 + enum iio_event_type type, 833 + enum iio_event_direction dir, 834 + enum iio_event_info info, 835 + int val, int val2) 836 + { 837 + int ret; 838 + struct vcnl4000_data *data = iio_priv(indio_dev); 839 + 840 + switch (dir) { 841 + case IIO_EV_DIR_RISING: 842 + ret = i2c_smbus_write_word_data(data->client, 843 + VCNL4040_PS_THDH_LM, val); 844 + if (ret < 0) 845 + return ret; 846 + return IIO_VAL_INT; 847 + case IIO_EV_DIR_FALLING: 848 + ret = i2c_smbus_write_word_data(data->client, 849 + VCNL4040_PS_THDL_LM, val); 850 + if (ret < 0) 851 + return ret; 852 + return IIO_VAL_INT; 853 + default: 854 + return -EINVAL; 855 + } 856 + } 857 + 812 858 static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 813 859 { 814 860 int ret; ··· 947 877 default: 948 878 return -EINVAL; 949 879 } 880 + } 881 + 882 + static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 883 + const struct iio_chan_spec *chan, 884 + enum iio_event_type type, 885 + enum iio_event_direction dir) 886 + { 887 + int ret; 888 + struct vcnl4000_data *data = iio_priv(indio_dev); 889 + 890 + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 891 + if (ret < 0) 892 + return ret; 893 + 894 + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 895 + 896 + return (dir == IIO_EV_DIR_RISING) ? 897 + FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 898 + FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 899 + } 900 + 901 + static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 902 + const struct iio_chan_spec *chan, 903 + enum iio_event_type type, 904 + enum iio_event_direction dir, int state) 905 + { 906 + int ret; 907 + u16 val, mask; 908 + struct vcnl4000_data *data = iio_priv(indio_dev); 909 + 910 + mutex_lock(&data->vcnl4000_lock); 911 + 912 + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 913 + if (ret < 0) 914 + goto out; 915 + 916 + if (dir == IIO_EV_DIR_RISING) 917 + mask = VCNL4040_PS_IF_AWAY; 918 + else 919 + mask = VCNL4040_PS_IF_CLOSE; 920 + 921 + val = state ? (ret | mask) : (ret & ~mask); 922 + 923 + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 924 + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 925 + 926 + out: 927 + mutex_unlock(&data->vcnl4000_lock); 928 + data->chip_spec->set_power_state(data, data->ps_int != 0); 929 + 930 + return ret; 931 + } 932 + 933 + static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 934 + { 935 + struct iio_dev *indio_dev = p; 936 + struct vcnl4000_data *data = iio_priv(indio_dev); 937 + int ret; 938 + 939 + ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS); 940 + if (ret < 0) 941 + return IRQ_HANDLED; 942 + 943 + if (ret & VCNL4040_PS_IF_CLOSE) { 944 + iio_push_event(indio_dev, 945 + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 946 + IIO_EV_TYPE_THRESH, 947 + IIO_EV_DIR_RISING), 948 + iio_get_time_ns(indio_dev)); 949 + } 950 + 951 + if (ret & VCNL4040_PS_IF_AWAY) { 952 + iio_push_event(indio_dev, 953 + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 954 + IIO_EV_TYPE_THRESH, 955 + IIO_EV_DIR_FALLING), 956 + iio_get_time_ns(indio_dev)); 957 + } 958 + 959 + return IRQ_HANDLED; 950 960 } 951 961 952 962 static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, ··· 1192 1042 } 1193 1043 }; 1194 1044 1045 + static const struct iio_event_spec vcnl4040_event_spec[] = { 1046 + { 1047 + .type = IIO_EV_TYPE_THRESH, 1048 + .dir = IIO_EV_DIR_RISING, 1049 + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 1050 + }, { 1051 + .type = IIO_EV_TYPE_THRESH, 1052 + .dir = IIO_EV_DIR_FALLING, 1053 + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 1054 + }, 1055 + }; 1056 + 1195 1057 static const struct iio_chan_spec vcnl4000_channels[] = { 1196 1058 { 1197 1059 .type = IIO_LIGHT, ··· 1252 1090 BIT(IIO_CHAN_INFO_INT_TIME), 1253 1091 .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 1254 1092 .ext_info = vcnl4000_ext_info, 1093 + .event_spec = vcnl4040_event_spec, 1094 + .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 1255 1095 } 1256 1096 }; 1257 1097 ··· 1274 1110 static const struct iio_info vcnl4040_info = { 1275 1111 .read_raw = vcnl4000_read_raw, 1276 1112 .write_raw = vcnl4040_write_raw, 1113 + .read_event_value = vcnl4040_read_event, 1114 + .write_event_value = vcnl4040_write_event, 1115 + .read_event_config = vcnl4040_read_event_config, 1116 + .write_event_config = vcnl4040_write_event_config, 1277 1117 .read_avail = vcnl4040_read_avail, 1278 1118 }; 1279 1119 ··· 1314 1146 .channels = vcnl4040_channels, 1315 1147 .num_channels = ARRAY_SIZE(vcnl4040_channels), 1316 1148 .info = &vcnl4040_info, 1149 + .irq_thread = vcnl4040_irq_thread, 1317 1150 }, 1318 1151 [VCNL4200] = { 1319 1152 .prod = "VCNL4200",