1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Device driver for leds in MAX5970 and MAX5978 IC |
4 | * |
5 | * Copyright (c) 2022 9elements GmbH |
6 | * |
7 | * Author: Patrick Rudolph <patrick.rudolph@9elements.com> |
8 | */ |
9 | |
10 | #include <linux/bits.h> |
11 | #include <linux/container_of.h> |
12 | #include <linux/device.h> |
13 | #include <linux/leds.h> |
14 | #include <linux/mfd/max5970.h> |
15 | #include <linux/mod_devicetable.h> |
16 | #include <linux/module.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/property.h> |
19 | #include <linux/regmap.h> |
20 | |
21 | #define ldev_to_maxled(c) container_of(c, struct max5970_led, cdev) |
22 | |
23 | struct max5970_led { |
24 | struct device *dev; |
25 | struct regmap *regmap; |
26 | struct led_classdev cdev; |
27 | unsigned int index; |
28 | }; |
29 | |
30 | static int max5970_led_set_brightness(struct led_classdev *cdev, |
31 | enum led_brightness brightness) |
32 | { |
33 | struct max5970_led *ddata = ldev_to_maxled(cdev); |
34 | int ret, val; |
35 | |
36 | /* Set/clear corresponding bit for given led index */ |
37 | val = !brightness ? BIT(ddata->index) : 0; |
38 | |
39 | ret = regmap_update_bits(map: ddata->regmap, MAX5970_REG_LED_FLASH, BIT(ddata->index), val); |
40 | if (ret < 0) |
41 | dev_err(cdev->dev, "failed to set brightness %d" , ret); |
42 | |
43 | return ret; |
44 | } |
45 | |
46 | static int max5970_led_probe(struct platform_device *pdev) |
47 | { |
48 | struct fwnode_handle *led_node, *child; |
49 | struct device *dev = &pdev->dev; |
50 | struct regmap *regmap; |
51 | struct max5970_led *ddata; |
52 | int ret = -ENODEV; |
53 | |
54 | regmap = dev_get_regmap(dev: dev->parent, NULL); |
55 | if (!regmap) |
56 | return -ENODEV; |
57 | |
58 | led_node = device_get_named_child_node(dev: dev->parent, childname: "leds" ); |
59 | if (!led_node) |
60 | return -ENODEV; |
61 | |
62 | fwnode_for_each_available_child_node(led_node, child) { |
63 | u32 reg; |
64 | |
65 | if (fwnode_property_read_u32(fwnode: child, propname: "reg" , val: ®)) |
66 | continue; |
67 | |
68 | if (reg >= MAX5970_NUM_LEDS) { |
69 | dev_err_probe(dev, err: -EINVAL, fmt: "invalid LED (%u >= %d)\n" , reg, MAX5970_NUM_LEDS); |
70 | continue; |
71 | } |
72 | |
73 | ddata = devm_kzalloc(dev, size: sizeof(*ddata), GFP_KERNEL); |
74 | if (!ddata) { |
75 | fwnode_handle_put(fwnode: child); |
76 | return -ENOMEM; |
77 | } |
78 | |
79 | ddata->index = reg; |
80 | ddata->regmap = regmap; |
81 | ddata->dev = dev; |
82 | |
83 | if (fwnode_property_read_string(fwnode: child, propname: "label" , val: &ddata->cdev.name)) |
84 | ddata->cdev.name = fwnode_get_name(fwnode: child); |
85 | |
86 | ddata->cdev.max_brightness = 1; |
87 | ddata->cdev.brightness_set_blocking = max5970_led_set_brightness; |
88 | ddata->cdev.default_trigger = "none" ; |
89 | |
90 | ret = devm_led_classdev_register(parent: dev, led_cdev: &ddata->cdev); |
91 | if (ret < 0) { |
92 | fwnode_handle_put(fwnode: child); |
93 | return dev_err_probe(dev, err: ret, fmt: "Failed to initialize LED %u\n" , reg); |
94 | } |
95 | } |
96 | |
97 | return ret; |
98 | } |
99 | |
100 | static struct platform_driver max5970_led_driver = { |
101 | .driver = { |
102 | .name = "max5970-led" , |
103 | }, |
104 | .probe = max5970_led_probe, |
105 | }; |
106 | module_platform_driver(max5970_led_driver); |
107 | |
108 | MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>" ); |
109 | MODULE_AUTHOR("Naresh Solanki <Naresh.Solanki@9elements.com>" ); |
110 | MODULE_DESCRIPTION("MAX5970_hot-swap controller LED driver" ); |
111 | MODULE_LICENSE("GPL" ); |
112 | |