14-S3C6410 I2C适配器驱动的模块加载与卸载
15.5.3 S3C6410 I2C适配器驱动的模块加载与卸载
I2C适配器驱动被作为一个单独的模块加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体,如代码清单15.18所示。
代码清单15.18 S3C6410 I2C总线驱动的模块加载与卸载
1 static int _ _init i2c_adap_s3c_init(void)
2 {
3 int ret;
4
5 ret = platform_driver_register(&s3c2410_i2c_driver);
6 if (ret == 0) {
7 ret = platform_driver_register(&s3c2440_i2c_driver);
8 if (ret)
9 platform_driver_unregister(&s3c2410_i2c_driver);
10 }
11
12 return ret;
13 }
14
15 static void _ _exit i2c_adap_s3c_exit(void)
16 {
17 platform_driver_unregister(&s3c2410_i2c_driver);
18 platform_driver_unregister(&s3c2440_i2c_driver);
19 }
20 module_init(i2c_adap_s3c_init);
21 module_exit(i2c_adap_s3c_exit);
platform_driver结构体包含了具体适配器的probe()函数、remove()函数、resume()函数指针等信息,它需要被定义和赋值,如代码清单15.19所示。
代码清单15.19 platform.driver结构体
1 static struct platform_driver s3c2410_i2c_driver = {
2 .probe = s3c24xx_i2c_probe,
3 .remove = s3c24xx_i2c_remove,
4 .resume = s3c24xx_i2c_resume,
5 .driver = {
6 .owner = THIS_MODULE,
7 .name = "s3c2410-i2c",
8 },
9 };
当通过Linux内核源代码/drivers/base/platform.c文件中定义platform_driver_unregister()函数注册platform_driver结构体时,其中probe指针指向的s3c24xx_i2c_probe()函数将被调用,以初始化适配器硬件,如代码清单15.20所示。
代码清单15.20 S3C6410 I2C总线驱动中的s3c24xx_i2c_probe函数
1 static int s3c24xx_i2c_probe(struct platform_device *pdev)
2 {
3 struct s3c24xx_i2c *i2c;
4 struct s3c2410_platform_i2c *pdata;
5 struct resource *res;
6 int ret;
7
8 pdata = pdev->dev.platform_data;
9 if (!pdata) {
10 dev_err(&pdev->dev, "no platform data\n");
11 return -EINVAL;
12 }
13
14 i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
15 if (!i2c) {
16 dev_err(&pdev->dev, "no memory for state\n");
17 return -ENOMEM;
18 }
19
20 strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
21 i2c->adap.owner = THIS_MODULE;
22 i2c->adap.algo = &s3c24xx_i2c_algorithm;
23 i2c->adap.retries = 2;
24 i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
25 i2c->tx_sep = 50;
26
27 spin_lock_init(&i2c->lock);
28 init_waitqueue_head(&i2c->wait);
29
30 / 发现时钟并使能它 /
31
32 i2c->dev = &pdev->dev;
33 i2c->clk = clk_get(&pdev->dev, "i2c");
34 if (IS_ERR(i2c->clk)) {
35 dev_err(&pdev->dev, "cannot get clock\n");
36 ret = -ENOENT;
37 goto err_noclk;
38 }
39
40 dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
41
42 clk_enable(i2c->clk);
43
44 / 映射寄存器 /
45
46 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
47 if (res == NULL) {
48 dev_err(&pdev->dev, "cannot find IO resource\n");
49 ret = -ENOENT;
50 goto err_clk;
51 }
52
53 i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
54 pdev->name);
55
56 if (i2c->ioarea == NULL) {
57 dev_err(&pdev->dev, "cannot request IO\n");
58 ret = -ENXIO;
59 goto err_clk;
60 }
61
62 i2c->regs = ioremap(res->start, (res->end-res->start)+1);
63
64 if (i2c->regs == NULL) {
65 dev_err(&pdev->dev, "cannot map IO\n");
66 ret = -ENXIO;
67 goto err_ioarea;
68 }
69
70 dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
71 i2c->regs, i2c->ioarea, res);
72
73 / 设置i2c核心需要的信息 /
74
75 i2c->adap.algo_data = i2c;
76 i2c->adap.dev.parent = &pdev->dev;
77
78 / initialise the i2c controller /
79
80 ret = s3c24xx_i2c_init(i2c);
81 if (ret != 0)
82 goto err_iomap;
83
84 /* 申请中断
85 */
86
87 i2c->irq = ret = platform_get_irq(pdev, 0);
88 if (ret <= 0) {
89 dev_err(&pdev->dev, "cannt find IRQ\n");
90 goto err_iomap;
91 }
92
93 ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
94 dev_name(&pdev->dev), i2c);
95
96 if (ret != 0) {
97 dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
98 goto err_iomap;
99 }
100
101 ret = s3c24xx_i2c_register_cpufreq(i2c);
102 if (ret < 0) {
103 dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
104 goto err_irq;
105 }
106
107 /* bus_num是platform数据
108 */
109
110 i2c->adap.nr = pdata->bus_num;
111
112 ret = i2c_add_numbered_adapter(&i2c->adap);
113 if (ret < 0) {
114 dev_err(&pdev->dev, "failed to add bus to i2c core\n");
115 goto err_cpufreq;
116 }
117
118 platform_set_drvdata(pdev, i2c);
119
120 dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
121 rern 0;
122
123 ...
124 }
上述代码中的主体工作是使能硬件并申请I2C适配器使用的I/O地址、中断号等,在这些工作都完成无误后,通过I2C核心提供的i2c_add_adapter()函数添加这个适配器。当处理器包含多个I2C控制器时,我们通过板文件定义的platform数据中的bus_num进行区分。
与s3c24xx_i2c_probe()函数完成相反功能的函数是s3c24xx_i2c_remove()函数,它在适配器模块卸载函数调用platform_driver_unregister()函数时通过platform_driver的remove指针方式被调用。xxx_i2c_remove()的设计模板如代码清单15.21所示。
代码清单15.21 S3C6410 I2C总线驱动中的s3c24xx_i2c_remove函数
1 static int s3c24xx_i2c_remove(struct platform_device *pdev)
2 {
3 struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
4
5 s3c24xx_i2c_deregister_cpufreq(i2c);
6
7 i2c_del_adapter(&i2c->adap);
8 free_irq(i2c->irq, i2c);
9
10 clk_disable(i2c->clk);
11 clk_put(i2c->clk);
12
13 iounmap(i2c->regs);
14
15 release_resource(i2c->ioarea);
16 kfree(i2c->ioarea);
17 kfree(i2c);
18
19 return 0;
20 }
代码清单15.20和代码清单15.21中用到的s3c24xx_i2c结构体进行适配器所有信息的封装,类似于私有信息结构体,它与代码清单15.12所示的xxx_i2c结构体模板对应。代码清单15.22所示为s3c24xx_i2c结构体的定义。
代码清单15.22 s3c24xx_i2c结构体
1 struct s3c24xx_i2c {
2 spinlock_t lock;
3 wait_queue_head_t wait;
4 unsigned int suspended:1;
5
6 struct i2c_msg *msg;
7 unsigned int msg_num;
8 unsigned int msg_idx;
9 unsigned int msg_ptr;
10
11 unsigned int tx_setup;
12 unsigned int irq;
13
14 enum s3c24xx_i2c_state state;
15 unsigned long clkrate;
16
17 void __iomem *regs;
18 struct clk *clk;
19 struct device *dev;
20 struct resource *ioarea;
21 struct i2c_adapter adap;
22
23 #ifdef CONFIG_CPU_FREQ
24 struct notifier_block freq_transition;
25 #endif
26 };