驱动之i2c核心(designware-i2c)

<记录生活点滴>
疑问:这是那家公司的芯片,哈哈哈哈,英文的没看懂~~~
1.贴上设备树

     i2c0: i2c@28002000 {                                                                                                      
         #address-cells = <1>;                                                                                                 
         #size-cells = <0>;                                                                                                    
         compatible = "snps,designware-i2c";                                                                                   
         reg = <0x0 0x28002000 0x0 0x1000>;                                                                                    
         interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;                                                                        
         clock-frequency = <100000>;                                                                                           
         clocks = <&clk_100mhz>;                                                                                               
         status = "disabled";
      }
      address-cell size-cell :此地址和大小为下一级规则 (挂载i2c总线设备) 如:传感器,时钟等。
      snps,designware-i2c: 芯片匹配name
      reg 注意:这里是64位。。
      interrupts :<中断类型 中断源 触发方式>   为啥是高电平,我也不知道,反正就是高电平
      clock 时钟

2.开始代码gogogo

让我们一起走过注册platform_driver_register的天和地
此时省1万万万行代码。。。

probe函数来了

static int dw_i2c_plat_probe(struct platform_device *pdev)
{
struct dw_i2c_dev *dev;
struct i2c_adapter *adap;
struct resource *mem;
struct dw_i2c_platform_data *pdata;
int irq, r;
u32 clk_freq, ht = 0;

irq = platform_get_irq(pdev, 0);中断号获取,哪里获取呢,唯一:设备树
if (irq < 0)
	return irq;

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

mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);获取资源,不获取,映射什么
dev->base = devm_ioremap_resource(&pdev->dev, mem);好吧,映射
if (IS_ERR(dev->base))
	return PTR_ERR(dev->base);

dev->dev = &pdev->dev;
dev->irq = irq;
platform_set_drvdata(pdev, dev);

/* fast mode by default because of legacy reasons */
clk_freq = 400000;

if (has_acpi_companion(&pdev->dev)) {   呀呀呀,这是啥,我没看,哈哈哈,反正我不走
	dw_i2c_acpi_configure(pdev);
} else if (pdev->dev.of_node) {
	of_property_read_u32(pdev->dev.of_node,
				"i2c-sda-hold-time-ns", &ht);
	of_property_read_u32(pdev->dev.of_node,
			     "i2c-sda-falling-time-ns",
			     &dev->sda_falling_time);
	of_property_read_u32(pdev->dev.of_node,
			     "i2c-scl-falling-time-ns",
			     &dev->scl_falling_time);
	of_property_read_u32(pdev->dev.of_node, "clock-frequency",
			     &clk_freq);
	只能说我获取获取再获取(设备树)
	/* Only standard mode at 100kHz and fast mode at 400kHz
	 * are supported.
	 */
	if (clk_freq != 100000 && clk_freq != 400000) {
		dev_err(&pdev->dev, "Only 100kHz and 400kHz supported");
		return -EINVAL;
	}
} else {
	pdata = dev_get_platdata(&pdev->dev);
	if (pdata)
		clk_freq = pdata->i2c_scl_freq;
}

r = i2c_dw_eval_lock_support(dev);
if (r)
	return r;

dev->functionality =
	I2C_FUNC_I2C |
	I2C_FUNC_10BIT_ADDR |
	I2C_FUNC_SMBUS_BYTE |
	I2C_FUNC_SMBUS_BYTE_DATA |
	I2C_FUNC_SMBUS_WORD_DATA |
	I2C_FUNC_SMBUS_I2C_BLOCK;
时钟问题:很关键。人生信条:越慢越好。。100K,快速模式不建议使用,为啥?听别人说的
if (clk_freq == 100000)
	dev->master_cfg =  DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
		DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_STD;
else
	dev->master_cfg =  DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
		DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;

dev->clk = devm_clk_get(&pdev->dev, NULL);
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
if (IS_ERR(dev->clk))
	return PTR_ERR(dev->clk);
clk_prepare_enable(dev->clk);

这个下面会有解释,往下看→
if (!dev->sda_hold_time && ht) {
	u32 ic_clk = dev->get_clk_rate_khz(dev);
	dev->sda_hold_time = div_u64((u64)ic_clk * ht + 500000,
				     1000000);
}

此时:很关键,决定了:发送和接受缓存区深度(系统的,非i2c模块的)  中断触发 配合使用  
if (!dev->tx_fifo_depth) {
	u32 param1 = i2c_dw_read_comp_param(dev);/*这个参数,参考手册*/
	dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
	dev->rx_fifo_depth = ((param1 >> 8)  & 0xff) + 1;
	dev->adapter.nr = pdev->id;
}

adap = &dev->adapter;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->dev.of_node = pdev->dev.of_node;

if (dev->pm_runtime_disabled) {
	pm_runtime_forbid(&pdev->dev);
} else {
	pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
	pm_runtime_use_autosuspend(&pdev->dev);
	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
}

好了,开始接下来的i2c_dw真正配置函数了,
r = i2c_dw_probe(dev);
if (r && !dev->pm_runtime_disabled)
	pm_runtime_disable(&pdev->dev);

return r;
}

i2c_dw 算是核心了吧,开始

int i2c_dw_probe(struct dw_i2c_dev *dev)
{
struct i2c_adapter *adap = &dev->adapter;
int r;
初始化初始化,锁和complete。。。
init_completion(&dev->cmd_complete);
mutex_init(&dev->lock);

i2c_dw初始化,明显就是寄存器这设置,那设置,反正就是设置
r = i2c_dw_init(dev);
if (r)
	return r;

snprintf(adap->name, sizeof(adap->name),
	 "Synopsys DesignWare I2C adapter");
adap->algo = &i2c_dw_algo;  algo 算法,厉害了,通过他传输的。有人说他比较叼,但是看起来很渣,不知道为									啥,很无奈呀
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);

关机使能,关机中断,关闭关闭
i2c_dw_disable_int(dev);
关闭了,开始申请中断函数吧
r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, IRQF_SHARED,
		     dev_name(dev->dev), dev);
if (r) {
	dev_err(dev->dev, "failure requesting irq %i: %d\n",
		dev->irq, r);
	return r;
}

r = i2c_add_numbered_adapter(adap);
if (r)
	dev_err(dev->dev, "failure adding adapter: %d\n", r);

return r;
}

接下来看啥呢,初始化吧,没啥用

int i2c_dw_init(struct dw_i2c_dev *dev)
{
u32 input_clock_khz;
u32 hcnt, lcnt;
u32 reg;
u32 sda_falling_time, scl_falling_time;
int ret;

if (dev->acquire_lock) {
	ret = dev->acquire_lock(dev);
	if (ret) {
		dev_err(dev->dev, "couldn't acquire bus ownership\n");
		return ret;
	}
}

输出时钟时钟
input_clock_khz = dev->get_clk_rate_khz(dev);

reg = dw_readl(dev, DW_IC_COMP_TYPE);
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
	/* Configure register endianess access */
	dev->accessor_flags |= ACCESS_SWAP;
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
	/* Configure register access mode 16bit */
	dev->accessor_flags |= ACCESS_16BIT;
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
	dev_err(dev->dev, "Unknown Synopsys component type: "
		"0x%08x\n", reg);
	if (dev->release_lock)
		dev->release_lock(dev);
	return -ENODEV;
}

/* Disable the adapter */
__i2c_dw_enable(dev, false);

/* set standard and fast speed deviders for high/low periods */

sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */

这个就重要了,直接影响到了时钟DW_IC_SS_SCL_HCNT DW_IC_SS_SCL_LCNT高电平和低电平时钟,直接影响了时钟频率
/* Set SCL timing parameters for standard-mode */
if (dev->ss_hcnt && dev->ss_lcnt) {
	hcnt = dev->ss_hcnt;
	lcnt = dev->ss_lcnt;
} else {
	hcnt = i2c_dw_scl_hcnt(input_clock_khz,
				4000,	/* tHD;STA = tHIGH = 4.0 us */
				sda_falling_time,
				0,	/* 0: DW default, 1: Ideal */
				0);	/* No offset */
	lcnt = i2c_dw_scl_lcnt(input_clock_khz,
				4700,	/* tLOW = 4.7 us */
				scl_falling_time,
				0);	/* No offset */
}
dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
高速算了,不解释,不用
/* Set SCL timing parameters for fast-mode */
if (dev->fs_hcnt && dev->fs_lcnt) {
	hcnt = dev->fs_hcnt;
	lcnt = dev->fs_lcnt;
} else {
	hcnt = i2c_dw_scl_hcnt(input_clock_khz,
				600,	/* tHD;STA = tHIGH = 0.6 us */
				sda_falling_time,
				0,	/* 0: DW default, 1: Ideal */
				0);	/* No offset */
	lcnt = i2c_dw_scl_lcnt(input_clock_khz,
				1300,	/* tLOW = 1.3 us */
				scl_falling_time,
				0);	/* No offset */
}
dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);

/* Configure SDA Hold Time if required */
if (dev->sda_hold_time) {
	reg = dw_readl(dev, DW_IC_COMP_VERSION);
	if (reg >= DW_IC_SDA_HOLD_MIN_VERS)
		dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
	else
		dev_warn(dev->dev,
			"Hardware too old to adjust SDA hold time.");
}
DW_IC_TX_TL   DW_IC_RX_TL这两个寄存器厉害了,直接影响中断,接受缓存区放几个触发中断
															发送就看你发几个了
/* Configure Tx/Rx FIFO threshold levels */
dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
dw_writel(dev, 0, DW_IC_RX_TL);

/* configure the i2c master */
dw_writel(dev, dev->master_cfg , DW_IC_CON);

if (dev->release_lock)
	dev->release_lock(dev);
return 0;
}
EXPORT_SYMBOL_GPL(i2c_dw_init);

好像没啥也,直接来算法吧

static struct i2c_algorithm i2c_dw_algo = {
.master_xfer	= i2c_dw_xfer,
.functionality	= i2c_dw_func,
};
i2c_dw_xfer 会被i2c 从设备驱动调用,等着被调用

今天到这里,明天写写调用流程。。。很烦

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值