Skip to content

Commit

Permalink
feat(d1): add smhc driver (#299)
Browse files Browse the repository at this point in the history
This PR adds support for SD card interaction on the Allwinner D1 platform.
It is split in an `sdmmc` kernel service and a driver for the
SD/MMC host controller peripheral.
Currently only SD card support is implemented,
but this should allow for adding MMC and SDIO support later.
The interface between the kernel service and the driver aims to work on
the specification/protocol level and not contain any Allwinner D1 details,
which should make it suitable for other platforms as well.

This PR also improves the `BusGatingResetRegister` trait in
`platforms/allwinner-d1/d1-core/src/ccu.rs`

---------

Co-authored-by: Eliza Weisman <[email protected]>
  • Loading branch information
jspngh and hawkw authored Dec 10, 2023
1 parent 32157cd commit 3534ed7
Show file tree
Hide file tree
Showing 8 changed files with 1,471 additions and 14 deletions.
38 changes: 27 additions & 11 deletions platforms/allwinner-d1/d1-core/src/ccu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@ pub struct Ccu {
ccu: CCU,
}

#[derive(PartialEq)]
pub enum BusGating {
Mask,
Pass,
}

#[derive(PartialEq)]
pub enum BusReset {
Assert,
Deassert,
}

/// Trait to be implemented for module clocks that can be gated and reset
pub trait BusGatingResetRegister {
/// Enable or disable the clock reset bit
fn gating(ccu: &mut CCU, pass: bool);
/// Enable or disable the clock gating bit
fn reset(ccu: &mut CCU, deassert: bool);
fn gating(ccu: &mut CCU, gating: BusGating);
/// Enable or disable the clock reset bit
fn reset(ccu: &mut CCU, reset: BusReset);
}

// TODO: should this move into the `Clint`?
Expand All @@ -49,16 +61,16 @@ impl Ccu {

/// De-assert the reset bit and enable the clock gating bit for the given module
pub fn enable_module<MODULE: BusGatingResetRegister>(&mut self, _mod: &mut MODULE) {
MODULE::reset(&mut self.ccu, true);
MODULE::reset(&mut self.ccu, BusReset::Deassert);
sdelay(20);
MODULE::gating(&mut self.ccu, true);
MODULE::gating(&mut self.ccu, BusGating::Pass);
}

/// Disable the clock gating bit and assert the reset bit for the given module
pub fn disable_module<MODULE: BusGatingResetRegister>(&mut self, _mod: &mut MODULE) {
MODULE::gating(&mut self.ccu, false);
MODULE::gating(&mut self.ccu, BusGating::Mask);
// TODO: delay?
MODULE::reset(&mut self.ccu, false);
MODULE::reset(&mut self.ccu, BusReset::Assert);
}

/// Allow modules to configure their own clock on a PAC level
Expand Down Expand Up @@ -248,12 +260,16 @@ macro_rules! impl_bgr {
($($MODULE:ident : ($reg:ident, $gating:ident, $reset:ident),)+) => {
$(
impl BusGatingResetRegister for $MODULE {
fn gating(ccu: &mut CCU, pass: bool) {
ccu.$reg.modify(|_, w| w.$gating().bit(pass));
fn gating(ccu: &mut CCU, gating: BusGating) {
ccu.$reg.modify(|_, w| {
w.$gating().bit(gating == BusGating::Pass)
});
}

fn reset(ccu: &mut CCU, deassert: bool) {
ccu.$reg.modify(|_, w| w.$reset().bit(deassert));
fn reset(ccu: &mut CCU, reset: BusReset) {
ccu.$reg.modify(|_, w| {
w.$reset().bit(reset == BusReset::Deassert)
});
}
}
)+
Expand Down
7 changes: 4 additions & 3 deletions platforms/allwinner-d1/d1-core/src/dmac/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub mod descriptor;
/// This struct is constructed using [`Dmac::new`], which initializes the DMA
/// controller and returns a `Dmac`. Since this struct is essentially a token
/// representing that the DMAC has been initialized, it may be freely copied
/// into any driver that wishes to perform DMA oeprations.
/// into any driver that wishes to perform DMA operations.
#[derive(Copy, Clone)]
pub struct Dmac {
// this struct is essentially used as a "yes, the DMAC is initialized now" token...
Expand Down Expand Up @@ -89,7 +89,7 @@ pub enum ChannelMode {
/// transfer completes, and waits for the peripheral to pull the DMA
/// Active signal low before starting the next transfer.
///
/// The Allwinner documentationh for the D1 describes this mode as follows:
/// The Allwinner documentation for the D1 describes this mode as follows:
///
/// > * When the DMAC detects a valid external request signal, the DMAC
/// > starts to operate the peripheral device. The internal DRQ always
Expand Down Expand Up @@ -222,7 +222,7 @@ impl Dmac {
/// some of the data it wanted. If we were reading from a device, reads may
/// have side effects and incomplete reads may leave the device in a weird
/// state. Cancelling an incomplete transfer may result in, for example,
/// writing out half of a string to the UART, or only part of a structured
/// writing out half of a string to the UART, or only part of a structured
/// message over SPI, and so on. But, at least we don't have abandoned DMA
/// transfers running around in random parts of the heap you probably wanted
/// to use for normal stuff like having strings, or whatever it is that
Expand Down Expand Up @@ -398,6 +398,7 @@ impl Channel {
/// dropped, the descriptor and its associated memory region may also be
/// dropped safely.
///
/// Of course, the transfer may still have completed partially. If we
/// were writing to a device, the device may be unhappy to have only gotten
/// some of the data it wanted. If we were reading from a device, reads may
/// have side effects and incomplete reads may leave the device in a weird
Expand Down
1 change: 1 addition & 0 deletions platforms/allwinner-d1/d1-core/src/drivers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(feature = "sharp-display")]
pub mod sharp_display;
pub mod smhc;
pub mod spim;
pub mod twi;
pub mod uart;
Loading

0 comments on commit 3534ed7

Please sign in to comment.