PLATFORM设备驱动

  字符设备,杂项设备虽然简单,但是在工程中,比如SDK中,通常都使用platform设备驱动来实现硬件驱动,为什么呢?先看看platform设备驱动的结构:

  platform由两部分组成:设备--platform_device和驱动--platform_driver。它们之间通过platform总线来绑定,这个我们不需要关心,内核里面的东西。

  platform总线是一个虚拟的总线,跟其他总线(比如:I2C,SPI,USB,PCIE)一样,当系统注册一个设备(platform_device)的时候,会通过它去寻找与之匹配的驱动(platform_driver);当注册一个驱动(platform_driver)的时候,也会通过platform总线去寻找一个相应的设备(platform_device)。

  platform设备在注册的时候,将其使用的资源也注册进去,由内核统一管理,在 驱动程序 使用这些资源时 使用统一的接口,这样提高了程序的可移植性。

  好吧,看看相关的结构体吧。

 1 struct platform_device {
 2
 3       const char    * name ;  /* 设备名 */
 4
 5       int    id ;   //  设备名编号, 配合设备名使用
 6
 7       struct device    dev ;
 8
 9       u32    num_resources ;
10
11       struct resource    * resource ;   /* 设备资源 */
12
13   }
14
15   struct resource {
16
17       resource_size_t start; /* 资源的起始物理地址 */
18
19       resource_size_t end;   <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">/* 资源的结束物理地址 */
20
21       const char      *name;  /* 资源的名称 */
22
23       unsigned long   flags;   /* 资源的类型,比如MEM ,IO,IRQ类型 */
24
25       struct resource *parent, *sibling, *child; /* 资源链表指针 */
26
27   };
28
29   struct platform_driver {
30
31           int (*probe)(struct platform_device *);
32
33           int (*remove)(struct platform_device *);
34
35           void (*shutdown)(struct platform_device *);
36
37           int (*suspend)(struct platform_device *, pm_message_t state);
38
39           int (*suspend_late)(struct platform_device *, pm_message_t state);
40
41           int (*resume_early)(struct platform_device *);
42
43           int (*resume)(struct platform_device *);
44
45           struct device_driver driver;
46
47   };  

  这就是最基础的三个结构体,把这三个结构体里面的部分或全部成员填满,一个platform设备驱动就基本完成了。

  和字符驱动一样,看流程吧:

  1,先定义,再注册device,一般的我们可以放在板文件里。

  在dev-rtc.c文件中:

 1 static struct resource s3c_rtc_resource[] = {
 2     [0] = {
 3         .start    = S3C_PA_RTC,
 4         .end    = S3C_PA_RTC + 0xff,
 5         .flags    = IORESOURCE_MEM,
 6     },
 7     [1] = {
 8         .start    = IRQ_RTC_ALARM,
 9         .end    = IRQ_RTC_ALARM,
10         .flags    = IORESOURCE_IRQ,
11     },
12     [2] = {
13         .start    = IRQ_RTC_TIC,
14         .end    = IRQ_RTC_TIC,
15         .flags    = IORESOURCE_IRQ
16     }
17 };
18
19 struct platform_device s3c_device_rtc = {
20     .name        = "s3c64xx-rtc",
21     .id        = -1,
22     .num_resources    = ARRAY_SIZE(s3c_rtc_resource),
23     .resource    = s3c_rtc_resource,
24 };
25 EXPORT_SYMBOL(s3c_device_rtc);

  在这里我们看到一个设备struct platform_device s3c_device_rtc,以及它用到的resource。刚我说了,一般device是在板级文件中定义和注册,但是这个是作为系统的rtc,它就有了它专门定义的文件,但是它在这里导出(EXPORT_SYMBOL(s3c_device_rtc)),注册在哪呢?这回真的在板文件了

 1 /* Add platform device */
 2 static struct platform_device *xc2440_devices[] __initdata = {
 3     &s3c_device_nand,
 4     &s3c_device_lcd,
 5     &s3c_device_adc,
 6     &s3c_device_hwmon,
 7     &s3c_device_ts,
 8     &xc2440_ethernet_device,
 9     &s3c_device_ohci,
10     &s3c_device_usbgadget,
11     &s3c_device_timer[0],
12     &s3c_device_timer[1],
13     &xc2440_backlight_device,
14     &xc2440_beeper_device,
15     &xc2440_device_led,
16     &xc2440_button_device,
17     &s3c_device_i2c0,
18     &s3c_device_rtc,
19     &s3c_device_wdt,
20     &s3c_device_sdi,
21     &s3c_device_iis,
22     &xc2440_audio_device,
23     &xc2440_ds18b20_device,
24     &xc2440_irm_device,
25
26 /*    &s3c_device_spi0,
27     &s3c_device_spi1,
28     &s3c_device_camif,
29     &xc2440_spi_gpio, */
30
31 };

  我们看到rtc的device在一个platform_device的数列里面,这样便于我们理解和移植。还是没注册啊~怎么那么啰嗦

 1 static void __init xc2440_machine_init(void)
 2 {
 3     s3c24xx_fb_set_platdata(&xc2440_fb_info);
 4     s3c_i2c0_set_platdata(NULL);
 5     s3c_nand_set_platdata(&xc2440_nand_info);
 6     s3c24xx_mci_set_platdata(&xc2440_mmc_cfg);
 7     s3c_hwmon_set_platdata(&xc2440_hwmon_info);
 8     s3c24xx_ts_set_platdata(&xc2440_ts_cfg);
 9     i2c_register_board_info(0, xc2440_i2c_devs,ARRAY_SIZE(xc2440_i2c_devs));
10     s3c24xx_udc_set_platdata(&xc2440_udc_cfg);
11
12     spi_register_board_info(xc2440_spi_board_info, ARRAY_SIZE(xc2440_spi_board_info));
13     platform_add_devices(xc2440_devices, ARRAY_SIZE(xc2440_devices));
14 }

终于注册进去了、、、、、、、、

下面是应用中的一个设备定义,注册已经在上面了:

 1 /* IRM3638 */
 2 static struct platform_xc2440_irm_data xc2440_irm_pins = {
 3     .ctrl_pin_irq = IRQ_EINT(11),
 4     .irq_type     = IRQF_TRIGGER_FALLING,
 5 };
 6
 7
 8 static struct platform_device xc2440_irm_device = {
 9     .name        = "xc2440-irm",
10     .id        = -1,
11     .dev        = {
12         .platform_data    = &xc2440_irm_pins,
13     },
14 };

  这个platform_device就没有resources,因为它不使用。但是它多了一个自己定义的结构体platform_xc2440_irm_data,作为与驱动交换数据。

2,platform_driver的编写。

  实现跟字符驱动没什么区别,都需要完成file_operations这个结构体的成员函数。但是字符驱动的注册和卸载,放到了platform_driver结构体的probe和remove函数里。而驱动本身的注册和卸载就用到了platform_driver_register和platform_driver_unregister。

看一个刚才s3c的rtc程序,关于rtc相关的函数先略掉,主要看架构:

  1 /* drivers/rtc/rtc-s3c.c
  2  *
  3  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
  4  *        http://www.samsung.com/
  5  *
  6  * Copyright (c) 2004,2006 Simtec Electronics
  7  *    Ben Dooks, <[email protected]>
  8  *    http://armlinux.simtec.co.uk/
  9  *
 10  * This program is free software; you can redistribute it and/or modify
 11  * it under the terms of the GNU General Public License version 2 as
 12  * published by the Free Software Foundation.
 13  *
 14  * S3C2410/S3C2440/S3C24XX Internal RTC Driver
 15 */
 16
 17 #include <linux/module.h>
 18 #include <linux/fs.h>
 19 #include <linux/string.h>
 20 #include <linux/init.h>
 21 #include <linux/platform_device.h>
 22 #include <linux/interrupt.h>
 23 #include <linux/rtc.h>
 24 #include <linux/bcd.h>
 25 #include <linux/clk.h>
 26 #include <linux/log2.h>
 27 #include <linux/slab.h>
 28
 29 #include <mach/hardware.h>
 30 #include <asm/uaccess.h>
 31 #include <asm/io.h>
 32 #include <asm/irq.h>
 33 #include <plat/regs-rtc.h>
 34
 35 enum s3c_cpu_type {
 36     TYPE_S3C2410,
 37     TYPE_S3C64XX,
 38 };
 39
 40 /* I have yet to find an S3C implementation with more than one
 41  * of these rtc blocks in */
 42
 43 static struct resource *s3c_rtc_mem;
 44
 45 static struct clk *rtc_clk;
 46 static void __iomem *s3c_rtc_base;
 47 static int s3c_rtc_alarmno = NO_IRQ;
 48 static int s3c_rtc_tickno  = NO_IRQ;
 49 static enum s3c_cpu_type s3c_rtc_cpu_type;
 50
 51 static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
 52
 53 /* IRQ Handlers */
 54
 55 static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
 56 {
 57     struct rtc_device *rdev = id;
 58
 59     rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
 60
 61     if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 62         writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
 63
 64     return IRQ_HANDLED;
 65 }
 66
 67 static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
 68 {
 69     struct rtc_device *rdev = id;
 70
 71     rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
 72
 73     if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 74         writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
 75
 76     return IRQ_HANDLED;
 77 }
 78
 79 /* Update control registers */
 80 static void s3c_rtc_setaie(int to)
 81 {
 82     unsigned int tmp;
 83
 84     pr_debug("%s: aie=%d\n", __func__, to);
 85
 86     tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
 87
 88     if (to)
 89         tmp |= S3C2410_RTCALM_ALMEN;
 90
 91     writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
 92 }
 93
 94 static int s3c_rtc_setpie(struct device *dev, int enabled)
 95 {
 96     unsigned int tmp;
 97
 98     pr_debug("%s: pie=%d\n", __func__, enabled);
 99
100     spin_lock_irq(&s3c_rtc_pie_lock);
101
102     if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
103         tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
104         tmp &= ~S3C64XX_RTCCON_TICEN;
105
106         if (enabled)
107             tmp |= S3C64XX_RTCCON_TICEN;
108
109         writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
110     } else {
111         tmp = readb(s3c_rtc_base + S3C2410_TICNT);
112         tmp &= ~S3C2410_TICNT_ENABLE;
113
114         if (enabled)
115             tmp |= S3C2410_TICNT_ENABLE;
116
117         writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
118     }
119
120     spin_unlock_irq(&s3c_rtc_pie_lock);
121
122     return 0;
123 }
124
125 static int s3c_rtc_setfreq(struct device *dev, int freq)
126 {
127     struct platform_device *pdev = to_platform_device(dev);
128     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
129     unsigned int tmp = 0;
130
131     if (!is_power_of_2(freq))
132         return -EINVAL;
133
134     spin_lock_irq(&s3c_rtc_pie_lock);
135
136     if (s3c_rtc_cpu_type == TYPE_S3C2410) {
137         tmp = readb(s3c_rtc_base + S3C2410_TICNT);
138         tmp &= S3C2410_TICNT_ENABLE;
139     }
140
141     tmp |= (rtc_dev->max_user_freq / freq)-1;
142
143     writel(tmp, s3c_rtc_base + S3C2410_TICNT);
144     spin_unlock_irq(&s3c_rtc_pie_lock);
145
146     return 0;
147 }
148
149 /* Time read/write */
150
151 static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
152 {
153     unsigned int have_retried = 0;
154     void __iomem *base = s3c_rtc_base;
155
156  retry_get_time:
157     rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);
158     rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
159     rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
160     rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);
161     rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
162     rtc_tm->tm_sec  = readb(base + S3C2410_RTCSEC);
163
164     /* the only way to work out wether the system was mid-update
165      * when we read it is to check the second counter, and if it
166      * is zero, then we re-try the entire read
167      */
168
169     if (rtc_tm->tm_sec == 0 && !have_retried) {
170         have_retried = 1;
171         goto retry_get_time;
172     }
173
174     pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n",
175          1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
176          rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
177
178     rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
179     rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
180     rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
181     rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
182     rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
183     rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
184
185     rtc_tm->tm_year += 100;
186     rtc_tm->tm_mon -= 1;
187
188     return rtc_valid_tm(rtc_tm);
189 }
190
191 static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
192 {
193     void __iomem *base = s3c_rtc_base;
194     int year = tm->tm_year - 100;
195
196     pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
197          1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
198          tm->tm_hour, tm->tm_min, tm->tm_sec);
199
200     /* we get around y2k by simply not supporting it */
201
202     if (year < 0 || year >= 100) {
203         dev_err(dev, "rtc only supports 100 years\n");
204         return -EINVAL;
205     }
206
207     writeb(bin2bcd(tm->tm_sec),  base + S3C2410_RTCSEC);
208     writeb(bin2bcd(tm->tm_min),  base + S3C2410_RTCMIN);
209     writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
210     writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
211     writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
212     writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
213
214     return 0;
215 }
216
217 static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
218 {
219     struct rtc_time *alm_tm = &alrm->time;
220     void __iomem *base = s3c_rtc_base;
221     unsigned int alm_en;
222
223     alm_tm->tm_sec  = readb(base + S3C2410_ALMSEC);
224     alm_tm->tm_min  = readb(base + S3C2410_ALMMIN);
225     alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
226     alm_tm->tm_mon  = readb(base + S3C2410_ALMMON);
227     alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);
228     alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
229
230     alm_en = readb(base + S3C2410_RTCALM);
231
232     alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
233
234     pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
235          alm_en,
236          1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
237          alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
238
239
240     /* decode the alarm enable field */
241
242     if (alm_en & S3C2410_RTCALM_SECEN)
243         alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
244     else
245         alm_tm->tm_sec = -1;
246
247     if (alm_en & S3C2410_RTCALM_MINEN)
248         alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
249     else
250         alm_tm->tm_min = -1;
251
252     if (alm_en & S3C2410_RTCALM_HOUREN)
253         alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
254     else
255         alm_tm->tm_hour = -1;
256
257     if (alm_en & S3C2410_RTCALM_DAYEN)
258         alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
259     else
260         alm_tm->tm_mday = -1;
261
262     if (alm_en & S3C2410_RTCALM_MONEN) {
263         alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
264         alm_tm->tm_mon -= 1;
265     } else {
266         alm_tm->tm_mon = -1;
267     }
268
269     if (alm_en & S3C2410_RTCALM_YEAREN)
270         alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
271     else
272         alm_tm->tm_year = -1;
273
274     return 0;
275 }
276
277 static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
278 {
279     struct rtc_time *tm = &alrm->time;
280     void __iomem *base = s3c_rtc_base;
281     unsigned int alrm_en;
282
283     pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
284          alrm->enabled,
285          1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
286          tm->tm_hour, tm->tm_min, tm->tm_sec);
287
288
289     alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
290     writeb(0x00, base + S3C2410_RTCALM);
291
292     if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
293         alrm_en |= S3C2410_RTCALM_SECEN;
294         writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC);
295     }
296
297     if (tm->tm_min < 60 && tm->tm_min >= 0) {
298         alrm_en |= S3C2410_RTCALM_MINEN;
299         writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN);
300     }
301
302     if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
303         alrm_en |= S3C2410_RTCALM_HOUREN;
304         writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);
305     }
306
307     pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
308
309     writeb(alrm_en, base + S3C2410_RTCALM);
310
311     s3c_rtc_setaie(alrm->enabled);
312
313     return 0;
314 }
315
316 static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
317 {
318     unsigned int ticnt;
319
320     if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
321         ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
322         ticnt &= S3C64XX_RTCCON_TICEN;
323     } else {
324         ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
325         ticnt &= S3C2410_TICNT_ENABLE;
326     }
327
328     seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
329     return 0;
330 }
331
332 static int s3c_rtc_open(struct device *dev)
333 {
334     struct platform_device *pdev = to_platform_device(dev);
335     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
336     int ret;
337
338     ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
339               IRQF_DISABLED,  "s3c2410-rtc alarm", rtc_dev);
340
341     if (ret) {
342         dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
343         return ret;
344     }
345
346     ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
347               IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);
348
349     if (ret) {
350         dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
351         goto tick_err;
352     }
353
354     return ret;
355
356  tick_err:
357     free_irq(s3c_rtc_alarmno, rtc_dev);
358     return ret;
359 }
360
361 static void s3c_rtc_release(struct device *dev)
362 {
363     struct platform_device *pdev = to_platform_device(dev);
364     struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
365
366     /* do not clear AIE here, it may be needed for wake */
367
368     s3c_rtc_setpie(dev, 0);
369     free_irq(s3c_rtc_alarmno, rtc_dev);
370     free_irq(s3c_rtc_tickno, rtc_dev);
371 }
372
373 static const struct rtc_class_ops s3c_rtcops = {
374     .open        = s3c_rtc_open,
375     .release    = s3c_rtc_release,
376     .read_time    = s3c_rtc_gettime,
377     .set_time    = s3c_rtc_settime,
378     .read_alarm    = s3c_rtc_getalarm,
379     .set_alarm    = s3c_rtc_setalarm,
380     .irq_set_freq    = s3c_rtc_setfreq,
381     .irq_set_state    = s3c_rtc_setpie,
382     .proc        = s3c_rtc_proc,
383     .alarm_irq_enable = s3c_rtc_setaie,
384 };
385
386 static void s3c_rtc_enable(struct platform_device *pdev, int en)
387 {
388     void __iomem *base = s3c_rtc_base;
389     unsigned int tmp;
390
391     if (s3c_rtc_base == NULL)
392         return;
393
394     if (!en) {
395         tmp = readw(base + S3C2410_RTCCON);
396         if (s3c_rtc_cpu_type == TYPE_S3C64XX)
397             tmp &= ~S3C64XX_RTCCON_TICEN;
398         tmp &= ~S3C2410_RTCCON_RTCEN;
399         writew(tmp, base + S3C2410_RTCCON);
400
401         if (s3c_rtc_cpu_type == TYPE_S3C2410) {
402             tmp = readb(base + S3C2410_TICNT);
403             tmp &= ~S3C2410_TICNT_ENABLE;
404             writeb(tmp, base + S3C2410_TICNT);
405         }
406     } else {
407         /* re-enable the device, and check it is ok */
408
409         if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) {
410             dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
411
412             tmp = readw(base + S3C2410_RTCCON);
413             writew(tmp | S3C2410_RTCCON_RTCEN,
414                 base + S3C2410_RTCCON);
415         }
416
417         if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
418             dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
419
420             tmp = readw(base + S3C2410_RTCCON);
421             writew(tmp & ~S3C2410_RTCCON_CNTSEL,
422                 base + S3C2410_RTCCON);
423         }
424
425         if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
426             dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
427
428             tmp = readw(base + S3C2410_RTCCON);
429             writew(tmp & ~S3C2410_RTCCON_CLKRST,
430                 base + S3C2410_RTCCON);
431         }
432     }
433 }
434
435 static int __devexit s3c_rtc_remove(struct platform_device *dev)
436 {
437     struct rtc_device *rtc = platform_get_drvdata(dev);
438
439     platform_set_drvdata(dev, NULL);
440     rtc_device_unregister(rtc);
441
442     s3c_rtc_setpie(&dev->dev, 0);
443     s3c_rtc_setaie(0);
444
445     clk_disable(rtc_clk);
446     clk_put(rtc_clk);
447     rtc_clk = NULL;
448
449     iounmap(s3c_rtc_base);
450     release_resource(s3c_rtc_mem);
451     kfree(s3c_rtc_mem);
452
453     return 0;
454 }
455
456 static int __devinit s3c_rtc_probe(struct platform_device *pdev)
457 {
458     struct rtc_device *rtc;
459     struct rtc_time rtc_tm;
460     struct resource *res;
461     int ret;
462
463     pr_debug("%s: probe=%p\n", __func__, pdev);
464
465     /* find the IRQs */
466
467     s3c_rtc_tickno = platform_get_irq(pdev, 1);
468     if (s3c_rtc_tickno < 0) {
469         dev_err(&pdev->dev, "no irq for rtc tick\n");
470         return -ENOENT;
471     }
472
473     s3c_rtc_alarmno = platform_get_irq(pdev, 0);
474     if (s3c_rtc_alarmno < 0) {
475         dev_err(&pdev->dev, "no irq for alarm\n");
476         return -ENOENT;
477     }
478
479     pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
480          s3c_rtc_tickno, s3c_rtc_alarmno);
481
482     /* get the memory region */
483
484     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
485     if (res == NULL) {
486         dev_err(&pdev->dev, "failed to get memory region resource\n");
487         return -ENOENT;
488     }
489
490     s3c_rtc_mem = request_mem_region(res->start,
491                      res->end-res->start+1,
492                      pdev->name);
493
494     if (s3c_rtc_mem == NULL) {
495         dev_err(&pdev->dev, "failed to reserve memory region\n");
496         ret = -ENOENT;
497         goto err_nores;
498     }
499
500     s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
501     if (s3c_rtc_base == NULL) {
502         dev_err(&pdev->dev, "failed ioremap()\n");
503         ret = -EINVAL;
504         goto err_nomap;
505     }
506
507     rtc_clk = clk_get(&pdev->dev, "rtc");
508     if (IS_ERR(rtc_clk)) {
509         dev_err(&pdev->dev, "failed to find rtc clock source\n");
510         ret = PTR_ERR(rtc_clk);
511         rtc_clk = NULL;
512         goto err_clk;
513     }
514
515     clk_enable(rtc_clk);
516
517     /* check to see if everything is setup correctly */
518
519     s3c_rtc_enable(pdev, 1);
520
521     pr_debug("s3c2410_rtc: RTCCON=%02x\n",
522          readw(s3c_rtc_base + S3C2410_RTCCON));
523
524     device_init_wakeup(&pdev->dev, 1);
525
526     /* register RTC and exit */
527
528     rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
529                   THIS_MODULE);
530
531     if (IS_ERR(rtc)) {
532         dev_err(&pdev->dev, "cannot attach rtc\n");
533         ret = PTR_ERR(rtc);
534         goto err_nortc;
535     }
536
537     s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
538
539     /* Check RTC Time */
540
541     s3c_rtc_gettime(NULL, &rtc_tm);
542
543     if (rtc_valid_tm(&rtc_tm)) {
544         rtc_tm.tm_year    = 100;
545         rtc_tm.tm_mon    = 0;
546         rtc_tm.tm_mday    = 1;
547         rtc_tm.tm_hour    = 0;
548         rtc_tm.tm_min    = 0;
549         rtc_tm.tm_sec    = 0;
550
551         s3c_rtc_settime(NULL, &rtc_tm);
552
553         dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
554     }
555
556     if (s3c_rtc_cpu_type == TYPE_S3C64XX)
557         rtc->max_user_freq = 32768;
558     else
559         rtc->max_user_freq = 128;
560
561     platform_set_drvdata(pdev, rtc);
562
563     s3c_rtc_setfreq(&pdev->dev, 1);
564
565     return 0;
566
567  err_nortc:
568     s3c_rtc_enable(pdev, 0);
569     clk_disable(rtc_clk);
570     clk_put(rtc_clk);
571
572  err_clk:
573     iounmap(s3c_rtc_base);
574
575  err_nomap:
576     release_resource(s3c_rtc_mem);
577
578  err_nores:
579     return ret;
580 }
581
582 #ifdef CONFIG_PM
583
584 /* RTC Power management control */
585
586 static int ticnt_save, ticnt_en_save;
587
588 static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
589 {
590     /* save TICNT for anyone using periodic interrupts */
591     ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
592     if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
593         ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON);
594         ticnt_en_save &= S3C64XX_RTCCON_TICEN;
595     }
596     s3c_rtc_enable(pdev, 0);
597
598     if (device_may_wakeup(&pdev->dev))
599         enable_irq_wake(s3c_rtc_alarmno);
600
601     return 0;
602 }
603
604 static int s3c_rtc_resume(struct platform_device *pdev)
605 {
606     unsigned int tmp;
607
608     s3c_rtc_enable(pdev, 1);
609     writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
610     if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
611         tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
612         writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);
613     }
614
615     if (device_may_wakeup(&pdev->dev))
616         disable_irq_wake(s3c_rtc_alarmno);
617
618     return 0;
619 }
620 #else
621 #define s3c_rtc_suspend NULL
622 #define s3c_rtc_resume  NULL
623 #endif
624
625 static struct platform_device_id s3c_rtc_driver_ids[] = {
626     {
627         .name        = "s3c2410-rtc",
628         .driver_data    = TYPE_S3C2410,
629     }, {
630         .name        = "s3c64xx-rtc",
631         .driver_data    = TYPE_S3C64XX,
632     },
633     { }
634 };
635
636 MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);
637
638 static struct platform_driver s3c_rtc_driver = {
639     .probe        = s3c_rtc_probe,
640     .remove        = __devexit_p(s3c_rtc_remove),
641     .suspend    = s3c_rtc_suspend,
642     .resume        = s3c_rtc_resume,
643     .id_table    = s3c_rtc_driver_ids,
644     .driver        = {
645         .name    = "s3c-rtc",
646         .owner    = THIS_MODULE,
647     },
648 };
649
650 static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";
651
652 static int __init s3c_rtc_init(void)
653 {
654     printk(banner);
655     return platform_driver_register(&s3c_rtc_driver);
656 }
657
658 static void __exit s3c_rtc_exit(void)
659 {
660     platform_driver_unregister(&s3c_rtc_driver);
661 }
662
663 module_init(s3c_rtc_init);
664 module_exit(s3c_rtc_exit);
665
666 MODULE_DESCRIPTION("Samsung S3C RTC Driver");
667 MODULE_AUTHOR("Ben Dooks <[email protected]>");
668 MODULE_LICENSE("GPL");
669 MODULE_ALIAS("platform:s3c2410-rtc");

里面的rtc_class_ops(就是字符驱动里file_operations的作用),完成了硬件的操作。如下:

static const struct rtc_class_ops s3c_rtcops = {
 .open  = s3c_rtc_open,
 .release = s3c_rtc_release,
 .read_time = s3c_rtc_gettime,
 .set_time = s3c_rtc_settime,
 .read_alarm = s3c_rtc_getalarm,
 .set_alarm = s3c_rtc_setalarm,
 .irq_set_freq = s3c_rtc_setfreq,
 .irq_set_state = s3c_rtc_setpie,
 .proc  = s3c_rtc_proc,
 .alarm_irq_enable = s3c_rtc_setaie,
};

  再看probe函数,先从device里面获取资源,irq和mem,里面的函数以后慢慢再看啊!然后再使能rct,注册rtc_class_ops。后面是一些小操作。

  这里的suspend和resume函数是电源管理的时候使用,这里先忽略。

  remove函数,前面说了,就是字符设备里面的卸载,看看里面都有些什么吧。

platform_set_drvdata(dev, NULL);  //将设备数据置0;
 rtc_device_unregister(rtc);    //注销rtc设备

s3c_rtc_setpie(&dev->dev, 0);   //停止硬件中断
 s3c_rtc_setaie(0);

clk_disable(rtc_clk);    //rtc的一些注销
 clk_put(rtc_clk);
 rtc_clk = NULL;

iounmap(s3c_rtc_base);  //释放内存空间
 release_resource(s3c_rtc_mem);
 kfree(s3c_rtc_mem);

注册和卸载的程序很简单。

static int __init s3c_rtc_init(void)
{
 printk(banner);
 return platform_driver_register(&s3c_rtc_driver);
}

static void __exit s3c_rtc_exit(void)
{
 platform_driver_unregister(&s3c_rtc_driver);
}

  

至此,platform设备驱动粗略的讲述完毕。再次强调:它这样封装的好处,在设备和驱动分离,方便移植。当然,在同个平台,这样做很方便添加或删除外设驱动。 只需在板文件添加或注释掉就ok了。

ps:ioremap内核中的使用,往往是为某个设备预留一块内存,当使用的时候需要在board中定义这个设备的内存resource。通过 platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。

时间: 2024-10-15 21:49:42

PLATFORM设备驱动的相关文章

【linux设备模型】之platform设备驱动

一.platform总线.设备和驱动 platform是一种虚拟总线,对应的设备称为platform_device,对应的驱动称为platform_driver. platform_device定义在<linux/platform_device.h>中: 1 struct platform_device { 2 const char * name; 3 int id; 4 struct device dev; 5 u32 num_resources; 6 struct resource * r

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

platform设备驱动框架搭建分析

之前接触到的字符设备驱动是非常单纯的Linux字符设备驱动,他不具备工程中Linux驱动中的设备与驱动分离思想和设备驱动的分层思想,不具备"总线-设备-驱动"模型的概念.接下来通过分析platform设备驱动模型的搭建过程来看看Linux的设备驱动模型究竟是怎样的? platform驱动模型搭建: (1)platform核心层:为设备层和驱动层提供注册接口.为设备层和驱动层的匹配提供标准 ①搭建总线框架: 总线类结构体: struct bus_type { const char *na

linux platform设备驱动之match自动匹配

<span style="font-size:14px;">struct platform_device { // linux/platform_device.h const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; struct platform_device_id *id_entry; /* arch specific additions

Linux Platform设备驱动学习与小结

Platform 设备先被注册然后platfrom驱动加载时会调用驱动程序中的probe()入口函数,扫描系统中已注册的设备,通过.Name域找到匹配设备后将驱动和设备绑定.一个驱动可以对应多个设备,但是一个设备只对一个驱动.Linux下的虚拟总线platform对应设备platform_device,对应的驱动为platform_driver.一个很不恰当的例子:设备好比男人,驱动好比女人,platform作为媒人,将两个对上眼的(name域相同)的相匹配到一起.然后男人(device)到她(

platform设备驱动全透析

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://21cnbao.blog.51cto.com/109393/337609 1.1 platform总线.设备与驱动 在Linux 2.6的设备驱动模型中,关心总线.设备和驱动这3个实体,总线将设备和驱动绑定.在系统每注册一个设备的时候,会寻找与之匹配的驱动:相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成. 一个现实的Linux设备和驱动通常都需要挂接在

linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这一节可以理解是第八章的延伸,从这节开始介绍platform设备驱动. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

fl2440 platform总线led字符设备驱动

首先需要知道的是,设备跟驱动是分开的.设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中: 例如:struct platform_device: 1 在include/linux/platform_device.h文件中: //这个位置还是个盲点,我一直没找到这个位置在哪里 2 struct platform_device { 3 const char * name 4 u32 id 5 struct device dev 6 u32 num_res

Linux设备驱动开发 - 平台设备驱动

Linux2.6的内核中引入了一种新的设备驱动模型-平台(platform)设备驱动,平台设备驱动分为平台设备(platform_device)和平台驱动(platform_driver),平台设备的引入使得Linux设备驱动更加便于移植. 一.平台设备平台设备结构体: 1 struct platform_device { 2 const char * name; /* 设备名 */ 3 int id; 4 struct device dev; /* 设备结构体 */ 5 u32 num_res