diff options
-rw-r--r-- | board/cr50/board.c | 4 | ||||
-rw-r--r-- | board/cr50/board.h | 10 | ||||
-rw-r--r-- | board/cr50/build.mk | 1 | ||||
-rw-r--r-- | board/cr50/rdd.c | 54 | ||||
-rw-r--r-- | board/cr50/usb_i2c.c | 93 | ||||
-rw-r--r-- | chip/g/build.mk | 1 | ||||
-rw-r--r-- | chip/g/rdd.h | 6 | ||||
-rw-r--r-- | chip/g/usb_i2c.c | 114 | ||||
-rw-r--r-- | chip/g/usb_i2c.h | 152 |
9 files changed, 378 insertions, 57 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c index c4012d49a1..686387eefd 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -28,6 +28,7 @@ #include "usb_descriptor.h" #include "usb_hid.h" #include "usb_spi.h" +#include "usb_i2c.h" #include "util.h" /* Define interrupt and gpio structs */ @@ -325,6 +326,7 @@ const void * const usb_strings[] = { [USB_STR_UPGRADE_NAME] = USB_STRING_DESC("Firmware upgrade"), [USB_STR_SPI_NAME] = USB_STRING_DESC("AP EC upgrade"), [USB_STR_SERIALNO] = USB_STRING_DESC(DEFAULT_SERIALNO), + [USB_STR_I2C_NAME] = USB_STRING_DESC("I2C"), }; BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); #endif @@ -607,7 +609,7 @@ static void servo_attached(void) uartn_tx_disconnect(UART_EC); /* Disconnect i2cm interface to ina */ - ina_disconnect(); + usb_i2c_board_disable(0); } void device_state_on(enum gpio_signal signal) diff --git a/board/cr50/board.h b/board/cr50/board.h index 120928afce..ad8bb4faa6 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -60,8 +60,9 @@ /* USB configuration */ #define CONFIG_USB -#define CONFIG_USB_HID #define CONFIG_USB_CONSOLE +#define CONFIG_USB_HID +#define CONFIG_USB_I2C #define CONFIG_USB_INHIBIT_INIT #define CONFIG_USB_SELECT_PHY #define CONFIG_USB_SPI @@ -118,6 +119,7 @@ enum usb_strings { USB_STR_UPGRADE_NAME, USB_STR_SPI_NAME, USB_STR_SERIALNO, + USB_STR_I2C_NAME, USB_STR_COUNT }; @@ -161,7 +163,8 @@ int is_ec_rst_asserted(void); #define USB_IFACE_EC 3 #define USB_IFACE_UPGRADE 4 #define USB_IFACE_SPI 5 -#define USB_IFACE_COUNT 6 +#define USB_IFACE_I2C 6 +#define USB_IFACE_COUNT 7 /* USB endpoint indexes (use define rather than enum to expand them) */ #define USB_EP_CONTROL 0 @@ -171,7 +174,8 @@ int is_ec_rst_asserted(void); #define USB_EP_EC 4 #define USB_EP_UPGRADE 5 #define USB_EP_SPI 6 -#define USB_EP_COUNT 7 +#define USB_EP_I2C 7 +#define USB_EP_COUNT 8 /* UART indexes (use define rather than enum to expand them) */ #define UART_CR50 0 diff --git a/board/cr50/build.mk b/board/cr50/build.mk index 379e5e26de..48fb0c2a92 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -32,6 +32,7 @@ dirs-y += $(BDIR)/tpm2 board-y = board.o board-${CONFIG_RDD} += rdd.o board-${CONFIG_USB_SPI} += usb_spi.o +board-${CONFIG_USB_I2C} += usb_i2c.o board-y += tpm2/NVMem.o board-y += tpm2/aes.o board-y += tpm2/ecc.o diff --git a/board/cr50/rdd.c b/board/cr50/rdd.c index 9345210f2c..947808ede0 100644 --- a/board/cr50/rdd.c +++ b/board/cr50/rdd.c @@ -15,6 +15,7 @@ #include "system.h" #include "uartn.h" #include "usb_api.h" +#include "usb_i2c.h" #define CPRINTS(format, args...) cprints(CC_USB, format, ## args) @@ -95,45 +96,6 @@ void uartn_tx_disconnect(int uart) uart_select_tx(uart, 0); } -void ina_connect(void) -{ - /* Apply power to INA chips */ - gpio_set_level(GPIO_EN_PP3300_INA_L, 0); - /* Allow enough time for power rail to come up */ - usleep(25); - - /* - * Connect B0/B1 pads to I2C0 input SDA/SCL. Note, that the inputs - * for these pads are already enabled for the gpio signals I2C_SCL_INA - * and I2C_SDA_INA in gpio.inc. - */ - GWRITE(PINMUX, I2C0_SDA_SEL, GC_PINMUX_DIOB1_SEL); - GWRITE(PINMUX, I2C0_SCL_SEL, GC_PINMUX_DIOB0_SEL); - - /* Connect I2CS SDA/SCL output to B1/B0 pads */ - GWRITE(PINMUX, DIOB1_SEL, GC_PINMUX_I2C0_SDA_SEL); - GWRITE(PINMUX, DIOB0_SEL, GC_PINMUX_I2C0_SCL_SEL); - - /* - * Initialize the i2cm module after the INAs are powered and the signal - * lines are connected. - */ - i2cm_init(); -} - -void ina_disconnect(void) -{ - /* Disonnect I2C0 SDA/SCL output to B1/B0 pads */ - GWRITE(PINMUX, DIOB1_SEL, 0); - GWRITE(PINMUX, DIOB0_SEL, 0); - /* Disconnect B1/B0 pads to I2C0 input SDA/SCL */ - GWRITE(PINMUX, I2C0_SDA_SEL, 0); - GWRITE(PINMUX, I2C0_SCL_SEL, 0); - - /* Disable power to INA chips */ - gpio_set_level(GPIO_EN_PP3300_INA_L, 1); -} - void rdd_attached(void) { /* Indicate case-closed debug mode (active low) */ @@ -238,16 +200,16 @@ static int command_ccd(int argc, char **argv) ec_uart_enabled = 0; uartn_tx_disconnect(UART_EC); } - } else if (!strcasecmp("ina", argv[1]) && argc > 2) { + } else if (!strcasecmp("i2c", argv[1]) && argc > 2) { if (!parse_bool(argv[2], &val)) return EC_ERROR_PARAM2; if (val) { - ina_connect(); - ccprintf("CCD: INAs enabled\n"); + usb_i2c_board_enable(); + ccprintf("CCD: i2c enabled\n"); } else { - ina_disconnect(); - ccprintf("CCD: INAs disabled\n"); + usb_i2c_board_disable(0); + ccprintf("CCD: i2c disabled\n"); } } else if (argc == 2) { if (!parse_bool(argv[1], &val)) @@ -268,7 +230,7 @@ static int command_ccd(int argc, char **argv) return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(ccd, command_ccd, - "[uart|ina] [<BOOLEAN>]", + "[uart|i2c] [<BOOLEAN>]", "Get/set the case closed debug state"); static int command_sys_rst(int argc, char **argv) @@ -362,5 +324,3 @@ static int command_powerbtn(int argc, char **argv) DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn, "[pulse [ms] | press | release]", "get/set the state of the power button"); - - diff --git a/board/cr50/usb_i2c.c b/board/cr50/usb_i2c.c new file mode 100644 index 0000000000..8ba90c5f63 --- /dev/null +++ b/board/cr50/usb_i2c.c @@ -0,0 +1,93 @@ +/* Copyright 2016 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 "console.h" +#include "device_state.h" +#include "gpio.h" +#include "hooks.h" +#include "i2c.h" +#include "rdd.h" +#include "registers.h" +#include "system.h" +#include "timer.h" +#include "usb_i2c.h" + +#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) + +static int i2c_enabled(void) +{ + return !gpio_get_level(GPIO_EN_PP3300_INA_L); +} + +static void ina_disconnect(void) +{ + CPRINTS("Disabling I2C"); + + /* Disonnect I2C0 SDA/SCL output to B1/B0 pads */ + GWRITE(PINMUX, DIOB1_SEL, 0); + GWRITE(PINMUX, DIOB0_SEL, 0); + /* Disconnect B1/B0 pads to I2C0 input SDA/SCL */ + GWRITE(PINMUX, I2C0_SDA_SEL, 0); + GWRITE(PINMUX, I2C0_SCL_SEL, 0); + + /* Disable power to INA chips */ + gpio_set_level(GPIO_EN_PP3300_INA_L, 1); +} +DECLARE_DEFERRED(ina_disconnect); + +static void ina_connect(void) +{ + CPRINTS("Enabling I2C"); + + /* Apply power to INA chips */ + gpio_set_level(GPIO_EN_PP3300_INA_L, 0); + /* Allow enough time for power rail to come up */ + usleep(25); + + /* + * Connect B0/B1 pads to I2C0 input SDA/SCL. Note, that the inputs + * for these pads are already enabled for the gpio signals I2C_SCL_INA + * and I2C_SDA_INA in gpio.inc. + */ + GWRITE(PINMUX, I2C0_SDA_SEL, GC_PINMUX_DIOB1_SEL); + GWRITE(PINMUX, I2C0_SCL_SEL, GC_PINMUX_DIOB0_SEL); + + /* Connect I2CS SDA/SCL output to B1/B0 pads */ + GWRITE(PINMUX, DIOB1_SEL, GC_PINMUX_I2C0_SDA_SEL); + GWRITE(PINMUX, DIOB0_SEL, GC_PINMUX_I2C0_SCL_SEL); + + /* + * Initialize the i2cm module after the INAs are powered and the signal + * lines are connected. + */ + i2cm_init(); +} + +void usb_i2c_board_disable(int debounce) +{ + if (!i2c_enabled()) + return; + + /* + * Wait to disable i2c in case we are doing a bunch of i2c transactions + * in a row. + */ + hook_call_deferred(&ina_disconnect_data, debounce ? 1 * SECOND : 0); +} + +int usb_i2c_board_enable(void) +{ + if (device_get_state(DEVICE_SERVO) != DEVICE_STATE_OFF) { + CPRINTS("Servo is attached I2C cannot be enabled"); + usb_i2c_board_disable(0); + return EC_ERROR_BUSY; + } + + hook_call_deferred(&ina_disconnect_data, -1); + + if (!i2c_enabled()) + ina_connect(); + return EC_SUCCESS; +} diff --git a/chip/g/build.mk b/chip/g/build.mk index df028da65b..c6325f4884 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -56,6 +56,7 @@ chip-$(CONFIG_WATCHDOG)+=watchdog.o chip-$(CONFIG_USB)+=usb.o usb_endpoints.o chip-$(CONFIG_USB_CONSOLE)+=usb_console.o chip-$(CONFIG_USB_HID)+=usb_hid.o +chip-$(CONFIG_USB_I2C)+=usb_i2c.o chip-$(CONFIG_USB_BLOB)+=blob.o chip-$(CONFIG_USB_SPI)+=usb_spi.o chip-$(CONFIG_RDD)+=rdd.o diff --git a/chip/g/rdd.h b/chip/g/rdd.h index 45dbb049de..1f19ee9b3e 100644 --- a/chip/g/rdd.h +++ b/chip/g/rdd.h @@ -18,10 +18,4 @@ void rdd_attached(void); * cable is detached. */ int is_utmi_wakeup_allowed(void); - -/* Power up INAs and initialize I2C0 interface */ -void ina_connect(void); - -/* Disconnect I2C0 interface and powerdown INAs */ -void ina_disconnect(void); #endif /* __CROS_RDD_H */ diff --git a/chip/g/usb_i2c.c b/chip/g/usb_i2c.c new file mode 100644 index 0000000000..b3094d716b --- /dev/null +++ b/chip/g/usb_i2c.c @@ -0,0 +1,114 @@ +/* Copyright 2016 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 "common.h" +#include "link_defs.h" +#include "registers.h" +#include "i2c.h" +#include "usb_descriptor.h" +#include "util.h" + +#include "common.h" +#include "console.h" +#include "consumer.h" +#include "queue.h" +#include "queue_policies.h" +#include "producer.h" +#include "task.h" +#include "usb-stream.h" +#include "usb_i2c.h" + + +#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) + +USB_I2C_CONFIG(i2c, + USB_IFACE_I2C, + USB_STR_I2C_NAME, + USB_EP_I2C) + +static int16_t usb_i2c_map_error(int error) +{ + switch (error) { + case EC_SUCCESS: return USB_I2C_SUCCESS; + case EC_ERROR_TIMEOUT: return USB_I2C_TIMEOUT; + case EC_ERROR_BUSY: return USB_I2C_BUSY; + default: return USB_I2C_UNKNOWN_ERROR | (error & 0x7fff); + } +} + +static uint8_t usb_i2c_read_packet(struct usb_i2c_config const *config) +{ + return QUEUE_REMOVE_UNITS(config->consumer.queue, config->buffer, + queue_count(config->consumer.queue)); +} + +static void usb_i2c_write_packet(struct usb_i2c_config const *config, + uint8_t count) +{ + QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count); +} + +void usb_i2c_deferred(struct usb_i2c_config const *config) +{ + /* + * And if there is a USB packet waiting we process it and generate a + * response. + */ + uint8_t count = usb_i2c_read_packet(config); + int portindex = (config->buffer[0] >> 0) & 0xff; + uint8_t slave_addr = (config->buffer[0] >> 8) & 0xff; + int write_count = (config->buffer[1] >> 0) & 0xff; + int read_count = (config->buffer[1] >> 8) & 0xff; + int port; + int rv; + + config->buffer[0] = 0; + config->buffer[1] = 0; + + if (!count || (!read_count && !write_count)) + return; + + if (write_count > USB_I2C_MAX_WRITE_COUNT || + write_count != (count - 4)) { + config->buffer[0] = USB_I2C_WRITE_COUNT_INVALID; + } else if (read_count > USB_I2C_MAX_READ_COUNT) { + config->buffer[0] = USB_I2C_READ_COUNT_INVALID; + } else if (portindex >= i2c_ports_used) { + config->buffer[0] = USB_I2C_PORT_INVALID; + } else { + rv = usb_i2c_board_enable(); + if (rv) { + config->buffer[0] = usb_i2c_map_error(rv); + } else { + port = i2c_ports[portindex].port; + config->buffer[0] = usb_i2c_map_error( + i2c_xfer(port, slave_addr, + (uint8_t *)(config->buffer + 2), + write_count, + (uint8_t *)(config->buffer + 2), + read_count, I2C_XFER_SINGLE)); + usb_i2c_board_disable(1); + } + } + + usb_i2c_write_packet(config, read_count + 4); +} + +static void usb_i2c_written(struct consumer const *consumer, size_t count) +{ + struct usb_i2c_config const *config = + DOWNCAST(consumer, struct usb_i2c_config, consumer); + + hook_call_deferred(config->deferred, 0); +} + +static void usb_i2c_flush(struct consumer const *consumer) +{ +} + +struct consumer_ops const usb_i2c_consumer_ops = { + .written = usb_i2c_written, + .flush = usb_i2c_flush, +}; diff --git a/chip/g/usb_i2c.h b/chip/g/usb_i2c.h new file mode 100644 index 0000000000..401b20a0c5 --- /dev/null +++ b/chip/g/usb_i2c.h @@ -0,0 +1,152 @@ +/* Copyright 2016 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 "consumer.h" +#include "producer.h" +#include "registers.h" +#include "task.h" +#include "usb_descriptor.h" + +#ifndef __CROS_USB_I2C_H +#define __CROS_USB_I2C_H + +/* + * Command: + * +----------+-----------+---------------+---------------+---------------+ + * | port: 1B | addr: 1B | wr count : 1B | rd count : 1B | data : <= 60B | + * +----------+-----------+---------------+---------------+---------------+ + * + * port address: 1 byte, i2c interface index + * + * slave address: 1 byte, i2c 7-bit bus address + * + * write count: 1 byte, zero based count of bytes to write + * + * read count: 1 byte, zero based count of bytes to read + * + * data: write payload up to 60 bytes of data to write, + * length must match write count + * + * Response: + * +-------------+---+---+-----------------------+ + * | status : 2B | 0 | 0 | read payload : <= 60B | + * +-------------+---+---+-----------------------+ + * + * status: 2 byte status + * 0x0000: Success + * 0x0001: I2C timeout + * 0x0002: Busy, try again + * This can happen if someone else has acquired the shared memory + * buffer that the I2C driver uses as /dev/null + * 0x0003: Write count invalid (> 60 bytes, or mismatch with payload) + * 0x0004: Read count invalid (> 60 bytes) + * 0x0005: The port specified is invalid. + * 0x8000: Unknown error mask + * The bottom 15 bits will contain the bottom 15 bits from the EC + * error code. + * + * read payload: up to 60 bytes of data read from I2C, length will match + * requested read count + */ + +enum usb_i2c_error { + USB_I2C_SUCCESS = 0x0000, + USB_I2C_TIMEOUT = 0x0001, + USB_I2C_BUSY = 0x0002, + USB_I2C_WRITE_COUNT_INVALID = 0x0003, + USB_I2C_READ_COUNT_INVALID = 0x0004, + USB_I2C_PORT_INVALID = 0x0005, + USB_I2C_UNKNOWN_ERROR = 0x8000, +}; + + +#define USB_I2C_MAX_WRITE_COUNT 60 +#define USB_I2C_MAX_READ_COUNT 60 + +BUILD_ASSERT(USB_MAX_PACKET_SIZE == (1 + 1 + 1 + 1 + USB_I2C_MAX_WRITE_COUNT)); +BUILD_ASSERT(USB_MAX_PACKET_SIZE == (2 + 1 + 1 + USB_I2C_MAX_READ_COUNT)); + +/* + * Compile time Per-USB gpio configuration stored in flash. Instances of this + * structure are provided by the user of the USB i2c. This structure binds + * together all information required to operate a USB i2c. + */ +struct usb_i2c_config { + uint16_t *buffer; + + /* Deferred function to call to handle SPI request. */ + const struct deferred_data *deferred; + + struct consumer const consumer; + struct queue const *tx_queue; +}; + +extern struct consumer_ops const usb_i2c_consumer_ops; + +/* + * Convenience macro for defining a USB I2C bridge driver. + * + * NAME is used to construct the names of the trampoline functions and the + * usb_i2c_config struct, the latter is just called NAME. + * + * INTERFACE is the index of the USB interface to associate with this + * I2C driver. + * + * INTERFACE_NAME is the index of the USB string descriptor (iInterface). + * + * ENDPOINT is the index of the USB bulk endpoint used for receiving and + * transmitting bytes. + */ +#define USB_I2C_CONFIG(NAME, \ + INTERFACE, \ + INTERFACE_NAME, \ + ENDPOINT) \ + static uint16_t \ + CONCAT2(NAME, _buffer_)[USB_MAX_PACKET_SIZE/2]; \ + static void CONCAT2(NAME, _deferred_)(void); \ + DECLARE_DEFERRED(CONCAT2(NAME, _deferred_)); \ + static struct queue const CONCAT2(NAME, _to_usb_); \ + static struct queue const CONCAT3(usb_to_, NAME, _); \ + USB_STREAM_CONFIG_FULL(CONCAT2(NAME, _usb_), \ + INTERFACE, \ + USB_CLASS_VENDOR_SPEC, \ + USB_SUBCLASS_GOOGLE_I2C, \ + USB_PROTOCOL_GOOGLE_I2C, \ + INTERFACE_NAME, \ + ENDPOINT, \ + USB_MAX_PACKET_SIZE, \ + USB_MAX_PACKET_SIZE, \ + CONCAT3(usb_to_, NAME, _), \ + CONCAT2(NAME, _to_usb_)) \ + struct usb_i2c_config const NAME = { \ + .buffer = CONCAT2(NAME, _buffer_), \ + .deferred = &CONCAT2(NAME, _deferred__data), \ + .consumer = { \ + .queue = &CONCAT3(usb_to_, NAME, _), \ + .ops = &usb_i2c_consumer_ops, \ + }, \ + .tx_queue = &CONCAT2(NAME, _to_usb_), \ + }; \ + static struct queue const CONCAT2(NAME, _to_usb_) = \ + QUEUE_DIRECT(USB_MAX_PACKET_SIZE, uint8_t, \ + null_producer, CONCAT2(NAME, _usb_).consumer); \ + static struct queue const CONCAT3(usb_to_, NAME, _) = \ + QUEUE_DIRECT(USB_MAX_PACKET_SIZE, uint8_t, \ + CONCAT2(NAME, _usb_).producer, NAME.consumer); \ + static void CONCAT2(NAME, _deferred_)(void) \ + { usb_i2c_deferred(&NAME); } + +/* + * Handle I2C request in a deferred callback. + */ +void usb_i2c_deferred(struct usb_i2c_config const *config); + +/* + * These functions should be implemented by the board to provide any board + * specific operations required to enable or disable access to the I2C device. + */ +int usb_i2c_board_enable(void); +void usb_i2c_board_disable(int debounce); +#endif /* __CROS_USB_I2C_H */ |