34 IIC(十二)IIC Adapter 源码分析

发布时间 2023-07-23 14:04:56作者: 人民广场的二道贩子

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;
      }