diff options
Diffstat (limited to 'zephyr')
27 files changed, 275 insertions, 588 deletions
diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 8b727e6aca..cbcb0bee4b 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -543,17 +543,15 @@ config PLATFORM_EC_POWER_BUTTON This requires a GPIO named GPIO_POWER_BUTTON_L in gpio_map.h. -config PLATFORM_EC_PWM +config PLATFORM_EC_PWM_HC bool - default y if PWM help - Enable the PWM (Pulse Width Modulation) module. This module is used to - support variable brightness LEDs, backlight controls, and - variable-speed fans. + Enable the PWM (Pulse Width Modulation) host command support. This + implements EC_CMD_PWM_SET_DUTY and EC_CMD_PWM_GET_DUTY. config PLATFORM_EC_PWM_DISPLIGHT bool "PWM display backlight" - depends on PLATFORM_EC_PWM + select PLATFORM_EC_PWM_HC help Enables display backlight controlled by a PWM signal connected directly to the EC chipset. The board devicetree file must define a diff --git a/zephyr/Kconfig.keyboard b/zephyr/Kconfig.keyboard index 8c0a60a620..41805be43b 100644 --- a/zephyr/Kconfig.keyboard +++ b/zephyr/Kconfig.keyboard @@ -189,6 +189,7 @@ choice config PLATFORM_EC_PWM_KBLIGHT bool "PWM keyboard backlight" + select PLATFORM_EC_PWM_HC help Enables a PWM-controlled keyboard backlight controlled by a PWM signal connected directly to the EC chipset. The board devicetree file must diff --git a/zephyr/dts/bindings/pwm/named-pwms.yaml b/zephyr/dts/bindings/pwm/named-pwms.yaml deleted file mode 100644 index 3e5c1c789f..0000000000 --- a/zephyr/dts/bindings/pwm/named-pwms.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2021 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. - -description: PWM KEYS parent node - -compatible: "named-pwms" - -child-binding: - description: Named PWMs child node - properties: - pwms: - type: phandle-array - required: true - frequency: - required: true - type: int - description: PWM frequency, in Hz diff --git a/zephyr/projects/nissa/nereid_generated.dts b/zephyr/projects/nissa/nereid_generated.dts index 2f772a3530..d31fabad92 100644 --- a/zephyr/projects/nissa/nereid_generated.dts +++ b/zephyr/projects/nissa/nereid_generated.dts @@ -238,23 +238,6 @@ enum-name = "I2C_PORT_USB_C0_TCPC"; }; }; - - named-pwms { - compatible = "named-pwms"; - - pwm_pwm_kb_bl: pwm_kb_bl { - pwms = <&pwm0 0 0>; - }; - pwm_pwm_led_1_odl: pwm_led_1_odl { - pwms = <&pwm1 1 1>; - }; - pwm_pwm_led_2_odl: pwm_led_2_odl { - pwms = <&pwm2 2 1>; - }; - pwm_pwm_led_3_odl: pwm_led_3_odl { - pwms = <&pwm3 3 1>; - }; - }; }; &adc0 { @@ -280,19 +263,3 @@ &i2c5 { status = "okay"; }; - -&pwm0 { - status = "okay"; -}; - -&pwm1 { - status = "okay"; -}; - -&pwm2 { - status = "okay"; -}; - -&pwm3 { - status = "okay"; -}; diff --git a/zephyr/projects/nissa/nereid_keyboard.dts b/zephyr/projects/nissa/nereid_keyboard.dts index 8767d23828..a020a92ba9 100644 --- a/zephyr/projects/nissa/nereid_keyboard.dts +++ b/zephyr/projects/nissa/nereid_keyboard.dts @@ -10,3 +10,8 @@ frequency = <10000>; }; }; + +&pwm0 { + status = "okay"; + prescaler-cx = <PWM_PRESCALER_C4>; +}; diff --git a/zephyr/projects/nissa/nereid_overlay.dts b/zephyr/projects/nissa/nereid_overlay.dts index 08b796d0fe..a23c070359 100644 --- a/zephyr/projects/nissa/nereid_overlay.dts +++ b/zephyr/projects/nissa/nereid_overlay.dts @@ -264,36 +264,3 @@ label = "I2C_USB_C0_TCPC"; clock-frequency = <I2C_BITRATE_FAST_PLUS>; }; - -/* PWM config */ -&pwm0 { - prescaler-cx = <PWM_PRESCALER_C4>; -}; - -&pwm1 { - prescaler-cx = <PWM_PRESCALER_C4>; -}; - -&pwm2 { - prescaler-cx = <PWM_PRESCALER_C4>; -}; - -&pwm3 { - prescaler-cx = <PWM_PRESCALER_C4>; -}; - -&pwm_pwm_kb_bl { - frequency = <10000>; -}; - -&pwm_pwm_led_1_odl { - frequency = <324>; -}; - -&pwm_pwm_led_2_odl { - frequency = <324>; -}; - -&pwm_pwm_led_3_odl { - frequency = <324>; -}; diff --git a/zephyr/projects/nissa/nereid_pwm_leds.dts b/zephyr/projects/nissa/nereid_pwm_leds.dts index a25e4487c0..7cd2df01b0 100644 --- a/zephyr/projects/nissa/nereid_pwm_leds.dts +++ b/zephyr/projects/nissa/nereid_pwm_leds.dts @@ -38,3 +38,18 @@ }; }; }; + +&pwm1 { + status = "okay"; + prescaler-cx = <PWM_PRESCALER_C4>; +}; + +&pwm2 { + status = "okay"; + prescaler-cx = <PWM_PRESCALER_C4>; +}; + +&pwm3 { + status = "okay"; + prescaler-cx = <PWM_PRESCALER_C4>; +}; diff --git a/zephyr/projects/nissa/nivviks_generated.dts b/zephyr/projects/nissa/nivviks_generated.dts index ad8d2de0d1..7b3c83886f 100644 --- a/zephyr/projects/nissa/nivviks_generated.dts +++ b/zephyr/projects/nissa/nivviks_generated.dts @@ -227,23 +227,6 @@ enum-name = "I2C_PORT_BATTERY"; }; }; - - named-pwms { - compatible = "named-pwms"; - - pwm_pwm_kb_bl: pwm_kb_bl { - pwms = <&pwm6 6 0>; - }; - pwm_pwm_led_1_odl: pwm_led_1_odl { - pwms = <&pwm2 2 1>; - }; - pwm_pwm_led_2_odl: pwm_led_2_odl { - pwms = <&pwm0 0 1>; - }; - pwm_pwm_led_3_odl: pwm_led_3_odl { - pwms = <&pwm1 1 1>; - }; - }; }; &adc0 { @@ -289,19 +272,3 @@ &i2c_ctrl7 { status = "okay"; }; - -&pwm0 { - status = "okay"; -}; - -&pwm1 { - status = "okay"; -}; - -&pwm2 { - status = "okay"; -}; - -&pwm6 { - status = "okay"; -}; diff --git a/zephyr/projects/nissa/nivviks_keyboard.dts b/zephyr/projects/nissa/nivviks_keyboard.dts index bb3dd9067e..b1b4b5af8b 100644 --- a/zephyr/projects/nissa/nivviks_keyboard.dts +++ b/zephyr/projects/nissa/nivviks_keyboard.dts @@ -10,3 +10,7 @@ frequency = <10000>; }; }; + +&pwm6 { + status = "okay"; +}; diff --git a/zephyr/projects/nissa/nivviks_overlay.dts b/zephyr/projects/nissa/nivviks_overlay.dts index 63c857cf6d..ad335f80e1 100644 --- a/zephyr/projects/nissa/nivviks_overlay.dts +++ b/zephyr/projects/nissa/nivviks_overlay.dts @@ -287,37 +287,6 @@ clock-frequency = <I2C_BITRATE_STANDARD>; }; -/* PWM frequencies */ - -&pwm_pwm_kb_bl { - frequency = <10000>; -}; - -&pwm_pwm_led_1_odl { - frequency = <324>; -}; - -&pwm_pwm_led_2_odl { - frequency = <324>; -}; - -&pwm_pwm_led_3_odl { - frequency = <324>; -}; - -/* Enable LEDs to work while CPU suspended */ -&pwm0 { - clock-bus = "NPCX_CLOCK_BUS_LFCLK"; -}; - -&pwm1 { - clock-bus = "NPCX_CLOCK_BUS_LFCLK"; -}; - -&pwm2 { - clock-bus = "NPCX_CLOCK_BUS_LFCLK"; -}; - &pwm5 { status = "okay"; drive-open-drain; diff --git a/zephyr/projects/nissa/nivviks_pwm_leds.dts b/zephyr/projects/nissa/nivviks_pwm_leds.dts index de76880cbe..36a5a5b461 100644 --- a/zephyr/projects/nissa/nivviks_pwm_leds.dts +++ b/zephyr/projects/nissa/nivviks_pwm_leds.dts @@ -38,3 +38,20 @@ }; }; }; + +/* Enable LEDs to work while CPU suspended */ + +&pwm0 { + status = "okay"; + clock-bus = "NPCX_CLOCK_BUS_LFCLK"; +}; + +&pwm1 { + status = "okay"; + clock-bus = "NPCX_CLOCK_BUS_LFCLK"; +}; + +&pwm2 { + status = "okay"; + clock-bus = "NPCX_CLOCK_BUS_LFCLK"; +}; diff --git a/zephyr/projects/skyrim/power_signals.c b/zephyr/projects/skyrim/power_signals.c index f36f2ae576..e85cea1f04 100644 --- a/zephyr/projects/skyrim/power_signals.c +++ b/zephyr/projects/skyrim/power_signals.c @@ -81,10 +81,10 @@ DECLARE_HOOK(HOOK_INIT, baseboard_init, HOOK_PRIO_POST_I2C); /** * b/227296844: On G3->S5, wait for RSMRST_L to be deasserted before asserting - * PCH_PWRBTN_L. This typically takes 32-35 ms in testing. Then wait an + * PCH_PWRBTN_L. This can be as long as ~65ms after cold boot. Then wait an * additional delay of T1a defined in the EDS before changing the power button. */ -#define RSMRST_WAIT_DELAY 40 +#define RSMRST_WAIT_DELAY 70 #define EDS_PWR_BTN_RSMRST_T1A_DELAY 16 void board_pwrbtn_to_pch(int level) { diff --git a/zephyr/shim/include/board.h b/zephyr/shim/include/board.h index 74caadfeef..39d80a3495 100644 --- a/zephyr/shim/include/board.h +++ b/zephyr/shim/include/board.h @@ -25,10 +25,6 @@ #include "i2c/i2c.h" #endif -#ifdef CONFIG_PWM -#include "pwm/pwm.h" -#endif - /* Include board specific sensor configuration if motionsense is enabled */ #ifdef CONFIG_MOTIONSENSE #include "motionsense_sensors_defs.h" diff --git a/zephyr/shim/include/config_chip.h b/zephyr/shim/include/config_chip.h index 5f865c3fa7..4b84447aa5 100644 --- a/zephyr/shim/include/config_chip.h +++ b/zephyr/shim/include/config_chip.h @@ -871,11 +871,6 @@ extern struct jump_data mock_jump_data; #define CONFIG_FAKE_SHMEM #endif -#undef CONFIG_PWM -#ifdef CONFIG_PLATFORM_EC_PWM -#define CONFIG_PWM -#endif - #undef CONFIG_CMD_S5_TIMEOUT #ifdef CONFIG_PLATFORM_EC_CONSOLE_CMD_S5_TIMEOUT #define CONFIG_CMD_S5_TIMEOUT diff --git a/zephyr/shim/include/pwm/pwm.h b/zephyr/shim/include/pwm/pwm.h deleted file mode 100644 index 1bf4685837..0000000000 --- a/zephyr/shim/include/pwm/pwm.h +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright 2021 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. - */ - -#ifndef ZEPHYR_SHIM_INCLUDE_PWM_PWM_H_ -#define ZEPHYR_SHIM_INCLUDE_PWM_PWM_H_ - -#include <device.h> -#include <devicetree.h> - -#if DT_NODE_EXISTS(DT_PATH(named_pwms)) - -#define PWM_CHANNEL(id) DT_CAT(PWM_, id) -#define PWM_CHANNEL_WITH_COMMA(id) PWM_CHANNEL(id), - -enum pwm_channel { - DT_FOREACH_CHILD(DT_PATH(named_pwms), PWM_CHANNEL_WITH_COMMA) - PWM_CH_COUNT, -}; - -#define NAMED_PWM(name) PWM_CHANNEL(DT_PATH(named_pwms, name)) - -#endif /* named_pwms */ - -#endif /* ZEPHYR_SHIM_INCLUDE_PWM_PWM_H_ */ diff --git a/zephyr/shim/src/CMakeLists.txt b/zephyr/shim/src/CMakeLists.txt index bc9729abae..23aacc177f 100644 --- a/zephyr/shim/src/CMakeLists.txt +++ b/zephyr/shim/src/CMakeLists.txt @@ -43,7 +43,7 @@ zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_MKBP_EVENT mkbp_event.c) zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_MOTIONSENSE motionsense_sensors.c) zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_PANIC panic.c) -zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_PWM pwm.c) +zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_PWM_HC pwm_hc.c) zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_LED_PWM pwm_led.c) zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_RTC rtc.c) zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_SWITCHCAP_GPIO diff --git a/zephyr/shim/src/pwm.c b/zephyr/shim/src/pwm.c deleted file mode 100644 index 5f76878b12..0000000000 --- a/zephyr/shim/src/pwm.c +++ /dev/null @@ -1,295 +0,0 @@ -/* Copyright 2021 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. - */ - -#include <device.h> -#include <devicetree.h> -#include <drivers/pwm.h> -#include <logging/log.h> - -#include "common.h" -#include "console.h" -#include "drivers/cros_displight.h" -#include "ec_commands.h" -#include "host_command.h" -#include "pwm.h" -#include "keyboard_backlight.h" -#include "util.h" - -#include "pwm/pwm.h" - -LOG_MODULE_REGISTER(pwm_shim, LOG_LEVEL_ERR); - -#define PWM_RAW_TO_PERCENT(v) \ - DIV_ROUND_NEAREST((uint32_t)(v) * 100, UINT16_MAX) -#define PWM_PERCENT_TO_RAW(v) ((uint32_t)(v) * UINT16_MAX / 100) - -/* TODO(b/217741090): drop the PWM shim code once all callers have been - * converted. - */ -#if DT_HAS_COMPAT_STATUS_OKAY(named_pwms) - -/* - * Initialize the device bindings in pwm_channels. - * This macro is called from within DT_FOREACH_CHILD - */ -#define INIT_DEV_BINDING(id) { \ - pwm_configs[PWM_CHANNEL(id)].name = DT_NODE_FULL_NAME(id); \ - pwm_configs[PWM_CHANNEL(id)].dev = DEVICE_DT_GET( \ - DT_PHANDLE(id, pwms)); \ - pwm_configs[PWM_CHANNEL(id)].pin = DT_PWMS_CHANNEL(id); \ - pwm_configs[PWM_CHANNEL(id)].flags = DT_PWMS_FLAGS(id); \ - pwm_configs[PWM_CHANNEL(id)].freq = DT_PROP(id, frequency); \ - } - -struct pwm_config { - /* Name */ - const char *name; - /* PWM pin */ - uint32_t pin; - /* PWM channel flags. See dt-bindings/pwm/pwm.h */ - pwm_flags_t flags; - /* PWM operating frequency. Configured by the devicetree */ - uint32_t freq; - - /* PWM period in microseconds. Automatically set to 1/frequency */ - uint32_t period_us; - /* PWM pulse in microseconds. Set by pwm_set_raw_duty */ - uint32_t pulse_us; - /* Saves whether the PWM channel is currently enabled */ - bool enabled; - - /* Runtime device for PWM */ - const struct device *dev; -}; - -static struct pwm_config pwm_configs[PWM_CH_COUNT]; - -static int init_pwms(const struct device *unused) -{ - struct pwm_config *pwm; - int rv = 0; - - ARG_UNUSED(unused); - - /* Initialize PWM data from the device tree */ - DT_FOREACH_CHILD(DT_PATH(named_pwms), INIT_DEV_BINDING) - - /* Read the PWM operating frequency, set by the chip driver */ - for (size_t i = 0; i < PWM_CH_COUNT; ++i) { - pwm = &pwm_configs[i]; - - if (pwm->dev == NULL) { - LOG_ERR("Not found (%s)", pwm->name); - rv = -ENODEV; - continue; - } - - /* - * TODO - check that devicetree frequency is less than 1/2 - * max frequency from the chip driver. - */ - pwm->period_us = USEC_PER_SEC / pwm->freq; - } - - return rv; -} -#if CONFIG_PLATFORM_EC_PWM_INIT_PRIORITY <= CONFIG_KERNEL_INIT_PRIORITY_DEVICE -#error "PWM init priority must be > KERNEL_INIT_PRIORITY_DEVICE" -#endif -SYS_INIT(init_pwms, PRE_KERNEL_1, CONFIG_PLATFORM_EC_PWM_INIT_PRIORITY); - -static struct pwm_config* pwm_lookup(enum pwm_channel ch) -{ - __ASSERT(ch < ARRAY_SIZE(pwm_configs), "Invalid PWM channel %d", ch); - - return &pwm_configs[ch]; -} - -void pwm_enable(enum pwm_channel ch, int enabled) -{ - struct pwm_config *pwm; - uint32_t pulse_us; - int rv; - - pwm = pwm_lookup(ch); - pwm->enabled = enabled; - - /* - * The Zephyr API doesn't provide explicit enable and disable - * commands. However, setting the pulse width to zero disables - * the PWM. - */ - if (enabled) - pulse_us = pwm->pulse_us; - else - pulse_us = 0; - - rv = pwm_pin_set_usec(pwm->dev, pwm->pin, pwm->period_us, pulse_us, - pwm->flags); - - if (rv) - LOG_ERR("pwm_pin_set_usec() failed %s (%d)", pwm->name, rv); -} - -int pwm_get_enabled(enum pwm_channel ch) -{ - struct pwm_config *pwm; - - pwm = pwm_lookup(ch); - return pwm->enabled; -} - -void pwm_set_raw_duty(enum pwm_channel ch, uint16_t duty) -{ - struct pwm_config *pwm; - int rv; - - pwm = pwm_lookup(ch); - - pwm->pulse_us = - DIV_ROUND_NEAREST(pwm->period_us * duty, EC_PWM_MAX_DUTY); - - LOG_DBG("PWM %s set raw duty (0x%04x), pulse %d", pwm->name, duty, - pwm->pulse_us); - - rv = pwm_pin_set_usec(pwm->dev, pwm->pin, pwm->period_us, pwm->pulse_us, - pwm->flags); - - if (rv) - LOG_ERR("pwm_pin_set_usec() failed %s (%d)", pwm->name, rv); -} - -uint16_t pwm_get_raw_duty(enum pwm_channel ch) -{ - struct pwm_config *pwm; - - pwm = pwm_lookup(ch); - - return DIV_ROUND_NEAREST(pwm->pulse_us * EC_PWM_MAX_DUTY, - pwm->period_us); -} - -void pwm_set_duty(enum pwm_channel ch, int percent) -{ - struct pwm_config *pwm; - int rv; - - pwm = pwm_lookup(ch); - - pwm->pulse_us = DIV_ROUND_NEAREST(pwm->period_us * percent, 100); - - LOG_DBG("PWM %s set percent (%d), pulse %d", pwm->name, percent, - pwm->pulse_us); - - rv = pwm_pin_set_usec(pwm->dev, pwm->pin, pwm->period_us, pwm->pulse_us, - pwm->flags); - - if (rv) - LOG_ERR("pwm_pin_set_usec() failed %s (%d)", pwm->name, rv); -} - -int pwm_get_duty(enum pwm_channel ch) -{ - struct pwm_config *pwm; - - pwm = pwm_lookup(ch); - - return DIV_ROUND_NEAREST(pwm->pulse_us * 100, pwm->period_us); -} - -#endif /* DT_HAS_COMPAT_STATUS_OKAY(named_pwm) */ - -#define HAS_PWM_GENERIC_CHANNEL(compat) \ - DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(compat), \ - generic_pwm_channel) - -#define PWM_GENERIC_CHANNEL_ID(compat) \ - DT_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(compat), \ - generic_pwm_channel) - -#ifdef CONFIG_PWM_KBLIGHT -static bool pwm_is_kblight(int type, int index) -{ - if (type == EC_PWM_TYPE_KB_LIGHT) - return true; - -#if HAS_PWM_GENERIC_CHANNEL(cros_ec_kblight_pwm) - if (type == EC_PWM_TYPE_GENERIC && - index == PWM_GENERIC_CHANNEL_ID(cros_ec_kblight_pwm)) - return true; -#endif /* HAS_PWM_GENERIC_CHANNEL(cros_ec_kblight_pwm) */ - - return false; -} -#endif /* CONFIG_PWM_KBLIGHT */ - -#ifdef CONFIG_PLATFORM_EC_PWM_DISPLIGHT -static bool pwm_is_displight(int type, int index) -{ - if (type == EC_PWM_TYPE_DISPLAY_LIGHT) - return true; - -#if HAS_PWM_GENERIC_CHANNEL(cros_ec_displight) - if (type == EC_PWM_TYPE_GENERIC && - index == PWM_GENERIC_CHANNEL_ID(cros_ec_displight)) - return true; -#endif /* HAS_PWM_GENERIC_CHANNEL(cros_ec_displight) */ - - return false; -} -#endif /* CONFIG_PLATFORM_EC_PWM_DISPLIGHT */ - - -static enum ec_status host_command_pwm_set_duty( - struct host_cmd_handler_args *args) -{ - __maybe_unused const struct ec_params_pwm_set_duty *p = args->params; - -#ifdef CONFIG_PLATFORM_EC_PWM_KBLIGHT - if (pwm_is_kblight(p->pwm_type, p->index)) { - kblight_set(PWM_RAW_TO_PERCENT(p->duty)); - kblight_enable(p->duty > 0); - return EC_RES_SUCCESS; - } -#endif -#ifdef CONFIG_PLATFORM_EC_PWM_DISPLIGHT - if (pwm_is_displight(p->pwm_type, p->index)) { - displight_set(PWM_RAW_TO_PERCENT(p->duty)); - return EC_RES_SUCCESS; - } -#endif - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_DUTY, - host_command_pwm_set_duty, - EC_VER_MASK(0)); - -static enum ec_status host_command_pwm_get_duty( - struct host_cmd_handler_args *args) -{ - __maybe_unused const struct ec_params_pwm_get_duty *p = args->params; - __maybe_unused struct ec_response_pwm_get_duty *r = args->response; - -#ifdef CONFIG_PLATFORM_EC_PWM_KBLIGHT - if (pwm_is_kblight(p->pwm_type, p->index)) { - r->duty = PWM_PERCENT_TO_RAW(kblight_get()); - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - } -#endif -#ifdef CONFIG_PLATFORM_EC_PWM_DISPLIGHT - if (pwm_is_displight(p->pwm_type, p->index)) { - r->duty = PWM_PERCENT_TO_RAW(displight_get()); - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - } -#endif - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_DUTY, - host_command_pwm_get_duty, - EC_VER_MASK(0)); diff --git a/zephyr/shim/src/pwm_hc.c b/zephyr/shim/src/pwm_hc.c new file mode 100644 index 0000000000..3aaba20bc4 --- /dev/null +++ b/zephyr/shim/src/pwm_hc.c @@ -0,0 +1,117 @@ +/* Copyright 2021 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. + */ + +#include <device.h> +#include <devicetree.h> +#include <drivers/pwm.h> +#include <logging/log.h> + +#include "common.h" +#include "console.h" +#include "drivers/cros_displight.h" +#include "ec_commands.h" +#include "host_command.h" +#include "pwm.h" +#include "keyboard_backlight.h" +#include "util.h" + +LOG_MODULE_REGISTER(pwm_shim, LOG_LEVEL_ERR); + +#define PWM_RAW_TO_PERCENT(v) \ + DIV_ROUND_NEAREST((uint32_t)(v) * 100, UINT16_MAX) +#define PWM_PERCENT_TO_RAW(v) ((uint32_t)(v) * UINT16_MAX / 100) + +#define HAS_PWM_GENERIC_CHANNEL(compat) \ + DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(compat), \ + generic_pwm_channel) + +#define PWM_GENERIC_CHANNEL_ID(compat) \ + DT_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(compat), \ + generic_pwm_channel) + +#ifdef CONFIG_PWM_KBLIGHT +static bool pwm_is_kblight(int type, int index) +{ + if (type == EC_PWM_TYPE_KB_LIGHT) + return true; + +#if HAS_PWM_GENERIC_CHANNEL(cros_ec_kblight_pwm) + if (type == EC_PWM_TYPE_GENERIC && + index == PWM_GENERIC_CHANNEL_ID(cros_ec_kblight_pwm)) + return true; +#endif /* HAS_PWM_GENERIC_CHANNEL(cros_ec_kblight_pwm) */ + + return false; +} +#endif /* CONFIG_PWM_KBLIGHT */ + +#ifdef CONFIG_PLATFORM_EC_PWM_DISPLIGHT +static bool pwm_is_displight(int type, int index) +{ + if (type == EC_PWM_TYPE_DISPLAY_LIGHT) + return true; + +#if HAS_PWM_GENERIC_CHANNEL(cros_ec_displight) + if (type == EC_PWM_TYPE_GENERIC && + index == PWM_GENERIC_CHANNEL_ID(cros_ec_displight)) + return true; +#endif /* HAS_PWM_GENERIC_CHANNEL(cros_ec_displight) */ + + return false; +} +#endif /* CONFIG_PLATFORM_EC_PWM_DISPLIGHT */ + + +static enum ec_status host_command_pwm_set_duty( + struct host_cmd_handler_args *args) +{ + __maybe_unused const struct ec_params_pwm_set_duty *p = args->params; + +#ifdef CONFIG_PLATFORM_EC_PWM_KBLIGHT + if (pwm_is_kblight(p->pwm_type, p->index)) { + kblight_set(PWM_RAW_TO_PERCENT(p->duty)); + kblight_enable(p->duty > 0); + return EC_RES_SUCCESS; + } +#endif +#ifdef CONFIG_PLATFORM_EC_PWM_DISPLIGHT + if (pwm_is_displight(p->pwm_type, p->index)) { + displight_set(PWM_RAW_TO_PERCENT(p->duty)); + return EC_RES_SUCCESS; + } +#endif + + return EC_RES_INVALID_PARAM; +} +DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_DUTY, + host_command_pwm_set_duty, + EC_VER_MASK(0)); + +static enum ec_status host_command_pwm_get_duty( + struct host_cmd_handler_args *args) +{ + __maybe_unused const struct ec_params_pwm_get_duty *p = args->params; + __maybe_unused struct ec_response_pwm_get_duty *r = args->response; + +#ifdef CONFIG_PLATFORM_EC_PWM_KBLIGHT + if (pwm_is_kblight(p->pwm_type, p->index)) { + r->duty = PWM_PERCENT_TO_RAW(kblight_get()); + args->response_size = sizeof(*r); + return EC_RES_SUCCESS; + } +#endif +#ifdef CONFIG_PLATFORM_EC_PWM_DISPLIGHT + if (pwm_is_displight(p->pwm_type, p->index)) { + r->duty = PWM_PERCENT_TO_RAW(displight_get()); + args->response_size = sizeof(*r); + return EC_RES_SUCCESS; + } +#endif + + return EC_RES_INVALID_PARAM; +} +DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_DUTY, + host_command_pwm_get_duty, + EC_VER_MASK(0)); diff --git a/zephyr/subsys/ap_pwrseq/include/signal_vw.h b/zephyr/subsys/ap_pwrseq/include/signal_vw.h index bb05d15a71..d005daaa40 100644 --- a/zephyr/subsys/ap_pwrseq/include/signal_vw.h +++ b/zephyr/subsys/ap_pwrseq/include/signal_vw.h @@ -45,11 +45,4 @@ int power_signal_vw_get(enum pwr_sig_vw vw); */ void power_signal_vw_init(void); -/** - * @brief External notification when the bus is ready or not. - * - * @param ready true When signals are valid, false when bus is not ready. - */ -void notify_espi_ready(bool ready); - #endif /* __AP_PWRSEQ_SIGNAL_VW_H__ */ diff --git a/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h b/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h index 6eee6ce140..9b6c60b53c 100644 --- a/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h +++ b/zephyr/subsys/ap_pwrseq/include/x86_common_pwrseq.h @@ -18,9 +18,6 @@ struct pwrseq_context { enum power_states_ndsx power_state; /* Indicate should exit G3 power state or not */ bool want_g3_exit; -#if defined(PWRSEQ_REQUIRE_ESPI) - bool espi_ready; -#endif }; diff --git a/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h b/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h index 8ec4770e99..1832c4877c 100644 --- a/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h +++ b/zephyr/subsys/ap_pwrseq/include/x86_power_signals.h @@ -24,16 +24,6 @@ #define IN_ALL_S0_VALUE IN_PGOOD_ALL_CORE #define CHIPSET_G3S5_POWERUP_SIGNAL IN_PCH_SLP_SUS_DEASSERTED -#if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S3) || \ - defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S4) || \ - defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S5) -/* - * Set if ESPI signals are required, so need to check - * whether ESPI is ready or not - */ -#define PWRSEQ_REQUIRE_ESPI -#endif - #else #warning("Input power signals state flags not defined"); #endif diff --git a/zephyr/subsys/ap_pwrseq/power_host_sleep.c b/zephyr/subsys/ap_pwrseq/power_host_sleep.c index 13e6aba765..2e7df63c51 100644 --- a/zephyr/subsys/ap_pwrseq/power_host_sleep.c +++ b/zephyr/subsys/ap_pwrseq/power_host_sleep.c @@ -203,7 +203,7 @@ void ap_power_chipset_handle_host_sleep_event( * notification needs to be sent to listeners. */ ap_power_sleep_set_notify(AP_POWER_SLEEP_SUSPEND); - power_signal_enable_interrupt(PWR_SLP_S0); + power_signal_enable(PWR_SLP_S0); } else if (state == HOST_SLEEP_EVENT_S0IX_RESUME) { /* @@ -212,7 +212,7 @@ void ap_power_chipset_handle_host_sleep_event( */ ap_power_sleep_set_notify(AP_POWER_SLEEP_RESUME); power_s0ix_resume_restore_masks(); - power_signal_disable_interrupt(PWR_SLP_S0); + power_signal_disable(PWR_SLP_S0); /* * If the sleep signal timed out and never transitioned, then @@ -223,7 +223,7 @@ void ap_power_chipset_handle_host_sleep_event( power_update_wake_mask(); } else if (state == HOST_SLEEP_EVENT_DEFAULT_RESET) { - power_signal_disable_interrupt(PWR_SLP_S0); + power_signal_disable(PWR_SLP_S0); } #endif /* CONFIG_AP_PWRSEQ_S0IX */ } diff --git a/zephyr/subsys/ap_pwrseq/power_signals.c b/zephyr/subsys/ap_pwrseq/power_signals.c index c463e8e82e..7df7b3ef00 100644 --- a/zephyr/subsys/ap_pwrseq/power_signals.c +++ b/zephyr/subsys/ap_pwrseq/power_signals.c @@ -86,16 +86,18 @@ DT_FOREACH_STATUS_OKAY(intel_ap_pwrseq_external, PWR_SIGNAL_POLLED) }; /* - * Bitmask of power signals updated via interrupt. + * Bitmasks of power signals. A previous copy is held so that + * logging of changes can occur if the signal is in the debug mask. */ -static atomic_t interrupt_power_signals; +static atomic_t power_signals, prev_power_signals; -static power_signal_mask_t output_signals; static power_signal_mask_t debug_signals; void power_set_debug(power_signal_mask_t debug) { debug_signals = debug; + /* Copy the current values */ + atomic_set(&prev_power_signals, atomic_get(&power_signals)); } power_signal_mask_t power_get_debug(void) @@ -103,12 +105,19 @@ power_signal_mask_t power_get_debug(void) return debug_signals; } -static inline void check_debug(power_signal_mask_t mask, - enum power_signal signal, - int value) +static inline void check_debug(enum power_signal signal) { - if (debug_signals & mask) { - LOG_INF("%s -> %d", power_signal_name(signal), value); + /* + * Only check for debug display if the logging level requires it. + */ + if ((CONFIG_AP_PWRSEQ_LOG_LEVEL >= LOG_LEVEL_INF) && + (debug_signals & POWER_SIGNAL_MASK(signal))) { + bool value = atomic_test_bit(&power_signals, signal); + + if (value != atomic_test_bit(&prev_power_signals, signal)) { + LOG_INF("%s -> %d", power_signal_name(signal), value); + atomic_set_bit_to(&prev_power_signals, signal, value); + } } } @@ -121,14 +130,13 @@ power_signal_mask_t power_get_signals(void) mask |= POWER_SIGNAL_MASK(polled_signals[i]); } } - return mask | output_signals | - atomic_get(&interrupt_power_signals); + return mask | atomic_get(&power_signals); } void power_signal_interrupt(enum power_signal signal, int value) { - atomic_set_bit_to(&interrupt_power_signals, signal, value); - check_debug(POWER_SIGNAL_MASK(signal), signal, value); + atomic_set_bit_to(&power_signals, signal, value); + check_debug(signal); } int power_wait_mask_signals_timeout(power_signal_mask_t mask, @@ -209,19 +217,11 @@ int power_signal_set(enum power_signal signal, int value) #endif } /* - * Output succeeded, update output mask. + * Output succeeded, update mask. */ if (ret == 0) { - power_signal_mask_t mask = POWER_SIGNAL_MASK(signal); - power_signal_mask_t old = output_signals; - - if (value) - output_signals |= mask; - else - output_signals &= ~mask; - if (old != output_signals) { - check_debug(mask, signal, value); - } + atomic_set_bit_to(&power_signals, signal, value); + check_debug(signal); } return ret; } @@ -295,4 +295,26 @@ void power_signal_init(void) if (IS_ENABLED(HAS_ADC_SIGNALS)) { power_signal_adc_init(); } + /* + * Initialise the mask with the current values. + * This includes the outputs as well. + */ + for (int i = 0; i < POWER_SIGNAL_COUNT; i++) { + if (power_signal_get(i) == 1) { + atomic_set_bit(&power_signals, i); + } + } + /* + * Some signals are polled (such as the board external signals), + * so clear these values from the initial state so they + * don't get OR'ed in later on. + */ + for (int i = 0; i < ARRAY_SIZE(polled_signals); i++) { + atomic_clear_bit(&power_signals, i); + } + /* + * Save the current state so that new changes can be + * checked against the debug mask. + */ + atomic_set(&prev_power_signals, atomic_get(&power_signals)); } diff --git a/zephyr/subsys/ap_pwrseq/signal_adc.c b/zephyr/subsys/ap_pwrseq/signal_adc.c index 9898c8e4bd..17038ba2ea 100644 --- a/zephyr/subsys/ap_pwrseq/signal_adc.c +++ b/zephyr/subsys/ap_pwrseq/signal_adc.c @@ -175,6 +175,10 @@ void power_signal_adc_init(void) /* Set high and low trigger callbacks */ sensor_trigger_set(config[i].dev_trig_high, &trig, high_cb[i]); sensor_trigger_set(config[i].dev_trig_low, &trig, low_cb[i]); + /* + * TODO: Get current value and initialise adc_state. + * + */ power_signal_adc_enable(i); } } diff --git a/zephyr/subsys/ap_pwrseq/signal_gpio.c b/zephyr/subsys/ap_pwrseq/signal_gpio.c index b4888f8cff..e28df96e6b 100644 --- a/zephyr/subsys/ap_pwrseq/signal_gpio.c +++ b/zephyr/subsys/ap_pwrseq/signal_gpio.c @@ -6,7 +6,7 @@ #include <power_signals.h> #include <signal_gpio.h> #include <drivers/gpio.h> -#include "system.h" +#include "sysjump.h" #define MY_COMPAT intel_ap_pwrseq_gpio @@ -121,8 +121,11 @@ void power_signal_gpio_init(void) /* * If there has been a sysjump, do not set the output * to the deasserted state. + * We can't use system_jumped_late() since that is not + * initialised at this point. */ - gpio_flags_t out_flags = system_jumped_late() ? + struct jump_data *jdata = get_jump_data(); + gpio_flags_t out_flags = (jdata && jdata->magic == JUMP_DATA_MAGIC) ? GPIO_OUTPUT : GPIO_OUTPUT_INACTIVE; for (int i = 0; i < ARRAY_SIZE(gpio_config); i++) { diff --git a/zephyr/subsys/ap_pwrseq/signal_vw.c b/zephyr/subsys/ap_pwrseq/signal_vw.c index b0abe4fe13..a42be8ae85 100644 --- a/zephyr/subsys/ap_pwrseq/signal_vw.c +++ b/zephyr/subsys/ap_pwrseq/signal_vw.c @@ -3,6 +3,7 @@ * found in the LICENSE file. */ +#include <atomic.h> #include <drivers/espi.h> #include <x86_non_dsx_common_pwrseq_sm_handler.h> @@ -34,18 +35,20 @@ const static struct vw_config vw_config[] = { DT_FOREACH_STATUS_OKAY(MY_COMPAT, INIT_ESPI_SIGNAL) }; -static bool signal_data[ARRAY_SIZE(vw_config)]; +/* + * Current signal value. + */ +static atomic_t signal_data; +/* + * Mask of valid signals. If the bus is reset, this is cleared, + * and when a signal is updated the associated bit is set to indicate + * the signal is valid. + */ +static atomic_t signal_valid; #define espi_dev DEVICE_DT_GET(DT_CHOSEN(intel_ap_pwrseq_espi)) -/* - * Mask of updated signals. If the bus is reset, this is cleared, - * and it is only when all the signals have been updated that - * notification is sent that the signals are ready. - */ -static uint8_t espi_mask; -static bool espi_not_valid; -BUILD_ASSERT(ARRAY_SIZE(vw_config) <= 8); +BUILD_ASSERT(ARRAY_SIZE(vw_config) <= (sizeof(atomic_t) * 8)); static void espi_handler(const struct device *dev, struct espi_callback *cb, @@ -61,53 +64,35 @@ static void espi_handler(const struct device *dev, case ESPI_BUS_RESET: /* - * Notify that the bus isn't ready, and clear - * the signal mask. + * Clear the signal valid mask. */ - notify_espi_ready(false); - espi_mask = 0; - espi_not_valid = true; + atomic_clear(&signal_valid); break; case ESPI_BUS_EVENT_VWIRE_RECEIVED: for (int i = 0; i < ARRAY_SIZE(vw_config); i++) { if (event.evt_details == vw_config[i].espi_signal) { - int value = vw_config[i].invert + bool value = vw_config[i].invert ? !event.evt_data : !!event.evt_data; - signal_data[i] = value; - if (espi_not_valid) { - espi_mask |= BIT(i); - } + atomic_set_bit_to(&signal_data, i, value); + atomic_set_bit(&signal_valid, i); power_signal_interrupt(vw_config[i].signal, value); } } - /* - * When all the signals have been updated, notify that - * the ESPI signals are valid. - */ - if (espi_not_valid && - espi_mask == BIT_MASK(ARRAY_SIZE(vw_config))) { - espi_not_valid = false; - LOG_DBG("ESPI signals valid"); - /* - * TODO(b/222946923): Convert to generalised - * callback pattern. - */ - notify_espi_ready(true); - } break; } } int power_signal_vw_get(enum pwr_sig_vw vw) { - if (vw < 0 || vw >= ARRAY_SIZE(vw_config)) { + if (vw < 0 || vw >= ARRAY_SIZE(vw_config) || + !atomic_test_bit(&signal_valid, vw)) { return -EINVAL; } - return signal_data[vw]; + return atomic_test_bit(&signal_data, vw); } void power_signal_vw_init(void) @@ -138,6 +123,24 @@ void power_signal_vw_init(void) ESPI_BUS_RESET | ESPI_BUS_EVENT_VWIRE_RECEIVED); espi_add_callback(espi_dev, &espi_cb); + /* + * Check whether the bus is ready, and if so, + * initialise the current values of the signals. + */ + if (espi_get_channel_status(espi_dev, ESPI_CHANNEL_VWIRE)) { + for (int i = 0; i < ARRAY_SIZE(vw_config); i++) { + uint8_t vw_value; + + if (espi_receive_vwire(espi_dev, + vw_config[i].espi_signal, + &vw_value) == 0) { + atomic_set_bit_to(&signal_data, i, + vw_config[i].invert + ? !vw_value + : !!vw_value); + } + } + } } #endif /* HAS_VW_SIGNALS */ diff --git a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c index 34c3d5df18..bcc34bd32d 100644 --- a/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c +++ b/zephyr/subsys/ap_pwrseq/x86_non_dsx_common_pwrseq_sm_handler.c @@ -42,34 +42,26 @@ const char pwrsm_dbg[][25] = { #endif }; -#ifdef PWRSEQ_REQUIRE_ESPI - -void notify_espi_ready(bool ready) -{ - pwrseq_ctx.espi_ready = ready; -} -#endif - /* * Returns true if all signals in mask are valid. + * This is only done for virtual wire signals. */ static inline bool signals_valid(power_signal_mask_t signals) { -#ifdef PWRSEQ_REQUIRE_ESPI - if (!pwrseq_ctx.espi_ready) { #if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S3) - if (signals & POWER_SIGNAL_MASK(PWR_SLP_S3)) - return false; + if ((signals & POWER_SIGNAL_MASK(PWR_SLP_S3)) && + power_signal_get(PWR_SLP_S3) < 0) + return false; #endif #if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S4) - if (signals & POWER_SIGNAL_MASK(PWR_SLP_S3)) - return false; + if ((signals & POWER_SIGNAL_MASK(PWR_SLP_S4)) && + power_signal_get(PWR_SLP_S4) < 0) + return false; #endif #if defined(CONFIG_PLATFORM_EC_ESPI_VW_SLP_S5) - if (signals & POWER_SIGNAL_MASK(PWR_SLP_S3)) - return false; -#endif - } + if ((signals & POWER_SIGNAL_MASK(PWR_SLP_S5)) && + power_signal_get(PWR_SLP_S5) < 0) + return false; #endif return true; } @@ -526,4 +518,8 @@ static int pwrseq_init(const struct device *dev) return 0; } +/* + * The initialisation must occur after system I/O initialisation that + * the signals depend upon, such as GPIO, ADC etc. + */ SYS_INIT(pwrseq_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); |