ARM: SoC-related driver updates
Various driver updates for platforms:
 
  - A larger set of work on Tegra 2/3 around memory controller and
  regulator features, some fuse cleanups, etc..
 
  - MMP platform drivers, in particular for USB PHY, and other smaller
  additions.
 
  - Samsung Exynos 5422 driver for DMC (dynamic memory configuration),
  and ASV (adaptive voltage), allowing the platform to run at more
  optimal operating points.
 
  - Misc refactorings and support for RZ/G2N and R8A774B1 from Renesas
 
  - Clock/reset control driver for TI/OMAP
 
  - Meson-A1 reset controller support
 
  - Qualcomm sdm845 and sda845 SoC IDs for socinfo
 -----BEGIN PGP SIGNATURE-----
 
 iQJDBAABCAAtFiEElf+HevZ4QCAJmMQ+jBrnPN6EHHcFAl3pORkPHG9sb2ZAbGl4
 b20ubmV0AAoJEIwa5zzehBx3FK0P/0EG4lK+il7nE3pd9yIGUjlcYuumIjoxvyC9
 9ef202POJLIO3yMlsNyGFR+aOknFO/GtGvDkDFhTtlsGCL40tVzVsyo7ZQo+8mXD
 abr+H74NmRXImc+SISYR8X1CD6vEi3oi/no1y5dRzknlBikfsdSLKXJSMYBJ2A6t
 DNLwu0h1IZhPk7XQQsxaElG/a9HN8eueMdP20J1IlhOh0GiOwm+rbsLSZNbA/W9m
 53XhFs3Ag39SDE0BfXsS+XOWTE7FheZsZk2XQrOwYm9PnxjpIWH7FE2sYsk6uUIc
 Pa1b6wB5zlRnxvVHP0m3GXhbTUJDYDK3oybHffI4Mzd0cyZQHC92LhUXFrlTxkaf
 6kyhJOTdd5KMlZ2LS7jkwLqb30ieXBPKAREjdbRt6hpvu5P6G+bZQphTEeNAZC61
 XnX8mQ/XeoHdoGY5MvS8ht6a1qDF29ebA0/02seicThGK6tS9Qsju6Zo0sg9H1NH
 weK6jDuzLq5jpv/LB1apigrDSx+zddRzrwkwy85hR5aWOQhG0xjOoFBProbTS0to
 wR46zCEkbGZv4uc0gRuIdp0NR/lguqgDWPeoLluoTqmcpKS6N3RyxD0bWzlvgDFA
 fpYxVNKavHneWjfZ7U5RbYXD6jycJcuLaCOs16nrtUbMgJ9pqclLIaZXn7ZTRIuT
 RW6NgfZV
 =dk7w
 -----END PGP SIGNATURE-----
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Olof Johansson:
 "Various driver updates for platforms:
   - A larger set of work on Tegra 2/3 around memory controller and
     regulator features, some fuse cleanups, etc..
   - MMP platform drivers, in particular for USB PHY, and other smaller
     additions.
   - Samsung Exynos 5422 driver for DMC (dynamic memory configuration),
     and ASV (adaptive voltage), allowing the platform to run at more
     optimal operating points.
   - Misc refactorings and support for RZ/G2N and R8A774B1 from Renesas
   - Clock/reset control driver for TI/OMAP
   - Meson-A1 reset controller support
   - Qualcomm sdm845 and sda845 SoC IDs for socinfo"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (150 commits)
  firmware: arm_scmi: Fix doorbell ring logic for !CONFIG_64BIT
  soc: fsl: add RCPM driver
  dt-bindings: fsl: rcpm: Add 'little-endian' and update Chassis definition
  memory: tegra: Consolidate registers definition into common header
  memory: tegra: Ensure timing control debug features are disabled
  memory: tegra: Introduce Tegra30 EMC driver
  memory: tegra: Do not handle error from wait_for_completion_timeout()
  memory: tegra: Increase handshake timeout on Tegra20
  memory: tegra: Print a brief info message about EMC timings
  memory: tegra: Pre-configure debug register on Tegra20
  memory: tegra: Include io.h instead of iopoll.h
  memory: tegra: Adapt for Tegra20 clock driver changes
  memory: tegra: Don't set EMC rate to maximum on probe for Tegra20
  memory: tegra: Add gr2d and gr3d to DRM IOMMU group
  memory: tegra: Set DMA mask based on supported address bits
  soc: at91: Add Atmel SFR SN (Serial Number) support
  memory: atmel-ebi: switch to SPDX license identifiers
  memory: atmel-ebi: move NUM_CS definition inside EBI driver
  soc: mediatek: Refactor bus protection control
  soc: mediatek: Refactor sram control
  ...
			
			
This commit is contained in:
		
						commit
						ec939e4c94
					
				| @ -103,7 +103,7 @@ the Microchip website: http://www.microchip.com. | ||||
| 
 | ||||
|           * Datasheet | ||||
| 
 | ||||
|           http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11121-32-bit-Cortex-A5-Microcontroller-SAMA5D3_Datasheet.pdf | ||||
|           http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11121-32-bit-Cortex-A5-Microcontroller-SAMA5D3_Datasheet_B.pdf | ||||
| 
 | ||||
|     * ARM Cortex-A5 + NEON based SoCs | ||||
|       - sama5d4 family | ||||
| @ -167,7 +167,7 @@ the Microchip website: http://www.microchip.com. | ||||
| 
 | ||||
|           * Datasheet | ||||
| 
 | ||||
|           http://ww1.microchip.com/downloads/en/DeviceDoc/60001527A.pdf | ||||
|           http://ww1.microchip.com/downloads/en/DeviceDoc/SAM-E70-S70-V70-V71-Family-Data-Sheet-DS60001527D.pdf | ||||
| 
 | ||||
| 
 | ||||
| Linux kernel information | ||||
|  | ||||
| @ -1,41 +0,0 @@ | ||||
| == Introduction== | ||||
| 
 | ||||
| LLCC (Last Level Cache Controller) provides last level of cache memory in SOC, | ||||
| that can be shared by multiple clients. Clients here are different cores in the | ||||
| SOC, the idea is to minimize the local caches at the clients and migrate to | ||||
| common pool of memory. Cache memory is divided into partitions called slices | ||||
| which are assigned to clients. Clients can query the slice details, activate | ||||
| and deactivate them. | ||||
| 
 | ||||
| Properties: | ||||
| - compatible: | ||||
| 	Usage: required | ||||
| 	Value type: <string> | ||||
| 	Definition: must be "qcom,sdm845-llcc" | ||||
| 
 | ||||
| - reg: | ||||
| 	Usage: required | ||||
| 	Value Type: <prop-encoded-array> | ||||
| 	Definition: The first element specifies the llcc base start address and | ||||
| 		    the size of the register region. The second element specifies | ||||
| 		    the llcc broadcast base address and size of the register region. | ||||
| 
 | ||||
| - reg-names: | ||||
|         Usage: required | ||||
|         Value Type: <stringlist> | ||||
|         Definition: Register region names. Must be "llcc_base", "llcc_broadcast_base". | ||||
| 
 | ||||
| - interrupts: | ||||
| 	Usage: required | ||||
| 	Definition: The interrupt is associated with the llcc edac device. | ||||
| 			It's used for llcc cache single and double bit error detection | ||||
| 			and reporting. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| 	cache-controller@1100000 { | ||||
| 		compatible = "qcom,sdm845-llcc"; | ||||
| 		reg = <0x1100000 0x200000>, <0x1300000 0x50000> ; | ||||
| 		reg-names = "llcc_base", "llcc_broadcast_base"; | ||||
| 		interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>; | ||||
| 	}; | ||||
							
								
								
									
										55
									
								
								Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| # SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) | ||||
| %YAML 1.2 | ||||
| --- | ||||
| $id: http://devicetree.org/schemas/arm/msm/qcom,llcc.yaml# | ||||
| $schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| 
 | ||||
| title: Last Level Cache Controller | ||||
| 
 | ||||
| maintainers: | ||||
|   - Rishabh Bhatnagar <rishabhb@codeaurora.org> | ||||
|   - Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org> | ||||
| 
 | ||||
| description: | | ||||
|   LLCC (Last Level Cache Controller) provides last level of cache memory in SoC, | ||||
|   that can be shared by multiple clients. Clients here are different cores in the | ||||
|   SoC, the idea is to minimize the local caches at the clients and migrate to | ||||
|   common pool of memory. Cache memory is divided into partitions called slices | ||||
|   which are assigned to clients. Clients can query the slice details, activate | ||||
|   and deactivate them. | ||||
| 
 | ||||
| properties: | ||||
|   compatible: | ||||
|     enum: | ||||
|       - qcom,sc7180-llcc | ||||
|       - qcom,sdm845-llcc | ||||
| 
 | ||||
|   reg: | ||||
|     items: | ||||
|       - description: LLCC base register region | ||||
|       - description: LLCC broadcast base register region | ||||
| 
 | ||||
|   reg-names: | ||||
|     items: | ||||
|       - const: llcc_base | ||||
|       - const: llcc_broadcast_base | ||||
| 
 | ||||
|   interrupts: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
|   - reg-names | ||||
|   - interrupts | ||||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     #include <dt-bindings/interrupt-controller/arm-gic.h> | ||||
| 
 | ||||
|     cache-controller@1100000 { | ||||
|       compatible = "qcom,sdm845-llcc"; | ||||
|       reg = <0x1100000 0x200000>, <0x1300000 0x50000> ; | ||||
|       reg-names = "llcc_base", "llcc_broadcast_base"; | ||||
|       interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>; | ||||
|     }; | ||||
							
								
								
									
										29
									
								
								Documentation/devicetree/bindings/arm/omap/prm-inst.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Documentation/devicetree/bindings/arm/omap/prm-inst.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| OMAP PRM instance bindings | ||||
| 
 | ||||
| Power and Reset Manager is an IP block on OMAP family of devices which | ||||
| handle the power domains and their current state, and provide reset | ||||
| handling for the domains and/or separate IP blocks under the power domain | ||||
| hierarchy. | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible:	Must contain one of the following: | ||||
| 		"ti,am3-prm-inst" | ||||
| 		"ti,am4-prm-inst" | ||||
| 		"ti,omap4-prm-inst" | ||||
| 		"ti,omap5-prm-inst" | ||||
| 		"ti,dra7-prm-inst" | ||||
| 		and additionally must contain: | ||||
| 		"ti,omap-prm-inst" | ||||
| - reg:		Contains PRM instance register address range | ||||
| 		(base address and length) | ||||
| 
 | ||||
| Optional properties: | ||||
| - #reset-cells:	Should be 1 if the PRM instance in question supports resets. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| prm_dsp2: prm@1b00 { | ||||
| 	compatible = "ti,dra7-prm-inst", "ti,omap-prm-inst"; | ||||
| 	reg = <0x1b00 0x40>; | ||||
| 	#reset-cells = <1>; | ||||
| }; | ||||
| @ -11,7 +11,9 @@ power management service, FPGA service and other platform management | ||||
| services. | ||||
| 
 | ||||
| Required properties: | ||||
|  - compatible:	Must contain:	"xlnx,zynqmp-firmware" | ||||
|  - compatible:	Must contain any of below: | ||||
| 		"xlnx,zynqmp-firmware" for Zynq Ultrascale+ MPSoC | ||||
| 		"xlnx,versal-firmware" for Versal | ||||
|  - method:	The method of calling the PM-API firmware layer. | ||||
| 		Permitted values are: | ||||
| 		  - "smc" : SMC #0, following the SMCCC | ||||
| @ -21,6 +23,8 @@ Required properties: | ||||
| Example | ||||
| ------- | ||||
| 
 | ||||
| Zynq Ultrascale+ MPSoC | ||||
| ---------------------- | ||||
| firmware { | ||||
| 	zynqmp_firmware: zynqmp-firmware { | ||||
| 		compatible = "xlnx,zynqmp-firmware"; | ||||
| @ -28,3 +32,13 @@ firmware { | ||||
| 		... | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| Versal | ||||
| ------ | ||||
| firmware { | ||||
| 	versal_firmware: versal-firmware { | ||||
| 		compatible = "xlnx,versal-firmware"; | ||||
| 		method = "smc"; | ||||
| 		... | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| @ -4,6 +4,7 @@ Required properties: | ||||
| - compatible: should be "amlogic,meson-gxbb-efuse" | ||||
| - clocks: phandle to the efuse peripheral clock provided by the | ||||
| 	  clock controller. | ||||
| - secure-monitor: phandle to the secure-monitor node | ||||
| 
 | ||||
| = Data cells = | ||||
| Are child nodes of eFuse, bindings of which as described in | ||||
| @ -16,6 +17,7 @@ Example: | ||||
| 		clocks = <&clkc CLKID_EFUSE>; | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		secure-monitor = <&sm>; | ||||
| 
 | ||||
| 		sn: sn@14 { | ||||
| 			reg = <0x14 0x10>; | ||||
| @ -30,6 +32,10 @@ Example: | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	sm: secure-monitor { | ||||
| 		compatible = "amlogic,meson-gxbb-sm"; | ||||
| 	}; | ||||
| 
 | ||||
| = Data consumers = | ||||
| Are device nodes which consume nvmem data cells. | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ which then translates it into a corresponding voltage on a rail | ||||
| 
 | ||||
| Required Properties: | ||||
|  - compatible: Should be one of the following | ||||
| 	* qcom,msm8976-rpmpd: RPM Power domain for the msm8976 family of SoC | ||||
| 	* qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC | ||||
| 	* qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC | ||||
| 	* qcom,qcs404-rpmpd: RPM Power domain for the qcs404 family of SoC | ||||
|  | ||||
| @ -4,7 +4,8 @@ The Amlogic Audio ARB is a simple device which enables or | ||||
| disables the access of Audio FIFOs to DDR on AXG based SoC. | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible: 'amlogic,meson-axg-audio-arb' | ||||
| - compatible: 'amlogic,meson-axg-audio-arb' or | ||||
| 	      'amlogic,meson-sm1-audio-arb' | ||||
| - reg: physical base address of the controller and length of memory | ||||
|        mapped region. | ||||
| - clocks: phandle to the fifo peripheral clock provided by the audio | ||||
|  | ||||
| @ -16,6 +16,7 @@ properties: | ||||
|       - amlogic,meson8b-reset # Reset Controller on Meson8b and compatible SoCs | ||||
|       - amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs | ||||
|       - amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs | ||||
|       - amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs | ||||
| 
 | ||||
|   reg: | ||||
|     maxItems: 1 | ||||
|  | ||||
| @ -1,52 +0,0 @@ | ||||
| Qualcomm AOSS Reset Controller | ||||
| ====================================== | ||||
| 
 | ||||
| This binding describes a reset-controller found on AOSS-CC (always on subsystem) | ||||
| for Qualcomm SDM845 SoCs. | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible: | ||||
| 	Usage: required | ||||
| 	Value type: <string> | ||||
| 	Definition: must be: | ||||
| 		    "qcom,sdm845-aoss-cc" | ||||
| 
 | ||||
| - reg: | ||||
| 	Usage: required | ||||
| 	Value type: <prop-encoded-array> | ||||
| 	Definition: must specify the base address and size of the register | ||||
| 	            space. | ||||
| 
 | ||||
| - #reset-cells: | ||||
| 	Usage: required | ||||
| 	Value type: <uint> | ||||
| 	Definition: must be 1; cell entry represents the reset index. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| aoss_reset: reset-controller@c2a0000 { | ||||
| 	compatible = "qcom,sdm845-aoss-cc"; | ||||
| 	reg = <0xc2a0000 0x31000>; | ||||
| 	#reset-cells = <1>; | ||||
| }; | ||||
| 
 | ||||
| Specifying reset lines connected to IP modules | ||||
| ============================================== | ||||
| 
 | ||||
| Device nodes that need access to reset lines should | ||||
| specify them as a reset phandle in their corresponding node as | ||||
| specified in reset.txt. | ||||
| 
 | ||||
| For list of all valid reset indicies see | ||||
| <dt-bindings/reset/qcom,sdm845-aoss.h> | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| modem-pil@4080000 { | ||||
| 	... | ||||
| 
 | ||||
| 	resets = <&aoss_reset AOSS_CC_MSS_RESTART>; | ||||
| 	reset-names = "mss_restart"; | ||||
| 
 | ||||
| 	... | ||||
| }; | ||||
							
								
								
									
										47
									
								
								Documentation/devicetree/bindings/reset/qcom,aoss-reset.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Documentation/devicetree/bindings/reset/qcom,aoss-reset.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||||
| %YAML 1.2 | ||||
| --- | ||||
| $id: http://devicetree.org/schemas/reset/qcom,aoss-reset.yaml# | ||||
| $schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| 
 | ||||
| title: Qualcomm AOSS Reset Controller | ||||
| 
 | ||||
| maintainers: | ||||
|   - Sibi Sankar <sibis@codeaurora.org> | ||||
| 
 | ||||
| description: | ||||
|   The bindings describe the reset-controller found on AOSS-CC (always on | ||||
|   subsystem) for Qualcomm Technologies Inc SoCs. | ||||
| 
 | ||||
| properties: | ||||
|   compatible: | ||||
|     oneOf: | ||||
|       - description: on SC7180 SoCs the following compatibles must be specified | ||||
|         items: | ||||
|           - const: "qcom,sc7180-aoss-cc" | ||||
|           - const: "qcom,sdm845-aoss-cc" | ||||
| 
 | ||||
|       - description: on SDM845 SoCs the following compatibles must be specified | ||||
|         items: | ||||
|           - const: "qcom,sdm845-aoss-cc" | ||||
| 
 | ||||
|   reg: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   '#reset-cells': | ||||
|     const: 1 | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
|   - '#reset-cells' | ||||
| 
 | ||||
| additionalProperties: false | ||||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     aoss_reset: reset-controller@c2a0000 { | ||||
|       compatible = "qcom,sdm845-aoss-cc"; | ||||
|       reg = <0xc2a0000 0x31000>; | ||||
|       #reset-cells = <1>; | ||||
|     }; | ||||
| @ -1,52 +0,0 @@ | ||||
| PDC Global | ||||
| ====================================== | ||||
| 
 | ||||
| This binding describes a reset-controller found on PDC-Global (Power Domain | ||||
| Controller) block for Qualcomm Technologies Inc SDM845 SoCs. | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible: | ||||
| 	Usage: required | ||||
| 	Value type: <string> | ||||
| 	Definition: must be: | ||||
| 		    "qcom,sdm845-pdc-global" | ||||
| 
 | ||||
| - reg: | ||||
| 	Usage: required | ||||
| 	Value type: <prop-encoded-array> | ||||
| 	Definition: must specify the base address and size of the register | ||||
| 	            space. | ||||
| 
 | ||||
| - #reset-cells: | ||||
| 	Usage: required | ||||
| 	Value type: <uint> | ||||
| 	Definition: must be 1; cell entry represents the reset index. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| pdc_reset: reset-controller@b2e0000 { | ||||
| 	compatible = "qcom,sdm845-pdc-global"; | ||||
| 	reg = <0xb2e0000 0x20000>; | ||||
| 	#reset-cells = <1>; | ||||
| }; | ||||
| 
 | ||||
| PDC reset clients | ||||
| ====================================== | ||||
| 
 | ||||
| Device nodes that need access to reset lines should | ||||
| specify them as a reset phandle in their corresponding node as | ||||
| specified in reset.txt. | ||||
| 
 | ||||
| For a list of all valid reset indices see | ||||
| <dt-bindings/reset/qcom,sdm845-pdc.h> | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| modem-pil@4080000 { | ||||
| 	... | ||||
| 
 | ||||
| 	resets = <&pdc_reset PDC_MODEM_SYNC_RESET>; | ||||
| 	reset-names = "pdc_reset"; | ||||
| 
 | ||||
| 	... | ||||
| }; | ||||
							
								
								
									
										47
									
								
								Documentation/devicetree/bindings/reset/qcom,pdc-global.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Documentation/devicetree/bindings/reset/qcom,pdc-global.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||||
| %YAML 1.2 | ||||
| --- | ||||
| $id: http://devicetree.org/schemas/reset/qcom,pdc-global.yaml# | ||||
| $schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| 
 | ||||
| title: Qualcomm PDC Global | ||||
| 
 | ||||
| maintainers: | ||||
|   - Sibi Sankar <sibis@codeaurora.org> | ||||
| 
 | ||||
| description: | ||||
|   The bindings describes the reset-controller found on PDC-Global (Power Domain | ||||
|   Controller) block for Qualcomm Technologies Inc SoCs. | ||||
| 
 | ||||
| properties: | ||||
|   compatible: | ||||
|     oneOf: | ||||
|       - description: on SC7180 SoCs the following compatibles must be specified | ||||
|         items: | ||||
|           - const: "qcom,sc7180-pdc-global" | ||||
|           - const: "qcom,sdm845-pdc-global" | ||||
| 
 | ||||
|       - description: on SDM845 SoCs the following compatibles must be specified | ||||
|         items: | ||||
|           - const: "qcom,sdm845-pdc-global" | ||||
| 
 | ||||
|   reg: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   '#reset-cells': | ||||
|     const: 1 | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
|   - '#reset-cells' | ||||
| 
 | ||||
| additionalProperties: false | ||||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     pdc_reset: reset-controller@b2e0000 { | ||||
|       compatible = "qcom,sdm845-pdc-global"; | ||||
|       reg = <0xb2e0000 0x20000>; | ||||
|       #reset-cells = <1>; | ||||
|     }; | ||||
| @ -130,6 +130,7 @@ this layer. These clocks and resets should be described in each property. | ||||
| Required properties: | ||||
| - compatible: Should be | ||||
|     "socionext,uniphier-pro4-usb3-reset" - for Pro4 SoC USB3 | ||||
|     "socionext,uniphier-pro5-usb3-reset" - for Pro5 SoC USB3 | ||||
|     "socionext,uniphier-pxs2-usb3-reset" - for PXs2 SoC USB3 | ||||
|     "socionext,uniphier-ld20-usb3-reset" - for LD20 SoC USB3 | ||||
|     "socionext,uniphier-pxs3-usb3-reset" - for PXs3 SoC USB3 | ||||
| @ -141,12 +142,12 @@ Required properties: | ||||
| - clocks: A list of phandles to the clock gate for the glue layer. | ||||
| 	According to the clock-names, appropriate clocks are required. | ||||
| - clock-names: Should contain | ||||
|     "gio", "link" - for Pro4 SoC | ||||
|     "gio", "link" - for Pro4 and Pro5 SoCs | ||||
|     "link"        - for others | ||||
| - resets: A list of phandles to the reset control for the glue layer. | ||||
| 	According to the reset-names, appropriate resets are required. | ||||
| - reset-names: Should contain | ||||
|     "gio", "link" - for Pro4 SoC | ||||
|     "gio", "link" - for Pro4 and Pro5 SoCs | ||||
|     "link"        - for others | ||||
| 
 | ||||
| Example: | ||||
|  | ||||
| @ -5,7 +5,7 @@ and power management. | ||||
| 
 | ||||
| Required properites: | ||||
|   - reg : Offset and length of the register set of the RCPM block. | ||||
|   - fsl,#rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the | ||||
|   - #fsl,rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the | ||||
| 	fsl,rcpm-wakeup property. | ||||
|   - compatible : Must contain a chip-specific RCPM block compatible string | ||||
| 	and (if applicable) may contain a chassis-version RCPM compatible | ||||
| @ -20,6 +20,7 @@ Required properites: | ||||
| 	* "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm | ||||
| 	* "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm | ||||
| 	* "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm | ||||
| 	* "fsl,qoriq-rcpm-2.1+": for chassis 2.1+ rcpm | ||||
| 
 | ||||
| All references to "1.0" and "2.0" refer to the QorIQ chassis version to | ||||
| which the chip complies. | ||||
| @ -27,14 +28,19 @@ Chassis Version		Example Chips | ||||
| ---------------		------------------------------- | ||||
| 1.0				p4080, p5020, p5040, p2041, p3041 | ||||
| 2.0				t4240, b4860, b4420 | ||||
| 2.1				t1040, ls1021 | ||||
| 2.1				t1040, | ||||
| 2.1+				ls1021a, ls1012a, ls1043a, ls1046a | ||||
| 
 | ||||
| Optional properties: | ||||
|  - little-endian : RCPM register block is Little Endian. Without it RCPM | ||||
|    will be Big Endian (default case). | ||||
| 
 | ||||
| Example: | ||||
| The RCPM node for T4240: | ||||
| 	rcpm: global-utilities@e2000 { | ||||
| 		compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0"; | ||||
| 		reg = <0xe2000 0x1000>; | ||||
| 		fsl,#rcpm-wakeup-cells = <2>; | ||||
| 		#fsl,rcpm-wakeup-cells = <2>; | ||||
| 	}; | ||||
| 
 | ||||
| * Freescale RCPM Wakeup Source Device Tree Bindings | ||||
| @ -44,7 +50,7 @@ can be used as a wakeup source. | ||||
| 
 | ||||
|   - fsl,rcpm-wakeup: Consists of a phandle to the rcpm node and the IPPDEXPCR | ||||
| 	register cells. The number of IPPDEXPCR register cells is defined in | ||||
| 	"fsl,#rcpm-wakeup-cells" in the rcpm node. The first register cell is | ||||
| 	"#fsl,rcpm-wakeup-cells" in the rcpm node. The first register cell is | ||||
| 	the bit mask that should be set in IPPDEXPCR0, and the second register | ||||
| 	cell is for IPPDEXPCR1, and so on. | ||||
| 
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ resources. | ||||
| 		    "qcom,rpm-apq8084" | ||||
| 		    "qcom,rpm-msm8916" | ||||
| 		    "qcom,rpm-msm8974" | ||||
| 		    "qcom,rpm-msm8976" | ||||
| 		    "qcom,rpm-msm8998" | ||||
| 		    "qcom,rpm-sdm660" | ||||
| 		    "qcom,rpm-qcs404" | ||||
|  | ||||
							
								
								
									
										17
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								MAINTAINERS
									
									
									
									
									
								
							| @ -2140,6 +2140,7 @@ S:	Maintained | ||||
| 
 | ||||
| ARM/QUALCOMM SUPPORT | ||||
| M:	Andy Gross <agross@kernel.org> | ||||
| M:	Bjorn Andersson <bjorn.andersson@linaro.org> | ||||
| L:	linux-arm-msm@vger.kernel.org | ||||
| S:	Maintained | ||||
| F:	Documentation/devicetree/bindings/soc/qcom/ | ||||
| @ -5001,6 +5002,14 @@ F:	include/linux/dma-direct.h | ||||
| F:	include/linux/dma-mapping.h | ||||
| F:	include/linux/dma-noncoherent.h | ||||
| 
 | ||||
| DMC FREQUENCY DRIVER FOR SAMSUNG EXYNOS5422 | ||||
| M:	Lukasz Luba <l.luba@partner.samsung.com> | ||||
| L:	linux-pm@vger.kernel.org | ||||
| L:	linux-samsung-soc@vger.kernel.org | ||||
| S:	Maintained | ||||
| F:	drivers/memory/samsung/exynos5422-dmc.c | ||||
| F:	Documentation/devicetree/bindings/memory-controllers/exynos5422-dmc.txt | ||||
| 
 | ||||
| DME1737 HARDWARE MONITOR DRIVER | ||||
| M:	Juerg Haefliger <juergh@gmail.com> | ||||
| L:	linux-hwmon@vger.kernel.org | ||||
| @ -11066,6 +11075,13 @@ F:	arch/arm/boot/dts/mmp* | ||||
| F:	arch/arm/mach-mmp/ | ||||
| F:	linux/soc/mmp/ | ||||
| 
 | ||||
| MMP USB PHY DRIVERS | ||||
| R:	Lubomir Rintel <lkundrak@v3.sk> | ||||
| L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) | ||||
| S:	Maintained | ||||
| F:	drivers/phy/marvell/phy-mmp3-usb.c | ||||
| F:	drivers/phy/marvell/phy-pxa-usb.c | ||||
| 
 | ||||
| MMU GATHER AND TLB INVALIDATION | ||||
| M:	Will Deacon <will@kernel.org> | ||||
| M:	"Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com> | ||||
| @ -14033,6 +14049,7 @@ F:	include/dt-bindings/reset/ | ||||
| F:	include/linux/reset.h | ||||
| F:	include/linux/reset/ | ||||
| F:	include/linux/reset-controller.h | ||||
| K:      \b(?:devm_|of_)?reset_control(?:ler_[a-z]+|_[a-z_]+)?\b | ||||
| 
 | ||||
| RESTARTABLE SEQUENCES SUPPORT | ||||
| M:	Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||||
|  | ||||
| @ -109,6 +109,7 @@ config ARCH_OMAP2PLUS | ||||
| 	select TI_SYSC | ||||
| 	select OMAP_IRQCHIP | ||||
| 	select CLKSRC_TI_32K | ||||
| 	select ARCH_HAS_RESET_CONTROLLER | ||||
| 	help | ||||
| 	  Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 | ||||
| 
 | ||||
|  | ||||
| @ -247,6 +247,60 @@ void wakeup_source_unregister(struct wakeup_source *ws) | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(wakeup_source_unregister); | ||||
| 
 | ||||
| /**
 | ||||
|  * wakeup_sources_read_lock - Lock wakeup source list for read. | ||||
|  * | ||||
|  * Returns an index of srcu lock for struct wakeup_srcu. | ||||
|  * This index must be passed to the matching wakeup_sources_read_unlock(). | ||||
|  */ | ||||
| int wakeup_sources_read_lock(void) | ||||
| { | ||||
| 	return srcu_read_lock(&wakeup_srcu); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(wakeup_sources_read_lock); | ||||
| 
 | ||||
| /**
 | ||||
|  * wakeup_sources_read_unlock - Unlock wakeup source list. | ||||
|  * @idx: return value from corresponding wakeup_sources_read_lock() | ||||
|  */ | ||||
| void wakeup_sources_read_unlock(int idx) | ||||
| { | ||||
| 	srcu_read_unlock(&wakeup_srcu, idx); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock); | ||||
| 
 | ||||
| /**
 | ||||
|  * wakeup_sources_walk_start - Begin a walk on wakeup source list | ||||
|  * | ||||
|  * Returns first object of the list of wakeup sources. | ||||
|  * | ||||
|  * Note that to be safe, wakeup sources list needs to be locked by calling | ||||
|  * wakeup_source_read_lock() for this. | ||||
|  */ | ||||
| struct wakeup_source *wakeup_sources_walk_start(void) | ||||
| { | ||||
| 	struct list_head *ws_head = &wakeup_sources; | ||||
| 
 | ||||
| 	return list_entry_rcu(ws_head->next, struct wakeup_source, entry); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(wakeup_sources_walk_start); | ||||
| 
 | ||||
| /**
 | ||||
|  * wakeup_sources_walk_next - Get next wakeup source from the list | ||||
|  * @ws: Previous wakeup source object | ||||
|  * | ||||
|  * Note that to be safe, wakeup sources list needs to be locked by calling | ||||
|  * wakeup_source_read_lock() for this. | ||||
|  */ | ||||
| struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws) | ||||
| { | ||||
| 	struct list_head *ws_head = &wakeup_sources; | ||||
| 
 | ||||
| 	return list_next_or_null_rcu(ws_head, &ws->entry, | ||||
| 				struct wakeup_source, entry); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(wakeup_sources_walk_next); | ||||
| 
 | ||||
| /**
 | ||||
|  * device_wakeup_attach - Attach a wakeup source object to a device object. | ||||
|  * @dev: Device to handle. | ||||
|  | ||||
| @ -41,8 +41,9 @@ config MOXTET | ||||
| 
 | ||||
| config HISILICON_LPC | ||||
| 	bool "Support for ISA I/O space on HiSilicon Hip06/7" | ||||
| 	depends on ARM64 && (ARCH_HISI || COMPILE_TEST) | ||||
| 	select INDIRECT_PIO | ||||
| 	depends on (ARM64 && ARCH_HISI) || (COMPILE_TEST && !ALPHA && !HEXAGON && !PARISC && !C6X) | ||||
| 	depends on HAS_IOMEM | ||||
| 	select INDIRECT_PIO if ARM64 | ||||
| 	help | ||||
| 	  Driver to enable I/O access to devices attached to the Low Pin | ||||
| 	  Count bus on the HiSilicon Hip06/7 SoC. | ||||
|  | ||||
| @ -74,7 +74,7 @@ struct hisi_lpc_dev { | ||||
| /* About 10us. This is specific for single IO operations, such as inb */ | ||||
| #define LPC_PEROP_WAITCNT	100 | ||||
| 
 | ||||
| static int wait_lpc_idle(unsigned char *mbase, unsigned int waitcnt) | ||||
| static int wait_lpc_idle(void __iomem *mbase, unsigned int waitcnt) | ||||
| { | ||||
| 	u32 status; | ||||
| 
 | ||||
| @ -209,7 +209,7 @@ static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth) | ||||
| 	struct hisi_lpc_dev *lpcdev = hostdata; | ||||
| 	struct lpc_cycle_para iopara; | ||||
| 	unsigned long addr; | ||||
| 	u32 rd_data = 0; | ||||
| 	__le32 rd_data = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) | ||||
| @ -244,13 +244,12 @@ static void hisi_lpc_comm_out(void *hostdata, unsigned long pio, | ||||
| 	struct lpc_cycle_para iopara; | ||||
| 	const unsigned char *buf; | ||||
| 	unsigned long addr; | ||||
| 	__le32 _val = cpu_to_le32(val); | ||||
| 
 | ||||
| 	if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) | ||||
| 		return; | ||||
| 
 | ||||
| 	val = cpu_to_le32(val); | ||||
| 
 | ||||
| 	buf = (const unsigned char *)&val; | ||||
| 	buf = (const unsigned char *)&_val; | ||||
| 	addr = hisi_lpc_pio_to_addr(lpcdev, pio); | ||||
| 
 | ||||
| 	iopara.opflags = FG_INCRADDR_LPC; | ||||
|  | ||||
| @ -917,6 +917,9 @@ set_midle: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY) | ||||
| 		best_mode = SYSC_IDLE_NO; | ||||
| 
 | ||||
| 	reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); | ||||
| 	reg |= best_mode << regbits->midle_shift; | ||||
| 	sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | ||||
| @ -978,6 +981,9 @@ static int sysc_disable_module(struct device *dev) | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY) | ||||
| 		best_mode = SYSC_IDLE_FORCE; | ||||
| 
 | ||||
| 	reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); | ||||
| 	reg |= best_mode << regbits->midle_shift; | ||||
| 	sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | ||||
| @ -1037,8 +1043,6 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev, | ||||
| 	struct ti_sysc_platform_data *pdata; | ||||
| 	int error; | ||||
| 
 | ||||
| 	reset_control_deassert(ddata->rsts); | ||||
| 
 | ||||
| 	pdata = dev_get_platdata(ddata->dev); | ||||
| 	if (!pdata) | ||||
| 		return 0; | ||||
| @ -1051,6 +1055,8 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev, | ||||
| 		dev_err(dev, "%s: could not enable: %i\n", | ||||
| 			__func__, error); | ||||
| 
 | ||||
| 	reset_control_deassert(ddata->rsts); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -1104,8 +1110,6 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) | ||||
| 
 | ||||
| 	sysc_clkdm_deny_idle(ddata); | ||||
| 
 | ||||
| 	reset_control_deassert(ddata->rsts); | ||||
| 
 | ||||
| 	if (sysc_opt_clks_needed(ddata)) { | ||||
| 		error = sysc_enable_opt_clocks(ddata); | ||||
| 		if (error) | ||||
| @ -1116,6 +1120,8 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) | ||||
| 	if (error) | ||||
| 		goto err_opt_clocks; | ||||
| 
 | ||||
| 	reset_control_deassert(ddata->rsts); | ||||
| 
 | ||||
| 	if (ddata->legacy_mode) { | ||||
| 		error = sysc_runtime_resume_legacy(dev, ddata); | ||||
| 		if (error) | ||||
| @ -1251,6 +1257,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { | ||||
| 	SYSC_QUIRK("gpu", 0x50000000, 0x14, -1, -1, 0x00010201, 0xffffffff, 0), | ||||
| 	SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff, | ||||
| 		   SYSC_MODULE_QUIRK_SGX), | ||||
| 	SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, | ||||
| 		   0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), | ||||
| 	SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -1, 0x4ea2080d, 0xffffffff, | ||||
| 		   SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), | ||||
| 	SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, | ||||
| 		   SYSC_MODULE_QUIRK_WDT), | ||||
| 	/* Watchdog on am3 and am4 */ | ||||
| @ -1309,8 +1319,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { | ||||
| 	SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), | ||||
| 	SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0), | ||||
| 	SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), | ||||
| 	SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, | ||||
| 		   0xffffffff, 0), | ||||
| 	SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0), | ||||
| #endif | ||||
| }; | ||||
| @ -1532,37 +1540,6 @@ static int sysc_legacy_init(struct sysc *ddata) | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * sysc_rstctrl_reset_deassert - deassert rstctrl reset | ||||
|  * @ddata: device driver data | ||||
|  * @reset: reset before deassert | ||||
|  * | ||||
|  * A module can have both OCP softreset control and external rstctrl. | ||||
|  * If more complicated rstctrl resets are needed, please handle these | ||||
|  * directly from the child device driver and map only the module reset | ||||
|  * for the parent interconnect target module device. | ||||
|  * | ||||
|  * Automatic reset of the module on init can be skipped with the | ||||
|  * "ti,no-reset-on-init" device tree property. | ||||
|  */ | ||||
| static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset) | ||||
| { | ||||
| 	int error; | ||||
| 
 | ||||
| 	if (!ddata->rsts) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (reset) { | ||||
| 		error = reset_control_assert(ddata->rsts); | ||||
| 		if (error) | ||||
| 			return error; | ||||
| 	} | ||||
| 
 | ||||
| 	reset_control_deassert(ddata->rsts); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Note that the caller must ensure the interconnect target module is enabled | ||||
|  * before calling reset. Otherwise reset will not complete. | ||||
| @ -1625,15 +1602,6 @@ static int sysc_reset(struct sysc *ddata) | ||||
| static int sysc_init_module(struct sysc *ddata) | ||||
| { | ||||
| 	int error = 0; | ||||
| 	bool manage_clocks = true; | ||||
| 
 | ||||
| 	error = sysc_rstctrl_reset_deassert(ddata, false); | ||||
| 	if (error) | ||||
| 		return error; | ||||
| 
 | ||||
| 	if (ddata->cfg.quirks & | ||||
| 	    (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT)) | ||||
| 		manage_clocks = false; | ||||
| 
 | ||||
| 	error = sysc_clockdomain_init(ddata); | ||||
| 	if (error) | ||||
| @ -1654,7 +1622,7 @@ static int sysc_init_module(struct sysc *ddata) | ||||
| 		goto err_opt_clocks; | ||||
| 
 | ||||
| 	if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) { | ||||
| 		error = sysc_rstctrl_reset_deassert(ddata, true); | ||||
| 		error = reset_control_deassert(ddata->rsts); | ||||
| 		if (error) | ||||
| 			goto err_main_clocks; | ||||
| 	} | ||||
| @ -1666,28 +1634,32 @@ static int sysc_init_module(struct sysc *ddata) | ||||
| 	if (ddata->legacy_mode) { | ||||
| 		error = sysc_legacy_init(ddata); | ||||
| 		if (error) | ||||
| 			goto err_main_clocks; | ||||
| 			goto err_reset; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!ddata->legacy_mode) { | ||||
| 		error = sysc_enable_module(ddata->dev); | ||||
| 		if (error) | ||||
| 			goto err_main_clocks; | ||||
| 			goto err_reset; | ||||
| 	} | ||||
| 
 | ||||
| 	error = sysc_reset(ddata); | ||||
| 	if (error) | ||||
| 		dev_err(ddata->dev, "Reset failed with %d\n", error); | ||||
| 
 | ||||
| 	if (!ddata->legacy_mode && manage_clocks) | ||||
| 	if (error && !ddata->legacy_mode) | ||||
| 		sysc_disable_module(ddata->dev); | ||||
| 
 | ||||
| err_reset: | ||||
| 	if (error && !(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) | ||||
| 		reset_control_assert(ddata->rsts); | ||||
| 
 | ||||
| err_main_clocks: | ||||
| 	if (manage_clocks) | ||||
| 	if (error) | ||||
| 		sysc_disable_main_clocks(ddata); | ||||
| err_opt_clocks: | ||||
| 	/* No re-enable of clockdomain autoidle to prevent module autoidle */ | ||||
| 	if (manage_clocks) { | ||||
| 	if (error) { | ||||
| 		sysc_disable_opt_clocks(ddata); | ||||
| 		sysc_clkdm_allow_idle(ddata); | ||||
| 	} | ||||
| @ -2460,10 +2432,17 @@ static int sysc_probe(struct platform_device *pdev) | ||||
| 		goto unprepare; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Balance reset counts */ | ||||
| 	if (ddata->rsts) | ||||
| 	/* Balance use counts as PM runtime should have enabled these all */ | ||||
| 	if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) | ||||
| 		reset_control_assert(ddata->rsts); | ||||
| 
 | ||||
| 	if (!(ddata->cfg.quirks & | ||||
| 	      (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))) { | ||||
| 		sysc_disable_main_clocks(ddata); | ||||
| 		sysc_disable_opt_clocks(ddata); | ||||
| 		sysc_clkdm_allow_idle(ddata); | ||||
| 	} | ||||
| 
 | ||||
| 	sysc_show_registers(ddata); | ||||
| 
 | ||||
| 	ddata->dev->type = &sysc_device_type; | ||||
|  | ||||
| @ -323,7 +323,7 @@ static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db) | ||||
| 
 | ||||
| 		if (db->mask) | ||||
| 			val = ioread64_hi_lo(db->addr) & db->mask; | ||||
| 		iowrite64_hi_lo(db->set, db->addr); | ||||
| 		iowrite64_hi_lo(db->set | val, db->addr); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| @ -114,7 +114,7 @@ static int imx_dsp_probe(struct platform_device *pdev) | ||||
| 
 | ||||
| 	dev_info(dev, "NXP i.MX DSP IPC initialized\n"); | ||||
| 
 | ||||
| 	return devm_of_platform_populate(dev); | ||||
| 	return 0; | ||||
| out: | ||||
| 	kfree(chan_name); | ||||
| 	for (j = 0; j < i; j++) { | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| 
 | ||||
| #include <dt-bindings/firmware/imx/rsrc.h> | ||||
| #include <linux/firmware/imx/ipc.h> | ||||
| #include <linux/firmware/imx/sci.h> | ||||
| #include <linux/mailbox_client.h> | ||||
| 
 | ||||
| #define IMX_SC_IRQ_FUNC_ENABLE	1 | ||||
|  | ||||
| @ -107,6 +107,12 @@ static void imx_scu_rx_callback(struct mbox_client *c, void *msg) | ||||
| 	struct imx_sc_rpc_msg *hdr; | ||||
| 	u32 *data = msg; | ||||
| 
 | ||||
| 	if (!sc_ipc->msg) { | ||||
| 		dev_warn(sc_ipc->dev, "unexpected rx idx %d 0x%08x, ignore!\n", | ||||
| 				sc_chan->idx, *data); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (sc_chan->idx == 0) { | ||||
| 		hdr = msg; | ||||
| 		sc_ipc->rx_size = hdr->size; | ||||
| @ -156,6 +162,7 @@ static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg) | ||||
|  */ | ||||
| int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) | ||||
| { | ||||
| 	uint8_t saved_svc, saved_func; | ||||
| 	struct imx_sc_rpc_msg *hdr; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -165,7 +172,11 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) | ||||
| 	mutex_lock(&sc_ipc->lock); | ||||
| 	reinit_completion(&sc_ipc->done); | ||||
| 
 | ||||
| 	sc_ipc->msg = msg; | ||||
| 	if (have_resp) { | ||||
| 		sc_ipc->msg = msg; | ||||
| 		saved_svc = ((struct imx_sc_rpc_msg *)msg)->svc; | ||||
| 		saved_func = ((struct imx_sc_rpc_msg *)msg)->func; | ||||
| 	} | ||||
| 	sc_ipc->count = 0; | ||||
| 	ret = imx_scu_ipc_write(sc_ipc, msg); | ||||
| 	if (ret < 0) { | ||||
| @ -184,9 +195,20 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) | ||||
| 		/* response status is stored in hdr->func field */ | ||||
| 		hdr = msg; | ||||
| 		ret = hdr->func; | ||||
| 		/*
 | ||||
| 		 * Some special SCU firmware APIs do NOT have return value | ||||
| 		 * in hdr->func, but they do have response data, those special | ||||
| 		 * APIs are defined as void function in SCU firmware, so they | ||||
| 		 * should be treated as return success always. | ||||
| 		 */ | ||||
| 		if ((saved_svc == IMX_SC_RPC_SVC_MISC) && | ||||
| 			(saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID || | ||||
| 			 saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS)) | ||||
| 			ret = 0; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	sc_ipc->msg = NULL; | ||||
| 	mutex_unlock(&sc_ipc->lock); | ||||
| 
 | ||||
| 	dev_dbg(sc_ipc->dev, "RPC SVC done\n"); | ||||
|  | ||||
| @ -35,7 +35,7 @@ struct meson_sm_chip { | ||||
| 	struct meson_sm_cmd cmd[]; | ||||
| }; | ||||
| 
 | ||||
| struct meson_sm_chip gxbb_chip = { | ||||
| static const struct meson_sm_chip gxbb_chip = { | ||||
| 	.shmem_size		= SZ_4K, | ||||
| 	.cmd_shmem_in_base	= 0x82000020, | ||||
| 	.cmd_shmem_out_base	= 0x82000021, | ||||
| @ -54,8 +54,6 @@ struct meson_sm_firmware { | ||||
| 	void __iomem *sm_shmem_out_base; | ||||
| }; | ||||
| 
 | ||||
| static struct meson_sm_firmware fw; | ||||
| 
 | ||||
| static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, | ||||
| 			    unsigned int cmd_index) | ||||
| { | ||||
| @ -90,6 +88,7 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) | ||||
| /**
 | ||||
|  * meson_sm_call - generic SMC32 call to the secure-monitor | ||||
|  * | ||||
|  * @fw:		Pointer to secure-monitor firmware | ||||
|  * @cmd_index:	Index of the SMC32 function ID | ||||
|  * @ret:	Returned value | ||||
|  * @arg0:	SMC32 Argument 0 | ||||
| @ -100,15 +99,15 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) | ||||
|  * | ||||
|  * Return:	0 on success, a negative value on error | ||||
|  */ | ||||
| int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, | ||||
| 		  u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||||
| int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index, | ||||
| 		  u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||||
| { | ||||
| 	u32 cmd, lret; | ||||
| 
 | ||||
| 	if (!fw.chip) | ||||
| 	if (!fw->chip) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	cmd = meson_sm_get_cmd(fw.chip, cmd_index); | ||||
| 	cmd = meson_sm_get_cmd(fw->chip, cmd_index); | ||||
| 	if (!cmd) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| @ -124,6 +123,7 @@ EXPORT_SYMBOL(meson_sm_call); | ||||
| /**
 | ||||
|  * meson_sm_call_read - retrieve data from secure-monitor | ||||
|  * | ||||
|  * @fw:		Pointer to secure-monitor firmware | ||||
|  * @buffer:	Buffer to store the retrieved data | ||||
|  * @bsize:	Size of the buffer | ||||
|  * @cmd_index:	Index of the SMC32 function ID | ||||
| @ -137,22 +137,23 @@ EXPORT_SYMBOL(meson_sm_call); | ||||
|  *		When 0 is returned there is no guarantee about the amount of | ||||
|  *		data read and bsize bytes are copied in buffer. | ||||
|  */ | ||||
| int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, | ||||
| 		       u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||||
| int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, | ||||
| 		       unsigned int bsize, unsigned int cmd_index, u32 arg0, | ||||
| 		       u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||||
| { | ||||
| 	u32 size; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!fw.chip) | ||||
| 	if (!fw->chip) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	if (!fw.chip->cmd_shmem_out_base) | ||||
| 	if (!fw->chip->cmd_shmem_out_base) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (bsize > fw.chip->shmem_size) | ||||
| 	if (bsize > fw->chip->shmem_size) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) | ||||
| 	if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (size > bsize) | ||||
| @ -164,7 +165,7 @@ int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, | ||||
| 		size = bsize; | ||||
| 
 | ||||
| 	if (buffer) | ||||
| 		memcpy(buffer, fw.sm_shmem_out_base, size); | ||||
| 		memcpy(buffer, fw->sm_shmem_out_base, size); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| @ -173,6 +174,7 @@ EXPORT_SYMBOL(meson_sm_call_read); | ||||
| /**
 | ||||
|  * meson_sm_call_write - send data to secure-monitor | ||||
|  * | ||||
|  * @fw:		Pointer to secure-monitor firmware | ||||
|  * @buffer:	Buffer containing data to send | ||||
|  * @size:	Size of the data to send | ||||
|  * @cmd_index:	Index of the SMC32 function ID | ||||
| @ -184,23 +186,24 @@ EXPORT_SYMBOL(meson_sm_call_read); | ||||
|  * | ||||
|  * Return:	size of sent data on success, a negative value on error | ||||
|  */ | ||||
| int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, | ||||
| 			u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||||
| int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, | ||||
| 			unsigned int size, unsigned int cmd_index, u32 arg0, | ||||
| 			u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||||
| { | ||||
| 	u32 written; | ||||
| 
 | ||||
| 	if (!fw.chip) | ||||
| 	if (!fw->chip) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	if (size > fw.chip->shmem_size) | ||||
| 	if (size > fw->chip->shmem_size) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (!fw.chip->cmd_shmem_in_base) | ||||
| 	if (!fw->chip->cmd_shmem_in_base) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	memcpy(fw.sm_shmem_in_base, buffer, size); | ||||
| 	memcpy(fw->sm_shmem_in_base, buffer, size); | ||||
| 
 | ||||
| 	if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) | ||||
| 	if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (!written) | ||||
| @ -210,6 +213,24 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, | ||||
| } | ||||
| EXPORT_SYMBOL(meson_sm_call_write); | ||||
| 
 | ||||
| /**
 | ||||
|  * meson_sm_get - get pointer to meson_sm_firmware structure. | ||||
|  * | ||||
|  * @sm_node:		Pointer to the secure-monitor Device Tree node. | ||||
|  * | ||||
|  * Return:		NULL is the secure-monitor device is not ready. | ||||
|  */ | ||||
| struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node) | ||||
| { | ||||
| 	struct platform_device *pdev = of_find_device_by_node(sm_node); | ||||
| 
 | ||||
| 	if (!pdev) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return platform_get_drvdata(pdev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(meson_sm_get); | ||||
| 
 | ||||
| #define SM_CHIP_ID_LENGTH	119 | ||||
| #define SM_CHIP_ID_OFFSET	4 | ||||
| #define SM_CHIP_ID_SIZE		12 | ||||
| @ -217,33 +238,25 @@ EXPORT_SYMBOL(meson_sm_call_write); | ||||
| static ssize_t serial_show(struct device *dev, struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	struct platform_device *pdev = to_platform_device(dev); | ||||
| 	struct meson_sm_firmware *fw; | ||||
| 	uint8_t *id_buf; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	fw = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL); | ||||
| 	if (!id_buf) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, | ||||
| 	ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, | ||||
| 				 0, 0, 0, 0, 0); | ||||
| 	if (ret < 0) { | ||||
| 		kfree(id_buf); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 0], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 1], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 2], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 3], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 4], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 5], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 6], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 7], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 8], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 9], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 10], | ||||
| 			id_buf[SM_CHIP_ID_OFFSET + 11]); | ||||
| 	ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]); | ||||
| 
 | ||||
| 	kfree(id_buf); | ||||
| 
 | ||||
| @ -268,25 +281,34 @@ static const struct of_device_id meson_sm_ids[] = { | ||||
| 
 | ||||
| static int __init meson_sm_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	const struct meson_sm_chip *chip; | ||||
| 	struct meson_sm_firmware *fw; | ||||
| 
 | ||||
| 	chip = of_match_device(meson_sm_ids, &pdev->dev)->data; | ||||
| 	fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); | ||||
| 	if (!fw) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	chip = of_match_device(meson_sm_ids, dev)->data; | ||||
| 
 | ||||
| 	if (chip->cmd_shmem_in_base) { | ||||
| 		fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, | ||||
| 							 chip->shmem_size); | ||||
| 		if (WARN_ON(!fw.sm_shmem_in_base)) | ||||
| 		fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, | ||||
| 							  chip->shmem_size); | ||||
| 		if (WARN_ON(!fw->sm_shmem_in_base)) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (chip->cmd_shmem_out_base) { | ||||
| 		fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base, | ||||
| 							  chip->shmem_size); | ||||
| 		if (WARN_ON(!fw.sm_shmem_out_base)) | ||||
| 		fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base, | ||||
| 							   chip->shmem_size); | ||||
| 		if (WARN_ON(!fw->sm_shmem_out_base)) | ||||
| 			goto out_in_base; | ||||
| 	} | ||||
| 
 | ||||
| 	fw.chip = chip; | ||||
| 	fw->chip = chip; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, fw); | ||||
| 
 | ||||
| 	pr_info("secure-monitor enabled\n"); | ||||
| 
 | ||||
| 	if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group)) | ||||
| @ -295,7 +317,7 @@ static int __init meson_sm_probe(struct platform_device *pdev) | ||||
| 	return 0; | ||||
| 
 | ||||
| out_in_base: | ||||
| 	iounmap(fw.sm_shmem_in_base); | ||||
| 	iounmap(fw->sm_shmem_in_base); | ||||
| out: | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| @ -804,7 +804,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev) | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops tegra_bpmp_pm_ops = { | ||||
| 	.resume_early = tegra_bpmp_resume, | ||||
| 	.resume_noirq = tegra_bpmp_resume, | ||||
| }; | ||||
| 
 | ||||
| #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||||
|  | ||||
| @ -711,8 +711,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) | ||||
| 	int ret; | ||||
| 
 | ||||
| 	np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); | ||||
| 	if (!np) | ||||
| 		return 0; | ||||
| 	if (!np) { | ||||
| 		np = of_find_compatible_node(NULL, NULL, "xlnx,versal"); | ||||
| 		if (!np) | ||||
| 			return 0; | ||||
| 	} | ||||
| 	of_node_put(np); | ||||
| 
 | ||||
| 	ret = get_set_conduit_method(dev->of_node); | ||||
| @ -770,6 +773,7 @@ static int zynqmp_firmware_remove(struct platform_device *pdev) | ||||
| 
 | ||||
| static const struct of_device_id zynqmp_firmware_of_match[] = { | ||||
| 	{.compatible = "xlnx,zynqmp-firmware"}, | ||||
| 	{.compatible = "xlnx,versal-firmware"}, | ||||
| 	{}, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match); | ||||
|  | ||||
| @ -1,12 +1,9 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * EBI driver for Atmel chips | ||||
|  * inspired by the fsl weim bus driver | ||||
|  * | ||||
|  * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com> | ||||
|  * | ||||
|  * This file is licensed under the terms of the GNU General Public | ||||
|  * License version 2. This program is licensed "as is" without any | ||||
|  * warranty of any kind, whether express or implied. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/clk.h> | ||||
| @ -19,6 +16,8 @@ | ||||
| #include <linux/regmap.h> | ||||
| #include <soc/at91/atmel-sfr.h> | ||||
| 
 | ||||
| #define AT91_EBI_NUM_CS		8 | ||||
| 
 | ||||
| struct atmel_ebi_dev_config { | ||||
| 	int cs; | ||||
| 	struct atmel_smc_cs_conf smcconf; | ||||
| @ -314,7 +313,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		if (cs >= AT91_MATRIX_EBI_NUM_CS || | ||||
| 		if (cs >= AT91_EBI_NUM_CS || | ||||
| 		    !(ebi->caps->available_cs & BIT(cs))) { | ||||
| 			dev_err(dev, "invalid reg property in %pOF\n", np); | ||||
| 			return -EINVAL; | ||||
| @ -344,7 +343,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, | ||||
| 		apply = true; | ||||
| 
 | ||||
| 	i = 0; | ||||
| 	for_each_set_bit(cs, &cslines, AT91_MATRIX_EBI_NUM_CS) { | ||||
| 	for_each_set_bit(cs, &cslines, AT91_EBI_NUM_CS) { | ||||
| 		ebid->configs[i].cs = cs; | ||||
| 
 | ||||
| 		if (apply) { | ||||
|  | ||||
| @ -127,7 +127,6 @@ enum dpfe_msg_fields { | ||||
| 	MSG_COMMAND, | ||||
| 	MSG_ARG_COUNT, | ||||
| 	MSG_ARG0, | ||||
| 	MSG_CHKSUM, | ||||
| 	MSG_FIELD_MAX	= 16 /* Max number of arguments */ | ||||
| }; | ||||
| 
 | ||||
| @ -180,7 +179,7 @@ struct dpfe_api { | ||||
| }; | ||||
| 
 | ||||
| /* Things we need for as long as we are active. */ | ||||
| struct private_data { | ||||
| struct brcmstb_dpfe_priv { | ||||
| 	void __iomem *regs; | ||||
| 	void __iomem *dmem; | ||||
| 	void __iomem *imem; | ||||
| @ -232,9 +231,13 @@ static struct attribute *dpfe_v3_attrs[] = { | ||||
| }; | ||||
| ATTRIBUTE_GROUPS(dpfe_v3); | ||||
| 
 | ||||
| /* API v2 firmware commands */ | ||||
| static const struct dpfe_api dpfe_api_v2 = { | ||||
| 	.version = 2, | ||||
| /*
 | ||||
|  * Old API v2 firmware commands, as defined in the rev 0.61 specification, we | ||||
|  * use a version set to 1 to denote that it is not compatible with the new API | ||||
|  * v2 and onwards. | ||||
|  */ | ||||
| static const struct dpfe_api dpfe_api_old_v2 = { | ||||
| 	.version = 1, | ||||
| 	.fw_name = "dpfe.bin", | ||||
| 	.sysfs_attrs = dpfe_v2_groups, | ||||
| 	.command = { | ||||
| @ -243,21 +246,42 @@ static const struct dpfe_api dpfe_api_v2 = { | ||||
| 			[MSG_COMMAND] = 1, | ||||
| 			[MSG_ARG_COUNT] = 1, | ||||
| 			[MSG_ARG0] = 1, | ||||
| 			[MSG_CHKSUM] = 4, | ||||
| 		}, | ||||
| 		[DPFE_CMD_GET_REFRESH] = { | ||||
| 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||||
| 			[MSG_COMMAND] = 2, | ||||
| 			[MSG_ARG_COUNT] = 1, | ||||
| 			[MSG_ARG0] = 1, | ||||
| 			[MSG_CHKSUM] = 5, | ||||
| 		}, | ||||
| 		[DPFE_CMD_GET_VENDOR] = { | ||||
| 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||||
| 			[MSG_COMMAND] = 2, | ||||
| 			[MSG_ARG_COUNT] = 1, | ||||
| 			[MSG_ARG0] = 2, | ||||
| 			[MSG_CHKSUM] = 6, | ||||
| 		}, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * API v2 firmware commands, as defined in the rev 0.8 specification, named new | ||||
|  * v2 here | ||||
|  */ | ||||
| static const struct dpfe_api dpfe_api_new_v2 = { | ||||
| 	.version = 2, | ||||
| 	.fw_name = NULL, /* We expect the firmware to have been downloaded! */ | ||||
| 	.sysfs_attrs = dpfe_v2_groups, | ||||
| 	.command = { | ||||
| 		[DPFE_CMD_GET_INFO] = { | ||||
| 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||||
| 			[MSG_COMMAND] = 0x101, | ||||
| 		}, | ||||
| 		[DPFE_CMD_GET_REFRESH] = { | ||||
| 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||||
| 			[MSG_COMMAND] = 0x201, | ||||
| 		}, | ||||
| 		[DPFE_CMD_GET_VENDOR] = { | ||||
| 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||||
| 			[MSG_COMMAND] = 0x202, | ||||
| 		}, | ||||
| 	} | ||||
| }; | ||||
| @ -273,49 +297,51 @@ static const struct dpfe_api dpfe_api_v3 = { | ||||
| 			[MSG_COMMAND] = 0x0101, | ||||
| 			[MSG_ARG_COUNT] = 1, | ||||
| 			[MSG_ARG0] = 1, | ||||
| 			[MSG_CHKSUM] = 0x104, | ||||
| 		}, | ||||
| 		[DPFE_CMD_GET_REFRESH] = { | ||||
| 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||||
| 			[MSG_COMMAND] = 0x0202, | ||||
| 			[MSG_ARG_COUNT] = 0, | ||||
| 			/*
 | ||||
| 			 * This is a bit ugly. Without arguments, the checksum | ||||
| 			 * follows right after the argument count and not at | ||||
| 			 * offset MSG_CHKSUM. | ||||
| 			 */ | ||||
| 			[MSG_ARG0] = 0x203, | ||||
| 		}, | ||||
| 		/* There's no GET_VENDOR command in API v3. */ | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static bool is_dcpu_enabled(void __iomem *regs) | ||||
| static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	val = readl_relaxed(regs + REG_DCPU_RESET); | ||||
| 	mutex_lock(&priv->lock); | ||||
| 	val = readl_relaxed(priv->regs + REG_DCPU_RESET); | ||||
| 	mutex_unlock(&priv->lock); | ||||
| 
 | ||||
| 	return !(val & DCPU_RESET_MASK); | ||||
| } | ||||
| 
 | ||||
| static void __disable_dcpu(void __iomem *regs) | ||||
| static void __disable_dcpu(struct brcmstb_dpfe_priv *priv) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	if (!is_dcpu_enabled(regs)) | ||||
| 	if (!is_dcpu_enabled(priv)) | ||||
| 		return; | ||||
| 
 | ||||
| 	mutex_lock(&priv->lock); | ||||
| 
 | ||||
| 	/* Put DCPU in reset if it's running. */ | ||||
| 	val = readl_relaxed(regs + REG_DCPU_RESET); | ||||
| 	val = readl_relaxed(priv->regs + REG_DCPU_RESET); | ||||
| 	val |= (1 << DCPU_RESET_SHIFT); | ||||
| 	writel_relaxed(val, regs + REG_DCPU_RESET); | ||||
| 	writel_relaxed(val, priv->regs + REG_DCPU_RESET); | ||||
| 
 | ||||
| 	mutex_unlock(&priv->lock); | ||||
| } | ||||
| 
 | ||||
| static void __enable_dcpu(void __iomem *regs) | ||||
| static void __enable_dcpu(struct brcmstb_dpfe_priv *priv) | ||||
| { | ||||
| 	void __iomem *regs = priv->regs; | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	mutex_lock(&priv->lock); | ||||
| 
 | ||||
| 	/* Clear mailbox registers. */ | ||||
| 	writel_relaxed(0, regs + REG_TO_DCPU_MBOX); | ||||
| 	writel_relaxed(0, regs + REG_TO_HOST_MBOX); | ||||
| @ -329,6 +355,8 @@ static void __enable_dcpu(void __iomem *regs) | ||||
| 	val = readl_relaxed(regs + REG_DCPU_RESET); | ||||
| 	val &= ~(1 << DCPU_RESET_SHIFT); | ||||
| 	writel_relaxed(val, regs + REG_DCPU_RESET); | ||||
| 
 | ||||
| 	mutex_unlock(&priv->lock); | ||||
| } | ||||
| 
 | ||||
| static unsigned int get_msg_chksum(const u32 msg[], unsigned int max) | ||||
| @ -343,7 +371,7 @@ static unsigned int get_msg_chksum(const u32 msg[], unsigned int max) | ||||
| 	return sum; | ||||
| } | ||||
| 
 | ||||
| static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, | ||||
| static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response, | ||||
| 				 char *buf, ssize_t *size) | ||||
| { | ||||
| 	unsigned int msg_type; | ||||
| @ -382,7 +410,7 @@ static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, | ||||
| 	return ptr; | ||||
| } | ||||
| 
 | ||||
| static void __finalize_command(struct private_data *priv) | ||||
| static void __finalize_command(struct brcmstb_dpfe_priv *priv) | ||||
| { | ||||
| 	unsigned int release_mbox; | ||||
| 
 | ||||
| @ -390,12 +418,12 @@ static void __finalize_command(struct private_data *priv) | ||||
| 	 * It depends on the API version which MBOX register we have to write to | ||||
| 	 * to signal we are done. | ||||
| 	 */ | ||||
| 	release_mbox = (priv->dpfe_api->version < 3) | ||||
| 	release_mbox = (priv->dpfe_api->version < 2) | ||||
| 			? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX; | ||||
| 	writel_relaxed(0, priv->regs + release_mbox); | ||||
| } | ||||
| 
 | ||||
| static int __send_command(struct private_data *priv, unsigned int cmd, | ||||
| static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd, | ||||
| 			  u32 result[]) | ||||
| { | ||||
| 	const u32 *msg = priv->dpfe_api->command[cmd]; | ||||
| @ -421,9 +449,17 @@ static int __send_command(struct private_data *priv, unsigned int cmd, | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Compute checksum over the message */ | ||||
| 	chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1; | ||||
| 	chksum = get_msg_chksum(msg, chksum_idx); | ||||
| 
 | ||||
| 	/* Write command and arguments to message area */ | ||||
| 	for (i = 0; i < MSG_FIELD_MAX; i++) | ||||
| 		writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); | ||||
| 	for (i = 0; i < MSG_FIELD_MAX; i++) { | ||||
| 		if (i == chksum_idx) | ||||
| 			writel_relaxed(chksum, regs + DCPU_MSG_RAM(i)); | ||||
| 		else | ||||
| 			writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Tell DCPU there is a command waiting */ | ||||
| 	writel_relaxed(1, regs + REG_TO_DCPU_MBOX); | ||||
| @ -517,7 +553,7 @@ static int __verify_firmware(struct init_data *init, | ||||
| 
 | ||||
| /* Verify checksum by reading back the firmware from co-processor RAM. */ | ||||
| static int __verify_fw_checksum(struct init_data *init, | ||||
| 				struct private_data *priv, | ||||
| 				struct brcmstb_dpfe_priv *priv, | ||||
| 				const struct dpfe_firmware_header *header, | ||||
| 				u32 checksum) | ||||
| { | ||||
| @ -571,26 +607,23 @@ static int __write_firmware(u32 __iomem *mem, const u32 *fw, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, | ||||
| 					  struct init_data *init) | ||||
| static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv) | ||||
| { | ||||
| 	const struct dpfe_firmware_header *header; | ||||
| 	unsigned int dmem_size, imem_size; | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct device *dev = priv->dev; | ||||
| 	bool is_big_endian = false; | ||||
| 	struct private_data *priv; | ||||
| 	const struct firmware *fw; | ||||
| 	const u32 *dmem, *imem; | ||||
| 	struct init_data init; | ||||
| 	const void *fw_blob; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	priv = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Skip downloading the firmware if the DCPU is already running and | ||||
| 	 * responding to commands. | ||||
| 	 */ | ||||
| 	if (is_dcpu_enabled(priv->regs)) { | ||||
| 	if (is_dcpu_enabled(priv)) { | ||||
| 		u32 response[MSG_FIELD_MAX]; | ||||
| 
 | ||||
| 		ret = __send_command(priv, DPFE_CMD_GET_INFO, response); | ||||
| @ -606,20 +639,23 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, | ||||
| 	if (!priv->dpfe_api->fw_name) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	ret = request_firmware(&fw, priv->dpfe_api->fw_name, dev); | ||||
| 	/* request_firmware() prints its own error messages. */ | ||||
| 	ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev); | ||||
| 	/*
 | ||||
| 	 * Defer the firmware download if the firmware file couldn't be found. | ||||
| 	 * The root file system may not be available yet. | ||||
| 	 */ | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 		return (ret == -ENOENT) ? -EPROBE_DEFER : ret; | ||||
| 
 | ||||
| 	ret = __verify_firmware(init, fw); | ||||
| 	ret = __verify_firmware(&init, fw); | ||||
| 	if (ret) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	__disable_dcpu(priv->regs); | ||||
| 	__disable_dcpu(priv); | ||||
| 
 | ||||
| 	is_big_endian = init->is_big_endian; | ||||
| 	dmem_size = init->dmem_len; | ||||
| 	imem_size = init->imem_len; | ||||
| 	is_big_endian = init.is_big_endian; | ||||
| 	dmem_size = init.dmem_len; | ||||
| 	imem_size = init.imem_len; | ||||
| 
 | ||||
| 	/* At the beginning of the firmware blob is a header. */ | ||||
| 	header = (struct dpfe_firmware_header *)fw->data; | ||||
| @ -637,17 +673,17 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = __verify_fw_checksum(init, priv, header, init->chksum); | ||||
| 	ret = __verify_fw_checksum(&init, priv, header, init.chksum); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	__enable_dcpu(priv->regs); | ||||
| 	__enable_dcpu(priv); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t generic_show(unsigned int command, u32 response[], | ||||
| 			    struct private_data *priv, char *buf) | ||||
| 			    struct brcmstb_dpfe_priv *priv, char *buf) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -665,7 +701,7 @@ static ssize_t show_info(struct device *dev, struct device_attribute *devattr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	u32 response[MSG_FIELD_MAX]; | ||||
| 	struct private_data *priv; | ||||
| 	struct brcmstb_dpfe_priv *priv; | ||||
| 	unsigned int info; | ||||
| 	ssize_t ret; | ||||
| 
 | ||||
| @ -688,7 +724,7 @@ static ssize_t show_refresh(struct device *dev, | ||||
| { | ||||
| 	u32 response[MSG_FIELD_MAX]; | ||||
| 	void __iomem *info; | ||||
| 	struct private_data *priv; | ||||
| 	struct brcmstb_dpfe_priv *priv; | ||||
| 	u8 refresh, sr_abort, ppre, thermal_offs, tuf; | ||||
| 	u32 mr4; | ||||
| 	ssize_t ret; | ||||
| @ -721,7 +757,7 @@ static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, | ||||
| 			  const char *buf, size_t count) | ||||
| { | ||||
| 	u32 response[MSG_FIELD_MAX]; | ||||
| 	struct private_data *priv; | ||||
| 	struct brcmstb_dpfe_priv *priv; | ||||
| 	void __iomem *info; | ||||
| 	unsigned long val; | ||||
| 	int ret; | ||||
| @ -747,7 +783,7 @@ static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr, | ||||
| 			   char *buf) | ||||
| { | ||||
| 	u32 response[MSG_FIELD_MAX]; | ||||
| 	struct private_data *priv; | ||||
| 	struct brcmstb_dpfe_priv *priv; | ||||
| 	void __iomem *info; | ||||
| 	ssize_t ret; | ||||
| 	u32 mr5, mr6, mr7, mr8, err; | ||||
| @ -778,7 +814,7 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	u32 response[MSG_FIELD_MAX]; | ||||
| 	struct private_data *priv; | ||||
| 	struct brcmstb_dpfe_priv *priv; | ||||
| 	ssize_t ret; | ||||
| 	u32 mr4, mr5, mr6, mr7, mr8, err; | ||||
| 
 | ||||
| @ -800,16 +836,15 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, | ||||
| 
 | ||||
| static int brcmstb_dpfe_resume(struct platform_device *pdev) | ||||
| { | ||||
| 	struct init_data init; | ||||
| 	struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	return brcmstb_dpfe_download_firmware(pdev, &init); | ||||
| 	return brcmstb_dpfe_download_firmware(priv); | ||||
| } | ||||
| 
 | ||||
| static int brcmstb_dpfe_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct private_data *priv; | ||||
| 	struct init_data init; | ||||
| 	struct brcmstb_dpfe_priv *priv; | ||||
| 	struct resource *res; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -817,6 +852,8 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) | ||||
| 	if (!priv) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	priv->dev = dev; | ||||
| 
 | ||||
| 	mutex_init(&priv->lock); | ||||
| 	platform_set_drvdata(pdev, priv); | ||||
| 
 | ||||
| @ -851,9 +888,10 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) | ||||
| 		return -ENOENT; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = brcmstb_dpfe_download_firmware(pdev, &init); | ||||
| 	ret = brcmstb_dpfe_download_firmware(priv); | ||||
| 	if (ret) { | ||||
| 		dev_err(dev, "Couldn't download firmware -- %d\n", ret); | ||||
| 		if (ret != -EPROBE_DEFER) | ||||
| 			dev_err(dev, "Couldn't download firmware -- %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| @ -867,7 +905,7 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) | ||||
| 
 | ||||
| static int brcmstb_dpfe_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct private_data *priv = dev_get_drvdata(&pdev->dev); | ||||
| 	struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev); | ||||
| 
 | ||||
| 	sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs); | ||||
| 
 | ||||
| @ -876,10 +914,10 @@ static int brcmstb_dpfe_remove(struct platform_device *pdev) | ||||
| 
 | ||||
| static const struct of_device_id brcmstb_dpfe_of_match[] = { | ||||
| 	/* Use legacy API v2 for a select number of chips */ | ||||
| 	{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 }, | ||||
| 	{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 }, | ||||
| 	/* API v3 is the default going forward */ | ||||
| 	{ .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 }, | ||||
| 	{} | ||||
|  | ||||
| @ -1613,7 +1613,7 @@ static void emif_shutdown(struct platform_device *pdev) | ||||
| static int get_emif_reg_values(struct emif_data *emif, u32 freq, | ||||
| 		struct emif_regs *regs) | ||||
| { | ||||
| 	u32				cs1_used, ip_rev, phy_type; | ||||
| 	u32				ip_rev, phy_type; | ||||
| 	u32				cl, type; | ||||
| 	const struct lpddr2_timings	*timings; | ||||
| 	const struct lpddr2_min_tck	*min_tck; | ||||
| @ -1621,7 +1621,6 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq, | ||||
| 	const struct lpddr2_addressing	*addressing; | ||||
| 	struct emif_data		*emif_for_calc; | ||||
| 	struct device			*dev; | ||||
| 	const struct emif_custom_configs *custom_configs; | ||||
| 
 | ||||
| 	dev = emif->dev; | ||||
| 	/*
 | ||||
| @ -1639,12 +1638,10 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq, | ||||
| 
 | ||||
| 	device_info	= emif_for_calc->plat_data->device_info; | ||||
| 	type		= device_info->type; | ||||
| 	cs1_used	= device_info->cs1_used; | ||||
| 	ip_rev		= emif_for_calc->plat_data->ip_rev; | ||||
| 	phy_type	= emif_for_calc->plat_data->phy_type; | ||||
| 
 | ||||
| 	min_tck		= emif_for_calc->plat_data->min_tck; | ||||
| 	custom_configs	= emif_for_calc->plat_data->custom_configs; | ||||
| 
 | ||||
| 	set_ddr_clk_period(freq); | ||||
| 
 | ||||
|  | ||||
| @ -29,6 +29,7 @@ | ||||
| #define DDR_TYPE_LPDDR2_S4	3 | ||||
| #define DDR_TYPE_LPDDR2_S2	4 | ||||
| #define DDR_TYPE_LPDDR2_NVM	5 | ||||
| #define DDR_TYPE_LPDDR3		6 | ||||
| 
 | ||||
| /* DDR IO width */ | ||||
| #define DDR_IO_WIDTH_4		1 | ||||
| @ -169,4 +170,64 @@ extern const struct lpddr2_timings | ||||
| 	lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; | ||||
| extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; | ||||
| 
 | ||||
| /*
 | ||||
|  * Structure for timings for LPDDR3 based on LPDDR2 plus additional fields. | ||||
|  * All parameters are in pico seconds(ps) excluding max_freq, min_freq which | ||||
|  * are in Hz. | ||||
|  */ | ||||
| struct lpddr3_timings { | ||||
| 	u32 max_freq; | ||||
| 	u32 min_freq; | ||||
| 	u32 tRFC; | ||||
| 	u32 tRRD; | ||||
| 	u32 tRPab; | ||||
| 	u32 tRPpb; | ||||
| 	u32 tRCD; | ||||
| 	u32 tRC; | ||||
| 	u32 tRAS; | ||||
| 	u32 tWTR; | ||||
| 	u32 tWR; | ||||
| 	u32 tRTP; | ||||
| 	u32 tW2W_C2C; | ||||
| 	u32 tR2R_C2C; | ||||
| 	u32 tWL; | ||||
| 	u32 tDQSCK; | ||||
| 	u32 tRL; | ||||
| 	u32 tFAW; | ||||
| 	u32 tXSR; | ||||
| 	u32 tXP; | ||||
| 	u32 tCKE; | ||||
| 	u32 tCKESR; | ||||
| 	u32 tMRD; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Min value for some parameters in terms of number of tCK cycles(nCK) | ||||
|  * Please set to zero parameters that are not valid for a given memory | ||||
|  * type | ||||
|  */ | ||||
| struct lpddr3_min_tck { | ||||
| 	u32 tRFC; | ||||
| 	u32 tRRD; | ||||
| 	u32 tRPab; | ||||
| 	u32 tRPpb; | ||||
| 	u32 tRCD; | ||||
| 	u32 tRC; | ||||
| 	u32 tRAS; | ||||
| 	u32 tWTR; | ||||
| 	u32 tWR; | ||||
| 	u32 tRTP; | ||||
| 	u32 tW2W_C2C; | ||||
| 	u32 tR2R_C2C; | ||||
| 	u32 tWL; | ||||
| 	u32 tDQSCK; | ||||
| 	u32 tRL; | ||||
| 	u32 tFAW; | ||||
| 	u32 tXSR; | ||||
| 	u32 tXP; | ||||
| 	u32 tCKE; | ||||
| 	u32 tCKESR; | ||||
| 	u32 tMRD; | ||||
| }; | ||||
| 
 | ||||
| #endif /* __JEDEC_DDR_H */ | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
|  * OpenFirmware helpers for memory drivers | ||||
|  * | ||||
|  * Copyright (C) 2012 Texas Instruments, Inc. | ||||
|  * Copyright (C) 2019 Samsung Electronics Co., Ltd. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/device.h> | ||||
| @ -149,3 +150,151 @@ default_timings: | ||||
| 	return lpddr2_jedec_timings; | ||||
| } | ||||
| EXPORT_SYMBOL(of_get_ddr_timings); | ||||
| 
 | ||||
| /**
 | ||||
|  * of_lpddr3_get_min_tck() - extract min timing values for lpddr3 | ||||
|  * @np: pointer to ddr device tree node | ||||
|  * @device: device requesting for min timing values | ||||
|  * | ||||
|  * Populates the lpddr3_min_tck structure by extracting data | ||||
|  * from device tree node. Returns a pointer to the populated | ||||
|  * structure. If any error in populating the structure, returns NULL. | ||||
|  */ | ||||
| const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, | ||||
| 						   struct device *dev) | ||||
| { | ||||
| 	int			ret = 0; | ||||
| 	struct lpddr3_min_tck	*min; | ||||
| 
 | ||||
| 	min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL); | ||||
| 	if (!min) | ||||
| 		goto default_min_tck; | ||||
| 
 | ||||
| 	ret |= of_property_read_u32(np, "tRFC-min-tck", &min->tRFC); | ||||
| 	ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD); | ||||
| 	ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab); | ||||
| 	ret |= of_property_read_u32(np, "tRPpb-min-tck", &min->tRPpb); | ||||
| 	ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD); | ||||
| 	ret |= of_property_read_u32(np, "tRC-min-tck", &min->tRC); | ||||
| 	ret |= of_property_read_u32(np, "tRAS-min-tck", &min->tRAS); | ||||
| 	ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR); | ||||
| 	ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR); | ||||
| 	ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP); | ||||
| 	ret |= of_property_read_u32(np, "tW2W-C2C-min-tck", &min->tW2W_C2C); | ||||
| 	ret |= of_property_read_u32(np, "tR2R-C2C-min-tck", &min->tR2R_C2C); | ||||
| 	ret |= of_property_read_u32(np, "tWL-min-tck", &min->tWL); | ||||
| 	ret |= of_property_read_u32(np, "tDQSCK-min-tck", &min->tDQSCK); | ||||
| 	ret |= of_property_read_u32(np, "tRL-min-tck", &min->tRL); | ||||
| 	ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW); | ||||
| 	ret |= of_property_read_u32(np, "tXSR-min-tck", &min->tXSR); | ||||
| 	ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP); | ||||
| 	ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE); | ||||
| 	ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR); | ||||
| 	ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 		dev_warn(dev, "%s: errors while parsing min-tck values\n", | ||||
| 			 __func__); | ||||
| 		devm_kfree(dev, min); | ||||
| 		goto default_min_tck; | ||||
| 	} | ||||
| 
 | ||||
| 	return min; | ||||
| 
 | ||||
| default_min_tck: | ||||
| 	dev_warn(dev, "%s: using default min-tck values\n", __func__); | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(of_lpddr3_get_min_tck); | ||||
| 
 | ||||
| static int of_lpddr3_do_get_timings(struct device_node *np, | ||||
| 				    struct lpddr3_timings *tim) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* The 'reg' param required since DT has changed, used as 'max-freq' */ | ||||
| 	ret = of_property_read_u32(np, "reg", &tim->max_freq); | ||||
| 	ret |= of_property_read_u32(np, "min-freq", &tim->min_freq); | ||||
| 	ret |= of_property_read_u32(np, "tRFC", &tim->tRFC); | ||||
| 	ret |= of_property_read_u32(np, "tRRD", &tim->tRRD); | ||||
| 	ret |= of_property_read_u32(np, "tRPab", &tim->tRPab); | ||||
| 	ret |= of_property_read_u32(np, "tRPpb", &tim->tRPpb); | ||||
| 	ret |= of_property_read_u32(np, "tRCD", &tim->tRCD); | ||||
| 	ret |= of_property_read_u32(np, "tRC", &tim->tRC); | ||||
| 	ret |= of_property_read_u32(np, "tRAS", &tim->tRAS); | ||||
| 	ret |= of_property_read_u32(np, "tWTR", &tim->tWTR); | ||||
| 	ret |= of_property_read_u32(np, "tWR", &tim->tWR); | ||||
| 	ret |= of_property_read_u32(np, "tRTP", &tim->tRTP); | ||||
| 	ret |= of_property_read_u32(np, "tW2W-C2C", &tim->tW2W_C2C); | ||||
| 	ret |= of_property_read_u32(np, "tR2R-C2C", &tim->tR2R_C2C); | ||||
| 	ret |= of_property_read_u32(np, "tFAW", &tim->tFAW); | ||||
| 	ret |= of_property_read_u32(np, "tXSR", &tim->tXSR); | ||||
| 	ret |= of_property_read_u32(np, "tXP", &tim->tXP); | ||||
| 	ret |= of_property_read_u32(np, "tCKE", &tim->tCKE); | ||||
| 	ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR); | ||||
| 	ret |= of_property_read_u32(np, "tMRD", &tim->tMRD); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * of_lpddr3_get_ddr_timings() - extracts the lpddr3 timings and updates no of | ||||
|  * frequencies available. | ||||
|  * @np_ddr: Pointer to ddr device tree node | ||||
|  * @dev: Device requesting for ddr timings | ||||
|  * @device_type: Type of ddr | ||||
|  * @nr_frequencies: No of frequencies available for ddr | ||||
|  * (updated by this function) | ||||
|  * | ||||
|  * Populates lpddr3_timings structure by extracting data from device | ||||
|  * tree node. Returns pointer to populated structure. If any error | ||||
|  * while populating, returns NULL. | ||||
|  */ | ||||
| const struct lpddr3_timings | ||||
| *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, struct device *dev, | ||||
| 			   u32 device_type, u32 *nr_frequencies) | ||||
| { | ||||
| 	struct lpddr3_timings	*timings = NULL; | ||||
| 	u32			arr_sz = 0, i = 0; | ||||
| 	struct device_node	*np_tim; | ||||
| 	char			*tim_compat = NULL; | ||||
| 
 | ||||
| 	switch (device_type) { | ||||
| 	case DDR_TYPE_LPDDR3: | ||||
| 		tim_compat = "jedec,lpddr3-timings"; | ||||
| 		break; | ||||
| 	default: | ||||
| 		dev_warn(dev, "%s: un-supported memory type\n", __func__); | ||||
| 	} | ||||
| 
 | ||||
| 	for_each_child_of_node(np_ddr, np_tim) | ||||
| 		if (of_device_is_compatible(np_tim, tim_compat)) | ||||
| 			arr_sz++; | ||||
| 
 | ||||
| 	if (arr_sz) | ||||
| 		timings = devm_kcalloc(dev, arr_sz, sizeof(*timings), | ||||
| 				       GFP_KERNEL); | ||||
| 
 | ||||
| 	if (!timings) | ||||
| 		goto default_timings; | ||||
| 
 | ||||
| 	for_each_child_of_node(np_ddr, np_tim) { | ||||
| 		if (of_device_is_compatible(np_tim, tim_compat)) { | ||||
| 			if (of_lpddr3_do_get_timings(np_tim, &timings[i])) { | ||||
| 				devm_kfree(dev, timings); | ||||
| 				goto default_timings; | ||||
| 			} | ||||
| 			i++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	*nr_frequencies = arr_sz; | ||||
| 
 | ||||
| 	return timings; | ||||
| 
 | ||||
| default_timings: | ||||
| 	dev_warn(dev, "%s: failed to get timings\n", __func__); | ||||
| 	*nr_frequencies = 0; | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(of_lpddr3_get_ddr_timings); | ||||
|  | ||||
| @ -14,6 +14,11 @@ extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, | ||||
| extern const struct lpddr2_timings | ||||
| 	*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, | ||||
| 	u32 device_type, u32 *nr_frequencies); | ||||
| extern const struct lpddr3_min_tck | ||||
| 	*of_lpddr3_get_min_tck(struct device_node *np, struct device *dev); | ||||
| extern const struct lpddr3_timings | ||||
| 	*of_lpddr3_get_ddr_timings(struct device_node *np_ddr, | ||||
| 	struct device *dev, u32 device_type, u32 *nr_frequencies); | ||||
| #else | ||||
| static inline const struct lpddr2_min_tck | ||||
| 	*of_get_min_tck(struct device_node *np, struct device *dev) | ||||
| @ -27,6 +32,19 @@ static inline const struct lpddr2_timings | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static inline const struct lpddr3_min_tck | ||||
| 	*of_lpddr3_get_min_tck(struct device_node *np, struct device *dev) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static inline const struct lpddr3_timings | ||||
| 	*of_lpddr3_get_ddr_timings(struct device_node *np_ddr, | ||||
| 	struct device *dev, u32 device_type, u32 *nr_frequencies) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| #endif /* CONFIG_OF && CONFIG_DDR */ | ||||
| 
 | ||||
| #endif /* __LINUX_MEMORY_OF_REG_ */ | ||||
|  | ||||
| @ -7,6 +7,19 @@ config SAMSUNG_MC | ||||
| 
 | ||||
| if SAMSUNG_MC | ||||
| 
 | ||||
| config EXYNOS5422_DMC | ||||
| 	tristate "EXYNOS5422 Dynamic Memory Controller driver" | ||||
| 	depends on ARCH_EXYNOS || (COMPILE_TEST && HAS_IOMEM) | ||||
| 	select DDR | ||||
| 	depends on DEVFREQ_GOV_SIMPLE_ONDEMAND | ||||
| 	depends on (PM_DEVFREQ && PM_DEVFREQ_EVENT) | ||||
| 	help | ||||
| 	  This adds driver for Exynos5422 DMC (Dynamic Memory Controller). | ||||
| 	  The driver provides support for Dynamic Voltage and Frequency Scaling in | ||||
| 	  DMC and DRAM. It also supports changing timings of DRAM running with | ||||
| 	  different frequency. The timings are calculated based on DT memory | ||||
| 	  information. | ||||
| 
 | ||||
| config EXYNOS_SROM | ||||
| 	bool "Exynos SROM controller driver" if COMPILE_TEST | ||||
| 	depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM) | ||||
|  | ||||
| @ -1,2 +1,3 @@ | ||||
| # SPDX-License-Identifier: GPL-2.0
 | ||||
| obj-$(CONFIG_EXYNOS5422_DMC)	+= exynos5422-dmc.o | ||||
| obj-$(CONFIG_EXYNOS_SROM)	+= exynos-srom.o | ||||
|  | ||||
							
								
								
									
										1550
									
								
								drivers/memory/samsung/exynos5422-dmc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1550
									
								
								drivers/memory/samsung/exynos5422-dmc.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -17,6 +17,16 @@ config TEGRA20_EMC | ||||
| 	  This driver is required to change memory timings / clock rate for | ||||
| 	  external memory. | ||||
| 
 | ||||
| config TEGRA30_EMC | ||||
| 	bool "NVIDIA Tegra30 External Memory Controller driver" | ||||
| 	default y | ||||
| 	depends on TEGRA_MC && ARCH_TEGRA_3x_SOC | ||||
| 	help | ||||
| 	  This driver is for the External Memory Controller (EMC) found on | ||||
| 	  Tegra30 chips. The EMC controls the external DRAM on the board. | ||||
| 	  This driver is required to change memory timings / clock rate for | ||||
| 	  external memory. | ||||
| 
 | ||||
| config TEGRA124_EMC | ||||
| 	bool "NVIDIA Tegra124 External Memory Controller driver" | ||||
| 	default y | ||||
|  | ||||
| @ -11,5 +11,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o | ||||
| obj-$(CONFIG_TEGRA_MC) += tegra-mc.o | ||||
| 
 | ||||
| obj-$(CONFIG_TEGRA20_EMC)  += tegra20-emc.o | ||||
| obj-$(CONFIG_TEGRA30_EMC)  += tegra30-emc.o | ||||
| obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o | ||||
| obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| 
 | ||||
| #include <linux/clk.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| @ -18,39 +19,6 @@ | ||||
| 
 | ||||
| #include "mc.h" | ||||
| 
 | ||||
| #define MC_INTSTATUS 0x000 | ||||
| 
 | ||||
| #define MC_INTMASK 0x004 | ||||
| 
 | ||||
| #define MC_ERR_STATUS 0x08 | ||||
| #define  MC_ERR_STATUS_TYPE_SHIFT 28 | ||||
| #define  MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT) | ||||
| #define  MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT) | ||||
| #define  MC_ERR_STATUS_READABLE (1 << 27) | ||||
| #define  MC_ERR_STATUS_WRITABLE (1 << 26) | ||||
| #define  MC_ERR_STATUS_NONSECURE (1 << 25) | ||||
| #define  MC_ERR_STATUS_ADR_HI_SHIFT 20 | ||||
| #define  MC_ERR_STATUS_ADR_HI_MASK 0x3 | ||||
| #define  MC_ERR_STATUS_SECURITY (1 << 17) | ||||
| #define  MC_ERR_STATUS_RW (1 << 16) | ||||
| 
 | ||||
| #define MC_ERR_ADR 0x0c | ||||
| 
 | ||||
| #define MC_GART_ERROR_REQ		0x30 | ||||
| #define MC_DECERR_EMEM_OTHERS_STATUS	0x58 | ||||
| #define MC_SECURITY_VIOLATION_STATUS	0x74 | ||||
| 
 | ||||
| #define MC_EMEM_ARB_CFG 0x90 | ||||
| #define  MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x)	(((x) & 0x1ff) << 0) | ||||
| #define  MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK	0x1ff | ||||
| #define MC_EMEM_ARB_MISC0 0xd8 | ||||
| 
 | ||||
| #define MC_EMEM_ADR_CFG 0x54 | ||||
| #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) | ||||
| 
 | ||||
| #define MC_TIMING_CONTROL		0xfc | ||||
| #define MC_TIMING_UPDATE		BIT(0) | ||||
| 
 | ||||
| static const struct of_device_id tegra_mc_of_match[] = { | ||||
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||||
| 	{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, | ||||
| @ -307,7 +275,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) | ||||
| int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	struct tegra_mc_timing *timing = NULL; | ||||
| @ -322,11 +290,13 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) | ||||
| 	if (!timing) { | ||||
| 		dev_err(mc->dev, "no memory timing registered for rate %lu\n", | ||||
| 			rate); | ||||
| 		return; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < mc->soc->num_emem_regs; ++i) | ||||
| 		mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) | ||||
| @ -626,6 +596,7 @@ static int tegra_mc_probe(struct platform_device *pdev) | ||||
| 	struct resource *res; | ||||
| 	struct tegra_mc *mc; | ||||
| 	void *isr; | ||||
| 	u64 mask; | ||||
| 	int err; | ||||
| 
 | ||||
| 	mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); | ||||
| @ -637,6 +608,14 @@ static int tegra_mc_probe(struct platform_device *pdev) | ||||
| 	mc->soc = of_device_get_match_data(&pdev->dev); | ||||
| 	mc->dev = &pdev->dev; | ||||
| 
 | ||||
| 	mask = DMA_BIT_MASK(mc->soc->num_address_bits); | ||||
| 
 | ||||
| 	err = dma_coerce_mask_and_coherent(&pdev->dev, mask); | ||||
| 	if (err < 0) { | ||||
| 		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	/* length of MC tick in nanoseconds */ | ||||
| 	mc->tick = 30; | ||||
| 
 | ||||
| @ -658,6 +637,9 @@ static int tegra_mc_probe(struct platform_device *pdev) | ||||
| 	} else | ||||
| #endif | ||||
| 	{ | ||||
| 		/* ensure that debug features are disabled */ | ||||
| 		mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG); | ||||
| 
 | ||||
| 		err = tegra_mc_setup_latency_allowance(mc); | ||||
| 		if (err < 0) { | ||||
| 			dev_err(&pdev->dev, | ||||
|  | ||||
| @ -6,20 +6,76 @@ | ||||
| #ifndef MEMORY_TEGRA_MC_H | ||||
| #define MEMORY_TEGRA_MC_H | ||||
| 
 | ||||
| #include <linux/bits.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| #include <soc/tegra/mc.h> | ||||
| 
 | ||||
| #define MC_INT_DECERR_MTS (1 << 16) | ||||
| #define MC_INT_SECERR_SEC (1 << 13) | ||||
| #define MC_INT_DECERR_VPR (1 << 12) | ||||
| #define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) | ||||
| #define MC_INT_INVALID_SMMU_PAGE (1 << 10) | ||||
| #define MC_INT_ARBITRATION_EMEM (1 << 9) | ||||
| #define MC_INT_SECURITY_VIOLATION (1 << 8) | ||||
| #define MC_INT_INVALID_GART_PAGE (1 << 7) | ||||
| #define MC_INT_DECERR_EMEM (1 << 6) | ||||
| #define MC_INTSTATUS					0x00 | ||||
| #define MC_INTMASK					0x04 | ||||
| #define MC_ERR_STATUS					0x08 | ||||
| #define MC_ERR_ADR					0x0c | ||||
| #define MC_GART_ERROR_REQ				0x30 | ||||
| #define MC_EMEM_ADR_CFG					0x54 | ||||
| #define MC_DECERR_EMEM_OTHERS_STATUS			0x58 | ||||
| #define MC_SECURITY_VIOLATION_STATUS			0x74 | ||||
| #define MC_EMEM_ARB_CFG					0x90 | ||||
| #define MC_EMEM_ARB_OUTSTANDING_REQ			0x94 | ||||
| #define MC_EMEM_ARB_TIMING_RCD				0x98 | ||||
| #define MC_EMEM_ARB_TIMING_RP				0x9c | ||||
| #define MC_EMEM_ARB_TIMING_RC				0xa0 | ||||
| #define MC_EMEM_ARB_TIMING_RAS				0xa4 | ||||
| #define MC_EMEM_ARB_TIMING_FAW				0xa8 | ||||
| #define MC_EMEM_ARB_TIMING_RRD				0xac | ||||
| #define MC_EMEM_ARB_TIMING_RAP2PRE			0xb0 | ||||
| #define MC_EMEM_ARB_TIMING_WAP2PRE			0xb4 | ||||
| #define MC_EMEM_ARB_TIMING_R2R				0xb8 | ||||
| #define MC_EMEM_ARB_TIMING_W2W				0xbc | ||||
| #define MC_EMEM_ARB_TIMING_R2W				0xc0 | ||||
| #define MC_EMEM_ARB_TIMING_W2R				0xc4 | ||||
| #define MC_EMEM_ARB_DA_TURNS				0xd0 | ||||
| #define MC_EMEM_ARB_DA_COVERS				0xd4 | ||||
| #define MC_EMEM_ARB_MISC0				0xd8 | ||||
| #define MC_EMEM_ARB_MISC1				0xdc | ||||
| #define MC_EMEM_ARB_RING1_THROTTLE			0xe0 | ||||
| #define MC_EMEM_ARB_OVERRIDE				0xe8 | ||||
| #define MC_TIMING_CONTROL_DBG				0xf8 | ||||
| #define MC_TIMING_CONTROL				0xfc | ||||
| 
 | ||||
| #define MC_INT_DECERR_MTS				BIT(16) | ||||
| #define MC_INT_SECERR_SEC				BIT(13) | ||||
| #define MC_INT_DECERR_VPR				BIT(12) | ||||
| #define MC_INT_INVALID_APB_ASID_UPDATE			BIT(11) | ||||
| #define MC_INT_INVALID_SMMU_PAGE			BIT(10) | ||||
| #define MC_INT_ARBITRATION_EMEM				BIT(9) | ||||
| #define MC_INT_SECURITY_VIOLATION			BIT(8) | ||||
| #define MC_INT_INVALID_GART_PAGE			BIT(7) | ||||
| #define MC_INT_DECERR_EMEM				BIT(6) | ||||
| 
 | ||||
| #define MC_ERR_STATUS_TYPE_SHIFT			28 | ||||
| #define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE		(0x6 << 28) | ||||
| #define MC_ERR_STATUS_TYPE_MASK				(0x7 << 28) | ||||
| #define MC_ERR_STATUS_READABLE				BIT(27) | ||||
| #define MC_ERR_STATUS_WRITABLE				BIT(26) | ||||
| #define MC_ERR_STATUS_NONSECURE				BIT(25) | ||||
| #define MC_ERR_STATUS_ADR_HI_SHIFT			20 | ||||
| #define MC_ERR_STATUS_ADR_HI_MASK			0x3 | ||||
| #define MC_ERR_STATUS_SECURITY				BIT(17) | ||||
| #define MC_ERR_STATUS_RW				BIT(16) | ||||
| 
 | ||||
| #define MC_EMEM_ADR_CFG_EMEM_NUMDEV			BIT(0) | ||||
| 
 | ||||
| #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x)		((x) & 0x1ff) | ||||
| #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK		0x1ff | ||||
| 
 | ||||
| #define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK		0x1ff | ||||
| #define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE	BIT(30) | ||||
| #define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE	BIT(31) | ||||
| 
 | ||||
| #define MC_EMEM_ARB_OVERRIDE_EACK_MASK			0x3 | ||||
| 
 | ||||
| #define MC_TIMING_UPDATE				BIT(0) | ||||
| 
 | ||||
| static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) | ||||
| { | ||||
|  | ||||
| @ -909,16 +909,18 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = { | ||||
| 	{ .name = "tsec",      .swgroup = TEGRA_SWGROUP_TSEC,      .reg = 0x294 }, | ||||
| }; | ||||
| 
 | ||||
| static const unsigned int tegra114_group_display[] = { | ||||
| static const unsigned int tegra114_group_drm[] = { | ||||
| 	TEGRA_SWGROUP_DC, | ||||
| 	TEGRA_SWGROUP_DCB, | ||||
| 	TEGRA_SWGROUP_G2, | ||||
| 	TEGRA_SWGROUP_NV, | ||||
| }; | ||||
| 
 | ||||
| static const struct tegra_smmu_group_soc tegra114_groups[] = { | ||||
| 	{ | ||||
| 		.name = "display", | ||||
| 		.swgroups = tegra114_group_display, | ||||
| 		.num_swgroups = ARRAY_SIZE(tegra114_group_display), | ||||
| 		.name = "drm", | ||||
| 		.swgroups = tegra114_group_drm, | ||||
| 		.num_swgroups = ARRAY_SIZE(tegra114_group_drm), | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -10,26 +10,6 @@ | ||||
| 
 | ||||
| #include "mc.h" | ||||
| 
 | ||||
| #define MC_EMEM_ARB_CFG				0x90 | ||||
| #define MC_EMEM_ARB_OUTSTANDING_REQ		0x94 | ||||
| #define MC_EMEM_ARB_TIMING_RCD			0x98 | ||||
| #define MC_EMEM_ARB_TIMING_RP			0x9c | ||||
| #define MC_EMEM_ARB_TIMING_RC			0xa0 | ||||
| #define MC_EMEM_ARB_TIMING_RAS			0xa4 | ||||
| #define MC_EMEM_ARB_TIMING_FAW			0xa8 | ||||
| #define MC_EMEM_ARB_TIMING_RRD			0xac | ||||
| #define MC_EMEM_ARB_TIMING_RAP2PRE		0xb0 | ||||
| #define MC_EMEM_ARB_TIMING_WAP2PRE		0xb4 | ||||
| #define MC_EMEM_ARB_TIMING_R2R			0xb8 | ||||
| #define MC_EMEM_ARB_TIMING_W2W			0xbc | ||||
| #define MC_EMEM_ARB_TIMING_R2W			0xc0 | ||||
| #define MC_EMEM_ARB_TIMING_W2R			0xc4 | ||||
| #define MC_EMEM_ARB_DA_TURNS			0xd0 | ||||
| #define MC_EMEM_ARB_DA_COVERS			0xd4 | ||||
| #define MC_EMEM_ARB_MISC0			0xd8 | ||||
| #define MC_EMEM_ARB_MISC1			0xdc | ||||
| #define MC_EMEM_ARB_RING1_THROTTLE		0xe0 | ||||
| 
 | ||||
| static const struct tegra_mc_client tegra124_mc_clients[] = { | ||||
| 	{ | ||||
| 		.id = 0x00, | ||||
| @ -974,16 +954,18 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = { | ||||
| 	{ .name = "vi",        .swgroup = TEGRA_SWGROUP_VI,        .reg = 0x280 }, | ||||
| }; | ||||
| 
 | ||||
| static const unsigned int tegra124_group_display[] = { | ||||
| static const unsigned int tegra124_group_drm[] = { | ||||
| 	TEGRA_SWGROUP_DC, | ||||
| 	TEGRA_SWGROUP_DCB, | ||||
| 	TEGRA_SWGROUP_GPU, | ||||
| 	TEGRA_SWGROUP_VIC, | ||||
| }; | ||||
| 
 | ||||
| static const struct tegra_smmu_group_soc tegra124_groups[] = { | ||||
| 	{ | ||||
| 		.name = "display", | ||||
| 		.swgroups = tegra124_group_display, | ||||
| 		.num_swgroups = ARRAY_SIZE(tegra124_group_display), | ||||
| 		.name = "drm", | ||||
| 		.swgroups = tegra124_group_drm, | ||||
| 		.num_swgroups = ARRAY_SIZE(tegra124_group_drm), | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -6,10 +6,11 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/clk.h> | ||||
| #include <linux/clk/tegra.h> | ||||
| #include <linux/completion.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/iopoll.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| @ -21,6 +22,7 @@ | ||||
| 
 | ||||
| #define EMC_INTSTATUS				0x000 | ||||
| #define EMC_INTMASK				0x004 | ||||
| #define EMC_DBG					0x008 | ||||
| #define EMC_TIMING_CONTROL			0x028 | ||||
| #define EMC_RC					0x02c | ||||
| #define EMC_RFC					0x030 | ||||
| @ -79,6 +81,12 @@ | ||||
| #define EMC_REFRESH_OVERFLOW_INT		BIT(3) | ||||
| #define EMC_CLKCHANGE_COMPLETE_INT		BIT(4) | ||||
| 
 | ||||
| #define EMC_DBG_READ_MUX_ASSEMBLY		BIT(0) | ||||
| #define EMC_DBG_WRITE_MUX_ACTIVE		BIT(1) | ||||
| #define EMC_DBG_FORCE_UPDATE			BIT(2) | ||||
| #define EMC_DBG_READ_DQM_CTRL			BIT(9) | ||||
| #define EMC_DBG_CFG_PRIORITY			BIT(24) | ||||
| 
 | ||||
| static const u16 emc_timing_registers[] = { | ||||
| 	EMC_RC, | ||||
| 	EMC_RFC, | ||||
| @ -137,9 +145,6 @@ struct tegra_emc { | ||||
| 	struct device *dev; | ||||
| 	struct completion clk_handshake_complete; | ||||
| 	struct notifier_block clk_nb; | ||||
| 	struct clk *backup_clk; | ||||
| 	struct clk *emc_mux; | ||||
| 	struct clk *pll_m; | ||||
| 	struct clk *clk; | ||||
| 	void __iomem *regs; | ||||
| 
 | ||||
| @ -219,7 +224,7 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) | ||||
| 
 | ||||
| static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) | ||||
| { | ||||
| 	long timeout; | ||||
| 	unsigned long timeout; | ||||
| 
 | ||||
| 	dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush); | ||||
| 
 | ||||
| @ -231,14 +236,10 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) | ||||
| 	} | ||||
| 
 | ||||
| 	timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, | ||||
| 					      usecs_to_jiffies(100)); | ||||
| 					      msecs_to_jiffies(100)); | ||||
| 	if (timeout == 0) { | ||||
| 		dev_err(emc->dev, "EMC-CAR handshake failed\n"); | ||||
| 		return -EIO; | ||||
| 	} else if (timeout < 0) { | ||||
| 		dev_err(emc->dev, "failed to wait for EMC-CAR handshake: %ld\n", | ||||
| 			timeout); | ||||
| 		return timeout; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| @ -363,6 +364,13 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, | ||||
| 	sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, | ||||
| 	     NULL); | ||||
| 
 | ||||
| 	dev_info(emc->dev, | ||||
| 		 "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", | ||||
| 		 emc->num_timings, | ||||
| 		 tegra_read_ram_code(), | ||||
| 		 emc->timings[0].rate / 1000000, | ||||
| 		 emc->timings[emc->num_timings - 1].rate / 1000000); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -398,7 +406,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev) | ||||
| static int emc_setup_hw(struct tegra_emc *emc) | ||||
| { | ||||
| 	u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; | ||||
| 	u32 emc_cfg; | ||||
| 	u32 emc_cfg, emc_dbg; | ||||
| 
 | ||||
| 	emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); | ||||
| 
 | ||||
| @ -421,42 +429,53 @@ static int emc_setup_hw(struct tegra_emc *emc) | ||||
| 	writel_relaxed(intmask, emc->regs + EMC_INTMASK); | ||||
| 	writel_relaxed(intmask, emc->regs + EMC_INTSTATUS); | ||||
| 
 | ||||
| 	/* ensure that unwanted debug features are disabled */ | ||||
| 	emc_dbg = readl_relaxed(emc->regs + EMC_DBG); | ||||
| 	emc_dbg |= EMC_DBG_CFG_PRIORITY; | ||||
| 	emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY; | ||||
| 	emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE; | ||||
| 	emc_dbg &= ~EMC_DBG_FORCE_UPDATE; | ||||
| 	writel_relaxed(emc_dbg, emc->regs + EMC_DBG); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int emc_init(struct tegra_emc *emc, unsigned long rate) | ||||
| static long emc_round_rate(unsigned long rate, | ||||
| 			   unsigned long min_rate, | ||||
| 			   unsigned long max_rate, | ||||
| 			   void *arg) | ||||
| { | ||||
| 	int err; | ||||
| 	struct emc_timing *timing = NULL; | ||||
| 	struct tegra_emc *emc = arg; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	err = clk_set_parent(emc->emc_mux, emc->backup_clk); | ||||
| 	if (err) { | ||||
| 		dev_err(emc->dev, | ||||
| 			"failed to reparent to backup source: %d\n", err); | ||||
| 		return err; | ||||
| 	min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate); | ||||
| 
 | ||||
| 	for (i = 0; i < emc->num_timings; i++) { | ||||
| 		if (emc->timings[i].rate < rate && i != emc->num_timings - 1) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (emc->timings[i].rate > max_rate) { | ||||
| 			i = max(i, 1u) - 1; | ||||
| 
 | ||||
| 			if (emc->timings[i].rate < min_rate) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (emc->timings[i].rate < min_rate) | ||||
| 			continue; | ||||
| 
 | ||||
| 		timing = &emc->timings[i]; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	err = clk_set_rate(emc->pll_m, rate); | ||||
| 	if (err) { | ||||
| 		dev_err(emc->dev, | ||||
| 			"failed to change pll_m rate: %d\n", err); | ||||
| 		return err; | ||||
| 	if (!timing) { | ||||
| 		dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n", | ||||
| 			rate, min_rate, max_rate); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	err = clk_set_parent(emc->emc_mux, emc->pll_m); | ||||
| 	if (err) { | ||||
| 		dev_err(emc->dev, | ||||
| 			"failed to reparent to pll_m: %d\n", err); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	err = clk_set_rate(emc->clk, rate); | ||||
| 	if (err) { | ||||
| 		dev_err(emc->dev, | ||||
| 			"failed to change emc rate: %d\n", err); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 	return timing->rate; | ||||
| } | ||||
| 
 | ||||
| static int tegra_emc_probe(struct platform_device *pdev) | ||||
| @ -515,57 +534,26 @@ static int tegra_emc_probe(struct platform_device *pdev) | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	tegra20_clk_set_emc_round_callback(emc_round_rate, emc); | ||||
| 
 | ||||
| 	emc->clk = devm_clk_get(&pdev->dev, "emc"); | ||||
| 	if (IS_ERR(emc->clk)) { | ||||
| 		err = PTR_ERR(emc->clk); | ||||
| 		dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	emc->pll_m = clk_get_sys(NULL, "pll_m"); | ||||
| 	if (IS_ERR(emc->pll_m)) { | ||||
| 		err = PTR_ERR(emc->pll_m); | ||||
| 		dev_err(&pdev->dev, "failed to get pll_m clock: %d\n", err); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	emc->backup_clk = clk_get_sys(NULL, "pll_p"); | ||||
| 	if (IS_ERR(emc->backup_clk)) { | ||||
| 		err = PTR_ERR(emc->backup_clk); | ||||
| 		dev_err(&pdev->dev, "failed to get pll_p clock: %d\n", err); | ||||
| 		goto put_pll_m; | ||||
| 	} | ||||
| 
 | ||||
| 	emc->emc_mux = clk_get_parent(emc->clk); | ||||
| 	if (IS_ERR(emc->emc_mux)) { | ||||
| 		err = PTR_ERR(emc->emc_mux); | ||||
| 		dev_err(&pdev->dev, "failed to get emc_mux clock: %d\n", err); | ||||
| 		goto put_backup; | ||||
| 		goto unset_cb; | ||||
| 	} | ||||
| 
 | ||||
| 	err = clk_notifier_register(emc->clk, &emc->clk_nb); | ||||
| 	if (err) { | ||||
| 		dev_err(&pdev->dev, "failed to register clk notifier: %d\n", | ||||
| 			err); | ||||
| 		goto put_backup; | ||||
| 	} | ||||
| 
 | ||||
| 	/* set DRAM clock rate to maximum */ | ||||
| 	err = emc_init(emc, emc->timings[emc->num_timings - 1].rate); | ||||
| 	if (err) { | ||||
| 		dev_err(&pdev->dev, "failed to initialize EMC clock rate: %d\n", | ||||
| 			err); | ||||
| 		goto unreg_notifier; | ||||
| 		goto unset_cb; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| unreg_notifier: | ||||
| 	clk_notifier_unregister(emc->clk, &emc->clk_nb); | ||||
| put_backup: | ||||
| 	clk_put(emc->backup_clk); | ||||
| put_pll_m: | ||||
| 	clk_put(emc->pll_m); | ||||
| unset_cb: | ||||
| 	tegra20_clk_set_emc_round_callback(NULL, NULL); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
|  | ||||
							
								
								
									
										1232
									
								
								drivers/memory/tegra/tegra30-emc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1232
									
								
								drivers/memory/tegra/tegra30-emc.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -10,6 +10,27 @@ | ||||
| 
 | ||||
| #include "mc.h" | ||||
| 
 | ||||
| static const unsigned long tegra30_mc_emem_regs[] = { | ||||
| 	MC_EMEM_ARB_CFG, | ||||
| 	MC_EMEM_ARB_OUTSTANDING_REQ, | ||||
| 	MC_EMEM_ARB_TIMING_RCD, | ||||
| 	MC_EMEM_ARB_TIMING_RP, | ||||
| 	MC_EMEM_ARB_TIMING_RC, | ||||
| 	MC_EMEM_ARB_TIMING_RAS, | ||||
| 	MC_EMEM_ARB_TIMING_FAW, | ||||
| 	MC_EMEM_ARB_TIMING_RRD, | ||||
| 	MC_EMEM_ARB_TIMING_RAP2PRE, | ||||
| 	MC_EMEM_ARB_TIMING_WAP2PRE, | ||||
| 	MC_EMEM_ARB_TIMING_R2R, | ||||
| 	MC_EMEM_ARB_TIMING_W2W, | ||||
| 	MC_EMEM_ARB_TIMING_R2W, | ||||
| 	MC_EMEM_ARB_TIMING_W2R, | ||||
| 	MC_EMEM_ARB_DA_TURNS, | ||||
| 	MC_EMEM_ARB_DA_COVERS, | ||||
| 	MC_EMEM_ARB_MISC0, | ||||
| 	MC_EMEM_ARB_RING1_THROTTLE, | ||||
| }; | ||||
| 
 | ||||
| static const struct tegra_mc_client tegra30_mc_clients[] = { | ||||
| 	{ | ||||
| 		.id = 0x00, | ||||
| @ -931,16 +952,19 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { | ||||
| 	{ .name = "isp",  .swgroup = TEGRA_SWGROUP_ISP,  .reg = 0x258 }, | ||||
| }; | ||||
| 
 | ||||
| static const unsigned int tegra30_group_display[] = { | ||||
| static const unsigned int tegra30_group_drm[] = { | ||||
| 	TEGRA_SWGROUP_DC, | ||||
| 	TEGRA_SWGROUP_DCB, | ||||
| 	TEGRA_SWGROUP_G2, | ||||
| 	TEGRA_SWGROUP_NV, | ||||
| 	TEGRA_SWGROUP_NV2, | ||||
| }; | ||||
| 
 | ||||
| static const struct tegra_smmu_group_soc tegra30_groups[] = { | ||||
| 	{ | ||||
| 		.name = "display", | ||||
| 		.swgroups = tegra30_group_display, | ||||
| 		.num_swgroups = ARRAY_SIZE(tegra30_group_display), | ||||
| 		.name = "drm", | ||||
| 		.swgroups = tegra30_group_drm, | ||||
| 		.num_swgroups = ARRAY_SIZE(tegra30_group_drm), | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| @ -994,6 +1018,8 @@ const struct tegra_mc_soc tegra30_mc_soc = { | ||||
| 	.atom_size = 16, | ||||
| 	.client_id_mask = 0x7f, | ||||
| 	.smmu = &tegra30_smmu_soc, | ||||
| 	.emem_regs = tegra30_mc_emem_regs, | ||||
| 	.num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs), | ||||
| 	.intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | | ||||
| 		   MC_INT_DECERR_EMEM, | ||||
| 	.reset_ops = &tegra_mc_reset_ops_common, | ||||
|  | ||||
| @ -17,14 +17,18 @@ | ||||
| static int meson_efuse_read(void *context, unsigned int offset, | ||||
| 			    void *val, size_t bytes) | ||||
| { | ||||
| 	return meson_sm_call_read((u8 *)val, bytes, SM_EFUSE_READ, offset, | ||||
| 	struct meson_sm_firmware *fw = context; | ||||
| 
 | ||||
| 	return meson_sm_call_read(fw, (u8 *)val, bytes, SM_EFUSE_READ, offset, | ||||
| 				  bytes, 0, 0, 0); | ||||
| } | ||||
| 
 | ||||
| static int meson_efuse_write(void *context, unsigned int offset, | ||||
| 			     void *val, size_t bytes) | ||||
| { | ||||
| 	return meson_sm_call_write((u8 *)val, bytes, SM_EFUSE_WRITE, offset, | ||||
| 	struct meson_sm_firmware *fw = context; | ||||
| 
 | ||||
| 	return meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset, | ||||
| 				   bytes, 0, 0, 0); | ||||
| } | ||||
| 
 | ||||
| @ -37,12 +41,25 @@ MODULE_DEVICE_TABLE(of, meson_efuse_match); | ||||
| static int meson_efuse_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct meson_sm_firmware *fw; | ||||
| 	struct device_node *sm_np; | ||||
| 	struct nvmem_device *nvmem; | ||||
| 	struct nvmem_config *econfig; | ||||
| 	struct clk *clk; | ||||
| 	unsigned int size; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0); | ||||
| 	if (!sm_np) { | ||||
| 		dev_err(&pdev->dev, "no secure-monitor node\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	fw = meson_sm_get(sm_np); | ||||
| 	of_node_put(sm_np); | ||||
| 	if (!fw) | ||||
| 		return -EPROBE_DEFER; | ||||
| 
 | ||||
| 	clk = devm_clk_get(dev, NULL); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		ret = PTR_ERR(clk); | ||||
| @ -65,7 +82,7 @@ static int meson_efuse_probe(struct platform_device *pdev) | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) { | ||||
| 	if (meson_sm_call(fw, SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) { | ||||
| 		dev_err(dev, "failed to get max user"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| @ -81,6 +98,7 @@ static int meson_efuse_probe(struct platform_device *pdev) | ||||
| 	econfig->reg_read = meson_efuse_read; | ||||
| 	econfig->reg_write = meson_efuse_write; | ||||
| 	econfig->size = size; | ||||
| 	econfig->priv = fw; | ||||
| 
 | ||||
| 	nvmem = devm_nvmem_register(&pdev->dev, econfig); | ||||
| 
 | ||||
|  | ||||
| @ -103,3 +103,14 @@ config PHY_PXA_USB | ||||
| 	  The PHY driver will be used by Marvell udc/ehci/otg driver. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here. | ||||
| 
 | ||||
| config PHY_MMP3_USB | ||||
| 	tristate "Marvell MMP3 USB PHY Driver" | ||||
| 	depends on MACH_MMP3_DT || COMPILE_TEST | ||||
| 	select GENERIC_PHY | ||||
| 	help | ||||
| 	  Enable this to support Marvell MMP3 USB PHY driver for Marvell | ||||
| 	  SoC. This driver will do the PHY initialization and shutdown. | ||||
| 	  The PHY driver will be used by Marvell udc/ehci/otg driver. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here. | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY)	+= phy-armada375-usb2.o | ||||
| obj-$(CONFIG_PHY_BERLIN_SATA)		+= phy-berlin-sata.o | ||||
| obj-$(CONFIG_PHY_BERLIN_USB)		+= phy-berlin-usb.o | ||||
| obj-$(CONFIG_PHY_MMP3_USB)		+= phy-mmp3-usb.o | ||||
| obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY)	+= phy-mvebu-a3700-comphy.o | ||||
| obj-$(CONFIG_PHY_MVEBU_A3700_UTMI)	+= phy-mvebu-a3700-utmi.o | ||||
| obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY)	+= phy-armada38x-comphy.o | ||||
|  | ||||
							
								
								
									
										291
									
								
								drivers/phy/marvell/phy-mmp3-usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								drivers/phy/marvell/phy-mmp3-usb.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,291 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Copyright (C) 2011 Marvell International Ltd. All rights reserved. | ||||
|  * Copyright (C) 2018,2019 Lubomir Rintel <lkundrak@v3.sk> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/delay.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/phy/phy.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/soc/mmp/cputype.h> | ||||
| 
 | ||||
| #define USB2_PLL_REG0		0x4 | ||||
| #define USB2_PLL_REG1		0x8 | ||||
| #define USB2_TX_REG0		0x10 | ||||
| #define USB2_TX_REG1		0x14 | ||||
| #define USB2_TX_REG2		0x18 | ||||
| #define USB2_RX_REG0		0x20 | ||||
| #define USB2_RX_REG1		0x24 | ||||
| #define USB2_RX_REG2		0x28 | ||||
| #define USB2_ANA_REG0		0x30 | ||||
| #define USB2_ANA_REG1		0x34 | ||||
| #define USB2_ANA_REG2		0x38 | ||||
| #define USB2_DIG_REG0		0x3C | ||||
| #define USB2_DIG_REG1		0x40 | ||||
| #define USB2_DIG_REG2		0x44 | ||||
| #define USB2_DIG_REG3		0x48 | ||||
| #define USB2_TEST_REG0		0x4C | ||||
| #define USB2_TEST_REG1		0x50 | ||||
| #define USB2_TEST_REG2		0x54 | ||||
| #define USB2_CHARGER_REG0	0x58 | ||||
| #define USB2_OTG_REG0		0x5C | ||||
| #define USB2_PHY_MON0		0x60 | ||||
| #define USB2_RESETVE_REG0	0x64 | ||||
| #define USB2_ICID_REG0		0x78 | ||||
| #define USB2_ICID_REG1		0x7C | ||||
| 
 | ||||
| /* USB2_PLL_REG0 */ | ||||
| 
 | ||||
| /* This is for Ax stepping */ | ||||
| #define USB2_PLL_FBDIV_SHIFT_MMP3		0 | ||||
| #define USB2_PLL_FBDIV_MASK_MMP3		(0xFF << 0) | ||||
| 
 | ||||
| #define USB2_PLL_REFDIV_SHIFT_MMP3		8 | ||||
| #define USB2_PLL_REFDIV_MASK_MMP3		(0xF << 8) | ||||
| 
 | ||||
| #define USB2_PLL_VDD12_SHIFT_MMP3		12 | ||||
| #define USB2_PLL_VDD18_SHIFT_MMP3		14 | ||||
| 
 | ||||
| /* This is for B0 stepping */ | ||||
| #define USB2_PLL_FBDIV_SHIFT_MMP3_B0		0 | ||||
| #define USB2_PLL_REFDIV_SHIFT_MMP3_B0		9 | ||||
| #define USB2_PLL_VDD18_SHIFT_MMP3_B0		14 | ||||
| #define USB2_PLL_FBDIV_MASK_MMP3_B0		0x01FF | ||||
| #define USB2_PLL_REFDIV_MASK_MMP3_B0		0x3E00 | ||||
| 
 | ||||
| #define USB2_PLL_CAL12_SHIFT_MMP3		0 | ||||
| #define USB2_PLL_CALI12_MASK_MMP3		(0x3 << 0) | ||||
| 
 | ||||
| #define USB2_PLL_VCOCAL_START_SHIFT_MMP3	2 | ||||
| 
 | ||||
| #define USB2_PLL_KVCO_SHIFT_MMP3		4 | ||||
| #define USB2_PLL_KVCO_MASK_MMP3			(0x7<<4) | ||||
| 
 | ||||
| #define USB2_PLL_ICP_SHIFT_MMP3			8 | ||||
| #define USB2_PLL_ICP_MASK_MMP3			(0x7<<8) | ||||
| 
 | ||||
| #define USB2_PLL_LOCK_BYPASS_SHIFT_MMP3		12 | ||||
| 
 | ||||
| #define USB2_PLL_PU_PLL_SHIFT_MMP3		13 | ||||
| #define USB2_PLL_PU_PLL_MASK			(0x1 << 13) | ||||
| 
 | ||||
| #define USB2_PLL_READY_MASK_MMP3		(0x1 << 15) | ||||
| 
 | ||||
| /* USB2_TX_REG0 */ | ||||
| #define USB2_TX_IMPCAL_VTH_SHIFT_MMP3		8 | ||||
| #define USB2_TX_IMPCAL_VTH_MASK_MMP3		(0x7 << 8) | ||||
| 
 | ||||
| #define USB2_TX_RCAL_START_SHIFT_MMP3		13 | ||||
| 
 | ||||
| /* USB2_TX_REG1 */ | ||||
| #define USB2_TX_CK60_PHSEL_SHIFT_MMP3		0 | ||||
| #define USB2_TX_CK60_PHSEL_MASK_MMP3		(0xf << 0) | ||||
| 
 | ||||
| #define USB2_TX_AMP_SHIFT_MMP3			4 | ||||
| #define USB2_TX_AMP_MASK_MMP3			(0x7 << 4) | ||||
| 
 | ||||
| #define USB2_TX_VDD12_SHIFT_MMP3		8 | ||||
| #define USB2_TX_VDD12_MASK_MMP3			(0x3 << 8) | ||||
| 
 | ||||
| /* USB2_TX_REG2 */ | ||||
| #define USB2_TX_DRV_SLEWRATE_SHIFT		10 | ||||
| 
 | ||||
| /* USB2_RX_REG0 */ | ||||
| #define USB2_RX_SQ_THRESH_SHIFT_MMP3		4 | ||||
| #define USB2_RX_SQ_THRESH_MASK_MMP3		(0xf << 4) | ||||
| 
 | ||||
| #define USB2_RX_SQ_LENGTH_SHIFT_MMP3		10 | ||||
| #define USB2_RX_SQ_LENGTH_MASK_MMP3		(0x3 << 10) | ||||
| 
 | ||||
| /* USB2_ANA_REG1*/ | ||||
| #define USB2_ANA_PU_ANA_SHIFT_MMP3		14 | ||||
| 
 | ||||
| /* USB2_OTG_REG0 */ | ||||
| #define USB2_OTG_PU_OTG_SHIFT_MMP3		3 | ||||
| 
 | ||||
| struct mmp3_usb_phy { | ||||
| 	struct phy *phy; | ||||
| 	void __iomem *base; | ||||
| }; | ||||
| 
 | ||||
| static unsigned int u2o_get(void __iomem *base, unsigned int offset) | ||||
| { | ||||
| 	return readl_relaxed(base + offset); | ||||
| } | ||||
| 
 | ||||
| static void u2o_set(void __iomem *base, unsigned int offset, | ||||
| 		unsigned int value) | ||||
| { | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	reg = readl_relaxed(base + offset); | ||||
| 	reg |= value; | ||||
| 	writel_relaxed(reg, base + offset); | ||||
| 	readl_relaxed(base + offset); | ||||
| } | ||||
| 
 | ||||
| static void u2o_clear(void __iomem *base, unsigned int offset, | ||||
| 		unsigned int value) | ||||
| { | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	reg = readl_relaxed(base + offset); | ||||
| 	reg &= ~value; | ||||
| 	writel_relaxed(reg, base + offset); | ||||
| 	readl_relaxed(base + offset); | ||||
| } | ||||
| 
 | ||||
| static int mmp3_usb_phy_init(struct phy *phy) | ||||
| { | ||||
| 	struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); | ||||
| 	void __iomem *base = mmp3_usb_phy->base; | ||||
| 
 | ||||
| 	if (cpu_is_mmp3_a0()) { | ||||
| 		u2o_clear(base, USB2_PLL_REG0, (USB2_PLL_FBDIV_MASK_MMP3 | ||||
| 			| USB2_PLL_REFDIV_MASK_MMP3)); | ||||
| 		u2o_set(base, USB2_PLL_REG0, | ||||
| 			0xd << USB2_PLL_REFDIV_SHIFT_MMP3 | ||||
| 			| 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3); | ||||
| 	} else if (cpu_is_mmp3_b0()) { | ||||
| 		u2o_clear(base, USB2_PLL_REG0, USB2_PLL_REFDIV_MASK_MMP3_B0 | ||||
| 			| USB2_PLL_FBDIV_MASK_MMP3_B0); | ||||
| 		u2o_set(base, USB2_PLL_REG0, | ||||
| 			0xd << USB2_PLL_REFDIV_SHIFT_MMP3_B0 | ||||
| 			| 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3_B0); | ||||
| 	} else { | ||||
| 		dev_err(&phy->dev, "unsupported silicon revision\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	u2o_clear(base, USB2_PLL_REG1, USB2_PLL_PU_PLL_MASK | ||||
| 		| USB2_PLL_ICP_MASK_MMP3 | ||||
| 		| USB2_PLL_KVCO_MASK_MMP3 | ||||
| 		| USB2_PLL_CALI12_MASK_MMP3); | ||||
| 	u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_PU_PLL_SHIFT_MMP3 | ||||
| 		| 1 << USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 | ||||
| 		| 3 << USB2_PLL_ICP_SHIFT_MMP3 | ||||
| 		| 3 << USB2_PLL_KVCO_SHIFT_MMP3 | ||||
| 		| 3 << USB2_PLL_CAL12_SHIFT_MMP3); | ||||
| 
 | ||||
| 	u2o_clear(base, USB2_TX_REG0, USB2_TX_IMPCAL_VTH_MASK_MMP3); | ||||
| 	u2o_set(base, USB2_TX_REG0, 2 << USB2_TX_IMPCAL_VTH_SHIFT_MMP3); | ||||
| 
 | ||||
| 	u2o_clear(base, USB2_TX_REG1, USB2_TX_VDD12_MASK_MMP3 | ||||
| 		| USB2_TX_AMP_MASK_MMP3 | ||||
| 		| USB2_TX_CK60_PHSEL_MASK_MMP3); | ||||
| 	u2o_set(base, USB2_TX_REG1, 3 << USB2_TX_VDD12_SHIFT_MMP3 | ||||
| 		| 4 << USB2_TX_AMP_SHIFT_MMP3 | ||||
| 		| 4 << USB2_TX_CK60_PHSEL_SHIFT_MMP3); | ||||
| 
 | ||||
| 	u2o_clear(base, USB2_TX_REG2, 3 << USB2_TX_DRV_SLEWRATE_SHIFT); | ||||
| 	u2o_set(base, USB2_TX_REG2, 2 << USB2_TX_DRV_SLEWRATE_SHIFT); | ||||
| 
 | ||||
| 	u2o_clear(base, USB2_RX_REG0, USB2_RX_SQ_THRESH_MASK_MMP3); | ||||
| 	u2o_set(base, USB2_RX_REG0, 0xa << USB2_RX_SQ_THRESH_SHIFT_MMP3); | ||||
| 
 | ||||
| 	u2o_set(base, USB2_ANA_REG1, 0x1 << USB2_ANA_PU_ANA_SHIFT_MMP3); | ||||
| 
 | ||||
| 	u2o_set(base, USB2_OTG_REG0, 0x1 << USB2_OTG_PU_OTG_SHIFT_MMP3); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int mmp3_usb_phy_calibrate(struct phy *phy) | ||||
| { | ||||
| 	struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); | ||||
| 	void __iomem *base = mmp3_usb_phy->base; | ||||
| 	int loops; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * PLL VCO and TX Impedance Calibration Timing: | ||||
| 	 * | ||||
| 	 *                _____________________________________ | ||||
| 	 * PU  __________| | ||||
| 	 *                        _____________________________ | ||||
| 	 * VCOCAL START _________| | ||||
| 	 *                                 ___ | ||||
| 	 * REG_RCAL_START ________________|   |________|_______ | ||||
| 	 *               | 200us | 400us  | 40| 400us  | USB PHY READY | ||||
| 	 */ | ||||
| 
 | ||||
| 	udelay(200); | ||||
| 	u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_VCOCAL_START_SHIFT_MMP3); | ||||
| 	udelay(400); | ||||
| 	u2o_set(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); | ||||
| 	udelay(40); | ||||
| 	u2o_clear(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); | ||||
| 	udelay(400); | ||||
| 
 | ||||
| 	loops = 0; | ||||
| 	while ((u2o_get(base, USB2_PLL_REG1) & USB2_PLL_READY_MASK_MMP3) == 0) { | ||||
| 		mdelay(1); | ||||
| 		loops++; | ||||
| 		if (loops > 100) { | ||||
| 			dev_err(&phy->dev, "PLL_READY not set after 100mS.\n"); | ||||
| 			return -ETIMEDOUT; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct phy_ops mmp3_usb_phy_ops = { | ||||
| 	.init		= mmp3_usb_phy_init, | ||||
| 	.calibrate	= mmp3_usb_phy_calibrate, | ||||
| 	.owner		= THIS_MODULE, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id mmp3_usb_phy_of_match[] = { | ||||
| 	{ .compatible = "marvell,mmp3-usb-phy", }, | ||||
| 	{ }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match); | ||||
| 
 | ||||
| static int mmp3_usb_phy_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct resource *resource; | ||||
| 	struct mmp3_usb_phy *mmp3_usb_phy; | ||||
| 	struct phy_provider *provider; | ||||
| 
 | ||||
| 	mmp3_usb_phy = devm_kzalloc(dev, sizeof(*mmp3_usb_phy), GFP_KERNEL); | ||||
| 	if (!mmp3_usb_phy) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	mmp3_usb_phy->base = devm_ioremap_resource(dev, resource); | ||||
| 	if (IS_ERR(mmp3_usb_phy->base)) { | ||||
| 		dev_err(dev, "failed to remap PHY regs\n"); | ||||
| 		return PTR_ERR(mmp3_usb_phy->base); | ||||
| 	} | ||||
| 
 | ||||
| 	mmp3_usb_phy->phy = devm_phy_create(dev, NULL, &mmp3_usb_phy_ops); | ||||
| 	if (IS_ERR(mmp3_usb_phy->phy)) { | ||||
| 		dev_err(dev, "failed to create PHY\n"); | ||||
| 		return PTR_ERR(mmp3_usb_phy->phy); | ||||
| 	} | ||||
| 
 | ||||
| 	phy_set_drvdata(mmp3_usb_phy->phy, mmp3_usb_phy); | ||||
| 	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||||
| 	if (IS_ERR(provider)) { | ||||
| 		dev_err(dev, "failed to register PHY provider\n"); | ||||
| 		return PTR_ERR(provider); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver mmp3_usb_phy_driver = { | ||||
| 	.probe		= mmp3_usb_phy_probe, | ||||
| 	.driver		= { | ||||
| 		.name	= "mmp3-usb-phy", | ||||
| 		.of_match_table = mmp3_usb_phy_of_match, | ||||
| 	}, | ||||
| }; | ||||
| module_platform_driver(mmp3_usb_phy_driver); | ||||
| 
 | ||||
| MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); | ||||
| MODULE_DESCRIPTION("Marvell MMP3 USB PHY Driver"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| @ -129,7 +129,7 @@ config RESET_SCMI | ||||
| 
 | ||||
| config RESET_SIMPLE | ||||
| 	bool "Simple Reset Controller Driver" if COMPILE_TEST | ||||
| 	default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN || ARC | ||||
| 	default ARCH_AGILEX || ARCH_ASPEED || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARC | ||||
| 	help | ||||
| 	  This enables a simple reset controller driver for reset lines that | ||||
| 	  that can be asserted and deasserted by toggling bits in a contiguous, | ||||
| @ -138,10 +138,11 @@ config RESET_SIMPLE | ||||
| 	  Currently this driver supports: | ||||
| 	   - Altera SoCFPGAs | ||||
| 	   - ASPEED BMC SoCs | ||||
| 	   - Bitmain BM1880 SoC | ||||
| 	   - Realtek SoCs | ||||
| 	   - RCC reset controller in STM32 MCUs | ||||
| 	   - Allwinner SoCs | ||||
| 	   - ZTE's zx2967 family | ||||
| 	   - Bitmain BM1880 SoC | ||||
| 
 | ||||
| config RESET_STM32MP157 | ||||
| 	bool "STM32MP157 Reset Driver" if COMPILE_TEST | ||||
|  | ||||
| @ -77,8 +77,10 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) | ||||
|  * @rcdev: a pointer to the reset controller device | ||||
|  * @reset_spec: reset line specifier as found in the device tree | ||||
|  * | ||||
|  * This simple translation function should be used for reset controllers | ||||
|  * with 1:1 mapping, where reset lines can be indexed by number without gaps. | ||||
|  * This static translation function is used by default if of_xlate in | ||||
|  * :c:type:`reset_controller_dev` is not set. It is useful for all reset | ||||
|  * controllers with 1:1 mapping, where reset lines can be indexed by number | ||||
|  * without gaps. | ||||
|  */ | ||||
| static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, | ||||
| 			  const struct of_phandle_args *reset_spec) | ||||
| @ -333,7 +335,6 @@ EXPORT_SYMBOL_GPL(reset_control_reset); | ||||
|  * internal state to be reset, but must be prepared for this to happen. | ||||
|  * Consumers must not use reset_control_reset on shared reset lines when | ||||
|  * reset_control_(de)assert has been used. | ||||
|  * return 0. | ||||
|  * | ||||
|  * If rstc is NULL it is an optional reset and the function will just | ||||
|  * return 0. | ||||
| @ -392,7 +393,6 @@ EXPORT_SYMBOL_GPL(reset_control_assert); | ||||
|  * After calling this function, the reset is guaranteed to be deasserted. | ||||
|  * Consumers must not use reset_control_reset on shared reset lines when | ||||
|  * reset_control_(de)assert has been used. | ||||
|  * return 0. | ||||
|  * | ||||
|  * If rstc is NULL it is an optional reset and the function will just | ||||
|  * return 0. | ||||
|  | ||||
| @ -56,7 +56,7 @@ static int hi3660_reset_dev(struct reset_controller_dev *rcdev, | ||||
| 	return hi3660_reset_deassert(rcdev, idx); | ||||
| } | ||||
| 
 | ||||
| static struct reset_control_ops hi3660_reset_ops = { | ||||
| static const struct reset_control_ops hi3660_reset_ops = { | ||||
| 	.reset    = hi3660_reset_dev, | ||||
| 	.assert   = hi3660_reset_assert, | ||||
| 	.deassert = hi3660_reset_deassert, | ||||
|  | ||||
| @ -19,6 +19,11 @@ struct meson_audio_arb_data { | ||||
| 	spinlock_t lock; | ||||
| }; | ||||
| 
 | ||||
| struct meson_audio_arb_match_data { | ||||
| 	const unsigned int *reset_bits; | ||||
| 	unsigned int reset_num; | ||||
| }; | ||||
| 
 | ||||
| #define ARB_GENERAL_BIT	31 | ||||
| 
 | ||||
| static const unsigned int axg_audio_arb_reset_bits[] = { | ||||
| @ -30,6 +35,27 @@ static const unsigned int axg_audio_arb_reset_bits[] = { | ||||
| 	[AXG_ARB_FRDDR_C]	= 6, | ||||
| }; | ||||
| 
 | ||||
| static const struct meson_audio_arb_match_data axg_audio_arb_match = { | ||||
| 	.reset_bits = axg_audio_arb_reset_bits, | ||||
| 	.reset_num = ARRAY_SIZE(axg_audio_arb_reset_bits), | ||||
| }; | ||||
| 
 | ||||
| static const unsigned int sm1_audio_arb_reset_bits[] = { | ||||
| 	[AXG_ARB_TODDR_A]	= 0, | ||||
| 	[AXG_ARB_TODDR_B]	= 1, | ||||
| 	[AXG_ARB_TODDR_C]	= 2, | ||||
| 	[AXG_ARB_FRDDR_A]	= 4, | ||||
| 	[AXG_ARB_FRDDR_B]	= 5, | ||||
| 	[AXG_ARB_FRDDR_C]	= 6, | ||||
| 	[AXG_ARB_TODDR_D]	= 3, | ||||
| 	[AXG_ARB_FRDDR_D]	= 7, | ||||
| }; | ||||
| 
 | ||||
| static const struct meson_audio_arb_match_data sm1_audio_arb_match = { | ||||
| 	.reset_bits = sm1_audio_arb_reset_bits, | ||||
| 	.reset_num = ARRAY_SIZE(sm1_audio_arb_reset_bits), | ||||
| }; | ||||
| 
 | ||||
| static int meson_audio_arb_update(struct reset_controller_dev *rcdev, | ||||
| 				  unsigned long id, bool assert) | ||||
| { | ||||
| @ -82,7 +108,13 @@ static const struct reset_control_ops meson_audio_arb_rstc_ops = { | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id meson_audio_arb_of_match[] = { | ||||
| 	{ .compatible = "amlogic,meson-axg-audio-arb", }, | ||||
| 	{ | ||||
| 		.compatible = "amlogic,meson-axg-audio-arb", | ||||
| 		.data = &axg_audio_arb_match, | ||||
| 	}, { | ||||
| 		.compatible = "amlogic,meson-sm1-audio-arb", | ||||
| 		.data = &sm1_audio_arb_match, | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match); | ||||
| @ -104,10 +136,15 @@ static int meson_audio_arb_remove(struct platform_device *pdev) | ||||
| static int meson_audio_arb_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	const struct meson_audio_arb_match_data *data; | ||||
| 	struct meson_audio_arb_data *arb; | ||||
| 	struct resource *res; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	data = of_device_get_match_data(dev); | ||||
| 	if (!data) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL); | ||||
| 	if (!arb) | ||||
| 		return -ENOMEM; | ||||
| @ -126,8 +163,8 @@ static int meson_audio_arb_probe(struct platform_device *pdev) | ||||
| 		return PTR_ERR(arb->regs); | ||||
| 
 | ||||
| 	spin_lock_init(&arb->lock); | ||||
| 	arb->reset_bits = axg_audio_arb_reset_bits; | ||||
| 	arb->rstc.nr_resets = ARRAY_SIZE(axg_audio_arb_reset_bits); | ||||
| 	arb->reset_bits = data->reset_bits; | ||||
| 	arb->rstc.nr_resets = data->reset_num; | ||||
| 	arb->rstc.ops = &meson_audio_arb_rstc_ops; | ||||
| 	arb->rstc.of_node = dev->of_node; | ||||
| 	arb->rstc.owner = THIS_MODULE; | ||||
|  | ||||
| @ -15,12 +15,16 @@ | ||||
| #include <linux/types.h> | ||||
| #include <linux/of_device.h> | ||||
| 
 | ||||
| #define REG_COUNT	8 | ||||
| #define BITS_PER_REG	32 | ||||
| #define LEVEL_OFFSET	0x7c | ||||
| 
 | ||||
| struct meson_reset_param { | ||||
| 	int reg_count; | ||||
| 	int level_offset; | ||||
| }; | ||||
| 
 | ||||
| struct meson_reset { | ||||
| 	void __iomem *reg_base; | ||||
| 	const struct meson_reset_param *param; | ||||
| 	struct reset_controller_dev rcdev; | ||||
| 	spinlock_t lock; | ||||
| }; | ||||
| @ -46,10 +50,12 @@ static int meson_reset_level(struct reset_controller_dev *rcdev, | ||||
| 		container_of(rcdev, struct meson_reset, rcdev); | ||||
| 	unsigned int bank = id / BITS_PER_REG; | ||||
| 	unsigned int offset = id % BITS_PER_REG; | ||||
| 	void __iomem *reg_addr = data->reg_base + LEVEL_OFFSET + (bank << 2); | ||||
| 	void __iomem *reg_addr; | ||||
| 	unsigned long flags; | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	reg_addr = data->reg_base + data->param->level_offset + (bank << 2); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&data->lock, flags); | ||||
| 
 | ||||
| 	reg = readl(reg_addr); | ||||
| @ -81,10 +87,21 @@ static const struct reset_control_ops meson_reset_ops = { | ||||
| 	.deassert	= meson_reset_deassert, | ||||
| }; | ||||
| 
 | ||||
| static const struct meson_reset_param meson8b_param = { | ||||
| 	.reg_count	= 8, | ||||
| 	.level_offset	= 0x7c, | ||||
| }; | ||||
| 
 | ||||
| static const struct meson_reset_param meson_a1_param = { | ||||
| 	.reg_count	= 3, | ||||
| 	.level_offset	= 0x40, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id meson_reset_dt_ids[] = { | ||||
| 	 { .compatible = "amlogic,meson8b-reset" }, | ||||
| 	 { .compatible = "amlogic,meson-gxbb-reset" }, | ||||
| 	 { .compatible = "amlogic,meson-axg-reset" }, | ||||
| 	 { .compatible = "amlogic,meson8b-reset",    .data = &meson8b_param}, | ||||
| 	 { .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param}, | ||||
| 	 { .compatible = "amlogic,meson-axg-reset",  .data = &meson8b_param}, | ||||
| 	 { .compatible = "amlogic,meson-a1-reset",   .data = &meson_a1_param}, | ||||
| 	 { /* sentinel */ }, | ||||
| }; | ||||
| 
 | ||||
| @ -102,12 +119,16 @@ static int meson_reset_probe(struct platform_device *pdev) | ||||
| 	if (IS_ERR(data->reg_base)) | ||||
| 		return PTR_ERR(data->reg_base); | ||||
| 
 | ||||
| 	data->param = of_device_get_match_data(&pdev->dev); | ||||
| 	if (!data->param) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, data); | ||||
| 
 | ||||
| 	spin_lock_init(&data->lock); | ||||
| 
 | ||||
| 	data->rcdev.owner = THIS_MODULE; | ||||
| 	data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG; | ||||
| 	data->rcdev.nr_resets = data->param->reg_count * BITS_PER_REG; | ||||
| 	data->rcdev.ops = &meson_reset_ops; | ||||
| 	data->rcdev.of_node = pdev->dev.of_node; | ||||
| 
 | ||||
|  | ||||
| @ -140,6 +140,10 @@ static const struct of_device_id uniphier_glue_reset_match[] = { | ||||
| 		.compatible = "socionext,uniphier-pro4-usb3-reset", | ||||
| 		.data = &uniphier_pro4_data, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible = "socionext,uniphier-pro5-usb3-reset", | ||||
| 		.data = &uniphier_pro4_data, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible = "socionext,uniphier-pxs2-usb3-reset", | ||||
| 		.data = &uniphier_pxs2_data, | ||||
|  | ||||
| @ -64,7 +64,7 @@ static int zynqmp_reset_reset(struct reset_controller_dev *rcdev, | ||||
| 					    PM_RESET_ACTION_PULSE); | ||||
| } | ||||
| 
 | ||||
| static struct reset_control_ops zynqmp_reset_ops = { | ||||
| static const struct reset_control_ops zynqmp_reset_ops = { | ||||
| 	.reset = zynqmp_reset_reset, | ||||
| 	.assert = zynqmp_reset_assert, | ||||
| 	.deassert = zynqmp_reset_deassert, | ||||
|  | ||||
| @ -40,6 +40,7 @@ static const struct meson_gx_soc_id { | ||||
| 	{ "G12A", 0x28 }, | ||||
| 	{ "G12B", 0x29 }, | ||||
| 	{ "SM1", 0x2b }, | ||||
| 	{ "A1", 0x2c }, | ||||
| }; | ||||
| 
 | ||||
| static const struct meson_gx_package_id { | ||||
| @ -68,6 +69,8 @@ static const struct meson_gx_package_id { | ||||
| 	{ "S922X", 0x29, 0x40, 0xf0 }, | ||||
| 	{ "A311D", 0x29, 0x10, 0xf0 }, | ||||
| 	{ "S905X3", 0x2b, 0x5, 0xf }, | ||||
| 	{ "S905D3", 0x2b, 0xb0, 0xf0 }, | ||||
| 	{ "A113L", 0x2c, 0x0, 0xf8 }, | ||||
| }; | ||||
| 
 | ||||
| static inline unsigned int socinfo_to_major(u32 socinfo) | ||||
|  | ||||
| @ -5,3 +5,14 @@ config AT91_SOC_ID | ||||
| 	default ARCH_AT91 | ||||
| 	help | ||||
| 	  Include support for the SoC bus on the Atmel ARM SoCs. | ||||
| 
 | ||||
| config AT91_SOC_SFR | ||||
| 	tristate "Special Function Registers support" | ||||
| 	depends on ARCH_AT91 || COMPILE_TEST | ||||
| 	help | ||||
| 	  This is a driver for the Special Function Registers available on | ||||
| 	  Atmel SAMA5Dx SoCs, providing access to specific aspects of the | ||||
| 	  integrated memory, bridge implementations, processor etc. | ||||
| 
 | ||||
| 	  This driver can also be built as a module. If so, the module | ||||
| 	  will be called sfr. | ||||
|  | ||||
| @ -1,2 +1,3 @@ | ||||
| # SPDX-License-Identifier: GPL-2.0-only
 | ||||
| obj-$(CONFIG_AT91_SOC_ID) += soc.o | ||||
| obj-$(CONFIG_AT91_SOC_SFR) += sfr.o | ||||
|  | ||||
							
								
								
									
										99
									
								
								drivers/soc/atmel/sfr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								drivers/soc/atmel/sfr.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * sfr.c - driver for special function registers | ||||
|  * | ||||
|  * Copyright (C) 2019 Bootlin. | ||||
|  * | ||||
|  */ | ||||
| #include <linux/mfd/syscon.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/nvmem-provider.h> | ||||
| #include <linux/random.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/regmap.h> | ||||
| 
 | ||||
| #define SFR_SN0		0x4c | ||||
| #define SFR_SN_SIZE	8 | ||||
| 
 | ||||
| struct atmel_sfr_priv { | ||||
| 	struct regmap			*regmap; | ||||
| }; | ||||
| 
 | ||||
| static int atmel_sfr_read(void *context, unsigned int offset, | ||||
| 			  void *buf, size_t bytes) | ||||
| { | ||||
| 	struct atmel_sfr_priv *priv = context; | ||||
| 
 | ||||
| 	return regmap_bulk_read(priv->regmap, SFR_SN0 + offset, | ||||
| 				buf, bytes / 4); | ||||
| } | ||||
| 
 | ||||
| static struct nvmem_config atmel_sfr_nvmem_config = { | ||||
| 	.name = "atmel-sfr", | ||||
| 	.read_only = true, | ||||
| 	.word_size = 4, | ||||
| 	.stride = 4, | ||||
| 	.size = SFR_SN_SIZE, | ||||
| 	.reg_read = atmel_sfr_read, | ||||
| }; | ||||
| 
 | ||||
| static int atmel_sfr_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct device_node *np = dev->of_node; | ||||
| 	struct nvmem_device *nvmem; | ||||
| 	struct atmel_sfr_priv *priv; | ||||
| 	u8 sn[SFR_SN_SIZE]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); | ||||
| 	if (!priv) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	priv->regmap = syscon_node_to_regmap(np); | ||||
| 	if (IS_ERR(priv->regmap)) { | ||||
| 		dev_err(dev, "cannot get parent's regmap\n"); | ||||
| 		return PTR_ERR(priv->regmap); | ||||
| 	} | ||||
| 
 | ||||
| 	atmel_sfr_nvmem_config.dev = dev; | ||||
| 	atmel_sfr_nvmem_config.priv = priv; | ||||
| 
 | ||||
| 	nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config); | ||||
| 	if (IS_ERR(nvmem)) { | ||||
| 		dev_err(dev, "error registering nvmem config\n"); | ||||
| 		return PTR_ERR(nvmem); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE); | ||||
| 	if (ret == 0) | ||||
| 		add_device_randomness(sn, SFR_SN_SIZE); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id atmel_sfr_dt_ids[] = { | ||||
| 	{ | ||||
| 		.compatible = "atmel,sama5d2-sfr", | ||||
| 	}, { | ||||
| 		.compatible = "atmel,sama5d4-sfr", | ||||
| 	}, { | ||||
| 		/* sentinel */ | ||||
| 	}, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids); | ||||
| 
 | ||||
| static struct platform_driver atmel_sfr_driver = { | ||||
| 	.probe = atmel_sfr_probe, | ||||
| 	.driver = { | ||||
| 		.name = "atmel-sfr", | ||||
| 		.of_match_table = atmel_sfr_dt_ids, | ||||
| 	}, | ||||
| }; | ||||
| module_platform_driver(atmel_sfr_driver); | ||||
| 
 | ||||
| MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); | ||||
| MODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| @ -40,4 +40,14 @@ config DPAA2_CONSOLE | ||||
| 	  /dev/dpaa2_mc_console and /dev/dpaa2_aiop_console, | ||||
| 	  which can be used to dump the Management Complex and AIOP | ||||
| 	  firmware logs. | ||||
| 
 | ||||
| config FSL_RCPM | ||||
| 	bool "Freescale RCPM support" | ||||
| 	depends on PM_SLEEP && (ARM || ARM64) | ||||
| 	help | ||||
| 	  The NXP QorIQ Processors based on ARM Core have RCPM module | ||||
| 	  (Run Control and Power Management), which performs all device-level | ||||
| 	  tasks associated with power management, such as wakeup source control. | ||||
| 	  Note that currently this driver will not support PowerPC based | ||||
| 	  QorIQ processor. | ||||
| endmenu | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| obj-$(CONFIG_FSL_DPAA)                 += qbman/ | ||||
| obj-$(CONFIG_QUICC_ENGINE)		+= qe/ | ||||
| obj-$(CONFIG_CPM)			+= qe/ | ||||
| obj-$(CONFIG_FSL_RCPM)			+= rcpm.o | ||||
| obj-$(CONFIG_FSL_GUTS)			+= guts.o | ||||
| obj-$(CONFIG_FSL_MC_DPIO) 		+= dpio/ | ||||
| obj-$(CONFIG_DPAA2_CONSOLE)		+= dpaa2-console.o | ||||
|  | ||||
							
								
								
									
										151
									
								
								drivers/soc/fsl/rcpm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								drivers/soc/fsl/rcpm.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| //
 | ||||
| // rcpm.c - Freescale QorIQ RCPM driver
 | ||||
| //
 | ||||
| // Copyright 2019 NXP
 | ||||
| //
 | ||||
| // Author: Ran Wang <ran.wang_1@nxp.com>
 | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/of_address.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/suspend.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #define RCPM_WAKEUP_CELL_MAX_SIZE	7 | ||||
| 
 | ||||
| struct rcpm { | ||||
| 	unsigned int	wakeup_cells; | ||||
| 	void __iomem	*ippdexpcr_base; | ||||
| 	bool		little_endian; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * rcpm_pm_prepare - performs device-level tasks associated with power | ||||
|  * management, such as programming related to the wakeup source control. | ||||
|  * @dev: Device to handle. | ||||
|  * | ||||
|  */ | ||||
| static int rcpm_pm_prepare(struct device *dev) | ||||
| { | ||||
| 	int i, ret, idx; | ||||
| 	void __iomem *base; | ||||
| 	struct wakeup_source	*ws; | ||||
| 	struct rcpm		*rcpm; | ||||
| 	struct device_node	*np = dev->of_node; | ||||
| 	u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1]; | ||||
| 	u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0}; | ||||
| 
 | ||||
| 	rcpm = dev_get_drvdata(dev); | ||||
| 	if (!rcpm) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	base = rcpm->ippdexpcr_base; | ||||
| 	idx = wakeup_sources_read_lock(); | ||||
| 
 | ||||
| 	/* Begin with first registered wakeup source */ | ||||
| 	for_each_wakeup_source(ws) { | ||||
| 
 | ||||
| 		/* skip object which is not attached to device */ | ||||
| 		if (!ws->dev || !ws->dev->parent) | ||||
| 			continue; | ||||
| 
 | ||||
| 		ret = device_property_read_u32_array(ws->dev->parent, | ||||
| 				"fsl,rcpm-wakeup", value, | ||||
| 				rcpm->wakeup_cells + 1); | ||||
| 
 | ||||
| 		/*  Wakeup source should refer to current rcpm device */ | ||||
| 		if (ret || (np->phandle != value[0])) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
 | ||||
| 		 * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup" | ||||
| 		 * of wakeup source IP contains an integer array: <phandle to | ||||
| 		 * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting, | ||||
| 		 * IPPDEXPCR2 setting, etc>. | ||||
| 		 * | ||||
| 		 * So we will go thought them to collect setting data. | ||||
| 		 */ | ||||
| 		for (i = 0; i < rcpm->wakeup_cells; i++) | ||||
| 			setting[i] |= value[i + 1]; | ||||
| 	} | ||||
| 
 | ||||
| 	wakeup_sources_read_unlock(idx); | ||||
| 
 | ||||
| 	/* Program all IPPDEXPCRn once */ | ||||
| 	for (i = 0; i < rcpm->wakeup_cells; i++) { | ||||
| 		u32 tmp = setting[i]; | ||||
| 		void __iomem *address = base + i * 4; | ||||
| 
 | ||||
| 		if (!tmp) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* We can only OR related bits */ | ||||
| 		if (rcpm->little_endian) { | ||||
| 			tmp |= ioread32(address); | ||||
| 			iowrite32(tmp, address); | ||||
| 		} else { | ||||
| 			tmp |= ioread32be(address); | ||||
| 			iowrite32be(tmp, address); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops rcpm_pm_ops = { | ||||
| 	.prepare =  rcpm_pm_prepare, | ||||
| }; | ||||
| 
 | ||||
| static int rcpm_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device	*dev = &pdev->dev; | ||||
| 	struct resource *r; | ||||
| 	struct rcpm	*rcpm; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL); | ||||
| 	if (!rcpm) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!r) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r); | ||||
| 	if (IS_ERR(rcpm->ippdexpcr_base)) { | ||||
| 		ret =  PTR_ERR(rcpm->ippdexpcr_base); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	rcpm->little_endian = device_property_read_bool( | ||||
| 			&pdev->dev, "little-endian"); | ||||
| 
 | ||||
| 	ret = device_property_read_u32(&pdev->dev, | ||||
| 			"#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	dev_set_drvdata(&pdev->dev, rcpm); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id rcpm_of_match[] = { | ||||
| 	{ .compatible = "fsl,qoriq-rcpm-2.1+", }, | ||||
| 	{} | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, rcpm_of_match); | ||||
| 
 | ||||
| static struct platform_driver rcpm_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "rcpm", | ||||
| 		.of_match_table = rcpm_of_match, | ||||
| 		.pm	= &rcpm_pm_ops, | ||||
| 	}, | ||||
| 	.probe = rcpm_probe, | ||||
| }; | ||||
| 
 | ||||
| module_platform_driver(rcpm_driver); | ||||
| @ -33,12 +33,10 @@ struct imx_sc_msg_misc_get_soc_uid { | ||||
| 	u32 uid_high; | ||||
| } __packed; | ||||
| 
 | ||||
| static ssize_t soc_uid_show(struct device *dev, | ||||
| 			    struct device_attribute *attr, char *buf) | ||||
| static int imx_scu_soc_uid(u64 *soc_uid) | ||||
| { | ||||
| 	struct imx_sc_msg_misc_get_soc_uid msg; | ||||
| 	struct imx_sc_rpc_msg *hdr = &msg.hdr; | ||||
| 	u64 soc_uid; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	hdr->ver = IMX_SC_RPC_VERSION; | ||||
| @ -52,15 +50,13 @@ static ssize_t soc_uid_show(struct device *dev, | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	soc_uid = msg.uid_high; | ||||
| 	soc_uid <<= 32; | ||||
| 	soc_uid |= msg.uid_low; | ||||
| 	*soc_uid = msg.uid_high; | ||||
| 	*soc_uid <<= 32; | ||||
| 	*soc_uid |= msg.uid_low; | ||||
| 
 | ||||
| 	return sprintf(buf, "%016llX\n", soc_uid); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static DEVICE_ATTR_RO(soc_uid); | ||||
| 
 | ||||
| static int imx_scu_soc_id(void) | ||||
| { | ||||
| 	struct imx_sc_msg_misc_get_soc_id msg; | ||||
| @ -89,6 +85,7 @@ static int imx_scu_soc_probe(struct platform_device *pdev) | ||||
| 	struct soc_device_attribute *soc_dev_attr; | ||||
| 	struct soc_device *soc_dev; | ||||
| 	int id, ret; | ||||
| 	u64 uid = 0; | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	ret = imx_scu_get_handle(&soc_ipc_handle); | ||||
| @ -112,6 +109,10 @@ static int imx_scu_soc_probe(struct platform_device *pdev) | ||||
| 	if (id < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = imx_scu_soc_uid(&uid); | ||||
| 	if (ret < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* format soc_id value passed from SCU firmware */ | ||||
| 	val = id & 0x1f; | ||||
| 	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val); | ||||
| @ -130,19 +131,22 @@ static int imx_scu_soc_probe(struct platform_device *pdev) | ||||
| 		goto free_soc_id; | ||||
| 	} | ||||
| 
 | ||||
| 	soc_dev = soc_device_register(soc_dev_attr); | ||||
| 	if (IS_ERR(soc_dev)) { | ||||
| 		ret = PTR_ERR(soc_dev); | ||||
| 	soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid); | ||||
| 	if (!soc_dev_attr->serial_number) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto free_revision; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = device_create_file(soc_device_to_device(soc_dev), | ||||
| 				 &dev_attr_soc_uid); | ||||
| 	if (ret) | ||||
| 		goto free_revision; | ||||
| 	soc_dev = soc_device_register(soc_dev_attr); | ||||
| 	if (IS_ERR(soc_dev)) { | ||||
| 		ret = PTR_ERR(soc_dev); | ||||
| 		goto free_serial_number; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| free_serial_number: | ||||
| 	kfree(soc_dev_attr->serial_number); | ||||
| free_revision: | ||||
| 	kfree(soc_dev_attr->revision); | ||||
| free_soc_id: | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #include <linux/slab.h> | ||||
| #include <linux/sys_soc.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/arm-smccc.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #define REV_B1				0x21 | ||||
| @ -16,6 +17,8 @@ | ||||
| #define IMX8MQ_SW_INFO_B1		0x40 | ||||
| #define IMX8MQ_SW_MAGIC_B1		0xff0055aa | ||||
| 
 | ||||
| #define IMX_SIP_GET_SOC_INFO		0xc2000006 | ||||
| 
 | ||||
| #define OCOTP_UID_LOW			0x410 | ||||
| #define OCOTP_UID_HIGH			0x420 | ||||
| 
 | ||||
| @ -29,13 +32,21 @@ struct imx8_soc_data { | ||||
| 
 | ||||
| static u64 soc_uid; | ||||
| 
 | ||||
| static ssize_t soc_uid_show(struct device *dev, | ||||
| 			    struct device_attribute *attr, char *buf) | ||||
| #ifdef CONFIG_HAVE_ARM_SMCCC | ||||
| static u32 imx8mq_soc_revision_from_atf(void) | ||||
| { | ||||
| 	return sprintf(buf, "%016llX\n", soc_uid); | ||||
| } | ||||
| 	struct arm_smccc_res res; | ||||
| 
 | ||||
| static DEVICE_ATTR_RO(soc_uid); | ||||
| 	arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); | ||||
| 
 | ||||
| 	if (res.a0 == SMCCC_RET_NOT_SUPPORTED) | ||||
| 		return 0; | ||||
| 	else | ||||
| 		return res.a0 & 0xff; | ||||
| } | ||||
| #else | ||||
| static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; }; | ||||
| #endif | ||||
| 
 | ||||
| static u32 __init imx8mq_soc_revision(void) | ||||
| { | ||||
| @ -51,9 +62,16 @@ static u32 __init imx8mq_soc_revision(void) | ||||
| 	ocotp_base = of_iomap(np, 0); | ||||
| 	WARN_ON(!ocotp_base); | ||||
| 
 | ||||
| 	magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); | ||||
| 	if (magic == IMX8MQ_SW_MAGIC_B1) | ||||
| 		rev = REV_B1; | ||||
| 	/*
 | ||||
| 	 * SOC revision on older imx8mq is not available in fuses so query | ||||
| 	 * the value from ATF instead. | ||||
| 	 */ | ||||
| 	rev = imx8mq_soc_revision_from_atf(); | ||||
| 	if (!rev) { | ||||
| 		magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); | ||||
| 		if (magic == IMX8MQ_SW_MAGIC_B1) | ||||
| 			rev = REV_B1; | ||||
| 	} | ||||
| 
 | ||||
| 	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); | ||||
| 	soc_uid <<= 32; | ||||
| @ -174,22 +192,25 @@ static int __init imx8_soc_init(void) | ||||
| 		goto free_soc; | ||||
| 	} | ||||
| 
 | ||||
| 	soc_dev = soc_device_register(soc_dev_attr); | ||||
| 	if (IS_ERR(soc_dev)) { | ||||
| 		ret = PTR_ERR(soc_dev); | ||||
| 	soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid); | ||||
| 	if (!soc_dev_attr->serial_number) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto free_rev; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = device_create_file(soc_device_to_device(soc_dev), | ||||
| 				 &dev_attr_soc_uid); | ||||
| 	if (ret) | ||||
| 		goto free_rev; | ||||
| 	soc_dev = soc_device_register(soc_dev_attr); | ||||
| 	if (IS_ERR(soc_dev)) { | ||||
| 		ret = PTR_ERR(soc_dev); | ||||
| 		goto free_serial_number; | ||||
| 	} | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) | ||||
| 		platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| free_serial_number: | ||||
| 	kfree(soc_dev_attr->serial_number); | ||||
| free_rev: | ||||
| 	if (strcmp(soc_dev_attr->revision, "unknown")) | ||||
| 		kfree(soc_dev_attr->revision); | ||||
|  | ||||
| @ -21,7 +21,7 @@ | ||||
| #include <dt-bindings/power/mt8173-power.h> | ||||
| 
 | ||||
| #define MTK_POLL_DELAY_US   10 | ||||
| #define MTK_POLL_TIMEOUT    (jiffies_to_usecs(HZ)) | ||||
| #define MTK_POLL_TIMEOUT    USEC_PER_SEC | ||||
| 
 | ||||
| #define MTK_SCPD_ACTIVE_WAKEUP		BIT(0) | ||||
| #define MTK_SCPD_FWAIT_SRAM		BIT(1) | ||||
| @ -108,6 +108,17 @@ static const char * const clk_names[] = { | ||||
| 
 | ||||
| #define MAX_CLKS	3 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct scp_domain_data - scp domain data for power on/off flow | ||||
|  * @name: The domain name. | ||||
|  * @sta_mask: The mask for power on/off status bit. | ||||
|  * @ctl_offs: The offset for main power control register. | ||||
|  * @sram_pdn_bits: The mask for sram power control bits. | ||||
|  * @sram_pdn_ack_bits: The mask for sram power control acked bits. | ||||
|  * @bus_prot_mask: The mask for single step bus protection. | ||||
|  * @clk_id: The basic clocks required by this power domain. | ||||
|  * @caps: The flag for active wake-up action. | ||||
|  */ | ||||
| struct scp_domain_data { | ||||
| 	const char *name; | ||||
| 	u32 sta_mask; | ||||
| @ -180,32 +191,132 @@ static int scpsys_domain_is_on(struct scp_domain *scpd) | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static int scpsys_regulator_enable(struct scp_domain *scpd) | ||||
| { | ||||
| 	if (!scpd->supply) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return regulator_enable(scpd->supply); | ||||
| } | ||||
| 
 | ||||
| static int scpsys_regulator_disable(struct scp_domain *scpd) | ||||
| { | ||||
| 	if (!scpd->supply) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return regulator_disable(scpd->supply); | ||||
| } | ||||
| 
 | ||||
| static void scpsys_clk_disable(struct clk *clk[], int max_num) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = max_num - 1; i >= 0; i--) | ||||
| 		clk_disable_unprepare(clk[i]); | ||||
| } | ||||
| 
 | ||||
| static int scpsys_clk_enable(struct clk *clk[], int max_num) | ||||
| { | ||||
| 	int i, ret = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < max_num && clk[i]; i++) { | ||||
| 		ret = clk_prepare_enable(clk[i]); | ||||
| 		if (ret) { | ||||
| 			scpsys_clk_disable(clk, i); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int scpsys_sram_enable(struct scp_domain *scpd, void __iomem *ctl_addr) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||||
| 	int tmp; | ||||
| 
 | ||||
| 	val = readl(ctl_addr); | ||||
| 	val &= ~scpd->data->sram_pdn_bits; | ||||
| 	writel(val, ctl_addr); | ||||
| 
 | ||||
| 	/* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ | ||||
| 	if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { | ||||
| 		/*
 | ||||
| 		 * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for | ||||
| 		 * MT7622_POWER_DOMAIN_WB and thus just a trivial setup | ||||
| 		 * is applied here. | ||||
| 		 */ | ||||
| 		usleep_range(12000, 12100); | ||||
| 	} else { | ||||
| 		/* Either wait until SRAM_PDN_ACK all 1 or 0 */ | ||||
| 		int ret = readl_poll_timeout(ctl_addr, tmp, | ||||
| 				(tmp & pdn_ack) == 0, | ||||
| 				MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||||
| 	int tmp; | ||||
| 
 | ||||
| 	val = readl(ctl_addr); | ||||
| 	val |= scpd->data->sram_pdn_bits; | ||||
| 	writel(val, ctl_addr); | ||||
| 
 | ||||
| 	/* Either wait until SRAM_PDN_ACK all 1 or 0 */ | ||||
| 	return readl_poll_timeout(ctl_addr, tmp, | ||||
| 			(tmp & pdn_ack) == pdn_ack, | ||||
| 			MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||||
| } | ||||
| 
 | ||||
| static int scpsys_bus_protect_enable(struct scp_domain *scpd) | ||||
| { | ||||
| 	struct scp *scp = scpd->scp; | ||||
| 
 | ||||
| 	if (!scpd->data->bus_prot_mask) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return mtk_infracfg_set_bus_protection(scp->infracfg, | ||||
| 			scpd->data->bus_prot_mask, | ||||
| 			scp->bus_prot_reg_update); | ||||
| } | ||||
| 
 | ||||
| static int scpsys_bus_protect_disable(struct scp_domain *scpd) | ||||
| { | ||||
| 	struct scp *scp = scpd->scp; | ||||
| 
 | ||||
| 	if (!scpd->data->bus_prot_mask) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return mtk_infracfg_clear_bus_protection(scp->infracfg, | ||||
| 			scpd->data->bus_prot_mask, | ||||
| 			scp->bus_prot_reg_update); | ||||
| } | ||||
| 
 | ||||
| static int scpsys_power_on(struct generic_pm_domain *genpd) | ||||
| { | ||||
| 	struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); | ||||
| 	struct scp *scp = scpd->scp; | ||||
| 	void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; | ||||
| 	u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||||
| 	u32 val; | ||||
| 	int ret, tmp; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (scpd->supply) { | ||||
| 		ret = regulator_enable(scpd->supply); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	ret = scpsys_regulator_enable(scpd); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { | ||||
| 		ret = clk_prepare_enable(scpd->clk[i]); | ||||
| 		if (ret) { | ||||
| 			for (--i; i >= 0; i--) | ||||
| 				clk_disable_unprepare(scpd->clk[i]); | ||||
| 
 | ||||
| 			goto err_clk; | ||||
| 		} | ||||
| 	} | ||||
| 	ret = scpsys_clk_enable(scpd->clk, MAX_CLKS); | ||||
| 	if (ret) | ||||
| 		goto err_clk; | ||||
| 
 | ||||
| 	/* subsys power on */ | ||||
| 	val = readl(ctl_addr); | ||||
| 	val |= PWR_ON_BIT; | ||||
| 	writel(val, ctl_addr); | ||||
| @ -227,43 +338,20 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) | ||||
| 	val |= PWR_RST_B_BIT; | ||||
| 	writel(val, ctl_addr); | ||||
| 
 | ||||
| 	val &= ~scpd->data->sram_pdn_bits; | ||||
| 	writel(val, ctl_addr); | ||||
| 	ret = scpsys_sram_enable(scpd, ctl_addr); | ||||
| 	if (ret < 0) | ||||
| 		goto err_pwr_ack; | ||||
| 
 | ||||
| 	/* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ | ||||
| 	if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { | ||||
| 		/*
 | ||||
| 		 * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for | ||||
| 		 * MT7622_POWER_DOMAIN_WB and thus just a trivial setup is | ||||
| 		 * applied here. | ||||
| 		 */ | ||||
| 		usleep_range(12000, 12100); | ||||
| 
 | ||||
| 	} else { | ||||
| 		ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == 0, | ||||
| 					 MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||||
| 		if (ret < 0) | ||||
| 			goto err_pwr_ack; | ||||
| 	} | ||||
| 
 | ||||
| 	if (scpd->data->bus_prot_mask) { | ||||
| 		ret = mtk_infracfg_clear_bus_protection(scp->infracfg, | ||||
| 				scpd->data->bus_prot_mask, | ||||
| 				scp->bus_prot_reg_update); | ||||
| 		if (ret) | ||||
| 			goto err_pwr_ack; | ||||
| 	} | ||||
| 	ret = scpsys_bus_protect_disable(scpd); | ||||
| 	if (ret < 0) | ||||
| 		goto err_pwr_ack; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_pwr_ack: | ||||
| 	for (i = MAX_CLKS - 1; i >= 0; i--) { | ||||
| 		if (scpd->clk[i]) | ||||
| 			clk_disable_unprepare(scpd->clk[i]); | ||||
| 	} | ||||
| 	scpsys_clk_disable(scpd->clk, MAX_CLKS); | ||||
| err_clk: | ||||
| 	if (scpd->supply) | ||||
| 		regulator_disable(scpd->supply); | ||||
| 	scpsys_regulator_disable(scpd); | ||||
| 
 | ||||
| 	dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); | ||||
| 
 | ||||
| @ -275,29 +363,19 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) | ||||
| 	struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); | ||||
| 	struct scp *scp = scpd->scp; | ||||
| 	void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; | ||||
| 	u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||||
| 	u32 val; | ||||
| 	int ret, tmp; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (scpd->data->bus_prot_mask) { | ||||
| 		ret = mtk_infracfg_set_bus_protection(scp->infracfg, | ||||
| 				scpd->data->bus_prot_mask, | ||||
| 				scp->bus_prot_reg_update); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	val = readl(ctl_addr); | ||||
| 	val |= scpd->data->sram_pdn_bits; | ||||
| 	writel(val, ctl_addr); | ||||
| 
 | ||||
| 	/* wait until SRAM_PDN_ACK all 1 */ | ||||
| 	ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack, | ||||
| 				 MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||||
| 	ret = scpsys_bus_protect_enable(scpd); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	ret = scpsys_sram_disable(scpd, ctl_addr); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* subsys power off */ | ||||
| 	val = readl(ctl_addr); | ||||
| 	val |= PWR_ISO_BIT; | ||||
| 	writel(val, ctl_addr); | ||||
| 
 | ||||
| @ -319,11 +397,11 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) | ||||
| 		clk_disable_unprepare(scpd->clk[i]); | ||||
| 	scpsys_clk_disable(scpd->clk, MAX_CLKS); | ||||
| 
 | ||||
| 	if (scpd->supply) | ||||
| 		regulator_disable(scpd->supply); | ||||
| 	ret = scpsys_regulator_disable(scpd); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  | ||||
| @ -58,17 +58,9 @@ config QCOM_LLCC | ||||
| 	depends on ARCH_QCOM || COMPILE_TEST | ||||
| 	help | ||||
| 	  Qualcomm Technologies, Inc. platform specific | ||||
| 	  Last Level Cache Controller(LLCC) driver. This provides interfaces | ||||
| 	  to clients that use the LLCC. Say yes here to enable LLCC slice | ||||
| 	  driver. | ||||
| 
 | ||||
| config QCOM_SDM845_LLCC | ||||
| 	tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver" | ||||
| 	depends on QCOM_LLCC | ||||
| 	help | ||||
| 	  Say yes here to enable the LLCC driver for SDM845. This provides | ||||
| 	  data required to configure LLCC so that clients can start using the | ||||
| 	  LLCC slices. | ||||
| 	  Last Level Cache Controller(LLCC) driver for platforms such as, | ||||
| 	  SDM845. This provides interfaces to clients that use the LLCC. | ||||
| 	  Say yes here to enable LLCC slice driver. | ||||
| 
 | ||||
| config QCOM_MDT_LOADER | ||||
| 	tristate | ||||
|  | ||||
| @ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_SMSM)	+= smsm.o | ||||
| obj-$(CONFIG_QCOM_SOCINFO)	+= socinfo.o | ||||
| obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o | ||||
| obj-$(CONFIG_QCOM_APR) += apr.o | ||||
| obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o | ||||
| obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o | ||||
| obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o | ||||
| obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o | ||||
| obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | ||||
|  * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| @ -11,6 +11,7 @@ | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/sizes.h> | ||||
| @ -46,15 +47,90 @@ | ||||
| 
 | ||||
| #define BANK_OFFSET_STRIDE	      0x80000 | ||||
| 
 | ||||
| static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; | ||||
| 
 | ||||
| static const struct regmap_config llcc_regmap_config = { | ||||
| 	.reg_bits = 32, | ||||
| 	.reg_stride = 4, | ||||
| 	.val_bits = 32, | ||||
| 	.fast_io = true, | ||||
| /**
 | ||||
|  * llcc_slice_config - Data associated with the llcc slice | ||||
|  * @usecase_id: Unique id for the client's use case | ||||
|  * @slice_id: llcc slice id for each client | ||||
|  * @max_cap: The maximum capacity of the cache slice provided in KB | ||||
|  * @priority: Priority of the client used to select victim line for replacement | ||||
|  * @fixed_size: Boolean indicating if the slice has a fixed capacity | ||||
|  * @bonus_ways: Bonus ways are additional ways to be used for any slice, | ||||
|  *		if client ends up using more than reserved cache ways. Bonus | ||||
|  *		ways are allocated only if they are not reserved for some | ||||
|  *		other client. | ||||
|  * @res_ways: Reserved ways for the cache slice, the reserved ways cannot | ||||
|  *		be used by any other client than the one its assigned to. | ||||
|  * @cache_mode: Each slice operates as a cache, this controls the mode of the | ||||
|  *             slice: normal or TCM(Tightly Coupled Memory) | ||||
|  * @probe_target_ways: Determines what ways to probe for access hit. When | ||||
|  *                    configured to 1 only bonus and reserved ways are probed. | ||||
|  *                    When configured to 0 all ways in llcc are probed. | ||||
|  * @dis_cap_alloc: Disable capacity based allocation for a client | ||||
|  * @retain_on_pc: If this bit is set and client has maintained active vote | ||||
|  *               then the ways assigned to this client are not flushed on power | ||||
|  *               collapse. | ||||
|  * @activate_on_init: Activate the slice immediately after it is programmed | ||||
|  */ | ||||
| struct llcc_slice_config { | ||||
| 	u32 usecase_id; | ||||
| 	u32 slice_id; | ||||
| 	u32 max_cap; | ||||
| 	u32 priority; | ||||
| 	bool fixed_size; | ||||
| 	u32 bonus_ways; | ||||
| 	u32 res_ways; | ||||
| 	u32 cache_mode; | ||||
| 	u32 probe_target_ways; | ||||
| 	bool dis_cap_alloc; | ||||
| 	bool retain_on_pc; | ||||
| 	bool activate_on_init; | ||||
| }; | ||||
| 
 | ||||
| struct qcom_llcc_config { | ||||
| 	const struct llcc_slice_config *sct_data; | ||||
| 	int size; | ||||
| }; | ||||
| 
 | ||||
| static const struct llcc_slice_config sc7180_data[] =  { | ||||
| 	{ LLCC_CPUSS,    1,  256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 }, | ||||
| 	{ LLCC_MDM,      8,  128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, | ||||
| 	{ LLCC_GPUHTW,   11, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, | ||||
| 	{ LLCC_GPU,      12, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct llcc_slice_config sdm845_data[] =  { | ||||
| 	{ LLCC_CPUSS,    1,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 1 }, | ||||
| 	{ LLCC_VIDSC0,   2,  512,  2, 1, 0x0,   0x0f0, 0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_VIDSC1,   3,  512,  2, 1, 0x0,   0x0f0, 0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_ROTATOR,  4,  563,  2, 1, 0x0,   0x00e, 2, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_VOICE,    5,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_AUDIO,    6,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_MDMHPGRW, 7,  1024, 2, 0, 0xfc,  0xf00, 0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_MDM,      8,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_CMPT,     10, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_GPUHTW,   11, 512,  1, 1, 0xc,   0x0,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_GPU,      12, 2304, 1, 0, 0xff0, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_MMUHWT,   13, 256,  2, 0, 0x0,   0x1,   0, 0, 1, 0, 1 }, | ||||
| 	{ LLCC_CMPTDMA,  15, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_DISP,     16, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_VIDFW,    17, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_MDMHPFX,  20, 1024, 2, 1, 0x0,   0xf00, 0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_MDMPNG,   21, 1024, 0, 1, 0x1e,  0x0,   0, 0, 1, 1, 0 }, | ||||
| 	{ LLCC_AUDHW,    22, 1024, 1, 1, 0xffc, 0x2,   0, 0, 1, 1, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct qcom_llcc_config sc7180_cfg = { | ||||
| 	.sct_data	= sc7180_data, | ||||
| 	.size		= ARRAY_SIZE(sc7180_data), | ||||
| }; | ||||
| 
 | ||||
| static const struct qcom_llcc_config sdm845_cfg = { | ||||
| 	.sct_data	= sdm845_data, | ||||
| 	.size		= ARRAY_SIZE(sdm845_data), | ||||
| }; | ||||
| 
 | ||||
| static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; | ||||
| 
 | ||||
| /**
 | ||||
|  * llcc_slice_getd - get llcc slice descriptor | ||||
|  * @uid: usecase_id for the client | ||||
| @ -301,19 +377,24 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int qcom_llcc_remove(struct platform_device *pdev) | ||||
| static int qcom_llcc_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	/* Set the global pointer to a error code to avoid referencing it */ | ||||
| 	drv_data = ERR_PTR(-ENODEV); | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(qcom_llcc_remove); | ||||
| 
 | ||||
| static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, | ||||
| 		const char *name) | ||||
| { | ||||
| 	struct resource *res; | ||||
| 	void __iomem *base; | ||||
| 	struct regmap_config llcc_regmap_config = { | ||||
| 		.reg_bits = 32, | ||||
| 		.reg_stride = 4, | ||||
| 		.val_bits = 32, | ||||
| 		.fast_io = true, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | ||||
| 	if (!res) | ||||
| @ -323,16 +404,19 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, | ||||
| 	if (IS_ERR(base)) | ||||
| 		return ERR_CAST(base); | ||||
| 
 | ||||
| 	llcc_regmap_config.name = name; | ||||
| 	return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); | ||||
| } | ||||
| 
 | ||||
| int qcom_llcc_probe(struct platform_device *pdev, | ||||
| 		      const struct llcc_slice_config *llcc_cfg, u32 sz) | ||||
| static int qcom_llcc_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	u32 num_banks; | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	int ret, i; | ||||
| 	struct platform_device *llcc_edac; | ||||
| 	const struct qcom_llcc_config *cfg; | ||||
| 	const struct llcc_slice_config *llcc_cfg; | ||||
| 	u32 sz; | ||||
| 
 | ||||
| 	drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); | ||||
| 	if (!drv_data) { | ||||
| @ -362,6 +446,10 @@ int qcom_llcc_probe(struct platform_device *pdev, | ||||
| 	num_banks >>= LLCC_LB_CNT_SHIFT; | ||||
| 	drv_data->num_banks = num_banks; | ||||
| 
 | ||||
| 	cfg = of_device_get_match_data(&pdev->dev); | ||||
| 	llcc_cfg = cfg->sct_data; | ||||
| 	sz = cfg->size; | ||||
| 
 | ||||
| 	for (i = 0; i < sz; i++) | ||||
| 		if (llcc_cfg[i].slice_id > drv_data->max_slices) | ||||
| 			drv_data->max_slices = llcc_cfg[i].slice_id; | ||||
| @ -407,6 +495,22 @@ err: | ||||
| 	drv_data = ERR_PTR(-ENODEV); | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(qcom_llcc_probe); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| 
 | ||||
| static const struct of_device_id qcom_llcc_of_match[] = { | ||||
| 	{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, | ||||
| 	{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, | ||||
| 	{ } | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver qcom_llcc_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "qcom-llcc", | ||||
| 		.of_match_table = qcom_llcc_of_match, | ||||
| 	}, | ||||
| 	.probe = qcom_llcc_probe, | ||||
| 	.remove = qcom_llcc_remove, | ||||
| }; | ||||
| module_platform_driver(qcom_llcc_driver); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| @ -1,100 +0,0 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/soc/qcom/llcc-qcom.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * SCT(System Cache Table) entry contains of the following members: | ||||
|  * usecase_id: Unique id for the client's use case | ||||
|  * slice_id: llcc slice id for each client | ||||
|  * max_cap: The maximum capacity of the cache slice provided in KB | ||||
|  * priority: Priority of the client used to select victim line for replacement | ||||
|  * fixed_size: Boolean indicating if the slice has a fixed capacity | ||||
|  * bonus_ways: Bonus ways are additional ways to be used for any slice, | ||||
|  *		if client ends up using more than reserved cache ways. Bonus | ||||
|  *		ways are allocated only if they are not reserved for some | ||||
|  *		other client. | ||||
|  * res_ways: Reserved ways for the cache slice, the reserved ways cannot | ||||
|  *		be used by any other client than the one its assigned to. | ||||
|  * cache_mode: Each slice operates as a cache, this controls the mode of the | ||||
|  *             slice: normal or TCM(Tightly Coupled Memory) | ||||
|  * probe_target_ways: Determines what ways to probe for access hit. When | ||||
|  *                    configured to 1 only bonus and reserved ways are probed. | ||||
|  *                    When configured to 0 all ways in llcc are probed. | ||||
|  * dis_cap_alloc: Disable capacity based allocation for a client | ||||
|  * retain_on_pc: If this bit is set and client has maintained active vote | ||||
|  *               then the ways assigned to this client are not flushed on power | ||||
|  *               collapse. | ||||
|  * activate_on_init: Activate the slice immediately after the SCT is programmed | ||||
|  */ | ||||
| #define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \ | ||||
| 	{					\ | ||||
| 		.usecase_id = uid,		\ | ||||
| 		.slice_id = sid,		\ | ||||
| 		.max_cap = mc,			\ | ||||
| 		.priority = p,			\ | ||||
| 		.fixed_size = fs,		\ | ||||
| 		.bonus_ways = bway,		\ | ||||
| 		.res_ways = rway,		\ | ||||
| 		.cache_mode = cmod,		\ | ||||
| 		.probe_target_ways = ptw,	\ | ||||
| 		.dis_cap_alloc = dca,		\ | ||||
| 		.retain_on_pc = rp,		\ | ||||
| 		.activate_on_init = a,		\ | ||||
| 	} | ||||
| 
 | ||||
| static struct llcc_slice_config sdm845_data[] =  { | ||||
| 	SCT_ENTRY(LLCC_CPUSS,    1,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 1), | ||||
| 	SCT_ENTRY(LLCC_VIDSC0,   2,  512,  2, 1, 0x0,   0x0f0, 0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_VIDSC1,   3,  512,  2, 1, 0x0,   0x0f0, 0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_ROTATOR,  4,  563,  2, 1, 0x0,   0x00e, 2, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_VOICE,    5,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_AUDIO,    6,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_MDMHPGRW, 7,  1024, 2, 0, 0xfc,  0xf00, 0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_MDM,      8,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_CMPT,     10, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_GPUHTW,   11, 512,  1, 1, 0xc,   0x0,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_GPU,      12, 2304, 1, 0, 0xff0, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_MMUHWT,   13, 256,  2, 0, 0x0,   0x1,   0, 0, 1, 0, 1), | ||||
| 	SCT_ENTRY(LLCC_CMPTDMA,  15, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_DISP,     16, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_VIDFW,    17, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_MDMHPFX,  20, 1024, 2, 1, 0x0,   0xf00, 0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_MDMPNG,   21, 1024, 0, 1, 0x1e,  0x0,   0, 0, 1, 1, 0), | ||||
| 	SCT_ENTRY(LLCC_AUDHW,    22, 1024, 1, 1, 0xffc, 0x2,   0, 0, 1, 1, 0), | ||||
| }; | ||||
| 
 | ||||
| static int sdm845_qcom_llcc_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	return qcom_llcc_remove(pdev); | ||||
| } | ||||
| 
 | ||||
| static int sdm845_qcom_llcc_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id sdm845_qcom_llcc_of_match[] = { | ||||
| 	{ .compatible = "qcom,sdm845-llcc", }, | ||||
| 	{ } | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver sdm845_qcom_llcc_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "sdm845-llcc", | ||||
| 		.of_match_table = sdm845_qcom_llcc_of_match, | ||||
| 	}, | ||||
| 	.probe = sdm845_qcom_llcc_probe, | ||||
| 	.remove = sdm845_qcom_llcc_remove, | ||||
| }; | ||||
| module_platform_driver(sdm845_qcom_llcc_driver); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| @ -115,6 +115,28 @@ struct rpmpd_desc { | ||||
| 
 | ||||
| static DEFINE_MUTEX(rpmpd_lock); | ||||
| 
 | ||||
| /* msm8976 RPM Power Domains */ | ||||
| DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2); | ||||
| DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6); | ||||
| 
 | ||||
| DEFINE_RPMPD_VFL(msm8976, vddcx_vfl, RWSC, 2); | ||||
| DEFINE_RPMPD_VFL(msm8976, vddmx_vfl, RWSM, 6); | ||||
| 
 | ||||
| static struct rpmpd *msm8976_rpmpds[] = { | ||||
| 	[MSM8976_VDDCX] =	&msm8976_vddcx, | ||||
| 	[MSM8976_VDDCX_AO] =	&msm8976_vddcx_ao, | ||||
| 	[MSM8976_VDDCX_VFL] =	&msm8976_vddcx_vfl, | ||||
| 	[MSM8976_VDDMX] =	&msm8976_vddmx, | ||||
| 	[MSM8976_VDDMX_AO] =	&msm8976_vddmx_ao, | ||||
| 	[MSM8976_VDDMX_VFL] =	&msm8976_vddmx_vfl, | ||||
| }; | ||||
| 
 | ||||
| static const struct rpmpd_desc msm8976_desc = { | ||||
| 	.rpmpds = msm8976_rpmpds, | ||||
| 	.num_pds = ARRAY_SIZE(msm8976_rpmpds), | ||||
| 	.max_state = RPM_SMD_LEVEL_TURBO_HIGH, | ||||
| }; | ||||
| 
 | ||||
| /* msm8996 RPM Power domains */ | ||||
| DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1); | ||||
| DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2); | ||||
| @ -198,6 +220,7 @@ static const struct rpmpd_desc qcs404_desc = { | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id rpmpd_match_table[] = { | ||||
| 	{ .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, | ||||
| 	{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, | ||||
| 	{ .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, | ||||
| 	{ .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, | ||||
|  | ||||
| @ -19,12 +19,14 @@ | ||||
| /**
 | ||||
|  * struct qcom_smd_rpm - state of the rpm device driver | ||||
|  * @rpm_channel:	reference to the smd channel | ||||
|  * @icc:		interconnect proxy device | ||||
|  * @ack:		completion for acks | ||||
|  * @lock:		mutual exclusion around the send/complete pair | ||||
|  * @ack_status:		result of the rpm request | ||||
|  */ | ||||
| struct qcom_smd_rpm { | ||||
| 	struct rpmsg_endpoint *rpm_channel; | ||||
| 	struct platform_device *icc; | ||||
| 	struct device *dev; | ||||
| 
 | ||||
| 	struct completion ack; | ||||
| @ -193,6 +195,7 @@ static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, | ||||
| static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) | ||||
| { | ||||
| 	struct qcom_smd_rpm *rpm; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); | ||||
| 	if (!rpm) | ||||
| @ -205,11 +208,23 @@ static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) | ||||
| 	rpm->rpm_channel = rpdev->ept; | ||||
| 	dev_set_drvdata(&rpdev->dev, rpm); | ||||
| 
 | ||||
| 	return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); | ||||
| 	rpm->icc = platform_device_register_data(&rpdev->dev, "icc_smd_rpm", -1, | ||||
| 						 NULL, 0); | ||||
| 	if (IS_ERR(rpm->icc)) | ||||
| 		return PTR_ERR(rpm->icc); | ||||
| 
 | ||||
| 	ret = of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); | ||||
| 	if (ret) | ||||
| 		platform_device_unregister(rpm->icc); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) | ||||
| { | ||||
| 	struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev); | ||||
| 
 | ||||
| 	platform_device_unregister(rpm->icc); | ||||
| 	of_platform_depopulate(&rpdev->dev); | ||||
| } | ||||
| 
 | ||||
| @ -217,6 +232,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { | ||||
| 	{ .compatible = "qcom,rpm-apq8084" }, | ||||
| 	{ .compatible = "qcom,rpm-msm8916" }, | ||||
| 	{ .compatible = "qcom,rpm-msm8974" }, | ||||
| 	{ .compatible = "qcom,rpm-msm8976" }, | ||||
| 	{ .compatible = "qcom,rpm-msm8996" }, | ||||
| 	{ .compatible = "qcom,rpm-msm8998" }, | ||||
| 	{ .compatible = "qcom,rpm-sdm660" }, | ||||
|  | ||||
| @ -198,6 +198,8 @@ static const struct soc_id soc_id[] = { | ||||
| 	{ 310, "MSM8996AU" }, | ||||
| 	{ 311, "APQ8096AU" }, | ||||
| 	{ 312, "APQ8096SG" }, | ||||
| 	{ 321, "SDM845" }, | ||||
| 	{ 341, "SDA845" }, | ||||
| }; | ||||
| 
 | ||||
| static const char *socinfo_machine(struct device *dev, unsigned int id) | ||||
|  | ||||
| @ -178,6 +178,13 @@ config ARCH_R8A774A1 | ||||
| 	help | ||||
| 	  This enables support for the Renesas RZ/G2M SoC. | ||||
| 
 | ||||
| config ARCH_R8A774B1 | ||||
| 	bool "Renesas RZ/G2N SoC Platform" | ||||
| 	select ARCH_RCAR_GEN3 | ||||
| 	select SYSC_R8A774B1 | ||||
| 	help | ||||
| 	  This enables support for the Renesas RZ/G2N SoC. | ||||
| 
 | ||||
| config ARCH_R8A774C0 | ||||
| 	bool "Renesas RZ/G2E SoC Platform" | ||||
| 	select ARCH_RCAR_GEN3 | ||||
| @ -192,13 +199,24 @@ config ARCH_R8A7795 | ||||
| 	help | ||||
| 	  This enables support for the Renesas R-Car H3 SoC. | ||||
| 
 | ||||
| config ARCH_R8A77960 | ||||
| 	bool | ||||
| 	select ARCH_RCAR_GEN3 | ||||
| 	select SYSC_R8A77960 | ||||
| 
 | ||||
| config ARCH_R8A7796 | ||||
| 	bool "Renesas R-Car M3-W SoC Platform" | ||||
| 	select ARCH_RCAR_GEN3 | ||||
| 	select SYSC_R8A7796 | ||||
| 	select ARCH_R8A77960 | ||||
| 	help | ||||
| 	  This enables support for the Renesas R-Car M3-W SoC. | ||||
| 
 | ||||
| config ARCH_R8A77961 | ||||
| 	bool "Renesas R-Car M3-W+ SoC Platform" | ||||
| 	select ARCH_RCAR_GEN3 | ||||
| 	select SYSC_R8A77961 | ||||
| 	help | ||||
| 	  This enables support for the Renesas R-Car M3-W+ SoC. | ||||
| 
 | ||||
| config ARCH_R8A77965 | ||||
| 	bool "Renesas R-Car M3-N SoC Platform" | ||||
| 	select ARCH_RCAR_GEN3 | ||||
| @ -253,6 +271,10 @@ config SYSC_R8A774A1 | ||||
| 	bool "RZ/G2M System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
| 
 | ||||
| config SYSC_R8A774B1 | ||||
| 	bool "RZ/G2N System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
| 
 | ||||
| config SYSC_R8A774C0 | ||||
| 	bool "RZ/G2E System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
| @ -281,10 +303,14 @@ config SYSC_R8A7795 | ||||
| 	bool "R-Car H3 System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
| 
 | ||||
| config SYSC_R8A7796 | ||||
| config SYSC_R8A77960 | ||||
| 	bool "R-Car M3-W System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
| 
 | ||||
| config SYSC_R8A77961 | ||||
| 	bool "R-Car M3-W+ System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
| 
 | ||||
| config SYSC_R8A77965 | ||||
| 	bool "R-Car M3-N System Controller support" if COMPILE_TEST | ||||
| 	select SYSC_RCAR | ||||
|  | ||||
| @ -7,6 +7,7 @@ obj-$(CONFIG_SYSC_R8A7743)	+= r8a7743-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7745)	+= r8a7745-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A77470)	+= r8a77470-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A774A1)	+= r8a774a1-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A774B1)	+= r8a774b1-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A774C0)	+= r8a774c0-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7779)	+= r8a7779-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7790)	+= r8a7790-sysc.o | ||||
| @ -14,7 +15,8 @@ obj-$(CONFIG_SYSC_R8A7791)	+= r8a7791-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7792)	+= r8a7792-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7794)	+= r8a7794-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7795)	+= r8a7795-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A7796)	+= r8a7796-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A77960)	+= r8a7796-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A77961)	+= r8a7796-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A77965)	+= r8a77965-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A77970)	+= r8a77970-sysc.o | ||||
| obj-$(CONFIG_SYSC_R8A77980)	+= r8a77980-sysc.o | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Cogent Embedded Inc. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7743-sysc.h> | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Cogent Embedded Inc. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7745-sysc.h> | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2018 Renesas Electronics Corp. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a77470-sysc.h> | ||||
|  | ||||
| @ -7,7 +7,6 @@ | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a774a1-sysc.h> | ||||
|  | ||||
							
								
								
									
										37
									
								
								drivers/soc/renesas/r8a774b1-sysc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								drivers/soc/renesas/r8a774b1-sysc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Renesas RZ/G2N System Controller | ||||
|  * Copyright (C) 2019 Renesas Electronics Corp. | ||||
|  * | ||||
|  * Based on Renesas R-Car M3-W System Controller | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a774b1-sysc.h> | ||||
| 
 | ||||
| #include "rcar-sysc.h" | ||||
| 
 | ||||
| static const struct rcar_sysc_area r8a774b1_areas[] __initconst = { | ||||
| 	{ "always-on",	    0, 0, R8A774B1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, | ||||
| 	{ "ca57-scu",	0x1c0, 0, R8A774B1_PD_CA57_SCU,	R8A774B1_PD_ALWAYS_ON, | ||||
| 	  PD_SCU }, | ||||
| 	{ "ca57-cpu0",	 0x80, 0, R8A774B1_PD_CA57_CPU0, R8A774B1_PD_CA57_SCU, | ||||
| 	  PD_CPU_NOCR }, | ||||
| 	{ "ca57-cpu1",	 0x80, 1, R8A774B1_PD_CA57_CPU1, R8A774B1_PD_CA57_SCU, | ||||
| 	  PD_CPU_NOCR }, | ||||
| 	{ "a3vc",	0x380, 0, R8A774B1_PD_A3VC,	R8A774B1_PD_ALWAYS_ON }, | ||||
| 	{ "a3vp",	0x340, 0, R8A774B1_PD_A3VP,	R8A774B1_PD_ALWAYS_ON }, | ||||
| 	{ "a2vc1",	0x3c0, 1, R8A774B1_PD_A2VC1,	R8A774B1_PD_A3VC }, | ||||
| 	{ "3dg-a",	0x100, 0, R8A774B1_PD_3DG_A,	R8A774B1_PD_ALWAYS_ON }, | ||||
| 	{ "3dg-b",	0x100, 1, R8A774B1_PD_3DG_B,	R8A774B1_PD_3DG_A }, | ||||
| }; | ||||
| 
 | ||||
| const struct rcar_sysc_info r8a774b1_sysc_info __initconst = { | ||||
| 	.areas = r8a774b1_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a774b1_areas), | ||||
| 	.extmask_offs = 0x2f8, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
| @ -6,7 +6,7 @@ | ||||
|  * Based on Renesas R-Car E3 System Controller | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sys_soc.h> | ||||
| 
 | ||||
| @ -50,4 +50,6 @@ const struct rcar_sysc_info r8a774c0_sysc_info __initconst = { | ||||
| 	.init = r8a774c0_sysc_init, | ||||
| 	.areas = r8a774c0_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a774c0_areas), | ||||
| 	.extmask_offs = 0x2f8, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7779-sysc.h> | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7790-sysc.h> | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7791-sysc.h> | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Cogent Embedded Inc. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7794-sysc.h> | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  * Copyright (C) 2016-2017 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sys_soc.h> | ||||
| 
 | ||||
| @ -51,25 +51,46 @@ static struct rcar_sysc_area r8a7795_areas[] __initdata = { | ||||
| 
 | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Fixups for R-Car H3 revisions after ES1.x | ||||
| 	 * Fixups for R-Car H3 revisions | ||||
| 	 */ | ||||
| 
 | ||||
| static const struct soc_device_attribute r8a7795es1[] __initconst = { | ||||
| 	{ .soc_id = "r8a7795", .revision = "ES1.*" }, | ||||
| #define HAS_A2VC0	BIT(0)		/* Power domain A2VC0 is present */ | ||||
| #define NO_EXTMASK	BIT(1)		/* Missing SYSCEXTMASK register */ | ||||
| 
 | ||||
| static const struct soc_device_attribute r8a7795_quirks_match[] __initconst = { | ||||
| 	{ | ||||
| 		.soc_id = "r8a7795", .revision = "ES1.*", | ||||
| 		.data = (void *)(HAS_A2VC0 | NO_EXTMASK), | ||||
| 	}, { | ||||
| 		.soc_id = "r8a7795", .revision = "ES2.*", | ||||
| 		.data = (void *)(NO_EXTMASK), | ||||
| 	}, | ||||
| 	{ /* sentinel */ } | ||||
| }; | ||||
| 
 | ||||
| static int __init r8a7795_sysc_init(void) | ||||
| { | ||||
| 	if (!soc_device_match(r8a7795es1)) | ||||
| 	const struct soc_device_attribute *attr; | ||||
| 	u32 quirks = 0; | ||||
| 
 | ||||
| 	attr = soc_device_match(r8a7795_quirks_match); | ||||
| 	if (attr) | ||||
| 		quirks = (uintptr_t)attr->data; | ||||
| 
 | ||||
| 	if (!(quirks & HAS_A2VC0)) | ||||
| 		rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas), | ||||
| 				  R8A7795_PD_A2VC0); | ||||
| 
 | ||||
| 	if (quirks & NO_EXTMASK) | ||||
| 		r8a7795_sysc_info.extmask_val = 0; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| const struct rcar_sysc_info r8a7795_sysc_info __initconst = { | ||||
| struct rcar_sysc_info r8a7795_sysc_info __initdata = { | ||||
| 	.init = r8a7795_sysc_init, | ||||
| 	.areas = r8a7795_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a7795_areas), | ||||
| 	.extmask_offs = 0x2f8, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
|  | ||||
| @ -1,18 +1,19 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Renesas R-Car M3-W System Controller | ||||
|  * Renesas R-Car M3-W/W+ System Controller | ||||
|  * | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  * Copyright (C) 2018-2019 Renesas Electronics Corporation | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a7796-sysc.h> | ||||
| 
 | ||||
| #include "rcar-sysc.h" | ||||
| 
 | ||||
| static const struct rcar_sysc_area r8a7796_areas[] __initconst = { | ||||
| static struct rcar_sysc_area r8a7796_areas[] __initdata = { | ||||
| 	{ "always-on",	    0, 0, R8A7796_PD_ALWAYS_ON,	-1, PD_ALWAYS_ON }, | ||||
| 	{ "ca57-scu",	0x1c0, 0, R8A7796_PD_CA57_SCU,	R8A7796_PD_ALWAYS_ON, | ||||
| 	  PD_SCU }, | ||||
| @ -39,7 +40,28 @@ static const struct rcar_sysc_area r8a7796_areas[] __initconst = { | ||||
| 	{ "a3ir",	0x180, 0, R8A7796_PD_A3IR,	R8A7796_PD_ALWAYS_ON }, | ||||
| }; | ||||
| 
 | ||||
| const struct rcar_sysc_info r8a7796_sysc_info __initconst = { | ||||
| 
 | ||||
| #ifdef CONFIG_SYSC_R8A77960 | ||||
| const struct rcar_sysc_info r8a77960_sysc_info __initconst = { | ||||
| 	.areas = r8a7796_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a7796_areas), | ||||
| }; | ||||
| #endif /* CONFIG_SYSC_R8A77960 */ | ||||
| 
 | ||||
| #ifdef CONFIG_SYSC_R8A77961 | ||||
| static int __init r8a77961_sysc_init(void) | ||||
| { | ||||
| 	rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas), | ||||
| 			  R8A7796_PD_A2VC0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| const struct rcar_sysc_info r8a77961_sysc_info __initconst = { | ||||
| 	.init = r8a77961_sysc_init, | ||||
| 	.areas = r8a7796_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a7796_areas), | ||||
| 	.extmask_offs = 0x2f8, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
| #endif /* CONFIG_SYSC_R8A77961 */ | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
|  * Copyright (C) 2016 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a77965-sysc.h> | ||||
| @ -33,4 +33,6 @@ static const struct rcar_sysc_area r8a77965_areas[] __initconst = { | ||||
| const struct rcar_sysc_info r8a77965_sysc_info __initconst = { | ||||
| 	.areas = r8a77965_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a77965_areas), | ||||
| 	.extmask_offs = 0x2f8, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  * Copyright (C) 2017 Cogent Embedded Inc. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a77970-sysc.h> | ||||
| @ -32,4 +32,6 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = { | ||||
| const struct rcar_sysc_info r8a77970_sysc_info __initconst = { | ||||
| 	.areas = r8a77970_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a77970_areas), | ||||
| 	.extmask_offs = 0x1b0, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
|  | ||||
| @ -6,7 +6,7 @@ | ||||
|  * Copyright (C) 2018 Cogent Embedded, Inc. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a77980-sysc.h> | ||||
| @ -49,4 +49,6 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = { | ||||
| const struct rcar_sysc_info r8a77980_sysc_info __initconst = { | ||||
| 	.areas = r8a77980_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a77980_areas), | ||||
| 	.extmask_offs = 0x138, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  * Copyright (C) 2018 Renesas Electronics Corp. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sys_soc.h> | ||||
| 
 | ||||
| @ -50,4 +50,6 @@ const struct rcar_sysc_info r8a77990_sysc_info __initconst = { | ||||
| 	.init = r8a77990_sysc_init, | ||||
| 	.areas = r8a77990_areas, | ||||
| 	.num_areas = ARRAY_SIZE(r8a77990_areas), | ||||
| 	.extmask_offs = 0x2f8, | ||||
| 	.extmask_val = BIT(0), | ||||
| }; | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
|  * Copyright (C) 2017 Glider bvba | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #include <dt-bindings/power/r8a77995-sysc.h> | ||||
|  | ||||
| @ -45,6 +45,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { | ||||
| 	{ .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 }, | ||||
| 	/* RZ/G2 is handled like R-Car Gen3 */ | ||||
| 	{ .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, | ||||
| 	/* R-Car Gen1 */ | ||||
| 	{ .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, | ||||
| @ -58,6 +59,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { | ||||
| 	/* R-Car Gen3 */ | ||||
| 	{ .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, | ||||
| 	{ .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, | ||||
|  | ||||
| @ -63,6 +63,7 @@ struct rcar_sysc_ch { | ||||
| 
 | ||||
| static void __iomem *rcar_sysc_base; | ||||
| static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ | ||||
| static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; | ||||
| 
 | ||||
| static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) | ||||
| { | ||||
| @ -105,6 +106,14 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) | ||||
| 
 | ||||
| 	spin_lock_irqsave(&rcar_sysc_lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Mask external power requests for CPU or 3DG domains | ||||
| 	 */ | ||||
| 	if (rcar_sysc_extmask_val) { | ||||
| 		iowrite32(rcar_sysc_extmask_val, | ||||
| 			  rcar_sysc_base + rcar_sysc_extmask_offs); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The interrupt source needs to be enabled, but masked, to prevent the | ||||
| 	 * CPU from receiving it. | ||||
| @ -148,6 +157,9 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) | ||||
| 	iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); | ||||
| 
 | ||||
|  out: | ||||
| 	if (rcar_sysc_extmask_val) | ||||
| 		iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&rcar_sysc_lock, flags); | ||||
| 
 | ||||
| 	pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", | ||||
| @ -275,6 +287,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { | ||||
| #ifdef CONFIG_SYSC_R8A774A1 | ||||
| 	{ .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, | ||||
| #endif | ||||
| #ifdef CONFIG_SYSC_R8A774B1 | ||||
| 	{ .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info }, | ||||
| #endif | ||||
| #ifdef CONFIG_SYSC_R8A774C0 | ||||
| 	{ .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, | ||||
| #endif | ||||
| @ -298,8 +313,11 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { | ||||
| #ifdef CONFIG_SYSC_R8A7795 | ||||
| 	{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, | ||||
| #endif | ||||
| #ifdef CONFIG_SYSC_R8A7796 | ||||
| 	{ .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info }, | ||||
| #ifdef CONFIG_SYSC_R8A77960 | ||||
| 	{ .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, | ||||
| #endif | ||||
| #ifdef CONFIG_SYSC_R8A77961 | ||||
| 	{ .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info }, | ||||
| #endif | ||||
| #ifdef CONFIG_SYSC_R8A77965 | ||||
| 	{ .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, | ||||
| @ -360,6 +378,10 @@ static int __init rcar_sysc_pd_init(void) | ||||
| 
 | ||||
| 	rcar_sysc_base = base; | ||||
| 
 | ||||
| 	/* Optional External Request Mask Register */ | ||||
| 	rcar_sysc_extmask_offs = info->extmask_offs; | ||||
| 	rcar_sysc_extmask_val = info->extmask_val; | ||||
| 
 | ||||
| 	domains = kzalloc(sizeof(*domains), GFP_KERNEL); | ||||
| 	if (!domains) { | ||||
| 		error = -ENOMEM; | ||||
|  | ||||
| @ -44,20 +44,25 @@ struct rcar_sysc_info { | ||||
| 	int (*init)(void);	/* Optional */ | ||||
| 	const struct rcar_sysc_area *areas; | ||||
| 	unsigned int num_areas; | ||||
| 	/* Optional External Request Mask Register */ | ||||
| 	u32 extmask_offs;	/* SYSCEXTMASK register offset */ | ||||
| 	u32 extmask_val;	/* SYSCEXTMASK register mask value */ | ||||
| }; | ||||
| 
 | ||||
| extern const struct rcar_sysc_info r8a7743_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7745_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a77470_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a774a1_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a774b1_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a774c0_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7779_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7790_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7791_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7792_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7794_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7795_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a7796_sysc_info; | ||||
| extern struct rcar_sysc_info r8a7795_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a77960_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a77961_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a77965_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a77970_sysc_info; | ||||
| extern const struct rcar_sysc_info r8a77980_sysc_info; | ||||
|  | ||||
| @ -116,6 +116,11 @@ static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = { | ||||
| 	.id	= 0x52, | ||||
| }; | ||||
| 
 | ||||
| static const struct renesas_soc soc_rz_g2n __initconst __maybe_unused = { | ||||
| 	.family = &fam_rzg2, | ||||
| 	.id     = 0x55, | ||||
| }; | ||||
| 
 | ||||
| static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { | ||||
| 	.family	= &fam_rzg2, | ||||
| 	.id	= 0x57, | ||||
| @ -227,6 +232,9 @@ static const struct of_device_id renesas_socs[] __initconst = { | ||||
| #ifdef CONFIG_ARCH_R8A774A1 | ||||
| 	{ .compatible = "renesas,r8a774a1",	.data = &soc_rz_g2m }, | ||||
| #endif | ||||
| #ifdef CONFIG_ARCH_R8A774B1 | ||||
| 	{ .compatible = "renesas,r8a774b1",	.data = &soc_rz_g2n }, | ||||
| #endif | ||||
| #ifdef CONFIG_ARCH_R8A774C0 | ||||
| 	{ .compatible = "renesas,r8a774c0",	.data = &soc_rz_g2e }, | ||||
| #endif | ||||
| @ -254,9 +262,12 @@ static const struct of_device_id renesas_socs[] __initconst = { | ||||
| #ifdef CONFIG_ARCH_R8A7795 | ||||
| 	{ .compatible = "renesas,r8a7795",	.data = &soc_rcar_h3 }, | ||||
| #endif | ||||
| #ifdef CONFIG_ARCH_R8A7796 | ||||
| #ifdef CONFIG_ARCH_R8A77960 | ||||
| 	{ .compatible = "renesas,r8a7796",	.data = &soc_rcar_m3_w }, | ||||
| #endif | ||||
| #ifdef CONFIG_ARCH_R8A77961 | ||||
| 	{ .compatible = "renesas,r8a77961",	.data = &soc_rcar_m3_w }, | ||||
| #endif | ||||
| #ifdef CONFIG_ARCH_R8A77965 | ||||
| 	{ .compatible = "renesas,r8a77965",	.data = &soc_rcar_m3_n }, | ||||
| #endif | ||||
| @ -326,7 +337,7 @@ static int __init renesas_soc_init(void) | ||||
| 	if (np) { | ||||
| 		chipid = of_iomap(np, 0); | ||||
| 		of_node_put(np); | ||||
| 	} else if (soc->id) { | ||||
| 	} else if (soc->id && family->reg) { | ||||
| 		chipid = ioremap(family->reg, 4); | ||||
| 	} | ||||
| 	if (chipid) { | ||||
|  | ||||
| @ -7,6 +7,16 @@ menuconfig SOC_SAMSUNG | ||||
| 
 | ||||
| if SOC_SAMSUNG | ||||
| 
 | ||||
| config EXYNOS_ASV | ||||
| 	bool "Exynos Adaptive Supply Voltage support" if COMPILE_TEST | ||||
| 	depends on (ARCH_EXYNOS && EXYNOS_CHIPID) || COMPILE_TEST | ||||
| 	select EXYNOS_ASV_ARM if ARM && ARCH_EXYNOS | ||||
| 
 | ||||
| # There is no need to enable these drivers for ARMv8 | ||||
| config EXYNOS_ASV_ARM | ||||
| 	bool "Exynos ASV ARMv7-specific driver extensions" if COMPILE_TEST | ||||
| 	depends on EXYNOS_ASV | ||||
| 
 | ||||
| config EXYNOS_CHIPID | ||||
| 	bool "Exynos Chipid controller driver" if COMPILE_TEST | ||||
| 	depends on ARCH_EXYNOS || COMPILE_TEST | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user