1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * LEDs triggers for power supply class
4 *
5 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
6 * Copyright © 2004 Szabolcs Gyurko
7 * Copyright © 2003 Ian Molton <spyro@f2s.com>
8 *
9 * Modified: 2004, Oct Szabolcs Gyurko
10 */
11
12 #include <linux/kernel.h>
13 #include <linux/device.h>
14 #include <linux/power_supply.h>
15 #include <linux/slab.h>
16 #include <linux/leds.h>
17
18 #include "power_supply.h"
19
20 /* Battery specific LEDs triggers. */
21
22 struct power_supply_led_trigger {
23 struct led_trigger trig;
24 struct power_supply *psy;
25 };
26
27 #define trigger_to_psy_trigger(trigger) \
28 container_of(trigger, struct power_supply_led_trigger, trig)
29
power_supply_led_trigger_activate(struct led_classdev * led_cdev)30 static int power_supply_led_trigger_activate(struct led_classdev *led_cdev)
31 {
32 struct power_supply_led_trigger *psy_trig =
33 trigger_to_psy_trigger(led_cdev->trigger);
34
35 /* Sync current power-supply state to LED being activated */
36 power_supply_update_leds(psy_trig->psy);
37 return 0;
38 }
39
power_supply_register_led_trigger(struct power_supply * psy,const char * name_template,struct led_trigger ** tp,int * err)40 static int power_supply_register_led_trigger(struct power_supply *psy,
41 const char *name_template,
42 struct led_trigger **tp, int *err)
43 {
44 struct power_supply_led_trigger *psy_trig;
45 int ret = -ENOMEM;
46
47 /* Bail on previous errors */
48 if (err && *err)
49 return *err;
50
51 psy_trig = kzalloc(sizeof(*psy_trig), GFP_KERNEL);
52 if (!psy_trig)
53 goto err_free_trigger;
54
55 psy_trig->trig.name = kasprintf(GFP_KERNEL, name_template, psy->desc->name);
56 if (!psy_trig->trig.name)
57 goto err_free_trigger;
58
59 psy_trig->trig.activate = power_supply_led_trigger_activate;
60 psy_trig->psy = psy;
61
62 ret = led_trigger_register(&psy_trig->trig);
63 if (ret)
64 goto err_free_name;
65
66 *tp = &psy_trig->trig;
67 return 0;
68
69 err_free_name:
70 kfree(psy_trig->trig.name);
71 err_free_trigger:
72 kfree(psy_trig);
73 if (err)
74 *err = ret;
75
76 return ret;
77 }
78
power_supply_unregister_led_trigger(struct led_trigger * trig)79 static void power_supply_unregister_led_trigger(struct led_trigger *trig)
80 {
81 struct power_supply_led_trigger *psy_trig;
82
83 if (!trig)
84 return;
85
86 psy_trig = trigger_to_psy_trigger(trig);
87 led_trigger_unregister(&psy_trig->trig);
88 kfree(psy_trig->trig.name);
89 kfree(psy_trig);
90 }
91
power_supply_update_bat_leds(struct power_supply * psy)92 static void power_supply_update_bat_leds(struct power_supply *psy)
93 {
94 union power_supply_propval status;
95 unsigned int intensity_green[3] = { 0, 255, 0 };
96 unsigned int intensity_orange[3] = { 255, 128, 0 };
97
98 if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
99 return;
100
101 dev_dbg(&psy->dev, "%s %d\n", __func__, status.intval);
102
103 switch (status.intval) {
104 case POWER_SUPPLY_STATUS_FULL:
105 led_trigger_event(psy->trig, LED_FULL);
106 led_trigger_event(psy->charging_trig, LED_OFF);
107 led_trigger_event(psy->full_trig, LED_FULL);
108 /* Going from blink to LED on requires a LED_OFF event to stop blink */
109 led_trigger_event(psy->charging_blink_full_solid_trig, LED_OFF);
110 led_trigger_event(psy->charging_blink_full_solid_trig, LED_FULL);
111 led_mc_trigger_event(psy->charging_orange_full_green_trig,
112 intensity_green,
113 ARRAY_SIZE(intensity_green),
114 LED_FULL);
115 break;
116 case POWER_SUPPLY_STATUS_CHARGING:
117 led_trigger_event(psy->trig, LED_FULL);
118 led_trigger_event(psy->charging_trig, LED_FULL);
119 led_trigger_event(psy->full_trig, LED_OFF);
120 led_trigger_blink(psy->charging_blink_full_solid_trig, 0, 0);
121 led_mc_trigger_event(psy->charging_orange_full_green_trig,
122 intensity_orange,
123 ARRAY_SIZE(intensity_orange),
124 LED_FULL);
125 break;
126 default:
127 led_trigger_event(psy->trig, LED_OFF);
128 led_trigger_event(psy->charging_trig, LED_OFF);
129 led_trigger_event(psy->full_trig, LED_OFF);
130 led_trigger_event(psy->charging_blink_full_solid_trig,
131 LED_OFF);
132 led_trigger_event(psy->charging_orange_full_green_trig,
133 LED_OFF);
134 break;
135 }
136 }
137
power_supply_remove_bat_triggers(struct power_supply * psy)138 static void power_supply_remove_bat_triggers(struct power_supply *psy)
139 {
140 power_supply_unregister_led_trigger(psy->trig);
141 power_supply_unregister_led_trigger(psy->charging_trig);
142 power_supply_unregister_led_trigger(psy->full_trig);
143 power_supply_unregister_led_trigger(psy->charging_blink_full_solid_trig);
144 power_supply_unregister_led_trigger(psy->charging_orange_full_green_trig);
145 }
146
power_supply_create_bat_triggers(struct power_supply * psy)147 static int power_supply_create_bat_triggers(struct power_supply *psy)
148 {
149 int err = 0;
150
151 power_supply_register_led_trigger(psy, "%s-charging-or-full",
152 &psy->trig, &err);
153 power_supply_register_led_trigger(psy, "%s-charging",
154 &psy->charging_trig, &err);
155 power_supply_register_led_trigger(psy, "%s-full",
156 &psy->full_trig, &err);
157 power_supply_register_led_trigger(psy, "%s-charging-blink-full-solid",
158 &psy->charging_blink_full_solid_trig, &err);
159 power_supply_register_led_trigger(psy, "%s-charging-orange-full-green",
160 &psy->charging_orange_full_green_trig, &err);
161 if (err)
162 power_supply_remove_bat_triggers(psy);
163
164 return err;
165 }
166
167 /* Generated power specific LEDs triggers. */
168
power_supply_update_gen_leds(struct power_supply * psy)169 static void power_supply_update_gen_leds(struct power_supply *psy)
170 {
171 union power_supply_propval online;
172
173 if (power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
174 return;
175
176 dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval);
177
178 if (online.intval)
179 led_trigger_event(psy->trig, LED_FULL);
180 else
181 led_trigger_event(psy->trig, LED_OFF);
182 }
183
power_supply_create_gen_triggers(struct power_supply * psy)184 static int power_supply_create_gen_triggers(struct power_supply *psy)
185 {
186 return power_supply_register_led_trigger(psy, "%s-online", &psy->trig, NULL);
187 }
188
power_supply_remove_gen_triggers(struct power_supply * psy)189 static void power_supply_remove_gen_triggers(struct power_supply *psy)
190 {
191 power_supply_unregister_led_trigger(psy->trig);
192 }
193
194 /* Choice what triggers to create&update. */
195
power_supply_update_leds(struct power_supply * psy)196 void power_supply_update_leds(struct power_supply *psy)
197 {
198 if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
199 power_supply_update_bat_leds(psy);
200 else
201 power_supply_update_gen_leds(psy);
202 }
203
power_supply_create_triggers(struct power_supply * psy)204 int power_supply_create_triggers(struct power_supply *psy)
205 {
206 if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
207 return power_supply_create_bat_triggers(psy);
208 return power_supply_create_gen_triggers(psy);
209 }
210
power_supply_remove_triggers(struct power_supply * psy)211 void power_supply_remove_triggers(struct power_supply *psy)
212 {
213 if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
214 power_supply_remove_bat_triggers(psy);
215 else
216 power_supply_remove_gen_triggers(psy);
217 }
218