Commit 8c1b7dc9 authored by Bjorn Andersson's avatar Bjorn Andersson Committed by Andy Gross

firmware: qcom: scm: Expose download-mode control

In order to aid post-mortem debugging the Qualcomm platforms provide a
"memory download mode", where the boot loader will provide an interface
for custom tools to "download" the content of RAM to a host machine.

The mode is triggered by writing a magic value somewhere in RAM, that is
read in the boot code path after a warm-restart. Two mechanism for
setting this magic value are supported in modern platforms; a direct SCM
call to enable the mode or through a secure io write of a magic value.

In order for a normal reboot not to trigger "download mode" the magic
must be cleared during a clean reboot.

Download mode has to be enabled by including qcom_scm.download_mode=1 on
the command line.
Reviewed-by: default avatarStephen Boyd <sboyd@codeaurora.org>
Signed-off-by: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: default avatarAndy Gross <andy.gross@linaro.org>
parent 4e659dbe
...@@ -18,6 +18,8 @@ Required properties: ...@@ -18,6 +18,8 @@ Required properties:
* Core, iface, and bus clocks required for "qcom,scm" * Core, iface, and bus clocks required for "qcom,scm"
- clock-names: Must contain "core" for the core clock, "iface" for the interface - clock-names: Must contain "core" for the core clock, "iface" for the interface
clock and "bus" for the bus clock per the requirements of the compatible. clock and "bus" for the bus clock per the requirements of the compatible.
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the
download mode control register (optional)
Example for MSM8916: Example for MSM8916:
......
...@@ -215,6 +215,17 @@ config QCOM_SCM_64 ...@@ -215,6 +215,17 @@ config QCOM_SCM_64
def_bool y def_bool y
depends on QCOM_SCM && ARM64 depends on QCOM_SCM && ARM64
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
bool "Qualcomm download mode enabled by default"
depends on QCOM_SCM
help
A device with "download mode" enabled will upon an unexpected
warm-restart enter a special debug mode that allows the user to
"download" memory content over USB for offline postmortem analysis.
The feature can be enabled/disabled on the kernel command line.
Say Y here to enable "download mode" by default.
config TI_SCI_PROTOCOL config TI_SCI_PROTOCOL
tristate "TI System Control Interface (TISCI) Message Protocol" tristate "TI System Control Interface (TISCI) Message Protocol"
depends on TI_MESSAGE_MANAGER depends on TI_MESSAGE_MANAGER
......
...@@ -561,6 +561,12 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) ...@@ -561,6 +561,12 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
return ret ? : le32_to_cpu(out); return ret ? : le32_to_cpu(out);
} }
int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
{
return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
enable ? QCOM_SCM_SET_DLOAD_MODE : 0, 0);
}
int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
{ {
struct { struct {
......
...@@ -440,6 +440,19 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, ...@@ -440,6 +440,19 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
return ret; return ret;
} }
int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
{
struct qcom_scm_desc desc = {0};
struct arm_smccc_res res;
desc.args[0] = QCOM_SCM_SET_DLOAD_MODE;
desc.args[1] = enable ? QCOM_SCM_SET_DLOAD_MODE : 0;
desc.arginfo = QCOM_SCM_ARGS(2);
return qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
&desc, &res);
}
int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
unsigned int *val) unsigned int *val)
{ {
......
...@@ -19,15 +19,20 @@ ...@@ -19,15 +19,20 @@
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/qcom_scm.h> #include <linux/qcom_scm.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include "qcom_scm.h" #include "qcom_scm.h"
static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
module_param(download_mode, bool, 0);
#define SCM_HAS_CORE_CLK BIT(0) #define SCM_HAS_CORE_CLK BIT(0)
#define SCM_HAS_IFACE_CLK BIT(1) #define SCM_HAS_IFACE_CLK BIT(1)
#define SCM_HAS_BUS_CLK BIT(2) #define SCM_HAS_BUS_CLK BIT(2)
...@@ -38,6 +43,8 @@ struct qcom_scm { ...@@ -38,6 +43,8 @@ struct qcom_scm {
struct clk *iface_clk; struct clk *iface_clk;
struct clk *bus_clk; struct clk *bus_clk;
struct reset_controller_dev reset; struct reset_controller_dev reset;
u64 dload_mode_addr;
}; };
static struct qcom_scm *__scm; static struct qcom_scm *__scm;
...@@ -345,6 +352,54 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) ...@@ -345,6 +352,54 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
} }
EXPORT_SYMBOL(qcom_scm_io_writel); EXPORT_SYMBOL(qcom_scm_io_writel);
static void qcom_scm_set_download_mode(bool enable)
{
bool avail;
int ret = 0;
avail = __qcom_scm_is_call_available(__scm->dev,
QCOM_SCM_SVC_BOOT,
QCOM_SCM_SET_DLOAD_MODE);
if (avail) {
ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
} else if (__scm->dload_mode_addr) {
ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr,
enable ? QCOM_SCM_SET_DLOAD_MODE : 0);
} else {
dev_err(__scm->dev,
"No available mechanism for setting download mode\n");
}
if (ret)
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
}
static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
{
struct device_node *tcsr;
struct device_node *np = dev->of_node;
struct resource res;
u32 offset;
int ret;
tcsr = of_parse_phandle(np, "qcom,dload-mode", 0);
if (!tcsr)
return 0;
ret = of_address_to_resource(tcsr, 0, &res);
of_node_put(tcsr);
if (ret)
return ret;
ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset);
if (ret < 0)
return ret;
*addr = res.start + offset;
return 0;
}
/** /**
* qcom_scm_is_available() - Checks if SCM is available * qcom_scm_is_available() - Checks if SCM is available
*/ */
...@@ -370,6 +425,10 @@ static int qcom_scm_probe(struct platform_device *pdev) ...@@ -370,6 +425,10 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (!scm) if (!scm)
return -ENOMEM; return -ENOMEM;
ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
if (ret < 0)
return ret;
clks = (unsigned long)of_device_get_match_data(&pdev->dev); clks = (unsigned long)of_device_get_match_data(&pdev->dev);
if (clks & SCM_HAS_CORE_CLK) { if (clks & SCM_HAS_CORE_CLK) {
scm->core_clk = devm_clk_get(&pdev->dev, "core"); scm->core_clk = devm_clk_get(&pdev->dev, "core");
...@@ -418,9 +477,24 @@ static int qcom_scm_probe(struct platform_device *pdev) ...@@ -418,9 +477,24 @@ static int qcom_scm_probe(struct platform_device *pdev)
__qcom_scm_init(); __qcom_scm_init();
/*
* If requested enable "download mode", from this point on warmboot
* will cause the the boot stages to enter download mode, unless
* disabled below by a clean shutdown/reboot.
*/
if (download_mode)
qcom_scm_set_download_mode(true);
return 0; return 0;
} }
static void qcom_scm_shutdown(struct platform_device *pdev)
{
/* Clean shutdown, disable download mode to allow normal restart */
if (download_mode)
qcom_scm_set_download_mode(false);
}
static const struct of_device_id qcom_scm_dt_match[] = { static const struct of_device_id qcom_scm_dt_match[] = {
{ .compatible = "qcom,scm-apq8064", { .compatible = "qcom,scm-apq8064",
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
...@@ -448,6 +522,7 @@ static struct platform_driver qcom_scm_driver = { ...@@ -448,6 +522,7 @@ static struct platform_driver qcom_scm_driver = {
.of_match_table = qcom_scm_dt_match, .of_match_table = qcom_scm_dt_match,
}, },
.probe = qcom_scm_probe, .probe = qcom_scm_probe,
.shutdown = qcom_scm_shutdown,
}; };
static int __init qcom_scm_init(void) static int __init qcom_scm_init(void)
......
...@@ -14,9 +14,11 @@ ...@@ -14,9 +14,11 @@
#define QCOM_SCM_SVC_BOOT 0x1 #define QCOM_SCM_SVC_BOOT 0x1
#define QCOM_SCM_BOOT_ADDR 0x1 #define QCOM_SCM_BOOT_ADDR 0x1
#define QCOM_SCM_SET_DLOAD_MODE 0x10
#define QCOM_SCM_BOOT_ADDR_MC 0x11 #define QCOM_SCM_BOOT_ADDR_MC 0x11
#define QCOM_SCM_SET_REMOTE_STATE 0xa #define QCOM_SCM_SET_REMOTE_STATE 0xa
extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id); extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id);
extern int __qcom_scm_set_dload_mode(struct device *dev, bool enable);
#define QCOM_SCM_FLAG_HLOS 0x01 #define QCOM_SCM_FLAG_HLOS 0x01
#define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment