drivers/i2c/busses/i2c-tegra.c
I2C总线驱动是一个单独的驱动, 在模块的加载和卸载函数中, 只需注册和注销一个platform_driver结构体, 代码如下
static const struct of_device_id tegra_i2c_of_match[] = { { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .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", .of_match_table = tegra_i2c_of_match, .pm = &tegra_i2c_pm, }, }; module_platform_driver(tegra_i2c_driver); |
当在arch/arm/mach-tegra下创建一个名字为tegra-i2c的同名platform_device, 或者在tegra的设备树中添加了tegra_i2c_of_match匹配表兼容的节点后, 上述platform_driver中的probe() 函数会执行。
其中probe指针指向的tegra_i2c_probe() 函数将被调用, 以初始化适配器硬件、 申请适配器要的内存、 时钟、 中断等资源, 最终注册适配器, 代码如下
static int tegra_i2c_probe(struct platform_device *pdev) { struct tegra_i2c_dev *i2c_dev; struct resource *res; int err; i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) return -ENOMEM; platform_set_drvdata(pdev, i2c_dev); init_completion(&i2c_dev->msg_complete); init_completion(&i2c_dev->dma_complete); i2c_dev->hw = of_device_get_match_data(&pdev->dev); i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2c_dev->base)) return PTR_ERR(i2c_dev->base); i2c_dev->base_phys = res->start; err = platform_get_irq(pdev, 0); if (err < 0) return err; i2c_dev->irq = err; /* interrupt will be enabled during of transfer time */ irq_set_status_flags(i2c_dev->irq, IRQ_NOAUTOEN); err = devm_request_threaded_irq(i2c_dev->dev, i2c_dev->irq, NULL, tegra_i2c_isr, IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(i2c_dev->dev), i2c_dev); if (err) return err; i2c_dev->rst = devm_reset_control_get_exclusive(i2c_dev->dev, "i2c"); if (IS_ERR(i2c_dev->rst)) { dev_err_probe(i2c_dev->dev, PTR_ERR(i2c_dev->rst), "failed to get reset control\n"); return PTR_ERR(i2c_dev->rst); } tegra_i2c_parse_dt(i2c_dev); err = tegra_i2c_init_clocks(i2c_dev); if (err) return err; err = tegra_i2c_init_dma(i2c_dev); if (err) goto release_clocks; /* * VI I2C is in VE power domain which is not always ON and not * IRQ-safe. Thus, IRQ-safe device shouldn't be attached to a * non IRQ-safe domain because this prevents powering off the power * domain. * * VI I2C device shouldn't be marked as IRQ-safe because VI I2C won't * be used for atomic transfers. */ if (!i2c_dev->is_vi) pm_runtime_irq_safe(i2c_dev->dev); pm_runtime_enable(i2c_dev->dev); err = tegra_i2c_init_hardware(i2c_dev); if (err) goto release_rpm; i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); i2c_dev->adapter.dev.of_node = i2c_dev->dev->of_node; i2c_dev->adapter.dev.parent = i2c_dev->dev; i2c_dev->adapter.retries = 1; i2c_dev->adapter.timeout = 6 * HZ; i2c_dev->adapter.quirks = i2c_dev->hw->quirks; i2c_dev->adapter.owner = THIS_MODULE; i2c_dev->adapter.class = I2C_CLASS_DEPRECATED; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.nr = pdev->id; if (i2c_dev->hw->supports_bus_clear) i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; strlcpy(i2c_dev->adapter.name, dev_name(i2c_dev->dev), sizeof(i2c_dev->adapter.name)); err = i2c_add_numbered_adapter(&i2c_dev->adapter); if (err) goto release_rpm; return 0; release_rpm: pm_runtime_disable(i2c_dev->dev); tegra_i2c_release_dma(i2c_dev); release_clocks: tegra_i2c_release_clocks(i2c_dev); return err; } |
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); pm_runtime_force_suspend(i2c_dev->dev); tegra_i2c_release_dma(i2c_dev); tegra_i2c_release_clocks(i2c_dev); return 0; } |
struct tegra_i2c_dev { struct device *dev; struct i2c_adapter adapter; const struct tegra_i2c_hw_feature *hw; struct reset_control *rst; unsigned int cont_id; unsigned int irq; phys_addr_t base_phys; void __iomem *base; struct clk_bulk_data clocks[2]; unsigned int nclocks; struct clk *div_clk; u32 bus_clk_rate; struct completion msg_complete; size_t msg_buf_remaining; int msg_err; u8 *msg_buf; struct completion dma_complete; struct dma_chan *tx_dma_chan; struct dma_chan *rx_dma_chan; unsigned int dma_buf_size; dma_addr_t dma_phys; void *dma_buf; bool multimaster_mode; bool atomic_mode; bool dma_mode; bool msg_read; bool is_dvc; bool is_vi; }; |
tegra_i2c_probe() 函数中的platform_set_drvdata(pdev, i2c_dev) 和i2c_set_adapdata(&i2c_dev->adapter, i2c_dev) 已经把这个结构体的实例依附到了platform_device和i2c_adapter的私有数据上了, 在其他地方只要用相应的方法就可以把这个结构体的实例取出来。
与I2C适配器对应的i2c_algorithm结构体实例为tegra_i2c_algo,给出为tegra_i2c_algo的定义。
static const struct i2c_algorithm tegra_i2c_algo = { .master_xfer = tegra_i2c_xfer, .master_xfer_atomic = tegra_i2c_xfer_atomic, .functionality = tegra_i2c_func, }; |
static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *msg, enum msg_end_type end_state) { unsigned long time_left, xfer_time = 100; size_t xfer_size; u32 int_mask; int err; err = tegra_i2c_flush_fifos(i2c_dev); if (err) return err; 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); if (i2c_dev->msg_read) xfer_size = msg->len; else xfer_size = msg->len + I2C_PACKET_HEADER_SIZE; xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD); i2c_dev->dma_mode = xfer_size > I2C_PIO_MODE_PREFERRED_LEN && i2c_dev->dma_buf && !i2c_dev->atomic_mode; tegra_i2c_config_fifo_trig(i2c_dev, xfer_size); /* * Transfer time in mSec = Total bits / transfer rate * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits */ xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC, i2c_dev->bus_clk_rate); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; tegra_i2c_unmask_irq(i2c_dev, int_mask); if (i2c_dev->dma_mode) { if (i2c_dev->msg_read) { dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys, xfer_size, DMA_FROM_DEVICE); err = tegra_i2c_dma_submit(i2c_dev, xfer_size); if (err) return err; } else { dma_sync_single_for_cpu(i2c_dev->dev, i2c_dev->dma_phys, xfer_size, DMA_TO_DEVICE); } } tegra_i2c_push_packet_header(i2c_dev, msg, end_state); if (!i2c_dev->msg_read) { if (i2c_dev->dma_mode) { memcpy(i2c_dev->dma_buf + I2C_PACKET_HEADER_SIZE, msg->buf, msg->len); dma_sync_single_for_device(i2c_dev->dev, i2c_dev->dma_phys, xfer_size, DMA_TO_DEVICE); err = tegra_i2c_dma_submit(i2c_dev, xfer_size); if (err) return err; } else { tegra_i2c_fill_tx_fifo(i2c_dev); } } if (i2c_dev->hw->has_per_pkt_xfer_complete_irq) int_mask |= I2C_INT_PACKET_XFER_COMPLETE; if (!i2c_dev->dma_mode) { 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)); if (i2c_dev->dma_mode) { time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->dma_complete, xfer_time); /* * Synchronize DMA first, since dmaengine_terminate_sync() * performs synchronization after the transfer's termination * and we want to get a completion if transfer succeeded. */ dmaengine_synchronize(i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan); dmaengine_terminate_sync(i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan); if (!time_left && !completion_done(&i2c_dev->dma_complete)) { dev_err(i2c_dev->dev, "DMA transfer timed out\n"); tegra_i2c_init(i2c_dev); return -ETIMEDOUT; } if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) { dma_sync_single_for_cpu(i2c_dev->dev, i2c_dev->dma_phys, xfer_size, DMA_FROM_DEVICE); memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, msg->len); } } time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete, xfer_time); tegra_i2c_mask_irq(i2c_dev, int_mask); if (time_left == 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: %lu %d %d\n", time_left, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); i2c_dev->dma_mode = false; err = tegra_i2c_error_recover(i2c_dev, msg); if (err) return err; return 0; } |
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, ret; ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) { dev_err(i2c_dev->dev, "runtime resume failed %d\n", ret); pm_runtime_put_noidle(i2c_dev->dev); return ret; } for (i = 0; i < num; i++) { enum msg_end_type end_type = MSG_END_STOP; if (i < (num - 1)) { /* check whether follow up message is coming */ 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; } pm_runtime_put(i2c_dev->dev); return ret ?: i; } |
tegra_i2c_xfer的for循环遍历所有的i2c_msg, 而每个i2c_msg则由tegra_i2c_xfer_msg() 函数处理, 它每次发起硬件操作后, 实际上需要通过wait_for_completion_timeout() 等待传输的完成, 因此这里面就会有一个被调度出去的过程。 中断到来且I2C的包传输结束的时候, 就是唤醒这个睡眠进程的时候, 代码如下
Tegra I2C总线驱动的中断服务程序
static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) { const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; struct tegra_i2c_dev *i2c_dev = dev_id; u32 status; 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; goto err; } if (status & status_err) { tegra_i2c_disable_packet_mode(i2c_dev); 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; } /* * I2C transfer is terminated during the bus clear, so skip * processing the other interrupts. */ if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) goto err; if (!i2c_dev->dma_mode) { if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { if (tegra_i2c_empty_rx_fifo(i2c_dev)) { /* * Overflow error condition: message fully sent, * with no XFER_COMPLETE interrupt but hardware * asks to transfer more. */ i2c_dev->msg_err |= I2C_ERR_RX_BUFFER_OVERFLOW; goto err; } } 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); /* * During message read XFER_COMPLETE interrupt is triggered prior to * DMA completion and during message write XFER_COMPLETE interrupt is * triggered after DMA completion. * * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer, * so forcing msg_buf_remaining to 0 in DMA mode. */ if (status & I2C_INT_PACKET_XFER_COMPLETE) { if (i2c_dev->dma_mode) i2c_dev->msg_buf_remaining = 0; /* * Underflow error condition: XFER_COMPLETE before message * fully sent. */ if (WARN_ON_ONCE(i2c_dev->msg_buf_remaining)) { i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; goto err; } complete(&i2c_dev->msg_complete); } goto done; err: /* mask all interrupts on error */ 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); if (i2c_dev->hw->supports_bus_clear) tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); 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 (i2c_dev->dma_mode) { if (i2c_dev->msg_read) dmaengine_terminate_async(i2c_dev->rx_dma_chan); else dmaengine_terminate_async(i2c_dev->tx_dma_chan); complete(&i2c_dev->dma_complete); } complete(&i2c_dev->msg_complete); done: return IRQ_HANDLED; } |