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