<记录生活点滴>
疑问:这是那家公司的芯片,哈哈哈哈,英文的没看懂~~~
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 从设备驱动调用,等着被调用
今天到这里,明天写写调用流程。。。很烦