天天看點

platform驅動管理機制第1章platform驅動管理機制

  1. 作者yuanlulu httpblogcsdnnetyuanlulu版權沒有但是轉載請保留此段聲明
  2. 第1章platform驅動管理機制
  3. platform_device
    1. 資料結構
    2. 注冊流程
  4. platform_driver
  5. platform使用例子
    1. lpc32xx的i2c
    2. platform驅動的使用總結
  6. Linux資源樹

    ============================================

    作者:yuanlulu

    http://blog.csdn.net/yuanlulu

    版權沒有,但是轉載請保留此段聲明

    ============================================

第1章platform驅動管理機制

Linux2.6核心引入了一套新的驅動管理機制:Platform_device和Platform_driver。裝置用Platform_device表示,驅動用Platform_driver表示,另外裝置描述了使用的資源,驅動則負責電源管理和使用資源。由于驅動和資源之間的獨立,使得程式的可移植性和可擴充性大大增強。

1.1     platform_device

1.1.1  資料結構

plarform裝置用platform_device來表示,如程式清單1.1所示。

程式清單1.1 platform_device

struct platform_device {

         constchar           *name;                                                                                                                               ①

         int                        id;                                                                                                                                       ②

         structdevice        dev;                                                                                                                                    ③

         u32                     num_resources;                                                                                                                  ④

         structresource     * resource;                                                                                                                          ⑤

};

各個成員的解釋如下:

①name指向裝置的名字字元串,這個名字用來和同名的platform_driver進行比對。

②id是裝置編号(如果有多個同類裝置的話),如果隻有一個則需設為-1。

③裝置模型用到的成員。

④使用到的資源的數量。

⑤資源數組頭位址。

最後一個成員resource指向此裝置用到的資源的數組,struct resource的定義如程式清單1.2所示。

程式清單1.2 struct resource

struct resource {

         resource_size_t          start;                                                                                                                          ①

         resource_size_t          end;                                                                                                                           ②

         constchar                   *name;                                                                                                                      ③

         unsignedlong             flags;                                                                                                                         ④

         structresource            *parent, *sibling,*child;                                                                                           ⑤

};

#define IORESOURCE_BITS                                    0x000000ff                 

#define IORESOURCE_IO                                       0x00000100       

#define IORESOURCE_MEM                                  0x00000200

#define IORESOURCE_IRQ                                     0x00000400

#define IORESOURCE_DMA                                  0x00000800

#define IORESOURCE_PREFETCH                       0x00001000       

#define IORESOURCE_READONLY                     0x00002000

#define IORESOURCE_CACHEABLE                   0x00004000

#define IORESOURCE_RANGELENGTH             0x00008000

#define IORESOURCE_SHADOWABLE               0x00010000

#define IORESOURCE_SIZEALIGN                      0x00020000       

#define IORESOURCE_STARTALIGN                   0x00040000       

#define IORESOURCE_DISABLED                       0x10000000

#define IORESOURCE_UNSET                              0x20000000

#define IORESOURCE_AUTO                                0x40000000

#define IORESOURCE_BUSY                                0x80000000       

各個成員的解釋如下:

①start記錄資源範圍的起始值,比如一段記憶體空間的首位址。

②end記錄資源範圍的結束位置。

③name資源的名字。

④flags用來記錄資源類型以及是否可寫、忙碌等資訊,各位的定義如程式清單1.2所示。

⑤分别記錄資源樹的父節點、兄弟節點和子節點。

1.1.2  注冊流程

注冊platform_device有兩個接口platform_add_devices和platform_device_register,它們的代碼如所示。

程式清單1.3 platform_device注冊接口

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;

                   }

         }

         returnret;

}

int platform_device_register(struct platform_device*pdev)

{

         device_initialize(&pdev->dev);

         returnplatform_device_add(pdev);

}

platform_add_devices注冊多個裝置,循環調用了platform_device_register。platform_device_register隻有兩步,第一步初始化platform_device的dev成員,接下來調用platform_device_add函數,它的實作如程式清單1.4所示。

程式清單1.4 platform_device_add

int platform_device_add(struct platform_device *pdev)

{

         int i,ret = 0;

         if(!pdev)

                   return-EINVAL;

         if(!pdev->dev.parent)                                                                                                                                    ①

                   pdev->dev.parent= &platform_bus;

         pdev->dev.bus= &platform_bus_type;

         if(pdev->id != -1)                                                                                                                                          ②

                   snprintf(pdev->dev.bus_id,BUS_ID_SIZE, "%s.%d", pdev->name,

                             pdev->id);

         else

                   strlcpy(pdev->dev.bus_id,pdev->name, BUS_ID_SIZE);

         for (i =0; i < pdev->num_resources; i++) {                                                                                                  ③

                   structresource *p, *r = &pdev->resource[i];

                   if(r->name == NULL)

                            r->name= pdev->dev.bus_id;

                   p= r->parent;

                   if(!p) {                                                                                                                                                 ④

                            if(r->flags & IORESOURCE_MEM)

                                     p= &iomem_resource;

                            elseif (r->flags & IORESOURCE_IO)

                                     p= &ioport_resource;

                   }

                   if(p && insert_resource(p, r)) {                                                                                                           ⑤

                            printk(KERN_ERR

                                  "%s: failed to claim resource%d/n",

                                  pdev->dev.bus_id, i);

                            ret= -EBUSY;

                            gotofailed;

                   }

         }

         pr_debug("Registeringplatform device '%s'. Parent at %s/n",

                    pdev->dev.bus_id,pdev->dev.parent->bus_id);

         ret =device_add(&pdev->dev);                                                                                                                     ⑥

         if (ret== 0)

                   returnret;

failed:

         while(--i >= 0)

                   if(pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))

                            release_resource(&pdev->resource[i]);

         returnret;

}

代碼的解釋如下:

①處初始化裝置的父裝置和總線類型。

②處填寫該裝置在裝置模型中的名字:如果編号不為-1則名字為裝置名加編号,否則隻包含裝置名。

③處循環處理裝置的每一個資源。

④如果父資源指針為空,則為IO端口和IO記憶體設定預設的父資源節點。這裡看到,中斷資源和DMA資源并不設定父節點,因為核心沒有為這兩類資源建立資源樹,它們的使用情況使用另外的方式記錄。

⑤如果有父資源節點,則将資源注冊進核心資源樹(僅僅針對IO端口和IO記憶體)。

       ⑥将本裝置注冊進核心的驅動模型中。

1.2     platform_driver

platform驅動用platform_driver來表示,它的定義如程式清單1.5所示。

程式清單1.5 platform_driver

struct platform_driver {

         int(*probe)(struct platform_device *);

         int(*remove)(struct platform_device *);

         void(*shutdown)(struct platform_device *);

         int(*suspend)(struct platform_device *, pm_message_t state);

         int(*suspend_late)(struct platform_device *, pm_message_t state);

         int(*resume_early)(struct platform_device *);

         int(*resume)(struct platform_device *);

         structpm_ext_ops *pm;

         structdevice_driver driver;

};

platform_driver的各個成員主要用來進行電源管理。其成員driver.name用來比對支援的裝置,它支援的platfirm_device必須使用這個名字。當有platform_device和該驅動名字比對時,probe成員會被調用,傳入比對的裝置指針。解除綁定時remove被調用。

platform_driver的注冊較為簡單,如程式清單1.6所示。

程式清單1.6 platform_driver_register

int platform_driver_register(struct platform_driver*drv)

{

         drv->driver.bus= &platform_bus_type;

         if(drv->probe)

                   drv->driver.probe= platform_drv_probe;

         if(drv->remove)

                   drv->driver.remove= platform_drv_remove;

         if (drv->shutdown)

                   drv->driver.shutdown= platform_drv_shutdown;

         if(drv->suspend)

                   drv->driver.suspend= platform_drv_suspend;

         if(drv->resume)

                   drv->driver.resume= platform_drv_resume;

         if(drv->pm)

                   drv->driver.pm= &drv->pm->base;

         returndriver_register(&drv->driver);

}

注冊過程就是将platform_driver的成員函數賦給driver成員的成員函數指針,然後注冊進裝置模型。

1.3     platform使用例子

1.3.1  lpc32xx的i2c

platform提供了裝置的資源視圖,可以比較集中的管理資源的使用。在Linux核心硬體相關的檔案當中,Linux使用platform機制定義了很多驅動使用的資源,可以在一個檔案中看到多個驅動的資源配置設定情況,有利于資源的規劃和配置設定。對于smartarm3250來說,這個檔案是arch/arm/mach-lpc32xx/arch-lpc32xx.c,這個檔案定義了看門狗、rtc、i2c、spi等各種總線/裝置用到的資源,定義了這些子產品的驅動platform_device,對于i2c的定義如程式清單1.7所示。

程式清單1.7 i2c的platform_device

#if defined (CONFIG_MACH_LPC32XX_I2C0_ENABLE)

static struct i2c_pnx_algo_data lpc32xx_algo_data0 = {

         .base =I2C1_BASE,

         .irq =IRQ_I2C_1,

};

static struct i2c_adapter lpc32xx_adapter0 = {

         .name =I2C_CHIP_NAME "0",

         .algo_data= &lpc32xx_algo_data0,

};

static struct i2c_pnx_data i2c0_data = {

         .suspend= i2c_lpc32xx_suspend,

         .resume= i2c_lpc32xx_resume,

         .calculate_input_freq= calculate_input_freq,

         .set_clock_run= set_clock_run,

         .set_clock_stop= set_clock_stop,

         .adapter= &lpc32xx_adapter0,

};

static struct platform_device i2c0_device = {

         .name ="pnx-i2c",

         .id = 0,

         .dev = {

                   .platform_data= &i2c0_data,

         },

};

#endif

這裡資源的傳遞沒有通過struct resource來傳遞,而是使用了struct platform_device的dev. platform_data傳遞過去的,這種用法不多見,其它子產品的資源通過struct platform_device的resource成員來傳遞。可見這裡已經定義好了裝置的一切資源。

lpc32xx的platform_device是通過phy3250_board_init()->lpc32xx_init()->platform_add_devices()來注冊的。而phy3250_board_init是在硬體架構的初始化時被調用的。

上面的内容是platform_device的定義,完整的驅動模型包含驅動和裝置兩部分。對應的platform_driver定義在driver/i2c/busses/i2c-pnx.c,如所示。

程式清單1.8 lpc32xx的platform_driver

static struct platform_driver i2c_pnx_driver = {

         .driver= {

                   .name= "pnx-i2c",

                   .owner= THIS_MODULE,

         },

         .probe =i2c_pnx_probe,

         .remove= __devexit_p(i2c_pnx_remove),

         .suspend= i2c_pnx_controller_suspend,

         .resume= i2c_pnx_controller_resume,

};

static int __init i2c_adap_pnx_init(void)

{

         returnplatform_driver_register(&i2c_pnx_driver);

}

static void __exit i2c_adap_pnx_exit(void)

{

         platform_driver_unregister(&i2c_pnx_driver);

}

subsys_initcall(i2c_adap_pnx_init);

module_exit(i2c_adap_pnx_exit);

       可以看到這個驅動的名字和platform_device的名字是一緻的,因為名字是它們互相綁定的依據。在Linux裝置模型中,當發現新注冊的驅動和已經注冊的裝置符合綁定條件之後,驅動的probe函數将被調用,probe的主要職能是申請裝置定義的資源(比如中斷和IO記憶體空間)并初始化裝置的硬體,注冊裝置的其它子系統資源(比如i2c的擴充卡結構體)。

       由于i2c_adap_pnx_init是在子系統初始化的時候被調用的,是以比platform_device的初始化要晚。

1.3.2  platform驅動的使用總結

最典型的platform應用就是1.3.1節的所示的形式,在一個檔案中定義裝置的資源和資料結構,在另一個檔案的驅動中進行申請和初始化。裝置和驅動的綁定由Linux裝置模型負責。驅動和裝置的分離使得每個驅動可以對應多個同類的裝置,資源獨立之後可以在不修改裝置資源配置的情況下更新驅動子產品。

典型的應用是在核心編譯之前就定義好了各種平台裝置和它們的資源,如果想在驅動子產品中定義platform_device并申請資源也是可以的。可以使用以下接口動态申請一個platform_device:

struct platform_device *platform_device_alloc( constchar *name, int id);

進行适當的初始化之後就可以使用platform_device_register()進行注冊了。當然也可以直接動态構并注冊:

struct platform_device*platform_device_register_simple(

                      const char *name, int id,

                      struct resource *res, unsigned int nres);

       在子產品當中定義platform_devic和platform_driver并不是核心文檔推薦的用法,因為platform_devic可能被其他子產品或者子系統所依賴或引用。當本子產品解除安裝時,其它子產品可能還在使用。是以在驅動子產品中定義裝置是不合适的,因為驅動的職責不包含裝置的定義,這違背了Linux裝置模型的思想。但是如果保證不會和其它子產品産生依賴,使用平台裝置可以添加裝置電源管理的接口,這對需要低功耗的嵌入式裝置具有重大意義。是以如果對功耗沒有要求,盡量不要用platform,如果有需求則需要小心使用,如果這個裝置比較固定,最好将裝置的定義添加到核心中。

1.4     Linux資源樹

我們看到Linux的平台裝置使用了資源的概念,如程式清單1.2所示。如程式清單1.4所示,platform_device注冊過程中會将沒有設定父節點的IO端口和IO記憶體資源的父節點指針指向預設的兩個節點:ioport_resource和iomem_resource,核心會以倒置的兄弟節點樹的形式來記錄資源的使用情況。它們的定義如程式清單1.9所示。

程式清單1.9 ioport_resource和iomem_resource

struct resource ioport_resource = {

         .name         = "PCI IO",

         .start  = 0,

         .end   = IO_SPACE_LIMIT,

         .flags = IORESOURCE_IO,

};

struct resource iomem_resource = {

         .name         = "PCI mem",

         .start  = 0,

         .end   = -1,

         .flags = IORESOURCE_MEM,

};

對于中斷和DMA類的資源,核心中并沒有相應的資源樹。

在使用IO端口和IO記憶體資源之前,首先要進行申請,也就是從資源樹中申請。IO端口空間的申請接口如下所示:

#define request_region(start,n,name)    __request_region(&ioport_resource,(start), (n), (name));

#define release_region(start,n)__release_region(&ioport_resource, (start), (n));

static inline int __deprecatedcheck_region(resource_size_t s, resource_size_t n);           

這三個接口分别用來申請一段IO端口資源、釋放一段資源和檢查一段資源是否被占用。但是最後一個接口是不安全的,不推薦使用,因為檢查和申請資源并不是原子的。

IO記憶體資源的管理接口如下:

#define request_mem_region(start,n,name)__request_region(&iomem_resource, (start), (n), (name));

#define release_mem_region(start,n)     __release_region(&iomem_resource,(start), (n));

#define check_mem_region(start,n)       __check_region(&iomem_resource,(start), (n));

這三個接口的功能分别用來申請一段IO記憶體資源、釋放一段資源和檢查一段資源是否被占用。同樣check_mem_region也是不安全的。

資源樹僅僅用來使用的記錄,實際使用IO端口和IO記憶體之前還要進行實體位址到虛拟位址之間的映射。

繼續閱讀