1 /*
2 * Copyright 2023 Google LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ams_tcs3400
8
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/sensor/tcs3400.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <zephyr/sys/util.h>
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(tcs3400, CONFIG_SENSOR_LOG_LEVEL);
18
19 #define TCS3400_ENABLE_REG 0x80
20 #define TCS3400_ENABLE_AIEN BIT(4)
21 #define TCS3400_ENABLE_AEN BIT(1)
22 #define TCS3400_ENABLE_PON BIT(0)
23
24 #define TCS3400_ATIME_REG 0x81
25
26 #define TCS3400_PERS_REG 0x8c
27
28 #define TCS3400_CONFIG_REG 0x8d
29
30 #define TCS3400_CONTROL_REG 0x8f
31
32 #define TCS3400_ID_REG 0x92
33 #define TCS3400_ID_1 0x90
34 #define TCS3400_ID_2 0x93
35
36 #define TCS3400_STATUS_REG 0x93
37 #define TCS3400_STATUS_AVALID BIT(0)
38
39 #define TCS3400_CDATAL_REG 0x94
40 #define TCS3400_CDATAH_REG 0x95
41 #define TCS3400_RDATAL_REG 0x96
42 #define TCS3400_RDATAH_REG 0x97
43 #define TCS3400_GDATAL_REG 0x98
44 #define TCS3400_GDATAH_REG 0x99
45 #define TCS3400_BDATAL_REG 0x9A
46 #define TCS3400_BDATAH_REG 0x9B
47
48 #define TCS3400_AICLEAR_REG 0xe7
49
50 /* Default values */
51 #define TCS3400_DEFAULT_ENABLE 0x00
52 #define TCS3400_DEFAULT_ATIME 0xff
53 #define TCS3400_DEFAULT_PERS 0x00
54 #define TCS3400_DEFAULT_CONFIG 0x00
55 #define TCS3400_DEFAULT_CONTROL 0x00
56 #define TCS3400_AICLEAR_RESET 0x00
57
58 struct tcs3400_config {
59 struct i2c_dt_spec i2c;
60 struct gpio_dt_spec int_gpio;
61 };
62
63 struct tcs3400_data {
64 struct gpio_callback gpio_cb;
65 const struct device *dev;
66
67 uint16_t sample_crgb[4];
68
69 struct k_sem data_sem;
70 };
71
tcs3400_setup_int(const struct tcs3400_config * config,bool enable)72 static void tcs3400_setup_int(const struct tcs3400_config *config, bool enable)
73 {
74 unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
75
76 gpio_pin_interrupt_configure_dt(&config->int_gpio, flags);
77 }
78
tcs3400_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)79 static void tcs3400_gpio_callback(const struct device *dev,
80 struct gpio_callback *cb, uint32_t pins)
81 {
82 struct tcs3400_data *data = CONTAINER_OF(cb, struct tcs3400_data,
83 gpio_cb);
84
85 tcs3400_setup_int(data->dev->config, false);
86
87 k_sem_give(&data->data_sem);
88 }
89
tcs3400_sample_fetch(const struct device * dev,enum sensor_channel chan)90 static int tcs3400_sample_fetch(const struct device *dev,
91 enum sensor_channel chan)
92 {
93 const struct tcs3400_config *cfg = dev->config;
94 struct tcs3400_data *data = dev->data;
95 int ret;
96 uint8_t status;
97
98 if (chan != SENSOR_CHAN_ALL) {
99 LOG_ERR("Unsupported sensor channel");
100 return -ENOTSUP;
101 }
102
103 tcs3400_setup_int(cfg, true);
104
105 ret = i2c_reg_write_byte_dt(&cfg->i2c, TCS3400_ENABLE_REG,
106 TCS3400_ENABLE_AIEN | TCS3400_ENABLE_AEN |
107 TCS3400_ENABLE_PON);
108 if (ret) {
109 return ret;
110 }
111
112 k_sem_take(&data->data_sem, K_FOREVER);
113
114 ret = i2c_reg_read_byte_dt(&cfg->i2c, TCS3400_STATUS_REG, &status);
115 if (ret) {
116 return ret;
117 }
118
119 if (status & TCS3400_STATUS_AVALID) {
120 ret = i2c_burst_read_dt(&cfg->i2c, TCS3400_CDATAL_REG,
121 (uint8_t *)&data->sample_crgb,
122 sizeof(data->sample_crgb));
123 if (ret) {
124 return ret;
125 }
126 } else {
127 LOG_ERR("Unexpected status: %02x", status);
128 }
129
130 ret = i2c_reg_write_byte_dt(&cfg->i2c, TCS3400_ENABLE_REG, 0);
131 if (ret) {
132 return ret;
133 }
134
135 ret = i2c_reg_write_byte_dt(&cfg->i2c, TCS3400_AICLEAR_REG, 0);
136 if (ret) {
137 return ret;
138 }
139
140 return 0;
141 }
142
tcs3400_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)143 static int tcs3400_channel_get(const struct device *dev,
144 enum sensor_channel chan,
145 struct sensor_value *val)
146 {
147 struct tcs3400_data *data = dev->data;
148
149 switch (chan) {
150 case SENSOR_CHAN_LIGHT:
151 val->val1 = sys_le16_to_cpu(data->sample_crgb[0]);
152 val->val2 = 0;
153 break;
154 case SENSOR_CHAN_RED:
155 val->val1 = sys_le16_to_cpu(data->sample_crgb[1]);
156 val->val2 = 0;
157 break;
158 case SENSOR_CHAN_GREEN:
159 val->val1 = sys_le16_to_cpu(data->sample_crgb[2]);
160 val->val2 = 0;
161 break;
162 case SENSOR_CHAN_BLUE:
163 val->val1 = sys_le16_to_cpu(data->sample_crgb[3]);
164 val->val2 = 0;
165 break;
166 default:
167 return -ENOTSUP;
168 }
169
170 return 0;
171 }
172
tcs3400_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)173 static int tcs3400_attr_set(const struct device *dev,
174 enum sensor_channel chan,
175 enum sensor_attribute attr,
176 const struct sensor_value *val)
177 {
178 const struct tcs3400_config *cfg = dev->config;
179 int ret;
180 uint8_t reg_val;
181
182 switch (attr) {
183 case SENSOR_ATTR_TCS3400_INTEGRATION_CYCLES:
184 if (!IN_RANGE(val->val1, 1, 256)) {
185 return -EINVAL;
186 }
187 reg_val = UINT8_MAX - val->val1 + 1;
188 ret = i2c_reg_write_byte_dt(&cfg->i2c,
189 TCS3400_ATIME_REG, reg_val);
190 if (ret) {
191 return ret;
192 }
193 break;
194 default:
195 return -ENOTSUP;
196 }
197
198 return 0;
199 }
200
tcs3400_sensor_setup(const struct device * dev)201 static int tcs3400_sensor_setup(const struct device *dev)
202 {
203 const struct tcs3400_config *cfg = dev->config;
204 uint8_t chip_id;
205 int ret;
206 struct {
207 uint8_t reg_addr;
208 uint8_t value;
209 } reset_regs[] = {
210 {TCS3400_ENABLE_REG, TCS3400_DEFAULT_ENABLE},
211 {TCS3400_AICLEAR_REG, TCS3400_AICLEAR_RESET},
212 {TCS3400_ATIME_REG, TCS3400_DEFAULT_ATIME},
213 {TCS3400_PERS_REG, TCS3400_DEFAULT_PERS},
214 {TCS3400_CONFIG_REG, TCS3400_DEFAULT_CONFIG},
215 {TCS3400_CONTROL_REG, TCS3400_DEFAULT_CONTROL},
216 };
217
218 ret = i2c_reg_read_byte_dt(&cfg->i2c, TCS3400_ID_REG, &chip_id);
219 if (ret) {
220 LOG_DBG("Failed to read chip id: %d", ret);
221 return ret;
222 }
223
224 if (!((chip_id == TCS3400_ID_1) || (chip_id == TCS3400_ID_2))) {
225 LOG_DBG("Invalid chip id: %02x", chip_id);
226 return -EIO;
227 }
228
229 LOG_INF("chip id: 0x%x", chip_id);
230
231 for (size_t i = 0; i < ARRAY_SIZE(reset_regs); i++) {
232 ret = i2c_reg_write_byte_dt(&cfg->i2c, reset_regs[i].reg_addr,
233 reset_regs[i].value);
234 if (ret) {
235 LOG_ERR("Failed to set default register: %02x",
236 reset_regs[i].reg_addr);
237 return ret;
238 }
239 }
240
241 return 0;
242 }
243
244 static const struct sensor_driver_api tcs3400_api = {
245 .sample_fetch = tcs3400_sample_fetch,
246 .channel_get = tcs3400_channel_get,
247 .attr_set = tcs3400_attr_set,
248 };
249
tcs3400_init(const struct device * dev)250 static int tcs3400_init(const struct device *dev)
251 {
252 const struct tcs3400_config *cfg = dev->config;
253 struct tcs3400_data *data = dev->data;
254 int ret;
255
256 k_sem_init(&data->data_sem, 0, K_SEM_MAX_LIMIT);
257 data->dev = dev;
258
259 if (!i2c_is_ready_dt(&cfg->i2c)) {
260 LOG_ERR("I2C bus is not ready");
261 return -ENODEV;
262 }
263
264 ret = tcs3400_sensor_setup(dev);
265 if (ret < 0) {
266 LOG_ERR("Failed to setup device");
267 return ret;
268 }
269
270 if (!gpio_is_ready_dt(&cfg->int_gpio)) {
271 LOG_ERR("Interrupt GPIO device not ready");
272 return -ENODEV;
273 }
274
275 ret = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
276 if (ret < 0) {
277 LOG_ERR("Failed to configure interrupt pin");
278 return ret;
279 }
280
281 gpio_init_callback(&data->gpio_cb, tcs3400_gpio_callback,
282 BIT(cfg->int_gpio.pin));
283
284 ret = gpio_add_callback(cfg->int_gpio.port, &data->gpio_cb);
285 if (ret < 0) {
286 LOG_ERR("Failed to set GPIO callback");
287 return ret;
288 }
289
290 return 0;
291 }
292
293 #define TCS3400_INIT(n) \
294 static struct tcs3400_data tcs3400_data_##n; \
295 static const struct tcs3400_config tcs3400_config_##n = { \
296 .i2c = I2C_DT_SPEC_INST_GET(n), \
297 .int_gpio = GPIO_DT_SPEC_INST_GET(n, int_gpios), \
298 }; \
299 SENSOR_DEVICE_DT_INST_DEFINE(n, &tcs3400_init, NULL, \
300 &tcs3400_data_##n, &tcs3400_config_##n, \
301 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
302 &tcs3400_api);
303
304 DT_INST_FOREACH_STATUS_OKAY(TCS3400_INIT)
305