1use crate::Address;
4use crate::*;
5
6use cfg_if::cfg_if;
7#[allow(unused_imports)]
8use core::arch::asm;
9
10pub fn store_page<'a>(address: impl Into<Address>, data: impl Into<&'a DataPage>) {
14 let page_address: Address = address.into();
15
16 erase_page(page_address);
17 copy_to_buffer(data);
18 write_page(page_address);
19 rww_enable();
20}
21
22#[cfg_attr(not(target_arch = "avr"), allow(unused_variables))]
26pub fn erase_page(address: impl Into<Address>) {
27 let page_address: Address = address.into();
28 let z_address: u16 = page_address.into_page_aligned().into();
29
30 busy_wait();
31 rampz(page_address.ramp());
32 cfg_if! {
33 if #[cfg(all(target_arch = "avr", not(doc)))] {
34 unsafe {
35 asm!(
36 "
37 out {SPMCSR} r24
38 spm
39 ",
40 in("r24") PAGE_ERASE,
41 in("Z") z_address,
42 SPMCSR = const SPMCSR_ADDR - 0x20,
43 );
44 }
45 }
46 }
47}
48
49#[cfg_attr(not(target_arch = "avr"), allow(unused_variables))]
53pub fn fill_page(address: impl Into<Address>, data: u16) {
54 let page_address: Address = address.into();
55 let z_address: u16 = page_address.into();
56
57 busy_wait();
58 cfg_if! {
59 if #[cfg(all(target_arch = "avr", not(doc)))] {
60 unsafe {
61 asm!(
62 "
63 movw r0 {data}
64 out {SPMCSR} r24
65 spm
66 eor r1, r1
67 ",
68 data = in(reg_iw) data,
69 in("r24") PAGE_FILL,
70 in("Z") z_address,
71 SPMCSR = const SPMCSR_ADDR - 0x20,
72 )
73 }
74 }
75 }
76}
77
78#[cfg_attr(not(target_arch = "avr"), allow(unused_variables))]
82pub fn write_page(address: impl Into<Address>) {
83 let page_address: Address = address.into();
84 let z_address: u16 = page_address.into_page_aligned().into();
85
86 busy_wait();
87 rampz(page_address.ramp());
88 cfg_if! {
89 if #[cfg(all(target_arch = "avr", not(doc)))] {
90 unsafe {
91 asm!(
92 "
93 out {SPMCSR} r24
94 spm
95 ",
96 in("r24") PAGE_WRITE,
97 in("Z") z_address,
98 SPMCSR = const SPMCSR_ADDR - 0x20,
99 )
100 }
101 }
102 }
103}
104
105#[cfg_attr(not(target_arch = "avr"), allow(unused_variables))]
108pub fn copy_to_buffer<'a>(data: impl Into<&'a DataPage>) {
109 busy_wait();
110 rampz(0);
111 cfg_if! {
112 if #[cfg(all(target_arch = "avr", not(doc)))] {
113 unsafe {
114 asm!(
115 "
116 1:
117 ld r0, X+ // Load r0r1 pair with data from X pointer
118 ld r1, X+
119 out {SPMCSR} r24
120 spm // call spm(PAGE_FILL) (r24 is always 1st byte argument)
121 adiw Z, 2 // increment Z
122 subi {words}, 1 // decrement counter
123 brne 1b // loop until counter reaches 0
124
125 clr r1
126 ",
127
128 words = inout(reg) SPM_PAGESIZE_WORDS as u8 => _,
129 in("r24") PAGE_FILL,
130 inout("X") data.into().as_ptr() => _,
131 inout("Z") 0u16 => _,
132 SPMCSR = const SPMCSR_ADDR - 0x20,
133 )
134 }
135 }
136 }
137}
138
139#[cfg_attr(not(target_arch = "avr"), allow(unused_variables))]
140pub fn lock_bits_set(lock_bits: u8) {
141 rampz(0);
142 cfg_if! {
143 if #[cfg(all(target_arch = "avr", not(doc)))] {
144 let value = !lock_bits;
145 unsafe {
146 asm!(
147 "
148 mov r0 {value}
149 out {SPMCSR} r24
150 spm
151 ",
152 value = in(reg) value,
153 in("r24") LOCK_BITS_SET,
154 in("Z") 0x0001u16,
155 SPMCSR = const SPMCSR_ADDR - 0x20,
156 )
157 }
158 }
159 }
160}
161
162#[cfg(rww_enable)]
164pub fn rww_enable() {
165 busy_wait();
166 cfg_if! {
167 if #[cfg(all(target_arch = "avr", not(doc)))] {
168 unsafe {
169 asm!(
170 "
171 out {SPMCSR} r24
172 spm
173 ",
174 in("r24") RWW_ENABLE,
175 SPMCSR = const SPMCSR_ADDR - 0x20,
176 );
177 }
178 }
179 }
180}
181
182#[cfg(not(rww_enable))]
184pub fn rww_enable() {}
185
186pub fn busy_wait() {
191 cfg_if! {
192 if #[cfg(all(target_arch = "avr", not(doc)))] {
193 while unsafe { core::ptr::read_volatile(SPMCSR) } & PAGE_FILL != 0 {}
194 }
195 }
196}
197
198#[cfg_attr(
199 not(all(target_arch = "avr", extended_addressing)),
200 allow(unused_variables)
201)]
202fn rampz(value: u8) {
203 cfg_if! {
204 if #[cfg(all(target_arch = "avr", extended_addressing, not(doc)))] {
205 unsafe {
206 core::ptr::write_volatile(crate::RAMPZ, value);
207 }
208 }
209 }
210}