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, &current_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