目录
寄存器篇
修改寄存器
intel I2C 驱动结构
lpss-pci文件
lpss文件
驱动结构
Synopsys DesignWare I2C
BIOS配置修改
ACPI表的查看
I2C速率
寄存器篇
修改速率很简单,看到手册里面的寄存器说明,然后将其改掉即可。
寄存器偏移量为0,bit2:1修改为不同值可以配置为100和400两个速率。
修改寄存器
在用户态修改寄存器很简单。
1) 首先查看设备的地址段
我们看到 15.0 I2C控制器占用的地址段 为 0xdf930000开始。
于是我们编写一个用户态mmap映射的程序,进行测试。由于地址空间已经被内核驱动占用,导致mmap读取访问返回为FF。
2)所以我们先卸载掉驱动。
rmmod intel_lpss_pci
同时又需要将设备pcie配置空间相关操作使能
setpci -s 0:15.0 4.B=6
3) 修改寄存器
./pcie_test 0x8086 0xa160
bar phyaddr:0xdf930000
Memory mapped at address 0x7fda2d086000.
bar map virtual address 0x7fda2d086000.
00000075 读取的值为75,即bit1-2为10 400K
./write_pci 0x8086 0xa160 将寄存器写为100K
这时,我们将lpss驱动重新加载,发现寄存器的值又变成了400K。也就是驱动本身还会改这个寄存器,手动修改寄存器改速率这条路是不同的。
intel I2C 驱动结构
于是我们只能寄希望于驱动的修改。但是明明我们加载的驱动是lpss,但是i2cdetect -l查询到的驱动信息却是:i2c-designware ,而这个又是编译到内核中的,并非module模块!!
首先,intel I2C驱动涉及到的文件有:
文件 | 功能 |
intel-lpss-pci.c drivers\mfd | pci probe驱动的入口,各个参数配置 |
intel-lpss.c drivers\mfd | 统一的注册,使能接口供第一行使用 |
lpss-pci文件
在这个文件中,实际上主要关心如下两个结构体的配置,即可,最终我们的速率修改在这里可以完成。
static const struct intel_lpss_platform_info bxt_uart_info = {
.clk_rate = 100000000,
.clk_con_id = "baudclk",
.properties = uart_properties,
};
static struct property_entry bxt_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 42),
PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171),
PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208),
{ },
};
lpss文件
这里主要关注mfd_cell这个结构体,这里name即描述了此cell所使用的驱动。由此我们可以找到最终实现I2C协议的驱动。
static const struct mfd_cell intel_lpss_i2c_cell = {
.name = "i2c_designware",
.num_resources = ARRAY_SIZE(intel_lpss_dev_resources),
.resources = intel_lpss_dev_resources,
};
static const struct mfd_cell intel_lpss_uart_cell = {
.name = "dw-apb-uart",
.num_resources = ARRAY_SIZE(intel_lpss_dev_resources),
.resources = intel_lpss_dev_resources,
};
以及添加mfd设备的接口
ret = mfd_add_devices(dev, lpss->devid, lpss->cell,
1, info->mem, info->irq, NULL);
驱动结构
于是我们大概得出这样一个驱动结构
即从左到右,pcie设备--》pcie驱动--》mfd_cell设备--》i2c驱动
Synopsys DesignWare I2C
驱动文件: i2c-designware-platdrv.c drivers\i2c\busses
如下代码中的“” 中的字段与lpss-pci 文件中的两个数据结构对应,于是如果我们想修改速率,则添加clock-frequency字段,并将其配置为100K或者400K即可。
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
{
bool u = use_defaults;
u32 d;
i2c_parse_timing(dev, "clock-frequency", &t->bus_freq_hz,
I2C_MAX_STANDARD_MODE_FREQ, u);
d = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 :
t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns, d, u);
d = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
i2c_parse_timing(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns, d, u);
i2c_parse_timing(dev, "i2c-scl-internal-delay-ns",
&t->scl_int_delay_ns, 0, u);
i2c_parse_timing(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns,
t->scl_fall_ns, u);
i2c_parse_timing(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, u);
i2c_parse_timing(dev, "i2c-digital-filter-width-ns",
&t->digital_filter_width_ns, 0, u);
i2c_parse_timing(dev, "i2c-analog-filter-cutoff-frequency",
&t->analog_filter_cutoff_freq_hz, 0, u);
}
实际速率调整,这里我们看到除了从驱动里面解析速率,还从ACPI表里面解析:
void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev)
{
u32 acpi_speed = i2c_dw_acpi_round_bus_speed(dev->dev);
struct i2c_timings *t = &dev->timings;
/*
* Find bus speed from the "clock-frequency" device property, ACPI
* or by using fast mode if neither is set.
*/
if (acpi_speed && t->bus_freq_hz)
t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed);
else if (acpi_speed || t->bus_freq_hz)
t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
else
t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ;
}
1) 两者如果都存在,则取最小值
2) 如果只有一个存在,则取最大值,实际上,驱动里面默认 clock-frequency并没有配置,因而实际的速率是由bios中acpi进行配置的。
3) 如果两个都不存在,则默认400K
至此,我们已经知道如何在驱动里面配置。
以上是 platform的例子,此外i2c-designware-pcidrv.c drivers\i2c\busses 在哪些设备上使用,请自己分析,比较简单。
BIOS配置修改
通过上面的分析,我们已经知晓如何修改了。但是由于ubuntu等商用系统,即使编译出驱动,加载可能也会遇到各种问题,那么我们是否可以在bios里面修改?
ACPI表的查看
1) sudo cat /sys/firmware/acpi/tables/DSDT > DSDT.aml
2)安装软件 iasl
sudo apt-get install iasl
3) 进行转换
iasl -d DSDT.aml
这样你就可以在当前目录下发现你所要的ACPI表文件
DSDT.dsl
I2C速率
此处蓝色部分即为400K的16进展,修改此处即可修改上述驱动代码中读取到的ACPI中的速率值。