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

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

Merge "Net: CNSS_SDIO: Add SSR support for QCA WIFI Devices"

parents 3cc68602 d2003c1c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -9,6 +9,9 @@ PMIC voltage regulator resources.

Required properties:
  - compatible: "qcom,cnss_sdio"
  - reg: memory resource to save firmware dump, optional.
  - reg-names: memory resource name.
  - subsys-name: cnss sdio subsytem device name, required.
  - vdd-wlan-supply: phandle to the WLAN vdd regulator device tree node.
  - vdd-wlan-dsrc-supply: phandle to the WLAN dsrc vdd regulator device tree node.
  - vdd-wlan-io-supply: phandle to the WLAN IO regulator device tree node.
@@ -17,6 +20,9 @@ Required properties:
Example:
	qcom,cnss-sdio {
		compatible = "qcom,cnss_sdio";
		reg = <0x87a00000, 0x200000>;
		reg-names = "ramdump";
		subsys-name = "AR6320";
		vdd-wlan-supply = <&rome_vreg>;
		vdd-wlan-dsrc-supply = <&sdcard_ext_vreg>;
		vdd-wlan-io-supply = <&mdm9607_l11>;
+3 −0
Original line number Diff line number Diff line
@@ -499,6 +499,9 @@

	qcom,cnss-sdio {
		compatible = "qcom,cnss_sdio";
		reg = <0x87a00000 0x200000>;
		reg-names = "ramdump";
		subsys-name = "AR6320";
		vdd-wlan-supply = <&rome_vreg>;
		vdd-wlan-dsrc-supply = <&sdcard_ext_vreg>;
		vdd-wlan-io-supply = <&mdm9607_l11>;
+312 −2
Original line number Diff line number Diff line
@@ -21,6 +21,11 @@
#include <linux/slab.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/io.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/ramdump.h>
#include <soc/qcom/memory_dump.h>
#include <net/cnss.h>

#define WLAN_VREG_NAME		"vdd-wlan"
@@ -57,14 +62,30 @@ struct cnss_sdio_info {
	const struct sdio_device_id *id;
};

struct cnss_ssr_info {
	struct subsys_device *subsys;
	struct subsys_desc subsysdesc;
	void *subsys_handle;
	struct ramdump_device *ramdump_dev;
	unsigned long ramdump_size;
	void *ramdump_addr;
	phys_addr_t ramdump_phys;
	char subsys_name[10];
};

static struct cnss_sdio_data {
	struct cnss_sdio_regulator regulator;
	struct platform_device *pdev;
	struct cnss_dfs_nol_info dfs_info;
	struct cnss_unsafe_channel_list unsafe_list;
	struct cnss_sdio_info cnss_sdio_info;
	struct cnss_ssr_info ssr_info;
} *cnss_pdata;

#define WLAN_RECOVERY_DELAY 1
/* cnss sdio subsytem device name, required property */
#define CNSS_SUBSYS_NAME_KEY "subsys-name"

/* SDIO manufacturer ID and Codes */
#define MANUFACTURER_ID_AR6320_BASE        0x500
#define MANUFACTURER_ID_QCA9377_BASE       0x700
@@ -203,6 +224,277 @@ int cnss_wlan_get_dfs_nol(void *info, u16 info_len)
}
EXPORT_SYMBOL(cnss_wlan_get_dfs_nol);

static int cnss_sdio_shutdown(const struct subsys_desc *subsys, bool force_stop)
{
	struct cnss_sdio_info *cnss_info;
	struct cnss_sdio_wlan_driver *wdrv;

	if (!cnss_pdata)
		return -ENODEV;

	cnss_info = &cnss_pdata->cnss_sdio_info;
	wdrv = cnss_info->wdrv;
	if (wdrv && wdrv->shutdown)
		wdrv->shutdown(cnss_info->func);
	return 0;
}

static int cnss_sdio_powerup(const struct subsys_desc *subsys)
{
	struct cnss_sdio_info *cnss_info;
	struct cnss_sdio_wlan_driver *wdrv;
	int ret = 0;

	if (!cnss_pdata)
		return -ENODEV;

	cnss_info = &cnss_pdata->cnss_sdio_info;
	wdrv = cnss_info->wdrv;
	if (wdrv && wdrv->reinit) {
		ret = wdrv->reinit(cnss_info->func, cnss_info->id);
		if (ret)
			pr_err("%s: wlan reinit error=%d\n", __func__, ret);
	}
	return ret;
}

static void cnss_sdio_crash_shutdown(const struct subsys_desc *subsys)
{
	struct cnss_sdio_info *cnss_info;
	struct cnss_sdio_wlan_driver *wdrv;

	if (!cnss_pdata)
		return;

	cnss_info = &cnss_pdata->cnss_sdio_info;
	wdrv = cnss_info->wdrv;
	if (wdrv && wdrv->crash_shutdown)
		wdrv->crash_shutdown(cnss_info->func);
}

static int cnss_sdio_ramdump(int enable, const struct subsys_desc *subsys)
{
	struct cnss_ssr_info *ssr_info;
	struct ramdump_segment segment;
	int ret;

	if (!cnss_pdata)
		return -ENODEV;

	if (!cnss_pdata->ssr_info.ramdump_size)
		return -ENOENT;

	if (!enable)
		return 0;

	ssr_info = &cnss_pdata->ssr_info;

	memset(&segment, 0, sizeof(segment));
	segment.v_address = ssr_info->ramdump_addr;
	segment.size = ssr_info->ramdump_size;
	ret = do_ramdump(ssr_info->ramdump_dev, &segment, 1);
	if (ret)
		pr_err("%s: do_ramdump failed error=%d\n", __func__, ret);
	return ret;
}

static int cnss_subsys_init(void)
{
	struct cnss_ssr_info *ssr_info;
	int ret = 0;

	if (!cnss_pdata)
		return -ENODEV;

	ssr_info = &cnss_pdata->ssr_info;
	ssr_info->subsysdesc.name = ssr_info->subsys_name;
	ssr_info->subsysdesc.owner = THIS_MODULE;
	ssr_info->subsysdesc.shutdown = cnss_sdio_shutdown;
	ssr_info->subsysdesc.powerup = cnss_sdio_powerup;
	ssr_info->subsysdesc.ramdump = cnss_sdio_ramdump;
	ssr_info->subsysdesc.crash_shutdown = cnss_sdio_crash_shutdown;
	ssr_info->subsysdesc.dev = &cnss_pdata->pdev->dev;
	ssr_info->subsys = subsys_register(&ssr_info->subsysdesc);
	if (IS_ERR(ssr_info->subsys)) {
		ret = PTR_ERR(ssr_info->subsys);
		ssr_info->subsys = NULL;
		dev_err(&cnss_pdata->pdev->dev, "Failed to subsys_register error=%d\n",
			ret);
		goto err_subsys_reg;
	}
	ssr_info->subsys_handle = subsystem_get(ssr_info->subsysdesc.name);
	if (IS_ERR(ssr_info->subsys_handle)) {
		ret = PTR_ERR(ssr_info->subsys_handle);
		ssr_info->subsys_handle = NULL;
		dev_err(&cnss_pdata->pdev->dev, "Failed to subsystem_get error=%d\n",
			ret);
		goto err_subsys_get;
	}
	return 0;
err_subsys_get:
	subsys_unregister(ssr_info->subsys);
	ssr_info->subsys = NULL;
err_subsys_reg:
	return ret;
}

static void cnss_subsys_exit(void)
{
	struct cnss_ssr_info *ssr_info;

	if (!cnss_pdata)
		return;

	ssr_info = &cnss_pdata->ssr_info;
	if (ssr_info->subsys_handle)
		subsystem_put(ssr_info->subsys_handle);
	ssr_info->subsys_handle = NULL;
	if (ssr_info->subsys)
		subsys_unregister(ssr_info->subsys);
	ssr_info->subsys = NULL;
}

static int cnss_configure_ramdump(void)
{
	struct cnss_ssr_info *ssr_info;
	int ret = 0;
	struct resource *res;
	const char *name;

	if (!cnss_pdata)
		return -ENODEV;
	ssr_info = &cnss_pdata->ssr_info;

	ret = of_property_read_string(cnss_pdata->pdev->dev.of_node,
				      CNSS_SUBSYS_NAME_KEY,
				      &name);
	if (ret) {
		pr_err("%s: cnss missing DT key '%s'\n", __func__,
		       CNSS_SUBSYS_NAME_KEY);
		ret = -ENODEV;
		goto err_subsys_name_query;
	}
	strlcpy(ssr_info->subsys_name, name, sizeof(ssr_info->subsys_name));

	res = platform_get_resource_byname(cnss_pdata->pdev,
					   IORESOURCE_MEM, "ramdump");
	if (res) {
		ssr_info->ramdump_phys = res->start;
		ssr_info->ramdump_size = resource_size(res);
	} else {
		pr_err("%s: CNSS ramdump mem not available\n", __func__);
		return 0;
	}

	ssr_info->ramdump_addr = ioremap(ssr_info->ramdump_phys,
		ssr_info->ramdump_size);

	if (!ssr_info->ramdump_addr || ssr_info->ramdump_size == 0) {
		ssr_info->ramdump_size = 0;
		ssr_info->ramdump_addr = NULL;
		pr_err("%s: CNSS ramdump will not be collected\n", __func__);
		return 0;
	}

	pr_info("%s: ramdump addr: %p, phys: %pa subsys:'%s'\n", __func__,
		ssr_info->ramdump_addr,
		&ssr_info->ramdump_phys,
		ssr_info->subsys_name);

	ssr_info->ramdump_dev = create_ramdump_device(ssr_info->subsys_name,
		&cnss_pdata->pdev->dev);
	if (!ssr_info->ramdump_dev) {
		ret = -ENOMEM;
		pr_err("%s: ramdump dev create failed: error=%d\n",
		       __func__, ret);
		goto err_create_ramdump;
	}
	return 0;
err_create_ramdump:
	iounmap(ssr_info->ramdump_addr);
	ssr_info->ramdump_addr = NULL;
	ssr_info->ramdump_size = 0;
err_subsys_name_query:
	return ret;
}

static void cnss_ramdump_cleanup(void)
{
	struct cnss_ssr_info *ssr_info;

	if (!cnss_pdata)
		return;

	ssr_info = &cnss_pdata->ssr_info;
	if (ssr_info->ramdump_addr)
		iounmap(ssr_info->ramdump_addr);
	ssr_info->ramdump_addr = NULL;
	if (ssr_info->ramdump_dev)
		destroy_ramdump_device(ssr_info->ramdump_dev);
	ssr_info->ramdump_dev = NULL;
}

int cnss_get_ramdump_mem(unsigned long *address, unsigned long *size)
{
	struct cnss_ssr_info *ssr_info;

	if (!cnss_pdata || !cnss_pdata->pdev)
		return -ENODEV;

	ssr_info = &cnss_pdata->ssr_info;
	*address = ssr_info->ramdump_phys;
	*size = ssr_info->ramdump_size;

	return 0;
}
EXPORT_SYMBOL(cnss_get_ramdump_mem);

void *cnss_get_virt_ramdump_mem(unsigned long *size)
{
	if (!cnss_pdata || !cnss_pdata->pdev)
		return NULL;

	*size = cnss_pdata->ssr_info.ramdump_size;

	return cnss_pdata->ssr_info.ramdump_addr;
}
EXPORT_SYMBOL(cnss_get_virt_ramdump_mem);

void cnss_device_self_recovery(void)
{
	cnss_sdio_shutdown(NULL, false);
	msleep(WLAN_RECOVERY_DELAY);
	cnss_sdio_powerup(NULL);
}
EXPORT_SYMBOL(cnss_device_self_recovery);

static void recovery_work_handler(struct work_struct *recovery)
{
	cnss_device_self_recovery();
}

DECLARE_WORK(recovery_work, recovery_work_handler);

void cnss_schedule_recovery_work(void)
{
	schedule_work(&recovery_work);
}
EXPORT_SYMBOL(cnss_schedule_recovery_work);

void cnss_device_crashed(void)
{
	struct cnss_ssr_info *ssr_info;

	if (!cnss_pdata)
		return;
	ssr_info = &cnss_pdata->ssr_info;
	if (ssr_info->subsys) {
		subsys_set_crash_status(ssr_info->subsys, true);
		subsystem_restart_dev(ssr_info->subsys);
	}
}
EXPORT_SYMBOL(cnss_device_crashed);

static int cnss_sdio_wlan_inserted(
				struct sdio_func *func,
				const struct sdio_device_id *id)
@@ -572,9 +864,26 @@ static int cnss_sdio_probe(struct platform_device *pdev)
		goto err_wlan_dsrc_enable_regulator;
	}

	error = cnss_configure_ramdump();
	if (error) {
		dev_err(&pdev->dev, "Failed to configure ramdump error=%d\n",
			error);
		goto err_ramdump_create;
	}

	error = cnss_subsys_init();
	if (error) {
		dev_err(&pdev->dev, "Failed to cnss_subsys_init error=%d\n",
			error);
		goto err_subsys_init;
	}

	dev_info(&pdev->dev, "CNSS SDIO Driver registered");
	return 0;

err_subsys_init:
	cnss_ramdump_cleanup();
err_ramdump_create:
	cnss_sdio_wlan_exit();
err_wlan_dsrc_enable_regulator:
	regulator_put(cnss_pdata->regulator.wlan_vreg_dsrc);
err_wlan_enable_regulator:
@@ -595,7 +904,8 @@ static int cnss_sdio_remove(struct platform_device *pdev)

	dfs_info = &cnss_pdata->dfs_info;
	kfree(dfs_info->dfs_nol_info);

	cnss_subsys_exit();
	cnss_ramdump_cleanup();
	cnss_sdio_release_resource();
	return 0;
}
+8 −5
Original line number Diff line number Diff line
@@ -115,11 +115,6 @@ enum cnss_runtime_request {
extern int cnss_get_fw_image(struct image_desc_info *image_desc_info);
extern void cnss_runtime_init(struct device *dev, int auto_delay);
extern void cnss_runtime_exit(struct device *dev);
extern void cnss_device_crashed(void);
extern void cnss_device_self_recovery(void);
extern int cnss_get_ramdump_mem(unsigned long *address, unsigned long *size);
extern void *cnss_get_virt_ramdump_mem(unsigned long *size);
extern void cnss_schedule_recovery_work(void);
extern void cnss_wlan_pci_link_down(void);
extern int cnss_pcie_shadow_control(struct pci_dev *dev, bool enable);
extern int cnss_wlan_register_driver(struct cnss_wlan_driver *driver);
@@ -188,6 +183,11 @@ extern int cnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list,
		u16 *ch_count, u16 buf_len);
extern int cnss_wlan_set_dfs_nol(const void *info, u16 info_len);
extern int cnss_wlan_get_dfs_nol(void *info, u16 info_len);
extern void cnss_device_crashed(void);
extern void cnss_device_self_recovery(void);
extern int cnss_get_ramdump_mem(unsigned long *address, unsigned long *size);
extern void *cnss_get_virt_ramdump_mem(unsigned long *size);
extern void cnss_schedule_recovery_work(void);

#ifdef CONFIG_CNSS_SDIO
struct cnss_sdio_wlan_driver {
@@ -195,6 +195,9 @@ struct cnss_sdio_wlan_driver {
	const struct sdio_device_id *id_table;
	int (*probe)(struct sdio_func *, const struct sdio_device_id *);
	void (*remove)(struct sdio_func *);
	int (*reinit)(struct sdio_func *, const struct sdio_device_id *);
	void (*shutdown)(struct sdio_func *);
	void (*crash_shutdown)(struct sdio_func *);
	int (*suspend)(struct device *);
	int (*resume)(struct device *);
};