apollo3/
ios.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! IO Slave Driver (I2C and SPI)
6//!
7//! This file provides support for the Apollo3 IOS. The IOS is a little
8//! strange in that I2C operations go straight to a local RAM area.
9//!
10//! The first byte of data (after the I2C address and operation byte)
11//! will be interpreteted as an address offset by the hardware.
12//!
13//! If the offset was between 0x00 and 0x77 the data will be in the
14//! RAM which we can access from 0x5000_0000 to 0x5000_0077. This
15//! will generate the XCMPWR interrupt on writes from the master.
16//!
17//! If the offset was the following it is written to the interrupt
18//! or FIFO registers. This will generate a XCMPWF on writes from the
19//! master:
20//!     - 0x78-7B -> IOINT Regs
21//!     - 0x7C    -> FIFOCTRLO
22//!     - 0x7D    -> FIFOCTRUP
23//!     - 0x7F    -> FIFO (DATA)
24//!
25//! Unfortunately we have no way to know where the data was written.
26//!
27//! We currently don't support the FIFO registers. As there is no way
28//! to know where the data was written we assume it was written to offset
29//! 0x0F. This is the first non interrupt flag generating address.
30//! This also matches the first byte of a MCTP packet.
31//!
32//! So, if you would like to write data to this device, the first byte of
33//! data must be 0x0F.
34
35use crate::ios::i2c::SlaveTransmissionType;
36use core::cell::Cell;
37use kernel::debug;
38use kernel::hil::i2c::{self, Error, I2CHwSlaveClient, I2CSlave};
39use kernel::utilities::cells::OptionalCell;
40use kernel::utilities::cells::TakeCell;
41use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
42use kernel::utilities::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
43use kernel::utilities::StaticRef;
44
45const SRAM_ROBASE_OFFSET: u32 = 0x78;
46
47const IOS_BASE: StaticRef<IosRegisters> =
48    unsafe { StaticRef::new(0x5000_0100 as *const IosRegisters) };
49
50register_structs! {
51    pub IosRegisters {
52        (0x000 => fifoptr: ReadWrite<u32, FIFOPTR::Register>),
53        (0x004 => fifocfg: ReadWrite<u32, FIFOCFG::Register>),
54        (0x008 => fifothr: ReadWrite<u32, FIFOTHR::Register>),
55        (0x00C => fupd: ReadWrite<u32, FUPD::Register>),
56        (0x010 => fifoctr: ReadWrite<u32, FIFOCTR::Register>),
57        (0x014 => fifoinc : ReadWrite<u32, FIFOINC::Register>),
58        (0x018 => cfg: ReadWrite<u32, CFG::Register>),
59        (0x01C => prenc: ReadWrite<u32, PRENC::Register>),
60        (0x020 => iointctl: ReadWrite<u32, IOINTCTL::Register>),
61        (0x024 => genadd: ReadOnly<u32, GENADD::Register>),
62        (0x028 => _reserved2),
63        (0x100 => inten: ReadWrite<u32, INT::Register>),
64        (0x104 => intstat: ReadWrite<u32, INT::Register>),
65        (0x108 => intclr: ReadWrite<u32, INT::Register>),
66        (0x10C => intset: ReadWrite<u32, INT::Register>),
67        (0x110 => regaccinten: ReadWrite<u32, REGACC::Register>),
68        (0x114 => regaccintstat: ReadWrite<u32, REGACC::Register>),
69        (0x118 => regaccintclr: ReadWrite<u32, REGACC::Register>),
70        (0x11C => regaccintset: ReadWrite<u32, REGACC::Register>),
71        (0x120 => @END),
72    }
73}
74
75register_bitfields![u32,
76    FIFOPTR [
77        FIFOPTR OFFSET(0) NUMBITS(8) [],
78        FIFOSIZ OFFSET(8) NUMBITS(8) []
79    ],
80    FIFOCFG [
81        FIFOBASE OFFSET(0) NUMBITS(5) [],
82        FIFOMAX OFFSET(8) NUMBITS(6) [],
83        ROBASE OFFSET(24) NUMBITS(6) [],
84    ],
85    FIFOTHR [
86        FIFOTHR OFFSET(8) NUMBITS(8) []
87    ],
88    FUPD [
89        FIFOUPD OFFSET(0) NUMBITS(1) [],
90        IOREAD OFFSET(1) NUMBITS(1) []
91    ],
92    FIFOCTR [
93        FIFOCTR OFFSET(0) NUMBITS(10) []
94    ],
95    FIFOINC [
96        FIFOINC OFFSET(0) NUMBITS(10) []
97    ],
98    CFG [
99        IFCSEL OFFSET(0) NUMBITS(1) [],
100        SPOL OFFSET(1) NUMBITS(1) [],
101        LSB OFFSET(2) NUMBITS(1) [],
102        STARTRD OFFSET(4) NUMBITS(1) [],
103        I2CADDR OFFSET(8) NUMBITS(11) [],
104        IFCEN OFFSET(31) NUMBITS(1) []
105    ],
106    PRENC [
107        PRENC OFFSET(0) NUMBITS(5) []
108    ],
109    IOINTCTL [
110        IOINTEN OFFSET(0) NUMBITS(8) [],
111        IOINT OFFSET(8) NUMBITS(8) [],
112        IOINTCLR OFFSET(16) NUMBITS(1) [],
113        IOINTSET OFFSET(24) NUMBITS(8) []
114    ],
115    GENADD [
116        GADATA OFFSET(0) NUMBITS(8) [],
117    ],
118    INT [
119        FSIZE OFFSET(0) NUMBITS(1) [],
120        FOVFL OFFSET(1) NUMBITS(1) [],
121        FUNDFL OFFSET(2) NUMBITS(1) [],
122        FRDERR OFFSET(3) NUMBITS(1) [],
123        GENAD OFFSET(4) NUMBITS(1) [],
124        IOINTW OFFSET(5) NUMBITS(1) [],
125        XCMPRF OFFSET(6) NUMBITS(1) [],
126        XCMPRR OFFSET(7) NUMBITS(1) [],
127        XCMPWF OFFSET(8) NUMBITS(1) [],
128        XCMPWR OFFSET(9) NUMBITS(1) []
129    ],
130    REGACC [
131        REGACC OFFSET(0) NUMBITS(32) []
132    ]
133];
134
135#[derive(Clone, Copy, PartialEq, Debug)]
136enum Operation {
137    None,
138    I2C,
139}
140
141pub struct Ios<'a> {
142    registers: StaticRef<IosRegisters>,
143
144    i2c_slave_client: OptionalCell<&'a dyn I2CHwSlaveClient>,
145
146    write_buf: TakeCell<'static, [u8]>,
147    write_len: Cell<usize>,
148    read_buf: TakeCell<'static, [u8]>,
149    read_len: Cell<usize>,
150    op: Cell<Operation>,
151}
152
153impl<'a> Ios<'a> {
154    pub fn new() -> Ios<'a> {
155        Ios {
156            registers: IOS_BASE,
157            i2c_slave_client: OptionalCell::empty(),
158            write_buf: TakeCell::empty(),
159            write_len: Cell::new(0),
160            read_buf: TakeCell::empty(),
161            read_len: Cell::new(0),
162            op: Cell::new(Operation::None),
163        }
164    }
165
166    fn i2c_interface_enable(&self) {
167        let regs = self.registers;
168
169        regs.cfg.modify(CFG::IFCEN::SET);
170        regs.cfg.modify(CFG::IFCSEL::CLEAR);
171    }
172
173    pub fn handle_interrupt(&self) {
174        let irqs = self.registers.intstat.extract();
175
176        // Clear interrrupts
177        self.registers.intclr.set(0xFFFF_FFFF);
178        self.registers.regaccintclr.set(0xFFFF_FFFF);
179        // Ensure interrupts remain enabled
180        self.registers.inten.set(0xFFFF_FFFF);
181        self.registers.regaccinten.set(0xFFFF_FFFF);
182
183        let _offset = self.registers.fifoptr.read(FIFOPTR::FIFOPTR);
184
185        if irqs.is_set(INT::XCMPWR) {
186            // If we get here that means the I2C master has written something
187            // addressed to us and the offset is in the "Direct Area", between
188            // 0x00 and 0x77.
189            //
190            // Unfortunately we have no way to know where the data was written,
191            // so we assume it starts at 0x0F.
192            let len = (SRAM_ROBASE_OFFSET as usize).min(self.write_len.get());
193
194            self.write_buf.take().map(|buf| {
195                buf[0] = 0x0F;
196
197                for i in 1..len {
198                    unsafe {
199                        buf[i] = *((0x5000_000F + (i as u32 - 1)) as *mut u8);
200                        // Zero the data after we read it
201                        *((0x5000_000F + (i as u32 - 1)) as *mut u8) = 0x00;
202                    }
203                }
204
205                self.i2c_slave_client.get().map(|client| {
206                    client.command_complete(buf, len, SlaveTransmissionType::Write);
207                });
208            });
209        }
210
211        if irqs.is_set(INT::XCMPWF) {
212            // If we get here that means the I2C master has written something
213            // addressed to us and the offset is in the "FIFO Area", 0x7F
214
215            // We currently don't support the FIFO area. We have no way
216            // to report errors, so let's just print something.
217
218            debug!("Write to the FIFO area, which is not currently supported");
219        }
220    }
221}
222
223impl<'a> I2CSlave<'a> for Ios<'a> {
224    fn set_slave_client(&self, slave_client: &'a dyn i2c::I2CHwSlaveClient) {
225        self.i2c_slave_client.set(slave_client);
226    }
227
228    fn enable(&self) {
229        self.op.set(Operation::I2C);
230
231        // Eliminate the "read-only" section, so an external host can use the
232        // entire "direct write" section.
233        self.registers
234            .fifocfg
235            .modify(FIFOCFG::ROBASE.val(SRAM_ROBASE_OFFSET / 8));
236
237        // Set the FIFO base to the maximum value, making the "direct write"
238        // section as big as possible.
239        self.registers
240            .fifocfg
241            .modify(FIFOCFG::FIFOBASE.val(SRAM_ROBASE_OFFSET / 8));
242
243        // We don't need any RAM space, so extend the FIFO all the way to the end
244        // of the LRAM.
245        self.registers
246            .fifocfg
247            .modify(FIFOCFG::FIFOMAX.val(0x100 / 8));
248
249        // Clear FIFOs
250        self.registers.fifoctr.modify(FIFOCTR::FIFOCTR.val(0x00));
251        self.registers.fifoptr.modify(FIFOPTR::FIFOSIZ.val(0x00));
252
253        // Setup FIFO interrupt threshold
254        self.registers.fifothr.modify(FIFOTHR::FIFOTHR.val(0x08));
255
256        self.i2c_interface_enable();
257
258        // Clear interrrupts
259        self.registers.intclr.set(0xFFFF_FFFF);
260
261        // Update the FIFO
262        self.registers.fupd.modify(FUPD::FIFOUPD::SET);
263        self.registers.fifoptr.modify(FIFOPTR::FIFOPTR.val(0x80));
264        self.registers.fupd.modify(FUPD::FIFOUPD::CLEAR);
265    }
266
267    fn disable(&self) {
268        if self.op.get() == Operation::I2C {
269            self.registers.cfg.modify(CFG::IFCEN::CLEAR);
270
271            self.op.set(Operation::None);
272        }
273    }
274
275    fn set_address(&self, addr: u8) -> Result<(), Error> {
276        self.registers
277            .cfg
278            .modify(CFG::I2CADDR.val((addr as u32) << 1));
279
280        Ok(())
281    }
282
283    fn write_receive(
284        &self,
285        data: &'static mut [u8],
286        max_len: usize,
287    ) -> Result<(), (Error, &'static mut [u8])> {
288        self.write_len.set(max_len.min(data.len()));
289        self.write_buf.replace(data);
290
291        Ok(())
292    }
293
294    fn read_send(
295        &self,
296        data: &'static mut [u8],
297        max_len: usize,
298    ) -> Result<(), (Error, &'static mut [u8])> {
299        for (i, d) in data.iter().enumerate() {
300            unsafe {
301                *((0x5000_0000 + 0x7F + (i as u32)) as *mut u8) = *d;
302            }
303        }
304
305        self.read_len.set(max_len.min(data.len()));
306        self.read_buf.replace(data);
307
308        Ok(())
309    }
310
311    fn listen(&self) {
312        self.registers.inten.modify(
313            INT::FSIZE::SET
314                + INT::FOVFL::SET
315                + INT::FUNDFL::SET
316                + INT::FRDERR::SET
317                + INT::GENAD::SET
318                + INT::IOINTW::SET
319                + INT::XCMPRF::SET
320                + INT::XCMPRF::SET
321                + INT::XCMPWF::SET
322                + INT::XCMPWR::SET,
323        );
324    }
325}