// SPDX-License-Identifier: GPL-2.0+ /* * Software blinking helpers * Copyright (C) 2024 IOPSYS Software Solutions AB * Author: Mikhail Kshevetskiy */ #include #include #include #include #include #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) { struct led_ops *ops = led_get_ops(dev); /* * toggle LED initially and start blinking on next * led_sw_blink() call. */ switch (ops->get_state(dev)) { case LEDST_ON: ops->set_state(dev, LEDST_OFF); sw_blink->state = LED_SW_BLINK_ST_OFF; default: ops->set_state(dev, LEDST_ON); sw_blink->state = LED_SW_BLINK_ST_ON; } return true; } /* stop blinking */ uc_plat->sw_blink = NULL; cyclic_unregister(&sw_blink->cyclic); free(sw_blink); return false; }