blob: 9e36edbee47f72eb7c95c8bffb39e8736eb92a06 [file] [log] [blame]
Michael Polyntsov353deb92024-07-19 13:12:12 +04001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Software blinking helpers
4 * Copyright (C) 2024 IOPSYS Software Solutions AB
5 * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
6 */
7
8#include <dm.h>
9#include <led.h>
10#include <time.h>
11#include <stdlib.h>
12
13#define CYCLIC_NAME_PREFIX "led_sw_blink_"
14
15static void led_sw_blink(struct cyclic_info *c)
16{
17 struct led_sw_blink *sw_blink;
18 struct udevice *dev;
19 struct led_ops *ops;
20
21 sw_blink = container_of(c, struct led_sw_blink, cyclic);
22 dev = sw_blink->dev;
23 ops = led_get_ops(dev);
24
25 switch (sw_blink->state) {
26 case LED_SW_BLINK_ST_OFF:
27 sw_blink->state = LED_SW_BLINK_ST_ON;
28 ops->set_state(dev, LEDST_ON);
29 break;
30 case LED_SW_BLINK_ST_ON:
31 sw_blink->state = LED_SW_BLINK_ST_OFF;
32 ops->set_state(dev, LEDST_OFF);
33 break;
34 case LED_SW_BLINK_ST_NOT_READY:
35 /*
36 * led_set_period has been called, but
37 * led_set_state(LDST_BLINK) has not yet,
38 * so doing nothing
39 */
40 break;
41 default:
42 break;
43 }
44}
45
46int led_sw_set_period(struct udevice *dev, int period_ms)
47{
48 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
49 struct led_sw_blink *sw_blink = uc_plat->sw_blink;
50 struct led_ops *ops = led_get_ops(dev);
51 int half_period_us;
52
53 half_period_us = period_ms * 1000 / 2;
54
55 if (!sw_blink) {
56 int len = sizeof(struct led_sw_blink) +
57 strlen(CYCLIC_NAME_PREFIX) +
58 strlen(uc_plat->label) + 1;
59
60 sw_blink = calloc(1, len);
61 if (!sw_blink)
62 return -ENOMEM;
63
64 sw_blink->dev = dev;
65 sw_blink->state = LED_SW_BLINK_ST_DISABLED;
66 strcpy((char *)sw_blink->cyclic_name, CYCLIC_NAME_PREFIX);
67 strcat((char *)sw_blink->cyclic_name, uc_plat->label);
68
69 uc_plat->sw_blink = sw_blink;
70 }
71
72 if (sw_blink->state == LED_SW_BLINK_ST_DISABLED) {
73 cyclic_register(&sw_blink->cyclic, led_sw_blink,
74 half_period_us, sw_blink->cyclic_name);
75 } else {
76 sw_blink->cyclic.delay_us = half_period_us;
77 sw_blink->cyclic.start_time_us = timer_get_us();
78 }
79
80 sw_blink->state = LED_SW_BLINK_ST_NOT_READY;
81 ops->set_state(dev, LEDST_OFF);
82
83 return 0;
84}
85
86bool led_sw_is_blinking(struct udevice *dev)
87{
88 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
89 struct led_sw_blink *sw_blink = uc_plat->sw_blink;
90
91 if (!sw_blink)
92 return false;
93
94 return sw_blink->state > LED_SW_BLINK_ST_NOT_READY;
95}
96
97bool led_sw_on_state_change(struct udevice *dev, enum led_state_t state)
98{
99 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
100 struct led_sw_blink *sw_blink = uc_plat->sw_blink;
101
102 if (!sw_blink || sw_blink->state == LED_SW_BLINK_ST_DISABLED)
103 return false;
104
105 if (state == LEDST_BLINK) {
106 /* start blinking on next led_sw_blink() call */
107 sw_blink->state = LED_SW_BLINK_ST_OFF;
108 return true;
109 }
110
111 /* stop blinking */
112 uc_plat->sw_blink = NULL;
113 cyclic_unregister(&sw_blink->cyclic);
114 free(sw_blink);
115
116 return false;
117}