litex/
gpio.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 Tristate GPIO controller
6//!
7//! Hardware source and documentation available at
8//! [`litex/soc/cores/gpio.py`](https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/gpio.py).
9
10use core::cell::Cell;
11use core::mem;
12use kernel::hil;
13use kernel::utilities::cells::MapCell;
14use kernel::utilities::StaticRef;
15
16use crate::event_manager::LiteXEventManager;
17use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
18
19// TODO: Make the register width adaptable, perhaps by another trait
20// with the integer type as an associated type?
21
22type LiteXGPIOEV<'a, R> = LiteXEventManager<
23    'a,
24    u32,
25    <R as LiteXSoCRegisterConfiguration>::ReadOnly32,
26    <R as LiteXSoCRegisterConfiguration>::ReadWrite32,
27    <R as LiteXSoCRegisterConfiguration>::ReadWrite32,
28>;
29
30/// [`LiteXGPIOController`] register layout
31#[repr(C)]
32pub struct LiteXGPIORegisters<R: LiteXSoCRegisterConfiguration> {
33    gpio_output_enable: R::ReadWrite32,
34    gpio_input: R::ReadOnly32,
35    gpio_output: R::ReadWrite32,
36    gpio_mode: R::ReadWrite32,
37    gpio_edge: R::ReadWrite32,
38    gpio_ev_status: R::ReadOnly32,
39    gpio_ev_pending: R::ReadWrite32,
40    gpio_ev_enable: R::ReadWrite32,
41}
42
43impl<R: LiteXSoCRegisterConfiguration> LiteXGPIORegisters<R> {
44    fn ev(&self) -> LiteXGPIOEV<'_, R> {
45        LiteXGPIOEV::<R>::new(
46            &self.gpio_ev_status,
47            &self.gpio_ev_pending,
48            &self.gpio_ev_enable,
49        )
50    }
51}
52
53/// LiteX Tristate GPIO controller core
54pub struct LiteXGPIOController<'client, R: LiteXSoCRegisterConfiguration> {
55    regs: StaticRef<LiteXGPIORegisters<R>>,
56    gpio_count: usize,
57    gpio_references: Cell<u32>,
58    // We can't reasonably put this field only on the GPIOPin
59    // instances, as then the controller would need to have a way to
60    // call out to them. Thus, allocate space for a client for every
61    // pin in the controller.
62    gpio_clients: MapCell<[Option<&'client dyn hil::gpio::Client>; 32]>,
63}
64
65impl<'client, R: LiteXSoCRegisterConfiguration> LiteXGPIOController<'client, R> {
66    pub fn new(
67        base: StaticRef<LiteXGPIORegisters<R>>,
68        gpio_count: usize,
69    ) -> LiteXGPIOController<'client, R> {
70        // The number of GPIOs may not be larger than the bit width of
71        // the supplied register layout
72        //
73        // TODO: Automatically determine based on the type
74        assert!(
75            gpio_count <= 32,
76            "LiteXGPIOController register width insufficient to support the requested GPIO count"
77        );
78
79        LiteXGPIOController {
80            regs: base,
81            gpio_count,
82            gpio_references: Cell::new(0),
83            gpio_clients: MapCell::new([None; 32]),
84        }
85    }
86
87    /// Initialize the [`LiteXGPIOController`]
88    ///
89    /// This will set all GPIOs to be inputs.
90    pub fn initialize(&self) {
91        self.regs.gpio_output_enable.set(0);
92        self.regs.ev().disable_all();
93        self.regs.ev().clear_all();
94    }
95
96    /// Returns the number of GPIOs managed by the
97    /// [`LiteXGPIOController`]
98    pub fn gpio_count(&self) -> usize {
99        self.gpio_count
100    }
101
102    /// Create a [`LiteXGPIOPin`] instance
103    ///
104    /// To avoid duplicate use of a GPIO, this will return `None` if
105    /// an instance for the requested GPIO already exists. Call
106    /// [`LiteXGPIOPin::destroy`] (or drop the [`LiteXGPIOPin`]) to be
107    /// able to create a new instance for this GPIO.
108    pub fn get_gpio_pin<'controller>(
109        &'controller self,
110        index: usize,
111    ) -> Option<LiteXGPIOPin<'controller, 'client, R>> {
112        if index < self.gpio_count() && (self.gpio_references.get() & (1 << index)) == 0 {
113            self.gpio_references
114                .set(self.gpio_references.get() | (1 << index));
115            Some(LiteXGPIOPin::new(self, index))
116        } else {
117            None
118        }
119    }
120
121    /// Internal method to mark a [`LiteXGPIOPin`] instance as destroyed
122    pub(self) fn destroy_gpio_pin(&self, index: usize) {
123        self.gpio_clients.map(|clients| clients[index] = None);
124        self.gpio_references
125            .set(self.gpio_references.get() & !(1 << index));
126    }
127
128    /// Internal method to set a GPIO output enable configuration
129    pub(self) fn set_gpio_output_enable(&self, index: usize, oe: bool) {
130        if oe {
131            self.regs
132                .gpio_output_enable
133                .set(self.regs.gpio_output_enable.get() | (1 << index));
134        } else {
135            self.regs
136                .gpio_output_enable
137                .set(self.regs.gpio_output_enable.get() & !(1 << index));
138        }
139    }
140
141    /// Internal method to set a GPIO output
142    pub(self) fn set_gpio_output(&self, index: usize, output: bool) {
143        if output {
144            self.regs
145                .gpio_output
146                .set(self.regs.gpio_output.get() | (1 << index));
147        } else {
148            self.regs
149                .gpio_output
150                .set(self.regs.gpio_output.get() & !(1 << index));
151        }
152    }
153
154    /// Internal method to read the current state of a GPIO
155    ///
156    /// Returns a tuple of (oe, out, in).
157    pub(self) fn read_gpio(&self, index: usize) -> (bool, bool, bool) {
158        (
159            (self.regs.gpio_output_enable.get() & (1 << index)) != 0,
160            (self.regs.gpio_output.get() & (1 << index)) != 0,
161            (self.regs.gpio_input.get() & (1 << index)) != 0,
162        )
163    }
164
165    /// Internal method to set a GPIO pins' interrupt client
166    fn set_gpio_client(&self, index: usize, client: &'client dyn hil::gpio::Client) {
167        self.gpio_clients
168            .map(|clients| clients[index] = Some(client));
169    }
170
171    /// Internal method to check whether an interrupt of a GPIO pin is
172    /// pending.
173    ///
174    /// Only GPIO pins which are in an input state will be
175    /// reported as having pending interrupts.
176    pub(self) fn gpio_interrupt_pending(&self, index: usize) -> bool {
177        self.regs.ev().event_asserted(index)
178    }
179
180    /// Internal method to configure a GPIO pin's interrupts, or
181    /// disable them.
182    pub(self) fn configure_gpio_interrupt(
183        &self,
184        index: usize,
185        edge: Option<hil::gpio::InterruptEdge>,
186    ) {
187        if let Some(e) = edge {
188            // To make sure we don't cause any CPU interrupts just
189            // because of reconfiguration, disable the event first.
190            self.regs.ev().disable_event(index);
191
192            // Now, set the configuration. Interrupts are configured
193            // in two bits:
194            // - mode: 0 for a specific edge, 1 for every edge
195            // - edge: if mode == 1, 0 for rising edge, 1 for falling egde
196            match e {
197                hil::gpio::InterruptEdge::RisingEdge => {
198                    self.regs
199                        .gpio_mode
200                        .set(self.regs.gpio_mode.get() & !(1 << index));
201                    self.regs
202                        .gpio_edge
203                        .set(self.regs.gpio_edge.get() & !(1 << index));
204                }
205                hil::gpio::InterruptEdge::FallingEdge => {
206                    self.regs
207                        .gpio_mode
208                        .set(self.regs.gpio_mode.get() & !(1 << index));
209                    self.regs
210                        .gpio_edge
211                        .set(self.regs.gpio_edge.get() & !(1 << index));
212                }
213                hil::gpio::InterruptEdge::EitherEdge => {
214                    self.regs
215                        .gpio_mode
216                        .set(self.regs.gpio_mode.get() & !(1 << index));
217                }
218            }
219
220            // (Re)enable the event associated with the GPIO pin
221            self.regs.ev().enable_event(index);
222        } else {
223            // Simply disable the interrupts in the EV. This will
224            // prevent the source from asserting the event manager's
225            // CPU interrupt.
226            self.regs.ev().disable_event(index);
227        }
228    }
229
230    pub fn service_interrupt(&self) {
231        while let Some(event_index) = self.regs.ev().next_asserted() {
232            self.regs.ev().clear_event(event_index);
233            self.gpio_clients
234                .map(|clients| clients[event_index].map(|client| client.fired()));
235        }
236    }
237}
238
239/// Single GPIO pin of a [`LiteXGPIOController`]
240///
241/// Can be obtained by calling [`LiteXGPIOController::get_gpio_pin`].
242///
243/// Only one [`LiteXGPIOPin`] instance may exist per GPIO pin. To
244/// deregister this instance, call [`LiteXGPIOPin::destroy`] (or drop it).
245pub struct LiteXGPIOPin<'controller, 'client, R: LiteXSoCRegisterConfiguration> {
246    controller: &'controller LiteXGPIOController<'client, R>,
247    index: usize,
248}
249
250impl<'controller, 'client, R: LiteXSoCRegisterConfiguration> LiteXGPIOPin<'controller, 'client, R> {
251    fn new(
252        controller: &'controller LiteXGPIOController<'client, R>,
253        index: usize,
254    ) -> LiteXGPIOPin<'controller, 'client, R> {
255        LiteXGPIOPin { controller, index }
256    }
257
258    /// Index of this GPIO pin in the [`LiteXGPIOController`] GPIO array
259    pub fn index(&self) -> usize {
260        self.index
261    }
262
263    /// Returns a reference to the [`LiteXGPIOController`] of this GPIO
264    pub fn controller(&self) -> &'controller LiteXGPIOController<'client, R> {
265        self.controller
266    }
267
268    /// Destroy (deregister & consume) the [`LiteXGPIOPin`]
269    pub fn destroy(self) {
270        mem::drop(self);
271    }
272}
273
274impl<R: LiteXSoCRegisterConfiguration> hil::gpio::Configure for LiteXGPIOPin<'_, '_, R> {
275    fn configuration(&self) -> hil::gpio::Configuration {
276        let (output_enable, _, _) = self.controller.read_gpio(self.index);
277        if output_enable {
278            hil::gpio::Configuration::Output
279        } else {
280            hil::gpio::Configuration::Input
281        }
282    }
283
284    fn make_output(&self) -> hil::gpio::Configuration {
285        self.controller.set_gpio_output_enable(self.index, true);
286        hil::gpio::Configuration::Output
287    }
288
289    fn disable_output(&self) -> hil::gpio::Configuration {
290        // Only meaningful thing to do here is to switch to being an
291        // input.
292        self.make_input()
293    }
294
295    fn make_input(&self) -> hil::gpio::Configuration {
296        self.controller.set_gpio_output_enable(self.index, false);
297        hil::gpio::Configuration::Input
298    }
299
300    fn disable_input(&self) -> hil::gpio::Configuration {
301        // The GPIO tristate pin has to be in either output or
302        // input. We can't not be in input, but also not in
303        // output. However, switching to an output when one wants to
304        // "disable_input" is pretty dangerous. We can however remain
305        // an output if we are one. Thus, do nothing and return the
306        // current configuration.
307        self.configuration()
308    }
309
310    fn deactivate_to_low_power(&self) {
311        self.make_input();
312    }
313
314    fn set_floating_state(&self, _state: hil::gpio::FloatingState) {
315        // Do nothing, we don't have any pullups we could reasonably
316        // use.
317    }
318
319    fn floating_state(&self) -> hil::gpio::FloatingState {
320        hil::gpio::FloatingState::PullNone
321    }
322}
323
324impl<R: LiteXSoCRegisterConfiguration> hil::gpio::Output for LiteXGPIOPin<'_, '_, R> {
325    fn set(&self) {
326        self.controller.set_gpio_output(self.index, true);
327    }
328
329    fn clear(&self) {
330        self.controller.set_gpio_output(self.index, false);
331    }
332
333    fn toggle(&self) -> bool {
334        let (_, current, _) = self.controller.read_gpio(self.index);
335        self.controller.set_gpio_output(self.index, !current);
336        !current
337    }
338}
339
340impl<R: LiteXSoCRegisterConfiguration> hil::gpio::Input for LiteXGPIOPin<'_, '_, R> {
341    fn read(&self) -> bool {
342        // For a proper tristate, we could probably just read it and
343        // if the pin is an output, retrieve the current output value
344        // directly. However, the simulation behaves a litte
345        // different. Thus check the pin state and either return the
346        // current input or output state, depending on output_enable.
347        let (output_enable, output, input) = self.controller.read_gpio(self.index);
348        if output_enable {
349            output
350        } else {
351            input
352        }
353    }
354}
355
356impl<'client, R: LiteXSoCRegisterConfiguration> hil::gpio::Interrupt<'client>
357    for LiteXGPIOPin<'_, 'client, R>
358{
359    fn set_client(&self, client: &'client dyn hil::gpio::Client) {
360        self.controller.set_gpio_client(self.index, client);
361    }
362
363    fn is_pending(&self) -> bool {
364        self.controller.gpio_interrupt_pending(self.index)
365    }
366
367    fn enable_interrupts(&self, mode: hil::gpio::InterruptEdge) {
368        self.controller
369            .configure_gpio_interrupt(self.index, Some(mode));
370    }
371
372    fn disable_interrupts(&self) {
373        self.controller.configure_gpio_interrupt(self.index, None);
374    }
375}
376
377impl<R: LiteXSoCRegisterConfiguration> Drop for LiteXGPIOPin<'_, '_, R> {
378    /// Deregister the GPIO with the controller
379    fn drop(&mut self) {
380        self.controller.destroy_gpio_pin(self.index);
381    }
382}