summaryrefslogtreecommitdiff
path: root/chip/lm4/keyboard_raw.c
blob: a063f53486420f7514374df4ae5a5b62e91ac31c (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
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Functions needed by keyboard scanner module for Chrome EC */

#include "common.h"
#include "keyboard_raw.h"
#include "keyboard_scan.h"
#include "registers.h"
#include "system.h"
#include "task.h"

void keyboard_raw_init(void)
{
	/* Ensure top-level interrupt is disabled */
	keyboard_raw_enable_interrupt(0);

	/*
	 * Set column outputs as open-drain; we either pull them low or let
	 * them float high.
	 */
	LM4_GPIO_AFSEL(LM4_GPIO_P) = 0;  /* KSO[7:0] */
	LM4_GPIO_AFSEL(LM4_GPIO_Q) &= ~0x1f;  /* KSO[12:8] */
	LM4_GPIO_DEN(LM4_GPIO_P) = 0xff;
	LM4_GPIO_DEN(LM4_GPIO_Q) |= 0x1f;
	LM4_GPIO_DIR(LM4_GPIO_P) = 0xff;
	LM4_GPIO_DIR(LM4_GPIO_Q) |= 0x1f;
	LM4_GPIO_ODR(LM4_GPIO_P) = 0xff;
	LM4_GPIO_ODR(LM4_GPIO_Q) |= 0x1f;

#ifdef CONFIG_KEYBOARD_COL2_INVERTED
	/*
	 * When column 2 is inverted, the Silego has a pulldown instead of a
	 * pullup.  So drive it push-pull instead of open-drain.
	 */
#ifdef BOARD_PAINE
	if (0x04 != system_get_board_version())
		LM4_GPIO_ODR(LM4_GPIO_P) &= ~(1 << 2);
#else
	LM4_GPIO_ODR(LM4_GPIO_P) &= ~(1 << 2);
#endif
#endif

	/* Set row inputs with pull-up */
	LM4_GPIO_AFSEL(KB_SCAN_ROW_GPIO) &= 0xff;
	LM4_GPIO_DEN(KB_SCAN_ROW_GPIO) |= 0xff;
	LM4_GPIO_DIR(KB_SCAN_ROW_GPIO) = 0;
	LM4_GPIO_PUR(KB_SCAN_ROW_GPIO) = 0xff;

	/* Edge-sensitive on both edges. */
	LM4_GPIO_IS(KB_SCAN_ROW_GPIO) = 0;
	LM4_GPIO_IBE(KB_SCAN_ROW_GPIO) = 0xff;

	/*
	 * Enable interrupts for the inputs.  The top-level interrupt is still
	 * masked off, so this won't trigger interrupts yet.
	 */
	LM4_GPIO_IM(KB_SCAN_ROW_GPIO) = 0xff;
}

void keyboard_raw_task_start(void)
{
	task_enable_irq(KB_SCAN_ROW_IRQ);
}

test_mockable void keyboard_raw_drive_column(int col)
{
	int mask;

	if (col == KEYBOARD_COLUMN_NONE)
		mask = 0x1fff;			/* Tri-state all outputs */
	else if (col == KEYBOARD_COLUMN_ALL)
		mask = 0;			/* Assert all outputs */
	else
		mask = 0x1fff ^ (1 << col);	/* Assert a single output */

#ifdef CONFIG_KEYBOARD_COL2_INVERTED
#ifdef BOARD_PAINE
	if (0x04 != system_get_board_version())
		/* Invert column 2 output */
		mask ^= (1 << 2);
#else
	/* Invert column 2 output */
	mask ^= (1 << 2);
#endif
#endif

	LM4_GPIO_DATA(LM4_GPIO_P, 0xff) = mask & 0xff;
	LM4_GPIO_DATA(LM4_GPIO_Q, 0x1f) = (mask >> 8) & 0x1f;
}

test_mockable int keyboard_raw_read_rows(void)
{
	/* Bits are active-low, so invert returned levels */
	return LM4_GPIO_DATA(KB_SCAN_ROW_GPIO, 0xff) ^ 0xff;
}

void keyboard_raw_enable_interrupt(int enable)
{
	if (enable) {
		/*
		 * Clear pending interrupts before enabling them, because the
		 * raw interrupt status may have been tripped by keyboard
		 * scanning or, if a key is already pressed, by driving all the
		 * outputs.
		 *
		 * We won't lose keyboard events because the scanning task will
		 * explicitly check the raw row state before waiting for an
		 * interrupt.  If a key is pressed, the task won't wait.
		 */
		LM4_GPIO_ICR(KB_SCAN_ROW_GPIO) = 0xff;
		LM4_GPIO_IM(KB_SCAN_ROW_GPIO) = 0xff;
	} else {
		LM4_GPIO_IM(KB_SCAN_ROW_GPIO) = 0;
	}
}

/**
 * Interrupt handler for the entire GPIO bank of keyboard rows.
 */
void keyboard_raw_interrupt(void)
{
	/* Clear all pending keyboard interrupts */
	LM4_GPIO_ICR(KB_SCAN_ROW_GPIO) = 0xff;

	/* Wake the scan task */
	task_wake(TASK_ID_KEYSCAN);
}
DECLARE_IRQ(KB_SCAN_ROW_IRQ, keyboard_raw_interrupt, 3);