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.
45//! 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).
910use core::cell::Cell;
11use core::mem;
12use kernel::hil;
13use kernel::utilities::StaticRef;
1415use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
1617// TODO: Make the register width adaptable, perhaps by another trait
18// with the integer type as an associated type?
1920/// [`LiteXLedController`] register layout
21#[repr(C)]
22pub struct LiteXLedRegisters<R: LiteXSoCRegisterConfiguration> {
23 leds_out: R::ReadWrite8,
24}
2526/// LiteX led controller core
27pub struct LiteXLedController<R: LiteXSoCRegisterConfiguration> {
28 regs: StaticRef<LiteXLedRegisters<R>>,
29 led_count: usize,
30 led_references: Cell<u8>,
31}
3233impl<R: LiteXSoCRegisterConfiguration> LiteXLedController<R> {
34pub 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
39assert!(
40 led_count <= 8,
41"LiteXLedController register width insufficient to support the requested LED count"
42);
4344 LiteXLedController {
45 regs: base,
46 led_count,
47 led_references: Cell::new(0),
48 }
49 }
5051/// 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.
56pub fn initialize(&self) {
57self.regs.leds_out.set(0);
58 }
5960/// Returns the number of LEDs managed by the
61 /// [`LiteXLedController`]
62pub fn led_count(&self) -> usize {
63self.led_count
64 }
6566/// 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.
72pub fn get_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
73if index < self.led_count() && (self.led_references.get() & (1 << index)) == 0 {
74self.led_references
75 .set(self.led_references.get() | (1 << index));
76Some(LiteXLed::new(self, index))
77 } else {
78None
79}
80 }
8182/// 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.
92pub unsafe fn panic_led(&self, index: usize) -> Option<LiteXLed<'_, R>> {
93if index < self.led_count() {
94Some(LiteXLed::new(self, index))
95 } else {
96None
97}
98 }
99100/// Internal method to mark a [`LiteXLed`] instance as destroyed
101pub(self) fn destroy_led(&self, index: usize) {
102self.led_references
103 .set(self.led_references.get() & !(1 << index));
104 }
105106/// Internal method to set a LED output
107pub(self) fn set_led(&self, index: usize, val: bool) {
108if val {
109self.regs
110 .leds_out
111 .set(self.regs.leds_out.get() | (1 << index));
112 } else {
113self.regs
114 .leds_out
115 .set(self.regs.leds_out.get() & !(1 << index));
116 }
117 }
118119/// Internal method to read the current state of a LED
120pub(self) fn read_led(&self, index: usize) -> bool {
121 (self.regs.leds_out.get() & (1 << index)) != 0
122}
123}
124125/// 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}
135136impl<'a, R: LiteXSoCRegisterConfiguration> LiteXLed<'a, R> {
137fn new(controller: &'a LiteXLedController<R>, index: usize) -> LiteXLed<'a, R> {
138 LiteXLed { controller, index }
139 }
140141/// Index of this LED in the [`LiteXLedController`] LED array
142pub fn index(&self) -> usize {
143self.index
144 }
145146/// Returns a reference to the [`LiteXLedController`] of this LED
147pub fn controller(&self) -> &'a LiteXLedController<R> {
148self.controller
149 }
150151/// Destroy (deregister & consume) the [`LiteXLed`]
152pub fn destroy(self) {
153 mem::drop(self);
154 }
155}
156157impl<R: LiteXSoCRegisterConfiguration> hil::led::Led for LiteXLed<'_, R> {
158fn init(&self) {
159self.controller.set_led(self.index, false);
160 }
161162fn on(&self) {
163self.controller.set_led(self.index, true);
164 }
165166fn off(&self) {
167self.controller.set_led(self.index, false);
168 }
169170fn toggle(&self) {
171self.controller
172 .set_led(self.index, !self.controller.read_led(self.index));
173 }
174175fn read(&self) -> bool {
176self.controller.read_led(self.index)
177 }
178}
179180impl<R: LiteXSoCRegisterConfiguration> Drop for LiteXLed<'_, R> {
181/// Deregister the LED with the controller
182fn drop(&mut self) {
183self.controller.destroy_led(self.index);
184 }
185}