码迷,mamicode.com
首页 > 其他好文 > 详细

PLATFORM设备驱动

时间:2015-03-19 23:22:06      阅读:222      评论:0      收藏:0      [点我收藏+]

标签:

  字符设备,杂项设备虽然简单,但是在工程中,比如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, <ben@simtec.co.uk>
  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 <ben@simtec.co.uk>");
668 MODULE_LICENSE("GPL");
669 MODULE_ALIAS("platform:s3c2410-rtc");
View Code

里面的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等操作,以便应用程序对其进行操作。

 

PLATFORM设备驱动

标签:

原文地址:http://www.cnblogs.com/cyc2009/p/4351142.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!