Files
OpenCellular/common/console.c
Randall Spangler 858d87cfaa Add basic SPI support to link
This adds SPI transaction support, and a debug command to read a few
values from the SPI EEPROM.

Note that the SPI controller is normally *disabled* with all its I/Os
high-Z, so this will not interfere with main processor or Servo on the
SPI bus.  The bus is only enabled during the SPIROM command itself.

BUG=chrome-os-partner:7844
TEST=manual

1) Reboot system
2) on EC console, 'spirom'.  Should print

Man/Dev ID  : 0xef 0x16
JEDEC ID    : 0xef 0x40 0x17
Unique ID   : 0xd1 0x61 0x44 0xb0 0x63 0x5d 0x40 0x32
Status reg 1: 0x00
Status reg 2: 0x00

Note that unique ID is, well, unique, so it won't match my value.  But
it should still be something not all 0xff's.

3) Power on the system.  x86 should still boot normally, indicating
that the EC isn't interfering with the SPI bus.

Change-Id: I53bf5fdbbe7a37949375d0463e30e408cc6fb6a8
2012-05-30 13:12:34 -07:00

338 lines
7.0 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.
*/
/* Console module for Chrome EC */
#include "console.h"
#include "link_defs.h"
#include "task.h"
#include "uart.h"
#include "util.h"
#define MAX_ARGS_PER_COMMAND 10
#define PROMPT "> "
/* Default to all channels active */
#ifndef CC_DEFAULT
#define CC_DEFAULT CC_ALL
#endif
static uint32_t channel_mask = CC_DEFAULT;
static char input_buf[80]; /* Current console command line */
/* List of channel names; must match enum console_channel. */
/* TODO: move this to board.c */
static const char *channel_names[CC_CHANNEL_COUNT] = {
"command",
"charger",
"chipset",
"dma",
"gpio",
"hostcmd",
"i2c",
"i8042",
"keyboard",
"keyscan",
"lightbar",
"lpc",
"port80",
"powerbtn",
"pwm",
"spi",
"system",
"task",
"usbcharge",
"vboot",
};
/*****************************************************************************/
/* Channel-based console output */
int cputs(enum console_channel channel, const char *outstr)
{
/* Filter out inactive channels */
if (!(CC_MASK(channel) & channel_mask))
return EC_SUCCESS;
return uart_puts(outstr);
}
int cprintf(enum console_channel channel, const char *format, ...)
{
int rv;
va_list args;
/* Filter out inactive channels */
if (!(CC_MASK(channel) & channel_mask))
return EC_SUCCESS;
va_start(args, format);
rv = uart_vprintf(format, args);
va_end(args);
return rv;
}
void cflush(void)
{
uart_flush_output();
}
/*****************************************************************************/
/* Console input */
/* Splits a line of input into words. Stores the count of words in
* <argc>. Stores pointers to the words in <argv>, which must be at
* least <max_argc> long. If more than <max_argc> words are found,
* discards the excess and returns EC_ERROR_OVERFLOW. */
static int split_words(char *input, int max_argc, int *argc, char **argv)
{
char *c;
int in_word = 0;
/* Parse input into words */
*argc = 0;
for (c = input; *c; c++) {
if (isspace(*c)) {
if (in_word) {
/* Ending a word */
*c = '\0';
++*argc;
in_word = 0;
}
} else if (*c == '#') {
/* After the hash sign is comment, ignored.
* TODO: Need more logic to suuport escaping. */
break;
} else {
if (!in_word) {
/* Starting a new word */
if (*argc >= max_argc)
return EC_ERROR_OVERFLOW;
argv[*argc] = c;
in_word = 1;
}
}
}
return EC_SUCCESS;
}
/* Find a command by name. Returns the command structure, or NULL if no match
* found. */
static const struct console_command *find_command(char *name)
{
const struct console_command *cmd, *match = NULL;
int match_length = strlen(name);
for (cmd = __cmds; cmd < __cmds_end; cmd++) {
if (!strncasecmp(name, cmd->name, match_length)) {
if (match)
return NULL;
match = cmd;
}
}
return match;
}
/* Handle a line of input containing a single command. Modifies the input
* string during parsing. */
static int handle_command(char *input)
{
const struct console_command *cmd;
char *argv[MAX_ARGS_PER_COMMAND];
int argc = 0;
int rv;
/* Split input into words. Ignore words past our limit. */
split_words(input, MAX_ARGS_PER_COMMAND, &argc, argv);
/* If no command, nothing to do */
if (!argc)
return EC_SUCCESS;
cmd = find_command(argv[0]);
if (!cmd) {
ccprintf("Command '%s' not found or ambiguous.\n", argv[0]);
return EC_ERROR_UNKNOWN;
}
rv = cmd->handler(argc, argv);
if (rv == EC_SUCCESS)
return rv;
/* Print more info for errors */
if (rv == EC_ERROR_INVAL)
ccputs("Command usage/param invalid.\n");
else if (rv == EC_ERROR_PARAM_COUNT)
ccputs("Wrong number of params.\n");
else if (rv >= EC_ERROR_PARAM1 && rv <= EC_ERROR_PARAM9)
ccprintf("Parameter %d invalid.\n", rv - EC_ERROR_PARAM1 + 1);
else if (rv != EC_SUCCESS) {
ccprintf("Command returned error %d\n", rv);
return rv;
}
#ifdef CONFIG_CONSOLE_CMDHELP
if (cmd->argdesc)
ccprintf("Usage: %s %s\n", cmd->name, cmd->argdesc);
#endif
return rv;
}
static int console_init(void)
{
*input_buf = '\0';
uart_set_console_mode(1);
ccprintf("Console is enabled; type HELP for help.\n");
ccputs(PROMPT);
/* TODO: restore channel list from EEPROM */
return EC_SUCCESS;
}
/* handle a console command */
static void console_process(void)
{
/* Process all the pending commands. Need to do this all at once
* since our interrupt may have been triggered multiple times. */
while (uart_peek('\n') >= 0) {
uart_gets(input_buf, sizeof(input_buf));
handle_command(input_buf);
ccputs(PROMPT);
}
}
void console_has_input(void)
{
/* Wake up the console task */
task_wake(TASK_ID_CONSOLE);
}
void console_task(void)
{
console_init();
while (1) {
console_process();
/* Wait for the next command message */
task_wait_event(-1);
}
}
/*****************************************************************************/
/* Console commands */
/* Command handler - prints help. */
static int command_help(int argc, char **argv)
{
const int ncmds = __cmds_end - __cmds;
const int cols = 5; /* printing in five columns */
const int rows = (ncmds + cols - 1) / cols;
int i, j;
#ifdef CONFIG_CONSOLE_CMDHELP
if (argc == 2) {
const struct console_command *cmd;
if (!strcasecmp(argv[1], "list")) {
ccputs("Known commands:\n");
for (i = 0; i < ncmds; i++) {
ccprintf(" %-15s%s\n",
__cmds[i].name, __cmds[i].shorthelp);
cflush();
}
ccputs("HELP CMD = help on CMD.\n");
return EC_SUCCESS;
}
cmd = find_command(argv[1]);
if (!cmd) {
ccprintf("Command '%s' not found or ambiguous.\n",
argv[1]);
return EC_ERROR_UNKNOWN;
}
ccprintf("Usage: %s %s\n", cmd->name,
(cmd->argdesc ? cmd->argdesc : ""));
if (cmd->shorthelp)
ccprintf("%s\n", cmd->shorthelp);
return EC_SUCCESS;
}
#endif
ccputs("Known commands:\n");
for (i = 0; i < rows; i++) {
ccputs(" ");
for (j = 0; j < cols; j++) {
int index = j * rows + i;
if (index >= ncmds)
break;
ccprintf("%-15s", __cmds[index].name);
}
ccputs("\n");
cflush();
}
#ifdef CONFIG_CONSOLE_CMDHELP
ccputs("HELP LIST = more info; ");
ccputs("HELP CMD = help on CMD.\n");
#endif
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(help, command_help,
"[ list | <name> ]",
"Print command help",
NULL);
/* Set active channels */
static int command_ch(int argc, char **argv)
{
int i;
char *e;
/* If one arg, set the mask */
if (argc == 2) {
int m = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
/* No disabling the command output channel */
channel_mask = m | CC_MASK(CC_COMMAND);
/* TODO: save channel list to EEPROM */
return EC_SUCCESS;
}
/* Print the list of channels */
ccputs(" # Mask E Channel\n");
for (i = 0; i < CC_CHANNEL_COUNT; i++) {
ccprintf("%2d %08x %c %s\n",
i, CC_MASK(i),
(channel_mask & CC_MASK(i)) ? '*' : ' ',
channel_names[i]);
cflush();
}
return EC_SUCCESS;
};
DECLARE_CONSOLE_COMMAND(chan, command_ch,
"[mask]",
"Get or set console channel mask",
NULL);