i2c子系统分析:源码1. kernel-3.18\drivers\misc\mediatek\i2c\mt6735\i2c.c

本文详细介绍了MTK I2C子系统的驱动实现过程,包括mt_i2c_init()模块初始化、平台驱动注册、mt_i2c_probe()的探测过程、i2c_client的创建与注册,以及中断处理函数mt_i2c_irq()。在mt_i2c_probe()中,驱动获取内存资源、ioremap、申请中断、初始化硬件,并将i2c_adapter注册到总线。同时,文章还展示了标准I2C传输的实现流程。
摘要由CSDN通过智能技术生成
/*
	i2c.c分析

	1. mt_i2c_init()			// module_init
		|
		platform_driver_register(&mt_i2c_driver)

	2. static struct platform_driver mt_i2c_driver = {
			.probe = mt_i2c_probe,
			.remove = mt_i2c_remove,
			.suspend = mt_i2c_suspend,
			.resume = mt_i2c_resume,
			.driver = {
				   .of_match_table = mt_i2c_of_match,	// 用于与dts中的 platform device 匹配 {.compatible = "mediatek,mt6735m-i2c",},
			},
		};	

	3. mt_i2c_probe(struct platform_device *pdev)
		|
		platform_get_resource(pdev, IORESOURCE_MEM, 0);					// 1. 获取平台内存资源 - dts中写的
		request_mem_region(res->start, resource_size(res), pdev->name)	// 向系统申请占用内存资源,其他驱动不可用,类似锁
		i2c = kzalloc(sizeof(struct mt_i2c_t), GFP_KERNEL);
		i2c->base = of_iomap(pdev->dev.of_node, 0);						// 2. ioremap()
		irq = irq_of_parse_and_map(pdev->dev.of_node, 0);				// 获取中断号irq,从dts获取平台设备资源,并映射出中断号
		request_irq(irq, mt_i2c_irq, IRQF_TRIGGER_LOW, I2C_DRV_NAME, i2c);	// 3. 获取平台中断资源(在上面),并申请中断request_irq()
		mt_i2c_init_hw(i2c);												// i2c硬件初始化 - 通过直接写寄存器
			|
			i2c_writel(i2c, OFFSET_SOFTRESET, 0x0001);
			i2c_writel(i2c, OFFSET_DCM_EN, 0x0);
		
		i2c->adap.nr = i2c->id;
		i2c->adap.owner = THIS_MODULE;
		i2c->adap.algo = &mt_i2c_algorithm;
							|
							.master_xfer = standard_i2c_transfer,
							gt1x_tpd.c中
							gt1x_i2c_read()
								i2c_read_mtk(addr, buffer, len);
									_do_i2c_read(msgs, addr, buffer, len);
										i2c_transfer(gt1x_i2c_client->adapter, msgs, 2);
											__i2c_transfer(adap, msgs, num);
												adap->algo->master_xfer(adap, msgs, num);
		i2c->adap.algo_data = NULL;
		i2c->adap.timeout = 2 * HZ;	
		i2c->adap.retries = 1;
		i2c_add_numbered_adapter(&i2c->adap);							//注册i2c adapter,构建、初始化在前面
				|
				i2c_add_adapter(adap);									// 注册i2c adapter

	4. i2c_add_adapter(struct i2c_adapter *adapter)
		|
		adapter->nr = id;
		i2c_register_adapter(adapter);
			|
			if (unlikely(WARN_ON(!i2c_bus_type.p))) {		// unlikely() 编译器会根据上下文判断,这段代码是否成立,如果不成立,直接在编译的时候去掉
			 
			dev_set_name(&adap->dev, "i2c-%d", adap->nr);	// 设置device的名字: /sys/bus/i2c/devices/i2c-%d
			adap->dev.bus = &i2c_bus_type;					// 指定总线为: i2c总线
			adap->dev.type = &i2c_adapter_type;
			device_register(&adap->dev);					// 将i2c_client注册到i2c总线中 client->dev.bus = &i2c_bus_type;

			i2c_scan_static_board_info(adap);				// 遍历__i2c_board_list链表,逐个创建i2c_client(只创建总线编号与adapter相同的)
				|
				list_for_each_entry(devinfo, &__i2c_board_list, list) {	// 遍历__i2c_board_list链表
															// 调用i2c_new_device(),将当前的adapter和board_info传入
					if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter,	&devinfo->board_info))

		总结:
			1. i2c_client是在adapter注册的时候创建的,并注册到i2c总线
			2. adapter也会注册到i2c总线

		5. i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
			|
			client = kzalloc(sizeof *client, GFP_KERNEL);	// 分配一下i2c_client
															// 初始化i2c_client
			client->adapter = adap;							// client 关联adapter

			client->addr = info->addr;						// 【info来自dts】将dts中的i2c addr赋值给client
			client->irq = info->irq;

			strlcpy(client->name, info->type, sizeof(client->name));	// 名字也是来自于dts
			client->dev.parent = &client->adapter->dev;
			client->dev.bus = &i2c_bus_type;				// 指定总线为: i2c总线
			client->dev.type = &i2c_client_type;
			client->dev.of_node = info->of_node;

			i2c_dev_set_name(adap, client);					// 设置device的名字: /sys/bus/i2c/devices/0-0050
				|
				dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),	client->addr | ((client->flags & I2C_CLIENT_TEN) ? 0xa000 : 0));

			device_register(&client->dev);					// 将i2c_client注册到i2c总线中 client->dev.bus = &i2c_bus_type;

			总结:
				1. 构建了一个i2c_client
				2. 通过dts中的信息和当前的adapter 初始化i2c_client
				3. 将i2c_client注册到i2c总线, /sys/bus/i2c/devices/(0-0050)"%d-%04x"
*/

/*
1. mt_i2c_init()			// module_init
	|
	platform_driver_register(&mt_i2c_driver)

2. static struct platform_driver mt_i2c_driver = {
		.probe = mt_i2c_probe,

3. mt_i2c_probe(struct platform_device *pdev)
	|
	i2c->adap.algo = &mt_i2c_algorithm;
						|
						.master_xfer = standard_i2c_transfer,
					gt1x_tpd.c中
						gt1x_i2c_read()
							i2c_read_mtk(addr, buffer, len);
								_do_i2c_read(msgs, addr, buffer, len);
									i2c_transfer(gt1x_i2c_client->adapter, msgs, 2);
										__i2c_transfer(adap, msgs, num);
											adap->algo->master_xfer(adap, msgs, num);
											
4. static struct i2c_algorithm mt_i2c_algorithm = {
		.master_xfer = standard_i2c_transfer,
		.functionality = mt_i2c_functionality,
	};
	
5. standard_i2c_do_transfer(i2c, msgs, num);
		|
		s32 left_num = num;
		while (left_num--) {
			standard_i2c_start_xfer(i2c, msgs++);
				|
				msg_ext.addr = msg->addr;			// 初始化i2c结构体、和msg结构体
				msg_ext.flags = msg->flags;
				msg_ext.len = msg->len;
				msg_ext.buf = msg->buf;
				msg_ext.ext_flag = 0;
				msg_ext.timing = i2c->defaul_speed;
																						
				i2c->read_flag = (msg_ext.flags & I2C_M_RD);
				i2c->addr = msg_ext.addr;

				_i2c_translate_msg(i2c, &msg_ext);	// 继续初始化i2c结构体、和msg结构体
					|
					I2CINFO(I2C_T_TRANSFERFLOW, "Before i2c transfer .....\n");
					if (msg->addr & 0xFF00)				msg->ext_flag |= msg->addr & 0xFF00;
					i2c->msg_buf = msg->buf;
					i2c->msg_len = msg->len;
					if (msg->ext_flag & I2C_DMA_FLAG)	i2c->dma_en = true;
					else								i2c->dma_en = false;
					if (msg->ext_flag & I2C_WR_FLAG)	i2c->op = I2C_MASTER_WRRD;
					else {			
							if (msg->flags & I2C_M_RD)	i2c->op = I2C_MASTER_RD;
							else						i2c->op = I2C_MASTER_WR;
					}
				mt_i2c_clock_enable(i2c);			// 设置i2c时钟,在clk.c中实现
					|
					if (i2c->dma_en) 		
						enable_clock(MT_CG_PERI_APDMA, "i2c");
					enable_clock(i2c->pdn, "i2c");

				_i2c_transfer_interface(i2c);
					|
					i2c_set_speed(i2c);						// 设置i2c速率
					_i2c_write_reg(i2c);					// 在i2c时序的起始位之前,初始化i2c adapter寄存器
					i2c_writel(i2c, OFFSET_START, 0x0001);	// i2c时序的起始位
					_i2c_deal_result(i2c);					// 处理结果: 根据应答位是否被拉低,做出处理
						|
						if (i2c->poll_en) {	// log 显示不是轮询模式: poll_en==0
						} else {			// 如果是中断模式,此处睡眠,等待中断显式唤醒等待队列,才继续往下走
							tmo = wait_event_timeout(i2c->wait, atomic_read(&i2c->trans_stop), tmo);
						}
						if (!(tmo == 0 || atomic_read(&i2c->trans_err))) {		// Transfer success 传输成功 - 应答位正常
						} else {												// Transfer err 传输失败 - 根据应答位判断是超时or无应答
							if (tmo == 0) 		I2CERR("id=%d,addr: %x, transfer timeout\n", i2c->id, i2c->addr);
							else 				I2CERR("id=%d,addr: %x, transfer error\n", i2c->id, i2c->addr);		// i2c addr 不对的时候走这里
							if (i2c->irq_stat & I2C_HS_NACKERR)		I2CERR("I2C_HS_NACKERR\n");
							if (i2c->irq_stat & I2C_ACKERR)			I2CERR("I2C_ACKERR\n");							// i2c addr 不对的时候走这里
							_i2c_dump_info(i2c);								// 此处倾倒i2c info			
						I2CINFO(I2C_T_TRANSFERFLOW, "After i2c transfer .....\n");

6. static irqreturn_t mt_i2c_irq(s32 irqno, void *dev_id)	// mt_i2c_probe()中申请的中断
		|
		i2c->irq_stat = i2c_readl(i2c, OFFSET_INTR_STAT);	// 读取i2c ack的状态,后面用以判断是timeout 还是ack err
		wake_up(&i2c->wait);		// 显式唤醒i2c的等待队列

*/



#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#ifdef CONFIG_OF
#include <linux/of_irq.h>
#include <linux/of_address.h>
#endif
#include <mach/mt_clkmgr.h>	/* mt_clkmgr.h will be removed after CCF porting is finished. */
#include <asm/io.h>
/* #include <mach/dma.h> */
/* #include <mach/mt_reg_base.h> */
#include <mt_i2c.h>
#include <mt-plat/sync_write.h>
#include "../../base/power/mt6735/mt_pm_init.h"
/* #include "mach/memory.h" */
/* #include <mach/i2c.h> */
/* #include <linux/aee.h> */
#define TAG     "MT_I2C"

#define DMA_LOG_LEN 7
static struct i2c_dma_info g_dma_data[DMA_LOG_LEN];
static void __iomem *spm_i2c_base;

/* extern unsigned int mt_get_bus_freq(void); */

typedef int (*pmaster_xfer) (struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

/* define ONLY_KERNEL */
/******************************internal API********************************************************/
void i2c_writel(struct mt_i2c_t *i2c, u8 offset, u16 value)
{
	/* __raw_writew(value, (i2c->base) + (offset)); */
	mt_reg_sync_writel(value, (i2c->base) + (offset));
}

u32 i2c_readl(struct mt_i2c_t *i2c, u8 offset)
{
	return __raw_readl((void *)((i2c->base) + (offset)));
}

/***********************************declare  API**************************/
static void mt_i2c_clock_enable(struct mt_i2c_t *i2c);
static void mt_i2c_clock_disable(struct mt_i2c_t *i2c);

/***********************************I2C common Param **************************/
u32 I2C_TIMING_REG_BACKUP[7] = { 0 };
u32 I2C_HIGHSP_REG_BACKUP[7] = { 0 };

#ifdef CONFIG_OF
static void __iomem *ap_dma_base;
#endif
/***********************************I2C Param only used in kernel*****************/
/*this field is only for 3d camera*/
static struct mt_i2c_msg g_msg[2];
static struct mt_i2c_t *g_i2c[2];
#define I2C_DRV_NAME        "mt-i2c"
/***********************************i2c debug********************************************************/
/* #define I2C_DEBUG_FS */
#ifdef I2C_DEBUG_FS
#define PORT_COUNT 7
#define MESSAGE_COUNT 16
#define I2C_T_DMA 1
#define I2C_T_TRANSFERFLOW 2
#define I2C_T_SPEED 3
/*7 ports,16 types of message */
u8 i2c_port[PORT_COUNT][MESSAGE_COUNT];

#define I2CINFO(type, format, arg...)	I2CLOG(format, ## arg)

static ssize_t show_config(struct device *dev, struct device_attribute *attr, char *buff)
{
	s32 i = 0;
	s32 j = 0;
	char *buf = buff;

	for (i = 0; i < PORT_COUNT; i++) {
		for (j = 0; j < MESSAGE_COUNT; j++)
			i2c_port[i][j] += '0';
		strncpy(buf, (char *)i2c_port[i], MESSAGE_COUNT);
		buf += MESSAGE_COUNT;
		*buf = '\n';
		buf++;
		for (j = 0; j < MESSAGE_COUNT; j++)
			i2c_port[i][j] -= '0';
	}
	return buf - buff;
}

static ssize_t set_config(struct device *dev, struct device_attribute *attr, const char *buf,
			  size_t count)
{
	s32 port, type, status;

	if (sscanf(buf, "%d %d %d", &port, &type, &status) != 0) {
		if (port >= PORT_COUNT || port < 0 || type >= MESSAGE_COUNT || type < 0) {
			/*Invalid param */
			I2CERR("i2c debug system: Parameter overflowed!\n");
		} else {
			if (status != 0)
				i2c_port[port][type] = 1;
			else
				i2c_port[port][type] = 0;

			I2CLOG("port:%d type:%d status:%s\ni2c debug system: Parameter accepted!\n",
			       port, type, status ? "on" : "off");
		}
	} else {
		/*parameter invalid */
		I2CERR("i2c debug system: Parameter invalid!\n");
	}
	return count;
}

static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR, show_config, set_config);
#else
#define I2CINFO(type, format, arg...)
#endif
/***********************************common API********************************************************/

/*
add this function for cast pointer from PA to VA
1 32bit, but open 4G RAM
2 64bit
   -32-bit/64-bit is ok
   -32-bit with open 4G DRAM config will lose high 32bit ([63:32])
Note: this function will cast 64 bit address to 32 bit, so, must need to confirm [63:32] of address
is 0. need flag GFP_DMA32 for dma_alloc_coherent()
*/
char *mt_i2c_bus_to_virt(unsigned long address)
{
	return (char *)address;
}

/*Set i2c port speed*/
static s32 i2c_set_speed(struct mt_i2c_t *i2c)
{
	s32 ret = 0;
	s32 mode = 0;
	u32 khz = 0;

	/* u32 base = i2c->base; */
	u16 step_cnt_div = 0;
	u16 sample_cnt_div = 0;
	u32 tmp, sclk, hclk = i2c->clk;
	u16 max_step_cnt_div = 0;
	u32 diff, min_diff = i2c->clk;
	u16 sample_div = MAX_SAMPLE_CNT_DIV;
	u16 step_div = 0;
	/* I2CFUC(); */
	/* I2CLOG("i2c_set_speed=================\n"); */
	/* compare the current speed with the latest mode */


	mode &#
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值