"./drivers/i2c/busses/i2c-s3c2410.c"是3.14.0內核中三星SoC的i2c控制器驅動程序, 本文試圖經過對這個程序的分析, 剝離繁複的細節, 總結一套編寫i2c主機控制器驅動的框架以及一個分析內核驅動的流程.node
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隨內核啓動被註冊, 因此控制器驅動在系統啓動的時候就能夠工做了ios
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就執行!ui
一旦匹配上, 分析流程就會有點變化, 驅動開發都是基於面向對象的思想的, 內核雖然給咱們封裝了不少"類", 但當咱們開發一個具體的驅動的時候, 仍是要對其進行"繼承", 進而建立針對具體設備的資源對象, 資源對象管理着驅動中諸多函數的共用資源, 是整個驅動運行過程當中資源管理者與橋樑, 主要包括:內核類+資源(io, irq,時鐘, 寄存器)+狀態表示+其餘,因此, 設計驅動的工做中很重要的一個工做就是"設計資源類". 下面就是三星設計的類, 我把次要的部分剔除了this
資源對象是整個驅動運做的核心, 全部的方法須要的資源都是對這個對象的操做, 它的設計是迭代的過程, 但當整個框架搭起來以後, 不該該有大的變化設計
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的分析, 就能夠對整個驅動的構建有一個
提綱挈領的理解code
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的電源管理
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 }