Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a134de4c authored by Sarada Prasanna Garnayak's avatar Sarada Prasanna Garnayak
Browse files

cnss: add support for power up sequence in CNSS SDIO



To power up the Wi-Fi card GPIO and voltage regulator
need to be configured properly with a certain sequence,
so configure GPIO and enable voltage regulators to enable
the power up support in CNSS SDIO platform driver.

CRs-Fixed: 920931
Change-Id: I41ced578f0aec1339a9f626352d4225ed901bb76
Signed-off-by: default avatarSarada Prasanna Garnayak <sgarna@codeaurora.org>
parent d5b845bf
Loading
Loading
Loading
Loading
+12 −3
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. The main purpose of this device tree entry below
is to invoke the CNSS SDIO platform driver.
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>;
	};
+191 −0
Original line number Diff line number Diff line
@@ -15,14 +15,205 @@
#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;
}