Commit | Line | Data |
---|---|---|
5be102eb BG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Copyright (C) 2018 BayLibre SAS | |
4 | // Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> | |
5 | // | |
6 | // LED driver for MAXIM 77650/77651 charger/power-supply. | |
7 | ||
8 | #include <linux/i2c.h> | |
9 | #include <linux/leds.h> | |
10 | #include <linux/mfd/max77650.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/regmap.h> | |
14 | ||
15 | #define MAX77650_LED_NUM_LEDS 3 | |
16 | ||
17 | #define MAX77650_LED_A_BASE 0x40 | |
18 | #define MAX77650_LED_B_BASE 0x43 | |
19 | ||
20 | #define MAX77650_LED_BR_MASK GENMASK(4, 0) | |
21 | #define MAX77650_LED_EN_MASK GENMASK(7, 6) | |
22 | ||
23 | #define MAX77650_LED_MAX_BRIGHTNESS MAX77650_LED_BR_MASK | |
24 | ||
25 | /* Enable EN_LED_MSTR. */ | |
26 | #define MAX77650_LED_TOP_DEFAULT BIT(0) | |
27 | ||
28 | #define MAX77650_LED_ENABLE GENMASK(7, 6) | |
29 | #define MAX77650_LED_DISABLE 0x00 | |
30 | ||
31 | #define MAX77650_LED_A_DEFAULT MAX77650_LED_DISABLE | |
32 | /* 100% on duty */ | |
33 | #define MAX77650_LED_B_DEFAULT GENMASK(3, 0) | |
34 | ||
35 | struct max77650_led { | |
36 | struct led_classdev cdev; | |
37 | struct regmap *map; | |
38 | unsigned int regA; | |
39 | unsigned int regB; | |
40 | }; | |
41 | ||
42 | static struct max77650_led *max77650_to_led(struct led_classdev *cdev) | |
43 | { | |
44 | return container_of(cdev, struct max77650_led, cdev); | |
45 | } | |
46 | ||
47 | static int max77650_led_brightness_set(struct led_classdev *cdev, | |
48 | enum led_brightness brightness) | |
49 | { | |
50 | struct max77650_led *led = max77650_to_led(cdev); | |
51 | int val, mask; | |
52 | ||
53 | mask = MAX77650_LED_BR_MASK | MAX77650_LED_EN_MASK; | |
54 | ||
55 | if (brightness == LED_OFF) | |
56 | val = MAX77650_LED_DISABLE; | |
57 | else | |
58 | val = MAX77650_LED_ENABLE | brightness; | |
59 | ||
60 | return regmap_update_bits(led->map, led->regA, mask, val); | |
61 | } | |
62 | ||
63 | static int max77650_led_probe(struct platform_device *pdev) | |
64 | { | |
ae6c4c70 | 65 | struct fwnode_handle *child; |
5be102eb | 66 | struct max77650_led *leds, *led; |
5be102eb BG |
67 | struct device *dev; |
68 | struct regmap *map; | |
5be102eb BG |
69 | int rv, num_leds; |
70 | u32 reg; | |
71 | ||
72 | dev = &pdev->dev; | |
5be102eb BG |
73 | |
74 | leds = devm_kcalloc(dev, sizeof(*leds), | |
75 | MAX77650_LED_NUM_LEDS, GFP_KERNEL); | |
76 | if (!leds) | |
77 | return -ENOMEM; | |
78 | ||
79 | map = dev_get_regmap(dev->parent, NULL); | |
80 | if (!map) | |
81 | return -ENODEV; | |
82 | ||
ae6c4c70 | 83 | num_leds = device_get_child_node_count(dev); |
5be102eb BG |
84 | if (!num_leds || num_leds > MAX77650_LED_NUM_LEDS) |
85 | return -ENODEV; | |
86 | ||
ae6c4c70 | 87 | device_for_each_child_node(dev, child) { |
d7d02b8a MB |
88 | struct led_init_data init_data = {}; |
89 | ||
ae6c4c70 | 90 | rv = fwnode_property_read_u32(child, "reg", ®); |
730f693d ND |
91 | if (rv || reg >= MAX77650_LED_NUM_LEDS) { |
92 | rv = -EINVAL; | |
93 | goto err_node_put; | |
94 | } | |
5be102eb BG |
95 | |
96 | led = &leds[reg]; | |
97 | led->map = map; | |
98 | led->regA = MAX77650_LED_A_BASE + reg; | |
99 | led->regB = MAX77650_LED_B_BASE + reg; | |
100 | led->cdev.brightness_set_blocking = max77650_led_brightness_set; | |
101 | led->cdev.max_brightness = MAX77650_LED_MAX_BRIGHTNESS; | |
102 | ||
d7d02b8a MB |
103 | init_data.fwnode = child; |
104 | init_data.devicename = "max77650"; | |
105 | /* for backwards compatibility if `label` is not present */ | |
106 | init_data.default_label = ":"; | |
107 | ||
108 | rv = devm_led_classdev_register_ext(dev, &led->cdev, | |
109 | &init_data); | |
5be102eb | 110 | if (rv) |
730f693d | 111 | goto err_node_put; |
5be102eb BG |
112 | |
113 | rv = regmap_write(map, led->regA, MAX77650_LED_A_DEFAULT); | |
114 | if (rv) | |
730f693d | 115 | goto err_node_put; |
5be102eb BG |
116 | |
117 | rv = regmap_write(map, led->regB, MAX77650_LED_B_DEFAULT); | |
118 | if (rv) | |
730f693d | 119 | goto err_node_put; |
5be102eb BG |
120 | } |
121 | ||
122 | return regmap_write(map, | |
123 | MAX77650_REG_CNFG_LED_TOP, | |
124 | MAX77650_LED_TOP_DEFAULT); | |
730f693d | 125 | err_node_put: |
ae6c4c70 | 126 | fwnode_handle_put(child); |
730f693d | 127 | return rv; |
5be102eb BG |
128 | } |
129 | ||
2424415d BG |
130 | static const struct of_device_id max77650_led_of_match[] = { |
131 | { .compatible = "maxim,max77650-led" }, | |
132 | { } | |
133 | }; | |
134 | MODULE_DEVICE_TABLE(of, max77650_led_of_match); | |
135 | ||
5be102eb BG |
136 | static struct platform_driver max77650_led_driver = { |
137 | .driver = { | |
138 | .name = "max77650-led", | |
2424415d | 139 | .of_match_table = max77650_led_of_match, |
5be102eb BG |
140 | }, |
141 | .probe = max77650_led_probe, | |
142 | }; | |
143 | module_platform_driver(max77650_led_driver); | |
144 | ||
145 | MODULE_DESCRIPTION("MAXIM 77650/77651 LED driver"); | |
146 | MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); | |
147 | MODULE_LICENSE("GPL v2"); | |
51251542 | 148 | MODULE_ALIAS("platform:max77650-led"); |