diff --git a/ci.sh b/ci.sh index 810a9ab42..f08c38b7c 100755 --- a/ci.sh +++ b/ci.sh @@ -53,6 +53,8 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ + --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt633s,defmt,unstable-pac \ + --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt685s,defmt,unstable-pac \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,gpiote,time,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time,time-driver-rtc1 \ diff --git a/docs/pages/imxrt.adoc b/docs/pages/imxrt.adoc new file mode 100644 index 000000000..adf5218e8 --- /dev/null +++ b/docs/pages/imxrt.adoc @@ -0,0 +1,13 @@ += Embassy iMXRT HAL + +The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Embassy iMXRT HAL] is based on the following PACs (Peripheral Access Crate): + +* link:https://github.com/OpenDevicePartnership/mimxrt685s-pac[mimxrt685s-pac] +* link:https://github.com/OpenDevicePartnership/mimxrt633s-pac[mimxrt633s-pac] + +== Peripherals + +The following peripherals have a HAL implementation at present + +* GPIO + diff --git a/docs/pages/system.adoc b/docs/pages/system.adoc index 985f92b18..09241a8df 100644 --- a/docs/pages/system.adoc +++ b/docs/pages/system.adoc @@ -6,6 +6,7 @@ include::runtime.adoc[leveloffset = 2] include::bootloader.adoc[leveloffset = 2] include::time_keeping.adoc[leveloffset = 2] include::hal.adoc[leveloffset = 2] +include::imxrt.adoc[leveloffset = 2] include::nrf.adoc[leveloffset = 2] include::stm32.adoc[leveloffset = 2] include::sharing_peripherals.adoc[leveloffset = 2] diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml new file mode 100644 index 000000000..38087bf77 --- /dev/null +++ b/embassy-imxrt/Cargo.toml @@ -0,0 +1,84 @@ +[package] +name = "embassy-imxrt" +version = "0.1.0" +edition = "2021" +license = "MIT" +description = "Embassy Hardware Abstraction Layer (HAL) for the IMXRT microcontroller" +keywords = ["embedded", "async", "imxrt", "rt600", "embedded-hal"] +categories = ["embedded", "hardware-support", "no-std", "asynchronous"] +repository = "https://github.com/embassy-rs/embassy" +documentation = "https://docs.embassy.dev/embassy-imxrt" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/" +features = ["defmt", "unstable-pac"] +flavors = [ + { regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" } +] + +[package.metadata.docs.rs] +features = ["mimxrt685s", "defmt", "unstable-pac"] +rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = ["rt"] + +## Cortex-M runtime (enabled by default) +rt = [ + "mimxrt685s-pac?/rt", + "mimxrt633s-pac?/rt", +] + +## Enable defmt +defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "mimxrt685s-pac?/defmt", "mimxrt633s-pac?/defmt"] + +## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable) +unstable-pac = [] + +# Features starting with `_` are for internal use only. They're not intended +# to be enabled by other crates, and are not covered by semver guarantees. + +_mimxrt685s = [] +_mimxrt633s = ["_espi"] + +# Peripherals +_espi = [] + +#! ### Chip selection features +## MIMXRT685S +mimxrt685s = ["mimxrt685s-pac", "_mimxrt685s"] +## MIMXRT633S +mimxrt633s = ["mimxrt633s-pac", "_mimxrt633s"] + +[dependencies] +embassy-sync = { version = "0.6.2", path = "../embassy-sync" } +embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } +embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } +embassy-futures = { version = "0.1.1", path = "../embassy-futures" } + +defmt = { version = "1.0", optional = true } +log = { version = "0.4.14", optional = true } +nb = "1.0.0" +cfg-if = "1.0.0" +cortex-m-rt = ">=0.7.3,<0.8" +cortex-m = "0.7.6" +critical-section = "1.1" +embedded-io = { version = "0.6.1" } +embedded-io-async = { version = "0.6.1" } +rand_core = "0.6.4" +fixed = "1.23.1" + +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ + "unproven", +] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = { version = "1.0" } +embedded-hal-nb = { version = "1.0" } + +document-features = "0.2.7" +paste = "1.0" + +# PACs +mimxrt685s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] } +mimxrt633s-pac = { version = "0.4.0", optional = true, features = ["rt", "critical-section"] } diff --git a/embassy-imxrt/README.md b/embassy-imxrt/README.md new file mode 100644 index 000000000..cfdfb8ce2 --- /dev/null +++ b/embassy-imxrt/README.md @@ -0,0 +1,59 @@ +# Embassy iMXRT HAL + +## Introduction + +HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so +raw register manipulation is not needed. + +The Embassy iMXRT HAL targets the NXP iMXRT Family of MCUs. The HAL implements +both blocking and async APIs for many peripherals. The benefit of using the +async APIs is that the HAL takes care of waiting for peripherals to complete +operations in low power mode and handling of interrupts, so that applications +can focus on business logic. + +NOTE: The Embassy HALs can be used both for non-async and async operations. For +async, you can choose which runtime you want to use. + +For a complete list of available peripherals and features, see the +[embassy-imxrt documentation](https://docs.embassy.dev/embassy-imxrt). + +## Hardware support + +The `embassy-imxrt` HAL currently supports two main variants of the iMXRT +family: + +* MIMXRT685S + ([examples](https://github.com/OpenDevicePartnership/embassy-imxrt/tree/main/examples/rt685s-evk)) +* MIMXRT633s + ([examples](https://github.com/OpenDevicePartnership/embassy-imxrt/tree/main/examples/rt633)) + +Several peripherals are supported and tested on both supported chip variants. To +check what's available, make sure to the MCU you're targetting in the top menu +in the [documentation](https://docs.embassy.dev/embassy-imxrt). + +## TrustZone support + +TrustZone support is yet to be implemented. + +## Time driver + +If the `time-driver` feature is enabled, the HAL uses the RTC peripheral as a +global time driver for [embassy-time](https://crates.io/crates/embassy-time), +with a tick rate of 32768 Hz. + +## Embedded-hal + +The `embassy-imxrt` HAL implements the traits from +[embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and +[embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as +[embedded-io](https://crates.io/crates/embedded-io) and +[embedded-io-async](https://crates.io/crates/embedded-io-async). + +## Interoperability + +This crate can run on any executor. + +Optionally, some features requiring +[`embassy-time`](https://crates.io/crates/embassy-time) can be activated with +the `time` feature. If you enable it, you must link an `embassy-time` driver in +your project. diff --git a/embassy-imxrt/src/chips/mimxrt633s.rs b/embassy-imxrt/src/chips/mimxrt633s.rs new file mode 100644 index 000000000..30072132a --- /dev/null +++ b/embassy-imxrt/src/chips/mimxrt633s.rs @@ -0,0 +1,389 @@ +pub use mimxrt633s_pac as pac; + +#[allow(clippy::missing_safety_doc)] +pub mod interrupts { + embassy_hal_internal::interrupt_mod!( + ACMP, + ADC0, + CASPER, + CTIMER0, + CTIMER1, + CTIMER2, + CTIMER3, + CTIMER4, + DMA0, + DMA1, + DMIC0, + ESPI, + FLEXCOMM0, + FLEXCOMM1, + FLEXCOMM14, + FLEXCOMM15, + FLEXCOMM2, + FLEXCOMM3, + FLEXCOMM4, + FLEXCOMM5, + FLEXCOMM6, + FLEXCOMM7, + FLEXSPI, + GPIO_INTA, + GPIO_INTB, + HASHCRYPT, + HWVAD0, + HYPERVISOR, + I3C0, + MRT0, + MU_A, + OS_EVENT, + PIN_INT0, + PIN_INT1, + PIN_INT2, + PIN_INT3, + PIN_INT4, + PIN_INT5, + PIN_INT6, + PIN_INT7, + PMC_PMIC, + POWERQUAD, + PUF, + RNG, + RTC, + SCT0, + SECUREVIOLATION, + SGPIO_INTA, + SGPIO_INTB, + USB, + USBPHY_DCD, + USB_WAKEUP, + USDHC0, + USDHC1, + UTICK0, + WDT0, + WDT1, + ); +} + +embassy_hal_internal::peripherals!( + ACMP, + ADC0, + CASPER, + CRC, + CTIMER0_COUNT_CHANNEL0, + CTIMER0_COUNT_CHANNEL1, + CTIMER0_COUNT_CHANNEL2, + CTIMER0_COUNT_CHANNEL3, + CTIMER0_CAPTURE_CHANNEL0, + CTIMER0_CAPTURE_CHANNEL1, + CTIMER0_CAPTURE_CHANNEL2, + CTIMER0_CAPTURE_CHANNEL3, + CTIMER1_COUNT_CHANNEL0, + CTIMER1_COUNT_CHANNEL1, + CTIMER1_COUNT_CHANNEL2, + CTIMER1_COUNT_CHANNEL3, + CTIMER1_CAPTURE_CHANNEL0, + CTIMER1_CAPTURE_CHANNEL1, + CTIMER1_CAPTURE_CHANNEL2, + CTIMER1_CAPTURE_CHANNEL3, + CTIMER2_COUNT_CHANNEL0, + CTIMER2_COUNT_CHANNEL1, + CTIMER2_COUNT_CHANNEL2, + CTIMER2_COUNT_CHANNEL3, + CTIMER2_CAPTURE_CHANNEL0, + CTIMER2_CAPTURE_CHANNEL1, + CTIMER2_CAPTURE_CHANNEL2, + CTIMER2_CAPTURE_CHANNEL3, + CTIMER3_COUNT_CHANNEL0, + CTIMER3_COUNT_CHANNEL1, + CTIMER3_COUNT_CHANNEL2, + CTIMER3_COUNT_CHANNEL3, + CTIMER3_CAPTURE_CHANNEL0, + CTIMER3_CAPTURE_CHANNEL1, + CTIMER3_CAPTURE_CHANNEL2, + CTIMER3_CAPTURE_CHANNEL3, + CTIMER4_COUNT_CHANNEL0, + CTIMER4_COUNT_CHANNEL1, + CTIMER4_COUNT_CHANNEL2, + CTIMER4_COUNT_CHANNEL3, + CTIMER4_CAPTURE_CHANNEL0, + CTIMER4_CAPTURE_CHANNEL1, + CTIMER4_CAPTURE_CHANNEL2, + CTIMER4_CAPTURE_CHANNEL3, + DMA0, + DMA0_CH0, + DMA0_CH1, + DMA0_CH2, + DMA0_CH3, + DMA0_CH4, + DMA0_CH5, + DMA0_CH6, + DMA0_CH7, + DMA0_CH8, + DMA0_CH9, + DMA0_CH10, + DMA0_CH11, + DMA0_CH12, + DMA0_CH13, + DMA0_CH14, + DMA0_CH15, + DMA0_CH16, + DMA0_CH17, + DMA0_CH18, + DMA0_CH19, + DMA0_CH20, + DMA0_CH21, + DMA0_CH22, + DMA0_CH23, + DMA0_CH24, + DMA0_CH25, + DMA0_CH26, + DMA0_CH27, + DMA0_CH28, + DMA0_CH29, + DMA0_CH30, + DMA0_CH31, + DMA0_CH32, + DMA1, + DMA1_CH0, + DMA1_CH1, + DMA1_CH2, + DMA1_CH3, + DMA1_CH4, + DMA1_CH5, + DMA1_CH6, + DMA1_CH7, + DMA1_CH8, + DMA1_CH9, + DMA1_CH10, + DMA1_CH11, + DMA1_CH12, + DMA1_CH13, + DMA1_CH14, + DMA1_CH15, + DMA1_CH16, + DMA1_CH17, + DMA1_CH18, + DMA1_CH19, + DMA1_CH20, + DMA1_CH21, + DMA1_CH22, + DMA1_CH23, + DMA1_CH24, + DMA1_CH25, + DMA1_CH26, + DMA1_CH27, + DMA1_CH28, + DMA1_CH29, + DMA1_CH30, + DMA1_CH31, + DMA1_CH32, + DMIC0, + DSPWAKE, + ESPI, + FLEXCOMM0, + FLEXCOMM1, + FLEXCOMM14, + FLEXCOMM15, + FLEXCOMM2, + FLEXCOMM3, + FLEXCOMM4, + FLEXCOMM5, + FLEXCOMM6, + FLEXCOMM7, + FLEXSPI, + FREQME, + GPIO_INTA, + GPIO_INTB, + HASHCRYPT, + HSGPIO0, + HSGPIO1, + HSGPIO2, + HSGPIO3, + HSGPIO4, + HSGPIO5, + HSGPIO6, + HSGPIO7, + HWVAD0, + HYPERVISOR, + I3C0, + MRT0, + MU_A, + OS_EVENT, + PIN_INT0, + PIN_INT1, + PIN_INT2, + PIN_INT3, + PIN_INT4, + PIN_INT5, + PIN_INT6, + PIN_INT7, + PIO0_0, + PIO0_1, + PIO0_10, + PIO0_11, + PIO0_12, + PIO0_13, + PIO0_14, + PIO0_15, + PIO0_16, + PIO0_17, + PIO0_18, + PIO0_19, + PIO0_2, + PIO0_20, + PIO0_21, + PIO0_22, + PIO0_23, + PIO0_24, + PIO0_25, + PIO0_26, + PIO0_27, + PIO0_28, + PIO0_29, + PIO0_3, + PIO0_30, + PIO0_31, + PIO0_4, + PIO0_5, + PIO0_6, + PIO0_7, + PIO0_8, + PIO0_9, + PIO1_0, + PIO1_1, + PIO1_10, + PIO1_11, + PIO1_12, + PIO1_13, + PIO1_14, + PIO1_15, + PIO1_16, + PIO1_17, + PIO1_18, + PIO1_19, + PIO1_2, + PIO1_20, + PIO1_21, + PIO1_22, + PIO1_23, + PIO1_24, + PIO1_25, + PIO1_26, + PIO1_27, + PIO1_28, + PIO1_29, + PIO1_3, + PIO1_30, + PIO1_31, + PIO1_4, + PIO1_5, + PIO1_6, + PIO1_7, + PIO1_8, + PIO1_9, + PIO2_0, + PIO2_1, + PIO2_10, + PIO2_11, + PIO2_12, + PIO2_13, + PIO2_14, + PIO2_15, + PIO2_16, + PIO2_17, + PIO2_18, + PIO2_19, + PIO2_2, + PIO2_20, + PIO2_21, + PIO2_22, + PIO2_23, + PIO2_24, + PIO2_25, + PIO2_26, + PIO2_27, + PIO2_28, + PIO2_29, + PIO2_3, + PIO2_30, + PIO2_31, + PIO2_4, + PIO2_5, + PIO2_6, + PIO2_7, + PIO2_8, + PIO2_9, + PIO3_0, + PIO3_1, + PIO3_10, + PIO3_11, + PIO3_12, + PIO3_13, + PIO3_14, + PIO3_15, + PIO3_16, + PIO3_17, + PIO3_18, + PIO3_19, + PIO3_2, + PIO3_20, + PIO3_21, + PIO3_22, + PIO3_23, + PIO3_24, + PIO3_25, + PIO3_26, + PIO3_27, + PIO3_28, + PIO3_29, + PIO3_3, + PIO3_30, + PIO3_31, + PIO3_4, + PIO3_5, + PIO3_6, + PIO3_7, + PIO3_8, + PIO3_9, + PIO4_0, + PIO4_1, + PIO4_10, + PIO4_2, + PIO4_3, + PIO4_4, + PIO4_5, + PIO4_6, + PIO4_7, + PIO4_8, + PIO4_9, + PIO7_24, + PIO7_25, + PIO7_26, + PIO7_27, + PIO7_28, + PIO7_29, + PIO7_30, + PIO7_31, + PIOFC15_SCL, + PIOFC15_SDA, + PMC_PMIC, + PIMCTL, + POWERQUAD, + PUF, + RNG, + RTC, + SCT0, + SECGPIO, + SECUREVIOLATION, + SEMA42, + SGPIO_INTA, + SGPIO_INTB, + USBHSD, + USBHSH, + USBPHY, + USB_WAKEUP, + USDHC0, + USDHC1, + UTICK0, + WDT0, + WDT1, +); diff --git a/embassy-imxrt/src/chips/mimxrt685s.rs b/embassy-imxrt/src/chips/mimxrt685s.rs new file mode 100644 index 000000000..746861e35 --- /dev/null +++ b/embassy-imxrt/src/chips/mimxrt685s.rs @@ -0,0 +1,388 @@ +pub use mimxrt685s_pac as pac; + +#[allow(clippy::missing_safety_doc)] +pub mod interrupts { + embassy_hal_internal::interrupt_mod!( + ACMP, + ADC0, + CASPER, + CTIMER0, + CTIMER1, + CTIMER2, + CTIMER3, + CTIMER4, + DMA0, + DMA1, + DMIC0, + DSPWAKE, + FLEXCOMM0, + FLEXCOMM1, + FLEXCOMM14, + FLEXCOMM15, + FLEXCOMM2, + FLEXCOMM3, + FLEXCOMM4, + FLEXCOMM5, + FLEXCOMM6, + FLEXCOMM7, + FLEXSPI, + GPIO_INTA, + GPIO_INTB, + HASHCRYPT, + HWVAD0, + HYPERVISOR, + I3C0, + MRT0, + MU_A, + OS_EVENT, + PIN_INT0, + PIN_INT1, + PIN_INT2, + PIN_INT3, + PIN_INT4, + PIN_INT5, + PIN_INT6, + PIN_INT7, + PMC_PMIC, + POWERQUAD, + PUF, + RNG, + RTC, + SCT0, + SECUREVIOLATION, + SGPIO_INTA, + SGPIO_INTB, + USB, + USBPHY_DCD, + USB_WAKEUP, + USDHC0, + USDHC1, + UTICK0, + WDT0, + WDT1, + ); +} + +embassy_hal_internal::peripherals!( + ACMP, + ADC0, + CASPER, + CRC, + CTIMER0_COUNT_CHANNEL0, + CTIMER0_COUNT_CHANNEL1, + CTIMER0_COUNT_CHANNEL2, + CTIMER0_COUNT_CHANNEL3, + CTIMER0_CAPTURE_CHANNEL0, + CTIMER0_CAPTURE_CHANNEL1, + CTIMER0_CAPTURE_CHANNEL2, + CTIMER0_CAPTURE_CHANNEL3, + CTIMER1_COUNT_CHANNEL0, + CTIMER1_COUNT_CHANNEL1, + CTIMER1_COUNT_CHANNEL2, + CTIMER1_COUNT_CHANNEL3, + CTIMER1_CAPTURE_CHANNEL0, + CTIMER1_CAPTURE_CHANNEL1, + CTIMER1_CAPTURE_CHANNEL2, + CTIMER1_CAPTURE_CHANNEL3, + CTIMER2_COUNT_CHANNEL0, + CTIMER2_COUNT_CHANNEL1, + CTIMER2_COUNT_CHANNEL2, + CTIMER2_COUNT_CHANNEL3, + CTIMER2_CAPTURE_CHANNEL0, + CTIMER2_CAPTURE_CHANNEL1, + CTIMER2_CAPTURE_CHANNEL2, + CTIMER2_CAPTURE_CHANNEL3, + CTIMER3_COUNT_CHANNEL0, + CTIMER3_COUNT_CHANNEL1, + CTIMER3_COUNT_CHANNEL2, + CTIMER3_COUNT_CHANNEL3, + CTIMER3_CAPTURE_CHANNEL0, + CTIMER3_CAPTURE_CHANNEL1, + CTIMER3_CAPTURE_CHANNEL2, + CTIMER3_CAPTURE_CHANNEL3, + CTIMER4_COUNT_CHANNEL0, + CTIMER4_COUNT_CHANNEL1, + CTIMER4_COUNT_CHANNEL2, + CTIMER4_COUNT_CHANNEL3, + CTIMER4_CAPTURE_CHANNEL0, + CTIMER4_CAPTURE_CHANNEL1, + CTIMER4_CAPTURE_CHANNEL2, + CTIMER4_CAPTURE_CHANNEL3, + DMA0, + DMA0_CH0, + DMA0_CH1, + DMA0_CH2, + DMA0_CH3, + DMA0_CH4, + DMA0_CH5, + DMA0_CH6, + DMA0_CH7, + DMA0_CH8, + DMA0_CH9, + DMA0_CH10, + DMA0_CH11, + DMA0_CH12, + DMA0_CH13, + DMA0_CH14, + DMA0_CH15, + DMA0_CH16, + DMA0_CH17, + DMA0_CH18, + DMA0_CH19, + DMA0_CH20, + DMA0_CH21, + DMA0_CH22, + DMA0_CH23, + DMA0_CH24, + DMA0_CH25, + DMA0_CH26, + DMA0_CH27, + DMA0_CH28, + DMA0_CH29, + DMA0_CH30, + DMA0_CH31, + DMA0_CH32, + DMA1, + DMA1_CH0, + DMA1_CH1, + DMA1_CH2, + DMA1_CH3, + DMA1_CH4, + DMA1_CH5, + DMA1_CH6, + DMA1_CH7, + DMA1_CH8, + DMA1_CH9, + DMA1_CH10, + DMA1_CH11, + DMA1_CH12, + DMA1_CH13, + DMA1_CH14, + DMA1_CH15, + DMA1_CH16, + DMA1_CH17, + DMA1_CH18, + DMA1_CH19, + DMA1_CH20, + DMA1_CH21, + DMA1_CH22, + DMA1_CH23, + DMA1_CH24, + DMA1_CH25, + DMA1_CH26, + DMA1_CH27, + DMA1_CH28, + DMA1_CH29, + DMA1_CH30, + DMA1_CH31, + DMA1_CH32, + DMIC0, + DSPWAKE, + FLEXCOMM0, + FLEXCOMM1, + FLEXCOMM14, + FLEXCOMM15, + FLEXCOMM2, + FLEXCOMM3, + FLEXCOMM4, + FLEXCOMM5, + FLEXCOMM6, + FLEXCOMM7, + FLEXSPI, + FREQME, + GPIO_INTA, + GPIO_INTB, + HASHCRYPT, + HSGPIO0, + HSGPIO1, + HSGPIO2, + HSGPIO3, + HSGPIO4, + HSGPIO5, + HSGPIO6, + HSGPIO7, + HWVAD0, + HYPERVISOR, + I3C0, + MRT0, + MU_A, + OS_EVENT, + PIN_INT0, + PIN_INT1, + PIN_INT2, + PIN_INT3, + PIN_INT4, + PIN_INT5, + PIN_INT6, + PIN_INT7, + PIO0_0, + PIO0_1, + PIO0_10, + PIO0_11, + PIO0_12, + PIO0_13, + PIO0_14, + PIO0_15, + PIO0_16, + PIO0_17, + PIO0_18, + PIO0_19, + PIO0_2, + PIO0_20, + PIO0_21, + PIO0_22, + PIO0_23, + PIO0_24, + PIO0_25, + PIO0_26, + PIO0_27, + PIO0_28, + PIO0_29, + PIO0_3, + PIO0_30, + PIO0_31, + PIO0_4, + PIO0_5, + PIO0_6, + PIO0_7, + PIO0_8, + PIO0_9, + PIO1_0, + PIO1_1, + PIO1_10, + PIO1_11, + PIO1_12, + PIO1_13, + PIO1_14, + PIO1_15, + PIO1_16, + PIO1_17, + PIO1_18, + PIO1_19, + PIO1_2, + PIO1_20, + PIO1_21, + PIO1_22, + PIO1_23, + PIO1_24, + PIO1_25, + PIO1_26, + PIO1_27, + PIO1_28, + PIO1_29, + PIO1_3, + PIO1_30, + PIO1_31, + PIO1_4, + PIO1_5, + PIO1_6, + PIO1_7, + PIO1_8, + PIO1_9, + PIO2_0, + PIO2_1, + PIO2_10, + PIO2_11, + PIO2_12, + PIO2_13, + PIO2_14, + PIO2_15, + PIO2_16, + PIO2_17, + PIO2_18, + PIO2_19, + PIO2_2, + PIO2_20, + PIO2_21, + PIO2_22, + PIO2_23, + PIO2_24, + PIO2_25, + PIO2_26, + PIO2_27, + PIO2_28, + PIO2_29, + PIO2_3, + PIO2_30, + PIO2_31, + PIO2_4, + PIO2_5, + PIO2_6, + PIO2_7, + PIO2_8, + PIO2_9, + PIO3_0, + PIO3_1, + PIO3_10, + PIO3_11, + PIO3_12, + PIO3_13, + PIO3_14, + PIO3_15, + PIO3_16, + PIO3_17, + PIO3_18, + PIO3_19, + PIO3_2, + PIO3_20, + PIO3_21, + PIO3_22, + PIO3_23, + PIO3_24, + PIO3_25, + PIO3_26, + PIO3_27, + PIO3_28, + PIO3_29, + PIO3_3, + PIO3_30, + PIO3_31, + PIO3_4, + PIO3_5, + PIO3_6, + PIO3_7, + PIO3_8, + PIO3_9, + PIO4_0, + PIO4_1, + PIO4_10, + PIO4_2, + PIO4_3, + PIO4_4, + PIO4_5, + PIO4_6, + PIO4_7, + PIO4_8, + PIO4_9, + PIO7_24, + PIO7_25, + PIO7_26, + PIO7_27, + PIO7_28, + PIO7_29, + PIO7_30, + PIO7_31, + PIOFC15_SCL, + PIOFC15_SDA, + PMC_PMIC, + PIMCTL, + POWERQUAD, + PUF, + RNG, + RTC, + SCT0, + SECGPIO, + SECUREVIOLATION, + SEMA42, + SGPIO_INTA, + SGPIO_INTB, + USBHSD, + USBHSH, + USBPHY, + USB_WAKEUP, + USDHC0, + USDHC1, + UTICK0, + WDT0, + WDT1, +); diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs new file mode 100644 index 000000000..1d36fb142 --- /dev/null +++ b/embassy-imxrt/src/clocks.rs @@ -0,0 +1,1687 @@ +//! Clock configuration for the `RT6xx` +use core::sync::atomic::{AtomicU32, AtomicU8, Ordering}; + +#[cfg(feature = "defmt")] +use defmt; +use paste::paste; + +use crate::pac; + +/// Clock configuration; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Clocks { + /// Low power oscillator + Lposc, + /// System Frequency Resonance Oscillator (SFRO) + Sfro, + /// Real Time Clock + Rtc, + /// Feed-forward Ring Oscillator + Ffro, // This includes that div2 and div4 variations + /// External Clock Input + ClkIn, + /// AHB Clock + Hclk, + /// Main Clock + MainClk, + /// Main PLL Clock + MainPllClk, // also has aux0,aux1,dsp, and audio pll's downstream + /// System Clock + SysClk, + /// System Oscillator + SysOscClk, + /// ADC Clock + Adc, +} + +/// Clock configuration. +pub struct ClockConfig { + /// low-power oscillator config + pub lposc: LposcConfig, + /// 16Mhz internal oscillator config + pub sfro: SfroConfig, + /// Real Time Clock config + pub rtc: RtcClkConfig, + /// 48/60 Mhz internal oscillator config + pub ffro: FfroConfig, + // pub pll: Option, //potentially covered in main pll clk + /// External Clock-In config + pub clk_in: ClkInConfig, + /// AHB bus clock config + pub hclk: HclkConfig, + /// Main Clock config + pub main_clk: MainClkConfig, + /// Main Pll clock config + pub main_pll_clk: MainPllClkConfig, + /// Software concept to be used with systick, doesn't map to a register + pub sys_clk: SysClkConfig, + /// System Oscillator Config + pub sys_osc: SysOscConfig, + // todo: move ADC here +} + +impl ClockConfig { + /// Clock configuration derived from external crystal. + #[must_use] + pub fn crystal() -> Self { + const CORE_CPU_FREQ: u32 = 500_000_000; + const PLL_CLK_FREQ: u32 = 528_000_000; + const SYS_CLK_FREQ: u32 = CORE_CPU_FREQ / 2; + Self { + lposc: LposcConfig { + state: State::Enabled, + freq: AtomicU32::new(Into::into(LposcFreq::Lp1m)), + }, + sfro: SfroConfig { state: State::Enabled }, + rtc: RtcClkConfig { + state: State::Enabled, + wake_alarm_state: State::Disabled, + sub_second_state: State::Disabled, + freq: AtomicU32::new(Into::into(RtcFreq::Default1Hz)), + rtc_int: RtcInterrupts::None, + }, + ffro: FfroConfig { + state: State::Enabled, + freq: AtomicU32::new(Into::into(FfroFreq::Ffro48m)), + }, + //pll: Some(PllConfig {}),//includes aux0 and aux1 pll + clk_in: ClkInConfig { + state: State::Disabled, + // This is an externally sourced clock + // Don't give it an initial frequency + freq: Some(AtomicU32::new(0)), + }, + hclk: HclkConfig { state: State::Disabled }, + main_clk: MainClkConfig { + state: State::Enabled, + //FFRO divided by 4 is reset values of Main Clk Sel A, Sel B + src: MainClkSrc::FFRO, + div_int: AtomicU32::new(4), + freq: AtomicU32::new(CORE_CPU_FREQ), + }, + main_pll_clk: MainPllClkConfig { + state: State::Enabled, + src: MainPllClkSrc::SFRO, + freq: AtomicU32::new(PLL_CLK_FREQ), + mult: AtomicU8::new(16), + pfd0: 19, // + pfd1: 0, // future field + pfd2: 19, // 0x13 + pfd3: 0, // future field + aux0_div: 0, + aux1_div: 0, + }, + sys_clk: SysClkConfig { + sysclkfreq: AtomicU32::new(SYS_CLK_FREQ), + }, + sys_osc: SysOscConfig { state: State::Enabled }, + //adc: Some(AdcConfig {}), // TODO: add config + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Clock state enum +pub enum State { + /// Clock is enabled + Enabled, + /// Clock is disabled + Disabled, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Low Power Oscillator valid frequencies +pub enum LposcFreq { + /// 1 `MHz` oscillator + Lp1m, + /// 32kHz oscillator + Lp32k, +} + +impl From for u32 { + fn from(value: LposcFreq) -> Self { + match value { + LposcFreq::Lp1m => 1_000_000, + LposcFreq::Lp32k => 32_768, + } + } +} + +impl TryFrom for LposcFreq { + type Error = ClockError; + fn try_from(value: u32) -> Result { + match value { + 1_000_000 => Ok(LposcFreq::Lp1m), + 32_768 => Ok(LposcFreq::Lp32k), + _ => Err(ClockError::InvalidFrequency), + } + } +} + +/// Low power oscillator config +pub struct LposcConfig { + state: State, + // low power osc + freq: AtomicU32, +} + +const SFRO_FREQ: u32 = 16_000_000; +/// SFRO config +pub struct SfroConfig { + state: State, +} + +/// Valid RTC frequencies +pub enum RtcFreq { + /// "Alarm" aka 1Hz clock + Default1Hz, + /// "Wake" aka 1kHz clock + HighResolution1khz, + /// 32kHz clock + SubSecond32kHz, +} + +impl From for u32 { + fn from(value: RtcFreq) -> Self { + match value { + RtcFreq::Default1Hz => 1, + RtcFreq::HighResolution1khz => 1_000, + RtcFreq::SubSecond32kHz => 32_768, + } + } +} + +impl TryFrom for RtcFreq { + type Error = ClockError; + fn try_from(value: u32) -> Result { + match value { + 1 => Ok(RtcFreq::Default1Hz), + 1_000 => Ok(RtcFreq::HighResolution1khz), + 32_768 => Ok(RtcFreq::SubSecond32kHz), + _ => Err(ClockError::InvalidFrequency), + } + } +} + +/// RTC Interrupt options +pub enum RtcInterrupts { + /// No interrupts are set + None, + /// 1Hz RTC clock aka Alarm interrupt set + Alarm, + /// 1kHz RTC clock aka Wake interrupt set + Wake, +} + +impl From for u8 { + fn from(value: RtcInterrupts) -> Self { + match value { + RtcInterrupts::None => 0b00, + RtcInterrupts::Alarm => 0b01, + RtcInterrupts::Wake => 0b10, + } + } +} +/// RTC clock config. +pub struct RtcClkConfig { + /// 1 Hz Clock state + pub state: State, + /// 1kHz Clock state + pub wake_alarm_state: State, + /// 32kHz Clock state + pub sub_second_state: State, + /// RTC clock source. + pub freq: AtomicU32, + /// RTC Interrupt + pub rtc_int: RtcInterrupts, +} + +/// Valid FFRO Frequencies +pub enum FfroFreq { + /// 48 Mhz Internal Oscillator + Ffro48m, + /// 60 `MHz` Internal Oscillator + Ffro60m, +} + +/// FFRO Clock Config +pub struct FfroConfig { + /// FFRO Clock state + state: State, + /// FFRO Frequency + freq: AtomicU32, +} + +impl From for u32 { + fn from(value: FfroFreq) -> Self { + match value { + FfroFreq::Ffro48m => 48_000_000, + FfroFreq::Ffro60m => 60_000_000, + } + } +} + +impl TryFrom for FfroFreq { + type Error = ClockError; + fn try_from(value: u32) -> Result { + match value { + 48_000_000 => Ok(FfroFreq::Ffro48m), + 60_000_000 => Ok(FfroFreq::Ffro60m), + _ => Err(ClockError::InvalidFrequency), + } + } +} + +/// PLL clock source +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MainPllClkSrc { + /// SFRO + SFRO, + /// External Clock + ClkIn, + /// FFRO + FFRO, +} + +/// Transform from Source Clock enum to Clocks +impl From for Clocks { + fn from(value: MainPllClkSrc) -> Self { + match value { + MainPllClkSrc::SFRO => Clocks::Sfro, + MainPllClkSrc::ClkIn => Clocks::ClkIn, + MainPllClkSrc::FFRO => Clocks::Ffro, + } + } +} + +impl TryFrom for MainPllClkSrc { + type Error = ClockError; + fn try_from(value: Clocks) -> Result { + match value { + Clocks::Sfro => Ok(MainPllClkSrc::SFRO), + Clocks::Ffro => Ok(MainPllClkSrc::FFRO), + Clocks::ClkIn => Ok(MainPllClkSrc::ClkIn), + _ => Err(ClockError::ClockNotSupported), + } + } +} + +/// PLL configuration. +pub struct MainPllClkConfig { + /// Clock active state + pub state: State, + /// Main clock source. + pub src: MainPllClkSrc, + /// Main clock frequency + pub freq: AtomicU32, + //TODO: numerator and denominator not used but present in register + /// Multiplication factor. + pub mult: AtomicU8, + // the following are actually 6-bits not 8 + /// Fractional divider 0, main pll clock + pub pfd0: u8, + /// Fractional divider 1 + pub pfd1: u8, + /// Fractional divider 2 + pub pfd2: u8, + /// Fractional divider 3 + pub pfd3: u8, + // Aux dividers + /// aux divider 0 + pub aux0_div: u8, + /// aux divider 1 + pub aux1_div: u8, +} +/// External input clock config +pub struct ClkInConfig { + /// External clock input state + state: State, + /// External clock input rate + freq: Option, +} + +/// AHB clock config +pub struct HclkConfig { + /// divider to turn main clk into hclk for AHB bus + pub state: State, +} + +/// Main clock source. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MainClkSrc { + /// FFRO divided by 4 + FFROdiv4, // probably don't need since it'll be covered by div_int + /// External Clock + ClkIn, + /// Low Power Oscillator + Lposc, + /// FFRO + FFRO, + /// SFRO + SFRO, + /// Main PLL Clock + PllMain, + /// RTC 32kHz oscillator. + RTC32k, +} + +impl From for Clocks { + fn from(value: MainClkSrc) -> Self { + match value { + MainClkSrc::ClkIn => Clocks::ClkIn, + MainClkSrc::Lposc => Clocks::Lposc, + MainClkSrc::FFRO => Clocks::Ffro, + MainClkSrc::SFRO => Clocks::Sfro, + MainClkSrc::PllMain => Clocks::MainPllClk, + MainClkSrc::RTC32k => Clocks::Rtc, + MainClkSrc::FFROdiv4 => Clocks::Ffro, + } + } +} + +impl TryFrom for MainClkSrc { + type Error = ClockError; + fn try_from(value: Clocks) -> Result { + match value { + Clocks::ClkIn => Ok(MainClkSrc::ClkIn), + Clocks::Lposc => Ok(MainClkSrc::Lposc), + Clocks::Sfro => Ok(MainClkSrc::SFRO), + Clocks::MainPllClk => Ok(MainClkSrc::PllMain), + Clocks::Rtc => Ok(MainClkSrc::RTC32k), + Clocks::Ffro => Ok(MainClkSrc::FFRO), + _ => Err(ClockError::ClockNotSupported), + } + } +} + +/// Main clock config. +pub struct MainClkConfig { + /// Main clock state + pub state: State, + /// Main clock source. + pub src: MainClkSrc, + /// Main clock divider. + pub div_int: AtomicU32, + /// Clock Frequency + pub freq: AtomicU32, +} + +/// System Core Clock config, SW concept for systick +pub struct SysClkConfig { + /// keeps track of the system core clock frequency + /// future use with systick + pub sysclkfreq: AtomicU32, +} + +/// System Oscillator Config +pub struct SysOscConfig { + /// Clock State + pub state: State, +} +const SYS_OSC_DEFAULT_FREQ: u32 = 24_000_000; + +/// Clock Errors +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockError { + /// Error due to attempting to change a clock with the wrong config block + ClockMismatch, + /// Error due to attempting to modify a clock that's not yet been enabled + ClockNotEnabled, + /// Error due to attempting to set a clock source that's not a supported option + ClockNotSupported, + /// Error due to attempting to set a clock to an invalid frequency + InvalidFrequency, + /// Error due to attempting to modify a clock output with an invalid divider + InvalidDiv, + /// Error due to attempting to modify a clock output with an invalid multiplier + InvalidMult, +} + +/// Trait to configure one of the clocks +pub trait ConfigurableClock { + /// Reset the clock, will enable it + fn disable(&self) -> Result<(), ClockError>; + /// Enable the clock + fn enable_and_reset(&self) -> Result<(), ClockError>; + /// Return the clock rate (Hz) + fn get_clock_rate(&self) -> Result; + /// Set the desired clock rate (Hz) + fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError>; + /// Returns whether this clock is enabled + fn is_enabled(&self) -> bool; +} + +impl LposcConfig { + /// Initializes low-power oscillator. + fn init_lposc() { + // Enable low power oscillator + // SAFETY: unsafe needed to take pointer to Sysctl0, only happens once during init + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + sysctl0.pdruncfg0_clr().write(|w| w.lposc_pd().clr_pdruncfg0()); + + // Wait for low-power oscillator to be ready (typically 64 us) + // Busy loop seems better here than trying to shoe-in an async delay + // SAFETY: unsafe needed to take pointer to Clkctl0, needed to validate HW is ready + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + while clkctl0.lposcctl0().read().clkrdy().bit_is_clear() {} + } +} +impl ConfigurableClock for LposcConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + LposcConfig::init_lposc(); + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + // SAFETY: unsafe needed to take pointer to Sysctl0, needed to power down the LPOSC HW + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + sysctl0.pdruncfg0_set().write(|w| w.lposc_pd().set_pdruncfg0()); + // Wait until LPOSC disabled + while !sysctl0.pdruncfg0().read().lposc_pd().is_power_down() {} + Ok(()) + } + fn get_clock_rate(&self) -> Result { + Ok(self.freq.load(Ordering::Relaxed)) + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { + if let Ok(r) = >::try_into(freq) { + match r { + LposcFreq::Lp1m => { + self.freq + .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed); + Ok(()) + } + LposcFreq::Lp32k => { + self.freq + .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed); + Ok(()) + } + } + } else { + error!("failed to convert desired clock rate, {:#}, to LPOSC Freq", freq); + Err(ClockError::InvalidFrequency) + } + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +impl FfroConfig { + /// Necessary register writes to initialize the FFRO clock + pub fn init_ffro_clk() { + // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up FFRO + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + + /* Power on FFRO (48/60MHz) */ + sysctl0.pdruncfg0_clr().write(|w| w.ffro_pd().clr_pdruncfg0()); + + // SAFETY: unsafe needed to take pointer to Clkctl0, only to set proper ffro update mode + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + + clkctl0.ffroctl1().write(|w| w.update().normal_mode()); + + // No FFRO enable/disable control in CLKCTL. + // Delay enough for FFRO to be stable in case it was just powered on + delay_loop_clocks(50, 12_000_000); + } +} + +impl ConfigurableClock for FfroConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + // SAFETY: should be called once + FfroConfig::init_ffro_clk(); + // default is 48 MHz + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down FFRO + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + sysctl0.pdruncfg0_set().write(|w| w.ffro_pd().set_pdruncfg0()); + delay_loop_clocks(30, 12_000_000); + // Wait until FFRO disabled + while !sysctl0.pdruncfg0().read().ffro_pd().is_power_down() {} + Ok(()) + } + fn get_clock_rate(&self) -> Result { + trace!("getting ffro clock rate"); + Ok(self.freq.load(Ordering::Relaxed)) + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { + if let Ok(r) = >::try_into(freq) { + match r { + FfroFreq::Ffro48m => { + // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + clkctl0.ffroctl1().write(|w| w.update().update_safe_mode()); + clkctl0.ffroctl0().write(|w| w.trim_range().ffro_48mhz()); + clkctl0.ffroctl1().write(|w| w.update().normal_mode()); + + self.freq + .store(FfroFreq::Ffro48m as u32, core::sync::atomic::Ordering::Relaxed); + Ok(()) + } + FfroFreq::Ffro60m => { + // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + clkctl0.ffroctl1().write(|w| w.update().update_safe_mode()); + clkctl0.ffroctl0().write(|w| w.trim_range().ffro_60mhz()); + clkctl0.ffroctl1().write(|w| w.update().normal_mode()); + + self.freq + .store(FfroFreq::Ffro60m as u32, core::sync::atomic::Ordering::Relaxed); + Ok(()) + } + } + } else { + error!("failed to convert desired clock rate, {:#}, to FFRO Freq", freq); + Err(ClockError::InvalidFrequency) + } + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +impl ConfigurableClock for SfroConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up SFRO + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + sysctl0.pdruncfg0_clr().write(|w| w.sfro_pd().clr_pdruncfg0()); + // wait until ready + while !sysctl0.pdruncfg0().read().sfro_pd().is_enabled() {} + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down SFRO + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + sysctl0.pdruncfg0_set().write(|w| w.sfro_pd().set_pdruncfg0()); + delay_loop_clocks(30, 12_000_000); + // Wait until SFRO disabled + while !sysctl0.pdruncfg0().read().sfro_pd().is_power_down() {} + Ok(()) + } + fn get_clock_rate(&self) -> Result { + if self.state == State::Enabled { + Ok(SFRO_FREQ) + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { + if self.state == State::Enabled { + if freq == SFRO_FREQ { + trace!("Sfro frequency is already set at 16MHz"); + Ok(()) + } else { + Err(ClockError::InvalidFrequency) + } + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +/// A Clock with multiple options for clock source +pub trait MultiSourceClock { + /// Returns which clock is being used as the clock source and its rate + fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError>; + /// Sets a specific clock source and its associated rate + fn set_clock_source_and_rate( + &mut self, + clock_src_config: &mut impl ConfigurableClock, + clock_src: &Clocks, + rate: u32, + ) -> Result<(), ClockError>; +} + +impl MultiSourceClock for MainPllClkConfig { + fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> { + match clock { + Clocks::MainPllClk => { + let converted_clock = Clocks::from(self.src); + Ok((converted_clock, self.freq.load(Ordering::Relaxed))) + } + _ => Err(ClockError::ClockMismatch), + } + } + fn set_clock_source_and_rate( + &mut self, + clock_src_config: &mut impl ConfigurableClock, + clock_src: &Clocks, + rate: u32, + ) -> Result<(), ClockError> { + if let Ok(c) = >::try_into(*clock_src) { + match c { + MainPllClkSrc::ClkIn => { + self.src = MainPllClkSrc::ClkIn; + // div mult and rate don't matter since this is an external clock + self.set_clock_rate(1, 1, rate) + } + MainPllClkSrc::FFRO => { + // FFRO Clock is divided by 2 + let r = clock_src_config.get_clock_rate()?; + let base_rate = r / 2; + let m = MainPllClkConfig::calc_mult(rate, base_rate)?; + + self.src = MainPllClkSrc::FFRO; + self.set_clock_rate(2, m, rate) + } + MainPllClkSrc::SFRO => { + if !clock_src_config.is_enabled() { + error!("Can't set SFRO as source for MainPll as it's not enabled"); + return Err(ClockError::ClockNotEnabled); + } + // check if desired frequency is a valid multiple of 16m SFRO clock + let m = MainPllClkConfig::calc_mult(rate, SFRO_FREQ)?; + self.src = MainPllClkSrc::SFRO; + self.set_clock_rate(1, m, rate) + } + } + } else { + Err(ClockError::ClockNotSupported) + } + } +} + +impl ConfigurableClock for MainPllClkConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + MainPllClkConfig::init_syspll(); + + MainPllClkConfig::init_syspll_pfd0(self.pfd0); + + MainPllClkConfig::init_syspll_pfd2(self.pfd2); + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + if self.is_enabled() { + error!("Attempting to reset the Main Pll Clock, should be resetting its source"); + Err(ClockError::ClockNotSupported) + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn get_clock_rate(&self) -> Result { + if self.is_enabled() { + let (_c, rate) = self.get_clock_source_and_rate(&Clocks::MainPllClk)?; + Ok(rate) + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> { + if self.is_enabled() { + trace!("attempting to set main pll clock rate"); + // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0 + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + + // Power down pll before changes + sysctl0 + .pdruncfg0_set() + .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0()); + + let desired_freq: u64 = self.freq.load(Ordering::Relaxed).into(); + + match self.src { + c if c == MainPllClkSrc::ClkIn || c == MainPllClkSrc::FFRO || c == MainPllClkSrc::SFRO => { + let mut base_rate; + match c { + MainPllClkSrc::ClkIn => { + clkctl0.syspll0clksel().write(|w| w.sel().sysxtal_clk()); + let r = self.get_clock_rate()?; + base_rate = r; + } + MainPllClkSrc::FFRO => { + trace!("found FFRO as source, wait a bit"); + delay_loop_clocks(1000, desired_freq); + match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() { + true => base_rate = Into::into(FfroFreq::Ffro48m), + false => base_rate = Into::into(FfroFreq::Ffro60m), + } + trace!("found ffro rate to be: {:#}", base_rate); + if div == 2 { + trace!("dividing FFRO rate by 2"); + clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2()); + delay_loop_clocks(150, desired_freq); + base_rate /= 2; + } else { + return Err(ClockError::InvalidDiv); + } + } + MainPllClkSrc::SFRO => { + base_rate = SFRO_FREQ; + clkctl0.syspll0clksel().write(|w| w.sel().sfro_clk()); + } + }; + base_rate *= u32::from(mult); + trace!("calculated base rate at: {:#}", base_rate); + if base_rate != freq { + // make sure to power syspll back up before returning the error + error!("invalid frequency found, powering syspll back up before returning error. Check div and mult"); + // Clear System PLL reset + clkctl0.syspll0ctl0().write(|w| w.reset().normal()); + // Power up SYSPLL + sysctl0 + .pdruncfg0_clr() + .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0()); + return Err(ClockError::InvalidFrequency); + } + trace!("setting default num and denom"); + // SAFETY: unsafe needed to write the bits for the num and demon fields + clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) }); + clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) }); + delay_loop_clocks(30, desired_freq); + self.mult.store(mult, Ordering::Relaxed); + trace!("setting self.mult as: {:#}", mult); + match mult { + 16 => { + clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16()); + } + 17 => { + clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_17()); + } + 20 => { + clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_20()); + } + 22 => { + clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_22()); + } + 27 => { + clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_27()); + } + 33 => { + clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_33()); + } + _ => return Err(ClockError::InvalidMult), + } + trace!("clear syspll reset"); + // Clear System PLL reset + clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal()); + // Power up SYSPLL + sysctl0 + .pdruncfg0_clr() + .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0()); + + // Set System PLL HOLDRINGOFF_ENA + clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable()); + delay_loop_clocks(75, desired_freq); + + // Clear System PLL HOLDRINGOFF_ENA + clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable()); + delay_loop_clocks(15, desired_freq); + + trace!("setting new PFD0 bits"); + // gate the output and clear bits. + // SAFETY: unsafe needed to write the bits for pfd0 + clkctl0 + .syspll0pfd() + .modify(|_, w| unsafe { w.pfd0_clkgate().gated().pfd0().bits(0x0) }); + // set pfd bits and un-gate the clock output + // output is multiplied by syspll * 18/pfd0_bits + // SAFETY: unsafe needed to write the bits for pfd0 + clkctl0 + .syspll0pfd() + .modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) }); + // wait for ready bit to be set + delay_loop_clocks(50, desired_freq); + trace!("waiting for mainpll clock to be ready"); + while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {} + // clear by writing a 1 + clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit()); + + Ok(()) + } + _ => Err(ClockError::ClockNotSupported), + } + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +impl MainPllClkConfig { + /// Calculate the mult value of a desired frequency, return error if invalid + pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result { + trace!("calculating mult for {:#} / {:#}", rate, base_freq); + const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33]; + if rate > base_freq && rate % base_freq == 0 { + let mult = (rate / base_freq) as u8; + trace!("verifying that calculated mult {:#} is a valid one", mult); + if VALIDMULTS.into_iter().any(|i| i == mult) { + Ok(mult) + } else { + Err(ClockError::InvalidFrequency) + } + } else { + Err(ClockError::InvalidFrequency) + } + } + pub(self) fn init_syspll() { + // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0 + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + + // Power down SYSPLL before change fractional settings + sysctl0 + .pdruncfg0_set() + .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0()); + + clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2()); + // SAFETY: unsafe needed to write the bits for both num and denom + clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0x0) }); + clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0x1) }); + + // kCLOCK_SysPllMult22 + clkctl0.syspll0ctl0().modify(|_, w| w.mult().div_22()); + + // Clear System PLL reset + clkctl0.syspll0ctl0().modify(|_, w| w.reset().normal()); + + // Power up SYSPLL + sysctl0 + .pdruncfg0_clr() + .write(|w| w.syspllldo_pd().clr_pdruncfg0().syspllana_pd().clr_pdruncfg0()); + delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000); + + // Set System PLL HOLDRINGOFF_ENA + clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable()); + delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000); + + // Clear System PLL HOLDRINGOFF_ENA + clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable()); + delay_loop_clocks((15 & 0xFFFF) / 2, 12_000_000); + } + /// enables default settings for pfd2 bits + pub(self) fn init_syspll_pfd2(config_bits: u8) { + // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits + // needed to change the output of pfd0 + unsafe { + let clkctl0 = crate::pac::Clkctl0::steal(); + + // Disable the clock output first. + // SAFETY: unsafe needed to write the bits for pfd2 + clkctl0 + .syspll0pfd() + .modify(|_, w| w.pfd2_clkgate().gated().pfd2().bits(0x0)); + + // Set the new value and enable output. + // SAFETY: unsafe needed to write the bits for pfd2 + clkctl0 + .syspll0pfd() + .modify(|_, w| w.pfd2_clkgate().not_gated().pfd2().bits(config_bits)); + + // Wait for output becomes stable. + while clkctl0.syspll0pfd().read().pfd2_clkrdy().bit_is_clear() {} + + // Clear ready status flag. + clkctl0.syspll0pfd().modify(|_, w| w.pfd2_clkrdy().clear_bit()); + } + } + /// Enables default settings for pfd0 + pub(self) fn init_syspll_pfd0(config_bits: u8) { + // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits + // needed to change the output of pfd0 + unsafe { + let clkctl0 = crate::pac::Clkctl0::steal(); + // Disable the clock output first + clkctl0 + .syspll0pfd() + .modify(|_, w| w.pfd0_clkgate().gated().pfd0().bits(0x0)); + + // Set the new value and enable output + clkctl0 + .syspll0pfd() + .modify(|_, w| w.pfd0_clkgate().not_gated().pfd0().bits(config_bits)); + + // Wait for output becomes stable + while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {} + + // Clear ready status flag + clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().clear_bit()); + } + } +} + +impl MainClkConfig { + fn init_main_clk() { + // SAFETY:: unsafe needed to take pointers to Clkctl0 and Clkctl1 + // used to set the right HW frequency + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; + + clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk()); + + // Set PFC0DIV divider to value 2, Subtract 1 since 0-> 1, 1-> 2, etc... + clkctl0.pfcdiv(0).modify(|_, w| w.reset().set_bit()); + // SAFETY: unsafe needed to write the bits for pfcdiv + clkctl0 + .pfcdiv(0) + .write(|w| unsafe { w.div().bits(2 - 1).halt().clear_bit() }); + while clkctl0.pfcdiv(0).read().reqflag().bit_is_set() {} + + // Set FRGPLLCLKDIV divider to value 12, Subtract 1 since 0-> 1, 1-> 2, etc... + clkctl1.frgpllclkdiv().modify(|_, w| w.reset().set_bit()); + // SAFETY: unsafe needed to write the bits for frgpllclkdiv + clkctl1 + .frgpllclkdiv() + .write(|w| unsafe { w.div().bits(12 - 1).halt().clear_bit() }); + while clkctl1.frgpllclkdiv().read().reqflag().bit_is_set() {} + } +} +impl MultiSourceClock for MainClkConfig { + fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> { + match clock { + Clocks::MainClk => { + let div: u32 = if self.src == MainClkSrc::FFROdiv4 { 4 } else { 1 }; + let converted_clock = Clocks::from(self.src); + match ConfigurableClock::get_clock_rate(self) { + Ok(_rate) => { + // SAFETY: unsafe needed to take pointer to Clkctl0 + // needed to calculate the clock rate from the bits written in the registers + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + if self.src == MainClkSrc::PllMain && clkctl0.syspll0ctl0().read().bypass().is_programmed_clk() + { + let mut temp; + temp = self.freq.load(Ordering::Relaxed) + * u32::from(clkctl0.syspll0ctl0().read().mult().bits()); + temp = (u64::from(temp) * 18 / u64::from(clkctl0.syspll0pfd().read().pfd0().bits())) as u32; + return Ok((converted_clock, temp)); + } + Ok((converted_clock, self.freq.load(Ordering::Relaxed) / div)) + } + Err(clk_err) => Err(clk_err), + } + } + _ => Err(ClockError::ClockMismatch), + } + } + fn set_clock_source_and_rate( + &mut self, + clock_src_config: &mut impl ConfigurableClock, + clock_src: &Clocks, + rate: u32, + ) -> Result<(), ClockError> { + if !clock_src_config.is_enabled() { + return Err(ClockError::ClockNotEnabled); + } + if let Ok(c) = >::try_into(*clock_src) { + // SAFETY: unsafe needed to take pointer to Clkctl0 + // needed to change the clock source + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + match c { + MainClkSrc::ClkIn => { + self.src = MainClkSrc::ClkIn; + + clkctl0.mainclksela().write(|w| w.sel().sysxtal_clk()); + clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); + Ok(()) + } + // the following will yield the same result as if compared to FFROdiv4 + MainClkSrc::FFRO | MainClkSrc::FFROdiv4 => match rate { + div4 if div4 == (FfroFreq::Ffro60m as u32) / 4 || div4 == (FfroFreq::Ffro48m as u32) / 4 => { + self.src = MainClkSrc::FFROdiv4; + self.freq.store(div4, Ordering::Relaxed); + + clkctl0.mainclksela().write(|w| w.sel().ffro_div_4()); + clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); + Ok(()) + } + div1 if div1 == FfroFreq::Ffro60m as u32 || div1 == FfroFreq::Ffro48m as u32 => { + self.src = MainClkSrc::FFRO; + self.freq.store(div1, Ordering::Relaxed); + + clkctl0.mainclksela().write(|w| w.sel().ffro_clk()); + clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); + Ok(()) + } + _ => Err(ClockError::InvalidFrequency), + }, + MainClkSrc::Lposc => { + if let Ok(r) = >::try_into(rate) { + match r { + LposcFreq::Lp1m => { + self.src = MainClkSrc::Lposc; + self.freq.store(rate, Ordering::Relaxed); + + clkctl0.mainclksela().write(|w| w.sel().lposc()); + clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); + Ok(()) + } + LposcFreq::Lp32k => Err(ClockError::InvalidFrequency), + } + } else { + Err(ClockError::InvalidFrequency) + } + } + MainClkSrc::SFRO => { + if rate == SFRO_FREQ { + self.src = MainClkSrc::SFRO; + self.freq.store(rate, Ordering::Relaxed); + clkctl0.mainclkselb().write(|w| w.sel().sfro_clk()); + Ok(()) + } else { + Err(ClockError::InvalidFrequency) + } + } + MainClkSrc::PllMain => { + let r = rate; + // From Section 4.6.1.1 Pll Limitations of the RT6xx User manual + let pll_max = 572_000_000; + let pll_min = 80_000_000; + if pll_min <= r && r <= pll_max { + clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk()); + self.src = MainClkSrc::PllMain; + self.freq.store(r, Ordering::Relaxed); + Ok(()) + } else { + Err(ClockError::InvalidFrequency) + } + } + MainClkSrc::RTC32k => { + if rate == RtcFreq::SubSecond32kHz as u32 { + self.src = MainClkSrc::RTC32k; + self.freq.store(rate, Ordering::Relaxed); + clkctl0.mainclkselb().write(|w| w.sel().rtc_32k_clk()); + Ok(()) + } else { + Err(ClockError::InvalidFrequency) + } + } + } + } else { + Err(ClockError::ClockNotSupported) + } + } +} + +impl ConfigurableClock for MainClkConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + MainClkConfig::init_main_clk(); + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + error!("Attempting to reset the main clock, should NOT happen during runtime"); + Err(ClockError::ClockNotSupported) + } + fn get_clock_rate(&self) -> Result { + let (_c, rate) = MainClkConfig::get_clock_source_and_rate(self, &Clocks::MainClk)?; + Ok(rate) + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> { + error!("The multi-source set_clock_rate_and_source method should be used instead of set_clock_rate"); + Err(ClockError::ClockNotSupported) + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +impl ConfigurableClock for ClkInConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + // External Input, no hw writes needed + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + error!("Attempting to reset a clock input"); + Err(ClockError::ClockNotSupported) + } + fn get_clock_rate(&self) -> Result { + if self.freq.is_some() { + Ok(self.freq.as_ref().unwrap().load(Ordering::Relaxed)) + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { + trace!("Setting value of clk in config, this won't change the clock itself"); + self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed); + Ok(()) + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +impl RtcClkConfig { + /// Register writes to initialize the RTC Clock + fn init_rtc_clk() { + // SAFETY: unsafe needed to take pointer to Clkctl0, Clkctl1, and RTC + // needed to enable the RTC HW + let cc0 = unsafe { pac::Clkctl0::steal() }; + let cc1 = unsafe { pac::Clkctl1::steal() }; + let r = unsafe { pac::Rtc::steal() }; + // Enable the RTC peripheral clock + cc1.pscctl2_set().write(|w| w.rtc_lite_clk_set().set_clock()); + // Make sure the reset bit is cleared amd RTC OSC is powered up + r.ctrl().modify(|_, w| w.swreset().not_in_reset().rtc_osc_pd().enable()); + + // set initial match value, note that with a 15 bit count-down timer this would + // typically be 0x8000, but we are "doing some clever things" in time-driver.rs, + // read more about it in the comments there + // SAFETY: unsafe needed to write the bits + r.wake().write(|w| unsafe { w.bits(0xA) }); + + // Enable 32K OSC + cc0.osc32khzctl0().write(|w| w.ena32khz().enabled()); + + // enable rtc clk + r.ctrl().modify(|_, w| w.rtc_en().enable()); + } +} + +impl ConfigurableClock for RtcClkConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + // should only be called once if previously disabled + RtcClkConfig::init_rtc_clk(); + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + error!("Resetting the RTC clock, this should NOT happen during runtime"); + Err(ClockError::ClockNotSupported) + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { + if let Ok(r) = >::try_into(freq) { + // SAFETY: unsafe needed to take pointer to RTC + // needed to enable the HW for the different RTC frequencies, powered down by default + let rtc = unsafe { crate::pac::Rtc::steal() }; + match r { + RtcFreq::Default1Hz => { + if rtc.ctrl().read().rtc_en().is_enable() { + trace!("Attempting to enable an already enabled clock, RTC 1Hz"); + } else { + rtc.ctrl().modify(|_r, w| w.rtc_en().enable()); + } + Ok(()) + } + RtcFreq::HighResolution1khz => { + if rtc.ctrl().read().rtc1khz_en().is_enable() { + trace!("Attempting to enable an already enabled clock, RTC 1Hz"); + } else { + rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable()); + } + Ok(()) + } + RtcFreq::SubSecond32kHz => { + if rtc.ctrl().read().rtc_subsec_ena().is_enable() { + trace!("Attempting to enable an already enabled clock, RTC 1Hz"); + } else { + rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable()); + } + Ok(()) + } + } + } else { + Err(ClockError::InvalidFrequency) + } + } + // unlike the others, since this provides multiple clocks, return the fastest one + fn get_clock_rate(&self) -> Result { + if self.sub_second_state == State::Enabled { + Ok(RtcFreq::SubSecond32kHz as u32) + } else if self.wake_alarm_state == State::Enabled { + Ok(RtcFreq::HighResolution1khz as u32) + } else if self.state == State::Enabled { + Ok(RtcFreq::Default1Hz as u32) + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } +} + +impl SysClkConfig { + /// Updates the system core clock frequency, SW concept used for systick + fn update_sys_core_clock(&self) { + trace!( + "System core clock has been updated to {:?}, this involves no HW reg writes", + self.sysclkfreq.load(Ordering::Relaxed) + ); + } +} + +impl ConfigurableClock for SysOscConfig { + fn enable_and_reset(&self) -> Result<(), ClockError> { + if self.state == State::Enabled { + trace!("SysOsc was already enabled"); + return Ok(()); + } + + // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + + // Let CPU run on ffro for safe switching + clkctl0.mainclksela().write(|w| w.sel().ffro_clk()); + clkctl0.mainclksela().write(|w| w.sel().ffro_div_4()); + + // Power on SYSXTAL + sysctl0.pdruncfg0_clr().write(|w| w.sysxtal_pd().clr_pdruncfg0()); + + // Enable system OSC + clkctl0 + .sysoscctl0() + .write(|w| w.lp_enable().lp().bypass_enable().normal_mode()); + + delay_loop_clocks(260, SYS_OSC_DEFAULT_FREQ.into()); + Ok(()) + } + fn disable(&self) -> Result<(), ClockError> { + // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; + + // Let CPU run on ffro for safe switching + clkctl0.mainclksela().write(|w| w.sel().ffro_clk()); + clkctl0.mainclksela().write(|w| w.sel().ffro_div_4()); + + // Power on SYSXTAL + sysctl0.pdruncfg0_set().write(|w| w.sysxtal_pd().set_pdruncfg0()); + Ok(()) + } + fn get_clock_rate(&self) -> Result { + if self.state == State::Enabled { + Ok(SYS_OSC_DEFAULT_FREQ) + } else { + Err(ClockError::ClockNotEnabled) + } + } + fn is_enabled(&self) -> bool { + self.state == State::Enabled + } + fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> { + Err(ClockError::ClockNotSupported) + } +} + +/// Method to delay for a certain number of microseconds given a clock rate +pub fn delay_loop_clocks(usec: u64, freq_mhz: u64) { + let mut ticks = usec * freq_mhz / 1_000_000 / 4; + if ticks > u64::from(u32::MAX) { + ticks = u64::from(u32::MAX); + } + // won't panic since we check value above + cortex_m::asm::delay(ticks as u32); +} + +/// Configure the pad voltage pmc registers for all 3 vddio ranges +fn set_pad_voltage_range() { + // SAFETY: unsafe needed to take pointer to PNC as well as to write specific bits + unsafe { + let pmc = crate::pac::Pmc::steal(); + // Set up IO voltages + // all 3 ranges need to be 1.71-1.98V which is 01 + pmc.padvrange().write(|w| { + w.vddio_0range() + .bits(0b01) + .vddio_1range() + .bits(0b01) + .vddio_2range() + .bits(0b01) + }); + } +} + +/// Initialize AHB clock +fn init_syscpuahb_clk() { + // SAFETY: unsafe needed to take pointer to Clkctl0 + let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; + // SAFETY: unsafe needed to write the bits + // Set syscpuahbclkdiv to value 2, Subtract 1 since 0-> 1, 1-> 2, etc... + clkctl0.syscpuahbclkdiv().write(|w| unsafe { w.div().bits(2 - 1) }); + + while clkctl0.syscpuahbclkdiv().read().reqflag().bit_is_set() {} +} + +/// `ClockOut` config +pub struct ClockOutConfig { + src: ClkOutSrc, + div: u8, +} + +/// `ClockOut` sources +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// `ClockOut` sources +pub enum ClkOutSrc { + /// No Source, reduce power consumption + None, + /// SFRO clock + Sfro, + /// External input clock + ClkIn, + /// Low-power oscillator + Lposc, + /// FFRO clock + Ffro, + /// Main clock + MainClk, + /// Main DSP clock + DspMainClk, + /// Main Pll clock + MainPllClk, + /// `SysPll` Aux0 clock + Aux0PllClk, + /// `SysPll` DSP clock + DspPllClk, + /// `SysPll` Aux1 clock + Aux1PllClk, + /// Audio Pll clock + AudioPllClk, + /// 32 `KHz` RTC + RTC32k, +} + +/// Initialize the `ClkOutConfig` +impl ClockOutConfig { + /// Default configuration for Clock out + #[must_use] + pub fn default_config() -> Self { + Self { + src: ClkOutSrc::None, + div: 0, + } + } + + /// Enable the Clock Out output + pub fn enable_and_reset(&mut self) -> Result<(), ClockError> { + self.set_clkout_source_and_div(self.src, self.div)?; + Ok(()) + } + + /// Disable Clock Out output and select None as the source to conserve power + pub fn disable(&mut self) -> Result<(), ClockError> { + self.set_clkout_source_and_div(ClkOutSrc::None, 0)?; + Ok(()) + } + + /// Set the source of the Clock Out pin + fn set_clkout_source(&mut self, src: ClkOutSrc) -> Result<(), ClockError> { + // SAFETY: unsafe needed to take pointers to Clkctl1, needed to set source in HW + let cc1 = unsafe { pac::Clkctl1::steal() }; + match src { + ClkOutSrc::None => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().none()); + } + ClkOutSrc::Sfro => { + cc1.clkoutsel0().write(|w| w.sel().sfro_clk()); + cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); + } + ClkOutSrc::ClkIn => { + cc1.clkoutsel0().write(|w| w.sel().xtalin_clk()); + cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); + } + ClkOutSrc::Lposc => { + cc1.clkoutsel0().write(|w| w.sel().lposc()); + cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); + } + ClkOutSrc::Ffro => { + cc1.clkoutsel0().write(|w| w.sel().ffro_clk()); + cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); + } + ClkOutSrc::MainClk => { + cc1.clkoutsel0().write(|w| w.sel().main_clk()); + cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); + } + ClkOutSrc::DspMainClk => { + cc1.clkoutsel0().write(|w| w.sel().dsp_main_clk()); + cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); + } + ClkOutSrc::MainPllClk => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().main_pll_clk()); + } + ClkOutSrc::Aux0PllClk => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().syspll0_aux0_pll_clk()); + } + ClkOutSrc::DspPllClk => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().dsp_pll_clk()); + } + ClkOutSrc::AudioPllClk => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().audio_pll_clk()); + } + ClkOutSrc::Aux1PllClk => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().syspll0_aux1_pll_clk()); + } + ClkOutSrc::RTC32k => { + cc1.clkoutsel0().write(|w| w.sel().none()); + cc1.clkoutsel1().write(|w| w.sel().rtc_clk_32khz()); + } + } + self.src = src; + Ok(()) + } + /// set the clock out divider + /// note that 1 will be added to div when mapping to the divider + /// so bits(0) -> divide by 1 + /// ... + /// bits(255)-> divide by 256 + pub fn set_clkout_divider(&self, div: u8) -> Result<(), ClockError> { + // don't wait for clock to be ready if there's no source + if self.src != ClkOutSrc::None { + let cc1 = unsafe { pac::Clkctl1::steal() }; + + cc1.clkoutdiv() + .modify(|_, w| unsafe { w.div().bits(div).halt().clear_bit() }); + while cc1.clkoutdiv().read().reqflag().bit_is_set() {} + } + Ok(()) + } + /// set the source and divider for the clockout pin + pub fn set_clkout_source_and_div(&mut self, src: ClkOutSrc, div: u8) -> Result<(), ClockError> { + self.set_clkout_source(src)?; + + self.set_clkout_divider(div)?; + + Ok(()) + } +} + +/// Using the config, enables all desired clocks to desired clock rates +fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> { + if let Err(e) = config.rtc.enable_and_reset() { + error!("couldn't Power on OSC for RTC, result: {:?}", e); + return Err(e); + } + + if let Err(e) = config.lposc.enable_and_reset() { + error!("couldn't Power on LPOSC, result: {:?}", e); + return Err(e); + } + + if let Err(e) = config.ffro.enable_and_reset() { + error!("couldn't Power on FFRO, result: {:?}", e); + return Err(e); + } + + if let Err(e) = config.sfro.enable_and_reset() { + error!("couldn't Power on SFRO, result: {:?}", e); + return Err(e); + } + + if let Err(e) = config.sys_osc.enable_and_reset() { + error!("Couldn't enable sys oscillator {:?}", e); + return Err(e); + } + + if let Err(e) = config.main_pll_clk.enable_and_reset() { + error!("Couldn't enable main pll clock {:?}", e); + return Err(e); + } + + // Move FLEXSPI clock source from main clock to FFRO to avoid instruction/data fetch issue in XIP when + // updating PLL and main clock. + // SAFETY: unsafe needed to take pointers to Clkctl0 + let cc0 = unsafe { pac::Clkctl0::steal() }; + cc0.flexspifclksel().write(|w| w.sel().ffro_clk()); + + // Move ESPI clock source to FFRO + #[cfg(feature = "_espi")] + { + cc0.espiclksel().write(|w| w.sel().use_48_60m()); + } + + init_syscpuahb_clk(); + + if let Err(e) = config.main_clk.enable_and_reset() { + error!("Couldn't enable main clock {:?}", e); + return Err(e); + } + + config.sys_clk.update_sys_core_clock(); + Ok(()) +} + +/// SAFETY: must be called exactly once at bootup +pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> { + init_clock_hw(config)?; + + // set VDDIO ranges 0-2 + set_pad_voltage_range(); + Ok(()) +} + +///Trait to expose perph clocks +trait SealedSysconPeripheral { + fn enable_and_reset_perph_clock(); + fn disable_perph_clock(); +} + +/// Clock and Reset control for peripherals +#[allow(private_bounds)] +pub trait SysconPeripheral: SealedSysconPeripheral + 'static {} +/// Enables and resets peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +pub fn enable_and_reset() { + T::enable_and_reset_perph_clock(); +} + +/// Disables peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +pub fn disable() { + T::disable_perph_clock(); +} +macro_rules! impl_perph_clk { + ($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => { + impl SealedSysconPeripheral for crate::peripherals::$peripheral { + fn enable_and_reset_perph_clock() { + // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 + let cc1 = unsafe { pac::$clkctl::steal() }; + let rc1 = unsafe { pac::$rstctl::steal() }; + + paste! { + // SAFETY: unsafe due to the use of bits() + cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); + + // SAFETY: unsafe due to the use of bits() + rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); + } + } + + fn disable_perph_clock() { + // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 + let cc1 = unsafe { pac::$clkctl::steal() }; + let rc1 = unsafe { pac::$rstctl::steal() }; + + paste! { + // SAFETY: unsafe due to the use of bits() + rc1.[<$rstreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); + + // SAFETY: unsafe due to the use of bits() + cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); + } + } + } + + impl SysconPeripheral for crate::peripherals::$peripheral {} + }; +} + +// These should enabled once the relevant peripherals are implemented. +// impl_perph_clk!(GPIOINTCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 30); +// impl_perph_clk!(OTP, Clkctl0, pscctl0, Rstctl0, prstctl0, 17); + +// impl_perph_clk!(ROM_CTL_128KB, Clkctl0, pscctl0, Rstctl0, prstctl0, 2); +// impl_perph_clk!(USBHS_SRAM, Clkctl0, pscctl0, Rstctl0, prstctl0, 23); + +impl_perph_clk!(PIMCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 31); +impl_perph_clk!(ACMP, Clkctl0, pscctl1, Rstctl0, prstctl1, 15); +impl_perph_clk!(ADC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 16); +impl_perph_clk!(CASPER, Clkctl0, pscctl0, Rstctl0, prstctl0, 9); +impl_perph_clk!(CRC, Clkctl1, pscctl1, Rstctl1, prstctl1, 16); +impl_perph_clk!(CTIMER0_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 0); +impl_perph_clk!(CTIMER1_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 1); +impl_perph_clk!(CTIMER2_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 2); +impl_perph_clk!(CTIMER3_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 3); +impl_perph_clk!(CTIMER4_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 4); +impl_perph_clk!(DMA0, Clkctl1, pscctl1, Rstctl1, prstctl1, 23); +impl_perph_clk!(DMA1, Clkctl1, pscctl1, Rstctl1, prstctl1, 24); +impl_perph_clk!(DMIC0, Clkctl1, pscctl0, Rstctl1, prstctl0, 24); + +#[cfg(feature = "_espi")] +impl_perph_clk!(ESPI, Clkctl0, pscctl1, Rstctl0, prstctl1, 7); + +impl_perph_clk!(FLEXCOMM0, Clkctl1, pscctl0, Rstctl1, prstctl0, 8); +impl_perph_clk!(FLEXCOMM1, Clkctl1, pscctl0, Rstctl1, prstctl0, 9); +impl_perph_clk!(FLEXCOMM14, Clkctl1, pscctl0, Rstctl1, prstctl0, 22); +impl_perph_clk!(FLEXCOMM15, Clkctl1, pscctl0, Rstctl1, prstctl0, 23); +impl_perph_clk!(FLEXCOMM2, Clkctl1, pscctl0, Rstctl1, prstctl0, 10); +impl_perph_clk!(FLEXCOMM3, Clkctl1, pscctl0, Rstctl1, prstctl0, 11); +impl_perph_clk!(FLEXCOMM4, Clkctl1, pscctl0, Rstctl1, prstctl0, 12); +impl_perph_clk!(FLEXCOMM5, Clkctl1, pscctl0, Rstctl1, prstctl0, 13); +impl_perph_clk!(FLEXCOMM6, Clkctl1, pscctl0, Rstctl1, prstctl0, 14); +impl_perph_clk!(FLEXCOMM7, Clkctl1, pscctl0, Rstctl1, prstctl0, 15); +impl_perph_clk!(FLEXSPI, Clkctl0, pscctl0, Rstctl0, prstctl0, 16); +impl_perph_clk!(FREQME, Clkctl1, pscctl1, Rstctl1, prstctl1, 31); +impl_perph_clk!(HASHCRYPT, Clkctl0, pscctl0, Rstctl0, prstctl0, 10); +impl_perph_clk!(HSGPIO0, Clkctl1, pscctl1, Rstctl1, prstctl1, 0); +impl_perph_clk!(HSGPIO1, Clkctl1, pscctl1, Rstctl1, prstctl1, 1); +impl_perph_clk!(HSGPIO2, Clkctl1, pscctl1, Rstctl1, prstctl1, 2); +impl_perph_clk!(HSGPIO3, Clkctl1, pscctl1, Rstctl1, prstctl1, 3); +impl_perph_clk!(HSGPIO4, Clkctl1, pscctl1, Rstctl1, prstctl1, 4); +impl_perph_clk!(HSGPIO5, Clkctl1, pscctl1, Rstctl1, prstctl1, 5); +impl_perph_clk!(HSGPIO6, Clkctl1, pscctl1, Rstctl1, prstctl1, 6); +impl_perph_clk!(HSGPIO7, Clkctl1, pscctl1, Rstctl1, prstctl1, 7); +impl_perph_clk!(I3C0, Clkctl1, pscctl2, Rstctl1, prstctl2, 16); +impl_perph_clk!(MRT0, Clkctl1, pscctl2, Rstctl1, prstctl2, 8); +impl_perph_clk!(MU_A, Clkctl1, pscctl1, Rstctl1, prstctl1, 28); +impl_perph_clk!(OS_EVENT, Clkctl1, pscctl0, Rstctl1, prstctl0, 27); +impl_perph_clk!(POWERQUAD, Clkctl0, pscctl0, Rstctl0, prstctl0, 8); +impl_perph_clk!(PUF, Clkctl0, pscctl0, Rstctl0, prstctl0, 11); +impl_perph_clk!(RNG, Clkctl0, pscctl0, Rstctl0, prstctl0, 12); +impl_perph_clk!(RTC, Clkctl1, pscctl2, Rstctl1, prstctl2, 7); +impl_perph_clk!(SCT0, Clkctl0, pscctl0, Rstctl0, prstctl0, 24); +impl_perph_clk!(SECGPIO, Clkctl0, pscctl1, Rstctl0, prstctl1, 24); +impl_perph_clk!(SEMA42, Clkctl1, pscctl1, Rstctl1, prstctl1, 29); +impl_perph_clk!(USBHSD, Clkctl0, pscctl0, Rstctl0, prstctl0, 21); +impl_perph_clk!(USBHSH, Clkctl0, pscctl0, Rstctl0, prstctl0, 22); +impl_perph_clk!(USBPHY, Clkctl0, pscctl0, Rstctl0, prstctl0, 20); +impl_perph_clk!(USDHC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 2); +impl_perph_clk!(USDHC1, Clkctl0, pscctl1, Rstctl0, prstctl1, 3); +impl_perph_clk!(UTICK0, Clkctl0, pscctl2, Rstctl0, prstctl2, 0); +impl_perph_clk!(WDT0, Clkctl0, pscctl2, Rstctl0, prstctl2, 1); +impl_perph_clk!(WDT1, Clkctl1, pscctl2, Rstctl1, prstctl2, 10); diff --git a/embassy-imxrt/src/fmt.rs b/embassy-imxrt/src/fmt.rs new file mode 100644 index 000000000..c65273340 --- /dev/null +++ b/embassy-imxrt/src/fmt.rs @@ -0,0 +1,257 @@ +#![macro_use] +#![allow(unused)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[collapse_debuginfo(yes)] +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(feature = "defmt"))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(feature = "defmt"))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(feature = "defmt"))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(feature = "defmt"))] + let _ = ($( & $x ),*); + } + }; +} + +#[collapse_debuginfo(yes)] +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(feature = "defmt"))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +#[collapse_debuginfo(yes)] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +#[collapse_debuginfo(yes)] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl Debug for Bytes<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl Display for Bytes<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl LowerHex for Bytes<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Bytes<'_> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-imxrt/src/gpio.rs b/embassy-imxrt/src/gpio.rs new file mode 100644 index 000000000..6883c4ee0 --- /dev/null +++ b/embassy-imxrt/src/gpio.rs @@ -0,0 +1,1060 @@ +//! GPIO + +use core::convert::Infallible; +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin as FuturePin; +use core::task::{Context, Poll}; + +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::clocks::enable_and_reset; +use crate::iopctl::IopctlPin; +pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate}; +use crate::sealed::Sealed; +use crate::{interrupt, peripherals, Peri, PeripheralType}; + +// This should be unique per IMXRT package +const PORT_COUNT: usize = 8; + +/// Digital input or output level. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Level { + /// Logic Low + Low, + /// Logic High + High, +} + +impl From for Level { + fn from(val: bool) -> Self { + match val { + true => Self::High, + false => Self::Low, + } + } +} + +impl From for bool { + fn from(level: Level) -> bool { + match level { + Level::Low => false, + Level::High => true, + } + } +} + +/// Interrupt trigger levels. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum InterruptType { + /// Trigger on level. + Level, + /// Trigger on edge. + Edge, +} + +#[cfg(feature = "rt")] +#[interrupt] +#[allow(non_snake_case)] +fn GPIO_INTA() { + irq_handler(&GPIO_WAKERS); +} + +#[cfg(feature = "rt")] +struct BitIter(u32); + +#[cfg(feature = "rt")] +impl Iterator for BitIter { + type Item = u32; + + fn next(&mut self) -> Option { + match self.0.trailing_zeros() { + 32 => None, + b => { + self.0 &= !(1 << b); + Some(b) + } + } + } +} + +#[cfg(feature = "rt")] +fn irq_handler(port_wakers: &[Option<&PortWaker>]) { + let reg = unsafe { crate::pac::Gpio::steal() }; + + for (port, port_waker) in port_wakers.iter().enumerate() { + let Some(port_waker) = port_waker else { + continue; + }; + + let stat = reg.intstata(port).read().bits(); + for pin in BitIter(stat) { + // Clear the interrupt from this pin + reg.intstata(port).write(|w| unsafe { w.status().bits(1 << pin) }); + // Disable interrupt from this pin + reg.intena(port) + .modify(|r, w| unsafe { w.int_en().bits(r.int_en().bits() & !(1 << pin)) }); + + let Some(waker) = port_waker.get_waker(pin as usize) else { + continue; + }; + + waker.wake(); + } + } +} + +/// Initialization Logic +/// Note: GPIO port clocks are initialized in the clocks module. +pub(crate) fn init() { + // Enable GPIO clocks + enable_and_reset::(); + enable_and_reset::(); + enable_and_reset::(); + enable_and_reset::(); + enable_and_reset::(); + enable_and_reset::(); + enable_and_reset::(); + enable_and_reset::(); + + // Enable INTA + interrupt::GPIO_INTA.unpend(); + + // SAFETY: + // + // At this point, all GPIO interrupts are masked. No interrupts + // will trigger until a pin is configured as Input, which can only + // happen after initialization of the HAL + unsafe { interrupt::GPIO_INTA.enable() }; +} + +/// Input Sense mode. +pub trait Sense: Sealed {} + +/// Sense Enabled Flex pin. +/// +/// This is a true flex pin as the input buffer is enabled. +/// It can sense its own level when even when configured as an output pin. +pub enum SenseEnabled {} +impl Sealed for SenseEnabled {} +impl Sense for SenseEnabled {} + +/// Sense Enabled Flex pin. +/// +/// This is **not** a true flex pin as the input buffer is disabled. +/// It cannot be turned into an input and it cannot see its own state, but it consumes less power. +/// Consider using a sense enabled flex pin if you need to read the pin's state or turn this into an input, +/// however note that **power consumption may be increased**. +pub enum SenseDisabled {} +impl Sealed for SenseDisabled {} +impl Sense for SenseDisabled {} + +/// Flex pin. +/// +/// This pin can be either an input or output pin. The output level register bit will +/// remain set while not in output mode, so the pin's level will be 'remembered' when it is not in +/// output mode. +pub struct Flex<'d, S: Sense> { + pin: Peri<'d, AnyPin>, + _sense_mode: PhantomData, +} + +impl Flex<'_, S> { + /// Converts pin to output pin + /// + /// The pin level will be whatever was set before (or low by default). If you want it to begin + /// at a specific level, call `set_high`/`set_low` on the pin first. + pub fn set_as_output(&mut self, mode: DriveMode, strength: DriveStrength, slew_rate: SlewRate) { + self.pin + .set_pull(Pull::None) + .set_drive_mode(mode) + .set_drive_strength(strength) + .set_slew_rate(slew_rate); + + self.pin.block().dirset(self.pin.port()).write(|w| + // SAFETY: Writing a 0 to bits in this register has no effect, + // however PAC has it marked unsafe due to using the bits() method. + // There is not currently a "safe" method for setting a single-bit. + unsafe { w.dirsetp().bits(1 << self.pin.pin()) }); + } + + /// Set high + pub fn set_high(&mut self) { + self.pin.block().set(self.pin.port()).write(|w| + // SAFETY: Writing a 0 to bits in this register has no effect, + // however PAC has it marked unsafe due to using the bits() method. + // There is not currently a "safe" method for setting a single-bit. + unsafe { w.setp().bits(1 << self.pin.pin()) }); + } + + /// Set low + pub fn set_low(&mut self) { + self.pin.block().clr(self.pin.port()).write(|w| + // SAFETY: Writing a 0 to bits in this register has no effect, + // however PAC has it marked unsafe due to using the bits() method. + // There is not currently a "safe" method for setting a single-bit. + unsafe { w.clrp().bits(1 << self.pin.pin()) }); + } + + /// Set level + pub fn set_level(&mut self, level: Level) { + match level { + Level::High => self.set_high(), + Level::Low => self.set_low(), + } + } + + /// Is the output level high? + #[must_use] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + + /// Is the output level low? + #[must_use] + pub fn is_set_low(&self) -> bool { + (self.pin.block().set(self.pin.port()).read().setp().bits() & (1 << self.pin.pin())) == 0 + } + + /// Toggle + pub fn toggle(&mut self) { + self.pin.block().not(self.pin.port()).write(|w| + // SAFETY: Writing a 0 to bits in this register has no effect, + // however PAC has it marked unsafe due to using the bits() method. + // There is not currently a "safe" method for setting a single-bit. + unsafe { w.notp().bits(1 << self.pin.pin()) }); + } +} + +impl Drop for Flex<'_, S> { + #[inline] + fn drop(&mut self) { + critical_section::with(|_| { + self.pin.reset(); + }); + } +} + +impl<'d> Flex<'d, SenseEnabled> { + /// New flex pin. + pub fn new_with_sense(pin: Peri<'d, impl GpioPin>) -> Self { + pin.set_function(Function::F0) + .disable_analog_multiplex() + .enable_input_buffer(); + + Self { + pin: pin.into(), + _sense_mode: PhantomData::, + } + } + + /// Converts pin to input pin + pub fn set_as_input(&mut self, pull: Pull, inverter: Inverter) { + self.pin.set_pull(pull).set_input_inverter(inverter); + + self.pin.block().dirclr(self.pin.port()).write(|w| + // SAFETY: Writing a 0 to bits in this register has no effect, + // however PAC has it marked unsafe due to using the bits() method. + // There is not currently a "safe" method for setting a single-bit. + unsafe { w.dirclrp().bits(1 << self.pin.pin()) }); + } + + /// Converts pin to special function pin + /// # Safety + /// Unsafe to require justifying change from default to a special function + /// + pub unsafe fn set_as_special_function(&mut self, func: Function) { + self.pin.set_function(func); + } + + /// Is high? + #[must_use] + pub fn is_high(&self) -> bool { + !self.is_low() + } + + /// Is low? + #[must_use] + pub fn is_low(&self) -> bool { + self.pin.block().b(self.pin.port()).b_(self.pin.pin()).read() == 0 + } + + /// Current level + #[must_use] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + pub async fn wait_for_high(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptType::Level, Level::High).await; + } + + /// Wait until the pin is low. If it is already low, return immediately. + #[inline] + pub async fn wait_for_low(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptType::Level, Level::Low).await; + } + + /// Wait for the pin to undergo a transition from low to high. + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::High).await; + } + + /// Wait for the pin to undergo a transition from high to low. + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::Low).await; + } + + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + if self.is_high() { + InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::Low).await; + } else { + InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::High).await; + } + } + + /// Return a new Flex pin instance with level sensing disabled. + /// + /// Consumes less power than a flex pin with sensing enabled. + #[must_use] + pub fn disable_sensing(self) -> Flex<'d, SenseDisabled> { + // Cloning the pin is ok since we consume self immediately + let new_pin = unsafe { self.pin.clone_unchecked() }; + drop(self); + Flex::::new(new_pin) + } +} + +impl<'d> Flex<'d, SenseDisabled> { + /// New flex pin. + pub fn new(pin: Peri<'d, impl GpioPin>) -> Self { + pin.set_function(Function::F0) + .disable_analog_multiplex() + .disable_input_buffer(); + + Self { + pin: pin.into(), + _sense_mode: PhantomData::, + } + } + + /// Return a new Flex pin instance with level sensing enabled. + #[must_use] + pub fn enable_sensing(self) -> Flex<'d, SenseEnabled> { + // Cloning the pin is ok since we consume self immediately + let new_pin = unsafe { self.pin.clone_unchecked() }; + drop(self); + Flex::new_with_sense(new_pin) + } +} + +/// Input pin +pub struct Input<'d> { + // When Input is dropped, Flex's drop() will make sure the pin is reset to its default state. + pin: Flex<'d, SenseEnabled>, +} + +impl<'d> Input<'d> { + /// New input pin + pub fn new(pin: Peri<'d, impl GpioPin>, pull: Pull, inverter: Inverter) -> Self { + let mut pin = Flex::new_with_sense(pin); + pin.set_as_input(pull, inverter); + Self { pin } + } + + /// Is high? + #[must_use] + pub fn is_high(&self) -> bool { + self.pin.is_high() + } + + /// Is low? + #[must_use] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } + + /// Input level + #[must_use] + pub fn get_level(&self) -> Level { + self.pin.get_level() + } + + /// Wait until the pin is high. If it is already high, return immediately. + #[inline] + pub async fn wait_for_high(&mut self) { + self.pin.wait_for_high().await; + } + + /// Wait until the pin is low. If it is already low, return immediately. + #[inline] + pub async fn wait_for_low(&mut self) { + self.pin.wait_for_low().await; + } + + /// Wait for the pin to undergo a transition from low to high. + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + self.pin.wait_for_rising_edge().await; + } + + /// Wait for the pin to undergo a transition from high to low. + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + self.pin.wait_for_falling_edge().await; + } + + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await; + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct InputFuture<'d> { + pin: Peri<'d, AnyPin>, +} + +impl<'d> InputFuture<'d> { + fn new(pin: Peri<'d, impl GpioPin>, int_type: InterruptType, level: Level) -> Self { + critical_section::with(|_| { + // Clear any existing pending interrupt on this pin + pin.block() + .intstata(pin.port()) + .write(|w| unsafe { w.status().bits(1 << pin.pin()) }); + + /* Pin interrupt configuration */ + pin.block().intedg(pin.port()).modify(|r, w| match int_type { + InterruptType::Edge => unsafe { w.bits(r.bits() | (1 << pin.pin())) }, + InterruptType::Level => unsafe { w.bits(r.bits() & !(1 << pin.pin())) }, + }); + + pin.block().intpol(pin.port()).modify(|r, w| match level { + Level::High => unsafe { w.bits(r.bits() & !(1 << pin.pin())) }, + Level::Low => unsafe { w.bits(r.bits() | (1 << pin.pin())) }, + }); + + // Enable pin interrupt on GPIO INT A + pin.block() + .intena(pin.port()) + .modify(|r, w| unsafe { w.int_en().bits(r.int_en().bits() | (1 << pin.pin())) }); + }); + + Self { pin: pin.into() } + } +} + +impl Future for InputFuture<'_> { + type Output = (); + + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // We need to register/re-register the waker for each poll because any + // calls to wake will deregister the waker. + if self.pin.port() >= GPIO_WAKERS.len() { + panic!("Invalid GPIO port index {}", self.pin.port()); + } + + let port_waker = GPIO_WAKERS[self.pin.port()]; + if port_waker.is_none() { + panic!("Waker not present for GPIO port {}", self.pin.port()); + } + + let waker = port_waker.unwrap().get_waker(self.pin.pin()); + if waker.is_none() { + panic!( + "Waker not present for GPIO pin {}, port {}", + self.pin.pin(), + self.pin.port() + ); + } + waker.unwrap().register(cx.waker()); + + // Double check that the pin interrut has been disabled by IRQ handler + if self.pin.block().intena(self.pin.port()).read().bits() & (1 << self.pin.pin()) == 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + } +} + +/// Output pin +/// Cannot be set as an input and cannot read its own pin state! +/// Consider using a Flex pin if you want that functionality, at the cost of higher power consumption. +pub struct Output<'d> { + // When Output is dropped, Flex's drop() will make sure the pin is reset to its default state. + pin: Flex<'d, SenseDisabled>, +} + +impl<'d> Output<'d> { + /// New output pin + pub fn new( + pin: Peri<'d, impl GpioPin>, + initial_output: Level, + mode: DriveMode, + strength: DriveStrength, + slew_rate: SlewRate, + ) -> Self { + let mut pin = Flex::new(pin); + pin.set_level(initial_output); + pin.set_as_output(mode, strength, slew_rate); + + Self { pin } + } + + /// Set high + pub fn set_high(&mut self) { + self.pin.set_high(); + } + + /// Set low + pub fn set_low(&mut self) { + self.pin.set_low(); + } + + /// Toggle + pub fn toggle(&mut self) { + self.pin.toggle(); + } + + /// Set level + pub fn set_level(&mut self, level: Level) { + self.pin.set_level(level); + } + + /// Is set high? + #[must_use] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Is set low? + #[must_use] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() + } +} + +trait SealedPin: IopctlPin { + fn pin_port(&self) -> usize; + + fn port(&self) -> usize { + self.pin_port() / 32 + } + + fn pin(&self) -> usize { + self.pin_port() % 32 + } + + fn block(&self) -> crate::pac::Gpio { + // SAFETY: Assuming GPIO pin specific registers are only accessed through this HAL, + // this is safe because the HAL ensures ownership or exclusive mutable references + // to pins. + unsafe { crate::pac::Gpio::steal() } + } +} + +/// GPIO pin trait. +#[allow(private_bounds)] +pub trait GpioPin: SealedPin + Sized + PeripheralType + Into + 'static { + /// Type-erase the pin. + fn degrade(self) -> AnyPin { + // SAFETY: This is only called within the GpioPin trait, which is only + // implemented within this module on valid pin peripherals and thus + // has been verified to be correct. + unsafe { AnyPin::steal(self.port() as u8, self.pin() as u8) } + } +} + +impl SealedPin for AnyPin { + fn pin_port(&self) -> usize { + self.pin_port() + } +} +impl GpioPin for AnyPin {} + +macro_rules! impl_pin { + ($pin_periph:ident, $pin_port:expr, $pin_no:expr) => { + impl SealedPin for crate::peripherals::$pin_periph { + fn pin_port(&self) -> usize { + $pin_port * 32 + $pin_no + } + } + impl GpioPin for crate::peripherals::$pin_periph {} + impl From for AnyPin { + fn from(value: crate::peripherals::$pin_periph) -> Self { + value.degrade() + } + } + }; +} + +/// Container for pin wakers +struct PortWaker { + offset: usize, + wakers: &'static [AtomicWaker], +} + +impl PortWaker { + fn get_waker(&self, pin: usize) -> Option<&AtomicWaker> { + self.wakers.get(pin - self.offset) + } +} + +macro_rules! define_port_waker { + ($name:ident, $start:expr, $end:expr) => { + mod $name { + static PIN_WAKERS: [super::AtomicWaker; $end - $start + 1] = + [const { super::AtomicWaker::new() }; $end - $start + 1]; + pub static WAKER: super::PortWaker = super::PortWaker { + offset: $start, + wakers: &PIN_WAKERS, + }; + } + }; +} + +// GPIO port 0 +define_port_waker!(port0_waker, 0, 31); +impl_pin!(PIO0_0, 0, 0); +impl_pin!(PIO0_1, 0, 1); +impl_pin!(PIO0_2, 0, 2); +impl_pin!(PIO0_3, 0, 3); +impl_pin!(PIO0_4, 0, 4); +impl_pin!(PIO0_5, 0, 5); +impl_pin!(PIO0_6, 0, 6); +impl_pin!(PIO0_7, 0, 7); +impl_pin!(PIO0_8, 0, 8); +impl_pin!(PIO0_9, 0, 9); +impl_pin!(PIO0_10, 0, 10); +impl_pin!(PIO0_11, 0, 11); +impl_pin!(PIO0_12, 0, 12); +impl_pin!(PIO0_13, 0, 13); +impl_pin!(PIO0_14, 0, 14); +impl_pin!(PIO0_15, 0, 15); +impl_pin!(PIO0_16, 0, 16); +impl_pin!(PIO0_17, 0, 17); +impl_pin!(PIO0_18, 0, 18); +impl_pin!(PIO0_19, 0, 19); +impl_pin!(PIO0_20, 0, 20); +impl_pin!(PIO0_21, 0, 21); +impl_pin!(PIO0_22, 0, 22); +impl_pin!(PIO0_23, 0, 23); +impl_pin!(PIO0_24, 0, 24); +impl_pin!(PIO0_25, 0, 25); +impl_pin!(PIO0_26, 0, 26); +impl_pin!(PIO0_27, 0, 27); +impl_pin!(PIO0_28, 0, 28); +impl_pin!(PIO0_29, 0, 29); +impl_pin!(PIO0_30, 0, 30); +impl_pin!(PIO0_31, 0, 31); + +// GPIO port 1 +define_port_waker!(port1_waker, 0, 31); +impl_pin!(PIO1_0, 1, 0); +impl_pin!(PIO1_1, 1, 1); +impl_pin!(PIO1_2, 1, 2); +impl_pin!(PIO1_3, 1, 3); +impl_pin!(PIO1_4, 1, 4); +impl_pin!(PIO1_5, 1, 5); +impl_pin!(PIO1_6, 1, 6); +impl_pin!(PIO1_7, 1, 7); +impl_pin!(PIO1_8, 1, 8); +impl_pin!(PIO1_9, 1, 9); +impl_pin!(PIO1_10, 1, 10); +impl_pin!(PIO1_11, 1, 11); +impl_pin!(PIO1_12, 1, 12); +impl_pin!(PIO1_13, 1, 13); +impl_pin!(PIO1_14, 1, 14); +impl_pin!(PIO1_15, 1, 15); +impl_pin!(PIO1_16, 1, 16); +impl_pin!(PIO1_17, 1, 17); +impl_pin!(PIO1_18, 1, 18); +impl_pin!(PIO1_19, 1, 19); +impl_pin!(PIO1_20, 1, 20); +impl_pin!(PIO1_21, 1, 21); +impl_pin!(PIO1_22, 1, 22); +impl_pin!(PIO1_23, 1, 23); +impl_pin!(PIO1_24, 1, 24); +impl_pin!(PIO1_25, 1, 25); +impl_pin!(PIO1_26, 1, 26); +impl_pin!(PIO1_27, 1, 27); +impl_pin!(PIO1_28, 1, 28); +impl_pin!(PIO1_29, 1, 29); +impl_pin!(PIO1_30, 1, 30); +impl_pin!(PIO1_31, 1, 31); + +// GPIO port 2 +define_port_waker!(port2_waker, 0, 31); +impl_pin!(PIO2_0, 2, 0); +impl_pin!(PIO2_1, 2, 1); +impl_pin!(PIO2_2, 2, 2); +impl_pin!(PIO2_3, 2, 3); +impl_pin!(PIO2_4, 2, 4); +impl_pin!(PIO2_5, 2, 5); +impl_pin!(PIO2_6, 2, 6); +impl_pin!(PIO2_7, 2, 7); +impl_pin!(PIO2_8, 2, 8); +impl_pin!(PIO2_9, 2, 9); +impl_pin!(PIO2_10, 2, 10); +impl_pin!(PIO2_11, 2, 11); +impl_pin!(PIO2_12, 2, 12); +impl_pin!(PIO2_13, 2, 13); +impl_pin!(PIO2_14, 2, 14); +impl_pin!(PIO2_15, 2, 15); +impl_pin!(PIO2_16, 2, 16); +impl_pin!(PIO2_17, 2, 17); +impl_pin!(PIO2_18, 2, 18); +impl_pin!(PIO2_19, 2, 19); +impl_pin!(PIO2_20, 2, 20); +impl_pin!(PIO2_21, 2, 21); +impl_pin!(PIO2_22, 2, 22); +impl_pin!(PIO2_23, 2, 23); +impl_pin!(PIO2_24, 2, 24); +impl_pin!(PIO2_25, 2, 25); +impl_pin!(PIO2_26, 2, 26); +impl_pin!(PIO2_27, 2, 27); +impl_pin!(PIO2_28, 2, 28); +impl_pin!(PIO2_29, 2, 29); +impl_pin!(PIO2_30, 2, 30); +impl_pin!(PIO2_31, 2, 31); + +// GPIO port 3 +define_port_waker!(port3_waker, 0, 31); +impl_pin!(PIO3_0, 3, 0); +impl_pin!(PIO3_1, 3, 1); +impl_pin!(PIO3_2, 3, 2); +impl_pin!(PIO3_3, 3, 3); +impl_pin!(PIO3_4, 3, 4); +impl_pin!(PIO3_5, 3, 5); +impl_pin!(PIO3_6, 3, 6); +impl_pin!(PIO3_7, 3, 7); +impl_pin!(PIO3_8, 3, 8); +impl_pin!(PIO3_9, 3, 9); +impl_pin!(PIO3_10, 3, 10); +impl_pin!(PIO3_11, 3, 11); +impl_pin!(PIO3_12, 3, 12); +impl_pin!(PIO3_13, 3, 13); +impl_pin!(PIO3_14, 3, 14); +impl_pin!(PIO3_15, 3, 15); +impl_pin!(PIO3_16, 3, 16); +impl_pin!(PIO3_17, 3, 17); +impl_pin!(PIO3_18, 3, 18); +impl_pin!(PIO3_19, 3, 19); +impl_pin!(PIO3_20, 3, 20); +impl_pin!(PIO3_21, 3, 21); +impl_pin!(PIO3_22, 3, 22); +impl_pin!(PIO3_23, 3, 23); +impl_pin!(PIO3_24, 3, 24); +impl_pin!(PIO3_25, 3, 25); +impl_pin!(PIO3_26, 3, 26); +impl_pin!(PIO3_27, 3, 27); +impl_pin!(PIO3_28, 3, 28); +impl_pin!(PIO3_29, 3, 29); +impl_pin!(PIO3_30, 3, 30); +impl_pin!(PIO3_31, 3, 31); + +// GPIO port 4 +define_port_waker!(port4_waker, 0, 10); +impl_pin!(PIO4_0, 4, 0); +impl_pin!(PIO4_1, 4, 1); +impl_pin!(PIO4_2, 4, 2); +impl_pin!(PIO4_3, 4, 3); +impl_pin!(PIO4_4, 4, 4); +impl_pin!(PIO4_5, 4, 5); +impl_pin!(PIO4_6, 4, 6); +impl_pin!(PIO4_7, 4, 7); +impl_pin!(PIO4_8, 4, 8); +impl_pin!(PIO4_9, 4, 9); +impl_pin!(PIO4_10, 4, 10); + +// GPIO port 7 +define_port_waker!(port7_waker, 24, 31); +impl_pin!(PIO7_24, 7, 24); +impl_pin!(PIO7_25, 7, 25); +impl_pin!(PIO7_26, 7, 26); +impl_pin!(PIO7_27, 7, 27); +impl_pin!(PIO7_28, 7, 28); +impl_pin!(PIO7_29, 7, 29); +impl_pin!(PIO7_30, 7, 30); +impl_pin!(PIO7_31, 7, 31); + +static GPIO_WAKERS: [Option<&PortWaker>; PORT_COUNT] = [ + Some(&port0_waker::WAKER), + Some(&port1_waker::WAKER), + Some(&port2_waker::WAKER), + Some(&port3_waker::WAKER), + Some(&port4_waker::WAKER), + None, + None, + Some(&port7_waker::WAKER), +]; + +impl embedded_hal_02::digital::v2::InputPin for Flex<'_, SenseEnabled> { + type Error = Infallible; + + #[inline] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +impl embedded_hal_02::digital::v2::OutputPin for Flex<'_, S> { + type Error = Infallible; + + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_, SenseEnabled> { + #[inline] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_, S> { + type Error = Infallible; + + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl embedded_hal_02::digital::v2::InputPin for Input<'_> { + type Error = Infallible; + + #[inline] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +impl embedded_hal_02::digital::v2::OutputPin for Output<'_> { + type Error = Infallible; + + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl embedded_hal_02::digital::v2::StatefulOutputPin for Output<'_> { + #[inline] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'_> { + type Error = Infallible; + + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl embedded_hal_1::digital::ErrorType for Flex<'_, S> { + type Error = Infallible; +} + +impl embedded_hal_1::digital::InputPin for Flex<'_, SenseEnabled> { + #[inline] + fn is_high(&mut self) -> Result { + // Dereference of self is used here and a few other places to + // access the correct method (since different types/traits + // share method names) + Ok((*self).is_high()) + } + + #[inline] + fn is_low(&mut self) -> Result { + Ok((*self).is_low()) + } +} + +impl embedded_hal_1::digital::OutputPin for Flex<'_, S> { + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl embedded_hal_1::digital::StatefulOutputPin for Flex<'_, SenseEnabled> { + #[inline] + fn is_set_high(&mut self) -> Result { + Ok((*self).is_set_high()) + } + + #[inline] + fn is_set_low(&mut self) -> Result { + Ok((*self).is_set_low()) + } +} + +impl<'d> embedded_hal_async::digital::Wait for Flex<'d, SenseEnabled> { + #[inline] + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) + } + + #[inline] + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) + } + + #[inline] + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) + } + + #[inline] + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) + } + + #[inline] + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) + } +} + +impl embedded_hal_1::digital::ErrorType for Input<'_> { + type Error = Infallible; +} + +impl embedded_hal_1::digital::InputPin for Input<'_> { + #[inline] + fn is_high(&mut self) -> Result { + Ok((*self).is_high()) + } + + #[inline] + fn is_low(&mut self) -> Result { + Ok((*self).is_low()) + } +} + +impl<'d> embedded_hal_async::digital::Wait for Input<'d> { + #[inline] + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) + } + + #[inline] + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) + } + + #[inline] + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) + } + + #[inline] + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) + } + + #[inline] + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) + } +} + +impl embedded_hal_1::digital::ErrorType for Output<'_> { + type Error = Infallible; +} + +impl embedded_hal_1::digital::OutputPin for Output<'_> { + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl embedded_hal_1::digital::StatefulOutputPin for Output<'_> { + #[inline] + fn is_set_high(&mut self) -> Result { + Ok((*self).is_set_high()) + } + + #[inline] + fn is_set_low(&mut self) -> Result { + Ok((*self).is_set_low()) + } +} diff --git a/embassy-imxrt/src/iopctl.rs b/embassy-imxrt/src/iopctl.rs new file mode 100644 index 000000000..a3b8b14d6 --- /dev/null +++ b/embassy-imxrt/src/iopctl.rs @@ -0,0 +1,717 @@ +//! IO Pad Controller (IOPCTL) +//! +//! Also known as IO Pin Configuration (IOCON) + +use crate::pac::{iopctl, Iopctl}; + +// A generic pin of any type. +// +// The actual pin type used here is arbitrary, +// as all PioM_N types provide the same methods. +// +// Merely need some pin type to cast a raw pointer +// to in order to access the provided methods. +#[allow(non_camel_case_types)] +type PioM_N = iopctl::Pio0_0; + +/// Pin function number. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Function { + /// Function 0 + F0, + /// Function 1 + F1, + /// Function 2 + F2, + /// Function 3 + F3, + /// Function 4 + F4, + /// Function 5 + F5, + /// Function 6 + F6, + /// Function 7 + F7, + /// Function 8 + F8, +} + +/// Internal pull-up/down resistors on a pin. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pull { + /// No pull-up or pull-down resistor selected + None, + /// Pull-up resistor + Up, + /// Pull-down resistor + Down, +} + +/// Pin slew rate. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SlewRate { + /// Standard slew rate + Standard, + /// Slow slew rate + Slow, +} + +/// Output drive strength of a pin. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum DriveStrength { + /// Normal + Normal, + /// Full + Full, +} + +/// Output drive mode of a pin. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum DriveMode { + /// Push-Pull + PushPull, + /// Pseudo Open-Drain + OpenDrain, +} + +/// Input inverter of a pin. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Inverter { + /// No inverter + Disabled, + /// Enable input inverter on the input port. A low signal will be + /// seen as a high signal by the pin. + Enabled, +} + +trait SealedPin {} +trait ToAnyPin: SealedPin { + #[inline] + fn to_raw(port: u8, pin: u8) -> AnyPin { + // SAFETY: This is safe since this is only called from within the module, + // where the port and pin numbers have been verified to be correct. + unsafe { AnyPin::steal(port, pin) } + } +} + +trait ToFC15Pin: SealedPin { + #[inline] + fn to_raw(pin: u8) -> FC15Pin { + // SAFETY: This is safe since this is only called from within the module, + // where the port and pin numbers have been verified to be correct. + unsafe { FC15Pin::steal(pin) } + } +} + +/// A pin that can be configured via iopctl. +#[allow(private_bounds)] +pub trait IopctlPin: SealedPin { + /// Sets the function number of a pin. + /// + /// This number corresponds to a specific function that the pin supports. + /// + /// Typically, function 0 corresponds to GPIO while other numbers correspond to a special function. + /// + /// See Section 7.5.3 in reference manual for list of pins and their supported functions. + fn set_function(&self, function: Function) -> &Self; + + /// Enables either a pull-up or pull-down resistor on a pin. + /// + /// Setting this to [`Pull::None`] will disable the resistor. + fn set_pull(&self, pull: Pull) -> &Self; + + /// Enables the input buffer of a pin. + /// + /// This must be enabled for any pin acting as an input, + /// and some peripheral pins acting as output may need this enabled as well. + /// + /// If there is any doubt, it is best to enable the input buffer. + /// + /// See Section 7.4.2.3 of reference manual. + fn enable_input_buffer(&self) -> &Self; + + /// Disables the input buffer of a pin. + fn disable_input_buffer(&self) -> &Self; + + /// Sets the slew rate of a pin. + /// + /// This controls the speed at which a pin can toggle, + /// which is voltage and load dependent. + fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self; + + /// Sets the output drive strength of a pin. + /// + /// A drive strength of [`DriveStrength::Full`] has twice the + /// high and low drive capability of the [`DriveStrength::Normal`] setting. + fn set_drive_strength(&self, strength: DriveStrength) -> &Self; + + /// Enables the analog multiplexer of a pin. + /// + /// This must be called to allow analog functionalities of a pin. + /// + /// To protect the analog input, [`IopctlPin::set_function`] should be + /// called with [`Function::F0`] to disable digital functions. + /// + /// Additionally, [`IopctlPin::disable_input_buffer`] and [`IopctlPin::set_pull`] + /// with [`Pull::None`] should be called. + fn enable_analog_multiplex(&self) -> &Self; + + /// Disables the analog multiplexer of a pin. + fn disable_analog_multiplex(&self) -> &Self; + + /// Sets the ouput drive mode of a pin. + /// + /// A pin configured as [`DriveMode::OpenDrain`] actually operates in + /// a "pseudo" open-drain mode which is somewhat different than true open-drain. + /// + /// See Section 7.4.2.7 of reference manual. + fn set_drive_mode(&self, mode: DriveMode) -> &Self; + + /// Sets the input inverter of an input pin. + /// + /// Setting this to [`Inverter::Enabled`] will invert + /// the input signal. + fn set_input_inverter(&self, inverter: Inverter) -> &Self; + + /// Returns a pin to its reset state. + fn reset(&self) -> &Self; +} + +/// Represents a pin peripheral created at run-time from given port and pin numbers. +pub struct AnyPin { + pin_port: u8, + reg: &'static PioM_N, +} + +impl AnyPin { + /// Creates a pin from raw port and pin numbers which can then be configured. + /// + /// This should ONLY be called when there is no other choice + /// (e.g. from a type-erased GPIO pin). + /// + /// Otherwise, pin peripherals should be configured directly. + /// + /// # Safety + /// + /// The caller MUST ensure valid port and pin numbers are provided, + /// and that multiple instances of [`AnyPin`] with the same port + /// and pin combination are not being used simultaneously. + /// + /// Failure to uphold these requirements will result in undefined behavior. + /// + /// See Table 297 in reference manual for a list of valid + /// pin and port number combinations. + #[must_use] + pub unsafe fn steal(port: u8, pin: u8) -> Self { + // Calculates the offset from the beginning of the IOPCTL register block + // address to the register address representing the pin. + // + // See Table 297 in reference manual for how this offset is calculated. + let offset = ((port as usize) << 7) + ((pin as usize) << 2); + + // SAFETY: This is safe assuming the caller of this function satisfies the safety requirements above. + let reg = unsafe { &*Iopctl::ptr().byte_offset(offset as isize).cast() }; + Self { + pin_port: port * 32 + pin, + reg, + } + } + + /// Returns the pin's port and pin combination. + #[must_use] + pub fn pin_port(&self) -> usize { + self.pin_port as usize + } +} + +/// Represents a FC15 pin peripheral created at run-time from given pin number. +pub struct FC15Pin { + reg: &'static PioM_N, +} + +impl FC15Pin { + /// Creates an FC15 pin from raw pin number which can then be configured. + /// + /// This should ONLY be called when there is no other choice + /// (e.g. from a type-erased GPIO pin). + /// + /// Otherwise, pin peripherals should be configured directly. + /// + /// # Safety + /// + /// The caller MUST ensure valid port and pin numbers are provided, + /// and that multiple instances of [`AnyPin`] with the same port + /// and pin combination are not being used simultaneously. + /// + /// Failure to uphold these requirements will result in undefined behavior. + /// + /// See Table 297 in reference manual for a list of valid + /// pin and port number combinations. + #[must_use] + pub unsafe fn steal(pin: u8) -> Self { + // Table 297: FC15_I2C_SCL offset = 0x400, FC15_I2C_SCL offset = 0x404 + let iopctl = unsafe { crate::pac::Iopctl::steal() }; + + let reg = if pin == 0 { + &*iopctl.fc15_i2c_scl().as_ptr().cast() + } else { + &*iopctl.fc15_i2c_sda().as_ptr().cast() + }; + + Self { reg } + } +} + +// This allows AnyPin/FC15Pin to be used in HAL constructors that require types +// which impl Peripheral. Used primarily by GPIO HAL to convert type-erased +// GPIO pins back into an Output or Input pin specifically. +embassy_hal_internal::impl_peripheral!(AnyPin); + +impl SealedPin for AnyPin {} + +embassy_hal_internal::impl_peripheral!(FC15Pin); + +impl SealedPin for FC15Pin {} + +macro_rules! impl_iopctlpin { + ($pintype:ident) => { + impl IopctlPin for $pintype { + fn set_function(&self, function: Function) -> &Self { + critical_section::with(|_| match function { + Function::F0 => { + self.reg.modify(|_, w| w.fsel().function_0()); + } + Function::F1 => { + self.reg.modify(|_, w| w.fsel().function_1()); + } + Function::F2 => { + self.reg.modify(|_, w| w.fsel().function_2()); + } + Function::F3 => { + self.reg.modify(|_, w| w.fsel().function_3()); + } + Function::F4 => { + self.reg.modify(|_, w| w.fsel().function_4()); + } + Function::F5 => { + self.reg.modify(|_, w| w.fsel().function_5()); + } + Function::F6 => { + self.reg.modify(|_, w| w.fsel().function_6()); + } + Function::F7 => { + self.reg.modify(|_, w| w.fsel().function_7()); + } + Function::F8 => { + self.reg.modify(|_, w| w.fsel().function_8()); + } + }); + self + } + + fn set_pull(&self, pull: Pull) -> &Self { + critical_section::with(|_| { + match pull { + Pull::None => { + self.reg.modify(|_, w| w.pupdena().disabled()); + } + Pull::Up => { + self.reg.modify(|_, w| w.pupdena().enabled().pupdsel().pull_up()); + } + Pull::Down => { + self.reg + .modify(|_, w| w.pupdena().enabled().pupdsel().pull_down()); + } + } + self + }) + } + + fn enable_input_buffer(&self) -> &Self { + critical_section::with(|_| self.reg.modify(|_, w| w.ibena().enabled())); + self + } + + fn disable_input_buffer(&self) -> &Self { + critical_section::with(|_| self.reg.modify(|_, w| w.ibena().disabled())); + self + } + + fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self { + critical_section::with(|_| match slew_rate { + SlewRate::Standard => { + self.reg.modify(|_, w| w.slewrate().normal()); + } + SlewRate::Slow => { + self.reg.modify(|_, w| w.slewrate().slow()); + } + }); + self + } + + fn set_drive_strength(&self, strength: DriveStrength) -> &Self { + critical_section::with(|_| match strength { + DriveStrength::Normal => { + self.reg.modify(|_, w| w.fulldrive().normal_drive()); + } + DriveStrength::Full => { + self.reg.modify(|_, w| w.fulldrive().full_drive()); + } + }); + self + } + + fn enable_analog_multiplex(&self) -> &Self { + critical_section::with(|_| self.reg.modify(|_, w| w.amena().enabled())); + self + } + + fn disable_analog_multiplex(&self) -> &Self { + critical_section::with(|_| self.reg.modify(|_, w| w.amena().disabled())); + self + } + + fn set_drive_mode(&self, mode: DriveMode) -> &Self { + critical_section::with(|_| match mode { + DriveMode::PushPull => { + self.reg.modify(|_, w| w.odena().disabled()); + } + DriveMode::OpenDrain => { + self.reg.modify(|_, w| w.odena().enabled()); + } + }); + self + } + + fn set_input_inverter(&self, inverter: Inverter) -> &Self { + critical_section::with(|_| match inverter { + Inverter::Disabled => { + self.reg.modify(|_, w| w.iiena().disabled()); + } + Inverter::Enabled => { + self.reg.modify(|_, w| w.iiena().enabled()); + } + }); + self + } + + fn reset(&self) -> &Self { + self.reg.reset(); + self + } + } + }; +} + +impl_iopctlpin!(AnyPin); +impl_iopctlpin!(FC15Pin); + +macro_rules! impl_FC15pin { + ($pin_periph:ident, $pin_no:expr) => { + impl SealedPin for crate::peripherals::$pin_periph {} + impl ToFC15Pin for crate::peripherals::$pin_periph {} + impl IopctlPin for crate::peripherals::$pin_periph { + #[inline] + fn set_function(&self, _function: Function) -> &Self { + //No function configuration for FC15 pin + self + } + + #[inline] + fn set_pull(&self, pull: Pull) -> &Self { + Self::to_raw($pin_no).set_pull(pull); + self + } + + #[inline] + fn enable_input_buffer(&self) -> &Self { + Self::to_raw($pin_no).enable_input_buffer(); + self + } + + #[inline] + fn disable_input_buffer(&self) -> &Self { + Self::to_raw($pin_no).disable_input_buffer(); + self + } + + #[inline] + fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self { + Self::to_raw($pin_no).set_slew_rate(slew_rate); + self + } + + #[inline] + fn set_drive_strength(&self, strength: DriveStrength) -> &Self { + Self::to_raw($pin_no).set_drive_strength(strength); + self + } + + #[inline] + fn enable_analog_multiplex(&self) -> &Self { + Self::to_raw($pin_no).enable_analog_multiplex(); + self + } + + #[inline] + fn disable_analog_multiplex(&self) -> &Self { + Self::to_raw($pin_no).disable_analog_multiplex(); + self + } + + #[inline] + fn set_drive_mode(&self, mode: DriveMode) -> &Self { + Self::to_raw($pin_no).set_drive_mode(mode); + self + } + + #[inline] + fn set_input_inverter(&self, inverter: Inverter) -> &Self { + Self::to_raw($pin_no).set_input_inverter(inverter); + self + } + + #[inline] + fn reset(&self) -> &Self { + Self::to_raw($pin_no).reset(); + self + } + } + }; +} + +macro_rules! impl_pin { + ($pin_periph:ident, $pin_port:expr, $pin_no:expr) => { + impl SealedPin for crate::peripherals::$pin_periph {} + impl ToAnyPin for crate::peripherals::$pin_periph {} + impl IopctlPin for crate::peripherals::$pin_periph { + #[inline] + fn set_function(&self, function: Function) -> &Self { + Self::to_raw($pin_port, $pin_no).set_function(function); + self + } + + #[inline] + fn set_pull(&self, pull: Pull) -> &Self { + Self::to_raw($pin_port, $pin_no).set_pull(pull); + self + } + + #[inline] + fn enable_input_buffer(&self) -> &Self { + Self::to_raw($pin_port, $pin_no).enable_input_buffer(); + self + } + + #[inline] + fn disable_input_buffer(&self) -> &Self { + Self::to_raw($pin_port, $pin_no).disable_input_buffer(); + self + } + + #[inline] + fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self { + Self::to_raw($pin_port, $pin_no).set_slew_rate(slew_rate); + self + } + + #[inline] + fn set_drive_strength(&self, strength: DriveStrength) -> &Self { + Self::to_raw($pin_port, $pin_no).set_drive_strength(strength); + self + } + + #[inline] + fn enable_analog_multiplex(&self) -> &Self { + Self::to_raw($pin_port, $pin_no).enable_analog_multiplex(); + self + } + + #[inline] + fn disable_analog_multiplex(&self) -> &Self { + Self::to_raw($pin_port, $pin_no).disable_analog_multiplex(); + self + } + + #[inline] + fn set_drive_mode(&self, mode: DriveMode) -> &Self { + Self::to_raw($pin_port, $pin_no).set_drive_mode(mode); + self + } + + #[inline] + fn set_input_inverter(&self, inverter: Inverter) -> &Self { + Self::to_raw($pin_port, $pin_no).set_input_inverter(inverter); + self + } + + #[inline] + fn reset(&self) -> &Self { + Self::to_raw($pin_port, $pin_no).reset(); + self + } + } + }; +} + +impl_pin!(PIO0_0, 0, 0); +impl_pin!(PIO0_1, 0, 1); +impl_pin!(PIO0_2, 0, 2); +impl_pin!(PIO0_3, 0, 3); +impl_pin!(PIO0_4, 0, 4); +impl_pin!(PIO0_5, 0, 5); +impl_pin!(PIO0_6, 0, 6); +impl_pin!(PIO0_7, 0, 7); +impl_pin!(PIO0_8, 0, 8); +impl_pin!(PIO0_9, 0, 9); +impl_pin!(PIO0_10, 0, 10); +impl_pin!(PIO0_11, 0, 11); +impl_pin!(PIO0_12, 0, 12); +impl_pin!(PIO0_13, 0, 13); +impl_pin!(PIO0_14, 0, 14); +impl_pin!(PIO0_15, 0, 15); +impl_pin!(PIO0_16, 0, 16); +impl_pin!(PIO0_17, 0, 17); +impl_pin!(PIO0_18, 0, 18); +impl_pin!(PIO0_19, 0, 19); +impl_pin!(PIO0_20, 0, 20); +impl_pin!(PIO0_21, 0, 21); +impl_pin!(PIO0_22, 0, 22); +impl_pin!(PIO0_23, 0, 23); +impl_pin!(PIO0_24, 0, 24); +impl_pin!(PIO0_25, 0, 25); +impl_pin!(PIO0_26, 0, 26); +impl_pin!(PIO0_27, 0, 27); +impl_pin!(PIO0_28, 0, 28); +impl_pin!(PIO0_29, 0, 29); +impl_pin!(PIO0_30, 0, 30); +impl_pin!(PIO0_31, 0, 31); +impl_pin!(PIO1_0, 1, 0); +impl_pin!(PIO1_1, 1, 1); +impl_pin!(PIO1_2, 1, 2); +impl_pin!(PIO1_3, 1, 3); +impl_pin!(PIO1_4, 1, 4); +impl_pin!(PIO1_5, 1, 5); +impl_pin!(PIO1_6, 1, 6); +impl_pin!(PIO1_7, 1, 7); +impl_pin!(PIO1_8, 1, 8); +impl_pin!(PIO1_9, 1, 9); +impl_pin!(PIO1_10, 1, 10); +impl_pin!(PIO1_11, 1, 11); +impl_pin!(PIO1_12, 1, 12); +impl_pin!(PIO1_13, 1, 13); +impl_pin!(PIO1_14, 1, 14); +impl_pin!(PIO1_15, 1, 15); +impl_pin!(PIO1_16, 1, 16); +impl_pin!(PIO1_17, 1, 17); +impl_pin!(PIO1_18, 1, 18); +impl_pin!(PIO1_19, 1, 19); +impl_pin!(PIO1_20, 1, 20); +impl_pin!(PIO1_21, 1, 21); +impl_pin!(PIO1_22, 1, 22); +impl_pin!(PIO1_23, 1, 23); +impl_pin!(PIO1_24, 1, 24); +impl_pin!(PIO1_25, 1, 25); +impl_pin!(PIO1_26, 1, 26); +impl_pin!(PIO1_27, 1, 27); +impl_pin!(PIO1_28, 1, 28); +impl_pin!(PIO1_29, 1, 29); +impl_pin!(PIO1_30, 1, 30); +impl_pin!(PIO1_31, 1, 31); +impl_pin!(PIO2_0, 2, 0); +impl_pin!(PIO2_1, 2, 1); +impl_pin!(PIO2_2, 2, 2); +impl_pin!(PIO2_3, 2, 3); +impl_pin!(PIO2_4, 2, 4); +impl_pin!(PIO2_5, 2, 5); +impl_pin!(PIO2_6, 2, 6); +impl_pin!(PIO2_7, 2, 7); +impl_pin!(PIO2_8, 2, 8); +impl_pin!(PIO2_9, 2, 9); +impl_pin!(PIO2_10, 2, 10); +impl_pin!(PIO2_11, 2, 11); +impl_pin!(PIO2_12, 2, 12); +impl_pin!(PIO2_13, 2, 13); +impl_pin!(PIO2_14, 2, 14); +impl_pin!(PIO2_15, 2, 15); +impl_pin!(PIO2_16, 2, 16); +impl_pin!(PIO2_17, 2, 17); +impl_pin!(PIO2_18, 2, 18); +impl_pin!(PIO2_19, 2, 19); +impl_pin!(PIO2_20, 2, 20); +impl_pin!(PIO2_21, 2, 21); +impl_pin!(PIO2_22, 2, 22); +impl_pin!(PIO2_23, 2, 23); +impl_pin!(PIO2_24, 2, 24); + +// Note: These have have reset values of 0x41 to support SWD by default +impl_pin!(PIO2_25, 2, 25); +impl_pin!(PIO2_26, 2, 26); + +impl_pin!(PIO2_27, 2, 27); +impl_pin!(PIO2_28, 2, 28); +impl_pin!(PIO2_29, 2, 29); +impl_pin!(PIO2_30, 2, 30); +impl_pin!(PIO2_31, 2, 31); +impl_pin!(PIO3_0, 3, 0); +impl_pin!(PIO3_1, 3, 1); +impl_pin!(PIO3_2, 3, 2); +impl_pin!(PIO3_3, 3, 3); +impl_pin!(PIO3_4, 3, 4); +impl_pin!(PIO3_5, 3, 5); +impl_pin!(PIO3_6, 3, 6); +impl_pin!(PIO3_7, 3, 7); +impl_pin!(PIO3_8, 3, 8); +impl_pin!(PIO3_9, 3, 9); +impl_pin!(PIO3_10, 3, 10); +impl_pin!(PIO3_11, 3, 11); +impl_pin!(PIO3_12, 3, 12); +impl_pin!(PIO3_13, 3, 13); +impl_pin!(PIO3_14, 3, 14); +impl_pin!(PIO3_15, 3, 15); +impl_pin!(PIO3_16, 3, 16); +impl_pin!(PIO3_17, 3, 17); +impl_pin!(PIO3_18, 3, 18); +impl_pin!(PIO3_19, 3, 19); +impl_pin!(PIO3_20, 3, 20); +impl_pin!(PIO3_21, 3, 21); +impl_pin!(PIO3_22, 3, 22); +impl_pin!(PIO3_23, 3, 23); +impl_pin!(PIO3_24, 3, 24); +impl_pin!(PIO3_25, 3, 25); +impl_pin!(PIO3_26, 3, 26); +impl_pin!(PIO3_27, 3, 27); +impl_pin!(PIO3_28, 3, 28); +impl_pin!(PIO3_29, 3, 29); +impl_pin!(PIO3_30, 3, 30); +impl_pin!(PIO3_31, 3, 31); +impl_pin!(PIO4_0, 4, 0); +impl_pin!(PIO4_1, 4, 1); +impl_pin!(PIO4_2, 4, 2); +impl_pin!(PIO4_3, 4, 3); +impl_pin!(PIO4_4, 4, 4); +impl_pin!(PIO4_5, 4, 5); +impl_pin!(PIO4_6, 4, 6); +impl_pin!(PIO4_7, 4, 7); +impl_pin!(PIO4_8, 4, 8); +impl_pin!(PIO4_9, 4, 9); +impl_pin!(PIO4_10, 4, 10); +impl_pin!(PIO7_24, 7, 24); +impl_pin!(PIO7_25, 7, 25); +impl_pin!(PIO7_26, 7, 26); +impl_pin!(PIO7_27, 7, 27); +impl_pin!(PIO7_28, 7, 28); +impl_pin!(PIO7_29, 7, 29); +impl_pin!(PIO7_30, 7, 30); +impl_pin!(PIO7_31, 7, 31); + +// FC15 pins +impl_FC15pin!(PIOFC15_SCL, 0); +impl_FC15pin!(PIOFC15_SDA, 1); diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs new file mode 100644 index 000000000..d56d993c3 --- /dev/null +++ b/embassy-imxrt/src/lib.rs @@ -0,0 +1,130 @@ +#![no_std] +#![allow(async_fn_in_trait)] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] + +//! ## Feature flags +#![doc = document_features::document_features!(feature_label = r#"{feature}"#)] + +#[cfg(not(any(feature = "mimxrt633s", feature = "mimxrt685s",)))] +compile_error!( + "No chip feature activated. You must activate exactly one of the following features: + mimxrt633s, + mimxrt685s, + " +); + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod clocks; +pub mod gpio; +pub mod iopctl; + +// This mod MUST go last, so that it sees all the `impl_foo!' macros +#[cfg_attr(feature = "mimxrt633s", path = "chips/mimxrt633s.rs")] +#[cfg_attr(feature = "mimxrt685s", path = "chips/mimxrt685s.rs")] +mod chip; + +// Reexports +pub use chip::interrupts::*; +#[cfg(feature = "unstable-pac")] +pub use chip::pac; +#[cfg(not(feature = "unstable-pac"))] +pub(crate) use chip::pac; +pub use chip::{peripherals, Peripherals}; +pub use embassy_hal_internal::{Peri, PeripheralType}; + +#[cfg(feature = "rt")] +pub use crate::pac::NVIC_PRIO_BITS; + +/// Macro to bind interrupts to handlers. +/// +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right \[`Binding`\]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +/// +/// Example of how to bind one interrupt: +/// +/// ```rust,ignore +/// use embassy_imxrt::{bind_interrupts, flexspi, peripherals}; +/// +/// bind_interrupts!(struct Irqs { +/// FLEXSPI_IRQ => flexspi::InterruptHandler; +/// }); +/// ``` +/// +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. +#[macro_export] +macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + #[derive(Copy, Clone)] + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} + )* + )* + }; +} + +/// HAL configuration for iMX RT600. +pub mod config { + use crate::clocks::ClockConfig; + + /// HAL configuration passed when initializing. + #[non_exhaustive] + pub struct Config { + /// Clock configuration. + pub clocks: ClockConfig, + } + + impl Default for Config { + fn default() -> Self { + Self { + clocks: ClockConfig::crystal(), + } + } + } + + impl Config { + /// Create a new configuration with the provided clock config. + pub fn new(clocks: ClockConfig) -> Self { + Self { clocks } + } + } +} + +/// Initialize the `embassy-imxrt` HAL with the provided configuration. +/// +/// This returns the peripheral singletons that can be used for creating drivers. +/// +/// This should only be called once at startup, otherwise it panics. +pub fn init(config: config::Config) -> Peripherals { + // Do this first, so that it panics if user is calling `init` a second time + // before doing anything important. + let peripherals = Peripherals::take(); + + unsafe { + if let Err(e) = clocks::init(config.clocks) { + error!("unable to initialize Clocks for reason: {:?}", e); + // Panic here? + } + gpio::init(); + } + + peripherals +} + +pub(crate) mod sealed { + pub trait Sealed {} +} diff --git a/examples/mimxrt6/.cargo/config.toml b/examples/mimxrt6/.cargo/config.toml new file mode 100644 index 000000000..db42be81d --- /dev/null +++ b/examples/mimxrt6/.cargo/config.toml @@ -0,0 +1,17 @@ +[target.thumbv8m.main-none-eabihf] +runner = 'probe-rs run --chip MIMXRT685SFVKB' + +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] + +[build] +target = "thumbv8m.main-none-eabihf" # Cortex-M33 + +[env] +DEFMT_LOG = "trace" diff --git a/examples/mimxrt6/.gitignore b/examples/mimxrt6/.gitignore new file mode 100644 index 000000000..418e01907 --- /dev/null +++ b/examples/mimxrt6/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +/debug +/target + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml new file mode 100644 index 000000000..894ce174c --- /dev/null +++ b/examples/mimxrt6/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "embassy-imxrt-examples" +version = "0.1.0" +edition = "2021" +license = "MIT or Apache-2.0" + +[dependencies] +cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.3" +defmt = "1.0" +defmt-rtt = "1.0" + +embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } +embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } +embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac"] } +embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = "1.0.0" + +mimxrt600-fcb = "0.1.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +rand = { version = "0.8.5", default-features = false } + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/mimxrt6/README.md b/examples/mimxrt6/README.md new file mode 100644 index 000000000..6d5031cf9 --- /dev/null +++ b/examples/mimxrt6/README.md @@ -0,0 +1,18 @@ +# embassy-imxrt-examples + +## Introduction + +These examples illustrates how to use the embassy-imxrt HAL. + +## Adding Examples +Add uniquely named example to `src/bin` like `adc.rs` + +## Build +`cd` to examples folder +`cargo build --bin ` for example, `cargo build --bin adc` + +## Run +Assuming RT685 is powered and connected to Jlink debug probe and the latest probe-rs is installed via + `$ cargo install probe-rs-tools --git https://github.com/probe-rs/probe-rs --locked` +`cd` to examples folder +`cargo run --bin ` for example, `cargo run --bin adc` \ No newline at end of file diff --git a/examples/mimxrt6/build.rs b/examples/mimxrt6/build.rs new file mode 100644 index 000000000..9c0ed3213 --- /dev/null +++ b/examples/mimxrt6/build.rs @@ -0,0 +1,45 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + // Inject crate version into the .biv section. + File::create(out.join("biv.rs")) + .unwrap() + .write_all( + format!( + r##" +#[link_section = ".biv"] +#[used] +static BOOT_IMAGE_VERSION: u32 = 0x{:02x}{:02x}{:02x}00; +"##, + env!("CARGO_PKG_VERSION_MAJOR") + .parse::() + .expect("should have major version"), + env!("CARGO_PKG_VERSION_MINOR") + .parse::() + .expect("should have minor version"), + env!("CARGO_PKG_VERSION_PATCH") + .parse::() + .expect("should have patch version"), + ) + .as_bytes(), + ) + .unwrap(); +} diff --git a/examples/mimxrt6/memory.x b/examples/mimxrt6/memory.x new file mode 100644 index 000000000..5ea82fd71 --- /dev/null +++ b/examples/mimxrt6/memory.x @@ -0,0 +1,34 @@ +MEMORY { + OTFAD : ORIGIN = 0x08000000, LENGTH = 256 + FCB : ORIGIN = 0x08000400, LENGTH = 512 + BIV : ORIGIN = 0x08000600, LENGTH = 4 + KEYSTORE : ORIGIN = 0x08000800, LENGTH = 2K + FLASH : ORIGIN = 0x08001000, LENGTH = 1M + RAM : ORIGIN = 0x20080000, LENGTH = 1536K +} + +SECTIONS { + .otfad : { + . = ALIGN(4); + KEEP(* (.otfad)) + . = ALIGN(4); + } > OTFAD + + .fcb : { + . = ALIGN(4); + KEEP(* (.fcb)) + . = ALIGN(4); + } > FCB + + .biv : { + . = ALIGN(4); + KEEP(* (.biv)) + . = ALIGN(4); + } > BIV + + .keystore : { + . = ALIGN(4); + KEEP(* (.keystore)) + . = ALIGN(4); + } > KEYSTORE +} diff --git a/examples/mimxrt6/src/bin/blinky.rs b/examples/mimxrt6/src/bin/blinky.rs new file mode 100644 index 000000000..e40e71e6f --- /dev/null +++ b/examples/mimxrt6/src/bin/blinky.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_imxrt::gpio; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("Initializing GPIO"); + + let mut led = gpio::Output::new( + p.PIO0_26, + gpio::Level::Low, + gpio::DriveMode::PushPull, + gpio::DriveStrength::Normal, + gpio::SlewRate::Standard, + ); + + loop { + info!("Toggling LED"); + led.toggle(); + cortex_m::asm::delay(5_000_000); + } +} diff --git a/examples/mimxrt6/src/bin/hello.rs b/examples/mimxrt6/src/bin/hello.rs new file mode 100644 index 000000000..c640241ce --- /dev/null +++ b/examples/mimxrt6/src/bin/hello.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::info; +use embassy_executor::Spawner; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let _p = embassy_imxrt::init(Default::default()); + loop { + info!("Hello"); + cortex_m::asm::delay(5_000_000); + } +} diff --git a/examples/mimxrt6/src/lib.rs b/examples/mimxrt6/src/lib.rs new file mode 100644 index 000000000..da6e14427 --- /dev/null +++ b/examples/mimxrt6/src/lib.rs @@ -0,0 +1,20 @@ +#![no_std] + +use mimxrt600_fcb::FlexSPIFlashConfigurationBlock; +use {defmt_rtt as _, panic_probe as _}; + +// auto-generated version information from Cargo.toml +include!(concat!(env!("OUT_DIR"), "/biv.rs")); + +#[link_section = ".otfad"] +#[used] +static OTFAD: [u8; 256] = [0; 256]; + +#[rustfmt::skip] +#[link_section = ".fcb"] +#[used] +static FCB: FlexSPIFlashConfigurationBlock = FlexSPIFlashConfigurationBlock::build(); + +#[link_section = ".keystore"] +#[used] +static KEYSTORE: [u8; 2048] = [0; 2048];