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);
}