1. 程式人生 > >MTK I2C驅動程式碼分析

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

I2C開始和結束訊號
   開始訊號:SCL為高電平時,SDA由高電平向低電平跳變,開始傳送資料。
   結束訊號:SCL為高電平時,SDA由低電平向高電平跳變,結束傳送資料。

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架構圖:

程式碼分析:

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);


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

第1510行,註冊下降沿中斷,中斷處理函式為mt_i2c_irq。該函式讀取I2C中斷狀態,喚醒等待對列i2c->wait佇列。

第1521行,呼叫i2c_add_numbered_adapter(&i2c->adap);該函式會呼叫到__i2c_add_numbered_adapter,其程式碼如下:

1090行,分配ID號,IDR機制具體描述如下:

IDR機制在Linux核心中指的是整數ID管理機制。實質上來講,這就是一種將一個整數ID號和一個指標關聯在一起的機制。這個機制最早在032月加入核心,當時作為POSIX定時器的一個補丁。現在,核心中很多地方都可以找到它的身影。

IDR機制適用在那些需要把某個整數和特定指標關聯在一起的地方。例如,在IIC匯流排中,每個裝置都有自己的地址,要想在總線上找到特定的裝置,就必須要先發送裝置的地址。當介面卡要訪問總線上的IIC裝置時,首先要知道它們的ID號,同時要在核心中建立一個用於描述該裝置的結構體,和驅動程式。將ID號和裝置結構體結合起來,如果使用陣列進行索引,一旦ID號很大,則用陣列索引會佔據大量記憶體空間。這顯然不可能。或者用連結串列,但是,如果匯流排中實際存在的裝置很多,則連結串列的查詢效率會很低。此時,IDR機制應運而生。該機制內部採用紅黑樹實現,可以很方便的將整數和指標關聯起來,並且具有很高的搜尋效率。

1096行呼叫i2c_register_adapter註冊adapter,主要程式碼如下:

1013行,註冊&adap->dev.

1020行,呼叫class_compat_create_link,程式碼如下:

即建立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函式,該函式程式碼如下:

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/[email protected]/[email protected]

request_module("%s%s", I2C_MODULE_PREFIX, info.type);即請求載入 i2c:ncp1854

而外部加的模組則在之前的i2c_scan_static_board_info函式已作了處理。

第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裝置。

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

postcore_initcall(i2c_init);

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

1811行對driver->detect || !address_list進行判斷,如I2C驅動這兩個成員變數沒有賦值,則直接退出。

i2c_register_board_info函式解析:

生成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關係見下圖:

I2c資料傳送程式碼流程:

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

該函式定義並初始化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函式。

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

參考檔案:

相關推薦

MTK I2C驅動程式碼分析

   I2C匯流排(I2C bus,Inter-IC bus)是一個雙向的兩線連續匯流排,提供積體電路(ICs)之間的通訊線路。I2C匯流排是一種序列擴充套件技術,最早由Philips公司推出。Philips公司推出的I2C匯流排採用一條資料線(SDA),加一條時鐘線(SC

MTK UART驅動程式碼分析

首先參考網上的一些資料,給出UART驅動的整體描述與框架, 在 linux 系統中,tty 表示各種終端。終端通常都跟硬體相對應。比如對應於輸入裝置鍵盤 滑鼠,輸出裝置顯示器的控制終端和串列埠終端。 最上面的使用者空間會有很多對底層硬體的操作,像 read,write 

linux驅動由淺入深系列:usb子系統之四(android平臺滑鼠驅動程式碼分析

android上的usb口是支援OTG(on the go)的,USB OTG既可以作為Host又可以作為Device,我們本文來看一下android手機作為Host連線滑鼠的情況。OTG是如何做到既可以做Host又可以作為Device的呢 標準usb接頭中有四根線:vbu

Linux ASoC音訊驅動架構 及 Machine驅動程式碼分析

【軟體框架】     在對要做的事情一無所知的時候,從全域性看看系統的拓撲圖對我們認識新事物有很大的幫助。Audio 部分的驅動程式框架如下圖所示:     這幅圖明顯地分為 3 級。     上方藍色系的 ALSA Kernel 整體屬於Linux Kernel,是原生

嵌入式Linux——IIC驅動(2):i2c驅動框架分析

簡介:         本文主要介紹i2c匯流排框架,即對i2c的各個層次(i2c匯流排,i2c核心,i2c裝置)進行分析。同時我也會結合程式對框架進行說明。所以本文將分為兩部分,第一部分對i2c的框架進行介紹,而第二部分就是結合程式碼分析。 核心:linux-2.6.2

linux i2c子系統程式碼分析9---i2c裝置的註冊方法

/**  * i2c_register_board_info - statically declare I2C devices  * @busnum: identifies the bus to which these devices belong  * @info: vector of i2c device

I2C EEPROM驅動例項分析

    上篇分析了Linux Kernel中的I2C驅動框架,本篇舉一個具體的I2C裝置驅動(eeprom)來對I2C裝置驅動有個實際的認識。     s3c24xx系列集成了一個基於I2C的eeprom裝置at24cxx系列。at24cxx系列晶片包含at24c01

MTK 驅動(38)---MTK 待機問題分析

mtk 平臺待機問題分析: 1 待機問題分類  1)無法待機:  1. 需要確認 APK 是否有 partial_wakelock  2. kernel 是否持有 wakeup source 不釋放.  2)可以待機,但是喚醒頻繁.  1. 是否有開啟資料連線,

Linux I2C驅動分析與實現--例子

通過上篇《Linux I2C驅動分析與實現(二)》,我們對Linux子系統已經不陌生,那麼如何實現I2C驅動呢? 編寫客戶驅動的方法 在核心中有兩種方式的i2c客戶驅動的編寫方法,一種叫legacy傳統方式,另一種是newstyle方式. 前 一種legacy是一種舊式的方法,在2.

Linux裝置驅動程式架構分析之一個I2C驅動例項

作者:劉昊昱  核心版本:3.10.1 編寫一個I2C裝置驅動程式的工作可分為兩部分,一是定義和註冊I2C裝置,即i2c_client;二是定義和註冊I2C裝置驅動,即i2c_driver。下面我們就以mini2440的I2C裝置at24c08 EEPROM為例,介紹如

MTK平臺camera驅動架構分析

MTK6580 AndroidO(android8.1)版本camera 驅動分析 CAMERA驅動整個框架分為:三個部分hal部分邏輯呼叫,kernel層的通用驅動sensorlist.c 和具體IC的驅動xxxx_mipi_raw.c 這裡主要介紹kernel部分和HAL層部分。

linux驅動學習(八) i2c驅動架構(史上最全) davinc dm368 i2c驅動分析

預備知識 在閱讀本文最好先熟悉一種i2c裝置的驅動程式,並且瀏覽一下i2c-core.c以及晶片提供商的提供的i2c匯流排驅動(i2c-davinci.c)。標題黨請見諒! 其實i2c介面非常的簡單,即使用51單片的gpio來模擬i2c,編寫一個e2prom或者其他i2c介

FPGA音訊編解碼驅動I2C寫入程式碼

FPGA音訊編解碼驅動及I2C寫入程式碼 使用音訊編解碼晶片為WM8731,其通過I2C對WM8731進行暫存器寫入,將需要寫入的資料放入例化的ROM塊中,通過狀態機控制資料的寫入;通過對50M和24M的時鐘分頻提供WM8731的主時鐘和位寫入時鐘,資料沒有進行

Intel 82599 ixgbe & ixgbevf CNA 卡驅動分析03——部分功能程式碼分析

I/O Operations and Activities      使用SR-IOV 的根本原因就是使得虛擬機器中的一個驅動可以直接訪問PCI進行I/O操作,並能夠在虛擬機器之間共享裝置。Intel VF 驅動了解自己執行在一個虛擬化的環境中擁有優先的PCI資源。      可用的資源包括基本的接收和

linux驅動由淺入深系列:camera驅動之二(基於高通平臺的V4L2結構及程式碼分析

在上一篇文章中介紹了camera的基礎知識和相關概念,我們一起來了解一下驅動相關的程式碼結構。本文以高通+android平臺為示例,首先看一下整體框圖:這張圖是從整體上來看的1,圖中最下面的是kernel層的驅動,其中按照V4L2架構實現了camera sensor等驅動,向

Linux下I2C驅動分析(一)

        最近在做一個基於全志A33晶片的android移植時發現嵌入式裝置很多都用到了I2C匯流排通訊,比如說攝像頭,G-sensor,觸控式螢幕等,為此我覺得很好的理解I2C裝置驅動在今後的嵌入式開發中是非常有好處的,而目前我也是處於學習階段,便將這些學習的過程給

i2c驅動程式(2) i2c_driver probe被呼叫的流程分析

(本文為個人的筆記 難免有錯 望各位高人賜教指正 謝謝!) i2c驅動程式i2c_driver probe被呼叫的流程分析 step1 i2c_add_driver(&at24_driver); step2 i2c_register_driver(T

jni呼叫kernel驅動檔案程式碼分析

轉自: https://www.cnblogs.com/armlinux/archive/2012/01/14/2396768.html 重溫了一下程式碼流程,留個腳印,以備後用                                             

i2c驅動程式全面分析,從adapter驅動程式到裝置驅動程式

開發板    :mini2440 核心版本:linux2.6.32.2 驅動程式參考:韋東山老師畢業班i2c 內容概括:    1、adapter client 簡介2、adapter 驅動框架      2.1 裝置側2.2 驅動側         2.2.1 probe

nvdimm 驅動分析

list smb 通過 虛擬 數據一致性 簡介 而在 異常 .mm nvdimm 驅動的分析 1.驅動的簡介 nvdimm在內核中的驅動,是基於一個典型的字符類型的設備驅動模型去實現的。因此,同樣需要實現設備驅動的幾個典型操作:open/release/mmap/ioctl