/* * Copyright (c) 2018 naehrwert * * 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 <http://www.gnu.org/licenses/>. */ #include <string.h> #include "i2c.h" #include "../utils/util.h" static u32 i2c_addrs[] = { 0x7000C000, 0x7000C400, 0x7000C500, 0x7000C700, 0x7000D000, 0x7000D100 }; static void _i2c_wait(vu32 *base) { base[I2C_CONFIG_LOAD] = 0x25; for (u32 i = 0; i < 20; i++) { usleep(1); if (!(base[I2C_CONFIG_LOAD] & 1)) break; } } static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size) { if (size > 4) return 0; u32 tmp = 0; memcpy(&tmp, buf, size); vu32 *base = (vu32 *)i2c_addrs[idx]; base[I2C_CMD_ADDR0] = x << 1; //Set x (send mode). base[I2C_CMD_DATA1] = tmp; //Set value. base[I2C_CNFG] = ((size - 1) << 1) | 0x2800; //Set size and send mode. _i2c_wait(base); //Kick transaction. base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200; while (base[I2C_STATUS] & 0x100) ; if (base[I2C_STATUS] << 28) return 0; return 1; } static int _i2c_recv_pkt(u32 idx, u8 *buf, u32 size, u32 x) { if (size > 8) return 0; vu32 *base = (vu32 *)i2c_addrs[idx]; base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode). base[I2C_CNFG] = ((size - 1) << 1) | 0x2840; // Set size and recv mode. _i2c_wait(base); // Kick transaction. base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200; while (base[I2C_STATUS] & 0x100) ; if (base[I2C_STATUS] << 28) 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; } void i2c_init(u32 idx) { vu32 *base = (vu32 *)i2c_addrs[idx]; base[I2C_CLK_DIVISOR_REGISTER] = 0x50001; base[I2C_BUS_CLEAR_CONFIG] = 0x90003; _i2c_wait(base); for (u32 i = 0; i < 10; i++) { usleep(20000); if (base[INTERRUPT_STATUS_REGISTER] & 0x800) break; } (vu32)base[I2C_BUS_CLEAR_STATUS]; base[INTERRUPT_STATUS_REGISTER] = base[INTERRUPT_STATUS_REGISTER]; } int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size) { u8 tmp[4]; if (size > 3) return 0; tmp[0] = y; memcpy(tmp + 1, buf, size); return _i2c_send_pkt(idx, x, tmp, size + 1); } int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y) { int res = _i2c_send_pkt(idx, x, (u8 *)&y, 1); if (res) res = _i2c_recv_pkt(idx, buf, size, x); return res; } int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b) { return i2c_send_buf_small(idx, x, y, &b, 1); } u8 i2c_recv_byte(u32 idx, u32 x, u32 y) { u8 tmp = 0; i2c_recv_buf_small(&tmp, 1, idx, x, y); return tmp; }