找回密码
 注册
关于网站域名变更的通知
查看: 217|回复: 1
打印 上一主题 下一主题

基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-6-15 10:53 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
本文将介绍看门狗驱动的实现。' j, U- K$ w/ B! D' J

2 B  T, c( }/ L0 I. `目标平台:TQ2440
) E4 e, ]2 i0 \0 |$ B9 I7 d! w2 r; w" _- \
CPU:s3c24407 l6 I" p: D% ^$ d
/ [- U3 g4 v% z; e3 ]6 P
内核版本:2.6.30
6 R7 ?# N, {6 ^
: _; U% v* c# d% e( j( {) E/ w$ d( d6 d+ ]; h/ C8 R

" F3 c! j2 ]9 g! Q9 @) E+ m1. 看门狗概述
/ @. B1 w' Q" E0 T1 W  I5 N' @   看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行"喂狗“,如果不这样做,定时器溢出后则将复位CPU。
. U  F, j" m' h2 R; |$ k, R
, ]5 `' n7 Z. F8 K+ m' D: l   因此,看门狗通常用于对处于异常状态的CPU进行复位。" x3 X7 P3 A. X$ `2 ^' \! o2 H# ]
8 U- B  e. W/ }
   具体的概念请自行百度。
, o3 `2 t" i: `' P5 W% L6 x- ?3 \$ i4 v

) @, S. D) V' n2. S3C2440看门狗9 S# V# P' f( s' c9 {
   s3c2440的看门狗的原理框图如下:
' d- q$ Y  E0 s9 ]3 d
* E! q! s. ?% a5 L) O7 T
- d/ W- I  U2 t$ [7 F; J' H8 ]; N; r0 x
  可以看出,看门狗定时器的频率由PCLK提供,其预分频器最大取值为255+1;另外,通过MUX,可以进一步降低频率。# F) ]* ~/ |  d

& f! c+ v0 }) F: T3 k  定时器采用递减模式,一旦到0,则可以触发看门狗中断以及RESET复位信号。# N  j! E% s. F2 i
+ g* K5 ?& s! \
  看门狗定时器的频率的计算公式如下:
3 S. ^! |( l. K( O2 L1 i& B5 r( c# L8 e( U; Y3 R3 N* [  N6 ^9 B

. q$ M. ?& ]! x. `$ N' j. F& g
8 ^  i3 D  _  m, Z6 S- y
6 @" p: p- x6 [# d* r
8 z. m. i+ }* A& A! h3 v3. 看门狗驱动
. p- L8 ^- ]3 C5 @$ _. E4 z  看门狗驱动代码位于: linux/drivers/char/watchdog/s3c2410_wdt.c
/ ?# N% o# f; o# i" b# }6 z+ O' T+ |
3.1 模块注册以及probe函数# i7 w5 r( H3 b4 q1 f7 P
static struct platform_driver s3c2410wdt_driver = {
3 \" x4 d2 F9 K2 C/ ]2 {) H    .probe        = s3c2410wdt_probe,6 J3 D, c7 Y; n+ X- T" D
    .remove        = s3c2410wdt_remove,2 u2 G0 n; q& y2 @& p7 j
    .shutdown    = s3c2410wdt_shutdown," {- t0 @) q9 y# H. N
    .suspend    = s3c2410wdt_suspend,
: i/ X* k1 B% |0 U    .resume        = s3c2410wdt_resume,9 m) k$ d& E0 O/ i- ~, M8 T
    .driver        = {
0 C  n; k3 ^7 T% N! Z* m6 H2 e        .owner    = THIS_MODULE,
( O/ P- _0 E/ H- d. T7 [        .name    = "s3c2410-wdt"," ^# [* M8 T" ?$ `4 A$ f" x7 {
    },
) C% M, N- h  T};
5 Y; i5 G. g5 `7 T5 ]/ M/ _5 G
static char banner[] __initdata =
- u0 d* I! e: R    KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
0 |* _- W( o7 U) O
& ?3 Y4 T) L! Z6 ^1 |- Qstatic int __init watchdog_init(void){printk(banner);return platform_driver_register(&s3c2410wdt_driver);}8 W1 ^5 |* i2 I& T4 U; @% c0 g' g
+ j8 P/ i% O+ i
module_init(watchdog_init)
, E) V# X, Y8 z$ G模块的注册函数很简单,直接调用了 platform的驱动注册函数platform_driver_register。% ?) R) f& z% P8 s. `

3 e6 J* p4 K- _  O, X( f 该函数在注册时会调用驱动的probe方法,也即s3c2410wdt_probe函数。3 N. f+ a  I- U' h  h
7 N  I( y5 b8 G; C) A9 p% K
我们来看下这个函数:% ~' H  S. Z' R# D# @

2 `3 f) q5 n" y+ ostatic int s3c2410wdt_probe(struct platform_device *pdev)
' Y0 `* H  m) N/ H9 v: e1 o' s{
1 t( |) P, J0 d7 v- n. ^1 S        struct resource *res;6 b$ J3 S( C* z! ^9 U$ u, W
        struct device *dev;' x1 C$ {1 a# E, L  A9 v9 ^
        unsigned int wtcon;
+ G+ E; I) o- ~        int started = 0;3 w! F/ s6 d* ?* m2 L# P
        int ret;8 j6 k& z$ Z6 `3 Z7 O8 Q
        int size;
$ E0 D: t/ D0 U' l% g% I7 y9 w' h+ C' W5 H
        DBG("%s: probe=%p\n", __func__, pdev);
/ E( P6 I2 i. c4 a$ F0 T( ?7 F  N5 H
        dev = &pdev->dev;* U- s: X3 U, X3 p5 A0 }1 R
        wdt_dev = &pdev->dev;5 @$ {' T' T) y( W( m
+ b$ X; o9 n/ C: I# P! ^) M. E
        /* get the memory region for the watchdog timer */4 w* L, t8 w: ]1 X3 L8 {8 j% V  D4 e
        /*获取平台资源,寄存器地址范围*/7 r$ {5 ]. A/ \/ j& X
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/ `9 d( `9 r5 y6 B9 t        if (res == NULL) {- y# r- D$ d# Q
                dev_err(dev, "no memory resource specified\n");
+ G6 ]  M) c+ B9 P$ G2 y                return -ENOENT;+ A8 W* Y& A$ ^5 `9 @
        }
( Q& ~2 u% L5 L9 t  A  }7 F
7 d! n, [$ O; ?" K+ y# ], `        /*内存申请*/
/ j% D; @" A; q  b        size = (res->end - res->start) + 1;
. a9 I" q7 q% X; L# t  _# p        wdt_mem = request_mem_region(res->start, size, pdev->name);
4 W' J; t: q2 D2 v9 P        if (wdt_mem == NULL) {% O( O6 b2 R$ j: }0 _- B
                dev_err(dev, "failed to get memory region\n");
! |' {( q$ I3 Q& V- N/ V                ret = -ENOENT;
# e" k, z3 p+ X2 A8 c0 g                goto err_req;  A$ x8 V5 f$ x8 U
        }8 Y" t, k# m3 X( A" }: o* i
( @2 R! M+ n/ ^0 i
        /*内存映射*/9 P+ e. s1 z9 r
        wdt_base = ioremap(res->start, size);
, }' i3 e  U) v4 s9 z$ O        if (wdt_base == NULL) {+ @# o2 ~% I( y' w, K6 F2 Q
                dev_err(dev, "failed to ioremap() region\n");7 ^4 c8 ^9 O2 J
                ret = -EINVAL;( K# [* i. [' a
                goto err_req;
" g/ `( @8 Q- {% @& D        }5 N! E3 c0 H! _) g
+ K- U3 N# T' L# u' z2 _
        DBG("probe: mapped wdt_base=%p\n", wdt_base);
  u  R8 h% J- P  N  J. |+ l3 a       
  W" z0 s8 D* n* M0 |) A        /*获取平台资源,看门狗定时器中断号*/9 V( W! j3 j9 k- ?" g$ r; @9 j
        wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);1 x4 Q) A* ?- ?; j  q
        if (wdt_irq == NULL) {
! p) `' i+ e- A$ k; W3 H0 @0 V                dev_err(dev, "no irq resource specified\n");
+ D" Q) c6 k3 o                ret = -ENOENT;
4 ?: t# D$ |' [3 Y4 K! D/ D                goto err_map;' T' \! g8 k; ~/ I% }; v3 W
        }5 M8 [" x8 ?  X' T
       
+ X  e  X& O' ~2 c& ~8 n        /*注册看门狗定时器中断*/
- k, e4 k  w/ q2 b: u' ^        ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
" N2 H# t3 f: a) G0 c+ U        if (ret != 0) {
6 X4 @7 U, }7 d4 h1 r3 h                dev_err(dev, "failed to install irq (%d)\n", ret);+ x8 z4 {+ B5 \7 s3 p8 e0 S  F4 U
                goto err_map;
$ ?  \/ m8 K. {. s        }7 H- K$ Y, D6 ?7 h) ?" |/ I
        /*获取看门狗模块的时钟*/# O/ G0 P5 c2 b* K$ G5 ]0 G
        wdt_clock = clk_get(&pdev->dev, "watchdog");
2 y0 h3 \2 g6 w        if (IS_ERR(wdt_clock)) {- s6 A- M/ P- ]7 @) w
                dev_err(dev, "failed to find watchdog clock source\n");
% M) i8 F' f: @! L                ret = PTR_ERR(wdt_clock);2 s" U6 p4 k' ]* i* r! W6 [8 T
                goto err_irq;
4 `; G) d4 x! V* q# n/ }        }
; S& D  F  S5 r0 a" |3 x* q8 [' n% Q0 p
        /*使能该时钟*/7 c4 d- u: |& B9 V" X- y
        clk_enable(wdt_clock);+ ~  m9 I9 H) V8 @7 i5 X
2 r8 h( N* W+ u! W
        /* see if we can actually set the requested timer margin, and if
/ ~/ I5 M" D9 h6 @& K5 x) ~1 z         * not, try the default value */% Z9 i" j- v3 s' ^$ M' y
; t+ a4 F2 T! q) E) E- R
        /*设置定时器模块的时钟频率*/: [; `, g, p* Z+ [- Y
        if (s3c2410wdt_set_heartbeat(tmr_margin)) {7 y" |! f( p9 {) q7 S
                started = s3c2410wdt_set_heartbeat(
0 \' v8 \, j0 a* \7 e                                        CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);% P3 }5 c/ J0 |, r/ Z( D3 W

6 j( R5 }' o; N* J3 q3 r5 L  f5 r                if (started == 0); L) X: I  S! ]# }/ e% n0 R
                        dev_info(dev,$ P* K" u  Q( U: b0 ?
                           "tmr_margin value out of range, default %d used\n",8 T: y& _  S0 W  |2 I% M, I0 H- G
                               CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
- U7 J/ j* S4 J; ]! _                else
. y* J. |3 m. r                        dev_info(dev, "default timer value is out of range, cannot start\n");
  T) B6 }! }: J  [        }! o* m% n/ r1 _/ w# J) L0 p

7 ^, Y% {6 @. n6 P) S        /*注册混杂设备,设备名watchdog,次设备号130*/
4 P8 ^, m) C0 F2 B1 [7 j, N; E5 w        ret = misc_register(&s3c2410wdt_miscdev);2 J% p* _2 {* O! b' T1 {; R" ^
        if (ret) {6 J; s$ b" v# k+ S9 C  T
                dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",3 q- U2 @) y, |  V
                        WATCHDOG_MINOR, ret);
% L/ L  H9 B1 p+ q( F2 [9 D                goto err_clk;: G) E6 K4 H+ j7 _6 ?
        }, ?8 Y6 X: }" R" c4 N
) b) _! P. o; U
        /*8 ^7 G" M4 U7 Z6 O. W
          *如果需要在看门狗模块加载时启动看门狗则
2 Y, P9 r; C/ [# r$ m          *调用s3c2410wdt_start,否则调用s3c2410wdt_stop
& D. e3 H( D. m* r1 Z1 Y6 }        */
: a$ @4 t. k1 q: E       
! J! x  `8 |  W$ T( m9 h3 P+ Y! Z9 e2 L        if (tmr_atboot && started == 0) {# v* H6 M( R, h; j
                dev_info(dev, "starting watchdog timer\n");# F8 o% t1 D1 L# U% T
                s3c2410wdt_start();3 p7 n9 [4 o5 {7 |  E3 k
        } else if (!tmr_atboot) {
! `5 L. G$ S  ]" I9 e6 ~4 r                /* if we're not enabling the watchdog, then ensure it is+ e3 p- _. y7 H' I3 e" D
                 * disabled if it has been left running from the bootloader
- X: w& E6 c' G6 [' Z                 * or other source */* S: S0 S; |1 N" D# \( E6 Y

3 I4 y7 y, a- c6 e' g) u. v                s3c2410wdt_stop();
- k# _2 H& C# y' q6 Z0 i        }( R, P% @- t: a& I7 p" m3 X

1 a) k4 o3 K* o        /* print out a statement of readiness */
& F& B: i/ S1 [) U5 T/ }        /*读取控制寄存器,打印目前看门狗的状态*/
+ b: x# o7 w, F. K5 n# s        wtcon = readl(wdt_base + S3C2410_WTCON);( P$ q0 e$ }' C$ z/ k3 d
% M% g9 _: x( C  _( X& p
        dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
) G! j) H8 ?9 i. S. @                 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",* t  p) P# o: U8 n
                 (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
; \  H6 Z2 u- j) g6 I                 (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");
& S+ S! H. a3 ]1 s7 b. P( @
! U7 I$ s& p5 W        return 0;( [0 Z8 i& g! v; F; B

) P4 y. ~) t9 M- |9 ~ err_clk:6 R) R# ?) `! [2 V( O
        clk_disable(wdt_clock);
3 c; s, j1 @: N$ ~7 k9 f9 s        clk_put(wdt_clock);" I- l& V0 ^$ r. g
: g* m! W- l! ]# y, J) d/ a
err_irq:' A  Q1 d0 _4 g4 O7 K
        free_irq(wdt_irq->start, pdev);
' Z4 T( p1 \/ ^7 k/ a
9 x7 [6 a, M" u( w5 } err_map:
! m/ i# F) t: E8 w        iounmap(wdt_base);
+ E0 `) z9 G, b" x# [3 T. I; \! ^
2 U7 S' ?/ B, s/ s, Y0 C err_req:3 T) D2 p) e' r: [5 U
        release_resource(wdt_mem);, G1 H& ^) s& Z( O$ n4 C
        kfree(wdt_mem);8 X" f! X6 z! c5 F

2 s& m2 _! Z9 ?9 X+ q2 H        return ret;
0 J, \/ @& L. f7 @9 l+ v}
! s; h" I) B0 y+ T4 z  a
: u6 P8 E; f; a& ~该函数的前面几步和其他驱动的类似:获取平台资源后进行相应的注册,并使能时钟。接着,将调用s3c2410wdt_set_heartbeat函数来设置看门狗的工作频率。# Y6 G6 g# n, B2 g- [

. @6 h1 }0 l% Q" L6 b然后,注册一个混杂设备,为看门狗注册相应的API到内核中。最后,判断是否需要启动看门狗并调用相应的函数。5 e6 v: w$ J! t* x0 }5 Z
7 P4 I9 D  h9 B
上面是probe函数大致的执行过程。随后我们看下其中被调用的s3c2410wdt_set_heartbeat函数,该函数将设置看门狗的工作频率。
* C! {& a8 l, J" \" A& T& q
5 u0 z0 p% g) e2 U- Q8 u+ pPS:probe函数的执行依赖于平台设备,而看门狗平台设备的添加和注册在linux/arch/ARM/plat-s3c24xx/devs.c和 linux/arch/arm/mach-s3c2410/mach-smdk2410.c中已经完成,因此对于看门狗驱动无需进行移植。
  {  |/ s8 P; P/ F. a$ y5 {6 P0 Y' y  M
3.2 s3c2410wdt_set_heartbeat
  o8 c9 S) \  X3 n# xstatic int s3c2410wdt_set_heartbeat(int timeout)  /*timeout 超时时间,单位秒*/7 y1 ]9 z+ \! G0 D
{# M5 b2 w2 K; \( i
        unsigned int freq = clk_get_rate(wdt_clock);$ I2 l8 b* H. W$ ?0 L
        unsigned int count;' ^# B; q' w, q5 n
        unsigned int divisor = 1;
, `( e" V& w( S        unsigned long wtcon;9 C& K- M2 I  I% f" k

' K) _: ]5 ^( d' C1 D        if (timeout < 1)) h( H/ W  |% A! w- ^
                return -EINVAL;
. Y3 ?# R9 `, N: |. ^8 A( |# ?0 B+ J' K' s
        freq /= 128;    /*时钟源为PCLK/128,不使用16 32 和64*/) R; p! u9 U* Z' ]+ }
        count = timeout * freq;  /*得出计数器值*/
) i- _- u4 f& f  c! `8 ^3 @- R+ N7 {
        DBG("%s: count=%d, timeout=%d, freq=%d\n",
+ d. ]+ b5 V) U) {$ d5 c: g- v! l            __func__, count, timeout, freq);
0 e7 X3 l( ~( X: o) `
( V2 Q6 b2 [8 d1 y% @        /* if the count is bigger than the watchdog register,, H4 N, H/ O# A
           then work out what we need to do (and if) we can
3 m" q+ Y6 r! A0 F$ M7 E$ `4 x& R           actually make this value
- e0 J& ^0 a: r  l; g        */
# a; ^+ l) k3 }$ u/ Y. ]        /*计数器最大值为0xFFFF,如果大于则要计算Prescaler value*/
/ n' \! e  l. c! L4 w6 [2 ?        if (count >= 0x10000) {
! `, P0 {' W; @2 o                for (divisor = 1; divisor <= 0x100; divisor++) {     /*Prescaler value最大为0xff*/
; S6 z9 R1 x; Y2 b* Q                        if ((count / divisor) < 0x10000)# a/ M) Q6 _* s- k! v% O; u; r% n
                                break;- b! b2 J9 B9 O3 z
                }& i" [, |& w  p- F! H
        /*找不到合适的Prescaler value,报错返回*/+ s; N- H0 \9 U) S. D
                if ((count / divisor) >= 0x10000) {
  u+ Q1 B2 f! X1 j& X1 f6 u$ s! a                        dev_err(wdt_dev, "timeout %d too big\n", timeout);
9 G( t% p' ]4 C+ i& Y0 j8 O6 @6 f                        return -EINVAL;8 k4 ~  _2 k1 Q1 p: z& s' P8 H
                }
2 X; J/ d6 T( }( z4 E$ K- e* s        }
$ b1 o! V( t. z, I9 @" \3 @! @' P, W/ |( x
        tmr_margin = timeout;  /*保存timeout*/+ v$ u8 }0 R% w/ @" i& A

3 S# X# B3 u& t: ~1 g3 Q8 R, \( F5 j  J        DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",* Z) |$ g/ k1 U" W
            __func__, timeout, divisor, count, count/divisor);; X& C# R$ I" Q, A) c, X

/ z( Q- G0 a' m7 E: K  N- Z        count /= divisor;   /*根据Prescaler value计算出新的计数器值*/8 R: l3 K& B( ?8 \
        wdt_count = count; /*保存计数器值*/9 _1 u3 m4 }: r

. O- L' G( r  z2 g) A        /* update the pre-scaler */ , |1 L6 M  W8 e0 M3 e6 L- c" q& s. h( g
        /*
, F# p6 s$ x3 W2 S& a$ d          *设置预分频计数器和数据寄存器! }1 s1 [0 {; `6 d7 \2 _! H
        * NOTE:此时并未使能看门狗定时器: a6 R8 h/ r+ n8 l
           */( i/ |% j0 t4 U9 @+ y. B/ Z
        wtcon = readl(wdt_base + S3C2410_WTCON);
- l3 e% a/ U0 q& n( ?/ P        wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;+ W) A: }" W* r
        wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);  i% h0 [7 \) f/ r8 L# w# R
6 ]0 K) w, y# Q2 z
        writel(count, wdt_base + S3C2410_WTDAT);
, Z9 I7 G0 Z. D: x9 Q, w5 M2 _        writel(wtcon, wdt_base + S3C2410_WTCON);
* ]9 r, H8 D+ m4 t7 a/ l. G' x
. Q5 o7 n' m& i        return 0;( I  T7 X5 `/ P0 _9 W* }1 ]
}+ V2 }/ d* C9 n) @
" N, Y9 y7 H& r% |
形参timeout为定时时间,单位为秒。3 E9 J! z. E+ n9 f) W6 }0 H# G2 G

# Z0 n- L, L. u. N9 @# T9 o  这里唯一需要注意的是freq/= 128这一步。在第2节我们看到,通过MUX,可选择的分频系数为16,32,64和128,但是在这里驱动直接使用了128来计算系数。
( Q& }. n7 O) `* A7 K1 \6 s+ n& U$ ]4 Y7 Z6 s; _! N
  在下一节我们将会看到,驱动为什么在这里只使用了128这个分频系数。
3 w  ]% @+ a- p; \. U0 e# p4 V) {; b
  当该函数调用结束时,Prescalervalue 和计数器值都将计算完成,并写入寄存器。, m5 x, ^9 {1 P8 B- N6 r% [( Z' T

9 J" ~/ h- B1 e1 X) g3.3 定时器的启动、停止和保活
/ b; h6 _5 Z1 F+ S$ @1 E3.3.1 停止% O0 ?( C  C) }/ g# k. R
定时器的停止由 s3c2410wdt_stop函数完成。! s* `, s# h# e6 W2 P. N

5 Q. q  w7 s" k# `static void __s3c2410wdt_stop(void)! g/ _! @. b. O. H% |
{
! |3 ]9 ~/ T/ C        unsigned long wtcon;: d  ~+ _" D4 G) K) C* g
1 K9 g7 O& X! A* j$ E" S
        wtcon = readl(wdt_base + S3C2410_WTCON);
% y5 y$ k# Z6 t4 V6 ?& X. l        /*禁止看门狗,禁止RESET*/
& ?# ~) }0 M% T( L        wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
" D2 h5 h, q+ m& N        writel(wtcon, wdt_base + S3C2410_WTCON);
. H( K0 |2 v3 h+ S( n( |}  t6 \' D$ x0 U4 O/ T1 }. y
+ d: p3 l' r( z0 s% P
static void s3c2410wdt_stop(void)  t/ X# j! [: y6 Z2 I9 @$ J0 H. h
{5 i3 f% U( k5 L9 z4 p( ~( y& ?
    spin_lock(&wdt_lock);% @- C$ H, Y( ^! X" ]' Z
    __s3c2410wdt_stop();
% n' j% H. s0 i) v/ H    spin_unlock(&wdt_lock);, V' ?/ j  @+ d; T1 {
}
) F6 U0 G8 F+ g* t9 }2 q/ c" Y! y8 o; u. f6 @% C
3.3.2 启动
0 P3 |: I4 \' A+ Y定时器的启动由s3c2410wdt_start函数完成。- q! |( h' ?/ \. Z/ ?1 t. p3 d

1 H7 i0 C* z( K- r' Wstatic void s3c2410wdt_start(void)3 ?" C6 ?% F' z& @0 \
{
6 W! C6 U( d3 b2 O3 ^        unsigned long wtcon;% n! A2 |+ @* S, j1 B5 z6 W3 O! B3 [
. V; \4 M8 A# B" A5 i6 s  _
        spin_lock(&wdt_lock);7 c  B* x2 ~! ^* g3 y6 e2 x4 k
$ W- N1 y. D( @- E9 O7 m
        __s3c2410wdt_stop(); ./*先禁止看门狗*/: h( p3 ?; W5 `0 j  }6 Y  ^4 b

  G5 N2 _& m1 [9 W$ y        wtcon = readl(wdt_base + S3C2410_WTCON); /*读取控制寄存器*/
% l: @1 g4 d4 {        /*启动定时器,设置分频系数为128*// b5 V2 y5 L, D" ?/ T" U
        wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128; . z, Y2 N5 c# A8 J# h$ Y4 G! l
( u5 D/ C; d7 f6 L) J7 O0 g  X% V
        if (soft_noboot) { /*判断许是否需要RESET*/
; j! O) j7 r0 g9 j                wtcon |= S3C2410_WTCON_INTEN;          /*使能看门狗中断*/. o5 x, G& \+ a1 ], R7 c
                wtcon &= ~S3C2410_WTCON_RSTEN;        /*取消RESET*/" E% l& y- R; W9 b& y! c) W
        } else {  /*复位*/# S* U5 U, M8 t9 Q+ p# D
                wtcon &= ~S3C2410_WTCON_INTEN; /*禁止看门狗中断*/4 s3 t" o8 w2 a+ C/ S( O% P
                wtcon |= S3C2410_WTCON_RSTEN;  /*设置RESET*/6 E; {6 b2 ^& {9 a
        }/ q3 c+ R* f2 ?' l8 W8 [

8 l! d" E& O/ c( K        DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",9 B/ R/ h% X  T8 A6 u: Q# ]- T- R( L
            __func__, wdt_count, wtcon);
3 g# R9 q5 s9 p0 M( i- ^' a2 |. U( W
        writel(wdt_count, wdt_base + S3C2410_WTDAT);
& `& y6 B; F* p$ L6 \, D" r        writel(wdt_count, wdt_base + S3C2410_WTCNT);
" r0 B0 y6 o/ B* b, Z        /*写入控制器,此时将启动看门狗定时器*/- c- h2 t  [; y! B
        writel(wtcon, wdt_base + S3C2410_WTCON);        
2 U) b6 t2 s: N( @0 V7 I/ {( E        spin_unlock(&wdt_lock);
3 L- i; ]  A( Z4 _" {}
4 p5 R  e5 r9 s在这里我们到了一个宏S3C2410_WTCON_DIV128,这里设置了分频系数为128。而s3c2410wdt_start函数的调用肯定在s3c2410wdt_set_heartbeat之后,这也就是为什么在3.2节中使用了freq/= 128这一步。. w/ f% s+ j. _7 V
/ O% J5 H' A7 y6 w
3.3.3 保活2 }' t9 O; j% A( z
定时器的保活由s3c2410wdt_keepalive函数完成。
" @; n8 g; T+ c2 ?8 o, A( C" J' |$ |8 V$ z: N
static void s3c2410wdt_keepalive(void)
7 r+ `% w" J; u; X0 X* }8 N& s1 f- r{
- |7 `9 w, y0 K* U        spin_lock(&wdt_lock);
0 S6 n  T" a. @        writel(wdt_count, wdt_base + S3C2410_WTCNT); /*重置计数器值*/
8 i4 ~; m0 f/ x* y        spin_unlock(&wdt_lock);
0 f1 S! y& Q& \; i) `" p}3 g8 D+ Y- S  |8 _, g3 N
最后需要说明的是,从3.1节probe函数的执行来看,由于tmr_atboot变量的初值为0,因此看门狗定时器是没有工作的。
8 F- V6 N2 ^: H8 s' u
1 r+ `; o  w) y, G7 ]7 O6 B; n2 O8 S$ S3 ^' R8 R
3.4 看门狗驱动API$ Y/ O3 z+ Q  n' l* u: Z* O2 ?
看门狗驱动提供的API如下:- Y7 f8 ?5 T5 B" E! E

! X( y3 _$ x! Fstatic const struct file_operations s3c2410wdt_fops = {3 s/ d: I# O7 n+ L% W' F
        .owner                = THIS_MODULE,4 h' h' |+ V4 N0 m& }2 a
        .llseek                = no_llseek,
( a1 U; d+ Q8 G0 q) d" y        .write                = s3c2410wdt_write,
4 t9 O! p+ p3 D; b, O# m( a; \8 f        .unlocked_ioctl        = s3c2410wdt_ioctl,+ P/ W# ^# c9 E; v, Q/ j
        .open                = s3c2410wdt_open,! i0 z9 B. ^& \
        .release        = s3c2410wdt_release,
$ S8 z' p9 L& p7 Z! O8 x0 G};5 H. q( W( Q  W

+ @1 _3 {8 Y( \% e我们可以看到驱动提供了4个API,同时,驱动并不支持llseek方法。+ |. p7 z# w: T; @+ o# z/ M* A* m
3.4.1 open方法/ q/ {7 H( p: D- r5 A' l1 j
static int s3c2410wdt_open(struct inode *inode, struct file *file)
8 F; p: p, Q1 K- |{
, F; s' U. @; V: b5 i( K4 L, ^        if (test_and_set_bit(0, &open_lock))/*看门狗设备文件只能open一次*/" y6 y# N# c$ E" m& z/ d7 h9 O/ d% u
                return -EBUSY;
9 D' ?& l: B, `. x3 P( T; Z
. r3 O  e3 y% F/ {        if (nowayout)
: r8 Q0 f7 H+ q                __module_get(THIS_MODULE); /*增加模块引用计数*/9 o. f# i( n0 d5 Z7 X1 b4 E

7 [/ t% d$ B- u, J1 m5 o        allow_close = CLOSE_STATE_NOT;  /*设置标志位,不允许关闭看门狗*/
# A3 e4 V' b1 J0 S9 Y, _" E/ b
, y7 V+ Q, J+ N& ~' |+ x" h        /* start the timer */' v! \/ |* w  |! o2 W  \
        s3c2410wdt_start();                /*启动定时器*/9 i% S0 f: Q+ Q/ ?
        return nonseekable_open(inode, file);  /*告知内核不支持llseek操作*/& x' {4 M  X* A6 i  b! J' p! B
}* r; n! D9 Y. g* ?$ j

* L5 ^1 A: p+ C这里需要注意的是,设备文件/dev/watchdog 只能被open一次,大于一次的open都将返回-EBUSY。
# x0 f" O  l: L7 P% g1 r3.4.2 release方法
, E+ A, T* P9 Z6 Wstatic int s3c2410wdt_release(struct inode *inode, struct file *file)
: X( @; @2 F9 z+ S; a: w- l: p( ?5 o{7 h" {# e2 P6 O9 t5 S7 z
        /*
6 P0 U# I; `/ _0 @+ N         *        Shut off the timer.
) y0 i' o( l% c1 ?% A         *         Lock it in if it's a module and we set nowayout3 e+ L0 l- [* w6 o  r
         */6 q7 u3 b/ H+ Q: e, S/ K
          /*状态是允许关闭看门狗,则停止看门狗,否则保活*/
. \# Y7 F5 C) X! \: W/ R/ R        if (allow_close == CLOSE_STATE_ALLOW)
& N6 ]! _4 o  u                s3c2410wdt_stop();6 b6 U. I9 N- }# H2 r& P, K% A' s" m
        else {) I' p. Q# f( t0 M
                dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");, q7 A& W( M& `
                s3c2410wdt_keepalive();. p# N1 Q/ z0 m: L1 u1 J$ J
        }
+ `$ T4 |# ^: F* E        allow_close = CLOSE_STATE_NOT;  /*设置标志位,不允许关闭看门狗*/
9 m% r8 ^9 k) y        clear_bit(0, &open_lock);% T& I9 I$ W" a% R7 l) d) _
        return 0;) W, H& b, W) ]+ `- u) i5 J( Q
}; O, u/ U2 ]" m$ E' {+ G5 m
3.4.3 wirte方法
% ^5 d' Y: B0 U/ n7 W. Jstatic ssize_t s3c2410wdt_write(struct file *file, const char __user *data,+ Q( I$ \" |. ]; q- @7 {! \4 i
                                size_t len, loff_t *ppos); t8 }1 ?, `5 l0 t
{
: q9 e0 G7 J1 H4 R5 E- u9 j: k        /*% J! I% Q6 `7 ~! w( C& l8 x
         *        Refresh the timer." q. V, d( Y. t0 n7 F! u$ \* q. b
         */
* q" p1 j, T3 I        if (len) {6 m6 X% b+ m0 k( e, e
                /*nowayout 为真,不允许看门狗停止,使其保活*/6 ^  r6 F9 V2 f/ ~$ p, O3 @
                if (!nowayout) {0 |2 W4 ]+ g4 E: T: c) w4 ^
                        size_t i;
6 L1 p4 x7 f& t* d% t& v, N  Q/ ]. X7 m& R
                        /* In case it was set long ago */" H! R' o: i" M$ C% m" \
                        allow_close = CLOSE_STATE_NOT;/*设置标志位,不允许关闭看门狗*/
2 W' S% }3 \" H( X5 ]# G4 u7 a1 T& l1 z
                        for (i = 0; i != len; i++) {
, i, O! t& }; U% v                                char c;, d' P( Y1 ?4 V0 w, e
' a. N0 U- Y% l2 d; |- o
                                if (get_user(c, data + i)) /*从用户空间获取一个字节的数据*/7 P9 P+ w( ?& \. a
                                        return -EFAULT;
. X: j" U2 v- L7 _9 R( Q                                /*读取到字符V,设置标志位,允许关闭看门狗*/
3 P. n( C" k2 u3 A                                if (c == 'V')               
: q; h: d, ~& h1 S9 w7 b0 B7 u                                        allow_close = CLOSE_STATE_ALLOW;
, m. R! f* s$ B/ Y' o  k5 `1 ]1 H  q                        }
/ J7 {- p  C) e3 S# Q/ B. @% {                }
( f# @  t! W# O                s3c2410wdt_keepalive(); /*保活*/3 s1 b) x; M  Z4 _6 p5 K' k, b& X
        }& R9 b! ]0 p# \2 r  C
        return len;
2 E5 ~: ~5 W' Y9 G: I4 x2 I& \}
. T1 {. h/ B7 ^7 N) F2 @8 ~( O5 u; i$ M" {0 p9 [/ n! M, N1 a
只要写入数据的长度不为0,都会调用s3c2410wdt_keepalive函数来重置定时器。/ Z& X% K& I( X6 W/ c  I
+ I( l8 e! U% t8 c
3.4.4 unlocked_ioctl方法; i& U  _* G- Y' i; [& p" y/ v
static long s3c2410wdt_ioctl(struct file *file,        unsigned int cmd,7 E8 r: b/ H* V
                                                        unsigned long arg)+ V& f$ E( |* m5 A. t& U
{. d6 j  S: N8 E: G; |0 H
        void __user *argp = (void __user *)arg;: b: _% F4 y- S
        int __user *p = argp;% g! T( y. H& N5 Z- `
        int new_margin;$ m/ O/ [" _1 |+ P9 \
: H5 `- Q% f% q4 Z% m
        switch (cmd) {
+ M1 S2 ]% |! }( c        case WDIOC_GETSUPPORT:. J' V$ Q6 ?! u  ^" }, O$ d
                return copy_to_user(argp, &s3c2410_wdt_ident,
2 I' c& k1 K$ V% X                        sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;* W0 N( {8 B7 J9 ^3 x
        case WDIOC_GETSTATUS:2 i4 W! I; [. q& M. B# H* R
        case WDIOC_GETBOOTSTATUS:8 w+ L; `0 F- ^# h
                return put_user(0, p);, E0 \5 u4 h- e- M1 ^
        case WDIOC_KEEPALIVE:
! N- D) M2 y( J, h/ J                s3c2410wdt_keepalive();- r" [- [; r9 _- o' Y8 ~+ _
                return 0;
$ a% k" j" W/ e  ?        case WDIOC_SETTIMEOUT:, g6 W5 t& m$ L
                if (get_user(new_margin, p))
' C& b6 P0 |) h: G" p' H( I                        return -EFAULT;
- t. Z$ z, c3 w* M# \1 V                if (s3c2410wdt_set_heartbeat(new_margin))% c# R( U2 M! {$ \: q$ m: R
                        return -EINVAL;. ?8 {- S) S  T1 ]
                s3c2410wdt_keepalive();
# Q# H7 c( X3 k9 p- W                return put_user(tmr_margin, p);
  p# W( O, X! o  L' o0 N        case WDIOC_GETTIMEOUT:
5 h; H* w- G/ U                return put_user(tmr_margin, p);0 ^% v/ ]7 ]9 I# @7 n" |
        default:
# z% K$ G' o6 ~* Q/ O. f6 }, |2 @: L/ v                return -ENOTTY;
" b, b# H: f. G9 L0 o. l* g        }. y! N0 m8 E, ?% c$ C6 m* x
}
, f8 G  e$ b8 z4 m* G" O. C4. 测试程序
4 X" Q+ F* m' e4 ]  t#include <stdio.h>
, w. `3 u- [! E3 r" z#include <unistd.h>. \, F& f. t) V% Z% ]: P
#include <linux/watchdog.h>( K5 r- i: ]5 e8 F& l6 S+ K1 Z
#include <fcntl.h>
  n2 v/ P, q4 J$ R5 a# o8 R1 Y9 f5 o9 s$ ~
int main(void)
2 j% Z" k6 A4 l  E; ~0 a8 N{
; p8 Z& y7 G6 ^5 h: g. D- B        int fd, val, ret;
- G% b! I" O/ G5 H6 b8 i  @0 E       
/ p' p. ^1 ^& d) I5 k4 T2 W        fd = open("/dev/watchdog", O_RDWR);8 t" z9 L& R2 j5 j. O, \9 G! Q% @
        if(fd < 0){
  L! w4 {9 o* m8 Q                printf("open device fail\n");
9 h. Z3 b. e  I* F: H                return -1;
% }9 u7 x- r8 |) U! @" g& [3 e( {        }
7 N' l' a6 c/ I* j       
0 u) X) |! x  \, n+ h) z" @% F0 z        while(1){
- a4 k9 a2 s6 h: R; b+ u2 J                ret = write(fd, &val, sizeof(val));
( A0 a; D, S9 i% y9 }$ k( b( S                if(ret < 0){; y% ]# k" ~& k' |2 }- D
                        perror("watchdog write wrong\n");8 }) b) p2 i3 t9 K# t
                        return -1;
/ _0 h/ z* K+ ]- Z$ g" y& `, f8 T                }
* m7 d: u: o& E, \! w' q; P                sleep(5);* _9 W' v& w, f# o6 I; E
        }+ k% b: L, W' j) E7 E1 S- l! k
        " Y: q) b6 A4 z1 n% U) k, T
        return 0;, ~( W3 B/ z+ b
}
2 Q% J- I9 y$ ^8 u1 x2 r% X' R- G0 t/ I
该测试程序每隔5秒重置看门狗定时器,而驱动默认的超时时间是15秒。
* D$ E" }+ `$ N5 j% M4 u+ V. ^可以将5秒替换为16秒,你会发现系统自动重启了。' s, c# N( A" q0 l& v  z, C- A) o: B

  l- v& Y) d! t" T# e& S2 \9 P5. 结束语
9 Q! e" @. z- Z- T; w0 e6 s$ X: i   本文主要对基于S3C2440的看门狗驱动作出了分析。该驱动只是一个简单的字符设备,比较简单。其中,用于计算预分频系数的s3c2410wdt_set_heartbeat函数比较关键,读者可以好好琢磨下该系数是如何计算出来的。
( W* B" N4 Q; D# R/ F
2 R# \. }! X0 K. f  m) j; h6 l# ^
# g- Q( Y) |' a/ R/ ~. g) K) Z+ a; e7 A' K6 D  f
  • TA的每日心情

    2019-11-29 15:37
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2020-6-15 13:09 | 只看该作者
    看门狗(watchdog)驱动
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-25 17:51 , Processed in 0.218750 second(s), 27 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表