当前位置:嗨网首页>书籍在线阅读

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 };