/* * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. * * SPDX-License-Identifier: MIT */ #include "common.h" #include "uart.h" #include "FreeRTOS.h" #include #include "meson_i2c.h" #include #include "timer_source.h" #include #include "drv_errno.h" #include "suspend.h" #define ERR_I2C(errno) (DRV_ERRNO_I2C_BASE | errno) #define BIT(nr) (1UL << (nr)) #define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) #define I2C_TIMEOUT_MS (1000 * 100) /* Control register fields */ #define REG_CTRL_START BIT(0) #define REG_CTRL_ACK_IGNORE BIT(1) #define REG_CTRL_STATUS BIT(2) #define REG_CTRL_ERROR BIT(3) #define REG_CTRL_CLKDIV_SHIFT 12 #define REG_CTRL_CLKDIV_MASK GENMASK(21, 12) #define REG_CTRL_CLKDIVEXT_SHIFT 28 #define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28) #define I2C_DEBUG 0 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #undef MIN #define MIN(X, Y)\ ({\ typeof(X) x_ = (X);\ typeof(Y) y_ = (Y);\ (x_ < y_) ? x_ : y_;\ }) enum bool { false, true }; enum { TOKEN_END = 0, TOKEN_START, TOKEN_SLAVE_ADDR_WRITE, TOKEN_SLAVE_ADDR_READ, TOKEN_DATA, TOKEN_DATA_LAST, TOKEN_STOP, }; struct xI2cRegs { uint32_t ctrl; uint32_t slave_addr; uint32_t tok_list0; uint32_t tok_list1; uint32_t tok_wdata0; uint32_t tok_wdata1; uint32_t tok_rdata0; uint32_t tok_rdata1; }; struct xMesonI2c { struct xI2cRegs *regs; struct xI2cMsg *msg; /* Current I2C message */ enum bool last; /* Whether the message is the last */ uint32_t count; /* Number of bytes in the current transfer */ uint32_t pos; /* Position of current transfer in message */ uint32_t tokens[2]; /* Sequence of tokens to be written */ uint32_t num_tokens; /* Number of tokens to be written */ uint32_t clock_frequency; uint32_t div_factor; uint32_t delay_ajust; uint32_t clkin_rate; }; struct xMesonI2c i2cs[8]; struct xMesonI2cPlatdata *plat; uint32_t current_id; #define DEFAULT_CLK81 0 static void prvSetBitsLe32(uint32_t *reg, uint32_t set) { uint32_t val; val = REG32(reg); val |= (set); REG32(reg) = val; } static void prvClrBitsLe32(uint32_t *reg, uint32_t clr) { uint32_t val; val = REG32(reg); val &= (~(clr)); REG32(reg) = val; } static void prvClrSetBitsLe32(uint32_t *reg, uint32_t clr, uint32_t set) { uint32_t val; val = REG32(reg); val &= (~(clr)); val |= (set); REG32(reg) = val; } #if I2C_DEBUG void xI2cDumpRegs(void) { iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->ctrl, i2cs[current_id].regs->ctrl); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->slave_addr, i2cs[current_id].regs->slave_addr); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->tok_list0, i2cs[current_id].regs->tok_list0); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->tok_list1, i2cs[current_id].regs->tok_list1); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->tok_wdata0, i2cs[current_id].regs->tok_wdata0); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->tok_wdata1, i2cs[current_id].regs->tok_wdata1); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->tok_rdata0, i2cs[current_id].regs->tok_rdata0); iprintf("i2c reg : 0x%x = 0x%x\n", &i2cs[current_id].regs->tok_rdata1, i2cs[current_id].regs->tok_rdata1); } #endif static void prvMesonI2cRegsInit(void) { uint32_t i; for (i = 0; i < 8; i++) REG32(&i2cs[current_id].regs->ctrl + i) = 0; } static void prvMesonI2cResetTokens(void) { i2cs[current_id].tokens[0] = 0; i2cs[current_id].tokens[1] = 0; i2cs[current_id].num_tokens = 0; } static void prvMesonI2cAddToken(uint32_t token) { if (i2cs[current_id].num_tokens < 8) i2cs[current_id].tokens[0] |= (token & 0xf) << (i2cs[current_id].num_tokens * 4); else i2cs[current_id].tokens[1] |= (token & 0xf) << ((i2cs[current_id].num_tokens % 8) * 4); i2cs[current_id].num_tokens++; } /* * Retrieve data for the current transfer (which can be at most 8 * bytes) from the device internal buffer. */ static void prvMesonI2cGetData(uint8_t *buf, uint32_t len) { uint32_t rdata0, rdata1; uint32_t i; rdata0 = i2cs[current_id].regs->tok_rdata0; rdata1 = i2cs[current_id].regs->tok_rdata1; #if I2C_DEBUG iprintf("meson i2c: read data %08x %08x len %d\n", rdata0, rdata1, len); #endif for (i = 0; i < MIN((uint32_t)4, len); i++) *buf++ = (rdata0 >> i * 8) & 0xff; for (i = 4; i < MIN((uint32_t)8, len); i++) *buf++ = (rdata1 >> (i - 4) * 8) & 0xff; } /* * Write data for the current transfer (which can be at most 8 bytes) * to the device internal buffer. */ static void prvMesonI2cPutData(uint8_t *buf, uint32_t len) { uint32_t wdata0 = 0, wdata1 = 0; uint32_t i; for (i = 0; i < MIN((uint32_t)4, len); i++) wdata0 |= *buf++ << (i * 8); for (i = 4; i < MIN((uint32_t)8, len); i++) wdata1 |= *buf++ << ((i - 4) * 8); i2cs[current_id].regs->tok_wdata0 = wdata0; i2cs[current_id].regs->tok_wdata1 = wdata1; #if I2C_DEBUG iprintf("meson i2c: write data 0x%08x 0x%08x len %d\n", wdata0, wdata1, len); #endif } /* * Prepare the next transfer: pick the next 8 bytes in the remaining * part of message and write tokens and data (if needed) to the * device. */ static void prvMesonI2cPrepareXfer(void) { enum bool write = !(i2cs[current_id].msg->flags & I2C_M_RD); uint32_t i; i2cs[current_id].count = MIN(i2cs[current_id].msg->len - i2cs[current_id].pos, 8u); for (i = 0; i + 1 < i2cs[current_id].count; i++) prvMesonI2cAddToken(TOKEN_DATA); if (i2cs[current_id].count) { if (write || i2cs[current_id].pos + i2cs[current_id].count < i2cs[current_id].msg->len) prvMesonI2cAddToken(TOKEN_DATA); else prvMesonI2cAddToken(TOKEN_DATA_LAST); } if (write) prvMesonI2cPutData(i2cs[current_id].msg->buf + i2cs[current_id].pos, i2cs[current_id].count); if (i2cs[current_id].last && i2cs[current_id].pos + i2cs[current_id].count >= i2cs[current_id].msg->len) prvMesonI2cAddToken(TOKEN_STOP); i2cs[current_id].regs->tok_list0 = i2cs[current_id].tokens[0]; i2cs[current_id].regs->tok_list1 = i2cs[current_id].tokens[1]; } static void prvMesonI2cDoStart(struct xI2cMsg *msg) { uint32_t token; token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ : TOKEN_SLAVE_ADDR_WRITE; /* change it if duty change */ i2cs[current_id].regs->slave_addr = msg->addr << 1; prvMesonI2cAddToken(TOKEN_START); prvMesonI2cAddToken(token); } static int32_t prvMesonI2cXferMsg(struct xI2cMsg *msg, uint32_t last) { uint32_t time_count = 0; uint32_t *ctrl = &i2cs[current_id].regs->ctrl; taskENTER_CRITICAL(); #if I2C_DEBUG iprintf("meson i2c: %s addr 0x%x len %u\n", (msg->flags & I2C_M_RD) ? "read" : "write", msg->addr, msg->len); #endif i2cs[current_id].msg = msg; i2cs[current_id].last = last; i2cs[current_id].pos = 0; i2cs[current_id].count = 0; prvMesonI2cResetTokens(); prvMesonI2cDoStart(msg); do { prvMesonI2cPrepareXfer(); /* start the transfer */ prvSetBitsLe32(&(i2cs[current_id].regs->ctrl), REG_CTRL_START); while (REG32(ctrl) & REG_CTRL_STATUS) { if (time_count > I2C_TIMEOUT_MS) { prvClrBitsLe32(&i2cs[current_id].regs->ctrl, REG_CTRL_START); taskEXIT_CRITICAL(); return ERR_I2C(DRV_ERROR_TIMEOUT); } udelay(1); time_count++; } prvMesonI2cResetTokens(); prvClrBitsLe32(&i2cs[current_id].regs->ctrl, REG_CTRL_START); if (REG32(ctrl) & REG_CTRL_ERROR) { taskEXIT_CRITICAL(); return ERR_I2C(DRV_ERROR_ACCESS); } if ((msg->flags & I2C_M_RD) && i2cs[current_id].count) { prvMesonI2cGetData(i2cs[current_id].msg->buf + i2cs[current_id].pos, i2cs[current_id].count); } i2cs[current_id].pos += i2cs[current_id].count; } while (i2cs[current_id].pos < msg->len); taskEXIT_CRITICAL(); return 0; } int32_t xI2cMesonXfer(struct xI2cMsg *msg, uint32_t nmsgs) { uint32_t i; int32_t ret; for (i = 0; i < nmsgs; i++) { ret = prvMesonI2cXferMsg(msg + i, i == (nmsgs - 1)); if (ret) { iprintf("meson_i2c_xfer error\n"); return ret; } } return 0; } int32_t xI2cMesonSetBusSpeed(uint32_t speed) { uint32_t clk_rate = i2cs[current_id].clkin_rate; uint32_t div; taskENTER_CRITICAL(); div = DIV_ROUND_UP(clk_rate, speed * i2cs[current_id].div_factor); /* clock divider has 12 bits */ if (div >= (1 << 12)) { iprintf("meson i2c: requested bus frequency too low\n"); div = (1 << 12) - 1; } prvClrSetBitsLe32(&i2cs[current_id].regs->ctrl, REG_CTRL_CLKDIV_MASK, (div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT); prvClrSetBitsLe32(&i2cs[current_id].regs->ctrl, REG_CTRL_CLKDIVEXT_MASK, (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT); taskEXIT_CRITICAL(); return 0; } int32_t xI2cMesonRead(uint32_t addr, uint8_t offset, uint8_t *buffer, uint32_t len) { struct xI2cMsg msg[2], *ptr; uint32_t msg_count; int32_t ret; if (!len) return ERR_I2C(DRV_ERROR_PARAMETER); ptr = msg; ptr->addr = addr; ptr->flags = 0; ptr->len = 1; ptr->buf = &offset; ptr++; ptr->addr = addr; ptr->flags = I2C_M_RD; ptr->len = len; ptr->buf = buffer; ptr++; msg_count = ptr - msg; ret = xI2cMesonXfer(msg, msg_count); if (ret < 0) return ret; return 0; } int32_t xI2cMesonWrite(uint32_t addr, uint8_t offset, uint8_t *buffer, uint32_t len) { struct xI2cMsg msg[1]; int32_t ret = 0; uint8_t buf[len + 1]; buf[0] = offset; msg->addr = addr; msg->len = len + 1; /* addr's length + len */ msg->buf = buf; msg->flags = 0; memcpy(&buf[1], buffer, len); ret = xI2cMesonXfer(msg, 1); if (ret < 0) return ret; return 0; } int32_t xI2cMesonRead16(uint32_t addr, unsigned int offset, uint8_t *buffer, uint32_t len) { struct xI2cMsg msg[2], *ptr; uint8_t offset_buf[2]; unsigned int msg_count; int ret; if (!len) return ERR_I2C(DRV_ERROR_PARAMETER); offset_buf[0] = offset >> 8; offset_buf[1] = offset & 0xff; ptr = msg; ptr->addr = addr; ptr->flags = 0; ptr->len = 2; ptr->buf = offset_buf; ptr++; ptr->addr = addr; ptr->flags = I2C_M_RD; ptr->len = len; ptr->buf = buffer; ptr++; msg_count = ptr - msg; ret = xI2cMesonXfer(msg, msg_count); if (ret < 0) return ret; return 0; } int32_t xI2cMesonWrite16(uint32_t addr, unsigned int offset, uint8_t *buffer, uint32_t len) { struct xI2cMsg msg[1]; int ret = 0; unsigned char buf[len + 2]; buf[0] = offset >> 8; buf[1] = offset & 0xff; msg->addr = addr; msg->len = len + 2;/* addr's length + len */ msg->buf = buf; msg->flags = 0; memcpy(&buf[2], buffer, len); ret = xI2cMesonXfer(msg, 1); if (ret < 0) return ret; return 0; } /* *i2c master platform data init */ int32_t xI2cMesonPortInit(uint32_t id) { struct xMesonI2cPlatdata *cur_plat = NULL; /* get the global i2c plat data */ plat = i2c_plat_data; cur_plat = &plat[id]; i2cs[id].regs = (struct xI2cRegs *)cur_plat->reg; i2cs[id].div_factor = cur_plat->div_factor; i2cs[id].delay_ajust = cur_plat->delay_ajust; i2cs[id].clock_frequency = cur_plat->clock_frequency; if (cur_plat->clk_base) REG32_UPDATE_BITS(cur_plat->clk_base, BIT(cur_plat->clk_offset), BIT(cur_plat->clk_offset)); if (get_suspend_flag()) { #if I2C_DEBUG iprintf("meson i2c: clkin is 24M\n"); #endif i2cs[id].clkin_rate = 24000000; } else { #if I2C_DEBUG iprintf("meson i2c: clkin is 166M\n"); #endif i2cs[id].clkin_rate = cur_plat->clkin_rate; } current_id = id; #if I2C_DEBUG iprintf("index = %u, reg = 0x%x, div = %u, delay = %u ,clock-frequency = %u\n", cur_plat->bus_num, i2cs[id].regs, i2cs[id].div_factor, i2cs[id].delay_ajust, i2cs[id].clock_frequency); #endif prvMesonI2cRegsInit(); xI2cMesonSetBusSpeed(i2cs[id].clock_frequency); /*init i2c work speed */ return 0; } void xI2cSetCurrentId(uint32_t id) { taskENTER_CRITICAL(); current_id = id; taskEXIT_CRITICAL(); }