blob: ee1546d02d473c37fe8475489122de26aee22bb9 [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
Rasmus Villemoes1654d382024-10-03 23:27:52 +02008#include <cyclic.h>
Michael Polyntsov353deb92024-07-19 13:12:12 +04009#include <dm.h>
10#include <led.h>
11#include <time.h>
12#include <stdlib.h>
13
14#define CYCLIC_NAME_PREFIX "led_sw_blink_"
15
16static void led_sw_blink(struct cyclic_info *c)
17{
18 struct led_sw_blink *sw_blink;
19 struct udevice *dev;
20 struct led_ops *ops;
21
22 sw_blink = container_of(c, struct led_sw_blink, cyclic);
23 dev = sw_blink->dev;
24 ops = led_get_ops(dev);
25
26 switch (sw_blink->state) {
27 case LED_SW_BLINK_ST_OFF:
28 sw_blink->state = LED_SW_BLINK_ST_ON;
29 ops->set_state(dev, LEDST_ON);
30 break;
31 case LED_SW_BLINK_ST_ON:
32 sw_blink->state = LED_SW_BLINK_ST_OFF;
33 ops->set_state(dev, LEDST_OFF);
34 break;
35 case LED_SW_BLINK_ST_NOT_READY:
36 /*
37 * led_set_period has been called, but
38 * led_set_state(LDST_BLINK) has not yet,
39 * so doing nothing
40 */
41 break;
42 default:
43 break;
44 }
45}
46
47int led_sw_set_period(struct udevice *dev, int period_ms)
48{
49 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
50 struct led_sw_blink *sw_blink = uc_plat->sw_blink;
51 struct led_ops *ops = led_get_ops(dev);
52 int half_period_us;
53
54 half_period_us = period_ms * 1000 / 2;
55
56 if (!sw_blink) {
57 int len = sizeof(struct led_sw_blink) +
58 strlen(CYCLIC_NAME_PREFIX) +
59 strlen(uc_plat->label) + 1;
60
61 sw_blink = calloc(1, len);
62 if (!sw_blink)
63 return -ENOMEM;
64
65 sw_blink->dev = dev;
66 sw_blink->state = LED_SW_BLINK_ST_DISABLED;
67 strcpy((char *)sw_blink->cyclic_name, CYCLIC_NAME_PREFIX);
68 strcat((char *)sw_blink->cyclic_name, uc_plat->label);
69
70 uc_plat->sw_blink = sw_blink;
71 }
72
73 if (sw_blink->state == LED_SW_BLINK_ST_DISABLED) {
74 cyclic_register(&sw_blink->cyclic, led_sw_blink,
75 half_period_us, sw_blink->cyclic_name);
76 } else {
77 sw_blink->cyclic.delay_us = half_period_us;
78 sw_blink->cyclic.start_time_us = timer_get_us();
79 }
80
81 sw_blink->state = LED_SW_BLINK_ST_NOT_READY;
82 ops->set_state(dev, LEDST_OFF);
83
84 return 0;
85}
86
87bool led_sw_is_blinking(struct udevice *dev)
88{
89 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
90 struct led_sw_blink *sw_blink = uc_plat->sw_blink;
91
92 if (!sw_blink)
93 return false;
94
95 return sw_blink->state > LED_SW_BLINK_ST_NOT_READY;
96}
97
98bool led_sw_on_state_change(struct udevice *dev, enum led_state_t state)
99{
100 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
101 struct led_sw_blink *sw_blink = uc_plat->sw_blink;
102
103 if (!sw_blink || sw_blink->state == LED_SW_BLINK_ST_DISABLED)
104 return false;
105
106 if (state == LEDST_BLINK) {
Christian Marangibe131cf2024-10-01 14:24:34 +0200107 struct led_ops *ops = led_get_ops(dev);
108
109 /*
110 * toggle LED initially and start blinking on next
111 * led_sw_blink() call.
112 */
113 switch (ops->get_state(dev)) {
114 case LEDST_ON:
115 ops->set_state(dev, LEDST_OFF);
116 sw_blink->state = LED_SW_BLINK_ST_OFF;
117 default:
118 ops->set_state(dev, LEDST_ON);
119 sw_blink->state = LED_SW_BLINK_ST_ON;
120 }
121
Michael Polyntsov353deb92024-07-19 13:12:12 +0400122 return true;
123 }
124
125 /* stop blinking */
126 uc_plat->sw_blink = NULL;
127 cyclic_unregister(&sw_blink->cyclic);
128 free(sw_blink);
129
130 return false;
131}