Linux下DM644x设备驱动I2C之总线驱动(一)详解

2023-11-18

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:200803090209@zjut.comgzzaigcn2012@gmail.com

 
linux DM6441下I2C设备驱动的开发
前言:
和常用的Linux I2C的驱动开发一样,主要包括总线Bus驱动,以及相应的设备驱动开发。
总线驱动和CPU的关系较为密切,涉及到相关硬件的操作。
本文以Linux2.6.1和Dm6441简单解析一下整个驱动的编写架构。
 
1.   总线驱动
涉及到总线驱动的相关代码在drivers/i2c/bussses下面,该目录下是各CPU的I2C模块涉及到的内容,这里我们选择i2c-davinci.c文件
总线驱动中Init函数i2c_davinci_init如下:
static int __init i2c_davinci_init(void)
{
	int status;
	struct device 	*dev = NULL;

	DEB0("%s %s", __TIME__, __DATE__);

	DEB1("i2c_davinci_init()");

#if 0
	if (i2c_davinci_busFreq > 200)
		i2c_davinci_busFreq = 400;	/*Fast mode */
	else
		i2c_davinci_busFreq = 100;	/*Standard mode */
#endif

	i2c_clock = clk_get (dev, "I2CCLK"); //获取I2C输入时钟批频率27MHz

	if (IS_ERR(i2c_clock))
        	return -1;

	clk_use (i2c_clock);
	clk_enable (i2c_clock);
	i2c_davinci_inputClock = clk_get_rate (i2c_clock);    //获取I2C输入时钟批频率27MHz

	DEB1 ("IP CLOCK = %ld", i2c_davinci_inputClock);

	memset(&i2c_davinci_dev, 0, sizeof(i2c_davinci_dev));
	init_waitqueue_head(&i2c_davinci_dev.cmd_wait);

	i2c_davinci_dev.regs = (davinci_i2cregsovly)I2C_BASE;      //I2C控制器相关寄存器所在地址

	status = (int)request_region(I2C_BASE, I2C_IOSIZE, MODULE_NAME); //申请一块IO输入输出区域#define MODULE_NAME "DaVinci I2C"
	if (!status) {
		i2c_err("I2C is already in use\n");
		return -ENODEV;
	}

	status = request_irq(IRQ_I2C, i2c_davinci_isr, 0, "i2c",
			     &i2c_davinci_dev);  //申请I2C中断号IRQ_I2C=39
	if (status) {
		i2c_err("failed to request I2C IRQ");
		goto do_release_region;
	}

	i2c_set_adapdata(&i2c_davinci_adap, &i2c_davinci_dev);  //adap设备的驱动程序私有数据i2c_davinci_adap->dev->driver_data =i2c_davinci_dev
	status = i2c_add_adapter(&i2c_davinci_adap);    //I2C上添加adapter
	if (status) {
		i2c_err("failed to add adapter");
		goto do_free_irq;
	}

	i2c_davinci_reset(&i2c_davinci_dev);   //完成I2C相关寄存器的初始化

	if (driver_register(&davinci_i2c_driver) != 0)            // I2C驱动注册
		printk(KERN_ERR "Driver register failed for davinci_i2c\n");
	if (platform_device_register(&davinci_i2c_device) != 0) {
		printk(KERN_ERR "Device register failed for i2c\n");
		driver_unregister(&davinci_i2c_driver);
	}

	return 0;

      do_free_irq:
	free_irq(IRQ_I2C, &i2c_davinci_dev);
      do_release_region:
	release_region(I2C_BASE, I2C_IOSIZE);

	return status;
}

操作主要围绕以下4个结构体展开:

static struct i2c_algorithm i2c_davinci_algo = {            //主要涉及相关的I2C通信协议以及数据的传输和接受
	.name = "DAVINCI I2C algorithm",
	.id = I2C_ALGO_EXP,
	.master_xfer = i2c_davinci_xfer,
	.smbus_xfer = NULL,
	.slave_send = NULL,
	.slave_recv = NULL,
	.algo_control = NULL,
	.functionality = i2c_davinci_func,
};

static struct i2c_adapter i2c_davinci_adap = {       //对I2C通信协议的封装,定义的适配器实例
	.owner = THIS_MODULE,
	.name = "DAVINCI I2C adapter",
	.id = I2C_ALGO_EXP,
	.algo = &i2c_davinci_algo,
	.algo_data = NULL,
	.client_register = NULL,
	.client_unregister = NULL,
};

static struct device_driver davinci_i2c_driver = {            //达芬奇的设备驱动
	.name = "davinci_i2c",
	.bus = &platform_bus_type,
	.remove = davinci_i2c_remove,
};

static struct platform_device davinci_i2c_device = {         //定义的达芬奇平台设备
	.name = "i2c",
	.id = -1,
	.dev = {
		.driver = &davinci_i2c_driver,
		.release = davinci_i2c_device_release,
		},
};

结合以上两段代码,可以知道以下内容

1 .在模块初始化函数中完成适配器与设备。的连接了设备驱动davinci_i2c_driver和平台设备davinci_i2c_device的注册

2.获取I2C相关端口的申请,I2C相应寄存器的物理首地址0x01C21000,完成了I2C模块初始化操作以及IRQ_I2C=39的中断申请。

在这里对I2C模块的初始化进行解析,内容如下:

static int i2c_davinci_reset(struct i2c_davinci_device *dev)
{
	u16 psc;
	u32 clk;

        DEB1("i2c: reset called");

	/* put I2C into reset */
	dev->regs->icmdr &= ~DAVINCI_I2C_ICMDR_IRS_MASK; //首先需复位I2C模块

        /* NOTE: I2C Clock divider programming info
 	 * As per I2C specs the following formulas provide prescalar
         * and low/high divider values
 	 *
 	 * input clk --> PSC Div -----------> ICCL/H Div --> output clock
 	 *                       module clk
 	 *
 	 * output clk = module clk / (PSC + 1) [ (ICCL + d) + (ICCH + d) ]
 	 *
 	 * Thus,
 	 * (ICCL + ICCH) = clk = (input clk / ((psc +1) * output clk)) - 2d;
 	 *
 	 * where if PSC == 0, d = 7,
 	 *       if PSC == 1, d = 6
 	 *       if PSC > 1 , d = 5
 	 */

	psc = 26; /* To get 1MHz clock */

  clk = ((i2c_davinci_inputClock/(psc + 1)) / (i2c_davinci_busFreq * 1000)) - 10; //i2c_davinci_busFreq=20

	dev->regs->icpsc = psc;  //PSC寄存器写26,即预分频数27
	dev->regs->icclkh = (50 * clk) / 100; /* duty cycle should be 50% */
	dev->regs->icclkl = (clk - dev->regs->icclkh); //高低电平配置内容

	DEB1("CLK  = %d", clk);
	DEB1("PSC  = %d", dev->regs->icpsc);
	DEB1("CLKL = %d", dev->regs->icclkl);
	DEB1("CLKH = %d", dev->regs->icclkh);

	/* Set Own Address: */
	dev->regs->icoar = i2c_davinci_own_addr; //oxa

	/* Enable interrupts */
	dev->regs->icimr = I2C_DAVINCI_INTR_ALL;   //所有中断都使能

	/* Take the I2C module out of reset: */
	dev->regs->icmdr |= DAVINCI_I2C_ICMDR_IRS_MASK;    //使能I2C模块

	return 0;
}

主要完成Dm6441上I2C的总线时钟的配置。以及做一些中断使能的初始化。在这里都是对dev->regs这个结构体指针的内容

该结构体的实际内容如下:

typedef struct {
	u16 icoar;
	u8 rsvd0[2];
	u16 icimr;
	u8 rsvd1[2];
	u16 icstr;
	u8 rsvd2[2];
	u16 icclkl;
	u8 rsvd3[2];
	u16 icclkh;
	u8 rsvd4[2];
	u16 iccnt;
	u8 rsvd5[2];
	u16 icdrr;
	u8 rsvd6[2];
	u16 icsar;
	u8 rsvd7[2];
	u16 icdxr;
	u8 rsvd8[2];
	u16 icmdr;
	u8 rsvd9[2];
	u16 icivr;
	u8 rsvd10[2];
	u16 icemdr;
	u8 rsvd11[2];
	u16 icpsc;
	u8 rsvd12[2];
	u16 icpid1;
	u8 rsvd13[2];
	u16 icpid2;
	u8 rsvd14[14];
	u32 ipcfunc;
	u32 icpdir;
	u32 icpdin;
	u32 icpdout;
	u32 icpdset;
	u32 icpdclr;
} davinci_i2cregs;

/**************************************************************************\
* Overlay structure typedef definition
\**************************************************************************/
typedef volatile davinci_i2cregs *davinci_i2cregsovly;

涉及的内容是DM6441 I2C模块的相关寄存器。在获取寄存器的物理首地址之后,就可以直接对这个结构体实例的内容写数值,该结构体被包含在以下结构体中

struct i2c_davinci_device {
	int cmd_complete, cmd_err;
	wait_queue_head_t cmd_wait;
	u8 *buf;
	size_t buf_len;
	davinci_i2cregsovly regs;
};

 

这里的另一块内容是添加适配器到I2C驱动中使用i2c_add_adapter函数完成i2c_davinci_adap(该结构体实例内容见上述代码)

int i2c_add_adapter(struct i2c_adapter *adap)
{
	int id, res = 0;
	struct list_head   *item;
	struct i2c_driver  *driver;

	down(&core_lists);

	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) {
		res = -ENOMEM;
		goto out_unlock;
	}

	res = idr_get_new(&i2c_adapter_idr, NULL, &id);
	if (res < 0) {
		if (res == -EAGAIN)
			res = -ENOMEM;
		goto out_unlock;
	}

	adap->nr =  id & MAX_ID_MASK;
	init_MUTEX(&adap->bus_lock);
	init_MUTEX(&adap->clist_lock);
	list_add_tail(&adap->list,&adapters);
	INIT_LIST_HEAD(&adap->clients);

	/* Add the adapter to the driver core.
	 * If the parent pointer is not set up,
	 * we add this adapter to the host bus.
	 */
	if (adap->dev.parent == NULL)
		adap->dev.parent = &platform_bus;
	sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
	adap->dev.driver = &i2c_adapter_driver;   //适配器的设备驱动指向i2c_adapter_driver
	adap->dev.release = &i2c_adapter_dev_release;
	device_register(&adap->dev);                       //适配器设备注册
	device_create_file(&adap->dev, &dev_attr_name);

	/* Add this adapter to the i2c_adapter class */
	memset(&adap->class_dev, 0x00, sizeof(struct class_device));
	adap->class_dev.dev = &adap->dev;
	adap->class_dev.class = &i2c_adapter_class;
	strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
	class_device_register(&adap->class_dev);

	/* inform drivers of new adapters */
	list_for_each(item,&drivers) {
		driver = list_entry(item, struct i2c_driver, list);
		if (driver->flags & I2C_DF_NOTIFY)
			/* We ignore the return code; if it fails, too bad */
			driver->attach_adapter(adap);
	}

	dev_dbg(&adap->dev, "registered as adapter #%d\n", adap->nr);

out_unlock:
	up(&core_lists);
	return res;
}

在该函数内主要完成适配器结构体中成员变量设备dev的初始化,紧接着完成适配器这个抽象设备的注册,再完成适配器设备类的注册以及相关设备节点的生成。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux下DM644x设备驱动I2C之总线驱动(一)详解 的相关文章

随机推荐

  • LayUI中的基本元素之面板

    前言 最近在准备找工作的事项 但是作为一个后台也是需要了解一些前端框架的 就目前的来说有大火的VUE 但是VUE还是存在一定的学习成本 所以决定先从对后台友好的LayUI开始入手先做一些小项目练练手 后面会考虑使用VUE ElementUI
  • React.Component

    React Component 本章节提供了 React class 组件的详细 API 参考 本章节默认你已熟悉基本的 React 概念 例如 组件 Props 以及 State 生命周期等 概览 React 的组件可以定义为 class
  • teacher-student network

    最近读到一篇文章 An On device Deep Neural Network for Face Detection 讲的是苹果如何将基于深度学习的人脸识别方法应用到iPhone上 同时解决多任务并行及能耗的问题 文中提到了一个teac
  • python第三方库概述_计算机二级python:python第三方库概述

    1 本节课我们进行讲解python第三方库概述 先看一下考纲考点如图所示 2 然后我们来看一下知识导图如图所示 3 查看一下pip工具安装然后进行根据要求步骤安装 4 然后我们来看一下pyinstaller库概述如图所示 5 接下来我们再来
  • android 图像识别sdk,在android系统下实现图像识别

    首先我们了解下 Android系统常用的图像识别框架 一 调用一些不开源库进行识别 旷视的图像识别及 OCR 文字识别库 及其他厂家如阿里 百度 华为 腾讯的 OCR 文字识别库等 二 调用一些开源库进行识别 一 tensorflow 训练
  • python写出梯度下降的代码

    以下是一个使用Python实现的基本梯度下降 Gradient Descent 算法的示例 导入必要的库 import numpy as np import matplotlib pyplot as plt 定义损失函数 def compu
  • seq2seq模型

    转载自 http blog csdn net sunlylorn article details 50607376
  • k--最近邻算法(KNN)

    目录 一 简介 二 举例理解 三 算法步骤 四 其他说明 1 关于距离的计算 2 超参数 3 关于K值的选择 4 取K值的方法 5 关于决策依据 6 优缺点 五 代码 一 简介 邻近算法 KNN 是数据挖掘分类技术最简单的方法之一 所谓K最
  • Asp.net_Study学习笔记

    Asp net Study web基本原理 浏览器向服务器发送请求 服务器响应 报错 HTTP Error 403 14 Forbidden Web 服务器被配置为不列出此目录的内容 解决 打开控制面板里的程序 点击启用或关闭Windows
  • Matlab实现Kmeans算法(每行代码标注详细注解)

    本文主要为了完成平日作业 并进一步加深对算法的理解 也希望对来访的读者有所帮助 该算法的优化Kmean 算法的代码详解已在其他文章给出 Matlab实现Kmeans 算法 每行代码标注详细注解 高垚淼的博客 CSDN博客 Matlab实现B
  • ESD静电放电最常用的三种模型及其防护设计

    推荐好文 建议直接看链接 1 静电放电最常用的三种模型及其防护设计 http www 360doc com content 17 0827 14 32066980 682502209 shtml 人体模型HBM 机器模型MM 充电器件模型C
  • LA@齐次线性方程组解的结构

    文章目录 齐次线性方程组解的结构 解的性质 齐次线性方程组的解的线性组合还是方程组的解 基础解系 通解 定理 齐次线性方程组基础解系存在定理 齐次线性方程组的基础解系包含的向量个数 秩 应用和示例 推论1 推论2 推论3 转置矩阵对的乘积秩
  • 解决jar文件不显示图标问题

    版权声明 本文为转载文章 遵循 CC 4 0 BY SA 版权协议 本文链接 https blog csdn net qyfx123456 article details 104713149 一 问题的产生 最近 重新装了JDK 配置了环境
  • 生命在于学习——Socket编程(偏安全方面)

    本篇文章仅用于学习记录和交流 不得用于其他违规用途 产生的不良后果 自己负责 一 Socket介绍 首先socket 套接字 是工作在应用层和传输层之间一个抽像层 为什么要有他呢 虽然我们已经有了ip port可以和世界上任意一台计算机上的
  • 扫雷游戏是一款十分经典的单机小游戏。 问题 H: 扫雷游戏

    题目描述 扫雷游戏是一款十分经典的单机小游戏 在n行m列的雷区中有一些格子含有地雷 称之为地雷格 其他格子不含地雷 称之为非地雷格 玩家翻开一个非地雷格时 该格将会出现一个数字 提示周围格子中有多少个是地雷格 游戏的目标是在不翻出任何地雷格
  • axios封装(例如:请求头、token、超时、BaseUrl、请求错误、请求重复)

    axios封装 文章目录 axios封装 前言 1 为什么封装Axios 2 包括的功能 一 Axios是什么 二 安装 Axios 1 安装axios JavaScript版本 2 安装axios TypeScript版本 三 封装 Ax
  • 关于 forza horizon 4 极限竞速地平线4 进游戏没声音 看见车没声音问题 的解决方案 (可能是微星的锅)

    这个问题可能是我微星笔记本的问题 型号GS63 7RE 018n 这个机器有个独立声卡 有个单独的sb软件 地平线4用蓝牙音箱的特殊问题 如果音箱有麦克风 声音就崩了 我用的bose soundlink 我用线从电脑上接出来 结果进了游戏就
  • QT线程池实验研究与分析(QThread与和QThreadPool + QRunnable使用上的区别)

    第一部分 QT线程池的构建与使用 网上关于QT线程池QThreadPool的文章很多 而且大都千篇一律 基本上都是参考QT的帮助文档介绍QT全局线程池的用法 这样就往往会使人产生误解 QT是不是推荐大家使用其全局线程池 而不推荐使用自定义构
  • Docker客户端连接Docker Daemon的方式

    Docker为C S架构 服务端为docker daemon 客户端为docker service 支持本地unix socket域套接字通信与远程socket通信 默认为本地unix socket通信 要支持远程客户端访问需要做如下设置
  • Linux下DM644x设备驱动I2C之总线驱动(一)详解

    本文均属自己阅读源码的点滴总结 转账请注明出处谢谢 欢迎和大家交流 qq 1037701636 email 200803090209 zjut com gzzaigcn2012 gmail com linux DM6441下I2C设备驱动的