Skip to main content

atmos/kernel/
sdhost.rs

1// sdhost.rs - BCM2835 SDHOST Controller Driver for Raspberry Pi 3B+
2#![allow(dead_code)]
3#![allow(static_mut_refs)]
4
5use core::sync::atomic::{AtomicBool, Ordering};
6
7// --- BCM2837 GPIO Registers ---
8const GPIO_BASE: usize = 0x3F20_0000;
9const GPFSEL4: *mut u32 = (GPIO_BASE + 0x10) as *mut u32;
10const GPFSEL5: *mut u32 = (GPIO_BASE + 0x14) as *mut u32;
11const GPPUD: *mut u32 = (GPIO_BASE + 0x94) as *mut u32;
12const GPPUDCLK1: *mut u32 = (GPIO_BASE + 0x9C) as *mut u32;
13
14// --- BCM2835 SDHOST Base Address ---
15const SDHOST_BASE: usize = 0x3F20_2000;
16
17const SDCMD:  *mut u32 = SDHOST_BASE as *mut u32;
18const SDARG:  *mut u32 = (SDHOST_BASE + 0x04) as *mut u32;
19const SDTOUT: *mut u32 = (SDHOST_BASE + 0x08) as *mut u32;
20const SDCDIV: *mut u32 = (SDHOST_BASE + 0x0C) as *mut u32;
21const SDRSP0: *mut u32 = (SDHOST_BASE + 0x10) as *mut u32;
22const SDRSP1: *mut u32 = (SDHOST_BASE + 0x14) as *mut u32;
23const SDRSP2: *mut u32 = (SDHOST_BASE + 0x18) as *mut u32;
24const SDRSP3: *mut u32 = (SDHOST_BASE + 0x1C) as *mut u32;
25const SDHSTS: *mut u32 = (SDHOST_BASE + 0x20) as *mut u32;
26const SDVDD:  *mut u32 = (SDHOST_BASE + 0x30) as *mut u32;
27const SDEDM:  *mut u32 = (SDHOST_BASE + 0x34) as *mut u32;
28const SDHCFG: *mut u32 = (SDHOST_BASE + 0x38) as *mut u32;
29/// 1 ブロックあたりのバイト数(512)。
30const SDHBCT: *mut u32 = (SDHOST_BASE + 0x3C) as *mut u32;
31const SDDATA: *mut u32 = (SDHOST_BASE + 0x40) as *mut u32;
32/// 転送ブロック数。
33/// 【2026-07-26】従来この定義自体が存在せず、ブロック数を `SDHBCT` の
34/// 上位ビットへ詰め込んで書いていた(`512 | (1 << 16)`)。BCM2835 SDHOST では
35/// バイト数(SDHBCT 0x3C)とブロック数(SDHBLC 0x50)は**別レジスタ**であり、
36/// SDHBLC が 0 のままではコントローラが転送すべきブロック数を知り得ないため
37/// データ転送が開始されない。コマンド応答は正常なのに読み書きだけが
38/// 成立しなかった原因の一つ。
39const SDHBLC: *mut u32 = (SDHOST_BASE + 0x50) as *mut u32;
40
41// --- SDCMD bits (BCM2835 SDHOST) ---
42//
43// 【2026-07-26 重大バグ修正】レスポンス種別のビット定義が実機の仕様と
44// 食い違っており、**全コマンドが誤った種別で発行されていた**。
45// BCM2835 SDHOST の SDCMD レジスタでは
46//   0x400 = NO_RESPONSE(応答なし)
47//   0x200 = LONG_RESPONSE(136bit 応答)
48//   48bit 応答は「フラグを立てない(0)」
49// が正しい。従来の定義は
50//   RESP_NONE=0x000 / RESP_48=0x200 / RESP_136=0x400
51// と 3 つとも取り違えていたため、
52//   * CMD8/CMD55/ACMD41 等の 48bit 応答コマンド → LONG_RESPONSE として発行され、
53//     来るはずのない 136bit 応答を待って失敗する
54//   * CMD0(応答なし)→ 48bit 応答を期待して待ってしまう
55//   * CMD2/CMD9(本当の 136bit)→ NO_RESPONSE として発行され応答を取り込めない
56// という状態だった。起動時に必ず出ていた
57// `SDHOST: Card initialization failed (ACMD41 timeout)` の直接原因。
58// (定義は Linux の `drivers/mmc/host/bcm2835-sdhost.c` と同じ)
59const SDCMD_NEW_CMD: u32 = 0x8000;
60const SDCMD_FAIL_ERROR: u32 = 0x4000;
61const SDCMD_BUSYWAIT: u32 = 0x0800;
62const SDCMD_NO_RESPONSE: u32 = 0x0400;
63const SDCMD_LONG_RESPONSE: u32 = 0x0200;
64/// データ書き込みを伴うコマンド。従来 `0x8000` と定義されており
65/// `SDCMD_NEW_CMD` と衝突していたため、**書き込みビットが一度も立って
66/// いなかった**(CMD24 が書き込みとして扱われない)。正しくは 0x80。
67const SDCMD_WRITE: u32 = 0x0080;
68/// データ読み出しを伴うコマンド(CMD17 等)。従来まったく指定されていなかった。
69const SDCMD_READ: u32 = 0x0040;
70
71// `send_command` の `resp_type` 引数として渡すレスポンス種別。
72const SDCMD_RESP_NONE: u32 = SDCMD_NO_RESPONSE;
73const SDCMD_RESP_48: u32 = 0x0000;
74const SDCMD_RESP_136: u32 = SDCMD_LONG_RESPONSE;
75
76// --- SDHCFG bits (BCM2835 SDHOST HOST CONFIG) ---
77const SDHCFG_REL_CMD_LINE: u32 = 0x0001;
78/// 内部バス(FIFO 側)を広幅で扱う。常に立てる。
79const SDHCFG_WIDE_INT_BUS: u32 = 0x0002;
80/// 外部バス(カードとの DAT 線)を 4bit で扱う。ACMD6 で 4bit へ切り替えたら
81/// **必ずこちらも立てる**こと。片方だけだとデータ転送が成立しない。
82const SDHCFG_WIDE_EXT_BUS: u32 = 0x0004;
83const SDHCFG_SLOW_CARD: u32 = 0x0008;
84const SDHCFG_DATA_IRPT_EN: u32 = 0x0010;
85
86// --- SDEDM bits (BCM2835 SDHOST Extended Data Mode) ---
87const SDEDM_WRITE_THRESHOLD_SHIFT: u32 = 9;
88const SDEDM_READ_THRESHOLD_SHIFT: u32 = 14;
89const SDEDM_THRESHOLD_MASK: u32 = 0x1F;
90const FIFO_READ_THRESHOLD: u32 = 4;
91const FIFO_WRITE_THRESHOLD: u32 = 4;
92
93// --- SDHSTS bits ---
94// 【2026-07-26 重大バグ修正】BCM2835 SDHOST の SDHSTS ビット割り当ては
95//   bit0 FIFO_ERROR / bit1 CRC7_ERROR / bit2 CRC16_ERROR /
96//   bit3 CMD_TIME_OUT / bit4 REW_TIME_OUT / bit5 SDIO_IRPT /
97//   bit8 DATA_FLAG(FIFOにデータあり) / bit9 BLOCK_IRPT / bit10 BUSY_IRPT
98// である。従来の定義は
99//   FIFO_DATA=0x200 / FIFO_ERROR=0x008 / ERROR=0x078
100// と誤っており、特に **「FIFO にデータがある」の判定に 0x200
101// (実際はブロック転送完了割り込み)を使っていた**ため、読み出しループが
102// データ到着を永久に検知できずタイムアウトしていた。コマンド応答は
103// 正常に返るのに読み書きだけが成立しなかった直接の原因。
104// また FIFO_ERROR として見ていた 0x008 は実際には CMD_TIME_OUT である。
105const SDHSTS_FIFO_ERROR: u32 = 0x0001;
106const SDHSTS_CRC7_ERROR: u32 = 0x0002;
107const SDHSTS_CRC16_ERROR: u32 = 0x0004;
108const SDHSTS_CMD_TIME_OUT: u32 = 0x0008;
109const SDHSTS_REW_TIME_OUT: u32 = 0x0010;
110/// FIFO にデータが存在する(読み出し可能/書き込み可能)。
111const SDHSTS_DATA_FLAG: u32 = 0x0100;
112const SDHSTS_BLOCK_IRPT: u32 = 0x0200;
113const SDHSTS_BUSY: u32 = 0x0400;
114/// エラー系ビットのマスク(タイムアウト・CRC・FIFO)。
115const SDHSTS_ERROR: u32 = SDHSTS_FIFO_ERROR
116    | SDHSTS_CRC7_ERROR
117    | SDHSTS_CRC16_ERROR
118    | SDHSTS_CMD_TIME_OUT
119    | SDHSTS_REW_TIME_OUT;
120
121// --- Globals ---
122static SD_INITIALIZED: AtomicBool = AtomicBool::new(false);
123static mut RCA: u32 = 0;
124static mut CARD_SECTORS: usize = 0;
125
126/// マイクロ秒単位の簡単なディレイ
127fn delay_us(us: u32) {
128    let start = crate::kernel::timer::get_system_time_us();
129    while crate::kernel::timer::get_system_time_us() - start < us as u64 {
130        core::hint::spin_loop();
131    }
132}
133
134/// GPIO 48-53 を SDHOST (ALT0) に設定
135fn setup_gpio() {
136    unsafe {
137        // GPFSEL4 (GPIO 40-49)
138        // GPIO 48 (DAT0), GPIO 49 (DAT1) -> ALT0 (0b100)
139        let mut fsel4 = core::ptr::read_volatile(GPFSEL4);
140        fsel4 &= !(0b111 << 24); // clear 48
141        fsel4 |= 0b100 << 24;  // set 48 to ALT0
142        fsel4 &= !(0b111 << 27); // clear 49
143        fsel4 |= 0b100 << 27;  // set 49 to ALT0
144        core::ptr::write_volatile(GPFSEL4, fsel4);
145
146        // GPFSEL5 (GPIO 50-53)
147        // GPIO 50 (DAT2), 51 (DAT3), 52 (CLK), 53 (CMD) -> ALT0 (0b100)
148        let mut fsel5 = core::ptr::read_volatile(GPFSEL5);
149        for pin in 50..=53 {
150            let shift = (pin - 50) * 3;
151            fsel5 &= !(0b111 << shift);
152            fsel5 |= 0b100 << shift;
153        }
154        core::ptr::write_volatile(GPFSEL5, fsel5);
155
156        // プルアップ設定 (CMD, DAT0-3) とプルダウン設定 (CLK)
157        core::ptr::write_volatile(GPPUD, 2); // Pull Up
158        delay_us(10);
159        core::ptr::write_volatile(GPPUDCLK1, (1 << (48 - 32)) | (1 << (49 - 32)) | (1 << (50 - 32)) | (1 << (51 - 32)) | (1 << (53 - 32)));
160        delay_us(10);
161        core::ptr::write_volatile(GPPUD, 0);
162        core::ptr::write_volatile(GPPUDCLK1, 0);
163
164        core::ptr::write_volatile(GPPUD, 1); // Pull Down
165        delay_us(10);
166        core::ptr::write_volatile(GPPUDCLK1, 1 << (52 - 32)); // CLK
167        delay_us(10);
168        core::ptr::write_volatile(GPPUD, 0);
169        core::ptr::write_volatile(GPPUDCLK1, 0);
170    }
171}
172
173/// SDHOST コントローラにコマンドを送信し、結果を待つ
174fn send_command(cmd_idx: u32, arg: u32, resp_type: u32) -> Result<[u32; 4], &'static str> {
175    unsafe {
176        // 前のコマンド完了待ち
177        let mut timeout = 100000;
178        while (core::ptr::read_volatile(SDCMD) & SDCMD_NEW_CMD) != 0 {
179            timeout -= 1;
180            if timeout == 0 {
181                return Err("SDHOST: send_command previous timeout");
182            }
183            core::hint::spin_loop();
184        }
185
186        // 引数とレスポンスクリア
187        core::ptr::write_volatile(SDARG, arg);
188
189        // コマンドレジスタへ書き込んで発行
190        let cmd = (cmd_idx & 0x3F) | resp_type | SDCMD_NEW_CMD;
191        core::ptr::write_volatile(SDCMD, cmd);
192
193        // 発行完了とレスポンス準備完了待ち
194        timeout = 100000;
195        while (core::ptr::read_volatile(SDCMD) & SDCMD_NEW_CMD) != 0 {
196            timeout -= 1;
197            if timeout == 0 {
198                return Err("SDHOST: command execution timeout");
199            }
200            core::hint::spin_loop();
201        }
202
203        let cmd_status = core::ptr::read_volatile(SDCMD);
204        if (cmd_status & SDCMD_FAIL_ERROR) != 0 {
205            return Err("SDHOST: command execution failed");
206        }
207
208        // レスポンス取得
209        let mut resp = [0u32; 4];
210        resp[0] = core::ptr::read_volatile(SDRSP0);
211        if resp_type == SDCMD_RESP_136 {
212            resp[1] = core::ptr::read_volatile(SDRSP1);
213            resp[2] = core::ptr::read_volatile(SDRSP2);
214            resp[3] = core::ptr::read_volatile(SDRSP3);
215        }
216
217        Ok(resp)
218    }
219}
220
221/// SDHOST クロック分周器設定 (周波数変更用)
222fn set_clock(hz: u32) {
223    unsafe {
224        // SDHOST コントローラの内部クロックは約 250MHz (QEMUではダミーだが実機対応)
225        let host_clk = 250_000_000;
226        let mut div = host_clk / hz;
227        if div < 2 {
228            div = 2;
229        }
230        // SDCDIV の設定値は (div / 2) - 1 もしくは簡易に
231        let cdiv = if div >= 2 { (div / 2) - 1 } else { 0 };
232        core::ptr::write_volatile(SDCDIV, cdiv & 0x7FF);
233        delay_us(100);
234    }
235}
236
237/// SDHOST および SDカードの初期化シーケンス
238pub fn init() -> Result<(), &'static str> {
239    if SD_INITIALIZED.load(Ordering::SeqCst) {
240        return Ok(());
241    }
242
243    crate::info!("[SD] Initializing BCM2835 SDHOST controller...");
244
245    setup_gpio();
246
247    unsafe {
248        // SDHOST のパワーON
249        core::ptr::write_volatile(SDVDD, 1); // Power on VDD
250        core::ptr::write_volatile(SDTOUT, 0xF00000); // タイムアウト値設定
251        core::ptr::write_volatile(SDHSTS, 0x7F); // ステータスクリア
252        core::ptr::write_volatile(SDHCFG, 0); // 初期設定クリア
253    }
254
255    // 初期化用低速クロック (400kHz)
256    set_clock(400_000);
257
258    // QEMU/実機での安定稼働のためのウェイト
259    delay_us(10000);
260
261    // CMD0: GO_IDLE_STATE
262    let _ = send_command(0, 0, SDCMD_RESP_NONE);
263    delay_us(5000);
264
265    // CMD8: SEND_IF_COND (3.3V, check pattern 0xAA)
266    let has_hc = match send_command(8, 0x1AA, SDCMD_RESP_48) {
267        Ok(resp) => (resp[0] & 0xFF) == 0xAA,
268        Err(_) => false,
269    };
270
271    // ACMD41: SD_SEND_OP_COND でカードがReadyになるまでポーリング
272    let mut ocr = 0x0010_0000; // 3.2-3.3V support
273    if has_hc {
274        ocr |= 0x4000_0000; // HCS (High Capacity Support)
275    }
276
277    let mut ready = false;
278    for _ in 0..100 {
279        // ACMD は CMD55 -> CMD41 の順
280        if send_command(55, 0, SDCMD_RESP_48).is_err() {
281            continue;
282        }
283        if let Ok(resp) = send_command(41, ocr, SDCMD_RESP_48) {
284            if (resp[0] & 0x8000_0000) != 0 {
285                ready = true;
286                ocr = resp[0];
287                break;
288            }
289        }
290        delay_us(10000);
291    }
292
293    if !ready {
294        return Err("SDHOST: Card initialization failed (ACMD41 timeout)");
295    }
296
297    let is_sdhc = (ocr & 0x4000_0000) != 0;
298    crate::info!("[SD] Card detected: SDHC={}", is_sdhc);
299
300    // CMD2: ALL_SEND_CID
301    let _cid = send_command(2, 0, SDCMD_RESP_136)?;
302
303    // CMD3: SEND_RELATIVE_ADDR
304    let resp3 = send_command(3, 0, SDCMD_RESP_48)?;
305    let rca = resp3[0] & 0xFFFF_0000;
306    unsafe {
307        RCA = rca;
308    }
309
310    // CMD9: SEND_CSD (容量算出のためレジスタをパース)
311    let csd = send_command(9, rca, SDCMD_RESP_136)?;
312    parse_csd(csd)?;
313
314    // CMD7: SELECT_CARD (カードを選択状態にしデータ通信を許可)
315    let _ = send_command(7, rca, SDCMD_RESP_48)?;
316
317    // CMD16: SET_BLOCKLEN (ブロック長を 512 バイトに設定)
318    let _ = send_command(16, 512, SDCMD_RESP_48)?;
319
320    // ACMD6: SET_BUS_WIDTH (4-bit バス幅に切り替え)
321    let _ = send_command(55, rca, SDCMD_RESP_48)?;
322    let _ = send_command(6, 2, SDCMD_RESP_48)?; // 2: 4-bit bus
323
324    // データ転送用の高速クロック (25MHz) へ切り替え
325    set_clock(25_000_000);
326
327    // SDHOST ホスト設定レジスタ (SDHCFG) の設定。
328    //
329    // 【2026-07-26 重大バグ修正】従来は `0x12` を書いていた。BCM2835 SDHOST の
330    // SDHCFG のビット割り当ては
331    //   bit0 REL_CMD_LINE / bit1 WIDE_INT_BUS / bit2 WIDE_EXT_BUS /
332    //   bit3 SLOW_CARD / bit4 DATA_IRPT_EN / bit5 SDIO_IRPT_EN /
333    //   bit8 BLOCK_IRPT_EN / bit10 BUSY_IRPT_EN
334    // であり、`0x12` は WIDE_INT_BUS + DATA_IRPT_EN を意味する
335    // (コード内コメントの「bit4 = Slow Card / No DMA」は誤りで、
336    //   SLOW_CARD は bit3、bit4 はデータ割り込み許可)。
337    //
338    // 問題は **WIDE_EXT_BUS (bit2) が立っていなかった**こと。直前の ACMD6 で
339    // カード側は 4bit バス幅へ切り替えているのに、ホスト側は 1bit のままだった。
340    // カードが 4 本の DAT 線で送出するデータをホストが 1 本しか読まないため、
341    // コマンド応答は正常に返るのに**データ転送だけが成立しない**という状態に
342    // なっていた(SDカードは初期化できるのに読み書きが全滅する症状の原因)。
343    // 本ドライバはポーリング方式で割り込みハンドラを持たないため、
344    // 割り込み許可ビットは立てない。
345    unsafe {
346        core::ptr::write_volatile(SDHCFG, SDHCFG_WIDE_INT_BUS | SDHCFG_WIDE_EXT_BUS);
347
348        // SDEDM: FIFO のしきい値設定。
349        // 【2026-07-26】本ドライバはこのレジスタを一度も設定していなかった。
350        // SDHOST は FIFO のしきい値に達して初めて `SDHSTS_DATA_FLAG` を
351        // 立てるため、しきい値が未設定(既定値が不定/0)だとデータ転送の
352        // 準備完了が通知されない可能性がある。Linux の `bcm2835-sdhost.c`
353        // と同じく read/write とも 4 に設定する。
354        let mut edm = core::ptr::read_volatile(SDEDM);
355        edm &= !((SDEDM_THRESHOLD_MASK << SDEDM_READ_THRESHOLD_SHIFT)
356            | (SDEDM_THRESHOLD_MASK << SDEDM_WRITE_THRESHOLD_SHIFT));
357        edm |= (FIFO_READ_THRESHOLD << SDEDM_READ_THRESHOLD_SHIFT)
358            | (FIFO_WRITE_THRESHOLD << SDEDM_WRITE_THRESHOLD_SHIFT);
359        core::ptr::write_volatile(SDEDM, edm);
360    }
361
362    SD_INITIALIZED.store(true, Ordering::SeqCst);
363    unsafe {
364        crate::info!("[SD] SDCard initialization success. Capacity: {} sectors ({} MiB)", CARD_SECTORS, (CARD_SECTORS * 512) / (1024 * 1024));
365    }
366
367    Ok(())
368}
369
370/// CSD (Card Specific Data) レジスタからカードセクタ数を取得
371fn parse_csd(csd: [u32; 4]) -> Result<(), &'static str> {
372    // 136ビットの CSD は 4つの 32ビットレジスタにまたがって格納される。
373    // csd[0] = SDRSP0 (bits 127-96)
374    // csd[1] = SDRSP1 (bits 95-64)
375    // csd[2] = SDRSP2 (bits 63-32)
376    // csd[3] = SDRSP3 (bits 31-0)
377    //
378    // CSD のバージョン確認 (SDRSP0 の bits 127-126, すなわち csd[0] の bits 31-30)
379    let csd_structure = (csd[0] >> 30) & 0x3;
380
381    if csd_structure == 1 {
382        // CSD Version 2.0 (SDHC / SDXC)
383        // C_SIZE は bits 69-48 に格納される。
384        // これは SDRSP1 (bits 95-64) と SDRSP2 (bits 63-32) にまたがる。
385        // SDRSP1 の bits 5-0 (csd[1] & 0x3F) -> C_SIZE の上位 6ビット
386        // SDRSP2 の bits 31-16 (csd[2] >> 16) -> C_SIZE の下位 16ビット
387        let c_size_upper = csd[1] & 0x3F;
388        let c_size_lower = csd[2] >> 16;
389        let c_size = (c_size_upper << 16) | c_size_lower;
390
391        // 容量計算: (C_SIZE + 1) * 512 KB
392        let capacity_kb = (c_size + 1) * 512;
393        let sectors = (capacity_kb * 1024) / 512;
394        unsafe {
395            CARD_SECTORS = sectors as usize;
396        }
397    } else {
398        // CSD Version 1.0 (標準容量 SD)
399        // C_SIZE は bits 73-62、C_SIZE_MULT は bits 49-47
400        let c_size = ((csd[1] & 0x3FF) << 2) | ((csd[2] >> 30) & 0x3);
401        let c_size_mult = (csd[2] >> 15) & 0x7;
402        let read_bl_len = csd[1] & 0xF;
403
404        let mult = 1 << (c_size_mult + 2);
405        let block_len = 1 << read_bl_len;
406        let capacity_bytes = (c_size + 1) * mult * block_len;
407        unsafe {
408            CARD_SECTORS = (capacity_bytes / 512) as usize;
409        }
410    }
411
412    Ok(())
413}
414
415/// 公開用ゲッター
416pub fn get_total_sectors() -> usize {
417    unsafe { CARD_SECTORS }
418}
419
420/// 1セクタ (512バイト) のポーリング読み込み
421pub fn read_sector(lba: usize, buf: &mut [u8]) -> Result<(), &'static str> {
422    if !SD_INITIALIZED.load(Ordering::SeqCst) {
423        return Err("SDHOST: not initialized");
424    }
425    if buf.len() < 512 {
426        return Err("SDHOST: read buffer too small");
427    }
428
429    unsafe {
430        // 1. ブロックサイズおよびブロック数をホストコントローラに設定
431        // バイト数とブロック数はそれぞれ専用レジスタへ設定する。
432        core::ptr::write_volatile(SDHBCT, 512);
433        core::ptr::write_volatile(SDHBLC, 1);
434
435        // 2. 読込コマンド (CMD17) を送信
436        // アドレス引数は、SDHCの場合はセクタ番号(LBA)、標準SDの場合はバイトアドレス
437        let arg = lba as u32;
438        // データ読み出しを伴うコマンドなので READ ビットを立てる
439        // (従来は指定漏れで、コントローラがデータ転送と認識できなかった)。
440        send_command(17, arg, SDCMD_RESP_48 | SDCMD_READ)?;
441
442        // 3. FIFOからデータを 512バイト(32ビットワードで128回)読み出し
443        let mut words_read = 0;
444        let mut timeout = 500000;
445        let buf_u32 = buf.as_mut_ptr() as *mut u32;
446
447        while words_read < 128 {
448            let hsts = core::ptr::read_volatile(SDHSTS);
449            if (hsts & SDHSTS_DATA_FLAG) != 0 {
450                let data = core::ptr::read_volatile(SDDATA);
451                core::ptr::write_volatile(buf_u32.add(words_read), data);
452                words_read += 1;
453            } else if (hsts & SDHSTS_FIFO_ERROR) != 0 {
454                return Err("SDHOST: FIFO error during read");
455            } else {
456                timeout -= 1;
457                if timeout == 0 {
458                    return Err("SDHOST: read timeout waiting for FIFO data");
459                }
460                core::hint::spin_loop();
461            }
462        }
463
464        // コマンド転送完了までビジーチェック
465        timeout = 100000;
466        while (core::ptr::read_volatile(SDHSTS) & SDHSTS_BUSY) != 0 {
467            timeout -= 1;
468            if timeout == 0 {
469                return Err("SDHOST: read busy timeout");
470            }
471            core::hint::spin_loop();
472        }
473
474        // エラーチェック
475        let hsts = core::ptr::read_volatile(SDHSTS);
476        if (hsts & SDHSTS_ERROR) != 0 {
477            core::ptr::write_volatile(SDHSTS, SDHSTS_ERROR); // クリア
478            return Err("SDHOST: read error flag set in host status");
479        }
480    }
481
482    Ok(())
483}
484
485/// 1セクタ (512バイト) のポーリング書き込み
486pub fn write_sector(lba: usize, buf: &[u8]) -> Result<(), &'static str> {
487    if !SD_INITIALIZED.load(Ordering::SeqCst) {
488        return Err("SDHOST: not initialized");
489    }
490    if buf.len() < 512 {
491        return Err("SDHOST: write buffer too small");
492    }
493
494    unsafe {
495        // 1. ブロックサイズおよびブロック数を設定
496        core::ptr::write_volatile(SDHBCT, 512);
497        core::ptr::write_volatile(SDHBLC, 1);
498
499        // 2. 書込コマンド (CMD24) を送信
500        let arg = lba as u32;
501        send_command(24, arg, SDCMD_RESP_48 | SDCMD_WRITE)?;
502
503        // 3. FIFOへデータを 512バイト(32ビットワードで128回)書き出し
504        let mut words_written = 0;
505        let mut timeout = 500000;
506        let buf_u32 = buf.as_ptr() as *const u32;
507
508        while words_written < 128 {
509            let hsts = core::ptr::read_volatile(SDHSTS);
510            if (hsts & SDHSTS_DATA_FLAG) != 0 {
511                let data = core::ptr::read_volatile(buf_u32.add(words_written));
512                core::ptr::write_volatile(SDDATA, data);
513                words_written += 1;
514            } else if (hsts & SDHSTS_FIFO_ERROR) != 0 {
515                return Err("SDHOST: FIFO error during write");
516            } else {
517                timeout -= 1;
518                if timeout == 0 {
519                    return Err("SDHOST: write timeout waiting for FIFO space");
520                }
521                core::hint::spin_loop();
522            }
523        }
524
525        // コマンドおよびフラッシュ完了待ち
526        timeout = 100000;
527        while (core::ptr::read_volatile(SDHSTS) & SDHSTS_BUSY) != 0 {
528            timeout -= 1;
529            if timeout == 0 {
530                return Err("SDHOST: write busy timeout");
531            }
532            core::hint::spin_loop();
533        }
534
535        // エラーチェック
536        let hsts = core::ptr::read_volatile(SDHSTS);
537        if (hsts & SDHSTS_ERROR) != 0 {
538            core::ptr::write_volatile(SDHSTS, SDHSTS_ERROR); // クリア
539            return Err("SDHOST: write error flag set in host status");
540        }
541    }
542
543    Ok(())
544}