MTK I2C驱动代码分析

   I2C总线(I2C bus,Inter-IC bus)是一个双向的两线连续总线,提供集成电路(ICs)之间的通信线路。I2C总线是一种串行扩展技术,最早由Philips公司推出。Philips公司推出的I2C总线采用一条数据线(SDA),加一条时钟线(SCL)来完成数据的传输及外围器件的扩展;对各个节点的寻址是软寻址方式,节省了片选线,标准的寻址字节SLAM为7位,可以寻址127个单元。
 I2C总线有三种数据传输速度:标准,快速模式和高速模式。标准的是100Kbps,快速模式为400Kbps,高速模式支持快至3.4Mbps的速度。

 

I2C位传输

   数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;若SDA发生跳变,则用来表示一个会话的开始或结束(后面讲)
   数据改变:SCL为低电平时,SDA线才能改变传输的bit

MTK I2C驱动代码分析_第1张图片

I2C开始和结束信号
   开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
   结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

MTK I2C驱动代码分析_第2张图片

I2C应答信号
   Master每发送完8bit数据后等待Slave的ACK。
   即在第9个clock,若从IC发ACK,SDA会被拉低。
   若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程,如下所示:

SCL is a clock signal that is driven by the master. SDA is a bi-directional data signal that can be driven by either the master or the slave.

 

I2C架构图:

MTK I2C驱动代码分析_第3张图片

代码分析:

kernel-3.10\drivers\misc\mediatek\i2c\mt6735\I2c.c 

(注:所有的i2c驱动,在该模块加载前就已经加载,调用i2c_register_board_info函数加载的board_info信息,已经加入__i2c_board_list链表)。

设备驱动以qn8027为例。


第16071623行,在device tree上找到AP_DMA结点,见kernel-3.10/arch/arm64/boot/dts/mt6735m.dtsi

DT的相关知识请参考:http://blog.csdn.net/21cnbao/article/details/8457546

第1624行执行platform_driver_register(&mt_i2c_driver);


MTK I2C驱动代码分析_第4张图片

MTK I2C驱动代码分析_第5张图片

MTK I2C驱动代码分析_第6张图片

MTK I2C驱动代码分析_第7张图片

MTK I2C驱动代码分析_第8张图片

第1508行,初始化i2c->wait等待对列。

第1510行,注册下降沿中断,中断处理函数为mt_i2c_irq。该函数读取I2C中断状态,唤醒等待对列i2c->wait队列。

第1521行,调用i2c_add_numbered_adapter(&i2c->adap);该函数会调用到__i2c_add_numbered_adapter,其代码如下:

MTK I2C驱动代码分析_第9张图片

1090行,分配ID号,IDR机制具体描述如下:

IDR机制在Linux内核中指的是整数ID管理机制。实质上来讲,这就是一种将一个整数ID号和一个指针关联在一起的机制。这个机制最早在032月加入内核,当时作为POSIX定时器的一个补丁。现在,内核中很多地方都可以找到它的身影。

IDR机制适用在那些需要把某个整数和特定指针关联在一起的地方。例如,在IIC总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送设备的地址。当适配器要访问总线上的IIC设备时,首先要知道它们的ID号,同时要在内核中建立一个用于描述该设备的结构体,和驱动程序。将ID号和设备结构体结合起来,如果使用数组进行索引,一旦ID号很大,则用数组索引会占据大量内存空间。这显然不可能。或者用链表,但是,如果总线中实际存在的设备很多,则链表的查询效率会很低。此时,IDR机制应运而生。该机制内部采用红黑树实现,可以很方便的将整数和指针关联起来,并且具有很高的搜索效率。

1096行调用i2c_register_adapter注册adapter,主要代码如下:

MTK I2C驱动代码分析_第10张图片

1013行,注册&adap->dev.

1020行,调用class_compat_create_link,代码如下:

MTK I2C驱动代码分析_第11张图片

即创建i2c_adapter_compat_class->kobj目录下指向&adap->dev&adap->dev->kobj目录下指向adap->dev.parent目录的软链接。

1062行,因其他驱动模块已经加载,即所有其他模块用到的i2c总线,例如如果用到i2c4,则根据i2c_register_board_info函数,

__i2c_first_dynamic_bus_num会为5. 故会调用到i2c_scan_static_board_info函数,该函数代码如下:

MTK I2C驱动代码分析_第12张图片

948行,调用i2c_new_device(adap, &info);根据info数据,注册新的client设备。

回到i2c_register_adapter函数,第1066行,调用bus_for_each_drvi2c bus上的驱动进行遍历,对找到驱动,执行__process_new_adapter(drv,adap),该函数调用

i2c_do_add_adapter(to_i2c_driver(drv), adap); 再调用 i2c_detect(adap, driver);driver->detect || address_list为空,会直接退出。

driver->attach_adapterqn8027_driver中没有赋值,故不会调用到。

回到mt_i2c_probe函数,第1529行,调用of_i2c_register_devices,

注:系统集成的模块会走该流程,如ncp1854

node->full_name = /bus/I2C3@0x1100F000/ncp1854@36

request_module("%s%s", I2C_MODULE_PREFIX, info.type);即请求加载 i2c:ncp1854

而外部加的模块则在之前的i2c_scan_static_board_info函数已作了处理。

MTK I2C驱动代码分析_第13张图片

第67行,让linux系统的用户空间调用/sbin/modprobe函数加载名为i2c:I2Cx模块。

kernel-3.10/include/linux/mod_devicetable.h:

#define I2C_MODULE_PREFIX "i2c:"

第69行,调用i2c_new_device(adap, &info);根据info数据,注册新的client设备。

MTK I2C驱动代码分析_第14张图片 MTK I2C驱动代码分析_第15张图片


kernel-3.10\drivers\i2c\I2c-core.c

postcore_initcall(i2c_init);

MTK I2C驱动代码分析_第16张图片

1433行,注册i2c_bus_type总线。

1437行, register a compatibility class

1443,调用i2c_add_driver添加一个dummy_driver驱动。该函数调用i2c_register_driver,其代码如下:

第1327行调用driver_register(&driver->driver);i2c bus注册i2c驱动。

第1343行调用i2c_for_each_dev

MTK I2C驱动代码分析_第17张图片 MTK I2C驱动代码分析_第18张图片 MTK I2C驱动代码分析_第19张图片

1811行对driver->detect || !address_list进行判断,如I2C驱动这两个成员变量没有赋值,则直接退出。

MTK I2C驱动代码分析_第20张图片


i2c_register_board_info函数解析:

MTK I2C驱动代码分析_第21张图片

生成i2c_devinfo结构体,赋值为busnum*info,将其加入__i2c_board_list链表。

 

小结:设备驱动程序通过i2c_register_board_info函数注册i2c_board_info数据,i2c_scan_static_board_info函数会根据这些数据生成对应的client设备,设备驱动提供接口给应用层,并且在加载时调用i2c_add_driver完成与对应的client配对,而Clienti2c_adapter关联在一起。数据最终由mt_i2c处理。I2c_adaptermt_i2cplatform_device关系见下图:

MTK I2C驱动代码分析_第22张图片

I2c数据发送代码流程:

kernel-3.10\drivers\i2c\I2c-core.c

MTK I2C驱动代码分析_第23张图片

该函数定义并初始化i2c_msg消息结构体后,第1593行调用i2c_transfer进行数据传送。i2c_transfer进行相应的判断及互斥操作后调用__i2c_transfer函数,该函数最终会调用

adap->algo->master_xfer(adap, msgs, num);

1159行通过i2c_get_adapdata找到关联的mt_i2c

1163行调用mt_i2c_do_transfer函数,该函数再会调用到mt_i2c_start_xfer函数。MTK I2C驱动代码分析_第24张图片

MTK I2C驱动代码分析_第25张图片

947行,调用_i2c_translate_msg,将msg转换为mt_i2c

960行,调用_i2c_transfer_interface进行传输。该函数最后会调用_i2c_deal_result函数来处理传输结果。

tmo = wait_event_timeout(i2c->wait,atomic_read(&i2c->trans_stop), tmo);即中断模式下,当前进程加入i2c->wait等待对列进行休眠。

唤醒时机:a. 发生中断,正常唤醒,

                    b. 超时唤醒。


QN8027.C驱动代码示例:

/*****************************************************************************
  Copyright(c) 2012 xxxx Inc. All Rights Reserved
 
  File name : xxxx-qn8027.c
 
  Description : NM326 host interface
 
  History :
  ----------------------------------------------------------------------
  2015/02/28	 dlj	 initial
 *******************************************************************************/
 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/kobject.h>
#include <linux/earlysuspend.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/err.h>

#include "../inc/fm_ioctl.h"
#include "xxxx-qn8027.h"

static QN8027_CONF qn8027_conf_setting;

static unsigned char load_setting_flag = 0;
static unsigned char power_up_flag = 0;
static int qn8027_reg = -1;
static struct class *qn8027_class;
static struct i2c_client *qn8027_client;
static struct mutex		 qn8027_i2c_lock;

static struct i2c_board_info __initdata i2c_qn8027={ I2C_BOARD_INFO(QN8027_DEV_NAME, QN8027_I2C_SLAVE_ADDR)};

static int qn8027_save_conf(void)
{
	struct file *fp;
	mm_segment_t fs;
	loff_t pos = 0;
	int ret = 0;

	fp = filp_open(QN8027_CONF_FILE, O_RDWR|O_CREAT, 0644);
	if(IS_ERR(fp))
	{
		QN8027_ERR("create %s file failed!\n", QN8027_CONF_FILE);
		ret = -1;
	}
	else
	{
		fs = get_fs();
		set_fs(KERNEL_DS);
		vfs_write(fp, (char *)&qn8027_conf_setting, sizeof(qn8027_conf_setting), &pos);
		set_fs(fs);
		filp_close(fp, NULL);
	}

	return ret;
}

static int qn8027_load_conf(void)
{
	struct file *fp;
	mm_segment_t fs;
	loff_t pos = 0;
	int ret = 0;

	fp = filp_open(QN8027_CONF_FILE, O_RDONLY, 0644);
	if(IS_ERR(fp))
	{
		QN8027_ERR("read %s file failed!\n", QN8027_CONF_FILE);
		ret = -1;
	}
	else
	{
		fs = get_fs();
		set_fs(KERNEL_DS);
		vfs_read(fp, (char *)&qn8027_conf_setting, sizeof(qn8027_conf_setting), &pos);
		set_fs(fs);
		filp_close(fp, NULL);
	}

	return ret;

}

static int qn8027_i2c_write(u8 reg, u8 writedata)
{
	u8 databuf[2] = {0};
	int ret = 0;

	databuf[0] = reg;
	databuf[1] = writedata;

	mutex_lock(&qn8027_i2c_lock);
	QN8027_LOG("%s: write value(0x%x) to reg(0x%x) \n", __FUNCTION__, (int)writedata, (int)reg);
	ret = i2c_master_send(qn8027_client, databuf, 0x2);
	if(ret < 0)
	{
		QN8027_LOG("qn8027_i2c_write send data failed!!!\n");
	}
	mutex_unlock(&qn8027_i2c_lock);

	return ret;
}

static int qn8027_i2c_read(u8 reg, u8 *readdata)
{
	u8 databuf = 0;
	int ret = 0;

	databuf = reg;

	mutex_lock(&qn8027_i2c_lock);
	qn8027_client->ext_flag = I2C_WR_FLAG;
	ret = i2c_master_send(qn8027_client, &databuf, (1<<8 | 1));
	if(ret < 0)
	{
		QN8027_LOG("qn8027_i2c_write send data failed!!!\n");
	}

	qn8027_client->ext_flag = 0;
	mutex_unlock(&qn8027_i2c_lock);

	QN8027_LOG("qn8027_i2c_read: databuf = 0x%x\n", databuf);
	*readdata = databuf;

	return ret;
}

void QNF_SetRegBit(u8 reg, u8 bitMask, u8 data_val)
{
    u8 temp;
	
    qn8027_i2c_read(reg, &temp);
    temp &= (u8)(~bitMask);
    temp |= (data_val & bitMask);

    qn8027_i2c_write(reg, temp);
}

static int qn8027_open(struct inode *inode, struct file *file)
{
	QN8027_FUN();
	return 0;
}

static int qn8027_release(struct inode *inode, struct file *file)
{
	QN8027_FUN();
	return 0;
}

static long qn8027_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	void __user *data;
	char strbuf[QN8027_BUFSIZE] = {0};

	QN8027_FUN();
	QN8027_LOG("qn8027_ioctl: cmd = %u\n", cmd);
	QN8027_LOG("qn8027_ioctl: QN8027_IOCTL_POWERUP = %lu\n", QN8027_IOCTL_POWERUP);
	QN8027_LOG("qn8027_ioctl: QN8027_IOCTL_SETCHENNEL = %lu\n", QN8027_IOCTL_SETCHENNEL);
	QN8027_LOG("qn8027_ioctl: QN8027_IOCTL_GETINFO = %lu\n", QN8027_IOCTL_GETINFO);

	switch (cmd) {
		case QN8027_IOCTL_POWERUP:
			 QN8027_LOG("qn8027_ioctl: arg = %ld\n", arg);
			 if(arg == QN8027_POWERON)
			 {
			 	if(power_up_flag == 0)
				{
					gpio_set_value(QN8027_POWER_ENABLE_PIN, 1);
					QN_ChipInitialization();
					QND_TuneToCH(qn8027_conf_setting.channel);

					power_up_flag = 1;
				}
			 }
			 else if(arg == QN8027_POWEROFF)
			 {
			 	if(power_up_flag == 1)
				{
				 	QNF_SetRegBit(SYSTEM, R_TXRX_MASK, 0); 
					gpio_set_value(QN8027_POWER_ENABLE_PIN, 0);
					power_up_flag = 0;
				}
			 }
			 break;

		case QN8027_IOCTL_SETCHENNEL:
			if(arg<7600)
				arg=7600; 
			if(arg>10800)
				arg= 10800;

			qn8027_conf_setting.channel = arg;
			QN8027_LOG("qn8027_ioctl: arg = %ld\n", arg);

			QND_TuneToCH(arg);
			if(qn8027_save_conf())
			{
				QN8027_ERR("qn8027_save_conf failed\n");					
			}

			break;

		case QN8027_IOCTL_GETINFO:
			data = (void __user *) arg;
			if(data == NULL)
			{
				ret = -EINVAL;
				break;	  
			}

			if(load_setting_flag == 0 )
			{
				if(qn8027_load_conf())
				{
					QN8027_ERR("qn8027_load_conf failed\n");
				}
			}

			load_setting_flag = 1;
			snprintf(strbuf, QN8027_BUFSIZE, "CH=%ld\n", qn8027_conf_setting.channel); 

			if(copy_to_user(data, strbuf, sizeof(strbuf)))
			{
				ret = -EFAULT;	  
			}

			break;

		case QN8027_IOCTL_GETPOWERUP_STATUS:
			data = (void __user *) arg;
			if(data == NULL)
			{
				ret = -EINVAL;
				break;	  
			}

			snprintf(strbuf, QN8027_BUFSIZE, "%d\n", power_up_flag); 

			if(copy_to_user(data, strbuf, sizeof(strbuf)))
			{
				ret = -EFAULT;	  
			}

			break;

		default:
			QN8027_ERR("cmd not support!\n");
			ret = -EPERM;
	}


	return ret;
}

void QND_TuneToCH(int freq) 
{
	u8 tStep;
	u8 tCh;
	int f; 

	if(freq<7600)
		freq=7600; 
	if(freq>10800)
		freq= 10800;

	QN8027_LOG("QND_TuneToCH: ch = %ld\n", qn8027_conf_setting.channel);
	f = FREQ2CHREG(freq); 
	// set to reg: CH
	tCh = (u8) f;
	qn8027_i2c_write(0x01, tCh);
	// set to reg: CH_STEP
	qn8027_i2c_read(0x00, &tStep);
	tStep &= ~0x03;
	tStep |= ((u8) (f >> 8) & 0x03);
	qn8027_i2c_write(0x00, tStep);
	
}

void QN_ChipInitialization(void)
{
	QN8027_FUN();

	qn8027_i2c_write(0x00,0x80);// reset all registers to the default value
	mdelay(200);

	qn8027_i2c_write(0x03,0x3F);
	qn8027_i2c_write(0x04,0x33);//set 12Mhz clock
	qn8027_i2c_write(0x00,0x41);
	qn8027_i2c_write(0x00,0x01);
	mdelay(200);
	qn8027_i2c_write(0x18,0xE4);
	qn8027_i2c_write(0x1B,0xF0);
	qn8027_i2c_write(0x01,0x00);
	qn8027_i2c_write(0x02,0xB9);
	qn8027_i2c_write(0x00,0x22);//send
}

/******************************************************************************/
static ssize_t qn8027_store_reg_addr(struct device* dev, struct device_attribute *attr,
                                  const char *buf, size_t count)
{
	int write_data;

    if (!dev)
	{
        QN8027_ERR("dev is null!!\n");
        return 0;
    } 

	if (!strncmp(buf, "reg=",4) && (1 == sscanf(buf+4, "%d", &qn8027_reg))) {
		QN8027_LOG("qn8027_reg = 0x%x\n", qn8027_reg);
	}
	if (!strncmp(buf, "val=", 4)&&(1==sscanf(buf+4, "%d", &write_data))) {
		QN8027_LOG("write_data = 0x%x\n", write_data);
		qn8027_i2c_write((u8)qn8027_reg, (u8)write_data);
	}

    return count;
}

/******************************************************************************/
static ssize_t qn8027_show_reg_value(struct device* dev, 
                                 struct device_attribute *attr, char *buf)
{
    ssize_t res = 0;
	u8 value = 0;

	QN8027_LOG("reg is: 0x%x\n", qn8027_reg);
	qn8027_i2c_read((u8)qn8027_reg, &value);
	res = snprintf(buf, PAGE_SIZE, "0x%x\n", (int)value); 

    return res;
}

/******************************************************************************/
static ssize_t qn8027_store_gpio_value(struct device* dev, struct device_attribute *attr,
                                  const char *buf, size_t count)
{
	int value;
    if (!dev)
	{
        QN8027_ERR("dev is null!!\n");
        return 0;
    } 

	QN8027_LOG("qn8027_init_test now!!!\n");

	if (!strncmp(buf, "val=",4) && (1 == sscanf(buf+4, "%d", &value))) {
		QN8027_LOG("val = %d\n", value);
	}

	if(value == 0 || value == 1)
	{
		gpio_set_value(QN8027_POWER_ENABLE_PIN, value);
	}

    return count;
}

/******************************************************************************/
static ssize_t qn8027_show_gpio_value(struct device* dev, 
                                 struct device_attribute *attr, char *buf)
{
    ssize_t res = 0;
	int value = 0;

	value = gpio_get_value(QN8027_POWER_ENABLE_PIN);
	QN8027_LOG("val = %d\n", value);
	res = snprintf(buf, PAGE_SIZE, "%d\n", value); 

    return res;
}

DEVICE_ATTR(reg, S_IWUSR | S_IWGRP | S_IRUGO,    qn8027_show_reg_value,  qn8027_store_reg_addr);
DEVICE_ATTR(gpio_val,  S_IWUSR | S_IWGRP | S_IRUGO,     qn8027_show_gpio_value,   qn8027_store_gpio_value);

static struct device_attribute *qn8027_attr_list[] = {
    &dev_attr_reg,
	&dev_attr_gpio_val,
};

static int xxxx_create_attr(struct device *dev) 
{
    int idx, err = 0;
    int num = (int)(sizeof(qn8027_attr_list)/sizeof(qn8027_attr_list[0]));
    if (!dev)
	{
        return -EINVAL;
	}

    for (idx = 0; idx < num; idx++)
	{
        if ((err = device_create_file(dev, qn8027_attr_list[idx])))
		{
            QN8027_ERR("device_create_file\n");        
            break;
        }
    }

    return err;
}

static struct file_operations qn8027_fops = {
		.owner = THIS_MODULE,
		.open = qn8027_open,
		.release = qn8027_release,
		.unlocked_ioctl = qn8027_ioctl,
};

static int qn8027_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret = 0;
	struct device *qn8027_dev;

	QN8027_FUN();

	qn8027_client = client;

	// 1. register character device
	ret = register_chrdev(QN8027_DEV_MAJOR, QN8027_DEV_NAME, &qn8027_fops);
	if(ret)
	{
		QN8027_ERR("<xxxx> register_chrdev(QN8027_DEV) failed\n");
		goto error;
	}

	// 2. class create
	qn8027_class = class_create(THIS_MODULE, QN8027_DEV_NAME);
	if(IS_ERR(qn8027_class))
	{
		QN8027_ERR("<xxxx> class create failed\n");
		goto error;
	}

	// 3. device create
	qn8027_dev = device_create(qn8027_class, NULL, MKDEV(QN8027_DEV_MAJOR, QN8027_DEV_MINOR), NULL, QN8027_DEV_NAME);

    if (xxxx_create_attr(qn8027_dev))
	{
		class_destroy(qn8027_class);
        goto error;
	}

	mutex_init(&qn8027_i2c_lock);

	QNF_SetRegBit(SYSTEM, R_TXRX_MASK, 0); 
	gpio_set_value(QN8027_POWER_ENABLE_PIN, 0);
	qn8027_conf_setting.channel = QN8027_DEFCH;
	gpio_set_value(QN8027_POWER_ENABLE_PIN, 0);

	return 0;

error:
    if (ret == 0)
	{
        unregister_chrdev(QN8027_DEV_MAJOR, QN8027_DEV_NAME);
	}

    return -1;

}

static int qn8027_remove(struct i2c_client *client)
{
	unregister_chrdev(QN8027_DEV_MAJOR, QN8027_DEV_NAME);
	device_destroy(qn8027_class, MKDEV(QN8027_DEV_MAJOR, QN8027_DEV_MINOR));
	class_destroy(qn8027_class);

	return 0;
}

static const struct i2c_device_id qn8027_ids[] = {
		{ QN8027_DEV_NAME, 0, },
		{ /* LIST END */ }
};

static struct i2c_driver qn8027_driver = {
		.driver = {
				.name = QN8027_DEV_NAME,
				},
		.probe = qn8027_probe,
		.remove = qn8027_remove,
		.id_table = qn8027_ids,
};

/*----------------------------------------------------------------------------*/
static int __init qn8027_init(void)
{
	QN8027_FUN();
	i2c_register_board_info(1, &i2c_qn8027, 1);
	if(i2c_add_driver(&qn8027_driver)!=0)
	{
		QN8027_ERR("qn8027_driver initialization failed!!\n");
		return -1;
	}
	else
	{
		QN8027_LOG("qn8027_driver initialization succeed!!\n");
	}

	return 0;    
}
/*----------------------------------------------------------------------------*/
static void __exit qn8027_exit(void)
{
	QN8027_FUN();
}

/*----------------------------------------------------------------------------*/
module_init(qn8027_init);
module_exit(qn8027_exit);
/*----------------------------------------------------------------------------*/
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("QN8027  driver");
MODULE_AUTHOR("[email protected]");

xxxx-qn8027.h
/*****************************************************************************
  Copyright(c) 2012 XXXX Inc. All Rights Reserved
 
  File name : xxxx-qn8027.h
 
  Description : NM326 host interface
 
  History :
  ----------------------------------------------------------------------
  2015/02/28	 dlj	 initial
 *******************************************************************************/
 
#ifndef XXXX_QN8027_H
#define XXXX_QN8027_H

#define QN8027_BUFSIZE 32
#define QN8027_CONF_FILE "/data/qn8027.setting"

#define QN8027_DEV_MAJOR	228
#define QN8027_DEV_MINOR	0

#define QN8027_DEV_NAME             "QN8027"
#define QN8027_I2C_SLAVE_ADDR		0x2C

#define QN8027_POWER_ENABLE_PIN  5

#define R_TXRX_MASK    0x20

#define SYSTEM        0x00
#define CH            0x01
#define CH_STEP       0x00
#define RDSD0         0x08
#define PAG_CAL       0x1f
#define CID2          0x06
#define RDSEN         0x80
#define TXREQ         0x20
#define CH_CH         0x03
#define RDSTXRDY      0x04
#define TX_FDEV       0x11  // FDEV on datasheet

typedef struct
{
	unsigned long channel;
}QN8027_CONF;

/*----------------------------------------------------------------------------*/
#define QN8027_TAG                  "[QN8027] "
#define QN8027_FUN(f)               printk(KERN_ERR QN8027_TAG"%s\n", __FUNCTION__)
#define QN8027_ERR(fmt, args...)    printk(KERN_ERR QN8027_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
#define QN8027_LOG(fmt, args...)    printk(KERN_ERR QN8027_TAG fmt, ##args)


#define QN8027_DEFCH       8800
#define FREQ2CHREG(freq)   ((freq-7600)/5)

typedef enum
{
	QN8027_POWEROFF = 0,
	QN8027_POWERON  = 1,
}QN8027_POWER_TYPE;

#define QN8027_IOC_MAGIC        0xf6

#define QN8027_IOCTL_POWERUP              _IOWR(QN8027_IOC_MAGIC, 0, int32_t)
#define QN8027_IOCTL_SETCHENNEL           _IOWR(QN8027_IOC_MAGIC, 1, int32_t)
#define QN8027_IOCTL_GETINFO              _IOWR(QN8027_IOC_MAGIC, 2, int32_t)
#define QN8027_IOCTL_GETPOWERUP_STATUS    _IOWR(QN8027_IOC_MAGIC, 3, int32_t)


//#define QN8027_IOCTL_POWERUP       1
//#define QN8027_IOCTL_SETCHENNEL    2
//#define QN8027_IOCTL_GETINFO       3


void QND_TuneToCH(int freq);
void QN_ChipInitialization(void);

#endif //XXXX_QN8027_H

参考文件:

http://blog.chinaunix.net/uid-24148050-id-120532.html

你可能感兴趣的:(MTK,i2c驱动,QN8027驱动源码)