diff options
author | Keith Short <keithshort@chromium.org> | 2020-12-15 15:13:10 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-01-29 21:57:44 +0000 |
commit | 9a2920efeffc04953ce01489ef1a5669e8662232 (patch) | |
tree | 5c0120bc2785a0b92ae8b5fa9ded524078d34205 | |
parent | 40b2572cf845020c9380fb7052af5a2d804ac1c9 (diff) | |
download | chrome-ec-9a2920efeffc04953ce01489ef1a5669e8662232.tar.gz |
zephyr: add PWM shim
Adds PWM shim to connect the platform/ec PWM API to the Zephyr PWM API.
BUG=b:174850923
BRANCH=none
TEST=make buildall
TEST=Run "pwmduty" command against Volteer LEDs
Signed-off-by: Keith Short <keithshort@chromium.org>
Change-Id: I49047734bc1a3fb3d4010f0040145171275c5657
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2623163
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
-rw-r--r-- | common/pwm.c | 11 | ||||
-rw-r--r-- | zephyr/CMakeLists.txt | 1 | ||||
-rw-r--r-- | zephyr/Kconfig | 7 | ||||
-rw-r--r-- | zephyr/dts/bindings/pwm/named-pwms.yaml | 24 | ||||
-rw-r--r-- | zephyr/projects/volteer/boards/arm/volteer/volteer.dts | 45 | ||||
-rw-r--r-- | zephyr/projects/volteer/prj.conf | 4 | ||||
-rw-r--r-- | zephyr/shim/include/config_chip.h | 5 | ||||
-rw-r--r-- | zephyr/shim/include/pwm/pwm.h | 24 | ||||
-rw-r--r-- | zephyr/shim/src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | zephyr/shim/src/pwm.c | 187 |
10 files changed, 308 insertions, 1 deletions
diff --git a/common/pwm.c b/common/pwm.c index 73b12fc20c..41989a94e0 100644 --- a/common/pwm.c +++ b/common/pwm.c @@ -11,6 +11,10 @@ #include "pwm.h" #include "util.h" +#ifdef CONFIG_ZEPHYR +#include "pwm/pwm.h" +#endif + #ifdef CONFIG_PWM /* @@ -162,10 +166,15 @@ DECLARE_CONSOLE_COMMAND(pwmduty, cc_pwm_duty, "Get/set PWM duty cycles "); #endif /* CONFIG_PWM */ -/* Initialize all PWM pins as functional */ +#ifndef CONFIG_ZEPHYR +/* + * Initialize all PWM pins as functional. This is not required under + * Zephyr as pin configuration is automatically performed by chip driver + */ static void pwm_pin_init(void) { gpio_config_module(MODULE_PWM, 1); } /* HOOK_PRIO_INIT_PWM may be used for chip PWM unit init, so use PRIO + 1 */ DECLARE_HOOK(HOOK_INIT, pwm_pin_init, HOOK_PRIO_INIT_PWM + 1); +#endif /* CONFIG_ZEPHYR */ diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 7a62e4d7e4..a23a862f57 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -155,6 +155,7 @@ zephyr_sources_ifdef(CONFIG_PLATFORM_EC_THROTTLE_AP "${PLATFORM_EC}/common/throttle_ap.c") zephyr_sources_ifdef(CONFIG_PLATFORM_EC_TABLET_MODE "${PLATFORM_EC}/common/tablet_mode.c") +zephyr_sources_ifdef(CONFIG_PLATFORM_EC_PWM "${PLATFORM_EC}/common/pwm.c") zephyr_sources_ifdef(CONFIG_PLATFORM_EC_TIMER "${PLATFORM_EC}/common/timer.c") zephyr_sources_ifdef(CONFIG_PLATFORM_EC_USB_CHARGER diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 9a318358a0..e74aa487c7 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -453,6 +453,13 @@ config PLATFORM_EC_POWER_BUTTON This requires a GPIO named GPIO_POWER_BUTTON_L in gpio_map.h. +config PLATFORM_EC_PWM + bool "PWM (Pulse Width Modulation) module" + help + Enable the PWM (Pulse Width Modulation) module. This module is used to + support variable brightness LEDs, backlight controls, and + variable-speed fans. + config PLATFORM_EC_CONSOLE_CMD_SHMEM bool "Console command: shmem" default y diff --git a/zephyr/dts/bindings/pwm/named-pwms.yaml b/zephyr/dts/bindings/pwm/named-pwms.yaml new file mode 100644 index 0000000000..f01fd5a30a --- /dev/null +++ b/zephyr/dts/bindings/pwm/named-pwms.yaml @@ -0,0 +1,24 @@ +# 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 + label: + required: true + type: string + description: + Human readable string describing the device (used as + device_get_binding() argument) + frequency: + required: true + type: int + description: PWM frequency, in Hz diff --git a/zephyr/projects/volteer/boards/arm/volteer/volteer.dts b/zephyr/projects/volteer/boards/arm/volteer/volteer.dts index e35b59240c..59cc5f2cbc 100644 --- a/zephyr/projects/volteer/boards/arm/volteer/volteer.dts +++ b/zephyr/projects/volteer/boards/arm/volteer/volteer.dts @@ -57,6 +57,31 @@ }; }; + named-pwms { + compatible = "named-pwms"; + + led1_blue { + pwms = <&pwm2 0 PWM_POLARITY_INVERTED>; + label = "LED1_BLUE"; + frequency = <4800>; + }; + led2_green { + pwms = <&pwm0 0 PWM_POLARITY_INVERTED>; + label = "LED2_GREEN"; + frequency = <4800>; + }; + led3_red { + pwms = <&pwm1 0 PWM_POLARITY_INVERTED>; + label = "LED3_RED"; + frequency = <4800>; + }; + led3_sidesel { + pwms = <&pwm7 0 PWM_POLARITY_INVERTED>; + label = "LED4_SIDESEL"; + frequency = <2400>; + }; + }; + named-gpios { compatible = "named-gpios"; @@ -467,3 +492,23 @@ &cros_kb_raw { status = "okay"; }; + +/* Green LED */ +&pwm0 { + status = "okay"; +}; + +/* Red LED */ +&pwm1 { + status = "okay"; +}; + +/* Blue LED */ +&pwm2 { + status = "okay"; +}; + +/* Side selection LED */ +&pwm7 { + status = "okay"; +}; diff --git a/zephyr/projects/volteer/prj.conf b/zephyr/projects/volteer/prj.conf index 8a23041b54..b113b9af8c 100644 --- a/zephyr/projects/volteer/prj.conf +++ b/zephyr/projects/volteer/prj.conf @@ -35,6 +35,10 @@ CONFIG_PLATFORM_EC_POWERSEQ=y CONFIG_PLATFORM_EC_POWERSEQ_RTC_RESET=y CONFIG_PLATFORM_EC_POWERSEQ_S0IX=y +# PWM +CONFIG_PWM=y +CONFIG_PLATFORM_EC_PWM=y + # Miscellaneous tasks CONFIG_HAS_TASK_KEYPROTO=y CONFIG_HAS_TASK_POWERBTN=y diff --git a/zephyr/shim/include/config_chip.h b/zephyr/shim/include/config_chip.h index ce9063d4f9..5da5f63a72 100644 --- a/zephyr/shim/include/config_chip.h +++ b/zephyr/shim/include/config_chip.h @@ -252,6 +252,11 @@ enum battery_type { #define CONFIG_FAKE_SHMEM #endif +#undef CONFIG_PWM +#ifdef CONFIG_PLATFORM_EC_PWM +#define CONFIG_PWM +#endif + #undef CONFIG_CMD_SHMEM #ifdef CONFIG_PLATFORM_EC_CONSOLE_CMD_SHMEM #define CONFIG_CMD_SHMEM diff --git a/zephyr/shim/include/pwm/pwm.h b/zephyr/shim/include/pwm/pwm.h new file mode 100644 index 0000000000..38720abb39 --- /dev/null +++ b/zephyr/shim/include/pwm/pwm.h @@ -0,0 +1,24 @@ +/* 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, +}; + +#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 6512938e56..1a4a75025e 100644 --- a/zephyr/shim/src/CMakeLists.txt +++ b/zephyr/shim/src/CMakeLists.txt @@ -15,6 +15,7 @@ zephyr_sources_ifdef(CONFIG_PLATFORM_EC_HOOKS hooks.c) zephyr_sources_ifdef(CONFIG_PLATFORM_EC_HOSTCMD host_command.c) zephyr_sources_ifdef(CONFIG_PLATFORM_EC_MKBP_EVENT mkbp_event.c) zephyr_sources_ifdef(CONFIG_PLATFORM_EC_PANIC panic.c) +zephyr_sources_ifdef(CONFIG_PLATFORM_EC_PWM pwm.c) zephyr_sources_ifdef(CONFIG_PLATFORM_EC_TIMER hwtimer.c) zephyr_sources_ifdef(CONFIG_PLATFORM_EC_I2C i2c.c) zephyr_sources_ifdef(CONFIG_CROS_EC system.c) diff --git a/zephyr/shim/src/pwm.c b/zephyr/shim/src/pwm.c new file mode 100644 index 0000000000..a9466684d6 --- /dev/null +++ b/zephyr/shim/src/pwm.c @@ -0,0 +1,187 @@ +/* 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 "ec_commands.h" +#include "pwm.h" +#include "util.h" + +#include "pwm/pwm.h" + +LOG_MODULE_REGISTER(pwm_shim, LOG_LEVEL_ERR); + +#define USECS_PER_SEC 1000000 + +/* + * 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_LABEL(id); \ + pwm_configs[PWM_CHANNEL(id)].dev = device_get_binding( \ + DT_PROP_BY_PHANDLE(id, pwms, label)); \ + 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 = USECS_PER_SEC / pwm->freq; + } + + return rv; +} +SYS_INIT(init_pwms, PRE_KERNEL_1, 50); + +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); +} |