2021-04-02

一、从地址映射

例如:实际地址0x08(0001000) 打印出来的从地址写:0x11(00010001),读0x10(00010000)
原因:映射时,从地址向左移一位,写则在最低位写1,读则写0.因此,对于i2c有两个地址
参考资料:
https://www.cnblogs.com/aspirs/p/12371237.html

二、I2C 驱动注册过程

2.1 module_init(mt_i2c_init); 模块注册流程:

module_init(drv)
->__initcall(x)
-> device_initcall(fn)
-> __define_initcall(fn, 6)//这个6代表启动时顺序等级,还有如下等级,如果想要自己所在模块提前启动,可以使用其他的宏
include/linux/init.h

#define pure_initcall(fn)		__define_initcall(fn, 0)
#define core_initcall(fn)		__define_initcall(fn, 1)
#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
#define postcore_initcall(fn)		__define_initcall(fn, 2)
#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)
#define arch_initcall(fn)		__define_initcall(fn, 3)
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
#define subsys_initcall(fn)		__define_initcall(fn, 4)
#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)
#define fs_initcall(fn)			__define_initcall(fn, 5)
#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)
#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)
#define device_initcall(fn)		__define_initcall(fn, 6)
#define device_initcall_sync(fn)	__define_initcall(fn, 6s)
#define late_initcall(fn)		__define_initcall(fn, 7)
#define late_initcall_sync(fn)		__define_initcall(fn, 7s)

初始化在SMP 模块之前,只适合非嵌入模块
#define early_initcall(fn) __define_initcall(fn, early)

init_module中调用load_module然后将用户传入的内核文件创建为一整个内核模块,返回一个module结构体,系统即以此表示嵌入的模块。
include/linux/module.h

struct module
{
	enum module_state state;
 
	/* Member of list of modules */
	struct list_head list;
 
	/* Unique handle for this module */
	char name[MODULE_NAME_LEN];
	/*省略下面的内容*/
}  state是一个枚举型变量,代表模块的状态
    enum module_state
    {
    	MODULE_STATE_LIVE,		//模块存活,也就是已经加载
    	MODULE_STATE_COMING,	//模块已经加载,正在初始化
    	MODULE_STATE_GOING,		//模块正在被卸载
        MODULE_STATE_UNFORMED,//模块正在加载
    };

2.2 i2c init 函数操作

drivers/i2c/busses/i2c-mtk.c
1、 enable_arbitration();//配置pericfg寄存器,使能I2C
-> pericfg_node = of_find_compatible_node(NULL, NULL, “mediatek,pericfg”);
2、 if (!map_dma_regs()) 配置寄存器
3、 if (!mt_i2c_parse_comp_data()) 获取设备共用参数(设备树)
4、 register_syscore_ops(&mtk_i2c_syscore_ops); 向系统注册i2c操作函数(挂起和释放)
5、 platform_driver_register(&mt_i2c_driver);注册I2C平台驱动

2.3 平台驱动注册

platform_driver_register
-> __platform_driver_register(drv, THIS_MODULE)
-> driver_register(&drv->driver);
在驱动注册的时候,无论是平台总线,I2C、 SPI 总线,最终调用到的函数都是driver_register

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);//判断是否已经注册了
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);//将驱动加入到注册表
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}

2.4 I2C 驱动Probe 流程

前面提及了,所有模块驱动init后都会调用到driver_register。之后便是驱动设备匹配,跑probe函数了。大致流程如下:
drivers/base/dd.c drivers/base/bus.c drivers/base/*
driver_register –》bus_add_driver –》driver_attach –》bus_for_each_dev ->__driver_attach(无论是)
1、 ID 匹配
–》driver_match_device
–》(drv->bus->match ? drv->bus->match(dev, drv) : 1;)
-》platform_match
2、匹配完成后probe函数调用
-》driver_probe_device(drv, dev);
-》really_probe(dev, drv);
2.1这里会有两种probe方式
if (dev->bus->probe) {//注册设备时,发起的匹配
-》dev->bus->probe(dev);
else if (drv->probe) {
-》drv->probe(dev);

drivers/base/bus.c

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);//此处调用匹配函数__driver_attach,进行匹配,如果匹配到多个节点,则probe也会多次运行
	klist_iter_exit(&i);
	return error;
}

存在5种匹配方式,暂时未深入研究

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table) //(常用)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

前面已经提及了,在匹配过后,代码会根据匹配的类型调用probe函数,获取到dev设备,进行I2C的配置了

static int mt_i2c_probe(struct platform_device *pdev)
{
//1、解析dts树,获取到设置资源
	i2c = devm_kzalloc(&pdev->dev, sizeof(struct mt_i2c), GFP_KERNEL);

	ret = mt_i2c_parse_dt(pdev->dev.of_node, i2c);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//后两个变量定位资源
	i2c->base = devm_ioremap_resource(&pdev->dev, res);//IO映射得到内存中的地址

	if (i2c->id < I2C_MAX_CHANNEL)//最大10路
		g_mt_i2c[i2c->id] = i2c;//保存I2C设备,是属于哪路I2C的从设备

	if (!i2c->fifo_only) {//FIFO 类型
		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
		i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
	}
	//GPIO 地址
i2c->gpiobase = devm_ioremap(&pdev->dev, i2c->gpio_start, i2c->mem_len);
    //中断号
	i2c->irqnr = platform_get_irq(pdev, 0);

	init_waitqueue_head(&i2c->wait);
    //中断设置
	ret = devm_request_irq(&pdev->dev, i2c->irqnr, mt_i2c_irq,
		IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
	of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
    //I2C适配器设置,属于平台层面的,一个适配器可能管理多路I2C设备
	i2c->dev_comp = of_id->data;
	i2c->i2c_pll_info = &i2c_pll_info;
	i2c->adap.dev.of_node = pdev->dev.of_node;
	i2c->dev = &i2c->adap.dev;
	i2c->adap.dev.parent = &pdev->dev;
	i2c->adap.owner = THIS_MODULE;
	i2c->adap.algo = &mt_i2c_algorithm;
	i2c->adap.algo_data = NULL;
	i2c->adap.timeout = 2 * HZ;
	i2c->adap.retries = 1;
	i2c->adap.nr = i2c->id;
	spin_lock_init(&i2c->cg_lock);

//MDA 大小匹配,在dts中配置,I2C Init时会解析到Match ID的data结构体中

if (i2c->dev_comp->dma_support == MDA_SUPPORT_8G) {
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(33))) {
dev_info(&pdev->dev, “dma_set_mask return error.\n”);
return -EINVAL;
}
} else if (i2c->dev_comp->dma_support == DMA_SUPPORT_64G) {
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(36))) {
dev_info(&pdev->dev, “dma_set_mask return error.\n”);
return -EINVAL;
}
}

//以下是时钟配置,开始

#if !defined(CONFIG_MT_I2C_FPGA_ENABLE)
	i2c->clk_main = devm_clk_get(&pdev->dev, "main");
	if (IS_ERR(i2c->clk_main)) {
		dev_info(&pdev->dev, "cannot get main clock\n");
		return PTR_ERR(i2c->clk_main);
	}
	i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
	if (IS_ERR(i2c->clk_dma)) {
		if (!i2c->fifo_only) {
			dev_info(&pdev->dev, "cannot get dma clock\n");
			return PTR_ERR(i2c->clk_dma);
		}
		i2c->clk_dma = NULL;
	}
	i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
	if (IS_ERR(i2c->clk_arb))
		i2c->clk_arb = NULL;
	else
		dev_dbg(&pdev->dev, "i2c%d has the relevant arbitrator clk.\n",
			i2c->id);
	i2c->clk_pal = devm_clk_get(&pdev->dev, "pal");
	if (IS_ERR(i2c->clk_pal))
		i2c->clk_pal = NULL;
	else
		dev_dbg(&pdev->dev, "i2c%d has the relevant pal clk.\n",
			i2c->id);

//锁相环设定(PLL)

if (i2c->i2c_pll_info->clk_mux == NULL) {
		i2c->i2c_pll_info->clk_mux = devm_clk_get(&pdev->dev, "mux");
		if (IS_ERR(i2c->i2c_pll_info->clk_mux)) {
			i2c->i2c_pll_info->clk_mux = NULL;
			dev_info(&pdev->dev, "cannot get mux clock\n");
		} else
			dev_info(&pdev->dev,
				"i2c%d has the relevant clk_mux clk.\n",
				i2c->id);
	}
	if (i2c->i2c_pll_info->clk_p_main == NULL) {
		i2c->i2c_pll_info->clk_p_main =
			devm_clk_get(&pdev->dev, "p_main");
		if (IS_ERR(i2c->i2c_pll_info->clk_p_main)) {
			i2c->i2c_pll_info->clk_p_main = NULL;
			dev_info(&pdev->dev, "cannot get p_main clock\n");
		} else
			dev_info(&pdev->dev,
				"i2c%d has the relevant clk_p_main clk.\n",
				i2c->id);
	}
	if (i2c->i2c_pll_info->clk_p_univ == NULL) {
		i2c->i2c_pll_info->clk_p_univ =
			devm_clk_get(&pdev->dev, "p_univ");
		if (IS_ERR(i2c->i2c_pll_info->clk_p_univ)) {
			i2c->i2c_pll_info->clk_p_univ = NULL;
			dev_info(&pdev->dev, "cannot get p_univ clock\n");
		} else
			dev_info(&pdev->dev,
				"i2c%d has the relevant clk_p_univ clk.\n",
				i2c->id);
	}
#endif
if (i2c->have_pmic) {
	i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
	if (IS_ERR(i2c->clk_pmic)) {
		dev_info(&pdev->dev, "cannot get pmic clock\n");
		return PTR_ERR(i2c->clk_pmic);
	}
    //换算时钟,pmic出来的时钟除以分频倍数
	clk_src_in_hz = clk_get_rate(i2c->clk_pmic) / i2c->clk_src_div;
} else {
	clk_src_in_hz = clk_get_rate(i2c->clk_main) / i2c->clk_src_div;
}
i2c->main_clk = clk_src_in_hz;
dev_info(&pdev->dev, "i2c%d clock source %p,clock src frequency %d\n",
	i2c->id, i2c->clk_main, clk_src_in_hz);

//时钟选择 结束
//设置I2C 速度

strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
	mutex_init(&i2c->i2c_mutex);
	ret = i2c_set_speed(i2c, clk_src_in_hz);

	ret = mt_i2c_clock_enable(i2c);
	mt_i2c_init_hw(i2c);//配置寄存器
	mt_i2c_clock_disable(i2c);
	if (i2c->ch_offset_default)
		i2c->dma_buf.vaddr = dma_alloc_coherent(&pdev->dev,
			(PAGE_SIZE * 2), &i2c->dma_buf.paddr, GFP_KERNEL);
	else
		i2c->dma_buf.vaddr = dma_alloc_coherent(&pdev->dev,
			PAGE_SIZE, &i2c->dma_buf.paddr, GFP_KERNEL);

	if (i2c->dma_buf.vaddr == NULL) {
		dev_info(&pdev->dev, "dma_alloc_coherent fail\n");
		return -ENOMEM;
	}
	i2c_set_adapdata(&i2c->adap, i2c);//设置I2C适配器。I2C数据
	/* ret = i2c_add_adapter(&i2c->adap); */
	ret = i2c_add_numbered_adapter(&i2c->adap);
	if (ret) {
		dev_info(&pdev->dev, "Failed to add i2c bus to i2c core\n");
		free_i2c_dma_bufs(i2c);
		return ret;
	}
	platform_set_drvdata(pdev, i2c);//保存I2C数据到Pdev结构体中

	if (!map_cg_regs(i2c))
		pr_info("Map cg regs successfully.\n");

	return 0;
}

device 与drv 注册流程图

在这里插入图片描述
参考博客资料:

https://blog.csdn.net/happy987818/article/details/74741466
https://blog.csdn.net/Richard_LiuJH/article/details/48245715?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=1328666.20549.16160529210091425&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值