usb_i2c: Fail if board I2C bridge is disabled

Add usb_i2c_board_is_enabled().

On Cr50, this is now also connected to the I2C CCD capability.  The
USB-I2C bridge can only be used when the capability is available.

On other platforms (Servo V4, etc.) where usb_i2c_board_enable() is
a no-op, add a dummy implementation which always returns true.

See go/cr50-ccd-wp for more information.

BUG=b:62537474
BRANCH=cr50
TEST=manual with CR50_DEV=1
	Connect host PC to dev board USB port
	On host PC:
		sudo servod -c ccd_cr50.xml -c reef_r1_inas.xml
		dut-control pp3300_ec_shv_reg --> fail, error 0x8001

	ccdoops --> reset I2C config
	ccd i2c disable --> I2C disabled

	On host PC:
		sudo servod -c ccd_cr50.xml -c reef_r1_inas.xml
		dut-control pp3300_ec_shv_reg --> fail, error 0x0006

	ccd i2c enable --> I2C enabled
	ccdunlock --> I2C disabled
	ccdoops --> I2C enabled
	ccdset i2c unlesslocked
	ccdlock --> I2C disabled
	ccdunlock --> I2C enabled

Change-Id: Ia3df32e239a5f7c5915bc6c7e408ce0dc8b26c89
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/590577
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
This commit is contained in:
Randall Spangler
2017-07-25 16:22:27 -07:00
committed by chrome-bot
parent ddbfe690e2
commit 00ea73ab16
11 changed files with 84 additions and 6 deletions

View File

@@ -289,6 +289,8 @@ static int command_ccd(int argc, char **argv)
ccprintf("AP UART: %s\nEC UART: %s\n",
uart_tx_is_connected(UART_AP) ? " enabled" : "disabled",
uart_tx_is_connected(UART_EC) ? " enabled" : "disabled");
ccprintf("I2C: %s\n",
usb_i2c_board_is_enabled() ? " enabled" : "disabled");
return EC_SUCCESS;
}

View File

@@ -3,6 +3,7 @@
* found in the LICENSE file.
*/
#include "case_closed_debug.h"
#include "console.h"
#include "device_state.h"
#include "gpio.h"
@@ -16,7 +17,7 @@
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
static int i2c_enabled(void)
int usb_i2c_board_is_enabled(void)
{
return !gpio_get_level(GPIO_EN_PP3300_INA_L);
}
@@ -64,7 +65,7 @@ static void ina_connect(void)
void usb_i2c_board_disable(void)
{
if (!i2c_enabled())
if (!usb_i2c_board_is_enabled())
return;
ina_disconnect();
@@ -78,8 +79,41 @@ int usb_i2c_board_enable(void)
return EC_ERROR_BUSY;
}
if (!i2c_enabled())
if (ccd_get_mode() != CCD_MODE_ENABLED)
return EC_ERROR_BUSY;
if (!ccd_is_cap_enabled(CCD_CAP_I2C))
return EC_ERROR_ACCESS_DENIED;
if (!usb_i2c_board_is_enabled())
ina_connect();
return EC_SUCCESS;
}
/**
* CCD config change hook
*/
static void ccd_change_i2c(void)
{
/*
* If the capability state doesn't match the current I2C enable state,
* try to make them match.
*/
if (usb_i2c_board_is_enabled() && !ccd_is_cap_enabled(CCD_CAP_I2C)) {
/* I2C bridge is enabled, but it's no longer allowed to be */
usb_i2c_board_disable();
} else if (!usb_i2c_board_is_enabled() &&
ccd_is_cap_enabled(CCD_CAP_I2C)) {
/*
* I2C bridge is disabled, but is allowed to be enabled. Try
* enabling it. Note that this could fail for several reasons,
* such as CCD not connected, or servo attached. That's ok;
* those things will also attempt usb_i2c_board_enable() if
* their state changes later.
*/
usb_i2c_board_enable();
}
}
DECLARE_HOOK(HOOK_CCD_CHANGE, ccd_change_i2c, HOOK_PRIO_DEFAULT);

View File

@@ -80,6 +80,7 @@ BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
int usb_i2c_board_enable(void) { return EC_SUCCESS; }
void usb_i2c_board_disable(void) {}
int usb_i2c_board_is_enabled(void) { return 1; }
#ifdef CONFIG_KEYBOARD_BOARD_CONFIG
struct keyboard_scan_config keyscan_config = {

View File

@@ -103,6 +103,11 @@ int usb_i2c_board_enable(void)
return EC_SUCCESS;
}
int usb_i2c_board_is_enabled(void)
{
return 1;
}
/* Initialize board. */
static void board_init(void)
{

View File

@@ -244,6 +244,8 @@ const struct i2c_port_t i2c_ports[] = {
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
int usb_i2c_board_is_enabled(void) { return 1; }
/******************************************************************************
* Initialize board.
*/

View File

@@ -238,6 +238,7 @@ const struct i2c_port_t i2c_ports[] = {
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
int usb_i2c_board_is_enabled(void) { return 1; }
/******************************************************************************
* Initialize board.

View File

@@ -111,7 +111,7 @@ const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
int usb_i2c_board_enable(void) {return EC_SUCCESS; }
void usb_i2c_board_disable(void) {}
int usb_i2c_board_is_enabled(void) { return 1; }
/******************************************************************************
* Console commands.

View File

@@ -136,6 +136,7 @@ static const struct ccd_capability_info cap_info[CCD_CAP_COUNT] = {
{"OpenNoLongPP", CCD_CAP_STATE_IF_OPENED},
{"BatteryBypassPP", CCD_CAP_STATE_ALWAYS},
{"UpdateNoTPMWipe", CCD_CAP_STATE_ALWAYS},
{"I2C", CCD_CAP_STATE_IF_OPENED},
};
static const char *ccd_state_names[CCD_STATE_COUNT] = {

View File

@@ -90,7 +90,9 @@ void usb_i2c_execute(struct usb_i2c_config const *config)
if (!count || (!read_count && !write_count))
return;
if (write_count > CONFIG_USB_I2C_MAX_WRITE_COUNT ||
if (!usb_i2c_board_is_enabled()) {
config->buffer[0] = USB_I2C_DISABLED;
} else if (write_count > CONFIG_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) {
@@ -98,6 +100,12 @@ void usb_i2c_execute(struct usb_i2c_config const *config)
} else if (portindex >= i2c_ports_used) {
config->buffer[0] = USB_I2C_PORT_INVALID;
} else {
/*
* TODO (crbug.com/750397): Add security. This currently
* blindly passes through ALL I2C commands on any bus the EC
* knows about. It should behave closer to
* EC_CMD_I2C_PASSTHRU, which can protect ports and ranges.
*/
port = i2c_ports[portindex].port;
config->buffer[0] = usb_i2c_map_error(
i2c_xfer(port, slave_addr,

View File

@@ -130,6 +130,9 @@ enum ccd_capability {
/* Allow Cr50 firmware update without wiping TPM data */
CCD_CAP_CR50_FW_UPDATE_WITHOUT_TPM_WIPE = 14,
/* Access to I2C via USB */
CCD_CAP_I2C = 15,
/* Number of currently defined capabilities */
CCD_CAP_COUNT
};

View File

@@ -45,6 +45,7 @@
* 0x0003: Write count invalid (mismatch with merged payload)
* 0x0004: Read count invalid (> 60 bytes)
* 0x0005: The port specified is invalid.
* 0x0006: The I2C interface is disabled.
* 0x8000: Unknown error mask
* The bottom 15 bits will contain the bottom 15 bits from the EC
* error code.
@@ -60,6 +61,7 @@ enum usb_i2c_error {
USB_I2C_WRITE_COUNT_INVALID = 0x0003,
USB_I2C_READ_COUNT_INVALID = 0x0004,
USB_I2C_PORT_INVALID = 0x0005,
USB_I2C_DISABLED = 0x0006,
USB_I2C_UNKNOWN_ERROR = 0x8000,
};
@@ -149,8 +151,27 @@ 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.
* specific operations required to enable or disable access to the I2C device,
* and to return the current board enable state.
*/
/**
* Enable the I2C device
*
* @return EC_SUCCESS or non-zero error code.
*/
int usb_i2c_board_enable(void);
/**
* Disable the I2C device
*/
void usb_i2c_board_disable(void);
/**
* Check if the I2C device is enabled
*
* @return 1 if enabled, 0 if disabled.
*/
int usb_i2c_board_is_enabled(void);
#endif /* __CROS_USB_I2C_H */