Updated I2C routines

Thank @profi200
This commit is contained in:
d0k3 2017-07-10 01:46:52 +02:00
parent dc85dccd06
commit e434c5aac8
5 changed files with 235 additions and 168 deletions

View File

@ -18,11 +18,13 @@ u32 InputWait() {
u32 sd_state = SD_STATE; u32 sd_state = SD_STATE;
if (sd_state != sd_state_old) if (sd_state != sd_state_old)
return sd_state ? SD_INSERT : SD_EJECT; return sd_state ? SD_INSERT : SD_EJECT;
u32 special_key = i2cReadRegister(I2C_DEV_MCU, 0x10); u8 special_key;
if (I2C_readRegBuf(I2C_DEV_MCU, 0x10, &special_key, 1)) {
if (special_key == 0x01) if (special_key == 0x01)
return pad_state | BUTTON_POWER; return pad_state | BUTTON_POWER;
else if (special_key == 0x04) else if (special_key == 0x04)
return pad_state | BUTTON_HOME; return pad_state | BUTTON_HOME;
}
pad_state_old = pad_state; pad_state_old = pad_state;
delay = 0; delay = 0;
continue; continue;

View File

@ -1,156 +1,195 @@
/*
* 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 "i2c.h" #include "i2c.h"
#include "timer.h"
//-----------------------------------------------------------------------------
static const struct { u8 bus_id, reg_addr; } dev_data[] = { #define I2C1_REGS_BASE (0x10161000)
{0, 0x4A}, {0, 0x7A}, {0, 0x78}, #define REG_I2C1_DATA *((vu8* )(I2C1_REGS_BASE + 0x00))
{1, 0x4A}, {1, 0x78}, {1, 0x2C}, #define REG_I2C1_CNT *((vu8* )(I2C1_REGS_BASE + 0x01))
{1, 0x2E}, {1, 0x40}, {1, 0x44}, #define REG_I2C1_CNTEX *((vu16*)(I2C1_REGS_BASE + 0x02))
{2, 0xD6}, {2, 0xD0}, {2, 0xD2}, #define REG_I2C1_SCL *((vu16*)(I2C1_REGS_BASE + 0x04))
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
};
inline u8 i2cGetDeviceBusId(u8 device_id) { #define I2C2_REGS_BASE (0x10144000)
return dev_data[device_id].bus_id; #define REG_I2C2_DATA *((vu8* )(I2C2_REGS_BASE + 0x00))
} #define REG_I2C2_CNT *((vu8* )(I2C2_REGS_BASE + 0x01))
#define REG_I2C2_CNTEX *((vu16*)(I2C2_REGS_BASE + 0x02))
#define REG_I2C2_SCL *((vu16*)(I2C2_REGS_BASE + 0x04))
inline u8 i2cGetDeviceRegAddr(u8 device_id) { #define I2C3_REGS_BASE (0x10148000)
return dev_data[device_id].reg_addr; #define REG_I2C3_DATA *((vu8* )(I2C3_REGS_BASE + 0x00))
} #define REG_I2C3_CNT *((vu8* )(I2C3_REGS_BASE + 0x01))
#define REG_I2C3_CNTEX *((vu16*)(I2C3_REGS_BASE + 0x02))
#define REG_I2C3_SCL *((vu16*)(I2C3_REGS_BASE + 0x04))
//-----------------------------------------------------------------------------
static vu8* reg_data_addrs[] = { static const struct
(vu8*)(I2C1_REG_OFF + I2C_REG_DATA),
(vu8*)(I2C2_REG_OFF + I2C_REG_DATA),
(vu8*)(I2C3_REG_OFF + I2C_REG_DATA),
};
inline vu8* i2cGetDataReg(u8 bus_id) {
return reg_data_addrs[bus_id];
}
//-----------------------------------------------------------------------------
static vu8* reg_cnt_addrs[] = {
(vu8*)(I2C1_REG_OFF + I2C_REG_CNT),
(vu8*)(I2C2_REG_OFF + I2C_REG_CNT),
(vu8*)(I2C3_REG_OFF + I2C_REG_CNT),
};
inline vu8* i2cGetCntReg(u8 bus_id) {
return reg_cnt_addrs[bus_id];
}
//-----------------------------------------------------------------------------
inline void i2cWaitBusy(u8 bus_id) {
while (*i2cGetCntReg(bus_id) & 0x80);
}
inline bool i2cGetResult(u8 bus_id) {
i2cWaitBusy(bus_id);
return (*i2cGetCntReg(bus_id) >> 4) & 1;
}
void i2cStop(u8 bus_id, u8 arg0) {
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
i2cWaitBusy(bus_id);
*i2cGetCntReg(bus_id) = 0xC5;
}
//-----------------------------------------------------------------------------
bool i2cSelectDevice(u8 bus_id, u8 dev_reg) {
i2cWaitBusy(bus_id);
*i2cGetDataReg(bus_id) = dev_reg;
*i2cGetCntReg(bus_id) = 0xC2;
return i2cGetResult(bus_id);
}
bool i2cSelectRegister(u8 bus_id, u8 reg) {
i2cWaitBusy(bus_id);
*i2cGetDataReg(bus_id) = reg;
*i2cGetCntReg(bus_id) = 0xC0;
return i2cGetResult(bus_id);
}
//-----------------------------------------------------------------------------
u8 i2cReadRegister(u8 dev_id, u8 reg) {
u8 bus_id = i2cGetDeviceBusId(dev_id);
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
for (size_t i = 0; i < 8; i++) {
if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) {
if (i2cSelectDevice(bus_id, dev_addr | 1)) {
i2cWaitBusy(bus_id);
i2cStop(bus_id, 1);
i2cWaitBusy(bus_id);
return *i2cGetDataReg(bus_id);
}
}
*i2cGetCntReg(bus_id) = 0xC5;
i2cWaitBusy(bus_id);
}
return 0xff;
}
bool i2cReadRegisterBuffer(unsigned int dev_id, int reg, u8* buffer, size_t buf_size) {
u8 bus_id = i2cGetDeviceBusId(dev_id);
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
size_t j = 0;
while (!i2cSelectDevice(bus_id, dev_addr)
|| !i2cSelectRegister(bus_id, reg)
|| !i2cSelectDevice(bus_id, dev_addr | 1))
{ {
i2cWaitBusy(bus_id); u8 busId;
*i2cGetCntReg(bus_id) = 0xC5; u8 devAddr;
i2cWaitBusy(bus_id); } i2cDevTable[] =
if (++j >= 8) {
return false; {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(vu8 *cntReg)
{
while(*cntReg & I2C_ENABLE);
} }
if (buf_size != 1) { static vu8* i2cGetBusRegsBase(u8 busId)
for (size_t i = 0; i < buf_size - 1; i++) { {
i2cWaitBusy(bus_id); vu8 *base;
*i2cGetCntReg(bus_id) = 0xF0; if(!busId) base = (vu8*)I2C1_REGS_BASE;
i2cWaitBusy(bus_id); else if(busId == 1) base = (vu8*)I2C2_REGS_BASE;
buffer[i] = *i2cGetDataReg(bus_id); else base = (vu8*)I2C3_REGS_BASE;
return base;
}
static bool i2cStartTransfer(I2cDevice devId, u8 regAddr, bool read, vu8 *regsBase)
{
const u8 devAddr = i2cDevTable[devId].devAddr;
vu8 *const i2cData = regsBase;
vu8 *const i2cCnt = regsBase + 1;
u32 i = 0;
for(; i < 8; i++)
{
i2cWaitBusy(i2cCnt);
// Select device and start.
*i2cData = devAddr;
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START;
i2cWaitBusy(i2cCnt);
if(!I2C_GET_ACK(*i2cCnt)) // If ack flag is 0 it failed.
{
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
// Select register and change direction to write.
*i2cData = regAddr;
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE;
i2cWaitBusy(i2cCnt);
if(!I2C_GET_ACK(*i2cCnt)) // If ack flag is 0 it failed.
{
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
}
// Select device in read mode for read transfer.
if(read)
{
*i2cData = devAddr | 1u; // Set bit 0 for read.
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START;
i2cWaitBusy(i2cCnt);
if(!I2C_GET_ACK(*i2cCnt)) // If ack flag is 0 it failed.
{
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
continue;
} }
} }
i2cWaitBusy(bus_id); break;
*i2cGetCntReg(bus_id) = 0xE1; }
i2cWaitBusy(bus_id);
buffer[buf_size - 1] = *i2cGetDataReg(bus_id); if(i < 8) return true;
else return false;
}
void I2C_init(void)
{
i2cWaitBusy(i2cGetBusRegsBase(0));
REG_I2C1_CNTEX = 2; // ?
REG_I2C1_SCL = 1280; // ?
i2cWaitBusy(i2cGetBusRegsBase(1));
REG_I2C2_CNTEX = 2; // ?
REG_I2C2_SCL = 1280; // ?
i2cWaitBusy(i2cGetBusRegsBase(2));
REG_I2C3_CNTEX = 2; // ?
REG_I2C3_SCL = 1280; // ?
}
bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
{
const u8 busId = i2cDevTable[devId].busId;
vu8 *const i2cData = i2cGetBusRegsBase(busId);
vu8 *const i2cCnt = i2cData + 1;
if(!i2cStartTransfer(devId, regAddr, true, i2cData)) return false;
while(--size)
{
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK;
i2cWaitBusy(i2cCnt);
*out++ = *i2cData;
}
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP;
i2cWaitBusy(i2cCnt);
*out = *i2cData; // Last byte
return true; return true;
} }
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data) { bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data)
u8 bus_id = i2cGetDeviceBusId(dev_id); {
u8 dev_addr = i2cGetDeviceRegAddr(dev_id); const u8 busId = i2cDevTable[devId].busId;
vu8 *const i2cData = i2cGetBusRegsBase(busId);
vu8 *const i2cCnt = i2cData + 1;
wait_msec(3ULL);
for (int i = 0; i < 8; i++) {
if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) {
i2cWaitBusy(bus_id);
*i2cGetDataReg(bus_id) = data;
*i2cGetCntReg(bus_id) = 0xC1;
i2cStop(bus_id, 0);
if (i2cGetResult(bus_id)) {
wait_msec(3ULL);
return true;
}
}
*i2cGetCntReg(bus_id) = 0xC5;
i2cWaitBusy(bus_id);
}
wait_msec(3ULL); if(!i2cStartTransfer(devId, regAddr, false, i2cData)) return false;
*i2cData = data;
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE | I2C_STOP;
i2cWaitBusy(i2cCnt);
if(!I2C_GET_ACK(*i2cCnt)) // If ack flag is 0 it failed.
{
*i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
return false; return false;
} }
return true;
}

View File

@ -1,35 +1,56 @@
#pragma once #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 "common.h" #include "common.h"
#define I2C1_REG_OFF 0x10161000
#define I2C2_REG_OFF 0x10144000
#define I2C3_REG_OFF 0x10148000
#define I2C_REG_DATA 0 #define I2C_STOP (1u)
#define I2C_REG_CNT 1 #define I2C_START (1u<<1)
#define I2C_REG_CNTEX 2 #define I2C_ERROR (1u<<2)
#define I2C_REG_SCL 4 #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_DEV_MCU 3 #define I2C_GET_ACK(reg) ((bool)((reg)>>4 & 1u))
#define I2C_DEV_GYRO 10
#define I2C_DEV_IR 13
u8 i2cGetDeviceBusId(u8 device_id);
u8 i2cGetDeviceRegAddr(u8 device_id);
vu8* i2cGetDataReg(u8 bus_id); typedef enum
vu8* i2cGetCntReg(u8 bus_id); {
I2C_DEV_POWER = 0, // Unconfirmed
I2C_DEV_CAMERA = 1, // Unconfirmed
I2C_DEV_CAMERA2 = 2, // Unconfirmed
I2C_DEV_MCU = 3,
I2C_DEV_GYRO = 10,
I2C_DEV_DEBUG_PAD = 12,
I2C_DEV_IR = 13,
I2C_DEV_EEPROM = 14, // Unconfirmed
I2C_DEV_NFC = 15,
I2C_DEV_QTM = 16,
I2C_DEV_N3DS_HID = 17
} I2cDevice;
void i2cWaitBusy(u8 bus_id);
bool i2cGetResult(u8 bus_id);
u8 i2cGetData(u8 bus_id);
void i2cStop(u8 bus_id, u8 arg0);
bool i2cSelectDevice(u8 bus_id, u8 dev_reg);
bool i2cSelectRegister(u8 bus_id, u8 reg);
u8 i2cReadRegister(u8 dev_id, u8 reg); void I2C_init(void);
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data); bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size);
bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data);
bool i2cReadRegisterBuffer(unsigned int dev_id, int reg, u8* buffer, size_t buf_size);

View File

@ -6,7 +6,7 @@
void Reboot() { void Reboot() {
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
flushEntireDCache(); flushEntireDCache();
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); if (I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 2))
while(true); while(true);
} }
@ -14,6 +14,6 @@ void PowerOff()
{ {
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
flushEntireDCache(); flushEntireDCache();
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); if (I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 0))
while (true); while (true);
} }

View File

@ -23,6 +23,8 @@
#include "chainload.h" #include "chainload.h"
#include "qlzcomp.h" #include "qlzcomp.h"
#include "timer.h" #include "timer.h"
#include "power.h"
#include "i2c.h"
#include QLZ_SPLASH_H #include QLZ_SPLASH_H
#define N_PANES 2 #define N_PANES 2
@ -1599,6 +1601,9 @@ u32 GodMode() {
clipboard->n_entries = 0; clipboard->n_entries = 0;
memset(panedata, 0x00, 0x10000); memset(panedata, 0x00, 0x10000);
// I2C init
// I2C_init();
// check for embedded essential backup // check for embedded essential backup
if (IS_SIGHAX && !PathExist("S:/essential.exefs") && CheckGenuineNandNcsd() && if (IS_SIGHAX && !PathExist("S:/essential.exefs") && CheckGenuineNandNcsd() &&
ShowPrompt(true, "Essential files backup not found.\nCreate one now?")) { ShowPrompt(true, "Essential files backup not found.\nCreate one now?")) {