/* 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 "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. */ LM4_GPIO_ODR(LM4_GPIO_P) &= ~(1 << 2); #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 /* Invert column 2 output */ mask ^= (1 << 2); #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);