Skip to content

Commit 7428b13

Browse files
committed
Enable PLL1 with basic setpoint support
Enable the PLL_LDO in all setpoints, and enable PLL1 in setpoint 1. Since I'm only using setpoints for PLL_LDO and PLL1 bring-up, I'm implementing the rest of the GPC state machine in software (specifically for the ENET clocks). This seems sufficient for enabling PLL1 and getting a clean 50MHz ENET reference clock. I can increase the ENET transfer speeds in the MAC and PHY. The GPC, PMU, and CCM APIs are a bare-minimal. They should be introduced in a separate commit / PR. But they're good enough for prototyping.
1 parent 9ea7127 commit 7428b13

File tree

8 files changed

+266
-6
lines changed

8 files changed

+266
-6
lines changed

board/src/imxrt1170evk-cm7.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ const CLOCK_GATES: &[clock_gate::Locator] = &[
4040
clock_gate::flexpwm::<{ PWM_INSTANCE }>(),
4141
clock_gate::lpi2c::<{ I2C_INSTANCE }>(),
4242
clock_gate::snvs(),
43+
];
44+
45+
const ENET_CLOCK_GATES: &[clock_gate::Locator] = &[
4346
clock_gate::enet(),
4447
clock_gate::enet_1g(),
4548
clock_gate::enet_qos(),
@@ -48,10 +51,49 @@ const CLOCK_GATES: &[clock_gate::Locator] = &[
4851
pub(crate) unsafe fn configure() {
4952
let mut ccm = ral::ccm::CCM::instance();
5053

54+
let gpc = unsafe { ral::gpc_cpu_mode_ctrl_::GPC_CPU_MODE_CTRL_0::instance() };
55+
let gpc = &*gpc;
56+
let pll = unsafe { ral::anadig_pll::ANADIG_PLL::instance() };
57+
let mut pmu = unsafe { ral::anadig_pmu::ANADIG_PMU::instance() };
58+
59+
hal::pmu::enable_pll_reference_voltage(&mut pmu, true);
60+
hal::pmu::set_phy_ldo_setpoints(&mut pmu, hal::pmu::Setpoint::all());
61+
hal::pmu::set_phy_ldo_control(&mut pmu, hal::pmu::ControlMode::Gpc);
62+
hal::pmu::set_pll_reference_control(&mut pmu, hal::pmu::ControlMode::Gpc);
63+
64+
for clock_source in {
65+
use hal::ccm::ClockSource::*;
66+
[Pll1, Pll1Clk, Pll1Div2, Pll1Div5]
67+
} {
68+
let oscpll = &ccm.OSCPLL[clock_source as usize];
69+
ral::write_reg!(ral::ccm::oscpll, oscpll, OSCPLL_DIRECT, 0);
70+
hal::ccm::set_setpoints(&mut ccm, clock_source, hal::ccm::Setpoint::SP1);
71+
hal::ccm::set_gpc_control_mode(&mut ccm, clock_source, hal::ccm::ControlMode::Gpc).unwrap();
72+
}
73+
74+
ral::modify_reg!(ral::anadig_pll, pll, SYS_PLL1_CTRL,
75+
SYS_PLL1_CONTROL_MODE: 1,
76+
SYS_PLL1_DIV2_CONTROL_MODE: 1,
77+
SYS_PLL1_DIV5_CONTROL_MODE: 1,
78+
);
79+
5180
prepare_clock_tree(&mut ccm);
5281
CLOCK_GATES
5382
.iter()
5483
.for_each(|locator| locator.set(&mut ccm, clock_gate::ON));
84+
85+
ENET_CLOCK_GATES
86+
.iter()
87+
.for_each(|l| l.set(&mut ccm, clock_gate::OFF));
88+
clock_tree::enet_root_on(&mut ccm, false);
89+
90+
clock_tree::configure_enet(RUN_MODE, &mut ccm);
91+
hal::gpc::request_setpoint_transition(gpc, 1).unwrap();
92+
93+
clock_tree::enet_root_on(&mut ccm, true);
94+
ENET_CLOCK_GATES
95+
.iter()
96+
.for_each(|l| l.set(&mut ccm, clock_gate::ON));
5597
}
5698

5799
fn prepare_clock_tree(ccm: &mut ral::ccm::CCM) {
@@ -61,7 +103,6 @@ fn prepare_clock_tree(ccm: &mut ral::ccm::CCM) {
61103
clock_tree::configure_lpuart::<{ CONSOLE_INSTANCE }>(RUN_MODE, ccm);
62104
clock_tree::configure_lpspi::<SPI_INSTANCE>(RUN_MODE, ccm);
63105
clock_tree::configure_lpi2c::<{ I2C_INSTANCE }>(RUN_MODE, ccm);
64-
clock_tree::configure_enet(RUN_MODE, ccm);
65106
}
66107

67108
pub const PIT_FREQUENCY: u32 = clock_tree::bus_frequency(RUN_MODE);
@@ -441,7 +482,7 @@ fn init_enet_phy_ksz8081rnb<
441482
mdio.write(
442483
KSZ8081_PHY_ADDR,
443484
PHY_AUTONEG_ADVERTISE_REG,
444-
(AutoNegotiation::BASETX_10_FULL_DUPLEX | AutoNegotiation::IEEE802_3_SELECTOR).bits(),
485+
(AutoNegotiation::BASETX_100_FULL_DUPLEX | AutoNegotiation::IEEE802_3_SELECTOR).bits(),
445486
)
446487
.unwrap();
447488
mdio.write(

board/src/imxrt11xx/clock_tree.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ enum ClockSource {
2929
/// SYS_PLL1, with dedicated 1GHz frequency
3030
/// for ETH.
3131
SysPll1,
32+
/// SYS_PLL1 with a fixed divide-by-2.
33+
SysPll1Div2,
3234
/// SYS_PLL2, main PLL (no PFDs).
3335
SysPll2,
3436
/// SYS_PLL3, main PLL (no PDFs).
@@ -50,6 +52,7 @@ impl ClockSource {
5052
match (self, run_mode) {
5153
(ClockSource::ArmCm7, RunMode::Overdrive) => 1_000_000_000, // Brr...
5254
(ClockSource::SysPll1, _) => 1_000_000_000,
55+
(ClockSource::SysPll1Div2, _) => 1_000_000_000 / 2,
5356
(ClockSource::SysPll2, _) => 528_000_000,
5457
(ClockSource::SysPll3, _) => 480_000_000,
5558
(ClockSource::XtalOsc24MHz, _) => XTAL_OSCILLATOR_HZ,
@@ -107,6 +110,12 @@ fn configure_clock_root(offset: usize, selection: &Selection, ccm: &mut CCM) {
107110
) {}
108111
}
109112

113+
#[inline(always)]
114+
fn clock_root_on(offset: usize, ccm: &mut CCM, on: bool) {
115+
let clock_root = &ccm.CLOCK_ROOT[offset];
116+
ral::modify_reg!(ral::ccm::clockroot, clock_root, CLOCK_ROOT_CONTROL, OFF: !on as u32);
117+
}
118+
110119
/// Set the bus clock (IPG) configuration for the Cortex M7.
111120
///
112121
/// When this call returns, the bus clock frequency matches the value returned
@@ -266,17 +275,24 @@ where
266275

267276
pub const ENET1_FREQUENCY: u32 = 50_000_000;
268277

278+
/// Turn on / off all ENET root clocks.
279+
pub fn enet_root_on(ccm: &mut CCM, on: bool) {
280+
for offset in 51..=57 {
281+
clock_root_on(offset, ccm, on);
282+
}
283+
}
284+
269285
/// Configure clocks for all-things ETH.
270286
///
271287
/// When this call returns, the ENET clock frequencies matches the value returned
272288
/// by [`enet_frequency`].
273289
pub fn configure_enet(run_mode: RunMode, ccm: &mut CCM) {
274-
const ENET1_ROOT_CLOCK_SOURCE: ClockSource = ClockSource::RcOsc400MHz;
290+
const ENET1_ROOT_CLOCK_SOURCE: ClockSource = ClockSource::SysPll1Div2;
275291
let enet1_divider: u32 = ENET1_ROOT_CLOCK_SOURCE.frequency(run_mode) / ENET1_FREQUENCY;
276292
configure_clock_root(
277293
51,
278294
&Selection {
279-
mux: 0b010,
295+
mux: 4,
280296
source: ENET1_ROOT_CLOCK_SOURCE,
281297
divider: enet1_divider,
282298
},

examples/rtic_enet_dhcp_tcp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ mod app {
154154
dev.set_duplex(hal::enet::Duplex::Full);
155155
dev.enable_mib(true);
156156
dev.enable_rmii_mode(true);
157-
dev.enable_10t_mode(true);
157+
dev.enable_10t_mode(false);
158158
dev.enable_mac(true);
159159

160160
let mut time: i64 = 0;

src/chip/imxrt11xx.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
pub mod ccm;
66
#[path = "dma.rs"]
77
pub mod dma;
8+
pub mod gpc;
9+
pub mod pmu;
810
pub mod usbphy;
911

1012
cfg_if::cfg_if! {
@@ -15,7 +17,7 @@ cfg_if::cfg_if! {
1517
}
1618

1719
pub(crate) mod reexports {
18-
pub use super::usbphy;
20+
pub use super::{gpc, pmu, usbphy};
1921
}
2022

2123
pub(crate) mod iomuxc {

src/chip/imxrt11xx/ccm.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,68 @@ pub mod clock_gate;
1010
pub mod output_source;
1111

1212
pub use crate::common::ccm::XTAL_OSCILLATOR_HZ;
13+
14+
pub use crate::gpc::{ControlMode, Setpoint};
15+
use crate::ral::{self, ccm::CCM};
16+
17+
/// A clock source.
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19+
#[repr(usize)]
20+
#[non_exhaustive]
21+
pub enum ClockSource {
22+
/// The voltage controlled oscillator for PLL1.
23+
///
24+
/// This output isn't exposed by the clock tree.
25+
/// However, the [`Pll1Clk`] is exposed.
26+
Pll1 = 21,
27+
/// The PLL1 output into the clock tree.
28+
Pll1Clk = 22,
29+
/// PLL1 with a fixed divide-by-2.
30+
Pll1Div2 = 23,
31+
/// PLL1 with a fixed divide-by-5.
32+
Pll1Div5 = 24,
33+
}
34+
35+
/// Indicates that the clock does not support setpoint control.
36+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37+
pub struct SetpointNotImplementedError(());
38+
39+
/// Signals if the clock source supports setpoint control.
40+
#[inline]
41+
pub fn has_setpoint(ccm: &CCM, clock_source: ClockSource) -> bool {
42+
let oscpll = &ccm.OSCPLL[clock_source as usize];
43+
ral::read_reg!(
44+
ral::ccm::oscpll,
45+
oscpll,
46+
OSCPLL_CONFIG,
47+
SETPOINT_PRESENT == 1
48+
)
49+
}
50+
51+
/// Set the GPC setpoint control mode for the clock source.
52+
///
53+
/// Returns an error if [`has_setpoint`] does not report support
54+
/// for setpoints.
55+
#[inline]
56+
pub fn set_gpc_control_mode(
57+
ccm: &mut CCM,
58+
clock_source: ClockSource,
59+
control_mode: ControlMode,
60+
) -> Result<(), SetpointNotImplementedError> {
61+
if control_mode == ControlMode::Gpc && !has_setpoint(ccm, clock_source) {
62+
return Err(SetpointNotImplementedError(()));
63+
}
64+
65+
let oscpll = &ccm.OSCPLL[clock_source as usize];
66+
ral::modify_reg!(ral::ccm::oscpll, oscpll, OSCPLL_AUTHEN, SETPOINT_MODE: control_mode.is_gpc() as u32);
67+
Ok(())
68+
}
69+
70+
/// Configure the setpoints for the clock.
71+
///
72+
/// Note: this does not affect the standby setpoints.
73+
#[inline]
74+
pub fn set_setpoints(ccm: &mut CCM, clock_source: ClockSource, setpoint: Setpoint) {
75+
let oscpll = &ccm.OSCPLL[clock_source as usize];
76+
ral::modify_reg!(ral::ccm::oscpll, oscpll, OSCPLL_SETPOINT, SETPOINT: setpoint.bits() as u32);
77+
}

src/chip/imxrt11xx/ccm/clock_gate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! This module exposes a similar API as the `clock_gate` API
44
//! for the 10xx MCUs. Consult that module's documentation for
55
//! more information.
6+
//!
7+
//! The API works for clock gates in unassigned mode.
68
79
use crate::ral::{self, ccm::CCM};
810

src/chip/imxrt11xx/gpc.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//! General Power Controller.
2+
3+
use crate::ral::{self, gpc_cpu_mode_ctrl_::RegisterBlock as GpcCtrl};
4+
5+
/// Describes the power / clock control mode.
6+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7+
#[repr(u32)]
8+
pub enum ControlMode {
9+
/// Software, not the GPC, controls the element.
10+
Software,
11+
/// The GPC controls the element.
12+
Gpc,
13+
}
14+
15+
impl ControlMode {
16+
pub(crate) const fn is_gpc(self) -> bool {
17+
matches!(self, Self::Gpc)
18+
}
19+
}
20+
21+
bitflags::bitflags! {
22+
/// A bitmask for specifying setpoints.
23+
///
24+
/// No matter the API, a high bit indicates that the setpoint
25+
/// is "enabled,", "compatible," or "on" for the given configuration.
26+
/// A lower bit indicates that the setpoint is "disabled," "not
27+
/// compatible," or "off" for the configuration. (Some registers have
28+
/// inverted interpretation; nevertheless, the APIs hide this for
29+
/// consistency.)
30+
pub struct Setpoint: u16 {
31+
/// Setpoint 0.
32+
const SP0 = 1 << 0;
33+
/// Setpoint 1.
34+
const SP1 = 1 << 1;
35+
/// Setpoint 2.
36+
const SP2 = 1 << 2;
37+
/// Setpoint 3.
38+
const SP3 = 1 << 3;
39+
/// Setpoint 4.
40+
const SP4 = 1 << 4;
41+
/// Setpoint 5.
42+
const SP5 = 1 << 5;
43+
/// Setpoint 6.
44+
const SP6 = 1 << 6;
45+
/// Setpoint 7.
46+
const SP7 = 1 << 7;
47+
/// Setpoint 8.
48+
const SP8 = 1 << 8;
49+
/// Setpoint 9.
50+
const SP9 = 1 << 9;
51+
/// Setpoint 10.
52+
const SP10 = 1 << 10;
53+
/// Setpoint 11.
54+
const SP11 = 1 << 11;
55+
/// Setpoint 12.
56+
const SP12 = 1 << 12;
57+
/// Setpoint 13.
58+
const SP13 = 1 << 13;
59+
/// Setpoint 14.
60+
const SP14 = 1 << 14;
61+
/// Setpoint 15.
62+
const SP15 = 1 << 15;
63+
}
64+
}
65+
66+
/// The setpoint is out of range.
67+
///
68+
/// Setpoints are bound between 0 and 15, inclusive.
69+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70+
pub struct SetpointOutOfRangeError(pub u32);
71+
72+
/// Request a run mode setpoint transition.
73+
///
74+
/// Blocks until the transition completes.
75+
pub fn request_setpoint_transition(
76+
gpc: &GpcCtrl,
77+
setpoint: u32,
78+
) -> Result<(), SetpointOutOfRangeError> {
79+
if setpoint < 16 {
80+
ral::modify_reg!(ral::gpc_cpu_mode_ctrl_, gpc, CM_SP_CTRL,
81+
CPU_SP_RUN: setpoint,
82+
CPU_SP_RUN_EN: 1
83+
);
84+
while ral::read_reg!(ral::gpc_cpu_mode_ctrl_, gpc, CM_SP_CTRL, CPU_SP_RUN_EN == 1) {}
85+
Ok(())
86+
} else {
87+
Err(SetpointOutOfRangeError(setpoint))
88+
}
89+
}

src/chip/imxrt11xx/pmu.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! Power Management Unit.
2+
//!
3+
//! The PMu also provides some control for the PHY LDO.
4+
//! Note that the terms "PHY LDO" and `LDO_PLL` represent
5+
//! the same thing, and this API prefers the PHY LDO term.
6+
7+
pub use crate::gpc::{ControlMode, Setpoint};
8+
use crate::ral::{self, anadig_pmu::ANADIG_PMU as PMU};
9+
10+
/// Set the control mode for PHY LDO.
11+
#[inline]
12+
pub fn set_phy_ldo_control(pmu: &mut PMU, control_mode: ControlMode) {
13+
ral::modify_reg!(ral::anadig_pmu, pmu, PMU_LDO_PLL, LDO_PLL_CONTROL_MODE: control_mode.is_gpc() as u32);
14+
}
15+
16+
/// Specify with setpoints should turn on the PHY_LDO.
17+
///
18+
/// Each high bit indicates that the PHY LDO is on for that setpoint.
19+
/// See [`Setpoint`] documentation for more information.
20+
#[inline]
21+
pub fn set_phy_ldo_setpoints(pmu: &PMU, setpoint: Setpoint) {
22+
// Low bit == on.
23+
// High bit == off.
24+
//
25+
// Flip the bits, since each software setpoint bit
26+
// suggests "on."
27+
ral::write_reg!(
28+
ral::anadig_pmu,
29+
pmu,
30+
LDO_PLL_ENABLE_SP,
31+
(!setpoint).bits() as u32
32+
);
33+
}
34+
35+
/// Enable or disable the PLL bandgap reference voltage.
36+
#[inline]
37+
pub fn enable_pll_reference_voltage(pmu: &mut PMU, enable: bool) {
38+
ral::modify_reg!(ral::anadig_pmu, pmu, PMU_REF_CTRL, EN_PLL_VOL_REF_BUFFER: enable as u32);
39+
}
40+
41+
/// Set the control mode for the PLL bandgap reference voltage.
42+
#[inline]
43+
pub fn set_pll_reference_control(pmu: &mut PMU, control_mode: ControlMode) {
44+
ral::modify_reg!(ral::anadig_pmu, pmu, PMU_REF_CTRL, REF_CONTROL_MODE: control_mode.is_gpc() as u32);
45+
}

0 commit comments

Comments
 (0)