summaryrefslogtreecommitdiff
path: root/zephyr/drivers/cros_kb_raw/cros_kb_raw_xec.c
blob: 30ffd7ca856915f318c00927a2cc779477ff14d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* Copyright 2022 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#define DT_DRV_COMPAT microchip_xec_cros_kb_raw

#include "ec_tasks.h"
#include "keyboard_raw.h"
#include "task.h"

#include <assert.h>

#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>

#include <drivers/cros_kb_raw.h>
#include <soc.h>
#include <soc/microchip_xec/reg_def_cros.h>
LOG_MODULE_REGISTER(cros_kb_raw, LOG_LEVEL_ERR);

#ifdef CONFIG_PLATFORM_EC_KEYBOARD_COL2_INVERTED
#if !DT_NODE_EXISTS(KBD_KS02_NODE)
#error gpio_kbd_kso2 alias has to point to the keyboard column 2 output pin.
#endif
#endif /* CONFIG_PLATFORM_EC_KEYBOARD_COL2_INVERTED */

#define XEC_MAX_KEY_COLS 18 /* Maximum rows of keyboard matrix */
#define XEC_MAX_KEY_ROWS 8 /* Maximum columns of keyboard matrix */
#define XEC_KB_ROW_MASK (BIT(XEC_MAX_KEY_ROWS) - 1)

/* Device config */
struct cros_kb_raw_xec_config {
	/* keyboard scan controller base address */
	uintptr_t base;
	/* Keyboard scan input (KSI) wake-up irq */
	int irq;
	const struct pinctrl_dev_config *pcfg;
};

static int kb_raw_xec_init(const struct device *dev)
{
	ARG_UNUSED(dev);

	/* Clock default is on */
	return 0;
}

/* Clear keyboard source bits: hw status, block source and NVIC pending */
static void kb_raw_xec_clr_src(const struct device *dev)
{
	struct cros_kb_raw_xec_config const *cfg = dev->config;
	struct kscan_regs *const inst = (struct kscan_regs *)cfg->base;

	inst->KSI_STS = 0xff;
	mchp_soc_ecia_girq_src_clr(MCHP_GIRQ21_ID, MCHP_KEYSCAN_GIRQ_POS);
	mchp_xec_ecia_nvic_clr_pend(MCHP_KEYSCAN_GIRQ_NVIC_DIRECT);
}

/* Cros ec keyboard raw api functions */
static int cros_kb_raw_xec_enable_interrupt(const struct device *dev,
					    int enable)
{
	struct cros_kb_raw_xec_config const *cfg = dev->config;

	if (enable) {
		kb_raw_xec_clr_src(dev);
		/* Enable Kscan NVIC interrupt */
		irq_enable(cfg->irq);
	} else {
		/* Disable Kscan NVIC interrupt */
		irq_disable(cfg->irq);
	}

	return 0;
}

static int cros_kb_raw_xec_read_row(const struct device *dev)
{
	struct cros_kb_raw_xec_config const *cfg = dev->config;
	struct kscan_regs *const inst = (struct kscan_regs *)cfg->base;
	int val;

	val = inst->KSI_IN;
	LOG_DBG("rows raw %02x", val);

	/* 1 means key pressed, otherwise means key released. */
	return (~val & 0xFF);
}

static int cros_kb_raw_xec_drive_column(const struct device *dev, int col)
{
	struct cros_kb_raw_xec_config const *cfg = dev->config;
	struct kscan_regs *const inst = (struct kscan_regs *)cfg->base;

	/* Drive all lines to high. i.e. Key detection is disabled. */
	if (col == KEYBOARD_COLUMN_NONE) {
		inst->KSO_SEL = MCHP_KSCAN_KSO_EN;
		/* Set logical level high on COL2 */
		cros_kb_raw_set_col2(1);
	}
	/* Drive all lines to low for detection any key press */
	else if (col == KEYBOARD_COLUMN_ALL) {
		mchp_soc_ecia_girq_src_dis(MCHP_GIRQ21_ID,
					   MCHP_KEYSCAN_GIRQ_POS);
		inst->KSO_SEL = MCHP_KSCAN_KSO_ALL;
		/* Set logical level low on COL2 */
		cros_kb_raw_set_col2(0);

		/*
		 * Fix glitches on KSIs pins as all KSOs are driven low
		 * As keyboard is enabled, either in POR initialization
		 * or after previous key's break scan code sent to host,
		 * EC will drive all KSOs low for ready next key press
		 * detection. KSIs may have glitches based on differnt
		 * hardward design, this source bit checking and clean
		 * can prevent one faulty interrupt occurring, thought
		 * this faulty interrupt is harmless.
		 */
		if (inst->KSI_IN != 0xff) {
			kb_raw_xec_clr_src(dev);
		}
		mchp_soc_ecia_girq_src_en(MCHP_GIRQ21_ID,
					  MCHP_KEYSCAN_GIRQ_POS);
	}
	/* Drive one line to low for determining
	 * which key's state changed.
	 */
	else if (IS_ENABLED(CONFIG_PLATFORM_EC_KEYBOARD_COL2_INVERTED)) {
		if (col == 2) {
			inst->KSO_SEL = MCHP_KSCAN_KSO_EN;
			/* Set logical level low on COL2 */
			cros_kb_raw_set_col2(0);
		} else {
			inst->KSO_SEL = col + CONFIG_KEYBOARD_KSO_BASE;
			/* Set logical level high on COL2 */
			cros_kb_raw_set_col2(1);
		}
	} else {
		inst->KSO_SEL = col + CONFIG_KEYBOARD_KSO_BASE;
	}

	return 0;
}

static void cros_kb_raw_xec_ksi_isr(const struct device *dev)
{
	kb_raw_xec_clr_src(dev);
	/* Wake-up keyboard scan task */
	task_wake(TASK_ID_KEYSCAN);
}

static int cros_kb_raw_xec_init(const struct device *dev)
{
	struct cros_kb_raw_xec_config const *cfg = dev->config;
	struct kscan_regs *const inst = (struct kscan_regs *)cfg->base;

	/* Use zephyr pinctrl to initialize pins */
	int ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);

	if (ret)
		return ret;

	/* Set up Kscan IRQ and ISR */
	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
		    cros_kb_raw_xec_ksi_isr, DEVICE_DT_INST_GET(0), 0);

	/* Disable Kscan NVIC and source interrupts */
	irq_disable(cfg->irq);
	mchp_soc_ecia_girq_src_dis(MCHP_GIRQ21_ID, MCHP_KEYSCAN_GIRQ_POS);
	kb_raw_xec_clr_src(dev);
	/* Enable all Kscan KSIs interrupt */
	inst->KSI_IEN = 0xff;
	/* Enable Kscan source interrupt */
	mchp_soc_ecia_girq_src_en(MCHP_GIRQ21_ID, MCHP_KEYSCAN_GIRQ_POS);

	return 0;
}

static const struct cros_kb_raw_driver_api cros_kb_raw_xec_driver_api = {
	.init = cros_kb_raw_xec_init,
	.drive_colum = cros_kb_raw_xec_drive_column,
	.read_rows = cros_kb_raw_xec_read_row,
	.enable_interrupt = cros_kb_raw_xec_enable_interrupt,
};

/* instantiate zephyr pinctrl constant info */
PINCTRL_DT_INST_DEFINE(0);

static const struct cros_kb_raw_xec_config cros_kb_raw_cfg = {
	.base = DT_INST_REG_ADDR(0),
	.irq = DT_INST_IRQN(0),
	.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
};

/* Verify there's exactly one enabled cros,kb-raw-xec node. */
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1);
DEVICE_DT_INST_DEFINE(0, kb_raw_xec_init, NULL, NULL, &cros_kb_raw_cfg,
		      PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
		      &cros_kb_raw_xec_driver_api);