blob: 3ae298a4c20d410c5a1428318568e1ece001be87 [file] [log] [blame]
Sylvain Munaut26bc4652020-09-14 10:19:49 +02001/*
2 * led.c
3 *
4 * Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
5 * SPDX-License-Identifier: LGPL-3.0-or-later
6 */
7
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "config.h"
12#include "led.h"
13
14
15struct ledda_ip {
16 uint32_t _rsvd0;
17 uint32_t pwrr; /* 0001 LEDDPWRR - Pulse Width Register Red */
18 uint32_t pwrg; /* 0010 LEDDPWRG - Pulse Width Register Green */
19 uint32_t pwrb; /* 0011 LEDDPWRB - Pulse Width Register Blue */
20 uint32_t _rsvd1;
21 uint32_t bcrr; /* 0101 LEDDBCRR - Breathe Control Rise Register */
22 uint32_t bcfr; /* 0101 LEDDBCFR - Breathe Control Fall Register */
23 uint32_t _rsvd2;
24 uint32_t cr0; /* 1000 LEDDCR0 - Control Register 0 */
25 uint32_t br; /* 1001 LEDDBR - Pre-scale Register */
26 uint32_t onr; /* 1010 LEDONR - ON Time Register */
27 uint32_t ofr; /* 1011 LEDOFR - OFF Time Register */
28} __attribute__((packed,aligned(4)));
29
30#define LEDDA_IP_CR0_LEDDEN (1 << 7)
31#define LEDDA_IP_CR0_FR250 (1 << 6)
32#define LEDDA_IP_CR0_OUTPOL (1 << 5)
33#define LEDDA_IP_CR0_OUTSKEW (1 << 4)
34#define LEDDA_IP_CR0_QUICK_STOP (1 << 3)
35#define LEDDA_IP_CR0_PWM_LINEAR (0 << 2)
36#define LEDDA_IP_CR0_PWM_LFSR (1 << 2)
37#define LEDDA_IP_CR0_SCALE_MSB(x) (((x) >> 8) & 3)
38
39#define LEDDA_IP_BR_SCALE_LSB(x) ((x) & 0xff)
40
41#define LEDDA_IP_ONOFF_TIME_MS(x) (((x) >> 5) & 0xff) /* 32ms interval up to 8s */
42
43#define LEDDA_IP_BREATHE_ENABLE (1 << 7)
44#define LEDDA_IP_BREATHE_MODULATE (1 << 5)
45#define LEDDA_IP_BREATHE_TIME_MS(x) (((x) >> 7) & 0x0f) /* 128ms interval up to 2s */
46
47
48struct led {
49 uint32_t csr;
50 uint32_t _rsvd[15];
51 struct ledda_ip ip;
52} __attribute__((packed,aligned(4)));
53
54#define LED_CSR_LEDDEXE (1 << 1)
55#define LED_CSR_RGBLEDEN (1 << 2)
56#define LED_CSR_CURREN (1 << 3)
57
58
59static volatile struct led * const led_regs = (void*)(LED_BASE);
60
61static const uint32_t led_cr0_base =
62 LEDDA_IP_CR0_FR250 |
63 LEDDA_IP_CR0_OUTSKEW |
64 LEDDA_IP_CR0_QUICK_STOP |
65 LEDDA_IP_CR0_PWM_LFSR |
66 LEDDA_IP_CR0_SCALE_MSB(480);
67
68
69void
70led_init(void)
71{
72 led_regs->ip.pwrr = 0;
73 led_regs->ip.pwrg = 0;
74 led_regs->ip.pwrb = 0;
75
76 led_regs->ip.bcrr = 0;
77 led_regs->ip.bcfr = 0;
78
79 led_regs->ip.onr = 0;
80 led_regs->ip.ofr = 0;
81
82 led_regs->ip.br = LEDDA_IP_BR_SCALE_LSB(480);
83 led_regs->ip.cr0 = led_cr0_base;
84
85 led_regs->csr = LED_CSR_LEDDEXE | LED_CSR_RGBLEDEN | LED_CSR_CURREN;
86}
87
88void
89led_color(uint8_t r, uint8_t g, uint8_t b)
90{
91#if defined(BOARD_ICE1USB)
92 // icE1usb
93 led_regs->ip.pwrr = b;
94 led_regs->ip.pwrg = g;
95 led_regs->ip.pwrb = r;
96#elif defined(BOARD_ICE1USB_PROTO_ICEBREAKER)
97 // iCEBreaker v1.0b tnt
98 led_regs->ip.pwrr = r;
99 led_regs->ip.pwrg = b;
100 led_regs->ip.pwrb = g;
101/*
102 // iCEBreaker v1.0c+
103 led_regs->ip.pwrr = b;
104 led_regs->ip.pwrg = g;
105 led_regs->ip.pwrb = r;
106 */
107#elif defined(BOARD_ICE1USB_PROTO_BITSY)
108 // iCEBreaker bitsy v0 (RGB led 'hacked on')
109 led_regs->ip.pwrr = g;
110 led_regs->ip.pwrg = r;
111 led_regs->ip.pwrb = b;
112#elif defined(BOARD_E1_TRACER)
113 // E1 tracer
114 led_regs->ip.pwrr = b;
115 led_regs->ip.pwrg = g;
116 led_regs->ip.pwrb = r;
117#else
118 // Default / Unknown
119 led_regs->ip.pwrr = r;
120 led_regs->ip.pwrg = g;
121 led_regs->ip.pwrb = b;
122#endif
123}
124
125void
126led_state(bool on)
127{
128 if (on)
129 led_regs->ip.cr0 = led_cr0_base | LEDDA_IP_CR0_LEDDEN;
130 else
131 led_regs->ip.cr0 = led_cr0_base;
132}
133
134void
135led_blink(bool enabled, int on_time_ms, int off_time_ms)
136{
137 /* Disable EXE before doing any change */
138 led_regs->csr = LED_CSR_RGBLEDEN | LED_CSR_CURREN;
139
140 /* Load new config */
141 if (enabled) {
142 led_regs->ip.onr = LEDDA_IP_ONOFF_TIME_MS(on_time_ms);
143 led_regs->ip.ofr = LEDDA_IP_ONOFF_TIME_MS(off_time_ms);
144 } else {
145 led_regs->ip.onr = 0;
146 led_regs->ip.ofr = 0;
147 }
148
149 /* Re-enable execution */
150 led_regs->csr = LED_CSR_LEDDEXE | LED_CSR_RGBLEDEN | LED_CSR_CURREN;
151}
152
153void
154led_breathe(bool enabled, int rise_time_ms, int fall_time_ms)
155{
156 if (enabled) {
157 led_regs->ip.bcrr = LEDDA_IP_BREATHE_ENABLE |
158 LEDDA_IP_BREATHE_MODULATE |
159 LEDDA_IP_BREATHE_TIME_MS(rise_time_ms);
160 led_regs->ip.bcfr = LEDDA_IP_BREATHE_ENABLE |
161 LEDDA_IP_BREATHE_MODULATE |
162 LEDDA_IP_BREATHE_TIME_MS(fall_time_ms);
163 } else {
164 led_regs->ip.bcrr = 0;
165 led_regs->ip.bcfr = 0;
166 }
167}