summaryrefslogtreecommitdiff
path: root/zephyr/projects/intelrvp/src
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/projects/intelrvp/src')
-rw-r--r--zephyr/projects/intelrvp/src/chg_usb_pd.c129
-rw-r--r--zephyr/projects/intelrvp/src/chg_usb_pd_mecc_1_1.c92
-rw-r--r--zephyr/projects/intelrvp/src/intel_rvp_board_id.c30
-rw-r--r--zephyr/projects/intelrvp/src/intel_rvp_led.c168
-rw-r--r--zephyr/projects/intelrvp/src/intelrvp.c25
-rw-r--r--zephyr/projects/intelrvp/src/usb_pd_policy_mecc_1_1.c106
6 files changed, 550 insertions, 0 deletions
diff --git a/zephyr/projects/intelrvp/src/chg_usb_pd.c b/zephyr/projects/intelrvp/src/chg_usb_pd.c
new file mode 100644
index 0000000000..63a1853b4d
--- /dev/null
+++ b/zephyr/projects/intelrvp/src/chg_usb_pd.c
@@ -0,0 +1,129 @@
+/* Copyright 2022 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Common USB PD charge configuration */
+
+#include "charge_manager.h"
+#include "charge_state_v2.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "intelrvp.h"
+#include "tcpm/tcpci.h"
+
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ##args)
+
+bool is_typec_port(int port)
+{
+#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
+ return !(port == DEDICATED_CHARGE_PORT || port == CHARGE_PORT_NONE);
+#else
+ return !(port == CHARGE_PORT_NONE);
+#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 */
+}
+
+static inline int board_dc_jack_present(void)
+{
+#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
+ return gpio_get_level(GPIO_DC_JACK_PRESENT);
+#else
+ return 0;
+#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 */
+}
+
+static void board_dc_jack_handle(void)
+{
+#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
+ struct charge_port_info charge_dc_jack;
+
+ /* System is booted from DC Jack */
+ if (board_dc_jack_present()) {
+ charge_dc_jack.current =
+ (PD_MAX_POWER_MW * 1000) / DC_JACK_MAX_VOLTAGE_MV;
+ charge_dc_jack.voltage = DC_JACK_MAX_VOLTAGE_MV;
+ } else {
+ charge_dc_jack.current = 0;
+ charge_dc_jack.voltage = USB_CHARGER_VOLTAGE_MV;
+ }
+
+ charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED,
+ DEDICATED_CHARGE_PORT, &charge_dc_jack);
+#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 */
+}
+
+void board_dc_jack_interrupt(enum gpio_signal signal)
+{
+ board_dc_jack_handle();
+}
+
+static void board_charge_init(void)
+{
+ int port, supplier;
+ struct charge_port_info charge_init = {
+ .current = 0,
+ .voltage = USB_CHARGER_VOLTAGE_MV,
+ };
+
+ /* Initialize all charge suppliers to seed the charge manager */
+ for (port = 0; port < CHARGE_PORT_COUNT; port++) {
+ for (supplier = 0; supplier < CHARGE_SUPPLIER_COUNT;
+ supplier++) {
+ charge_manager_update_charge(supplier, port,
+ &charge_init);
+ }
+ }
+
+ board_dc_jack_handle();
+}
+DECLARE_HOOK(HOOK_INIT, board_charge_init, HOOK_PRIO_DEFAULT);
+
+int board_set_active_charge_port(int port)
+{
+ int i;
+ /* charge port is a realy physical port */
+ int is_real_port = (port >= 0 && port < CHARGE_PORT_COUNT);
+ /* check if we are source vbus on that port */
+ int source = board_vbus_source_enabled(port);
+
+ if (is_real_port && source) {
+ CPRINTS("Skip enable p%d", port);
+ return EC_ERROR_INVAL;
+ }
+
+#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
+ /*
+ * Do not enable Type-C port if the DC Jack is present.
+ * When the Type-C is active port, hardware circuit will
+ * block DC jack from enabling +VADP_OUT.
+ */
+ if (port != DEDICATED_CHARGE_PORT && board_dc_jack_present()) {
+ CPRINTS("DC Jack present, Skip enable p%d", port);
+ return EC_ERROR_INVAL;
+ }
+#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT */
+
+ /* Make sure non-charging ports are disabled */
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ if (i != port) {
+ board_charging_enable(i, 0);
+ }
+ }
+
+ /* Enable charging port */
+ if (is_typec_port(port)) {
+ board_charging_enable(port, 1);
+ }
+
+ CPRINTS("New chg p%d", port);
+
+ return EC_SUCCESS;
+}
+
+void board_set_charge_limit(int port, int supplier, int charge_ma, int max_ma,
+ int charge_mv)
+{
+ charge_set_input_current_limit(
+ MAX(charge_ma, CONFIG_CHARGER_INPUT_CURRENT), charge_mv);
+}
diff --git a/zephyr/projects/intelrvp/src/chg_usb_pd_mecc_1_1.c b/zephyr/projects/intelrvp/src/chg_usb_pd_mecc_1_1.c
new file mode 100644
index 0000000000..45fbbc6f65
--- /dev/null
+++ b/zephyr/projects/intelrvp/src/chg_usb_pd_mecc_1_1.c
@@ -0,0 +1,92 @@
+/* Copyright 2022 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Intel-RVP family-specific configuration */
+
+#include "console.h"
+#include "gpio/gpio_int.h"
+#include "hooks.h"
+#include "include/gpio.h"
+#include "intelrvp.h"
+#include "ioexpander.h"
+#include "system.h"
+#include "tcpm/tcpci.h"
+#include "usbc_ppc.h"
+
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ##args)
+
+void tcpc_alert_event(enum gpio_signal signal)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ /* No alerts for embedded TCPC */
+ if (tcpc_config[i].bus_type == EC_BUS_TYPE_EMBEDDED) {
+ continue;
+ }
+
+ if (signal == tcpc_aic_gpios[i].tcpc_alert) {
+ schedule_deferred_pd_interrupt(i);
+ break;
+ }
+ }
+}
+
+uint16_t tcpc_get_alert_status(void)
+{
+ uint16_t status = 0;
+ int i;
+
+ /* Check which port has the ALERT line set */
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ /* No alerts for embdeded TCPC */
+ if (tcpc_config[i].bus_type == EC_BUS_TYPE_EMBEDDED) {
+ continue;
+ }
+
+ if (!gpio_get_level(tcpc_aic_gpios[i].tcpc_alert)) {
+ status |= PD_STATUS_TCPC_ALERT_0 << i;
+ }
+ }
+
+ return status;
+}
+
+int ppc_get_alert_status(int port)
+{
+ return tcpc_aic_gpios[port].ppc_intr_handler &&
+ !gpio_get_level(tcpc_aic_gpios[port].ppc_alert);
+}
+
+/* PPC support routines */
+void ppc_interrupt(enum gpio_signal signal)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ if (tcpc_aic_gpios[i].ppc_intr_handler &&
+ signal == tcpc_aic_gpios[i].ppc_alert) {
+ tcpc_aic_gpios[i].ppc_intr_handler(i);
+ break;
+ }
+ }
+}
+
+void board_charging_enable(int port, int enable)
+{
+ int rv;
+
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ rv = ppc_vbus_sink_enable(port, enable);
+ } else {
+ rv = tcpc_config[port].drv->set_snk_ctrl(port, enable);
+ }
+
+ if (rv) {
+ CPRINTS("C%d: sink path %s failed", port,
+ enable ? "en" : "dis");
+ }
+}
diff --git a/zephyr/projects/intelrvp/src/intel_rvp_board_id.c b/zephyr/projects/intelrvp/src/intel_rvp_board_id.c
new file mode 100644
index 0000000000..77d4e93afd
--- /dev/null
+++ b/zephyr/projects/intelrvp/src/intel_rvp_board_id.c
@@ -0,0 +1,30 @@
+/* Copyright 2022 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <zephyr/devicetree.h>
+#include "intel_rvp_board_id.h"
+
+#define DT_DRV_COMPAT intel_rvp_board_id
+
+BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
+ "Unsupported RVP Board ID instance");
+
+#define RVP_ID_GPIO_DT_SPEC_GET(idx, node_id, prop) \
+ GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx),
+
+#define RVP_ID_CONFIG_LIST(node_id, prop) \
+ LISTIFY(DT_PROP_LEN(node_id, prop), RVP_ID_GPIO_DT_SPEC_GET, (), \
+ node_id, prop)
+
+#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
+const struct gpio_dt_spec bom_id_config[] = { RVP_ID_CONFIG_LIST(DT_DRV_INST(0),
+ bom_gpios) };
+
+const struct gpio_dt_spec fab_id_config[] = { RVP_ID_CONFIG_LIST(DT_DRV_INST(0),
+ fab_gpios) };
+
+const struct gpio_dt_spec board_id_config[] = { RVP_ID_CONFIG_LIST(
+ DT_DRV_INST(0), board_gpios) };
+#endif /* #if DT_HAS_COMPAT_STATUS_OKAY */
diff --git a/zephyr/projects/intelrvp/src/intel_rvp_led.c b/zephyr/projects/intelrvp/src/intel_rvp_led.c
new file mode 100644
index 0000000000..0e4d872963
--- /dev/null
+++ b/zephyr/projects/intelrvp/src/intel_rvp_led.c
@@ -0,0 +1,168 @@
+/* Copyright 2022 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "battery.h"
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "ec_commands.h"
+#include "extpower.h"
+#include "hooks.h"
+#include "led_common.h"
+#include "led_pwm.h"
+#include "pwm.h"
+#include "timer.h"
+#include "util.h"
+
+/* Battery percentage thresholds to blink at different rates. */
+#define LOW_BATTERY_PERCENTAGE 10
+#define NORMAL_BATTERY_PERCENTAGE 90
+
+#define LED_OFF -1
+
+#define LED_PULSE_TICK (125 * MSEC)
+
+#define LED_FAST_PULSE_PERIOD (250 / 125) /* 250 ms */
+#define LED_SLOW_PULSE_PERIOD ((2 * MSEC) / 125) /* 2 sec */
+
+struct led_pulse_data {
+ bool led_is_pulsing;
+ uint8_t led_pulse_period;
+ uint8_t led_tick_count;
+};
+
+static struct led_pulse_data rvp_led[CONFIG_LED_PWM_COUNT];
+
+static void pulse_led_deferred(void);
+DECLARE_DEFERRED(pulse_led_deferred);
+
+static void pulse_led_deferred(void)
+{
+ int i = 0;
+ bool call_deferred = false;
+
+ for (i = 0; i < CONFIG_LED_PWM_COUNT; i++) {
+ if (!rvp_led[i].led_is_pulsing) {
+ rvp_led[i].led_tick_count = 0;
+ continue;
+ }
+
+ /*
+ * LED will be in ON state first half of the pulse period
+ * and in OFF state in second half of the pulse period.
+ */
+ if (rvp_led[i].led_tick_count <
+ (rvp_led[i].led_pulse_period >> 1))
+ set_pwm_led_color(i, EC_LED_COLOR_GREEN);
+ else
+ set_pwm_led_color(i, LED_OFF);
+
+ rvp_led[i].led_tick_count = (rvp_led[i].led_tick_count + 1) %
+ rvp_led[i].led_pulse_period;
+ call_deferred = true;
+ }
+
+ if (call_deferred)
+ hook_call_deferred(&pulse_led_deferred_data, LED_PULSE_TICK);
+}
+
+static void pulse_leds(enum pwm_led_id id, int period)
+{
+ rvp_led[id].led_pulse_period = period;
+ rvp_led[id].led_is_pulsing = true;
+
+ pulse_led_deferred();
+}
+
+static void update_charger_led(enum pwm_led_id id)
+{
+ enum charge_state chg_st = charge_get_state();
+
+ /*
+ * The colors listed below are the default, but can be overridden.
+ *
+ * Fast Flash = Charging error
+ * Slow Flash = Discharging
+ * LED on = Charging
+ * LED off = No Charger connected
+ */
+ if (chg_st == PWR_STATE_CHARGE ||
+ chg_st == PWR_STATE_CHARGE_NEAR_FULL) {
+ /* Charging: LED ON */
+ rvp_led[id].led_is_pulsing = false;
+ set_pwm_led_color(id, EC_LED_COLOR_GREEN);
+ } else if (chg_st == PWR_STATE_DISCHARGE ||
+ chg_st == PWR_STATE_DISCHARGE_FULL) {
+ if (extpower_is_present()) {
+ /* Discharging:
+ * Flash slower (2 second period, 100% duty cycle)
+ */
+ pulse_leds(id, LED_SLOW_PULSE_PERIOD);
+ } else {
+ /* No Charger connected: LED OFF */
+ rvp_led[id].led_is_pulsing = false;
+ set_pwm_led_color(id, LED_OFF);
+ }
+ } else if (chg_st == PWR_STATE_ERROR) {
+ /* Charging error:
+ * Flash faster (250 ms period, 100% duty cycle)
+ */
+ pulse_leds(id, LED_FAST_PULSE_PERIOD);
+ } else {
+ /* LED OFF */
+ rvp_led[id].led_is_pulsing = false;
+ set_pwm_led_color(id, LED_OFF);
+ }
+}
+
+static void update_battery_led(enum pwm_led_id id)
+{
+ /*
+ * Fast Flash = Low Battery
+ * Slow Flash = Normal Battery
+ * LED on = Full Battery
+ * LED off = No Battery
+ */
+ if (battery_is_present() == BP_YES) {
+ int batt_percentage = charge_get_percent();
+
+ if (batt_percentage < LOW_BATTERY_PERCENTAGE) {
+ /* Low Battery:
+ * Flash faster (250 ms period, 100% duty cycle)
+ */
+ pulse_leds(id, LED_FAST_PULSE_PERIOD);
+ } else if (batt_percentage < NORMAL_BATTERY_PERCENTAGE) {
+ /* Normal Battery:
+ * Flash slower (2 second period, 100% duty cycle)
+ */
+ pulse_leds(id, LED_SLOW_PULSE_PERIOD);
+ } else {
+ /* Full Battery: LED ON */
+ rvp_led[id].led_is_pulsing = false;
+ set_pwm_led_color(id, EC_LED_COLOR_GREEN);
+ }
+ } else {
+ /* No Battery: LED OFF */
+ rvp_led[id].led_is_pulsing = false;
+ set_pwm_led_color(id, LED_OFF);
+ }
+}
+
+static void init_rvp_leds_off(void)
+{
+ /* Turn off LEDs such that they are in a known state with zero duty. */
+ set_pwm_led_color(PWM_LED0, LED_OFF);
+ set_pwm_led_color(PWM_LED1, LED_OFF);
+}
+DECLARE_HOOK(HOOK_INIT, init_rvp_leds_off, HOOK_PRIO_POST_PWM);
+
+static void update_led(void)
+{
+ update_battery_led(PWM_LED0);
+ update_charger_led(PWM_LED1);
+}
+DECLARE_HOOK(HOOK_SECOND, update_led, HOOK_PRIO_DEFAULT);
diff --git a/zephyr/projects/intelrvp/src/intelrvp.c b/zephyr/projects/intelrvp/src/intelrvp.c
new file mode 100644
index 0000000000..7098f26cbf
--- /dev/null
+++ b/zephyr/projects/intelrvp/src/intelrvp.c
@@ -0,0 +1,25 @@
+/* Copyright 2022 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* TODO: b/218904113: Convert to using Zephyr GPIOs */
+#include "gpio.h"
+#include "hooks.h"
+
+static void board_init(void)
+{
+ /* Enable SOC SPI */
+ gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(ec_spi_oe_mecc), 1);
+}
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_LAST);
+
+__override void intel_x86_sys_reset_delay(void)
+{
+ /*
+ * From MAX6818 Data sheet, Range of 'Debounce Duaration' is
+ * Minimum - 20 ms, Typical - 40 ms, Maximum - 80 ms.
+ * See b/153128296.
+ */
+ udelay(60 * MSEC);
+}
diff --git a/zephyr/projects/intelrvp/src/usb_pd_policy_mecc_1_1.c b/zephyr/projects/intelrvp/src/usb_pd_policy_mecc_1_1.c
new file mode 100644
index 0000000000..a194b358f1
--- /dev/null
+++ b/zephyr/projects/intelrvp/src/usb_pd_policy_mecc_1_1.c
@@ -0,0 +1,106 @@
+/* Copyright 2022 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "console.h"
+#include "gpio.h"
+#include "intelrvp.h"
+#include "usb_mux.h"
+#include "usbc_ppc.h"
+
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ##args)
+
+static inline void board_pd_set_vbus_discharge(int port, bool enable)
+{
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ ppc_discharge_vbus(port, enable);
+ } else {
+ tcpc_discharge_vbus(port, enable);
+ }
+}
+
+int pd_set_power_supply_ready(int port)
+{
+ int rv;
+
+ /* Disable charging. */
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ rv = ppc_vbus_sink_enable(port, 0);
+ } else {
+ rv = tcpc_config[port].drv->set_snk_ctrl(port, 0);
+ }
+
+ if (rv) {
+ return rv;
+ }
+
+ board_pd_set_vbus_discharge(port, false);
+
+ /* Provide Vbus. */
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ rv = ppc_vbus_source_enable(port, 1);
+ } else {
+ tcpc_config[port].drv->set_src_ctrl(port, 1);
+ }
+
+ if (rv) {
+ return rv;
+ }
+
+ /* Notify host of power info change. */
+ pd_send_host_event(PD_EVENT_POWER_CHANGE);
+
+ return EC_SUCCESS;
+}
+
+void pd_power_supply_reset(int port)
+{
+ int prev_en;
+
+ prev_en = board_vbus_source_enabled(port);
+
+ /* Disable VBUS. */
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ ppc_vbus_source_enable(port, 0);
+ } else {
+ tcpc_config[port].drv->set_src_ctrl(port, 0);
+ }
+
+ /* Enable discharge if we were previously sourcing 5V */
+ if (prev_en) {
+ board_pd_set_vbus_discharge(port, true);
+ }
+
+ /* Notify host of power info change. */
+ pd_send_host_event(PD_EVENT_POWER_CHANGE);
+}
+
+int pd_check_vconn_swap(int port)
+{
+ /* Only allow vconn swap if PP3300 rail is enabled */
+ return gpio_get_level(GPIO_EN_PP3300_A);
+}
+
+int pd_snk_is_vbus_provided(int port)
+{
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ return ppc_is_vbus_present(port);
+ } else {
+ return tcpc_config[port].drv->check_vbus_level(port,
+ VBUS_PRESENT);
+ }
+}
+
+int board_vbus_source_enabled(int port)
+{
+ if (is_typec_port(port)) {
+ if (tcpc_aic_gpios[port].ppc_intr_handler) {
+ return ppc_is_sourcing_vbus(port);
+ } else {
+ return tcpc_config[port].drv->get_src_ctrl(port);
+ }
+ }
+ return 0;
+}