/* * Copyright (c) 2018 naehrwert * Copyright (c) 2018-2020 CTCaer * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 #include #define I2C_PACKET_PROT_I2C BIT(4) #define I2C_HEADER_CONT_XFER BIT(15) #define I2C_HEADER_REP_START BIT(16) #define I2C_HEADER_IE_ENABLE BIT(17) #define I2C_HEADER_READ BIT(19) #define I2C_CNFG (0x00 / 4) #define CMD1_WRITE (0 << 6) #define CMD1_READ BIT(6) #define NORMAL_MODE_GO BIT(9) #define PACKET_MODE_GO BIT(10) #define NEW_MASTER_FSM BIT(11) #define DEBOUNCE_CNT_4T (2 << 12) #define I2C_CMD_ADDR0 (0x04 / 4) #define ADDR0_WRITE 0 #define ADDR0_READ 1 #define I2C_CMD_DATA1 (0x0C / 4) #define I2C_CMD_DATA2 (0x10 / 4) #define I2C_STATUS (0x1C / 4) #define I2C_STATUS_NOACK (0xF << 0) #define I2C_STATUS_BUSY BIT(8) #define I2C_TX_FIFO (0x50 / 4) #define I2C_RX_FIFO (0x54 / 4) #define I2C_FIFO_CONTROL (0x5C / 4) #define RX_FIFO_FLUSH BIT(0) #define TX_FIFO_FLUSH BIT(1) #define I2C_FIFO_STATUS (0x60 / 4) #define RX_FIFO_FULL_CNT (0xF << 0) #define TX_FIFO_EMPTY_CNT (0xF << 4) #define I2C_INT_EN (0x64 / 4) #define I2C_INT_STATUS (0x68 / 4) #define I2C_INT_SOURCE (0x70 / 4) #define RX_FIFO_DATA_REQ BIT(0) #define TX_FIFO_DATA_REQ BIT(1) #define ARB_LOST BIT(2) #define NO_ACK BIT(3) #define RX_FIFO_UNDER BIT(4) #define TX_FIFO_OVER BIT(5) #define ALL_PACKETS_COMPLETE BIT(6) #define PACKET_COMPLETE BIT(7) #define BUS_CLEAR_DONE BIT(11) #define I2C_CLK_DIVISOR (0x6C / 4) #define I2C_BUS_CLEAR_CONFIG (0x84 / 4) #define BC_ENABLE BIT(0) #define BC_TERMINATE BIT(1) #define I2C_BUS_CLEAR_STATUS (0x88 / 4) #define I2C_CONFIG_LOAD (0x8C / 4) #define MSTR_CONFIG_LOAD BIT(0) #define TIMEOUT_CONFIG_LOAD BIT(2) static const u32 i2c_addrs[] = { 0x7000C000, // I2C_1. 0x7000C400, // I2C_2. 0x7000C500, // I2C_3. 0x7000C700, // I2C_4. 0x7000D000, // I2C_5. 0x7000D100 // I2C_6. }; static void _i2c_load_cfg_wait(vu32 *base) { base[I2C_CONFIG_LOAD] = BIT(5) | TIMEOUT_CONFIG_LOAD | MSTR_CONFIG_LOAD; for (u32 i = 0; i < 20; i++) { usleep(1); if (!(base[I2C_CONFIG_LOAD] & MSTR_CONFIG_LOAD)) break; } } static int _i2c_send_single(u32 i2c_idx, u32 dev_addr, u8 *buf, u32 size) { if (size > 8) return 0; u32 tmp = 0; vu32 *base = (vu32 *)i2c_addrs[i2c_idx]; // Set device address and send mode. base[I2C_CMD_ADDR0] = dev_addr << 1 | ADDR0_WRITE; if (size > 4) { memcpy(&tmp, buf, 4); base[I2C_CMD_DATA1] = tmp; //Set value. tmp = 0; memcpy(&tmp, buf + 4, size - 4); base[I2C_CMD_DATA2] = tmp; } else { memcpy(&tmp, buf, size); base[I2C_CMD_DATA1] = tmp; //Set value. } // Set size and send mode. base[I2C_CNFG] = ((size - 1) << 1) | DEBOUNCE_CNT_4T | NEW_MASTER_FSM | CMD1_WRITE; // Load configuration. _i2c_load_cfg_wait(base); // Initiate transaction on normal mode. base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFF9FF) | NORMAL_MODE_GO; u32 timeout = get_tmr_us() + 200000; // Actual for max 8 bytes at 100KHz is 0.74ms. while (base[I2C_STATUS] & I2C_STATUS_BUSY) { if (get_tmr_us() > timeout) return 0; } if (base[I2C_STATUS] & I2C_STATUS_NOACK) return 0; return 1; } static int _i2c_recv_single(u32 i2c_idx, u8 *buf, u32 size, u32 dev_addr) { if (size > 8) return 0; vu32 *base = (vu32 *)i2c_addrs[i2c_idx]; // Set device address and recv mode. base[I2C_CMD_ADDR0] = (dev_addr << 1) | ADDR0_READ; // Set size and recv mode. base[I2C_CNFG] = ((size - 1) << 1) | DEBOUNCE_CNT_4T | NEW_MASTER_FSM | CMD1_READ; // Load configuration. _i2c_load_cfg_wait(base); // Initiate transaction on normal mode. base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFF9FF) | NORMAL_MODE_GO; u32 timeout = get_tmr_us() + 200000; // Actual for max 8 bytes at 100KHz is 0.74ms. while (base[I2C_STATUS] & I2C_STATUS_BUSY) { if (get_tmr_us() > timeout) return 0; } if (base[I2C_STATUS] & I2C_STATUS_NOACK) return 0; u32 tmp = base[I2C_CMD_DATA1]; // Get LS value. if (size > 4) { memcpy(buf, &tmp, 4); tmp = base[I2C_CMD_DATA2]; // Get MS value. memcpy(buf + 4, &tmp, size - 4); } else memcpy(buf, &tmp, size); return 1; } static int _i2c_send_pkt(u32 i2c_idx, u8 *buf, u32 size, u32 dev_addr) { if (size > 32) return 0; int res = 0; vu32 *base = (vu32 *)i2c_addrs[i2c_idx]; // Enable interrupts. base[I2C_INT_EN] = ALL_PACKETS_COMPLETE | PACKET_COMPLETE | NO_ACK | ARB_LOST | TX_FIFO_OVER | RX_FIFO_UNDER | TX_FIFO_DATA_REQ; base[I2C_INT_STATUS] = base[I2C_INT_STATUS]; // Set device address and recv mode. base[I2C_CMD_ADDR0] = (dev_addr << 1) | ADDR0_READ; // Set recv mode. base[I2C_CNFG] = DEBOUNCE_CNT_4T | NEW_MASTER_FSM | CMD1_WRITE; // Set and flush FIFO. base[I2C_FIFO_CONTROL] = RX_FIFO_FLUSH | TX_FIFO_FLUSH; // Load configuration. _i2c_load_cfg_wait(base); // Initiate transaction on packet mode. base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFF9FF) | PACKET_MODE_GO; u32 hdr[3]; hdr[0] = I2C_PACKET_PROT_I2C; hdr[1] = size - 1; hdr[2] = I2C_HEADER_IE_ENABLE | I2C_HEADER_CONT_XFER | (dev_addr << 1); // Send header with request. base[I2C_TX_FIFO] = hdr[0]; base[I2C_TX_FIFO] = hdr[1]; base[I2C_TX_FIFO] = hdr[2]; u32 timeout = get_tmr_ms() + 400; while (size) { if (base[I2C_FIFO_STATUS] & TX_FIFO_EMPTY_CNT) { u32 tmp = 0; u32 snd_size = MIN(size, 4); memcpy(&tmp, buf, snd_size); base[I2C_TX_FIFO] = tmp; buf += snd_size; size -= snd_size; } if (get_tmr_ms() > timeout) { res = 1; break; } } if (base[I2C_STATUS] & I2C_STATUS_NOACK || base[I2C_INT_STATUS] & NO_ACK) res = 1; // Disable packet mode. usleep(20); base[I2C_CNFG] &= 0xFFFFF9FF; // Disable interrupts. base[I2C_INT_EN] = 0; return res; } static int _i2c_recv_pkt(u32 i2c_idx, u8 *buf, u32 size, u32 dev_addr, u32 reg) { if (size > 32) return 0; int res = 0; vu32 *base = (vu32 *)i2c_addrs[i2c_idx]; // Enable interrupts. base[I2C_INT_EN] = ALL_PACKETS_COMPLETE | PACKET_COMPLETE | NO_ACK | ARB_LOST | TX_FIFO_OVER | RX_FIFO_UNDER | RX_FIFO_DATA_REQ; base[I2C_INT_STATUS] = base[I2C_INT_STATUS]; // Set device address and recv mode. base[I2C_CMD_ADDR0] = (dev_addr << 1) | ADDR0_READ; // Set recv mode. base[I2C_CNFG] = DEBOUNCE_CNT_4T | NEW_MASTER_FSM | CMD1_READ; // Set and flush FIFO. base[I2C_FIFO_CONTROL] = RX_FIFO_FLUSH | TX_FIFO_FLUSH; // Load configuration. _i2c_load_cfg_wait(base); // Initiate transaction on packet mode. base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFF9FF) | PACKET_MODE_GO; // Send reg request. u32 hdr[3]; hdr[0] = I2C_PACKET_PROT_I2C; hdr[1] = 1 - 1; hdr[2] = I2C_HEADER_REP_START | (dev_addr << 1); // Send header with reg request. base[I2C_TX_FIFO] = hdr[0]; base[I2C_TX_FIFO] = hdr[1]; base[I2C_TX_FIFO] = hdr[2]; base[I2C_TX_FIFO] = reg; u32 timeout = get_tmr_ms() + 400; while (!(base[I2C_FIFO_STATUS] & TX_FIFO_EMPTY_CNT)) if (get_tmr_ms() > timeout) break; // Send read request. hdr[1] = size - 1; hdr[2] = I2C_HEADER_READ | (dev_addr << 1); // Send header with read request. base[I2C_TX_FIFO] = hdr[0]; base[I2C_TX_FIFO] = hdr[1]; base[I2C_TX_FIFO] = hdr[2]; timeout = get_tmr_ms() + 400; while (size) { if (base[I2C_FIFO_STATUS] & RX_FIFO_FULL_CNT) { u32 rcv_size = MIN(size, 4); u32 tmp = base[I2C_RX_FIFO]; memcpy(buf, &tmp, rcv_size); buf += rcv_size; size -= rcv_size; } if (get_tmr_ms() > timeout) { res = 1; break; } } if (base[I2C_STATUS] & I2C_STATUS_NOACK || base[I2C_INT_STATUS] & NO_ACK) res = 1; // Disable packet mode. usleep(20); base[I2C_CNFG] &= 0xFFFFF9FF; // Disable interrupts. base[I2C_INT_EN] = 0; return res; } void i2c_init(u32 i2c_idx) { vu32 *base = (vu32 *)i2c_addrs[i2c_idx]; base[I2C_CLK_DIVISOR] = (5 << 16) | 1; // SF mode Div: 6, HS mode div: 2. base[I2C_BUS_CLEAR_CONFIG] = (9 << 16) | BC_TERMINATE | BC_ENABLE; // Load configuration. _i2c_load_cfg_wait(base); for (u32 i = 0; i < 10; i++) { usleep(20000); if (base[I2C_INT_STATUS] & BUS_CLEAR_DONE) break; } (vu32)base[I2C_BUS_CLEAR_STATUS]; base[I2C_INT_STATUS] = base[I2C_INT_STATUS]; } int i2c_recv_buf(u8 *buf, u32 size, u32 i2c_idx, u32 dev_addr) { return _i2c_recv_single(i2c_idx, buf, size, dev_addr); } int i2c_send_buf_big(u32 i2c_idx, u32 dev_addr, u8 *buf, u32 size) { if (size > 32) return 0; return _i2c_send_pkt(i2c_idx, buf, size, dev_addr); } int i2c_recv_buf_big(u8 *buf, u32 size, u32 i2c_idx, u32 dev_addr, u32 reg) { return _i2c_recv_pkt(i2c_idx, buf, size, dev_addr, reg); } int i2c_send_buf_small(u32 i2c_idx, u32 dev_addr, u32 reg, u8 *buf, u32 size) { u8 tmp[4]; if (size > 7) return 0; tmp[0] = reg; memcpy(tmp + 1, buf, size); return _i2c_send_single(i2c_idx, dev_addr, tmp, size + 1); } int i2c_recv_buf_small(u8 *buf, u32 size, u32 i2c_idx, u32 dev_addr, u32 reg) { int res = _i2c_send_single(i2c_idx, dev_addr, (u8 *)®, 1); if (res) res = _i2c_recv_single(i2c_idx, buf, size, dev_addr); return res; } int i2c_send_byte(u32 i2c_idx, u32 dev_addr, u32 reg, u8 val) { return i2c_send_buf_small(i2c_idx, dev_addr, reg, &val, 1); } u8 i2c_recv_byte(u32 i2c_idx, u32 dev_addr, u32 reg) { u8 tmp = 0; i2c_recv_buf_small(&tmp, 1, i2c_idx, dev_addr, reg); return tmp; }