第15章 Linux I2C核心、总线与设备驱动之I2C总线驱动实例

15.5 Tegra I2C总线驱动实例

    NVIDIA Tegra I2C总线驱动位于drivers/i2c/busses/i2c-tegra.c(I2C 总线 controller driver)下,这里不具体研究硬件细节,只看驱动的框架和流程。

    I2C总线驱动是一个单独的驱动,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结
构体,如代码清单15.18所示。

    代码清单15.18 Tegra I2C总线驱动的模块加载与卸载

    /* Match table for of_platform binding */
static const struct of_device_id tegra_i2c_of_match[] = {
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
{ .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
{},
};

MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);

 static struct platform_driver tegra_i2c_driver = {
.probe   = tegra_i2c_probe,
.remove  = tegra_i2c_remove,
.driver  = {
.name  = "tegra-i2c",
.owner = THIS_MODULE,
.of_match_table = tegra_i2c_of_match,
.pm    = TEGRA_I2C_PM,
},
};

static int __init tegra_i2c_init_driver(void)
{
return platform_driver_register(&tegra_i2c_driver);
}

static void __exit tegra_i2c_exit_driver(void)
{
platform_driver_unregister(&tegra_i2c_driver);
}

    当在tegra的设备树中添加tegra_i2c_of_match匹配表兼容的节点后,上述platform_driver中的probe()函数会执行。

    其中probe指针指向的tegra_i2c_probe()函数将被调用,以初始化适配器硬件申请适配器需要的内存时钟、中断等资源,最后注册适配器,如代码清单15.19所示。

    代码清单15.19 Tegra I2C总线驱动中的tegra_i2c_probe()函数

static int tegra_i2c_probe(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev;
struct resource *res;
struct clk *div_clk;
struct clk *fast_clk;
void __iomem *base;
int irq;
int ret = 0;
int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "no irq resource\n");
return -EINVAL;
}
irq = res->start;

div_clk = devm_clk_get(&pdev->dev, "div-clk");
if (IS_ERR(div_clk)) {
dev_err(&pdev->dev, "missing controller clock");
return PTR_ERR(div_clk);
}

i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return -ENOMEM;

i2c_dev->base = base;
i2c_dev->div_clk = div_clk;
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;

i2c_dev->rst = devm_reset_control_get(&pdev->dev, "i2c");
if (IS_ERR(i2c_dev->rst)) {
dev_err(&pdev->dev, "missing controller reset");
return PTR_ERR(i2c_dev->rst);
}

ret = of_property_read_u32(i2c_dev->dev->of_node, "clock-frequency",
&i2c_dev->bus_clk_rate);
if (ret)
i2c_dev->bus_clk_rate = 100000; /* default clock rate */

i2c_dev->hw = &tegra20_i2c_hw;

if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_device(tegra_i2c_of_match, &pdev->dev);
i2c_dev->hw = match->data;
i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
"nvidia,tegra20-i2c-dvc");
} else if (pdev->id == 3) {
i2c_dev->is_dvc = 1;
}
init_completion(&i2c_dev->msg_complete);

if (!i2c_dev->hw->has_single_clk_source) {
fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
if (IS_ERR(fast_clk)) {
dev_err(&pdev->dev, "missing fast clock");
return PTR_ERR(fast_clk);
}
i2c_dev->fast_clk = fast_clk;
}

platform_set_drvdata(pdev, i2c_dev);

if (!i2c_dev->hw->has_single_clk_source) {
ret = clk_prepare(i2c_dev->fast_clk);
if (ret < 0) {
dev_err(i2c_dev->dev, "Clock prepare failed %d\n", ret);
return ret;
}
}

clk_multiplier *= (i2c_dev->hw->clk_divisor_std_fast_mode + 1);
ret = clk_set_rate(i2c_dev->div_clk,
   i2c_dev->bus_clk_rate * clk_multiplier);
if (ret) {
dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret);
goto unprepare_fast_clk;
}

ret = clk_prepare(i2c_dev->div_clk);
if (ret < 0) {
dev_err(i2c_dev->dev, "Clock prepare failed %d\n", ret);
goto unprepare_fast_clk;
}

ret = tegra_i2c_init(i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize i2c controller");
goto unprepare_div_clk;
}

ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
goto unprepare_div_clk;
}

i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
i2c_dev->adapter.owner = THIS_MODULE;
i2c_dev->adapter.class = I2C_CLASS_DEPRECATED;
strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
sizeof(i2c_dev->adapter.name));
i2c_dev->adapter.algo = &tegra_i2c_algo; // i2c_adapter通信方法
i2c_dev->adapter.dev.parent = &pdev->dev;
i2c_dev->adapter.nr = pdev->id;
i2c_dev->adapter.dev.of_node = pdev->dev.of_node;

ret = i2c_add_numbered_adapter(&i2c_dev->adapter);// 注册i2c adapter
if (ret) {
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
goto unprepare_div_clk;
}

return 0;

unprepare_div_clk:
clk_unprepare(i2c_dev->div_clk);
unprepare_fast_clk:
if (!i2c_dev->hw->has_single_clk_source)
clk_unprepare(i2c_dev->fast_clk);

return ret;
}

    与tegra_i2c_probe()函数相反功能的函数是tegra_i2c_remove()函数,tegra_i2c_remove()函数在适配器模块卸载函数调用platform_driver_unregister()函数时通过platform_driver的remove指针方式被调用。tegra_i2c_remove()的代码如清单15.20所示。

代码清单15.20 Tegra I2C总线驱动中的tegra_i2c_remove()函数

static int tegra_i2c_remove(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); 
i2c_del_adapter(&i2c_dev->adapter); // 注销i2c adapter

clk_unprepare(i2c_dev->div_clk);
if (!i2c_dev->hw->has_single_clk_source)
clk_unprepare(i2c_dev->fast_clk);

return 0;
}

备注:

代码清单15.19和代码清单15.20中的tegra_i2c_dev结构体可进行适配器所有信息的封装,类似于私有信息结构体,代码清单15.21所示为tegra_i2c_dev结构体的定义。在编程中要牢记Linux这个编程习惯,这实际上也是面向对象的一种体现。

代码清单15.21 tegra_i2c_dev结构体

/**
 * struct tegra_i2c_dev - per device i2c context
 * @dev: device reference for power management
 * @hw: Tegra i2c hw feature.
 * @adapter: core i2c layer adapter information
 * @div_clk: clock reference for div clock of i2c controller.
 * @fast_clk: clock reference for fast clock of i2c controller.
 * @base: ioremapped registers cookie
 * @cont_id: i2c controller id, used for for packet header
 * @irq: irq number of transfer complete interrupt
 * @is_dvc: identifies the DVC i2c controller, has a different register layout
 * @msg_complete: transfer completion notifier
 * @msg_err: error code for completed message
 * @msg_buf: pointer to current message data
 * @msg_buf_remaining: size of unsent data in the message buffer
 * @msg_read: identifies read transfers
 * @bus_clk_rate: current i2c bus clock rate
 * @is_suspended: prevents i2c controller accesses after suspend is called
 */
struct tegra_i2c_dev {
struct device *dev;
const struct tegra_i2c_hw_feature *hw;
struct i2c_adapter adapter;
struct clk *div_clk;
struct clk *fast_clk;
struct reset_control *rst;
void __iomem *base;
int cont_id;
int irq;
bool irq_disabled;
int is_dvc;
struct completion msg_complete;
int msg_err;
u8 *msg_buf;
size_t msg_buf_remaining;
int msg_read;
u32 bus_clk_rate;
bool is_suspended;
};

分析:

tegra_i2c_probe()函数中的platform_set_drvdata(pdev,i2c_dev)和

i2c_set_adapdata(&i2c_dev->adapter,i2c_dev)已经把这个结构体的实例i2c_dev依附到了platform_device和i2c_adapter的私有数据上了,在其他地方用相应的方法就可以把这个结构体的实例取出来。

由代码清单15.19看出,与I2C适配器对应的i2c_algorithm结构体实例为tegra_i2c_algo,代码清单15.22给出为tegra_i2c_algo的定义。

代码清单15.22 tegra_i2c_algo结构体

static const struct i2c_algorithm tegra_i2c_algo = {
.master_xfer = tegra_i2c_xfer,
.functionality = tegra_i2c_func,
};

分析:

tegra_i2c_xfer()为Tegra I2C总线通信传输函数,所有在I2C总线上对设备的访问最终由函数tegra_i2c_xfer()完成,代码清单15.23所示为这个重要函数以及其依赖的tegra_i2c_xfer_msg()函数的源代码。

代码清单15.23 Tegra I2C总线驱动的tegra_i2c_xfer()函数

static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
struct i2c_msg *msg, enum msg_end_type end_state)
{
u32 packet_header;
u32 int_mask;
int ret;

tegra_i2c_flush_fifos(i2c_dev);

if (msg->len == 0)
return -EINVAL;

i2c_dev->msg_buf = msg->buf;
i2c_dev->msg_buf_remaining = msg->len;
i2c_dev->msg_err = I2C_ERR_NONE;
i2c_dev->msg_read = (msg->flags & I2C_M_RD);
reinit_completion(&i2c_dev->msg_complete);

packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
PACKET_HEADER0_PROTOCOL_I2C |
(i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
(1 << PACKET_HEADER0_PACKET_ID_SHIFT);
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);

packet_header = msg->len - 1;
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);

packet_header = I2C_HEADER_IE_ENABLE;
if (end_state == MSG_END_CONTINUE)
packet_header |= I2C_HEADER_CONTINUE_XFER;
else if (end_state == MSG_END_REPEAT_START)
packet_header |= I2C_HEADER_REPEAT_START;
if (msg->flags & I2C_M_TEN) {
packet_header |= msg->addr;
packet_header |= I2C_HEADER_10BIT_ADDR;
} else {
packet_header |= msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
}
if (msg->flags & I2C_M_IGNORE_NAK)
packet_header |= I2C_HEADER_CONT_ON_NAK;
if (msg->flags & I2C_M_RD)
packet_header |= I2C_HEADER_READ;
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);

if (!(msg->flags & I2C_M_RD))
tegra_i2c_fill_tx_fifo(i2c_dev);

int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
if (msg->flags & I2C_M_RD)
int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
else if (i2c_dev->msg_buf_remaining)
int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
tegra_i2c_unmask_irq(i2c_dev, int_mask);
dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
i2c_readl(i2c_dev, I2C_INT_MASK));

ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT);
tegra_i2c_mask_irq(i2c_dev, int_mask);

if (ret == 0) {
dev_err(i2c_dev->dev, "i2c transfer timed out\n");
tegra_i2c_init(i2c_dev);
return -ETIMEDOUT;
}

dev_dbg(i2c_dev->dev, "transfer complete: %d %d %d\n",
ret, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err);

if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
return 0;

/*
* NACK interrupt is generated before the I2C controller generates the
* STOP condition on the bus. So wait for 2 clock periods before resetting
* the controller so that STOP condition has been delivered properly.
*/
if (i2c_dev->msg_err == I2C_ERR_NO_ACK)
udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->bus_clk_rate));

tegra_i2c_init(i2c_dev);
if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return 0;
return -EREMOTEIO;
}

return -EIO;
}

static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
int i;
int ret = 0;

if (i2c_dev->is_suspended)
return -EBUSY;

ret = tegra_i2c_clock_enable(i2c_dev);
if (ret < 0) {
dev_err(i2c_dev->dev, "Clock enable failed %d\n", ret);
return ret;
}

for (i = 0; i < num; i++) { 
enum msg_end_type end_type = MSG_END_STOP;
if (i < (num - 1)) {
if (msgs[i + 1].flags & I2C_M_NOSTART)
end_type = MSG_END_CONTINUE;
else
end_type = MSG_END_REPEAT_START;
}
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
if (ret)
break;
}
tegra_i2c_clock_disable(i2c_dev);
return ret ?: i;
}

分析:

        for (i = 0; i < num; i++) { 
enum msg_end_type end_type = MSG_END_STOP;
if (i < (num - 1)) {
if (msgs[i + 1].flags & I2C_M_NOSTART)
end_type = MSG_END_CONTINUE;
else
end_type = MSG_END_REPEAT_START;
}
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
if (ret)
break;
}

上述代码中for循环遍历所有的i2c_msg,而每个i2c_msg则由tegra_i2c_xfer_msg()函数处理,tegra_i2c_xfer_msg()每次发起硬件操作后,需要通过wait_for_completion_timeout()等待传输的完成,因此,这里面有一个被调度出去的过程。中断到来且I2C的包传输结束时,就是唤醒这个睡眠进程的时候,如代码清单15.24所示。

代码清单15.24 Tegra I2C总线驱动的中断服务程序

static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
{
u32 status;
const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
struct tegra_i2c_dev *i2c_dev = dev_id;

status = i2c_readl(i2c_dev, I2C_INT_STATUS);

if (status == 0) {
dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
i2c_readl(i2c_dev, I2C_STATUS),
i2c_readl(i2c_dev, I2C_CNFG));
i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;

if (!i2c_dev->irq_disabled) {
disable_irq_nosync(i2c_dev->irq);
i2c_dev->irq_disabled = 1;
}
goto err;
}

if (unlikely(status & status_err)) {
if (status & I2C_INT_NO_ACK)
i2c_dev->msg_err |= I2C_ERR_NO_ACK;
if (status & I2C_INT_ARBITRATION_LOST)
i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST;
goto err;
}

if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
if (i2c_dev->msg_buf_remaining)
tegra_i2c_empty_rx_fifo(i2c_dev);
else
BUG();
}

if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
if (i2c_dev->msg_buf_remaining)
tegra_i2c_fill_tx_fifo(i2c_dev);
else
tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
}

i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);

if (status & I2C_INT_PACKET_XFER_COMPLETE) {
BUG_ON(i2c_dev->msg_buf_remaining);
complete(&i2c_dev->msg_complete);
}

return IRQ_HANDLED;
err:
/* An error occurred, mask all interrupts */
tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
I2C_INT_RX_FIFO_DATA_REQ);
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);

complete(&i2c_dev->msg_complete);
return IRQ_HANDLED;
}


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80568022