Chinaunix首页 | 论坛 | 博客
  • 博客访问: 529220
  • 博文数量: 81
  • 博客积分: 1438
  • 博客等级: 上尉
  • 技术积分: 866
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-12 11:32
文章分类

全部博文(81)

文章存档

2014年(1)

2013年(1)

2012年(33)

2011年(46)

分类: 嵌入式

2012-01-03 20:57:43

  1. /*********************************************************************************************************
  2. * @Description:s3c2410的rtc驱动的实现,rtc(real time clock)实时时钟的驱动是个很好的
  3. * 理解如果编写驱动的硬件,它包括了最基本的硬中断,软中断的底层机制;
  4. * s3c2410的RTC驱动的实现个人认为更是对linux设备驱动一个很好的例子,他是通过二层结构来
  5. * 实现的一个驱动,上层是一个arm common的公共层,对上提供标准的通用的RTC操作接口,下层由
  6. * 我们来实现针对自己的chip和自己要提供的功能来实现的一层驱动;
  7. *
  8. * @FileTree:
  9. **********************************************************************************************************
  10. linux-2.6.14.6
  11. |
  12. |--arch
  13. | |
  14. | |--arm
  15. | | |--mach-s3c2410
  16. | | | |-devs.c //包含了对各个部件的resource的分配和定义,在这看rtc的资源;
  17. | | |--common
  18. | | | |-rtctime.c //一个arm平台的通用rtc函数层,它对上隐藏了各种soc的rtc driver的区别;
  19. | | |--kernel
  20. | | | |-time.c //内核的初始化例程time_init()会调用的xxx_cmos_xxx函数的实现;定义了全局自旋锁rtc_lock用来串行化所有CPU对RTC的操作
  21. |
  22. |--drivers
  23. | |--char
  24. | | |-s3c2410-rtc.c //具体的s3c2410上的rtc chip的驱动实现,如果需要在arm平台的
  25. | | 板子上实现一个驱动,改写它就ok了。
  26. |
  27. |--include
  28. | |--asm-arm
  29. | | |--arch-s3c2410
  30. | | | |-regs-rtc.h //S3C2410 Internal RTC register definition refer to datasheet;
  31. | | |-rtc.h //arm平台rtc操作抽象层rtctime.c对应的.h
  32. | |--linux
  33. | | |-time.h //mktime的实现;
  34. | | |-rtc.h //公用RTC .h
  35. *
  36. *[小结]
  37. *1)提供给user的接口,在arch/arm/common/rtctime.c,include/asm-arm/rtc.h中实现,调用操作硬件驱动在drivers/char/s3c2410-rtc.c,include/asm-arm/arch-s3c2410/regs-rtc.h实现;
  38. *2)提供给kernel的接口,在arch/arm/kernel/time.c,include/linux/time.h中实现,调用操作硬件驱动在drivers/char/s3c2410-rtc.c,include/asm-arm/arch-s3c2410/regs-rtc.h实现;
  39. **********************************************************************************************************
  40. *
  41. * @Author: liyangth@gmail.com
  42. *
  43. * @Function List: <Functions>
  44. *
  45. * <Static functions>
  46. *
  47. *
  48. * @Changelog:
  49. * 2007-06-24 LiYang First version
  50. *
  51. * @FQA:
  52. * [50%]Q1.在驱动中要将设备注册到总线,必须将设备封装成struct device_driver;调查这个结构体中的每个成员.
  53. * [0%]Q2.在板子(什么类型)上什么样的设备要用总线(什么类型)注册?
  54. * [90%]Q3.
  55. * struct device --总线设备
  56. * struct device_driver --总线设备驱动
  57. * struct platform_device --平台设备
  58. * struct resource --平台资源
  59. *
  60. * [!0%]Q4.初始化rtc register的函数的后面的flag具体控制什么? (在s2s65a里是否可以用它控制是softReset or hardwareReset)
  61. *
  62. * [0%]Q5.什么时候调用suspend, resume?
  63. **********************************************************************************************************/

  64. /**//*****************************************************************************
  65. * Structures & Unions & Enums (#typedef)
  66. */
  67. /**//*[include/linux/device.h]
  68. *总线设备驱动结构体,将它注册到板子的总线上
  69. */
  70. static struct device_driver s3c2410_rtcdrv = ...{
  71.     .name = "s3c2410-rtc",
  72.     .owner = THIS_MODULE,
  73.     .bus = &platform_bus_type, //总线类型,貌似不用管
  74.     .probe = s3c2410_rtc_probe, //自检->初始化REG->注册到上一层
  75.     .remove = s3c2410_rtc_remove, //注销
  76.     .suspend = s3c2410_rtc_suspend, //[挂起???]
  77.     .resume = s3c2410_rtc_resume, //[重起???]
  78. };

  79. /**//*[/include/asm-arm/rtc.h]
  80. *底层特别操作集,将他注册到上层的arm common操作层
  81. */
  82. static struct rtc_ops s3c2410_rtcops = ...{
  83.     .owner = THIS_MODULE,
  84.     .open = s3c2410_rtc_open,
  85.     .release = s3c2410_rtc_release,
  86.     .ioctl = s3c2410_rtc_ioctl,
  87.     .read_time = s3c2410_rtc_gettime,
  88.     .set_time = s3c2410_rtc_settime,
  89.     .read_alarm = s3c2410_rtc_getalarm,
  90.     .set_alarm = s3c2410_rtc_setalarm,
  91.     .proc = s3c2410_rtc_proc,
  92. };

  93. /**//*****************************************************************************
  94. * Global Variables
  95. */



  96. s3c2410-rtc.c
  97. |
  98. |/**//* IRQ Handlers */
  99. |-s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r)
  100. | |
  101. | |-rtc_update(1, RTC_AF | RTC_IRQF); //获得中断标志,和唤醒read阻塞,异步通知;
  102. |
  103. |-s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r)
  104. |
  105. |/**//* Update control registers,与硬件实现有关,refer to datasheet */
  106. |-s3c2410_rtc_setaie(int to)
  107. |-s3c2410_rtc_setpie(int to)
  108. |-s3c2410_rtc_setfreq(int freq)
  109. |
  110. |/**//* 实现了要插到上层arm common层的具体的硬件操作,来填充struct rtc_ops,这个具体与硬件相关的操作集会用
  111. | register_rtc注册到上层的 */
  112. |
  113. |-s3c2410_rtc_gettime(struct rtc_time *rtc_tm)
  114. |
  115. |-s3c2410_rtc_settime(struct rtc_time *tm)
  116. |
  117. |-s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm)
  118. |
  119. |-s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm)
  120. |
  121. |/**//*
  122. | * 插入到上层ioctl中的ioctl,上层中已经通过这个driver中的gettime,settime在其ioctl中实现了取得和设置时间
  123. | *,和一些共同的ioctl操作了
  124. | * 所以我们在这只要实现与硬件不同部分的ioctl操作 */
  125. |-s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg)
  126. |
  127. |-s3c2410_rtc_proc(char *buf)
  128. |-s3c2410_rtc_open(void)
  129. | |
  130. | |1.注册申请闹钟中断ISQ --s3c2410_rtc_alarmirq
  131. | |2.周期中断ISQ --s3c2410_rtc_tickirq
  132. |
  133. |-s3c2410_rtc_release(void)
  134. |
  135. |/**//* Initialize RTC Regs */
  136. |-s3c2410_rtc_enable(struct device *dev, int en)
  137. | |
  138. | |-if(!en)
  139. | |/**//*before power off, the RTCEN bit should be cleared to
  140. | | 0 to prevent inadvertent writing into RTC registers.*/
  141. | |1.将控制R的RTCEN位清0;
  142. | |
  143. | |2.disable interrupt.
  144. | |
  145. | |-else /**//* re-enable the device, and check it is ok */
  146. | |
  147. | |1.将控制R的RTCEN位致1。
  148. | |
  149. | |2.BCD count select.--0=Merge BCD counters
  150. | |
  151. | |3.RTC clock count reset.--0=noreset
  152. | |
  153. |
  154. |
  155. |
  156. |-s3c2410_rtc_probe(struct device *dev)
  157. | |[??]struct platform_device *pdev = to_platform_device(dev); //通过这个设备找到它宿主平台的大设备;
  158. | | struct resource *res;
  159. | |
  160. | |/**//* find the IRQs,RTC有2中中断,周期中断和闹钟中断 */
  161. | |-s3c2410_rtc_tickno = platform_get_irq(pdev, 1); //从平台上取得一个IRQ号给这个设备;
  162. | |
  163. | |/**//* get the memory region */
  164. | |-res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  165. | |
  166. | |/**//* 向内核申请资源空间 */
  167. | |-s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1,
  168.                      pdev->name); //res->start这些资源的分配和在哪个段下,可以看../mach-s3c2410/devs.c
  169. | |
  170. | |/**//* 然后将物理地址映射到虚拟地址,这样驱动和内核就可以看到设备的I/O regs了 */
  171. | |-s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1);
  172. | |
  173. | |/**//* 初始化设备regs */
  174. | |-s3c2410_rtc_enable(dev, 1); //可以用后面这个1(这个flag在s2s65a中可以用ram0-7来保存,掉电不清的)来控制是softreset还是hardwarereset.
  175. | |
  176. | |-s3c2410_rtc_setfreq(s3c2410_rtc_freq); //设定RTC周期频率;
  177. | |
  178. | |/**//* 最关键的一步,将与具体不同的底层硬件相关的设备驱动注册给arm通用操作层common/rtctime.c */
  179. | |-register_rtc(&s3c2410_rtcops);
  180. |
  181. |-s3c2410_rtc_remove(struct device *dev)
  182. | |-unregister_rtc(&s3c2410_rtcops); //从上一层将s3c2410的rtc的device dirver operation set拔下来。
  183. | |
  184. | |-s3c2410_rtc_setpie(0); //disable 周期中断
  185. | |
  186. | |-s3c2410_rtc_setaie(0); //disable alarm interrupt
  187. | |
  188. | |-iounmap(s3c2410_rtc_base);
  189. | |
  190. | |-release_resource(s3c2410_rtc_mem);
  191. | |
  192. | |-kfree(s3c2410_rtc_mem); //[MQA]哪块kmalloc了呢,为什么这要free?
  193. |
  194. |
  195. |#ifdef CONFIG_PM //如果电源控制开关打开
  196. |
  197. |-s3c2410_rtc_suspend(struct device *dev, pm_message_t state, u32 level)
  198. | |
  199. | |if (level == SUSPEND_POWER_DOWN) //SUSPEND_POWER_DOWN在include/linux/device.h(generic, centralized driver model)中定义,这个里面是否是对设备的一些公用的行为的操作宏的定义呢?
  200. | | |1.保存周期中断寄存器的值;
  201. | | |
  202. | | |2.从RTC中读出时间
  203. | | |-s3c2410_rtc_gettime(&tm); //[local variable]struct rtc_time tm;
  204. | | |
  205. | | |3.将从RTC取出的时间Convert Gregorian date to seconds since 01-01-1970 00:00:00.
  206. | | |-rtc_tm_to_time(&tm, &time.tv_sec); //[arch/arm/common/rtctime.c]
  207. | | |
  208. | | |4.将系统时间和RTC时间的差值保存到s3c2410_rtc_delta里;
  209. | | |-save_time_delta(&s3c2410_rtc_delta, &time);
  210. | | |
  211. | | |5.启动RTC,注意这次启动后面的falg为0了;
  212. | | |-s3c2410_rtc_enable(dev, 0);
  213. |
  214. |
  215. |-s3c2410_rtc_resume(struct device *dev, u32 level)
  216. | |
  217. | |1.启动RTC,注意这次启动后面的falg为1了;
  218. | |-s3c2410_rtc_enable(dev, 1);
  219. | |
  220. | |2.从RTC中读出时间
  221. | |-s3c2410_rtc_gettime(&tm);
  222. | |
  223. | |3.转换
  224. | |-rtc_tm_to_time(&tm, &time.tv_sec);
  225. | |
  226. | |4.利用在suspend中保存的delta来恢复系统时间
  227. | |-restore_time_delta(&s3c2410_rtc_delta, &time);
  228. | |
  229. | |5.恢复周期中断寄存器的值;
  230. |
  231. |[LiY]
  232. |suspend(暂停,挂起)和resume(恢复,再开始)有点象关机前保存现场和开机后再恢复现场一样;
  233. |
  234. |#else
  235. |#define s3c2410_rtc_suspend NULL
  236. |#define s3c2410_rtc_resume NULL
  237. |#endif
  238. |
  239. |
  240. |module_init(s3c2410_rtc_init)
  241. |-s3c2410_rtc_init
  242. | |-driver_register(&s3c2410_rtcdrv);
  243. | | |[purpose]register driver with bus;
  244. |
  245. |module_exit(s3c2410_rtc_exit)
  246. |-s3c2410_rtc_exit(void)
  247. | |-driver_unregister(&s3c2410_rtcdrv)

  248. /**//*****************************************************************************
  249. * Extern Function Details
  250. */
  251. /**//**[drivers/base/driver.c]
  252. * driver_register - register driver with bus
  253. * @drv: driver to register
  254. *
  255. * We pass off most of the work to the bus_add_driver() call,
  256. * since most of the things we have to do deal with the bus
  257. * structures.
  258. *
  259. * The one interesting aspect is that we setup @drv->unloaded
  260. * as a completion that gets complete when the driver reference
  261. * count reaches 0.
  262. */
  263. int driver_register(struct device_driver * drv)
  264. ...{
  265.     klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
  266.     init_completion(&drv->unloaded);
  267.     return bus_add_driver(drv);
  268. }

  269. /**//**[drivers/base/platform.c]
  270. * platform_get_irq - get an IRQ for a device
  271. * @dev: platform device
  272. * @num: IRQ number index
  273. */
  274. int platform_get_irq(struct platform_device *dev, unsigned int num)
  275. ...{
  276.     struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

  277.     return r ? r->start : 0;
  278. }

  279. /**//**[drivers/base/platform.c]
  280. * platform_get_resource - get a resource for a device
  281. * @dev: platform device
  282. * @type: resource type
  283. * @num: resource index
  284. */
  285. struct resource *
  286. platform_get_resource(struct platform_device *dev, unsigned int type,
  287.               unsigned int num)
  288. ...{
  289.     int i;

  290.     for (i = 0; i < dev->num_resources; i++) ...{
  291.         struct resource *r = &dev->resource[i];

  292.         if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
  293.                  IORESOURCE_IRQ|IORESOURCE_DMA))
  294.             == type)
  295.             if (num-- == 0)
  296.                 return r;
  297.     }
  298.     return NULL;
  299. }

  300. /**//*[../include/linux/ioport.h]
  301. * Resources are tree-like, allowing
  302. * nesting etc..
  303. */
  304. struct resource ...{
  305.     const char *name;
  306.     unsigned long start, end;
  307.     unsigned long flags;
  308.     struct resource *parent, *sibling, *child;
  309. };

  310. /**//**[arch/arm/kernel/time.c]
  311. * save_time_delta - Save the offset between system time and RTC time
  312. * @delta: pointer to timespec to store delta
  313. * @rtc: pointer to timespec for current RTC time
  314. *
  315. * Return a delta between the system time and the RTC time, such
  316. * that system time can be restored later with restore_time_delta()
  317. *
  318. *[LiY]返回一个系统时间和RTC时间的差值,在重起或系统去读RTC后,可以再用这个差值(delta)来恢复系统时间(系统时间不一定非要和RTC时间相同的);
  319. */
  320. void save_time_delta(struct timespec *delta, struct timespec *rtc)
  321. ...{
  322.     set_normalized_timespec(delta,
  323.                 xtime.tv_sec - rtc->tv_sec,
  324.                 xtime.tv_nsec - rtc->tv_nsec);
  325. }

  326. /**//***[arch/arm/kernel/time.c]
  327. * restore_time_delta - Restore the current system time
  328. * @delta: delta returned by save_time_delta()
  329. * @rtc: pointer to timespec for current RTC time
  330. */
  331. void restore_time_delta(struct timespec *delta, struct timespec *rtc)
  332. ...{
  333.     struct timespec ts;

  334.     set_normalized_timespec(&ts,
  335.                 delta->tv_sec + rtc->tv_sec,
  336.                 delta->tv_nsec + rtc->tv_nsec);

  337.     do_settimeofday(&ts);
  338. }

  339. /**//*
  340. *[arch/arm/common/rtctime.c]
  341. */
  342. void rtc_update(unsigned long num, unsigned long events)
  343. ...{
  344.     spin_lock(&rtc_lock);
  345.     rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
  346.     spin_unlock(&rtc_lock);

  347.     wake_up_interruptible(&rtc_wait);
  348.     kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
  349. }
 
 
阅读(1336) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~