前言
简单介绍了下 mtk 平台的 I2C 适配器注册流程
基于 3.18 内核
Adapter 注册流程
参考自韦东山视频 3.4 内核
static int s3c2440_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
}
static u32 s3c2440_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
static const struct i2c_algorithm s3c2440_i2c_algo = {
// .smbus_xfer = ,
.master_xfer = s3c2440_i2c_xfer,
.functionality = s3c2440_i2c_func,
};
/* 1. 分配/设置i2c_adapter
*/
static struct i2c_adapter s3c2440_i2c_adapter = {
.name = "s3c2440_100ask",
.algo = &s3c2440_i2c_algo,
.owner = THIS_MODULE,
};
static int i2c_bus_s3c2440_init(void)
{
/* 2. 注册i2c_adapter */
i2c_add_adapter(&s3c2440_i2c_adapter);
return 0;
}
static void i2c_bus_s3c2440_exit(void)
{
i2c_del_adapter(&s3c2440_i2c_adapter);
}
module_init(i2c_bus_s3c2440_init);
module_exit(i2c_bus_s3c2440_init);
MODULE_LICENSE("GPL");
MTK 适配器注册流程
/
// 为什么确定调用此文件?
// 定义位置:非主流
./drivers/misc/mediatek/cam_cal/src/common/cat24c16/cat24c16.c:19:#ifndef CONFIG_MTK_I2C_EXTENSION
./drivers/misc/mediatek/cam_cal/src/common/cat24c16/cat24c16.c:20:#define CONFIG_MTK_I2C_EXTENSION // 【这里定义了?应该不影响吧】
./drivers/misc/mediatek/cam_cal/src/common/cat24c16/cat24c16.c:23:#undef CONFIG_MTK_I2C_EXTENSION
./drivers/misc/mediatek/cam_cal/src/common/GT24c32a/GT24c32a.c:35:#ifndef CONFIG_MTK_I2C_EXTENSION
./drivers/misc/mediatek/cam_cal/src/common/GT24c32a/GT24c32a.c:36:#define CONFIG_MTK_I2C_EXTENSION
./drivers/misc/mediatek/cam_cal/src/common/GT24c32a/GT24c32a.c:39:#undef CONFIG_MTK_I2C_EXTENSION
///
// 最终确认:
Z:\work\E266L_CMCC_0508_eng\out\target\product\E266L\obj\KERNEL_OBJ\.config
CONFIG_MTK_I2C=y
CONFIG_MTK_I2C_EXTENSION=y
/
// 使用位置:使用了哪种 i2c
./drivers/misc/mediatek/i2c/Makefile:1:ifeq ($(CONFIG_MTK_I2C_EXTENSION),y)
ifeq ($(CONFIG_MTK_I2C_EXTENSION),y)
obj-$(CONFIG_MTK_I2C) += i2c/
endif
// i2c/Makefile
obj-$(CONFIG_MTK_I2C) := i2c.o i2c_common.o
./drivers/i2c/busses/Makefile:88:ifneq ($(CONFIG_MTK_I2C_EXTENSION),y)
ifneq ($(CONFIG_MTK_I2C_EXTENSION),y) // 注册判断条件为没有定义
obj-$(CONFIG_MTK_I2C) += i2c-mtk.o
obj-$(CONFIG_MTK_I2C) += i2c_mtk_debug.o
endif
///
// i2c 总线的注册:
// I2c-core.c (kernel-3.18\drivers\i2c)
postcore_initcall(i2c_init);
static int __init i2c_init(void)
//
// struct bus_type i2c_bus_type = {
// .name = "i2c",
// .match = i2c_device_match,
// .probe = i2c_device_probe,
// .remove = i2c_device_remove,
// .shutdown = i2c_device_shutdown,
// .pm = &i2c_device_pm_ops,
// };
retval = bus_register(&i2c_bus_type);
retval = i2c_add_driver(&dummy_driver);
// I2c.c (kernel-3.18\drivers\misc\mediatek\i2c\mt6755)
【i2c 适配器初始化流程】:
module_init(mt_i2c_init);
mt_i2c_init(void)
/
// ioremap the AP_DMA base and use offset get the I2C DMA base
// dts 中的相关设备 dma 设置
// ap_dma@11000000 {
// compatible = "mediatek,ap_dma";
// reg = <0x11000000 0x1000>;
// interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_LOW>;
// };
ap_dma_node = of_find_compatible_node(NULL, NULL, "mediatek,ap_dma");
ap_dma_base = of_iomap(ap_dma_node, 0);
//
// 注册平台设备
// static struct platform_driver mt_i2c_driver = {
// .probe = mt_i2c_probe,
// .remove = mt_i2c_remove,
// .suspend = mt_i2c_suspend,
// .resume = mt_i2c_resume,
// .driver = {
// .name = I2C_DRV_NAME,
// .owner = THIS_MODULE,
// #ifdef CONFIG_OF
// .of_match_table = mt_i2c_of_match,
// // static const struct of_device_id mt_i2c_of_match[] = {
// // {.compatible = "mediatek,mt6755-i2c",},
// // {},
// // };
//
// // dts 相关项:【注册流程在 平台初始化时添加的 platform_device 】
// // // Mt6755.dtsi (z:\work\e266l_cmcc\kernel-3.18\arch\arm64\boot\dts)
// // i2c1: i2c@11008000 {
// // compatible = "mediatek,mt6755-i2c", "mediatek,i2c1";
// // cell-index = <1>;
// // reg = <0x11008000 0x1000>,
// // <0x11000180 0x80>;
// // interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_LOW>;
// // clock-div = <10>;
// // clocks = <&infrasys INFRA_I2C1>, <&infrasys INFRA_AP_DMA>;
// // clock-names = "main", "dma";
// // #address-cells = <1>;
// // #size-cells = <0>;
// // };
// #endif
// },
// };
platform_driver_register(&mt_i2c_driver);
/
// 匹配后调用 probe() 详见【平台设备驱动】看如何解析 dts, 添加 platform_device
mt_i2c_probe(struct platform_device *pdev)
/
// 这也是在解析 dts 时添加了相关硬件资源
// Request platform_device IO resource
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
request_mem_region(res->start, resource_size(res), pdev->name)
/
// 分配 MTK 封装的适配器驱动结构体
//
// struct mt_i2c_t {
// #ifdef I2C_DRIVER_IN_KERNEL
// // ==========only used in kernel================
// struct i2c_adapter adap; // i2c host adapter
// struct device *dev; // the device object of i2c host adapter
// atomic_t trans_err; // i2c transfer error
// atomic_t trans_comp; // i2c transfer completion
// atomic_t trans_stop; // i2c transfer stop
// spinlock_t lock; // for struct mt_i2c_t protection
// struct mutex mutex; // protect from different API
// wait_queue_head_t wait; // i2c transfer wait queue
// #endif
// // ==========set in i2c probe============
// void __iomem *base; // i2c base addr
// u16 id;
// u32 irqnr; // i2c interrupt number
// u16 irq_stat; // i2c interrupt status
// u32 clk; // host clock speed in khz
// u32 pdn; //clock number
// // ==========common data define============
// enum i2c_trans_st_rs st_rs;
// enum mt_trans_op op;
// void __iomem *pdmabase;
// u32 speed; // The speed (khz)
// u16 delay_len; // number of half pulse between transfers in a trasaction
// u32 msg_len; // number of bytes for transaction
// u8 *msg_buf; // pointer to msg data
// u8 addr; // The 7-bit address of the slave device
// u8 master_code; // master code in HS mode
// u8 mode; // ST/FS/HS mode
// // ==========reserved function============
// u8 is_push_pull_enable; // IO push-pull or open-drain
// u8 is_clk_ext_disable; // clk entend default enable
// u8 is_dma_enabled; // Transaction via DMA instead of 8-byte FIFO
// u8 read_flag; // read,write,read_write
// bool dma_en;
// bool poll_en;
// bool pushpull; // open drain
// bool filter_msg; // filter msg error log
// bool i2c_3dcamera_flag; // flag for 3dcamera
//
// // ==========define reg============
// u16 timing_reg;
// u16 high_speed_reg;
// u16 control_reg;
// u32 last_speed;
// u8 last_mode;
// u32 default_speed;
// struct mt_trans_data trans_data;
// struct i2c_dma_buf dma_buf;
// #if !defined(CONFIG_MTK_CLKMGR)
// struct clk *clk_main; // main clock for i2c bus
// struct clk *clk_dma; // DMA clock for i2c via DMA
// #endif
// };
i2c = kzalloc(sizeof(struct mt_i2c_t), GFP_KERNEL);
/
// 初始化 mt_i2c_t 结构体
i2c->base = of_iomap(pdev->dev.of_node, 0);
if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id))
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (of_property_read_u32(pdev->dev.of_node, "def_speed", &pdev->id))
i2c->irqnr = irq;
i2c->adap.nr = i2c->id;
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &mt_i2c_algorithm; // 核心收发函数
i2c->adap.algo_data = NULL;
i2c->adap.timeout = 2 * HZ; // 2s
i2c->adap.retries = 1; // DO NOT TRY
// need GFP_DMA32 flag to confirm DMA alloc PA is 32bit range
i2c->dma_buf.vaddr = dma_alloc_coherent(&pdev->dev, MAX_DMA_TRANS_NUM,&i2c->dma_buf.paddr, GFP_KERNEL | GFP_DMA32);
i2c->pdmabase = DMA_I2C_BASE(i2c->id, ap_dma_base);
init_waitqueue_head(&i2c->wait);
mt_i2c_clock_enable(i2c);
mt_i2c_init_hw(i2c);
mt_i2c_clock_disable(i2c);
/
// 申请 I2C 中断
ret = request_irq(i2c->irqnr, mt_i2c_irq, IRQF_TRIGGER_LOW, dev_name(&pdev->dev), i2c);
/
// i2c_add_numbered_adapter()
// 如果 dts 没有指定 id, 则动态分配一个,再进行注册
// 注册指定 id 号的 i2c 适配器
// device_register(): 这里仅是将 adapter 也放在总线的设备列表中
// 发送 uevent 事件通知用户空间
// 查找现有驱动是否有匹配当前设备的
// 1. 在 dts 中进行匹配
// 2. 匹配 i2c driver 的 id_table
// 匹配上了,调用 probe() 函数
// 首先调用总线的 i2c_device_probe() 函数, 主要根据 dts 设置通信频率
// 然后调用驱动的 probe() 函数
//
// of_i2c_register_devices(adap):
// 这里遍历的应该是 compatible = "mediatek,mt6755-i2c"
// 相关的节点,因为适配器适配时,就用的些属性值,表示其代表一个 i2c 适配器
// 那么这个节点下辖相关的,就是i2c device,需要通过 i2c_new_device() 注册
//
// 遍历调用 i2c_board_info, 注册 i2c device
//
// Detect supported devices on that bus, and instantiate them
// 遍历 drviver 的 address_list, 通过 i2c_smbus_xfer() 检测是否有设备
// 如果检测到设备:创建 i2c device
// 调用老的 i2c 驱动接口 driver->attach_adapter(adap);
ret = i2c_add_numbered_adapter(&i2c->adap);