kernel/bsp/device_driver/bcm/
bcm2xxx_gpio.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
//! GPIO Driver.

use crate::{
    bsp::device_driver::common::MMIODerefWrapper,
    driver,
    synchronization::{self, NullLock},
};
use tock_registers::{
    interfaces::{ReadWriteable, Writeable},
    register_bitfields, register_structs,
    registers::ReadWrite,
};

//----------------------------------------------------------------------------
// Private Definitions
//----------------------------------------------------------------------------

// GPIO registers.
//
// Descriptions taken from
// - RPI3: https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// - RPI4: https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf
// - RPI5: https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf
register_bitfields! {
  u32,

  /// GPIO Function Select 1
  GPFSEL1 [
    // Pin 15
    FSEL15 OFFSET(15) NUMBITS(3) [
      Input = 0b000,
      Output = 0b001,
      AltFunc0 = 0b100 // PL011 UART RX
    ],

    FSEL14 OFFSET(12) NUMBITS(3) [
      Input = 0b000,
      Output = 0b001,
      AltFunc0 = 0b100 // PL011 UART RX
    ]
  ],

  /// GPIO Pull-up/down Register
  ///
  /// BCM2837 only
  GPPUD [
    /// Controls the actuation of the internal pull-up/down control line to ALL
    PUD OFFSET(0) NUMBITS(2) [
      Off = 0b00,
      PullDown = 0b01,
      PullUp = 0b10
    ]
  ],

  /// GPIO Pull-up/down Clock Register 0
  ///
  /// BCM2837 only
  GPPUDCLK0 [
    /// Pin 15
    PUDCLK15 OFFSET(15) NUMBITS(1) [
      NoEffect = 0,
      AssertClock = 1
    ],

    /// Pin 14
    PUDCLK14 OFFSET(14) NUMBITS(1) [
      NoEffect = 0,
      AssertClock = 1
    ]
  ],

  /// GPIO Pull-up / Pull-down Register 0
  ///
  /// BCM2711 and BCM2712 only.
  GPIO_PUP_PDN_CNTRL_REG0 [
    /// Pin 15
    GPIO_PUP_PDN_CNTRL15 OFFSET(30) NUMBITS(2) [
      NoResistor = 0b00,
      PullUp = 0b01
    ],

    GPIO_PUP_PDN_CNTRL14 OFFSET(28) NUMBITS(2) [
      NoResistor = 0b00,
      PullUp = 0b01
    ],
  ]


}

register_structs! {
    #[allow(non_snake_case)]
    RegisterBlock {
        (0x00 => _reserved1),
        (0x04 => GPFSEL1: ReadWrite<u32, GPFSEL1::Register>),
        (0x08 => _reserved2),
        (0x94 => GPPUD: ReadWrite<u32, GPPUD::Register>),
        (0x98 => GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>),
        (0x9C => _reserved3),
        (0xE4 => GPIO_PUP_PDN_CNTRL_REG0: ReadWrite<u32, GPIO_PUP_PDN_CNTRL_REG0::Register>),
        (0xE8 => @END),
    }
}

/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;

struct GPIOInner {
    registers: Registers,
}

//----------------------------------------------------------------------------
// Public Definitions
//----------------------------------------------------------------------------

/// Representation of the GPIO HW.
pub struct GPIO {
    inner: NullLock<GPIOInner>,
}

//----------------------------------------------------------------------------
// Private Code
//----------------------------------------------------------------------------

impl GPIOInner {
    /// Create an instance.
    ///
    /// # Safety
    ///
    /// - The user must ensure to provide a correct MMIO start address.
    pub const unsafe fn new(mmio_start_addr: usize) -> Self {
        Self {
            registers: Registers::new(mmio_start_addr),
        }
    }

    /// Disable pull-up/down on pins 14 and 15.
    #[cfg(feature = "bsp_rpi3")]
    fn disable_pud_14_15_bcm2837(&mut self) {
        use crate::cpu;

        // Make an educated guess for a good delay value (Sequence described in the BCM2837
        // peripherals PDF).
        //
        // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz.
        // - The Linux 2837 GPIO driver waits 1 µs between the steps.
        //
        // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs
        // would the CPU be clocked at 2 GHz.
        const DELAY: usize = 2000;

        self.registers.GPPUD.write(GPPUD::PUD::Off);
        cpu::spin_for_cycles(DELAY);

        self.registers
            .GPPUDCLK0
            .write(GPPUDCLK0::PUDCLK15::AssertClock + GPPUDCLK0::PUDCLK14::AssertClock);
        cpu::spin_for_cycles(DELAY);

        self.registers.GPPUD.write(GPPUD::PUD::Off);
        self.registers.GPPUDCLK0.set(0);
    }

    /// Disable pull-up/down on pins 14 and 15.
    #[cfg(feature = "bsp_rpi4")]
    fn disable_pud_14_15_bcm2711(&mut self) {
        self.registers.GPIO_PUP_PDN_CNTRL_REG0.write(
            GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL15::PullUp
                + GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL14::PullUp,
        );
    }

    #[cfg(feature = "bsp_rpi5")]
    fn disable_pud_14_15_bcm2712(&mut self) {
        self.registers.GPIO_PUP_PDN_CNTRL_REG0.write(
            GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL15::PullUp
                + GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL14::PullUp,
        );
    }

    pub fn map_pl011_uart(&mut self) {
        self.registers
            .GPFSEL1
            .modify(GPFSEL1::FSEL15::AltFunc0 + GPFSEL1::FSEL14::AltFunc0);

        //Disable pull-up/down on pins 14 and 15.
        #[cfg(feature = "bsp_rpi3")]
        self.disable_pud_14_15_bcm2837();

        #[cfg(feature = "bsp_rpi4")]
        self.disable_pud_14_15_bcm2711();

        #[cfg(feature = "bsp_rpi5")]
        self.disable_pud_14_15_bcm2712();
    }
}

//----------------------------------------------------------------------------
// Public Code
//----------------------------------------------------------------------------

impl GPIO {
    pub const COMPATIBLE: &'static str = "BCM GPIO";

    /// Create an instance.
    ///
    /// # Safety
    ///
    /// - The user must ensure to provide a correct MMIO start address.
    pub const unsafe fn new(mmio_start_addr: usize) -> Self {
        Self {
            inner: NullLock::new(GPIOInner::new(mmio_start_addr)),
        }
    }

    /// Concurrency safe version of `GPIOInner.map_pl011_uart()`
    pub fn map_pl011_uart(&self) {
        self.inner.lock(|inner| inner.map_pl011_uart())
    }
}

//----------------------------------------------------------------------------
// Operating System Interface Code
//----------------------------------------------------------------------------

use synchronization::interface::Mutex;

impl driver::interface::DeviceDriver for GPIO {
    fn compatible(&self) -> &'static str {
        Self::COMPATIBLE
    }
}