led: bcm6858: Drop duplicate OF "label" property parsing
[platform/kernel/u-boot.git] / drivers / led / led_pwm.c
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
17 struct 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
26 static 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
48 static 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
66 static 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
88 static 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
95 static int led_pwm_probe(struct udevice *dev)
96 {
97         struct led_pwm_priv *priv = dev_get_priv(dev);
98         struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
99
100         /* Ignore the top-level LED node */
101         if (!uc_plat->label)
102                 return 0;
103
104         return led_pwm_set_state(dev, (priv->enabled) ? LEDST_ON : LEDST_OFF);
105 }
106
107 static int led_pwm_of_to_plat(struct udevice *dev)
108 {
109         struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
110         struct led_pwm_priv *priv = dev_get_priv(dev);
111         struct ofnode_phandle_args args;
112         uint def_brightness, max_brightness;
113         int ret;
114
115         /* Ignore the top-level LED node */
116         if (!uc_plat->label)
117                 return 0;
118
119         ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0, &args);
120         if (ret)
121                 return ret;
122
123         ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm);
124         if (ret)
125                 return ret;
126
127         priv->channel = args.args[0];
128         priv->period = args.args[1];
129         priv->active_low = dev_read_bool(dev, "active-low");
130
131         def_brightness = dev_read_u32_default(dev, "u-boot,default-brightness", 0);
132         max_brightness = dev_read_u32_default(dev, "max-brightness", 255);
133         priv->enabled =  !!def_brightness;
134
135         /*
136          * No need to handle pwm iverted case (active_low)
137          * because of pwm_set_invert function
138          */
139         if (def_brightness < max_brightness)
140                 priv->duty = priv->period * def_brightness / max_brightness;
141         else
142                 priv->duty = priv->period;
143
144         return 0;
145 }
146
147 static int led_pwm_bind(struct udevice *parent)
148 {
149         struct udevice *dev;
150         ofnode node;
151         int ret;
152
153         dev_for_each_subnode(node, parent) {
154                 struct led_uc_plat *uc_plat;
155                 const char *label;
156
157                 label = ofnode_read_string(node, "label");
158                 if (!label)
159                         label = ofnode_get_name(node);
160
161                 ret = device_bind_driver_to_node(parent, LEDS_PWM_DRIVER_NAME,
162                                                  ofnode_get_name(node),
163                                                  node, &dev);
164                 if (ret)
165                         return ret;
166
167                 uc_plat = dev_get_uclass_plat(dev);
168                 uc_plat->label = label;
169         }
170         return 0;
171 }
172
173 static const struct led_ops led_pwm_ops = {
174         .set_state = led_pwm_set_state,
175         .get_state = led_pwm_get_state,
176 };
177
178 static const struct udevice_id led_pwm_ids[] = {
179         { .compatible = "pwm-leds" },
180         { }
181 };
182
183 U_BOOT_DRIVER(led_pwm) = {
184         .name = LEDS_PWM_DRIVER_NAME,
185         .id = UCLASS_LED,
186         .of_match = led_pwm_ids,
187         .ops = &led_pwm_ops,
188         .priv_auto = sizeof(struct led_pwm_priv),
189         .bind = led_pwm_bind,
190         .probe = led_pwm_probe,
191         .of_to_plat = led_pwm_of_to_plat,
192 };