summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLior David <liord@codeaurora.org>2016-02-06 15:11:47 +0200
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 19:58:03 -0700
commitadd338b4a5d1e308c884deff4366ecc65e817fcc (patch)
tree0424725b33877fd21c2b8d01ab08806c10032c60 /drivers
parentf9b44400f792a29a1146d16807a1f1049b5bc874 (diff)
msm_11ad: add support for subsystem_restart framework
Add 11ad as a subsystem (WIGIG) within the subsystem_restart framework, in order to allow proper support for recovery from 11ad firmware crashes, and support automated collection of crash dumps for offline analysis and bug reporting. Change-Id: I7acbe8860e046cdba2ce027f91ed2fa995a70c70 Signed-off-by: Lior David <liord@codeaurora.org> [merez@codeaurora.org: fixed merge conflicts] Signed-off-by: Maya Erez <merez@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.c4
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.c247
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.h19
3 files changed, 263 insertions, 7 deletions
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c
index e5c92caf682a..263740a9b980 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.c
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -46,7 +46,7 @@ void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops,
return NULL;
}
- handle = msm_11ad_dev_init(dev, ops);
+ handle = msm_11ad_dev_init(dev, ops, rops, wil_handle);
return handle;
}
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 04783a1b9813..28aeb489c13d 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, 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
@@ -20,6 +20,10 @@
#include <linux/iommu.h>
#include <linux/version.h>
#include <linux/delay.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 "wil_platform.h"
#include "msm_11ad.h"
@@ -34,6 +38,11 @@
MSM_PCIE_CONFIG_LINKDOWN)
#define PM_OPT_RESUME MSM_PCIE_CONFIG_NO_CFG_RESTORE
+#define WIGIG_SUBSYS_NAME "WIGIG"
+#define WIGIG_RAMDUMP_SIZE 0x200000 /* maximum ramdump size */
+#define WIGIG_DUMP_FORMAT_VER 0x1
+#define WIGIG_DUMP_MAGIC_VER_V1 0x57474947
+
struct device;
static const char * const gpio_en_name = "qcom,wigig-en";
@@ -57,6 +66,19 @@ struct msm11ad_ctx {
/* bus frequency scaling */
struct msm_bus_scale_pdata *bus_scale;
u32 msm_bus_handle;
+
+ /* subsystem restart */
+ struct wil_platform_rops rops;
+ void *wil_handle;
+ struct subsys_desc subsysdesc;
+ struct subsys_device *subsys;
+ void *subsys_handle;
+ bool recovery_in_progress;
+
+ /* ramdump */
+ void *ramdump_addr;
+ struct msm_dump_data dump_data;
+ struct ramdump_device *ramdump_dev;
};
static LIST_HEAD(dev_list);
@@ -205,6 +227,173 @@ release_mapping:
return rc;
}
+static int msm_11ad_ssr_shutdown(const struct subsys_desc *subsys,
+ bool force_stop)
+{
+ pr_info("%s(%p,%d)\n", __func__, subsys, force_stop);
+ /* nothing is done in shutdown. We do full recovery in powerup */
+ return 0;
+}
+
+static int msm_11ad_ssr_powerup(const struct subsys_desc *subsys)
+{
+ int rc = 0;
+ struct platform_device *pdev;
+ struct msm11ad_ctx *ctx;
+
+ pr_info("%s(%p)\n", __func__, subsys);
+
+ pdev = to_platform_device(subsys->dev);
+ ctx = platform_get_drvdata(pdev);
+
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->recovery_in_progress) {
+ if (ctx->rops.fw_recovery && ctx->wil_handle) {
+ dev_info(ctx->dev, "requesting FW recovery\n");
+ rc = ctx->rops.fw_recovery(ctx->wil_handle);
+ }
+ ctx->recovery_in_progress = false;
+ }
+
+ return rc;
+}
+
+static int msm_11ad_ssr_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ int rc;
+ struct ramdump_segment segment;
+ struct platform_device *pdev;
+ struct msm11ad_ctx *ctx;
+
+ pdev = to_platform_device(subsys->dev);
+ ctx = platform_get_drvdata(pdev);
+
+ if (!ctx)
+ return -ENODEV;
+
+ if (!enable)
+ return 0;
+
+ if (ctx->rops.ramdump && ctx->wil_handle) {
+ rc = ctx->rops.ramdump(ctx->wil_handle, ctx->ramdump_addr,
+ WIGIG_RAMDUMP_SIZE);
+ if (rc) {
+ dev_err(ctx->dev, "ramdump failed : %d\n", rc);
+ return -EINVAL;
+ }
+ }
+
+ memset(&segment, 0, sizeof(segment));
+ segment.v_address = ctx->ramdump_addr;
+ segment.size = WIGIG_RAMDUMP_SIZE;
+
+ return do_ramdump(ctx->ramdump_dev, &segment, 1);
+}
+
+static void msm_11ad_ssr_crash_shutdown(const struct subsys_desc *subsys)
+{
+ int rc;
+ struct platform_device *pdev;
+ struct msm11ad_ctx *ctx;
+
+ pdev = to_platform_device(subsys->dev);
+ ctx = platform_get_drvdata(pdev);
+
+ if (!ctx) {
+ pr_err("%s: no context\n", __func__);
+ return;
+ }
+
+ if (ctx->rops.ramdump && ctx->wil_handle) {
+ rc = ctx->rops.ramdump(ctx->wil_handle, ctx->ramdump_addr,
+ WIGIG_RAMDUMP_SIZE);
+ if (rc)
+ dev_err(ctx->dev, "ramdump failed : %d\n", rc);
+ /* continue */
+ }
+
+ ctx->dump_data.version = WIGIG_DUMP_FORMAT_VER;
+ strlcpy(ctx->dump_data.name, WIGIG_SUBSYS_NAME,
+ sizeof(ctx->dump_data.name));
+
+ ctx->dump_data.magic = WIGIG_DUMP_MAGIC_VER_V1;
+}
+
+static void msm_11ad_ssr_deinit(struct msm11ad_ctx *ctx)
+{
+ if (ctx->ramdump_dev) {
+ destroy_ramdump_device(ctx->ramdump_dev);
+ ctx->ramdump_dev = NULL;
+ }
+
+ kfree(ctx->ramdump_addr);
+ ctx->ramdump_addr = NULL;
+
+ if (ctx->subsys_handle) {
+ subsystem_put(ctx->subsys_handle);
+ ctx->subsys_handle = NULL;
+ }
+
+ if (ctx->subsys) {
+ subsys_unregister(ctx->subsys);
+ ctx->subsys = NULL;
+ }
+}
+
+static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx)
+{
+ int rc;
+ struct msm_dump_entry dump_entry;
+
+ ctx->subsysdesc.name = "WIGIG";
+ ctx->subsysdesc.owner = THIS_MODULE;
+ ctx->subsysdesc.shutdown = msm_11ad_ssr_shutdown;
+ ctx->subsysdesc.powerup = msm_11ad_ssr_powerup;
+ ctx->subsysdesc.ramdump = msm_11ad_ssr_ramdump;
+ ctx->subsysdesc.crash_shutdown = msm_11ad_ssr_crash_shutdown;
+ ctx->subsysdesc.dev = ctx->dev;
+ ctx->subsys = subsys_register(&ctx->subsysdesc);
+ if (IS_ERR(ctx->subsys)) {
+ rc = PTR_ERR(ctx->subsys);
+ dev_err(ctx->dev, "subsys_register failed :%d\n", rc);
+ goto out_rc;
+ }
+
+ /* register ramdump area */
+ ctx->ramdump_addr = kmalloc(WIGIG_RAMDUMP_SIZE, GFP_KERNEL);
+ if (!ctx->ramdump_addr) {
+ rc = -ENOMEM;
+ goto out_rc;
+ }
+
+ ctx->dump_data.addr = virt_to_phys(ctx->ramdump_addr);
+ ctx->dump_data.len = WIGIG_RAMDUMP_SIZE;
+ dump_entry.id = MSM_DUMP_DATA_WIGIG;
+ dump_entry.addr = virt_to_phys(&ctx->dump_data);
+
+ rc = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
+ if (rc) {
+ dev_err(ctx->dev, "Dump table setup failed: %d\n", rc);
+ goto out_rc;
+ }
+
+ ctx->ramdump_dev = create_ramdump_device(ctx->subsysdesc.name,
+ ctx->subsysdesc.dev);
+ if (!ctx->ramdump_dev) {
+ dev_err(ctx->dev, "Create ramdump device failed: %d\n", rc);
+ rc = -ENOMEM;
+ goto out_rc;
+ }
+
+ return 0;
+
+out_rc:
+ msm_11ad_ssr_deinit(ctx);
+ return rc;
+}
+
static int msm_11ad_probe(struct platform_device *pdev)
{
struct msm11ad_ctx *ctx;
@@ -324,6 +513,13 @@ static int msm_11ad_probe(struct platform_device *pdev)
}
}
+ /* register for subsystem restart */
+ rc = msm_11ad_ssr_init(ctx);
+ if (rc) {
+ dev_err(ctx->dev, "msm_11ad_ssr_init failed: %d\n", rc);
+ goto out_rc;
+ }
+
/* report */
dev_info(ctx->dev, "msm_11ad discovered. %p {\n"
" gpio_en = %d\n"
@@ -356,6 +552,7 @@ static int msm_11ad_remove(struct platform_device *pdev)
{
struct msm11ad_ctx *ctx = platform_get_drvdata(pdev);
+ msm_11ad_ssr_deinit(ctx);
list_del(&ctx->list);
dev_info(ctx->dev, "%s: pdev %p pcidev %p\n", __func__, pdev,
ctx->pcidev);
@@ -432,10 +629,34 @@ static void ops_uninit(void *handle)
arm_iommu_release_mapping(ctx->mapping);
ctx->mapping = NULL;
}
+
+ memset(&ctx->rops, 0, sizeof(ctx->rops));
+ ctx->wil_handle = NULL;
+
ops_suspend(ctx);
}
-void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops)
+static int ops_notify_crash(void *handle)
+{
+ struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
+ int rc;
+
+ if (ctx->subsys) {
+ dev_info(ctx->dev, "SSR requested\n");
+ ctx->recovery_in_progress = true;
+ rc = subsystem_restart_dev(ctx->subsys);
+ if (rc) {
+ dev_err(ctx->dev,
+ "subsystem_restart_dev fail: %d\n", rc);
+ ctx->recovery_in_progress = false;
+ }
+ }
+
+ return 0;
+}
+
+void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops,
+ const struct wil_platform_rops *rops, void *wil_handle)
{
struct pci_dev *pcidev = to_pci_dev(dev);
struct msm11ad_ctx *ctx = pcidev2ctx(pcidev);
@@ -460,12 +681,19 @@ void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops)
return NULL;
}
+ /* subsystem restart */
+ if (rops) {
+ ctx->rops = *rops;
+ ctx->wil_handle = wil_handle;
+ }
+
/* fill ops */
memset(ops, 0, sizeof(*ops));
ops->bus_request = ops_bus_request;
ops->suspend = ops_suspend;
ops->resume = ops_resume;
ops->uninit = ops_uninit;
+ ops->notify_crash = ops_notify_crash;
return ctx;
}
@@ -492,12 +720,27 @@ int msm_11ad_modinit(void)
ctx->pristine_state = pci_store_saved_state(ctx->pcidev);
}
+ ctx->subsys_handle = subsystem_get(ctx->subsysdesc.name);
+
return ops_resume(ctx);
}
EXPORT_SYMBOL(msm_11ad_modinit);
void msm_11ad_modexit(void)
{
+ struct msm11ad_ctx *ctx = list_first_entry_or_null(&dev_list,
+ struct msm11ad_ctx,
+ list);
+
+ if (!ctx) {
+ pr_err("Context not found\n");
+ return;
+ }
+
+ if (ctx->subsys_handle) {
+ subsystem_put(ctx->subsys_handle);
+ ctx->subsys_handle = NULL;
+ }
}
EXPORT_SYMBOL(msm_11ad_modexit);
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.h b/drivers/platform/msm/msm_11ad/msm_11ad.h
index 455cdb9935cf..15fedcc47fef 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.h
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, 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
@@ -14,9 +14,22 @@
struct device;
struct wil_platform_ops;
+struct wil_platform_rops;
-/* call when binding to device, during probe() */
-void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops);
+/* msm_11ad_dev_init - call when binding to device, during probe()
+ * @dev: device structure of pci device
+ * @ops: pointer to operations supported by platform driver
+ * Will be filled by this function call
+ * @rops: pointer to callback functions provided by wil device driver.
+ * the platform driver copies the structure contents to its
+ * internal storage. May be NULL if device driver does not
+ * support rops.
+ * @wil_handle: context for wil device driver, will be provided
+ * when platform driver invokes any of the callback
+ * functions in rops. May be NULL if rops is also NULL
+ */
+void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops,
+ const struct wil_platform_rops *rops, void *wil_handle);
/* call on insmod */
int msm_11ad_modinit(void);