diff options
author | Neill Corlett <corlett@google.com> | 2021-01-12 13:17:06 -0500 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-01-13 17:37:27 +0000 |
commit | a789bfc06aff8edf82e225d48b1c539ba0cd3518 (patch) | |
tree | ad85a38825ab2cf3aedaddca23519dec4544ba23 | |
parent | a2653c4f49ddd84301c9691c0da82bd31ac655ff (diff) | |
download | chrome-ec-a789bfc06aff8edf82e225d48b1c539ba0cd3518.tar.gz |
board/genesis: Backport LTC4291 power-over-ethernet changes from
board/endeavour
BUG=b:173566309
TEST=Connected a PoE device to my EVT1; tried pse # on/off commands
Change-Id: I1f6a28d96c987787b1d5b21cdfe530c287cd6881
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2624928
Tested-by: Neill Corlett <corlett@google.com>
Commit-Queue: Neill Corlett <corlett@google.com>
Reviewed-by: Matthew Ziegelbaum <ziegs@chromium.org>
-rw-r--r-- | board/genesis/board.c | 1 | ||||
-rw-r--r-- | board/genesis/board.h | 1 | ||||
-rw-r--r-- | board/genesis/build.mk | 1 | ||||
-rw-r--r-- | board/genesis/gpio.inc | 7 | ||||
-rw-r--r-- | board/genesis/pse.c | 266 |
5 files changed, 273 insertions, 3 deletions
diff --git a/board/genesis/board.c b/board/genesis/board.c index 58ed667002..f51b77df8b 100644 --- a/board/genesis/board.c +++ b/board/genesis/board.c @@ -143,6 +143,7 @@ const struct i2c_port_t i2c_ports[] = { {"ina", I2C_PORT_INA, 400, GPIO_I2C0_SCL, GPIO_I2C0_SDA}, {"ppc0", I2C_PORT_PPC0, 400, GPIO_I2C1_SCL, GPIO_I2C1_SDA}, {"tcpc0", I2C_PORT_TCPC0, 400, GPIO_I2C3_SCL, GPIO_I2C3_SDA}, + {"pse", I2C_PORT_PSE, 400, GPIO_I2C4_SCL, GPIO_I2C4_SDA}, {"power", I2C_PORT_POWER, 400, GPIO_I2C5_SCL, GPIO_I2C5_SDA}, {"eeprom", I2C_PORT_EEPROM, 400, GPIO_I2C7_SCL, GPIO_I2C7_SDA}, }; diff --git a/board/genesis/board.h b/board/genesis/board.h index 3b16a45573..c7400d4420 100644 --- a/board/genesis/board.h +++ b/board/genesis/board.h @@ -124,6 +124,7 @@ #define I2C_PORT_INA NPCX_I2C_PORT0_0 #define I2C_PORT_PPC0 NPCX_I2C_PORT1_0 #define I2C_PORT_TCPC0 NPCX_I2C_PORT3_0 +#define I2C_PORT_PSE NPCX_I2C_PORT4_1 #define I2C_PORT_POWER NPCX_I2C_PORT5_0 #define I2C_PORT_EEPROM NPCX_I2C_PORT7_0 #define I2C_ADDR_EEPROM_FLAGS 0x50 diff --git a/board/genesis/build.mk b/board/genesis/build.mk index 7e10dc26b9..2785133e11 100644 --- a/board/genesis/build.mk +++ b/board/genesis/build.mk @@ -12,3 +12,4 @@ CHIP_VARIANT:=npcx7m6fc board-y=board.o board-y+=led.o +board-y+=pse.o diff --git a/board/genesis/gpio.inc b/board/genesis/gpio.inc index f68b406513..287bece1d2 100644 --- a/board/genesis/gpio.inc +++ b/board/genesis/gpio.inc @@ -93,8 +93,8 @@ GPIO(USB_C0_TCPC_RST, PIN(9, 7), GPIO_OUT_LOW) GPIO(M2_SSD_PLN, PIN(A, 0), GPIO_INPUT) GPIO(EC_ENTERING_RW, PIN(E, 3), GPIO_OUT_LOW) GPIO(CCD_MODE_ODL, PIN(E, 5), GPIO_ODR_HIGH) -GPIO(EN_PP_MST_OD, PIN(9, 6), GPIO_ODR_HIGH) GPIO(PACKET_MODE_EN, PIN(7, 5), GPIO_OUT_LOW) +GPIO(EC_RST_LTC4291_L, PIN(9, 6), GPIO_OUT_HIGH) /* PSE controller reset */ /* HDMI/CEC */ GPIO(EN_PP5000_HDMI, PIN(5, 0), GPIO_OUT_LOW) @@ -110,6 +110,8 @@ GPIO(I2C1_SCL, PIN(9, 0), GPIO_INPUT) /* EC_I2C_USB_C0 GPIO(I2C1_SDA, PIN(8, 7), GPIO_INPUT) /* EC_I2C_USB_C0_TCPPC_SDA */ GPIO(I2C3_SCL, PIN(D, 1), GPIO_INPUT) /* EC_I2C_USB_C0_TCPC_SCL */ GPIO(I2C3_SDA, PIN(D, 0), GPIO_INPUT) /* EC_I2C_USB_C0_TCPC_SDA */ +GPIO(I2C4_SCL, PIN(F, 3), GPIO_INPUT) /* EC_I2C_LTC_SCL */ +GPIO(I2C4_SDA, PIN(F, 2), GPIO_INPUT) /* EC_I2C_LTC_SDA */ GPIO(I2C5_SCL, PIN(3, 3), GPIO_INPUT) /* EC_I2C_IMVP8_SCL */ GPIO(I2C5_SDA, PIN(3, 6), GPIO_INPUT) /* EC_I2C_IMVP8_SDA */ GPIO(I2C7_SCL, PIN(B, 3), GPIO_INPUT) /* EC_I2C_EEPROM_SCL */ @@ -120,6 +122,7 @@ ALTERNATE(PIN_MASK(B, 0x30), 0, MODULE_I2C, 0) /* I2C0 */ ALTERNATE(PIN_MASK(9, 0x01), 0, MODULE_I2C, 0) /* I2C1 SCL */ ALTERNATE(PIN_MASK(8, 0x80), 0, MODULE_I2C, 0) /* I2C1 SDA */ ALTERNATE(PIN_MASK(D, 0x03), 0, MODULE_I2C, 0) /* I2C3 */ +ALTERNATE(PIN_MASK(F, 0x0C), 0, MODULE_I2C, 0) /* I2C4 */ ALTERNATE(PIN_MASK(3, 0x48), 0, MODULE_I2C, 0) /* I2C5 */ ALTERNATE(PIN_MASK(B, 0x0C), 0, MODULE_I2C, 0) /* I2C7 */ @@ -139,8 +142,6 @@ ALTERNATE(PIN_MASK(6, 0x30), 0, MODULE_UART, 0) /* UART from EC /* Unused pins */ UNUSED(PIN(1, 3)) /* EC_GP_SEL1_ODL */ -UNUSED(PIN(F, 2)) /* EC_I2C_RFU_SDA */ -UNUSED(PIN(F, 3)) /* EC_I2C_RFU_SCL */ UNUSED(PIN(C, 0)) /* FAN_PWM_2 */ UNUSED(PIN(8, 0)) /* LED_BLUE_L */ UNUSED(PIN(4, 4)) /* ADC1/TEMP_SENSOR_2 */ diff --git a/board/genesis/pse.c b/board/genesis/pse.c new file mode 100644 index 0000000000..671288ccf5 --- /dev/null +++ b/board/genesis/pse.c @@ -0,0 +1,266 @@ +/* Copyright 2020 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. + */ + +/* + * The LTC4291 is a power over ethernet (PoE) power sourcing equipment (PSE) + * controller. + */ + +#include "common.h" +#include "console.h" +#include "ec_commands.h" +#include "hooks.h" +#include "host_command.h" +#include "i2c.h" +#include "string.h" +#include "timer.h" +#include "util.h" + +#define LTC4291_I2C_ADDR 0x2C + +#define LTC4291_REG_SUPEVN_COR 0x0B +#define LTC4291_REG_STATPWR 0x10 +#define LTC4291_REG_STATPIN 0x11 +#define LTC4291_REG_OPMD 0x12 +#define LTC4291_REG_DISENA 0x13 +#define LTC4291_REG_DETENA 0x14 +#define LTC4291_REG_DETPB 0x18 +#define LTC4291_REG_PWRPB 0x19 +#define LTC4291_REG_RSTPB 0x1A +#define LTC4291_REG_ID 0x1B +#define LTC4291_REG_DEVID 0x43 +#define LTC4291_REG_HPMD1 0x46 +#define LTC4291_REG_HPMD2 0x4B +#define LTC4291_REG_HPMD3 0x50 +#define LTC4291_REG_HPMD4 0x55 +#define LTC4291_REG_LPWRPB 0x6E + +#define LTC4291_FLD_STATPIN_AUTO BIT(0) +#define LTC4291_FLD_RSTPB_RSTALL BIT(4) + +#define LTC4291_STATPWR_ON_PORT(port) (0x01 << (port)) +#define LTC4291_DETENA_EN_PORT(port) (0x11 << (port)) +#define LTC4291_DETPB_EN_PORT(port) (0x11 << (port)) +#define LTC4291_PWRPB_OFF_PORT(port) (0x10 << (port)) + +#define LTC4291_OPMD_AUTO 0xFF +#define LTC4291_DISENA_ALL 0x0F +#define LTC4291_DETENA_ALL 0xFF +#define LTC4291_ID 0x64 +#define LTC4291_DEVID 0x38 +#define LTC4291_HPMD_MIN 0x00 +#define LTC4291_HPMD_MAX 0xA8 + +#define LTC4291_PORT_MAX 4 + +#define LTC4291_RESET_DELAY_US (20 * MSEC) + +#define I2C_PSE_READ(reg, data) \ + i2c_read8(I2C_PORT_PSE, LTC4291_I2C_ADDR, LTC4291_REG_##reg, (data)) + +#define I2C_PSE_WRITE(reg, data) \ + i2c_write8(I2C_PORT_PSE, LTC4291_I2C_ADDR, LTC4291_REG_##reg, (data)) + +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) + +static int pse_write_hpmd(int port, int val) +{ + switch (port) { + case 0: + return I2C_PSE_WRITE(HPMD1, val); + case 1: + return I2C_PSE_WRITE(HPMD2, val); + case 2: + return I2C_PSE_WRITE(HPMD3, val); + case 3: + return I2C_PSE_WRITE(HPMD4, val); + default: + return EC_ERROR_INVAL; + } +} + +/* + * Port 1: 100W + * Port 2-4: 15W + */ +static int pse_port_hpmd[4] = { + LTC4291_HPMD_MAX, + LTC4291_HPMD_MIN, + LTC4291_HPMD_MIN, + LTC4291_HPMD_MIN, +}; + +static int pse_port_enable(int port) +{ + /* Enable detection and classification */ + return I2C_PSE_WRITE(DETPB, LTC4291_DETPB_EN_PORT(port)); +} + +static int pse_port_disable(int port) +{ + /* Request power off (this also disables detection/classification) */ + return I2C_PSE_WRITE(PWRPB, LTC4291_PWRPB_OFF_PORT(port)); +} + +static int pse_init_worker(void) +{ + timestamp_t deadline; + int err, id, devid, statpin, port; + + /* Ignore errors -- may already be resetting */ + I2C_PSE_WRITE(RSTPB, LTC4291_FLD_RSTPB_RSTALL); + + deadline.val = get_time().val + LTC4291_RESET_DELAY_US; + while ((err = I2C_PSE_READ(ID, &id)) != 0) { + if (timestamp_expired(deadline, NULL)) + return err; + } + + err = I2C_PSE_READ(DEVID, &devid); + if (err != 0) + return err; + + if (id != LTC4291_ID || devid != LTC4291_DEVID) + return EC_ERROR_INVAL; + + err = I2C_PSE_READ(STATPIN, &statpin); + if (err != 0) + return err; + + /* + * We don't want to supply power until we've had a chance to set the + * limits. + */ + if (statpin & LTC4291_FLD_STATPIN_AUTO) + CPRINTS("WARN: PSE reset in AUTO mode"); + + err = I2C_PSE_WRITE(OPMD, LTC4291_OPMD_AUTO); + if (err != 0) + return err; + + /* Set maximum power each port is allowed to allocate. */ + for (port = 0; port < LTC4291_PORT_MAX; port++) { + err = pse_write_hpmd(port, pse_port_hpmd[port]); + if (err != 0) + return err; + } + + err = I2C_PSE_WRITE(DISENA, LTC4291_DISENA_ALL); + if (err != 0) + return err; + + err = I2C_PSE_WRITE(DETENA, LTC4291_DETENA_ALL); + if (err != 0) + return err; + + return 0; +} + +static void pse_init(void) +{ + int err; + + err = pse_init_worker(); + if (err != 0) + CPRINTS("PSE init failed: %d", err); + else + CPRINTS("PSE init done"); +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, pse_init, HOOK_PRIO_DEFAULT); + +/* Also reset the PSE on a reboot to toggle the power. */ +DECLARE_HOOK(HOOK_CHIPSET_RESET, pse_init, HOOK_PRIO_DEFAULT); + +static int command_pse(int argc, char **argv) +{ + int port; + + /* + * TODO(b/156399232): endeavour: PSE controller reset by PLTRST + * + * Initialization does not reliably work after reset because the device + * is held in reset by the AP. Running this command after boot finishes + * always succeeds. Remove once the reset signal changes. + */ + if (!strncmp(argv[1], "init", 4)) + return pse_init_worker(); + + if (argc != 3) + return EC_ERROR_PARAM_COUNT; + + port = atoi(argv[1]); + if (port < 0 || port >= LTC4291_PORT_MAX) + return EC_ERROR_PARAM1; + + if (!strncmp(argv[2], "off", 3)) + return pse_port_disable(port); + else if (!strncmp(argv[2], "on", 2)) + return pse_port_enable(port); + else if (!strncmp(argv[2], "min", 3)) + return pse_write_hpmd(port, LTC4291_HPMD_MIN); + else if (!strncmp(argv[2], "max", 3)) + return pse_write_hpmd(port, LTC4291_HPMD_MAX); + else + return EC_ERROR_PARAM2; +} +DECLARE_CONSOLE_COMMAND(pse, command_pse, + "<port# 0-3> <off | on | min | max>", + "Set PSE port power"); + +static int ec_command_pse_status(int port, uint8_t *status) +{ + int detena, statpwr; + int err; + + err = I2C_PSE_READ(DETENA, &detena); + if (err != 0) + return err; + + err = I2C_PSE_READ(STATPWR, &statpwr); + if (err != 0) + return err; + + if ((detena & LTC4291_DETENA_EN_PORT(port)) == 0) + *status = EC_PSE_STATUS_DISABLED; + else if ((statpwr & LTC4291_STATPWR_ON_PORT(port)) == 0) + *status = EC_PSE_STATUS_ENABLED; + else + *status = EC_PSE_STATUS_POWERED; + + return 0; +} + +static enum ec_status ec_command_pse(struct host_cmd_handler_args *args) +{ + const struct ec_params_pse *p = args->params; + int err = 0; + + if (p->port >= LTC4291_PORT_MAX) + return EC_RES_INVALID_PARAM; + + switch (p->cmd) { + case EC_PSE_STATUS: { + struct ec_response_pse_status *r = args->response; + + args->response_size = sizeof(*r); + err = ec_command_pse_status(p->port, &r->status); + break; + } + case EC_PSE_ENABLE: + err = pse_port_enable(p->port); + break; + case EC_PSE_DISABLE: + err = pse_port_disable(p->port); + break; + default: + return EC_RES_INVALID_PARAM; + } + + if (err) + return EC_RES_ERROR; + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_PSE, ec_command_pse, EC_VER_MASK(0)); |