]>
Commit | Line | Data |
---|---|---|
736214b4 PR |
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 <[email protected]> | |
8 | */ | |
9 | ||
808c7881 AS |
10 | #include <linux/bits.h> |
11 | #include <linux/container_of.h> | |
12 | #include <linux/device.h> | |
736214b4 PR |
13 | #include <linux/leds.h> |
14 | #include <linux/mfd/max5970.h> | |
6d63d05e | 15 | #include <linux/mod_devicetable.h> |
808c7881 | 16 | #include <linux/module.h> |
736214b4 | 17 | #include <linux/platform_device.h> |
6d63d05e | 18 | #include <linux/property.h> |
736214b4 PR |
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(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 | { | |
6d63d05e | 48 | struct fwnode_handle *led_node, *child; |
736214b4 | 49 | struct device *dev = &pdev->dev; |
736214b4 | 50 | struct regmap *regmap; |
736214b4 | 51 | struct max5970_led *ddata; |
d3578b49 | 52 | int ret = -ENODEV; |
736214b4 | 53 | |
6d63d05e | 54 | regmap = dev_get_regmap(dev->parent, NULL); |
736214b4 PR |
55 | if (!regmap) |
56 | return -ENODEV; | |
57 | ||
6d63d05e | 58 | led_node = device_get_named_child_node(dev->parent, "leds"); |
736214b4 PR |
59 | if (!led_node) |
60 | return -ENODEV; | |
61 | ||
6d63d05e | 62 | fwnode_for_each_available_child_node(led_node, child) { |
736214b4 PR |
63 | u32 reg; |
64 | ||
6d63d05e | 65 | if (fwnode_property_read_u32(child, "reg", ®)) |
736214b4 PR |
66 | continue; |
67 | ||
68 | if (reg >= MAX5970_NUM_LEDS) { | |
e7baa5b4 | 69 | dev_err_probe(dev, -EINVAL, "invalid LED (%u >= %d)\n", reg, MAX5970_NUM_LEDS); |
736214b4 PR |
70 | continue; |
71 | } | |
72 | ||
73 | ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); | |
74 | if (!ddata) { | |
6d63d05e | 75 | fwnode_handle_put(child); |
736214b4 PR |
76 | return -ENOMEM; |
77 | } | |
78 | ||
79 | ddata->index = reg; | |
80 | ddata->regmap = regmap; | |
81 | ddata->dev = dev; | |
82 | ||
6d63d05e AS |
83 | if (fwnode_property_read_string(child, "label", &ddata->cdev.name)) |
84 | ddata->cdev.name = fwnode_get_name(child); | |
736214b4 PR |
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(dev, &ddata->cdev); | |
91 | if (ret < 0) { | |
6d63d05e | 92 | fwnode_handle_put(child); |
e7baa5b4 | 93 | return dev_err_probe(dev, ret, "Failed to initialize LED %u\n", reg); |
736214b4 | 94 | } |
736214b4 PR |
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 | }; | |
736214b4 | 106 | module_platform_driver(max5970_led_driver); |
6d63d05e | 107 | |
736214b4 PR |
108 | MODULE_AUTHOR("Patrick Rudolph <[email protected]>"); |
109 | MODULE_AUTHOR("Naresh Solanki <[email protected]>"); | |
110 | MODULE_DESCRIPTION("MAX5970_hot-swap controller LED driver"); | |
111 | MODULE_LICENSE("GPL"); |