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        let res = (
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        res
164    }
165
166    /// Internal method to set a GPIO pins' interrupt client
167    fn set_gpio_client(&self, index: usize, client: &'client dyn hil::gpio::Client) {
168        self.gpio_clients
169            .map(|clients| clients[index] = Some(client));
170    }
171
172    /// Internal method to check whether an interrupt of a GPIO pin is
173    /// pending.
174    ///
175    /// Only GPIO pins which are in an input state will be
176    /// reported as having pending interrupts.
177    pub(self) fn gpio_interrupt_pending(&self, index: usize) -> bool {
178        self.regs.ev().event_asserted(index)
179    }
180
181    /// Internal method to configure a GPIO pin's interrupts, or
182    /// disable them.
183    pub(self) fn configure_gpio_interrupt(
184        &self,
185        index: usize,
186        edge: Option<hil::gpio::InterruptEdge>,
187    ) {
188        if let Some(e) = edge {
189            // To make sure we don't cause any CPU interrupts just
190            // because of reconfiguration, disable the event first.
191            self.regs.ev().disable_event(index);
192
193            // Now, set the configuration. Interrupts are configured
194            // in two bits:
195            // - mode: 0 for a specific edge, 1 for every edge
196            // - edge: if mode == 1, 0 for rising edge, 1 for falling egde
197            match e {
198                hil::gpio::InterruptEdge::RisingEdge => {
199                    self.regs
200                        .gpio_mode
201                        .set(self.regs.gpio_mode.get() & !(1 << index));
202                    self.regs
203                        .gpio_edge
204                        .set(self.regs.gpio_edge.get() & !(1 << index));
205                }
206                hil::gpio::InterruptEdge::FallingEdge => {
207                    self.regs
208                        .gpio_mode
209                        .set(self.regs.gpio_mode.get() & !(1 << index));
210                    self.regs
211                        .gpio_edge
212                        .set(self.regs.gpio_edge.get() & !(1 << index));
213                }
214                hil::gpio::InterruptEdge::EitherEdge => {
215                    self.regs
216                        .gpio_mode
217                        .set(self.regs.gpio_mode.get() & !(1 << index));
218                }
219            }
220
221            // (Re)enable the event associated with the GPIO pin
222            self.regs.ev().enable_event(index);
223        } else {
224            // Simply disable the interrupts in the EV. This will
225            // prevent the source from asserting the event manager's
226            // CPU interrupt.
227            self.regs.ev().disable_event(index);
228        }
229    }
230
231    pub fn service_interrupt(&self) {
232        while let Some(event_index) = self.regs.ev().next_asserted() {
233            self.regs.ev().clear_event(event_index);
234            self.gpio_clients
235                .map(|clients| clients[event_index].map(|client| client.fired()));
236        }
237    }
238}
239
240/// Single GPIO pin of a [`LiteXGPIOController`]
241///
242/// Can be obtained by calling [`LiteXGPIOController::get_gpio_pin`].
243///
244/// Only one [`LiteXGPIOPin`] instance may exist per GPIO pin. To
245/// deregister this instance, call [`LiteXGPIOPin::destroy`] (or drop it).
246pub struct LiteXGPIOPin<'controller, 'client, R: LiteXSoCRegisterConfiguration> {
247    controller: &'controller LiteXGPIOController<'client, R>,
248    index: usize,
249}
250
251impl<'controller, 'client, R: LiteXSoCRegisterConfiguration> LiteXGPIOPin<'controller, 'client, R> {
252    fn new(
253        controller: &'controller LiteXGPIOController<'client, R>,
254        index: usize,
255    ) -> LiteXGPIOPin<'controller, 'client, R> {
256        LiteXGPIOPin { controller, index }
257    }
258
259    /// Index of this GPIO pin in the [`LiteXGPIOController`] GPIO array
260    pub fn index(&self) -> usize {
261        self.index
262    }
263
264    /// Returns a reference to the [`LiteXGPIOController`] of this GPIO
265    pub fn controller(&self) -> &'controller LiteXGPIOController<'client, R> {
266        self.controller
267    }
268
269    /// Destroy (deregister & consume) the [`LiteXGPIOPin`]
270    pub fn destroy(self) {
271        mem::drop(self);
272    }
273}
274
275impl<R: LiteXSoCRegisterConfiguration> hil::gpio::Configure for LiteXGPIOPin<'_, '_, R> {
276    fn configuration(&self) -> hil::gpio::Configuration {
277        let (output_enable, _, _) = self.controller.read_gpio(self.index);
278        if output_enable {
279            hil::gpio::Configuration::Output
280        } else {
281            hil::gpio::Configuration::Input
282        }
283    }
284
285    fn make_output(&self) -> hil::gpio::Configuration {
286        self.controller.set_gpio_output_enable(self.index, true);
287        hil::gpio::Configuration::Output
288    }
289
290    fn disable_output(&self) -> hil::gpio::Configuration {
291        // Only meaningful thing to do here is to switch to being an
292        // input.
293        self.make_input()
294    }
295
296    fn make_input(&self) -> hil::gpio::Configuration {
297        self.controller.set_gpio_output_enable(self.index, false);
298        hil::gpio::Configuration::Input
299    }
300
301    fn disable_input(&self) -> hil::gpio::Configuration {
302        // The GPIO tristate pin has to be in either output or
303        // input. We can't not be in input, but also not in
304        // output. However, switching to an output when one wants to
305        // "disable_input" is pretty dangerous. We can however remain
306        // an output if we are one. Thus, do nothing and return the
307        // current configuration.
308        self.configuration()
309    }
310
311    fn deactivate_to_low_power(&self) {
312        self.make_input();
313    }
314
315    fn set_floating_state(&self, _state: hil::gpio::FloatingState) {
316        // Do nothing, we don't have any pullups we could reasonably
317        // use.
318    }
319
320    fn floating_state(&self) -> hil::gpio::FloatingState {
321        hil::gpio::FloatingState::PullNone
322    }
323}
324
325impl<R: LiteXSoCRegisterConfiguration> hil::gpio::Output for LiteXGPIOPin<'_, '_, R> {
326    fn set(&self) {
327        self.controller.set_gpio_output(self.index, true);
328    }
329
330    fn clear(&self) {
331        self.controller.set_gpio_output(self.index, false);
332    }
333
334    fn toggle(&self) -> bool {
335        let (_, current, _) = self.controller.read_gpio(self.index);
336        self.controller.set_gpio_output(self.index, !current);
337        !current
338    }
339}
340
341impl<R: LiteXSoCRegisterConfiguration> hil::gpio::Input for LiteXGPIOPin<'_, '_, R> {
342    fn read(&self) -> bool {
343        // For a proper tristate, we could probably just read it and
344        // if the pin is an output, retrieve the current output value
345        // directly. However, the simulation behaves a litte
346        // different. Thus check the pin state and either return the
347        // current input or output state, depending on output_enable.
348        let (output_enable, output, input) = self.controller.read_gpio(self.index);
349        if output_enable {
350            output
351        } else {
352            input
353        }
354    }
355}
356
357impl<'client, R: LiteXSoCRegisterConfiguration> hil::gpio::Interrupt<'client>
358    for LiteXGPIOPin<'_, 'client, R>
359{
360    fn set_client(&self, client: &'client dyn hil::gpio::Client) {
361        self.controller.set_gpio_client(self.index, client);
362    }
363
364    fn is_pending(&self) -> bool {
365        self.controller.gpio_interrupt_pending(self.index)
366    }
367
368    fn enable_interrupts(&self, mode: hil::gpio::InterruptEdge) {
369        self.controller
370            .configure_gpio_interrupt(self.index, Some(mode));
371    }
372
373    fn disable_interrupts(&self) {
374        self.controller.configure_gpio_interrupt(self.index, None);
375    }
376}
377
378impl<R: LiteXSoCRegisterConfiguration> Drop for LiteXGPIOPin<'_, '_, R> {
379    /// Deregister the GPIO with the controller
380    fn drop(&mut self) {
381        self.controller.destroy_gpio_pin(self.index);
382    }
383}