mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
Instead of using a runtime callback to register the console commands, put them in a special linker section. So we can do a macro to "register" them during the build. It saves 684 bytes and a few microseconds at startup. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BUG=None TEST=run a few commands from the BDS command line. Change-Id: Id33ea210b9035bf76ed720373c74c5dd24ccd1b1
350 lines
9.2 KiB
C
350 lines
9.2 KiB
C
/* Copyright (c) 2012 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.
|
|
*/
|
|
|
|
/* GPIO module for Chrome EC */
|
|
|
|
#include "board.h"
|
|
#include "console.h"
|
|
#include "gpio.h"
|
|
#include "power_button.h"
|
|
#include "registers.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "uart.h"
|
|
#include "util.h"
|
|
|
|
|
|
/* 0-terminated list of GPIO bases */
|
|
const uint32_t gpio_bases[] = {
|
|
LM4_GPIO_A, LM4_GPIO_B, LM4_GPIO_C, LM4_GPIO_D,
|
|
LM4_GPIO_E, LM4_GPIO_F, LM4_GPIO_G, LM4_GPIO_H,
|
|
LM4_GPIO_J, LM4_GPIO_K, LM4_GPIO_L, LM4_GPIO_M,
|
|
LM4_GPIO_N, LM4_GPIO_P, LM4_GPIO_Q, 0
|
|
};
|
|
|
|
|
|
/* Signal information from board.c. Must match order from enum gpio_signal. */
|
|
extern const struct gpio_info gpio_list[GPIO_COUNT];
|
|
|
|
|
|
/* Find a GPIO signal by name. Returns the signal index, or GPIO_COUNT if
|
|
* no match. */
|
|
static enum gpio_signal find_signal_by_name(const char *name)
|
|
{
|
|
const struct gpio_info *g = gpio_list;
|
|
int i;
|
|
|
|
if (!name || !*name)
|
|
return GPIO_COUNT;
|
|
|
|
for (i = 0; i < GPIO_COUNT; i++, g++) {
|
|
if (!strcasecmp(name, g->name))
|
|
return i;
|
|
}
|
|
|
|
return GPIO_COUNT;
|
|
}
|
|
|
|
|
|
/* Find the index of a GPIO port base address (LM4_GPIO_[A-Q]); this is used by
|
|
* the clock gating registers. Returns the index, or -1 if no match. */
|
|
static int find_gpio_port_index(uint32_t port_base)
|
|
{
|
|
int i;
|
|
for (i = 0; gpio_bases[i]; i++) {
|
|
if (gpio_bases[i] == port_base)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int gpio_pre_init(void)
|
|
{
|
|
volatile uint32_t scratch __attribute__((unused));
|
|
const struct gpio_info *g = gpio_list;
|
|
int i;
|
|
|
|
/* Enable clocks to all the GPIO blocks (since we use all of them as
|
|
* GPIOs) */
|
|
LM4_SYSTEM_RCGCGPIO |= 0x7fff;
|
|
scratch = LM4_SYSTEM_RCGCGPIO; /* Delay a few clocks */
|
|
|
|
/* Disable GPIO commit control for PD7 and PF0, since we don't use the
|
|
* NMI pin function. */
|
|
LM4_GPIO_LOCK(LM4_GPIO_D) = LM4_GPIO_LOCK_UNLOCK;
|
|
LM4_GPIO_CR(LM4_GPIO_D) |= 0x80;
|
|
LM4_GPIO_LOCK(LM4_GPIO_D) = 0;
|
|
LM4_GPIO_LOCK(LM4_GPIO_F) = LM4_GPIO_LOCK_UNLOCK;
|
|
LM4_GPIO_CR(LM4_GPIO_F) |= 0x01;
|
|
LM4_GPIO_LOCK(LM4_GPIO_F) = 0;
|
|
|
|
/* Clear SSI0 alternate function on PA2:5 */
|
|
LM4_GPIO_AFSEL(LM4_GPIO_A) &= ~0x3c;
|
|
|
|
/* Set all GPIOs to defaults */
|
|
for (i = 0; i < GPIO_COUNT; i++, g++) {
|
|
|
|
/* Handle GPIO direction */
|
|
if (g->flags & GPIO_OUTPUT) {
|
|
/* Output with default level */
|
|
gpio_set_level(i, g->flags & GPIO_HIGH);
|
|
LM4_GPIO_DIR(g->port) |= g->mask;
|
|
} else {
|
|
/* Input */
|
|
if (g->flags & GPIO_PULL) {
|
|
/* With pull up/down */
|
|
if (g->flags & GPIO_HIGH)
|
|
LM4_GPIO_PUR(g->port) |= g->mask;
|
|
else
|
|
LM4_GPIO_PDR(g->port) |= g->mask;
|
|
}
|
|
}
|
|
|
|
/* Use as GPIO, not alternate function */
|
|
gpio_set_alternate_function(g->port, g->mask, 0);
|
|
|
|
/* Set up interrupts if necessary */
|
|
if (g->flags & GPIO_INT_LEVEL)
|
|
LM4_GPIO_IS(g->port) |= g->mask;
|
|
if (g->flags & (GPIO_INT_RISING | GPIO_INT_HIGH))
|
|
LM4_GPIO_IEV(g->port) |= g->mask;
|
|
if (g->flags & GPIO_INT_BOTH)
|
|
LM4_GPIO_IBE(g->port) |= g->mask;
|
|
/* Interrupt is enabled by gpio_enable_interrupt() */
|
|
}
|
|
|
|
/* Enable IRQs now that pins are set up */
|
|
task_enable_irq(LM4_IRQ_GPIOA);
|
|
task_enable_irq(LM4_IRQ_GPIOB);
|
|
task_enable_irq(LM4_IRQ_GPIOC);
|
|
task_enable_irq(LM4_IRQ_GPIOD);
|
|
task_enable_irq(LM4_IRQ_GPIOE);
|
|
task_enable_irq(LM4_IRQ_GPIOF);
|
|
task_enable_irq(LM4_IRQ_GPIOG);
|
|
#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPIOH)
|
|
task_enable_irq(LM4_IRQ_GPIOH);
|
|
#endif
|
|
task_enable_irq(LM4_IRQ_GPIOJ);
|
|
task_enable_irq(LM4_IRQ_GPIOK);
|
|
task_enable_irq(LM4_IRQ_GPIOL);
|
|
task_enable_irq(LM4_IRQ_GPIOM);
|
|
#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPION)
|
|
task_enable_irq(LM4_IRQ_GPION);
|
|
#endif
|
|
task_enable_irq(LM4_IRQ_GPIOP);
|
|
task_enable_irq(LM4_IRQ_GPIOQ);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
void gpio_set_alternate_function(int port, int mask, int func)
|
|
{
|
|
int port_index = find_gpio_port_index(port);
|
|
int cgmask;
|
|
|
|
if (port_index < 0)
|
|
return; /* TODO: assert */
|
|
|
|
/* Enable the GPIO port if necessary */
|
|
cgmask = 1 << port_index;
|
|
if ((LM4_SYSTEM_RCGCGPIO & cgmask) != cgmask) {
|
|
volatile uint32_t scratch __attribute__((unused));
|
|
LM4_SYSTEM_RCGCGPIO |= cgmask;
|
|
/* Delay a few clocks before accessing GPIO registers on that
|
|
* port. */
|
|
scratch = LM4_SYSTEM_RCGCGPIO;
|
|
}
|
|
|
|
if (func) {
|
|
int pctlmask = 0;
|
|
int i;
|
|
/* Expand mask from bits to nibbles */
|
|
for (i = 0; i < 8; i++) {
|
|
if (mask & (1 << i))
|
|
pctlmask |= 1 << (4 * i);
|
|
}
|
|
|
|
LM4_GPIO_PCTL(port) =
|
|
(LM4_GPIO_PCTL(port) & ~(pctlmask * 0xf)) |
|
|
(pctlmask * func);
|
|
LM4_GPIO_AFSEL(port) |= mask;
|
|
} else {
|
|
LM4_GPIO_AFSEL(port) &= ~mask;
|
|
}
|
|
LM4_GPIO_DEN(port) |= mask;
|
|
}
|
|
|
|
|
|
int gpio_get_level(enum gpio_signal signal)
|
|
{
|
|
return LM4_GPIO_DATA(gpio_list[signal].port,
|
|
gpio_list[signal].mask) ? 1 : 0;
|
|
}
|
|
|
|
|
|
int gpio_set_level(enum gpio_signal signal, int value)
|
|
{
|
|
/* Ok to write 0xff becuase LM4_GPIO_DATA bit-masks only the bit
|
|
* we care about. */
|
|
LM4_GPIO_DATA(gpio_list[signal].port,
|
|
gpio_list[signal].mask) = (value ? 0xff : 0);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
int gpio_enable_interrupt(enum gpio_signal signal)
|
|
{
|
|
const struct gpio_info *g = gpio_list + signal;
|
|
|
|
/* Fail if no interrupt handler */
|
|
if (!g->irq_handler)
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
LM4_GPIO_IM(g->port) |= g->mask;
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Interrupt handlers */
|
|
|
|
static void gpio_interrupt(int port, uint32_t mis)
|
|
{
|
|
int i = 0;
|
|
const struct gpio_info *g = gpio_list;
|
|
|
|
for (i = 0; i < GPIO_COUNT; i++, g++) {
|
|
if (port == g->port && (mis & g->mask) && g->irq_handler)
|
|
g->irq_handler(i);
|
|
}
|
|
}
|
|
|
|
/* Handlers for each GPIO port. These read and clear the interrupt bits for
|
|
* the port, then call the master handler above. */
|
|
#define GPIO_IRQ_FUNC(irqfunc, gpiobase) \
|
|
static void irqfunc(void) \
|
|
{ \
|
|
uint32_t mis = LM4_GPIO_MIS(gpiobase); \
|
|
LM4_GPIO_ICR(gpiobase) = mis; \
|
|
gpio_interrupt(gpiobase, mis); \
|
|
}
|
|
|
|
GPIO_IRQ_FUNC(__gpio_a_interrupt, LM4_GPIO_A);
|
|
GPIO_IRQ_FUNC(__gpio_b_interrupt, LM4_GPIO_B);
|
|
GPIO_IRQ_FUNC(__gpio_c_interrupt, LM4_GPIO_C);
|
|
GPIO_IRQ_FUNC(__gpio_d_interrupt, LM4_GPIO_D);
|
|
GPIO_IRQ_FUNC(__gpio_e_interrupt, LM4_GPIO_E);
|
|
GPIO_IRQ_FUNC(__gpio_f_interrupt, LM4_GPIO_F);
|
|
GPIO_IRQ_FUNC(__gpio_g_interrupt, LM4_GPIO_G);
|
|
#if (KB_SCAN_ROW_GPIO != LM4_GPIO_H)
|
|
GPIO_IRQ_FUNC(__gpio_h_interrupt, LM4_GPIO_H);
|
|
#endif
|
|
GPIO_IRQ_FUNC(__gpio_j_interrupt, LM4_GPIO_J);
|
|
GPIO_IRQ_FUNC(__gpio_k_interrupt, LM4_GPIO_K);
|
|
GPIO_IRQ_FUNC(__gpio_l_interrupt, LM4_GPIO_L);
|
|
GPIO_IRQ_FUNC(__gpio_m_interrupt, LM4_GPIO_M);
|
|
#if (KB_SCAN_ROW_GPIO != LM4_GPIO_N)
|
|
GPIO_IRQ_FUNC(__gpio_n_interrupt, LM4_GPIO_N);
|
|
#endif
|
|
GPIO_IRQ_FUNC(__gpio_p_interrupt, LM4_GPIO_P);
|
|
GPIO_IRQ_FUNC(__gpio_q_interrupt, LM4_GPIO_Q);
|
|
|
|
#undef GPIO_IRQ_FUNC
|
|
|
|
/* Declare IRQs */
|
|
/* TODO: nesting this macro inside the GPIO_IRQ_FUNC macro works poorly because
|
|
* DECLARE_IRQ() stringizes its inputs. */
|
|
DECLARE_IRQ(LM4_IRQ_GPIOA, __gpio_a_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOB, __gpio_b_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOC, __gpio_c_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOD, __gpio_d_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOE, __gpio_e_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOF, __gpio_f_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOG, __gpio_g_interrupt, 1);
|
|
#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPIOH)
|
|
DECLARE_IRQ(LM4_IRQ_GPIOH, __gpio_h_interrupt, 1);
|
|
#endif
|
|
DECLARE_IRQ(LM4_IRQ_GPIOJ, __gpio_j_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOK, __gpio_k_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOL, __gpio_l_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOM, __gpio_m_interrupt, 1);
|
|
#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPION)
|
|
DECLARE_IRQ(LM4_IRQ_GPION, __gpio_n_interrupt, 1);
|
|
#endif
|
|
DECLARE_IRQ(LM4_IRQ_GPIOP, __gpio_p_interrupt, 1);
|
|
DECLARE_IRQ(LM4_IRQ_GPIOQ, __gpio_q_interrupt, 1);
|
|
|
|
/*****************************************************************************/
|
|
/* Console commands */
|
|
|
|
static int command_gpio_get(int argc, char **argv)
|
|
{
|
|
const struct gpio_info *g = gpio_list;
|
|
int i;
|
|
|
|
/* If a signal is specified, print only that one */
|
|
if (argc == 2) {
|
|
i = find_signal_by_name(argv[1]);
|
|
if (i == GPIO_COUNT) {
|
|
uart_puts("Unknown signal name.\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
g = gpio_list + i;
|
|
uart_printf(" %d %s\n", gpio_get_level(i), g->name);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/* Otherwise print them all */
|
|
uart_puts("Current GPIO levels:\n");
|
|
for (i = 0; i < GPIO_COUNT; i++, g++) {
|
|
if (g->mask)
|
|
uart_printf(" %d %s\n", gpio_get_level(i), g->name);
|
|
/* We have enough GPIOs that we'll overflow the output buffer
|
|
* without flushing */
|
|
uart_flush_output();
|
|
}
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(gpioget, command_gpio_get);
|
|
|
|
|
|
static int command_gpio_set(int argc, char **argv)
|
|
{
|
|
const struct gpio_info *g;
|
|
char *e;
|
|
int v, i;
|
|
|
|
if (argc < 3) {
|
|
uart_puts("Usage: gpioset <signal_name> <0|1>\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
|
|
i = find_signal_by_name(argv[1]);
|
|
if (i == GPIO_COUNT) {
|
|
uart_puts("Unknown signal name.\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
g = gpio_list + i;
|
|
|
|
if (!g->mask) {
|
|
uart_puts("Signal is not implemented.\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
if (!(g->flags & GPIO_OUTPUT)) {
|
|
uart_puts("Signal is not an output.\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
|
|
v = strtoi(argv[2], &e, 0);
|
|
if (*e) {
|
|
uart_puts("Invalid signal value.\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
|
|
return gpio_set_level(i, v);
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(gpioset, command_gpio_set);
|