Merge "Use common host command processing for Daisy I2C"

This commit is contained in:
Gerrit
2012-05-21 09:10:59 -07:00
committed by Gerrit Code Review
13 changed files with 123 additions and 277 deletions

View File

@@ -17,6 +17,9 @@
/* support programming on-chip flash */
#define CONFIG_FLASH
/* use I2C for host communication */
#define CONFIG_I2C
/* By default, enable all console messages except keyboard */
#define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_KEYSCAN))

View File

@@ -18,5 +18,4 @@
TASK(KEYSCAN, keyboard_scan_task, NULL) \
TASK(GAIAPOWER, gaia_power_task, NULL) \
TASK(CONSOLE, console_task, NULL) \
TASK(SPI_WORK, spi_work_task, NULL) \
TASK(I2C2_WORK, i2c2_work_task, NULL)
TASK(HOSTCMD, host_command_task, NULL)

View File

@@ -14,6 +14,9 @@
/* Use USART1 as console serial port */
#define CONFIG_CONSOLE_UART 1
/* use I2C for host communication */
#define CONFIG_I2C
#define USB_CHARGE_PORT_COUNT 0
/* EC drives 13 outputs to keyboard matrix */

View File

@@ -18,5 +18,4 @@
TASK(KEYSCAN, keyboard_scan_task, NULL) \
TASK(GAIAPOWER, gaia_power_task, NULL) \
TASK(CONSOLE, console_task, NULL) \
TASK(SPI_WORK, spi_work_task, NULL) \
TASK(I2C2_WORK, i2c2_work_task, NULL)
TASK(HOSTCMD, host_command_task, NULL)

View File

@@ -11,8 +11,8 @@ CORE:=cortex-m
chip-y=dma.o gpio.o hwtimer.o system.o uart.o
chip-y+=jtag-$(CHIP_VARIANT).o clock-$(CHIP_VARIANT).o gpio-$(CHIP_VARIANT).o
chip-$(CONFIG_TASK_SPI_WORK)+=spi.o
chip-$(CONFIG_TASK_I2C2_WORK)+=i2c.o
chip-$(CONFIG_SPI)+=spi.o
chip-$(CONFIG_I2C)+=i2c.o
chip-$(CONFIG_TASK_WATCHDOG)+=watchdog.o
chip-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o
chip-$(CONFIG_FLASH)+=flash-$(CHIP_VARIANT).o

View File

@@ -6,12 +6,14 @@
#include "board.h"
#include "common.h"
#include "console.h"
#include "ec_commands.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "i2c.h"
#include "message.h"
#include "registers.h"
#include "task.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_I2C, outstr)
@@ -30,35 +32,15 @@
#define I2C1 STM32_I2C1_PORT
#define I2C2 STM32_I2C2_PORT
static task_id_t task_waiting_on_port[NUM_PORTS];
static struct mutex port_mutex[NUM_PORTS];
static uint16_t i2c_sr1[NUM_PORTS];
/* per-transaction counters */
static unsigned int tx_byte_count;
static unsigned int rx_byte_count;
/* buffer for host commands (including error code and checksum) */
static uint8_t host_buffer[EC_PARAM_SIZE + 2];
/*
* i2c_xmit_mode determines what EC sends when AP initiates a
* read transaction. If AP has not set a transmit mode, then
* default to NOP.
*/
static enum message_cmd_t i2c_xmit_mode[NUM_PORTS] = { CMDC_NOP, CMDC_NOP };
/* current position in host buffer for reception */
static int rx_index;
/*
* Our output buffers. These must be large enough for our largest message,
* including protocol overhead.
*/
static uint8_t out_msg[32];
static void wait_rx(int port)
{
/* TODO: Add timeouts and error checking for safety */
while (!(STM32_I2C_SR1(port) & (1 << 6)))
;
}
static void wait_tx(int port)
{
@@ -67,71 +49,59 @@ static void wait_tx(int port)
;
}
static int i2c_read_raw(int port, void *buf, int len)
{
int i;
uint8_t *data = buf;
mutex_lock(&port_mutex[port]);
rx_byte_count = 0;
for (i = 0; i < len; i++) {
wait_rx(port);
data[i] = STM32_I2C_DR(port);
rx_byte_count++;
}
mutex_unlock(&port_mutex[port]);
return len;
}
static int i2c_write_raw(int port, void *buf, int len)
{
int i;
uint8_t *data = buf;
mutex_lock(&port_mutex[port]);
tx_byte_count = 0;
for (i = 0; i < len; i++) {
tx_byte_count++;
STM32_I2C_DR(port) = data[i];
wait_tx(port);
}
mutex_unlock(&port_mutex[port]);
return len;
}
void i2c2_work_task(void)
static void _send_result(int slot, int result, int size)
{
int msg_len;
uint16_t tmp16;
task_waiting_on_port[1] = task_get_current();
int i;
int len = 1;
uint8_t sum = 0;
while (1) {
task_wait_event(-1);
tmp16 = i2c_sr1[I2C2];
if (tmp16 & (1 << 6)) {
/* RxNE; AP issued write command */
i2c_read_raw(I2C2, &i2c_xmit_mode[I2C2], 1);
#ifdef CONFIG_DEBUG
CPRINTF("%s: i2c2_xmit_mode: %02x\n",
__func__, i2c_xmit_mode[I2C2]);
#endif
} else if (tmp16 & (1 << 7)) {
/* RxE; AP is waiting for EC response */
msg_len = message_process_cmd(i2c_xmit_mode[I2C2],
out_msg, sizeof(out_msg));
if (msg_len > 0) {
i2c_write_raw(I2C2, out_msg, msg_len);
} else {
CPRINTF("%s: unexpected mode %u\n",
__func__, i2c_xmit_mode[I2C2]);
}
/* reset EC mode to NOP after transfer is finished */
i2c_xmit_mode[I2C2] = CMDC_NOP;
}
ASSERT(slot == 0);
/* record the error code */
host_buffer[0] = result;
if (size) {
/* compute checksum */
for (i = 1; i <= size; i++)
sum += host_buffer[i];
host_buffer[size + 1] = sum;
len = size + 2;
}
/* send the answer to the AP */
i2c_write_raw(I2C2, host_buffer, len);
}
void host_send_result(int slot, int result)
{
_send_result(slot, result, 0);
}
void host_send_response(int slot, const uint8_t *data, int size)
{
uint8_t *out = host_get_buffer(slot);
if (data != out)
memcpy(out, data, size);
_send_result(slot, EC_RES_SUCCESS, size);
}
uint8_t *host_get_buffer(int slot)
{
ASSERT(slot == 0);
return host_buffer + 1 /* skip room for error code */;
}
static void i2c_event_handler(int port)
@@ -146,32 +116,35 @@ static void i2c_event_handler(int port)
/* cleared by reading SR1 followed by reading SR2 */
STM32_I2C_SR1(port);
STM32_I2C_SR2(port);
#ifdef CONFIG_DEBUG
CPRINTF("%s: ADDR\n", __func__);
#endif
} else if (i2c_sr1[port] & (1 << 2)) {
;
#ifdef CONFIG_DEBUG
CPRINTF("%s: BTF\n", __func__);
#endif
} else if (i2c_sr1[port] & (1 << 4)) {
/* clear STOPF bit by reading SR1 and then writing CR1 */
STM32_I2C_SR1(port);
STM32_I2C_CR1(port) = STM32_I2C_CR1(port);
#ifdef CONFIG_DEBUG
CPRINTF("%s: STOPF\n", __func__);
#endif
} else {
;
#ifdef CONFIG_DEBUG
CPRINTF("%s: unknown event\n", __func__);
#endif
}
/* RxNE or TxE, wake the worker task */
if (i2c_sr1[port] & ((1 << 6) | (1 << 7))) {
if (port == I2C2)
task_wake(TASK_ID_I2C2_WORK);
/* RxNE event */
if (i2c_sr1[port] & (1 << 6)) {
if (port == I2C2) { /* AP issued write command */
if (rx_index >= sizeof(host_buffer) - 1) {
rx_index = 0;
CPRINTF("I2C message too large\n");
}
host_buffer[rx_index++] = STM32_I2C_DR(I2C2);
}
}
/* TxE event */
if (i2c_sr1[port] & (1 << 7)) {
if (port == I2C2) { /* AP is waiting for EC response */
if (rx_index) {
/* we have an available command : execute it */
host_command_received(0, host_buffer[0]);
/* reset host buffer after end of transfer */
rx_index = 0;
} else {
/* spurious read : return dummy value */
STM32_I2C_DR(port) = 0xec;
}
}
}
}
static void i2c2_event_interrupt(void) { i2c_event_handler(I2C2); }
@@ -185,13 +158,12 @@ static void i2c_error_handler(int port)
if (i2c_sr1[port] & 1 << 10) {
/* ACK failed (NACK); expected when AP reads final byte.
* Software must clear AF bit. */
CPRINTF("%s: AF detected\n", __func__);
}
CPRINTF("%s: tx byte count: %u, rx_byte_count: %u\n",
__func__, tx_byte_count, rx_byte_count);
CPRINTF("%s: I2C_SR1(%s): 0x%04x\n", __func__, port, i2c_sr1[port]);
CPRINTF("%s: I2C_SR2(%s): 0x%04x\n",
} else {
CPRINTF("%s: I2C_SR1(%s): 0x%04x\n",
__func__, port, i2c_sr1[port]);
CPRINTF("%s: I2C_SR2(%s): 0x%04x\n",
__func__, port, STM32_I2C_SR2(port));
}
#endif
STM32_I2C_SR1(port) &= ~0xdf00;
@@ -201,8 +173,6 @@ DECLARE_IRQ(STM32_IRQ_I2C2_ER, i2c2_error_interrupt, 2);
static int i2c_init2(void)
{
int i;
/* enable I2C2 clock */
STM32_RCC_APB1ENR |= 1 << 22;
@@ -220,10 +190,6 @@ static int i2c_init2(void)
/* clear status */
STM32_I2C_SR1(I2C2) = 0;
/* No tasks are waiting on ports */
for (i = 0; i < NUM_PORTS; i++)
task_waiting_on_port[i] = TASK_ID_INVALID;
/* enable event and error interrupts */
task_enable_irq(STM32_IRQ_I2C2_EV);
task_enable_irq(STM32_IRQ_I2C2_ER);

View File

@@ -13,6 +13,7 @@
#include "board.h"
#include "console.h"
#include "gpio.h"
#include "host_command.h"
#include "keyboard.h"
#include "keyboard_scan.h"
#include "registers.h"
@@ -40,9 +41,6 @@ enum COL_INDEX {
/* The keyboard state from the last read */
static uint8_t raw_state[KB_OUTPUTS];
/* The keyboard state we will return when requested */
static uint8_t saved_state[KB_OUTPUTS];
/* Mask with 1 bits only for keys that actually exist */
static const uint8_t *actual_key_mask;
@@ -386,9 +384,23 @@ int keyboard_scan_recovery_pressed(void)
return 0;
}
int keyboard_get_scan(uint8_t **buffp, int max_bytes)
static int keyboard_get_scan(uint8_t *data, int *resp_size)
{
kb_fifo_remove(saved_state);
*buffp = saved_state;
return KB_OUTPUTS;
kb_fifo_remove(data);
*resp_size = KB_OUTPUTS;
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_MKBP_STATE, keyboard_get_scan);
static int keyboard_get_info(uint8_t *data, int *resp_size)
{
struct ec_response_mkbp_info *r = (struct ec_response_mkbp_info *)data;
r->rows = 8;
r->cols = KB_OUTPUTS;
*resp_size = sizeof(struct ec_response_mkbp_info);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_MKBP_INFO, keyboard_get_info);

View File

@@ -6,7 +6,7 @@
# Common files build
#
common-y=main.o message.o util.o console.o uart_buffering.o
common-y=main.o util.o console.o uart_buffering.o
common-y+=memory_commands.o shared_mem.o system_common.o hooks.o
common-y+=gpio_commands.o version.o
common-$(CONFIG_BATTERY_ATL706486)+=battery_atl706486.o

View File

@@ -106,6 +106,7 @@ static int host_command_read_test(uint8_t *data, int *resp_size)
DECLARE_HOST_COMMAND(EC_CMD_READ_TEST, host_command_read_test);
#ifdef CONFIG_LPC
/* ACPI query event handler. Note that the returned value is NOT actually
* an EC_RES enum; it's 0 if no event was pending, or the 1-based
* index of the lowest bit which was set. */
@@ -126,6 +127,7 @@ static int host_command_acpi_query_event(uint8_t *data, int *resp_size)
}
DECLARE_HOST_COMMAND(EC_CMD_ACPI_QUERY_EVENT,
host_command_acpi_query_event);
#endif
/* Finds a command by command number. Returns the command structure, or NULL if

View File

@@ -1,93 +0,0 @@
/*
* This handles incoming commands and provides responses.
*
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* EC <--> AP message handling.
*/
#include "board.h"
#include "message.h"
#include "keyboard_scan.h"
#include "util.h"
/* EC ID */
/* (TODO(dhendrix): Define this in board-specific code */
static const char ec_id[] = "Google Chrome EC";
/* Protocol version (least significant byte in lowest byte position) */
static const uint8_t proto_ver[] = { 1, 0, 0, 0 };
/**
* Get the response to a given command
*
* @param cmd Command byte to respond to
* @param buffp Buffer to use for data, or it can be updated to
* point to a new buffer
* @param max_len Number of bytes free at *buffp for the response.
* If the supplied buffer is used, this size must not
* be exceeded.
* @return number of bytes in response (which is in *buffp), or -1 on error
*/
static int message_get_response(int cmd, uint8_t **buffp, int max_len)
{
/*
* Invalid commands are ignored, just returning a stream of 0xff
* bytes.
*/
switch (cmd) {
case CMDC_PROTO_VER:
*buffp = (uint8_t *)proto_ver;
return sizeof(proto_ver);
case CMDC_NOP:
return 0;
case CMDC_ID:
*buffp = (char *)ec_id;
return sizeof(ec_id) - 1;
#ifdef CONFIG_TASK_KEYSCAN
case CMDC_KEY_STATE:
return keyboard_get_scan(buffp, max_len);
#endif
default:
return -1;
}
return 0;
}
int message_process_cmd(int cmd, uint8_t *out_msg, int max_len)
{
uint8_t *msg;
int msg_len;
int need_copy;
int sum = 0;
int i;
msg = out_msg;
msg_len = message_get_response(cmd, &msg, max_len - MSG_PROTO_BYTES);
if (msg_len < 0)
return msg_len;
/*
* We add MSG_PROTO_BYTES bytes of overhead: truncate the reply
* if needed.
*/
if (msg_len + MSG_PROTO_BYTES > max_len)
msg_len = max_len - MSG_PROTO_BYTES;
ASSERT(msg_len >= 0 && msg_len < 0xffff);
need_copy = msg != out_msg;
ASSERT(!need_copy ||
msg + msg_len < out_msg ||
msg > out_msg + sizeof(out_msg));
for (i = 0; i < msg_len; i++) {
if (need_copy)
out_msg[i] = msg[i];
sum += msg[i];
}
out_msg[i] = sum;
return msg_len + MSG_PROTO_BYTES;
}

View File

@@ -8,6 +8,25 @@
#ifndef __CROS_EC_COMMANDS_H
#define __CROS_EC_COMMANDS_H
/* Protocol overview
*
* request: CMD [ P0 P1 P2 ... Pn S ]
* response: ERR [ P0 P1 P2 ... Pn S ]
*
* where the bytes are defined as follow :
* - CMD is the command code. (defined by EC_CMD_ constants)
* - ERR is the error code. (defined by EC_RES_ constants)
* - Px is the optional payload.
* it is not sent if the error code is not success.
* (defined by ec_params_ and ec_response_ structures)
* - S is the checksum which is the sum of all payload bytes.
*
* On LPC, CMD and ERR are sent/received at EC_LPC_ADDR_KERNEL|USER_CMD
* and the payloads are sent/received at EC_LPC_ADDR_KERNEL|USER_PARAM.
* On I2C, all bytes are sent serially in the same message.
*/
/* During the development stage, the LPC bus has high error bit rate.
* Using checksum can detect the error and trigger re-transmit.
* FIXME: remove this after mass production.

View File

@@ -16,21 +16,6 @@ int keyboard_scan_init(void);
/* Returns non-zero if recovery key was pressed at boot. */
int keyboard_scan_recovery_pressed(void);
/**
* Get the scan data from the keyboard.
*
* This returns the results of the last keyboard scan, by pointing the
* supplied buffer to it, and returning the number of bytes available.
*
* The supplied buffer can be used directly if required, but in that case
* the number of bytes available is limited to 'max_bytes'.
*
* @param buffp Pointer to buffer to contain data
* @param max_bytes Maximum number of bytes available in *buffp
* @return number of bytes available, or -1 for error
*/
int keyboard_get_scan(uint8_t **buffp, int max_bytes);
/* clear any saved keyboard state (empty FIFO, etc) */
void keyboard_clear_state(void);

View File

@@ -1,49 +0,0 @@
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef __MESSAGE_IF_H
#define __MESSAGE_IF_H
#include <common.h>
/* Command interface between EC and AP */
enum {
MSG_TRAILER_BYTES = 1,
MSG_PROTO_BYTES = MSG_TRAILER_BYTES,
};
/* The command codes that we understand */
enum message_cmd_t {
/* EC control/status messages */
CMDC_PROTO_VER = 0x00, /* Protocol version */
CMDC_NOP, /* No operation / ping */
CMDC_ID, /* Read EC ID */
/* Functional messages */
CMDC_KEY_STATE = 0x20, /* Read key state */
};
/**
* Process a command received and return the response
*
* There is no time to compute a reply. The data should be ready
* immediately. This function can be called in interrupt context.
*
* The format of a reply is a sequence of bytes:
*
* <msg bytes> <sum>
*
* The checksum is calculated as the sum of all message bytes
*
* @param cmd Command to process (CMD_...)
* @param buff Pointer to buffer to store reponse
* @param max_len Maximum length of buffer
* @return number of bytes in reply, 0 if none, -1 for unknown command
*/
int message_process_cmd(int cmd, uint8_t *buff, int max_len);
#endif