| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Software blinking helpers | |
| * Copyright (C) 2024 IOPSYS Software Solutions AB | |
| * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> | |
| */ | |
| #include <dm.h> | |
| #include <led.h> | |
| #include <time.h> | |
| #include <stdlib.h> | |
| #define CYCLIC_NAME_PREFIX "led_sw_blink_" | |
| static void led_sw_blink(struct cyclic_info *c) | |
| { | |
| struct led_sw_blink *sw_blink; | |
| struct udevice *dev; | |
| struct led_ops *ops; | |
| sw_blink = container_of(c, struct led_sw_blink, cyclic); | |
| dev = sw_blink->dev; | |
| ops = led_get_ops(dev); | |
| switch (sw_blink->state) { | |
| case LED_SW_BLINK_ST_OFF: | |
| sw_blink->state = LED_SW_BLINK_ST_ON; | |
| ops->set_state(dev, LEDST_ON); | |
| break; | |
| case LED_SW_BLINK_ST_ON: | |
| sw_blink->state = LED_SW_BLINK_ST_OFF; | |
| ops->set_state(dev, LEDST_OFF); | |
| break; | |
| case LED_SW_BLINK_ST_NOT_READY: | |
| /* | |
| * led_set_period has been called, but | |
| * led_set_state(LDST_BLINK) has not yet, | |
| * so doing nothing | |
| */ | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| int led_sw_set_period(struct udevice *dev, int period_ms) | |
| { | |
| struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); | |
| struct led_sw_blink *sw_blink = uc_plat->sw_blink; | |
| struct led_ops *ops = led_get_ops(dev); | |
| int half_period_us; | |
| half_period_us = period_ms * 1000 / 2; | |
| if (!sw_blink) { | |
| int len = sizeof(struct led_sw_blink) + | |
| strlen(CYCLIC_NAME_PREFIX) + | |
| strlen(uc_plat->label) + 1; | |
| sw_blink = calloc(1, len); | |
| if (!sw_blink) | |
| return -ENOMEM; | |
| sw_blink->dev = dev; | |
| sw_blink->state = LED_SW_BLINK_ST_DISABLED; | |
| strcpy((char *)sw_blink->cyclic_name, CYCLIC_NAME_PREFIX); | |
| strcat((char *)sw_blink->cyclic_name, uc_plat->label); | |
| uc_plat->sw_blink = sw_blink; | |
| } | |
| if (sw_blink->state == LED_SW_BLINK_ST_DISABLED) { | |
| cyclic_register(&sw_blink->cyclic, led_sw_blink, | |
| half_period_us, sw_blink->cyclic_name); | |
| } else { | |
| sw_blink->cyclic.delay_us = half_period_us; | |
| sw_blink->cyclic.start_time_us = timer_get_us(); | |
| } | |
| sw_blink->state = LED_SW_BLINK_ST_NOT_READY; | |
| ops->set_state(dev, LEDST_OFF); | |
| return 0; | |
| } | |
| bool led_sw_is_blinking(struct udevice *dev) | |
| { | |
| struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); | |
| struct led_sw_blink *sw_blink = uc_plat->sw_blink; | |
| if (!sw_blink) | |
| return false; | |
| return sw_blink->state > LED_SW_BLINK_ST_NOT_READY; | |
| } | |
| bool led_sw_on_state_change(struct udevice *dev, enum led_state_t state) | |
| { | |
| struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); | |
| struct led_sw_blink *sw_blink = uc_plat->sw_blink; | |
| if (!sw_blink || sw_blink->state == LED_SW_BLINK_ST_DISABLED) | |
| return false; | |
| if (state == LEDST_BLINK) { | |
| /* start blinking on next led_sw_blink() call */ | |
| sw_blink->state = LED_SW_BLINK_ST_OFF; | |
| return true; | |
| } | |
| /* stop blinking */ | |
| uc_plat->sw_blink = NULL; | |
| cyclic_unregister(&sw_blink->cyclic); | |
| free(sw_blink); | |
| return false; | |
| } |