Rtwo/kernel/motorola/sm8550/drivers/i2c/busses/i2c-slave.c
2025-09-30 19:22:48 -05:00

1063 lines
27 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/interconnect.h>
#include <linux/io.h>
#include <linux/ipc_logging.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include "i2c-slave.h"
#define CREATE_TRACE_POINTS
#include <trace/i2c_slave_trace.h>
/**
* i2c_slave_trace_log: FTRACE Logging.
* @dev: Driver model device node for the i2c slave.
* @fmt: ftrace log format.
*
* This function will add logs to ftrace
* file.
*
* Return: None.
*/
void i2c_slave_trace_log(struct device *dev, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
trace_i2c_slave_log_info(dev_name(dev), &vaf);
va_end(args);
}
/**
* dump_register - To dump the register value.
* @i2c_slave: Pointer to Main Structure.
*
* This function will dump the all readable register
* value to IPC log.
*
* Return: None.
*/
static void dump_register(struct i2c_slave *i2c_slave)
{
unsigned int temp;
temp = readl_relaxed(i2c_slave->base + I2C_S_DEVICE_ADDR);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_DEVICE_ADDR: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_IRQ_STATUS);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_IRQ_STATUS: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_CONFIG);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_CONFIG: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_IRQ_EN);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_IRQ_EN: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_FIFOS_STATUS);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_FIFOS_STATUS: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_DEBUG_REG1);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_DEBUG_REG1: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_DEBUG_REG2);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_DEBUG_REG2: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_CLK_LOW_TIMEOUT);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_CLK_LOW_TIMEOUT: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_CLK_RELEASE_DELAY_CNT_VAL);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_CLK_RELEASE_DELAY_CNT_VAL: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + I2C_S_SDA_HOLD_CNT_VAL);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"I2C_S_SDA_HOLD_CNT_VAL: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + SMBALERT_STATUS_REG);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"SMBALERT_STATUS_REG: 0x%x\n", temp);
temp = readl_relaxed(i2c_slave->base + SMBALERT_PEC_REG);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"SMBALERT_PEC_REG: 0x%x\n", temp);
}
/**
* read_tx_fifo_byte_count: To read TX FIFO count.
* @i2c_slave: Pointer to Main Structure.
*
* This function will read the number of bytes written
* to the TX FIFO.
*
* Return: Number of bytes written to the TX FIFO.
*/
static unsigned int read_tx_fifo_byte_count(struct i2c_slave *i2c_slave)
{
unsigned int count = 0;
count = readl_relaxed(i2c_slave->base + I2C_S_FIFOS_STATUS);
return (count & 0xFFFF);
}
/**
* read_rx_fifo_byte_count: To read RX FIFO count.
* @i2c_slave: Pointer to Main Structure.
*
* This function will read the number of bytes written
* to the RX FIFO.
*
* Return: Number of bytes written to the RX FIFO.
*/
static unsigned int read_rx_fifo_byte_count(struct i2c_slave *i2c_slave)
{
unsigned int count = 0;
count = readl_relaxed(i2c_slave->base + I2C_S_FIFOS_STATUS);
return ((count & 0xFFFF0000) >> 16);
}
/**
* i2c_slave_write_fifo: This function will write data to TX FIFO.
* @i2c_slave: Pointer to Main Structure.
*
* This function will write data to TX FIFO.
*
* Return: None.
*/
static void i2c_slave_write_fifo(struct i2c_slave *i2c_slave)
{
int i;
if (i2c_slave->tx_count == 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, false, i2c_slave->dev,
"TX FIFO write count is zero\n");
return;
}
for (i = 0; i < i2c_slave->tx_count; i++) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"Data to Tx FIFO: 0x%x\n", i2c_slave->tx_msg_buf[i]);
writel_relaxed(i2c_slave->tx_msg_buf[i],
i2c_slave->base + I2C_S_TX_FIFO);
}
i2c_slave->tx_count = 0;
}
/**
* i2c_slave_read_fifo: This function will read data to RX FIFO.
* @i2c_slave: Pointer to Main Structure.
*
* This function will read data to RX FIFO.
*
* Return: None.
*/
static void i2c_slave_read_fifo(struct i2c_slave *i2c_slave)
{
unsigned int rx_data_count;
int i;
rx_data_count = read_rx_fifo_byte_count(i2c_slave);
if (rx_data_count == 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, false, i2c_slave->dev,
"RX FIFO empty\n");
} else if (i2c_slave->rx_count >= I2C_SLAVE_MAX_MSG_SIZE) {
I2C_SLAVE_ERR(i2c_slave->ipcl, false, i2c_slave->dev,
"RX data buffer full\n");
} else {
for (i = 0; i < rx_data_count &&
i2c_slave->rx_count < I2C_SLAVE_MAX_MSG_SIZE; i++) {
i2c_slave->rx_msg_buf[i2c_slave->rx_count] =
readl_relaxed(i2c_slave->base + I2C_S_RX_FIFO);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"Data from RX_FIFO: 0x%x",
i2c_slave->rx_msg_buf[i2c_slave->rx_count]);
i2c_slave->rx_count++;
}
wake_up_interruptible(&i2c_slave->readq);
}
}
/**
* i2c_slave_write_bit: To SET the register bit.
* @i2c_slave: Pointer to Main Structure.
* @reg: offset of Register address.
* @bit: bit to modify.
*
* This function will set the bit to given register.
*
* Return: None.
*/
static void i2c_slave_write_bit(struct i2c_slave *i2c_slave, int reg, int bit)
{
unsigned int temp;
temp = readl_relaxed(i2c_slave->base + reg) | bit;
writel_relaxed(temp, i2c_slave->base + reg);
}
/**
* i2c_slave_send_ack: To send ACK to master.
* @i2c_slave: Pointer to Main Structure.
*
* This function will send ACK to master.
*
* Return: None.
*/
static void i2c_slave_send_ack(struct i2c_slave *i2c_slave)
{
writel_relaxed(ACK_RESUME, i2c_slave->base + I2C_S_CONTROL);
}
/**
* i2c_slave_send_nack: To send NACK to master.
* @i2c_slave: Pointer to Main Structure.
*
* This function will send NACK to master.
*
* Return: None.
*/
static void i2c_slave_send_nack(struct i2c_slave *i2c_slave)
{
writel_relaxed(NACK, i2c_slave->base + I2C_S_CONTROL);
}
/**
* i2c_slave_clear_irq: To clear IRQ bit
* @i2c_slave: Pointer to Main Structure.
* enum: enum to IRQ bit
*
* This function will clear the handled IRQ bit.
*
* Return: None.
*/
static void i2c_slave_clear_irq(struct i2c_slave *i2c_slave,
enum i2c_slave_irq_status bit)
{
if (bit == ALL_IRQ)
writel_relaxed(ALL_IRQ, i2c_slave->base + I2C_S_IRQ_CLR);
else
writel_relaxed(1 << bit, i2c_slave->base + I2C_S_IRQ_CLR);
}
/**
* i2c_slave_enable_irq: To enable IRQ.
* @i2c_slave: Pointer to Main Structure.
*
* This function will enable the IRQ.
*
* Return: None.
*/
static void i2c_slave_enable_irq(struct i2c_slave *i2c_slave)
{
writel_relaxed(ALL_IRQ, i2c_slave->base + I2C_S_IRQ_EN);
}
/**
* i2c_slave_irq: I2C Slave IQR handler
* @irq: IRQ number
* @dev: Pointer to Dev Structure.
*
* This function will handle IRQ bits.
*
* return: IRQ_HANDLED for success.
*/
static irqreturn_t i2c_slave_irq(int irq, void *dev)
{
unsigned int irq_stat;
struct i2c_slave *i2c_slave = dev;
irq_stat = readl_relaxed(i2c_slave->base + I2C_S_IRQ_STATUS);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"irq status: 0x%x\n", irq_stat);
if (irq_stat & (1 << ERR_CONDITION)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[ERR_CONDITION]);
dump_register(i2c_slave);
i2c_slave_write_bit(i2c_slave, I2C_S_SW_RESET_REG, SW_RESET);
i2c_slave_clear_irq(i2c_slave, ALL_IRQ);
i2c_slave_enable_irq(i2c_slave);
i2c_slave_write_bit(i2c_slave, I2C_S_CONTROL,
CLEAR_TX_FIFO | CLEAR_RX_FIFO);
i2c_slave_write_bit(i2c_slave, I2C_S_CLK_LOW_TIMEOUT, TIMER_MODE);
i2c_slave_write_bit(i2c_slave, I2C_S_CONFIG, CORE_EN);
i2c_slave_send_nack(i2c_slave);
}
if (irq_stat & (1 << CLOCK_LOW_TIMEOUT)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[CLOCK_LOW_TIMEOUT]);
i2c_slave_clear_irq(i2c_slave, ALL_IRQ);
}
if (irq_stat & (1 << STOP_DETECTED)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[STOP_DETECTED]);
i2c_slave_clear_irq(i2c_slave, STOP_DETECTED);
i2c_slave_read_fifo(i2c_slave);
}
if (irq_stat & (1 << RX_FIFO_FULL)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[RX_FIFO_FULL]);
i2c_slave_send_nack(i2c_slave);
i2c_slave_clear_irq(i2c_slave, RX_FIFO_FULL);
}
if (irq_stat & (1 << STRCH_RD)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[STRCH_RD]);
i2c_slave->tx_count = read_tx_fifo_byte_count(i2c_slave);
if (i2c_slave->tx_count > 0) {
i2c_slave_send_ack(i2c_slave);
} else {
I2C_SLAVE_ERR(i2c_slave->ipcl, false, i2c_slave->dev,
"TX FIFO empty\n");
i2c_slave_send_nack(i2c_slave);
}
i2c_slave_clear_irq(i2c_slave, STRCH_RD);
}
if (irq_stat & (1 << RX_DATA_AVAIL)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[RX_DATA_AVAIL]);
i2c_slave_clear_irq(i2c_slave, RX_DATA_AVAIL);
}
if (irq_stat & (1 << STRCH_WR)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[STRCH_WR]);
i2c_slave_clear_irq(i2c_slave, STRCH_WR);
i2c_slave_send_ack(i2c_slave);
}
if (irq_stat & (1 << TX_FIFO_EMPTY)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[TX_FIFO_EMPTY]);
i2c_slave->tx_count = read_tx_fifo_byte_count(i2c_slave);
i2c_slave_write_fifo(i2c_slave);
i2c_slave_clear_irq(i2c_slave, TX_FIFO_EMPTY);
}
if (irq_stat & (1 << GCA_DETECTED)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[GCA_DETECTED]);
i2c_slave_send_nack(i2c_slave);
i2c_slave_clear_irq(i2c_slave, GCA_DETECTED);
}
if (irq_stat & (1 << RESTART_DETECTED)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[RESTART_DETECTED]);
i2c_slave_send_ack(i2c_slave);
i2c_slave_clear_irq(i2c_slave, RESTART_DETECTED);
}
if (irq_stat & (1 << SMBALERT_ARA_DONE)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[SMBALERT_ARA_DONE]);
i2c_slave_clear_irq(i2c_slave, SMBALERT_ARA_DONE);
}
if (irq_stat & (1 << SMBALERT_LOST_ARB)) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev, "%s\n",
irq_log[SMBALERT_LOST_ARB]);
i2c_slave_clear_irq(i2c_slave, SMBALERT_LOST_ARB);
}
return IRQ_HANDLED;
}
/**
* i2c_slave_write: write data to fifo.
* @i2c_slave: Pointer to Main Structure.
* @buf: TX Data buffer.
* @count: TX Data count.
*
* This function will write data to TX FIFO.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_write(struct i2c_slave *i2c_slave, uint8_t *buf, size_t count)
{
if (!buf) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Invalid write buffer\n");
return -EINVAL;
}
if (!count) {
I2C_SLAVE_ERR(i2c_slave->ipcl, false, i2c_slave->dev,
"Write count is zero\n");
return -EINVAL;
}
i2c_slave->tx_msg_buf = buf;
i2c_slave->tx_count = count;
i2c_slave_write_fifo(i2c_slave);
return 0;
}
/**
* i2c_slave_read: read data from fifo.
* @i2c_slave: Pointer to Main Structure.
* @buf: RX Data buffer.
* @count: RX Data count.
*
* This function will read data from RX FIFO.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_read(struct i2c_slave *i2c_slave, uint8_t *buf, size_t count)
{
int i;
if (!buf) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Invalid Read buffer\n");
return -EINVAL;
}
if (count == 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, false, i2c_slave->dev,
"Read count is zero\n");
return -EINVAL;
}
if (count <= I2C_SMBUS_BYTE_DATA &&
i2c_slave->rx_count < count) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Data not available\n");
return -EINVAL;
}
if (count > I2C_SMBUS_BYTE_DATA)
count = i2c_slave->rx_count;
i2c_slave->rx_count -= count;
for (i = 0; i < count; i++)
buf[i] = i2c_slave->rx_msg_buf[i];
if (count <= I2C_SMBUS_BYTE_DATA) {
for (i = 0; i < i2c_slave->rx_count; i++)
i2c_slave->rx_msg_buf[i] =
i2c_slave->rx_msg_buf[i + count];
}
i2c_slave_read_fifo(i2c_slave);
return count;
}
/**
* i2c_slave_xfer: SMbus transfer function.
* @adap: I2C driver adapter
* @addr: Slave address
* @flags: i2c client flag
* @read_write: Read/Write flag
* @command: SMbus command code
* @protocol: Command type
* @data: Pointer to client data
*
* Based on read_write flag, it will call read
* write function.
*
* Return: 0 for success, Negative number for error condition.
*/
static int i2c_slave_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int protocol, union i2c_smbus_data *data)
{
struct i2c_slave *i2c_slave = i2c_get_adapdata(adap);
u8 buf[I2C_SMBUS_BLOCK_MAX];
int ret = 0, count, i;
/* Every open/close of i2c device node, i2c framework will set
* slave address variable to 0 in i2c client structure and
* client has to run IOCTL on every open/close opration.
* So, to avoid slave address to be set to zero, added
* check for non-zero slave address.
*/
if (addr && addr != i2c_slave->slave_addr) {
i2c_slave->slave_addr = addr;
writel_relaxed(addr, i2c_slave->base + I2C_S_DEVICE_ADDR);
}
if (read_write == I2C_SMBUS_READ) {
switch (protocol) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
count = i2c_slave_read(i2c_slave, buf, I2C_SLAVE_BYTE_DATA);
if (count == I2C_SMBUS_BYTE) {
data->byte = buf[0];
return 0;
}
ret = count;
break;
case I2C_SMBUS_WORD_DATA:
count = i2c_slave_read(i2c_slave, buf, I2C_SLAVE_WORD_DATA);
if (count == I2C_SMBUS_BYTE_DATA) {
data->word = (buf[0] | (buf[1] << 8));
return 0;
}
ret = count;
break;
case I2C_SMBUS_BLOCK_DATA:
count = i2c_slave_read(i2c_slave, buf, I2C_SMBUS_BLOCK_MAX);
if (count > 0) {
data->block[0] = count;
for (i = 0; i < count; i++)
data->block[i + 1] = buf[i];
return 0;
}
ret = count;
break;
default:
break;
}
} else if (read_write == I2C_SMBUS_WRITE) {
switch (protocol) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
buf[0] = data->byte;
ret = i2c_slave_write(i2c_slave, buf, I2C_SLAVE_BYTE_DATA);
break;
case I2C_SMBUS_WORD_DATA:
buf[0] = (uint8_t)(data->word & 0xFF);
buf[1] = (uint8_t)(data->word >> 8);
ret = i2c_slave_write(i2c_slave, buf, I2C_SLAVE_WORD_DATA);
break;
case I2C_SMBUS_BLOCK_DATA:
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
data->block[0] = I2C_SMBUS_BLOCK_MAX;
for (i = 0; i < data->block[0]; i++)
buf[i] = data->block[i + 1];
ret = i2c_slave_write(i2c_slave, buf, data->block[0]);
break;
default:
break;
}
}
return ret;
}
/**
* i2c_slave_enable_clk: Enable AHB & XO clock.
* @i2c_slave: Pointer to Main Structure.
*
* This function will enable AHB and XO clock.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_enable_clk(struct i2c_slave *i2c_slave)
{
int ret = 0;
i2c_slave->xo_clk = devm_clk_get(i2c_slave->dev, "sm_bus_xo_clk");
if (IS_ERR(i2c_slave->xo_clk)) {
ret = PTR_ERR(i2c_slave->xo_clk);
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Err getting XO clk %d\n", ret);
return ret;
}
i2c_slave->ahb_clk = devm_clk_get(i2c_slave->dev, "sm_bus_ahb_clk");
if (IS_ERR(i2c_slave->ahb_clk)) {
ret = PTR_ERR(i2c_slave->ahb_clk);
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Err getting AHB clk %d\n", ret);
return ret;
}
ret = clk_prepare_enable(i2c_slave->ahb_clk);
if (ret)
return ret;
ret = clk_prepare_enable(i2c_slave->xo_clk);
if (ret)
goto err_on_xo;
return 0;
err_on_xo:
clk_disable_unprepare(i2c_slave->ahb_clk);
return ret;
}
/**
* i2c_slave_icc_init: Enable ICB voting.
* @i2c_slave: Pointer to Main Structure.
*
* This function will enable ICB voting for i2c slave.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_icc_init(struct i2c_slave *i2c_slave)
{
int ret = 0;
i2c_slave->icc_path = devm_of_icc_get(i2c_slave->dev, "i2c-slave-config");
if (IS_ERR(i2c_slave->icc_path)) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"devm_of_icc_get failed: %d:\n", ret);
return -EINVAL;
}
i2c_slave->bw = APPS_PROC_TO_I2C_SLAVE_VOTE;
icc_set_bw(i2c_slave->icc_path, i2c_slave->bw, i2c_slave->bw);
ret = icc_enable(i2c_slave->icc_path);
if (ret) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"ICC enable failed err: %d\n", ret);
devres_free(&i2c_slave->icc_path);
return ret;
}
return 0;
}
/*
* i2c_slave_open: open function for dev node.
* @inode: Pointer to inode.
* @file: Pointer to file descriptor.
*
* This function will be called when we open the dev node
* from user-space application.
*
* Return: 0 for success.
*/
static int i2c_slave_open(struct inode *inode, struct file *file)
{
struct cdev *cdev = inode->i_cdev;
struct i2c_slave *i2c_slave = container_of(cdev, struct i2c_slave, cdev);
file->private_data = i2c_slave;
return 0;
}
/*
* i2c_slave_release: close function for dev node.
* @inode: pointer to inode.
* @file: Pointer to file descriptor.
*
* This function will be called when we close the dev node
* from user-space application.
*
* Return: 0 for success.
*/
static int i2c_slave_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
/**
* i2c_slave_poll: poll() syscall for I2C slave.
* @f: Pointer to the file structure.
* @wait: Pointer to Poll table.
*
* This function is used to poll on the I2C slave device.
* when userspace client do a poll() system call.
*
* Return: POLLIN if RX data available else 0.
*/
static __poll_t i2c_slave_poll(struct file *file, struct poll_table_struct *wait)
{
struct i2c_slave *i2c_slave = file->private_data;
__poll_t mask = 0;
poll_wait(file, &i2c_slave->readq, wait);
if (i2c_slave->rx_count > 0) {
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"%s: RX data available\n", __func__);
mask |= POLLIN;
}
return mask;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = i2c_slave_open,
.poll = i2c_slave_poll,
.release = i2c_slave_release,
};
/**
* i2c_slave_create_dev_node: Create dev node for poll
* @i2c_slave: Pointer to Main Structure.
*
* This function will create user-space dev node to support
* poll function on the I2C slave device.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_create_dev_node(struct i2c_slave *i2c_slave)
{
struct device *dev_ret;
struct class *i2c_slave_class;
dev_t i2c_slave_dev;
int ret = 0;
ret = alloc_chrdev_region(&i2c_slave_dev, 0, 1, I2C_SLAVE_DEV);
if (ret < 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"%s: failed in alloc_chrdev_region ret:%d\n", ret);
goto err_alloc;
}
cdev_init(&i2c_slave->cdev, &fops);
ret = cdev_add(&i2c_slave->cdev, i2c_slave_dev, 1);
if (ret < 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"%s: failed in cdev_add ret:%d\n", ret);
goto err_cdev_add;
}
i2c_slave_class = class_create(THIS_MODULE, I2C_SLAVE_DEV);
if (IS_ERR_OR_NULL(i2c_slave_class)) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"%s: failed in class_create:%d\n", ret);
ret = PTR_ERR(i2c_slave_class);
goto err_class_create;
}
dev_ret = device_create(i2c_slave_class, NULL, i2c_slave_dev, NULL, I2C_SLAVE_DEV);
if (IS_ERR_OR_NULL(dev_ret)) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"%s: failed in device_create:%d\n", ret);
ret = PTR_ERR(dev_ret);
goto err_device_create;
}
return ret;
err_device_create:
class_destroy(i2c_slave_class);
err_class_create:
cdev_del(&i2c_slave->cdev);
err_cdev_add:
unregister_chrdev_region(i2c_slave_dev, 1);
err_alloc:
return ret;
}
/**
* i2c_slave_func: To check supported i2c functionality.
* @adap: I2C driver adapter.
*
* This function will use to determine what the adapter supports.
*
* Return: 0 for success, negative number for error condition.
*/
static u32 i2c_slave_func(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_BYTE;
}
static const struct i2c_algorithm i2c_slave_algo = {
.smbus_xfer = i2c_slave_xfer,
.functionality = i2c_slave_func,
};
/**
* i2c_slave_probe: Driver Probe function.
* @pdev: Pointer to platform device structure.
*
* This function will performs pre-initialization tasks such as reading dtsi property,
* setting clock, IRQ request, initializing registers, allocating memory,
* and initializing registers for i2c slave.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_probe(struct platform_device *pdev)
{
struct i2c_slave *i2c_slave;
struct resource *res;
static const char adapName[10] = "I2C-slave";
char ipc_name[I2C_NAME_SIZE];
int ret = 0;
i2c_slave = devm_kzalloc(&pdev->dev,
sizeof(*i2c_slave), GFP_KERNEL);
if (!i2c_slave) {
ret = -ENOMEM;
goto err;
}
i2c_slave->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -EINVAL;
goto err;
}
i2c_slave->base = devm_ioremap_resource(i2c_slave->dev, res);
if (IS_ERR(i2c_slave->base)) {
ret = PTR_ERR(i2c_slave->base);
goto err;
}
scnprintf(ipc_name, I2C_NAME_SIZE, "%s", dev_name(i2c_slave->dev));
i2c_slave->ipcl = ipc_log_context_create(2, ipc_name, 0);
if (!i2c_slave->ipcl) {
dev_err(i2c_slave->dev, "Error: Failed to create IPC log file\n");
ret = -EINVAL;
goto err_ipc;
}
ret = i2c_slave_create_dev_node(i2c_slave);
if (ret < 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"failed to create dev node ret: %d:\n", ret);
goto err_ipc;
}
ret = i2c_slave_enable_clk(i2c_slave);
if (ret)
goto err_ipc;
i2c_slave->irq = platform_get_irq(pdev, 0);
if (i2c_slave->irq < 0) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"get_irq failed: %d:\n", i2c_slave->irq);
ret = i2c_slave->irq;
goto err_irq;
}
ret = devm_request_irq(i2c_slave->dev, i2c_slave->irq, i2c_slave_irq, 0,
"i2c_slave", i2c_slave);
if (ret) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Request_irq failed: %d: err: %d\n", i2c_slave->irq, ret);
goto err_irq;
}
ret = i2c_slave_icc_init(i2c_slave);
if (ret) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"ICC init failed ret: %d\n", ret);
goto err_icc;
}
i2c_slave->adap.algo = &i2c_slave_algo;
i2c_slave->rx_count = 0;
/* Enable IRQ */
i2c_slave_enable_irq(i2c_slave);
/* Set default slave address */
writel_relaxed(SLAVE_ADDR, i2c_slave->base + I2C_S_DEVICE_ADDR);
i2c_slave->slave_addr = SLAVE_ADDR;
/* Enable core */
writel_relaxed(CORE_EN, i2c_slave->base + I2C_S_CONFIG);
ret = strscpy(i2c_slave->adap.name, adapName, sizeof(i2c_slave->adap.name));
if (ret != strlen(adapName)) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Failed to set adapter name\n");
goto err_adap;
}
init_waitqueue_head(&i2c_slave->readq);
i2c_set_adapdata(&i2c_slave->adap, i2c_slave);
platform_set_drvdata(pdev, i2c_slave);
i2c_slave->adap.dev.parent = i2c_slave->dev;
i2c_slave->adap.dev.of_node = pdev->dev.of_node;
ret = i2c_add_adapter(&i2c_slave->adap);
if (ret) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"Add adapter failed, ret=%d\n", ret);
goto err_adap;
}
I2C_SLAVE_DBG(i2c_slave->ipcl, true, i2c_slave->dev,
"I2C Slave probed\n");
return 0;
err_adap:
icc_disable(i2c_slave->icc_path);
devres_free(&i2c_slave->icc_path);
err_icc:
disable_irq(i2c_slave->irq);
err_irq:
clk_disable_unprepare(i2c_slave->ahb_clk);
clk_disable_unprepare(i2c_slave->xo_clk);
err_ipc:
if (i2c_slave->ipcl)
ipc_log_context_destroy(i2c_slave->ipcl);
err:
return ret;
}
/**
* i2c_slave_remove: This function will release the resources.
* @pdev: Pointer to platform device structure.
*
* This function will release clocks, IRQ and other allocated resources for
* i2c slave.
*
* Return: 0 for success.
*/
static int i2c_slave_remove(struct platform_device *pdev)
{
struct i2c_slave *i2c_slave = platform_get_drvdata(pdev);
clk_disable_unprepare(i2c_slave->ahb_clk);
clk_disable_unprepare(i2c_slave->xo_clk);
disable_irq(i2c_slave->irq);
icc_disable(i2c_slave->icc_path);
devres_free(&i2c_slave->icc_path);
i2c_del_adapter(&i2c_slave->adap);
if (i2c_slave->ipcl)
ipc_log_context_destroy(i2c_slave->ipcl);
return 0;
}
/**
* i2c_slave_suspend: driver suspend function.
* @dev: Pointer to device structure.
*
* This function will put driver into suspend state by releasing
* icc, irq and core clock.
*
* Return: 0 for success.
*/
#ifdef I2C_SLAVE_SUSPEND_RESUME
static int i2c_slave_suspend(struct device *dev)
{
struct i2c_slave *i2c_slave = dev_get_drvdata(dev);
disable_irq(i2c_slave->irq);
icc_disable(i2c_slave->icc_path);
clk_disable_unprepare(i2c_slave->ahb_clk);
clk_disable_unprepare(i2c_slave->xo_clk);
I2C_SLAVE_DBG(i2c_slave->ipcl, false, i2c_slave->dev,
"%s\n", __func__);
return 0;
}
/**
* i2c_slave_resume: driver resume function.
* @dev: Pointer to device structure.
*
* This function will resume the driver by enabling icc, irq
* and core clock.
*
* Return: 0 for success, negative number for error condition.
*/
static int i2c_slave_resume(struct device *dev)
{
struct i2c_slave *i2c_slave = dev_get_drvdata(dev);
int ret = 0;
enable_irq(i2c_slave->irq);
ret = icc_enable(i2c_slave->icc_path);
if (ret) {
I2C_SLAVE_ERR(i2c_slave->ipcl, true, i2c_slave->dev,
"ICC enable failed err: %d\n", ret);
goto err_icc;
}
ret = clk_prepare_enable(i2c_slave->ahb_clk);
if (ret) {
I2C_SLAVE_DBG(i2c_slave->ipcl, true, i2c_slave->dev,
"%s: failing at ahb clk prepare enable ret=%d\n",
__func__, ret);
goto err_ahb_clk;
}
ret = clk_prepare_enable(i2c_slave->xo_clk);
if (ret) {
I2C_SLAVE_DBG(i2c_slave->ipcl, true, i2c_slave->dev,
"%s: failing at xo clk prepare enable ret=%d\n",
__func__, ret);
clk_disable_unprepare(i2c_slave->ahb_clk);
goto err_xo_clk;
}
I2C_SLAVE_DBG(i2c_slave->ipcl, true, i2c_slave->dev,
"%s:\n", __func__);
return 0;
err_xo_clk:
clk_disable_unprepare(i2c_slave->ahb_clk);
err_ahb_clk:
icc_disable(i2c_slave->icc_path);
err_icc:
return ret;
}
#else
static int i2c_slave_suspend(struct device *dev)
{
return 0;
}
static int i2c_slave_resume(struct device *dev)
{
return 0;
}
#endif
static const struct dev_pm_ops i2c_slave_pm_ops = {
.suspend = i2c_slave_suspend,
.resume = i2c_slave_resume,
};
static const struct of_device_id i2c_slave_dt_match[] = {
{.compatible = "qcom,i2c-slave" },
{ }
};
MODULE_DEVICE_TABLE(of, i2c_slave_dt_match);
static struct platform_driver i2c_slave_driver = {
.driver = {
.name = "i2c_slave",
.pm = &i2c_slave_pm_ops,
.of_match_table = i2c_slave_dt_match,
},
.probe = i2c_slave_probe,
.remove = i2c_slave_remove,
};
module_platform_driver(i2c_slave_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("i2c slave");