cr50: Implement reading all strapping pins for board config

Previously only 1 pin DI0A1 was being read to distinguish between
SPI/I2C configurations. This change adds code to support reading 4
strapping pins DIOA9|DIOA1 and DIOA12|DIOA6 and enabling the internal
pullup/pulldown reistors to differentiate between weak and strong
external pull up/dn restistors. An 8 bit strap config id is produced
and then a config table is searched to match the config id with known
configuraitons. The board properties to be used are read from the
config table.

BRANCH=none
BUG=chrome-os-partner:59833
TEST=manual
Modified the Cr50 dev board with 1M and 5k pullup/pulldown resistors
and connected them to 4 GPIOs (defined as strapping pins). Tested the
12 possible external pullup/pulldown configurations and verified that
the correct 5 bit value was produced for each configuration.

Tested with both Reef and Gru. On Reef the strap config = 0x12 and on
Gru it reads 0x2 as expected. Verfifed TPM was functional on both systems.

Change-Id: I18c625a2b6b904bf4bcdaf2665ed9c3cbdafeb54
Signed-off-by: Scott <scollyer@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/421580
Commit-Ready: Scott Collyer <scollyer@chromium.org>
Tested-by: Scott Collyer <scollyer@chromium.org>
Tested-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
This commit is contained in:
Scott
2016-12-14 10:43:06 -08:00
committed by chrome-bot
parent f6a7f07dc9
commit 92cda018f8
3 changed files with 293 additions and 32 deletions

View File

@@ -115,6 +115,50 @@ const struct i2c_port_t i2c_ports[] = {
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
/* Strapping pin info structure */
#define STRAP_PIN_DELAY_USEC 100
enum strap_list {
a0,
a1,
b0,
b1,
};
struct strap_desc {
/* GPIO enum from gpio.inc for the strap pin */
uint8_t gpio_signal;
/* Offset into pinmux register section for pad SEL register */
uint8_t sel_offset;
/* Entry in the pinmux peripheral selector table for pad */
uint8_t pad_select;
};
struct board_cfg {
/* Value the strap pins should read for a given board */
uint8_t strap_cfg;
/* Properties required for a given board */
uint32_t board_properties;
};
/*
* This table contains both the GPIO and pad specific information required to
* configure each strapping pin to be either a GPIO input or output.
*/
const struct strap_desc strap_regs[] = {
{ GPIO_STRAP_A0, GOFFSET(PINMUX, DIOA1_SEL), GC_PINMUX_DIOA1_SEL, },
{ GPIO_STRAP_A1, GOFFSET(PINMUX, DIOA9_SEL), GC_PINMUX_DIOA9_SEL, },
{ GPIO_STRAP_B0, GOFFSET(PINMUX, DIOA6_SEL), GC_PINMUX_DIOA6_SEL, },
{ GPIO_STRAP_B1, GOFFSET(PINMUX, DIOA12_SEL), GC_PINMUX_DIOA12_SEL, },
};
#define BOARD_PORPERTIES_DEFAULT (BOARD_SLAVE_CONFIG_I2C | BOARD_USE_PLT_RESET)
static struct board_cfg board_cfg_table[] = {
/* Kevin/Gru: DI0A9 = 5k PD, DIOA1 = 1M PU */
{ 0x02, BOARD_SLAVE_CONFIG_SPI | BOARD_NEEDS_SYS_RST_PULL_UP },
/* Reef/Eve: DIOA12 = 5k PD, DIOA6 = 1M PU */
{ 0x20, BOARD_SLAVE_CONFIG_I2C | BOARD_USB_AP | BOARD_USE_PLT_RESET },
};
void post_reboot_request(void)
{
/* Reboot the device next time TPM reset is requested. */
@@ -821,6 +865,183 @@ void enable_int_ap_l(void)
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, enable_int_ap_l, HOOK_PRIO_DEFAULT);
/*
* This function duplicates some of the functionality in chip/g/gpio.c in order
* to configure a given strap pin to be either a low gpio output, a gpio input
* with or without an internal pull resistor, or disconnect the gpio signal
* from the pin pad.
*
* The desired gpio functionality is contained in the input parameter flags,
* while the strap parameter is an index into the array strap_regs.
*/
static void strap_config_pin(enum strap_list strap, int flags)
{
const struct gpio_info *g = gpio_list + strap_regs[strap].gpio_signal;
int bitnum = GPIO_MASK_TO_NUM(g->mask);
int mask = DIO_CTL_IE_MASK | DIO_CTL_PD_MASK | DIO_CTL_PU_MASK;
int val;
if (!flags) {
/* Reset strap pins, disconnect output and clear pull up/dn */
/* Disconnect gpio from pin mux */
DIO_SEL_REG(strap_regs[strap].sel_offset) = 0;
/* Clear input enable and pulldown/pullup in pinmux */
REG_WRITE_MLV(DIO_CTL_REG(strap_regs[strap].sel_offset),
mask, 0, 0);
return;
}
if (flags & GPIO_OUT_LOW) {
/* Config gpio to output and drive low */
gpio_set_flags(strap_regs[strap].gpio_signal, GPIO_OUT_LOW);
/* connect pin mux to gpio */
DIO_SEL_REG(strap_regs[strap].sel_offset) =
GET_GPIO_FUNC(g->port, bitnum);
return;
}
if (flags & GPIO_INPUT) {
/* Configure gpio pin to be an input */
gpio_set_flags(strap_regs[strap].gpio_signal, GPIO_INPUT);
/* Connect pad to gpio */
GET_GPIO_SEL_REG(g->port, bitnum) =
strap_regs[strap].pad_select;
/*
* Input enable is bit 2 of the CTL register. Pulldown enable is
* bit 3, and pullup enable is bit 4. Always set input enable
* and clear the pullup/pulldown bits unless the flags variable
* specifies that pulldown or pullup should be enabled.
*/
val = DIO_CTL_IE_MASK;
if (flags & GPIO_PULL_DOWN)
val |= DIO_CTL_PD_MASK;
if (flags & GPIO_PULL_UP)
val |= DIO_CTL_PU_MASK;
/* Set input enable and pulldown/pullup in pinmux */
REG_WRITE_MLV(DIO_CTL_REG(strap_regs[strap].sel_offset),
mask, 0, val);
}
}
static int get_strap_config(uint8_t *config)
{
enum strap_list s0;
int lvl;
int flags;
uint8_t pullup_bits;
/*
* There are 4 pins that are used to determine Cr50 board strapping
* options. These pins are:
* 1. DIOA1 -> I2CS_SDA
* 2. DI0A9 -> I2CS_SCL
* 3. DIOA6 -> SPS_CLK
* 4. DIOA12 -> SPS_CS_L
* There are two main configuration options based on whether I2C or SPI
* is used for TPM2 communication to/from the host AP. If SPI is the
* TPM2 bus, then the pair of pins DIOA9|DIOA1 are used to designate
* strapping options. If TPM uses I2C, then DIOA12|DIOA6 are the
* strapping pins.
*
* Each strapping pin will have either an external pullup or pulldown
* resistor. The external pull resistors have two levels, 5k for strong
* and 1M for weak. Cr50 has internal pullup/pulldown 50k resistors that
* can be configured via pinmux register settings. This combination of
* external and internal pullup/pulldown resistors allows for 4 possible
* states per strapping pin. The following table shows the different
* combinations. Note that when a strong external pull down/up resistor
* is used, the internal resistor is a don't care and those cases are
* marked by n/a. The bits column represents the signal level read on
* the gpio pin. Bit 1 of this field is the value read with the internal
* pull down/up resistors disabled, and bit 0 is the gpio signal level
* of the same pin when the internal pull resistor is selected as shown
* in the 'internal' column.
* external internal bits
* -------- -------- ----
* 5K PD n/a 00
* 1M PD 50k PU 01
* 1M PU 50k PD 10
* 5K PU n/a 11
*
* To determine the bits associated with each strapping pin, the
* following method is used.
* 1. Set all 4 pins as inputs with internal pulls disabled.
* 2. For each pin do the following to encode 2 bits b1:b0
* a. b1 = gpio_get_level(pin)
* b. If b1 == 1, then enable internal pulldown, else enable
* internal pullup resistor.
* c. b0 = gpio_get_level(pin)
*
* To be considered a valid strap configuraiton, the upper 4 bits must
* have no pullups and at least one pullup in the lower 4 bits or vice
* versa. So can use 0xA0 and 0x0A as masks to check for each
* condition. Once this check is passed, the 4 bits which are used to
* distinguish between SPI vs I2C are masked since reading them as weak
* pulldowns is not being explicitly required due to concerns that the
* AP could prevent accurate differentiation between strong and weak
* pull down cases.
*/
/* Drive all 4 strap pins low to discharge caps. */
for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++)
strap_config_pin(s0, GPIO_OUT_LOW);
/* Delay long enough to discharge any caps. */
udelay(STRAP_PIN_DELAY_USEC);
/* Set all 4 strap pins as inputs with pull resistors disabled. */
for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++)
strap_config_pin(s0, GPIO_INPUT);
/* Delay so voltage levels can settle. */
udelay(STRAP_PIN_DELAY_USEC);
*config = 0;
/* Read 2 bit value of each strapping pin. */
for (s0 = a0; s0 < ARRAY_SIZE(strap_regs); s0++) {
lvl = gpio_get_level(strap_regs[s0].gpio_signal);
flags = GPIO_INPUT;
if (lvl)
flags |= GPIO_PULL_DOWN;
else
flags |= GPIO_PULL_UP;
/* Enable internal pull down/up resistor. */
strap_config_pin(s0, flags);
udelay(STRAP_PIN_DELAY_USEC);
lvl = (lvl << 1) |
gpio_get_level(strap_regs[s0].gpio_signal);
*config |= lvl << s0 * 2;
/*
* Finished with this pin. Disable internal pull up/dn resistor
* and disconnect gpio from pin mux. The pins used for straps
* are configured for their desired role when either the SPI or
* I2C interfaces are initialized.
*/
strap_config_pin(s0, 0);
}
/*
* The strap bits for DIOA12|DIOA6 are in the upper 4 bits of 'config'
* while the strap bits for DIOA9|DIOA1 are in the lower 4 bits. Check
* for SPI vs I2C config by checking for presence of external pullups in
* one group of 4 bits and confirming no external pullups in the other
* group. For SPI config the weak pulldowns may not be accurately read
* on DIOA12|DIOA6 and similarly for I2C config on
* DIOA9|DIOA1. Therefore, only requiring that there be no external
* pullups on these pins and will mask the bits so they will match the
* config table entries.
*/
pullup_bits = *config & 0xaa;
if (!pullup_bits)
return EC_ERROR_UNKNOWN;
/* Now that I2C vs SPI is known, mask the unused strap bits. */
*config &= pullup_bits & 0xa ? 0xf : 0xf0;
return EC_SUCCESS;
}
static void init_board_properties(void)
{
uint32_t properties;
@@ -831,47 +1052,69 @@ static void init_board_properties(void)
* This must be a power on reset or maybe restart due to a software
* update from a version not setting the register.
*/
if (!properties || (system_get_reset_flags() & RESET_FLAG_HARD)) {
if (!(properties & BOARD_ALL_PROPERTIES) || (system_get_reset_flags() &
RESET_FLAG_HARD)) {
int i;
uint8_t config;
/*
* Reset the properties, because after a hard reset the register
* Mask board properties because following hard reset, they
* won't be cleared.
*/
properties = 0;
properties &= ~BOARD_ALL_PROPERTIES;
/* Read DIOA1 strap pin */
if (gpio_get_level(GPIO_STRAP0)) {
/* Strap is pulled high -> Kevin SPI TPM option */
properties |= BOARD_SLAVE_CONFIG_SPI;
/* Add an internal pull up on sys_rst_l */
if (get_strap_config(&config) != EC_SUCCESS) {
/*
* TODO(crosbug.com/p/56945): Remove once SYS_RST_L can
* be pulled up externally.
* No pullups were detected on any of the strap pins so
* there is no point in checking for a matching config
* table entry. For this case default to I2C with
* platform reset and don't store in long life register.
*/
properties |= BOARD_NEEDS_SYS_RST_PULL_UP;
} else {
/* Strap is low -> Reef I2C TPM option */
properties |= BOARD_SLAVE_CONFIG_I2C;
/* One PHY is connected to the AP */
properties |= BOARD_USB_AP;
/*
* Platform reset is present and will need to be
* configured as a an falling edge interrupt.
*/
properties |= BOARD_USE_PLT_RESET;
CPRINTS("No pullup on strap pins detected!");
/* Save this configuration setting */
board_properties = BOARD_PORPERTIES_DEFAULT;
return;
}
/* Search board config table to find a matching entry */
i = 0;
while (i < ARRAY_SIZE(board_cfg_table)) {
if (board_cfg_table[i].strap_cfg == config) {
/* Read board properties for this config */
properties |=
board_cfg_table[i].board_properties;
CPRINTS("Valid strap: 0x%x properties: 0x%x",
config, properties);
/*
* Now save the properties value for future use.
*
* Enable access to LONG_LIFE_SCRATCH1 reg.
*/
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN,
REG1, 1);
/* Save properties in LONG_LIFE register */
GREG32(PMU, LONG_LIFE_SCRATCH1) = properties;
/* Disable access to LONG_LIFE_SCRATCH1 reg */
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN,
REG1, 0);
/* Save this configuration setting */
board_properties = properties;
return;
}
i++;
}
/*
* Now save the properties value for future use.
*
* First enable write access to the LONG_LIFE_SCRATCH1 register.
* Reached the end of the table and didn't find a
* matching config entry. However, the SPI vs I2C
* determination can still be made as get_strap_config()
* returned EC_SUCCESS.
*/
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 1);
/* Save properties in LONG_LIFE register */
GREG32(PMU, LONG_LIFE_SCRATCH1) = properties;
/* Disabel write access to the LONG_LIFE_SCRATCH1 register */
GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG1, 0);
properties = config & 0xa ? BOARD_SLAVE_CONFIG_SPI :
BOARD_PORPERTIES_DEFAULT;
CPRINTS("strap_cfg 0x%x has no table entry, prop = 0x%x",
config, properties);
}
/* Save this configuration setting */
board_properties = properties;
}
@@ -1051,3 +1294,12 @@ static enum vendor_cmd_rc vc_commit_nvmem(enum vendor_cmd_cc code,
return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_COMMIT_NVMEM, vc_commit_nvmem);
static int command_board_properties(int argc, char **argv)
{
ccprintf("properties = 0x%x\n", board_properties);
return EC_SUCCESS;
}
DECLARE_SAFE_CONSOLE_COMMAND(brdprop, command_board_properties,
NULL, "Display board properties");

View File

@@ -84,7 +84,10 @@ GPIO(SPI_CLK, PIN(0, 8), GPIO_INPUT)
GPIO(SPI_CS_L, PIN(0, 9), GPIO_INPUT)
/* GPIOs used for Cr50 strapping options */
GPIO(STRAP0, PIN(0, 10), GPIO_INPUT)
GPIO(STRAP_A0, PIN(1, 12), GPIO_INPUT)
GPIO(STRAP_A1, PIN(1, 13), GPIO_INPUT)
GPIO(STRAP_B0, PIN(1, 14), GPIO_INPUT)
GPIO(STRAP_B1, PIN(1, 15), GPIO_INPUT)
/* Control the load switch powering the INA 3.3V rail */
GPIO(EN_PP3300_INA_L, PIN(0, 11), GPIO_ODR_HIGH)
@@ -118,7 +121,6 @@ PINMUX(GPIO(EN_PP3300_INA_L), B7, DIO_INPUT)
PINMUX(GPIO(SYS_RST_L_OUT), M0, DIO_INPUT)
PINMUX(GPIO(CCD_MODE_L), M1, DIO_INPUT)
PINMUX(GPIO(BATT_PRES_L), M2, 0)
PINMUX(GPIO(STRAP0), A1, DIO_INPUT)
PINMUX(GPIO(I2C_SCL_INA), B0, DIO_INPUT)
PINMUX(GPIO(I2C_SDA_INA), B1, DIO_INPUT)
/* UARTs */

View File

@@ -34,4 +34,11 @@
#define BOARD_CONSOLE_UNLOCKED (1 << 7)
#define BOARD_WP_ASSERTED (1 << 8)
/*
* Macro to capture all properties related to board strapping pins. This must be
* updated if additional strap related properties are added.
*/
#define BOARD_ALL_PROPERTIES (BOARD_SLAVE_CONFIG_SPI | BOARD_SLAVE_CONFIG_I2C \
| BOARD_USB_AP | BOARD_NEEDS_SYS_RST_PULL_UP | BOARD_USE_PLT_RESET)
#endif /* ! __EC_BOARD_CR50_SCRATCH_REG1_H */