Linux驅動之IIC總線


<作用>

電子設備中有很多IIC設備之間需要進行相互通信,這樣就產生了IIC總線,常用來實現設備之間的數據通信。
 
<IIC總線結構>
IIC總線只有兩條線,一條是串行數據線(SDA),另外一條是串行時鍾線(SCL).
注:每一個連接到總線上的設備都有一個唯一的地址可以訪問(這一點有點像USB設備)
 
<IIC總線信號類型>
a:開始信號(S):當SCL信號為高電平,SDA的電平由高電平變為低電平表示開始傳輸數據。
b:結束信號(P):當SCL信號為高電平,SDA的電平由低電平變為高電平表示結束傳輸數據。
c:相應信號(ACK):從機接收到8位數據后,在第9個時鍾周期,拉低SDA電平,表示接受到數據,這個信號稱為應答信號。
 
<IIC數據傳輸方式>
主機(IIC控制器)主要通過SDA向從機發送數據,當總線處於空閑的時候SDA和SCL都處於高電平。
a:當主機檢測到總線處於空閑狀態時,主機發送給開始信號(S)
b:主機發出8位數據,8位數據中,前7位中表示從機地址,第8位表示數據的傳輸方向。
c:和地址匹配的從機發出相應信號。
d:從機傳輸一系列響應序列
e:主機接收到這些數據后,發出結束信號P,該次數據傳輸完成。
 
<IIC控制器結構圖>
a:主要涉及的寄存器
IICCON:控制寄存器
IICSTAT:狀態寄存器
IICADD:地址寄存器
 
IICCDS:接收和發送的數據存儲寄存器
 
<linux 驅動之IIC>
a:背景
對於IIC設備的控制,但是IIC設備太多了,為了很好的管理這些設備(不同等的設備名,管理各種設備,不同設備的地址,不同設備對應的驅動),Linux內核開發了如下結構體:
b:IIC設備(i2c_client)
struct i2c_client {
unsigned short flags;
unsigned short addr;
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; //通過該指針可以找到適配器,然后通過適配器找到                       //適配器算法中的發送消息和接受函數完成同IIC設備                          //的通信              
struct i2c_driver *driver;
struct device dev; //用於建立設備模型
int irq;
struct list_head detected;
};
 
c:IIC驅動(i2c_driver)
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;//用於建立設備模型
const struct i2c_device_id *id_table;//是一個數組,保存該驅動支持的所有的 的                               //設備信息 
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
d:IIC適配器(i2c_adapter)//其本質表示適配器這個設備
-----i2c_adapter is the structure used to identify a physical i2c bus along with the access algorithms necessary to access it.內核中可以有很多總線適配器,其中內核中有一個靜態指針數組adapters記錄了所有已經注冊的總線適配器。
 
struct i2c_adapter {
struct module *owner;
unsigned int class;  /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
 
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
 
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
 
int nr;
char name[48];
struct completion dev_released;
 
struct mutex userspace_clients_lock;
struct list_head userspace_clients;//用於連接所有連接到這個適配器上的i2c設備
};
d:IIC適配器驅動(i2c_algorithm)
 
/*
 * The following structs are for those who like to implement new bus drivers:
 * i2c_algorithm is the interface to a class of hardware solutions which can
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
 * to name two of the most common.
 */
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
  to NULL. If an adapter algorithm can do SMBus access, set
  smbus_xfer. If set to NULL, the SMBus protocol is simulated
  using common I2C messages */
/* master_xfer should return the number of messages successfully
  processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
  int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
  unsigned short flags, char read_write,
  u8 command, int size, union i2c_smbus_data *data);
 
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
e:注意:i2c_driver和i2c_client屬於設備層,i2c_adapter和i2c_algorithm屬於總線層。他們之間的關系如圖所示:
f:IIC設備之間的通信方式
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
填該結構體,用於同IIC設備通信。
 
<IIC設備驅動流程>
a:流程圖
 
(1)xxx_init()和i2c_add_driver()實現對IIC子系統的初始化,這項工作由內核完成。
(2)xxx_attach_adapter()用來將IIC設備驅動和適配器掛接在一起,這樣才能 IIC設備驅動才能利用 適配器驅動控制 適配器和IIC設備 通信。(注意:在這兩步中間需要根據具體適配器實現相應的驅動,以及相應struct algorithm{}的實現)
(3)i2c_probe()函數是在檢測到IIC設備后調用的函數,進一步實現IIC設備的各數據結構的初始化。包括下面的函數都為了實現相應的功能。
 
b:IIC子系統初始化
(1)初始化函數
(2)卸載函數
 
c:適配器驅動程序
(1)IIC適配器內核結構
注意:
"struct i2c_adapter adap"
這是內核提供的IIC適配器的結構體,特定類型的適配器結構體需要在這個結構上進行擴充(面向對象的思想)。
"void __iomem  *regs"
指向IIC適配器的控制寄存器地址,注意這個地址是物理地址
"struct resource *ioarea"
指向適配器申請的資源,這里應該是內存資源,先進行申請,然后進行映射
"struct i2c_msg *msg"
用來實現IIC設備驅動於設備之間的通信
 
2)適配器加載函數
(3)適配器卸載函數
注意:這個適配器驅動跟字符設備驅動很相似,因為這里不需要同用戶空間進行交互,所以這里沒有相應的fops,但是有相應的 類似fops—— struct i2c_algorithm{}。和字符設備驅動相似,
重點就是要實現這個類似fops,struct i2c_algorithm{}中的相應的函數通過操作"void __iomem  *regs"中的相應的寄存器。
 
<IIC適配器驅動模塊的加載與卸載>
a:這里將適配器驅動定性為平台設備
加載函數
b:卸載函數
c:注意其中的"s3c2410_i2c_driver"
d:對於一個平台設備來"s3c2410_i2c_probe()"函數是很重要的
(1)" s3c2410_i2c_probe()"中的"s3c2410_i2c_init()"函數
 
e:與" s3c2410_i2c_probe()"相反的函數是s3c2410_i2c_remove()。
 
<IIC設備驅動實例>
a:設備文件操作函數集
static struct file_operations eep_fops = {
.owner = THIS_MODULE,
.llseek = eep_llseek,
.read = eep_read,
.ioctl = eep_ioctl,
.open = eep_open,
.release = eep_release,
.write = eep_write,
};
static dev_t dev_number; /* Allotted Device Number */
static struct class *eep_class; /* Device class */
/* Per-device client data structure for each
* memory bank supported by the driver
*/
 
b:IIC設備結構體
struct eep_bank {
struct i2c_client *client; /* I2C client for this bank */
unsigned int addr; /* Slave address of this bank */
unsigned short current_pointer; /* File pointer */
int bank_number; /* Actual memory bank number */
/* ... */ /* Spinlocks, data cache for slow devices,.. */
};
#define NUM_BANKS 2 /* Two supported banks */
#define BANK_SIZE 2048 /* Size of each bank */
struct ee_bank *ee_bank_list; /* List of private data
structures, one per bank */
c:初始化函數
int __init
eep_init(void)
{
int err, i;
/* Allocate the per-device data structure, ee_bank */
ee_bank_list = kmalloc(sizeof(struct ee_bank)*NUM_BANKS, GFP_KERNEL);
memset(ee_bank_list, 0, sizeof(struct ee_bank)*NUM_BANKS);
/* Register and create the /dev interfaces to access the EEPROM
banks. Refer back to Chapter 5, "Character Drivers" for more details */
if (alloc_chrdev_region(&dev_number, 0,
NUM_BANKS, "eep") < 0) {
printk(KERN_DEBUG "Can't register device\n");
return -1;
}
eep_class = class_create(THIS_MODULE, DEVICE_NAME);
for (i=0; i < NUM_BANKS;i++) {
/* Connect the file operations with cdev */
cdev_init(&ee_bank[i].cdev, &ee_fops);
/* Connect the major/minor number to the cdev */
if (cdev_add(&ee_bank[i].cdev, (dev_number + i), 1)) {
printk("Bad kmalloc\n");
return 1;
}
device_create(eep_class, NULL, MKDEV (MAJOR) (dev_number),i),
"eeprom%d", i);
}
/* Inform the I2C core about our existence. See the section
"Probing the Device" for the definition of eep_driver */
err = i2c_add_driver(&eep_driver);
if (err) {
printk("Registering I2C driver failed, errno is %d\n", err);
return err;
}
printk("EEPROM Driver Initialized.\n");
return 0;
}
(1) eep_driver結構體如下所示:
eep_init()在設備初始化的時調用i2c_add_driver()將注冊eep_probe()
static struct i2c_driver eep_driver =
{
.driver = {
.name = "EEP", /* Name */
},
.id = I2C_DRIVERID_EEP, //設備標志符I2C_DRIVERID_EEP對於每個設備應該是唯一的
.attach_adapter = eep_probe, /* Probe Method */
.detach_client = eep_detach, /* Detach Method */
};
(2)當IIC核心調用表明主機適配器已經存在的客戶驅動程序的probe()方法時,其會發過來調用i2c_probe(),該函數的參數是驅動程序所關聯的設備地址以及具體的探測函數attach()。
 
#include <linux/i2c.h>
/* The EEPROM has two memory banks having addresses SLAVE_ADDR1
* and SLAVE_ADDR2, respectively
*/
static unsigned short normal_i2c[] = {
SLAVE_ADDR1, SLAVE_ADDR2, I2C_CLIENT_END
};
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.probe = ignore,
.ignore = ignore,
.forces = ignore,
};

static int eep_probe(struct i2c_adapter *adapter)
{
/* The callback function eep_attach(), is shown in Listing 8.5 */
return i2c_probe(adapter, &addr_data, eep_attach);
}

int eep_attach(struct i2c_adapter *adapter, int address, int kind)
{
static struct i2c_client *eep_client;
eep_client = kmalloc(sizeof(*eep_client), GFP_KERNEL);
eep_client->driver = &eep_driver; /* Registered in Listing 8.2 */
eep_client->addr = address; /* Detected Address */
eep_client->adapter = adapter; /* Host Adapter */
eep_client->flags = 0;
strlcpy(eep_client->name, "eep", I2C_NAME_SIZE);
/* Populate fields in the associated per-device data structure */
/* ... */
/* Attach */
i2c_attach_client(new_client);
}  
 
<整個程序框架>
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM