乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      WatchDog驅(qū)動程序

       落寒影LIB 2014-09-22

      看門狗是當CPU進入錯誤狀態(tài)后,無法恢復的情況下,使計算機重新啟動

      由于計算機在工作時不可避免的受到各種各樣的因素干擾,即使再優(yōu)秀的計算機程序也可能因為這種干擾使計算機進入一個死循環(huán),更嚴重的就是導致死機。

      有兩種辦法來處理這種情況:

      一是:采用人工復位的方法

      二是:依賴于某種硬件來執(zhí)行這個復位工作。這種硬件通常叫做看門狗(Watch Dog,WD)

      看門狗,就像一只狗一樣,在那看著們,計算機中通常用定時器來處理這種周期性的動作

      看門狗實際上是一個定時器,其硬件內(nèi)部維護了一個定時器,每當時鐘信號到來時,計數(shù)寄存器減1。如果減到0,則系統(tǒng)重啟(就像狗一樣,看你不認識就咬你,可不管你是誰)。

      如果在減到0之前,系統(tǒng)又設(shè)置計數(shù)寄存器一個較大的值,那么系統(tǒng)永遠不會重啟。系統(tǒng)的這種設(shè)置能力表示系統(tǒng)一直處于一種正常運行狀態(tài)。反之,如果計算機系統(tǒng)崩潰,那么就無法重新設(shè)置計數(shù)寄存器的值。當計數(shù)寄存器為0,系統(tǒng)重啟

       

      看門狗的工作原來很簡單,處理器內(nèi)部一般都集成了一個看門狗硬件。其提供了三個寄存器

      看門狗控制寄存器(WTCON)

      看門狗數(shù)據(jù)寄存器(WTDAT)

      看門狗計數(shù)寄存器(WTCNT)

       

      結(jié)合上圖可知,看門狗從一個PCLK頻率到產(chǎn)生一個RESET復位信號的過程如下:

      1,處理器向看門狗提供一個PCLK時鐘信號。其通過一個8位預分頻器(8-bit Prescaler)使頻率降低

      2,8位預分頻器由控制寄存器WTCON的第8~15位決定。分頻后的頻率就相當于PCLK除以(WTCON[15:8]+1).

      3,然后再通過一個4相分頻器,分成4種大小的頻率。這4種頻率系數(shù)分別是16,32,64,128.看門狗可以通過寄存器的3,4位決定使用哪種頻率

      4,當選擇的時鐘頻率到達計數(shù)器(Down Counter)時,會按照工作頻率將WTCNT減1.當達到0時,就會產(chǎn)生一個中斷信號或者復位信號

      5,如果控制寄存器WTCOON的第二位為1,則發(fā)出一個中斷信號;如果控制寄存器WTCON第0位為1,則輸出一個復位信號,使系統(tǒng)重新啟動

       

      看門狗驅(qū)動涉及兩種設(shè)備模型,分別是平臺設(shè)備和混雜設(shè)備

      平臺設(shè)備模型:

      從Linux2.6起引入了一套新的驅(qū)動管理和注冊模型,即平臺設(shè)備platform_device和平臺驅(qū)動platform_driver.Linux中大部分的設(shè)備驅(qū)動,都可以使用這套機制,設(shè)備用platform_device表示,驅(qū)動用platform_driver表示

       

      平臺設(shè)備模型與傳統(tǒng)的device和driver模型相比,一個十分明顯的優(yōu)勢在于平臺設(shè)備模型將設(shè)備本身的資源注冊進內(nèi)核,由內(nèi)核統(tǒng)一管理。這樣提高了驅(qū)動和資源管理的獨立性,并且擁有較好的可移植性和安全性。通過平臺設(shè)備模型開發(fā)底層驅(qū)動的大致流程為下圖:

       

      平臺設(shè)備是指處理器上集成的額外功能的附加設(shè)備,如Watch Dog,IIC,IIS,RTC,ADC等設(shè)備。這些額外功能設(shè)備是為了節(jié)約硬件成本、減少產(chǎn)品功耗、縮小產(chǎn)品形狀而集成到處理器內(nèi)部的。需要注意的是,平臺設(shè)備并不是與字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備并列的概念,而是一種平行的概念,其從另一個角度對設(shè)備進行了概括。如果從內(nèi)核開發(fā)者的角度來看,平臺設(shè)備的引入,是為了更容易開發(fā)字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備驅(qū)動

      平臺設(shè)備結(jié)構(gòu)體(platform_device)

      struct platform_device

      {

        const  char *name;      //平臺設(shè)備的名字,與驅(qū)動的名字對應

        int  id;                        //與驅(qū)動綁定有關(guān),一般為-1

          struct device dev;   //設(shè)備結(jié)構(gòu)體說明platform_device派生于device

        u32  num_resources;  //設(shè)備使用的資源數(shù)量

             struct resource *resource;   //指向資源的數(shù)組,數(shù)量由num_resources指定

      };

       

      看門狗的平臺設(shè)備為:

      struct platform_device s3c_device_wdt = {
          .name          = "s3c2410-wdt",
          .id          = -1,
          .num_resources      = ARRAY_SIZE(s3c_wdt_resource),
          .resource      = s3c_wdt_resource,
      };

      為了統(tǒng)一管理平臺設(shè)備的資源,在platform_device機構(gòu)體中定義了平臺設(shè)備所使用的資源。

      看門狗的資源如下:

      static struct resource s3c_wdt_resource[] = {
          [0] = {  //  I/O資源指向看門狗的寄存器
              .start = S3C24XX_PA_WATCHDOG,          //看門狗I/O內(nèi)存開始位置,被定義為WTCON的地址0x53000000
              .end   = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,                         //1M的地址空間
              .flags = IORESOURCE_MEM,                                                                           //I/O內(nèi)存資源
          },
          [1] = { //IRQ資源
              .start = IRQ_WDT,                                                                                          //看門狗的開始中斷號,被定義為80
              .end   = IRQ_WDT,                                                                                        //看門狗的結(jié)束中斷號
              .flags = IORESOURCE_IRQ,                                                                          //中斷的IRQ資源
          }

      };

       

      struct resource

      {

                resource_size_t  start;            //資源的開始地址,resource_size是32位或者64位的無符號整數(shù)

                resource_size_t  end;             //資源的結(jié)束地址

                const char *name;                  //資源名

                unsigned long flags;               //資源的類型 (IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等)

                struct resource *parent,*sibling,*child;         //用于構(gòu)建資源的樹形結(jié)構(gòu)

      };

       

      通過platfrom_add_devices()函數(shù)可以將一組設(shè)備添加到系統(tǒng)中,其主要完成以下兩個功能:

      1,分配平臺設(shè)備所使用的資源,并將這些資源掛接到資源樹中

      2,初始化device設(shè)備,并將設(shè)備注冊到系統(tǒng)中

      第一個參數(shù)是平臺設(shè)備數(shù)組的指針,第2個參數(shù)是平臺設(shè)備的數(shù)量

      int platform_add_devices(struct platform_device **devs, int num)
      {
          int i, ret = 0;

          for (i = 0; i < num; i++) {
              ret = platform_device_register(devs[i]);
              if (ret) {
                  while (--i >= 0)
                      platform_device_unregister(devs[i]);
                  break;
              }
          }

          return ret;
      }

       

      通過platform_get_resource()函數(shù)可以獲得平臺設(shè)備的resource資源:

      第一個參數(shù)dev是平臺設(shè)備的指針,第2個參數(shù)type是資源的類型,這些類型可以是(IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等),第3個參數(shù)num是同種資源的索引。例如一個平臺設(shè)備有3哥IORESOURCE_MEM資源,如果要獲得第2個資源,那么需要使num等于1

      struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
      {
          int i;

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

              if (type == resource_type(r) && num-- == 0)
                  return r;
          }
          return NULL;
      }

      平臺設(shè)備驅(qū)動 :

      每一個平臺設(shè)備都對應一個平臺設(shè)備驅(qū)動,這個驅(qū)動用來對平臺設(shè)備進行探測、移除、關(guān)閉和電源管理等操作。

      struct platform_driver {
          int (*probe)(struct platform_device *);                        //探測函數(shù)
          int (*remove)(struct platform_device *);                     //移除函數(shù)
          void (*shutdown)(struct platform_device *);                  //關(guān)閉設(shè)備時調(diào)用該函數(shù)
          int (*suspend)(struct platform_device *, pm_message_t state);          //掛起函數(shù)
          int (*suspend_late)(struct platform_device *, pm_message_t state);    //掛起之后調(diào)用的函數(shù)
          int (*resume_early)(struct platform_device *);                                      //恢復正常狀態(tài)之前調(diào)用的函數(shù)
          int (*resume)(struct platform_device *);                                              //恢復正常狀態(tài)的函數(shù)
          struct device_driver driver;                                                                 //設(shè)備驅(qū)動核心結(jié)構(gòu)
      };

      看門狗的平臺設(shè)備驅(qū)動:

      static struct platform_driver s3c2410wdt_driver = {
          .probe        = s3c2410wdt_probe,
          .remove        = s3c2410wdt_remove,
          .shutdown    = s3c2410wdt_shutdown,
          .suspend    = s3c2410wdt_suspend,
          .resume        = s3c2410wdt_resume,
          .driver        = {
              .owner    = THIS_MODULE,
              .name    = "s3c2410-wdt",
          },
      };

       

      一般來說,在內(nèi)核啟動時,會注冊平臺設(shè)備和平臺設(shè)備驅(qū)動程序。內(nèi)核將在適當?shù)臅r候,將平臺設(shè)備和平臺驅(qū)動連接起來。連接的方法,是用系統(tǒng)中的所有平臺設(shè)備和所有已經(jīng)注冊的平臺驅(qū)動進行匹配。下面是源代碼:

      static int platform_match(struct device *dev, struct device_driver *drv)
      {
          struct platform_device *pdev;

          pdev = container_of(dev, struct platform_device, dev);
          return (strcmp(pdev->name, drv->name) == 0);
      }

      該函數(shù)將由內(nèi)核自己調(diào)用,當設(shè)備找到對應的驅(qū)動時,會觸發(fā)probe函數(shù)。所以,probe函數(shù)一般是驅(qū)動程序加載成功后的第一個調(diào)用函數(shù),在該函數(shù)中可以申請設(shè)備所需要的資源

      driver的綁定是通過driver core自動完成的,完成driver和device的匹配后以后會自動執(zhí)行probe()函數(shù),如果函數(shù)執(zhí)行成功,則driver和device就綁定在一起了,drvier和device匹配的方法有3種: 
      >> 當一個設(shè)備注冊的時候,他會在總線上尋找匹配的driver,platform device一般在系統(tǒng)啟動很早的時候就注冊了 
      >> 當一個驅(qū)動注冊[platform_driver_register()]的時候,他會遍歷所有總線上的設(shè)備來尋找匹配,在啟動的過程驅(qū)動的注冊一般比較晚,或者在模塊載入的時候 
      >> 當一個驅(qū)動注冊[platform_driver_probe()]的時候, 功能上和使用platform_driver_register()是一樣的,唯一的區(qū)別是它不能被以后其他的device probe了,也就是說這個driver只能和一個device綁定。

       

      int __init_or_module platform_driver_probe(struct platform_driver *drv,
              int (*probe)(struct platform_device *))
      {
          int retval, code;

          /* temporary section violation during probe() */
          drv->probe = probe;
          retval = code = platform_driver_register(drv);

          /* Fixup that section violation, being paranoid about code scanning
           * the list of drivers in order to probe new devices.  Check to see
           * if the probe was successful, and make sure any forced probes of
           * new devices fail.
           */
          spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
          drv->probe = NULL;
          if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
              retval = -ENODEV;
          drv->driver.probe = platform_drv_probe_fail;
          spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);

          if (code != retval)
              platform_driver_unregister(drv);
          return retval;
      }

       

      static int platform_drv_probe(struct device *_dev)
      {
          struct platform_driver *drv = to_platform_driver(_dev->driver);
          struct platform_device *dev = to_platform_device(_dev);

          return drv->probe(dev);
      }

      static int platform_drv_probe_fail(struct device *_dev)
      {
          return -ENXIO;
      }

      static int platform_drv_remove(struct device *_dev)
      {
          struct platform_driver *drv = to_platform_driver(_dev->driver);
          struct platform_device *dev = to_platform_device(_dev);

          return drv->remove(dev);
      }

      static void platform_drv_shutdown(struct device *_dev)
      {
          struct platform_driver *drv = to_platform_driver(_dev->driver);
          struct platform_device *dev = to_platform_device(_dev);

          drv->shutdown(dev);
      }

      static int platform_drv_suspend(struct device *_dev, pm_message_t state)
      {
          struct platform_driver *drv = to_platform_driver(_dev->driver);
          struct platform_device *dev = to_platform_device(_dev);

          return drv->suspend(dev, state);
      }

      static int platform_drv_resume(struct device *_dev)
      {
          struct platform_driver *drv = to_platform_driver(_dev->driver);
          struct platform_device *dev = to_platform_device(_dev);

          return drv->resume(dev);
      }

       

      需要將平臺設(shè)備驅(qū)動注冊到系統(tǒng)中才能使用,內(nèi)核提供了platform_driver_register()函數(shù)實現(xiàn)這個功能:

      int platform_driver_register(struct platform_driver *drv)
      {
          drv->driver.bus = &platform_bus_type;                  //平臺總線類型

        //如果定義了probe函數(shù),該函數(shù)將覆蓋driver中定義的函數(shù)(即覆蓋父函數(shù))

          if (drv->probe)
              drv->driver.probe = platform_drv_probe;                                     //默認探測函數(shù)
          if (drv->remove)
              drv->driver.remove = platform_drv_remove;                                //默認移除函數(shù)
          if (drv->shutdown) 
              drv->driver.shutdown = platform_drv_shutdown;                         //默認關(guān)閉函數(shù)
          if (drv->suspend)
              drv->driver.suspend = platform_drv_suspend;                             //默認掛起函數(shù)
          if (drv->resume)
              drv->driver.resume = platform_drv_resume;                                   //默認恢復函數(shù)
          return driver_register(&drv->driver);                          //將驅(qū)動注冊到系統(tǒng)中
      }

      在平臺設(shè)備platform_driver和其父結(jié)構(gòu)driver中相同的方法。如果平臺設(shè)備驅(qū)動中定義probe()方法,那么內(nèi)核將會調(diào)用平臺設(shè)備驅(qū)動中的方法;如果平臺設(shè)備驅(qū)動中沒有定義probe()方法,那么將調(diào)用driver中對應方法。platform_driver_register()函數(shù)用來完成這種功能,并注冊設(shè)備驅(qū)動到內(nèi)核中。platform_driver注冊到內(nèi)核后,內(nèi)核調(diào)用驅(qū)動的關(guān)系如下圖:

       模塊卸載時需要注銷函數(shù) :

      void platform_driver_unregister(struct platform_driver *drv)
      {
          driver_unregister(&drv->driver);
      }

       

      可以使用platform_device_alloc動態(tài)地創(chuàng)建一個設(shè)備:

      struct platform_device *platform_device_alloc(const char *name, int id)
      {
          struct platform_object *pa;

          pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
          if (pa) {
              strcpy(pa->name, name);
              pa->pdev.name = pa->name;
              pa->pdev.id = id;
              device_initialize(&pa->pdev.dev);
              pa->pdev.dev.release = platform_device_release;
          }

          return pa ? &pa->pdev : NULL;
      }

       

      一個更好的方法是,通過下面的函數(shù)動態(tài)創(chuàng)建一個設(shè)備,并把這個設(shè)備注冊到系統(tǒng)中:

      struct platform_device *platform_device_register_simple(const char *name,
                                  int id,
                                  struct resource *res,
                                  unsigned int num)
      {
          struct platform_device *pdev;
          int retval;

          pdev = platform_device_alloc(name, id);
          if (!pdev) {
              retval = -ENOMEM;
              goto error;
          }

          if (num) {
              retval = platform_device_add_resources(pdev, res, num);
              if (retval)
                  goto error;
          }

          retval = platform_device_add(pdev);
          if (retval)
              goto error;

          return pdev;

      error:
          platform_device_put(pdev);
          return ERR_PTR(retval);
      }

      獲取資源中的中斷號:

      int platform_get_irq(struct platform_device *dev, unsigned int num)
      {
          struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

          return r ? r->start : -ENXIO;
      }

      根據(jù)參數(shù)name所指定的名稱,來獲取資源中的中斷號:

      int platform_get_irq_byname(struct platform_device *dev, char *name)
      {
          struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ,
                                    name);

          return r ? r->start : -ENXIO;
      }

       

      Platform device 和 Platform driver實際上是cpu總線可以直接尋址的設(shè)備和驅(qū)動,他們掛載在一個虛擬的總線platform_bus_type上,是一種bus- specific設(shè)備和驅(qū)動。與其他bus-specific驅(qū)動比如pci是一樣的。他們都是將device和device_driver加了一個 warpper產(chǎn)生,仔細看看platform_device就可以看到它必然包含一個device dev,而platform_driver也一樣,它必然包含一個device_driver driver。 
      所有的設(shè)備通過bus_id 掛在總線上,多個device可以共用一個driver,但是一個device不可以對應多個driver。驅(qū)動去注冊時候會根據(jù)設(shè)備名尋找設(shè)備,沒有設(shè) 備會注冊失敗,注冊的過程會通過probe來進行相應資源的申請,以及硬件的初始化,如果probe執(zhí)行成功,則device和driver的綁定就成功 了。設(shè)備注冊的時候同樣會在總線上尋找相應的驅(qū)動,如果找到他也會試圖綁定,綁定的過程同樣是執(zhí)行probe          (參考http://www.cnblogs.com/alfredzzj/archive/2012/07/02/2573699.html)

       

      混雜設(shè)備:

      混雜設(shè)備并沒有一個明確的定義。由于設(shè)備號比較緊張,所以一些不相關(guān)的設(shè)備可以使用同一主設(shè)備號。主設(shè)備號通常是10.由于這個原因,一些設(shè)備也可以叫做混雜設(shè)備。

      struct miscdevice

      {

                 int minor;                               //次設(shè)備號

                 const char *name;               //混雜設(shè)備名字

                 const struct file_operations *fops;     //設(shè)備的操作函數(shù),與字符設(shè)備相同

                 struct list_head list;                           //連向下一個混雜設(shè)備的鏈表

                 struct  device *parent;                  //指向父設(shè)備

                 struct   device  *this_device;         指向當前設(shè)備結(jié)構(gòu)體

      };

       

      看門狗的混雜設(shè)備定義:

      static struct miscdevice s3c2410wdt_miscdev = {
          .minor        = WATCHDOG_MINOR,            //次設(shè)備號,定義為130
          .name        = "watchdog",                           //混雜設(shè)備名字
          .fops        = &s3c2410wdt_fops,             //混雜設(shè)備操作指針
      };

      混雜設(shè)備的注冊很簡單,misc_register(),傳遞一個混雜設(shè)備的指針

      int misc_register(struct miscdevice *misc);

      該函數(shù)內(nèi)部檢測次設(shè)備號是否合法,如果次設(shè)備號被占用,則返回設(shè)備忙狀態(tài)。如果miscdevice的成員minor為255,則嘗試動態(tài)申請一個次設(shè)備號。當次設(shè)備號可用時,函數(shù)會將混雜設(shè)備注冊到內(nèi)核設(shè)備模型中

      相反,注銷函數(shù):

      int  misc_deregister(struct misc_miscdevice *misc);

       

      看門狗設(shè)備驅(qū)動程序:

      主要變量:

      static int nowayout    = WATCHDOG_NOWAYOUT;

          表示絕不允許看門狗關(guān)閉,為1表示不允許關(guān)閉,為0表示允許關(guān)閉,當不允許關(guān)閉調(diào)用close()是沒用的.WATCHDOG_NOWAYOUT的取值由配置選項                  

                CONFIG_WATCHDOG_NOWAYOUT決定:

            #ifdef  CONFIG_WATCHDOG_NOWAYOUT

                 #define  WATCHDOG_NOWAYOUT      1

                 #else

                 #define  WATCHDOG_NOWAYOUT       0

                 #endif

                                                                             

      static int tmr_margin    = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;           //表示默認的看門狗喂狗時間我15秒
      static int tmr_atboot    = CONFIG_S3C2410_WATCHDOG_ATBOOT;       //表示系統(tǒng)啟動時就使能看門狗。為1表示使能,為0表示關(guān)閉
      static int soft_noboot;             //看門狗工作方式,為1表示看門狗作為定時器使用,不發(fā)送復位信號,為0表示發(fā)送復位信號
      static int debug;                 //是否使用調(diào)試模式來調(diào)試代碼。該模式中會打印調(diào)試信息

       

      另一個重要的枚舉值close_state來標識看門狗是否允許關(guān)閉

      typedef enum close_state {
          CLOSE_STATE_NOT,                          //不允許關(guān)閉看門狗
          CLOSE_STATE_ALLOW = 0x4021        //允許關(guān)閉看門狗
      } close_state_t;

       

      看門狗的加載和卸載函數(shù):

      static char banner[] __initdata =
          KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";

      static int __init watchdog_init(void)
      {
          printk(banner);
          return platform_driver_register(&s3c2410wdt_driver);
      }

      static void __exit watchdog_exit(void)
      {
          platform_driver_unregister(&s3c2410wdt_driver);
      }

       

      當調(diào)用platform_driver_register()函數(shù)注冊驅(qū)動后,會觸發(fā)設(shè)備和驅(qū)動的匹配函數(shù)platform_match().匹配成功,則會調(diào)用平臺設(shè)備驅(qū)動中的probe()函數(shù),看門狗驅(qū)動中對應的函數(shù)是s3c2410wdt_probe():代碼:

      static int s3c2410wdt_probe(struct platform_device *pdev)
      {
          struct resource *res;            //資源指針
          struct device *dev;                  //設(shè)備結(jié)構(gòu)體指針
          unsigned int wtcon;                  //用于暫時存放WTCON寄存器的數(shù)據(jù)
          int started = 0;
          int ret;
          int size;

          DBG("%s: probe=%p\n", __func__, pdev);

          dev = &pdev->dev;        //平臺設(shè)備中取出設(shè)備device
          wdt_dev = &pdev->dev;

          /* get the memory region for the watchdog timer */

          res = platform_get_resource(pdev, IORESOURCE_MEM, 0);         //獲得看門狗的內(nèi)存資源
          if (res == NULL) {                          //失敗退出
              dev_err(dev, "no memory resource specified\n");
              return -ENOENT;
          }

          size = (res->end - res->start) + 1;                   //內(nèi)存資源所占的字節(jié)數(shù)
          wdt_mem = request_mem_region(res->start, size, pdev->name);                //申請一塊I/O內(nèi)存,對應看門狗的3個寄存器
          if (wdt_mem == NULL) {                                   //申請設(shè)備退出
              dev_err(dev, "failed to get memory region\n");
              ret = -ENOENT;
              goto err_req;
          }

          wdt_base = ioremap(res->start, size);              //將設(shè)備內(nèi)存映射到虛擬地址空間,這樣可以使用函數(shù)訪問
          if (wdt_base == NULL) {                               //映射失敗退出
              dev_err(dev, "failed to ioremap() region\n");
              ret = -EINVAL;
              goto err_req;
          }

          DBG("probe: mapped wdt_base=%p\n", wdt_base);                //輸出映射基地址,調(diào)試時用
       
          wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);                     //獲得看門狗可以申請的中斷號
          if (wdt_irq == NULL) {                       //獲取中斷號失敗,退出
              dev_err(dev, "no irq resource specified\n");
              ret = -ENOENT;
              goto err_map;
          }

          ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);            //申請中斷,并注冊中斷處理函數(shù)s3c2410wdt_irq()
          if (ret != 0) {                               //申請失敗退出
              dev_err(dev, "failed to install irq (%d)\n", ret);
              goto err_map;
          }

          wdt_clock = clk_get(&pdev->dev, "watchdog");      //得到看門狗時鐘源
          if (IS_ERR(wdt_clock)) {
              dev_err(dev, "failed to find watchdog clock source\n");
              ret = PTR_ERR(wdt_clock);
              goto err_irq;
          }

          clk_enable(wdt_clock);               //使能看門狗時鐘

          /* see if we can actually set the requested timer margin, and if
           * not, try the default value */

          if (s3c2410wdt_set_heartbeat(tmr_margin)) {               //設(shè)置看門狗復位時間tmr_margin,如果時間值不合法,返回非0,重新設(shè)置默認復位時間
              started = s3c2410wdt_set_heartbeat(
                          CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

              if (started == 0)
                  dev_info(dev,
                     "tmr_margin value out of range, default %d used\n",
                         CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
              else
                  dev_info(dev, "default timer value is out of range, cannot start\n");
          }

          ret = misc_register(&s3c2410wdt_miscdev);                  //注冊混雜設(shè)備
          if (ret) {                   //注冊失敗退出
              dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
                  WATCHDOG_MINOR, ret);
              goto err_clk;
          }

          if (tmr_atboot && started == 0) {              //開機時就立即啟動看門狗定時器
              dev_info(dev, "starting watchdog timer\n");
              s3c2410wdt_start();                     //啟動看門狗
          } else if (!tmr_atboot) {
              /* if we're not enabling the watchdog, then ensure it is
               * disabled if it has been left running from the bootloader
               * or other source */

              s3c2410wdt_stop();            //停止看門狗
          }

          /* print out a statement of readiness */

          wtcon = readl(wdt_base + S3C2410_WTCON);         //讀出控制寄存器的值

          dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
               (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",                //看門狗是否啟動
               (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",                //看門狗是否允許發(fā)生復位信號
               (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");                //看門狗是否允許發(fā)出中斷信號

          return 0;

       err_clk:                                   //注冊混雜設(shè)備失敗
          clk_disable(wdt_clock);
          clk_put(wdt_clock);

       err_irq:                   //得到時鐘失敗
          free_irq(wdt_irq->start, pdev);

       err_map:              //獲取中斷失敗
          iounmap(wdt_base);

       err_req:          //申請I/O內(nèi)存失敗
          release_resource(wdt_mem);
          kfree(wdt_mem);

          return ret;
      }

      設(shè)置看門狗復位時間函數(shù):s3c2410wdt_set_heartbeat()

      該函數(shù)的參數(shù)接受看門狗復位時間,默認值是15秒。該函數(shù)主要完成如下幾個功能:

      1,使用clk_set_rate()函數(shù)獲得看門狗的時鐘頻率PCLK

      2,判斷復位時間timeout是否超過計數(shù)寄存器WTCNT能表示的最大值,該寄存器的最大值為65536

      3,設(shè)置第一個分頻器的分頻系數(shù)

      4,設(shè)置數(shù)據(jù)寄存器WTDAT

      源代碼:

      static int s3c2410wdt_set_heartbeat(int timeout)
      {
          unsigned int freq = clk_get_rate(wdt_clock);     //得到看門狗的時鐘頻率PCLK
          unsigned int count;                                             //將填入WTCNT的計數(shù)值
          unsigned int divisor = 1;                                     //要填入WTCON[15:8]的預分頻系數(shù)
          unsigned long wtcon;                                         //暫存WTCON的值

          if (timeout < 1)                                                   //看門狗的復位時間不能小于1秒
              return -EINVAL;

          freq /= 128;                                                        //看門狗默認使用128的四相分頻
          count = timeout * freq;                                       //計數(shù)值 = 秒  X  頻率(每秒時鐘滴答)

          DBG("%s: count=%d, timeout=%d, freq=%d\n",
              __func__, count, timeout, freq);                                     //打印相關(guān)的信息用于調(diào)試

          /* if the count is bigger than the watchdog register,
             then work out what we need to do (and if) we can
             actually make this value
          */

          if (count >= 0x10000) {           //最終填入的計數(shù)值不能大于WTCNT的范圍,WTCNT是一個16位寄存器,其最大值為0x10000
              for (divisor = 1; divisor <= 0x100; divisor++) {    //從1到256,尋找一個合適的預分頻系數(shù)
                  if ((count / divisor) < 0x10000)
                      break;                 //找到則退出
              }

              if ((count / divisor) >= 0x10000) {                                                //經(jīng)過預分頻和四相分頻的計數(shù)值仍大于0x10000,則復位時間太長,看門狗不支持

                  dev_err(wdt_dev, "timeout %d too big\n", timeout);
                  return -EINVAL;
              }
          }

          tmr_margin = timeout;                          //合法的復位時間

          DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
              __func__, timeout, divisor, count, count/divisor);                           //打印相關(guān)的調(diào)試信息

          count /= divisor;                                          //分頻后最終的計數(shù)值
          wdt_count = count;

          /* update the pre-scaler */
          wtcon = readl(wdt_base + S3C2410_WTCON);                           //讀WTCNT的值
          wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;                       //將WTCNT的高8位清零
          wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);                         //填入預分頻系數(shù)

          writel(count, wdt_base + S3C2410_WTDAT);                  //將計數(shù)值寫到數(shù)據(jù)寄存器WTDAT中
          writel(wtcon, wdt_base + S3C2410_WTCON);                //設(shè)置控制寄存器WTCON

          return 0;
      }

      看門狗開始函數(shù)s3c2410wdt_start():

      當所有的工作完成后,并且允許看門狗隨機啟動(tmp_atboot = 1),則會調(diào)用s3c2410wdt_start()函數(shù)使看門狗開始工作 :

      static void s3c2410wdt_start(void)
      {
          unsigned long wtcon;                   //暫存WTCNT

          spin_lock(&wdt_lock);                      //避免不多線程同時訪問臨界資源

          __s3c2410wdt_stop();                  //先停止看門狗便于設(shè)置

          wtcon = readl(wdt_base + S3C2410_WTCON);            //讀取WTCON的值
          wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;            //通過設(shè)置WTCON的第5位允許看門狗工作,并將第3,4位設(shè)置為11,使用四相分頻

          if (soft_noboot) {                                                //看門狗作為定時器使用
              wtcon |= S3C2410_WTCON_INTEN;                       //使能中斷
              wtcon &= ~S3C2410_WTCON_RSTEN;                     //不允許發(fā)送復位信號
          } else {                                                                //看門狗作為復位器使用
              wtcon &= ~S3C2410_WTCON_INTEN;          //禁止發(fā)出中斷
              wtcon |= S3C2410_WTCON_RSTEN;            // 允許發(fā)出復位信號
          }

          DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n", 
              __func__, wdt_count, wtcon);            //打印相關(guān)調(diào)試信息用于調(diào)試


          writel(wdt_count, wdt_base + S3C2410_WTDAT);     //重新寫數(shù)據(jù)寄存器的值
          writel(wdt_count, wdt_base + S3C2410_WTCNT);     //重新寫計數(shù)寄存器的值
          writel(wtcon, wdt_base + S3C2410_WTCON);            //寫控制寄存器的值
          spin_unlock(&wdt_lock);                                            //自旋鎖解鎖
      }

      看門狗停止函數(shù):s3c2410wdt_stop():

      當所有的工作準備完成后,如果不允許看門狗立即啟動(tmp_atboot = 0),則會調(diào)用s3c2410wdt_stop()函數(shù)使看門狗停止工作:

      static void s3c2410wdt_stop(void)
      {
          spin_lock(&wdt_lock);
          __s3c2410wdt_stop();
          spin_unlock(&wdt_lock);
      }

      static void __s3c2410wdt_stop(void)
      {
          unsigned long wtcon;            //暫存WTCNT的值

          wtcon = readl(wdt_base + S3C2410_WTCON);                        //讀取WTCON值
          wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);     //設(shè)置WTCON,使看門狗不工作,并且不發(fā)出復位信號
          writel(wtcon, wdt_base + S3C2410_WTCON);                              //寫控制寄存器的值
      }

       

      看門狗驅(qū)動程序移除函數(shù):s3c2410wdt_remove():

      s3c2440看門狗驅(qū)動程序的移除函數(shù)完成與探測函數(shù)相反的功能。包括釋放I/O內(nèi)存資源、釋放IRQ資源、禁止看門狗時鐘源和注銷混雜設(shè)備:

      static int s3c2410wdt_remove(struct platform_device *dev)
      {
          release_resource(wdt_mem);             //釋放資源resource
          kfree(wdt_mem);                                   //釋放I/O內(nèi)存
          wdt_mem = NULL;

          free_irq(wdt_irq->start, dev);                  //釋放中斷號
          wdt_irq = NULL;

          clk_disable(wdt_clock);                                //禁止時鐘
          clk_put(wdt_clock);                                        //減少時鐘引用計數(shù)
          wdt_clock = NULL;

          iounmap(wdt_base);                                     //關(guān)閉內(nèi)存映射
          misc_deregister(&s3c2410wdt_miscdev);          //注銷混雜設(shè)備
          return 0;
      }

       

      當看門狗關(guān)閉時,內(nèi)核會自動調(diào)用s3c2410wdt_shutdown()函數(shù)先停止看門狗設(shè)備:

      static void s3c2410wdt_shutdown(struct platform_device *dev)
      {
          s3c2410wdt_stop();
      }

      當需要暫??撮T狗時,可以調(diào)用s3c2410wdt_suspend()函數(shù)。該函數(shù)保存看門狗的寄存器,并設(shè)置看門狗為停止狀態(tài)。該函數(shù)一般由電源管理子模塊調(diào)用,用來節(jié)省電源:

      #ifdef CONFIG_PM

      static unsigned long wtcon_save;
      static unsigned long wtdat_save;

      static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
      {
          /* Save watchdog state, and turn it off. */           //保存看門狗當前狀態(tài),就是WTDAT和WTCON。不需要保存WTCNT
          wtcon_save = readl(wdt_base + S3C2410_WTCON);
          wtdat_save = readl(wdt_base + S3C2410_WTDAT);

          /* Note that WTCNT doesn't need to be saved. */
          s3c2410wdt_stop();

          return 0;
      }

      與掛起相反的函數(shù)是恢復函數(shù)s3c2410wdt_resume().該函數(shù)恢復看門狗寄存器的值。如果掛起之前為停止狀態(tài),則恢復后看門狗為停止狀態(tài);如果掛起前為啟動狀態(tài),則恢復后也為啟動:

      static int s3c2410wdt_resume(struct platform_device *dev)
      {
          /* Restore watchdog state. */

          writel(wtdat_save, wdt_base + S3C2410_WTDAT);
          writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
          writel(wtcon_save, wdt_base + S3C2410_WTCON);

          printk(KERN_INFO PFX "watchdog %sabled\n",
                 (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");

          return 0;
      }

       

      混雜設(shè)備是一種特殊的字符設(shè)備,所以混雜設(shè)備的操作方法和字符設(shè)備的操作方法基本一樣。

      看門狗驅(qū)動中,混雜設(shè)備定義:

      static struct miscdevice s3c2410wdt_miscdev = {
          .minor        = WATCHDOG_MINOR,
          .name        = "watchdog",
          .fops        = &s3c2410wdt_fops,
      };

      static const struct file_operations s3c2410wdt_fops = {
          .owner        = THIS_MODULE,
          .llseek        = no_llseek,
          .write        = s3c2410wdt_write,
          .unlocked_ioctl    = s3c2410wdt_ioctl,
          .open        = s3c2410wdt_open,
          .release    = s3c2410wdt_release,
      };

      當用戶程序調(diào)用open()函數(shù)時,內(nèi)核會最終調(diào)用s3c2410wdt_open()函數(shù):

      1,調(diào)用test_and_set_bit()函數(shù)測試open_lock的第0位。如果open_lock的第0位為0,則表示test_and_set_bit()函數(shù)返回0,表示設(shè)備沒有被另外的程序打開。如果為1,則表示設(shè)備已經(jīng)被打開,返回忙EBUSY狀態(tài)

      2,nowayout不為0,表示看門狗絕不允許關(guān)閉,則增加看門狗模塊引用計數(shù)

      3,將是否運行關(guān)閉變量allow_close()設(shè)為CLOSE_STATE_NOT,表示不允許關(guān)閉設(shè)備

      4,使用s3c2410wdt_start()函數(shù)打開設(shè)備

      5,使用nonseekable_open()函數(shù)設(shè)置設(shè)備文件file不允許seek操作,即是不允許對設(shè)備進行定位

      static int s3c2410wdt_open(struct inode *inode, struct file *file)
      {
          if (test_and_set_bit(0, &open_lock))            //只允許打開一次
              return -EBUSY;

          if (nowayout)                                    不允許關(guān)閉設(shè)備
              __module_get(THIS_MODULE);      增加引用計數(shù)

          allow_close = CLOSE_STATE_NOT;              //設(shè)為不允許關(guān)閉

          /* start the timer */
          s3c2410wdt_start();             開始運行看門狗設(shè)備
          return nonseekable_open(inode, file);                      不允許調(diào)用seek()
      }

       

      為了使看門狗設(shè)備在調(diào)用close()函數(shù)關(guān)閉后,能夠使用open()方法重新打開,驅(qū)動程序需要定義s3c2410wdt_release()函數(shù)。應該在s3c2410wdt_release()函數(shù)中清除open_lock的第0位,是設(shè)備能夠被open()函數(shù)打開。如果看門狗允許關(guān)閉,則應該調(diào)用s3c2410wdt_stop()函數(shù)關(guān)閉看門狗。如果不允許關(guān)閉設(shè)備,則調(diào)用s3c2410wdt_keepalive()函數(shù),使看門狗為活動狀態(tài):

      static int s3c2410wdt_release(struct inode *inode, struct file *file)
      {
          /*
           *    Shut off the timer.
           *     Lock it in if it's a module and we set nowayout
           */

          if (allow_close == CLOSE_STATE_ALLOW)               //看門狗為允許狀態(tài)
              s3c2410wdt_stop();
          else {
              dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
              s3c2410wdt_keepalive();
          }
          allow_close = CLOSE_STATE_NOT;
          clear_bit(0, &open_lock);                                  //將open_lock的第0位設(shè)為0,是原子操作
          return 0;
      }
      static void s3c2410wdt_keepalive(void)         //相當于一個喂狗功能
      {
          spin_lock(&wdt_lock);
          writel(wdt_count, wdt_base + S3C2410_WTCNT);             //重寫計數(shù)寄存器WTCNT
          spin_unlock(&wdt_lock);
      }

      混雜設(shè)備s3c2410wdt_miscdev的file_operations中沒有實現(xiàn)read()函數(shù),因為很少需要從看門狗的寄存器中獲取數(shù)據(jù),但是實現(xiàn)了寫函數(shù)s3c2410wdt_write()。該函數(shù)主要用來設(shè)置allow_close變量為允許關(guān)閉狀態(tài)。如果想看門狗設(shè)備中寫入V,那么就允許關(guān)閉設(shè)備:

      static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
                      size_t len, loff_t *ppos)
      {
          /*
           *    Refresh the timer.
           */
          if (len) {                                                                                  //有數(shù)據(jù)寫入len不為0
              if (!nowayout) {                                                                   //允許關(guān)閉
                  size_t i;

                  /* In case it was set long ago */
                  allow_close = CLOSE_STATE_NOT;                                         //允許關(guān)閉狀態(tài)

                  for (i = 0; i != len; i++) {
                      char c;

                      if (get_user(c, data + i))
                          return -EFAULT;
                      if (c == 'V')
                          allow_close = CLOSE_STATE_ALLOW;
                  }
              }
              s3c2410wdt_keepalive();
          }
          return len;
      }

      s3c2410wdt_ioctl()函數(shù)接受一些系統(tǒng)命令,用來設(shè)置看門狗內(nèi)部狀態(tài):

      static long s3c2410wdt_ioctl(struct file *file,    unsigned int cmd,
                                  unsigned long arg)
      {
          void __user *argp = (void __user *)arg;
          int __user *p = argp;
          int new_margin;

          switch (cmd) {
          case WDIOC_GETSUPPORT:                                                  //獲得看門狗設(shè)備信息,這些信息包含在一個watchdog_info結(jié)構(gòu)體中
              return copy_to_user(argp, &s3c2410_wdt_ident,
                  sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
          case WDIOC_GETSTATUS:                                            //和下面的一個,這兩個表示獲得看門狗狀態(tài),一般將0返回給用戶
          case WDIOC_GETBOOTSTATUS:
              return put_user(0, p);
          case WDIOC_KEEPALIVE:                     //對看門狗進行喂狗操作
              s3c2410wdt_keepalive();
              return 0;
          case WDIOC_SETTIMEOUT:                       //用來設(shè)置看門狗的新超時時間,并返回舊超時時間。使用get_user()函數(shù)從用戶空間獲得超時時間,并使用s3c2410wdt_set_heartbeat()函數(shù)設(shè)置新的超時時間。通過put_user()函數(shù)返回舊的超時時間
              if (get_user(new_margin, p))
                  return -EFAULT;
              if (s3c2410wdt_set_heartbeat(new_margin))
                  return -EINVAL;
              s3c2410wdt_keepalive();
              return put_user(tmr_margin, p);
          case WDIOC_GETTIMEOUT:                        //用來獲取當前的超時時間
              return put_user(tmr_margin, p);
          default:
              return -ENOTTY;
          }
      }

       

      看門狗的內(nèi)部存儲單元為一組寄存器,這些寄存器是WTCON,WTDAT,WTCNT。這些寄存器不需要像文件一樣對位置進行尋址,所以不需要對llseek()函數(shù)進行具體實現(xiàn)。ESPIPE表示該設(shè)備不允許尋址:

      loff_t    no_seek(struct  file  * file, loff_t  offset,  int  origin)

      {

               return -ESPIPE;

      }

       

      當看門狗設(shè)備作為定時器使用時,發(fā)出中斷信號,而不是復位信號。該中斷在探測函數(shù)s3c2410wdt_probe()中通過調(diào)用request_irq()函數(shù)向系統(tǒng)做了申請。中斷處理函數(shù)的只要功能是喂狗操作,使看門狗重新開始計數(shù):

      static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
      {
          dev_info(wdt_dev, "watchdog timer expired (irq)\n");

          s3c2410wdt_keepalive();
          return IRQ_HANDLED;
      }

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多