linuxOS_D21X/source/linux-5.10/drivers/i2c/busses/i2c-artinchip-master.c
2025-06-05 14:33:02 +08:00

391 lines
9.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C driver of ArtInChip SoC
*
* Copyright (C) 2020-2023 ArtInChip Technology Co., Ltd.
* Authors: dwj <weijie.ding@artinchip.com>
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/device.h>
#include "i2c-artinchip.h"
static void i2c_config_fifo_master(struct aic_i2c_dev *i2c_dev)
{
/* Configure Tx/Rx FIFO threshold levels */
i2c_writel(i2c_dev, I2C_TXFIFO_THRESHOLD, I2C_TX_TL);
i2c_writel(i2c_dev, I2C_RXFIFO_THRESHOLD, I2C_RX_TL);
}
static int i2c_set_timmings_master(struct aic_i2c_dev *i2c_dev)
{
const char *mode_str;
u32 ic_clk;
int ret;
if (!i2c_dev->scl_hcnt || !i2c_dev->scl_lcnt) {
ic_clk = clk_get_rate(i2c_dev->clk);
if (!ic_clk) {
dev_err(i2c_dev->dev, "get i2c clock rate failed\n");
return -EINVAL;
}
ret = i2c_scl_cnt(ic_clk, i2c_dev->target_rate,
&i2c_dev->scl_hcnt, &i2c_dev->scl_lcnt);
if (ret) {
dev_err(i2c_dev->dev, "set i2c scl cnt error\n");
return ret;
}
}
switch (i2c_dev->i2c_speed) {
case I2C_SPEED_STD:
mode_str = "Standard Mode";
break;
case I2C_SPEED_FAST:
default:
mode_str = "Fast Mode";
break;
}
dev_dbg(i2c_dev->dev, "%s HCNT: %d, LCNT: %d\n", mode_str,
i2c_dev->scl_hcnt, i2c_dev->scl_lcnt);
return 0;
}
static void i2c_init_master(struct aic_i2c_dev *i2c_dev)
{
i2c_disable(i2c_dev);
i2c_dev->master_cfg = I2C_ENABLE_MASTER_DISABLE_SLAVE |
I2C_CTL_RESTART_ENABLE;
/* Configure SCL hcnt, lcnt and speed mode */
if (i2c_dev->i2c_speed == I2C_SPEED_STD) {
i2c_writel(i2c_dev, i2c_dev->scl_hcnt, I2C_SS_SCL_HCNT);
i2c_writel(i2c_dev, i2c_dev->scl_lcnt, I2C_SS_SCL_LCNT);
i2c_dev->master_cfg |= I2C_CTL_SPEED_MODE_SS;
} else {
i2c_writel(i2c_dev, i2c_dev->scl_hcnt, I2C_FS_SCL_HCNT);
i2c_writel(i2c_dev, i2c_dev->scl_lcnt, I2C_FS_SCL_LCNT);
i2c_dev->master_cfg |= I2C_CTL_SPEED_MODE_FS;
}
/* Configure Tx/Rx FIFO */
i2c_config_fifo_master(i2c_dev);
/* Configure I2C as master */
i2c_writel(i2c_dev, i2c_dev->master_cfg, I2C_CTL);
}
/* Set the slave address and transmission mode(7bit or 10bit)
* before enable I2C module.
*/
static void i2c_xfer_init(struct aic_i2c_dev *i2c_dev)
{
u32 ic_ctl;
/* If the slave address is ten bit address, enable 10ADDR */
ic_ctl = i2c_readl(i2c_dev, I2C_CTL);
if (i2c_dev->msg->flags & I2C_M_TEN)
ic_ctl |= I2C_CTL_10BIT_SELECT_MASTER;
else
ic_ctl &= ~I2C_CTL_10BIT_SELECT_MASTER;
i2c_writel(i2c_dev, ic_ctl, I2C_CTL);
/* Set the slave address */
i2c_writel(i2c_dev, i2c_dev->msg->addr, I2C_TAR);
}
/*
* Set the state of the message to its initial value before
* each message transmission
*/
static void i2c_xfer_msg_init(struct aic_i2c_dev *i2c_dev)
{
/* configure transfer status */
i2c_dev->buf_write_idx = 0;
i2c_dev->buf_read_idx = 0;
i2c_dev->msg_err = 0;
i2c_dev->abort_source = 0;
i2c_dev->msg_status = MSG_IDLE;
/* clear and enable interrupts */
i2c_writel(i2c_dev, 0xFFFF, I2C_INTR_CLR);
i2c_writel(i2c_dev, I2C_INTR_MASTER_MASK, I2C_INTR_MASK);
}
static void i2c_handle_read(struct aic_i2c_dev *i2c_dev)
{
struct i2c_msg *msg = i2c_dev->msg;
int rx_valid;
rx_valid = i2c_readl(i2c_dev, I2C_RXFLR);
while (rx_valid--) {
msg->buf[i2c_dev->buf_read_idx++] = i2c_readl(i2c_dev,
I2C_DATA_CMD);
}
/* message transfer done if it is a read message */
if (i2c_dev->buf_read_idx == msg->len) {
i2c_disable_interrupts(i2c_dev);
complete(&i2c_dev->cmd_complete);
}
}
static void i2c_handle_write(struct aic_i2c_dev *i2c_dev)
{
struct i2c_msg *msg = i2c_dev->msg;
int rx_valid, tx_valid, buf_len;
u32 intr_mask = I2C_INTR_MASTER_MASK;
/* In case of detecting the bus which slave address is used. */
/* The data transferred on I2C bus is: START ADDR Wr ACK STOP. */
if (i2c_dev->msg_status == MSG_IDLE && msg->len == 0 &&
i2c_dev->is_first_message && i2c_dev->is_last_message) {
u32 cmd = 0;
cmd |= I2C_DATA_CMD_STOP;
/* Write operation */
i2c_writel(i2c_dev, cmd, I2C_DATA_CMD);
i2c_disable_interrupts(i2c_dev);
complete(&i2c_dev->cmd_complete);
}
i2c_dev->msg_status = MSG_IN_PROCESS;
rx_valid = I2C_FIFO_DEPTH - i2c_readl(i2c_dev, I2C_RXFLR);
tx_valid = I2C_FIFO_DEPTH - i2c_readl(i2c_dev, I2C_TXFLR);
buf_len = msg->len - i2c_dev->buf_write_idx;
while (buf_len > 0 && rx_valid > 0 && tx_valid > 0) {
u32 cmd = 0;
if (buf_len == 1 && i2c_dev->is_last_message)
cmd |= I2C_DATA_CMD_STOP;
if (!i2c_dev->is_first_message && !i2c_dev->buf_write_idx)
cmd |= I2C_DATA_CMD_RESTART;
if (msg->flags & I2C_M_RD) {
/* Read operation, I2C read one byte when you set
* CMD bit to 1 in I2C_DATA_CMD register. Slave receive
* ACK signal and transmit next byte data when you
* set CMD bit to 1 in I2C_DATA_CMD register.
*/
i2c_writel(i2c_dev, cmd | I2C_DATA_CMD_READ,
I2C_DATA_CMD);
rx_valid--;
} else {
/* Write operation */
i2c_writel(i2c_dev,
cmd | msg->buf[i2c_dev->buf_write_idx],
I2C_DATA_CMD);
}
i2c_dev->buf_write_idx++;
tx_valid--;
buf_len--;
if (i2c_dev->buf_write_idx == msg->len) {
intr_mask &= ~I2C_INTR_TX_EMPTY;
i2c_writel(i2c_dev, intr_mask, I2C_INTR_MASK);
/* message transfer done if it is a write message */
if (!(msg->flags & I2C_M_RD)) {
i2c_disable_interrupts(i2c_dev);
complete(&i2c_dev->cmd_complete);
}
}
}
}
static int i2c_xfer_msg(struct aic_i2c_dev *i2c_dev, struct i2c_msg *msg,
bool is_first, bool is_last)
{
unsigned long timeout;
int ret = 0;
i2c_dev->msg = msg;
i2c_dev->is_first_message = is_first;
i2c_dev->is_last_message = is_last;
reinit_completion(&i2c_dev->cmd_complete);
if (is_first) {
i2c_xfer_init(i2c_dev);
/* Enable the adapter */
i2c_enable(i2c_dev);
ret = i2c_wait_bus_free(i2c_dev);
if (ret)
return ret;
}
i2c_xfer_msg_init(i2c_dev);
timeout = wait_for_completion_timeout(&i2c_dev->cmd_complete,
i2c_dev->adap.timeout);
if (!timeout) {
dev_dbg(i2c_dev->dev, "message xfer timeout\n");
ret = -ETIMEDOUT;
}
/* Because you need to send restart between different messages,
* So, disable I2C module between messages is prohibited
*/
if (i2c_dev->msg_err) {
if (i2c_dev->msg_err & I2C_INTR_ERROR_ABRT)
ret = i2c_handle_tx_abort(i2c_dev);
else
ret = -EIO;
}
return ret;
}
static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
int num)
{
struct aic_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
int ret = 0, i;
pm_runtime_get_sync(i2c_dev->dev);
for (i = 0; i < num && !ret; i++)
ret = i2c_xfer_msg(i2c_dev, &msgs[i], i == 0, i == (num -1));
/* Because you can only set the address mode when I2C disabled,
* so you must disable I2C after each transfer
*/
i2c_disable(i2c_dev);
pm_runtime_mark_last_busy(i2c_dev->dev);
pm_runtime_put_autosuspend(i2c_dev->dev);
return (ret < 0) ? ret : num;
}
static irqreturn_t i2c_isr(int irqno, void *dev_id)
{
struct aic_i2c_dev *i2c_dev = dev_id;
u32 intr_stat;
intr_stat = i2c_readl(i2c_dev, I2C_INTR_CLR);
/* clear all interrupt flags */
i2c_writel(i2c_dev, 0xFFFF, I2C_INTR_CLR);
if (intr_stat & I2C_INTR_TX_ABRT) {
i2c_dev->msg_err |= I2C_INTR_ERROR_ABRT;
i2c_dev->abort_source = i2c_readl(i2c_dev, I2C_TX_ABRT_SOURCE);
goto tx_aborted;
}
if (intr_stat & (I2C_INTR_RX_OVER | I2C_INTR_RX_UNDER |
I2C_INTR_TX_OVER)) {
i2c_dev->msg_err |= I2C_INTR_ERROR_TX_RX;
goto tx_aborted;
}
if (intr_stat & I2C_INTR_RX_FULL)
i2c_handle_read(i2c_dev);
if (intr_stat & I2C_INTR_TX_EMPTY)
i2c_handle_write(i2c_dev);
tx_aborted:
if (intr_stat & (I2C_INTR_TX_ABRT | I2C_INTR_STOP_DET |
I2C_INTR_RX_OVER | I2C_INTR_RX_UNDER |
I2C_INTR_TX_OVER)) {
i2c_disable_interrupts(i2c_dev);
complete(&i2c_dev->cmd_complete);
}
return IRQ_HANDLED;
}
static u32 i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm i2c_algo = {
.master_xfer = i2c_xfer,
.functionality = i2c_func,
};
int i2c_probe_master(struct aic_i2c_dev *i2c_dev)
{
struct i2c_adapter *adap = &i2c_dev->adap;
int ret, cmd, rate;
init_completion(&i2c_dev->cmd_complete);
ret = i2c_set_timmings_master(i2c_dev);
if (ret)
return ret;
i2c_init_master(i2c_dev);
snprintf(adap->name, sizeof(adap->name),
"ArtInChip I2C adapter");
adap->retries = 3;
adap->algo = &i2c_algo;
i2c_set_adapdata(adap, i2c_dev);
/* disable all interrupt and clear interrupt flags */
i2c_disable_interrupts(i2c_dev);
i2c_writel(i2c_dev, 0xFFFF, I2C_INTR_CLR);
/* Enable SDA &SCL Stuck Timeout Restore */
cmd = i2c_readl(i2c_dev, I2C_CTL);
i2c_writel(i2c_dev, I2C_CTL_BUS_CLEAR_FEATURE | cmd, I2C_CTL);
cmd = i2c_readl(i2c_dev, I2C_ENABLE);
i2c_writel(i2c_dev, I2C_SDA_STUCK_RECOVERY_ENABLE | cmd, I2C_ENABLE);
msleep(1);
/* Set I2C SDA & SCL Sutuck Time*/
rate = i2c_dev->target_rate;
if (rate <= 10000) {
i2c_writel(i2c_dev, 0xa00000, I2C_SDA_STUCK_TIMEOUT);
i2c_writel(i2c_dev, 0xa00000, I2C_SCL_STUCK_TIMEOUT);
} else if (rate > 10000 && rate < 100000) {
i2c_writel(i2c_dev, 0x6000, I2C_SDA_STUCK_TIMEOUT);
i2c_writel(i2c_dev, 0x6000, I2C_SCL_STUCK_TIMEOUT);
} else {
i2c_writel(i2c_dev, 0x1000, I2C_SDA_STUCK_TIMEOUT);
i2c_writel(i2c_dev, 0x1000, I2C_SCL_STUCK_TIMEOUT);
}
ret = devm_request_irq(i2c_dev->dev, i2c_dev->irq, i2c_isr, 0,
dev_name(i2c_dev->dev), i2c_dev);
if (ret) {
dev_err(i2c_dev->dev, "failed to request irq %i: %d\n",
i2c_dev->irq, ret);
return ret;
}
ret = i2c_add_numbered_adapter(adap);
if (ret)
dev_err(i2c_dev->dev, "failure adding adapter: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(i2c_probe_master);
MODULE_DESCRIPTION("ArtInChip I2C bus master adapter");
MODULE_LICENSE("GPL");