Platform: RK3399
OS: Android 7.1
Kernel: v4.4.83
框架:
引用网友一张框图,画得很不错
从硬件角度来看:
CPU -> SDIO -> AP6356S
从软件角度来看分:
电源/IO管理
SDIO通道(Host和Client)
WiFi驱动
电源/GPIO管理模块:
主要完成对电源、GPIO的初始化和控制等。
配置是在DTS中
rk3399-mid-818-android.dts:
wireless-wlan {
compatible = "wlan-platdata";
rockchip,grf = <&grf>;
wifi_chip_type = "ap6354";
sdio_vref = <1800>;
WIFI,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>;
status = "okay";
};
sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple";
clocks = <&rk818 1>;
clock-names = "ext_clock";
pinctrl-names = "default";
pinctrl-0 = <&wifi_enable_h>;
reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
};
&sdio0 {
cap-sdio-irq;
mmc-pwrseq = <&sdio_pwrseq>;
};
解析配置的驱动在rfkill-wlan.c
static struct of_device_id wlan_platdata_of_match[] = {
{ .compatible = "wlan-platdata" },
{ }
};
static int wlan_platdata_parse_dt(struct device *dev,
struct rksdmmc_gpio_wifi_moudle *data)
{
syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
of_property_read_string(node, "wifi_chip_type", &strings);
of_find_property(node, "keep_wifi_power_on", NULL)
of_find_property(node, "power_ctrl_by_pmu", NULL)
of_get_named_gpio_flags(node, "WIFI,poweren_gpio", 0, &flags);
of_get_named_gpio_flags(node, "WIFI,reset_gpio", 0, &flags);
of_get_named_gpio_flags(node, "WIFI,host_wake_irq", 0, &flags);
}
SDIO Host:
对应的是对CPU上的SDIO Host Controller的控制实现,不管是哪个平台,最终都是通过mmc_add_host()添加mmc框架的core层中去。
host驱动位于kernel/drivers/mmc/host目录,使用的是Synopsys DW(DesignWare)的ip核。
kris@eco:~/rk3399/kernel/drivers/mmc/host$ ls *.o
built-in.o dw_mmc-pltfm.o rk_sdmmc_ops.o sdhci-of-arasan.o
dw_mmc.o dw_mmc-rockchip.o sdhci.o sdhci-pltfm.o
dw_mmc.c:
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
.pre_req = dw_mci_pre_req,
.post_req = dw_mci_post_req,
.set_ios = dw_mci_set_ios,
.set_sdio_status = dw_mci_set_sdio_status,
.get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq,
.execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
.init_card = dw_mci_init_card,
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
}
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
{
mmc->ops = &dw_mci_ops
dw_mci_slot_of_parse(slot)
mmc_of_parse(mmc)
mmc_add_host(mmc)
}
解析的配置位于sdio0节点中
rk3399-mid-818-android.dts
//板级配置
&sdio0 {
clock-frequency = <150000000>;
clock-freq-min-max = <200000 150000000>;
supports-sdio;
bus-width = <4>;
disable-wp;
cap-sd-highspeed;
cap-sdio-irq;
keep-power-in-suspend;
mmc-pwrseq = <&sdio_pwrseq>;
non-removable;
num-slots = <1>;
pinctrl-names = "default";
pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;
sd-uhs-sdr104;
status = "okay";
};
还有一部分配置在rk3399.dtsi中
//平台配置
sdio0: dwmmc@fe310000 {
compatible = "rockchip,rk3399-dw-mshc",
"rockchip,rk3288-dw-mshc";
reg = <0x0 0xfe310000 0x0 0x4000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH 0>;
clock-freq-min-max = <400000 150000000>;
clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
<&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
fifo-depth = <0x100>;
power-domains = <&power RK3399_PD_SDIOAUDIO>;
status = "disabled";
};
SDIO Client:
对应的是对WiFi模组内部的SDIO控制器的控制实现,因此此模块代码也是集成于WiFi驱动中。
AP6356S用的是broadcom模组,驱动位于kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd目录下
SDIO注册调用流程如下:
rockchip_wifi_init_module_rkwifi -> dhd_linux.c
wifi_init_thread ->
dhd_module_init ->
dhd_wifi_platform_register_drv ->
dhd_wifi_platform_load ->
dhd_wifi_platform_load_sdio ->
dhd_bus_register ->
bcmsdh_register ->
bcmsdh_register_client_driver ->
sdio_register_driver
对应sdio_driver:
static struct sdio_driver bcmsdh_sdmmc_driver = {
.probe = bcmsdh_sdmmc_probe,
.remove = bcmsdh_sdmmc_remove,
.name = "bcmsdh_sdmmc",
.id_table = bcmsdh_sdmmc_ids,
}
SDIO Client通过SDIO Host去控制WiFi模块。
WiFi驱动因为内容过多,后面再分析了。
参考:
WiFi驱动(1)框架解析