1 /*
2 * Copyright (c) 2019 Intel Corporation
3 * Copyright (c) 2022 Microchip Technology Inc.
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT microchip_xec_kbd
8
9 #include <cmsis_core.h>
10 #include <errno.h>
11 #include <soc.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/pinctrl.h>
14 #include <zephyr/input/input.h>
15 #include <zephyr/input/input_kbd_matrix.h>
16 #include <zephyr/irq.h>
17 #include <zephyr/kernel.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/pm/device.h>
20 #include <zephyr/pm/policy.h>
21 #ifdef CONFIG_SOC_SERIES_MEC172X
22 #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
23 #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
24 #endif
25
26 LOG_MODULE_REGISTER(input_xec_kbd, CONFIG_INPUT_LOG_LEVEL);
27
28 struct xec_kbd_config {
29 struct input_kbd_matrix_common_config common;
30
31 struct kscan_regs *regs;
32 const struct pinctrl_dev_config *pcfg;
33 uint8_t girq;
34 uint8_t girq_pos;
35 #ifdef CONFIG_SOC_SERIES_MEC172X
36 uint8_t pcr_idx;
37 uint8_t pcr_pos;
38 #endif
39 bool wakeup_source;
40 };
41
42 struct xec_kbd_data {
43 struct input_kbd_matrix_common_data common;
44 bool pm_lock_taken;
45 };
46
xec_kbd_clear_girq_status(const struct device * dev)47 static void xec_kbd_clear_girq_status(const struct device *dev)
48 {
49 struct xec_kbd_config const *cfg = dev->config;
50
51 #ifdef CONFIG_SOC_SERIES_MEC172X
52 mchp_xec_ecia_girq_src_clr(cfg->girq, cfg->girq_pos);
53 #else
54 MCHP_GIRQ_SRC(cfg->girq) = BIT(cfg->girq_pos);
55 #endif
56 }
57
xec_kbd_configure_girq(const struct device * dev)58 static void xec_kbd_configure_girq(const struct device *dev)
59 {
60 struct xec_kbd_config const *cfg = dev->config;
61
62 #ifdef CONFIG_SOC_SERIES_MEC172X
63 mchp_xec_ecia_enable(cfg->girq, cfg->girq_pos);
64 #else
65 MCHP_GIRQ_ENSET(cfg->girq) = BIT(cfg->girq_pos);
66 #endif
67 }
68
xec_kbd_clr_slp_en(const struct device * dev)69 static void xec_kbd_clr_slp_en(const struct device *dev)
70 {
71 #ifdef CONFIG_SOC_SERIES_MEC172X
72 struct xec_kbd_config const *cfg = dev->config;
73
74 z_mchp_xec_pcr_periph_sleep(cfg->pcr_idx, cfg->pcr_pos, 0);
75 #else
76 ARG_UNUSED(dev);
77 mchp_pcr_periph_slp_ctrl(PCR_KEYSCAN, 0);
78 #endif
79 }
80
xec_kbd_drive_column(const struct device * dev,int data)81 static void xec_kbd_drive_column(const struct device *dev, int data)
82 {
83 struct xec_kbd_config const *cfg = dev->config;
84 struct kscan_regs *regs = cfg->regs;
85
86 if (data == INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL) {
87 /* KSO output controlled by the KSO_SELECT field */
88 regs->KSO_SEL = MCHP_KSCAN_KSO_ALL;
89 } else if (data == INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE) {
90 /* Keyboard scan disabled. All KSO output buffers disabled */
91 regs->KSO_SEL = MCHP_KSCAN_KSO_EN;
92 } else {
93 /* Assume, ALL was previously set */
94 regs->KSO_SEL = data;
95 }
96 }
97
xec_kbd_read_row(const struct device * dev)98 static kbd_row_t xec_kbd_read_row(const struct device *dev)
99 {
100 struct xec_kbd_config const *cfg = dev->config;
101 struct kscan_regs *regs = cfg->regs;
102
103 /* In this implementation a 1 means key pressed */
104 return ~(regs->KSI_IN & 0xff);
105 }
106
xec_kbd_isr(const struct device * dev)107 static void xec_kbd_isr(const struct device *dev)
108 {
109 xec_kbd_clear_girq_status(dev);
110 irq_disable(DT_INST_IRQN(0));
111
112 input_kbd_matrix_poll_start(dev);
113 }
114
xec_kbd_set_detect_mode(const struct device * dev,bool enabled)115 static void xec_kbd_set_detect_mode(const struct device *dev, bool enabled)
116 {
117 struct xec_kbd_config const *cfg = dev->config;
118 struct xec_kbd_data *data = dev->data;
119 struct kscan_regs *regs = cfg->regs;
120
121 if (enabled) {
122 if (data->pm_lock_taken) {
123 pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE,
124 PM_ALL_SUBSTATES);
125 }
126
127 regs->KSI_STS = MCHP_KSCAN_KSO_SEL_REG_MASK;
128
129 xec_kbd_clear_girq_status(dev);
130 NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
131 irq_enable(DT_INST_IRQN(0));
132 } else {
133 pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE,
134 PM_ALL_SUBSTATES);
135 data->pm_lock_taken = true;
136 }
137 }
138
139 #ifdef CONFIG_PM_DEVICE
xec_kbd_pm_action(const struct device * dev,enum pm_device_action action)140 static int xec_kbd_pm_action(const struct device *dev, enum pm_device_action action)
141 {
142 struct xec_kbd_config const *cfg = dev->config;
143 struct kscan_regs *regs = cfg->regs;
144 int ret;
145
146 ret = input_kbd_matrix_pm_action(dev, action);
147 if (ret < 0) {
148 return ret;
149 }
150
151 if (cfg->wakeup_source) {
152 return 0;
153 }
154
155 switch (action) {
156 case PM_DEVICE_ACTION_RESUME:
157 ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
158 if (ret != 0) {
159 LOG_ERR("XEC KSCAN pinctrl init failed (%d)", ret);
160 return ret;
161 }
162
163 regs->KSO_SEL &= ~BIT(MCHP_KSCAN_KSO_EN_POS);
164 /* Clear status register */
165 regs->KSI_STS = MCHP_KSCAN_KSO_SEL_REG_MASK;
166 regs->KSI_IEN = MCHP_KSCAN_KSI_IEN_REG_MASK;
167 break;
168
169 case PM_DEVICE_ACTION_SUSPEND:
170 regs->KSO_SEL |= BIT(MCHP_KSCAN_KSO_EN_POS);
171 regs->KSI_IEN = (~MCHP_KSCAN_KSI_IEN_REG_MASK);
172 ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
173 if (ret != -ENOENT) {
174 /* pinctrl-1 does not exist */
175 return ret;
176 }
177 break;
178
179 default:
180 return -ENOTSUP;
181 }
182
183 return 0;
184 }
185 #endif /* CONFIG_PM_DEVICE */
186
xec_kbd_init(const struct device * dev)187 static int xec_kbd_init(const struct device *dev)
188 {
189 struct xec_kbd_config const *cfg = dev->config;
190 struct kscan_regs *regs = cfg->regs;
191 int ret;
192
193 ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
194 if (ret != 0) {
195 LOG_ERR("XEC KSCAN pinctrl init failed (%d)", ret);
196 return ret;
197 }
198
199 xec_kbd_clr_slp_en(dev);
200
201 /* Enable predrive */
202 regs->KSO_SEL |= BIT(MCHP_KSCAN_KSO_EN_POS);
203 regs->EXT_CTRL = MCHP_KSCAN_EXT_CTRL_PREDRV_EN;
204 regs->KSO_SEL &= ~BIT(MCHP_KSCAN_KSO_EN_POS);
205 regs->KSI_IEN = MCHP_KSCAN_KSI_IEN_REG_MASK;
206
207 /* Interrupts are enabled in the thread function */
208 IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
209 xec_kbd_isr, DEVICE_DT_INST_GET(0), 0);
210
211 xec_kbd_clear_girq_status(dev);
212 xec_kbd_configure_girq(dev);
213
214 return input_kbd_matrix_common_init(dev);
215 }
216
217 PINCTRL_DT_INST_DEFINE(0);
218
219 PM_DEVICE_DT_INST_DEFINE(0, xec_kbd_pm_action);
220
221 INPUT_KBD_MATRIX_DT_INST_DEFINE(0);
222
223 static const struct input_kbd_matrix_api xec_kbd_api = {
224 .drive_column = xec_kbd_drive_column,
225 .read_row = xec_kbd_read_row,
226 .set_detect_mode = xec_kbd_set_detect_mode,
227 };
228
229 /* To enable wakeup, set the "wakeup-source" on the keyboard scanning device
230 * node.
231 */
232 static struct xec_kbd_config xec_kbd_cfg_0 = {
233 .common = INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT(0, &xec_kbd_api),
234 .regs = (struct kscan_regs *)(DT_INST_REG_ADDR(0)),
235 .girq = DT_INST_PROP_BY_IDX(0, girqs, 0),
236 .girq_pos = DT_INST_PROP_BY_IDX(0, girqs, 1),
237 #ifdef CONFIG_SOC_SERIES_MEC172X
238 .pcr_idx = DT_INST_PROP_BY_IDX(0, pcrs, 0),
239 .pcr_pos = DT_INST_PROP_BY_IDX(0, pcrs, 1),
240 #endif
241 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
242 .wakeup_source = DT_INST_PROP(0, wakeup_source)
243 };
244
245 static struct xec_kbd_data kbd_data_0;
246
247 DEVICE_DT_INST_DEFINE(0, xec_kbd_init,
248 PM_DEVICE_DT_INST_GET(0), &kbd_data_0, &xec_kbd_cfg_0,
249 POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
250
251 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
252 "only one microchip,xec-kbd compatible node can be supported");
253 BUILD_ASSERT(IN_RANGE(DT_INST_PROP(0, row_size), 1, 8), "invalid row-size");
254 BUILD_ASSERT(IN_RANGE(DT_INST_PROP(0, col_size), 1, 18), "invalid col-size");
255