summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeill Corlett <corlett@google.com>2021-01-12 13:17:06 -0500
committerCommit Bot <commit-bot@chromium.org>2021-01-13 17:37:27 +0000
commita789bfc06aff8edf82e225d48b1c539ba0cd3518 (patch)
treead85a38825ab2cf3aedaddca23519dec4544ba23
parenta2653c4f49ddd84301c9691c0da82bd31ac655ff (diff)
downloadchrome-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.c1
-rw-r--r--board/genesis/board.h1
-rw-r--r--board/genesis/build.mk1
-rw-r--r--board/genesis/gpio.inc7
-rw-r--r--board/genesis/pse.c266
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));