zoukankan      html  css  js  c++  java
  • 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, <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
    ", __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
    ", __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
    ",
    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
    ",
    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
    ");
    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
    ",
    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
    ",
    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
    ", 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	: %s
    ", 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
    ", 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
    ", 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
    ");
    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
    ");
    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
    ");
    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
    ", __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
    ");
    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
    ");
    476         return -ENOENT;
    477     }
    478 
    479     pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d
    ",
    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
    ");
    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
    ");
    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()
    ");
    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
    ");
    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
    ",
    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
    ");
    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
    ");
    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
    ";
    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等操作,以便应用程序对其进行操作。

  • 相关阅读:
    top 命令
    通过公网访问内网的几种方式总结
    借助IDE到处Runnable JAR 的步骤
    Java SE 基础知识(String,Array)
    Java SE 基础知识(二)
    周记 2014.12.13
    nginx 总结
    在VPS上配置SS
    Markdown 语法手册
    ASCII码表
  • 原文地址:https://www.cnblogs.com/cyc2009/p/4351142.html
Copyright © 2011-2022 走看看