[PATCH v2 2/2] pwm: Add a driver for Chrome OS EC PWM

Peter Robinson pbrobinson at gmail.com
Sun Sep 27 09:27:22 CEST 2020


On Sat, Sep 26, 2020 at 9:09 PM Alper Nebi Yasak
<alpernebiyasak at gmail.com> wrote:
>
> This PWM is used in rk3399-gru-bob and rk3399-gru-kevin to control
> the display brightness. We can only change the duty cycle, so on
> set_config() we just try to match the duty cycle that dividing duty_ns
> by period_ns gives us. To disable, we set the duty cycle to zero while
> keeping the old value for when we want to re-enable it.
>
> The cros_ec_set_pwm_duty() function is taken from Depthcharge's
> cros_ec_set_bl_pwm_duty() but modified to use the generic pwm type.
> The driver itself is very loosely based on rk_pwm.c for the general pwm
> driver structure.
>
> The devicetree binding file is from Linux, before it was converted to
> YAML at 5df5a577a6b4 ("dt-bindings: pwm: Convert google,cros-ec-pwm.txt
> to YAML format") in their repo.
>
> Signed-off-by: Alper Nebi Yasak <alpernebiyasak at gmail.com>
> ---
> I'm testing on a rk3399-gru-kevin with a lot of other patches to get it
> (and it's screen) barely working, but using something like the following
> in a rk_board_late_init() added to gru.c gets the backlight to change:

A quick look at the Linux DT for the GRU Chromebooks and it looks like
the panels are also attached via EDP like on the Pinebook Pro so this
patch series adding initial support for the rk3399 EDP might be of use
here as well:

https://lists.denx.de/pipermail/u-boot/2020-September/427790.html

>     struct *udevice panel;
>     uclass_first_device_err(UCLASS_PANEL, &panel);
>     panel_enable_backlight(panel);
>     panel_set_backlight(panel, 50);
>     panel_set_backlight(panel, 100);
>
> The actual tree I'm testing and how I'm getting it to chainload from the
> vendor firmware (see commit message there) is available here:
>
>     https://github.com/alpernebbi/u-boot/tree/rk3399-gru-kevin/wip
>     (currently at commit 4b5a8441303facb6e419da9f5e86980de3eb090a)
>
> Changes in v2:
> - Add device-tree-binding/pwm/cros-ec-pwm.txt adapted from Linux
> - Return ec_command() errors correctly in cros_ec_set_pwm_duty()
> - Add newlines before last returns in cros_ec_pwm.c
>
> v1: https://patchwork.ozlabs.org/project/uboot/patch/20200923165231.18188-2-alpernebiyasak@gmail.com/
>
>  doc/device-tree-bindings/pwm/cros-ec-pwm.txt | 23 ++++++
>  drivers/misc/cros_ec.c                       | 17 ++++
>  drivers/pwm/Kconfig                          |  9 +++
>  drivers/pwm/Makefile                         |  1 +
>  drivers/pwm/cros_ec_pwm.c                    | 84 ++++++++++++++++++++
>  include/cros_ec.h                            | 13 +++
>  6 files changed, 147 insertions(+)
>  create mode 100644 doc/device-tree-bindings/pwm/cros-ec-pwm.txt
>  create mode 100644 drivers/pwm/cros_ec_pwm.c
>
> diff --git a/doc/device-tree-bindings/pwm/cros-ec-pwm.txt b/doc/device-tree-bindings/pwm/cros-ec-pwm.txt
> new file mode 100644
> index 0000000000..f198d08891
> --- /dev/null
> +++ b/doc/device-tree-bindings/pwm/cros-ec-pwm.txt
> @@ -0,0 +1,23 @@
> +PWM controlled by ChromeOS EC
> +
> +Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller
> +(EC) and controlled via a host-command interface.
> +
> +An EC PWM node should be only found as a sub-node of the EC node (see
> +doc/device-tree-bindings/misc/cros-ec.txt).
> +
> +Required properties:
> +- compatible: Must contain "google,cros-ec-pwm"
> +- #pwm-cells: Should be 1. The cell specifies the PWM index.
> +
> +Example:
> +       cros-ec at 0 {
> +               compatible = "google,cros-ec-spi";
> +
> +               ...
> +
> +               cros_ec_pwm: ec-pwm {
> +                       compatible = "google,cros-ec-pwm";
> +                       #pwm-cells = <1>;
> +               };
> +       };
> diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c
> index a5534b1667..3be2dd6f14 100644
> --- a/drivers/misc/cros_ec.c
> +++ b/drivers/misc/cros_ec.c
> @@ -1110,6 +1110,23 @@ int cros_ec_battery_cutoff(struct udevice *dev, uint8_t flags)
>         return 0;
>  }
>
> +int cros_ec_set_pwm_duty(struct udevice *dev, uint8_t index, uint16_t duty)
> +{
> +       struct ec_params_pwm_set_duty p;
> +       int ret;
> +
> +       p.duty = duty;
> +       p.pwm_type = EC_PWM_TYPE_GENERIC;
> +       p.index = index;
> +
> +       ret = ec_command(dev, EC_CMD_PWM_SET_DUTY, 0, &p, sizeof(p),
> +                        NULL, 0);
> +       if (ret < 0)
> +               return ret;
> +
> +       return 0;
> +}
> +
>  int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state)
>  {
>         struct ec_params_ldo_set params;
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 61eb468cde..73b0e5ed1e 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -9,6 +9,15 @@ config DM_PWM
>           frequency/period can be controlled along with the proportion of that
>           time that the signal is high.
>
> +config PWM_CROS_EC
> +       bool "Enable support for the Chrome OS EC PWM"
> +       depends on DM_PWM
> +       help
> +         This PWM is found on several Chrome OS devices and controlled by
> +         the Chrome OS embedded controller. It may be used to control the
> +         screen brightness and/or the keyboard backlight depending on the
> +         device.
> +
>  config PWM_EXYNOS
>         bool "Enable support for the Exynos PWM"
>         depends on DM_PWM
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 0f4e84b04d..7af13538de 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -10,6 +10,7 @@
>
>  obj-$(CONFIG_DM_PWM)           += pwm-uclass.o
>
> +obj-$(CONFIG_PWM_CROS_EC)      += cros_ec_pwm.o
>  obj-$(CONFIG_PWM_EXYNOS)       += exynos_pwm.o
>  obj-$(CONFIG_PWM_IMX)          += pwm-imx.o pwm-imx-util.o
>  obj-$(CONFIG_PWM_MTK)          += pwm-mtk.o
> diff --git a/drivers/pwm/cros_ec_pwm.c b/drivers/pwm/cros_ec_pwm.c
> new file mode 100644
> index 0000000000..44f4105dfd
> --- /dev/null
> +++ b/drivers/pwm/cros_ec_pwm.c
> @@ -0,0 +1,84 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <common.h>
> +#include <cros_ec.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <log.h>
> +#include <pwm.h>
> +
> +struct cros_ec_pwm_priv {
> +       bool enabled;
> +       uint duty;
> +};
> +
> +static int cros_ec_pwm_set_config(struct udevice *dev, uint channel,
> +                                 uint period_ns, uint duty_ns)
> +{
> +       struct cros_ec_pwm_priv *priv = dev_get_priv(dev);
> +       uint duty;
> +       int ret;
> +
> +       debug("%s: period_ns=%u, duty_ns=%u asked\n", __func__,
> +             period_ns, duty_ns);
> +
> +       /* No way to set the period, only a relative duty cycle */
> +       duty = EC_PWM_MAX_DUTY * duty_ns / period_ns;
> +       if (duty > EC_PWM_MAX_DUTY)
> +               duty = EC_PWM_MAX_DUTY;
> +
> +       if (!priv->enabled) {
> +               priv->duty = duty;
> +               debug("%s: duty=%#x to-be-set\n", __func__, duty);
> +               return 0;
> +       }
> +
> +       ret = cros_ec_set_pwm_duty(dev->parent, channel, duty);
> +       if (ret) {
> +               debug("%s: duty=%#x failed\n", __func__, duty);
> +               return ret;
> +       }
> +
> +       priv->duty = duty;
> +       debug("%s: duty=%#x set\n", __func__, duty);
> +
> +       return 0;
> +}
> +
> +static int cros_ec_pwm_set_enable(struct udevice *dev, uint channel,
> +                                 bool enable)
> +{
> +       struct cros_ec_pwm_priv *priv = dev_get_priv(dev);
> +       int ret;
> +
> +       ret = cros_ec_set_pwm_duty(dev->parent, channel,
> +                                  enable ? priv->duty : 0);
> +       if (ret) {
> +               debug("%s: enable=%d failed\n", __func__, enable);
> +               return ret;
> +       }
> +
> +       priv->enabled = enable;
> +       debug("%s: enable=%d (duty=%#x) set\n", __func__,
> +             enable, priv->duty);
> +
> +       return 0;
> +}
> +
> +static const struct pwm_ops cros_ec_pwm_ops = {
> +       .set_config     = cros_ec_pwm_set_config,
> +       .set_enable     = cros_ec_pwm_set_enable,
> +};
> +
> +static const struct udevice_id cros_ec_pwm_ids[] = {
> +       { .compatible = "google,cros-ec-pwm" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(cros_ec_pwm) = {
> +       .name   = "cros_ec_pwm",
> +       .id     = UCLASS_PWM,
> +       .of_match = cros_ec_pwm_ids,
> +       .ops    = &cros_ec_pwm_ops,
> +       .priv_auto_alloc_size   = sizeof(struct cros_ec_pwm_priv),
> +};
> diff --git a/include/cros_ec.h b/include/cros_ec.h
> index f4b9b7a5c2..202e5c2c74 100644
> --- a/include/cros_ec.h
> +++ b/include/cros_ec.h
> @@ -443,6 +443,19 @@ int cros_ec_efs_verify(struct udevice *dev, enum ec_flash_region region);
>   */
>  int cros_ec_battery_cutoff(struct udevice *dev, uint8_t flags);
>
> +/**
> + * cros_ec_set_pwm_duty() - Set duty cycle of a generic pwm
> + *
> + * Note that duty value needs to be passed to the EC as a 16 bit number
> + * for increased precision.
> + *
> + * @param dev          CROS-EC device
> + * @param index                Index of the pwm
> + * @param duty         Desired duty cycle, in 0..EC_PWM_MAX_DUTY range.
> + * @return 0 if OK, -ve on error
> + */
> +int cros_ec_set_pwm_duty(struct udevice *dev, uint8_t index, uint16_t duty);
> +
>  /**
>   * cros_ec_read_limit_power() - Check if power is limited by batter/charger
>   *
> --
> 2.28.0
>


More information about the U-Boot mailing list