xref: /linux/drivers/power/supply/power_supply_leds.c (revision 55d0969c451159cff86949b38c39171cab962069)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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