From de5f605d603ed1118e0cb3aeb771c5e0d74cf144 Mon Sep 17 00:00:00 2001 From: Astro Date: Wed, 29 Jul 2020 19:45:01 +0200 Subject: [PATCH] eth: refactor peripheral instance into type parameter, improve clock setup --- libboard_zynq/src/eth/mod.rs | 254 +++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 101 deletions(-) diff --git a/libboard_zynq/src/eth/mod.rs b/libboard_zynq/src/eth/mod.rs index 8b6f6a6..e329053 100644 --- a/libboard_zynq/src/eth/mod.rs +++ b/libboard_zynq/src/eth/mod.rs @@ -1,4 +1,7 @@ -use core::ops::{Deref, DerefMut}; +use core::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; use log::{error, info, warn}; use libregister::*; use super::slcr; @@ -42,14 +45,109 @@ impl DerefMut for Buffer { } } -pub struct Eth<'r, RX, TX> { +/// Gigabit Ethernet Peripheral +pub trait Gem { + fn setup_clock(tx_clock: u32); + fn regs() -> &'static mut regs::RegisterBlock; +} + +/// first Gigabit Ethernet peripheral +pub struct Gem0; + +impl Gem for Gem0 { + fn setup_clock(tx_clock: u32) { + let (divisor0, divisor1) = calculate_tx_divisors(tx_clock); + + slcr::RegisterBlock::unlocked(|slcr| { + slcr.gem0_clk_ctrl.write( + // 0x0050_0801: 8, 5: 100 Mb/s + // ...: 8, 1: 1000 Mb/s + slcr::GemClkCtrl::zeroed() + .clkact(true) + .srcsel(slcr::PllSource::IoPll) + .divisor(divisor0 as u8) + .divisor1(divisor1 as u8) + ); + // Enable gem0 recv clock + slcr.gem0_rclk_ctrl.write( + // 0x0000_0801 + slcr::RclkCtrl::zeroed() + .clkact(true) + ); + }); + } + + fn regs() -> &'static mut regs::RegisterBlock { + regs::RegisterBlock::gem0() + } +} + +/// second Gigabit Ethernet peripheal +pub struct Gem1; + +impl Gem for Gem1 { + fn setup_clock(tx_clock: u32) { + let (divisor0, divisor1) = calculate_tx_divisors(tx_clock); + + slcr::RegisterBlock::unlocked(|slcr| { + slcr.gem1_clk_ctrl.write( + slcr::GemClkCtrl::zeroed() + .clkact(true) + .srcsel(slcr::PllSource::IoPll) + .divisor(divisor0 as u8) + .divisor1(divisor1 as u8) + ); + // Enable gem1 recv clock + slcr.gem1_rclk_ctrl.write( + // 0x0000_0801 + slcr::RclkCtrl::zeroed() + .clkact(true) + ); + }); + } + + fn regs() -> &'static mut regs::RegisterBlock { + regs::RegisterBlock::gem1() + } +} + +fn calculate_tx_divisors(tx_clock: u32) -> (u8, u8) { + let io_pll = Clocks::get().io; + let target = (tx_clock - 1 + io_pll) / tx_clock; + + let mut best = None; + let mut best_error = 0; + for divisor0 in 1..63 { + for divisor1 in 1..63 { + let current = (divisor0 as u32) * (divisor1 as u32); + let error = if current > target { + current - target + } else { + target - current + }; + if best.is_none() || best_error > error { + best = Some((divisor0, divisor1)); + best_error = error; + } + } + } + let result = best.unwrap(); + info!("Eth TX clock for {}: {} / {} / {} = {}", + tx_clock, io_pll, + result.0, result.1, + io_pll / result.0 as u32 / result.1 as u32 + ); + result +} + +pub struct Eth { rx: RX, tx: TX, - inner: EthInner<'r>, + inner: EthInner, phy: Phy, } -impl<'r> Eth<'r, (), ()> { +impl Eth { pub fn default(macaddr: [u8; 6]) -> Self { slcr::RegisterBlock::unlocked(|slcr| { // Manual example: 0x0000_1280 @@ -182,22 +280,23 @@ impl<'r> Eth<'r, (), ()> { } pub fn gem0(macaddr: [u8; 6]) -> Self { - Self::setup_gem0_clock(TX_1000); - - let regs = regs::RegisterBlock::gem0(); - Self::from_regs(regs, macaddr) + Self::new(macaddr) } +} + +impl Eth { pub fn gem1(macaddr: [u8; 6]) -> Self { - Self::setup_gem1_clock(TX_1000); - - let regs = regs::RegisterBlock::gem1(); - Self::from_regs(regs, macaddr) + Self::new(macaddr) } +} + +impl Eth { + fn new(macaddr: [u8; 6]) -> Self { + GEM::setup_clock(TX_1000); - fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self { let mut inner = EthInner { - regs, + gem: PhantomData, link: None, }; inner.init(); @@ -216,54 +315,8 @@ impl<'r> Eth<'r, (), ()> { } } -impl<'r, RX, TX> Eth<'r, RX, TX> { - pub fn setup_gem0_clock(tx_clock: u32) { - let io_pll = Clocks::get().io; - let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63); - let d1 = (io_pll / tx_clock / d0).max(1).min(63); - - slcr::RegisterBlock::unlocked(|slcr| { - slcr.gem0_clk_ctrl.write( - // 0x0050_0801: 8, 5: 100 Mb/s - // ...: 8, 1: 1000 Mb/s - slcr::GemClkCtrl::zeroed() - .clkact(true) - .srcsel(slcr::PllSource::IoPll) - .divisor(d0 as u8) - .divisor1(d1 as u8) - ); - // Enable gem0 recv clock - slcr.gem0_rclk_ctrl.write( - // 0x0000_0801 - slcr::RclkCtrl::zeroed() - .clkact(true) - ); - }); - } - - pub fn setup_gem1_clock(tx_clock: u32) { - let io_pll = Clocks::get().io; - let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63); - let d1 = (io_pll / tx_clock / d0).max(1).min(63); - - slcr::RegisterBlock::unlocked(|slcr| { - slcr.gem1_clk_ctrl.write( - slcr::GemClkCtrl::zeroed() - .clkact(true) - .srcsel(slcr::PllSource::IoPll) - .divisor(d0 as u8) - .divisor1(d1 as u8) - ); - // Enable gem1 recv clock - slcr.gem1_rclk_ctrl.write( - // 0x0000_0801 - slcr::RclkCtrl::zeroed() - .clkact(true) - ); - }); - } - - pub fn start_rx(self, rx_size: usize) -> Eth<'r, rx::DescList, TX> { +impl Eth { + pub fn start_rx(self, rx_size: usize) -> Eth { let new_self = Eth { rx: rx::DescList::new(rx_size), tx: self.tx, @@ -272,17 +325,17 @@ impl<'r, RX, TX> Eth<'r, RX, TX> { }; let list_addr = new_self.rx.list_addr(); assert!(list_addr & 0b11 == 0); - new_self.inner.regs.rx_qbar.write( + GEM::regs().rx_qbar.write( regs::RxQbar::zeroed() .rx_q_baseaddr(list_addr >> 2) ); - new_self.inner.regs.net_ctrl.modify(|_, w| + GEM::regs().net_ctrl.modify(|_, w| w.rx_en(true) ); new_self } - pub fn start_tx(self, tx_size: usize) -> Eth<'r, RX, tx::DescList> { + pub fn start_tx(self, tx_size: usize) -> Eth { let new_self = Eth { rx: self.rx, tx: tx::DescList::new(tx_size), @@ -291,23 +344,23 @@ impl<'r, RX, TX> Eth<'r, RX, TX> { }; let list_addr = &new_self.tx.list_addr(); assert!(list_addr & 0b11 == 0); - new_self.inner.regs.tx_qbar.write( + GEM::regs().tx_qbar.write( regs::TxQbar::zeroed() .tx_q_baseaddr(list_addr >> 2) ); - new_self.inner.regs.net_ctrl.modify(|_, w| + GEM::regs().net_ctrl.modify(|_, w| w.tx_en(true) ); new_self } } -impl<'r, TX> Eth<'r, rx::DescList, TX> { +impl Eth { pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result>, rx::Error> { - let status = self.inner.regs.rx_status.read(); + let status = GEM::regs().rx_status.read(); if status.hresp_not_ok() { // Clear - self.inner.regs.rx_status.write( + GEM::regs().rx_status.write( regs::RxStatus::zeroed() .hresp_not_ok(true) ); @@ -315,7 +368,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> { } if status.rx_overrun() { // Clear - self.inner.regs.rx_status.write( + GEM::regs().rx_status.write( regs::RxStatus::zeroed() .rx_overrun(true) ); @@ -323,7 +376,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> { } if status.buffer_not_avail() { // Clear - self.inner.regs.rx_status.write( + GEM::regs().rx_status.write( regs::RxStatus::zeroed() .buffer_not_avail(true) ); @@ -335,7 +388,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> { match result { Ok(None) => { // No packet, clear status bit - self.inner.regs.rx_status.write( + GEM::regs().rx_status.write( regs::RxStatus::zeroed() .frame_recd(true) ); @@ -350,13 +403,13 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> { } } -impl<'r, RX> Eth<'r, RX, tx::DescList> { +impl Eth { pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option> { - self.tx.send(self.inner.regs, length) + self.tx.send(GEM::regs(), length) } } -impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescList> { +impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth { type RxToken = rx::PktRef<'a>; type TxToken = tx::Token<'a>; @@ -380,7 +433,7 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis match self.rx.recv_next() { Ok(Some(pktref)) => { let tx_token = tx::Token { - regs: self.inner.regs, + regs: GEM::regs(), desc_list: &mut self.tx, }; Some((pktref, tx_token)) @@ -398,33 +451,32 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis fn transmit(&'a mut self) -> Option { Some(tx::Token { - regs: self.inner.regs, + regs: GEM::regs(), desc_list: &mut self.tx, }) } - } -struct EthInner<'r> { - regs: &'r mut regs::RegisterBlock, +struct EthInner { + gem: PhantomData, link: Option, } -impl<'r> EthInner<'r> { +impl EthInner { fn init(&mut self) { // Clear the Network Control register. - self.regs.net_ctrl.write(regs::NetCtrl::zeroed()); - self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true)); + GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed()); + GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true)); // Clear the Status registers. - self.regs.rx_status.write( + GEM::regs().rx_status.write( regs::RxStatus::zeroed() .buffer_not_avail(true) .frame_recd(true) .rx_overrun(true) .hresp_not_ok(true) ); - self.regs.tx_status.write( + GEM::regs().tx_status.write( regs::TxStatus::zeroed() .used_bit_read(true) .collision(true) @@ -438,7 +490,7 @@ impl<'r> EthInner<'r> { .hresp_not_ok(true) ); // Disable all interrupts. - self.regs.intr_dis.write( + GEM::regs().intr_dis.write( regs::IntrDis::zeroed() .mgmt_done(true) .rx_complete(true) @@ -468,10 +520,10 @@ impl<'r> EthInner<'r> { .tsu_sec_incr(true) ); // Clear the buffer queues. - self.regs.rx_qbar.write( + GEM::regs().rx_qbar.write( regs::RxQbar::zeroed() ); - self.regs.tx_qbar.write( + GEM::regs().tx_qbar.write( regs::TxQbar::zeroed() ); } @@ -480,7 +532,7 @@ impl<'r> EthInner<'r> { let clocks = Clocks::get(); let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1; - self.regs.net_cfg.write( + GEM::regs().net_cfg.write( regs::NetCfg::zeroed() .full_duplex(true) .gige_en(true) @@ -505,17 +557,17 @@ impl<'r> EthInner<'r> { (u32::from(macaddr[3]) << 16) | (u32::from(macaddr[4]) << 8) | u32::from(macaddr[5]); - self.regs.spec_addr1_top.write( + GEM::regs().spec_addr1_top.write( regs::SpecAddrTop::zeroed() .addr_msbs(macaddr_msbs) ); - self.regs.spec_addr1_bot.write( + GEM::regs().spec_addr1_bot.write( regs::SpecAddrBot::zeroed() .addr_lsbs(macaddr_lsbs) ); - self.regs.dma_cfg.write( + GEM::regs().dma_cfg.write( regs::DmaCfg::zeroed() // 1536 bytes .ahb_mem_rx_buf_size((MTU >> 6) as u8) @@ -531,7 +583,7 @@ impl<'r> EthInner<'r> { .ahb_fixed_burst_len(0x10) ); - self.regs.net_ctrl.write( + GEM::regs().net_ctrl.write( regs::NetCtrl::zeroed() .mgmt_port_en(true) ); @@ -539,7 +591,7 @@ impl<'r> EthInner<'r> { fn wait_phy_idle(&self) { - while !self.regs.net_status.read().phy_mgmt_idle() {} + while !GEM::regs().net_status.read().phy_mgmt_idle() {} } @@ -564,10 +616,10 @@ impl<'r> EthInner<'r> { S100 => TX_100, S1000 => TX_1000, }; - Eth::<(), ()>::setup_gem0_clock(txclock); + GEM::setup_clock(txclock); /* .full_duplex(false) doesn't work even if half duplex has been negotiated. */ - self.regs.net_cfg.modify(|_, w| w + GEM::regs().net_cfg.modify(|_, w| w .full_duplex(true) .gige_en(link.speed == S1000) .speed(link.speed != S10) @@ -587,10 +639,10 @@ impl<'r> EthInner<'r> { } } -impl<'r> PhyAccess for EthInner<'r> { +impl PhyAccess for EthInner { fn read_phy(&mut self, addr: u8, reg: u8) -> u16 { self.wait_phy_idle(); - self.regs.phy_maint.write( + GEM::regs().phy_maint.write( regs::PhyMaint::zeroed() .clause_22(true) .operation(regs::PhyOperation::Read) @@ -599,12 +651,12 @@ impl<'r> PhyAccess for EthInner<'r> { .must_10(0b10) ); self.wait_phy_idle(); - self.regs.phy_maint.read().data() + GEM::regs().phy_maint.read().data() } fn write_phy(&mut self, addr: u8, reg: u8, data: u16) { self.wait_phy_idle(); - self.regs.phy_maint.write( + GEM::regs().phy_maint.write( regs::PhyMaint::zeroed() .clause_22(true) .operation(regs::PhyOperation::Write)