34 IIC(十二)IIC Adapter 源码分析
1. device tree
-
100ask_imx6ull-14x14.dts
iic adapter注册
pinctrl_i2c1: i2c1grp { fsl,pins = < MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 >; }; ... &i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; };
-
imx6ull.dtsi
iic control注册
i2c1: i2c@021a0000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; reg = <0x021a0000 0x4000>; // i2c control register phy addr and size interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; // interrupt clocks = <&clks IMX6UL_CLK_I2C1>; // clock status = "disabled"; };
-
2. platform
-
匹配compatible
-
调用probe
-
初始化i2c adapter, 设置i2c algo,向内核注册i2c adapter
-
初始化i2c adapter control相关寄存器
static int i2c_imx_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids, &pdev->dev); struct imx_i2c_struct *i2c_imx; struct resource *res; struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev); void __iomem *base; int irq, ret; dma_addr_t phy_addr; dev_dbg(&pdev->dev, "<%s>\n", __func__); /* 获取中断号 */ irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "can't get irq number\n"); return irq; } /* 获取resource * 从设备树中获取物理基地址, 0x021a0000 */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* devmem_ioremap_resource对基地址进行映射, 可以在linux内核中得到虚拟地址 */ base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); phy_addr = (dma_addr_t)res->start; /* 资源申请 */ i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL); if (!i2c_imx) return -ENOMEM; if (of_id) i2c_imx->hwdata = of_id->data; else i2c_imx->hwdata = (struct imx_i2c_hwdata *) platform_get_device_id(pdev)->driver_data; /* Setup i2c_imx driver structure */ /* iic adapter init */ strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name)); i2c_imx->adapter.owner = THIS_MODULE; i2c_imx->adapter.algo = &i2c_imx_algo; i2c_imx->adapter.dev.parent = &pdev->dev; i2c_imx->adapter.nr = pdev->id; i2c_imx->adapter.dev.of_node = pdev->dev.of_node; i2c_imx->base = base; /* Get I2C clock */ i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c_imx->clk)) { dev_err(&pdev->dev, "can't get I2C clock\n"); return PTR_ERR(i2c_imx->clk); } ret = clk_prepare_enable(i2c_imx->clk); if (ret) { dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret); return ret; } /* Request IRQ */ /* 设置irq, i2c_imxisr为中断服务函数 */ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_NO_SUSPEND, pdev->name, i2c_imx); if (ret) { dev_err(&pdev->dev, "can't claim irq %d\n", irq); goto clk_disable; } /* Init queue */ init_waitqueue_head(&i2c_imx->queue); /* Set up adapter data */ i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); /* Set up platform driver data */ platform_set_drvdata(pdev, i2c_imx); pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) goto rpm_disable; /* Set up clock divider */ i2c_imx->bitrate = IMX_I2C_BIT_RATE; ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &i2c_imx->bitrate); if (ret < 0 && pdata && pdata->bitrate) i2c_imx->bitrate = pdata->bitrate; /* Set up chip registers to defaults */ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, i2c_imx, IMX_I2C_I2CR); imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR); /* Init optional bus recovery function */ ret = i2c_imx_init_recovery_info(i2c_imx, pdev); /* Give it another chance if pinctrl used is not ready yet */ if (ret == -EPROBE_DEFER) goto rpm_disable; /* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); if (ret < 0) goto rpm_disable; pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq); dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", i2c_imx->adapter.name); dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); /* Init DMA config if supported */ i2c_imx_dma_request(i2c_imx, phy_addr); return 0; /* Return OK */ rpm_disable: pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); clk_disable: clk_disable_unprepare(i2c_imx->clk); return ret; }
-
-
i2c_imx_algo此部分为iic adapter的核心,主要是实现i2c adapter control寄存器的相关控制
static struct i2c_algorithm i2c_imx_algo = { .master_xfer = i2c_imx_xfer, .functionality = i2c_imx_func, };
-
i2c_imx_func返回此adapter支持的功能
static u32 i2c_imx_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA; }
-
i2c_imx_xfer iic传输通过此函数来实现
/* num为读取数据的个数 */ static int i2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { unsigned int i, temp; int result; bool is_lastmsg = false; bool enable_runtime_pm = false; struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); if (!pm_runtime_enabled(i2c_imx->adapter.dev.parent)) { pm_runtime_enable(i2c_imx->adapter.dev.parent); enable_runtime_pm = true; } result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); if (result < 0) goto out; /* Start I2C transfer */ result = i2c_imx_start(i2c_imx); if (result) { if (i2c_imx->adapter.bus_recovery_info) { i2c_recover_bus(&i2c_imx->adapter); result = i2c_imx_start(i2c_imx); } } if (result) goto fail0; /* read/write data */ for (i = 0; i < num; i++) { if (i == num - 1) is_lastmsg = true; if (i) { dev_dbg(&i2c_imx->adapter.dev, "<%s> repeated start\n", __func__); /* 发送ACK */ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp |= I2CR_RSTA; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); result = i2c_imx_bus_busy(i2c_imx, 1); if (result) goto fail0; } dev_dbg(&i2c_imx->adapter.dev, "<%s> transfer message: %d\n", __func__, i); /* write/read data */ #ifdef CONFIG_I2C_DEBUG_BUS temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__, (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0), (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0), (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0)); /* 读取i2c status register */ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); dev_dbg(&i2c_imx->adapter.dev, "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__, (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0), (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0), (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0), (temp & I2SR_RXAK ? 1 : 0)); #endif if (msgs[i].flags & I2C_M_RD) /* 如果flag为read, 则此操作为读取数据 */ result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); else { /* 则此操作为写数据操作 */ if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) result = i2c_imx_dma_write(i2c_imx, &msgs[i]); else result = i2c_imx_write(i2c_imx, &msgs[i]); } if (result) goto fail0; } /* 数据读取完后发送stop信号 */ fail0: /* Stop I2C transfer */ i2c_imx_stop(i2c_imx); pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent); pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent); out: if (enable_runtime_pm) pm_runtime_disable(i2c_imx->adapter.dev.parent); dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__, (result < 0) ? "error" : "success msg", (result < 0) ? result : num); return (result < 0) ? result : num; }
-
i2c_imx_read
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bool is_lastmsg) { int i, result; unsigned int temp; int block_data = msgs->flags & I2C_M_RECV_LEN; dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n", __func__, (msgs->addr << 1) | 0x01); /* write slave address */ /* 将msg->addr 于0x01一起构造一个数据,写入I2Cx_I2DR * 0x01表示读数据 */ imx_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_imx, IMX_I2C_I2DR); /* 等待超时或中断产生 */ result = i2c_imx_trx_complete(i2c_imx); if (result) return result; /* 等待ACK */ result = i2c_imx_acked(i2c_imx); if (result) return result; dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__); /* setup bus to read data */ /* 设置控制寄存器为接受状态 */ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp &= ~I2CR_MTX; /* * Reset the I2CR_TXAK flag initially for SMBus block read since the * length is unknown */ if ((msgs->len - 1) || block_data) temp &= ~I2CR_TXAK; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */ dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); /* read data */ /* 循环读取数据 * 知道读取msgs->len */ for (i = 0; i < msgs->len; i++) { u8 len = 0; result = i2c_imx_trx_complete(i2c_imx); if (result) return result; /* * First byte is the length of remaining packet * in the SMBus block data read. Add it to * msgs->len. */ if ((!i) && block_data) { len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) return -EPROTO; dev_dbg(&i2c_imx->adapter.dev, "<%s> read length: 0x%X\n", __func__, len); msgs->len += len; } if (i == (msgs->len - 1)) { if (is_lastmsg) { /* * It must generate STOP before read I2DR to prevent * controller from generating another clock cycle */ dev_dbg(&i2c_imx->adapter.dev, "<%s> clear MSTA\n", __func__); temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); i2c_imx_bus_busy(i2c_imx, 0); i2c_imx->stopped = 1; } else { /* * For i2c master receiver repeat restart operation like: * read -> repeat MSTA -> read/write * The controller must set MTX before read the last byte in * the first read operation, otherwise the first read cost * one extra clock cycle. */ temp = readb(i2c_imx->base + IMX_I2C_I2CR); temp |= I2CR_MTX; writeb(temp, i2c_imx->base + IMX_I2C_I2CR); } } else if (i == (msgs->len - 2)) { dev_dbg(&i2c_imx->adapter.dev, "<%s> set TXAK\n", __func__); temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp |= I2CR_TXAK; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); } if ((!i) && block_data) msgs->buf[0] = len; else msgs->buf[i] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); dev_dbg(&i2c_imx->adapter.dev, "<%s> read byte: B%d=0x%X\n", __func__, i, msgs->buf[i]); } return 0; }
-