litex/led_controller.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! LiteX led controller (`LedChaser` core)
6//!
7//! Hardware source and documentation available at
8//! [`litex/soc/cores/led.py`](https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/led.py).
9
10use core::cell::Cell;
11use core::mem;
12use kernel::hil;
13use kernel::utilities::StaticRef;
14
15use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
16
17// TODO: Make the register width adaptable, perhaps by another trait
18// with the integer type as an associated type?
19
20/// [`LiteXLedController`] register layout
21#[repr(C)]
22pub struct LiteXLedRegisters<R: LiteXSoCRegisterConfiguration> {
23 leds_out: R::ReadWrite8,
24}
25
26/// LiteX led controller core
27pub struct LiteXLedController<R: LiteXSoCRegisterConfiguration> {
28 regs: StaticRef<LiteXLedRegisters<R>>,
29 led_count: usize,
30 led_references: Cell<u8>,
31}
32
33impl<R: LiteXSoCRegisterConfiguration> LiteXLedController<R> {
34 pub fn new(base: StaticRef<LiteXLedRegisters<R>>, led_count: usize) -> LiteXLedController<R> {
35 // The number of leds may not be larger than the bit width of
36 // the supplied register layout
37 //
38 // TODO: Automatically determine based on the type
39 assert!(
40 led_count <= 8,
41 "LiteXLedController register width insufficient to support the requested LED count"
42 );
43
44 LiteXLedController {
45 regs: base,
46 led_count,
47 led_references: Cell::new(0),
48 }
49 }
50
51 /// Initialize the [`LiteXLedController`]
52 ///
53 /// This will turn all LEDs off, thus disabling the *LED Chaser*
54 /// hardware-pattern of the LiteX core and switching to explicit
55 /// software control.
56 pub fn initialize(&self) {
57 self.regs.leds_out.set(0);
58 }
59
60 /// Returns the number of LEDs managed by the
61 /// [`LiteXLedController`]
62 pub fn led_count(&self) -> usize {
63 self.led_count
64 }
65
66 /// Create a [`LiteXLed`] instance
67 ///
68 /// To avoid duplicate use of a LED, this will return `None` if an
69 /// instance for the requested LED already exists. Call
70 /// [`LiteXLed::destroy`] (or drop the [`LiteXLed`]) to be create
71 /// a new instance for this LED.
72 pub fn get_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
73 if index < self.led_count() && (self.led_references.get() & (1 << index)) == 0 {
74 self.led_references
75 .set(self.led_references.get() | (1 << index));
76 Some(LiteXLed::new(self, index))
77 } else {
78 None
79 }
80 }
81
82 /// Create a [`LiteXLed`] without checking for duplicates
83 ///
84 /// This function must only be used in a panic handler, if no
85 /// other code will be running afterwards, in order to guarantee
86 /// consistency between ownership of the LiteXLed instance and
87 /// control over the LED state
88 ///
89 /// This function only checks whether the requested LEDs is within
90 /// the controller's range of available LEDs, but *NOT* whether
91 /// there already is a different reference to the same LED.
92 pub unsafe fn panic_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
93 if index < self.led_count() {
94 Some(LiteXLed::new(self, index))
95 } else {
96 None
97 }
98 }
99
100 /// Internal method to mark a [`LiteXLed`] instance as destroyed
101 pub(self) fn destroy_led(&self, index: usize) {
102 self.led_references
103 .set(self.led_references.get() & !(1 << index));
104 }
105
106 /// Internal method to set a LED output
107 pub(self) fn set_led(&self, index: usize, val: bool) {
108 if val {
109 self.regs
110 .leds_out
111 .set(self.regs.leds_out.get() | (1 << index));
112 } else {
113 self.regs
114 .leds_out
115 .set(self.regs.leds_out.get() & !(1 << index));
116 }
117 }
118
119 /// Internal method to read the current state of a LED
120 pub(self) fn read_led(&self, index: usize) -> bool {
121 (self.regs.leds_out.get() & (1 << index)) != 0
122 }
123}
124
125/// Single LED of a [`LiteXLedController`]
126///
127/// Can be obtained by calling [`LiteXLedController::get_led`].
128///
129/// Only one [`LiteXLed`] instance may exist per LED. To deregister
130/// this instance, call [`LiteXLed::destroy`] (or drop it).
131pub struct LiteXLed<'a, R: LiteXSoCRegisterConfiguration> {
132 controller: &'a LiteXLedController<R>,
133 index: usize,
134}
135
136impl<'a, R: LiteXSoCRegisterConfiguration> LiteXLed<'a, R> {
137 fn new(controller: &'a LiteXLedController<R>, index: usize) -> LiteXLed<'a, R> {
138 LiteXLed { controller, index }
139 }
140
141 /// Index of this LED in the [`LiteXLedController`] LED array
142 pub fn index(&self) -> usize {
143 self.index
144 }
145
146 /// Returns a reference to the [`LiteXLedController`] of this LED
147 pub fn controller(&self) -> &'a LiteXLedController<R> {
148 self.controller
149 }
150
151 /// Destroy (deregister & consume) the [`LiteXLed`]
152 pub fn destroy(self) {
153 mem::drop(self);
154 }
155}
156
157impl<R: LiteXSoCRegisterConfiguration> hil::led::Led for LiteXLed<'_, R> {
158 fn init(&self) {
159 self.controller.set_led(self.index, false);
160 }
161
162 fn on(&self) {
163 self.controller.set_led(self.index, true);
164 }
165
166 fn off(&self) {
167 self.controller.set_led(self.index, false);
168 }
169
170 fn toggle(&self) {
171 self.controller
172 .set_led(self.index, !self.controller.read_led(self.index));
173 }
174
175 fn read(&self) -> bool {
176 self.controller.read_led(self.index)
177 }
178}
179
180impl<R: LiteXSoCRegisterConfiguration> Drop for LiteXLed<'_, R> {
181 /// Deregister the LED with the controller
182 fn drop(&mut self) {
183 self.controller.destroy_led(self.index);
184 }
185}