Loading Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt 0 → 100644 +22 −0 Original line number Diff line number Diff line * Qualcomm Connectivity SubSystem Platform Driver This platform driver adds support for the CNSS subsystem used for SDIO based Wi-Fi devices. It also adds support to manage two 1.8V voltage regulators and WLAN power enable 3.3V PMIC GPIO. The main purpose of this device tree entry below is to invoke the CNSS SDIO platform driver and provide handle to the WLAN power enable 3.3V pmic GPIO and two 1.8V PMIC voltage regulator resources. Required properties: - compatible: "qcom,cnss_sdio" - wlan-pmic-gpio: 3.3V PMIC GPIO for external power supply. - vdd-wlan-io-supply: phandle to the WLAN IO regulator device tree node. - vdd-wlan-xtal-supply: phandle to the WLAM XTAL regulator device tree node. Example: qcom,cnss-sdio { compatible = "qcom,cnss_sdio"; cnss_sdio,wlan-pmic-gpio = <&pm8019_gpios 3 0>; vdd-wlan-io-supply = <&mdmfermium_l11>; vdd-wlan-xtal-supply = <&mdmfermium_l2>; }; drivers/net/wireless/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -308,6 +308,17 @@ config CNSS This driver also adds support to integrate PCIe WLAN module to subsystem restart framework. config CNSS_SDIO tristate "Flag to enable platform driver for SIDO based wifi device" depends on MMC_SDHCI depends on MMC_SDHCI_MSM ---help--- This module specifies whether CNSS Platform Driver supports SDIO. This flag needs to be disabled if CNSS platform Driver need to be supported for other buses. This Flag is used by CLD Driver to use the SDIO GPL API's through CNSS Platform Driver. config CLD_LL_CORE tristate "Qualcomm core WLAN driver for QCA6174 chipset" select NL80211_TESTMODE Loading drivers/net/wireless/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -63,5 +63,6 @@ obj-$(CONFIG_RSI_91X) += rsi/ obj-$(CONFIG_WCNSS_CORE) += wcnss/ obj-$(CONFIG_CNSS_SDIO) += cnss/cnss_sdio.o obj-$(CONFIG_CNSS) += cnss/ obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/ drivers/net/wireless/cnss/cnss_sdio.c 0 → 100644 +250 −0 Original line number Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/delay.h> #include <linux/err.h> #define WLAN_VREG_IO_NAME "vdd-wlan-io" #define WLAN_VREG_XTAL_NAME "vdd-wlan-xtal" #define WLAN_VREG_IO_MAX 1800000 #define WLAN_VREG_IO_MIN 1800000 #define WLAN_VREG_XTAL_MAX 1800000 #define WLAN_VREG_XTAL_MIN 1800000 #define POWER_ON_DELAY 4 struct cnss_sdio_wlan_gpio_info { u32 num; u32 flags; }; static struct cnss_sdio_data { struct regulator *wlan_reg_io; struct regulator *wlan_reg_xtal; struct platform_device *pdev; struct cnss_sdio_wlan_gpio_info pmic_gpio; } *cnss_pdata; static int cnss_sdio_configure_gpio(void) { int error; struct device *dev = &cnss_pdata->pdev->dev; if (gpio_is_valid(cnss_pdata->pmic_gpio.num)) { error = gpio_request( cnss_pdata->pmic_gpio.num, "wlan_pmic_gpio"); if (error) { dev_err(dev, "PMIC gpio request failed\n"); return error; } error = gpio_direction_output(cnss_pdata->pmic_gpio.num, 0); if (error) { dev_err(dev, "PMIC gpio set direction failed\n"); goto err_pmic_gpio; } else { gpio_set_value_cansleep(cnss_pdata->pmic_gpio.num, 1); msleep(POWER_ON_DELAY); } } return 0; err_pmic_gpio: gpio_free(cnss_pdata->pmic_gpio.num); return error; } static int cnss_sdio_configure_regulator(void) { int error; struct device *dev = &cnss_pdata->pdev->dev; if (of_get_property( cnss_pdata->pdev->dev.of_node, WLAN_VREG_IO_NAME "-supply", NULL)) { cnss_pdata->wlan_reg_io = regulator_get( &cnss_pdata->pdev->dev, WLAN_VREG_IO_NAME); if (IS_ERR(cnss_pdata->wlan_reg_io)) { error = PTR_ERR(cnss_pdata->wlan_reg_io); dev_err(dev, "VDD-IO get failed error=%d\n", error); return error; } error = regulator_set_voltage( cnss_pdata->wlan_reg_io, WLAN_VREG_IO_MIN, WLAN_VREG_IO_MAX); if (error) { dev_err(dev, "VDD-IO set failed error=%d\n", error); goto err_vdd_io_regulator; } else { error = regulator_enable(cnss_pdata->wlan_reg_io); if (error) { dev_err(dev, "VDD-IO enable failed error=%d\n", error); goto err_vdd_io_regulator; } } } if (of_get_property( cnss_pdata->pdev->dev.of_node, WLAN_VREG_XTAL_NAME "-supply", NULL)) { cnss_pdata->wlan_reg_xtal = regulator_get( &cnss_pdata->pdev->dev, WLAN_VREG_XTAL_NAME); if (IS_ERR(cnss_pdata->wlan_reg_xtal)) { error = PTR_ERR(cnss_pdata->wlan_reg_xtal); dev_err(dev, "VDD-XTAL get failed error=%d\n", error); goto err_vdd_xtal_regulator; } error = regulator_set_voltage( cnss_pdata->wlan_reg_xtal, WLAN_VREG_XTAL_MIN, WLAN_VREG_XTAL_MAX); if (error) { dev_err(dev, "VDD-XTAL set failed error=%d\n", error); goto err_vdd_xtal_regulator; } else { error = regulator_enable(cnss_pdata->wlan_reg_xtal); if (error) { dev_err(dev, "VDD-XTAL enable failed err=%d\n", error); goto err_vdd_xtal_regulator; } } } return 0; err_vdd_xtal_regulator: regulator_put(cnss_pdata->wlan_reg_xtal); err_vdd_io_regulator: regulator_put(cnss_pdata->wlan_reg_io); return error; } static int cnss_sdio_get_resource(struct device *dev) { struct device_node *np = dev->of_node; /* wlan enable pmic gpio info */ cnss_pdata->pmic_gpio.num = of_get_named_gpio_flags(np, "cnss_sdio,wlan-pmic-gpio", 0, &cnss_pdata->pmic_gpio.flags); if (cnss_pdata->pmic_gpio.num < 0) return cnss_pdata->pmic_gpio.num; return 0; } static void cnss_sdio_release_regulator(void) { if (cnss_pdata->wlan_reg_xtal) regulator_put(cnss_pdata->wlan_reg_xtal); if (cnss_pdata->wlan_reg_io) regulator_put(cnss_pdata->wlan_reg_io); } static void cnss_sdio_release_resourse(void) { if (gpio_is_valid(cnss_pdata->pmic_gpio.num)) { gpio_set_value_cansleep(cnss_pdata->pmic_gpio.num, 0); gpio_free(cnss_pdata->pmic_gpio.num); } cnss_sdio_release_regulator(); } static int cnss_sdio_probe(struct platform_device *pdev) { int error; if (pdev->dev.of_node) { cnss_pdata = devm_kzalloc( &pdev->dev, sizeof(*cnss_pdata), GFP_KERNEL); if (!cnss_pdata) return -ENOMEM; error = cnss_sdio_get_resource(&pdev->dev); if (error) return error; } else { cnss_pdata = pdev->dev.platform_data; } if (!cnss_pdata) return -EINVAL; cnss_pdata->pdev = pdev; error = cnss_sdio_configure_regulator(); if (error) { dev_err(&pdev->dev, "Failed to config voltage regulator\n"); return error; } error = cnss_sdio_configure_gpio(); if (error) { dev_err(&pdev->dev, "Failed to config gpio\n"); goto err_gpio_config; } dev_dbg(&pdev->dev, "CNSS SDIO Driver registered"); return 0; err_gpio_config: cnss_sdio_release_regulator(); return error; } static int cnss_sdio_remove(struct platform_device *pdev) { cnss_sdio_release_resourse(); return 0; } static const struct of_device_id cnss_sdio_dt_match[] = { {.compatible = "qcom,cnss_sdio"}, {} }; MODULE_DEVICE_TABLE(of, cnss_sdio_dt_match); static struct platform_driver cnss_sdio_driver = { .probe = cnss_sdio_probe, .remove = cnss_sdio_remove, .driver = { .name = "cnss_sdio", .owner = THIS_MODULE, .of_match_table = cnss_sdio_dt_match, }, }; static int __init cnss_sdio_init(void) { return platform_driver_register(&cnss_sdio_driver); } static void __exit cnss_sdio_exit(void) { platform_driver_unregister(&cnss_sdio_driver); } module_init(cnss_sdio_init); module_exit(cnss_sdio_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION(DEVICE "CNSS SDIO Driver"); Loading
Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt 0 → 100644 +22 −0 Original line number Diff line number Diff line * Qualcomm Connectivity SubSystem Platform Driver This platform driver adds support for the CNSS subsystem used for SDIO based Wi-Fi devices. It also adds support to manage two 1.8V voltage regulators and WLAN power enable 3.3V PMIC GPIO. The main purpose of this device tree entry below is to invoke the CNSS SDIO platform driver and provide handle to the WLAN power enable 3.3V pmic GPIO and two 1.8V PMIC voltage regulator resources. Required properties: - compatible: "qcom,cnss_sdio" - wlan-pmic-gpio: 3.3V PMIC GPIO for external power supply. - vdd-wlan-io-supply: phandle to the WLAN IO regulator device tree node. - vdd-wlan-xtal-supply: phandle to the WLAM XTAL regulator device tree node. Example: qcom,cnss-sdio { compatible = "qcom,cnss_sdio"; cnss_sdio,wlan-pmic-gpio = <&pm8019_gpios 3 0>; vdd-wlan-io-supply = <&mdmfermium_l11>; vdd-wlan-xtal-supply = <&mdmfermium_l2>; };
drivers/net/wireless/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -308,6 +308,17 @@ config CNSS This driver also adds support to integrate PCIe WLAN module to subsystem restart framework. config CNSS_SDIO tristate "Flag to enable platform driver for SIDO based wifi device" depends on MMC_SDHCI depends on MMC_SDHCI_MSM ---help--- This module specifies whether CNSS Platform Driver supports SDIO. This flag needs to be disabled if CNSS platform Driver need to be supported for other buses. This Flag is used by CLD Driver to use the SDIO GPL API's through CNSS Platform Driver. config CLD_LL_CORE tristate "Qualcomm core WLAN driver for QCA6174 chipset" select NL80211_TESTMODE Loading
drivers/net/wireless/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -63,5 +63,6 @@ obj-$(CONFIG_RSI_91X) += rsi/ obj-$(CONFIG_WCNSS_CORE) += wcnss/ obj-$(CONFIG_CNSS_SDIO) += cnss/cnss_sdio.o obj-$(CONFIG_CNSS) += cnss/ obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/
drivers/net/wireless/cnss/cnss_sdio.c 0 → 100644 +250 −0 Original line number Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/delay.h> #include <linux/err.h> #define WLAN_VREG_IO_NAME "vdd-wlan-io" #define WLAN_VREG_XTAL_NAME "vdd-wlan-xtal" #define WLAN_VREG_IO_MAX 1800000 #define WLAN_VREG_IO_MIN 1800000 #define WLAN_VREG_XTAL_MAX 1800000 #define WLAN_VREG_XTAL_MIN 1800000 #define POWER_ON_DELAY 4 struct cnss_sdio_wlan_gpio_info { u32 num; u32 flags; }; static struct cnss_sdio_data { struct regulator *wlan_reg_io; struct regulator *wlan_reg_xtal; struct platform_device *pdev; struct cnss_sdio_wlan_gpio_info pmic_gpio; } *cnss_pdata; static int cnss_sdio_configure_gpio(void) { int error; struct device *dev = &cnss_pdata->pdev->dev; if (gpio_is_valid(cnss_pdata->pmic_gpio.num)) { error = gpio_request( cnss_pdata->pmic_gpio.num, "wlan_pmic_gpio"); if (error) { dev_err(dev, "PMIC gpio request failed\n"); return error; } error = gpio_direction_output(cnss_pdata->pmic_gpio.num, 0); if (error) { dev_err(dev, "PMIC gpio set direction failed\n"); goto err_pmic_gpio; } else { gpio_set_value_cansleep(cnss_pdata->pmic_gpio.num, 1); msleep(POWER_ON_DELAY); } } return 0; err_pmic_gpio: gpio_free(cnss_pdata->pmic_gpio.num); return error; } static int cnss_sdio_configure_regulator(void) { int error; struct device *dev = &cnss_pdata->pdev->dev; if (of_get_property( cnss_pdata->pdev->dev.of_node, WLAN_VREG_IO_NAME "-supply", NULL)) { cnss_pdata->wlan_reg_io = regulator_get( &cnss_pdata->pdev->dev, WLAN_VREG_IO_NAME); if (IS_ERR(cnss_pdata->wlan_reg_io)) { error = PTR_ERR(cnss_pdata->wlan_reg_io); dev_err(dev, "VDD-IO get failed error=%d\n", error); return error; } error = regulator_set_voltage( cnss_pdata->wlan_reg_io, WLAN_VREG_IO_MIN, WLAN_VREG_IO_MAX); if (error) { dev_err(dev, "VDD-IO set failed error=%d\n", error); goto err_vdd_io_regulator; } else { error = regulator_enable(cnss_pdata->wlan_reg_io); if (error) { dev_err(dev, "VDD-IO enable failed error=%d\n", error); goto err_vdd_io_regulator; } } } if (of_get_property( cnss_pdata->pdev->dev.of_node, WLAN_VREG_XTAL_NAME "-supply", NULL)) { cnss_pdata->wlan_reg_xtal = regulator_get( &cnss_pdata->pdev->dev, WLAN_VREG_XTAL_NAME); if (IS_ERR(cnss_pdata->wlan_reg_xtal)) { error = PTR_ERR(cnss_pdata->wlan_reg_xtal); dev_err(dev, "VDD-XTAL get failed error=%d\n", error); goto err_vdd_xtal_regulator; } error = regulator_set_voltage( cnss_pdata->wlan_reg_xtal, WLAN_VREG_XTAL_MIN, WLAN_VREG_XTAL_MAX); if (error) { dev_err(dev, "VDD-XTAL set failed error=%d\n", error); goto err_vdd_xtal_regulator; } else { error = regulator_enable(cnss_pdata->wlan_reg_xtal); if (error) { dev_err(dev, "VDD-XTAL enable failed err=%d\n", error); goto err_vdd_xtal_regulator; } } } return 0; err_vdd_xtal_regulator: regulator_put(cnss_pdata->wlan_reg_xtal); err_vdd_io_regulator: regulator_put(cnss_pdata->wlan_reg_io); return error; } static int cnss_sdio_get_resource(struct device *dev) { struct device_node *np = dev->of_node; /* wlan enable pmic gpio info */ cnss_pdata->pmic_gpio.num = of_get_named_gpio_flags(np, "cnss_sdio,wlan-pmic-gpio", 0, &cnss_pdata->pmic_gpio.flags); if (cnss_pdata->pmic_gpio.num < 0) return cnss_pdata->pmic_gpio.num; return 0; } static void cnss_sdio_release_regulator(void) { if (cnss_pdata->wlan_reg_xtal) regulator_put(cnss_pdata->wlan_reg_xtal); if (cnss_pdata->wlan_reg_io) regulator_put(cnss_pdata->wlan_reg_io); } static void cnss_sdio_release_resourse(void) { if (gpio_is_valid(cnss_pdata->pmic_gpio.num)) { gpio_set_value_cansleep(cnss_pdata->pmic_gpio.num, 0); gpio_free(cnss_pdata->pmic_gpio.num); } cnss_sdio_release_regulator(); } static int cnss_sdio_probe(struct platform_device *pdev) { int error; if (pdev->dev.of_node) { cnss_pdata = devm_kzalloc( &pdev->dev, sizeof(*cnss_pdata), GFP_KERNEL); if (!cnss_pdata) return -ENOMEM; error = cnss_sdio_get_resource(&pdev->dev); if (error) return error; } else { cnss_pdata = pdev->dev.platform_data; } if (!cnss_pdata) return -EINVAL; cnss_pdata->pdev = pdev; error = cnss_sdio_configure_regulator(); if (error) { dev_err(&pdev->dev, "Failed to config voltage regulator\n"); return error; } error = cnss_sdio_configure_gpio(); if (error) { dev_err(&pdev->dev, "Failed to config gpio\n"); goto err_gpio_config; } dev_dbg(&pdev->dev, "CNSS SDIO Driver registered"); return 0; err_gpio_config: cnss_sdio_release_regulator(); return error; } static int cnss_sdio_remove(struct platform_device *pdev) { cnss_sdio_release_resourse(); return 0; } static const struct of_device_id cnss_sdio_dt_match[] = { {.compatible = "qcom,cnss_sdio"}, {} }; MODULE_DEVICE_TABLE(of, cnss_sdio_dt_match); static struct platform_driver cnss_sdio_driver = { .probe = cnss_sdio_probe, .remove = cnss_sdio_remove, .driver = { .name = "cnss_sdio", .owner = THIS_MODULE, .of_match_table = cnss_sdio_dt_match, }, }; static int __init cnss_sdio_init(void) { return platform_driver_register(&cnss_sdio_driver); } static void __exit cnss_sdio_exit(void) { platform_driver_unregister(&cnss_sdio_driver); } module_init(cnss_sdio_init); module_exit(cnss_sdio_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION(DEVICE "CNSS SDIO Driver");