diff options
| -rw-r--r-- | embassy-nrf-examples/src/bin/qspi.rs | 2 | ||||
| -rw-r--r-- | embassy-nrf/src/qspi.rs | 140 |
2 files changed, 88 insertions, 54 deletions
diff --git a/embassy-nrf-examples/src/bin/qspi.rs b/embassy-nrf-examples/src/bin/qspi.rs index 14f215187..1f33192e9 100644 --- a/embassy-nrf-examples/src/bin/qspi.rs +++ b/embassy-nrf-examples/src/bin/qspi.rs | |||
| @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 33 | 33 | ||
| 34 | let config = qspi::Config::default(); | 34 | let config = qspi::Config::default(); |
| 35 | let irq = interrupt::take!(QSPI); | 35 | let irq = interrupt::take!(QSPI); |
| 36 | let mut q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config); | 36 | let mut q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config).await; |
| 37 | 37 | ||
| 38 | let mut id = [1; 3]; | 38 | let mut id = [1; 3]; |
| 39 | q.custom_instruction(0x9F, &[], &mut id).await.unwrap(); | 39 | q.custom_instruction(0x9F, &[], &mut id).await.unwrap(); |
diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index e5d21ceaf..6cabe1168 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::future::Future; | 3 | use core::future::Future; |
| 4 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::ptr; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | use embassy::interrupt::{Interrupt, InterruptExt}; | 7 | use embassy::interrupt::{Interrupt, InterruptExt}; |
| 7 | use embassy::traits::flash::{Error, Flash}; | 8 | use embassy::traits::flash::{Error, Flash}; |
| @@ -10,7 +11,8 @@ use embassy_extras::unborrow; | |||
| 10 | use futures::future::poll_fn; | 11 | use futures::future::poll_fn; |
| 11 | 12 | ||
| 12 | use crate::fmt::{assert, assert_eq, *}; | 13 | use crate::fmt::{assert, assert_eq, *}; |
| 13 | use crate::gpio::Pin as GpioPin; | 14 | use crate::gpio::sealed::Pin as _; |
| 15 | use crate::gpio::{self, Pin as GpioPin}; | ||
| 14 | use crate::pac; | 16 | use crate::pac; |
| 15 | 17 | ||
| 16 | pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; | 18 | pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; |
| @@ -29,7 +31,9 @@ pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode; | |||
| 29 | // - set gpio in high drive | 31 | // - set gpio in high drive |
| 30 | 32 | ||
| 31 | pub struct DeepPowerDownConfig { | 33 | pub struct DeepPowerDownConfig { |
| 34 | /// Time required for entering DPM, in units of 16us | ||
| 32 | pub enter_time: u16, | 35 | pub enter_time: u16, |
| 36 | /// Time required for exiting DPM, in units of 16us | ||
| 33 | pub exit_time: u16, | 37 | pub exit_time: u16, |
| 34 | } | 38 | } |
| 35 | 39 | ||
| @@ -55,11 +59,12 @@ impl Default for Config { | |||
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | pub struct Qspi<'d, T: Instance> { | 61 | pub struct Qspi<'d, T: Instance> { |
| 62 | dpm_enabled: bool, | ||
| 58 | phantom: PhantomData<&'d mut T>, | 63 | phantom: PhantomData<&'d mut T>, |
| 59 | } | 64 | } |
| 60 | 65 | ||
| 61 | impl<'d, T: Instance> Qspi<'d, T> { | 66 | impl<'d, T: Instance> Qspi<'d, T> { |
| 62 | pub fn new( | 67 | pub async fn new( |
| 63 | _qspi: impl Unborrow<Target = T> + 'd, | 68 | _qspi: impl Unborrow<Target = T> + 'd, |
| 64 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | 69 | irq: impl Unborrow<Target = T::Interrupt> + 'd, |
| 65 | sck: impl Unborrow<Target = impl GpioPin> + 'd, | 70 | sck: impl Unborrow<Target = impl GpioPin> + 'd, |
| @@ -69,20 +74,21 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 69 | io2: impl Unborrow<Target = impl GpioPin> + 'd, | 74 | io2: impl Unborrow<Target = impl GpioPin> + 'd, |
| 70 | io3: impl Unborrow<Target = impl GpioPin> + 'd, | 75 | io3: impl Unborrow<Target = impl GpioPin> + 'd, |
| 71 | config: Config, | 76 | config: Config, |
| 72 | ) -> Self { | 77 | ) -> Qspi<'d, T> { |
| 73 | unborrow!(irq, sck, csn, io0, io1, io2, io3); | 78 | unborrow!(irq, sck, csn, io0, io1, io2, io3); |
| 74 | 79 | ||
| 75 | let r = T::regs(); | 80 | let r = T::regs(); |
| 76 | 81 | ||
| 77 | for cnf in &[ | 82 | let sck = sck.degrade(); |
| 78 | sck.conf(), | 83 | let csn = csn.degrade(); |
| 79 | csn.conf(), | 84 | let io0 = io0.degrade(); |
| 80 | io0.conf(), | 85 | let io1 = io1.degrade(); |
| 81 | io1.conf(), | 86 | let io2 = io2.degrade(); |
| 82 | io2.conf(), | 87 | let io3 = io3.degrade(); |
| 83 | io3.conf(), | 88 | |
| 84 | ] { | 89 | for pin in [&sck, &csn, &io0, &io1, &io2, &io3] { |
| 85 | cnf.write(|w| w.dir().output().drive().h0h1()); | 90 | pin.set_high(); |
| 91 | pin.conf().write(|w| w.dir().output().drive().h0h1()); | ||
| 86 | } | 92 | } |
| 87 | 93 | ||
| 88 | r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); | 94 | r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); |
| @@ -92,53 +98,56 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 92 | r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) }); | 98 | r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) }); |
| 93 | r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) }); | 99 | r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) }); |
| 94 | 100 | ||
| 95 | r.ifconfig0.write(|mut w| { | 101 | r.ifconfig0.write(|w| { |
| 96 | w = w.addrmode().variant(AddressMode::_24BIT); | 102 | w.addrmode().variant(AddressMode::_24BIT); |
| 97 | if config.deep_power_down.is_some() { | 103 | w.dpmenable().bit(config.deep_power_down.is_some()); |
| 98 | w = w.dpmenable().enable(); | 104 | w.ppsize().variant(config.write_page_size); |
| 99 | } else { | 105 | w.readoc().variant(config.read_opcode); |
| 100 | w = w.dpmenable().disable(); | 106 | w.writeoc().variant(config.write_opcode); |
| 101 | } | ||
| 102 | w = w.ppsize().variant(config.write_page_size); | ||
| 103 | w = w.readoc().variant(config.read_opcode); | ||
| 104 | w = w.writeoc().variant(config.write_opcode); | ||
| 105 | w | 107 | w |
| 106 | }); | 108 | }); |
| 107 | 109 | ||
| 108 | if let Some(dpd) = &config.deep_power_down { | 110 | if let Some(dpd) = &config.deep_power_down { |
| 109 | r.dpmdur.write(|mut w| unsafe { | 111 | r.dpmdur.write(|w| unsafe { |
| 110 | w = w.enter().bits(dpd.enter_time); | 112 | w.enter().bits(dpd.enter_time); |
| 111 | w = w.exit().bits(dpd.exit_time); | 113 | w.exit().bits(dpd.exit_time); |
| 112 | w | 114 | w |
| 113 | }) | 115 | }) |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | r.ifconfig1.write(|w| { | 118 | r.ifconfig1.write(|w| unsafe { |
| 117 | let w = unsafe { w.sckdelay().bits(80) }; | 119 | w.sckdelay().bits(80); |
| 118 | let w = w.dpmen().exit(); | 120 | w.dpmen().exit(); |
| 119 | let w = w.spimode().mode0(); | 121 | w.spimode().mode0(); |
| 120 | let w = unsafe { w.sckfreq().bits(3) }; | 122 | w.sckfreq().bits(3); |
| 123 | w | ||
| 124 | }); | ||
| 125 | |||
| 126 | r.xipoffset.write(|w| unsafe { | ||
| 127 | w.xipoffset().bits(config.xip_offset); | ||
| 121 | w | 128 | w |
| 122 | }); | 129 | }); |
| 123 | 130 | ||
| 124 | r.xipoffset | 131 | irq.set_handler(Self::on_interrupt); |
| 125 | .write(|w| unsafe { w.xipoffset().bits(config.xip_offset) }); | 132 | irq.unpend(); |
| 133 | irq.enable(); | ||
| 126 | 134 | ||
| 127 | // Enable it | 135 | // Enable it |
| 128 | r.enable.write(|w| w.enable().enabled()); | 136 | r.enable.write(|w| w.enable().enabled()); |
| 129 | 137 | ||
| 138 | let mut res = Self { | ||
| 139 | dpm_enabled: config.deep_power_down.is_some(), | ||
| 140 | phantom: PhantomData, | ||
| 141 | }; | ||
| 142 | |||
| 130 | r.events_ready.reset(); | 143 | r.events_ready.reset(); |
| 144 | r.intenset.write(|w| w.ready().set()); | ||
| 145 | |||
| 131 | r.tasks_activate.write(|w| w.tasks_activate().bit(true)); | 146 | r.tasks_activate.write(|w| w.tasks_activate().bit(true)); |
| 132 | while r.events_ready.read().bits() == 0 {} | ||
| 133 | r.events_ready.reset(); | ||
| 134 | 147 | ||
| 135 | irq.set_handler(Self::on_interrupt); | 148 | res.wait_ready().await; |
| 136 | irq.unpend(); | ||
| 137 | irq.enable(); | ||
| 138 | 149 | ||
| 139 | Self { | 150 | res |
| 140 | phantom: PhantomData, | ||
| 141 | } | ||
| 142 | } | 151 | } |
| 143 | 152 | ||
| 144 | fn on_interrupt(_: *mut ()) { | 153 | fn on_interrupt(_: *mut ()) { |
| @@ -151,19 +160,6 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 151 | } | 160 | } |
| 152 | } | 161 | } |
| 153 | 162 | ||
| 154 | pub fn sleep(&mut self) { | ||
| 155 | let r = T::regs(); | ||
| 156 | |||
| 157 | info!("flash: sleeping"); | ||
| 158 | info!("flash: state = {:?}", r.status.read().bits()); | ||
| 159 | r.ifconfig1.modify(|_, w| w.dpmen().enter()); | ||
| 160 | info!("flash: state = {:?}", r.status.read().bits()); | ||
| 161 | cortex_m::asm::delay(1000000); | ||
| 162 | info!("flash: state = {:?}", r.status.read().bits()); | ||
| 163 | |||
| 164 | r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit()); | ||
| 165 | } | ||
| 166 | |||
| 167 | pub async fn custom_instruction( | 163 | pub async fn custom_instruction( |
| 168 | &mut self, | 164 | &mut self, |
| 169 | opcode: u8, | 165 | opcode: u8, |
| @@ -246,6 +242,44 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 246 | } | 242 | } |
| 247 | } | 243 | } |
| 248 | 244 | ||
| 245 | impl<'d, T: Instance> Drop for Qspi<'d, T> { | ||
| 246 | fn drop(&mut self) { | ||
| 247 | let r = T::regs(); | ||
| 248 | |||
| 249 | if self.dpm_enabled { | ||
| 250 | info!("qspi: doing deep powerdown..."); | ||
| 251 | |||
| 252 | r.ifconfig1.modify(|_, w| w.dpmen().enter()); | ||
| 253 | |||
| 254 | // Wait for DPM enter. | ||
| 255 | // Unfortunately we must spin. There's no way to do this interrupt-driven. | ||
| 256 | // The READY event does NOT fire on DPM enter (but it does fire on DPM exit :shrug:) | ||
| 257 | while r.status.read().dpm().is_disabled() {} | ||
| 258 | } | ||
| 259 | |||
| 260 | // it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it. | ||
| 261 | r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit()); | ||
| 262 | |||
| 263 | // Workaround https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev1/ERR/nRF52840/Rev1/latest/anomaly_840_122.html?cp=4_0_1_2_1_7 | ||
| 264 | // Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate, | ||
| 265 | // so we only do the second one here. | ||
| 266 | unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) } | ||
| 267 | |||
| 268 | r.enable.write(|w| w.enable().disabled()); | ||
| 269 | |||
| 270 | // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, | ||
| 271 | // leaving it floating, the flash chip might read it as zero which would cause it to | ||
| 272 | // spuriously exit DPM. | ||
| 273 | gpio::deconfigure_pin(r.psel.sck.read().bits()); | ||
| 274 | gpio::deconfigure_pin(r.psel.io0.read().bits()); | ||
| 275 | gpio::deconfigure_pin(r.psel.io1.read().bits()); | ||
| 276 | gpio::deconfigure_pin(r.psel.io2.read().bits()); | ||
| 277 | gpio::deconfigure_pin(r.psel.io3.read().bits()); | ||
| 278 | |||
| 279 | info!("qspi: dropped"); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 249 | impl<'d, T: Instance> Flash for Qspi<'d, T> { | 283 | impl<'d, T: Instance> Flash for Qspi<'d, T> { |
| 250 | #[rustfmt::skip] | 284 | #[rustfmt::skip] |
| 251 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a; | 285 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a; |
