Skip to main content

atmos/kernel/
sdio.rs

1// sdio.rs - Arasan SDHCI SDIO driver for CYW43455 on RPi3B+
2// WiFi chip (CYW43455) is connected via GPIO 34-39 to the Arasan eMMC2 controller.
3#![allow(dead_code)]
4
5// Arasan eMMC2 (SDIO for WiFi) base address on BCM2837
6// Bus address 0x7E340000 -> CPU address 0x3F340000
7const SDIO_BASE: usize = 0x3F340000;
8
9// SDHCI register offsets
10const SDHCI_DMA_ADDRESS: *mut u32 = SDIO_BASE as *mut u32;
11const SDHCI_BLOCK_SIZE: *mut u16 = (SDIO_BASE + 0x04) as *mut u16;
12const SDHCI_BLOCK_COUNT: *mut u16 = (SDIO_BASE + 0x06) as *mut u16;
13const SDHCI_ARGUMENT: *mut u32 = (SDIO_BASE + 0x08) as *mut u32;
14const SDHCI_TRANSFER_MODE: *mut u16 = (SDIO_BASE + 0x0C) as *mut u16;
15const SDHCI_COMMAND: *mut u16 = (SDIO_BASE + 0x0E) as *mut u16;
16const SDHCI_RESPONSE: *mut u32 = (SDIO_BASE + 0x10) as *mut u32; // 4x u32 at 0x10,14,18,1C
17const SDHCI_BUFFER: *mut u32 = (SDIO_BASE + 0x20) as *mut u32;
18const SDHCI_PRESENT_STATE: *mut u32 = (SDIO_BASE + 0x24) as *mut u32;
19const SDHCI_HOST_CONTROL: *mut u8 = (SDIO_BASE + 0x28) as *mut u8;
20const SDHCI_POWER_CONTROL: *mut u8 = (SDIO_BASE + 0x29) as *mut u8;
21const SDHCI_BLOCK_GAP_CONTROL: *mut u8 = (SDIO_BASE + 0x2A) as *mut u8;
22const SDHCI_CLOCK_CONTROL: *mut u16 = (SDIO_BASE + 0x2C) as *mut u16;
23const SDHCI_TIMEOUT_CONTROL: *mut u8 = (SDIO_BASE + 0x2E) as *mut u8;
24const SDHCI_SOFTWARE_RESET: *mut u8 = (SDIO_BASE + 0x2F) as *mut u8;
25const SDHCI_INT_STATUS: *mut u32 = (SDIO_BASE + 0x30) as *mut u32;
26const SDHCI_INT_ENABLE: *mut u32 = (SDIO_BASE + 0x34) as *mut u32;
27const SDHCI_SIGNAL_ENABLE: *mut u32 = (SDIO_BASE + 0x38) as *mut u32;
28const SDHCI_CAPABILITIES: *mut u32 = (SDIO_BASE + 0x40) as *mut u32;
29const SDHCI_HOST_VERSION: *mut u16 = (SDIO_BASE + 0xFE) as *mut u16;
30
31// SDHCI_PRESENT_STATE bits
32const SDHCI_CMD_INHIBIT: u32 = 1 << 0;
33const SDHCI_DATA_INHIBIT: u32 = 1 << 1;
34
35// SDHCI_INT_STATUS bits
36const SDHCI_INT_RESPONSE: u32 = 1 << 0;
37const SDHCI_INT_DATA_END: u32 = 1 << 1;
38const SDHCI_INT_ERROR: u32 = 1 << 15;
39
40// SDIO command flags
41const SDHCI_CMD_RESP_MASK: u16 = 0x03;
42const SDHCI_CMD_CRC: u16 = 0x08;
43const SDHCI_CMD_INDEX: u16 = 0x10;
44const SDHCI_CMD_DATA: u16 = 0x20;
45const SDHCI_CMD_RESP_SHORT: u16 = 0x02; // R1, R3, R4, R5, R6
46const SDHCI_CMD_RESP_LONG: u16 = 0x01; // R2
47
48// SDIO function numbers
49pub const SDIO_FUNC_BUS: u8 = 0;
50pub const SDIO_FUNC_BACKPLANE: u8 = 1;
51pub const SDIO_FUNC_WLAN: u8 = 2;
52
53// CYW43455 specific
54const CYW43_BACKPLANE_FUNC: u8 = 1;
55const CYW43_WLAN_FUNC: u8 = 2;
56
57// CCCR (Card Common Control Register) addresses via SDIO CMD52
58const CCCR_SDIO_REVISION: u32 = 0x00;
59const CCCR_CCCR_REVISION: u32 = 0x00;
60const CCCR_IO_ENABLE: u32 = 0x02;
61const CCCR_IO_READY: u32 = 0x03;
62const CCCR_INT_ENABLE: u32 = 0x04;
63const CCCR_BUS_CONTROL: u32 = 0x07;
64const CCCR_CARD_CAPABILITY: u32 = 0x08;
65const CCCR_BUS_WIDTH: u32 = 0x07;
66
67static mut SDIO_INITIALIZED: bool = false;
68
69fn delay_us(us: u32) {
70    for _ in 0..(us * 10) {
71        unsafe { core::arch::asm!("nop") };
72    }
73}
74
75fn delay_ms(ms: u32) {
76    delay_us(ms * 1000);
77}
78
79unsafe fn wait_inhibit(data: bool) -> bool {
80    let mask = if data {
81        SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT
82    } else {
83        SDHCI_CMD_INHIBIT
84    };
85    let mut timeout = 100_000;
86    while (core::ptr::read_volatile(SDHCI_PRESENT_STATE) & mask) != 0 {
87        timeout -= 1;
88        if timeout == 0 {
89            return false;
90        }
91    }
92    true
93}
94
95unsafe fn wait_int(mask: u32) -> bool {
96    let mut timeout = 1_000_000;
97    loop {
98        let ints = core::ptr::read_volatile(SDHCI_INT_STATUS);
99        if (ints & SDHCI_INT_ERROR) != 0 {
100            core::ptr::write_volatile(SDHCI_INT_STATUS, ints);
101            return false;
102        }
103        if (ints & mask) != 0 {
104            core::ptr::write_volatile(SDHCI_INT_STATUS, ints & mask);
105            return true;
106        }
107        timeout -= 1;
108        if timeout == 0 {
109            return false;
110        }
111    }
112}
113
114/// SDIO CMD52: Single byte read/write (no data transfer, uses SDHCI response)
115/// fn=function, write=true for write, addr=register address, data=byte to write
116/// Returns read byte on success.
117///
118/// # Safety
119/// SDHCI MMIO レジスタへ直接アクセスする。SDIO コントローラが初期化済みで、
120/// 同時に他コア/割り込みから SDHCI を操作していない状態で呼ぶこと。
121pub unsafe fn cmd52(fn_num: u8, write: bool, addr: u32, data: u8) -> Option<u8> {
122    if !wait_inhibit(false) {
123        return None;
124    }
125
126    // Argument: [31]=R/W, [30:28]=func, [27]=RAW, [25:9]=addr, [7:0]=data
127    let arg = (((write as u32) << 31)
128        | ((fn_num as u32 & 0x7) << 28)) // RAW=0
129        | ((addr & 0x1FFFF) << 9)
130        | (data as u32);
131
132    core::ptr::write_volatile(SDHCI_ARGUMENT, arg);
133    // CMD52: index=52, response=R5 (short), no data
134    let cmd = (52 << 8) | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_CRC | SDHCI_CMD_INDEX;
135    core::ptr::write_volatile(SDHCI_COMMAND, cmd);
136
137    if !wait_int(SDHCI_INT_RESPONSE) {
138        return None;
139    }
140    let resp = core::ptr::read_volatile(SDHCI_RESPONSE);
141    // R5 response: bits[15:8]=flags, bits[7:0]=data
142    Some((resp & 0xFF) as u8)
143}
144
145/// SDIO CMD53: Multi-byte block read/write
146/// fn=function, write=direction, addr=start address, buf=data buffer, len=byte count
147///
148/// # Safety
149/// SDHCI MMIO レジスタと DMA/PIO バッファへ直接アクセスする。SDIO コントローラが
150/// 初期化済みで、`buf` が有効かつ排他的に使用できる状態で呼ぶこと。
151pub unsafe fn cmd53(fn_num: u8, write: bool, addr: u32, buf: &mut [u8]) -> bool {
152    let len = buf.len();
153    if len == 0 || len > 512 {
154        return false;
155    }
156    if !wait_inhibit(true) {
157        return false;
158    }
159
160    // Block mode=0 (byte mode). WLAN function (func 2) is a FIFO — no addr increment.
161    // Backplane function (func 1) accesses sequential memory — increment enabled.
162    let addr_inc = fn_num != SDIO_FUNC_WLAN;
163    let arg = (((write as u32) << 31)
164        | ((fn_num as u32 & 0x7) << 28)
165        | ((addr_inc as u32) << 26)) // block mode=0 (byte mode)
166        | ((addr & 0x1FFFF) << 9)
167        | (len as u32 & 0x1FF);
168
169    core::ptr::write_volatile(SDHCI_BLOCK_SIZE, len as u16);
170    core::ptr::write_volatile(SDHCI_BLOCK_COUNT, 1);
171    core::ptr::write_volatile(SDHCI_ARGUMENT, arg);
172
173    let xfer_mode: u16 = if write { 0x00 } else { 0x10 }; // bit4=data dir(1=read)
174    core::ptr::write_volatile(SDHCI_TRANSFER_MODE, xfer_mode);
175
176    let cmd = (53 << 8) | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_CRC | SDHCI_CMD_INDEX | SDHCI_CMD_DATA;
177    core::ptr::write_volatile(SDHCI_COMMAND, cmd);
178
179    if !wait_int(SDHCI_INT_RESPONSE) {
180        return false;
181    }
182
183    if write {
184        // Write data to FIFO
185        let mut i = 0;
186        while i < len {
187            let mut word: u32 = 0;
188            for j in 0..4usize {
189                if i + j < len {
190                    word |= (buf[i + j] as u32) << (j * 8);
191                }
192            }
193            core::ptr::write_volatile(SDHCI_BUFFER, word);
194            i += 4;
195        }
196    }
197
198    if !wait_int(SDHCI_INT_DATA_END) {
199        return false;
200    }
201
202    if !write {
203        // Read data from FIFO
204        let mut i = 0;
205        while i < len {
206            let word = core::ptr::read_volatile(SDHCI_BUFFER);
207            for j in 0..4usize {
208                if i + j < len {
209                    buf[i + j] = ((word >> (j * 8)) & 0xFF) as u8;
210                }
211            }
212            i += 4;
213        }
214    }
215
216    true
217}
218
219/// SDIO CLKを設定 (Arasan SDHCI clock divider)
220unsafe fn set_clock(khz: u32) {
221    // ベースクロック250MHzとして分周器を計算
222    let base_khz = 250_000u32;
223    let divisor = base_khz.div_ceil(khz).next_power_of_two().max(2);
224    let div8 = (divisor >> 1) as u16;
225
226    // Clock disable
227    core::ptr::write_volatile(SDHCI_CLOCK_CONTROL, 0);
228    delay_ms(1);
229
230    // Internal clock enable + divisor
231    let clk_ctrl = (div8 << 8) | 0x01; // bit0=internal clk enable
232    core::ptr::write_volatile(SDHCI_CLOCK_CONTROL, clk_ctrl);
233
234    // Wait stable
235    let mut timeout = 10000;
236    while (core::ptr::read_volatile(SDHCI_CLOCK_CONTROL) & 0x02) == 0 {
237        timeout -= 1;
238        if timeout == 0 {
239            break;
240        }
241    }
242
243    // SD clock enable
244    let clk_ctrl = core::ptr::read_volatile(SDHCI_CLOCK_CONTROL) | 0x04;
245    core::ptr::write_volatile(SDHCI_CLOCK_CONTROL, clk_ctrl);
246    delay_ms(1);
247}
248
249/// SDIOコントローラの初期化とCYW43455の検出
250pub fn init() -> bool {
251    unsafe {
252        crate::debug!("[FS] SDIO: Initializing Arasan eMMC2 controller...");
253
254        // ソフトウェアリセット
255        core::ptr::write_volatile(SDHCI_SOFTWARE_RESET, 0x07);
256        let mut timeout = 100_000;
257        while (core::ptr::read_volatile(SDHCI_SOFTWARE_RESET) & 0x07) != 0 {
258            timeout -= 1;
259            if timeout == 0 {
260                crate::debug!("[FS] SDIO: Reset timeout");
261                return false;
262            }
263        }
264        delay_ms(10);
265
266        // 電源ON (3.3V)
267        core::ptr::write_volatile(SDHCI_POWER_CONTROL, 0x0F); // 3.3V + power on
268        delay_ms(10);
269
270        // 割り込み有効化 (ポーリング用)
271        core::ptr::write_volatile(SDHCI_INT_ENABLE, 0x00FF_00FF);
272        core::ptr::write_volatile(SDHCI_SIGNAL_ENABLE, 0);
273
274        // タイムアウト設定 (最大値)
275        core::ptr::write_volatile(SDHCI_TIMEOUT_CONTROL, 0x0E);
276
277        // 初期CLK 400kHz (識別フェーズ)
278        set_clock(400);
279        delay_ms(5);
280
281        // CMD0: GO_IDLE_STATE
282        core::ptr::write_volatile(SDHCI_INT_STATUS, 0xFFFF_FFFF);
283        core::ptr::write_volatile(SDHCI_ARGUMENT, 0);
284        core::ptr::write_volatile(SDHCI_COMMAND, 0); // CMD0 (index 0 << 8), no response
285        delay_ms(2);
286
287        // CMD5: IO_SEND_OP_COND (SDIO discovery)
288        core::ptr::write_volatile(SDHCI_INT_STATUS, 0xFFFF_FFFF);
289        core::ptr::write_volatile(SDHCI_ARGUMENT, 0);
290        core::ptr::write_volatile(SDHCI_COMMAND, (5 << 8) | SDHCI_CMD_RESP_SHORT);
291        if !wait_int(SDHCI_INT_RESPONSE) {
292            crate::debug!("[FS] SDIO: CMD5 failed (no SDIO device?)");
293            return false;
294        }
295        let r4 = core::ptr::read_volatile(SDHCI_RESPONSE);
296        let num_functions = (r4 >> 28) & 0x7;
297        crate::debug!("[FS] SDIO: CMD5 R4=0x{:08X}, {} function(s)", r4, num_functions);
298
299        // CMD5 with voltage: accept OCR
300        let ocr = r4 & 0x00FF_FF00;
301        core::ptr::write_volatile(SDHCI_INT_STATUS, 0xFFFF_FFFF);
302        core::ptr::write_volatile(SDHCI_ARGUMENT, ocr);
303        core::ptr::write_volatile(SDHCI_COMMAND, (5 << 8) | SDHCI_CMD_RESP_SHORT);
304        if !wait_int(SDHCI_INT_RESPONSE) {
305            crate::debug!("[FS] SDIO: CMD5 (OCR) failed");
306            return false;
307        }
308        let r4 = core::ptr::read_volatile(SDHCI_RESPONSE);
309        if (r4 & 0x8000_0000) == 0 {
310            crate::debug!("[FS] SDIO: Card not ready (OCR=0x{:08X})", r4);
311            return false;
312        }
313
314        // CMD3: SEND_RELATIVE_ADDR → RCA取得
315        core::ptr::write_volatile(SDHCI_INT_STATUS, 0xFFFF_FFFF);
316        core::ptr::write_volatile(SDHCI_ARGUMENT, 0);
317        core::ptr::write_volatile(
318            SDHCI_COMMAND,
319            (3 << 8) | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_CRC | SDHCI_CMD_INDEX,
320        );
321        if !wait_int(SDHCI_INT_RESPONSE) {
322            crate::debug!("[FS] SDIO: CMD3 failed");
323            return false;
324        }
325        let rca = (core::ptr::read_volatile(SDHCI_RESPONSE) >> 16) as u16;
326        crate::debug!("[FS] SDIO: RCA=0x{:04X}", rca);
327
328        // CMD7: SELECT_CARD (RCA)
329        core::ptr::write_volatile(SDHCI_INT_STATUS, 0xFFFF_FFFF);
330        core::ptr::write_volatile(SDHCI_ARGUMENT, (rca as u32) << 16);
331        core::ptr::write_volatile(
332            SDHCI_COMMAND,
333            (7 << 8) | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_CRC | SDHCI_CMD_INDEX,
334        );
335        if !wait_int(SDHCI_INT_RESPONSE) {
336            crate::debug!("[FS] SDIO: CMD7 failed");
337            return false;
338        }
339
340        // 高速CLK 25MHz に変更
341        set_clock(25_000);
342        delay_ms(5);
343
344        // 4-bit bus width (CCCR BUS_IF: bits[1:0]=10 = 4-bit)
345        cmd52(SDIO_FUNC_BUS, true, CCCR_BUS_WIDTH, 0x02);
346
347        // HostControl: 4-bit mode
348        let hc = core::ptr::read_volatile(SDHCI_HOST_CONTROL);
349        core::ptr::write_volatile(SDHCI_HOST_CONTROL, hc | 0x02);
350        delay_ms(2);
351
352        // Function 1 (backplane) と Function 2 (WLAN) を有効化
353        cmd52(SDIO_FUNC_BUS, true, CCCR_IO_ENABLE, 0x06); // bit1=F1, bit2=F2
354        delay_ms(20);
355
356        let ready = match cmd52(SDIO_FUNC_BUS, false, CCCR_IO_READY, 0) {
357            Some(v) => v,
358            None => {
359                crate::debug!("[FS] SDIO: IO_READY read failed");
360                return false;
361            }
362        };
363        crate::debug!("[FS] SDIO: IO_READY=0x{:02X}", ready);
364        if (ready & 0x06) != 0x06 {
365            crate::debug!("[FS] SDIO: Functions not ready");
366            return false;
367        }
368
369        SDIO_INITIALIZED = true;
370        crate::debug!("[FS] SDIO: Controller initialized successfully.");
371        true
372    }
373}
374
375pub fn is_initialized() -> bool {
376    unsafe { SDIO_INITIALIZED }
377}