1 /*
2 * Copyright (c) 2025 Basalte bv
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT cypress_cy8cmbr3xxx
8
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/input/cy8cmbr3xxx.h>
12 #include <zephyr/input/input.h>
13 #include <zephyr/logging/log.h>
14
15 LOG_MODULE_REGISTER(cy8cmbr3xxx, CONFIG_INPUT_LOG_LEVEL);
16
17 #define CY8CMBR3XXX_SENSOR_EN 0x00
18 #define CY8CMBR3XXX_FSS_EN 0x02
19 #define CY8CMBR3XXX_TOGGLE_EN 0x04
20 #define CY8CMBR3XXX_LED_ON_EN 0x06
21 #define CY8CMBR3XXX_SENSITIVITY0 0x08
22 #define CY8CMBR3XXX_SENSITIVITY1 0x09
23 #define CY8CMBR3XXX_SENSITIVITY2 0x0A
24 #define CY8CMBR3XXX_SENSITIVITY3 0x0B
25 #define CY8CMBR3XXX_BASE_THRESHOLD0 0x0C
26 #define CY8CMBR3XXX_BASE_THRESHOLD1 0x0D
27 #define CY8CMBR3XXX_FINGER_THRESHOLD2 0x0E
28 #define CY8CMBR3XXX_FINGER_THRESHOLD3 0x0F
29 #define CY8CMBR3XXX_FINGER_THRESHOLD4 0x10
30 #define CY8CMBR3XXX_FINGER_THRESHOLD5 0x11
31 #define CY8CMBR3XXX_FINGER_THRESHOLD6 0x12
32 #define CY8CMBR3XXX_FINGER_THRESHOLD7 0x13
33 #define CY8CMBR3XXX_FINGER_THRESHOLD8 0x14
34 #define CY8CMBR3XXX_FINGER_THRESHOLD9 0x15
35 #define CY8CMBR3XXX_FINGER_THRESHOLD10 0x16
36 #define CY8CMBR3XXX_FINGER_THRESHOLD11 0x17
37 #define CY8CMBR3XXX_FINGER_THRESHOLD12 0x18
38 #define CY8CMBR3XXX_FINGER_THRESHOLD13 0x19
39 #define CY8CMBR3XXX_FINGER_THRESHOLD14 0x1A
40 #define CY8CMBR3XXX_FINGER_THRESHOLD15 0x1B
41 #define CY8CMBR3XXX_SENSOR_DEBOUNCE 0x1C
42 #define CY8CMBR3XXX_BUTTON_HYS 0x1D
43 #define CY8CMBR3XXX_BUTTON_LBR 0x1F
44 #define CY8CMBR3XXX_BUTTON_NNT 0x20
45 #define CY8CMBR3XXX_BUTTON_NT 0x21
46 #define CY8CMBR3XXX_PROX_EN 0x26
47 #define CY8CMBR3XXX_PROX_CFG 0x27
48 #define CY8CMBR3XXX_PROX_CFG2 0x28
49 #define CY8CMBR3XXX_PROX_TOUCH_TH0 0x2A
50 #define CY8CMBR3XXX_PROX_TOUCH_TH1 0x2C
51 #define CY8CMBR3XXX_PROX_RESOLUTION0 0x2E
52 #define CY8CMBR3XXX_PROX_RESOLUTION1 0x2F
53 #define CY8CMBR3XXX_PROX_HYS 0x30
54 #define CY8CMBR3XXX_PROX_LBR 0x32
55 #define CY8CMBR3XXX_PROX_NNT 0x33
56 #define CY8CMBR3XXX_PROX_NT 0x34
57 #define CY8CMBR3XXX_PROX_POSITIVE_TH0 0x35
58 #define CY8CMBR3XXX_PROX_POSITIVE_TH1 0x36
59 #define CY8CMBR3XXX_PROX_NEGATIVE_TH0 0x39
60 #define CY8CMBR3XXX_PROX_NEGATIVE_TH1 0x3A
61 #define CY8CMBR3XXX_LED_ON_TIME 0x3D
62 #define CY8CMBR3XXX_BUZZER_CFG 0x3E
63 #define CY8CMBR3XXX_BUZZER_ON_TIME 0x3F
64 #define CY8CMBR3XXX_GPO_CFG 0x40
65 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG0 0x41
66 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG1 0x42
67 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG2 0x43
68 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG3 0x44
69 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG4 0x45
70 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG5 0x46
71 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG6 0x47
72 #define CY8CMBR3XXX_PWM_DUTYCYCLE_CFG7 0x48
73 #define CY8CMBR3XXX_SPO_CFG 0x4C
74 #define CY8CMBR3XXX_DEVICE_CFG0 0x4D
75 #define CY8CMBR3XXX_DEVICE_CFG1 0x4E
76 #define CY8CMBR3XXX_DEVICE_CFG2 0x4F
77 #define CY8CMBR3XXX_DEVICE_CFG3 0x50
78 #define CY8CMBR3XXX_I2C_ADDR 0x51
79 #define CY8CMBR3XXX_REFRESH_CTRL 0x52
80 #define CY8CMBR3XXX_STATE_TIMEOUT 0x55
81 #define CY8CMBR3XXX_SLIDER_CFG 0x5D
82 #define CY8CMBR3XXX_SLIDER1_CFG 0x61
83 #define CY8CMBR3XXX_SLIDER1_RESOLUTION 0x62
84 #define CY8CMBR3XXX_SLIDER1_THRESHOLD 0x63
85 #define CY8CMBR3XXX_SLIDER2_CFG 0x67
86 #define CY8CMBR3XXX_SLIDER2_RESOLUTION 0x68
87 #define CY8CMBR3XXX_SLIDER2_THRESHOLD 0x69
88 #define CY8CMBR3XXX_SLIDER_LBR 0x71
89 #define CY8CMBR3XXX_SLIDER_NNT 0x72
90 #define CY8CMBR3XXX_SLIDER_NT 0x73
91 #define CY8CMBR3XXX_SCRATCHPAD0 0x7A
92 #define CY8CMBR3XXX_SCRATCHPAD1 0x7B
93 #define CY8CMBR3XXX_CONFIG_CRC 0x7E
94 #define CY8CMBR3XXX_GPO_OUTPUT_STATE 0x80
95 #define CY8CMBR3XXX_SENSOR_ID 0x82
96 #define CY8CMBR3XXX_CTRL_CMD 0x86
97 #define CY8CMBR3XXX_CTRL_CMD_STATUS 0x88
98 #define CY8CMBR3XXX_CTRL_CMD_ERR 0x89
99 #define CY8CMBR3XXX_SYSTEM_STATUS 0x8A
100 #define CY8CMBR3XXX_PREV_CTRL_CMD_CODE 0x8C
101 #define CY8CMBR3XXX_FAMILY_ID 0x8F
102 #define CY8CMBR3XXX_DEVICE_ID 0x90
103 #define CY8CMBR3XXX_DEVICE_REV 0x92
104 #define CY8CMBR3XXX_CALC_CRC 0x94
105 #define CY8CMBR3XXX_TOTAL_WORKING_SNS 0x97
106 #define CY8CMBR3XXX_SNS_CP_HIGH 0x98
107 #define CY8CMBR3XXX_SNS_VDD_SHORT 0x9A
108 #define CY8CMBR3XXX_SNS_GND_SHORT 0x9C
109 #define CY8CMBR3XXX_SNS_SNS_SHORT 0x9E
110 #define CY8CMBR3XXX_CMOD_SHIELD_TEST 0xA0
111 #define CY8CMBR3XXX_BUTTON_STAT 0xAA
112 #define CY8CMBR3XXX_LATCHED_BUTTON_STAT 0xAC
113 #define CY8CMBR3XXX_PROX_STAT 0xAE
114 #define CY8CMBR3XXX_LATCHED_PROX_STAT 0xAF
115 #define CY8CMBR3XXX_SLIDER1_POSITION 0xB0
116 #define CY8CMBR3XXX_LIFTOFF_SLIDER1_POSITION 0xB1
117 #define CY8CMBR3XXX_SLIDER2_POSITION 0xB2
118 #define CY8CMBR3XXX_LIFTOFF_SLIDER2_POSITION 0xB3
119 #define CY8CMBR3XXX_SYNC_COUNTER0 0xB9
120 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR0 0xBA
121 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR1 0xBC
122 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR2 0xBE
123 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR3 0xC0
124 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR4 0xC2
125 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR5 0xC4
126 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR6 0xC6
127 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR7 0xC8
128 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR8 0xCA
129 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR9 0xCC
130 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR10 0xCE
131 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR11 0xD0
132 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR12 0xD2
133 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR13 0xD4
134 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR14 0xD6
135 #define CY8CMBR3XXX_DIFFERENCE_COUNT_SENSOR15 0xD8
136 #define CY8CMBR3XXX_GPO_DATA 0xDA
137 #define CY8CMBR3XXX_SYNC_COUNTER1 0xDB
138 #define CY8CMBR3XXX_DEBUG_SENSOR_ID 0xDC
139 #define CY8CMBR3XXX_DEBUG_CP 0xDD
140 #define CY8CMBR3XXX_DEBUG_DIFFERENCE_COUNT0 0xDE
141 #define CY8CMBR3XXX_DEBUG_BASELINE0 0xE0
142 #define CY8CMBR3XXX_DEBUG_RAW_COUNT0 0xE2
143 #define CY8CMBR3XXX_DEBUG_AVG_RAW_COUNT0 0xE4
144 #define CY8CMBR3XXX_SYNC_COUNTER2 0xE7
145
146 #define CY8CMBR3XXX_CTRL_CMD_CALC_CRC 0x02
147 #define CY8CMBR3XXX_CTRL_CMD_RESET 0xFF
148
149 /* The controller wakes up from the low-power state on an address match but sends NACK
150 * until it transitions into the Active state. When the device NACKs a transaction, the host is
151 * expected to retry the transaction until it receives an ACK. Typically, no more than 3 retries are
152 * necessary, depending on time between the interrupt and the first i2c transfer or if
153 * no interrupt has caused the initiation of the communication.
154 */
155 #define CY8CMBR3XXX_I2C_RETRIES 5
156
157 struct cy8cmbr3xxx_config {
158 struct i2c_dt_spec i2c;
159 struct gpio_dt_spec int_gpio;
160 struct gpio_dt_spec rst_gpio;
161 const uint16_t *input_codes;
162 uint8_t input_codes_count;
163 const uint16_t *proximity_codes;
164 uint8_t proximity_codes_count;
165 };
166
167 struct cy8cmbr3xxx_data {
168 const struct device *dev;
169 struct k_work work;
170 struct gpio_callback int_gpio_cb;
171 uint16_t prev_button_state;
172 uint8_t prev_proximity_state;
173 };
174
cy8cmbr3xxx_i2c_read(const struct device * dev,uint8_t address,void * buf,size_t len)175 static int cy8cmbr3xxx_i2c_read(const struct device *dev, uint8_t address, void *buf, size_t len)
176 {
177 const struct cy8cmbr3xxx_config *config = dev->config;
178 int ret;
179
180 for (int i = 0; i < CY8CMBR3XXX_I2C_RETRIES; i++) {
181 ret = i2c_write_read_dt(&config->i2c, &address, sizeof(address), buf, len);
182 if (ret == 0) {
183 break;
184 }
185 }
186
187 return ret;
188 }
189
cy8cmbr3xxx_i2c_write(const struct device * dev,uint8_t address,const void * buf,size_t len)190 static int cy8cmbr3xxx_i2c_write(const struct device *dev, uint8_t address, const void *buf,
191 size_t len)
192 {
193 const struct cy8cmbr3xxx_config *config = dev->config;
194 int ret;
195
196 for (int i = 0; i < CY8CMBR3XXX_I2C_RETRIES; i++) {
197 ret = i2c_burst_write_dt(&config->i2c, address, buf, len);
198 if (ret == 0) {
199 break;
200 }
201 }
202
203 return ret;
204 }
205
cy8cmbr3xxx_wait_for_command_completion(const struct device * dev,k_timeout_t timeout)206 static int cy8cmbr3xxx_wait_for_command_completion(const struct device *dev, k_timeout_t timeout)
207 {
208 uint8_t current_command;
209 int ret;
210 k_timepoint_t end = sys_timepoint_calc(timeout);
211
212 do {
213 /* Wait for the completion of the command. After a reset command, it can
214 * happen that the device NACKs for some time.
215 */
216 ret = cy8cmbr3xxx_i2c_read(dev, CY8CMBR3XXX_CTRL_CMD, ¤t_command,
217 sizeof(uint8_t));
218
219 /* As soon as current_command is 0x00, the command is completed */
220 if (ret == 0 && current_command == 0x00) {
221 return 0;
222 }
223
224 k_msleep(1);
225 } while (!sys_timepoint_expired(end));
226
227 LOG_ERR("Wait for command completion timed out");
228
229 return -ETIMEDOUT;
230 }
231
cy8cmbr3xxx_configure(const struct device * dev,const struct cy8cmbr3xxx_config_data * config)232 int cy8cmbr3xxx_configure(const struct device *dev, const struct cy8cmbr3xxx_config_data *config)
233 {
234 int ret;
235 uint8_t read_config[CY8CMBR3XXX_EZ_CLICK_CONFIG_SIZE];
236 uint8_t command;
237
238 if (config == NULL) {
239 return -EINVAL;
240 }
241
242 /* Read the complete configuration */
243 ret = cy8cmbr3xxx_i2c_read(dev, CY8CMBR3XXX_SENSOR_EN, read_config, sizeof(read_config));
244 if (ret < 0) {
245 LOG_ERR("Failed to read i2c (%d)", ret);
246 return ret;
247 }
248
249 if (memcmp(read_config, config->data, sizeof(config->data)) == 0) {
250 return 0;
251 }
252
253 /* Write the complete configuration of 128 bytes to the CY8CMBR3XXX controller */
254 ret = cy8cmbr3xxx_i2c_write(dev, CY8CMBR3XXX_SENSOR_EN, config->data, sizeof(config->data));
255 if (ret < 0) {
256 LOG_ERR("Failed to write i2c (%d)", ret);
257 return ret;
258 }
259
260 /* The device calculates a CRC checksum over the configuration data in this
261 * register map and compares the result with the content of CONFIG_CRC. If the
262 * two values match, the device saves the configuration and the CRC checksum to
263 * nonvolatile memory.
264 */
265 command = CY8CMBR3XXX_CTRL_CMD_CALC_CRC;
266 ret = cy8cmbr3xxx_i2c_write(dev, CY8CMBR3XXX_CTRL_CMD, &command, 1);
267 if (ret < 0) {
268 LOG_ERR("Failed to write i2c (%d)", ret);
269 return ret;
270 }
271
272 /* 600ms seems to be sufficient */
273 ret = cy8cmbr3xxx_wait_for_command_completion(dev, K_MSEC(600));
274 if (ret < 0) {
275 LOG_ERR("Failed to wait for command completion (%d)", ret);
276 return ret;
277 }
278
279 /* The device resets itself */
280 command = CY8CMBR3XXX_CTRL_CMD_RESET;
281 ret = cy8cmbr3xxx_i2c_write(dev, CY8CMBR3XXX_CTRL_CMD, &command, 1);
282 if (ret < 0) {
283 LOG_ERR("Failed to write i2c (%d)", ret);
284 return ret;
285 }
286
287 ret = cy8cmbr3xxx_wait_for_command_completion(dev, K_MSEC(50));
288 if (ret < 0) {
289 LOG_ERR("Failed to wait for command completion (%d)", ret);
290 return ret;
291 }
292
293 return 0;
294 }
295
cy8cmbr3xxx_process(const struct device * dev)296 static int cy8cmbr3xxx_process(const struct device *dev)
297 {
298 const struct cy8cmbr3xxx_config *config = dev->config;
299 struct cy8cmbr3xxx_data *data = dev->data;
300 int ret;
301 uint16_t button_state, single_button_state;
302 uint8_t proximity_state, single_proximity_state;
303
304 /* Request button status */
305 ret = cy8cmbr3xxx_i2c_read(dev, CY8CMBR3XXX_BUTTON_STAT, &button_state, sizeof(uint16_t));
306 if (ret < 0) {
307 LOG_ERR("Failed to read button status (%d)", ret);
308 return ret;
309 }
310
311 for (uint8_t i = 0; i < config->input_codes_count; i++) {
312 single_button_state = button_state & BIT(i);
313 if (single_button_state != (data->prev_button_state & BIT(i))) {
314 input_report_key(dev, config->input_codes[i], single_button_state, true,
315 K_FOREVER);
316 }
317 }
318 data->prev_button_state = button_state;
319
320 /* Request proximity status */
321 if (config->proximity_codes_count > 0) {
322 ret = cy8cmbr3xxx_i2c_read(dev, CY8CMBR3XXX_PROX_STAT, &proximity_state,
323 sizeof(uint8_t));
324 if (ret < 0) {
325 LOG_ERR("Failed to read proximity status (%d)", ret);
326 return ret;
327 }
328
329 for (uint8_t i = 0; i < config->proximity_codes_count; i++) {
330 single_proximity_state = proximity_state & BIT(i);
331 if (single_proximity_state != (data->prev_proximity_state & BIT(i))) {
332 input_report_key(dev, config->proximity_codes[i],
333 single_proximity_state, true, K_FOREVER);
334 }
335 }
336 data->prev_proximity_state = proximity_state;
337 }
338
339 return 0;
340 }
341
cy8cmbr3xxx_work_handler(struct k_work * work)342 static void cy8cmbr3xxx_work_handler(struct k_work *work)
343 {
344 struct cy8cmbr3xxx_data *data = CONTAINER_OF(work, struct cy8cmbr3xxx_data, work);
345
346 cy8cmbr3xxx_process(data->dev);
347 }
348
cy8cmbr3xxx_isr_handler(const struct device * dev,struct gpio_callback * cb,uint32_t pins)349 static void cy8cmbr3xxx_isr_handler(const struct device *dev, struct gpio_callback *cb,
350 uint32_t pins)
351 {
352 struct cy8cmbr3xxx_data *data = CONTAINER_OF(cb, struct cy8cmbr3xxx_data, int_gpio_cb);
353
354 k_work_submit(&data->work);
355 }
356
cy8cmbr3xxx_reset(const struct device * dev)357 static void cy8cmbr3xxx_reset(const struct device *dev)
358 {
359 const struct cy8cmbr3xxx_config *config = dev->config;
360 int ret;
361
362 if (!gpio_is_ready_dt(&config->rst_gpio)) {
363 LOG_ERR("GPIO controller device not ready");
364 return;
365 }
366
367 ret = gpio_pin_configure_dt(&config->rst_gpio, GPIO_OUTPUT_ACTIVE);
368 if (ret < 0) {
369 LOG_ERR("Could not configure reset GPIO pin (%d)", ret);
370 return;
371 }
372
373 k_usleep(5);
374
375 ret = gpio_pin_set_dt(&config->rst_gpio, 0);
376 if (ret < 0) {
377 LOG_ERR("Could not set reset GPIO pin (%d)", ret);
378 return;
379 }
380 }
381
cy8cmbr3xxx_init(const struct device * dev)382 static int cy8cmbr3xxx_init(const struct device *dev)
383 {
384 const struct cy8cmbr3xxx_config *config = dev->config;
385 struct cy8cmbr3xxx_data *data = dev->data;
386 int ret;
387
388 data->dev = dev;
389
390 k_work_init(&data->work, cy8cmbr3xxx_work_handler);
391
392 if (!i2c_is_ready_dt(&config->i2c)) {
393 LOG_ERR("I2C controller device not ready");
394 return -ENODEV;
395 }
396
397 cy8cmbr3xxx_reset(dev);
398
399 if (!gpio_is_ready_dt(&config->int_gpio)) {
400 LOG_ERR("GPIO controller device not ready");
401 return -ENODEV;
402 }
403
404 ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
405 if (ret < 0) {
406 LOG_ERR("Could not configure interrupt GPIO pin (%d)", ret);
407 return ret;
408 }
409
410 ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
411 if (ret < 0) {
412 LOG_ERR("Could not configure GPIO interrupt (%d)", ret);
413 return ret;
414 }
415
416 gpio_init_callback(&data->int_gpio_cb, cy8cmbr3xxx_isr_handler, BIT(config->int_gpio.pin));
417
418 ret = gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb);
419 if (ret < 0) {
420 LOG_ERR("Could not set gpio callback (%d)", ret);
421 return ret;
422 }
423
424 return 0;
425 }
426
427 #define CY8CMBR3XXX_INIT(inst) \
428 static const uint16_t cy8cmbr3xxx_input_codes_##inst[] = DT_INST_PROP(inst, input_codes); \
429 static const uint16_t cy8cmbr3xxx_proximity_codes_##inst[] = \
430 DT_INST_PROP_OR(inst, proximity_codes, {}); \
431 static const struct cy8cmbr3xxx_config cy8cmbr3xxx_config_##inst = { \
432 .i2c = I2C_DT_SPEC_INST_GET(inst), \
433 .int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \
434 .rst_gpio = GPIO_DT_SPEC_INST_GET(inst, rst_gpios), \
435 .input_codes = cy8cmbr3xxx_input_codes_##inst, \
436 .input_codes_count = DT_INST_PROP_LEN(inst, input_codes), \
437 .proximity_codes = cy8cmbr3xxx_proximity_codes_##inst, \
438 .proximity_codes_count = DT_INST_PROP_LEN_OR(inst, proximity_codes, 0), \
439 }; \
440 static struct cy8cmbr3xxx_data cy8cmbr3xxx_data_##inst; \
441 DEVICE_DT_INST_DEFINE(inst, cy8cmbr3xxx_init, NULL, &cy8cmbr3xxx_data_##inst, \
442 &cy8cmbr3xxx_config_##inst, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
443 NULL);
444
445 DT_INST_FOREACH_STATUS_OKAY(CY8CMBR3XXX_INIT)
446