Bläddra i källkod

Merge branch 'rpmsg-ti-linux-4.19.y' of git://git.ti.com/rpmsg/rpmsg into rpmsg-ti-linux-4.19.y-intg

Signed-off-by: Suman Anna <s-anna@ti.com>
Suman Anna 6 år sedan
förälder
incheckning
979348652e
58 ändrade filer med 5907 tillägg och 93 borttagningar
  1. 62 0
      Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.txt
  2. 42 8
      Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
  3. 161 0
      Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.txt
  4. 338 0
      Documentation/devicetree/bindings/soc/ti/ti,pruss.txt
  5. 8 0
      arch/arm/boot/dts/am335x-bone-common.dtsi
  6. 8 0
      arch/arm/boot/dts/am335x-evm.dts
  7. 8 0
      arch/arm/boot/dts/am335x-evmsk.dts
  8. 8 0
      arch/arm/boot/dts/am335x-icev2.dts
  9. 88 0
      arch/arm/boot/dts/am33xx.dtsi
  10. 165 0
      arch/arm/boot/dts/am4372.dtsi
  11. 12 0
      arch/arm/boot/dts/am437x-gp-evm.dts
  12. 12 0
      arch/arm/boot/dts/am437x-idk-evm.dts
  13. 12 0
      arch/arm/boot/dts/am437x-sk-evm.dts
  14. 16 0
      arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
  15. 16 0
      arch/arm/boot/dts/am57xx-idk-common.dtsi
  16. 190 0
      arch/arm/boot/dts/dra7.dtsi
  17. 224 0
      arch/arm/boot/dts/keystone-k2g.dtsi
  18. 54 0
      arch/arm/mach-omap2/omap_hwmod_7xx_data.c
  19. 11 0
      arch/arm/mach-omap2/pdata-quirks.c
  20. 492 0
      arch/arm64/boot/dts/ti/k3-am65-main.dtsi
  21. 1 1
      drivers/clk/ti/clk-33xx.c
  22. 1 1
      drivers/clk/ti/clk-43xx.c
  23. 1 0
      drivers/irqchip/Makefile
  24. 703 0
      drivers/irqchip/irq-pruss-intc.c
  25. 1 1
      drivers/mailbox/Kconfig
  26. 26 17
      drivers/mailbox/omap-mailbox.c
  27. 14 0
      drivers/remoteproc/Kconfig
  28. 1 0
      drivers/remoteproc/Makefile
  29. 1 1
      drivers/remoteproc/imx_rproc.c
  30. 6 3
      drivers/remoteproc/keystone_remoteproc.c
  31. 1306 0
      drivers/remoteproc/pru_rproc.c
  32. 92 0
      drivers/remoteproc/pru_rproc.h
  33. 1 1
      drivers/remoteproc/qcom_adsp_pil.c
  34. 1 1
      drivers/remoteproc/qcom_q6v5_pil.c
  35. 1 1
      drivers/remoteproc/qcom_q6v5_wcss.c
  36. 1 1
      drivers/remoteproc/qcom_wcnss.c
  37. 123 5
      drivers/remoteproc/remoteproc_core.c
  38. 13 1
      drivers/remoteproc/remoteproc_debugfs.c
  39. 4 2
      drivers/remoteproc/remoteproc_elf_loader.c
  40. 17 32
      drivers/remoteproc/remoteproc_sysfs.c
  41. 2 1
      drivers/remoteproc/st_slim_rproc.c
  42. 2 1
      drivers/remoteproc/wkup_m3_rproc.c
  43. 11 0
      drivers/rpmsg/Kconfig
  44. 1 0
      drivers/rpmsg/Makefile
  45. 350 0
      drivers/rpmsg/rpmsg_pru.c
  46. 14 5
      drivers/rpmsg/virtio_rpmsg_bus.c
  47. 11 0
      drivers/soc/ti/Kconfig
  48. 1 0
      drivers/soc/ti/Makefile
  49. 453 0
      drivers/soc/ti/pruss.c
  50. 315 0
      drivers/soc/ti/pruss_soc_bus.c
  51. 2 3
      drivers/soc/ti/wkup_m3_ipc.c
  52. 6 0
      include/linux/omap-mailbox.h
  53. 25 0
      include/linux/platform_data/ti-pruss.h
  54. 302 0
      include/linux/pruss.h
  55. 101 0
      include/linux/pruss_driver.h
  56. 50 7
      include/linux/remoteproc.h
  57. 5 0
      ti_config_fragments/ipc.cfg
  58. 15 0
      ti_config_fragments/v8_ipc.cfg

+ 62 - 0
Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.txt

@@ -0,0 +1,62 @@
+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.
+
+The K3 family of SoCs can handle 160 input events that can be mapped to 20
+different possible output interrupts. The additional output interrupts are
+connected to new sub-modules within the ICSSG instances.
+
+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
+                             "ti,am654-icssg-intc" for K3 AM65x 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>;
+			};
+		};
+	};

+ 42 - 8
Documentation/devicetree/bindings/mailbox/omap-mailbox.txt

@@ -1,4 +1,4 @@
-OMAP2+ Mailbox Driver
+OMAP2+ and K3 Mailbox
 =====================
 =====================
 
 
 The OMAP mailbox hardware facilitates communication between different processors
 The OMAP mailbox hardware facilitates communication between different processors
@@ -7,7 +7,7 @@ various processor subsystems and is connected on an interconnect bus. The
 communication is achieved through a set of registers for message storage and
 communication is achieved through a set of registers for message storage and
 interrupt configuration registers.
 interrupt configuration registers.
 
 
-Each mailbox IP block has a certain number of h/w fifo queues and output
+Each mailbox IP block/cluster has a certain number of h/w fifo queues and output
 interrupt lines. An output interrupt line is routed to an interrupt controller
 interrupt lines. An output interrupt line is routed to an interrupt controller
 within a processor subsystem, and there can be more than one line going to a
 within a processor subsystem, and there can be more than one line going to a
 specific processor's interrupt controller. The interrupt line connections are
 specific processor's interrupt controller. The interrupt line connections are
@@ -23,12 +23,14 @@ All the current OMAP SoCs except for the newest DRA7xx SoC has a single IP
 instance. DRA7xx has multiple instances with different number of h/w fifo queues
 instance. DRA7xx has multiple instances with different number of h/w fifo queues
 and interrupt lines between different instances. The interrupt lines can also be
 and interrupt lines between different instances. The interrupt lines can also be
 routed to different processor sub-systems on DRA7xx as they are routed through
 routed to different processor sub-systems on DRA7xx as they are routed through
-the Crossbar, a kind of interrupt router/multiplexer.
+the Crossbar, a kind of interrupt router/multiplexer. The K3 AM65x SoCs has each
+of these instances form a cluster and combine multiple clusters into a single IP
+block.
 
 
 Mailbox Device Node:
 Mailbox Device Node:
 ====================
 ====================
-A Mailbox device node is used to represent a Mailbox IP instance within a SoC.
-The sub-mailboxes are represented as child nodes of this parent node.
+A Mailbox device node is used to represent a Mailbox IP instance/cluster within
+a SoC. The sub-mailboxes are represented as child nodes of this parent node.
 
 
 Required properties:
 Required properties:
 --------------------
 --------------------
@@ -37,12 +39,12 @@ Required properties:
 			    "ti,omap3-mailbox" for OMAP3430, OMAP3630 SoCs
 			    "ti,omap3-mailbox" for OMAP3430, OMAP3630 SoCs
 			    "ti,omap4-mailbox" for OMAP44xx, OMAP54xx, AM33xx,
 			    "ti,omap4-mailbox" for OMAP44xx, OMAP54xx, AM33xx,
 						   AM43xx and DRA7xx SoCs
 						   AM43xx and DRA7xx SoCs
+			    "ti,am654-mailbox" for K3 AM65x SoCs
 - reg:			Contains the mailbox register address range (base
 - reg:			Contains the mailbox register address range (base
 			address and length)
 			address and length)
 - interrupts:		Contains the interrupt information for the mailbox
 - interrupts:		Contains the interrupt information for the mailbox
 			device. The format is dependent on which interrupt
 			device. The format is dependent on which interrupt
 			controller the OMAP device uses
 			controller the OMAP device uses
-- ti,hwmods:		Name of the hwmod associated with the mailbox
 - #mbox-cells:		Common mailbox binding property to identify the number
 - #mbox-cells:		Common mailbox binding property to identify the number
 			of cells required for the mailbox specifier. Should be
 			of cells required for the mailbox specifier. Should be
 			1
 			1
@@ -50,6 +52,20 @@ Required properties:
 			device can interrupt
 			device can interrupt
 - ti,mbox-num-fifos:	Number of h/w fifo queues within the mailbox IP block
 - ti,mbox-num-fifos:	Number of h/w fifo queues within the mailbox IP block
 
 
+SoC-specific Required properties:
+---------------------------------
+The following are mandatory properties for the OMAP architecture based SoCs
+only:
+- ti,hwmods:		Name of the hwmod associated with the mailbox
+
+The following are mandatory properties for the K3 AM65x SoCs only:
+- interrupt-parent:	Should contain a phandle to the TI-SCI interrupt
+			controller node that is used to dynamically program
+			the interrupt routes between the IP and the main GIC
+			controllers. See the following binding for additional
+			details,
+			Documentation/devicetree/bindings/interrupt-controller/ti,sci-irq.txt
+
 Child Nodes:
 Child Nodes:
 ============
 ============
 A child node is used for representing the actual sub-mailbox device that is
 A child node is used for representing the actual sub-mailbox device that is
@@ -98,7 +114,7 @@ to be used by the client user.
 Example:
 Example:
 --------
 --------
 
 
-/* OMAP4 */
+1. /* OMAP4 */
 mailbox: mailbox@4a0f4000 {
 mailbox: mailbox@4a0f4000 {
 	compatible = "ti,omap4-mailbox";
 	compatible = "ti,omap4-mailbox";
 	reg = <0x4a0f4000 0x200>;
 	reg = <0x4a0f4000 0x200>;
@@ -123,7 +139,7 @@ dsp {
 	...
 	...
 };
 };
 
 
-/* AM33xx */
+2. /* AM33xx */
 mailbox: mailbox@480c8000 {
 mailbox: mailbox@480c8000 {
 	compatible = "ti,omap4-mailbox";
 	compatible = "ti,omap4-mailbox";
 	reg = <0x480C8000 0x200>;
 	reg = <0x480C8000 0x200>;
@@ -137,3 +153,21 @@ mailbox: mailbox@480c8000 {
 		ti,mbox-rx = <0 0 3>;
 		ti,mbox-rx = <0 0 3>;
 	};
 	};
 };
 };
+
+3. /* AM65x */
+&cbass_main {
+	mailbox0_cluster0: mailbox@31f80000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f80000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		interrupt-parent = <&main_navss_intr>;
+		interrupts = <164 0 IRQ_TYPE_LEVEL_HIGH>;
+
+		mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 {
+			ti,mbox-tx = <1 0 0>;
+			ti,mbox-rx = <0 0 0>;
+		};
+	};
+};

+ 161 - 0
Documentation/devicetree/bindings/remoteproc/ti,pru-rproc.txt

@@ -0,0 +1,161 @@
+PRU/RTU 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. K3 SoCs have two
+additional PRU cores called RTUs with slightly different IP integration. Each
+RTU core can be used independently like a PRU, or alongside a corresponding
+PRU core to provide/implement auxiliary functionality/support.
+
+Each PRU or RTU 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
+                       "ti,am654-pru" for PRUs in K3 AM65x family of SoCs
+                       "ti,am654-rtu" for RTUs in K3 AM65x 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 or RTU 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>;
+	}

+ 338 - 0
Documentation/devicetree/bindings/soc/ti/ti,pruss.txt

@@ -0,0 +1,338 @@
+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
+                       "ti,am654-icssg-soc-bus" for K3 AM65x 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 and
+K3 AM65x 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
+                       "ti,am654-icssg" for K3 AM65x 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,
+
+The following sub-modules are new and present only on ICSSG IP instances and
+are applicable only for K3 SoCs. They are represented as follows:
+	MII_G_RT sub-module: represent as a syscon node with name "mii-g-rt"
+
+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>;
+			...
+		};
+	};

+ 8 - 0
arch/arm/boot/dts/am335x-bone-common.dtsi

@@ -423,3 +423,11 @@
 &wkup_m3_ipc {
 &wkup_m3_ipc {
 	ti,scale-data-fw = "am335x-bone-scale-data.bin";
 	ti,scale-data-fw = "am335x-bone-scale-data.bin";
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss: pruss@4a300000 {
+		status = "okay";
+	};
+};

+ 8 - 0
arch/arm/boot/dts/am335x-evm.dts

@@ -808,3 +808,11 @@
 &sgx {
 &sgx {
 	status = "okay";
 	status = "okay";
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss: pruss@4a300000 {
+		status = "okay";
+	};
+};

+ 8 - 0
arch/arm/boot/dts/am335x-evmsk.dts

@@ -749,3 +749,11 @@
 	clocks = <&clk_32768_ck>, <&l4_per_clkctrl AM3_CLKDIV32K_CLKCTRL 0>;
 	clocks = <&clk_32768_ck>, <&l4_per_clkctrl AM3_CLKDIV32K_CLKCTRL 0>;
 	clock-names = "ext-clk", "int-clk";
 	clock-names = "ext-clk", "int-clk";
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss: pruss@4a300000 {
+		status = "okay";
+	};
+};

+ 8 - 0
arch/arm/boot/dts/am335x-icev2.dts

@@ -504,3 +504,11 @@
 		reg = <3>;
 		reg = <3>;
 	};
 	};
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss: pruss@4a300000 {
+		status = "okay";
+	};
+};

+ 88 - 0
arch/arm/boot/dts/am33xx.dtsi

@@ -936,6 +936,94 @@
 			};
 			};
 		};
 		};
 
 
+		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 {
 		elm: elm@48080000 {
 			compatible = "ti,am3352-elm";
 			compatible = "ti,am3352-elm";
 			reg = <0x48080000 0x2000>;
 			reg = <0x48080000 0x2000>;

+ 165 - 0
arch/arm/boot/dts/am4372.dtsi

@@ -957,6 +957,171 @@
 			interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
 			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 {
 		mcasp0: mcasp@48038000 {
 			compatible = "ti,am33xx-mcasp-audio";
 			compatible = "ti,am33xx-mcasp-audio";
 			ti,hwmods = "mcasp0";
 			ti,hwmods = "mcasp0";

+ 12 - 0
arch/arm/boot/dts/am437x-gp-evm.dts

@@ -1148,3 +1148,15 @@
 &sgx {
 &sgx {
 	status = "okay";
 	status = "okay";
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss1: pruss@54400000 {
+		status = "okay";
+	};
+
+	pruss0: pruss@54440000 {
+		status = "okay";
+	};
+};

+ 12 - 0
arch/arm/boot/dts/am437x-idk-evm.dts

@@ -537,3 +537,15 @@
 &sgx {
 &sgx {
 	status = "okay";
 	status = "okay";
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss1: pruss@54400000 {
+		status = "okay";
+	};
+
+	pruss0: pruss@54440000 {
+		status = "okay";
+	};
+};

+ 12 - 0
arch/arm/boot/dts/am437x-sk-evm.dts

@@ -913,3 +913,15 @@
 &sgx {
 &sgx {
 	status = "okay";
 	status = "okay";
 };
 };
+
+&pruss_soc_bus {
+	status = "okay";
+
+	pruss1: pruss@54400000 {
+		status = "okay";
+	};
+
+	pruss0: pruss@54440000 {
+		status = "okay";
+	};
+};

+ 16 - 0
arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi

@@ -595,3 +595,19 @@
 		status = "okay";
 		status = "okay";
 	};
 	};
 };
 };
+
+&pruss_soc_bus1 {
+	status = "okay";
+
+	pruss1: pruss@4b200000 {
+		status = "okay";
+	};
+};
+
+&pruss_soc_bus2 {
+	status = "okay";
+
+	pruss2: pruss@4b280000 {
+		status = "okay";
+	};
+};

+ 16 - 0
arch/arm/boot/dts/am57xx-idk-common.dtsi

@@ -619,3 +619,19 @@
 		};
 		};
 	};
 	};
 };
 };
+
+&pruss_soc_bus1 {
+	status = "okay";
+
+	pruss1: pruss@4b200000 {
+		status = "okay";
+	};
+};
+
+&pruss_soc_bus2 {
+	status = "okay";
+
+	pruss2: pruss@4b280000 {
+		status = "okay";
+	};
+};

+ 190 - 0
arch/arm/boot/dts/dra7.dtsi

@@ -1174,6 +1174,196 @@
 			status = "disabled";
 			status = "disabled";
 		};
 		};
 
 
+		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 {
 		abb_mpu: regulator-abb-mpu {
 			compatible = "ti,abb-v3";
 			compatible = "ti,abb-v3";
 			regulator-name = "abb_mpu";
 			regulator-name = "abb_mpu";

+ 224 - 0
arch/arm/boot/dts/keystone-k2g.dtsi

@@ -142,6 +142,38 @@
 				#gpio-cells = <2>;
 				#gpio-cells = <2>;
 				gpio,syscon-dev = <&devctrl 0x240>;
 				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 {
 		uart0: serial@2530c00 {
@@ -641,6 +673,198 @@
 			interrupts = <GIC_SPI 123 IRQ_TYPE_EDGE_RISING>;
 			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 {
 		mdio: mdio@4200f00 {
 			compatible = "ti,keystone_mdio", "ti,davinci_mdio";
 			compatible = "ti,keystone_mdio", "ti,davinci_mdio";
 			reg = <0x04200f00 0x100>;
 			reg = <0x04200f00 0x100>;

+ 54 - 0
arch/arm/mach-omap2/omap_hwmod_7xx_data.c

@@ -2082,6 +2082,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
  * 'qspi' class
  *
  *
@@ -3653,6 +3689,22 @@ static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pciess2 = {
 	.user		= OCP_USER_MPU | OCP_USER_SDMA,
 	.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 */
 /* l3_main_1 -> qspi */
 static struct omap_hwmod_ocp_if dra7xx_l3_main_1__qspi = {
 static struct omap_hwmod_ocp_if dra7xx_l3_main_1__qspi = {
 	.master		= &dra7xx_l3_main_1_hwmod,
 	.master		= &dra7xx_l3_main_1_hwmod,
@@ -4154,6 +4206,8 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
 	&dra7xx_l4_cfg__pciess1,
 	&dra7xx_l4_cfg__pciess1,
 	&dra7xx_l3_main_1__pciess2,
 	&dra7xx_l3_main_1__pciess2,
 	&dra7xx_l4_cfg__pciess2,
 	&dra7xx_l4_cfg__pciess2,
+	&dra7xx_l4_cfg__pruss1,
+	&dra7xx_l4_cfg__pruss2,
 	&dra7xx_l3_main_1__qspi,
 	&dra7xx_l3_main_1__qspi,
 	&dra7xx_l4_cfg__sata,
 	&dra7xx_l4_cfg__sata,
 	&dra7xx_l4_cfg__smartreflex_core,
 	&dra7xx_l4_cfg__smartreflex_core,

+ 11 - 0
arch/arm/mach-omap2/pdata-quirks.c

@@ -25,6 +25,7 @@
 #include <linux/platform_data/hsmmc-omap.h>
 #include <linux/platform_data/hsmmc-omap.h>
 #include <linux/platform_data/iommu-omap.h>
 #include <linux/platform_data/iommu-omap.h>
 #include <linux/platform_data/ti-sysc.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/wkup_m3.h>
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 #include <linux/platform_data/sgx-omap.h>
 #include <linux/platform_data/sgx-omap.h>
@@ -433,6 +434,12 @@ static struct wkup_m3_platform_data wkup_m3_data = {
 	.assert_reset = omap_device_assert_hardreset,
 	.assert_reset = omap_device_assert_hardreset,
 	.deassert_reset = omap_device_deassert_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
 #endif
 
 
 #ifdef CONFIG_SOC_OMAP5
 #ifdef CONFIG_SOC_OMAP5
@@ -577,12 +584,16 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = {
 #ifdef CONFIG_SOC_AM33XX
 #ifdef CONFIG_SOC_AM33XX
 	OF_DEV_AUXDATA("ti,am3352-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
 	OF_DEV_AUXDATA("ti,am3352-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
 		       &wkup_m3_data),
 		       &wkup_m3_data),
+	OF_DEV_AUXDATA("ti,am3356-pruss-soc-bus", 0x4a326004,
+		       "4a326004.pruss-soc-bus", &pruss_pdata),
 	OF_DEV_AUXDATA("ti,am3352-sgx530", 0x56000000, "56000000.sgx",
 	OF_DEV_AUXDATA("ti,am3352-sgx530", 0x56000000, "56000000.sgx",
 		       &sgx_pdata),
 		       &sgx_pdata),
 #endif
 #endif
 #ifdef CONFIG_SOC_AM43XX
 #ifdef CONFIG_SOC_AM43XX
 	OF_DEV_AUXDATA("ti,am4372-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
 	OF_DEV_AUXDATA("ti,am4372-wkup-m3", 0x44d00000, "44d00000.wkup_m3",
 		       &wkup_m3_data),
 		       &wkup_m3_data),
+	OF_DEV_AUXDATA("ti,am4376-pruss-soc-bus", 0x54426004,
+		       "54426004.pruss_soc_bus", &pruss_pdata),
 	OF_DEV_AUXDATA("ti,am4376-sgx530", 0x56000000, "56000000.sgx",
 	OF_DEV_AUXDATA("ti,am4376-sgx530", 0x56000000, "56000000.sgx",
 		       &sgx_pdata),
 		       &sgx_pdata),
 #endif
 #endif

+ 492 - 0
arch/arm64/boot/dts/ti/k3-am65-main.dtsi

@@ -461,6 +461,498 @@
 		#hwlock-cells = <1>;
 		#hwlock-cells = <1>;
 	};
 	};
 
 
+	mailbox0_cluster0: mailbox@31f80000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f80000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		interrupt-parent = <&main_navss_intr>;
+		interrupts = <164 0 IRQ_TYPE_LEVEL_HIGH>;
+
+		mbox_mcu_r5fss0_core0: mbox-mcu-r5fss0-core0 {
+			ti,mbox-tx = <1 0 0>;
+			ti,mbox-rx = <0 0 0>;
+		};
+	};
+
+	mailbox0_cluster1: mailbox@31f81000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f81000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		interrupt-parent = <&main_navss_intr>;
+		interrupts = <165 0 IRQ_TYPE_LEVEL_HIGH>;
+
+		mbox_mcu_r5fss0_core1: mbox-mcu-r5fss0-core1 {
+			ti,mbox-tx = <1 0 0>;
+			ti,mbox-rx = <0 0 0>;
+		};
+	};
+
+	mailbox0_cluster2: mailbox@31f82000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f82000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster3: mailbox@31f83000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f83000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster4: mailbox@31f84000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f84000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster5: mailbox@31f85000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f85000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster6: mailbox@31f86000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f86000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster7: mailbox@31f87000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f87000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster8: mailbox@31f88000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f88000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster9: mailbox@31f89000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f89000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster10: mailbox@31f8a000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f8a000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	mailbox0_cluster11: mailbox@31f8b000 {
+		compatible = "ti,am654-mailbox";
+		reg = <0x00 0x31f8b000 0x00 0x200>;
+		#mbox-cells = <1>;
+		ti,mbox-num-users = <4>;
+		ti,mbox-num-fifos = <16>;
+		status = "disabled";
+	};
+
+	icssg_soc_bus0: pruss-soc-bus@b026004 {
+		compatible = "ti,am654-icssg-soc-bus";
+		reg = <0x00 0x0b026004 0x00 0x4>;
+		power-domains = <&k3_pds 62>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x0b000000 0x00 0x0b000000 0x100000>;
+		dma-ranges;
+
+		icssg0: icssg@b000000 {
+			compatible = "ti,am654-icssg";
+			reg = <0xb000000 0x80000>;
+			interrupts = <GIC_SPI 254 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 255 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "host2", "host3", "host4",
+					  "host5", "host6", "host7",
+					  "host8", "host9";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges;
+			dma-ranges;
+
+			icssg0_mem: memories@b000000 {
+				reg = <0xb000000 0x2000>,
+				      <0xb002000 0x2000>,
+				      <0xb010000 0x10000>;
+				reg-names = "dram0", "dram1",
+					    "shrdram2";
+			};
+
+			icssg0_cfg: cfg@b026000 {
+				compatible = "syscon";
+				reg = <0xb026000 0x200>;
+			};
+
+			icssg0_iep: iep@b02e000 {
+				compatible = "syscon";
+				reg = <0xb02e000 0x1000>;
+			};
+
+			icssg0_mii_rt: mii-rt@b032000 {
+				compatible = "syscon";
+				reg = <0xb032000 0x100>;
+			};
+
+			icssg0_mii_g_rt: mii-g-rt@b033000 {
+				compatible = "syscon";
+				reg = <0xb033000 0x1000>;
+			};
+
+			icssg0_intc: interrupt-controller@b020000 {
+				compatible = "ti,am654-icssg-intc";
+				reg = <0xb020000 0x2000>;
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+
+			pru0_0: pru@b034000 {
+				compatible = "ti,am654-pru";
+				reg = <0xb034000 0x4000>,
+				      <0xb022000 0x100>,
+				      <0xb022400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-pru0_0-fw";
+				interrupt-parent = <&icssg0_intc>;
+				interrupts = <16>, <17>;
+				interrupt-names = "vring", "kick";
+			};
+
+			rtu0_0: rtu@b004000 {
+				compatible = "ti,am654-rtu";
+				reg = <0xb004000 0x2000>,
+				      <0xb023000 0x100>,
+				      <0xb023400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-rtu0_0-fw";
+				interrupt-parent = <&icssg0_intc>;
+				interrupts = <20>, <21>;
+				interrupt-names = "vring", "kick";
+			};
+
+			pru0_1: pru@b038000 {
+				compatible = "ti,am654-pru";
+				reg = <0xb038000 0x4000>,
+				      <0xb024000 0x100>,
+				      <0xb024400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-pru0_1-fw";
+				interrupt-parent = <&icssg0_intc>;
+				interrupts = <18>, <19>;
+				interrupt-names = "vring", "kick";
+			};
+
+			rtu0_1: rtu@b006000 {
+				compatible = "ti,am654-rtu";
+				reg = <0xb006000 0x2000>,
+				      <0xb023800 0x100>,
+				      <0xb023c00 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-rtu0_1-fw";
+				interrupt-parent = <&icssg0_intc>;
+				interrupts = <22>, <23>;
+				interrupt-names = "vring", "kick";
+			};
+
+			icssg0_mdio: mdio@b032400 {
+				compatible = "ti,davinci_mdio";
+				reg = <0xb032400 0x100>;
+				clocks = <&k3_clks 62 3>;
+				clock-names = "fck";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus_freq = <1000000>;
+				status = "disabled";
+			};
+		};
+	};
+
+	icssg_soc_bus1: pruss-soc-bus@b126004 {
+		compatible = "ti,am654-icssg-soc-bus";
+		reg = <0x00 0x0b126004 0x00 0x4>;
+		power-domains = <&k3_pds 63>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x0b100000 0x00 0x0b100000 0x100000>;
+		dma-ranges;
+
+		icssg1: icssg@b100000 {
+			compatible = "ti,am654-icssg";
+			reg = <0xb100000 0x80000>;
+			interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "host2", "host3", "host4",
+					  "host5", "host6", "host7",
+					  "host8", "host9";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges;
+			dma-ranges;
+
+			icssg1_mem: memories@b100000 {
+				reg = <0xb100000 0x2000>,
+				      <0xb102000 0x2000>,
+				      <0xb110000 0x10000>;
+				reg-names = "dram0", "dram1",
+					    "shrdram2";
+			};
+
+			icssg1_cfg: cfg@b126000 {
+				compatible = "syscon";
+				reg = <0xb126000 0x200>;
+			};
+
+			icssg1_iep: iep@b12e000 {
+				compatible = "syscon";
+				reg = <0xb12e000 0x1000>;
+			};
+
+			icssg1_mii_rt: mii-rt@b132000 {
+				compatible = "syscon";
+				reg = <0xb132000 0x100>;
+			};
+
+			icssg1_mii_g_rt: mii-g-rt@b133000 {
+				compatible = "syscon";
+				reg = <0xb133000 0x1000>;
+			};
+
+			icssg1_intc: interrupt-controller@b120000 {
+				compatible = "ti,am654-icssg-intc";
+				reg = <0xb120000 0x2000>;
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+
+			pru1_0: pru@b134000 {
+				compatible = "ti,am654-pru";
+				reg = <0xb134000 0x4000>,
+				      <0xb122000 0x100>,
+				      <0xb122400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-pru1_0-fw";
+				interrupt-parent = <&icssg1_intc>;
+				interrupts = <16>, <17>;
+				interrupt-names = "vring", "kick";
+			};
+
+			rtu1_0: rtu@b104000 {
+				compatible = "ti,am654-rtu";
+				reg = <0xb104000 0x2000>,
+				      <0xb123000 0x100>,
+				      <0xb123400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-rtu1_0-fw";
+				interrupt-parent = <&icssg1_intc>;
+				interrupts = <20>, <21>;
+				interrupt-names = "vring", "kick";
+			};
+
+			pru1_1: pru@b138000 {
+				compatible = "ti,am654-pru";
+				reg = <0xb138000 0x4000>,
+				      <0xb124000 0x100>,
+				      <0xb124400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-pru1_1-fw";
+				interrupt-parent = <&icssg1_intc>;
+				interrupts = <18>, <19>;
+				interrupt-names = "vring", "kick";
+			};
+
+			rtu1_1: rtu@b106000 {
+				compatible = "ti,am654-rtu";
+				reg = <0xb106000 0x2000>,
+				      <0xb123800 0x100>,
+				      <0xb123c00 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-rtu1_1-fw";
+				interrupt-parent = <&icssg1_intc>;
+				interrupts = <22>, <23>;
+				interrupt-names = "vring", "kick";
+			};
+
+			icssg1_mdio: mdio@b132400 {
+				compatible = "ti,davinci_mdio";
+				reg = <0xb132400 0x100>;
+				clocks = <&k3_clks 63 3>;
+				clock-names = "fck";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus_freq = <1000000>;
+				status = "disabled";
+			};
+		};
+	};
+
+	icssg_soc_bus2: pruss-soc-bus@b226004 {
+		compatible = "ti,am654-icssg-soc-bus";
+		reg = <0x00 0x0b226004 0x00 0x4>;
+		power-domains = <&k3_pds 64>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges = <0x0b200000 0x00 0x0b200000 0x100000>;
+		dma-ranges;
+
+		icssg2: icssg@b200000 {
+			compatible = "ti,am654-icssg";
+			reg = <0xb200000 0x80000>;
+			interrupts = <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 275 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "host2", "host3", "host4",
+					  "host5", "host6", "host7",
+					  "host8", "host9";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges;
+			dma-ranges;
+
+			icssg2_mem: memories@b200000 {
+				reg = <0xb200000 0x2000>,
+				      <0xb202000 0x2000>,
+				      <0xb210000 0x10000>;
+				reg-names = "dram0", "dram1",
+					    "shrdram2";
+			};
+
+			icssg2_cfg: cfg@b226000 {
+				compatible = "syscon";
+				reg = <0xb226000 0x200>;
+			};
+
+			icssg2_iep: iep@b22e000 {
+				compatible = "syscon";
+				reg = <0xb22e000 0x1000>;
+			};
+
+			icssg2_mii_rt: mii-rt@b232000 {
+				compatible = "syscon";
+				reg = <0xb232000 0x100>;
+			};
+
+			icssg2_mii_g_rt: mii-g-rt@b233000 {
+				compatible = "syscon";
+				reg = <0xb233000 0x1000>;
+			};
+
+			icssg2_intc: interrupt-controller@b220000 {
+				compatible = "ti,am654-icssg-intc";
+				reg = <0xb220000 0x2000>;
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+
+			pru2_0: pru@b234000 {
+				compatible = "ti,am654-pru";
+				reg = <0xb234000 0x4000>,
+				      <0xb222000 0x100>,
+				      <0xb222400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-pru2_0-fw";
+				interrupt-parent = <&icssg2_intc>;
+				interrupts = <16>, <17>;
+				interrupt-names = "vring", "kick";
+			};
+
+			rtu2_0: rtu@b204000 {
+				compatible = "ti,am654-rtu";
+				reg = <0xb204000 0x2000>,
+				      <0xb223000 0x100>,
+				      <0xb223400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-rtu2_0-fw";
+				interrupt-parent = <&icssg2_intc>;
+				interrupts = <20>, <21>;
+				interrupt-names = "vring", "kick";
+			};
+
+			pru2_1: pru@b238000 {
+				compatible = "ti,am654-pru";
+				reg = <0xb238000 0x4000>,
+				      <0xb224000 0x100>,
+				      <0xb224400 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-pru2_1-fw";
+				interrupt-parent = <&icssg2_intc>;
+				interrupts = <18>, <19>;
+				interrupt-names = "vring", "kick";
+			};
+
+			rtu2_1: rtu@b206000 {
+				compatible = "ti,am654-rtu";
+				reg = <0xb206000 0x2000>,
+				      <0xb223800 0x100>,
+				      <0xb223c00 0x100>;
+				reg-names = "iram", "control", "debug";
+				firmware-name = "am65x-rtu2_1-fw";
+				interrupt-parent = <&icssg2_intc>;
+				interrupts = <22>, <23>;
+				interrupt-names = "vring", "kick";
+			};
+
+			icssg2_mdio: mdio@b232400 {
+				compatible = "ti,davinci_mdio";
+				reg = <0xb232400 0x100>;
+				clocks = <&k3_clks 64 3>;
+				clock-names = "fck";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus_freq = <1000000>;
+				status = "disabled";
+			};
+		};
+	};
+
 	ecap0: pwm@3100000 {
 	ecap0: pwm@3100000 {
 		compatible = "ti,am654-ecap", "ti,am3352-ecap";
 		compatible = "ti,am654-ecap", "ti,am3352-ecap";
 		#pwm-cells = <3>;
 		#pwm-cells = <3>;

+ 1 - 1
drivers/clk/ti/clk-33xx.c

@@ -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_EPWMSS2_CLKCTRL, NULL, CLKF_SW_SUP, "l4ls_gclk" },
 	{ AM3_L3_INSTR_CLKCTRL, NULL, CLKF_SW_SUP, "l3_gclk", "l3_clkdm" },
 	{ 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_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_TIMER5_CLKCTRL, NULL, CLKF_SW_SUP, "timer5_fck" },
 	{ AM3_TIMER6_CLKCTRL, NULL, CLKF_SW_SUP, "timer6_fck" },
 	{ AM3_TIMER6_CLKCTRL, NULL, CLKF_SW_SUP, "timer6_fck" },
 	{ AM3_MMC2_CLKCTRL, NULL, CLKF_SW_SUP, "mmc_clk" },
 	{ AM3_MMC2_CLKCTRL, NULL, CLKF_SW_SUP, "mmc_clk" },

+ 1 - 1
drivers/clk/ti/clk-43xx.c

@@ -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_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_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_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_L4_LS_CLKCTRL, NULL, CLKF_SW_SUP, "l4ls_gclk" },
 	{ AM4_D_CAN0_CLKCTRL, NULL, CLKF_SW_SUP, "dcan0_fck" },
 	{ AM4_D_CAN0_CLKCTRL, NULL, CLKF_SW_SUP, "dcan0_fck" },
 	{ AM4_D_CAN1_CLKCTRL, NULL, CLKF_SW_SUP, "dcan1_fck" },
 	{ AM4_D_CAN1_CLKCTRL, NULL, CLKF_SW_SUP, "dcan1_fck" },

+ 1 - 0
drivers/irqchip/Makefile

@@ -88,5 +88,6 @@ obj-$(CONFIG_GOLDFISH_PIC) 		+= irq-goldfish-pic.o
 obj-$(CONFIG_NDS32)			+= irq-ativic32.o
 obj-$(CONFIG_NDS32)			+= irq-ativic32.o
 obj-$(CONFIG_QCOM_PDC)			+= qcom-pdc.o
 obj-$(CONFIG_QCOM_PDC)			+= qcom-pdc.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
+obj-$(CONFIG_TI_PRUSS)			+= irq-pruss-intc.o
 obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.o

+ 703 - 0
drivers/irqchip/irq-pruss-intc.c

@@ -0,0 +1,703 @@
+// 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_SRSR(x)	(0x0200 + (x) * 4)
+#define PRU_INTC_SRSR0		0x0200
+#define PRU_INTC_SRSR1		0x0204
+#define PRU_INTC_SECR(x)	(0x0280 + (x) * 4)
+#define PRU_INTC_SECR0		0x0280
+#define PRU_INTC_SECR1		0x0284
+#define PRU_INTC_ESR(x)		(0x0300 + (x) * 4)
+#define PRU_INTC_ESR0		0x0300
+#define PRU_INTC_ESR1		0x0304
+#define PRU_INTC_ECR(x)		(0x0380 + (x) * 4)
+#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_SIPR(x)	(0x0D00 + (x) * 4)
+#define PRU_INTC_SITR(x)	(0x0D80 + (x) * 4)
+#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
+ * @num_system_events: number of input system events handled by the PRUSS INTC
+ * @num_host_intrs: number of host interrupts supported by the PRUSS INTC
+ * @no_host7_intr: flag denoting the absence of host7 interrupt into MPU
+ */
+struct pruss_intc_match_data {
+	u8 num_system_events;
+	u8 num_host_intrs;
+	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
+ * @data: cached PRUSS INTC IP configuration data
+ * @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;
+	const struct pruss_intc_match_data *data;
+	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 >= intc->data->num_system_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;
+	s8 ch, host;
+	u32 num_events = intc->data->num_system_events;
+	u32 num_intrs = intc->data->num_host_intrs;
+	u32 num_regs = DIV_ROUND_UP(num_events, 32);
+	u32 *sysevt_mask = NULL;
+	u32 ch_mask = 0;
+	u32 host_mask = 0;
+	int ret = 0;
+	u32 val;
+
+	if (!intc)
+		return -EINVAL;
+
+	sysevt_mask = kcalloc(num_regs, sizeof(*sysevt_mask), GFP_KERNEL);
+	if (!sysevt_mask)
+		return -ENOMEM;
+
+	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 < num_events; 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[i / 32] |= BIT(i % 32);
+		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 < num_intrs; 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)));
+	}
+
+	if (num_events == MAX_PRU_SYS_EVENTS) {
+		dev_info(dev, "configured system_events[63-0] = 0x%08x.%08x",
+			 sysevt_mask[1], sysevt_mask[0]);
+	} else if (num_events == MAX_PRU_SYS_EVENTS_K3) {
+		dev_info(dev, "configured system_events[159-0] = 0x%08x.%08x.%08x.%08x.%08x",
+			 sysevt_mask[4], sysevt_mask[3],  sysevt_mask[2],
+			 sysevt_mask[1],  sysevt_mask[0]);
+	}
+	dev_info(dev, "configured intr_channels = 0x%08x host_intr = 0x%08x\n",
+		 ch_mask, host_mask);
+
+	/* enable system events, writing 0 has no-effect */
+	for (i = 0; i < num_regs; i++) {
+		pruss_intc_write_reg(intc, PRU_INTC_ESR(i), sysevt_mask[i]);
+		pruss_intc_write_reg(intc, PRU_INTC_SECR(i), sysevt_mask[i]);
+	}
+
+	/* enable host interrupts */
+	for (i = 0; i < num_intrs; 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;
+
+unlock:
+	mutex_unlock(&intc->lock);
+	kfree(sysevt_mask);
+	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;
+	u32 num_events = intc->data->num_system_events;
+	u32 num_intrs = intc->data->num_host_intrs;
+	u32 num_regs = DIV_ROUND_UP(num_events, 32);
+	u32 *sysevt_mask = NULL;
+	u32 host_mask = 0;
+
+	if (!intc)
+		return -EINVAL;
+
+	sysevt_mask = kcalloc(num_regs, sizeof(*sysevt_mask), GFP_KERNEL);
+	if (!sysevt_mask)
+		return -ENOMEM;
+
+	mutex_lock(&intc->lock);
+
+	for (i = 0; i < num_events; 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[i / 32] |= BIT(i % 32);
+	}
+
+	for (i = 0; i < num_intrs; 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);
+	}
+
+	if (num_events == MAX_PRU_SYS_EVENTS) {
+		dev_info(dev, "unconfigured system_events[63-0] = 0x%08x.%08x",
+			 sysevt_mask[1], sysevt_mask[0]);
+	} else if (num_events == MAX_PRU_SYS_EVENTS_K3) {
+		dev_info(dev, "unconfigured system_events[159-0] = 0x%08x.%08x.%08x.%08x.%08x",
+			 sysevt_mask[4], sysevt_mask[3],  sysevt_mask[2],
+			 sysevt_mask[1],  sysevt_mask[0]);
+	}
+	dev_info(dev, "unconfigured host_intr = 0x%08x\n", host_mask);
+
+	for (i = 0; i < num_regs; i++) {
+		/* disable system events, writing 0 has no-effect */
+		pruss_intc_write_reg(intc, PRU_INTC_ECR(i), sysevt_mask[i]);
+		/* clear any pending status */
+		pruss_intc_write_reg(intc, PRU_INTC_SECR(i), sysevt_mask[i]);
+	}
+
+	/* 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);
+	kfree(sysevt_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pruss_intc_unconfigure);
+
+static void pruss_intc_init(struct pruss_intc *intc)
+{
+	int i;
+	int num_chnl_map_regs = DIV_ROUND_UP(intc->data->num_system_events, 4);
+	int num_host_intr_regs = DIV_ROUND_UP(intc->data->num_host_intrs, 4);
+	int num_event_type_regs =
+			DIV_ROUND_UP(intc->data->num_system_events, 32);
+
+	/*
+	 * configure polarity (SIPR register) to active high and
+	 * type (SITR register) to pulse interrupt for all system events
+	 */
+	for (i = 0; i < num_event_type_regs; i++) {
+		pruss_intc_write_reg(intc, PRU_INTC_SIPR(i), 0xffffffff);
+		pruss_intc_write_reg(intc, PRU_INTC_SITR(i), 0);
+	}
+
+	/* clear all interrupt channel map registers, 4 events per register */
+	for (i = 0; i < num_chnl_map_regs; i++)
+		pruss_intc_write_reg(intc, PRU_INTC_CMR(i), 0);
+
+	/* clear all host interrupt map registers, 4 channels per register */
+	for (i = 0; i < num_host_intr_regs; 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;
+	u8 max_system_events;
+
+	data = of_device_get_match_data(dev);
+	if (!data)
+		return -ENODEV;
+
+	skip_host7 = data->no_host7_intr;
+	max_system_events = data->num_system_events;
+
+	intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL);
+	if (!intc)
+		return -ENOMEM;
+	intc->data = data;
+	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;
+
+	intc->domain = irq_domain_add_linear(dev->of_node, max_system_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);
+	u8 max_system_events = intc->data->num_system_events;
+	unsigned int hwirq;
+
+	if (intc->domain) {
+		for (hwirq = 0; hwirq < max_system_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 am335x_am57xx_pruss_intc_data = {
+	.num_system_events = 64,
+	.num_host_intrs = 10,
+	.no_host7_intr = false,
+};
+
+static const struct pruss_intc_match_data am437x_k2g_pruss_intc_data = {
+	.num_system_events = 64,
+	.num_host_intrs = 10,
+	.no_host7_intr = true,
+};
+
+static const struct pruss_intc_match_data am65x_icssg_intc_data = {
+	.num_system_events = 160,
+	.num_host_intrs = 20,
+	.no_host7_intr = false,
+};
+
+static const struct of_device_id pruss_intc_of_match[] = {
+	{
+		.compatible = "ti,am3356-pruss-intc",
+		.data = &am335x_am57xx_pruss_intc_data,
+	},
+	{
+		.compatible = "ti,am4376-pruss-intc",
+		.data = &am437x_k2g_pruss_intc_data,
+	},
+	{
+		.compatible = "ti,am5728-pruss-intc",
+		.data = &am335x_am57xx_pruss_intc_data,
+	},
+	{
+		.compatible = "ti,k2g-pruss-intc",
+		.data = &am437x_k2g_pruss_intc_data,
+	},
+	{
+		.compatible = "ti,am654-icssg-intc",
+		.data = &am65x_icssg_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");

+ 1 - 1
drivers/mailbox/Kconfig

@@ -43,7 +43,7 @@ config PL320_MBOX
 
 
 config OMAP2PLUS_MBOX
 config OMAP2PLUS_MBOX
 	tristate "OMAP2+ Mailbox framework support"
 	tristate "OMAP2+ Mailbox framework support"
-	depends on ARCH_OMAP2PLUS
+	depends on ARCH_OMAP2PLUS || ARCH_K3
 	help
 	help
 	  Mailbox implementation for OMAP family chips with hardware for
 	  Mailbox implementation for OMAP family chips with hardware for
 	  interprocessor communication involving DSP, IVA1.0 and IVA2 in
 	  interprocessor communication involving DSP, IVA1.0 and IVA2 in

+ 26 - 17
drivers/mailbox/omap-mailbox.c

@@ -3,7 +3,7 @@
  * OMAP mailbox driver
  * OMAP mailbox driver
  *
  *
  * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
  * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
- * Copyright (C) 2013-2016 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2013-2018 Texas Instruments Incorporated - http://www.ti.com
  *
  *
  * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
  * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
  *          Suman Anna <s-anna@ti.com>
  *          Suman Anna <s-anna@ti.com>
@@ -141,14 +141,14 @@ void mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs)
 }
 }
 
 
 /* Mailbox FIFO handle functions */
 /* Mailbox FIFO handle functions */
-static mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
+static u32 mbox_fifo_read(struct omap_mbox *mbox)
 {
 {
 	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
 	struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
 
 
-	return (mbox_msg_t)mbox_read_reg(mbox->parent, fifo->msg);
+	return mbox_read_reg(mbox->parent, fifo->msg);
 }
 }
 
 
-static void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
+static void mbox_fifo_write(struct omap_mbox *mbox, u32 msg)
 {
 {
 	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
 	struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
 
 
@@ -256,14 +256,16 @@ static void mbox_rx_work(struct work_struct *work)
 {
 {
 	struct omap_mbox_queue *mq =
 	struct omap_mbox_queue *mq =
 			container_of(work, struct omap_mbox_queue, work);
 			container_of(work, struct omap_mbox_queue, work);
-	mbox_msg_t msg;
+	mbox_msg_t data;
+	u32 msg;
 	int len;
 	int len;
 
 
 	while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
 	while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
 		len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
 		len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
 		WARN_ON(len != sizeof(msg));
 		WARN_ON(len != sizeof(msg));
+		data = msg;
 
 
-		mbox_chan_received_data(mq->mbox->chan, (void *)msg);
+		mbox_chan_received_data(mq->mbox->chan, (void *)data);
 		spin_lock_irq(&mq->lock);
 		spin_lock_irq(&mq->lock);
 		if (mq->full) {
 		if (mq->full) {
 			mq->full = false;
 			mq->full = false;
@@ -286,7 +288,7 @@ static void __mbox_tx_interrupt(struct omap_mbox *mbox)
 static void __mbox_rx_interrupt(struct omap_mbox *mbox)
 static void __mbox_rx_interrupt(struct omap_mbox *mbox)
 {
 {
 	struct omap_mbox_queue *mq = mbox->rxq;
 	struct omap_mbox_queue *mq = mbox->rxq;
-	mbox_msg_t msg;
+	u32 msg;
 	int len;
 	int len;
 
 
 	while (!mbox_fifo_empty(mbox)) {
 	while (!mbox_fifo_empty(mbox)) {
@@ -542,13 +544,13 @@ static void omap_mbox_chan_shutdown(struct mbox_chan *chan)
 	mutex_unlock(&mdev->cfg_lock);
 	mutex_unlock(&mdev->cfg_lock);
 }
 }
 
 
-static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, void *data)
+static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg)
 {
 {
 	int ret = -EBUSY;
 	int ret = -EBUSY;
 
 
 	if (!mbox_fifo_full(mbox)) {
 	if (!mbox_fifo_full(mbox)) {
 		_omap_mbox_enable_irq(mbox, IRQ_RX);
 		_omap_mbox_enable_irq(mbox, IRQ_RX);
-		mbox_fifo_write(mbox, (mbox_msg_t)data);
+		mbox_fifo_write(mbox, msg);
 		ret = 0;
 		ret = 0;
 		_omap_mbox_disable_irq(mbox, IRQ_RX);
 		_omap_mbox_disable_irq(mbox, IRQ_RX);
 
 
@@ -560,12 +562,12 @@ static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, void *data)
 	return ret;
 	return ret;
 }
 }
 
 
-static int omap_mbox_chan_send(struct omap_mbox *mbox, void *data)
+static int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg)
 {
 {
 	int ret = -EBUSY;
 	int ret = -EBUSY;
 
 
 	if (!mbox_fifo_full(mbox)) {
 	if (!mbox_fifo_full(mbox)) {
-		mbox_fifo_write(mbox, (mbox_msg_t)data);
+		mbox_fifo_write(mbox, msg);
 		ret = 0;
 		ret = 0;
 	}
 	}
 
 
@@ -578,14 +580,15 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
 {
 {
 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
 	struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan);
 	int ret;
 	int ret;
+	u32 msg = to_omap_mbox_msg(data);
 
 
 	if (!mbox)
 	if (!mbox)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (mbox->send_no_irq)
 	if (mbox->send_no_irq)
-		ret = omap_mbox_chan_send_noirq(mbox, data);
+		ret = omap_mbox_chan_send_noirq(mbox, msg);
 	else
 	else
-		ret = omap_mbox_chan_send(mbox, data);
+		ret = omap_mbox_chan_send(mbox, msg);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -658,6 +661,10 @@ static const struct of_device_id omap_mailbox_of_match[] = {
 		.compatible	= "ti,omap4-mailbox",
 		.compatible	= "ti,omap4-mailbox",
 		.data		= &omap4_data,
 		.data		= &omap4_data,
 	},
 	},
+	{
+		.compatible	= "ti,am654-mailbox",
+		.data		= &omap4_data,
+	},
 	{
 	{
 		/* end */
 		/* end */
 	},
 	},
@@ -832,7 +839,10 @@ static int omap_mbox_probe(struct platform_device *pdev)
 	mdev->intr_type = intr_type;
 	mdev->intr_type = intr_type;
 	mdev->mboxes = list;
 	mdev->mboxes = list;
 
 
-	/* OMAP does not have a Tx-Done IRQ, but rather a Tx-Ready IRQ */
+	/*
+	 * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready
+	 * IRQ and is needed to run the Tx state machine
+	 */
 	mdev->controller.txdone_irq = true;
 	mdev->controller.txdone_irq = true;
 	mdev->controller.dev = mdev->dev;
 	mdev->controller.dev = mdev->dev;
 	mdev->controller.ops = &omap_mbox_chan_ops;
 	mdev->controller.ops = &omap_mbox_chan_ops;
@@ -901,9 +911,8 @@ static int __init omap_mbox_init(void)
 		return err;
 		return err;
 
 
 	/* kfifo size sanity check: alignment and minimal size */
 	/* kfifo size sanity check: alignment and minimal size */
-	mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
-	mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
-							sizeof(mbox_msg_t));
+	mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32));
+	mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32));
 
 
 	err = platform_driver_register(&omap_mbox_driver);
 	err = platform_driver_register(&omap_mbox_driver);
 	if (err)
 	if (err)

+ 14 - 0
drivers/remoteproc/Kconfig

@@ -85,6 +85,20 @@ config KEYSTONE_REMOTEPROC
 	  It's safe to say N here if you're not interested in the Keystone
 	  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.
 	  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 || ARCH_K3
+	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
 config QCOM_ADSP_PIL
 	tristate "Qualcomm ADSP Peripheral Image Loader"
 	tristate "Qualcomm ADSP Peripheral Image Loader"
 	depends on OF && ARCH_QCOM
 	depends on OF && ARCH_QCOM

+ 1 - 0
drivers/remoteproc/Makefile

@@ -14,6 +14,7 @@ obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
 obj-$(CONFIG_KEYSTONE_REMOTEPROC)	+= keystone_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_ADSP_PIL)		+= qcom_adsp_pil.o
 obj-$(CONFIG_QCOM_RPROC_COMMON)		+= qcom_common.o
 obj-$(CONFIG_QCOM_RPROC_COMMON)		+= qcom_common.o
 obj-$(CONFIG_QCOM_Q6V5_COMMON)		+= qcom_q6v5.o
 obj-$(CONFIG_QCOM_Q6V5_COMMON)		+= qcom_q6v5.o

+ 1 - 1
drivers/remoteproc/imx_rproc.c

@@ -211,7 +211,7 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
 	return -ENOENT;
 	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;
 	struct imx_rproc *priv = rproc->priv;
 	void *va = NULL;
 	void *va = NULL;

+ 6 - 3
drivers/remoteproc/keystone_remoteproc.c

@@ -197,7 +197,8 @@ static void keystone_rproc_mem_del_attrs(struct keystone_rproc *ksproc)
 	}
 	}
 }
 }
 
 
-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);
 
 
 /* uio handler dealing with userspace controlled exception interrupt */
 /* uio handler dealing with userspace controlled exception interrupt */
 static irqreturn_t keystone_rproc_uio_handler(int irq, struct uio_info *uio)
 static irqreturn_t keystone_rproc_uio_handler(int irq, struct uio_info *uio)
@@ -360,7 +361,8 @@ static int keystone_rproc_set_loaded_rsc_table(struct keystone_rproc *ksproc,
 	if (!ksproc->rsc_table_size || !ksproc->rsc_table)
 	if (!ksproc->rsc_table_size || !ksproc->rsc_table)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	ptr = keystone_rproc_da_to_va(rproc, dma_addr, ksproc->rsc_table_size);
+	ptr = keystone_rproc_da_to_va(rproc, dma_addr, ksproc->rsc_table_size,
+				      RPROC_FLAGS_NONE);
 	if (!ptr)
 	if (!ptr)
 		return -EINVAL;
 		return -EINVAL;
 
 
@@ -793,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
  * can be used either by the remoteproc core for loading (when using kernel
  * remoteproc loader), or by any rpmsg bus drivers.
  * 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;
 	struct keystone_rproc *ksproc = rproc->priv;
 	void __iomem *va = NULL;
 	void __iomem *va = NULL;

+ 1306 - 0
drivers/remoteproc/pru_rproc.c

@@ -0,0 +1,1306 @@
+// 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/omap-mailbox.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/RTU Core IRAM address masks */
+#define PRU0_IRAM_ADDR_MASK	0x34000
+#define PRU1_IRAM_ADDR_MASK	0x38000
+#define RTU0_IRAM_ADDR_MASK	0x4000
+#define RTU1_IRAM_ADDR_MASK	0x6000
+
+/**
+ * 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
+ * @is_k3: boolean flag used to indicate the core has increased number of events
+ * @is_rtu: boolean flag to indicate the core is a RTU core
+ */
+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;
+	unsigned int is_k3 : 1;
+	unsigned int is_rtu : 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";
+	u8 max_system_events, max_pru_channels, max_pru_host_ints;
+	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;
+	}
+
+	max_system_events = pru->is_k3 ?
+				MAX_PRU_SYS_EVENTS_K3 : MAX_PRU_SYS_EVENTS;
+	max_pru_channels = pru->is_k3 ? MAX_PRU_CHANNELS_K3 : MAX_PRU_CHANNELS;
+	max_pru_host_ints = pru->is_k3 ? MAX_PRU_HOST_INT_K3 : MAX_PRU_HOST_INT;
+
+	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_system_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_ints) {
+			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") &&
+	    !strstr(dev_name(&pdev->dev), "rtu")) {
+		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") &&
+	    !strstr(dev_name(rproc->dev.parent), "rtu"))
+		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") &&
+	    !strstr(dev_name(rproc->dev.parent), "rtu"))
+		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 = to_omap_mbox_msg(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;
+	mbox_msg_t msg = (mbox_msg_t)vq_id;
+	const char *name = pru->is_rtu ? "RTU" : "PRU";
+
+	dev_dbg(dev, "kicking vqid %d on %s%d\n", vq_id, name, 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 *)msg);
+		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;
+	const char *name = pru->is_rtu ? "RTU" : "PRU";
+	u32 val;
+	int ret;
+
+	dev_dbg(dev, "starting %s%d: entry-point = 0x%x\n",
+		name, 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;
+	const char *name = pru->is_rtu ? "RTU" : "PRU";
+	u32 val;
+
+	dev_dbg(dev, "stopping %s%d\n", name, 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_rsc0;
+	struct fw_rsc_pruss_intrmap_k3 *intr_rsc1;
+	int i, ret;
+	u32 event_chnl_map_da, event_chnl_map_size;
+	s8 sys_evt, chnl, intr_no;
+	s8 *chnl_host_intr_map;
+	u8 max_system_events, max_pru_channels, max_pru_host_ints;
+
+	if (rsc->u.st.st_ver != 0 && rsc->u.st.st_ver != 1) {
+		dev_err(dev, "only PRU interrupt resource versions 0 and 1 are supported\n");
+		return -EINVAL;
+	}
+
+	if (!rsc->u.st.st_ver) {
+		intr_rsc0 = (struct fw_rsc_pruss_intrmap *)rsc->data;
+		event_chnl_map_da = intr_rsc0->event_chnl_map_addr;
+		event_chnl_map_size = intr_rsc0->event_chnl_map_size;
+		chnl_host_intr_map = intr_rsc0->chnl_host_intr_map;
+		max_system_events = MAX_PRU_SYS_EVENTS;
+		max_pru_channels = MAX_PRU_CHANNELS;
+		max_pru_host_ints = MAX_PRU_HOST_INT;
+
+		dev_dbg(dev, "version %d event_chnl_map_size %d event_chnl_map_da 0x%x\n",
+			rsc->u.st.st_ver, intr_rsc0->event_chnl_map_size,
+			event_chnl_map_da);
+	} else {
+		intr_rsc1 = (struct fw_rsc_pruss_intrmap_k3 *)rsc->data;
+		event_chnl_map_da = intr_rsc1->event_chnl_map_addr;
+		event_chnl_map_size = intr_rsc1->event_chnl_map_size;
+		chnl_host_intr_map = intr_rsc1->chnl_host_intr_map;
+		max_system_events = MAX_PRU_SYS_EVENTS_K3;
+		max_pru_channels = MAX_PRU_CHANNELS_K3;
+		max_pru_host_ints = MAX_PRU_HOST_INT_K3;
+
+		dev_dbg(dev, "version %d event_chnl_map_size %d event_chnl_map_da 0x%x\n",
+			rsc->u.st.st_ver, intr_rsc1->event_chnl_map_size,
+			event_chnl_map_da);
+	}
+
+	if (event_chnl_map_size < 0 ||
+	    event_chnl_map_size >= max_system_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, event_chnl_map_da,
+					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 < 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_system_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 = 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_ints) {
+			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,
+};
+
+/*
+ * Custom memory copy implementation for ICSSG PRU/RTU Cores
+ *
+ * The ICSSG PRU/RTU cores have a memory copying issue with IRAM memories, that
+ * is not seen on previous generation SoCs. The data is reflected properly in
+ * the IRAM memories only for integer (4-byte) copies. Any unaligned copies
+ * result in all the other pre-existing bytes zeroed out within that 4-byte
+ * boundary, thereby resulting in wrong text/code in the IRAMs. Also, the
+ * IRAM memory port interface does not allow any 8-byte copies (as commonly
+ * used by ARM64 memcpy implementation) and throws an exception. The DRAM
+ * memory ports do not show this behavior. Use this custom copying function
+ * to properly load the PRU/RTU firmware images on all memories for simplicity.
+ *
+ * TODO: Improve the function to deal with additional corner cases like
+ * unaligned copy sizes or sub-integer trailing bytes when the need arises.
+ */
+static int pru_rproc_memcpy(void *dest, const void *src, size_t count)
+{
+	const int *s = src;
+	int *d = dest;
+	int size = count / 4;
+	int *tmp_src = NULL;
+
+	/* limited to 4-byte aligned addresses and copy sizes */
+	if ((long)dest % 4 || count % 4)
+		return -EINVAL;
+
+	/* src offsets in ELF firmware image can be non-aligned */
+	if ((long)src % 4) {
+		tmp_src = kmemdup(src, count, GFP_KERNEL);
+		if (!tmp_src)
+			return -ENOMEM;
+		s = tmp_src;
+	}
+
+	while (size--)
+		*d++ = *s++;
+
+	kfree(tmp_src);
+
+	return 0;
+}
+
+static int
+pru_rproc_load_elf_segments(struct rproc *rproc, const struct firmware *fw)
+{
+	struct device *dev = &rproc->dev;
+	struct elf32_hdr *ehdr;
+	struct elf32_phdr *phdr;
+	int i, ret = 0;
+	const u8 *elf_data = fw->data;
+
+	ehdr = (struct elf32_hdr *)elf_data;
+	phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
+
+	/* go through the available ELF segments */
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		u32 da = phdr->p_paddr;
+		u32 memsz = phdr->p_memsz;
+		u32 filesz = phdr->p_filesz;
+		u32 offset = phdr->p_offset;
+		void *ptr;
+
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+			phdr->p_type, da, memsz, filesz);
+
+		if (filesz > memsz) {
+			dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+				filesz, memsz);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (offset + filesz > fw->size) {
+			dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
+				offset + filesz, fw->size);
+			ret = -EINVAL;
+			break;
+		}
+
+		/* grab the kernel address for this device address */
+		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;
+			break;
+		}
+
+		/* skip the memzero logic performed by remoteproc ELF loader */
+		if (!phdr->p_filesz)
+			continue;
+
+		ret = pru_rproc_memcpy(ptr, elf_data + phdr->p_offset, filesz);
+		if (ret) {
+			dev_err(dev, "PRU custom memory copy failed for da 0x%x memsz 0x%x\n",
+				da, memsz);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * 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 device_node *np, struct pru_rproc *pru)
+{
+	int ret = 0;
+	u32 mask1 = PRU0_IRAM_ADDR_MASK;
+	u32 mask2 = PRU1_IRAM_ADDR_MASK;
+
+	if (of_device_is_compatible(np, "ti,am654-rtu")) {
+		mask1 = RTU0_IRAM_ADDR_MASK;
+		mask2 = RTU1_IRAM_ADDR_MASK;
+		pru->is_rtu = 1;
+	}
+
+	if ((pru->mem_regions[PRU_IOMEM_IRAM].pa & mask2) == mask2)
+		pru->id = PRUSS_PRU1;
+	else if ((pru->mem_regions[PRU_IOMEM_IRAM].pa & mask1) == mask1)
+		pru->id = PRUSS_PRU0;
+	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);
+
+	if (of_device_is_compatible(np, "ti,am654-pru") ||
+	    of_device_is_compatible(np, "ti,am654-rtu")) {
+		/* use generic elf ops for undefined platform driver ops */
+		rproc->ops->load = pru_rproc_load_elf_segments;
+
+		pru->is_k3 = 1;
+	}
+
+	/* 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(np, 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",    },
+	{ .compatible = "ti,am654-pru",  },
+	{ .compatible = "ti,am654-rtu",  },
+	{},
+};
+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");

+ 92 - 0
drivers/remoteproc/pru_rproc.h

@@ -0,0 +1,92 @@
+/* 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;
+};
+
+/**
+ * struct fw_rsc_pruss_intrmap_k3 - K3 custom resource to define PRU interrupts
+ * @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).
+ *
+ * This structure needs to be used using PRU vendor interrupt resource version
+ * number 1. This structure is to be used with firmwares dealing with the
+ * additional host interrupts on ICSSG IP instances. The firmwares for PRU
+ * cores on ICSSG can get away with the standard version (if not dealing with
+ * Task Manager), but the firmwares for RTU cores would definitely need this
+ * for mapping to the corresponding higher host interrupts.
+ */
+struct fw_rsc_pruss_intrmap_k3 {
+	s8 chnl_host_intr_map[20];
+	u32 event_chnl_map_size;
+	u32 event_chnl_map_addr;
+};
+
+#endif	/* _PRU_RPROC_H_ */

+ 1 - 1
drivers/remoteproc/qcom_adsp_pil.c

@@ -167,7 +167,7 @@ static int adsp_stop(struct rproc *rproc)
 	return ret;
 	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;
 	struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
 	int offset;
 	int offset;

+ 1 - 1
drivers/remoteproc/qcom_q6v5_pil.c

@@ -976,7 +976,7 @@ static int q6v5_stop(struct rproc *rproc)
 	return 0;
 	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;
 	struct q6v5 *qproc = rproc->priv;
 	int offset;
 	int offset;

+ 1 - 1
drivers/remoteproc/qcom_q6v5_wcss.c

@@ -406,7 +406,7 @@ static int q6v5_wcss_stop(struct rproc *rproc)
 	return 0;
 	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;
 	struct q6v5_wcss *wcss = rproc->priv;
 	int offset;
 	int offset;

+ 1 - 1
drivers/remoteproc/qcom_wcnss.c

@@ -295,7 +295,7 @@ static int wcnss_stop(struct rproc *rproc)
 	return ret;
 	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;
 	struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
 	int offset;
 	int offset;

+ 123 - 5
drivers/remoteproc/remoteproc_core.c

@@ -147,6 +147,7 @@ static void rproc_disable_iommu(struct rproc *rproc)
  * @rproc: handle of a remote processor
  * @rproc: handle of a remote processor
  * @da: remoteproc device address to translate
  * @da: remoteproc device address to translate
  * @len: length of the memory region @da is pointing to
  * @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
  * Some remote processors will ask us to allocate them physically contiguous
  * memory regions (which we call "carveouts"), and map them to specific
  * memory regions (which we call "carveouts"), and map them to specific
@@ -162,7 +163,10 @@ static void rproc_disable_iommu(struct rproc *rproc)
  * carveouts and translate specific device addresses to kernel virtual addresses
  * carveouts and translate specific device addresses to kernel virtual addresses
  * so we can access the referenced memory. This function also allows to perform
  * so we can access the referenced memory. This function also allows to perform
  * translations on the internal remoteproc memory regions through a platform
  * 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.
  * The function returns a valid kernel address on success or NULL on failure.
  *
  *
@@ -171,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
  * here the output of the DMA API for the carveouts, which should be more
  * correct.
  * 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;
 	struct rproc_mem_entry *carveout;
 	void *ptr = NULL;
 	void *ptr = NULL;
 
 
 	if (rproc->ops->da_to_va) {
 	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)
 		if (ptr)
 			goto out;
 			goto out;
 	}
 	}
@@ -527,7 +531,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
 	}
 	}
 
 
 	/* what's the kernel address of this resource ? */
 	/* 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) {
 	if (!ptr) {
 		dev_err(dev, "erroneous trace resource entry\n");
 		dev_err(dev, "erroneous trace resource entry\n");
 		return -EINVAL;
 		return -EINVAL;
@@ -782,6 +786,42 @@ free_carv:
 	return ret;
 	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
  * A lookup table for resource handlers. The indices are defined in
  * enum fw_resource_type.
  * enum fw_resource_type.
@@ -790,9 +830,15 @@ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
 	[RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
 	[RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
 	[RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
 	[RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
 	[RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
 	[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,
 	[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 */
 /* handle firmware resource entries before booting the remote processor */
 static int rproc_handle_resources(struct rproc *rproc,
 static int rproc_handle_resources(struct rproc *rproc,
 				  rproc_handle_resource_t handlers[RSC_LAST])
 				  rproc_handle_resource_t handlers[RSC_LAST])
@@ -998,6 +1044,14 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw)
 		rproc->table_ptr = loaded_table;
 		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);
 	ret = rproc_prepare_subdevices(rproc);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "failed to prepare subdevices for %s: %d\n",
 		dev_err(dev, "failed to prepare subdevices for %s: %d\n",
@@ -1249,7 +1303,8 @@ static void rproc_coredump(struct rproc *rproc)
 		phdr->p_flags = PF_R | PF_W | PF_X;
 		phdr->p_flags = PF_R | PF_W | PF_X;
 		phdr->p_align = 0;
 		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) {
 		if (!ptr) {
 			dev_err(&rproc->dev,
 			dev_err(&rproc->dev,
 				"invalid coredump segment (%pad, %zu)\n",
 				"invalid coredump segment (%pad, %zu)\n",
@@ -1878,6 +1933,69 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
 }
 }
 EXPORT_SYMBOL(rproc_report_crash);
 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)
 static int __init remoteproc_init(void)
 {
 {
 	rproc_init_sysfs();
 	rproc_init_sysfs();

+ 13 - 1
drivers/remoteproc/remoteproc_debugfs.c

@@ -158,13 +158,15 @@ static const struct file_operations rproc_recovery_ops = {
 /* Expose resource table content via debugfs */
 /* Expose resource table content via debugfs */
 static int rproc_rsc_table_show(struct seq_file *seq, void *p)
 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 rproc *rproc = seq->private;
 	struct resource_table *table = rproc->table_ptr;
 	struct resource_table *table = rproc->table_ptr;
 	struct fw_rsc_carveout *c;
 	struct fw_rsc_carveout *c;
 	struct fw_rsc_devmem *d;
 	struct fw_rsc_devmem *d;
 	struct fw_rsc_trace *t;
 	struct fw_rsc_trace *t;
 	struct fw_rsc_vdev *v;
 	struct fw_rsc_vdev *v;
+	struct fw_rsc_vendor *vr;
 	int i, j;
 	int i, j;
 
 
 	if (!table) {
 	if (!table) {
@@ -230,6 +232,16 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p)
 					   v->vring[j].pa);
 					   v->vring[j].pa);
 			}
 			}
 			break;
 			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:
 		default:
 			seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n",
 			seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n",
 				   hdr->type, hdr);
 				   hdr->type, hdr);

+ 4 - 2
drivers/remoteproc/remoteproc_elf_loader.c

@@ -182,7 +182,8 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
 		}
 		}
 
 
 		/* grab the kernel address for this device address */
 		/* 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) {
 		if (!ptr) {
 			dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
 			dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
 			ret = -EINVAL;
 			ret = -EINVAL;
@@ -333,6 +334,7 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
 	if (!shdr)
 	if (!shdr)
 		return NULL;
 		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);
 EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);

+ 17 - 32
drivers/remoteproc/remoteproc_sysfs.c

@@ -11,6 +11,7 @@
  * GNU General Public License for more details.
  * GNU General Public License for more details.
  */
  */
 
 
+#include <linux/module.h>
 #include <linux/remoteproc.h>
 #include <linux/remoteproc.h>
 
 
 #include "remoteproc_internal.h"
 #include "remoteproc_internal.h"
@@ -32,42 +33,13 @@ static ssize_t firmware_store(struct device *dev,
 			      const char *buf, size_t count)
 			      const char *buf, size_t count)
 {
 {
 	struct rproc *rproc = to_rproc(dev);
 	struct rproc *rproc = to_rproc(dev);
-	char *p;
-	int err, len = count;
+	int err;
 
 
 	/* restrict sysfs operations if not allowed by remoteproc drivers */
 	/* restrict sysfs operations if not allowed by remoteproc drivers */
 	if (rproc->deny_sysfs_ops)
 	if (rproc->deny_sysfs_ops)
 		return -EPERM;
 		return -EPERM;
 
 
-	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;
-	}
-
-	len = strcspn(buf, "\n");
-	if (!len) {
-		dev_err(dev, "can't provide a NULL firmware\n");
-		err = -EINVAL;
-		goto out;
-	}
-
-	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;
 	return err ? err : count;
 }
 }
@@ -113,14 +85,27 @@ static ssize_t state_store(struct device *dev,
 		if (rproc->state == RPROC_RUNNING)
 		if (rproc->state == RPROC_RUNNING)
 			return -EBUSY;
 			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);
 		ret = rproc_boot(rproc);
-		if (ret)
+		if (ret) {
 			dev_err(&rproc->dev, "Boot failed: %d\n", 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")) {
 	} else if (sysfs_streq(buf, "stop")) {
 		if (rproc->state != RPROC_RUNNING)
 		if (rproc->state != RPROC_RUNNING)
 			return -EINVAL;
 			return -EINVAL;
 
 
 		rproc_shutdown(rproc);
 		rproc_shutdown(rproc);
+		if (!rproc->auto_boot)
+			module_put(dev->parent->driver->owner);
 	} else {
 	} else {
 		dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
 		dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
 		ret = -EINVAL;
 		ret = -EINVAL;

+ 2 - 1
drivers/remoteproc/st_slim_rproc.c

@@ -178,7 +178,8 @@ static int slim_rproc_stop(struct rproc *rproc)
 	return 0;
 	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;
 	struct st_slim_rproc *slim_rproc = rproc->priv;
 	void *va = NULL;
 	void *va = NULL;

+ 2 - 1
drivers/remoteproc/wkup_m3_rproc.c

@@ -88,7 +88,8 @@ static int wkup_m3_rproc_stop(struct rproc *rproc)
 	return 0;
 	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;
 	struct wkup_m3_rproc *wkupm3 = rproc->priv;
 	void *va = NULL;
 	void *va = NULL;

+ 11 - 0
drivers/rpmsg/Kconfig

@@ -68,6 +68,17 @@ config RPMSG_RPC
 	  remote calls to more power-efficient remote processors. This is
 	  remote calls to more power-efficient remote processors. This is
 	  currently available only on OMAP4+ systems.
 	  currently available only on OMAP4+ systems.
 
 
+config RPMSG_PRU
+	tristate "PRU RPMsg Communication driver"
+	depends on RPMSG_VIRTIO
+	depends on REMOTEPROC
+	depends on PRU_REMOTEPROC
+	help
+	  An rpmsg driver that exposes interfaces to user space, to allow
+	  applications to communicate with the PRU processors on available
+	  TI SoCs. This is restricted to SoCs that have the PRUSS remoteproc
+	  support.
+
 	  If unsure, say N.
 	  If unsure, say N.
 
 
 endmenu
 endmenu

+ 1 - 0
drivers/rpmsg/Makefile

@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_RPMSG)		+= rpmsg_core.o
 obj-$(CONFIG_RPMSG)		+= rpmsg_core.o
 obj-$(CONFIG_RPMSG_CHAR)	+= rpmsg_char.o
 obj-$(CONFIG_RPMSG_CHAR)	+= rpmsg_char.o
+obj-$(CONFIG_RPMSG_PRU)		+= rpmsg_pru.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o

+ 350 - 0
drivers/rpmsg/rpmsg_pru.c

@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PRU Remote Processor Messaging Driver
+ *
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Jason Reeder <jreeder@ti.com>
+ *	Suman Anna <s-anna@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/kfifo.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/rpmsg/virtio_rpmsg.h>
+
+#define PRU_MAX_DEVICES				(16)
+/* Matches the definition in virtio_rpmsg_bus.c */
+#define RPMSG_BUF_SIZE				(512)
+#define MAX_FIFO_MSG				(32)
+#define FIFO_MSG_SIZE				RPMSG_BUF_SIZE
+
+/**
+ * struct rpmsg_pru_dev - Structure that contains the per-device data
+ * @rpdev: rpmsg channel device that is associated with this rpmsg_pru device
+ * @dev: device
+ * @cdev: character device
+ * @locked: boolean used to determine whether or not the device file is in use
+ * @devt: dev_t structure for the rpmsg_pru device
+ * @msg_fifo: kernel fifo used to buffer the messages between userspace and PRU
+ * @msg_len: array storing the lengths of each message in the kernel fifo
+ * @msg_idx_rd: kernel fifo read index
+ * @msg_idx_wr: kernel fifo write index
+ * @wait_list: wait queue used to implement the poll operation of the character
+ *             device
+ *
+ * Each rpmsg_pru device provides an interface, using an rpmsg channel (rpdev),
+ * between a user space character device (cdev) and a PRU core. A kernel fifo
+ * (msg_fifo) is used to buffer the messages in the kernel that are
+ * being passed between the character device and the PRU.
+ */
+struct rpmsg_pru_dev {
+	struct rpmsg_device *rpdev;
+	struct device *dev;
+	struct cdev cdev;
+	bool locked;
+	dev_t devt;
+	struct kfifo msg_fifo;
+	u32 msg_len[MAX_FIFO_MSG];
+	int msg_idx_rd;
+	int msg_idx_wr;
+	wait_queue_head_t wait_list;
+};
+
+static struct class *rpmsg_pru_class;
+static dev_t rpmsg_pru_devt;
+static DEFINE_MUTEX(rpmsg_pru_lock);
+static DEFINE_IDR(rpmsg_pru_minors);
+
+static int rpmsg_pru_open(struct inode *inode, struct file *filp)
+{
+	struct rpmsg_pru_dev *prudev;
+	int ret = -EACCES;
+
+	prudev = container_of(inode->i_cdev, struct rpmsg_pru_dev, cdev);
+
+	mutex_lock(&rpmsg_pru_lock);
+	if (!prudev->locked) {
+		prudev->locked = true;
+		filp->private_data = prudev;
+		ret = 0;
+	}
+	mutex_unlock(&rpmsg_pru_lock);
+
+	if (ret)
+		dev_err(prudev->dev, "Device already open\n");
+
+	return ret;
+}
+
+static int rpmsg_pru_release(struct inode *inode, struct file *filp)
+{
+	struct rpmsg_pru_dev *prudev;
+
+	prudev = container_of(inode->i_cdev, struct rpmsg_pru_dev, cdev);
+	mutex_lock(&rpmsg_pru_lock);
+	prudev->locked = false;
+	mutex_unlock(&rpmsg_pru_lock);
+	return 0;
+}
+
+static ssize_t rpmsg_pru_read(struct file *filp, char __user *buf,
+			      size_t count, loff_t *f_pos)
+{
+	int ret;
+	u32 length;
+	struct rpmsg_pru_dev *prudev;
+
+	prudev = filp->private_data;
+
+	if (kfifo_is_empty(&prudev->msg_fifo) &&
+	    (filp->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	ret = wait_event_interruptible(prudev->wait_list,
+				       !kfifo_is_empty(&prudev->msg_fifo));
+	if (ret)
+		return -EINTR;
+
+	ret = kfifo_to_user(&prudev->msg_fifo, buf,
+			    prudev->msg_len[prudev->msg_idx_rd], &length);
+	prudev->msg_idx_rd = (prudev->msg_idx_rd + 1) % MAX_FIFO_MSG;
+
+	return ret ? ret : length;
+}
+
+static ssize_t rpmsg_pru_write(struct file *filp, const char __user *buf,
+			       size_t count, loff_t *f_pos)
+{
+	int ret;
+	struct rpmsg_pru_dev *prudev;
+	static char rpmsg_pru_buf[RPMSG_BUF_SIZE];
+
+	prudev = filp->private_data;
+
+	if (count > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) {
+		dev_err(prudev->dev, "Data too large for RPMsg Buffer\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(rpmsg_pru_buf, buf, count)) {
+		dev_err(prudev->dev, "Error copying buffer from user space");
+		return -EFAULT;
+	}
+
+	ret = rpmsg_send(prudev->rpdev->ept, (void *)rpmsg_pru_buf, count);
+	if (ret)
+		dev_err(prudev->dev, "rpmsg_send failed: %d\n", ret);
+
+	return ret ? ret : count;
+}
+
+static unsigned int rpmsg_pru_poll(struct file *filp,
+				   struct poll_table_struct *wait)
+{
+	int mask;
+	struct rpmsg_pru_dev *prudev;
+
+	prudev = filp->private_data;
+
+	poll_wait(filp, &prudev->wait_list, wait);
+
+	mask = POLLOUT | POLLWRNORM;
+
+	if (!kfifo_is_empty(&prudev->msg_fifo))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static const struct file_operations rpmsg_pru_fops = {
+	.owner = THIS_MODULE,
+	.open = rpmsg_pru_open,
+	.release = rpmsg_pru_release,
+	.read = rpmsg_pru_read,
+	.write = rpmsg_pru_write,
+	.poll = rpmsg_pru_poll,
+};
+
+static int rpmsg_pru_cb(struct rpmsg_device *rpdev, void *data, int len,
+			void *priv, u32 src)
+{
+	u32 length;
+	struct rpmsg_pru_dev *prudev;
+
+	prudev = dev_get_drvdata(&rpdev->dev);
+
+	if (kfifo_avail(&prudev->msg_fifo) < len) {
+		dev_err(&rpdev->dev, "Not enough space on the FIFO\n");
+		return -ENOSPC;
+	}
+
+	if ((prudev->msg_idx_wr + 1) % MAX_FIFO_MSG ==
+		prudev->msg_idx_rd) {
+		dev_err(&rpdev->dev, "Message length table is full\n");
+		return -ENOSPC;
+	}
+
+	length = kfifo_in(&prudev->msg_fifo, data, len);
+	prudev->msg_len[prudev->msg_idx_wr] = length;
+	prudev->msg_idx_wr = (prudev->msg_idx_wr + 1) % MAX_FIFO_MSG;
+
+	wake_up_interruptible(&prudev->wait_list);
+
+	return 0;
+}
+
+static int rpmsg_pru_probe(struct rpmsg_device *rpdev)
+{
+	int ret;
+	struct rpmsg_pru_dev *prudev;
+	int minor_got;
+
+	prudev = devm_kzalloc(&rpdev->dev, sizeof(*prudev), GFP_KERNEL);
+	if (!prudev)
+		return -ENOMEM;
+
+	mutex_lock(&rpmsg_pru_lock);
+	minor_got = idr_alloc(&rpmsg_pru_minors, prudev, 0, PRU_MAX_DEVICES,
+			      GFP_KERNEL);
+	mutex_unlock(&rpmsg_pru_lock);
+	if (minor_got < 0) {
+		ret = minor_got;
+		dev_err(&rpdev->dev, "Failed to get a minor number for the rpmsg_pru device: %d\n",
+			ret);
+		goto fail_alloc_minor;
+	}
+
+	prudev->devt = MKDEV(MAJOR(rpmsg_pru_devt), minor_got);
+
+	cdev_init(&prudev->cdev, &rpmsg_pru_fops);
+	prudev->cdev.owner = THIS_MODULE;
+	ret = cdev_add(&prudev->cdev, prudev->devt, 1);
+	if (ret) {
+		dev_err(&rpdev->dev, "Unable to add cdev for the rpmsg_pru device\n");
+		goto fail_add_cdev;
+	}
+
+	prudev->dev = device_create(rpmsg_pru_class, &rpdev->dev, prudev->devt,
+				    NULL, "rpmsg_pru%d", rpdev->dst);
+	if (IS_ERR(prudev->dev)) {
+		dev_err(&rpdev->dev, "Unable to create the rpmsg_pru device\n");
+		ret = PTR_ERR(prudev->dev);
+		goto fail_create_device;
+	}
+
+	prudev->rpdev = rpdev;
+
+	ret = kfifo_alloc(&prudev->msg_fifo, MAX_FIFO_MSG * FIFO_MSG_SIZE,
+			  GFP_KERNEL);
+	if (ret) {
+		dev_err(&rpdev->dev, "Unable to allocate fifo for the rpmsg_pru device\n");
+		goto fail_alloc_fifo;
+	}
+
+	init_waitqueue_head(&prudev->wait_list);
+
+	dev_set_drvdata(&rpdev->dev, prudev);
+
+	dev_info(&rpdev->dev, "new rpmsg_pru device: /dev/rpmsg_pru%d",
+		 rpdev->dst);
+
+	return 0;
+
+fail_alloc_fifo:
+	device_destroy(rpmsg_pru_class, prudev->devt);
+fail_create_device:
+	cdev_del(&prudev->cdev);
+fail_add_cdev:
+	mutex_lock(&rpmsg_pru_lock);
+	idr_remove(&rpmsg_pru_minors, minor_got);
+	mutex_unlock(&rpmsg_pru_lock);
+fail_alloc_minor:
+	return ret;
+}
+
+static void rpmsg_pru_remove(struct rpmsg_device *rpdev)
+{
+	struct rpmsg_pru_dev *prudev;
+
+	prudev = dev_get_drvdata(&rpdev->dev);
+
+	kfifo_free(&prudev->msg_fifo);
+	device_destroy(rpmsg_pru_class, prudev->devt);
+	cdev_del(&prudev->cdev);
+	mutex_lock(&rpmsg_pru_lock);
+	idr_remove(&rpmsg_pru_minors, MINOR(prudev->devt));
+	mutex_unlock(&rpmsg_pru_lock);
+}
+
+/* .name matches on RPMsg Channels and causes a probe */
+static const struct rpmsg_device_id rpmsg_driver_pru_id_table[] = {
+	{ .name	= "rpmsg-pru" },
+	{ },
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_pru_id_table);
+
+static struct rpmsg_driver rpmsg_pru_driver = {
+	.drv.name	= KBUILD_MODNAME,
+	.id_table	= rpmsg_driver_pru_id_table,
+	.probe		= rpmsg_pru_probe,
+	.callback	= rpmsg_pru_cb,
+	.remove		= rpmsg_pru_remove,
+};
+
+static int __init rpmsg_pru_init(void)
+{
+	int ret;
+
+	rpmsg_pru_class = class_create(THIS_MODULE, "rpmsg_pru");
+	if (IS_ERR(rpmsg_pru_class)) {
+		pr_err("Unable to create class\n");
+		ret = PTR_ERR(rpmsg_pru_class);
+		goto fail_create_class;
+	}
+
+	ret = alloc_chrdev_region(&rpmsg_pru_devt, 0, PRU_MAX_DEVICES,
+				  "rpmsg_pru");
+	if (ret) {
+		pr_err("Unable to allocate chrdev region\n");
+		goto fail_alloc_region;
+	}
+
+	ret = register_rpmsg_driver(&rpmsg_pru_driver);
+	if (ret) {
+		pr_err("Unable to register rpmsg driver");
+		goto fail_register_rpmsg_driver;
+	}
+
+	return 0;
+
+fail_register_rpmsg_driver:
+	unregister_chrdev_region(rpmsg_pru_devt, PRU_MAX_DEVICES);
+fail_alloc_region:
+	class_destroy(rpmsg_pru_class);
+fail_create_class:
+	return ret;
+}
+
+static void __exit rpmsg_pru_exit(void)
+{
+	unregister_rpmsg_driver(&rpmsg_pru_driver);
+	idr_destroy(&rpmsg_pru_minors);
+	mutex_destroy(&rpmsg_pru_lock);
+	class_destroy(rpmsg_pru_class);
+	unregister_chrdev_region(rpmsg_pru_devt, PRU_MAX_DEVICES);
+}
+
+module_init(rpmsg_pru_init);
+module_exit(rpmsg_pru_exit);
+
+MODULE_AUTHOR("Jason Reeder <jreeder@ti.com>");
+MODULE_DESCRIPTION("PRU Remote Processor Messaging Driver");
+MODULE_LICENSE("GPL v2");

+ 14 - 5
drivers/rpmsg/virtio_rpmsg_bus.c

@@ -186,6 +186,7 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
 
 
 /**
 /**
  * rpmsg_sg_init - initialize scatterlist according to cpu address location
  * rpmsg_sg_init - initialize scatterlist according to cpu address location
+ * @vrp: virtual remoteproc structure used with this buffer
  * @sg: scatterlist to fill
  * @sg: scatterlist to fill
  * @cpu_addr: virtual address of the buffer
  * @cpu_addr: virtual address of the buffer
  * @len: buffer length
  * @len: buffer length
@@ -194,9 +195,17 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
  * location (in vmalloc or in kernel).
  * location (in vmalloc or in kernel).
  */
  */
 static void
 static void
-rpmsg_sg_init(struct scatterlist *sg, void *cpu_addr, unsigned int len)
+rpmsg_sg_init(struct virtproc_info *vrp, struct scatterlist *sg,
+	      void *cpu_addr, unsigned int len)
 {
 {
-	if (is_vmalloc_addr(cpu_addr)) {
+	if (of_machine_is_compatible("ti,keystone")) {
+		unsigned long offset = cpu_addr - vrp->rbufs;
+		dma_addr_t dma_addr = vrp->bufs_dma + offset;
+
+		sg_init_table(sg, 1);
+		sg_set_page(sg, pfn_to_page(PHYS_PFN(dma_addr)), len,
+			    offset_in_page(dma_addr));
+	} else if (is_vmalloc_addr(cpu_addr)) {
 		sg_init_table(sg, 1);
 		sg_init_table(sg, 1);
 		sg_set_page(sg, vmalloc_to_page(cpu_addr), len,
 		sg_set_page(sg, vmalloc_to_page(cpu_addr), len,
 			    offset_in_page(cpu_addr));
 			    offset_in_page(cpu_addr));
@@ -626,7 +635,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
 			 msg, sizeof(*msg) + msg->len, true);
 			 msg, sizeof(*msg) + msg->len, true);
 #endif
 #endif
 
 
-	rpmsg_sg_init(&sg, msg, sizeof(*msg) + len);
+	rpmsg_sg_init(vrp, &sg, msg, sizeof(*msg) + len);
 
 
 	mutex_lock(&vrp->tx_lock);
 	mutex_lock(&vrp->tx_lock);
 
 
@@ -750,7 +759,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
 		dev_warn(dev, "msg received with no recipient\n");
 		dev_warn(dev, "msg received with no recipient\n");
 
 
 	/* publish the real size of the buffer */
 	/* publish the real size of the buffer */
-	rpmsg_sg_init(&sg, msg, vrp->buf_size);
+	rpmsg_sg_init(vrp, &sg, msg, vrp->buf_size);
 
 
 	/* add the buffer back to the remote processor's virtqueue */
 	/* add the buffer back to the remote processor's virtqueue */
 	err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
 	err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
@@ -945,7 +954,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
 		struct scatterlist sg;
 		struct scatterlist sg;
 		void *cpu_addr = vrp->rbufs + i * vrp->buf_size;
 		void *cpu_addr = vrp->rbufs + i * vrp->buf_size;
 
 
-		rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);
+		rpmsg_sg_init(vrp, &sg, cpu_addr, vrp->buf_size);
 
 
 		err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
 		err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
 					  GFP_KERNEL);
 					  GFP_KERNEL);

+ 11 - 0
drivers/soc/ti/Kconfig

@@ -89,6 +89,17 @@ config TI_SCI_PM_DOMAINS
 	  called ti_sci_pm_domains. Note this is needed early in boot before
 	  called ti_sci_pm_domains. Note this is needed early in boot before
 	  rootfs may be available.
 	  rootfs may be available.
 
 
+config TI_PRUSS
+	tristate "TI PRU-ICSS Subsystem Platform drivers"
+	depends on SOC_AM33XX || SOC_AM43XX || SOC_DRA7XX || ARCH_KEYSTONE || ARCH_K3
+	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.
+
 config TI_K3_RINGACC
 config TI_K3_RINGACC
 	tristate "K3 Ring accelerator Sub System"
 	tristate "K3 Ring accelerator Sub System"
 	depends on ARCH_K3 || COMPILE_TEST
 	depends on ARCH_K3 || COMPILE_TEST

+ 1 - 0
drivers/soc/ti/Makefile

@@ -9,5 +9,6 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA)	+= knav_dma.o
 obj-$(CONFIG_AMX3_PM)			+= pm33xx.o
 obj-$(CONFIG_AMX3_PM)			+= pm33xx.o
 obj-$(CONFIG_WKUP_M3_IPC)		+= wkup_m3_ipc.o
 obj-$(CONFIG_WKUP_M3_IPC)		+= wkup_m3_ipc.o
 obj-$(CONFIG_TI_SCI_PM_DOMAINS)		+= ti_sci_pm_domains.o
 obj-$(CONFIG_TI_SCI_PM_DOMAINS)		+= ti_sci_pm_domains.o
+obj-$(CONFIG_TI_PRUSS)			+= pruss_soc_bus.o pruss.o
 obj-$(CONFIG_TI_K3_RINGACC)		+= k3-ringacc.o
 obj-$(CONFIG_TI_K3_RINGACC)		+= k3-ringacc.o
 obj-$(CONFIG_TI_K3_UDMA_DESC_POOL)	+= k3-navss-desc-pool.o
 obj-$(CONFIG_TI_K3_UDMA_DESC_POOL)	+= k3-navss-desc-pool.o

+ 453 - 0
drivers/soc/ti/pruss.c

@@ -0,0 +1,453 @@
+// 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 or RTU */
+	if (!strstr(dev_name(dev->parent), "pru") &&
+	    !strstr(dev_name(dev->parent), "rtu"))
+		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);
+
+/* Custom configuration of couple of PRUSS clocks only on AM65x SoCs */
+static int pruss_configure_clocks(struct platform_device *pdev,
+				  struct pruss *pruss)
+{
+	int ret;
+
+	if (!of_device_is_compatible(pdev->dev.of_node, "ti,am654-icssg"))
+		return 0;
+
+	ret = pruss_regmap_update(pruss, PRUSS_SYSCON_CFG, ICSSG_CFG_CORE_SYNC,
+				  ICSSG_CORE_VBUSP_SYNC_EN,
+				  ICSSG_CORE_VBUSP_SYNC_EN);
+	if (ret)
+		return ret;
+
+	ret = pruss_regmap_update(pruss, PRUSS_SYSCON_CFG, PRUSS_CFG_IEPCLK,
+				  PRUSS_IEPCLK_IEP_OCP_CLK_EN,
+				  PRUSS_IEPCLK_IEP_OCP_CLK_EN);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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);
+
+	ret = pruss_configure_clocks(pdev, pruss);
+	if (ret) {
+		dev_err(dev, "clock frequency config failed, ret = %d\n", ret);
+		return ret;
+	}
+
+	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, },
+	{ .compatible = "ti,am654-icssg", .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");

+ 315 - 0
drivers/soc/ti/pruss_soc_bus.c

@@ -0,0 +1,315 @@
+// 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, },
+	{ .compatible = "ti,am654-icssg-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");

+ 2 - 3
drivers/soc/ti/wkup_m3_ipc.c

@@ -109,9 +109,8 @@ static unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc,
 	void *aux_data_addr;
 	void *aux_data_addr;
 
 
 	aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET;
 	aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET;
-	aux_data_addr = rproc_da_to_va(m3_ipc->rproc,
-				       aux_data_dev_addr,
-				       WKUP_M3_AUXDATA_SIZE);
+	aux_data_addr = rproc_da_to_va(m3_ipc->rproc, aux_data_dev_addr,
+				       WKUP_M3_AUXDATA_SIZE, RPROC_FLAGS_NONE);
 	memcpy(aux_data_addr, data, sz);
 	memcpy(aux_data_addr, data, sz);
 
 
 	return WKUP_M3_AUXDATA_OFFSET;
 	return WKUP_M3_AUXDATA_OFFSET;

+ 6 - 0
include/linux/omap-mailbox.h

@@ -6,7 +6,13 @@
 #ifndef OMAP_MAILBOX_H
 #ifndef OMAP_MAILBOX_H
 #define OMAP_MAILBOX_H
 #define OMAP_MAILBOX_H
 
 
+#if defined(CONFIG_ARCH_K3)
+typedef u64 mbox_msg_t;
+#else
 typedef u32 mbox_msg_t;
 typedef u32 mbox_msg_t;
+#endif
+
+#define to_omap_mbox_msg(data) (u32)(*(mbox_msg_t *)(&(data)))
 
 
 typedef int __bitwise omap_mbox_irq_t;
 typedef int __bitwise omap_mbox_irq_t;
 #define IRQ_TX ((__force omap_mbox_irq_t) 1)
 #define IRQ_TX ((__force omap_mbox_irq_t) 1)

+ 25 - 0
include/linux/platform_data/ti-pruss.h

@@ -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 */

+ 302 - 0
include/linux/pruss.h

@@ -0,0 +1,302 @@
+/* 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
+
+#define ICSSG_CFG_CORE_SYNC	0x3c
+
+/* 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)
+
+/* PRUSS_IEPCLK register bits */
+#define PRUSS_IEPCLK_IEP_OCP_CLK_EN		BIT(0)
+
+/* ICSSG CORE_SYNC register bits */
+#define ICSSG_CORE_VBUSP_SYNC_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 */

+ 101 - 0
include/linux/pruss_driver.h

@@ -0,0 +1,101 @@
+/* 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
+#define MAX_PRU_SYS_EVENTS_K3	160
+
+/* maximum number of interrupt channels */
+#define MAX_PRU_CHANNELS	10
+#define MAX_PRU_CHANNELS_K3	20
+
+/* minimum starting host interrupt number for MPU */
+#define MIN_PRU_HOST_INT	2
+
+/* maximum number of host interrupts */
+#define MAX_PRU_HOST_INT	10
+#define MAX_PRU_HOST_INT_K3	20
+
+/**
+ * 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_K3];
+	s8 ch_to_host[MAX_PRU_CHANNELS_K3];
+};
+
+/**
+ * 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_ */

+ 50 - 7
include/linux/remoteproc.h

@@ -41,6 +41,7 @@
 #include <linux/completion.h>
 #include <linux/completion.h>
 #include <linux/idr.h>
 #include <linux/idr.h>
 #include <linux/of.h>
 #include <linux/of.h>
+#include <linux/bitops.h>
 
 
 /**
 /**
  * struct resource_table - firmware resource table header
  * struct resource_table - firmware resource table header
@@ -100,6 +101,10 @@ struct fw_rsc_hdr {
  *		    the remote processor will be writing logs.
  *		    the remote processor will be writing logs.
  * @RSC_VDEV:       declare support for a virtio device, and serve as its
  * @RSC_VDEV:       declare support for a virtio device, and serve as its
  *		    virtio header.
  *		    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
  * @RSC_LAST:       just keep this one at the end
  *
  *
  * For more details regarding a specific resource type, please see its
  * For more details regarding a specific resource type, please see its
@@ -111,11 +116,13 @@ struct fw_rsc_hdr {
  * please update it as needed.
  * please update it as needed.
  */
  */
 enum fw_resource_type {
 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)
 #define FW_RSC_ADDR_ANY (-1)
@@ -305,6 +312,24 @@ struct fw_rsc_vdev {
 	struct fw_rsc_vdev_vring vring[0];
 	struct fw_rsc_vdev_vring vring[0];
 } __packed;
 } __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
  * struct rproc_mem_entry - memory entry descriptor
  * @va:	virtual address
  * @va:	virtual address
@@ -328,6 +353,19 @@ struct rproc_mem_entry {
 struct rproc;
 struct rproc;
 struct firmware;
 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
  * struct rproc_ops - platform-specific device handlers
  * @start:	power on the device and boot it
  * @start:	power on the device and boot it
@@ -336,6 +374,7 @@ struct firmware;
  * @da_to_va:	optional platform hook to perform address translations
  * @da_to_va:	optional platform hook to perform address translations
  * @load_rsc_table:	load resource table from firmware image
  * @load_rsc_table:	load resource table from firmware image
  * @find_loaded_rsc_table: find the loaded resouce table
  * @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
  * @load:		load firmeware to memory, where the remote processor
  *			expects to find it
  *			expects to find it
  * @sanity_check:	sanity check the fw image
  * @sanity_check:	sanity check the fw image
@@ -345,10 +384,12 @@ struct rproc_ops {
 	int (*start)(struct rproc *rproc);
 	int (*start)(struct rproc *rproc);
 	int (*stop)(struct rproc *rproc);
 	int (*stop)(struct rproc *rproc);
 	void (*kick)(struct rproc *rproc, int vqid);
 	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);
 	int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
 	struct resource_table *(*find_loaded_rsc_table)(
 	struct resource_table *(*find_loaded_rsc_table)(
 				struct rproc *rproc, const struct firmware *fw);
 				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 (*load)(struct rproc *rproc, const struct firmware *fw);
 	int (*sanity_check)(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);
 	u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
@@ -441,6 +482,7 @@ struct rproc_dump_segment {
  * @cached_table: copy of the resource table
  * @cached_table: copy of the resource table
  * @table_sz: size of @cached_table
  * @table_sz: size of @cached_table
  * @has_iommu: flag to indicate if remote processor is behind an MMU
  * @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
  * @deny_sysfs_ops: flag to not permit sysfs operations on state and firmware
  * @skip_firmware_request: flag to skip requesting the firmware
  * @skip_firmware_request: flag to skip requesting the firmware
  * @skip_load: flag to skip the loading of firmware segments
  * @skip_load: flag to skip the loading of firmware segments
@@ -563,8 +605,9 @@ void rproc_free(struct rproc *rproc);
 
 
 int rproc_boot(struct rproc *rproc);
 int rproc_boot(struct rproc *rproc);
 void rproc_shutdown(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_report_crash(struct rproc *rproc, enum rproc_crash_type type);
-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);
 int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size);
 int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size);
 int rproc_get_id(struct rproc *rproc);
 int rproc_get_id(struct rproc *rproc);
 int rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da);
 int rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da);

+ 5 - 0
ti_config_fragments/ipc.cfg

@@ -14,15 +14,20 @@ CONFIG_IOMMU_SUPPORT=y
 CONFIG_OMAP_IOMMU=y
 CONFIG_OMAP_IOMMU=y
 CONFIG_OMAP_IOMMU_DEBUG=y
 CONFIG_OMAP_IOMMU_DEBUG=y
 
 
+# SoC
+CONFIG_TI_PRUSS=m
+
 # Remoteproc
 # Remoteproc
 CONFIG_REMOTEPROC=m
 CONFIG_REMOTEPROC=m
 CONFIG_OMAP_REMOTEPROC=m
 CONFIG_OMAP_REMOTEPROC=m
 CONFIG_KEYSTONE_REMOTEPROC=m
 CONFIG_KEYSTONE_REMOTEPROC=m
+CONFIG_PRU_REMOTEPROC=m
 
 
 # RPMsg
 # RPMsg
 CONFIG_RPMSG_VIRTIO=m
 CONFIG_RPMSG_VIRTIO=m
 CONFIG_RPMSG_PROTO=m
 CONFIG_RPMSG_PROTO=m
 CONFIG_RPMSG_RPC=m
 CONFIG_RPMSG_RPC=m
+CONFIG_RPMSG_PRU=m
 
 
 # DSP Memory Mapper for Keystone MPM
 # DSP Memory Mapper for Keystone MPM
 CONFIG_KEYSTONE_DSP_MEM=m
 CONFIG_KEYSTONE_DSP_MEM=m

+ 15 - 0
ti_config_fragments/v8_ipc.cfg

@@ -4,3 +4,18 @@
 # HwSpinLock
 # HwSpinLock
 CONFIG_HWSPINLOCK=y
 CONFIG_HWSPINLOCK=y
 CONFIG_HWSPINLOCK_OMAP=y
 CONFIG_HWSPINLOCK_OMAP=y
+
+# Mailbox
+CONFIG_MAILBOX=y
+CONFIG_OMAP2PLUS_MBOX=y
+
+# SoC Drivers
+CONFIG_TI_PRUSS=m
+
+# Remoteproc
+CONFIG_REMOTEPROC=m
+CONFIG_PRU_REMOTEPROC=m
+
+# RPMsg
+CONFIG_RPMSG_VIRTIO=m
+CONFIG_RPMSG_PRU=m