SDIO Wi-Fi 配置
Wi-Fi/BT硬件管脚的配置主要有以下几点:
切记一定要对照原理图进行配置,且确保使用的dts/dtsi里面包含以下节点!
SDIO 接口Wi-Fi:WL_REG_ON由sdio_pwrseq节点进行管理控制,不需要在wireless-wlan节点下面重复
添加WIFI,poweren_gpio配置;
/* SDIO接口Wi-Fi专用配置: WIFI_REG_ON: Wi-Fi的电源使能PIN脚 */
sdio_pwrseq: sdio-pwrseq
{
compatible = "mmc-pwrseq-simple";
pinctrl-names = "default";
pinctrl-0 = <&wifi_enable_h>;
/* 特别注意:WIFI_REG_ON GPIO_ACTIVE 配置跟使能状态恰好是相反的,
* 高有效为LOW,低有效则为HIGH
* 切记:这个配置跟下面的WIFI,poweren_gpio是互斥的,不能同时配置!!!
*/
reset-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>;
};
/* SDIO接口Wi-Fi专用配置:WIFI_REG_ON脚的pinctrl的配置 */
&pinctrl {
sdio-pwrseq {
wifi_enable_h: wifi-enable-h {
rockchip,pins =
/* 对应上面的WIFI_REG_ON,关掉上下拉,防止不能拉高或拉低 */
<0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
/* SDIO接口Wi-Fi专用配置:SDIO接口节点 */
&sdio {
max-frequency = <150000000>; /* sdio接口的{BANNED}最佳大频率,可调整 */
bus-width = <4>; /* 4线模式,可调整1线模式 */
sd-uhs-sdr104; /* 支持SDIO3.0 */
… …
status = "okay";
};
/* Wi-Fi节点 */
wireless-wlan {
compatible = "wlan-platdata";
rockchip,grf = <&grf>;
/* 注意:如果排查发现Wi-Fi模块没有32.768K波形,且硬件上是有
* RK PMU供给的,则打开下面的clock属性,按照实际使用的PMU型号填写,
* 否在SDIO/Wi-Fi无法使用.
*/
clocks = <&rk809 1>; //如果使用RK809,只能配置一个
clocks = <&hym8563>; //如果使用hym8563,只能配置一个
clock-names = "ext_clock";
/* 按实际名字填写 */
wifi_chip_type = "ap6255";
/* WIFI_WAKE_HOST: Wi-Fi中断通知主控的PIN脚。
* 特别注意:确认下这个Wi-Fi pin脚跟主控的pin的
* 硬件连接关系,直连的话就是GPIO_ACTIVE_HIGH;
* 如果中间加了一个反向管就要改成低电平GPIO_ACTIVE_LOW触发
*/
WIFI,host_wake_irq = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
//SDIO Wi-Fi 无需此配置,除非有动态加载协助驱动ko的需求,参考11.8章节
//WIFI,poweren_gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>;
status = "okay";
};
/* WIFI_WAKE_HOST脚的pinctrl的配置 */
wireless-wlan {
/omit-if-no-ref/
wifi_wake_host: wifi-wake-host {
/* 注意一般Wi-Fi的wake host pin都是高电平触发,
* 所以默认这里要配置为下拉; 如果客户的硬件设计
* 是反向的则要改为上拉,总之要初始化为与触发电平
* 相反的状态
*/
rockchip,pins = <0 RK_PA0 0 &pcfg_pull_down>;
};
}
rk3326的SDIO控制器驱动
drivers/mmc/host/dw_mmc-rockchip.c
ap6265驱动
drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd
drivers\mmc\host\dw_mmc-rockchip.c
dw_mci_rockchip_probe(struct platform_device *pdev)
--> dw_mci_pltfm_register(pdev, drv_data); //解析设备树信息,用来构造一个dw_mci *host;
--> dw_mci_probe(dw_mci *host); //将上面构造好的dw_mci *host结构体传参进来;
--> setup_timer(&host->cmd11_timer,dw_mci_cmd11_timer, (unsigned long)host); //设置一些定时器,定时发送一些命令
--> host->dma_ops = host->pdata->dma_ops;
--> ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
//这个dw_mci_interrupt中断函数会被注册两次,一次是emmc(irq=25),一次是sd卡(irq=26),所以源码虽然只注册了一次,但是实际上注册了两次,用的都是同一个中断处理函数
//这个是mci寄存器的中断,我们的这款soc可以根据这个寄存器来判断发送,接收,SD卡热拔插的行为,都会触发这个中断,在这个中断处理函数里面再细分是什么操作触发的中断,进行处理,比如热拔插:
--> dw_mci_init_slot(dw_mci *host, i); // 定义了mmc_host *mmc;,用我们封装了一层的dw_mci *host去填充注册他
--> mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
--> INIT_DELAYED_WORK(&host->detect, mmc_rescan); //初始化工作队列,里面有扫描SD卡的函数
--> mmc->ops = &dw_mci_ops; //mmc_host_ops类型的变量,用于定义本mmc_host的操作接口(包括与mmc card通信的接口request、卡检测相关的接口等)
--> mmc_add_host(mmc);
--> mmc_start_host(mmc_host *host);
--> mmc_schedule_delayed_work(&host->detect, delay); //上电添加mmc_host的时候,先主动执行一次mmc_rescan函数
--> dw_mci_enable_cd(host); //cd = card detect ≠ sdmmc_cd 引脚中断,主要看函数内容,像这款soc就是在里面开启mci寄存器的SD卡热拔插中断
--> temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; mci_writel(host, INTMASK, temp); //开启mci寄存器的SD卡热拔插中断
dw_mci_interrupt()
if (pending & SDMMC_INT_CD)
dw_mci_handle_cd(dw_mci *host);
--> mmc_detect_change(slot->mmc,msecs_to_jiffies(host->pdata->detect_delay_ms));
--> _mmc_detect_change(host, delay, true);
--> mmc_schedule_delayed_work(&host->detect, delay); //开启工作队列
--> mmc_rescan() //{BANNED}最佳终会调用此函数
重点看看mmc_rescan检测SD卡热拔插的函数,我们在设备树中的SD卡定义了 supports-sd 所以:
drivers\mmc\core\host.c
mmc_of_parse
if (of_property_read_bool(np, "supports-sd"))
host->restrict_caps |= RESTRICT_CARD_TYPE_SD;
mmc_rescan()
if (host->bus_ops && !host->bus_dead && !(host->caps & MMC_CAP_NONREMOVABLE)) //上电{BANNED}中国第一次调用这个函数时候,还没有设置host->bus_ops,所以会执行后面的语句来设置host->bus_ops
host->bus_ops->detect(host);
--> mmc_rescan_try_freq(host, max(freqs[i], host->f_min)) //以各种频率给host发,选择{BANNED}最佳合适的频率
if ((host->restrict_caps & RESTRICT_CARD_TYPE_SD) && !mmc_attach_sd(host)) return 0; 所以就会调用mmc_attach_sd
--> mmc_attach_sd(host)
--> mmc_attach_bus(host, &mmc_sd_ops);
--> host->bus_ops = ops; //这里的ops就是上面的mmc_ops参数
--> mmc_sd_init_card(host, rocr, NULL);
--> card = mmc_alloc_card(host, &sd_type);
--> mmc_add_card(host->card);
--> device_add(&card->dev);
--> bus_add_device(dev); //Add the device to its bus's list of devices, 会给mmc_bus注册一个devices,会和系统自动注册的mmc_driver匹配,调用它的probe函数
......
--> mmc_driver->probe
--> mmc_blk_probe() //如上分析的,会读取SD卡的寄存器获取分区信息,根据分区信息注册block等。
Mmc card:
①host完成mmc_card与mmc_host的绑定;
②dev完成将该mmc_card与系统中的设备、总线、驱动的关联(即完成与系统设备驱动总线的关联与绑定操作);
③卡类型与卡状态记录
④卡相关的寄存器信息记录(主要从mmc card中读取),包括cid、csd、scr、ssr等内容
⑤Mmc card的分区信息,主要完成块设备的创建(由mmc driver实现)若为sdio设备,则有cccr、cis等(关于sdio的部分,请参考sdio协议相关的文档)
mmc_ios:
该结构体主要定义mmc总线相关的参数,主要包括时钟频率、电源、总线模式、电源状态、总线带宽、支持的信号电压值等等这些参数的设置可通过mmc_host_ops->set_ios实现。
mmc_host_ops:
该数据结构定义了mmc_host的操作方法
mmc_bus_ops:
该接口主要是针对mmc card的sleep/awake的操作,因mmc card也需要进行sleep/awake等接口,这些接口类似于设备驱动的电源管理(suspend/resume)相关的接口。该结构体中的成员,不需要我们进行实现,mmc子系统已经完成,mmc子系统根据mmc各协议版本针对sleep/awake的支持情况,实现对应的操作。
wifi调试
查看rk3326的sdio属性
cat /sys/kernel/debug/mmc2/ios
阅读(1253) | 评论(0) | 转发(0) |