]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/commitdiff
Merge branch 'iommu-linux-4.19.y' of git://git.ti.com/rpmsg/iommu into rproc-linux...
authorSuman Anna <s-anna@ti.com>
Mon, 4 Mar 2019 16:09:10 +0000 (10:09 -0600)
committerSuman Anna <s-anna@ti.com>
Mon, 4 Mar 2019 16:09:10 +0000 (10:09 -0600)
Merge in the updated iommu feature branch into remoteproc tree to
pull in the necessary support to fix the DRA7 IPU1 boot issue and
couple of DRA7 DSP idle issues with HW_AUTO setting. The DSP idle
status is achieved across all the DSPs and boards only with the
fix for errata i879.

* 'iommu-linux-4.19.y' of git://git.ti.com/rpmsg/iommu:
  ARM: OMAP2+: Add workaround for DRA7 DSP MStandby errata i879
  ARM: OMAP2+: Extend DRA7 IPU1 MMU pdata quirks to DSP MDMA MMUs
  ARM: OMAP2+: Use separate IOMMU pdata to fix DRA7 IPU1 boot
  iommu/omap: Fix boot issue on remoteprocs with AMMU/Unicache

Signed-off-by: Suman Anna <s-anna@ti.com>
100 files changed:
Documentation/ABI/testing/sysfs-bus-rpmsg
Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt [new file with mode: 0644]
Documentation/devicetree/bindings/soc/ti/ti,pruss.txt [new file with mode: 0644]
arch/arm/boot/dts/am335x-bone-common.dtsi
arch/arm/boot/dts/am335x-evm.dts
arch/arm/boot/dts/am335x-evmsk.dts
arch/arm/boot/dts/am335x-icev2.dts
arch/arm/boot/dts/am33xx.dtsi
arch/arm/boot/dts/am4372.dtsi
arch/arm/boot/dts/am437x-gp-evm.dts
arch/arm/boot/dts/am437x-idk-evm.dts
arch/arm/boot/dts/am437x-sk-evm.dts
arch/arm/boot/dts/am571x-idk.dts
arch/arm/boot/dts/am572x-idk-common.dtsi
arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
arch/arm/boot/dts/am57xx-idk-common.dtsi
arch/arm/boot/dts/da850.dtsi
arch/arm/boot/dts/dra7-evm-common.dtsi
arch/arm/boot/dts/dra7-evm.dts
arch/arm/boot/dts/dra7-ipu-dsp-common.dtsi [new file with mode: 0644]
arch/arm/boot/dts/dra7.dtsi
arch/arm/boot/dts/dra71-evm.dts
arch/arm/boot/dts/dra72-evm-common.dtsi
arch/arm/boot/dts/dra72-evm-revc.dts
arch/arm/boot/dts/dra72-evm.dts
arch/arm/boot/dts/dra72x.dtsi
arch/arm/boot/dts/dra74-ipu-dsp-common.dtsi [new file with mode: 0644]
arch/arm/boot/dts/dra74x.dtsi
arch/arm/boot/dts/dra76-evm.dts
arch/arm/boot/dts/keystone-k2e-evm.dts
arch/arm/boot/dts/keystone-k2e.dtsi
arch/arm/boot/dts/keystone-k2g-evm.dts
arch/arm/boot/dts/keystone-k2g-ice.dts
arch/arm/boot/dts/keystone-k2g.dtsi
arch/arm/boot/dts/keystone-k2hk-evm.dts
arch/arm/boot/dts/keystone-k2hk.dtsi
arch/arm/boot/dts/keystone-k2l-evm.dts
arch/arm/boot/dts/keystone-k2l.dtsi
arch/arm/boot/dts/omap4-panda-common.dtsi
arch/arm/boot/dts/omap4.dtsi
arch/arm/boot/dts/omap5-uevm.dts
arch/arm/boot/dts/omap5.dtsi
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/omap_hwmod_44xx_data.c
arch/arm/mach-omap2/omap_hwmod_54xx_data.c
arch/arm/mach-omap2/omap_hwmod_7xx_data.c
arch/arm/mach-omap2/pdata-quirks.c
arch/arm/mach-omap2/remoteproc.c [new file with mode: 0644]
arch/arm/mach-omap2/remoteproc.h [new file with mode: 0644]
drivers/clk/ti/clk-33xx.c
drivers/clk/ti/clk-43xx.c
drivers/clk/ti/clk-44xx.c
drivers/clk/ti/clk-54xx.c
drivers/clk/ti/clk-7xx.c
drivers/clocksource/timer-ti-dm.c
drivers/irqchip/Makefile
drivers/irqchip/irq-pruss-intc.c [new file with mode: 0644]
drivers/remoteproc/Kconfig
drivers/remoteproc/Makefile
drivers/remoteproc/da8xx_remoteproc.c
drivers/remoteproc/imx_rproc.c
drivers/remoteproc/keystone_remoteproc.c
drivers/remoteproc/omap_remoteproc.c
drivers/remoteproc/omap_remoteproc.h
drivers/remoteproc/pru_rproc.c [new file with mode: 0644]
drivers/remoteproc/pru_rproc.h [new file with mode: 0644]
drivers/remoteproc/qcom_adsp_pil.c
drivers/remoteproc/qcom_q6v5_pil.c
drivers/remoteproc/qcom_q6v5_wcss.c
drivers/remoteproc/qcom_wcnss.c
drivers/remoteproc/remoteproc_core.c
drivers/remoteproc/remoteproc_debugfs.c
drivers/remoteproc/remoteproc_elf_loader.c
drivers/remoteproc/remoteproc_internal.h
drivers/remoteproc/remoteproc_sysfs.c
drivers/remoteproc/st_slim_rproc.c
drivers/remoteproc/wkup_m3_rproc.c
drivers/rpmsg/qcom_glink_native.c
drivers/rpmsg/qcom_smd.c
drivers/rpmsg/rpmsg_char.c
drivers/rpmsg/rpmsg_core.c
drivers/rpmsg/virtio_rpmsg_bus.c
drivers/soc/qcom/wcnss_ctrl.c
drivers/soc/ti/Kconfig
drivers/soc/ti/Makefile
drivers/soc/ti/keystone_dsp_mem.c [new file with mode: 0644]
drivers/soc/ti/pruss.c [new file with mode: 0644]
drivers/soc/ti/pruss_soc_bus.c [new file with mode: 0644]
include/linux/platform_data/remoteproc-omap.h
include/linux/platform_data/ti-pruss.h [new file with mode: 0644]
include/linux/pruss.h [new file with mode: 0644]
include/linux/pruss_driver.h [new file with mode: 0644]
include/linux/remoteproc.h
include/linux/rpmsg.h
include/uapi/linux/keystone_dsp_mem.h [new file with mode: 0644]
include/uapi/linux/keystone_remoteproc.h [new file with mode: 0644]
samples/rpmsg/rpmsg_client_sample.c

index 990fcc42093539b77ac3d4918d0c700a1be410c2..877ca35adc130c4188c04184912c1e4bd257e9bd 100644 (file)
@@ -93,3 +93,32 @@ Description:
                This sysfs entry allows the rpmsg driver for a rpmsg device
                to be specified which will override standard OF, ID table
                and name matching.
+
+What:          /sys/bus/rpmsg/devices/.../desc
+Date:          September 2018
+KernelVersion: 4.19
+Contact:       Suman Anna <s-anna@ti.com>
+Description:
+               Every rpmsg device is a communication channel with a remote
+               processor. Channels are identified by a textual name (see
+               /sys/bus/rpmsg/devices/.../name above) and have a local
+               ("source") rpmsg address, and remote ("destination") rpmsg
+               address.
+
+               A channel is first created when an entity, whether local
+               or remote, starts listening on it for messages (and is thus
+               called an rpmsg server). When that happens, a "name service"
+               announcement is sent to the other processor, in order to let
+               it know about the creation of the channel (this way remote
+               clients know they can start sending messages).
+
+               The listening entity (or client) which communicates with a
+               remote processor is referred as rpmsg driver. The rpmsg device
+               and rpmsg driver are matched based on rpmsg device name (see
+               /sys/bus/rpmsg/devices/.../name above) and rpmsg driver ID table.
+
+               This sysfs entry contains an additional optional description of
+               the rpmsg device that can be optionally included as part of the
+               "name service" announcement. This description is then passed on
+               to the corresponding rpmsg drivers to further distinguish multiple
+               devices associated with the same rpmsg driver.
diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.txt
new file mode 100644 (file)
index 0000000..eac3587
--- /dev/null
@@ -0,0 +1,57 @@
+PRU ICSS INTC on TI SoCs
+========================
+
+Each PRUSS has a single interrupt controller instance that is common to both
+the PRU cores. Each interrupt controller can detect 64 input events which are
+then mapped to 10 possible output interrupts through two levels of mapping. The
+input events can be triggered by either the PRUs and/or various other PRUSS
+internal and external peripherals. The first 2 output interrupts are fed
+exclusively to the internal PRU cores, with the remaining 8 connected to
+external interrupt controllers including the MPU.
+
+This interrupt-controller node should be defined as a child node of the
+corresponding PRUSS node. The node should be named "interrupt-controller".
+Please see the overall PRUSS bindings document for additional details
+including a complete example,
+    Documentation/devicetree/bindings/soc/ti/ti,pruss.txt
+
+Required Properties:
+--------------------
+- compatible           : should be one of,
+                             "ti,am3356-pruss-intc" for AM335x family of SoCs
+                             "ti,am4376-pruss-intc" for AM437x family of SoCs
+                             "ti,am5728-pruss-intc" for AM57xx family of SoCs
+                             "ti,k2g-pruss-intc" for 66AK2G family of SoCs
+- reg                  : base address and size for the PRUSS INTC sub-module
+- interrupt-controller : mark this node as an interrupt controller
+- #interrupt-cells     : should be 1. Client users shall use the PRU System
+                         event number (the interrupt source that the client
+                         is interested in) as the value of the interrupts
+                         property in their node
+
+Example:
+--------
+
+1.     /* AM33xx PRU-ICSS */
+       pruss_soc_bus: pruss-soc-bus@4a326004 {
+               compatible = "ti,am3356-pruss-soc-bus";
+               ...
+
+               pruss: pruss@4a300000 {
+                       compatible = "ti,am3356-pruss";
+                       reg = <0x4a300000 0x80000>;
+                       interrupts = <20 21 22 23 24 25 26 27>;
+                       interrupt-names = "host2", "host3", "host4",
+                                         "host5", "host6", "host7",
+                                         "host8", "host9";
+                       #address-cells = <1>;
+                       ...
+
+                       pruss_intc: interrupt-controller@4a320000 {
+                               compatible = "ti,am3356-pruss-intc";
+                               reg = <0x4a320000 0x2000>;
+                               interrupt-controller;
+                               #interrupt-cells = <1>;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.txt b/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.txt
new file mode 100644 (file)
index 0000000..d5aeb1a
--- /dev/null
@@ -0,0 +1,210 @@
+OMAP4+ Remoteproc Devices
+=========================
+
+The OMAP family of SoCs usually have one or more slave processor sub-systems
+that are used to offload some of the processor-intensive tasks, or to manage
+other hardware accelerators, for achieving various system level goals.
+
+The processor cores in the sub-system are usually behind an IOMMU, and may
+contain additional sub-modules like Internal RAM and/or ROMs, L1 and/or L2
+caches, an Interrupt Controller, a Cache Controller etc.
+
+The OMAP SoCs usually have a DSP processor sub-system and/or an IPU processor
+sub-system. The DSP processor sub-system can contain any of the TI's C64x,
+C66x or C67x family of DSP cores as the main execution unit. The IPU processor
+sub-system usually contains either a Dual-Core Cortex-M3 or Dual-Core Cortex-M4
+processors.
+
+Remote Processor Node:
+======================
+Each remote processor sub-system is represented as a single DT node. Each node
+has a number of required or optional properties that enable the OS running on
+the host processor (MPU) to perform the device management of the remote
+processor and to communicate with the remote processor. The various properties
+can be classified as constant or variable. The constant properties are dictated
+by the SoC and does not change from one board to another having the same SoC.
+Examples of constant properties include 'iommus', 'reg'. The variable properties
+are dictated by the system integration aspects such as memory on the board, or
+configuration used within the corresponding firmware image. Examples of variable
+properties include 'mboxes', 'memory-region', 'timers', 'watchdog-timers' etc.
+
+Required properties:
+--------------------
+The following are the mandatory properties:
+
+- compatible:  Should be one of the following,
+                   "ti,omap4-dsp" for DSPs on OMAP4 SoCs
+                   "ti,omap5-dsp" for DSPs on OMAP5 SoCs
+                   "ti,dra7-dsp" for DSPs on DRA7xx/AM57xx SoCs
+                   "ti,omap4-ipu" for IPUs on OMAP4 SoCs
+                   "ti,omap5-ipu" for IPUs on OMAP5 SoCs
+                   "ti,dra7-ipu" for IPUs on DRA7xx/AM57xx SoCs
+
+- ti,hwmods:   Name of the hwmod associated with the remoteproc device
+
+- iommus:      phandles to OMAP IOMMU nodes, that need to be programmed
+               for this remote processor to access any external RAM memory or
+               other peripheral device address spaces. This property usually
+               has only a single phandle. Multiple phandles are used only in
+               cases where the sub-system has different ports for different
+               sub-modules within the processor sub-system (eg: DRA7 DSPs),
+               and need the same programming in both the MMUs.
+
+- mboxes:      OMAP Mailbox specifier denoting the sub-mailbox, to be used for
+               communication with the remote processor. The specifier format is
+               as per the bindings,
+               Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
+               This property should match with the sub-mailbox node used in
+               the firmware image.
+
+Optional properties:
+--------------------
+Some of these properties are mandatory on some SoCs, and some are optional
+depending on the configuration of the firmware image to be executed on the
+remote processor. The conditions are mentioned for each property.
+
+The following are the optional properties:
+- reg:                 Address space for any remoteproc memories present on
+                       the SoC. Should contain an entry for each value in
+                       'reg-names'. These are mandatory for all DSP and IPU
+                       processors that have them (OMAP4/OMAP5 DSPs do not have
+                       any RAMs)
+
+- reg-names:           Required names for each of the address spaces defined in
+                       the 'reg' property. Should contain a string from among
+                       the following names, each representing the corresponding
+                       internal RAM memory region,
+                          "l2ram" for L2 RAM,
+                          "l1pram" for L1 Program RAM Memory/Cache,
+                          "l1dram" for L1 Data RAM Memory/Cache,
+
+                       All devices may not have all the above memories.
+
+- syscon-bootreg:      Should be a pair of the phandle to the System Control
+                       Configuration region that contains the boot address
+                       register, and the register offset of the boot address
+                       register within the System Control module. This property
+                       is required for all the DSP instances on OMAP4, OMAP5
+                       and DRA7xx SoCs.
+
+- 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,
+                       Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+
+- mbox-names:          Optional names for the OMAP mailbox specifiers mentioned
+                       in the 'mboxes' property, one per specifier value
+
+- timers:              One or more phandles to OMAP DMTimer nodes, that serve
+                       as System/Tick timers for the OS running on the remote
+                       processors. This will usually be a single timer if the
+                       processor sub-system is running in SMP mode, or one per
+                       core in the processor sub-system. This can also be used
+                       to reserve specific timers to be dedicated to the
+                       remote processors.
+
+                       This property is mandatory on remote processors requiring
+                       external tick wakeup, and to support Power Management
+                       features. The timers to be used should match with the
+                       timers used in the firmware image.
+
+- watchdog-timers:     One or more phandles to OMAP DMTimer nodes, used to
+                       serve as Watchdog timers for the processor cores. This
+                       will usually be one per executing processor core, even
+                       if the processor sub-system is running a SMP OS.
+
+                       The timers to be used should match with the watchdog
+                       timers used in the firmware image.
+
+Example:
+--------
+
+1. OMAP4 DSP
+       /* DSP Reserved Memory node */
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               dsp_memory_region: dsp-memory@98000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x98000000 0x800000>;
+                       reusable;
+               };
+       };
+
+       /* DSP node */
+       ocp {
+               dsp: dsp {
+                       compatible = "ti,omap4-dsp";
+                       ti,hwmods = "dsp";
+                       syscon-bootreg = <&scm_conf 0x304>;
+                       iommus = <&mmu_dsp>;
+                       mboxes = <&mailbox &mbox_dsp>;
+                       memory-region = <&dsp_memory_region>;
+                       timers = <&timer5>;
+                       watchdog-timers = <&timer6>;
+               };
+       };
+
+2. OMAP5 IPU
+       /* IPU Reserved Memory node */
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu_memory_region: ipu-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0 0x95800000 0 0x3800000>;
+                       reusable;
+               };
+       };
+
+       /* IPU node */
+       ocp {
+               ipu: ipu@55020000 {
+                       compatible = "ti,omap5-ipu";
+                       reg = <0x55020000 0x10000>;
+                       reg-names = "l2ram";
+                       ti,hwmods = "ipu";
+                       iommus = <&mmu_ipu>;
+                       mboxes = <&mailbox &mbox_ipu>;
+                       memory-region = <&ipu_memory_region>;
+                       timers = <&timer3>, <&timer4>;
+                       watchdog-timers = <&timer9>, <&timer11>;
+               };
+       };
+
+3. DRA7xx/AM57xx DSP
+       /* DSP1 Reserved Memory node */
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+               };
+       };
+
+       /* DSP1 node */
+       ocp {
+               dsp1: dsp@40800000 {
+                       compatible = "ti,dra7-dsp";
+                       reg = <0x40800000 0x48000>,
+                             <0x40e00000 0x8000>,
+                             <0x40f00000 0x8000>;
+                       reg-names = "l2ram", "l1pram", "l1dram";
+                       ti,hwmods = "dsp1";
+                       syscon-bootreg = <&scm_conf 0x55c>;
+                       iommus = <&mmu0_dsp1>, <&mmu1_dsp1>;
+                       mboxes = <&mailbox5 &mbox_dsp1_ipc3x>;
+                       memory-region = <&dsp1_memory_region>;
+                       timers = <&timer5>;
+                       watchdog-timers = <&timer10>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.txt b/Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.txt
new file mode 100644 (file)
index 0000000..f299395
--- /dev/null
@@ -0,0 +1,156 @@
+PRU Core on TI SoCs
+===================
+
+Each PRUSS has dual PRU cores, each represented by a PRU node. Each PRU core
+has a dedicated Instruction RAM, Control and Debug register sets, and use
+the Data RAMs present within the PRUSS for code execution.
+
+Each PRU core node should be defined as a child node of the corresponding PRUSS
+node. Each node can optionally be rendered inactive by using the standard DT
+string property, "status".
+
+Please see the overall PRUSS bindings document for additional details
+including a complete example,
+    Documentation/devicetree/bindings/soc/ti/ti,pruss.txt
+
+Required Properties:
+--------------------
+- compatible     : should be
+                       "ti,am3356-pru" for AM335x family of SoCs
+                       "ti,am4376-pru" for AM437x family of SoCs
+                       "ti,am5728-pru" for AM57xx family of SoCs
+                       "ti,k2g-pru" for 66AK2G family of SoCs
+- reg            : base address and size for each of the 3 sub-module address
+                   spaces as mentioned in reg-names, and in the same order as
+                   the reg-names
+- reg-names      : should contain each of the following 3 names, the binding is
+                   agnostic of the order of these reg-names, preferable to have
+                   the "iram" entry as the first one
+                       "iram" for Instruction RAM,
+                       "control" for the CTRL sub-module registers,
+                       "debug" for the Debug sub-module registers,
+- firmware-name  : should contain the name of the default firmware image file
+                   located on the firmware search path
+
+Optional Properties:
+--------------------
+The virtio based communication between the MPU and a PRU core _requires_
+either the 'mboxes' property, or the set of 'interrupt-parent', 'interrupts'
+and 'interrupt-names' properties to be defined. The latter option is the
+preferred choice. The 'mboxes' property is not applicable for 66AK2G and
+DA850/OMAP-L138 SoCs.
+
+- mboxes           : OMAP Mailbox specifier denoting the sub-mailbox, if using
+                     a mailbox for IPC signalling between host and a PRU core.
+                     The specifier format is as per the bindings,
+                         Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
+                     This property should match with the sub-mailbox node used
+                     in the corresponding firmware image.
+- interrupt-parent : phandle to the PRUSS INTC node. Should be defined if
+                     interrupts property is to be used.
+- interrupts       : array of interrupt specifiers if using PRU system events
+                     for IPC signalling between host and a PRU core. This
+                     property should match with the PRU system event used in
+                     the corresponding firmware image.
+- interrupt-names  : should use one of the following names for each interrupt,
+                     the name should match the corresponding PRU system event
+                     number,
+                         "vring" - for PRU to HOST virtqueue signalling
+                         "kick"  - for HOST to PRU virtqueue signalling
+
+
+Application/Consumer Nodes
+==========================
+A PRU application/consumer/user node typically uses one or more PRU device nodes
+to implement a PRU application/functionality. Each application/client node would
+need a reference to at least a PRU node, and optionally define some properties
+needed for hardware/firmware configuration. The below properties are a list of
+common properties supported by the PRU remoteproc infrastructure.
+
+The application nodes shall define their own bindings like regular platform
+devices, so below are in addition to each node's bindings.
+
+Required Properties:
+--------------------
+- prus                 : phandles to the PRU nodes used
+
+Optional Properties:
+--------------------
+- firmware-name        : firmwares for the PRU cores, the default firmware
+                         for the core from the PRU node will be used if not
+                         provided. The firmware names should correspond to
+                         the PRU cores listed in the 'prus' property
+- ti,pruss-gp-mux-sel  : array of values for the GP_MUX_SEL under PRUSS_GPCFG
+                         register for a PRU. This selects the internal muxing
+                         scheme for the PRU instance. If not provided, the
+                         default out-of-reset value (0) for the PRU core is
+                         used. Values should correspond to the PRU cores listed
+                         in the 'prus' property
+- ti,pru-interrupt-map : PRU interrupt mappings, containing an array of entries
+                         with each entry consisting of 4 cell-values. First one
+                         is an index towards the "prus" property to identify the
+                         PRU core for the interrupt map, second is the PRU
+                         System Event id, third is the PRU interrupt channel id
+                         and fourth is the PRU host interrupt id. If provided,
+                         this map will supercede any other configuration
+                         provided through firmware
+
+
+Example:
+--------
+
+1.     /* AM33xx PRU-ICSS */
+       pruss_soc_bus: pruss-soc-bus@4a326004 {
+               compatible = "ti,am3356-pruss-soc-bus";
+               ...
+
+               pruss: pruss@4a300000 {
+                       compatible = "ti,am3356-pruss";
+                       reg = <0x4a300000 0x80000>;
+                       ...
+
+                       pruss_mem: memories@4a300000 {
+                               reg = <0x4a300000 0x2000>,
+                                     <0x4a302000 0x2000>,
+                                     <0x4a310000 0x3000>;
+                               reg-names = "dram0", "dram1", "shrdram2";
+                       };
+
+                       pru0: pru@4a334000 {
+                               compatible = "ti,am3356-pru";
+                               reg = <0x4a334000 0x2000>,
+                                     <0x4a322000 0x400>,
+                                     <0x4a322400 0x100>;
+                               reg-names = "iram", "control", "debug";
+                               firmware-name = "am335x-pru0-fw";
+                               interrupt-parent = <&pruss_intc>;
+                               interrupts = <16>, <17>;
+                               interrupt-names = "vring", "kick";
+                       };
+
+                       pru1: pru@4a338000 {
+                               compatible = "ti,am3356-pru";
+                               reg = <0x4a338000 0x2000>,
+                                     <0x4a324000 0x400>,
+                                     <0x4a324400 0x100>;
+                               reg-names = "iram", "control", "debug";
+                               firmware-name = "am335x-pru1-fw";
+                               interrupt-parent = <&pruss_intc>;
+                               interrupts = <18>, <19>;
+                               interrupt-names = "vring", "kick";
+                               /* mboxes = <&mailbox &mbox_pru1>; */
+                       };
+               };
+       };
+
+3:     /* PRU application node example */
+       app_node: app_node {
+               ...
+               prus = <&pru0>, <&pru1>;
+               firmware-name = "pruss-app-fw", "pruss-app-fw-2";
+               ti,pruss-gp-mux-sel = <2>, <1>;
+               /* setup interrupts for prus:
+                  prus[0] => pru1_0: ev=16, chnl=2, host-irq=7,
+                  prus[1] => pru1_1: ev=19, chnl=1, host-irq=3 */
+               ti,pru-interrupt-map = <0 16 2 7 >, <1 19 1 3>;
+       }
diff --git a/Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt b/Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt
new file mode 100644 (file)
index 0000000..5d132b6
--- /dev/null
@@ -0,0 +1,73 @@
+TI Keystone DSP Memory Mapping
+==============================
+
+Binding status: Unstable - ABI compatibility may be broken in the future
+
+The Keystone DSP Memory Mapping binding defines the memory regions that
+are reserved to be used with DSPs. These regions can be mapped into
+userspace for providing direct user-mode access to these regions for the
+purposes of shared memory communication with the DSP remote processor
+devices on the SoC. These memory regions can also be used for supporting
+user-space based loading of the DSP remoteproc devices.
+
+The memory regions can either be from regular DDR memory or from the
+On-chip RAM, and there can be one or more regions of each memory type.
+
+DDR Memory usage (Optional):
+----------------------------
+One or more memory regions from DDR memory can be reserved specifically
+to be used by the DSPs on the SoC. Each region should be defined as a
+child node of the reserved-memory node with the following required
+properties:
+
+- compatible : Should be "ti,keystone-dsp-mem-pool"
+- reg        : Should contain the region's start address and size following
+               the #address-cells and #size-cells defined in the parent
+               reserved-memory node.
+- no-map     : Should be defined to remove this region from kernel
+
+Please see Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+for more details on reserved-memory node.
+
+SRAM usage (Optional):
+----------------------
+The On-chip Multicore Shared Memory (MSM) RAM can also be exposed to
+userspace by defining specific child nodes under the corresponding parent
+SRAM node. The generic SRAM binding is as per the binding document
+Documentation/devicetree/bindings/misc/sram.txt. Following properties
+should be used in each corresponding child node for the userspace mapping
+usecase:
+
+- compatible : Should be "ti,keystone-dsp-msm-ram"
+- reg        : Should contain a pair of values for the address and size
+               of the region, following the parent-child ranges convention.
+
+Example:
+--------
+       /* 66AK2H EVM */
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+                       compatible = "ti,keystone-dsp-mem-pool";
+                       reg = <0x00000008 0x20000000 0x00000000 0x20000000>;
+                       no-map;
+               };
+       };
+
+       soc {
+               msm_ram: msmram@c000000 {
+                       compatible = "mmio-sram";
+                       reg = <0x0c000000 0x600000>;
+                       ranges = <0x0 0x0c000000 0x600000>;
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       sram-mpm@0 {
+                               compatible = "ti,keystone-dsp-msm-ram";
+                               reg = <0x0 0x80000>;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/soc/ti/ti,pruss.txt b/Documentation/devicetree/bindings/soc/ti/ti,pruss.txt
new file mode 100644 (file)
index 0000000..09eda26
--- /dev/null
@@ -0,0 +1,331 @@
+PRU-ICSS on TI SoCs
+===================
+
+Binding status: Unstable - Subject to changes for undocumented sub-modules
+
+The Programmable Real-Time Unit and Industrial Communication Subsystem
+(PRU-ICSS) is present on various TI SoCs such as AM335x, AM437x, Keystone
+66AK2G, OMAP-L138/DA850 etc. A PRUSS consists of dual 32-bit RISC cores
+(Programmable Real-Time Units, or PRUs), shared RAM, data and instruction
+RAMs, some internal peripheral modules to facilitate industrial communication,
+and an interrupt controller.
+
+The programmable nature of the PRUs provide flexibility to implement custom
+peripheral interfaces, fast real-time responses, or specialized data handling.
+The common peripheral modules include the following,
+  - an Ethernet MII_RT module with two MII ports
+  - an MDIO port to control external Ethernet PHYs
+  - an Industrial Ethernet Peripheral (IEP) to manage/generate Industrial
+    Ethernet functions
+  - an Enhanced Capture Module (eCAP)
+  - an Industrial Ethernet Timer with 7/9 capture and 16 compare events
+  - a 16550-compatible UART to support PROFIBUS
+  - Enhanced GPIO with async capture and serial support
+
+A PRU-ICSS subsystem can have up to three shared data memories. A PRU core
+acts on a primary Data RAM (there are usually 2 Data RAMs) at its address
+0x0, but also has access to a secondary Data RAM (primary to the other PRU
+core) at its address 0x2000. A shared Data RAM, if present, can be accessed
+by both the PRU cores. The Interrupt Controller (INTC) and a CFG module are
+common to both the PRU cores. Each PRU core also has a private instruction RAM,
+and specific register spaces for Control and Debug functionalities.
+
+Various sub-modules within a PRU-ICSS subsystem are represented as individual
+nodes and are defined using a parent-child hierarchy depending on their
+integration within the IP and the SoC. These nodes are described in the
+following sections.
+
+
+PRU-ICSS SoC Bus Node
+======================
+This node represents the integration of the PRU-ICSS IP into a SoC, and is
+required for all SoCs. The PRU-ICSS parent nodes need to be defined as child
+nodes of this node.
+
+Required Properties:
+--------------------
+- compatible     : should be one of,
+                       "ti,am3356-pruss-soc-bus" for AM335x family of SoCs
+                       "ti,am4376-pruss-soc-bus" for AM437x family of SoCs
+                       "ti,am5728-pruss-soc-bus" for AM57xx family of SoCs
+                       "ti,k2g-pruss-soc-bus" for 66AK2G family of SoCs
+- reg            : address and size of the PRUSS CFG sub-module register
+                   dictating the interconnect configuration
+- #address-cells : should be 1
+- #size-cells    : should be 1
+- ranges         : standard ranges definition
+
+SoC-specific Required properties:
+---------------------------------
+
+The following are mandatory properties for all OMAP-architecture based SoCs:
+- ti,hwmods      : name of the hwmod associated with the PRUSS instance
+
+The following properties are _required_ only for Keystone 2 66AK2G SoCs only:
+- power-domains  : Should contain a phandle to a PM domain provider node and an
+                   args specifier containing the PRUSS SCI device id value. This
+                   property is as per the binding,
+                       Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
+
+
+PRU-ICSS Node
+==============
+Each PRU-ICSS subsystem instance is represented as a child node of
+the PRUSS SoC bus node, with the individual PRU processor cores, a
+memories node, an INTC node and an MDIO node represented as child nodes
+within this parent PRUSS node.
+
+Required Properties:
+--------------------
+- compatible     : should be one of,
+                       "ti,am3356-pruss" for AM335x family of SoCs
+                       "ti,am4376-pruss" for AM437x family of SoCs
+                       "ti,am5728-pruss" for AM57xx family of SoCs
+                       "ti,k2g-pruss" for 66AK2G family of SoCs
+- reg            : base address and size of the entire PRU-ICSS space
+- interrupts     : all the interrupts generated towards the main host
+                   processor in the SoC. The format depends on the
+                   interrupt specifier for the particular SoC's MPU
+                   parent interrupt controller
+- interrupt-names: should use one of the following names for each interrupt,
+                   the name should match the corresponding host interrupt
+                   number,
+                       "host2", "host3", "host4", "host5", "host6",
+                       "host7", "host8" or "host9"
+                   NOTE: AM437x and 66AK2G SoCs do not have "host7" interrupt
+                         connected to MPU
+- #address-cells : should be 1
+- #size-cells    : should be 1
+- ranges         : no specific range translations required, child nodes have the
+                   same address view as the parent, so should be mentioned without
+                   any value for the property
+
+
+PRU-ICSS Memories Node
+=======================
+The various Data RAMs within a PRU-ICSS are represented as a single
+node with the name 'memories'.
+
+Required Properties:
+--------------------
+- reg            : base address and size for each of the Data RAMs as
+                   mentioned in reg-names, and in the same order as the
+                   reg-names
+- reg-names      : should contain a string(s) from among the following names,
+                   each representing a specific Data RAM region. A PRU-ICSS may
+                   not have all of the Data RAMs. The binding is agnostic
+                   of the order of these reg-names
+                       "dram0" for Data RAM0,
+                       "dram1" for Data RAM1,
+                       "shrdram2" for Shared Data RAM,
+
+
+PRU-ICSS CFG, IEP, MII_RT Nodes
+================================
+The individual sub-modules CFG, IEP and MII_RT are represented as a syscon
+node each for now with specific node names as below:
+                  "cfg" for CFG sub-module,
+                  "iep" for IEP sub-module,
+                  "mii-rt" for MII-RT sub-module,
+
+See Documentation/devicetree/bindings/mfd/syscon.txt for generic syscon
+binding details.
+
+
+PRUSS INTC Node
+================
+Each PRUSS has a single interrupt controller instance that is common to both
+the PRU cores. This should be represented as an interrupt-controller node. The
+bindings for this shall be defined in the file,
+    Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.txt
+
+
+PRU Node
+=========
+Each PRUSS has dual PRU cores, each represented as a remoteproc device through
+a PRU child node each. Each node can optionally be rendered inactive by using
+the standard DT string property, "status". The bindings for this shall be
+defined in the file,
+    Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.txt
+
+
+MDIO Node
+==========
+Each PRUSS has an MDIO module that can be used to control external PHYs. The
+MDIO module used within the PRU-ICSS is an instance of the MDIO Controller
+used in TI Davinci SoCs. Please refer to the corresponding binding document,
+Documentation/devicetree/bindings/net/davinci-mdio.txt for details.
+
+
+Example:
+========
+1.     /* AM33xx PRU-ICSS */
+       pruss_soc_bus: pruss-soc-bus@4a326004 {
+               compatible = "ti,am3356-pruss-soc-bus";
+               ti,hwmods = "pruss";
+               reg = <0x4a326004 0x4>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               pruss: pruss@4a300000 {
+                       compatible = "ti,am3356-pruss";
+                       reg = <0x4a300000 0x80000>;
+                       interrupts = <20 21 22 23 24 25 26 27>;
+                       interrupt-names = "host2", "host3", "host4",
+                                         "host5", "host6", "host7",
+                                         "host8", "host9";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       pruss_mem: memories@4a300000 {
+                               reg = <0x4a300000 0x2000>,
+                                     <0x4a302000 0x2000>,
+                                     <0x4a310000 0x3000>;
+                               reg-names = "dram0", "dram1", "shrdram2";
+                       };
+
+                       pruss_cfg: cfg@4a326000 {
+                               compatible = "syscon";
+                               reg = <0x4a326000 0x2000>;
+                       };
+
+                       pruss_iep: iep@4a32e000 {
+                               compatible = "syscon";
+                               reg = <0x4a32e000 0x31c>;
+                       };
+
+                       pruss_mii_rt: mii-rt@4a332000 {
+                               compatible = "syscon";
+                               reg = <0x4a332000 0x58>;
+                       };
+
+                       pruss_intc: interrupt-controller@4a320000 {
+                               compatible = "ti,am3356-pruss-intc";
+                               reg = <0x4a320000 0x2000>;
+                               interrupt-controller;
+                               #interrupt-cells = <1>;
+                       };
+
+                       pru0: pru@4a334000 {
+                               compatible = "ti,am3356-pru";
+                               reg = <0x4a334000 0x2000>,
+                                     <0x4a322000 0x400>,
+                                     <0x4a322400 0x100>;
+                               reg-names = "iram", "control", "debug";
+                               firmware-name = "am335x-pru0-fw";
+                       };
+
+                       pru1: pru@4a338000 {
+                               compatible = "ti,am3356-pru";
+                               reg = <0x4a338000 0x2000>,
+                                     <0x4a324000 0x400>,
+                                     <0x4a324400 0x100>;
+                               reg-names = "iram", "control", "debug";
+                               firmware-name = "am335x-pru1-fw";
+                       };
+
+                       pruss_mdio: mdio@4a332400 {
+                               compatible = "ti,davinci_mdio";
+                               reg = <0x4a332400 0x90>;
+                               clocks = <&dpll_core_m4_ck>;
+                               clock-names = "fck";
+                               bus_freq = <1000000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+       };
+
+2.     /* AM43xx PRU-ICSS with PRUSS1 node (PRUSS0 not shown completely) */
+       pruss_soc_bus: pruss-soc-bus@54426004 {
+               compatible = "ti,am4376-pruss-soc-bus";
+               reg = <0x54426004 0x4>;
+               ti,hwmods = "pruss";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               pruss1: pruss@54400000 {
+                       compatible = "ti,am4376-pruss";
+                       reg = <0x54400000 0x40000>;
+                       interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH
+                                     GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH
+                                     GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH
+                                     GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH
+                                     GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH
+                                     GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH
+                                     GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "host2", "host3", "host4",
+                                         "host5", "host6", "host8",
+                                         "host9";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       pruss1_mem: memories@54400000 {
+                               reg = <0x54400000 0x2000>,
+                                     <0x54402000 0x2000>,
+                                     <0x54410000 0x8000>;
+                               reg-names = "dram0", "dram1", "shrdram2";
+                       };
+
+                       pruss1_cfg: cfg@54426000 {
+                               compatible = "syscon";
+                               reg = <0x54426000 0x2000>;
+                       };
+
+                       pruss1_iep: iep@5442e000 {
+                               compatible = "syscon";
+                               reg = <0x5442e000 0x31c>;
+                       };
+
+                       pruss1_mii_rt: mii-rt@54432000 {
+                               compatible = "syscon";
+                               reg = <0x54432000 0x58>;
+                       };
+
+                       pruss1_intc: interrupt-controller@54420000 {
+                               compatible = "ti,am4376-pruss-intc";
+                               reg = <0x54420000 0x2000>;
+                               interrupt-controller;
+                               #interrupt-cells = <1>;
+                       };
+
+                       pru1_0: pru@54434000 {
+                               compatible = "ti,am4376-pru";
+                               reg = <0x54434000 0x3000>,
+                                     <0x54422000 0x400>,
+                                     <0x54422400 0x100>;
+                               reg-names = "iram", "control", "debug";
+                               firmware-name = "am437x-pru1_0-fw";
+                       };
+
+                       pru1_1: pru@54438000 {
+                               compatible = "ti,am4376-pru";
+                               reg = <0x54438000 0x3000>,
+                                     <0x54424000 0x400>,
+                                     <0x54424400 0x100>;
+                               reg-names = "iram", "control", "debug";
+                               firmware-name = "am437x-pru1_1-fw";
+                       };
+
+                       pruss1_mdio: mdio@54432400 {
+                               compatible = "ti,davinci_mdio";
+                               reg = <0x54432400 0x90>;
+                               clocks = <&dpll_core_m4_ck>;
+                               clock-names = "fck";
+                               bus_freq = <1000000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               status = "disabled";
+                       };
+               };
+
+               pruss0: pruss@54440000 {
+                       compatible = "ti,am4376-pruss";
+                       reg = <0x54440000 0x40000>;
+                       ...
+               };
+       };
index 73b514dddf65b281b0c3093f40b05496240b5455..e279f0f33c0053203cd6a505c175e03d48b92ddf 100644 (file)
        clocks = <&clk_32768_ck>, <&l4_per_clkctrl AM3_CLKDIV32K_CLKCTRL 0>;
        clock-names = "ext-clk", "int-clk";
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss: pruss@4a300000 {
+               status = "okay";
+       };
+};
index c87d01297a013b60b8752594b13b89392ce95e7a..f9578b10bbe7b2e355f2b08b98a0df7920e982bd 100644 (file)
        clocks = <&clk_32768_ck>, <&l4_per_clkctrl AM3_CLKDIV32K_CLKCTRL 0>;
        clock-names = "ext-clk", "int-clk";
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss: pruss@4a300000 {
+               status = "okay";
+       };
+};
index bf1a40e45c97b1b3a0cbbfa6ea583509f4e5bb8a..a83e5809fd76dbdcb5e66ee867eaade4bf24bd6e 100644 (file)
        clocks = <&clk_32768_ck>, <&l4_per_clkctrl AM3_CLKDIV32K_CLKCTRL 0>;
        clock-names = "ext-clk", "int-clk";
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss: pruss@4a300000 {
+               status = "okay";
+       };
+};
index f2005ecca74fb545ced3e438eb53cb8ea5733a9b..7d2fff2e992cdd002729f8a6aabe62cfa0c747b1 100644 (file)
                reg = <3>;
        };
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss: pruss@4a300000 {
+               status = "okay";
+       };
+};
index d3dd6a16e70a706f214bf5ab54bdff396ed2c2bc..bc9d65db7a46a5bd797ce2763b45730d73cbcca7 100644 (file)
                        };
                };
 
+               pruss_soc_bus: pruss-soc-bus@4a326004 {
+                       compatible = "ti,am3356-pruss-soc-bus";
+                       reg = <0x4a326004 0x4>;
+                       ti,hwmods = "pruss";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       status = "disabled";
+
+                       pruss: pruss@4a300000 {
+                               compatible = "ti,am3356-pruss";
+                               reg = <0x4a300000 0x80000>;
+                               interrupts = <20 21 22 23 24 25 26 27>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host7",
+                                                 "host8", "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               status = "disabled";
+
+                               pruss_mem: memories@4a300000 {
+                                       reg = <0x4a300000 0x2000>,
+                                             <0x4a302000 0x2000>,
+                                             <0x4a310000 0x3000>;
+                                       reg-names = "dram0", "dram1",
+                                                   "shrdram2";
+                               };
+
+                               pruss_cfg: cfg@4a326000 {
+                                       compatible = "syscon";
+                                       reg = <0x4a326000 0x2000>;
+                               };
+
+                               pruss_iep: iep@4a32e000 {
+                                       compatible = "syscon";
+                                       reg = <0x4a32e000 0x31c>;
+                               };
+
+                               pruss_mii_rt: mii-rt@4a332000 {
+                                       compatible = "syscon";
+                                       reg = <0x4a332000 0x58>;
+                               };
+
+                               pruss_intc: interrupt-controller@4a320000 {
+                                       compatible = "ti,am3356-pruss-intc";
+                                       reg = <0x4a320000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru0: pru@4a334000 {
+                                       compatible = "ti,am3356-pru";
+                                       reg = <0x4a334000 0x2000>,
+                                             <0x4a322000 0x400>,
+                                             <0x4a322400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am335x-pru0-fw";
+                                       interrupt-parent = <&pruss_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru1: pru@4a338000 {
+                                       compatible = "ti,am3356-pru";
+                                       reg = <0x4a338000 0x2000>,
+                                             <0x4a324000 0x400>,
+                                             <0x4a324400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am335x-pru1-fw";
+                                       interrupt-parent = <&pruss_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pruss_mdio: mdio@4a332400 {
+                                       compatible = "ti,davinci_mdio";
+                                       reg = <0x4a332400 0x90>;
+                                       clocks = <&dpll_core_m4_ck>;
+                                       clock-names = "fck";
+                                       bus_freq = <1000000>;
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       status = "disabled";
+                               };
+                       };
+               };
+
                elm: elm@48080000 {
                        compatible = "ti,am3352-elm";
                        reg = <0x48080000 0x2000>;
index d4b7c59eec6853f2f836b5b3ffd686988ad0f6b1..37637aee83a724dac8bf3f70d171d7b38e083307 100644 (file)
                        interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
                };
 
+               pruss_soc_bus: pruss-soc-bus@54426004 {
+                       compatible = "ti,am4376-pruss-soc-bus";
+                       reg = <0x54426004 0x4>;
+                       ti,hwmods = "pruss";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       status = "disabled";
+
+                       pruss1: pruss@54400000 {
+                               compatible = "ti,am4376-pruss";
+                               reg = <0x54400000 0x40000>;
+                               interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host8",
+                                                 "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               status = "disabled";
+
+                               pruss1_mem: memories@54400000 {
+                                       reg = <0x54400000 0x2000>,
+                                             <0x54402000 0x2000>,
+                                             <0x54410000 0x8000>;
+                                       reg-names = "dram0", "dram1",
+                                                   "shrdram2";
+                               };
+
+                               pruss1_cfg: cfg@54426000 {
+                                       compatible = "syscon";
+                                       reg = <0x54426000 0x2000>;
+                               };
+
+                               pruss1_iep: iep@5442e000 {
+                                       compatible = "syscon";
+                                       reg = <0x5442e000 0x31c>;
+                               };
+
+                               pruss1_mii_rt: mii-rt@54432000 {
+                                       compatible = "syscon";
+                                       reg = <0x54432000 0x58>;
+                               };
+
+                               pruss1_intc: interrupt-controller@54420000 {
+                                       compatible = "ti,am4376-pruss-intc";
+                                       reg = <0x54420000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru1_0: pru@54434000 {
+                                       compatible = "ti,am4376-pru";
+                                       reg = <0x54434000 0x3000>,
+                                             <0x54422000 0x400>,
+                                             <0x54422400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am437x-pru1_0-fw";
+                                       interrupt-parent = <&pruss1_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru1_1: pru@54438000 {
+                                       compatible = "ti,am4376-pru";
+                                       reg = <0x54438000 0x3000>,
+                                             <0x54424000 0x400>,
+                                             <0x54424400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am437x-pru1_1-fw";
+                                       interrupt-parent = <&pruss1_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pruss1_mdio: mdio@54432400 {
+                                       compatible = "ti,davinci_mdio";
+                                       reg = <0x54432400 0x90>;
+                                       clocks = <&dpll_core_m4_ck>;
+                                       clock-names = "fck";
+                                       bus_freq = <1000000>;
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       status = "disabled";
+                               };
+                       };
+
+                       pruss0: pruss@54440000 {
+                               compatible = "ti,am4376-pruss";
+                               reg = <0x54440000 0x40000>;
+                               interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH
+                                             GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host8",
+                                                 "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               status = "disabled";
+
+                               pruss0_mem: memories@54440000 {
+                                       reg = <0x54440000 0x1000>,
+                                             <0x54442000 0x1000>;
+                                       reg-names = "dram0", "dram1";
+                               };
+
+                               pruss0_cfg: cfg@54466000 {
+                                       compatible = "syscon";
+                                       reg = <0x54466000 0x2000>;
+                               };
+
+                               pruss0_iep: iep@5446e000 {
+                                       compatible = "syscon";
+                                       reg = <0x6e000 0x31c>;
+                               };
+
+                               pruss0_mii_rt: mii-rt@54472000 {
+                                       compatible = "syscon";
+                                       reg = <0x54472000 0x58>;
+                               };
+
+                               pruss0_intc: interrupt-controller@54460000 {
+                                       compatible = "ti,am4376-pruss-intc";
+                                       reg = <0x54460000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru0_0: pru@54474000 {
+                                       compatible = "ti,am4376-pru";
+                                       reg = <0x54474000 0x1000>,
+                                             <0x54462000 0x400>,
+                                             <0x54462400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am437x-pru0_0-fw";
+                                       interrupt-parent = <&pruss0_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru0_1: pru@54478000 {
+                                       compatible = "ti,am4376-pru";
+                                       reg = <0x54478000 0x1000>,
+                                             <0x54464000 0x400>,
+                                             <0x54464400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am437x-pru0_1-fw";
+                                       interrupt-parent = <&pruss0_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+                       };
+               };
+
                mcasp0: mcasp@48038000 {
                        compatible = "ti,am33xx-mcasp-audio";
                        ti,hwmods = "mcasp0";
index 5b97c20c5ed49c2de9432742442a322e4e127728..b93a3c8282ca3df7c1bc5282b7f8477f678351a5 100644 (file)
 &cpu {
        cpu0-supply = <&dcdc2>;
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss1: pruss@54400000 {
+               status = "okay";
+       };
+
+       pruss0: pruss@54440000 {
+               status = "okay";
+       };
+};
index 20132477a87114e45f2f8a317c32e6f807b47085..136b1062e77c3ac1e81dd8373dae609031175249 100644 (file)
                opp-suspend;
        };
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss1: pruss@54400000 {
+               status = "okay";
+       };
+
+       pruss0: pruss@54440000 {
+               status = "okay";
+       };
+};
index d4be3fd0b6f4094643ef98660e0f2dbcb5edca9d..026a0f28f734fd973cc43c1b9228814ad8a584b2 100644 (file)
                };
        };
 };
+
+&pruss_soc_bus {
+       status = "okay";
+
+       pruss1: pruss@54400000 {
+               status = "okay";
+       };
+
+       pruss0: pruss@54440000 {
+               status = "okay";
+       };
+};
index d9a2049a1ea8ad49163c8028d85ab9af45a0af0b..1eca1ae565866bc29e693833ab3ebff30ca8b87e 100644 (file)
@@ -13,6 +13,7 @@
 #include "dra7-mmc-iodelay.dtsi"
 #include "dra72x-mmc-iodelay.dtsi"
 #include "am57xx-idk-common.dtsi"
+#include "dra7-ipu-dsp-common.dtsi"
 
 / {
        model = "TI AM5718 IDK";
                reg = <0x0 0x80000000 0x0 0x40000000>;
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_memory_region: ipu2-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_memory_region: ipu1-memory@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        leds {
                compatible = "gpio-leds";
                cpu0-led {
        vbus-gpio = <&gpio7 22 GPIO_ACTIVE_HIGH>;
 };
 
-&mailbox5 {
+&ipu2 {
        status = "okay";
-       mbox_ipu1_ipc3x: mbox_ipu1_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp1_ipc3x: mbox_dsp1_ipc3x {
-               status = "okay";
-       };
+       memory-region = <&ipu2_memory_region>;
 };
 
-&mailbox6 {
+&ipu1 {
        status = "okay";
-       mbox_ipu2_ipc3x: mbox_ipu2_ipc3x {
-               status = "okay";
-       };
+       memory-region = <&ipu1_memory_region>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_memory_region>;
 };
 
 &pcie1_rc {
index 784639ddf4513a316971ec181b3a1ade74fe18f1..ab893f496458dc2397df199cb53d92764ba27706 100644 (file)
@@ -6,6 +6,7 @@
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 #include "am57xx-idk-common.dtsi"
+#include "dra74-ipu-dsp-common.dtsi"
 
 / {
        memory@0 {
                reg = <0x0 0x80000000 0x0 0x80000000>;
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_memory_region: ipu2-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_memory_region: ipu1-memory@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp2_memory_region: dsp2-memory@9f000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9f000000 0x0 0x800000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        status-leds {
                compatible = "gpio-leds";
                cpu0-led {
        gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>;
 };
 
-&mailbox5 {
+&ipu2 {
        status = "okay";
-       mbox_ipu1_ipc3x: mbox_ipu1_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp1_ipc3x: mbox_dsp1_ipc3x {
-               status = "okay";
-       };
+       memory-region = <&ipu2_memory_region>;
 };
 
-&mailbox6 {
+&ipu1 {
        status = "okay";
-       mbox_ipu2_ipc3x: mbox_ipu2_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp2_ipc3x: mbox_dsp2_ipc3x {
-               status = "okay";
-       };
+       memory-region = <&ipu1_memory_region>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_memory_region>;
+};
+
+&dsp2 {
+       status = "okay";
+       memory-region = <&dsp2_memory_region>;
 };
index ad953113cefbda10f32bf03c6b11e0551dad8834..cecc2f1e53e6cd1011594d584b996c1b178287b3 100644 (file)
@@ -10,6 +10,7 @@
 #include "dra74x.dtsi"
 #include "am57xx-commercial-grade.dtsi"
 #include "dra74x-mmc-iodelay.dtsi"
+#include "dra74-ipu-dsp-common.dtsi"
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 
                reg = <0x0 0x80000000 0x0 0x80000000>;
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_memory_region: ipu2-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_memory_region: ipu1-memory@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp2_memory_region: dsp2-memory@9f000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9f000000 0x0 0x800000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        vdd_3v3: fixedregulator-vdd_3v3 {
                compatible = "regulator-fixed";
                regulator-name = "vdd_3v3";
        rx-num-evt = <32>;
 };
 
-&mailbox5 {
+&pruss_soc_bus1 {
        status = "okay";
-       mbox_ipu1_ipc3x: mbox_ipu1_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp1_ipc3x: mbox_dsp1_ipc3x {
+
+       pruss1: pruss@4b200000 {
                status = "okay";
        };
 };
 
-&mailbox6 {
+&pruss_soc_bus2 {
        status = "okay";
-       mbox_ipu2_ipc3x: mbox_ipu2_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp2_ipc3x: mbox_dsp2_ipc3x {
+
+       pruss2: pruss@4b280000 {
                status = "okay";
        };
 };
+
+&ipu2 {
+       status = "okay";
+       memory-region = <&ipu2_memory_region>;
+};
+
+&ipu1 {
+       status = "okay";
+       memory-region = <&ipu1_memory_region>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_memory_region>;
+};
+
+&dsp2 {
+       status = "okay";
+       memory-region = <&dsp2_memory_region>;
+};
index c9063ffca524c8022f97930964d7b03f4de38c4c..e4c71080fba5fd06e8edd0dd2ea7d4cf7085def9 100644 (file)
                };
        };
 };
+
+&pruss_soc_bus1 {
+       status = "okay";
+
+       pruss1: pruss@4b200000 {
+               status = "okay";
+       };
+};
+
+&pruss_soc_bus2 {
+       status = "okay";
+
+       pruss2: pruss@4b280000 {
+               status = "okay";
+       };
+};
index 47aa53ba6b92236d4616992aeea113a81bfb8c97..165a8ab90b780036a93f51dcfddbd70f1bd0b4b9 100644 (file)
@@ -13,7 +13,9 @@
        #address-cells = <1>;
        #size-cells = <1>;
        chosen { };
-       aliases { };
+       aliases {
+               rproc0 = &dsp;
+       };
 
        memory@c0000000 {
                device_type = "memory";
index 7e18147dc5635b414a4d51a179234adf41f9243e..8e677f95366fbc846deee3648bfa6d9193f94e27 100644 (file)
@@ -6,6 +6,7 @@
  * published by the Free Software Foundation.
  */
 
+#include "dra74-ipu-dsp-common.dtsi"
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/clk/ti-dra7-atl.h>
 #include <dt-bindings/input/input.h>
        rx-num-evt = <32>;
 };
 
-&mailbox5 {
-       status = "okay";
-       mbox_ipu1_ipc3x: mbox_ipu1_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp1_ipc3x: mbox_dsp1_ipc3x {
-               status = "okay";
-       };
-};
-
-&mailbox6 {
-       status = "okay";
-       mbox_ipu2_ipc3x: mbox_ipu2_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp2_ipc3x: mbox_dsp2_ipc3x {
-               status = "okay";
-       };
-};
-
 &pcie1_rc {
        status = "okay";
 };
index 6ed5f915627000010fe9883259c18cb132fd7c96..7f348b56e5f785bd082af57e938e11a23db3c8de 100644 (file)
                regulator-max-microvolt = <1800000>;
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_memory_region: ipu2-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_memory_region: ipu1-memory@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp2_memory_region: dsp2-memory@9f000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9f000000 0x0 0x800000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        evm_3v3_sd: fixedregulator-sd {
                compatible = "regulator-fixed";
                regulator-name = "evm_3v3_sd";
        pinctrl-1 = <&dcan1_pins_sleep>;
        pinctrl-2 = <&dcan1_pins_default>;
 };
+
+&ipu2 {
+       status = "okay";
+       memory-region = <&ipu2_memory_region>;
+};
+
+&ipu1 {
+       status = "okay";
+       memory-region = <&ipu1_memory_region>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_memory_region>;
+};
+
+&dsp2 {
+       status = "okay";
+       memory-region = <&dsp2_memory_region>;
+};
diff --git a/arch/arm/boot/dts/dra7-ipu-dsp-common.dtsi b/arch/arm/boot/dts/dra7-ipu-dsp-common.dtsi
new file mode 100644 (file)
index 0000000..782ad2b
--- /dev/null
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common IPU and DSP data for TI DRA7xx/AM57xx platforms
+ */
+
+&mailbox5 {
+       status = "okay";
+       mbox_ipu1_ipc3x: mbox_ipu1_ipc3x {
+               status = "okay";
+       };
+       mbox_dsp1_ipc3x: mbox_dsp1_ipc3x {
+               status = "okay";
+       };
+};
+
+&mailbox6 {
+       status = "okay";
+       mbox_ipu2_ipc3x: mbox_ipu2_ipc3x {
+               status = "okay";
+       };
+};
+
+&ipu2 {
+       mboxes = <&mailbox6 &mbox_ipu2_ipc3x>;
+       timers = <&timer3>;
+};
+
+&ipu1 {
+       mboxes = <&mailbox5 &mbox_ipu1_ipc3x>;
+       timers = <&timer11>;
+};
+
+&dsp1 {
+       mboxes = <&mailbox5 &mbox_dsp1_ipc3x>;
+       timers = <&timer5>;
+};
index 41d4977b452a5a7b680188264cbb2ba1fd895945..1b37091d5fde1540cf5dc5f917a9777a0e9fa08a 100644 (file)
                        reg = <0x48032000 0x80>;
                        interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer2";
+                       clocks = <&l4per_clkctrl DRA7_TIMER2_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer3: timer@48034000 {
                        reg = <0x48034000 0x80>;
                        interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer3";
+                       clocks = <&l4per_clkctrl DRA7_TIMER3_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer4: timer@48036000 {
                        reg = <0x48036000 0x80>;
                        interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer4";
+                       clocks = <&l4per_clkctrl DRA7_TIMER4_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer5: timer@48820000 {
                        reg = <0x48820000 0x80>;
                        interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer5";
+                       clocks = <&ipu_clkctrl DRA7_TIMER5_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer6: timer@48822000 {
                        reg = <0x48822000 0x80>;
                        interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer6";
+                       clocks = <&ipu_clkctrl DRA7_TIMER6_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer7: timer@48824000 {
                        reg = <0x48824000 0x80>;
                        interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer7";
+                       clocks = <&ipu_clkctrl DRA7_TIMER7_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer8: timer@48826000 {
                        reg = <0x48826000 0x80>;
                        interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer8";
+                       clocks = <&ipu_clkctrl DRA7_TIMER8_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer9: timer@4803e000 {
                        reg = <0x4803e000 0x80>;
                        interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer9";
+                       clocks = <&l4per_clkctrl DRA7_TIMER9_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer10: timer@48086000 {
                        reg = <0x48086000 0x80>;
                        interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer10";
+                       clocks = <&l4per_clkctrl DRA7_TIMER10_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer11: timer@48088000 {
                        reg = <0x48088000 0x80>;
                        interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer11";
+                       clocks = <&l4per_clkctrl DRA7_TIMER11_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer12: timer@4ae20000 {
                        ti,hwmods = "timer12";
                        ti,timer-alwon;
                        ti,timer-secure;
+                       clocks = <&wkupaon_clkctrl DRA7_TIMER12_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer13: timer@48828000 {
                        reg = <0x48828000 0x80>;
                        interrupts = <GIC_SPI 339 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer13";
+                       clocks = <&l4per_clkctrl DRA7_TIMER13_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer14: timer@4882a000 {
                        reg = <0x4882a000 0x80>;
                        interrupts = <GIC_SPI 340 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer14";
+                       clocks = <&l4per_clkctrl DRA7_TIMER14_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer15: timer@4882c000 {
                        reg = <0x4882c000 0x80>;
                        interrupts = <GIC_SPI 341 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer15";
+                       clocks = <&l4per_clkctrl DRA7_TIMER15_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer16: timer@4882e000 {
                        reg = <0x4882e000 0x80>;
                        interrupts = <GIC_SPI 342 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer16";
+                       clocks = <&l4per_clkctrl DRA7_TIMER16_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                wdt2: wdt@4ae14000 {
                        ti,hwmods = "dmm";
                };
 
+               ipu1: ipu@58820000 {
+                       compatible = "ti,dra7-ipu";
+                       reg = <0x58820000 0x10000>;
+                       reg-names = "l2ram";
+                       ti,hwmods = "ipu1";
+                       iommus = <&mmu_ipu1>;
+                       status = "disabled";
+               };
+
+               ipu2: ipu@55020000 {
+                       compatible = "ti,dra7-ipu";
+                       reg = <0x55020000 0x10000>;
+                       reg-names = "l2ram";
+                       ti,hwmods = "ipu2";
+                       iommus = <&mmu_ipu2>;
+                       status = "disabled";
+               };
+
+               dsp1: dsp@40800000 {
+                       compatible = "ti,dra7-dsp";
+                       reg = <0x40800000 0x48000>,
+                             <0x40e00000 0x8000>,
+                             <0x40f00000 0x8000>;
+                       reg-names = "l2ram", "l1pram", "l1dram";
+                       ti,hwmods = "dsp1";
+                       syscon-bootreg = <&scm_conf 0x55c>;
+                       iommus = <&mmu0_dsp1>, <&mmu1_dsp1>;
+                       status = "disabled";
+               };
+
                i2c1: i2c@48070000 {
                        compatible = "ti,omap4-i2c";
                        reg = <0x48070000 0x100>;
                        ti,iommu-bus-err-back;
                };
 
+               pruss_soc_bus1: pruss-soc-bus@4b226004 {
+                       compatible = "ti,am5728-pruss-soc-bus";
+                       reg = <0x4b226004 0x4>;
+                       ti,hwmods = "pruss1";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       status = "disabled";
+
+                       pruss1: pruss@4b200000 {
+                               compatible = "ti,am5728-pruss";
+                               reg = <0x4b200000 0x80000>;
+                               interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host7",
+                                                 "host8", "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               status = "disabled";
+
+                               pruss1_mem: memories@4b200000 {
+                                       reg = <0x4b200000 0x2000>,
+                                             <0x4b202000 0x2000>,
+                                             <0x4b210000 0x8000>;
+                                       reg-names = "dram0", "dram1",
+                                                   "shrdram2";
+                               };
+
+                               pruss1_cfg: cfg@4b226000 {
+                                       compatible = "syscon";
+                                       reg = <0x4b226000 0x2000>;
+                               };
+
+                               pruss1_iep: iep@4b22e000 {
+                                       compatible = "syscon";
+                                       reg = <0x4b22e000 0x31c>;
+                               };
+
+                               pruss1_mii_rt: mii-rt@4b232000 {
+                                       compatible = "syscon";
+                                       reg = <0x4b232000 0x58>;
+                               };
+
+                               pruss1_intc: interrupt-controller@4b220000 {
+                                       compatible = "ti,am5728-pruss-intc";
+                                       reg = <0x4b220000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru1_0: pru@4b234000 {
+                                       compatible = "ti,am5728-pru";
+                                       reg = <0x4b234000 0x3000>,
+                                             <0x4b222000 0x400>,
+                                             <0x4b222400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am57xx-pru1_0-fw";
+                                       interrupt-parent = <&pruss1_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru1_1: pru@4b238000 {
+                                       compatible = "ti,am5728-pru";
+                                       reg = <0x4b238000 0x3000>,
+                                             <0x4b224000 0x400>,
+                                             <0x4b224400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am57xx-pru1_1-fw";
+                                       interrupt-parent = <&pruss1_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pruss1_mdio: mdio@4b232400 {
+                                       compatible = "ti,davinci_mdio";
+                                       reg = <0x4b232400 0x90>;
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       clocks = <&dpll_gmac_h13x2_ck>;
+                                       clock-names = "fck";
+                                       bus_freq = <1000000>;
+                                       status = "disabled";
+                               };
+                       };
+               };
+
+               pruss_soc_bus2: pruss-soc-bus@4b2a6004 {
+                       compatible = "ti,am5728-pruss-soc-bus";
+                       reg = <0x4b2a6004 0x4>;
+                       ti,hwmods = "pruss2";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       status = "disabled";
+
+                       pruss2: pruss@4b280000 {
+                               compatible = "ti,am5728-pruss";
+                               reg = <0x4b280000 0x80000>;
+                               interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 199 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 200 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 201 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 202 IRQ_TYPE_LEVEL_HIGH>,
+                                            <GIC_SPI 203 IRQ_TYPE_LEVEL_HIGH>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host7",
+                                                 "host8", "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               status = "disabled";
+
+                               pruss2_mem: memories@4b280000 {
+                                       reg = <0x4b280000 0x2000>,
+                                             <0x4b282000 0x2000>,
+                                             <0x4b290000 0x8000>;
+                                       reg-names = "dram0", "dram1",
+                                                   "shrdram2";
+                               };
+
+                               pruss2_cfg: cfg@4b2a6000 {
+                                       compatible = "syscon";
+                                       reg = <0x4b2a6000 0x2000>;
+                               };
+
+                               pruss2_iep: iep@4b2ae000 {
+                                       compatible = "syscon";
+                                       reg = <0x4b2ae000 0x31c>;
+                               };
+
+                               pruss2_mii_rt: mii-rt@4b2b2000 {
+                                       compatible = "syscon";
+                                       reg = <0x4b2b2000 0x58>;
+                               };
+
+                               pruss2_intc: interrupt-controller@4b2a0000 {
+                                       compatible = "ti,am5728-pruss-intc";
+                                       reg = <0x4b2a0000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru2_0: pru@4b2b4000 {
+                                       compatible = "ti,am5728-pru";
+                                       reg = <0x4b2b4000 0x3000>,
+                                             <0x4b2a2000 0x400>,
+                                             <0x4b2a2400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am57xx-pru2_0-fw";
+                                       interrupt-parent = <&pruss2_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru2_1: pru@4b2b8000 {
+                                       compatible = "ti,am5728-pru";
+                                       reg = <0x4b2b8000 0x3000>,
+                                             <0x4b2a4000 0x400>,
+                                             <0x4b2a4400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "am57xx-pru2_1-fw";
+                                       interrupt-parent = <&pruss2_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pruss2_mdio: mdio@4b2b2400 {
+                                       compatible = "ti,davinci_mdio";
+                                       reg = <0x4b2b2400 0x90>;
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       clocks = <&dpll_gmac_h13x2_ck>;
+                                       clock-names = "fck";
+                                       bus_freq = <1000000>;
+                                       status = "disabled";
+                               };
+                       };
+               };
+
                abb_mpu: regulator-abb-mpu {
                        compatible = "ti,abb-v3";
                        regulator-name = "abb_mpu";
index c471bf3277b4fd2aeb6077eb3ad4470c14b87187..a7938e72e73f86e510f6d448afd59f4c70676e08 100644 (file)
                reg = <0x0 0x80000000 0x0 0x80000000>; /* 2GB */
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_memory_region: ipu2-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_memory_region: ipu1-memory@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        vpo_sd_1v8_3v3: gpio-regulator-TPS74801 {
                compatible = "regulator-gpio";
 
 &extcon_usb2 {
        vbus-gpio = <&pcf_lcd 15 GPIO_ACTIVE_HIGH>;
 };
+
+&ipu2 {
+       status = "okay";
+       memory-region = <&ipu2_memory_region>;
+};
+
+&ipu1 {
+       status = "okay";
+       memory-region = <&ipu1_memory_region>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_memory_region>;
+};
index e297b923b71a703a6ce27139de9e18d857db62be..637ed7a3c0dd0b0673632edd5c2da374e8e0778a 100644 (file)
@@ -8,6 +8,7 @@
 /dts-v1/;
 
 #include "dra72x.dtsi"
+#include "dra7-ipu-dsp-common.dtsi"
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/clk/ti-dra7-atl.h>
 
        rx-num-evt = <32>;
 };
 
-&mailbox5 {
-       status = "okay";
-       mbox_ipu1_ipc3x: mbox_ipu1_ipc3x {
-               status = "okay";
-       };
-       mbox_dsp1_ipc3x: mbox_dsp1_ipc3x {
-               status = "okay";
-       };
-};
-
-&mailbox6 {
-       status = "okay";
-       mbox_ipu2_ipc3x: mbox_ipu2_ipc3x {
-               status = "okay";
-       };
-};
-
 &pcie1_rc {
        status = "okay";
 };
index bf588d00728d1973c3426f09421dc5c2bbb5bbe0..08c75a7e3e8cf75a61efae16eb81c6b3ab646e30 100644 (file)
                reg = <0x0 0x80000000 0x0 0x80000000>; /* 2GB */
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_cma_pool: ipu2_cma@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_cma_pool: dsp1_cma@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_cma_pool: ipu1_cma@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        evm_1v8_sw: fixedregulator-evm_1v8 {
                compatible = "regulator-fixed";
                regulator-name = "evm_1v8";
        pinctrl-3 = <&mmc2_pins_hs200 &mmc2_iodelay_hs200_rev20_conf>;
        vmmc-supply = <&evm_1v8_sw>;
 };
+
+&ipu2 {
+       status = "okay";
+       memory-region = <&ipu2_cma_pool>;
+};
+
+&ipu1 {
+       status = "okay";
+       memory-region = <&ipu1_cma_pool>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_cma_pool>;
+};
index c572693b16657b69565b3581ac39c9bd8298cba2..417414a3abe0ee739857934545f1a66b29721355 100644 (file)
                reg = <0x0 0x80000000 0x0 0x40000000>; /* 1024 MB */
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_memory_region: ipu2-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_memory_region: dsp1-memory@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_memory_region: ipu1-memory@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        evm_1v8_sw: fixedregulator-evm_1v8 {
                compatible = "regulator-fixed";
                regulator-name = "evm_1v8";
        pinctrl-3 = <&mmc2_pins_hs200 &mmc2_iodelay_hs200_rev10_conf>;
        vmmc-supply = <&evm_1v8_sw>;
 };
+
+&ipu2 {
+       status = "okay";
+       memory-region = <&ipu2_memory_region>;
+};
+
+&ipu1 {
+       status = "okay";
+       memory-region = <&ipu1_memory_region>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_memory_region>;
+};
index c011d2e64feffb036f5f569df757ad9cca05f162..f27fbe6cbafddfd390523f51332cf215253b2346 100644 (file)
 / {
        compatible = "ti,dra722", "ti,dra72", "ti,dra7";
 
+       aliases {
+               rproc0 = &ipu1;
+               rproc1 = &ipu2;
+               rproc2 = &dsp1;
+       };
+
        pmu {
                compatible = "arm,cortex-a15-pmu";
                interrupt-parent = <&wakeupgen>;
diff --git a/arch/arm/boot/dts/dra74-ipu-dsp-common.dtsi b/arch/arm/boot/dts/dra74-ipu-dsp-common.dtsi
new file mode 100644 (file)
index 0000000..95706b8
--- /dev/null
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common IPU and DSP data for TI DRA74x/DRA76x/AM572x/AM574x platforms
+ */
+
+#include "dra7-ipu-dsp-common.dtsi"
+
+&mailbox6 {
+       mbox_dsp2_ipc3x: mbox_dsp2_ipc3x {
+               status = "okay";
+       };
+};
+
+&dsp2 {
+       mboxes = <&mailbox6 &mbox_dsp2_ipc3x>;
+       timers = <&timer6>;
+};
index f1a759e6ef3dc92ca5dece22695ee508f5392a21..492ee7d355ef2f537d739d2a3b8c4b7c16d6ceab 100644 (file)
                };
        };
 
+       aliases {
+               rproc0 = &ipu1;
+               rproc1 = &ipu2;
+               rproc2 = &dsp1;
+               rproc3 = &dsp2;
+       };
+
        pmu {
                compatible = "arm,cortex-a15-pmu";
                interrupt-parent = <&wakeupgen>;
                        #iommu-cells = <0>;
                        ti,syscon-mmuconfig = <&dsp2_system 0x1>;
                };
+
+               dsp2: dsp@41000000 {
+                       compatible = "ti,dra7-dsp";
+                       reg = <0x41000000 0x48000>,
+                             <0x41600000 0x8000>,
+                             <0x41700000 0x8000>;
+                       reg-names = "l2ram", "l1pram", "l1dram";
+                       ti,hwmods = "dsp2";
+                       syscon-bootreg = <&scm_conf 0x560>;
+                       iommus = <&mmu0_dsp2>, <&mmu1_dsp2>;
+                       status = "disabled";
+               };
        };
 };
 
index 5a46163d465f5180b0b220c1feea4fc40e6a54a3..216029d9595e3b608daf7558683506929eef56af 100644 (file)
                reg = <0x0 0x80000000 0x0 0x80000000>;
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ipu2_cma_pool: ipu2_cma@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x95800000 0x0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp1_cma_pool: dsp1_cma@99000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x99000000 0x0 0x4000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu1_cma_pool: ipu1_cma@9d000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9d000000 0x0 0x2000000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               dsp2_cma_pool: dsp2_cma@9f000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x0 0x9f000000 0x0 0x800000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        vsys_12v0: fixedregulator-vsys12v0 {
                /* main supply */
                compatible = "regulator-fixed";
                max-bitrate = <5000000>;
        };
 };
+
+&ipu2 {
+       status = "okay";
+       memory-region = <&ipu2_cma_pool>;
+};
+
+&ipu1 {
+       status = "okay";
+       memory-region = <&ipu1_cma_pool>;
+};
+
+&dsp1 {
+       status = "okay";
+       memory-region = <&dsp1_cma_pool>;
+};
+
+&dsp2 {
+       status = "okay";
+       memory-region = <&dsp2_cma_pool>;
+};
index 66fec5f5d081653fd7865c4cc179f00cafe68806..26c6242a7108b488df30b69069e3ff871a8e4ac2 100644 (file)
                        reusable;
                        status = "okay";
                };
+
+               dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+                       compatible = "ti,keystone-dsp-mem-pool";
+                       reg = <0x00000008 0x20000000 0x00000000 0x10000000>;
+                       no-map;
+                       status = "okay";
+               };
        };
 };
 
index 085e7326ea8e3c3f2e432acaf8c5743a2d56b3a1..c37987182f9b7d2e5ddc86047a48c18f88317912 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <1>;
 
+                       sram-mpm@0 {
+                               compatible = "ti,keystone-dsp-msm-ram";
+                               reg = <0x0 0x80000>;
+                       };
+
                        sram-bm@1f0000 {
                                reg = <0x001f0000 0x8000>;
                        };
index b7f10bf945760e2563fd8c3f84d1ec67eb57b04f..98b1100ccc9e4db57c325733163b16d411c6706a 100644 (file)
                #size-cells = <2>;
                ranges;
 
+               dsp_common_mpm_memory: dsp-common-mpm-memory@81d000000 {
+                       compatible = "ti,keystone-dsp-mem-pool";
+                       reg = <0x00000008 0x1d000000 0x00000000 0x2800000>;
+                       no-map;
+                       status = "okay";
+               };
+
                dsp_common_memory: dsp-common-memory@81f800000 {
                        compatible = "shared-dma-pool";
                        reg = <0x00000008 0x1f800000 0x00000000 0x800000>;
index 2a2d38cf0fff75fc66d923b881a92bc9dd1baebf..681cadf655ed7e70edffc4d074ec0e51c3592c2b 100644 (file)
                #size-cells = <2>;
                ranges;
 
+               dsp_common_mpm_memory: dsp-common-mpm-memory@81d000000 {
+                       compatible = "ti,keystone-dsp-mem-pool";
+                       reg = <0x00000008 0x1d000000 0x00000000 0x2800000>;
+                       no-map;
+                       status = "okay";
+               };
+
                dsp_common_memory: dsp-common-memory@81f800000 {
                        compatible = "shared-dma-pool";
                        reg = <0x00000008 0x1f800000 0x00000000 0x800000>;
index 738b44cf2b0bbdd5eb398af5fff2d6eb60095b53..a84b2092124657649f3df7279124b4f0a6ff5d5b 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <1>;
 
+                       sram-mpm@0 {
+                               compatible = "ti,keystone-dsp-msm-ram";
+                               reg = <0x0 0x80000>;
+                       };
+
                        sram-bm@f7000 {
                                reg = <0x000f7000 0x8000>;
                        };
                                #gpio-cells = <2>;
                                gpio,syscon-dev = <&devctrl 0x240>;
                        };
+
+                       prussgpio0: keystone-pruss-gpio@26c {
+                               compatible = "ti,keystone-dsp-gpio";
+                               reg = <0x26c 0x4>;
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               gpio,syscon-dev = <&devctrl 0x26c>;
+                       };
+
+                       prussgpio1: keystone-pruss-gpio@270 {
+                               compatible = "ti,keystone-dsp-gpio";
+                               reg = <0x270 0x4>;
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               gpio,syscon-dev = <&devctrl 0x270>;
+                       };
+
+                       prussgpio2: keystone-pruss-gpio@274 {
+                               compatible = "ti,keystone-dsp-gpio";
+                               reg = <0x274 0x4>;
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               gpio,syscon-dev = <&devctrl 0x274>;
+                       };
+
+                       prussgpio3: keystone-pruss-gpio@278 {
+                               compatible = "ti,keystone-dsp-gpio";
+                               reg = <0x278 0x4>;
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               gpio,syscon-dev = <&devctrl 0x278>;
+                       };
                };
 
                uart0: serial@2530c00 {
                        interrupts = <GIC_SPI 123 IRQ_TYPE_EDGE_RISING>;
                };
 
+               pruss_soc_bus0: pruss-soc-bus@20aa6004 {
+                       compatible = "ti,k2g-pruss-soc-bus";
+                       reg = <0x20aa6004 0x4>;
+                       power-domains = <&k2g_pds 0x0014>;
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       dma-ranges;
+                       dma-coherent;
+
+                       pruss0: pruss@20a80000 {
+                               compatible = "ti,k2g-pruss";
+                               reg = <0x20a80000 0x40000>;
+                               interrupts = <GIC_SPI 224 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 225 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 226 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 227 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 228 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 230 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 231 IRQ_TYPE_EDGE_RISING>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host8",
+                                                 "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               dma-ranges;
+                               dma-coherent;
+
+                               pruss0_mem: memories@20a80000 {
+                                       reg = <0x20a80000 0x2000>,
+                                             <0x20a82000 0x2000>,
+                                             <0x20a90000 0x10000>;
+                                       reg-names = "dram0", "dram1",
+                                                   "shrdram2";
+                               };
+
+                               pruss0_cfg: cfg@20aa6000 {
+                                       compatible = "syscon";
+                                       reg = <0x20aa6000 0x2000>;
+                               };
+
+                               pruss0_iep: iep@20aae000 {
+                                       compatible = "syscon";
+                                       reg = <0x20aae000 0x31c>;
+                               };
+
+                               pruss0_mii_rt: mii-rt@20ab2000 {
+                                       compatible = "syscon";
+                                       reg = <0x20ab2000 0x70>;
+                               };
+
+                               pruss0_intc: interrupt-controller@20aa0000 {
+                                       compatible = "ti,k2g-pruss-intc";
+                                       reg = <0x20aa0000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru0_0: pru@20ab4000 {
+                                       compatible = "ti,k2g-pru";
+                                       reg = <0x20ab4000 0x3000>,
+                                             <0x20aa2000 0x400>,
+                                             <0x20aa2400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "k2g-pru0_0-fw";
+                                       interrupt-parent = <&pruss0_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru0_1: pru@20ab8000 {
+                                       compatible = "ti,k2g-pru";
+                                       reg = <0x20ab8000 0x3000>,
+                                             <0x20aa4000 0x400>,
+                                             <0x20aa4400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "k2g-pru0_1-fw";
+                                       interrupt-parent = <&pruss0_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pruss0_mdio: mdio@20ab2400 {
+                                       compatible = "ti,davinci_mdio";
+                                       reg = <0x20ab2400 0x90>;
+                                       clocks = <&k2g_clks 0x0014 1>;
+                                       clock-names = "fck";
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       bus_freq = <2500000>;
+                                       status = "disabled";
+                               };
+                       };
+               };
+
+               pruss_soc_bus1: pruss-soc-bus@20ae6004 {
+                       compatible = "ti,k2g-pruss-soc-bus";
+                       reg = <0x20ae6004 0x4>;
+                       power-domains = <&k2g_pds 0x0015>;
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+                       dma-ranges;
+                       dma-coherent;
+
+                       pruss1: pruss@20ac0000 {
+                               compatible = "ti,k2g-pruss";
+                               reg = <0x20ac0000 0x40000>;
+                               interrupts = <GIC_SPI 232 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 233 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 234 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 235 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 236 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 238 IRQ_TYPE_EDGE_RISING>,
+                                            <GIC_SPI 239 IRQ_TYPE_EDGE_RISING>;
+                               interrupt-names = "host2", "host3", "host4",
+                                                 "host5", "host6", "host8",
+                                                 "host9";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges;
+                               dma-ranges;
+                               dma-coherent;
+
+                               pruss1_mem: memories@20ac0000 {
+                                       reg = <0x20ac0000 0x2000>,
+                                             <0x20ac2000 0x2000>,
+                                             <0x20ad0000 0x10000>;
+                                       reg-names = "dram0", "dram1",
+                                                   "shrdram2";
+                               };
+
+                               pruss1_cfg: cfg@20ae6000 {
+                                       compatible = "syscon";
+                                       reg = <0x20ae6000 0x2000>;
+                               };
+
+                               pruss1_iep: iep@20aee000 {
+                                       compatible = "syscon";
+                                       reg = <0x20aee000 0x31c>;
+                               };
+
+                               pruss1_mii_rt: mii-rt@20af2000 {
+                                       compatible = "syscon";
+                                       reg = <0x20af2000 0x70>;
+                               };
+
+                               pruss1_intc: interrupt-controller@20ae0000 {
+                                       compatible = "ti,k2g-pruss-intc";
+                                       reg = <0x20ae0000 0x2000>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <1>;
+                               };
+
+                               pru1_0: pru@20af4000 {
+                                       compatible = "ti,k2g-pru";
+                                       reg = <0x20af4000 0x3000>,
+                                             <0x20ae2000 0x400>,
+                                             <0x20ae2400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "k2g-pru1_0-fw";
+                                       interrupt-parent = <&pruss1_intc>;
+                                       interrupts = <16>, <17>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pru1_1: pru@20af8000 {
+                                       compatible = "ti,k2g-pru";
+                                       reg = <0x20af8000 0x3000>,
+                                             <0x20ae4000 0x400>,
+                                             <0x20ae4400 0x100>;
+                                       reg-names = "iram", "control", "debug";
+                                       firmware-name = "k2g-pru1_1-fw";
+                                       interrupt-parent = <&pruss1_intc>;
+                                       interrupts = <18>, <19>;
+                                       interrupt-names = "vring", "kick";
+                               };
+
+                               pruss1_mdio: mdio@20af2400 {
+                                       compatible = "ti,davinci_mdio";
+                                       reg = <0x20af2400 0x90>;
+                                       clocks = <&k2g_clks 0x0015 1>;
+                                       clock-names = "fck";
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       bus_freq = <2500000>;
+                                       status = "disabled";
+                               };
+                       };
+               };
+
                mdio: mdio@4200f00 {
                        compatible = "ti,keystone_mdio", "ti,davinci_mdio";
                        reg = <0x04200f00 0x100>;
index ad4e22afe13309fbf4f8736ac1aa72d589bf9010..926df79d377eaa3b615a9a7ecdf8181d23a74e3c 100644 (file)
                        reusable;
                        status = "okay";
                };
+
+               dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+                       compatible = "ti,keystone-dsp-mem-pool";
+                       reg = <0x00000008 0x20000000 0x00000000 0x10000000>;
+                       no-map;
+                       status = "okay";
+               };
        };
 
        leds {
index ca0f198ba627ee933ac1c3a4c65918381101672f..88b6f44542916baa44d3f5a69046b131f1dd6957 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <1>;
 
+                       sram-mpm@0 {
+                               compatible = "ti,keystone-dsp-msm-ram";
+                               reg = <0x0 0x80000>;
+                       };
+
                        sram-bm@5f0000 {
                                reg = <0x5f0000 0x8000>;
                        };
index e200533d26a42ac4b00c175bf727103c3290d7fb..60c71225aff820113dd2e253de34d94685ce5f66 100644 (file)
                        reusable;
                        status = "okay";
                };
+
+               dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+                       compatible = "ti,keystone-dsp-mem-pool";
+                       reg = <0x00000008 0x20000000 0x00000000 0x10000000>;
+                       no-map;
+                       status = "okay";
+               };
        };
 };
 
index 374c80124c4eab6647ca7037ff4c65ab6874e395..76cbfabba66a86dc2cfe55f4d72f5215bc4f1d7f 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <1>;
 
+                       sram-mpm@0 {
+                               compatible = "ti,keystone-dsp-msm-ram";
+                               reg = <0x0 0x80000>;
+                       };
+
                        sram-bm@1f8000 {
                                reg = <0x001f8000 0x8000>;
                        };
index 27895c1604b9bb7837bdb674a3b581c4325c0fdd..56b70aae76b1ec9d9d6d2952f2c950e0ee87ee2f 100644 (file)
                reg = <0x80000000 0x40000000>; /* 1 GB */
        };
 
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               dsp_memory_region: dsp-memory@98000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x98000000 0x800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu_memory_region: ipu-memory@98800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0x98800000 0x7000000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        chosen {
                stdout-path = &uart3;
        };
                };
        };
 };
+
+&dsp {
+       status = "okay";
+       memory-region = <&dsp_memory_region>;
+       timers = <&timer5>;
+};
+
+&ipu {
+       status = "okay";
+       memory-region = <&ipu_memory_region>;
+       timers = <&timer3>;
+};
index 1a96d4317c9757e7dc6d991d1a25405b9090e952..f51870087ba51f23d95d3c2296ea2770ab0da1ed 100644 (file)
@@ -29,6 +29,8 @@
                serial1 = &uart2;
                serial2 = &uart3;
                serial3 = &uart4;
+               rproc0 = &dsp;
+               rproc1 = &ipu;
        };
 
        cpus {
                        sram = <&ocmcram>;
                };
 
-               dsp {
-                       compatible = "ti,omap3-c64";
-                       ti,hwmods = "dsp";
-               };
-
                iva {
                        compatible = "ti,ivahd";
                        ti,hwmods = "iva";
                        interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer5";
                        ti,timer-dsp;
+                       clocks = <&abe_clkctrl OMAP4_TIMER5_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer6: timer@4013a000 {
                        interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer6";
                        ti,timer-dsp;
+                       clocks = <&abe_clkctrl OMAP4_TIMER6_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer7: timer@4013c000 {
                        interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer7";
                        ti,timer-dsp;
+                       clocks = <&abe_clkctrl OMAP4_TIMER7_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer8: timer@4013e000 {
                        ti,hwmods = "timer8";
                        ti,timer-pwm;
                        ti,timer-dsp;
+                       clocks = <&abe_clkctrl OMAP4_TIMER8_CLKCTRL 24>;
+                       clock-names = "fck";
+               };
+
+               dsp: dsp {
+                       compatible = "ti,omap4-dsp";
+                       ti,hwmods = "dsp";
+                       syscon-bootreg = <&scm_conf 0x304>;
+                       iommus = <&mmu_dsp>;
+                       mboxes = <&mailbox &mbox_dsp>;
+                       status = "disabled";
+               };
+
+               ipu: ipu@55020000 {
+                       compatible = "ti,omap4-ipu";
+                       reg = <0x55020000 0x10000>;
+                       reg-names = "l2ram";
+                       ti,hwmods = "ipu";
+                       iommus = <&mmu_ipu>;
+                       mboxes = <&mailbox &mbox_ipu>;
+                       status = "disabled";
                };
 
                aes1: aes@4b501000 {
index 592e17fd4eeb7ccd1faa8f9d16635cf562092982..a800a49b1547e432a24faa1b285cf60f4fe9c088 100644 (file)
                reg = <0 0x80000000 0 0x7f000000>; /* 2032 MB */
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               dsp_memory_region: dsp-memory@95000000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0 0x95000000 0 0x800000>;
+                       reusable;
+                       status = "okay";
+               };
+
+               ipu_memory_region: ipu-memory@95800000 {
+                       compatible = "shared-dma-pool";
+                       reg = <0 0x95800000 0 0x3800000>;
+                       reusable;
+                       status = "okay";
+               };
+       };
+
        aliases {
                ethernet = &ethernet;
        };
 &wlcore {
        compatible = "ti,wl1837";
 };
+
+&dsp {
+       status = "okay";
+       memory-region = <&dsp_memory_region>;
+       timers = <&timer5>;
+};
+
+&ipu {
+       status = "okay";
+       memory-region = <&ipu_memory_region>;
+       timers = <&timer3>;
+};
index 574ac11c04896c3d1728c12a2945a46dafe16e03..d09180fb6fe3cd0a6c6767c9ee75cd7480da9f89 100644 (file)
@@ -32,6 +32,8 @@
                serial3 = &uart4;
                serial4 = &uart5;
                serial5 = &uart6;
+               rproc0 = &dsp;
+               rproc1 = &ipu;
        };
 
        cpus {
                        reg = <0x48032000 0x80>;
                        interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer2";
+                       clocks = <&l4per_clkctrl OMAP5_TIMER2_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer3: timer@48034000 {
                        reg = <0x48034000 0x80>;
                        interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer3";
+                       clocks = <&l4per_clkctrl OMAP5_TIMER3_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer4: timer@48036000 {
                        reg = <0x48036000 0x80>;
                        interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer4";
+                       clocks = <&l4per_clkctrl OMAP5_TIMER4_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer5: timer@40138000 {
                        ti,hwmods = "timer5";
                        ti,timer-dsp;
                        ti,timer-pwm;
+                       clocks = <&abe_clkctrl OMAP5_TIMER5_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer6: timer@4013a000 {
                        ti,hwmods = "timer6";
                        ti,timer-dsp;
                        ti,timer-pwm;
+                       clocks = <&abe_clkctrl OMAP5_TIMER6_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer7: timer@4013c000 {
                        interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer7";
                        ti,timer-dsp;
+                       clocks = <&abe_clkctrl OMAP5_TIMER7_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer8: timer@4013e000 {
                        ti,hwmods = "timer8";
                        ti,timer-dsp;
                        ti,timer-pwm;
+                       clocks = <&abe_clkctrl OMAP5_TIMER8_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer9: timer@4803e000 {
                        interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer9";
                        ti,timer-pwm;
+                       clocks = <&l4per_clkctrl OMAP5_TIMER9_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer10: timer@48086000 {
                        interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer10";
                        ti,timer-pwm;
+                       clocks = <&l4per_clkctrl OMAP5_TIMER10_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                timer11: timer@48088000 {
                        interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
                        ti,hwmods = "timer11";
                        ti,timer-pwm;
+                       clocks = <&l4per_clkctrl OMAP5_TIMER11_CLKCTRL 24>;
+                       clock-names = "fck";
                };
 
                wdt2: wdt@4ae14000 {
                        ti,hwmods = "wd_timer2";
                };
 
+               dsp: dsp {
+                       compatible = "ti,omap5-dsp";
+                       ti,hwmods = "dsp";
+                       syscon-bootreg = <&scm_conf 0x304>;
+                       iommus = <&mmu_dsp>;
+                       mboxes = <&mailbox &mbox_dsp>;
+                       status = "disabled";
+               };
+
+               ipu: ipu@55020000 {
+                       compatible = "ti,omap5-ipu";
+                       reg = <0x55020000 0x10000>;
+                       reg-names = "l2ram";
+                       ti,hwmods = "ipu";
+                       iommus = <&mmu_ipu>;
+                       mboxes = <&mailbox &mbox_ipu>;
+                       status = "disabled";
+               };
+
                dmm@4e000000 {
                        compatible = "ti,omap5-dmm";
                        reg = <0x4e000000 0x800>;
index d51a694e74dbde588f52a5ae5a338b02a4906cc6..359c9b11d44b3d06e156f26429508e9079fc370f 100644 (file)
@@ -245,3 +245,6 @@ include/generated/ti-pm-asm-offsets.h: arch/arm/mach-omap2/pm-asm-offsets.s FORC
 $(obj)/sleep33xx.o $(obj)/sleep43xx.o: include/generated/ti-pm-asm-offsets.h
 
 obj-$(CONFIG_OMAP_IOMMU)               += omap-iommu.o
+ifneq ($(CONFIG_OMAP_REMOTEPROC),)
+obj-y                                  += remoteproc.o
+endif
index a95dbac57a814a92d23e660f2273549da6cce2cc..2c1403f6a51393a568c2f3c28a710868bc5cd49b 100644 (file)
@@ -535,7 +535,6 @@ static struct omap_hwmod omap44xx_dsp_hwmod = {
                        .clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET,
                        .rstctrl_offs = OMAP4_RM_TESLA_RSTCTRL_OFFSET,
                        .context_offs = OMAP4_RM_TESLA_TESLA_CONTEXT_OFFSET,
-                       .modulemode   = MODULEMODE_HWCTRL,
                },
        },
 };
@@ -1469,7 +1468,6 @@ static struct omap_hwmod omap44xx_ipu_hwmod = {
                        .clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET,
                        .rstctrl_offs = OMAP4_RM_DUCATI_RSTCTRL_OFFSET,
                        .context_offs = OMAP4_RM_DUCATI_DUCATI_CONTEXT_OFFSET,
-                       .modulemode   = MODULEMODE_HWCTRL,
                },
        },
 };
index 115473d441cde08bdbf360572b35ca41ae500ebd..9b5dbc017c71b69e97ececb3338f825dcbaa48a8 100644 (file)
@@ -320,6 +320,36 @@ static struct omap_hwmod omap54xx_dmic_hwmod = {
        },
 };
 
+/*
+ * 'dsp' class
+ * dsp sub-system
+ */
+
+static struct omap_hwmod_class omap54xx_dsp_hwmod_class = {
+       .name   = "dsp",
+};
+
+static struct omap_hwmod_rst_info omap54xx_dsp_resets[] = {
+       { .name = "dsp", .rst_shift = 0 },
+};
+
+/* dsp */
+static struct omap_hwmod omap54xx_dsp_hwmod = {
+       .name           = "dsp",
+       .class          = &omap54xx_dsp_hwmod_class,
+       .clkdm_name     = "dsp_clkdm",
+       .rst_lines      = omap54xx_dsp_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(omap54xx_dsp_resets),
+       .main_clk       = "dpll_iva_h11x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_DSP_DSP_CLKCTRL_OFFSET,
+                       .rstctrl_offs = OMAP54XX_RM_DSP_RSTCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_DSP_DSP_CONTEXT_OFFSET,
+               },
+       },
+};
+
 /*
  * 'dss' class
  * display sub-system
@@ -902,6 +932,37 @@ static struct omap_hwmod omap54xx_i2c5_hwmod = {
        },
 };
 
+/*
+ * 'ipu' class
+ * imaging processor unit
+ */
+
+static struct omap_hwmod_class omap54xx_ipu_hwmod_class = {
+       .name   = "ipu",
+};
+
+static struct omap_hwmod_rst_info omap54xx_ipu_resets[] = {
+       { .name = "cpu0", .rst_shift = 0 },
+       { .name = "cpu1", .rst_shift = 1 },
+};
+
+/* ipu */
+static struct omap_hwmod omap54xx_ipu_hwmod = {
+       .name           = "ipu",
+       .class          = &omap54xx_ipu_hwmod_class,
+       .clkdm_name     = "ipu_clkdm",
+       .rst_lines      = omap54xx_ipu_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(omap54xx_ipu_resets),
+       .main_clk       = "dpll_core_h22x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = OMAP54XX_CM_IPU_IPU_CLKCTRL_OFFSET,
+                       .rstctrl_offs = OMAP54XX_RM_IPU_RSTCTRL_OFFSET,
+                       .context_offs = OMAP54XX_RM_IPU_IPU_CONTEXT_OFFSET,
+               },
+       },
+};
+
 /*
  * 'kbd' class
  * keyboard controller
@@ -2074,6 +2135,14 @@ static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_1 = {
        .user           = OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* dsp -> l3_main_1 */
+static struct omap_hwmod_ocp_if omap54xx_dsp__l3_main_1 = {
+       .master         = &omap54xx_dsp_hwmod,
+       .slave          = &omap54xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* l4_cfg -> mmu_dsp */
 static struct omap_hwmod_ocp_if omap54xx_l4_cfg__mmu_dsp = {
        .master         = &omap54xx_l4_cfg_hwmod,
@@ -2106,6 +2175,14 @@ static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_2 = {
        .user           = OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* l3_main_2 -> ipu */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__ipu = {
+       .master         = &omap54xx_l3_main_2_hwmod,
+       .slave          = &omap54xx_ipu_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* l3_main_2 -> mmu_ipu */
 static struct omap_hwmod_ocp_if omap54xx_l3_main_2__mmu_ipu = {
        .master         = &omap54xx_l3_main_2_hwmod,
@@ -2695,7 +2772,9 @@ static struct omap_hwmod_ocp_if *omap54xx_hwmod_ocp_ifs[] __initdata = {
        &omap54xx_l3_main_3__l3_instr,
        &omap54xx_l3_main_2__l3_main_1,
        &omap54xx_l4_cfg__l3_main_1,
+       &omap54xx_dsp__l3_main_1,
        &omap54xx_mpu__l3_main_1,
+       &omap54xx_l3_main_2__ipu,
        &omap54xx_l3_main_1__l3_main_2,
        &omap54xx_l4_cfg__l3_main_2,
        &omap54xx_l3_main_1__l3_main_3,
index ba9aa0bf3cfb251fee9e043091989d4b37eb65ec..892c58ea0cf939741de833820aa24c6a835f7a29 100644 (file)
@@ -550,6 +550,53 @@ static struct omap_hwmod dra7xx_tptc1_hwmod = {
        },
 };
 
+/*
+ * 'dsp' class
+ * dsp sub-system
+ */
+
+static struct omap_hwmod_class dra7xx_dsp_hwmod_class = {
+       .name   = "dsp",
+};
+
+static struct omap_hwmod_rst_info dra7xx_dsp_resets[] = {
+       { .name = "dsp", .rst_shift = 0 },
+};
+
+/* dsp1 processor */
+static struct omap_hwmod dra7xx_dsp1_hwmod = {
+       .name           = "dsp1",
+       .class          = &dra7xx_dsp_hwmod_class,
+       .clkdm_name     = "dsp1_clkdm",
+       .rst_lines      = dra7xx_dsp_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(dra7xx_dsp_resets),
+       .main_clk       = "dpll_dsp_m2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = DRA7XX_CM_DSP1_DSP1_CLKCTRL_OFFSET,
+                       .rstctrl_offs = DRA7XX_RM_DSP1_RSTCTRL_OFFSET,
+                       .context_offs = DRA7XX_RM_DSP1_DSP1_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/* dsp2 processor */
+static struct omap_hwmod dra7xx_dsp2_hwmod = {
+       .name           = "dsp2",
+       .class          = &dra7xx_dsp_hwmod_class,
+       .clkdm_name     = "dsp2_clkdm",
+       .rst_lines      = dra7xx_dsp_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(dra7xx_dsp_resets),
+       .main_clk       = "dpll_dsp_m2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = DRA7XX_CM_DSP2_DSP2_CLKCTRL_OFFSET,
+                       .rstctrl_offs = DRA7XX_RM_DSP2_RSTCTRL_OFFSET,
+                       .context_offs = DRA7XX_RM_DSP2_DSP2_CONTEXT_OFFSET,
+               },
+       },
+};
+
 /*
  * 'dss' class
  *
@@ -1168,6 +1215,54 @@ static struct omap_hwmod dra7xx_i2c5_hwmod = {
        },
 };
 
+/*
+ * 'ipu' class
+ * imaging processor unit
+ */
+
+static struct omap_hwmod_class dra7xx_ipu_hwmod_class = {
+       .name   = "ipu",
+};
+
+static struct omap_hwmod_rst_info dra7xx_ipu_resets[] = {
+       { .name = "cpu0", .rst_shift = 0 },
+       { .name = "cpu1", .rst_shift = 1 },
+};
+
+/* ipu1 processor */
+static struct omap_hwmod dra7xx_ipu1_hwmod = {
+       .name           = "ipu1",
+       .class          = &dra7xx_ipu_hwmod_class,
+       .clkdm_name     = "ipu1_clkdm",
+       .rst_lines      = dra7xx_ipu_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(dra7xx_ipu_resets),
+       .main_clk       = "ipu1_gfclk_mux",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = DRA7XX_CM_IPU1_IPU1_CLKCTRL_OFFSET,
+                       .rstctrl_offs = DRA7XX_RM_IPU1_RSTCTRL_OFFSET,
+                       .context_offs = DRA7XX_RM_IPU1_IPU1_CONTEXT_OFFSET,
+               },
+       },
+};
+
+/* ipu2 processor */
+static struct omap_hwmod dra7xx_ipu2_hwmod = {
+       .name           = "ipu2",
+       .class          = &dra7xx_ipu_hwmod_class,
+       .clkdm_name     = "ipu2_clkdm",
+       .rst_lines      = dra7xx_ipu_resets,
+       .rst_lines_cnt  = ARRAY_SIZE(dra7xx_ipu_resets),
+       .main_clk       = "dpll_core_h22x2_ck",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = DRA7XX_CM_IPU2_IPU2_CLKCTRL_OFFSET,
+                       .rstctrl_offs = DRA7XX_RM_IPU2_RSTCTRL_OFFSET,
+                       .context_offs = DRA7XX_RM_IPU2_IPU2_CONTEXT_OFFSET,
+               },
+       },
+};
+
 /*
  * 'mailbox' class
  *
@@ -2030,6 +2125,42 @@ static struct omap_hwmod dra7xx_pciess2_hwmod = {
        },
 };
 
+/*
+ * 'pru-icss' class
+ * Programmable Real-Time Unit and Industrial Communication Subsystem
+ */
+static struct omap_hwmod_class dra7xx_pruss_hwmod_class = {
+       .name   = "pruss",
+};
+
+/* pru-icss1 */
+static struct omap_hwmod dra7xx_pruss1_hwmod = {
+       .name           = "pruss1",
+       .class          = &dra7xx_pruss_hwmod_class,
+       .clkdm_name     = "l4per2_clkdm",
+       .prcm           = {
+               .omap4  = {
+                       .clkctrl_offs   = DRA7XX_CM_L4PER2_PRUSS1_CLKCTRL_OFFSET,
+                       .context_offs   = DRA7XX_RM_L4PER2_PRUSS1_CONTEXT_OFFSET,
+                       .modulemode     = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
+/* pru-icss2 */
+static struct omap_hwmod dra7xx_pruss2_hwmod = {
+       .name           = "pruss2",
+       .class          = &dra7xx_pruss_hwmod_class,
+       .clkdm_name     = "l4per2_clkdm",
+       .prcm           = {
+               .omap4  = {
+                       .clkctrl_offs   = DRA7XX_CM_L4PER2_PRUSS2_CLKCTRL_OFFSET,
+                       .context_offs   = DRA7XX_RM_L4PER2_PRUSS2_CONTEXT_OFFSET,
+                       .modulemode     = MODULEMODE_SWCTRL,
+               },
+       },
+};
+
 /*
  * 'qspi' class
  *
@@ -3153,6 +3284,22 @@ static struct omap_hwmod_ocp_if dra7xx_l3_main_1__tptc1 = {
        .user           = OCP_USER_MPU,
 };
 
+/* dsp1 -> l3_main_1 */
+static struct omap_hwmod_ocp_if dra7xx_dsp1__l3_main_1 = {
+       .master         = &dra7xx_dsp1_hwmod,
+       .slave          = &dra7xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* dsp2 -> l3_main_1 */
+static struct omap_hwmod_ocp_if dra7xx_dsp2__l3_main_1 = {
+       .master         = &dra7xx_dsp2_hwmod,
+       .slave          = &dra7xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* l3_main_1 -> dss */
 static struct omap_hwmod_ocp_if dra7xx_l3_main_1__dss = {
        .master         = &dra7xx_l3_main_1_hwmod,
@@ -3417,6 +3564,22 @@ static struct omap_hwmod_ocp_if dra7xx_l4_per1__i2c5 = {
        .user           = OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* ipu1 -> l3_main_1 */
+static struct omap_hwmod_ocp_if dra7xx_ipu1__l3_main_1 = {
+       .master         = &dra7xx_ipu1_hwmod,
+       .slave          = &dra7xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* ipu2 -> l3_main_1 */
+static struct omap_hwmod_ocp_if dra7xx_ipu2__l3_main_1 = {
+       .master         = &dra7xx_ipu2_hwmod,
+       .slave          = &dra7xx_l3_main_1_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* l4_cfg -> mailbox1 */
 static struct omap_hwmod_ocp_if dra7xx_l4_cfg__mailbox1 = {
        .master         = &dra7xx_l4_cfg_hwmod,
@@ -3641,6 +3804,22 @@ static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pciess2 = {
        .user           = OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* l4_cfg -> pruss1 */
+static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pruss1 = {
+       .master         = &dra7xx_l4_cfg_hwmod,
+       .slave          = &dra7xx_pruss1_hwmod,
+       .clk            = "dpll_gmac_h13x2_ck",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l4_cfg -> pruss2 */
+static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pruss2 = {
+       .master         = &dra7xx_l4_cfg_hwmod,
+       .slave          = &dra7xx_pruss2_hwmod,
+       .clk            = "dpll_gmac_h13x2_ck",
+       .user           = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* l3_main_1 -> qspi */
 static struct omap_hwmod_ocp_if dra7xx_l3_main_1__qspi = {
        .master         = &dra7xx_l3_main_1_hwmod,
@@ -4045,6 +4224,7 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
        &dra7xx_l3_main_1__tptc1,
        &dra7xx_l3_main_1__dss,
        &dra7xx_l3_main_1__dispc,
+       &dra7xx_dsp1__l3_main_1,
        &dra7xx_l3_main_1__hdmi,
        &dra7xx_l3_main_1__aes1,
        &dra7xx_l3_main_1__aes2,
@@ -4065,6 +4245,8 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
        &dra7xx_l4_per1__i2c3,
        &dra7xx_l4_per1__i2c4,
        &dra7xx_l4_per1__i2c5,
+       &dra7xx_ipu1__l3_main_1,
+       &dra7xx_ipu2__l3_main_1,
        &dra7xx_l4_cfg__mailbox1,
        &dra7xx_l4_per3__mailbox2,
        &dra7xx_l4_per3__mailbox3,
@@ -4097,6 +4279,8 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
        &dra7xx_l4_cfg__pciess1,
        &dra7xx_l3_main_1__pciess2,
        &dra7xx_l4_cfg__pciess2,
+       &dra7xx_l4_cfg__pruss1,
+       &dra7xx_l4_cfg__pruss2,
        &dra7xx_l3_main_1__qspi,
        &dra7xx_l4_cfg__sata,
        &dra7xx_l4_cfg__smartreflex_core,
@@ -4151,6 +4335,7 @@ static struct omap_hwmod_ocp_if *dra7xx_gp_hwmod_ocp_ifs[] __initdata = {
 
 /* SoC variant specific hwmod links */
 static struct omap_hwmod_ocp_if *dra76x_hwmod_ocp_ifs[] __initdata = {
+       &dra7xx_dsp2__l3_main_1,
        &dra7xx_l3_main_1__mmu0_dsp2,
        &dra7xx_l3_main_1__mmu1_dsp2,
        &dra7xx_l4_per3__usb_otg_ss4,
@@ -4162,6 +4347,7 @@ static struct omap_hwmod_ocp_if *acd_76x_hwmod_ocp_ifs[] __initdata = {
 };
 
 static struct omap_hwmod_ocp_if *dra74x_hwmod_ocp_ifs[] __initdata = {
+       &dra7xx_dsp2__l3_main_1,
        &dra7xx_l3_main_1__mmu0_dsp2,
        &dra7xx_l3_main_1__mmu1_dsp2,
        &dra7xx_l4_per3__usb_otg_ss4,
index 9126961b5f5fd35ccae34dc60b1c223180f0f486..2271add0a6619e50648598e5a4e8843921b680ed 100644 (file)
@@ -24,7 +24,9 @@
 #include <linux/platform_data/pinctrl-single.h>
 #include <linux/platform_data/hsmmc-omap.h>
 #include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/remoteproc-omap.h>
 #include <linux/platform_data/ti-sysc.h>
+#include <linux/platform_data/ti-pruss.h>
 #include <linux/platform_data/wkup_m3.h>
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 
@@ -35,6 +37,7 @@
 #include "omap-secure.h"
 #include "soc.h"
 #include "hsmmc.h"
+#include "remoteproc.h"
 
 static struct omap_hsmmc_platform_data __maybe_unused mmc_pdata[2];
 
@@ -421,6 +424,14 @@ static void __init omap3_pandora_legacy_init(void)
 }
 #endif /* CONFIG_ARCH_OMAP3 */
 
+#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
+       defined(CONFIG_SOC_DRA7XX)
+static struct omap_rproc_pdata omap4_ipu_dsp_pdata = {
+       .device_enable = omap_rproc_device_enable,
+       .device_shutdown = omap_rproc_device_shutdown,
+};
+#endif
+
 #if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
        defined(CONFIG_SOC_DRA7XX)
 static struct iommu_platform_data omap4_iommu_pdata = {
@@ -436,6 +447,12 @@ static struct wkup_m3_platform_data wkup_m3_data = {
        .assert_reset = omap_device_assert_hardreset,
        .deassert_reset = omap_device_deassert_hardreset,
 };
+
+static struct pruss_platform_data pruss_pdata = {
+       .reset_name = "pruss",
+       .assert_reset = omap_device_assert_hardreset,
+       .deassert_reset = omap_device_deassert_hardreset,
+};
 #endif
 
 #ifdef CONFIG_SOC_OMAP5
@@ -587,10 +604,22 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = {
 #ifdef CONFIG_SOC_AM33XX
        OF_DEV_AUXDATA("ti,am3352-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
                       &wkup_m3_data),
+       OF_DEV_AUXDATA("ti,am3356-pruss-soc-bus", 0x4a326004,
+                      "4a326004.pruss-soc-bus", &pruss_pdata),
 #endif
 #ifdef CONFIG_SOC_AM43XX
        OF_DEV_AUXDATA("ti,am4372-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
                       &wkup_m3_data),
+       OF_DEV_AUXDATA("ti,am4376-pruss-soc-bus", 0x54426004,
+                      "54426004.pruss_soc_bus", &pruss_pdata),
+#endif
+#ifdef CONFIG_ARCH_OMAP4
+       OF_DEV_AUXDATA("ti,omap4-dsp", 0, "dsp", &omap4_ipu_dsp_pdata),
+       OF_DEV_AUXDATA("ti,omap4-ipu", 0x55020000, "ipu", &omap4_ipu_dsp_pdata),
+#endif
+#ifdef CONFIG_SOC_OMAP5
+       OF_DEV_AUXDATA("ti,omap5-dsp", 0, "dsp", &omap4_ipu_dsp_pdata),
+       OF_DEV_AUXDATA("ti,omap5-ipu", 0x55020000, "ipu", &omap4_ipu_dsp_pdata),
 #endif
 #if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
        OF_DEV_AUXDATA("ti,omap4-iommu", 0x4a066000, "4a066000.mmu",
@@ -619,6 +648,14 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = {
                       &omap4_iommu_pdata),
        OF_DEV_AUXDATA("ti,dra7-iommu", 0x58882000, "58882000.mmu",
                       &dra7_ipu1_dsp_iommu_pdata),
+       OF_DEV_AUXDATA("ti,dra7-ipu", 0x55020000, "55020000.ipu",
+                      &omap4_ipu_dsp_pdata),
+       OF_DEV_AUXDATA("ti,dra7-ipu", 0x58820000, "58820000.ipu",
+                      &omap4_ipu_dsp_pdata),
+       OF_DEV_AUXDATA("ti,dra7-dsp", 0x40800000, "40800000.dsp",
+                      &omap4_ipu_dsp_pdata),
+       OF_DEV_AUXDATA("ti,dra7-dsp", 0x41000000, "41000000.dsp",
+                      &omap4_ipu_dsp_pdata),
 #endif
        /* Common auxdata */
        OF_DEV_AUXDATA("ti,sysc", 0, NULL, &ti_sysc_pdata),
diff --git a/arch/arm/mach-omap2/remoteproc.c b/arch/arm/mach-omap2/remoteproc.c
new file mode 100644 (file)
index 0000000..093ebcf
--- /dev/null
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Remote processor machine-specific module for OMAP4+ SoCs
+ *
+ * Copyright (C) 2011-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *      Suman Anna <s-anna@ti.com>
+ */
+
+#include <linux/kernel.h>
+
+#include "omap_device.h"
+#include "remoteproc.h"
+
+/**
+ * omap_rproc_device_enable - enable the remoteproc device
+ * @pdev: the rproc platform device
+ *
+ * This function performs the necessary low-level functions to enable
+ * a remoteproc device to start executing. This typically includes
+ * releasing the reset lines, and enabling the clocks for the device.
+ * We do not usually expect this function to fail.
+ *
+ * Return: 0 on success, or the return code from the failed function
+ */
+int omap_rproc_device_enable(struct platform_device *pdev)
+{
+       int ret = -EINVAL;
+       struct omap_device *od = to_omap_device(pdev);
+
+       if (!od) {
+               dev_err(&pdev->dev, "device does not have a backing omap_device\n");
+               goto out;
+       }
+
+       /*
+        * This reset management follows a device name check to differentiate
+        * DSP and IPU processor subsystems. This check is weak and is ok for
+        * now because of the dependencies against the pdata-quirks, where
+        * the devices are given specific device names that satisfy the
+        * criteria for the check. It can easily be replaced with a stronger
+        * check like device node compatibility check, if needed.
+        */
+       if (strstr(dev_name(&pdev->dev), "dsp")) {
+               ret = omap_device_deassert_hardreset(pdev, "dsp");
+               if (ret)
+                       goto out;
+       } else if (strstr(dev_name(&pdev->dev), "ipu")) {
+               ret = omap_device_deassert_hardreset(pdev, "cpu0");
+               if (ret)
+                       goto out;
+
+               ret = omap_device_deassert_hardreset(pdev, "cpu1");
+               if (ret)
+                       goto out;
+       } else {
+               dev_err(&pdev->dev, "unsupported remoteproc\n");
+               goto out;
+       }
+
+       ret = omap_device_enable(pdev);
+
+out:
+       if (ret)
+               dev_err(&pdev->dev, "%s failed, ret = %d\n", __func__, ret);
+       return ret;
+}
+
+/**
+ * omap_rproc_device_shutdown - shutdown the remoteproc device
+ * @pdev: the rproc platform device
+ *
+ * This function performs the necessary low-level functions to shutdown
+ * a remoteproc device. This typically includes disabling the clocks
+ * for the device and asserting the associated reset lines. We do not
+ * usually expect this function to fail.
+ *
+ * Return: 0 on success, or the return code from the failed function
+ */
+int omap_rproc_device_shutdown(struct platform_device *pdev)
+{
+       int ret = -EINVAL;
+       struct omap_device *od = to_omap_device(pdev);
+
+       if (!od) {
+               dev_err(&pdev->dev, "device does not have a backing omap_device\n");
+               goto out;
+       }
+
+       ret = omap_device_idle(pdev);
+       if (ret)
+               goto out;
+
+       /*
+        * This reset management follows a device name check to differentiate
+        * DSP and IPU processor subsystems. This check is weak and is ok for
+        * now because of the dependencies against the pdata-quirks, where
+        * the devices are given specific device names that satisfy the
+        * criteria for the check. It can easily be replaced with a stronger
+        * check like device node compatibility check, if needed.
+        */
+       if (strstr(dev_name(&pdev->dev), "dsp")) {
+               ret = omap_device_assert_hardreset(pdev, "dsp");
+       } else if (strstr(dev_name(&pdev->dev), "ipu")) {
+               ret = omap_device_assert_hardreset(pdev, "cpu1");
+               if (ret)
+                       goto out;
+
+               ret = omap_device_assert_hardreset(pdev, "cpu0");
+               if (ret)
+                       goto out;
+       } else {
+               dev_err(&pdev->dev, "unsupported remoteproc\n");
+       }
+
+out:
+       if (ret)
+               dev_err(&pdev->dev, "%s failed, ret = %d\n", __func__, ret);
+       return ret;
+}
diff --git a/arch/arm/mach-omap2/remoteproc.h b/arch/arm/mach-omap2/remoteproc.h
new file mode 100644 (file)
index 0000000..80244a6
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Remote processor machine-specific quirks for OMAP4+ SoCs
+ *
+ * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *      Suman Anna <s-anna@ti.com>
+ */
+
+#ifndef __ARCH_ARM_MACH_OMAP2_REMOTEPROC_H
+#define __ARCH_ARM_MACH_OMAP2_REMOTEPROC_H
+
+#include "linux/platform_device.h"
+
+#if IS_ENABLED(CONFIG_OMAP_REMOTEPROC)
+int omap_rproc_device_enable(struct platform_device *pdev);
+int omap_rproc_device_shutdown(struct platform_device *pdev);
+#else
+static inline int omap_rproc_device_enable(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline int omap_rproc_device_shutdown(struct platform_device *pdev)
+{
+       return 0;
+}
+#endif
+
+#endif
index 12e0a2d1991124504c3ac9a0c5245b991b9921f4..e58bf8d1fadcc852549b568b99025148c0e41ba2 100644 (file)
@@ -83,7 +83,7 @@ static const struct omap_clkctrl_reg_data am3_l4_per_clkctrl_regs[] __initconst
        { AM3_EPWMSS2_CLKCTRL, NULL, CLKF_SW_SUP, "l4ls_gclk" },
        { AM3_L3_INSTR_CLKCTRL, NULL, CLKF_SW_SUP, "l3_gclk", "l3_clkdm" },
        { AM3_L3_MAIN_CLKCTRL, NULL, CLKF_SW_SUP, "l3_gclk", "l3_clkdm" },
-       { AM3_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP, "pruss_ocp_gclk", "pruss_ocp_clkdm" },
+       { AM3_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP | CLKF_NO_IDLEST, "pruss_ocp_gclk", "pruss_ocp_clkdm" },
        { AM3_TIMER5_CLKCTRL, NULL, CLKF_SW_SUP, "timer5_fck" },
        { AM3_TIMER6_CLKCTRL, NULL, CLKF_SW_SUP, "timer6_fck" },
        { AM3_MMC2_CLKCTRL, NULL, CLKF_SW_SUP, "mmc_clk" },
index 63c5ddb501876993f0584364f44ed56a28bc175d..0c89d0e0f0ba2342a56e81a8bdc80574e55d00f4 100644 (file)
@@ -140,7 +140,7 @@ static const struct omap_clkctrl_reg_data am4_l4_per_clkctrl_regs[] __initconst
        { AM4_QSPI_CLKCTRL, NULL, CLKF_SW_SUP, "l3s_gclk", "l3s_clkdm" },
        { AM4_USB_OTG_SS0_CLKCTRL, am4_usb_otg_ss0_bit_data, CLKF_SW_SUP, "l3s_gclk", "l3s_clkdm" },
        { AM4_USB_OTG_SS1_CLKCTRL, am4_usb_otg_ss1_bit_data, CLKF_SW_SUP, "l3s_gclk", "l3s_clkdm" },
-       { AM4_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP, "pruss_ocp_gclk", "pruss_ocp_clkdm" },
+       { AM4_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP | CLKF_NO_IDLEST, "pruss_ocp_gclk", "pruss_ocp_clkdm" },
        { AM4_L4_LS_CLKCTRL, NULL, CLKF_SW_SUP, "l4ls_gclk" },
        { AM4_D_CAN0_CLKCTRL, NULL, CLKF_SW_SUP, "dcan0_fck" },
        { AM4_D_CAN1_CLKCTRL, NULL, CLKF_SW_SUP, "dcan1_fck" },
index a23deecb75546059f90422662d62e23ada7c2b05..20b178cd56bab65167c6fe9f83dbc50e68cdc8d6 100644 (file)
@@ -773,6 +773,17 @@ static struct ti_dt_clk omap44xx_clks[] = {
        DT_CLK(NULL, "usb_tll_hs_usb_ch2_clk", "l3_init_cm:0048:10"),
        DT_CLK(NULL, "utmi_p1_gfclk", "l3_init_cm:0038:24"),
        DT_CLK(NULL, "utmi_p2_gfclk", "l3_init_cm:0038:25"),
+       DT_CLK("4a318000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("48032000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("48034000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("48036000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("4803e000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("48086000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("48088000.timer", "timer_sys_ck", "sys_clkin_ck"),
+       DT_CLK("40138000.timer", "timer_sys_ck", "syc_clk_div_ck"),
+       DT_CLK("4013a000.timer", "timer_sys_ck", "syc_clk_div_ck"),
+       DT_CLK("4013c000.timer", "timer_sys_ck", "syc_clk_div_ck"),
+       DT_CLK("4013e000.timer", "timer_sys_ck", "syc_clk_div_ck"),
        { .node_name = NULL },
 };
 
index 5d3fc560b6bcc533ffacb0d47a87bdd85d99bae0..7c09646d0b41b78a7830699bac0010e10deda247 100644 (file)
@@ -533,6 +533,17 @@ static struct ti_dt_clk omap54xx_clks[] = {
        DT_CLK(NULL, "usb_tll_hs_usb_ch2_clk", "l3init_cm:0048:10"),
        DT_CLK(NULL, "utmi_p1_gfclk", "l3init_cm:0038:24"),
        DT_CLK(NULL, "utmi_p2_gfclk", "l3init_cm:0038:25"),
+       DT_CLK("4ae18000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("48032000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("48034000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("48036000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("4803e000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("48086000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("48088000.timer", "timer_sys_ck", "sys_clkin"),
+       DT_CLK("40138000.timer", "timer_sys_ck", "dss_syc_gfclk_div"),
+       DT_CLK("4013a000.timer", "timer_sys_ck", "dss_syc_gfclk_div"),
+       DT_CLK("4013c000.timer", "timer_sys_ck", "dss_syc_gfclk_div"),
+       DT_CLK("4013e000.timer", "timer_sys_ck", "dss_syc_gfclk_div"),
        { .node_name = NULL },
 };
 
index e4812e7d97b0a6820e793d690032be06725b7068..dd059ae73c7d81da7e48238cbcdd6f70cfc44ef8 100644 (file)
@@ -771,6 +771,21 @@ static struct ti_dt_clk dra7xx_clks[] = {
        DT_CLK(NULL, "timer_32k_ck", "sys_32k_ck"),
        DT_CLK(NULL, "sys_clkin_ck", "timer_sys_clk_div"),
        DT_CLK(NULL, "sys_clkin", "sys_clkin1"),
+       DT_CLK("4ae18000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48032000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48034000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48036000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("4803e000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48086000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48088000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48820000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48822000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48824000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48826000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("48828000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("4882a000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("4882c000.timer", "timer_sys_ck", "timer_sys_clk_div"),
+       DT_CLK("4882e000.timer", "timer_sys_ck", "timer_sys_clk_div"),
        DT_CLK(NULL, "atl_dpll_clk_mux", "atl_cm:0000:24"),
        DT_CLK(NULL, "atl_gfclk_mux", "atl_cm:0000:26"),
        DT_CLK(NULL, "dcan1_sys_clk_mux", "wkupaon_cm:0068:24"),
index 4cce6b224b87c1104daecfe0f4769344853b8dbf..d7adbc102649619bef817a522e35934d2612a111 100644 (file)
@@ -138,31 +138,6 @@ static int omap_dm_timer_reset(struct omap_dm_timer *timer)
        return 0;
 }
 
-static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer)
-{
-       int ret;
-       struct clk *parent;
-
-       /*
-        * FIXME: OMAP1 devices do not use the clock framework for dmtimers so
-        * do not call clk_get() for these devices.
-        */
-       if (!timer->fclk)
-               return -ENODEV;
-
-       parent = clk_get(&timer->pdev->dev, NULL);
-       if (IS_ERR(parent))
-               return -ENODEV;
-
-       ret = clk_set_parent(timer->fclk, parent);
-       if (ret < 0)
-               pr_err("%s: failed to set parent\n", __func__);
-
-       clk_put(parent);
-
-       return ret;
-}
-
 static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
 {
        int ret;
@@ -272,9 +247,7 @@ static int omap_dm_timer_prepare(struct omap_dm_timer *timer)
        __omap_dm_timer_enable_posted(timer);
        omap_dm_timer_disable(timer);
 
-       rc = omap_dm_timer_of_set_source(timer);
-       if (rc == -ENODEV)
-               return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
+       rc = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
 
        return rc;
 }
index fbd1ec8070efa89e5439321f410af09579e76b1e..b49c2470fa94ad76df1be15c6b4ac7a969485772 100644 (file)
@@ -88,3 +88,4 @@ obj-$(CONFIG_GOLDFISH_PIC)            += irq-goldfish-pic.o
 obj-$(CONFIG_NDS32)                    += irq-ativic32.o
 obj-$(CONFIG_QCOM_PDC)                 += qcom-pdc.o
 obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
+obj-$(CONFIG_TI_PRUSS)                 += irq-pruss-intc.o
diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c
new file mode 100644 (file)
index 0000000..022b4cf
--- /dev/null
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PRU-ICSS INTC IRQChip driver for various TI SoCs
+ *
+ * Copyright (C) 2016-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *     Andrew F. Davis <afd@ti.com>
+ *     Suman Anna <s-anna@ti.com>
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pruss_driver.h>
+
+/*
+ * Number of host interrupts reaching the main MPU sub-system. Note that this
+ * is not the same as the total number of host interrupts supported by the PRUSS
+ * INTC instance
+ */
+#define MAX_HOST_NUM_IRQS      8
+
+/* PRU_ICSS_INTC registers */
+#define PRU_INTC_REVID         0x0000
+#define PRU_INTC_CR            0x0004
+#define PRU_INTC_GER           0x0010
+#define PRU_INTC_GNLR          0x001C
+#define PRU_INTC_SISR          0x0020
+#define PRU_INTC_SICR          0x0024
+#define PRU_INTC_EISR          0x0028
+#define PRU_INTC_EICR          0x002C
+#define PRU_INTC_HIEISR                0x0034
+#define PRU_INTC_HIDISR                0x0038
+#define PRU_INTC_GPIR          0x0080
+#define PRU_INTC_SRSR0         0x0200
+#define PRU_INTC_SRSR1         0x0204
+#define PRU_INTC_SECR0         0x0280
+#define PRU_INTC_SECR1         0x0284
+#define PRU_INTC_ESR0          0x0300
+#define PRU_INTC_ESR1          0x0304
+#define PRU_INTC_ECR0          0x0380
+#define PRU_INTC_ECR1          0x0384
+#define PRU_INTC_CMR(x)                (0x0400 + (x) * 4)
+#define PRU_INTC_HMR(x)                (0x0800 + (x) * 4)
+#define PRU_INTC_HIPIR(x)      (0x0900 + (x) * 4)
+#define PRU_INTC_SIPR0         0x0D00
+#define PRU_INTC_SIPR1         0x0D04
+#define PRU_INTC_SITR0         0x0D80
+#define PRU_INTC_SITR1         0x0D84
+#define PRU_INTC_HINLR(x)      (0x1100 + (x) * 4)
+#define PRU_INTC_HIER          0x1500
+
+/* HIPIR register bit-fields */
+#define INTC_HIPIR_NONE_HINT   0x80000000
+
+static const char * const irq_names[] = {
+       "host2", "host3", "host4", "host5", "host6", "host7", "host8", "host9",
+};
+
+/**
+ * struct pruss_intc_match_data - match data to handle SoC variations
+ * @no_host7_intr: flag denoting the absence of host7 interrupt into MPU
+ */
+struct pruss_intc_match_data {
+       bool no_host7_intr;
+};
+
+/**
+ * struct pruss_intc - PRUSS interrupt controller structure
+ * @pruss: back-reference to parent PRUSS structure
+ * @irqs: kernel irq numbers corresponding to PRUSS host interrupts
+ * @base: base virtual address of INTC register space
+ * @irqchip: irq chip for this interrupt controller
+ * @domain: irq domain for this interrupt controller
+ * @config_map: stored INTC configuration mapping data
+ * @lock: mutex to serialize access to INTC
+ * @host_mask: indicate which HOST IRQs are enabled
+ */
+struct pruss_intc {
+       struct pruss *pruss;
+       unsigned int irqs[MAX_HOST_NUM_IRQS];
+       void __iomem *base;
+       struct irq_chip *irqchip;
+       struct irq_domain *domain;
+       struct pruss_intc_config config_map;
+       struct mutex lock; /* PRUSS INTC lock */
+       u32 host_mask;
+};
+
+static inline u32 pruss_intc_read_reg(struct pruss_intc *intc, unsigned int reg)
+{
+       return readl_relaxed(intc->base + reg);
+}
+
+static inline void pruss_intc_write_reg(struct pruss_intc *intc,
+                                       unsigned int reg, u32 val)
+{
+       writel_relaxed(val, intc->base + reg);
+}
+
+static int pruss_intc_check_write(struct pruss_intc *intc, unsigned int reg,
+                                 unsigned int sysevent)
+{
+       if (!intc)
+               return -EINVAL;
+
+       if (sysevent >= MAX_PRU_SYS_EVENTS)
+               return -EINVAL;
+
+       pruss_intc_write_reg(intc, reg, sysevent);
+
+       return 0;
+}
+
+static struct pruss_intc *to_pruss_intc(struct pruss *pruss)
+{
+       struct device_node *parent = pruss->dev->of_node;
+       struct device_node *np;
+       struct platform_device *pdev;
+       struct pruss_intc *intc = NULL;
+
+       np = of_get_child_by_name(parent, "interrupt-controller");
+       if (!np) {
+               dev_err(pruss->dev, "pruss does not have an interrupt-controller node\n");
+               return NULL;
+       }
+
+       pdev = of_find_device_by_node(np);
+       if (!pdev) {
+               dev_err(pruss->dev, "no associated platform device\n");
+               goto out;
+       }
+
+       intc = platform_get_drvdata(pdev);
+out:
+       of_node_put(np);
+       return intc;
+}
+
+/**
+ * pruss_intc_configure() - configure the PRUSS INTC
+ * @pruss: the pruss instance
+ * @intc_config: PRU core-specific INTC configuration
+ *
+ * Configures the PRUSS INTC with the provided configuration from
+ * a PRU core. Any existing event to channel mappings or channel to
+ * host interrupt mappings are checked to make sure there are no
+ * conflicting configuration between both the PRU cores. The function
+ * is intended to be used only by the PRU remoteproc driver.
+ *
+ * Returns 0 on success, or a suitable error code otherwise
+ */
+int pruss_intc_configure(struct pruss *pruss,
+                        struct pruss_intc_config *intc_config)
+{
+       struct device *dev = pruss->dev;
+       struct pruss_intc *intc = to_pruss_intc(pruss);
+       int i, idx, ret;
+       s8 ch, host;
+       u64 sysevt_mask = 0;
+       u32 ch_mask = 0;
+       u32 host_mask = 0;
+       u32 val;
+
+       if (!intc)
+               return -EINVAL;
+
+       mutex_lock(&intc->lock);
+
+       /*
+        * configure channel map registers - each register holds map info
+        * for 4 events, with each event occupying the lower nibble in
+        * a register byte address in little-endian fashion
+        */
+       for (i = 0; i < ARRAY_SIZE(intc_config->sysev_to_ch); i++) {
+               ch = intc_config->sysev_to_ch[i];
+               if (ch < 0)
+                       continue;
+
+               /* check if sysevent already assigned */
+               if (intc->config_map.sysev_to_ch[i] != -1) {
+                       dev_err(dev, "event %d (req. channel %d) already assigned to channel %d\n",
+                               i, ch, intc->config_map.sysev_to_ch[i]);
+                       ret = -EEXIST;
+                       goto unlock;
+               }
+
+               intc->config_map.sysev_to_ch[i] = ch;
+
+               idx = i / 4;
+               val = pruss_intc_read_reg(intc, PRU_INTC_CMR(idx));
+               val |= ch << ((i & 3) * 8);
+               pruss_intc_write_reg(intc, PRU_INTC_CMR(idx), val);
+               sysevt_mask |= BIT_ULL(i);
+               ch_mask |= BIT(ch);
+
+               dev_dbg(dev, "SYSEV%d -> CH%d (CMR%d 0x%08x)\n", i, ch, idx,
+                       pruss_intc_read_reg(intc, PRU_INTC_CMR(idx)));
+       }
+
+       /*
+        * set host map registers - each register holds map info for
+        * 4 channels, with each channel occupying the lower nibble in
+        * a register byte address in little-endian fashion
+        */
+       for (i = 0; i < ARRAY_SIZE(intc_config->ch_to_host); i++) {
+               host = intc_config->ch_to_host[i];
+               if (host < 0)
+                       continue;
+
+               /* check if channel already assigned */
+               if (intc->config_map.ch_to_host[i] != -1) {
+                       dev_err(dev, "channel %d (req. intr_no %d) already assigned to intr_no %d\n",
+                               i, host, intc->config_map.ch_to_host[i]);
+                       ret = -EEXIST;
+                       goto unlock;
+               }
+
+               /* check if host intr is already in use by other PRU */
+               if (intc->host_mask & (1U << host)) {
+                       dev_err(dev, "%s: host intr %d already in use\n",
+                               __func__, host);
+                       ret = -EEXIST;
+                       goto unlock;
+               }
+
+               intc->config_map.ch_to_host[i] = host;
+
+               idx = i / 4;
+
+               val = pruss_intc_read_reg(intc, PRU_INTC_HMR(idx));
+               val |= host << ((i & 3) * 8);
+               pruss_intc_write_reg(intc, PRU_INTC_HMR(idx), val);
+
+               ch_mask |= BIT(i);
+               host_mask |= BIT(host);
+
+               dev_dbg(dev, "CH%d -> HOST%d (HMR%d 0x%08x)\n", i, host, idx,
+                       pruss_intc_read_reg(intc, PRU_INTC_HMR(idx)));
+       }
+
+       dev_info(dev, "configured system_events = 0x%016llx intr_channels = 0x%08x host_intr = 0x%08x\n",
+                sysevt_mask, ch_mask, host_mask);
+
+       /* enable system events, writing 0 has no-effect */
+       pruss_intc_write_reg(intc, PRU_INTC_ESR0, lower_32_bits(sysevt_mask));
+       pruss_intc_write_reg(intc, PRU_INTC_SECR0, lower_32_bits(sysevt_mask));
+       pruss_intc_write_reg(intc, PRU_INTC_ESR1, upper_32_bits(sysevt_mask));
+       pruss_intc_write_reg(intc, PRU_INTC_SECR1, upper_32_bits(sysevt_mask));
+
+       /* enable host interrupts */
+       for (i = 0; i < MAX_PRU_HOST_INT; i++) {
+               if (host_mask & BIT(i))
+                       pruss_intc_write_reg(intc, PRU_INTC_HIEISR, i);
+       }
+
+       /* global interrupt enable */
+       pruss_intc_write_reg(intc, PRU_INTC_GER, 1);
+
+       intc->host_mask |= host_mask;
+
+       mutex_unlock(&intc->lock);
+       return 0;
+
+unlock:
+       mutex_unlock(&intc->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pruss_intc_configure);
+
+/**
+ * pruss_intc_unconfigure() - unconfigure the PRUSS INTC
+ * @pruss: the pruss instance
+ * @intc_config: PRU core specific INTC configuration
+ *
+ * Undo whatever was done in pruss_intc_configure() for a PRU core.
+ * It should be sufficient to just mark the resources free in the
+ * global map and disable the host interrupts and sysevents.
+ */
+int pruss_intc_unconfigure(struct pruss *pruss,
+                          struct pruss_intc_config *intc_config)
+{
+       struct device *dev = pruss->dev;
+       struct pruss_intc *intc = to_pruss_intc(pruss);
+       int i;
+       s8 ch, host;
+       u64 sysevt_mask = 0;
+       u32 host_mask = 0;
+
+       if (!intc)
+               return -EINVAL;
+
+       mutex_lock(&intc->lock);
+
+       for (i = 0; i < ARRAY_SIZE(intc_config->sysev_to_ch); i++) {
+               ch = intc_config->sysev_to_ch[i];
+               if (ch < 0)
+                       continue;
+
+               /* mark sysevent free in global map */
+               intc->config_map.sysev_to_ch[i] = -1;
+               sysevt_mask |= BIT_ULL(i);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(intc_config->ch_to_host); i++) {
+               host = intc_config->ch_to_host[i];
+               if (host < 0)
+                       continue;
+
+               /* mark channel free in global map */
+               intc->config_map.ch_to_host[i] = -1;
+               host_mask |= BIT(host);
+       }
+
+       dev_info(dev, "unconfigured system_events = 0x%016llx host_intr = 0x%08x\n",
+                sysevt_mask, host_mask);
+
+       /* disable system events, writing 0 has no-effect */
+       pruss_intc_write_reg(intc, PRU_INTC_ECR0, lower_32_bits(sysevt_mask));
+       pruss_intc_write_reg(intc, PRU_INTC_ECR1, upper_32_bits(sysevt_mask));
+       /* clear any pending status */
+       pruss_intc_write_reg(intc, PRU_INTC_SECR0, lower_32_bits(sysevt_mask));
+       pruss_intc_write_reg(intc, PRU_INTC_SECR1, upper_32_bits(sysevt_mask));
+
+       /* disable host interrupts */
+       for (i = 0; i < MAX_PRU_HOST_INT; i++) {
+               if (host_mask & BIT(i))
+                       pruss_intc_write_reg(intc, PRU_INTC_HIDISR, i);
+       }
+
+       intc->host_mask &= ~host_mask;
+       mutex_unlock(&intc->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pruss_intc_unconfigure);
+
+static void pruss_intc_init(struct pruss_intc *intc)
+{
+       int i;
+
+       /* configure polarity to active high for all system interrupts */
+       pruss_intc_write_reg(intc, PRU_INTC_SIPR0, 0xffffffff);
+       pruss_intc_write_reg(intc, PRU_INTC_SIPR1, 0xffffffff);
+
+       /* configure type to pulse interrupt for all system interrupts */
+       pruss_intc_write_reg(intc, PRU_INTC_SITR0, 0);
+       pruss_intc_write_reg(intc, PRU_INTC_SITR1, 0);
+
+       /* clear all 16 interrupt channel map registers */
+       for (i = 0; i < 16; i++)
+               pruss_intc_write_reg(intc, PRU_INTC_CMR(i), 0);
+
+       /* clear all 3 host interrupt map registers */
+       for (i = 0; i < 3; i++)
+               pruss_intc_write_reg(intc, PRU_INTC_HMR(i), 0);
+}
+
+static void pruss_intc_irq_ack(struct irq_data *data)
+{
+       struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
+       unsigned int hwirq = data->hwirq;
+
+       pruss_intc_check_write(intc, PRU_INTC_SICR, hwirq);
+}
+
+static void pruss_intc_irq_mask(struct irq_data *data)
+{
+       struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
+       unsigned int hwirq = data->hwirq;
+
+       pruss_intc_check_write(intc, PRU_INTC_EICR, hwirq);
+}
+
+static void pruss_intc_irq_unmask(struct irq_data *data)
+{
+       struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
+       unsigned int hwirq = data->hwirq;
+
+       pruss_intc_check_write(intc, PRU_INTC_EISR, hwirq);
+}
+
+static int pruss_intc_irq_retrigger(struct irq_data *data)
+{
+       struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
+       unsigned int hwirq = data->hwirq;
+
+       return pruss_intc_check_write(intc, PRU_INTC_SISR, hwirq);
+}
+
+static int pruss_intc_irq_reqres(struct irq_data *data)
+{
+       if (!try_module_get(THIS_MODULE))
+               return -ENODEV;
+
+       return 0;
+}
+
+static void pruss_intc_irq_relres(struct irq_data *data)
+{
+       module_put(THIS_MODULE);
+}
+
+/**
+ * pruss_intc_trigger() - trigger a PRU system event
+ * @irq: linux IRQ number associated with a PRU system event
+ *
+ * Trigger an interrupt by signalling a specific PRU system event.
+ * This can be used by PRUSS client users to raise/send an event to
+ * a PRU or any other core that is listening on the host interrupt
+ * mapped to that specific PRU system event. The @irq variable is the
+ * Linux IRQ number associated with a specific PRU system event that
+ * a client user/application uses. The interrupt mappings for this is
+ * provided by the PRUSS INTC irqchip instance.
+ *
+ * Returns 0 on success, or an error value upon failure.
+ */
+int pruss_intc_trigger(unsigned int irq)
+{
+       struct irq_desc *desc;
+
+       if (irq <= 0)
+               return -EINVAL;
+
+       desc = irq_to_desc(irq);
+       if (!desc)
+               return -EINVAL;
+
+       pruss_intc_irq_retrigger(&desc->irq_data);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pruss_intc_trigger);
+
+static int pruss_intc_irq_domain_map(struct irq_domain *d, unsigned int virq,
+                                    irq_hw_number_t hw)
+{
+       struct pruss_intc *intc = d->host_data;
+
+       irq_set_chip_data(virq, intc);
+       irq_set_chip_and_handler(virq, intc->irqchip, handle_level_irq);
+
+       return 0;
+}
+
+static void pruss_intc_irq_domain_unmap(struct irq_domain *d, unsigned int virq)
+{
+       irq_set_chip_and_handler(virq, NULL, NULL);
+       irq_set_chip_data(virq, NULL);
+}
+
+static const struct irq_domain_ops pruss_intc_irq_domain_ops = {
+       .xlate  = irq_domain_xlate_onecell,
+       .map    = pruss_intc_irq_domain_map,
+       .unmap  = pruss_intc_irq_domain_unmap,
+};
+
+static void pruss_intc_irq_handler(struct irq_desc *desc)
+{
+       unsigned int irq = irq_desc_get_irq(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct pruss_intc *intc = irq_get_handler_data(irq);
+       u32 hipir;
+       unsigned int virq;
+       int i, hwirq;
+
+       chained_irq_enter(chip, desc);
+
+       /* find our host irq number */
+       for (i = 0; i < MAX_HOST_NUM_IRQS; i++)
+               if (intc->irqs[i] == irq)
+                       break;
+       if (i == MAX_HOST_NUM_IRQS)
+               goto err;
+
+       i += MIN_PRU_HOST_INT;
+
+       /* get highest priority pending PRUSS system event */
+       hipir = pruss_intc_read_reg(intc, PRU_INTC_HIPIR(i));
+       while (!(hipir & BIT(31))) {
+               hwirq = hipir & GENMASK(9, 0);
+               virq = irq_linear_revmap(intc->domain, hwirq);
+
+               /*
+                * XXX: manually ACK any system events that do not have a
+                * handler mapped yet
+                */
+               if (unlikely(!virq))
+                       pruss_intc_check_write(intc, PRU_INTC_SICR, hwirq);
+               else
+                       generic_handle_irq(virq);
+
+               /* get next system event */
+               hipir = pruss_intc_read_reg(intc, PRU_INTC_HIPIR(i));
+       }
+err:
+       chained_irq_exit(chip, desc);
+}
+
+static int pruss_intc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct platform_device *ppdev = to_platform_device(dev->parent);
+       struct pruss_intc *intc;
+       struct resource *res;
+       struct irq_chip *irqchip;
+       int i, irq;
+       const struct pruss_intc_match_data *data;
+       bool skip_host7;
+
+       data = of_device_get_match_data(dev);
+       skip_host7 = data ? data->no_host7_intr : false;
+
+       intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL);
+       if (!intc)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, intc);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       intc->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(intc->base)) {
+               dev_err(dev, "failed to parse and map intc memory resource\n");
+               return PTR_ERR(intc->base);
+       }
+
+       dev_dbg(dev, "intc memory: pa %pa size 0x%zx va %pK\n", &res->start,
+               (size_t)resource_size(res), intc->base);
+
+       mutex_init(&intc->lock);
+
+       for (i = 0; i < ARRAY_SIZE(intc->config_map.sysev_to_ch); i++)
+               intc->config_map.sysev_to_ch[i] = -1;
+
+       for (i = 0; i < ARRAY_SIZE(intc->config_map.ch_to_host); i++)
+               intc->config_map.ch_to_host[i] = -1;
+
+       intc->pruss = platform_get_drvdata(ppdev);
+       pruss_intc_init(intc);
+
+       irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL);
+       if (!irqchip)
+               return -ENOMEM;
+
+       irqchip->irq_ack = pruss_intc_irq_ack;
+       irqchip->irq_mask = pruss_intc_irq_mask;
+       irqchip->irq_unmask = pruss_intc_irq_unmask;
+       irqchip->irq_retrigger = pruss_intc_irq_retrigger;
+       irqchip->irq_request_resources = pruss_intc_irq_reqres;
+       irqchip->irq_release_resources = pruss_intc_irq_relres;
+       irqchip->name = dev_name(dev);
+       intc->irqchip = irqchip;
+
+       /* always 64 events */
+       intc->domain = irq_domain_add_linear(dev->of_node, MAX_PRU_SYS_EVENTS,
+                                            &pruss_intc_irq_domain_ops, intc);
+       if (!intc->domain)
+               return -ENOMEM;
+
+       for (i = 0; i < MAX_HOST_NUM_IRQS; i++) {
+               irq = platform_get_irq_byname(ppdev, irq_names[i]);
+               if (irq < 0) {
+                       if (!strcmp(irq_names[i], "host7") && !!skip_host7)
+                               continue;
+
+                       dev_err(dev->parent, "platform_get_irq_byname failed for %s : %d\n",
+                               irq_names[i], irq);
+                       goto fail_irq;
+               }
+
+               intc->irqs[i] = irq;
+               irq_set_handler_data(irq, intc);
+               irq_set_chained_handler(irq, pruss_intc_irq_handler);
+       }
+
+       return 0;
+
+fail_irq:
+       irq_domain_remove(intc->domain);
+       return irq;
+}
+
+static int pruss_intc_remove(struct platform_device *pdev)
+{
+       struct pruss_intc *intc = platform_get_drvdata(pdev);
+       unsigned int hwirq;
+
+       if (intc->domain) {
+               for (hwirq = 0; hwirq < MAX_PRU_SYS_EVENTS; hwirq++)
+                       irq_dispose_mapping(irq_find_mapping(intc->domain,
+                                                            hwirq));
+               irq_domain_remove(intc->domain);
+       }
+
+       return 0;
+}
+
+static const struct pruss_intc_match_data am437x_pruss_intc_data = {
+       .no_host7_intr = true,
+};
+
+static const struct pruss_intc_match_data k2g_pruss_intc_data = {
+       .no_host7_intr = true,
+};
+
+static const struct of_device_id pruss_intc_of_match[] = {
+       {
+               .compatible = "ti,am3356-pruss-intc",
+               .data = NULL,
+       },
+       {
+               .compatible = "ti,am4376-pruss-intc",
+               .data = &am437x_pruss_intc_data,
+       },
+       {
+               .compatible = "ti,am5728-pruss-intc",
+               .data = NULL,
+       },
+       {
+               .compatible = "ti,k2g-pruss-intc",
+               .data = &k2g_pruss_intc_data,
+       },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, pruss_intc_of_match);
+
+static struct platform_driver pruss_intc_driver = {
+       .driver = {
+               .name = "pruss-intc",
+               .of_match_table = pruss_intc_of_match,
+       },
+       .probe  = pruss_intc_probe,
+       .remove = pruss_intc_remove,
+};
+module_platform_driver(pruss_intc_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_DESCRIPTION("PRU-ICSS INTC Driver");
+MODULE_LICENSE("GPL v2");
index 052d4dd347f90c185916b5db0708c20379be599d..722aa4ee8c16bfd132f145a165d3822abe597eee 100644 (file)
@@ -24,7 +24,7 @@ config IMX_REMOTEPROC
 
 config OMAP_REMOTEPROC
        tristate "OMAP remoteproc support"
-       depends on ARCH_OMAP4 || SOC_OMAP5
+       depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX
        depends on OMAP_IOMMU
        select MAILBOX
        select OMAP2PLUS_MBOX
@@ -77,6 +77,7 @@ config DA8XX_REMOTEPROC
 config KEYSTONE_REMOTEPROC
        tristate "Keystone Remoteproc support"
        depends on ARCH_KEYSTONE
+       depends on UIO
        help
          Say Y here here to support Keystone remote processors (DSP)
          via the remote processor framework.
@@ -84,6 +85,20 @@ config KEYSTONE_REMOTEPROC
          It's safe to say N here if you're not interested in the Keystone
          DSPs or just want to use a bare minimum kernel.
 
+config PRU_REMOTEPROC
+       tristate "TI PRU remoteproc support"
+       depends on TI_PRUSS
+       default TI_PRUSS
+       select MAILBOX
+       select OMAP2PLUS_MBOX if ARCH_OMAP2PLUS
+       help
+         Support for TI PRU remote processors present within a PRU-ICSS
+         subsystem via the remote processor framework.
+
+         Say Y or M here to support the Programmable Realtime Unit (PRU)
+         processors on various TI SoCs. It's safe to say N here if you're
+         not interested in the PRU or if you are unsure.
+
 config QCOM_ADSP_PIL
        tristate "Qualcomm ADSP Peripheral Image Loader"
        depends on OF && ARCH_QCOM
index 03332fa7e2ee7515d5281c175e632d4a600b3382..d005af3c71a8cc55691b612d51f1694a9b892ca5 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_OMAP_REMOTEPROC)         += omap_remoteproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)            += wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)         += da8xx_remoteproc.o
 obj-$(CONFIG_KEYSTONE_REMOTEPROC)      += keystone_remoteproc.o
+obj-$(CONFIG_PRU_REMOTEPROC)           += pru_rproc.o
 obj-$(CONFIG_QCOM_ADSP_PIL)            += qcom_adsp_pil.o
 obj-$(CONFIG_QCOM_RPROC_COMMON)                += qcom_common.o
 obj-$(CONFIG_QCOM_Q6V5_COMMON)         += qcom_q6v5.o
index e230bef71be1c67abb92e44ca540545bdf3333ff..c8561c5b63e50d47ef6dd2b9e5c0ffd6fe025526 100644 (file)
@@ -226,7 +226,7 @@ static int da8xx_rproc_get_internal_memories(struct platform_device *pdev,
                                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",
+               dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %pK 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);
@@ -352,6 +352,9 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
                goto free_rproc;
        }
 
+       if (rproc_get_id(rproc) < 0)
+               dev_warn(dev, "device does not have an alias id or platform device id\n");
+
        return 0;
 
 free_rproc:
index 54c07fd3f2042efdbf60d6a1feb6d891af0a76ee..14d7f15a7c26b9cef87a229379ceecc8e74fbe66 100644 (file)
@@ -211,7 +211,7 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
        return -ENOENT;
 }
 
-static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
 {
        struct imx_rproc *priv = rproc->priv;
        void *va = NULL;
index aaac31134e3952441dbc7a8260768f6f00540d22..0565f6ee41fec06e821b6baa1ffa4666579263a3 100644 (file)
@@ -1,22 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * TI Keystone DSP remoteproc driver
  *
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
- *
- * 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.
- *
- * 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.
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
  */
 
 #include <linux/module.h>
+#include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/spinlock.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/workqueue.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <linux/remoteproc.h>
+#include <linux/miscdevice.h>
+#include <linux/uio_driver.h>
 #include <linux/reset.h>
 
+#include <uapi/linux/keystone_remoteproc.h>
+
 #include "remoteproc_internal.h"
 
+#define DRIVER_UIO_VERSION                     "0.1"
+
+#define KEYSTONE_RPROC_MAX_RSC_TABLE           SZ_1K
 #define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK      (SZ_16M - 1)
 
+/*
+ * XXX: evaluate if this param needs to be enhanced so that the switch between
+ * userspace and remoteproc core loaders can be controlled per device.
+ */
+static bool use_rproc_core_loader;
+module_param(use_rproc_core_loader, bool, 0444);
+
 /**
  * struct keystone_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
+ * @kobj: kobject for the sysfs directory file
  */
 struct keystone_rproc_mem {
        void __iomem *cpu_addr;
        phys_addr_t bus_addr;
        u32 dev_addr;
        size_t size;
+       struct kobject kobj;
 };
 
 /**
@@ -59,6 +69,18 @@ struct keystone_rproc_mem {
  * @irq_fault: irq entry for exception
  * @kick_gpio: gpio used for virtio kicks
  * @workqueue: workqueue for processing virtio interrupts
+ * @misc: misc device structure used to expose fops to user-space
+ * @uio: uio device information
+ * @mlock: lock to protect resources in fops
+ * @lock: lock to protect shared resources within UIO interrupt handlers
+ * @flags: flags to keep track of UIO interrupt occurrence
+ * @rsc_table: resource table pointer copied from userspace
+ * @rsc_table_size: size of resource table
+ * @loaded_rsc_table: kernel pointer of loaded resource table
+ * @boot_addr: remote processor boot address used with userspace loader
+ * @open_count: fops open reference counter
+ * @use_userspace_loader: flag to denote if driver is configured for userspace
+ *                       loader
  */
 struct keystone_rproc {
        struct device *dev;
@@ -72,8 +94,283 @@ struct keystone_rproc {
        int irq_fault;
        int kick_gpio;
        struct work_struct workqueue;
+       struct miscdevice misc;
+       struct uio_info uio;
+       struct mutex mlock; /* fops lock */
+       spinlock_t lock; /* uio handler lock */
+       unsigned long flags;
+       struct resource_table *rsc_table;
+       int rsc_table_size;
+       void *loaded_rsc_table;
+       u32 boot_addr;
+       int open_count;
+       unsigned int use_userspace_loader : 1;
+};
+
+struct mem_sysfs_entry {
+       struct attribute attr;
+       ssize_t (*show)(struct keystone_rproc_mem *mem, char *buf);
+       ssize_t (*store)(struct keystone_rproc_mem *mem, const char *buf,
+                        size_t len);
+};
+
+static ssize_t mem_addr_show(struct keystone_rproc_mem *mem, char *buf)
+{
+       return sprintf(buf, "%pa\n", &mem->bus_addr);
+}
+
+static ssize_t mem_size_show(struct keystone_rproc_mem *mem, char *buf)
+{
+       return sprintf(buf, "0x%016zx\n", mem->size);
+}
+
+static struct mem_sysfs_entry addr_attribute =
+       __ATTR(addr, 0444, mem_addr_show, NULL);
+static struct mem_sysfs_entry size_attribute =
+       __ATTR(size, 0444, mem_size_show, NULL);
+
+static struct attribute *attrs[] = {
+       &addr_attribute.attr,
+       &size_attribute.attr,
+       NULL,   /* sentinel */
 };
 
+#define to_dsp_mem(m) container_of(m, struct keystone_rproc_mem, kobj)
+
+static ssize_t mem_type_show(struct kobject *kobj, struct attribute *attr,
+                            char *buf)
+{
+       struct keystone_rproc_mem *mem = to_dsp_mem(kobj);
+       struct mem_sysfs_entry *entry;
+
+       entry = container_of(attr, struct mem_sysfs_entry, attr);
+       if (!entry->show)
+               return -EIO;
+
+       return entry->show(mem, buf);
+}
+
+static const struct sysfs_ops mem_sysfs_ops = {
+       .show = mem_type_show,
+};
+
+static struct kobj_type mem_attr_type = {
+       .sysfs_ops      = &mem_sysfs_ops,
+       .default_attrs  = attrs,
+};
+
+static int keystone_rproc_mem_add_attrs(struct keystone_rproc *ksproc)
+{
+       int i, ret;
+       struct keystone_rproc_mem *mem;
+       struct kobject *kobj_parent = &ksproc->misc.this_device->kobj;
+
+       for (i = 0; i < ksproc->num_mems; i++) {
+               mem = &ksproc->mem[i];
+               kobject_init(&mem->kobj, &mem_attr_type);
+               ret = kobject_add(&mem->kobj, kobj_parent, "memory%d", i);
+               if (ret)
+                       goto err_kobj;
+               ret = kobject_uevent(&mem->kobj, KOBJ_ADD);
+               if (ret)
+                       goto err_kobj;
+       }
+
+       return 0;
+
+err_kobj:
+       for (; i >= 0; i--) {
+               mem = &ksproc->mem[i];
+               kobject_put(&mem->kobj);
+       }
+       return ret;
+}
+
+static void keystone_rproc_mem_del_attrs(struct keystone_rproc *ksproc)
+{
+       int i;
+       struct keystone_rproc_mem *mem;
+
+       for (i = 0; i < ksproc->num_mems; i++) {
+               mem = &ksproc->mem[i];
+               kobject_put(&mem->kobj);
+       }
+}
+
+static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len,
+                                    u32 flags);
+
+/* uio handler dealing with userspace controlled exception interrupt */
+static irqreturn_t keystone_rproc_uio_handler(int irq, struct uio_info *uio)
+{
+       struct keystone_rproc *ksproc = uio->priv;
+
+       spin_lock(&ksproc->lock);
+       if (!__test_and_set_bit(0, &ksproc->flags))
+               disable_irq_nosync(irq);
+       spin_unlock(&ksproc->lock);
+
+       return IRQ_HANDLED;
+}
+
+/* uio driver interrupt control dealing with exception interrupt */
+static int keystone_rproc_uio_irqcontrol(struct uio_info *uio, s32 irq_on)
+{
+       struct keystone_rproc *ksproc = uio->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ksproc->lock, flags);
+       if (irq_on) {
+               if (__test_and_clear_bit(0, &ksproc->flags))
+                       enable_irq(uio->irq);
+       } else {
+               if (!__test_and_set_bit(0, &ksproc->flags))
+                       disable_irq(uio->irq);
+       }
+       spin_unlock_irqrestore(&ksproc->lock, flags);
+
+       return 0;
+}
+
+/* Reset previously set rsc table variables */
+static void keystone_rproc_reset_rsc_table(struct keystone_rproc *ksproc)
+{
+       kfree(ksproc->rsc_table);
+       ksproc->rsc_table = NULL;
+       ksproc->loaded_rsc_table = NULL;
+       ksproc->rsc_table_size = 0;
+}
+
+/*
+ * Create/delete the virtio devices in kernel once the user-space loading is
+ * complete, configure the remoteproc states appropriately, and boot or reset
+ * the remote processor. The resource table should have been published through
+ * KEYSTONE_RPROC_IOC_SET_RSC_TABLE & KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE
+ * ioctls before invoking this. The boot address is passed through the
+ * KEYSTONE_RPROC_IOC_SET_STATE ioctl when setting the KEYSTONE_RPROC_RUNNING
+ * state.
+ *
+ * NOTE:
+ * The ioctls KEYSTONE_RPROC_IOC_DSP_RESET and KEYSTONE_RPROC_IOC_DSP_BOOT
+ * are restricted to support the booting or resetting the DSP devices only
+ * for firmware images without any resource table.
+ */
+static int keystone_rproc_set_state(struct keystone_rproc *ksproc,
+                                   void __user *argp)
+{
+       struct rproc *rproc = ksproc->rproc;
+       struct keystone_rproc_set_state_params set_state_params;
+       int ret = 0;
+
+       if (copy_from_user(&set_state_params, argp, sizeof(set_state_params)))
+               return -EFAULT;
+
+       switch (set_state_params.state) {
+       case KEYSTONE_RPROC_RUNNING:
+               if (!ksproc->rsc_table || !ksproc->loaded_rsc_table)
+                       return -EINVAL;
+
+               /*
+                * store boot address for .get_boot_addr() rproc fw ops
+                * XXX: validate the boot address so it is not set to a
+                * random address
+                */
+               ksproc->boot_addr = set_state_params.boot_addr;
+
+               /*
+                * invoke rproc_boot to trigger the boot, the resource table
+                * is parsed during the process and is agnostic of the presence
+                * or absence of virtio devices
+                */
+               ret = rproc_boot(rproc);
+               break;
+
+       case KEYSTONE_RPROC_OFFLINE:
+               if (rproc->state != RPROC_RUNNING)
+                       return -EINVAL;
+
+               /* invoke rproc_shutdown to match rproc_boot */
+               rproc_shutdown(rproc);
+
+               mutex_lock(&ksproc->mlock);
+               keystone_rproc_reset_rsc_table(ksproc);
+               mutex_unlock(&ksproc->mlock);
+
+               break;
+
+       default:
+               ret = -ENOTSUPP;
+       }
+
+       return ret;
+}
+
+/* Copy the resource table from userspace into kernel */
+static int keystone_rproc_set_rsc_table(struct keystone_rproc *ksproc,
+                                       void __user *data)
+{
+       unsigned long len = 0;
+       void *rsc_table = NULL;
+
+       if (!data)
+               return -EFAULT;
+
+       if (copy_from_user(&len, data, sizeof(len)))
+               return -EFAULT;
+
+       if (len >= KEYSTONE_RPROC_MAX_RSC_TABLE)
+               return -EOVERFLOW;
+
+       data += sizeof(len);
+
+       rsc_table = kzalloc(len, GFP_KERNEL);
+       if (!rsc_table)
+               return -ENOMEM;
+
+       if (copy_from_user(rsc_table, data, len))
+               goto error_return;
+
+       mutex_lock(&ksproc->mlock);
+
+       kfree(ksproc->rsc_table);
+
+       ksproc->rsc_table = rsc_table;
+       ksproc->rsc_table_size = len;
+       ksproc->loaded_rsc_table = NULL;
+
+       mutex_unlock(&ksproc->mlock);
+
+       return 0;
+
+error_return:
+       kfree(rsc_table);
+       return -EFAULT;
+}
+
+/*
+ * Store the equivalent kernel virtual address of the loaded resource table in
+ * device memory. Userspace published the device address of the loaded resource
+ * table.
+ */
+static int keystone_rproc_set_loaded_rsc_table(struct keystone_rproc *ksproc,
+                                              unsigned int dma_addr)
+{
+       struct rproc *rproc = ksproc->rproc;
+       void *ptr;
+
+       if (!ksproc->rsc_table_size || !ksproc->rsc_table)
+               return -EINVAL;
+
+       ptr = keystone_rproc_da_to_va(rproc, dma_addr, ksproc->rsc_table_size,
+                                     RPROC_FLAGS_NONE);
+       if (!ptr)
+               return -EINVAL;
+
+       ksproc->loaded_rsc_table = ptr;
+
+       return 0;
+}
+
 /* Put the DSP processor into reset */
 static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc)
 {
@@ -81,7 +378,8 @@ static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc)
 }
 
 /* Configure the boot address and boot the DSP processor */
-static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
+static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc,
+                                  uint32_t boot_addr)
 {
        int ret;
 
@@ -103,6 +401,235 @@ static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
        return 0;
 }
 
+static long
+keystone_rproc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct miscdevice *misc = filp->private_data;
+       struct keystone_rproc *ksproc =
+               container_of(misc, struct keystone_rproc, misc);
+       void __user *argp = (void __user *)arg;
+       int ret = 0;
+
+       dev_dbg(ksproc->dev, "%s: cmd 0x%.8x (%d), arg 0x%lx\n",
+               __func__, cmd, _IOC_NR(cmd), arg);
+
+       if (_IOC_TYPE(cmd) != KEYSTONE_RPROC_IOC_MAGIC)
+               return -ENOTTY;
+
+       if (_IOC_NR(cmd) >= KEYSTONE_RPROC_IOC_MAXNR)
+               return -ENOTTY;
+
+       switch (cmd) {
+       case KEYSTONE_RPROC_IOC_SET_STATE:
+               ret = keystone_rproc_set_state(ksproc, argp);
+               break;
+
+       case KEYSTONE_RPROC_IOC_SET_RSC_TABLE:
+               ret = keystone_rproc_set_rsc_table(ksproc, argp);
+               break;
+
+       case KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE:
+               ret = keystone_rproc_set_loaded_rsc_table(ksproc, arg);
+               break;
+
+       case KEYSTONE_RPROC_IOC_DSP_RESET:
+               if (ksproc->rsc_table) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               keystone_rproc_dsp_reset(ksproc);
+               break;
+
+       case KEYSTONE_RPROC_IOC_DSP_BOOT:
+               if (ksproc->rsc_table) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               ret = keystone_rproc_dsp_boot(ksproc, arg);
+               break;
+
+       default:
+               ret = -ENOTTY;
+               break;
+       }
+
+       if (ret) {
+               dev_err(ksproc->dev, "error in ioctl call: cmd 0x%.8x (%d), ret %d\n",
+                       cmd, _IOC_NR(cmd), ret);
+       }
+
+       return ret;
+}
+
+/*
+ * Map DSP memories into userspace for supporting Userspace loading.
+ *
+ * This is a custom mmap function following semantics based on the UIO
+ * mmap implementation. The vm_pgoff passed in the vma structure is a
+ * combination of the memory region index and the actual page offset in
+ * that region. This checks if user request is in valid range before
+ * providing mmap access.
+ *
+ * XXX: Evaluate this approach, as the internal memories can be mapped in
+ * whole into userspace as they are not super-large, or switch to using
+ * direct addresses to look more like a traditional implementation.
+ */
+static int keystone_rproc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct miscdevice *misc = file->private_data;
+       struct keystone_rproc *ksproc =
+               container_of(misc, struct keystone_rproc, misc);
+       size_t size = vma->vm_end - vma->vm_start;
+       size_t req_offset;
+       u32 idx;
+
+       idx = vma->vm_pgoff & KEYSTONE_RPROC_UIO_MAP_INDEX_MASK;
+
+       if (idx >= ksproc->num_mems) {
+               dev_err(ksproc->dev, "invalid mmap region index %d\n", idx);
+               return -EINVAL;
+       }
+
+       req_offset = (vma->vm_pgoff - idx) << PAGE_SHIFT;
+       if (req_offset + size < req_offset) {
+               dev_err(ksproc->dev, "invalid request - overflow, mmap offset = 0x%zx size 0x%zx region %d\n",
+                       req_offset, size, idx);
+               return -EINVAL;
+       }
+
+       if ((req_offset + size) > ksproc->mem[idx].size) {
+               dev_err(ksproc->dev, "invalid request - out of range, mmap offset 0x%zx size 0x%zx region %d\n",
+                       req_offset, size, idx);
+               return -EINVAL;
+       }
+
+       vma->vm_page_prot =
+               phys_mem_access_prot(file,
+                                    (ksproc->mem[idx].bus_addr >> PAGE_SHIFT) +
+                                    (vma->vm_pgoff - idx), size,
+                                    vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           (ksproc->mem[idx].bus_addr >> PAGE_SHIFT) +
+                           (vma->vm_pgoff - idx), size, vma->vm_page_prot))
+               return -EAGAIN;
+
+       return 0;
+}
+
+static int keystone_rproc_open(struct inode *inode, struct file *file)
+{
+       struct miscdevice *misc = file->private_data;
+       struct keystone_rproc *ksproc =
+               container_of(misc, struct keystone_rproc, misc);
+
+       mutex_lock(&ksproc->mlock);
+       ksproc->open_count++;
+       mutex_unlock(&ksproc->mlock);
+
+       return 0;
+}
+
+static int keystone_rproc_release(struct inode *inode, struct file *filp)
+{
+       struct miscdevice *misc = filp->private_data;
+       struct keystone_rproc *ksproc =
+               container_of(misc, struct keystone_rproc, misc);
+       struct rproc *rproc = ksproc->rproc;
+
+       mutex_lock(&ksproc->mlock);
+
+       if ((WARN_ON(ksproc->open_count == 0)))
+               goto end;
+
+       if (--ksproc->open_count > 0)
+               goto end;
+
+       if (rproc->state != RPROC_OFFLINE) {
+               rproc_shutdown(rproc);
+               WARN_ON(rproc->state != RPROC_OFFLINE);
+       }
+
+       keystone_rproc_reset_rsc_table(ksproc);
+
+end:
+       mutex_unlock(&ksproc->mlock);
+       return 0;
+}
+
+/*
+ * File operations exposed through a miscdevice for supporting
+ * the userspace loader/boot mechanism.
+ */
+static const struct file_operations keystone_rproc_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = keystone_rproc_ioctl,
+       .mmap           = keystone_rproc_mmap,
+       .open           = keystone_rproc_open,
+       .release        = keystone_rproc_release,
+};
+
+/*
+ * Used only with userspace loader/boot mechanism, the parsing of the firmware
+ * is done in userspace, and a copy of the resource table is added for the
+ * kernel-level access through an ioctl. Create the remoteproc cached table
+ * using this resource table and configure the table pointer and table size
+ * accordingly to allow the remoteproc core to process the resource table for
+ * creating the vrings and traces.
+ */
+static int keystone_rproc_load_rsc_table(struct rproc *rproc,
+                                        const struct firmware *fw)
+{
+       struct keystone_rproc *ksproc = rproc->priv;
+
+       rproc->cached_table = kmemdup(ksproc->rsc_table, ksproc->rsc_table_size,
+                                     GFP_KERNEL);
+       if (!rproc->cached_table)
+               return -ENOMEM;
+
+       rproc->table_ptr = rproc->cached_table;
+       rproc->table_sz = ksproc->rsc_table_size;
+
+       return 0;
+}
+
+/*
+ * Used only with userspace loader/boot mechanism, the device address of the
+ * loaded resource table is published to the kernel-level through an ioctl
+ * at which point the equivalent kernel virtual pointer is stored in a local
+ * variable in the keystone_rproc device structure. Return this kernel pointer
+ * to the remoteproc core for runtime publishing/modification of the resource
+ * table entries.
+ *
+ * NOTE: Only loaded resource tables in the DSP internal memories is supported
+ *       at present.
+ */
+static struct resource_table *
+keystone_rproc_find_loaded_rsc_table(struct rproc *rproc,
+                                    const struct firmware *fw)
+{
+       struct keystone_rproc *ksproc = rproc->priv;
+
+       return ksproc->loaded_rsc_table;
+}
+
+/*
+ * Used only with userspace loader/boot mechanism, the boot address
+ * is published to the kernel-level through an ioctl call and is
+ * stored in a local variable in the keystone_rproc device structure.
+ * Return this address to the remoteproc core through the .get_boot_addr()
+ * remoteproc firmware ops
+ */
+static u32 keystone_rproc_get_boot_addr(struct rproc *rproc,
+                                       const struct firmware *fw)
+{
+       struct keystone_rproc *ksproc = rproc->priv;
+
+       return ksproc->boot_addr;
+}
+
 /*
  * Process the remoteproc exceptions
  *
@@ -112,7 +639,9 @@ static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
  * generation register.
  *
  * This function just invokes the rproc_report_crash to report the exception
- * to the remoteproc driver core, to trigger a recovery.
+ * to the remoteproc driver core, to trigger a recovery. This is the case
+ * only when using in-kernel remoteproc core loader/boot mechanism, and is
+ * handled through an UIO interrupt otherwise.
  */
 static irqreturn_t keystone_rproc_exception_interrupt(int irq, void *dev_id)
 {
@@ -172,7 +701,11 @@ static irqreturn_t keystone_rproc_vring_interrupt(int irq, void *dev_id)
  *
  * This function will be invoked only after the firmware for this rproc
  * was loaded, parsed successfully, and all of its resource requirements
- * were met.
+ * were met. The function skips releasing the processor from reset and
+ * registering for the exception interrupt if using the userspace controlled
+ * load/boot mechanism. The processor will be started through an ioctl when
+ * controlled from userspace, but the virtio interrupt still is handled at
+ * the kernel layer.
  */
 static int keystone_rproc_start(struct rproc *rproc)
 {
@@ -189,12 +722,15 @@ static int keystone_rproc_start(struct rproc *rproc)
                goto out;
        }
 
-       ret = request_irq(ksproc->irq_fault, keystone_rproc_exception_interrupt,
-                         0, dev_name(ksproc->dev), ksproc);
-       if (ret) {
-               dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n",
-                       ret);
-               goto free_vring_irq;
+       if (!ksproc->use_userspace_loader) {
+               ret = request_irq(ksproc->irq_fault,
+                                 keystone_rproc_exception_interrupt, 0,
+                                 dev_name(ksproc->dev), ksproc);
+               if (ret) {
+                       dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n",
+                               ret);
+                       goto free_vring_irq;
+               }
        }
 
        ret = keystone_rproc_dsp_boot(ksproc, rproc->bootaddr);
@@ -216,24 +752,29 @@ out:
  * Stop the DSP remote processor.
  *
  * This function puts the DSP processor into reset, and finishes processing
- * of any pending messages.
+ * of any pending messages. The reset procedure is completed only if using
+ * kernel-mode remoteproc loading/booting mechanism, it is handled outside
+ * if using userspace load/boot mechanism either through an ioctl, or when
+ * the handle to the device is closed without triggering a reset.
  */
 static int keystone_rproc_stop(struct rproc *rproc)
 {
        struct keystone_rproc *ksproc = rproc->priv;
 
-       keystone_rproc_dsp_reset(ksproc);
-       free_irq(ksproc->irq_fault, ksproc);
+       if (!ksproc->use_userspace_loader) {
+               keystone_rproc_dsp_reset(ksproc);
+               free_irq(ksproc->irq_fault, ksproc);
+       }
+
        free_irq(ksproc->irq_ring, ksproc);
        flush_work(&ksproc->workqueue);
-
        return 0;
 }
 
 /*
  * Kick the remote processor to notify about pending unprocessed messages.
  * The vqid usage is not used and is inconsequential, as the kick is performed
- * through a simulated GPIO (a bit in an IPC interrupt-triggering register),
+ * through a simulated GPIO (an bit in an IPC interrupt-triggering register),
  * the remote processor is expected to process both its Tx and Rx virtqueues.
  */
 static void keystone_rproc_kick(struct rproc *rproc, int vqid)
@@ -254,7 +795,8 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid)
  * can be used either by the remoteproc core for loading (when using kernel
  * remoteproc loader), or by any rpmsg bus drivers.
  */
-static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len,
+                                    u32 flags)
 {
        struct keystone_rproc *ksproc = rproc->priv;
        void __iomem *va = NULL;
@@ -371,8 +913,11 @@ static int keystone_rproc_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        struct keystone_rproc *ksproc;
+       struct miscdevice *misc;
+       struct uio_info *uio;
        struct rproc *rproc;
        int dsp_id;
+       char *uio_name = NULL;
        char *fw_name = NULL;
        char *template = "keystone-dsp%d-fw";
        int name_len = 0;
@@ -389,6 +934,13 @@ static int keystone_rproc_probe(struct platform_device *pdev)
                return dsp_id;
        }
 
+       /* construct a name for uio devices - assuming a single digit alias */
+       name_len = strlen("dsp%d");
+       uio_name = devm_kzalloc(dev, name_len, GFP_KERNEL);
+       if (!uio_name)
+               return -ENOMEM;
+       snprintf(uio_name, name_len, "dsp%d", dsp_id);
+
        /* construct a custom default fw name - subject to change in future */
        name_len = strlen(template); /* assuming a single digit alias */
        fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL);
@@ -402,9 +954,33 @@ static int keystone_rproc_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        rproc->has_iommu = false;
+
        ksproc = rproc->priv;
        ksproc->rproc = rproc;
        ksproc->dev = dev;
+       ksproc->use_userspace_loader = !use_rproc_core_loader;
+
+       /*
+        * customize the remoteproc core config flags and ELF fw ops for
+        * userspace loader/boot mechanism
+        */
+       if (ksproc->use_userspace_loader) {
+               rproc->recovery_disabled = true;
+               rproc->auto_boot = false;
+               rproc->skip_firmware_request = 1;
+               rproc->skip_load = 1;
+               rproc->deny_sysfs_ops = true;
+
+               rproc->ops->parse_fw = keystone_rproc_load_rsc_table;
+               rproc->ops->find_loaded_rsc_table =
+                                       keystone_rproc_find_loaded_rsc_table;
+               rproc->ops->get_boot_addr = keystone_rproc_get_boot_addr;
+               rproc->ops->sanity_check = NULL;
+               rproc->ops->load = NULL;
+       }
+
+       mutex_init(&ksproc->mlock);
+       spin_lock_init(&ksproc->lock);
 
        ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc);
        if (ret)
@@ -475,8 +1051,52 @@ static int keystone_rproc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, ksproc);
 
+       if (ksproc->use_userspace_loader) {
+               uio = &ksproc->uio;
+               uio->name = uio_name;
+               uio->version = DRIVER_UIO_VERSION;
+               uio->irq = ksproc->irq_fault;
+               uio->priv = ksproc;
+               uio->handler = keystone_rproc_uio_handler;
+               uio->irqcontrol = keystone_rproc_uio_irqcontrol;
+               ret = uio_register_device(dev, uio);
+               if (ret) {
+                       dev_err(dev, "failed to register uio device, status = %d\n",
+                               ret);
+                       goto del_rproc;
+               }
+               dev_dbg(dev, "registered uio device %s\n", uio->name);
+
+               misc = &ksproc->misc;
+               misc->minor = MISC_DYNAMIC_MINOR;
+               misc->name = uio->name;
+               misc->fops = &keystone_rproc_fops;
+               misc->parent = dev;
+               ret = misc_register(misc);
+               if (ret) {
+                       dev_err(dev, "failed to register misc device, status = %d\n",
+                               ret);
+                       goto unregister_uio;
+               }
+
+               ret = keystone_rproc_mem_add_attrs(ksproc);
+               if (ret) {
+                       dev_err(ksproc->dev, "error creating sysfs files (%d)\n",
+                               ret);
+                       goto unregister_misc;
+               }
+
+               dev_dbg(dev, "registered misc device %s\n", misc->name);
+       }
+
        return 0;
 
+unregister_misc:
+       misc_deregister(misc);
+unregister_uio:
+       uio_unregister_device(uio);
+del_rproc:
+       rproc_del(rproc);
 release_mem:
        of_reserved_mem_device_release(dev);
 disable_clk:
@@ -492,6 +1112,11 @@ static int keystone_rproc_remove(struct platform_device *pdev)
 {
        struct keystone_rproc *ksproc = platform_get_drvdata(pdev);
 
+       if (ksproc->use_userspace_loader) {
+               keystone_rproc_mem_del_attrs(ksproc);
+               misc_deregister(&ksproc->misc);
+               uio_unregister_device(&ksproc->uio);
+       }
        rproc_del(ksproc->rproc);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -519,8 +1144,22 @@ static struct platform_driver keystone_rproc_driver = {
        },
 };
 
-module_platform_driver(keystone_rproc_driver);
+static int __init keystone_rproc_init(void)
+{
+       keystone_rproc_driver.driver.suppress_bind_attrs =
+               !use_rproc_core_loader;
+
+       return platform_driver_register(&keystone_rproc_driver);
+}
+module_init(keystone_rproc_init);
+
+static void __exit keystone_rproc_exit(void)
+{
+       platform_driver_unregister(&keystone_rproc_driver);
+}
+module_exit(keystone_rproc_exit);
 
 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_AUTHOR("Sam Nelson <sam.nelson@ti.com>");
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("TI Keystone DSP Remoteproc driver");
index a96ce9083f7faba98f460aa8cfdde8a6f55dd0d3..3e34778c2cebe81efa8436cc63d48cfc1ed66e66 100644 (file)
@@ -1,7 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * OMAP Remote Processor driver
  *
- * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011-2019 Texas Instruments Incorporated - http://www.ti.com/
  * Copyright (C) 2011 Google, Inc.
  *
  * Ohad Ben-Cohen <ohad@wizery.com>
  * Mark Grosen <mgrosen@ti.com>
  * Suman Anna <s-anna@ti.com>
  * Hari Kanigeri <h-kanigeri2@ti.com>
- *
- * 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.
- *
- * 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/kernel.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/remoteproc.h>
 #include <linux/mailbox_client.h>
 #include <linux/omap-mailbox.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <clocksource/timer-ti-dm.h>
 
 #include <linux/platform_data/remoteproc-omap.h>
+#include <linux/platform_data/dmtimer-omap.h>
 
 #include "omap_remoteproc.h"
 #include "remoteproc_internal.h"
 
+#define OMAP_RPROC_DSP_LOCAL_MEM_OFFSET                (0x00800000)
+#define OMAP_RPROC_IPU_L2RAM_DEV_ADDR          (0x20000000)
+
+/**
+ * struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs
+ * @syscon: regmap handle for the system control configuration module
+ * @boot_reg: boot register offset within the @syscon regmap
+ * @boot_reg_shift: bit-field shift required for the boot address value in
+ *                 @boot_reg
+ */
+struct omap_rproc_boot_data {
+       struct regmap *syscon;
+       unsigned int boot_reg;
+       unsigned int boot_reg_shift;
+};
+
+/**
+ * struct omap_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 omap_rproc_mem {
+       void __iomem *cpu_addr;
+       phys_addr_t bus_addr;
+       u32 dev_addr;
+       size_t size;
+};
+
+/**
+ * struct omap_rproc_timer - data structure for a timer used by a omap rproc
+ * @odt: timer pointer
+ * @timer_ops: OMAP dmtimer ops for @odt timer
+ */
+struct omap_rproc_timer {
+       struct omap_dm_timer *odt;
+       const struct omap_dm_timer_ops *timer_ops;
+};
+
 /**
  * struct omap_rproc - omap remote processor state
  * @mbox: mailbox channel handle
  * @client: mailbox client to request the mailbox channel
+ * @boot_data: boot data structure for setting processor boot address
+ * @mem: internal memory regions data
+ * @num_mems: number of internal memory regions
+ * @num_timers: number of rproc timer(s)
+ * @timers: timer(s) info used by rproc
  * @rproc: rproc handle
  */
 struct omap_rproc {
        struct mbox_chan *mbox;
        struct mbox_client client;
+       struct omap_rproc_boot_data *boot_data;
+       struct omap_rproc_mem *mem;
+       int num_mems;
+       int num_timers;
+       struct omap_rproc_timer *timers;
        struct rproc *rproc;
 };
 
+/**
+ * struct omap_rproc_dev_data - device data for the omap remote processor
+ * @device_name: device name of the remote processor
+ * @fw_name: firmware name to use
+ */
+struct omap_rproc_dev_data {
+       const char *device_name;
+       const char *fw_name;
+};
+
+/**
+ * omap_rproc_request_timer - request a timer for a remoteproc
+ * @np: device node pointer to the desired timer
+ * @timer: handle to a struct omap_rproc_timer to return the timer handle
+ *
+ * This helper function is used primarily to request a timer associated with
+ * a remoteproc. The returned handle is stored in the .odt field of the
+ * @timer structure passed in, and is used to invoke other timer specific
+ * ops (like starting a timer either during device initialization or during
+ * a resume operation, or for stopping/freeing a timer).
+ *
+ * Returns 0 on success, otherwise an appropriate failure
+ */
+static int omap_rproc_request_timer(struct device_node *np,
+                                   struct omap_rproc_timer *timer)
+{
+       int ret = 0;
+
+       timer->odt = timer->timer_ops->request_by_node(np);
+       if (!timer->odt) {
+               pr_err("request for timer node %p failed\n", np);
+               return -EBUSY;
+       }
+
+       ret = timer->timer_ops->set_source(timer->odt, OMAP_TIMER_SRC_SYS_CLK);
+       if (ret) {
+               pr_err("error setting OMAP_TIMER_SRC_SYS_CLK as source for timer node %p\n",
+                      np);
+               timer->timer_ops->free(timer->odt);
+               return ret;
+       }
+
+       /* clean counter, remoteproc code will set the value */
+       timer->timer_ops->set_load(timer->odt, 0, 0);
+
+       return ret;
+}
+
+/**
+ * omap_rproc_start_timer - start a timer for a remoteproc
+ * @timer: handle to a OMAP rproc timer
+ *
+ * This helper function is used to start a timer associated with a remoteproc,
+ * obtained using the request_timer ops. The helper function needs to be
+ * invoked by the driver to start the timer (during device initialization)
+ * or to just resume the timer.
+ *
+ * Returns 0 on success, otherwise a failure as returned by DMTimer ops
+ */
+static inline int omap_rproc_start_timer(struct omap_rproc_timer *timer)
+{
+       return timer->timer_ops->start(timer->odt);
+}
+
+/**
+ * omap_rproc_stop_timer - stop a timer for a remoteproc
+ * @timer: handle to a OMAP rproc timer
+ *
+ * This helper function is used to disable a timer associated with a
+ * remoteproc, and needs to be called either during a device shutdown
+ * or suspend operation. The separate helper function allows the driver
+ * to just stop a timer without having to release the timer during a
+ * suspend operation.
+ *
+ * Returns 0 on success, otherwise a failure as returned by DMTimer ops
+ */
+static inline int omap_rproc_stop_timer(struct omap_rproc_timer *timer)
+{
+       return timer->timer_ops->stop(timer->odt);
+}
+
+/**
+ * omap_rproc_release_timer - release a timer for a remoteproc
+ * @timer: handle to a OMAP rproc timer
+ *
+ * This helper function is used primarily to release a timer associated
+ * with a remoteproc. The dmtimer will be available for other clients to
+ * use once released.
+ *
+ * Returns 0 on success, otherwise a failure as returned by DMTimer ops
+ */
+static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer)
+{
+       return timer->timer_ops->free(timer->odt);
+}
+
+/**
+ * omap_rproc_enable_timers - enable the timers for a remoteproc
+ * @rproc: handle of a remote processor
+ * @configure: boolean flag used to acquire and configure the timer handle
+ *
+ * This function is used primarily to enable the timers associated with
+ * a remoteproc. The configure flag is provided to allow the driver to
+ * to either acquire and start a timer (during device initialization) or
+ * to just start a timer (during a resume operation).
+ */
+static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
+{
+       int i;
+       int ret = 0;
+       struct platform_device *tpdev;
+       struct dmtimer_platform_data *tpdata;
+       const struct omap_dm_timer_ops *timer_ops;
+       struct omap_rproc *oproc = rproc->priv;
+       struct omap_rproc_timer *timers = oproc->timers;
+       struct device *dev = rproc->dev.parent;
+       struct device_node *np = NULL;
+
+       if (oproc->num_timers <= 0)
+               return 0;
+
+       if (!configure)
+               goto start_timers;
+
+       for (i = 0; i < oproc->num_timers; i++) {
+               np = of_parse_phandle(dev->of_node, "timers", i);
+               if (!np) {
+                       ret = -ENXIO;
+                       dev_err(dev, "device node lookup for timer at index %d failed: %d\n",
+                               i, ret);
+                       goto free_timers;
+               }
+
+               tpdev = of_find_device_by_node(np);
+               if (!tpdev) {
+                       ret = -ENODEV;
+                       dev_err(dev, "could not get timer platform device\n");
+                       goto put_node;
+               }
+
+               tpdata = dev_get_platdata(&tpdev->dev);
+               put_device(&tpdev->dev);
+               if (!tpdata) {
+                       ret = -EINVAL;
+                       dev_err(dev, "dmtimer pdata structure NULL\n");
+                       goto put_node;
+               }
+
+               timer_ops = tpdata->timer_ops;
+               if (!timer_ops || !timer_ops->request_by_node ||
+                   !timer_ops->set_source || !timer_ops->set_load ||
+                   !timer_ops->free || !timer_ops->start ||
+                   !timer_ops->stop) {
+                       ret = -EINVAL;
+                       dev_err(dev, "device does not have required timer ops\n");
+                       goto put_node;
+               }
+
+               timers[i].timer_ops = timer_ops;
+               ret = omap_rproc_request_timer(np, &timers[i]);
+               if (ret) {
+                       dev_err(dev, "request for timer %p failed: %d\n", np,
+                               ret);
+                       goto put_node;
+               }
+               of_node_put(np);
+       }
+
+start_timers:
+       for (i = 0; i < oproc->num_timers; i++)
+               omap_rproc_start_timer(&timers[i]);
+       return 0;
+
+put_node:
+       of_node_put(np);
+free_timers:
+       while (i--) {
+               omap_rproc_release_timer(&timers[i]);
+               timers[i].odt = NULL;
+               timers[i].timer_ops = NULL;
+       }
+
+       return ret;
+}
+
+/**
+ * omap_rproc_disable_timers - disable the timers for a remoteproc
+ * @rproc: handle of a remote processor
+ * @configure: boolean flag used to release the timer handle
+ *
+ * This function is used primarily to disable the timers associated with
+ * a remoteproc. The configure flag is provided to allow the driver to
+ * to either stop and release a timer (during device shutdown) or to just
+ * stop a timer (during a suspend operation).
+ */
+static int omap_rproc_disable_timers(struct rproc *rproc, bool configure)
+{
+       int i;
+       struct omap_rproc *oproc = rproc->priv;
+       struct omap_rproc_timer *timers = oproc->timers;
+
+       if (oproc->num_timers <= 0)
+               return 0;
+
+       for (i = 0; i < oproc->num_timers; i++) {
+               omap_rproc_stop_timer(&timers[i]);
+               if (configure) {
+                       omap_rproc_release_timer(&timers[i]);
+                       timers[i].odt = NULL;
+                       timers[i].timer_ops = NULL;
+               }
+       }
+
+       return 0;
+}
+
 /**
  * omap_rproc_mbox_callback() - inbound mailbox message handler
  * @client: mailbox client pointer used for requesting the mailbox channel
@@ -80,6 +345,12 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data)
                dev_info(dev, "received echo reply from %s\n", name);
                break;
        default:
+               if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG)
+                       return;
+               if (msg > oproc->rproc->max_notifyid) {
+                       dev_dbg(dev, "dropping unknown message 0x%x", msg);
+                       return;
+               }
                /* msg contains the index of the triggered vring */
                if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
                        dev_dbg(dev, "no message was found in vqid %d\n", msg);
@@ -100,6 +371,35 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
                        ret);
 }
 
+/**
+ * omap_rproc_write_dsp_boot_addr - set boot address for a DSP remote processor
+ * @rproc: handle of a remote processor
+ *
+ * Set boot address for a supported DSP remote processor.
+ */
+static int omap_rproc_write_dsp_boot_addr(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev.parent;
+       struct omap_rproc *oproc = rproc->priv;
+       struct omap_rproc_boot_data *bdata = oproc->boot_data;
+       u32 offset = bdata->boot_reg;
+       unsigned int value = rproc->bootaddr;
+       unsigned int mask = ~(SZ_1K - 1);
+
+       if (value & (SZ_1K - 1)) {
+               dev_err(dev, "invalid boot address 0x%x, must be aligned on a 1KB boundary\n",
+                       value);
+               return -EINVAL;
+       }
+
+       value >>= bdata->boot_reg_shift;
+       mask >>= bdata->boot_reg_shift;
+
+       regmap_update_bits(bdata->syscon, offset, mask, value);
+
+       return 0;
+}
+
 /*
  * Power up the remote processor.
  *
@@ -116,8 +416,11 @@ static int omap_rproc_start(struct rproc *rproc)
        int ret;
        struct mbox_client *client = &oproc->client;
 
-       if (pdata->set_bootaddr)
-               pdata->set_bootaddr(rproc->bootaddr);
+       if (oproc->boot_data) {
+               ret = omap_rproc_write_dsp_boot_addr(rproc);
+               if (ret)
+                       return ret;
+       }
 
        client->dev = dev;
        client->tx_done = NULL;
@@ -125,7 +428,7 @@ static int omap_rproc_start(struct rproc *rproc)
        client->tx_block = false;
        client->knows_txdone = false;
 
-       oproc->mbox = omap_mbox_request_channel(client, pdata->mbox_name);
+       oproc->mbox = mbox_request_channel(client, 0);
        if (IS_ERR(oproc->mbox)) {
                ret = -EBUSY;
                dev_err(dev, "mbox_request_channel failed: %ld\n",
@@ -146,14 +449,22 @@ static int omap_rproc_start(struct rproc *rproc)
                goto put_mbox;
        }
 
+       ret = omap_rproc_enable_timers(rproc, true);
+       if (ret) {
+               dev_err(dev, "omap_rproc_enable_timers failed: %d\n", ret);
+               goto put_mbox;
+       }
+
        ret = pdata->device_enable(pdev);
        if (ret) {
                dev_err(dev, "omap_device_enable failed: %d\n", ret);
-               goto put_mbox;
+               goto reset_timers;
        }
 
        return 0;
 
+reset_timers:
+       omap_rproc_disable_timers(rproc, true);
 put_mbox:
        mbox_free_channel(oproc->mbox);
        return ret;
@@ -172,32 +483,301 @@ static int omap_rproc_stop(struct rproc *rproc)
        if (ret)
                return ret;
 
+       ret = omap_rproc_disable_timers(rproc, true);
+       if (ret)
+               return ret;
+
        mbox_free_channel(oproc->mbox);
 
        return 0;
 }
 
+/*
+ * Internal Memory translation helper
+ *
+ * Custom function implementing the rproc .da_to_va ops to provide address
+ * translation (device address to kernel virtual address) for internal RAMs
+ * present in a DSP or IPU device). The translated addresses can be used
+ * either by the remoteproc core for loading, or by any rpmsg bus drivers.
+ */
+static void *omap_rproc_da_to_va(struct rproc *rproc, u64 da, int len,
+                                u32 flags)
+{
+       struct omap_rproc *oproc = rproc->priv;
+       void *va = NULL;
+       int i;
+       u32 offset;
+
+       if (len <= 0)
+               return NULL;
+
+       if (!oproc->num_mems)
+               return NULL;
+
+       for (i = 0; i < oproc->num_mems; i++) {
+               if (da >= oproc->mem[i].dev_addr && da + len <=
+                   oproc->mem[i].dev_addr +  oproc->mem[i].size) {
+                       offset = da -  oproc->mem[i].dev_addr;
+                       /* __force to make sparse happy with type conversion */
+                       va = (__force void *)(oproc->mem[i].cpu_addr + offset);
+                       break;
+               }
+       }
+
+       return va;
+}
+
 static const struct rproc_ops omap_rproc_ops = {
        .start          = omap_rproc_start,
        .stop           = omap_rproc_stop,
        .kick           = omap_rproc_kick,
+       .da_to_va       = omap_rproc_da_to_va,
+};
+
+static const struct omap_rproc_dev_data omap4_dsp_dev_data = {
+       .device_name    = "dsp",
+       .fw_name        = "omap4-dsp-fw.xe64T",
+};
+
+static const struct omap_rproc_dev_data omap4_ipu_dev_data = {
+       .device_name    = "ipu",
+       .fw_name        = "omap4-ipu-fw.xem3",
+};
+
+static const struct omap_rproc_dev_data omap5_dsp_dev_data = {
+       .device_name    = "dsp",
+       .fw_name        = "omap5-dsp-fw.xe64T",
+};
+
+static const struct omap_rproc_dev_data omap5_ipu_dev_data = {
+       .device_name    = "ipu",
+       .fw_name        = "omap5-ipu-fw.xem4",
+};
+
+static const struct omap_rproc_dev_data dra7_rproc_dev_data[] = {
+       {
+               .device_name    = "40800000.dsp",
+               .fw_name        = "dra7-dsp1-fw.xe66",
+       },
+       {
+               .device_name    = "41000000.dsp",
+               .fw_name        = "dra7-dsp2-fw.xe66",
+       },
+       {
+               .device_name    = "55020000.ipu",
+               .fw_name        = "dra7-ipu2-fw.xem4",
+       },
+       {
+               .device_name    = "58820000.ipu",
+               .fw_name        = "dra7-ipu1-fw.xem4",
+       },
+       {
+               /* sentinel */
+       },
 };
 
+static const struct of_device_id omap_rproc_of_match[] = {
+       {
+               .compatible     = "ti,omap4-dsp",
+               .data           = &omap4_dsp_dev_data,
+       },
+       {
+               .compatible     = "ti,omap4-ipu",
+               .data           = &omap4_ipu_dev_data,
+       },
+       {
+               .compatible     = "ti,omap5-dsp",
+               .data           = &omap5_dsp_dev_data,
+       },
+       {
+               .compatible     = "ti,omap5-ipu",
+               .data           = &omap5_ipu_dev_data,
+       },
+       {
+               .compatible     = "ti,dra7-dsp",
+               .data           = dra7_rproc_dev_data,
+       },
+       {
+               .compatible     = "ti,dra7-ipu",
+               .data           = dra7_rproc_dev_data,
+       },
+       {
+               /* end */
+       },
+};
+MODULE_DEVICE_TABLE(of, omap_rproc_of_match);
+
+static const char *omap_rproc_get_firmware(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       const struct omap_rproc_dev_data *data;
+
+       data = of_device_get_match_data(&pdev->dev);
+       if (!data)
+               return ERR_PTR(-ENODEV);
+
+       if (!of_device_is_compatible(np, "ti,dra7-dsp") &&
+           !of_device_is_compatible(np, "ti,dra7-ipu"))
+               return data->fw_name;
+
+       for (; data && data->device_name; data++) {
+               if (!strcmp(dev_name(&pdev->dev), data->device_name))
+                       return data->fw_name;
+       }
+
+       return ERR_PTR(-ENOENT);
+}
+
+static int omap_rproc_get_boot_data(struct platform_device *pdev,
+                                   struct rproc *rproc)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct omap_rproc *oproc = rproc->priv;
+       int ret;
+
+       if (!of_device_is_compatible(np, "ti,omap4-dsp") &&
+           !of_device_is_compatible(np, "ti,omap5-dsp") &&
+           !of_device_is_compatible(np, "ti,dra7-dsp"))
+               return 0;
+
+       oproc->boot_data = devm_kzalloc(&pdev->dev, sizeof(*oproc->boot_data),
+                                       GFP_KERNEL);
+       if (!oproc->boot_data)
+               return -ENOMEM;
+
+       if (!of_property_read_bool(np, "syscon-bootreg")) {
+               dev_err(&pdev->dev, "syscon-bootreg property is missing\n");
+               return -EINVAL;
+       }
+
+       oproc->boot_data->syscon =
+                       syscon_regmap_lookup_by_phandle(np, "syscon-bootreg");
+       if (IS_ERR(oproc->boot_data->syscon)) {
+               ret = PTR_ERR(oproc->boot_data->syscon);
+               return ret;
+       }
+
+       if (of_property_read_u32_index(np, "syscon-bootreg", 1,
+                                      &oproc->boot_data->boot_reg)) {
+               dev_err(&pdev->dev, "couldn't get the boot register\n");
+               return -EINVAL;
+       }
+
+       if (of_device_is_compatible(np, "ti,dra7-dsp"))
+               oproc->boot_data->boot_reg_shift = 10;
+
+       return 0;
+}
+
+static int omap_rproc_of_get_internal_memories(struct platform_device *pdev,
+                                              struct rproc *rproc)
+{
+       static const char * const ipu_mem_names[] = {"l2ram"};
+       static const char * const dra7_dsp_mem_names[] = {"l2ram", "l1pram",
+                                                               "l1dram"};
+       struct device_node *np = pdev->dev.of_node;
+       struct omap_rproc *oproc = rproc->priv;
+       struct device *dev = &pdev->dev;
+       const char * const *mem_names;
+       struct resource *res;
+       int num_mems;
+       const __be32 *addrp;
+       u32 l4_offset = 0;
+       u64 size;
+       int i;
+
+       /* OMAP4 and OMAP5 DSPs do not have support for flat SRAM */
+       if (of_device_is_compatible(np, "ti,omap4-dsp") ||
+           of_device_is_compatible(np, "ti,omap5-dsp"))
+               return 0;
+
+       /* DRA7 DSPs have two additional SRAMs at L1 level */
+       if (of_device_is_compatible(np, "ti,dra7-dsp")) {
+               mem_names = dra7_dsp_mem_names;
+               num_mems = ARRAY_SIZE(dra7_dsp_mem_names);
+       } else {
+               mem_names = ipu_mem_names;
+               num_mems = ARRAY_SIZE(ipu_mem_names);
+       }
+
+       oproc->mem = devm_kcalloc(dev, num_mems, sizeof(*oproc->mem),
+                                 GFP_KERNEL);
+       if (!oproc->mem)
+               return -ENOMEM;
+
+       for (i = 0; i < num_mems; i++) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  mem_names[i]);
+               oproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
+               if (IS_ERR(oproc->mem[i].cpu_addr)) {
+                       dev_err(dev, "failed to parse and map %s memory\n",
+                               mem_names[i]);
+                       return PTR_ERR(oproc->mem[i].cpu_addr);
+               }
+               oproc->mem[i].bus_addr = res->start;
+
+               /*
+                * The DSPs have the internal memories starting at a fixed
+                * offset of 0x800000 from address 0, and this corresponds to
+                * L2RAM. The L3 address view has the L2RAM bus address as the
+                * starting address for the IP, so the L2RAM memory region needs
+                * to be processed first, and the device addresses for each
+                * memory region can be computed using the relative offset
+                * from this base address.
+                */
+               if (of_device_is_compatible(np, "ti,dra7-dsp") &&
+                   !strcmp(mem_names[i], "l2ram")) {
+                       addrp = of_get_address(dev->of_node, i, &size, NULL);
+                       l4_offset = be32_to_cpu(*addrp);
+               }
+               oproc->mem[i].dev_addr =
+                       of_device_is_compatible(np, "ti,dra7-dsp") ?
+                               res->start - l4_offset +
+                               OMAP_RPROC_DSP_LOCAL_MEM_OFFSET :
+                               OMAP_RPROC_IPU_L2RAM_DEV_ADDR;
+               oproc->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], &oproc->mem[i].bus_addr,
+                       oproc->mem[i].size, oproc->mem[i].cpu_addr,
+                       oproc->mem[i].dev_addr);
+       }
+       oproc->num_mems = num_mems;
+
+       return 0;
+}
+
 static int omap_rproc_probe(struct platform_device *pdev)
 {
-       struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+       struct omap_rproc_pdata *pdata = dev_get_platdata(&pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
        struct omap_rproc *oproc;
        struct rproc *rproc;
+       const char *firmware;
        int ret;
 
+       if (!np) {
+               dev_err(&pdev->dev, "only DT-based devices are supported\n");
+               return -ENODEV;
+       }
+
+       if (!pdata || !pdata->device_enable || !pdata->device_shutdown) {
+               dev_err(&pdev->dev, "platform data is either missing or incomplete\n");
+               return -ENODEV;
+       }
+
+       firmware = omap_rproc_get_firmware(pdev);
+       if (IS_ERR(firmware))
+               return PTR_ERR(firmware);
+
        ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
        if (ret) {
                dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret);
                return ret;
        }
 
-       rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops,
-                           pdata->firmware, sizeof(*oproc));
+       rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops,
+                           firmware, sizeof(*oproc));
        if (!rproc)
                return -ENOMEM;
 
@@ -206,14 +786,57 @@ static int omap_rproc_probe(struct platform_device *pdev)
        /* All existing OMAP IPU and DSP processors have an MMU */
        rproc->has_iommu = true;
 
+       ret = omap_rproc_of_get_internal_memories(pdev, rproc);
+       if (ret)
+               goto free_rproc;
+
+       ret = omap_rproc_get_boot_data(pdev, rproc);
+       if (ret)
+               goto free_rproc;
+
+       /*
+        * Timer nodes are directly used in client nodes as phandles, so
+        * retrieve the count using appropriate size
+        */
+       oproc->num_timers = of_property_count_elems_of_size(np, "timers",
+                                                           sizeof(phandle));
+       if (oproc->num_timers <= 0) {
+               dev_dbg(&pdev->dev, "device does not have timers, status = %d\n",
+                       oproc->num_timers);
+               oproc->num_timers = 0;
+       }
+
+       if (oproc->num_timers) {
+               oproc->timers = devm_kzalloc(&pdev->dev, sizeof(*oproc->timers)
+                                            * oproc->num_timers, GFP_KERNEL);
+               if (!oproc->timers) {
+                       ret = -ENOMEM;
+                       goto free_rproc;
+               }
+
+               dev_dbg(&pdev->dev, "device has %d tick timers\n",
+                       oproc->num_timers);
+       }
+
+       ret = of_reserved_mem_device_init(&pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "device does not have specific CMA pool\n");
+               goto free_rproc;
+       }
+
        platform_set_drvdata(pdev, rproc);
 
        ret = rproc_add(rproc);
        if (ret)
-               goto free_rproc;
+               goto release_mem;
+
+       if (rproc_get_id(rproc) < 0)
+               dev_warn(&pdev->dev, "device does not have an alias id\n");
 
        return 0;
 
+release_mem:
+       of_reserved_mem_device_release(&pdev->dev);
 free_rproc:
        rproc_free(rproc);
        return ret;
@@ -225,6 +848,7 @@ static int omap_rproc_remove(struct platform_device *pdev)
 
        rproc_del(rproc);
        rproc_free(rproc);
+       of_reserved_mem_device_release(&pdev->dev);
 
        return 0;
 }
@@ -234,6 +858,7 @@ static struct platform_driver omap_rproc_driver = {
        .remove = omap_rproc_remove,
        .driver = {
                .name = "omap-rproc",
+               .of_match_table = omap_rproc_of_match,
        },
 };
 
index f6d2036d383d671a14167160a721bde820e910d6..18f5226176834608e98eb00d5aa8e518120dfde6 100644 (file)
@@ -1,35 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
 /*
  * Remote processor messaging
  *
  * Copyright (C) 2011 Texas Instruments, Inc.
  * Copyright (C) 2011 Google, Inc.
  * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in
- *   the documentation and/or other materials provided with the
- *   distribution.
- * * Neither the name Texas Instruments nor the names of its
- *   contributors may be used to endorse or promote products derived
- *   from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef _OMAP_RPMSG_H
  *
  * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the
  * recovery mechanism (to some extent).
+ *
+ * Introduce new message definitions if any here.
+ *
+ * @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core
+ * This should be the last definition.
+ *
  */
 enum omap_rp_mbox_messages {
        RP_MBOX_READY           = 0xFFFFFF00,
@@ -64,6 +45,7 @@ enum omap_rp_mbox_messages {
        RP_MBOX_ECHO_REQUEST    = 0xFFFFFF03,
        RP_MBOX_ECHO_REPLY      = 0xFFFFFF04,
        RP_MBOX_ABORT_REQUEST   = 0xFFFFFF05,
+       RP_MBOX_END_MSG         = 0xFFFFFF06,
 };
 
 #endif /* _OMAP_RPMSG_H */
diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c
new file mode 100644 (file)
index 0000000..8d1bb51
--- /dev/null
@@ -0,0 +1,1138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PRU-ICSS remoteproc driver for various TI SoCs
+ *
+ * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *     Suman Anna <s-anna@ti.com>
+ *     Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pruss.h>
+#include <linux/pruss_driver.h>
+#include <linux/remoteproc.h>
+
+#include "remoteproc_internal.h"
+#include "pru_rproc.h"
+
+/* PRU_ICSS_PRU_CTRL registers */
+#define PRU_CTRL_CTRL          0x0000
+#define PRU_CTRL_STS           0x0004
+#define PRU_CTRL_WAKEUP_EN     0x0008
+#define PRU_CTRL_CYCLE         0x000C
+#define PRU_CTRL_STALL         0x0010
+#define PRU_CTRL_CTBIR0                0x0020
+#define PRU_CTRL_CTBIR1                0x0024
+#define PRU_CTRL_CTPPR0                0x0028
+#define PRU_CTRL_CTPPR1                0x002C
+
+/* CTRL register bit-fields */
+#define CTRL_CTRL_SOFT_RST_N   BIT(0)
+#define CTRL_CTRL_EN           BIT(1)
+#define CTRL_CTRL_SLEEPING     BIT(2)
+#define CTRL_CTRL_CTR_EN       BIT(3)
+#define CTRL_CTRL_SINGLE_STEP  BIT(8)
+#define CTRL_CTRL_RUNSTATE     BIT(15)
+
+/* PRU_ICSS_PRU_DEBUG registers */
+#define PRU_DEBUG_GPREG(x)     (0x0000 + (x) * 4)
+#define PRU_DEBUG_CT_REG(x)    (0x0080 + (x) * 4)
+
+/* PRU Core IRAM address masks */
+#define PRU0_IRAM_ADDR_MASK    0x34000
+#define PRU1_IRAM_ADDR_MASK    0x38000
+
+/**
+ * enum pru_iomem - PRU core memory/register range identifiers
+ */
+enum pru_iomem {
+       PRU_IOMEM_IRAM = 0,
+       PRU_IOMEM_CTRL,
+       PRU_IOMEM_DEBUG,
+       PRU_IOMEM_MAX,
+};
+
+/**
+ * struct pru_rproc - PRU remoteproc structure
+ * @id: id of the PRU core within the PRUSS
+ * @pruss: back-reference to parent PRUSS structure
+ * @rproc: remoteproc pointer for this PRU core
+ * @client_np: client device node
+ * @mbox: mailbox channel handle used for vring signalling with MPU
+ * @client: mailbox client to request the mailbox channel
+ * @irq_ring: IRQ number to use for processing vring buffers
+ * @irq_kick: IRQ number to use to perform virtio kick
+ * @mem_regions: data for each of the PRU memory regions
+ * @intc_config: PRU INTC configuration data
+ * @rmw_lock: lock for read, modify, write operations on registers
+ * @iram_da: device address of Instruction RAM for this PRU
+ * @pdram_da: device address of primary Data RAM for this PRU
+ * @sdram_da: device address of secondary Data RAM for this PRU
+ * @shrdram_da: device address of shared Data RAM
+ * @fw_name: name of firmware image used during loading
+ * @dt_irqs: number of irqs configured from DT
+ * @gpmux_save: saved value for gpmux config
+ * @lock: mutex to protect client usage
+ * @dbg_single_step: debug state variable to set PRU into single step mode
+ * @dbg_continuous: debug state variable to restore PRU execution mode
+ * @fw_has_intc_rsc: boolean flag to indicate INTC config through firmware
+ */
+struct pru_rproc {
+       int id;
+       struct pruss *pruss;
+       struct rproc *rproc;
+       struct device_node *client_np;
+       struct mbox_chan *mbox;
+       struct mbox_client client;
+       int irq_vring;
+       int irq_kick;
+       struct pruss_mem_region mem_regions[PRU_IOMEM_MAX];
+       struct pruss_intc_config intc_config;
+       spinlock_t rmw_lock; /* register access lock */
+       u32 iram_da;
+       u32 pdram_da;
+       u32 sdram_da;
+       u32 shrdram_da;
+       const char *fw_name;
+       int dt_irqs;
+       u8 gpmux_save;
+       struct mutex lock; /* client access lock */
+       u32 dbg_single_step;
+       u32 dbg_continuous;
+       unsigned int fw_has_intc_rsc : 1;
+};
+
+static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, int len);
+
+static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg)
+{
+       return readl_relaxed(pru->mem_regions[PRU_IOMEM_CTRL].va + reg);
+}
+
+static inline
+void pru_control_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val)
+{
+       writel_relaxed(val, pru->mem_regions[PRU_IOMEM_CTRL].va + reg);
+}
+
+static inline
+void pru_control_set_reg(struct pru_rproc *pru, unsigned int reg,
+                        u32 mask, u32 set)
+{
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pru->rmw_lock, flags);
+
+       val = pru_control_read_reg(pru, reg);
+       val &= ~mask;
+       val |= (set & mask);
+       pru_control_write_reg(pru, reg, val);
+
+       spin_unlock_irqrestore(&pru->rmw_lock, flags);
+}
+
+/**
+ * pru_rproc_set_firmware() - set firmware for a pru core
+ * @rproc: the rproc instance of the PRU
+ * @fw_name: the new firmware name, or NULL if default is desired
+ */
+static int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name)
+{
+       struct pru_rproc *pru = rproc->priv;
+
+       if (!fw_name)
+               fw_name = pru->fw_name;
+
+       return rproc_set_firmware(rproc, fw_name);
+}
+
+static int pru_rproc_intc_dt_config(struct pru_rproc *pru, int index)
+{
+       struct device *dev = &pru->rproc->dev;
+       struct device_node *np = pru->client_np;
+       struct property *prop;
+       const char *prop_name = "ti,pru-interrupt-map";
+       int ret = 0, i;
+       int dt_irqs;
+       u32 *arr;
+       bool has_irqs = false;
+
+       prop = of_find_property(np, prop_name, NULL);
+       if (!prop)
+               return 0;
+
+       dt_irqs = of_property_count_u32_elems(np, prop_name);
+       if (dt_irqs <= 0 || dt_irqs % 4) {
+               dev_err(dev, "bad interrupt map data %d, expected multiple of 4\n",
+                       dt_irqs);
+               return -EINVAL;
+       }
+
+       arr = kmalloc_array(dt_irqs, sizeof(u32), GFP_KERNEL);
+       if (!arr)
+               return -ENOMEM;
+
+       ret = of_property_read_u32_array(np, prop_name, arr, dt_irqs);
+       if (ret) {
+               dev_err(dev, "failed to read pru irq map: %d\n", ret);
+               goto out;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(pru->intc_config.sysev_to_ch); i++)
+               pru->intc_config.sysev_to_ch[i] = -1;
+
+       for (i = 0; i < ARRAY_SIZE(pru->intc_config.ch_to_host); i++)
+               pru->intc_config.ch_to_host[i] = -1;
+
+       for (i = 0; i < dt_irqs; i += 4) {
+               if (arr[i] != index)
+                       continue;
+
+               if (arr[i + 1] < 0 ||
+                   arr[i + 1] >= MAX_PRU_SYS_EVENTS) {
+                       dev_err(dev, "bad sys event %d\n", arr[i + 1]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (arr[i + 2] < 0 ||
+                   arr[i + 2] >= MAX_PRU_CHANNELS) {
+                       dev_err(dev, "bad channel %d\n", arr[i + 2]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (arr[i + 3] < 0 ||
+                   arr[i + 3] >= MAX_PRU_HOST_INT) {
+                       dev_err(dev, "bad irq %d\n", arr[i + 3]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               pru->intc_config.sysev_to_ch[arr[i + 1]] = arr[i + 2];
+               dev_dbg(dev, "sysevt-to-ch[%d] -> %d\n", arr[i + 1],
+                       arr[i + 2]);
+
+               pru->intc_config.ch_to_host[arr[i + 2]] = arr[i + 3];
+               dev_dbg(dev, "chnl-to-host[%d] -> %d\n", arr[i + 2],
+                       arr[i + 3]);
+
+               has_irqs = true;
+       }
+
+       /*
+        * The property "ti,pru-interrupt-map" is used in a consumer node, but
+        * need not necessarily have data for all referenced PRUs. Provide a
+        * fallback to get the interrupt data from firmware for PRUs ith no
+        * interrupt data.
+        */
+       if (!has_irqs) {
+               dev_dbg(dev, "no DT irqs, falling back to firmware intc rsc mode\n");
+               goto out;
+       }
+
+       pru->dt_irqs = dt_irqs;
+       ret = pruss_intc_configure(pru->pruss, &pru->intc_config);
+       if (ret) {
+               dev_err(dev, "failed to configure intc %d\n", ret);
+               pru->dt_irqs = 0;
+       }
+
+out:
+       kfree(arr);
+
+       return ret;
+}
+
+static struct rproc *__pru_rproc_get(struct device_node *np, int index)
+{
+       struct device_node *rproc_np = NULL;
+       struct platform_device *pdev;
+       struct rproc *rproc;
+
+       rproc_np = of_parse_phandle(np, "prus", index);
+       if (!rproc_np || !of_device_is_available(rproc_np))
+               return ERR_PTR(-ENODEV);
+
+       pdev = of_find_device_by_node(rproc_np);
+       of_node_put(rproc_np);
+
+       if (!pdev)
+               /* probably PRU not yet probed */
+               return ERR_PTR(-EPROBE_DEFER);
+
+       /* TODO: replace the crude string based check to make sure it is PRU */
+       if (!strstr(dev_name(&pdev->dev), "pru")) {
+               put_device(&pdev->dev);
+               return ERR_PTR(-ENODEV);
+       }
+
+       rproc = platform_get_drvdata(pdev);
+       put_device(&pdev->dev);
+       if (!rproc)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       get_device(&rproc->dev);
+
+       return rproc;
+}
+
+/**
+ * pru_rproc_get() - get the PRU rproc instance from a device node
+ * @np: the user/client device node
+ * @index: index to use for the prus property
+ *
+ * This function looks through a client device node's "prus" property at index
+ * @index and returns the rproc handle for a valid PRU remote processor if
+ * found. The function allows only one user to own the PRU rproc resource at
+ * a time. Caller must call pru_rproc_put() when done with using the rproc,
+ * not required if the function returns a failure.
+ *
+ * Returns the rproc handle on success, and an ERR_PTR on failure using one
+ * of the following error values
+ *    -ENODEV if device is not found
+ *    -EBUSY if PRU is already acquired by anyone
+ *    -EPROBE_DEFER is PRU device is not probed yet
+ */
+struct rproc *pru_rproc_get(struct device_node *np, int index)
+{
+       struct rproc *rproc;
+       struct pru_rproc *pru;
+       const char *fw_name;
+       struct device *dev;
+       int ret;
+       u32 mux;
+
+       rproc = __pru_rproc_get(np, index);
+       if (IS_ERR(rproc))
+               return rproc;
+
+       pru = rproc->priv;
+       dev = &rproc->dev;
+
+       mutex_lock(&pru->lock);
+
+       if (pru->client_np) {
+               mutex_unlock(&pru->lock);
+               put_device(&rproc->dev);
+               return ERR_PTR(-EBUSY);
+       }
+
+       pru->client_np = np;
+       rproc->deny_sysfs_ops = 1;
+
+       mutex_unlock(&pru->lock);
+
+       ret = pruss_cfg_get_gpmux(pru->pruss, pru->id, &pru->gpmux_save);
+       if (ret) {
+               dev_err(dev, "failed to get cfg gpmux: %d\n", ret);
+               goto err;
+       }
+
+       ret = of_property_read_u32_index(np, "ti,pruss-gp-mux-sel", index,
+                                        &mux);
+       if (!ret) {
+               ret = pruss_cfg_set_gpmux(pru->pruss, pru->id, mux);
+               if (ret) {
+                       dev_err(dev, "failed to set cfg gpmux: %d\n", ret);
+                       goto err;
+               }
+       }
+
+       ret = of_property_read_string_index(np, "firmware-name", index,
+                                           &fw_name);
+       if (!ret) {
+               ret = pru_rproc_set_firmware(rproc, fw_name);
+               if (ret) {
+                       dev_err(dev, "failed to set firmware: %d\n", ret);
+                       goto err;
+               }
+       }
+
+       ret = pru_rproc_intc_dt_config(pru, index);
+       if (ret)
+               goto err;
+
+       return rproc;
+
+err:
+       pru_rproc_put(rproc);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pru_rproc_get);
+
+/**
+ * pru_rproc_put() - release the PRU rproc resource
+ * @rproc: the rproc resource to release
+ *
+ * Releases the PRU rproc resource and makes it available to other
+ * users.
+ */
+void pru_rproc_put(struct rproc *rproc)
+{
+       struct pru_rproc *pru;
+
+       if (IS_ERR_OR_NULL(rproc))
+               return;
+
+       /* TODO: replace the crude string based check to make sure it is PRU */
+       if (!strstr(dev_name(rproc->dev.parent), "pru"))
+               return;
+
+       pru = rproc->priv;
+       if (!pru->client_np)
+               return;
+
+       pruss_cfg_set_gpmux(pru->pruss, pru->id, pru->gpmux_save);
+
+       if (pru->dt_irqs)
+               pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
+
+       pru_rproc_set_firmware(rproc, NULL);
+
+       mutex_lock(&pru->lock);
+       pru->client_np = NULL;
+       rproc->deny_sysfs_ops = 0;
+       mutex_unlock(&pru->lock);
+
+       put_device(&rproc->dev);
+}
+EXPORT_SYMBOL_GPL(pru_rproc_put);
+
+/**
+ * pru_rproc_get_id() - get PRU id from a previously acquired PRU remoteproc
+ * @rproc: the rproc instance of the PRU
+ *
+ * Returns the PRU id of the PRU remote processor that has been acquired through
+ * a pru_rproc_get(), or a negative value on error
+ */
+enum pruss_pru_id pru_rproc_get_id(struct rproc *rproc)
+{
+       struct pru_rproc *pru;
+
+       if (IS_ERR_OR_NULL(rproc) || !rproc->dev.parent)
+               return -EINVAL;
+
+       /* TODO: replace the crude string based check to make sure it is PRU */
+       if (!strstr(dev_name(rproc->dev.parent), "pru"))
+               return -EINVAL;
+
+       pru = rproc->priv;
+       return pru->id;
+}
+EXPORT_SYMBOL_GPL(pru_rproc_get_id);
+
+/**
+ * pru_rproc_set_ctable() - set the constant table index for the PRU
+ * @rproc: the rproc instance of the PRU
+ * @c: constant table index to set
+ * @addr: physical address to set it to
+ */
+int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr)
+{
+       struct pru_rproc *pru = rproc->priv;
+       unsigned int reg;
+       u32 mask, set;
+       u16 idx;
+       u16 idx_mask;
+
+       /* pointer is 16 bit and index is 8-bit so mask out the rest */
+       idx_mask = (c >= PRU_C28) ? 0xFFFF : 0xFF;
+
+       /* ctable uses bit 8 and upwards only */
+       idx = (addr >> 8) & idx_mask;
+
+       /* configurable ctable (i.e. C24) starts at PRU_CTRL_CTBIR0 */
+       reg = PRU_CTRL_CTBIR0 + 4 * (c >> 1);
+       mask = idx_mask << (16 * (c & 1));
+       set = idx << (16 * (c & 1));
+
+       pru_control_set_reg(pru, reg, mask, set);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pru_rproc_set_ctable);
+
+static inline u32 pru_debug_read_reg(struct pru_rproc *pru, unsigned int reg)
+{
+       return readl_relaxed(pru->mem_regions[PRU_IOMEM_DEBUG].va + reg);
+}
+
+static inline
+void pru_debug_write_reg(struct pru_rproc *pru, unsigned int reg, u32 val)
+{
+       writel_relaxed(val, pru->mem_regions[PRU_IOMEM_DEBUG].va + reg);
+}
+
+static int regs_show(struct seq_file *s, void *data)
+{
+       struct rproc *rproc = s->private;
+       struct pru_rproc *pru = rproc->priv;
+       int i, nregs = 32;
+       u32 pru_sts;
+       int pru_is_running;
+
+       seq_puts(s, "============== Control Registers ==============\n");
+       seq_printf(s, "CTRL      := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_CTRL));
+       pru_sts = pru_control_read_reg(pru, PRU_CTRL_STS);
+       seq_printf(s, "STS (PC)  := 0x%08x (0x%08x)\n", pru_sts, pru_sts << 2);
+       seq_printf(s, "WAKEUP_EN := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_WAKEUP_EN));
+       seq_printf(s, "CYCLE     := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_CYCLE));
+       seq_printf(s, "STALL     := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_STALL));
+       seq_printf(s, "CTBIR0    := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_CTBIR0));
+       seq_printf(s, "CTBIR1    := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_CTBIR1));
+       seq_printf(s, "CTPPR0    := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_CTPPR0));
+       seq_printf(s, "CTPPR1    := 0x%08x\n",
+                  pru_control_read_reg(pru, PRU_CTRL_CTPPR1));
+
+       seq_puts(s, "=============== Debug Registers ===============\n");
+       pru_is_running = pru_control_read_reg(pru, PRU_CTRL_CTRL) &
+                               CTRL_CTRL_RUNSTATE;
+       if (pru_is_running) {
+               seq_puts(s, "PRU is executing, cannot print/access debug registers.\n");
+               return 0;
+       }
+
+       for (i = 0; i < nregs; i++) {
+               seq_printf(s, "GPREG%-2d := 0x%08x\tCT_REG%-2d := 0x%08x\n",
+                          i, pru_debug_read_reg(pru, PRU_DEBUG_GPREG(i)),
+                          i, pru_debug_read_reg(pru, PRU_DEBUG_CT_REG(i)));
+       }
+
+       return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(regs);
+
+/*
+ * Control PRU single-step mode
+ *
+ * This is a debug helper function used for controlling the single-step
+ * mode of the PRU. The PRU Debug registers are not accessible when the
+ * PRU is in RUNNING state.
+ *
+ * Writing a non-zero value sets the PRU into single-step mode irrespective
+ * of its previous state. The PRU mode is saved only on the first set into
+ * a single-step mode. Writing a zero value will restore the PRU into its
+ * original mode.
+ */
+static int pru_rproc_debug_ss_set(void *data, u64 val)
+{
+       struct rproc *rproc = data;
+       struct pru_rproc *pru = rproc->priv;
+       u32 reg_val;
+
+       val = val ? 1 : 0;
+       if (!val && !pru->dbg_single_step)
+               return 0;
+
+       reg_val = pru_control_read_reg(pru, PRU_CTRL_CTRL);
+
+       if (val && !pru->dbg_single_step)
+               pru->dbg_continuous = reg_val;
+
+       if (val)
+               reg_val |= CTRL_CTRL_SINGLE_STEP | CTRL_CTRL_EN;
+       else
+               reg_val = pru->dbg_continuous;
+
+       pru->dbg_single_step = val;
+       pru_control_write_reg(pru, PRU_CTRL_CTRL, reg_val);
+
+       return 0;
+}
+
+static int pru_rproc_debug_ss_get(void *data, u64 *val)
+{
+       struct rproc *rproc = data;
+       struct pru_rproc *pru = rproc->priv;
+
+       *val = pru->dbg_single_step;
+
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(pru_rproc_debug_ss_fops, pru_rproc_debug_ss_get,
+                       pru_rproc_debug_ss_set, "%llu\n");
+
+/*
+ * Create PRU-specific debugfs entries
+ *
+ * The entries are created only if the parent remoteproc debugfs directory
+ * exists, and will be cleaned up by the remoteproc core.
+ */
+static void pru_rproc_create_debug_entries(struct rproc *rproc)
+{
+       if (!rproc->dbg_dir)
+               return;
+
+       debugfs_create_file("regs", 0400, rproc->dbg_dir,
+                           rproc, &regs_fops);
+       debugfs_create_file("single_step", 0600, rproc->dbg_dir,
+                           rproc, &pru_rproc_debug_ss_fops);
+}
+
+/**
+ * pru_rproc_mbox_callback() - inbound mailbox message handler
+ * @client: mailbox client pointer used for requesting the mailbox channel
+ * @data: mailbox payload
+ *
+ * This handler is invoked by omap's mailbox driver whenever a mailbox
+ * message is received. Usually, the mailbox payload simply contains
+ * the index of the virtqueue that is kicked by the PRU remote processor,
+ * and we let remoteproc core handle it.
+ *
+ * In addition to virtqueue indices, we might also have some out-of-band
+ * values that indicates different events. Those values are deliberately
+ * very big so they don't coincide with virtqueue indices.
+ */
+static void pru_rproc_mbox_callback(struct mbox_client *client, void *data)
+{
+       struct pru_rproc *pru = container_of(client, struct pru_rproc, client);
+       struct device *dev = &pru->rproc->dev;
+       u32 msg = (u32)data;
+
+       dev_dbg(dev, "mbox msg: 0x%x\n", msg);
+
+       /* msg contains the index of the triggered vring */
+       if (rproc_vq_interrupt(pru->rproc, msg) == IRQ_NONE)
+               dev_dbg(dev, "no message was found in vqid %d\n", msg);
+}
+
+/**
+ * pru_rproc_vring_interrupt() - interrupt handler for processing vrings
+ * @irq: irq number associated with the PRU event MPU is listening on
+ * @data: interrupt handler data, will be a PRU rproc structure
+ *
+ * This handler is used by the PRU remoteproc driver when using PRU system
+ * events for processing the virtqueues. Unlike the mailbox IP, there is
+ * no payload associated with an interrupt, so either a unique event is
+ * used for each virtqueue kick, or a both virtqueues are processed on
+ * a single event. The latter is chosen to conserve the usable PRU system
+ * events.
+ */
+static irqreturn_t pru_rproc_vring_interrupt(int irq, void *data)
+{
+       struct pru_rproc *pru = data;
+
+       dev_dbg(&pru->rproc->dev, "got vring irq\n");
+
+       /* process incoming buffers on both the Rx and Tx vrings */
+       rproc_vq_interrupt(pru->rproc, 0);
+       rproc_vq_interrupt(pru->rproc, 1);
+
+       return IRQ_HANDLED;
+}
+
+/* kick a virtqueue */
+static void pru_rproc_kick(struct rproc *rproc, int vq_id)
+{
+       struct device *dev = &rproc->dev;
+       struct pru_rproc *pru = rproc->priv;
+       int ret;
+
+       dev_dbg(dev, "kicking vqid %d on PRU%d\n", vq_id, pru->id);
+
+       if (pru->irq_kick > 0) {
+               ret = pruss_intc_trigger(pru->irq_kick);
+               if (ret < 0)
+                       dev_err(dev, "pruss_intc_trigger failed: %d\n", ret);
+       } else if (pru->mbox) {
+               /*
+                * send the index of the triggered virtqueue in the mailbox
+                * payload
+                */
+               ret = mbox_send_message(pru->mbox, (void *)vq_id);
+               if (ret < 0)
+                       dev_err(dev, "mbox_send_message failed: %d\n", ret);
+       }
+}
+
+/* start a PRU core */
+static int pru_rproc_start(struct rproc *rproc)
+{
+       struct device *dev = &rproc->dev;
+       struct pru_rproc *pru = rproc->priv;
+       u32 val;
+       int ret;
+
+       dev_dbg(dev, "starting PRU%d: entry-point = 0x%x\n",
+               pru->id, (rproc->bootaddr >> 2));
+
+       if (!list_empty(&pru->rproc->rvdevs)) {
+               if (!pru->mbox && (pru->irq_vring <= 0 || pru->irq_kick <= 0)) {
+                       dev_err(dev, "virtio vring interrupt mechanisms are not provided\n");
+                       ret = -EINVAL;
+                       goto fail;
+               }
+
+               if (!pru->mbox && pru->irq_vring > 0) {
+                       ret = request_threaded_irq(pru->irq_vring, NULL,
+                                                  pru_rproc_vring_interrupt,
+                                                  IRQF_ONESHOT, dev_name(dev),
+                                                  pru);
+                       if (ret) {
+                               dev_err(dev, "failed to enable vring interrupt, ret = %d\n",
+                                       ret);
+                               goto fail;
+                       }
+               }
+       }
+
+       val = CTRL_CTRL_EN | ((rproc->bootaddr >> 2) << 16);
+       pru_control_write_reg(pru, PRU_CTRL_CTRL, val);
+
+       return 0;
+
+fail:
+       if (!pru->dt_irqs && pru->fw_has_intc_rsc)
+               pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
+       return ret;
+}
+
+/* stop/disable a PRU core */
+static int pru_rproc_stop(struct rproc *rproc)
+{
+       struct device *dev = &rproc->dev;
+       struct pru_rproc *pru = rproc->priv;
+       u32 val;
+
+       dev_dbg(dev, "stopping PRU%d\n", pru->id);
+
+       val = pru_control_read_reg(pru, PRU_CTRL_CTRL);
+       val &= ~CTRL_CTRL_EN;
+       pru_control_write_reg(pru, PRU_CTRL_CTRL, val);
+
+       if (!list_empty(&pru->rproc->rvdevs) &&
+           !pru->mbox && pru->irq_vring > 0)
+               free_irq(pru->irq_vring, pru);
+
+       /* undo INTC config */
+       if (!pru->dt_irqs && pru->fw_has_intc_rsc)
+               pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
+
+       return 0;
+}
+
+/*
+ * parse the custom PRU interrupt map resource and configure the INTC
+ * appropriately
+ */
+static int pru_handle_vendor_intrmap(struct rproc *rproc,
+                                    struct fw_rsc_vendor *rsc)
+{
+       struct device *dev = rproc->dev.parent;
+       struct pru_rproc *pru = rproc->priv;
+       struct pruss *pruss = pru->pruss;
+       struct pruss_event_chnl *event_chnl_map;
+       struct fw_rsc_pruss_intrmap *intr_rsc =
+               (struct fw_rsc_pruss_intrmap *)rsc->data;
+       int i, ret;
+       s8 sys_evt, chnl, intr_no;
+
+       dev_dbg(dev, "version %d event_chnl_map_size %d event_chnl_map_addr 0x%x\n",
+               rsc->u.st.st_ver, intr_rsc->event_chnl_map_size,
+               intr_rsc->event_chnl_map_addr);
+
+       if (rsc->u.st.st_ver != 0) {
+               dev_err(dev, "only PRU interrupt resource version 0 supported\n");
+               return -EINVAL;
+       }
+
+       if (intr_rsc->event_chnl_map_size < 0 ||
+           intr_rsc->event_chnl_map_size >= MAX_PRU_SYS_EVENTS) {
+               dev_err(dev, "PRU interrupt resource has more events than present on hardware\n");
+               return -EINVAL;
+       }
+
+       /*
+        * XXX: The event_chnl_map_addr mapping is currently a pointer in device
+        * memory, evaluate if this needs to be directly in firmware file.
+        */
+       event_chnl_map = pru_d_da_to_va(pru, intr_rsc->event_chnl_map_addr,
+                                       intr_rsc->event_chnl_map_size *
+                                       sizeof(*event_chnl_map));
+       if (!event_chnl_map) {
+               dev_err(dev, "PRU interrupt resource has inadequate event_chnl_map configuration\n");
+               return -EINVAL;
+       }
+
+       /* init intc_config to defaults */
+       for (i = 0; i < ARRAY_SIZE(pru->intc_config.sysev_to_ch); i++)
+               pru->intc_config.sysev_to_ch[i] = -1;
+
+       for (i = 0; i < ARRAY_SIZE(pru->intc_config.ch_to_host); i++)
+               pru->intc_config.ch_to_host[i] = -1;
+
+       /* parse and fill in system event to interrupt channel mapping */
+       for (i = 0; i < intr_rsc->event_chnl_map_size; i++) {
+               sys_evt = event_chnl_map[i].event;
+               chnl = event_chnl_map[i].chnl;
+
+               if (sys_evt < 0 || sys_evt >= MAX_PRU_SYS_EVENTS) {
+                       dev_err(dev, "[%d] bad sys event %d\n", i, sys_evt);
+                       return -EINVAL;
+               }
+               if (chnl < 0 || chnl >= MAX_PRU_CHANNELS) {
+                       dev_err(dev, "[%d] bad channel value %d\n", i, chnl);
+                       return -EINVAL;
+               }
+
+               pru->intc_config.sysev_to_ch[sys_evt] = chnl;
+               dev_dbg(dev, "sysevt-to-ch[%d] -> %d\n", sys_evt, chnl);
+       }
+
+       /* parse and handle interrupt channel-to-host interrupt mapping */
+       for (i = 0; i < MAX_PRU_CHANNELS; i++) {
+               intr_no = intr_rsc->chnl_host_intr_map[i];
+               if (intr_no < 0) {
+                       dev_dbg(dev, "skip intr mapping for chnl %d\n", i);
+                       continue;
+               }
+
+               if (intr_no >= MAX_PRU_HOST_INT) {
+                       dev_err(dev, "bad intr mapping for chnl %d, intr_no %d\n",
+                               i, intr_no);
+                       return -EINVAL;
+               }
+
+               pru->intc_config.ch_to_host[i] = intr_no;
+               dev_dbg(dev, "chnl-to-host[%d] -> %d\n", i, intr_no);
+       }
+
+       pru->fw_has_intc_rsc = 1;
+
+       ret = pruss_intc_configure(pruss, &pru->intc_config);
+       if (ret)
+               dev_err(dev, "failed to configure pruss intc %d\n", ret);
+
+       return ret;
+}
+
+/* PRU-specific vendor resource handler */
+static int pru_rproc_handle_vendor_rsc(struct rproc *rproc,
+                                      struct fw_rsc_vendor *rsc)
+{
+       struct device *dev = rproc->dev.parent;
+       struct pru_rproc *pru = rproc->priv;
+       int ret = 0;
+
+       switch (rsc->u.st.st_type) {
+       case PRUSS_RSC_INTRS:
+               if (!pru->dt_irqs)
+                       ret = pru_handle_vendor_intrmap(rproc, rsc);
+               break;
+       default:
+               dev_err(dev, "%s: cannot handle unknown type %d\n", __func__,
+                       rsc->u.st.st_type);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*
+ * Convert PRU device address (data spaces only) to kernel virtual address
+ *
+ * Each PRU has access to all data memories within the PRUSS, accessible at
+ * different ranges. So, look through both its primary and secondary Data
+ * RAMs as well as any shared Data RAM to convert a PRU device address to
+ * kernel virtual address. Data RAM0 is primary Data RAM for PRU0 and Data
+ * RAM1 is primary Data RAM for PRU1.
+ */
+static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, int len)
+{
+       struct pruss_mem_region dram0, dram1, shrd_ram;
+       struct pruss *pruss = pru->pruss;
+       u32 offset;
+       void *va = NULL;
+
+       if (len <= 0)
+               return NULL;
+
+       dram0 = pruss->mem_regions[PRUSS_MEM_DRAM0];
+       dram1 = pruss->mem_regions[PRUSS_MEM_DRAM1];
+       /* PRU1 has its local RAM addresses reversed */
+       if (pru->id == 1)
+               swap(dram0, dram1);
+       shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2];
+
+       if (da >= pru->pdram_da && da + len <= pru->pdram_da + dram0.size) {
+               offset = da - pru->pdram_da;
+               va = (__force void *)(dram0.va + offset);
+       } else if (da >= pru->sdram_da &&
+                  da + len <= pru->sdram_da + dram1.size) {
+               offset = da - pru->sdram_da;
+               va = (__force void *)(dram1.va + offset);
+       } else if (da >= pru->shrdram_da &&
+                  da + len <= pru->shrdram_da + shrd_ram.size) {
+               offset = da - pru->shrdram_da;
+               va = (__force void *)(shrd_ram.va + offset);
+       }
+
+       return va;
+}
+
+/*
+ * Convert PRU device address (instruction space) to kernel virtual address
+ *
+ * A PRU does not have an unified address space. Each PRU has its very own
+ * private Instruction RAM, and its device address is identical to that of
+ * its primary Data RAM device address.
+ */
+static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, int len)
+{
+       u32 offset;
+       void *va = NULL;
+
+       if (len <= 0)
+               return NULL;
+
+       if (da >= pru->iram_da &&
+           da + len <= pru->iram_da + pru->mem_regions[PRU_IOMEM_IRAM].size) {
+               offset = da - pru->iram_da;
+               va = (__force void *)(pru->mem_regions[PRU_IOMEM_IRAM].va +
+                                     offset);
+       }
+
+       return va;
+}
+
+/* PRU-specific address translator */
+static void *pru_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
+{
+       struct pru_rproc *pru = rproc->priv;
+       void *va;
+       u32 exec_flag;
+
+       exec_flag = ((flags & RPROC_FLAGS_ELF_SHDR) ? flags & SHF_EXECINSTR :
+                    ((flags & RPROC_FLAGS_ELF_PHDR) ? flags & PF_X : 0));
+
+       if (exec_flag)
+               va = pru_i_da_to_va(pru, da, len);
+       else
+               va = pru_d_da_to_va(pru, da, len);
+
+       return va;
+}
+
+static struct rproc_ops pru_rproc_ops = {
+       .start                  = pru_rproc_start,
+       .stop                   = pru_rproc_stop,
+       .kick                   = pru_rproc_kick,
+       .handle_vendor_rsc      = pru_rproc_handle_vendor_rsc,
+       .da_to_va               = pru_da_to_va,
+};
+
+/*
+ * compute PRU id based on the IRAM addresses. The PRU IRAMs are
+ * always at a particular offset within the PRUSS address space.
+ * The other alternative is to use static data for each core (not a
+ * hardware property to define it in DT), and the id can always be
+ * computated using this inherent address logic.
+ */
+static int pru_rproc_set_id(struct pru_rproc *pru)
+{
+       int ret = 0;
+       u32 mask1 = PRU0_IRAM_ADDR_MASK;
+       u32 mask2 = PRU1_IRAM_ADDR_MASK;
+
+       if ((pru->mem_regions[PRU_IOMEM_IRAM].pa & mask1) == mask1)
+               pru->id = PRUSS_PRU0;
+       else if ((pru->mem_regions[PRU_IOMEM_IRAM].pa & mask2) == mask2)
+               pru->id = PRUSS_PRU1;
+       else
+               ret = -EINVAL;
+
+       return ret;
+}
+
+static int pru_rproc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct platform_device *ppdev = to_platform_device(dev->parent);
+       struct pru_rproc *pru;
+       const char *fw_name;
+       struct rproc *rproc = NULL;
+       struct mbox_client *client;
+       struct resource *res;
+       int i, ret;
+       const char *mem_names[PRU_IOMEM_MAX] = { "iram", "control", "debug" };
+
+       if (!np) {
+               dev_err(dev, "Non-DT platform device not supported\n");
+               return -ENODEV;
+       }
+
+       ret = of_property_read_string(np, "firmware-name", &fw_name);
+       if (ret) {
+               dev_err(dev, "unable to retrieve firmware-name %d\n", ret);
+               return ret;
+       }
+
+       rproc = rproc_alloc(dev, pdev->name, &pru_rproc_ops, fw_name,
+                           sizeof(*pru));
+       if (!rproc) {
+               dev_err(dev, "rproc_alloc failed\n");
+               return -ENOMEM;
+       }
+       /* error recovery is not supported for PRUs */
+       rproc->recovery_disabled = true;
+
+       /*
+        * rproc_add will auto-boot the processor normally, but this is
+        * not desired with PRU client driven boot-flow methodology. A PRU
+        * application/client driver will boot the corresponding PRU
+        * remote-processor as part of its state machine either through
+        * the remoteproc sysfs interface or through the equivalent kernel API
+        */
+       rproc->auto_boot = false;
+
+       pru = rproc->priv;
+       pru->pruss = platform_get_drvdata(ppdev);
+       pru->rproc = rproc;
+       pru->fw_name = fw_name;
+       spin_lock_init(&pru->rmw_lock);
+       mutex_init(&pru->lock);
+
+       /* XXX: get this from match data if different in the future */
+       pru->iram_da = 0;
+       pru->pdram_da = 0;
+       pru->sdram_da = 0x2000;
+       pru->shrdram_da = 0x10000;
+
+       for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  mem_names[i]);
+               pru->mem_regions[i].va = devm_ioremap_resource(dev, res);
+               if (IS_ERR(pru->mem_regions[i].va)) {
+                       dev_err(dev, "failed to parse and map memory resource %d %s\n",
+                               i, mem_names[i]);
+                       ret = PTR_ERR(pru->mem_regions[i].va);
+                       goto free_rproc;
+               }
+               pru->mem_regions[i].pa = res->start;
+               pru->mem_regions[i].size = resource_size(res);
+
+               dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %p\n",
+                       mem_names[i], &pru->mem_regions[i].pa,
+                       pru->mem_regions[i].size, pru->mem_regions[i].va);
+       }
+
+       ret = pru_rproc_set_id(pru);
+       if (ret < 0)
+               goto free_rproc;
+
+       platform_set_drvdata(pdev, rproc);
+
+       /* get optional vring and kick interrupts for supporting virtio rpmsg */
+       pru->irq_vring = platform_get_irq_byname(pdev, "vring");
+       if (pru->irq_vring <= 0) {
+               ret = pru->irq_vring;
+               if (ret == -EPROBE_DEFER)
+                       goto free_rproc;
+               dev_dbg(dev, "unable to get vring interrupt, status = %d\n",
+                       ret);
+       }
+
+       pru->irq_kick = platform_get_irq_byname(pdev, "kick");
+       if (pru->irq_kick <= 0) {
+               ret = pru->irq_kick;
+               if (ret == -EPROBE_DEFER)
+                       goto free_rproc;
+               dev_dbg(dev, "unable to get kick interrupt, status = %d\n",
+                       ret);
+       }
+
+       /*
+        * get optional mailbox for virtio rpmsg signalling if vring and kick
+        * interrupts are not specified for OMAP architecture based SoCs
+        */
+       if (pru->irq_vring <= 0 && pru->irq_kick <= 0 &&
+           !of_device_is_compatible(np, "ti,k2g-pru")) {
+               client = &pru->client;
+               client->dev = dev;
+               client->tx_done = NULL;
+               client->rx_callback = pru_rproc_mbox_callback;
+               client->tx_block = false;
+               client->knows_txdone = false;
+               pru->mbox = mbox_request_channel(client, 0);
+               if (IS_ERR(pru->mbox)) {
+                       ret = PTR_ERR(pru->mbox);
+                       pru->mbox = NULL;
+                       dev_dbg(dev, "unable to get mailbox channel, status = %d\n",
+                               ret);
+               }
+       }
+
+       ret = rproc_add(pru->rproc);
+       if (ret) {
+               dev_err(dev, "rproc_add failed: %d\n", ret);
+               goto put_mbox;
+       }
+
+       pru_rproc_create_debug_entries(rproc);
+
+       dev_info(dev, "PRU rproc node %s probed successfully\n", np->full_name);
+
+       return 0;
+
+put_mbox:
+       mbox_free_channel(pru->mbox);
+free_rproc:
+       rproc_free(rproc);
+       return ret;
+}
+
+static int pru_rproc_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rproc *rproc = platform_get_drvdata(pdev);
+       struct pru_rproc *pru = rproc->priv;
+
+       dev_info(dev, "%s: removing rproc %s\n", __func__, rproc->name);
+
+       mbox_free_channel(pru->mbox);
+
+       rproc_del(rproc);
+       rproc_free(rproc);
+
+       return 0;
+}
+
+static const struct of_device_id pru_rproc_match[] = {
+       { .compatible = "ti,am3356-pru", },
+       { .compatible = "ti,am4376-pru", },
+       { .compatible = "ti,am5728-pru", },
+       { .compatible = "ti,k2g-pru",    },
+       {},
+};
+MODULE_DEVICE_TABLE(of, pru_rproc_match);
+
+static struct platform_driver pru_rproc_driver = {
+       .driver = {
+               .name   = "pru-rproc",
+               .of_match_table = pru_rproc_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe  = pru_rproc_probe,
+       .remove = pru_rproc_remove,
+};
+module_platform_driver(pru_rproc_driver);
+
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_DESCRIPTION("PRU-ICSS Remote Processor Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/pru_rproc.h b/drivers/remoteproc/pru_rproc.h
new file mode 100644 (file)
index 0000000..9cfe399
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * PRUSS Remote Processor specific types
+ *
+ * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *     Suman Anna <s-anna@ti.com>
+ */
+
+#ifndef _PRU_RPROC_H_
+#define _PRU_RPROC_H_
+
+/**
+ * enum pruss_rsc_types - PRU specific resource types
+ *
+ * @PRUSS_RSC_INTRS: Resource holding information on PRU INTC configuration
+ * @PRUSS_RSC_MAX: Indicates end of known/defined PRU resource types.
+ *                This should be the last definition.
+ *
+ * Introduce new vendor resource types before PRUSS_RSC_MAX.
+ */
+enum pruss_rsc_types {
+       PRUSS_RSC_INTRS = 1,
+       PRUSS_RSC_MAX   = 2,
+};
+
+/**
+ * struct pruss_event_chnl - PRU system events _to_ channel mapping
+ * @event: number of the system event
+ * @chnl: channel number assigned to a given @event
+ *
+ * PRU system events are mapped to channels, and these channels are mapped
+ * to host interrupts. Events can be mapped to channels in a one-to-one or
+ * many-to-one ratio (multiple events per channel), and channels can be
+ * mapped to host interrupts in a one-to-one or many-to-one ratio (multiple
+ * channels per interrupt).
+ *
+ */
+struct pruss_event_chnl {
+       s8 event;
+       s8 chnl;
+};
+
+/**
+ * struct fw_rsc_pruss_intrmap - custom/vendor resource to define PRU interrupts
+ * @reserved: reserved field providing padding and alignment
+ * @chnl_host_intr_map: array of PRU channels to host interrupt mappings
+ * @event_chnl_map_size: number of event_channel mappings defined in
+ *                      @event_chnl_map_addr
+ * @event_chnl_map_addr: PRU device address of pointer to array of events to
+ *                      channel mappings (struct pruss_event_chnl elements)
+ *
+ * PRU system events are mapped to channels, and these channels are mapped
+ * to host interrupts. Events can be mapped to channels in a one-to-one or
+ * many-to-one ratio (multiple events per channel), and channels can be
+ * mapped to host interrupts in a one-to-one or many-to-one ratio (multiple
+ * channels per interrupt).
+ */
+struct fw_rsc_pruss_intrmap {
+       u16 reserved;
+       s8 chnl_host_intr_map[10];
+       u32 event_chnl_map_size;
+       u32 event_chnl_map_addr;
+};
+
+#endif /* _PRU_RPROC_H_ */
index d4339a6da616ecfa9d201ca38cece5ee35e7696b..458fc64101a5ae1b4d795ad287af70b801fb3db3 100644 (file)
@@ -167,7 +167,7 @@ static int adsp_stop(struct rproc *rproc)
        return ret;
 }
 
-static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
 {
        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
        int offset;
index d7a4b9eca5d25bc6d7d37257f23bb424bc45541e..beeaa5b3b4873a77170cd8f306715ba02d086a57 100644 (file)
@@ -976,7 +976,7 @@ static int q6v5_stop(struct rproc *rproc)
        return 0;
 }
 
-static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
 {
        struct q6v5 *qproc = rproc->priv;
        int offset;
index f93e1e4a1cc081a987014da52d0460ea454b06d1..5b4d4c662b7b3ac1c7d357b27bfc212fee39e969 100644 (file)
@@ -406,7 +406,7 @@ static int q6v5_wcss_stop(struct rproc *rproc)
        return 0;
 }
 
-static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
 {
        struct q6v5_wcss *wcss = rproc->priv;
        int offset;
index b0e07e9f42d5698e64baa7897d75460c28a89690..eb7d4b9f0e03e4dfb0f4fdc514e867fb9939f942 100644 (file)
@@ -295,7 +295,7 @@ static int wcnss_stop(struct rproc *rproc)
        return ret;
 }
 
-static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
 {
        struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
        int offset;
index aa6206706fe335008060a9e3da7a8a97d2f91e50..89580b957d66239ecdbde266ea4996c670965a50 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/crc32.h>
 #include <linux/virtio_ids.h>
 #include <linux/virtio_ring.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <asm/byteorder.h>
 
 #include "remoteproc_internal.h"
@@ -145,6 +147,7 @@ static void rproc_disable_iommu(struct rproc *rproc)
  * @rproc: handle of a remote processor
  * @da: remoteproc device address to translate
  * @len: length of the memory region @da is pointing to
+ * @flags: flags to pass onto platform implementations for aiding translations
  *
  * Some remote processors will ask us to allocate them physically contiguous
  * memory regions (which we call "carveouts"), and map them to specific
@@ -160,7 +163,10 @@ static void rproc_disable_iommu(struct rproc *rproc)
  * carveouts and translate specific device addresses to kernel virtual addresses
  * so we can access the referenced memory. This function also allows to perform
  * translations on the internal remoteproc memory regions through a platform
- * implementation specific da_to_va ops, if present.
+ * implementation specific da_to_va ops, if present. The @flags field is passed
+ * onto these ops to aid the translation within the ops implementation. The
+ * @flags field is to be passed as a combination of the RPROC_FLAGS_xxx type
+ * and the pertinent flags value for that type.
  *
  * The function returns a valid kernel address on success or NULL on failure.
  *
@@ -169,13 +175,13 @@ static void rproc_disable_iommu(struct rproc *rproc)
  * here the output of the DMA API for the carveouts, which should be more
  * correct.
  */
-void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+void *rproc_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags)
 {
        struct rproc_mem_entry *carveout;
        void *ptr = NULL;
 
        if (rproc->ops->da_to_va) {
-               ptr = rproc->ops->da_to_va(rproc, da, len);
+               ptr = rproc->ops->da_to_va(rproc, da, len, flags);
                if (ptr)
                        goto out;
        }
@@ -201,6 +207,61 @@ out:
 }
 EXPORT_SYMBOL(rproc_da_to_va);
 
+/**
+ * rproc_pa_to_da() - lookup the rproc device address for a physical address
+ * @rproc: handle of a remote processor
+ * @pa: physical address of the buffer to translate
+ * @da: device address to return
+ *
+ * Communication clients of remote processors usually would need a means to
+ * convert a host buffer pointer to an equivalent device virtual address pointer
+ * that the code running on the remote processor can operate on. These buffer
+ * pointers can either be from the physically contiguous memory regions (or
+ * "carveouts") or can be some memory-mapped Device IO memory. This function
+ * provides a means to translate a given physical address to its associated
+ * device address.
+ *
+ * The function looks through both the carveouts and the device memory mappings
+ * since both of them are stored in separate lists.
+ *
+ * Returns 0 on success, or an appropriate error code otherwise. The translated
+ * device address is returned through the appropriate function argument.
+ */
+int rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da)
+{
+       int ret = -EINVAL;
+       struct rproc_mem_entry *maps = NULL;
+
+       if (!rproc || !da)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&rproc->lock))
+               return -EINTR;
+
+       if (rproc->state == RPROC_RUNNING || rproc->state == RPROC_SUSPENDED) {
+               /* Look in the mappings first */
+               list_for_each_entry(maps, &rproc->mappings, node) {
+                       if (pa >= maps->dma && pa < (maps->dma + maps->len)) {
+                               *da = maps->da + (pa - maps->dma);
+                               ret = 0;
+                               goto exit;
+                       }
+               }
+               /* If not, check in the carveouts */
+               list_for_each_entry(maps, &rproc->carveouts, node) {
+                       if (pa >= maps->dma && pa < (maps->dma + maps->len)) {
+                               *da = maps->da + (pa - maps->dma);
+                               ret = 0;
+                               break;
+                       }
+               }
+       }
+exit:
+       mutex_unlock(&rproc->lock);
+       return ret;
+}
+EXPORT_SYMBOL(rproc_pa_to_da);
+
 int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
 {
        struct rproc *rproc = rvdev->rproc;
@@ -470,7 +531,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
        }
 
        /* what's the kernel address of this resource ? */
-       ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
+       ptr = rproc_da_to_va(rproc, rsc->da, rsc->len, RPROC_FLAGS_NONE);
        if (!ptr) {
                dev_err(dev, "erroneous trace resource entry\n");
                return -EINVAL;
@@ -569,6 +630,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
         * We can't trust the remote processor not to change the resource
         * table, so we must maintain this info independently.
         */
+       mapping->dma = rsc->pa;
        mapping->da = rsc->da;
        mapping->len = rsc->len;
        list_add_tail(&mapping->node, &rproc->mappings);
@@ -709,6 +771,7 @@ static int rproc_handle_carveout(struct rproc *rproc,
        carveout->len = rsc->len;
        carveout->dma = dma;
        carveout->da = rsc->da;
+       strlcpy(carveout->name, rsc->name, sizeof(carveout->name));
 
        list_add_tail(&carveout->node, &rproc->carveouts);
 
@@ -723,6 +786,42 @@ free_carv:
        return ret;
 }
 
+/**
+ * rproc_handle_vendor_rsc() - provide implementation specific hook
+ *                            to handle vendor/custom resources
+ * @rproc: the remote processor
+ * @rsc: vendor resource to be handled by remoteproc drivers
+ * @offset: offset of the resource data in resource table
+ * @avail: size of available data
+ *
+ * Remoteproc implementations might want to add resource table entries
+ * that are not generic enough to be handled by the framework. This
+ * provides a hook to handle such custom resources. Note that a single
+ * hook is reused between RSC_PRELOAD_VENDOR and RSC_PRELOAD_VENDOR
+ * resources with the platform driver implementation distinguishing
+ * the two based on the sub-type resource.
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_vendor_rsc(struct rproc *rproc,
+                                  struct fw_rsc_vendor *rsc,
+                                  int offset, int avail)
+{
+       struct device *dev = &rproc->dev;
+
+       if (!rproc->ops->handle_vendor_rsc) {
+               dev_err(dev, "vendor resource handler not implemented, ignoring resource\n");
+               return 0;
+       }
+
+       if (sizeof(*rsc) > avail) {
+               dev_err(dev, "vendor resource is truncated\n");
+               return -EINVAL;
+       }
+
+       return rproc->ops->handle_vendor_rsc(rproc, (void *)rsc);
+}
+
 /*
  * A lookup table for resource handlers. The indices are defined in
  * enum fw_resource_type.
@@ -731,9 +830,15 @@ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
        [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
        [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
        [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
+       [RSC_PRELOAD_VENDOR] = (rproc_handle_resource_t)rproc_handle_vendor_rsc,
        [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
 };
 
+static rproc_handle_resource_t rproc_post_loading_handlers[RSC_LAST] = {
+       [RSC_POSTLOAD_VENDOR] =
+                       (rproc_handle_resource_t)rproc_handle_vendor_rsc,
+};
+
 /* handle firmware resource entries before booting the remote processor */
 static int rproc_handle_resources(struct rproc *rproc,
                                  rproc_handle_resource_t handlers[RSC_LAST])
@@ -915,11 +1020,14 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw)
        struct device *dev = &rproc->dev;
        int ret;
 
-       /* load the ELF segments to memory */
-       ret = rproc_load_segments(rproc, fw);
-       if (ret) {
-               dev_err(dev, "Failed to load program segments: %d\n", ret);
-               return ret;
+       if (!rproc->skip_load) {
+               /* load the ELF segments to memory */
+               ret = rproc_load_segments(rproc, fw);
+               if (ret) {
+                       dev_err(dev, "Failed to load program segments: %d\n",
+                               ret);
+                       return ret;
+               }
        }
 
        /*
@@ -936,6 +1044,14 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw)
                rproc->table_ptr = loaded_table;
        }
 
+       /* handle fw resources which require fw segments to be loaded */
+       ret = rproc_handle_resources(rproc, rproc_post_loading_handlers);
+       if (ret) {
+               dev_err(dev, "Failed to process post-loading resources: %d\n",
+                       ret);
+               goto reset_table_ptr;
+       }
+
        ret = rproc_prepare_subdevices(rproc);
        if (ret) {
                dev_err(dev, "failed to prepare subdevices for %s: %d\n",
@@ -987,7 +1103,11 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
        if (ret)
                return ret;
 
-       dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
+       if (!rproc->skip_firmware_request)
+               dev_info(dev, "Booting fw image %s, size %zd\n",
+                        name, fw->size);
+       else
+               dev_info(dev, "Booting unspecified pre-loaded fw image\n");
 
        /*
         * if enabling an IOMMU isn't relevant for this rproc, this is
@@ -1183,7 +1303,8 @@ static void rproc_coredump(struct rproc *rproc)
                phdr->p_flags = PF_R | PF_W | PF_X;
                phdr->p_align = 0;
 
-               ptr = rproc_da_to_va(rproc, segment->da, segment->size);
+               ptr = rproc_da_to_va(rproc, segment->da, segment->size,
+                                    RPROC_FLAGS_NONE);
                if (!ptr) {
                        dev_err(&rproc->dev,
                                "invalid coredump segment (%pad, %zu)\n",
@@ -1277,6 +1398,36 @@ static void rproc_crash_handler_work(struct work_struct *work)
                rproc_trigger_recovery(rproc);
 }
 
+/**
+ * rproc_get_id() - return the id for the rproc device
+ * @rproc: handle of a remote processor
+ *
+ * Each rproc device is associated with a platform device, which is created
+ * either from device tree (majority newer platforms) or using legacy style
+ * platform device creation (fewer legacy platforms). This function retrieves
+ * an unique id for each remote processor and is useful for clients needing
+ * to distinguish each of the remoteprocs. This unique id is derived using
+ * the platform device id for non-DT devices, or an alternate alias id for
+ * DT devices (since they do not have a valid platform device id). It is
+ * assumed that the platform devices were created with known ids or were
+ * given proper alias ids using the stem "rproc".
+ *
+ * Return: alias id for DT devices or platform device id for non-DT devices
+ * associated with the rproc
+ */
+int rproc_get_id(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev.parent;
+       struct device_node *np = dev->of_node;
+       struct platform_device *pdev = to_platform_device(dev);
+
+       if (np)
+               return of_alias_get_id(np, "rproc");
+       else
+               return pdev->id;
+}
+EXPORT_SYMBOL(rproc_get_id);
+
 /**
  * rproc_boot() - boot a remote processor
  * @rproc: handle of a remote processor
@@ -1321,16 +1472,19 @@ int rproc_boot(struct rproc *rproc)
 
        dev_info(dev, "powering up %s\n", rproc->name);
 
-       /* load firmware */
-       ret = request_firmware(&firmware_p, rproc->firmware, dev);
-       if (ret < 0) {
-               dev_err(dev, "request_firmware failed: %d\n", ret);
-               goto downref_rproc;
+       if (!rproc->skip_firmware_request) {
+               /* load firmware */
+               ret = request_firmware(&firmware_p, rproc->firmware, dev);
+               if (ret < 0) {
+                       dev_err(dev, "request_firmware failed: %d\n", ret);
+                       goto downref_rproc;
+               }
        }
 
        ret = rproc_fw_boot(rproc, firmware_p);
 
-       release_firmware(firmware_p);
+       if (!rproc->skip_firmware_request)
+               release_firmware(firmware_p);
 
 downref_rproc:
        if (ret)
@@ -1779,6 +1933,69 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
 }
 EXPORT_SYMBOL(rproc_report_crash);
 
+/**
+ * rproc_set_firmware() - assign a new firmware
+ * @rproc: rproc handle to which the new firmware is being assigned
+ * @fw_name: new firmware name to be assigned
+ *
+ * This function allows remoteproc drivers or clients to configure a custom
+ * firmware name that is different from the default name used during remoteproc
+ * registration. The function does not trigger a remote processor boot,
+ * only sets the firmware name used for a subsequent boot. This function
+ * should also be called only when the remote processor is offline.
+ *
+ * This allows either the userspace to configure a different name through
+ * sysfs or a kernel-level remoteproc or a remoteproc client driver to set
+ * a specific firmware when it is controlling the boot and shutdown of the
+ * remote processor.
+ *
+ * Returns 0 on success or a negative value upon failure
+ */
+int rproc_set_firmware(struct rproc *rproc, const char *fw_name)
+{
+       struct device *dev;
+       int ret, len;
+       char *p;
+
+       if (!rproc || !fw_name)
+               return -EINVAL;
+
+       dev = rproc->dev.parent;
+
+       ret = mutex_lock_interruptible(&rproc->lock);
+       if (ret) {
+               dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
+               return -EINVAL;
+       }
+
+       if (rproc->state != RPROC_OFFLINE) {
+               dev_err(dev, "can't change firmware while running\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       len = strcspn(fw_name, "\n");
+       if (!len) {
+               dev_err(dev, "can't provide empty string for firmware name\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       p = kstrndup(fw_name, len, GFP_KERNEL);
+       if (!p) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       kfree(rproc->firmware);
+       rproc->firmware = p;
+
+out:
+       mutex_unlock(&rproc->lock);
+       return ret;
+}
+EXPORT_SYMBOL(rproc_set_firmware);
+
 static int __init remoteproc_init(void)
 {
        rproc_init_sysfs();
index a5c29f2764a34f4d12b3080cbcec30d4fc908506..79d6fa14e0ba0bb69fedfeaa36df7bc0a1e87166 100644 (file)
@@ -158,13 +158,15 @@ static const struct file_operations rproc_recovery_ops = {
 /* Expose resource table content via debugfs */
 static int rproc_rsc_table_show(struct seq_file *seq, void *p)
 {
-       static const char * const types[] = {"carveout", "devmem", "trace", "vdev"};
+       static const char * const types[] = {"carveout", "devmem", "trace",
+                                            "vdev", "preload", "postload"};
        struct rproc *rproc = seq->private;
        struct resource_table *table = rproc->table_ptr;
        struct fw_rsc_carveout *c;
        struct fw_rsc_devmem *d;
        struct fw_rsc_trace *t;
        struct fw_rsc_vdev *v;
+       struct fw_rsc_vendor *vr;
        int i, j;
 
        if (!table) {
@@ -230,6 +232,16 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p)
                                           v->vring[j].pa);
                        }
                        break;
+               case RSC_PRELOAD_VENDOR:
+               case RSC_POSTLOAD_VENDOR:
+                       vr = rsc;
+                       seq_printf(seq, "Entry %d is of type vendor-%s\n",
+                                  i, types[hdr->type]);
+                       seq_printf(seq, "  Vendor sub-type %d version %d\n",
+                                  vr->u.st.st_type, vr->u.st.st_ver);
+                       seq_printf(seq, "  Vendor resource size %d\n",
+                                  vr->size);
+                       break;
                default:
                        seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n",
                                   hdr->type, hdr);
@@ -260,6 +272,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p)
 
        list_for_each_entry(carveout, &rproc->carveouts, node) {
                seq_puts(seq, "Carveout memory entry:\n");
+               seq_printf(seq, "\tName: %s\n", carveout->name);
                seq_printf(seq, "\tVirtual address: %pK\n", carveout->va);
                seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma);
                seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da);
index b17d72ec8603bca2b1a6481c2e3d70c37537cbfd..fff2e0b4685e35a9d96cbf5ae10d42a21814951a 100644 (file)
@@ -182,7 +182,8 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
                }
 
                /* grab the kernel address for this device address */
-               ptr = rproc_da_to_va(rproc, da, memsz);
+               ptr = rproc_da_to_va(rproc, da, memsz,
+                                    RPROC_FLAGS_ELF_PHDR | phdr->p_flags);
                if (!ptr) {
                        dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
                        ret = -EINVAL;
@@ -333,6 +334,7 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
        if (!shdr)
                return NULL;
 
-       return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
+       return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size,
+                             RPROC_FLAGS_ELF_SHDR | shdr->sh_flags);
 }
 EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);
index 7570beb035b5f462f9c9ae19ef9ed1ceff6a6424..ada691be23186cb08ce0408e28a9afb207e27e44 100644 (file)
@@ -51,7 +51,6 @@ void rproc_exit_sysfs(void);
 void rproc_free_vring(struct rproc_vring *rvring);
 int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
 
-void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
 int rproc_trigger_recovery(struct rproc *rproc);
 
 int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw);
index 47be411400e56aed1b48f44d4254e0178a640412..6b7e8b2b43be0753ba55296d1ada1b5525c53fc8 100644 (file)
@@ -11,6 +11,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/module.h>
 #include <linux/remoteproc.h>
 
 #include "remoteproc_internal.h"
@@ -32,33 +33,13 @@ static ssize_t firmware_store(struct device *dev,
                              const char *buf, size_t count)
 {
        struct rproc *rproc = to_rproc(dev);
-       char *p;
-       int err, len = count;
+       int err;
 
-       err = mutex_lock_interruptible(&rproc->lock);
-       if (err) {
-               dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
-               return -EINVAL;
-       }
-
-       if (rproc->state != RPROC_OFFLINE) {
-               dev_err(dev, "can't change firmware while running\n");
-               err = -EBUSY;
-               goto out;
-       }
+       /* restrict sysfs operations if not allowed by remoteproc drivers */
+       if (rproc->deny_sysfs_ops)
+               return -EPERM;
 
-       len = strcspn(buf, "\n");
-
-       p = kstrndup(buf, len, GFP_KERNEL);
-       if (!p) {
-               err = -ENOMEM;
-               goto out;
-       }
-
-       kfree(rproc->firmware);
-       rproc->firmware = p;
-out:
-       mutex_unlock(&rproc->lock);
+       err = rproc_set_firmware(rproc, buf);
 
        return err ? err : count;
 }
@@ -96,18 +77,35 @@ static ssize_t state_store(struct device *dev,
        struct rproc *rproc = to_rproc(dev);
        int ret = 0;
 
+       /* restrict sysfs operations if not allowed by remoteproc drivers */
+       if (rproc->deny_sysfs_ops)
+               return -EPERM;
+
        if (sysfs_streq(buf, "start")) {
                if (rproc->state == RPROC_RUNNING)
                        return -EBUSY;
 
+               /*
+                * prevent underlying implementation from being removed
+                * when remoteproc does not support auto-boot
+                */
+               if (!rproc->auto_boot &&
+                   !try_module_get(dev->parent->driver->owner))
+                       return -EINVAL;
+
                ret = rproc_boot(rproc);
-               if (ret)
+               if (ret) {
                        dev_err(&rproc->dev, "Boot failed: %d\n", ret);
+                       if (!rproc->auto_boot)
+                               module_put(dev->parent->driver->owner);
+               }
        } else if (sysfs_streq(buf, "stop")) {
                if (rproc->state != RPROC_RUNNING)
                        return -EINVAL;
 
                rproc_shutdown(rproc);
+               if (!rproc->auto_boot)
+                       module_put(dev->parent->driver->owner);
        } else {
                dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
                ret = -EINVAL;
index d711d9430a4ffe2392c3a8af265e1b5d27ccf3a9..c41e58c7f9bdfaa396632aa160703a1861d78a9c 100644 (file)
@@ -178,7 +178,8 @@ static int slim_rproc_stop(struct rproc *rproc)
        return 0;
 }
 
-static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len,
+                                u32 flags)
 {
        struct st_slim_rproc *slim_rproc = rproc->priv;
        void *va = NULL;
index 1ada0e51fef61d26cbcb519443943b7b79b9bcc0..6257b1507a16579b8c728c1650875dc78bec1fc7 100644 (file)
@@ -88,7 +88,8 @@ static int wkup_m3_rproc_stop(struct rproc *rproc)
        return 0;
 }
 
-static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len,
+                                   u32 flags)
 {
        struct wkup_m3_rproc *wkupm3 = rproc->priv;
        void *va = NULL;
@@ -168,6 +169,7 @@ static int wkup_m3_rproc_probe(struct platform_device *pdev)
        }
 
        rproc->auto_boot = false;
+       rproc->deny_sysfs_ops = 1;
 
        wkupm3 = rproc->priv;
        wkupm3->rproc = rproc;
index e2ce4e638258b7a64de8e9f41e6167f09aaf8c74..a4fe983f424071ff861b9908ec4da5eba5cf7d38 100644 (file)
@@ -1459,6 +1459,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
                strncpy(chinfo.name, channel->name, sizeof(chinfo.name));
                chinfo.src = RPMSG_ADDR_ANY;
                chinfo.dst = RPMSG_ADDR_ANY;
+               chinfo.desc[0] = '\0';
 
                rpmsg_unregister_device(glink->dev, &chinfo);
        }
index 8da83a4ebadc32143ec2950f3fda2a922f86812c..ec342c87927057e0247b3d9128e17314b5cd7ee5 100644 (file)
@@ -1304,6 +1304,7 @@ static void qcom_channel_state_worker(struct work_struct *work)
                strncpy(chinfo.name, channel->name, sizeof(chinfo.name));
                chinfo.src = RPMSG_ADDR_ANY;
                chinfo.dst = RPMSG_ADDR_ANY;
+               chinfo.desc[0] = '\0';
                rpmsg_unregister_device(&edge->dev, &chinfo);
                channel->registered = false;
                spin_lock_irqsave(&edge->channels_lock, flags);
index a76b963a7e50f31e919c9e476de8d2161fad390f..4bd91445a2fdad78cfae5f93e953f523583a5935 100644 (file)
@@ -167,9 +167,9 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static ssize_t rpmsg_eptdev_read(struct file *filp, char __user *buf,
-                                size_t len, loff_t *f_pos)
+static ssize_t rpmsg_eptdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
+       struct file *filp = iocb->ki_filp;
        struct rpmsg_eptdev *eptdev = filp->private_data;
        unsigned long flags;
        struct sk_buff *skb;
@@ -205,8 +205,8 @@ static ssize_t rpmsg_eptdev_read(struct file *filp, char __user *buf,
        if (!skb)
                return -EFAULT;
 
-       use = min_t(size_t, len, skb->len);
-       if (copy_to_user(buf, skb->data, use))
+       use = min_t(size_t, iov_iter_count(to), skb->len);
+       if (copy_to_iter(skb->data, use, to) != use)
                use = -EFAULT;
 
        kfree_skb(skb);
@@ -214,16 +214,21 @@ static ssize_t rpmsg_eptdev_read(struct file *filp, char __user *buf,
        return use;
 }
 
-static ssize_t rpmsg_eptdev_write(struct file *filp, const char __user *buf,
-                                 size_t len, loff_t *f_pos)
+static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb,
+                                      struct iov_iter *from)
 {
+       struct file *filp = iocb->ki_filp;
        struct rpmsg_eptdev *eptdev = filp->private_data;
+       size_t len = iov_iter_count(from);
        void *kbuf;
        int ret;
 
-       kbuf = memdup_user(buf, len);
-       if (IS_ERR(kbuf))
-               return PTR_ERR(kbuf);
+       kbuf = kzalloc(len, GFP_KERNEL);
+       if (!kbuf)
+               return -ENOMEM;
+
+       if (!copy_from_iter_full(kbuf, len, from))
+               return -EFAULT;
 
        if (mutex_lock_interruptible(&eptdev->ept_lock)) {
                ret = -ERESTARTSYS;
@@ -281,8 +286,8 @@ static const struct file_operations rpmsg_eptdev_fops = {
        .owner = THIS_MODULE,
        .open = rpmsg_eptdev_open,
        .release = rpmsg_eptdev_release,
-       .read = rpmsg_eptdev_read,
-       .write = rpmsg_eptdev_write,
+       .read_iter = rpmsg_eptdev_read_iter,
+       .write_iter = rpmsg_eptdev_write_iter,
        .poll = rpmsg_eptdev_poll,
        .unlocked_ioctl = rpmsg_eptdev_ioctl,
        .compat_ioctl = rpmsg_eptdev_ioctl,
@@ -437,6 +442,7 @@ static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd,
        chinfo.name[RPMSG_NAME_SIZE-1] = '\0';
        chinfo.src = eptinfo.src;
        chinfo.dst = eptinfo.dst;
+       chinfo.desc[0] = '\0';
 
        return rpmsg_eptdev_create(ctrldev, chinfo);
 };
index 8122807db380949b44624866a9403ddbc0569d15..ccec818c9fd1b4670f87113c6aa15c2e5606df5a 100644 (file)
@@ -302,6 +302,10 @@ static int rpmsg_device_match(struct device *dev, void *data)
        if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE))
                return 0;
 
+       if (chinfo->desc && chinfo->desc != rpdev->desc &&
+           strncmp(chinfo->desc, rpdev->desc, RPMSG_NAME_SIZE))
+               return 0;
+
        /* found a match ! */
        return 1;
 }
@@ -365,6 +369,7 @@ static DEVICE_ATTR_RW(field)
 
 /* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
 rpmsg_show_attr(name, id.name, "%s\n");
+rpmsg_show_attr(desc, desc, "%s\n");
 rpmsg_show_attr(src, src, "0x%x\n");
 rpmsg_show_attr(dst, dst, "0x%x\n");
 rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
@@ -386,6 +391,7 @@ static DEVICE_ATTR_RO(modalias);
 
 static struct attribute *rpmsg_dev_attrs[] = {
        &dev_attr_name.attr,
+       &dev_attr_desc.attr,
        &dev_attr_modalias.attr,
        &dev_attr_dst.attr,
        &dev_attr_src.attr,
index 664f957012cdee0247484fed2e157a30b7ec1fc9..f29dee7310260e5114487eaedbbd76a45faf0200 100644 (file)
@@ -110,6 +110,23 @@ struct rpmsg_ns_msg {
        u32 flags;
 } __packed;
 
+/**
+ * struct rpmsg_ns_msg_ext - dynamic name service announcement message v2
+ * @name: name of remote service that is published
+ * @desc: description of remote service
+ * @addr: address of remote service that is published
+ * @flags: indicates whether service is created or destroyed
+ *
+ * Interchangeable nameservice message with rpmsg_ns_msg. This one has
+ * the addition of the desc field for extra flexibility.
+ */
+struct rpmsg_ns_msg_ext {
+       char name[RPMSG_NAME_SIZE];
+       char desc[RPMSG_NAME_SIZE];
+       u32 addr;
+       u32 flags;
+} __packed;
+
 /**
  * enum rpmsg_ns_flags - dynamic name service announcement flags
  *
@@ -402,8 +419,9 @@ static struct rpmsg_device *rpmsg_create_channel(struct virtproc_info *vrp,
        if (tmp) {
                /* decrement the matched device's refcount back */
                put_device(tmp);
-               dev_err(dev, "channel %s:%x:%x already exist\n",
-                               chinfo->name, chinfo->src, chinfo->dst);
+               dev_err(dev, "channel %s:%s:%x:%x already exist\n",
+                       chinfo->name, chinfo->desc,
+                       chinfo->src, chinfo->dst);
                return NULL;
        }
 
@@ -419,6 +437,7 @@ static struct rpmsg_device *rpmsg_create_channel(struct virtproc_info *vrp,
        rpdev->src = chinfo->src;
        rpdev->dst = chinfo->dst;
        rpdev->ops = &virtio_rpmsg_ops;
+       strncpy(rpdev->desc, chinfo->desc, RPMSG_NAME_SIZE);
 
        /*
         * rpmsg server channels has predefined local address (for now),
@@ -816,18 +835,29 @@ static int rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,
                       void *priv, u32 src)
 {
        struct rpmsg_ns_msg *msg = data;
+       struct rpmsg_ns_msg_ext *msg_ext = data;
        struct rpmsg_device *newch;
        struct rpmsg_channel_info chinfo;
        struct virtproc_info *vrp = priv;
        struct device *dev = &vrp->vdev->dev;
        int ret;
+       u32 addr;
+       u32 flags;
 
 #if defined(CONFIG_DYNAMIC_DEBUG)
        dynamic_hex_dump("NS announcement: ", DUMP_PREFIX_NONE, 16, 1,
                         data, len, true);
 #endif
 
-       if (len != sizeof(*msg)) {
+       if (len == sizeof(*msg)) {
+               addr = msg->addr;
+               flags = msg->flags;
+               chinfo.desc[0] = '\0';
+       } else if (len == sizeof(*msg_ext)) {
+               addr = msg_ext->addr;
+               flags = msg_ext->flags;
+               strncpy(chinfo.desc, msg_ext->desc, sizeof(chinfo.desc));
+       } else if (len != sizeof(*msg)) {
                dev_err(dev, "malformed ns msg (%d)\n", len);
                return -EINVAL;
        }
@@ -847,14 +877,14 @@ static int rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,
        msg->name[RPMSG_NAME_SIZE - 1] = '\0';
 
        dev_info(dev, "%sing channel %s addr 0x%x\n",
-                msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat",
-                msg->name, msg->addr);
+                flags & RPMSG_NS_DESTROY ? "destroy" : "creat",
+                msg->name, addr);
 
        strncpy(chinfo.name, msg->name, sizeof(chinfo.name));
        chinfo.src = RPMSG_ADDR_ANY;
-       chinfo.dst = msg->addr;
+       chinfo.dst = addr;
 
-       if (msg->flags & RPMSG_NS_DESTROY) {
+       if (flags & RPMSG_NS_DESTROY) {
                ret = rpmsg_unregister_device(&vrp->vdev->dev, &chinfo);
                if (ret)
                        dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);
index df3ccb30bc2dddba0d2d6accccdd6a6c5a7bc53c..028ce1a0c99ce1f7cb916258ba6482c23ff3c177 100644 (file)
@@ -284,6 +284,7 @@ struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rp
        strncpy(chinfo.name, name, sizeof(chinfo.name));
        chinfo.src = RPMSG_ADDR_ANY;
        chinfo.dst = RPMSG_ADDR_ANY;
+       chinfo.desc[0] = '\0';
 
        return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo);
 }
index be4570baad96c0c1a1bd5c1d9eb1d0d70e4da97d..35574ecef8cae15b972a61d95842a885fff523f7 100644 (file)
@@ -20,6 +20,17 @@ menuconfig SOC_TI
 
 if SOC_TI
 
+config KEYSTONE_DSP_MEM
+       tristate "TI Keystone DSP Memory Mapping Driver"
+       depends on ARCH_KEYSTONE
+       help
+         Userspace memory mapping interface driver for TI Keystone SoCs.
+         Provides access to MSM SRAM memory regions and dedicated DDR
+         carveout memory regions to user space to aid userspace loading
+         of the DSPs within the SoC.
+
+         If unsure, say N.
+
 config KEYSTONE_NAVIGATOR_QMSS
        tristate "Keystone Queue Manager Sub System"
        depends on ARCH_KEYSTONE
@@ -73,4 +84,15 @@ config TI_SCI_PM_DOMAINS
          called ti_sci_pm_domains. Note this is needed early in boot before
          rootfs may be available.
 
+config TI_PRUSS
+       tristate "TI PRU-ICSS Subsystem Platform drivers"
+       depends on SOC_AM33XX || SOC_AM43XX || SOC_DRA7XX || ARCH_KEYSTONE
+       select MFD_SYSCON
+       help
+         TI PRU-ICSS Subsystem platform specific support.
+
+         Say Y or M here to support the Programmable Realtime Unit (PRU)
+         processors on various TI SoCs. It's safe to say N here if you're
+         not interested in the PRU or if you are unsure.
+
 endif # SOC_TI
index a22edc0b258ae48a4c2fa08217688f033b524965..b69beb059b60721eeb219064754b67a0fe06dd95 100644 (file)
@@ -2,9 +2,11 @@
 #
 # TI Keystone SOC drivers
 #
+obj-$(CONFIG_KEYSTONE_DSP_MEM)         += keystone_dsp_mem.o
 obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS)  += knav_qmss.o
 knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
 obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA)   += knav_dma.o
 obj-$(CONFIG_AMX3_PM)                  += pm33xx.o
 obj-$(CONFIG_WKUP_M3_IPC)              += wkup_m3_ipc.o
 obj-$(CONFIG_TI_SCI_PM_DOMAINS)                += ti_sci_pm_domains.o
+obj-$(CONFIG_TI_PRUSS)                 += pruss_soc_bus.o pruss.o
diff --git a/drivers/soc/ti/keystone_dsp_mem.c b/drivers/soc/ti/keystone_dsp_mem.c
new file mode 100644 (file)
index 0000000..f065cd3
--- /dev/null
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI Keystone DSP Memory Mapping Driver
+ *
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <uapi/linux/keystone_dsp_mem.h>
+
+#define KEYSTONE_ALIAS_PHYS_START      0x80000000ULL
+#define KEYSTONE_ALIAS_PHYS_SIZE       0x80000000ULL /* 2G */
+
+#define KEYSTONE_HIGH_PHYS_START       0x800000000ULL
+#define KEYSTONE_HIGH_PHYS_LIMIT       (KEYSTONE_HIGH_PHYS_START + \
+                                        KEYSTONE_ALIAS_PHYS_SIZE)
+
+#define to_alias_addr(addr)    (((addr) - KEYSTONE_HIGH_PHYS_START) + \
+                                KEYSTONE_ALIAS_PHYS_START)
+
+/**
+ * struct keystone_dsp_mem - internal memory structure
+ * @addr: physical address on the bus to access the memory region
+ * @size: size of the memory region
+ * @kobj: kobject for the sysfs directory file
+ */
+struct keystone_dsp_mem {
+       phys_addr_t addr;
+       resource_size_t size;
+       struct kobject kobj;
+};
+
+#define to_dsp_mem(obj) container_of(obj, struct keystone_dsp_mem, kobj)
+
+/**
+ * struct keystone_dsp_mem_info - Keystone DSP Memory device structure
+ * @misc: child miscdevice structure
+ * @mem: memory region array pointer
+ * @num_maps: number of memory regions
+ */
+struct keystone_dsp_mem_info {
+       struct miscdevice misc;
+       struct keystone_dsp_mem *mem;
+       int num_maps;
+};
+
+static struct keystone_dsp_mem_info *dsp_mem;
+
+#define to_dsp_mem_info(m) container_of(m, struct keystone_dsp_mem_info, misc)
+
+static ssize_t mem_addr_show(struct keystone_dsp_mem *mem, char *buf)
+{
+       return sprintf(buf, "%pa\n", &mem->addr);
+}
+
+static ssize_t mem_size_show(struct keystone_dsp_mem *mem, char *buf)
+{
+       return sprintf(buf, "%pa\n", &mem->size);
+}
+
+struct mem_sysfs_entry {
+       struct attribute attr;
+       ssize_t (*show)(struct keystone_dsp_mem *mem, char *buf);
+       ssize_t (*store)(struct keystone_dsp_mem *mem, const char *buf,
+                        size_t len);
+};
+
+static struct mem_sysfs_entry addr_attribute =
+       __ATTR(addr, 0444, mem_addr_show, NULL);
+static struct mem_sysfs_entry size_attribute =
+       __ATTR(size, 0444, mem_size_show, NULL);
+
+static struct attribute *attrs[] = {
+       &addr_attribute.attr,
+       &size_attribute.attr,
+       NULL,   /* sentinel */
+};
+
+static ssize_t mem_type_show(struct kobject *kobj, struct attribute *attr,
+                            char *buf)
+{
+       struct keystone_dsp_mem *mem = to_dsp_mem(kobj);
+       struct mem_sysfs_entry *entry;
+
+       entry = container_of(attr, struct mem_sysfs_entry, attr);
+       if (!entry->show)
+               return -EIO;
+
+       return entry->show(mem, buf);
+}
+
+static const struct sysfs_ops mem_sysfs_ops = {
+       .show = mem_type_show,
+};
+
+static struct kobj_type mem_attr_type = {
+       .sysfs_ops      = &mem_sysfs_ops,
+       .default_attrs  = attrs,
+};
+
+static int keystone_dsp_mem_add_attrs(struct keystone_dsp_mem_info *dsp_mem)
+{
+       int i, ret;
+       struct keystone_dsp_mem *mem;
+       struct kobject *kobj_parent = &dsp_mem->misc.this_device->kobj;
+
+       for (i = 0; i < dsp_mem->num_maps; i++) {
+               mem = &dsp_mem->mem[i];
+               kobject_init(&mem->kobj, &mem_attr_type);
+               ret = kobject_add(&mem->kobj, kobj_parent, "memory%d", i);
+               if (ret)
+                       goto err_kobj;
+               ret = kobject_uevent(&mem->kobj, KOBJ_ADD);
+               if (ret)
+                       goto err_kobj;
+       }
+
+       return 0;
+
+err_kobj:
+       for (; i >= 0; i--) {
+               mem = &dsp_mem->mem[i];
+               kobject_put(&mem->kobj);
+       }
+       return ret;
+}
+
+static void keystone_dsp_mem_del_attrs(struct keystone_dsp_mem_info *dsp_mem)
+{
+       int i;
+       struct keystone_dsp_mem *mem;
+
+       for (i = 0; i < dsp_mem->num_maps; i++) {
+               mem = &dsp_mem->mem[i];
+               kobject_put(&mem->kobj);
+       }
+}
+
+static int keystone_dsp_mem_check_addr(struct keystone_dsp_mem_info *dsp_mem,
+                                      int mask, size_t size)
+{
+       size_t req_offset;
+       u32 index;
+
+       index = mask & KEYSTONE_DSP_MEM_MAP_INDEX_MASK;
+       if (index >= dsp_mem->num_maps) {
+               pr_err("%s: invalid mmap region index %d\n", __func__, index);
+               return -EINVAL;
+       }
+
+       req_offset = (mask - index) << PAGE_SHIFT;
+       if (req_offset + size < req_offset) {
+               pr_err("%s: invalid request - overflow, mmap offset = 0x%zx size 0x%zx region %d\n",
+                      __func__, req_offset, size, index);
+               return -EINVAL;
+       }
+
+       if ((req_offset + size) > dsp_mem->mem[index].size) {
+               pr_err("%s: invalid request - out of range, mmap offset 0x%zx size 0x%zx region %d\n",
+                      __func__, req_offset, size, index);
+               return -EINVAL;
+       }
+
+       return index;
+}
+
+/*
+ * This is a custom mmap function following semantics based on the UIO
+ * mmap implementation. The vm_pgoff passed in the vma structure is a
+ * combination of the memory region index and the actual page offset in
+ * that region. This checks if user request is in valid range before
+ * providing mmap access.
+ *
+ * XXX: Evaluate this approach, as the MSMC memory can be mapped in whole
+ * into userspace as it is not super-large, and the allowable kernel
+ * unmapped DDR memory can be mmaped using traditional mmap semantics.
+ */
+static int keystone_dsp_mem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       size_t size = vma->vm_end - vma->vm_start;
+       struct miscdevice *misc = file->private_data;
+       struct keystone_dsp_mem_info *dsp_mem = to_dsp_mem_info(misc);
+       int index;
+
+       index = keystone_dsp_mem_check_addr(dsp_mem, vma->vm_pgoff, size);
+       if (index < 0)
+               return index;
+
+       vma->vm_page_prot =
+               phys_mem_access_prot(file,
+                                    (dsp_mem->mem[index].addr >> PAGE_SHIFT) +
+                                    (vma->vm_pgoff - index), size,
+                                    vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           (dsp_mem->mem[index].addr >> PAGE_SHIFT) +
+                           (vma->vm_pgoff - index), size, vma->vm_page_prot))
+               return -EAGAIN;
+
+       return 0;
+}
+
+static const struct file_operations keystone_dsp_mem_fops = {
+       .owner  = THIS_MODULE,
+       .mmap   = keystone_dsp_mem_mmap,
+};
+
+static int keystone_dsp_mem_parse(struct device_node *np, int index)
+{
+       phys_addr_t start, end, addr, size;
+       struct resource res;
+       resource_size_t rsize;
+       int ret, j;
+
+       if (!of_find_property(np, "no-map", NULL)) {
+               pr_err("dsp reserved memory regions without no-map are not supported\n");
+               return -EINVAL;
+       }
+
+       ret = of_address_to_resource(np, 0, &res);
+       if (ret)
+               return ret;
+
+       /* make sure only aliased addresses are covered */
+       rsize = resource_size(&res);
+       start = res.start;
+       end = res.start + rsize;
+       if (start < KEYSTONE_HIGH_PHYS_START ||
+           start >= KEYSTONE_HIGH_PHYS_LIMIT ||
+           end > KEYSTONE_HIGH_PHYS_LIMIT) {
+               pr_err("invalid address/size for keystone dsp memory carveout: %pa of size %pa\n",
+                      &start, &rsize);
+               return -EINVAL;
+       }
+
+       /* check for overlaps */
+       start = to_alias_addr(start);
+       end = to_alias_addr(end);
+       for (j = 0; j < index; j++) {
+               addr = dsp_mem->mem[j].addr;
+               size = dsp_mem->mem[j].size;
+               if ((end > addr && end <= addr + size) ||
+                   (start >= addr && start < addr + size) ||
+                   (start < addr && end > addr + size)) {
+                       pr_err("dsp memory carveout (%pa of size %pa) overlaps with (%pa of size %pa)\n",
+                              &start, &rsize, &addr, &size);
+                       return -EINVAL;
+               }
+       }
+
+       dsp_mem->mem[index].addr = to_alias_addr(res.start);
+       dsp_mem->mem[index].size = resource_size(&res);
+
+       return 0;
+}
+
+static int keystone_dsp_mem_init(void)
+{
+       struct miscdevice *misc;
+       struct resource res;
+       struct device_node *rmem_np, *sram_np, *np;
+       int ret, i = 0;
+       int num_maps = 0, num_sram = 0;
+
+       if (!of_have_populated_dt())
+               return -ENOTSUPP;
+
+       /* module is supported only on TI Keystone SoCs */
+       if (!of_machine_is_compatible("ti,keystone"))
+               return -ENOTSUPP;
+
+       /* count the number of DDR regions */
+       rmem_np = of_find_node_by_path("/reserved-memory");
+       if (rmem_np) {
+               for_each_available_child_of_node(rmem_np, np) {
+                       if (of_device_is_compatible(np,
+                                                   "ti,keystone-dsp-mem-pool"))
+                               num_maps++;
+               }
+       }
+
+       for_each_compatible_node(sram_np, NULL, "ti,keystone-dsp-msm-ram") {
+               if (!of_device_is_available(sram_np))
+                       continue;
+               num_sram++;
+       }
+
+       if ((!num_maps && !num_sram) ||
+           (num_maps + num_sram > KEYSTONE_DSP_MEM_MAP_INDEX_MASK)) {
+               ret = -EINVAL;
+               goto put_rmem;
+       }
+
+       dsp_mem = kzalloc(sizeof(*dsp_mem), GFP_KERNEL);
+       if (!dsp_mem) {
+               ret = -ENOMEM;
+               goto put_rmem;
+       }
+
+       dsp_mem->mem = kcalloc(num_maps + num_sram, sizeof(*dsp_mem->mem),
+                              GFP_KERNEL);
+       if (!dsp_mem->mem) {
+               ret = -ENOMEM;
+               goto free_dsp;
+       }
+
+       /* handle reserved-memory carveouts */
+       if (num_maps) {
+               for_each_available_child_of_node(rmem_np, np) {
+                       if (!of_device_is_compatible(np,
+                                               "ti,keystone-dsp-mem-pool"))
+                               continue;
+
+                       ret = keystone_dsp_mem_parse(np, i);
+                       if (ret) {
+                               of_node_put(np);
+                               goto free_mem;
+                       }
+                       i++;
+                       dsp_mem->num_maps++;
+               }
+       }
+
+       /* handle on-chip SRAM reserved regions */
+       if (num_sram) {
+               for_each_compatible_node(sram_np, NULL,
+                                        "ti,keystone-dsp-msm-ram") {
+                       if (!of_device_is_available(sram_np))
+                               continue;
+
+                       ret = of_address_to_resource(sram_np, 0, &res);
+                       if (ret) {
+                               ret = -EINVAL;
+                               of_node_put(sram_np);
+                               goto free_mem;
+                       }
+                       dsp_mem->mem[i].addr = res.start;
+                       dsp_mem->mem[i].size = resource_size(&res);
+                       i++;
+                       dsp_mem->num_maps++;
+               }
+       }
+
+       misc = &dsp_mem->misc;
+       misc->minor = MISC_DYNAMIC_MINOR;
+       misc->name = "dspmem";
+       misc->fops = &keystone_dsp_mem_fops;
+       misc->parent = NULL;
+       ret = misc_register(misc);
+       if (ret) {
+               pr_err("%s: could not register dspmem misc device\n", __func__);
+               goto free_mem;
+       }
+
+       ret = keystone_dsp_mem_add_attrs(dsp_mem);
+       if (ret) {
+               pr_err("%s: error creating sysfs files (%d)\n", __func__, ret);
+               goto unregister_misc;
+       }
+       of_node_put(rmem_np);
+
+       pr_info("registered dspmem misc device\n");
+
+       return 0;
+
+unregister_misc:
+       misc_deregister(&dsp_mem->misc);
+free_mem:
+       kfree(dsp_mem->mem);
+free_dsp:
+       kfree(dsp_mem);
+       dsp_mem = NULL;
+put_rmem:
+       of_node_put(rmem_np);
+       return ret;
+}
+
+static void keystone_dsp_mem_exit(void)
+{
+       keystone_dsp_mem_del_attrs(dsp_mem);
+
+       misc_deregister(&dsp_mem->misc);
+
+       kfree(dsp_mem->mem);
+       kfree(dsp_mem);
+       dsp_mem = NULL;
+}
+
+module_init(keystone_dsp_mem_init);
+module_exit(keystone_dsp_mem_exit);
+
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI Keystone DSP Memory Mapping Driver");
diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c
new file mode 100644 (file)
index 0000000..f78dbf0
--- /dev/null
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PRU-ICSS platform driver for various TI SoCs
+ *
+ * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *     Suman Anna <s-anna@ti.com>
+ *     Andrew F. Davis <afd@ti.com>
+ *     Tero Kristo <t-kristo@ti.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pruss_driver.h>
+#include <linux/regmap.h>
+#include <linux/remoteproc.h>
+
+/**
+ * struct pruss_private_data - PRUSS driver private data
+ * @has_no_sharedram: flag to indicate the absence of PRUSS Shared Data RAM
+ */
+struct pruss_private_data {
+       bool has_no_sharedram;
+};
+
+/**
+ * struct pruss_match_private_data - private data to handle multiple instances
+ * @device_name: device name of the PRUSS instance
+ * @priv_data: PRUSS driver private data for this PRUSS instance
+ */
+struct pruss_match_private_data {
+       const char *device_name;
+       const struct pruss_private_data *priv_data;
+};
+
+/**
+ * pruss_get() - get the pruss for a given PRU remoteproc
+ * @rproc: remoteproc handle of a PRU instance
+ *
+ * Finds the parent pruss device for a PRU given the @rproc handle of the
+ * PRU remote processor. This function increments the pruss device's refcount,
+ * so always use pruss_put() to decrement it back once pruss isn't needed
+ * anymore.
+ *
+ * Returns the pruss handle on success, and an ERR_PTR on failure using one
+ * of the following error values
+ *    -EINVAL if invalid parameter
+ *    -ENODEV if PRU device or PRUSS device is not found
+ */
+struct pruss *pruss_get(struct rproc *rproc)
+{
+       struct pruss *pruss;
+       struct device *dev;
+       struct platform_device *ppdev;
+
+       if (IS_ERR_OR_NULL(rproc))
+               return ERR_PTR(-EINVAL);
+
+       dev = &rproc->dev;
+       if (!dev->parent)
+               return ERR_PTR(-ENODEV);
+
+       /* rudimentary check to make sure rproc handle is for a PRU */
+       if (!strstr(dev_name(dev->parent), "pru"))
+               return ERR_PTR(-ENODEV);
+
+       ppdev = to_platform_device(dev->parent->parent);
+       pruss = platform_get_drvdata(ppdev);
+       if (!pruss)
+               return ERR_PTR(-ENODEV);
+
+       get_device(pruss->dev);
+
+       return pruss;
+}
+EXPORT_SYMBOL_GPL(pruss_get);
+
+/**
+ * pruss_put() - decrement pruss device's usecount
+ * @pruss: pruss handle
+ *
+ * Complimentary function for pruss_get(). Needs to be called
+ * after the PRUSS is used, and only if the pruss_get() succeeds.
+ */
+void pruss_put(struct pruss *pruss)
+{
+       if (IS_ERR_OR_NULL(pruss))
+               return;
+
+       put_device(pruss->dev);
+}
+EXPORT_SYMBOL_GPL(pruss_put);
+
+/**
+ * pruss_request_mem_region() - request a memory resource
+ * @pruss: the pruss instance
+ * @mem_id: the memory resource id
+ * @region: pointer to memory region structure to be filled in
+ *
+ * This function allows a client driver to request a memory resource,
+ * and if successful, will let the client driver own the particular
+ * memory region until released using the pruss_release_mem_region()
+ * API.
+ *
+ * Returns the memory region if requested resource is available, an
+ * error otherwise
+ */
+int pruss_request_mem_region(struct pruss *pruss, enum pruss_mem mem_id,
+                            struct pruss_mem_region *region)
+{
+       if (!pruss || !region)
+               return -EINVAL;
+
+       if (mem_id >= PRUSS_MEM_MAX)
+               return -EINVAL;
+
+       mutex_lock(&pruss->lock);
+
+       if (pruss->mem_in_use[mem_id]) {
+               mutex_unlock(&pruss->lock);
+               return -EBUSY;
+       }
+
+       *region = pruss->mem_regions[mem_id];
+       pruss->mem_in_use[mem_id] = region;
+
+       mutex_unlock(&pruss->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pruss_request_mem_region);
+
+/**
+ * pruss_release_mem_region() - release a memory resource
+ * @pruss: the pruss instance
+ * @region: the memory region to release
+ *
+ * This function is the complimentary function to
+ * pruss_request_mem_region(), and allows the client drivers to
+ * release back a memory resource.
+ *
+ * Returns 0 on success, an error code otherwise
+ */
+int pruss_release_mem_region(struct pruss *pruss,
+                            struct pruss_mem_region *region)
+{
+       int id;
+
+       if (!pruss || !region)
+               return -EINVAL;
+
+       mutex_lock(&pruss->lock);
+
+       /* find out the memory region being released */
+       for (id = 0; id < PRUSS_MEM_MAX; id++) {
+               if (pruss->mem_in_use[id] == region)
+                       break;
+       }
+
+       if (id == PRUSS_MEM_MAX) {
+               mutex_unlock(&pruss->lock);
+               return -EINVAL;
+       }
+
+       pruss->mem_in_use[id] = NULL;
+
+       mutex_unlock(&pruss->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pruss_release_mem_region);
+
+/**
+ * pruss_regmap_read() - read a PRUSS syscon sub-module register
+ * @pruss: the pruss instance handle
+ * @mod: the pruss syscon sub-module identifier
+ * @reg: register offset within the sub-module
+ * @val: pointer to return the value in
+ *
+ * Reads a given register within one of the PRUSS sub-modules represented
+ * by a syscon and returns it through the passed-in @val pointer
+ *
+ * Returns 0 on success, or an error code otherwise
+ */
+int pruss_regmap_read(struct pruss *pruss, enum pruss_syscon mod,
+                     unsigned int reg, unsigned int *val)
+{
+       struct regmap *map;
+
+       if (IS_ERR_OR_NULL(pruss) || mod < PRUSS_SYSCON_CFG ||
+           mod >= PRUSS_SYSCON_MAX)
+               return -EINVAL;
+
+       map = (mod == PRUSS_SYSCON_CFG) ? pruss->cfg :
+              ((mod == PRUSS_SYSCON_IEP ? pruss->iep : pruss->mii_rt));
+
+       return regmap_read(map, reg, val);
+}
+EXPORT_SYMBOL_GPL(pruss_regmap_read);
+
+/**
+ * pruss_regmap_update() - configure a PRUSS syscon sub-module register
+ * @pruss: the pruss instance handle
+ * @mod: the pruss syscon sub-module identifier
+ * @reg: register offset within the sub-module
+ * @mask: bit mask to use for programming the @val
+ * @val: value to write
+ *
+ * Programs a given register within one of the PRUSS sub-modules represented
+ * by a syscon
+ *
+ * Returns 0 on success, or an error code otherwise
+ */
+int pruss_regmap_update(struct pruss *pruss, enum pruss_syscon mod,
+                       unsigned int reg, unsigned int mask, unsigned int val)
+{
+       struct regmap *map;
+
+       if (IS_ERR_OR_NULL(pruss) || mod < PRUSS_SYSCON_CFG ||
+           mod >= PRUSS_SYSCON_MAX)
+               return -EINVAL;
+
+       map = (mod == PRUSS_SYSCON_CFG) ? pruss->cfg :
+              ((mod == PRUSS_SYSCON_IEP ? pruss->iep : pruss->mii_rt));
+
+       return regmap_update_bits(map, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(pruss_regmap_update);
+
+static const
+struct pruss_private_data *pruss_get_private_data(struct platform_device *pdev)
+{
+       const struct pruss_match_private_data *data;
+
+       if (!of_device_is_compatible(pdev->dev.of_node, "ti,am4376-pruss"))
+               return NULL;
+
+       data = of_device_get_match_data(&pdev->dev);
+       for (; data && data->device_name; data++) {
+               if (!strcmp(dev_name(&pdev->dev), data->device_name))
+                       return data->priv_data;
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+
+static int pruss_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct device_node *np;
+       struct pruss *pruss;
+       struct resource res;
+       int ret, i, index;
+       const struct pruss_private_data *data;
+       const char *mem_names[PRUSS_MEM_MAX] = { "dram0", "dram1", "shrdram2" };
+
+       if (!node) {
+               dev_err(dev, "Non-DT platform device not supported\n");
+               return -ENODEV;
+       }
+
+       data = pruss_get_private_data(pdev);
+       if (IS_ERR(data)) {
+               dev_err(dev, "missing private data\n");
+               return -ENODEV;
+       }
+
+       ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+       if (ret) {
+               dev_err(dev, "dma_set_coherent_mask: %d\n", ret);
+               return ret;
+       }
+
+       pruss = devm_kzalloc(dev, sizeof(*pruss), GFP_KERNEL);
+       if (!pruss)
+               return -ENOMEM;
+
+       pruss->dev = dev;
+       mutex_init(&pruss->lock);
+
+       np = of_get_child_by_name(node, "cfg");
+       if (!np) {
+               dev_err(dev, "%pOF is missing cfg node\n", np);
+               return -ENODEV;
+       }
+
+       pruss->cfg = syscon_node_to_regmap(np);
+       of_node_put(np);
+       if (IS_ERR(pruss->cfg))
+               return -ENODEV;
+
+       np = of_get_child_by_name(node, "iep");
+       if (!np) {
+               dev_err(dev, "%pOF is missing iep node\n", np);
+               return -ENODEV;
+       }
+
+       pruss->iep = syscon_node_to_regmap(np);
+       of_node_put(np);
+       if (IS_ERR(pruss->iep))
+               return -ENODEV;
+
+       np = of_get_child_by_name(node, "mii-rt");
+       if (!np) {
+               dev_err(dev, "%pOF is missing mii-rt node\n", np);
+               return -ENODEV;
+       }
+
+       pruss->mii_rt = syscon_node_to_regmap(np);
+       of_node_put(np);
+       if (IS_ERR(pruss->mii_rt))
+               return -ENODEV;
+
+       np = of_get_child_by_name(node, "memories");
+       if (!np) {
+               dev_err(dev, "%pOF is missing memories node\n", np);
+               return -ENODEV;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
+               if (data && data->has_no_sharedram &&
+                   !strcmp(mem_names[i], "shrdram2"))
+                       continue;
+
+               index = of_property_match_string(np, "reg-names", mem_names[i]);
+               if (index < 0) {
+                       of_node_put(np);
+                       return index;
+               }
+
+               if (of_address_to_resource(np, index, &res)) {
+                       of_node_put(np);
+                       return -EINVAL;
+               }
+
+               pruss->mem_regions[i].va = devm_ioremap(dev, res.start,
+                                                       resource_size(&res));
+               if (!pruss->mem_regions[i].va) {
+                       dev_err(dev, "failed to parse and map memory resource %d %s\n",
+                               i, mem_names[i]);
+                       of_node_put(np);
+                       return -ENOMEM;
+               }
+               pruss->mem_regions[i].pa = res.start;
+               pruss->mem_regions[i].size = resource_size(&res);
+
+               dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n",
+                       mem_names[i], &pruss->mem_regions[i].pa,
+                       pruss->mem_regions[i].size, pruss->mem_regions[i].va);
+       }
+       of_node_put(np);
+
+       platform_set_drvdata(pdev, pruss);
+
+       dev_dbg(&pdev->dev, "creating PRU cores and other child platform devices\n");
+       ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
+       if (ret)
+               dev_err(dev, "of_platform_populate failed\n");
+
+       return ret;
+}
+
+static int pruss_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "remove PRU cores and other child platform devices\n");
+       of_platform_depopulate(dev);
+
+       return 0;
+}
+
+/* instance-specific driver private data */
+static const struct pruss_private_data am437x_pruss1_priv_data = {
+       .has_no_sharedram = false,
+};
+
+static const struct pruss_private_data am437x_pruss0_priv_data = {
+       .has_no_sharedram = true,
+};
+
+static const struct pruss_match_private_data am437x_match_data[] = {
+       {
+               .device_name    = "54400000.pruss",
+               .priv_data      = &am437x_pruss1_priv_data,
+       },
+       {
+               .device_name    = "54440000.pruss",
+               .priv_data      = &am437x_pruss0_priv_data,
+       },
+       {
+               /* sentinel */
+       },
+};
+
+static const struct of_device_id pruss_of_match[] = {
+       { .compatible = "ti,am3356-pruss", .data = NULL, },
+       { .compatible = "ti,am4376-pruss", .data = &am437x_match_data, },
+       { .compatible = "ti,am5728-pruss", .data = NULL, },
+       { .compatible = "ti,k2g-pruss", .data = NULL, },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, pruss_of_match);
+
+static struct platform_driver pruss_driver = {
+       .driver = {
+               .name = "pruss",
+               .of_match_table = pruss_of_match,
+       },
+       .probe  = pruss_probe,
+       .remove = pruss_remove,
+};
+module_platform_driver(pruss_driver);
+
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_DESCRIPTION("PRU-ICSS Subsystem Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/ti/pruss_soc_bus.c b/drivers/soc/ti/pruss_soc_bus.c
new file mode 100644 (file)
index 0000000..47545fc
--- /dev/null
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PRU-ICSS SoC bus driver for various TI SoCs
+ *
+ * Copyright (C) 2016-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *     Suman Anna <s-anna@ti.com>
+ *     Keerthy <j-keerthy@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/platform_data/ti-pruss.h>
+
+#define SYSCFG_STANDBY_INIT    BIT(4)
+#define SYSCFG_SUB_MWAIT_READY BIT(5)
+
+#define SYSCFG_STANDBY_MODE_FORCE      (0 << 2)
+#define SYSCFG_STANDBY_MODE_NO         (1 << 2)
+#define SYSCFG_STANDBY_MODE_SMART      (2 << 2)
+#define SYSCFG_STANDBY_MODE_MASK       (3 << 2)
+
+#define SYSCFG_IDLE_MODE_FORCE         0
+#define SYSCFG_IDLE_MODE_NO            1
+#define SYSCFG_IDLE_MODE_SMART         2
+#define SYSCFG_IDLE_MODE_MASK          3
+
+/**
+ * struct pruss_soc_bus - PRUSS SoC bus structure
+ * @syscfg: kernel mapped address for SYSCFG register
+ * @in_standby: flag for storing standby status
+ * @has_reset: cached variable for storing global module reset flag
+ * @skip_syscfg: flag to indicate if PRCM master standby/slave idle is needed
+ */
+struct pruss_soc_bus {
+       void __iomem *syscfg;
+       bool in_standby;
+       bool has_reset;
+       bool skip_syscfg;
+};
+
+/**
+ * struct pruss_soc_bus_match_data - PRUSS SoC bus driver match data
+ * @has_reset: flag to indicate the presence of global module reset
+ * @uses_prcm: flag to indicate the usage of PRCM master standby/slave idle
+ *            protocol
+ */
+struct pruss_soc_bus_match_data {
+       bool has_reset;
+       bool uses_prcm;
+};
+
+static inline void pruss_soc_bus_rmw(void __iomem *reg, u32 mask, u32 set)
+{
+       u32 val;
+
+       val = readl_relaxed(reg);
+       val &= ~mask;
+       val |= (set & mask);
+       writel_relaxed(val, reg);
+}
+
+/*
+ * This function programs the PRUSS_SYSCFG.STANDBY_INIT bit to achieve dual
+ * functionalities - one is to deassert the MStandby signal to the device
+ * PRCM, and the other is to enable OCP master ports to allow accesses
+ * outside of the PRU-ICSS. The function has to wait for the PRCM to
+ * acknowledge through the monitoring of the PRUSS_SYSCFG.SUB_MWAIT bit.
+ */
+static int pruss_soc_bus_enable_ocp_master_ports(struct device *dev)
+{
+       struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
+       u32 syscfg_val, i;
+       bool ready = false;
+
+       pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_INIT, 0);
+
+       /* wait till we are ready for transactions - delay is arbitrary */
+       for (i = 0; i < 10; i++) {
+               syscfg_val = readl_relaxed(psoc_bus->syscfg);
+               ready = !(syscfg_val & SYSCFG_SUB_MWAIT_READY);
+               if (ready)
+                       break;
+               udelay(5);
+       }
+
+       if (!ready) {
+               dev_err(dev, "timeout waiting for SUB_MWAIT_READY\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused pruss_soc_bus_suspend(struct device *dev)
+{
+       struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
+       u32 syscfg_val;
+
+       if (psoc_bus->skip_syscfg)
+               return 0;
+
+       syscfg_val = readl_relaxed(psoc_bus->syscfg);
+       psoc_bus->in_standby = syscfg_val & SYSCFG_STANDBY_INIT;
+
+       /* initiate MStandby, undo the MStandby config in probe */
+       if (!psoc_bus->in_standby) {
+               pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_INIT,
+                                 SYSCFG_STANDBY_INIT);
+       }
+
+       return 0;
+}
+
+static int __maybe_unused pruss_soc_bus_resume(struct device *dev)
+{
+       struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
+       int ret = 0;
+
+       /* re-enable OCP master ports/disable MStandby */
+       if (!psoc_bus->skip_syscfg && !psoc_bus->in_standby) {
+               ret = pruss_soc_bus_enable_ocp_master_ports(dev);
+               if (ret)
+                       dev_err(dev, "%s failed\n", __func__);
+       }
+
+       return ret;
+}
+
+/* firmware must be idle when calling this function */
+static void pruss_disable_module(struct device *dev)
+{
+       struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
+
+       if (psoc_bus->skip_syscfg)
+               goto put_sync;
+
+       /* configure Smart Standby */
+       pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_MODE_MASK,
+                         SYSCFG_STANDBY_MODE_SMART);
+
+       /* initiate MStandby */
+       pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_INIT,
+                         SYSCFG_STANDBY_INIT);
+
+put_sync:
+       /* initiate IDLE request, disable clocks */
+       pm_runtime_put_sync(dev);
+}
+
+static int pruss_enable_module(struct device *dev)
+{
+       struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
+       int ret;
+
+       /* enable clocks, de-assert IDLE request */
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               pm_runtime_put_noidle(dev);
+               return ret;
+       }
+
+       if (psoc_bus->skip_syscfg)
+               return ret;
+
+       /* configure for Smart Idle & Smart Standby */
+       pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_IDLE_MODE_MASK,
+                         SYSCFG_IDLE_MODE_SMART);
+       pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_MODE_MASK,
+                         SYSCFG_STANDBY_MODE_SMART);
+
+       /* enable OCP master ports/disable MStandby */
+       ret = pruss_soc_bus_enable_ocp_master_ports(dev);
+       if (ret)
+               pruss_disable_module(dev);
+
+       return ret;
+}
+
+static int pruss_soc_bus_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct pruss_platform_data *pdata = dev_get_platdata(dev);
+       struct pruss_soc_bus *psoc_bus;
+       const struct pruss_soc_bus_match_data *data;
+       int ret;
+
+       psoc_bus = devm_kzalloc(dev, sizeof(*psoc_bus), GFP_KERNEL);
+       if (!psoc_bus)
+               return -ENOMEM;
+
+       psoc_bus->syscfg = of_iomap(node, 0);
+       if (!psoc_bus->syscfg)
+               return -ENOMEM;
+
+       data = of_device_get_match_data(dev);
+       if (!data) {
+               dev_err(dev, "missing match data\n");
+               return -ENODEV;
+       }
+
+       if (data->has_reset && (!pdata || !pdata->deassert_reset ||
+                               !pdata->assert_reset || !pdata->reset_name)) {
+               dev_err(dev, "platform data (reset configuration information) missing\n");
+               return -ENODEV;
+       }
+       psoc_bus->has_reset = data->has_reset;
+       psoc_bus->skip_syscfg = !data->uses_prcm;
+       platform_set_drvdata(pdev, psoc_bus);
+
+       if (psoc_bus->has_reset) {
+               ret = pdata->deassert_reset(pdev, pdata->reset_name);
+               if (ret) {
+                       dev_err(dev, "deassert_reset failed: %d\n", ret);
+                       goto fail_reset;
+               }
+       }
+
+       pm_runtime_enable(dev);
+       ret = pruss_enable_module(dev);
+       if (ret < 0) {
+               dev_err(dev, "couldn't enable module\n");
+               goto fail_module;
+       }
+
+       ret = of_platform_populate(node, NULL, NULL, dev);
+       if (ret)
+               goto fail_of;
+
+       return 0;
+
+fail_of:
+       pruss_disable_module(dev);
+fail_module:
+       pm_runtime_disable(dev);
+       if (psoc_bus->has_reset)
+               pdata->assert_reset(pdev, pdata->reset_name);
+fail_reset:
+       iounmap(psoc_bus->syscfg);
+       return ret;
+}
+
+static int pruss_soc_bus_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct pruss_platform_data *pdata = dev_get_platdata(dev);
+       struct pruss_soc_bus *psoc_bus = platform_get_drvdata(pdev);
+
+       of_platform_depopulate(dev);
+
+       pruss_disable_module(dev);
+       pm_runtime_disable(dev);
+
+       if (psoc_bus->has_reset)
+               pdata->assert_reset(pdev, pdata->reset_name);
+       iounmap(psoc_bus->syscfg);
+
+       return 0;
+}
+
+/* instance-specific driver private data */
+static const struct pruss_soc_bus_match_data am335x_data = {
+       .has_reset = true,
+       .uses_prcm = true,
+};
+
+static const struct pruss_soc_bus_match_data am437x_data = {
+       .has_reset = true,
+       .uses_prcm = true,
+};
+
+static const struct pruss_soc_bus_match_data am57xx_data = {
+       .has_reset = false,
+       .uses_prcm = true,
+};
+
+static const struct pruss_soc_bus_match_data k2g_data = {
+       .has_reset = false,
+       .uses_prcm = false,
+};
+
+static const struct of_device_id pruss_soc_bus_of_match[] = {
+       { .compatible = "ti,am3356-pruss-soc-bus", .data = &am335x_data, },
+       { .compatible = "ti,am4376-pruss-soc-bus", .data = &am437x_data, },
+       { .compatible = "ti,am5728-pruss-soc-bus", .data = &am57xx_data, },
+       { .compatible = "ti,k2g-pruss-soc-bus", .data = &k2g_data, },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, pruss_soc_bus_of_match);
+
+static SIMPLE_DEV_PM_OPS(pruss_soc_bus_pm_ops,
+                        pruss_soc_bus_suspend, pruss_soc_bus_resume);
+
+static struct platform_driver pruss_soc_bus_driver = {
+       .driver = {
+               .name = "pruss-soc-bus",
+               .pm = &pruss_soc_bus_pm_ops,
+               .of_match_table = pruss_soc_bus_of_match,
+       },
+       .probe  = pruss_soc_bus_probe,
+       .remove = pruss_soc_bus_remove,
+};
+module_platform_driver(pruss_soc_bus_driver);
+
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_AUTHOR("Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("PRU-ICSS SoC Bus Driver for TI SoCs");
+MODULE_LICENSE("GPL v2");
index 71a1b2399c4805c8539a0da0bf592eec20a79302..55e3c007e264f2f3591283be422e6c54594e070a 100644 (file)
@@ -1,59 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Remote Processor - omap-specific bits
  *
- * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011-2019 Texas Instruments Incorporated - http://www.ti.com/
  * Copyright (C) 2011 Google, Inc.
- *
- * 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.
- *
- * 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.
  */
 
 #ifndef _PLAT_REMOTEPROC_H
 #define _PLAT_REMOTEPROC_H
 
-struct rproc_ops;
 struct platform_device;
 
 /*
  * struct omap_rproc_pdata - omap remoteproc's platform data
- * @name: the remoteproc's name
- * @oh_name: omap hwmod device
- * @oh_name_opt: optional, secondary omap hwmod device
- * @firmware: name of firmware file to load
- * @mbox_name: name of omap mailbox device to use with this rproc
- * @ops: start/stop rproc handlers
  * @device_enable: omap-specific handler for enabling a device
  * @device_shutdown: omap-specific handler for shutting down a device
- * @set_bootaddr: omap-specific handler for setting the rproc boot address
  */
 struct omap_rproc_pdata {
-       const char *name;
-       const char *oh_name;
-       const char *oh_name_opt;
-       const char *firmware;
-       const char *mbox_name;
-       const struct rproc_ops *ops;
        int (*device_enable)(struct platform_device *pdev);
        int (*device_shutdown)(struct platform_device *pdev);
-       void (*set_bootaddr)(u32);
 };
 
-#if defined(CONFIG_OMAP_REMOTEPROC) || defined(CONFIG_OMAP_REMOTEPROC_MODULE)
-
-void __init omap_rproc_reserve_cma(void);
-
-#else
-
-static inline void __init omap_rproc_reserve_cma(void)
-{
-}
-
-#endif
-
 #endif /* _PLAT_REMOTEPROC_H */
diff --git a/include/linux/platform_data/ti-pruss.h b/include/linux/platform_data/ti-pruss.h
new file mode 100644 (file)
index 0000000..463312a
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Platform data for PRUSS on TI SoCs
+ *
+ * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef _PLAT_TI_PRUSS_H
+#define _PLAT_TI_PRUSS_H
+
+struct platform_device;
+
+/**
+ * struct pruss_platform_data - PRUSS platform data
+ * @reset_name: name of the reset
+ * @assert_reset: PRU-specific handler for putting the device in reset
+ * @deassert_reset: PRU-specific handler for releasing the device from reset
+ */
+struct pruss_platform_data {
+       const char *reset_name;
+       int (*assert_reset)(struct platform_device *pdev, const char *name);
+       int (*deassert_reset)(struct platform_device *pdev, const char *name);
+};
+
+#endif /* _PLAT_TI_PRUSS_H */
diff --git a/include/linux/pruss.h b/include/linux/pruss.h
new file mode 100644 (file)
index 0000000..a50eece
--- /dev/null
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/**
+ * PRU-ICSS Subsystem user interfaces
+ *
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com
+ *     Suman Anna <s-anna@ti.com>
+ *     Tero Kristo <t-kristo@ti.com>
+ */
+
+#ifndef __LINUX_PRUSS_H
+#define __LINUX_PRUSS_H
+
+/*
+ * PRU_ICSS_CFG registers
+ * SYSCFG, ISRP, ISP, IESP, IECP, SCRP applicable on AMxxxx devices only
+ */
+#define PRUSS_CFG_REVID                0x00
+#define PRUSS_CFG_SYSCFG       0x04
+#define PRUSS_CFG_GPCFG(x)     (0x08 + (x) * 4)
+#define PRUSS_CFG_CGR          0x10
+#define PRUSS_CFG_ISRP         0x14
+#define PRUSS_CFG_ISP          0x18
+#define PRUSS_CFG_IESP         0x1C
+#define PRUSS_CFG_IECP         0x20
+#define PRUSS_CFG_SCRP         0x24
+#define PRUSS_CFG_PMAO         0x28
+#define PRUSS_CFG_MII_RT       0x2C
+#define PRUSS_CFG_IEPCLK       0x30
+#define PRUSS_CFG_SPP          0x34
+#define PRUSS_CFG_PIN_MX       0x40
+
+/* PRUSS_GPCFG register bits */
+#define PRUSS_GPCFG_PRU_GPO_SH_SEL             BIT(25)
+
+#define PRUSS_GPCFG_PRU_DIV1_SHIFT             20
+#define PRUSS_GPCFG_PRU_DIV1_MASK              GENMASK(24, 20)
+
+#define PRUSS_GPCFG_PRU_DIV0_SHIFT             15
+#define PRUSS_GPCFG_PRU_DIV0_MASK              GENMASK(15, 19)
+
+#define PRUSS_GPCFG_PRU_GPO_MODE               BIT(14)
+#define PRUSS_GPCFG_PRU_GPO_MODE_DIRECT                0
+#define PRUSS_GPCFG_PRU_GPO_MODE_SERIAL                BIT(14)
+
+#define PRUSS_GPCFG_PRU_GPI_SB                 BIT(13)
+
+#define PRUSS_GPCFG_PRU_GPI_DIV1_SHIFT         8
+#define PRUSS_GPCFG_PRU_GPI_DIV1_MASK          GENMASK(12, 8)
+
+#define PRUSS_GPCFG_PRU_GPI_DIV0_SHIFT         3
+#define PRUSS_GPCFG_PRU_GPI_DIV0_MASK          GENMASK(7, 3)
+
+#define PRUSS_GPCFG_PRU_GPI_CLK_MODE_POSITIVE  0
+#define PRUSS_GPCFG_PRU_GPI_CLK_MODE_NEGATIVE  BIT(2)
+#define PRUSS_GPCFG_PRU_GPI_CLK_MODE           BIT(2)
+
+#define PRUSS_GPCFG_PRU_GPI_MODE_MASK          GENMASK(1, 0)
+#define PRUSS_GPCFG_PRU_GPI_MODE_SHIFT         0
+
+#define PRUSS_GPCFG_PRU_MUX_SEL_SHIFT          26
+#define PRUSS_GPCFG_PRU_MUX_SEL_MASK           GENMASK(29, 26)
+
+/* PRUSS_MII_RT register bits */
+#define PRUSS_MII_RT_EVENT_EN                  BIT(0)
+
+/* PRUSS_SPP register bits */
+#define PRUSS_SPP_XFER_SHIFT_EN                        BIT(1)
+#define PRUSS_SPP_PRU1_PAD_HP_EN               BIT(0)
+
+/**
+ * enum pruss_gp_mux_sel - PRUSS GPI/O Mux modes for the
+ * PRUSS_GPCFG0/1 registers
+ *
+ * NOTE: The below defines are the most common values, but there
+ * are some exceptions like on 66AK2G, where the RESERVED and MII2
+ * values are interchanged. Also, this bit-field does not exist on
+ * AM335x SoCs
+ */
+enum pruss_gp_mux_sel {
+       PRUSS_GP_MUX_SEL_GP = 0,
+       PRUSS_GP_MUX_SEL_ENDAT,
+       PRUSS_GP_MUX_SEL_RESERVED,
+       PRUSS_GP_MUX_SEL_SD,
+       PRUSS_GP_MUX_SEL_MII2,
+       PRUSS_GP_MUX_SEL_MAX,
+};
+
+/**
+ * enum pruss_gpi_mode - PRUSS GPI configuration modes, used
+ *                      to program the PRUSS_GPCFG0/1 registers
+ */
+enum pruss_gpi_mode {
+       PRUSS_GPI_MODE_DIRECT = 0,
+       PRUSS_GPI_MODE_PARALLEL,
+       PRUSS_GPI_MODE_28BIT_SHIFT,
+       PRUSS_GPI_MODE_MII,
+};
+
+/**
+ * enum pruss_syscon - PRUSS sub-module syscon identifiers
+ */
+enum pruss_syscon {
+       PRUSS_SYSCON_CFG = 0,
+       PRUSS_SYSCON_IEP,
+       PRUSS_SYSCON_MII_RT,
+       PRUSS_SYSCON_MAX,
+};
+
+/**
+ * enum pruss_pru_id - PRU core identifiers
+ */
+enum pruss_pru_id {
+       PRUSS_PRU0 = 0,
+       PRUSS_PRU1,
+       PRUSS_NUM_PRUS,
+};
+
+/**
+ * enum pru_ctable_idx - Configurable Constant table index identifiers
+ */
+enum pru_ctable_idx {
+       PRU_C24 = 0,
+       PRU_C25,
+       PRU_C26,
+       PRU_C27,
+       PRU_C28,
+       PRU_C29,
+       PRU_C30,
+       PRU_C31,
+};
+
+/**
+ * enum pruss_mem - PRUSS memory range identifiers
+ */
+enum pruss_mem {
+       PRUSS_MEM_DRAM0 = 0,
+       PRUSS_MEM_DRAM1,
+       PRUSS_MEM_SHRD_RAM2,
+       PRUSS_MEM_MAX,
+};
+
+/**
+ * struct pruss_mem_region - PRUSS memory region structure
+ * @va: kernel virtual address of the PRUSS memory region
+ * @pa: physical (bus) address of the PRUSS memory region
+ * @size: size of the PRUSS memory region
+ */
+struct pruss_mem_region {
+       void __iomem *va;
+       phys_addr_t pa;
+       size_t size;
+};
+
+struct rproc;
+struct pruss;
+
+#if IS_ENABLED(CONFIG_TI_PRUSS)
+
+struct pruss *pruss_get(struct rproc *rproc);
+void pruss_put(struct pruss *pruss);
+int pruss_request_mem_region(struct pruss *pruss, enum pruss_mem mem_id,
+                            struct pruss_mem_region *region);
+int pruss_release_mem_region(struct pruss *pruss,
+                            struct pruss_mem_region *region);
+int pruss_regmap_read(struct pruss *pruss, enum pruss_syscon mod,
+                     unsigned int reg, unsigned int *val);
+int pruss_regmap_update(struct pruss *pruss, enum pruss_syscon mod,
+                       unsigned int reg, unsigned int mask, unsigned int val);
+int pruss_intc_trigger(unsigned int irq);
+
+#else
+
+static inline struct pruss *pruss_get(struct rproc *rproc)
+{
+       return ERR_PTR(-ENOTSUPP);
+}
+
+static inline void pruss_put(struct pruss *pruss) { }
+
+static inline int pruss_request_mem_region(struct pruss *pruss,
+                                          enum pruss_mem mem_id,
+                                          struct pruss_mem_region *region)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pruss_release_mem_region(struct pruss *pruss,
+                                          struct pruss_mem_region *region)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pruss_regmap_read(struct pruss *pruss, enum pruss_syscon mod,
+                                   unsigned int reg, unsigned int *val)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pruss_regmap_update(struct pruss *pruss,
+                                     enum pruss_syscon mod, unsigned int reg,
+                                     unsigned int mask, unsigned int val)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pruss_intc_trigger(unsigned int irq)
+{
+       return -ENOTSUPP;
+}
+
+#endif /* CONFIG_TI_PRUSS */
+
+#if IS_ENABLED(CONFIG_PRU_REMOTEPROC)
+
+struct rproc *pru_rproc_get(struct device_node *node, int index);
+void pru_rproc_put(struct rproc *rproc);
+enum pruss_pru_id pru_rproc_get_id(struct rproc *rproc);
+int pru_rproc_set_ctable(struct rproc *rproc, enum pru_ctable_idx c, u32 addr);
+
+#else
+
+static inline struct rproc *pru_rproc_get(struct device_node *node, int index)
+{
+       return ERR_PTR(-ENOTSUPP);
+}
+
+static inline void pru_rproc_put(struct rproc *rproc) { }
+
+static inline enum pruss_pru_id pru_rproc_get_id(struct rproc *rproc)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pru_rproc_set_ctable(struct rproc *rproc,
+                                      enum pru_ctable_idx c, u32 addr)
+{
+       return -ENOTSUPP;
+}
+
+#endif /* CONFIG_PRU_REMOTEPROC */
+
+/**
+ * pruss_cfg_gpimode() - set the GPI mode of the PRU
+ * @pruss: the pruss instance handle
+ * @pru: the rproc instance handle of the PRU
+ * @mode: GPI mode to set
+ *
+ * Sets the GPI mode for a given PRU by programming the
+ * corresponding PRUSS_CFG_GPCFGx register
+ *
+ * Returns 0 on success, or an error code otherwise
+ */
+static inline int pruss_cfg_gpimode(struct pruss *pruss, struct rproc *pru,
+                                   enum pruss_gpi_mode mode)
+{
+       enum pruss_pru_id id = pru_rproc_get_id(pru);
+
+       if (id < 0)
+               return -EINVAL;
+
+       return pruss_regmap_update(pruss, PRUSS_SYSCON_CFG, PRUSS_CFG_GPCFG(id),
+                                  PRUSS_GPCFG_PRU_GPI_MODE_MASK,
+                                  mode << PRUSS_GPCFG_PRU_GPI_MODE_SHIFT);
+}
+
+/**
+ * pruss_cfg_miirt_enable() - Enable/disable MII RT Events
+ * @pruss: the pruss instance
+ * @enable: enable/disable
+ *
+ * Enable/disable the MII RT Events for the PRUSS.
+ */
+static inline int pruss_cfg_miirt_enable(struct pruss *pruss, bool enable)
+{
+       u32 set = enable ? PRUSS_MII_RT_EVENT_EN : 0;
+
+       return pruss_regmap_update(pruss, PRUSS_SYSCON_CFG, PRUSS_CFG_MII_RT,
+                                  PRUSS_MII_RT_EVENT_EN, set);
+}
+
+/**
+ * pruss_cfg_xfr_enable() - Enable/disable XIN XOUT shift functionality
+ * @pruss: the pruss instance
+ * @enable: enable/disable
+ */
+static inline int pruss_cfg_xfr_enable(struct pruss *pruss, bool enable)
+{
+       u32 set = enable ? PRUSS_SPP_XFER_SHIFT_EN : 0;
+
+       return pruss_regmap_update(pruss, PRUSS_SYSCON_CFG, PRUSS_CFG_SPP,
+                                  PRUSS_SPP_XFER_SHIFT_EN, set);
+}
+
+#endif /* __LINUX_PRUSS_H */
diff --git a/include/linux/pruss_driver.h b/include/linux/pruss_driver.h
new file mode 100644 (file)
index 0000000..8a691da
--- /dev/null
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PRU-ICSS sub-system specific definitions
+ *
+ * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *     Suman Anna <s-anna@ti.com>
+ */
+
+#ifndef _PRUSS_DRIVER_H_
+#define _PRUSS_DRIVER_H_
+
+#include <linux/pruss.h>
+
+/* maximum number of system events */
+#define MAX_PRU_SYS_EVENTS     64
+
+/* maximum number of interrupt channels */
+#define MAX_PRU_CHANNELS       10
+
+/* minimum starting host interrupt number for MPU */
+#define MIN_PRU_HOST_INT       2
+
+/* maximum number of host interrupts */
+#define MAX_PRU_HOST_INT       10
+
+/**
+ * struct pruss_intc_config - INTC configuration info
+ * @sysev_to_ch: system events to channel mapping information
+ * @ch_to_host: interrupt channel to host interrupt information
+ */
+struct pruss_intc_config {
+       s8 sysev_to_ch[MAX_PRU_SYS_EVENTS];
+       s8 ch_to_host[MAX_PRU_CHANNELS];
+};
+
+/**
+ * struct pruss - PRUSS parent structure
+ * @dev: pruss device pointer
+ * @cfg: regmap for config region
+ * @iep: regmap for IEP sub-module
+ * @mii_rt: regmap for MII_RT sub-module
+ * @mem_regions: data for each of the PRUSS memory regions
+ * @mem_in_use: to indicate if memory resource is in use
+ * @lock: mutex to serialize access to resources
+ */
+struct pruss {
+       struct device *dev;
+       struct regmap *cfg;
+       struct regmap *iep;
+       struct regmap *mii_rt;
+       struct pruss_mem_region mem_regions[PRUSS_MEM_MAX];
+       struct pruss_mem_region *mem_in_use[PRUSS_MEM_MAX];
+       struct mutex lock; /* PRU resource lock */
+};
+
+int pruss_intc_configure(struct pruss *pruss,
+                        struct pruss_intc_config *intc_config);
+int pruss_intc_unconfigure(struct pruss *pruss,
+                          struct pruss_intc_config *intc_config);
+
+/**
+ * pruss_cfg_get_gpmux() - get the current GPMUX value for a PRU device
+ * @pruss: pruss instance
+ * @id: PRU identifier (0-1)
+ * @mux: pointer to store the current mux value into
+ */
+static inline int pruss_cfg_get_gpmux(struct pruss *pruss,
+                                     enum pruss_pru_id id, u8 *mux)
+{
+       int ret = 0;
+       u32 val;
+
+       ret = pruss_regmap_read(pruss, PRUSS_SYSCON_CFG,
+                               PRUSS_CFG_GPCFG(id), &val);
+       if (!ret)
+               *mux = (u8)((val & PRUSS_GPCFG_PRU_MUX_SEL_MASK) >>
+                           PRUSS_GPCFG_PRU_MUX_SEL_SHIFT);
+       return ret;
+}
+
+/**
+ * pruss_cfg_set_gpmux() - set the GPMUX value for a PRU device
+ * @pruss: pruss instance
+ * @pru_id: PRU identifier (0-1)
+ * @mux: new mux value for PRU
+ */
+static inline int pruss_cfg_set_gpmux(struct pruss *pruss,
+                                     enum pruss_pru_id id, u8 mux)
+{
+       if (mux >= PRUSS_GP_MUX_SEL_MAX)
+               return -EINVAL;
+
+       return pruss_regmap_update(pruss, PRUSS_SYSCON_CFG, PRUSS_CFG_GPCFG(id),
+                                  PRUSS_GPCFG_PRU_MUX_SEL_MASK,
+                                 (u32)mux << PRUSS_GPCFG_PRU_MUX_SEL_SHIFT);
+}
+
+#endif /* _PRUSS_DRIVER_H_ */
index e3c5d856b6da52f367873271db0e436d5b371284..e0baae1659c2ac3a20753b7db9e652bad99f29bb 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/completion.h>
 #include <linux/idr.h>
 #include <linux/of.h>
+#include <linux/bitops.h>
 
 /**
  * struct resource_table - firmware resource table header
@@ -100,6 +101,10 @@ struct fw_rsc_hdr {
  *                 the remote processor will be writing logs.
  * @RSC_VDEV:       declare support for a virtio device, and serve as its
  *                 virtio header.
+ * @RSC_PRELOAD_VENDOR: a vendor resource type that needs to be handled by
+ *                 remoteproc implementations before loading
+ * @RSC_POSTLOAD_VENDOR: a vendor resource type that needs to be handled by
+ *                 remoteproc implementations after loading
  * @RSC_LAST:       just keep this one at the end
  *
  * For more details regarding a specific resource type, please see its
@@ -111,11 +116,13 @@ struct fw_rsc_hdr {
  * please update it as needed.
  */
 enum fw_resource_type {
-       RSC_CARVEOUT    = 0,
-       RSC_DEVMEM      = 1,
-       RSC_TRACE       = 2,
-       RSC_VDEV        = 3,
-       RSC_LAST        = 4,
+       RSC_CARVEOUT            = 0,
+       RSC_DEVMEM              = 1,
+       RSC_TRACE               = 2,
+       RSC_VDEV                = 3,
+       RSC_PRELOAD_VENDOR      = 4,
+       RSC_POSTLOAD_VENDOR     = 5,
+       RSC_LAST                = 6,
 };
 
 #define FW_RSC_ADDR_ANY (-1)
@@ -305,6 +312,24 @@ struct fw_rsc_vdev {
        struct fw_rsc_vdev_vring vring[0];
 } __packed;
 
+/**
+ * struct fw_rsc_vendor - vendor resource definition
+ * @sub_type: implementation specific type including version field
+ * @size: size of the vendor custom resource
+ * @data: label for the start of the resource
+ */
+struct fw_rsc_vendor {
+       union {
+               u32 sub_type;
+               struct {
+                       u16 st_type;
+                       u16 st_ver;
+               } st;
+       } u;
+       u32 size;
+       u8 data[0];
+} __packed;
+
 /**
  * struct rproc_mem_entry - memory entry descriptor
  * @va:        virtual address
@@ -312,6 +337,7 @@ struct fw_rsc_vdev {
  * @len: length, in bytes
  * @da: device address
  * @priv: associated data
+ * @name: associated memory region name (optional)
  * @node: list node
  */
 struct rproc_mem_entry {
@@ -320,12 +346,26 @@ struct rproc_mem_entry {
        int len;
        u32 da;
        void *priv;
+       char name[32];
        struct list_head node;
 };
 
 struct rproc;
 struct firmware;
 
+/*
+ * Macros to use with flags field in rproc_da_to_va API. Use
+ * the upper 16 bits to dictate the flags type and the lower
+ * 16 bits to pass on the value of the flags pertinent to that
+ * type.
+ *
+ * Add any new flags type at a new bit-field position
+ */
+#define RPROC_FLAGS_SHIFT      16
+#define RPROC_FLAGS_NONE       0
+#define RPROC_FLAGS_ELF_PHDR   BIT(0 + RPROC_FLAGS_SHIFT)
+#define RPROC_FLAGS_ELF_SHDR   BIT(1 + RPROC_FLAGS_SHIFT)
+
 /**
  * struct rproc_ops - platform-specific device handlers
  * @start:     power on the device and boot it
@@ -334,6 +374,7 @@ struct firmware;
  * @da_to_va:  optional platform hook to perform address translations
  * @load_rsc_table:    load resource table from firmware image
  * @find_loaded_rsc_table: find the loaded resouce table
+ * @handle_vendor_rsc: hook to handle device specific resource table entries
  * @load:              load firmeware to memory, where the remote processor
  *                     expects to find it
  * @sanity_check:      sanity check the fw image
@@ -343,10 +384,12 @@ struct rproc_ops {
        int (*start)(struct rproc *rproc);
        int (*stop)(struct rproc *rproc);
        void (*kick)(struct rproc *rproc, int vqid);
-       void * (*da_to_va)(struct rproc *rproc, u64 da, int len);
+       void * (*da_to_va)(struct rproc *rproc, u64 da, int len, u32 flags);
        int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
        struct resource_table *(*find_loaded_rsc_table)(
                                struct rproc *rproc, const struct firmware *fw);
+       int (*handle_vendor_rsc)(struct rproc *rproc,
+                                struct fw_rsc_vendor *rsc);
        int (*load)(struct rproc *rproc, const struct firmware *fw);
        int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
        u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
@@ -439,6 +482,10 @@ struct rproc_dump_segment {
  * @cached_table: copy of the resource table
  * @table_sz: size of @cached_table
  * @has_iommu: flag to indicate if remote processor is behind an MMU
+ * @auto_boot: flag to indicate if remote processor should be auto-started
+ * @deny_sysfs_ops: flag to not permit sysfs operations on state and firmware
+ * @skip_firmware_request: flag to skip requesting the firmware
+ * @skip_load: flag to skip the loading of firmware segments
  * @dump_segments: list of segments in the firmware
  */
 struct rproc {
@@ -471,6 +518,9 @@ struct rproc {
        size_t table_sz;
        bool has_iommu;
        bool auto_boot;
+       unsigned int deny_sysfs_ops             : 1;
+       unsigned int skip_firmware_request      : 1;
+       unsigned int skip_load                  : 1;
        struct list_head dump_segments;
 };
 
@@ -555,8 +605,12 @@ void rproc_free(struct rproc *rproc);
 
 int rproc_boot(struct rproc *rproc);
 void rproc_shutdown(struct rproc *rproc);
+int rproc_set_firmware(struct rproc *rproc, const char *fw_name);
 void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type);
+void *rproc_da_to_va(struct rproc *rproc, u64 da, int len, u32 flags);
 int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size);
+int rproc_get_id(struct rproc *rproc);
+int rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da);
 
 static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
 {
index 9fe156d1c018e3d4dd5f5717a81fd9fde6212d96..436faf04ba1ca78ce5cadb91857a3c36147e440b 100644 (file)
@@ -28,11 +28,13 @@ struct rpmsg_endpoint_ops;
 /**
  * struct rpmsg_channel_info - channel info representation
  * @name: name of service
+ * @desc: description of service
  * @src: local address
  * @dst: destination address
  */
 struct rpmsg_channel_info {
        char name[RPMSG_NAME_SIZE];
+       char desc[RPMSG_NAME_SIZE];
        u32 src;
        u32 dst;
 };
@@ -42,6 +44,7 @@ struct rpmsg_channel_info {
  * @dev: the device struct
  * @id: device id (used to match between rpmsg drivers and devices)
  * @driver_override: driver name to force a match
+ * @desc: description of remote service
  * @src: local address
  * @dst: destination address
  * @ept: the rpmsg endpoint of this channel
@@ -51,6 +54,7 @@ struct rpmsg_device {
        struct device dev;
        struct rpmsg_device_id id;
        char *driver_override;
+       char desc[RPMSG_NAME_SIZE];
        u32 src;
        u32 dst;
        struct rpmsg_endpoint *ept;
diff --git a/include/uapi/linux/keystone_dsp_mem.h b/include/uapi/linux/keystone_dsp_mem.h
new file mode 100644 (file)
index 0000000..8ad1120
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef _UAPI_KEYSTONE_DSP_MEM_H_
+#define _UAPI_KEYSTONE_DSP_MEM_H_
+
+#define KEYSTONE_DSP_MEM_MAP_INDEX_MASK                (0x7)
+#define KEYSTONE_DSP_MEM_MAP_OFFSET_SHIFT      (3)
+
+#endif /* _UAPI_KEYSTONE_DSP_MEM_H_ */
diff --git a/include/uapi/linux/keystone_remoteproc.h b/include/uapi/linux/keystone_remoteproc.h
new file mode 100644 (file)
index 0000000..a2aac0f
--- /dev/null
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_
+#define _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/**
+ * enum keystone_rproc_state - keystone remoteproc state setting values
+ *
+ * @KEYSTONE_RPROC_OFFLINE: request to configure the remoteproc into an offline
+ *                         state
+ * @KEYSTONE_RPROC_RUNNING: request to configure the remoteproc into a ready
+ *                         state
+ */
+enum keystone_rproc_state {
+       KEYSTONE_RPROC_OFFLINE,
+       KEYSTONE_RPROC_RUNNING,
+};
+
+/**
+ * struct keystone_rproc_set_state_params - keystone remoteproc set state
+ *                                         parameters structure
+ *
+ * @state: enumerated state value to set
+ * @boot_addr: boot address/entry point for the remote processor
+ */
+struct keystone_rproc_set_state_params {
+       enum keystone_rproc_state state;
+       uint32_t boot_addr;
+};
+
+/* Macros used within mmap function */
+#define KEYSTONE_RPROC_UIO_MAP_INDEX_MASK      (0x7)
+#define KEYSTONE_RPROC_UIO_MAP_OFFSET_SHIFT    (3)
+
+/* IOCTL definitions */
+#define KEYSTONE_RPROC_IOC_MAGIC               'I'
+#define KEYSTONE_RPROC_IOC_SET_RSC_TABLE       _IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+                                               0, void *)
+#define KEYSTONE_RPROC_IOC_SET_STATE   _IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+                                       1, \
+                                       struct keystone_rproc_set_state_params)
+#define KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE _IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+                                               2, uint32_t)
+#define KEYSTONE_RPROC_IOC_DSP_RESET           _IO(KEYSTONE_RPROC_IOC_MAGIC, 3)
+#define KEYSTONE_RPROC_IOC_DSP_BOOT            _IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+                                               4, uint32_t)
+
+#define KEYSTONE_RPROC_IOC_MAXNR               (5)
+
+#endif /* _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_ */
index f161dfd3e70a7aaf575e022f4390762f0608a9bc..9b6b27ea504f472b06a84f71ee392728b9889251 100644 (file)
@@ -22,7 +22,9 @@
 #include <linux/rpmsg.h>
 
 #define MSG            "hello world!"
-#define MSG_LIMIT      100
+
+static int count = 100;
+module_param(count, int, 0644);
 
 struct instance_data {
        int rx_count;
@@ -41,7 +43,7 @@ static int rpmsg_sample_cb(struct rpmsg_device *rpdev, void *data, int len,
                       data, len,  true);
 
        /* samples should not live forever */
-       if (idata->rx_count >= MSG_LIMIT) {
+       if (idata->rx_count >= count) {
                dev_info(&rpdev->dev, "goodbye!\n");
                return 0;
        }