mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-19 01:15:34 -04:00
129 lines
3.2 KiB
C
Executable File
129 lines
3.2 KiB
C
Executable File
// 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 from the old SPI
|
|
// interface used during the NDS days
|
|
*REG_CFG_SPI_CNT = 7;
|
|
}
|