Merge ARM11 spi and ARM9 spicard drivers

Also centralize device IDs into SPI.h
This commit is contained in:
Balint Kovacs 2019-07-27 17:03:54 +02:00 committed by d0k3
parent 48d8c48d12
commit d2f596e7a3
10 changed files with 216 additions and 411 deletions

View File

@ -24,9 +24,7 @@
#include <hid_map.h>
#include "hw/codec.h"
#include "hw/spi.h"
#define CODEC_SPI_DEV 3
#include <spi.h>
#define CPAD_THRESH_X (750)
#define CPAD_THRESH_Y (150)
@ -44,7 +42,7 @@ static void CODEC_WriteRead(u32 *txb, u8 txl, u32 *rxb, u8 rxl)
xfers[1].len = rxl;
xfers[1].read = true;
SPI_DoXfer(CODEC_SPI_DEV, xfers, 2);
SPI_DoXfer(SPI_DEV_CODEC, xfers, 2, true);
}
static void CODEC_RegSelect(u8 reg)
@ -58,7 +56,7 @@ static void CODEC_RegSelect(u8 reg)
xfer.len = 2;
xfer.read = false;
SPI_DoXfer(CODEC_SPI_DEV, &xfer, 1);
SPI_DoXfer(SPI_DEV_CODEC, &xfer, 1, true);
}
static u8 CODEC_RegRead(u8 reg)
@ -86,7 +84,7 @@ static void CODEC_RegWrite(u8 reg, u8 val)
xfer.len = 2;
xfer.read = false;
SPI_DoXfer(CODEC_SPI_DEV, &xfer, 1);
SPI_DoXfer(SPI_DEV_CODEC, &xfer, 1, true);
}
static void CODEC_RegMask(u8 reg, u8 mask0, u8 mask1)

View File

@ -18,11 +18,9 @@
#include <types.h>
#include "hw/spi.h"
#include <spi.h>
#include "hw/nvram.h"
#define NVRAM_SPI_DEV 1
// returns manuf id, memory type and size
// size = (1 << id[2]) ?
// apparently unreliable on some Sanyo chips?
@ -51,7 +49,7 @@ static u32 NVRAM_SendStatusCommand(u32 cmd, u32 width)
xfer[1].read = true;
ret = 0;
SPI_DoXfer(NVRAM_SPI_DEV, xfer, 2);
SPI_DoXfer(SPI_DEV_NVRAM, xfer, 2, true);
return ret;
}
@ -91,5 +89,5 @@ void NVRAM_Read(u32 address, u32 *buffer, u32 len)
xfer[1].len = len;
xfer[1].read = true;
SPI_DoXfer(NVRAM_SPI_DEV, xfer, 2);
SPI_DoXfer(SPI_DEV_NVRAM, xfer, 2, true);
}

View File

@ -20,7 +20,7 @@
#include <types.h>
#include "hw/spi.h"
#include <spi.h>
#define NVRAM_SR_WIP BIT(0) // work in progress / busy
#define NVRAM_SR_WEL BIT(1) // write enable latch

View File

@ -30,7 +30,7 @@
#include "hw/gpulcd.h"
#include "hw/i2c.h"
#include "hw/mcu.h"
#include "hw/spi.h"
#include <spi.h>
#include "system/sections.h"

View File

@ -19,7 +19,7 @@
*/
#include "spi.h"
#include "spicard.h"
#include <spi.h>
#include "timer.h"
// declarations for actual implementations
@ -103,19 +103,27 @@ const CardType FLASH_256KB_2_INFRARED = FlashInfraredTypes + 1;
const CardType FLASH_512KB_1_INFRARED = FlashInfraredTypes + 2;
const CardType FLASH_512KB_2_INFRARED = FlashInfraredTypes + 3;
int SPIWriteRead(CardType type, void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize) {
const u32 headerFooterVal = 0;
bool b = type->infrared;
#define REG_CFG9_CARDCTL *((vu16*)0x1000000C)
#define CARDCTL_SPICARD (1u<<8)
SPICARD_init();
int SPIWriteRead(CardType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize) {
u32 headerFooterVal = 0;
if (b) {
SPICARD_writeRead(NSPI_CLK_1MHz, &headerFooterVal, NULL, 1, 0, false);
REG_CFG9_CARDCTL |= CARDCTL_SPICARD;
if (type->infrared) {
SPI_XferInfo irXfer = { &headerFooterVal, 1, false };
SPI_DoXfer(SPI_DEV_CART_IR, &irXfer, 1, false);
}
SPICARD_writeRead(NSPI_CLK_4MHz, cmd, answer, cmdSize, answerSize, false);
SPICARD_writeRead(NSPI_CLK_4MHz, data, NULL, dataSize, 0, true);
SPICARD_deinit();
SPI_XferInfo transfers[3] = {
{ (u8*) cmd, cmdSize, false },
{ answer, answerSize, true },
{ (u8*) data, dataSize, false },
};
SPI_DoXfer(SPI_DEV_CART_FLASH, transfers, 3, true);
REG_CFG9_CARDCTL &= ~CARDCTL_SPICARD;
return 0;
}

View File

@ -97,7 +97,7 @@ const CardType FLASH_256KB_2_INFRARED;
const CardType FLASH_512KB_1_INFRARED;
const CardType FLASH_512KB_2_INFRARED;
int SPIWriteRead(CardType type, void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize);
int SPIWriteRead(CardType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize);
int SPIWaitWriteEnd(CardType type);
int SPIEnableWriting(CardType type);
int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg);

View File

@ -1,120 +0,0 @@
/*
* This file is part of fastboot 3DS
* Copyright (C) 2019 derrek, profi200
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "types.h"
#include "spicard.h"
#include "delay.h"
#define REG_CFG9_CARDCTL *((vu16*)0x1000000C)
#define SPICARD_REGS_BASE 0x1000D800
#define REG_NSPI_CNT *((vu32*)(SPICARD_REGS_BASE + 0x00))
#define REG_NSPI_DONE *((vu32*)(SPICARD_REGS_BASE + 0x04))
#define REG_NSPI_BLKLEN *((vu32*)(SPICARD_REGS_BASE + 0x08))
#define REG_NSPI_FIFO *((vu32*)(SPICARD_REGS_BASE + 0x0C))
#define REG_NSPI_STATUS *((vu32*)(SPICARD_REGS_BASE + 0x10))
#define REG_NSPI_AUTOPOLL *((vu32*)(SPICARD_REGS_BASE + 0x14))
#define REG_NSPI_INT_MASK *((vu32*)(SPICARD_REGS_BASE + 0x18))
#define REG_NSPI_INT_STAT *((vu32*)(SPICARD_REGS_BASE + 0x1C))
static inline void nspiWaitBusy(void)
{
while(REG_NSPI_CNT & NSPI_CNT_ENABLE);
}
static inline void nspiWaitFifoBusy(void)
{
while(REG_NSPI_STATUS & NSPI_STATUS_BUSY);
}
void SPICARD_init(void)
{
REG_CFG9_CARDCTL |= 1u<<8;
}
void SPICARD_deinit(void)
{
REG_CFG9_CARDCTL &= ~(1u<<8);
}
/*
bool _SPICARD_autoPollBit(u32 params)
{
REG_NSPI_AUTOPOLL = NSPI_AUTOPOLL_START | params;
u32 res;
do
{
__wfi();
res = REG_NSPI_INT_STAT;
} while(!(res & (NSPI_INT_AP_TIMEOUT | NSPI_INT_AP_SUCCESS)));
REG_NSPI_INT_STAT = res; // Aknowledge
return (res & NSPI_INT_AP_TIMEOUT) == 0; // Timeout error
}
*/
void SPICARD_writeRead(NspiClk clk, const void *in, void *out, u32 inSize, u32 outSize, bool done)
{
const u32 cntParams = NSPI_CNT_ENABLE | NSPI_CNT_BUS_1BIT | clk;
REG_CFG9_CARDCTL |= 1u<<8;
u32 buf;
char *in_ = (char *) in;
char *out_ = (char *) out;
if(in_)
{
REG_NSPI_BLKLEN = inSize;
REG_NSPI_CNT = cntParams | NSPI_CNT_DIRE_WRITE;
u32 counter = 0;
do
{
if((counter & 31) == 0) nspiWaitFifoBusy();
memcpy(&buf, in_, min(4, inSize - counter));
REG_NSPI_FIFO = buf;
counter += 4;
in_ += 4;
} while(counter < inSize);
nspiWaitBusy();
}
if(out_)
{
REG_NSPI_BLKLEN = outSize;
REG_NSPI_CNT = cntParams | NSPI_CNT_DIRE_READ;
u32 counter = 0;
do
{
if((counter & 31) == 0) nspiWaitFifoBusy();
buf = REG_NSPI_FIFO;
memcpy(out_, &buf, min(4, outSize - counter));
counter += 4;
out_ += 4;
} while(counter < outSize);
nspiWaitBusy();
}
if(done) REG_NSPI_DONE = NSPI_DONE;
}

View File

@ -1,101 +0,0 @@
#pragma once
/*
* This file is part of fastboot 3DS
* Copyright (C) 2019 derrek, profi200
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "types.h"
// REG_NSPI_CNT
#define NSPI_CNT_BUS_1BIT (0u)
#define NSPI_CNT_BUS_4BIT (1u<<12)
#define NSPI_CNT_DIRE_READ (0u)
#define NSPI_CNT_DIRE_WRITE (1u<<13)
#define NSPI_CNT_ENABLE (1u<<15)
// REG_NSPI_DONE
#define NSPI_DONE (0u)
// REG_NSPI_STATUS
#define NSPI_STATUS_BUSY (1u)
// REG_NSPI_AUTOPOLL
#define NSPI_AUTOPOLL_START (1u<<31)
// REG_NSPI_INT_MASK Bit set = disabled.
// REG_NSPI_INT_STAT Status and aknowledge.
#define NSPI_INT_TRANSF_END (1u) // Fires on (each?) auto poll try aswell
#define NSPI_INT_AP_SUCCESS (1u<<1) // Auto poll
#define NSPI_INT_AP_TIMEOUT (1u<<2) // Auto poll
typedef enum
{
NSPI_CLK_512KHz = 0u,
NSPI_CLK_1MHz = 1u,
NSPI_CLK_2MHz = 2u,
NSPI_CLK_4MHz = 3u,
NSPI_CLK_8MHz = 4u,
NSPI_CLK_16MHz = 5u
} NspiClk;
/**
* @brief Activates the SPI bus. Use after some cartridge interface has been initialized.
*/
void SPICARD_init(void);
/**
* @brief Deactivates the SPI bus.
*/
void SPICARD_deinit(void);
/**
* @brief Automatically polls a bit of the command response. Use with the macro below.
*
* @param[in] params The parameters. Use the macro below.
*
* @return Returns false on failure/timeout and true on success.
*/
bool _SPICARD_autoPollBit(u32 params);
/**
* @brief Writes and/or reads data to/from a SPI device.
*
* @param[in] clk The clock frequency to use.
* @param[in] in Input data pointer for write.
* @param out Output data pointer for read.
* @param[in] inSize Input size. Must be <= 0x1FFFFF.
* @param[in] outSize Output size. Must be <= 0x1FFFFF.
* @param[in] done Set to true if this is the last transfer (chip select).
*/
void SPICARD_writeRead(NspiClk clk, const void *in, void *out, u32 inSize, u32 outSize, bool done);
/**
* @brief Automatically polls a bit of the command response.
*
* @param[in] cmd The command.
* @param[in] timeout The timeout. Must be 0-15. Tries = 31<<NspiClk + timeout.
* @param[in] off The bit offset. Must be 0-7.
* @param[in] bitSet Poll for a set ur unset bit.
*
* @return Returns false on failure/timeout and true on success.
*/
#define SPICARD_autoPollBit(cmd, timeout, off, bitSet) _SPICARD_autoPollBit((bitSet)<<30 | (off)<<24 | (timeout)<<16 | (cmd))

View File

@ -1,134 +1,151 @@
// Somewhat based on xerpi's SPI driver for Linux
/*
* This file is part of GodMode9
* Copyright (C) 2016 Sergi Granell
* Copyright (C) 2019 Wolfvak
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <common.h>
#include <types.h>
#include "hw/spi.h"
#define REG_CFG_SPI_CNT ((vu16*)0x101401C0)
#define REG_SPI(bus, reg) (*((vu32*)((bus) + (reg))))
#define REG_SPI_BUS0 (0x10160800)
#define REG_SPI_BUS1 (0x10142800)
#define REG_SPI_BUS2 (0x10143800)
#define REG_SPI_CONTROL 0x00
#define REG_SPI_DONE 0x04
#define REG_SPI_BLKLEN 0x08
#define REG_SPI_FIFO 0x0C
#define REG_SPI_STAT 0x10
#define SPI_CONTROL_RATE(n) (n)
#define SPI_CONTROL_CS(n) ((n) << 6)
#define SPI_DIRECTION_READ (0)
#define SPI_DIRECTION_WRITE BIT(13)
#define SPI_CONTROL_BUSY BIT(15)
#define SPI_CONTROL_START BIT(15)
#define SPI_STAT_BUSY BIT(0)
#define SPI_FIFO_WIDTH (32)
static struct {
u32 bus;
u32 reg;
} SPI_Devices[] = {
{REG_SPI_BUS0, SPI_CONTROL_RATE(2) | SPI_CONTROL_CS(0)},
{REG_SPI_BUS0, SPI_CONTROL_RATE(0) | SPI_CONTROL_CS(1)}, // NVRAM
{REG_SPI_BUS0, SPI_CONTROL_RATE(0) | SPI_CONTROL_CS(2)},
{REG_SPI_BUS1, SPI_CONTROL_RATE(5) | SPI_CONTROL_CS(0)}, // CODEC
// TODO: complete this table
};
static void SPI_WaitBusy(u32 bus)
{
while(REG_SPI(bus, REG_SPI_CONTROL) & SPI_CONTROL_BUSY);
}
static void SPI_WaitFIFO(u32 bus)
{
while(REG_SPI(bus, REG_SPI_STAT) & SPI_STAT_BUSY);
}
static void SPI_Done(u32 bus)
{
REG_SPI(bus, REG_SPI_DONE) = 0;
}
static void SPI_SingleXfer(u32 reg, u32 bus, u32 *buffer, u32 len, bool read)
{
u32 pos = 0;
REG_SPI(bus, REG_SPI_BLKLEN) = len;
REG_SPI(bus, REG_SPI_CONTROL) = reg |
(read ? SPI_DIRECTION_READ : SPI_DIRECTION_WRITE) | SPI_CONTROL_START;
SPI_WaitFIFO(bus);
do {
if ((pos % SPI_FIFO_WIDTH) == 0)
SPI_WaitFIFO(bus);
if (read) {
buffer[pos / 4] = REG_SPI(bus, REG_SPI_FIFO);
} else {
REG_SPI(bus, REG_SPI_FIFO) = buffer[pos / 4];
}
pos += 4;
} while(pos < len);
}
int SPI_DoXfer(u32 dev, const SPI_XferInfo *xfers, u32 xfer_cnt)
{
u32 bus, reg;
bus = SPI_Devices[dev].bus;
reg = SPI_Devices[dev].reg;
for (u32 i = 0; i < xfer_cnt; i++) {
const SPI_XferInfo *xfer = &xfers[i];
if (!xfer->buf || !xfer->len)
continue;
SPI_WaitBusy(bus);
SPI_SingleXfer(reg, bus, xfer->buf, xfer->len, xfer->read);
}
SPI_WaitBusy(bus);
SPI_Done(bus);
return 0;
}
void SPI_Init(void)
{
// This cuts off access to the old NDS SPI interface
*REG_CFG_SPI_CNT = 7;
}
void SPI_Deinit(void)
{
// Keep backwards compatibility with software that
// assumes all bus interfaces will be set to old
*REG_CFG_SPI_CNT = 0;
}
// Somewhat based on xerpi's SPI driver for Linux
/*
* This file is part of GodMode9
* Copyright (C) 2016 Sergi Granell
* Copyright (C) 2019 Wolfvak
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <common.h>
#include <types.h>
#include "spi.h"
#define REG_CFG_SPI_CNT ((vu16*)0x101401C0)
#define REG_SPI(bus, reg) (*((vu32*)((bus) + (reg))))
#define REG_SPI_BUS0 (0x10160800)
#define REG_SPI_BUS1 (0x10142800)
#define REG_SPI_BUS2 (0x10143800)
#define REG_SPI_CARD (0x1000D800)
#define REG_SPI_CONTROL 0x00
#define REG_SPI_DONE 0x04
#define REG_SPI_BLKLEN 0x08
#define REG_SPI_FIFO 0x0C
#define REG_SPI_STAT 0x10
#define SPI_CONTROL_RATE(n) (n)
#define SPI_CONTROL_CS(n) ((n) << 6)
#define SPI_DIRECTION_READ (0)
#define SPI_DIRECTION_WRITE BIT(13)
#define SPI_CONTROL_BUSY BIT(15)
#define SPI_CONTROL_START BIT(15)
#define SPI_STAT_BUSY BIT(0)
#define SPI_FIFO_WIDTH (32)
static struct {
u32 bus;
u32 reg;
} SPI_Devices[] = {
{REG_SPI_BUS0, SPI_CONTROL_RATE(2) | SPI_CONTROL_CS(0)},
{REG_SPI_BUS0, SPI_CONTROL_RATE(0) | SPI_CONTROL_CS(1)}, // NVRAM
{REG_SPI_BUS0, SPI_CONTROL_RATE(0) | SPI_CONTROL_CS(2)},
{REG_SPI_BUS1, SPI_CONTROL_RATE(5) | SPI_CONTROL_CS(0)}, // CODEC
{REG_SPI_CARD, SPI_CONTROL_RATE(3) | SPI_CONTROL_CS(0)}, // Gamecard flash chip
{REG_SPI_CARD, SPI_CONTROL_RATE(1) | SPI_CONTROL_CS(0)}, // Gamecard IR chip
// TODO: complete this table
};
static void SPI_WaitBusy(u32 bus)
{
while(REG_SPI(bus, REG_SPI_CONTROL) & SPI_CONTROL_BUSY);
}
static void SPI_WaitFIFO(u32 bus)
{
while(REG_SPI(bus, REG_SPI_STAT) & SPI_STAT_BUSY);
}
static void SPI_Done(u32 bus)
{
REG_SPI(bus, REG_SPI_DONE) = 0;
}
static void SPI_SingleXfer(u32 reg, u32 bus, void *buffer, u32 len, bool read)
{
u32 pos = 0;
bool aligned = ((u32)buffer % 4 == 0) && (len % 4 == 0);
REG_SPI(bus, REG_SPI_BLKLEN) = len;
REG_SPI(bus, REG_SPI_CONTROL) = reg |
(read ? SPI_DIRECTION_READ : SPI_DIRECTION_WRITE) | SPI_CONTROL_START;
SPI_WaitFIFO(bus);
do {
if ((pos % SPI_FIFO_WIDTH) == 0)
SPI_WaitFIFO(bus);
if(aligned) {
if (read) {
((u32*)buffer)[pos / 4] = REG_SPI(bus, REG_SPI_FIFO);
} else {
REG_SPI(bus, REG_SPI_FIFO) = ((u32*)buffer)[pos / 4];
}
} else {
if (read) {
u32 tmp = REG_SPI(bus, REG_SPI_FIFO);
memcpy((u8 *) buffer + pos, &tmp, min(4, len - pos));
} else {
u32 tmp;
memcpy(&tmp, (u8 *) buffer + pos, min(4, len - pos));
REG_SPI(bus, REG_SPI_FIFO) = tmp;
}
}
pos += 4;
} while(pos < len);
}
int SPI_DoXfer(u32 dev, const SPI_XferInfo *xfers, u32 xfer_cnt, bool done)
{
u32 bus, reg;
bus = SPI_Devices[dev].bus;
reg = SPI_Devices[dev].reg;
for (u32 i = 0; i < xfer_cnt; i++) {
const SPI_XferInfo *xfer = &xfers[i];
if (!xfer->buf || !xfer->len)
continue;
SPI_WaitBusy(bus);
SPI_SingleXfer(reg, bus, xfer->buf, xfer->len, xfer->read);
}
SPI_WaitBusy(bus);
if(done) {
SPI_Done(bus);
}
return 0;
}
void SPI_Init(void)
{
// This cuts off access to the old NDS SPI interface
*REG_CFG_SPI_CNT = 7;
}
void SPI_Deinit(void)
{
// Keep backwards compatibility with software that
// assumes all bus interfaces will be set to old
*REG_CFG_SPI_CNT = 0;
}

View File

@ -1,32 +1,37 @@
/*
* This file is part of GodMode9
* Copyright (C) 2019 Wolfvak
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
typedef struct {
u32 *buf;
u32 len;
bool read;
} SPI_XferInfo;
int SPI_DoXfer(u32 dev, const SPI_XferInfo *xfer, u32 xfer_cnt);
void SPI_Init(void);
void SPI_Deinit(void);
/*
* This file is part of GodMode9
* Copyright (C) 2019 Wolfvak
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#define SPI_DEV_NVRAM 1
#define SPI_DEV_CODEC 3
#define SPI_DEV_CART_FLASH 4
#define SPI_DEV_CART_IR 5
typedef struct {
void *buf;
u32 len;
bool read;
} SPI_XferInfo;
int SPI_DoXfer(u32 dev, const SPI_XferInfo *xfer, u32 xfer_cnt, bool done);
void SPI_Init(void);
void SPI_Deinit(void);