diff options
Diffstat (limited to 'zephyr/projects/intelrvp/src')
-rw-r--r-- | zephyr/projects/intelrvp/src/chg_usb_pd.c | 129 | ||||
-rw-r--r-- | zephyr/projects/intelrvp/src/chg_usb_pd_mecc_1_1.c | 92 | ||||
-rw-r--r-- | zephyr/projects/intelrvp/src/intel_rvp_board_id.c | 30 | ||||
-rw-r--r-- | zephyr/projects/intelrvp/src/intel_rvp_led.c | 168 | ||||
-rw-r--r-- | zephyr/projects/intelrvp/src/intelrvp.c | 25 | ||||
-rw-r--r-- | zephyr/projects/intelrvp/src/usb_pd_policy_mecc_1_1.c | 106 |
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; +} |