forked from luck/tmp_suning_uos_patched
remoteproc updates for v4.14
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZswmrAAoJEAsfOT8Nma3FVAwP/1SEQkDsfrx9HIVtm9zSy7h5 DJ2dAUVMATJkTiDU8djz/KttL9bOLIcG5u0hiNbtPELm/5t3qAHiN8axO0w5Oxrv 0xO2yIwzxyhOU4cI8k+RWReTcihSqBYs5nHDXXhbhO5IKiaIfAQWljL6H1rL41gH SnKEuQuXfD6j/cvRyHEh10EWfDrVMnaim9aK3OiSz6i6YRyRC3r2VT+KiRnbaLq0 XIk1HATM6xEd6YfAeiuPZh6SbKuEibsBoPVQpAl9HjZvKY+n1dC11solohAEMYFC r222I6LYPRy5eteLnSqhTYof5l+pd9/eECIuXCdvxGfbkPhyPf7/K3WI3iV9sD8a h+QM22IpiwSzxnJPNJzmYtPnhqfOHZQTb64Fp2xiTTYa/ILbSH7VCAohxofEaheS 8rtIJqGhxT1WINvcBaoxwhL/CMMnBL+TlrqlAc5Qjd9zKFhJu4rcv1oQFNGMBMrN WY4R4x//oYgfmagIBEkmxMEcVMoJeI34AsbtSvazmcIQdvpcJ3Y0AMa+Ly0eyzBf RoTJY9y/Q3Y2DjcJoK+6iFeGRo0qI8/ytkKC6wb3RpaTnbDQ39X3GfCICwhsDCh7 uv19AmG5M26VRHxjlNnLpJp8jG3Up335TYQhkXV/CZYZcOxTmfJjGe3ItxOK/tTz YMv+IR72T/M+feeAS4Ge =R9dP -----END PGP SIGNATURE----- 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()
This commit is contained in:
commit
d7efc352ab
33
Documentation/devicetree/bindings/remoteproc/imx-rproc.txt
Normal file
33
Documentation/devicetree/bindings/remoteproc/imx-rproc.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
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>;
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
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:
|
|||
"ti,k2hk-dsp" for DSPs on Keystone 2 66AK2H/K SoCs
|
||||
"ti,k2l-dsp" for DSPs on Keystone 2 66AK2L 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'.
|
||||
Each entry should have the memory region's start address
|
||||
|
@ -37,20 +38,18 @@ The following are the mandatory properties:
|
|||
should be defined in this order,
|
||||
"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
|
||||
State Control node, and the register offset of the DSP
|
||||
boot address register within that node's address space.
|
||||
|
||||
- resets: Should contain the phandle to the reset controller node
|
||||
managing the resets for this device, and a reset
|
||||
specifier. Please refer to the following reset bindings
|
||||
for the reset argument specifier as per SoC,
|
||||
specifier. Please refer to either of the following reset
|
||||
bindings for the reset argument specifier as per SoC,
|
||||
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
|
||||
IP node that is used by the ARM CorePac processor to
|
||||
|
@ -75,6 +74,22 @@ The following are the mandatory properties:
|
|||
The gpio device to be used is as per the bindings in,
|
||||
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:
|
||||
--------------------
|
||||
|
||||
|
@ -85,8 +100,10 @@ Optional properties:
|
|||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
|
||||
|
||||
|
||||
Example:
|
||||
--------
|
||||
Examples:
|
||||
---------
|
||||
|
||||
1.
|
||||
/* 66AK2H/K DSP aliases */
|
||||
aliases {
|
||||
rproc0 = &dsp0;
|
||||
|
@ -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
|
|||
|
||||
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
|
||||
tristate "OMAP remoteproc support"
|
||||
depends on HAS_DMA
|
||||
|
|
|
@ -8,6 +8,7 @@ remoteproc-y += remoteproc_debugfs.o
|
|||
remoteproc-y += remoteproc_sysfs.o
|
||||
remoteproc-y += remoteproc_virtio.o
|
||||
remoteproc-y += remoteproc_elf_loader.o
|
||||
obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
|
||||
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
|
||||
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
|
||||
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/remoteproc.h>
|
||||
|
||||
|
@ -38,9 +39,27 @@ MODULE_PARM_DESC(da8xx_fw_name,
|
|||
#define SYSCFG_CHIPSIG3 BIT(3)
|
||||
#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
|
||||
* @rproc: rproc handle
|
||||
* @mem: internal memory regions data
|
||||
* @num_mems: number of internal memory regions
|
||||
* @dsp_clk: placeholder for platform's DSP clk
|
||||
* @ack_fxn: chip-specific ack function for ack'ing irq
|
||||
* @irq_data: ack_fxn function parameter
|
||||
|
@ -50,6 +69,8 @@ MODULE_PARM_DESC(da8xx_fw_name,
|
|||
*/
|
||||
struct da8xx_rproc {
|
||||
struct rproc *rproc;
|
||||
struct da8xx_rproc_mem *mem;
|
||||
int num_mems;
|
||||
struct clk *dsp_clk;
|
||||
void (*ack_fxn)(struct irq_data *data);
|
||||
struct irq_data *irq_data;
|
||||
|
@ -158,6 +179,44 @@ static const struct rproc_ops da8xx_rproc_ops = {
|
|||
.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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -184,12 +243,14 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
|||
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);
|
||||
if (IS_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);
|
||||
if (IS_ERR(chipsig))
|
||||
return PTR_ERR(chipsig);
|
||||
|
@ -201,16 +262,31 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
|||
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,
|
||||
sizeof(*drproc));
|
||||
if (!rproc)
|
||||
return -ENOMEM;
|
||||
if (!rproc) {
|
||||
ret = -ENOMEM;
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
drproc = rproc->priv;
|
||||
drproc->rproc = rproc;
|
||||
drproc->dsp_clk = dsp_clk;
|
||||
rproc->has_iommu = false;
|
||||
|
||||
ret = da8xx_rproc_get_internal_memories(pdev, drproc);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
|
||||
platform_set_drvdata(pdev, rproc);
|
||||
|
||||
/* everything the ISR needs is now setup, so hook it up */
|
||||
|
@ -247,7 +323,9 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
|||
|
||||
free_rproc:
|
||||
rproc_free(rproc);
|
||||
|
||||
free_mem:
|
||||
if (dev->of_node)
|
||||
of_reserved_mem_device_release(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -255,6 +333,7 @@ static int da8xx_rproc_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct rproc *rproc = platform_get_drvdata(pdev);
|
||||
struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/*
|
||||
* The devm subsystem might end up releasing things before
|
||||
|
@ -265,15 +344,24 @@ static int da8xx_rproc_remove(struct platform_device *pdev)
|
|||
|
||||
rproc_del(rproc);
|
||||
rproc_free(rproc);
|
||||
if (dev->of_node)
|
||||
of_reserved_mem_device_release(dev);
|
||||
|
||||
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 = {
|
||||
.probe = da8xx_rproc_probe,
|
||||
.remove = da8xx_rproc_remove,
|
||||
.driver = {
|
||||
.name = "davinci-rproc",
|
||||
.of_match_table = of_match_ptr(davinci_rproc_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
426
drivers/remoteproc/imx_rproc.c
Normal file
426
drivers/remoteproc/imx_rproc.c
Normal file
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/remoteproc.h>
|
||||
|
||||
#define IMX7D_SRC_SCR 0x0C
|
||||
#define IMX7D_ENABLE_M4 BIT(3)
|
||||
#define IMX7D_SW_M4P_RST BIT(2)
|
||||
#define IMX7D_SW_M4C_RST BIT(1)
|
||||
#define IMX7D_SW_M4C_NON_SCLR_RST BIT(0)
|
||||
|
||||
#define IMX7D_M4_RST_MASK (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
|
||||
| IMX7D_SW_M4C_RST \
|
||||
| IMX7D_SW_M4C_NON_SCLR_RST)
|
||||
|
||||
#define IMX7D_M4_START (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
|
||||
| IMX7D_SW_M4C_RST)
|
||||
#define IMX7D_M4_STOP IMX7D_SW_M4C_NON_SCLR_RST
|
||||
|
||||
/* Address: 0x020D8000 */
|
||||
#define IMX6SX_SRC_SCR 0x00
|
||||
#define IMX6SX_ENABLE_M4 BIT(22)
|
||||
#define IMX6SX_SW_M4P_RST BIT(12)
|
||||
#define IMX6SX_SW_M4C_NON_SCLR_RST BIT(4)
|
||||
#define IMX6SX_SW_M4C_RST BIT(3)
|
||||
|
||||
#define IMX6SX_M4_START (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
|
||||
| IMX6SX_SW_M4C_RST)
|
||||
#define IMX6SX_M4_STOP IMX6SX_SW_M4C_NON_SCLR_RST
|
||||
#define IMX6SX_M4_RST_MASK (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
|
||||
| IMX6SX_SW_M4C_NON_SCLR_RST \
|
||||
| IMX6SX_SW_M4C_RST)
|
||||
|
||||
#define IMX7D_RPROC_MEM_MAX 8
|
||||
|
||||
/**
|
||||
* struct imx_rproc_mem - slim internal memory structure
|
||||
* @cpu_addr: MPU virtual address of the memory region
|
||||
* @sys_addr: Bus address used to access the memory region
|
||||
* @size: Size of the memory region
|
||||
*/
|
||||
struct imx_rproc_mem {
|
||||
void __iomem *cpu_addr;
|
||||
phys_addr_t sys_addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* att flags */
|
||||
/* M4 own area. Can be mapped at probe */
|
||||
#define ATT_OWN BIT(1)
|
||||
|
||||
/* address translation table */
|
||||
struct imx_rproc_att {
|
||||
u32 da; /* device address (From Cortex M4 view)*/
|
||||
u32 sa; /* system bus address */
|
||||
u32 size; /* size of reg range */
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct imx_rproc_dcfg {
|
||||
u32 src_reg;
|
||||
u32 src_mask;
|
||||
u32 src_start;
|
||||
u32 src_stop;
|
||||
const struct imx_rproc_att *att;
|
||||
size_t att_size;
|
||||
};
|
||||
|
||||
struct imx_rproc {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct rproc *rproc;
|
||||
const struct imx_rproc_dcfg *dcfg;
|
||||
struct imx_rproc_mem mem[IMX7D_RPROC_MEM_MAX];
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static const struct imx_rproc_att imx_rproc_att_imx7d[] = {
|
||||
/* dev addr , sys addr , size , flags */
|
||||
/* OCRAM_S (M4 Boot code) - alias */
|
||||
{ 0x00000000, 0x00180000, 0x00008000, 0 },
|
||||
/* OCRAM_S (Code) */
|
||||
{ 0x00180000, 0x00180000, 0x00008000, ATT_OWN },
|
||||
/* OCRAM (Code) - alias */
|
||||
{ 0x00900000, 0x00900000, 0x00020000, 0 },
|
||||
/* OCRAM_EPDC (Code) - alias */
|
||||
{ 0x00920000, 0x00920000, 0x00020000, 0 },
|
||||
/* OCRAM_PXP (Code) - alias */
|
||||
{ 0x00940000, 0x00940000, 0x00008000, 0 },
|
||||
/* TCML (Code) */
|
||||
{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
|
||||
/* DDR (Code) - alias, first part of DDR (Data) */
|
||||
{ 0x10000000, 0x80000000, 0x0FFF0000, 0 },
|
||||
|
||||
/* TCMU (Data) */
|
||||
{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
|
||||
/* OCRAM (Data) */
|
||||
{ 0x20200000, 0x00900000, 0x00020000, 0 },
|
||||
/* OCRAM_EPDC (Data) */
|
||||
{ 0x20220000, 0x00920000, 0x00020000, 0 },
|
||||
/* OCRAM_PXP (Data) */
|
||||
{ 0x20240000, 0x00940000, 0x00008000, 0 },
|
||||
/* DDR (Data) */
|
||||
{ 0x80000000, 0x80000000, 0x60000000, 0 },
|
||||
};
|
||||
|
||||
static const struct imx_rproc_att imx_rproc_att_imx6sx[] = {
|
||||
/* dev addr , sys addr , size , flags */
|
||||
/* TCML (M4 Boot Code) - alias */
|
||||
{ 0x00000000, 0x007F8000, 0x00008000, 0 },
|
||||
/* OCRAM_S (Code) */
|
||||
{ 0x00180000, 0x008F8000, 0x00004000, 0 },
|
||||
/* OCRAM_S (Code) - alias */
|
||||
{ 0x00180000, 0x008FC000, 0x00004000, 0 },
|
||||
/* TCML (Code) */
|
||||
{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
|
||||
/* DDR (Code) - alias, first part of DDR (Data) */
|
||||
{ 0x10000000, 0x80000000, 0x0FFF8000, 0 },
|
||||
|
||||
/* TCMU (Data) */
|
||||
{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
|
||||
/* OCRAM_S (Data) - alias? */
|
||||
{ 0x208F8000, 0x008F8000, 0x00004000, 0 },
|
||||
/* DDR (Data) */
|
||||
{ 0x80000000, 0x80000000, 0x60000000, 0 },
|
||||
};
|
||||
|
||||
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
|
||||
.src_reg = IMX7D_SRC_SCR,
|
||||
.src_mask = IMX7D_M4_RST_MASK,
|
||||
.src_start = IMX7D_M4_START,
|
||||
.src_stop = IMX7D_M4_STOP,
|
||||
.att = imx_rproc_att_imx7d,
|
||||
.att_size = ARRAY_SIZE(imx_rproc_att_imx7d),
|
||||
};
|
||||
|
||||
static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
|
||||
.src_reg = IMX6SX_SRC_SCR,
|
||||
.src_mask = IMX6SX_M4_RST_MASK,
|
||||
.src_start = IMX6SX_M4_START,
|
||||
.src_stop = IMX6SX_M4_STOP,
|
||||
.att = imx_rproc_att_imx6sx,
|
||||
.att_size = ARRAY_SIZE(imx_rproc_att_imx6sx),
|
||||
};
|
||||
|
||||
static int imx_rproc_start(struct rproc *rproc)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
|
||||
dcfg->src_mask, dcfg->src_start);
|
||||
if (ret)
|
||||
dev_err(dev, "Filed to enable M4!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
|
||||
dcfg->src_mask, dcfg->src_stop);
|
||||
if (ret)
|
||||
dev_err(dev, "Filed to stop M4!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
|
||||
int len, u64 *sys)
|
||||
{
|
||||
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
||||
int i;
|
||||
|
||||
/* parse address translation table */
|
||||
for (i = 0; i < dcfg->att_size; i++) {
|
||||
const struct imx_rproc_att *att = &dcfg->att[i];
|
||||
|
||||
if (da >= att->da && da + len < att->da + att->size) {
|
||||
unsigned int offset = da - att->da;
|
||||
|
||||
*sys = att->sa + offset;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev_warn(priv->dev, "Translation filed: da = 0x%llx len = 0x%x\n",
|
||||
da, len);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
void *va = NULL;
|
||||
u64 sys;
|
||||
int i;
|
||||
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* On device side we have many aliases, so we need to convert device
|
||||
* address (M4) to system bus address first.
|
||||
*/
|
||||
if (imx_rproc_da_to_sys(priv, da, len, &sys))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < IMX7D_RPROC_MEM_MAX; i++) {
|
||||
if (sys >= priv->mem[i].sys_addr && sys + len <
|
||||
priv->mem[i].sys_addr + priv->mem[i].size) {
|
||||
unsigned int offset = sys - priv->mem[i].sys_addr;
|
||||
/* __force to make sparse happy with type conversion */
|
||||
va = (__force void *)(priv->mem[i].cpu_addr + offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va);
|
||||
|
||||
return va;
|
||||
}
|
||||
|
||||
static const struct rproc_ops imx_rproc_ops = {
|
||||
.start = imx_rproc_start,
|
||||
.stop = imx_rproc_stop,
|
||||
.da_to_va = imx_rproc_da_to_va,
|
||||
};
|
||||
|
||||
static int imx_rproc_addr_init(struct imx_rproc *priv,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int a, b = 0, err, nph;
|
||||
|
||||
/* remap required addresses */
|
||||
for (a = 0; a < dcfg->att_size; a++) {
|
||||
const struct imx_rproc_att *att = &dcfg->att[a];
|
||||
|
||||
if (!(att->flags & ATT_OWN))
|
||||
continue;
|
||||
|
||||
if (b > IMX7D_RPROC_MEM_MAX)
|
||||
break;
|
||||
|
||||
priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev,
|
||||
att->sa, att->size);
|
||||
if (IS_ERR(priv->mem[b].cpu_addr)) {
|
||||
dev_err(dev, "devm_ioremap_resource failed\n");
|
||||
err = PTR_ERR(priv->mem[b].cpu_addr);
|
||||
return err;
|
||||
}
|
||||
priv->mem[b].sys_addr = att->sa;
|
||||
priv->mem[b].size = att->size;
|
||||
b++;
|
||||
}
|
||||
|
||||
/* memory-region is optional property */
|
||||
nph = of_count_phandle_with_args(np, "memory-region", NULL);
|
||||
if (nph <= 0)
|
||||
return 0;
|
||||
|
||||
/* remap optional addresses */
|
||||
for (a = 0; a < nph; a++) {
|
||||
struct device_node *node;
|
||||
struct resource res;
|
||||
|
||||
node = of_parse_phandle(np, "memory-region", a);
|
||||
err = of_address_to_resource(node, 0, &res);
|
||||
if (err) {
|
||||
dev_err(dev, "unable to resolve memory region\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (b > IMX7D_RPROC_MEM_MAX)
|
||||
break;
|
||||
|
||||
priv->mem[b].cpu_addr = devm_ioremap_resource(&pdev->dev, &res);
|
||||
if (IS_ERR(priv->mem[b].cpu_addr)) {
|
||||
dev_err(dev, "devm_ioremap_resource failed\n");
|
||||
err = PTR_ERR(priv->mem[b].cpu_addr);
|
||||
return err;
|
||||
}
|
||||
priv->mem[b].sys_addr = res.start;
|
||||
priv->mem[b].size = resource_size(&res);
|
||||
b++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rproc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct imx_rproc *priv;
|
||||
struct rproc *rproc;
|
||||
struct regmap_config config = { .name = "imx-rproc" };
|
||||
const struct imx_rproc_dcfg *dcfg;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "failed to find syscon\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
regmap_attach_dev(dev, regmap, &config);
|
||||
|
||||
/* set some other name then imx */
|
||||
rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops,
|
||||
NULL, sizeof(*priv));
|
||||
if (!rproc) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dcfg = of_device_get_match_data(dev);
|
||||
if (!dcfg)
|
||||
return -EINVAL;
|
||||
|
||||
priv = rproc->priv;
|
||||
priv->rproc = rproc;
|
||||
priv->regmap = regmap;
|
||||
priv->dcfg = dcfg;
|
||||
priv->dev = dev;
|
||||
|
||||
dev_set_drvdata(dev, rproc);
|
||||
|
||||
ret = imx_rproc_addr_init(priv, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "filed on imx_rproc_addr_init\n");
|
||||
goto err_put_rproc;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(dev, "Failed to get clock\n");
|
||||
rproc_free(rproc);
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
/*
|
||||
* clk for M4 block including memory. Should be
|
||||
* enabled before .start for FW transfer.
|
||||
*/
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(&rproc->dev, "Failed to enable clock\n");
|
||||
rproc_free(rproc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rproc_add(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "rproc_add failed\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_put_clk:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
err_put_rproc:
|
||||
rproc_free(rproc);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_rproc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rproc *rproc = platform_get_drvdata(pdev);
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
rproc_del(rproc);
|
||||
rproc_free(rproc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_rproc_of_match[] = {
|
||||
{ .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d },
|
||||
{ .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
|
||||
|
||||
static struct platform_driver imx_rproc_driver = {
|
||||
.probe = imx_rproc_probe,
|
||||
.remove = imx_rproc_remove,
|
||||
.driver = {
|
||||
.name = "imx-rproc",
|
||||
.of_match_table = imx_rproc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(imx_rproc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("IMX6SX/7D remote processor control driver");
|
||||
MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
|
|
@ -410,7 +410,7 @@ static int keystone_rproc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
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)) {
|
||||
ret = PTR_ERR(ksproc->reset);
|
||||
goto free_rproc;
|
||||
|
@ -505,6 +505,7 @@ static const struct of_device_id keystone_rproc_of_match[] = {
|
|||
{ .compatible = "ti,k2hk-dsp", },
|
||||
{ .compatible = "ti,k2l-dsp", },
|
||||
{ .compatible = "ti,k2e-dsp", },
|
||||
{ .compatible = "ti,k2g-dsp", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, keystone_rproc_of_match);
|
||||
|
|
|
@ -38,6 +38,7 @@ struct adsp_data {
|
|||
const char *firmware_name;
|
||||
int pas_id;
|
||||
bool has_aggre2_clk;
|
||||
const char *ssr_name;
|
||||
};
|
||||
|
||||
struct qcom_adsp {
|
||||
|
@ -72,6 +73,7 @@ struct qcom_adsp {
|
|||
size_t mem_size;
|
||||
|
||||
struct qcom_rproc_subdev smd_subdev;
|
||||
struct qcom_rproc_ssr ssr_subdev;
|
||||
};
|
||||
|
||||
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
|
||||
|
@ -266,10 +268,7 @@ static int adsp_init_regulator(struct qcom_adsp *adsp)
|
|||
regulator_set_load(adsp->cx_supply, 100000);
|
||||
|
||||
adsp->px_supply = devm_regulator_get(adsp->dev, "px");
|
||||
if (IS_ERR(adsp->px_supply))
|
||||
return PTR_ERR(adsp->px_supply);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(adsp->px_supply);
|
||||
}
|
||||
|
||||
static int adsp_request_irq(struct qcom_adsp *adsp,
|
||||
|
@ -402,6 +401,7 @@ static int adsp_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
|
||||
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
|
||||
|
||||
ret = rproc_add(rproc);
|
||||
if (ret)
|
||||
|
@ -423,6 +423,7 @@ static int adsp_remove(struct platform_device *pdev)
|
|||
rproc_del(adsp->rproc);
|
||||
|
||||
qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
|
||||
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
|
||||
rproc_free(adsp->rproc);
|
||||
|
||||
return 0;
|
||||
|
@ -433,6 +434,7 @@ static const struct adsp_data adsp_resource_init = {
|
|||
.firmware_name = "adsp.mdt",
|
||||
.pas_id = 1,
|
||||
.has_aggre2_clk = false,
|
||||
.ssr_name = "lpass",
|
||||
};
|
||||
|
||||
static const struct adsp_data slpi_resource_init = {
|
||||
|
@ -440,6 +442,7 @@ static const struct adsp_data slpi_resource_init = {
|
|||
.firmware_name = "slpi.mdt",
|
||||
.pas_id = 12,
|
||||
.has_aggre2_clk = true,
|
||||
.ssr_name = "dsps",
|
||||
};
|
||||
|
||||
static const struct of_device_id adsp_of_match[] = {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/rpmsg/qcom_smd.h>
|
||||
|
||||
|
@ -25,6 +26,9 @@
|
|||
#include "qcom_common.h"
|
||||
|
||||
#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
|
||||
|
@ -51,7 +55,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev)
|
|||
|
||||
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)
|
||||
|
@ -92,5 +96,72 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
|
|||
}
|
||||
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_LICENSE("GPL v2");
|
||||
|
|
|
@ -12,6 +12,12 @@ struct qcom_rproc_subdev {
|
|||
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,
|
||||
const struct firmware *fw,
|
||||
int *tablesz);
|
||||
|
@ -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_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
|
||||
|
|
|
@ -153,6 +153,7 @@ struct q6v5 {
|
|||
size_t mpss_size;
|
||||
|
||||
struct qcom_rproc_subdev smd_subdev;
|
||||
struct qcom_rproc_ssr ssr_subdev;
|
||||
};
|
||||
|
||||
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,
|
|||
|
||||
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)) {
|
||||
dev_err(qproc->dev, "failed to acquire mss restart\n");
|
||||
return PTR_ERR(qproc->mss_restart);
|
||||
|
@ -1038,6 +1040,7 @@ static int q6v5_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
|
||||
qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
|
||||
|
||||
ret = rproc_add(rproc);
|
||||
if (ret)
|
||||
|
@ -1058,6 +1061,7 @@ static int q6v5_remove(struct platform_device *pdev)
|
|||
rproc_del(qproc->rproc);
|
||||
|
||||
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
|
||||
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
|
||||
rproc_free(qproc->rproc);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -794,7 +794,7 @@ static void rproc_remove_subdevices(struct rproc *rproc)
|
|||
{
|
||||
struct rproc_subdev *subdev;
|
||||
|
||||
list_for_each_entry(subdev, &rproc->subdevs, node)
|
||||
list_for_each_entry_reverse(subdev, &rproc->subdevs, node)
|
||||
subdev->remove(subdev);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
* 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)
|
|||
*
|
||||
* 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;
|
||||
struct device *dev;
|
||||
|
@ -1180,15 +1180,6 @@ static int __rproc_boot(struct rproc *rproc)
|
|||
mutex_unlock(&rproc->lock);
|
||||
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);
|
||||
|
||||
/**
|
||||
|
@ -1369,7 +1360,7 @@ static void rproc_type_release(struct device *dev)
|
|||
kfree(rproc);
|
||||
}
|
||||
|
||||
static struct device_type rproc_type = {
|
||||
static const struct device_type rproc_type = {
|
||||
.name = "remoteproc",
|
||||
.release = rproc_type_release,
|
||||
};
|
||||
|
@ -1440,6 +1431,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
|
|||
rproc->dev.parent = dev;
|
||||
rproc->dev.type = &rproc_type;
|
||||
rproc->dev.class = &rproc_class;
|
||||
rproc->dev.driver_data = rproc;
|
||||
|
||||
/* Assign a unique device index and name */
|
||||
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)
|
|||
}
|
||||
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: remote processor
|
||||
|
|
|
@ -48,7 +48,6 @@ struct rproc_fw_ops {
|
|||
/* from remoteproc_core.c */
|
||||
void rproc_release(struct kref *kref);
|
||||
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);
|
||||
|
||||
/* from remoteproc_virtio.c */
|
||||
|
|
|
@ -212,7 +212,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev)
|
|||
int err;
|
||||
|
||||
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)) {
|
||||
dev_err(dev, "Failed to get S/W Reset\n");
|
||||
return PTR_ERR(ddata->sw_reset);
|
||||
|
@ -220,7 +221,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
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)) {
|
||||
dev_err(dev, "Failed to get Power Reset\n");
|
||||
return PTR_ERR(ddata->pwr_reset);
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
#
|
||||
# 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
|
||||
tristate "QCOM General Serial Bus Interface"
|
||||
depends on ARCH_QCOM
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
|
||||
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
|
||||
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
|
||||
obj-$(CONFIG_QCOM_PM) += spm.o
|
||||
|
|
164
drivers/soc/qcom/glink_ssr.c
Normal file
164
drivers/soc/qcom/glink_ssr.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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 {
|
|||
};
|
||||
|
||||
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,
|
||||
const struct rproc_ops *ops,
|
||||
const char *firmware, int len);
|
||||
|
|
22
include/linux/remoteproc/qcom_rproc.h
Normal file
22
include/linux/remoteproc/qcom_rproc.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#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
|
Loading…
Reference in New Issue
Block a user