diff --git a/platforms/allwinner-d1/boards/src/bin/lichee-rv.rs b/platforms/allwinner-d1/boards/src/bin/lichee-rv.rs index 4161b13e..3ebc3d20 100644 --- a/platforms/allwinner-d1/boards/src/bin/lichee-rv.rs +++ b/platforms/allwinner-d1/boards/src/bin/lichee-rv.rs @@ -26,15 +26,13 @@ fn main() -> ! { } let mut p = unsafe { d1_pac::Peripherals::steal() }; - let uart = unsafe { kernel_uart(&mut p.CCU, &mut p.GPIO, p.UART0) }; - let spim = unsafe { kernel_spim1(p.SPI_DBI, &mut p.CCU, &mut p.GPIO) }; - let i2c0 = unsafe { twi::I2c0::lichee_rv_dock(p.TWI2, &mut p.CCU, &mut p.GPIO) }; + let uart = unsafe { kernel_uart(&mut p.CCU, p.UART0) }; + let spim = unsafe { kernel_spim1(p.SPI_DBI, &mut p.CCU) }; + let i2c0 = unsafe { twi::I2c0::lichee_rv_dock(p.TWI2, &mut p.CCU) }; let timers = Timers::new(p.TIMER); let dmac = Dmac::new(p.DMAC, &mut p.CCU); let plic = Plic::new(p.PLIC); - let d1 = D1::initialize(timers, uart, spim, dmac, plic, i2c0).unwrap(); - p.GPIO.pc_cfg0.modify(|_r, w| { w.pc1_select().output(); w @@ -44,25 +42,27 @@ fn main() -> ! { w }); + let d1 = D1::initialize(timers, uart, spim, dmac, plic, i2c0, p.GPIO).unwrap(); + d1.initialize_sharp_display(); - // Initialize LED loop - d1.kernel - .initialize(async move { - loop { - p.GPIO.pc_dat.modify(|_r, w| { - w.pc_dat().variant(0b0000_0010); - w - }); - d1.kernel.sleep(Duration::from_millis(250)).await; - p.GPIO.pc_dat.modify(|_r, w| { - w.pc_dat().variant(0b0000_0000); - w - }); - d1.kernel.sleep(Duration::from_millis(250)).await; - } - }) - .unwrap(); + // // Initialize LED loop + // d1.kernel + // .initialize(async move { + // loop { + // p.GPIO.pc_dat.modify(|_r, w| { + // w.pc_dat().variant(0b0000_0010); + // w + // }); + // d1.kernel.sleep(Duration::from_millis(250)).await; + // p.GPIO.pc_dat.modify(|_r, w| { + // w.pc_dat().variant(0b0000_0000); + // w + // }); + // d1.kernel.sleep(Duration::from_millis(250)).await; + // } + // }) + // .unwrap(); d1.run() } diff --git a/platforms/allwinner-d1/boards/src/bin/mq-pro.rs b/platforms/allwinner-d1/boards/src/bin/mq-pro.rs index 524635d4..478ef021 100644 --- a/platforms/allwinner-d1/boards/src/bin/mq-pro.rs +++ b/platforms/allwinner-d1/boards/src/bin/mq-pro.rs @@ -27,15 +27,13 @@ fn main() -> ! { } let mut p = unsafe { d1_pac::Peripherals::steal() }; - let uart = unsafe { kernel_uart(&mut p.CCU, &mut p.GPIO, p.UART0) }; - let spim = unsafe { kernel_spim1(p.SPI_DBI, &mut p.CCU, &mut p.GPIO) }; - let i2c0 = unsafe { twi::I2c0::mq_pro(p.TWI0, &mut p.CCU, &mut p.GPIO) }; + let uart = unsafe { kernel_uart(&mut p.CCU, p.UART0) }; + let spim = unsafe { kernel_spim1(p.SPI_DBI, &mut p.CCU) }; + let i2c0 = unsafe { twi::I2c0::mq_pro(p.TWI0, &mut p.CCU) }; let timers = Timers::new(p.TIMER); let dmac = Dmac::new(p.DMAC, &mut p.CCU); let plic = Plic::new(p.PLIC); - let d1 = D1::initialize(timers, uart, spim, dmac, plic, i2c0).unwrap(); - p.GPIO.pd_cfg2.modify(|_r, w| { w.pd18_select().output(); w @@ -45,23 +43,25 @@ fn main() -> ! { w }); - // Initialize LED loop - d1.kernel - .initialize(async move { - loop { - p.GPIO.pd_dat.modify(|_r, w| { - w.pd_dat().variant(1 << 18); - w - }); - d1.kernel.sleep(Duration::from_millis(250)).await; - p.GPIO.pd_dat.modify(|_r, w| { - w.pd_dat().variant(0); - w - }); - d1.kernel.sleep(Duration::from_millis(250)).await; - } - }) - .unwrap(); + let d1 = D1::initialize(timers, uart, spim, dmac, plic, i2c0, p.GPIO).unwrap(); + + // // Initialize LED loop + // d1.kernel + // .initialize(async move { + // loop { + // p.GPIO.pd_dat.modify(|_r, w| { + // w.pd_dat().variant(1 << 18); + // w + // }); + // d1.kernel.sleep(Duration::from_millis(250)).await; + // p.GPIO.pd_dat.modify(|_r, w| { + // w.pd_dat().variant(0); + // w + // }); + // d1.kernel.sleep(Duration::from_millis(250)).await; + // } + // }) + // .unwrap(); d1.initialize_sharp_display(); diff --git a/platforms/allwinner-d1/core/src/drivers/gpio.rs b/platforms/allwinner-d1/core/src/drivers/gpio.rs index 9fcea6d2..c85c72a0 100644 --- a/platforms/allwinner-d1/core/src/drivers/gpio.rs +++ b/platforms/allwinner-d1/core/src/drivers/gpio.rs @@ -7,6 +7,7 @@ use kernel::{ }, isr::Isr, maitake::sync::WaitQueue, + mnemos_alloc::containers::FixedVec, registry::{self, uuid, Envelope, KernelHandle, RegisteredDriver, Uuid}, trace, Kernel, }; @@ -33,12 +34,13 @@ pub enum Request { mode: InterruptMode, }, RegisterCustom { - pin: Pin, - name: &'static str, + pins: FixedVec<(Pin, &'static str)>, register: fn(&gpio::RegisterBlock), }, PinState(Pin), } + +#[derive(Debug)] pub enum Response { RegisterIrq(&'static WaitQueue), RegisterCustom, @@ -46,6 +48,7 @@ pub enum Response { // actually do IO... } +#[derive(Debug)] pub enum Error { PinInUse(Pin, PinState), } @@ -206,6 +209,27 @@ pub enum PinState { Other(&'static str), } +macro_rules! impl_from_pins { + ($($P:ty => $pin:ident),+ $(,)?) => { + $( + impl From<$P> for Pin { + fn from(p: $P) -> Self { + Self::$pin(p) + } + } + )+ + } +} + +impl_from_pins! { + PinB => B, + PinC => C, + PinD => D, + PinE => E, + PinF => F, + PinG => G, +} + //////////////////////////////////////////////////////////////////////////////// // Client Definition //////////////////////////////////////////////////////////////////////////////// @@ -246,6 +270,32 @@ impl GpioClient { reply: Reusable::new_async().await, }) } + + /// Configure a pin with a custom configuration. + /// + /// # Warnings + /// + /// The provided function must only modify the pin configuration for `pin`. + /// There is currently no way to enforce this. + pub async fn register_custom( + &mut self, + pins: impl Into>, + register: fn(&gpio::RegisterBlock), + ) -> Result<(), Error> { + let pins = pins.into(); + let resp = self + .handle + .request_oneshot(Request::RegisterCustom { pins, register }, &self.reply) + .await + .unwrap(); + match resp.body { + Ok(Response::RegisterCustom) => Ok(()), + Ok(resp) => unreachable!( + "expected the GpioService to respond with RegisterCustom, got {resp:?}" + ), + Err(e) => Err(e), + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -287,59 +337,82 @@ impl GpioServer { #[trace::instrument(level = trace::Level::INFO, name = "GpioServer", skip(self, gpio))] async fn run(mut self, gpio: GPIO) { while let Ok(registry::Message { msg, reply }) = self.rx.dequeue_async().await { - let rsp = match msg.body { - Request::RegisterIrq { pin, mode } => { - tracing::debug!(?pin, ?mode, "registering GPIO interrupt..."); - let (state, irq) = self.pin(pin); - match *state { - PinState::Unregistered => { - *state = PinState::Interrupt(mode); - // TODO(eliza): configure the interrupt mode! + let rsp = msg.reply_with_body(|body| self.handle_msg(&gpio, body)); + if let Err(error) = reply.reply_konly(rsp).await { + tracing::warn!(?error, "requester cancelled request!") + // TODO(eliza): we should probably undo any pin state changes here... + } + } + } - tracing::info!(?pin, ?mode, "GPIO interrupt registered!"); - Ok(Response::RegisterIrq(irq)) - } - PinState::Interrupt(cur_mode) if cur_mode == mode => { - tracing::info!(?pin, ?mode, "GPIO interrupt subscribed."); - Ok(Response::RegisterIrq(irq)) - } - state => { + fn handle_msg(&mut self, gpio: &GPIO, msg: Request) -> Result { + match msg { + Request::RegisterIrq { pin, mode } => { + tracing::debug!(?pin, ?mode, "registering GPIO interrupt..."); + let (state, irq) = self.pin(pin); + match *state { + PinState::Unregistered => { + *state = PinState::Interrupt(mode); + // TODO(eliza): configure the interrupt mode! + + tracing::info!(?pin, ?mode, "GPIO interrupt registered!"); + Ok(Response::RegisterIrq(irq)) + } + PinState::Interrupt(cur_mode) if cur_mode == mode => { + tracing::info!(?pin, ?mode, "GPIO interrupt subscribed."); + Ok(Response::RegisterIrq(irq)) + } + state => { + tracing::warn!( + ?pin, + ?state, + ?mode, + "can't register GPIO interrupt, pin already in use!" + ); + // TODO(eliza): add a way for a requester to wait + // for a pin to become available? + Err(Error::PinInUse(pin, state)) + } + } + } + Request::RegisterCustom { pins, register } => { + // first, try to claim all the requested pins --- don't do + // the register block manipulation if we can't claim any of + // the requested pins. + for &(pin, name) in pins.as_slice() { + match self.pin(pin) { + (PinState::Unregistered, _) => {} + (&mut PinState::Other(other_name), _) if other_name == name => {} + (&mut state, _) => { tracing::warn!( ?pin, ?state, - ?mode, - "can't register GPIO interrupt, pin already in use!" + "can't claim pin for {name}, already in use!" ); - // TODO(eliza): add a way for a requester to wait - // for a pin to become available? - Err(Error::PinInUse(pin, state)) + return Err(Error::PinInUse(pin, state)); } } } - Request::RegisterCustom { - pin, - name, - register, - } => { + // now that we've confirmed that all pins are claimable, + // actually perform the registration and set the pin states. + register(gpio); + for &(pin, name) in pins.as_slice() { let (state, _) = self.pin(pin); match *state { PinState::Unregistered => { - register(&gpio); + tracing::info!(?pin, state = %name, "claimed pin"); *state = PinState::Other(name); - Ok(Response::RegisterCustom) } - PinState::Other(cur_mode) if cur_mode == name => { - Ok(Response::RegisterCustom) + PinState::Other(_) => {} + state => { + unreachable!("we just checked the pin's state, and it was claimable!") } - state => Err(Error::PinInUse(pin, state)), } } - Request::PinState(pin) => Ok(Response::PinState(pin, *self.pin(pin).0)), - }; - if let Err(error) = reply.reply_konly(msg.reply_with(rsp)).await { - tracing::warn!(?error, "requester cancelled request!") - // TODO(eliza): we should probably undo any pin state changes here... + + Ok(Response::RegisterCustom) } + Request::PinState(pin) => Ok(Response::PinState(pin, *self.pin(pin).0)), } } diff --git a/platforms/allwinner-d1/core/src/drivers/sharp_display.rs b/platforms/allwinner-d1/core/src/drivers/sharp_display.rs index 8ec84cdd..e89cde19 100644 --- a/platforms/allwinner-d1/core/src/drivers/sharp_display.rs +++ b/platforms/allwinner-d1/core/src/drivers/sharp_display.rs @@ -112,7 +112,7 @@ impl SharpDisplay { let draw = Draw { kernel, buf: linebuf, - spim: SpiSenderClient::from_registry(kernel).await.unwrap(), + spim: SpiSenderClient::from_registry(kernel).await, ctxt, }; diff --git a/platforms/allwinner-d1/core/src/drivers/spim.rs b/platforms/allwinner-d1/core/src/drivers/spim.rs index 2dd601e2..612549d5 100644 --- a/platforms/allwinner-d1/core/src/drivers/spim.rs +++ b/platforms/allwinner-d1/core/src/drivers/spim.rs @@ -1,19 +1,19 @@ // Spi Sender -use core::ptr::NonNull; - +use super::gpio::{self, GpioClient, PinD}; use crate::dmac::{ descriptor::{ AddressMode, BModeSel, BlockSize, DataWidth, DescriptorConfig, DestDrqType, SrcDrqType, }, Channel, ChannelMode, }; -use d1_pac::{CCU, GPIO, SPI_DBI}; +use core::ptr::NonNull; +use d1_pac::{CCU, SPI_DBI}; use kernel::{ comms::{kchannel::KChannel, oneshot::Reusable}, maitake::sync::WaitCell, mnemos_alloc::containers::FixedVec, - registry::{uuid, Envelope, KernelHandle, Message, RegisteredDriver, ReplyTo, Uuid}, + registry::{self, uuid, Envelope, KernelHandle, Message, RegisteredDriver, ReplyTo, Uuid}, Kernel, }; @@ -23,7 +23,7 @@ pub struct Spim1 { _x: (), } -pub unsafe fn kernel_spim1(spi1: SPI_DBI, ccu: &mut CCU, gpio: &mut GPIO) -> Spim1 { +pub unsafe fn kernel_spim1(spi1: SPI_DBI, ccu: &mut CCU) -> Spim1 { // Set clock rate (fixed to 2MHz), and enable the SPI peripheral ccu.spi1_clk.write(|w| { // Enable clock @@ -31,7 +31,8 @@ pub unsafe fn kernel_spim1(spi1: SPI_DBI, ccu: &mut CCU, gpio: &mut GPIO) -> Spi // base: 24 MHz w.clk_src_sel().hosc(); // /1: 24 MHz - w.factor_n().n1(); + // /8: 3 MHz + w.factor_n().n8(); // /12: 2 MHz w.factor_m().variant(11); w @@ -41,22 +42,6 @@ pub unsafe fn kernel_spim1(spi1: SPI_DBI, ccu: &mut CCU, gpio: &mut GPIO) -> Spi w }); - // Map the pins - gpio.pd_cfg1.write(|w| { - // Select SPI pin mode - w.pd10_select().spi1_cs_dbi_csx(); - w.pd11_select().spi1_clk_dbi_sclk(); - w.pd12_select().spi1_mosi_dbi_sdo(); - w - }); - gpio.pd_pull0.write(|w| { - // Disable pull up/downs - w.pd10_pull().pull_disable(); - w.pd11_pull().pull_disable(); - w.pd12_pull().pull_disable(); - w - }); - // Hard coded configuration for specifically supporting the SHARP memory display spi1.spi_tcr.write(|w| { // Allow the hardware to control the chip select @@ -96,8 +81,41 @@ impl RegisteredDriver for SpiSender { pub struct SpiSender; pub struct SpiSenderServer; +#[derive(Debug)] +#[non_exhaustive] +pub enum RegistrationError { + Gpio(gpio::Error), + Registry(registry::RegistrationError), +} + impl SpiSenderServer { - pub async fn register(kernel: &'static Kernel, queued: usize) -> Result<(), ()> { + pub async fn register(kernel: &'static Kernel, queued: usize) -> Result<(), RegistrationError> { + let mut gpio = GpioClient::from_registry(kernel).await; + let pins = FixedVec::from_slice(&[ + (PinD::D10.into(), "SPI1_CS_DBI_CSX"), + (PinD::D11.into(), "SPI1_CLK_DBI_SCLK"), + (PinD::D12.into(), "SPI1_MOSI_DBI_SDO"), + ]) + .await; + gpio.register_custom(pins, |gpio| { + // Map the pins + gpio.pd_cfg1.write(|w| { + // Select SPI pin mode + w.pd10_select().spi1_cs_dbi_csx(); + w.pd11_select().spi1_clk_dbi_sclk(); + w.pd12_select().spi1_mosi_dbi_sdo(); + w + }); + gpio.pd_pull0.write(|w| { + // Disable pull up/downs + w.pd10_pull().pull_disable(); + w.pd11_pull().pull_disable(); + w.pd12_pull().pull_disable(); + w + }); + }) + .await + .map_err(RegistrationError::Gpio)?; let (kprod, kcons) = KChannel::new_async(queued).await.split(); kernel @@ -182,8 +200,9 @@ impl SpiSenderServer { .await; kernel - .with_registry(move |reg| reg.register_konly::(&kprod).map_err(drop)) - .await?; + .with_registry(move |reg| reg.register_konly::(&kprod)) + .await + .map_err(RegistrationError::Registry)?; Ok(()) } @@ -207,10 +226,32 @@ pub struct SpiSenderClient { } impl SpiSenderClient { - pub async fn from_registry(kernel: &'static Kernel) -> Result { - let hdl = kernel.with_registry(|reg| reg.get()).await.ok_or(())?; + /// Obtain a `SpiSenderClient` + /// + /// If the [`SpiSender`] hasn't been registered yet, we will retry until it + /// has been registered. + pub async fn from_registry(kernel: &'static Kernel) -> Self { + loop { + match Self::from_registry_no_retry(kernel).await { + Some(port) => return port, + None => { + // I2C probably isn't registered yet. Try again in a bit + kernel.sleep(core::time::Duration::from_millis(10)).await; + } + } + } + } + + /// Obtain a `SpiSenderClient` + /// + /// Does NOT attempt to get an [`SpiSender`] handle more than once. + /// + /// Prefer [`SpiSenderClient::from_registry`] unless you will not be spawning one + /// around the same time as obtaining a client. + pub async fn from_registry_no_retry(kernel: &'static Kernel) -> Option { + let hdl = kernel.with_registry(|reg| reg.get::()).await?; - Ok(SpiSenderClient { + Some(Self { hdl, osc: Reusable::new_async().await, }) diff --git a/platforms/allwinner-d1/core/src/drivers/twi.rs b/platforms/allwinner-d1/core/src/drivers/twi.rs index c6845596..6b3d2008 100644 --- a/platforms/allwinner-d1/core/src/drivers/twi.rs +++ b/platforms/allwinner-d1/core/src/drivers/twi.rs @@ -48,12 +48,13 @@ //! has a driver for this device, which can be found [here][linux-driver]. //! //! [linux-driver]: https://github.com/torvalds/linux/blob/995b406c7e972fab181a4bb57f3b95e59b8e5bf3/drivers/i2c/busses/i2c-mv64xxx.c +use super::gpio::{self, GpioClient}; use core::{ cell::UnsafeCell, ops::{Deref, DerefMut}, task::{Poll, Waker}, }; -use d1_pac::{twi, Interrupt, CCU, GPIO, TWI0, TWI1, TWI2, TWI3}; +use d1_pac::{twi, Interrupt, CCU, TWI0, TWI1, TWI2, TWI3}; use kernel::{ comms::kchannel::{KChannel, KConsumer}, embedded_hal_async::i2c::{ErrorKind, NoAcknowledgeSource}, @@ -68,10 +69,16 @@ use kernel::{ /// A TWI mapped to the Raspberry Pi header's I²C0 pins. pub struct I2c0 { - isr: &'static IsrData, twi: &'static twi::RegisterBlock, - /// Which TWI does this TWI Engine use? - int: (Interrupt, fn()), + isr: &'static IsrData, + map: I2c0PinMap, +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum RegistrationError { + Gpio(gpio::Error), + Registry(registry::RegistrationError), } /// Data used by a TWI interrupt. @@ -116,6 +123,12 @@ enum TwiOp { None, } +#[derive(Debug)] +enum I2c0PinMap { + MqPro, + LicheeRv, +} + /// TWI state machine #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[allow(dead_code)] // TODO(eliza): implement 10-bit addresses @@ -147,17 +160,7 @@ impl I2c0 { /// - The TWI register block must not be concurrently written to. /// - This function should be called only while running on a MangoPi MQ Pro /// board. - pub unsafe fn mq_pro(_twi: TWI0, ccu: &mut CCU, gpio: &mut GPIO) -> Self { - // Step 1: Configure GPIO pin mappings. - gpio.pg_cfg1.modify(|_r, w| { - // on the Mango Pi MQ Pro, the pi header's I2C0 pins are mapped to - // TWI0 on PG12 and PG13: - // https://mangopi.org/_media/mq-pro-sch-v12.pdf - w.pg12_select().twi0_sck(); - w.pg13_select().twi0_sda(); - w - }); - + pub unsafe fn mq_pro(_twi: TWI0, ccu: &mut CCU) -> Self { ccu.twi_bgr.modify(|_r, w| { // Step 2: Set TWI_BGR_REG[TWI(n)_GATING] to 0 to close TWI(n) clock. w.twi0_gating().mask(); @@ -174,11 +177,7 @@ impl I2c0 { w }); - Self::init( - unsafe { &*TWI0::ptr() }, - Interrupt::TWI0, - Self::handle_twi0_interrupt, - ) + Self::init(unsafe { &*TWI0::ptr() }, I2c0PinMap::MqPro) } /// Initialize a TWI for the Lichee RV Dock's Pi header I²C0 @@ -190,18 +189,7 @@ impl I2c0 { /// - The TWI register block must not be concurrently written to. /// - This function should be called only while running on a Lichee RV /// board. - pub unsafe fn lichee_rv_dock(_twi: TWI2, ccu: &mut CCU, gpio: &mut GPIO) -> Self { - // Step 1: Configure GPIO pin mappings. - gpio.pb_cfg0.modify(|_r, w| { - // on the Lichee RV Dock, the Pi header's I2C0 corresponds to TWI2, not - // TWI0 as on the MQ Pro. - // I2C0 SDA is mapped to TWI2 PB1, and I2C0 SCL is mapped to TWI2 PB0: - // https://dl.sipeed.com/fileList/LICHEE/D1/Lichee_RV-Dock/2_Schematic/Lichee_RV_DOCK_3516(Schematic).pdf - w.pb0_select().twi2_sck(); - w.pb1_select().twi2_sda(); - w - }); - + pub unsafe fn lichee_rv_dock(_twi: TWI2, ccu: &mut CCU) -> Self { ccu.twi_bgr.modify(|_r, w| { // Step 2: Set TWI_BGR_REG[TWI(n)_GATING] to 0 to close TWI(n) clock. w.twi2_gating().mask(); @@ -218,16 +206,15 @@ impl I2c0 { w }); - Self::init( - unsafe { &*TWI2::ptr() }, - Interrupt::TWI2, - Self::handle_twi2_interrupt, - ) + Self::init(unsafe { &*TWI2::ptr() }, I2c0PinMap::LicheeRv) } /// Returns the interrupt and ISR for this TWI. pub fn interrupt(&self) -> (Interrupt, fn()) { - self.int + match self.map { + I2c0PinMap::LicheeRv => (Interrupt::TWI2, Self::handle_twi2_interrupt), + I2c0PinMap::MqPro => (Interrupt::TWI0, Self::handle_twi0_interrupt), + } } /// Handle a TWI 0 interrupt on the I2C0 pins. @@ -285,7 +272,7 @@ impl I2c0 { } /// This assumes the GPIO pin mappings are already configured. - unsafe fn init(twi: &'static twi::RegisterBlock, int: Interrupt, isr: fn()) -> Self { + unsafe fn init(twi: &'static twi::RegisterBlock, map: I2c0PinMap) -> Self { // soft reset bit twi.twi_srst.write(|w| w.soft_rst().set_bit()); @@ -313,19 +300,68 @@ impl I2c0 { Self { twi, + map, isr: &I2C0_ISR, - int: (int, isr), } } - pub async fn register(self, kernel: &'static Kernel, queued: usize) -> Result<(), ()> { + pub async fn register( + self, + kernel: &'static Kernel, + queued: usize, + ) -> Result<(), RegistrationError> { + // set up GPIO pin mappings + let mut gpio = GpioClient::from_registry(kernel).await; + match self.map { + I2c0PinMap::LicheeRv => { + let pins = FixedVec::from_slice(&[ + (gpio::PinB::B0.into(), "TWI2_SCK"), + (gpio::PinB::B1.into(), "TWI2_SDA"), + ]) + .await; + gpio.register_custom(pins, |gpio| { + gpio.pb_cfg0.modify(|_r, w| { + // on the Lichee RV Dock, the Pi header's I2C0 corresponds to TWI2, not + // TWI0 as on the MQ Pro. + // I2C0 SDA is mapped to TWI2 PB1, and I2C0 SCL is mapped to TWI2 PB0: + // https://dl.sipeed.com/fileList/LICHEE/D1/Lichee_RV-Dock/2_Schematic/Lichee_RV_DOCK_3516(Schematic).pdf + w.pb0_select().twi2_sck(); + w.pb1_select().twi2_sda(); + w + }) + }) + .await + .map_err(RegistrationError::Gpio)?; + } + I2c0PinMap::MqPro => { + let pins = FixedVec::from_slice(&[ + (gpio::PinG::G12.into(), "TWI0_SCK"), + (gpio::PinG::G13.into(), "TWI0_SDA"), + ]) + .await; + gpio.register_custom(pins, |gpio| { + gpio.pg_cfg1.modify(|_r, w| { + // on the Mango Pi MQ Pro, the pi header's I2C0 pins are mapped to + // TWI0 on PG12 and PG13: + // https://mangopi.org/_media/mq-pro-sch-v12.pdf + w.pg12_select().twi0_sck(); + w.pg13_select().twi0_sda(); + w + }) + }) + .await + .map_err(RegistrationError::Gpio)?; + } + }; + let (tx, rx) = KChannel::new_async(queued).await.split(); kernel.spawn(self.run(rx)).await; trace::debug!("TWI driver task spawned"); kernel - .with_registry(move |reg| reg.register_konly::(&tx).map_err(drop)) - .await?; + .with_registry(move |reg| reg.register_konly::(&tx)) + .await + .map_err(RegistrationError::Registry)?; Ok(()) } diff --git a/platforms/allwinner-d1/core/src/drivers/uart.rs b/platforms/allwinner-d1/core/src/drivers/uart.rs index 923a0860..f722c60c 100644 --- a/platforms/allwinner-d1/core/src/drivers/uart.rs +++ b/platforms/allwinner-d1/core/src/drivers/uart.rs @@ -1,10 +1,11 @@ -use d1_pac::{CCU, GPIO, UART0}; +use d1_pac::{CCU, UART0}; use core::{ ptr::{null_mut, NonNull}, sync::atomic::{AtomicPtr, Ordering}, }; +use super::gpio::{self, GpioClient}; use crate::dmac::{ descriptor::{ AddressMode, BModeSel, BlockSize, DataWidth, DescriptorConfig, DestDrqType, SrcDrqType, @@ -17,8 +18,8 @@ use kernel::{ kchannel::{KChannel, KConsumer}, }, maitake::sync::WaitCell, - mnemos_alloc::containers::Box, - registry::Message, + mnemos_alloc::containers::{Box, FixedVec}, + registry::{self, Message}, services::simple_serial::{Request, Response, SimpleSerialError, SimpleSerialService}, Kernel, }; @@ -50,6 +51,13 @@ pub struct D1Uart { _x: (), } +#[derive(Debug)] +#[non_exhaustive] +pub enum RegistrationError { + Gpio(gpio::Error), + Registry(registry::RegistrationError), +} + impl D1Uart { pub fn tx_done_waker() -> &'static WaitCell { &TX_DONE @@ -169,9 +177,26 @@ impl D1Uart { cap_in: usize, cap_out: usize, tx_channel: Channel, - ) -> Result<(), ()> { + ) -> Result<(), RegistrationError> { assert_eq!(tx_channel.channel_index(), 0); + // set up GPIO pin mappings + let mut gpio = GpioClient::from_registry(k).await; + let pins = FixedVec::from_slice(&[ + (gpio::PinB::B8.into(), "UART0_TX"), + (gpio::PinB::B9.into(), "UART0_RX"), + ]) + .await; + gpio.register_custom(pins, |gpio| { + // Set PB8 and PB9 to function 6, UART0, internal pullup. + gpio.pb_cfg1 + .write(|w| w.pb8_select().uart0_tx().pb9_select().uart0_rx()); + gpio.pb_pull0 + .write(|w| w.pc8_pull().pull_up().pc9_pull().pull_up()); + }) + .await + .map_err(RegistrationError::Gpio)?; + let (kprod, kcons) = KChannel::>::new_async(4) .await .split(); @@ -189,23 +214,17 @@ impl D1Uart { k.with_registry(|reg| reg.register_konly::(&kprod)) .await - .map_err(drop)?; + .map_err(RegistrationError::Registry)?; Ok(()) } } -pub unsafe fn kernel_uart(ccu: &mut CCU, gpio: &mut GPIO, uart0: UART0) -> Uart { +pub unsafe fn kernel_uart(ccu: &mut CCU, uart0: UART0) -> Uart { // Enable UART0 clock. ccu.uart_bgr .write(|w| w.uart0_gating().pass().uart0_rst().deassert()); - // Set PB8 and PB9 to function 6, UART0, internal pullup. - gpio.pb_cfg1 - .write(|w| w.pb8_select().uart0_tx().pb9_select().uart0_rx()); - gpio.pb_pull0 - .write(|w| w.pc8_pull().pull_up().pc9_pull().pull_up()); - // Configure UART0 for 115200 8n1. // By default APB1 is 24MHz, use divisor 13 for 115200. diff --git a/platforms/allwinner-d1/core/src/lib.rs b/platforms/allwinner-d1/core/src/lib.rs index 7a969e78..2eead1aa 100644 --- a/platforms/allwinner-d1/core/src/lib.rs +++ b/platforms/allwinner-d1/core/src/lib.rs @@ -14,7 +14,7 @@ use core::{ sync::atomic::{AtomicBool, Ordering}, time::Duration, }; -use d1_pac::{Interrupt, DMAC, TIMER}; +use d1_pac::{Interrupt, DMAC, GPIO, TIMER}; use kernel::{ mnemos_alloc::containers::Box, trace::{self, Instrument}, @@ -59,6 +59,7 @@ impl D1 { dmac: Dmac, plic: Plic, i2c0: twi::I2c0, + gpio: GPIO, ) -> Result { let k_settings = KernelSettings { max_drivers: 16, @@ -81,6 +82,13 @@ impl D1 { w }); + k.initialize(async move { + tracing::debug!("initializing GPIO..."); + gpio::GpioServer::register(k, gpio, 8).await.unwrap(); + tracing::info!("GPIO initialized!"); + }) + .unwrap(); + // Initialize SPI stuff k.initialize(async move { // Register a new SpiSenderServer diff --git a/source/alloc/src/containers.rs b/source/alloc/src/containers.rs index 08583464..f4987458 100644 --- a/source/alloc/src/containers.rs +++ b/source/alloc/src/containers.rs @@ -326,6 +326,16 @@ impl FixedVec { } } + pub async fn from_slice(slice: &[T]) -> Self + where + T: Clone, + { + let mut this = Self::new(slice.len()).await; + this.try_extend_from_slice(slice) + .expect("we just allocated the FixedVec with enough capacity for the desired length!"); + this + } + /// Attempt to push an item into the fixed vec. /// /// Returns an error if the fixed vec is full