218 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * LEDs triggers for power supply class4 *5 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>6 * Copyright © 2004 Szabolcs Gyurko7 * Copyright © 2003 Ian Molton <spyro@f2s.com>8 *9 * Modified: 2004, Oct Szabolcs Gyurko10 */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 22struct 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 30static 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 40static 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 69err_free_name:70 kfree(psy_trig->trig.name);71err_free_trigger:72 kfree(psy_trig);73 if (err)74 *err = ret;75 76 return ret;77}78 79static 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 92static 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 138static 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 147static 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 169static 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 else181 led_trigger_event(psy->trig, LED_OFF);182}183 184static 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 189static 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 196void 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 else201 power_supply_update_gen_leds(psy);202}203 204int 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 211void 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 else216 power_supply_remove_gen_triggers(psy);217}218