diff --git a/arm9/source/system/i2c.c b/arm9/source/system/i2c.c index 43969d9..8495914 100644 --- a/arm9/source/system/i2c.c +++ b/arm9/source/system/i2c.c @@ -17,112 +17,136 @@ */ #include +#include "types.h" #include "i2c.h" #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)) +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} + {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) +static void i2cWaitBusy(I2cRegs *const regs) { - while(*cntReg & I2C_ENABLE); + while(regs->REG_I2C_CNT & I2C_ENABLE); } -static vu8* i2cGetBusRegsBase(u8 busId) +static I2cRegs* 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; + 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; } -static bool i2cStartTransfer(I2cDevice devId, u8 regAddr, bool read, vu8 *regsBase) +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; - vu8 *const i2cData = regsBase; - vu8 *const i2cCnt = regsBase + 1; u32 i = 0; for(; i < 8; i++) { - i2cWaitBusy(i2cCnt); + i2cWaitBusy(regs); // 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. + 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. { - *i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; + regs->REG_I2C_CNT = 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. + 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. { - *i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; + regs->REG_I2C_CNT = 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. + 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. { - *i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; + regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; continue; } } @@ -134,62 +158,68 @@ static bool i2cStartTransfer(I2cDevice devId, u8 regAddr, bool read, vu8 *regsBa else return false; } -void I2C_init(void) -{ - i2cWaitBusy(i2cGetBusRegsBase(0) + 1); - REG_I2C1_CNTEX = 2; // ? - REG_I2C1_SCL = 1280; // ? - - i2cWaitBusy(i2cGetBusRegsBase(1) + 1); - REG_I2C2_CNTEX = 2; // ? - REG_I2C2_SCL = 1280; // ? - - i2cWaitBusy(i2cGetBusRegsBase(2) + 1); - 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; + I2cRegs *const regs = i2cGetBusRegsBase(busId); - if(!i2cStartTransfer(devId, regAddr, true, i2cData)) return false; + if(!i2cStartTransfer(devId, regAddr, true, regs)) return false; while(--size) { - *i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK; - i2cWaitBusy(i2cCnt); - *out++ = *i2cData; + regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK; + i2cWaitBusy(regs); + *out++ = regs->REG_I2C_DATA; } - *i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP; - i2cWaitBusy(i2cCnt); - *out = *i2cData; // Last byte + 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_writeReg(I2cDevice devId, u8 regAddr, u8 data) +bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size) { const u8 busId = i2cDevTable[devId].busId; - vu8 *const i2cData = i2cGetBusRegsBase(busId); - vu8 *const i2cCnt = i2cData + 1; + I2cRegs *const regs = i2cGetBusRegsBase(busId); - if(!i2cStartTransfer(devId, regAddr, false, i2cData)) return false; + if(!i2cStartTransfer(devId, regAddr, false, regs)) 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. + while(--size) { - *i2cCnt = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; + 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/arm9/source/system/i2c.h b/arm9/source/system/i2c.h index 8229e3f..ee08dd7 100644 --- a/arm9/source/system/i2c.h +++ b/arm9/source/system/i2c.h @@ -19,7 +19,7 @@ */ #include -#include "common.h" +#include "types.h" #define I2C_STOP (1u) @@ -51,6 +51,52 @@ typedef enum +/** + * @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);