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