mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-08 23:04:13 +08:00
144 lines
3.9 KiB
C
144 lines
3.9 KiB
C
/*
|
|
* This file is part of open_agb_firm
|
|
* Copyright (C) 2021 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 "mem_map.h"
|
|
#include "arm11/drivers/spi.h"
|
|
#include "arm11/drivers/cfg11.h"
|
|
#include "arm11/drivers/interrupt.h"
|
|
|
|
|
|
static const struct
|
|
{
|
|
u8 busId;
|
|
u8 csClk;
|
|
} g_spiDevTable[4] =
|
|
{
|
|
{SPI_BUS1, NSPI_CS_0 | NSPI_CLK_2MHZ},
|
|
{SPI_BUS1, NSPI_CS_1 | NSPI_CLK_4MHZ}, // Used with the old interface in Horizon OS?
|
|
{SPI_BUS1, NSPI_CS_2 | NSPI_CLK_4MHZ}, // Used with the old interface in Horizon OS.
|
|
{SPI_BUS2, NSPI_CS_0 | NSPI_CLK_16MHZ},
|
|
//{SPI_BUS2, NSPI_CS_1 | NSPI_CLK_512KHZ}, // Unused.
|
|
//{SPI_BUS2, NSPI_CS_2 | NSPI_CLK_512KHZ}, // Unused.
|
|
//{SPI_BUS3, NSPI_CS_0 | NSPI_CLK_512KHZ} // Debugger?
|
|
};
|
|
|
|
|
|
|
|
static NspiBus* getBusRegs(const u8 busId)
|
|
{
|
|
// Don't force inline here.
|
|
return getNspiBusRegs(busId);
|
|
}
|
|
|
|
static inline void nspiWaitBusy(const NspiBus *const nspiBus)
|
|
{
|
|
while(nspiBus->cnt & NSPI_EN);
|
|
}
|
|
|
|
static inline void nspiWaitFifoBusy(const NspiBus *const nspiBus)
|
|
{
|
|
while(nspiBus->fifo_stat & NSPI_FIFO_BUSY);
|
|
}
|
|
|
|
void NSPI_init(void)
|
|
{
|
|
static bool inited = false;
|
|
if(inited) return;
|
|
inited = true;
|
|
|
|
// Switch all 3 buses to the new interface.
|
|
getCfg11Regs()->spi_cnt = SPI_CNT_SPI3_NEW_IF | SPI_CNT_SPI2_NEW_IF | SPI_CNT_SPI1_NEW_IF;
|
|
|
|
NspiBus *nspiBus = getBusRegs(SPI_BUS1);
|
|
nspiBus->int_mask = NSPI_INT_TRAN_END; // Disable interrupt 1.
|
|
nspiBus->int_stat = NSPI_INT_AP_TMOUT | NSPI_INT_AP_MATCH | NSPI_INT_TRAN_END; // Acknowledge.
|
|
|
|
nspiBus = getBusRegs(SPI_BUS2);
|
|
nspiBus->int_mask = NSPI_INT_TRAN_END;
|
|
nspiBus->int_stat = NSPI_INT_AP_TMOUT | NSPI_INT_AP_MATCH | NSPI_INT_TRAN_END;
|
|
|
|
nspiBus = getBusRegs(SPI_BUS3);
|
|
nspiBus->int_mask = NSPI_INT_TRAN_END;
|
|
nspiBus->int_stat = NSPI_INT_AP_TMOUT | NSPI_INT_AP_MATCH | NSPI_INT_TRAN_END;
|
|
|
|
IRQ_registerIsr(IRQ_SPI2, 14, 0, NULL);
|
|
IRQ_registerIsr(IRQ_SPI3, 14, 0, NULL);
|
|
IRQ_registerIsr(IRQ_SPI1, 14, 0, NULL);
|
|
}
|
|
|
|
bool NSPI_autoPollBit(SpiDevice dev, const u32 apParams)
|
|
{
|
|
dev &= 0x7Fu;
|
|
NspiBus *const nspiBus = getBusRegs(g_spiDevTable[dev].busId);
|
|
|
|
nspiBus->cnt = g_spiDevTable[dev].csClk;
|
|
nspiBus->autopoll = NSPI_AP_START | apParams;
|
|
|
|
u32 res;
|
|
do
|
|
{
|
|
__wfi();
|
|
res = nspiBus->int_stat;
|
|
} while(!(res & (NSPI_INT_AP_TMOUT | NSPI_INT_AP_MATCH)));
|
|
nspiBus->int_stat = res; // Aknowledge.
|
|
|
|
return (res & NSPI_INT_AP_TMOUT) == 0; // Timeout error.
|
|
}
|
|
|
|
// Note: Removing the counter check very slightly reduces performance.
|
|
void NSPI_sendRecv(const SpiDevice dev, const void *in, void *out, const u32 inSize, const u32 outSize)
|
|
{
|
|
NspiBus *const nspiBus = getBusRegs(g_spiDevTable[dev & 0x7Fu].busId);
|
|
const u32 cntParams = NSPI_EN | g_spiDevTable[dev & 0x7Fu].csClk;
|
|
const u32 *in32 = in;
|
|
u32 *out32 = out;
|
|
|
|
if(in32)
|
|
{
|
|
nspiBus->blklen = inSize;
|
|
nspiBus->cnt = cntParams | NSPI_DIR_S;
|
|
|
|
u32 counter = 0;
|
|
do
|
|
{
|
|
if((counter & 31) == 0) nspiWaitFifoBusy(nspiBus);
|
|
nspiBus->fifo = *in32++;
|
|
counter += 4;
|
|
} while(counter < inSize);
|
|
|
|
nspiWaitBusy(nspiBus);
|
|
}
|
|
if(out32)
|
|
{
|
|
nspiBus->blklen = outSize;
|
|
nspiBus->cnt = cntParams | NSPI_DIR_R;
|
|
|
|
u32 counter = 0;
|
|
do
|
|
{
|
|
if((counter & 31) == 0) nspiWaitFifoBusy(nspiBus);
|
|
*out32++ = nspiBus->fifo;
|
|
counter += 4;
|
|
} while(counter < outSize);
|
|
|
|
nspiWaitBusy(nspiBus);
|
|
}
|
|
|
|
if(dev & NSPI_DEV_CS_HIGH) nspiBus->cs = NSPI_CS_HIGH;
|
|
} |