diff --git a/exceptions/arm9/source/i2c.c b/exceptions/arm9/source/i2c.c index 7a98aea..5074cf6 100644 --- a/exceptions/arm9/source/i2c.c +++ b/exceptions/arm9/source/i2c.c @@ -1,143 +1,225 @@ /* -* This file is part of Luma3DS -* Copyright (C) 2016-2019 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 . -* -* 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. -*/ - -/* -* Thanks to whoever contributed in the development of this file -*/ + * 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 "types.h" #include "i2c.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 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 inline u8 i2cGetDeviceBusId(u8 device_id) + + +static void i2cWaitBusy(I2cRegs *const regs) { - return dev_data[device_id].bus_id; + while(regs->REG_I2C_CNT & I2C_ENABLE); } -static inline u8 i2cGetDeviceRegAddr(u8 device_id) +static I2cRegs* i2cGetBusRegsBase(u8 busId) { - return dev_data[device_id].reg_addr; -} - -//----------------------------------------------------------------------------- - -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), -}; - -static 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), -}; - -static inline vu8 *i2cGetCntReg(u8 bus_id) -{ - return reg_cnt_addrs[bus_id]; -} - -//----------------------------------------------------------------------------- - -static inline void i2cWaitBusy(u8 bus_id) -{ - while (*i2cGetCntReg(bus_id) & 0x80); -} - -static inline bool i2cGetResult(u8 bus_id) -{ - i2cWaitBusy(bus_id); - - return (*i2cGetCntReg(bus_id) >> 4) & 1; -} - -static void i2cStop(u8 bus_id, u8 arg0) -{ - *i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0; - i2cWaitBusy(bus_id); - *i2cGetCntReg(bus_id) = 0xC5; -} - -//----------------------------------------------------------------------------- - -static bool i2cSelectDevice(u8 bus_id, u8 dev_reg) -{ - i2cWaitBusy(bus_id); - *i2cGetDataReg(bus_id) = dev_reg; - *i2cGetCntReg(bus_id) = 0xC2; - - return i2cGetResult(bus_id); -} - -static bool i2cSelectRegister(u8 bus_id, u8 reg) -{ - i2cWaitBusy(bus_id); - *i2cGetDataReg(bus_id) = reg; - *i2cGetCntReg(bus_id) = 0xC0; - - return i2cGetResult(bus_id); -} - -//----------------------------------------------------------------------------- - -bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data) -{ - u8 bus_id = i2cGetDeviceBusId(dev_id); - u8 dev_addr = i2cGetDeviceRegAddr(dev_id); - - for(u32 i = 0; i < 8; i++) + I2cRegs *base; + switch(busId) { - 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)) return true; - } - *i2cGetCntReg(bus_id) = 0xC5; - i2cWaitBusy(bus_id); + 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 false; + 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; +} + +bool I2C_readRegBuf(I2cDevice 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(I2cDevice 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; +} + +u8 I2C_readReg(I2cDevice devId, u8 regAddr) +{ + u8 data; + if(!I2C_readRegBuf(devId, regAddr, &data, 1)) return 0xFF; + return data; +} + +bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data) +{ + return I2C_writeRegBuf(devId, regAddr, &data, 1); } diff --git a/exceptions/arm9/source/i2c.h b/exceptions/arm9/source/i2c.h index 469eba2..ea94422 100644 --- a/exceptions/arm9/source/i2c.h +++ b/exceptions/arm9/source/i2c.h @@ -1,48 +1,102 @@ -/* -* This file is part of Luma3DS -* Copyright (C) 2016-2019 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 . -* -* 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. -*/ - -/* -* Thanks to whoever contributed in the development of this file -*/ - #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 "types.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)) -bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data); + +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; + + + +/** + * @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(I2cDevice 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(I2cDevice devId, u8 regAddr, const u8 *in, u32 size); + +/** + * @brief Reads a byte from a I2C register. + * + * @param[in] devId The device ID. Use the enum above. + * @param[in] regAddr The register address. + * + * @return Returns the value read on success otherwise 0xFF. + */ +u8 I2C_readReg(I2cDevice devId, u8 regAddr); + +/** + * @brief Writes a byte to a I2C register. + * + * @param[in] devId The device ID. Use the enum above. + * @param[in] regAddr The register address. + * @param[in] data The data to write. + * + * @return Returns true on success and false on failure. + */ +bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data); diff --git a/exceptions/arm9/source/mainHandler.c b/exceptions/arm9/source/mainHandler.c index 69545e6..1612005 100644 --- a/exceptions/arm9/source/mainHandler.c +++ b/exceptions/arm9/source/mainHandler.c @@ -73,10 +73,10 @@ void __attribute__((noreturn)) mainHandler(u32 *registerDump, u32 type) //Copy header (actually optimized by the compiler) *(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader; - if(ARESCREENSINITIALIZED) i2cWriteRegister(I2C_DEV_MCU, 0x22, 1 << 0); //Shutdown LCD + if(ARESCREENSINITIALIZED) I2C_writeReg(I2C_DEV_MCU, 0x22, 1 << 0); //Shutdown LCD ((void (*)())0xFFFF0830)(); //Ensure that all memory transfers have completed and that the data cache has been flushed - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot + I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot while(true); } diff --git a/source/i2c.c b/source/i2c.c index d0c1aac..5074cf6 100644 --- a/source/i2c.c +++ b/source/i2c.c @@ -1,176 +1,225 @@ /* -* This file is part of Luma3DS -* Copyright (C) 2016-2019 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 . -* -* 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. -*/ + * 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 . + */ -/* -* Thanks to whoever contributed in the development of this file -*/ - -#include "utils.h" +#include +#include "types.h" #include "i2c.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 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 inline u8 i2cGetDeviceBusId(u8 device_id) + + +static void i2cWaitBusy(I2cRegs *const regs) { - return dev_data[device_id].bus_id; + while(regs->REG_I2C_CNT & I2C_ENABLE); } -static inline u8 i2cGetDeviceRegAddr(u8 device_id) +static I2cRegs* i2cGetBusRegsBase(u8 busId) { - return dev_data[device_id].reg_addr; -} - -//----------------------------------------------------------------------------- - -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), -}; - -static 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), -}; - -static inline vu8 *i2cGetCntReg(u8 bus_id) -{ - return reg_cnt_addrs[bus_id]; -} - -//----------------------------------------------------------------------------- - -static inline void i2cWaitBusy(u8 bus_id) -{ - while (*i2cGetCntReg(bus_id) & 0x80); -} - -static inline bool i2cGetResult(u8 bus_id) -{ - i2cWaitBusy(bus_id); - - return (*i2cGetCntReg(bus_id) >> 4) & 1; -} - -static void i2cStop(u8 bus_id, u8 arg0) -{ - *i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0; - i2cWaitBusy(bus_id); - *i2cGetCntReg(bus_id) = 0xC5; -} - -//----------------------------------------------------------------------------- - -static bool i2cSelectDevice(u8 bus_id, u8 dev_reg) -{ - i2cWaitBusy(bus_id); - *i2cGetDataReg(bus_id) = dev_reg; - *i2cGetCntReg(bus_id) = 0xC2; - - return i2cGetResult(bus_id); -} - -static 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), - dev_addr = i2cGetDeviceRegAddr(dev_id), - ret = 0xFF; - - for(u32 i = 0; i < 8 && ret == 0xFF; i++) + I2cRegs *base; + switch(busId) { - 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); + 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; + } - ret = *i2cGetDataReg(bus_id); + 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; } } - *i2cGetCntReg(bus_id) = 0xC5; - i2cWaitBusy(bus_id); + + break; } - wait(3ULL); - - return ret; + if(i < 8) return true; + else return false; } -bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data) +bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size) { - u8 bus_id = i2cGetDeviceBusId(dev_id), - dev_addr = i2cGetDeviceRegAddr(dev_id); + const u8 busId = i2cDevTable[devId].busId; + I2cRegs *const regs = i2cGetBusRegsBase(busId); - bool ret = false; - for(u32 i = 0; i < 8 && !ret; i++) + if(!i2cStartTransfer(devId, regAddr, true, regs)) return false; + + while(--size) { - 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)) ret = true; - } - *i2cGetCntReg(bus_id) = 0xC5; - i2cWaitBusy(bus_id); + regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK; + i2cWaitBusy(regs); + *out++ = regs->REG_I2C_DATA; } - wait(3ULL); + regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP; + i2cWaitBusy(regs); + *out = regs->REG_I2C_DATA; // Last byte - return ret; + return true; +} + +bool I2C_writeRegBuf(I2cDevice 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; +} + +u8 I2C_readReg(I2cDevice devId, u8 regAddr) +{ + u8 data; + if(!I2C_readRegBuf(devId, regAddr, &data, 1)) return 0xFF; + return data; +} + +bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data) +{ + return I2C_writeRegBuf(devId, regAddr, &data, 1); } diff --git a/source/i2c.h b/source/i2c.h index 8cd5695..ea94422 100644 --- a/source/i2c.h +++ b/source/i2c.h @@ -1,49 +1,102 @@ -/* -* This file is part of Luma3DS -* Copyright (C) 2016-2019 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 . -* -* 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. -*/ - -/* -* Thanks to whoever contributed in the development of this file -*/ - #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 "types.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 i2cReadRegister(u8 dev_id, u8 reg); -bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data); + +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; + + + +/** + * @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(I2cDevice 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(I2cDevice devId, u8 regAddr, const u8 *in, u32 size); + +/** + * @brief Reads a byte from a I2C register. + * + * @param[in] devId The device ID. Use the enum above. + * @param[in] regAddr The register address. + * + * @return Returns the value read on success otherwise 0xFF. + */ +u8 I2C_readReg(I2cDevice devId, u8 regAddr); + +/** + * @brief Writes a byte to a I2C register. + * + * @param[in] devId The device ID. Use the enum above. + * @param[in] regAddr The register address. + * @param[in] data The data to write. + * + * @return Returns true on success and false on failure. + */ +bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data); diff --git a/source/main.c b/source/main.c index e95e919..d010cd5 100644 --- a/source/main.c +++ b/source/main.c @@ -37,6 +37,7 @@ #include "crypto.h" #include "memory.h" #include "screen.h" +#include "i2c.h" #include "fatfs/sdmmc/sdmmc.h" extern CfgData configData; @@ -59,6 +60,8 @@ void main(int argc, char **argv, u32 magicWord) const vu8 *bootMediaStatus = (const vu8 *)0x1FFFE00C; const vu32 *bootPartitionsStatus = (const vu32 *)0x1FFFE010; + I2C_init(); + //Shell closed, no error booting NTRCARD, NAND paritions not even considered isNtrBoot = bootMediaStatus[3] == 2 && !bootMediaStatus[1] && !bootPartitionsStatus[0] && !bootPartitionsStatus[1]; diff --git a/source/screen.c b/source/screen.c index 3793944..2bb4275 100644 --- a/source/screen.c +++ b/source/screen.c @@ -103,7 +103,7 @@ void initScreens(void) invokeArm11Function(INIT_SCREENS); //Turn on backlight - i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); + I2C_writeReg(I2C_DEV_MCU, 0x22, 0x2A); } else updateBrightness(MULTICONFIG(BRIGHTNESS)); diff --git a/source/utils.c b/source/utils.c index 5846400..359c057 100644 --- a/source/utils.c +++ b/source/utils.c @@ -86,8 +86,8 @@ u32 waitInput(bool isMenu) if(!key) { - if((!(i2cReadRegister(I2C_DEV_MCU, 0xF) & 2) && shouldShellShutdown) || - (i2cReadRegister(I2C_DEV_MCU, 0x10) & 1) == 1) mcuPowerOff(); + if((!(I2C_readReg(I2C_DEV_MCU, 0xF) & 2) && shouldShellShutdown) || + (I2C_readReg(I2C_DEV_MCU, 0x10) & 1) == 1) mcuPowerOff(); oldKey = 0; dPadDelay = 0; continue; @@ -109,12 +109,12 @@ void mcuPowerOff(void) if(!needToSetupScreens) clearScreens(false); //Shutdown LCD - if(ARESCREENSINITIALIZED) i2cWriteRegister(I2C_DEV_MCU, 0x22, 1 << 0); + if(ARESCREENSINITIALIZED) I2C_writeReg(I2C_DEV_MCU, 0x22, 1 << 0); //Ensure that all memory transfers have completed and that the data cache has been flushed flushEntireDCache(); - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); + I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 0); while(true); }