diff options
47 files changed, 5921 insertions, 0 deletions
diff --git a/board/bds/board.h b/board/bds/board.h new file mode 100644 index 0000000000..d485980b8f --- /dev/null +++ b/board/bds/board.h @@ -0,0 +1,50 @@ +/* Stellaris EKB-LM4F-EAC board configuration */ + +#ifndef __BOARD_H +#define __BOARD_H + +/* 66.667 Mhz clock frequency */ +#define CPU_CLOCK 66666667 + +/* Fan PWM channels */ +#define FAN_CH_KBLIGHT 1 /* Keyboard backlight */ +#define FAN_CH_POWER_LED 3 /* Power adapter LED */ +#define FAN_CH_CPU 4 /* CPU fan */ + +/* LPC channels */ +#define LPC_CH_KERNEL 0 /* Kernel commands */ +#define LPC_CH_PORT80 1 /* Port 80 debug output */ +#define LPC_CH_CMD_DATA 2 /* Data for kernel/user-mode commands */ +#define LPC_CH_KEYBOARD 3 /* 8042 keyboard emulation */ +#define LPC_CH_USER 4 /* User-mode commands */ +#define LPC_CH_COMX 7 /* UART emulation */ +/* LPC pool offsets */ +#define LPC_POOL_OFFS_KERNEL 0 /* Kernel commands - 0=in, 1=out */ +#define LPC_POOL_OFFS_PORT80 4 /* Port 80 - 4=in, 5=out */ +#define LPC_POOL_OFFS_COMX 8 /* UART emulation range - 8-15 */ +#define LPC_POOL_OFFS_KEYBOARD 16 /* Keyboard - 16=in, 17=out */ +#define LPC_POOL_OFFS_CMD_DATA 512 /* Data range for commands - 512-1023 */ +/* LPC pool data pointers */ +#define LPC_POOL_KERNEL (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_KERNEL) +#define LPC_POOL_PORT80 (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_PORT80) +#define LPC_POOL_COMX (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_COMX) +#define LPC_POOL_KEYBOARD (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_KEYBOARD) +#define LPC_POOL_CMD_DATA (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_CMD_DATA) + +/* ADC inputs */ +/* TODO: really just need a lookup table for channels to inputs */ +#define ADC_IN_POT 0 /* Turn POT on badger board */ + +/* I2C ports */ +#define I2C_PORT_BATTERY 5 // port 0 / PB2:3 on Link, open on badger +#define I2C_PORT_CHARGER 5 // port 1 / PA6:7 on Link, user LED on badger +#define I2C_PORT_THERMAL 5 // port 5 / PB6:7 on link, but PG6:7 on badger +/* I2C port speeds in kbps */ +#define I2C_SPEED_BATTERY 100 +#define I2C_SPEED_CHARGER 100 +#define I2C_SPEED_THERMAL 400 /* TODO: TMP007 supports 3.4Mbps + operation; use faster speed? */ + +void configure_board(void); + +#endif /* __BOARD_H */ diff --git a/board/link/board.h b/board/link/board.h new file mode 100644 index 0000000000..45ea36833e --- /dev/null +++ b/board/link/board.h @@ -0,0 +1,26 @@ +/* configuration for Link mainboard */ + +#ifndef __BOARD_H +#define __BOARD_H + +/* 66.667 Mhz clock frequency */ +#define CPU_CLOCK 66666667 + +/* Fan PWM channels */ +#define FAN_CH_CPU 0 /* CPU fan */ +#define FAN_CH_KBLIGHT 1 /* Keyboard backlight */ +#define FAN_CH_POWER_LED 5 /* Power adapter LED */ + +/* I2C ports */ +#define I2C_PORT_BATTERY 0 +#define I2C_PORT_CHARGER 1 +#define I2C_PORT_THERMAL 5 +/* I2C port speeds in kbps */ +#define I2C_SPEED_BATTERY 100 +#define I2C_SPEED_CHARGER 100 +#define I2C_SPEED_THERMAL 400 /* TODO: TMP007 supports 3.4Mbps + operation; use faster speed? */ + +void configure_board(void); + +#endif /* __BOARD_H */ diff --git a/chip/lm4/adc.c b/chip/lm4/adc.c new file mode 100644 index 0000000000..fab5c43cef --- /dev/null +++ b/chip/lm4/adc.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2011 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. + */ + +/* ADC module for Chrome EC */ + +#include "board.h" +#include "console.h" +#include "adc.h" +#include "timer.h" +#include "registers.h" +#include "uart.h" +#include "util.h" + + +static void configure_gpio(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable GPIOE module and delay a few clocks */ + LM4_SYSTEM_RCGCGPIO |= 0x0010; + scratch = LM4_SYSTEM_RCGCGPIO; + + /* Use analog function for PE3 (AIN0) */ + LM4_GPIO_DEN(E) &= ~0x08; + LM4_GPIO_AMSEL(E) |= 0x08; +} + + +int adc_read(enum adc_channel ch) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* TODO: right now we have only a single channel so this is + * simple. When we have multiple channels, should we... + * + * 1) Read them all using a timer interrupt, and then return + * the most recent value? This is lowest-latency for the + * caller, but won't return accurate data if read frequently. + * + * 2) Reserve SS3 for reading a single value, and configure it + * on each read? Needs mutex if we could have multiple + * callers; doesn't matter if just used for debugging. + * + * 3) Both? */ + if (ch != ADC_CH_POT) + return ADC_READ_ERROR; + + /* Empty the FIFO of any previous results */ + while (!(LM4_ADC_SSFSTAT(0) & 0x100)) + scratch = LM4_ADC_SSFIFO(0); + + /* Clear the interrupt status */ + LM4_ADC_ADCISC |= 0x01; + + /* Initiate sample sequence */ + LM4_ADC_ADCPSSI |= 0x01; + + /* Wait for interrupt */ + /* TODO: use a real interrupt */ + while (!(LM4_ADC_ADCRIS & 0x01)); + + /* Read the FIFO */ + return LM4_ADC_SSFIFO(0); +} + + +int adc_read_ec_temperature(void) +{ + volatile uint32_t scratch __attribute__((unused)); + int a; + + /* Empty the FIFO of any previous results */ + while (!(LM4_ADC_SSFSTAT(3) & 0x100)) + scratch = LM4_ADC_SSFIFO(3); + + /* Clear the interrupt status */ + LM4_ADC_ADCISC |= 0x08; + + /* Initiate sample sequence */ + LM4_ADC_ADCPSSI |= 0x08; + + /* Wait for interrupt */ + /* TODO: use a real interrupt */ + /* TODO: timeout */ + while (!(LM4_ADC_ADCRIS & 0x08)); + + /* Read the FIFO and convert to temperature */ + a = LM4_ADC_SSFIFO(3); + return 273 + (295 - (225 * 2 * a) / ADC_READ_MAX) / 2; +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_adc(int argc, char **argv) +{ + uart_printf("ADC POT channel = 0x%03x\n",adc_read(ADC_CH_POT)); + return EC_SUCCESS; +} + + +static int command_ectemp(int argc, char **argv) +{ + int t = adc_read_ec_temperature(); + uart_printf("EC temperature is %d K = %d C\n", t, t-273); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"adc", command_adc}, + {"ectemp", command_ectemp}, +}; +static const struct console_group command_group = { + "ADC", console_commands, ARRAY_SIZE(console_commands) +}; + + +/*****************************************************************************/ +/* Initialization */ + +int adc_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable ADC0 module and delay a few clocks */ + LM4_SYSTEM_RCGCADC |= 0x01; + scratch = LM4_SYSTEM_RCGCADC; + + /* Configure GPIOs */ + configure_gpio(); + + /* Use external voltage references (VREFA+, VREFA-) instead of + * VDDA and GNDA. */ + LM4_ADC_ADCCTL = 0x01; + + /* TODO: set up clock using ADCCC register? */ + + /* Configure sample sequencer 0 */ + LM4_ADC_ADCACTSS &= ~0x01; + /* Trigger SS0 by processor request */ + LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & 0xfffffff0) | 0x00; + /* Sample AIN0 only */ + LM4_ADC_SSMUX(0) = ADC_IN_POT & 0x0f; + LM4_ADC_SSEMUX(0) = (ADC_IN_POT >> 4) & 0x0f; + LM4_ADC_SSCTL(0) = 0x06; /* IE0 | END0 */ + /* Enable sample sequencer 0 */ + LM4_ADC_ADCACTSS |= 0x01; + + /* Configure sample sequencer 3 */ + LM4_ADC_ADCACTSS &= ~0x08; + /* Trigger SS3 by processor request */ + LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & 0xffffff0f) | 0x00; + /* Sample internal temp sensor */ + LM4_ADC_SSMUX(3) = 0x00; + LM4_ADC_SSEMUX(3) = 0x00; + LM4_ADC_SSCTL(3) = 0x0e; /* TS0 | IE0 | END0 */ + /* Enable sample sequencer 3 */ + LM4_ADC_ADCACTSS |= 0x08; + + console_register_commands(&command_group); + return EC_SUCCESS; +} diff --git a/chip/lm4/eeprom.c b/chip/lm4/eeprom.c new file mode 100644 index 0000000000..266dbe3d44 --- /dev/null +++ b/chip/lm4/eeprom.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2011 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. + */ + +/* EEPROM module for Chrome EC */ + +#include "eeprom.h" +#include "console.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + +/* Size of EEPROM block in bytes */ +#define EEPROM_BLOCK_SIZE 64 + +/* Count of EEPROM blocks */ +static int block_count; + + +/* Waits for the current EEPROM operation to finish. */ +static int wait_for_done(void) +{ + /* TODO: how long is a reasonable timeout? */ + int i; + for (i = 0; i < 1000000; i++) { + if (!(LM4_EEPROM_EEDONE & 0x01)) + return EC_SUCCESS; + } + return EC_ERROR_UNKNOWN; +} + + +int eeprom_get_block_count(void) +{ + return block_count; +} + + +int eeprom_get_block_size(void) +{ + return EEPROM_BLOCK_SIZE; +} + + +int eeprom_read(int block, int offset, int size, char *data) +{ + uint32_t *d = (uint32_t *)data; + int rv; + + if (block < 0 || block >= block_count || + offset < 0 || offset > EEPROM_BLOCK_SIZE || offset & 3 || + size < 0 || offset + size >= EEPROM_BLOCK_SIZE || size & 3) + return EC_ERROR_UNKNOWN; + + rv = wait_for_done(); + if (rv) + return rv; + + LM4_EEPROM_EEBLOCK = block; + if (LM4_EEPROM_EEBLOCK != block) + return EC_ERROR_UNKNOWN; /* Error setting block */ + + LM4_EEPROM_EEOFFSET = offset >> 2; + + for ( ; size; size -= sizeof(uint32_t)) + *(d++) = LM4_EEPROM_EERDWRINC; + + return EC_SUCCESS; +} + + +int eeprom_write(int block, int offset, int size, const char *data) +{ + uint32_t *d = (uint32_t *)data; + int rv; + + if (block < 0 || block >= block_count || + offset < 0 || offset > EEPROM_BLOCK_SIZE || offset & 3 || + size < 0 || offset + size >= EEPROM_BLOCK_SIZE || size & 3) + return EC_ERROR_UNKNOWN; + + rv = wait_for_done(); + if (rv) + return rv; + + LM4_EEPROM_EEBLOCK = block; + if (LM4_EEPROM_EEBLOCK != block) + return EC_ERROR_UNKNOWN; /* Error setting block */ + + LM4_EEPROM_EEOFFSET = offset >> 2; + + /* Write 32 bits at a time; wait for each write to complete */ + for ( ; size; size -= sizeof(uint32_t)) { + LM4_EEPROM_EERDWRINC = *(d++); + rv = wait_for_done(); + if (rv) + return rv; + if (LM4_EEPROM_EEDONE) + return EC_ERROR_UNKNOWN; + } + + return EC_SUCCESS; +} + + +int eeprom_hide(int block) +{ + /* Block 0 can't be hidden */ + if (block <= 0 || block >= block_count) + return EC_ERROR_UNKNOWN; + + LM4_EEPROM_EEHIDE |= 1 << block; + return EC_SUCCESS; +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_eeprom_info(int argc, char **argv) +{ + uart_printf("EEPROM: %d blocks of %d bytes\n", + eeprom_get_block_count(), eeprom_get_block_size()); + uart_printf(" Block-hide flags: 0x%08x\n", LM4_EEPROM_EEHIDE); + return EC_SUCCESS; +} + + +static int command_eeprom_read(int argc, char **argv) +{ + int block = 0; + int offset = 0; + char *e; + int rv; + uint32_t d; + + if (argc < 2) { + uart_puts("Usage: eeread <block> [offset]\n"); + return EC_ERROR_UNKNOWN; + } + + block = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid block\n"); + return EC_ERROR_UNKNOWN; + } + + if (argc > 2) { + offset = strtoi(argv[2], &e, 0); + if (*e) { + uart_puts("Invalid offset\n"); + return EC_ERROR_UNKNOWN; + } + } + + rv = eeprom_read(block, offset, sizeof(d), (char *)&d); + if (rv == EC_SUCCESS) + uart_printf("Block %d offset %d = 0x%08x\n", + block, offset, d); + return rv; +} + + +static int command_eeprom_write(int argc, char **argv) +{ + int block = 0; + int offset = 0; + char *e; + int rv; + uint32_t d; + + if (argc < 4) { + uart_puts("Usage: eeread <block> <offset> <data>\n"); + return EC_ERROR_UNKNOWN; + } + + block = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid block\n"); + return EC_ERROR_UNKNOWN; + } + offset = strtoi(argv[2], &e, 0); + if (*e) { + uart_puts("Invalid offset\n"); + return EC_ERROR_UNKNOWN; + } + d = strtoi(argv[3], &e, 0); + if (*e) { + uart_puts("Invalid data\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("Writing 0x%08x to block %d offset %d...\n", + d, block, offset); + rv = eeprom_write(block, offset, sizeof(d), (char *)&d); + if (rv == EC_SUCCESS) + uart_puts("done.\n"); + return rv; +} + + +static int command_eeprom_hide(int argc, char **argv) +{ + int block = 0; + char *e; + int rv; + + if (argc < 2) { + uart_puts("Usage: eehide <block>\n"); + return EC_ERROR_UNKNOWN; + } + + block = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid block\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("Hiding EEPROM block %d...\n", block); + rv = eeprom_hide(block); + if (rv == EC_SUCCESS) + uart_printf("Done.\n"); + return rv; +} + + +static const struct console_command console_commands[] = { + {"eeinfo", command_eeprom_info}, + {"eeread", command_eeprom_read}, + {"eewrite", command_eeprom_write}, + {"eehide", command_eeprom_hide}, +}; +static const struct console_group command_group = { + "EEPROM", console_commands, ARRAY_SIZE(console_commands) +}; + + +/*****************************************************************************/ +/* Initialization */ + + +int eeprom_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable the EEPROM module and delay a few clocks */ + LM4_SYSTEM_RCGCEEPROM = 1; + scratch = LM4_SYSTEM_RCGCEEPROM; + + wait_for_done(); + block_count = LM4_EEPROM_EESIZE >> 16; + + console_register_commands(&command_group); + return EC_SUCCESS; +} diff --git a/chip/lm4/flash.c b/chip/lm4/flash.c new file mode 100644 index 0000000000..115747d7d9 --- /dev/null +++ b/chip/lm4/flash.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2011 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. + */ + +/* Flash memory module for Chrome EC */ + +#include "flash.h" +#include "gpio.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + + +static int usable_flash_size; + + +int flash_get_size(void) +{ + return usable_flash_size; +} + + +int flash_get_write_block_size(void) +{ + return FLASH_WRITE_BYTES; +} + + +int flash_get_erase_block_size(void) +{ + return FLASH_ERASE_BYTES; +} + + +int flash_get_protect_block_size(void) +{ + return FLASH_PROTECT_BYTES; +} + + +int flash_read(int offset, int size, char *data) +{ + if (size < 0 || offset > usable_flash_size || + offset + size > usable_flash_size) + return EC_ERROR_UNKNOWN; /* Invalid range */ + + /* Just read the flash from its memory window. */ + /* TODO: is this affected by data cache? That is, if we read a + * block, then alter it, then read it again, do we get the old + * data? */ + memcpy(data, (char *)offset, size); + return EC_SUCCESS; +} + + +/* Performs a write-buffer operation. Buffer (FWB) and address (FMA) + * must be pre-loaded. */ +static int WriteBuffer(void) +{ + if (!LM4_FLASH_FWBVAL) + return EC_SUCCESS; /* Nothing to do */ + + /* Clear previous error status */ + LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; + + /* Start write operation at page boundary */ + LM4_FLASH_FMC2 = 0xa4420001; + + /* Wait for write to complete */ + while (LM4_FLASH_FMC2 & 0x01) {} + + /* Check for error conditions - program failed, erase needed, + * voltage error. */ + if (LM4_FLASH_FCRIS & 0x2600) + return EC_ERROR_UNKNOWN; + + return EC_SUCCESS; +} + + +int flash_write(int offset, int size, const char *data) +{ + const uint32_t *data32 = (const uint32_t *)data; + int rv; + int i; + + if (size < 0 || offset > usable_flash_size || + offset + size > usable_flash_size || + (offset | size) & (FLASH_WRITE_BYTES - 1)) + return EC_ERROR_UNKNOWN; /* Invalid range */ + + /* TODO - safety check - don't allow writing to the image we're + * running from */ + + /* Get initial page and write buffer index */ + LM4_FLASH_FMA = offset & ~(FLASH_FWB_BYTES - 1); + i = (offset >> 2) & (FLASH_FWB_WORDS - 1); + + /* Copy words into buffer */ + for ( ; size > 0; size -= 4) { + LM4_FLASH_FWB[i++] = *data32++; + if (i == FLASH_FWB_WORDS) { + rv = WriteBuffer(); + if (rv != EC_SUCCESS) + return rv; + + /* Advance to next page */ + i = 0; + LM4_FLASH_FMA += FLASH_FWB_BYTES; + } + } + + /* Handle final partial page, if any */ + if (i > 0) { + rv = WriteBuffer(); + if (rv != EC_SUCCESS) + return rv; + } + return EC_SUCCESS; +} + + +int flash_erase(int offset, int size) +{ + if (size < 0 || offset > usable_flash_size || + offset + size > usable_flash_size || + (offset | size) & (FLASH_ERASE_BYTES - 1)) + return EC_ERROR_UNKNOWN; /* Invalid range */ + + /* TODO - safety check - don't allow erasing the image we're running + * from */ + + LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */ + LM4_FLASH_FMA = offset; + + for ( ; size > 0; size -= FLASH_ERASE_BYTES) { + /* Start erase */ + LM4_FLASH_FMC = 0xa4420002; + + /* Wait for erase to complete */ + while (LM4_FLASH_FMC & 0x02) {} + + /* Check for error conditions - erase failed, voltage error */ + if (LM4_FLASH_FCRIS & 0x0a00) + return EC_ERROR_UNKNOWN; + + LM4_FLASH_FMA += FLASH_ERASE_BYTES; + } + + return EC_SUCCESS; +} + + +int flash_get_write_protect_range(int *offset, int *size) +{ + return EC_ERROR_UNIMPLEMENTED; +} + + +int flash_set_write_protect_range(int offset, int size) +{ + return EC_ERROR_UNIMPLEMENTED; +} + + +int flash_get_write_protect_status(void) +{ + return EC_ERROR_UNIMPLEMENTED; +} + + +int flash_init(void) +{ + /* Calculate usable flash size. Reserve one protection block + * at the top to hold the write protect range. FSIZE already + * returns one less than the number of protection pages. */ + usable_flash_size = LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES; + + /* TODO - check WP# GPIO. If it's set and the flash protect range + * is set, write the flash protection registers. */ + return EC_SUCCESS; +} diff --git a/chip/lm4/gpio.c b/chip/lm4/gpio.c new file mode 100644 index 0000000000..2ec64ce8af --- /dev/null +++ b/chip/lm4/gpio.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2011 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 "gpio.h" +#include "registers.h" + + +int gpio_pre_init(void) +{ + /* Enable clock to GPIO block A */ + LM4_SYSTEM_RCGCGPIO |= 0x0001; + + /* Turn off the LED before we make it an output */ + gpio_set(EC_GPIO_DEBUG_LED, 0); + + /* Clear GPIOAFSEL bits for block A pin 7 */ + LM4_GPIO_AFSEL(A) &= ~(0x80); + + /* Set GPIO to digital enable, output */ + LM4_GPIO_DEN(A) |= 0x80; + LM4_GPIO_DIR(A) |= 0x80; + + return EC_SUCCESS; +} + + +int gpio_init(void) +{ + return EC_SUCCESS; +} + + +int gpio_get(enum gpio_signal signal, int *value_ptr) +{ + switch (signal) { + case EC_GPIO_DEBUG_LED: + *value_ptr = (LM4_GPIO_DATA_BITS(A, 0x200) & 0x80 ? 1 : 0); + return EC_SUCCESS; + default: + return EC_ERROR_UNKNOWN; + } +} + + +int gpio_set(enum gpio_signal signal, int value) +{ + switch (signal) { + case EC_GPIO_DEBUG_LED: + LM4_GPIO_DATA_BITS(A, 0x200) = (value ? 0x80 : 0); + return EC_SUCCESS; + default: + return EC_ERROR_UNKNOWN; + } +} diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c new file mode 100644 index 0000000000..401bfdf6b9 --- /dev/null +++ b/chip/lm4/i2c.c @@ -0,0 +1,224 @@ +/* Copyright (c) 2011 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. + */ + +/* Temperature sensor module for Chrome EC */ + +#include "board.h" +#include "console.h" +#include "i2c.h" +#include "timer.h" +#include "registers.h" +#include "uart.h" +#include "util.h" + +/* I2C ports */ +#define I2C_PORT_THERMAL 5 // port 5 / PB6:7 on link, but PG6:7 on badger +#define I2C_PORT_BATTERY 5 // port 0 / PB2:3 on Link, open on badger +#define I2C_PORT_CHARGER 5 // port 1 / PA6:7 on Link, user LED on badger +/* I2C port speed in kbps */ +#define I2C_SPEED_THERMAL 400 /* TODO: TMP007 supports 3.4Mbps + operation; use faster speed? */ +#define I2C_SPEED_BATTERY 100 +#define I2C_SPEED_CHARGER 100 + + +/* Waits for the I2C bus to go idle */ +static int wait_idle(int port) +{ + int s, i; + + /* Spin waiting for busy flag to go away */ + /* TODO: should use interrupt handler */ + for (i = 1000; i >= 0; i--) { + s = LM4_I2C_MCS(port); + if (s & 0x01) { + /* Still busy */ + udelay(1000); + continue; + } + + /* Check for errors */ + if (s & 0x02) + return EC_ERROR_UNKNOWN; + + return EC_SUCCESS; + } + return EC_ERROR_TIMEOUT; +} + + +int i2c_read16(int port, int slave_addr, int offset, int* data) +{ + int rv; + int d; + + *data = 0; + + /* Transmit the offset address to the slave; leave the master in + * transmit state. */ + LM4_I2C_MSA(port) = (slave_addr & 0xff) | 0x00; + LM4_I2C_MDR(port) = offset & 0xff; + LM4_I2C_MCS(port) = 0x03; + + rv = wait_idle(port); + if (rv) + return rv; + + /* Send repeated start followed by receive */ + LM4_I2C_MSA(port) = (slave_addr & 0xff) | 0x01; + LM4_I2C_MCS(port) = 0x0b; + + rv = wait_idle(port); + if (rv) + return rv; + + /* Read the first byte */ + d = LM4_I2C_MDR(port) & 0xff; + + /* Issue another read and then a stop. */ + LM4_I2C_MCS(port) = 0x05; + + rv = wait_idle(port); + if (rv) + return rv; + + /* Read the second byte */ + if (slave_addr & I2C_FLAG_BIG_ENDIAN) + *data = (d << 8) | (LM4_I2C_MDR(port) & 0xff); + else + *data = ((LM4_I2C_MDR(port) & 0xff) << 8) | d; + return EC_SUCCESS; +} + + +int i2c_write16(int port, int slave_addr, int offset, int data) +{ + int rv; + + /* Transmit the offset address to the slave; leave the master in + * transmit state. */ + LM4_I2C_MDR(port) = offset & 0xff; + LM4_I2C_MSA(port) = (slave_addr & 0xff) | 0x00; + LM4_I2C_MCS(port) = 0x03; + + rv = wait_idle(port); + if (rv) + return rv; + + /* Transmit the first byte */ + if (slave_addr & I2C_FLAG_BIG_ENDIAN) + LM4_I2C_MDR(port) = (data >> 8) & 0xff; + else + LM4_I2C_MDR(port) = data & 0xff; + LM4_I2C_MCS(port) = 0x01; + + rv = wait_idle(port); + if (rv) + return rv; + + /* Transmit the second byte and then a stop */ + if (slave_addr & I2C_FLAG_BIG_ENDIAN) + LM4_I2C_MDR(port) = data & 0xff; + else + LM4_I2C_MDR(port) = (data >> 8) & 0xff; + LM4_I2C_MCS(port) = 0x05; + + return wait_idle(port); +} + + +/*****************************************************************************/ +/* Console commands */ + + +static void scan_bus(int port, char *desc) +{ + int rv; + int a; + + uart_printf("Scanning %s I2C bus...\n", desc); + + for (a = 0; a < 0x100; a += 2) { + uart_puts("."); + + /* Do a single read */ + LM4_I2C_MSA(port) = a | 0x01; + LM4_I2C_MCS(port) = 0x07; + rv = wait_idle(port); + if (rv == EC_SUCCESS) + uart_printf("\nFound device at 0x%02x\n", a); + } + uart_puts("\n"); +} + + +static int command_scan(int argc, char **argv) +{ + scan_bus(I2C_PORT_THERMAL, "thermal"); + scan_bus(I2C_PORT_BATTERY, "battery"); + scan_bus(I2C_PORT_CHARGER, "charger"); + uart_puts("done.\n"); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"i2cscan", command_scan}, +}; +static const struct console_group command_group = { + "I2C", console_commands, ARRAY_SIZE(console_commands) +}; + + +/*****************************************************************************/ +/* Initialization */ + +/* Configures GPIOs for the module. */ +static void configure_gpio(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable GPIOG module and delay a few clocks */ + LM4_SYSTEM_RCGCGPIO |= 0x0040; + scratch = LM4_SYSTEM_RCGCGPIO; + + /* Use alternate function 3 for PG6:7 */ + LM4_GPIO_AFSEL(G) |= 0xc0; + LM4_GPIO_PCTL(G) = (LM4_GPIO_PCTL(N) & 0x00ffffff) | 0x33000000; + LM4_GPIO_DEN(G) |= 0xc0; + /* Configure SDA as open-drain. SCL should not be open-drain, + * since it has an internal pull-up. */ + LM4_GPIO_ODR(G) |= 0x80; +} + + +int i2c_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable I2C5 module and delay a few clocks */ + LM4_SYSTEM_RCGCI2C |= (1 << I2C_PORT_THERMAL) | + (1 << I2C_PORT_BATTERY) | (1 << I2C_PORT_CHARGER); + scratch = LM4_SYSTEM_RCGCI2C; + + /* Configure GPIOs */ + configure_gpio(); + + /* Initialize ports as master */ + LM4_I2C_MCR(I2C_PORT_THERMAL) = 0x10; + LM4_I2C_MTPR(I2C_PORT_THERMAL) = + (CPU_CLOCK / (I2C_SPEED_THERMAL * 10 * 2)) - 1; + + LM4_I2C_MCR(I2C_PORT_BATTERY) = 0x10; + LM4_I2C_MTPR(I2C_PORT_BATTERY) = + (CPU_CLOCK / (I2C_SPEED_BATTERY * 10 * 2)) - 1; + + LM4_I2C_MCR(I2C_PORT_CHARGER) = 0x10; + LM4_I2C_MTPR(I2C_PORT_CHARGER) = + (CPU_CLOCK / (I2C_SPEED_CHARGER * 10 * 2)) - 1; + + console_register_commands(&command_group); + return EC_SUCCESS; +} diff --git a/chip/lm4/keyboard_scan.c b/chip/lm4/keyboard_scan.c new file mode 100644 index 0000000000..e78e8a5241 --- /dev/null +++ b/chip/lm4/keyboard_scan.c @@ -0,0 +1,231 @@ +/* Copyright (c) 2011 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. + */ + +/* Keyboard scanner module for Chrome EC */ + +#include "console.h" +#include "keyboard.h" +#include "keyboard_scan.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" + +#define KB_COLS 13 + +/* Notes: + * + * Columns (outputs): + * KSO0 - KSO7 = PQ0:7 + * KSO8 - KSO11 = PK0:3 + * KSO12 = PN2 + * Rows (inputs): + * KSI0 - KSI7 = PH0:7 + * Other: + * PWR_BTN# = PC5 + */ + +static uint8_t raw_state[KB_COLS]; + +/* Mask with 1 bits only for keys that actually exist */ +static const uint8_t *actual_key_mask; + +/* All actual key masks (todo: move to keyboard matrix definition */ +/* TODO: fill in real key mask with 0-bits for coords that aren't keys */ +static const uint8_t actual_key_masks[4][KB_COLS] = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0}, + {0}, + {0}, + }; + +/* Drives the specified column low; other columns are tri-stated */ +static void select_column(int col) +{ +#if defined(EVT) + if (col < 0) { + LM4_GPIO_DIR(P) &= ~0xff; + LM4_GPIO_DIR(Q) &= ~0x1f; + } else if (col < 8) { + LM4_GPIO_DIR(P) &= ~0xff; + LM4_GPIO_DIR(Q) &= ~0x1f; + LM4_GPIO_DATA_BITS(P, 0xff << 2) = ~(1 << col); + LM4_GPIO_DIR(P) = (1 << col) & 0xff; + } else { + LM4_GPIO_DIR(P) &= ~0xff; + LM4_GPIO_DIR(Q) &= ~0x1f; + LM4_GPIO_DATA_BITS(Q, 0xff << 2) = ~(1 << (col - 8)); + LM4_GPIO_DIR(Q) |= 1 << (col - 8); + } +#else /* BDS definition */ + /* Somehow the col 10 and 11 are swapped on bds. */ + if (col == 10) { + col = 11; + } else if (col == 11) { + col = 10; + } + + if (col < 0) { + /* All tri-stated */ + LM4_GPIO_DIR(Q) = 0; + LM4_GPIO_DIR(K) &= ~0x0f; + LM4_GPIO_DIR(N) &= ~0x04; + } else if (col < 8) { + LM4_GPIO_DIR(Q) = 1 << col; + LM4_GPIO_DIR(K) &= ~0x0f; + LM4_GPIO_DIR(N) &= ~0x04; + LM4_GPIO_DATA_BITS(Q, 0xff << 2) = ~(1 << col); + } else if (col < 12) { + LM4_GPIO_DIR(Q) = 0; + LM4_GPIO_DIR(K) = (LM4_GPIO_DIR(K) & ~0x0f) | (1 << (col - 8)); + LM4_GPIO_DIR(N) &= ~0x04; + LM4_GPIO_DATA_BITS(K, 0x0f << 2) = ~(1 << (col - 8)); + } else { /* col == 12 */ + LM4_GPIO_DIR(Q) = 0; + LM4_GPIO_DIR(K) &= ~0x0f; + LM4_GPIO_DIR(N) |= 0x04; + LM4_GPIO_DATA_BITS(N, 0x04 << 2) = ~0x04; + } +#endif +} + +int keyboard_scan_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + int i; + + /* Enable GPIOs */ +#if defined(EVT) + /* Enable clock to GPIO modules C,H,K,N,P,Q */ + LM4_SYSTEM_RCGCGPIO |= 0x7284; +#else + /* Enable clock to GPIO modules C,H,K,N,Q */ + LM4_SYSTEM_RCGCGPIO |= 0x5284; +#endif + scratch = LM4_SYSTEM_RCGCGPIO; + + /* Clear GPIOAFSEL and enable digital function for PC5, PH0:7, + * PK0:3, PN2, PQ0:7. */ + LM4_GPIO_AFSEL(C) &= ~0x20; + LM4_GPIO_DEN(C) |= 0x20; +#if defined(EVT) + LM4_GPIO_AFSEL(N) &= 0xff; /* KSI[7:0] */ + LM4_GPIO_DEN(N) |= 0xff; + LM4_GPIO_AFSEL(P) &= 0xff; /* KSO[7:0] */ + LM4_GPIO_DEN(P) |= 0xff; + LM4_GPIO_AFSEL(Q) &= 0x1f; /* KSO[12:8] */ + LM4_GPIO_DEN(Q) |= 0x1f; +#else + LM4_GPIO_AFSEL(H) = 0; + LM4_GPIO_DEN(H) = 0xff; + LM4_GPIO_AFSEL(K) &= ~0x0f; + LM4_GPIO_DEN(K) |= 0x0f; + LM4_GPIO_AFSEL(N) &= ~0x04; + LM4_GPIO_DEN(N) |= 0x04; + LM4_GPIO_AFSEL(Q) = 0; + LM4_GPIO_DEN(Q) = 0xff; +#endif + +#if defined(EVT) + /* Set PN0:7 as inputs with pull-up */ + LM4_GPIO_DIR(N) = 0; + LM4_GPIO_PUR(N) = 0xff; +#else + /* Set PH0:7 as inputs with pull-up */ + LM4_GPIO_DIR(H) = 0; + LM4_GPIO_PUR(H) = 0xff; +#endif + + /* Set PC5 as input with pull-up. */ + /* TODO: no need for pull-up on real circuit, since it'll be + * externally pulled up. */ + LM4_GPIO_DIR(C) &= ~0x04; + LM4_GPIO_PUR(C) |= 0x04; + + /* Tri-state the columns */ + select_column(-1); + + /* Initialize raw state */ + for (i = 0; i < KB_COLS; i++) + raw_state[i] = 0; + + /* TODO: method to set which keyboard we have, so we set the actual + * key mask properly */ + actual_key_mask = actual_key_masks[0]; + + return EC_SUCCESS; +} + + +void check_keys_down(void) +{ + int c; + uint8_t r; + int change = 0; + + for (c = 0; c < KB_COLS; c++) { + /* Select column, then wait a bit for it to settle */ + select_column(c); + usleep(20); + /* Read the row state */ +#if defined(EVT) + r = LM4_GPIO_DATA_BITS(N, 0xff << 2); +#else + r = LM4_GPIO_DATA_BITS(H, 0xff << 2); +#endif + /* Invert it so 0=not pressed, 1=pressed */ + r ^= 0xff; + /* Mask off keys that don't exist so they never show + * as pressed */ + r &= actual_key_mask[c]; + +#ifdef OR_WITH_CURRENT_STATE_FOR_TESTING + /* KLUDGE - or current state in, so we can make sure + * all the lines are hooked up */ + r |= raw_state[c]; +#endif + + /* Check for changes */ + if (r != raw_state[c]) { + int i; + for (i = 0; i < 8; ++i) { + uint8_t prev = (raw_state[c] >> i) & 1; + uint8_t now = (r >> i) & 1; + if (prev != now) { + keyboard_state_changed(i, c, now); + } + } + raw_state[c] = r; + change = 1; + } + } + select_column(-1); + + if (change) { + uart_puts("[Keyboard state:"); + for (c = 0; c < KB_COLS; c++) { + if (raw_state[c]) + uart_printf(" %02x", raw_state[c]); + else + uart_puts(" --"); + } + uart_puts("]\n"); + } +} + + +void keyboard_scan_task(void) +{ + keyboard_scan_init(); + + while (1) { + /* Sleep for a while */ + usleep(25000); + /* Check for keys down */ + check_keys_down(); + } +} diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c new file mode 100644 index 0000000000..a377890c41 --- /dev/null +++ b/chip/lm4/lpc.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2011 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. + */ + +/* LPC module for Chrome EC */ + +#include "board.h" +#include "host_command.h" +#include "i8042.h" +#include "lpc.h" +#include "port80.h" +#include "registers.h" +#include "task.h" +#include "uart.h" + + +/* Configures GPIOs for module. */ +static void configure_gpio(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable clocks to GPIO modules L, M (p. 404) */ + LM4_SYSTEM_RCGCGPIO |= 0x0c00; + scratch = LM4_SYSTEM_RCGCGPIO; + + /* Set digital alternate function 15 for PL0:5, PM0:2, PM4:5 pins. */ + LM4_GPIO_AFSEL(L) |= 0x3f; + LM4_GPIO_AFSEL(M) |= 0x37; + LM4_GPIO_PCTL(L) |= 0x00ffffff; + LM4_GPIO_PCTL(M) |= 0x00ff0fff; + LM4_GPIO_DEN(L) |= 0x3f; + LM4_GPIO_DEN(M) |= 0x37; + + /* Set the drive strength to 8mA. */ + /* TODO: Only necessary on BDS because the cabling to the x86 + is long and flaky; remove this for Link. */ + LM4_GPIO_DR8R(L) |= 0x0000003f; + LM4_GPIO_DR8R(M) |= 0x00000037; +} + + +int lpc_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable RGCGLPC then delay a few clocks. */ + LM4_SYSTEM_RCGCLPC = 1; + scratch = LM4_SYSTEM_RCGCLPC; + + /* Configure GPIOs */ + configure_gpio(); + + /* Set LPC channel 0 to I/O address 0x62 (data) / 0x66 (command), + * single endpoint, offset 0 for host command/writes and 1 for EC + * data writes, pool bytes 0(data)/1(cmd) */ + LM4_LPC_ADR(0) = 0x62; + LM4_LPC_CTL(0) = (LPC_POOL_OFFS_KERNEL << (5 - 1)); + + /* Set LPC channel 1 to I/O address 0x80 (data), single endpoint, + * pool bytes 4(data)/5(cmd). */ + LM4_LPC_ADR(1) = 0x80; + LM4_LPC_CTL(1) = (LPC_POOL_OFFS_PORT80 << (5 - 1)); + + /* Set LPC channel 2 to I/O address 0x800, range endpoint, + * arbitration disabled, pool bytes 512-1023. To access this from + * x86, use the following commands to set GEN_LPC2 and GEN_LPC3: + * + * pci_write32 0 0x1f 0 0x88 0x007c0801 + * pci_write32 0 0x1f 0 0x8c 0x007c0901 + */ + LM4_LPC_ADR(2) = 0x800; + LM4_LPC_CTL(2) = 0x801D | (LPC_POOL_OFFS_CMD_DATA << (5 - 1)); + + /* Set LPC channel 3 to I/O address 0x60 (data) / 0x64 (command), + * single endpoint, offset 0 for host command/writes and 1 for EC + * data writes, pool bytes 0(data)/1(cmd) */ + LM4_LPC_ADR(3) = 0x60; + LM4_LPC_CTL(3) = (1 << 24/* IRQSEL1 */) | + (1 << 18/* IRQEN1 */) | + (LPC_POOL_OFFS_KEYBOARD << (5 - 1)); + LM4_LPC_ST(3) = 0; + + /* Set LPC channel 7 to I/O address 0x2F8 (COM2), bytes 8-15. + * Channel 7 ignores the TYPE bit. */ + LM4_LPC_ADR(7) = 0x2f8; + /* TODO: could configure IRQSELs and set IRQEN2/CX, and then the host + * can enable IRQs on its own. */ + LM4_LPC_CTL(7) = 0x0004 | (LPC_POOL_OFFS_COMX << (5 - 1)); + +#ifdef USE_LPC_COMx_DMA + /* TODO: haven't been able to get this to work yet */ + /* COMx UART DMA mode */ + LM4_LPC_LPCDMACX = 0x00070000; + + /* TODO: set up DMA */ + LM4_SYSTEM_RCGCDMA = 1; + /* Wait 3 clocks before accessing other DMA regs */ + LM4_SYSTEM_RCGCDMA = 1; + LM4_SYSTEM_RCGCDMA = 1; + LM4_SYSTEM_RCGCDMA = 1; + /* Enable master */ + LM4_DMA_DMACFG = 1; + /* TODO: hope we don't need the channel control structs; we're just + * throwing this somewhere in memory. Shouldn't need it if we leave + * all the channel disabled, though. */ + LM4_DMA_DMACTLBASE = 0x20004000; + /* Map UART and LPC DMA functions to channels */ + LM4_DMA_DMACHMAP0 = 0x00003000; /* Channel 3 encoding 3 = LPC0 Ch3 */ + LM4_DMA_DMACHMAP1 = 0x00000011; /* Channels 8,9 encoding 1 = UART1 */ +#else + /* Use our LPC interrupt handler to notify COMxIM on write-from-host */ + LM4_LPC_LPCDMACX = 0x00110000; + LM4_LPC_LPCIM |= 0x20000000; +#endif + + /* Unmask interrupt bits for channel 0 command write (CH0IM2), + * channel 1 data write (CH1IM1) and channel 3 data write (CH3IM3). */ + LM4_LPC_LPCIM |= 0x7024; + + /* Enable LPC channels 0, 1, 2, 3, 7 in LPCCTL */ + LM4_LPC_LPCCTL |= 0x8F; + + return EC_SUCCESS; +} + + +volatile uint8_t *lpc_get_host_range(void) +{ + return LPC_POOL_CMD_DATA; +} + + +void lpc_send_host_response(int status) +{ + /* Set status nibble (bits 7:4 from host side) and clear the busy + * bit (0x1000) (bit 2 from host side) */ + LM4_LPC_ST(0) = (LM4_LPC_ST(0) & 0xffffe0ff) | ((status & 0xf) << 8); + + /* Write dummy value to data byte. This sets the TOH bit in the + * status byte and triggers an IRQ on the host so the host can read + * the status. */ + /* TODO: or it would, if we actually set up host IRQs */ + LPC_POOL_KERNEL[1] = 0; +} + + +/* LPC interrupt handler */ +static void lpc_interrupt(void) +{ + uint32_t mis = LM4_LPC_LPCMIS; + + /* Clear the interrupt bits we're handling */ + LM4_LPC_LPCIC = mis; + + /* Handle port 66 command writes */ + if (mis & 0x04) { + /* Set the busy bit and clear the status */ + LM4_LPC_ST(0) = (LM4_LPC_ST(0) & 0xffffe0ff) | 0x1000; + + /* Read the command byte and pass to the host command handler. + * This clears the FRMH bit in the status byte. */ + host_command_received(LPC_POOL_KERNEL[0]); + } + + /* Handle port 80 writes (CH0MIS1) */ + if (mis & 0x20) + port_80_write(LPC_POOL_PORT80[0]); + + /* Handle port 60 command (CH3MIS2) and data (CH3MIS1) */ + if (mis & 0x2000) { + /* Read the data byte and pass to the i8042 handler. + * This clears the FRMH bit in the status byte. */ + i8042_receives_data(LPC_POOL_KEYBOARD[0]); + } + if (mis & 0x4000) { + /* Read the command byte and pass to the i8042 handler. + * This clears the FRMH bit in the status byte. */ + i8042_receives_command(LPC_POOL_KEYBOARD[0]); + } + if (mis & 0x1000) { + /* Host picks up the data, try to send remaining bytes */ + task_send_msg(TASK_ID_I8042CMD, TASK_ID_I8042CMD, 0); + } + + /* Handle COMx */ + if (mis & 0x20000000) { + uint32_t cis = LM4_LPC_LPCDMACX; + /* Clear the interrupt reasons we're handling */ + LM4_LPC_LPCDMACX = cis; + + /* Handle host writes */ + if (LM4_LPC_ST(7) & 0x02) { + if (LM4_UART_FR(1) & 0x20) { + /* FIFO is full, so enable transmit + * interrupt to let us know when it + * empties */ + LM4_UART_IM(1) |= 0x20; + } else { + /* Space in FIFO, so copy byte */ + LM4_UART_DR(1) = LPC_POOL_COMX[0]; + } + } + + /* TODO: if host read the from-host data, see if + * there's another byte still waiting on UART1. */ + } +} + +DECLARE_IRQ(108, lpc_interrupt, 2); diff --git a/chip/lm4/pwm.c b/chip/lm4/pwm.c new file mode 100644 index 0000000000..da9acb5e89 --- /dev/null +++ b/chip/lm4/pwm.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2011 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. + */ + +/* PWM control module for Chrome EC */ + +#include "board.h" +#include "console.h" +#include "pwm.h" +#include "registers.h" +#include "uart.h" +#include "util.h" + +/* Maximum RPM for fan controller */ +#define MAX_RPM 0x1fff +/* Max PWM for fan controller */ +#define MAX_PWM 0x1ff + + +/* Configures the GPIOs for the fan module. */ +/* TODO: this is currently hard-coded for BDS; needs to do the right + thing for Link. */ +static void configure_gpios(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable GPIO K,N modules and delay a few clocks */ + LM4_SYSTEM_RCGCGPIO |= 0x1200; + scratch = LM4_SYSTEM_RCGCGPIO; + + /* Use alternate function 1 for PN4 (channel 3 PWM) and PN6:7 + (channel 4 PWM/tach) */ + // TODO: depends on which PWMs we're using + LM4_GPIO_AFSEL(N) |= 0xd0; + LM4_GPIO_PCTL(N) = (LM4_GPIO_PCTL(N) & 0x00f0ffff) | 0x11010000; + LM4_GPIO_DEN(N) |= 0xd0; + + /* Use alternate function 1 for PK6 (channel 1 PWM) */ + // TODO: GPIO module depends on fan channel + LM4_GPIO_AFSEL(K) |= 0x40; + LM4_GPIO_PCTL(K) = (LM4_GPIO_PCTL(K) & 0xf0ffffff) | 0x01000000; + LM4_GPIO_DEN(K) |= 0x40; + +} + + +int pwm_get_fan_rpm(void) +{ + return LM4_FAN_FANCST(FAN_CH_CPU) & MAX_RPM; +} + + +int pwm_set_fan_target_rpm(int rpm) +{ + /* Treat out-of-range requests as requests for maximum fan speed */ + if (rpm < 0 || rpm > MAX_RPM) + rpm = MAX_RPM; + + LM4_FAN_FANCMD(FAN_CH_CPU) = rpm; + return EC_SUCCESS; +} + + +int pwm_set_keyboard_backlight(int percent) +{ + LM4_FAN_FANCMD(FAN_CH_KBLIGHT) = ((percent * MAX_PWM) / 100) << 16; + return EC_SUCCESS; +} + + +int pwm_set_power_led(int percent) +{ + LM4_FAN_FANCMD(FAN_CH_POWER_LED) = ((percent * MAX_PWM) / 100) << 16; + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Console commands */ + +static int command_fan_info(int argc, char **argv) +{ + uart_printf("Fan actual speed: %4d rpm\n", pwm_get_fan_rpm()); + uart_printf(" target speed: %4d rpm\n", + LM4_FAN_FANCMD(FAN_CH_CPU) & MAX_RPM); + uart_printf(" duty cycle: %d%%\n", + ((LM4_FAN_FANCMD(FAN_CH_CPU) >> 16)) * 100 / MAX_PWM); + uart_printf(" status: %d\n", + (LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03); + return EC_SUCCESS; +} + + +static int command_fan_set(int argc, char **argv) +{ + int rpm = 0; + char *e; + int rv; + + if (argc < 2) { + uart_puts("Usage: fanset <rpm>\n"); + return EC_ERROR_UNKNOWN; + } + + rpm = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid speed\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("Setting fan speed to %d rpm...\n", rpm); + + /* Move the fan to automatic control */ + if (LM4_FAN_FANCH(FAN_CH_CPU) & 0x0001) { + LM4_FAN_FANCTL &= ~(1 << FAN_CH_CPU); + LM4_FAN_FANCH(FAN_CH_CPU) &= ~0x0001; + LM4_FAN_FANCTL |= (1 << FAN_CH_CPU); + } + + rv = pwm_set_fan_target_rpm(rpm); + if (rv == EC_SUCCESS) + uart_printf("Done.\n"); + return rv; +} + + +/* TODO: this is a temporary command for debugging tach issues */ +static int command_fan_duty(int argc, char **argv) +{ + int d = 0, pwm; + char *e; + + if (argc < 2) { + uart_puts("Usage: fanduty <percent>\n"); + return EC_ERROR_UNKNOWN; + } + + d = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid duty cycle\n"); + return EC_ERROR_UNKNOWN; + } + + pwm = (MAX_PWM * d) / 100; + uart_printf("Setting fan duty cycle to %d%% = 0x%x...\n", d, pwm); + + /* Move the fan to manual control */ + if (!(LM4_FAN_FANCH(FAN_CH_CPU) & 0x0001)) { + LM4_FAN_FANCTL &= ~(1 << FAN_CH_CPU); + LM4_FAN_FANCH(FAN_CH_CPU) |= 0x0001; + LM4_FAN_FANCTL |= (1 << FAN_CH_CPU); + } + + /* Set the duty cycle */ + LM4_FAN_FANCMD(FAN_CH_CPU) = pwm << 16; + + return EC_SUCCESS; +} + + +static int command_kblight(int argc, char **argv) +{ + char *e; + int rv; + int i; + + if (argc < 2) { + uart_puts("Usage: kblight <percent>\n"); + return EC_ERROR_UNKNOWN; + } + + i = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid percent\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("Setting keyboard backlight to %d%%...\n", i); + rv = pwm_set_keyboard_backlight(i); + if (rv == EC_SUCCESS) + uart_printf("Done.\n"); + return rv; +} + + +static int command_powerled(int argc, char **argv) +{ + char *e; + int rv; + int i; + + if (argc < 2) { + uart_puts("Usage: powerled <percent>\n"); + return EC_ERROR_UNKNOWN; + } + + i = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid percent\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("Setting power LED to %d%%...\n", i); + rv = pwm_set_keyboard_backlight(i); + if (rv == EC_SUCCESS) + uart_printf("Done.\n"); + return rv; +} + + +static const struct console_command console_commands[] = { + {"fanduty", command_fan_duty}, + {"faninfo", command_fan_info}, + {"fanset", command_fan_set}, + {"kblight", command_kblight}, + {"powerled", command_powerled}, +}; +static const struct console_group command_group = { + "PWM", console_commands, ARRAY_SIZE(console_commands) +}; + + +/*****************************************************************************/ +/* Initialization */ + +int pwm_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable the fan module and delay a few clocks */ + LM4_SYSTEM_RCGCFAN = 1; + scratch = LM4_SYSTEM_RCGCFAN; + + /* Configure GPIOs */ + configure_gpios(); + + /* Disable all fans */ + LM4_FAN_FANCTL = 0; + + /* Configure CPU fan: + * 0x8000 = bit 15 = auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0020 = bits 5:4 = average 4 edges when calculating RPM + * 0x0008 = bits 3:2 = 4 pulses per revolution + * 0x0000 = bit 0 = automatic control */ + LM4_FAN_FANCH(FAN_CH_CPU) = 0x8028; + + /* Configure keyboard backlight: + * 0x0000 = bit 15 = auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0000 = bits 5:4 = average 4 edges when calculating RPM + * 0x0000 = bits 3:2 = 4 pulses per revolution + * 0x0001 = bit 0 = manual control */ + LM4_FAN_FANCH(FAN_CH_KBLIGHT) = 0x0001; + + /* Configure power LED: + * 0x0000 = bit 15 = auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0000 = bits 5:4 = average 4 edges when calculating RPM + * 0x0000 = bits 3:2 = 4 pulses per revolution + * 0x0001 = bit 0 = manual control */ + LM4_FAN_FANCH(FAN_CH_POWER_LED) = 0x0001; + + /* Set initial fan speed to maximum, backlight off, power LED off */ + pwm_set_fan_target_rpm(-1); + pwm_set_keyboard_backlight(0); + pwm_set_power_led(0); + + /* Enable CPU fan and keyboard backlight */ + LM4_FAN_FANCTL |= (1 << FAN_CH_CPU) | (1 << FAN_CH_KBLIGHT) | + (1 << FAN_CH_POWER_LED); + + console_register_commands(&command_group); + return EC_SUCCESS; +} diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h new file mode 100644 index 0000000000..c2c214dbd1 --- /dev/null +++ b/chip/lm4/registers.h @@ -0,0 +1,312 @@ +/* Copyright (c) 2011 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. + * Copyright 2011 Google Inc. + * + * Register map for LM4x processor + */ + +#ifndef __LM4_REGISTERS +#define __LM4_REGISTERS + +#include <stdint.h> + +/* Macro to access 32-bit registers */ +#define LM4REG(addr) (*(volatile uint32_t*)(addr)) + +#define LM4_UART_CH0_BASE 0x4000c000 +#define LM4_UART_CH1_BASE 0x4000d000 +#define LM4UARTREG(ch, offset) LM4REG(LM4_UART_CH##ch##_BASE + (offset)) +#define LM4_UART_DR(ch) LM4UARTREG(ch, 0x000) +#define LM4_UART_FR(ch) LM4UARTREG(ch, 0x018) +#define LM4_UART_IBRD(ch) LM4UARTREG(ch, 0x024) +#define LM4_UART_FBRD(ch) LM4UARTREG(ch, 0x028) +#define LM4_UART_LCRH(ch) LM4UARTREG(ch, 0x02c) +#define LM4_UART_CTL(ch) LM4UARTREG(ch, 0x030) +#define LM4_UART_IFLS(ch) LM4UARTREG(ch, 0x034) +#define LM4_UART_IM(ch) LM4UARTREG(ch, 0x038) +#define LM4_UART_ICR(ch) LM4UARTREG(ch, 0x044) +#define LM4_UART_DMACTL(ch) LM4UARTREG(ch, 0x048) + +#define LM4_ADC_ADCACTSS LM4REG(0x40038000) +#define LM4_ADC_ADCRIS LM4REG(0x40038004) +#define LM4_ADC_ADCIM LM4REG(0x40038008) +#define LM4_ADC_ADCISC LM4REG(0x4003800C) +#define LM4_ADC_ADCOSTAT LM4REG(0x40038010) +#define LM4_ADC_ADCEMUX LM4REG(0x40038014) +#define LM4_ADC_ADCUSTAT LM4REG(0x40038018) +#define LM4_ADC_ADCSSPRI LM4REG(0x40038020) +#define LM4_ADC_ADCSPC LM4REG(0x40038024) +#define LM4_ADC_ADCPSSI LM4REG(0x40038028) +#define LM4_ADC_ADCSAC LM4REG(0x40038030) +#define LM4_ADC_ADCCTL LM4REG(0x40038038) +#define LM4_ADC_SS0_BASE 0x40038040 +#define LM4_ADC_SS1_BASE 0x40038060 +#define LM4_ADC_SS2_BASE 0x40038080 +#define LM4_ADC_SS3_BASE 0x400380a0 +#define LM4ADCREG(ss, offset) LM4REG(LM4_ADC_SS##ss##_BASE + (offset)) +#define LM4_ADC_SSMUX(ss) LM4ADCREG(ss, 0x000) +#define LM4_ADC_SSCTL(ss) LM4ADCREG(ss, 0x004) +#define LM4_ADC_SSFIFO(ss) LM4ADCREG(ss, 0x008) +#define LM4_ADC_SSFSTAT(ss) LM4ADCREG(ss, 0x00c) +#define LM4_ADC_SSOP(ss) LM4ADCREG(ss, 0x010) +#define LM4_ADC_SSEMUX(ss) LM4ADCREG(ss, 0x018) + +#define LM4_LPC_LPCCTL LM4REG(0x40080000) +#define LM4_LPC_LPCSTS LM4REG(0x40080004) +#define LM4_LPC_LPCIRQCTL LM4REG(0x40080008) +#define LM4_LPC_LPCIRQST LM4REG(0x4008000c) +#define LM4_LPC_LPCIM LM4REG(0x40080100) +#define LM4_LPC_LPCRIS LM4REG(0x40080104) +#define LM4_LPC_LPCMIS LM4REG(0x40080108) +#define LM4_LPC_LPCIC LM4REG(0x4008010C) +#define LM4_LPC_LPCDMACX LM4REG(0x40080120) +#define LM4_LPC_CH0_BASE 0x40080010 +#define LM4_LPC_CH1_BASE 0x40080020 +#define LM4_LPC_CH2_BASE 0x40080030 +#define LM4_LPC_CH3_BASE 0x40080040 +#define LM4_LPC_CH4_BASE 0x40080050 +#define LM4_LPC_CH5_BASE 0x40080060 +#define LM4_LPC_CH6_BASE 0x40080070 +#define LM4_LPC_CH7_BASE 0x40080080 +#define LM4LPCREG(ch, offset) LM4REG(LM4_LPC_CH##ch##_BASE + (offset)) +#define LM4_LPC_CTL(ch) LM4LPCREG(ch, 0x000) +#define LM4_LPC_ST(ch) LM4LPCREG(ch, 0x004) +#define LM4_LPC_ADR(ch) LM4LPCREG(ch, 0x008) +#define LM4_LPC_POOL_BYTES 1024 /* Size of LPCPOOL in bytes */ +#define LM4_LPC_LPCPOOL ((volatile unsigned char*)0x40080400) + +#define LM4_FAN_FANSTS LM4REG(0x40084000) +#define LM4_FAN_FANCTL LM4REG(0x40084004) +#define LM4_FAN_CH0_BASE 0x40084010 +#define LM4_FAN_CH1_BASE 0x40084020 +#define LM4_FAN_CH2_BASE 0x40084030 +#define LM4_FAN_CH3_BASE 0x40084040 +#define LM4_FAN_CH4_BASE 0x40084050 +#define LM4_FAN_CH5_BASE 0x40084060 +#define LM4FANREG(ch, offset) LM4REG(LM4_FAN_CH##ch##_BASE + (offset)) +#define LM4_FAN_FANCH(ch) LM4FANREG(ch, 0x000) +#define LM4_FAN_FANCMD(ch) LM4FANREG(ch, 0x004) +#define LM4_FAN_FANCST(ch) LM4FANREG(ch, 0x008) + +#define LM4_EEPROM_EESIZE LM4REG(0x400af000) +#define LM4_EEPROM_EEBLOCK LM4REG(0x400af004) +#define LM4_EEPROM_EEOFFSET LM4REG(0x400af008) +#define LM4_EEPROM_EERDWR LM4REG(0x400af010) +#define LM4_EEPROM_EERDWRINC LM4REG(0x400af014) +#define LM4_EEPROM_EEDONE LM4REG(0x400af018) +#define LM4_EEPROM_EESUPP LM4REG(0x400af01c) +#define LM4_EEPROM_EEUNLOCK LM4REG(0x400af020) +#define LM4_EEPROM_EEPROT LM4REG(0x400af030) +#define LM4_EEPROM_EEPASS0 LM4REG(0x400af034) +#define LM4_EEPROM_EEPASS1 LM4REG(0x400af038) +#define LM4_EEPROM_EEPASS2 LM4REG(0x400af03c) +#define LM4_EEPROM_EEINT LM4REG(0x400af040) +#define LM4_EEPROM_EEHIDE LM4REG(0x400af050) + +#define LM4_HIBERNATE_HIBRTCC LM4REG(0x400fc000) +#define LM4_HIBERNATE_HIBRTCM0 LM4REG(0x400fc004) +#define LM4_HIBERNATE_HIBRTCLD LM4REG(0x400fc00c) +#define LM4_HIBERNATE_HIBCTL LM4REG(0x400fc010) +#define LM4_HIBERNATE_HIBIM LM4REG(0x400fc014) +#define LM4_HIBERNATE_HIBRIS LM4REG(0x400fc018) +#define LM4_HIBERNATE_HIBIC LM4REG(0x400fc020) +#define LM4_HIBERNATE_HIBRTCT LM4REG(0x400fc024) +#define LM4_HIBERNATE_HIBRTCSS LM4REG(0x400fc028) +#define LM4_HIBERNATE_HIBDATA LM4REG(0x400fc030) + +#define LM4_FLASH_FMA LM4REG(0x400fd000) +#define LM4_FLASH_FMD LM4REG(0x400fd004) +#define LM4_FLASH_FMC LM4REG(0x400fd008) +#define LM4_FLASH_FCRIS LM4REG(0x400fd00c) +#define LM4_FLASH_FCMISC LM4REG(0x400fd014) +#define LM4_FLASH_FMC2 LM4REG(0x400fd020) +#define LM4_FLASH_FWBVAL LM4REG(0x400fd030) +/* FWB size is 32 words = 128 bytes */ +#define LM4_FLASH_FWB ((volatile uint32_t*)0x400fd100) +#define LM4_FLASH_FSIZE LM4REG(0x400fdfc0) +#define LM4_FLASH_FMPRE0 LM4REG(0x400fe200) +#define LM4_FLASH_FMPRE1 LM4REG(0x400fe204) +#define LM4_FLASH_FMPRE2 LM4REG(0x400fe208) +#define LM4_FLASH_FMPRE3 LM4REG(0x400fe20c) +#define LM4_FLASH_FMPPE0 LM4REG(0x400fe400) +#define LM4_FLASH_FMPPE1 LM4REG(0x400fe404) +#define LM4_FLASH_FMPPE2 LM4REG(0x400fe408) +#define LM4_FLASH_FMPPE3 LM4REG(0x400fe40c) + +#define LM4_SYSTEM_RIS LM4REG(0x400fe050) +#define LM4_SYSTEM_MISC LM4REG(0x400fe058) +#define LM4_SYSTEM_RESC LM4REG(0x400fe05c) +#define LM4_SYSTEM_RCC LM4REG(0x400fe060) +#define LM4_SYSTEM_RCC2 LM4REG(0x400fe070) +#define LM4_SYSTEM_PLLSTAT LM4REG(0x400fe168) +#define LM4_SYSTEM_RCGCWD LM4REG(0x400fe600) +#define LM4_SYSTEM_RCGCTIMER LM4REG(0x400fe604) +#define LM4_SYSTEM_RCGCGPIO LM4REG(0x400fe608) +#define LM4_SYSTEM_RCGCDMA LM4REG(0x400fe60c) +#define LM4_SYSTEM_RCGCHIB LM4REG(0x400fe614) +#define LM4_SYSTEM_RCGCUART LM4REG(0x400fe618) +#define LM4_SYSTEM_RCGCI2C LM4REG(0x400fe620) +#define LM4_SYSTEM_RCGCADC LM4REG(0x400fe638) +#define LM4_SYSTEM_RCGCLPC LM4REG(0x400fe648) +#define LM4_SYSTEM_RCGCFAN LM4REG(0x400fe654) +#define LM4_SYSTEM_RCGCEEPROM LM4REG(0x400fe658) +#define LM4_SYSTEM_RCGCWTIMER LM4REG(0x400fe65c) + +#define LM4_DMA_DMACFG LM4REG(0x400ff004) +#define LM4_DMA_DMACTLBASE LM4REG(0x400ff008) +#define LM4_DMA_DMACHMAP0 LM4REG(0x400ff510) +#define LM4_DMA_DMACHMAP1 LM4REG(0x400ff514) +#define LM4_DMA_DMACHMAP2 LM4REG(0x400ff518) +#define LM4_DMA_DMACHMAP3 LM4REG(0x400ff51c) + +#define LM4_NVIC_EN(x) LM4REG(0xe000e100 + 4 * (x)) +#define LM4_NVIC_DIS(x) LM4REG(0xe000e180 + 4 * (x)) +#define LM4_NVIC_PRI(x) LM4REG(0xe000e400 + 4 * (x)) +#define LM4_NVIC_APINT LM4REG(0xe000ed0c) +#define LM4_NVIC_SWTRIG LM4REG(0xe000ef00) + +#define LM4_SCB_SYSCTRL LM4REG(0xe000ed10) + +/* GPIO */ +#define LM4_GPIO_PORTA_BASE 0x40004000 +#define LM4_GPIO_PORTB_BASE 0x40005000 +#define LM4_GPIO_PORTC_BASE 0x40006000 +#define LM4_GPIO_PORTD_BASE 0x40007000 +#define LM4_GPIO_PORTE_BASE 0x40024000 +#define LM4_GPIO_PORTF_BASE 0x40025000 +#define LM4_GPIO_PORTG_BASE 0x40026000 +#define LM4_GPIO_PORTH_BASE 0x40027000 +#define LM4_GPIO_PORTK_BASE 0x40061000 +#define LM4_GPIO_PORTL_BASE 0x40062000 +#define LM4_GPIO_PORTM_BASE 0x40063000 +#define LM4_GPIO_PORTN_BASE 0x40064000 +#define LM4_GPIO_PORTP_BASE 0x40065000 +#define LM4_GPIO_PORTQ_BASE 0x40066000 +#define LM4_GPIO_PORTA_AHB_BASE 0x40058000 +#define LM4_GPIO_PORTB_AHB_BASE 0x40059000 +#define LM4_GPIO_PORTC_AHB_BASE 0x4005A000 +#define LM4_GPIO_PORTD_AHB_BASE 0x4005B000 +#define LM4_GPIO_PORTE_AHB_BASE 0x4005C000 +#define LM4_GPIO_PORTF_AHB_BASE 0x4005D000 +#define LM4_GPIO_PORTG_AHB_BASE 0x4005E000 +#define LM4_GPIO_PORTH_AHB_BASE 0x4005F000 +#define LM4_GPIO_PORTJ_AHB_BASE 0x40060000 + +#define LM4GPIOREG(port, offset) LM4REG(LM4_GPIO_PORT##port##_BASE + (offset)) + +#define LM4_GPIO_DATA_BITS(port, off) LM4GPIOREG(port, 0x000 + (off)) +#define LM4_GPIO_DIR(port) LM4GPIOREG(port, 0x400) +#define LM4_GPIO_IS(port) LM4GPIOREG(port, 0x404) +#define LM4_GPIO_IBE(port) LM4GPIOREG(port, 0x408) +#define LM4_GPIO_IM(port) LM4GPIOREG(port, 0x410) +#define LM4_GPIO_RIS(port) LM4GPIOREG(port, 0x414) +#define LM4_GPIO_MIS(port) LM4GPIOREG(port, 0x418) +#define LM4_GPIO_ICR(port) LM4GPIOREG(port, 0x41c) +#define LM4_GPIO_AFSEL(port) LM4GPIOREG(port, 0x420) +#define LM4_GPIO_DR2R(port) LM4GPIOREG(port, 0x500) +#define LM4_GPIO_DR4R(port) LM4GPIOREG(port, 0x504) +#define LM4_GPIO_DR8R(port) LM4GPIOREG(port, 0x508) +#define LM4_GPIO_ODR(port) LM4GPIOREG(port, 0x50c) +#define LM4_GPIO_PUR(port) LM4GPIOREG(port, 0x510) +#define LM4_GPIO_PDR(port) LM4GPIOREG(port, 0x514) +#define LM4_GPIO_SLR(port) LM4GPIOREG(port, 0x518) +#define LM4_GPIO_DEN(port) LM4GPIOREG(port, 0x51c) +#define LM4_GPIO_LOCK(port) LM4GPIOREG(port, 0x520) +#define LM4_GPIO_CR(port) LM4GPIOREG(port, 0x524) +#define LM4_GPIO_AMSEL(port) LM4GPIOREG(port, 0x528) +#define LM4_GPIO_PCTL(port) LM4GPIOREG(port, 0x52c) + + +/* I2C */ +#define LM4_I2C0_BASE 0x40020000 +#define LM4_I2C1_BASE 0x40021000 +#define LM4_I2C2_BASE 0x40022000 +#define LM4_I2C3_BASE 0x40023000 +#define LM4_I2C4_BASE 0x400c0000 +#define LM4_I2C5_BASE 0x400c1000 +#define LM4_I2C_BASESEP 0x00001000 +/* I2C base address by port. Compiles to a constant in gcc if port + and offset are constant. */ +static inline int lm4_i2c_addr(int port, int offset) +{ + return offset + (port < 4 ? + LM4_I2C0_BASE + LM4_I2C_BASESEP * port : + LM4_I2C4_BASE + LM4_I2C_BASESEP * (port - 4)); +} +#define LM4I2CREG(port, offset) LM4REG(lm4_i2c_addr(port, offset)) +#define LM4_I2C_MSA(port) LM4I2CREG(port, 0x000) +#define LM4_I2C_MCS(port) LM4I2CREG(port, 0x004) +#define LM4_I2C_MDR(port) LM4I2CREG(port, 0x008) +#define LM4_I2C_MTPR(port) LM4I2CREG(port, 0x00c) +#define LM4_I2C_MIMR(port) LM4I2CREG(port, 0x010) +#define LM4_I2C_MRIS(port) LM4I2CREG(port, 0x014) +#define LM4_I2C_MMIS(port) LM4I2CREG(port, 0x018) +#define LM4_I2C_MICR(port) LM4I2CREG(port, 0x01c) +#define LM4_I2C_MCR(port) LM4I2CREG(port, 0x020) +#define LM4_I2C_MCLKOCNT(port) LM4I2CREG(port, 0x024) +#define LM4_I2C_MBMON(port) LM4I2CREG(port, 0x02c) + + +/* Timers */ +#define LM4TIMERREG(tmr, offset) LM4REG(LM4_TIMER##tmr##_BASE + (offset)) + +#define LM4_TIMER0_BASE 0x40030000 +#define LM4_TIMER1_BASE 0x40031000 +#define LM4_TIMER2_BASE 0x40032000 +#define LM4_TIMER3_BASE 0x40033000 +#define LM4_TIMER4_BASE 0x40034000 +#define LM4_TIMER5_BASE 0x40035000 +#define LM4_TIMERW0_BASE 0x40036000 +#define LM4_TIMERW1_BASE 0x40037000 +#define LM4_TIMERW2_BASE 0x4004C000 +#define LM4_TIMERW3_BASE 0x4004D000 +#define LM4_TIMERW4_BASE 0x4004E000 +#define LM4_TIMERW5_BASE 0x4004F000 + +#define LM4_TIMER_CFG(tmr) LM4TIMERREG(tmr, 0x00) +#define LM4_TIMER_TAMR(tmr) LM4TIMERREG(tmr, 0x04) +#define LM4_TIMER_TBMR(tmr) LM4TIMERREG(tmr, 0x08) +#define LM4_TIMER_CTL(tmr) LM4TIMERREG(tmr, 0x0C) +#define LM4_TIMER_SYNC(tmr) LM4TIMERREG(tmr, 0x10) +#define LM4_TIMER_IMR(tmr) LM4TIMERREG(tmr, 0x18) +#define LM4_TIMER_RIS(tmr) LM4TIMERREG(tmr, 0x1C) +#define LM4_TIMER_MIS(tmr) LM4TIMERREG(tmr, 0x20) +#define LM4_TIMER_ICR(tmr) LM4TIMERREG(tmr, 0x24) +#define LM4_TIMER_TAILR(tmr) LM4TIMERREG(tmr, 0x28) +#define LM4_TIMER_TBILR(tmr) LM4TIMERREG(tmr, 0x2C) +#define LM4_TIMER_TAMATCHR(tmr) LM4TIMERREG(tmr, 0x30) +#define LM4_TIMER_TBMATCHR(tmr) LM4TIMERREG(tmr, 0x34) +#define LM4_TIMER_TAPR(tmr) LM4TIMERREG(tmr, 0x38) +#define LM4_TIMER_TBPR(tmr) LM4TIMERREG(tmr, 0x3C) +#define LM4_TIMER_TAPMR(tmr) LM4TIMERREG(tmr, 0x40) +#define LM4_TIMER_TBPMR(tmr) LM4TIMERREG(tmr, 0x44) +#define LM4_TIMER_TAR(tmr) LM4TIMERREG(tmr, 0x48) +#define LM4_TIMER_TBR(tmr) LM4TIMERREG(tmr, 0x4C) +#define LM4_TIMER_TAV(tmr) LM4TIMERREG(tmr, 0x50) +#define LM4_TIMER_TBV(tmr) LM4TIMERREG(tmr, 0x54) +#define LM4_TIMER_RTCPD(tmr) LM4TIMERREG(tmr, 0x58) +#define LM4_TIMER_TAPS(tmr) LM4TIMERREG(tmr, 0x5C) +#define LM4_TIMER_TBPS(tmr) LM4TIMERREG(tmr, 0x60) +#define LM4_TIMER_TAPV(tmr) LM4TIMERREG(tmr, 0x64) +#define LM4_TIMER_TBPV(tmr) LM4TIMERREG(tmr, 0x68) + +#define LM4_SYSTICK_CTRL LM4REG(0xE000E010) +#define LM4_SYSTICK_RELOAD LM4REG(0xE000E014) +#define LM4_SYSTICK_CURRENT LM4REG(0xE000E018) + +/* Watchdogs */ +#define LM4WDTREG(num, offset) LM4REG(LM4_WATCHDOG##num##_BASE + (offset)) + +#define LM4_WATCHDOG0_BASE 0x40000000 +#define LM4_WATCHDOG1_BASE 0x40001000 + +#define LM4_WATCHDOG_LOAD(n) LM4WDTREG(n, 0x000) +#define LM4_WATCHDOG_VALUE(n) LM4WDTREG(n, 0x004) +#define LM4_WATCHDOG_CTL(n) LM4WDTREG(n, 0x008) +#define LM4_WATCHDOG_ICR(n) LM4WDTREG(n, 0x00C) +#define LM4_WATCHDOG_RIS(n) LM4WDTREG(n, 0x010) +#define LM4_WATCHDOG_TEST(n) LM4WDTREG(n, 0x418) +#define LM4_WATCHDOG_LOCK(n) LM4WDTREG(n, 0xC00) + +#endif /* __LM4_REGISTERS */ diff --git a/chip/lm4/system.c b/chip/lm4/system.c new file mode 100644 index 0000000000..a6d74e32cd --- /dev/null +++ b/chip/lm4/system.c @@ -0,0 +1,371 @@ +/* Copyright (c) 2011 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. + */ + +/* System module for Chrome EC */ + +#include "console.h" +#include "registers.h" +#include "system.h" +#include "uart.h" +#include "util.h" +#include "version.h" + +/* Forward declarations for console commands */ +static int command_sysinfo(int argc, char **argv); +static int command_set_scratchpad(int argc, char **argv); +static int command_hibernate(int argc, char **argv); + +static const struct console_command console_commands[] = { + {"setscratch", command_set_scratchpad}, + {"sysinfo", command_sysinfo}, + {"hibernate", command_hibernate} +}; +static const struct console_group command_group = { + "System", console_commands, ARRAY_SIZE(console_commands) +}; + +struct version_struct { + uint32_t cookie1; + char version[32]; + uint32_t cookie2; +} __attribute__ ((packed)); + +static const struct version_struct version_data = { + 0xce112233, + CROS_EC_VERSION_STRING, + 0xce445566 +}; + +static uint32_t raw_reset_cause = 0; +static enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN; + + +static int wait_for_hibctl_wc(void) +{ + int i; + /* Wait for write-capable */ + for (i = 0; i < 1000000; i++) { + if (LM4_HIBERNATE_HIBCTL & 0x80000000) + return EC_SUCCESS; + } + return EC_ERROR_UNKNOWN; +} + + +static void check_reset_cause(void) +{ + enum system_image_copy_t copy = system_get_image_copy(); + uint32_t hib_status = LM4_HIBERNATE_HIBRIS; + + /* Read the raw reset cause */ + raw_reset_cause = LM4_SYSTEM_RESC; + + if (hib_status & 0x0d) { + /* the hibernation module wakes up the system */ + if (hib_status & 0x8) + reset_cause = SYSTEM_RESET_WAKE_PIN; + else if (hib_status & 0x1) + reset_cause = SYSTEM_RESET_RTC_ALARM; + else if (hib_status & 0x4) + reset_cause = SYSTEM_RESET_LOW_BATTERY; + /* clear the pending interrupt */ + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBIC = hib_status; + } else if (copy == SYSTEM_IMAGE_RW_A || copy == SYSTEM_IMAGE_RW_B) { + /* If we're in image A or B, the only way we can get there is + * via a warm reset. */ + reset_cause = SYSTEM_RESET_SOFT_WARM; + } else if (raw_reset_cause & 0x28) { + /* Watchdog timer 0 or 1 */ + reset_cause = SYSTEM_RESET_WATCHDOG; + } else if (raw_reset_cause & 0x10) { + reset_cause = SYSTEM_RESET_SOFT_COLD; + } else if (raw_reset_cause & 0x04) { + reset_cause = SYSTEM_RESET_BROWNOUT; + } else if (raw_reset_cause & 0x02) { + reset_cause = SYSTEM_RESET_POWER_ON; + } else if (raw_reset_cause & 0x01) { + reset_cause = SYSTEM_RESET_RESET_PIN; + } else if (raw_reset_cause) { + reset_cause = SYSTEM_RESET_OTHER; + } else { + reset_cause = SYSTEM_RESET_UNKNOWN; + } +} + + +void system_hibernate(uint32_t seconds, uint32_t microseconds) +{ + /* clear pending interrupt */ + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS; + /* set RTC alarm match */ + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBRTCM0 = seconds; + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBRTCSS = (microseconds * 512 / 15625) << 16; + + /* start counting toward the alarm */ + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBRTCLD = 0; + /* go to hibernation and wake on RTC match or WAKE pin */ + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBCTL = 0x5B; + /* we are going to hibernate ... */ + while (1) ; +} + + +int system_pre_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable clocks to the hibernation module */ + LM4_SYSTEM_RCGCHIB = 1; + /* Wait 3 clock cycles before using the module */ + scratch = LM4_SYSTEM_RCGCHIB; + + /* Enable the hibernation oscillator, if it's not already enabled. We + * use this to hold our scratchpad value across reboots. */ + if (!(LM4_HIBERNATE_HIBCTL & 0x40)) { + int rv, i; + rv = wait_for_hibctl_wc(); + if (rv != EC_SUCCESS) + return rv; + + /* Enable clock to hibernate module */ + LM4_HIBERNATE_HIBCTL |= 0x40; + /* Wait for write-complete */ + for (i = 0; i < 1000000; i++) { + if (LM4_HIBERNATE_HIBRIS & 0x10) + break; + } + } + /* initialize properly registers after reset (cf errata) */ + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBRTCT = 0x7fff; + wait_for_hibctl_wc(); + LM4_HIBERNATE_HIBIM = 0; + + check_reset_cause(); + + return EC_SUCCESS; +} + + +int system_init(void) +{ + /* Clear the hardware reset cause, now that we've committed to running + * this image. */ + LM4_SYSTEM_RESC = 0; + + /* Register our internal commands */ + return console_register_commands(&command_group); +} + + +enum system_reset_cause_t system_get_reset_cause(void) +{ + return reset_cause; +} + + +const char *system_get_reset_cause_string(void) +{ + static const char * const cause_descs[] = { + "unknown", "other", "brownout", "power-on", "reset pin", + "soft cold", "soft warm", "watchdog", "rtc alarm", "wake pin", + "low battery"}; + + return reset_cause < ARRAY_SIZE(cause_descs) ? + cause_descs[reset_cause] : "?"; +} + + +enum system_image_copy_t system_get_image_copy(void) +{ + int copy = (uint32_t)system_get_image_copy / CONFIG_FW_IMAGE_SIZE; + switch (copy) { + case 0: + return SYSTEM_IMAGE_RO; + case 1: + return SYSTEM_IMAGE_RW_A; + case 2: + return SYSTEM_IMAGE_RW_B; + default: + return SYSTEM_IMAGE_UNKNOWN; + } +} + + +const char *system_get_image_copy_string(void) +{ + static const char * const copy_descs[] = {"unknown", "RO", "A", "B"}; + int copy = system_get_image_copy(); + return copy < ARRAY_SIZE(copy_descs) ? copy_descs[copy] : "?"; +} + + +int system_run_image_copy(enum system_image_copy_t copy) +{ + uint32_t init_addr; + void (*resetvec)(void); + + /* Fail if we're not in RO firmware */ + if (system_get_image_copy() != SYSTEM_IMAGE_RO) + return EC_ERROR_UNKNOWN; + + /* TODO: fail if we've already started up interrupts, etc. */ + + /* Load the appropriate reset vector */ + if (copy == SYSTEM_IMAGE_RW_A) + init_addr = *(uint32_t *)(CONFIG_FW_A_OFF + 4); + else if (copy == SYSTEM_IMAGE_RW_B) + init_addr = *(uint32_t *)(CONFIG_FW_B_OFF + 4); + else + return EC_ERROR_UNKNOWN; + + /* TODO: sanity check reset vector; must be inside the + * appropriate image. */ + + /* Jump to the reset vector */ + resetvec = (void(*)(void))init_addr; + resetvec(); + + /* Should never get here */ + return EC_ERROR_UNIMPLEMENTED; +} + + +int system_reset(int is_cold) +{ + /* TODO - warm vs. cold */ + LM4_NVIC_APINT = 0x05fa0004; + + /* Spin and wait for reboot; should never return */ + /* TODO: should disable task swaps while waiting */ + while (1) {} + + return EC_ERROR_UNKNOWN; +} + + +int system_set_scratchpad(uint32_t value) +{ + int rv; + + /* Wait for ok-to-write */ + rv = wait_for_hibctl_wc(); + if (rv != EC_SUCCESS) + return rv; + + /* Write scratchpad */ + /* TODO: might be more elegant to have a write_hibernate_reg() method + * which takes an address and data and does the delays */ + LM4_HIBERNATE_HIBDATA = value; + + /* Wait for write-complete */ + return wait_for_hibctl_wc(); +} + + +uint32_t system_get_scratchpad(void) +{ + return LM4_HIBERNATE_HIBDATA; +} + + +const char *system_get_version(enum system_image_copy_t copy) +{ + int imoffset; + const uint32_t *p, *pend; + const struct version_struct *v; + + /* Handle version of current image */ + if (copy == system_get_image_copy() || copy == SYSTEM_IMAGE_UNKNOWN) + return version_data.version; + + switch (copy) { + case SYSTEM_IMAGE_RO: + imoffset = CONFIG_FW_RO_OFF; + break; + case SYSTEM_IMAGE_RW_A: + imoffset = CONFIG_FW_A_OFF; + break; + case SYSTEM_IMAGE_RW_B: + imoffset = CONFIG_FW_B_OFF; + break; + default: + return ""; + } + + /* Search for version cookies in target image */ + /* TODO: could be smarter about where to search if we stuffed + * the version data into a predefined area of the image - for + * example, immediately following the reset vectors. */ + pend = (uint32_t *)(imoffset + CONFIG_FW_IMAGE_SIZE + - sizeof(version_data)); + for (p = (uint32_t *)imoffset; p <= pend; p++) { + v = (const struct version_struct *)p; + if (v->cookie1 == version_data.cookie1 && + v->cookie2 == version_data.cookie2) + return v->version; + } + + return ""; +} + + +static int command_sysinfo(int argc, char **argv) +{ + uart_printf("Reset cause: %d (%s)\n", + system_get_reset_cause(), + system_get_reset_cause_string()); + uart_printf("Raw reset cause: 0x%x\n", raw_reset_cause); + uart_printf("Scratchpad: 0x%08x\n", system_get_scratchpad()); + uart_printf("Firmware copy: %s\n", system_get_image_copy_string()); + return EC_SUCCESS; +} + + +static int command_set_scratchpad(int argc, char **argv) +{ + int s; + char *e; + + if (argc < 2) { + uart_puts("Usage: scratchpad <value>\n"); + return EC_ERROR_UNKNOWN; + } + + s = strtoi(argv[1], &e, 0); + if (*e) { + uart_puts("Invalid scratchpad value\n"); + return EC_ERROR_UNKNOWN; + } + uart_printf("Setting scratchpad to 0x%08x\n", s); + return system_set_scratchpad(s); +} + +static int command_hibernate(int argc, char **argv) +{ + int seconds; + int microseconds = 0; + + if (argc < 2) { + uart_puts("Usage: hibernate <seconds> [<microseconds>]\n"); + return EC_ERROR_UNKNOWN; + } + seconds = strtoi(argv[1], NULL, 0); + if (argc >= 3) + microseconds = strtoi(argv[2], NULL, 0); + + uart_printf("Hibernating for %d.%06d s ...\n", seconds, microseconds); + uart_flush_output(); + + system_hibernate(seconds, microseconds); + + return EC_SUCCESS; +} diff --git a/chip/lm4/temp_sensor.c b/chip/lm4/temp_sensor.c new file mode 100644 index 0000000000..bdb8b42ea6 --- /dev/null +++ b/chip/lm4/temp_sensor.c @@ -0,0 +1,197 @@ +/* Copyright (c) 2011 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. + */ + +/* Temperature sensor module for Chrome EC */ + +#include "adc.h" +#include "board.h" +#include "console.h" +#include "i2c.h" +#include "temp_sensor.h" +#include "uart.h" +#include "util.h" + + +/* Address of first temp sensor in system */ +#define TEMP0_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN) +/* TODO: support all 6 temp sensors */ + +/* Address of battery charger */ +#define CHARGER_ADDR 0x12 + +/* Address of battery */ +#define BATTERY_ADDR 0x16 + + +int temp_sensor_read(enum temp_sensor_id id) +{ + int traw, t; + int rv; + + switch(id) { + case TEMP_SENSOR_CASE: + /* TODO: fix temperature correction factor. For now, + * just return the die temperature. */ + return temp_sensor_read(TEMP_SENSOR_CASE_DIE); + + case TEMP_SENSOR_CASE_DIE: + rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x01, &traw); + if (rv) + return -1; + t = (int)(int16_t)traw / 128; + return t + 273; + + case TEMP_SENSOR_EC_INTERNAL: + return adc_read_ec_temperature(); + } + + /* If we're still here, we don't handle that sensor */ + return -1; +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_temps(int argc, char **argv) +{ + int vraw, v; + int traw, t; + int rv; + int d; + + uart_puts("Reading temperature sensors...\n"); + + uart_printf(" Temp from die: %d K\n\n", + temp_sensor_read(TEMP_SENSOR_CASE_DIE)); + + uart_puts("Debug data:\n"); + rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0xfe, &d); + if (rv) + return rv; + uart_printf(" Manufacturer ID: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0xff, &d); + uart_printf(" Device ID: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x02, &d); + uart_printf(" Config: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x00, &vraw); + v = ((int)(int16_t)vraw * 15625) / 100; + uart_printf(" Voltage: 0x%04x = %d nV\n", vraw, v); + + rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x01, &traw); + t = ((int)(int16_t)traw * 100) / 128; + uart_printf(" Temperature: 0x%04x = %d.%02d C\n", + traw, t / 100, t > 0 ? t % 100 : 100 - (t % 100)); + + /* TODO: correction factor from voltage offset */ + + return EC_SUCCESS; +} + + +/* TODO: the battery charger would normally be on a separate I2C bus. + * For evaluation, it's on the same bus as the thermal sensor, so I + * put the debug command here for now. */ +static int command_charger(int argc, char **argv) +{ + int rv; + int d; + + uart_puts("Reading battery charger...\n"); + + rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0xfe, &d); + if (rv) + return rv; + uart_printf(" Manufacturer ID: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0xff, &d); + uart_printf(" Device ID: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x12, &d); + uart_printf(" Option: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x14, &d); + uart_printf(" Charge current: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x15, &d); + uart_printf(" Charge voltage: 0x%04x\n", d); + + rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x3f, &d); + uart_printf(" Input current: 0x%04x\n", d); + + return EC_SUCCESS; +} + + +/* TODO: the battery would normally be on a separate I2C bus. For + * evaluation, it's on the same bus as the thermal sensor so I put the + * debug command here for now. */ +static int command_battery(int argc, char **argv) +{ + int rv; + int d; + + uart_puts("Reading battery...\n"); + + rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x08, &d); + if (rv) + return rv; + uart_printf(" Temperature: 0x%04x = %d C\n", + d, (d-2731)/10); + + rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x09, &d); + uart_printf(" Voltage: 0x%04x = %d mV\n", d, d); + + rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x0f, &d); + uart_printf(" Remaining capacity: 0x%04x = %d mAh\n", d, d); + rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x10, &d); + uart_printf(" Full charge capacity: 0x%04x = %d mAh\n", d, d); + + rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x14, &d); + uart_printf(" Desired charge current: 0x%04x = %d mA\n", d, d); + rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x15, &d); + uart_printf(" Desired charge voltage: 0x%04x = %d mV\n", d, d); + + + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"temps", command_temps}, + {"charger", command_charger}, + {"battery", command_battery}, +}; +static const struct console_group command_group = { + "Temp sensor", console_commands, ARRAY_SIZE(console_commands) +}; + + +/*****************************************************************************/ +/* Initialization */ + +int temp_sensor_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + +#ifdef CONFIG_SENSOR + /* TODO: not necessary since these are the power-on defaults, + * except for the DRDY pin. It's unclear DRDY will be used + * anyway. */ + + /* Configure the sensor: + * 0x7000 = bits 14:12 = continuous conversion + * 0x0400 = bits 11:9 = ADC conversion rate (1/sec) + * 0x0100 = bit 8 = DRDY pin enabled */ + /* TODO: support shutdown mode for power-saving? */ + i2c_write16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x02, 0x7500); +#endif + + console_register_commands(&command_group); + return EC_SUCCESS; +} diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c new file mode 100644 index 0000000000..49115e418c --- /dev/null +++ b/chip/lm4/uart.c @@ -0,0 +1,563 @@ +/* Copyright (c) 2011 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. + */ + +/* UART module for Chrome EC */ + +#include <stdarg.h> + +#include "board.h" +#include "console.h" +#include "registers.h" +#include "task.h" +#include "uart.h" +#include "util.h" + +/* Baud rate for UARTs */ +#define BAUD_RATE 115200 + +/* Buffer sizes; should be power of 2 */ +#define TX_BUF_SIZE 512 +#define RX_BUF_SIZE 128 /* suggest larger than 80 to copy&paste script. */ + +/* Macros to advance in the circular transmit and receive buffers */ +#define TX_BUF_NEXT(i) (((i) + 1) & (TX_BUF_SIZE - 1)) +#define RX_BUF_NEXT(i) (((i) + 1) & (RX_BUF_SIZE - 1)) +#define RX_BUF_PREV(i) (((i) - 1) & (RX_BUF_SIZE - 1)) + +/* Transmit and receive buffers */ +static volatile char tx_buf[TX_BUF_SIZE]; +static volatile int tx_buf_head = 0; +static volatile int tx_buf_tail = 0; +static volatile char rx_buf[RX_BUF_SIZE]; +static volatile int rx_buf_head = 0; +static volatile int rx_buf_tail = 0; +static int last_rx_was_cr = 0; + +static int console_mode = 1; + +/* TODO: should have an API to set raw mode for the UART. In raw + * mode, we don't do CRLF translation or echo input. */ + + +/* Put a single character into the transmit buffer. Does not enable + * the transmit interrupt; assumes that happens elsewhere. Returns + * zero if the character was transmitted, 1 if it was dropped. */ +static int __tx_char(int c) +{ + int tx_buf_next; + + /* Do newline to CRLF translation */ + if (console_mode && c == '\n' && __tx_char('\r')) + return 1; + + tx_buf_next = TX_BUF_NEXT(tx_buf_head); + if (tx_buf_next == tx_buf_tail) + return 1; + + tx_buf[tx_buf_head] = c; + tx_buf_head = tx_buf_next; + return 0; +} + + +/* Interrupt handler for UART0 */ +static void uart_0_interrupt(void) +{ + /* Clear transmit and receive interrupt status */ + LM4_UART_ICR(0) = 0x70; + + /* Copy input from buffer until RX fifo empty */ + while (!(LM4_UART_FR(0) & 0x10)) { + int c = LM4_UART_DR(0); + + /* Handle console mode echoing and translation */ + if (console_mode) { + /* Translate CR and CRLF to LF (newline) */ + if (c == '\r') { + last_rx_was_cr = 1; + c = '\n'; + } else if (c == '\n' && last_rx_was_cr) { + last_rx_was_cr = 0; + continue; + } else { + last_rx_was_cr = 0; + } + + /* Echo characters directly to the transmit + * FIFO so we don't interfere with the + * transmit buffer. This means that if a lot + * of output is happening, input characters + * won't always be properly echoed. */ + if (console_mode && c == '\n') + LM4_UART_DR(0) = '\r'; + LM4_UART_DR(0) = c; + + /* Handle backspace if we can */ + if (c == '\b') { + if (rx_buf_head != rx_buf_tail) { + /* Delete the previous + * character (and space over + * it on the output) */ + LM4_UART_DR(0) = ' '; + LM4_UART_DR(0) = '\b'; + rx_buf_head = RX_BUF_PREV(rx_buf_head); + } + continue; + } + } + + rx_buf[rx_buf_head] = c; + rx_buf_head = RX_BUF_NEXT(rx_buf_head); + /* On overflow, discard oldest output */ + if (rx_buf_head == rx_buf_tail) + rx_buf_tail = RX_BUF_NEXT(rx_buf_tail); + + /* Call console callback on newline, if in console mode */ + if (console_mode && c == '\n') + console_has_input(); + } + + /* Copy output from buffer until TX fifo full or output buffer empty */ + while (!(LM4_UART_FR(0) & 0x20) && (tx_buf_head != tx_buf_tail)) { + LM4_UART_DR(0) = tx_buf[tx_buf_tail]; + tx_buf_tail = TX_BUF_NEXT(tx_buf_tail); + } + + /* If output buffer is empty, disable transmit interrupt */ + if (tx_buf_tail == tx_buf_head) + LM4_UART_IM(0) &= ~0x20; +} +DECLARE_IRQ(5, uart_0_interrupt, 1); + + +/* Interrupt handler for UART1 */ +static void uart_1_interrupt(void) +{ + /* Clear transmit and receive interrupt status */ + LM4_UART_ICR(1) = 0x70; + + /* TODO: handle input */ + + /* If we have space in our FIFO and a character is pending in LPC, + * grab it. + * + * TODO: move UART1 interrupt to the LPC module? */ + if (!(LM4_UART_FR(1) & 0x20) && (LM4_LPC_ST(7) & 0x02)) { + /* TODO: this clears the receive-ready interrupt too, + * which will be ok once we're handing input as well. + * But we're not yet. */ + LM4_LPC_LPCDMACX = LM4_LPC_LPCDMACX; + /* Copy the next byte */ + LM4_UART_DR(1) = LPC_POOL_COMX[0]; + /* Disable transmit interrupt */ + LM4_UART_IM(1) &= ~0x20; + } +} +/* Must be same prio as LPC interrupt handler so they don't preempt */ +DECLARE_IRQ(6, uart_1_interrupt, 2); + + +/* Configure GPIOs for the UART module. */ +/* TODO: board-dependent; on Link UART1 is PC4/PC5, not PB0/PB1. */ +static void configure_gpio(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable clocks to GPIO blocks A and B, then delay a few clocks. */ + LM4_SYSTEM_RCGCGPIO |= 0x0003; + + /* UART0 setup; RX and TX are GPIO PA0 and PA1 */ + /* Enable alternate function */ + LM4_GPIO_AFSEL(A) |= 0x03; + /* Alternate function 1 */ + LM4_GPIO_PCTL(A) = (LM4_GPIO_PCTL(A) & 0xffffff00) | 0x11; + /* Enable digital function */ + LM4_GPIO_DEN(A) |= 0x03; + + /* UART1 setup; RX and TX are GPIO PB0 and PB1 */ + /* Enable alternate function */ + LM4_GPIO_AFSEL(B) |= 0x03; + /* Alternate function 1 */ + LM4_GPIO_PCTL(B) = (LM4_GPIO_PCTL(B) & 0xffffff00) | 0x11; + /* Enable digital function */ + LM4_GPIO_DEN(B) = 0x03; +} + + +int uart_init(void) +{ + volatile uint32_t scratch __attribute__((unused)); + + /* Enable UART0 and UART1 and delay a few clocks */ + LM4_SYSTEM_RCGCUART |= 0x03; + scratch = LM4_SYSTEM_RCGCUART; + + /* Configure GPIOs */ + configure_gpio(); + + /* UART0 setup */ + /* Disable the port via UARTCTL */ + LM4_UART_CTL(0) = 0x0300; + /* Set the baud rate divisor */ + LM4_UART_IBRD(0) = (CPU_CLOCK / 16) / BAUD_RATE; + LM4_UART_FBRD(0) = + (((CPU_CLOCK / 16) % BAUD_RATE) * 64 + BAUD_RATE / 2) / + BAUD_RATE; + /* Set UARTLCRH to 8-N-1, FIFO enabled. Must be done after setting + * the divisor for the new divisor to take effect. */ + LM4_UART_LCRH(0) = 0x70; + /* Interrupt when RX fifo at minimum (>= 1/8 full), and TX fifo when + * <= 1/4 full */ + LM4_UART_IFLS(0) = 0x01; + /* Unmask receive-FIFO, receive-timeout. We need + * receive-timeout because the minimum RX FIFO depth is 1/8 = + * 2 bytes; without the receive-timeout we'd never be notified + * about single received characters. */ + LM4_UART_IM(0) = 0x50; + /* Enable the port */ + LM4_UART_CTL(0) |= 0x0001; + + /* UART1 setup */ + /* Disable the port via UARTCTL */ + LM4_UART_CTL(1) = 0x0300; + /* Set the baud rate divisor */ + LM4_UART_IBRD(1) = (CPU_CLOCK / 16) / BAUD_RATE; + LM4_UART_FBRD(1) = + (((CPU_CLOCK / 16) % BAUD_RATE) * 64 + BAUD_RATE / 2) / + BAUD_RATE; + /* Set UARTLCRH to 8-N-1, FIFO enabled. Must be done after setting + * the divisor for the new divisor to take effect. */ + LM4_UART_LCRH(1) = 0x70; + /* Unmask receive-FIFO, receive-timeout. We need + * receive-timeout because the minimum RX FIFO depth is 1/8 = + * 2 bytes; without the receive-timeout we'd never be notified + * about single received characters. */ + LM4_UART_IM(1) = 0x50; +#ifdef USE_UART_DMA + /* No interrupts on UART1 */ + LM4_UART_IM(1) = 0x00; + /* Enable RX and TX DMA */ + LM4_UART_DMACTL(1) = 0x03; +#endif + /* Enable the port */ + LM4_UART_CTL(1) |= 0x0001; + + /* Print hello on UART1 for debugging */ + { + const char *c = "Hello on UART1\r\n"; + while (*c) + LM4_UART_DR(1) = *c++; + } + + return EC_SUCCESS; +} + + +void uart_set_console_mode(int enable) +{ + console_mode = enable; +} + + +int uart_puts(const char *outstr) +{ + int was_empty = (tx_buf_head == tx_buf_tail); + + /* Put all characters in the output buffer */ + while (*outstr) { + if (__tx_char(*outstr++) != 0) + break; + } + + if (was_empty) { + /* Re-enable the transmit interrupt, then forcibly trigger the + * interrupt. This works around a hardware problem with the + * UART where the FIFO only triggers the interrupt when its + * threshold is _crossed_, not just met. */ + LM4_UART_IM(0) |= 0x20; + LM4_NVIC_SWTRIG = 5; + } + + /* Successful if we consumed all output */ + return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS; +} + + +int uart_printf(const char *format, ...) +{ + static const char int_chars[] = "0123456789abcdef"; + static const char error_str[] = "ERROR"; + char intbuf[21]; /* Longest uint64 */ + int dropped_chars = 0; + int is_left; + int pad_zero; + int pad_width; + int was_empty = (tx_buf_head == tx_buf_tail); + va_list args; + char *vstr; + int vlen; + + va_start(args, format); + + while (*format && !dropped_chars) { + int c = *format++; + + /* Copy normal characters */ + if (c != '%') { + dropped_chars |= __tx_char(c); + continue; + } + + /* Get first format character */ + c = *format++; + + /* Send "%" for "%%" input */ + if (c == '%' || c == '\0') { + dropped_chars |= __tx_char('%'); + continue; + } + + /* Handle %c */ + if (c == 'c') { + c = va_arg(args, int); + dropped_chars |= __tx_char(c); + continue; + } + + /* Handle left-justification ("%-5s") */ + is_left = (c == '-'); + if (is_left) + c = *format++; + + /* Handle padding with 0's */ + pad_zero = (c == '0'); + if (pad_zero) + c = *format++; + + /* Count padding length */ + pad_width = 0; + while (c >= '0' && c <= '9') { + pad_width = (10 * pad_width) + c - '0'; + c = *format++; + } + if (pad_width > 80) { + /* Sanity check for width failed */ + format = error_str; + continue; + } + + if (c == 's') { + vstr = va_arg(args, char *); + if (vstr == NULL) + vstr = "(NULL)"; + } else { + uint32_t v; + int is_negative = 0; + int base = 10; + + /* TODO: handle "%l" prefix for uint64_t */ + + v = va_arg(args, uint32_t); + + switch (c) { + case 'd': + if ((int)v < 0) { + is_negative = 1; + v = -v; + } + break; + case 'u': + break; + case 'x': + case 'p': + base = 16; + break; + default: + format = error_str; + } + if (format == error_str) + continue; /* Bad format specifier */ + + /* Convert integer to string, starting at end of + * buffer and working backwards. */ + vstr = intbuf + sizeof(intbuf) - 1; + *(vstr) = '\0'; + + if (!v) + *(--vstr) = '0'; + + while (v) { + *(--vstr) = int_chars[v % base]; + v /= base; + } + if (is_negative) + *(--vstr) = '-'; + } + + /* Copy string (or stringified integer) */ + vlen = strlen(vstr); + while (vlen < pad_width && !is_left) { + dropped_chars |= __tx_char(pad_zero ? '0' : ' '); + vlen++; + } + while (*vstr) + dropped_chars |= __tx_char(*vstr++); + while (vlen < pad_width && is_left) { + dropped_chars |= __tx_char(' '); + vlen++; + } + } + va_end(args); + + if (was_empty) { + /* Re-enable the transmit interrupt, then forcibly trigger the + * interrupt. This works around a hardware problem with the + * UART where the FIFO only triggers the interrupt when its + * threshold is _crossed_, not just met. */ + LM4_UART_IM(0) |= 0x20; + LM4_NVIC_SWTRIG = 5; + } + + /* Successful if we consumed all output */ + return dropped_chars ? EC_ERROR_OVERFLOW : EC_SUCCESS; +} + + +void uart_flush_output(void) +{ + /* Wait for buffer to empty */ + while (tx_buf_head != tx_buf_tail) { + /* It's possible we're in some other interrupt, and the + * previous context was doing a printf() or puts() but hadn't + * enabled the UART interrupt. Check if the interrupt is + * disabled, and if so, re-enable and trigger it. Note that + * this check is inside the while loop, so we'll be safe even + * if the context switches away from us to another partial + * printf() and back. */ + if (!(LM4_UART_IM(0) & 0x20)) { + LM4_UART_IM(0) |= 0x20; + LM4_NVIC_SWTRIG = 5; + } + } + + /* Wait for transmit FIFO empty */ + while (!(LM4_UART_FR(0) & 0x80)) {} +} + +void uart_emergency_flush(void) +{ + do { + /* Copy output from buffer until TX fifo full + * or output buffer empty + */ + while (!(LM4_UART_FR(0) & 0x20) && + (tx_buf_head != tx_buf_tail)) { + LM4_UART_DR(0) = tx_buf[tx_buf_tail]; + tx_buf_tail = TX_BUF_NEXT(tx_buf_tail); + } + /* Wait for transmit FIFO empty */ + while (!(LM4_UART_FR(0) & 0x80)) {} + } while (tx_buf_head != tx_buf_tail); +} + +void uart_flush_input(void) +{ + /* Disable interrupts */ + LM4_NVIC_DIS(0) = (1 << 5); + + /* Call interrupt handler to empty the hardware FIFO */ + uart_0_interrupt(); + + /* Clear the input buffer */ + rx_buf_tail = rx_buf_head; + + /* Re-enable interrupts */ + LM4_NVIC_EN(0) = (1 << 5); +} + + +int uart_peek(int c) +{ + int index = -1; + int i = 0; + + /* Disable interrupts while we pull characters out, because the + * interrupt handler can also modify the tail pointer. */ + LM4_NVIC_DIS(0) = (1 << 5); + + /* Call interrupt handler to empty the hardware FIFO. The minimum + * FIFO trigger depth is 1/8 (2 chars), so this is the only way to + * ensure we've pulled the very last character out of the FIFO. */ + uart_0_interrupt(); + + for (i = rx_buf_tail; i != rx_buf_head; i = RX_BUF_NEXT(i)) { + if (rx_buf[i] == c) { + index = (RX_BUF_SIZE + i - rx_buf_tail) & + (RX_BUF_SIZE - 1); + break; + } + } + + /* Re-enable interrupts */ + LM4_NVIC_EN(0) = (1 << 5); + + return index; +} + + +int uart_getc(void) +{ + int c; + + /* Disable interrupts */ + LM4_NVIC_DIS(0) = (1 << 5); + + /* Call interrupt handler to empty the hardware FIFO */ + uart_0_interrupt(); + + if (rx_buf_tail == rx_buf_head) { + c = -1; /* No pending input */ + } else { + c = rx_buf[rx_buf_tail]; + rx_buf_tail = RX_BUF_NEXT(rx_buf_tail); + } + + /* Re-enable interrupts */ + LM4_NVIC_EN(0) = (1 << 5); + + return c; +} + + +int uart_gets(char *dest, int size) +{ + int got = 0; + int c; + + /* Disable interrupts while we pull characters out, because the + * interrupt handler can also modify the tail pointer. */ + LM4_NVIC_DIS(0) = (1 << 5); + + /* Call interrupt handler to empty the hardware FIFO */ + uart_0_interrupt(); + + /* Read characters */ + while (got < size - 1 && rx_buf_tail != rx_buf_head) { + c = rx_buf[rx_buf_tail]; + dest[got++] = c; + rx_buf_tail = RX_BUF_NEXT(rx_buf_tail); + if (c == '\n') + break; /* Stop on newline */ + } + + /* Re-enable interrupts */ + LM4_NVIC_EN(0) = (1 << 5); + + /* Null-terminate */ + dest[got] = '\0'; + + /* Return the length we got */ + return got; +} diff --git a/chip/lm4/x86_power.c b/chip/lm4/x86_power.c new file mode 100644 index 0000000000..cda5dcd0eb --- /dev/null +++ b/chip/lm4/x86_power.c @@ -0,0 +1,150 @@ +/* Copyright (c) 2011 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. + */ + +/* x86 power control module for Chrome EC */ + +#include "registers.h" +#include "x86_power.h" + +/* Signals to/from EC. These are ALL the signals on the schematic + * that seem related. From the 11/14 schematic. */ +#if 0 +enum ec_signal { +}; + +/* Signals from Chief River Platform Power Sequence document. Directions are + * from the EC's point of view. */ +enum x86_signal { + // Schematic GPIO dir Intel doc name + CPU1.5V_S3_GATE, PH5, out /* Connected to SUSP# via resistor. Can + * disable VDDQ when driven low. */ + PBTN_OUT#, PG3, out PWRBTN# + PCH_DPWROK, PG0, out, DPWROK + PCH_PWROK, PC5, out, PWROK /* AND'd with VGATE (CPU_CORE + * good), then connected to + * PWROK. Also * connected to + * APWROK via resistor */ + PCH_RSMRST#, PF1, out, RSMRST# /* Also connected to + * PCH_DPWROK via resistor */ + PM_SLP_SUS#, PD3, inp, SLP_SUS + SLP_A#, PG5, i/o, SLP_A# /* Intel claims inp; why does + * schematic claim I/O? */ + SLP_ME_CSW_DEV#, PG4, i/o, SLP_ME_CSW_DEV# /* Intel also claims + * inp? */ + PM_SLP_S3#, PJ0, inp, SLP_S3# + PM_SLP_S4#, PJ1, inp, SLP_S4# + PM_SLP_S5#, PJ2, inp, SLP_S5# + SUSACK#, PD2, out, SUS_ACK# /* Also connected to SUSWARN# + * via (no-load) resistor */ + SUSWARN#, PH1, inp, SUSWARN# + + /* On EC schematic, but not mentioned directly in doc */ + ACOFF, PG2, inp /* Not connected? */ + BKOFF#, PH4, out /* Turns off display when pulled low. + * Can tri-state when not using. */ + EC_ACIN, PC7, inp /* Connected to ACIN, which comes from + * ACOK on charger */ + EC_KBRST#, PQ7, out, RCIN# + EC_LID_OUT#, PF0, out, GPIO15 + EC_ON, PC6, out + ENBKL, PF4, inp, L_BKLTEN /* From Panther Point LVDS */ + GATEA20, PQ6, out, A20GATE + HDA_SDO, PG1, out, HDA_SDO + H_PROCHOT#_EC, PF2, out, PROCHOT# /* Ivy Bridge. Has pullup. */ + SA_PGOOD, PF3, inp /* Set by power control when VccSA is + * good */ + SMI#, PJ3, out, GPIO8 + SUSP#, PG6, out /* Disables VCCP, VDDQ, +5VS, +3VS, + * +1.5VS, +0.75VS, +1.8VS */ + SYSON, PB6, out /* Enables +1.5VP, VCCP */ + VR_ON, PB7, out /* Enables CPU_CORE, VGFX_CORE */ +}; + + +/* Signals from the Ivy Bridge power sequencing document that aren't connected + * to the EC (at least, directly) */ +enum x86_signal_no_control { + // Platform to PCH + ACPRESENT, /* Comes from ACIN, which comes from ACOK on charger. + * There's an EC_ACIN line mentioned, but it doesn't seem + * to go to the EC(!) */ + IMVP7_VR_EN, /* (not on schematic) */ + RTCRST#, /* Can short to ground via jumper */ + SA_VR_PWROK, /* (not on schematic) */ + SYS_PWROK, + VR_VDDPWRGD, + + // PCH to platform + CL_RST# + SUSCLK, + SUS_STAT#, + SLP_LAN# + + // Platform to platform + ALL_SYS_PWRGD, + + // Power rails + VccASW, + VccAXG, + VccCore (CPU), // aka Vboot + VccCore (PCH), + VccDSW, + VccRTC, + VCCP, // aka VccIO + VccSA, + VccSPI, + VccSUS, + Vcc_WLAN, + VDDQ, + // (+ all platform rails?) +}; + +/* Signals we explicitly don't care about */ +enum x86_signal_dont_care { + SUSPWRDNACK, // Only applicable if deep sleep well not supported + +}; + + +#endif + + + + +/* Signal definitions are messy, split across multiple GPIOs. + * Fortunately, we only need to set them one at a time. */ +struct signal_gpio { + int is_output; + +}; + +struct signal_gpio gpios[] = { + + + +}; + + + + +int x86_power_init(void) +{ +#if 0 + /* Enable clock to GPIO block A */ + LM4_SYSTEM_RCGCGPIO |= 0x0001; + + /* Turn off the LED before we make it an output */ + gpio_set(EC_GPIO_DEBUG_LED, 0); + + /* Clear GPIOAFSEL bits for block A pin 7 */ + LM4_GPIO_AFSEL(A) &= ~(0x80); + + /* Set GPIO to digital enable, output */ + LM4_GPIO_DEN(A) |= 0x80; + LM4_GPIO_DIR(A) |= 0x80; +#endif + return EC_SUCCESS; +} + diff --git a/common/console.c b/common/console.c new file mode 100644 index 0000000000..bc558f5cf4 --- /dev/null +++ b/common/console.c @@ -0,0 +1,207 @@ +/* Copyright (c) 2011 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 "task.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + +#define MAX_COMMAND_GROUPS 20 +#define MAX_ARGS_PER_COMMAND 10 + +#define PROMPT "> " + +static const struct console_group *group_list[MAX_COMMAND_GROUPS]; +static int group_count = 0; + + +void console_has_input(void) +{ + /* Wake up the console task */ + task_send_msg(TASK_ID_CONSOLE, TASK_ID_CONSOLE, 0); +} + + +int console_register_commands(const struct console_group *group) +{ + if (group_count >= MAX_COMMAND_GROUPS) + return EC_ERROR_OVERFLOW; + + group_list[group_count++] = group; + return EC_SUCCESS; +} + + +/* 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. */ +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; +} + + +/* Finds a command by name. Returns the command structure, or NULL if + * no match found. */ +const struct console_command *find_command(char *name) +{ + const struct console_command *cmd; + int c, g; + + /* Find the command in the command groups */ + for (g = 0; g < group_count; g++) { + cmd = group_list[g]->commands; + for (c = group_list[g]->command_count; c > 0; c--, cmd++) { + if (!strcasecmp(name, cmd->name)) + return cmd; + } + } + + return NULL; +} + + +/* Handles 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; + + /* 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) + return cmd->handler(argc, argv); + + uart_printf("Command '%s' not found.\n", argv[0]); + return EC_ERROR_UNKNOWN; +} + + +static char last_input[80]; +static char input_buf[80]; + +/* handle a console command */ +void console_process(void) +{ + int rv; + + /* Process all the pending commands. Need to do this all at once + * since our interrupt may have been triggered multiple times. */ + /* TODO: Go to sleep briefly between commands to give lower + * priority tasks a chance to run? */ + while (uart_peek('\n') >= 0) { + uart_gets(input_buf, sizeof(input_buf)); + + /* "." repeats the last command, if any */ + if (!strcasecmp(input_buf, ".\n")) + strzcpy(input_buf, last_input, sizeof(input_buf)); + else if (!isspace(*input_buf)) + strzcpy(last_input, input_buf, sizeof(last_input)); + + rv = handle_command(input_buf); + if (rv != EC_SUCCESS) + uart_printf("Command returned error %d\n", rv); + uart_puts(PROMPT); + } +} + +void console_task(void) +{ + console_init(); + + while (1) { + console_process(); + /* wait for the next command message */ + task_wait_msg(-1); + } +} + +/*****************************************************************************/ +/* Console commands */ + +/* Command handler - prints help. */ +static int command_help(int argc, char **argv) +{ + const struct console_command *cmd; + int c, g; + + uart_puts("Known commands:\n"); + + for (g = 0; g < group_count; g++) { + cmd = group_list[g]->commands; + uart_printf("Group %s:\n", group_list[g]->group_name); + for (c = group_list[g]->command_count; c > 0; c--, cmd++) + uart_printf(" %s\n", cmd->name); + /* Generates enough output to overflow the buffer */ + uart_flush_output(); + } + + uart_puts("'.' repeats the last command.\n"); + + return EC_SUCCESS; +} + +static const struct console_command console_commands[] = { + {"help", command_help}, + {"?", command_help}, +}; +static const struct console_group command_group = { + "Console", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int console_init(void) +{ + *last_input = '\0'; + *input_buf = '\0'; + uart_set_console_mode(1); + uart_printf("Console is enabled; type HELP for help.\n"); + uart_puts(PROMPT); + /* Register our internal commands */ + return console_register_commands(&command_group); +} diff --git a/common/flash_commands.c b/common/flash_commands.c new file mode 100644 index 0000000000..c53f8e6d97 --- /dev/null +++ b/common/flash_commands.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2011 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. + */ + +/* Flash memory module for Chrome EC */ + +#include "console.h" +#include "flash.h" +#include "flash_commands.h" +#include "lpc_commands.h" +#include "registers.h" /* TODO: remove; only for temp debugging */ +#include "shared_mem.h" +#include "uart.h" +#include "util.h" + + +/*****************************************************************************/ +/* Console commands */ + +static int command_flash_info(int argc, char **argv) +{ + uart_printf("Usable flash size: %d KB\n", flash_get_size()); + return EC_SUCCESS; +} + + +static int command_flash_erase(int argc, char **argv) +{ + int offset = 0; + int size = FLASH_ERASE_BYTES; + char *endptr; + int rv; + + if (argc < 2) { + uart_puts("Usage: flasherase <offset> [size]\n"); + return EC_ERROR_UNKNOWN; + } + + offset = strtoi(argv[1], &endptr, 0); + if (*endptr) { + uart_puts("Invalid offset\n"); + return EC_ERROR_UNKNOWN; + } + + if (argc > 2) { + size = strtoi(argv[2], &endptr, 0); + if (*endptr) { + uart_puts("Invalid size\n"); + return EC_ERROR_UNKNOWN; + } + } + + uart_printf("Erasing %d bytes at offset 0x%x (%d)...\n", + size, offset, offset); + rv = flash_erase(offset, size); + if (rv == EC_SUCCESS) + uart_puts("done.\n"); + else + uart_printf("failed. (error %d)\n", rv); + + return rv; +} + + +static int command_flash_write(int argc, char **argv) +{ + char *data; + int offset = 0; + int size = 1024; /* Default size */ + char *endptr; + int rv; + int i; + + if (argc < 2) { + uart_puts("Usage: flashwrite <offset> <size>\n"); + return EC_ERROR_UNKNOWN; + } + + offset = strtoi(argv[1], &endptr, 0); + if (*endptr) { + uart_puts("Invalid offset\n"); + return EC_ERROR_UNKNOWN; + } + + if (argc > 2) { + size = strtoi(argv[2], &endptr, 0); + if (*endptr) { + uart_puts("Invalid size\n"); + return EC_ERROR_UNKNOWN; + } + if (size > shared_mem_size()) { + uart_puts("Truncating size\n"); + size = sizeof(data); + } + } + + /* Acquire the shared memory buffer */ + rv = shared_mem_acquire(size, 0, &data); + if (rv) { + uart_printf("Unable to acquire %d byte buffer\n", size); + return rv; + } + + /* Fill the data buffer with a pattern */ + for (i = 0; i < size; i++) + data[i] = i; + + uart_printf("Writing %d bytes to offset 0x%x (%d)...\n", + size, offset, offset); + rv = flash_write(offset, size, data); + if (rv == EC_SUCCESS) + uart_puts("done.\n"); + else + uart_printf("failed. (error %d)\n", rv); + + /* Free the buffer */ + shared_mem_release(data); + + return rv; +} + + +static int command_flash_wp(int argc, char **argv) +{ + int b = 0; + char *endptr; + + if (argc < 2) { + uart_puts("Usage: flashwp [bitmask]\n"); + uart_printf("(current value of FMPPE1: 0x%08x)\n", + LM4_FLASH_FMPPE1); + return EC_SUCCESS; + } + + b = strtoi(argv[1], &endptr, 0); + if (*endptr) { + uart_puts("Invalid bitmask\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("FMPPE1 before: 0x%08x\n", LM4_FLASH_FMPPE1); + LM4_FLASH_FMPPE1 = b; + uart_printf("FMPPE1 after: 0x%08x\n", LM4_FLASH_FMPPE1); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"flasherase", command_flash_erase}, + {"flashinfo", command_flash_info}, + {"flashwrite", command_flash_write}, + {"flashwp", command_flash_wp}, +}; +static const struct console_group command_group = { + "Flash", console_commands, ARRAY_SIZE(console_commands) +}; + + + +/*****************************************************************************/ +/* Host commands */ + +enum lpc_status flash_command_get_info(uint8_t *data) +{ + struct lpc_response_flash_info *r = + (struct lpc_response_flash_info *)data; + + r->flash_size = flash_get_size(); + r->write_block_size = FLASH_WRITE_BYTES; + r->erase_block_size = FLASH_ERASE_BYTES; + r->protect_block_size = FLASH_PROTECT_BYTES; + return EC_LPC_STATUS_SUCCESS; +} + + +enum lpc_status flash_command_read(uint8_t *data) +{ + struct lpc_params_flash_read *p = + (struct lpc_params_flash_read *)data; + struct lpc_response_flash_read *r = + (struct lpc_response_flash_read *)data; + + if (p->size > sizeof(r->data)) + return EC_LPC_STATUS_ERROR; + + if (flash_read(p->offset, p->size, r->data)) + return EC_LPC_STATUS_ERROR; + + return EC_LPC_STATUS_SUCCESS; +} + + +enum lpc_status flash_command_write(uint8_t *data) +{ + struct lpc_params_flash_write *p = + (struct lpc_params_flash_write *)data; + + if (p->size > sizeof(p->data)) + return EC_LPC_STATUS_ERROR; + + if (flash_write(p->offset, p->size, p->data)) + return EC_LPC_STATUS_ERROR; + + return EC_LPC_STATUS_SUCCESS; +} + + +enum lpc_status flash_command_erase(uint8_t *data) +{ + struct lpc_params_flash_erase *p = + (struct lpc_params_flash_erase *)data; + + if (flash_erase(p->offset, p->size)) + return EC_LPC_STATUS_ERROR; + + return EC_LPC_STATUS_SUCCESS; +} + +/*****************************************************************************/ +/* Initialization */ + +int flash_commands_init(void) +{ + /* Register our internal commands */ + console_register_commands(&command_group); + return EC_SUCCESS; +} + diff --git a/common/host_command.c b/common/host_command.c new file mode 100644 index 0000000000..1f27196412 --- /dev/null +++ b/common/host_command.c @@ -0,0 +1,203 @@ +/* Copyright (c) 2011 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. + */ + +/* Host command module for Chrome EC */ + +#include "config.h" +#include "console.h" +#include "flash_commands.h" +#include "host_command.h" +#include "lpc.h" +#include "lpc_commands.h" +#include "system.h" +#include "task.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + +static int host_command = 0; +static uint8_t *host_data; + +/*****************************************************************************/ +/* Host commands */ + +void host_command_received(int command) +{ + /* TODO: should warn if we already think we're in a command */ + + /* If this is the reboot command, reboot immediately. This gives + * the host processor a way to unwedge the EC even if it's busy with + * some other command. */ + if (command == EC_LPC_COMMAND_REBOOT) { + system_reset(1); + /* Reset should never return; if it does, post an error */ + lpc_send_host_response(EC_LPC_STATUS_ERROR); + return; + } + + /* Save the command */ + host_command = command; + + /* Wake up the task to handle the command */ + task_send_msg(TASK_ID_HOSTCMD, TASK_ID_HOSTCMD, 0); +} + + +static enum lpc_status HostCommandHello(uint8_t *data) +{ + struct lpc_params_hello *p = (struct lpc_params_hello *)data; + struct lpc_response_hello *r = (struct lpc_response_hello *)data; + uint32_t d = p->in_data; + + uart_printf("[LPC Hello 0x%08x]\n", d); + + /* TODO - remove this delay. For now, pretend command takes a long + * time, so we can see the busy bit set on the host side. */ + { + volatile int i __attribute__((unused)); + int j; + for (j = 0; j < 10000000; j++) + i = j; + } + uart_puts("[LPC sending hello back]\n"); + + r->out_data = d + 0x01020304; + return EC_LPC_STATUS_SUCCESS; +} + + +static enum lpc_status HostCommandGetVersion(uint8_t *data) +{ + struct lpc_response_get_version *r = + (struct lpc_response_get_version *)data; + + uart_printf("[LPC GetVersion]\n"); + + strzcpy(r->version_string_ro, system_get_version(SYSTEM_IMAGE_RO), + sizeof(r->version_string_ro)); + strzcpy(r->version_string_rw_a, system_get_version(SYSTEM_IMAGE_RW_A), + sizeof(r->version_string_rw_a)); + strzcpy(r->version_string_rw_b, system_get_version(SYSTEM_IMAGE_RW_B), + sizeof(r->version_string_rw_b)); + + switch(system_get_image_copy()) { + case SYSTEM_IMAGE_RO: + r->current_image = EC_LPC_IMAGE_RO; + break; + case SYSTEM_IMAGE_RW_A: + r->current_image = EC_LPC_IMAGE_RW_A; + break; + case SYSTEM_IMAGE_RW_B: + r->current_image = EC_LPC_IMAGE_RW_B; + break; + default: + r->current_image = EC_LPC_IMAGE_UNKNOWN; + break; + } + + return EC_LPC_STATUS_SUCCESS; +} + + +static enum lpc_status HostCommandReadTest(uint8_t *data) +{ + struct lpc_params_read_test *p = (struct lpc_params_read_test *)data; + struct lpc_response_read_test *r = + (struct lpc_response_read_test *)data; + + int offset = p->offset; + int size = p->size / sizeof(uint32_t); + int i; + + if (size > ARRAY_SIZE(r->data)) + return EC_LPC_STATUS_ERROR; + + for (i = 0; i < size; i++) + r->data[i] = offset + i; + + return EC_LPC_STATUS_SUCCESS; +} + + +/* handle a LPC command */ +static void command_process(void) +{ + uart_printf("[hostcmd 0x%02x]\n", host_command); + + /* TODO: might be smaller to make this a table, once we get a bunch + * of commands. */ + switch (host_command) { + case EC_LPC_COMMAND_HELLO: + lpc_send_host_response(HostCommandHello(host_data)); + return; + case EC_LPC_COMMAND_GET_VERSION: + lpc_send_host_response(HostCommandGetVersion(host_data)); + return; + case EC_LPC_COMMAND_READ_TEST: + lpc_send_host_response(HostCommandReadTest(host_data)); + return; + case EC_LPC_COMMAND_FLASH_INFO: + lpc_send_host_response(flash_command_get_info(host_data)); + return; + case EC_LPC_COMMAND_FLASH_READ: + lpc_send_host_response(flash_command_read(host_data)); + return; + case EC_LPC_COMMAND_FLASH_WRITE: + lpc_send_host_response(flash_command_write(host_data)); + return; + case EC_LPC_COMMAND_FLASH_ERASE: + lpc_send_host_response(flash_command_erase(host_data)); + return; + default: + lpc_send_host_response(EC_LPC_STATUS_INVALID_COMMAND); + } +} + +/*****************************************************************************/ +/* Console commands */ + +/* Command handler - prints EC version. */ +static int command_version(int argc, char **argv) +{ + uart_printf("RO version: %s\n", + system_get_version(SYSTEM_IMAGE_RO)); + uart_printf("RW-A version: %s\n", + system_get_version(SYSTEM_IMAGE_RW_A)); + uart_printf("RW-B version: %s\n", + system_get_version(SYSTEM_IMAGE_RW_B)); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"version", command_version}, +}; + +static const struct console_group command_group = { + "Host commands", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int host_command_init(void) +{ + host_data = (uint8_t *)lpc_get_host_range(); + + console_register_commands(&command_group); + return EC_SUCCESS; +} + +void host_command_task(void) +{ + host_command_init(); + + while (1) { + /* wait for the next command message */ + task_wait_msg(-1); + /* process it */ + command_process(); + } +} diff --git a/common/i8042.c b/common/i8042.c new file mode 100644 index 0000000000..42c6095ecc --- /dev/null +++ b/common/i8042.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2011 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. + * + * Chrome OS EC i8042 interface code. + */ + +#include "board.h" +#include "common.h" +#include "i8042.h" +#include "keyboard.h" +/* TODO: Code in common.c should not directly access chip registers */ +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" + + +#define I8042_DEBUG 1 + +#define MAX_QUEUED_KEY_PRESS 16 + +/* Circular buffer to host. + * head: next to dequeqe + * tail: next to enqueue + * head == tail: empty. + * tail + 1 == head: full + */ +static int head_to_buffer = 0; +static int tail_to_buffer = 0; +#define HOST_BUFFER_SIZE (16) +static uint8_t to_host_buffer[HOST_BUFFER_SIZE]; + + +void i8042_init() +{ + head_to_buffer = tail_to_buffer = 0; + LM4_LPC_ST(3) = 0; /* clear the TOH bit */ +} + + +void i8042_receives_data(int data) +{ + int ret_len; + uint8_t output[MAX_SCAN_CODE_LEN]; + enum ec_error_list ret; + + ret_len = handle_keyboard_data(data, output); + ret = i8042_send_to_host(ret_len, output); + ASSERT(ret == EC_SUCCESS); +} + + +void i8042_receives_command(int cmd) +{ + int ret_len; + uint8_t output[MAX_SCAN_CODE_LEN]; + enum ec_error_list ret; + + ret_len = handle_keyboard_command(cmd, output); + ret = i8042_send_to_host(ret_len, output); + ASSERT(ret == EC_SUCCESS); +} + + +static void enq_to_host(int len, uint8_t *to_host) +{ + int from, to; + + /* TODO: need atomic protection */ + if ((tail_to_buffer + len) <= (head_to_buffer + HOST_BUFFER_SIZE - 1)) { + for (from = 0, to = tail_to_buffer; from < len;) { + to_host_buffer[to++] = to_host[from++]; + to %= HOST_BUFFER_SIZE; + } + tail_to_buffer = (tail_to_buffer + len) % HOST_BUFFER_SIZE; + } + /* end of atomic protection */ +} + + +void i8042_command_task(void) +{ + while (1) { + /* Either a new byte to host or host picking up can un-block. */ + task_wait_msg(-1); + + while (1) { + uint8_t chr; + int empty = 0; + + /* TODO: need atomic protection */ + if (head_to_buffer == tail_to_buffer) { + empty = 1; /* nothing to host */ + } + /* end of atomic protection */ + if (empty) break; + + /* if the host still didn't read that away, + try next time. */ + if (LM4_LPC_ST(3) & (1 << 0 /* TOH */)) { +#if I8042_DEBUG >= 5 + uart_printf("[%d] i8042_command_task() " + "cannot send to host due to TOH\n", + get_time().le.lo); +#endif + break; + } + + /* TODO: need atomic protection */ + chr = to_host_buffer[head_to_buffer]; + head_to_buffer = + (head_to_buffer + 1) % HOST_BUFFER_SIZE; + /* end of atomic protection */ + + /* Write to host. TOH is set automatically. */ + LPC_POOL_KEYBOARD[1] = chr; +#if I8042_DEBUG >= 4 + uart_printf("[%d] i8042_command_task() " + "sends to host: 0x%02x\n", + get_time().le.lo, chr); +#endif + } + } +} + + +enum ec_error_list i8042_send_to_host(int len, uint8_t *to_host) +{ + enq_to_host(len, to_host); + + /* Wake up the task to handle the command */ + task_send_msg(TASK_ID_I8042CMD, TASK_ID_I8042CMD, 0); + + return EC_SUCCESS; +} diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000000..c9e1bbe75d --- /dev/null +++ b/common/main.c @@ -0,0 +1,94 @@ +/* Copyright (c) 2011 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. + * Copyright 2011 Google Inc. + * + * Example of EC main loop + */ + +#include "registers.h" +#include "board.h" + +#include "adc.h" +#include "clock.h" +#include "console.h" +#include "eeprom.h" +#include "flash.h" +#include "flash_commands.h" +#include "gpio.h" +#include "i2c.h" +#include "keyboard.h" +#include "lpc.h" +#include "memory_commands.h" +#include "port80.h" +#include "powerdemo.h" +#include "pwm.h" +#include "system.h" +#include "task.h" +#include "temp_sensor.h" +#include "timer.h" +#include "uart.h" +#include "vboot.h" +#include "watchdog.h" + +/* example task blinking the user LED */ +void UserLedBlink(void) +{ + while (1) { + gpio_set(EC_GPIO_DEBUG_LED, 1); + usleep(500000); + watchdog_reload(); + gpio_set(EC_GPIO_DEBUG_LED, 0); + usleep(500000); + watchdog_reload(); + } +} + + +int main(void) +{ + /* Configure the pin multiplexers */ + configure_board(); + /* Set the CPU clocks / PLLs */ + clock_init(); + + /* Do system, gpio, and vboot pre-initialization so we can jump to + * another image if necessary. This must be done as early as + * possible, so that the minimum number of components get + * re-initialized if we jump to another image. */ + system_pre_init(); + gpio_pre_init(); + vboot_pre_init(); + + /* TODO - race condition on enabling interrupts. Module inits + * should call task_IntEnable(int) when they're ready... */ + task_init(); + + watchdog_init(1100); + timer_init(); + uart_init(); + system_init(); + gpio_init(); + flash_init(); + eeprom_init(); + port_80_init(); + lpc_init(); + flash_commands_init(); + vboot_init(); + pwm_init(); + i2c_init(); + temp_sensor_init(); + memory_commands_init(); + keyboard_init(); + adc_init(); + + /* Print the reset cause */ + uart_printf("\n\n--- Chrome EC initialized! ---\n"); + uart_printf("(image: %s, version: %s, last reset: %s)\n", + system_get_image_copy_string(), + system_get_version(SYSTEM_IMAGE_UNKNOWN), + system_get_reset_cause_string()); + + /* Launch task scheduling (never returns) */ + return task_start(); +} diff --git a/common/port80.c b/common/port80.c new file mode 100644 index 0000000000..68d3219aa0 --- /dev/null +++ b/common/port80.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2011 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. + */ + +/* Port 80 module for Chrome EC */ + +#include "console.h" +#include "port80.h" +#include "uart.h" +#include "util.h" + + +#define HISTORY_LEN 16 + +static uint8_t history[HISTORY_LEN]; +static int head = 0; /* Next index to use / oldest previous entry */ +static int last_data = -1; /* Last data written to port 80 */ + + +void port_80_write(int data) +{ + /* Ignore duplicate writes, since the linux kernel writes to port 80 + * as a delay mechanism during boot. */ + if (data == last_data) + return; + + /* TODO: post to SWI and print from there? This currently + * prints from inside the LPC interrupt itself. */ + + uart_printf("[Port 80: 0x%02x]\n", data); + last_data = data; + + history[head] = data; + head = (head + 1) & (HISTORY_LEN - 1); +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_port80(int argc, char **argv) +{ + int h = head; + int i; + + /* Technically, if a port 80 write comes in while we're + * printing this, we could print an incorrect history. + * Probably not worth the complexity to work around that. */ + + uart_puts("Last port 80 writes:"); + for (i = 0; i < HISTORY_LEN; i++) + uart_printf(" %02x", history[(h + i) & (HISTORY_LEN - 1)]); + uart_puts(" <--newest\n"); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"port80", command_port80}, +}; +static const struct console_group command_group = { + "Port 80", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int port_80_init(void) +{ + console_register_commands(&command_group); + memset(history, 0, sizeof(history)); + return EC_SUCCESS; +} diff --git a/common/shared_mem.c b/common/shared_mem.c new file mode 100644 index 0000000000..bff3ec6da3 --- /dev/null +++ b/common/shared_mem.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2011 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. + */ + +/* Shared memory module for Chrome EC */ + +#include "shared_mem.h" +#include "uart.h" + +/* Size of shared memory buffer */ +#define SHARED_MEM_SIZE 4096 + +static char shared_buf[SHARED_MEM_SIZE]; +static int buf_in_use = 0; + + +int shared_mem_init(void) +{ + return EC_SUCCESS; +} + + +int shared_mem_size(void) +{ + return SHARED_MEM_SIZE; +} + + +int shared_mem_acquire(int size, int wait, char **dest_ptr) +{ + if (size > SHARED_MEM_SIZE || size <= 0) + return EC_ERROR_INVAL; + + /* TODO: if task_start() hasn't been called, fail immediately + * if not available. */ + + /* TODO: wait if requested; for now, we fail immediately if + * not available. */ + if (buf_in_use) + return EC_ERROR_BUSY; + + /* TODO: atomically acquire buf_in_use. */ + buf_in_use = 1; + *dest_ptr = shared_buf; + return EC_SUCCESS; +} + + +void shared_mem_release(void *ptr) +{ + /* TODO: use event to wake up a previously-blocking acquire */ + buf_in_use = 0; +} diff --git a/common/util.c b/common/util.c new file mode 100644 index 0000000000..5eefcbe6ff --- /dev/null +++ b/common/util.c @@ -0,0 +1,151 @@ +/* Copyright (c) 2011 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. + */ + +/* Utility functions for Chrome EC */ + +#include "util.h" + +int strlen(const char *s) +{ + int len = 0; + + while (*s++) + len++; + + return len; +} + + +int isspace(int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + + +int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + + +int tolower(int c) +{ + return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c; +} + + +int strcasecmp(const char *s1, const char *s2) +{ + int diff; + do { + diff = tolower(*s1) - tolower(*s2); + if (diff) + return diff; + } while (*(s1++) && *(s2++)); + return 0; +} + + +int atoi(const char *nptr) +{ + int result = 0; + int neg = 0; + char c = '\0'; + + while ((c = *nptr++) && isspace(c)) {} + + if (c == '-') { + neg = 1; + c = *nptr++; + } + + while (isdigit(c)) { + result = result * 10 + (c - '0'); + c = *nptr++; + } + + return neg ? -result : result; +} + + +/* Like strtol(), but for integers */ +int strtoi(const char *nptr, char **endptr, int base) +{ + int result = 0; + int neg = 0; + int c = '\0'; + + if (endptr) + *endptr = (char *)nptr; + + while((c = *nptr++) && isspace(c)) {} + + if (c == '0' && *nptr == 'x' && (base == 0 || base == 16)) { + base = 16; + c = nptr[1]; + nptr += 2; + } else { + base = 10; + if (c == '-') { + neg = 1; + c = *nptr++; + } + } + + while (c) { + if (c >= '0' && c < '0' + MIN(base, 10)) + result = result * base + (c - '0'); + else if (c >= 'A' && c < 'A' + base - 10) + result = result * base + (c - 'A' + 10); + else if (c >= 'a' && c < 'a' + base - 10) + result = result * base + (c - 'a' + 10); + else + break; + + if (endptr) + *endptr = (char *)nptr; + c = *nptr++; + } + + return neg ? -result : result; +} + + +void *memcpy(void *dest, const void *src, int len) +{ + /* TODO: optimized version using LDM/STM would be much faster */ + char *d = (char *)dest; + const char *s = (const char *)src; + while (len > 0) { + *(d++) = *(s++); + len--; + } + return dest; +} + + +void *memset(void *dest, int c, int len) +{ + /* TODO: optimized version using STM would be much faster */ + char *d = (char *)dest; + while (len > 0) { + *(d++) = c; + len--; + } + return dest; +} + + +/* Like strncpy(), but guarantees null termination */ +char *strzcpy(char *dest, const char *src, int len) +{ + char *d = dest; + while (len > 1 && *src) { + *(d++) = *(src++); + len--; + } + *d = '\0'; + return dest; +} diff --git a/common/vboot.c b/common/vboot.c new file mode 100644 index 0000000000..ebf514d79d --- /dev/null +++ b/common/vboot.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2011 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. + */ + +/* Verified boot module for Chrome EC */ + +#include "console.h" +#include "system.h" +#include "uart.h" +#include "util.h" +#include "vboot.h" + + +#define SCRATCHPAD_EMPTY 0 +#define SCRATCHPAD_REQUEST_A 0xb00daaaa +#define SCRATCHPAD_REQUEST_B 0xb00dbbbb +#define SCRATCHPAD_SELECTED_A 0x0000d1da +#define SCRATCHPAD_SELECTED_B 0x0000d1db +#define SCRATCHPAD_SELECTED_RO 0x0000d1d0 +#define SCRATCHPAD_FAILED_A 0x0000eeea +#define SCRATCHPAD_FAILED_B 0x0000eeeb + + +/* Jumps to one of the RW images if necessary. */ +static void jump_to_other_image(void) +{ + int s; + + if (system_get_image_copy() != SYSTEM_IMAGE_RO) + return; /* Not in RO firmware, so ignore scratchpad */ + + if (system_get_reset_cause() != SYSTEM_RESET_SOFT_COLD) { + /* In RO firmware, but not because of a warm boot. + * Stay in RO regardless of scratchpad, and clear it + * so we don't use it on the next boot. */ + system_set_scratchpad(SCRATCHPAD_EMPTY); + return; + } + + /* TODO: check recovery button; if it's pressed, stay in RO */ + + /* Check for a scratchpad value we recognize. Clear the + * scratchpad before jumping, so we only do this once. */ + s = system_get_scratchpad(); + if (s == SCRATCHPAD_REQUEST_A) { + system_set_scratchpad(SCRATCHPAD_SELECTED_A); + system_run_image_copy(SYSTEM_IMAGE_RW_A); + /* Shouldn't normally return; if we did, flag error */ + system_set_scratchpad(SCRATCHPAD_FAILED_A); + } else if (s == SCRATCHPAD_REQUEST_B) { + system_set_scratchpad(SCRATCHPAD_SELECTED_B); + system_run_image_copy(SYSTEM_IMAGE_RW_B); + /* Shouldn't normally return; if we did, flag error */ + system_set_scratchpad(SCRATCHPAD_FAILED_B); + } else { + system_set_scratchpad(SCRATCHPAD_EMPTY); + } +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_reboot(int argc, char **argv) +{ + /* Handle request to boot to a specific image */ + if (argc >= 2) { + if (!strcasecmp(argv[1], "a")) { + uart_puts("Rebooting to image A!\n\n\n"); + system_set_scratchpad(SCRATCHPAD_REQUEST_A); + } else if (!strcasecmp(argv[1], "b")) { + uart_puts("Rebooting to image B!\n\n\n"); + system_set_scratchpad(SCRATCHPAD_REQUEST_B); + } else { + uart_puts("Usage: reboot [ A | B ]\n"); + return EC_ERROR_UNKNOWN; + } + } else { + uart_puts("Rebooting to RO!\n\n\n"); + } + + uart_flush_output(); + /* TODO - param to specify warm/cold */ + system_reset(1); + return EC_SUCCESS; +} + +static const struct console_command console_commands[] = { + {"reboot", command_reboot}, +}; + +static const struct console_group command_group = { + "Verified boot", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int vboot_pre_init(void) +{ + /* Jump to a different image if necessary; this may not return */ + jump_to_other_image(); + return EC_SUCCESS; +} + + +int vboot_init(void) +{ + /* Register our internal commands */ + console_register_commands(&command_group); + return EC_SUCCESS; +} diff --git a/include/adc.h b/include/adc.h new file mode 100644 index 0000000000..1f01f2c08f --- /dev/null +++ b/include/adc.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2011 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. + */ + +/* ADC interface for Chrome EC */ + +#ifndef __CROS_EC_ADC_H +#define __CROS_EC_ADC_H + +#include "common.h" + +/* Value returned by adc_read_*() methods if the read failed. */ +#define ADC_READ_ERROR -1 + +/* Minimum and maximum values returned by adc_read(). */ +#define ADC_READ_MIN 0 +#define ADC_READ_MAX 4095 + +/* ADC channels */ +/* TODO: channel mapping is board-specific */ +enum adc_channel { + ADC_CH_POT = 0, +}; + +/* Initializes the module. */ +int adc_init(void); + +/* Reads an ADC channel. Returns the ADC value, or ADC_READ_ERROR if error. */ +int adc_read(enum adc_channel ch); + +/* Returns the internal EC temperature in K, or ADC_READ_ERROR if error. */ +int adc_read_ec_temperature(void); + +#endif /* __CROS_EC_ADC_H */ diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000000..ceb2bc873f --- /dev/null +++ b/include/common.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2011 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. + */ + +/* common.h - Common includes for Chrome EC */ + +#ifndef __CROS_EC_COMMON_H +#define __CROS_EC_COMMON_H + +#include <stdint.h> + +/* List of common error codes that can be returned */ +enum ec_error_list { + /* Success - no error */ + EC_SUCCESS = 0, + /* Unknown error */ + EC_ERROR_UNKNOWN = 1, + /* Function not implemented yet */ + EC_ERROR_UNIMPLEMENTED = 2, + /* Overflow error; too much input provided. */ + EC_ERROR_OVERFLOW = 3, + /* Timeout */ + EC_ERROR_TIMEOUT = 4, + /* Invalid argument */ + EC_ERROR_INVAL = 5, + /* Already in use */ + EC_ERROR_BUSY = 6, + + /* Module-internal error codes may use this range. */ + EC_ERROR_INTERNAL_FIRST = 0x10000, + EC_ERROR_INTERNAL_LAST = 0x1FFFF +}; + +#endif /* __CROS_EC_COMMON_H */ diff --git a/include/console.h b/include/console.h new file mode 100644 index 0000000000..f8f76860da --- /dev/null +++ b/include/console.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2011 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.h - Debug console for Chrome EC */ + +#ifndef __CROS_EC_CONSOLE_H +#define __CROS_EC_CONSOLE_H + +#include "common.h" + +/* Console command */ +struct console_command { + /* Command name. Case-insensitive. */ + const char *name; + /* Handler for the command. argv[0] will be the command name. */ + int (*handler)(int argc, char **argv); +}; + + +/* Console command group */ +struct console_group { + const char *group_name; /* Name of the command group */ + const struct console_command *commands; /* List of commands */ + int command_count; /* Number of commands in list */ +}; + + +/* Initializes the console module. */ +int console_init(void); + + +/* Called by UART when a line of input is pending. */ +void console_has_input(void); + + +/* Registers a group of console commands. */ +int console_register_commands(const struct console_group *group); + +#endif /* __CROS_EC_CONSOLE_H */ diff --git a/include/eeprom.h b/include/eeprom.h new file mode 100644 index 0000000000..70290e17d4 --- /dev/null +++ b/include/eeprom.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2011 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. + */ + +/* EEPROM module for Chrome EC */ + +#ifndef __CROS_EC_EEPROM_H +#define __CROS_EC_EEPROM_H + +#include "common.h" + +/* Initializes the module. */ +int eeprom_init(void); + +/* Returns the number of EEPROM blocks on the system. */ +int eeprom_get_block_count(void); + +/* Returns the EEPROM block size in bytes. */ +int eeprom_get_block_size(void); + +/* Reads <size> bytes of data from <offset> in <block> of EEPROM. Offset + * and size must be a multiple of 32 bits. */ +int eeprom_read(int block, int offset, int size, char *data); + +/* Writes <size> bytes of data to <offset> in <block> of EEPROM. Offset + * and size must be a multiple of 32 bits. */ +int eeprom_write(int block, int offset, int size, const char *data); + +/* Hides an EEPROM block until the next reset. */ +int eeprom_hide(int block); + +/* TODO: write protect */ + +#endif /* __CROS_EC_EEPROM_H */ diff --git a/include/flash.h b/include/flash.h new file mode 100644 index 0000000000..38944e43cb --- /dev/null +++ b/include/flash.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2011 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. + */ + +/* Flash memory module for Chrome EC */ + +#ifndef __CROS_EC_FLASH_H +#define __CROS_EC_FLASH_H + +#include "common.h" + + +#define FLASH_WRITE_BYTES 4 +#define FLASH_FWB_WORDS 32 +#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4) +#define FLASH_ERASE_BYTES 1024 +#define FLASH_PROTECT_BYTES 2048 + + +/* Initializes the module. */ +int flash_init(void); + +/* Returns the usable size of flash in bytes. Note that this is + * smaller than the actual flash size, */ +int flash_get_size(void); + +/* Returns the write / erase / protect block size, in bytes. + * Operations must be aligned to and multiples of the granularity. + * For example, erase operations must have offset and size which are + * multiples of the erase block size. */ +int flash_get_write_block_size(void); +int flash_get_erase_block_size(void); +int flash_get_protect_block_size(void); + +/* Reads <size> bytes of data from offset <offset> into <data>. */ +int flash_read(int offset, int size, char *data); + +/* Writes <size> bytes of data to flash at byte offset <offset>. + * <data> must be 32-bit aligned. */ +int flash_write(int offset, int size, const char *data); + +/* Erases <size> bytes of flash at byte offset <offset>. */ +int flash_erase(int offset, int size); + +/* TODO: not super happy about the following APIs yet. + * + * The theory of operation is that we'll use the last page of flash to + * hold the write protect range, and the flag for whether the last + * page itself should be protected. Then when flash_init() is called, + * it checks if the write protect pin is asserted, and if so, it + * writes (but does not commit) the flash protection registers. + * + * This simulates what a SPI flash does, where the status register + * holds the write protect range, and a bit which protects the status + * register itself. The bit is only obeyed if the write protect pin + * is enabled. + * + * It's an imperfect simulation, because in a SPI flash, as soon as + * you deassert the pin you can alter the status register, where here + * it'll take a cold boot to clear protection. Also, here protection + * gets written to the registers as soon as you set the write protect + * lock, which is different than SPI, where it's effective as soon as + * you set the write protect range. */ + +/* Gets or sets the write protect range in bytes. This setting is + * stored in flash, and persists across reboots. If size is non-zero, + * the write protect range is also locked, and may not be subsequently + * altered until after a cold boot with the write protect pin + * deasserted. */ +int flash_get_write_protect_range(int *offset, int *size); +int flash_set_write_protect_range(int offset, int size); + +/* The write protect range has been stored into the chip registers + * this boot. The flash is write protected and the range cannot be + * changed without rebooting. */ +#define EC_FLASH_WP_RANGE_LOCKED 0x01 +/* The write protect pin was asserted at init time. */ +#define EC_FLASH_WP_PIN_ASSERTED_AT_INIT 0x02 +/* The write protect pin is asserted now. */ +#define EC_FLASH_WP_PIN_ASSERTED_NOW 0x04 + +/* Returns the current write protect status; see EC_FLASH_WP_* + * for valid flags. */ +int flash_get_write_protect_status(void); + + +#endif /* __CROS_EC_FLASH_H */ diff --git a/include/flash_commands.h b/include/flash_commands.h new file mode 100644 index 0000000000..e74fef1318 --- /dev/null +++ b/include/flash_commands.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2011 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. + */ + +/* Flash memory commands for Chrome EC */ + +#ifndef __CROS_EC_FLASH_COMMANDS_H +#define __CROS_EC_FLASH_COMMANDS_H + +#include "common.h" +#include "lpc_commands.h" + +/* Initializes the module. */ +int flash_commands_init(void); + +/* Host command handlers. */ +enum lpc_status flash_command_get_info(uint8_t *data); +enum lpc_status flash_command_read(uint8_t *data); +enum lpc_status flash_command_write(uint8_t *data); +enum lpc_status flash_command_erase(uint8_t *data); + +#endif /* __CROS_EC_FLASH_COMMANDS_H */ diff --git a/include/gpio.h b/include/gpio.h new file mode 100644 index 0000000000..05123c4eb6 --- /dev/null +++ b/include/gpio.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2011 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 */ + +#ifndef __CROS_EC_GPIO_H +#define __CROS_EC_GPIO_H + +#include "common.h" + +/* GPIO signal definitions. */ +enum gpio_signal { + /* Firmware write protect */ + EC_GPIO_WRITE_PROTECT = 0, + /* Recovery switch */ + EC_GPIO_RECOVERY_SWITCH, + /* Debug LED */ + EC_GPIO_DEBUG_LED +}; + + +/* Pre-initializes the module. This occurs before clocks or tasks are + * set up. */ +int gpio_pre_init(void); + +/* Initializes the GPIO module. */ +int gpio_init(void); + +/* Functions should return an error if the requested signal is not + * supported / not present on the board. */ + +/* Gets the current value of a signal (0=low, 1=hi). */ +int gpio_get(enum gpio_signal signal, int *value_ptr); + +/* Sets the current value of a signal. Returns error if the signal is + * not supported or is an input signal. */ +int gpio_set(enum gpio_signal signal, int value); + +#endif /* __CROS_EC_GPIO_H */ diff --git a/include/host_command.h b/include/host_command.h new file mode 100644 index 0000000000..fc39683c06 --- /dev/null +++ b/include/host_command.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2011 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. + */ + +/* Host command module for Chrome EC */ + +#ifndef __CROS_EC_HOST_COMMAND_H +#define __CROS_EC_HOST_COMMAND_H + +#include "common.h" + +/* Initializes the module. */ +int host_command_init(void); + +/* Called by LPC module when a command is written to port 66h. */ +void host_command_received(int command); + +#endif /* __CROS_EC_HOST_COMMAND_H */ diff --git a/include/i2c.h b/include/i2c.h new file mode 100644 index 0000000000..34076b1f34 --- /dev/null +++ b/include/i2c.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2011 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. + */ + +/* I2C interface for Chrome EC */ + +#ifndef __CROS_EC_I2C_H +#define __CROS_EC_I2C_H + +#include "common.h" + +/* Flags for slave address field, in addition to the 8-bit address */ +#define I2C_FLAG_BIG_ENDIAN 0x100 /* 16 byte values are MSB-first */ + +/* Initializes the module. */ +int i2c_init(void); + +/* Reads a 16-bit register from the slave at 8-bit slave address + * <slaveaddr>, at the specified 8-bit <offset> in the slave's address + * space. */ +int i2c_read16(int port, int slave_addr, int offset, int* data); + +/* Writes a 16-bit register to the slave at 8-bit slave address + * <slaveaddr>, at the specified 8-bit <offset> in the slave's address + * space. */ +int i2c_write16(int port, int slave_addr, int offset, int data); + +#endif /* __CROS_EC_I2C_H */ diff --git a/include/keyboard_scan.h b/include/keyboard_scan.h new file mode 100644 index 0000000000..8bc10c2c17 --- /dev/null +++ b/include/keyboard_scan.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2011 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. + */ + +/* Keyboard scanner module for Chrome EC */ + +#ifndef __CROS_EC_KEYBOARD_SCAN_H +#define __CROS_EC_KEYBOARD_SCAN_H + +#include "common.h" + +/* Initializes the module. */ +int keyboard_scan_init(void); + +#endif /* __CROS_KEYBOARD_SCAN_H */ diff --git a/include/lpc.h b/include/lpc.h new file mode 100644 index 0000000000..ac0b56c137 --- /dev/null +++ b/include/lpc.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2011 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. + */ + +/* LPC module for Chrome EC */ + +#ifndef __CROS_EC_LPC_H +#define __CROS_EC_LPC_H + +#include "common.h" + +/* Initializes the LPC module. */ +int lpc_init(void); + +/* Returns a pointer to the host command data buffer. This buffer + * must only be accessed between a notification to + * host_command_received() and a subsequent call to + * lpc_SendHostResponse(). */ +volatile uint8_t *lpc_get_host_range(void); + +/* Sends a response to a host command. The bottom 4 bits of <status> + * are sent in the status byte. */ +void lpc_send_host_response(int status); + +#endif /* __CROS_EC_LPC_H */ diff --git a/include/lpc_commands.h b/include/lpc_commands.h new file mode 100644 index 0000000000..4923db4adb --- /dev/null +++ b/include/lpc_commands.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2011 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. + */ + +/* LPC command constants for Chrome EC */ + +#ifndef __CROS_EC_LPC_COMMANDS_H +#define __CROS_EC_LPC_COMMANDS_H + +#include <stdint.h> + +/* LPC command status byte masks */ +/* EC is busy processing a command. This covers both bit 0x04, which + * is the busy-bit, and 0x02, which is the bit which indicates the + * host has written a byte but the EC hasn't picked it up yet. */ +#define EC_LPC_BUSY_MASK 0x06 +#define EC_LPC_STATUS_MASK 0xF0 /* Mask for status codes in status byte */ +#define EC_LPC_GET_STATUS(x) (((x) & EC_LPC_STATUS_MASK) >> 4) + +/* LPC command response codes */ +enum lpc_status { + EC_LPC_STATUS_SUCCESS = 0, + EC_LPC_STATUS_INVALID_COMMAND = 1, + EC_LPC_STATUS_ERROR = 2, + EC_LPC_STATUS_INVALID_PARAM = 3, +}; + + +/* Notes on commands: + * + * Each command is an 8-byte command value. Commands which take + * params or return response data specify structs for that data. If + * no struct is specified, the command does not input or output data, + * respectively. */ + +/* Reboot. This command will work even when the EC LPC interface is + * busy, because the reboot command is processed at interrupt + * level. Note that when the EC reboots, the host will reboot too, so + * there is no response to this command. */ +#define EC_LPC_COMMAND_REBOOT 0xD1 /* Think "die" */ + + +/* Hello. This is a simple command to test the EC is responsive to + * commands. */ +#define EC_LPC_COMMAND_HELLO 0x01 +struct lpc_params_hello { + uint32_t in_data; /* Pass anything here */ +} __attribute__ ((packed)); +struct lpc_response_hello { + uint32_t out_data; /* Output will be in_data + 0x01020304 */ +} __attribute__ ((packed)); + + +/* Get version number */ +#define EC_LPC_COMMAND_GET_VERSION 0x02 +enum lpc_current_image { + EC_LPC_IMAGE_UNKNOWN = 0, + EC_LPC_IMAGE_RO, + EC_LPC_IMAGE_RW_A, + EC_LPC_IMAGE_RW_B +}; +struct lpc_response_get_version { + /* Null-terminated version strings for RO, RW-A, RW-B */ + char version_string_ro[32]; + char version_string_rw_a[32]; + char version_string_rw_b[32]; + uint32_t current_image; /* One of lpc_current_image */ +} __attribute__ ((packed)); + + +/* Read test */ +#define EC_LPC_COMMAND_READ_TEST 0x03 +struct lpc_params_read_test { + uint32_t offset; /* Starting value for read buffer */ + uint32_t size; /* Size to read in bytes */ +} __attribute__ ((packed)); +struct lpc_response_read_test { + uint32_t data[32]; +} __attribute__ ((packed)); + +/*****************************************************************************/ +/* Flash commands */ + +/* Maximum bytes that can be read/written in a single command */ +#define EC_LPC_FLASH_SIZE_MAX 128 + +/* Get flash info */ +#define EC_LPC_COMMAND_FLASH_INFO 0x10 +struct lpc_response_flash_info { + /* Usable flash size, in bytes */ + uint32_t flash_size; + /* Write block size. Write offset and size must be a multiple + * of this. */ + uint32_t write_block_size; + /* Erase block size. Erase offset and size must be a multiple + * of this. */ + uint32_t erase_block_size; + /* Protection block size. Protection offset and size must be a + * multiple of this. */ + uint32_t protect_block_size; +} __attribute__ ((packed)); + + +/* Read flash */ +#define EC_LPC_COMMAND_FLASH_READ 0x11 +struct lpc_params_flash_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __attribute__ ((packed)); +struct lpc_response_flash_read { + uint8_t data[EC_LPC_FLASH_SIZE_MAX]; +} __attribute__ ((packed)); + + +/* Write flash */ +#define EC_LPC_COMMAND_FLASH_WRITE 0x12 +struct lpc_params_flash_write { + uint32_t offset; /* Byte offset to erase */ + uint32_t size; /* Size to erase in bytes */ + uint8_t data[EC_LPC_FLASH_SIZE_MAX]; +} __attribute__ ((packed)); + + +/* Erase flash */ +#define EC_LPC_COMMAND_FLASH_ERASE 0x13 +struct lpc_params_flash_erase { + uint32_t offset; /* Byte offset to erase */ + uint32_t size; /* Size to erase in bytes */ +} __attribute__ ((packed)); + + +#endif /* __CROS_EC_LPC_COMMANDS_H */ diff --git a/include/port80.h b/include/port80.h new file mode 100644 index 0000000000..060ee7bbd8 --- /dev/null +++ b/include/port80.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2011 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. + */ + +/* Port 80 module for Chrome EC */ + +#ifndef __CROS_EC_PORT80_H +#define __CROS_EC_PORT80_H + +#include "common.h" + +/* Initializes the module. */ +int port_80_init(void); + +/* Called by LPC module when a byte of data is written to port 80. */ +void port_80_write(int data); + +#endif /* __CROS_EC_PORT80_H */ diff --git a/include/powerdemo.h b/include/powerdemo.h new file mode 100644 index 0000000000..78acfc095b --- /dev/null +++ b/include/powerdemo.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2011 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. + */ + +/* Power state machine demo module for Chrome EC */ + +#ifndef __CROS_EC_POWERDEMO_H +#define __CROS_EC_POWERDEMO_H + +#include "common.h" + +/* Initializes the module. */ +int power_demo_init(void); + +#endif /* __CROS_EC_POWERDEMO_H */ diff --git a/include/pwm.h b/include/pwm.h new file mode 100644 index 0000000000..50534f2434 --- /dev/null +++ b/include/pwm.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2011 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. + */ + +/* PWM module for Chrome EC */ + +#ifndef __CROS_EC_PWM_H +#define __CROS_EC_PWM_H + +#include "common.h" + +/* Initializes the module. */ +int pwm_init(void); + +/* Gets the current fan RPM. */ +int pwm_get_fan_rpm(void); + +/* Sets the target fan RPM. Pass -1 to set fan to maximum. */ +int pwm_set_fan_target_rpm(int rpm); + +/* Sets the keyboard backlight percentage (0=off, 100=max). */ +int pwm_set_keyboard_backlight(int percent); + +/* Sets the power LED brightness to the specified percent (0=off, 100=max). */ +int pwm_set_power_led(int percent); + +#endif /* __CROS_EC_PWM_H */ diff --git a/include/shared_mem.h b/include/shared_mem.h new file mode 100644 index 0000000000..14b6613a9a --- /dev/null +++ b/include/shared_mem.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2011 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. + */ + +/* Shared memory interface for Chrome EC. + * + * This is intended to supply a relatively large block of memory for + * use by a task for a relatively short amount of time. For example, + * verified boot may need a buffer to hold signature data during a + * verification operation. It is NOT intended for allocating + * long-term buffers; those should in general be static variables + * allocated at compile-time. It is NOT a full-featured replacement + * for malloc() / free(). */ + +#ifndef __CROS_EC_SHARED_MEM_H +#define __CROS_EC_SHARED_MEM_H + +#include "common.h" + +/* Initializes the module. */ +int shared_mem_init(void); + +/* Returns the maximum amount of shared memory which can be acquired, + * in bytes. */ +int shared_mem_size(void); + +/* Acquires a shared memory area of the requested size in bytes. If + * wait != 0, will wait for the area to be available; if wait == 0, + * will fail with EC_ERROR_BUSY if the request cannot be fulfilled + * immediately. On success, sets *dest_ptr to the start of the memory + * area and returns EC_SUCCESS. */ +int shared_mem_acquire(int size, int wait, char **dest_ptr); + +/* Releases a shared memory area previously allocated via + * shared_mem_acquire(). */ +void shared_mem_release(void *ptr); + +#endif /* __CROS_EC_SHARED_MEM_H */ diff --git a/include/system.h b/include/system.h new file mode 100644 index 0000000000..46cb5bf730 --- /dev/null +++ b/include/system.h @@ -0,0 +1,100 @@ +/* Copyright (c) 2011 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. + */ + +/* System module for Chrome EC */ + +#ifndef __CROS_EC_SYSTEM_H +#define __CROS_EC_SYSTEM_H + +#include "common.h" + +/* Reset causes */ +enum system_reset_cause_t { + /* Unknown reset cause */ + SYSTEM_RESET_UNKNOWN = 0, + /* System reset cause is known, but not one of the causes + * listed below */ + SYSTEM_RESET_OTHER, + /* Brownout */ + SYSTEM_RESET_BROWNOUT, + /* Power-on reset */ + SYSTEM_RESET_POWER_ON, + /* Reset caused by asserting reset (RST#) pin */ + SYSTEM_RESET_RESET_PIN, + /* Software requested cold reset */ + SYSTEM_RESET_SOFT_COLD, + /* Software requested warm reset */ + SYSTEM_RESET_SOFT_WARM, + /* Watchdog timer reset */ + SYSTEM_RESET_WATCHDOG, + /* the RTC alarm triggered power on */ + SYSTEM_RESET_RTC_ALARM, + /* the Wake pin triggered power on */ + SYSTEM_RESET_WAKE_PIN, + /* the low battery detection triggered power on */ + SYSTEM_RESET_LOW_BATTERY, +}; + +/* System images */ +enum system_image_copy_t { + SYSTEM_IMAGE_UNKNOWN = 0, + SYSTEM_IMAGE_RO, + SYSTEM_IMAGE_RW_A, + SYSTEM_IMAGE_RW_B +}; + +/* Pre-initializes the module. This occurs before clocks or tasks are + * set up. */ +int system_pre_init(void); + +/* Initializes the system module. */ +int system_init(void); + +/* Returns the cause of the last reset, or SYSTEM_RESET_UNKNOWN if + * the cause is not known. */ +enum system_reset_cause_t system_get_reset_cause(void); + +/* Returns a text description of the last reset cause. */ +const char *system_get_reset_cause_string(void); + +/* Returns the image copy which is currently running. */ +enum system_image_copy_t system_get_image_copy(void); + +/* Returns a text description of the image copy which is currently running. */ +const char *system_get_image_copy_string(void); + +/* Jumps to the specified image copy. Only works from RO firmware. */ +int system_run_image_copy(enum system_image_copy_t copy); + +/* Returns the version string for an image copy, or an empty string if + * error. If copy==SYSTEM_IMAGE_UNKNOWN, returns the version for the + * currently-running image. */ +const char *system_get_version(enum system_image_copy_t copy); + +/* Resets the system. If is_cold!=0, performs a cold reset (which + * resets on-chip peripherals); else performs a warm reset (which does + * not reset on-chip peripherals). If successful, does not return. + * Returns error if the reboot type cannot be requested (e.g. brownout + * or reset pin). */ +int system_reset(int is_cold); + +/* Sets a scratchpad register to the specified value. The scratchpad + * register must maintain its contents across a software-requested + * warm reset. */ +int system_set_scratchpad(uint32_t value); + +/* Returns the current scratchpad register value. */ +uint32_t system_get_scratchpad(void); + +/* TODO: request sleep. How do we want to handle transitioning + * to/from low-power states? */ + +/* put the system in hibernation for the specified duration */ +void system_hibernate(uint32_t seconds, uint32_t microseconds); + +/* minimum duration to get proper hibernation */ +#define SYSTEM_HIB_MINIMUM_DURATION 0, 1000 + +#endif /* __CROS_EC_SYSTEM_H */ diff --git a/include/temp_sensor.h b/include/temp_sensor.h new file mode 100644 index 0000000000..863f9d7d51 --- /dev/null +++ b/include/temp_sensor.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2011 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. + */ + +/* Temperature sensor module for Chrome EC */ + +#ifndef __CROS_EC_TEMP_SENSOR_H +#define __CROS_EC_TEMP_SENSOR_H + +#include "common.h" + +enum temp_sensor_id { + TEMP_SENSOR_CASE = 0, /* Case temperature */ + TEMP_SENSOR_CASE_DIE, /* Case temperature sensor die */ + TEMP_SENSOR_EC_INTERNAL, /* EC internal temperature sensor */ +}; + +/* Initializes the module. */ +int temp_sensor_init(void); + +/* Returns the most recently measured temperature for the sensor in K, + * or -1 if error. */ +int temp_sensor_read(enum temp_sensor_id id); + +#endif /* __CROS_EC_TEMP_SENSOR_H */ diff --git a/include/uart.h b/include/uart.h new file mode 100644 index 0000000000..12ecf7a1a6 --- /dev/null +++ b/include/uart.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2011 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. + */ + +/* uart.h - UART module for Chrome EC */ + +#ifndef __CROS_EC_UART_H +#define __CROS_EC_UART_H + +#include "common.h" + + +/* Initializes the UART module. */ +int uart_init(void); + + +/* Enables console mode if <enable>!=0. In console mode: + * - Input is echoed + * - Input CRLF and CR are translated to LF + * - Input backspace will remove characters from the input buffer (which + * is pretty much only useful if the input handler is only triggered on + * newline) + * - Output LF is translated to CRLF */ +void uart_set_console_mode(int enable); + +/*****************************************************************************/ +/* Output functions + * + * Output is buffered. If the buffer overflows, subsequent output is + * discarded. */ + +/* Put a null-terminated string to the UART, like fputs(). + * + * Returns error if output was truncated. */ +int uart_puts(const char *outstr); + +/* Print formatted output to the UART, like printf(). + * + * Returns error if output was truncated. + * + * Must support format strings for: + * char (%c) + * string (%s) + * native int (signed/unsigned) (%d / %u / %x) + * int32_t / uint32_t (%d / %x) + * int64_t / uint64_t (%ld / %lu / %lx) + * pointer (%p) + * including padding (%-5s, %8d, %08x) + * + * Note: Floating point output (%f / %g) is not required. + */ +int uart_printf(const char *format, ...); + +/* Flushes output. Blocks until UART has transmitted all output. */ +void uart_flush_output(void); + +/* Flushes output. + * + * Blocks until UART has transmitted all output, + * even if we are in high priority interrupt context + */ +void uart_emergency_flush(void); + +/*****************************************************************************/ +/* Input functions + * + * Input is buffered. If the buffer overflows, the oldest input in + * the buffer is discarded to make room for the new input. + * + * Input lines may be terminated by CR ('\r'), LF ('\n'), or CRLF; all + * are translated to newline. */ + +/* Flushes input buffer, discarding all input. */ +void uart_flush_input(void); + +/* Non-destructively checks for a character in the input buffer. + * + * Returns the offset into the input buffer of character <c>, or -1 if + * it is not in the input buffer. */ +int uart_peek(int c); + +/* Reads a single character of input, similar to fgetc(). Returns the + * character, or -1 if no input waiting. */ +int uart_getc(void); + +/* Reads characters from the UART, similar to fgets(). + * + * Reads input until one of the following conditions is met: + * (1) <size-1> characters have been read. + * (2) A newline ('\n') has been read. + * (3) The input buffer is empty. + * + * Condition (3) means this call never blocks. This is important + * because it prevents a race condition where the caller calls + * UartPeek() to see if input is waiting, or is notified by the + * callack that input is waiting, but then the input buffer overflows + * or someone else grabs the input before UartGets() is called. + * + * Characters are stored in <dest> and are null-terminated. + * Characters include the newline if present, so that the caller can + * distinguish between a complete line and a truncated one. If the + * input buffer is empty, a null-terminated empty string ("") is + * returned. + * + * Returns the number of characters read (not counting the terminating + * null). */ +int uart_gets(char *dest, int size); + +/* TODO: getc(), putc() equivalents? */ + +#endif /* __CROS_EC_UART_H */ diff --git a/include/vboot.h b/include/vboot.h new file mode 100644 index 0000000000..430c617a52 --- /dev/null +++ b/include/vboot.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2011 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. + */ + +/* Verified boot module for Chrome EC */ + +#ifndef __CROS_EC_VBOOT_H +#define __CROS_EC_VBOOT_H + +#include "common.h" + +/* Pre-initializes the module. This occurs before clocks or tasks are + * set up. */ +int vboot_pre_init(void); + +/* Initializes the module. */ +int vboot_init(void); + +#endif /* __CROS_EC_VBOOT_H */ diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000000..0031339c34 --- /dev/null +++ b/include/version.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2011 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. + */ + +/* Version number for Chrome EC */ + +#ifndef __CROS_EC_VERSION_H +#define __CROS_EC_VERSION_H + +#define CROS_EC_VERSION_MAJOR 0 +#define CROS_EC_VERSION_MINOR 1 +#define CROS_EC_VERSION_SUBMINOR 2 +#define CROS_EC_VERSION_STRING "Link.0.1.2" + +#endif /* __CROS_EC_VERSION_H */ diff --git a/include/x86_power.h b/include/x86_power.h new file mode 100644 index 0000000000..3343b237e2 --- /dev/null +++ b/include/x86_power.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2011 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. + */ + +/* x86 power module for Chrome EC */ + +#ifndef __CROS_EC_X86_POWER_H +#define __CROS_EC_X86_POWER_H + +#include "common.h" + +/* Initializes the module. */ +int x86_power_init(void); + +#endif /* __CROS_EC_X86_POWER_H */ diff --git a/util/ectool.c b/util/ectool.c new file mode 100644 index 0000000000..42820a1f58 --- /dev/null +++ b/util/ectool.c @@ -0,0 +1,465 @@ +/* Copyright (c) 2011 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/io.h> +#include <unistd.h> + +#include "lpc_commands.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define MIN(a, b) (a < b ? a : b); + +#define EC_LPC_CMD_ADDR 0x66 +#define EC_LPC_DATA_ADDR 0x800 +#define EC_LPC_DATA_SIZE 512 + +const char help_str[] = + "Commands:\n" + " flashinfo\n" + " Prints information on the EC flash\n" + " flashread <offset> <size> <outfile>\n" + " Reads from EC flash to a file\n" + " flashwrite <offset> <infile>\n" + " Writes to EC flash from a file\n" + " flasherase <offset> <size>\n" + " Erases EC flash\n" + " hello\n" + " Checks for basic communication with EC\n" + " readtest <patternoffset> <size>\n" + " Reads a pattern from the EC via LPC\n" + " sertest\n" + " Serial output test for COM2\n" + " version\n" + " Prints EC version\n" + ""; + + + +/* Waits for the EC to be unbusy. Returns 0 if unbusy, non-zero if + * timeout. */ +int wait_for_ec(int timeout_usec) +{ + int i; + for (i = 0; i < timeout_usec; i += 10) { + usleep(10); /* Delay first, in case we just sent a command */ + if (!(inb(EC_LPC_CMD_ADDR) & EC_LPC_BUSY_MASK)) + return 0; + } + return -1; /* Timeout */ +} + + +/* Sends a command to the EC. Returns the command status code, or + * -1 if other error. */ +int ec_command(int command, const void *indata, int insize, + void *outdata, int outsize) { + uint8_t *d; + int i; + + outb(0x42, 0x80); + + if (insize > EC_LPC_DATA_SIZE || outsize > EC_LPC_DATA_SIZE) { + fprintf(stderr, "Data size too big\n"); + return -1; + } + + if (wait_for_ec(1000000)) { + fprintf(stderr, "Timeout waiting for EC ready\n"); + return -1; + } + + /* Write data, if any */ + /* TODO: optimized copy using outl() */ + for (i = 0, d = (uint8_t *)indata; i < insize; i++, d++) + outb(*d, EC_LPC_DATA_ADDR + i); + + outb(command, EC_LPC_CMD_ADDR); + + if (wait_for_ec(1000000)) { + fprintf(stderr, "Timeout waiting for EC response\n"); + return -1; + } + + /* Check status */ + i = inb(EC_LPC_CMD_ADDR); + i = EC_LPC_GET_STATUS(i); + if (i) { + fprintf(stderr, "EC returned error status %d\n", i); + return i; + } + + /* Read data, if any */ + for (i = 0, d = (uint8_t *)outdata; i < outsize; i++, d++) + *d = inb(EC_LPC_DATA_ADDR + i); + + return 0; +} + + +void print_help(const char *prog) +{ + printf("Usage: %s <command> [params]\n\n", prog); + puts(help_str); +} + + +int cmd_hello(void) +{ + struct lpc_params_hello p; + struct lpc_response_hello r; + int rv; + + p.in_data = 0xa0b0c0d0; + + rv = ec_command(EC_LPC_COMMAND_HELLO, &p, sizeof(p), &r, sizeof(r)); + if (rv) + return rv; + + if (r.out_data != 0xa1b2c3d4) { + fprintf(stderr, "Expected response 0x%08x, got 0x%08x\n", + 0xa1b2c3d4, r.out_data); + return -1; + } + + printf("EC says hello!\n"); + return 0; +} + + +int cmd_version(void) +{ + static const char * const fw_copies[] = {"unknown", "RO", "A", "B"}; + struct lpc_response_get_version r; + int rv; + + rv = ec_command(EC_LPC_COMMAND_GET_VERSION, NULL, 0, &r, sizeof(r)); + if (rv) + return rv; + + /* Ensure versions are null-terminated before we print them */ + r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0'; + r.version_string_rw_a[sizeof(r.version_string_rw_a) - 1] = '\0'; + r.version_string_rw_b[sizeof(r.version_string_rw_b) - 1] = '\0'; + + /* Print versions */ + printf("RO version: %s\n", r.version_string_ro); + printf("RW-A version: %s\n", r.version_string_rw_a); + printf("RW-B version: %s\n", r.version_string_rw_b); + printf("Firmware copy: %s\n", + (r.current_image < ARRAY_SIZE(fw_copies) ? + fw_copies[r.current_image] : "?")); + return 0; +} + + +int cmd_read_test(int argc, char *argv[]) +{ + struct lpc_params_read_test p; + struct lpc_response_read_test r; + int offset, size; + int errors = 0; + int rv; + int i; + char *e; + char *buf; + uint32_t *b; + + if (argc < 2) { + fprintf(stderr, "Usage: readtest <pattern_offset> <size>\n"); + return -1; + } + offset = strtol(argv[0], &e, 0); + size = strtol(argv[1], &e, 0); + if ((e && *e) || size <= 0 || size > 0x100000) { + fprintf(stderr, "Bad size.\n"); + return -1; + } + printf("Reading %d bytes with pattern offset 0x%x...\n", size, offset); + + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "Unable to allocate buffer.\n"); + return -1; + } + + /* Read data in chunks */ + for (i = 0; i < size; i += sizeof(r.data)) { + p.offset = offset + i / sizeof(uint32_t); + p.size = MIN(size - i, sizeof(r.data)); + rv = ec_command(EC_LPC_COMMAND_READ_TEST, &p, sizeof(p), + &r, sizeof(r)); + if (rv) { + fprintf(stderr, "Read error at offset %d\n", i); + free(buf); + return -1; + } + memcpy(buf + i, r.data, p.size); + } + + /* Check data */ + for (i = 0, b = (uint32_t *)buf; i < size / 4; i++, b++) { + if (*b != i + offset) { + printf("Mismatch at byte offset 0x%x: " + "expected 0x%08x, got 0x%08x\n", + (int)(i * sizeof(uint32_t)), i + offset, *b); + errors++; + } + } + + free(buf); + if (errors) { + printf("Found %d errors\n", errors); + return -1; + } + + printf("done.\n"); + return 0; +} + + +int cmd_flash_info(void) +{ + struct lpc_response_flash_info r; + int rv; + + rv = ec_command(EC_LPC_COMMAND_FLASH_INFO, NULL, 0, &r, sizeof(r)); + if (rv) + return rv; + + printf("FlashSize %d\nWriteSize %d\nEraseSize %d\nProtectSize %d\n", + r.flash_size, r.write_block_size, r.erase_block_size, + r.protect_block_size); + + return 0; +} + + +int cmd_flash_read(int argc, char *argv[]) +{ + struct lpc_params_flash_read p; + struct lpc_response_flash_read r; + int offset, size; + int rv; + int i; + char *e; + char *buf; + FILE *f; + + if (argc < 3) { + fprintf(stderr, + "Usage: flashread <offset> <size> <filename>\n"); + return -1; + } + offset = strtol(argv[0], &e, 0); + if ((e && *e) || offset < 0 || offset > 0x100000) { + fprintf(stderr, "Bad offset.\n"); + return -1; + } + size = strtol(argv[1], &e, 0); + if ((e && *e) || size <= 0 || size > 0x100000) { + fprintf(stderr, "Bad size.\n"); + return -1; + } + printf("Reading %d bytes at offset %d...\n", size, offset); + + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "Unable to allocate buffer.\n"); + return -1; + } + + /* Read data in chunks */ + for (i = 0; i < size; i += EC_LPC_FLASH_SIZE_MAX) { + p.offset = offset + i; + p.size = MIN(size - i, EC_LPC_FLASH_SIZE_MAX); + rv = ec_command(EC_LPC_COMMAND_FLASH_READ, + &p, sizeof(p), &r, sizeof(r)); + if (rv) { + fprintf(stderr, "Read error at offset %d\n", i); + free(buf); + return -1; + } + memcpy(buf + i, r.data, p.size); + } + + /* Write to file */ + f = fopen(argv[2], "wb"); + if (!f) { + perror("Error opening output file"); + free(buf); + return -1; + } + i = fwrite(buf, 1, size, f); + fclose(f); + free(buf); + if (i != size) { + perror("Error writing to file"); + return -1; + } + printf("done.\n"); + return 0; +} + + +int cmd_flash_write(int argc, char *argv[]) +{ + struct lpc_params_flash_write p; + int offset, size; + int rv; + int i; + char *e; + char *buf; + FILE *f; + + if (argc < 2) { + fprintf(stderr, "Usage: flashwrite <offset> <filename>\n"); + return -1; + } + offset = strtol(argv[0], &e, 0); + if ((e && *e) || offset < 0 || offset > 0x100000) { + fprintf(stderr, "Bad offset.\n"); + return -1; + } + + /* Read the input file */ + f = fopen(argv[1], "rb"); + if (!f) { + perror("Error opening input file"); + return -1; + } + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + if (size > 0x100000) { + fprintf(stderr, "File seems unreasonably large\n"); + fclose(f); + return -1; + } + + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "Unable to allocate buffer.\n"); + fclose(f); + return -1; + } + + printf("Reading %d bytes from %s...\n", size, argv[1]); + i = fread(buf, 1, size, f); + if (i != size) { + perror("Error reading file"); + free(buf); + return -1; + } + + printf("Writing to offset %d...\n", offset); + + /* Write data in chunks */ + for (i = 0; i < size; i += EC_LPC_FLASH_SIZE_MAX) { + p.offset = offset + i; + p.size = MIN(size - i, EC_LPC_FLASH_SIZE_MAX); + memcpy(p.data, buf + i, p.size); + rv = ec_command(EC_LPC_COMMAND_FLASH_WRITE, + &p, sizeof(p), NULL, 0); + if (rv) { + fprintf(stderr, "Write error at offset %d\n", i); + free(buf); + return -1; + } + } + + free(buf); + printf("done.\n"); + return 0; +} + + +int cmd_flash_erase(int argc, char *argv[]) +{ + struct lpc_params_flash_erase p; + char *e; + + if (argc < 2) { + fprintf(stderr, "Usage: flasherase <offset> <size>\n"); + return -1; + } + p.offset = strtol(argv[0], &e, 0); + if ((e && *e) || p.offset < 0 || p.offset > 0x100000) { + fprintf(stderr, "Bad offset.\n"); + return -1; + } + p.size = strtol(argv[1], &e, 0); + if ((e && *e) || p.size <= 0 || p.size > 0x100000) { + fprintf(stderr, "Bad size.\n"); + return -1; + } + + printf("Erasing %d bytes at offset %d...\n", p.size, p.offset); + if (ec_command(EC_LPC_COMMAND_FLASH_ERASE, &p, sizeof(p), NULL, 0)) + return -1; + + printf("done.\n"); + return 0; +} + + +int cmd_serial_test(int argc, char *argv[]) +{ + const char *c = "COM2 sample serial output from host!\r\n"; + + printf("Writing sample serial output to COM2\n"); + + while (*c) { + /* Wait for space in transmit FIFO */ + while (!(inb(0x2fd) & 0x20)) {} + + /* Put the next character */ + outb(*c++, 0x2f8); + } + + printf("done.\n"); + return 0; +} + + +int main(int argc, char *argv[]) +{ + if (argc < 2 || !strcasecmp(argv[1], "-?") || + !strcasecmp(argv[1], "help")) { + print_help(argv[0]); + return -2; + } + + /* Request I/O privilege */ + if (iopl(3) < 0) { + perror("Error getting I/O privilege"); + return -3; + } + + /* Handle commands */ + if (!strcasecmp(argv[1], "flashinfo")) + return cmd_flash_info(); + if (!strcasecmp(argv[1], "flashread")) + return cmd_flash_read(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "flashwrite")) + return cmd_flash_write(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "flasherase")) + return cmd_flash_erase(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "hello")) + return cmd_hello(); + if (!strcasecmp(argv[1], "readtest")) + return cmd_read_test(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "sertest")) + return cmd_serial_test(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "version")) + return cmd_version(); + + /* If we're still here, command was unknown */ + fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); + print_help(argv[0]); + return -2; +} |