字符设备,杂项设备虽然简单,但是在工程中,比如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等操作,以便应用程序对其进行操作。