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; | |
69 | const char *label; | |
70 | int rv, num_leds; | |
71 | u32 reg; | |
72 | ||
73 | dev = &pdev->dev; | |
5be102eb BG |
74 | |
75 | leds = devm_kcalloc(dev, sizeof(*leds), | |
76 | MAX77650_LED_NUM_LEDS, GFP_KERNEL); | |
77 | if (!leds) | |
78 | return -ENOMEM; | |
79 | ||
80 | map = dev_get_regmap(dev->parent, NULL); | |
81 | if (!map) | |
82 | return -ENODEV; | |
83 | ||
ae6c4c70 | 84 | num_leds = device_get_child_node_count(dev); |
5be102eb BG |
85 | if (!num_leds || num_leds > MAX77650_LED_NUM_LEDS) |
86 | return -ENODEV; | |
87 | ||
ae6c4c70 AS |
88 | device_for_each_child_node(dev, child) { |
89 | rv = fwnode_property_read_u32(child, "reg", ®); | |
730f693d ND |
90 | if (rv || reg >= MAX77650_LED_NUM_LEDS) { |
91 | rv = -EINVAL; | |
92 | goto err_node_put; | |
93 | } | |
5be102eb BG |
94 | |
95 | led = &leds[reg]; | |
96 | led->map = map; | |
97 | led->regA = MAX77650_LED_A_BASE + reg; | |
98 | led->regB = MAX77650_LED_B_BASE + reg; | |
99 | led->cdev.brightness_set_blocking = max77650_led_brightness_set; | |
100 | led->cdev.max_brightness = MAX77650_LED_MAX_BRIGHTNESS; | |
101 | ||
ae6c4c70 AS |
102 | rv = fwnode_property_read_string(child, "label", &label); |
103 | if (rv) { | |
5be102eb BG |
104 | led->cdev.name = "max77650::"; |
105 | } else { | |
106 | led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, | |
107 | "max77650:%s", label); | |
730f693d ND |
108 | if (!led->cdev.name) { |
109 | rv = -ENOMEM; | |
110 | goto err_node_put; | |
111 | } | |
5be102eb BG |
112 | } |
113 | ||
ae6c4c70 AS |
114 | fwnode_property_read_string(child, "linux,default-trigger", |
115 | &led->cdev.default_trigger); | |
5be102eb | 116 | |
b2b998c0 | 117 | rv = devm_led_classdev_register(dev, &led->cdev); |
5be102eb | 118 | if (rv) |
730f693d | 119 | goto err_node_put; |
5be102eb BG |
120 | |
121 | rv = regmap_write(map, led->regA, MAX77650_LED_A_DEFAULT); | |
122 | if (rv) | |
730f693d | 123 | goto err_node_put; |
5be102eb BG |
124 | |
125 | rv = regmap_write(map, led->regB, MAX77650_LED_B_DEFAULT); | |
126 | if (rv) | |
730f693d | 127 | goto err_node_put; |
5be102eb BG |
128 | } |
129 | ||
130 | return regmap_write(map, | |
131 | MAX77650_REG_CNFG_LED_TOP, | |
132 | MAX77650_LED_TOP_DEFAULT); | |
730f693d | 133 | err_node_put: |
ae6c4c70 | 134 | fwnode_handle_put(child); |
730f693d | 135 | return rv; |
5be102eb BG |
136 | } |
137 | ||
2424415d BG |
138 | static const struct of_device_id max77650_led_of_match[] = { |
139 | { .compatible = "maxim,max77650-led" }, | |
140 | { } | |
141 | }; | |
142 | MODULE_DEVICE_TABLE(of, max77650_led_of_match); | |
143 | ||
5be102eb BG |
144 | static struct platform_driver max77650_led_driver = { |
145 | .driver = { | |
146 | .name = "max77650-led", | |
2424415d | 147 | .of_match_table = max77650_led_of_match, |
5be102eb BG |
148 | }, |
149 | .probe = max77650_led_probe, | |
150 | }; | |
151 | module_platform_driver(max77650_led_driver); | |
152 | ||
153 | MODULE_DESCRIPTION("MAXIM 77650/77651 LED driver"); | |
154 | MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); | |
155 | MODULE_LICENSE("GPL v2"); | |
51251542 | 156 | MODULE_ALIAS("platform:max77650-led"); |