mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
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:
parent
9ab9c01aae
commit
987b820c4a
@ -12,6 +12,4 @@ SECTIONS
|
||||
.bss : ALIGN(4) { __bss_start = .; *(.bss*); __bss_end = .; }
|
||||
|
||||
. = ALIGN(4);
|
||||
|
||||
__stack_top = 0x1FFFE000;
|
||||
}
|
||||
|
@ -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
213
arm11/source/i2c.c
Normal 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
64
arm11/source/i2c.h
Normal 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);
|
@ -5,63 +5,79 @@
|
||||
#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();
|
||||
GPU_PSCFill(VRAM_START, VRAM_END, 0);
|
||||
GPU_SetFramebuffers((u32[]){VRAM_TOP_LA, VRAM_TOP_LB,
|
||||
VRAM_TOP_RA, VRAM_TOP_RB,
|
||||
VRAM_BOT_A, VRAM_BOT_B});
|
||||
|
||||
case PXI_SCREENINIT:
|
||||
{
|
||||
GPU_Init();
|
||||
GPU_PSCFill(VRAM_START, VRAM_END, 0);
|
||||
GPU_SetFramebuffers((u32[]){VRAM_TOP_LA, VRAM_TOP_LB,
|
||||
VRAM_TOP_RA, VRAM_TOP_RB,
|
||||
VRAM_BOT_A, VRAM_BOT_B});
|
||||
GPU_SetFramebufferMode(0, PDC_RGB24);
|
||||
GPU_SetFramebufferMode(1, PDC_RGB24);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
GPU_SetFramebufferMode(0, PDC_RGB24);
|
||||
GPU_SetFramebufferMode(1, PDC_RGB24);
|
||||
case PXI_BRIGHTNESS:
|
||||
{
|
||||
PXI_RecvArray(pxi_args, 1);
|
||||
LCD_SetBrightness(0, pxi_args[0]);
|
||||
LCD_SetBrightness(1, pxi_args[0]);
|
||||
ret = pxi_args[0];
|
||||
break;
|
||||
}
|
||||
|
||||
PXI_SetRemote(PXI_BUSY);
|
||||
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;
|
||||
}
|
||||
|
||||
/* New CMD template:
|
||||
case CMD_ID:
|
||||
{
|
||||
<var declarations/assignments>
|
||||
<receive args from PXI FIFO>
|
||||
<execute the command>
|
||||
<set the return value>
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
default:
|
||||
ret = 0xFFFFFFFF;
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
|
||||
/* New CMD template:
|
||||
case CMD_ID:
|
||||
{
|
||||
<var declarations/assignments>
|
||||
<receive args from PXI FIFO>
|
||||
<if necessary, copy stuff to pxi_buf>
|
||||
PXI_SetRemote(PXI_BUSY);
|
||||
<execute the command>
|
||||
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
0
arm11/source/mcu.c
Executable file
0
arm11/source/mcu.h
Executable file
0
arm11/source/mcu.h
Executable 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))
|
||||
while(true);
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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--);
|
||||
}
|
||||
|
@ -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
249
arm9/source/system/i2c.c
Normal file → Executable 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
21
arm9/source/system/i2c.h
Normal file → Executable 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.
|
||||
*
|
||||
|
@ -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)
|
||||
|
86
common/pxi.h
86
common/pxi.h
@ -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_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;
|
||||
*PXI_SEND = w;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user