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

Commit 6bba6094 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss: Add support to configure voltage regulator"

parents 4c38e617 df89a84b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -12,6 +12,10 @@ Required properties:
  - reg-names: Names of the memory regions defined in reg entry
  - interrupts: Copy engine interrupt table
  - qcom,wlan-msa-memory: MSA memory size
  - <supply-name>-supply: phandle to the regulator device tree node
                          Required "supply-name" is "vdd-io".
  - qcom,<supply>-voltage-level - specifies voltage levels for supply. Should be
                                  specified in pairs (min, max), units uV.

Optional properties:
  - qcom,skip-qmi: Boolean property to decide whether to use QMI or not
@@ -37,4 +41,6 @@ Example:
		   <0 141 0 /* CE11 */ >;
        qcom,wlan-msa-memory = <0x200000>;
        qcom,skip-qmi;
	vdd-io-supply = <&pmcobalt_l5>;
	qcom,vdd-io-voltage-level = <800000 800000>;
    };
+2 −0
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@
			     <0 424 0 /* CE10 */ >,
			     <0 425 0 /* CE11 */ >;
		qcom,wlan-msa-memory = <0x100000>;
		vdd-io-supply = <&pmcobalt_l5>;
		qcom,vdd-io-voltage-level = <800000 800000>;
	};

	msm_cpufreq: qcom,msm-cpufreq {
+262 −21
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/dma-mapping.h>
@@ -44,11 +45,14 @@ struct icnss_qmi_event {
#define WLFW_SERVICE_INS_ID_V01		0
#define ICNSS_WLFW_QMI_CONNECTED	BIT(0)
#define ICNSS_FW_READY			BIT(1)
#define MAX_PROP_SIZE			32
#define MAX_VOLTAGE_LEVEL		2
#define VREG_ON				1
#define VREG_OFF			0

#define ICNSS_IS_WLFW_QMI_CONNECTED(_state) \
		((_state) & ICNSS_WLFW_QMI_CONNECTED)
#define ICNSS_IS_FW_READY(_state) ((_state) & ICNSS_FW_READY)

#ifdef ICNSS_PANIC
#define ICNSS_ASSERT(_condition) do {			\
		if (!(_condition)) {				\
@@ -72,10 +76,19 @@ struct ce_irq_list {
	irqreturn_t (*handler)(int, void *);
};

struct icnss_vreg_info {
	struct regulator *reg;
	const char *name;
	u32 nominal_min;
	u32 max_voltage;
	bool state;
};

static struct {
	struct platform_device *pdev;
	struct icnss_driver_ops *ops;
	struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
	struct icnss_vreg_info vreg_info;
	u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
	phys_addr_t mem_base_pa;
	void __iomem *mem_base_va;
@@ -161,19 +174,121 @@ out:
	return ret;
}

static int icnss_vreg_on(struct icnss_vreg_info *vreg_info)
{
	int ret = 0;

	if (!vreg_info->reg) {
		pr_err("%s: regulator is not initialized\n", __func__);
		return -ENOENT;
	}

	if (!vreg_info->max_voltage || !vreg_info->nominal_min) {
		pr_err("%s: %s invalid constraints specified\n",
			__func__, vreg_info->name);
		return -EINVAL;
	}

	ret = regulator_set_voltage(vreg_info->reg,
			vreg_info->nominal_min, vreg_info->max_voltage);
	if (ret < 0) {
		pr_err("%s: regulator_set_voltage failed for (%s). min_uV=%d,max_uV=%d,ret=%d\n",
			__func__, vreg_info->name,
			vreg_info->nominal_min,
			vreg_info->max_voltage, ret);
		return ret;
	}

	ret = regulator_enable(vreg_info->reg);
	if (ret < 0) {
		pr_err("%s: Fail to enable regulator (%s) ret=%d\n",
			__func__, vreg_info->name, ret);
	}
	return ret;
}

static int icnss_vreg_off(struct icnss_vreg_info *vreg_info)
{
	int ret = 0;
	int min_uV = 0;

	if (!vreg_info->reg) {
		pr_err("%s: regulator is not initialized\n", __func__);
		return -ENOENT;
	}

	ret = regulator_disable(vreg_info->reg);
	if (ret < 0) {
		pr_err("%s: Fail to disable regulator (%s) ret=%d\n",
			__func__, vreg_info->name, ret);
		return ret;
	}

	ret = regulator_set_voltage(vreg_info->reg,
				    min_uV, vreg_info->max_voltage);
	if (ret < 0) {
		pr_err("%s: regulator_set_voltage failed for (%s). min_uV=%d,max_uV=%d,ret=%d\n",
			__func__, vreg_info->name, min_uV,
			vreg_info->max_voltage, ret);
	}
	return ret;
}

static int icnss_vreg_set(bool state)
{
	int ret = 0;
	struct icnss_vreg_info *vreg_info = &penv->vreg_info;

	if (vreg_info->state == state) {
		pr_debug("Already %s state is %s\n", vreg_info->name,
			state ? "enabled" : "disabled");
		return ret;
	}

	if (state)
		ret = icnss_vreg_on(vreg_info);
	else
		ret = icnss_vreg_off(vreg_info);

	if (ret < 0)
		goto out;

	pr_debug("%s: %s is now %s\n", __func__, vreg_info->name,
			state ? "enabled" : "disabled");

	vreg_info->state = state;
out:
	return ret;
}

static int icnss_adrastea_power_on(void)
{
	int ret = 0;

	ret = icnss_vreg_set(VREG_ON);
	if (ret < 0) {
		pr_err("%s: Failed to turn on voltagre regulator: %d\n",
		       __func__, ret);
		goto out;
	}
	/* TZ API of power on adrastea */

out:
	return ret;
}

void icnss_adrastea_power_off(void)
static int icnss_adrastea_power_off(void)
{
	int ret = 0;

	ret = icnss_vreg_set(VREG_OFF);
	if (ret < 0) {
		pr_err("%s: Failed to turn off voltagre regulator: %d\n",
		       __func__, ret);
		goto out;
	}
	/* TZ API of power off adrastea */
out:
	return ret;
}

static int wlfw_msa_mem_info_send_sync_msg(void)
@@ -629,7 +744,7 @@ static int icnss_qmi_event_server_arrive(void *data)
	if (ret < 0) {
		pr_err("%s: Failed to send indication message: %d\n",
		       __func__, ret);
		goto fail;
		goto err_power_on;
	}

	if (penv->msa_va) {
@@ -637,27 +752,33 @@ static int icnss_qmi_event_server_arrive(void *data)
		if (ret < 0) {
			pr_err("%s: Failed to send MSA info: %d\n",
			       __func__, ret);
			goto fail;
			goto err_power_on;
		}
		ret = wlfw_msa_ready_send_sync_msg();
		if (ret < 0) {
			pr_err("%s: Failed to send MSA ready : %d\n",
			       __func__, ret);
			goto fail;
		}
			goto err_power_on;
	} else {
		pr_err("%s: Invalid MSA address\n", __func__);
		ret = -EINVAL;
		goto fail;
		goto err_power_on;
	}

	ret = wlfw_cap_send_sync_msg();
	if (ret < 0) {
		pr_err("%s: Failed to get capability: %d\n",
		       __func__, ret);
		goto fail;
		goto err_power_on;
	}
	return ret;

err_power_on:
	ret = icnss_vreg_set(VREG_OFF);
	if (ret < 0) {
		pr_err("%s: Failed to turn off voltagre regulator: %d\n",
		       __func__, ret);
	}
fail:
	qmi_handle_destroy(penv->wlfw_clnt);
	penv->wlfw_clnt = NULL;
@@ -696,7 +817,12 @@ static int icnss_qmi_event_fw_ready_ind(void *data)
		goto out;
	}

	icnss_adrastea_power_off();
	ret = icnss_adrastea_power_off();
	if (ret < 0) {
		pr_err("%s: Failed to power off hardware: %d\n",
		       __func__, ret);
		goto out;
	}

	if (!penv->ops || !penv->ops->probe) {
		pr_err("%s: WLAN driver is not registed yet\n", __func__);
@@ -803,12 +929,17 @@ int icnss_register_driver(struct icnss_driver_ops *ops)

	/* check for all conditions before invoking probe */
	if (ICNSS_IS_FW_READY(penv->state) && penv->ops->probe) {
		ret = icnss_vreg_set(VREG_ON);
		if (ret < 0) {
			pr_err("%s: Failed to turn on voltagre regulator: %d\n",
				__func__, ret);
			goto out;
		}
		ret = penv->ops->probe(&pdev->dev);
	} else {
		pr_err("icnss: FW is not ready\n");
		ret = -ENOENT;
	}

out:
	return ret;
}
@@ -838,6 +969,11 @@ int icnss_unregister_driver(struct icnss_driver_ops *ops)
		penv->ops->remove(&pdev->dev);

	penv->ops = NULL;

	ret = icnss_vreg_set(VREG_OFF);
	if (ret < 0)
		pr_err("%s: Failed to turn off voltagre regulator: %d\n",
		       __func__, ret);
out:
	return ret;
}
@@ -1150,6 +1286,79 @@ static ssize_t icnss_wlan_mode_store(struct device *dev,

static DEVICE_ATTR(icnss_wlan_mode, S_IWUSR, NULL, icnss_wlan_mode_store);

static int icnss_dt_parse_vreg_info(struct device *dev,
				struct icnss_vreg_info *vreg_info,
				const char *vreg_name)
{
	int ret = 0;
	u32 voltage_levels[MAX_VOLTAGE_LEVEL]
	char prop_name[MAX_PROP_SIZE];
	struct device_node *np = dev->of_node;

	snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
	if (!of_parse_phandle(np, prop_name, 0)) {
		pr_err("%s: No vreg data found for %s\n", __func__, vreg_name);
		ret = -EINVAL;
		return ret;
	}

	vreg_info->name = vreg_name;

	snprintf(prop_name, MAX_PROP_SIZE,
		"qcom,%s-voltage-level", vreg_name);
	ret = of_property_read_u32_array(np, prop_name, voltage_levels,
					ARRAY_SIZE(voltage_levels));
	if (ret) {
		pr_err("%s: error reading %s property\n", __func__, prop_name);
		return ret;
	}

	vreg_info->nominal_min = voltage_levels[0];
	vreg_info->max_voltage = voltage_levels[1];

	return ret;
}

static int icnss_get_resources(struct device *dev)
{
	int ret = 0;
	struct icnss_vreg_info *vreg_info;

	vreg_info = &penv->vreg_info;
	if (vreg_info->reg) {
		pr_err("%s: %s regulator is already initialized\n", __func__,
			vreg_info->name);
		return ret;
	}

	vreg_info->reg = devm_regulator_get(dev, vreg_info->name);
	if (IS_ERR(vreg_info->reg)) {
		ret = PTR_ERR(vreg_info->reg);
		if (ret == -EPROBE_DEFER) {
			pr_err("%s: %s probe deferred!\n", __func__,
				vreg_info->name);
		} else {
			pr_err("%s: Get %s failed!\n", __func__,
				vreg_info->name);
		}
	}
	return ret;
}

static int icnss_release_resources(void)
{
	int ret = 0;
	struct icnss_vreg_info *vreg_info = &penv->vreg_info;

	if (!vreg_info->reg) {
		pr_err("%s: regulator is not initialized\n", __func__);
		return -ENOENT;
	}

	devm_regulator_put(vreg_info->reg);
	return ret;
}

static int icnss_probe(struct platform_device *pdev)
{
	int ret = 0;
@@ -1157,8 +1366,10 @@ static int icnss_probe(struct platform_device *pdev)
	int i;
	struct device *dev = &pdev->dev;

	if (penv)
	if (penv) {
		pr_err("%s: penv is already initialized\n", __func__);
		return -EEXIST;
	}

	penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
	if (!penv)
@@ -1166,26 +1377,38 @@ static int icnss_probe(struct platform_device *pdev)

	penv->pdev = pdev;

	ret = icnss_dt_parse_vreg_info(dev, &penv->vreg_info, "vdd-io");
	if (ret < 0) {
		pr_err("%s: failed parsing vdd io data\n", __func__);
		goto out;
	}

	ret = icnss_get_resources(dev);
	if (ret < 0) {
		pr_err("%s: Regulator setup failed (%d)\n", __func__, ret);
		goto out;
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
	if (!res) {
		pr_err("icnss: Memory base not found\n");
		pr_err("%s: Memory base not found\n", __func__);
		ret = -EINVAL;
		goto out;
		goto release_regulator;
	}
	penv->mem_base_pa = res->start;
	penv->mem_base_va = ioremap(penv->mem_base_pa, resource_size(res));
	if (!penv->mem_base_va) {
		pr_err("icnss: ioremap failed\n");
		pr_err("%s: ioremap failed\n", __func__);
		ret = -EINVAL;
		goto out;
		goto release_regulator;
	}

	for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
		if (!res) {
			pr_err("icnss: Fail to get IRQ-%d\n", i);
			pr_err("%s: Fail to get IRQ-%d\n", __func__, i);
			ret = -ENODEV;
			goto out;
			goto release_regulator;
		} else {
			penv->ce_irqs[i] = res->start;
		}
@@ -1201,7 +1424,7 @@ static int icnss_probe(struct platform_device *pdev)
			if (!penv->msa_va) {
				pr_err("%s: DMA alloc failed\n", __func__);
				ret = -EINVAL;
				goto out;
				goto release_regulator;
			}
			pr_debug("%s: MAS va: %p, MSA pa: %pa\n",
				 __func__, penv->msa_va, &penv->msa_pa);
@@ -1209,7 +1432,7 @@ static int icnss_probe(struct platform_device *pdev)
	} else {
		pr_err("%s: Fail to get MSA Memory Size\n", __func__);
		ret = -ENODEV;
		goto out;
		goto release_regulator;
	}

	penv->skip_qmi = of_property_read_bool(dev->of_node,
@@ -1255,12 +1478,21 @@ err_wlan_mode:
	if (penv->msa_va)
		dma_free_coherent(&pdev->dev, penv->msa_mem_size,
				  penv->msa_va, penv->msa_pa);
release_regulator:
	ret = icnss_release_resources();
	if (ret < 0)
		pr_err("%s: fail to release the platform resource\n",
			 __func__);
out:
	devm_kfree(&pdev->dev, penv);
	penv = NULL;
	return ret;
}

static int icnss_remove(struct platform_device *pdev)
{
	int ret = 0;

	qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
					  WLFW_SERVICE_VERS_V01,
					  WLFW_SERVICE_INS_ID_V01,
@@ -1272,7 +1504,16 @@ static int icnss_remove(struct platform_device *pdev)
		dma_free_coherent(&pdev->dev, penv->msa_mem_size,
				  penv->msa_va, penv->msa_pa);

	return 0;
	ret = icnss_vreg_set(VREG_OFF);
	if (ret < 0)
		pr_err("%s: Failed to turn off voltagre regulator: %d\n",
		       __func__, ret);

	ret = icnss_release_resources();
	if (ret < 0)
		pr_err("%s: fail to release the platform resource\n",
			 __func__);
	return ret;
}