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