Sylvain Munaut | 26bc465 | 2020-09-14 10:19:49 +0200 | [diff] [blame] | 1 | /* |
| 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 | |
| 15 | struct 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 | |
| 48 | struct 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 | |
| 59 | static volatile struct led * const led_regs = (void*)(LED_BASE); |
| 60 | |
| 61 | static 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 | |
| 69 | void |
| 70 | led_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 | |
| 88 | void |
| 89 | led_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 | |
| 125 | void |
| 126 | led_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 | |
| 134 | void |
| 135 | led_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 | |
| 153 | void |
| 154 | led_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 | } |