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