1 /*
2  * Copyright (c) 2024 ITE Corporation. All Rights Reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ite_it8801_kbd
8 
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/mfd/mfd_ite_it8801.h>
12 #include <zephyr/input/input.h>
13 #include <zephyr/input/input_kbd_matrix.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(input_ite_it8801_kbd, CONFIG_INPUT_LOG_LEVEL);
17 
18 struct it8801_mfd_input_altctrl_cfg {
19 	/* GPIO control device structure */
20 	const struct device *gpiocr;
21 	/* GPIO control pin */
22 	uint8_t pin;
23 	/* GPIO function select */
24 	uint8_t alt_func;
25 };
26 
27 struct kbd_it8801_config {
28 	struct input_kbd_matrix_common_config common;
29 	/* IT8801 controller dev */
30 	const struct device *mfd;
31 	/* KSO alternate configuration */
32 	const struct it8801_mfd_input_altctrl_cfg *altctrl;
33 	/* I2C device for the MFD parent */
34 	const struct i2c_dt_spec i2c_dev;
35 	int mfdctrl_len;
36 	uint8_t kso_mapping[DT_INST_PROP(0, col_size)];
37 	/* Keyboard scan out mode control register */
38 	uint8_t reg_ksomcr;
39 	/* Keyboard scan in data register */
40 	uint8_t reg_ksidr;
41 	/* Keyboard scan in edge event register */
42 	uint8_t reg_ksieer;
43 	/* Keyboard scan in interrupt enable register */
44 	uint8_t reg_ksiier;
45 };
46 
47 struct kbd_it8801_data {
48 	struct input_kbd_matrix_common_data common;
49 	struct it8801_mfd_callback it8801_kbd_callback;
50 };
51 
52 INPUT_KBD_STRUCT_CHECK(struct kbd_it8801_config, struct kbd_it8801_data);
53 
kbd_it8801_drive_column(const struct device * dev,int col)54 static void kbd_it8801_drive_column(const struct device *dev, int col)
55 {
56 	const struct kbd_it8801_config *config = dev->config;
57 	int ret;
58 	uint8_t kso_val;
59 
60 	if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE) {
61 		/* Tri-state all outputs. KSO[22:11, 6:0] output high */
62 		kso_val = IT8801_REG_MASK_KSOSDIC | IT8801_REG_MASK_AKSOSC;
63 	} else if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL) {
64 		/* Assert all outputs. KSO[22:11, 6:0] output low */
65 		kso_val = IT8801_REG_MASK_AKSOSC;
66 	} else {
67 		/* Selected KSO[22:11, 6:0] output low, all others KSO output high */
68 		kso_val = config->kso_mapping[col];
69 	}
70 
71 	ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_ksomcr, kso_val);
72 	if (ret != 0) {
73 		LOG_ERR("Failed to drive column (ret %d)", ret);
74 		return;
75 	}
76 }
77 
kbd_it8801_read_row(const struct device * dev)78 static kbd_row_t kbd_it8801_read_row(const struct device *dev)
79 {
80 	const struct kbd_it8801_config *const config = dev->config;
81 	int ret;
82 	uint8_t value;
83 
84 	ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_ksidr, &value);
85 	if (ret != 0) {
86 		LOG_ERR("Failed to read row (ret %d)", ret);
87 	}
88 
89 	/* Bits are active-low, so invert returned levels */
90 	return (~value) & 0xff;
91 }
92 
it8801_input_alert_handler(const struct device * dev)93 static void it8801_input_alert_handler(const struct device *dev)
94 {
95 	const struct kbd_it8801_config *const config = dev->config;
96 	int ret;
97 	uint8_t ksieer_val;
98 
99 	ret = i2c_reg_read_byte_dt(&config->i2c_dev, config->reg_ksieer, &ksieer_val);
100 	if (ret != 0) {
101 		LOG_ERR("Failed to read KBD interrupt status (ret %d)", ret);
102 	}
103 
104 	if (ksieer_val != 0) {
105 		/* Clear pending interrupts */
106 		ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_ksieer, GENMASK(7, 0));
107 		if (ret != 0) {
108 			LOG_ERR("Failed to clear pending interrupts (ret %d)", ret);
109 		}
110 
111 		input_kbd_matrix_poll_start(dev);
112 	}
113 }
114 
kbd_it8801_set_detect_mode(const struct device * dev,bool enable)115 static void kbd_it8801_set_detect_mode(const struct device *dev, bool enable)
116 {
117 	const struct kbd_it8801_config *const config = dev->config;
118 	int ret;
119 
120 	if (enable) {
121 		/* Clear pending interrupts */
122 		ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_ksieer, GENMASK(7, 0));
123 		if (ret != 0) {
124 			LOG_ERR("Failed to clear pending interrupts (ret %d)", ret);
125 			return;
126 		}
127 		/* Enable KSI falling edge event trigger interrupt */
128 		ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_ksiier, GENMASK(7, 0));
129 		if (ret != 0) {
130 			LOG_ERR("Failed to enable KSI event trigger interrupt (ret %d)", ret);
131 			return;
132 		}
133 	} else {
134 		/* Disable KSI falling edge event trigger interrupt */
135 		ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_ksiier, 0x00);
136 		if (ret != 0) {
137 			LOG_ERR("Failed to disable KSI event trigger interrupt (ret %d)", ret);
138 			return;
139 		}
140 	}
141 }
142 
kbd_it8801_init(const struct device * dev)143 static int kbd_it8801_init(const struct device *dev)
144 {
145 	const struct kbd_it8801_config *const config = dev->config;
146 	struct kbd_it8801_data *data = dev->data;
147 	int ret, status;
148 
149 	/* Verify multi-function parent is ready */
150 	if (!device_is_ready(config->mfd)) {
151 		LOG_ERR("(input)%s is not ready", config->mfd->name);
152 		return -ENODEV;
153 	}
154 
155 	for (int i = 0; i < config->mfdctrl_len; i++) {
156 		/* Switching the pin to KSO alternate function (KSO[21:18]) */
157 		status = mfd_it8801_configure_pins(&config->i2c_dev, config->altctrl[i].gpiocr,
158 						   config->altctrl[i].pin,
159 						   config->altctrl[i].alt_func);
160 		if (status != 0) {
161 			LOG_ERR("Failed to configure KSO[21:18] pins");
162 			return status;
163 		}
164 	}
165 
166 	/* Disable wakeup and interrupt of KSI pins before configuring */
167 	kbd_it8801_set_detect_mode(dev, false);
168 
169 	/* Start with KEYBOARD_COLUMN_ALL, KSO[22:11, 6:0] output low */
170 	ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_ksomcr, IT8801_REG_MASK_AKSOSC);
171 	if (ret != 0) {
172 		LOG_ERR("Failed to set all KSO output low (ret %d)", ret);
173 		return ret;
174 	}
175 	/* Gather KSI interrupt enable */
176 	ret = i2c_reg_write_byte_dt(&config->i2c_dev, IT8801_REG_GIECR, IT8801_REG_MASK_GKSIIE);
177 	if (ret != 0) {
178 		LOG_ERR("Failed to enable gather KSI interrupt (ret %d)", ret);
179 		return ret;
180 	}
181 	/* Alert response enable */
182 	ret = i2c_reg_write_byte_dt(&config->i2c_dev, IT8801_REG_SMBCR, IT8801_REG_MASK_ARE);
183 	if (ret != 0) {
184 		LOG_ERR("Failed to enable alert response (ret %d)", ret);
185 		return ret;
186 	}
187 
188 	/* Register the interrupt of IT8801 MFD callback function */
189 	data->it8801_kbd_callback.cb = it8801_input_alert_handler;
190 	data->it8801_kbd_callback.dev = dev;
191 	mfd_it8801_register_interrupt_callback(config->mfd, &data->it8801_kbd_callback);
192 
193 	return input_kbd_matrix_common_init(dev);
194 }
195 
196 static const struct input_kbd_matrix_api kbd_it8801_api = {
197 	.drive_column = kbd_it8801_drive_column,
198 	.read_row = kbd_it8801_read_row,
199 	.set_detect_mode = kbd_it8801_set_detect_mode,
200 };
201 
202 #define INPUT_IT8801_INIT(inst)                                                                    \
203 	INPUT_KBD_MATRIX_DT_INST_DEFINE(inst);                                                     \
204 	PM_DEVICE_DT_INST_DEFINE(inst, input_kbd_matrix_pm_action);                                \
205 	static const struct it8801_mfd_input_altctrl_cfg                                           \
206 		it8801_input_altctrl##inst[IT8801_DT_INST_MFDCTRL_LEN(inst)] =                     \
207 			IT8801_DT_MFD_ITEMS_LIST(inst);                                            \
208 	static struct kbd_it8801_data kbd_it8801_data_##inst;                                      \
209 	static const struct kbd_it8801_config kbd_it8801_cfg_##inst = {                            \
210 		.common = INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT(inst, &kbd_it8801_api),      \
211 		.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)),                                        \
212 		.i2c_dev = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)),                                  \
213 		.altctrl = it8801_input_altctrl##inst,                                             \
214 		.mfdctrl_len = IT8801_DT_INST_MFDCTRL_LEN(inst),                                   \
215 		.kso_mapping = DT_INST_PROP(inst, kso_mapping),                                    \
216 		.reg_ksomcr = DT_INST_REG_ADDR_BY_IDX(inst, 0),                                    \
217 		.reg_ksidr = DT_INST_REG_ADDR_BY_IDX(inst, 1),                                     \
218 		.reg_ksieer = DT_INST_REG_ADDR_BY_IDX(inst, 2),                                    \
219 		.reg_ksiier = DT_INST_REG_ADDR_BY_IDX(inst, 3),                                    \
220 	};                                                                                         \
221                                                                                                    \
222 	DEVICE_DT_INST_DEFINE(inst, &kbd_it8801_init, PM_DEVICE_DT_INST_GET(inst),                 \
223 			      &kbd_it8801_data_##inst, &kbd_it8801_cfg_##inst, POST_KERNEL,        \
224 			      CONFIG_MFD_INIT_PRIORITY, NULL);                                     \
225 	BUILD_ASSERT(IN_RANGE(DT_INST_PROP(inst, row_size), 1, 8), "invalid row-size");            \
226 	BUILD_ASSERT(IN_RANGE(DT_INST_PROP(inst, col_size), 1, 19), "invalid col-size");
227 
228 DT_INST_FOREACH_STATUS_OKAY(INPUT_IT8801_INIT)
229