Linux i2c子系統(四) _從i2c-s3c24xx.c看i2c控制器驅動的編寫


"./drivers/i2c/busses/i2c-s3c2410.c"是3.14.0內核中三星SoC的i2c控制器驅動程序, 本文試圖通過對這個程序的分析, 剝離繁復的細節, 總結一套編寫i2c主機控制器驅動的框架以及一個分析內核驅動的流程.

匹配之前

1287 static int __init i2c_adap_s3c_init(void)
1288 {
1289         return platform_driver_register(&s3c24xx_i2c_driver);
1290 }
1291 subsys_initcall(i2c_adap_s3c_init);

--1291-->將主機控制器驅動在系統啟動的時候就注冊好
--1289-->這個驅動是基於platform總線的, 設備信息的部分在板級文件i2c_board_info中描述並作為platform_device隨內核啟動被注冊, 所以控制器驅動在系統啟動的時候就可以工作了

1275 static struct platform_driver s3c24xx_i2c_driver = {
1276         .probe          = s3c24xx_i2c_probe,
1277         .remove         = s3c24xx_i2c_remove,
1278         .id_table       = s3c24xx_driver_ids,
1279         .driver         = {
1280                 .owner  = THIS_MODULE,
1281                 .name   = "s3c-i2c",
1282                 .pm     = S3C24XX_DEV_PM_OPS,
1283                 .of_match_table = of_match_ptr(s3c24xx_i2c_match),
1284         },
1285 };

既然是遵循的platform編寫, 那么所有的信息都要在一個platform_driver中描述, 分析也是圍繞這個對象展開

--1276-->probe函數, 最重要的函數
--1278-->用於匹配的id表, 由於是平台文件編寫的設備信息, 所以會使用這個域作為匹配的依據, 如下

 132 static struct platform_device_id s3c24xx_driver_ids[] = {                                          
 133         {
 134                 .name           = "s3c2410-i2c",
 135                 .driver_data    = 0,
 136         }, {
 137                 .name           = "s3c2440-i2c",
 138                 .driver_data    = QUIRK_S3C2440,
 139         }, {
 140                 .name           = "s3c2440-hdmiphy-i2c",
 141                 .driver_data    = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
 142         }, { }, 
 143 }; 

我們可以在"arch/arm/plat-samsung"中找到相應的設備信息

 485 struct platform_device s3c_device_i2c0 = {
 486         .name           = "s3c2410-i2c",
 487         .id             = 0,
 488         .num_resources  = ARRAY_SIZE(s3c_i2c0_resource),
 489         .resource       = s3c_i2c0_resource,
 490 };

二者一匹配, probe就執行!

匹配之后

一旦匹配上, 分析流程就會有點變化, 驅動開發都是基於面向對象的思想的, 內核雖然給我們封裝了很多"類", 但當我們開發一個具體的驅動的時候, 還是要對其進行"繼承", 進而創建針對具體設備的資源對象, 資源對象管理着驅動中諸多函數的共用資源, 是整個驅動運行過程中資源管理者與橋梁, 主要包括:內核類+資源(io, irq,時鍾, 寄存器)+狀態表示+其他,所以, 設計驅動的工作中很重要的一個工作就是"設計資源類". 下面就是三星設計的類, 我把次要的部分剔除了

資源類

資源對象是整個驅動運作的核心, 所有的方法需要的資源都是對這個對象的操作, 它的設計是迭代的過程, 但當整個框架搭起來之后, 不應該有大的變化

 103 struct s3c24xx_i2c {
 104         wait_queue_head_t       wait;
 108         struct i2c_msg          *msg;
 109         unsigned int            msg_num;
 110         unsigned int            msg_idx;
 111         unsigned int            msg_ptr;
 113         unsigned int            tx_setup;
 114         unsigned int            irq;
 116         enum s3c24xx_i2c_state  state;
 117         unsigned long           clkrate;
 119         void __iomem            *regs;
 120         struct clk              *clk;
 121         struct device           *dev;
 122         struct i2c_adapter      adap;
 124         struct s3c2410_platform_i2c     *pdata;
 125         int                     gpios[2];
 130 };

struct s3c24xx_i2c
--108-->收到的i2c-core.c發送過來的i2c_msg對象數組首地址
--109-->i2c_msg數組的元素個數
--110-->i2c_msg數組元素的索引
--114-->使用的中斷號
--116-->當前控制器的狀態, 用枚舉量表示STATE_IDLE, STATE_START,STATE_READ,STATE_WRITE,STATE_STOP
--117-->時鍾頻率
--120-->時鍾
--121-->屬於device, 按照device來管理
--122-->構造並使用的i2c_adapter對象, 和上一篇的框架圖對應
--124-->封裝的平台信息, 是一個數組首地址, 每一個元素包括從機地址, 標志位, 總線編號等

probe

probe主要負責"申請資源+初始化+提供接口", 通過對probe的分析, 就可以對整個驅動的構建有一個
提綱挈領的理解

1072 static int s3c24xx_i2c_probe(struct platform_device *pdev)
1073 {
1074         struct s3c24xx_i2c *i2c;
1075         struct s3c2410_platform_i2c *pdata = NULL;
1076         struct resource *res;
1077         int ret;
1078 
1079         if (!pdev->dev.of_node) {
1080                 pdata = dev_get_platdata(&pdev->dev);
1085         }
1086 
1087         i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
1092 
1093         i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
1098 
1099         i2c->quirks = s3c24xx_get_device_quirks(pdev);
1100         if (pdata)
1101                 memcpy(i2c->pdata, pdata, sizeof(*pdata));
1102         else
1103                 s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
1104 
1105         strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
1106         i2c->adap.owner   = THIS_MODULE;
1107         i2c->adap.algo    = &s3c24xx_i2c_algorithm;
1108         i2c->adap.retries = 2;
1109         i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
1110         i2c->tx_setup     = 50;
1111 
1112         init_waitqueue_head(&i2c->wait);
1113 
1114         /* find the clock and enable it */
1116         i2c->dev = &pdev->dev;
1117         i2c->clk = devm_clk_get(&pdev->dev, "i2c");
1124 
1126         /* map the registers */
1128         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1129         i2c->regs = devm_ioremap_resource(&pdev->dev, res);
1136 
1137         /* setup info block for the i2c core */
1139         i2c->adap.algo_data = i2c;
1140         i2c->adap.dev.parent = &pdev->dev;
1141 
1142         i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
1143 
1144         /* inititalise the i2c gpio lines */
1146         if (i2c->pdata->cfg_gpio) {
1147                 i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));
1148         } else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) {
1149                 return -EINVAL;
1150         }
1151 
1152         /* initialise the i2c controller */
1154         clk_prepare_enable(i2c->clk);
1155         ret = s3c24xx_i2c_init(i2c);
1156         clk_disable_unprepare(i2c->clk);
1161         /* find the IRQ for this unit (note, this relies on the init call to
1162          * ensure no current IRQs pending
1163          */
1165         if (!(i2c->quirks & QUIRK_POLL)) {
1166                 i2c->irq = ret = platform_get_irq(pdev, 0);
1171 
1172         ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0,
1173                                 dev_name(&pdev->dev), i2c);
1179         }
1180 
1181         ret = s3c24xx_i2c_register_cpufreq(i2c);
1192 
1193         i2c->adap.nr = i2c->pdata->bus_num;
1194         i2c->adap.dev.of_node = pdev->dev.of_node;
1196         ret = i2c_add_numbered_adapter(&i2c->adap);
1202 
1203         platform_set_drvdata(pdev, i2c);
1204 
1205         pm_runtime_enable(&pdev->dev);
1206         pm_runtime_enable(&i2c->adap.dev);
1209         return 0;
1210 }

s3c24xx_i2c_probe()
--1074-1077-->准備好指針與變量, 准備從傳入的對象中提取數據, 雖說這是C89的語法要求, 但這種寫法確實比較舒服, 遇到不認識的變量就去函數開頭找
--1079-->如果pdev->dev.of_node為空, 表示設備不是通過設備樹獲得的, 那么就調用dev_get_platdata獲取pdev->dev.oplatform_data中的數據, 顯然, 在編寫設備文件的時候這里藏的是一個s3c2410_platform_i2c對象, 所以我們用pdata取出來以備使用
--1087-->pdev->dev是device類型, 以它為的detach為標志分配一個我們自己的對象的空間並將分配的首地址返回給i2c。 這里使用的是devm_kzalloc(), 函數 devm_kzalloc()和kzalloc()一樣都是內核內存分配函數,但是devm_kzalloc()是跟設備(device)有關的,當設備(device)被detached或者驅動(driver)卸載(unloaded)時,內存會被自動釋放。另外,當內存不在使用時,可以使用函數devm_kfree()釋放。而kzalloc()則需要手動釋放(使用kfree()),但如果工程師檢查不仔細,則有可能造成內存泄漏
--1100-1103-->如果在--1079--中獲得了相應的s3c2410_platform_i2c對象地址,就將其拷貝到資源對象中的相應的域存起來,否則自己去設備樹中找
--1106-1110-->使用賦值的方式直接對一部分資源對象的域進行初始化
--1112-->初始化資源對象中的等待隊列頭wait_queue_head_t wait
--1116-->初始化資源對象中的device *dev
--1117-->初始化資源對象中的struct *clk
--1128-->獲取pdev中的地址resource, ioremap之后用於初始化資源對象中的regs域, 使用的是devm_ioremap_resource(), 同樣是基於device的資源自動回收API
--1139-->將自定義資源對象指針藏到algo_data中, 和--1203--的作用一樣, 給xfer()接口函數用
--1140-->初始化資源對象中的i2c_adapter對象中的部分成員, 指定其父設備是控制器設備的device域
--1142-->初始化資源對象中的pctrl域, 使用的是devm_pinctrl_get_select_default()
--1147-->使用to_platform_device(其實就是container_of)通過i2c->dev找到包含它的platform_device對象, 回調cfg_gpio()函數, 配置GPIO引腳
--1154-->初始化時鍾
--1166-->獲取中斷資源
--1171-->注冊中斷, devm_request_irq
--1193-1194-->初始化i2c->adap對象, 總線編號是來自於設備的
--1196-->將構造的adapter對象注冊到內核
--1203-->設置私有數據, pdev->dev->p->driver_data = i2c; 由於i2c->dev==pdev->dev, 所以其實就是將資源對象的首地址賦值給藏到device->device_private->driver_data中, 因為所有的接口都是使用platform_device作為形參的, 這種方法可以方便的找到自定義資源對象, 所以才叫void * driver_data
--1205-->設置dev的電源管理
--1206-->設置adap的電源管理

s3c24xx_i2c_algorithm

probe中我們最關心的就是這個--1107--實現的接口了, i2c-core最終就是通過algo->xfer將設備驅動的數據發送出去的, 是一個硬件相關的函數

 787 static const struct i2c_algorithm s3c24xx_i2c_algorithm = {                                        
 788         .master_xfer            = s3c24xx_i2c_xfer,
 789         .functionality          = s3c24xx_i2c_func,
 790 };
 748 static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
 750 {
 751         struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
 758         for (retry = 0; retry < adap->retries; retry++) {
 760                 ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
 770                 udelay(100);
 771         }
 776 }

s3c24xx_i2c_xfer()
--760-->循環調用發送函數, 函數的實現如下, 可以看到其中對寄存器的讀寫, 設備驅動中的發送的請求, 就是通過這些readl(), writel()來實現的.
--770-->時序要求!

 256 static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,                                     
 257                                       struct i2c_msg *msg)
 258 {
 275         /* todo - check for whether ack wanted or not */
 276         s3c24xx_i2c_enable_ack(i2c);
 277 
 278         iiccon = readl(i2c->regs + S3C2410_IICCON);
 279         writel(stat, i2c->regs + S3C2410_IICSTAT);
 280 
 281         dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
 282         writeb(addr, i2c->regs + S3C2410_IICDS);
 287         ndelay(i2c->tx_setup);
 288 
 290         writel(iiccon, i2c->regs + S3C2410_IICCON);
 291 
 292         stat |= S3C2410_IICSTAT_START;
 293         writel(stat, i2c->regs + S3C2410_IICSTAT);
 294 
 295         if (i2c->quirks & QUIRK_POLL) {
 296                 while ((i2c->msg_num != 0) && is_ack(i2c)) {
 297                         i2c_s3c_irq_nextbyte(i2c, stat);
 298                         stat = readl(i2c->regs + S3C2410_IICSTAT);
 299 
 300                         if (stat & S3C2410_IICSTAT_ARBITR)
 301                                 dev_err(i2c->dev, "deal with arbitration loss\n");
 302                 }
 303         }
 304 }


免責聲明!

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



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