Linux--核心---I2C匯流排驅動分析 以linux3.10.0 RK3288為例
Linux 3.10.0 iic匯流排註冊過程
I2C匯流排驅動包括I2C介面卡驅動載入與解除安裝以及I2C匯流排通訊方法
I2C核心提供了i2c_adapter的增加和刪除函式、i2c_driver的增加和刪除函式、i2c_client的依附和脫離函式
以及i2c傳輸、傳送和接收函式。
1、I2C核心 i2c_core.c
I2C核心提供了i2c_adapter的增加和刪除函式、i2c_driver的增加和刪除函式、i2c_client的依附和脫離函式
以及i2c傳輸、傳送和接收函式。
2、I2C匯流排驅動 i2c_rockchip.c
I2C匯流排驅動包括I2C介面卡驅動載入與解除安裝以及I2C匯流排通訊方法。其中I2C介面卡驅動載入(與解除安裝)要完成初
始化(釋放)I2C介面卡所使用的硬體資源,申請I/0地址、中斷號、通過i2c_add_ adapter()新增i2c_adapter
的資料結構(通過i2c_del_adapter()刪除i2c_adapter的資料結構)的工作。12C匯流排通訊方法主要對特定的I2C
介面卡實現i2c_algorithm的master_xfer()方法來實現i2c_ msg的傳輸。不同的介面卡對應的master_xfer()
方法由其處理器的硬體特性決定。
3、I2C裝置驅動 i2c_mydriver.c
I2C裝置驅動主要用於I2C裝置驅動模組載入與解除安裝以及提供I2C裝置驅動檔案操作介面。I2C裝置驅動的模組加
載通用的方法遵循以下流程:首先通過register_chrdev()將I2C設備註冊為一個字元裝置,然後利用I2C核心
中的i2c_add_adapter()新增i2c_driver。呼叫i2c_add_adapter()過程中會引發i2c_driver結構體中的
YYY_attach_adapter()的執行,它通過呼叫I2C核心的i2c_probe()實現物理裝置的探測。i2c_probe()會引發
yyy_detect()的呼叫。yyy_detect()中會初始化i2c_ client,然後呼叫核心的i2c_attach_client()通知I2C
核心此時系統中包含了一個新的I2C裝置。之後會引發I2C裝置驅動中yyy_init_client()來初始化裝置。解除安裝
過程執行相反的操作。
關心的主要檔案
kernel\drivers\i2c\busses\i2c-rockchip.c匯流排驅動程式,不同的晶片有不同的匯流排驅動程式
kernel\drivers\i2c\i2c-core.c 核心i2c核心程式
關心的幾個主要結構體
static struct platform_driver rockchip_i2c_driver = { //匯流排驅動是一個platform_driver型別
.probe = rockchip_i2c_probe, //在probe裡完成adapter,client註冊(硬體資源的繫結中斷號,引腳,時鐘,控制器暫存器等)
.remove = rockchip_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "rockchip_i2c",
.pm = ROCKCHIP_I2C_PM_OPS,
.of_match_table = of_match_ptr(rockchip_i2c_of_match),
},
};
static int __init rockchip_i2c_init_driver(void)
{
return platform_driver_register(&rockchip_i2c_driver);
}
struct rockchip_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1;
struct i2c_msg *msg; //傳送的從機地址和資料,時鐘頻率
unsigned int is_busy;
int error;
unsigned int msg_ptr;
unsigned int irq;//中斷號
enum rockchip_i2c_state state; //i2c狀態 開始 結束 讀/寫
unsigned int complete_what;
unsigned long clkrate;
void __iomem *regs; //存放對應控制器的暫存器
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap; //i2c介面卡 對應一個硬體i2c控制器
unsigned long scl_rate;
unsigned long i2c_rate;
unsigned int addr;
unsigned char addr_1st, addr_2nd;
unsigned int mode;
unsigned int count;
unsigned int check_idle;//標記是否有裝置樹資源
int sda_gpio, scl_gpio;
struct pinctrl_state *gpio_state;
};
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 */ //其中master_xfer()介面是硬體相關的,就是收發函式,操作控制器暫存器
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 */ dev->p->driver_data = rockchip_i2c;私有值指向當前i2c結構體(包含控制器暫存器io腳等
特定資源),of_node指向當前註冊的platform_device->dev.of_node;也包含特定的裝置樹資源//會被註冊進dev裝置掛接到核心中的連結串列中來管理。
int nr; //id號
char name[48]; //在i2c匯流排驅動裡命名
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients; //將一個i2c_adapter物件和它所屬的i2c_client物件以及相應的i2c_driver物件連線到一起
struct i2c_bus_recovery_info *bus_recovery_info;
};
struct i2c_client {
unsigned short flags; /* div., see below */ 各種模式標識 地址型別,是否有wakeuo-source等
unsigned short addr; /* chip address - NOTE: 7bit */子節點裡定義的reg屬性
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE]; //request_module("%s%s", I2C_MODULE_PREFIX, info.type);#define I2C_MODULE_PREFIX "i2c:"
struct i2c_adapter *adapter; /* the adapter we sit on */匯流排註冊的時候指向對應adapter
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */表示這是一個device,dev.of_node指向子節點的device_node dev.bus=&i2c_bus_type
int irq; /* irq issued by device */使用的中斷號
struct list_head detected; //將所有i2c_client連在一起的節點
};
///////////////////////////////註冊過程,目的:完成adapter,i2c_client的註冊//////////////////////
兩個問題,也是主要的兩個裝置
1,adapter是什麼,裡面有些什麼?
答:在i2c種一個adapter對應一個硬體上的控制器,裡面包含i2c的收發函式,控制器的暫存器地址,io口等資源;它被i2c匯流排驅動建立, 並和i2c_client相連(如果裝置樹裡有子節點)
2,i2c_client是什麼,裡面有些什麼?
答:i2c_client對應一個掛載在i2c總線上的i2c裝置,裡面包含裝置的地址,adapter的指標(註冊時會設定),裝置驅動driver的指標(裝置驅動註冊時繫結)。
描述一個掛接在硬體i2c總線上的裝置的裝置資訊,即i2c裝置的裝置物件
linux核心裡 i2c_adapter,i2c_client,i2c_driver這幾個結構體真的是我中有你你中有我啊,繞來繞去
I2C匯流排驅動是一個platform_driver型別的驅動所以使用platform_driver_register來註冊
platform_driver_register(&rockchip_i2c_driver);
關於platform_driver_register的過程在上篇文章中分析,這裡只要知道呼叫這個函式註冊後就會進入i2c匯流排驅動的probe,完成申請資源,初始化,提供介面等工作,接下來分析匯流排的rockchip_i2c_probe()
rockchip_i2c_probe(struct platform_device *pdev)//獲取platform_device的資源
i2c_add_adapter(&i2c->adap);配置adapter資源:將資源填入adapter的結構體中包含1、收發函式指標;2、裝置樹資源:名字,暫存器地址,中斷,id,io口
__i2c_add_numbered_adapter(adapter);
i2c_register_adapter(adap);配置adapter資源在sysfs裡生成
device_register(&adap->dev);註冊adapter裝置 dev裡已經包含了rockchip_i2c; 裝置樹對應的of_node等特定資源,已經對應上了一個i2c控制器
of_i2c_register_devices(&i2c->adap);//建立i2c_client 也就是掛在i2c總線上的從裝置
for_each_available_child_of_node(adap->dev.of_node, node)遍歷裝置樹裡的每個子節點
i2c_new_device(adap, &info);如果子節點存在則呼叫
device_register(&client->dev);//註冊i2c_client裝置 這個dev會找到這個i2c_client,i2c_client裡包含了adapter
之後自己寫的i2c裝置驅動就會匹配上這個client,在驅動的probe裡返回這個client的指標,這時就可以進行資料的收發了。
//////////////////////////////////////adapter收發函式的分析
adapter的收發函式共用一個rockchip_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
由i2c_msg指定是發還是收,裝置地址和資料
rockchip_i2c_xfer
rockchip_i2c_doxfer
i2c_writel
writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));//對應暫存器寫入資料就行了
struct i2c_msg {
__u16 addr; //裝置地址
__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_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#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 */ 要傳送或者接收的資料指標
#ifdef CONFIG_I2C_ROCKCHIP_COMPAT
__u32 scl_rate; /* add by kfx */
#endif
};