1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
//! API for system configuration (SYSCON) - always on //! //! The entry point to this API is [`SYSCON`]. Please refer to [`SYSCON`]'s //! documentation for additional information. //! //! This module mostly provides infrastructure required by other parts of the //! HAL API. For this reason, only a small subset of SYSCON functionality is //! currently implemented. //! //! The SYSCON peripheral is described in the user manual, chapter 4. // use core::marker::PhantomData; // use crate::raw::syscon::{ // // pdruncfg, presetctrl, starterp1, sysahbclkctrl, PDRUNCFG, PRESETCTRL, STARTERP1, SYSAHBCLKCTRL, // // UARTCLKDIV, UARTFRGDIV, UARTFRGMULT, // }; // use cortex_m_semihosting::dbg; // use crate::raw; // use crate::{ // time::{ // self, // clock, // }, // typestates::init_state, // }; crate::wrap_always_on_peripheral!(Syscon, SYSCON); impl Syscon { // TODO: relocate pub fn rev_id(&self) -> u8 { self.raw.dieid.read().rev_id().bits() } } /// The main API for the SYSCON peripheral impl Syscon { /// Enables the clock for a peripheral or other hardware component pub fn enable_clock<P: ClockControl>(&mut self, peripheral: &mut P) { peripheral.enable_clock(self); } /// Disable peripheral clock pub fn disable_clock<P: ClockControl>(&mut self, peripheral: &mut P) { peripheral.disable_clock(self); } /// Check if peripheral clock is enabled pub fn is_clock_enabled<P: ClockControl>(&self, peripheral: &P) -> bool { peripheral.is_clock_enabled(&self) } /// Reset a peripheral pub fn reset<P: ResetControl>(&mut self, peripheral: &mut P) { peripheral.assert_reset(self); peripheral.clear_reset(self); } } /// TODO: do this systematically /// By default, fro_12m is enabled in MAINCLKSELA impl Syscon { // pub fn get_main_clk(&self) -> u8 { // self.raw.mainclksela.read().sel().bits() // } // pub fn get_num_wait_states(&self) -> u8 { // self.raw.fmccr.read().flashtim().bits() // } // pub fn set_num_wait_states(&mut self, num_wait_states: u8) { // self.raw.fmccr.modify(|_, w| unsafe { w.flashtim().bits(num_wait_states) } ); // } // pub fn set_ahbclkdiv(&self, div: u8) { // assert!(div >= 1); // // dbg!(self.raw.ahbclkdiv.read().div().bits()); // self.raw.ahbclkdiv.modify(unsafe { |_, w| w.div().bits(div - 1) }); // // dbg!(self.raw.ahbclkdiv.read().div().bits()); // } // pub(crate) fn fro1mhz_as_main_clk(&mut self) { // self.raw.mainclksela.modify(|_, w| w.sel().enum_0x2()); // } // pub(crate) fn fro12mz_as_main_clk(&mut self) { // // TODO: change these names in the PAC to their UM names // // e.g. enum_0x0 -> fro_12m etc. // self.raw.mainclksela.modify(|_, w| w.sel().enum_0x0()); // } // pub(crate) fn fro96mhz_as_main_clk(&mut self) { // // 1. may have to anactrl_fro192m_ctrl_ena_96mhzclk // // 2. set voltage for 96MHz frequency // // 3. set flash access cycles // // formula is min(8, floor(9e-7*freq)) // // /* see fsl_clock.c, CLOCK_SetFLASHAccessCyclesForFreq */ // // in this case it's 8 // let num_wait_states = 8; // self.set_num_wait_states(num_wait_states); // // TODO: change these names in the PAC to their UM names // // e.g. enum_0x0 -> fro_12m etc. // self.raw.mainclksela.modify(|_, w| w.sel().enum_0x3()); // self.raw.mainclkselb.modify(|_, w| w.sel().enum_0x0()); // } // /// TODO: Check if fro_hf is actually 96Mhz?? // /// UM ANACTRL.FRO192M_CTRL.ENA_96MHZCLK says the 96Mhz clock // /// is disabled by default // pub fn fro_hf_as_usbfs_clk(&mut self) { // // 96 Mhz via changing main clock and sourcing that // // self.fro_hf_as_main_clk(); // // self.raw.usb0clksel.modify(|_, w| w.sel().enum_0x0()); // // Divide by n = 2 to get 48 Mhz (i.e., write (n - 1)) // dbg!(self.raw.usb0clkdiv.read().div().bits()); // self.raw // .usb0clkdiv // .modify(unsafe { |_, w| w.div().bits(0) }); // dbg!(self.raw.usb0clkdiv.read().div().bits()); // // Wait until the clock is stable (fsl_clock.c doesn't do this) // while self.raw.usb0clkdiv.read().reqflag().is_ongoing() {} // dbg!(self.raw.usb0clkdiv.read().div().bits()); // // Directly pick fro_hf as usbfs clock // self.raw.usb0clksel.modify(|_, w| w.sel().enum_0x3()); // } // pub fn is_enabled_usb0_hostm(&self) -> bool { // self.raw.ahbclkctrl2.read().usb0_hostm().is_enable() // } // pub fn enable_usb0_hostm(&mut self) { // self.raw.ahbclkctrl2.modify(|_, w| w.usb0_hostm().enable()); // } // pub fn is_enabled_usb0_hosts(&self) -> bool { // self.raw.ahbclkctrl2.read().usb0_hosts().is_enable() // } // pub fn enable_usb0_hosts(&mut self) { // self.raw.ahbclkctrl2.modify(|_, w| w.usb0_hosts().enable()); // } } /// Internal trait for controlling peripheral clocks /// /// This trait is an internal implementation detail and should neither be /// implemented nor used outside of LPC82x HAL. Any changes to this trait won't /// be considered breaking changes. /// /// Compared to https://git.io/fjpf9 (in lpc-rs/lpc8xx-hal/lpc8xx-hal-common) /// we use a less minimal API in order to hide the fact that there are three /// different AHLBCKLCTRL?, which a HAL user shouldn't really need to know about. pub trait ClockControl { /// Internal method to enable a peripheral clock fn enable_clock(&self, s: &mut Syscon); /// Internal method to disable a peripheral clock fn disable_clock(&self, s: &mut Syscon); /// Check if peripheral clock is enabled fn is_clock_enabled(&self, s: &Syscon) -> bool; } // // Unwrapped implementation for easier understanding // // impl ClockControl for raw::UTICK { // fn enable_clock<'h>(&self, h: &'h mut Handle) -> &'h mut Handle { // h.ahbclkctrl1.modify(|_, w| w.utick0().enable()); // h // } // fn disable_clock<'h>(&self, h: &'h mut Handle) -> &'h mut Handle { // h.ahbclkctrl1.modify(|_, w| w.utick0().disable()); // h // } // fn is_clock_enabled(&self, h: &Handle) -> bool { // h.ahbclkctrl1.read().utick0().is_enable() // } // } macro_rules! impl_clock_control { ($clock_control:ty, $clock:ident, $register:ident) => { impl ClockControl for $clock_control { fn enable_clock(&self, s: &mut Syscon) { s.raw.$register.modify(|_, w| w.$clock().enable()); while s.raw.$register.read().$clock().is_disable() {} } fn disable_clock(&self, s: &mut Syscon) { s.raw.$register.modify(|_, w| w.$clock().disable()); } fn is_clock_enabled(&self, s: &Syscon) -> bool { s.raw.$register.read().$clock().is_enable() } } }; } impl_clock_control!(raw::ADC0, adc, ahbclkctrl0); impl_clock_control!(raw::FLASH, flash, ahbclkctrl0); impl_clock_control!(raw::FLEXCOMM0, fc0, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM1, fc1, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM2, fc2, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM3, fc3, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM4, fc4, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM5, fc5, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM6, fc6, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM7, fc7, ahbclkctrl1); impl_clock_control!(raw::FLEXCOMM8, hs_lspi, ahbclkctrl2); impl_clock_control!(raw::IOCON, iocon, ahbclkctrl0); impl_clock_control!(raw::GINT0, gint, ahbclkctrl0); impl_clock_control!(raw::PINT, pint, ahbclkctrl0); impl_clock_control!(raw::USB0, usb0_dev, ahbclkctrl1); impl_clock_control!(raw::USBFSH, usb0_hosts, ahbclkctrl2); // well what about usb0_hostm? impl_clock_control!(raw::UTICK0, utick, ahbclkctrl1); impl_clock_control!(raw::ANACTRL, analog_ctrl, ahbclkctrl2); impl_clock_control!(raw::CASPER, casper, ahbclkctrl2); // there is no GPIO_SEC. what to do? create a PhantomData one? // impl_clock_control!(raw::GPIO_SEC, gpio_sec, ahbclkctrl2); impl_clock_control!(raw::PUF, puf, ahbclkctrl2); impl_clock_control!(raw::RNG, rng, ahbclkctrl2); // GPIO needs a separate implementation impl ClockControl for raw::GPIO { fn enable_clock(&self, s: &mut Syscon) { s.raw.ahbclkctrl0.modify(|_, w| w.gpio0().enable()); s.raw.ahbclkctrl0.modify(|_, w| w.gpio1().enable()); } fn disable_clock(&self, s: &mut Syscon) { s.raw.ahbclkctrl0.modify(|_, w| w.gpio0().disable()); s.raw.ahbclkctrl0.modify(|_, w| w.gpio1().disable()); } #[allow(clippy::nonminimal_bool)] fn is_clock_enabled(&self, s: &Syscon) -> bool { s.raw.ahbclkctrl0.read().gpio0().is_enable() && s.raw.ahbclkctrl0.read().gpio1().is_enable() } } pub trait ResetControl { /// Internal method to assert peripheral reset fn assert_reset(&self, syscon: &mut Syscon); /// Internal method to clear peripheral reset fn clear_reset(&self, syscon: &mut Syscon); } macro_rules! impl_reset_control { ($reset_control:ty, $field:ident, $register:ident) => { impl<'a> ResetControl for $reset_control { fn assert_reset(&self, syscon: &mut Syscon) { syscon.raw.$register.modify(|_, w| w.$field().asserted()); while syscon.raw.$register.read().$field().is_released() {} } fn clear_reset(&self, syscon: &mut Syscon) { syscon.raw.$register.modify(|_, w| w.$field().released()); while syscon.raw.$register.read().$field().is_asserted() {} } } }; ($reset_control:ty, $field1:ident, $field2:ident, $register:ident) => { impl<'a> ResetControl for $reset_control { fn assert_reset(&self, syscon: &mut Syscon) { syscon.raw.$register.modify(|_, w| w.$field1().asserted()); while syscon.raw.$register.read().$field1().is_released() {} syscon.raw.$register.modify(|_, w| w.$field2().asserted()); while syscon.raw.$register.read().$field2().is_released() {} } fn clear_reset(&self, syscon: &mut Syscon) { syscon.raw.$register.modify(|_, w| w.$field1().released()); while syscon.raw.$register.read().$field1().is_asserted() {} syscon.raw.$register.modify(|_, w| w.$field2().released()); while syscon.raw.$register.read().$field2().is_asserted() {} } } }; } // to be completed impl_reset_control!(raw::CASPER, casper_rst, presetctrl2); impl_reset_control!(raw::FLEXCOMM0, fc0_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM1, fc1_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM2, fc2_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM3, fc3_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM4, fc4_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM5, fc5_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM6, fc6_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM7, fc7_rst, presetctrl1); impl_reset_control!(raw::FLEXCOMM8, hs_lspi_rst, presetctrl2); impl_reset_control!(raw::USB0, usb0_dev_rst, presetctrl1); impl_reset_control!(raw::UTICK0, utick_rst, presetctrl1); impl_reset_control!(raw::USBFSH, usb0_hostm_rst, usb0_hosts_rst, presetctrl2);