Files
OpenCellular/board/mn50/board.c
Nick Sanders fd528684dd mn50: fix usb_update
Add support for update related vendor commands in mn50 by relocating
relevant code from board/cr50 to chip/g.

BUG=b:36910757
BRANCH=None
TEST=./extra/usb_updater/usb_updater -d 18d1:502a build/mn50/ec.bin

Change-Id: Iec0fe5585b5b6eb099f9254dfb0e5b02d5106abc
Reviewed-on: https://chromium-review.googlesource.com/537999
Commit-Ready: Nick Sanders <nsanders@chromium.org>
Tested-by: Nick Sanders <nsanders@chromium.org>
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
2017-06-16 17:24:28 -07:00

376 lines
9.5 KiB
C

/* Copyright 2017 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 <endian.h>
#include "case_closed_debug.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "dcrypto/dcrypto.h"
#include "device_state.h"
#include "ec_version.h"
#include "extension.h"
#include "flash.h"
#include "flash_config.h"
#include "gpio.h"
#include "hooks.h"
#include "i2c.h"
#include "ina2xx.h"
#include "init_chip.h"
#include "nvmem.h"
#include "nvmem_vars.h"
#include "registers.h"
#include "signed_header.h"
#include "spi.h"
#include "system.h"
#include "task.h"
#include "trng.h"
#include "uartn.h"
#include "usb_api.h"
#include "usb_descriptor.h"
#include "usb_hid.h"
#include "usb_spi.h"
#include "usb_i2c.h"
#include "util.h"
/* Define interrupt and gpio structs */
#include "gpio_list.h"
#include "cryptoc/sha.h"
#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
/* NvMem user buffer lengths table */
uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {
NVMEM_CR50_SIZE
};
/* I2C Port definition. No GPIO access. */
const struct i2c_port_t i2c_ports[] = {
{"master", I2C_PORT_MASTER, 100, 0, 0},
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
/* Recall whether we have enable socket power. */
static int socket_set_enabled;
/*****************************************************************************/
#include "gpio.wrap"
static void init_interrupts(void)
{
int i;
uint32_t exiten = GREG32(PINMUX, EXITEN0);
/* Clear wake pin interrupts */
GREG32(PINMUX, EXITEN0) = 0;
GREG32(PINMUX, EXITEN0) = exiten;
/* Enable all GPIO interrupts */
for (i = 0; i < gpio_ih_count; i++)
if (gpio_list[i].flags & GPIO_INT_ANY)
gpio_enable_interrupt(i);
}
void decrement_retry_counter(void)
{
uint32_t counter = GREG32(PMU, LONG_LIFE_SCRATCH0);
if (counter) {
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
GREG32(PMU, LONG_LIFE_SCRATCH0) = counter - 1;
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
}
}
void ccd_phy_init(int none)
{
usb_select_phy(USB_SEL_PHY1);
usb_init();
}
void usb_i2c_board_disable(void)
{
}
int usb_i2c_board_enable(void)
{
return EC_SUCCESS;
}
/* Initialize board. */
static void board_init(void)
{
/*
* Deep sleep resets should be considered valid and should not impact
* the rolling reboot count.
*/
if (system_get_reset_flags() & RESET_FLAG_HIBERNATE)
decrement_retry_counter();
init_interrupts();
init_trng();
init_jittery_clock(1);
init_runlevel(PERMISSION_MEDIUM);
/* Initialize NvMem partitions */
nvmem_init();
/* Initialize the persistent storage. */
initvars();
/* Disable all power to socket, for hot swapping. */
disable_socket();
/* Indication that firmware is running, for debug purposes. */
GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE;
/* Enable USB / CCD */
ccd_set_mode(CCD_MODE_ENABLED);
uartn_enable(UART_AP);
/* Calibrate INA0 (VBUS) with 1mA/LSB scale */
i2cm_init();
ina2xx_init(0, 0x8000, INA2XX_CALIB_1MA(150 /*mOhm*/));
ina2xx_init(1, 0x8000, INA2XX_CALIB_1MA(150 /*mOhm*/));
ina2xx_init(4, 0x8000, INA2XX_CALIB_1MA(150 /*mOhm*/));
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
const void * const usb_strings[] = {
[USB_STR_DESC] = usb_string_desc,
[USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."),
[USB_STR_PRODUCT] = USB_STRING_DESC("Mn50"),
[USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
[USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Shell"),
[USB_STR_BLOB_NAME] = USB_STRING_DESC("Blob"),
[USB_STR_AP_NAME] = USB_STRING_DESC("DUT UART"),
[USB_STR_UPGRADE_NAME] = USB_STRING_DESC("Firmware upgrade"),
[USB_STR_SPI_NAME] = USB_STRING_DESC("SPI"),
[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);
/* SPI devices */
/* port 0, 40MHz / (16 + 1) = 2.3MHz SPI, no soft CS */
const struct spi_device_t spi_devices[] = {
[CONFIG_SPI_FLASH_PORT] = {0, 16, GPIO_COUNT}
};
const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
int flash_regions_to_enable(struct g_flash_region *regions,
int max_regions)
{
/*
* This needs to account for two regions: the "other" RW partition and
* the NVRAM in TOP_B.
*
* When running from RW_A the two regions are adjacent, but it is
* simpler to keep function logic the same and always configure two
* separate regions.
*/
if (max_regions < 3)
return 0;
/* Enable access to the other RW image... */
if (system_get_image_copy() == SYSTEM_IMAGE_RW)
/* Running RW_A, enable RW_B */
regions[0].reg_base = CONFIG_MAPPED_STORAGE_BASE +
CONFIG_RW_B_MEM_OFF;
else
/* Running RW_B, enable RW_A */
regions[0].reg_base = CONFIG_MAPPED_STORAGE_BASE +
CONFIG_RW_MEM_OFF;
/* Size is the same */
regions[0].reg_size = CONFIG_RW_SIZE;
regions[0].reg_perms = FLASH_REGION_EN_ALL;
/* Enable access to the NVRAM partition A region */
regions[1].reg_base = CONFIG_MAPPED_STORAGE_BASE +
CONFIG_FLASH_NVMEM_OFFSET_A;
regions[1].reg_size = NVMEM_PARTITION_SIZE;
regions[1].reg_perms = FLASH_REGION_EN_ALL;
/* Enable access to the NVRAM partition B region */
regions[2].reg_base = CONFIG_MAPPED_STORAGE_BASE +
CONFIG_FLASH_NVMEM_OFFSET_B;
regions[2].reg_size = NVMEM_PARTITION_SIZE;
regions[2].reg_perms = FLASH_REGION_EN_ALL;
return 3;
}
/* Check if socket has been anabled and power is OK. */
int is_socket_enabled(void)
{
/* TODO: check voltage rails within approved bands. */
return (gpio_get_level(GPIO_DUT_PWRGOOD) && socket_set_enabled);
}
/* Determine whether the socket has no voltage. TODO: check GPIOS? */
int is_socket_off(void)
{
/* Check 3.3v = 0. */
if (ina2xx_get_voltage(1) > 10)
return 0;
/* Check 2.6v = 0. */
if (ina2xx_get_voltage(4) > 10)
return 0;
return 1;
}
void enable_socket(void)
{
/* Power up. */
gpio_set_level(GPIO_DUT_PWR_EN, 1);
/* Indicate socket powered with red LED. */
gpio_set_level(GPIO_LED_L, 0);
/* GPIOs as ioutputs. */
gpio_set_flags(GPIO_DUT_RST_L, GPIO_OUT_LOW);
gpio_set_flags(GPIO_DUT_BOOT_CFG, GPIO_OUT_LOW);
gpio_set_flags(GPIO_SPI_CS_ALT_L, GPIO_OUT_HIGH);
/* Connect DIO A4, A8 to the SPI peripheral */
GWRITE(PINMUX, DIOA4_SEL, 0); /* SPI_MOSI */
GWRITE(PINMUX, DIOA8_SEL, 0); /* SPI_CLK */
GWRITE(PINMUX, DIOA5_SEL, GC_PINMUX_GPIO0_GPIO10_SEL);
/* UART */
GWRITE(PINMUX, DIOA7_SEL, GC_PINMUX_UART1_TX_SEL);
/* Chip select. */
GWRITE_FIELD(PINMUX, DIOA5_CTL, PU, 1);
socket_set_enabled = 1;
}
void disable_socket(void)
{
/* Disable CS pin. */
GWRITE_FIELD(PINMUX, DIOA5_CTL, PU, 0);
/* TODO: Implement way to get the gpio */
ASSERT(GREAD(PINMUX, GPIO0_GPIO7_SEL) == GC_PINMUX_DIOA4_SEL);
ASSERT(GREAD(PINMUX, GPIO0_GPIO8_SEL) == GC_PINMUX_DIOA8_SEL);
ASSERT(GREAD(PINMUX, GPIO0_GPIO10_SEL) == GC_PINMUX_DIOA5_SEL);
/* Set SPI MOSI, CLK, and CS_L as inputs */
GWRITE(PINMUX, DIOA4_SEL, GC_PINMUX_GPIO0_GPIO7_SEL);
GWRITE(PINMUX, DIOA8_SEL, GC_PINMUX_GPIO0_GPIO8_SEL);
GWRITE(PINMUX, DIOA5_SEL, GC_PINMUX_GPIO0_GPIO10_SEL);
/* UART */
GWRITE(PINMUX, DIOA7_SEL, 0);
/* GPIOs as inputs. */
gpio_set_flags(GPIO_DUT_BOOT_CFG, GPIO_INPUT);
gpio_set_flags(GPIO_DUT_RST_L, GPIO_INPUT);
gpio_set_flags(GPIO_SPI_CS_ALT_L, GPIO_INPUT);
/* Turn off socket power. */
gpio_set_level(GPIO_DUT_PWR_EN, 0);
/* Indicate socket unpowered with no red LED. */
gpio_set_level(GPIO_LED_L, 1);
socket_set_enabled = 0;
}
static int command_socket(int argc, char **argv)
{
if (argc > 1) {
if (!strcasecmp("enable", argv[1]))
enable_socket();
else if (!strcasecmp("disable", argv[1]))
disable_socket();
else
return EC_ERROR_PARAM1;
/* Let power settle. */
msleep(5);
}
ccprintf("Socket enabled: %s, powered: %s\n",
is_socket_enabled() ? "yes" : "no",
is_socket_off() ? "off" : "on");
return EC_SUCCESS;
}
DECLARE_SAFE_CONSOLE_COMMAND(socket, command_socket,
"[enable|disable]",
"Activate and deactivate socket");
void post_reboot_request(void)
{
/* This will never return. */
system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD);
}
/* Determine key type based on the key ID. */
static const char *key_type(uint32_t key_id)
{
/*
* It is a mere convention, but all prod keys are required to have key
* IDs such, that bit D2 is set, and all dev keys are required to have
* key IDs such, that bit D2 is not set.
*
* This convention is enforced at the key generation time.
*/
if (key_id & (1 << 2))
return "prod";
else
return "dev";
}
static int command_sysinfo(int argc, char **argv)
{
enum system_image_copy_t active;
uintptr_t vaddr;
const struct SignedHeader *h;
ccprintf("Reset flags: 0x%08x (", system_get_reset_flags());
system_print_reset_flags();
ccprintf(")\n");
ccprintf("Chip: %s %s %s\n", system_get_chip_vendor(),
system_get_chip_name(), system_get_chip_revision());
active = system_get_ro_image_copy();
vaddr = get_program_memory_addr(active);
h = (const struct SignedHeader *)vaddr;
ccprintf("RO keyid: 0x%08x(%s)\n", h->keyid, key_type(h->keyid));
active = system_get_image_copy();
vaddr = get_program_memory_addr(active);
h = (const struct SignedHeader *)vaddr;
ccprintf("RW keyid: 0x%08x(%s)\n", h->keyid, key_type(h->keyid));
ccprintf("DEV_ID: 0x%08x 0x%08x\n",
GREG32(FUSE, DEV_ID0), GREG32(FUSE, DEV_ID1));
return EC_SUCCESS;
}
DECLARE_SAFE_CONSOLE_COMMAND(sysinfo, command_sysinfo,
NULL,
"Print system info");
/*
* SysInfo command:
* There are no input args.
* Output is this struct, all fields in network order.
*/
struct sysinfo_s {
uint32_t ro_keyid;
uint32_t rw_keyid;
uint32_t dev_id0;
uint32_t dev_id1;
} __packed;