beginning of better ARM11:

- moved I2C code to the ARM11 (with an ugly hack that MUST be fixed)
- reworked the PXI protocol to have lower latencies and remove any potential async support
This commit is contained in:
Wolfvak 2019-04-08 17:38:38 -03:00 committed by d0k3
parent 9ab9c01aae
commit 987b820c4a
15 changed files with 541 additions and 475 deletions

View File

@ -12,6 +12,4 @@ SECTIONS
.bss : ALIGN(4) { __bss_start = .; *(.bss*); __bss_end = .; }
. = ALIGN(4);
__stack_top = 0x1FFFE000;
}

View File

@ -5,14 +5,14 @@
.global __boot
__boot:
cpsid aif, #(SR_SVC_MODE)
cpsid aif, #SR_SVC_MODE
mov r0, #0
mcr p15, 0, r0, c7, c7, 0
mcr p15, 0, r0, c7, c14, 0
mcr p15, 0, r0, c7, c10, 4
ldr sp, =__stack_top
ldr sp, =_stack_top
@ Reset values
ldr r0, =0x00054078
@ -33,3 +33,8 @@ __boot:
bl main
b __boot
.section .bss.stack
.align 3
.space (8192 * 4)
_stack_top:

213
arm11/source/i2c.c Normal file
View File

@ -0,0 +1,213 @@
/*
* This file is part of fastboot 3DS
* Copyright (C) 2017 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 <stdbool.h>
#include "types.h"
#include "i2c.h"
#define I2C1_REGS_BASE (0x10161000)
#define I2C2_REGS_BASE (0x10144000)
#define I2C3_REGS_BASE (0x10148000)
typedef struct
{
vu8 REG_I2C_DATA;
vu8 REG_I2C_CNT;
vu16 REG_I2C_CNTEX;
vu16 REG_I2C_SCL;
} I2cRegs;
static const struct
{
u8 busId;
u8 devAddr;
} i2cDevTable[] =
{
{0, 0x4A},
{0, 0x7A},
{0, 0x78},
{1, 0x4A},
{1, 0x78},
{1, 0x2C},
{1, 0x2E},
{1, 0x40},
{1, 0x44},
{2, 0xA6}, // TODO: Find out if 0xA6 or 0xD6 is correct
{2, 0xD0},
{2, 0xD2},
{2, 0xA4},
{2, 0x9A},
{2, 0xA0},
{1, 0xEE},
{0, 0x40},
{2, 0x54}
};
static void i2cWaitBusy(I2cRegs *const regs)
{
while(regs->REG_I2C_CNT & I2C_ENABLE);
}
static I2cRegs* i2cGetBusRegsBase(u8 busId)
{
I2cRegs *base;
switch(busId)
{
case 0:
base = (I2cRegs*)I2C1_REGS_BASE;
break;
case 1:
base = (I2cRegs*)I2C2_REGS_BASE;
break;
case 2:
base = (I2cRegs*)I2C3_REGS_BASE;
break;
default:
base = NULL;
}
return base;
}
void I2C_init(void)
{
I2cRegs *regs = i2cGetBusRegsBase(0); // Bus 1
i2cWaitBusy(regs);
regs->REG_I2C_CNTEX = 2; // ?
regs->REG_I2C_SCL = 1280; // ?
regs = i2cGetBusRegsBase(1); // Bus 2
i2cWaitBusy(regs);
regs->REG_I2C_CNTEX = 2; // ?
regs->REG_I2C_SCL = 1280; // ?
regs = i2cGetBusRegsBase(2); // Bus 3
i2cWaitBusy(regs);
regs->REG_I2C_CNTEX = 2; // ?
regs->REG_I2C_SCL = 1280; // ?
}
static bool i2cStartTransfer(int devId, u8 regAddr, bool read, I2cRegs *const regs)
{
const u8 devAddr = i2cDevTable[devId].devAddr;
u32 i = 0;
for(; i < 8; i++)
{
i2cWaitBusy(regs);
// Select device and start.
regs->REG_I2C_DATA = devAddr;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
// Select register and change direction to write.
regs->REG_I2C_DATA = regAddr;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
// Select device in read mode for read transfer.
if(read)
{
regs->REG_I2C_DATA = devAddr | 1u; // Set bit 0 for read.
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
}
break;
}
if(i < 8) return true;
else return false;
}
bool I2C_readRegBuf(int devId, u8 regAddr, u8 *out, u32 size)
{
const u8 busId = i2cDevTable[devId].busId;
I2cRegs *const regs = i2cGetBusRegsBase(busId);
if(!i2cStartTransfer(devId, regAddr, true, regs)) return false;
while(--size)
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK;
i2cWaitBusy(regs);
*out++ = regs->REG_I2C_DATA;
}
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP;
i2cWaitBusy(regs);
*out = regs->REG_I2C_DATA; // Last byte
return true;
}
bool I2C_writeRegBuf(int devId, u8 regAddr, const u8 *in, u32 size)
{
const u8 busId = i2cDevTable[devId].busId;
I2cRegs *const regs = i2cGetBusRegsBase(busId);
if(!i2cStartTransfer(devId, regAddr, false, regs)) return false;
while(--size)
{
regs->REG_I2C_DATA = *in++;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
return false;
}
}
regs->REG_I2C_DATA = *in;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE | I2C_STOP;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
return false;
}
return true;
}

64
arm11/source/i2c.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
/*
* This file is part of fastboot 3DS
* Copyright (C) 2017 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 <stdbool.h>
#include "types.h"
#define I2C_STOP (1u)
#define I2C_START (1u<<1)
#define I2C_ERROR (1u<<2)
#define I2C_ACK (1u<<4)
#define I2C_DIRE_WRITE (0u)
#define I2C_DIRE_READ (1u<<5)
#define I2C_IRQ_ENABLE (1u<<6)
#define I2C_ENABLE (1u<<7)
#define I2C_GET_ACK(reg) ((bool)((reg)>>4 & 1u))
/**
* @brief Initializes the I2C buses. Call this only once.
*/
void I2C_init(void);
/**
* @brief Reads data from a I2C register to a buffer.
*
* @param[in] devId The device ID. Use the enum above.
* @param[in] regAddr The register address.
* @param out The output buffer pointer.
* @param[in] size The read size.
*
* @return Returns true on success and false on failure.
*/
bool I2C_readRegBuf(int devId, u8 regAddr, u8 *out, u32 size);
/**
* @brief Writes a buffer to a I2C register.
*
* @param[in] devId The device ID. Use the enum above.
* @param[in] regAddr The register address.
* @param[in] in The input buffer pointer.
* @param[in] size The write size.
*
* @return Returns true on success and false on failure.
*/
bool I2C_writeRegBuf(int devId, u8 regAddr, const u8 *in, u32 size);

View File

@ -5,19 +5,20 @@
#include <vram.h>
#include <types.h>
#include <i2c.h>
#define CFG11_MPCORE_CLKCNT ((vu16*)(0x10141300))
#define CFG11_SOCINFO ((vu16*)(0x10140FFC))
vu32 *entrypoint = (vu32*)0x1FFFFFFC;
void PXI_IRQHandler(void)
void PXI_RX_Handler(void)
{
// char pxi_buf[PXI_MAXBUFLEN] = {0};
u32 pxi_args[PXI_FIFO_LEN] = {0};
u8 pxi_cmd;
u32 pxi_cmd, ret;
u32 pxi_args[PXI_FIFO_LEN];
pxi_cmd = PXI_GetRemote();
pxi_cmd = PXI_Recv();
switch (pxi_cmd) {
default:
break;
case PXI_SCREENINIT:
{
GPU_Init();
@ -28,17 +29,32 @@ void PXI_IRQHandler(void)
GPU_SetFramebufferMode(0, PDC_RGB24);
GPU_SetFramebufferMode(1, PDC_RGB24);
PXI_SetRemote(PXI_BUSY);
ret = 0;
break;
}
case PXI_BRIGHTNESS:
{
PXI_RecvArray(pxi_args, 1);
PXI_SetRemote(PXI_BUSY);
LCD_SetBrightness(0, pxi_args[0]);
LCD_SetBrightness(1, pxi_args[0]);
ret = pxi_args[0];
break;
}
case PXI_I2C_READ:
{
PXI_RecvArray(pxi_args, 4);
ret = I2C_readRegBuf(pxi_args[0], pxi_args[1],
(u8*)pxi_args[2], pxi_args[3]);
break;
}
case PXI_I2C_WRITE:
{
PXI_RecvArray(pxi_args, 4);
ret = I2C_writeRegBuf(pxi_args[0], pxi_args[1],
(const u8*)pxi_args[2], pxi_args[3]);
break;
}
@ -47,21 +63,21 @@ void PXI_IRQHandler(void)
{
<var declarations/assignments>
<receive args from PXI FIFO>
<if necessary, copy stuff to pxi_buf>
PXI_SetRemote(PXI_BUSY);
<execute the command>
<set the return value>
break;
}
*/
default:
ret = 0xFFFFFFFF;
break;
}
PXI_SetRemote(PXI_READY);
PXI_Send(ret);
return;
}
vu16 *CFG11_MPCORE_CLKCNT = (vu16*)(0x10141300);
vu16 *CFG11_SOCINFO = (vu16*)(0x10140FFC);
void main(void)
{
u32 entry;
@ -77,20 +93,20 @@ void main(void)
CPU_DisableIRQ();
}
PXI_Reset();
GIC_Reset();
GIC_SetIRQ(IRQ_PXI_SYNC, PXI_IRQHandler);
PXI_EnableIRQ();
CPU_EnableIRQ();
PXI_SetRemote(PXI_READY);
PXI_Reset();
I2C_init();
//MCU_init();
GIC_SetIRQ(IRQ_PXI_RX, PXI_RX_Handler);
CPU_EnableIRQ();
*entrypoint = 0;
while((entry=*entrypoint) == 0);
CPU_DisableIRQ();
PXI_DisableIRQ();
PXI_Reset();
GIC_Reset();
((void (*)())(entry))();

0
arm11/source/mcu.c Executable file
View File

0
arm11/source/mcu.h Executable file
View File

View File

@ -12,7 +12,7 @@ void CheckBrightness() {
// Volume Slider value is always between 0x00 and 0x3F
curSlider >>= 2;
#else
curSlider = FIXED_BRIGHTNESS;
curSlider = FIXED_BRIGHTNESS % sizeof(br_settings);
#endif
if (curSlider != prev_brightness) {
PXI_DoCMD(PXI_BRIGHTNESS, (u32[]){br_settings[curSlider]}, 1);
@ -35,15 +35,17 @@ bool IsCharging() {
void Reboot() {
I2C_writeReg(I2C_DEV_MCU, 0x22, 1 << 0); // poweroff LCD to prevent MCU hangs
flushEntireDCache();
if (I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 2))
cpu_writeback_dc();
cpu_membarrier();
I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 2);
while(true);
}
void PowerOff()
{
I2C_writeReg(I2C_DEV_MCU, 0x22, 1 << 0); // poweroff LCD to prevent MCU hangs
flushEntireDCache();
if (I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 0))
while (true);
cpu_writeback_dc();
cpu_membarrier();
I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 0);
while(true);
}

View File

@ -1,18 +1,17 @@
#include "godmode.h"
#include "power.h"
#include "timer.h"
#include "pxi.h"
#include "i2c.h"
#include "vram.h"
void main(int argc, char** argv, int entrypoint)
{
(void) argc;
(void) argv;
PXI_Reset();
I2C_init();
// Wait for ARM11
PXI_WaitRemote(PXI_READY);
PXI_DoCMD(PXI_SCREENINIT, NULL, 0);
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x2A);

View File

@ -1,43 +1,102 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2017 Aurora Wright, TuxSH
*
* 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/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#pragma once
#include "common.h"
#include "types.h"
/***
The following functions flush the data cache, then waits for all memory transfers to be finished.
The data cache and/or the instruction cache MUST be flushed before doing one of the following:
- rebooting
- powering down
- setting the ARM11 entrypoint to execute a function
- jumping to a payload
***/
static inline void
cpu_membarrier(void) {
__asm__ __volatile__("mcr p15, 0, %0, c7, c10, 4\n\t"
:: "r"(0) : "memory");
}
void flushEntireDCache(void); //actually: "clean and flush"
void flushDCacheRange(void *startAddress, u32 size);
void flushEntireICache(void);
void flushICacheRange(void *startAddress, u32 size);
static inline void
cpu_invalidate_ic(void) {
__asm__ __volatile__("mcr p15, 0, %0, c7, c5, 0\n\t"
:: "r"(0) : "memory");
}
static inline void
cpu_invalidate_ic_range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F;
len >>= 5;
do {
__asm__ __volatile__("mcr p15, 0, %0, c7, c5, 1\n\t"
:: "r"(addr) : "memory");
addr += 0x20;
} while(len--);
}
static inline void
cpu_invalidate_dc(void) {
__asm__ __volatile__("mcr p15, 0, %0, c7, c6, 0\n\t"
:: "r"(0) : "memory");
}
static inline void
cpu_invalidate_dc_range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F;
len >>= 5;
do {
__asm__ __volatile__("mcr p15, 0, %0, c7, c6, 1"
:: "r"(addr) : "memory");
addr += 0x20;
} while(len--);
}
static inline void
cpu_writeback_dc(void) {
u32 seg = 0, ind;
do {
ind = 0;
do {
__asm__ __volatile__("mcr p15, 0, %0, c7, c10, 2\n\t"
:: "r"(seg | ind) : "memory");
ind += 0x20;
} while(ind < 0x400);
seg += 0x40000000;
} while(seg != 0);
}
static inline
void cpu_writeback_dc_range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F;
len >>= 5;
do {
__asm__ __volatile__("mcr p15, 0, %0, c7, c10, 1"
:: "r"(addr) : "memory");
addr += 0x20;
} while(len--);
}
static inline
void cpu_writeback_invalidate_dc(void) {
u32 seg = 0, ind;
do {
ind = 0;
do {
__asm__ __volatile__("mcr p15, 0, %0, c7, c14, 2\n\t"
:: "r"(seg | ind) : "memory");
ind += 0x20;
} while(ind < 0x400);
seg += 0x40000000;
} while(seg != 0);
}
static inline
void cpu_writeback_invalidate_dc_range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F;
len >>= 5;
do {
__asm__ __volatile__("mcr p15, 0, %0, c7, c14, 1"
:: "r"(addr) : "memory");
addr += 0x20;
} while(len--);
}

View File

@ -1,93 +0,0 @@
@ This file is part of Luma3DS
@ Copyright (C) 2016-2017 Aurora Wright, TuxSH
@
@ 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/>.
@
@ Additional Terms 7.b and 7.c of GPLv3 apply to this file:
@ * Requiring preservation of specified reasonable legal notices or
@ author attributions in that material or in the Appropriate Legal
@ Notices displayed by works containing it.
@ * Prohibiting misrepresentation of the origin of that material,
@ or requiring that modified versions of such material be marked in
@ reasonable ways as different from the original version.
.text
.arm
.align 4
.global flushEntireDCache
.type flushEntireDCache, %function
flushEntireDCache:
@ Adapted from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html,
@ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well
@ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has)
@ Implemented in bootROM at address 0xffff0830
mov r1, #0 @ segment counter
outer_loop:
mov r0, #0 @ line counter
inner_loop:
orr r2, r1, r0 @ generate segment and line address
mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line
add r0, #0x20 @ increment to next line
cmp r0, #0x400
bne inner_loop
add r1, #0x40000000
cmp r1, #0
bne outer_loop
mcr p15, 0, r1, c7, c10, 4 @ drain write buffer
bx lr
.global flushDCacheRange
.type flushDCacheRange, %function
flushDCacheRange:
@ Implemented in bootROM at address 0xffff08a0
add r1, r0, r1 @ end address
bic r0, #0x1f @ align source address to cache line size (32 bytes)
flush_dcache_range_loop:
mcr p15, 0, r0, c7, c14, 1 @ clean and flush the line corresponding to the address r0 is holding
add r0, #0x20
cmp r0, r1
blo flush_dcache_range_loop
mov r0, #0
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
bx lr
.global flushEntireICache
.type flushEntireICache, %function
flushEntireICache:
@ Implemented in bootROM at address 0xffff0ab4
mov r0, #0
mcr p15, 0, r0, c7, c5, 0
bx lr
.global flushICacheRange
.type flushICacheRange, %function
flushICacheRange:
@ Implemented in bootROM at address 0xffff0ac0
add r1, r0, r1 @ end address
bic r0, #0x1f @ align source address to cache line size (32 bytes)
flush_icache_range_loop:
mcr p15, 0, r0, c7, c5, 1 @ flush the line corresponding to the address r0 is holding
add r0, #0x20
cmp r0, r1
blo flush_icache_range_loop
bx lr

249
arm9/source/system/i2c.c Normal file → Executable file
View File

@ -1,224 +1,85 @@
/*
* This file is part of fastboot 3DS
* Copyright (C) 2017 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 "common.h"
#include <stdbool.h>
#include "types.h"
#include "cache.h"
#include "i2c.h"
#include "pxi.h"
/*
disgusting hack that deserves to die in hell
ideally all buffers would be able to be accessed
by the ARM11, but those in ARM9 RAM are inaccessible
(.data, .rodata & .bss)
#define I2C1_REGS_BASE (0x10161000)
#define I2C2_REGS_BASE (0x10144000)
#define I2C3_REGS_BASE (0x10148000)
typedef struct
{
vu8 REG_I2C_DATA;
vu8 REG_I2C_CNT;
vu16 REG_I2C_CNTEX;
vu16 REG_I2C_SCL;
} I2cRegs;
static const struct
{
u8 busId;
u8 devAddr;
} i2cDevTable[] =
{
{0, 0x4A},
{0, 0x7A},
{0, 0x78},
{1, 0x4A},
{1, 0x78},
{1, 0x2C},
{1, 0x2E},
{1, 0x40},
{1, 0x44},
{2, 0xA6}, // TODO: Find out if 0xA6 or 0xD6 is correct
{2, 0xD0},
{2, 0xD2},
{2, 0xA4},
{2, 0x9A},
{2, 0xA0},
{1, 0xEE},
{0, 0x40},
{2, 0x54}
};
static void i2cWaitBusy(I2cRegs *const regs)
{
while(regs->REG_I2C_CNT & I2C_ENABLE);
}
static I2cRegs* i2cGetBusRegsBase(u8 busId)
{
I2cRegs *base;
switch(busId)
{
case 0:
base = (I2cRegs*)I2C1_REGS_BASE;
break;
case 1:
base = (I2cRegs*)I2C2_REGS_BASE;
break;
case 2:
base = (I2cRegs*)I2C3_REGS_BASE;
break;
default:
base = NULL;
}
return base;
}
void I2C_init(void)
{
I2cRegs *regs = i2cGetBusRegsBase(0); // Bus 1
i2cWaitBusy(regs);
regs->REG_I2C_CNTEX = 2; // ?
regs->REG_I2C_SCL = 1280; // ?
regs = i2cGetBusRegsBase(1); // Bus 2
i2cWaitBusy(regs);
regs->REG_I2C_CNTEX = 2; // ?
regs->REG_I2C_SCL = 1280; // ?
regs = i2cGetBusRegsBase(2); // Bus 3
i2cWaitBusy(regs);
regs->REG_I2C_CNTEX = 2; // ?
regs->REG_I2C_SCL = 1280; // ?
}
static bool i2cStartTransfer(I2cDevice devId, u8 regAddr, bool read, I2cRegs *const regs)
{
const u8 devAddr = i2cDevTable[devId].devAddr;
u32 i = 0;
for(; i < 8; i++)
{
i2cWaitBusy(regs);
// Select device and start.
regs->REG_I2C_DATA = devAddr;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
// Select register and change direction to write.
regs->REG_I2C_DATA = regAddr;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
// Select device in read mode for read transfer.
if(read)
{
regs->REG_I2C_DATA = devAddr | 1u; // Set bit 0 for read.
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
}
break;
}
if(i < 8) return true;
else return false;
}
the current hack assumes all buffers in the heap are
located in FCRAM, which is accessible to both processors
but it's horrendous, and hopefully temporary
*/
static char *i2c_fcram_buf = NULL;
bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
{
const u8 busId = i2cDevTable[devId].busId;
I2cRegs *const regs = i2cGetBusRegsBase(busId);
if (!i2c_fcram_buf)
i2c_fcram_buf = malloc(256);
int ret;
u32 args[] = {devId, regAddr, (u32)i2c_fcram_buf, size};
if(!i2cStartTransfer(devId, regAddr, true, regs)) return false;
cpu_writeback_dc_range(i2c_fcram_buf, size);
cpu_membarrier();
while(--size)
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK;
i2cWaitBusy(regs);
*out++ = regs->REG_I2C_DATA;
}
ret = PXI_DoCMD(PXI_I2C_READ, args, 4);
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP;
i2cWaitBusy(regs);
*out = regs->REG_I2C_DATA; // Last byte
cpu_invalidate_dc_range(i2c_fcram_buf, size);
memcpy(out, i2c_fcram_buf, size);
return true;
return ret;
}
bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size)
{
const u8 busId = i2cDevTable[devId].busId;
I2cRegs *const regs = i2cGetBusRegsBase(busId);
if (!i2c_fcram_buf)
i2c_fcram_buf = malloc(256);
int ret;
u32 args[] = {devId, regAddr, (u32)i2c_fcram_buf, size};
if(!i2cStartTransfer(devId, regAddr, false, regs)) return false;
memcpy(i2c_fcram_buf, in, size);
cpu_writeback_dc_range(i2c_fcram_buf, size);
cpu_membarrier();
while(--size)
{
regs->REG_I2C_DATA = *in++;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
return false;
}
}
regs->REG_I2C_DATA = *in;
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE | I2C_STOP;
i2cWaitBusy(regs);
if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed.
{
regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
return false;
}
return true;
ret = PXI_DoCMD(PXI_I2C_WRITE, args, 4);
return ret;
}
/*bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
{
int ret;
u32 args[] = {devId, regAddr, (u32)out, size};
cpu_writeback_invalidate_dc_range(out, size);
cpu_membarrier();
ret = PXI_DoCMD(PXI_I2C_READ, args, 4);
cpu_invalidate_dc_range(out, size);
return ret;
}*/
u8 I2C_readReg(I2cDevice devId, u8 regAddr)
{
u8 data;
if(!I2C_readRegBuf(devId, regAddr, &data, 1)) return 0xFF;
if (!I2C_readRegBuf(devId, regAddr, &data, 1))
data = 0xFF;
return data;
}
/*bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size)
{
int ret;
u32 args[] = {devId, regAddr, (u32)in, size};
cpu_writeback_dc_range(in, size);
cpu_membarrier();
ret = PXI_DoCMD(PXI_I2C_WRITE, args, 4);
return ret;
}*/
bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data)
{
return I2C_writeRegBuf(devId, regAddr, &data, 1);

21
arm9/source/system/i2c.h Normal file → Executable file
View File

@ -18,22 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include "types.h"
#define I2C_STOP (1u)
#define I2C_START (1u<<1)
#define I2C_ERROR (1u<<2)
#define I2C_ACK (1u<<4)
#define I2C_DIRE_WRITE (0u)
#define I2C_DIRE_READ (1u<<5)
#define I2C_IRQ_ENABLE (1u<<6)
#define I2C_ENABLE (1u<<7)
#define I2C_GET_ACK(reg) ((bool)((reg)>>4 & 1u))
typedef enum
{
I2C_DEV_POWER = 0, // Unconfirmed
@ -49,13 +35,6 @@ typedef enum
I2C_DEV_N3DS_HID = 17
} I2cDevice;
/**
* @brief Initializes the I2C buses. Call this only once.
*/
void I2C_init(void);
/**
* @brief Reads data from a I2C register to a buffer.
*

View File

@ -13,6 +13,7 @@
#define SR_THUMB (1<<5)
#define SR_FIQ (1<<6)
#define SR_IRQ (1<<7)
#define SR_NOINT (SR_FIQ | SR_IRQ)
#ifdef ARM9
#define CR_ENABLE_MPU (1<<0)

View File

@ -8,27 +8,27 @@
#ifdef ARM9
#define PXI_BASE (0x10008000)
#define IRQ_PXI_SYNC (12)
#define PXI_INIT_SYNC_SET (33)
#define PXI_INIT_SYNC_WAIT (66)
#else
#define PXI_BASE (0x10163000)
#define IRQ_PXI_SYNC (80)
#define IRQ_PXI_RX (83)
#define PXI_INIT_SYNC_SET (66)
#define PXI_INIT_SYNC_WAIT (33)
#endif
enum {
PXI_NONE = 0,
PXI_READY,
PXI_BUSY,
PXI_SCREENINIT,
PXI_BRIGHTNESS
PXI_SCREENINIT = 0,
PXI_BRIGHTNESS,
PXI_I2C_READ,
PXI_I2C_WRITE,
};
#define PXI_MAXBUFLEN (2048)
#define PXI_FIFO_LEN (16)
#define PXI_SYNC_RECV ((vu8*)(PXI_BASE + 0x00))
#define PXI_SYNC_SEND ((vu8*)(PXI_BASE + 0x01))
#define PXI_SYNC_IRQ ((vu8*)(PXI_BASE + 0x03))
#define PXI_SYNC ((vu32*)(PXI_BASE + 0x00))
#define PXI_CNT ((vu16*)(PXI_BASE + 0x04))
#define PXI_SEND ((vu32*)(PXI_BASE + 0x08))
#define PXI_RECV ((vu32*)(PXI_BASE + 0x0C))
@ -39,7 +39,7 @@ enum {
#define PXI_CNT_SEND_FIFO_FLUSH (BIT(3))
#define PXI_CNT_RECV_FIFO_EMPTY (BIT(8))
#define PXI_CNT_RECV_FIFO_FULL (BIT(9))
#define PXI_CNT_RECV_FIFO_NEMPTY_IRQ (BIT(10))
#define PXI_CNT_RECV_FIFO_AVAIL_IRQ (BIT(10))
#define PXI_CNT_ERROR_ACK (BIT(14))
#define PXI_CNT_ENABLE_FIFO (BIT(15))
@ -59,83 +59,45 @@ static inline u8 PXI_GetRemote(void)
static inline void PXI_WaitRemote(u8 msg)
{
while(*PXI_SYNC_RECV != msg);
}
static inline void PXI_EnableIRQ(void)
{
*PXI_SYNC_IRQ = PXI_SYNC_ENABLE_IRQ;
}
static inline void PXI_DisableIRQ(void)
{
*PXI_SYNC_IRQ = 0;
}
static inline void PXI_Sync(void)
{
#ifdef ARM9
*PXI_SYNC_IRQ |= PXI_SYNC_TRIGGER_MPCORE;
#else
*PXI_SYNC_IRQ |= PXI_SYNC_TRIGGER_OLDARM;
#endif
while(PXI_GetRemote() != msg);
}
static void PXI_Reset(void)
{
*PXI_SYNC = 0;
*PXI_CNT = PXI_CNT_SEND_FIFO_FLUSH;
for (int i=0; i<16; i++) {
*PXI_SYNC_IRQ = 0;
*PXI_CNT = PXI_CNT_SEND_FIFO_FLUSH | PXI_CNT_ENABLE_FIFO;
for (int i = 0; i < PXI_FIFO_LEN; i++)
*PXI_RECV;
}
*PXI_CNT = 0;
*PXI_CNT = PXI_CNT_ENABLE_FIFO;
return;
*PXI_CNT = PXI_CNT_RECV_FIFO_AVAIL_IRQ | PXI_CNT_ENABLE_FIFO;
}
static void PXI_Send(u32 w)
{
while(*PXI_CNT & PXI_CNT_SEND_FIFO_FULL);
do {
*PXI_SEND = w;
} while(*PXI_CNT & PXI_CNT_ERROR_ACK);
return;
}
static u32 PXI_Recv(void)
{
u32 ret;
while(*PXI_CNT & PXI_CNT_RECV_FIFO_EMPTY);
do {
ret = *PXI_RECV;
} while(*PXI_CNT & PXI_CNT_ERROR_ACK);
return ret;
return *PXI_RECV;
}
static void PXI_SendArray(const u32 *w, u32 c)
{
if (c>PXI_FIFO_LEN) c=PXI_FIFO_LEN;
for (u32 i=0; i<c; i++) {
PXI_Send(w[i]);
}
return;
while(c--) PXI_Send(*(w++));
}
static void PXI_RecvArray(u32 *w, u32 c)
{
if (c>PXI_FIFO_LEN) c=PXI_FIFO_LEN;
for (u32 i=0; i<c; i++) {
w[i] = PXI_Recv();
}
return;
while(c--) *(w++) = PXI_Recv();
}
static void PXI_DoCMD(u8 cmd, u32 *args, u32 argc)
static u32 PXI_DoCMD(u32 cmd, const u32 *args, u32 argc)
{
PXI_WaitRemote(PXI_READY);
PXI_Send(cmd);
PXI_SendArray(args, argc);
PXI_SetRemote(cmd);
PXI_Sync();
PXI_WaitRemote(PXI_BUSY);
return;
return PXI_Recv();
}