三星平台SD/MMC驱动分析
2013-11-06 13:02
260 查看
三星平台SD/MMC驱动主要有两个文件sdhci.c和sdhci-s3c.c,核心驱动在后者里面。
我们一步一步分析,先从平台驱动注册看起
sdhci-s3c.c:
注册了sdhci-s3c-driver,看下该结构体定义:
看重点——probe函数:
有点长,但可以看出大致流程还是sdhci-alloc-host -> sdhci-add-host。
主机控制器驱动添加完成后,如果platform_data的cd_type值为EXTERNAL或者GPIO,就是调用相应函数进行检测。
关于三星对cd_type的定义,可以在platform-samsung/include/plat/sdhci.h找到:
几种卡检测模式没有具体研究,但三星在定义platform_data变量的时候并没有对cd_type成员赋值,也就是说随机,但在platform_samsung/dev-hsmmc.c中设置平台数据时有隐性表示:
可以看到,三星官方驱动认为cd_type必须给出,而max_width是可选变量。因此cd_type最好是定义下,否则可能导致驱动正常加载,但识别不到卡。
可以看到官方的说明,关于后两种cd_type,可能是程序一直在执行检测,我没有仔细研究,但测试了两个类型,全部可以识别到卡。
平台驱动的注册流程大致就这么多,只是做了框架性分析,因此内容并不多,下面看下平台设备是怎么注册的。
平台设备定义mach-s3c64xx/mach-mini6410.c:
mmc0设备plat-samsung/dev-hsmmc.c:
资源定义:
platform_data定义:
设备数据在平台初始化的过程中会做修改,文件mach-s3c64xx/mach-mini6410.c:
函数第一行有一句设置平台数据,平台数据定义如下:
由于该部分数据可能因为板子设计不一样而不相同,因此,三星并没有把该部分数据纳入到平台设备文件中,需要没个平台自己配置好参数,并调用设置平台数据函数进行设置。
这也是为什么新下载的内核配置好SD/MMC驱动后编译运行,SD卡驱动和平台设备均正常加载,但缺识别不到SD卡。就是因为默认情况下cd_type类型是不确定的,因此,卡无法被检测到,应该根据平台设置为相应的模式sd驱动程序才可以检测到。
至此,大致的流程全部分析完毕,在这个过程中我自己遇到的问题和需要注意的问题也用红色加粗很明确的标识了出来。
为了解决无法识别到SD卡的问题,下载编译了2.6.38、3.0.8、3.4的内核,并一一和官方比较,由于驱动加载都正常,导致整个查找过程可谓大海捞针,最终确定为平台启动文件中需要配置mmc设备数据。前后花了差不多2天时间,就等着搞定这个驱动了,下一步应该就可以将自己编译的安卓4.3启动了。等4.3成功启动了,下次再好好做个总结。
我们一步一步分析,先从平台驱动注册看起
sdhci-s3c.c:
static int __init sdhci_s3c_init(void) { return platform_driver_register(&sdhci_s3c_driver); }
注册了sdhci-s3c-driver,看下该结构体定义:
static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, .remove = __devexit_p(sdhci_s3c_remove), .suspend = sdhci_s3c_suspend, .resume = sdhci_s3c_resume, .driver = { .owner = THIS_MODULE, .name = "s3c-sdhci", }, };
看重点——probe函数:
static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host; struct sdhci_s3c *sc; struct resource *res; int ret, irq, ptr, clks; if (!pdata) { dev_err(dev, "no device data specified\n"); return -ENOENT; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq specified\n"); return irq; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "no memory specified\n"); return -ENOENT; } host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } sc = sdhci_priv(host); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; sc->ext_cd_gpio = -1; /* invalid gpio number */ platform_set_drvdata(pdev, host); sc->clk_io = clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); goto err_io_clk; } /* enable the local io clock and keep it running for the moment. */ clk_enable(sc->clk_io); for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { struct clk *clk; char *name = pdata->clocks[ptr]; if (name == NULL) continue; clk = clk_get(dev, name); if (IS_ERR(clk)) { dev_err(dev, "failed to get clock %s\n", name); continue; } clks++; sc->clk_bus[ptr] = clk; /* * save current clock index to know which clock bus * is used later in overriding functions. */ sc->cur_clk = ptr; clk_enable(clk); dev_info(dev, "clock source %d: %s (%ld Hz)\n", ptr, name, clk_get_rate(clk)); } if (clks == 0) { dev_err(dev, "failed to find any bus clocks\n"); ret = -ENOENT; goto err_no_busclks; } sc->ioarea = request_mem_region(res->start, resource_size(res), mmc_hostname(host->mmc)); if (!sc->ioarea) { dev_err(dev, "failed to reserve register area\n"); ret = -ENXIO; goto err_req_regs; } host->ioaddr = ioremap_nocache(res->start, resource_size(res)); if (!host->ioaddr) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; goto err_req_regs; } /* Ensure we have minimal gpio selected CMD/CLK/Detect */ if (pdata->cfg_gpio) pdata->cfg_gpio(pdev, pdata->max_width); host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; host->irq = irq; /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; #ifndef CONFIG_MMC_SDHCI_S3C_DMA /* we currently see overruns on errors, so disable the SDMA * support as well. */ host->quirks |= SDHCI_QUIRK_BROKEN_DMA; #endif /* CONFIG_MMC_SDHCI_S3C_DMA */ /* It seems we do not get an DATA transfer complete on non-busy * transfers, not sure if this is a problem with this specific * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; if (pdata->cd_type == S3C_SDHCI_CD_NONE || pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) host->mmc->caps = MMC_CAP_NONREMOVABLE; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; /* * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ if (pdata->clk_type) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; } /* It supports additional host capabilities if needed */ if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } /* The following two methods of card detection might call sdhci_s3c_notify_change() immediately, so they can be called only after sdhci_add_host(). Setup errors are ignored. */ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) pdata->ext_cd_init(&sdhci_s3c_notify_change); if (pdata->cd_type == S3C_SDHCI_CD_GPIO && gpio_is_valid(pdata->ext_cd_gpio)) sdhci_s3c_setup_card_detect_gpio(sc); return 0; err_add_host: release_resource(sc->ioarea); kfree(sc->ioarea); err_req_regs: for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) { clk_disable(sc->clk_bus[ptr]); clk_put(sc->clk_bus[ptr]); } err_no_busclks: clk_disable(sc->clk_io); clk_put(sc->clk_io); err_io_clk: sdhci_free_host(host); return ret; }
有点长,但可以看出大致流程还是sdhci-alloc-host -> sdhci-add-host。
主机控制器驱动添加完成后,如果platform_data的cd_type值为EXTERNAL或者GPIO,就是调用相应函数进行检测。
关于三星对cd_type的定义,可以在platform-samsung/include/plat/sdhci.h找到:
enum cd_types { S3C_SDHCI_CD_INTERNAL, /* use mmc internal CD line */ S3C_SDHCI_CD_EXTERNAL, /* use external callback */ S3C_SDHCI_CD_GPIO, /* use external gpio pin for CD line */ S3C_SDHCI_CD_NONE, /* no CD line, use polling to detect card */ S3C_SDHCI_CD_PERMANENT, /* no CD line, card permanently wired to host */ };
几种卡检测模式没有具体研究,但三星在定义platform_data变量的时候并没有对cd_type成员赋值,也就是说随机,但在platform_samsung/dev-hsmmc.c中设置平台数据时有隐性表示:
void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd) { struct s3c_sdhci_platdata *set = &s3c_hsmmc0_def_platdata; set->cd_type = pd->cd_type; set->ext_cd_init = pd->ext_cd_init; set->ext_cd_cleanup = pd->ext_cd_cleanup; set->ext_cd_gpio = pd->ext_cd_gpio; set->ext_cd_gpio_invert = pd->ext_cd_gpio_invert; if (pd->max_width) set->max_width = pd->max_width; if (pd->cfg_gpio) set->cfg_gpio = pd->cfg_gpio; if (pd->cfg_card) set->cfg_card = pd->cfg_card; if (pd->host_caps) set->host_caps |= pd->host_caps; if (pd->clk_type) set->clk_type = pd->clk_type; }
可以看到,三星官方驱动认为cd_type必须给出,而max_width是可选变量。因此cd_type最好是定义下,否则可能导致驱动正常加载,但识别不到卡。
可以看到官方的说明,关于后两种cd_type,可能是程序一直在执行检测,我没有仔细研究,但测试了两个类型,全部可以识别到卡。
平台驱动的注册流程大致就这么多,只是做了框架性分析,因此内容并不多,下面看下平台设备是怎么注册的。
平台设备定义mach-s3c64xx/mach-mini6410.c:
static struct platform_device *mini6410_devices[] __initdata = { &mini6410_device_eth, &s3c_device_hsmmc0, &s3c_device_hsmmc1, &s3c_device_ohci, &s3c_device_nand, &s3c_device_fb, &mini6410_lcd_powerdev, &s3c_device_adc, &s3c_device_ts, };
mmc0设备plat-samsung/dev-hsmmc.c:
struct platform_device s3c_device_hsmmc0 = { .name = "s3c-sdhci", .id = 0, .num_resources = ARRAY_SIZE(s3c_hsmmc_resource), .resource = s3c_hsmmc_resource, .dev = { .dma_mask = &s3c_device_hsmmc_dmamask, .coherent_dma_mask = 0xffffffffUL, .platform_data = &s3c_hsmmc0_def_platdata, }, };
资源定义:
#define S3C_SZ_HSMMC (0x1000) static struct resource s3c_hsmmc_resource[] = { [0] = { .start = S3C_PA_HSMMC0, .end = S3C_PA_HSMMC0 + S3C_SZ_HSMMC - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_HSMMC0, .end = IRQ_HSMMC0, .flags = IORESOURCE_IRQ, } };
platform_data定义:
struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = { .max_width = 4, .host_caps = (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), .clk_type = S3C_SDHCI_CLK_DIV_INTERNAL, };
设备数据在平台初始化的过程中会做修改,文件mach-s3c64xx/mach-mini6410.c:
static void __init mini6410_machine_init(void) { // mmc platdata - remme s3c_sdhci0_set_platdata(&mini6410_hsmmc0_pdata); u32 cs1; struct mini6410_features_t features = { 0 }; printk(KERN_INFO "MINI6410: Option string mini6410=%s\n", mini6410_features_str); /* Parse the feature string */ mini6410_parse_features(&features, mini6410_features_str); mini6410_lcd_pdata.win[0] = &mini6410_fb_win[features.lcd_index]; printk(KERN_INFO "MINI6410: selected LCD display is %dx%d\n", mini6410_lcd_pdata.win[0]->win_mode.xres, mini6410_lcd_pdata.win[0]->win_mode.yres); s3c_nand_set_platdata(&mini6410_nand_info); s3c_fb_set_platdata(&mini6410_lcd_pdata); s3c24xx_ts_set_platdata(&s3c_ts_platform); /* configure nCS1 width to 16 bits */ cs1 = __raw_readl(S3C64XX_SROM_BW) & ~(S3C64XX_SROM_BW__CS_MASK << S3C64XX_SROM_BW__NCS1__SHIFT); cs1 |= ((1 << S3C64XX_SROM_BW__DATAWIDTH__SHIFT) | (1 << S3C64XX_SROM_BW__WAITENABLE__SHIFT) | (1 << S3C64XX_SROM_BW__BYTEENABLE__SHIFT)) << S3C64XX_SROM_BW__NCS1__SHIFT; __raw_writel(cs1, S3C64XX_SROM_BW); /* set timing for nCS1 suitable for ethernet chip */ __raw_writel((0 << S3C64XX_SROM_BCX__PMC__SHIFT) | (6 << S3C64XX_SROM_BCX__TACP__SHIFT) | (4 << S3C64XX_SROM_BCX__TCAH__SHIFT) | (1 << S3C64XX_SROM_BCX__TCOH__SHIFT) | (13 << S3C64XX_SROM_BCX__TACC__SHIFT) | (4 << S3C64XX_SROM_BCX__TCOS__SHIFT) | (0 << S3C64XX_SROM_BCX__TACS__SHIFT), S3C64XX_SROM_BC1); gpio_request(S3C64XX_GPF(15), "LCD power"); gpio_request(S3C64XX_GPE(0), "LCD power"); platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices)); }
函数第一行有一句设置平台数据,平台数据定义如下:
static struct s3c_sdhci_platdata mini6410_hsmmc0_pdata = { .max_width = 4, .cd_type = S3C_SDHCI_CD_NONE, };
由于该部分数据可能因为板子设计不一样而不相同,因此,三星并没有把该部分数据纳入到平台设备文件中,需要没个平台自己配置好参数,并调用设置平台数据函数进行设置。
这也是为什么新下载的内核配置好SD/MMC驱动后编译运行,SD卡驱动和平台设备均正常加载,但缺识别不到SD卡。就是因为默认情况下cd_type类型是不确定的,因此,卡无法被检测到,应该根据平台设置为相应的模式sd驱动程序才可以检测到。
至此,大致的流程全部分析完毕,在这个过程中我自己遇到的问题和需要注意的问题也用红色加粗很明确的标识了出来。
为了解决无法识别到SD卡的问题,下载编译了2.6.38、3.0.8、3.4的内核,并一一和官方比较,由于驱动加载都正常,导致整个查找过程可谓大海捞针,最终确定为平台启动文件中需要配置mmc设备数据。前后花了差不多2天时间,就等着搞定这个驱动了,下一步应该就可以将自己编译的安卓4.3启动了。等4.3成功启动了,下次再好好做个总结。
相关文章推荐
- 使用 jQuery Mobile 与 HTML5 开发 listView样式总结
- [Sciter系列] MFC下的Sciter–3.Sciter脚本与底层交互
- 服务器并发处理能力
- 关于短信猫MyMondem.SetThreadMode报错的解决方案
- 如何设计并使用FireMonkeyStyle
- python调用动态链接库传送protobuf数据
- 把win7装到内存上的虚拟硬盘,并且从虚拟硬盘启动
- 【C++】引用与变量
- 模版方法模式
- 从面向对象编程的角度解析c#中的事件处理机制
- C# 中事件
- acm常用技巧二 POJ 3061 POJ 3320 POJ 3276 尺取法
- 如何吸引用户打开自己发送的EDM邮件
- 解密中国互联网
- 宠物医院管理系统
- Spring源码阅读——获得bean
- java利用反射访问类的私有成员
- Sql server 事务的两种用法
- 搜索到的解决方法
- 轻松搞定面试中的二叉树题目