diff --git a/include/arm11/hardware/cfg11.h b/include/arm11/hardware/cfg11.h index 773b2c7..0eb3176 100644 --- a/include/arm11/hardware/cfg11.h +++ b/include/arm11/hardware/cfg11.h @@ -23,6 +23,7 @@ #define CFG11_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x40000) #define REG_CFG11_FIQ_CNT *((vu8* )(CFG11_REGS_BASE + 0x0104)) +#define REG_CFG11_SPI_CNT *((vu32*)(CFG11_REGS_BASE + 0x01C0)) #define REG_UNK_10140400 *((vu8* )(CFG11_REGS_BASE + 0x0400)) #define REG_UNK_10140410 *((vu32*)(CFG11_REGS_BASE + 0x0410)) #define REG_CFG11_BOOTROM_OVERLAY_CNT *((vu8* )(CFG11_REGS_BASE + 0x0420)) diff --git a/include/arm11/hardware/codec.h b/include/arm11/hardware/codec.h new file mode 100644 index 0000000..f3c8b5f --- /dev/null +++ b/include/arm11/hardware/codec.h @@ -0,0 +1,34 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2019 Sergi Granell (xerpi), Paul LaMendola (paulguy), 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 . + */ + +#include "types.h" + + +/** + * @brief Initialize CODEC for Circle-Pad/Touchscreen. + */ +void CODEC_init(void); + +/** + * @brief Get raw ADC data for Circle-Pad/Touchscreen. + * + * @param[in] buf The buffer to write the data to. + */ +void CODEC_getRawData(u32 buf[13]); diff --git a/include/arm11/hardware/spi.h b/include/arm11/hardware/spi.h new file mode 100644 index 0000000..9b6f0b1 --- /dev/null +++ b/include/arm11/hardware/spi.h @@ -0,0 +1,61 @@ +#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 . + */ + +#include "types.h" + + +// SPI_NEW_CNT +#define SPI_CNT_DIRE_READ (0u) +#define SPI_CNT_DIRE_WRITE (1u<<13) +#define SPI_CNT_ENABLE (1u<<15) + +// SPI_NEW_STATUS +#define SPI_STATUS_BUSY (1u) + + +// TODO: Proper device table +typedef enum +{ + SPI_DEV_UNK1 = 0, + SPI_DEV_NVRAM = 1, + SPI_DEV_UNK2 = 2, + SPI_DEV_CODEC = 3, + SPI_DEV_UNK3 = 0, + SPI_DEV_UNK4 = 0, +} SpiDevice; + + + +/** + * @brief Initializes the SPI buses. Call this only once. + */ +void SPI_init(void); + +/** + * @brief Writes and/or reads data to/from a SPI device. + * + * @param[in] dev The device ID. See table above. + * @param[in] in Input data pointer for write. + * @param out Output data pointer for read. + * @param[in] inSize Input size. + * @param[in] outSize Output size. + * @param[in] done Set to true if this is the last transfer (chip select). + */ +void SPI_writeRead(SpiDevice dev, const u32 *in, u32 *out, u32 inSize, u32 outSize, bool done); diff --git a/source/arm11/hardware/codec.c b/source/arm11/hardware/codec.c new file mode 100644 index 0000000..be4bda5 --- /dev/null +++ b/source/arm11/hardware/codec.c @@ -0,0 +1,118 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2019 Sergi Granell (xerpi), Paul LaMendola (paulguy), 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 . + */ + +// Based on code from https://github.com/xerpi/linux_3ds/blob/master/drivers/input/misc/nintendo3ds_codec_hid.c + +#include "types.h" +#include "arm11/hardware/spi.h" + + + +static void codecSelectReg(u8 reg) +{ + alignas(4) u8 buf[4]; + + buf[0] = 0; + buf[1] = reg; + + SPI_writeRead(SPI_DEV_CODEC, (u32*)buf, NULL, 2, 0, true); +} + +static u8 codecReadReg(u8 offset) +{ + alignas(4) u8 buf[4]; + + buf[0] = offset<<1 | 1u; + SPI_writeRead(SPI_DEV_CODEC, (u32*)buf, (u32*)buf, 1, 1, true); + + return buf[0]; +} + +static void codecWriteReg(u8 reg, u8 val) +{ + alignas(4) u8 buf[4]; + + buf[0] = reg<<1; // Write + buf[1] = val; + + SPI_writeRead(SPI_DEV_CODEC, (u32*)buf, NULL, 2, 0, true); +} + +static void codecReadRegBuf(u8 offset, u32 *buffer, u32 size) +{ + alignas(4) u8 buf[4]; + + buf[0] = offset<<1 | 1u; + SPI_writeRead(SPI_DEV_CODEC, (u32*)buf, buffer, 1, size, true); +} + +static void codecMaskReg(u8 offset, u8 mask0, u8 mask1) +{ + alignas(4) u8 buf[4]; + + buf[0] = offset<<1 | 1u; + SPI_writeRead(SPI_DEV_CODEC, (u32*)buf, (u32*)buf, 1, 1, true); + + buf[1] = (buf[0] & ~mask1) | (mask0 & mask1); + buf[0] = offset<<1; + + SPI_writeRead(SPI_DEV_CODEC, (u32*)buf, NULL, 2, 0, true); +} + +void CODEC_init(void) +{ + codecSelectReg(0x67); + codecWriteReg(0x24, 0x98); + codecSelectReg(0x67); + codecWriteReg(0x26, 0x00); + codecSelectReg(0x67); + codecWriteReg(0x25, 0x43); + codecSelectReg(0x67); + codecWriteReg(0x24, 0x18); + codecSelectReg(0x67); + codecWriteReg(0x17, 0x43); + codecSelectReg(0x67); + codecWriteReg(0x19, 0x69); + codecSelectReg(0x67); + codecWriteReg(0x1B, 0x80); + codecSelectReg(0x67); + codecWriteReg(0x27, 0x11); + codecSelectReg(0x67); + codecWriteReg(0x26, 0xEC); + codecSelectReg(0x67); + codecWriteReg(0x24, 0x18); + codecSelectReg(0x67); + codecWriteReg(0x25, 0x53); + + codecSelectReg(0x67); + codecMaskReg(0x26, 0x80, 0x80); + codecSelectReg(0x67); + codecMaskReg(0x24, 0x00, 0x80); + codecSelectReg(0x67); + codecMaskReg(0x25, 0x10, 0x3C); +} + +void CODEC_getRawData(u32 buf[13]) +{ + //codecSelectReg(0x67); + // This reg read seems useless and doesn't affect funtionality. + //codecReadReg(0x26); + + codecSelectReg(0xFB); + codecReadRegBuf(1, buf, 52); +} diff --git a/source/arm11/hardware/spi.c b/source/arm11/hardware/spi.c new file mode 100644 index 0000000..16df7ee --- /dev/null +++ b/source/arm11/hardware/spi.c @@ -0,0 +1,148 @@ +/* + * 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 . + */ + +#include "types.h" +#include "mem_map.h" +#include "arm11/hardware/spi.h" +#include "arm11/hardware/cfg11.h" +#include "arm11/hardware/interrupt.h" + + +#define SPI1_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x42800) +#define SPI2_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x43800) +#define SPI3_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x60800) + + +typedef struct +{ + vu32 SPI_NEW_CNT; + vu32 SPI_NEW_DONE; + vu32 SPI_NEW_BLKLEN; + vu32 SPI_NEW_FIFO; + vu32 SPI_NEW_STATUS; +} SpiRegs; + +enum +{ + SPI_BUS1 = 0, + SPI_BUS2 = 1, + SPI_BUS3 = 2 +}; + +// TODO: Confirm these baudrates +enum +{ + SPI_BAUD_128KHz = 0, + SPI_BAUD_256KHz = 1, + SPI_BAUD_512KHz = 2, + SPI_BAUD_1MHz = 3, + SPI_BAUD_2MHz = 4, + SPI_BAUD_4MHz = 5, +}; + +static const struct +{ + u8 busId; + u8 csBaud; +} spiDevTable[] = +{ + {SPI_BUS1, 0u<<6 | SPI_BAUD_512KHz}, + {SPI_BUS3, 1u<<6 | 0}, // TODO: Bus, baudrate + {SPI_BUS1, 2u<<6 | 0}, // TODO: Bus, baudrate + {SPI_BUS1, 0u<<6 | SPI_BAUD_4MHz}, + {SPI_BUS1, 1u<<6 | 0}, // TODO: Bus, baudrate + {SPI_BUS1, 2u<<6 | 0} // TODO: Bus, baudrate +}; + + + +static SpiRegs* spiGetBusRegsBase(u8 busId) +{ + SpiRegs *base; + switch(busId) + { + case SPI_BUS1: + base = (SpiRegs*)SPI1_REGS_BASE; + break; + case SPI_BUS2: + base = (SpiRegs*)SPI2_REGS_BASE; + break; + case SPI_BUS3: + base = (SpiRegs*)SPI3_REGS_BASE; + break; + default: + base = NULL; + } + + return base; +} + +static void spiWaitBusy(const SpiRegs *const regs) +{ + while(regs->SPI_NEW_CNT & SPI_CNT_ENABLE); +} + +static void spiWaitFifoBusy(const SpiRegs *const regs) +{ + while(regs->SPI_NEW_STATUS & SPI_STATUS_BUSY); +} + +void SPI_init(void) +{ + // Switch all 3 buses to the new interface + REG_CFG11_SPI_CNT = 1u<<2 | 1u<<1 | 1u; +} + +void SPI_writeRead(SpiDevice dev, const u32 *in, u32 *out, u32 inSize, u32 outSize, bool done) +{ + SpiRegs *const regs = spiGetBusRegsBase(spiDevTable[dev].busId); + const u32 cntParams = SPI_CNT_ENABLE | spiDevTable[dev].csBaud; + + if(in) + { + regs->SPI_NEW_BLKLEN = inSize; + regs->SPI_NEW_CNT = cntParams | SPI_CNT_DIRE_WRITE; + + u32 counter = 0; + do + { + if((counter & 31) == 0) spiWaitFifoBusy(regs); + regs->SPI_NEW_FIFO = *in++; + counter += 4; + } while(counter < inSize); + + spiWaitBusy(regs); + } + if(out) + { + regs->SPI_NEW_BLKLEN = outSize; + regs->SPI_NEW_CNT = cntParams | SPI_CNT_DIRE_READ; + + u32 counter = 0; + do + { + if((counter & 31) == 0) spiWaitFifoBusy(regs); + *out++ = regs->SPI_NEW_FIFO; + counter += 4; + } while(counter < outSize); + + spiWaitBusy(regs); + } + + if(done) regs->SPI_NEW_DONE = 0; +}