From e434c5aac8a6b0564023ede26e7396e3912d87b8 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Mon, 10 Jul 2017 01:46:52 +0200 Subject: [PATCH] Updated I2C routines Thank @profi200 --- source/common/hid.c | 12 +- source/common/i2c.c | 309 ++++++++++++++++++++++++------------------ source/common/i2c.h | 69 ++++++---- source/common/power.c | 8 +- source/godmode.c | 5 + 5 files changed, 235 insertions(+), 168 deletions(-) diff --git a/source/common/hid.c b/source/common/hid.c index c6a0e72..a821c81 100644 --- a/source/common/hid.c +++ b/source/common/hid.c @@ -18,11 +18,13 @@ u32 InputWait() { u32 sd_state = SD_STATE; if (sd_state != sd_state_old) return sd_state ? SD_INSERT : SD_EJECT; - u32 special_key = i2cReadRegister(I2C_DEV_MCU, 0x10); - if (special_key == 0x01) - return pad_state | BUTTON_POWER; - else if (special_key == 0x04) - return pad_state | BUTTON_HOME; + u8 special_key; + if (I2C_readRegBuf(I2C_DEV_MCU, 0x10, &special_key, 1)) { + if (special_key == 0x01) + return pad_state | BUTTON_POWER; + else if (special_key == 0x04) + return pad_state | BUTTON_HOME; + } pad_state_old = pad_state; delay = 0; continue; diff --git a/source/common/i2c.c b/source/common/i2c.c index 6edc1da..0c027d8 100644 --- a/source/common/i2c.c +++ b/source/common/i2c.c @@ -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 . + */ + +#include #include "i2c.h" -#include "timer.h" -//----------------------------------------------------------------------------- -static const struct { u8 bus_id, reg_addr; } dev_data[] = { - {0, 0x4A}, {0, 0x7A}, {0, 0x78}, - {1, 0x4A}, {1, 0x78}, {1, 0x2C}, - {1, 0x2E}, {1, 0x40}, {1, 0x44}, - {2, 0xD6}, {2, 0xD0}, {2, 0xD2}, - {2, 0xA4}, {2, 0x9A}, {2, 0xA0}, +#define I2C1_REGS_BASE (0x10161000) +#define REG_I2C1_DATA *((vu8* )(I2C1_REGS_BASE + 0x00)) +#define REG_I2C1_CNT *((vu8* )(I2C1_REGS_BASE + 0x01)) +#define REG_I2C1_CNTEX *((vu16*)(I2C1_REGS_BASE + 0x02)) +#define REG_I2C1_SCL *((vu16*)(I2C1_REGS_BASE + 0x04)) + +#define I2C2_REGS_BASE (0x10144000) +#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)) + +#define I2C3_REGS_BASE (0x10148000) +#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 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} }; -inline u8 i2cGetDeviceBusId(u8 device_id) { - return dev_data[device_id].bus_id; + + +static void i2cWaitBusy(vu8 *cntReg) +{ + while(*cntReg & I2C_ENABLE); } -inline u8 i2cGetDeviceRegAddr(u8 device_id) { - return dev_data[device_id].reg_addr; +static vu8* i2cGetBusRegsBase(u8 busId) +{ + vu8 *base; + if(!busId) base = (vu8*)I2C1_REGS_BASE; + else if(busId == 1) base = (vu8*)I2C2_REGS_BASE; + 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; -static vu8* reg_data_addrs[] = { - (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]; + 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; + } + } + + break; + } + + if(i < 8) return true; + else return false; } -//----------------------------------------------------------------------------- +void I2C_init(void) +{ + i2cWaitBusy(i2cGetBusRegsBase(0)); + REG_I2C1_CNTEX = 2; // ? + REG_I2C1_SCL = 1280; // ? -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), -}; + i2cWaitBusy(i2cGetBusRegsBase(1)); + REG_I2C2_CNTEX = 2; // ? + REG_I2C2_SCL = 1280; // ? -inline vu8* i2cGetCntReg(u8 bus_id) { - return reg_cnt_addrs[bus_id]; + 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; -inline void i2cWaitBusy(u8 bus_id) { - while (*i2cGetCntReg(bus_id) & 0x80); + + 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; } -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); - *i2cGetCntReg(bus_id) = 0xC5; - i2cWaitBusy(bus_id); - if (++j >= 8) - return false; - } - - if (buf_size != 1) { - for (size_t i = 0; i < buf_size - 1; i++) { - i2cWaitBusy(bus_id); - *i2cGetCntReg(bus_id) = 0xF0; - i2cWaitBusy(bus_id); - buffer[i] = *i2cGetDataReg(bus_id); - } - } - - i2cWaitBusy(bus_id); - *i2cGetCntReg(bus_id) = 0xE1; - i2cWaitBusy(bus_id); - buffer[buf_size - 1] = *i2cGetDataReg(bus_id); - - return true; -} - -bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data) { - u8 bus_id = i2cGetDeviceBusId(dev_id); - u8 dev_addr = i2cGetDeviceRegAddr(dev_id); - - 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); - return false; +bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data) +{ + const u8 busId = i2cDevTable[devId].busId; + vu8 *const i2cData = i2cGetBusRegsBase(busId); + vu8 *const i2cCnt = i2cData + 1; + + + 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 true; } diff --git a/source/common/i2c.h b/source/common/i2c.h index f047c17..8229e3f 100644 --- a/source/common/i2c.h +++ b/source/common/i2c.h @@ -1,35 +1,56 @@ #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 . + */ + +#include #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_REG_CNT 1 -#define I2C_REG_CNTEX 2 -#define I2C_REG_SCL 4 +#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_DEV_MCU 3 -#define I2C_DEV_GYRO 10 -#define I2C_DEV_IR 13 +#define I2C_GET_ACK(reg) ((bool)((reg)>>4 & 1u)) -u8 i2cGetDeviceBusId(u8 device_id); -u8 i2cGetDeviceRegAddr(u8 device_id); -vu8* i2cGetDataReg(u8 bus_id); -vu8* i2cGetCntReg(u8 bus_id); +typedef enum +{ + 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); -bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data); - -bool i2cReadRegisterBuffer(unsigned int dev_id, int reg, u8* buffer, size_t buf_size); +void I2C_init(void); +bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size); +bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data); diff --git a/source/common/power.c b/source/common/power.c index 18b90bb..4b91905 100644 --- a/source/common/power.c +++ b/source/common/power.c @@ -6,14 +6,14 @@ void Reboot() { ClearScreenF(true, true, COLOR_STD_BG); flushEntireDCache(); - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); - while(true); + if (I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 2)) + while(true); } void PowerOff() { ClearScreenF(true, true, COLOR_STD_BG); flushEntireDCache(); - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); - while (true); + if (I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 0)) + while (true); } diff --git a/source/godmode.c b/source/godmode.c index cb798a4..ad7e229 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -23,6 +23,8 @@ #include "chainload.h" #include "qlzcomp.h" #include "timer.h" +#include "power.h" +#include "i2c.h" #include QLZ_SPLASH_H #define N_PANES 2 @@ -1599,6 +1601,9 @@ u32 GodMode() { clipboard->n_entries = 0; memset(panedata, 0x00, 0x10000); + // I2C init + // I2C_init(); + // check for embedded essential backup if (IS_SIGHAX && !PathExist("S:/essential.exefs") && CheckGenuineNandNcsd() && ShowPrompt(true, "Essential files backup not found.\nCreate one now?")) {