uboot/drivers/led/led_pwm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2022 VK
   4 * Author: Ivan Vozvakhov <i.vozvakhov@vk.team>
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <errno.h>
  10#include <led.h>
  11#include <malloc.h>
  12#include <dm/lists.h>
  13#include <pwm.h>
  14
  15#define LEDS_PWM_DRIVER_NAME    "led_pwm"
  16
  17struct led_pwm_priv {
  18        struct udevice *pwm;
  19        uint period;    /* period in ns */
  20        uint duty;      /* duty cycle in ns */
  21        uint channel;   /* pwm channel number */
  22        bool active_low;        /* pwm polarity */
  23        bool enabled;
  24};
  25
  26static int led_pwm_enable(struct udevice *dev)
  27{
  28        struct led_pwm_priv *priv = dev_get_priv(dev);
  29        int ret;
  30
  31        ret = pwm_set_invert(priv->pwm, priv->channel, priv->active_low);
  32        if (ret)
  33                return ret;
  34
  35        ret = pwm_set_config(priv->pwm, priv->channel, priv->period, priv->duty);
  36        if (ret)
  37                return ret;
  38
  39        ret = pwm_set_enable(priv->pwm, priv->channel, true);
  40        if (ret)
  41                return ret;
  42
  43        priv->enabled = true;
  44
  45        return 0;
  46}
  47
  48static int led_pwm_disable(struct udevice *dev)
  49{
  50        struct led_pwm_priv *priv = dev_get_priv(dev);
  51        int ret;
  52
  53        ret = pwm_set_config(priv->pwm, priv->channel, priv->period, 0);
  54        if (ret)
  55                return ret;
  56
  57        ret = pwm_set_enable(priv->pwm, priv->channel, false);
  58        if (ret)
  59                return ret;
  60
  61        priv->enabled = false;
  62
  63        return 0;
  64}
  65
  66static int led_pwm_set_state(struct udevice *dev, enum led_state_t state)
  67{
  68        struct led_pwm_priv *priv = dev_get_priv(dev);
  69        int ret;
  70
  71        switch (state) {
  72        case LEDST_OFF:
  73                ret = led_pwm_disable(dev);
  74                break;
  75        case LEDST_ON:
  76                ret = led_pwm_enable(dev);
  77                break;
  78        case LEDST_TOGGLE:
  79                ret = (priv->enabled) ? led_pwm_disable(dev) : led_pwm_enable(dev);
  80                break;
  81        default:
  82                ret = -ENOSYS;
  83        }
  84
  85        return ret;
  86}
  87
  88static enum led_state_t led_pwm_get_state(struct udevice *dev)
  89{
  90        struct led_pwm_priv *priv = dev_get_priv(dev);
  91
  92        return (priv->enabled) ? LEDST_ON : LEDST_OFF;
  93}
  94
  95static int led_pwm_probe(struct udevice *dev)
  96{
  97        struct led_pwm_priv *priv = dev_get_priv(dev);
  98
  99        return led_pwm_set_state(dev, (priv->enabled) ? LEDST_ON : LEDST_OFF);
 100}
 101
 102static int led_pwm_of_to_plat(struct udevice *dev)
 103{
 104        struct led_pwm_priv *priv = dev_get_priv(dev);
 105        struct ofnode_phandle_args args;
 106        uint def_brightness, max_brightness;
 107        int ret;
 108
 109        ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0, &args);
 110        if (ret)
 111                return ret;
 112
 113        ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm);
 114        if (ret)
 115                return ret;
 116
 117        priv->channel = args.args[0];
 118        priv->period = args.args[1];
 119        priv->active_low = dev_read_bool(dev, "active-low");
 120
 121        def_brightness = dev_read_u32_default(dev, "u-boot,default-brightness", 0);
 122        max_brightness = dev_read_u32_default(dev, "max-brightness", 255);
 123        priv->enabled =  !!def_brightness;
 124
 125        /*
 126         * No need to handle pwm iverted case (active_low)
 127         * because of pwm_set_invert function
 128         */
 129        if (def_brightness < max_brightness)
 130                priv->duty = priv->period * def_brightness / max_brightness;
 131        else
 132                priv->duty = priv->period;
 133
 134        return 0;
 135}
 136
 137static int led_pwm_bind(struct udevice *parent)
 138{
 139        struct udevice *dev;
 140        ofnode node;
 141        int ret;
 142
 143        dev_for_each_subnode(node, parent) {
 144                ret = device_bind_driver_to_node(parent, LEDS_PWM_DRIVER_NAME,
 145                                                 ofnode_get_name(node),
 146                                                 node, &dev);
 147                if (ret)
 148                        return ret;
 149        }
 150        return 0;
 151}
 152
 153static const struct led_ops led_pwm_ops = {
 154        .set_state = led_pwm_set_state,
 155        .get_state = led_pwm_get_state,
 156};
 157
 158static const struct udevice_id led_pwm_ids[] = {
 159        { .compatible = "pwm-leds" },
 160        { }
 161};
 162
 163U_BOOT_DRIVER(led_pwm) = {
 164        .name = LEDS_PWM_DRIVER_NAME,
 165        .id = UCLASS_LED,
 166        .ops = &led_pwm_ops,
 167        .priv_auto = sizeof(struct led_pwm_priv),
 168        .probe = led_pwm_probe,
 169        .of_to_plat = led_pwm_of_to_plat,
 170};
 171
 172U_BOOT_DRIVER(led_pwm_wrap) = {
 173        .name = LEDS_PWM_DRIVER_NAME "_wrap",
 174        .id = UCLASS_NOP,
 175        .of_match = led_pwm_ids,
 176        .bind = led_pwm_bind,
 177};
 178