Welcome 微信登录

首页 / 操作系统 / Linux / Linux 驱动分析--s3c6410 的SDIO驱动分析

正在研究mw8686 的Linux SDIO驱动,编译好后在s3c6410运行,发现未能出现网络接口设备。一查之下,它使用sdio总线进行操作驱动和设备,但是在 /sys/bus/sdio/devices 没有看到任何设备。因此推理是这个导致WiFi无法使用SDIO的原因。因为sdio驱动的初始化是放在probe当中,而probe是在sdio设备与sdio driver的match的过程中执行中。没有设备,意味着无法进行SDIO初始化。
我用的是Linux 2.6.28的版本,在网络上找到几个其它版本,主要Linux 2.6.21的,利用下午上课时间,进行一下课堂实验。发现这两个版本的实现差别很大,实际上两个分水岭。刚开始没明白这个道理,被两个版本的的源码搞得很混乱,因此为了解决问题,对这个两版本的SDIO实现进行分析

  关于mw8686的模块分析,参见我的博文  <<S3C6410硬件模块分析 -- SDIO WiFi模块分析>>http://www.linuxidc.com/Linux/2011-06/37480.htm

  首先根据档和源码来看三星发了几个关健的BSP,一个是针对linux 2.6.21.另外一个针对 linux 2.6.28的。这两个对SDIO的驱动采用不同的实际,有一篇官方文档可以看看,里面对Linux 2.6.21 SDIO实现分析得详细。    <<SMDK6400 6410 HS MMC porting Guide v1.0 >>,但是网上只能找到这一个http://www.linuxidc.com/Linux/2011-06/37481.htm 如果你对Linux 2.6的驱动模型相当了解,实际也能很快分析清楚。
SDIO总线
   首先mw8686的if_sdio.c直接使用了sdio 总线。这个虚拟总线是由 driver/mmc/core/sdio_bus.c实现,在两个版本,这个基本是变化不大的地方。要注意,这里的SDIO和MMC是两个兼容的接口,因此在源码中两个术语经常互换.    sdio bus是一个标准的Linux 的总线,因此它需要有标准 bus device 和标准bus driver来注册到系统中。     这是 sdio_bus.c的总线定义
static struct bus_type sdio_bus_type = {
 .name  = "sdio",
 .dev_attrs = sdio_dev_attrs,
 .match  = sdio_bus_match,
 .uevent  = sdio_bus_uevent,
 .probe  = sdio_bus_probe,
 .remove  = sdio_bus_remove,
};int sdio_register_bus(void)
{
 return bus_register(&sdio_bus_type);
}void sdio_unregister_bus(void)
{
 bus_unregister(&sdio_bus_type);
}

而对应总线驱动struct device_driver 是 struct sdio_driver
/*
 * SDIO function device driver
 */
struct sdio_driver {
 char *name;
 const struct sdio_device_id *id_table; int (*probe)(struct sdio_func *, const struct sdio_device_id *);
 void (*remove)(struct sdio_func *); struct device_driver drv;
};
它使用 如下两个函数来注册和销毁sdio_driver.extern int sdio_register_driver(struct sdio_driver *);
extern void sdio_unregister_driver(struct sdio_driver *);    而对应总线设备的就是 struct device  是 struct sdio_func,这个名字与device差别太大了,因此我一开没有太在意。   
/*
 * SDIO function devices
 */
struct sdio_func {
 struct mmc_card  *card;  /* the card this device belongs to */
 struct device  dev;  /* the device */
 sdio_irq_handler_t *irq_handler; /* IRQ callback */
 unsigned int  num;  /* function number */ unsigned char  class;  /* standard interface class */
 unsigned short  vendor;  /* vendor id */
 unsigned short  device;  /* device id */ unsigned  max_blksize; /* maximum block size */
 unsigned  cur_blksize; /* current block size */ unsigned  enable_timeout; /* max enable timeout in msec */ unsigned int  state;  /* function state */
#define SDIO_STATE_PRESENT (1<<0)  /* present in sysfs */ u8   tmpbuf[4]; /* DMA:able scratch buffer */ unsigned  num_info; /* number of info strings */
 const char  **info;  /* info strings */ struct sdio_func_tuple *tuples;
};
   对于sdio_func操作  int sdio_add_func(struct sdio_func *func);
  void sdio_remove_func(struct sdio_func *func); 在drivers/net/wireless/libertas/if_sdio.c 它定义了如下的 sdio_driver
static struct sdio_driver if_sdio_driver = {
 .name  = "libertas_sdio",
 .id_table = if_sdio_ids,
 .probe  = if_sdio_probe,
 .remove  = if_sdio_remove,
};static int __init if_sdio_init_module(void)  
{                                            
 int ret = 0;                               
 ret = sdio_register_driver(&if_sdio_driver);
 return ret;                                
}                                            
                                             
static void __exit if_sdio_exit_module(void) 
{                                            
 sdio_unregister_driver(&if_sdio_driver);   
}                                            
 SDIO采用的与MMC兼容的命令接口,因此对于mmc的操作,LINUX又封装一层,使用数据结构 mmc_host 
struct mmc_host {
 struct device  *parent;
 struct device  class_dev;
 int   index;
 const struct mmc_host_ops *ops;
 unsigned int  f_min;
 unsigned int  f_max;
 u32   ocr_avail;
 unsigned long  caps;  /* Host capabilities */
 /* host specific block data */
 unsigned int  max_seg_size; /* see blk_queue_max_segment_size */
 unsigned short  max_hw_segs; /* see blk_queue_max_hw_segments */
 unsigned short  max_phys_segs; /* see blk_queue_max_phys_segments */
 unsigned short  unused;
 unsigned int  max_req_size; /* maximum number of bytes in one req */
 unsigned int  max_blk_size; /* maximum size of one mmc block */
 unsigned int  max_blk_count; /* maximum number of blocks in one req */ /* private data */
 spinlock_t  lock;  /* lock for claim and bus ops */ struct mmc_ios  ios;  /* current io bus settings */
 u32   ocr;  /* the current OCR setting */ /* group bitfields together to minimize padding */
 unsigned int  use_spi_crc:1;
 unsigned int  claimed:1; /* host exclusively claimed */
 unsigned int  bus_dead:1; /* bus has been released */
#ifdef CONFIG_MMC_DEBUG
 unsigned int  removed:1; /* host is being removed */
#endif struct mmc_card  *card;  /* device attached to this host */ wait_queue_head_t wq; struct delayed_work detect; const struct mmc_bus_ops *bus_ops; /* current bus driver */
 unsigned int  bus_refs; /* reference counter */ unsigned int  sdio_irqs;
 struct task_struct *sdio_irq_thread;
 atomic_t  sdio_irq_thread_abort;#ifdef CONFIG_LEDS_TRIGGERS
 struct led_trigger *led;  /* activity led */
#endif struct dentry  *debugfs_root; unsigned long  private[0] ____cacheline_aligned;
};
 而mmc_host是一个特殊内核类 mmc_host,它会在 /sys/class/ 建立一个mmc_host类.
//drivers/mmc/core/host.cstatic struct class mmc_host_class = {
 .name  = "mmc_host",
 .dev_release = mmc_host_classdev_release,
};int mmc_register_host_class(void)
{
 return class_register(&mmc_host_class);
}void mmc_unregister_host_class(void)
{
 class_unregister(&mmc_host_class);
}
 mmc_host用于描述MMC接口 CPU一侧的设备,它可以看成是class device的特例,而插入的SDIO设备,如SD卡则封装成数据结构,如果mmc_host发送命令和数据是通过 struct mmc_host_ops 操作
struct mmc_host_ops {                                                 
 void (*request)(struct mmc_host *host, struct mmc_request *req);   
 void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);       
 int (*get_ro)(struct mmc_host *host);                               
 int (*get_cd)(struct mmc_host *host);                               
                                                                      
 void (*enable_sdio_irq)(struct mmc_host *host, int enable);        
};
 如果SDIO设备操作,直接采用如下定义
static struct mmc_host_ops s3c_hsmmc_ops = {
 .request = s3c_hsmmc_request,
 .set_ios = s3c_hsmmc_set_ios,
};
 mmc_card /mmc_bus如果SD卡的之存储设备进行块设备操作,则需要另一个额外的mmc bus总线,并且需要定义mmc_card结构,对应(device).并且进行存储的块设备操作定义在mmc_driver类。 如果SDIO设备本身不需要这个总线 在mmc又单独定义一个 mmc总线, 
static struct bus_type mmc_bus_type = {
 .name  = "mmc",
 .dev_attrs = mmc_dev_attrs,
 .match  = mmc_bus_match,
 .uevent  = mmc_bus_uevent,
 .probe  = mmc_bus_probe,
 .remove  = mmc_bus_remove,
 .suspend = mmc_bus_suspend,
 .resume  = mmc_bus_resume,
};int mmc_register_bus(void)
{
 return bus_register(&mmc_bus_type);
}void mmc_unregister_bus(void)
{
 bus_unregister(&mmc_bus_type);
}
  mmc_card.它可以看一个bus device 设备
// include/linux/mmc/card.h/*
 * MMC device
 */
struct mmc_card {
 struct mmc_host  *host;  /* the host this device belongs to */
 struct device  dev;  /* the device */
 unsigned int  rca;  /* relative card address of device */
 unsigned int  type;  /* card type */
#define MMC_TYPE_MMC  0  /* MMC card */
#define MMC_TYPE_SD  1  /* SD card */
#define MMC_TYPE_SDIO  2  /* SDIO card */
 unsigned int  state;  /* (our) card state */
#define MMC_STATE_PRESENT (1<<0)  /* present in sysfs */
#define MMC_STATE_READONLY (1<<1)  /* card is read-only */
#define MMC_STATE_HIGHSPEED (1<<2)  /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3)  /* card uses block-addressing */ u32   raw_cid[4]; /* raw card CID */
 u32   raw_csd[4]; /* raw card CSD */
 u32   raw_scr[2]; /* raw card SCR */
 struct mmc_cid  cid;  /* card identification */
 struct mmc_csd  csd;  /* card specific */
 struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
 struct sd_scr  scr;  /* extra SD information */
 struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ unsigned int  sdio_funcs; /* number of SDIO functions */
 struct sdio_cccr cccr;  /* common card info */
 struct sdio_cis  cis;  /* common tuple info */
 struct sdio_func *sdio_func[SDIO_MAX_FUNCS];
/* SDIO functions (devices) */
 unsigned  num_info; /* number of info strings */
 const char  **info;  /* info strings */
 struct sdio_func_tuple *tuples; /* unknown common tuples */ struct dentry  *debugfs_root;
};
 mmc_card 与 sdio 总线的device (sdio_func)的交互的关系体现在 mmc_card中sdio_func定义而mmc_card与mmc_host关系体现在定义 mmc_card的host定义中 而mmc_bus中,其驱动就是mmc_driver  
/*
 * MMC device driver (e.g., Flash card, I/O card...)
 */
struct mmc_driver {
 struct device_driver drv;
 int (*probe)(struct mmc_card *);
 void (*remove)(struct mmc_card *);
 int (*suspend)(struct mmc_card *, pm_message_t);
 int (*resume)(struct mmc_card *);
};extern int mmc_register_driver(struct mmc_driver *);
extern void mmc_unregister_driver(struct mmc_driver *)
 而使用了mmc_driver 好象只有处理SD卡块设备 
//driver/mmc/core/block.cstatic struct mmc_driver mmc_driver = {
 .drv  = {
  .name = "mmcblk",
 },
 .probe  = mmc_blk_probe,
 .remove  = mmc_blk_remove,
 .suspend = mmc_blk_suspend,
 .resume  = mmc_blk_resume,
};

它大体有如下关系,这里出问题就是BSP这一侧 sdio channel1的驱动