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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! An abstraction over the pin multiplexer, nRF5X-family
//!
//! Controller drivers should use the `Pinmux` type (instead of a `u32`) for
//! fields that determine which pins are used by the hardware. The board
//! configuration should create `Pinmux`s and pass them into controller drivers
//! during initialization.

use kernel::utilities::cells::VolatileCell;

// Note: only the nrf52840 has two ports, but we create two ports to avoid
// gating this code by a feature.
const NUM_PORTS: usize = 2;

const PIN_PER_PORT: usize = 32;

// Keep track of which pins has a `Pinmux` been created for.
static mut USED_PINS: [VolatileCell<u32>; NUM_PORTS] = [VolatileCell::new(0), VolatileCell::new(0)];

/// An opaque wrapper around a configurable pin.
#[derive(Copy, Clone)]
pub struct Pinmux(u32);

impl Pinmux {
    /// Creates a new `Pinmux` wrapping the numbered pin.
    ///
    /// # Panics
    ///
    /// If a `Pinmux` for this pin has already
    /// been created.
    ///
    pub unsafe fn new(pin: u32) -> Pinmux {
        let port: usize = (pin as usize) / PIN_PER_PORT;
        let pin_idx: usize = (pin as usize) % PIN_PER_PORT;
        let used_pins = USED_PINS[port].get();
        if used_pins & (1 << pin_idx) != 0 {
            panic!("Pin {} is already in use!", pin);
        } else {
            USED_PINS[port].set(used_pins | 1 << pin_idx);
            Pinmux(pin)
        }
    }
}

impl Into<u32> for Pinmux {
    fn into(self) -> u32 {
        self.0
    }
}