1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * linux/drivers/leds-pwm.c 4 * 5 * simple PWM based LED control 6 * 7 * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de) 8 * 9 * based on leds-gpio.c by Raphael Assenat <raph@8d.com> 10 */ 11 12 #include <linux/err.h> 13 #include <linux/gpio/consumer.h> 14 #include <linux/kernel.h> 15 #include <linux/leds.h> 16 #include <linux/module.h> 17 #include <linux/of.h> 18 #include <linux/platform_device.h> 19 #include <linux/pwm.h> 20 #include <linux/slab.h> 21 22 struct led_pwm { 23 const char *name; 24 u8 active_low; 25 u8 default_state; 26 unsigned int max_brightness; 27 }; 28 29 struct led_pwm_data { 30 struct gpio_desc *enable_gpio; 31 struct led_classdev cdev; 32 struct pwm_device *pwm; 33 struct pwm_state pwmstate; 34 unsigned int active_low; 35 }; 36 37 struct led_pwm_priv { 38 int num_leds; 39 struct led_pwm_data leds[]; 40 }; 41 42 static int led_pwm_set(struct led_classdev *led_cdev, 43 enum led_brightness brightness) 44 { 45 struct led_pwm_data *led_dat = 46 container_of(led_cdev, struct led_pwm_data, cdev); 47 unsigned int max = led_dat->cdev.max_brightness; 48 unsigned long long duty = led_dat->pwmstate.period; 49 50 duty *= brightness; 51 do_div(duty, max); 52 53 if (led_dat->active_low) 54 duty = led_dat->pwmstate.period - duty; 55 56 gpiod_set_value_cansleep(led_dat->enable_gpio, !!brightness); 57 58 led_dat->pwmstate.duty_cycle = duty; 59 /* 60 * Disabling a PWM doesn't guarantee that it emits the inactive level. 61 * So keep it on. Only for suspending the PWM should be disabled because 62 * otherwise it refuses to suspend. The possible downside is that the 63 * LED might stay (or even go) on. 64 */ 65 led_dat->pwmstate.enabled = !(led_cdev->flags & LED_SUSPENDED); 66 return pwm_apply_might_sleep(led_dat->pwm, &led_dat->pwmstate); 67 } 68 69 static int led_pwm_default_brightness_get(struct fwnode_handle *fwnode, 70 int max_brightness) 71 { 72 unsigned int default_brightness; 73 int ret; 74 75 ret = fwnode_property_read_u32(fwnode, "default-brightness", 76 &default_brightness); 77 if (ret < 0 || default_brightness > max_brightness) 78 default_brightness = max_brightness; 79 80 return default_brightness; 81 } 82 83 __attribute__((nonnull)) 84 static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, 85 struct led_pwm *led, struct fwnode_handle *fwnode) 86 { 87 struct led_pwm_data *led_data = &priv->leds[priv->num_leds]; 88 struct led_init_data init_data = { .fwnode = fwnode }; 89 int ret; 90 91 led_data->active_low = led->active_low; 92 led_data->cdev.name = led->name; 93 led_data->cdev.brightness = LED_OFF; 94 led_data->cdev.max_brightness = led->max_brightness; 95 led_data->cdev.flags = LED_CORE_SUSPENDRESUME; 96 97 led_data->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL); 98 if (IS_ERR(led_data->pwm)) 99 return dev_err_probe(dev, PTR_ERR(led_data->pwm), 100 "unable to request PWM for %s\n", 101 led->name); 102 103 led_data->cdev.brightness_set_blocking = led_pwm_set; 104 105 /* init PWM state */ 106 switch (led->default_state) { 107 case LEDS_DEFSTATE_KEEP: 108 pwm_get_state(led_data->pwm, &led_data->pwmstate); 109 if (led_data->pwmstate.period) 110 break; 111 led->default_state = LEDS_DEFSTATE_OFF; 112 dev_warn(dev, 113 "failed to read period for %s, default to off", 114 led->name); 115 fallthrough; 116 default: 117 pwm_init_state(led_data->pwm, &led_data->pwmstate); 118 break; 119 } 120 121 /* set brightness */ 122 switch (led->default_state) { 123 case LEDS_DEFSTATE_ON: 124 led_data->cdev.brightness = 125 led_pwm_default_brightness_get(fwnode, led->max_brightness); 126 break; 127 case LEDS_DEFSTATE_KEEP: 128 { 129 uint64_t brightness; 130 131 brightness = led->max_brightness; 132 brightness *= led_data->pwmstate.duty_cycle; 133 do_div(brightness, led_data->pwmstate.period); 134 led_data->cdev.brightness = brightness; 135 } 136 break; 137 } 138 139 /* 140 * Claim the GPIO as GPIOD_ASIS and set the value 141 * later on to honor the different default states 142 */ 143 led_data->enable_gpio = devm_fwnode_gpiod_get(dev, fwnode, "enable", GPIOD_ASIS, NULL); 144 if (IS_ERR(led_data->enable_gpio)) { 145 if (PTR_ERR(led_data->enable_gpio) == -ENOENT) 146 /* Enable GPIO is optional */ 147 led_data->enable_gpio = NULL; 148 else 149 return PTR_ERR(led_data->enable_gpio); 150 } 151 152 gpiod_direction_output(led_data->enable_gpio, !!led_data->cdev.brightness); 153 154 ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); 155 if (ret) { 156 dev_err(dev, "failed to register PWM led for %s: %d\n", 157 led->name, ret); 158 return ret; 159 } 160 161 if (led->default_state != LEDS_DEFSTATE_KEEP) { 162 ret = led_pwm_set(&led_data->cdev, led_data->cdev.brightness); 163 if (ret) { 164 dev_err(dev, "failed to set led PWM value for %s: %d", 165 led->name, ret); 166 return ret; 167 } 168 } 169 170 priv->num_leds++; 171 return 0; 172 } 173 174 static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv) 175 { 176 struct led_pwm led; 177 int ret; 178 179 device_for_each_child_node_scoped(dev, fwnode) { 180 memset(&led, 0, sizeof(led)); 181 182 ret = fwnode_property_read_string(fwnode, "label", &led.name); 183 if (ret && is_of_node(fwnode)) 184 led.name = to_of_node(fwnode)->name; 185 186 if (!led.name) 187 return -EINVAL; 188 189 led.active_low = fwnode_property_read_bool(fwnode, 190 "active-low"); 191 fwnode_property_read_u32(fwnode, "max-brightness", 192 &led.max_brightness); 193 194 led.default_state = led_init_default_state_get(fwnode); 195 196 ret = led_pwm_add(dev, priv, &led, fwnode); 197 if (ret) 198 return ret; 199 } 200 201 return 0; 202 } 203 204 static int led_pwm_probe(struct platform_device *pdev) 205 { 206 struct led_pwm_priv *priv; 207 int ret = 0; 208 int count; 209 210 count = device_get_child_node_count(&pdev->dev); 211 212 if (!count) 213 return -EINVAL; 214 215 priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, count), 216 GFP_KERNEL); 217 if (!priv) 218 return -ENOMEM; 219 220 ret = led_pwm_create_fwnode(&pdev->dev, priv); 221 222 if (ret) 223 return ret; 224 225 platform_set_drvdata(pdev, priv); 226 227 return 0; 228 } 229 230 static const struct of_device_id of_pwm_leds_match[] = { 231 { .compatible = "pwm-leds", }, 232 {}, 233 }; 234 MODULE_DEVICE_TABLE(of, of_pwm_leds_match); 235 236 static struct platform_driver led_pwm_driver = { 237 .probe = led_pwm_probe, 238 .driver = { 239 .name = "leds_pwm", 240 .of_match_table = of_pwm_leds_match, 241 }, 242 }; 243 244 module_platform_driver(led_pwm_driver); 245 246 MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); 247 MODULE_DESCRIPTION("generic PWM LED driver"); 248 MODULE_LICENSE("GPL v2"); 249 MODULE_ALIAS("platform:leds-pwm"); 250