diff options
Diffstat (limited to 'drivers/led/led_sw_blink.c')
| -rw-r--r-- | drivers/led/led_sw_blink.c | 117 | 
1 files changed, 117 insertions, 0 deletions
| diff --git a/drivers/led/led_sw_blink.c b/drivers/led/led_sw_blink.c new file mode 100644 index 00000000000..9e36edbee47 --- /dev/null +++ b/drivers/led/led_sw_blink.c @@ -0,0 +1,117 @@ +// 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; +} | 
