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

Commit 580e8bd4 authored by Yue Ma's avatar Yue Ma
Browse files

cnss2: Add MHI support for QCA6290



Enable MHI driver in order to support boot and firmware download
for QCA6290 Wi-Fi chipset.

Change-Id: I41d8ec9e88252eae616fc7eaf5a12520e64a497d
Signed-off-by: default avatarYue Ma <yuem@codeaurora.org>
parent 9e11d7b5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ Optional properties:
  - vdd-wlan-en-supply: WLAN_EN fixed regulator specified by QCA6174 specifications.
  - qcom,wlan-en-vreg-support: Boolean property to decide the whether the WLAN_EN pin
				is a gpio or fixed regulator.
  - qcom,mhi: phandle to indicate the device which needs MHI support.

Example:

@@ -64,4 +65,5 @@ Example:
        pinctrl-0 = <&cnss_default>;
        qcom,wlan-rc-num = <0>;
        qcom,wlan-smmu-iova-address = <0 0x10000000>;
        qcom,mhi = <&mhi_wlan>;
    };
+78 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#define CNSS_DUMP_MAGIC_VER_V2		0x42445953
#define CNSS_DUMP_NAME			"CNSS_WLAN"
#define WLAN_RECOVERY_DELAY		1000
#define FILE_SYSTEM_READY		1

static struct cnss_plat_data *plat_env;

@@ -638,13 +639,19 @@ static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv)
		goto power_off;
	}

	ret = cnss_pci_start_mhi(pci_priv);
	if (ret) {
		cnss_pr_err("Failed to start MHI, err = %d\n", ret);
		goto suspend_link;
	}

	if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) {
		ret = plat_priv->driver_ops->probe(pci_priv->pci_dev,
						   pci_priv->pci_device_id);
		if (ret) {
			cnss_pr_err("Failed to probe host driver, err = %d\n",
				    ret);
			goto suspend_link;
			goto stop_mhi;
		}
	} else if (plat_priv->recovery_in_progress) {
		ret = plat_priv->driver_ops->reinit(pci_priv->pci_dev,
@@ -652,16 +659,19 @@ static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv)
		if (ret) {
			cnss_pr_err("Failed to reinit host driver, err = %d\n",
				    ret);
			goto suspend_link;
			goto stop_mhi;
		}
		plat_priv->recovery_in_progress = false;
	} else {
		cnss_pr_err("Driver state is not correct to power up!\n");
		ret = -EINVAL;
		goto suspend_link;
		goto stop_mhi;
	}

	return 0;

stop_mhi:
	cnss_pci_stop_mhi(pci_priv);
suspend_link:
	cnss_suspend_pci_link(pci_priv);
power_off:
@@ -691,6 +701,8 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv)
		plat_priv->driver_ops->shutdown(pci_priv->pci_dev);
	}

	cnss_pci_stop_mhi(pci_priv);

	ret = cnss_suspend_pci_link(pci_priv);
	if (ret)
		cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret);
@@ -1064,6 +1076,62 @@ static void cnss_unregister_bus_scale(struct cnss_plat_data *plat_priv)
		msm_bus_scale_unregister_client(bus_bw_info->bus_client);
}

static ssize_t cnss_fs_ready_store(struct device *dev,
				   struct device_attribute *attr,
				   const char *buf,
				   size_t count)
{
	int fs_ready = 0;
	struct cnss_plat_data *plat_priv = dev_get_drvdata(dev);

	if (sscanf(buf, "%du", &fs_ready) != 1)
		return -EINVAL;

	cnss_pr_dbg("File system is ready, fs_ready is %d, count is %zu\n",
		    fs_ready, count);

	if (!plat_priv) {
		cnss_pr_err("plat_priv is NULL!\n");
		return count;
	}

	switch (plat_priv->device_id) {
	case QCA6290_DEVICE_ID:
		break;
	default:
		cnss_pr_err("Not supported for device ID 0x%lx\n",
			    plat_priv->device_id);
		return count;
	}

	if (fs_ready == FILE_SYSTEM_READY)
		cnss_pci_start_mhi(plat_priv->bus_priv);

	return count;
}

static DEVICE_ATTR(fs_ready, S_IWUSR | S_IWGRP, NULL, cnss_fs_ready_store);

static int cnss_create_sysfs(struct cnss_plat_data *plat_priv)
{
	int ret = 0;

	ret = device_create_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready);
	if (ret) {
		cnss_pr_err("Failed to create device file, err = %d\n", ret);
		goto out;
	}

	return 0;
out:
	return ret;
}

static void cnss_remove_sysfs(struct cnss_plat_data *plat_priv)
{
	device_remove_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready);
}

static const struct platform_device_id cnss_platform_id_table[] = {
	{ .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, },
	{ .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, },
@@ -1142,12 +1210,18 @@ static int cnss_probe(struct platform_device *plat_dev)
	if (ret)
		goto unreg_ramdump;

	ret = cnss_create_sysfs(plat_priv);
	if (ret)
		goto unreg_bus_scale;

	register_pm_notifier(&cnss_pm_notifier);

	cnss_pr_info("Platform driver probed successfully.\n");

	return 0;

unreg_bus_scale:
	cnss_unregister_bus_scale(plat_priv);
unreg_ramdump:
	cnss_unregister_ramdump(plat_priv);
unreg_subsys:
@@ -1172,6 +1246,7 @@ static int cnss_remove(struct platform_device *plat_dev)
	struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev);

	unregister_pm_notifier(&cnss_pm_notifier);
	cnss_remove_sysfs(plat_priv);
	cnss_unregister_bus_scale(plat_priv);
	cnss_unregister_ramdump(plat_priv);
	cnss_unregister_subsys(plat_priv);
+224 −4
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@
#define PCI_DMA_MASK			32
#endif

#define MHI_NODE_NAME			"qcom,mhi"

static DEFINE_SPINLOCK(pci_link_down_lock);

static unsigned int pci_link_down_panic;
@@ -816,6 +818,216 @@ static void cnss_pci_disable_bus(struct cnss_pci_data *pci_priv)
	pci_disable_device(pci_dev);
}

static int cnss_mhi_pm_runtime_get(struct pci_dev *pci_dev)
{
	return pm_runtime_get(&pci_dev->dev);
}

static void cnss_mhi_pm_runtime_put_noidle(struct pci_dev *pci_dev)
{
	pm_runtime_put_noidle(&pci_dev->dev);
}

static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
{
	int ret = 0;
	struct pci_dev *pci_dev = pci_priv->pci_dev;
	struct mhi_device *mhi_dev = &pci_priv->mhi_dev;

	mhi_dev->dev = &pci_priv->plat_priv->plat_dev->dev;
	mhi_dev->pci_dev = pci_dev;

	mhi_dev->resources[0].start = (resource_size_t)pci_priv->bar;
	mhi_dev->resources[0].end = (resource_size_t)pci_priv->bar +
		pci_resource_len(pci_dev, PCI_BAR_NUM);
	mhi_dev->resources[0].flags =
		pci_resource_flags(pci_dev, PCI_BAR_NUM);
	mhi_dev->resources[0].name = "BAR";
	cnss_pr_dbg("BAR start is %pa, BAR end is %pa\n",
		    &mhi_dev->resources[0].start, &mhi_dev->resources[0].end);

	if (!mhi_dev->resources[1].start) {
		mhi_dev->resources[1].start = pci_dev->irq;
		mhi_dev->resources[1].end = pci_dev->irq + 1;
		mhi_dev->resources[1].flags = IORESOURCE_IRQ;
		mhi_dev->resources[1].name = "IRQ";
	}
	cnss_pr_dbg("IRQ start is %pa, IRQ end is %pa\n",
		    &mhi_dev->resources[1].start, &mhi_dev->resources[1].end);

	mhi_dev->pm_runtime_get = cnss_mhi_pm_runtime_get;
	mhi_dev->pm_runtime_noidle = cnss_mhi_pm_runtime_put_noidle;

	ret = mhi_register_device(mhi_dev, MHI_NODE_NAME,
				  (unsigned long)pci_priv);
	if (ret) {
		cnss_pr_err("Failed to register as MHI device, err = %d\n",
			    ret);
		return ret;
	}

	return 0;
}

static void cnss_pci_unregister_mhi(struct cnss_pci_data *pci_priv)
{
}

static enum mhi_dev_ctrl cnss_to_mhi_dev_state(enum cnss_mhi_state state)
{
	switch (state) {
	case CNSS_MHI_INIT:
		return MHI_DEV_CTRL_INIT;
	case CNSS_MHI_DEINIT:
		return MHI_DEV_CTRL_DE_INIT;
	case CNSS_MHI_POWER_ON:
		return MHI_DEV_CTRL_POWER_ON;
	case CNSS_MHI_POWER_OFF:
		return MHI_DEV_CTRL_POWER_OFF;
	case CNSS_MHI_SUSPEND:
		return MHI_DEV_CTRL_SUSPEND;
	case CNSS_MHI_RESUME:
		return MHI_DEV_CTRL_RESUME;
	case CNSS_MHI_RAM_DUMP:
		return MHI_DEV_CTRL_RAM_DUMP;
	case CNSS_MHI_NOTIFY_LINK_ERROR:
		return MHI_DEV_CTRL_NOTIFY_LINK_ERROR;
	default:
		cnss_pr_err("Unknown CNSS MHI state (%d)\n", state);
		return -EINVAL;
	}
}

static int cnss_pci_check_mhi_state_bit(struct cnss_pci_data *pci_priv,
					enum mhi_dev_ctrl mhi_dev_state)
{
	switch (mhi_dev_state) {
	case MHI_DEV_CTRL_INIT:
		if (!test_bit(MHI_DEV_CTRL_INIT, &pci_priv->mhi_state))
			return 0;
		break;
	case MHI_DEV_CTRL_DE_INIT:
	case MHI_DEV_CTRL_POWER_ON:
		if (test_bit(MHI_DEV_CTRL_INIT, &pci_priv->mhi_state) &&
		    !test_bit(MHI_DEV_CTRL_POWER_ON, &pci_priv->mhi_state))
			return 0;
		break;
	case MHI_DEV_CTRL_POWER_OFF:
	case MHI_DEV_CTRL_SUSPEND:
		if (test_bit(MHI_DEV_CTRL_POWER_ON, &pci_priv->mhi_state) &&
		    !test_bit(MHI_DEV_CTRL_SUSPEND, &pci_priv->mhi_state))
			return 0;
		break;
	case MHI_DEV_CTRL_RESUME:
		if (test_bit(MHI_DEV_CTRL_SUSPEND, &pci_priv->mhi_state))
			return 0;
		break;
	default:
		cnss_pr_err("Unhandled MHI DEV state (%d)\n", mhi_dev_state);
	}

	cnss_pr_err("Cannot set MHI DEV state (%d) in current MHI state (0x%lx)\n",
		    mhi_dev_state, pci_priv->mhi_state);

	return -EINVAL;
}

static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv,
				       enum mhi_dev_ctrl mhi_dev_state)
{
	switch (mhi_dev_state) {
	case MHI_DEV_CTRL_INIT:
		set_bit(MHI_DEV_CTRL_INIT, &pci_priv->mhi_state);
		break;
	case MHI_DEV_CTRL_DE_INIT:
		clear_bit(MHI_DEV_CTRL_INIT, &pci_priv->mhi_state);
	case MHI_DEV_CTRL_POWER_ON:
		set_bit(MHI_DEV_CTRL_POWER_ON, &pci_priv->mhi_state);
		break;
	case MHI_DEV_CTRL_POWER_OFF:
		clear_bit(MHI_DEV_CTRL_POWER_ON, &pci_priv->mhi_state);
		break;
	case MHI_DEV_CTRL_SUSPEND:
		set_bit(MHI_DEV_CTRL_SUSPEND, &pci_priv->mhi_state);
		break;
	case MHI_DEV_CTRL_RESUME:
		clear_bit(MHI_DEV_CTRL_SUSPEND, &pci_priv->mhi_state);
		break;
	default:
		cnss_pr_err("Unhandled MHI DEV state (%d)\n", mhi_dev_state);
	}
}

int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
			   enum cnss_mhi_state state)
{
	int ret = 0;
	enum mhi_dev_ctrl mhi_dev_state = cnss_to_mhi_dev_state(state);

	if (!pci_priv) {
		cnss_pr_err("pci_priv is NULL!\n");
		return -ENODEV;
	}

	if (mhi_dev_state < 0) {
		cnss_pr_err("Invalid MHI DEV state (%d)\n", mhi_dev_state);
		return -EINVAL;
	}

	ret = cnss_pci_check_mhi_state_bit(pci_priv, mhi_dev_state);
	if (ret)
		goto out;

	cnss_pr_dbg("Setting MHI DEV state (%d)\n", mhi_dev_state);
	ret = mhi_pm_control_device(&pci_priv->mhi_dev, mhi_dev_state);
	if (ret) {
		cnss_pr_err("Failed to set MHI DEV state (%d)\n",
			    mhi_dev_state);
		goto out;
	}

	cnss_pci_set_mhi_state_bit(pci_priv, mhi_dev_state);

out:
	return ret;
}

int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
{
	int ret = 0;

	if (!pci_priv) {
		cnss_pr_err("pci_priv is NULL!\n");
		return -ENODEV;
	}

	ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_INIT);
	if (ret)
		goto out;

	ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_ON);
	if (ret)
		goto deinit_mhi;

	return 0;

deinit_mhi:
	cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT);
out:
	return ret;
}

void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv)
{
	if (!pci_priv) {
		cnss_pr_err("pci_priv is NULL!\n");
		return;
	}

	cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_OFF);
	cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT);
}

static int cnss_pci_probe(struct pci_dev *pci_dev,
			  const struct pci_device_id *id)
{
@@ -827,9 +1039,10 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
	cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
		    id->vendor, pci_dev->device);

	if (!plat_priv) {
		cnss_pr_err("plat_priv is NULL!\n");
		ret = -ENODEV;
	if (pci_dev->device == QCA6290_DEVICE_ID &&
	    !mhi_is_device_ready(&plat_priv->plat_dev->dev, MHI_NODE_NAME)) {
		cnss_pr_err("MHI driver is not ready, defer PCI probe!\n");
		ret = -EPROBE_DEFER;
		goto out;
	}

@@ -889,6 +1102,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
		ret = cnss_pci_enable_msi(pci_priv);
		if (ret)
			goto disable_bus;
		ret = cnss_pci_register_mhi(pci_priv);
		if (ret) {
			cnss_pci_disable_msi(pci_priv);
			goto disable_bus;
		}
		break;
	default:
		cnss_pr_err("Unknown PCI device found: 0x%x\n",
@@ -918,8 +1136,10 @@ static void cnss_pci_remove(struct pci_dev *pci_dev)
	struct cnss_plat_data *plat_priv =
		cnss_bus_dev_to_plat_priv(&pci_dev->dev);

	if (pci_dev->device == QCA6290_DEVICE_ID)
	if (pci_dev->device == QCA6290_DEVICE_ID) {
		cnss_pci_unregister_mhi(pci_priv);
		cnss_pci_disable_msi(pci_priv);
	}
	cnss_pci_disable_bus(pci_priv);
	cnss_dereg_pci_event(pci_priv);
	if (pci_priv->smmu_mapping)
+18 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#define _CNSS_PCI_H

#include <asm/dma-iommu.h>
#include <linux/msm_mhi.h>
#include <linux/msm_pcie.h>
#include <linux/pci.h>

@@ -27,6 +28,17 @@
#define QCA6290_VENDOR_ID		0x168C
#define QCA6290_DEVICE_ID		0xABCD

enum cnss_mhi_state {
	CNSS_MHI_INIT,
	CNSS_MHI_DEINIT,
	CNSS_MHI_SUSPEND,
	CNSS_MHI_RESUME,
	CNSS_MHI_POWER_ON,
	CNSS_MHI_POWER_OFF,
	CNSS_MHI_RAM_DUMP,
	CNSS_MHI_NOTIFY_LINK_ERROR,
};

struct cnss_msi_user {
	char *name;
	int num_vectors;
@@ -57,6 +69,8 @@ struct cnss_pci_data {
	void __iomem *bar;
	struct cnss_msi_config *msi_config;
	uint32_t msi_ep_base_data;
	struct mhi_device mhi_dev;
	unsigned long mhi_state;
};

static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data)
@@ -110,5 +124,9 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv);
void cnss_pci_deinit(struct cnss_plat_data *plat_priv);
int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va,
			  phys_addr_t *pa);
int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
			   enum cnss_mhi_state state);
int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv);
void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv);

#endif /* _CNSS_PCI_H */