1、背景介绍
最近在调试集群处理平台,模块上使用了支持IPMI的BMC控制芯片。该芯片与ZYNQ通过I2C总线相连,上面跑IPMB协议。ZYNQ作机箱管理,对所有BMC进行控制,而BMC再控制本模块的负载上下电。
2、问题描述
ZYNQ与BMC通过I2C总线进行数据传输,按照VITA46.11规范,要求机箱管理既能做I2C的master,也能做i2c slave(此时BMC做master),于是要求ZYNQ能进行I2C主从模式切换。ZYNQ PS端的I2C控制器作为master很容易,之前也通过I2C控制器配置1848交换芯片,不会的是如何让I2C控制器运行在slave模式。问了一下AVNET代理,发现他们也没搞过。在Xilinx论坛上面发帖,老外给了一个他们ZYNQ作为i2c从的测试例子(见链接),但帮助不大,唯一的帮助就是让我相信I2C控制器肯定支持slave模式。在xilinx wiki(链接)上面可以看到新发布的驱动版本已经支持了I2C slave mode。
3、解决思路
对i2c-cadence.c驱动进行了代码分析,发现CONFIG_I2C_SLAVE的宏定义随处可见,其中最重要的莫过于下面这个地方。
Cdns_reg_slave和cdns_unreg_slave这两个函数从字面意义来看明显是对寄存器配置成slave或unslave模式的意思。可惜在linux内核代码中完全没找到调用这两个函数的地方。
由此看来,需要手动添加这两个函数,可以在i2c-dev.c中修改ioctl部分的代码,如下图所示,把原来i2c_slave部分的代码修改为调用reg_slave。
在reg_slave中增加了对i2c控制器进行复位的操作
在set mode函数中,直接往i2c控制器写值,设置control寄存器、设置i2c地址、启用收数中断。
当i2c控制器收到数之后,会调用上面函数,根据模式进入不同的中断处理函数。
完成这些操作后,编译得到内核,在应用中可以通过ioctl设置i2c的从模式了。
在i2c驱动的收数函数中增加了部分打印
程序执行打印如下。
4、拓展
实现了i2c从模式后,就可以进行主从模式切换了。切换需要使用unreg_slave,再次修改i2c-dev.c
在unreg_slave函数中设置了i2c的模式为主模式
需要注意的是,从模式切换到主模式时需要判断总线是否被占用,这里用一个函数加以判断
在应用中调用下面函数即可进行切换
5、总结
I2c算是比较简单的驱动,但是调试的过程中遇到一些问题靠软件是查不出来的,需要硬件抓信号,如果硬件有问题,软件再怎么努力也没用。所以以后遇到类似的情况,当软件再也查不出问题时,一定要让硬件查一查,否则必然是白白浪费时间。
6、相关代码
i2c-cadence.c
/*
* I2C bus driver for the Cadence I2C controller.
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any
* later version.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/i2c-dev.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
/* Register offsets for the I2C device. */
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
#define CDNS_I2C_SR_OFFSET 0x04 /* Status Register, RO */
#define CDNS_I2C_ADDR_OFFSET 0x08 /* I2C Address Register, RW */
#define CDNS_I2C_DATA_OFFSET 0x0C /* I2C Data Register, RW */
#define CDNS_I2C_ISR_OFFSET 0x10 /* IRQ Status Register, RW */
#define CDNS_I2C_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */
#define CDNS_I2C_SLV_PAUSE_OFFSET 0x18 /* Transfer Size Register, RW */
#define CDNS_I2C_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */
#define CDNS_I2C_IMR_OFFSET 0x20 /* IRQ Mask Register, RO */
#define CDNS_I2C_IER_OFFSET 0x24 /* IRQ Enable Register, WO */
#define CDNS_I2C_IDR_OFFSET 0x28 /* IRQ Disable Register, WO */
/* Control Register Bit mask definitions */
#define CDNS_I2C_CR_SLVMON BIT(5) /* Slave monitor mode bit */
#define CDNS_I2C_CR_HOLD BIT(4) /* Hold Bus bit */
#define CDNS_I2C_CR_ACK_EN BIT(3)
#define CDNS_I2C_CR_NEA BIT(2)
#define CDNS_I2C_CR_MS BIT(1)
/* Read or Write Master transfer 0 = Transmitter, 1 = Receiver */
#define CDNS_I2C_CR_RW BIT(0)
/* 1 = Auto init FIFO to zeroes */
#define CDNS_I2C_CR_CLR_FIFO BIT(6)
#define CDNS_I2C_CR_DIVA_SHIFT 14
#define CDNS_I2C_CR_DIVA_MASK (3 << CDNS_I2C_CR_DIVA_SHIFT)
#define CDNS_I2C_CR_DIVB_SHIFT 8
#define CDNS_I2C_CR_DIVB_MASK (0x3f << CDNS_I2C_CR_DIVB_SHIFT)
#define CDNS_I2C_CR_SLAVE_EN_MASK (CDNS_I2C_CR_CLR_FIFO | \
CDNS_I2C_CR_NEA | \
CDNS_I2C_CR_ACK_EN | \
CDNS_I2C_CR_MS)
/* Status Register Bit mask definitions */
#define CDNS_I2C_SR_BA BIT(8)
#define CDNS_I2C_SR_TXDV BIT(6)
#define CDNS_I2C_SR_RXDV BIT(5)
#define CDNS_I2C_SR_RXRW BIT(3)
/*
* I2C Address Register Bit mask definitions
* Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0]
* bits. A write access to this register always initiates a transfer if the I2C
* is in master mode.
*/
#define CDNS_I2C_ADDR_MASK 0x000003FF /* I2C Address Mask */
/*
* I2C Interrupt Registers Bit mask definitions
* All the four interrupt registers (Status/Mask/Enable/Disable) have the same
* bit definitions.
*/
#define CDNS_I2C_IXR_ARB_LOST BIT(9)
#define CDNS_I2C_IXR_RX_UNF BIT(7)
#define CDNS_I2C_IXR_TX_OVF BIT(6)
#define CDNS_I2C_IXR_RX_OVF BIT(5)
#define CDNS_I2C_IXR_SLV_RDY BIT(4)
#define CDNS_I2C_IXR_TO BIT(3)
#define CDNS_I2C_IXR_NACK BIT(2)
#define CDNS_I2C_IXR_DATA BIT(1)
#define CDNS_I2C_IXR_COMP BIT(0)
#define CDNS_I2C_IXR_ALL_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
CDNS_I2C_IXR_RX_UNF | \
CDNS_I2C_IXR_TX_OVF | \
CDNS_I2C_IXR_RX_OVF | \
CDNS_I2C_IXR_SLV_RDY | \
CDNS_I2C_IXR_TO | \
CDNS_I2C_IXR_NACK | \
CDNS_I2C_IXR_DATA | \
CDNS_I2C_IXR_COMP)
#define CDNS_I2C_IXR_ERR_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
CDNS_I2C_IXR_RX_UNF | \
CDNS_I2C_IXR_TX_OVF | \
CDNS_I2C_IXR_RX_OVF | \
CDNS_I2C_IXR_NACK)
#define CDNS_I2C_ENABLED_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
CDNS_I2C_IXR_RX_UNF | \
CDNS_I2C_IXR_TX_OVF | \
CDNS_I2C_IXR_RX_OVF | \
CDNS_I2C_IXR_NACK | \
CDNS_I2C_IXR_DATA | \
CDNS_I2C_IXR_COMP)
#define CDNS_I2C_IXR_SLAVE_INTR_MASK (CDNS_I2C_IXR_RX_UNF | \
CDNS_I2C_IXR_TX_OVF | \
CDNS_I2C_IXR_RX_OVF | \
CDNS_I2C_IXR_TO | \
CDNS_I2C_IXR_NACK | \
CDNS_I2C_IXR_DATA | \
CDNS_I2C_IXR_COMP)
#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
/* timeout for pm runtime autosuspend */
#define CNDS_I2C_PM_TIMEOUT 1000 /* ms */
#define CDNS_I2C_FIFO_DEPTH 16
/* FIFO depth at which the DATA interrupt occurs */
#define CDNS_I2C_DATA_INTR_DEPTH (CDNS_I2C_FIFO_DEPTH - 2)
#define CDNS_I2C_MAX_TRANSFER_SIZE 255
/* Transfer size in multiples of data interrupt depth */
#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3)
#define DRIVER_NAME "cdns-i2c"
#define CDNS_I2C_SPEED_MAX 400000
#define CDNS_I2C_SPEED_DEFAULT 100000
#define CDNS_I2C_DIVA_MAX 4
#define CDNS_I2C_DIVB_MAX 64
#define CDNS_I2C_TIMEOUT_MAX 0xFF
#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/**
* enum cdns_i2c_mode - I2C Controller current operating mode
*
* @CDNS_I2C_MODE_SLAVE: I2C controller operating in slave mode
* @CDNS_I2C_MODE_MASTER: I2C Controller operating in master mode
*/
enum cdns_i2c_mode {
CDNS_I2C_MODE_SLAVE,
CDNS_I2C_MODE_MASTER,
};
/**
* enum cdns_i2c_slave_mode - Slave state when I2C is operating in slave mode
*
* @CDNS_I2C_SLAVE_STATE_IDLE: I2C slave idle
* @CDNS_I2C_SLAVE_STATE_SEND: I2C slave sending data to master
* @CDNS_I2C_SLAVE_STATE_RECV: I2C slave receiving data from master
*/
enum cdns_i2c_slave_state {
CDNS_I2C_SLAVE_STATE_IDLE,
CDNS_I2C_SLAVE_STATE_SEND,
CDNS_I2C_SLAVE_STATE_RECV,
};
#endif
/**
* struct cdns_i2c - I2C device private data structure
*
* @dev: Pointer to device structure
* @membase: Base address of the I2C device
* @adap: I2C adapter instance
* @p_msg: Message pointer
* @err_status: Error status in Interrupt Status Register
* @xfer_done: Transfer complete status
* @p_send_buf: Pointer to transmit buffer
* @p_recv_buf: Pointer to receive buffer
* @send_count: Number of bytes still expected to send
* @recv_count: Number of bytes still expected to receive
* @curr_recv_count: Number of bytes to be received in current transfer
* @irq: IRQ number
* @input_clk: Input clock to I2C controller
* @i2c_clk: Maximum I2C clock speed
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
* @clk: Pointer to struct clk
* @clk_rate_change_nb: Notifier block for clock rate changes
* @quirks: flag for broken hold bit usage in r1p10
* @ctrl_reg: Cached value of the control register.
* @rinfo: Structure holding recovery information.
* @pinctrl: Pin control state holder.
* @pinctrl_pins_default: Default pin control state.
* @pinctrl_pins_gpio: GPIO pin control state.
* @slave: Registered slave instance.
* @dev_mode: I2C operating role(master/slave).
* @slave_state: I2C Slave state(idle/read/write).
*/
struct cdns_i2c {
struct device *dev;
void __iomem *membase;
struct i2c_adapter adap;
struct i2c_msg *p_msg;
int err_status;
struct completion xfer_done;
unsigned char *p_send_buf;
unsigned char *p_recv_buf;
unsigned int send_count;
unsigned int recv_count;
unsigned int curr_recv_count;
int irq;
unsigned long input_clk;
unsigned int i2c_clk;
unsigned int bus_hold_flag;
struct clk *clk;
struct notifier_block clk_rate_change_nb;
u32 quirks;
u32 ctrl_reg;
struct i2c_bus_recovery_info rinfo;
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_pins_default;
struct pinctrl_state *pinctrl_pins_gpio;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *slave;
enum cdns_i2c_mode dev_mode;
enum cdns_i2c_slave_state slave_state;
#endif
};
struct cdns_platform_data {
u32 quirks;
};
#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \
clk_rate_change_nb)
/* ############ lan add 2024.2.27 ############ */
typedef struct {
u16 slave_addr;
u16 first_write;
u16 buffer_idx;
u8 *buffer;
}mySlave_data;
u8 myI2cBuffer[8192] = {0};
mySlave_data mySlave = {0, 0, 0, "0"};
static int my_slave_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val)
{
// struct mySlave_data *mySlave = i2c_get_clientdata(client);
if (mySlave.buffer == NULL)
{
printk("###i2c-dev debug###i2c slave my_slave_cb buffer null\n");
return 1;
}
if (mySlave.buffer_idx > 8192)
{
printk("###i2c-dev debug###i2c slave my_slave_cb buffer too big\n");
return 1;
}
// printk("###i2c-dev debug###i2c slave my_slave_cb, event = %d\n",event);
switch(event) {
case I2C_SLAVE_READ_REQUESTED:
// *val = mySlave.buffer[mySlave.buffer_idx];
// *val = mySlave.buffer_idx;
mySlave.buffer_idx = 0;
// *val = mySlave.buffer[mySlave.buffer_idx++];
*val = myI2cBuffer[mySlave.buffer_idx++];
break;
case I2C_SLAVE_WRITE_REQUESTED:
// mySlave.first_write = 1;
mySlave.buffer_idx = 0;
/* The previous byte made it to the bus, get next one */
break;
case I2C_SLAVE_READ_PROCESSED:
// mySlave.buffer_idx++;
// *val = mySlave.buffer[mySlave.buffer_idx++];
*val = myI2cBuffer[mySlave.buffer_idx++];
break;
case I2C_SLAVE_WRITE_RECEIVED:
// mySlave.buffer[mySlave.buffer_idx++] = *val;
myI2cBuffer[mySlave.buffer_idx++] = *val;
break;
case I2C_SLAVE_STOP:
break;
default:
break;
}
return 0;
}
/* ############ lan add 2024.2.27 ############ */
/**
* cdns_i2c_clear_bus_hold - Clear bus hold bit
* @id: Pointer to driver data struct
*
* Helper to clear the controller's bus hold bit.
*/
static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
{
u32 reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
if (reg & CDNS_I2C_CR_HOLD)
cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
}
static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround)
{
return (hold_wrkaround &&
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1));
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static void cdns_i2c_set_mode(enum cdns_i2c_mode mode, struct cdns_i2c *id)
{
u32 reg;
/* Disable all interrupts */
cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
/* Clear FIFO and transfer size */
cdns_i2c_writereg(CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
/* Update device mode and state */
id->dev_mode = mode;
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
switch (mode) {
case CDNS_I2C_MODE_MASTER:
/* Enable i2c master */
cdns_i2c_writereg(id->ctrl_reg, CDNS_I2C_CR_OFFSET);
break;
case CDNS_I2C_MODE_SLAVE:
/* Enable i2c slave */
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
reg &= ~CDNS_I2C_CR_SLAVE_EN_MASK;
// cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
cdns_i2c_writereg(0x4C, CDNS_I2C_CR_OFFSET);
/* Setting slave address */
cdns_i2c_writereg(id->slave->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET);
mySlave.slave_addr = id->slave->addr & CDNS_I2C_ADDR_MASK;
/* Enable slave send/receive interrupts */
cdns_i2c_writereg(CDNS_I2C_IXR_SLAVE_INTR_MASK,
CDNS_I2C_IER_OFFSET);
break;
}
}
static void cdns_i2c_slave_rcv_data(struct cdns_i2c *id)
{
u8 bytes;
unsigned char data;
int num = 0;
/* Prepare backend for data reception */
if (id->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
id->slave_state = CDNS_I2C_SLAVE_STATE_RECV;
// i2c_slave_event(id->slave, I2C_SLAVE_WRITE_REQUESTED, NULL);
my_slave_cb(id->slave, I2C_SLAVE_WRITE_REQUESTED, NULL);
}
/* Fetch number of bytes to receive */
bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
// printk("###i2c-dev debug###i2c slave recv byte is 0x%x\n", bytes);
/****************** offset set 0 ******************/
/* Read data and send to backend */
while (bytes--) {
data = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
// i2c_slave_event(id->slave, I2C_SLAVE_WRITE_RECEIVED, &data);
my_slave_cb(id->slave, I2C_SLAVE_WRITE_RECEIVED, &data);
}
}
static void cdns_i2c_slave_send_data(struct cdns_i2c *id)
{
u8 data;
/* Prepare backend for data transmission */
if (id->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
id->slave_state = CDNS_I2C_SLAVE_STATE_SEND;
// i2c_slave_event(id->slave, I2C_SLAVE_READ_REQUESTED, &data);
my_slave_cb(id->slave, I2C_SLAVE_READ_REQUESTED, &data);
} else {
// i2c_slave_event(id->slave, I2C_SLAVE_READ_PROCESSED, &data);
my_slave_cb(id->slave, I2C_SLAVE_READ_PROCESSED, &data);
}
// printk("###i2c-dev debug###i2c slave send data is 0x%x\n", data);
/* Send data over bus */
cdns_i2c_writereg(data, CDNS_I2C_DATA_OFFSET);
}
/**
* cdns_i2c_slave_isr - Interrupt handler for the I2C device in slave role
* @ptr: Pointer to I2C device private data
*
* This function handles the data interrupt and transfer complete interrupt of
* the I2C device in slave role.
*
* Return: IRQ_HANDLED always
*/
static irqreturn_t cdns_i2c_slave_isr(void *ptr)
{
struct cdns_i2c *id = ptr;
unsigned int isr_status, i2c_status;
/* Fetch the interrupt status */
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
// printk("###i2c-dev debug###i2c slave 1\n");
/* Ignore masked interrupts */
isr_status &= ~cdns_i2c_readreg(CDNS_I2C_IMR_OFFSET);
// printk("###i2c-dev debug###i2c slave 2\n");
/* Fetch transfer mode (send/receive) */
i2c_status = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
// printk("###i2c-dev debug###i2c slave 3\n");
/* Handle data send/receive */
if (i2c_status & CDNS_I2C_SR_RXRW) {
// printk("###i2c-dev debug###i2c slave 4 isr_status 0x%x\n", isr_status);
/* Send data to master */
if (isr_status & CDNS_I2C_IXR_DATA)
cdns_i2c_slave_send_data(id);
if (isr_status & CDNS_I2C_IXR_COMP) {
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
// i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
}
} else {
// printk("###i2c-dev debug###i2c slave 5 isr_status 0x%x\n", isr_status);
/* Receive data from master */
if (isr_status & CDNS_I2C_IXR_DATA)
cdns_i2c_slave_rcv_data(id);
// printk("###i2c-dev debug###i2c slave 6 isr_status 0x%x\n", isr_status);
if (isr_status & CDNS_I2C_IXR_COMP) {
cdns_i2c_slave_rcv_data(id);
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
// i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
}
}
// printk("###i2c-dev debug###i2c slave 7 isr_status 0x%x\n", isr_status);
/* Master indicated xfer stop or fifo underflow/overflow */
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_RX_OVF |
CDNS_I2C_IXR_RX_UNF | CDNS_I2C_IXR_TX_OVF)) {
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
// i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
cdns_i2c_writereg(CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
}
// printk("###i2c-dev debug###i2c slave 8 slave_status 0x%x\n", id->slave_state);
return IRQ_HANDLED;
}
#endif
/**
* cdns_i2c_master_isr - Interrupt handler for the I2C device in master role
* @ptr: Pointer to I2C device private data
*
* This function handles the data interrupt, transfer complete interrupt and
* the error interrupts of the I2C device in master role.
*
* Return: IRQ_HANDLED always
*/
static irqreturn_t cdns_i2c_master_isr(void *ptr)
{
unsigned int isr_status, avail_bytes, updatetx;
unsigned int bytes_to_send;
bool hold_quirk;
struct cdns_i2c *id = ptr;
/* Signal completion only after everything is updated */
int done_flag = 0;
irqreturn_t status = IRQ_NONE;
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
/* Handling nack and arbitration lost interrupt */
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
done_flag = 1;
status = IRQ_HANDLED;
}
/*
* Check if transfer size register needs to be updated again for a
* large data receive operation.
*/
updatetx = 0;
if (id->recv_count > id->curr_recv_count)
updatetx = 1;
hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
/* When receiving, handle data interrupt and completion interrupt */
if (id->p_recv_buf &&
((isr_status & CDNS_I2C_IXR_COMP) ||
(isr_status & CDNS_I2C_IXR_DATA))) {
/* Read data if receive data valid is set */
while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
CDNS_I2C_SR_RXDV) {
/*
* Clear hold bit that was set for FIFO control if
* RX data left is less than FIFO depth, unless
* repeated start is selected.
*/
if ((id->recv_count < CDNS_I2C_FIFO_DEPTH) &&
!id->bus_hold_flag)
cdns_i2c_clear_bus_hold(id);
*(id->p_recv_buf)++ =
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
id->recv_count--;
id->curr_recv_count--;
if (cdns_is_holdquirk(id, hold_quirk))
break;
}
/*
* The controller sends NACK to the slave when transfer size
* register reaches zero without considering the HOLD bit.
* This workaround is implemented for large data transfers to
* maintain transfer size non-zero while performing a large
* receive operation.
*/
if (cdns_is_holdquirk(id, hold_quirk)) {
/* wait while fifo is full */
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
(id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
;
/*
* Check number of bytes to be received against maximum
* transfer size and update register accordingly.
*/
if (((int)(id->recv_count) - CDNS_I2C_FIFO_DEPTH) >
CDNS_I2C_TRANSFER_SIZE) {
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
CDNS_I2C_FIFO_DEPTH;
} else {
cdns_i2c_writereg(id->recv_count -
CDNS_I2C_FIFO_DEPTH,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = id->recv_count;
}
} else if (id->recv_count && !hold_quirk &&
!id->curr_recv_count) {
/* Set the slave address in address register*/
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
} else {
cdns_i2c_writereg(id->recv_count,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = id->recv_count;
}
}
/* Clear hold (if not repeated start) and signal completion */
if ((isr_status & CDNS_I2C_IXR_COMP) && !id->recv_count) {
if (!id->bus_hold_flag)
cdns_i2c_clear_bus_hold(id);
done_flag = 1;
}
status = IRQ_HANDLED;
}
/* When sending, handle transfer complete interrupt */
if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) {
/*
* If there is more data to be sent, calculate the
* space available in FIFO and fill with that many bytes.
*/
if (id->send_count) {
avail_bytes = CDNS_I2C_FIFO_DEPTH -
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
if (id->send_count > avail_bytes)
bytes_to_send = avail_bytes;
else
bytes_to_send = id->send_count;
while (bytes_to_send--) {
cdns_i2c_writereg(
(*(id->p_send_buf)++),
CDNS_I2C_DATA_OFFSET);
id->send_count--;
}
} else {
/*
* Signal the completion of transaction and
* clear the hold bus bit if there are no
* further messages to be processed.
*/
done_flag = 1;
}
if (!id->send_count && !id->bus_hold_flag)
cdns_i2c_clear_bus_hold(id);
status = IRQ_HANDLED;
}
/* Handling Slave monitor mode interrupt */
if (isr_status & CDNS_I2C_IXR_SLV_RDY) {
unsigned int ctrl_reg;
/* Read control register */
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
/* Disable slave monitor mode */
ctrl_reg &= ~CDNS_I2C_CR_SLVMON;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
/* Clear interrupt flag for slvmon mode */
cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IDR_OFFSET);
done_flag = 1;
status = IRQ_HANDLED;
}
/* Update the status for errors */
id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
if (id->err_status)
status = IRQ_HANDLED;
if (done_flag)
complete(&id->xfer_done);
return status;
}
/**
* cdns_i2c_isr - Interrupt handler for the I2C device
* @irq: irq number for the I2C device
* @ptr: void pointer to cdns_i2c structure
*
* This function passes the control to slave/master based on current role of
* i2c controller.
*
* Return: IRQ_HANDLED always
*/
static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
{
#if IS_ENABLED(CONFIG_I2C_SLAVE)
struct cdns_i2c *id = ptr;
switch (id->dev_mode) {
case CDNS_I2C_MODE_SLAVE:
dev_dbg(&id->adap.dev, "slave interrupt\n");
// printk("###i2c-dev debug###i2c slave interrupt\n");
cdns_i2c_slave_isr(ptr);
break;
case CDNS_I2C_MODE_MASTER:
dev_dbg(&id->adap.dev, "master interrupt\n");
cdns_i2c_master_isr(ptr);
break;
default:
dev_dbg(&id->adap.dev, "undefined interrupt\n");
break;
}
#else
cdns_i2c_master_isr(ptr);
#endif
return IRQ_HANDLED;
}
/**
* cdns_i2c_mrecv - Prepare and start a master receive operation
* @id: pointer to the i2c device structure
*/
static void cdns_i2c_mrecv(struct cdns_i2c *id)
{
unsigned int ctrl_reg;
unsigned int isr_status;
id->p_recv_buf = id->p_msg->buf;
id->recv_count = id->p_msg->len;
/* Put the controller in master receive mode and clear the FIFO */
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;
if (id->p_msg->flags & I2C_M_RECV_LEN)
id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
id->curr_recv_count = id->recv_count;
/*
* Check for the message size against FIFO depth and set the
* 'hold bus' bit if it is greater than FIFO depth.
*/
if (id->recv_count > CDNS_I2C_FIFO_DEPTH)
ctrl_reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
/* Clear the interrupts in interrupt status register */
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
/*
* The no. of bytes to receive is checked against the limit of
* max transfer size. Set transfer size register with no of bytes
* receive if it is less than transfer size and transfer size if
* it is more. Enable the interrupts.
*/
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
} else {
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
}
/* Set the slave address in address register - triggers operation */
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
/* Clear the bus hold flag if bytes to receive is less than FIFO size */
if (!id->bus_hold_flag &&
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
cdns_i2c_clear_bus_hold(id);
}
/**
* cdns_i2c_msend - Prepare and start a master send operation
* @id: pointer to the i2c device
*/
static void cdns_i2c_msend(struct cdns_i2c *id)
{
unsigned int avail_bytes;
unsigned int bytes_to_send;
unsigned int ctrl_reg;
unsigned int isr_status;
id->p_recv_buf = NULL;
id->p_send_buf = id->p_msg->buf;
id->send_count = id->p_msg->len;
/* Set the controller in Master transmit mode and clear the FIFO. */
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
ctrl_reg &= ~CDNS_I2C_CR_RW;
ctrl_reg |= CDNS_I2C_CR_CLR_FIFO;
/*
* Check for the message size against FIFO depth and set the
* 'hold bus' bit if it is greater than FIFO depth.
*/
if (id->send_count > CDNS_I2C_FIFO_DEPTH)
ctrl_reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
/* Clear the interrupts in interrupt status register. */
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
/*
* Calculate the space available in FIFO. Check the message length
* against the space available, and fill the FIFO accordingly.
* Enable the interrupts.
*/
avail_bytes = CDNS_I2C_FIFO_DEPTH -
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
if (id->send_count > avail_bytes)
bytes_to_send = avail_bytes;
else
bytes_to_send = id->send_count;
while (bytes_to_send--) {
cdns_i2c_writereg((*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET);
id->send_count--;
}
/*
* Clear the bus hold flag if there is no more data
* and if it is the last message.
*/
if (!id->bus_hold_flag && !id->send_count)
cdns_i2c_clear_bus_hold(id);
/* Set the slave address in address register - triggers operation. */
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
}
/**
* cdns_i2c_slvmon - Handling Slav monitor mode feature
* @id: pointer to the i2c device
*/
static void cdns_i2c_slvmon(struct cdns_i2c *id)
{
unsigned int ctrl_reg;
unsigned int isr_status;
id->p_recv_buf = NULL;
id->p_send_buf = id->p_msg->buf;
id->send_count = id->p_msg->len;
/* Clear the interrupts in interrupt status register. */
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
/* Enable slvmon control reg */
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
ctrl_reg |= CDNS_I2C_CR_MS | CDNS_I2C_CR_NEA | CDNS_I2C_CR_SLVMON
| CDNS_I2C_CR_CLR_FIFO;
ctrl_reg &= ~(CDNS_I2C_CR_RW);
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
/* Initialize slvmon reg */
cdns_i2c_writereg(0xF, CDNS_I2C_SLV_PAUSE_OFFSET);
/* Set the slave address to start the slave address transmission */
cdns_i2c_writereg(id->p_msg->addr, CDNS_I2C_ADDR_OFFSET);
/* Setup slvmon interrupt flag */
cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IER_OFFSET);
}
/**
* cdns_i2c_master_reset - Reset the interface
* @adap: pointer to the i2c adapter driver instance
*
* This function cleanup the fifos, clear the hold bit and status
* and disable the interrupts.
*/
static void cdns_i2c_master_reset(struct i2c_adapter *adap)
{
struct cdns_i2c *id = adap->algo_data;
u32 regval;
/* Disable the interrupts */
cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
/* Clear the hold bit and fifos */
regval = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
regval &= ~(CDNS_I2C_CR_HOLD | CDNS_I2C_CR_SLVMON);
regval |= CDNS_I2C_CR_CLR_FIFO;
cdns_i2c_writereg(regval, CDNS_I2C_CR_OFFSET);
/* Update the transfercount register to zero */
cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
/* Clear the interrupt status register */
regval = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(regval, CDNS_I2C_ISR_OFFSET);
/* Clear the status register */
regval = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
cdns_i2c_writereg(regval, CDNS_I2C_SR_OFFSET);
}
static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
struct i2c_adapter *adap)
{
unsigned long time_left;
u32 reg;
id->p_msg = msg;
id->err_status = 0;
reinit_completion(&id->xfer_done);
/* Check for the TEN Bit mode on each msg */
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
if (msg->flags & I2C_M_TEN) {
if (reg & CDNS_I2C_CR_NEA)
cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA,
CDNS_I2C_CR_OFFSET);
} else {
if (!(reg & CDNS_I2C_CR_NEA))
cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA,
CDNS_I2C_CR_OFFSET);
}
/* Check for zero length - Slave monitor mode */
if (msg->len == 0)
cdns_i2c_slvmon(id);
/* Check for the R/W flag on each msg */
else if (msg->flags & I2C_M_RD)
cdns_i2c_mrecv(id);
else
cdns_i2c_msend(id);
/* Wait for the signal of completion */
time_left = wait_for_completion_timeout(&id->xfer_done, adap->timeout);
if (time_left == 0) {
i2c_recover_bus(adap);
cdns_i2c_master_reset(adap);
dev_err(id->adap.dev.parent,
"timeout waiting on completion\n");
return -ETIMEDOUT;
}
cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK,
CDNS_I2C_IDR_OFFSET);
/* If it is bus arbitration error, try again */
if (id->err_status & CDNS_I2C_IXR_ARB_LOST)
return -EAGAIN;
return 0;
}
/**
* cdns_i2c_master_xfer - The main i2c transfer function
* @adap: pointer to the i2c adapter driver instance
* @msgs: pointer to the i2c message structure
* @num: the number of messages to transfer
*
* Initiates the send/recv activity based on the transfer message received.
*
* Return: number of msgs processed on success, negative error otherwise
*/
static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
int ret, count;
u32 reg;
struct cdns_i2c *id = adap->algo_data;
bool hold_quirk;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
bool change_role = false;
#endif
ret = pm_runtime_get_sync(id->dev);
if (ret < 0)
return ret;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* Check i2c operating mode and switch if possible */
if (id->dev_mode == CDNS_I2C_MODE_SLAVE) {
if (id->slave_state != CDNS_I2C_SLAVE_STATE_IDLE)
return -EAGAIN;
/* Set mode to master */
cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, id);
/* Mark flag to change role once xfer is completed */
change_role = true;
}
#endif
/* Check if the bus is free */
if (msgs->len)
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) {
ret = -EAGAIN;
goto out;
}
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
/*
* Set the flag to one when multiple messages are to be
* processed with a repeated start.
*/
if (num > 1) {
/*
* This controller does not give completion interrupt after a
* master receive message if HOLD bit is set (repeated start),
* resulting in SW timeout. Hence, if a receive message is
* followed by any other message, an error is returned
* indicating that this sequence is not supported.
*/
for (count = 0; (count < num - 1 && hold_quirk); count++) {
if (msgs[count].flags & I2C_M_RD) {
dev_warn(adap->dev.parent,
"Can't do repeated start after a receive message\n");
ret = -EOPNOTSUPP;
goto out;
}
}
id->bus_hold_flag = 1;
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
} else {
id->bus_hold_flag = 0;
}
/* Process the msg one by one */
for (count = 0; count < num; count++, msgs++) {
if (count == (num - 1))
id->bus_hold_flag = 0;
ret = cdns_i2c_process_msg(id, msgs, adap);
if (ret)
goto out;
/* Report the other error interrupts to application */
if (id->err_status) {
cdns_i2c_master_reset(adap);
if (id->err_status & CDNS_I2C_IXR_NACK) {
ret = -ENXIO;
goto out;
}
ret = -EIO;
goto out;
}
}
ret = num;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* Switch i2c mode to slave */
if (change_role)
cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, id);
#endif
out:
pm_runtime_mark_last_busy(id->dev);
pm_runtime_put_autosuspend(id->dev);
return ret;
}
/**
* cdns_i2c_func - Returns the supported features of the I2C driver
* @adap: pointer to the i2c adapter structure
*
* Return: 32 bit value, each bit corresponding to a feature
*/
static u32 cdns_i2c_func(struct i2c_adapter *adap)
{
u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
I2C_FUNC_SMBUS_BLOCK_DATA;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
func |= I2C_FUNC_SLAVE;
#endif
return func;
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
static int cdns_reg_slave(struct i2c_client *slave)
{
int ret;
struct cdns_i2c *id = container_of(slave->adapter, struct cdns_i2c,
adap);
if (id->slave)
return -EBUSY;
if (slave->flags & I2C_CLIENT_TEN)
return -EAFNOSUPPORT;
ret = pm_runtime_get_sync(id->dev);
if (ret < 0)
return ret;
/* Store slave information */
id->slave = slave;
/* reset i2c controller */
cdns_i2c_writereg(0, CDNS_I2C_CR_OFFSET);
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
/* Enable I2C slave */
cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, id);
/****************** lanj add 2024.3.4 ******************/
mySlave.buffer = myI2cBuffer;
/************************* end *************************/
return 0;
}
static int cdns_i2c_bus_busy(struct cdns_i2c *id)
{
u32 StatusReg;
int Status;
StatusReg = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
if ((StatusReg & CDNS_I2C_SR_BA) != 0x0U) {
Status = 0;
} else {
Status = -1;
}
return 0;
}
static int cdns_unreg_slave(struct i2c_client *slave)
{
#if 0
struct cdns_i2c *id = container_of(slave->adapter, struct cdns_i2c,
adap);
pm_runtime_put(id->dev);
/* Remove slave information */
id->slave = NULL;
/* Enable I2C master */
cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, id);
#else
struct cdns_i2c *id = container_of(slave->adapter, struct cdns_i2c,
adap);
int ret;
pm_runtime_put(id->dev);
/* Remove slave information */
id->slave = NULL;
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
ret = cdns_i2c_bus_busy(id);
if (ret == 0) {
/* Enable I2C master */
cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, id);
return 0;
}
else {
printk("waring:i2c bus is busy\n");
return -1;
}
#endif
return 0;
}
static int cdns_reg_slave_write(struct i2c_client *slave, unsigned long arg)
{
unsigned long ret;
// printk("###i2c-dev debug###i2c slave write\n");
#if 1
mySlave_data *mySlave1 = NULL;
u8 *buffer;
mySlave1 = kmalloc(sizeof(mySlave_data), GFP_KERNEL);
if (mySlave1 == NULL)
{
printk("cdns_reg_slave_read kmalloc mySlave1 failed\n");
return -EFAULT;
}
ret = copy_from_user(mySlave1, (mySlave_data __user *)arg, sizeof(mySlave_data));
if (ret)
{
kfree(mySlave1);
printk("copy from user failed, ret = %d\n", ret);
return -EFAULT;
}
if (mySlave1->buffer_idx > 8192)
{
mySlave1->buffer_idx = 8192;
}
ret = copy_from_user(myI2cBuffer, mySlave1->buffer, mySlave1->buffer_idx);
if (ret)
{
kfree(mySlave1);
printk("copy from user failed, ret = %d\n", ret);
return -EFAULT;
}
kfree(mySlave1);
#else
ret = copy_from_user(&mySlave, (mySlave_data __user *)arg, sizeof(mySlave_data));
if (ret)
{
printk("copy from user failed, ret = %d\n", ret);
return -EFAULT;
}
#endif
return 0;
}
static int cdns_reg_slave_read(struct i2c_client *slave, unsigned long arg)
{
unsigned long ret;
// printk("###i2c-dev debug###i2c slave read %d\n", mySlave.buffer_idx);
#if 1
mySlave_data *mySlave1 = NULL;
u8 *buffer;
mySlave1 = kmalloc(sizeof(mySlave_data), GFP_KERNEL);
if (mySlave1 == NULL)
{
printk("cdns_reg_slave_read kmalloc mySlave1 failed\n");
return -EFAULT;
}
ret = copy_from_user(mySlave1, (mySlave_data __user *)arg, sizeof(mySlave_data));
if (ret)
{
kfree(mySlave1);
printk("copy from user failed, ret = %d\n", ret);
return -EFAULT;
}
if (mySlave1->buffer_idx > 8192)
{
mySlave1->buffer_idx = 8192;
}
ret = copy_to_user(mySlave1->buffer, myI2cBuffer, mySlave1->buffer_idx);
if (ret)
{
kfree(mySlave1);
printk("copy to user failed, ret = %d\n", ret);
return -EACCES;
}
kfree(mySlave1);
#else
ret = copy_to_user((mySlave_data __user *)arg, mySlave.buffer, sizeof(mySlave_data));
if (ret)
{
printk("copy to user failed, ret = %d\n", ret);
return -EACCES;
}
#endif
return 0;
}
#endif
static const struct i2c_algorithm cdns_i2c_algo = {
.master_xfer = cdns_i2c_master_xfer,
.functionality = cdns_i2c_func,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
.reg_slave = cdns_reg_slave,
.unreg_slave = cdns_unreg_slave,
.reg_slave_write = cdns_reg_slave_write,
.reg_slave_read = cdns_reg_slave_read,
#endif
};
/**
* cdns_i2c_calc_divs - Calculate clock dividers
* @f: I2C clock frequency
* @input_clk: Input clock frequency
* @a: First divider (return value)
* @b: Second divider (return value)
*
* f is used as input and output variable. As input it is used as target I2C
* frequency. On function exit f holds the actually resulting I2C frequency.
*
* Return: 0 on success, negative errno otherwise.
*/
static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
unsigned int *a, unsigned int *b)
{
unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp;
unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0;
unsigned int last_error, current_error;
/* calculate (divisor_a+1) x (divisor_b+1) */
temp = input_clk / (22 * fscl);
/*
* If the calculated value is negative or 0, the fscl input is out of
* range. Return error.
*/
if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX)))
return -EINVAL;
last_error = -1;
for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX))
continue;
div_b--;
actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
if (actual_fscl > fscl)
continue;
current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
(fscl - actual_fscl));
if (last_error > current_error) {
calc_div_a = div_a;
calc_div_b = div_b;
best_fscl = actual_fscl;
last_error = current_error;
}
}
*a = calc_div_a;
*b = calc_div_b;
*f = best_fscl;
return 0;
}
/**
* cdns_i2c_setclk - This function sets the serial clock rate for the I2C device
* @clk_in: I2C clock input frequency in Hz
* @id: Pointer to the I2C device structure
*
* The device must be idle rather than busy transferring data before setting
* these device options.
* The data rate is set by values in the control register.
* The formula for determining the correct register values is
* Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1))
* See the hardware data sheet for a full explanation of setting the serial
* clock rate. The clock can not be faster than the input clock divide by 22.
* The two most common clock rates are 100KHz and 400KHz.
*
* Return: 0 on success, negative error otherwise
*/
static int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id)
{
unsigned int div_a, div_b;
unsigned int ctrl_reg;
int ret = 0;
unsigned long fscl = id->i2c_clk;
ret = cdns_i2c_calc_divs(&fscl, clk_in, &div_a, &div_b);
if (ret)
return ret;
ctrl_reg = id->ctrl_reg;
ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
(div_b << CDNS_I2C_CR_DIVB_SHIFT));
id->ctrl_reg = ctrl_reg;
return 0;
}
/**
* cdns_i2c_clk_notifier_cb - Clock rate change callback
* @nb: Pointer to notifier block
* @event: Notification reason
* @data: Pointer to notification data object
*
* This function is called when the cdns_i2c input clock frequency changes.
* The callback checks whether a valid bus frequency can be generated after the
* change. If so, the change is acknowledged, otherwise the change is aborted.
* New dividers are written to the HW in the pre- or post change notification
* depending on the scaling direction.
*
* Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK
* to acknowledge the change, NOTIFY_DONE if the notification is
* considered irrelevant.
*/
static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
event, void *data)
{
struct clk_notifier_data *ndata = data;
struct cdns_i2c *id = to_cdns_i2c(nb);
if (pm_runtime_suspended(id->dev))
return NOTIFY_OK;
switch (event) {
case PRE_RATE_CHANGE:
{
unsigned long input_clk = ndata->new_rate;
unsigned long fscl = id->i2c_clk;
unsigned int div_a, div_b;
int ret;
ret = cdns_i2c_calc_divs(&fscl, input_clk, &div_a, &div_b);
if (ret) {
dev_warn(id->adap.dev.parent,
"clock rate change rejected\n");
return NOTIFY_STOP;
}
/* scale up */
if (ndata->new_rate > ndata->old_rate)
cdns_i2c_setclk(ndata->new_rate, id);
return NOTIFY_OK;
}
case POST_RATE_CHANGE:
id->input_clk = ndata->new_rate;
/* scale down */
if (ndata->new_rate < ndata->old_rate)
cdns_i2c_setclk(ndata->new_rate, id);
return NOTIFY_OK;
case ABORT_RATE_CHANGE:
/* scale up */
if (ndata->new_rate > ndata->old_rate)
cdns_i2c_setclk(ndata->old_rate, id);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
/**
* cdns_i2c_runtime_suspend - Runtime suspend method for the driver
* @dev: Address of the platform_device structure
*
* Put the driver into low power mode.
*
* Return: 0 always
*/
static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
clk_disable(xi2c->clk);
return 0;
}
/**
* cdns_i2c_init - Controller initialisation
* @id: Device private data structure
*
* Initialise the i2c controller.
*
*/
static void cdns_i2c_init(struct cdns_i2c *id)
{
cdns_i2c_writereg(id->ctrl_reg, CDNS_I2C_CR_OFFSET);
/*
* Cadence I2C controller has a bug wherein it generates
* invalid read transaction after HW timeout in master receiver mode.
* HW timeout is not used by this driver and the interrupt is disabled.
* But the feature itself cannot be disabled. Hence maximum value
* is written to this register to reduce the chances of error.
*/
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
}
/**
* cdns_i2c_runtime_resume - Runtime resume
* @dev: Address of the platform_device structure
*
* Runtime resume callback.
*
* Return: 0 on success and error value on error
*/
static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
int ret;
ret = clk_enable(xi2c->clk);
if (ret) {
dev_err(dev, "Cannot enable clock.\n");
return ret;
}
cdns_i2c_init(xi2c);
return 0;
}
/**
* cdns_i2c_prepare_recovery - Withold recovery state
* @adapter: Pointer to i2c adapter
*
* This function is called to prepare for recovery.
* It changes the state of pins from SCL/SDA to GPIO.
*/
static void cdns_i2c_prepare_recovery(struct i2c_adapter *adapter)
{
struct cdns_i2c *p_cdns_i2c;
p_cdns_i2c = container_of(adapter, struct cdns_i2c, adap);
/* Setting pin state as gpio */
pinctrl_select_state(p_cdns_i2c->pinctrl,
p_cdns_i2c->pinctrl_pins_gpio);
}
/**
* cdns_i2c_unprepare_recovery - Release recovery state
* @adapter: Pointer to i2c adapter
*
* This function is called on exiting recovery. It reverts
* the state of pins from GPIO to SCL/SDA.
*/
static void cdns_i2c_unprepare_recovery(struct i2c_adapter *adapter)
{
struct cdns_i2c *p_cdns_i2c;
p_cdns_i2c = container_of(adapter, struct cdns_i2c, adap);
/* Setting pin state to default(i2c) */
pinctrl_select_state(p_cdns_i2c->pinctrl,
p_cdns_i2c->pinctrl_pins_default);
}
/**
* cdns_i2c_init_recovery_info - Initialize I2C bus recovery
* @pid: Pointer to cdns i2c structure
* @pdev: Handle to the platform device structure
*
* This function does required initialization for i2c bus
* recovery. It registers three functions for prepare,
* recover and unprepare
*
* Return: 0 on Success, negative error otherwise.
*/
static int cdns_i2c_init_recovery_info(struct cdns_i2c *pid,
struct platform_device *pdev)
{
struct i2c_bus_recovery_info *rinfo = &pid->rinfo;
pid->pinctrl_pins_default = pinctrl_lookup_state(pid->pinctrl,
PINCTRL_STATE_DEFAULT);
pid->pinctrl_pins_gpio = pinctrl_lookup_state(pid->pinctrl, "gpio");
/* Fetches GPIO pins */
rinfo->sda_gpio = of_get_named_gpio(pdev->dev.of_node, "sda-gpios", 0);
rinfo->scl_gpio = of_get_named_gpio(pdev->dev.of_node, "scl-gpios", 0);
/* if GPIO driver isn't ready yet, deffer probe */
if (rinfo->sda_gpio == -EPROBE_DEFER ||
rinfo->scl_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER;
/* Validates fetched information */
if (!gpio_is_valid(rinfo->sda_gpio) ||
!gpio_is_valid(rinfo->scl_gpio) ||
IS_ERR(pid->pinctrl_pins_default) ||
IS_ERR(pid->pinctrl_pins_gpio)) {
dev_dbg(&pdev->dev, "recovery information incomplete\n");
return 0;
}
dev_dbg(&pdev->dev, "using scl-gpio %d and sda-gpio %d for recovery\n",
rinfo->sda_gpio, rinfo->scl_gpio);
rinfo->prepare_recovery = cdns_i2c_prepare_recovery;
rinfo->unprepare_recovery = cdns_i2c_unprepare_recovery;
rinfo->recover_bus = i2c_generic_gpio_recovery;
pid->adap.bus_recovery_info = rinfo;
return 0;
}
static const struct dev_pm_ops cdns_i2c_dev_pm_ops = {
SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
cdns_i2c_runtime_resume, NULL)
};
static const struct cdns_platform_data r1p10_i2c_def = {
.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
};
static const struct of_device_id cdns_i2c_of_match[] = {
{ .compatible = "cdns,i2c-r1p10", .data = &r1p10_i2c_def },
{ .compatible = "cdns,i2c-r1p14",},
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
/**
* cdns_i2c_probe - Platform registration call
* @pdev: Handle to the platform device structure
*
* This function does all the memory allocation and registration for the i2c
* device. User can modify the address mode to 10 bit address mode using the
* ioctl call with option I2C_TENBIT.
*
* Return: 0 on success, negative error otherwise
*/
static int cdns_i2c_probe(struct platform_device *pdev)
{
struct resource *r_mem;
struct cdns_i2c *id;
int ret;
const struct of_device_id *match;
id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
if (!id)
return -ENOMEM;
id->dev = &pdev->dev;
platform_set_drvdata(pdev, id);
match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);
if (match && match->data) {
const struct cdns_platform_data *data = match->data;
id->quirks = data->quirks;
}
id->pinctrl = devm_pinctrl_get(&pdev->dev);
if (!IS_ERR(id->pinctrl)) {
ret = cdns_i2c_init_recovery_info(id, pdev);
if (ret)
return ret;
}
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
if (IS_ERR(id->membase))
return PTR_ERR(id->membase);
id->irq = platform_get_irq(pdev, 0);
id->adap.owner = THIS_MODULE;
id->adap.dev.of_node = pdev->dev.of_node;
id->adap.algo = &cdns_i2c_algo;
id->adap.timeout = CDNS_I2C_TIMEOUT;
id->adap.retries = 3; /* Default retry value. */
id->adap.algo_data = id;
id->adap.dev.parent = &pdev->dev;
init_completion(&id->xfer_done);
snprintf(id->adap.name, sizeof(id->adap.name),
"Cadence I2C at %08lx", (unsigned long)r_mem->start);
id->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(id->clk)) {
dev_err(&pdev->dev, "input clock not found.\n");
return PTR_ERR(id->clk);
}
ret = clk_prepare_enable(id->clk);
if (ret)
dev_err(&pdev->dev, "Unable to enable clock.\n");
pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(id->dev);
pm_runtime_set_active(id->dev);
pm_runtime_enable(id->dev);
id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
id->input_clk = clk_get_rate(id->clk);
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&id->i2c_clk);
if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX))
id->i2c_clk = CDNS_I2C_SPEED_DEFAULT;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* Set initial mode to master */
id->dev_mode = CDNS_I2C_MODE_MASTER;
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
#endif
id->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
ret = cdns_i2c_setclk(id->input_clk, id);
if (ret) {
dev_err(&pdev->dev, "invalid SCL clock: %u Hz\n", id->i2c_clk);
ret = -EINVAL;
goto err_clk_dis;
}
ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,
DRIVER_NAME, id);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
goto err_clk_dis;
}
cdns_i2c_init(id);
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
ret = i2c_add_adapter(&id->adap);
if (ret < 0)
goto err_clk_dis;
return 0;
err_clk_dis:
clk_disable_unprepare(id->clk);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
return ret;
}
/**
* cdns_i2c_remove - Unregister the device after releasing the resources
* @pdev: Handle to the platform device structure
*
* This function frees all the resources allocated to the device.
*
* Return: 0 always
*/
static int cdns_i2c_remove(struct platform_device *pdev)
{
struct cdns_i2c *id = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
i2c_del_adapter(&id->adap);
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
clk_disable_unprepare(id->clk);
return 0;
}
static struct platform_driver cdns_i2c_drv = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = cdns_i2c_of_match,
.pm = &cdns_i2c_dev_pm_ops,
},
.probe = cdns_i2c_probe,
.remove = cdns_i2c_remove,
};
module_platform_driver(cdns_i2c_drv);
MODULE_AUTHOR("Xilinx Inc.");
MODULE_DESCRIPTION("Cadence I2C bus driver");
MODULE_LICENSE("GPL");
i2c-dev.c
/*
i2c-dev.c - i2c-bus driver, char device interface
Copyright (C) 1995-97 Simon G. Vogl
Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that 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.
*/
/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
But I have used so much of his original code and ideas that it seems
only fair to recognize him as co-author -- Frodo */
/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
/*
* An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a
* slave (i2c_client) with which messages will be exchanged. It's coupled
* with a character special file which is accessed by user mode drivers.
*
* The list of i2c_dev structures is parallel to the i2c_adapter lists
* maintained by the driver model, and is updated using bus notifications.
*/
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
struct device *dev;
struct cdev cdev;
};
#define I2C_MINORS MINORMASK
static LIST_HEAD(i2c_dev_list);
static DEFINE_SPINLOCK(i2c_dev_list_lock);
static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
spin_lock(&i2c_dev_list_lock);
list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
if (i2c_dev->adap->nr == index)
goto found;
}
i2c_dev = NULL;
found:
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
if (adap->nr >= I2C_MINORS) {
printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
adap->nr);
return ERR_PTR(-ENODEV);
}
i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
i2c_dev->adap = adap;
spin_lock(&i2c_dev_list_lock);
list_add_tail(&i2c_dev->list, &i2c_dev_list);
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
static void put_i2c_dev(struct i2c_dev *i2c_dev)
{
spin_lock(&i2c_dev_list_lock);
list_del(&i2c_dev->list);
spin_unlock(&i2c_dev_list_lock);
kfree(i2c_dev);
}
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));
if (!i2c_dev)
return -ENODEV;
return sprintf(buf, "%s\n", i2c_dev->adap->name);
}
static DEVICE_ATTR_RO(name);
static struct attribute *i2c_attrs[] = {
&dev_attr_name.attr,
NULL,
};
ATTRIBUTE_GROUPS(i2c);
/* ------------------------------------------------------------------------- */
/*
* After opening an instance of this character special file, a file
* descriptor starts out associated only with an i2c_adapter (and bus).
*
* Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg
* traffic to any devices on the bus used by that adapter. That's because
* the i2c_msg vectors embed all the addressing information they need, and
* are submitted directly to an i2c_adapter. However, SMBus-only adapters
* don't support that interface.
*
* To use read()/write() system calls on that file descriptor, or to use
* SMBus interfaces (and work with SMBus-only hosts!), you must first issue
* an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous
* (never registered) i2c_client so it holds the addressing information
* needed by those system calls and by this SMBus interface.
*/
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file_inode(file)), count);
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
tmp = memdup_user(buf, count);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file_inode(file)), count);
ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}
static int i2cdev_check(struct device *dev, void *addrp)
{
struct i2c_client *client = i2c_verify_client(dev);
if (!client || client->addr != *(unsigned int *)addrp)
return 0;
return dev->driver ? -EBUSY : 0;
}
/* walk up mux tree */
static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr)
{
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
int result;
result = device_for_each_child(&adapter->dev, &addr, i2cdev_check);
if (!result && parent)
result = i2cdev_check_mux_parents(parent, addr);
return result;
}
/* recurse down mux tree */
static int i2cdev_check_mux_children(struct device *dev, void *addrp)
{
int result;
if (dev->type == &i2c_adapter_type)
result = device_for_each_child(dev, addrp,
i2cdev_check_mux_children);
else
result = i2cdev_check(dev, addrp);
return result;
}
/* This address checking function differs from the one in i2c-core
in that it considers an address with a registered device, but no
driver bound to it, as NOT busy. */
static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
{
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
int result = 0;
if (parent)
result = i2cdev_check_mux_parents(parent, addr);
if (!result)
result = device_for_each_child(&adapter->dev, &addr,
i2cdev_check_mux_children);
return result;
}
static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
unsigned long arg)
{
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_msg *rdwr_pa;
u8 __user **data_ptrs;
int i, res;
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
/* Put an arbitrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
return -EINVAL;
rdwr_pa = memdup_user(rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg));
if (IS_ERR(rdwr_pa))
return PTR_ERR(rdwr_pa);
data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
res = 0;
for (i = 0; i < rdwr_arg.nmsgs; i++) {
/* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) {
res = -EINVAL;
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);
if (IS_ERR(rdwr_pa[i].buf)) {
res = PTR_ERR(rdwr_pa[i].buf);
break;
}
/*
* If the message length is received from the slave (similar
* to SMBus block read), we must ensure that the buffer will
* be large enough to cope with a message length of
* I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus
* drivers allow. The first byte in the buffer must be
* pre-filled with the number of extra bytes, which must be
* at least one to hold the message length, but can be
* greater (for example to account for a checksum byte at
* the end of the message.)
*/
if (rdwr_pa[i].flags & I2C_M_RECV_LEN) {
if (!(rdwr_pa[i].flags & I2C_M_RD) ||
rdwr_pa[i].buf[0] < 1 ||
rdwr_pa[i].len < rdwr_pa[i].buf[0] +
I2C_SMBUS_BLOCK_MAX) {
res = -EINVAL;
break;
}
rdwr_pa[i].len = rdwr_pa[i].buf[0];
}
}
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
while (i-- > 0) {
if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
rdwr_pa[i].len))
res = -EFAULT;
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
unsigned long arg)
{
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
int datasize, res;
if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
if ((data_arg.size != I2C_SMBUS_BYTE) &&
(data_arg.size != I2C_SMBUS_QUICK) &&
(data_arg.size != I2C_SMBUS_BYTE_DATA) &&
(data_arg.size != I2C_SMBUS_WORD_DATA) &&
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
dev_dbg(&client->adapter->dev,
"size out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.size);
return -EINVAL;
}
/* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
so the check is valid if size==I2C_SMBUS_QUICK too. */
if ((data_arg.read_write != I2C_SMBUS_READ) &&
(data_arg.read_write != I2C_SMBUS_WRITE)) {
dev_dbg(&client->adapter->dev,
"read_write out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.read_write);
return -EINVAL;
}
/* Note that command values are always valid! */
if ((data_arg.size == I2C_SMBUS_QUICK) ||
((data_arg.size == I2C_SMBUS_BYTE) &&
(data_arg.read_write == I2C_SMBUS_WRITE)))
/* These are special: we do not use data */
return i2c_smbus_xfer(client->adapter, client->addr,
client->flags, data_arg.read_write,
data_arg.command, data_arg.size, NULL);
if (data_arg.data == NULL) {
dev_dbg(&client->adapter->dev,
"data is NULL pointer in ioctl I2C_SMBUS.\n");
return -EINVAL;
}
if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
(data_arg.size == I2C_SMBUS_BYTE))
datasize = sizeof(data_arg.data->byte);
else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
(data_arg.size == I2C_SMBUS_PROC_CALL))
datasize = sizeof(data_arg.data->word);
else /* size == smbus block, i2c block, or block proc. call */
datasize = sizeof(data_arg.data->block);
if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) ||
(data_arg.read_write == I2C_SMBUS_WRITE)) {
if (copy_from_user(&temp, data_arg.data, datasize))
return -EFAULT;
}
if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
/* Convert old I2C block commands to the new
convention. This preserves binary compatibility. */
data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA;
if (data_arg.read_write == I2C_SMBUS_READ)
temp.block[0] = I2C_SMBUS_BLOCK_MAX;
}
res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
data_arg.read_write, data_arg.command, data_arg.size, &temp);
if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
}
return res;
}
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;
int ret;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch (cmd) {
case I2C_SLAVE:
printk("###i2c-dev debug###i2c slave mode###\n");
client->addr = (unsigned short)arg;
client->adapter->algo->reg_slave(client);
// printk("###i2c-dev debug###addr 0x%x\n", client->addr);
return 0;
case I2C_SLAVE_FORCE:
#if 0
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
#endif
printk("###i2c-dev debug###i2c master mode###\n");
ret = client->adapter->algo->unreg_slave(client);
return ret;
case I2C_SLAVE_WRITE:
client->adapter->algo->reg_slave_write(client, arg);
return 0;
case I2C_SLAVE_READ:
client->adapter->algo->reg_slave_read(client, arg);
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
/*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core. Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR:
return i2cdev_ioctl_rdwr(client, arg);
case I2C_SMBUS:
return i2cdev_ioctl_smbus(client, arg);
case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
/* NOTE: returning a fault code here could cause trouble
* in buggy userspace code. Some old kernel bugs returned
* zero in this case, and userspace code might accidentally
* have depended on that bug.
*/
return -ENOTTY;
}
return 0;
}
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
adap = i2c_get_adapter(minor);
if (!adap)
return -ENODEV;
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is ** NEVER REGISTERED ** with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->adapter = adap;
file->private_data = client;
return 0;
}
static int i2cdev_release(struct inode *inode, struct file *file)
{
struct i2c_client *client = file->private_data;
i2c_put_adapter(client->adapter);
kfree(client);
file->private_data = NULL;
return 0;
}
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
/* ------------------------------------------------------------------------- */
static struct class *i2c_dev_class;
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
cdev_init(&i2c_dev->cdev, &i2cdev_fops);
i2c_dev->cdev.owner = THIS_MODULE;
res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
if (res)
goto error_cdev;
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error:
cdev_del(&i2c_dev->cdev);
error_cdev:
put_i2c_dev(i2c_dev);
return res;
}
static int i2cdev_detach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = i2c_dev_get_by_minor(adap->nr);
if (!i2c_dev) /* attach_adapter must have failed */
return 0;
cdev_del(&i2c_dev->cdev);
put_i2c_dev(i2c_dev);
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
return 0;
}
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
return i2cdev_attach_adapter(dev, NULL);
case BUS_NOTIFY_DEL_DEVICE:
return i2cdev_detach_adapter(dev, NULL);
}
return 0;
}
static struct notifier_block i2cdev_notifier = {
.notifier_call = i2cdev_notifier_call,
};
/* ------------------------------------------------------------------------- */
/*
* module load/unload record keeping
*/
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
static void __exit i2c_dev_exit(void)
{
bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier);
i2c_for_each_dev(NULL, i2cdev_detach_adapter);
class_destroy(i2c_dev_class);
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C /dev entries driver");
MODULE_LICENSE("GPL");
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
文章知识点与官方知识档案匹配,可进一步学习相关知识