Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usbhs: implement USBHS USB device driver #63

Merged
merged 8 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
ch32-metapac = { features = [
"rt",
], git = "https://github.com/ch32-rs/ch32-metapac", rev = "cef1944dadf3a5d198e687500b8f5964f29efdf6" }
], git = "https://github.com/ch32-rs/ch32-metapac", rev = "b1cbc7a98e43af3fd3170821654784e2c01cb26b" }

qingke = { version = "0.4.0", features = ["critical-section-impl"] }
qingke-rt = { version = "0.4.0", optional = true }
Expand Down Expand Up @@ -49,7 +49,7 @@ embassy-hal-internal = "0.2.0"
[build-dependencies]
ch32-metapac = { features = [
"metadata",
], git = "https://github.com/ch32-rs/ch32-metapac", rev = "cef1944dadf3a5d198e687500b8f5964f29efdf6" }
], git = "https://github.com/ch32-rs/ch32-metapac", rev = "b1cbc7a98e43af3fd3170821654784e2c01cb26b" }

proc-macro2 = "1.0"
quote = "1.0"
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ For a full list of chip capabilities and peripherals, check the [ch32-data](http
| I2C | ✅ | ✅ | ✅ | ❓ | ❓ | ❓ | |
| ADC | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| Timer(PWM) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| USB/OTG FS | ✅ | N/A | N/A | N/A | N/A | N/A | |
| USB/OTG HS | ❌ | N/A | N/A | N/A | N/A | N/A | |
| USB/OTG FS | ✅* | N/A | N/A | N/A | N/A | N/A | |
| USB HS | ✅* | N/A | N/A | N/A | N/A | N/A | |


- ✅ : Expected to work
Expand All @@ -53,6 +53,9 @@ For a full list of chip capabilities and peripherals, check the [ch32-data](http
- TODO: I haven't got a dev board yet, help-wanted
- N/A: Not available

### Notes
- For USB OTGFS and HS, look at the `mod.rs` respsectively to understand what is / is not tested.

### TODOs

This section lists some key items that are not implemented yet. And should be noted when using this crate.
Expand Down
2 changes: 2 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ fn main() {
// USB is splitted into multiple impls
(("usbd", "DP"), quote!(crate::usbd::DpPin)),
(("usbd", "DM"), quote!(crate::usbd::DmPin)),
(("usbhs", "DP"), quote!(crate::usbhs::DpPin)),
(("usbhs", "DM"), quote!(crate::usbhs::DmPin)),
// USBPD, handled by usbpd/mod.rs
//(("usbpd", "CC1"), quote!(crate::usbpd::Cc1Pin)),
//(("usbpd", "CC2"), quote!(crate::usbpd::Cc2Pin)),
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![no_std]
#![allow(static_mut_refs, unexpected_cfgs)]
#![feature(naked_functions)]

pub use ch32_metapac as pac;
pub(crate) use embassy_hal_internal::{impl_peripheral, peripherals_definition, peripherals_struct};
Expand Down Expand Up @@ -84,6 +83,9 @@ pub mod otg_fs;
#[cfg(usbd)]
pub mod usbd;

#[cfg(usbhs_v3)]
pub mod usbhs;

#[cfg(usbpd)]
pub mod usbpd;

Expand Down
14 changes: 14 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,17 @@ macro_rules! error {
}
};
}

#[collapse_debuginfo(yes)]
macro_rules! unwrap {
($e:expr) => {{
#[cfg(feature = "defmt")]
{
::defmt::unwrap!($e)
}
#[cfg(not(feature = "defmt"))]
{
$e.unwrap()
}
}};
}
24 changes: 4 additions & 20 deletions src/otg_fs/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,8 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> {
// upper bits are reserved (0)
let len = regs.rx_len().read().0 as usize;

// Assertion exists because looks like embassy-usb expects no partial reads.
// https://github.com/embassy-rs/embassy/blob/6e0b08291b63a0da8eba9284869d1d046bc5dabb/embassy-usb/src/lib.rs#L408
debug_assert_eq!(len, buf.len());
if len == buf.len() {
self.data.buffer.read_volatile(&mut buf[..len]);
Poll::Ready(Ok(len))
} else {
Poll::Ready(Err(EndpointError::BufferOverflow))
}
self.data.buffer.read_volatile(&mut buf[..len]);
Poll::Ready(Ok(len))
}
token => {
error!("Unexpected USB Token {}", token.to_bits());
Expand Down Expand Up @@ -286,20 +279,11 @@ where
let res = match status.mask_token() {
UsbToken::OUT => {
let len = regs.rx_len().read().0 as usize;
// https://github.com/embassy-rs/embassy/blob/6e0b08291b63a0da8eba9284869d1d046bc5dabb/embassy-usb/src/lib.rs#L408
// Embassy expects the whole buffer to be filled
let res = if len == buf.len() {
self.ep0_buf.buffer.read_volatile(&mut buf[..len]);
Poll::Ready(Ok(len))
} else {
Poll::Ready(Err(EndpointError::BufferOverflow))
};

self.ep0_buf.buffer.read_volatile(&mut buf[..len]);
regs.uep_rx_ctrl(0).modify(|v| {
v.set_mask_r_res(EpRxResponse::NAK);
});

res
Poll::Ready(Ok(len))
}
UsbToken::RSVD | UsbToken::IN | UsbToken::SETUP => unreachable!(),
};
Expand Down
7 changes: 4 additions & 3 deletions src/otg_fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,15 @@ where
ep_type: EndpointType,
max_packet_size: u16,
interval_ms: u8,
dir: Direction,
) -> Result<Endpoint<'d, T, D>, embassy_usb_driver::EndpointAllocError> {
let ep_addr = self.alloc_ep_address();
let data = self.allocator.alloc_endpoint(max_packet_size)?;

Ok(Endpoint::new(
EndpointInfo {
// todo fix in embassy usb driver ep_addr should be u8 with top bit unset
addr: EndpointAddress::from_parts(ep_addr as usize, D::dir()),
addr: EndpointAddress::from_parts(ep_addr as usize, dir),
ep_type,
max_packet_size,
interval_ms,
Expand All @@ -183,7 +184,7 @@ impl<'d, T: Instance, const NR_EP: usize> embassy_usb_driver::Driver<'d> for Dri
max_packet_size: u16,
interval_ms: u8,
) -> Result<Self::EndpointOut, embassy_usb_driver::EndpointAllocError> {
self.alloc_endpoint::<Out>(ep_type, max_packet_size, interval_ms)
self.alloc_endpoint(ep_type, max_packet_size, interval_ms, Direction::Out)
}

fn alloc_endpoint_in(
Expand All @@ -192,7 +193,7 @@ impl<'d, T: Instance, const NR_EP: usize> embassy_usb_driver::Driver<'d> for Dri
max_packet_size: u16,
interval_ms: u8,
) -> Result<Self::EndpointIn, embassy_usb_driver::EndpointAllocError> {
self.alloc_endpoint::<In>(ep_type, max_packet_size, interval_ms)
self.alloc_endpoint(ep_type, max_packet_size, interval_ms, Direction::In)
}

fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
Expand Down
96 changes: 95 additions & 1 deletion src/rcc/v3.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! CH32V2, CH32V3
use core::ops;
use core::ops::{self, Div};

pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, PllMul, PllxMul, Ppre as APBPrescaler, Prediv as PllPreDiv, Sw as Sysclk, Usbpre,
Expand Down Expand Up @@ -53,6 +53,47 @@ pub struct Pllx {
pub pll3: Option<PllxMul>,
}

#[derive(Clone, Copy, PartialEq)]
// USBHSPLLSRC bits in rcc_cfgr2
pub enum HsPllSource {
HSI,
HSE,
}

// TODO: should be in PAC
// USBHSDIV bits in rcc_cfgr2
#[derive(Clone, Copy, PartialEq)]
pub enum HsPllPrescaler {
DIV1 = 0b000,
DIV2 = 0b001,
DIV3 = 0b010,
DIV4 = 0b011,
DIV5 = 0b100,
DIV6 = 0b101,
DIV7 = 0b110,
DIV8 = 0b111,
}

impl Div<HsPllPrescaler> for Hertz {
type Output = Hertz;
fn div(self, rhs: HsPllPrescaler) -> Hertz {
Hertz(self.0 / (rhs as u32 + 1))
}
}

#[derive(Clone, Copy, PartialEq)]
pub enum HsPllRef {
_3M = 0b00,
_4M = 0b01,
_8M = 0b10,
_5M = 0b11,
}

#[derive(Clone, Copy)]
pub struct HsPll {
pub pre: HsPllPrescaler,
}

pub struct Config {
// won't close hsi
// pub hsi: bool,
Expand All @@ -70,6 +111,9 @@ pub struct Config {
pub apb2_pre: APBPrescaler,

pub ls: super::LsConfig,

pub hspll_src: HsPllSource,
pub hspll: Option<HsPll>,
// /// Per-peripheral kernel clock selection muxes
// pub mux: super::mux::ClockMux,
}
Expand All @@ -93,6 +137,10 @@ impl Config {
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: super::LsConfig::default_lsi(),
hspll_src: HsPllSource::HSE,
hspll: Some(HsPll {
pre: HsPllPrescaler::DIV2,
}),
}
};
pub const SYSCLK_FREQ_144MHZ_HSE: Config = {
Expand All @@ -112,6 +160,10 @@ impl Config {
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: super::LsConfig::default_lsi(),
hspll_src: HsPllSource::HSE,
hspll: Some(HsPll {
pre: HsPllPrescaler::DIV2,
}),
}
};
pub const SYSCLK_FREQ_144MHZ_HSI: Config = {
Expand All @@ -128,6 +180,10 @@ impl Config {
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: super::LsConfig::default_lsi(),
hspll_src: HsPllSource::HSI,
hspll: Some(HsPll {
pre: HsPllPrescaler::DIV2,
}),
}
};
pub const SYSCLK_FREQ_96MHZ_HSI: Config = {
Expand All @@ -144,6 +200,10 @@ impl Config {
apb1_pre: APBPrescaler::DIV4, // 24MHz
apb2_pre: APBPrescaler::DIV4,
ls: super::LsConfig::default_lsi(),
hspll_src: HsPllSource::HSI,
hspll: Some(HsPll {
pre: HsPllPrescaler::DIV2,
}),
}
};
}
Expand All @@ -161,6 +221,8 @@ impl Default for Config {
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: super::LsConfig::default(),
hspll_src: HsPllSource::HSE,
hspll: None,
}
}
}
Expand Down Expand Up @@ -288,6 +350,38 @@ pub(crate) unsafe fn init(config: Config) {
}
};

// Configure HSPLL for USBHS

#[cfg(d8c)]
if let Some(hspll) = config.hspll {
// Disable HSPLL
RCC.cfgr2().modify(|w| w.set_usbhs_pllalive(false));
let hspll_src = match config.hspll_src {
HsPllSource::HSI => hsi.unwrap(),
HsPllSource::HSE => hse.unwrap(),
};
let in_freq = hspll_src / hspll.pre;
let ref_clk = match in_freq {
Hertz(3_000_000) => HsPllRef::_3M,
Hertz(4_000_000) => HsPllRef::_4M,
Hertz(8_000_000) => HsPllRef::_8M,
Hertz(5_000_000) => HsPllRef::_5M,
_ => panic!(),
};

RCC.cfgr2().modify(|w| {
w.set_usbhs_prediy(hspll.pre as u8);
w.set_usbhs_ckpef_sel(ref_clk as u8);
w.set_usbhs_pll_src(if config.hspll_src == HsPllSource::HSI {
true
} else {
false
});
w.set_usbhs_clk_src(true);
});
RCC.cfgr2().modify(|w| w.set_usbhs_pllalive(true));
};

// Configure sysclk
let sys = match config.sys {
Sysclk::HSI => hsi.unwrap(),
Expand Down
32 changes: 14 additions & 18 deletions src/usb/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use embassy_usb_driver::{Direction, EndpointAllocError};
use embassy_usb_driver::EndpointAllocError;

pub(crate) struct EndpointBufferAllocator<'d, const NR_EP: usize> {
ep_buffer: &'d mut [EndpointDataBuffer; NR_EP],
Expand All @@ -22,14 +22,17 @@ impl<'d, const NR_EP: usize> EndpointBufferAllocator<'d, NR_EP> {
let ep_buf_idx = self.ep_next;
self.ep_next += 1;

// TODO: Fix this, and allow variable buffer sizes
assert!(max_packet_size as usize <= ENDPOINT_DATA_BUFFER_SIZE);

Ok(EndpointData {
max_packet_size,
buffer: unsafe { core::mem::transmute(&self.ep_buffer[ep_buf_idx] as *const EndpointDataBuffer) },
})
}
}

pub(crate) struct EndpointData<'d> {
pub struct EndpointData<'d> {
pub max_packet_size: u16,
pub buffer: &'d mut EndpointDataBuffer,
}
Expand All @@ -41,11 +44,11 @@ impl<'d> EndpointData<'d> {
}

// todo generic
const EP_MAX_PACKET_SIZE: usize = 64;
const ENDPOINT_DATA_BUFFER_SIZE: usize = 64;

#[repr(C, align(4))]
pub struct EndpointDataBuffer {
data: [u8; EP_MAX_PACKET_SIZE as usize],
data: [u8; ENDPOINT_DATA_BUFFER_SIZE as usize],
}

impl Default for EndpointDataBuffer {
Expand Down Expand Up @@ -83,23 +86,16 @@ impl EndpointDataBuffer {
}

/// USB Direction Trait
pub trait Dir {
/// Returns the direction value.
fn dir() -> Direction;
}
pub trait Dir {}

/// Marker type for the "IN" direction.
pub struct In;
impl Dir for In {
fn dir() -> Direction {
Direction::In
}
}
impl Dir for In {}

/// Marker type for the "OUT" direction.
pub struct Out;
impl Dir for Out {
fn dir() -> Direction {
Direction::Out
}
}
impl Dir for Out {}

/// Marker type for the control endpoint
pub struct InOut;
impl Dir for InOut {}
Loading
Loading