Commit d7efc352 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rproc-v4.14' of git://github.com/andersson/remoteproc

Pull remoteproc updates from Bjorn Andersson:
 "This adds and improves remoteproc support for TI DA8xx/OMAP-L13x DSP,
  TI Keystone 66AK2G DSP and iMX6SX/7D Cortex M4 coprocessors. It
  introduces the Qualcomm restart notifier and a few fixes"

* tag 'rproc-v4.14' of git://github.com/andersson/remoteproc:
  remoteproc: Introduce rproc handle accessor for children
  remoteproc: qcom: Make ssr_notifiers local
  remoteproc: Stop subdevices in reverse order
  remoteproc: imx_rproc: add a NXP/Freescale imx_rproc driver
  remoteproc: dt: Provide bindings for iMX6SX/7D Remote Processor Controller driver
  remoteproc: qcom: Use PTR_ERR_OR_ZERO
  remoteproc: st: explicitly request exclusive reset control
  remoteproc: qcom: explicitly request exclusive reset control
  remoteproc/keystone: explicitly request exclusive reset control
  remoteproc/keystone: Add support for Keystone 66AK2G SOCs
  remoteproc/davinci: Add device tree support for OMAP-L138 DSP
  dt-bindings: remoteproc: Add bindings for Davinci DSP processors
  remoteproc/davinci: Add support to parse internal memories
  remoteproc/davinci: Switch to platform_get_resource_byname()
  remoteproc: make device_type const
  soc: qcom: GLINK SSR notifier
  remoteproc: qcom: Add support for SSR notifications
  remoteproc: Merge __rproc_boot() with rproc_boot()
parents 8c1d70b2 7c89717f
NXP iMX6SX/iMX7D Co-Processor Bindings
----------------------------------------
This binding provides support for ARM Cortex M4 Co-processor found on some
NXP iMX SoCs.
Required properties:
- compatible Should be one of:
"fsl,imx7d-cm4"
"fsl,imx6sx-cm4"
- clocks Clock for co-processor (See: ../clock/clock-bindings.txt)
- syscon Phandle to syscon block which provide access to
System Reset Controller
Optional properties:
- memory-region list of phandels to the reserved memory regions.
(See: ../reserved-memory/reserved-memory.txt)
Example:
m4_reserved_sysmem1: cm4@80000000 {
reg = <0x80000000 0x80000>;
};
m4_reserved_sysmem2: cm4@81000000 {
reg = <0x81000000 0x80000>;
};
imx7d-cm4 {
compatible = "fsl,imx7d-cm4";
memory-region = <&m4_reserved_sysmem1>, <&m4_reserved_sysmem2>;
syscon = <&src>;
clocks = <&clks IMX7D_ARM_M4_ROOT_CLK>;
};
TI Davinci DSP devices
=======================
Binding status: Unstable - Subject to changes for DT representation of clocks
and resets
The TI Davinci family of SoCs usually contains a TI DSP Core sub-system that
is used to offload some of the processor-intensive tasks or algorithms, for
achieving various system level goals.
The processor cores in the sub-system usually contain additional sub-modules
like L1 and/or L2 caches/SRAMs, an Interrupt Controller, an external memory
controller, a dedicated local power/sleep controller etc. The DSP processor
core used in Davinci SoCs is usually a C674x DSP CPU.
DSP Device Node:
================
Each DSP Core sub-system is represented as a single DT node.
Required properties:
--------------------
The following are the mandatory properties:
- compatible: Should be one of the following,
"ti,da850-dsp" for DSPs on OMAP-L138 SoCs
- reg: Should contain an entry for each value in 'reg-names'.
Each entry should have the memory region's start address
and the size of the region, the representation matching
the parent node's '#address-cells' and '#size-cells' values.
- reg-names: Should contain strings with the following names, each
representing a specific internal memory region or a
specific register space,
"l2sram", "l1pram", "l1dram", "host1cfg", "chipsig_base"
- interrupts: Should contain the interrupt number used to receive the
interrupts from the DSP. The value should follow the
interrupt-specifier format as dictated by the
'interrupt-parent' node.
- memory-region: phandle to the reserved memory node to be associated
with the remoteproc device. The reserved memory node
can be a CMA memory node, and should be defined as
per the bindings in
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
Optional properties:
--------------------
- interrupt-parent: phandle to the interrupt controller node. This property
is needed if the device node hierarchy doesn't have an
interrupt controller.
Example:
--------
/* DSP Reserved Memory node */
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
dsp_memory_region: dsp-memory@c3000000 {
compatible = "shared-dma-pool";
reg = <0xc3000000 0x1000000>;
reusable;
};
};
/* DSP node */
{
dsp: dsp@11800000 {
compatible = "ti,da850-dsp";
reg = <0x11800000 0x40000>,
<0x11e00000 0x8000>,
<0x11f00000 0x8000>,
<0x01c14044 0x4>,
<0x01c14174 0x8>;
reg-names = "l2sram", "l1pram", "l1dram", "host1cfg",
"chipsig";
interrupt-parent = <&intc>;
interrupts = <28>;
memory-region = <&dsp_memory_region>;
};
};
...@@ -26,6 +26,7 @@ The following are the mandatory properties: ...@@ -26,6 +26,7 @@ The following are the mandatory properties:
"ti,k2hk-dsp" for DSPs on Keystone 2 66AK2H/K SoCs "ti,k2hk-dsp" for DSPs on Keystone 2 66AK2H/K SoCs
"ti,k2l-dsp" for DSPs on Keystone 2 66AK2L SoCs "ti,k2l-dsp" for DSPs on Keystone 2 66AK2L SoCs
"ti,k2e-dsp" for DSPs on Keystone 2 66AK2E SoCs "ti,k2e-dsp" for DSPs on Keystone 2 66AK2E SoCs
"ti,k2g-dsp" for DSPs on Keystone 2 66AK2G SoCs
- reg: Should contain an entry for each value in 'reg-names'. - reg: Should contain an entry for each value in 'reg-names'.
Each entry should have the memory region's start address Each entry should have the memory region's start address
...@@ -37,20 +38,18 @@ The following are the mandatory properties: ...@@ -37,20 +38,18 @@ The following are the mandatory properties:
should be defined in this order, should be defined in this order,
"l2sram", "l1pram", "l1dram" "l2sram", "l1pram", "l1dram"
- clocks: Should contain the device's input clock, and should be
defined as per the bindings in,
Documentation/devicetree/bindings/clock/keystone-gate.txt
- ti,syscon-dev: Should be a pair of the phandle to the Keystone Device - ti,syscon-dev: Should be a pair of the phandle to the Keystone Device
State Control node, and the register offset of the DSP State Control node, and the register offset of the DSP
boot address register within that node's address space. boot address register within that node's address space.
- resets: Should contain the phandle to the reset controller node - resets: Should contain the phandle to the reset controller node
managing the resets for this device, and a reset managing the resets for this device, and a reset
specifier. Please refer to the following reset bindings specifier. Please refer to either of the following reset
for the reset argument specifier as per SoC, bindings for the reset argument specifier as per SoC,
Documentation/devicetree/bindings/reset/ti-syscon-reset.txt Documentation/devicetree/bindings/reset/ti-syscon-reset.txt
for 66AK2HK/66AK2L/66AK2E SoCs for 66AK2HK/66AK2L/66AK2E SoCs or,
Documentation/devicetree/bindings/reset/ti,sci-reset.txt
for 66AK2G SoCs
- interrupt-parent: Should contain a phandle to the Keystone 2 IRQ controller - interrupt-parent: Should contain a phandle to the Keystone 2 IRQ controller
IP node that is used by the ARM CorePac processor to IP node that is used by the ARM CorePac processor to
...@@ -75,6 +74,22 @@ The following are the mandatory properties: ...@@ -75,6 +74,22 @@ The following are the mandatory properties:
The gpio device to be used is as per the bindings in, The gpio device to be used is as per the bindings in,
Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt
SoC-specific Required properties:
---------------------------------
The following are mandatory properties for Keystone 2 66AK2HK, 66AK2L and 66AK2E
SoCs only:
- clocks: Should contain the device's input clock, and should be
defined as per the bindings in,
Documentation/devicetree/bindings/clock/keystone-gate.txt
The following are mandatory properties for Keystone 2 66AK2G SoCs only:
- power-domains: Should contain a phandle to a PM domain provider node
and an args specifier containing the DSP device id
value. This property is as per the binding,
Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
Optional properties: Optional properties:
-------------------- --------------------
...@@ -85,8 +100,10 @@ Optional properties: ...@@ -85,8 +100,10 @@ Optional properties:
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
Example: Examples:
-------- ---------
1.
/* 66AK2H/K DSP aliases */ /* 66AK2H/K DSP aliases */
aliases { aliases {
rproc0 = &dsp0; rproc0 = &dsp0;
...@@ -131,3 +148,41 @@ Example: ...@@ -131,3 +148,41 @@ Example:
}; };
}; };
2.
/* 66AK2G DSP alias */
aliases {
rproc0 = &dsp0;
};
/* 66AK2G DSP memory node */
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
dsp_common_memory: dsp-common-memory@81f800000 {
compatible = "shared-dma-pool";
reg = <0x00000008 0x1f800000 0x00000000 0x800000>;
reusable;
};
};
/* 66AK2G DSP node */
soc {
dsp0: dsp@10800000 {
compatible = "ti,k2g-dsp";
reg = <0x10800000 0x00100000>,
<0x10e00000 0x00008000>,
<0x10f00000 0x00008000>;
reg-names = "l2sram", "l1pram", "l1dram";
power-domains = <&k2g_pds 0x0046>;
ti,syscon-dev = <&devctrl 0x40>;
resets = <&k2g_reset 0x0046 0x1>;
interrupt-parent = <&kirq0>;
interrupts = <0 8>;
interrupt-names = "vring", "exception";
kick-gpios = <&dspgpio0 27 0>;
memory-region = <&dsp_common_memory>;
};
};
...@@ -12,6 +12,15 @@ config REMOTEPROC ...@@ -12,6 +12,15 @@ config REMOTEPROC
if REMOTEPROC if REMOTEPROC
config IMX_REMOTEPROC
tristate "IMX6/7 remoteproc support"
depends on SOC_IMX6SX || SOC_IMX7D
help
Say y here to support iMX's remote processors (Cortex M4
on iMX7D) via the remote processor framework.
It's safe to say N here.
config OMAP_REMOTEPROC config OMAP_REMOTEPROC
tristate "OMAP remoteproc support" tristate "OMAP remoteproc support"
depends on HAS_DMA depends on HAS_DMA
......
...@@ -8,6 +8,7 @@ remoteproc-y += remoteproc_debugfs.o ...@@ -8,6 +8,7 @@ remoteproc-y += remoteproc_debugfs.o
remoteproc-y += remoteproc_sysfs.o remoteproc-y += remoteproc_sysfs.o
remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
...@@ -38,9 +39,27 @@ MODULE_PARM_DESC(da8xx_fw_name, ...@@ -38,9 +39,27 @@ MODULE_PARM_DESC(da8xx_fw_name,
#define SYSCFG_CHIPSIG3 BIT(3) #define SYSCFG_CHIPSIG3 BIT(3)
#define SYSCFG_CHIPSIG4 BIT(4) #define SYSCFG_CHIPSIG4 BIT(4)
#define DA8XX_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1)
/**
* struct da8xx_rproc_mem - internal memory structure
* @cpu_addr: MPU virtual address of the memory region
* @bus_addr: Bus address used to access the memory region
* @dev_addr: Device address of the memory region from DSP view
* @size: Size of the memory region
*/
struct da8xx_rproc_mem {
void __iomem *cpu_addr;
phys_addr_t bus_addr;
u32 dev_addr;
size_t size;
};
/** /**
* struct da8xx_rproc - da8xx remote processor instance state * struct da8xx_rproc - da8xx remote processor instance state
* @rproc: rproc handle * @rproc: rproc handle
* @mem: internal memory regions data
* @num_mems: number of internal memory regions
* @dsp_clk: placeholder for platform's DSP clk * @dsp_clk: placeholder for platform's DSP clk
* @ack_fxn: chip-specific ack function for ack'ing irq * @ack_fxn: chip-specific ack function for ack'ing irq
* @irq_data: ack_fxn function parameter * @irq_data: ack_fxn function parameter
...@@ -50,6 +69,8 @@ MODULE_PARM_DESC(da8xx_fw_name, ...@@ -50,6 +69,8 @@ MODULE_PARM_DESC(da8xx_fw_name,
*/ */
struct da8xx_rproc { struct da8xx_rproc {
struct rproc *rproc; struct rproc *rproc;
struct da8xx_rproc_mem *mem;
int num_mems;
struct clk *dsp_clk; struct clk *dsp_clk;
void (*ack_fxn)(struct irq_data *data); void (*ack_fxn)(struct irq_data *data);
struct irq_data *irq_data; struct irq_data *irq_data;
...@@ -158,6 +179,44 @@ static const struct rproc_ops da8xx_rproc_ops = { ...@@ -158,6 +179,44 @@ static const struct rproc_ops da8xx_rproc_ops = {
.kick = da8xx_rproc_kick, .kick = da8xx_rproc_kick,
}; };
static int da8xx_rproc_get_internal_memories(struct platform_device *pdev,
struct da8xx_rproc *drproc)
{
static const char * const mem_names[] = {"l2sram", "l1pram", "l1dram"};
int num_mems = ARRAY_SIZE(mem_names);
struct device *dev = &pdev->dev;
struct resource *res;
int i;
drproc->mem = devm_kcalloc(dev, num_mems, sizeof(*drproc->mem),
GFP_KERNEL);
if (!drproc->mem)
return -ENOMEM;
for (i = 0; i < num_mems; i++) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
mem_names[i]);
drproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(drproc->mem[i].cpu_addr)) {
dev_err(dev, "failed to parse and map %s memory\n",
mem_names[i]);
return PTR_ERR(drproc->mem[i].cpu_addr);
}
drproc->mem[i].bus_addr = res->start;
drproc->mem[i].dev_addr =
res->start & DA8XX_RPROC_LOCAL_ADDRESS_MASK;
drproc->mem[i].size = resource_size(res);
dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %p da 0x%x\n",
mem_names[i], &drproc->mem[i].bus_addr,
drproc->mem[i].size, drproc->mem[i].cpu_addr,
drproc->mem[i].dev_addr);
}
drproc->num_mems = num_mems;
return 0;
}
static int da8xx_rproc_probe(struct platform_device *pdev) static int da8xx_rproc_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -184,12 +243,14 @@ static int da8xx_rproc_probe(struct platform_device *pdev) ...@@ -184,12 +243,14 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); bootreg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"host1cfg");
bootreg = devm_ioremap_resource(dev, bootreg_res); bootreg = devm_ioremap_resource(dev, bootreg_res);
if (IS_ERR(bootreg)) if (IS_ERR(bootreg))
return PTR_ERR(bootreg); return PTR_ERR(bootreg);
chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); chipsig_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"chipsig");
chipsig = devm_ioremap_resource(dev, chipsig_res); chipsig = devm_ioremap_resource(dev, chipsig_res);
if (IS_ERR(chipsig)) if (IS_ERR(chipsig))
return PTR_ERR(chipsig); return PTR_ERR(chipsig);
...@@ -201,16 +262,31 @@ static int da8xx_rproc_probe(struct platform_device *pdev) ...@@ -201,16 +262,31 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
return PTR_ERR(dsp_clk); return PTR_ERR(dsp_clk);
} }
if (dev->of_node) {
ret = of_reserved_mem_device_init(dev);
if (ret) {
dev_err(dev, "device does not have specific CMA pool: %d\n",
ret);
return ret;
}
}
rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name, rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
sizeof(*drproc)); sizeof(*drproc));
if (!rproc) if (!rproc) {
return -ENOMEM; ret = -ENOMEM;
goto free_mem;
}
drproc = rproc->priv; drproc = rproc->priv;
drproc->rproc = rproc; drproc->rproc = rproc;
drproc->dsp_clk = dsp_clk; drproc->dsp_clk = dsp_clk;
rproc->has_iommu = false; rproc->has_iommu = false;
ret = da8xx_rproc_get_internal_memories(pdev, drproc);
if (ret)
goto free_rproc;
platform_set_drvdata(pdev, rproc); platform_set_drvdata(pdev, rproc);
/* everything the ISR needs is now setup, so hook it up */ /* everything the ISR needs is now setup, so hook it up */
...@@ -247,7 +323,9 @@ static int da8xx_rproc_probe(struct platform_device *pdev) ...@@ -247,7 +323,9 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
free_rproc: free_rproc:
rproc_free(rproc); rproc_free(rproc);
free_mem:
if (dev->of_node)
of_reserved_mem_device_release(dev);
return ret; return ret;
} }
...@@ -255,6 +333,7 @@ static int da8xx_rproc_remove(struct platform_device *pdev) ...@@ -255,6 +333,7 @@ static int da8xx_rproc_remove(struct platform_device *pdev)
{ {
struct rproc *rproc = platform_get_drvdata(pdev); struct rproc *rproc = platform_get_drvdata(pdev);
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
struct device *dev = &pdev->dev;
/* /*
* The devm subsystem might end up releasing things before * The devm subsystem might end up releasing things before
...@@ -265,15 +344,24 @@ static int da8xx_rproc_remove(struct platform_device *pdev) ...@@ -265,15 +344,24 @@ static int da8xx_rproc_remove(struct platform_device *pdev)
rproc_del(rproc); rproc_del(rproc);
rproc_free(rproc); rproc_free(rproc);
if (dev->of_node)
of_reserved_mem_device_release(dev);
return 0; return 0;
} }
static const struct of_device_id davinci_rproc_of_match[] __maybe_unused = {
{ .compatible = "ti,da850-dsp", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, davinci_rproc_of_match);
static struct platform_driver da8xx_rproc_driver = { static struct platform_driver da8xx_rproc_driver = {
.probe = da8xx_rproc_probe, .probe = da8xx_rproc_probe,
.remove = da8xx_rproc_remove, .remove = da8xx_rproc_remove,
.driver = { .driver = {
.name = "davinci-rproc", .name = "davinci-rproc",
.of_match_table = of_match_ptr(davinci_rproc_of_match),
}, },
}; };
......
This diff is collapsed.
...@@ -410,7 +410,7 @@ static int keystone_rproc_probe(struct platform_device *pdev) ...@@ -410,7 +410,7 @@ static int keystone_rproc_probe(struct platform_device *pdev)
if (ret) if (ret)
goto free_rproc; goto free_rproc;
ksproc->reset = devm_reset_control_get(dev, NULL); ksproc->reset = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(ksproc->reset)) { if (IS_ERR(ksproc->reset)) {
ret = PTR_ERR(ksproc->reset); ret = PTR_ERR(ksproc->reset);
goto free_rproc; goto free_rproc;
...@@ -505,6 +505,7 @@ static const struct of_device_id keystone_rproc_of_match[] = { ...@@ -505,6 +505,7 @@ static const struct of_device_id keystone_rproc_of_match[] = {
{ .compatible = "ti,k2hk-dsp", }, { .compatible = "ti,k2hk-dsp", },
{ .compatible = "ti,k2l-dsp", }, { .compatible = "ti,k2l-dsp", },
{ .compatible = "ti,k2e-dsp", }, { .compatible = "ti,k2e-dsp", },
{ .compatible = "ti,k2g-dsp", },
{ /* sentinel */ }, { /* sentinel */ },
}; };
MODULE_DEVICE_TABLE(of, keystone_rproc_of_match); MODULE_DEVICE_TABLE(of, keystone_rproc_of_match);
......
...@@ -38,6 +38,7 @@ struct adsp_data { ...@@ -38,6 +38,7 @@ struct adsp_data {
const char *firmware_name; const char *firmware_name;
int pas_id; int pas_id;
bool has_aggre2_clk; bool has_aggre2_clk;
const char *ssr_name;
}; };
struct qcom_adsp { struct qcom_adsp {
...@@ -72,6 +73,7 @@ struct qcom_adsp { ...@@ -72,6 +73,7 @@ struct qcom_adsp {
size_t mem_size; size_t mem_size;
struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
}; };
static int adsp_load(struct rproc *rproc, const struct firmware *fw) static int adsp_load(struct rproc *rproc, const struct firmware *fw)
...@@ -266,10 +268,7 @@ static int adsp_init_regulator(struct qcom_adsp *adsp) ...@@ -266,10 +268,7 @@ static int adsp_init_regulator(struct qcom_adsp *adsp)
regulator_set_load(adsp->cx_supply, 100000); regulator_set_load(adsp->cx_supply, 100000);
adsp->px_supply = devm_regulator_get(adsp->dev, "px"); adsp->px_supply = devm_regulator_get(adsp->dev, "px");
if (IS_ERR(adsp->px_supply)) return PTR_ERR_OR_ZERO(adsp->px_supply);
return PTR_ERR(adsp->px_supply);
return 0;
} }
static int adsp_request_irq(struct qcom_adsp *adsp, static int adsp_request_irq(struct qcom_adsp *adsp,
...@@ -402,6 +401,7 @@ static int adsp_probe(struct platform_device *pdev) ...@@ -402,6 +401,7 @@ static int adsp_probe(struct platform_device *pdev)
} }
qcom_add_smd_subdev(rproc, &adsp->smd_subdev); qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
ret = rproc_add(rproc); ret = rproc_add(rproc);
if (ret) if (ret)
...@@ -423,6 +423,7 @@ static int adsp_remove(struct platform_device *pdev) ...@@ -423,6 +423,7 @@ static int adsp_remove(struct platform_device *pdev)
rproc_del(adsp->rproc); rproc_del(adsp->rproc);
qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
rproc_free(adsp->rproc); rproc_free(adsp->rproc);
return 0; return 0;
...@@ -433,6 +434,7 @@ static const struct adsp_data adsp_resource_init = { ...@@ -433,6 +434,7 @@ static const struct adsp_data adsp_resource_init = {
.firmware_name = "adsp.mdt", .firmware_name = "adsp.mdt",
.pas_id = 1, .pas_id = 1,
.has_aggre2_clk = false, .has_aggre2_clk = false,
.ssr_name = "lpass",
}; };
static const struct adsp_data slpi_resource_init = { static const struct adsp_data slpi_resource_init = {
...@@ -440,6 +442,7 @@ static const struct adsp_data slpi_resource_init = { ...@@ -440,6 +442,7 @@ static const struct adsp_data slpi_resource_init = {
.firmware_name = "slpi.mdt", .firmware_name = "slpi.mdt",
.pas_id = 12, .pas_id = 12,
.has_aggre2_clk = true, .has_aggre2_clk = true,
.ssr_name = "dsps",
}; };
static const struct of_device_id adsp_of_match[] = { static const struct of_device_id adsp_of_match[] = {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/notifier.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
#include <linux/rpmsg/qcom_smd.h> #include <linux/rpmsg/qcom_smd.h>
...@@ -25,6 +26,9 @@ ...@@ -25,6 +26,9 @@
#include "qcom_common.h" #include "qcom_common.h"
#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
#define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
static BLOCKING_NOTIFIER_HEAD(ssr_notifiers);
/** /**
* qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc
...@@ -51,7 +55,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev) ...@@ -51,7 +55,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev)
smd->edge = qcom_smd_register_edge(smd->dev, smd->node); smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
return IS_ERR(smd->edge) ? PTR_ERR(smd->edge) : 0; return PTR_ERR_OR_ZERO(smd->edge);
} }
static void smd_subdev_remove(struct rproc_subdev *subdev) static void smd_subdev_remove(struct rproc_subdev *subdev)
...@@ -92,5 +96,72 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) ...@@ -92,5 +96,72 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
} }
EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
/**
* qcom_register_ssr_notifier() - register SSR notification handler
* @nb: notifier_block to notify for restart notifications
*
* Returns 0 on success, negative errno on failure.
*
* This register the @notify function as handler for restart notifications. As
* remote processors are stopped this function will be called, with the SSR
* name passed as a parameter.
*/
int qcom_register_ssr_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&ssr_notifiers, nb);
}
EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
/**
* qcom_unregister_ssr_notifier() - unregister SSR notification handler
* @nb: notifier_block to unregister
*/
void qcom_unregister_ssr_notifier(struct notifier_block *nb)
{
blocking_notifier_chain_unregister(&ssr_notifiers, nb);
}
EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
static int ssr_notify_start(struct rproc_subdev *subdev)
{
return 0;
}
static void ssr_notify_stop(struct rproc_subdev *subdev)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name);
}
/**
* qcom_add_ssr_subdev() - register subdevice as restart notification source
* @rproc: rproc handle
* @ssr: SSR subdevice handle
* @ssr_name: identifier to use for notifications originating from @rproc
*
* As the @ssr is registered with the @rproc SSR events will be sent to all
* registered listeners in the system as the remoteproc is shut down.
*/
void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
const char *ssr_name)
{
ssr->name = ssr_name;
rproc_add_subdev(rproc, &ssr->subdev, ssr_notify_start, ssr_notify_stop);
}
EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
/**
* qcom_remove_ssr_subdev() - remove subdevice as restart notification source
* @rproc: rproc handle
* @ssr: SSR subdevice handle
*/
void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
{
rproc_remove_subdev(rproc, &ssr->subdev);
}
EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -12,6 +12,12 @@ struct qcom_rproc_subdev { ...@@ -12,6 +12,12 @@ struct qcom_rproc_subdev {
struct qcom_smd_edge *edge; struct qcom_smd_edge *edge;
}; };
struct qcom_rproc_ssr {
struct rproc_subdev subdev;
const char *name;
};
struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
const struct firmware *fw, const struct firmware *fw,
int *tablesz); int *tablesz);
...@@ -19,4 +25,8 @@ struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, ...@@ -19,4 +25,8 @@ struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
const char *ssr_name);
void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr);
#endif #endif
...@@ -153,6 +153,7 @@ struct q6v5 { ...@@ -153,6 +153,7 @@ struct q6v5 {
size_t mpss_size; size_t mpss_size;
struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
}; };
static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, static int q6v5_regulator_init(struct device *dev, struct reg_info *regs,
...@@ -867,7 +868,8 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks, ...@@ -867,7 +868,8 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks,
static int q6v5_init_reset(struct q6v5 *qproc) static int q6v5_init_reset(struct q6v5 *qproc)
{ {
qproc->mss_restart = devm_reset_control_get(qproc->dev, NULL); qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
NULL);
if (IS_ERR(qproc->mss_restart)) { if (IS_ERR(qproc->mss_restart)) {
dev_err(qproc->dev, "failed to acquire mss restart\n"); dev_err(qproc->dev, "failed to acquire mss restart\n");
return PTR_ERR(qproc->mss_restart); return PTR_ERR(qproc->mss_restart);
...@@ -1038,6 +1040,7 @@ static int q6v5_probe(struct platform_device *pdev) ...@@ -1038,6 +1040,7 @@ static int q6v5_probe(struct platform_device *pdev)
} }
qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
ret = rproc_add(rproc); ret = rproc_add(rproc);
if (ret) if (ret)
...@@ -1058,6 +1061,7 @@ static int q6v5_remove(struct platform_device *pdev) ...@@ -1058,6 +1061,7 @@ static int q6v5_remove(struct platform_device *pdev)
rproc_del(qproc->rproc); rproc_del(qproc->rproc);
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
rproc_free(qproc->rproc); rproc_free(qproc->rproc);
return 0; return 0;
......
...@@ -794,7 +794,7 @@ static void rproc_remove_subdevices(struct rproc *rproc) ...@@ -794,7 +794,7 @@ static void rproc_remove_subdevices(struct rproc *rproc)
{ {
struct rproc_subdev *subdev; struct rproc_subdev *subdev;
list_for_each_entry(subdev, &rproc->subdevs, node) list_for_each_entry_reverse(subdev, &rproc->subdevs, node)
subdev->remove(subdev); subdev->remove(subdev);
} }
...@@ -1119,7 +1119,7 @@ static void rproc_crash_handler_work(struct work_struct *work) ...@@ -1119,7 +1119,7 @@ static void rproc_crash_handler_work(struct work_struct *work)
} }
/** /**
* __rproc_boot() - boot a remote processor * rproc_boot() - boot a remote processor
* @rproc: handle of a remote processor * @rproc: handle of a remote processor
* *
* Boot a remote processor (i.e. load its firmware, power it on, ...). * Boot a remote processor (i.e. load its firmware, power it on, ...).
...@@ -1129,7 +1129,7 @@ static void rproc_crash_handler_work(struct work_struct *work) ...@@ -1129,7 +1129,7 @@ static void rproc_crash_handler_work(struct work_struct *work)
* *
* Returns 0 on success, and an appropriate error value otherwise. * Returns 0 on success, and an appropriate error value otherwise.
*/ */
static int __rproc_boot(struct rproc *rproc) int rproc_boot(struct rproc *rproc)
{ {
const struct firmware *firmware_p; const struct firmware *firmware_p;
struct device *dev; struct device *dev;
...@@ -1180,15 +1180,6 @@ static int __rproc_boot(struct rproc *rproc) ...@@ -1180,15 +1180,6 @@ static int __rproc_boot(struct rproc *rproc)
mutex_unlock(&rproc->lock); mutex_unlock(&rproc->lock);
return ret; return ret;
} }
/**
* rproc_boot() - boot a remote processor
* @rproc: handle of a remote processor
*/
int rproc_boot(struct rproc *rproc)
{
return __rproc_boot(rproc);
}
EXPORT_SYMBOL(rproc_boot); EXPORT_SYMBOL(rproc_boot);
/** /**
...@@ -1369,7 +1360,7 @@ static void rproc_type_release(struct device *dev) ...@@ -1369,7 +1360,7 @@ static void rproc_type_release(struct device *dev)
kfree(rproc); kfree(rproc);
} }
static struct device_type rproc_type = { static const struct device_type rproc_type = {
.name = "remoteproc", .name = "remoteproc",
.release = rproc_type_release, .release = rproc_type_release,
}; };
...@@ -1440,6 +1431,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, ...@@ -1440,6 +1431,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
rproc->dev.parent = dev; rproc->dev.parent = dev;
rproc->dev.type = &rproc_type; rproc->dev.type = &rproc_type;
rproc->dev.class = &rproc_class; rproc->dev.class = &rproc_class;
rproc->dev.driver_data = rproc;
/* Assign a unique device index and name */ /* Assign a unique device index and name */
rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL);
...@@ -1578,6 +1570,23 @@ void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev) ...@@ -1578,6 +1570,23 @@ void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev)
} }
EXPORT_SYMBOL(rproc_remove_subdev); EXPORT_SYMBOL(rproc_remove_subdev);
/**
* rproc_get_by_child() - acquire rproc handle of @dev's ancestor
* @dev: child device to find ancestor of
*
* Returns the ancestor rproc instance, or NULL if not found.
*/
struct rproc *rproc_get_by_child(struct device *dev)
{
for (dev = dev->parent; dev; dev = dev->parent) {
if (dev->type == &rproc_type)
return dev->driver_data;
}
return NULL;
}
EXPORT_SYMBOL(rproc_get_by_child);
/** /**
* rproc_report_crash() - rproc crash reporter function * rproc_report_crash() - rproc crash reporter function
* @rproc: remote processor * @rproc: remote processor
......
...@@ -48,7 +48,6 @@ struct rproc_fw_ops { ...@@ -48,7 +48,6 @@ struct rproc_fw_ops {
/* from remoteproc_core.c */ /* from remoteproc_core.c */
void rproc_release(struct kref *kref); void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
int rproc_boot_nowait(struct rproc *rproc);
void rproc_vdev_release(struct kref *ref); void rproc_vdev_release(struct kref *ref);
/* from remoteproc_virtio.c */ /* from remoteproc_virtio.c */
......
...@@ -212,7 +212,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev) ...@@ -212,7 +212,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev)
int err; int err;
if (ddata->config->sw_reset) { if (ddata->config->sw_reset) {
ddata->sw_reset = devm_reset_control_get(dev, "sw_reset"); ddata->sw_reset = devm_reset_control_get_exclusive(dev,
"sw_reset");
if (IS_ERR(ddata->sw_reset)) { if (IS_ERR(ddata->sw_reset)) {
dev_err(dev, "Failed to get S/W Reset\n"); dev_err(dev, "Failed to get S/W Reset\n");
return PTR_ERR(ddata->sw_reset); return PTR_ERR(ddata->sw_reset);
...@@ -220,7 +221,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev) ...@@ -220,7 +221,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev)
} }
if (ddata->config->pwr_reset) { if (ddata->config->pwr_reset) {
ddata->pwr_reset = devm_reset_control_get(dev, "pwr_reset"); ddata->pwr_reset = devm_reset_control_get_exclusive(dev,
"pwr_reset");
if (IS_ERR(ddata->pwr_reset)) { if (IS_ERR(ddata->pwr_reset)) {
dev_err(dev, "Failed to get Power Reset\n"); dev_err(dev, "Failed to get Power Reset\n");
return PTR_ERR(ddata->pwr_reset); return PTR_ERR(ddata->pwr_reset);
......
# #
# QCOM Soc drivers # QCOM Soc drivers
# #
config QCOM_GLINK_SSR
tristate "Qualcomm Glink SSR driver"
depends on RPMSG
depends on QCOM_RPROC_COMMON
help
Say y here to enable GLINK SSR support. The GLINK SSR driver
implements the SSR protocol for notifying the remote processor about
neighboring subsystems going up or down.
config QCOM_GSBI config QCOM_GSBI
tristate "QCOM General Serial Bus Interface" tristate "QCOM General Serial Bus Interface"
depends on ARCH_QCOM depends on ARCH_QCOM
......
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_PM) += spm.o
......
/*
* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
* Copyright (c) 2017, Linaro Ltd.
*
* 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/rpmsg.h>
#include <linux/remoteproc/qcom_rproc.h>
/**
* struct do_cleanup_msg - The data structure for an SSR do_cleanup message
* version: The G-Link SSR protocol version
* command: The G-Link SSR command - do_cleanup
* seq_num: Sequence number
* name_len: Length of the name of the subsystem being restarted
* name: G-Link edge name of the subsystem being restarted
*/
struct do_cleanup_msg {
__le32 version;
__le32 command;
__le32 seq_num;
__le32 name_len;
char name[32];
};
/**
* struct cleanup_done_msg - The data structure for an SSR cleanup_done message
* version: The G-Link SSR protocol version
* response: The G-Link SSR response to a do_cleanup command, cleanup_done
* seq_num: Sequence number
*/
struct cleanup_done_msg {
__le32 version;
__le32 response;
__le32 seq_num;
};
/**
* G-Link SSR protocol commands
*/
#define GLINK_SSR_DO_CLEANUP 0
#define GLINK_SSR_CLEANUP_DONE 1
struct glink_ssr {
struct device *dev;
struct rpmsg_endpoint *ept;
struct notifier_block nb;
u32 seq_num;
struct completion completion;
};
static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev,
void *data, int len, void *priv, u32 addr)
{
struct cleanup_done_msg *msg = data;
struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
if (len < sizeof(*msg)) {
dev_err(ssr->dev, "message too short\n");
return -EINVAL;
}
if (le32_to_cpu(msg->version) != 0)
return -EINVAL;
if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE)
return 0;
if (le32_to_cpu(msg->seq_num) != ssr->seq_num) {
dev_err(ssr->dev, "invalid sequence number of response\n");
return -EINVAL;
}
complete(&ssr->completion);
return 0;
}
static int qcom_glink_ssr_notify(struct notifier_block *nb, unsigned long event,
void *data)
{
struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb);
struct do_cleanup_msg msg;
char *ssr_name = data;
int ret;
ssr->seq_num++;
reinit_completion(&ssr->completion);
memset(&msg, 0, sizeof(msg));
msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP);
msg.seq_num = cpu_to_le32(ssr->seq_num);
msg.name_len = cpu_to_le32(strlen(ssr_name));
strlcpy(msg.name, ssr_name, sizeof(msg.name));
ret = rpmsg_send(ssr->ept, &msg, sizeof(msg));
if (ret < 0)
dev_err(ssr->dev, "failed to send cleanup message\n");
ret = wait_for_completion_timeout(&ssr->completion, HZ);
if (!ret)
dev_err(ssr->dev, "timeout waiting for cleanup done message\n");
return NOTIFY_DONE;
}
static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev)
{
struct glink_ssr *ssr;
ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL);
if (!ssr)
return -ENOMEM;
init_completion(&ssr->completion);
ssr->dev = &rpdev->dev;
ssr->ept = rpdev->ept;
ssr->nb.notifier_call = qcom_glink_ssr_notify;
dev_set_drvdata(&rpdev->dev, ssr);
return qcom_register_ssr_notifier(&ssr->nb);
}
static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev)
{
struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
qcom_unregister_ssr_notifier(&ssr->nb);
}
static const struct rpmsg_device_id qcom_glink_ssr_match[] = {
{ "glink_ssr" },
{}
};
static struct rpmsg_driver qcom_glink_ssr_driver = {
.probe = qcom_glink_ssr_probe,
.remove = qcom_glink_ssr_remove,
.callback = qcom_glink_ssr_callback,
.id_table = qcom_glink_ssr_match,
.drv = {
.name = "qcom_glink_ssr",
},
};
module_rpmsg_driver(qcom_glink_ssr_driver);
MODULE_ALIAS("rpmsg:glink_ssr");
MODULE_DESCRIPTION("Qualcomm GLINK SSR notifier");
MODULE_LICENSE("GPL v2");
...@@ -510,6 +510,8 @@ struct rproc_vdev { ...@@ -510,6 +510,8 @@ struct rproc_vdev {
}; };
struct rproc *rproc_get_by_phandle(phandle phandle); struct rproc *rproc_get_by_phandle(phandle phandle);
struct rproc *rproc_get_by_child(struct device *dev);
struct rproc *rproc_alloc(struct device *dev, const char *name, struct rproc *rproc_alloc(struct device *dev, const char *name,
const struct rproc_ops *ops, const struct rproc_ops *ops,
const char *firmware, int len); const char *firmware, int len);
......
#ifndef __QCOM_RPROC_H__
#define __QCOM_RPROC_H__
struct notifier_block;
#if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON)
int qcom_register_ssr_notifier(struct notifier_block *nb);
void qcom_unregister_ssr_notifier(struct notifier_block *nb);
#else
static inline int qcom_register_ssr_notifier(struct notifier_block *nb)
{
return 0;
}
static inline void qcom_unregister_ssr_notifier(struct notifier_block *nb) {}
#endif
#endif
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