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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将介绍看门狗驱动的实现。
2 r( a4 W7 |. J. y9 W/ z; w3 r" R0 L: e1 v8 T
目标平台:TQ2440 6 W9 L5 n7 s0 A: I& r: F' B
$ L. H8 w8 E% g$ }
CPU:s3c2440
* N8 t) J+ T, w! v8 j! ^. y
- ^: y* _/ Q, Z& q  a# p' }! @内核版本:2.6.30. X. a/ |. a  W- ]* ]2 e! {, D

8 p+ y" E7 a0 R1 f, C: [  I
: w0 n8 ~- r8 y( S  s! u$ R1 {: ?8 ]! B6 y2 Q( I) B
1. 看门狗概述
. g8 p: z) k. k7 V( p+ T   看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行"喂狗“,如果不这样做,定时器溢出后则将复位CPU。9 E' K$ a9 o5 [& _2 l2 a- o5 l

; G3 t7 r  P6 P# p5 r* i   因此,看门狗通常用于对处于异常状态的CPU进行复位。/ H4 a8 g3 X9 f7 q4 X& Q

" ?6 ]. W- y; n& Q   具体的概念请自行百度。
! B0 h/ m0 @2 z; _6 e, H2 r4 M- a$ a! @1 x6 ]2 U0 t. z# z

- W1 b" C' l( U2. S3C2440看门狗6 B2 z/ t$ \8 e. T! j7 n7 u3 p9 o! d
   s3c2440的看门狗的原理框图如下:" T0 O( @, m) W" \3 p

: m* s2 ~# L$ k; j8 I 9 b- ?: {9 T$ W
( G8 {" \3 C0 b6 W
  可以看出,看门狗定时器的频率由PCLK提供,其预分频器最大取值为255+1;另外,通过MUX,可以进一步降低频率。8 j, L% F2 D- i- R& \# ?) g
. {0 q4 |( c- z* c. g9 P
  定时器采用递减模式,一旦到0,则可以触发看门狗中断以及RESET复位信号。* S+ h3 o/ z3 u

" [5 A$ K0 P) h5 W  看门狗定时器的频率的计算公式如下:7 j) N" \! I" p, E

0 J( Z: h+ d3 Q4 i( K+ S- ? - e! O, S/ p/ D( J; f- V( s+ ?

* a) j5 g9 O: e- M' q/ Y, U
6 ?: V+ `% T" n+ j8 e0 E5 c
7 w) a; N) V+ }" {4 Y, x# ^- K3. 看门狗驱动
! B9 `0 Z9 e" Y0 G1 p' g  看门狗驱动代码位于: linux/drivers/char/watchdog/s3c2410_wdt.c
9 o$ {! E; p& Y$ D: g; b. {2 Z; v6 O5 ]
3.1 模块注册以及probe函数0 y( x4 P: ^# m4 F
static struct platform_driver s3c2410wdt_driver = {" e; k4 [1 a7 r% c% ^6 a/ W8 L
    .probe        = s3c2410wdt_probe,
) ^) A1 q& A; F. O) L    .remove        = s3c2410wdt_remove,0 R0 V4 `9 L1 o/ v$ Y! O
    .shutdown    = s3c2410wdt_shutdown,
* D! X; F  }8 O# J, R; C    .suspend    = s3c2410wdt_suspend,
* t. ]) d/ I8 x1 S  L1 |    .resume        = s3c2410wdt_resume,
2 H( H6 x: n9 u; b2 s    .driver        = {' A, p" x2 `! X( J4 d1 y% w+ @  ^
        .owner    = THIS_MODULE,! ^3 p/ S# d* O1 x
        .name    = "s3c2410-wdt",+ f' m% J0 r* V  b* L
    },
) T$ N1 g' f. x. ^7 @. {};& E, U9 o3 ^9 I! w% I9 C
7 h8 k! H! C0 f! z, T
static char banner[] __initdata =! R% z" K  F, ]
    KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
4 r0 h. X8 N% t2 r1 @
# n0 T* d0 u+ A0 C4 x  i8 E) C6 n! M7 ustatic int __init watchdog_init(void){printk(banner);return platform_driver_register(&s3c2410wdt_driver);}) w/ h$ M$ N2 |) I0 r/ q

' K! s, {9 }7 s* R5 @module_init(watchdog_init)# {/ e* t  G# Q. \" w+ k; o
模块的注册函数很简单,直接调用了 platform的驱动注册函数platform_driver_register。
) b4 d" E/ G  U) m; E% n  H/ B, \# X) N& F. `2 k- l
该函数在注册时会调用驱动的probe方法,也即s3c2410wdt_probe函数。
! j5 u; [% g4 l+ J" v/ ^$ K! |4 O8 a
我们来看下这个函数:. }) |. l' J0 g" \% ?: n0 k
3 f2 S8 a4 q0 g. k7 k' r+ n
static int s3c2410wdt_probe(struct platform_device *pdev)3 @* s3 ~! `) k  F# Z  P4 _5 j; p
{
* s3 O9 H9 [' L! r5 H        struct resource *res;8 Q2 {: ?3 D, d/ T* u
        struct device *dev;
, C6 _5 L! t/ \: @& [        unsigned int wtcon;
' t/ t# ~  z- H: b        int started = 0;
1 |% H% Q! u. H0 H( `$ Y& r1 A! k" j        int ret;4 g+ S0 y( D$ T2 o/ x7 U) m) t' b* Y
        int size;
4 R9 W7 A& ^  _/ v, x
# p: e1 k4 v# L        DBG("%s: probe=%p\n", __func__, pdev);
8 l* t% [5 h7 K5 g' P3 r
3 r. W  h' W& L9 ?1 |0 L        dev = &pdev->dev;
" [( n% k/ x( J8 p! c        wdt_dev = &pdev->dev;
3 M0 [  m0 J" n7 x  |& C
* d( _& R* f( j        /* get the memory region for the watchdog timer */
1 k8 w5 X. [- Q; `        /*获取平台资源,寄存器地址范围*/+ e" z* V1 Q  x
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
: ?. t- T( h- e2 H- p. u        if (res == NULL) {
8 ]: r' O7 _" x2 j( h) Q' E. Y                dev_err(dev, "no memory resource specified\n");
! P9 n4 M. U4 z                return -ENOENT;1 S6 @) q  {. u+ c4 `
        }
6 U: w% h9 ~7 H5 e
1 _/ C) ]0 [7 a2 w+ I% q4 Y. r6 F        /*内存申请*/
' T, `  ~6 F& Q9 w6 j        size = (res->end - res->start) + 1;
! @5 l( ], g" d8 E% @. }* Z. C        wdt_mem = request_mem_region(res->start, size, pdev->name);2 x+ M7 E- H4 v4 g$ p/ k
        if (wdt_mem == NULL) {
9 m0 w7 L# Z% e) x                dev_err(dev, "failed to get memory region\n");& O! z* _4 \  h( T) }' A% `
                ret = -ENOENT;
. e& ?! b. W* `: d' b: u                goto err_req;
# x/ n2 _- w; E7 W  v& ]        }# N& x, H1 r7 s: M, d  D

! e1 I5 |/ _! E7 [% J. Q        /*内存映射*/
4 ~3 J, \6 l7 {        wdt_base = ioremap(res->start, size);( P9 {' U2 P4 j
        if (wdt_base == NULL) {$ T4 R& ]3 E/ _& \. }& ^
                dev_err(dev, "failed to ioremap() region\n");) d! u1 Y& O& P4 `
                ret = -EINVAL;8 Q0 K. p* f2 J4 W
                goto err_req;1 I, Y. ^% H! I3 R; b4 a
        }
) t8 c7 \! O6 }/ y, y! H* T  ~' k3 A  D% @& w- {1 t7 |
        DBG("probe: mapped wdt_base=%p\n", wdt_base);
+ a" j! X/ d( q7 Q       
' N" g: U  {% e& c8 z' w. C9 @( U        /*获取平台资源,看门狗定时器中断号*/7 i4 P- w: P3 j1 N& j
        wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);8 m+ e: b: {* E1 C5 p4 F
        if (wdt_irq == NULL) {, N( k. F( [9 w& N3 ?4 c
                dev_err(dev, "no irq resource specified\n");" ^0 F# _# M- e; P. G
                ret = -ENOENT;, X6 p  {' {; O& o8 I* ]5 Q3 s
                goto err_map;
1 J4 k( k7 I3 M5 y' N( T        }
  k7 C, p7 R6 \- \1 K8 {$ \        $ U# M- J6 y' F
        /*注册看门狗定时器中断*/3 K. _& i! ]3 d; r4 l" h
        ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
" B0 z% _2 C9 A$ @0 c( W        if (ret != 0) {
( V, n( H1 g' b                dev_err(dev, "failed to install irq (%d)\n", ret);- [+ C8 z  k( g& z8 o0 V
                goto err_map;
) J2 k$ F! S) r% n' j# D! X        }
3 W0 [+ t9 M8 ^; C3 [1 A% K, @: a2 g6 @        /*获取看门狗模块的时钟*/
) [/ h0 b  j( e. N7 {# I        wdt_clock = clk_get(&pdev->dev, "watchdog");
4 B& l' C! X& V; Z$ C        if (IS_ERR(wdt_clock)) {3 W* z, g! [: C5 _! f( e& q/ f% i) E
                dev_err(dev, "failed to find watchdog clock source\n");
/ D" y) z- Y. x) c) x& q; r4 t                ret = PTR_ERR(wdt_clock);8 E% y9 X3 y4 ~8 `7 e& e
                goto err_irq;1 F" Q; ?( ~6 d" R3 j
        }0 I6 J- r. B0 Y! A1 b# V

7 L) M- T7 ]0 T7 Z. D- e9 t        /*使能该时钟*/
4 o( G7 J- B; G% F4 f        clk_enable(wdt_clock);: d$ s9 H# G5 x' E* [2 p/ Z5 K

' i/ s' j0 ~2 v2 v" l( Q8 J/ Q5 K        /* see if we can actually set the requested timer margin, and if4 H! {5 n9 J6 [1 B/ ?. n
         * not, try the default value *// f6 \  l7 ]) v! Y: R. l

" B& W4 X. h; s+ w( t# T        /*设置定时器模块的时钟频率*/
( e. j  W) O1 y- h! }  [        if (s3c2410wdt_set_heartbeat(tmr_margin)) {
' z0 _0 J# r- q) f; v. v& h4 l& k                started = s3c2410wdt_set_heartbeat(0 t7 r/ Z8 ]4 E/ _, e
                                        CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
4 ?" d6 l; Z$ e7 u* b
3 y1 k) A# q3 e3 h& \' y                if (started == 0)* }) ~3 m! c9 b# i& R
                        dev_info(dev,1 ?4 `' M) i0 v/ U2 t9 I0 \
                           "tmr_margin value out of range, default %d used\n",, D& M5 i. M5 R' x. v& C! D
                               CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
3 M. F9 V, A6 J9 |; Z! H                else
; H5 b1 Y! u9 l& a# v4 l                        dev_info(dev, "default timer value is out of range, cannot start\n");
0 S* B' X& h, G( J' ~; K, n        }
4 L1 {" @2 J8 r/ ^  O+ ]" _0 W' l1 J. A# M$ O
        /*注册混杂设备,设备名watchdog,次设备号130*/
$ @# u6 b6 T) p5 Q1 U+ f8 I        ret = misc_register(&s3c2410wdt_miscdev);6 v: N( Y5 `  o: ~' n$ q% K" L
        if (ret) {
! t( ]" [( ^; Y% N7 [8 ^% X7 S                dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
% X4 x, D1 N% c' I                        WATCHDOG_MINOR, ret);
7 o6 g! }* J; ~. r% X                goto err_clk;
/ U( q& d. ?. y# p) n: e        }
6 q( s- N1 V( b" R# _% e; [2 ]3 g& b1 y( w. K  Z) Q
        /*
) Y+ E, p9 N) z5 G3 ~& B          *如果需要在看门狗模块加载时启动看门狗则  a5 y* z' G9 _4 e+ P
          *调用s3c2410wdt_start,否则调用s3c2410wdt_stop
. A% j; F  n& G. E8 c        */9 t: o# Z4 n, y% v( J& P8 Q% b
        ; y6 ]8 y3 }( h* f! |' e
        if (tmr_atboot && started == 0) {6 e% `5 c) {8 [
                dev_info(dev, "starting watchdog timer\n");! R% u  T5 i+ F/ |: R" T
                s3c2410wdt_start();
5 y5 ]9 o/ ]# Q        } else if (!tmr_atboot) {
) d' }: u' D0 C+ J                /* if we're not enabling the watchdog, then ensure it is
$ p; G1 V) C! h                 * disabled if it has been left running from the bootloader  S" N8 A2 N/ }
                 * or other source */
8 B# M, ~5 d% a
; B. A+ V1 @( I                s3c2410wdt_stop();* m, b. a9 p1 v( _: G' X9 a
        }% C) L  M4 t  @' D: m

; t, i# n$ g  S1 v/ @5 x        /* print out a statement of readiness */
4 n4 G$ R8 D. g9 n5 r( _        /*读取控制寄存器,打印目前看门狗的状态*/$ ]. @8 t& m- R4 F4 ~& E1 n
        wtcon = readl(wdt_base + S3C2410_WTCON);. A5 l$ d8 d$ m/ H, q6 B  d& ]
8 ^! V9 c& Q+ e* h4 m
        dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
( V0 U$ ~' B* c4 t+ `& [2 I* d                 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",5 g) @9 C  y; m  q! T, u8 @1 ^. q
                 (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",. `* n" \- x" h+ ~8 N
                 (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");+ {% Y) G5 d  Y4 }) j# N
3 u5 E& `. x2 S3 D
        return 0;
& B7 ]6 w* N3 ~4 f5 H# @
; ]% i! O- I3 n' ~4 G err_clk:
3 j! i2 d3 l! ?/ t* Y+ L' r- m        clk_disable(wdt_clock);4 u: P) W) L7 P9 M0 g  c
        clk_put(wdt_clock);
! W  h' P: C; J5 i: {. H
! `' |9 r4 H# u0 ~6 `4 T6 j err_irq:( C$ K( N+ H' W
        free_irq(wdt_irq->start, pdev);' d7 B, ^4 C" b8 X

$ v1 V( E" m9 d err_map:7 s" Q' L! f& W/ W: S% m
        iounmap(wdt_base);
; J8 I9 T  r( o4 q$ m$ q, ^$ U2 I( S* V& Z$ O# i6 R
err_req:" b& q( ~! S$ E  {: z
        release_resource(wdt_mem);) E) |6 S4 v" ?6 f+ v
        kfree(wdt_mem);
- M8 g: }8 Z+ O2 ~* h
0 @/ z3 ]9 v% Z/ T/ Y6 _: h        return ret;
; n6 V1 W  |3 ^}0 ?. J: E; c5 j# Z
( `# O# U5 Y2 W2 W. M, x8 R/ g5 Y
该函数的前面几步和其他驱动的类似:获取平台资源后进行相应的注册,并使能时钟。接着,将调用s3c2410wdt_set_heartbeat函数来设置看门狗的工作频率。
; z: c( n" }* r0 y# {+ A+ Y: j
# e2 Q+ y, O) t然后,注册一个混杂设备,为看门狗注册相应的API到内核中。最后,判断是否需要启动看门狗并调用相应的函数。
% N5 v6 @& M# N8 o! L
' U+ s* ]- k& H6 I2 a6 }" N上面是probe函数大致的执行过程。随后我们看下其中被调用的s3c2410wdt_set_heartbeat函数,该函数将设置看门狗的工作频率。
0 A- g, b6 W9 I$ b2 x; d7 M" A; a6 f& L; L% J; e
PS:probe函数的执行依赖于平台设备,而看门狗平台设备的添加和注册在linux/arch/ARM/plat-s3c24xx/devs.c和 linux/arch/arm/mach-s3c2410/mach-smdk2410.c中已经完成,因此对于看门狗驱动无需进行移植。
- l3 w+ _7 J$ ~+ w9 w% ~2 g+ U1 T# \1 k
3.2 s3c2410wdt_set_heartbeat
  K' d! U# u( @) Lstatic int s3c2410wdt_set_heartbeat(int timeout)  /*timeout 超时时间,单位秒*/
" D, E  c, B' m! X0 d+ E8 t- ~{
9 m: z+ r/ \( Y        unsigned int freq = clk_get_rate(wdt_clock);  P9 k; m7 E) |& w
        unsigned int count;
9 R5 w7 Z* x/ l) w! B7 S        unsigned int divisor = 1;
" |# m. A9 |. m* S4 a        unsigned long wtcon;$ \1 H7 V& q( `. s9 ?
- B, g" P1 g# O
        if (timeout < 1)
/ ?7 i' g0 D2 z0 @  d                return -EINVAL;
2 k9 g) T) V, k/ |' B$ B# f
! Z! d! Y; d' i! K0 M+ l        freq /= 128;    /*时钟源为PCLK/128,不使用16 32 和64*/
5 R. Z0 K; l. c( c' e0 J  ~        count = timeout * freq;  /*得出计数器值*/
% ?1 e; [# H9 R2 D7 p0 U8 [8 g- z% \: D  Z- u
        DBG("%s: count=%d, timeout=%d, freq=%d\n",
! W' Q& W+ o5 V! U            __func__, count, timeout, freq);
1 W+ b" q4 u1 R# H5 W+ V
, H4 j' _' h9 _: C0 e        /* if the count is bigger than the watchdog register,
* k5 n' r+ T0 T3 w7 R9 k           then work out what we need to do (and if) we can4 |# }; s) R# R1 h/ h& D2 A- `
           actually make this value/ S% z3 x  |4 g% d) a
        */
: m/ H: I! `' t. W% A        /*计数器最大值为0xFFFF,如果大于则要计算Prescaler value*/0 S+ }8 f! @# h5 F
        if (count >= 0x10000) {
1 ?6 O7 W$ ?2 N8 r; N, L                for (divisor = 1; divisor <= 0x100; divisor++) {     /*Prescaler value最大为0xff*/, i! d5 g1 z3 o# e, I4 x
                        if ((count / divisor) < 0x10000)
, V" s+ Z3 M% c- d8 L                                break;
" `1 M' d8 q8 J0 ]2 p                }6 z0 l! D: y0 o# X$ j. V
        /*找不到合适的Prescaler value,报错返回*/9 ?- z! ?: }$ Q
                if ((count / divisor) >= 0x10000) {" o" |3 n- Y* n: `% v; b
                        dev_err(wdt_dev, "timeout %d too big\n", timeout);: _' o  R: H2 Z( h1 [
                        return -EINVAL;
# p: `+ x9 A0 _) B( |! ]  D( U                }
' Z; V& k* _# C. q        }
- ?+ P: l# r" \' e; d/ `; C4 b% m5 ~( T" c- e
        tmr_margin = timeout;  /*保存timeout*/  M4 q0 E7 y2 K6 N& b# X

& `5 F8 E9 }, V5 I8 X        DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
) d' r; [1 n% [            __func__, timeout, divisor, count, count/divisor);# G1 m. [' M9 b* J* _
1 X. ?: ^1 B9 U' c* P1 [* R- P& j( ~
        count /= divisor;   /*根据Prescaler value计算出新的计数器值*/! d  n5 b3 K: i6 K6 s. b  N" q1 _
        wdt_count = count; /*保存计数器值*/% Y/ D* ?9 I- Y2 F7 w/ K8 g8 A

5 L1 m4 d; \, |        /* update the pre-scaler */
1 n7 A& B3 z1 q+ A        /*/ H2 V$ r6 Z, A
          *设置预分频计数器和数据寄存器
* `; [8 t1 o( Z% n        * NOTE:此时并未使能看门狗定时器$ h1 l# t% h  W7 B- V
           */
( V- K- W7 {# ]' q        wtcon = readl(wdt_base + S3C2410_WTCON);/ j, T6 C% h; d; p
        wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;+ Y0 D1 G' R7 q
        wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);/ F+ s9 ^4 y$ a, n6 H1 c
' \. N& G/ v8 O
        writel(count, wdt_base + S3C2410_WTDAT);
' }0 U1 X3 ?8 q# Y- o        writel(wtcon, wdt_base + S3C2410_WTCON);' K  N8 O3 _; G

0 c. [/ W; v+ @% K        return 0;
$ V$ a0 E; f! n  h" M& A}- N7 l3 g6 \6 s+ p, j* i4 Y
5 p/ `4 j% y3 ^4 E  d0 E
形参timeout为定时时间,单位为秒。) [+ f3 e6 `; g) {% h' ?7 a
, a3 c3 [2 G% \! K
  这里唯一需要注意的是freq/= 128这一步。在第2节我们看到,通过MUX,可选择的分频系数为16,32,64和128,但是在这里驱动直接使用了128来计算系数。
, d0 J; m; R1 l# I8 A, @
" E* o6 M5 P+ o" @# }4 e$ N  在下一节我们将会看到,驱动为什么在这里只使用了128这个分频系数。
4 G- O6 }1 N% H+ X1 }5 _: q4 Z8 T% b" m0 E  m& W, s
  当该函数调用结束时,Prescalervalue 和计数器值都将计算完成,并写入寄存器。
1 k' ~  r6 _# U! Q
3 C5 f6 t; _/ V2 Q3.3 定时器的启动、停止和保活8 L/ K7 _: y% a0 `) p9 J
3.3.1 停止8 ?& q  k) y  m0 t! S7 O* K
定时器的停止由 s3c2410wdt_stop函数完成。* y/ ~; O. f! u' p3 F# i3 z
. H$ E( ^/ Y1 [8 e" e! P
static void __s3c2410wdt_stop(void)
; Z8 L7 S* K7 I; w{
' {( G* `8 a7 X        unsigned long wtcon;
* X, k1 r; q9 }+ l
- A0 L* d( u0 J* u8 |) w4 s        wtcon = readl(wdt_base + S3C2410_WTCON);) W* Y1 |1 b. Y, U2 G, H
        /*禁止看门狗,禁止RESET*/. g/ V& u! `/ v, }
        wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);/ x8 y, u( K: @- Z9 {
        writel(wtcon, wdt_base + S3C2410_WTCON);
+ ^5 w6 b" A9 a/ \$ G2 j: g}4 G6 `+ t( U# `

2 e2 ]0 h0 Q* l! Pstatic void s3c2410wdt_stop(void)' f$ _3 |% j" w0 `: A- T# t- e
{
, |/ @" M( K' b) a% x6 t" v  ^    spin_lock(&wdt_lock);
% |# X; l7 A6 ^3 c    __s3c2410wdt_stop();
4 t, |% }) x$ W) P- Z  f, Y" r    spin_unlock(&wdt_lock);
' U  X2 r+ W2 g+ v" {) Q: D) f}4 m# q0 R* c: \
1 p' {. d/ k! o2 q% u3 E
3.3.2 启动
9 ^1 f0 e& P& c% R定时器的启动由s3c2410wdt_start函数完成。
' t/ w% W) ~( T. t5 ~" p  ~0 p# m$ Z; n/ U5 v
static void s3c2410wdt_start(void)% U; q- r  g% w* k
{
$ g: N5 o2 C" ?- j9 r        unsigned long wtcon;  h# W  u- Y! k; M* |8 a

) }( k" Z! R, Z* B        spin_lock(&wdt_lock);. t$ z, K9 u" p8 J7 S9 B( P

5 k" D8 p8 |7 n! T        __s3c2410wdt_stop(); ./*先禁止看门狗*/
! }: i5 b' T' g$ B
6 T) y) r* S; N+ M6 `) ?& z. S        wtcon = readl(wdt_base + S3C2410_WTCON); /*读取控制寄存器*/! [( Z/ X( Q$ j
        /*启动定时器,设置分频系数为128*/
. r: w0 r8 ]  }0 i' H7 Y' z: v        wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
5 ?9 H& f' X/ e/ F, l& q) h6 o! j( \" E5 N
        if (soft_noboot) { /*判断许是否需要RESET*/) F3 g5 m  n( V! d: Z
                wtcon |= S3C2410_WTCON_INTEN;          /*使能看门狗中断*/; t! p8 t: H2 \$ J$ M0 ~
                wtcon &= ~S3C2410_WTCON_RSTEN;        /*取消RESET*/% Y9 D2 M7 s2 T  B5 f  s
        } else {  /*复位*/
, u5 H6 y4 l# N5 S  |: O3 e                wtcon &= ~S3C2410_WTCON_INTEN; /*禁止看门狗中断*/
* X. s; X- K+ p3 X  a: _                wtcon |= S3C2410_WTCON_RSTEN;  /*设置RESET*/
: }2 J# Q+ {( G" ]3 L4 d6 |: q        }( p+ l) `* e" w  s) X. \+ t
) q: `( T, D& S, g  _
        DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
$ ]: M1 K0 i) Z/ W            __func__, wdt_count, wtcon);
" C2 g! c: g, z# s  N1 @) s4 F/ e; ]: @
        writel(wdt_count, wdt_base + S3C2410_WTDAT);
0 J  u8 o0 C7 c/ A! F, d. ]        writel(wdt_count, wdt_base + S3C2410_WTCNT);
2 X* E0 V. g4 X+ u) K. S        /*写入控制器,此时将启动看门狗定时器*// u+ V+ N% D1 W
        writel(wtcon, wdt_base + S3C2410_WTCON);        
4 H" J3 d. E( j0 ~" y& l        spin_unlock(&wdt_lock);9 J9 X9 s5 Q, g" M& `- \
}
) P; R7 p' F6 T9 S  h8 |% A在这里我们到了一个宏S3C2410_WTCON_DIV128,这里设置了分频系数为128。而s3c2410wdt_start函数的调用肯定在s3c2410wdt_set_heartbeat之后,这也就是为什么在3.2节中使用了freq/= 128这一步。1 @: O' ~- H: H, z) i1 R

6 b$ K# p- M0 K/ K3.3.3 保活
& `) G9 M7 h1 i& Q" W定时器的保活由s3c2410wdt_keepalive函数完成。
% M8 C! ?6 o5 e5 s
+ z* Y' {. G3 D  ^static void s3c2410wdt_keepalive(void)2 g# F- S1 P" |  f. M, M. e
{9 O# T; m$ W% O4 ]2 y/ r* d
        spin_lock(&wdt_lock);. ?" J2 ?- T+ ~4 \( ]6 L
        writel(wdt_count, wdt_base + S3C2410_WTCNT); /*重置计数器值*/
; E7 n1 T- c3 T$ s8 r) Z1 @2 Z        spin_unlock(&wdt_lock);
; _, l# x: ]/ e$ L}
+ L# Q3 a# |3 v/ A9 v1 {最后需要说明的是,从3.1节probe函数的执行来看,由于tmr_atboot变量的初值为0,因此看门狗定时器是没有工作的。
% Q7 Y% F! A: V# U( |% I( K% ^4 v4 x4 s+ b

' J/ A6 F! k) z" _9 e7 q; U" x  T" s3.4 看门狗驱动API9 |  Q' l# X! r$ L' u. ^5 m& _+ \. Z; T
看门狗驱动提供的API如下:1 ~1 d$ W3 G$ B# ~6 T1 Z- N7 v
4 ?2 J) ]! N- d2 t
static const struct file_operations s3c2410wdt_fops = {! q% i  R9 U3 k6 o6 p
        .owner                = THIS_MODULE,
, ^* |1 u# i  a$ P8 e- m. }        .llseek                = no_llseek,  r) D2 F+ F! _: v6 p
        .write                = s3c2410wdt_write,
' u  u# x; R9 |) O        .unlocked_ioctl        = s3c2410wdt_ioctl,
. a  o  l9 U  ~7 ~7 }: g        .open                = s3c2410wdt_open,! J' V' t8 }$ a5 _
        .release        = s3c2410wdt_release,
' K5 c$ B* G3 D' l4 T: Z9 X};# I% e$ R1 o8 D$ m! ?6 `# |* K# M
% Q8 f( K. ^! |* @8 J: u$ K- c
我们可以看到驱动提供了4个API,同时,驱动并不支持llseek方法。
' D9 g- h( \  Z$ z1 C* \3.4.1 open方法
4 z  t. a1 R3 F. J6 {! Z  Dstatic int s3c2410wdt_open(struct inode *inode, struct file *file)
6 Y% e0 n) G( l( n{; F6 {% \$ c( x4 B2 q$ @0 M+ T8 y& ~
        if (test_and_set_bit(0, &open_lock))/*看门狗设备文件只能open一次*/, h1 `2 Z. D) `* O  [! @2 c9 J
                return -EBUSY;
; N  _) I' o' V8 c9 L- }: S! \. O3 n! W8 ?% Y
        if (nowayout)
0 N, E  ]4 _# Z9 L1 h                __module_get(THIS_MODULE); /*增加模块引用计数*/9 }5 C8 R( b, e' O0 @8 U( [( v0 n

, [! E) A; s. B* h1 F        allow_close = CLOSE_STATE_NOT;  /*设置标志位,不允许关闭看门狗*/, a9 H$ M. {6 _4 ^* P

' x4 s9 [' U0 o- w5 V/ U* r# \        /* start the timer */
6 A! p; `3 f1 j0 l: n" [        s3c2410wdt_start();                /*启动定时器*/+ J. A/ n" i' B7 m! g: F
        return nonseekable_open(inode, file);  /*告知内核不支持llseek操作*/
5 u2 Z& D6 x" [9 p1 r! y}( Y2 ~$ D8 b% W" Z. j

* T, u* o6 Y5 i4 I$ N这里需要注意的是,设备文件/dev/watchdog 只能被open一次,大于一次的open都将返回-EBUSY。
8 i/ l) U7 `5 Q3.4.2 release方法: t" Q5 V) b# F. `/ B* i
static int s3c2410wdt_release(struct inode *inode, struct file *file)
$ Y4 T4 q: e5 L2 g  z{
4 ~( T) l* ^7 L: p; @        /*7 S" q/ A. b1 H# g
         *        Shut off the timer.& U7 q; D# L  n3 d- n
         *         Lock it in if it's a module and we set nowayout
% `! n* z5 w# G  U: j         */
6 d8 _: n6 i6 i* h) g' T          /*状态是允许关闭看门狗,则停止看门狗,否则保活*/
) ~; ]3 G! Z5 h        if (allow_close == CLOSE_STATE_ALLOW)1 R* _' d% L& l/ U
                s3c2410wdt_stop();
( ^( g: t0 w. U* u) Y: q% w        else {
+ t7 |- C; O) h# W9 H5 x                dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");6 _4 c; `7 Y6 y6 C/ c
                s3c2410wdt_keepalive();3 Z% }0 [5 C0 O/ H, U+ N0 M
        }- |4 j  r9 b: L, [4 P" u! h7 T
        allow_close = CLOSE_STATE_NOT;  /*设置标志位,不允许关闭看门狗*/
! h, G1 g9 T2 Y1 M3 ?; j0 b; B        clear_bit(0, &open_lock);
: ]* X, j4 ^! F6 v        return 0;! E" A4 t) e. s7 ^
}2 [" O" K4 S" k3 C2 E3 @
3.4.3 wirte方法! |; m7 q; K4 _( r( _& I
static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
9 [: Y, ~6 i8 c, \/ f5 r/ Q4 M                                size_t len, loff_t *ppos)
. m5 u& N: J# U{
  R, z4 W3 M7 x3 n        /*) f) _- @- j/ _$ Z, G/ F; U& m
         *        Refresh the timer.6 G5 y$ j6 G7 B9 u0 I: e
         */
" g: B: _1 w3 y7 s& w! m  W; z. Q/ n        if (len) {" v# v- {) i+ x& }2 b2 u5 E
                /*nowayout 为真,不允许看门狗停止,使其保活*/
$ {2 S; s+ A! p9 n5 h. j                if (!nowayout) {1 k/ c0 f0 d' L6 q; A# z
                        size_t i;
5 v, m& I. d9 n
* J, x# E! i0 q                        /* In case it was set long ago */
! k& R) Q/ d" r7 S/ l. N4 p& D                        allow_close = CLOSE_STATE_NOT;/*设置标志位,不允许关闭看门狗*/0 ^" [2 e8 \7 N, Q. D- L6 h

0 T+ D1 w" Y1 _- Q                        for (i = 0; i != len; i++) {/ Q% `8 c2 R0 [+ {& ^
                                char c;3 L5 o6 J9 J/ V# X$ x1 I+ \
! c% p0 `+ M6 M$ T! I9 Y; A
                                if (get_user(c, data + i)) /*从用户空间获取一个字节的数据*/
; s4 |$ V+ m0 G' B& T' ]                                        return -EFAULT;
7 s3 v* s! T; V7 i2 n2 l                                /*读取到字符V,设置标志位,允许关闭看门狗*/
4 ]  c7 M( P# n: [9 M- F9 a4 b7 b4 z                                if (c == 'V')                2 r2 t! h% g. @/ k
                                        allow_close = CLOSE_STATE_ALLOW;* Q5 V1 C2 t4 P3 C  o3 E1 ?
                        }) B( X6 @! C/ j7 u" K, A8 q# f
                }/ K5 b# _" k. x2 [8 M+ _$ F
                s3c2410wdt_keepalive(); /*保活*/5 h& Q4 A- _7 t; W" N* |
        }3 h0 U; [6 t1 _# A5 w
        return len;) G! l* I' S" ]3 Y7 B7 |
}4 q2 I0 n4 A4 z; ~0 O" r6 o) q1 V

- G* d! T1 \# k' Y只要写入数据的长度不为0,都会调用s3c2410wdt_keepalive函数来重置定时器。/ u0 K' M- `" i. k/ T9 L+ b& N
# s, `3 b: P8 ]8 g
3.4.4 unlocked_ioctl方法% b5 D/ r% }; T
static long s3c2410wdt_ioctl(struct file *file,        unsigned int cmd,& ]4 a8 a+ \' h8 y8 H
                                                        unsigned long arg)- f- E* _( S0 C/ |2 d, H6 ]
{" q9 f3 f9 g7 M2 ?. ^& c
        void __user *argp = (void __user *)arg;" d0 `) X6 W) z6 {0 q4 m" ^0 e3 |
        int __user *p = argp;
- @- J9 B! K# u        int new_margin;6 O% W6 t0 s" ~$ A% l% l
2 ?+ r/ |1 ?3 V
        switch (cmd) {" u  ~6 G4 s8 z+ e
        case WDIOC_GETSUPPORT:+ [- [4 ^& v7 e- @  e
                return copy_to_user(argp, &s3c2410_wdt_ident,' ~+ J0 J( P( |
                        sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
" r/ L- n' \2 e        case WDIOC_GETSTATUS:5 w# ~6 Y; h4 k2 }7 H- e
        case WDIOC_GETBOOTSTATUS:
& z# o# ]8 s+ H% f# w; @/ o                return put_user(0, p);
5 j- ]; `: r/ b. t1 Y- [. C        case WDIOC_KEEPALIVE:
3 K! L7 [, ?; i) w# k5 v& l0 i3 d- \                s3c2410wdt_keepalive();
. [( P4 E0 P$ ?& g  E' @7 X                return 0;
  B- Z0 p# l5 }' |        case WDIOC_SETTIMEOUT:: Y0 ?. x& i2 P4 W0 `3 m) n
                if (get_user(new_margin, p))
' L2 u1 _4 g# q                        return -EFAULT;+ x1 o+ \, c( p- r4 ]8 P
                if (s3c2410wdt_set_heartbeat(new_margin))
) w9 l+ X- I% @                        return -EINVAL;" ?6 j7 g) a1 R" V" K+ w
                s3c2410wdt_keepalive();
5 v5 v9 _+ i4 R& M5 }+ P                return put_user(tmr_margin, p);( j! i- v7 W) @3 N) u; a
        case WDIOC_GETTIMEOUT:9 M+ |% s$ P- j' u3 j1 o' W
                return put_user(tmr_margin, p);
  O1 z) O. S# ]# E7 \        default:1 G  o! J6 c$ f7 Z( y% Z% Y! R1 h
                return -ENOTTY;' _1 H+ `6 }) l: w6 s: X* [
        }$ A: ?7 Z( g: \2 E
}
* r/ a* B7 y- M* P: [4. 测试程序- |) r3 x- l  E3 i
#include <stdio.h>. b$ [# v( {) R( A0 }, D
#include <unistd.h>$ q% h# }& A, H! b! U
#include <linux/watchdog.h>
1 R& u8 \! c' n$ \; h#include <fcntl.h>1 R5 t+ b* t  |7 X: I

/ G2 B: [, v  g0 p+ cint main(void)' o8 V4 ?0 u$ W5 z- N% L
{
8 L; r/ ?7 V  R) o" D        int fd, val, ret;; p, i" ?! I) \" U6 x0 l
        ( b) [( v7 S! w9 a0 K! u- g( u
        fd = open("/dev/watchdog", O_RDWR);
6 ?  i& w4 }( a: e1 r        if(fd < 0){
5 u' ?6 F& r& d5 @# P                printf("open device fail\n");4 Z  n  d% x; |# U& v. H. q
                return -1;
8 L0 g4 ]6 N) ?7 g6 v        }
+ N8 r  ^; i$ b0 ~        / \  c8 e5 u7 N
        while(1){8 v7 r; z9 c2 C4 |1 Y: w" O
                ret = write(fd, &val, sizeof(val));
# @- I, N# ]4 s) F" u- k                if(ret < 0){/ e  t. X0 Q- @9 W* N# f& _( G3 y
                        perror("watchdog write wrong\n");
: M+ m- d  n( `" v                        return -1;. H8 S! `9 }1 n1 s
                }; K( a& j7 {  G5 J8 a7 t* k
                sleep(5);
9 `7 b+ N  U! G  R: F        }
' D2 x9 n+ L: h        , e. }. t9 s+ E1 X3 L+ B
        return 0;
0 o6 l3 i* t3 f# d2 `( r}
0 ^* M8 j% m# f; O% i+ c4 C) \2 r4 _% c# y
该测试程序每隔5秒重置看门狗定时器,而驱动默认的超时时间是15秒。" ^0 Z& R) R9 y3 n( G
可以将5秒替换为16秒,你会发现系统自动重启了。
; @4 y! d8 J9 t/ d6 Q+ k7 F
5 @; q6 T, W1 n2 F, Y, j$ y+ Y' [5. 结束语
7 V, `1 y/ [7 }+ l0 q; O; ~   本文主要对基于S3C2440的看门狗驱动作出了分析。该驱动只是一个简单的字符设备,比较简单。其中,用于计算预分频系数的s3c2410wdt_set_heartbeat函数比较关键,读者可以好好琢磨下该系数是如何计算出来的。
4 E% Z; O; |, Z) g1 I( M; {6 k% |/ S$ ^+ z# F4 Y

5 T6 |, E6 k' o$ I: [6 S5 ^" J. _8 c1 n3 O7 q
  • 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-6-30 05:37 , Processed in 0.109375 second(s), 27 queries , Gzip On.

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

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

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