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 /zephyr/shim/src | |
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>
Diffstat (limited to 'zephyr/shim/src')
-rw-r--r-- | zephyr/shim/src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | zephyr/shim/src/pwm.c | 187 |
2 files changed, 188 insertions, 0 deletions
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); +} |