linuxOS_D21X/source/linux-5.10/drivers/i2c/busses/i2c-artinchip-slave.c
2024-11-29 16:13:46 +08:00

170 lines
4.1 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_configure_fifo_slave(struct aic_i2c_dev *i2c_dev)
{
i2c_writel(i2c_dev, 0, I2C_TX_TL);
i2c_writel(i2c_dev, 0, I2C_RX_TL);
}
static void i2c_init_slave(struct aic_i2c_dev *i2c_dev)
{
i2c_disable(i2c_dev);
i2c_dev->slave_cfg = I2C_ENABLE_SLAVE_DISABLE_MASTER |
I2C_CTL_STOP_DET_IFADDR;
i2c_configure_fifo_slave(i2c_dev);
/* Configure I2C slave mode */
i2c_writel(i2c_dev, i2c_dev->slave_cfg, I2C_CTL);
/* clear all interrupt flags */
i2c_writel(i2c_dev, 0xFFFF, I2C_INTR_CLR);
/* Enable slave interrupt */
i2c_writel(i2c_dev, I2C_INTR_SLAVE_MASK, I2C_INTR_MASK);
/* Enable I2C */
i2c_enable(i2c_dev);
}
static int i2c_reg_slave(struct i2c_client *slave)
{
u32 ic_ctl;
struct aic_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter);
pm_runtime_get_sync(i2c_dev->dev);
/* disable I2C adapter */
i2c_disable(i2c_dev);
ic_ctl = i2c_readl(i2c_dev, I2C_CTL);
if (i2c_dev->slave)
return -EBUSY;
/* Configure slave address mode */
if (slave->flags & I2C_CLIENT_TEN)
ic_ctl |= I2C_CTL_10BIT_SELECT_SLAVE;
else
ic_ctl &= ~I2C_CTL_10BIT_SELECT_SLAVE;
i2c_writel(i2c_dev, ic_ctl, I2C_CTL);
/* Configure slave address */
i2c_writel(i2c_dev, slave->addr, I2C_SAR);
i2c_dev->slave = slave;
i2c_enable(i2c_dev);
return 0;
}
static int i2c_unreg_slave(struct i2c_client *slave)
{
struct aic_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter);
i2c_disable_interrupts(i2c_dev);
i2c_disable(i2c_dev);
i2c_dev->slave = NULL;
pm_runtime_put(i2c_dev->dev);
return 0;
}
static u32 i2c_func_slave(struct i2c_adapter *adapter)
{
return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static irqreturn_t i2c_irq_handler_slave(int this_irq, void *dev_id)
{
struct aic_i2c_dev *i2c_dev = dev_id;
u32 intr_stat;
u8 val;
intr_stat = i2c_readl(i2c_dev, I2C_INTR_CLR);
if (intr_stat & I2C_INTR_START_DET) {
i2c_slave_event(i2c_dev->slave,
I2C_SLAVE_WRITE_REQUESTED, &val);
i2c_writel(i2c_dev, I2C_INTR_START_DET, I2C_INTR_CLR);
}
if (intr_stat & I2C_INTR_RX_FULL) {
val = i2c_readl(i2c_dev, I2C_DATA_CMD);
if (!i2c_slave_event(i2c_dev->slave,
I2C_SLAVE_WRITE_RECEIVED, &val))
dev_dbg(i2c_dev->dev, "Byte 0x%02x acked!\n", val);
i2c_writel(i2c_dev, I2C_INTR_RX_FULL, I2C_INTR_CLR);
}
if (intr_stat & I2C_INTR_RD_REQ) {
if (!i2c_slave_event(i2c_dev->slave,
I2C_SLAVE_READ_REQUESTED, &val))
i2c_writel(i2c_dev, val, I2C_DATA_CMD);
i2c_slave_event(i2c_dev->slave, I2C_SLAVE_READ_PROCESSED, &val);
i2c_writel(i2c_dev, I2C_INTR_RD_REQ, I2C_INTR_CLR);
}
if (intr_stat & I2C_INTR_RX_DONE) {
i2c_slave_event(i2c_dev->slave, I2C_SLAVE_STOP, &val);
i2c_writel(i2c_dev, I2C_INTR_RX_DONE, I2C_INTR_CLR);
}
return IRQ_HANDLED;
}
static const struct i2c_algorithm i2c_algo_slave = {
.reg_slave = i2c_reg_slave,
.unreg_slave = i2c_unreg_slave,
.functionality = i2c_func_slave,
};
int i2c_probe_slave(struct aic_i2c_dev *i2c_dev)
{
struct i2c_adapter *adap = &i2c_dev->adap;
int ret;
i2c_init_slave(i2c_dev);
snprintf(adap->name, sizeof(adap->name),
"ArtInChip I2C slave adapter");
adap->retries = 3;
adap->algo = &i2c_algo_slave;
adap->dev.parent = i2c_dev->dev;
i2c_set_adapdata(adap, i2c_dev);
ret = devm_request_irq(i2c_dev->dev, i2c_dev->irq,
i2c_irq_handler_slave, 0,
dev_name(i2c_dev->dev), i2c_dev);
if (ret) {
dev_err(i2c_dev->dev, "failure requesting 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_slave);
MODULE_DESCRIPTION("ArtInChip I2C bus slave adapter");
MODULE_LICENSE("GPL");