Files
OpenCellular/chip/lm4/gpio.c
Vincent Palatin c21f07e58e register console commands at compile-time
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
2012-01-24 00:50:08 +00:00

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);