aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf-examples/src/bin/qspi.rs2
-rw-r--r--embassy-nrf/src/qspi.rs140
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
3use core::future::Future; 3use core::future::Future;
4use core::marker::PhantomData; 4use core::marker::PhantomData;
5use core::ptr;
5use core::task::Poll; 6use core::task::Poll;
6use embassy::interrupt::{Interrupt, InterruptExt}; 7use embassy::interrupt::{Interrupt, InterruptExt};
7use embassy::traits::flash::{Error, Flash}; 8use embassy::traits::flash::{Error, Flash};
@@ -10,7 +11,8 @@ use embassy_extras::unborrow;
10use futures::future::poll_fn; 11use futures::future::poll_fn;
11 12
12use crate::fmt::{assert, assert_eq, *}; 13use crate::fmt::{assert, assert_eq, *};
13use crate::gpio::Pin as GpioPin; 14use crate::gpio::sealed::Pin as _;
15use crate::gpio::{self, Pin as GpioPin};
14use crate::pac; 16use crate::pac;
15 17
16pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; 18pub 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
31pub struct DeepPowerDownConfig { 33pub 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
57pub struct Qspi<'d, T: Instance> { 61pub 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
61impl<'d, T: Instance> Qspi<'d, T> { 66impl<'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
245impl<'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
249impl<'d, T: Instance> Flash for Qspi<'d, T> { 283impl<'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;