aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/ci/doc.sh1
-rwxr-xr-x.github/ci/test.sh2
-rw-r--r--embassy-net-adin1110/Cargo.toml41
-rw-r--r--embassy-net-adin1110/README.md55
-rw-r--r--embassy-net-adin1110/src/crc32.rs358
-rw-r--r--embassy-net-adin1110/src/crc8.rs53
-rw-r--r--embassy-net-adin1110/src/lib.rs1046
-rw-r--r--embassy-net-adin1110/src/mdio.rs175
-rw-r--r--embassy-net-adin1110/src/phy.rs142
-rw-r--r--embassy-net-adin1110/src/regs.rs408
-rw-r--r--examples/stm32l4/.cargo/config.toml2
-rw-r--r--examples/stm32l4/Cargo.toml12
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs438
13 files changed, 2730 insertions, 3 deletions
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh
index 57184dc1d..c317a12e3 100755
--- a/.github/ci/doc.sh
+++ b/.github/ci/doc.sh
@@ -39,6 +39,7 @@ docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/g
39docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup 39docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
40docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup 40docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
41docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static 41docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
42docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup
42 43
43export KUBECONFIG=/ci/secrets/kubeconfig.yml 44export KUBECONFIG=/ci/secrets/kubeconfig.yml
44POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) 45POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
diff --git a/.github/ci/test.sh b/.github/ci/test.sh
index 2892bcf8d..04f4fc7c4 100755
--- a/.github/ci/test.sh
+++ b/.github/ci/test.sh
@@ -28,3 +28,5 @@ cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --featu
28cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti 28cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
29cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti 29cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti
30cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti 30cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti
31
32cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml
diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml
new file mode 100644
index 000000000..e74fb7cd4
--- /dev/null
+++ b/embassy-net-adin1110/Cargo.toml
@@ -0,0 +1,41 @@
1[package]
2name = "embassy-net-adin1110"
3version = "0.1.0"
4description = "embassy-net driver for the ADIN1110 ethernet chip"
5keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"]
6categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
7license = "MIT OR Apache-2.0"
8edition = "2021"
9
10# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11
12[dependencies]
13heapless = "0.7.16"
14defmt = { version = "0.3", optional = true }
15log = { version = "0.4.4", default-features = false, optional = true }
16embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
17embedded-hal-async = { version = "=1.0.0-rc.1" }
18embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
19embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
20embassy-time = { version = "0.1.0" }
21embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
22bitfield = "0.14.0"
23
24
25[dev-dependencies]
26# reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged.
27#embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] }
28embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] }
29crc = "3.0.1"
30env_logger = "0.10"
31critical-section = { version = "1.1.1", features = ["std"] }
32futures-test = "0.3.17"
33
34[features]
35default = [ ]
36defmt = [ "dep:defmt" ]
37
38[package.metadata.embassy_docs]
39src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/"
40src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/"
41target = "thumbv7em-none-eabi"
diff --git a/embassy-net-adin1110/README.md b/embassy-net-adin1110/README.md
new file mode 100644
index 000000000..3c2804183
--- /dev/null
+++ b/embassy-net-adin1110/README.md
@@ -0,0 +1,55 @@
1# SPE ADIN1110 `embassy-net` integration
2
3[`embassy-net`](https://crates.io/crates/embassy-net) integration for the `Analog ADIN1110` SPI SPE ethernet chips.
4
5## What is SPE or Single Pair Ethernet / 10 BASE-T1L
6
7SPE stands for Single Pair Ethernet. As the names implies, SPE uses differential signalling with 2 wires (a twisted-pair) in a cable as the physical medium.
8SPE is full-duplex - it can transmit and receive ethernet packets at the same time. SPE is still ethernet, only the physical layer is different.
9
10SPE also supports [`PoDL (Power over Data Line)`](https://www.ti.com/lit/an/snla395/snla395.pdf), power delivery from 0.5 up to 50 Watts, similar to [`PoE`](https://en.wikipedia.org/wiki/Power_over_Ethernet), but an additional hardware and handshake protocol are needed.
11
12SPE has many link speeds but only `10 BASE-T1L` is able to reach cable lengths up to 1000 meters in `2.4 Vpp` transmit amplitude.
13Currently in 2023, none of the standards are compatible with each other.
14Thus `10 BASE-T1L` won't work with a `10 BASE-T1S`, `100 BASE-T1` or any standard `x BASE-T`.
15
16In the industry SPE is also called [`APL (Advanced Physical Layer)`](https://www.ethernet-apl.org), and is based on the `10 BASE-T1L` standard.
17
18APL can be used in [`intrinsic safety applications/explosion hazardous areas`](https://en.wikipedia.org/wiki/Electrical_equipment_in_hazardous_areas) which has its own name and standard called [`2-WISE (2-wire intrinsically safe ethernet) IEC TS 60079-47:2021`](https://webstore.iec.ch/publication/64292).
19
20`10 BASE-T1L` and `ADIN1110` are designed to support intrinsic safety applications. The power supply energy is fixed and PDoL is not supported.
21
22## Supported SPI modes
23
24`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf)
25
26Both modes support with and without additional CRC.
27Currently only `Generic` SPI with or without CRC is supported.
28
29*NOTE:* SPI Mode is selected by the hardware pins `SPI_CFG0` and `SPI_CFG1`. Software can't detect nor change the mode.
30
31## Hardware
32
33- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) dor an example.
34- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!**
35
36## Other SPE chips
37
38* [`Analog ADIN2111`](https://www.analog.com/en/products/adin2111.html) 2 Port SPI version. Can work with this driver.
39* [`Analog ADIN1100`](https://www.analog.com/en/products/adin1100.html) RGMII version.
40
41## Testing
42
43ADIN1110 library can tested on the host with a mock SPI driver.
44
45$ `cargo test --target x86_64-unknown-linux-gnu`
46
47## License
48
49This work is licensed under either of
50
51- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
52 http://www.apache.org/licenses/LICENSE-2.0)
53- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
54
55at your option.
diff --git a/embassy-net-adin1110/src/crc32.rs b/embassy-net-adin1110/src/crc32.rs
new file mode 100644
index 000000000..a3474f70a
--- /dev/null
+++ b/embassy-net-adin1110/src/crc32.rs
@@ -0,0 +1,358 @@
1pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [
2 0x0000_0000,
3 0x7707_3096,
4 0xEE0E_612C,
5 0x9909_51BA,
6 0x076D_C419,
7 0x706A_F48F,
8 0xE963_A535,
9 0x9E64_95A3,
10 0x0EDB_8832,
11 0x79DC_B8A4,
12 0xE0D5_E91E,
13 0x97D2_D988,
14 0x09B6_4C2B,
15 0x7EB1_7CBD,
16 0xE7B8_2D07,
17 0x90BF_1D91,
18 0x1DB7_1064,
19 0x6AB0_20F2,
20 0xF3B9_7148,
21 0x84BE_41DE,
22 0x1ADA_D47D,
23 0x6DDD_E4EB,
24 0xF4D4_B551,
25 0x83D3_85C7,
26 0x136C_9856,
27 0x646B_A8C0,
28 0xFD62_F97A,
29 0x8A65_C9EC,
30 0x1401_5C4F,
31 0x6306_6CD9,
32 0xFA0F_3D63,
33 0x8D08_0DF5,
34 0x3B6E_20C8,
35 0x4C69_105E,
36 0xD560_41E4,
37 0xA267_7172,
38 0x3C03_E4D1,
39 0x4B04_D447,
40 0xD20D_85FD,
41 0xA50A_B56B,
42 0x35B5_A8FA,
43 0x42B2_986C,
44 0xDBBB_C9D6,
45 0xACBC_F940,
46 0x32D8_6CE3,
47 0x45DF_5C75,
48 0xDCD6_0DCF,
49 0xABD1_3D59,
50 0x26D9_30AC,
51 0x51DE_003A,
52 0xC8D7_5180,
53 0xBFD0_6116,
54 0x21B4_F4B5,
55 0x56B3_C423,
56 0xCFBA_9599,
57 0xB8BD_A50F,
58 0x2802_B89E,
59 0x5F05_8808,
60 0xC60C_D9B2,
61 0xB10B_E924,
62 0x2F6F_7C87,
63 0x5868_4C11,
64 0xC161_1DAB,
65 0xB666_2D3D,
66 0x76DC_4190,
67 0x01DB_7106,
68 0x98D2_20BC,
69 0xEFD5_102A,
70 0x71B1_8589,
71 0x06B6_B51F,
72 0x9FBF_E4A5,
73 0xE8B8_D433,
74 0x7807_C9A2,
75 0x0F00_F934,
76 0x9609_A88E,
77 0xE10E_9818,
78 0x7F6A_0DBB,
79 0x086D_3D2D,
80 0x9164_6C97,
81 0xE663_5C01,
82 0x6B6B_51F4,
83 0x1C6C_6162,
84 0x8565_30D8,
85 0xF262_004E,
86 0x6C06_95ED,
87 0x1B01_A57B,
88 0x8208_F4C1,
89 0xF50F_C457,
90 0x65B0_D9C6,
91 0x12B7_E950,
92 0x8BBE_B8EA,
93 0xFCB9_887C,
94 0x62DD_1DDF,
95 0x15DA_2D49,
96 0x8CD3_7CF3,
97 0xFBD4_4C65,
98 0x4DB2_6158,
99 0x3AB5_51CE,
100 0xA3BC_0074,
101 0xD4BB_30E2,
102 0x4ADF_A541,
103 0x3DD8_95D7,
104 0xA4D1_C46D,
105 0xD3D6_F4FB,
106 0x4369_E96A,
107 0x346E_D9FC,
108 0xAD67_8846,
109 0xDA60_B8D0,
110 0x4404_2D73,
111 0x3303_1DE5,
112 0xAA0A_4C5F,
113 0xDD0D_7CC9,
114 0x5005_713C,
115 0x2702_41AA,
116 0xBE0B_1010,
117 0xC90C_2086,
118 0x5768_B525,
119 0x206F_85B3,
120 0xB966_D409,
121 0xCE61_E49F,
122 0x5EDE_F90E,
123 0x29D9_C998,
124 0xB0D0_9822,
125 0xC7D7_A8B4,
126 0x59B3_3D17,
127 0x2EB4_0D81,
128 0xB7BD_5C3B,
129 0xC0BA_6CAD,
130 0xEDB8_8320,
131 0x9ABF_B3B6,
132 0x03B6_E20C,
133 0x74B1_D29A,
134 0xEAD5_4739,
135 0x9DD2_77AF,
136 0x04DB_2615,
137 0x73DC_1683,
138 0xE363_0B12,
139 0x9464_3B84,
140 0x0D6D_6A3E,
141 0x7A6A_5AA8,
142 0xE40E_CF0B,
143 0x9309_FF9D,
144 0x0A00_AE27,
145 0x7D07_9EB1,
146 0xF00F_9344,
147 0x8708_A3D2,
148 0x1E01_F268,
149 0x6906_C2FE,
150 0xF762_575D,
151 0x8065_67CB,
152 0x196C_3671,
153 0x6E6B_06E7,
154 0xFED4_1B76,
155 0x89D3_2BE0,
156 0x10DA_7A5A,
157 0x67DD_4ACC,
158 0xF9B9_DF6F,
159 0x8EBE_EFF9,
160 0x17B7_BE43,
161 0x60B0_8ED5,
162 0xD6D6_A3E8,
163 0xA1D1_937E,
164 0x38D8_C2C4,
165 0x4FDF_F252,
166 0xD1BB_67F1,
167 0xA6BC_5767,
168 0x3FB5_06DD,
169 0x48B2_364B,
170 0xD80D_2BDA,
171 0xAF0A_1B4C,
172 0x3603_4AF6,
173 0x4104_7A60,
174 0xDF60_EFC3,
175 0xA867_DF55,
176 0x316E_8EEF,
177 0x4669_BE79,
178 0xCB61_B38C,
179 0xBC66_831A,
180 0x256F_D2A0,
181 0x5268_E236,
182 0xCC0C_7795,
183 0xBB0B_4703,
184 0x2202_16B9,
185 0x5505_262F,
186 0xC5BA_3BBE,
187 0xB2BD_0B28,
188 0x2BB4_5A92,
189 0x5CB3_6A04,
190 0xC2D7_FFA7,
191 0xB5D0_CF31,
192 0x2CD9_9E8B,
193 0x5BDE_AE1D,
194 0x9B64_C2B0,
195 0xEC63_F226,
196 0x756A_A39C,
197 0x026D_930A,
198 0x9C09_06A9,
199 0xEB0E_363F,
200 0x7207_6785,
201 0x0500_5713,
202 0x95BF_4A82,
203 0xE2B8_7A14,
204 0x7BB1_2BAE,
205 0x0CB6_1B38,
206 0x92D2_8E9B,
207 0xE5D5_BE0D,
208 0x7CDC_EFB7,
209 0x0BDB_DF21,
210 0x86D3_D2D4,
211 0xF1D4_E242,
212 0x68DD_B3F8,
213 0x1FDA_836E,
214 0x81BE_16CD,
215 0xF6B9_265B,
216 0x6FB0_77E1,
217 0x18B7_4777,
218 0x8808_5AE6,
219 0xFF0F_6A70,
220 0x6606_3BCA,
221 0x1101_0B5C,
222 0x8F65_9EFF,
223 0xF862_AE69,
224 0x616B_FFD3,
225 0x166C_CF45,
226 0xA00A_E278,
227 0xD70D_D2EE,
228 0x4E04_8354,
229 0x3903_B3C2,
230 0xA767_2661,
231 0xD060_16F7,
232 0x4969_474D,
233 0x3E6E_77DB,
234 0xAED1_6A4A,
235 0xD9D6_5ADC,
236 0x40DF_0B66,
237 0x37D8_3BF0,
238 0xA9BC_AE53,
239 0xDEBB_9EC5,
240 0x47B2_CF7F,
241 0x30B5_FFE9,
242 0xBDBD_F21C,
243 0xCABA_C28A,
244 0x53B3_9330,
245 0x24B4_A3A6,
246 0xBAD0_3605,
247 0xCDD7_0693,
248 0x54DE_5729,
249 0x23D9_67BF,
250 0xB366_7A2E,
251 0xC461_4AB8,
252 0x5D68_1B02,
253 0x2A6F_2B94,
254 0xB40B_BE37,
255 0xC30C_8EA1,
256 0x5A05_DF1B,
257 0x2D02_EF8D,
258];
259
260#[allow(non_camel_case_types)]
261#[derive(Debug)]
262pub struct ETH_FSC(pub u32);
263
264impl ETH_FSC {
265 pub const CRC32_OK: u32 = 0x2144_df1c;
266
267 #[must_use]
268 pub fn new(data: &[u8]) -> Self {
269 let fsc = data.iter().fold(u32::MAX, |crc, byte| {
270 let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;
271 CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)
272 }) ^ u32::MAX;
273 Self(fsc)
274 }
275
276 #[must_use]
277 pub fn update(self, data: &[u8]) -> Self {
278 let fsc = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| {
279 let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;
280 CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)
281 }) ^ u32::MAX;
282 Self(fsc)
283 }
284
285 #[must_use]
286 pub fn crc_ok(&self) -> bool {
287 self.0 == Self::CRC32_OK
288 }
289
290 #[must_use]
291 pub fn hton_bytes(&self) -> [u8; 4] {
292 self.0.to_le_bytes()
293 }
294
295 #[must_use]
296 pub fn hton(&self) -> u32 {
297 self.0.to_le()
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn crc32_ethernet_frame() {
307 let packet_a = &[
308 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xff, 0x06, 0x00, 0x01, 0x08, 0x00,
309 0x06, 0x04, 0x00, 0x01, 0x00, 0xe0, 0x4c, 0x68, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0xc0, 0xa8, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x65, 0x90, 0x3d,
312 ];
313
314 let packet_b = &[
315 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,
316 0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,
317 0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,
319 ];
320
321 // Packet A
322 let own_crc = ETH_FSC::new(&packet_a[0..60]);
323 let crc_bytes = own_crc.hton_bytes();
324 println!("{:08x} {:02x?}", own_crc.0, crc_bytes);
325 assert_eq!(&crc_bytes, &packet_a[60..64]);
326
327 let own_crc = ETH_FSC::new(packet_a);
328 println!("{:08x}", own_crc.0);
329 assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
330
331 // Packet B
332 let own_crc = ETH_FSC::new(&packet_b[0..60]);
333 let crc_bytes = own_crc.hton_bytes();
334 println!("{:08x} {:02x?}", own_crc.0, crc_bytes);
335 assert_eq!(&crc_bytes, &packet_b[60..64]);
336
337 let own_crc = ETH_FSC::new(packet_b);
338 println!("{:08x}", own_crc.0);
339 assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
340 }
341
342 #[test]
343 fn crc32_update() {
344 let full_data = &[
345 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,
346 0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,
347 0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,
349 ];
350
351 let (part_a, part_b) = full_data.split_at(16);
352 let crc_partially = ETH_FSC::new(part_a).update(part_b);
353
354 let crc_full = ETH_FSC::new(full_data);
355
356 assert_eq!(crc_full.0, crc_partially.0);
357 }
358}
diff --git a/embassy-net-adin1110/src/crc8.rs b/embassy-net-adin1110/src/crc8.rs
new file mode 100644
index 000000000..7d20a7401
--- /dev/null
+++ b/embassy-net-adin1110/src/crc8.rs
@@ -0,0 +1,53 @@
1/// CRC-8/ITU
2const CRC8X_TABLE: [u8; 256] = [
3 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e,
4 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb,
5 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8,
6 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6,
7 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d,
8 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50,
9 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95,
10 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
11 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f,
12 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a,
13 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e,
14 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
15 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc,
16 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,
17];
18
19/// Calculate the crc of a pease of data.
20pub fn crc8(data: &[u8]) -> u8 {
21 data.iter().fold(0, |crc, &byte| CRC8X_TABLE[usize::from(byte ^ crc)])
22}
23
24#[cfg(test)]
25mod tests {
26 use ::crc::{Crc, CRC_8_SMBUS};
27
28 use super::crc8;
29
30 #[test]
31 fn spi_header_crc8() {
32 let data = &[0x80, 0x00];
33
34 let c = Crc::<u8>::new(&CRC_8_SMBUS);
35 let mut dig = c.digest();
36 dig.update(data);
37 let sw_crc = dig.finalize();
38
39 let own_crc = crc8(data);
40
41 assert_eq!(own_crc, sw_crc);
42 assert_eq!(own_crc, 182);
43
44 let data = &[0x80, 0x01];
45 let mut dig = c.digest();
46 dig.update(data);
47 let sw_crc = dig.finalize();
48 let own_crc = crc8(data);
49
50 assert_eq!(own_crc, sw_crc);
51 assert_eq!(own_crc, 177);
52 }
53}
diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs
new file mode 100644
index 000000000..c0a9b44ee
--- /dev/null
+++ b/embassy-net-adin1110/src/lib.rs
@@ -0,0 +1,1046 @@
1#![deny(clippy::pedantic)]
2#![feature(async_fn_in_trait)]
3#![cfg_attr(not(any(test, feature = "std")), no_std)]
4#![allow(clippy::module_name_repetitions)]
5#![allow(clippy::missing_errors_doc)]
6#![allow(clippy::missing_panics_doc)]
7#![doc = include_str!("../README.md")]
8
9mod crc32;
10mod crc8;
11mod mdio;
12mod phy;
13mod regs;
14
15use ch::driver::LinkState;
16pub use crc32::ETH_FSC;
17use crc8::crc8;
18use embassy_futures::select::{select, Either};
19use embassy_net_driver_channel as ch;
20use embassy_time::{Duration, Timer};
21use embedded_hal_1::digital::OutputPin;
22use embedded_hal_async::digital::Wait;
23use embedded_hal_async::spi::{Operation, SpiDevice};
24use heapless::Vec;
25pub use mdio::MdioBus;
26pub use phy::{Phy10BaseT1x, RegsC22, RegsC45};
27pub use regs::{Config0, Config2, SpiRegisters as sr, Status0, Status1};
28
29use crate::regs::{LedCntrl, LedFunc, LedPol, LedPolarity, SpiHeader};
30
31pub const PHYID: u32 = 0x0283_BC91;
32
33/// Error values ADIN1110
34#[derive(Debug)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[allow(non_camel_case_types)]
37pub enum AdinError<E> {
38 Spi(E),
39 SENDERROR,
40 READERROR,
41 CRC,
42 PACKET_TOO_BIG,
43 PACKET_TOO_SMALL,
44 MDIO_ACC_TIMEOUT,
45}
46
47pub type AEResult<T, SPIError> = core::result::Result<T, AdinError<SPIError>>;
48pub const MDIO_PHY_ADDR: u8 = 0x01;
49
50/// Maximum Transmission Unit
51pub const MTU: usize = 1514;
52
53/// Max SPI/Frame buffer size
54pub const MAX_BUFF: usize = 2048;
55
56const DONT_CARE_BYTE: u8 = 0x00;
57const TURN_AROUND_BYTE: u8 = 0x00;
58
59/// Packet minimal frame/packet length
60const ETH_MIN_LEN: usize = 64;
61
62/// Ethernet `Frame Check Sequence` length
63const FSC_LEN: usize = 4;
64/// SPI Header, contains SPI action and register id.
65const SPI_HEADER_LEN: usize = 2;
66/// SPI Header CRC length
67const SPI_HEADER_CRC_LEN: usize = 1;
68/// Frame Header,
69const FRAME_HEADER_LEN: usize = 2;
70
71// P1 = 0x00, P2 = 0x01
72const PORT_ID_BYTE: u8 = 0x00;
73
74pub type Packet = Vec<u8, { SPI_HEADER_LEN + FRAME_HEADER_LEN + MTU + FSC_LEN + 1 + 4 }>;
75
76/// Type alias for the embassy-net driver for ADIN1110
77pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
78
79/// Internal state for the embassy-net integration.
80pub struct State<const N_RX: usize, const N_TX: usize> {
81 ch_state: ch::State<MTU, N_RX, N_TX>,
82}
83impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
84 /// Create a new `State`.
85 #[must_use]
86 pub const fn new() -> Self {
87 Self {
88 ch_state: ch::State::new(),
89 }
90 }
91}
92
93#[derive(Debug)]
94pub struct ADIN1110<SPI> {
95 /// SPI bus
96 spi: SPI,
97 /// Enable CRC on SPI transfer.
98 /// This must match with the hardware pin `SPI_CFG0` were low = CRC enable, high = CRC disabled.
99 crc: bool,
100}
101
102impl<SPI: SpiDevice> ADIN1110<SPI> {
103 pub fn new(spi: SPI, crc: bool) -> Self {
104 Self { spi, crc }
105 }
106
107 pub async fn read_reg(&mut self, reg: sr) -> AEResult<u32, SPI::Error> {
108 let mut tx_buf = Vec::<u8, 16>::new();
109
110 let mut spi_hdr = SpiHeader(0);
111 spi_hdr.set_control(true);
112 spi_hdr.set_addr(reg);
113 let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice());
114
115 if self.crc {
116 // Add CRC for header data
117 let _ = tx_buf.push(crc8(&tx_buf));
118 }
119
120 // Turn around byte, TODO: Unknown that this is.
121 let _ = tx_buf.push(TURN_AROUND_BYTE);
122
123 let mut rx_buf = [0; 5];
124
125 let spi_read_len = if self.crc { rx_buf.len() } else { rx_buf.len() - 1 };
126
127 let mut spi_op = [Operation::Write(&tx_buf), Operation::Read(&mut rx_buf[0..spi_read_len])];
128
129 self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?;
130
131 if self.crc {
132 let crc = crc8(&rx_buf[0..4]);
133 if crc != rx_buf[4] {
134 return Err(AdinError::CRC);
135 }
136 }
137
138 let value = u32::from_be_bytes(rx_buf[0..4].try_into().unwrap());
139
140 #[cfg(feature = "defmt")]
141 defmt::trace!("REG Read {} = {:08x} SPI {:02x}", reg, value, &tx_buf);
142
143 Ok(value)
144 }
145
146 pub async fn write_reg(&mut self, reg: sr, value: u32) -> AEResult<(), SPI::Error> {
147 let mut tx_buf = Vec::<u8, 16>::new();
148
149 let mut spi_hdr = SpiHeader(0);
150 spi_hdr.set_control(true);
151 spi_hdr.set_write(true);
152 spi_hdr.set_addr(reg);
153 let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice());
154
155 if self.crc {
156 // Add CRC for header data
157 let _ = tx_buf.push(crc8(&tx_buf));
158 }
159
160 let val = value.to_be_bytes();
161 let _ = tx_buf.extend_from_slice(val.as_slice());
162
163 if self.crc {
164 // Add CRC for header data
165 let _ = tx_buf.push(crc8(val.as_slice()));
166 }
167
168 #[cfg(feature = "defmt")]
169 defmt::trace!("REG Write {} = {:08x} SPI {:02x}", reg, value, &tx_buf);
170
171 self.spi.write(&tx_buf).await.map_err(AdinError::Spi)
172 }
173
174 /// helper function for write to `MDIO_ACC` register and wait for ready!
175 async fn write_mdio_acc_reg(&mut self, mdio_acc_val: u32) -> AEResult<u32, SPI::Error> {
176 self.write_reg(sr::MDIO_ACC, mdio_acc_val).await?;
177
178 // TODO: Add proper timeout!
179 for _ in 0..100_000 {
180 let val = self.read_reg(sr::MDIO_ACC).await?;
181 if val & 0x8000_0000 != 0 {
182 return Ok(val);
183 }
184 }
185
186 Err(AdinError::MDIO_ACC_TIMEOUT)
187 }
188
189 /// Read out fifo ethernet packet memory received via the wire.
190 pub async fn read_fifo(&mut self, packet: &mut [u8]) -> AEResult<usize, SPI::Error> {
191 let mut tx_buf = Vec::<u8, 16>::new();
192
193 // Size of the frame, also includes the appednded header.
194 let packet_size = self.read_reg(sr::RX_FSIZE).await? as usize;
195
196 // Packet read of write to the MAC packet buffer must be a multipul of 4!
197 let read_size = packet_size.next_multiple_of(4);
198
199 if packet_size < (SPI_HEADER_LEN + FSC_LEN) {
200 return Err(AdinError::PACKET_TOO_SMALL);
201 }
202
203 if read_size > packet.len() {
204 #[cfg(feature = "defmt")]
205 defmt::trace!("MAX: {} WANT: {}", packet.len(), read_size);
206 return Err(AdinError::PACKET_TOO_BIG);
207 }
208
209 let mut spi_hdr = SpiHeader(0);
210 spi_hdr.set_control(true);
211 spi_hdr.set_addr(sr::RX);
212 let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice());
213
214 if self.crc {
215 // Add CRC for header data
216 let _ = tx_buf.push(crc8(&tx_buf));
217 }
218
219 // Turn around byte, TODO: Unknown that this is.
220 let _ = tx_buf.push(TURN_AROUND_BYTE);
221
222 let spi_packet = &mut packet[0..read_size];
223
224 assert_eq!(spi_packet.len() & 0x03, 0x00);
225
226 let mut pkt_header = [0, 0];
227 let mut fsc = [0, 0, 0, 0];
228
229 let mut spi_op = [
230 Operation::Write(&tx_buf),
231 Operation::Read(&mut pkt_header),
232 Operation::Read(spi_packet),
233 Operation::Read(&mut fsc),
234 ];
235
236 self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?;
237
238 Ok(packet_size as usize)
239 }
240
241 /// Write to fifo ethernet packet memory send over the wire.
242 pub async fn write_fifo(&mut self, frame: &[u8]) -> AEResult<(), SPI::Error> {
243 const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + FRAME_HEADER_LEN;
244 const TAIL_LEN: usize = ETH_MIN_LEN - FSC_LEN + FSC_LEN + 1;
245
246 if frame.len() < (6 + 6 + 2) {
247 return Err(AdinError::PACKET_TOO_SMALL);
248 }
249 if frame.len() > (MAX_BUFF - FRAME_HEADER_LEN) {
250 return Err(AdinError::PACKET_TOO_BIG);
251 }
252
253 // SPI HEADER + [OPTIONAL SPI CRC] + FRAME HEADER
254 let mut head_data = Vec::<u8, HEAD_LEN>::new();
255 // [OPTIONAL PAD DATA] + FCS + [OPTINAL BYTES MAKE SPI FRAME EVEN]
256 let mut tail_data = Vec::<u8, TAIL_LEN>::new();
257
258 let mut spi_hdr = SpiHeader(0);
259 spi_hdr.set_control(true);
260 spi_hdr.set_write(true);
261 spi_hdr.set_addr(sr::TX);
262
263 head_data
264 .extend_from_slice(spi_hdr.0.to_be_bytes().as_slice())
265 .map_err(|_e| AdinError::PACKET_TOO_BIG)?;
266
267 if self.crc {
268 // Add CRC for header data
269 head_data
270 .push(crc8(&head_data[0..2]))
271 .map_err(|_| AdinError::PACKET_TOO_BIG)?;
272 }
273
274 // Add port number, ADIN1110 its fixed to zero/P1, but for ADIN2111 has two ports.
275 head_data
276 .extend_from_slice(u16::from(PORT_ID_BYTE).to_be_bytes().as_slice())
277 .map_err(|_e| AdinError::PACKET_TOO_BIG)?;
278
279 let mut frame_fcs = ETH_FSC::new(frame);
280
281 // ADIN1110 MAC and PHY don´t accept ethernet packet smaller than 64 bytes.
282 // So padded the data minus the FCS, FCS is automatilly added to by the MAC.
283 if let Some(pad_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(frame.len()) {
284 let _ = tail_data.resize(pad_len, 0x00);
285 frame_fcs = frame_fcs.update(&tail_data);
286 }
287
288 // Add ethernet FCS only over the ethernet packet.
289 // Only usefull when `CONFIG0`, `Transmit Frame Check Sequence Validation Enable` bit is enabled.
290 let _ = tail_data.extend_from_slice(frame_fcs.hton_bytes().as_slice());
291
292 // len = frame_size + optional padding + 2 bytes Frame header
293 let send_len_orig = frame.len() + tail_data.len() + FRAME_HEADER_LEN;
294 let spi_pad_len = send_len_orig.next_multiple_of(4);
295 let send_len = u32::try_from(send_len_orig).map_err(|_| AdinError::PACKET_TOO_BIG)?;
296
297 // Packet read of write to the MAC packet buffer must be a multipul of 4 bytes!
298 if spi_pad_len != send_len_orig {
299 let spi_pad_len = spi_pad_len - send_len_orig;
300 let _ = tail_data.extend_from_slice(&[DONT_CARE_BYTE, DONT_CARE_BYTE, DONT_CARE_BYTE][..spi_pad_len]);
301 }
302
303 #[cfg(feature = "defmt")]
304 defmt::trace!(
305 "TX: hdr {} [{}] {:02x}-{:02x}-{:02x} SIZE: {}",
306 head_data.len(),
307 frame.len(),
308 head_data.as_slice(),
309 frame,
310 tail_data.as_slice(),
311 send_len,
312 );
313
314 self.write_reg(sr::TX_FSIZE, send_len).await?;
315
316 let mut transaction = [
317 Operation::Write(head_data.as_slice()),
318 Operation::Write(frame),
319 Operation::Write(tail_data.as_slice()),
320 ];
321
322 self.spi.transaction(&mut transaction).await.map_err(AdinError::Spi)
323 }
324
325 /// Programs the mac address in the mac filters.
326 /// Also set the boardcast address.
327 /// The chip supports 2 priority queues but current code doesn't support this mode.
328 pub async fn set_mac_addr(&mut self, mac: &[u8; 6]) -> AEResult<(), SPI::Error> {
329 let mac_high_part = u16::from_be_bytes(mac[0..2].try_into().unwrap());
330 let mac_low_part = u32::from_be_bytes(mac[2..6].try_into().unwrap());
331
332 // program our mac address in the mac address filter
333 self.write_reg(sr::ADDR_FILT_UPR0, (1 << 16) | (1 << 30) | u32::from(mac_high_part))
334 .await?;
335 self.write_reg(sr::ADDR_FILT_LWR0, mac_low_part).await?;
336
337 self.write_reg(sr::ADDR_MSK_UPR0, u32::from(mac_high_part)).await?;
338 self.write_reg(sr::ADDR_MSK_LWR0, mac_low_part).await?;
339
340 // Also program broadcast address in the mac address filter
341 self.write_reg(sr::ADDR_FILT_UPR1, (1 << 16) | (1 << 30) | 0xFFFF)
342 .await?;
343 self.write_reg(sr::ADDR_FILT_LWR1, 0xFFFF_FFFF).await?;
344 self.write_reg(sr::ADDR_MSK_UPR1, 0xFFFF).await?;
345 self.write_reg(sr::ADDR_MSK_LWR1, 0xFFFF_FFFF).await?;
346
347 Ok(())
348 }
349}
350
351impl<SPI: SpiDevice> mdio::MdioBus for ADIN1110<SPI> {
352 type Error = AdinError<SPI::Error>;
353
354 /// Read from the PHY Registers as Clause 22.
355 async fn read_cl22(&mut self, phy_id: u8, reg: u8) -> Result<u16, Self::Error> {
356 let mdio_acc_val: u32 =
357 (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x3 << 26);
358
359 // Result is in the lower half of the answer.
360 #[allow(clippy::cast_possible_truncation)]
361 self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16)
362 }
363
364 /// Read from the PHY Registers as Clause 45.
365 async fn read_cl45(&mut self, phy_id: u8, regc45: (u8, u16)) -> Result<u16, Self::Error> {
366 let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | u32::from(regc45.1);
367
368 self.write_mdio_acc_reg(mdio_acc_val).await?;
369
370 let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | (0x03 << 26);
371
372 // Result is in the lower half of the answer.
373 #[allow(clippy::cast_possible_truncation)]
374 self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16)
375 }
376
377 /// Write to the PHY Registers as Clause 22.
378 async fn write_cl22(&mut self, phy_id: u8, reg: u8, val: u16) -> Result<(), Self::Error> {
379 let mdio_acc_val: u32 =
380 (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x1 << 26) | u32::from(val);
381
382 self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ())
383 }
384
385 /// Write to the PHY Registers as Clause 45.
386 async fn write_cl45(&mut self, phy_id: u8, regc45: (u8, u16), value: u16) -> AEResult<(), SPI::Error> {
387 let phy_id = u32::from(phy_id & 0x1F) << 21;
388 let dev_addr = u32::from(regc45.0 & 0x1F) << 16;
389 let reg = u32::from(regc45.1);
390
391 let mdio_acc_val: u32 = phy_id | dev_addr | reg;
392 self.write_mdio_acc_reg(mdio_acc_val).await?;
393
394 let mdio_acc_val: u32 = phy_id | dev_addr | (0x01 << 26) | u32::from(value);
395 self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ())
396 }
397}
398
399/// Background runner for the ADIN110.
400///
401/// You must call `.run()` in a background task for the ADIN1100 to operate.
402pub struct Runner<'d, SPI, INT, RST> {
403 mac: ADIN1110<SPI>,
404 ch: ch::Runner<'d, MTU>,
405 int: INT,
406 is_link_up: bool,
407 _reset: RST,
408}
409
410impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
411 #[allow(clippy::too_many_lines)]
412 pub async fn run(mut self) -> ! {
413 loop {
414 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
415
416 loop {
417 #[cfg(feature = "defmt")]
418 defmt::debug!("Waiting for interrupts");
419 match select(self.int.wait_for_low(), tx_chan.tx_buf()).await {
420 Either::First(_) => {
421 let mut status1_clr = Status1(0);
422 let mut status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap());
423
424 while status1.p1_rx_rdy() {
425 #[cfg(feature = "defmt")]
426 defmt::debug!("alloc RX packet buffer");
427 match select(rx_chan.rx_buf(), tx_chan.tx_buf()).await {
428 // Handle frames that needs to transmit from the wire.
429 // Note: rx_chan.rx_buf() channel don´t accept new request
430 // when the tx_chan is full. So these will be handled
431 // automaticly.
432 Either::First(frame) => match self.mac.read_fifo(frame).await {
433 Ok(n) => {
434 rx_chan.rx_done(n);
435 }
436 Err(e) => match e {
437 AdinError::PACKET_TOO_BIG => {
438 #[cfg(feature = "defmt")]
439 defmt::error!("RX Packet to big, DROP");
440 self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap();
441 }
442 AdinError::Spi(_) => {
443 #[cfg(feature = "defmt")]
444 defmt::error!("RX Spi error")
445 }
446 _ => {
447 #[cfg(feature = "defmt")]
448 defmt::error!("RX Error")
449 }
450 },
451 },
452 Either::Second(frame) => {
453 // Handle frames that needs to transmit to the wire.
454 self.mac.write_fifo(frame).await.unwrap();
455 tx_chan.tx_done();
456 }
457 }
458 status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap());
459 }
460
461 let status0 = Status0(self.mac.read_reg(sr::STATUS0).await.unwrap());
462 if status1.0 & !0x1b != 0 {
463 #[cfg(feature = "defmt")]
464 defmt::error!("SPE CHIP STATUS 0:{:08x} 1:{:08x}", status0.0, status1.0);
465 }
466
467 if status1.tx_rdy() {
468 status1_clr.set_tx_rdy(true);
469 #[cfg(feature = "defmt")]
470 defmt::info!("TX_DONE");
471 }
472
473 if status1.link_change() {
474 let link = status1.p1_link_status();
475 self.is_link_up = link;
476
477 #[cfg(feature = "defmt")]
478 if link {
479 let link_status = self
480 .mac
481 .read_cl45(MDIO_PHY_ADDR, RegsC45::DA7::AN_STATUS_EXTRA.into())
482 .await
483 .unwrap();
484
485 let volt = if link_status & (0b11 << 5) == (0b11 << 5) {
486 "2.4"
487 } else {
488 "1.0"
489 };
490
491 let mse = self
492 .mac
493 .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1::MSE_VAL.into())
494 .await
495 .unwrap();
496
497 defmt::info!("LINK Changed: Link Up, Volt: {} V p-p, MSE: {:0004}", volt, mse);
498 } else {
499 defmt::info!("LINK Changed: Link Down");
500 }
501
502 state_chan.set_link_state(if link { LinkState::Up } else { LinkState::Down });
503 status1_clr.set_link_change(true);
504 }
505
506 if status1.tx_ecc_err() {
507 #[cfg(feature = "defmt")]
508 defmt::error!("SPI TX_ECC_ERR error, CLEAR TX FIFO");
509 self.mac.write_reg(sr::FIFO_CLR, 2).await.unwrap();
510 status1_clr.set_tx_ecc_err(true);
511 }
512
513 if status1.rx_ecc_err() {
514 #[cfg(feature = "defmt")]
515 defmt::error!("SPI RX_ECC_ERR error");
516 status1_clr.set_rx_ecc_err(true);
517 }
518
519 if status1.spi_err() {
520 #[cfg(feature = "defmt")]
521 defmt::error!("SPI SPI_ERR CRC error");
522 status1_clr.set_spi_err(true);
523 }
524
525 if status0.phyint() {
526 #[cfg_attr(not(feature = "defmt"), allow(unused_variables))]
527 let crsm_irq_st = self
528 .mac
529 .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::CRSM_IRQ_STATUS.into())
530 .await
531 .unwrap();
532
533 #[cfg_attr(not(feature = "defmt"), allow(unused_variables))]
534 let phy_irq_st = self
535 .mac
536 .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1F::PHY_SYBSYS_IRQ_STATUS.into())
537 .await
538 .unwrap();
539
540 #[cfg(feature = "defmt")]
541 defmt::warn!(
542 "SPE CHIP PHY CRSM_IRQ_STATUS {:04x} PHY_SUBSYS_IRQ_STATUS {:04x}",
543 crsm_irq_st,
544 phy_irq_st
545 );
546 }
547
548 if status0.txfcse() {
549 #[cfg(feature = "defmt")]
550 defmt::error!("SPE CHIP PHY TX Frame CRC error");
551 }
552
553 // Clear status0
554 self.mac.write_reg(sr::STATUS0, 0xFFF).await.unwrap();
555 self.mac.write_reg(sr::STATUS1, status1_clr.0).await.unwrap();
556 }
557 Either::Second(packet) => {
558 // Handle frames that needs to transmit to the wire.
559 self.mac.write_fifo(packet).await.unwrap();
560 tx_chan.tx_done();
561 }
562 }
563 }
564 }
565 }
566}
567
568/// Obtain a driver for using the ADIN1110 with [`embassy-net`](crates.io/crates/embassy-net).
569pub async fn new<const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
570 mac_addr: [u8; 6],
571 state: &'_ mut State<N_RX, N_TX>,
572 spi_dev: SPI,
573 int: INT,
574 mut reset: RST,
575 crc: bool,
576) -> (Device<'_>, Runner<'_, SPI, INT, RST>) {
577 use crate::regs::{IMask0, IMask1};
578
579 #[cfg(feature = "defmt")]
580 defmt::info!("INIT ADIN1110");
581
582 // Reset sequence
583 reset.set_low().unwrap();
584
585 // Wait t1: 20-43mS
586 Timer::after(Duration::from_millis(30)).await;
587
588 reset.set_high().unwrap();
589
590 // Wait t3: 50mS
591 Timer::after(Duration::from_millis(50)).await;
592
593 // Create device
594 let mut mac = ADIN1110::new(spi_dev, crc);
595
596 // Check PHYID
597 let id = mac.read_reg(sr::PHYID).await.unwrap();
598 assert_eq!(id, PHYID);
599
600 #[cfg(feature = "defmt")]
601 defmt::debug!("SPE: CHIP MAC/ID: {:08x}", id);
602
603 #[cfg(feature = "defmt")]
604 let adin_phy = Phy10BaseT1x::default();
605 #[cfg(feature = "defmt")]
606 let phy_id = adin_phy.get_id(&mut mac).await.unwrap();
607 #[cfg(feature = "defmt")]
608 defmt::debug!("SPE: CHIP: PHY ID: {:08x}", phy_id);
609
610 let mi_control = mac.read_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8).await.unwrap();
611 #[cfg(feature = "defmt")]
612 defmt::println!("SPE CHIP PHY MI_CONTROL {:04x}", mi_control);
613 if mi_control & 0x0800 != 0 {
614 let val = mi_control & !0x0800;
615 #[cfg(feature = "defmt")]
616 defmt::println!("SPE CHIP PHY MI_CONTROL Disable PowerDown");
617 mac.write_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8, val)
618 .await
619 .unwrap();
620 }
621
622 // Config2: CRC_APPEND
623 let mut config2 = Config2(0x0000_0800);
624 config2.set_crc_append(true);
625 mac.write_reg(sr::CONFIG2, config2.0).await.unwrap();
626
627 // Pin Mux Config 1
628 let led_val = (0b11 << 6) | (0b11 << 4); // | (0b00 << 1);
629 mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::DIGIO_PINMUX.into(), led_val)
630 .await
631 .unwrap();
632
633 let mut led_pol = LedPolarity(0);
634 led_pol.set_led1_polarity(LedPol::ActiveLow);
635 led_pol.set_led0_polarity(LedPol::ActiveLow);
636
637 // Led Polarity Regisgere Active Low
638 mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_POLARITY.into(), led_pol.0)
639 .await
640 .unwrap();
641
642 // Led Both On
643 let mut led_cntr = LedCntrl(0x0);
644
645 // LED1: Yellow
646 led_cntr.set_led1_en(true);
647 led_cntr.set_led1_function(LedFunc::TxLevel2P4);
648 // LED0: Green
649 led_cntr.set_led0_en(true);
650 led_cntr.set_led0_function(LedFunc::LinkupTxRxActicity);
651
652 mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_CNTRL.into(), led_cntr.0)
653 .await
654 .unwrap();
655
656 // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE
657 // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR
658 // Have to clear the mask the enable it.
659 let mut imask0_val = IMask0(0x0000_1FBF);
660 imask0_val.set_txfcsem(false);
661 imask0_val.set_phyintm(false);
662 imask0_val.set_txboem(false);
663 imask0_val.set_rxboem(false);
664 imask0_val.set_txpem(false);
665
666 mac.write_reg(sr::IMASK0, imask0_val.0).await.unwrap();
667
668 // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE
669 // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR
670 // Have to clear the mask the enable it.
671 let mut imask1_val = IMask1(0x43FA_1F1A);
672 imask1_val.set_link_change_mask(false);
673 imask1_val.set_p1_rx_rdy_mask(false);
674 imask1_val.set_spi_err_mask(false);
675 imask1_val.set_tx_ecc_err_mask(false);
676 imask1_val.set_rx_ecc_err_mask(false);
677
678 mac.write_reg(sr::IMASK1, imask1_val.0).await.unwrap();
679
680 // Program mac address but also sets mac filters.
681 mac.set_mac_addr(&mac_addr).await.unwrap();
682
683 let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));
684 (
685 device,
686 Runner {
687 ch: runner,
688 mac,
689 int,
690 is_link_up: false,
691 _reset: reset,
692 },
693 )
694}
695
696#[allow(clippy::similar_names)]
697#[cfg(test)]
698mod tests {
699 use core::convert::Infallible;
700
701 use embedded_hal_1::digital::{ErrorType, OutputPin};
702 use embedded_hal_async::delay::DelayUs;
703 use embedded_hal_bus::spi::ExclusiveDevice;
704 use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction};
705
706 #[derive(Debug, Default)]
707 struct CsPinMock {
708 pub high: u32,
709 pub low: u32,
710 }
711 impl OutputPin for CsPinMock {
712 fn set_low(&mut self) -> Result<(), Self::Error> {
713 self.low += 1;
714 Ok(())
715 }
716
717 fn set_high(&mut self) -> Result<(), Self::Error> {
718 self.high += 1;
719 Ok(())
720 }
721 }
722 impl ErrorType for CsPinMock {
723 type Error = Infallible;
724 }
725
726 use super::*;
727
728 // TODO: This is currently a workaround unit `ExclusiveDevice` is moved to `embedded-hal-bus`
729 // see https://github.com/rust-embedded/embedded-hal/pull/462#issuecomment-1560014426
730 struct MockDelay {}
731
732 impl DelayUs for MockDelay {
733 async fn delay_us(&mut self, _us: u32) {
734 todo!()
735 }
736
737 async fn delay_ms(&mut self, _ms: u32) {
738 todo!()
739 }
740 }
741
742 #[futures_test::test]
743 async fn mac_read_registers_without_crc() {
744 // Configure expectations
745 let expectations = [
746 // 1st
747 SpiTransaction::write_vec(vec![0x80, 0x01, TURN_AROUND_BYTE]),
748 SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91]),
749 SpiTransaction::flush(),
750 // 2nd
751 SpiTransaction::write_vec(vec![0x80, 0x02, TURN_AROUND_BYTE]),
752 SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3]),
753 SpiTransaction::flush(),
754 ];
755 let mut spi = SpiMock::new(&expectations);
756
757 let cs = CsPinMock::default();
758 let delay = MockDelay {};
759 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
760 let mut spe = ADIN1110::new(spi_dev, false);
761
762 // Read PHIID
763 let val = spe.read_reg(sr::PHYID).await.expect("Error");
764 assert_eq!(val, 0x0283_BC91);
765
766 // Read CAPAVILITY
767 let val = spe.read_reg(sr::CAPABILITY).await.expect("Error");
768 assert_eq!(val, 0x0000_06C3);
769
770 spi.done();
771 }
772
773 #[futures_test::test]
774 async fn mac_read_registers_with_crc() {
775 // Configure expectations
776 let expectations = [
777 // 1st
778 SpiTransaction::write_vec(vec![0x80, 0x01, 177, TURN_AROUND_BYTE]),
779 SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91, 215]),
780 SpiTransaction::flush(),
781 // 2nd
782 SpiTransaction::write_vec(vec![0x80, 0x02, 184, TURN_AROUND_BYTE]),
783 SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3, 57]),
784 SpiTransaction::flush(),
785 ];
786 let mut spi = SpiMock::new(&expectations);
787
788 let cs = CsPinMock::default();
789 let delay = MockDelay {};
790 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
791
792 let mut spe = ADIN1110::new(spi_dev, true);
793
794 assert_eq!(crc8(0x0283_BC91_u32.to_be_bytes().as_slice()), 215);
795 assert_eq!(crc8(0x0000_06C3_u32.to_be_bytes().as_slice()), 57);
796
797 // Read PHIID
798 let val = spe.read_reg(sr::PHYID).await.expect("Error");
799 assert_eq!(val, 0x0283_BC91);
800
801 // Read CAPAVILITY
802 let val = spe.read_reg(sr::CAPABILITY).await.expect("Error");
803 assert_eq!(val, 0x0000_06C3);
804
805 spi.done();
806 }
807
808 #[futures_test::test]
809 async fn mac_write_registers_without_crc() {
810 // Configure expectations
811 let expectations = [
812 SpiTransaction::write_vec(vec![0xA0, 0x09, 0x12, 0x34, 0x56, 0x78]),
813 SpiTransaction::flush(),
814 ];
815 let mut spi = SpiMock::new(&expectations);
816
817 let cs = CsPinMock::default();
818 let delay = MockDelay {};
819 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
820
821 let mut spe = ADIN1110::new(spi_dev, false);
822
823 // Write reg: 0x1FFF
824 assert!(spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok());
825
826 spi.done();
827 }
828
829 #[futures_test::test]
830 async fn mac_write_registers_with_crc() {
831 // Configure expectations
832 let expectations = [
833 SpiTransaction::write_vec(vec![0xA0, 0x09, 39, 0x12, 0x34, 0x56, 0x78, 28]),
834 SpiTransaction::flush(),
835 ];
836
837 // Basic test init block
838 let mut spi = SpiMock::new(&expectations);
839 let cs = CsPinMock::default();
840 let delay = MockDelay {};
841 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
842 let mut spe = ADIN1110::new(spi_dev, true);
843
844 // Write reg: 0x1FFF
845 assert!(spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok());
846
847 spi.done();
848 }
849
850 #[futures_test::test]
851 async fn write_packet_to_fifo_minimal_with_crc() {
852 // Configure expectations
853 let mut expectations = vec![];
854
855 // Write TX_SIZE reg
856 expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201]));
857 expectations.push(SpiTransaction::flush());
858
859 // Write TX reg.
860 // SPI Header + optional CRC + Frame Header
861 expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));
862 // Packet data
863 let packet = [0xFF_u8; 60];
864 expectations.push(SpiTransaction::write_vec(packet.to_vec()));
865
866 let mut tail = std::vec::Vec::<u8>::with_capacity(100);
867 // Padding
868 if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) {
869 tail.resize(padding_len, 0x00);
870 }
871 // Packet FCS + optinal padding
872 tail.extend_from_slice(&[77, 241, 140, 244, DONT_CARE_BYTE, DONT_CARE_BYTE]);
873
874 expectations.push(SpiTransaction::write_vec(tail));
875 expectations.push(SpiTransaction::flush());
876
877 let mut spi = SpiMock::new(&expectations);
878
879 let cs = CsPinMock::default();
880 let delay = MockDelay {};
881 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
882
883 let mut spe = ADIN1110::new(spi_dev, true);
884
885 assert!(spe.write_fifo(&packet).await.is_ok());
886
887 spi.done();
888 }
889
890 #[futures_test::test]
891 async fn write_packet_to_fifo_max_mtu_with_crc() {
892 assert_eq!(MTU, 1514);
893 // Configure expectations
894 let mut expectations = vec![];
895
896 // Write TX_SIZE reg
897 expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 5, 240, 159]));
898 expectations.push(SpiTransaction::flush());
899
900 // Write TX reg.
901 // SPI Header + optional CRC + Frame Header
902 expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));
903 // Packet data
904 let packet = [0xAA_u8; MTU];
905 expectations.push(SpiTransaction::write_vec(packet.to_vec()));
906
907 let mut tail = std::vec::Vec::<u8>::with_capacity(100);
908 // Padding
909 if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) {
910 tail.resize(padding_len, 0x00);
911 }
912 // Packet FCS + optinal padding
913 tail.extend_from_slice(&[49, 196, 205, 160]);
914
915 expectations.push(SpiTransaction::write_vec(tail));
916 expectations.push(SpiTransaction::flush());
917
918 let mut spi = SpiMock::new(&expectations);
919
920 let cs = CsPinMock::default();
921 let delay = MockDelay {};
922 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
923
924 let mut spe = ADIN1110::new(spi_dev, true);
925
926 assert!(spe.write_fifo(&packet).await.is_ok());
927
928 spi.done();
929 }
930
931 #[futures_test::test]
932 async fn write_packet_to_fifo_invalid_lengths() {
933 assert_eq!(MTU, 1514);
934
935 // Configure expectations
936 let expectations = vec![];
937
938 // Max packet size = MAX_BUFF - FRAME_HEADER_LEN
939 let packet = [0xAA_u8; MAX_BUFF - FRAME_HEADER_LEN + 1];
940
941 let mut spi = SpiMock::new(&expectations);
942
943 let cs = CsPinMock::default();
944 let delay = MockDelay {};
945 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
946
947 let mut spe = ADIN1110::new(spi_dev, true);
948
949 // minimal
950 assert!(matches!(
951 spe.write_fifo(&packet[0..(6 + 6 + 2 - 1)]).await,
952 Err(AdinError::PACKET_TOO_SMALL)
953 ));
954
955 // max + 1
956 assert!(matches!(spe.write_fifo(&packet).await, Err(AdinError::PACKET_TOO_BIG)));
957
958 spi.done();
959 }
960
961 #[futures_test::test]
962 async fn write_packet_to_fifo_arp_46bytes_with_crc() {
963 // Configure expectations
964 let mut expectations = vec![];
965
966 // Write TX_SIZE reg
967 expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201]));
968 expectations.push(SpiTransaction::flush());
969
970 // Write TX reg.
971 // Header
972 expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));
973 // Packet data
974 let packet = [
975 34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154,
976 188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1,
977 ];
978 expectations.push(SpiTransaction::write_vec(packet.to_vec()));
979
980 let mut tail = std::vec::Vec::<u8>::with_capacity(100);
981 // Padding
982 if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) {
983 tail.resize(padding_len, 0x00);
984 }
985 // Packet FCS + optinal padding
986 tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]);
987
988 expectations.push(SpiTransaction::write_vec(tail));
989 expectations.push(SpiTransaction::flush());
990
991 let mut spi = SpiMock::new(&expectations);
992
993 let cs = CsPinMock::default();
994 let delay = MockDelay {};
995 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
996
997 let mut spe = ADIN1110::new(spi_dev, true);
998
999 assert!(spe.write_fifo(&packet).await.is_ok());
1000
1001 spi.done();
1002 }
1003
1004 #[futures_test::test]
1005 async fn write_packet_to_fifo_arp_46bytes_without_crc() {
1006 // Configure expectations
1007 let mut expectations = vec![];
1008
1009 // Write TX_SIZE reg
1010 expectations.push(SpiTransaction::write_vec(vec![160, 48, 0, 0, 0, 66]));
1011 expectations.push(SpiTransaction::flush());
1012
1013 // Write TX reg.
1014 // SPI Header + Frame Header
1015 expectations.push(SpiTransaction::write_vec(vec![160, 49, 0, 0]));
1016 // Packet data
1017 let packet = [
1018 34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154,
1019 188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1,
1020 ];
1021 expectations.push(SpiTransaction::write_vec(packet.to_vec()));
1022
1023 let mut tail = std::vec::Vec::<u8>::with_capacity(100);
1024 // Padding
1025 if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) {
1026 tail.resize(padding_len, 0x00);
1027 }
1028 // Packet FCS + optinal padding
1029 tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]);
1030
1031 expectations.push(SpiTransaction::write_vec(tail));
1032 expectations.push(SpiTransaction::flush());
1033
1034 let mut spi = SpiMock::new(&expectations);
1035
1036 let cs = CsPinMock::default();
1037 let delay = MockDelay {};
1038 let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay);
1039
1040 let mut spe = ADIN1110::new(spi_dev, false);
1041
1042 assert!(spe.write_fifo(&packet).await.is_ok());
1043
1044 spi.done();
1045 }
1046}
diff --git a/embassy-net-adin1110/src/mdio.rs b/embassy-net-adin1110/src/mdio.rs
new file mode 100644
index 000000000..68477006a
--- /dev/null
+++ b/embassy-net-adin1110/src/mdio.rs
@@ -0,0 +1,175 @@
1/// PHY Address: (0..=0x1F), 5-bits long.
2#[allow(dead_code)]
3type PhyAddr = u8;
4
5/// PHY Register: (0..=0x1F), 5-bits long.
6#[allow(dead_code)]
7type RegC22 = u8;
8
9/// PHY Register Clause 45.
10#[allow(dead_code)]
11type RegC45 = u16;
12
13/// PHY Register Value
14#[allow(dead_code)]
15type RegVal = u16;
16
17#[allow(dead_code)]
18const REG13: RegC22 = 13;
19#[allow(dead_code)]
20const REG14: RegC22 = 14;
21
22#[allow(dead_code)]
23const PHYADDR_MASK: u8 = 0x1f;
24#[allow(dead_code)]
25const DEV_MASK: u8 = 0x1f;
26
27#[allow(dead_code)]
28#[repr(u16)]
29enum Reg13Op {
30 Addr = 0b00 << 14,
31 Write = 0b01 << 14,
32 PostReadIncAddr = 0b10 << 14,
33 Read = 0b11 << 14,
34}
35/// `MdioBus` trait
36/// Driver needs to implement the Clause 22
37/// Optional Clause 45 is the device supports this.
38///
39/// Claus 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf>
40pub trait MdioBus {
41 type Error;
42
43 /// Read, Clause 22
44 async fn read_cl22(&mut self, phy_id: PhyAddr, reg: RegC22) -> Result<RegVal, Self::Error>;
45
46 /// Write, Clause 22
47 async fn write_cl22(&mut self, phy_id: PhyAddr, reg: RegC22, reg_val: RegVal) -> Result<(), Self::Error>;
48
49 /// Read, Clause 45
50 /// This is the default implementation.
51 /// Many hardware these days support direct Clause 45 operations.
52 /// Implement this function when your hardware supports it.
53 async fn read_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45)) -> Result<RegVal, Self::Error> {
54 // Write FN
55 let val = (Reg13Op::Addr as RegVal) | RegVal::from(regc45.0 & DEV_MASK);
56
57 self.write_cl22(phy_id, REG13, val).await?;
58 // Write Addr
59 self.write_cl22(phy_id, REG14, regc45.1).await?;
60
61 // Write FN
62 let val = (Reg13Op::Read as RegVal) | RegVal::from(regc45.0 & DEV_MASK);
63 self.write_cl22(phy_id, REG13, val).await?;
64 // Write Addr
65 self.read_cl22(phy_id, REG14).await
66 }
67
68 /// Write, Clause 45
69 /// This is the default implementation.
70 /// Many hardware these days support direct Clause 45 operations.
71 /// Implement this function when your hardware supports it.
72 async fn write_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45), reg_val: RegVal) -> Result<(), Self::Error> {
73 let dev_addr = RegVal::from(regc45.0 & DEV_MASK);
74 let reg = regc45.1;
75
76 // Write FN
77 let val = (Reg13Op::Addr as RegVal) | dev_addr;
78 self.write_cl22(phy_id, REG13, val).await?;
79 // Write Addr
80 self.write_cl22(phy_id, REG14, reg).await?;
81
82 // Write FN
83 let val = (Reg13Op::Write as RegVal) | dev_addr;
84 self.write_cl22(phy_id, REG13, val).await?;
85 // Write Addr
86 self.write_cl22(phy_id, REG14, reg_val).await
87 }
88}
89
90// #[cfg(test)]
91// mod tests {
92// use core::convert::Infallible;
93
94// use super::{MdioBus, PhyAddr, RegC22, RegVal};
95
96// #[derive(Debug, PartialEq, Eq)]
97// enum A {
98// Read(PhyAddr, RegC22),
99// Write(PhyAddr, RegC22, RegVal),
100// }
101
102// struct MockMdioBus(Vec<A>);
103
104// impl MockMdioBus {
105// pub fn clear(&mut self) {
106// self.0.clear();
107// }
108// }
109
110// impl MdioBus for MockMdioBus {
111// type Error = Infallible;
112
113// fn write_cl22(
114// &mut self,
115// phy_id: super::PhyAddr,
116// reg: super::RegC22,
117// reg_val: super::RegVal,
118// ) -> Result<(), Self::Error> {
119// self.0.push(A::Write(phy_id, reg, reg_val));
120// Ok(())
121// }
122
123// fn read_cl22(
124// &mut self,
125// phy_id: super::PhyAddr,
126// reg: super::RegC22,
127// ) -> Result<super::RegVal, Self::Error> {
128// self.0.push(A::Read(phy_id, reg));
129// Ok(0)
130// }
131// }
132
133// #[test]
134// fn read_test() {
135// let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
136
137// mdiobus.clear();
138// mdiobus.read_cl22(0x01, 0x00).unwrap();
139// assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]);
140
141// mdiobus.clear();
142// mdiobus.read_cl45(0x01, (0xBB, 0x1234)).unwrap();
143// assert_eq!(
144// mdiobus.0,
145// vec![
146// #[allow(clippy::identity_op)]
147// A::Write(0x01, 13, (0b00 << 14) | 27),
148// A::Write(0x01, 14, 0x1234),
149// A::Write(0x01, 13, (0b11 << 14) | 27),
150// A::Read(0x01, 14)
151// ]
152// );
153// }
154
155// #[test]
156// fn write_test() {
157// let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
158
159// mdiobus.clear();
160// mdiobus.write_cl22(0x01, 0x00, 0xABCD).unwrap();
161// assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]);
162
163// mdiobus.clear();
164// mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).unwrap();
165// assert_eq!(
166// mdiobus.0,
167// vec![
168// A::Write(0x01, 13, 27),
169// A::Write(0x01, 14, 0x1234),
170// A::Write(0x01, 13, (0b01 << 14) | 27),
171// A::Write(0x01, 14, 0xABCD)
172// ]
173// );
174// }
175// }
diff --git a/embassy-net-adin1110/src/phy.rs b/embassy-net-adin1110/src/phy.rs
new file mode 100644
index 000000000..176ad019b
--- /dev/null
+++ b/embassy-net-adin1110/src/phy.rs
@@ -0,0 +1,142 @@
1use crate::mdio::MdioBus;
2
3#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
4#[repr(u8)]
5/// Clause 22 Registers
6pub enum RegsC22 {
7 /// MII Control Register
8 CONTROL = 0x00,
9 /// MII Status Register
10 STATUS = 0x01,
11 /// PHY Identifier 1 Register
12 PHY_ID1 = 0x02,
13 /// PHY Identifier 2 Register.
14 PHY_ID2 = 0x03,
15}
16
17/// Clause 45 Registers
18#[allow(non_snake_case, dead_code)]
19pub mod RegsC45 {
20 /// Device Address: 0x01
21 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
22 #[repr(u16)]
23 pub enum DA1 {
24 /// PMA/PMD Control 1 Register
25 PMA_PMD_CNTRL1 = 0x0000,
26 /// PMA/PMD Status 1 Register
27 PMA_PMD_STAT1 = 0x0001,
28 /// MSE Value Register
29 MSE_VAL = 0x830B,
30 }
31
32 impl DA1 {
33 #[must_use]
34 pub fn into(self) -> (u8, u16) {
35 (0x01, self as u16)
36 }
37 }
38
39 /// Device Address: 0x03
40 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
41 #[repr(u16)]
42 pub enum DA3 {
43 /// PCS Control 1 Register
44 PCS_CNTRL1 = 0x0000,
45 /// PCS Status 1 Register
46 PCS_STAT1 = 0x0001,
47 /// PCS Status 2 Register
48 PCS_STAT2 = 0x0008,
49 }
50
51 impl DA3 {
52 #[must_use]
53 pub fn into(self) -> (u8, u16) {
54 (0x03, self as u16)
55 }
56 }
57
58 /// Device Address: 0x07
59 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
60 #[repr(u16)]
61 pub enum DA7 {
62 /// Extra Autonegotiation Status Register
63 AN_STATUS_EXTRA = 0x8001,
64 }
65
66 impl DA7 {
67 #[must_use]
68 pub fn into(self) -> (u8, u16) {
69 (0x07, self as u16)
70 }
71 }
72
73 /// Device Address: 0x1E
74 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
75 #[repr(u16)]
76 pub enum DA1E {
77 /// System Interrupt Status Register
78 CRSM_IRQ_STATUS = 0x0010,
79 /// System Interrupt Mask Register
80 CRSM_IRQ_MASK = 0x0020,
81 /// Pin Mux Configuration 1 Register
82 DIGIO_PINMUX = 0x8c56,
83 /// LED Control Register.
84 LED_CNTRL = 0x8C82,
85 /// LED Polarity Register
86 LED_POLARITY = 0x8C83,
87 }
88
89 impl DA1E {
90 #[must_use]
91 pub fn into(self) -> (u8, u16) {
92 (0x1e, self as u16)
93 }
94 }
95
96 /// Device Address: 0x1F
97 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
98 #[repr(u16)]
99 pub enum DA1F {
100 /// PHY Subsystem Interrupt Status Register
101 PHY_SYBSYS_IRQ_STATUS = 0x0011,
102 /// PHY Subsystem Interrupt Mask Register
103 PHY_SYBSYS_IRQ_MASK = 0x0021,
104 }
105
106 impl DA1F {
107 #[must_use]
108 pub fn into(self) -> (u8, u16) {
109 (0x1f, self as u16)
110 }
111 }
112}
113
114pub struct Phy10BaseT1x(u8);
115
116impl Default for Phy10BaseT1x {
117 fn default() -> Self {
118 Self(0x01)
119 }
120}
121
122impl Phy10BaseT1x {
123 /// Get the both parts of the PHYID.
124 pub async fn get_id<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u32, MDE>
125 where
126 MDIOBUS: MdioBus<Error = MDE>,
127 MDE: core::fmt::Debug,
128 {
129 let mut phyid = u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID1 as u8).await?) << 16;
130 phyid |= u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID2 as u8).await?);
131 Ok(phyid)
132 }
133
134 /// Get the Mean Squared Error Value.
135 pub async fn get_sqi<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u16, MDE>
136 where
137 MDIOBUS: MdioBus<Error = MDE>,
138 MDE: core::fmt::Debug,
139 {
140 mdiobus.read_cl45(self.0, RegsC45::DA1::MSE_VAL.into()).await
141 }
142}
diff --git a/embassy-net-adin1110/src/regs.rs b/embassy-net-adin1110/src/regs.rs
new file mode 100644
index 000000000..4557929f0
--- /dev/null
+++ b/embassy-net-adin1110/src/regs.rs
@@ -0,0 +1,408 @@
1use bitfield::{bitfield, bitfield_bitrange, bitfield_fields};
2
3#[allow(non_camel_case_types)]
4#[derive(Debug, Copy, Clone)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6#[repr(u16)]
7/// SPI REGISTER DETAILS
8/// Table 38.
9pub enum SpiRegisters {
10 IDVER = 0x00,
11 PHYID = 0x01,
12 CAPABILITY = 0x02,
13 RESET = 0x03,
14 CONFIG0 = 0x04,
15 CONFIG2 = 0x06,
16 STATUS0 = 0x08,
17 STATUS1 = 0x09,
18 IMASK0 = 0x0C,
19 IMASK1 = 0x0D,
20 MDIO_ACC = 0x20,
21 TX_FSIZE = 0x30,
22 TX = 0x31,
23 TX_SPACE = 0x32,
24 FIFO_CLR = 0x36,
25 ADDR_FILT_UPR0 = 0x50,
26 ADDR_FILT_LWR0 = 0x51,
27 ADDR_FILT_UPR1 = 0x52,
28 ADDR_FILT_LWR1 = 0x53,
29 ADDR_MSK_LWR0 = 0x70,
30 ADDR_MSK_UPR0 = 0x71,
31 ADDR_MSK_LWR1 = 0x72,
32 ADDR_MSK_UPR1 = 0x73,
33 RX_FSIZE = 0x90,
34 RX = 0x91,
35}
36
37impl From<SpiRegisters> for u16 {
38 fn from(val: SpiRegisters) -> Self {
39 val as u16
40 }
41}
42
43impl From<u16> for SpiRegisters {
44 fn from(value: u16) -> Self {
45 match value {
46 0x00 => Self::IDVER,
47 0x01 => Self::PHYID,
48 0x02 => Self::CAPABILITY,
49 0x03 => Self::RESET,
50 0x04 => Self::CONFIG0,
51 0x06 => Self::CONFIG2,
52 0x08 => Self::STATUS0,
53 0x09 => Self::STATUS1,
54 0x0C => Self::IMASK0,
55 0x0D => Self::IMASK1,
56 0x20 => Self::MDIO_ACC,
57 0x30 => Self::TX_FSIZE,
58 0x31 => Self::TX,
59 0x32 => Self::TX_SPACE,
60 0x36 => Self::FIFO_CLR,
61 0x50 => Self::ADDR_FILT_UPR0,
62 0x51 => Self::ADDR_FILT_LWR0,
63 0x52 => Self::ADDR_FILT_UPR1,
64 0x53 => Self::ADDR_FILT_LWR1,
65 0x70 => Self::ADDR_MSK_LWR0,
66 0x71 => Self::ADDR_MSK_UPR0,
67 0x72 => Self::ADDR_MSK_LWR1,
68 0x73 => Self::ADDR_MSK_UPR1,
69 0x90 => Self::RX_FSIZE,
70 0x91 => Self::RX,
71 e => panic!("Unknown value {e}"),
72 }
73 }
74}
75
76// Register definitions
77bitfield! {
78 /// Status0 Register bits
79 pub struct Status0(u32);
80 impl Debug;
81 u32;
82 /// Control Data Protection Error
83 pub cdpe, _ : 12;
84 /// Transmit Frame Check Squence Error
85 pub txfcse, _: 11;
86 /// Transmit Time Stamp Capture Available C
87 pub ttscac, _ : 10;
88 /// Transmit Time Stamp Capture Available B
89 pub ttscab, _ : 9;
90 /// Transmit Time Stamp Capture Available A
91 pub ttscaa, _ : 8;
92 /// PHY Interrupt for Port 1
93 pub phyint, _ : 7;
94 /// Reset Complete
95 pub resetc, _ : 6;
96 /// Header error
97 pub hdre, _ : 5;
98 /// Loss of Frame Error
99 pub lofe, _ : 4;
100 /// Receiver Buffer Overflow Error
101 pub rxboe, _ : 3;
102 /// Host Tx FIFO Under Run Error
103 pub txbue, _ : 2;
104 /// Host Tx FIFO Overflow
105 pub txboe, _ : 1;
106 /// Transmit Protocol Error
107 pub txpe, _ : 0;
108}
109
110bitfield! {
111 /// Status1 Register bits
112 pub struct Status1(u32);
113 impl Debug;
114 u32;
115 /// ECC Error on Reading the Frame Size from a Tx FIFO
116 pub tx_ecc_err, set_tx_ecc_err: 12;
117 /// ECC Error on Reading the Frame Size from an Rx FIFO
118 pub rx_ecc_err, set_rx_ecc_err : 11;
119 /// Detected an Error on an SPI Transaction
120 pub spi_err, set_spi_err: 10;
121 /// Rx MAC Interframe Gap Error
122 pub p1_rx_ifg_err, set_p1_rx_ifg_err : 8;
123 /// Port1 Rx Ready High Priority
124 pub p1_rx_rdy_hi, set_p1_rx_rdy_hi : 5;
125 /// Port 1 Rx FIFO Contains Data
126 pub p1_rx_rdy, set_p1_rx_rdy : 4;
127 /// Tx Ready
128 pub tx_rdy, set_tx_rdy : 3;
129 /// Link Status Changed
130 pub link_change, set_link_change : 1;
131 /// Port 1 Link Status
132 pub p1_link_status, _ : 0;
133}
134
135bitfield! {
136 /// Config0 Register bits
137 pub struct Config0(u32);
138 impl Debug;
139 u32;
140 /// Configuration Synchronization
141 pub sync, set_sync : 15;
142 /// Transmit Frame Check Sequence Validation Enable
143 pub txfcsve, set_txfcsve : 14;
144 /// !CS Align Receive Frame Enable
145 pub csarfe, set_csarfe : 13;
146 /// Zero Align Receive Frame Enable
147 pub zarfe, set_zarfe : 12;
148 /// Transmit Credit Threshold
149 pub tcxthresh, set_tcxthresh : 11, 10;
150 /// Transmit Cut Through Enable
151 pub txcte, set_txcte : 9;
152 /// Receive Cut Through Enable
153 pub rxcte, set_rxcte : 8;
154 /// Frame Time Stamp Enable
155 pub ftse, set_ftse : 7;
156 /// Receive Frame Time Stamp Select
157 pub ftss, set_ftss : 6;
158 /// Enable Control Data Read Write Protection
159 pub prote, set_prote : 5;
160 /// Enable TX Data Chunk Sequence and Retry
161 pub seqe, set_seqe : 4;
162 /// Chunk Payload Selector (N).
163 pub cps, set_cps : 2, 0;
164}
165
166bitfield! {
167 /// Config2 Register bits
168 pub struct Config2(u32);
169 impl Debug;
170 u32;
171 /// Assert TX_RDY When the Tx FIFO is Empty
172 pub tx_rdy_on_empty, set_tx_rdy_on_empty : 8;
173 /// Determines If the SFD is Detected in the PHY or MAC
174 pub sdf_detect_src, set_sdf_detect_src : 7;
175 /// Statistics Clear on Reading
176 pub stats_clr_on_rd, set_stats_clr_on_rd : 6;
177 /// Enable CRC Append
178 pub crc_append, set_crc_append : 5;
179 /// Admit Frames with IFG Errors on Port 1 (P1)
180 pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4;
181 /// Forward Frames Not Matching Any MAC Address to the Host
182 pub p1_fwd_unk2host, set_p1_fwd_unk2host : 2;
183 /// SPI to MDIO Bridge MDC Clock Speed
184 pub mspeed, set_mspeed : 0;
185}
186
187bitfield! {
188 /// IMASK0 Register bits
189 pub struct IMask0(u32);
190 impl Debug;
191 u32;
192 /// Control Data Protection Error Mask
193 pub cppem, set_cppem : 12;
194 /// Transmit Frame Check Sequence Error Mask
195 pub txfcsem, set_txfcsem : 11;
196 /// Transmit Time Stamp Capture Available C Mask
197 pub ttscacm, set_ttscacm : 10;
198 /// Transmit Time Stamp Capture Available B Mask
199 pub ttscabm, set_ttscabm : 9;
200 /// Transmit Time Stamp Capture Available A Mask
201 pub ttscaam, set_ttscaam : 8;
202 /// Physical Layer Interrupt Mask
203 pub phyintm, set_phyintm : 7;
204 /// RESET Complete Mask
205 pub resetcm, set_resetcm : 6;
206 /// Header Error Mask
207 pub hdrem, set_hdrem : 5;
208 /// Loss of Frame Error Mask
209 pub lofem, set_lofem : 4;
210 /// Receive Buffer Overflow Error Mask
211 pub rxboem, set_rxboem : 3;
212 /// Transmit Buffer Underflow Error Mask
213 pub txbuem, set_txbuem : 2;
214 /// Transmit Buffer Overflow Error Mask
215 pub txboem, set_txboem : 1;
216 /// Transmit Protocol Error Mask
217 pub txpem, set_txpem : 0;
218}
219
220bitfield! {
221 /// IMASK1 Register bits
222 pub struct IMask1(u32);
223 impl Debug;
224 u32;
225 /// Mask Bit for TXF_ECC_ERR
226 pub tx_ecc_err_mask, set_tx_ecc_err_mask : 12;
227 /// Mask Bit for RXF_ECC_ERR
228 pub rx_ecc_err_mask, set_rx_ecc_err_mask : 11;
229 /// Mask Bit for SPI_ERR
230 /// This field is only used with the generic SPI protocol
231 pub spi_err_mask, set_spi_err_mask : 10;
232 /// Mask Bit for RX_IFG_ERR
233 pub p1_rx_ifg_err_mask, set_p1_rx_ifg_err_mask : 8;
234 /// Mask Bit for P1_RX_RDY
235 /// This field is only used with the generic SPI protocol
236 pub p1_rx_rdy_mask, set_p1_rx_rdy_mask : 4;
237 /// Mask Bit for TX_FRM_DONE
238 /// This field is only used with the generic SPI protocol
239 pub tx_rdy_mask, set_tx_rdy_mask : 3;
240 /// Mask Bit for LINK_CHANGE
241 pub link_change_mask, set_link_change_mask : 1;
242}
243
244/// LED Functions
245#[repr(u8)]
246pub enum LedFunc {
247 LinkupTxRxActicity = 0,
248 LinkupTxActicity,
249 LinkupRxActicity,
250 LinkupOnly,
251 TxRxActivity,
252 TxActivity,
253 RxActivity,
254 LinkupRxEr,
255 LinkupRxTxEr,
256 RxEr,
257 RxTxEr,
258 TxSop,
259 RxSop,
260 On,
261 Off,
262 Blink,
263 TxLevel2P4,
264 TxLevel1P0,
265 Master,
266 Slave,
267 IncompatiableLinkCfg,
268 AnLinkGood,
269 AnComplete,
270 TsTimer,
271 LocRcvrStatus,
272 RemRcvrStatus,
273 Clk25Ref,
274 TxTCLK,
275 Clk120MHz,
276}
277
278impl From<LedFunc> for u8 {
279 fn from(val: LedFunc) -> Self {
280 val as u8
281 }
282}
283
284impl From<u8> for LedFunc {
285 fn from(value: u8) -> Self {
286 match value {
287 0 => LedFunc::LinkupTxRxActicity,
288 1 => LedFunc::LinkupTxActicity,
289 2 => LedFunc::LinkupRxActicity,
290 3 => LedFunc::LinkupOnly,
291 4 => LedFunc::TxRxActivity,
292 5 => LedFunc::TxActivity,
293 6 => LedFunc::RxActivity,
294 7 => LedFunc::LinkupRxEr,
295 8 => LedFunc::LinkupRxTxEr,
296 9 => LedFunc::RxEr,
297 10 => LedFunc::RxTxEr,
298 11 => LedFunc::TxSop,
299 12 => LedFunc::RxSop,
300 13 => LedFunc::On,
301 14 => LedFunc::Off,
302 15 => LedFunc::Blink,
303 16 => LedFunc::TxLevel2P4,
304 17 => LedFunc::TxLevel1P0,
305 18 => LedFunc::Master,
306 19 => LedFunc::Slave,
307 20 => LedFunc::IncompatiableLinkCfg,
308 21 => LedFunc::AnLinkGood,
309 22 => LedFunc::AnComplete,
310 23 => LedFunc::TsTimer,
311 24 => LedFunc::LocRcvrStatus,
312 25 => LedFunc::RemRcvrStatus,
313 26 => LedFunc::Clk25Ref,
314 27 => LedFunc::TxTCLK,
315 28 => LedFunc::Clk120MHz,
316 e => panic!("Invalid value {e}"),
317 }
318 }
319}
320
321/// LED Control Register
322#[derive(Copy, Clone, PartialEq, Eq, Hash)]
323pub struct LedCntrl(pub u16);
324bitfield_bitrange! {struct LedCntrl(u16)}
325
326impl LedCntrl {
327 bitfield_fields! {
328 u8;
329 /// LED 0 Pin Function
330 pub from into LedFunc, led0_function, set_led0_function: 4, 0;
331 /// LED 0 Mode Selection
332 pub led0_mode, set_led0_mode: 5;
333 /// Qualify Certain LED 0 Options with Link Status.
334 pub led0_link_st_qualify, set_led0_link_st_qualify: 6;
335 /// LED 0 Enable
336 pub led0_en, set_led0_en: 7;
337 /// LED 1 Pin Function
338 pub from into LedFunc, led1_function, set_led1_function: 12, 8;
339 /// /// LED 1 Mode Selection
340 pub led1_mode, set_led1_mode: 13;
341 /// Qualify Certain LED 1 Options with Link Status.
342 pub led1_link_st_qualify, set_led1_link_st_qualify: 14;
343 /// LED 1 Enable
344 pub led1_en, set_led1_en: 15;
345 }
346
347 pub fn new() -> Self {
348 LedCntrl(0)
349 }
350}
351
352// LED Polarity
353#[repr(u8)]
354pub enum LedPol {
355 AutoSense = 0,
356 ActiveHigh,
357 ActiveLow,
358}
359
360impl From<LedPol> for u8 {
361 fn from(val: LedPol) -> Self {
362 val as u8
363 }
364}
365
366impl From<u8> for LedPol {
367 fn from(value: u8) -> Self {
368 match value {
369 0 => LedPol::AutoSense,
370 1 => LedPol::ActiveHigh,
371 2 => LedPol::ActiveLow,
372 e => panic!("Invalid value {e}"),
373 }
374 }
375}
376
377/// LED Control Register
378#[derive(Copy, Clone, PartialEq, Eq, Hash)]
379pub struct LedPolarity(pub u16);
380bitfield_bitrange! {struct LedPolarity(u16)}
381
382impl LedPolarity {
383 bitfield_fields! {
384 u8;
385 /// LED 1 Polarity
386 pub from into LedPol, led1_polarity, set_led1_polarity: 3, 2;
387 /// LED 0 Polarity
388 pub from into LedPol, led0_polarity, set_led0_polarity: 1, 0;
389 }
390}
391
392/// SPI Header
393#[derive(Copy, Clone, PartialEq, Eq, Hash)]
394pub struct SpiHeader(pub u16);
395bitfield_bitrange! {struct SpiHeader(u16)}
396
397impl SpiHeader {
398 bitfield_fields! {
399 u16;
400 /// Mask Bit for TXF_ECC_ERR
401 pub control, set_control : 15;
402 pub full_duplex, set_full_duplex : 14;
403 /// Read or Write to register
404 pub write, set_write : 13;
405 /// Registers ID/addr
406 pub from into SpiRegisters, addr, set_addr: 11, 0;
407 }
408}
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml
index 36e74e5a5..db3a7ceff 100644
--- a/examples/stm32l4/.cargo/config.toml
+++ b/examples/stm32l4/.cargo/config.toml
@@ -2,7 +2,7 @@
2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3#runner = "probe-rs run --chip STM32L475VGT6" 3#runner = "probe-rs run --chip STM32L475VGT6"
4#runner = "probe-rs run --chip STM32L475VG" 4#runner = "probe-rs run --chip STM32L475VG"
5runner = "probe-rs run --chip STM32L4S5VI" 5runner = "probe-run --chip STM32L4S5QI"
6 6
7[build] 7[build]
8target = "thumbv7em-none-eabi" 8target = "thumbv7em-none-eabi"
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index f552a6109..e5be94eda 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -6,12 +6,17 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32l4s5vi to your chip name, if necessary. 8# Change stm32l4s5vi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "memory-x", "time-driver-any", "exti", "unstable-traits", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "unstable-traits", "chrono"] }
10embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", "unstable-traits", "nightly"] }
13embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net-adin1110 = { version = "0.1.0", path = "../../embassy-net-adin1110", default-features = false }
16embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embedded-io-async = { version = "0.5.0", features = ["defmt-03"] }
19embedded-io = { version = "0.5.0", features = ["defmt-03"] }
15 20
16defmt = "0.3" 21defmt = "0.3"
17defmt-rtt = "0.4" 22defmt-rtt = "0.4"
@@ -21,10 +26,13 @@ cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 26embedded-hal = "0.2.6"
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } 27embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
23embedded-hal-async = { version = "=1.0.0-rc.1" } 28embedded-hal-async = { version = "=1.0.0-rc.1" }
29embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
24panic-probe = { version = "0.3", features = ["print-defmt"] } 30panic-probe = { version = "0.3", features = ["print-defmt"] }
25futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 31futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
26heapless = { version = "0.7.5", default-features = false } 32heapless = { version = "0.7.5", default-features = false }
27chrono = { version = "^0.4", default-features = false } 33chrono = { version = "^0.4", default-features = false }
34rand = { version = "0.8.5", default-features = false }
35static_cell = {version = "1.1", features = ["nightly"]}
28 36
29micromath = "2.0.0" 37micromath = "2.0.0"
30 38
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
new file mode 100644
index 000000000..148c58771
--- /dev/null
+++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
@@ -0,0 +1,438 @@
1#![deny(clippy::pedantic)]
2#![allow(clippy::doc_markdown)]
3#![no_main]
4#![no_std]
5// Needed unitl https://github.com/rust-lang/rust/issues/63063 is stablised.
6#![feature(type_alias_impl_trait)]
7#![feature(associated_type_bounds)]
8#![allow(clippy::missing_errors_doc)]
9
10// This example works on a ANALOG DEVICE EVAL-ADIN110EBZ board.
11// Settings switch S201 "HW CFG":
12// - Without SPI CRC: OFF-ON-OFF-OFF-OFF
13// - With SPI CRC: ON -ON-OFF-OFF-OFF
14// Settings switch S303 "uC CFG": CFG0: On = static ip, Off = Dhcp
15// The webserver shows the actual temperature of the onboard i2c temp sensor.
16
17use core::marker::PhantomData;
18use core::sync::atomic::{AtomicI32, Ordering};
19
20use defmt::{error, info, println, unwrap, Format};
21use defmt_rtt as _; // global logger
22use embassy_executor::Spawner;
23use embassy_futures::select::{select, Either};
24use embassy_futures::yield_now;
25use embassy_net::tcp::TcpSocket;
26use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4};
27use embassy_time::{Delay, Duration, Ticker, Timer};
28use embedded_hal_async::i2c::I2c as I2cBus;
29use embedded_io::Write as bWrite;
30use embedded_io_async::Write;
31use hal::gpio::{Input, Level, Output, Speed};
32use hal::i2c::{self, I2c};
33use hal::rcc::{self};
34use hal::rng::{self, Rng};
35use hal::{bind_interrupts, exti, pac, peripherals};
36use heapless::Vec;
37use rand::RngCore;
38use static_cell::make_static;
39use {embassy_stm32 as hal, panic_probe as _};
40
41bind_interrupts!(struct Irqs {
42 I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
43 RNG => rng::InterruptHandler<peripherals::RNG>;
44});
45
46use embassy_net_adin1110::{self, Device, Runner, ADIN1110};
47use embedded_hal_bus::spi::ExclusiveDevice;
48use hal::gpio::Pull;
49use hal::i2c::Config as I2C_Config;
50use hal::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
51use hal::spi::{Config as SPI_Config, Spi};
52use hal::time::Hertz;
53
54// Basic settings
55// MAC-address used by the adin1110
56const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
57// Static IP settings
58const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24);
59// Listen port for the webserver
60const HTTP_LISTEN_PORT: u16 = 80;
61
62pub type SpeSpi = Spi<'static, peripherals::SPI2, peripherals::DMA1_CH1, peripherals::DMA1_CH2>;
63pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static, peripherals::PB12>, Delay>;
64pub type SpeInt = exti::ExtiInput<'static, peripherals::PB11>;
65pub type SpeRst = Output<'static, peripherals::PC7>;
66pub type Adin1110T = ADIN1110<SpeSpiCs>;
67pub type TempSensI2c = I2c<'static, peripherals::I2C3, peripherals::DMA1_CH6, peripherals::DMA1_CH7>;
68
69static TEMP: AtomicI32 = AtomicI32::new(0);
70
71#[embassy_executor::main]
72async fn main(spawner: Spawner) {
73 defmt::println!("Start main()");
74
75 let mut config = embassy_stm32::Config::default();
76
77 // 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2)
78 // 80MHz highest frequency for flash 0 wait.
79 config.rcc.mux = ClockSrc::PLL(
80 PLLSource::HSE(Hertz(8_000_000)),
81 PLLClkDiv::Div2,
82 PLLSrcDiv::Div1,
83 PLLMul::Mul20,
84 None,
85 );
86 config.rcc.hsi48 = true; // needed for rng
87 config.rcc.rtc_mux = rcc::RtcClockSource::LSI32;
88
89 let dp = embassy_stm32::init(config);
90
91 // RM0432rev9, 5.1.2: Independent I/O supply rail
92 // After reset, the I/Os supplied by VDDIO2 are logically and electrically isolated and
93 // therefore are not available. The isolation must be removed before using any I/O from
94 // PG[15:2], by setting the IOSV bit in the PWR_CR2 register, once the VDDIO2 supply is present
95 pac::PWR.cr2().modify(|w| w.set_iosv(true));
96
97 let reset_status = pac::RCC.bdcr().read().0;
98 defmt::println!("bdcr before: 0x{:X}", reset_status);
99
100 defmt::println!("Setup IO pins");
101
102 // Setup LEDs
103 let _led_uc1_green = Output::new(dp.PC13, Level::Low, Speed::Low);
104 let mut led_uc2_red = Output::new(dp.PE2, Level::High, Speed::Low);
105 let led_uc3_yellow = Output::new(dp.PE6, Level::High, Speed::Low);
106 let led_uc4_blue = Output::new(dp.PG15, Level::High, Speed::Low);
107
108 // Read the uc_cfg switches
109 let uc_cfg0 = Input::new(dp.PB2, Pull::None);
110 let _uc_cfg1 = Input::new(dp.PF11, Pull::None);
111 let _uc_cfg2 = Input::new(dp.PG6, Pull::None);
112 let _uc_cfg3 = Input::new(dp.PG11, Pull::None);
113
114 // Setup I2C pins
115 let temp_sens_i2c = I2c::new(
116 dp.I2C3,
117 dp.PG7,
118 dp.PG8,
119 Irqs,
120 dp.DMA1_CH6,
121 dp.DMA1_CH7,
122 Hertz(100_000),
123 I2C_Config::default(),
124 );
125
126 // Setup IO and SPI for the SPE chip
127 let spe_reset_n = Output::new(dp.PC7, Level::Low, Speed::Low);
128 let spe_cfg0 = Input::new(dp.PC8, Pull::None);
129 let spe_cfg1 = Input::new(dp.PC9, Pull::None);
130 let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low);
131
132 let spe_int = Input::new(dp.PB11, Pull::None);
133 let spe_int = exti::ExtiInput::new(spe_int, dp.EXTI11);
134
135 let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High);
136 let spe_spi_sclk = dp.PB13;
137 let spe_spi_miso = dp.PB14;
138 let spe_spi_mosi = dp.PB15;
139
140 // Don't turn the clock to high, clock must fit within the system clock as we get a runtime panic.
141 let mut spi_config = SPI_Config::default();
142 spi_config.frequency = Hertz(25_000_000);
143
144 let spe_spi: SpeSpi = Spi::new(
145 dp.SPI2,
146 spe_spi_sclk,
147 spe_spi_mosi,
148 spe_spi_miso,
149 dp.DMA1_CH1,
150 dp.DMA1_CH2,
151 spi_config,
152 );
153 let spe_spi = SpeSpiCs::new(spe_spi, spe_spi_cs_n, Delay);
154
155 let cfg0_without_crc = spe_cfg0.is_high();
156 let cfg1_spi_mode = spe_cfg1.is_high();
157
158 defmt::println!(
159 "ADIN1110: CFG SPI-MODE 1-{}, CRC-bit 0-{}",
160 cfg1_spi_mode,
161 cfg0_without_crc
162 );
163
164 // Check the SPI mode selected with the "HW CFG" dip-switch
165 if !cfg1_spi_mode {
166 error!("Driver doesn´t support SPI Protolcol \"OPEN Alliance\".\nplease use the \"Generic SPI\"! Turn On \"HW CFG\": \"SPI_CFG1\"");
167 loop {
168 led_uc2_red.toggle();
169 Timer::after(Duration::from_hz(10)).await;
170 }
171 };
172
173 let state = make_static!(embassy_net_adin1110::State::<8, 8>::new());
174
175 let (device, runner) =
176 embassy_net_adin1110::new(MAC, state, spe_spi, spe_int, spe_reset_n, !cfg0_without_crc).await;
177
178 // Start task blink_led
179 unwrap!(spawner.spawn(heartbeat_led(led_uc3_yellow)));
180 // Start task temperature measurement
181 unwrap!(spawner.spawn(temp_task(temp_sens_i2c, led_uc4_blue)));
182 // Start ethernet task
183 unwrap!(spawner.spawn(ethernet_task(runner)));
184
185 let mut rng = Rng::new(dp.RNG, Irqs);
186 // Generate random seed
187 let seed = rng.next_u64();
188
189 let ip_cfg = if uc_cfg0.is_low() {
190 println!("Waiting for DHCP...");
191 let dhcp4_config = embassy_net::DhcpConfig::default();
192 embassy_net::Config::dhcpv4(dhcp4_config)
193 } else {
194 embassy_net::Config::ipv4_static(StaticConfigV4 {
195 address: IP_ADDRESS,
196 gateway: None,
197 dns_servers: Vec::new(),
198 })
199 };
200
201 // Init network stack
202 let stack = &*make_static!(Stack::new(
203 device,
204 ip_cfg,
205 make_static!(StackResources::<2>::new()),
206 seed
207 ));
208
209 // Launch network task
210 unwrap!(spawner.spawn(net_task(stack)));
211
212 let cfg = wait_for_config(stack).await;
213 let local_addr = cfg.address.address();
214
215 // Then we can use it!
216 let mut rx_buffer = [0; 4096];
217 let mut tx_buffer = [0; 4096];
218 let mut mb_buf = [0; 4096];
219 loop {
220 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
221 socket.set_timeout(Some(Duration::from_secs(1)));
222
223 info!("Listening on http://{}:{}...", local_addr, HTTP_LISTEN_PORT);
224 if let Err(e) = socket.accept(HTTP_LISTEN_PORT).await {
225 defmt::error!("accept error: {:?}", e);
226 continue;
227 }
228
229 loop {
230 let _n = match socket.read(&mut mb_buf).await {
231 Ok(0) => {
232 defmt::info!("read EOF");
233 break;
234 }
235 Ok(n) => n,
236 Err(e) => {
237 defmt::error!("{:?}", e);
238 break;
239 }
240 };
241 led_uc2_red.set_low();
242
243 let status_line = "HTTP/1.1 200 OK";
244 let contents = PAGE;
245 let length = contents.len();
246
247 let _ = write!(
248 &mut mb_buf[..],
249 "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}\r\n\0"
250 );
251 let loc = mb_buf.iter().position(|v| *v == b'#').unwrap();
252
253 let temp = TEMP.load(Ordering::Relaxed);
254 let cel = temp / 1000;
255 let mcel = temp % 1000;
256
257 info!("{}.{}", cel, mcel);
258
259 let _ = write!(&mut mb_buf[loc..loc + 7], "{cel}.{mcel}");
260
261 let n = mb_buf.iter().position(|v| *v == 0).unwrap();
262
263 if let Err(e) = socket.write_all(&mb_buf[..n]).await {
264 error!("write error: {:?}", e);
265 break;
266 }
267
268 led_uc2_red.set_high();
269 }
270 }
271}
272
273async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 {
274 loop {
275 if let Some(config) = stack.config_v4() {
276 return config;
277 }
278 yield_now().await;
279 }
280}
281
282#[embassy_executor::task]
283async fn heartbeat_led(mut led: Output<'static, peripherals::PE6>) {
284 let mut tmr = Ticker::every(Duration::from_hz(3));
285 loop {
286 led.toggle();
287 tmr.next().await;
288 }
289}
290
291// ADT7422
292#[embassy_executor::task]
293async fn temp_task(temp_dev_i2c: TempSensI2c, mut led: Output<'static, peripherals::PG15>) -> ! {
294 let mut tmr = Ticker::every(Duration::from_hz(1));
295 let mut temp_sens = ADT7422::new(temp_dev_i2c, 0x48).unwrap();
296
297 loop {
298 led.set_low();
299 match select(temp_sens.read_temp(), Timer::after(Duration::from_millis(500))).await {
300 Either::First(i2c_ret) => match i2c_ret {
301 Ok(value) => {
302 led.set_high();
303 let temp = i32::from(value);
304 println!("TEMP: {:04x}, {}", temp, temp * 78 / 10);
305 TEMP.store(temp * 78 / 10, Ordering::Relaxed);
306 }
307 Err(e) => defmt::println!("ADT7422: {}", e),
308 },
309 Either::Second(_) => println!("Timeout"),
310 }
311
312 tmr.next().await;
313 }
314}
315
316#[embassy_executor::task]
317async fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! {
318 runner.run().await
319}
320
321#[embassy_executor::task]
322async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
323 stack.run().await
324}
325
326// same panicking *behavior* as `panic-probe` but doesn't print a panic message
327// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
328#[defmt::panic_handler]
329fn panic() -> ! {
330 cortex_m::asm::udf()
331}
332
333#[allow(non_camel_case_types)]
334#[repr(C)]
335pub enum Registers {
336 Temp_MSB = 0x00,
337 Temp_LSB,
338 Status,
339 Cfg,
340 T_HIGH_MSB,
341 T_HIGH_LSB,
342 T_LOW_MSB,
343 T_LOW_LSB,
344 T_CRIT_MSB,
345 T_CRIT_LSB,
346 T_HYST,
347 ID,
348 SW_RESET = 0x2F,
349}
350
351pub struct ADT7422<'d, BUS: I2cBus> {
352 addr: u8,
353 phantom: PhantomData<&'d ()>,
354 bus: BUS,
355}
356
357#[derive(Debug, Format)]
358pub enum Error<I2cError: Format> {
359 I2c(I2cError),
360 Address,
361}
362
363impl<'d, BUS> ADT7422<'d, BUS>
364where
365 BUS: I2cBus,
366 BUS::Error: Format,
367{
368 pub fn new(bus: BUS, addr: u8) -> Result<Self, Error<BUS::Error>> {
369 if !(0x48..=0x4A).contains(&addr) {
370 return Err(Error::Address);
371 }
372
373 Ok(Self {
374 bus,
375 phantom: PhantomData,
376 addr,
377 })
378 }
379
380 pub async fn init(&mut self) -> Result<(), Error<BUS::Error>> {
381 let mut cfg = 0b000_0000;
382 // if self.int.is_some() {
383 // // Set 1 SPS mode
384 // cfg |= 0b10 << 5;
385 // } else {
386 // One shot mode
387 cfg |= 0b01 << 5;
388 // }
389
390 self.write_cfg(cfg).await
391 }
392
393 pub async fn read(&mut self, reg: Registers) -> Result<u8, Error<BUS::Error>> {
394 let mut buffer = [0u8; 1];
395 self.bus
396 .write_read(self.addr, &[reg as u8], &mut buffer)
397 .await
398 .map_err(Error::I2c)?;
399 Ok(buffer[0])
400 }
401
402 pub async fn write_cfg(&mut self, cfg: u8) -> Result<(), Error<BUS::Error>> {
403 let buf = [Registers::Cfg as u8, cfg];
404 self.bus.write(self.addr, &buf).await.map_err(Error::I2c)
405 }
406
407 pub async fn read_temp(&mut self) -> Result<i16, Error<BUS::Error>> {
408 let mut buffer = [0u8; 2];
409
410 // if let Some(int) = &mut self.int {
411 // // Wait for interrupt
412 // int.wait_for_low().await.unwrap();
413 // } else {
414 // Start: One shot
415 let cfg = 0b01 << 5;
416 self.write_cfg(cfg).await?;
417 Timer::after(Duration::from_millis(250)).await;
418 self.bus
419 .write_read(self.addr, &[Registers::Temp_MSB as u8], &mut buffer)
420 .await
421 .map_err(Error::I2c)?;
422 Ok(i16::from_be_bytes(buffer))
423 }
424}
425
426// Web page
427const PAGE: &str = r#"<!DOCTYPE html>
428<html lang="en">
429 <head>
430 <meta charset="utf-8">
431 <meta http-equiv="refresh" content="1" >
432 <title>ADIN1110 with Rust</title>
433 </head>
434 <body>
435 <p>EVAL-ADIN1110EBZ</p>
436 <table><td>Temp Sensor ADT7422:</td><td> #00.00 &deg;C</td></table>
437 </body>
438</html>"#;