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 ...
Unified · Split
| @@ -103,7 +103,7 @@ the Microchip website: http://www.microchip.com. · 102 unmodified lines | |||
| 103 | 103 | ||
| 104 | * Datasheet | 104 | * Datasheet |
| 105 | 105 | ||
| 106 | http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11121-32-bit-Cortex-A5-Microcontroller-SAMA5D3_Datasheet.pdf | 106 | http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11121-32-bit-Cortex-A5-Microcontroller-SAMA5D3_Datasheet_B.pdf |
| 107 | 107 | ||
| 108 | * ARM Cortex-A5 + NEON based SoCs | 108 | * ARM Cortex-A5 + NEON based SoCs |
| 109 | - sama5d4 family | 109 | - sama5d4 family |
| @@ -167,7 +167,7 @@ the Microchip website: http://www.microchip.com. · 57 unmodified lines | |||
| 167 | 167 | ||
| 168 | * Datasheet | 168 | * Datasheet |
| 169 | 169 | ||
| 170 | http://ww1.microchip.com/downloads/en/DeviceDoc/60001527A.pdf | 170 | http://ww1.microchip.com/downloads/en/DeviceDoc/SAM-E70-S70-V70-V71-Family-Data-Sheet-DS60001527D.pdf |
| 171 | 171 | ||
| 172 | 172 | ||
| 173 | Linux kernel information | 173 | Linux kernel information |
| @@ -1,41 +0,0 @@ | |||
| 1 | == Introduction== | ||
| 2 | |||
| 3 | LLCC (Last Level Cache Controller) provides last level of cache memory in SOC, | ||
| 4 | that can be shared by multiple clients. Clients here are different cores in the | ||
| 5 | SOC, the idea is to minimize the local caches at the clients and migrate to | ||
| 6 | common pool of memory. Cache memory is divided into partitions called slices | ||
| 7 | which are assigned to clients. Clients can query the slice details, activate | ||
| 8 | and deactivate them. | ||
| 9 | |||
| 10 | Properties: | ||
| 11 | - compatible: | ||
| 12 | Usage: required | ||
| 13 | Value type: <string> | ||
| 14 | Definition: must be "qcom,sdm845-llcc" | ||
| 15 | |||
| 16 | - reg: | ||
| 17 | Usage: required | ||
| 18 | Value Type: <prop-encoded-array> | ||
| 19 | Definition: The first element specifies the llcc base start address and | ||
| 20 | the size of the register region. The second element specifies | ||
| 21 | the llcc broadcast base address and size of the register region. | ||
| 22 | |||
| 23 | - reg-names: | ||
| 24 | Usage: required | ||
| 25 | Value Type: <stringlist> | ||
| 26 | Definition: Register region names. Must be "llcc_base", "llcc_broadcast_base". | ||
| 27 | |||
| 28 | - interrupts: | ||
| 29 | Usage: required | ||
| 30 | Definition: The interrupt is associated with the llcc edac device. | ||
| 31 | It's used for llcc cache single and double bit error detection | ||
| 32 | and reporting. | ||
| 33 | |||
| 34 | Example: | ||
| 35 | |||
| 36 | cache-controller@1100000 { | ||
| 37 | compatible = "qcom,sdm845-llcc"; | ||
| 38 | reg = <0x1100000 0x200000>, <0x1300000 0x50000> ; | ||
| 39 | reg-names = "llcc_base", "llcc_broadcast_base"; | ||
| 40 | interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>; | ||
| 41 | }; | ||
| @@ -0,0 +1,55 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/arm/msm/qcom,llcc.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Last Level Cache Controller | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Rishabh Bhatnagar <rishabhb@codeaurora.org> | ||
| 11 | - Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org> | ||
| 12 | |||
| 13 | description: | | ||
| 14 | LLCC (Last Level Cache Controller) provides last level of cache memory in SoC, | ||
| 15 | that can be shared by multiple clients. Clients here are different cores in the | ||
| 16 | SoC, the idea is to minimize the local caches at the clients and migrate to | ||
| 17 | common pool of memory. Cache memory is divided into partitions called slices | ||
| 18 | which are assigned to clients. Clients can query the slice details, activate | ||
| 19 | and deactivate them. | ||
| 20 | |||
| 21 | properties: | ||
| 22 | compatible: | ||
| 23 | enum: | ||
| 24 | - qcom,sc7180-llcc | ||
| 25 | - qcom,sdm845-llcc | ||
| 26 | |||
| 27 | reg: | ||
| 28 | items: | ||
| 29 | - description: LLCC base register region | ||
| 30 | - description: LLCC broadcast base register region | ||
| 31 | |||
| 32 | reg-names: | ||
| 33 | items: | ||
| 34 | - const: llcc_base | ||
| 35 | - const: llcc_broadcast_base | ||
| 36 | |||
| 37 | interrupts: | ||
| 38 | maxItems: 1 | ||
| 39 | |||
| 40 | required: | ||
| 41 | - compatible | ||
| 42 | - reg | ||
| 43 | - reg-names | ||
| 44 | - interrupts | ||
| 45 | |||
| 46 | examples: | ||
| 47 | - | | ||
| 48 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 49 | |||
| 50 | cache-controller@1100000 { | ||
| 51 | compatible = "qcom,sdm845-llcc"; | ||
| 52 | reg = <0x1100000 0x200000>, <0x1300000 0x50000> ; | ||
| 53 | reg-names = "llcc_base", "llcc_broadcast_base"; | ||
| 54 | interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>; | ||
| 55 | }; | ||
| @@ -0,0 +1,29 @@ | |||
| 1 | OMAP PRM instance bindings | ||
| 2 | |||
| 3 | Power and Reset Manager is an IP block on OMAP family of devices which | ||
| 4 | handle the power domains and their current state, and provide reset | ||
| 5 | handling for the domains and/or separate IP blocks under the power domain | ||
| 6 | hierarchy. | ||
| 7 | |||
| 8 | Required properties: | ||
| 9 | - compatible: Must contain one of the following: | ||
| 10 | "ti,am3-prm-inst" | ||
| 11 | "ti,am4-prm-inst" | ||
| 12 | "ti,omap4-prm-inst" | ||
| 13 | "ti,omap5-prm-inst" | ||
| 14 | "ti,dra7-prm-inst" | ||
| 15 | and additionally must contain: | ||
| 16 | "ti,omap-prm-inst" | ||
| 17 | - reg: Contains PRM instance register address range | ||
| 18 | (base address and length) | ||
| 19 | |||
| 20 | Optional properties: | ||
| 21 | - #reset-cells: Should be 1 if the PRM instance in question supports resets. | ||
| 22 | |||
| 23 | Example: | ||
| 24 | |||
| 25 | prm_dsp2: prm@1b00 { | ||
| 26 | compatible = "ti,dra7-prm-inst", "ti,omap-prm-inst"; | ||
| 27 | reg = <0x1b00 0x40>; | ||
| 28 | #reset-cells = <1>; | ||
| 29 | }; | ||
| @@ -11,7 +11,9 @@ power management service, FPGA service and other platform management · 10 unmodified lines | |||
| 11 | services. | 11 | services. |
| 12 | 12 | ||
| 13 | Required properties: | 13 | Required properties: |
| 14 | - compatible: Must contain: "xlnx,zynqmp-firmware" | 14 | - compatible: Must contain any of below: |
| 15 | "xlnx,zynqmp-firmware" for Zynq Ultrascale+ MPSoC | ||
| 16 | "xlnx,versal-firmware" for Versal | ||
| 15 | - method: The method of calling the PM-API firmware layer. | 17 | - method: The method of calling the PM-API firmware layer. |
| 16 | Permitted values are: | 18 | Permitted values are: |
| 17 | - "smc" : SMC #0, following the SMCCC | 19 | - "smc" : SMC #0, following the SMCCC |
| @@ -21,6 +23,8 @@ Required properties: · 3 unmodified lines | |||
| 21 | Example | 23 | Example |
| 22 | ------- | 24 | ------- |
| 23 | 25 | ||
| 26 | Zynq Ultrascale+ MPSoC | ||
| 27 | ---------------------- | ||
| 24 | firmware { | 28 | firmware { |
| 25 | zynqmp_firmware: zynqmp-firmware { | 29 | zynqmp_firmware: zynqmp-firmware { |
| 26 | compatible = "xlnx,zynqmp-firmware"; | 30 | compatible = "xlnx,zynqmp-firmware"; |
| @@ -28,3 +32,13 @@ firmware { · 1 unmodified line | |||
| 28 | ... | 32 | ... |
| 29 | }; | 33 | }; |
| 30 | }; | 34 | }; |
| 35 | |||
| 36 | Versal | ||
| 37 | ------ | ||
| 38 | firmware { | ||
| 39 | versal_firmware: versal-firmware { | ||
| 40 | compatible = "xlnx,versal-firmware"; | ||
| 41 | method = "smc"; | ||
| 42 | ... | ||
| 43 | }; | ||
| 44 | }; | ||
| @@ -4,6 +4,7 @@ Required properties: · 3 unmodified lines | |||
| 4 | - compatible: should be "amlogic,meson-gxbb-efuse" | 4 | - compatible: should be "amlogic,meson-gxbb-efuse" |
| 5 | - clocks: phandle to the efuse peripheral clock provided by the | 5 | - clocks: phandle to the efuse peripheral clock provided by the |
| 6 | clock controller. | 6 | clock controller. |
| 7 | - secure-monitor: phandle to the secure-monitor node | ||
| 7 | 8 | ||
| 8 | = Data cells = | 9 | = Data cells = |
| 9 | Are child nodes of eFuse, bindings of which as described in | 10 | Are child nodes of eFuse, bindings of which as described in |
| @@ -16,6 +17,7 @@ Example: · 6 unmodified lines | |||
| 16 | clocks = <&clkc CLKID_EFUSE>; | 17 | clocks = <&clkc CLKID_EFUSE>; |
| 17 | #address-cells = <1>; | 18 | #address-cells = <1>; |
| 18 | #size-cells = <1>; | 19 | #size-cells = <1>; |
| 20 | secure-monitor = <&sm>; | ||
| 19 | 21 | ||
| 20 | sn: sn@14 { | 22 | sn: sn@14 { |
| 21 | reg = <0x14 0x10>; | 23 | reg = <0x14 0x10>; |
| @@ -30,6 +32,10 @@ Example: · 8 unmodified lines | |||
| 30 | }; | 32 | }; |
| 31 | }; | 33 | }; |
| 32 | 34 | ||
| 35 | sm: secure-monitor { | ||
| 36 | compatible = "amlogic,meson-gxbb-sm"; | ||
| 37 | }; | ||
| 38 | |||
| 33 | = Data consumers = | 39 | = Data consumers = |
| 34 | Are device nodes which consume nvmem data cells. | 40 | Are device nodes which consume nvmem data cells. |
| 35 | 41 | ||
| @@ -5,6 +5,7 @@ which then translates it into a corresponding voltage on a rail · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | Required Properties: | 6 | Required Properties: |
| 7 | - compatible: Should be one of the following | 7 | - compatible: Should be one of the following |
| 8 | * qcom,msm8976-rpmpd: RPM Power domain for the msm8976 family of SoC | ||
| 8 | * qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC | 9 | * qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC |
| 9 | * qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC | 10 | * qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC |
| 10 | * qcom,qcs404-rpmpd: RPM Power domain for the qcs404 family of SoC | 11 | * 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 · 3 unmodified lines | |||
| 4 | disables the access of Audio FIFOs to DDR on AXG based SoC. | 4 | disables the access of Audio FIFOs to DDR on AXG based SoC. |
| 5 | 5 | ||
| 6 | Required properties: | 6 | Required properties: |
| 7 | - compatible: 'amlogic,meson-axg-audio-arb' | 7 | - compatible: 'amlogic,meson-axg-audio-arb' or |
| 8 | 'amlogic,meson-sm1-audio-arb' | ||
| 8 | - reg: physical base address of the controller and length of memory | 9 | - reg: physical base address of the controller and length of memory |
| 9 | mapped region. | 10 | mapped region. |
| 10 | - clocks: phandle to the fifo peripheral clock provided by the audio | 11 | - clocks: phandle to the fifo peripheral clock provided by the audio |
| @@ -16,6 +16,7 @@ properties: · 15 unmodified lines | |||
| 16 | - amlogic,meson8b-reset # Reset Controller on Meson8b and compatible SoCs | 16 | - amlogic,meson8b-reset # Reset Controller on Meson8b and compatible SoCs |
| 17 | - amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs | 17 | - amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs |
| 18 | - amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs | 18 | - amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs |
| 19 | - amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs | ||
| 19 | 20 | ||
| 20 | reg: | 21 | reg: |
| 21 | maxItems: 1 | 22 | maxItems: 1 |
| @@ -1,52 +0,0 @@ | |||
| 1 | Qualcomm AOSS Reset Controller | ||
| 2 | ====================================== | ||
| 3 | |||
| 4 | This binding describes a reset-controller found on AOSS-CC (always on subsystem) | ||
| 5 | for Qualcomm SDM845 SoCs. | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | - compatible: | ||
| 9 | Usage: required | ||
| 10 | Value type: <string> | ||
| 11 | Definition: must be: | ||
| 12 | "qcom,sdm845-aoss-cc" | ||
| 13 | |||
| 14 | - reg: | ||
| 15 | Usage: required | ||
| 16 | Value type: <prop-encoded-array> | ||
| 17 | Definition: must specify the base address and size of the register | ||
| 18 | space. | ||
| 19 | |||
| 20 | - #reset-cells: | ||
| 21 | Usage: required | ||
| 22 | Value type: <uint> | ||
| 23 | Definition: must be 1; cell entry represents the reset index. | ||
| 24 | |||
| 25 | Example: | ||
| 26 | |||
| 27 | aoss_reset: reset-controller@c2a0000 { | ||
| 28 | compatible = "qcom,sdm845-aoss-cc"; | ||
| 29 | reg = <0xc2a0000 0x31000>; | ||
| 30 | #reset-cells = <1>; | ||
| 31 | }; | ||
| 32 | |||
| 33 | Specifying reset lines connected to IP modules | ||
| 34 | ============================================== | ||
| 35 | |||
| 36 | Device nodes that need access to reset lines should | ||
| 37 | specify them as a reset phandle in their corresponding node as | ||
| 38 | specified in reset.txt. | ||
| 39 | |||
| 40 | For list of all valid reset indicies see | ||
| 41 | <dt-bindings/reset/qcom,sdm845-aoss.h> | ||
| 42 | |||
| 43 | Example: | ||
| 44 | |||
| 45 | modem-pil@4080000 { | ||
| 46 | ... | ||
| 47 | |||
| 48 | resets = <&aoss_reset AOSS_CC_MSS_RESTART>; | ||
| 49 | reset-names = "mss_restart"; | ||
| 50 | |||
| 51 | ... | ||
| 52 | }; | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/reset/qcom,aoss-reset.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Qualcomm AOSS Reset Controller | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Sibi Sankar <sibis@codeaurora.org> | ||
| 11 | |||
| 12 | description: | ||
| 13 | The bindings describe the reset-controller found on AOSS-CC (always on | ||
| 14 | subsystem) for Qualcomm Technologies Inc SoCs. | ||
| 15 | |||
| 16 | properties: | ||
| 17 | compatible: | ||
| 18 | oneOf: | ||
| 19 | - description: on SC7180 SoCs the following compatibles must be specified | ||
| 20 | items: | ||
| 21 | - const: "qcom,sc7180-aoss-cc" | ||
| 22 | - const: "qcom,sdm845-aoss-cc" | ||
| 23 | |||
| 24 | - description: on SDM845 SoCs the following compatibles must be specified | ||
| 25 | items: | ||
| 26 | - const: "qcom,sdm845-aoss-cc" | ||
| 27 | |||
| 28 | reg: | ||
| 29 | maxItems: 1 | ||
| 30 | |||
| 31 | '#reset-cells': | ||
| 32 | const: 1 | ||
| 33 | |||
| 34 | required: | ||
| 35 | - compatible | ||
| 36 | - reg | ||
| 37 | - '#reset-cells' | ||
| 38 | |||
| 39 | additionalProperties: false | ||
| 40 | |||
| 41 | examples: | ||
| 42 | - | | ||
| 43 | aoss_reset: reset-controller@c2a0000 { | ||
| 44 | compatible = "qcom,sdm845-aoss-cc"; | ||
| 45 | reg = <0xc2a0000 0x31000>; | ||
| 46 | #reset-cells = <1>; | ||
| 47 | }; | ||
| @@ -1,52 +0,0 @@ | |||
| 1 | PDC Global | ||
| 2 | ====================================== | ||
| 3 | |||
| 4 | This binding describes a reset-controller found on PDC-Global (Power Domain | ||
| 5 | Controller) block for Qualcomm Technologies Inc SDM845 SoCs. | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | - compatible: | ||
| 9 | Usage: required | ||
| 10 | Value type: <string> | ||
| 11 | Definition: must be: | ||
| 12 | "qcom,sdm845-pdc-global" | ||
| 13 | |||
| 14 | - reg: | ||
| 15 | Usage: required | ||
| 16 | Value type: <prop-encoded-array> | ||
| 17 | Definition: must specify the base address and size of the register | ||
| 18 | space. | ||
| 19 | |||
| 20 | - #reset-cells: | ||
| 21 | Usage: required | ||
| 22 | Value type: <uint> | ||
| 23 | Definition: must be 1; cell entry represents the reset index. | ||
| 24 | |||
| 25 | Example: | ||
| 26 | |||
| 27 | pdc_reset: reset-controller@b2e0000 { | ||
| 28 | compatible = "qcom,sdm845-pdc-global"; | ||
| 29 | reg = <0xb2e0000 0x20000>; | ||
| 30 | #reset-cells = <1>; | ||
| 31 | }; | ||
| 32 | |||
| 33 | PDC reset clients | ||
| 34 | ====================================== | ||
| 35 | |||
| 36 | Device nodes that need access to reset lines should | ||
| 37 | specify them as a reset phandle in their corresponding node as | ||
| 38 | specified in reset.txt. | ||
| 39 | |||
| 40 | For a list of all valid reset indices see | ||
| 41 | <dt-bindings/reset/qcom,sdm845-pdc.h> | ||
| 42 | |||
| 43 | Example: | ||
| 44 | |||
| 45 | modem-pil@4080000 { | ||
| 46 | ... | ||
| 47 | |||
| 48 | resets = <&pdc_reset PDC_MODEM_SYNC_RESET>; | ||
| 49 | reset-names = "pdc_reset"; | ||
| 50 | |||
| 51 | ... | ||
| 52 | }; | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/reset/qcom,pdc-global.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Qualcomm PDC Global | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Sibi Sankar <sibis@codeaurora.org> | ||
| 11 | |||
| 12 | description: | ||
| 13 | The bindings describes the reset-controller found on PDC-Global (Power Domain | ||
| 14 | Controller) block for Qualcomm Technologies Inc SoCs. | ||
| 15 | |||
| 16 | properties: | ||
| 17 | compatible: | ||
| 18 | oneOf: | ||
| 19 | - description: on SC7180 SoCs the following compatibles must be specified | ||
| 20 | items: | ||
| 21 | - const: "qcom,sc7180-pdc-global" | ||
| 22 | - const: "qcom,sdm845-pdc-global" | ||
| 23 | |||
| 24 | - description: on SDM845 SoCs the following compatibles must be specified | ||
| 25 | items: | ||
| 26 | - const: "qcom,sdm845-pdc-global" | ||
| 27 | |||
| 28 | reg: | ||
| 29 | maxItems: 1 | ||
| 30 | |||
| 31 | '#reset-cells': | ||
| 32 | const: 1 | ||
| 33 | |||
| 34 | required: | ||
| 35 | - compatible | ||
| 36 | - reg | ||
| 37 | - '#reset-cells' | ||
| 38 | |||
| 39 | additionalProperties: false | ||
| 40 | |||
| 41 | examples: | ||
| 42 | - | | ||
| 43 | pdc_reset: reset-controller@b2e0000 { | ||
| 44 | compatible = "qcom,sdm845-pdc-global"; | ||
| 45 | reg = <0xb2e0000 0x20000>; | ||
| 46 | #reset-cells = <1>; | ||
| 47 | }; | ||
| @@ -130,6 +130,7 @@ this layer. These clocks and resets should be described in each property. · 129 unmodified lines | |||
| 130 | Required properties: | 130 | Required properties: |
| 131 | - compatible: Should be | 131 | - compatible: Should be |
| 132 | "socionext,uniphier-pro4-usb3-reset" - for Pro4 SoC USB3 | 132 | "socionext,uniphier-pro4-usb3-reset" - for Pro4 SoC USB3 |
| 133 | "socionext,uniphier-pro5-usb3-reset" - for Pro5 SoC USB3 | ||
| 133 | "socionext,uniphier-pxs2-usb3-reset" - for PXs2 SoC USB3 | 134 | "socionext,uniphier-pxs2-usb3-reset" - for PXs2 SoC USB3 |
| 134 | "socionext,uniphier-ld20-usb3-reset" - for LD20 SoC USB3 | 135 | "socionext,uniphier-ld20-usb3-reset" - for LD20 SoC USB3 |
| 135 | "socionext,uniphier-pxs3-usb3-reset" - for PXs3 SoC USB3 | 136 | "socionext,uniphier-pxs3-usb3-reset" - for PXs3 SoC USB3 |
| @@ -141,12 +142,12 @@ Required properties: · 5 unmodified lines | |||
| 141 | - clocks: A list of phandles to the clock gate for the glue layer. | 142 | - clocks: A list of phandles to the clock gate for the glue layer. |
| 142 | According to the clock-names, appropriate clocks are required. | 143 | According to the clock-names, appropriate clocks are required. |
| 143 | - clock-names: Should contain | 144 | - clock-names: Should contain |
| 144 | "gio", "link" - for Pro4 SoC | 145 | "gio", "link" - for Pro4 and Pro5 SoCs |
| 145 | "link" - for others | 146 | "link" - for others |
| 146 | - resets: A list of phandles to the reset control for the glue layer. | 147 | - resets: A list of phandles to the reset control for the glue layer. |
| 147 | According to the reset-names, appropriate resets are required. | 148 | According to the reset-names, appropriate resets are required. |
| 148 | - reset-names: Should contain | 149 | - reset-names: Should contain |
| 149 | "gio", "link" - for Pro4 SoC | 150 | "gio", "link" - for Pro4 and Pro5 SoCs |
| 150 | "link" - for others | 151 | "link" - for others |
| 151 | 152 | ||
| 152 | Example: | 153 | Example: |
| @@ -5,7 +5,7 @@ and power management. · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | Required properites: | 6 | Required properites: |
| 7 | - reg : Offset and length of the register set of the RCPM block. | 7 | - reg : Offset and length of the register set of the RCPM block. |
| 8 | - fsl,#rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the | 8 | - #fsl,rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the |
| 9 | fsl,rcpm-wakeup property. | 9 | fsl,rcpm-wakeup property. |
| 10 | - compatible : Must contain a chip-specific RCPM block compatible string | 10 | - compatible : Must contain a chip-specific RCPM block compatible string |
| 11 | and (if applicable) may contain a chassis-version RCPM compatible | 11 | and (if applicable) may contain a chassis-version RCPM compatible |
| @@ -20,6 +20,7 @@ Required properites: · 8 unmodified lines | |||
| 20 | * "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm | 20 | * "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm |
| 21 | * "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm | 21 | * "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm |
| 22 | * "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm | 22 | * "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm |
| 23 | * "fsl,qoriq-rcpm-2.1+": for chassis 2.1+ rcpm | ||
| 23 | 24 | ||
| 24 | All references to "1.0" and "2.0" refer to the QorIQ chassis version to | 25 | All references to "1.0" and "2.0" refer to the QorIQ chassis version to |
| 25 | which the chip complies. | 26 | which the chip complies. |
| @@ -27,14 +28,19 @@ Chassis Version Example Chips · 1 unmodified line | |||
| 27 | --------------- ------------------------------- | 28 | --------------- ------------------------------- |
| 28 | 1.0 p4080, p5020, p5040, p2041, p3041 | 29 | 1.0 p4080, p5020, p5040, p2041, p3041 |
| 29 | 2.0 t4240, b4860, b4420 | 30 | 2.0 t4240, b4860, b4420 |
| 30 | 2.1 t1040, ls1021 | 31 | 2.1 t1040, |
| 32 | 2.1+ ls1021a, ls1012a, ls1043a, ls1046a | ||
| 33 | |||
| 34 | Optional properties: | ||
| 35 | - little-endian : RCPM register block is Little Endian. Without it RCPM | ||
| 36 | will be Big Endian (default case). | ||
| 31 | 37 | ||
| 32 | Example: | 38 | Example: |
| 33 | The RCPM node for T4240: | 39 | The RCPM node for T4240: |
| 34 | rcpm: global-utilities@e2000 { | 40 | rcpm: global-utilities@e2000 { |
| 35 | compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0"; | 41 | compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0"; |
| 36 | reg = <0xe2000 0x1000>; | 42 | reg = <0xe2000 0x1000>; |
| 37 | fsl,#rcpm-wakeup-cells = <2>; | 43 | #fsl,rcpm-wakeup-cells = <2>; |
| 38 | }; | 44 | }; |
| 39 | 45 | ||
| 40 | * Freescale RCPM Wakeup Source Device Tree Bindings | 46 | * Freescale RCPM Wakeup Source Device Tree Bindings |
| @@ -44,7 +50,7 @@ can be used as a wakeup source. · 3 unmodified lines | |||
| 44 | 50 | ||
| 45 | - fsl,rcpm-wakeup: Consists of a phandle to the rcpm node and the IPPDEXPCR | 51 | - fsl,rcpm-wakeup: Consists of a phandle to the rcpm node and the IPPDEXPCR |
| 46 | register cells. The number of IPPDEXPCR register cells is defined in | 52 | register cells. The number of IPPDEXPCR register cells is defined in |
| 47 | "fsl,#rcpm-wakeup-cells" in the rcpm node. The first register cell is | 53 | "#fsl,rcpm-wakeup-cells" in the rcpm node. The first register cell is |
| 48 | the bit mask that should be set in IPPDEXPCR0, and the second register | 54 | the bit mask that should be set in IPPDEXPCR0, and the second register |
| 49 | cell is for IPPDEXPCR1, and so on. | 55 | cell is for IPPDEXPCR1, and so on. |
| 50 | 56 | ||
| @@ -22,6 +22,7 @@ resources. · 21 unmodified lines | |||
| 22 | "qcom,rpm-apq8084" | 22 | "qcom,rpm-apq8084" |
| 23 | "qcom,rpm-msm8916" | 23 | "qcom,rpm-msm8916" |
| 24 | "qcom,rpm-msm8974" | 24 | "qcom,rpm-msm8974" |
| 25 | "qcom,rpm-msm8976" | ||
| 25 | "qcom,rpm-msm8998" | 26 | "qcom,rpm-msm8998" |
| 26 | "qcom,rpm-sdm660" | 27 | "qcom,rpm-sdm660" |
| 27 | "qcom,rpm-qcs404" | 28 | "qcom,rpm-qcs404" |
| @@ -2140,6 +2140,7 @@ S: Maintained · 2139 unmodified lines | |||
| 2140 | 2140 | ||
| 2141 | ARM/QUALCOMM SUPPORT | 2141 | ARM/QUALCOMM SUPPORT |
| 2142 | M: Andy Gross <agross@kernel.org> | 2142 | M: Andy Gross <agross@kernel.org> |
| 2143 | M: Bjorn Andersson <bjorn.andersson@linaro.org> | ||
| 2143 | L: linux-arm-msm@vger.kernel.org | 2144 | L: linux-arm-msm@vger.kernel.org |
| 2144 | S: Maintained | 2145 | S: Maintained |
| 2145 | F: Documentation/devicetree/bindings/soc/qcom/ | 2146 | F: Documentation/devicetree/bindings/soc/qcom/ |
| @@ -5001,6 +5002,14 @@ F: include/linux/dma-direct.h · 2855 unmodified lines | |||
| 5001 | F: include/linux/dma-mapping.h | 5002 | F: include/linux/dma-mapping.h |
| 5002 | F: include/linux/dma-noncoherent.h | 5003 | F: include/linux/dma-noncoherent.h |
| 5003 | 5004 | ||
| 5005 | DMC FREQUENCY DRIVER FOR SAMSUNG EXYNOS5422 | ||
| 5006 | M: Lukasz Luba <l.luba@partner.samsung.com> | ||
| 5007 | L: linux-pm@vger.kernel.org | ||
| 5008 | L: linux-samsung-soc@vger.kernel.org | ||
| 5009 | S: Maintained | ||
| 5010 | F: drivers/memory/samsung/exynos5422-dmc.c | ||
| 5011 | F: Documentation/devicetree/bindings/memory-controllers/exynos5422-dmc.txt | ||
| 5012 | |||
| 5004 | DME1737 HARDWARE MONITOR DRIVER | 5013 | DME1737 HARDWARE MONITOR DRIVER |
| 5005 | M: Juerg Haefliger <juergh@gmail.com> | 5014 | M: Juerg Haefliger <juergh@gmail.com> |
| 5006 | L: linux-hwmon@vger.kernel.org | 5015 | L: linux-hwmon@vger.kernel.org |
| @@ -11066,6 +11075,13 @@ F: arch/arm/boot/dts/mmp* · 6059 unmodified lines | |||
| 11066 | F: arch/arm/mach-mmp/ | 11075 | F: arch/arm/mach-mmp/ |
| 11067 | F: linux/soc/mmp/ | 11076 | F: linux/soc/mmp/ |
| 11068 | 11077 | ||
| 11078 | MMP USB PHY DRIVERS | ||
| 11079 | R: Lubomir Rintel <lkundrak@v3.sk> | ||
| 11080 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) | ||
| 11081 | S: Maintained | ||
| 11082 | F: drivers/phy/marvell/phy-mmp3-usb.c | ||
| 11083 | F: drivers/phy/marvell/phy-pxa-usb.c | ||
| 11084 | |||
| 11069 | MMU GATHER AND TLB INVALIDATION | 11085 | MMU GATHER AND TLB INVALIDATION |
| 11070 | M: Will Deacon <will@kernel.org> | 11086 | M: Will Deacon <will@kernel.org> |
| 11071 | M: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com> | 11087 | M: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com> |
| @@ -14033,6 +14049,7 @@ F: include/dt-bindings/reset/ · 2961 unmodified lines | |||
| 14033 | F: include/linux/reset.h | 14049 | F: include/linux/reset.h |
| 14034 | F: include/linux/reset/ | 14050 | F: include/linux/reset/ |
| 14035 | F: include/linux/reset-controller.h | 14051 | F: include/linux/reset-controller.h |
| 14052 | K: \b(?:devm_|of_)?reset_control(?:ler_[a-z]+|_[a-z_]+)?\b | ||
| 14036 | 14053 | ||
| 14037 | RESTARTABLE SEQUENCES SUPPORT | 14054 | RESTARTABLE SEQUENCES SUPPORT |
| 14038 | M: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | 14055 | M: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
| @@ -109,6 +109,7 @@ config ARCH_OMAP2PLUS · 108 unmodified lines | |||
| 109 | select TI_SYSC | 109 | select TI_SYSC |
| 110 | select OMAP_IRQCHIP | 110 | select OMAP_IRQCHIP |
| 111 | select CLKSRC_TI_32K | 111 | select CLKSRC_TI_32K |
| 112 | select ARCH_HAS_RESET_CONTROLLER | ||
| 112 | help | 113 | help |
| 113 | Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 | 114 | Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 |
| 114 | 115 | ||
| @@ -247,6 +247,60 @@ void wakeup_source_unregister(struct wakeup_source *ws) · 246 unmodified lines | |||
| 247 | } | 247 | } |
| 248 | EXPORT_SYMBOL_GPL(wakeup_source_unregister); | 248 | EXPORT_SYMBOL_GPL(wakeup_source_unregister); |
| 249 | 249 | ||
| 250 | /** | ||
| 251 | * wakeup_sources_read_lock - Lock wakeup source list for read. | ||
| 252 | * | ||
| 253 | * Returns an index of srcu lock for struct wakeup_srcu. | ||
| 254 | * This index must be passed to the matching wakeup_sources_read_unlock(). | ||
| 255 | */ | ||
| 256 | int wakeup_sources_read_lock(void) | ||
| 257 | { | ||
| 258 | return srcu_read_lock(&wakeup_srcu); | ||
| 259 | } | ||
| 260 | EXPORT_SYMBOL_GPL(wakeup_sources_read_lock); | ||
| 261 | |||
| 262 | /** | ||
| 263 | * wakeup_sources_read_unlock - Unlock wakeup source list. | ||
| 264 | * @idx: return value from corresponding wakeup_sources_read_lock() | ||
| 265 | */ | ||
| 266 | void wakeup_sources_read_unlock(int idx) | ||
| 267 | { | ||
| 268 | srcu_read_unlock(&wakeup_srcu, idx); | ||
| 269 | } | ||
| 270 | EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock); | ||
| 271 | |||
| 272 | /** | ||
| 273 | * wakeup_sources_walk_start - Begin a walk on wakeup source list | ||
| 274 | * | ||
| 275 | * Returns first object of the list of wakeup sources. | ||
| 276 | * | ||
| 277 | * Note that to be safe, wakeup sources list needs to be locked by calling | ||
| 278 | * wakeup_source_read_lock() for this. | ||
| 279 | */ | ||
| 280 | struct wakeup_source *wakeup_sources_walk_start(void) | ||
| 281 | { | ||
| 282 | struct list_head *ws_head = &wakeup_sources; | ||
| 283 | |||
| 284 | return list_entry_rcu(ws_head->next, struct wakeup_source, entry); | ||
| 285 | } | ||
| 286 | EXPORT_SYMBOL_GPL(wakeup_sources_walk_start); | ||
| 287 | |||
| 288 | /** | ||
| 289 | * wakeup_sources_walk_next - Get next wakeup source from the list | ||
| 290 | * @ws: Previous wakeup source object | ||
| 291 | * | ||
| 292 | * Note that to be safe, wakeup sources list needs to be locked by calling | ||
| 293 | * wakeup_source_read_lock() for this. | ||
| 294 | */ | ||
| 295 | struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws) | ||
| 296 | { | ||
| 297 | struct list_head *ws_head = &wakeup_sources; | ||
| 298 | |||
| 299 | return list_next_or_null_rcu(ws_head, &ws->entry, | ||
| 300 | struct wakeup_source, entry); | ||
| 301 | } | ||
| 302 | EXPORT_SYMBOL_GPL(wakeup_sources_walk_next); | ||
| 303 | |||
| 250 | /** | 304 | /** |
| 251 | * device_wakeup_attach - Attach a wakeup source object to a device object. | 305 | * device_wakeup_attach - Attach a wakeup source object to a device object. |
| 252 | * @dev: Device to handle. | 306 | * @dev: Device to handle. |
| @@ -41,8 +41,9 @@ config MOXTET · 40 unmodified lines | |||
| 41 | 41 | ||
| 42 | config HISILICON_LPC | 42 | config HISILICON_LPC |
| 43 | bool "Support for ISA I/O space on HiSilicon Hip06/7" | 43 | bool "Support for ISA I/O space on HiSilicon Hip06/7" |
| 44 | depends on ARM64 && (ARCH_HISI || COMPILE_TEST) | 44 | depends on (ARM64 && ARCH_HISI) || (COMPILE_TEST && !ALPHA && !HEXAGON && !PARISC && !C6X) |
| 45 | select INDIRECT_PIO | 45 | depends on HAS_IOMEM |
| 46 | select INDIRECT_PIO if ARM64 | ||
| 46 | help | 47 | help |
| 47 | Driver to enable I/O access to devices attached to the Low Pin | 48 | Driver to enable I/O access to devices attached to the Low Pin |
| 48 | Count bus on the HiSilicon Hip06/7 SoC. | 49 | Count bus on the HiSilicon Hip06/7 SoC. |
| @@ -74,7 +74,7 @@ struct hisi_lpc_dev { · 73 unmodified lines | |||
| 74 | /* About 10us. This is specific for single IO operations, such as inb */ | 74 | /* About 10us. This is specific for single IO operations, such as inb */ |
| 75 | #define LPC_PEROP_WAITCNT 100 | 75 | #define LPC_PEROP_WAITCNT 100 |
| 76 | 76 | ||
| 77 | static int wait_lpc_idle(unsigned char *mbase, unsigned int waitcnt) | 77 | static int wait_lpc_idle(void __iomem *mbase, unsigned int waitcnt) |
| 78 | { | 78 | { |
| 79 | u32 status; | 79 | u32 status; |
| 80 | 80 | ||
| @@ -209,7 +209,7 @@ static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth) · 128 unmodified lines | |||
| 209 | struct hisi_lpc_dev *lpcdev = hostdata; | 209 | struct hisi_lpc_dev *lpcdev = hostdata; |
| 210 | struct lpc_cycle_para iopara; | 210 | struct lpc_cycle_para iopara; |
| 211 | unsigned long addr; | 211 | unsigned long addr; |
| 212 | u32 rd_data = 0; | 212 | __le32 rd_data = 0; |
| 213 | int ret; | 213 | int ret; |
| 214 | 214 | ||
| 215 | if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) | 215 | if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) |
| @@ -244,13 +244,12 @@ static void hisi_lpc_comm_out(void *hostdata, unsigned long pio, · 28 unmodified lines | |||
| 244 | struct lpc_cycle_para iopara; | 244 | struct lpc_cycle_para iopara; |
| 245 | const unsigned char *buf; | 245 | const unsigned char *buf; |
| 246 | unsigned long addr; | 246 | unsigned long addr; |
| 247 | __le32 _val = cpu_to_le32(val); | ||
| 247 | 248 | ||
| 248 | if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) | 249 | if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) |
| 249 | return; | 250 | return; |
| 250 | 251 | ||
| 251 | val = cpu_to_le32(val); | 252 | buf = (const unsigned char *)&_val; |
| 252 | |||
| 253 | buf = (const unsigned char *)&val; | ||
| 254 | addr = hisi_lpc_pio_to_addr(lpcdev, pio); | 253 | addr = hisi_lpc_pio_to_addr(lpcdev, pio); |
| 255 | 254 | ||
| 256 | iopara.opflags = FG_INCRADDR_LPC; | 255 | iopara.opflags = FG_INCRADDR_LPC; |
| @@ -917,6 +917,9 @@ set_midle: · 916 unmodified lines | |||
| 917 | return -EINVAL; | 917 | return -EINVAL; |
| 918 | } | 918 | } |
| 919 | 919 | ||
| 920 | if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY) | ||
| 921 | best_mode = SYSC_IDLE_NO; | ||
| 922 | |||
| 920 | reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); | 923 | reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); |
| 921 | reg |= best_mode << regbits->midle_shift; | 924 | reg |= best_mode << regbits->midle_shift; |
| 922 | sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | 925 | sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); |
| @@ -978,6 +981,9 @@ static int sysc_disable_module(struct device *dev) · 55 unmodified lines | |||
| 978 | return ret; | 981 | return ret; |
| 979 | } | 982 | } |
| 980 | 983 | ||
| 984 | if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY) | ||
| 985 | best_mode = SYSC_IDLE_FORCE; | ||
| 986 | |||
| 981 | reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); | 987 | reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); |
| 982 | reg |= best_mode << regbits->midle_shift; | 988 | reg |= best_mode << regbits->midle_shift; |
| 983 | sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | 989 | sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); |
| @@ -1037,8 +1043,6 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev, · 53 unmodified lines | |||
| 1037 | struct ti_sysc_platform_data *pdata; | 1043 | struct ti_sysc_platform_data *pdata; |
| 1038 | int error; | 1044 | int error; |
| 1039 | 1045 | ||
| 1040 | reset_control_deassert(ddata->rsts); | ||
| 1041 | |||
| 1042 | pdata = dev_get_platdata(ddata->dev); | 1046 | pdata = dev_get_platdata(ddata->dev); |
| 1043 | if (!pdata) | 1047 | if (!pdata) |
| 1044 | return 0; | 1048 | return 0; |
| @@ -1051,6 +1055,8 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev, · 6 unmodified lines | |||
| 1051 | dev_err(dev, "%s: could not enable: %i\n", | 1055 | dev_err(dev, "%s: could not enable: %i\n", |
| 1052 | __func__, error); | 1056 | __func__, error); |
| 1053 | 1057 | ||
| 1058 | reset_control_deassert(ddata->rsts); | ||
| 1059 | |||
| 1054 | return 0; | 1060 | return 0; |
| 1055 | } | 1061 | } |
| 1056 | 1062 | ||
| @@ -1104,8 +1110,6 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) · 47 unmodified lines | |||
| 1104 | 1110 | ||
| 1105 | sysc_clkdm_deny_idle(ddata); | 1111 | sysc_clkdm_deny_idle(ddata); |
| 1106 | 1112 | ||
| 1107 | reset_control_deassert(ddata->rsts); | ||
| 1108 | |||
| 1109 | if (sysc_opt_clks_needed(ddata)) { | 1113 | if (sysc_opt_clks_needed(ddata)) { |
| 1110 | error = sysc_enable_opt_clocks(ddata); | 1114 | error = sysc_enable_opt_clocks(ddata); |
| 1111 | if (error) | 1115 | if (error) |
| @@ -1116,6 +1120,8 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) · 4 unmodified lines | |||
| 1116 | if (error) | 1120 | if (error) |
| 1117 | goto err_opt_clocks; | 1121 | goto err_opt_clocks; |
| 1118 | 1122 | ||
| 1123 | reset_control_deassert(ddata->rsts); | ||
| 1124 | |||
| 1119 | if (ddata->legacy_mode) { | 1125 | if (ddata->legacy_mode) { |
| 1120 | error = sysc_runtime_resume_legacy(dev, ddata); | 1126 | error = sysc_runtime_resume_legacy(dev, ddata); |
| 1121 | if (error) | 1127 | if (error) |
| @@ -1251,6 +1257,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { · 129 unmodified lines | |||
| 1251 | SYSC_QUIRK("gpu", 0x50000000, 0x14, -1, -1, 0x00010201, 0xffffffff, 0), | 1257 | SYSC_QUIRK("gpu", 0x50000000, 0x14, -1, -1, 0x00010201, 0xffffffff, 0), |
| 1252 | SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff, | 1258 | SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff, |
| 1253 | SYSC_MODULE_QUIRK_SGX), | 1259 | SYSC_MODULE_QUIRK_SGX), |
| 1260 | SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, | ||
| 1261 | 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), | ||
| 1262 | SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -1, 0x4ea2080d, 0xffffffff, | ||
| 1263 | SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), | ||
| 1254 | SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, | 1264 | SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, |
| 1255 | SYSC_MODULE_QUIRK_WDT), | 1265 | SYSC_MODULE_QUIRK_WDT), |
| 1256 | /* Watchdog on am3 and am4 */ | 1266 | /* Watchdog on am3 and am4 */ |
| @@ -1309,8 +1319,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { · 52 unmodified lines | |||
| 1309 | SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), | 1319 | SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), |
| 1310 | SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0), | 1320 | SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0), |
| 1311 | SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), | 1321 | SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), |
| 1312 | SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, | ||
| 1313 | 0xffffffff, 0), | ||
| 1314 | SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0), | 1322 | SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0), |
| 1315 | #endif | 1323 | #endif |
| 1316 | }; | 1324 | }; |
| @@ -1532,37 +1540,6 @@ static int sysc_legacy_init(struct sysc *ddata) · 215 unmodified lines | |||
| 1532 | return error; | 1540 | return error; |
| 1533 | } | 1541 | } |
| 1534 | 1542 | ||
| 1535 | /** | ||
| 1536 | * sysc_rstctrl_reset_deassert - deassert rstctrl reset | ||
| 1537 | * @ddata: device driver data | ||
| 1538 | * @reset: reset before deassert | ||
| 1539 | * | ||
| 1540 | * A module can have both OCP softreset control and external rstctrl. | ||
| 1541 | * If more complicated rstctrl resets are needed, please handle these | ||
| 1542 | * directly from the child device driver and map only the module reset | ||
| 1543 | * for the parent interconnect target module device. | ||
| 1544 | * | ||
| 1545 | * Automatic reset of the module on init can be skipped with the | ||
| 1546 | * "ti,no-reset-on-init" device tree property. | ||
| 1547 | */ | ||
| 1548 | static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset) | ||
| 1549 | { | ||
| 1550 | int error; | ||
| 1551 | |||
| 1552 | if (!ddata->rsts) | ||
| 1553 | return 0; | ||
| 1554 | |||
| 1555 | if (reset) { | ||
| 1556 | error = reset_control_assert(ddata->rsts); | ||
| 1557 | if (error) | ||
| 1558 | return error; | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | reset_control_deassert(ddata->rsts); | ||
| 1562 | |||
| 1563 | return 0; | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | /* | 1543 | /* |
| 1567 | * Note that the caller must ensure the interconnect target module is enabled | 1544 | * Note that the caller must ensure the interconnect target module is enabled |
| 1568 | * before calling reset. Otherwise reset will not complete. | 1545 | * before calling reset. Otherwise reset will not complete. |
| @@ -1625,15 +1602,6 @@ static int sysc_reset(struct sysc *ddata) · 56 unmodified lines | |||
| 1625 | static int sysc_init_module(struct sysc *ddata) | 1602 | static int sysc_init_module(struct sysc *ddata) |
| 1626 | { | 1603 | { |
| 1627 | int error = 0; | 1604 | int error = 0; |
| 1628 | bool manage_clocks = true; | ||
| 1629 | |||
| 1630 | error = sysc_rstctrl_reset_deassert(ddata, false); | ||
| 1631 | if (error) | ||
| 1632 | return error; | ||
| 1633 | |||
| 1634 | if (ddata->cfg.quirks & | ||
| 1635 | (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT)) | ||
| 1636 | manage_clocks = false; | ||
| 1637 | 1605 | ||
| 1638 | error = sysc_clockdomain_init(ddata); | 1606 | error = sysc_clockdomain_init(ddata); |
| 1639 | if (error) | 1607 | if (error) |
| @@ -1654,7 +1622,7 @@ static int sysc_init_module(struct sysc *ddata) · 14 unmodified lines | |||
| 1654 | goto err_opt_clocks; | 1622 | goto err_opt_clocks; |
| 1655 | 1623 | ||
| 1656 | if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) { | 1624 | if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) { |
| 1657 | error = sysc_rstctrl_reset_deassert(ddata, true); | 1625 | error = reset_control_deassert(ddata->rsts); |
| 1658 | if (error) | 1626 | if (error) |
| 1659 | goto err_main_clocks; | 1627 | goto err_main_clocks; |
| 1660 | } | 1628 | } |
| @@ -1666,28 +1634,32 @@ static int sysc_init_module(struct sysc *ddata) · 5 unmodified lines | |||
| 1666 | if (ddata->legacy_mode) { | 1634 | if (ddata->legacy_mode) { |
| 1667 | error = sysc_legacy_init(ddata); | 1635 | error = sysc_legacy_init(ddata); |
| 1668 | if (error) | 1636 | if (error) |
| 1669 | goto err_main_clocks; | 1637 | goto err_reset; |
| 1670 | } | 1638 | } |
| 1671 | 1639 | ||
| 1672 | if (!ddata->legacy_mode) { | 1640 | if (!ddata->legacy_mode) { |
| 1673 | error = sysc_enable_module(ddata->dev); | 1641 | error = sysc_enable_module(ddata->dev); |
| 1674 | if (error) | 1642 | if (error) |
| 1675 | goto err_main_clocks; | 1643 | goto err_reset; |
| 1676 | } | 1644 | } |
| 1677 | 1645 | ||
| 1678 | error = sysc_reset(ddata); | 1646 | error = sysc_reset(ddata); |
| 1679 | if (error) | 1647 | if (error) |
| 1680 | dev_err(ddata->dev, "Reset failed with %d\n", error); | 1648 | dev_err(ddata->dev, "Reset failed with %d\n", error); |
| 1681 | 1649 | ||
| 1682 | if (!ddata->legacy_mode && manage_clocks) | 1650 | if (error && !ddata->legacy_mode) |
| 1683 | sysc_disable_module(ddata->dev); | 1651 | sysc_disable_module(ddata->dev); |
| 1684 | 1652 | ||
| 1653 | err_reset: | ||
| 1654 | if (error && !(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) | ||
| 1655 | reset_control_assert(ddata->rsts); | ||
| 1656 | |||
| 1685 | err_main_clocks: | 1657 | err_main_clocks: |
| 1686 | if (manage_clocks) | 1658 | if (error) |
| 1687 | sysc_disable_main_clocks(ddata); | 1659 | sysc_disable_main_clocks(ddata); |
| 1688 | err_opt_clocks: | 1660 | err_opt_clocks: |
| 1689 | /* No re-enable of clockdomain autoidle to prevent module autoidle */ | 1661 | /* No re-enable of clockdomain autoidle to prevent module autoidle */ |
| 1690 | if (manage_clocks) { | 1662 | if (error) { |
| 1691 | sysc_disable_opt_clocks(ddata); | 1663 | sysc_disable_opt_clocks(ddata); |
| 1692 | sysc_clkdm_allow_idle(ddata); | 1664 | sysc_clkdm_allow_idle(ddata); |
| 1693 | } | 1665 | } |
| @@ -2460,10 +2432,17 @@ static int sysc_probe(struct platform_device *pdev) · 766 unmodified lines | |||
| 2460 | goto unprepare; | 2432 | goto unprepare; |
| 2461 | } | 2433 | } |
| 2462 | 2434 | ||
| 2463 | /* Balance reset counts */ | 2435 | /* Balance use counts as PM runtime should have enabled these all */ |
| 2464 | if (ddata->rsts) | 2436 | if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) |
| 2465 | reset_control_assert(ddata->rsts); | 2437 | reset_control_assert(ddata->rsts); |
| 2466 | 2438 | ||
| 2439 | if (!(ddata->cfg.quirks & | ||
| 2440 | (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))) { | ||
| 2441 | sysc_disable_main_clocks(ddata); | ||
| 2442 | sysc_disable_opt_clocks(ddata); | ||
| 2443 | sysc_clkdm_allow_idle(ddata); | ||
| 2444 | } | ||
| 2445 | |||
| 2467 | sysc_show_registers(ddata); | 2446 | sysc_show_registers(ddata); |
| 2468 | 2447 | ||
| 2469 | ddata->dev->type = &sysc_device_type; | 2448 | ddata->dev->type = &sysc_device_type; |
| @@ -323,7 +323,7 @@ static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db) · 322 unmodified lines | |||
| 323 | 323 | ||
| 324 | if (db->mask) | 324 | if (db->mask) |
| 325 | val = ioread64_hi_lo(db->addr) & db->mask; | 325 | val = ioread64_hi_lo(db->addr) & db->mask; |
| 326 | iowrite64_hi_lo(db->set, db->addr); | 326 | iowrite64_hi_lo(db->set | val, db->addr); |
| 327 | } | 327 | } |
| 328 | #endif | 328 | #endif |
| 329 | } | 329 | } |
| @@ -114,7 +114,7 @@ static int imx_dsp_probe(struct platform_device *pdev) · 113 unmodified lines | |||
| 114 | 114 | ||
| 115 | dev_info(dev, "NXP i.MX DSP IPC initialized\n"); | 115 | dev_info(dev, "NXP i.MX DSP IPC initialized\n"); |
| 116 | 116 | ||
| 117 | return devm_of_platform_populate(dev); | 117 | return 0; |
| 118 | out: | 118 | out: |
| 119 | kfree(chan_name); | 119 | kfree(chan_name); |
| 120 | for (j = 0; j < i; j++) { | 120 | for (j = 0; j < i; j++) { |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | 8 | ||
| 9 | #include <dt-bindings/firmware/imx/rsrc.h> | 9 | #include <dt-bindings/firmware/imx/rsrc.h> |
| 10 | #include <linux/firmware/imx/ipc.h> | 10 | #include <linux/firmware/imx/ipc.h> |
| 11 | #include <linux/firmware/imx/sci.h> | ||
| 11 | #include <linux/mailbox_client.h> | 12 | #include <linux/mailbox_client.h> |
| 12 | 13 | ||
| 13 | #define IMX_SC_IRQ_FUNC_ENABLE 1 | 14 | #define IMX_SC_IRQ_FUNC_ENABLE 1 |
| @@ -107,6 +107,12 @@ static void imx_scu_rx_callback(struct mbox_client *c, void *msg) · 106 unmodified lines | |||
| 107 | struct imx_sc_rpc_msg *hdr; | 107 | struct imx_sc_rpc_msg *hdr; |
| 108 | u32 *data = msg; | 108 | u32 *data = msg; |
| 109 | 109 | ||
| 110 | if (!sc_ipc->msg) { | ||
| 111 | dev_warn(sc_ipc->dev, "unexpected rx idx %d 0x%08x, ignore!\n", | ||
| 112 | sc_chan->idx, *data); | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | |||
| 110 | if (sc_chan->idx == 0) { | 116 | if (sc_chan->idx == 0) { |
| 111 | hdr = msg; | 117 | hdr = msg; |
| 112 | sc_ipc->rx_size = hdr->size; | 118 | sc_ipc->rx_size = hdr->size; |
| @@ -156,6 +162,7 @@ static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg) · 43 unmodified lines | |||
| 156 | */ | 162 | */ |
| 157 | int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) | 163 | int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) |
| 158 | { | 164 | { |
| 165 | uint8_t saved_svc, saved_func; | ||
| 159 | struct imx_sc_rpc_msg *hdr; | 166 | struct imx_sc_rpc_msg *hdr; |
| 160 | int ret; | 167 | int ret; |
| 161 | 168 | ||
| @@ -165,7 +172,11 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) · 3 unmodified lines | |||
| 165 | mutex_lock(&sc_ipc->lock); | 172 | mutex_lock(&sc_ipc->lock); |
| 166 | reinit_completion(&sc_ipc->done); | 173 | reinit_completion(&sc_ipc->done); |
| 167 | 174 | ||
| 168 | sc_ipc->msg = msg; | 175 | if (have_resp) { |
| 176 | sc_ipc->msg = msg; | ||
| 177 | saved_svc = ((struct imx_sc_rpc_msg *)msg)->svc; | ||
| 178 | saved_func = ((struct imx_sc_rpc_msg *)msg)->func; | ||
| 179 | } | ||
| 169 | sc_ipc->count = 0; | 180 | sc_ipc->count = 0; |
| 170 | ret = imx_scu_ipc_write(sc_ipc, msg); | 181 | ret = imx_scu_ipc_write(sc_ipc, msg); |
| 171 | if (ret < 0) { | 182 | if (ret < 0) { |
| @@ -184,9 +195,20 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) · 12 unmodified lines | |||
| 184 | /* response status is stored in hdr->func field */ | 195 | /* response status is stored in hdr->func field */ |
| 185 | hdr = msg; | 196 | hdr = msg; |
| 186 | ret = hdr->func; | 197 | ret = hdr->func; |
| 198 | /* | ||
| 199 | * Some special SCU firmware APIs do NOT have return value | ||
| 200 | * in hdr->func, but they do have response data, those special | ||
| 201 | * APIs are defined as void function in SCU firmware, so they | ||
| 202 | * should be treated as return success always. | ||
| 203 | */ | ||
| 204 | if ((saved_svc == IMX_SC_RPC_SVC_MISC) && | ||
| 205 | (saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID || | ||
| 206 | saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS)) | ||
| 207 | ret = 0; | ||
| 187 | } | 208 | } |
| 188 | 209 | ||
| 189 | out: | 210 | out: |
| 211 | sc_ipc->msg = NULL; | ||
| 190 | mutex_unlock(&sc_ipc->lock); | 212 | mutex_unlock(&sc_ipc->lock); |
| 191 | 213 | ||
| 192 | dev_dbg(sc_ipc->dev, "RPC SVC done\n"); | 214 | dev_dbg(sc_ipc->dev, "RPC SVC done\n"); |
| @@ -35,7 +35,7 @@ struct meson_sm_chip { · 34 unmodified lines | |||
| 35 | struct meson_sm_cmd cmd[]; | 35 | struct meson_sm_cmd cmd[]; |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | struct meson_sm_chip gxbb_chip = { | 38 | static const struct meson_sm_chip gxbb_chip = { |
| 39 | .shmem_size = SZ_4K, | 39 | .shmem_size = SZ_4K, |
| 40 | .cmd_shmem_in_base = 0x82000020, | 40 | .cmd_shmem_in_base = 0x82000020, |
| 41 | .cmd_shmem_out_base = 0x82000021, | 41 | .cmd_shmem_out_base = 0x82000021, |
| @@ -54,8 +54,6 @@ struct meson_sm_firmware { · 12 unmodified lines | |||
| 54 | void __iomem *sm_shmem_out_base; | 54 | void __iomem *sm_shmem_out_base; |
| 55 | }; | 55 | }; |
| 56 | 56 | ||
| 57 | static struct meson_sm_firmware fw; | ||
| 58 | |||
| 59 | static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, | 57 | static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, |
| 60 | unsigned int cmd_index) | 58 | unsigned int cmd_index) |
| 61 | { | 59 | { |
| @@ -90,6 +88,7 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) · 28 unmodified lines | |||
| 90 | /** | 88 | /** |
| 91 | * meson_sm_call - generic SMC32 call to the secure-monitor | 89 | * meson_sm_call - generic SMC32 call to the secure-monitor |
| 92 | * | 90 | * |
| 91 | * @fw: Pointer to secure-monitor firmware | ||
| 93 | * @cmd_index: Index of the SMC32 function ID | 92 | * @cmd_index: Index of the SMC32 function ID |
| 94 | * @ret: Returned value | 93 | * @ret: Returned value |
| 95 | * @arg0: SMC32 Argument 0 | 94 | * @arg0: SMC32 Argument 0 |
| @@ -100,15 +99,15 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) · 4 unmodified lines | |||
| 100 | * | 99 | * |
| 101 | * Return: 0 on success, a negative value on error | 100 | * Return: 0 on success, a negative value on error |
| 102 | */ | 101 | */ |
| 103 | int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, | 102 | int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index, |
| 104 | u32 arg1, u32 arg2, u32 arg3, u32 arg4) | 103 | u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) |
| 105 | { | 104 | { |
| 106 | u32 cmd, lret; | 105 | u32 cmd, lret; |
| 107 | 106 | ||
| 108 | if (!fw.chip) | 107 | if (!fw->chip) |
| 109 | return -ENOENT; | 108 | return -ENOENT; |
| 110 | 109 | ||
| 111 | cmd = meson_sm_get_cmd(fw.chip, cmd_index); | 110 | cmd = meson_sm_get_cmd(fw->chip, cmd_index); |
| 112 | if (!cmd) | 111 | if (!cmd) |
| 113 | return -EINVAL; | 112 | return -EINVAL; |
| 114 | 113 | ||
| @@ -124,6 +123,7 @@ EXPORT_SYMBOL(meson_sm_call); · 9 unmodified lines | |||
| 124 | /** | 123 | /** |
| 125 | * meson_sm_call_read - retrieve data from secure-monitor | 124 | * meson_sm_call_read - retrieve data from secure-monitor |
| 126 | * | 125 | * |
| 126 | * @fw: Pointer to secure-monitor firmware | ||
| 127 | * @buffer: Buffer to store the retrieved data | 127 | * @buffer: Buffer to store the retrieved data |
| 128 | * @bsize: Size of the buffer | 128 | * @bsize: Size of the buffer |
| 129 | * @cmd_index: Index of the SMC32 function ID | 129 | * @cmd_index: Index of the SMC32 function ID |
| @@ -137,22 +137,23 @@ EXPORT_SYMBOL(meson_sm_call); · 7 unmodified lines | |||
| 137 | * When 0 is returned there is no guarantee about the amount of | 137 | * When 0 is returned there is no guarantee about the amount of |
| 138 | * data read and bsize bytes are copied in buffer. | 138 | * data read and bsize bytes are copied in buffer. |
| 139 | */ | 139 | */ |
| 140 | int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, | 140 | int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, |
| 141 | u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) | 141 | unsigned int bsize, unsigned int cmd_index, u32 arg0, |
| 142 | u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||
| 142 | { | 143 | { |
| 143 | u32 size; | 144 | u32 size; |
| 144 | int ret; | 145 | int ret; |
| 145 | 146 | ||
| 146 | if (!fw.chip) | 147 | if (!fw->chip) |
| 147 | return -ENOENT; | 148 | return -ENOENT; |
| 148 | 149 | ||
| 149 | if (!fw.chip->cmd_shmem_out_base) | 150 | if (!fw->chip->cmd_shmem_out_base) |
| 150 | return -EINVAL; | 151 | return -EINVAL; |
| 151 | 152 | ||
| 152 | if (bsize > fw.chip->shmem_size) | 153 | if (bsize > fw->chip->shmem_size) |
| 153 | return -EINVAL; | 154 | return -EINVAL; |
| 154 | 155 | ||
| 155 | if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) | 156 | if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) |
| 156 | return -EINVAL; | 157 | return -EINVAL; |
| 157 | 158 | ||
| 158 | if (size > bsize) | 159 | if (size > bsize) |
| @@ -164,7 +165,7 @@ int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, · 5 unmodified lines | |||
| 164 | size = bsize; | 165 | size = bsize; |
| 165 | 166 | ||
| 166 | if (buffer) | 167 | if (buffer) |
| 167 | memcpy(buffer, fw.sm_shmem_out_base, size); | 168 | memcpy(buffer, fw->sm_shmem_out_base, size); |
| 168 | 169 | ||
| 169 | return ret; | 170 | return ret; |
| 170 | } | 171 | } |
| @@ -173,6 +174,7 @@ EXPORT_SYMBOL(meson_sm_call_read); · 2 unmodified lines | |||
| 173 | /** | 174 | /** |
| 174 | * meson_sm_call_write - send data to secure-monitor | 175 | * meson_sm_call_write - send data to secure-monitor |
| 175 | * | 176 | * |
| 177 | * @fw: Pointer to secure-monitor firmware | ||
| 176 | * @buffer: Buffer containing data to send | 178 | * @buffer: Buffer containing data to send |
| 177 | * @size: Size of the data to send | 179 | * @size: Size of the data to send |
| 178 | * @cmd_index: Index of the SMC32 function ID | 180 | * @cmd_index: Index of the SMC32 function ID |
| @@ -184,23 +186,24 @@ EXPORT_SYMBOL(meson_sm_call_read); · 5 unmodified lines | |||
| 184 | * | 186 | * |
| 185 | * Return: size of sent data on success, a negative value on error | 187 | * Return: size of sent data on success, a negative value on error |
| 186 | */ | 188 | */ |
| 187 | int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, | 189 | int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, |
| 188 | u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) | 190 | unsigned int size, unsigned int cmd_index, u32 arg0, |
| 191 | u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||
| 189 | { | 192 | { |
| 190 | u32 written; | 193 | u32 written; |
| 191 | 194 | ||
| 192 | if (!fw.chip) | 195 | if (!fw->chip) |
| 193 | return -ENOENT; | 196 | return -ENOENT; |
| 194 | 197 | ||
| 195 | if (size > fw.chip->shmem_size) | 198 | if (size > fw->chip->shmem_size) |
| 196 | return -EINVAL; | 199 | return -EINVAL; |
| 197 | 200 | ||
| 198 | if (!fw.chip->cmd_shmem_in_base) | 201 | if (!fw->chip->cmd_shmem_in_base) |
| 199 | return -EINVAL; | 202 | return -EINVAL; |
| 200 | 203 | ||
| 201 | memcpy(fw.sm_shmem_in_base, buffer, size); | 204 | memcpy(fw->sm_shmem_in_base, buffer, size); |
| 202 | 205 | ||
| 203 | if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) | 206 | if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) |
| 204 | return -EINVAL; | 207 | return -EINVAL; |
| 205 | 208 | ||
| 206 | if (!written) | 209 | if (!written) |
| @@ -210,6 +213,24 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, · 3 unmodified lines | |||
| 210 | } | 213 | } |
| 211 | EXPORT_SYMBOL(meson_sm_call_write); | 214 | EXPORT_SYMBOL(meson_sm_call_write); |
| 212 | 215 | ||
| 216 | /** | ||
| 217 | * meson_sm_get - get pointer to meson_sm_firmware structure. | ||
| 218 | * | ||
| 219 | * @sm_node: Pointer to the secure-monitor Device Tree node. | ||
| 220 | * | ||
| 221 | * Return: NULL is the secure-monitor device is not ready. | ||
| 222 | */ | ||
| 223 | struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node) | ||
| 224 | { | ||
| 225 | struct platform_device *pdev = of_find_device_by_node(sm_node); | ||
| 226 | |||
| 227 | if (!pdev) | ||
| 228 | return NULL; | ||
| 229 | |||
| 230 | return platform_get_drvdata(pdev); | ||
| 231 | } | ||
| 232 | EXPORT_SYMBOL_GPL(meson_sm_get); | ||
| 233 | |||
| 213 | #define SM_CHIP_ID_LENGTH 119 | 234 | #define SM_CHIP_ID_LENGTH 119 |
| 214 | #define SM_CHIP_ID_OFFSET 4 | 235 | #define SM_CHIP_ID_OFFSET 4 |
| 215 | #define SM_CHIP_ID_SIZE 12 | 236 | #define SM_CHIP_ID_SIZE 12 |
| @@ -217,33 +238,25 @@ EXPORT_SYMBOL(meson_sm_call_write); · 1 unmodified line | |||
| 217 | static ssize_t serial_show(struct device *dev, struct device_attribute *attr, | 238 | static ssize_t serial_show(struct device *dev, struct device_attribute *attr, |
| 218 | char *buf) | 239 | char *buf) |
| 219 | { | 240 | { |
| 241 | struct platform_device *pdev = to_platform_device(dev); | ||
| 242 | struct meson_sm_firmware *fw; | ||
| 220 | uint8_t *id_buf; | 243 | uint8_t *id_buf; |
| 221 | int ret; | 244 | int ret; |
| 222 | 245 | ||
| 246 | fw = platform_get_drvdata(pdev); | ||
| 247 | |||
| 223 | id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL); | 248 | id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL); |
| 224 | if (!id_buf) | 249 | if (!id_buf) |
| 225 | return -ENOMEM; | 250 | return -ENOMEM; |
| 226 | 251 | ||
| 227 | ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, | 252 | ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, |
| 228 | 0, 0, 0, 0, 0); | 253 | 0, 0, 0, 0, 0); |
| 229 | if (ret < 0) { | 254 | if (ret < 0) { |
| 230 | kfree(id_buf); | 255 | kfree(id_buf); |
| 231 | return ret; | 256 | return ret; |
| 232 | } | 257 | } |
| 233 | 258 | ||
| 234 | ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", | 259 | ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]); |
| 235 | id_buf[SM_CHIP_ID_OFFSET + 0], | ||
| 236 | id_buf[SM_CHIP_ID_OFFSET + 1], | ||
| 237 | id_buf[SM_CHIP_ID_OFFSET + 2], | ||
| 238 | id_buf[SM_CHIP_ID_OFFSET + 3], | ||
| 239 | id_buf[SM_CHIP_ID_OFFSET + 4], | ||
| 240 | id_buf[SM_CHIP_ID_OFFSET + 5], | ||
| 241 | id_buf[SM_CHIP_ID_OFFSET + 6], | ||
| 242 | id_buf[SM_CHIP_ID_OFFSET + 7], | ||
| 243 | id_buf[SM_CHIP_ID_OFFSET + 8], | ||
| 244 | id_buf[SM_CHIP_ID_OFFSET + 9], | ||
| 245 | id_buf[SM_CHIP_ID_OFFSET + 10], | ||
| 246 | id_buf[SM_CHIP_ID_OFFSET + 11]); | ||
| 247 | 260 | ||
| 248 | kfree(id_buf); | 261 | kfree(id_buf); |
| 249 | 262 | ||
| @@ -268,25 +281,34 @@ static const struct of_device_id meson_sm_ids[] = { · 18 unmodified lines | |||
| 268 | 281 | ||
| 269 | static int __init meson_sm_probe(struct platform_device *pdev) | 282 | static int __init meson_sm_probe(struct platform_device *pdev) |
| 270 | { | 283 | { |
| 284 | struct device *dev = &pdev->dev; | ||
| 271 | const struct meson_sm_chip *chip; | 285 | const struct meson_sm_chip *chip; |
| 286 | struct meson_sm_firmware *fw; | ||
| 287 | |||
| 288 | fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); | ||
| 289 | if (!fw) | ||
| 290 | return -ENOMEM; | ||
| 272 | 291 | ||
| 273 | chip = of_match_device(meson_sm_ids, &pdev->dev)->data; | 292 | chip = of_match_device(meson_sm_ids, dev)->data; |
| 274 | 293 | ||
| 275 | if (chip->cmd_shmem_in_base) { | 294 | if (chip->cmd_shmem_in_base) { |
| 276 | fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, | 295 | fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, |
| 277 | chip->shmem_size); | 296 | chip->shmem_size); |
| 278 | if (WARN_ON(!fw.sm_shmem_in_base)) | 297 | if (WARN_ON(!fw->sm_shmem_in_base)) |
| 279 | goto out; | 298 | goto out; |
| 280 | } | 299 | } |
| 281 | 300 | ||
| 282 | if (chip->cmd_shmem_out_base) { | 301 | if (chip->cmd_shmem_out_base) { |
| 283 | fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base, | 302 | fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base, |
| 284 | chip->shmem_size); | 303 | chip->shmem_size); |
| 285 | if (WARN_ON(!fw.sm_shmem_out_base)) | 304 | if (WARN_ON(!fw->sm_shmem_out_base)) |
| 286 | goto out_in_base; | 305 | goto out_in_base; |
| 287 | } | 306 | } |
| 288 | 307 | ||
| 289 | fw.chip = chip; | 308 | fw->chip = chip; |
| 309 | |||
| 310 | platform_set_drvdata(pdev, fw); | ||
| 311 | |||
| 290 | pr_info("secure-monitor enabled\n"); | 312 | pr_info("secure-monitor enabled\n"); |
| 291 | 313 | ||
| 292 | if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group)) | 314 | 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) · 2 unmodified lines | |||
| 295 | return 0; | 317 | return 0; |
| 296 | 318 | ||
| 297 | out_in_base: | 319 | out_in_base: |
| 298 | iounmap(fw.sm_shmem_in_base); | 320 | iounmap(fw->sm_shmem_in_base); |
| 299 | out: | 321 | out: |
| 300 | return -EINVAL; | 322 | return -EINVAL; |
| 301 | } | 323 | } |
| @@ -804,7 +804,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev) · 803 unmodified lines | |||
| 804 | } | 804 | } |
| 805 | 805 | ||
| 806 | static const struct dev_pm_ops tegra_bpmp_pm_ops = { | 806 | static const struct dev_pm_ops tegra_bpmp_pm_ops = { |
| 807 | .resume_early = tegra_bpmp_resume, | 807 | .resume_noirq = tegra_bpmp_resume, |
| 808 | }; | 808 | }; |
| 809 | 809 | ||
| 810 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | 810 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ |
| @@ -711,8 +711,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) · 710 unmodified lines | |||
| 711 | int ret; | 711 | int ret; |
| 712 | 712 | ||
| 713 | np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); | 713 | np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); |
| 714 | if (!np) | 714 | if (!np) { |
| 715 | return 0; | 715 | np = of_find_compatible_node(NULL, NULL, "xlnx,versal"); |
| 716 | if (!np) | ||
| 717 | return 0; | ||
| 718 | } | ||
| 716 | of_node_put(np); | 719 | of_node_put(np); |
| 717 | 720 | ||
| 718 | ret = get_set_conduit_method(dev->of_node); | 721 | ret = get_set_conduit_method(dev->of_node); |
| @@ -770,6 +773,7 @@ static int zynqmp_firmware_remove(struct platform_device *pdev) · 51 unmodified lines | |||
| 770 | 773 | ||
| 771 | static const struct of_device_id zynqmp_firmware_of_match[] = { | 774 | static const struct of_device_id zynqmp_firmware_of_match[] = { |
| 772 | {.compatible = "xlnx,zynqmp-firmware"}, | 775 | {.compatible = "xlnx,zynqmp-firmware"}, |
| 776 | {.compatible = "xlnx,versal-firmware"}, | ||
| 773 | {}, | 777 | {}, |
| 774 | }; | 778 | }; |
| 775 | MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match); | 779 | MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match); |
| @@ -1,12 +1,9 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * EBI driver for Atmel chips | 3 | * EBI driver for Atmel chips |
| 3 | * inspired by the fsl weim bus driver | 4 | * inspired by the fsl weim bus driver |
| 4 | * | 5 | * |
| 5 | * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com> | 6 | * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com> |
| 6 | * | ||
| 7 | * This file is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2. This program is licensed "as is" without any | ||
| 9 | * warranty of any kind, whether express or implied. | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include <linux/clk.h> | 9 | #include <linux/clk.h> |
| @@ -19,6 +16,8 @@ · 6 unmodified lines | |||
| 19 | #include <linux/regmap.h> | 16 | #include <linux/regmap.h> |
| 20 | #include <soc/at91/atmel-sfr.h> | 17 | #include <soc/at91/atmel-sfr.h> |
| 21 | 18 | ||
| 19 | #define AT91_EBI_NUM_CS 8 | ||
| 20 | |||
| 22 | struct atmel_ebi_dev_config { | 21 | struct atmel_ebi_dev_config { |
| 23 | int cs; | 22 | int cs; |
| 24 | struct atmel_smc_cs_conf smcconf; | 23 | struct atmel_smc_cs_conf smcconf; |
| @@ -314,7 +313,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, · 289 unmodified lines | |||
| 314 | if (ret) | 313 | if (ret) |
| 315 | return ret; | 314 | return ret; |
| 316 | 315 | ||
| 317 | if (cs >= AT91_MATRIX_EBI_NUM_CS || | 316 | if (cs >= AT91_EBI_NUM_CS || |
| 318 | !(ebi->caps->available_cs & BIT(cs))) { | 317 | !(ebi->caps->available_cs & BIT(cs))) { |
| 319 | dev_err(dev, "invalid reg property in %pOF\n", np); | 318 | dev_err(dev, "invalid reg property in %pOF\n", np); |
| 320 | return -EINVAL; | 319 | return -EINVAL; |
| @@ -344,7 +343,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, · 23 unmodified lines | |||
| 344 | apply = true; | 343 | apply = true; |
| 345 | 344 | ||
| 346 | i = 0; | 345 | i = 0; |
| 347 | for_each_set_bit(cs, &cslines, AT91_MATRIX_EBI_NUM_CS) { | 346 | for_each_set_bit(cs, &cslines, AT91_EBI_NUM_CS) { |
| 348 | ebid->configs[i].cs = cs; | 347 | ebid->configs[i].cs = cs; |
| 349 | 348 | ||
| 350 | if (apply) { | 349 | if (apply) { |
| @@ -127,7 +127,6 @@ enum dpfe_msg_fields { · 126 unmodified lines | |||
| 127 | MSG_COMMAND, | 127 | MSG_COMMAND, |
| 128 | MSG_ARG_COUNT, | 128 | MSG_ARG_COUNT, |
| 129 | MSG_ARG0, | 129 | MSG_ARG0, |
| 130 | MSG_CHKSUM, | ||
| 131 | MSG_FIELD_MAX = 16 /* Max number of arguments */ | 130 | MSG_FIELD_MAX = 16 /* Max number of arguments */ |
| 132 | }; | 131 | }; |
| 133 | 132 | ||
| @@ -180,7 +179,7 @@ struct dpfe_api { · 46 unmodified lines | |||
| 180 | }; | 179 | }; |
| 181 | 180 | ||
| 182 | /* Things we need for as long as we are active. */ | 181 | /* Things we need for as long as we are active. */ |
| 183 | struct private_data { | 182 | struct brcmstb_dpfe_priv { |
| 184 | void __iomem *regs; | 183 | void __iomem *regs; |
| 185 | void __iomem *dmem; | 184 | void __iomem *dmem; |
| 186 | void __iomem *imem; | 185 | void __iomem *imem; |
| @@ -232,9 +231,13 @@ static struct attribute *dpfe_v3_attrs[] = { · 45 unmodified lines | |||
| 232 | }; | 231 | }; |
| 233 | ATTRIBUTE_GROUPS(dpfe_v3); | 232 | ATTRIBUTE_GROUPS(dpfe_v3); |
| 234 | 233 | ||
| 235 | /* API v2 firmware commands */ | 234 | /* |
| 236 | static const struct dpfe_api dpfe_api_v2 = { | 235 | * Old API v2 firmware commands, as defined in the rev 0.61 specification, we |
| 237 | .version = 2, | 236 | * use a version set to 1 to denote that it is not compatible with the new API |
| 237 | * v2 and onwards. | ||
| 238 | */ | ||
| 239 | static const struct dpfe_api dpfe_api_old_v2 = { | ||
| 240 | .version = 1, | ||
| 238 | .fw_name = "dpfe.bin", | 241 | .fw_name = "dpfe.bin", |
| 239 | .sysfs_attrs = dpfe_v2_groups, | 242 | .sysfs_attrs = dpfe_v2_groups, |
| 240 | .command = { | 243 | .command = { |
| @@ -243,21 +246,42 @@ static const struct dpfe_api dpfe_api_v2 = { · 2 unmodified lines | |||
| 243 | [MSG_COMMAND] = 1, | 246 | [MSG_COMMAND] = 1, |
| 244 | [MSG_ARG_COUNT] = 1, | 247 | [MSG_ARG_COUNT] = 1, |
| 245 | [MSG_ARG0] = 1, | 248 | [MSG_ARG0] = 1, |
| 246 | [MSG_CHKSUM] = 4, | ||
| 247 | }, | 249 | }, |
| 248 | [DPFE_CMD_GET_REFRESH] = { | 250 | [DPFE_CMD_GET_REFRESH] = { |
| 249 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | 251 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
| 250 | [MSG_COMMAND] = 2, | 252 | [MSG_COMMAND] = 2, |
| 251 | [MSG_ARG_COUNT] = 1, | 253 | [MSG_ARG_COUNT] = 1, |
| 252 | [MSG_ARG0] = 1, | 254 | [MSG_ARG0] = 1, |
| 253 | [MSG_CHKSUM] = 5, | ||
| 254 | }, | 255 | }, |
| 255 | [DPFE_CMD_GET_VENDOR] = { | 256 | [DPFE_CMD_GET_VENDOR] = { |
| 256 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | 257 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
| 257 | [MSG_COMMAND] = 2, | 258 | [MSG_COMMAND] = 2, |
| 258 | [MSG_ARG_COUNT] = 1, | 259 | [MSG_ARG_COUNT] = 1, |
| 259 | [MSG_ARG0] = 2, | 260 | [MSG_ARG0] = 2, |
| 260 | [MSG_CHKSUM] = 6, | 261 | }, |
| 262 | } | ||
| 263 | }; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * API v2 firmware commands, as defined in the rev 0.8 specification, named new | ||
| 267 | * v2 here | ||
| 268 | */ | ||
| 269 | static const struct dpfe_api dpfe_api_new_v2 = { | ||
| 270 | .version = 2, | ||
| 271 | .fw_name = NULL, /* We expect the firmware to have been downloaded! */ | ||
| 272 | .sysfs_attrs = dpfe_v2_groups, | ||
| 273 | .command = { | ||
| 274 | [DPFE_CMD_GET_INFO] = { | ||
| 275 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||
| 276 | [MSG_COMMAND] = 0x101, | ||
| 277 | }, | ||
| 278 | [DPFE_CMD_GET_REFRESH] = { | ||
| 279 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||
| 280 | [MSG_COMMAND] = 0x201, | ||
| 281 | }, | ||
| 282 | [DPFE_CMD_GET_VENDOR] = { | ||
| 283 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | ||
| 284 | [MSG_COMMAND] = 0x202, | ||
| 261 | }, | 285 | }, |
| 262 | } | 286 | } |
| 263 | }; | 287 | }; |
| @@ -273,49 +297,51 @@ static const struct dpfe_api dpfe_api_v3 = { · 9 unmodified lines | |||
| 273 | [MSG_COMMAND] = 0x0101, | 297 | [MSG_COMMAND] = 0x0101, |
| 274 | [MSG_ARG_COUNT] = 1, | 298 | [MSG_ARG_COUNT] = 1, |
| 275 | [MSG_ARG0] = 1, | 299 | [MSG_ARG0] = 1, |
| 276 | [MSG_CHKSUM] = 0x104, | ||
| 277 | }, | 300 | }, |
| 278 | [DPFE_CMD_GET_REFRESH] = { | 301 | [DPFE_CMD_GET_REFRESH] = { |
| 279 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, | 302 | [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, |
| 280 | [MSG_COMMAND] = 0x0202, | 303 | [MSG_COMMAND] = 0x0202, |
| 281 | [MSG_ARG_COUNT] = 0, | 304 | [MSG_ARG_COUNT] = 0, |
| 282 | /* | ||
| 283 | * This is a bit ugly. Without arguments, the checksum | ||
| 284 | * follows right after the argument count and not at | ||
| 285 | * offset MSG_CHKSUM. | ||
| 286 | */ | ||
| 287 | [MSG_ARG0] = 0x203, | ||
| 288 | }, | 305 | }, |
| 289 | /* There's no GET_VENDOR command in API v3. */ | 306 | /* There's no GET_VENDOR command in API v3. */ |
| 290 | }, | 307 | }, |
| 291 | }; | 308 | }; |
| 292 | 309 | ||
| 293 | static bool is_dcpu_enabled(void __iomem *regs) | 310 | static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv) |
| 294 | { | 311 | { |
| 295 | u32 val; | 312 | u32 val; |
| 296 | 313 | ||
| 297 | val = readl_relaxed(regs + REG_DCPU_RESET); | 314 | mutex_lock(&priv->lock); |
| 315 | val = readl_relaxed(priv->regs + REG_DCPU_RESET); | ||
| 316 | mutex_unlock(&priv->lock); | ||
| 298 | 317 | ||
| 299 | return !(val & DCPU_RESET_MASK); | 318 | return !(val & DCPU_RESET_MASK); |
| 300 | } | 319 | } |
| 301 | 320 | ||
| 302 | static void __disable_dcpu(void __iomem *regs) | 321 | static void __disable_dcpu(struct brcmstb_dpfe_priv *priv) |
| 303 | { | 322 | { |
| 304 | u32 val; | 323 | u32 val; |
| 305 | 324 | ||
| 306 | if (!is_dcpu_enabled(regs)) | 325 | if (!is_dcpu_enabled(priv)) |
| 307 | return; | 326 | return; |
| 308 | 327 | ||
| 328 | mutex_lock(&priv->lock); | ||
| 329 | |||
| 309 | /* Put DCPU in reset if it's running. */ | 330 | /* Put DCPU in reset if it's running. */ |
| 310 | val = readl_relaxed(regs + REG_DCPU_RESET); | 331 | val = readl_relaxed(priv->regs + REG_DCPU_RESET); |
| 311 | val |= (1 << DCPU_RESET_SHIFT); | 332 | val |= (1 << DCPU_RESET_SHIFT); |
| 312 | writel_relaxed(val, regs + REG_DCPU_RESET); | 333 | writel_relaxed(val, priv->regs + REG_DCPU_RESET); |
| 334 | |||
| 335 | mutex_unlock(&priv->lock); | ||
| 313 | } | 336 | } |
| 314 | 337 | ||
| 315 | static void __enable_dcpu(void __iomem *regs) | 338 | static void __enable_dcpu(struct brcmstb_dpfe_priv *priv) |
| 316 | { | 339 | { |
| 340 | void __iomem *regs = priv->regs; | ||
| 317 | u32 val; | 341 | u32 val; |
| 318 | 342 | ||
| 343 | mutex_lock(&priv->lock); | ||
| 344 | |||
| 319 | /* Clear mailbox registers. */ | 345 | /* Clear mailbox registers. */ |
| 320 | writel_relaxed(0, regs + REG_TO_DCPU_MBOX); | 346 | writel_relaxed(0, regs + REG_TO_DCPU_MBOX); |
| 321 | writel_relaxed(0, regs + REG_TO_HOST_MBOX); | 347 | writel_relaxed(0, regs + REG_TO_HOST_MBOX); |
| @@ -329,6 +355,8 @@ static void __enable_dcpu(void __iomem *regs) · 7 unmodified lines | |||
| 329 | val = readl_relaxed(regs + REG_DCPU_RESET); | 355 | val = readl_relaxed(regs + REG_DCPU_RESET); |
| 330 | val &= ~(1 << DCPU_RESET_SHIFT); | 356 | val &= ~(1 << DCPU_RESET_SHIFT); |
| 331 | writel_relaxed(val, regs + REG_DCPU_RESET); | 357 | writel_relaxed(val, regs + REG_DCPU_RESET); |
| 358 | |||
| 359 | mutex_unlock(&priv->lock); | ||
| 332 | } | 360 | } |
| 333 | 361 | ||
| 334 | static unsigned int get_msg_chksum(const u32 msg[], unsigned int max) | 362 | 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) · 8 unmodified lines | |||
| 343 | return sum; | 371 | return sum; |
| 344 | } | 372 | } |
| 345 | 373 | ||
| 346 | static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, | 374 | static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response, |
| 347 | char *buf, ssize_t *size) | 375 | char *buf, ssize_t *size) |
| 348 | { | 376 | { |
| 349 | unsigned int msg_type; | 377 | unsigned int msg_type; |
| @@ -382,7 +410,7 @@ static void __iomem *get_msg_ptr(struct private_data *priv, u32 response, · 32 unmodified lines | |||
| 382 | return ptr; | 410 | return ptr; |
| 383 | } | 411 | } |
| 384 | 412 | ||
| 385 | static void __finalize_command(struct private_data *priv) | 413 | static void __finalize_command(struct brcmstb_dpfe_priv *priv) |
| 386 | { | 414 | { |
| 387 | unsigned int release_mbox; | 415 | unsigned int release_mbox; |
| 388 | 416 | ||
| @@ -390,12 +418,12 @@ static void __finalize_command(struct private_data *priv) · 1 unmodified line | |||
| 390 | * It depends on the API version which MBOX register we have to write to | 418 | * It depends on the API version which MBOX register we have to write to |
| 391 | * to signal we are done. | 419 | * to signal we are done. |
| 392 | */ | 420 | */ |
| 393 | release_mbox = (priv->dpfe_api->version < 3) | 421 | release_mbox = (priv->dpfe_api->version < 2) |
| 394 | ? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX; | 422 | ? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX; |
| 395 | writel_relaxed(0, priv->regs + release_mbox); | 423 | writel_relaxed(0, priv->regs + release_mbox); |
| 396 | } | 424 | } |
| 397 | 425 | ||
| 398 | static int __send_command(struct private_data *priv, unsigned int cmd, | 426 | static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd, |
| 399 | u32 result[]) | 427 | u32 result[]) |
| 400 | { | 428 | { |
| 401 | const u32 *msg = priv->dpfe_api->command[cmd]; | 429 | const u32 *msg = priv->dpfe_api->command[cmd]; |
| @@ -421,9 +449,17 @@ static int __send_command(struct private_data *priv, unsigned int cmd, · 19 unmodified lines | |||
| 421 | return -ETIMEDOUT; | 449 | return -ETIMEDOUT; |
| 422 | } | 450 | } |
| 423 | 451 | ||
| 452 | /* Compute checksum over the message */ | ||
| 453 | chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1; | ||
| 454 | chksum = get_msg_chksum(msg, chksum_idx); | ||
| 455 | |||
| 424 | /* Write command and arguments to message area */ | 456 | /* Write command and arguments to message area */ |
| 425 | for (i = 0; i < MSG_FIELD_MAX; i++) | 457 | for (i = 0; i < MSG_FIELD_MAX; i++) { |
| 426 | writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); | 458 | if (i == chksum_idx) |
| 459 | writel_relaxed(chksum, regs + DCPU_MSG_RAM(i)); | ||
| 460 | else | ||
| 461 | writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); | ||
| 462 | } | ||
| 427 | 463 | ||
| 428 | /* Tell DCPU there is a command waiting */ | 464 | /* Tell DCPU there is a command waiting */ |
| 429 | writel_relaxed(1, regs + REG_TO_DCPU_MBOX); | 465 | writel_relaxed(1, regs + REG_TO_DCPU_MBOX); |
| @@ -517,7 +553,7 @@ static int __verify_firmware(struct init_data *init, · 87 unmodified lines | |||
| 517 | 553 | ||
| 518 | /* Verify checksum by reading back the firmware from co-processor RAM. */ | 554 | /* Verify checksum by reading back the firmware from co-processor RAM. */ |
| 519 | static int __verify_fw_checksum(struct init_data *init, | 555 | static int __verify_fw_checksum(struct init_data *init, |
| 520 | struct private_data *priv, | 556 | struct brcmstb_dpfe_priv *priv, |
| 521 | const struct dpfe_firmware_header *header, | 557 | const struct dpfe_firmware_header *header, |
| 522 | u32 checksum) | 558 | u32 checksum) |
| 523 | { | 559 | { |
| @@ -571,26 +607,23 @@ static int __write_firmware(u32 __iomem *mem, const u32 *fw, · 47 unmodified lines | |||
| 571 | return 0; | 607 | return 0; |
| 572 | } | 608 | } |
| 573 | 609 | ||
| 574 | static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, | 610 | static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv) |
| 575 | struct init_data *init) | ||
| 576 | { | 611 | { |
| 577 | const struct dpfe_firmware_header *header; | 612 | const struct dpfe_firmware_header *header; |
| 578 | unsigned int dmem_size, imem_size; | 613 | unsigned int dmem_size, imem_size; |
| 579 | struct device *dev = &pdev->dev; | 614 | struct device *dev = priv->dev; |
| 580 | bool is_big_endian = false; | 615 | bool is_big_endian = false; |
| 581 | struct private_data *priv; | ||
| 582 | const struct firmware *fw; | 616 | const struct firmware *fw; |
| 583 | const u32 *dmem, *imem; | 617 | const u32 *dmem, *imem; |
| 618 | struct init_data init; | ||
| 584 | const void *fw_blob; | 619 | const void *fw_blob; |
| 585 | int ret; | 620 | int ret; |
| 586 | 621 | ||
| 587 | priv = platform_get_drvdata(pdev); | ||
| 588 | |||
| 589 | /* | 622 | /* |
| 590 | * Skip downloading the firmware if the DCPU is already running and | 623 | * Skip downloading the firmware if the DCPU is already running and |
| 591 | * responding to commands. | 624 | * responding to commands. |
| 592 | */ | 625 | */ |
| 593 | if (is_dcpu_enabled(priv->regs)) { | 626 | if (is_dcpu_enabled(priv)) { |
| 594 | u32 response[MSG_FIELD_MAX]; | 627 | u32 response[MSG_FIELD_MAX]; |
| 595 | 628 | ||
| 596 | ret = __send_command(priv, DPFE_CMD_GET_INFO, response); | 629 | ret = __send_command(priv, DPFE_CMD_GET_INFO, response); |
| @@ -606,20 +639,23 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, · 9 unmodified lines | |||
| 606 | if (!priv->dpfe_api->fw_name) | 639 | if (!priv->dpfe_api->fw_name) |
| 607 | return -ENODEV; | 640 | return -ENODEV; |
| 608 | 641 | ||
| 609 | ret = request_firmware(&fw, priv->dpfe_api->fw_name, dev); | 642 | ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev); |
| 610 | /* request_firmware() prints its own error messages. */ | 643 | /* |
| 644 | * Defer the firmware download if the firmware file couldn't be found. | ||
| 645 | * The root file system may not be available yet. | ||
| 646 | */ | ||
| 611 | if (ret) | 647 | if (ret) |
| 612 | return ret; | 648 | return (ret == -ENOENT) ? -EPROBE_DEFER : ret; |
| 613 | 649 | ||
| 614 | ret = __verify_firmware(init, fw); | 650 | ret = __verify_firmware(&init, fw); |
| 615 | if (ret) | 651 | if (ret) |
| 616 | return -EFAULT; | 652 | return -EFAULT; |
| 617 | 653 | ||
| 618 | __disable_dcpu(priv->regs); | 654 | __disable_dcpu(priv); |
| 619 | 655 | ||
| 620 | is_big_endian = init->is_big_endian; | 656 | is_big_endian = init.is_big_endian; |
| 621 | dmem_size = init->dmem_len; | 657 | dmem_size = init.dmem_len; |
| 622 | imem_size = init->imem_len; | 658 | imem_size = init.imem_len; |
| 623 | 659 | ||
| 624 | /* At the beginning of the firmware blob is a header. */ | 660 | /* At the beginning of the firmware blob is a header. */ |
| 625 | header = (struct dpfe_firmware_header *)fw->data; | 661 | header = (struct dpfe_firmware_header *)fw->data; |
| @@ -637,17 +673,17 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, · 11 unmodified lines | |||
| 637 | if (ret) | 673 | if (ret) |
| 638 | return ret; | 674 | return ret; |
| 639 | 675 | ||
| 640 | ret = __verify_fw_checksum(init, priv, header, init->chksum); | 676 | ret = __verify_fw_checksum(&init, priv, header, init.chksum); |
| 641 | if (ret) | 677 | if (ret) |
| 642 | return ret; | 678 | return ret; |
| 643 | 679 | ||
| 644 | __enable_dcpu(priv->regs); | 680 | __enable_dcpu(priv); |
| 645 | 681 | ||
| 646 | return 0; | 682 | return 0; |
| 647 | } | 683 | } |
| 648 | 684 | ||
| 649 | static ssize_t generic_show(unsigned int command, u32 response[], | 685 | static ssize_t generic_show(unsigned int command, u32 response[], |
| 650 | struct private_data *priv, char *buf) | 686 | struct brcmstb_dpfe_priv *priv, char *buf) |
| 651 | { | 687 | { |
| 652 | int ret; | 688 | int ret; |
| 653 | 689 | ||
| @@ -665,7 +701,7 @@ static ssize_t show_info(struct device *dev, struct device_attribute *devattr, · 11 unmodified lines | |||
| 665 | char *buf) | 701 | char *buf) |
| 666 | { | 702 | { |
| 667 | u32 response[MSG_FIELD_MAX]; | 703 | u32 response[MSG_FIELD_MAX]; |
| 668 | struct private_data *priv; | 704 | struct brcmstb_dpfe_priv *priv; |
| 669 | unsigned int info; | 705 | unsigned int info; |
| 670 | ssize_t ret; | 706 | ssize_t ret; |
| 671 | 707 | ||
| @@ -688,7 +724,7 @@ static ssize_t show_refresh(struct device *dev, · 16 unmodified lines | |||
| 688 | { | 724 | { |
| 689 | u32 response[MSG_FIELD_MAX]; | 725 | u32 response[MSG_FIELD_MAX]; |
| 690 | void __iomem *info; | 726 | void __iomem *info; |
| 691 | struct private_data *priv; | 727 | struct brcmstb_dpfe_priv *priv; |
| 692 | u8 refresh, sr_abort, ppre, thermal_offs, tuf; | 728 | u8 refresh, sr_abort, ppre, thermal_offs, tuf; |
| 693 | u32 mr4; | 729 | u32 mr4; |
| 694 | ssize_t ret; | 730 | ssize_t ret; |
| @@ -721,7 +757,7 @@ static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, · 26 unmodified lines | |||
| 721 | const char *buf, size_t count) | 757 | const char *buf, size_t count) |
| 722 | { | 758 | { |
| 723 | u32 response[MSG_FIELD_MAX]; | 759 | u32 response[MSG_FIELD_MAX]; |
| 724 | struct private_data *priv; | 760 | struct brcmstb_dpfe_priv *priv; |
| 725 | void __iomem *info; | 761 | void __iomem *info; |
| 726 | unsigned long val; | 762 | unsigned long val; |
| 727 | int ret; | 763 | int ret; |
| @@ -747,7 +783,7 @@ static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr, · 19 unmodified lines | |||
| 747 | char *buf) | 783 | char *buf) |
| 748 | { | 784 | { |
| 749 | u32 response[MSG_FIELD_MAX]; | 785 | u32 response[MSG_FIELD_MAX]; |
| 750 | struct private_data *priv; | 786 | struct brcmstb_dpfe_priv *priv; |
| 751 | void __iomem *info; | 787 | void __iomem *info; |
| 752 | ssize_t ret; | 788 | ssize_t ret; |
| 753 | u32 mr5, mr6, mr7, mr8, err; | 789 | u32 mr5, mr6, mr7, mr8, err; |
| @@ -778,7 +814,7 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, · 24 unmodified lines | |||
| 778 | char *buf) | 814 | char *buf) |
| 779 | { | 815 | { |
| 780 | u32 response[MSG_FIELD_MAX]; | 816 | u32 response[MSG_FIELD_MAX]; |
| 781 | struct private_data *priv; | 817 | struct brcmstb_dpfe_priv *priv; |
| 782 | ssize_t ret; | 818 | ssize_t ret; |
| 783 | u32 mr4, mr5, mr6, mr7, mr8, err; | 819 | u32 mr4, mr5, mr6, mr7, mr8, err; |
| 784 | 820 | ||
| @@ -800,16 +836,15 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr, · 15 unmodified lines | |||
| 800 | 836 | ||
| 801 | static int brcmstb_dpfe_resume(struct platform_device *pdev) | 837 | static int brcmstb_dpfe_resume(struct platform_device *pdev) |
| 802 | { | 838 | { |
| 803 | struct init_data init; | 839 | struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev); |
| 804 | 840 | ||
| 805 | return brcmstb_dpfe_download_firmware(pdev, &init); | 841 | return brcmstb_dpfe_download_firmware(priv); |
| 806 | } | 842 | } |
| 807 | 843 | ||
| 808 | static int brcmstb_dpfe_probe(struct platform_device *pdev) | 844 | static int brcmstb_dpfe_probe(struct platform_device *pdev) |
| 809 | { | 845 | { |
| 810 | struct device *dev = &pdev->dev; | 846 | struct device *dev = &pdev->dev; |
| 811 | struct private_data *priv; | 847 | struct brcmstb_dpfe_priv *priv; |
| 812 | struct init_data init; | ||
| 813 | struct resource *res; | 848 | struct resource *res; |
| 814 | int ret; | 849 | int ret; |
| 815 | 850 | ||
| @@ -817,6 +852,8 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) · 1 unmodified line | |||
| 817 | if (!priv) | 852 | if (!priv) |
| 818 | return -ENOMEM; | 853 | return -ENOMEM; |
| 819 | 854 | ||
| 855 | priv->dev = dev; | ||
| 856 | |||
| 820 | mutex_init(&priv->lock); | 857 | mutex_init(&priv->lock); |
| 821 | platform_set_drvdata(pdev, priv); | 858 | platform_set_drvdata(pdev, priv); |
| 822 | 859 | ||
| @@ -851,9 +888,10 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) · 28 unmodified lines | |||
| 851 | return -ENOENT; | 888 | return -ENOENT; |
| 852 | } | 889 | } |
| 853 | 890 | ||
| 854 | ret = brcmstb_dpfe_download_firmware(pdev, &init); | 891 | ret = brcmstb_dpfe_download_firmware(priv); |
| 855 | if (ret) { | 892 | if (ret) { |
| 856 | dev_err(dev, "Couldn't download firmware -- %d\n", ret); | 893 | if (ret != -EPROBE_DEFER) |
| 894 | dev_err(dev, "Couldn't download firmware -- %d\n", ret); | ||
| 857 | return ret; | 895 | return ret; |
| 858 | } | 896 | } |
| 859 | 897 | ||
| @@ -867,7 +905,7 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) · 7 unmodified lines | |||
| 867 | 905 | ||
| 868 | static int brcmstb_dpfe_remove(struct platform_device *pdev) | 906 | static int brcmstb_dpfe_remove(struct platform_device *pdev) |
| 869 | { | 907 | { |
| 870 | struct private_data *priv = dev_get_drvdata(&pdev->dev); | 908 | struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev); |
| 871 | 909 | ||
| 872 | sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs); | 910 | sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs); |
| 873 | 911 | ||
| @@ -876,10 +914,10 @@ static int brcmstb_dpfe_remove(struct platform_device *pdev) · 2 unmodified lines | |||
| 876 | 914 | ||
| 877 | static const struct of_device_id brcmstb_dpfe_of_match[] = { | 915 | static const struct of_device_id brcmstb_dpfe_of_match[] = { |
| 878 | /* Use legacy API v2 for a select number of chips */ | 916 | /* Use legacy API v2 for a select number of chips */ |
| 879 | { .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_v2 }, | 917 | { .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 }, |
| 880 | { .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_v2 }, | 918 | { .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 }, |
| 881 | { .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_v2 }, | 919 | { .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 }, |
| 882 | { .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_v2 }, | 920 | { .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 }, |
| 883 | /* API v3 is the default going forward */ | 921 | /* API v3 is the default going forward */ |
| 884 | { .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 }, | 922 | { .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 }, |
| 885 | {} | 923 | {} |
| @@ -1613,7 +1613,7 @@ static void emif_shutdown(struct platform_device *pdev) · 1612 unmodified lines | |||
| 1613 | static int get_emif_reg_values(struct emif_data *emif, u32 freq, | 1613 | static int get_emif_reg_values(struct emif_data *emif, u32 freq, |
| 1614 | struct emif_regs *regs) | 1614 | struct emif_regs *regs) |
| 1615 | { | 1615 | { |
| 1616 | u32 cs1_used, ip_rev, phy_type; | 1616 | u32 ip_rev, phy_type; |
| 1617 | u32 cl, type; | 1617 | u32 cl, type; |
| 1618 | const struct lpddr2_timings *timings; | 1618 | const struct lpddr2_timings *timings; |
| 1619 | const struct lpddr2_min_tck *min_tck; | 1619 | const struct lpddr2_min_tck *min_tck; |
| @@ -1621,7 +1621,6 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq, · 1 unmodified line | |||
| 1621 | const struct lpddr2_addressing *addressing; | 1621 | const struct lpddr2_addressing *addressing; |
| 1622 | struct emif_data *emif_for_calc; | 1622 | struct emif_data *emif_for_calc; |
| 1623 | struct device *dev; | 1623 | struct device *dev; |
| 1624 | const struct emif_custom_configs *custom_configs; | ||
| 1625 | 1624 | ||
| 1626 | dev = emif->dev; | 1625 | dev = emif->dev; |
| 1627 | /* | 1626 | /* |
| @@ -1639,12 +1638,10 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq, · 11 unmodified lines | |||
| 1639 | 1638 | ||
| 1640 | device_info = emif_for_calc->plat_data->device_info; | 1639 | device_info = emif_for_calc->plat_data->device_info; |
| 1641 | type = device_info->type; | 1640 | type = device_info->type; |
| 1642 | cs1_used = device_info->cs1_used; | ||
| 1643 | ip_rev = emif_for_calc->plat_data->ip_rev; | 1641 | ip_rev = emif_for_calc->plat_data->ip_rev; |
| 1644 | phy_type = emif_for_calc->plat_data->phy_type; | 1642 | phy_type = emif_for_calc->plat_data->phy_type; |
| 1645 | 1643 | ||
| 1646 | min_tck = emif_for_calc->plat_data->min_tck; | 1644 | min_tck = emif_for_calc->plat_data->min_tck; |
| 1647 | custom_configs = emif_for_calc->plat_data->custom_configs; | ||
| 1648 | 1645 | ||
| 1649 | set_ddr_clk_period(freq); | 1646 | set_ddr_clk_period(freq); |
| 1650 | 1647 | ||
| @@ -29,6 +29,7 @@ · 28 unmodified lines | |||
| 29 | #define DDR_TYPE_LPDDR2_S4 3 | 29 | #define DDR_TYPE_LPDDR2_S4 3 |
| 30 | #define DDR_TYPE_LPDDR2_S2 4 | 30 | #define DDR_TYPE_LPDDR2_S2 4 |
| 31 | #define DDR_TYPE_LPDDR2_NVM 5 | 31 | #define DDR_TYPE_LPDDR2_NVM 5 |
| 32 | #define DDR_TYPE_LPDDR3 6 | ||
| 32 | 33 | ||
| 33 | /* DDR IO width */ | 34 | /* DDR IO width */ |
| 34 | #define DDR_IO_WIDTH_4 1 | 35 | #define DDR_IO_WIDTH_4 1 |
| @@ -169,4 +170,64 @@ extern const struct lpddr2_timings · 134 unmodified lines | |||
| 169 | lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; | 170 | lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; |
| 170 | extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; | 171 | extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; |
| 171 | 172 | ||
| 173 | /* | ||
| 174 | * Structure for timings for LPDDR3 based on LPDDR2 plus additional fields. | ||
| 175 | * All parameters are in pico seconds(ps) excluding max_freq, min_freq which | ||
| 176 | * are in Hz. | ||
| 177 | */ | ||
| 178 | struct lpddr3_timings { | ||
| 179 | u32 max_freq; | ||
| 180 | u32 min_freq; | ||
| 181 | u32 tRFC; | ||
| 182 | u32 tRRD; | ||
| 183 | u32 tRPab; | ||
| 184 | u32 tRPpb; | ||
| 185 | u32 tRCD; | ||
| 186 | u32 tRC; | ||
| 187 | u32 tRAS; | ||
| 188 | u32 tWTR; | ||
| 189 | u32 tWR; | ||
| 190 | u32 tRTP; | ||
| 191 | u32 tW2W_C2C; | ||
| 192 | u32 tR2R_C2C; | ||
| 193 | u32 tWL; | ||
| 194 | u32 tDQSCK; | ||
| 195 | u32 tRL; | ||
| 196 | u32 tFAW; | ||
| 197 | u32 tXSR; | ||
| 198 | u32 tXP; | ||
| 199 | u32 tCKE; | ||
| 200 | u32 tCKESR; | ||
| 201 | u32 tMRD; | ||
| 202 | }; | ||
| 203 | |||
| 204 | /* | ||
| 205 | * Min value for some parameters in terms of number of tCK cycles(nCK) | ||
| 206 | * Please set to zero parameters that are not valid for a given memory | ||
| 207 | * type | ||
| 208 | */ | ||
| 209 | struct lpddr3_min_tck { | ||
| 210 | u32 tRFC; | ||
| 211 | u32 tRRD; | ||
| 212 | u32 tRPab; | ||
| 213 | u32 tRPpb; | ||
| 214 | u32 tRCD; | ||
| 215 | u32 tRC; | ||
| 216 | u32 tRAS; | ||
| 217 | u32 tWTR; | ||
| 218 | u32 tWR; | ||
| 219 | u32 tRTP; | ||
| 220 | u32 tW2W_C2C; | ||
| 221 | u32 tR2R_C2C; | ||
| 222 | u32 tWL; | ||
| 223 | u32 tDQSCK; | ||
| 224 | u32 tRL; | ||
| 225 | u32 tFAW; | ||
| 226 | u32 tXSR; | ||
| 227 | u32 tXP; | ||
| 228 | u32 tCKE; | ||
| 229 | u32 tCKESR; | ||
| 230 | u32 tMRD; | ||
| 231 | }; | ||
| 232 | |||
| 172 | #endif /* __JEDEC_DDR_H */ | 233 | #endif /* __JEDEC_DDR_H */ |
| @@ -3,6 +3,7 @@ · 2 unmodified lines | |||
| 3 | * OpenFirmware helpers for memory drivers | 3 | * OpenFirmware helpers for memory drivers |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2012 Texas Instruments, Inc. | 5 | * Copyright (C) 2012 Texas Instruments, Inc. |
| 6 | * Copyright (C) 2019 Samsung Electronics Co., Ltd. | ||
| 6 | */ | 7 | */ |
| 7 | 8 | ||
| 8 | #include <linux/device.h> | 9 | #include <linux/device.h> |
| @@ -149,3 +150,151 @@ default_timings: · 140 unmodified lines | |||
| 149 | return lpddr2_jedec_timings; | 150 | return lpddr2_jedec_timings; |
| 150 | } | 151 | } |
| 151 | EXPORT_SYMBOL(of_get_ddr_timings); | 152 | EXPORT_SYMBOL(of_get_ddr_timings); |
| 153 | |||
| 154 | /** | ||
| 155 | * of_lpddr3_get_min_tck() - extract min timing values for lpddr3 | ||
| 156 | * @np: pointer to ddr device tree node | ||
| 157 | * @device: device requesting for min timing values | ||
| 158 | * | ||
| 159 | * Populates the lpddr3_min_tck structure by extracting data | ||
| 160 | * from device tree node. Returns a pointer to the populated | ||
| 161 | * structure. If any error in populating the structure, returns NULL. | ||
| 162 | */ | ||
| 163 | const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, | ||
| 164 | struct device *dev) | ||
| 165 | { | ||
| 166 | int ret = 0; | ||
| 167 | struct lpddr3_min_tck *min; | ||
| 168 | |||
| 169 | min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL); | ||
| 170 | if (!min) | ||
| 171 | goto default_min_tck; | ||
| 172 | |||
| 173 | ret |= of_property_read_u32(np, "tRFC-min-tck", &min->tRFC); | ||
| 174 | ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD); | ||
| 175 | ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab); | ||
| 176 | ret |= of_property_read_u32(np, "tRPpb-min-tck", &min->tRPpb); | ||
| 177 | ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD); | ||
| 178 | ret |= of_property_read_u32(np, "tRC-min-tck", &min->tRC); | ||
| 179 | ret |= of_property_read_u32(np, "tRAS-min-tck", &min->tRAS); | ||
| 180 | ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR); | ||
| 181 | ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR); | ||
| 182 | ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP); | ||
| 183 | ret |= of_property_read_u32(np, "tW2W-C2C-min-tck", &min->tW2W_C2C); | ||
| 184 | ret |= of_property_read_u32(np, "tR2R-C2C-min-tck", &min->tR2R_C2C); | ||
| 185 | ret |= of_property_read_u32(np, "tWL-min-tck", &min->tWL); | ||
| 186 | ret |= of_property_read_u32(np, "tDQSCK-min-tck", &min->tDQSCK); | ||
| 187 | ret |= of_property_read_u32(np, "tRL-min-tck", &min->tRL); | ||
| 188 | ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW); | ||
| 189 | ret |= of_property_read_u32(np, "tXSR-min-tck", &min->tXSR); | ||
| 190 | ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP); | ||
| 191 | ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE); | ||
| 192 | ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR); | ||
| 193 | ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD); | ||
| 194 | |||
| 195 | if (ret) { | ||
| 196 | dev_warn(dev, "%s: errors while parsing min-tck values\n", | ||
| 197 | __func__); | ||
| 198 | devm_kfree(dev, min); | ||
| 199 | goto default_min_tck; | ||
| 200 | } | ||
| 201 | |||
| 202 | return min; | ||
| 203 | |||
| 204 | default_min_tck: | ||
| 205 | dev_warn(dev, "%s: using default min-tck values\n", __func__); | ||
| 206 | return NULL; | ||
| 207 | } | ||
| 208 | EXPORT_SYMBOL(of_lpddr3_get_min_tck); | ||
| 209 | |||
| 210 | static int of_lpddr3_do_get_timings(struct device_node *np, | ||
| 211 | struct lpddr3_timings *tim) | ||
| 212 | { | ||
| 213 | int ret; | ||
| 214 | |||
| 215 | /* The 'reg' param required since DT has changed, used as 'max-freq' */ | ||
| 216 | ret = of_property_read_u32(np, "reg", &tim->max_freq); | ||
| 217 | ret |= of_property_read_u32(np, "min-freq", &tim->min_freq); | ||
| 218 | ret |= of_property_read_u32(np, "tRFC", &tim->tRFC); | ||
| 219 | ret |= of_property_read_u32(np, "tRRD", &tim->tRRD); | ||
| 220 | ret |= of_property_read_u32(np, "tRPab", &tim->tRPab); | ||
| 221 | ret |= of_property_read_u32(np, "tRPpb", &tim->tRPpb); | ||
| 222 | ret |= of_property_read_u32(np, "tRCD", &tim->tRCD); | ||
| 223 | ret |= of_property_read_u32(np, "tRC", &tim->tRC); | ||
| 224 | ret |= of_property_read_u32(np, "tRAS", &tim->tRAS); | ||
| 225 | ret |= of_property_read_u32(np, "tWTR", &tim->tWTR); | ||
| 226 | ret |= of_property_read_u32(np, "tWR", &tim->tWR); | ||
| 227 | ret |= of_property_read_u32(np, "tRTP", &tim->tRTP); | ||
| 228 | ret |= of_property_read_u32(np, "tW2W-C2C", &tim->tW2W_C2C); | ||
| 229 | ret |= of_property_read_u32(np, "tR2R-C2C", &tim->tR2R_C2C); | ||
| 230 | ret |= of_property_read_u32(np, "tFAW", &tim->tFAW); | ||
| 231 | ret |= of_property_read_u32(np, "tXSR", &tim->tXSR); | ||
| 232 | ret |= of_property_read_u32(np, "tXP", &tim->tXP); | ||
| 233 | ret |= of_property_read_u32(np, "tCKE", &tim->tCKE); | ||
| 234 | ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR); | ||
| 235 | ret |= of_property_read_u32(np, "tMRD", &tim->tMRD); | ||
| 236 | |||
| 237 | return ret; | ||
| 238 | } | ||
| 239 | |||
| 240 | /** | ||
| 241 | * of_lpddr3_get_ddr_timings() - extracts the lpddr3 timings and updates no of | ||
| 242 | * frequencies available. | ||
| 243 | * @np_ddr: Pointer to ddr device tree node | ||
| 244 | * @dev: Device requesting for ddr timings | ||
| 245 | * @device_type: Type of ddr | ||
| 246 | * @nr_frequencies: No of frequencies available for ddr | ||
| 247 | * (updated by this function) | ||
| 248 | * | ||
| 249 | * Populates lpddr3_timings structure by extracting data from device | ||
| 250 | * tree node. Returns pointer to populated structure. If any error | ||
| 251 | * while populating, returns NULL. | ||
| 252 | */ | ||
| 253 | const struct lpddr3_timings | ||
| 254 | *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, struct device *dev, | ||
| 255 | u32 device_type, u32 *nr_frequencies) | ||
| 256 | { | ||
| 257 | struct lpddr3_timings *timings = NULL; | ||
| 258 | u32 arr_sz = 0, i = 0; | ||
| 259 | struct device_node *np_tim; | ||
| 260 | char *tim_compat = NULL; | ||
| 261 | |||
| 262 | switch (device_type) { | ||
| 263 | case DDR_TYPE_LPDDR3: | ||
| 264 | tim_compat = "jedec,lpddr3-timings"; | ||
| 265 | break; | ||
| 266 | default: | ||
| 267 | dev_warn(dev, "%s: un-supported memory type\n", __func__); | ||
| 268 | } | ||
| 269 | |||
| 270 | for_each_child_of_node(np_ddr, np_tim) | ||
| 271 | if (of_device_is_compatible(np_tim, tim_compat)) | ||
| 272 | arr_sz++; | ||
| 273 | |||
| 274 | if (arr_sz) | ||
| 275 | timings = devm_kcalloc(dev, arr_sz, sizeof(*timings), | ||
| 276 | GFP_KERNEL); | ||
| 277 | |||
| 278 | if (!timings) | ||
| 279 | goto default_timings; | ||
| 280 | |||
| 281 | for_each_child_of_node(np_ddr, np_tim) { | ||
| 282 | if (of_device_is_compatible(np_tim, tim_compat)) { | ||
| 283 | if (of_lpddr3_do_get_timings(np_tim, &timings[i])) { | ||
| 284 | devm_kfree(dev, timings); | ||
| 285 | goto default_timings; | ||
| 286 | } | ||
| 287 | i++; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | *nr_frequencies = arr_sz; | ||
| 292 | |||
| 293 | return timings; | ||
| 294 | |||
| 295 | default_timings: | ||
| 296 | dev_warn(dev, "%s: failed to get timings\n", __func__); | ||
| 297 | *nr_frequencies = 0; | ||
| 298 | return NULL; | ||
| 299 | } | ||
| 300 | 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, · 13 unmodified lines | |||
| 14 | extern const struct lpddr2_timings | 14 | extern const struct lpddr2_timings |
| 15 | *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, | 15 | *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, |
| 16 | u32 device_type, u32 *nr_frequencies); | 16 | u32 device_type, u32 *nr_frequencies); |
| 17 | extern const struct lpddr3_min_tck | ||
| 18 | *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev); | ||
| 19 | extern const struct lpddr3_timings | ||
| 20 | *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, | ||
| 21 | struct device *dev, u32 device_type, u32 *nr_frequencies); | ||
| 17 | #else | 22 | #else |
| 18 | static inline const struct lpddr2_min_tck | 23 | static inline const struct lpddr2_min_tck |
| 19 | *of_get_min_tck(struct device_node *np, struct device *dev) | 24 | *of_get_min_tck(struct device_node *np, struct device *dev) |
| @@ -27,6 +32,19 @@ static inline const struct lpddr2_timings · 7 unmodified lines | |||
| 27 | { | 32 | { |
| 28 | return NULL; | 33 | return NULL; |
| 29 | } | 34 | } |
| 35 | |||
| 36 | static inline const struct lpddr3_min_tck | ||
| 37 | *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev) | ||
| 38 | { | ||
| 39 | return NULL; | ||
| 40 | } | ||
| 41 | |||
| 42 | static inline const struct lpddr3_timings | ||
| 43 | *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, | ||
| 44 | struct device *dev, u32 device_type, u32 *nr_frequencies) | ||
| 45 | { | ||
| 46 | return NULL; | ||
| 47 | } | ||
| 30 | #endif /* CONFIG_OF && CONFIG_DDR */ | 48 | #endif /* CONFIG_OF && CONFIG_DDR */ |
| 31 | 49 | ||
| 32 | #endif /* __LINUX_MEMORY_OF_REG_ */ | 50 | #endif /* __LINUX_MEMORY_OF_REG_ */ |
| @@ -7,6 +7,19 @@ config SAMSUNG_MC · 6 unmodified lines | |||
| 7 | 7 | ||
| 8 | if SAMSUNG_MC | 8 | if SAMSUNG_MC |
| 9 | 9 | ||
| 10 | config EXYNOS5422_DMC | ||
| 11 | tristate "EXYNOS5422 Dynamic Memory Controller driver" | ||
| 12 | depends on ARCH_EXYNOS || (COMPILE_TEST && HAS_IOMEM) | ||
| 13 | select DDR | ||
| 14 | depends on DEVFREQ_GOV_SIMPLE_ONDEMAND | ||
| 15 | depends on (PM_DEVFREQ && PM_DEVFREQ_EVENT) | ||
| 16 | help | ||
| 17 | This adds driver for Exynos5422 DMC (Dynamic Memory Controller). | ||
| 18 | The driver provides support for Dynamic Voltage and Frequency Scaling in | ||
| 19 | DMC and DRAM. It also supports changing timings of DRAM running with | ||
| 20 | different frequency. The timings are calculated based on DT memory | ||
| 21 | information. | ||
| 22 | |||
| 10 | config EXYNOS_SROM | 23 | config EXYNOS_SROM |
| 11 | bool "Exynos SROM controller driver" if COMPILE_TEST | 24 | bool "Exynos SROM controller driver" if COMPILE_TEST |
| 12 | depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM) | 25 | depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM) |
| @@ -1,2 +1,3 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | obj-$(CONFIG_EXYNOS5422_DMC) += exynos5422-dmc.o | ||
| 2 | obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o | 3 | obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o |
| @@ -0,0 +1,1550 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2019 Samsung Electronics Co., Ltd. | ||
| 4 | * Author: Lukasz Luba <l.luba@partner.samsung.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/clk.h> | ||
| 8 | #include <linux/devfreq.h> | ||
| 9 | #include <linux/devfreq-event.h> | ||
| 10 | #include <linux/device.h> | ||
| 11 | #include <linux/interrupt.h> | ||
| 12 | #include <linux/io.h> | ||
| 13 | #include <linux/mfd/syscon.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/of_device.h> | ||
| 16 | #include <linux/pm_opp.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/regmap.h> | ||
| 19 | #include <linux/regulator/consumer.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | #include "../jedec_ddr.h" | ||
| 22 | #include "../of_memory.h" | ||
| 23 | |||
| 24 | #define EXYNOS5_DREXI_TIMINGAREF (0x0030) | ||
| 25 | #define EXYNOS5_DREXI_TIMINGROW0 (0x0034) | ||
| 26 | #define EXYNOS5_DREXI_TIMINGDATA0 (0x0038) | ||
| 27 | #define EXYNOS5_DREXI_TIMINGPOWER0 (0x003C) | ||
| 28 | #define EXYNOS5_DREXI_TIMINGROW1 (0x00E4) | ||
| 29 | #define EXYNOS5_DREXI_TIMINGDATA1 (0x00E8) | ||
| 30 | #define EXYNOS5_DREXI_TIMINGPOWER1 (0x00EC) | ||
| 31 | #define CDREX_PAUSE (0x2091c) | ||
| 32 | #define CDREX_LPDDR3PHY_CON3 (0x20a20) | ||
| 33 | #define CDREX_LPDDR3PHY_CLKM_SRC (0x20700) | ||
| 34 | #define EXYNOS5_TIMING_SET_SWI BIT(28) | ||
| 35 | #define USE_MX_MSPLL_TIMINGS (1) | ||
| 36 | #define USE_BPLL_TIMINGS (0) | ||
| 37 | #define EXYNOS5_AREF_NORMAL (0x2e) | ||
| 38 | |||
| 39 | #define DREX_PPCCLKCON (0x0130) | ||
| 40 | #define DREX_PEREV2CONFIG (0x013c) | ||
| 41 | #define DREX_PMNC_PPC (0xE000) | ||
| 42 | #define DREX_CNTENS_PPC (0xE010) | ||
| 43 | #define DREX_CNTENC_PPC (0xE020) | ||
| 44 | #define DREX_INTENS_PPC (0xE030) | ||
| 45 | #define DREX_INTENC_PPC (0xE040) | ||
| 46 | #define DREX_FLAG_PPC (0xE050) | ||
| 47 | #define DREX_PMCNT2_PPC (0xE130) | ||
| 48 | |||
| 49 | /* | ||
| 50 | * A value for register DREX_PMNC_PPC which should be written to reset | ||
| 51 | * the cycle counter CCNT (a reference wall clock). It sets zero to the | ||
| 52 | * CCNT counter. | ||
| 53 | */ | ||
| 54 | #define CC_RESET BIT(2) | ||
| 55 | |||
| 56 | /* | ||
| 57 | * A value for register DREX_PMNC_PPC which does the reset of all performance | ||
| 58 | * counters to zero. | ||
| 59 | */ | ||
| 60 | #define PPC_COUNTER_RESET BIT(1) | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Enables all configured counters (including cycle counter). The value should | ||
| 64 | * be written to the register DREX_PMNC_PPC. | ||
| 65 | */ | ||
| 66 | #define PPC_ENABLE BIT(0) | ||
| 67 | |||
| 68 | /* A value for register DREX_PPCCLKCON which enables performance events clock. | ||
| 69 | * Must be written before first access to the performance counters register | ||
| 70 | * set, otherwise it could crash. | ||
| 71 | */ | ||
| 72 | #define PEREV_CLK_EN BIT(0) | ||
| 73 | |||
| 74 | /* | ||
| 75 | * Values which are used to enable counters, interrupts or configure flags of | ||
| 76 | * the performance counters. They configure counter 2 and cycle counter. | ||
| 77 | */ | ||
| 78 | #define PERF_CNT2 BIT(2) | ||
| 79 | #define PERF_CCNT BIT(31) | ||
| 80 | |||
| 81 | /* | ||
| 82 | * Performance event types which are used for setting the preferred event | ||
| 83 | * to track in the counters. | ||
| 84 | * There is a set of different types, the values are from range 0 to 0x6f. | ||
| 85 | * These settings should be written to the configuration register which manages | ||
| 86 | * the type of the event (register DREX_PEREV2CONFIG). | ||
| 87 | */ | ||
| 88 | #define READ_TRANSFER_CH0 (0x6d) | ||
| 89 | #define READ_TRANSFER_CH1 (0x6f) | ||
| 90 | |||
| 91 | #define PERF_COUNTER_START_VALUE 0xff000000 | ||
| 92 | #define PERF_EVENT_UP_DOWN_THRESHOLD 900000000ULL | ||
| 93 | |||
| 94 | /** | ||
| 95 | * struct dmc_opp_table - Operating level desciption | ||
| 96 | * | ||
| 97 | * Covers frequency and voltage settings of the DMC operating mode. | ||
| 98 | */ | ||
| 99 | struct dmc_opp_table { | ||
| 100 | u32 freq_hz; | ||
| 101 | u32 volt_uv; | ||
| 102 | }; | ||
| 103 | |||
| 104 | /** | ||
| 105 | * struct exynos5_dmc - main structure describing DMC device | ||
| 106 | * | ||
| 107 | * The main structure for the Dynamic Memory Controller which covers clocks, | ||
| 108 | * memory regions, HW information, parameters and current operating mode. | ||
| 109 | */ | ||
| 110 | struct exynos5_dmc { | ||
| 111 | struct device *dev; | ||
| 112 | struct devfreq *df; | ||
| 113 | struct devfreq_simple_ondemand_data gov_data; | ||
| 114 | void __iomem *base_drexi0; | ||
| 115 | void __iomem *base_drexi1; | ||
| 116 | struct regmap *clk_regmap; | ||
| 117 | struct mutex lock; | ||
| 118 | unsigned long curr_rate; | ||
| 119 | unsigned long curr_volt; | ||
| 120 | unsigned long bypass_rate; | ||
| 121 | struct dmc_opp_table *opp; | ||
| 122 | struct dmc_opp_table opp_bypass; | ||
| 123 | int opp_count; | ||
| 124 | u32 timings_arr_size; | ||
| 125 | u32 *timing_row; | ||
| 126 | u32 *timing_data; | ||
| 127 | u32 *timing_power; | ||
| 128 | const struct lpddr3_timings *timings; | ||
| 129 | const struct lpddr3_min_tck *min_tck; | ||
| 130 | u32 bypass_timing_row; | ||
| 131 | u32 bypass_timing_data; | ||
| 132 | u32 bypass_timing_power; | ||
| 133 | struct regulator *vdd_mif; | ||
| 134 | struct clk *fout_spll; | ||
| 135 | struct clk *fout_bpll; | ||
| 136 | struct clk *mout_spll; | ||
| 137 | struct clk *mout_bpll; | ||
| 138 | struct clk *mout_mclk_cdrex; | ||
| 139 | struct clk *mout_mx_mspll_ccore; | ||
| 140 | struct clk *mx_mspll_ccore_phy; | ||
| 141 | struct clk *mout_mx_mspll_ccore_phy; | ||
| 142 | struct devfreq_event_dev **counter; | ||
| 143 | int num_counters; | ||
| 144 | u64 last_overflow_ts[2]; | ||
| 145 | unsigned long load; | ||
| 146 | unsigned long total; | ||
| 147 | bool in_irq_mode; | ||
| 148 | }; | ||
| 149 | |||
| 150 | #define TIMING_FIELD(t_name, t_bit_beg, t_bit_end) \ | ||
| 151 | { .name = t_name, .bit_beg = t_bit_beg, .bit_end = t_bit_end } | ||
| 152 | |||
| 153 | #define TIMING_VAL2REG(timing, t_val) \ | ||
| 154 | ({ \ | ||
| 155 | u32 __val; \ | ||
| 156 | __val = (t_val) << (timing)->bit_beg; \ | ||
| 157 | __val; \ | ||
| 158 | }) | ||
| 159 | |||
| 160 | struct timing_reg { | ||
| 161 | char *name; | ||
| 162 | int bit_beg; | ||
| 163 | int bit_end; | ||
| 164 | unsigned int val; | ||
| 165 | }; | ||
| 166 | |||
| 167 | static const struct timing_reg timing_row[] = { | ||
| 168 | TIMING_FIELD("tRFC", 24, 31), | ||
| 169 | TIMING_FIELD("tRRD", 20, 23), | ||
| 170 | TIMING_FIELD("tRP", 16, 19), | ||
| 171 | TIMING_FIELD("tRCD", 12, 15), | ||
| 172 | TIMING_FIELD("tRC", 6, 11), | ||
| 173 | TIMING_FIELD("tRAS", 0, 5), | ||
| 174 | }; | ||
| 175 | |||
| 176 | static const struct timing_reg timing_data[] = { | ||
| 177 | TIMING_FIELD("tWTR", 28, 31), | ||
| 178 | TIMING_FIELD("tWR", 24, 27), | ||
| 179 | TIMING_FIELD("tRTP", 20, 23), | ||
| 180 | TIMING_FIELD("tW2W-C2C", 14, 14), | ||
| 181 | TIMING_FIELD("tR2R-C2C", 12, 12), | ||
| 182 | TIMING_FIELD("WL", 8, 11), | ||
| 183 | TIMING_FIELD("tDQSCK", 4, 7), | ||
| 184 | TIMING_FIELD("RL", 0, 3), | ||
| 185 | }; | ||
| 186 | |||
| 187 | static const struct timing_reg timing_power[] = { | ||
| 188 | TIMING_FIELD("tFAW", 26, 31), | ||
| 189 | TIMING_FIELD("tXSR", 16, 25), | ||
| 190 | TIMING_FIELD("tXP", 8, 15), | ||
| 191 | TIMING_FIELD("tCKE", 4, 7), | ||
| 192 | TIMING_FIELD("tMRD", 0, 3), | ||
| 193 | }; | ||
| 194 | |||
| 195 | #define TIMING_COUNT (ARRAY_SIZE(timing_row) + ARRAY_SIZE(timing_data) + \ | ||
| 196 | ARRAY_SIZE(timing_power)) | ||
| 197 | |||
| 198 | static int exynos5_counters_set_event(struct exynos5_dmc *dmc) | ||
| 199 | { | ||
| 200 | int i, ret; | ||
| 201 | |||
| 202 | for (i = 0; i < dmc->num_counters; i++) { | ||
| 203 | if (!dmc->counter[i]) | ||
| 204 | continue; | ||
| 205 | ret = devfreq_event_set_event(dmc->counter[i]); | ||
| 206 | if (ret < 0) | ||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | return 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | static int exynos5_counters_enable_edev(struct exynos5_dmc *dmc) | ||
| 213 | { | ||
| 214 | int i, ret; | ||
| 215 | |||
| 216 | for (i = 0; i < dmc->num_counters; i++) { | ||
| 217 | if (!dmc->counter[i]) | ||
| 218 | continue; | ||
| 219 | ret = devfreq_event_enable_edev(dmc->counter[i]); | ||
| 220 | if (ret < 0) | ||
| 221 | return ret; | ||
| 222 | } | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | static int exynos5_counters_disable_edev(struct exynos5_dmc *dmc) | ||
| 227 | { | ||
| 228 | int i, ret; | ||
| 229 | |||
| 230 | for (i = 0; i < dmc->num_counters; i++) { | ||
| 231 | if (!dmc->counter[i]) | ||
| 232 | continue; | ||
| 233 | ret = devfreq_event_disable_edev(dmc->counter[i]); | ||
| 234 | if (ret < 0) | ||
| 235 | return ret; | ||
| 236 | } | ||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | /** | ||
| 241 | * find_target_freq_id() - Finds requested frequency in local DMC configuration | ||
| 242 | * @dmc: device for which the information is checked | ||
| 243 | * @target_rate: requested frequency in KHz | ||
| 244 | * | ||
| 245 | * Seeks in the local DMC driver structure for the requested frequency value | ||
| 246 | * and returns index or error value. | ||
| 247 | */ | ||
| 248 | static int find_target_freq_idx(struct exynos5_dmc *dmc, | ||
| 249 | unsigned long target_rate) | ||
| 250 | { | ||
| 251 | int i; | ||
| 252 | |||
| 253 | for (i = dmc->opp_count - 1; i >= 0; i--) | ||
| 254 | if (dmc->opp[i].freq_hz <= target_rate) | ||
| 255 | return i; | ||
| 256 | |||
| 257 | return -EINVAL; | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * exynos5_switch_timing_regs() - Changes bank register set for DRAM timings | ||
| 262 | * @dmc: device for which the new settings is going to be applied | ||
| 263 | * @set: boolean variable passing set value | ||
| 264 | * | ||
| 265 | * Changes the register set, which holds timing parameters. | ||
| 266 | * There is two register sets: 0 and 1. The register set 0 | ||
| 267 | * is used in normal operation when the clock is provided from main PLL. | ||
| 268 | * The bank register set 1 is used when the main PLL frequency is going to be | ||
| 269 | * changed and the clock is taken from alternative, stable source. | ||
| 270 | * This function switches between these banks according to the | ||
| 271 | * currently used clock source. | ||
| 272 | */ | ||
| 273 | static void exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set) | ||
| 274 | { | ||
| 275 | unsigned int reg; | ||
| 276 | int ret; | ||
| 277 | |||
| 278 | ret = regmap_read(dmc->clk_regmap, CDREX_LPDDR3PHY_CON3, ®); | ||
| 279 | |||
| 280 | if (set) | ||
| 281 | reg |= EXYNOS5_TIMING_SET_SWI; | ||
| 282 | else | ||
| 283 | reg &= ~EXYNOS5_TIMING_SET_SWI; | ||
| 284 | |||
| 285 | regmap_write(dmc->clk_regmap, CDREX_LPDDR3PHY_CON3, reg); | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * exynos5_init_freq_table() - Initialized PM OPP framework | ||
| 290 | * @dmc: DMC device for which the frequencies are used for OPP init | ||
| 291 | * @profile: devfreq device's profile | ||
| 292 | * | ||
| 293 | * Populate the devfreq device's OPP table based on current frequency, voltage. | ||
| 294 | */ | ||
| 295 | static int exynos5_init_freq_table(struct exynos5_dmc *dmc, | ||
| 296 | struct devfreq_dev_profile *profile) | ||
| 297 | { | ||
| 298 | int i, ret; | ||
| 299 | int idx; | ||
| 300 | unsigned long freq; | ||
| 301 | |||
| 302 | ret = dev_pm_opp_of_add_table(dmc->dev); | ||
| 303 | if (ret < 0) { | ||
| 304 | dev_err(dmc->dev, "Failed to get OPP table\n"); | ||
| 305 | return ret; | ||
| 306 | } | ||
| 307 | |||
| 308 | dmc->opp_count = dev_pm_opp_get_opp_count(dmc->dev); | ||
| 309 | |||
| 310 | dmc->opp = devm_kmalloc_array(dmc->dev, dmc->opp_count, | ||
| 311 | sizeof(struct dmc_opp_table), GFP_KERNEL); | ||
| 312 | if (!dmc->opp) | ||
| 313 | goto err_opp; | ||
| 314 | |||
| 315 | idx = dmc->opp_count - 1; | ||
| 316 | for (i = 0, freq = ULONG_MAX; i < dmc->opp_count; i++, freq--) { | ||
| 317 | struct dev_pm_opp *opp; | ||
| 318 | |||
| 319 | opp = dev_pm_opp_find_freq_floor(dmc->dev, &freq); | ||
| 320 | if (IS_ERR(opp)) | ||
| 321 | goto err_opp; | ||
| 322 | |||
| 323 | dmc->opp[idx - i].freq_hz = freq; | ||
| 324 | dmc->opp[idx - i].volt_uv = dev_pm_opp_get_voltage(opp); | ||
| 325 | |||
| 326 | dev_pm_opp_put(opp); | ||
| 327 | } | ||
| 328 | |||
| 329 | return 0; | ||
| 330 | |||
| 331 | err_opp: | ||
| 332 | dev_pm_opp_of_remove_table(dmc->dev); | ||
| 333 | |||
| 334 | return -EINVAL; | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * exynos5_set_bypass_dram_timings() - Low-level changes of the DRAM timings | ||
| 339 | * @dmc: device for which the new settings is going to be applied | ||
| 340 | * @param: DRAM parameters which passes timing data | ||
| 341 | * | ||
| 342 | * Low-level function for changing timings for DRAM memory clocking from | ||
| 343 | * 'bypass' clock source (fixed frequency @400MHz). | ||
| 344 | * It uses timing bank registers set 1. | ||
| 345 | */ | ||
| 346 | static void exynos5_set_bypass_dram_timings(struct exynos5_dmc *dmc) | ||
| 347 | { | ||
| 348 | writel(EXYNOS5_AREF_NORMAL, | ||
| 349 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGAREF); | ||
| 350 | |||
| 351 | writel(dmc->bypass_timing_row, | ||
| 352 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGROW1); | ||
| 353 | writel(dmc->bypass_timing_row, | ||
| 354 | dmc->base_drexi1 + EXYNOS5_DREXI_TIMINGROW1); | ||
| 355 | writel(dmc->bypass_timing_data, | ||
| 356 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGDATA1); | ||
| 357 | writel(dmc->bypass_timing_data, | ||
| 358 | dmc->base_drexi1 + EXYNOS5_DREXI_TIMINGDATA1); | ||
| 359 | writel(dmc->bypass_timing_power, | ||
| 360 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGPOWER1); | ||
| 361 | writel(dmc->bypass_timing_power, | ||
| 362 | dmc->base_drexi1 + EXYNOS5_DREXI_TIMINGPOWER1); | ||
| 363 | } | ||
| 364 | |||
| 365 | /** | ||
| 366 | * exynos5_dram_change_timings() - Low-level changes of the DRAM final timings | ||
| 367 | * @dmc: device for which the new settings is going to be applied | ||
| 368 | * @target_rate: target frequency of the DMC | ||
| 369 | * | ||
| 370 | * Low-level function for changing timings for DRAM memory operating from main | ||
| 371 | * clock source (BPLL), which can have different frequencies. Thus, each | ||
| 372 | * frequency must have corresponding timings register values in order to keep | ||
| 373 | * the needed delays. | ||
| 374 | * It uses timing bank registers set 0. | ||
| 375 | */ | ||
| 376 | static int exynos5_dram_change_timings(struct exynos5_dmc *dmc, | ||
| 377 | unsigned long target_rate) | ||
| 378 | { | ||
| 379 | int idx; | ||
| 380 | |||
| 381 | for (idx = dmc->opp_count - 1; idx >= 0; idx--) | ||
| 382 | if (dmc->opp[idx].freq_hz <= target_rate) | ||
| 383 | break; | ||
| 384 | |||
| 385 | if (idx < 0) | ||
| 386 | return -EINVAL; | ||
| 387 | |||
| 388 | writel(EXYNOS5_AREF_NORMAL, | ||
| 389 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGAREF); | ||
| 390 | |||
| 391 | writel(dmc->timing_row[idx], | ||
| 392 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGROW0); | ||
| 393 | writel(dmc->timing_row[idx], | ||
| 394 | dmc->base_drexi1 + EXYNOS5_DREXI_TIMINGROW0); | ||
| 395 | writel(dmc->timing_data[idx], | ||
| 396 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGDATA0); | ||
| 397 | writel(dmc->timing_data[idx], | ||
| 398 | dmc->base_drexi1 + EXYNOS5_DREXI_TIMINGDATA0); | ||
| 399 | writel(dmc->timing_power[idx], | ||
| 400 | dmc->base_drexi0 + EXYNOS5_DREXI_TIMINGPOWER0); | ||
| 401 | writel(dmc->timing_power[idx], | ||
| 402 | dmc->base_drexi1 + EXYNOS5_DREXI_TIMINGPOWER0); | ||
| 403 | |||
| 404 | return 0; | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * exynos5_dmc_align_target_voltage() - Sets the final voltage for the DMC | ||
| 409 | * @dmc: device for which it is going to be set | ||
| 410 | * @target_volt: new voltage which is chosen to be final | ||
| 411 | * | ||
| 412 | * Function tries to align voltage to the safe level for 'normal' mode. | ||
| 413 | * It checks the need of higher voltage and changes the value. The target | ||
| 414 | * voltage might be lower that currently set and still the system will be | ||
| 415 | * stable. | ||
| 416 | */ | ||
| 417 | static int exynos5_dmc_align_target_voltage(struct exynos5_dmc *dmc, | ||
| 418 | unsigned long target_volt) | ||
| 419 | { | ||
| 420 | int ret = 0; | ||
| 421 | |||
| 422 | if (dmc->curr_volt <= target_volt) | ||
| 423 | return 0; | ||
| 424 | |||
| 425 | ret = regulator_set_voltage(dmc->vdd_mif, target_volt, | ||
| 426 | target_volt); | ||
| 427 | if (!ret) | ||
| 428 | dmc->curr_volt = target_volt; | ||
| 429 | |||
| 430 | return ret; | ||
| 431 | } | ||
| 432 | |||
| 433 | /** | ||
| 434 | * exynos5_dmc_align_bypass_voltage() - Sets the voltage for the DMC | ||
| 435 | * @dmc: device for which it is going to be set | ||
| 436 | * @target_volt: new voltage which is chosen to be final | ||
| 437 | * | ||
| 438 | * Function tries to align voltage to the safe level for the 'bypass' mode. | ||
| 439 | * It checks the need of higher voltage and changes the value. | ||
| 440 | * The target voltage must not be less than currently needed, because | ||
| 441 | * for current frequency the device might become unstable. | ||
| 442 | */ | ||
| 443 | static int exynos5_dmc_align_bypass_voltage(struct exynos5_dmc *dmc, | ||
| 444 | unsigned long target_volt) | ||
| 445 | { | ||
| 446 | int ret = 0; | ||
| 447 | unsigned long bypass_volt = dmc->opp_bypass.volt_uv; | ||
| 448 | |||
| 449 | target_volt = max(bypass_volt, target_volt); | ||
| 450 | |||
| 451 | if (dmc->curr_volt >= target_volt) | ||
| 452 | return 0; | ||
| 453 | |||
| 454 | ret = regulator_set_voltage(dmc->vdd_mif, target_volt, | ||
| 455 | target_volt); | ||
| 456 | if (!ret) | ||
| 457 | dmc->curr_volt = target_volt; | ||
| 458 | |||
| 459 | return ret; | ||
| 460 | } | ||
| 461 | |||
| 462 | /** | ||
| 463 | * exynos5_dmc_align_bypass_dram_timings() - Chooses and sets DRAM timings | ||
| 464 | * @dmc: device for which it is going to be set | ||
| 465 | * @target_rate: new frequency which is chosen to be final | ||
| 466 | * | ||
| 467 | * Function changes the DRAM timings for the temporary 'bypass' mode. | ||
| 468 | */ | ||
| 469 | static int exynos5_dmc_align_bypass_dram_timings(struct exynos5_dmc *dmc, | ||
| 470 | unsigned long target_rate) | ||
| 471 | { | ||
| 472 | int idx = find_target_freq_idx(dmc, target_rate); | ||
| 473 | |||
| 474 | if (idx < 0) | ||
| 475 | return -EINVAL; | ||
| 476 | |||
| 477 | exynos5_set_bypass_dram_timings(dmc); | ||
| 478 | |||
| 479 | return 0; | ||
| 480 | } | ||
| 481 | |||
| 482 | /** | ||
| 483 | * exynos5_dmc_switch_to_bypass_configuration() - Switching to temporary clock | ||
| 484 | * @dmc: DMC device for which the switching is going to happen | ||
| 485 | * @target_rate: new frequency which is going to be set as a final | ||
| 486 | * @target_volt: new voltage which is going to be set as a final | ||
| 487 | * | ||
| 488 | * Function configures DMC and clocks for operating in temporary 'bypass' mode. | ||
| 489 | * This mode is used only temporary but if required, changes voltage and timings | ||
| 490 | * for DRAM chips. It switches the main clock to stable clock source for the | ||
| 491 | * period of the main PLL reconfiguration. | ||
| 492 | */ | ||
| 493 | static int | ||
| 494 | exynos5_dmc_switch_to_bypass_configuration(struct exynos5_dmc *dmc, | ||
| 495 | unsigned long target_rate, | ||
| 496 | unsigned long target_volt) | ||
| 497 | { | ||
| 498 | int ret; | ||
| 499 | |||
| 500 | /* | ||
| 501 | * Having higher voltage for a particular frequency does not harm | ||
| 502 | * the chip. Use it for the temporary frequency change when one | ||
| 503 | * voltage manipulation might be avoided. | ||
| 504 | */ | ||
| 505 | ret = exynos5_dmc_align_bypass_voltage(dmc, target_volt); | ||
| 506 | if (ret) | ||
| 507 | return ret; | ||
| 508 | |||
| 509 | /* | ||
| 510 | * Longer delays for DRAM does not cause crash, the opposite does. | ||
| 511 | */ | ||
| 512 | ret = exynos5_dmc_align_bypass_dram_timings(dmc, target_rate); | ||
| 513 | if (ret) | ||
| 514 | return ret; | ||
| 515 | |||
| 516 | /* | ||
| 517 | * Delays are long enough, so use them for the new coming clock. | ||
| 518 | */ | ||
| 519 | exynos5_switch_timing_regs(dmc, USE_MX_MSPLL_TIMINGS); | ||
| 520 | |||
| 521 | return ret; | ||
| 522 | } | ||
| 523 | |||
| 524 | /** | ||
| 525 | * exynos5_dmc_change_freq_and_volt() - Changes voltage and frequency of the DMC | ||
| 526 | * using safe procedure | ||
| 527 | * @dmc: device for which the frequency is going to be changed | ||
| 528 | * @target_rate: requested new frequency | ||
| 529 | * @target_volt: requested voltage which corresponds to the new frequency | ||
| 530 | * | ||
| 531 | * The DMC frequency change procedure requires a few steps. | ||
| 532 | * The main requirement is to change the clock source in the clk mux | ||
| 533 | * for the time of main clock PLL locking. The assumption is that the | ||
| 534 | * alternative clock source set as parent is stable. | ||
| 535 | * The second parent's clock frequency is fixed to 400MHz, it is named 'bypass' | ||
| 536 | * clock. This requires alignment in DRAM timing parameters for the new | ||
| 537 | * T-period. There is two bank sets for keeping DRAM | ||
| 538 | * timings: set 0 and set 1. The set 0 is used when main clock source is | ||
| 539 | * chosen. The 2nd set of regs is used for 'bypass' clock. Switching between | ||
| 540 | * the two bank sets is part of the process. | ||
| 541 | * The voltage must also be aligned to the minimum required level. There is | ||
| 542 | * this intermediate step with switching to 'bypass' parent clock source. | ||
| 543 | * if the old voltage is lower, it requires an increase of the voltage level. | ||
| 544 | * The complexity of the voltage manipulation is hidden in low level function. | ||
| 545 | * In this function there is last alignment of the voltage level at the end. | ||
| 546 | */ | ||
| 547 | static int | ||
| 548 | exynos5_dmc_change_freq_and_volt(struct exynos5_dmc *dmc, | ||
| 549 | unsigned long target_rate, | ||
| 550 | unsigned long target_volt) | ||
| 551 | { | ||
| 552 | int ret; | ||
| 553 | |||
| 554 | ret = exynos5_dmc_switch_to_bypass_configuration(dmc, target_rate, | ||
| 555 | target_volt); | ||
| 556 | if (ret) | ||
| 557 | return ret; | ||
| 558 | |||
| 559 | /* | ||
| 560 | * Voltage is set at least to a level needed for this frequency, | ||
| 561 | * so switching clock source is safe now. | ||
| 562 | */ | ||
| 563 | clk_prepare_enable(dmc->fout_spll); | ||
| 564 | clk_prepare_enable(dmc->mout_spll); | ||
| 565 | clk_prepare_enable(dmc->mout_mx_mspll_ccore); | ||
| 566 | |||
| 567 | ret = clk_set_parent(dmc->mout_mclk_cdrex, dmc->mout_mx_mspll_ccore); | ||
| 568 | if (ret) | ||
| 569 | goto disable_clocks; | ||
| 570 | |||
| 571 | /* | ||
| 572 | * We are safe to increase the timings for current bypass frequency. | ||
| 573 | * Thanks to this the settings will be ready for the upcoming clock | ||
| 574 | * source change. | ||
| 575 | */ | ||
| 576 | exynos5_dram_change_timings(dmc, target_rate); | ||
| 577 | |||
| 578 | clk_set_rate(dmc->fout_bpll, target_rate); | ||
| 579 | |||
| 580 | exynos5_switch_timing_regs(dmc, USE_BPLL_TIMINGS); | ||
| 581 | |||
| 582 | ret = clk_set_parent(dmc->mout_mclk_cdrex, dmc->mout_bpll); | ||
| 583 | if (ret) | ||
| 584 | goto disable_clocks; | ||
| 585 | |||
| 586 | /* | ||
| 587 | * Make sure if the voltage is not from 'bypass' settings and align to | ||
| 588 | * the right level for power efficiency. | ||
| 589 | */ | ||
| 590 | ret = exynos5_dmc_align_target_voltage(dmc, target_volt); | ||
| 591 | |||
| 592 | disable_clocks: | ||
| 593 | clk_disable_unprepare(dmc->mout_mx_mspll_ccore); | ||
| 594 | clk_disable_unprepare(dmc->mout_spll); | ||
| 595 | clk_disable_unprepare(dmc->fout_spll); | ||
| 596 | |||
| 597 | return ret; | ||
| 598 | } | ||
| 599 | |||
| 600 | /** | ||
| 601 | * exynos5_dmc_get_volt_freq() - Gets the frequency and voltage from the OPP | ||
| 602 | * table. | ||
| 603 | * @dmc: device for which the frequency is going to be changed | ||
| 604 | * @freq: requested frequency in KHz | ||
| 605 | * @target_rate: returned frequency which is the same or lower than | ||
| 606 | * requested | ||
| 607 | * @target_volt: returned voltage which corresponds to the returned | ||
| 608 | * frequency | ||
| 609 | * | ||
| 610 | * Function gets requested frequency and checks OPP framework for needed | ||
| 611 | * frequency and voltage. It populates the values 'target_rate' and | ||
| 612 | * 'target_volt' or returns error value when OPP framework fails. | ||
| 613 | */ | ||
| 614 | static int exynos5_dmc_get_volt_freq(struct exynos5_dmc *dmc, | ||
| 615 | unsigned long *freq, | ||
| 616 | unsigned long *target_rate, | ||
| 617 | unsigned long *target_volt, u32 flags) | ||
| 618 | { | ||
| 619 | struct dev_pm_opp *opp; | ||
| 620 | |||
| 621 | opp = devfreq_recommended_opp(dmc->dev, freq, flags); | ||
| 622 | if (IS_ERR(opp)) | ||
| 623 | return PTR_ERR(opp); | ||
| 624 | |||
| 625 | *target_rate = dev_pm_opp_get_freq(opp); | ||
| 626 | *target_volt = dev_pm_opp_get_voltage(opp); | ||
| 627 | dev_pm_opp_put(opp); | ||
| 628 | |||
| 629 | return 0; | ||
| 630 | } | ||
| 631 | |||
| 632 | /** | ||
| 633 | * exynos5_dmc_target() - Function responsible for changing frequency of DMC | ||
| 634 | * @dev: device for which the frequency is going to be changed | ||
| 635 | * @freq: requested frequency in KHz | ||
| 636 | * @flags: flags provided for this frequency change request | ||
| 637 | * | ||
| 638 | * An entry function provided to the devfreq framework which provides frequency | ||
| 639 | * change of the DMC. The function gets the possible rate from OPP table based | ||
| 640 | * on requested frequency. It calls the next function responsible for the | ||
| 641 | * frequency and voltage change. In case of failure, does not set 'curr_rate' | ||
| 642 | * and returns error value to the framework. | ||
| 643 | */ | ||
| 644 | static int exynos5_dmc_target(struct device *dev, unsigned long *freq, | ||
| 645 | u32 flags) | ||
| 646 | { | ||
| 647 | struct exynos5_dmc *dmc = dev_get_drvdata(dev); | ||
| 648 | unsigned long target_rate = 0; | ||
| 649 | unsigned long target_volt = 0; | ||
| 650 | int ret; | ||
| 651 | |||
| 652 | ret = exynos5_dmc_get_volt_freq(dmc, freq, &target_rate, &target_volt, | ||
| 653 | flags); | ||
| 654 | |||
| 655 | if (ret) | ||
| 656 | return ret; | ||
| 657 | |||
| 658 | if (target_rate == dmc->curr_rate) | ||
| 659 | return 0; | ||
| 660 | |||
| 661 | mutex_lock(&dmc->lock); | ||
| 662 | |||
| 663 | ret = exynos5_dmc_change_freq_and_volt(dmc, target_rate, target_volt); | ||
| 664 | |||
| 665 | if (ret) { | ||
| 666 | mutex_unlock(&dmc->lock); | ||
| 667 | return ret; | ||
| 668 | } | ||
| 669 | |||
| 670 | dmc->curr_rate = target_rate; | ||
| 671 | |||
| 672 | mutex_unlock(&dmc->lock); | ||
| 673 | return 0; | ||
| 674 | } | ||
| 675 | |||
| 676 | /** | ||
| 677 | * exynos5_counters_get() - Gets the performance counters values. | ||
| 678 | * @dmc: device for which the counters are going to be checked | ||
| 679 | * @load_count: variable which is populated with counter value | ||
| 680 | * @total_count: variable which is used as 'wall clock' reference | ||
| 681 | * | ||
| 682 | * Function which provides performance counters values. It sums up counters for | ||
| 683 | * two DMC channels. The 'total_count' is used as a reference and max value. | ||
| 684 | * The ratio 'load_count/total_count' shows the busy percentage [0%, 100%]. | ||
| 685 | */ | ||
| 686 | static int exynos5_counters_get(struct exynos5_dmc *dmc, | ||
| 687 | unsigned long *load_count, | ||
| 688 | unsigned long *total_count) | ||
| 689 | { | ||
| 690 | unsigned long total = 0; | ||
| 691 | struct devfreq_event_data event; | ||
| 692 | int ret, i; | ||
| 693 | |||
| 694 | *load_count = 0; | ||
| 695 | |||
| 696 | /* Take into account only read+write counters, but stop all */ | ||
| 697 | for (i = 0; i < dmc->num_counters; i++) { | ||
| 698 | if (!dmc->counter[i]) | ||
| 699 | continue; | ||
| 700 | |||
| 701 | ret = devfreq_event_get_event(dmc->counter[i], &event); | ||
| 702 | if (ret < 0) | ||
| 703 | return ret; | ||
| 704 | |||
| 705 | *load_count += event.load_count; | ||
| 706 | |||
| 707 | if (total < event.total_count) | ||
| 708 | total = event.total_count; | ||
| 709 | } | ||
| 710 | |||
| 711 | *total_count = total; | ||
| 712 | |||
| 713 | return 0; | ||
| 714 | } | ||
| 715 | |||
| 716 | /** | ||
| 717 | * exynos5_dmc_start_perf_events() - Setup and start performance event counters | ||
| 718 | * @dmc: device for which the counters are going to be checked | ||
| 719 | * @beg_value: initial value for the counter | ||
| 720 | * | ||
| 721 | * Function which enables needed counters, interrupts and sets initial values | ||
| 722 | * then starts the counters. | ||
| 723 | */ | ||
| 724 | static void exynos5_dmc_start_perf_events(struct exynos5_dmc *dmc, | ||
| 725 | u32 beg_value) | ||
| 726 | { | ||
| 727 | /* Enable interrupts for counter 2 */ | ||
| 728 | writel(PERF_CNT2, dmc->base_drexi0 + DREX_INTENS_PPC); | ||
| 729 | writel(PERF_CNT2, dmc->base_drexi1 + DREX_INTENS_PPC); | ||
| 730 | |||
| 731 | /* Enable counter 2 and CCNT */ | ||
| 732 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi0 + DREX_CNTENS_PPC); | ||
| 733 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi1 + DREX_CNTENS_PPC); | ||
| 734 | |||
| 735 | /* Clear overflow flag for all counters */ | ||
| 736 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi0 + DREX_FLAG_PPC); | ||
| 737 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi1 + DREX_FLAG_PPC); | ||
| 738 | |||
| 739 | /* Reset all counters */ | ||
| 740 | writel(CC_RESET | PPC_COUNTER_RESET, dmc->base_drexi0 + DREX_PMNC_PPC); | ||
| 741 | writel(CC_RESET | PPC_COUNTER_RESET, dmc->base_drexi1 + DREX_PMNC_PPC); | ||
| 742 | |||
| 743 | /* | ||
| 744 | * Set start value for the counters, the number of samples that | ||
| 745 | * will be gathered is calculated as: 0xffffffff - beg_value | ||
| 746 | */ | ||
| 747 | writel(beg_value, dmc->base_drexi0 + DREX_PMCNT2_PPC); | ||
| 748 | writel(beg_value, dmc->base_drexi1 + DREX_PMCNT2_PPC); | ||
| 749 | |||
| 750 | /* Start all counters */ | ||
| 751 | writel(PPC_ENABLE, dmc->base_drexi0 + DREX_PMNC_PPC); | ||
| 752 | writel(PPC_ENABLE, dmc->base_drexi1 + DREX_PMNC_PPC); | ||
| 753 | } | ||
| 754 | |||
| 755 | /** | ||
| 756 | * exynos5_dmc_perf_events_calc() - Calculate utilization | ||
| 757 | * @dmc: device for which the counters are going to be checked | ||
| 758 | * @diff_ts: time between last interrupt and current one | ||
| 759 | * | ||
| 760 | * Function which calculates needed utilization for the devfreq governor. | ||
| 761 | * It prepares values for 'busy_time' and 'total_time' based on elapsed time | ||
| 762 | * between interrupts, which approximates utilization. | ||
| 763 | */ | ||
| 764 | static void exynos5_dmc_perf_events_calc(struct exynos5_dmc *dmc, u64 diff_ts) | ||
| 765 | { | ||
| 766 | /* | ||
| 767 | * This is a simple algorithm for managing traffic on DMC. | ||
| 768 | * When there is almost no load the counters overflow every 4s, | ||
| 769 | * no mater the DMC frequency. | ||
| 770 | * The high load might be approximated using linear function. | ||
| 771 | * Knowing that, simple calculation can provide 'busy_time' and | ||
| 772 | * 'total_time' to the devfreq governor which picks up target | ||
| 773 | * frequency. | ||
| 774 | * We want a fast ramp up and slow decay in frequency change function. | ||
| 775 | */ | ||
| 776 | if (diff_ts < PERF_EVENT_UP_DOWN_THRESHOLD) { | ||
| 777 | /* | ||
| 778 | * Set higher utilization for the simple_ondemand governor. | ||
| 779 | * The governor should increase the frequency of the DMC. | ||
| 780 | */ | ||
| 781 | dmc->load = 70; | ||
| 782 | dmc->total = 100; | ||
| 783 | } else { | ||
| 784 | /* | ||
| 785 | * Set low utilization for the simple_ondemand governor. | ||
| 786 | * The governor should decrease the frequency of the DMC. | ||
| 787 | */ | ||
| 788 | dmc->load = 35; | ||
| 789 | dmc->total = 100; | ||
| 790 | } | ||
| 791 | |||
| 792 | dev_dbg(dmc->dev, "diff_ts=%llu\n", diff_ts); | ||
| 793 | } | ||
| 794 | |||
| 795 | /** | ||
| 796 | * exynos5_dmc_perf_events_check() - Checks the status of the counters | ||
| 797 | * @dmc: device for which the counters are going to be checked | ||
| 798 | * | ||
| 799 | * Function which is called from threaded IRQ to check the counters state | ||
| 800 | * and to call approximation for the needed utilization. | ||
| 801 | */ | ||
| 802 | static void exynos5_dmc_perf_events_check(struct exynos5_dmc *dmc) | ||
| 803 | { | ||
| 804 | u32 val; | ||
| 805 | u64 diff_ts, ts; | ||
| 806 | |||
| 807 | ts = ktime_get_ns(); | ||
| 808 | |||
| 809 | /* Stop all counters */ | ||
| 810 | writel(0, dmc->base_drexi0 + DREX_PMNC_PPC); | ||
| 811 | writel(0, dmc->base_drexi1 + DREX_PMNC_PPC); | ||
| 812 | |||
| 813 | /* Check the source in interrupt flag registers (which channel) */ | ||
| 814 | val = readl(dmc->base_drexi0 + DREX_FLAG_PPC); | ||
| 815 | if (val) { | ||
| 816 | diff_ts = ts - dmc->last_overflow_ts[0]; | ||
| 817 | dmc->last_overflow_ts[0] = ts; | ||
| 818 | dev_dbg(dmc->dev, "drex0 0xE050 val= 0x%08x\n", val); | ||
| 819 | } else { | ||
| 820 | val = readl(dmc->base_drexi1 + DREX_FLAG_PPC); | ||
| 821 | diff_ts = ts - dmc->last_overflow_ts[1]; | ||
| 822 | dmc->last_overflow_ts[1] = ts; | ||
| 823 | dev_dbg(dmc->dev, "drex1 0xE050 val= 0x%08x\n", val); | ||
| 824 | } | ||
| 825 | |||
| 826 | exynos5_dmc_perf_events_calc(dmc, diff_ts); | ||
| 827 | |||
| 828 | exynos5_dmc_start_perf_events(dmc, PERF_COUNTER_START_VALUE); | ||
| 829 | } | ||
| 830 | |||
| 831 | /** | ||
| 832 | * exynos5_dmc_enable_perf_events() - Enable performance events | ||
| 833 | * @dmc: device for which the counters are going to be checked | ||
| 834 | * | ||
| 835 | * Function which is setup needed environment and enables counters. | ||
| 836 | */ | ||
| 837 | static void exynos5_dmc_enable_perf_events(struct exynos5_dmc *dmc) | ||
| 838 | { | ||
| 839 | u64 ts; | ||
| 840 | |||
| 841 | /* Enable Performance Event Clock */ | ||
| 842 | writel(PEREV_CLK_EN, dmc->base_drexi0 + DREX_PPCCLKCON); | ||
| 843 | writel(PEREV_CLK_EN, dmc->base_drexi1 + DREX_PPCCLKCON); | ||
| 844 | |||
| 845 | /* Select read transfers as performance event2 */ | ||
| 846 | writel(READ_TRANSFER_CH0, dmc->base_drexi0 + DREX_PEREV2CONFIG); | ||
| 847 | writel(READ_TRANSFER_CH1, dmc->base_drexi1 + DREX_PEREV2CONFIG); | ||
| 848 | |||
| 849 | ts = ktime_get_ns(); | ||
| 850 | dmc->last_overflow_ts[0] = ts; | ||
| 851 | dmc->last_overflow_ts[1] = ts; | ||
| 852 | |||
| 853 | /* Devfreq shouldn't be faster than initialization, play safe though. */ | ||
| 854 | dmc->load = 99; | ||
| 855 | dmc->total = 100; | ||
| 856 | } | ||
| 857 | |||
| 858 | /** | ||
| 859 | * exynos5_dmc_disable_perf_events() - Disable performance events | ||
| 860 | * @dmc: device for which the counters are going to be checked | ||
| 861 | * | ||
| 862 | * Function which stops, disables performance event counters and interrupts. | ||
| 863 | */ | ||
| 864 | static void exynos5_dmc_disable_perf_events(struct exynos5_dmc *dmc) | ||
| 865 | { | ||
| 866 | /* Stop all counters */ | ||
| 867 | writel(0, dmc->base_drexi0 + DREX_PMNC_PPC); | ||
| 868 | writel(0, dmc->base_drexi1 + DREX_PMNC_PPC); | ||
| 869 | |||
| 870 | /* Disable interrupts for counter 2 */ | ||
| 871 | writel(PERF_CNT2, dmc->base_drexi0 + DREX_INTENC_PPC); | ||
| 872 | writel(PERF_CNT2, dmc->base_drexi1 + DREX_INTENC_PPC); | ||
| 873 | |||
| 874 | /* Disable counter 2 and CCNT */ | ||
| 875 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi0 + DREX_CNTENC_PPC); | ||
| 876 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi1 + DREX_CNTENC_PPC); | ||
| 877 | |||
| 878 | /* Clear overflow flag for all counters */ | ||
| 879 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi0 + DREX_FLAG_PPC); | ||
| 880 | writel(PERF_CNT2 | PERF_CCNT, dmc->base_drexi1 + DREX_FLAG_PPC); | ||
| 881 | } | ||
| 882 | |||
| 883 | /** | ||
| 884 | * exynos5_dmc_get_status() - Read current DMC performance statistics. | ||
| 885 | * @dev: device for which the statistics are requested | ||
| 886 | * @stat: structure which has statistic fields | ||
| 887 | * | ||
| 888 | * Function reads the DMC performance counters and calculates 'busy_time' | ||
| 889 | * and 'total_time'. To protect from overflow, the values are shifted right | ||
| 890 | * by 10. After read out the counters are setup to count again. | ||
| 891 | */ | ||
| 892 | static int exynos5_dmc_get_status(struct device *dev, | ||
| 893 | struct devfreq_dev_status *stat) | ||
| 894 | { | ||
| 895 | struct exynos5_dmc *dmc = dev_get_drvdata(dev); | ||
| 896 | unsigned long load, total; | ||
| 897 | int ret; | ||
| 898 | |||
| 899 | if (dmc->in_irq_mode) { | ||
| 900 | stat->current_frequency = dmc->curr_rate; | ||
| 901 | stat->busy_time = dmc->load; | ||
| 902 | stat->total_time = dmc->total; | ||
| 903 | } else { | ||
| 904 | ret = exynos5_counters_get(dmc, &load, &total); | ||
| 905 | if (ret < 0) | ||
| 906 | return -EINVAL; | ||
| 907 | |||
| 908 | /* To protect from overflow, divide by 1024 */ | ||
| 909 | stat->busy_time = load >> 10; | ||
| 910 | stat->total_time = total >> 10; | ||
| 911 | |||
| 912 | ret = exynos5_counters_set_event(dmc); | ||
| 913 | if (ret < 0) { | ||
| 914 | dev_err(dev, "could not set event counter\n"); | ||
| 915 | return ret; | ||
| 916 | } | ||
| 917 | } | ||
| 918 | |||
| 919 | return 0; | ||
| 920 | } | ||
| 921 | |||
| 922 | /** | ||
| 923 | * exynos5_dmc_get_cur_freq() - Function returns current DMC frequency | ||
| 924 | * @dev: device for which the framework checks operating frequency | ||
| 925 | * @freq: returned frequency value | ||
| 926 | * | ||
| 927 | * It returns the currently used frequency of the DMC. The real operating | ||
| 928 | * frequency might be lower when the clock source value could not be divided | ||
| 929 | * to the requested value. | ||
| 930 | */ | ||
| 931 | static int exynos5_dmc_get_cur_freq(struct device *dev, unsigned long *freq) | ||
| 932 | { | ||
| 933 | struct exynos5_dmc *dmc = dev_get_drvdata(dev); | ||
| 934 | |||
| 935 | mutex_lock(&dmc->lock); | ||
| 936 | *freq = dmc->curr_rate; | ||
| 937 | mutex_unlock(&dmc->lock); | ||
| 938 | |||
| 939 | return 0; | ||
| 940 | } | ||
| 941 | |||
| 942 | /** | ||
| 943 | * exynos5_dmc_df_profile - Devfreq governor's profile structure | ||
| 944 | * | ||
| 945 | * It provides to the devfreq framework needed functions and polling period. | ||
| 946 | */ | ||
| 947 | static struct devfreq_dev_profile exynos5_dmc_df_profile = { | ||
| 948 | .target = exynos5_dmc_target, | ||
| 949 | .get_dev_status = exynos5_dmc_get_status, | ||
| 950 | .get_cur_freq = exynos5_dmc_get_cur_freq, | ||
| 951 | }; | ||
| 952 | |||
| 953 | /** | ||
| 954 | * exynos5_dmc_align_initial_frequency() - Align initial frequency value | ||
| 955 | * @dmc: device for which the frequency is going to be set | ||
| 956 | * @bootloader_init_freq: initial frequency set by the bootloader in KHz | ||
| 957 | * | ||
| 958 | * The initial bootloader frequency, which is present during boot, might be | ||
| 959 | * different that supported frequency values in the driver. It is possible | ||
| 960 | * due to different PLL settings or used PLL as a source. | ||
| 961 | * This function provides the 'initial_freq' for the devfreq framework | ||
| 962 | * statistics engine which supports only registered values. Thus, some alignment | ||
| 963 | * must be made. | ||
| 964 | */ | ||
| 965 | static unsigned long | ||
| 966 | exynos5_dmc_align_init_freq(struct exynos5_dmc *dmc, | ||
| 967 | unsigned long bootloader_init_freq) | ||
| 968 | { | ||
| 969 | unsigned long aligned_freq; | ||
| 970 | int idx; | ||
| 971 | |||
| 972 | idx = find_target_freq_idx(dmc, bootloader_init_freq); | ||
| 973 | if (idx >= 0) | ||
| 974 | aligned_freq = dmc->opp[idx].freq_hz; | ||
| 975 | else | ||
| 976 | aligned_freq = dmc->opp[dmc->opp_count - 1].freq_hz; | ||
| 977 | |||
| 978 | return aligned_freq; | ||
| 979 | } | ||
| 980 | |||
| 981 | /** | ||
| 982 | * create_timings_aligned() - Create register values and align with standard | ||
| 983 | * @dmc: device for which the frequency is going to be set | ||
| 984 | * @idx: speed bin in the OPP table | ||
| 985 | * @clk_period_ps: the period of the clock, known as tCK | ||
| 986 | * | ||
| 987 | * The function calculates timings and creates a register value ready for | ||
| 988 | * a frequency transition. The register contains a few timings. They are | ||
| 989 | * shifted by a known offset. The timing value is calculated based on memory | ||
| 990 | * specyfication: minimal time required and minimal cycles required. | ||
| 991 | */ | ||
| 992 | static int create_timings_aligned(struct exynos5_dmc *dmc, u32 *reg_timing_row, | ||
| 993 | u32 *reg_timing_data, u32 *reg_timing_power, | ||
| 994 | u32 clk_period_ps) | ||
| 995 | { | ||
| 996 | u32 val; | ||
| 997 | const struct timing_reg *reg; | ||
| 998 | |||
| 999 | if (clk_period_ps == 0) | ||
| 1000 | return -EINVAL; | ||
| 1001 | |||
| 1002 | *reg_timing_row = 0; | ||
| 1003 | *reg_timing_data = 0; | ||
| 1004 | *reg_timing_power = 0; | ||
| 1005 | |||
| 1006 | val = dmc->timings->tRFC / clk_period_ps; | ||
| 1007 | val += dmc->timings->tRFC % clk_period_ps ? 1 : 0; | ||
| 1008 | val = max(val, dmc->min_tck->tRFC); | ||
| 1009 | reg = &timing_row[0]; | ||
| 1010 | *reg_timing_row |= TIMING_VAL2REG(reg, val); | ||
| 1011 | |||
| 1012 | val = dmc->timings->tRRD / clk_period_ps; | ||
| 1013 | val += dmc->timings->tRRD % clk_period_ps ? 1 : 0; | ||
| 1014 | val = max(val, dmc->min_tck->tRRD); | ||
| 1015 | reg = &timing_row[1]; | ||
| 1016 | *reg_timing_row |= TIMING_VAL2REG(reg, val); | ||
| 1017 | |||
| 1018 | val = dmc->timings->tRPab / clk_period_ps; | ||
| 1019 | val += dmc->timings->tRPab % clk_period_ps ? 1 : 0; | ||
| 1020 | val = max(val, dmc->min_tck->tRPab); | ||
| 1021 | reg = &timing_row[2]; | ||
| 1022 | *reg_timing_row |= TIMING_VAL2REG(reg, val); | ||
| 1023 | |||
| 1024 | val = dmc->timings->tRCD / clk_period_ps; | ||
| 1025 | val += dmc->timings->tRCD % clk_period_ps ? 1 : 0; | ||
| 1026 | val = max(val, dmc->min_tck->tRCD); | ||
| 1027 | reg = &timing_row[3]; | ||
| 1028 | *reg_timing_row |= TIMING_VAL2REG(reg, val); | ||
| 1029 | |||
| 1030 | val = dmc->timings->tRC / clk_period_ps; | ||
| 1031 | val += dmc->timings->tRC % clk_period_ps ? 1 : 0; | ||
| 1032 | val = max(val, dmc->min_tck->tRC); | ||
| 1033 | reg = &timing_row[4]; | ||
| 1034 | *reg_timing_row |= TIMING_VAL2REG(reg, val); | ||
| 1035 | |||
| 1036 | val = dmc->timings->tRAS / clk_period_ps; | ||
| 1037 | val += dmc->timings->tRAS % clk_period_ps ? 1 : 0; | ||
| 1038 | val = max(val, dmc->min_tck->tRAS); | ||
| 1039 | reg = &timing_row[5]; | ||
| 1040 | *reg_timing_row |= TIMING_VAL2REG(reg, val); | ||
| 1041 | |||
| 1042 | /* data related timings */ | ||
| 1043 | val = dmc->timings->tWTR / clk_period_ps; | ||
| 1044 | val += dmc->timings->tWTR % clk_period_ps ? 1 : 0; | ||
| 1045 | val = max(val, dmc->min_tck->tWTR); | ||
| 1046 | reg = &timing_data[0]; | ||
| 1047 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1048 | |||
| 1049 | val = dmc->timings->tWR / clk_period_ps; | ||
| 1050 | val += dmc->timings->tWR % clk_period_ps ? 1 : 0; | ||
| 1051 | val = max(val, dmc->min_tck->tWR); | ||
| 1052 | reg = &timing_data[1]; | ||
| 1053 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1054 | |||
| 1055 | val = dmc->timings->tRTP / clk_period_ps; | ||
| 1056 | val += dmc->timings->tRTP % clk_period_ps ? 1 : 0; | ||
| 1057 | val = max(val, dmc->min_tck->tRTP); | ||
| 1058 | reg = &timing_data[2]; | ||
| 1059 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1060 | |||
| 1061 | val = dmc->timings->tW2W_C2C / clk_period_ps; | ||
| 1062 | val += dmc->timings->tW2W_C2C % clk_period_ps ? 1 : 0; | ||
| 1063 | val = max(val, dmc->min_tck->tW2W_C2C); | ||
| 1064 | reg = &timing_data[3]; | ||
| 1065 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1066 | |||
| 1067 | val = dmc->timings->tR2R_C2C / clk_period_ps; | ||
| 1068 | val += dmc->timings->tR2R_C2C % clk_period_ps ? 1 : 0; | ||
| 1069 | val = max(val, dmc->min_tck->tR2R_C2C); | ||
| 1070 | reg = &timing_data[4]; | ||
| 1071 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1072 | |||
| 1073 | val = dmc->timings->tWL / clk_period_ps; | ||
| 1074 | val += dmc->timings->tWL % clk_period_ps ? 1 : 0; | ||
| 1075 | val = max(val, dmc->min_tck->tWL); | ||
| 1076 | reg = &timing_data[5]; | ||
| 1077 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1078 | |||
| 1079 | val = dmc->timings->tDQSCK / clk_period_ps; | ||
| 1080 | val += dmc->timings->tDQSCK % clk_period_ps ? 1 : 0; | ||
| 1081 | val = max(val, dmc->min_tck->tDQSCK); | ||
| 1082 | reg = &timing_data[6]; | ||
| 1083 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1084 | |||
| 1085 | val = dmc->timings->tRL / clk_period_ps; | ||
| 1086 | val += dmc->timings->tRL % clk_period_ps ? 1 : 0; | ||
| 1087 | val = max(val, dmc->min_tck->tRL); | ||
| 1088 | reg = &timing_data[7]; | ||
| 1089 | *reg_timing_data |= TIMING_VAL2REG(reg, val); | ||
| 1090 | |||
| 1091 | /* power related timings */ | ||
| 1092 | val = dmc->timings->tFAW / clk_period_ps; | ||
| 1093 | val += dmc->timings->tFAW % clk_period_ps ? 1 : 0; | ||
| 1094 | val = max(val, dmc->min_tck->tXP); | ||
| 1095 | reg = &timing_power[0]; | ||
| 1096 | *reg_timing_power |= TIMING_VAL2REG(reg, val); | ||
| 1097 | |||
| 1098 | val = dmc->timings->tXSR / clk_period_ps; | ||
| 1099 | val += dmc->timings->tXSR % clk_period_ps ? 1 : 0; | ||
| 1100 | val = max(val, dmc->min_tck->tXSR); | ||
| 1101 | reg = &timing_power[1]; | ||
| 1102 | *reg_timing_power |= TIMING_VAL2REG(reg, val); | ||
| 1103 | |||
| 1104 | val = dmc->timings->tXP / clk_period_ps; | ||
| 1105 | val += dmc->timings->tXP % clk_period_ps ? 1 : 0; | ||
| 1106 | val = max(val, dmc->min_tck->tXP); | ||
| 1107 | reg = &timing_power[2]; | ||
| 1108 | *reg_timing_power |= TIMING_VAL2REG(reg, val); | ||
| 1109 | |||
| 1110 | val = dmc->timings->tCKE / clk_period_ps; | ||
| 1111 | val += dmc->timings->tCKE % clk_period_ps ? 1 : 0; | ||
| 1112 | val = max(val, dmc->min_tck->tCKE); | ||
| 1113 | reg = &timing_power[3]; | ||
| 1114 | *reg_timing_power |= TIMING_VAL2REG(reg, val); | ||
| 1115 | |||
| 1116 | val = dmc->timings->tMRD / clk_period_ps; | ||
| 1117 | val += dmc->timings->tMRD % clk_period_ps ? 1 : 0; | ||
| 1118 | val = max(val, dmc->min_tck->tMRD); | ||
| 1119 | reg = &timing_power[4]; | ||
| 1120 | *reg_timing_power |= TIMING_VAL2REG(reg, val); | ||
| 1121 | |||
| 1122 | return 0; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | /** | ||
| 1126 | * of_get_dram_timings() - helper function for parsing DT settings for DRAM | ||
| 1127 | * @dmc: device for which the frequency is going to be set | ||
| 1128 | * | ||
| 1129 | * The function parses DT entries with DRAM information. | ||
| 1130 | */ | ||
| 1131 | static int of_get_dram_timings(struct exynos5_dmc *dmc) | ||
| 1132 | { | ||
| 1133 | int ret = 0; | ||
| 1134 | int idx; | ||
| 1135 | struct device_node *np_ddr; | ||
| 1136 | u32 freq_mhz, clk_period_ps; | ||
| 1137 | |||
| 1138 | np_ddr = of_parse_phandle(dmc->dev->of_node, "device-handle", 0); | ||
| 1139 | if (!np_ddr) { | ||
| 1140 | dev_warn(dmc->dev, "could not find 'device-handle' in DT\n"); | ||
| 1141 | return -EINVAL; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | dmc->timing_row = devm_kmalloc_array(dmc->dev, TIMING_COUNT, | ||
| 1145 | sizeof(u32), GFP_KERNEL); | ||
| 1146 | if (!dmc->timing_row) | ||
| 1147 | return -ENOMEM; | ||
| 1148 | |||
| 1149 | dmc->timing_data = devm_kmalloc_array(dmc->dev, TIMING_COUNT, | ||
| 1150 | sizeof(u32), GFP_KERNEL); | ||
| 1151 | if (!dmc->timing_data) | ||
| 1152 | return -ENOMEM; | ||
| 1153 | |||
| 1154 | dmc->timing_power = devm_kmalloc_array(dmc->dev, TIMING_COUNT, | ||
| 1155 | sizeof(u32), GFP_KERNEL); | ||
| 1156 | if (!dmc->timing_power) | ||
| 1157 | return -ENOMEM; | ||
| 1158 | |||
| 1159 | dmc->timings = of_lpddr3_get_ddr_timings(np_ddr, dmc->dev, | ||
| 1160 | DDR_TYPE_LPDDR3, | ||
| 1161 | &dmc->timings_arr_size); | ||
| 1162 | if (!dmc->timings) { | ||
| 1163 | of_node_put(np_ddr); | ||
| 1164 | dev_warn(dmc->dev, "could not get timings from DT\n"); | ||
| 1165 | return -EINVAL; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | dmc->min_tck = of_lpddr3_get_min_tck(np_ddr, dmc->dev); | ||
| 1169 | if (!dmc->min_tck) { | ||
| 1170 | of_node_put(np_ddr); | ||
| 1171 | dev_warn(dmc->dev, "could not get tck from DT\n"); | ||
| 1172 | return -EINVAL; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | /* Sorted array of OPPs with frequency ascending */ | ||
| 1176 | for (idx = 0; idx < dmc->opp_count; idx++) { | ||
| 1177 | freq_mhz = dmc->opp[idx].freq_hz / 1000000; | ||
| 1178 | clk_period_ps = 1000000 / freq_mhz; | ||
| 1179 | |||
| 1180 | ret = create_timings_aligned(dmc, &dmc->timing_row[idx], | ||
| 1181 | &dmc->timing_data[idx], | ||
| 1182 | &dmc->timing_power[idx], | ||
| 1183 | clk_period_ps); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | of_node_put(np_ddr); | ||
| 1187 | |||
| 1188 | /* Take the highest frequency's timings as 'bypass' */ | ||
| 1189 | dmc->bypass_timing_row = dmc->timing_row[idx - 1]; | ||
| 1190 | dmc->bypass_timing_data = dmc->timing_data[idx - 1]; | ||
| 1191 | dmc->bypass_timing_power = dmc->timing_power[idx - 1]; | ||
| 1192 | |||
| 1193 | return ret; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | /** | ||
| 1197 | * exynos5_dmc_init_clks() - Initialize clocks needed for DMC operation. | ||
| 1198 | * @dmc: DMC structure containing needed fields | ||
| 1199 | * | ||
| 1200 | * Get the needed clocks defined in DT device, enable and set the right parents. | ||
| 1201 | * Read current frequency and initialize the initial rate for governor. | ||
| 1202 | */ | ||
| 1203 | static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc) | ||
| 1204 | { | ||
| 1205 | int ret; | ||
| 1206 | unsigned long target_volt = 0; | ||
| 1207 | unsigned long target_rate = 0; | ||
| 1208 | unsigned int tmp; | ||
| 1209 | |||
| 1210 | dmc->fout_spll = devm_clk_get(dmc->dev, "fout_spll"); | ||
| 1211 | if (IS_ERR(dmc->fout_spll)) | ||
| 1212 | return PTR_ERR(dmc->fout_spll); | ||
| 1213 | |||
| 1214 | dmc->fout_bpll = devm_clk_get(dmc->dev, "fout_bpll"); | ||
| 1215 | if (IS_ERR(dmc->fout_bpll)) | ||
| 1216 | return PTR_ERR(dmc->fout_bpll); | ||
| 1217 | |||
| 1218 | dmc->mout_mclk_cdrex = devm_clk_get(dmc->dev, "mout_mclk_cdrex"); | ||
| 1219 | if (IS_ERR(dmc->mout_mclk_cdrex)) | ||
| 1220 | return PTR_ERR(dmc->mout_mclk_cdrex); | ||
| 1221 | |||
| 1222 | dmc->mout_bpll = devm_clk_get(dmc->dev, "mout_bpll"); | ||
| 1223 | if (IS_ERR(dmc->mout_bpll)) | ||
| 1224 | return PTR_ERR(dmc->mout_bpll); | ||
| 1225 | |||
| 1226 | dmc->mout_mx_mspll_ccore = devm_clk_get(dmc->dev, | ||
| 1227 | "mout_mx_mspll_ccore"); | ||
| 1228 | if (IS_ERR(dmc->mout_mx_mspll_ccore)) | ||
| 1229 | return PTR_ERR(dmc->mout_mx_mspll_ccore); | ||
| 1230 | |||
| 1231 | dmc->mout_spll = devm_clk_get(dmc->dev, "ff_dout_spll2"); | ||
| 1232 | if (IS_ERR(dmc->mout_spll)) { | ||
| 1233 | dmc->mout_spll = devm_clk_get(dmc->dev, "mout_sclk_spll"); | ||
| 1234 | if (IS_ERR(dmc->mout_spll)) | ||
| 1235 | return PTR_ERR(dmc->mout_spll); | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | /* | ||
| 1239 | * Convert frequency to KHz values and set it for the governor. | ||
| 1240 | */ | ||
| 1241 | dmc->curr_rate = clk_get_rate(dmc->mout_mclk_cdrex); | ||
| 1242 | dmc->curr_rate = exynos5_dmc_align_init_freq(dmc, dmc->curr_rate); | ||
| 1243 | exynos5_dmc_df_profile.initial_freq = dmc->curr_rate; | ||
| 1244 | |||
| 1245 | ret = exynos5_dmc_get_volt_freq(dmc, &dmc->curr_rate, &target_rate, | ||
| 1246 | &target_volt, 0); | ||
| 1247 | if (ret) | ||
| 1248 | return ret; | ||
| 1249 | |||
| 1250 | dmc->curr_volt = target_volt; | ||
| 1251 | |||
| 1252 | clk_set_parent(dmc->mout_mx_mspll_ccore, dmc->mout_spll); | ||
| 1253 | |||
| 1254 | dmc->bypass_rate = clk_get_rate(dmc->mout_mx_mspll_ccore); | ||
| 1255 | |||
| 1256 | clk_prepare_enable(dmc->fout_bpll); | ||
| 1257 | clk_prepare_enable(dmc->mout_bpll); | ||
| 1258 | |||
| 1259 | /* | ||
| 1260 | * Some bootloaders do not set clock routes correctly. | ||
| 1261 | * Stop one path in clocks to PHY. | ||
| 1262 | */ | ||
| 1263 | regmap_read(dmc->clk_regmap, CDREX_LPDDR3PHY_CLKM_SRC, &tmp); | ||
| 1264 | tmp &= ~(BIT(1) | BIT(0)); | ||
| 1265 | regmap_write(dmc->clk_regmap, CDREX_LPDDR3PHY_CLKM_SRC, tmp); | ||
| 1266 | |||
| 1267 | return 0; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | /** | ||
| 1271 | * exynos5_performance_counters_init() - Initializes performance DMC's counters | ||
| 1272 | * @dmc: DMC for which it does the setup | ||
| 1273 | * | ||
| 1274 | * Initialization of performance counters in DMC for estimating usage. | ||
| 1275 | * The counter's values are used for calculation of a memory bandwidth and based | ||
| 1276 | * on that the governor changes the frequency. | ||
| 1277 | * The counters are not used when the governor is GOVERNOR_USERSPACE. | ||
| 1278 | */ | ||
| 1279 | static int exynos5_performance_counters_init(struct exynos5_dmc *dmc) | ||
| 1280 | { | ||
| 1281 | int counters_size; | ||
| 1282 | int ret, i; | ||
| 1283 | |||
| 1284 | dmc->num_counters = devfreq_event_get_edev_count(dmc->dev); | ||
| 1285 | if (dmc->num_counters < 0) { | ||
| 1286 | dev_err(dmc->dev, "could not get devfreq-event counters\n"); | ||
| 1287 | return dmc->num_counters; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | counters_size = sizeof(struct devfreq_event_dev) * dmc->num_counters; | ||
| 1291 | dmc->counter = devm_kzalloc(dmc->dev, counters_size, GFP_KERNEL); | ||
| 1292 | if (!dmc->counter) | ||
| 1293 | return -ENOMEM; | ||
| 1294 | |||
| 1295 | for (i = 0; i < dmc->num_counters; i++) { | ||
| 1296 | dmc->counter[i] = | ||
| 1297 | devfreq_event_get_edev_by_phandle(dmc->dev, i); | ||
| 1298 | if (IS_ERR_OR_NULL(dmc->counter[i])) | ||
| 1299 | return -EPROBE_DEFER; | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | ret = exynos5_counters_enable_edev(dmc); | ||
| 1303 | if (ret < 0) { | ||
| 1304 | dev_err(dmc->dev, "could not enable event counter\n"); | ||
| 1305 | return ret; | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | ret = exynos5_counters_set_event(dmc); | ||
| 1309 | if (ret < 0) { | ||
| 1310 | exynos5_counters_disable_edev(dmc); | ||
| 1311 | dev_err(dmc->dev, "could not set event counter\n"); | ||
| 1312 | return ret; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | return 0; | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | /** | ||
| 1319 | * exynos5_dmc_set_pause_on_switching() - Controls a pause feature in DMC | ||
| 1320 | * @dmc: device which is used for changing this feature | ||
| 1321 | * @set: a boolean state passing enable/disable request | ||
| 1322 | * | ||
| 1323 | * There is a need of pausing DREX DMC when divider or MUX in clock tree | ||
| 1324 | * changes its configuration. In such situation access to the memory is blocked | ||
| 1325 | * in DMC automatically. This feature is used when clock frequency change | ||
| 1326 | * request appears and touches clock tree. | ||
| 1327 | */ | ||
| 1328 | static inline int exynos5_dmc_set_pause_on_switching(struct exynos5_dmc *dmc) | ||
| 1329 | { | ||
| 1330 | unsigned int val; | ||
| 1331 | int ret; | ||
| 1332 | |||
| 1333 | ret = regmap_read(dmc->clk_regmap, CDREX_PAUSE, &val); | ||
| 1334 | if (ret) | ||
| 1335 | return ret; | ||
| 1336 | |||
| 1337 | val |= 1UL; | ||
| 1338 | regmap_write(dmc->clk_regmap, CDREX_PAUSE, val); | ||
| 1339 | |||
| 1340 | return 0; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | static irqreturn_t dmc_irq_thread(int irq, void *priv) | ||
| 1344 | { | ||
| 1345 | int res; | ||
| 1346 | struct exynos5_dmc *dmc = priv; | ||
| 1347 | |||
| 1348 | mutex_lock(&dmc->df->lock); | ||
| 1349 | |||
| 1350 | exynos5_dmc_perf_events_check(dmc); | ||
| 1351 | |||
| 1352 | res = update_devfreq(dmc->df); | ||
| 1353 | if (res) | ||
| 1354 | dev_warn(dmc->dev, "devfreq failed with %d\n", res); | ||
| 1355 | |||
| 1356 | mutex_unlock(&dmc->df->lock); | ||
| 1357 | |||
| 1358 | return IRQ_HANDLED; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | /** | ||
| 1362 | * exynos5_dmc_probe() - Probe function for the DMC driver | ||
| 1363 | * @pdev: platform device for which the driver is going to be initialized | ||
| 1364 | * | ||
| 1365 | * Initialize basic components: clocks, regulators, performance counters, etc. | ||
| 1366 | * Read out product version and based on the information setup | ||
| 1367 | * internal structures for the controller (frequency and voltage) and for DRAM | ||
| 1368 | * memory parameters: timings for each operating frequency. | ||
| 1369 | * Register new devfreq device for controlling DVFS of the DMC. | ||
| 1370 | */ | ||
| 1371 | static int exynos5_dmc_probe(struct platform_device *pdev) | ||
| 1372 | { | ||
| 1373 | int ret = 0; | ||
| 1374 | struct device *dev = &pdev->dev; | ||
| 1375 | struct device_node *np = dev->of_node; | ||
| 1376 | struct exynos5_dmc *dmc; | ||
| 1377 | struct resource *res; | ||
| 1378 | int irq[2]; | ||
| 1379 | |||
| 1380 | dmc = devm_kzalloc(dev, sizeof(*dmc), GFP_KERNEL); | ||
| 1381 | if (!dmc) | ||
| 1382 | return -ENOMEM; | ||
| 1383 | |||
| 1384 | mutex_init(&dmc->lock); | ||
| 1385 | |||
| 1386 | dmc->dev = dev; | ||
| 1387 | platform_set_drvdata(pdev, dmc); | ||
| 1388 | |||
| 1389 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 1390 | dmc->base_drexi0 = devm_ioremap_resource(dev, res); | ||
| 1391 | if (IS_ERR(dmc->base_drexi0)) | ||
| 1392 | return PTR_ERR(dmc->base_drexi0); | ||
| 1393 | |||
| 1394 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 1395 | dmc->base_drexi1 = devm_ioremap_resource(dev, res); | ||
| 1396 | if (IS_ERR(dmc->base_drexi1)) | ||
| 1397 | return PTR_ERR(dmc->base_drexi1); | ||
| 1398 | |||
| 1399 | dmc->clk_regmap = syscon_regmap_lookup_by_phandle(np, | ||
| 1400 | "samsung,syscon-clk"); | ||
| 1401 | if (IS_ERR(dmc->clk_regmap)) | ||
| 1402 | return PTR_ERR(dmc->clk_regmap); | ||
| 1403 | |||
| 1404 | ret = exynos5_init_freq_table(dmc, &exynos5_dmc_df_profile); | ||
| 1405 | if (ret) { | ||
| 1406 | dev_warn(dev, "couldn't initialize frequency settings\n"); | ||
| 1407 | return ret; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | dmc->vdd_mif = devm_regulator_get(dev, "vdd"); | ||
| 1411 | if (IS_ERR(dmc->vdd_mif)) { | ||
| 1412 | ret = PTR_ERR(dmc->vdd_mif); | ||
| 1413 | return ret; | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | ret = exynos5_dmc_init_clks(dmc); | ||
| 1417 | if (ret) | ||
| 1418 | return ret; | ||
| 1419 | |||
| 1420 | ret = of_get_dram_timings(dmc); | ||
| 1421 | if (ret) { | ||
| 1422 | dev_warn(dev, "couldn't initialize timings settings\n"); | ||
| 1423 | goto remove_clocks; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | ret = exynos5_dmc_set_pause_on_switching(dmc); | ||
| 1427 | if (ret) { | ||
| 1428 | dev_warn(dev, "couldn't get access to PAUSE register\n"); | ||
| 1429 | goto remove_clocks; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | /* There is two modes in which the driver works: polling or IRQ */ | ||
| 1433 | irq[0] = platform_get_irq_byname(pdev, "drex_0"); | ||
| 1434 | irq[1] = platform_get_irq_byname(pdev, "drex_1"); | ||
| 1435 | if (irq[0] > 0 && irq[1] > 0) { | ||
| 1436 | ret = devm_request_threaded_irq(dev, irq[0], NULL, | ||
| 1437 | dmc_irq_thread, IRQF_ONESHOT, | ||
| 1438 | dev_name(dev), dmc); | ||
| 1439 | if (ret) { | ||
| 1440 | dev_err(dev, "couldn't grab IRQ\n"); | ||
| 1441 | goto remove_clocks; | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | ret = devm_request_threaded_irq(dev, irq[1], NULL, | ||
| 1445 | dmc_irq_thread, IRQF_ONESHOT, | ||
| 1446 | dev_name(dev), dmc); | ||
| 1447 | if (ret) { | ||
| 1448 | dev_err(dev, "couldn't grab IRQ\n"); | ||
| 1449 | goto remove_clocks; | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | /* | ||
| 1453 | * Setup default thresholds for the devfreq governor. | ||
| 1454 | * The values are chosen based on experiments. | ||
| 1455 | */ | ||
| 1456 | dmc->gov_data.upthreshold = 55; | ||
| 1457 | dmc->gov_data.downdifferential = 5; | ||
| 1458 | |||
| 1459 | exynos5_dmc_enable_perf_events(dmc); | ||
| 1460 | |||
| 1461 | dmc->in_irq_mode = 1; | ||
| 1462 | } else { | ||
| 1463 | ret = exynos5_performance_counters_init(dmc); | ||
| 1464 | if (ret) { | ||
| 1465 | dev_warn(dev, "couldn't probe performance counters\n"); | ||
| 1466 | goto remove_clocks; | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | /* | ||
| 1470 | * Setup default thresholds for the devfreq governor. | ||
| 1471 | * The values are chosen based on experiments. | ||
| 1472 | */ | ||
| 1473 | dmc->gov_data.upthreshold = 30; | ||
| 1474 | dmc->gov_data.downdifferential = 5; | ||
| 1475 | |||
| 1476 | exynos5_dmc_df_profile.polling_ms = 500; | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | |||
| 1480 | dmc->df = devm_devfreq_add_device(dev, &exynos5_dmc_df_profile, | ||
| 1481 | DEVFREQ_GOV_SIMPLE_ONDEMAND, | ||
| 1482 | &dmc->gov_data); | ||
| 1483 | |||
| 1484 | if (IS_ERR(dmc->df)) { | ||
| 1485 | ret = PTR_ERR(dmc->df); | ||
| 1486 | goto err_devfreq_add; | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | if (dmc->in_irq_mode) | ||
| 1490 | exynos5_dmc_start_perf_events(dmc, PERF_COUNTER_START_VALUE); | ||
| 1491 | |||
| 1492 | dev_info(dev, "DMC initialized\n"); | ||
| 1493 | |||
| 1494 | return 0; | ||
| 1495 | |||
| 1496 | err_devfreq_add: | ||
| 1497 | if (dmc->in_irq_mode) | ||
| 1498 | exynos5_dmc_disable_perf_events(dmc); | ||
| 1499 | else | ||
| 1500 | exynos5_counters_disable_edev(dmc); | ||
| 1501 | remove_clocks: | ||
| 1502 | clk_disable_unprepare(dmc->mout_bpll); | ||
| 1503 | clk_disable_unprepare(dmc->fout_bpll); | ||
| 1504 | |||
| 1505 | return ret; | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | /** | ||
| 1509 | * exynos5_dmc_remove() - Remove function for the platform device | ||
| 1510 | * @pdev: platform device which is going to be removed | ||
| 1511 | * | ||
| 1512 | * The function relies on 'devm' framework function which automatically | ||
| 1513 | * clean the device's resources. It just calls explicitly disable function for | ||
| 1514 | * the performance counters. | ||
| 1515 | */ | ||
| 1516 | static int exynos5_dmc_remove(struct platform_device *pdev) | ||
| 1517 | { | ||
| 1518 | struct exynos5_dmc *dmc = dev_get_drvdata(&pdev->dev); | ||
| 1519 | |||
| 1520 | if (dmc->in_irq_mode) | ||
| 1521 | exynos5_dmc_disable_perf_events(dmc); | ||
| 1522 | else | ||
| 1523 | exynos5_counters_disable_edev(dmc); | ||
| 1524 | |||
| 1525 | clk_disable_unprepare(dmc->mout_bpll); | ||
| 1526 | clk_disable_unprepare(dmc->fout_bpll); | ||
| 1527 | |||
| 1528 | dev_pm_opp_remove_table(dmc->dev); | ||
| 1529 | |||
| 1530 | return 0; | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | static const struct of_device_id exynos5_dmc_of_match[] = { | ||
| 1534 | { .compatible = "samsung,exynos5422-dmc", }, | ||
| 1535 | { }, | ||
| 1536 | }; | ||
| 1537 | MODULE_DEVICE_TABLE(of, exynos5_dmc_of_match); | ||
| 1538 | |||
| 1539 | static struct platform_driver exynos5_dmc_platdrv = { | ||
| 1540 | .probe = exynos5_dmc_probe, | ||
| 1541 | .remove = exynos5_dmc_remove, | ||
| 1542 | .driver = { | ||
| 1543 | .name = "exynos5-dmc", | ||
| 1544 | .of_match_table = exynos5_dmc_of_match, | ||
| 1545 | }, | ||
| 1546 | }; | ||
| 1547 | module_platform_driver(exynos5_dmc_platdrv); | ||
| 1548 | MODULE_DESCRIPTION("Driver for Exynos5422 Dynamic Memory Controller dynamic frequency and voltage change"); | ||
| 1549 | MODULE_LICENSE("GPL v2"); | ||
| 1550 | MODULE_AUTHOR("Lukasz Luba"); | ||
| @@ -17,6 +17,16 @@ config TEGRA20_EMC · 16 unmodified lines | |||
| 17 | This driver is required to change memory timings / clock rate for | 17 | This driver is required to change memory timings / clock rate for |
| 18 | external memory. | 18 | external memory. |
| 19 | 19 | ||
| 20 | config TEGRA30_EMC | ||
| 21 | bool "NVIDIA Tegra30 External Memory Controller driver" | ||
| 22 | default y | ||
| 23 | depends on TEGRA_MC && ARCH_TEGRA_3x_SOC | ||
| 24 | help | ||
| 25 | This driver is for the External Memory Controller (EMC) found on | ||
| 26 | Tegra30 chips. The EMC controls the external DRAM on the board. | ||
| 27 | This driver is required to change memory timings / clock rate for | ||
| 28 | external memory. | ||
| 29 | |||
| 20 | config TEGRA124_EMC | 30 | config TEGRA124_EMC |
| 21 | bool "NVIDIA Tegra124 External Memory Controller driver" | 31 | bool "NVIDIA Tegra124 External Memory Controller driver" |
| 22 | default y | 32 | default y |
| @@ -11,5 +11,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o · 10 unmodified lines | |||
| 11 | obj-$(CONFIG_TEGRA_MC) += tegra-mc.o | 11 | obj-$(CONFIG_TEGRA_MC) += tegra-mc.o |
| 12 | 12 | ||
| 13 | obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o | 13 | obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o |
| 14 | obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o | ||
| 14 | obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o | 15 | obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o |
| 15 | obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o | 16 | obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | #include <linux/clk.h> | 6 | #include <linux/clk.h> |
| 7 | #include <linux/delay.h> | 7 | #include <linux/delay.h> |
| 8 | #include <linux/dma-mapping.h> | ||
| 8 | #include <linux/interrupt.h> | 9 | #include <linux/interrupt.h> |
| 9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 10 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| @@ -18,39 +19,6 @@ · 7 unmodified lines | |||
| 18 | 19 | ||
| 19 | #include "mc.h" | 20 | #include "mc.h" |
| 20 | 21 | ||
| 21 | #define MC_INTSTATUS 0x000 | ||
| 22 | |||
| 23 | #define MC_INTMASK 0x004 | ||
| 24 | |||
| 25 | #define MC_ERR_STATUS 0x08 | ||
| 26 | #define MC_ERR_STATUS_TYPE_SHIFT 28 | ||
| 27 | #define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT) | ||
| 28 | #define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT) | ||
| 29 | #define MC_ERR_STATUS_READABLE (1 << 27) | ||
| 30 | #define MC_ERR_STATUS_WRITABLE (1 << 26) | ||
| 31 | #define MC_ERR_STATUS_NONSECURE (1 << 25) | ||
| 32 | #define MC_ERR_STATUS_ADR_HI_SHIFT 20 | ||
| 33 | #define MC_ERR_STATUS_ADR_HI_MASK 0x3 | ||
| 34 | #define MC_ERR_STATUS_SECURITY (1 << 17) | ||
| 35 | #define MC_ERR_STATUS_RW (1 << 16) | ||
| 36 | |||
| 37 | #define MC_ERR_ADR 0x0c | ||
| 38 | |||
| 39 | #define MC_GART_ERROR_REQ 0x30 | ||
| 40 | #define MC_DECERR_EMEM_OTHERS_STATUS 0x58 | ||
| 41 | #define MC_SECURITY_VIOLATION_STATUS 0x74 | ||
| 42 | |||
| 43 | #define MC_EMEM_ARB_CFG 0x90 | ||
| 44 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0) | ||
| 45 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff | ||
| 46 | #define MC_EMEM_ARB_MISC0 0xd8 | ||
| 47 | |||
| 48 | #define MC_EMEM_ADR_CFG 0x54 | ||
| 49 | #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) | ||
| 50 | |||
| 51 | #define MC_TIMING_CONTROL 0xfc | ||
| 52 | #define MC_TIMING_UPDATE BIT(0) | ||
| 53 | |||
| 54 | static const struct of_device_id tegra_mc_of_match[] = { | 22 | static const struct of_device_id tegra_mc_of_match[] = { |
| 55 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | 23 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| 56 | { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, | 24 | { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, |
| @@ -307,7 +275,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) · 250 unmodified lines | |||
| 307 | return 0; | 275 | return 0; |
| 308 | } | 276 | } |
| 309 | 277 | ||
| 310 | void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) | 278 | int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) |
| 311 | { | 279 | { |
| 312 | unsigned int i; | 280 | unsigned int i; |
| 313 | struct tegra_mc_timing *timing = NULL; | 281 | struct tegra_mc_timing *timing = NULL; |
| @@ -322,11 +290,13 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) · 8 unmodified lines | |||
| 322 | if (!timing) { | 290 | if (!timing) { |
| 323 | dev_err(mc->dev, "no memory timing registered for rate %lu\n", | 291 | dev_err(mc->dev, "no memory timing registered for rate %lu\n", |
| 324 | rate); | 292 | rate); |
| 325 | return; | 293 | return -EINVAL; |
| 326 | } | 294 | } |
| 327 | 295 | ||
| 328 | for (i = 0; i < mc->soc->num_emem_regs; ++i) | 296 | for (i = 0; i < mc->soc->num_emem_regs; ++i) |
| 329 | mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]); | 297 | mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]); |
| 298 | |||
| 299 | return 0; | ||
| 330 | } | 300 | } |
| 331 | 301 | ||
| 332 | unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) | 302 | 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) · 293 unmodified lines | |||
| 626 | struct resource *res; | 596 | struct resource *res; |
| 627 | struct tegra_mc *mc; | 597 | struct tegra_mc *mc; |
| 628 | void *isr; | 598 | void *isr; |
| 599 | u64 mask; | ||
| 629 | int err; | 600 | int err; |
| 630 | 601 | ||
| 631 | mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); | 602 | mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); |
| @@ -637,6 +608,14 @@ static int tegra_mc_probe(struct platform_device *pdev) · 5 unmodified lines | |||
| 637 | mc->soc = of_device_get_match_data(&pdev->dev); | 608 | mc->soc = of_device_get_match_data(&pdev->dev); |
| 638 | mc->dev = &pdev->dev; | 609 | mc->dev = &pdev->dev; |
| 639 | 610 | ||
| 611 | mask = DMA_BIT_MASK(mc->soc->num_address_bits); | ||
| 612 | |||
| 613 | err = dma_coerce_mask_and_coherent(&pdev->dev, mask); | ||
| 614 | if (err < 0) { | ||
| 615 | dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); | ||
| 616 | return err; | ||
| 617 | } | ||
| 618 | |||
| 640 | /* length of MC tick in nanoseconds */ | 619 | /* length of MC tick in nanoseconds */ |
| 641 | mc->tick = 30; | 620 | mc->tick = 30; |
| 642 | 621 | ||
| @@ -658,6 +637,9 @@ static int tegra_mc_probe(struct platform_device *pdev) · 15 unmodified lines | |||
| 658 | } else | 637 | } else |
| 659 | #endif | 638 | #endif |
| 660 | { | 639 | { |
| 640 | /* ensure that debug features are disabled */ | ||
| 641 | mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG); | ||
| 642 | |||
| 661 | err = tegra_mc_setup_latency_allowance(mc); | 643 | err = tegra_mc_setup_latency_allowance(mc); |
| 662 | if (err < 0) { | 644 | if (err < 0) { |
| 663 | dev_err(&pdev->dev, | 645 | dev_err(&pdev->dev, |
| @@ -6,20 +6,76 @@ · 5 unmodified lines | |||
| 6 | #ifndef MEMORY_TEGRA_MC_H | 6 | #ifndef MEMORY_TEGRA_MC_H |
| 7 | #define MEMORY_TEGRA_MC_H | 7 | #define MEMORY_TEGRA_MC_H |
| 8 | 8 | ||
| 9 | #include <linux/bits.h> | ||
| 9 | #include <linux/io.h> | 10 | #include <linux/io.h> |
| 10 | #include <linux/types.h> | 11 | #include <linux/types.h> |
| 11 | 12 | ||
| 12 | #include <soc/tegra/mc.h> | 13 | #include <soc/tegra/mc.h> |
| 13 | 14 | ||
| 14 | #define MC_INT_DECERR_MTS (1 << 16) | 15 | #define MC_INTSTATUS 0x00 |
| 15 | #define MC_INT_SECERR_SEC (1 << 13) | 16 | #define MC_INTMASK 0x04 |
| 16 | #define MC_INT_DECERR_VPR (1 << 12) | 17 | #define MC_ERR_STATUS 0x08 |
| 17 | #define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) | 18 | #define MC_ERR_ADR 0x0c |
| 18 | #define MC_INT_INVALID_SMMU_PAGE (1 << 10) | 19 | #define MC_GART_ERROR_REQ 0x30 |
| 19 | #define MC_INT_ARBITRATION_EMEM (1 << 9) | 20 | #define MC_EMEM_ADR_CFG 0x54 |
| 20 | #define MC_INT_SECURITY_VIOLATION (1 << 8) | 21 | #define MC_DECERR_EMEM_OTHERS_STATUS 0x58 |
| 21 | #define MC_INT_INVALID_GART_PAGE (1 << 7) | 22 | #define MC_SECURITY_VIOLATION_STATUS 0x74 |
| 22 | #define MC_INT_DECERR_EMEM (1 << 6) | 23 | #define MC_EMEM_ARB_CFG 0x90 |
| 24 | #define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 | ||
| 25 | #define MC_EMEM_ARB_TIMING_RCD 0x98 | ||
| 26 | #define MC_EMEM_ARB_TIMING_RP 0x9c | ||
| 27 | #define MC_EMEM_ARB_TIMING_RC 0xa0 | ||
| 28 | #define MC_EMEM_ARB_TIMING_RAS 0xa4 | ||
| 29 | #define MC_EMEM_ARB_TIMING_FAW 0xa8 | ||
| 30 | #define MC_EMEM_ARB_TIMING_RRD 0xac | ||
| 31 | #define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 | ||
| 32 | #define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 | ||
| 33 | #define MC_EMEM_ARB_TIMING_R2R 0xb8 | ||
| 34 | #define MC_EMEM_ARB_TIMING_W2W 0xbc | ||
| 35 | #define MC_EMEM_ARB_TIMING_R2W 0xc0 | ||
| 36 | #define MC_EMEM_ARB_TIMING_W2R 0xc4 | ||
| 37 | #define MC_EMEM_ARB_DA_TURNS 0xd0 | ||
| 38 | #define MC_EMEM_ARB_DA_COVERS 0xd4 | ||
| 39 | #define MC_EMEM_ARB_MISC0 0xd8 | ||
| 40 | #define MC_EMEM_ARB_MISC1 0xdc | ||
| 41 | #define MC_EMEM_ARB_RING1_THROTTLE 0xe0 | ||
| 42 | #define MC_EMEM_ARB_OVERRIDE 0xe8 | ||
| 43 | #define MC_TIMING_CONTROL_DBG 0xf8 | ||
| 44 | #define MC_TIMING_CONTROL 0xfc | ||
| 45 | |||
| 46 | #define MC_INT_DECERR_MTS BIT(16) | ||
| 47 | #define MC_INT_SECERR_SEC BIT(13) | ||
| 48 | #define MC_INT_DECERR_VPR BIT(12) | ||
| 49 | #define MC_INT_INVALID_APB_ASID_UPDATE BIT(11) | ||
| 50 | #define MC_INT_INVALID_SMMU_PAGE BIT(10) | ||
| 51 | #define MC_INT_ARBITRATION_EMEM BIT(9) | ||
| 52 | #define MC_INT_SECURITY_VIOLATION BIT(8) | ||
| 53 | #define MC_INT_INVALID_GART_PAGE BIT(7) | ||
| 54 | #define MC_INT_DECERR_EMEM BIT(6) | ||
| 55 | |||
| 56 | #define MC_ERR_STATUS_TYPE_SHIFT 28 | ||
| 57 | #define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28) | ||
| 58 | #define MC_ERR_STATUS_TYPE_MASK (0x7 << 28) | ||
| 59 | #define MC_ERR_STATUS_READABLE BIT(27) | ||
| 60 | #define MC_ERR_STATUS_WRITABLE BIT(26) | ||
| 61 | #define MC_ERR_STATUS_NONSECURE BIT(25) | ||
| 62 | #define MC_ERR_STATUS_ADR_HI_SHIFT 20 | ||
| 63 | #define MC_ERR_STATUS_ADR_HI_MASK 0x3 | ||
| 64 | #define MC_ERR_STATUS_SECURITY BIT(17) | ||
| 65 | #define MC_ERR_STATUS_RW BIT(16) | ||
| 66 | |||
| 67 | #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) | ||
| 68 | |||
| 69 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff) | ||
| 70 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff | ||
| 71 | |||
| 72 | #define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff | ||
| 73 | #define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30) | ||
| 74 | #define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31) | ||
| 75 | |||
| 76 | #define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3 | ||
| 77 | |||
| 78 | #define MC_TIMING_UPDATE BIT(0) | ||
| 23 | 79 | ||
| 24 | static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) | 80 | static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) |
| 25 | { | 81 | { |
| @@ -909,16 +909,18 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = { · 908 unmodified lines | |||
| 909 | { .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, | 909 | { .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, |
| 910 | }; | 910 | }; |
| 911 | 911 | ||
| 912 | static const unsigned int tegra114_group_display[] = { | 912 | static const unsigned int tegra114_group_drm[] = { |
| 913 | TEGRA_SWGROUP_DC, | 913 | TEGRA_SWGROUP_DC, |
| 914 | TEGRA_SWGROUP_DCB, | 914 | TEGRA_SWGROUP_DCB, |
| 915 | TEGRA_SWGROUP_G2, | ||
| 916 | TEGRA_SWGROUP_NV, | ||
| 915 | }; | 917 | }; |
| 916 | 918 | ||
| 917 | static const struct tegra_smmu_group_soc tegra114_groups[] = { | 919 | static const struct tegra_smmu_group_soc tegra114_groups[] = { |
| 918 | { | 920 | { |
| 919 | .name = "display", | 921 | .name = "drm", |
| 920 | .swgroups = tegra114_group_display, | 922 | .swgroups = tegra114_group_drm, |
| 921 | .num_swgroups = ARRAY_SIZE(tegra114_group_display), | 923 | .num_swgroups = ARRAY_SIZE(tegra114_group_drm), |
| 922 | }, | 924 | }, |
| 923 | }; | 925 | }; |
| 924 | 926 | ||
| @@ -10,26 +10,6 @@ · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | #include "mc.h" | 11 | #include "mc.h" |
| 12 | 12 | ||
| 13 | #define MC_EMEM_ARB_CFG 0x90 | ||
| 14 | #define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 | ||
| 15 | #define MC_EMEM_ARB_TIMING_RCD 0x98 | ||
| 16 | #define MC_EMEM_ARB_TIMING_RP 0x9c | ||
| 17 | #define MC_EMEM_ARB_TIMING_RC 0xa0 | ||
| 18 | #define MC_EMEM_ARB_TIMING_RAS 0xa4 | ||
| 19 | #define MC_EMEM_ARB_TIMING_FAW 0xa8 | ||
| 20 | #define MC_EMEM_ARB_TIMING_RRD 0xac | ||
| 21 | #define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 | ||
| 22 | #define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 | ||
| 23 | #define MC_EMEM_ARB_TIMING_R2R 0xb8 | ||
| 24 | #define MC_EMEM_ARB_TIMING_W2W 0xbc | ||
| 25 | #define MC_EMEM_ARB_TIMING_R2W 0xc0 | ||
| 26 | #define MC_EMEM_ARB_TIMING_W2R 0xc4 | ||
| 27 | #define MC_EMEM_ARB_DA_TURNS 0xd0 | ||
| 28 | #define MC_EMEM_ARB_DA_COVERS 0xd4 | ||
| 29 | #define MC_EMEM_ARB_MISC0 0xd8 | ||
| 30 | #define MC_EMEM_ARB_MISC1 0xdc | ||
| 31 | #define MC_EMEM_ARB_RING1_THROTTLE 0xe0 | ||
| 32 | |||
| 33 | static const struct tegra_mc_client tegra124_mc_clients[] = { | 13 | static const struct tegra_mc_client tegra124_mc_clients[] = { |
| 34 | { | 14 | { |
| 35 | .id = 0x00, | 15 | .id = 0x00, |
| @@ -974,16 +954,18 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = { · 938 unmodified lines | |||
| 974 | { .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, | 954 | { .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, |
| 975 | }; | 955 | }; |
| 976 | 956 | ||
| 977 | static const unsigned int tegra124_group_display[] = { | 957 | static const unsigned int tegra124_group_drm[] = { |
| 978 | TEGRA_SWGROUP_DC, | 958 | TEGRA_SWGROUP_DC, |
| 979 | TEGRA_SWGROUP_DCB, | 959 | TEGRA_SWGROUP_DCB, |
| 960 | TEGRA_SWGROUP_GPU, | ||
| 961 | TEGRA_SWGROUP_VIC, | ||
| 980 | }; | 962 | }; |
| 981 | 963 | ||
| 982 | static const struct tegra_smmu_group_soc tegra124_groups[] = { | 964 | static const struct tegra_smmu_group_soc tegra124_groups[] = { |
| 983 | { | 965 | { |
| 984 | .name = "display", | 966 | .name = "drm", |
| 985 | .swgroups = tegra124_group_display, | 967 | .swgroups = tegra124_group_drm, |
| 986 | .num_swgroups = ARRAY_SIZE(tegra124_group_display), | 968 | .num_swgroups = ARRAY_SIZE(tegra124_group_drm), |
| 987 | }, | 969 | }, |
| 988 | }; | 970 | }; |
| 989 | 971 | ||
| @@ -6,10 +6,11 @@ · 5 unmodified lines | |||
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/clk.h> | 8 | #include <linux/clk.h> |
| 9 | #include <linux/clk/tegra.h> | ||
| 9 | #include <linux/completion.h> | 10 | #include <linux/completion.h> |
| 10 | #include <linux/err.h> | 11 | #include <linux/err.h> |
| 11 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
| 12 | #include <linux/iopoll.h> | 13 | #include <linux/io.h> |
| 13 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 14 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 15 | #include <linux/of.h> | 16 | #include <linux/of.h> |
| @@ -21,6 +22,7 @@ · 5 unmodified lines | |||
| 21 | 22 | ||
| 22 | #define EMC_INTSTATUS 0x000 | 23 | #define EMC_INTSTATUS 0x000 |
| 23 | #define EMC_INTMASK 0x004 | 24 | #define EMC_INTMASK 0x004 |
| 25 | #define EMC_DBG 0x008 | ||
| 24 | #define EMC_TIMING_CONTROL 0x028 | 26 | #define EMC_TIMING_CONTROL 0x028 |
| 25 | #define EMC_RC 0x02c | 27 | #define EMC_RC 0x02c |
| 26 | #define EMC_RFC 0x030 | 28 | #define EMC_RFC 0x030 |
| @@ -79,6 +81,12 @@ · 52 unmodified lines | |||
| 79 | #define EMC_REFRESH_OVERFLOW_INT BIT(3) | 81 | #define EMC_REFRESH_OVERFLOW_INT BIT(3) |
| 80 | #define EMC_CLKCHANGE_COMPLETE_INT BIT(4) | 82 | #define EMC_CLKCHANGE_COMPLETE_INT BIT(4) |
| 81 | 83 | ||
| 84 | #define EMC_DBG_READ_MUX_ASSEMBLY BIT(0) | ||
| 85 | #define EMC_DBG_WRITE_MUX_ACTIVE BIT(1) | ||
| 86 | #define EMC_DBG_FORCE_UPDATE BIT(2) | ||
| 87 | #define EMC_DBG_READ_DQM_CTRL BIT(9) | ||
| 88 | #define EMC_DBG_CFG_PRIORITY BIT(24) | ||
| 89 | |||
| 82 | static const u16 emc_timing_registers[] = { | 90 | static const u16 emc_timing_registers[] = { |
| 83 | EMC_RC, | 91 | EMC_RC, |
| 84 | EMC_RFC, | 92 | EMC_RFC, |
| @@ -137,9 +145,6 @@ struct tegra_emc { · 52 unmodified lines | |||
| 137 | struct device *dev; | 145 | struct device *dev; |
| 138 | struct completion clk_handshake_complete; | 146 | struct completion clk_handshake_complete; |
| 139 | struct notifier_block clk_nb; | 147 | struct notifier_block clk_nb; |
| 140 | struct clk *backup_clk; | ||
| 141 | struct clk *emc_mux; | ||
| 142 | struct clk *pll_m; | ||
| 143 | struct clk *clk; | 148 | struct clk *clk; |
| 144 | void __iomem *regs; | 149 | void __iomem *regs; |
| 145 | 150 | ||
| @@ -219,7 +224,7 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) · 73 unmodified lines | |||
| 219 | 224 | ||
| 220 | static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) | 225 | static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) |
| 221 | { | 226 | { |
| 222 | long timeout; | 227 | unsigned long timeout; |
| 223 | 228 | ||
| 224 | dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush); | 229 | dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush); |
| 225 | 230 | ||
| @@ -231,14 +236,10 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) · 5 unmodified lines | |||
| 231 | } | 236 | } |
| 232 | 237 | ||
| 233 | timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, | 238 | timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, |
| 234 | usecs_to_jiffies(100)); | 239 | msecs_to_jiffies(100)); |
| 235 | if (timeout == 0) { | 240 | if (timeout == 0) { |
| 236 | dev_err(emc->dev, "EMC-CAR handshake failed\n"); | 241 | dev_err(emc->dev, "EMC-CAR handshake failed\n"); |
| 237 | return -EIO; | 242 | return -EIO; |
| 238 | } else if (timeout < 0) { | ||
| 239 | dev_err(emc->dev, "failed to wait for EMC-CAR handshake: %ld\n", | ||
| 240 | timeout); | ||
| 241 | return timeout; | ||
| 242 | } | 243 | } |
| 243 | 244 | ||
| 244 | return 0; | 245 | return 0; |
| @@ -363,6 +364,13 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, · 118 unmodified lines | |||
| 363 | sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, | 364 | sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, |
| 364 | NULL); | 365 | NULL); |
| 365 | 366 | ||
| 367 | dev_info(emc->dev, | ||
| 368 | "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", | ||
| 369 | emc->num_timings, | ||
| 370 | tegra_read_ram_code(), | ||
| 371 | emc->timings[0].rate / 1000000, | ||
| 372 | emc->timings[emc->num_timings - 1].rate / 1000000); | ||
| 373 | |||
| 366 | return 0; | 374 | return 0; |
| 367 | } | 375 | } |
| 368 | 376 | ||
| @@ -398,7 +406,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev) · 29 unmodified lines | |||
| 398 | static int emc_setup_hw(struct tegra_emc *emc) | 406 | static int emc_setup_hw(struct tegra_emc *emc) |
| 399 | { | 407 | { |
| 400 | u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; | 408 | u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; |
| 401 | u32 emc_cfg; | 409 | u32 emc_cfg, emc_dbg; |
| 402 | 410 | ||
| 403 | emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); | 411 | emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); |
| 404 | 412 | ||
| @@ -421,42 +429,53 @@ static int emc_setup_hw(struct tegra_emc *emc) · 16 unmodified lines | |||
| 421 | writel_relaxed(intmask, emc->regs + EMC_INTMASK); | 429 | writel_relaxed(intmask, emc->regs + EMC_INTMASK); |
| 422 | writel_relaxed(intmask, emc->regs + EMC_INTSTATUS); | 430 | writel_relaxed(intmask, emc->regs + EMC_INTSTATUS); |
| 423 | 431 | ||
| 432 | /* ensure that unwanted debug features are disabled */ | ||
| 433 | emc_dbg = readl_relaxed(emc->regs + EMC_DBG); | ||
| 434 | emc_dbg |= EMC_DBG_CFG_PRIORITY; | ||
| 435 | emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY; | ||
| 436 | emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE; | ||
| 437 | emc_dbg &= ~EMC_DBG_FORCE_UPDATE; | ||
| 438 | writel_relaxed(emc_dbg, emc->regs + EMC_DBG); | ||
| 439 | |||
| 424 | return 0; | 440 | return 0; |
| 425 | } | 441 | } |
| 426 | 442 | ||
| 427 | static int emc_init(struct tegra_emc *emc, unsigned long rate) | 443 | static long emc_round_rate(unsigned long rate, |
| 444 | unsigned long min_rate, | ||
| 445 | unsigned long max_rate, | ||
| 446 | void *arg) | ||
| 428 | { | 447 | { |
| 429 | int err; | 448 | struct emc_timing *timing = NULL; |
| 449 | struct tegra_emc *emc = arg; | ||
| 450 | unsigned int i; | ||
| 430 | 451 | ||
| 431 | err = clk_set_parent(emc->emc_mux, emc->backup_clk); | 452 | min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate); |
| 432 | if (err) { | ||
| 433 | dev_err(emc->dev, | ||
| 434 | "failed to reparent to backup source: %d\n", err); | ||
| 435 | return err; | ||
| 436 | } | ||
| 437 | 453 | ||
| 438 | err = clk_set_rate(emc->pll_m, rate); | 454 | for (i = 0; i < emc->num_timings; i++) { |
| 439 | if (err) { | 455 | if (emc->timings[i].rate < rate && i != emc->num_timings - 1) |
| 440 | dev_err(emc->dev, | 456 | continue; |
| 441 | "failed to change pll_m rate: %d\n", err); | ||
| 442 | return err; | ||
| 443 | } | ||
| 444 | 457 | ||
| 445 | err = clk_set_parent(emc->emc_mux, emc->pll_m); | 458 | if (emc->timings[i].rate > max_rate) { |
| 446 | if (err) { | 459 | i = max(i, 1u) - 1; |
| 447 | dev_err(emc->dev, | 460 | |
| 448 | "failed to reparent to pll_m: %d\n", err); | 461 | if (emc->timings[i].rate < min_rate) |
| 449 | return err; | 462 | break; |
| 463 | } | ||
| 464 | |||
| 465 | if (emc->timings[i].rate < min_rate) | ||
| 466 | continue; | ||
| 467 | |||
| 468 | timing = &emc->timings[i]; | ||
| 469 | break; | ||
| 450 | } | 470 | } |
| 451 | 471 | ||
| 452 | err = clk_set_rate(emc->clk, rate); | 472 | if (!timing) { |
| 453 | if (err) { | 473 | dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n", |
| 454 | dev_err(emc->dev, | 474 | rate, min_rate, max_rate); |
| 455 | "failed to change emc rate: %d\n", err); | 475 | return -EINVAL; |
| 456 | return err; | ||
| 457 | } | 476 | } |
| 458 | 477 | ||
| 459 | return 0; | 478 | return timing->rate; |
| 460 | } | 479 | } |
| 461 | 480 | ||
| 462 | static int tegra_emc_probe(struct platform_device *pdev) | 481 | static int tegra_emc_probe(struct platform_device *pdev) |
| @@ -515,57 +534,26 @@ static int tegra_emc_probe(struct platform_device *pdev) · 52 unmodified lines | |||
| 515 | return err; | 534 | return err; |
| 516 | } | 535 | } |
| 517 | 536 | ||
| 537 | tegra20_clk_set_emc_round_callback(emc_round_rate, emc); | ||
| 538 | |||
| 518 | emc->clk = devm_clk_get(&pdev->dev, "emc"); | 539 | emc->clk = devm_clk_get(&pdev->dev, "emc"); |
| 519 | if (IS_ERR(emc->clk)) { | 540 | if (IS_ERR(emc->clk)) { |
| 520 | err = PTR_ERR(emc->clk); | 541 | err = PTR_ERR(emc->clk); |
| 521 | dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); | 542 | dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); |
| 522 | return err; | 543 | goto unset_cb; |
| 523 | } | ||
| 524 | |||
| 525 | emc->pll_m = clk_get_sys(NULL, "pll_m"); | ||
| 526 | if (IS_ERR(emc->pll_m)) { | ||
| 527 | err = PTR_ERR(emc->pll_m); | ||
| 528 | dev_err(&pdev->dev, "failed to get pll_m clock: %d\n", err); | ||
| 529 | return err; | ||
| 530 | } | ||
| 531 | |||
| 532 | emc->backup_clk = clk_get_sys(NULL, "pll_p"); | ||
| 533 | if (IS_ERR(emc->backup_clk)) { | ||
| 534 | err = PTR_ERR(emc->backup_clk); | ||
| 535 | dev_err(&pdev->dev, "failed to get pll_p clock: %d\n", err); | ||
| 536 | goto put_pll_m; | ||
| 537 | } | ||
| 538 | |||
| 539 | emc->emc_mux = clk_get_parent(emc->clk); | ||
| 540 | if (IS_ERR(emc->emc_mux)) { | ||
| 541 | err = PTR_ERR(emc->emc_mux); | ||
| 542 | dev_err(&pdev->dev, "failed to get emc_mux clock: %d\n", err); | ||
| 543 | goto put_backup; | ||
| 544 | } | 544 | } |
| 545 | 545 | ||
| 546 | err = clk_notifier_register(emc->clk, &emc->clk_nb); | 546 | err = clk_notifier_register(emc->clk, &emc->clk_nb); |
| 547 | if (err) { | 547 | if (err) { |
| 548 | dev_err(&pdev->dev, "failed to register clk notifier: %d\n", | 548 | dev_err(&pdev->dev, "failed to register clk notifier: %d\n", |
| 549 | err); | 549 | err); |
| 550 | goto put_backup; | 550 | goto unset_cb; |
| 551 | } | ||
| 552 | |||
| 553 | /* set DRAM clock rate to maximum */ | ||
| 554 | err = emc_init(emc, emc->timings[emc->num_timings - 1].rate); | ||
| 555 | if (err) { | ||
| 556 | dev_err(&pdev->dev, "failed to initialize EMC clock rate: %d\n", | ||
| 557 | err); | ||
| 558 | goto unreg_notifier; | ||
| 559 | } | 551 | } |
| 560 | 552 | ||
| 561 | return 0; | 553 | return 0; |
| 562 | 554 | ||
| 563 | unreg_notifier: | 555 | unset_cb: |
| 564 | clk_notifier_unregister(emc->clk, &emc->clk_nb); | 556 | tegra20_clk_set_emc_round_callback(NULL, NULL); |
| 565 | put_backup: | ||
| 566 | clk_put(emc->backup_clk); | ||
| 567 | put_pll_m: | ||
| 568 | clk_put(emc->pll_m); | ||
| 569 | 557 | ||
| 570 | return err; | 558 | return err; |
| 571 | } | 559 | } |
| @@ -0,0 +1,1232 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Tegra30 External Memory Controller driver | ||
| 4 | * | ||
| 5 | * Based on downstream driver from NVIDIA and tegra124-emc.c | ||
| 6 | * Copyright (C) 2011-2014 NVIDIA Corporation | ||
| 7 | * | ||
| 8 | * Author: Dmitry Osipenko <digetx@gmail.com> | ||
| 9 | * Copyright (C) 2019 GRATE-DRIVER project | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/clk/tegra.h> | ||
| 14 | #include <linux/completion.h> | ||
| 15 | #include <linux/delay.h> | ||
| 16 | #include <linux/err.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/iopoll.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/of_platform.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/sort.h> | ||
| 25 | #include <linux/types.h> | ||
| 26 | |||
| 27 | #include <soc/tegra/fuse.h> | ||
| 28 | |||
| 29 | #include "mc.h" | ||
| 30 | |||
| 31 | #define EMC_INTSTATUS 0x000 | ||
| 32 | #define EMC_INTMASK 0x004 | ||
| 33 | #define EMC_DBG 0x008 | ||
| 34 | #define EMC_CFG 0x00c | ||
| 35 | #define EMC_REFCTRL 0x020 | ||
| 36 | #define EMC_TIMING_CONTROL 0x028 | ||
| 37 | #define EMC_RC 0x02c | ||
| 38 | #define EMC_RFC 0x030 | ||
| 39 | #define EMC_RAS 0x034 | ||
| 40 | #define EMC_RP 0x038 | ||
| 41 | #define EMC_R2W 0x03c | ||
| 42 | #define EMC_W2R 0x040 | ||
| 43 | #define EMC_R2P 0x044 | ||
| 44 | #define EMC_W2P 0x048 | ||
| 45 | #define EMC_RD_RCD 0x04c | ||
| 46 | #define EMC_WR_RCD 0x050 | ||
| 47 | #define EMC_RRD 0x054 | ||
| 48 | #define EMC_REXT 0x058 | ||
| 49 | #define EMC_WDV 0x05c | ||
| 50 | #define EMC_QUSE 0x060 | ||
| 51 | #define EMC_QRST 0x064 | ||
| 52 | #define EMC_QSAFE 0x068 | ||
| 53 | #define EMC_RDV 0x06c | ||
| 54 | #define EMC_REFRESH 0x070 | ||
| 55 | #define EMC_BURST_REFRESH_NUM 0x074 | ||
| 56 | #define EMC_PDEX2WR 0x078 | ||
| 57 | #define EMC_PDEX2RD 0x07c | ||
| 58 | #define EMC_PCHG2PDEN 0x080 | ||
| 59 | #define EMC_ACT2PDEN 0x084 | ||
| 60 | #define EMC_AR2PDEN 0x088 | ||
| 61 | #define EMC_RW2PDEN 0x08c | ||
| 62 | #define EMC_TXSR 0x090 | ||
| 63 | #define EMC_TCKE 0x094 | ||
| 64 | #define EMC_TFAW 0x098 | ||
| 65 | #define EMC_TRPAB 0x09c | ||
| 66 | #define EMC_TCLKSTABLE 0x0a0 | ||
| 67 | #define EMC_TCLKSTOP 0x0a4 | ||
| 68 | #define EMC_TREFBW 0x0a8 | ||
| 69 | #define EMC_QUSE_EXTRA 0x0ac | ||
| 70 | #define EMC_ODT_WRITE 0x0b0 | ||
| 71 | #define EMC_ODT_READ 0x0b4 | ||
| 72 | #define EMC_WEXT 0x0b8 | ||
| 73 | #define EMC_CTT 0x0bc | ||
| 74 | #define EMC_MRS_WAIT_CNT 0x0c8 | ||
| 75 | #define EMC_MRS 0x0cc | ||
| 76 | #define EMC_EMRS 0x0d0 | ||
| 77 | #define EMC_SELF_REF 0x0e0 | ||
| 78 | #define EMC_MRW 0x0e8 | ||
| 79 | #define EMC_XM2DQSPADCTRL3 0x0f8 | ||
| 80 | #define EMC_FBIO_SPARE 0x100 | ||
| 81 | #define EMC_FBIO_CFG5 0x104 | ||
| 82 | #define EMC_FBIO_CFG6 0x114 | ||
| 83 | #define EMC_CFG_RSV 0x120 | ||
| 84 | #define EMC_AUTO_CAL_CONFIG 0x2a4 | ||
| 85 | #define EMC_AUTO_CAL_INTERVAL 0x2a8 | ||
| 86 | #define EMC_AUTO_CAL_STATUS 0x2ac | ||
| 87 | #define EMC_STATUS 0x2b4 | ||
| 88 | #define EMC_CFG_2 0x2b8 | ||
| 89 | #define EMC_CFG_DIG_DLL 0x2bc | ||
| 90 | #define EMC_CFG_DIG_DLL_PERIOD 0x2c0 | ||
| 91 | #define EMC_CTT_DURATION 0x2d8 | ||
| 92 | #define EMC_CTT_TERM_CTRL 0x2dc | ||
| 93 | #define EMC_ZCAL_INTERVAL 0x2e0 | ||
| 94 | #define EMC_ZCAL_WAIT_CNT 0x2e4 | ||
| 95 | #define EMC_ZQ_CAL 0x2ec | ||
| 96 | #define EMC_XM2CMDPADCTRL 0x2f0 | ||
| 97 | #define EMC_XM2DQSPADCTRL2 0x2fc | ||
| 98 | #define EMC_XM2DQPADCTRL2 0x304 | ||
| 99 | #define EMC_XM2CLKPADCTRL 0x308 | ||
| 100 | #define EMC_XM2COMPPADCTRL 0x30c | ||
| 101 | #define EMC_XM2VTTGENPADCTRL 0x310 | ||
| 102 | #define EMC_XM2VTTGENPADCTRL2 0x314 | ||
| 103 | #define EMC_XM2QUSEPADCTRL 0x318 | ||
| 104 | #define EMC_DLL_XFORM_DQS0 0x328 | ||
| 105 | #define EMC_DLL_XFORM_DQS1 0x32c | ||
| 106 | #define EMC_DLL_XFORM_DQS2 0x330 | ||
| 107 | #define EMC_DLL_XFORM_DQS3 0x334 | ||
| 108 | #define EMC_DLL_XFORM_DQS4 0x338 | ||
| 109 | #define EMC_DLL_XFORM_DQS5 0x33c | ||
| 110 | #define EMC_DLL_XFORM_DQS6 0x340 | ||
| 111 | #define EMC_DLL_XFORM_DQS7 0x344 | ||
| 112 | #define EMC_DLL_XFORM_QUSE0 0x348 | ||
| 113 | #define EMC_DLL_XFORM_QUSE1 0x34c | ||
| 114 | #define EMC_DLL_XFORM_QUSE2 0x350 | ||
| 115 | #define EMC_DLL_XFORM_QUSE3 0x354 | ||
| 116 | #define EMC_DLL_XFORM_QUSE4 0x358 | ||
| 117 | #define EMC_DLL_XFORM_QUSE5 0x35c | ||
| 118 | #define EMC_DLL_XFORM_QUSE6 0x360 | ||
| 119 | #define EMC_DLL_XFORM_QUSE7 0x364 | ||
| 120 | #define EMC_DLL_XFORM_DQ0 0x368 | ||
| 121 | #define EMC_DLL_XFORM_DQ1 0x36c | ||
| 122 | #define EMC_DLL_XFORM_DQ2 0x370 | ||
| 123 | #define EMC_DLL_XFORM_DQ3 0x374 | ||
| 124 | #define EMC_DLI_TRIM_TXDQS0 0x3a8 | ||
| 125 | #define EMC_DLI_TRIM_TXDQS1 0x3ac | ||
| 126 | #define EMC_DLI_TRIM_TXDQS2 0x3b0 | ||
| 127 | #define EMC_DLI_TRIM_TXDQS3 0x3b4 | ||
| 128 | #define EMC_DLI_TRIM_TXDQS4 0x3b8 | ||
| 129 | #define EMC_DLI_TRIM_TXDQS5 0x3bc | ||
| 130 | #define EMC_DLI_TRIM_TXDQS6 0x3c0 | ||
| 131 | #define EMC_DLI_TRIM_TXDQS7 0x3c4 | ||
| 132 | #define EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE 0x3c8 | ||
| 133 | #define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE 0x3cc | ||
| 134 | #define EMC_UNSTALL_RW_AFTER_CLKCHANGE 0x3d0 | ||
| 135 | #define EMC_SEL_DPD_CTRL 0x3d8 | ||
| 136 | #define EMC_PRE_REFRESH_REQ_CNT 0x3dc | ||
| 137 | #define EMC_DYN_SELF_REF_CONTROL 0x3e0 | ||
| 138 | #define EMC_TXSRDLL 0x3e4 | ||
| 139 | |||
| 140 | #define EMC_STATUS_TIMING_UPDATE_STALLED BIT(23) | ||
| 141 | |||
| 142 | #define EMC_MODE_SET_DLL_RESET BIT(8) | ||
| 143 | #define EMC_MODE_SET_LONG_CNT BIT(26) | ||
| 144 | |||
| 145 | #define EMC_SELF_REF_CMD_ENABLED BIT(0) | ||
| 146 | |||
| 147 | #define DRAM_DEV_SEL_ALL (0 << 30) | ||
| 148 | #define DRAM_DEV_SEL_0 (2 << 30) | ||
| 149 | #define DRAM_DEV_SEL_1 (1 << 30) | ||
| 150 | #define DRAM_BROADCAST(num) \ | ||
| 151 | ((num) > 1 ? DRAM_DEV_SEL_ALL : DRAM_DEV_SEL_0) | ||
| 152 | |||
| 153 | #define EMC_ZQ_CAL_CMD BIT(0) | ||
| 154 | #define EMC_ZQ_CAL_LONG BIT(4) | ||
| 155 | #define EMC_ZQ_CAL_LONG_CMD_DEV0 \ | ||
| 156 | (DRAM_DEV_SEL_0 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD) | ||
| 157 | #define EMC_ZQ_CAL_LONG_CMD_DEV1 \ | ||
| 158 | (DRAM_DEV_SEL_1 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD) | ||
| 159 | |||
| 160 | #define EMC_DBG_READ_MUX_ASSEMBLY BIT(0) | ||
| 161 | #define EMC_DBG_WRITE_MUX_ACTIVE BIT(1) | ||
| 162 | #define EMC_DBG_FORCE_UPDATE BIT(2) | ||
| 163 | #define EMC_DBG_CFG_PRIORITY BIT(24) | ||
| 164 | |||
| 165 | #define EMC_CFG5_QUSE_MODE_SHIFT 13 | ||
| 166 | #define EMC_CFG5_QUSE_MODE_MASK (7 << EMC_CFG5_QUSE_MODE_SHIFT) | ||
| 167 | |||
| 168 | #define EMC_CFG5_QUSE_MODE_INTERNAL_LPBK 2 | ||
| 169 | #define EMC_CFG5_QUSE_MODE_PULSE_INTERN 3 | ||
| 170 | |||
| 171 | #define EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE BIT(9) | ||
| 172 | |||
| 173 | #define EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE BIT(10) | ||
| 174 | |||
| 175 | #define EMC_XM2QUSEPADCTRL_IVREF_ENABLE BIT(4) | ||
| 176 | |||
| 177 | #define EMC_XM2DQSPADCTRL2_VREF_ENABLE BIT(5) | ||
| 178 | #define EMC_XM2DQSPADCTRL3_VREF_ENABLE BIT(5) | ||
| 179 | |||
| 180 | #define EMC_AUTO_CAL_STATUS_ACTIVE BIT(31) | ||
| 181 | |||
| 182 | #define EMC_FBIO_CFG5_DRAM_TYPE_MASK 0x3 | ||
| 183 | |||
| 184 | #define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK 0x3ff | ||
| 185 | #define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT 16 | ||
| 186 | #define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK \ | ||
| 187 | (0x3ff << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) | ||
| 188 | |||
| 189 | #define EMC_REFCTRL_DEV_SEL_MASK 0x3 | ||
| 190 | #define EMC_REFCTRL_ENABLE BIT(31) | ||
| 191 | #define EMC_REFCTRL_ENABLE_ALL(num) \ | ||
| 192 | (((num) > 1 ? 0 : 2) | EMC_REFCTRL_ENABLE) | ||
| 193 | #define EMC_REFCTRL_DISABLE_ALL(num) ((num) > 1 ? 0 : 2) | ||
| 194 | |||
| 195 | #define EMC_CFG_PERIODIC_QRST BIT(21) | ||
| 196 | #define EMC_CFG_DYN_SREF_ENABLE BIT(28) | ||
| 197 | |||
| 198 | #define EMC_CLKCHANGE_REQ_ENABLE BIT(0) | ||
| 199 | #define EMC_CLKCHANGE_PD_ENABLE BIT(1) | ||
| 200 | #define EMC_CLKCHANGE_SR_ENABLE BIT(2) | ||
| 201 | |||
| 202 | #define EMC_TIMING_UPDATE BIT(0) | ||
| 203 | |||
| 204 | #define EMC_REFRESH_OVERFLOW_INT BIT(3) | ||
| 205 | #define EMC_CLKCHANGE_COMPLETE_INT BIT(4) | ||
| 206 | |||
| 207 | enum emc_dram_type { | ||
| 208 | DRAM_TYPE_DDR3, | ||
| 209 | DRAM_TYPE_DDR1, | ||
| 210 | DRAM_TYPE_LPDDR2, | ||
| 211 | DRAM_TYPE_DDR2, | ||
| 212 | }; | ||
| 213 | |||
| 214 | enum emc_dll_change { | ||
| 215 | DLL_CHANGE_NONE, | ||
| 216 | DLL_CHANGE_ON, | ||
| 217 | DLL_CHANGE_OFF | ||
| 218 | }; | ||
| 219 | |||
| 220 | static const u16 emc_timing_registers[] = { | ||
| 221 | [0] = EMC_RC, | ||
| 222 | [1] = EMC_RFC, | ||
| 223 | [2] = EMC_RAS, | ||
| 224 | [3] = EMC_RP, | ||
| 225 | [4] = EMC_R2W, | ||
| 226 | [5] = EMC_W2R, | ||
| 227 | [6] = EMC_R2P, | ||
| 228 | [7] = EMC_W2P, | ||
| 229 | [8] = EMC_RD_RCD, | ||
| 230 | [9] = EMC_WR_RCD, | ||
| 231 | [10] = EMC_RRD, | ||
| 232 | [11] = EMC_REXT, | ||
| 233 | [12] = EMC_WEXT, | ||
| 234 | [13] = EMC_WDV, | ||
| 235 | [14] = EMC_QUSE, | ||
| 236 | [15] = EMC_QRST, | ||
| 237 | [16] = EMC_QSAFE, | ||
| 238 | [17] = EMC_RDV, | ||
| 239 | [18] = EMC_REFRESH, | ||
| 240 | [19] = EMC_BURST_REFRESH_NUM, | ||
| 241 | [20] = EMC_PRE_REFRESH_REQ_CNT, | ||
| 242 | [21] = EMC_PDEX2WR, | ||
| 243 | [22] = EMC_PDEX2RD, | ||
| 244 | [23] = EMC_PCHG2PDEN, | ||
| 245 | [24] = EMC_ACT2PDEN, | ||
| 246 | [25] = EMC_AR2PDEN, | ||
| 247 | [26] = EMC_RW2PDEN, | ||
| 248 | [27] = EMC_TXSR, | ||
| 249 | [28] = EMC_TXSRDLL, | ||
| 250 | [29] = EMC_TCKE, | ||
| 251 | [30] = EMC_TFAW, | ||
| 252 | [31] = EMC_TRPAB, | ||
| 253 | [32] = EMC_TCLKSTABLE, | ||
| 254 | [33] = EMC_TCLKSTOP, | ||
| 255 | [34] = EMC_TREFBW, | ||
| 256 | [35] = EMC_QUSE_EXTRA, | ||
| 257 | [36] = EMC_FBIO_CFG6, | ||
| 258 | [37] = EMC_ODT_WRITE, | ||
| 259 | [38] = EMC_ODT_READ, | ||
| 260 | [39] = EMC_FBIO_CFG5, | ||
| 261 | [40] = EMC_CFG_DIG_DLL, | ||
| 262 | [41] = EMC_CFG_DIG_DLL_PERIOD, | ||
| 263 | [42] = EMC_DLL_XFORM_DQS0, | ||
| 264 | [43] = EMC_DLL_XFORM_DQS1, | ||
| 265 | [44] = EMC_DLL_XFORM_DQS2, | ||
| 266 | [45] = EMC_DLL_XFORM_DQS3, | ||
| 267 | [46] = EMC_DLL_XFORM_DQS4, | ||
| 268 | [47] = EMC_DLL_XFORM_DQS5, | ||
| 269 | [48] = EMC_DLL_XFORM_DQS6, | ||
| 270 | [49] = EMC_DLL_XFORM_DQS7, | ||
| 271 | [50] = EMC_DLL_XFORM_QUSE0, | ||
| 272 | [51] = EMC_DLL_XFORM_QUSE1, | ||
| 273 | [52] = EMC_DLL_XFORM_QUSE2, | ||
| 274 | [53] = EMC_DLL_XFORM_QUSE3, | ||
| 275 | [54] = EMC_DLL_XFORM_QUSE4, | ||
| 276 | [55] = EMC_DLL_XFORM_QUSE5, | ||
| 277 | [56] = EMC_DLL_XFORM_QUSE6, | ||
| 278 | [57] = EMC_DLL_XFORM_QUSE7, | ||
| 279 | [58] = EMC_DLI_TRIM_TXDQS0, | ||
| 280 | [59] = EMC_DLI_TRIM_TXDQS1, | ||
| 281 | [60] = EMC_DLI_TRIM_TXDQS2, | ||
| 282 | [61] = EMC_DLI_TRIM_TXDQS3, | ||
| 283 | [62] = EMC_DLI_TRIM_TXDQS4, | ||
| 284 | [63] = EMC_DLI_TRIM_TXDQS5, | ||
| 285 | [64] = EMC_DLI_TRIM_TXDQS6, | ||
| 286 | [65] = EMC_DLI_TRIM_TXDQS7, | ||
| 287 | [66] = EMC_DLL_XFORM_DQ0, | ||
| 288 | [67] = EMC_DLL_XFORM_DQ1, | ||
| 289 | [68] = EMC_DLL_XFORM_DQ2, | ||
| 290 | [69] = EMC_DLL_XFORM_DQ3, | ||
| 291 | [70] = EMC_XM2CMDPADCTRL, | ||
| 292 | [71] = EMC_XM2DQSPADCTRL2, | ||
| 293 | [72] = EMC_XM2DQPADCTRL2, | ||
| 294 | [73] = EMC_XM2CLKPADCTRL, | ||
| 295 | [74] = EMC_XM2COMPPADCTRL, | ||
| 296 | [75] = EMC_XM2VTTGENPADCTRL, | ||
| 297 | [76] = EMC_XM2VTTGENPADCTRL2, | ||
| 298 | [77] = EMC_XM2QUSEPADCTRL, | ||
| 299 | [78] = EMC_XM2DQSPADCTRL3, | ||
| 300 | [79] = EMC_CTT_TERM_CTRL, | ||
| 301 | [80] = EMC_ZCAL_INTERVAL, | ||
| 302 | [81] = EMC_ZCAL_WAIT_CNT, | ||
| 303 | [82] = EMC_MRS_WAIT_CNT, | ||
| 304 | [83] = EMC_AUTO_CAL_CONFIG, | ||
| 305 | [84] = EMC_CTT, | ||
| 306 | [85] = EMC_CTT_DURATION, | ||
| 307 | [86] = EMC_DYN_SELF_REF_CONTROL, | ||
| 308 | [87] = EMC_FBIO_SPARE, | ||
| 309 | [88] = EMC_CFG_RSV, | ||
| 310 | }; | ||
| 311 | |||
| 312 | struct emc_timing { | ||
| 313 | unsigned long rate; | ||
| 314 | |||
| 315 | u32 data[ARRAY_SIZE(emc_timing_registers)]; | ||
| 316 | |||
| 317 | u32 emc_auto_cal_interval; | ||
| 318 | u32 emc_mode_1; | ||
| 319 | u32 emc_mode_2; | ||
| 320 | u32 emc_mode_reset; | ||
| 321 | u32 emc_zcal_cnt_long; | ||
| 322 | bool emc_cfg_periodic_qrst; | ||
| 323 | bool emc_cfg_dyn_self_ref; | ||
| 324 | }; | ||
| 325 | |||
| 326 | struct tegra_emc { | ||
| 327 | struct device *dev; | ||
| 328 | struct tegra_mc *mc; | ||
| 329 | struct completion clk_handshake_complete; | ||
| 330 | struct notifier_block clk_nb; | ||
| 331 | struct clk *clk; | ||
| 332 | void __iomem *regs; | ||
| 333 | unsigned int irq; | ||
| 334 | |||
| 335 | struct emc_timing *timings; | ||
| 336 | unsigned int num_timings; | ||
| 337 | |||
| 338 | u32 mc_override; | ||
| 339 | u32 emc_cfg; | ||
| 340 | |||
| 341 | u32 emc_mode_1; | ||
| 342 | u32 emc_mode_2; | ||
| 343 | u32 emc_mode_reset; | ||
| 344 | |||
| 345 | bool vref_cal_toggle : 1; | ||
| 346 | bool zcal_long : 1; | ||
| 347 | bool dll_on : 1; | ||
| 348 | bool prepared : 1; | ||
| 349 | bool bad_state : 1; | ||
| 350 | }; | ||
| 351 | |||
| 352 | static irqreturn_t tegra_emc_isr(int irq, void *data) | ||
| 353 | { | ||
| 354 | struct tegra_emc *emc = data; | ||
| 355 | u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; | ||
| 356 | u32 status; | ||
| 357 | |||
| 358 | status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask; | ||
| 359 | if (!status) | ||
| 360 | return IRQ_NONE; | ||
| 361 | |||
| 362 | /* notify about EMC-CAR handshake completion */ | ||
| 363 | if (status & EMC_CLKCHANGE_COMPLETE_INT) | ||
| 364 | complete(&emc->clk_handshake_complete); | ||
| 365 | |||
| 366 | /* notify about HW problem */ | ||
| 367 | if (status & EMC_REFRESH_OVERFLOW_INT) | ||
| 368 | dev_err_ratelimited(emc->dev, | ||
| 369 | "refresh request overflow timeout\n"); | ||
| 370 | |||
| 371 | /* clear interrupts */ | ||
| 372 | writel_relaxed(status, emc->regs + EMC_INTSTATUS); | ||
| 373 | |||
| 374 | return IRQ_HANDLED; | ||
| 375 | } | ||
| 376 | |||
| 377 | static struct emc_timing *emc_find_timing(struct tegra_emc *emc, | ||
| 378 | unsigned long rate) | ||
| 379 | { | ||
| 380 | struct emc_timing *timing = NULL; | ||
| 381 | unsigned int i; | ||
| 382 | |||
| 383 | for (i = 0; i < emc->num_timings; i++) { | ||
| 384 | if (emc->timings[i].rate >= rate) { | ||
| 385 | timing = &emc->timings[i]; | ||
| 386 | break; | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | if (!timing) { | ||
| 391 | dev_err(emc->dev, "no timing for rate %lu\n", rate); | ||
| 392 | return NULL; | ||
| 393 | } | ||
| 394 | |||
| 395 | return timing; | ||
| 396 | } | ||
| 397 | |||
| 398 | static bool emc_dqs_preset(struct tegra_emc *emc, struct emc_timing *timing, | ||
| 399 | bool *schmitt_to_vref) | ||
| 400 | { | ||
| 401 | bool preset = false; | ||
| 402 | u32 val; | ||
| 403 | |||
| 404 | if (timing->data[71] & EMC_XM2DQSPADCTRL2_VREF_ENABLE) { | ||
| 405 | val = readl_relaxed(emc->regs + EMC_XM2DQSPADCTRL2); | ||
| 406 | |||
| 407 | if (!(val & EMC_XM2DQSPADCTRL2_VREF_ENABLE)) { | ||
| 408 | val |= EMC_XM2DQSPADCTRL2_VREF_ENABLE; | ||
| 409 | writel_relaxed(val, emc->regs + EMC_XM2DQSPADCTRL2); | ||
| 410 | |||
| 411 | preset = true; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | if (timing->data[78] & EMC_XM2DQSPADCTRL3_VREF_ENABLE) { | ||
| 416 | val = readl_relaxed(emc->regs + EMC_XM2DQSPADCTRL3); | ||
| 417 | |||
| 418 | if (!(val & EMC_XM2DQSPADCTRL3_VREF_ENABLE)) { | ||
| 419 | val |= EMC_XM2DQSPADCTRL3_VREF_ENABLE; | ||
| 420 | writel_relaxed(val, emc->regs + EMC_XM2DQSPADCTRL3); | ||
| 421 | |||
| 422 | preset = true; | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | if (timing->data[77] & EMC_XM2QUSEPADCTRL_IVREF_ENABLE) { | ||
| 427 | val = readl_relaxed(emc->regs + EMC_XM2QUSEPADCTRL); | ||
| 428 | |||
| 429 | if (!(val & EMC_XM2QUSEPADCTRL_IVREF_ENABLE)) { | ||
| 430 | val |= EMC_XM2QUSEPADCTRL_IVREF_ENABLE; | ||
| 431 | writel_relaxed(val, emc->regs + EMC_XM2QUSEPADCTRL); | ||
| 432 | |||
| 433 | *schmitt_to_vref = true; | ||
| 434 | preset = true; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | return preset; | ||
| 439 | } | ||
| 440 | |||
| 441 | static int emc_seq_update_timing(struct tegra_emc *emc) | ||
| 442 | { | ||
| 443 | u32 val; | ||
| 444 | int err; | ||
| 445 | |||
| 446 | writel_relaxed(EMC_TIMING_UPDATE, emc->regs + EMC_TIMING_CONTROL); | ||
| 447 | |||
| 448 | err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_STATUS, val, | ||
| 449 | !(val & EMC_STATUS_TIMING_UPDATE_STALLED), | ||
| 450 | 1, 200); | ||
| 451 | if (err) { | ||
| 452 | dev_err(emc->dev, "failed to update timing: %d\n", err); | ||
| 453 | return err; | ||
| 454 | } | ||
| 455 | |||
| 456 | return 0; | ||
| 457 | } | ||
| 458 | |||
| 459 | static int emc_prepare_mc_clk_cfg(struct tegra_emc *emc, unsigned long rate) | ||
| 460 | { | ||
| 461 | struct tegra_mc *mc = emc->mc; | ||
| 462 | unsigned int misc0_index = 16; | ||
| 463 | unsigned int i; | ||
| 464 | bool same; | ||
| 465 | |||
| 466 | for (i = 0; i < mc->num_timings; i++) { | ||
| 467 | if (mc->timings[i].rate != rate) | ||
| 468 | continue; | ||
| 469 | |||
| 470 | if (mc->timings[i].emem_data[misc0_index] & BIT(27)) | ||
| 471 | same = true; | ||
| 472 | else | ||
| 473 | same = false; | ||
| 474 | |||
| 475 | return tegra20_clk_prepare_emc_mc_same_freq(emc->clk, same); | ||
| 476 | } | ||
| 477 | |||
| 478 | return -EINVAL; | ||
| 479 | } | ||
| 480 | |||
| 481 | static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) | ||
| 482 | { | ||
| 483 | struct emc_timing *timing = emc_find_timing(emc, rate); | ||
| 484 | enum emc_dll_change dll_change; | ||
| 485 | enum emc_dram_type dram_type; | ||
| 486 | bool schmitt_to_vref = false; | ||
| 487 | unsigned int pre_wait = 0; | ||
| 488 | bool qrst_used = false; | ||
| 489 | unsigned int dram_num; | ||
| 490 | unsigned int i; | ||
| 491 | u32 fbio_cfg5; | ||
| 492 | u32 emc_dbg; | ||
| 493 | u32 val; | ||
| 494 | int err; | ||
| 495 | |||
| 496 | if (!timing || emc->bad_state) | ||
| 497 | return -EINVAL; | ||
| 498 | |||
| 499 | dev_dbg(emc->dev, "%s: using timing rate %lu for requested rate %lu\n", | ||
| 500 | __func__, timing->rate, rate); | ||
| 501 | |||
| 502 | emc->bad_state = true; | ||
| 503 | |||
| 504 | err = emc_prepare_mc_clk_cfg(emc, rate); | ||
| 505 | if (err) { | ||
| 506 | dev_err(emc->dev, "mc clock preparation failed: %d\n", err); | ||
| 507 | return err; | ||
| 508 | } | ||
| 509 | |||
| 510 | emc->vref_cal_toggle = false; | ||
| 511 | emc->mc_override = mc_readl(emc->mc, MC_EMEM_ARB_OVERRIDE); | ||
| 512 | emc->emc_cfg = readl_relaxed(emc->regs + EMC_CFG); | ||
| 513 | emc_dbg = readl_relaxed(emc->regs + EMC_DBG); | ||
| 514 | |||
| 515 | if (emc->dll_on == !!(timing->emc_mode_1 & 0x1)) | ||
| 516 | dll_change = DLL_CHANGE_NONE; | ||
| 517 | else if (timing->emc_mode_1 & 0x1) | ||
| 518 | dll_change = DLL_CHANGE_ON; | ||
| 519 | else | ||
| 520 | dll_change = DLL_CHANGE_OFF; | ||
| 521 | |||
| 522 | emc->dll_on = !!(timing->emc_mode_1 & 0x1); | ||
| 523 | |||
| 524 | if (timing->data[80] && !readl_relaxed(emc->regs + EMC_ZCAL_INTERVAL)) | ||
| 525 | emc->zcal_long = true; | ||
| 526 | else | ||
| 527 | emc->zcal_long = false; | ||
| 528 | |||
| 529 | fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5); | ||
| 530 | dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK; | ||
| 531 | |||
| 532 | dram_num = tegra_mc_get_emem_device_count(emc->mc); | ||
| 533 | |||
| 534 | /* disable dynamic self-refresh */ | ||
| 535 | if (emc->emc_cfg & EMC_CFG_DYN_SREF_ENABLE) { | ||
| 536 | emc->emc_cfg &= ~EMC_CFG_DYN_SREF_ENABLE; | ||
| 537 | writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG); | ||
| 538 | |||
| 539 | pre_wait = 5; | ||
| 540 | } | ||
| 541 | |||
| 542 | /* update MC arbiter settings */ | ||
| 543 | val = mc_readl(emc->mc, MC_EMEM_ARB_OUTSTANDING_REQ); | ||
| 544 | if (!(val & MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE) || | ||
| 545 | ((val & MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK) > 0x50)) { | ||
| 546 | |||
| 547 | val = MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE | | ||
| 548 | MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE | 0x50; | ||
| 549 | mc_writel(emc->mc, val, MC_EMEM_ARB_OUTSTANDING_REQ); | ||
| 550 | mc_writel(emc->mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL); | ||
| 551 | } | ||
| 552 | |||
| 553 | if (emc->mc_override & MC_EMEM_ARB_OVERRIDE_EACK_MASK) | ||
| 554 | mc_writel(emc->mc, | ||
| 555 | emc->mc_override & ~MC_EMEM_ARB_OVERRIDE_EACK_MASK, | ||
| 556 | MC_EMEM_ARB_OVERRIDE); | ||
| 557 | |||
| 558 | /* check DQ/DQS VREF delay */ | ||
| 559 | if (emc_dqs_preset(emc, timing, &schmitt_to_vref)) { | ||
| 560 | if (pre_wait < 3) | ||
| 561 | pre_wait = 3; | ||
| 562 | } | ||
| 563 | |||
| 564 | if (pre_wait) { | ||
| 565 | err = emc_seq_update_timing(emc); | ||
| 566 | if (err) | ||
| 567 | return err; | ||
| 568 | |||
| 569 | udelay(pre_wait); | ||
| 570 | } | ||
| 571 | |||
| 572 | /* disable auto-calibration if VREF mode is switching */ | ||
| 573 | if (timing->emc_auto_cal_interval) { | ||
| 574 | val = readl_relaxed(emc->regs + EMC_XM2COMPPADCTRL); | ||
| 575 | val ^= timing->data[74]; | ||
| 576 | |||
| 577 | if (val & EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE) { | ||
| 578 | writel_relaxed(0, emc->regs + EMC_AUTO_CAL_INTERVAL); | ||
| 579 | |||
| 580 | err = readl_relaxed_poll_timeout_atomic( | ||
| 581 | emc->regs + EMC_AUTO_CAL_STATUS, val, | ||
| 582 | !(val & EMC_AUTO_CAL_STATUS_ACTIVE), 1, 300); | ||
| 583 | if (err) { | ||
| 584 | dev_err(emc->dev, | ||
| 585 | "failed to disable auto-cal: %d\n", | ||
| 586 | err); | ||
| 587 | return err; | ||
| 588 | } | ||
| 589 | |||
| 590 | emc->vref_cal_toggle = true; | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 594 | /* program shadow registers */ | ||
| 595 | for (i = 0; i < ARRAY_SIZE(timing->data); i++) { | ||
| 596 | /* EMC_XM2CLKPADCTRL should be programmed separately */ | ||
| 597 | if (i != 73) | ||
| 598 | writel_relaxed(timing->data[i], | ||
| 599 | emc->regs + emc_timing_registers[i]); | ||
| 600 | } | ||
| 601 | |||
| 602 | err = tegra_mc_write_emem_configuration(emc->mc, timing->rate); | ||
| 603 | if (err) | ||
| 604 | return err; | ||
| 605 | |||
| 606 | /* DDR3: predict MRS long wait count */ | ||
| 607 | if (dram_type == DRAM_TYPE_DDR3 && dll_change == DLL_CHANGE_ON) { | ||
| 608 | u32 cnt = 512; | ||
| 609 | |||
| 610 | if (emc->zcal_long) | ||
| 611 | cnt -= dram_num * 256; | ||
| 612 | |||
| 613 | val = timing->data[82] & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK; | ||
| 614 | if (cnt < val) | ||
| 615 | cnt = val; | ||
| 616 | |||
| 617 | val = timing->data[82] & ~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK; | ||
| 618 | val |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) & | ||
| 619 | EMC_MRS_WAIT_CNT_LONG_WAIT_MASK; | ||
| 620 | |||
| 621 | writel_relaxed(val, emc->regs + EMC_MRS_WAIT_CNT); | ||
| 622 | } | ||
| 623 | |||
| 624 | /* disable interrupt since read access is prohibited after stalling */ | ||
| 625 | disable_irq(emc->irq); | ||
| 626 | |||
| 627 | /* this read also completes the writes */ | ||
| 628 | val = readl_relaxed(emc->regs + EMC_SEL_DPD_CTRL); | ||
| 629 | |||
| 630 | if (!(val & EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE) && schmitt_to_vref) { | ||
| 631 | u32 cur_mode, new_mode; | ||
| 632 | |||
| 633 | cur_mode = fbio_cfg5 & EMC_CFG5_QUSE_MODE_MASK; | ||
| 634 | cur_mode >>= EMC_CFG5_QUSE_MODE_SHIFT; | ||
| 635 | |||
| 636 | new_mode = timing->data[39] & EMC_CFG5_QUSE_MODE_MASK; | ||
| 637 | new_mode >>= EMC_CFG5_QUSE_MODE_SHIFT; | ||
| 638 | |||
| 639 | if ((cur_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN && | ||
| 640 | cur_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK) || | ||
| 641 | (new_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN && | ||
| 642 | new_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK)) | ||
| 643 | qrst_used = true; | ||
| 644 | } | ||
| 645 | |||
| 646 | /* flow control marker 1 */ | ||
| 647 | writel_relaxed(0x1, emc->regs + EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE); | ||
| 648 | |||
| 649 | /* enable periodic reset */ | ||
| 650 | if (qrst_used) { | ||
| 651 | writel_relaxed(emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, | ||
| 652 | emc->regs + EMC_DBG); | ||
| 653 | writel_relaxed(emc->emc_cfg | EMC_CFG_PERIODIC_QRST, | ||
| 654 | emc->regs + EMC_CFG); | ||
| 655 | writel_relaxed(emc_dbg, emc->regs + EMC_DBG); | ||
| 656 | } | ||
| 657 | |||
| 658 | /* disable auto-refresh to save time after clock change */ | ||
| 659 | writel_relaxed(EMC_REFCTRL_DISABLE_ALL(dram_num), | ||
| 660 | emc->regs + EMC_REFCTRL); | ||
| 661 | |||
| 662 | /* turn off DLL and enter self-refresh on DDR3 */ | ||
| 663 | if (dram_type == DRAM_TYPE_DDR3) { | ||
| 664 | if (dll_change == DLL_CHANGE_OFF) | ||
| 665 | writel_relaxed(timing->emc_mode_1, | ||
| 666 | emc->regs + EMC_EMRS); | ||
| 667 | |||
| 668 | writel_relaxed(DRAM_BROADCAST(dram_num) | | ||
| 669 | EMC_SELF_REF_CMD_ENABLED, | ||
| 670 | emc->regs + EMC_SELF_REF); | ||
| 671 | } | ||
| 672 | |||
| 673 | /* flow control marker 2 */ | ||
| 674 | writel_relaxed(0x1, emc->regs + EMC_STALL_THEN_EXE_AFTER_CLKCHANGE); | ||
| 675 | |||
| 676 | /* enable write-active MUX, update unshadowed pad control */ | ||
| 677 | writel_relaxed(emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, emc->regs + EMC_DBG); | ||
| 678 | writel_relaxed(timing->data[73], emc->regs + EMC_XM2CLKPADCTRL); | ||
| 679 | |||
| 680 | /* restore periodic QRST and disable write-active MUX */ | ||
| 681 | val = !!(emc->emc_cfg & EMC_CFG_PERIODIC_QRST); | ||
| 682 | if (qrst_used || timing->emc_cfg_periodic_qrst != val) { | ||
| 683 | if (timing->emc_cfg_periodic_qrst) | ||
| 684 | emc->emc_cfg |= EMC_CFG_PERIODIC_QRST; | ||
| 685 | else | ||
| 686 | emc->emc_cfg &= ~EMC_CFG_PERIODIC_QRST; | ||
| 687 | |||
| 688 | writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG); | ||
| 689 | } | ||
| 690 | writel_relaxed(emc_dbg, emc->regs + EMC_DBG); | ||
| 691 | |||
| 692 | /* exit self-refresh on DDR3 */ | ||
| 693 | if (dram_type == DRAM_TYPE_DDR3) | ||
| 694 | writel_relaxed(DRAM_BROADCAST(dram_num), | ||
| 695 | emc->regs + EMC_SELF_REF); | ||
| 696 | |||
| 697 | /* set DRAM-mode registers */ | ||
| 698 | if (dram_type == DRAM_TYPE_DDR3) { | ||
| 699 | if (timing->emc_mode_1 != emc->emc_mode_1) | ||
| 700 | writel_relaxed(timing->emc_mode_1, | ||
| 701 | emc->regs + EMC_EMRS); | ||
| 702 | |||
| 703 | if (timing->emc_mode_2 != emc->emc_mode_2) | ||
| 704 | writel_relaxed(timing->emc_mode_2, | ||
| 705 | emc->regs + EMC_EMRS); | ||
| 706 | |||
| 707 | if (timing->emc_mode_reset != emc->emc_mode_reset || | ||
| 708 | dll_change == DLL_CHANGE_ON) { | ||
| 709 | val = timing->emc_mode_reset; | ||
| 710 | if (dll_change == DLL_CHANGE_ON) { | ||
| 711 | val |= EMC_MODE_SET_DLL_RESET; | ||
| 712 | val |= EMC_MODE_SET_LONG_CNT; | ||
| 713 | } else { | ||
| 714 | val &= ~EMC_MODE_SET_DLL_RESET; | ||
| 715 | } | ||
| 716 | writel_relaxed(val, emc->regs + EMC_MRS); | ||
| 717 | } | ||
| 718 | } else { | ||
| 719 | if (timing->emc_mode_2 != emc->emc_mode_2) | ||
| 720 | writel_relaxed(timing->emc_mode_2, | ||
| 721 | emc->regs + EMC_MRW); | ||
| 722 | |||
| 723 | if (timing->emc_mode_1 != emc->emc_mode_1) | ||
| 724 | writel_relaxed(timing->emc_mode_1, | ||
| 725 | emc->regs + EMC_MRW); | ||
| 726 | } | ||
| 727 | |||
| 728 | emc->emc_mode_1 = timing->emc_mode_1; | ||
| 729 | emc->emc_mode_2 = timing->emc_mode_2; | ||
| 730 | emc->emc_mode_reset = timing->emc_mode_reset; | ||
| 731 | |||
| 732 | /* issue ZCAL command if turning ZCAL on */ | ||
| 733 | if (emc->zcal_long) { | ||
| 734 | writel_relaxed(EMC_ZQ_CAL_LONG_CMD_DEV0, | ||
| 735 | emc->regs + EMC_ZQ_CAL); | ||
| 736 | |||
| 737 | if (dram_num > 1) | ||
| 738 | writel_relaxed(EMC_ZQ_CAL_LONG_CMD_DEV1, | ||
| 739 | emc->regs + EMC_ZQ_CAL); | ||
| 740 | } | ||
| 741 | |||
| 742 | /* re-enable auto-refresh */ | ||
| 743 | writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num), | ||
| 744 | emc->regs + EMC_REFCTRL); | ||
| 745 | |||
| 746 | /* flow control marker 3 */ | ||
| 747 | writel_relaxed(0x1, emc->regs + EMC_UNSTALL_RW_AFTER_CLKCHANGE); | ||
| 748 | |||
| 749 | reinit_completion(&emc->clk_handshake_complete); | ||
| 750 | |||
| 751 | /* interrupt can be re-enabled now */ | ||
| 752 | enable_irq(emc->irq); | ||
| 753 | |||
| 754 | emc->bad_state = false; | ||
| 755 | emc->prepared = true; | ||
| 756 | |||
| 757 | return 0; | ||
| 758 | } | ||
| 759 | |||
| 760 | static int emc_complete_timing_change(struct tegra_emc *emc, | ||
| 761 | unsigned long rate) | ||
| 762 | { | ||
| 763 | struct emc_timing *timing = emc_find_timing(emc, rate); | ||
| 764 | unsigned long timeout; | ||
| 765 | int ret; | ||
| 766 | |||
| 767 | timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, | ||
| 768 | msecs_to_jiffies(100)); | ||
| 769 | if (timeout == 0) { | ||
| 770 | dev_err(emc->dev, "emc-car handshake failed\n"); | ||
| 771 | emc->bad_state = true; | ||
| 772 | return -EIO; | ||
| 773 | } | ||
| 774 | |||
| 775 | /* restore auto-calibration */ | ||
| 776 | if (emc->vref_cal_toggle) | ||
| 777 | writel_relaxed(timing->emc_auto_cal_interval, | ||
| 778 | emc->regs + EMC_AUTO_CAL_INTERVAL); | ||
| 779 | |||
| 780 | /* restore dynamic self-refresh */ | ||
| 781 | if (timing->emc_cfg_dyn_self_ref) { | ||
| 782 | emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE; | ||
| 783 | writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG); | ||
| 784 | } | ||
| 785 | |||
| 786 | /* set number of clocks to wait after each ZQ command */ | ||
| 787 | if (emc->zcal_long) | ||
| 788 | writel_relaxed(timing->emc_zcal_cnt_long, | ||
| 789 | emc->regs + EMC_ZCAL_WAIT_CNT); | ||
| 790 | |||
| 791 | udelay(2); | ||
| 792 | /* update restored timing */ | ||
| 793 | ret = emc_seq_update_timing(emc); | ||
| 794 | if (ret) | ||
| 795 | emc->bad_state = true; | ||
| 796 | |||
| 797 | /* restore early ACK */ | ||
| 798 | mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE); | ||
| 799 | |||
| 800 | emc->prepared = false; | ||
| 801 | |||
| 802 | return ret; | ||
| 803 | } | ||
| 804 | |||
| 805 | static int emc_unprepare_timing_change(struct tegra_emc *emc, | ||
| 806 | unsigned long rate) | ||
| 807 | { | ||
| 808 | if (emc->prepared && !emc->bad_state) { | ||
| 809 | /* shouldn't ever happen in practice */ | ||
| 810 | dev_err(emc->dev, "timing configuration can't be reverted\n"); | ||
| 811 | emc->bad_state = true; | ||
| 812 | } | ||
| 813 | |||
| 814 | return 0; | ||
| 815 | } | ||
| 816 | |||
| 817 | static int emc_clk_change_notify(struct notifier_block *nb, | ||
| 818 | unsigned long msg, void *data) | ||
| 819 | { | ||
| 820 | struct tegra_emc *emc = container_of(nb, struct tegra_emc, clk_nb); | ||
| 821 | struct clk_notifier_data *cnd = data; | ||
| 822 | int err; | ||
| 823 | |||
| 824 | switch (msg) { | ||
| 825 | case PRE_RATE_CHANGE: | ||
| 826 | err = emc_prepare_timing_change(emc, cnd->new_rate); | ||
| 827 | break; | ||
| 828 | |||
| 829 | case ABORT_RATE_CHANGE: | ||
| 830 | err = emc_unprepare_timing_change(emc, cnd->old_rate); | ||
| 831 | break; | ||
| 832 | |||
| 833 | case POST_RATE_CHANGE: | ||
| 834 | err = emc_complete_timing_change(emc, cnd->new_rate); | ||
| 835 | break; | ||
| 836 | |||
| 837 | default: | ||
| 838 | return NOTIFY_DONE; | ||
| 839 | } | ||
| 840 | |||
| 841 | return notifier_from_errno(err); | ||
| 842 | } | ||
| 843 | |||
| 844 | static int load_one_timing_from_dt(struct tegra_emc *emc, | ||
| 845 | struct emc_timing *timing, | ||
| 846 | struct device_node *node) | ||
| 847 | { | ||
| 848 | u32 value; | ||
| 849 | int err; | ||
| 850 | |||
| 851 | err = of_property_read_u32(node, "clock-frequency", &value); | ||
| 852 | if (err) { | ||
| 853 | dev_err(emc->dev, "timing %pOF: failed to read rate: %d\n", | ||
| 854 | node, err); | ||
| 855 | return err; | ||
| 856 | } | ||
| 857 | |||
| 858 | timing->rate = value; | ||
| 859 | |||
| 860 | err = of_property_read_u32_array(node, "nvidia,emc-configuration", | ||
| 861 | timing->data, | ||
| 862 | ARRAY_SIZE(emc_timing_registers)); | ||
| 863 | if (err) { | ||
| 864 | dev_err(emc->dev, | ||
| 865 | "timing %pOF: failed to read emc timing data: %d\n", | ||
| 866 | node, err); | ||
| 867 | return err; | ||
| 868 | } | ||
| 869 | |||
| 870 | #define EMC_READ_BOOL(prop, dtprop) \ | ||
| 871 | timing->prop = of_property_read_bool(node, dtprop); | ||
| 872 | |||
| 873 | #define EMC_READ_U32(prop, dtprop) \ | ||
| 874 | err = of_property_read_u32(node, dtprop, &timing->prop); \ | ||
| 875 | if (err) { \ | ||
| 876 | dev_err(emc->dev, \ | ||
| 877 | "timing %pOFn: failed to read " #prop ": %d\n", \ | ||
| 878 | node, err); \ | ||
| 879 | return err; \ | ||
| 880 | } | ||
| 881 | |||
| 882 | EMC_READ_U32(emc_auto_cal_interval, "nvidia,emc-auto-cal-interval") | ||
| 883 | EMC_READ_U32(emc_mode_1, "nvidia,emc-mode-1") | ||
| 884 | EMC_READ_U32(emc_mode_2, "nvidia,emc-mode-2") | ||
| 885 | EMC_READ_U32(emc_mode_reset, "nvidia,emc-mode-reset") | ||
| 886 | EMC_READ_U32(emc_zcal_cnt_long, "nvidia,emc-zcal-cnt-long") | ||
| 887 | EMC_READ_BOOL(emc_cfg_dyn_self_ref, "nvidia,emc-cfg-dyn-self-ref") | ||
| 888 | EMC_READ_BOOL(emc_cfg_periodic_qrst, "nvidia,emc-cfg-periodic-qrst") | ||
| 889 | |||
| 890 | #undef EMC_READ_U32 | ||
| 891 | #undef EMC_READ_BOOL | ||
| 892 | |||
| 893 | dev_dbg(emc->dev, "%s: %pOF: rate %lu\n", __func__, node, timing->rate); | ||
| 894 | |||
| 895 | return 0; | ||
| 896 | } | ||
| 897 | |||
| 898 | static int cmp_timings(const void *_a, const void *_b) | ||
| 899 | { | ||
| 900 | const struct emc_timing *a = _a; | ||
| 901 | const struct emc_timing *b = _b; | ||
| 902 | |||
| 903 | if (a->rate < b->rate) | ||
| 904 | return -1; | ||
| 905 | |||
| 906 | if (a->rate > b->rate) | ||
| 907 | return 1; | ||
| 908 | |||
| 909 | return 0; | ||
| 910 | } | ||
| 911 | |||
| 912 | static int emc_check_mc_timings(struct tegra_emc *emc) | ||
| 913 | { | ||
| 914 | struct tegra_mc *mc = emc->mc; | ||
| 915 | unsigned int i; | ||
| 916 | |||
| 917 | if (emc->num_timings != mc->num_timings) { | ||
| 918 | dev_err(emc->dev, "emc/mc timings number mismatch: %u %u\n", | ||
| 919 | emc->num_timings, mc->num_timings); | ||
| 920 | return -EINVAL; | ||
| 921 | } | ||
| 922 | |||
| 923 | for (i = 0; i < mc->num_timings; i++) { | ||
| 924 | if (emc->timings[i].rate != mc->timings[i].rate) { | ||
| 925 | dev_err(emc->dev, | ||
| 926 | "emc/mc timing rate mismatch: %lu %lu\n", | ||
| 927 | emc->timings[i].rate, mc->timings[i].rate); | ||
| 928 | return -EINVAL; | ||
| 929 | } | ||
| 930 | } | ||
| 931 | |||
| 932 | return 0; | ||
| 933 | } | ||
| 934 | |||
| 935 | static int emc_load_timings_from_dt(struct tegra_emc *emc, | ||
| 936 | struct device_node *node) | ||
| 937 | { | ||
| 938 | struct device_node *child; | ||
| 939 | struct emc_timing *timing; | ||
| 940 | int child_count; | ||
| 941 | int err; | ||
| 942 | |||
| 943 | child_count = of_get_child_count(node); | ||
| 944 | if (!child_count) { | ||
| 945 | dev_err(emc->dev, "no memory timings in: %pOF\n", node); | ||
| 946 | return -EINVAL; | ||
| 947 | } | ||
| 948 | |||
| 949 | emc->timings = devm_kcalloc(emc->dev, child_count, sizeof(*timing), | ||
| 950 | GFP_KERNEL); | ||
| 951 | if (!emc->timings) | ||
| 952 | return -ENOMEM; | ||
| 953 | |||
| 954 | emc->num_timings = child_count; | ||
| 955 | timing = emc->timings; | ||
| 956 | |||
| 957 | for_each_child_of_node(node, child) { | ||
| 958 | err = load_one_timing_from_dt(emc, timing++, child); | ||
| 959 | if (err) { | ||
| 960 | of_node_put(child); | ||
| 961 | return err; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, | ||
| 966 | NULL); | ||
| 967 | |||
| 968 | err = emc_check_mc_timings(emc); | ||
| 969 | if (err) | ||
| 970 | return err; | ||
| 971 | |||
| 972 | dev_info(emc->dev, | ||
| 973 | "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", | ||
| 974 | emc->num_timings, | ||
| 975 | tegra_read_ram_code(), | ||
| 976 | emc->timings[0].rate / 1000000, | ||
| 977 | emc->timings[emc->num_timings - 1].rate / 1000000); | ||
| 978 | |||
| 979 | return 0; | ||
| 980 | } | ||
| 981 | |||
| 982 | static struct device_node *emc_find_node_by_ram_code(struct device *dev) | ||
| 983 | { | ||
| 984 | struct device_node *np; | ||
| 985 | u32 value, ram_code; | ||
| 986 | int err; | ||
| 987 | |||
| 988 | ram_code = tegra_read_ram_code(); | ||
| 989 | |||
| 990 | for_each_child_of_node(dev->of_node, np) { | ||
| 991 | err = of_property_read_u32(np, "nvidia,ram-code", &value); | ||
| 992 | if (err || value != ram_code) | ||
| 993 | continue; | ||
| 994 | |||
| 995 | return np; | ||
| 996 | } | ||
| 997 | |||
| 998 | dev_err(dev, "no memory timings for RAM code %u found in device-tree\n", | ||
| 999 | ram_code); | ||
| 1000 | |||
| 1001 | return NULL; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | static int emc_setup_hw(struct tegra_emc *emc) | ||
| 1005 | { | ||
| 1006 | u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; | ||
| 1007 | u32 fbio_cfg5, emc_cfg, emc_dbg; | ||
| 1008 | enum emc_dram_type dram_type; | ||
| 1009 | |||
| 1010 | fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5); | ||
| 1011 | dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK; | ||
| 1012 | |||
| 1013 | emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); | ||
| 1014 | |||
| 1015 | /* enable EMC and CAR to handshake on PLL divider/source changes */ | ||
| 1016 | emc_cfg |= EMC_CLKCHANGE_REQ_ENABLE; | ||
| 1017 | |||
| 1018 | /* configure clock change mode accordingly to DRAM type */ | ||
| 1019 | switch (dram_type) { | ||
| 1020 | case DRAM_TYPE_LPDDR2: | ||
| 1021 | emc_cfg |= EMC_CLKCHANGE_PD_ENABLE; | ||
| 1022 | emc_cfg &= ~EMC_CLKCHANGE_SR_ENABLE; | ||
| 1023 | break; | ||
| 1024 | |||
| 1025 | default: | ||
| 1026 | emc_cfg &= ~EMC_CLKCHANGE_SR_ENABLE; | ||
| 1027 | emc_cfg &= ~EMC_CLKCHANGE_PD_ENABLE; | ||
| 1028 | break; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | writel_relaxed(emc_cfg, emc->regs + EMC_CFG_2); | ||
| 1032 | |||
| 1033 | /* initialize interrupt */ | ||
| 1034 | writel_relaxed(intmask, emc->regs + EMC_INTMASK); | ||
| 1035 | writel_relaxed(0xffffffff, emc->regs + EMC_INTSTATUS); | ||
| 1036 | |||
| 1037 | /* ensure that unwanted debug features are disabled */ | ||
| 1038 | emc_dbg = readl_relaxed(emc->regs + EMC_DBG); | ||
| 1039 | emc_dbg |= EMC_DBG_CFG_PRIORITY; | ||
| 1040 | emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY; | ||
| 1041 | emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE; | ||
| 1042 | emc_dbg &= ~EMC_DBG_FORCE_UPDATE; | ||
| 1043 | writel_relaxed(emc_dbg, emc->regs + EMC_DBG); | ||
| 1044 | |||
| 1045 | return 0; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | static long emc_round_rate(unsigned long rate, | ||
| 1049 | unsigned long min_rate, | ||
| 1050 | unsigned long max_rate, | ||
| 1051 | void *arg) | ||
| 1052 | { | ||
| 1053 | struct emc_timing *timing = NULL; | ||
| 1054 | struct tegra_emc *emc = arg; | ||
| 1055 | unsigned int i; | ||
| 1056 | |||
| 1057 | min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate); | ||
| 1058 | |||
| 1059 | for (i = 0; i < emc->num_timings; i++) { | ||
| 1060 | if (emc->timings[i].rate < rate && i != emc->num_timings - 1) | ||
| 1061 | continue; | ||
| 1062 | |||
| 1063 | if (emc->timings[i].rate > max_rate) { | ||
| 1064 | i = max(i, 1u) - 1; | ||
| 1065 | |||
| 1066 | if (emc->timings[i].rate < min_rate) | ||
| 1067 | break; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | if (emc->timings[i].rate < min_rate) | ||
| 1071 | continue; | ||
| 1072 | |||
| 1073 | timing = &emc->timings[i]; | ||
| 1074 | break; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | if (!timing) { | ||
| 1078 | dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n", | ||
| 1079 | rate, min_rate, max_rate); | ||
| 1080 | return -EINVAL; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | return timing->rate; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | static int tegra_emc_probe(struct platform_device *pdev) | ||
| 1087 | { | ||
| 1088 | struct platform_device *mc; | ||
| 1089 | struct device_node *np; | ||
| 1090 | struct tegra_emc *emc; | ||
| 1091 | int err; | ||
| 1092 | |||
| 1093 | if (of_get_child_count(pdev->dev.of_node) == 0) { | ||
| 1094 | dev_info(&pdev->dev, | ||
| 1095 | "device-tree node doesn't have memory timings\n"); | ||
| 1096 | return 0; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0); | ||
| 1100 | if (!np) { | ||
| 1101 | dev_err(&pdev->dev, "could not get memory controller node\n"); | ||
| 1102 | return -ENOENT; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | mc = of_find_device_by_node(np); | ||
| 1106 | of_node_put(np); | ||
| 1107 | if (!mc) | ||
| 1108 | return -ENOENT; | ||
| 1109 | |||
| 1110 | np = emc_find_node_by_ram_code(&pdev->dev); | ||
| 1111 | if (!np) | ||
| 1112 | return -EINVAL; | ||
| 1113 | |||
| 1114 | emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL); | ||
| 1115 | if (!emc) { | ||
| 1116 | of_node_put(np); | ||
| 1117 | return -ENOMEM; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | emc->mc = platform_get_drvdata(mc); | ||
| 1121 | if (!emc->mc) | ||
| 1122 | return -EPROBE_DEFER; | ||
| 1123 | |||
| 1124 | init_completion(&emc->clk_handshake_complete); | ||
| 1125 | emc->clk_nb.notifier_call = emc_clk_change_notify; | ||
| 1126 | emc->dev = &pdev->dev; | ||
| 1127 | |||
| 1128 | err = emc_load_timings_from_dt(emc, np); | ||
| 1129 | of_node_put(np); | ||
| 1130 | if (err) | ||
| 1131 | return err; | ||
| 1132 | |||
| 1133 | emc->regs = devm_platform_ioremap_resource(pdev, 0); | ||
| 1134 | if (IS_ERR(emc->regs)) | ||
| 1135 | return PTR_ERR(emc->regs); | ||
| 1136 | |||
| 1137 | err = emc_setup_hw(emc); | ||
| 1138 | if (err) | ||
| 1139 | return err; | ||
| 1140 | |||
| 1141 | err = platform_get_irq(pdev, 0); | ||
| 1142 | if (err < 0) { | ||
| 1143 | dev_err(&pdev->dev, "interrupt not specified: %d\n", err); | ||
| 1144 | return err; | ||
| 1145 | } | ||
| 1146 | emc->irq = err; | ||
| 1147 | |||
| 1148 | err = devm_request_irq(&pdev->dev, emc->irq, tegra_emc_isr, 0, | ||
| 1149 | dev_name(&pdev->dev), emc); | ||
| 1150 | if (err) { | ||
| 1151 | dev_err(&pdev->dev, "failed to request irq: %d\n", err); | ||
| 1152 | return err; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | tegra20_clk_set_emc_round_callback(emc_round_rate, emc); | ||
| 1156 | |||
| 1157 | emc->clk = devm_clk_get(&pdev->dev, "emc"); | ||
| 1158 | if (IS_ERR(emc->clk)) { | ||
| 1159 | err = PTR_ERR(emc->clk); | ||
| 1160 | dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); | ||
| 1161 | goto unset_cb; | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | err = clk_notifier_register(emc->clk, &emc->clk_nb); | ||
| 1165 | if (err) { | ||
| 1166 | dev_err(&pdev->dev, "failed to register clk notifier: %d\n", | ||
| 1167 | err); | ||
| 1168 | goto unset_cb; | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | platform_set_drvdata(pdev, emc); | ||
| 1172 | |||
| 1173 | return 0; | ||
| 1174 | |||
| 1175 | unset_cb: | ||
| 1176 | tegra20_clk_set_emc_round_callback(NULL, NULL); | ||
| 1177 | |||
| 1178 | return err; | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | static int tegra_emc_suspend(struct device *dev) | ||
| 1182 | { | ||
| 1183 | struct tegra_emc *emc = dev_get_drvdata(dev); | ||
| 1184 | |||
| 1185 | /* | ||
| 1186 | * Suspending in a bad state will hang machine. The "prepared" var | ||
| 1187 | * shall be always false here unless it's a kernel bug that caused | ||
| 1188 | * suspending in a wrong order. | ||
| 1189 | */ | ||
| 1190 | if (WARN_ON(emc->prepared) || emc->bad_state) | ||
| 1191 | return -EINVAL; | ||
| 1192 | |||
| 1193 | emc->bad_state = true; | ||
| 1194 | |||
| 1195 | return 0; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | static int tegra_emc_resume(struct device *dev) | ||
| 1199 | { | ||
| 1200 | struct tegra_emc *emc = dev_get_drvdata(dev); | ||
| 1201 | |||
| 1202 | emc_setup_hw(emc); | ||
| 1203 | emc->bad_state = false; | ||
| 1204 | |||
| 1205 | return 0; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | static const struct dev_pm_ops tegra_emc_pm_ops = { | ||
| 1209 | .suspend = tegra_emc_suspend, | ||
| 1210 | .resume = tegra_emc_resume, | ||
| 1211 | }; | ||
| 1212 | |||
| 1213 | static const struct of_device_id tegra_emc_of_match[] = { | ||
| 1214 | { .compatible = "nvidia,tegra30-emc", }, | ||
| 1215 | {}, | ||
| 1216 | }; | ||
| 1217 | |||
| 1218 | static struct platform_driver tegra_emc_driver = { | ||
| 1219 | .probe = tegra_emc_probe, | ||
| 1220 | .driver = { | ||
| 1221 | .name = "tegra30-emc", | ||
| 1222 | .of_match_table = tegra_emc_of_match, | ||
| 1223 | .pm = &tegra_emc_pm_ops, | ||
| 1224 | .suppress_bind_attrs = true, | ||
| 1225 | }, | ||
| 1226 | }; | ||
| 1227 | |||
| 1228 | static int __init tegra_emc_init(void) | ||
| 1229 | { | ||
| 1230 | return platform_driver_register(&tegra_emc_driver); | ||
| 1231 | } | ||
| 1232 | subsys_initcall(tegra_emc_init); | ||
| @@ -10,6 +10,27 @@ · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | #include "mc.h" | 11 | #include "mc.h" |
| 12 | 12 | ||
| 13 | static const unsigned long tegra30_mc_emem_regs[] = { | ||
| 14 | MC_EMEM_ARB_CFG, | ||
| 15 | MC_EMEM_ARB_OUTSTANDING_REQ, | ||
| 16 | MC_EMEM_ARB_TIMING_RCD, | ||
| 17 | MC_EMEM_ARB_TIMING_RP, | ||
| 18 | MC_EMEM_ARB_TIMING_RC, | ||
| 19 | MC_EMEM_ARB_TIMING_RAS, | ||
| 20 | MC_EMEM_ARB_TIMING_FAW, | ||
| 21 | MC_EMEM_ARB_TIMING_RRD, | ||
| 22 | MC_EMEM_ARB_TIMING_RAP2PRE, | ||
| 23 | MC_EMEM_ARB_TIMING_WAP2PRE, | ||
| 24 | MC_EMEM_ARB_TIMING_R2R, | ||
| 25 | MC_EMEM_ARB_TIMING_W2W, | ||
| 26 | MC_EMEM_ARB_TIMING_R2W, | ||
| 27 | MC_EMEM_ARB_TIMING_W2R, | ||
| 28 | MC_EMEM_ARB_DA_TURNS, | ||
| 29 | MC_EMEM_ARB_DA_COVERS, | ||
| 30 | MC_EMEM_ARB_MISC0, | ||
| 31 | MC_EMEM_ARB_RING1_THROTTLE, | ||
| 32 | }; | ||
| 33 | |||
| 13 | static const struct tegra_mc_client tegra30_mc_clients[] = { | 34 | static const struct tegra_mc_client tegra30_mc_clients[] = { |
| 14 | { | 35 | { |
| 15 | .id = 0x00, | 36 | .id = 0x00, |
| @@ -931,16 +952,19 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { · 915 unmodified lines | |||
| 931 | { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, | 952 | { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, |
| 932 | }; | 953 | }; |
| 933 | 954 | ||
| 934 | static const unsigned int tegra30_group_display[] = { | 955 | static const unsigned int tegra30_group_drm[] = { |
| 935 | TEGRA_SWGROUP_DC, | 956 | TEGRA_SWGROUP_DC, |
| 936 | TEGRA_SWGROUP_DCB, | 957 | TEGRA_SWGROUP_DCB, |
| 958 | TEGRA_SWGROUP_G2, | ||
| 959 | TEGRA_SWGROUP_NV, | ||
| 960 | TEGRA_SWGROUP_NV2, | ||
| 937 | }; | 961 | }; |
| 938 | 962 | ||
| 939 | static const struct tegra_smmu_group_soc tegra30_groups[] = { | 963 | static const struct tegra_smmu_group_soc tegra30_groups[] = { |
| 940 | { | 964 | { |
| 941 | .name = "display", | 965 | .name = "drm", |
| 942 | .swgroups = tegra30_group_display, | 966 | .swgroups = tegra30_group_drm, |
| 943 | .num_swgroups = ARRAY_SIZE(tegra30_group_display), | 967 | .num_swgroups = ARRAY_SIZE(tegra30_group_drm), |
| 944 | }, | 968 | }, |
| 945 | }; | 969 | }; |
| 946 | 970 | ||
| @@ -994,6 +1018,8 @@ const struct tegra_mc_soc tegra30_mc_soc = { · 47 unmodified lines | |||
| 994 | .atom_size = 16, | 1018 | .atom_size = 16, |
| 995 | .client_id_mask = 0x7f, | 1019 | .client_id_mask = 0x7f, |
| 996 | .smmu = &tegra30_smmu_soc, | 1020 | .smmu = &tegra30_smmu_soc, |
| 1021 | .emem_regs = tegra30_mc_emem_regs, | ||
| 1022 | .num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs), | ||
| 997 | .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | | 1023 | .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | |
| 998 | MC_INT_DECERR_EMEM, | 1024 | MC_INT_DECERR_EMEM, |
| 999 | .reset_ops = &tegra_mc_reset_ops_common, | 1025 | .reset_ops = &tegra_mc_reset_ops_common, |
| @@ -17,14 +17,18 @@ · 16 unmodified lines | |||
| 17 | static int meson_efuse_read(void *context, unsigned int offset, | 17 | static int meson_efuse_read(void *context, unsigned int offset, |
| 18 | void *val, size_t bytes) | 18 | void *val, size_t bytes) |
| 19 | { | 19 | { |
| 20 | return meson_sm_call_read((u8 *)val, bytes, SM_EFUSE_READ, offset, | 20 | struct meson_sm_firmware *fw = context; |
| 21 | |||
| 22 | return meson_sm_call_read(fw, (u8 *)val, bytes, SM_EFUSE_READ, offset, | ||
| 21 | bytes, 0, 0, 0); | 23 | bytes, 0, 0, 0); |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | static int meson_efuse_write(void *context, unsigned int offset, | 26 | static int meson_efuse_write(void *context, unsigned int offset, |
| 25 | void *val, size_t bytes) | 27 | void *val, size_t bytes) |
| 26 | { | 28 | { |
| 27 | return meson_sm_call_write((u8 *)val, bytes, SM_EFUSE_WRITE, offset, | 29 | struct meson_sm_firmware *fw = context; |
| 30 | |||
| 31 | return meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset, | ||
| 28 | bytes, 0, 0, 0); | 32 | bytes, 0, 0, 0); |
| 29 | } | 33 | } |
| 30 | 34 | ||
| @@ -37,12 +41,25 @@ MODULE_DEVICE_TABLE(of, meson_efuse_match); · 6 unmodified lines | |||
| 37 | static int meson_efuse_probe(struct platform_device *pdev) | 41 | static int meson_efuse_probe(struct platform_device *pdev) |
| 38 | { | 42 | { |
| 39 | struct device *dev = &pdev->dev; | 43 | struct device *dev = &pdev->dev; |
| 44 | struct meson_sm_firmware *fw; | ||
| 45 | struct device_node *sm_np; | ||
| 40 | struct nvmem_device *nvmem; | 46 | struct nvmem_device *nvmem; |
| 41 | struct nvmem_config *econfig; | 47 | struct nvmem_config *econfig; |
| 42 | struct clk *clk; | 48 | struct clk *clk; |
| 43 | unsigned int size; | 49 | unsigned int size; |
| 44 | int ret; | 50 | int ret; |
| 45 | 51 | ||
| 52 | sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0); | ||
| 53 | if (!sm_np) { | ||
| 54 | dev_err(&pdev->dev, "no secure-monitor node\n"); | ||
| 55 | return -ENODEV; | ||
| 56 | } | ||
| 57 | |||
| 58 | fw = meson_sm_get(sm_np); | ||
| 59 | of_node_put(sm_np); | ||
| 60 | if (!fw) | ||
| 61 | return -EPROBE_DEFER; | ||
| 62 | |||
| 46 | clk = devm_clk_get(dev, NULL); | 63 | clk = devm_clk_get(dev, NULL); |
| 47 | if (IS_ERR(clk)) { | 64 | if (IS_ERR(clk)) { |
| 48 | ret = PTR_ERR(clk); | 65 | ret = PTR_ERR(clk); |
| @@ -65,7 +82,7 @@ static int meson_efuse_probe(struct platform_device *pdev) · 16 unmodified lines | |||
| 65 | return ret; | 82 | return ret; |
| 66 | } | 83 | } |
| 67 | 84 | ||
| 68 | if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) { | 85 | if (meson_sm_call(fw, SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) { |
| 69 | dev_err(dev, "failed to get max user"); | 86 | dev_err(dev, "failed to get max user"); |
| 70 | return -EINVAL; | 87 | return -EINVAL; |
| 71 | } | 88 | } |
| @@ -81,6 +98,7 @@ static int meson_efuse_probe(struct platform_device *pdev) · 9 unmodified lines | |||
| 81 | econfig->reg_read = meson_efuse_read; | 98 | econfig->reg_read = meson_efuse_read; |
| 82 | econfig->reg_write = meson_efuse_write; | 99 | econfig->reg_write = meson_efuse_write; |
| 83 | econfig->size = size; | 100 | econfig->size = size; |
| 101 | econfig->priv = fw; | ||
| 84 | 102 | ||
| 85 | nvmem = devm_nvmem_register(&pdev->dev, econfig); | 103 | nvmem = devm_nvmem_register(&pdev->dev, econfig); |
| 86 | 104 | ||
| @@ -103,3 +103,14 @@ config PHY_PXA_USB · 102 unmodified lines | |||
| 103 | The PHY driver will be used by Marvell udc/ehci/otg driver. | 103 | The PHY driver will be used by Marvell udc/ehci/otg driver. |
| 104 | 104 | ||
| 105 | To compile this driver as a module, choose M here. | 105 | To compile this driver as a module, choose M here. |
| 106 | |||
| 107 | config PHY_MMP3_USB | ||
| 108 | tristate "Marvell MMP3 USB PHY Driver" | ||
| 109 | depends on MACH_MMP3_DT || COMPILE_TEST | ||
| 110 | select GENERIC_PHY | ||
| 111 | help | ||
| 112 | Enable this to support Marvell MMP3 USB PHY driver for Marvell | ||
| 113 | SoC. This driver will do the PHY initialization and shutdown. | ||
| 114 | The PHY driver will be used by Marvell udc/ehci/otg driver. | ||
| 115 | |||
| 116 | To compile this driver as a module, choose M here. | ||
| @@ -2,6 +2,7 @@ · 1 unmodified line | |||
| 2 | obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o | 2 | obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o |
| 3 | obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o | 3 | obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o |
| 4 | obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o | 4 | obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o |
| 5 | obj-$(CONFIG_PHY_MMP3_USB) += phy-mmp3-usb.o | ||
| 5 | obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o | 6 | obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o |
| 6 | obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o | 7 | obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o |
| 7 | obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o | 8 | obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o |
| @@ -0,0 +1,291 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2011 Marvell International Ltd. All rights reserved. | ||
| 4 | * Copyright (C) 2018,2019 Lubomir Rintel <lkundrak@v3.sk> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/delay.h> | ||
| 8 | #include <linux/io.h> | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/phy/phy.h> | ||
| 11 | #include <linux/platform_device.h> | ||
| 12 | #include <linux/soc/mmp/cputype.h> | ||
| 13 | |||
| 14 | #define USB2_PLL_REG0 0x4 | ||
| 15 | #define USB2_PLL_REG1 0x8 | ||
| 16 | #define USB2_TX_REG0 0x10 | ||
| 17 | #define USB2_TX_REG1 0x14 | ||
| 18 | #define USB2_TX_REG2 0x18 | ||
| 19 | #define USB2_RX_REG0 0x20 | ||
| 20 | #define USB2_RX_REG1 0x24 | ||
| 21 | #define USB2_RX_REG2 0x28 | ||
| 22 | #define USB2_ANA_REG0 0x30 | ||
| 23 | #define USB2_ANA_REG1 0x34 | ||
| 24 | #define USB2_ANA_REG2 0x38 | ||
| 25 | #define USB2_DIG_REG0 0x3C | ||
| 26 | #define USB2_DIG_REG1 0x40 | ||
| 27 | #define USB2_DIG_REG2 0x44 | ||
| 28 | #define USB2_DIG_REG3 0x48 | ||
| 29 | #define USB2_TEST_REG0 0x4C | ||
| 30 | #define USB2_TEST_REG1 0x50 | ||
| 31 | #define USB2_TEST_REG2 0x54 | ||
| 32 | #define USB2_CHARGER_REG0 0x58 | ||
| 33 | #define USB2_OTG_REG0 0x5C | ||
| 34 | #define USB2_PHY_MON0 0x60 | ||
| 35 | #define USB2_RESETVE_REG0 0x64 | ||
| 36 | #define USB2_ICID_REG0 0x78 | ||
| 37 | #define USB2_ICID_REG1 0x7C | ||
| 38 | |||
| 39 | /* USB2_PLL_REG0 */ | ||
| 40 | |||
| 41 | /* This is for Ax stepping */ | ||
| 42 | #define USB2_PLL_FBDIV_SHIFT_MMP3 0 | ||
| 43 | #define USB2_PLL_FBDIV_MASK_MMP3 (0xFF << 0) | ||
| 44 | |||
| 45 | #define USB2_PLL_REFDIV_SHIFT_MMP3 8 | ||
| 46 | #define USB2_PLL_REFDIV_MASK_MMP3 (0xF << 8) | ||
| 47 | |||
| 48 | #define USB2_PLL_VDD12_SHIFT_MMP3 12 | ||
| 49 | #define USB2_PLL_VDD18_SHIFT_MMP3 14 | ||
| 50 | |||
| 51 | /* This is for B0 stepping */ | ||
| 52 | #define USB2_PLL_FBDIV_SHIFT_MMP3_B0 0 | ||
| 53 | #define USB2_PLL_REFDIV_SHIFT_MMP3_B0 9 | ||
| 54 | #define USB2_PLL_VDD18_SHIFT_MMP3_B0 14 | ||
| 55 | #define USB2_PLL_FBDIV_MASK_MMP3_B0 0x01FF | ||
| 56 | #define USB2_PLL_REFDIV_MASK_MMP3_B0 0x3E00 | ||
| 57 | |||
| 58 | #define USB2_PLL_CAL12_SHIFT_MMP3 0 | ||
| 59 | #define USB2_PLL_CALI12_MASK_MMP3 (0x3 << 0) | ||
| 60 | |||
| 61 | #define USB2_PLL_VCOCAL_START_SHIFT_MMP3 2 | ||
| 62 | |||
| 63 | #define USB2_PLL_KVCO_SHIFT_MMP3 4 | ||
| 64 | #define USB2_PLL_KVCO_MASK_MMP3 (0x7<<4) | ||
| 65 | |||
| 66 | #define USB2_PLL_ICP_SHIFT_MMP3 8 | ||
| 67 | #define USB2_PLL_ICP_MASK_MMP3 (0x7<<8) | ||
| 68 | |||
| 69 | #define USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 12 | ||
| 70 | |||
| 71 | #define USB2_PLL_PU_PLL_SHIFT_MMP3 13 | ||
| 72 | #define USB2_PLL_PU_PLL_MASK (0x1 << 13) | ||
| 73 | |||
| 74 | #define USB2_PLL_READY_MASK_MMP3 (0x1 << 15) | ||
| 75 | |||
| 76 | /* USB2_TX_REG0 */ | ||
| 77 | #define USB2_TX_IMPCAL_VTH_SHIFT_MMP3 8 | ||
| 78 | #define USB2_TX_IMPCAL_VTH_MASK_MMP3 (0x7 << 8) | ||
| 79 | |||
| 80 | #define USB2_TX_RCAL_START_SHIFT_MMP3 13 | ||
| 81 | |||
| 82 | /* USB2_TX_REG1 */ | ||
| 83 | #define USB2_TX_CK60_PHSEL_SHIFT_MMP3 0 | ||
| 84 | #define USB2_TX_CK60_PHSEL_MASK_MMP3 (0xf << 0) | ||
| 85 | |||
| 86 | #define USB2_TX_AMP_SHIFT_MMP3 4 | ||
| 87 | #define USB2_TX_AMP_MASK_MMP3 (0x7 << 4) | ||
| 88 | |||
| 89 | #define USB2_TX_VDD12_SHIFT_MMP3 8 | ||
| 90 | #define USB2_TX_VDD12_MASK_MMP3 (0x3 << 8) | ||
| 91 | |||
| 92 | /* USB2_TX_REG2 */ | ||
| 93 | #define USB2_TX_DRV_SLEWRATE_SHIFT 10 | ||
| 94 | |||
| 95 | /* USB2_RX_REG0 */ | ||
| 96 | #define USB2_RX_SQ_THRESH_SHIFT_MMP3 4 | ||
| 97 | #define USB2_RX_SQ_THRESH_MASK_MMP3 (0xf << 4) | ||
| 98 | |||
| 99 | #define USB2_RX_SQ_LENGTH_SHIFT_MMP3 10 | ||
| 100 | #define USB2_RX_SQ_LENGTH_MASK_MMP3 (0x3 << 10) | ||
| 101 | |||
| 102 | /* USB2_ANA_REG1*/ | ||
| 103 | #define USB2_ANA_PU_ANA_SHIFT_MMP3 14 | ||
| 104 | |||
| 105 | /* USB2_OTG_REG0 */ | ||
| 106 | #define USB2_OTG_PU_OTG_SHIFT_MMP3 3 | ||
| 107 | |||
| 108 | struct mmp3_usb_phy { | ||
| 109 | struct phy *phy; | ||
| 110 | void __iomem *base; | ||
| 111 | }; | ||
| 112 | |||
| 113 | static unsigned int u2o_get(void __iomem *base, unsigned int offset) | ||
| 114 | { | ||
| 115 | return readl_relaxed(base + offset); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void u2o_set(void __iomem *base, unsigned int offset, | ||
| 119 | unsigned int value) | ||
| 120 | { | ||
| 121 | u32 reg; | ||
| 122 | |||
| 123 | reg = readl_relaxed(base + offset); | ||
| 124 | reg |= value; | ||
| 125 | writel_relaxed(reg, base + offset); | ||
| 126 | readl_relaxed(base + offset); | ||
| 127 | } | ||
| 128 | |||
| 129 | static void u2o_clear(void __iomem *base, unsigned int offset, | ||
| 130 | unsigned int value) | ||
| 131 | { | ||
| 132 | u32 reg; | ||
| 133 | |||
| 134 | reg = readl_relaxed(base + offset); | ||
| 135 | reg &= ~value; | ||
| 136 | writel_relaxed(reg, base + offset); | ||
| 137 | readl_relaxed(base + offset); | ||
| 138 | } | ||
| 139 | |||
| 140 | static int mmp3_usb_phy_init(struct phy *phy) | ||
| 141 | { | ||
| 142 | struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); | ||
| 143 | void __iomem *base = mmp3_usb_phy->base; | ||
| 144 | |||
| 145 | if (cpu_is_mmp3_a0()) { | ||
| 146 | u2o_clear(base, USB2_PLL_REG0, (USB2_PLL_FBDIV_MASK_MMP3 | ||
| 147 | | USB2_PLL_REFDIV_MASK_MMP3)); | ||
| 148 | u2o_set(base, USB2_PLL_REG0, | ||
| 149 | 0xd << USB2_PLL_REFDIV_SHIFT_MMP3 | ||
| 150 | | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3); | ||
| 151 | } else if (cpu_is_mmp3_b0()) { | ||
| 152 | u2o_clear(base, USB2_PLL_REG0, USB2_PLL_REFDIV_MASK_MMP3_B0 | ||
| 153 | | USB2_PLL_FBDIV_MASK_MMP3_B0); | ||
| 154 | u2o_set(base, USB2_PLL_REG0, | ||
| 155 | 0xd << USB2_PLL_REFDIV_SHIFT_MMP3_B0 | ||
| 156 | | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3_B0); | ||
| 157 | } else { | ||
| 158 | dev_err(&phy->dev, "unsupported silicon revision\n"); | ||
| 159 | return -ENODEV; | ||
| 160 | } | ||
| 161 | |||
| 162 | u2o_clear(base, USB2_PLL_REG1, USB2_PLL_PU_PLL_MASK | ||
| 163 | | USB2_PLL_ICP_MASK_MMP3 | ||
| 164 | | USB2_PLL_KVCO_MASK_MMP3 | ||
| 165 | | USB2_PLL_CALI12_MASK_MMP3); | ||
| 166 | u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_PU_PLL_SHIFT_MMP3 | ||
| 167 | | 1 << USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 | ||
| 168 | | 3 << USB2_PLL_ICP_SHIFT_MMP3 | ||
| 169 | | 3 << USB2_PLL_KVCO_SHIFT_MMP3 | ||
| 170 | | 3 << USB2_PLL_CAL12_SHIFT_MMP3); | ||
| 171 | |||
| 172 | u2o_clear(base, USB2_TX_REG0, USB2_TX_IMPCAL_VTH_MASK_MMP3); | ||
| 173 | u2o_set(base, USB2_TX_REG0, 2 << USB2_TX_IMPCAL_VTH_SHIFT_MMP3); | ||
| 174 | |||
| 175 | u2o_clear(base, USB2_TX_REG1, USB2_TX_VDD12_MASK_MMP3 | ||
| 176 | | USB2_TX_AMP_MASK_MMP3 | ||
| 177 | | USB2_TX_CK60_PHSEL_MASK_MMP3); | ||
| 178 | u2o_set(base, USB2_TX_REG1, 3 << USB2_TX_VDD12_SHIFT_MMP3 | ||
| 179 | | 4 << USB2_TX_AMP_SHIFT_MMP3 | ||
| 180 | | 4 << USB2_TX_CK60_PHSEL_SHIFT_MMP3); | ||
| 181 | |||
| 182 | u2o_clear(base, USB2_TX_REG2, 3 << USB2_TX_DRV_SLEWRATE_SHIFT); | ||
| 183 | u2o_set(base, USB2_TX_REG2, 2 << USB2_TX_DRV_SLEWRATE_SHIFT); | ||
| 184 | |||
| 185 | u2o_clear(base, USB2_RX_REG0, USB2_RX_SQ_THRESH_MASK_MMP3); | ||
| 186 | u2o_set(base, USB2_RX_REG0, 0xa << USB2_RX_SQ_THRESH_SHIFT_MMP3); | ||
| 187 | |||
| 188 | u2o_set(base, USB2_ANA_REG1, 0x1 << USB2_ANA_PU_ANA_SHIFT_MMP3); | ||
| 189 | |||
| 190 | u2o_set(base, USB2_OTG_REG0, 0x1 << USB2_OTG_PU_OTG_SHIFT_MMP3); | ||
| 191 | |||
| 192 | return 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | static int mmp3_usb_phy_calibrate(struct phy *phy) | ||
| 196 | { | ||
| 197 | struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); | ||
| 198 | void __iomem *base = mmp3_usb_phy->base; | ||
| 199 | int loops; | ||
| 200 | |||
| 201 | /* | ||
| 202 | * PLL VCO and TX Impedance Calibration Timing: | ||
| 203 | * | ||
| 204 | * _____________________________________ | ||
| 205 | * PU __________| | ||
| 206 | * _____________________________ | ||
| 207 | * VCOCAL START _________| | ||
| 208 | * ___ | ||
| 209 | * REG_RCAL_START ________________| |________|_______ | ||
| 210 | * | 200us | 400us | 40| 400us | USB PHY READY | ||
| 211 | */ | ||
| 212 | |||
| 213 | udelay(200); | ||
| 214 | u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_VCOCAL_START_SHIFT_MMP3); | ||
| 215 | udelay(400); | ||
| 216 | u2o_set(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); | ||
| 217 | udelay(40); | ||
| 218 | u2o_clear(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); | ||
| 219 | udelay(400); | ||
| 220 | |||
| 221 | loops = 0; | ||
| 222 | while ((u2o_get(base, USB2_PLL_REG1) & USB2_PLL_READY_MASK_MMP3) == 0) { | ||
| 223 | mdelay(1); | ||
| 224 | loops++; | ||
| 225 | if (loops > 100) { | ||
| 226 | dev_err(&phy->dev, "PLL_READY not set after 100mS.\n"); | ||
| 227 | return -ETIMEDOUT; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | static const struct phy_ops mmp3_usb_phy_ops = { | ||
| 235 | .init = mmp3_usb_phy_init, | ||
| 236 | .calibrate = mmp3_usb_phy_calibrate, | ||
| 237 | .owner = THIS_MODULE, | ||
| 238 | }; | ||
| 239 | |||
| 240 | static const struct of_device_id mmp3_usb_phy_of_match[] = { | ||
| 241 | { .compatible = "marvell,mmp3-usb-phy", }, | ||
| 242 | { }, | ||
| 243 | }; | ||
| 244 | MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match); | ||
| 245 | |||
| 246 | static int mmp3_usb_phy_probe(struct platform_device *pdev) | ||
| 247 | { | ||
| 248 | struct device *dev = &pdev->dev; | ||
| 249 | struct resource *resource; | ||
| 250 | struct mmp3_usb_phy *mmp3_usb_phy; | ||
| 251 | struct phy_provider *provider; | ||
| 252 | |||
| 253 | mmp3_usb_phy = devm_kzalloc(dev, sizeof(*mmp3_usb_phy), GFP_KERNEL); | ||
| 254 | if (!mmp3_usb_phy) | ||
| 255 | return -ENOMEM; | ||
| 256 | |||
| 257 | resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 258 | mmp3_usb_phy->base = devm_ioremap_resource(dev, resource); | ||
| 259 | if (IS_ERR(mmp3_usb_phy->base)) { | ||
| 260 | dev_err(dev, "failed to remap PHY regs\n"); | ||
| 261 | return PTR_ERR(mmp3_usb_phy->base); | ||
| 262 | } | ||
| 263 | |||
| 264 | mmp3_usb_phy->phy = devm_phy_create(dev, NULL, &mmp3_usb_phy_ops); | ||
| 265 | if (IS_ERR(mmp3_usb_phy->phy)) { | ||
| 266 | dev_err(dev, "failed to create PHY\n"); | ||
| 267 | return PTR_ERR(mmp3_usb_phy->phy); | ||
| 268 | } | ||
| 269 | |||
| 270 | phy_set_drvdata(mmp3_usb_phy->phy, mmp3_usb_phy); | ||
| 271 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
| 272 | if (IS_ERR(provider)) { | ||
| 273 | dev_err(dev, "failed to register PHY provider\n"); | ||
| 274 | return PTR_ERR(provider); | ||
| 275 | } | ||
| 276 | |||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | |||
| 280 | static struct platform_driver mmp3_usb_phy_driver = { | ||
| 281 | .probe = mmp3_usb_phy_probe, | ||
| 282 | .driver = { | ||
| 283 | .name = "mmp3-usb-phy", | ||
| 284 | .of_match_table = mmp3_usb_phy_of_match, | ||
| 285 | }, | ||
| 286 | }; | ||
| 287 | module_platform_driver(mmp3_usb_phy_driver); | ||
| 288 | |||
| 289 | MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); | ||
| 290 | MODULE_DESCRIPTION("Marvell MMP3 USB PHY Driver"); | ||
| 291 | MODULE_LICENSE("GPL v2"); | ||
| @@ -129,7 +129,7 @@ config RESET_SCMI · 128 unmodified lines | |||
| 129 | 129 | ||
| 130 | config RESET_SIMPLE | 130 | config RESET_SIMPLE |
| 131 | bool "Simple Reset Controller Driver" if COMPILE_TEST | 131 | bool "Simple Reset Controller Driver" if COMPILE_TEST |
| 132 | default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN || ARC | 132 | default ARCH_AGILEX || ARCH_ASPEED || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARC |
| 133 | help | 133 | help |
| 134 | This enables a simple reset controller driver for reset lines that | 134 | This enables a simple reset controller driver for reset lines that |
| 135 | that can be asserted and deasserted by toggling bits in a contiguous, | 135 | that can be asserted and deasserted by toggling bits in a contiguous, |
| @@ -138,10 +138,11 @@ config RESET_SIMPLE · 2 unmodified lines | |||
| 138 | Currently this driver supports: | 138 | Currently this driver supports: |
| 139 | - Altera SoCFPGAs | 139 | - Altera SoCFPGAs |
| 140 | - ASPEED BMC SoCs | 140 | - ASPEED BMC SoCs |
| 141 | - Bitmain BM1880 SoC | ||
| 142 | - Realtek SoCs | ||
| 141 | - RCC reset controller in STM32 MCUs | 143 | - RCC reset controller in STM32 MCUs |
| 142 | - Allwinner SoCs | 144 | - Allwinner SoCs |
| 143 | - ZTE's zx2967 family | 145 | - ZTE's zx2967 family |
| 144 | - Bitmain BM1880 SoC | ||
| 145 | 146 | ||
| 146 | config RESET_STM32MP157 | 147 | config RESET_STM32MP157 |
| 147 | bool "STM32MP157 Reset Driver" if COMPILE_TEST | 148 | bool "STM32MP157 Reset Driver" if COMPILE_TEST |
| @@ -77,8 +77,10 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) · 76 unmodified lines | |||
| 77 | * @rcdev: a pointer to the reset controller device | 77 | * @rcdev: a pointer to the reset controller device |
| 78 | * @reset_spec: reset line specifier as found in the device tree | 78 | * @reset_spec: reset line specifier as found in the device tree |
| 79 | * | 79 | * |
| 80 | * This simple translation function should be used for reset controllers | 80 | * This static translation function is used by default if of_xlate in |
| 81 | * with 1:1 mapping, where reset lines can be indexed by number without gaps. | 81 | * :c:type:`reset_controller_dev` is not set. It is useful for all reset |
| 82 | * controllers with 1:1 mapping, where reset lines can be indexed by number | ||
| 83 | * without gaps. | ||
| 82 | */ | 84 | */ |
| 83 | static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, | 85 | static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, |
| 84 | const struct of_phandle_args *reset_spec) | 86 | const struct of_phandle_args *reset_spec) |
| @@ -333,7 +335,6 @@ EXPORT_SYMBOL_GPL(reset_control_reset); · 248 unmodified lines | |||
| 333 | * internal state to be reset, but must be prepared for this to happen. | 335 | * internal state to be reset, but must be prepared for this to happen. |
| 334 | * Consumers must not use reset_control_reset on shared reset lines when | 336 | * Consumers must not use reset_control_reset on shared reset lines when |
| 335 | * reset_control_(de)assert has been used. | 337 | * reset_control_(de)assert has been used. |
| 336 | * return 0. | ||
| 337 | * | 338 | * |
| 338 | * If rstc is NULL it is an optional reset and the function will just | 339 | * If rstc is NULL it is an optional reset and the function will just |
| 339 | * return 0. | 340 | * return 0. |
| @@ -392,7 +393,6 @@ EXPORT_SYMBOL_GPL(reset_control_assert); · 52 unmodified lines | |||
| 392 | * After calling this function, the reset is guaranteed to be deasserted. | 393 | * After calling this function, the reset is guaranteed to be deasserted. |
| 393 | * Consumers must not use reset_control_reset on shared reset lines when | 394 | * Consumers must not use reset_control_reset on shared reset lines when |
| 394 | * reset_control_(de)assert has been used. | 395 | * reset_control_(de)assert has been used. |
| 395 | * return 0. | ||
| 396 | * | 396 | * |
| 397 | * If rstc is NULL it is an optional reset and the function will just | 397 | * If rstc is NULL it is an optional reset and the function will just |
| 398 | * return 0. | 398 | * return 0. |
| @@ -56,7 +56,7 @@ static int hi3660_reset_dev(struct reset_controller_dev *rcdev, · 55 unmodified lines | |||
| 56 | return hi3660_reset_deassert(rcdev, idx); | 56 | return hi3660_reset_deassert(rcdev, idx); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | static struct reset_control_ops hi3660_reset_ops = { | 59 | static const struct reset_control_ops hi3660_reset_ops = { |
| 60 | .reset = hi3660_reset_dev, | 60 | .reset = hi3660_reset_dev, |
| 61 | .assert = hi3660_reset_assert, | 61 | .assert = hi3660_reset_assert, |
| 62 | .deassert = hi3660_reset_deassert, | 62 | .deassert = hi3660_reset_deassert, |
| @@ -19,6 +19,11 @@ struct meson_audio_arb_data { · 18 unmodified lines | |||
| 19 | spinlock_t lock; | 19 | spinlock_t lock; |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | struct meson_audio_arb_match_data { | ||
| 23 | const unsigned int *reset_bits; | ||
| 24 | unsigned int reset_num; | ||
| 25 | }; | ||
| 26 | |||
| 22 | #define ARB_GENERAL_BIT 31 | 27 | #define ARB_GENERAL_BIT 31 |
| 23 | 28 | ||
| 24 | static const unsigned int axg_audio_arb_reset_bits[] = { | 29 | static const unsigned int axg_audio_arb_reset_bits[] = { |
| @@ -30,6 +35,27 @@ static const unsigned int axg_audio_arb_reset_bits[] = { · 5 unmodified lines | |||
| 30 | [AXG_ARB_FRDDR_C] = 6, | 35 | [AXG_ARB_FRDDR_C] = 6, |
| 31 | }; | 36 | }; |
| 32 | 37 | ||
| 38 | static const struct meson_audio_arb_match_data axg_audio_arb_match = { | ||
| 39 | .reset_bits = axg_audio_arb_reset_bits, | ||
| 40 | .reset_num = ARRAY_SIZE(axg_audio_arb_reset_bits), | ||
| 41 | }; | ||
| 42 | |||
| 43 | static const unsigned int sm1_audio_arb_reset_bits[] = { | ||
| 44 | [AXG_ARB_TODDR_A] = 0, | ||
| 45 | [AXG_ARB_TODDR_B] = 1, | ||
| 46 | [AXG_ARB_TODDR_C] = 2, | ||
| 47 | [AXG_ARB_FRDDR_A] = 4, | ||
| 48 | [AXG_ARB_FRDDR_B] = 5, | ||
| 49 | [AXG_ARB_FRDDR_C] = 6, | ||
| 50 | [AXG_ARB_TODDR_D] = 3, | ||
| 51 | [AXG_ARB_FRDDR_D] = 7, | ||
| 52 | }; | ||
| 53 | |||
| 54 | static const struct meson_audio_arb_match_data sm1_audio_arb_match = { | ||
| 55 | .reset_bits = sm1_audio_arb_reset_bits, | ||
| 56 | .reset_num = ARRAY_SIZE(sm1_audio_arb_reset_bits), | ||
| 57 | }; | ||
| 58 | |||
| 33 | static int meson_audio_arb_update(struct reset_controller_dev *rcdev, | 59 | static int meson_audio_arb_update(struct reset_controller_dev *rcdev, |
| 34 | unsigned long id, bool assert) | 60 | unsigned long id, bool assert) |
| 35 | { | 61 | { |
| @@ -82,7 +108,13 @@ static const struct reset_control_ops meson_audio_arb_rstc_ops = { · 46 unmodified lines | |||
| 82 | }; | 108 | }; |
| 83 | 109 | ||
| 84 | static const struct of_device_id meson_audio_arb_of_match[] = { | 110 | static const struct of_device_id meson_audio_arb_of_match[] = { |
| 85 | { .compatible = "amlogic,meson-axg-audio-arb", }, | 111 | { |
| 112 | .compatible = "amlogic,meson-axg-audio-arb", | ||
| 113 | .data = &axg_audio_arb_match, | ||
| 114 | }, { | ||
| 115 | .compatible = "amlogic,meson-sm1-audio-arb", | ||
| 116 | .data = &sm1_audio_arb_match, | ||
| 117 | }, | ||
| 86 | {} | 118 | {} |
| 87 | }; | 119 | }; |
| 88 | MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match); | 120 | MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match); |
| @@ -104,10 +136,15 @@ static int meson_audio_arb_remove(struct platform_device *pdev) · 15 unmodified lines | |||
| 104 | static int meson_audio_arb_probe(struct platform_device *pdev) | 136 | static int meson_audio_arb_probe(struct platform_device *pdev) |
| 105 | { | 137 | { |
| 106 | struct device *dev = &pdev->dev; | 138 | struct device *dev = &pdev->dev; |
| 139 | const struct meson_audio_arb_match_data *data; | ||
| 107 | struct meson_audio_arb_data *arb; | 140 | struct meson_audio_arb_data *arb; |
| 108 | struct resource *res; | 141 | struct resource *res; |
| 109 | int ret; | 142 | int ret; |
| 110 | 143 | ||
| 144 | data = of_device_get_match_data(dev); | ||
| 145 | if (!data) | ||
| 146 | return -EINVAL; | ||
| 147 | |||
| 111 | arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL); | 148 | arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL); |
| 112 | if (!arb) | 149 | if (!arb) |
| 113 | return -ENOMEM; | 150 | return -ENOMEM; |
| @@ -126,8 +163,8 @@ static int meson_audio_arb_probe(struct platform_device *pdev) · 12 unmodified lines | |||
| 126 | return PTR_ERR(arb->regs); | 163 | return PTR_ERR(arb->regs); |
| 127 | 164 | ||
| 128 | spin_lock_init(&arb->lock); | 165 | spin_lock_init(&arb->lock); |
| 129 | arb->reset_bits = axg_audio_arb_reset_bits; | 166 | arb->reset_bits = data->reset_bits; |
| 130 | arb->rstc.nr_resets = ARRAY_SIZE(axg_audio_arb_reset_bits); | 167 | arb->rstc.nr_resets = data->reset_num; |
| 131 | arb->rstc.ops = &meson_audio_arb_rstc_ops; | 168 | arb->rstc.ops = &meson_audio_arb_rstc_ops; |
| 132 | arb->rstc.of_node = dev->of_node; | 169 | arb->rstc.of_node = dev->of_node; |
| 133 | arb->rstc.owner = THIS_MODULE; | 170 | arb->rstc.owner = THIS_MODULE; |
| @@ -15,12 +15,16 @@ · 14 unmodified lines | |||
| 15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
| 16 | #include <linux/of_device.h> | 16 | #include <linux/of_device.h> |
| 17 | 17 | ||
| 18 | #define REG_COUNT 8 | ||
| 19 | #define BITS_PER_REG 32 | 18 | #define BITS_PER_REG 32 |
| 20 | #define LEVEL_OFFSET 0x7c | 19 | |
| 20 | struct meson_reset_param { | ||
| 21 | int reg_count; | ||
| 22 | int level_offset; | ||
| 23 | }; | ||
| 21 | 24 | ||
| 22 | struct meson_reset { | 25 | struct meson_reset { |
| 23 | void __iomem *reg_base; | 26 | void __iomem *reg_base; |
| 27 | const struct meson_reset_param *param; | ||
| 24 | struct reset_controller_dev rcdev; | 28 | struct reset_controller_dev rcdev; |
| 25 | spinlock_t lock; | 29 | spinlock_t lock; |
| 26 | }; | 30 | }; |
| @@ -46,10 +50,12 @@ static int meson_reset_level(struct reset_controller_dev *rcdev, · 19 unmodified lines | |||
| 46 | container_of(rcdev, struct meson_reset, rcdev); | 50 | container_of(rcdev, struct meson_reset, rcdev); |
| 47 | unsigned int bank = id / BITS_PER_REG; | 51 | unsigned int bank = id / BITS_PER_REG; |
| 48 | unsigned int offset = id % BITS_PER_REG; | 52 | unsigned int offset = id % BITS_PER_REG; |
| 49 | void __iomem *reg_addr = data->reg_base + LEVEL_OFFSET + (bank << 2); | 53 | void __iomem *reg_addr; |
| 50 | unsigned long flags; | 54 | unsigned long flags; |
| 51 | u32 reg; | 55 | u32 reg; |
| 52 | 56 | ||
| 57 | reg_addr = data->reg_base + data->param->level_offset + (bank << 2); | ||
| 58 | |||
| 53 | spin_lock_irqsave(&data->lock, flags); | 59 | spin_lock_irqsave(&data->lock, flags); |
| 54 | 60 | ||
| 55 | reg = readl(reg_addr); | 61 | reg = readl(reg_addr); |
| @@ -81,10 +87,21 @@ static const struct reset_control_ops meson_reset_ops = { · 25 unmodified lines | |||
| 81 | .deassert = meson_reset_deassert, | 87 | .deassert = meson_reset_deassert, |
| 82 | }; | 88 | }; |
| 83 | 89 | ||
| 90 | static const struct meson_reset_param meson8b_param = { | ||
| 91 | .reg_count = 8, | ||
| 92 | .level_offset = 0x7c, | ||
| 93 | }; | ||
| 94 | |||
| 95 | static const struct meson_reset_param meson_a1_param = { | ||
| 96 | .reg_count = 3, | ||
| 97 | .level_offset = 0x40, | ||
| 98 | }; | ||
| 99 | |||
| 84 | static const struct of_device_id meson_reset_dt_ids[] = { | 100 | static const struct of_device_id meson_reset_dt_ids[] = { |
| 85 | { .compatible = "amlogic,meson8b-reset" }, | 101 | { .compatible = "amlogic,meson8b-reset", .data = &meson8b_param}, |
| 86 | { .compatible = "amlogic,meson-gxbb-reset" }, | 102 | { .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param}, |
| 87 | { .compatible = "amlogic,meson-axg-reset" }, | 103 | { .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param}, |
| 104 | { .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param}, | ||
| 88 | { /* sentinel */ }, | 105 | { /* sentinel */ }, |
| 89 | }; | 106 | }; |
| 90 | 107 | ||
| @@ -102,12 +119,16 @@ static int meson_reset_probe(struct platform_device *pdev) · 11 unmodified lines | |||
| 102 | if (IS_ERR(data->reg_base)) | 119 | if (IS_ERR(data->reg_base)) |
| 103 | return PTR_ERR(data->reg_base); | 120 | return PTR_ERR(data->reg_base); |
| 104 | 121 | ||
| 122 | data->param = of_device_get_match_data(&pdev->dev); | ||
| 123 | if (!data->param) | ||
| 124 | return -ENODEV; | ||
| 125 | |||
| 105 | platform_set_drvdata(pdev, data); | 126 | platform_set_drvdata(pdev, data); |
| 106 | 127 | ||
| 107 | spin_lock_init(&data->lock); | 128 | spin_lock_init(&data->lock); |
| 108 | 129 | ||
| 109 | data->rcdev.owner = THIS_MODULE; | 130 | data->rcdev.owner = THIS_MODULE; |
| 110 | data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG; | 131 | data->rcdev.nr_resets = data->param->reg_count * BITS_PER_REG; |
| 111 | data->rcdev.ops = &meson_reset_ops; | 132 | data->rcdev.ops = &meson_reset_ops; |
| 112 | data->rcdev.of_node = pdev->dev.of_node; | 133 | data->rcdev.of_node = pdev->dev.of_node; |
| 113 | 134 | ||
| @@ -140,6 +140,10 @@ static const struct of_device_id uniphier_glue_reset_match[] = { · 139 unmodified lines | |||
| 140 | .compatible = "socionext,uniphier-pro4-usb3-reset", | 140 | .compatible = "socionext,uniphier-pro4-usb3-reset", |
| 141 | .data = &uniphier_pro4_data, | 141 | .data = &uniphier_pro4_data, |
| 142 | }, | 142 | }, |
| 143 | { | ||
| 144 | .compatible = "socionext,uniphier-pro5-usb3-reset", | ||
| 145 | .data = &uniphier_pro4_data, | ||
| 146 | }, | ||
| 143 | { | 147 | { |
| 144 | .compatible = "socionext,uniphier-pxs2-usb3-reset", | 148 | .compatible = "socionext,uniphier-pxs2-usb3-reset", |
| 145 | .data = &uniphier_pxs2_data, | 149 | .data = &uniphier_pxs2_data, |
| @@ -64,7 +64,7 @@ static int zynqmp_reset_reset(struct reset_controller_dev *rcdev, · 63 unmodified lines | |||
| 64 | PM_RESET_ACTION_PULSE); | 64 | PM_RESET_ACTION_PULSE); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | static struct reset_control_ops zynqmp_reset_ops = { | 67 | static const struct reset_control_ops zynqmp_reset_ops = { |
| 68 | .reset = zynqmp_reset_reset, | 68 | .reset = zynqmp_reset_reset, |
| 69 | .assert = zynqmp_reset_assert, | 69 | .assert = zynqmp_reset_assert, |
| 70 | .deassert = zynqmp_reset_deassert, | 70 | .deassert = zynqmp_reset_deassert, |
| @@ -40,6 +40,7 @@ static const struct meson_gx_soc_id { · 39 unmodified lines | |||
| 40 | { "G12A", 0x28 }, | 40 | { "G12A", 0x28 }, |
| 41 | { "G12B", 0x29 }, | 41 | { "G12B", 0x29 }, |
| 42 | { "SM1", 0x2b }, | 42 | { "SM1", 0x2b }, |
| 43 | { "A1", 0x2c }, | ||
| 43 | }; | 44 | }; |
| 44 | 45 | ||
| 45 | static const struct meson_gx_package_id { | 46 | static const struct meson_gx_package_id { |
| @@ -68,6 +69,8 @@ static const struct meson_gx_package_id { · 22 unmodified lines | |||
| 68 | { "S922X", 0x29, 0x40, 0xf0 }, | 69 | { "S922X", 0x29, 0x40, 0xf0 }, |
| 69 | { "A311D", 0x29, 0x10, 0xf0 }, | 70 | { "A311D", 0x29, 0x10, 0xf0 }, |
| 70 | { "S905X3", 0x2b, 0x5, 0xf }, | 71 | { "S905X3", 0x2b, 0x5, 0xf }, |
| 72 | { "S905D3", 0x2b, 0xb0, 0xf0 }, | ||
| 73 | { "A113L", 0x2c, 0x0, 0xf8 }, | ||
| 71 | }; | 74 | }; |
| 72 | 75 | ||
| 73 | static inline unsigned int socinfo_to_major(u32 socinfo) | 76 | static inline unsigned int socinfo_to_major(u32 socinfo) |
| @@ -5,3 +5,14 @@ config AT91_SOC_ID · 4 unmodified lines | |||
| 5 | default ARCH_AT91 | 5 | default ARCH_AT91 |
| 6 | help | 6 | help |
| 7 | Include support for the SoC bus on the Atmel ARM SoCs. | 7 | Include support for the SoC bus on the Atmel ARM SoCs. |
| 8 | |||
| 9 | config AT91_SOC_SFR | ||
| 10 | tristate "Special Function Registers support" | ||
| 11 | depends on ARCH_AT91 || COMPILE_TEST | ||
| 12 | help | ||
| 13 | This is a driver for the Special Function Registers available on | ||
| 14 | Atmel SAMA5Dx SoCs, providing access to specific aspects of the | ||
| 15 | integrated memory, bridge implementations, processor etc. | ||
| 16 | |||
| 17 | This driver can also be built as a module. If so, the module | ||
| 18 | will be called sfr. | ||
| @@ -1,2 +1,3 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | obj-$(CONFIG_AT91_SOC_ID) += soc.o | 2 | obj-$(CONFIG_AT91_SOC_ID) += soc.o |
| 3 | obj-$(CONFIG_AT91_SOC_SFR) += sfr.o | ||
| @@ -0,0 +1,99 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* | ||
| 3 | * sfr.c - driver for special function registers | ||
| 4 | * | ||
| 5 | * Copyright (C) 2019 Bootlin. | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | #include <linux/mfd/syscon.h> | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/nvmem-provider.h> | ||
| 11 | #include <linux/random.h> | ||
| 12 | #include <linux/of.h> | ||
| 13 | #include <linux/of_device.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/regmap.h> | ||
| 16 | |||
| 17 | #define SFR_SN0 0x4c | ||
| 18 | #define SFR_SN_SIZE 8 | ||
| 19 | |||
| 20 | struct atmel_sfr_priv { | ||
| 21 | struct regmap *regmap; | ||
| 22 | }; | ||
| 23 | |||
| 24 | static int atmel_sfr_read(void *context, unsigned int offset, | ||
| 25 | void *buf, size_t bytes) | ||
| 26 | { | ||
| 27 | struct atmel_sfr_priv *priv = context; | ||
| 28 | |||
| 29 | return regmap_bulk_read(priv->regmap, SFR_SN0 + offset, | ||
| 30 | buf, bytes / 4); | ||
| 31 | } | ||
| 32 | |||
| 33 | static struct nvmem_config atmel_sfr_nvmem_config = { | ||
| 34 | .name = "atmel-sfr", | ||
| 35 | .read_only = true, | ||
| 36 | .word_size = 4, | ||
| 37 | .stride = 4, | ||
| 38 | .size = SFR_SN_SIZE, | ||
| 39 | .reg_read = atmel_sfr_read, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static int atmel_sfr_probe(struct platform_device *pdev) | ||
| 43 | { | ||
| 44 | struct device *dev = &pdev->dev; | ||
| 45 | struct device_node *np = dev->of_node; | ||
| 46 | struct nvmem_device *nvmem; | ||
| 47 | struct atmel_sfr_priv *priv; | ||
| 48 | u8 sn[SFR_SN_SIZE]; | ||
| 49 | int ret; | ||
| 50 | |||
| 51 | priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 52 | if (!priv) | ||
| 53 | return -ENOMEM; | ||
| 54 | |||
| 55 | priv->regmap = syscon_node_to_regmap(np); | ||
| 56 | if (IS_ERR(priv->regmap)) { | ||
| 57 | dev_err(dev, "cannot get parent's regmap\n"); | ||
| 58 | return PTR_ERR(priv->regmap); | ||
| 59 | } | ||
| 60 | |||
| 61 | atmel_sfr_nvmem_config.dev = dev; | ||
| 62 | atmel_sfr_nvmem_config.priv = priv; | ||
| 63 | |||
| 64 | nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config); | ||
| 65 | if (IS_ERR(nvmem)) { | ||
| 66 | dev_err(dev, "error registering nvmem config\n"); | ||
| 67 | return PTR_ERR(nvmem); | ||
| 68 | } | ||
| 69 | |||
| 70 | ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE); | ||
| 71 | if (ret == 0) | ||
| 72 | add_device_randomness(sn, SFR_SN_SIZE); | ||
| 73 | |||
| 74 | return ret; | ||
| 75 | } | ||
| 76 | |||
| 77 | static const struct of_device_id atmel_sfr_dt_ids[] = { | ||
| 78 | { | ||
| 79 | .compatible = "atmel,sama5d2-sfr", | ||
| 80 | }, { | ||
| 81 | .compatible = "atmel,sama5d4-sfr", | ||
| 82 | }, { | ||
| 83 | /* sentinel */ | ||
| 84 | }, | ||
| 85 | }; | ||
| 86 | MODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids); | ||
| 87 | |||
| 88 | static struct platform_driver atmel_sfr_driver = { | ||
| 89 | .probe = atmel_sfr_probe, | ||
| 90 | .driver = { | ||
| 91 | .name = "atmel-sfr", | ||
| 92 | .of_match_table = atmel_sfr_dt_ids, | ||
| 93 | }, | ||
| 94 | }; | ||
| 95 | module_platform_driver(atmel_sfr_driver); | ||
| 96 | |||
| 97 | MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); | ||
| 98 | MODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family"); | ||
| 99 | MODULE_LICENSE("GPL v2"); | ||
| @@ -40,4 +40,14 @@ config DPAA2_CONSOLE · 39 unmodified lines | |||
| 40 | /dev/dpaa2_mc_console and /dev/dpaa2_aiop_console, | 40 | /dev/dpaa2_mc_console and /dev/dpaa2_aiop_console, |
| 41 | which can be used to dump the Management Complex and AIOP | 41 | which can be used to dump the Management Complex and AIOP |
| 42 | firmware logs. | 42 | firmware logs. |
| 43 | |||
| 44 | config FSL_RCPM | ||
| 45 | bool "Freescale RCPM support" | ||
| 46 | depends on PM_SLEEP && (ARM || ARM64) | ||
| 47 | help | ||
| 48 | The NXP QorIQ Processors based on ARM Core have RCPM module | ||
| 49 | (Run Control and Power Management), which performs all device-level | ||
| 50 | tasks associated with power management, such as wakeup source control. | ||
| 51 | Note that currently this driver will not support PowerPC based | ||
| 52 | QorIQ processor. | ||
| 43 | endmenu | 53 | endmenu |
| @@ -6,6 +6,7 @@ · 5 unmodified lines | |||
| 6 | obj-$(CONFIG_FSL_DPAA) += qbman/ | 6 | obj-$(CONFIG_FSL_DPAA) += qbman/ |
| 7 | obj-$(CONFIG_QUICC_ENGINE) += qe/ | 7 | obj-$(CONFIG_QUICC_ENGINE) += qe/ |
| 8 | obj-$(CONFIG_CPM) += qe/ | 8 | obj-$(CONFIG_CPM) += qe/ |
| 9 | obj-$(CONFIG_FSL_RCPM) += rcpm.o | ||
| 9 | obj-$(CONFIG_FSL_GUTS) += guts.o | 10 | obj-$(CONFIG_FSL_GUTS) += guts.o |
| 10 | obj-$(CONFIG_FSL_MC_DPIO) += dpio/ | 11 | obj-$(CONFIG_FSL_MC_DPIO) += dpio/ |
| 11 | obj-$(CONFIG_DPAA2_CONSOLE) += dpaa2-console.o | 12 | obj-$(CONFIG_DPAA2_CONSOLE) += dpaa2-console.o |
| @@ -0,0 +1,151 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | // | ||
| 3 | // rcpm.c - Freescale QorIQ RCPM driver | ||
| 4 | // | ||
| 5 | // Copyright 2019 NXP | ||
| 6 | // | ||
| 7 | // Author: Ran Wang <ran.wang_1@nxp.com> | ||
| 8 | |||
| 9 | #include <linux/init.h> | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/platform_device.h> | ||
| 12 | #include <linux/of_address.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/suspend.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | |||
| 17 | #define RCPM_WAKEUP_CELL_MAX_SIZE 7 | ||
| 18 | |||
| 19 | struct rcpm { | ||
| 20 | unsigned int wakeup_cells; | ||
| 21 | void __iomem *ippdexpcr_base; | ||
| 22 | bool little_endian; | ||
| 23 | }; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * rcpm_pm_prepare - performs device-level tasks associated with power | ||
| 27 | * management, such as programming related to the wakeup source control. | ||
| 28 | * @dev: Device to handle. | ||
| 29 | * | ||
| 30 | */ | ||
| 31 | static int rcpm_pm_prepare(struct device *dev) | ||
| 32 | { | ||
| 33 | int i, ret, idx; | ||
| 34 | void __iomem *base; | ||
| 35 | struct wakeup_source *ws; | ||
| 36 | struct rcpm *rcpm; | ||
| 37 | struct device_node *np = dev->of_node; | ||
| 38 | u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1]; | ||
| 39 | u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0}; | ||
| 40 | |||
| 41 | rcpm = dev_get_drvdata(dev); | ||
| 42 | if (!rcpm) | ||
| 43 | return -EINVAL; | ||
| 44 | |||
| 45 | base = rcpm->ippdexpcr_base; | ||
| 46 | idx = wakeup_sources_read_lock(); | ||
| 47 | |||
| 48 | /* Begin with first registered wakeup source */ | ||
| 49 | for_each_wakeup_source(ws) { | ||
| 50 | |||
| 51 | /* skip object which is not attached to device */ | ||
| 52 | if (!ws->dev || !ws->dev->parent) | ||
| 53 | continue; | ||
| 54 | |||
| 55 | ret = device_property_read_u32_array(ws->dev->parent, | ||
| 56 | "fsl,rcpm-wakeup", value, | ||
| 57 | rcpm->wakeup_cells + 1); | ||
| 58 | |||
| 59 | /* Wakeup source should refer to current rcpm device */ | ||
| 60 | if (ret || (np->phandle != value[0])) | ||
| 61 | continue; | ||
| 62 | |||
| 63 | /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the | ||
| 64 | * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup" | ||
| 65 | * of wakeup source IP contains an integer array: <phandle to | ||
| 66 | * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting, | ||
| 67 | * IPPDEXPCR2 setting, etc>. | ||
| 68 | * | ||
| 69 | * So we will go thought them to collect setting data. | ||
| 70 | */ | ||
| 71 | for (i = 0; i < rcpm->wakeup_cells; i++) | ||
| 72 | setting[i] |= value[i + 1]; | ||
| 73 | } | ||
| 74 | |||
| 75 | wakeup_sources_read_unlock(idx); | ||
| 76 | |||
| 77 | /* Program all IPPDEXPCRn once */ | ||
| 78 | for (i = 0; i < rcpm->wakeup_cells; i++) { | ||
| 79 | u32 tmp = setting[i]; | ||
| 80 | void __iomem *address = base + i * 4; | ||
| 81 | |||
| 82 | if (!tmp) | ||
| 83 | continue; | ||
| 84 | |||
| 85 | /* We can only OR related bits */ | ||
| 86 | if (rcpm->little_endian) { | ||
| 87 | tmp |= ioread32(address); | ||
| 88 | iowrite32(tmp, address); | ||
| 89 | } else { | ||
| 90 | tmp |= ioread32be(address); | ||
| 91 | iowrite32be(tmp, address); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | static const struct dev_pm_ops rcpm_pm_ops = { | ||
| 99 | .prepare = rcpm_pm_prepare, | ||
| 100 | }; | ||
| 101 | |||
| 102 | static int rcpm_probe(struct platform_device *pdev) | ||
| 103 | { | ||
| 104 | struct device *dev = &pdev->dev; | ||
| 105 | struct resource *r; | ||
| 106 | struct rcpm *rcpm; | ||
| 107 | int ret; | ||
| 108 | |||
| 109 | rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL); | ||
| 110 | if (!rcpm) | ||
| 111 | return -ENOMEM; | ||
| 112 | |||
| 113 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 114 | if (!r) | ||
| 115 | return -ENODEV; | ||
| 116 | |||
| 117 | rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r); | ||
| 118 | if (IS_ERR(rcpm->ippdexpcr_base)) { | ||
| 119 | ret = PTR_ERR(rcpm->ippdexpcr_base); | ||
| 120 | return ret; | ||
| 121 | } | ||
| 122 | |||
| 123 | rcpm->little_endian = device_property_read_bool( | ||
| 124 | &pdev->dev, "little-endian"); | ||
| 125 | |||
| 126 | ret = device_property_read_u32(&pdev->dev, | ||
| 127 | "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells); | ||
| 128 | if (ret) | ||
| 129 | return ret; | ||
| 130 | |||
| 131 | dev_set_drvdata(&pdev->dev, rcpm); | ||
| 132 | |||
| 133 | return 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | static const struct of_device_id rcpm_of_match[] = { | ||
| 137 | { .compatible = "fsl,qoriq-rcpm-2.1+", }, | ||
| 138 | {} | ||
| 139 | }; | ||
| 140 | MODULE_DEVICE_TABLE(of, rcpm_of_match); | ||
| 141 | |||
| 142 | static struct platform_driver rcpm_driver = { | ||
| 143 | .driver = { | ||
| 144 | .name = "rcpm", | ||
| 145 | .of_match_table = rcpm_of_match, | ||
| 146 | .pm = &rcpm_pm_ops, | ||
| 147 | }, | ||
| 148 | .probe = rcpm_probe, | ||
| 149 | }; | ||
| 150 | |||
| 151 | module_platform_driver(rcpm_driver); | ||
| @@ -33,12 +33,10 @@ struct imx_sc_msg_misc_get_soc_uid { · 32 unmodified lines | |||
| 33 | u32 uid_high; | 33 | u32 uid_high; |
| 34 | } __packed; | 34 | } __packed; |
| 35 | 35 | ||
| 36 | static ssize_t soc_uid_show(struct device *dev, | 36 | static int imx_scu_soc_uid(u64 *soc_uid) |
| 37 | struct device_attribute *attr, char *buf) | ||
| 38 | { | 37 | { |
| 39 | struct imx_sc_msg_misc_get_soc_uid msg; | 38 | struct imx_sc_msg_misc_get_soc_uid msg; |
| 40 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | 39 | struct imx_sc_rpc_msg *hdr = &msg.hdr; |
| 41 | u64 soc_uid; | ||
| 42 | int ret; | 40 | int ret; |
| 43 | 41 | ||
| 44 | hdr->ver = IMX_SC_RPC_VERSION; | 42 | hdr->ver = IMX_SC_RPC_VERSION; |
| @@ -52,15 +50,13 @@ static ssize_t soc_uid_show(struct device *dev, · 7 unmodified lines | |||
| 52 | return ret; | 50 | return ret; |
| 53 | } | 51 | } |
| 54 | 52 | ||
| 55 | soc_uid = msg.uid_high; | 53 | *soc_uid = msg.uid_high; |
| 56 | soc_uid <<= 32; | 54 | *soc_uid <<= 32; |
| 57 | soc_uid |= msg.uid_low; | 55 | *soc_uid |= msg.uid_low; |
| 58 | 56 | ||
| 59 | return sprintf(buf, "%016llX\n", soc_uid); | 57 | return 0; |
| 60 | } | 58 | } |
| 61 | 59 | ||
| 62 | static DEVICE_ATTR_RO(soc_uid); | ||
| 63 | |||
| 64 | static int imx_scu_soc_id(void) | 60 | static int imx_scu_soc_id(void) |
| 65 | { | 61 | { |
| 66 | struct imx_sc_msg_misc_get_soc_id msg; | 62 | struct imx_sc_msg_misc_get_soc_id msg; |
| @@ -89,6 +85,7 @@ static int imx_scu_soc_probe(struct platform_device *pdev) · 22 unmodified lines | |||
| 89 | struct soc_device_attribute *soc_dev_attr; | 85 | struct soc_device_attribute *soc_dev_attr; |
| 90 | struct soc_device *soc_dev; | 86 | struct soc_device *soc_dev; |
| 91 | int id, ret; | 87 | int id, ret; |
| 88 | u64 uid = 0; | ||
| 92 | u32 val; | 89 | u32 val; |
| 93 | 90 | ||
| 94 | ret = imx_scu_get_handle(&soc_ipc_handle); | 91 | ret = imx_scu_get_handle(&soc_ipc_handle); |
| @@ -112,6 +109,10 @@ static int imx_scu_soc_probe(struct platform_device *pdev) · 17 unmodified lines | |||
| 112 | if (id < 0) | 109 | if (id < 0) |
| 113 | return -EINVAL; | 110 | return -EINVAL; |
| 114 | 111 | ||
| 112 | ret = imx_scu_soc_uid(&uid); | ||
| 113 | if (ret < 0) | ||
| 114 | return -EINVAL; | ||
| 115 | |||
| 115 | /* format soc_id value passed from SCU firmware */ | 116 | /* format soc_id value passed from SCU firmware */ |
| 116 | val = id & 0x1f; | 117 | val = id & 0x1f; |
| 117 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val); | 118 | 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) · 12 unmodified lines | |||
| 130 | goto free_soc_id; | 131 | goto free_soc_id; |
| 131 | } | 132 | } |
| 132 | 133 | ||
| 134 | soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid); | ||
| 135 | if (!soc_dev_attr->serial_number) { | ||
| 136 | ret = -ENOMEM; | ||
| 137 | goto free_revision; | ||
| 138 | } | ||
| 139 | |||
| 133 | soc_dev = soc_device_register(soc_dev_attr); | 140 | soc_dev = soc_device_register(soc_dev_attr); |
| 134 | if (IS_ERR(soc_dev)) { | 141 | if (IS_ERR(soc_dev)) { |
| 135 | ret = PTR_ERR(soc_dev); | 142 | ret = PTR_ERR(soc_dev); |
| 136 | goto free_revision; | 143 | goto free_serial_number; |
| 137 | } | 144 | } |
| 138 | 145 | ||
| 139 | ret = device_create_file(soc_device_to_device(soc_dev), | ||
| 140 | &dev_attr_soc_uid); | ||
| 141 | if (ret) | ||
| 142 | goto free_revision; | ||
| 143 | |||
| 144 | return 0; | 146 | return 0; |
| 145 | 147 | ||
| 148 | free_serial_number: | ||
| 149 | kfree(soc_dev_attr->serial_number); | ||
| 146 | free_revision: | 150 | free_revision: |
| 147 | kfree(soc_dev_attr->revision); | 151 | kfree(soc_dev_attr->revision); |
| 148 | free_soc_id: | 152 | free_soc_id: |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <linux/slab.h> | 9 | #include <linux/slab.h> |
| 10 | #include <linux/sys_soc.h> | 10 | #include <linux/sys_soc.h> |
| 11 | #include <linux/platform_device.h> | 11 | #include <linux/platform_device.h> |
| 12 | #include <linux/arm-smccc.h> | ||
| 12 | #include <linux/of.h> | 13 | #include <linux/of.h> |
| 13 | 14 | ||
| 14 | #define REV_B1 0x21 | 15 | #define REV_B1 0x21 |
| @@ -16,6 +17,8 @@ · 1 unmodified line | |||
| 16 | #define IMX8MQ_SW_INFO_B1 0x40 | 17 | #define IMX8MQ_SW_INFO_B1 0x40 |
| 17 | #define IMX8MQ_SW_MAGIC_B1 0xff0055aa | 18 | #define IMX8MQ_SW_MAGIC_B1 0xff0055aa |
| 18 | 19 | ||
| 20 | #define IMX_SIP_GET_SOC_INFO 0xc2000006 | ||
| 21 | |||
| 19 | #define OCOTP_UID_LOW 0x410 | 22 | #define OCOTP_UID_LOW 0x410 |
| 20 | #define OCOTP_UID_HIGH 0x420 | 23 | #define OCOTP_UID_HIGH 0x420 |
| 21 | 24 | ||
| @@ -29,13 +32,21 @@ struct imx8_soc_data { · 7 unmodified lines | |||
| 29 | 32 | ||
| 30 | static u64 soc_uid; | 33 | static u64 soc_uid; |
| 31 | 34 | ||
| 32 | static ssize_t soc_uid_show(struct device *dev, | 35 | #ifdef CONFIG_HAVE_ARM_SMCCC |
| 33 | struct device_attribute *attr, char *buf) | 36 | static u32 imx8mq_soc_revision_from_atf(void) |
| 34 | { | 37 | { |
| 35 | return sprintf(buf, "%016llX\n", soc_uid); | 38 | struct arm_smccc_res res; |
| 36 | } | ||
| 37 | 39 | ||
| 38 | static DEVICE_ATTR_RO(soc_uid); | 40 | arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); |
| 41 | |||
| 42 | if (res.a0 == SMCCC_RET_NOT_SUPPORTED) | ||
| 43 | return 0; | ||
| 44 | else | ||
| 45 | return res.a0 & 0xff; | ||
| 46 | } | ||
| 47 | #else | ||
| 48 | static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; }; | ||
| 49 | #endif | ||
| 39 | 50 | ||
| 40 | static u32 __init imx8mq_soc_revision(void) | 51 | static u32 __init imx8mq_soc_revision(void) |
| 41 | { | 52 | { |
| @@ -51,9 +62,16 @@ static u32 __init imx8mq_soc_revision(void) · 9 unmodified lines | |||
| 51 | ocotp_base = of_iomap(np, 0); | 62 | ocotp_base = of_iomap(np, 0); |
| 52 | WARN_ON(!ocotp_base); | 63 | WARN_ON(!ocotp_base); |
| 53 | 64 | ||
| 54 | magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); | 65 | /* |
| 55 | if (magic == IMX8MQ_SW_MAGIC_B1) | 66 | * SOC revision on older imx8mq is not available in fuses so query |
| 56 | rev = REV_B1; | 67 | * the value from ATF instead. |
| 68 | */ | ||
| 69 | rev = imx8mq_soc_revision_from_atf(); | ||
| 70 | if (!rev) { | ||
| 71 | magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); | ||
| 72 | if (magic == IMX8MQ_SW_MAGIC_B1) | ||
| 73 | rev = REV_B1; | ||
| 74 | } | ||
| 57 | 75 | ||
| 58 | soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); | 76 | soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); |
| 59 | soc_uid <<= 32; | 77 | soc_uid <<= 32; |
| @@ -174,22 +192,25 @@ static int __init imx8_soc_init(void) · 114 unmodified lines | |||
| 174 | goto free_soc; | 192 | goto free_soc; |
| 175 | } | 193 | } |
| 176 | 194 | ||
| 195 | soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid); | ||
| 196 | if (!soc_dev_attr->serial_number) { | ||
| 197 | ret = -ENOMEM; | ||
| 198 | goto free_rev; | ||
| 199 | } | ||
| 200 | |||
| 177 | soc_dev = soc_device_register(soc_dev_attr); | 201 | soc_dev = soc_device_register(soc_dev_attr); |
| 178 | if (IS_ERR(soc_dev)) { | 202 | if (IS_ERR(soc_dev)) { |
| 179 | ret = PTR_ERR(soc_dev); | 203 | ret = PTR_ERR(soc_dev); |
| 180 | goto free_rev; | 204 | goto free_serial_number; |
| 181 | } | 205 | } |
| 182 | 206 | ||
| 183 | ret = device_create_file(soc_device_to_device(soc_dev), | ||
| 184 | &dev_attr_soc_uid); | ||
| 185 | if (ret) | ||
| 186 | goto free_rev; | ||
| 187 | |||
| 188 | if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) | 207 | if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) |
| 189 | platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); | 208 | platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); |
| 190 | 209 | ||
| 191 | return 0; | 210 | return 0; |
| 192 | 211 | ||
| 212 | free_serial_number: | ||
| 213 | kfree(soc_dev_attr->serial_number); | ||
| 193 | free_rev: | 214 | free_rev: |
| 194 | if (strcmp(soc_dev_attr->revision, "unknown")) | 215 | if (strcmp(soc_dev_attr->revision, "unknown")) |
| 195 | kfree(soc_dev_attr->revision); | 216 | kfree(soc_dev_attr->revision); |
| @@ -21,7 +21,7 @@ · 20 unmodified lines | |||
| 21 | #include <dt-bindings/power/mt8173-power.h> | 21 | #include <dt-bindings/power/mt8173-power.h> |
| 22 | 22 | ||
| 23 | #define MTK_POLL_DELAY_US 10 | 23 | #define MTK_POLL_DELAY_US 10 |
| 24 | #define MTK_POLL_TIMEOUT (jiffies_to_usecs(HZ)) | 24 | #define MTK_POLL_TIMEOUT USEC_PER_SEC |
| 25 | 25 | ||
| 26 | #define MTK_SCPD_ACTIVE_WAKEUP BIT(0) | 26 | #define MTK_SCPD_ACTIVE_WAKEUP BIT(0) |
| 27 | #define MTK_SCPD_FWAIT_SRAM BIT(1) | 27 | #define MTK_SCPD_FWAIT_SRAM BIT(1) |
| @@ -108,6 +108,17 @@ static const char * const clk_names[] = { · 80 unmodified lines | |||
| 108 | 108 | ||
| 109 | #define MAX_CLKS 3 | 109 | #define MAX_CLKS 3 |
| 110 | 110 | ||
| 111 | /** | ||
| 112 | * struct scp_domain_data - scp domain data for power on/off flow | ||
| 113 | * @name: The domain name. | ||
| 114 | * @sta_mask: The mask for power on/off status bit. | ||
| 115 | * @ctl_offs: The offset for main power control register. | ||
| 116 | * @sram_pdn_bits: The mask for sram power control bits. | ||
| 117 | * @sram_pdn_ack_bits: The mask for sram power control acked bits. | ||
| 118 | * @bus_prot_mask: The mask for single step bus protection. | ||
| 119 | * @clk_id: The basic clocks required by this power domain. | ||
| 120 | * @caps: The flag for active wake-up action. | ||
| 121 | */ | ||
| 111 | struct scp_domain_data { | 122 | struct scp_domain_data { |
| 112 | const char *name; | 123 | const char *name; |
| 113 | u32 sta_mask; | 124 | u32 sta_mask; |
| @@ -180,32 +191,132 @@ static int scpsys_domain_is_on(struct scp_domain *scpd) · 66 unmodified lines | |||
| 180 | return -EINVAL; | 191 | return -EINVAL; |
| 181 | } | 192 | } |
| 182 | 193 | ||
| 194 | static int scpsys_regulator_enable(struct scp_domain *scpd) | ||
| 195 | { | ||
| 196 | if (!scpd->supply) | ||
| 197 | return 0; | ||
| 198 | |||
| 199 | return regulator_enable(scpd->supply); | ||
| 200 | } | ||
| 201 | |||
| 202 | static int scpsys_regulator_disable(struct scp_domain *scpd) | ||
| 203 | { | ||
| 204 | if (!scpd->supply) | ||
| 205 | return 0; | ||
| 206 | |||
| 207 | return regulator_disable(scpd->supply); | ||
| 208 | } | ||
| 209 | |||
| 210 | static void scpsys_clk_disable(struct clk *clk[], int max_num) | ||
| 211 | { | ||
| 212 | int i; | ||
| 213 | |||
| 214 | for (i = max_num - 1; i >= 0; i--) | ||
| 215 | clk_disable_unprepare(clk[i]); | ||
| 216 | } | ||
| 217 | |||
| 218 | static int scpsys_clk_enable(struct clk *clk[], int max_num) | ||
| 219 | { | ||
| 220 | int i, ret = 0; | ||
| 221 | |||
| 222 | for (i = 0; i < max_num && clk[i]; i++) { | ||
| 223 | ret = clk_prepare_enable(clk[i]); | ||
| 224 | if (ret) { | ||
| 225 | scpsys_clk_disable(clk, i); | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | return ret; | ||
| 231 | } | ||
| 232 | |||
| 233 | static int scpsys_sram_enable(struct scp_domain *scpd, void __iomem *ctl_addr) | ||
| 234 | { | ||
| 235 | u32 val; | ||
| 236 | u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||
| 237 | int tmp; | ||
| 238 | |||
| 239 | val = readl(ctl_addr); | ||
| 240 | val &= ~scpd->data->sram_pdn_bits; | ||
| 241 | writel(val, ctl_addr); | ||
| 242 | |||
| 243 | /* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ | ||
| 244 | if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { | ||
| 245 | /* | ||
| 246 | * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for | ||
| 247 | * MT7622_POWER_DOMAIN_WB and thus just a trivial setup | ||
| 248 | * is applied here. | ||
| 249 | */ | ||
| 250 | usleep_range(12000, 12100); | ||
| 251 | } else { | ||
| 252 | /* Either wait until SRAM_PDN_ACK all 1 or 0 */ | ||
| 253 | int ret = readl_poll_timeout(ctl_addr, tmp, | ||
| 254 | (tmp & pdn_ack) == 0, | ||
| 255 | MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||
| 256 | if (ret < 0) | ||
| 257 | return ret; | ||
| 258 | } | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr) | ||
| 264 | { | ||
| 265 | u32 val; | ||
| 266 | u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||
| 267 | int tmp; | ||
| 268 | |||
| 269 | val = readl(ctl_addr); | ||
| 270 | val |= scpd->data->sram_pdn_bits; | ||
| 271 | writel(val, ctl_addr); | ||
| 272 | |||
| 273 | /* Either wait until SRAM_PDN_ACK all 1 or 0 */ | ||
| 274 | return readl_poll_timeout(ctl_addr, tmp, | ||
| 275 | (tmp & pdn_ack) == pdn_ack, | ||
| 276 | MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||
| 277 | } | ||
| 278 | |||
| 279 | static int scpsys_bus_protect_enable(struct scp_domain *scpd) | ||
| 280 | { | ||
| 281 | struct scp *scp = scpd->scp; | ||
| 282 | |||
| 283 | if (!scpd->data->bus_prot_mask) | ||
| 284 | return 0; | ||
| 285 | |||
| 286 | return mtk_infracfg_set_bus_protection(scp->infracfg, | ||
| 287 | scpd->data->bus_prot_mask, | ||
| 288 | scp->bus_prot_reg_update); | ||
| 289 | } | ||
| 290 | |||
| 291 | static int scpsys_bus_protect_disable(struct scp_domain *scpd) | ||
| 292 | { | ||
| 293 | struct scp *scp = scpd->scp; | ||
| 294 | |||
| 295 | if (!scpd->data->bus_prot_mask) | ||
| 296 | return 0; | ||
| 297 | |||
| 298 | return mtk_infracfg_clear_bus_protection(scp->infracfg, | ||
| 299 | scpd->data->bus_prot_mask, | ||
| 300 | scp->bus_prot_reg_update); | ||
| 301 | } | ||
| 302 | |||
| 183 | static int scpsys_power_on(struct generic_pm_domain *genpd) | 303 | static int scpsys_power_on(struct generic_pm_domain *genpd) |
| 184 | { | 304 | { |
| 185 | struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); | 305 | struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); |
| 186 | struct scp *scp = scpd->scp; | 306 | struct scp *scp = scpd->scp; |
| 187 | void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; | 307 | void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; |
| 188 | u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||
| 189 | u32 val; | 308 | u32 val; |
| 190 | int ret, tmp; | 309 | int ret, tmp; |
| 191 | int i; | ||
| 192 | 310 | ||
| 193 | if (scpd->supply) { | 311 | ret = scpsys_regulator_enable(scpd); |
| 194 | ret = regulator_enable(scpd->supply); | 312 | if (ret < 0) |
| 195 | if (ret) | 313 | return ret; |
| 196 | return ret; | ||
| 197 | } | ||
| 198 | |||
| 199 | for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { | ||
| 200 | ret = clk_prepare_enable(scpd->clk[i]); | ||
| 201 | if (ret) { | ||
| 202 | for (--i; i >= 0; i--) | ||
| 203 | clk_disable_unprepare(scpd->clk[i]); | ||
| 204 | 314 | ||
| 205 | goto err_clk; | 315 | ret = scpsys_clk_enable(scpd->clk, MAX_CLKS); |
| 206 | } | 316 | if (ret) |
| 207 | } | 317 | goto err_clk; |
| 208 | 318 | ||
| 319 | /* subsys power on */ | ||
| 209 | val = readl(ctl_addr); | 320 | val = readl(ctl_addr); |
| 210 | val |= PWR_ON_BIT; | 321 | val |= PWR_ON_BIT; |
| 211 | writel(val, ctl_addr); | 322 | writel(val, ctl_addr); |
| @@ -227,43 +338,20 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) · 15 unmodified lines | |||
| 227 | val |= PWR_RST_B_BIT; | 338 | val |= PWR_RST_B_BIT; |
| 228 | writel(val, ctl_addr); | 339 | writel(val, ctl_addr); |
| 229 | 340 | ||
| 230 | val &= ~scpd->data->sram_pdn_bits; | 341 | ret = scpsys_sram_enable(scpd, ctl_addr); |
| 231 | writel(val, ctl_addr); | 342 | if (ret < 0) |
| 232 | 343 | goto err_pwr_ack; | |
| 233 | /* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ | ||
| 234 | if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { | ||
| 235 | /* | ||
| 236 | * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for | ||
| 237 | * MT7622_POWER_DOMAIN_WB and thus just a trivial setup is | ||
| 238 | * applied here. | ||
| 239 | */ | ||
| 240 | usleep_range(12000, 12100); | ||
| 241 | |||
| 242 | } else { | ||
| 243 | ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == 0, | ||
| 244 | MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||
| 245 | if (ret < 0) | ||
| 246 | goto err_pwr_ack; | ||
| 247 | } | ||
| 248 | 344 | ||
| 249 | if (scpd->data->bus_prot_mask) { | 345 | ret = scpsys_bus_protect_disable(scpd); |
| 250 | ret = mtk_infracfg_clear_bus_protection(scp->infracfg, | 346 | if (ret < 0) |
| 251 | scpd->data->bus_prot_mask, | 347 | goto err_pwr_ack; |
| 252 | scp->bus_prot_reg_update); | ||
| 253 | if (ret) | ||
| 254 | goto err_pwr_ack; | ||
| 255 | } | ||
| 256 | 348 | ||
| 257 | return 0; | 349 | return 0; |
| 258 | 350 | ||
| 259 | err_pwr_ack: | 351 | err_pwr_ack: |
| 260 | for (i = MAX_CLKS - 1; i >= 0; i--) { | 352 | scpsys_clk_disable(scpd->clk, MAX_CLKS); |
| 261 | if (scpd->clk[i]) | ||
| 262 | clk_disable_unprepare(scpd->clk[i]); | ||
| 263 | } | ||
| 264 | err_clk: | 353 | err_clk: |
| 265 | if (scpd->supply) | 354 | scpsys_regulator_disable(scpd); |
| 266 | regulator_disable(scpd->supply); | ||
| 267 | 355 | ||
| 268 | dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); | 356 | dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); |
| 269 | 357 | ||
| @@ -275,29 +363,19 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) · 5 unmodified lines | |||
| 275 | struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); | 363 | struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); |
| 276 | struct scp *scp = scpd->scp; | 364 | struct scp *scp = scpd->scp; |
| 277 | void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; | 365 | void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; |
| 278 | u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | ||
| 279 | u32 val; | 366 | u32 val; |
| 280 | int ret, tmp; | 367 | int ret, tmp; |
| 281 | int i; | ||
| 282 | |||
| 283 | if (scpd->data->bus_prot_mask) { | ||
| 284 | ret = mtk_infracfg_set_bus_protection(scp->infracfg, | ||
| 285 | scpd->data->bus_prot_mask, | ||
| 286 | scp->bus_prot_reg_update); | ||
| 287 | if (ret) | ||
| 288 | goto out; | ||
| 289 | } | ||
| 290 | 368 | ||
| 291 | val = readl(ctl_addr); | 369 | ret = scpsys_bus_protect_enable(scpd); |
| 292 | val |= scpd->data->sram_pdn_bits; | 370 | if (ret < 0) |
| 293 | writel(val, ctl_addr); | 371 | goto out; |
| 294 | 372 | ||
| 295 | /* wait until SRAM_PDN_ACK all 1 */ | 373 | ret = scpsys_sram_disable(scpd, ctl_addr); |
| 296 | ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack, | ||
| 297 | MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); | ||
| 298 | if (ret < 0) | 374 | if (ret < 0) |
| 299 | goto out; | 375 | goto out; |
| 300 | 376 | ||
| 377 | /* subsys power off */ | ||
| 378 | val = readl(ctl_addr); | ||
| 301 | val |= PWR_ISO_BIT; | 379 | val |= PWR_ISO_BIT; |
| 302 | writel(val, ctl_addr); | 380 | writel(val, ctl_addr); |
| 303 | 381 | ||
| @@ -319,11 +397,11 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) · 15 unmodified lines | |||
| 319 | if (ret < 0) | 397 | if (ret < 0) |
| 320 | goto out; | 398 | goto out; |
| 321 | 399 | ||
| 322 | for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) | 400 | scpsys_clk_disable(scpd->clk, MAX_CLKS); |
| 323 | clk_disable_unprepare(scpd->clk[i]); | ||
| 324 | 401 | ||
| 325 | if (scpd->supply) | 402 | ret = scpsys_regulator_disable(scpd); |
| 326 | regulator_disable(scpd->supply); | 403 | if (ret < 0) |
| 404 | goto out; | ||
| 327 | 405 | ||
| 328 | return 0; | 406 | return 0; |
| 329 | 407 | ||
| @@ -58,17 +58,9 @@ config QCOM_LLCC · 57 unmodified lines | |||
| 58 | depends on ARCH_QCOM || COMPILE_TEST | 58 | depends on ARCH_QCOM || COMPILE_TEST |
| 59 | help | 59 | help |
| 60 | Qualcomm Technologies, Inc. platform specific | 60 | Qualcomm Technologies, Inc. platform specific |
| 61 | Last Level Cache Controller(LLCC) driver. This provides interfaces | 61 | Last Level Cache Controller(LLCC) driver for platforms such as, |
| 62 | to clients that use the LLCC. Say yes here to enable LLCC slice | 62 | SDM845. This provides interfaces to clients that use the LLCC. |
| 63 | driver. | 63 | Say yes here to enable LLCC slice driver. |
| 64 | |||
| 65 | config QCOM_SDM845_LLCC | ||
| 66 | tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver" | ||
| 67 | depends on QCOM_LLCC | ||
| 68 | help | ||
| 69 | Say yes here to enable the LLCC driver for SDM845. This provides | ||
| 70 | data required to configure LLCC so that clients can start using the | ||
| 71 | LLCC slices. | ||
| 72 | 64 | ||
| 73 | config QCOM_MDT_LOADER | 65 | config QCOM_MDT_LOADER |
| 74 | tristate | 66 | tristate |
| @@ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_SMSM) += smsm.o · 20 unmodified lines | |||
| 21 | obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o | 21 | obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o |
| 22 | obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o | 22 | obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o |
| 23 | obj-$(CONFIG_QCOM_APR) += apr.o | 23 | obj-$(CONFIG_QCOM_APR) += apr.o |
| 24 | obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o | 24 | obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o |
| 25 | obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o | ||
| 26 | obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o | 25 | obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o |
| 27 | obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o | 26 | obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o |
| @@ -1,6 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. |
| 4 | * | 4 | * |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| @@ -11,6 +11,7 @@ · 4 unmodified lines | |||
| 11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
| 12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
| 13 | #include <linux/mutex.h> | 13 | #include <linux/mutex.h> |
| 14 | #include <linux/of.h> | ||
| 14 | #include <linux/of_device.h> | 15 | #include <linux/of_device.h> |
| 15 | #include <linux/regmap.h> | 16 | #include <linux/regmap.h> |
| 16 | #include <linux/sizes.h> | 17 | #include <linux/sizes.h> |
| @@ -46,15 +47,90 @@ · 29 unmodified lines | |||
| 46 | 47 | ||
| 47 | #define BANK_OFFSET_STRIDE 0x80000 | 48 | #define BANK_OFFSET_STRIDE 0x80000 |
| 48 | 49 | ||
| 49 | static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; | 50 | /** |
| 51 | * llcc_slice_config - Data associated with the llcc slice | ||
| 52 | * @usecase_id: Unique id for the client's use case | ||
| 53 | * @slice_id: llcc slice id for each client | ||
| 54 | * @max_cap: The maximum capacity of the cache slice provided in KB | ||
| 55 | * @priority: Priority of the client used to select victim line for replacement | ||
| 56 | * @fixed_size: Boolean indicating if the slice has a fixed capacity | ||
| 57 | * @bonus_ways: Bonus ways are additional ways to be used for any slice, | ||
| 58 | * if client ends up using more than reserved cache ways. Bonus | ||
| 59 | * ways are allocated only if they are not reserved for some | ||
| 60 | * other client. | ||
| 61 | * @res_ways: Reserved ways for the cache slice, the reserved ways cannot | ||
| 62 | * be used by any other client than the one its assigned to. | ||
| 63 | * @cache_mode: Each slice operates as a cache, this controls the mode of the | ||
| 64 | * slice: normal or TCM(Tightly Coupled Memory) | ||
| 65 | * @probe_target_ways: Determines what ways to probe for access hit. When | ||
| 66 | * configured to 1 only bonus and reserved ways are probed. | ||
| 67 | * When configured to 0 all ways in llcc are probed. | ||
| 68 | * @dis_cap_alloc: Disable capacity based allocation for a client | ||
| 69 | * @retain_on_pc: If this bit is set and client has maintained active vote | ||
| 70 | * then the ways assigned to this client are not flushed on power | ||
| 71 | * collapse. | ||
| 72 | * @activate_on_init: Activate the slice immediately after it is programmed | ||
| 73 | */ | ||
| 74 | struct llcc_slice_config { | ||
| 75 | u32 usecase_id; | ||
| 76 | u32 slice_id; | ||
| 77 | u32 max_cap; | ||
| 78 | u32 priority; | ||
| 79 | bool fixed_size; | ||
| 80 | u32 bonus_ways; | ||
| 81 | u32 res_ways; | ||
| 82 | u32 cache_mode; | ||
| 83 | u32 probe_target_ways; | ||
| 84 | bool dis_cap_alloc; | ||
| 85 | bool retain_on_pc; | ||
| 86 | bool activate_on_init; | ||
| 87 | }; | ||
| 88 | |||
| 89 | struct qcom_llcc_config { | ||
| 90 | const struct llcc_slice_config *sct_data; | ||
| 91 | int size; | ||
| 92 | }; | ||
| 93 | |||
| 94 | static const struct llcc_slice_config sc7180_data[] = { | ||
| 95 | { LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 }, | ||
| 96 | { LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, | ||
| 97 | { LLCC_GPUHTW, 11, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, | ||
| 98 | { LLCC_GPU, 12, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, | ||
| 99 | }; | ||
| 100 | |||
| 101 | static const struct llcc_slice_config sdm845_data[] = { | ||
| 102 | { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, | ||
| 103 | { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, | ||
| 104 | { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, | ||
| 105 | { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 }, | ||
| 106 | { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 107 | { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 108 | { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 }, | ||
| 109 | { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 110 | { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 111 | { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 }, | ||
| 112 | { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 }, | ||
| 113 | { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 }, | ||
| 114 | { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 115 | { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 116 | { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 117 | { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 }, | ||
| 118 | { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 }, | ||
| 119 | { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, | ||
| 120 | }; | ||
| 121 | |||
| 122 | static const struct qcom_llcc_config sc7180_cfg = { | ||
| 123 | .sct_data = sc7180_data, | ||
| 124 | .size = ARRAY_SIZE(sc7180_data), | ||
| 125 | }; | ||
| 50 | 126 | ||
| 51 | static const struct regmap_config llcc_regmap_config = { | 127 | static const struct qcom_llcc_config sdm845_cfg = { |
| 52 | .reg_bits = 32, | 128 | .sct_data = sdm845_data, |
| 53 | .reg_stride = 4, | 129 | .size = ARRAY_SIZE(sdm845_data), |
| 54 | .val_bits = 32, | ||
| 55 | .fast_io = true, | ||
| 56 | }; | 130 | }; |
| 57 | 131 | ||
| 132 | static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; | ||
| 133 | |||
| 58 | /** | 134 | /** |
| 59 | * llcc_slice_getd - get llcc slice descriptor | 135 | * llcc_slice_getd - get llcc slice descriptor |
| 60 | * @uid: usecase_id for the client | 136 | * @uid: usecase_id for the client |
| @@ -301,19 +377,24 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) · 240 unmodified lines | |||
| 301 | return ret; | 377 | return ret; |
| 302 | } | 378 | } |
| 303 | 379 | ||
| 304 | int qcom_llcc_remove(struct platform_device *pdev) | 380 | static int qcom_llcc_remove(struct platform_device *pdev) |
| 305 | { | 381 | { |
| 306 | /* Set the global pointer to a error code to avoid referencing it */ | 382 | /* Set the global pointer to a error code to avoid referencing it */ |
| 307 | drv_data = ERR_PTR(-ENODEV); | 383 | drv_data = ERR_PTR(-ENODEV); |
| 308 | return 0; | 384 | return 0; |
| 309 | } | 385 | } |
| 310 | EXPORT_SYMBOL_GPL(qcom_llcc_remove); | ||
| 311 | 386 | ||
| 312 | static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, | 387 | static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, |
| 313 | const char *name) | 388 | const char *name) |
| 314 | { | 389 | { |
| 315 | struct resource *res; | 390 | struct resource *res; |
| 316 | void __iomem *base; | 391 | void __iomem *base; |
| 392 | struct regmap_config llcc_regmap_config = { | ||
| 393 | .reg_bits = 32, | ||
| 394 | .reg_stride = 4, | ||
| 395 | .val_bits = 32, | ||
| 396 | .fast_io = true, | ||
| 397 | }; | ||
| 317 | 398 | ||
| 318 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | 399 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); |
| 319 | if (!res) | 400 | if (!res) |
| @@ -323,16 +404,19 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, · 3 unmodified lines | |||
| 323 | if (IS_ERR(base)) | 404 | if (IS_ERR(base)) |
| 324 | return ERR_CAST(base); | 405 | return ERR_CAST(base); |
| 325 | 406 | ||
| 407 | llcc_regmap_config.name = name; | ||
| 326 | return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); | 408 | return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); |
| 327 | } | 409 | } |
| 328 | 410 | ||
| 329 | int qcom_llcc_probe(struct platform_device *pdev, | 411 | static int qcom_llcc_probe(struct platform_device *pdev) |
| 330 | const struct llcc_slice_config *llcc_cfg, u32 sz) | ||
| 331 | { | 412 | { |
| 332 | u32 num_banks; | 413 | u32 num_banks; |
| 333 | struct device *dev = &pdev->dev; | 414 | struct device *dev = &pdev->dev; |
| 334 | int ret, i; | 415 | int ret, i; |
| 335 | struct platform_device *llcc_edac; | 416 | struct platform_device *llcc_edac; |
| 417 | const struct qcom_llcc_config *cfg; | ||
| 418 | const struct llcc_slice_config *llcc_cfg; | ||
| 419 | u32 sz; | ||
| 336 | 420 | ||
| 337 | drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); | 421 | drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); |
| 338 | if (!drv_data) { | 422 | if (!drv_data) { |
| @@ -362,6 +446,10 @@ int qcom_llcc_probe(struct platform_device *pdev, · 23 unmodified lines | |||
| 362 | num_banks >>= LLCC_LB_CNT_SHIFT; | 446 | num_banks >>= LLCC_LB_CNT_SHIFT; |
| 363 | drv_data->num_banks = num_banks; | 447 | drv_data->num_banks = num_banks; |
| 364 | 448 | ||
| 449 | cfg = of_device_get_match_data(&pdev->dev); | ||
| 450 | llcc_cfg = cfg->sct_data; | ||
| 451 | sz = cfg->size; | ||
| 452 | |||
| 365 | for (i = 0; i < sz; i++) | 453 | for (i = 0; i < sz; i++) |
| 366 | if (llcc_cfg[i].slice_id > drv_data->max_slices) | 454 | if (llcc_cfg[i].slice_id > drv_data->max_slices) |
| 367 | drv_data->max_slices = llcc_cfg[i].slice_id; | 455 | drv_data->max_slices = llcc_cfg[i].slice_id; |
| @@ -407,6 +495,22 @@ err: · 39 unmodified lines | |||
| 407 | drv_data = ERR_PTR(-ENODEV); | 495 | drv_data = ERR_PTR(-ENODEV); |
| 408 | return ret; | 496 | return ret; |
| 409 | } | 497 | } |
| 410 | EXPORT_SYMBOL_GPL(qcom_llcc_probe); | 498 | |
| 411 | MODULE_LICENSE("GPL v2"); | 499 | static const struct of_device_id qcom_llcc_of_match[] = { |
| 500 | { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, | ||
| 501 | { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, | ||
| 502 | { } | ||
| 503 | }; | ||
| 504 | |||
| 505 | static struct platform_driver qcom_llcc_driver = { | ||
| 506 | .driver = { | ||
| 507 | .name = "qcom-llcc", | ||
| 508 | .of_match_table = qcom_llcc_of_match, | ||
| 509 | }, | ||
| 510 | .probe = qcom_llcc_probe, | ||
| 511 | .remove = qcom_llcc_remove, | ||
| 512 | }; | ||
| 513 | module_platform_driver(qcom_llcc_driver); | ||
| 514 | |||
| 412 | MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); | 515 | MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); |
| 516 | MODULE_LICENSE("GPL v2"); | ||
| @@ -1,100 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | ||
| 4 | * | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/kernel.h> | ||
| 8 | #include <linux/module.h> | ||
| 9 | #include <linux/of.h> | ||
| 10 | #include <linux/of_device.h> | ||
| 11 | #include <linux/soc/qcom/llcc-qcom.h> | ||
| 12 | |||
| 13 | /* | ||
| 14 | * SCT(System Cache Table) entry contains of the following members: | ||
| 15 | * usecase_id: Unique id for the client's use case | ||
| 16 | * slice_id: llcc slice id for each client | ||
| 17 | * max_cap: The maximum capacity of the cache slice provided in KB | ||
| 18 | * priority: Priority of the client used to select victim line for replacement | ||
| 19 | * fixed_size: Boolean indicating if the slice has a fixed capacity | ||
| 20 | * bonus_ways: Bonus ways are additional ways to be used for any slice, | ||
| 21 | * if client ends up using more than reserved cache ways. Bonus | ||
| 22 | * ways are allocated only if they are not reserved for some | ||
| 23 | * other client. | ||
| 24 | * res_ways: Reserved ways for the cache slice, the reserved ways cannot | ||
| 25 | * be used by any other client than the one its assigned to. | ||
| 26 | * cache_mode: Each slice operates as a cache, this controls the mode of the | ||
| 27 | * slice: normal or TCM(Tightly Coupled Memory) | ||
| 28 | * probe_target_ways: Determines what ways to probe for access hit. When | ||
| 29 | * configured to 1 only bonus and reserved ways are probed. | ||
| 30 | * When configured to 0 all ways in llcc are probed. | ||
| 31 | * dis_cap_alloc: Disable capacity based allocation for a client | ||
| 32 | * retain_on_pc: If this bit is set and client has maintained active vote | ||
| 33 | * then the ways assigned to this client are not flushed on power | ||
| 34 | * collapse. | ||
| 35 | * activate_on_init: Activate the slice immediately after the SCT is programmed | ||
| 36 | */ | ||
| 37 | #define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \ | ||
| 38 | { \ | ||
| 39 | .usecase_id = uid, \ | ||
| 40 | .slice_id = sid, \ | ||
| 41 | .max_cap = mc, \ | ||
| 42 | .priority = p, \ | ||
| 43 | .fixed_size = fs, \ | ||
| 44 | .bonus_ways = bway, \ | ||
| 45 | .res_ways = rway, \ | ||
| 46 | .cache_mode = cmod, \ | ||
| 47 | .probe_target_ways = ptw, \ | ||
| 48 | .dis_cap_alloc = dca, \ | ||
| 49 | .retain_on_pc = rp, \ | ||
| 50 | .activate_on_init = a, \ | ||
| 51 | } | ||
| 52 | |||
| 53 | static struct llcc_slice_config sdm845_data[] = { | ||
| 54 | SCT_ENTRY(LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1), | ||
| 55 | SCT_ENTRY(LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), | ||
| 56 | SCT_ENTRY(LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), | ||
| 57 | SCT_ENTRY(LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0), | ||
| 58 | SCT_ENTRY(LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 59 | SCT_ENTRY(LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 60 | SCT_ENTRY(LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0), | ||
| 61 | SCT_ENTRY(LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 62 | SCT_ENTRY(LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 63 | SCT_ENTRY(LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0), | ||
| 64 | SCT_ENTRY(LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0), | ||
| 65 | SCT_ENTRY(LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1), | ||
| 66 | SCT_ENTRY(LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 67 | SCT_ENTRY(LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 68 | SCT_ENTRY(LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 69 | SCT_ENTRY(LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0), | ||
| 70 | SCT_ENTRY(LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0), | ||
| 71 | SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
| 72 | }; | ||
| 73 | |||
| 74 | static int sdm845_qcom_llcc_remove(struct platform_device *pdev) | ||
| 75 | { | ||
| 76 | return qcom_llcc_remove(pdev); | ||
| 77 | } | ||
| 78 | |||
| 79 | static int sdm845_qcom_llcc_probe(struct platform_device *pdev) | ||
| 80 | { | ||
| 81 | return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); | ||
| 82 | } | ||
| 83 | |||
| 84 | static const struct of_device_id sdm845_qcom_llcc_of_match[] = { | ||
| 85 | { .compatible = "qcom,sdm845-llcc", }, | ||
| 86 | { } | ||
| 87 | }; | ||
| 88 | |||
| 89 | static struct platform_driver sdm845_qcom_llcc_driver = { | ||
| 90 | .driver = { | ||
| 91 | .name = "sdm845-llcc", | ||
| 92 | .of_match_table = sdm845_qcom_llcc_of_match, | ||
| 93 | }, | ||
| 94 | .probe = sdm845_qcom_llcc_probe, | ||
| 95 | .remove = sdm845_qcom_llcc_remove, | ||
| 96 | }; | ||
| 97 | module_platform_driver(sdm845_qcom_llcc_driver); | ||
| 98 | |||
| 99 | MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); | ||
| 100 | MODULE_LICENSE("GPL v2"); | ||
| @@ -115,6 +115,28 @@ struct rpmpd_desc { · 114 unmodified lines | |||
| 115 | 115 | ||
| 116 | static DEFINE_MUTEX(rpmpd_lock); | 116 | static DEFINE_MUTEX(rpmpd_lock); |
| 117 | 117 | ||
| 118 | /* msm8976 RPM Power Domains */ | ||
| 119 | DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2); | ||
| 120 | DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6); | ||
| 121 | |||
| 122 | DEFINE_RPMPD_VFL(msm8976, vddcx_vfl, RWSC, 2); | ||
| 123 | DEFINE_RPMPD_VFL(msm8976, vddmx_vfl, RWSM, 6); | ||
| 124 | |||
| 125 | static struct rpmpd *msm8976_rpmpds[] = { | ||
| 126 | [MSM8976_VDDCX] = &msm8976_vddcx, | ||
| 127 | [MSM8976_VDDCX_AO] = &msm8976_vddcx_ao, | ||
| 128 | [MSM8976_VDDCX_VFL] = &msm8976_vddcx_vfl, | ||
| 129 | [MSM8976_VDDMX] = &msm8976_vddmx, | ||
| 130 | [MSM8976_VDDMX_AO] = &msm8976_vddmx_ao, | ||
| 131 | [MSM8976_VDDMX_VFL] = &msm8976_vddmx_vfl, | ||
| 132 | }; | ||
| 133 | |||
| 134 | static const struct rpmpd_desc msm8976_desc = { | ||
| 135 | .rpmpds = msm8976_rpmpds, | ||
| 136 | .num_pds = ARRAY_SIZE(msm8976_rpmpds), | ||
| 137 | .max_state = RPM_SMD_LEVEL_TURBO_HIGH, | ||
| 138 | }; | ||
| 139 | |||
| 118 | /* msm8996 RPM Power domains */ | 140 | /* msm8996 RPM Power domains */ |
| 119 | DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1); | 141 | DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1); |
| 120 | DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2); | 142 | DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2); |
| @@ -198,6 +220,7 @@ static const struct rpmpd_desc qcs404_desc = { · 77 unmodified lines | |||
| 198 | }; | 220 | }; |
| 199 | 221 | ||
| 200 | static const struct of_device_id rpmpd_match_table[] = { | 222 | static const struct of_device_id rpmpd_match_table[] = { |
| 223 | { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, | ||
| 201 | { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, | 224 | { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, |
| 202 | { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, | 225 | { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, |
| 203 | { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, | 226 | { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, |
| @@ -19,12 +19,14 @@ · 18 unmodified lines | |||
| 19 | /** | 19 | /** |
| 20 | * struct qcom_smd_rpm - state of the rpm device driver | 20 | * struct qcom_smd_rpm - state of the rpm device driver |
| 21 | * @rpm_channel: reference to the smd channel | 21 | * @rpm_channel: reference to the smd channel |
| 22 | * @icc: interconnect proxy device | ||
| 22 | * @ack: completion for acks | 23 | * @ack: completion for acks |
| 23 | * @lock: mutual exclusion around the send/complete pair | 24 | * @lock: mutual exclusion around the send/complete pair |
| 24 | * @ack_status: result of the rpm request | 25 | * @ack_status: result of the rpm request |
| 25 | */ | 26 | */ |
| 26 | struct qcom_smd_rpm { | 27 | struct qcom_smd_rpm { |
| 27 | struct rpmsg_endpoint *rpm_channel; | 28 | struct rpmsg_endpoint *rpm_channel; |
| 29 | struct platform_device *icc; | ||
| 28 | struct device *dev; | 30 | struct device *dev; |
| 29 | 31 | ||
| 30 | struct completion ack; | 32 | struct completion ack; |
| @@ -193,6 +195,7 @@ static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, · 162 unmodified lines | |||
| 193 | static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) | 195 | static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) |
| 194 | { | 196 | { |
| 195 | struct qcom_smd_rpm *rpm; | 197 | struct qcom_smd_rpm *rpm; |
| 198 | int ret; | ||
| 196 | 199 | ||
| 197 | rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); | 200 | rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); |
| 198 | if (!rpm) | 201 | if (!rpm) |
| @@ -205,11 +208,23 @@ static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) · 6 unmodified lines | |||
| 205 | rpm->rpm_channel = rpdev->ept; | 208 | rpm->rpm_channel = rpdev->ept; |
| 206 | dev_set_drvdata(&rpdev->dev, rpm); | 209 | dev_set_drvdata(&rpdev->dev, rpm); |
| 207 | 210 | ||
| 208 | return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); | 211 | rpm->icc = platform_device_register_data(&rpdev->dev, "icc_smd_rpm", -1, |
| 212 | NULL, 0); | ||
| 213 | if (IS_ERR(rpm->icc)) | ||
| 214 | return PTR_ERR(rpm->icc); | ||
| 215 | |||
| 216 | ret = of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); | ||
| 217 | if (ret) | ||
| 218 | platform_device_unregister(rpm->icc); | ||
| 219 | |||
| 220 | return ret; | ||
| 209 | } | 221 | } |
| 210 | 222 | ||
| 211 | static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) | 223 | static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) |
| 212 | { | 224 | { |
| 225 | struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev); | ||
| 226 | |||
| 227 | platform_device_unregister(rpm->icc); | ||
| 213 | of_platform_depopulate(&rpdev->dev); | 228 | of_platform_depopulate(&rpdev->dev); |
| 214 | } | 229 | } |
| 215 | 230 | ||
| @@ -217,6 +232,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { · 1 unmodified line | |||
| 217 | { .compatible = "qcom,rpm-apq8084" }, | 232 | { .compatible = "qcom,rpm-apq8084" }, |
| 218 | { .compatible = "qcom,rpm-msm8916" }, | 233 | { .compatible = "qcom,rpm-msm8916" }, |
| 219 | { .compatible = "qcom,rpm-msm8974" }, | 234 | { .compatible = "qcom,rpm-msm8974" }, |
| 235 | { .compatible = "qcom,rpm-msm8976" }, | ||
| 220 | { .compatible = "qcom,rpm-msm8996" }, | 236 | { .compatible = "qcom,rpm-msm8996" }, |
| 221 | { .compatible = "qcom,rpm-msm8998" }, | 237 | { .compatible = "qcom,rpm-msm8998" }, |
| 222 | { .compatible = "qcom,rpm-sdm660" }, | 238 | { .compatible = "qcom,rpm-sdm660" }, |
| @@ -198,6 +198,8 @@ static const struct soc_id soc_id[] = { · 197 unmodified lines | |||
| 198 | { 310, "MSM8996AU" }, | 198 | { 310, "MSM8996AU" }, |
| 199 | { 311, "APQ8096AU" }, | 199 | { 311, "APQ8096AU" }, |
| 200 | { 312, "APQ8096SG" }, | 200 | { 312, "APQ8096SG" }, |
| 201 | { 321, "SDM845" }, | ||
| 202 | { 341, "SDA845" }, | ||
| 201 | }; | 203 | }; |
| 202 | 204 | ||
| 203 | static const char *socinfo_machine(struct device *dev, unsigned int id) | 205 | static const char *socinfo_machine(struct device *dev, unsigned int id) |
| @@ -178,6 +178,13 @@ config ARCH_R8A774A1 · 177 unmodified lines | |||
| 178 | help | 178 | help |
| 179 | This enables support for the Renesas RZ/G2M SoC. | 179 | This enables support for the Renesas RZ/G2M SoC. |
| 180 | 180 | ||
| 181 | config ARCH_R8A774B1 | ||
| 182 | bool "Renesas RZ/G2N SoC Platform" | ||
| 183 | select ARCH_RCAR_GEN3 | ||
| 184 | select SYSC_R8A774B1 | ||
| 185 | help | ||
| 186 | This enables support for the Renesas RZ/G2N SoC. | ||
| 187 | |||
| 181 | config ARCH_R8A774C0 | 188 | config ARCH_R8A774C0 |
| 182 | bool "Renesas RZ/G2E SoC Platform" | 189 | bool "Renesas RZ/G2E SoC Platform" |
| 183 | select ARCH_RCAR_GEN3 | 190 | select ARCH_RCAR_GEN3 |
| @@ -192,13 +199,24 @@ config ARCH_R8A7795 · 8 unmodified lines | |||
| 192 | help | 199 | help |
| 193 | This enables support for the Renesas R-Car H3 SoC. | 200 | This enables support for the Renesas R-Car H3 SoC. |
| 194 | 201 | ||
| 202 | config ARCH_R8A77960 | ||
| 203 | bool | ||
| 204 | select ARCH_RCAR_GEN3 | ||
| 205 | select SYSC_R8A77960 | ||
| 206 | |||
| 195 | config ARCH_R8A7796 | 207 | config ARCH_R8A7796 |
| 196 | bool "Renesas R-Car M3-W SoC Platform" | 208 | bool "Renesas R-Car M3-W SoC Platform" |
| 197 | select ARCH_RCAR_GEN3 | 209 | select ARCH_R8A77960 |
| 198 | select SYSC_R8A7796 | ||
| 199 | help | 210 | help |
| 200 | This enables support for the Renesas R-Car M3-W SoC. | 211 | This enables support for the Renesas R-Car M3-W SoC. |
| 201 | 212 | ||
| 213 | config ARCH_R8A77961 | ||
| 214 | bool "Renesas R-Car M3-W+ SoC Platform" | ||
| 215 | select ARCH_RCAR_GEN3 | ||
| 216 | select SYSC_R8A77961 | ||
| 217 | help | ||
| 218 | This enables support for the Renesas R-Car M3-W+ SoC. | ||
| 219 | |||
| 202 | config ARCH_R8A77965 | 220 | config ARCH_R8A77965 |
| 203 | bool "Renesas R-Car M3-N SoC Platform" | 221 | bool "Renesas R-Car M3-N SoC Platform" |
| 204 | select ARCH_RCAR_GEN3 | 222 | select ARCH_RCAR_GEN3 |
| @@ -253,6 +271,10 @@ config SYSC_R8A774A1 · 48 unmodified lines | |||
| 253 | bool "RZ/G2M System Controller support" if COMPILE_TEST | 271 | bool "RZ/G2M System Controller support" if COMPILE_TEST |
| 254 | select SYSC_RCAR | 272 | select SYSC_RCAR |
| 255 | 273 | ||
| 274 | config SYSC_R8A774B1 | ||
| 275 | bool "RZ/G2N System Controller support" if COMPILE_TEST | ||
| 276 | select SYSC_RCAR | ||
| 277 | |||
| 256 | config SYSC_R8A774C0 | 278 | config SYSC_R8A774C0 |
| 257 | bool "RZ/G2E System Controller support" if COMPILE_TEST | 279 | bool "RZ/G2E System Controller support" if COMPILE_TEST |
| 258 | select SYSC_RCAR | 280 | select SYSC_RCAR |
| @@ -281,10 +303,14 @@ config SYSC_R8A7795 · 22 unmodified lines | |||
| 281 | bool "R-Car H3 System Controller support" if COMPILE_TEST | 303 | bool "R-Car H3 System Controller support" if COMPILE_TEST |
| 282 | select SYSC_RCAR | 304 | select SYSC_RCAR |
| 283 | 305 | ||
| 284 | config SYSC_R8A7796 | 306 | config SYSC_R8A77960 |
| 285 | bool "R-Car M3-W System Controller support" if COMPILE_TEST | 307 | bool "R-Car M3-W System Controller support" if COMPILE_TEST |
| 286 | select SYSC_RCAR | 308 | select SYSC_RCAR |
| 287 | 309 | ||
| 310 | config SYSC_R8A77961 | ||
| 311 | bool "R-Car M3-W+ System Controller support" if COMPILE_TEST | ||
| 312 | select SYSC_RCAR | ||
| 313 | |||
| 288 | config SYSC_R8A77965 | 314 | config SYSC_R8A77965 |
| 289 | bool "R-Car M3-N System Controller support" if COMPILE_TEST | 315 | bool "R-Car M3-N System Controller support" if COMPILE_TEST |
| 290 | select SYSC_RCAR | 316 | select SYSC_RCAR |
| @@ -7,6 +7,7 @@ obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o · 6 unmodified lines | |||
| 7 | obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o | 7 | obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o |
| 8 | obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o | 8 | obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o |
| 9 | obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o | 9 | obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o |
| 10 | obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o | ||
| 10 | obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o | 11 | obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o |
| 11 | obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o | 12 | obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o |
| 12 | obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o | 13 | obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o |
| @@ -14,7 +15,8 @@ obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o · 1 unmodified line | |||
| 14 | obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o | 15 | obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o |
| 15 | obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o | 16 | obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o |
| 16 | obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o | 17 | obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o |
| 17 | obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o | 18 | obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o |
| 19 | obj-$(CONFIG_SYSC_R8A77961) += r8a7796-sysc.o | ||
| 18 | obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o | 20 | obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o |
| 19 | obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o | 21 | obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o |
| 20 | obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o | 22 | obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Cogent Embedded Inc. | 5 | * Copyright (C) 2016 Cogent Embedded Inc. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a7743-sysc.h> | 10 | #include <dt-bindings/power/r8a7743-sysc.h> |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Cogent Embedded Inc. | 5 | * Copyright (C) 2016 Cogent Embedded Inc. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a7745-sysc.h> | 10 | #include <dt-bindings/power/r8a7745-sysc.h> |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2018 Renesas Electronics Corp. | 5 | * Copyright (C) 2018 Renesas Electronics Corp. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a77470-sysc.h> | 10 | #include <dt-bindings/power/r8a77470-sysc.h> |
| @@ -7,7 +7,6 @@ · 6 unmodified lines | |||
| 7 | * Copyright (C) 2016 Glider bvba | 7 | * Copyright (C) 2016 Glider bvba |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <linux/bug.h> | ||
| 11 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 12 | 11 | ||
| 13 | #include <dt-bindings/power/r8a774a1-sysc.h> | 12 | #include <dt-bindings/power/r8a774a1-sysc.h> |
| @@ -0,0 +1,37 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Renesas RZ/G2N System Controller | ||
| 4 | * Copyright (C) 2019 Renesas Electronics Corp. | ||
| 5 | * | ||
| 6 | * Based on Renesas R-Car M3-W System Controller | ||
| 7 | * Copyright (C) 2016 Glider bvba | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/bits.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | |||
| 13 | #include <dt-bindings/power/r8a774b1-sysc.h> | ||
| 14 | |||
| 15 | #include "rcar-sysc.h" | ||
| 16 | |||
| 17 | static const struct rcar_sysc_area r8a774b1_areas[] __initconst = { | ||
| 18 | { "always-on", 0, 0, R8A774B1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, | ||
| 19 | { "ca57-scu", 0x1c0, 0, R8A774B1_PD_CA57_SCU, R8A774B1_PD_ALWAYS_ON, | ||
| 20 | PD_SCU }, | ||
| 21 | { "ca57-cpu0", 0x80, 0, R8A774B1_PD_CA57_CPU0, R8A774B1_PD_CA57_SCU, | ||
| 22 | PD_CPU_NOCR }, | ||
| 23 | { "ca57-cpu1", 0x80, 1, R8A774B1_PD_CA57_CPU1, R8A774B1_PD_CA57_SCU, | ||
| 24 | PD_CPU_NOCR }, | ||
| 25 | { "a3vc", 0x380, 0, R8A774B1_PD_A3VC, R8A774B1_PD_ALWAYS_ON }, | ||
| 26 | { "a3vp", 0x340, 0, R8A774B1_PD_A3VP, R8A774B1_PD_ALWAYS_ON }, | ||
| 27 | { "a2vc1", 0x3c0, 1, R8A774B1_PD_A2VC1, R8A774B1_PD_A3VC }, | ||
| 28 | { "3dg-a", 0x100, 0, R8A774B1_PD_3DG_A, R8A774B1_PD_ALWAYS_ON }, | ||
| 29 | { "3dg-b", 0x100, 1, R8A774B1_PD_3DG_B, R8A774B1_PD_3DG_A }, | ||
| 30 | }; | ||
| 31 | |||
| 32 | const struct rcar_sysc_info r8a774b1_sysc_info __initconst = { | ||
| 33 | .areas = r8a774b1_areas, | ||
| 34 | .num_areas = ARRAY_SIZE(r8a774b1_areas), | ||
| 35 | .extmask_offs = 0x2f8, | ||
| 36 | .extmask_val = BIT(0), | ||
| 37 | }; | ||
| @@ -6,7 +6,7 @@ · 5 unmodified lines | |||
| 6 | * Based on Renesas R-Car E3 System Controller | 6 | * Based on Renesas R-Car E3 System Controller |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | #include <linux/bug.h> | 9 | #include <linux/bits.h> |
| 10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 11 | #include <linux/sys_soc.h> | 11 | #include <linux/sys_soc.h> |
| 12 | 12 | ||
| @@ -50,4 +50,6 @@ const struct rcar_sysc_info r8a774c0_sysc_info __initconst = { · 37 unmodified lines | |||
| 50 | .init = r8a774c0_sysc_init, | 50 | .init = r8a774c0_sysc_init, |
| 51 | .areas = r8a774c0_areas, | 51 | .areas = r8a774c0_areas, |
| 52 | .num_areas = ARRAY_SIZE(r8a774c0_areas), | 52 | .num_areas = ARRAY_SIZE(r8a774c0_areas), |
| 53 | .extmask_offs = 0x2f8, | ||
| 54 | .extmask_val = BIT(0), | ||
| 53 | }; | 55 | }; |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Glider bvba | 5 | * Copyright (C) 2016 Glider bvba |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a7779-sysc.h> | 10 | #include <dt-bindings/power/r8a7779-sysc.h> |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Glider bvba | 5 | * Copyright (C) 2016 Glider bvba |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a7790-sysc.h> | 10 | #include <dt-bindings/power/r8a7790-sysc.h> |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Glider bvba | 5 | * Copyright (C) 2016 Glider bvba |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a7791-sysc.h> | 10 | #include <dt-bindings/power/r8a7791-sysc.h> |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Cogent Embedded Inc. | 5 | * Copyright (C) 2016 Cogent Embedded Inc. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/init.h> | 8 | #include <linux/init.h> |
| 10 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 11 | 10 | ||
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016 Glider bvba | 5 | * Copyright (C) 2016 Glider bvba |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a7794-sysc.h> | 10 | #include <dt-bindings/power/r8a7794-sysc.h> |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2016-2017 Glider bvba | 5 | * Copyright (C) 2016-2017 Glider bvba |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | 8 | #include <linux/bits.h> |
| 9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 10 | #include <linux/sys_soc.h> | 10 | #include <linux/sys_soc.h> |
| 11 | 11 | ||
| @@ -51,25 +51,46 @@ static struct rcar_sysc_area r8a7795_areas[] __initdata = { · 39 unmodified lines | |||
| 51 | 51 | ||
| 52 | 52 | ||
| 53 | /* | 53 | /* |
| 54 | * Fixups for R-Car H3 revisions after ES1.x | 54 | * Fixups for R-Car H3 revisions |
| 55 | */ | 55 | */ |
| 56 | 56 | ||
| 57 | static const struct soc_device_attribute r8a7795es1[] __initconst = { | 57 | #define HAS_A2VC0 BIT(0) /* Power domain A2VC0 is present */ |
| 58 | { .soc_id = "r8a7795", .revision = "ES1.*" }, | 58 | #define NO_EXTMASK BIT(1) /* Missing SYSCEXTMASK register */ |
| 59 | |||
| 60 | static const struct soc_device_attribute r8a7795_quirks_match[] __initconst = { | ||
| 61 | { | ||
| 62 | .soc_id = "r8a7795", .revision = "ES1.*", | ||
| 63 | .data = (void *)(HAS_A2VC0 | NO_EXTMASK), | ||
| 64 | }, { | ||
| 65 | .soc_id = "r8a7795", .revision = "ES2.*", | ||
| 66 | .data = (void *)(NO_EXTMASK), | ||
| 67 | }, | ||
| 59 | { /* sentinel */ } | 68 | { /* sentinel */ } |
| 60 | }; | 69 | }; |
| 61 | 70 | ||
| 62 | static int __init r8a7795_sysc_init(void) | 71 | static int __init r8a7795_sysc_init(void) |
| 63 | { | 72 | { |
| 64 | if (!soc_device_match(r8a7795es1)) | 73 | const struct soc_device_attribute *attr; |
| 74 | u32 quirks = 0; | ||
| 75 | |||
| 76 | attr = soc_device_match(r8a7795_quirks_match); | ||
| 77 | if (attr) | ||
| 78 | quirks = (uintptr_t)attr->data; | ||
| 79 | |||
| 80 | if (!(quirks & HAS_A2VC0)) | ||
| 65 | rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas), | 81 | rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas), |
| 66 | R8A7795_PD_A2VC0); | 82 | R8A7795_PD_A2VC0); |
| 67 | 83 | ||
| 84 | if (quirks & NO_EXTMASK) | ||
| 85 | r8a7795_sysc_info.extmask_val = 0; | ||
| 86 | |||
| 68 | return 0; | 87 | return 0; |
| 69 | } | 88 | } |
| 70 | 89 | ||
| 71 | const struct rcar_sysc_info r8a7795_sysc_info __initconst = { | 90 | struct rcar_sysc_info r8a7795_sysc_info __initdata = { |
| 72 | .init = r8a7795_sysc_init, | 91 | .init = r8a7795_sysc_init, |
| 73 | .areas = r8a7795_areas, | 92 | .areas = r8a7795_areas, |
| 74 | .num_areas = ARRAY_SIZE(r8a7795_areas), | 93 | .num_areas = ARRAY_SIZE(r8a7795_areas), |
| 94 | .extmask_offs = 0x2f8, | ||
| 95 | .extmask_val = BIT(0), | ||
| 75 | }; | 96 | }; |
| @@ -1,18 +1,19 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | 2 | /* |
| 3 | * Renesas R-Car M3-W System Controller | 3 | * Renesas R-Car M3-W/W+ System Controller |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2016 Glider bvba | 5 | * Copyright (C) 2016 Glider bvba |
| 6 | * Copyright (C) 2018-2019 Renesas Electronics Corporation | ||
| 6 | */ | 7 | */ |
| 7 | 8 | ||
| 8 | #include <linux/bug.h> | 9 | #include <linux/bits.h> |
| 9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 10 | 11 | ||
| 11 | #include <dt-bindings/power/r8a7796-sysc.h> | 12 | #include <dt-bindings/power/r8a7796-sysc.h> |
| 12 | 13 | ||
| 13 | #include "rcar-sysc.h" | 14 | #include "rcar-sysc.h" |
| 14 | 15 | ||
| 15 | static const struct rcar_sysc_area r8a7796_areas[] __initconst = { | 16 | static struct rcar_sysc_area r8a7796_areas[] __initdata = { |
| 16 | { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, | 17 | { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, |
| 17 | { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, | 18 | { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, |
| 18 | PD_SCU }, | 19 | PD_SCU }, |
| @@ -39,7 +40,28 @@ static const struct rcar_sysc_area r8a7796_areas[] __initconst = { · 20 unmodified lines | |||
| 39 | { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON }, | 40 | { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON }, |
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 42 | const struct rcar_sysc_info r8a7796_sysc_info __initconst = { | 43 | |
| 44 | #ifdef CONFIG_SYSC_R8A77960 | ||
| 45 | const struct rcar_sysc_info r8a77960_sysc_info __initconst = { | ||
| 46 | .areas = r8a7796_areas, | ||
| 47 | .num_areas = ARRAY_SIZE(r8a7796_areas), | ||
| 48 | }; | ||
| 49 | #endif /* CONFIG_SYSC_R8A77960 */ | ||
| 50 | |||
| 51 | #ifdef CONFIG_SYSC_R8A77961 | ||
| 52 | static int __init r8a77961_sysc_init(void) | ||
| 53 | { | ||
| 54 | rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas), | ||
| 55 | R8A7796_PD_A2VC0); | ||
| 56 | |||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | const struct rcar_sysc_info r8a77961_sysc_info __initconst = { | ||
| 61 | .init = r8a77961_sysc_init, | ||
| 43 | .areas = r8a7796_areas, | 62 | .areas = r8a7796_areas, |
| 44 | .num_areas = ARRAY_SIZE(r8a7796_areas), | 63 | .num_areas = ARRAY_SIZE(r8a7796_areas), |
| 64 | .extmask_offs = 0x2f8, | ||
| 65 | .extmask_val = BIT(0), | ||
| 45 | }; | 66 | }; |
| 67 | #endif /* CONFIG_SYSC_R8A77961 */ | ||
| @@ -7,7 +7,7 @@ · 6 unmodified lines | |||
| 7 | * Copyright (C) 2016 Glider bvba | 7 | * Copyright (C) 2016 Glider bvba |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <linux/bug.h> | 10 | #include <linux/bits.h> |
| 11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
| 12 | 12 | ||
| 13 | #include <dt-bindings/power/r8a77965-sysc.h> | 13 | #include <dt-bindings/power/r8a77965-sysc.h> |
| @@ -33,4 +33,6 @@ static const struct rcar_sysc_area r8a77965_areas[] __initconst = { · 19 unmodified lines | |||
| 33 | const struct rcar_sysc_info r8a77965_sysc_info __initconst = { | 33 | const struct rcar_sysc_info r8a77965_sysc_info __initconst = { |
| 34 | .areas = r8a77965_areas, | 34 | .areas = r8a77965_areas, |
| 35 | .num_areas = ARRAY_SIZE(r8a77965_areas), | 35 | .num_areas = ARRAY_SIZE(r8a77965_areas), |
| 36 | .extmask_offs = 0x2f8, | ||
| 37 | .extmask_val = BIT(0), | ||
| 36 | }; | 38 | }; |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2017 Cogent Embedded Inc. | 5 | * Copyright (C) 2017 Cogent Embedded Inc. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | 8 | #include <linux/bits.h> |
| 9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 10 | 10 | ||
| 11 | #include <dt-bindings/power/r8a77970-sysc.h> | 11 | #include <dt-bindings/power/r8a77970-sysc.h> |
| @@ -32,4 +32,6 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = { · 20 unmodified lines | |||
| 32 | const struct rcar_sysc_info r8a77970_sysc_info __initconst = { | 32 | const struct rcar_sysc_info r8a77970_sysc_info __initconst = { |
| 33 | .areas = r8a77970_areas, | 33 | .areas = r8a77970_areas, |
| 34 | .num_areas = ARRAY_SIZE(r8a77970_areas), | 34 | .num_areas = ARRAY_SIZE(r8a77970_areas), |
| 35 | .extmask_offs = 0x1b0, | ||
| 36 | .extmask_val = BIT(0), | ||
| 35 | }; | 37 | }; |
| @@ -6,7 +6,7 @@ · 5 unmodified lines | |||
| 6 | * Copyright (C) 2018 Cogent Embedded, Inc. | 6 | * Copyright (C) 2018 Cogent Embedded, Inc. |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | #include <linux/bug.h> | 9 | #include <linux/bits.h> |
| 10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 11 | 11 | ||
| 12 | #include <dt-bindings/power/r8a77980-sysc.h> | 12 | #include <dt-bindings/power/r8a77980-sysc.h> |
| @@ -49,4 +49,6 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = { · 36 unmodified lines | |||
| 49 | const struct rcar_sysc_info r8a77980_sysc_info __initconst = { | 49 | const struct rcar_sysc_info r8a77980_sysc_info __initconst = { |
| 50 | .areas = r8a77980_areas, | 50 | .areas = r8a77980_areas, |
| 51 | .num_areas = ARRAY_SIZE(r8a77980_areas), | 51 | .num_areas = ARRAY_SIZE(r8a77980_areas), |
| 52 | .extmask_offs = 0x138, | ||
| 53 | .extmask_val = BIT(0), | ||
| 52 | }; | 54 | }; |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2018 Renesas Electronics Corp. | 5 | * Copyright (C) 2018 Renesas Electronics Corp. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | 8 | #include <linux/bits.h> |
| 9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 10 | #include <linux/sys_soc.h> | 10 | #include <linux/sys_soc.h> |
| 11 | 11 | ||
| @@ -50,4 +50,6 @@ const struct rcar_sysc_info r8a77990_sysc_info __initconst = { · 38 unmodified lines | |||
| 50 | .init = r8a77990_sysc_init, | 50 | .init = r8a77990_sysc_init, |
| 51 | .areas = r8a77990_areas, | 51 | .areas = r8a77990_areas, |
| 52 | .num_areas = ARRAY_SIZE(r8a77990_areas), | 52 | .num_areas = ARRAY_SIZE(r8a77990_areas), |
| 53 | .extmask_offs = 0x2f8, | ||
| 54 | .extmask_val = BIT(0), | ||
| 53 | }; | 55 | }; |
| @@ -5,7 +5,6 @@ · 4 unmodified lines | |||
| 5 | * Copyright (C) 2017 Glider bvba | 5 | * Copyright (C) 2017 Glider bvba |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bug.h> | ||
| 9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 10 | 9 | ||
| 11 | #include <dt-bindings/power/r8a77995-sysc.h> | 10 | #include <dt-bindings/power/r8a77995-sysc.h> |
| @@ -45,6 +45,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { · 44 unmodified lines | |||
| 45 | { .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 }, | 45 | { .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 }, |
| 46 | /* RZ/G2 is handled like R-Car Gen3 */ | 46 | /* RZ/G2 is handled like R-Car Gen3 */ |
| 47 | { .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, | 47 | { .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, |
| 48 | { .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 }, | ||
| 48 | { .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, | 49 | { .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, |
| 49 | /* R-Car Gen1 */ | 50 | /* R-Car Gen1 */ |
| 50 | { .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, | 51 | { .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, |
| @@ -58,6 +59,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { · 7 unmodified lines | |||
| 58 | /* R-Car Gen3 */ | 59 | /* R-Car Gen3 */ |
| 59 | { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, | 60 | { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, |
| 60 | { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, | 61 | { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, |
| 62 | { .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 }, | ||
| 61 | { .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 }, | 63 | { .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 }, |
| 62 | { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, | 64 | { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, |
| 63 | { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, | 65 | { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, |
| @@ -63,6 +63,7 @@ struct rcar_sysc_ch { · 62 unmodified lines | |||
| 63 | 63 | ||
| 64 | static void __iomem *rcar_sysc_base; | 64 | static void __iomem *rcar_sysc_base; |
| 65 | static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ | 65 | static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ |
| 66 | static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; | ||
| 66 | 67 | ||
| 67 | static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) | 68 | static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) |
| 68 | { | 69 | { |
| @@ -105,6 +106,14 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) · 36 unmodified lines | |||
| 105 | 106 | ||
| 106 | spin_lock_irqsave(&rcar_sysc_lock, flags); | 107 | spin_lock_irqsave(&rcar_sysc_lock, flags); |
| 107 | 108 | ||
| 109 | /* | ||
| 110 | * Mask external power requests for CPU or 3DG domains | ||
| 111 | */ | ||
| 112 | if (rcar_sysc_extmask_val) { | ||
| 113 | iowrite32(rcar_sysc_extmask_val, | ||
| 114 | rcar_sysc_base + rcar_sysc_extmask_offs); | ||
| 115 | } | ||
| 116 | |||
| 108 | /* | 117 | /* |
| 109 | * The interrupt source needs to be enabled, but masked, to prevent the | 118 | * The interrupt source needs to be enabled, but masked, to prevent the |
| 110 | * CPU from receiving it. | 119 | * CPU from receiving it. |
| @@ -148,6 +157,9 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) · 37 unmodified lines | |||
| 148 | iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); | 157 | iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); |
| 149 | 158 | ||
| 150 | out: | 159 | out: |
| 160 | if (rcar_sysc_extmask_val) | ||
| 161 | iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs); | ||
| 162 | |||
| 151 | spin_unlock_irqrestore(&rcar_sysc_lock, flags); | 163 | spin_unlock_irqrestore(&rcar_sysc_lock, flags); |
| 152 | 164 | ||
| 153 | pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", | 165 | 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 = { · 121 unmodified lines | |||
| 275 | #ifdef CONFIG_SYSC_R8A774A1 | 287 | #ifdef CONFIG_SYSC_R8A774A1 |
| 276 | { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, | 288 | { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, |
| 277 | #endif | 289 | #endif |
| 290 | #ifdef CONFIG_SYSC_R8A774B1 | ||
| 291 | { .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info }, | ||
| 292 | #endif | ||
| 278 | #ifdef CONFIG_SYSC_R8A774C0 | 293 | #ifdef CONFIG_SYSC_R8A774C0 |
| 279 | { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, | 294 | { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, |
| 280 | #endif | 295 | #endif |
| @@ -298,8 +313,11 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { · 17 unmodified lines | |||
| 298 | #ifdef CONFIG_SYSC_R8A7795 | 313 | #ifdef CONFIG_SYSC_R8A7795 |
| 299 | { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, | 314 | { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, |
| 300 | #endif | 315 | #endif |
| 301 | #ifdef CONFIG_SYSC_R8A7796 | 316 | #ifdef CONFIG_SYSC_R8A77960 |
| 302 | { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info }, | 317 | { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, |
| 318 | #endif | ||
| 319 | #ifdef CONFIG_SYSC_R8A77961 | ||
| 320 | { .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info }, | ||
| 303 | #endif | 321 | #endif |
| 304 | #ifdef CONFIG_SYSC_R8A77965 | 322 | #ifdef CONFIG_SYSC_R8A77965 |
| 305 | { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, | 323 | { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, |
| @@ -360,6 +378,10 @@ static int __init rcar_sysc_pd_init(void) · 54 unmodified lines | |||
| 360 | 378 | ||
| 361 | rcar_sysc_base = base; | 379 | rcar_sysc_base = base; |
| 362 | 380 | ||
| 381 | /* Optional External Request Mask Register */ | ||
| 382 | rcar_sysc_extmask_offs = info->extmask_offs; | ||
| 383 | rcar_sysc_extmask_val = info->extmask_val; | ||
| 384 | |||
| 363 | domains = kzalloc(sizeof(*domains), GFP_KERNEL); | 385 | domains = kzalloc(sizeof(*domains), GFP_KERNEL); |
| 364 | if (!domains) { | 386 | if (!domains) { |
| 365 | error = -ENOMEM; | 387 | error = -ENOMEM; |
| @@ -44,20 +44,25 @@ struct rcar_sysc_info { · 43 unmodified lines | |||
| 44 | int (*init)(void); /* Optional */ | 44 | int (*init)(void); /* Optional */ |
| 45 | const struct rcar_sysc_area *areas; | 45 | const struct rcar_sysc_area *areas; |
| 46 | unsigned int num_areas; | 46 | unsigned int num_areas; |
| 47 | /* Optional External Request Mask Register */ | ||
| 48 | u32 extmask_offs; /* SYSCEXTMASK register offset */ | ||
| 49 | u32 extmask_val; /* SYSCEXTMASK register mask value */ | ||
| 47 | }; | 50 | }; |
| 48 | 51 | ||
| 49 | extern const struct rcar_sysc_info r8a7743_sysc_info; | 52 | extern const struct rcar_sysc_info r8a7743_sysc_info; |
| 50 | extern const struct rcar_sysc_info r8a7745_sysc_info; | 53 | extern const struct rcar_sysc_info r8a7745_sysc_info; |
| 51 | extern const struct rcar_sysc_info r8a77470_sysc_info; | 54 | extern const struct rcar_sysc_info r8a77470_sysc_info; |
| 52 | extern const struct rcar_sysc_info r8a774a1_sysc_info; | 55 | extern const struct rcar_sysc_info r8a774a1_sysc_info; |
| 56 | extern const struct rcar_sysc_info r8a774b1_sysc_info; | ||
| 53 | extern const struct rcar_sysc_info r8a774c0_sysc_info; | 57 | extern const struct rcar_sysc_info r8a774c0_sysc_info; |
| 54 | extern const struct rcar_sysc_info r8a7779_sysc_info; | 58 | extern const struct rcar_sysc_info r8a7779_sysc_info; |
| 55 | extern const struct rcar_sysc_info r8a7790_sysc_info; | 59 | extern const struct rcar_sysc_info r8a7790_sysc_info; |
| 56 | extern const struct rcar_sysc_info r8a7791_sysc_info; | 60 | extern const struct rcar_sysc_info r8a7791_sysc_info; |
| 57 | extern const struct rcar_sysc_info r8a7792_sysc_info; | 61 | extern const struct rcar_sysc_info r8a7792_sysc_info; |
| 58 | extern const struct rcar_sysc_info r8a7794_sysc_info; | 62 | extern const struct rcar_sysc_info r8a7794_sysc_info; |
| 59 | extern const struct rcar_sysc_info r8a7795_sysc_info; | 63 | extern struct rcar_sysc_info r8a7795_sysc_info; |
| 60 | extern const struct rcar_sysc_info r8a7796_sysc_info; | 64 | extern const struct rcar_sysc_info r8a77960_sysc_info; |
| 65 | extern const struct rcar_sysc_info r8a77961_sysc_info; | ||
| 61 | extern const struct rcar_sysc_info r8a77965_sysc_info; | 66 | extern const struct rcar_sysc_info r8a77965_sysc_info; |
| 62 | extern const struct rcar_sysc_info r8a77970_sysc_info; | 67 | extern const struct rcar_sysc_info r8a77970_sysc_info; |
| 63 | extern const struct rcar_sysc_info r8a77980_sysc_info; | 68 | extern const struct rcar_sysc_info r8a77980_sysc_info; |
| @@ -116,6 +116,11 @@ static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = { · 115 unmodified lines | |||
| 116 | .id = 0x52, | 116 | .id = 0x52, |
| 117 | }; | 117 | }; |
| 118 | 118 | ||
| 119 | static const struct renesas_soc soc_rz_g2n __initconst __maybe_unused = { | ||
| 120 | .family = &fam_rzg2, | ||
| 121 | .id = 0x55, | ||
| 122 | }; | ||
| 123 | |||
| 119 | static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { | 124 | static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { |
| 120 | .family = &fam_rzg2, | 125 | .family = &fam_rzg2, |
| 121 | .id = 0x57, | 126 | .id = 0x57, |
| @@ -227,6 +232,9 @@ static const struct of_device_id renesas_socs[] __initconst = { · 105 unmodified lines | |||
| 227 | #ifdef CONFIG_ARCH_R8A774A1 | 232 | #ifdef CONFIG_ARCH_R8A774A1 |
| 228 | { .compatible = "renesas,r8a774a1", .data = &soc_rz_g2m }, | 233 | { .compatible = "renesas,r8a774a1", .data = &soc_rz_g2m }, |
| 229 | #endif | 234 | #endif |
| 235 | #ifdef CONFIG_ARCH_R8A774B1 | ||
| 236 | { .compatible = "renesas,r8a774b1", .data = &soc_rz_g2n }, | ||
| 237 | #endif | ||
| 230 | #ifdef CONFIG_ARCH_R8A774C0 | 238 | #ifdef CONFIG_ARCH_R8A774C0 |
| 231 | { .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e }, | 239 | { .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e }, |
| 232 | #endif | 240 | #endif |
| @@ -254,9 +262,12 @@ static const struct of_device_id renesas_socs[] __initconst = { · 21 unmodified lines | |||
| 254 | #ifdef CONFIG_ARCH_R8A7795 | 262 | #ifdef CONFIG_ARCH_R8A7795 |
| 255 | { .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 }, | 263 | { .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 }, |
| 256 | #endif | 264 | #endif |
| 257 | #ifdef CONFIG_ARCH_R8A7796 | 265 | #ifdef CONFIG_ARCH_R8A77960 |
| 258 | { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, | 266 | { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, |
| 259 | #endif | 267 | #endif |
| 268 | #ifdef CONFIG_ARCH_R8A77961 | ||
| 269 | { .compatible = "renesas,r8a77961", .data = &soc_rcar_m3_w }, | ||
| 270 | #endif | ||
| 260 | #ifdef CONFIG_ARCH_R8A77965 | 271 | #ifdef CONFIG_ARCH_R8A77965 |
| 261 | { .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n }, | 272 | { .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n }, |
| 262 | #endif | 273 | #endif |
| @@ -326,7 +337,7 @@ static int __init renesas_soc_init(void) · 63 unmodified lines | |||
| 326 | if (np) { | 337 | if (np) { |
| 327 | chipid = of_iomap(np, 0); | 338 | chipid = of_iomap(np, 0); |
| 328 | of_node_put(np); | 339 | of_node_put(np); |
| 329 | } else if (soc->id) { | 340 | } else if (soc->id && family->reg) { |
| 330 | chipid = ioremap(family->reg, 4); | 341 | chipid = ioremap(family->reg, 4); |
| 331 | } | 342 | } |
| 332 | if (chipid) { | 343 | if (chipid) { |
| @@ -7,6 +7,16 @@ menuconfig SOC_SAMSUNG · 6 unmodified lines | |||
| 7 | 7 | ||
| 8 | if SOC_SAMSUNG | 8 | if SOC_SAMSUNG |
| 9 | 9 | ||
| 10 | config EXYNOS_ASV | ||
| 11 | bool "Exynos Adaptive Supply Voltage support" if COMPILE_TEST | ||
| 12 | depends on (ARCH_EXYNOS && EXYNOS_CHIPID) || COMPILE_TEST | ||
| 13 | select EXYNOS_ASV_ARM if ARM && ARCH_EXYNOS | ||
| 14 | |||
| 15 | # There is no need to enable these drivers for ARMv8 | ||
| 16 | config EXYNOS_ASV_ARM | ||
| 17 | bool "Exynos ASV ARMv7-specific driver extensions" if COMPILE_TEST | ||
| 18 | depends on EXYNOS_ASV | ||
| 19 | |||
| 10 | config EXYNOS_CHIPID | 20 | config EXYNOS_CHIPID |
| 11 | bool "Exynos Chipid controller driver" if COMPILE_TEST | 21 | bool "Exynos Chipid controller driver" if COMPILE_TEST |
| 12 | depends on ARCH_EXYNOS || COMPILE_TEST | 22 | depends on ARCH_EXYNOS || COMPILE_TEST |
| @@ -1,5 +1,8 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | ||
| 3 | obj-$(CONFIG_EXYNOS_ASV) += exynos-asv.o | ||
| 4 | obj-$(CONFIG_EXYNOS_ASV_ARM) += exynos5422-asv.o | ||
| 5 | |||
| 3 | obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o | 6 | obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o |
| 4 | obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o | 7 | obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o |
| 5 | 8 | ||
| @@ -0,0 +1,177 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2019 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 6 | * | ||
| 7 | * Samsung Exynos SoC Adaptive Supply Voltage support | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/cpu.h> | ||
| 11 | #include <linux/device.h> | ||
| 12 | #include <linux/errno.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/mfd/syscon.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/of.h> | ||
| 17 | #include <linux/of_device.h> | ||
| 18 | #include <linux/platform_device.h> | ||
| 19 | #include <linux/pm_opp.h> | ||
| 20 | #include <linux/regmap.h> | ||
| 21 | #include <linux/soc/samsung/exynos-chipid.h> | ||
| 22 | |||
| 23 | #include "exynos-asv.h" | ||
| 24 | #include "exynos5422-asv.h" | ||
| 25 | |||
| 26 | #define MHZ 1000000U | ||
| 27 | |||
| 28 | static int exynos_asv_update_cpu_opps(struct exynos_asv *asv, | ||
| 29 | struct device *cpu) | ||
| 30 | { | ||
| 31 | struct exynos_asv_subsys *subsys = NULL; | ||
| 32 | struct dev_pm_opp *opp; | ||
| 33 | unsigned int opp_freq; | ||
| 34 | int i; | ||
| 35 | |||
| 36 | for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) { | ||
| 37 | if (of_device_is_compatible(cpu->of_node, | ||
| 38 | asv->subsys[i].cpu_dt_compat)) { | ||
| 39 | subsys = &asv->subsys[i]; | ||
| 40 | break; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | if (!subsys) | ||
| 44 | return -EINVAL; | ||
| 45 | |||
| 46 | for (i = 0; i < subsys->table.num_rows; i++) { | ||
| 47 | unsigned int new_volt, volt; | ||
| 48 | int ret; | ||
| 49 | |||
| 50 | opp_freq = exynos_asv_opp_get_frequency(subsys, i); | ||
| 51 | |||
| 52 | opp = dev_pm_opp_find_freq_exact(cpu, opp_freq * MHZ, true); | ||
| 53 | if (IS_ERR(opp)) { | ||
| 54 | dev_info(asv->dev, "cpu%d opp%d, freq: %u missing\n", | ||
| 55 | cpu->id, i, opp_freq); | ||
| 56 | |||
| 57 | continue; | ||
| 58 | } | ||
| 59 | |||
| 60 | volt = dev_pm_opp_get_voltage(opp); | ||
| 61 | new_volt = asv->opp_get_voltage(subsys, i, volt); | ||
| 62 | dev_pm_opp_put(opp); | ||
| 63 | |||
| 64 | if (new_volt == volt) | ||
| 65 | continue; | ||
| 66 | |||
| 67 | ret = dev_pm_opp_adjust_voltage(cpu, opp_freq * MHZ, | ||
| 68 | new_volt, new_volt, new_volt); | ||
| 69 | if (ret < 0) | ||
| 70 | dev_err(asv->dev, | ||
| 71 | "Failed to adjust OPP %u Hz/%u uV for cpu%d\n", | ||
| 72 | opp_freq, new_volt, cpu->id); | ||
| 73 | else | ||
| 74 | dev_dbg(asv->dev, | ||
| 75 | "Adjusted OPP %u Hz/%u -> %u uV, cpu%d\n", | ||
| 76 | opp_freq, volt, new_volt, cpu->id); | ||
| 77 | } | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int exynos_asv_update_opps(struct exynos_asv *asv) | ||
| 83 | { | ||
| 84 | struct opp_table *last_opp_table = NULL; | ||
| 85 | struct device *cpu; | ||
| 86 | int ret, cpuid; | ||
| 87 | |||
| 88 | for_each_possible_cpu(cpuid) { | ||
| 89 | struct opp_table *opp_table; | ||
| 90 | |||
| 91 | cpu = get_cpu_device(cpuid); | ||
| 92 | if (!cpu) | ||
| 93 | continue; | ||
| 94 | |||
| 95 | opp_table = dev_pm_opp_get_opp_table(cpu); | ||
| 96 | if (IS_ERR_OR_NULL(opp_table)) | ||
| 97 | continue; | ||
| 98 | |||
| 99 | if (!last_opp_table || opp_table != last_opp_table) { | ||
| 100 | last_opp_table = opp_table; | ||
| 101 | |||
| 102 | ret = exynos_asv_update_cpu_opps(asv, cpu); | ||
| 103 | if (ret < 0) | ||
| 104 | dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n", | ||
| 105 | cpuid); | ||
| 106 | } | ||
| 107 | |||
| 108 | dev_pm_opp_put_opp_table(opp_table); | ||
| 109 | } | ||
| 110 | |||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int exynos_asv_probe(struct platform_device *pdev) | ||
| 115 | { | ||
| 116 | int (*probe_func)(struct exynos_asv *asv); | ||
| 117 | struct exynos_asv *asv; | ||
| 118 | struct device *cpu_dev; | ||
| 119 | u32 product_id = 0; | ||
| 120 | int ret, i; | ||
| 121 | |||
| 122 | cpu_dev = get_cpu_device(0); | ||
| 123 | ret = dev_pm_opp_get_opp_count(cpu_dev); | ||
| 124 | if (ret < 0) | ||
| 125 | return -EPROBE_DEFER; | ||
| 126 | |||
| 127 | asv = devm_kzalloc(&pdev->dev, sizeof(*asv), GFP_KERNEL); | ||
| 128 | if (!asv) | ||
| 129 | return -ENOMEM; | ||
| 130 | |||
| 131 | asv->chipid_regmap = device_node_to_regmap(pdev->dev.of_node); | ||
| 132 | if (IS_ERR(asv->chipid_regmap)) { | ||
| 133 | dev_err(&pdev->dev, "Could not find syscon regmap\n"); | ||
| 134 | return PTR_ERR(asv->chipid_regmap); | ||
| 135 | } | ||
| 136 | |||
| 137 | regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id); | ||
| 138 | |||
| 139 | switch (product_id & EXYNOS_MASK) { | ||
| 140 | case 0xE5422000: | ||
| 141 | probe_func = exynos5422_asv_init; | ||
| 142 | break; | ||
| 143 | default: | ||
| 144 | return -ENODEV; | ||
| 145 | } | ||
| 146 | |||
| 147 | ret = of_property_read_u32(pdev->dev.of_node, "samsung,asv-bin", | ||
| 148 | &asv->of_bin); | ||
| 149 | if (ret < 0) | ||
| 150 | asv->of_bin = -EINVAL; | ||
| 151 | |||
| 152 | asv->dev = &pdev->dev; | ||
| 153 | dev_set_drvdata(&pdev->dev, asv); | ||
| 154 | |||
| 155 | for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) | ||
| 156 | asv->subsys[i].asv = asv; | ||
| 157 | |||
| 158 | ret = probe_func(asv); | ||
| 159 | if (ret < 0) | ||
| 160 | return ret; | ||
| 161 | |||
| 162 | return exynos_asv_update_opps(asv); | ||
| 163 | } | ||
| 164 | |||
| 165 | static const struct of_device_id exynos_asv_of_device_ids[] = { | ||
| 166 | { .compatible = "samsung,exynos4210-chipid" }, | ||
| 167 | {} | ||
| 168 | }; | ||
| 169 | |||
| 170 | static struct platform_driver exynos_asv_driver = { | ||
| 171 | .driver = { | ||
| 172 | .name = "exynos-asv", | ||
| 173 | .of_match_table = exynos_asv_of_device_ids, | ||
| 174 | }, | ||
| 175 | .probe = exynos_asv_probe, | ||
| 176 | }; | ||
| 177 | module_platform_driver(exynos_asv_driver); | ||
| @@ -0,0 +1,71 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2019 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 6 | * | ||
| 7 | * Samsung Exynos SoC Adaptive Supply Voltage support | ||
| 8 | */ | ||
| 9 | #ifndef __LINUX_SOC_EXYNOS_ASV_H | ||
| 10 | #define __LINUX_SOC_EXYNOS_ASV_H | ||
| 11 | |||
| 12 | struct regmap; | ||
| 13 | |||
| 14 | /* HPM, IDS values to select target group */ | ||
| 15 | struct asv_limit_entry { | ||
| 16 | unsigned int hpm; | ||
| 17 | unsigned int ids; | ||
| 18 | }; | ||
| 19 | |||
| 20 | struct exynos_asv_table { | ||
| 21 | unsigned int num_rows; | ||
| 22 | unsigned int num_cols; | ||
| 23 | u32 *buf; | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct exynos_asv_subsys { | ||
| 27 | struct exynos_asv *asv; | ||
| 28 | const char *cpu_dt_compat; | ||
| 29 | int id; | ||
| 30 | struct exynos_asv_table table; | ||
| 31 | |||
| 32 | unsigned int base_volt; | ||
| 33 | unsigned int offset_volt_h; | ||
| 34 | unsigned int offset_volt_l; | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct exynos_asv { | ||
| 38 | struct device *dev; | ||
| 39 | struct regmap *chipid_regmap; | ||
| 40 | struct exynos_asv_subsys subsys[2]; | ||
| 41 | |||
| 42 | int (*opp_get_voltage)(const struct exynos_asv_subsys *subs, | ||
| 43 | int level, unsigned int voltage); | ||
| 44 | unsigned int group; | ||
| 45 | unsigned int table; | ||
| 46 | |||
| 47 | /* True if SG fields from PKG_ID register should be used */ | ||
| 48 | bool use_sg; | ||
| 49 | /* ASV bin read from DT */ | ||
| 50 | int of_bin; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static inline u32 __asv_get_table_entry(const struct exynos_asv_table *table, | ||
| 54 | unsigned int row, unsigned int col) | ||
| 55 | { | ||
| 56 | return table->buf[row * (table->num_cols) + col]; | ||
| 57 | } | ||
| 58 | |||
| 59 | static inline u32 exynos_asv_opp_get_voltage(const struct exynos_asv_subsys *subsys, | ||
| 60 | unsigned int level, unsigned int group) | ||
| 61 | { | ||
| 62 | return __asv_get_table_entry(&subsys->table, level, group + 1); | ||
| 63 | } | ||
| 64 | |||
| 65 | static inline u32 exynos_asv_opp_get_frequency(const struct exynos_asv_subsys *subsys, | ||
| 66 | unsigned int level) | ||
| 67 | { | ||
| 68 | return __asv_get_table_entry(&subsys->table, level, 0); | ||
| 69 | } | ||
| 70 | |||
| 71 | #endif /* __LINUX_SOC_EXYNOS_ASV_H */ | ||
| @@ -45,17 +45,25 @@ static const char * __init product_id_to_soc_id(unsigned int product_id) · 44 unmodified lines | |||
| 45 | return NULL; | 45 | return NULL; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | int __init exynos_chipid_early_init(void) | 48 | static int __init exynos_chipid_early_init(void) |
| 49 | { | 49 | { |
| 50 | struct soc_device_attribute *soc_dev_attr; | 50 | struct soc_device_attribute *soc_dev_attr; |
| 51 | struct soc_device *soc_dev; | 51 | struct soc_device *soc_dev; |
| 52 | struct device_node *root; | 52 | struct device_node *root; |
| 53 | struct device_node *syscon; | ||
| 53 | struct regmap *regmap; | 54 | struct regmap *regmap; |
| 54 | u32 product_id; | 55 | u32 product_id; |
| 55 | u32 revision; | 56 | u32 revision; |
| 56 | int ret; | 57 | int ret; |
| 57 | 58 | ||
| 58 | regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid"); | 59 | syscon = of_find_compatible_node(NULL, NULL, |
| 60 | "samsung,exynos4210-chipid"); | ||
| 61 | if (!syscon) | ||
| 62 | return ENODEV; | ||
| 63 | |||
| 64 | regmap = device_node_to_regmap(syscon); | ||
| 65 | of_node_put(syscon); | ||
| 66 | |||
| 59 | if (IS_ERR(regmap)) | 67 | if (IS_ERR(regmap)) |
| 60 | return PTR_ERR(regmap); | 68 | return PTR_ERR(regmap); |
| 61 | 69 | ||
| @@ -0,0 +1,505 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2019 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * Samsung Exynos 5422 SoC Adaptive Supply Voltage support | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/bitrev.h> | ||
| 10 | #include <linux/errno.h> | ||
| 11 | #include <linux/regmap.h> | ||
| 12 | #include <linux/soc/samsung/exynos-chipid.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | |||
| 15 | #include "exynos-asv.h" | ||
| 16 | #include "exynos5422-asv.h" | ||
| 17 | |||
| 18 | #define ASV_GROUPS_NUM 14 | ||
| 19 | #define ASV_ARM_DVFS_NUM 20 | ||
| 20 | #define ASV_ARM_BIN2_DVFS_NUM 17 | ||
| 21 | #define ASV_KFC_DVFS_NUM 14 | ||
| 22 | #define ASV_KFC_BIN2_DVFS_NUM 12 | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This array is a set of 4 ASV data tables, first column of each ASV table | ||
| 26 | * contains frequency value in MHz and subsequent columns contain the CPU | ||
| 27 | * cluster's supply voltage values in uV. | ||
| 28 | * In order to create a set of OPPs for specific SoC revision one of the voltage | ||
| 29 | * columns (1...14) from one of the tables (0...3) is selected during | ||
| 30 | * initialization. There are separate ASV tables for the big (ARM) and little | ||
| 31 | * (KFC) CPU cluster. Only OPPs which are already defined in devicetree | ||
| 32 | * will be updated. | ||
| 33 | */ | ||
| 34 | |||
| 35 | static const u32 asv_arm_table[][ASV_ARM_DVFS_NUM][ASV_GROUPS_NUM + 1] = { | ||
| 36 | { | ||
| 37 | /* ARM 0, 1 */ | ||
| 38 | { 2100, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, | ||
| 39 | 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 }, | ||
| 40 | { 2000, 1312500, 1312500, 1300000, 1287500, 1275000, 1262500, 1250000, | ||
| 41 | 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 }, | ||
| 42 | { 1900, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, | ||
| 43 | 1162500, 1150000, 1162500, 1150000, 1137500, 1125000, 1112500 }, | ||
| 44 | { 1800, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, | ||
| 45 | 1112500, 1100000, 1112500, 1100000, 1087500, 1075000, 1062500 }, | ||
| 46 | { 1700, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, | ||
| 47 | 1075000, 1062500, 1075000, 1062500, 1050000, 1037500, 1025000 }, | ||
| 48 | { 1600, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, | ||
| 49 | 1037500, 1025000, 1037500, 1025000, 1012500, 1000000, 987500 }, | ||
| 50 | { 1500, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, | ||
| 51 | 1000000, 987500, 1000000, 987500, 975000, 962500, 950000 }, | ||
| 52 | { 1400, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, | ||
| 53 | 975000, 962500, 975000, 962500, 950000, 937500, 925000 }, | ||
| 54 | { 1300, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, | ||
| 55 | 962500, 950000, 962500, 950000, 937500, 925000, 912500 }, | ||
| 56 | { 1200, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, | ||
| 57 | 937500, 925000, 937500, 925000, 912500, 900000, 900000 }, | ||
| 58 | { 1100, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, | ||
| 59 | 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 60 | { 1000, 975000, 962500, 950000, 937500, 925000, 912500, 900000, | ||
| 61 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 62 | { 900, 950000, 937500, 925000, 912500, 900000, 900000, 900000, | ||
| 63 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 64 | { 800, 925000, 912500, 900000, 900000, 900000, 900000, 900000, | ||
| 65 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 66 | { 700, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 67 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 68 | { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 69 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 70 | { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 71 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 72 | { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 73 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 74 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 75 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 76 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 77 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 78 | }, { | ||
| 79 | /* ARM 2 */ | ||
| 80 | { 2100, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, | ||
| 81 | 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 }, | ||
| 82 | { 2000, 1312500, 1312500, 1312500, 1300000, 1275000, 1262500, 1250000, | ||
| 83 | 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 }, | ||
| 84 | { 1900, 1262500, 1250000, 1250000, 1237500, 1212500, 1200000, 1187500, | ||
| 85 | 1175000, 1162500, 1175000, 1162500, 1150000, 1137500, 1125000 }, | ||
| 86 | { 1800, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, | ||
| 87 | 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000 }, | ||
| 88 | { 1700, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, | ||
| 89 | 1087500, 1075000, 1087500, 1075000, 1062500, 1050000, 1037500 }, | ||
| 90 | { 1600, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, | ||
| 91 | 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000 }, | ||
| 92 | { 1500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, | ||
| 93 | 1012500, 1000000, 1012500, 1000000, 987500, 975000, 962500 }, | ||
| 94 | { 1400, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, | ||
| 95 | 987500, 975000, 987500, 975000, 962500, 950000, 937500 }, | ||
| 96 | { 1300, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, | ||
| 97 | 962500, 950000, 962500, 950000, 937500, 925000, 912500 }, | ||
| 98 | { 1200, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, | ||
| 99 | 937500, 925000, 937500, 925000, 912500, 900000, 900000 }, | ||
| 100 | { 1100, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, | ||
| 101 | 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 102 | { 1000, 975000, 962500, 950000, 937500, 925000, 912500, 900000, | ||
| 103 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 104 | { 900, 950000, 937500, 925000, 912500, 900000, 900000, 900000, | ||
| 105 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 106 | { 800, 925000, 912500, 900000, 900000, 900000, 900000, 900000, | ||
| 107 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 108 | { 700, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 109 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 110 | { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 111 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 112 | { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 113 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 114 | { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 115 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 116 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 117 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 118 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 119 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 120 | }, { | ||
| 121 | /* ARM 3 */ | ||
| 122 | { 2100, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, | ||
| 123 | 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 }, | ||
| 124 | { 2000, 1312500, 1312500, 1300000, 1287500, 1275000, 1262500, 1250000, | ||
| 125 | 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 }, | ||
| 126 | { 1900, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, | ||
| 127 | 1175000, 1162500, 1175000, 1162500, 1150000, 1137500, 1125000 }, | ||
| 128 | { 1800, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, | ||
| 129 | 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000 }, | ||
| 130 | { 1700, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, | ||
| 131 | 1087500, 1075000, 1087500, 1075000, 1062500, 1050000, 1037500 }, | ||
| 132 | { 1600, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, | ||
| 133 | 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000 }, | ||
| 134 | { 1500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, | ||
| 135 | 1012500, 1000000, 1012500, 1000000, 987500, 975000, 962500 }, | ||
| 136 | { 1400, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, | ||
| 137 | 987500, 975000, 987500, 975000, 962500, 950000, 937500 }, | ||
| 138 | { 1300, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, | ||
| 139 | 962500, 950000, 962500, 950000, 937500, 925000, 912500 }, | ||
| 140 | { 1200, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, | ||
| 141 | 937500, 925000, 937500, 925000, 912500, 900000, 900000 }, | ||
| 142 | { 1100, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, | ||
| 143 | 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 144 | { 1000, 975000, 962500, 950000, 937500, 925000, 912500, 900000, | ||
| 145 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 146 | { 900, 950000, 937500, 925000, 912500, 900000, 900000, 900000, | ||
| 147 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 148 | { 800, 925000, 912500, 900000, 900000, 900000, 900000, 900000, | ||
| 149 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 150 | { 700, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 151 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 152 | { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 153 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 154 | { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 155 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 156 | { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 157 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 158 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 159 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 160 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 161 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 162 | }, { | ||
| 163 | /* ARM bin 2 */ | ||
| 164 | { 1800, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, | ||
| 165 | 1150000, 1137500, 1150000, 1137500, 1125000, 1112500, 1100000 }, | ||
| 166 | { 1700, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, | ||
| 167 | 1112500, 1100000, 1112500, 1100000, 1087500, 1075000, 1062500 }, | ||
| 168 | { 1600, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, | ||
| 169 | 1075000, 1062500, 1075000, 1062500, 1050000, 1037500, 1025000 }, | ||
| 170 | { 1500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, | ||
| 171 | 1037500, 1025000, 1037500, 1025000, 1012500, 1000000, 987500 }, | ||
| 172 | { 1400, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, | ||
| 173 | 1012500, 1000000, 1012500, 1000000, 987500, 975000, 962500 }, | ||
| 174 | { 1300, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, | ||
| 175 | 1000000, 987500, 1000000, 987500, 975000, 962500, 950000 }, | ||
| 176 | { 1200, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, | ||
| 177 | 975000, 962500, 975000, 962500, 950000, 937500, 925000 }, | ||
| 178 | { 1100, 1037500, 1025000, 1012500, 1000000, 987500, 975000, 962500, | ||
| 179 | 950000, 937500, 950000, 937500, 925000, 912500, 900000 }, | ||
| 180 | { 1000, 1012500, 1000000, 987500, 975000, 962500, 950000, 937500, | ||
| 181 | 925000, 912500, 925000, 912500, 900000, 900000, 900000 }, | ||
| 182 | { 900, 987500, 975000, 962500, 950000, 937500, 925000, 912500, | ||
| 183 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 184 | { 800, 962500, 950000, 937500, 925000, 912500, 900000, 900000, | ||
| 185 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 186 | { 700, 937500, 925000, 912500, 900000, 900000, 900000, 900000, | ||
| 187 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 188 | { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 189 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 190 | { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 191 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 192 | { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 193 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 194 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 195 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 196 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 197 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 198 | } | ||
| 199 | }; | ||
| 200 | |||
| 201 | static const u32 asv_kfc_table[][ASV_KFC_DVFS_NUM][ASV_GROUPS_NUM + 1] = { | ||
| 202 | { | ||
| 203 | /* KFC 0, 1 */ | ||
| 204 | { 1500000, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, | ||
| 205 | 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 }, | ||
| 206 | { 1400000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, | ||
| 207 | 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 }, | ||
| 208 | { 1300000, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, | ||
| 209 | 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, | ||
| 210 | { 1200000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, | ||
| 211 | 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 }, | ||
| 212 | { 1100000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, | ||
| 213 | 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000 }, | ||
| 214 | { 1000000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, | ||
| 215 | 1012500, 1000000, 987500, 975000, 962500, 950000, 937500 }, | ||
| 216 | { 900000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, | ||
| 217 | 975000, 962500, 950000, 937500, 925000, 912500, 900000 }, | ||
| 218 | { 800000, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, | ||
| 219 | 937500, 925000, 912500, 900000, 900000, 900000, 900000 }, | ||
| 220 | { 700000, 987500, 975000, 962500, 950000, 937500, 925000, 912500, | ||
| 221 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 222 | { 600000, 950000, 937500, 925000, 912500, 900000, 900000, 900000, | ||
| 223 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 224 | { 500000, 912500, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 225 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 226 | { 400000, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 227 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 228 | { 300000, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 229 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 230 | { 200000, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 231 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 232 | }, { | ||
| 233 | /* KFC 2 */ | ||
| 234 | { 1500, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, | ||
| 235 | 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 }, | ||
| 236 | { 1400, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, | ||
| 237 | 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 }, | ||
| 238 | { 1300, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, | ||
| 239 | 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, | ||
| 240 | { 1200, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, | ||
| 241 | 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 }, | ||
| 242 | { 1100, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, | ||
| 243 | 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000 }, | ||
| 244 | { 1000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, | ||
| 245 | 1012500, 1000000, 987500, 975000, 962500, 950000, 937500 }, | ||
| 246 | { 900, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, | ||
| 247 | 975000, 962500, 950000, 937500, 925000, 912500, 900000 }, | ||
| 248 | { 800, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, | ||
| 249 | 937500, 925000, 912500, 900000, 900000, 900000, 900000 }, | ||
| 250 | { 700, 987500, 975000, 962500, 950000, 937500, 925000, 912500, | ||
| 251 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 252 | { 600, 950000, 937500, 925000, 912500, 900000, 900000, 900000, | ||
| 253 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 254 | { 500, 912500, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 255 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 256 | { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 257 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 258 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 259 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 260 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 261 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 262 | }, { | ||
| 263 | /* KFC 3 */ | ||
| 264 | { 1500, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, | ||
| 265 | 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 }, | ||
| 266 | { 1400, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, | ||
| 267 | 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 }, | ||
| 268 | { 1300, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, | ||
| 269 | 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, | ||
| 270 | { 1200, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, | ||
| 271 | 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 }, | ||
| 272 | { 1100, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, | ||
| 273 | 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000 }, | ||
| 274 | { 1000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, | ||
| 275 | 1012500, 1000000, 987500, 975000, 962500, 950000, 937500 }, | ||
| 276 | { 900, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, | ||
| 277 | 975000, 962500, 950000, 937500, 925000, 912500, 900000 }, | ||
| 278 | { 800, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, | ||
| 279 | 937500, 925000, 912500, 900000, 900000, 900000, 900000 }, | ||
| 280 | { 700, 987500, 975000, 962500, 950000, 937500, 925000, 912500, | ||
| 281 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 282 | { 600, 950000, 937500, 925000, 912500, 900000, 900000, 900000, | ||
| 283 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 284 | { 500, 912500, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 285 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 286 | { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 287 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 288 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 289 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 290 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 291 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 292 | }, { | ||
| 293 | /* KFC bin 2 */ | ||
| 294 | { 1300, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, | ||
| 295 | 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500 }, | ||
| 296 | { 1200, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, | ||
| 297 | 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500 }, | ||
| 298 | { 1100, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, | ||
| 299 | 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000 }, | ||
| 300 | { 1000, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, | ||
| 301 | 1037500, 1025000, 1012500, 1000000, 987500, 975000, 962500 }, | ||
| 302 | { 900, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, | ||
| 303 | 1000000, 987500, 975000, 962500, 950000, 937500, 925000 }, | ||
| 304 | { 800, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, | ||
| 305 | 962500, 950000, 937500, 925000, 912500, 900000, 900000 }, | ||
| 306 | { 700, 1012500, 1000000, 987500, 975000, 962500, 950000, 937500, | ||
| 307 | 925000, 912500, 900000, 900000, 900000, 900000, 900000 }, | ||
| 308 | { 600, 975000, 962500, 950000, 937500, 925000, 912500, 900000, | ||
| 309 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 310 | { 500, 937500, 925000, 912500, 900000, 900000, 900000, 900000, | ||
| 311 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 312 | { 400, 925000, 912500, 900000, 900000, 900000, 900000, 900000, | ||
| 313 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 314 | { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 315 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 316 | { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, | ||
| 317 | 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, | ||
| 318 | } | ||
| 319 | }; | ||
| 320 | |||
| 321 | static const struct asv_limit_entry __asv_limits[ASV_GROUPS_NUM] = { | ||
| 322 | { 13, 55 }, | ||
| 323 | { 21, 65 }, | ||
| 324 | { 25, 69 }, | ||
| 325 | { 30, 72 }, | ||
| 326 | { 36, 74 }, | ||
| 327 | { 43, 76 }, | ||
| 328 | { 51, 78 }, | ||
| 329 | { 65, 80 }, | ||
| 330 | { 81, 82 }, | ||
| 331 | { 98, 84 }, | ||
| 332 | { 119, 87 }, | ||
| 333 | { 135, 89 }, | ||
| 334 | { 150, 92 }, | ||
| 335 | { 999, 999 }, | ||
| 336 | }; | ||
| 337 | |||
| 338 | static int exynos5422_asv_get_group(struct exynos_asv *asv) | ||
| 339 | { | ||
| 340 | unsigned int pkgid_reg, auxi_reg; | ||
| 341 | int hpm, ids, i; | ||
| 342 | |||
| 343 | regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PKG_ID, &pkgid_reg); | ||
| 344 | regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_AUX_INFO, &auxi_reg); | ||
| 345 | |||
| 346 | if (asv->use_sg) { | ||
| 347 | u32 sga = (pkgid_reg >> EXYNOS5422_SG_A_OFFSET) & | ||
| 348 | EXYNOS5422_SG_A_MASK; | ||
| 349 | |||
| 350 | u32 sgb = (pkgid_reg >> EXYNOS5422_SG_B_OFFSET) & | ||
| 351 | EXYNOS5422_SG_B_MASK; | ||
| 352 | |||
| 353 | if ((pkgid_reg >> EXYNOS5422_SG_BSIGN_OFFSET) & | ||
| 354 | EXYNOS5422_SG_BSIGN_MASK) | ||
| 355 | return sga + sgb; | ||
| 356 | else | ||
| 357 | return sga - sgb; | ||
| 358 | } | ||
| 359 | |||
| 360 | hpm = (auxi_reg >> EXYNOS5422_TMCB_OFFSET) & EXYNOS5422_TMCB_MASK; | ||
| 361 | ids = (pkgid_reg >> EXYNOS5422_IDS_OFFSET) & EXYNOS5422_IDS_MASK; | ||
| 362 | |||
| 363 | for (i = 0; i < ASV_GROUPS_NUM; i++) { | ||
| 364 | if (ids <= __asv_limits[i].ids) | ||
| 365 | break; | ||
| 366 | if (hpm <= __asv_limits[i].hpm) | ||
| 367 | break; | ||
| 368 | } | ||
| 369 | if (i < ASV_GROUPS_NUM) | ||
| 370 | return i; | ||
| 371 | |||
| 372 | return 0; | ||
| 373 | } | ||
| 374 | |||
| 375 | static int __asv_offset_voltage(unsigned int index) | ||
| 376 | { | ||
| 377 | switch (index) { | ||
| 378 | case 1: | ||
| 379 | return 12500; | ||
| 380 | case 2: | ||
| 381 | return 50000; | ||
| 382 | case 3: | ||
| 383 | return 25000; | ||
| 384 | default: | ||
| 385 | return 0; | ||
| 386 | }; | ||
| 387 | } | ||
| 388 | |||
| 389 | static void exynos5422_asv_offset_voltage_setup(struct exynos_asv *asv) | ||
| 390 | { | ||
| 391 | struct exynos_asv_subsys *subsys; | ||
| 392 | unsigned int reg, value; | ||
| 393 | |||
| 394 | regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_AUX_INFO, ®); | ||
| 395 | |||
| 396 | /* ARM offset voltage setup */ | ||
| 397 | subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_ARM]; | ||
| 398 | |||
| 399 | subsys->base_volt = 1000000; | ||
| 400 | |||
| 401 | value = (reg >> EXYNOS5422_ARM_UP_OFFSET) & EXYNOS5422_ARM_UP_MASK; | ||
| 402 | subsys->offset_volt_h = __asv_offset_voltage(value); | ||
| 403 | |||
| 404 | value = (reg >> EXYNOS5422_ARM_DN_OFFSET) & EXYNOS5422_ARM_DN_MASK; | ||
| 405 | subsys->offset_volt_l = __asv_offset_voltage(value); | ||
| 406 | |||
| 407 | /* KFC offset voltage setup */ | ||
| 408 | subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_KFC]; | ||
| 409 | |||
| 410 | subsys->base_volt = 1000000; | ||
| 411 | |||
| 412 | value = (reg >> EXYNOS5422_KFC_UP_OFFSET) & EXYNOS5422_KFC_UP_MASK; | ||
| 413 | subsys->offset_volt_h = __asv_offset_voltage(value); | ||
| 414 | |||
| 415 | value = (reg >> EXYNOS5422_KFC_DN_OFFSET) & EXYNOS5422_KFC_DN_MASK; | ||
| 416 | subsys->offset_volt_l = __asv_offset_voltage(value); | ||
| 417 | } | ||
| 418 | |||
| 419 | static int exynos5422_asv_opp_get_voltage(const struct exynos_asv_subsys *subsys, | ||
| 420 | int level, unsigned int volt) | ||
| 421 | { | ||
| 422 | unsigned int asv_volt; | ||
| 423 | |||
| 424 | if (level >= subsys->table.num_rows) | ||
| 425 | return volt; | ||
| 426 | |||
| 427 | asv_volt = exynos_asv_opp_get_voltage(subsys, level, | ||
| 428 | subsys->asv->group); | ||
| 429 | |||
| 430 | if (volt > subsys->base_volt) | ||
| 431 | asv_volt += subsys->offset_volt_h; | ||
| 432 | else | ||
| 433 | asv_volt += subsys->offset_volt_l; | ||
| 434 | |||
| 435 | return asv_volt; | ||
| 436 | } | ||
| 437 | |||
| 438 | static unsigned int exynos5422_asv_parse_table(unsigned int pkg_id) | ||
| 439 | { | ||
| 440 | return (pkg_id >> EXYNOS5422_TABLE_OFFSET) & EXYNOS5422_TABLE_MASK; | ||
| 441 | } | ||
| 442 | |||
| 443 | static bool exynos5422_asv_parse_bin2(unsigned int pkg_id) | ||
| 444 | { | ||
| 445 | return (pkg_id >> EXYNOS5422_BIN2_OFFSET) & EXYNOS5422_BIN2_MASK; | ||
| 446 | } | ||
| 447 | |||
| 448 | static bool exynos5422_asv_parse_sg(unsigned int pkg_id) | ||
| 449 | { | ||
| 450 | return (pkg_id >> EXYNOS5422_USESG_OFFSET) & EXYNOS5422_USESG_MASK; | ||
| 451 | } | ||
| 452 | |||
| 453 | int exynos5422_asv_init(struct exynos_asv *asv) | ||
| 454 | { | ||
| 455 | struct exynos_asv_subsys *subsys; | ||
| 456 | unsigned int table_index; | ||
| 457 | unsigned int pkg_id; | ||
| 458 | bool bin2; | ||
| 459 | |||
| 460 | regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PKG_ID, &pkg_id); | ||
| 461 | |||
| 462 | if (asv->of_bin == 2) { | ||
| 463 | bin2 = true; | ||
| 464 | asv->use_sg = false; | ||
| 465 | } else { | ||
| 466 | asv->use_sg = exynos5422_asv_parse_sg(pkg_id); | ||
| 467 | bin2 = exynos5422_asv_parse_bin2(pkg_id); | ||
| 468 | } | ||
| 469 | |||
| 470 | asv->group = exynos5422_asv_get_group(asv); | ||
| 471 | asv->table = exynos5422_asv_parse_table(pkg_id); | ||
| 472 | |||
| 473 | exynos5422_asv_offset_voltage_setup(asv); | ||
| 474 | |||
| 475 | if (bin2) { | ||
| 476 | table_index = 3; | ||
| 477 | } else { | ||
| 478 | if (asv->table == 2 || asv->table == 3) | ||
| 479 | table_index = asv->table - 1; | ||
| 480 | else | ||
| 481 | table_index = 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_ARM]; | ||
| 485 | subsys->cpu_dt_compat = "arm,cortex-a15"; | ||
| 486 | if (bin2) | ||
| 487 | subsys->table.num_rows = ASV_ARM_BIN2_DVFS_NUM; | ||
| 488 | else | ||
| 489 | subsys->table.num_rows = ASV_ARM_DVFS_NUM; | ||
| 490 | subsys->table.num_cols = ASV_GROUPS_NUM + 1; | ||
| 491 | subsys->table.buf = (u32 *)asv_arm_table[table_index]; | ||
| 492 | |||
| 493 | subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_KFC]; | ||
| 494 | subsys->cpu_dt_compat = "arm,cortex-a7"; | ||
| 495 | if (bin2) | ||
| 496 | subsys->table.num_rows = ASV_KFC_BIN2_DVFS_NUM; | ||
| 497 | else | ||
| 498 | subsys->table.num_rows = ASV_KFC_DVFS_NUM; | ||
| 499 | subsys->table.num_cols = ASV_GROUPS_NUM + 1; | ||
| 500 | subsys->table.buf = (u32 *)asv_kfc_table[table_index]; | ||
| 501 | |||
| 502 | asv->opp_get_voltage = exynos5422_asv_opp_get_voltage; | ||
| 503 | |||
| 504 | return 0; | ||
| 505 | } | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2019 Samsung Electronics Co., Ltd. | ||
| 4 | * http://www.samsung.com/ | ||
| 5 | * | ||
| 6 | * Samsung Exynos 5422 SoC Adaptive Supply Voltage support | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef __LINUX_SOC_EXYNOS5422_ASV_H | ||
| 10 | #define __LINUX_SOC_EXYNOS5422_ASV_H | ||
| 11 | |||
| 12 | #include <linux/errno.h> | ||
| 13 | |||
| 14 | enum { | ||
| 15 | EXYNOS_ASV_SUBSYS_ID_ARM, | ||
| 16 | EXYNOS_ASV_SUBSYS_ID_KFC, | ||
| 17 | EXYNOS_ASV_SUBSYS_ID_MAX | ||
| 18 | }; | ||
| 19 | |||
| 20 | struct exynos_asv; | ||
| 21 | |||
| 22 | #ifdef CONFIG_EXYNOS_ASV_ARM | ||
| 23 | int exynos5422_asv_init(struct exynos_asv *asv); | ||
| 24 | #else | ||
| 25 | static inline int exynos5422_asv_init(struct exynos_asv *asv) | ||
| 26 | { | ||
| 27 | return -ENOTSUPP; | ||
| 28 | } | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #endif /* __LINUX_SOC_EXYNOS5422_ASV_H */ | ||
| @@ -15,6 +15,7 @@ config ARCH_TEGRA_2x_SOC · 14 unmodified lines | |||
| 15 | select PL310_ERRATA_769419 if CACHE_L2X0 | 15 | select PL310_ERRATA_769419 if CACHE_L2X0 |
| 16 | select SOC_TEGRA_FLOWCTRL | 16 | select SOC_TEGRA_FLOWCTRL |
| 17 | select SOC_TEGRA_PMC | 17 | select SOC_TEGRA_PMC |
| 18 | select SOC_TEGRA20_VOLTAGE_COUPLER | ||
| 18 | select TEGRA_TIMER | 19 | select TEGRA_TIMER |
| 19 | help | 20 | help |
| 20 | Support for NVIDIA Tegra AP20 and T20 processors, based on the | 21 | Support for NVIDIA Tegra AP20 and T20 processors, based on the |
| @@ -28,6 +29,7 @@ config ARCH_TEGRA_3x_SOC · 7 unmodified lines | |||
| 28 | select PL310_ERRATA_769419 if CACHE_L2X0 | 29 | select PL310_ERRATA_769419 if CACHE_L2X0 |
| 29 | select SOC_TEGRA_FLOWCTRL | 30 | select SOC_TEGRA_FLOWCTRL |
| 30 | select SOC_TEGRA_PMC | 31 | select SOC_TEGRA_PMC |
| 32 | select SOC_TEGRA30_VOLTAGE_COUPLER | ||
| 31 | select TEGRA_TIMER | 33 | select TEGRA_TIMER |
| 32 | help | 34 | help |
| 33 | Support for NVIDIA Tegra T30 processor family, based on the | 35 | Support for NVIDIA Tegra T30 processor family, based on the |
| @@ -135,3 +137,11 @@ config SOC_TEGRA_POWERGATE_BPMP · 101 unmodified lines | |||
| 135 | def_bool y | 137 | def_bool y |
| 136 | depends on PM_GENERIC_DOMAINS | 138 | depends on PM_GENERIC_DOMAINS |
| 137 | depends on TEGRA_BPMP | 139 | depends on TEGRA_BPMP |
| 140 | |||
| 141 | config SOC_TEGRA20_VOLTAGE_COUPLER | ||
| 142 | bool "Voltage scaling support for Tegra20 SoCs" | ||
| 143 | depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST | ||
| 144 | |||
| 145 | config SOC_TEGRA30_VOLTAGE_COUPLER | ||
| 146 | bool "Voltage scaling support for Tegra30 SoCs" | ||
| 147 | depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST | ||
| @@ -5,3 +5,5 @@ obj-y += common.o · 4 unmodified lines | |||
| 5 | obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o | 5 | obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o |
| 6 | obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o | 6 | obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o |
| 7 | obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o | 7 | obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o |
| 8 | obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o | ||
| 9 | obj-$(CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER) += regulators-tegra30.o | ||
| @@ -8,6 +8,8 @@ · 7 unmodified lines | |||
| 8 | #include <linux/kobject.h> | 8 | #include <linux/kobject.h> |
| 9 | #include <linux/init.h> | 9 | #include <linux/init.h> |
| 10 | #include <linux/io.h> | 10 | #include <linux/io.h> |
| 11 | #include <linux/nvmem-consumer.h> | ||
| 12 | #include <linux/nvmem-provider.h> | ||
| 11 | #include <linux/of.h> | 13 | #include <linux/of.h> |
| 12 | #include <linux/of_address.h> | 14 | #include <linux/of_address.h> |
| 13 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
| @@ -31,50 +33,6 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { · 17 unmodified lines | |||
| 31 | [TEGRA_REVISION_A04] = "A04", | 33 | [TEGRA_REVISION_A04] = "A04", |
| 32 | }; | 34 | }; |
| 33 | 35 | ||
| 34 | static u8 fuse_readb(struct tegra_fuse *fuse, unsigned int offset) | ||
| 35 | { | ||
| 36 | u32 val; | ||
| 37 | |||
| 38 | val = fuse->read(fuse, round_down(offset, 4)); | ||
| 39 | val >>= (offset % 4) * 8; | ||
| 40 | val &= 0xff; | ||
| 41 | |||
| 42 | return val; | ||
| 43 | } | ||
| 44 | |||
| 45 | static ssize_t fuse_read(struct file *fd, struct kobject *kobj, | ||
| 46 | struct bin_attribute *attr, char *buf, | ||
| 47 | loff_t pos, size_t size) | ||
| 48 | { | ||
| 49 | struct device *dev = kobj_to_dev(kobj); | ||
| 50 | struct tegra_fuse *fuse = dev_get_drvdata(dev); | ||
| 51 | int i; | ||
| 52 | |||
| 53 | if (pos < 0 || pos >= attr->size) | ||
| 54 | return 0; | ||
| 55 | |||
| 56 | if (size > attr->size - pos) | ||
| 57 | size = attr->size - pos; | ||
| 58 | |||
| 59 | for (i = 0; i < size; i++) | ||
| 60 | buf[i] = fuse_readb(fuse, pos + i); | ||
| 61 | |||
| 62 | return i; | ||
| 63 | } | ||
| 64 | |||
| 65 | static struct bin_attribute fuse_bin_attr = { | ||
| 66 | .attr = { .name = "fuse", .mode = S_IRUGO, }, | ||
| 67 | .read = fuse_read, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static int tegra_fuse_create_sysfs(struct device *dev, unsigned int size, | ||
| 71 | const struct tegra_fuse_info *info) | ||
| 72 | { | ||
| 73 | fuse_bin_attr.size = size; | ||
| 74 | |||
| 75 | return device_create_bin_file(dev, &fuse_bin_attr); | ||
| 76 | } | ||
| 77 | |||
| 78 | static const struct of_device_id car_match[] __initconst = { | 36 | static const struct of_device_id car_match[] __initconst = { |
| 79 | { .compatible = "nvidia,tegra20-car", }, | 37 | { .compatible = "nvidia,tegra20-car", }, |
| 80 | { .compatible = "nvidia,tegra30-car", }, | 38 | { .compatible = "nvidia,tegra30-car", }, |
| @@ -115,9 +73,111 @@ static const struct of_device_id tegra_fuse_match[] = { · 34 unmodified lines | |||
| 115 | { /* sentinel */ } | 73 | { /* sentinel */ } |
| 116 | }; | 74 | }; |
| 117 | 75 | ||
| 76 | static int tegra_fuse_read(void *priv, unsigned int offset, void *value, | ||
| 77 | size_t bytes) | ||
| 78 | { | ||
| 79 | unsigned int count = bytes / 4, i; | ||
| 80 | struct tegra_fuse *fuse = priv; | ||
| 81 | u32 *buffer = value; | ||
| 82 | |||
| 83 | for (i = 0; i < count; i++) | ||
| 84 | buffer[i] = fuse->read(fuse, offset + i * 4); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static const struct nvmem_cell_info tegra_fuse_cells[] = { | ||
| 90 | { | ||
| 91 | .name = "tsensor-cpu1", | ||
| 92 | .offset = 0x084, | ||
| 93 | .bytes = 4, | ||
| 94 | .bit_offset = 0, | ||
| 95 | .nbits = 32, | ||
| 96 | }, { | ||
| 97 | .name = "tsensor-cpu2", | ||
| 98 | .offset = 0x088, | ||
| 99 | .bytes = 4, | ||
| 100 | .bit_offset = 0, | ||
| 101 | .nbits = 32, | ||
| 102 | }, { | ||
| 103 | .name = "tsensor-cpu0", | ||
| 104 | .offset = 0x098, | ||
| 105 | .bytes = 4, | ||
| 106 | .bit_offset = 0, | ||
| 107 | .nbits = 32, | ||
| 108 | }, { | ||
| 109 | .name = "xusb-pad-calibration", | ||
| 110 | .offset = 0x0f0, | ||
| 111 | .bytes = 4, | ||
| 112 | .bit_offset = 0, | ||
| 113 | .nbits = 32, | ||
| 114 | }, { | ||
| 115 | .name = "tsensor-cpu3", | ||
| 116 | .offset = 0x12c, | ||
| 117 | .bytes = 4, | ||
| 118 | .bit_offset = 0, | ||
| 119 | .nbits = 32, | ||
| 120 | }, { | ||
| 121 | .name = "sata-calibration", | ||
| 122 | .offset = 0x124, | ||
| 123 | .bytes = 1, | ||
| 124 | .bit_offset = 0, | ||
| 125 | .nbits = 2, | ||
| 126 | }, { | ||
| 127 | .name = "tsensor-gpu", | ||
| 128 | .offset = 0x154, | ||
| 129 | .bytes = 4, | ||
| 130 | .bit_offset = 0, | ||
| 131 | .nbits = 32, | ||
| 132 | }, { | ||
| 133 | .name = "tsensor-mem0", | ||
| 134 | .offset = 0x158, | ||
| 135 | .bytes = 4, | ||
| 136 | .bit_offset = 0, | ||
| 137 | .nbits = 32, | ||
| 138 | }, { | ||
| 139 | .name = "tsensor-mem1", | ||
| 140 | .offset = 0x15c, | ||
| 141 | .bytes = 4, | ||
| 142 | .bit_offset = 0, | ||
| 143 | .nbits = 32, | ||
| 144 | }, { | ||
| 145 | .name = "tsensor-pllx", | ||
| 146 | .offset = 0x160, | ||
| 147 | .bytes = 4, | ||
| 148 | .bit_offset = 0, | ||
| 149 | .nbits = 32, | ||
| 150 | }, { | ||
| 151 | .name = "tsensor-common", | ||
| 152 | .offset = 0x180, | ||
| 153 | .bytes = 4, | ||
| 154 | .bit_offset = 0, | ||
| 155 | .nbits = 32, | ||
| 156 | }, { | ||
| 157 | .name = "tsensor-realignment", | ||
| 158 | .offset = 0x1fc, | ||
| 159 | .bytes = 4, | ||
| 160 | .bit_offset = 0, | ||
| 161 | .nbits = 32, | ||
| 162 | }, { | ||
| 163 | .name = "gpu-calibration", | ||
| 164 | .offset = 0x204, | ||
| 165 | .bytes = 4, | ||
| 166 | .bit_offset = 0, | ||
| 167 | .nbits = 32, | ||
| 168 | }, { | ||
| 169 | .name = "xusb-pad-calibration-ext", | ||
| 170 | .offset = 0x250, | ||
| 171 | .bytes = 4, | ||
| 172 | .bit_offset = 0, | ||
| 173 | .nbits = 32, | ||
| 174 | }, | ||
| 175 | }; | ||
| 176 | |||
| 118 | static int tegra_fuse_probe(struct platform_device *pdev) | 177 | static int tegra_fuse_probe(struct platform_device *pdev) |
| 119 | { | 178 | { |
| 120 | void __iomem *base = fuse->base; | 179 | void __iomem *base = fuse->base; |
| 180 | struct nvmem_config nvmem; | ||
| 121 | struct resource *res; | 181 | struct resource *res; |
| 122 | int err; | 182 | int err; |
| 123 | 183 | ||
| @@ -146,20 +206,42 @@ static int tegra_fuse_probe(struct platform_device *pdev) · 22 unmodified lines | |||
| 146 | 206 | ||
| 147 | if (fuse->soc->probe) { | 207 | if (fuse->soc->probe) { |
| 148 | err = fuse->soc->probe(fuse); | 208 | err = fuse->soc->probe(fuse); |
| 149 | if (err < 0) { | 209 | if (err < 0) |
| 150 | fuse->base = base; | 210 | goto restore; |
| 151 | return err; | ||
| 152 | } | ||
| 153 | } | 211 | } |
| 154 | 212 | ||
| 155 | if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size, | 213 | memset(&nvmem, 0, sizeof(nvmem)); |
| 156 | fuse->soc->info)) | 214 | nvmem.dev = &pdev->dev; |
| 157 | return -ENODEV; | 215 | nvmem.name = "fuse"; |
| 216 | nvmem.id = -1; | ||
| 217 | nvmem.owner = THIS_MODULE; | ||
| 218 | nvmem.cells = tegra_fuse_cells; | ||
| 219 | nvmem.ncells = ARRAY_SIZE(tegra_fuse_cells); | ||
| 220 | nvmem.type = NVMEM_TYPE_OTP; | ||
| 221 | nvmem.read_only = true; | ||
| 222 | nvmem.root_only = true; | ||
| 223 | nvmem.reg_read = tegra_fuse_read; | ||
| 224 | nvmem.size = fuse->soc->info->size; | ||
| 225 | nvmem.word_size = 4; | ||
| 226 | nvmem.stride = 4; | ||
| 227 | nvmem.priv = fuse; | ||
| 228 | |||
| 229 | fuse->nvmem = devm_nvmem_register(&pdev->dev, &nvmem); | ||
| 230 | if (IS_ERR(fuse->nvmem)) { | ||
| 231 | err = PTR_ERR(fuse->nvmem); | ||
| 232 | dev_err(&pdev->dev, "failed to register NVMEM device: %d\n", | ||
| 233 | err); | ||
| 234 | goto restore; | ||
| 235 | } | ||
| 158 | 236 | ||
| 159 | /* release the early I/O memory mapping */ | 237 | /* release the early I/O memory mapping */ |
| 160 | iounmap(base); | 238 | iounmap(base); |
| 161 | 239 | ||
| 162 | return 0; | 240 | return 0; |
| 241 | |||
| 242 | restore: | ||
| 243 | fuse->base = base; | ||
| 244 | return err; | ||
| 163 | } | 245 | } |
| 164 | 246 | ||
| 165 | static struct platform_driver tegra_fuse_driver = { | 247 | static struct platform_driver tegra_fuse_driver = { |
| @@ -186,9 +268,12 @@ u32 __init tegra_fuse_read_early(unsigned int offset) · 20 unmodified lines | |||
| 186 | 268 | ||
| 187 | int tegra_fuse_readl(unsigned long offset, u32 *value) | 269 | int tegra_fuse_readl(unsigned long offset, u32 *value) |
| 188 | { | 270 | { |
| 189 | if (!fuse->read) | 271 | if (!fuse->read || !fuse->clk) |
| 190 | return -EPROBE_DEFER; | 272 | return -EPROBE_DEFER; |
| 191 | 273 | ||
| 274 | if (IS_ERR(fuse->clk)) | ||
| 275 | return PTR_ERR(fuse->clk); | ||
| 276 | |||
| 192 | *value = fuse->read(fuse, offset); | 277 | *value = fuse->read(fuse, offset); |
| 193 | 278 | ||
| 194 | return 0; | 279 | return 0; |
| @@ -338,6 +423,15 @@ static int __init tegra_init_fuse(void) · 143 unmodified lines | |||
| 338 | pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", | 423 | pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", |
| 339 | tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); | 424 | tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); |
| 340 | 425 | ||
| 426 | if (fuse->soc->lookups) { | ||
| 427 | size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups; | ||
| 428 | |||
| 429 | fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL); | ||
| 430 | if (!fuse->lookups) | ||
| 431 | return -ENOMEM; | ||
| 432 | |||
| 433 | nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); | ||
| 434 | } | ||
| 341 | 435 | ||
| 342 | return 0; | 436 | return 0; |
| 343 | } | 437 | } |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | #include <linux/err.h> | 8 | #include <linux/err.h> |
| 9 | #include <linux/io.h> | 9 | #include <linux/io.h> |
| 10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 11 | #include <linux/nvmem-consumer.h> | ||
| 11 | #include <linux/of_device.h> | 12 | #include <linux/of_device.h> |
| 12 | #include <linux/of_address.h> | 13 | #include <linux/of_address.h> |
| 13 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
| @@ -127,6 +128,70 @@ const struct tegra_fuse_soc tegra114_fuse_soc = { · 113 unmodified lines | |||
| 127 | #endif | 128 | #endif |
| 128 | 129 | ||
| 129 | #if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) | 130 | #if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) |
| 131 | static const struct nvmem_cell_lookup tegra124_fuse_lookups[] = { | ||
| 132 | { | ||
| 133 | .nvmem_name = "fuse", | ||
| 134 | .cell_name = "xusb-pad-calibration", | ||
| 135 | .dev_id = "7009f000.padctl", | ||
| 136 | .con_id = "calibration", | ||
| 137 | }, { | ||
| 138 | .nvmem_name = "fuse", | ||
| 139 | .cell_name = "sata-calibration", | ||
| 140 | .dev_id = "70020000.sata", | ||
| 141 | .con_id = "calibration", | ||
| 142 | }, { | ||
| 143 | .nvmem_name = "fuse", | ||
| 144 | .cell_name = "tsensor-common", | ||
| 145 | .dev_id = "700e2000.thermal-sensor", | ||
| 146 | .con_id = "common", | ||
| 147 | }, { | ||
| 148 | .nvmem_name = "fuse", | ||
| 149 | .cell_name = "tsensor-realignment", | ||
| 150 | .dev_id = "700e2000.thermal-sensor", | ||
| 151 | .con_id = "realignment", | ||
| 152 | }, { | ||
| 153 | .nvmem_name = "fuse", | ||
| 154 | .cell_name = "tsensor-cpu0", | ||
| 155 | .dev_id = "700e2000.thermal-sensor", | ||
| 156 | .con_id = "cpu0", | ||
| 157 | }, { | ||
| 158 | .nvmem_name = "fuse", | ||
| 159 | .cell_name = "tsensor-cpu1", | ||
| 160 | .dev_id = "700e2000.thermal-sensor", | ||
| 161 | .con_id = "cpu1", | ||
| 162 | }, { | ||
| 163 | .nvmem_name = "fuse", | ||
| 164 | .cell_name = "tsensor-cpu2", | ||
| 165 | .dev_id = "700e2000.thermal-sensor", | ||
| 166 | .con_id = "cpu2", | ||
| 167 | }, { | ||
| 168 | .nvmem_name = "fuse", | ||
| 169 | .cell_name = "tsensor-cpu3", | ||
| 170 | .dev_id = "700e2000.thermal-sensor", | ||
| 171 | .con_id = "cpu3", | ||
| 172 | }, { | ||
| 173 | .nvmem_name = "fuse", | ||
| 174 | .cell_name = "tsensor-mem0", | ||
| 175 | .dev_id = "700e2000.thermal-sensor", | ||
| 176 | .con_id = "mem0", | ||
| 177 | }, { | ||
| 178 | .nvmem_name = "fuse", | ||
| 179 | .cell_name = "tsensor-mem1", | ||
| 180 | .dev_id = "700e2000.thermal-sensor", | ||
| 181 | .con_id = "mem1", | ||
| 182 | }, { | ||
| 183 | .nvmem_name = "fuse", | ||
| 184 | .cell_name = "tsensor-gpu", | ||
| 185 | .dev_id = "700e2000.thermal-sensor", | ||
| 186 | .con_id = "gpu", | ||
| 187 | }, { | ||
| 188 | .nvmem_name = "fuse", | ||
| 189 | .cell_name = "tsensor-pllx", | ||
| 190 | .dev_id = "700e2000.thermal-sensor", | ||
| 191 | .con_id = "pllx", | ||
| 192 | }, | ||
| 193 | }; | ||
| 194 | |||
| 130 | static const struct tegra_fuse_info tegra124_fuse_info = { | 195 | static const struct tegra_fuse_info tegra124_fuse_info = { |
| 131 | .read = tegra30_fuse_read, | 196 | .read = tegra30_fuse_read, |
| 132 | .size = 0x300, | 197 | .size = 0x300, |
| @@ -137,10 +202,81 @@ const struct tegra_fuse_soc tegra124_fuse_soc = { · 4 unmodified lines | |||
| 137 | .init = tegra30_fuse_init, | 202 | .init = tegra30_fuse_init, |
| 138 | .speedo_init = tegra124_init_speedo_data, | 203 | .speedo_init = tegra124_init_speedo_data, |
| 139 | .info = &tegra124_fuse_info, | 204 | .info = &tegra124_fuse_info, |
| 205 | .lookups = tegra124_fuse_lookups, | ||
| 206 | .num_lookups = ARRAY_SIZE(tegra124_fuse_lookups), | ||
| 140 | }; | 207 | }; |
| 141 | #endif | 208 | #endif |
| 142 | 209 | ||
| 143 | #if defined(CONFIG_ARCH_TEGRA_210_SOC) | 210 | #if defined(CONFIG_ARCH_TEGRA_210_SOC) |
| 211 | static const struct nvmem_cell_lookup tegra210_fuse_lookups[] = { | ||
| 212 | { | ||
| 213 | .nvmem_name = "fuse", | ||
| 214 | .cell_name = "tsensor-cpu1", | ||
| 215 | .dev_id = "700e2000.thermal-sensor", | ||
| 216 | .con_id = "cpu1", | ||
| 217 | }, { | ||
| 218 | .nvmem_name = "fuse", | ||
| 219 | .cell_name = "tsensor-cpu2", | ||
| 220 | .dev_id = "700e2000.thermal-sensor", | ||
| 221 | .con_id = "cpu2", | ||
| 222 | }, { | ||
| 223 | .nvmem_name = "fuse", | ||
| 224 | .cell_name = "tsensor-cpu0", | ||
| 225 | .dev_id = "700e2000.thermal-sensor", | ||
| 226 | .con_id = "cpu0", | ||
| 227 | }, { | ||
| 228 | .nvmem_name = "fuse", | ||
| 229 | .cell_name = "xusb-pad-calibration", | ||
| 230 | .dev_id = "7009f000.padctl", | ||
| 231 | .con_id = "calibration", | ||
| 232 | }, { | ||
| 233 | .nvmem_name = "fuse", | ||
| 234 | .cell_name = "tsensor-cpu3", | ||
| 235 | .dev_id = "700e2000.thermal-sensor", | ||
| 236 | .con_id = "cpu3", | ||
| 237 | }, { | ||
| 238 | .nvmem_name = "fuse", | ||
| 239 | .cell_name = "sata-calibration", | ||
| 240 | .dev_id = "70020000.sata", | ||
| 241 | .con_id = "calibration", | ||
| 242 | }, { | ||
| 243 | .nvmem_name = "fuse", | ||
| 244 | .cell_name = "tsensor-gpu", | ||
| 245 | .dev_id = "700e2000.thermal-sensor", | ||
| 246 | .con_id = "gpu", | ||
| 247 | }, { | ||
| 248 | .nvmem_name = "fuse", | ||
| 249 | .cell_name = "tsensor-mem0", | ||
| 250 | .dev_id = "700e2000.thermal-sensor", | ||
| 251 | .con_id = "mem0", | ||
| 252 | }, { | ||
| 253 | .nvmem_name = "fuse", | ||
| 254 | .cell_name = "tsensor-mem1", | ||
| 255 | .dev_id = "700e2000.thermal-sensor", | ||
| 256 | .con_id = "mem1", | ||
| 257 | }, { | ||
| 258 | .nvmem_name = "fuse", | ||
| 259 | .cell_name = "tsensor-pllx", | ||
| 260 | .dev_id = "700e2000.thermal-sensor", | ||
| 261 | .con_id = "pllx", | ||
| 262 | }, { | ||
| 263 | .nvmem_name = "fuse", | ||
| 264 | .cell_name = "tsensor-common", | ||
| 265 | .dev_id = "700e2000.thermal-sensor", | ||
| 266 | .con_id = "common", | ||
| 267 | }, { | ||
| 268 | .nvmem_name = "fuse", | ||
| 269 | .cell_name = "gpu-calibration", | ||
| 270 | .dev_id = "57000000.gpu", | ||
| 271 | .con_id = "calibration", | ||
| 272 | }, { | ||
| 273 | .nvmem_name = "fuse", | ||
| 274 | .cell_name = "xusb-pad-calibration-ext", | ||
| 275 | .dev_id = "7009f000.padctl", | ||
| 276 | .con_id = "calibration-ext", | ||
| 277 | }, | ||
| 278 | }; | ||
| 279 | |||
| 144 | static const struct tegra_fuse_info tegra210_fuse_info = { | 280 | static const struct tegra_fuse_info tegra210_fuse_info = { |
| 145 | .read = tegra30_fuse_read, | 281 | .read = tegra30_fuse_read, |
| 146 | .size = 0x300, | 282 | .size = 0x300, |
| @@ -151,10 +287,26 @@ const struct tegra_fuse_soc tegra210_fuse_soc = { · 4 unmodified lines | |||
| 151 | .init = tegra30_fuse_init, | 287 | .init = tegra30_fuse_init, |
| 152 | .speedo_init = tegra210_init_speedo_data, | 288 | .speedo_init = tegra210_init_speedo_data, |
| 153 | .info = &tegra210_fuse_info, | 289 | .info = &tegra210_fuse_info, |
| 290 | .lookups = tegra210_fuse_lookups, | ||
| 291 | .num_lookups = ARRAY_SIZE(tegra210_fuse_lookups), | ||
| 154 | }; | 292 | }; |
| 155 | #endif | 293 | #endif |
| 156 | 294 | ||
| 157 | #if defined(CONFIG_ARCH_TEGRA_186_SOC) | 295 | #if defined(CONFIG_ARCH_TEGRA_186_SOC) |
| 296 | static const struct nvmem_cell_lookup tegra186_fuse_lookups[] = { | ||
| 297 | { | ||
| 298 | .nvmem_name = "fuse", | ||
| 299 | .cell_name = "xusb-pad-calibration", | ||
| 300 | .dev_id = "3520000.padctl", | ||
| 301 | .con_id = "calibration", | ||
| 302 | }, { | ||
| 303 | .nvmem_name = "fuse", | ||
| 304 | .cell_name = "xusb-pad-calibration-ext", | ||
| 305 | .dev_id = "3520000.padctl", | ||
| 306 | .con_id = "calibration-ext", | ||
| 307 | }, | ||
| 308 | }; | ||
| 309 | |||
| 158 | static const struct tegra_fuse_info tegra186_fuse_info = { | 310 | static const struct tegra_fuse_info tegra186_fuse_info = { |
| 159 | .read = tegra30_fuse_read, | 311 | .read = tegra30_fuse_read, |
| 160 | .size = 0x300, | 312 | .size = 0x300, |
| @@ -164,5 +316,7 @@ static const struct tegra_fuse_info tegra186_fuse_info = { · 3 unmodified lines | |||
| 164 | const struct tegra_fuse_soc tegra186_fuse_soc = { | 316 | const struct tegra_fuse_soc tegra186_fuse_soc = { |
| 165 | .init = tegra30_fuse_init, | 317 | .init = tegra30_fuse_init, |
| 166 | .info = &tegra186_fuse_info, | 318 | .info = &tegra186_fuse_info, |
| 319 | .lookups = tegra186_fuse_lookups, | ||
| 320 | .num_lookups = ARRAY_SIZE(tegra186_fuse_lookups), | ||
| 167 | }; | 321 | }; |
| 168 | #endif | 322 | #endif |
| @@ -13,6 +13,8 @@ · 12 unmodified lines | |||
| 13 | #include <linux/dmaengine.h> | 13 | #include <linux/dmaengine.h> |
| 14 | #include <linux/types.h> | 14 | #include <linux/types.h> |
| 15 | 15 | ||
| 16 | struct nvmem_cell_lookup; | ||
| 17 | struct nvmem_device; | ||
| 16 | struct tegra_fuse; | 18 | struct tegra_fuse; |
| 17 | 19 | ||
| 18 | struct tegra_fuse_info { | 20 | struct tegra_fuse_info { |
| @@ -27,6 +29,9 @@ struct tegra_fuse_soc { · 8 unmodified lines | |||
| 27 | int (*probe)(struct tegra_fuse *fuse); | 29 | int (*probe)(struct tegra_fuse *fuse); |
| 28 | 30 | ||
| 29 | const struct tegra_fuse_info *info; | 31 | const struct tegra_fuse_info *info; |
| 32 | |||
| 33 | const struct nvmem_cell_lookup *lookups; | ||
| 34 | unsigned int num_lookups; | ||
| 30 | }; | 35 | }; |
| 31 | 36 | ||
| 32 | struct tegra_fuse { | 37 | struct tegra_fuse { |
| @@ -48,6 +53,9 @@ struct tegra_fuse { · 15 unmodified lines | |||
| 48 | dma_addr_t phys; | 53 | dma_addr_t phys; |
| 49 | u32 *virt; | 54 | u32 *virt; |
| 50 | } apbdma; | 55 | } apbdma; |
| 56 | |||
| 57 | struct nvmem_device *nvmem; | ||
| 58 | struct nvmem_cell_lookup *lookups; | ||
| 51 | }; | 59 | }; |
| 52 | 60 | ||
| 53 | void tegra_init_revision(void); | 61 | void tegra_init_revision(void); |
| @@ -56,8 +56,14 @@ · 55 unmodified lines | |||
| 56 | #define PMC_CNTRL_SIDE_EFFECT_LP0 BIT(14) /* LP0 when CPU pwr gated */ | 56 | #define PMC_CNTRL_SIDE_EFFECT_LP0 BIT(14) /* LP0 when CPU pwr gated */ |
| 57 | #define PMC_CNTRL_SYSCLK_OE BIT(11) /* system clock enable */ | 57 | #define PMC_CNTRL_SYSCLK_OE BIT(11) /* system clock enable */ |
| 58 | #define PMC_CNTRL_SYSCLK_POLARITY BIT(10) /* sys clk polarity */ | 58 | #define PMC_CNTRL_SYSCLK_POLARITY BIT(10) /* sys clk polarity */ |
| 59 | #define PMC_CNTRL_PWRREQ_POLARITY BIT(8) | ||
| 59 | #define PMC_CNTRL_MAIN_RST BIT(4) | 60 | #define PMC_CNTRL_MAIN_RST BIT(4) |
| 60 | 61 | ||
| 62 | #define PMC_WAKE_MASK 0x0c | ||
| 63 | #define PMC_WAKE_LEVEL 0x10 | ||
| 64 | #define PMC_WAKE_STATUS 0x14 | ||
| 65 | #define PMC_SW_WAKE_STATUS 0x18 | ||
| 66 | |||
| 61 | #define DPD_SAMPLE 0x020 | 67 | #define DPD_SAMPLE 0x020 |
| 62 | #define DPD_SAMPLE_ENABLE BIT(0) | 68 | #define DPD_SAMPLE_ENABLE BIT(0) |
| 63 | #define DPD_SAMPLE_DISABLE (0 << 0) | 69 | #define DPD_SAMPLE_DISABLE (0 << 0) |
| @@ -82,11 +88,18 @@ · 18 unmodified lines | |||
| 82 | 88 | ||
| 83 | #define PMC_CPUPWRGOOD_TIMER 0xc8 | 89 | #define PMC_CPUPWRGOOD_TIMER 0xc8 |
| 84 | #define PMC_CPUPWROFF_TIMER 0xcc | 90 | #define PMC_CPUPWROFF_TIMER 0xcc |
| 91 | #define PMC_COREPWRGOOD_TIMER 0x3c | ||
| 92 | #define PMC_COREPWROFF_TIMER 0xe0 | ||
| 85 | 93 | ||
| 86 | #define PMC_PWR_DET_VALUE 0xe4 | 94 | #define PMC_PWR_DET_VALUE 0xe4 |
| 87 | 95 | ||
| 88 | #define PMC_SCRATCH41 0x140 | 96 | #define PMC_SCRATCH41 0x140 |
| 89 | 97 | ||
| 98 | #define PMC_WAKE2_MASK 0x160 | ||
| 99 | #define PMC_WAKE2_LEVEL 0x164 | ||
| 100 | #define PMC_WAKE2_STATUS 0x168 | ||
| 101 | #define PMC_SW_WAKE2_STATUS 0x16c | ||
| 102 | |||
| 90 | #define PMC_SENSOR_CTRL 0x1b0 | 103 | #define PMC_SENSOR_CTRL 0x1b0 |
| 91 | #define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2) | 104 | #define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2) |
| 92 | #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1) | 105 | #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1) |
| @@ -226,6 +239,8 @@ struct tegra_pmc_soc { · 133 unmodified lines | |||
| 226 | void (*setup_irq_polarity)(struct tegra_pmc *pmc, | 239 | void (*setup_irq_polarity)(struct tegra_pmc *pmc, |
| 227 | struct device_node *np, | 240 | struct device_node *np, |
| 228 | bool invert); | 241 | bool invert); |
| 242 | int (*irq_set_wake)(struct irq_data *data, unsigned int on); | ||
| 243 | int (*irq_set_type)(struct irq_data *data, unsigned int type); | ||
| 229 | 244 | ||
| 230 | const char * const *reset_sources; | 245 | const char * const *reset_sources; |
| 231 | unsigned int num_reset_sources; | 246 | unsigned int num_reset_sources; |
| @@ -309,6 +324,7 @@ static const char * const tegra210_reset_sources[] = { · 77 unmodified lines | |||
| 309 | * @pctl_dev: pin controller exposed by the PMC | 324 | * @pctl_dev: pin controller exposed by the PMC |
| 310 | * @domain: IRQ domain provided by the PMC | 325 | * @domain: IRQ domain provided by the PMC |
| 311 | * @irq: chip implementation for the IRQ domain | 326 | * @irq: chip implementation for the IRQ domain |
| 327 | * @clk_nb: pclk clock changes handler | ||
| 312 | */ | 328 | */ |
| 313 | struct tegra_pmc { | 329 | struct tegra_pmc { |
| 314 | struct device *dev; | 330 | struct device *dev; |
| @@ -344,6 +360,8 @@ struct tegra_pmc { · 29 unmodified lines | |||
| 344 | 360 | ||
| 345 | struct irq_domain *domain; | 361 | struct irq_domain *domain; |
| 346 | struct irq_chip irq; | 362 | struct irq_chip irq; |
| 363 | |||
| 364 | struct notifier_block clk_nb; | ||
| 347 | }; | 365 | }; |
| 348 | 366 | ||
| 349 | static struct tegra_pmc *pmc = &(struct tegra_pmc) { | 367 | static struct tegra_pmc *pmc = &(struct tegra_pmc) { |
| @@ -1192,7 +1210,7 @@ static int tegra_io_pad_prepare(struct tegra_pmc *pmc, enum tegra_io_pad id, · 842 unmodified lines | |||
| 1192 | return err; | 1210 | return err; |
| 1193 | 1211 | ||
| 1194 | if (pmc->clk) { | 1212 | if (pmc->clk) { |
| 1195 | rate = clk_get_rate(pmc->clk); | 1213 | rate = pmc->rate; |
| 1196 | if (!rate) { | 1214 | if (!rate) { |
| 1197 | dev_err(pmc->dev, "failed to get clock rate\n"); | 1215 | dev_err(pmc->dev, "failed to get clock rate\n"); |
| 1198 | return -ENODEV; | 1216 | return -ENODEV; |
| @@ -1433,6 +1451,7 @@ void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) · 234 unmodified lines | |||
| 1433 | void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) | 1451 | void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) |
| 1434 | { | 1452 | { |
| 1435 | unsigned long long rate = 0; | 1453 | unsigned long long rate = 0; |
| 1454 | u64 ticks; | ||
| 1436 | u32 value; | 1455 | u32 value; |
| 1437 | 1456 | ||
| 1438 | switch (mode) { | 1457 | switch (mode) { |
| @@ -1441,7 +1460,7 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) · 2 unmodified lines | |||
| 1441 | break; | 1460 | break; |
| 1442 | 1461 | ||
| 1443 | case TEGRA_SUSPEND_LP2: | 1462 | case TEGRA_SUSPEND_LP2: |
| 1444 | rate = clk_get_rate(pmc->clk); | 1463 | rate = pmc->rate; |
| 1445 | break; | 1464 | break; |
| 1446 | 1465 | ||
| 1447 | default: | 1466 | default: |
| @@ -1451,21 +1470,13 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) · 3 unmodified lines | |||
| 1451 | if (WARN_ON_ONCE(rate == 0)) | 1470 | if (WARN_ON_ONCE(rate == 0)) |
| 1452 | rate = 100000000; | 1471 | rate = 100000000; |
| 1453 | 1472 | ||
| 1454 | if (rate != pmc->rate) { | 1473 | ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; |
| 1455 | u64 ticks; | 1474 | do_div(ticks, USEC_PER_SEC); |
| 1456 | 1475 | tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER); | |
| 1457 | ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; | ||
| 1458 | do_div(ticks, USEC_PER_SEC); | ||
| 1459 | tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER); | ||
| 1460 | 1476 | ||
| 1461 | ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; | 1477 | ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; |
| 1462 | do_div(ticks, USEC_PER_SEC); | 1478 | do_div(ticks, USEC_PER_SEC); |
| 1463 | tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER); | 1479 | tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER); |
| 1464 | |||
| 1465 | wmb(); | ||
| 1466 | |||
| 1467 | pmc->rate = rate; | ||
| 1468 | } | ||
| 1469 | 1480 | ||
| 1470 | value = tegra_pmc_readl(pmc, PMC_CNTRL); | 1481 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
| 1471 | value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; | 1482 | value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; |
| @@ -1899,6 +1910,20 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq, · 427 unmodified lines | |||
| 1899 | event->id, | 1910 | event->id, |
| 1900 | &pmc->irq, pmc); | 1911 | &pmc->irq, pmc); |
| 1901 | 1912 | ||
| 1913 | /* | ||
| 1914 | * GPIOs don't have an equivalent interrupt in the | ||
| 1915 | * parent controller (GIC). However some code, such | ||
| 1916 | * as the one in irq_get_irqchip_state(), require a | ||
| 1917 | * valid IRQ chip to be set. Make sure that's the | ||
| 1918 | * case by passing NULL here, which will install a | ||
| 1919 | * dummy IRQ chip for the interrupt in the parent | ||
| 1920 | * domain. | ||
| 1921 | */ | ||
| 1922 | if (domain->parent) | ||
| 1923 | irq_domain_set_hwirq_and_chip(domain->parent, | ||
| 1924 | virq, 0, NULL, | ||
| 1925 | NULL); | ||
| 1926 | |||
| 1902 | break; | 1927 | break; |
| 1903 | } | 1928 | } |
| 1904 | } | 1929 | } |
| @@ -1908,10 +1933,22 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq, · 3 unmodified lines | |||
| 1908 | * dummy hardware IRQ number. This is used in the ->irq_set_type() | 1933 | * dummy hardware IRQ number. This is used in the ->irq_set_type() |
| 1909 | * and ->irq_set_wake() callbacks to return early for these IRQs. | 1934 | * and ->irq_set_wake() callbacks to return early for these IRQs. |
| 1910 | */ | 1935 | */ |
| 1911 | if (i == soc->num_wake_events) | 1936 | if (i == soc->num_wake_events) { |
| 1912 | err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX, | 1937 | err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX, |
| 1913 | &pmc->irq, pmc); | 1938 | &pmc->irq, pmc); |
| 1914 | 1939 | ||
| 1940 | /* | ||
| 1941 | * Interrupts without a wake event don't have a corresponding | ||
| 1942 | * interrupt in the parent controller (GIC). Pass NULL for the | ||
| 1943 | * chip here, which causes a dummy IRQ chip to be installed | ||
| 1944 | * for the interrupt in the parent domain, to make this | ||
| 1945 | * explicit. | ||
| 1946 | */ | ||
| 1947 | if (domain->parent) | ||
| 1948 | irq_domain_set_hwirq_and_chip(domain->parent, virq, 0, | ||
| 1949 | NULL, NULL); | ||
| 1950 | } | ||
| 1951 | |||
| 1915 | return err; | 1952 | return err; |
| 1916 | } | 1953 | } |
| 1917 | 1954 | ||
| @@ -1920,7 +1957,87 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = { · 2 unmodified lines | |||
| 1920 | .alloc = tegra_pmc_irq_alloc, | 1957 | .alloc = tegra_pmc_irq_alloc, |
| 1921 | }; | 1958 | }; |
| 1922 | 1959 | ||
| 1923 | static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on) | 1960 | static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on) |
| 1961 | { | ||
| 1962 | struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); | ||
| 1963 | unsigned int offset, bit; | ||
| 1964 | u32 value; | ||
| 1965 | |||
| 1966 | if (data->hwirq == ULONG_MAX) | ||
| 1967 | return 0; | ||
| 1968 | |||
| 1969 | offset = data->hwirq / 32; | ||
| 1970 | bit = data->hwirq % 32; | ||
| 1971 | |||
| 1972 | /* clear wake status */ | ||
| 1973 | tegra_pmc_writel(pmc, 0, PMC_SW_WAKE_STATUS); | ||
| 1974 | tegra_pmc_writel(pmc, 0, PMC_SW_WAKE2_STATUS); | ||
| 1975 | |||
| 1976 | tegra_pmc_writel(pmc, 0, PMC_WAKE_STATUS); | ||
| 1977 | tegra_pmc_writel(pmc, 0, PMC_WAKE2_STATUS); | ||
| 1978 | |||
| 1979 | /* enable PMC wake */ | ||
| 1980 | if (data->hwirq >= 32) | ||
| 1981 | offset = PMC_WAKE2_MASK; | ||
| 1982 | else | ||
| 1983 | offset = PMC_WAKE_MASK; | ||
| 1984 | |||
| 1985 | value = tegra_pmc_readl(pmc, offset); | ||
| 1986 | |||
| 1987 | if (on) | ||
| 1988 | value |= BIT(bit); | ||
| 1989 | else | ||
| 1990 | value &= ~BIT(bit); | ||
| 1991 | |||
| 1992 | tegra_pmc_writel(pmc, value, offset); | ||
| 1993 | |||
| 1994 | return 0; | ||
| 1995 | } | ||
| 1996 | |||
| 1997 | static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type) | ||
| 1998 | { | ||
| 1999 | struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); | ||
| 2000 | unsigned int offset, bit; | ||
| 2001 | u32 value; | ||
| 2002 | |||
| 2003 | if (data->hwirq == ULONG_MAX) | ||
| 2004 | return 0; | ||
| 2005 | |||
| 2006 | offset = data->hwirq / 32; | ||
| 2007 | bit = data->hwirq % 32; | ||
| 2008 | |||
| 2009 | if (data->hwirq >= 32) | ||
| 2010 | offset = PMC_WAKE2_LEVEL; | ||
| 2011 | else | ||
| 2012 | offset = PMC_WAKE_LEVEL; | ||
| 2013 | |||
| 2014 | value = tegra_pmc_readl(pmc, offset); | ||
| 2015 | |||
| 2016 | switch (type) { | ||
| 2017 | case IRQ_TYPE_EDGE_RISING: | ||
| 2018 | case IRQ_TYPE_LEVEL_HIGH: | ||
| 2019 | value |= BIT(bit); | ||
| 2020 | break; | ||
| 2021 | |||
| 2022 | case IRQ_TYPE_EDGE_FALLING: | ||
| 2023 | case IRQ_TYPE_LEVEL_LOW: | ||
| 2024 | value &= ~BIT(bit); | ||
| 2025 | break; | ||
| 2026 | |||
| 2027 | case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING: | ||
| 2028 | value ^= BIT(bit); | ||
| 2029 | break; | ||
| 2030 | |||
| 2031 | default: | ||
| 2032 | return -EINVAL; | ||
| 2033 | } | ||
| 2034 | |||
| 2035 | tegra_pmc_writel(pmc, value, offset); | ||
| 2036 | |||
| 2037 | return 0; | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on) | ||
| 1924 | { | 2041 | { |
| 1925 | struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); | 2042 | struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); |
| 1926 | unsigned int offset, bit; | 2043 | unsigned int offset, bit; |
| @@ -1952,7 +2069,7 @@ static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on) · 25 unmodified lines | |||
| 1952 | return 0; | 2069 | return 0; |
| 1953 | } | 2070 | } |
| 1954 | 2071 | ||
| 1955 | static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type) | 2072 | static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type) |
| 1956 | { | 2073 | { |
| 1957 | struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); | 2074 | struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); |
| 1958 | u32 value; | 2075 | u32 value; |
| @@ -2006,8 +2123,8 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc) · 47 unmodified lines | |||
| 2006 | pmc->irq.irq_unmask = irq_chip_unmask_parent; | 2123 | pmc->irq.irq_unmask = irq_chip_unmask_parent; |
| 2007 | pmc->irq.irq_eoi = irq_chip_eoi_parent; | 2124 | pmc->irq.irq_eoi = irq_chip_eoi_parent; |
| 2008 | pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent; | 2125 | pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent; |
| 2009 | pmc->irq.irq_set_type = tegra_pmc_irq_set_type; | 2126 | pmc->irq.irq_set_type = pmc->soc->irq_set_type; |
| 2010 | pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake; | 2127 | pmc->irq.irq_set_wake = pmc->soc->irq_set_wake; |
| 2011 | 2128 | ||
| 2012 | pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node, | 2129 | pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node, |
| 2013 | &tegra_pmc_irq_domain_ops, pmc); | 2130 | &tegra_pmc_irq_domain_ops, pmc); |
| @@ -2019,6 +2136,33 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc) · 5 unmodified lines | |||
| 2019 | return 0; | 2136 | return 0; |
| 2020 | } | 2137 | } |
| 2021 | 2138 | ||
| 2139 | static int tegra_pmc_clk_notify_cb(struct notifier_block *nb, | ||
| 2140 | unsigned long action, void *ptr) | ||
| 2141 | { | ||
| 2142 | struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, clk_nb); | ||
| 2143 | struct clk_notifier_data *data = ptr; | ||
| 2144 | |||
| 2145 | switch (action) { | ||
| 2146 | case PRE_RATE_CHANGE: | ||
| 2147 | mutex_lock(&pmc->powergates_lock); | ||
| 2148 | break; | ||
| 2149 | |||
| 2150 | case POST_RATE_CHANGE: | ||
| 2151 | pmc->rate = data->new_rate; | ||
| 2152 | /* fall through */ | ||
| 2153 | |||
| 2154 | case ABORT_RATE_CHANGE: | ||
| 2155 | mutex_unlock(&pmc->powergates_lock); | ||
| 2156 | break; | ||
| 2157 | |||
| 2158 | default: | ||
| 2159 | WARN_ON_ONCE(1); | ||
| 2160 | return notifier_from_errno(-EINVAL); | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | return NOTIFY_OK; | ||
| 2164 | } | ||
| 2165 | |||
| 2022 | static int tegra_pmc_probe(struct platform_device *pdev) | 2166 | static int tegra_pmc_probe(struct platform_device *pdev) |
| 2023 | { | 2167 | { |
| 2024 | void __iomem *base; | 2168 | void __iomem *base; |
| @@ -2082,6 +2226,23 @@ static int tegra_pmc_probe(struct platform_device *pdev) · 57 unmodified lines | |||
| 2082 | pmc->clk = NULL; | 2226 | pmc->clk = NULL; |
| 2083 | } | 2227 | } |
| 2084 | 2228 | ||
| 2229 | /* | ||
| 2230 | * PCLK clock rate can't be retrieved using CLK API because it | ||
| 2231 | * causes lockup if CPU enters LP2 idle state from some other | ||
| 2232 | * CLK notifier, hence we're caching the rate's value locally. | ||
| 2233 | */ | ||
| 2234 | if (pmc->clk) { | ||
| 2235 | pmc->clk_nb.notifier_call = tegra_pmc_clk_notify_cb; | ||
| 2236 | err = clk_notifier_register(pmc->clk, &pmc->clk_nb); | ||
| 2237 | if (err) { | ||
| 2238 | dev_err(&pdev->dev, | ||
| 2239 | "failed to register clk notifier\n"); | ||
| 2240 | return err; | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | pmc->rate = clk_get_rate(pmc->clk); | ||
| 2244 | } | ||
| 2245 | |||
| 2085 | pmc->dev = &pdev->dev; | 2246 | pmc->dev = &pdev->dev; |
| 2086 | 2247 | ||
| 2087 | tegra_pmc_init(pmc); | 2248 | tegra_pmc_init(pmc); |
| @@ -2133,6 +2294,8 @@ cleanup_debugfs: · 45 unmodified lines | |||
| 2133 | cleanup_sysfs: | 2294 | cleanup_sysfs: |
| 2134 | device_remove_file(&pdev->dev, &dev_attr_reset_reason); | 2295 | device_remove_file(&pdev->dev, &dev_attr_reset_reason); |
| 2135 | device_remove_file(&pdev->dev, &dev_attr_reset_level); | 2296 | device_remove_file(&pdev->dev, &dev_attr_reset_level); |
| 2297 | clk_notifier_unregister(pmc->clk, &pmc->clk_nb); | ||
| 2298 | |||
| 2136 | return err; | 2299 | return err; |
| 2137 | } | 2300 | } |
| 2138 | 2301 | ||
| @@ -2184,7 +2347,7 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = { · 45 unmodified lines | |||
| 2184 | 2347 | ||
| 2185 | static void tegra20_pmc_init(struct tegra_pmc *pmc) | 2348 | static void tegra20_pmc_init(struct tegra_pmc *pmc) |
| 2186 | { | 2349 | { |
| 2187 | u32 value; | 2350 | u32 value, osc, pmu, off; |
| 2188 | 2351 | ||
| 2189 | /* Always enable CPU power request */ | 2352 | /* Always enable CPU power request */ |
| 2190 | value = tegra_pmc_readl(pmc, PMC_CNTRL); | 2353 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
| @@ -2198,6 +2361,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc) · 7 unmodified lines | |||
| 2198 | else | 2361 | else |
| 2199 | value |= PMC_CNTRL_SYSCLK_POLARITY; | 2362 | value |= PMC_CNTRL_SYSCLK_POLARITY; |
| 2200 | 2363 | ||
| 2364 | if (pmc->corereq_high) | ||
| 2365 | value &= ~PMC_CNTRL_PWRREQ_POLARITY; | ||
| 2366 | else | ||
| 2367 | value |= PMC_CNTRL_PWRREQ_POLARITY; | ||
| 2368 | |||
| 2201 | /* configure the output polarity while the request is tristated */ | 2369 | /* configure the output polarity while the request is tristated */ |
| 2202 | tegra_pmc_writel(pmc, value, PMC_CNTRL); | 2370 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
| 2203 | 2371 | ||
| @@ -2205,6 +2373,16 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc) · 1 unmodified line | |||
| 2205 | value = tegra_pmc_readl(pmc, PMC_CNTRL); | 2373 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
| 2206 | value |= PMC_CNTRL_SYSCLK_OE; | 2374 | value |= PMC_CNTRL_SYSCLK_OE; |
| 2207 | tegra_pmc_writel(pmc, value, PMC_CNTRL); | 2375 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
| 2376 | |||
| 2377 | /* program core timings which are applicable only for suspend state */ | ||
| 2378 | if (pmc->suspend_mode != TEGRA_SUSPEND_NONE) { | ||
| 2379 | osc = DIV_ROUND_UP(pmc->core_osc_time * 8192, 1000000); | ||
| 2380 | pmu = DIV_ROUND_UP(pmc->core_pmu_time * 32768, 1000000); | ||
| 2381 | off = DIV_ROUND_UP(pmc->core_off_time * 32768, 1000000); | ||
| 2382 | tegra_pmc_writel(pmc, ((osc << 8) & 0xff00) | (pmu & 0xff), | ||
| 2383 | PMC_COREPWRGOOD_TIMER); | ||
| 2384 | tegra_pmc_writel(pmc, off, PMC_COREPWROFF_TIMER); | ||
| 2385 | } | ||
| 2208 | } | 2386 | } |
| 2209 | 2387 | ||
| 2210 | static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc, | 2388 | static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc, |
| @@ -2538,6 +2716,10 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = { · 327 unmodified lines | |||
| 2538 | TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) | 2716 | TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) |
| 2539 | }; | 2717 | }; |
| 2540 | 2718 | ||
| 2719 | static const struct tegra_wake_event tegra210_wake_events[] = { | ||
| 2720 | TEGRA_WAKE_IRQ("rtc", 16, 2), | ||
| 2721 | }; | ||
| 2722 | |||
| 2541 | static const struct tegra_pmc_soc tegra210_pmc_soc = { | 2723 | static const struct tegra_pmc_soc tegra210_pmc_soc = { |
| 2542 | .num_powergates = ARRAY_SIZE(tegra210_powergates), | 2724 | .num_powergates = ARRAY_SIZE(tegra210_powergates), |
| 2543 | .powergates = tegra210_powergates, | 2725 | .powergates = tegra210_powergates, |
| @@ -2555,10 +2737,14 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { · 11 unmodified lines | |||
| 2555 | .regs = &tegra20_pmc_regs, | 2737 | .regs = &tegra20_pmc_regs, |
| 2556 | .init = tegra20_pmc_init, | 2738 | .init = tegra20_pmc_init, |
| 2557 | .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, | 2739 | .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, |
| 2740 | .irq_set_wake = tegra210_pmc_irq_set_wake, | ||
| 2741 | .irq_set_type = tegra210_pmc_irq_set_type, | ||
| 2558 | .reset_sources = tegra210_reset_sources, | 2742 | .reset_sources = tegra210_reset_sources, |
| 2559 | .num_reset_sources = ARRAY_SIZE(tegra210_reset_sources), | 2743 | .num_reset_sources = ARRAY_SIZE(tegra210_reset_sources), |
| 2560 | .reset_levels = NULL, | 2744 | .reset_levels = NULL, |
| 2561 | .num_reset_levels = 0, | 2745 | .num_reset_levels = 0, |
| 2746 | .num_wake_events = ARRAY_SIZE(tegra210_wake_events), | ||
| 2747 | .wake_events = tegra210_wake_events, | ||
| 2562 | }; | 2748 | }; |
| 2563 | 2749 | ||
| 2564 | #define TEGRA186_IO_PAD_TABLE(_pad) \ | 2750 | #define TEGRA186_IO_PAD_TABLE(_pad) \ |
| @@ -2680,6 +2866,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { · 115 unmodified lines | |||
| 2680 | .regs = &tegra186_pmc_regs, | 2866 | .regs = &tegra186_pmc_regs, |
| 2681 | .init = NULL, | 2867 | .init = NULL, |
| 2682 | .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, | 2868 | .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, |
| 2869 | .irq_set_wake = tegra186_pmc_irq_set_wake, | ||
| 2870 | .irq_set_type = tegra186_pmc_irq_set_type, | ||
| 2683 | .reset_sources = tegra186_reset_sources, | 2871 | .reset_sources = tegra186_reset_sources, |
| 2684 | .num_reset_sources = ARRAY_SIZE(tegra186_reset_sources), | 2872 | .num_reset_sources = ARRAY_SIZE(tegra186_reset_sources), |
| 2685 | .reset_levels = tegra186_reset_levels, | 2873 | .reset_levels = tegra186_reset_levels, |
| @@ -0,0 +1,365 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Voltage regulators coupler for NVIDIA Tegra20 | ||
| 4 | * Copyright (C) 2019 GRATE-DRIVER project | ||
| 5 | * | ||
| 6 | * Voltage constraints borrowed from downstream kernel sources | ||
| 7 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 8 | */ | ||
| 9 | |||
| 10 | #define pr_fmt(fmt) "tegra voltage-coupler: " fmt | ||
| 11 | |||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/of.h> | ||
| 15 | #include <linux/regulator/coupler.h> | ||
| 16 | #include <linux/regulator/driver.h> | ||
| 17 | #include <linux/regulator/machine.h> | ||
| 18 | |||
| 19 | struct tegra_regulator_coupler { | ||
| 20 | struct regulator_coupler coupler; | ||
| 21 | struct regulator_dev *core_rdev; | ||
| 22 | struct regulator_dev *cpu_rdev; | ||
| 23 | struct regulator_dev *rtc_rdev; | ||
| 24 | int core_min_uV; | ||
| 25 | }; | ||
| 26 | |||
| 27 | static inline struct tegra_regulator_coupler * | ||
| 28 | to_tegra_coupler(struct regulator_coupler *coupler) | ||
| 29 | { | ||
| 30 | return container_of(coupler, struct tegra_regulator_coupler, coupler); | ||
| 31 | } | ||
| 32 | |||
| 33 | static int tegra20_core_limit(struct tegra_regulator_coupler *tegra, | ||
| 34 | struct regulator_dev *core_rdev) | ||
| 35 | { | ||
| 36 | int core_min_uV = 0; | ||
| 37 | int core_max_uV; | ||
| 38 | int core_cur_uV; | ||
| 39 | int err; | ||
| 40 | |||
| 41 | if (tegra->core_min_uV > 0) | ||
| 42 | return tegra->core_min_uV; | ||
| 43 | |||
| 44 | core_cur_uV = regulator_get_voltage_rdev(core_rdev); | ||
| 45 | if (core_cur_uV < 0) | ||
| 46 | return core_cur_uV; | ||
| 47 | |||
| 48 | core_max_uV = max(core_cur_uV, 1200000); | ||
| 49 | |||
| 50 | err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); | ||
| 51 | if (err) | ||
| 52 | return err; | ||
| 53 | |||
| 54 | /* | ||
| 55 | * Limit minimum CORE voltage to a value left from bootloader or, | ||
| 56 | * if it's unreasonably low value, to the most common 1.2v or to | ||
| 57 | * whatever maximum value defined via board's device-tree. | ||
| 58 | */ | ||
| 59 | tegra->core_min_uV = core_max_uV; | ||
| 60 | |||
| 61 | pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV); | ||
| 62 | |||
| 63 | return tegra->core_min_uV; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev, | ||
| 67 | struct regulator_dev *rtc_rdev) | ||
| 68 | { | ||
| 69 | struct coupling_desc *c_desc = &core_rdev->coupling_desc; | ||
| 70 | struct regulator_dev *rdev; | ||
| 71 | int max_spread; | ||
| 72 | unsigned int i; | ||
| 73 | |||
| 74 | for (i = 1; i < c_desc->n_coupled; i++) { | ||
| 75 | max_spread = core_rdev->constraints->max_spread[i - 1]; | ||
| 76 | rdev = c_desc->coupled_rdevs[i]; | ||
| 77 | |||
| 78 | if (rdev == rtc_rdev && max_spread) | ||
| 79 | return max_spread; | ||
| 80 | } | ||
| 81 | |||
| 82 | pr_err_once("rtc-core max-spread is undefined in device-tree\n"); | ||
| 83 | |||
| 84 | return 150000; | ||
| 85 | } | ||
| 86 | |||
| 87 | static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra, | ||
| 88 | struct regulator_dev *core_rdev, | ||
| 89 | struct regulator_dev *rtc_rdev, | ||
| 90 | int cpu_uV, int cpu_min_uV) | ||
| 91 | { | ||
| 92 | int core_min_uV, core_max_uV = INT_MAX; | ||
| 93 | int rtc_min_uV, rtc_max_uV = INT_MAX; | ||
| 94 | int core_target_uV; | ||
| 95 | int rtc_target_uV; | ||
| 96 | int max_spread; | ||
| 97 | int core_uV; | ||
| 98 | int rtc_uV; | ||
| 99 | int err; | ||
| 100 | |||
| 101 | /* | ||
| 102 | * RTC and CORE voltages should be no more than 170mV from each other, | ||
| 103 | * CPU should be below RTC and CORE by at least 120mV. This applies | ||
| 104 | * to all Tegra20 SoC's. | ||
| 105 | */ | ||
| 106 | max_spread = tegra20_core_rtc_max_spread(core_rdev, rtc_rdev); | ||
| 107 | |||
| 108 | /* | ||
| 109 | * The core voltage scaling is currently not hooked up in drivers, | ||
| 110 | * hence we will limit the minimum core voltage to a reasonable value. | ||
| 111 | * This should be good enough for the time being. | ||
| 112 | */ | ||
| 113 | core_min_uV = tegra20_core_limit(tegra, core_rdev); | ||
| 114 | if (core_min_uV < 0) | ||
| 115 | return core_min_uV; | ||
| 116 | |||
| 117 | err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); | ||
| 118 | if (err) | ||
| 119 | return err; | ||
| 120 | |||
| 121 | err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV, | ||
| 122 | PM_SUSPEND_ON); | ||
| 123 | if (err) | ||
| 124 | return err; | ||
| 125 | |||
| 126 | core_uV = regulator_get_voltage_rdev(core_rdev); | ||
| 127 | if (core_uV < 0) | ||
| 128 | return core_uV; | ||
| 129 | |||
| 130 | core_min_uV = max(cpu_min_uV + 125000, core_min_uV); | ||
| 131 | if (core_min_uV > core_max_uV) | ||
| 132 | return -EINVAL; | ||
| 133 | |||
| 134 | if (cpu_uV + 120000 > core_uV) | ||
| 135 | pr_err("core-cpu voltage constraint violated: %d %d\n", | ||
| 136 | core_uV, cpu_uV + 120000); | ||
| 137 | |||
| 138 | rtc_uV = regulator_get_voltage_rdev(rtc_rdev); | ||
| 139 | if (rtc_uV < 0) | ||
| 140 | return rtc_uV; | ||
| 141 | |||
| 142 | if (cpu_uV + 120000 > rtc_uV) | ||
| 143 | pr_err("rtc-cpu voltage constraint violated: %d %d\n", | ||
| 144 | rtc_uV, cpu_uV + 120000); | ||
| 145 | |||
| 146 | if (abs(core_uV - rtc_uV) > 170000) | ||
| 147 | pr_err("core-rtc voltage constraint violated: %d %d\n", | ||
| 148 | core_uV, rtc_uV); | ||
| 149 | |||
| 150 | rtc_min_uV = max(cpu_min_uV + 125000, core_min_uV - max_spread); | ||
| 151 | |||
| 152 | err = regulator_check_voltage(rtc_rdev, &rtc_min_uV, &rtc_max_uV); | ||
| 153 | if (err) | ||
| 154 | return err; | ||
| 155 | |||
| 156 | while (core_uV != core_min_uV || rtc_uV != rtc_min_uV) { | ||
| 157 | if (core_uV < core_min_uV) { | ||
| 158 | core_target_uV = min(core_uV + max_spread, core_min_uV); | ||
| 159 | core_target_uV = min(rtc_uV + max_spread, core_target_uV); | ||
| 160 | } else { | ||
| 161 | core_target_uV = max(core_uV - max_spread, core_min_uV); | ||
| 162 | core_target_uV = max(rtc_uV - max_spread, core_target_uV); | ||
| 163 | } | ||
| 164 | |||
| 165 | err = regulator_set_voltage_rdev(core_rdev, | ||
| 166 | core_target_uV, | ||
| 167 | core_max_uV, | ||
| 168 | PM_SUSPEND_ON); | ||
| 169 | if (err) | ||
| 170 | return err; | ||
| 171 | |||
| 172 | core_uV = core_target_uV; | ||
| 173 | |||
| 174 | if (rtc_uV < rtc_min_uV) { | ||
| 175 | rtc_target_uV = min(rtc_uV + max_spread, rtc_min_uV); | ||
| 176 | rtc_target_uV = min(core_uV + max_spread, rtc_target_uV); | ||
| 177 | } else { | ||
| 178 | rtc_target_uV = max(rtc_uV - max_spread, rtc_min_uV); | ||
| 179 | rtc_target_uV = max(core_uV - max_spread, rtc_target_uV); | ||
| 180 | } | ||
| 181 | |||
| 182 | err = regulator_set_voltage_rdev(rtc_rdev, | ||
| 183 | rtc_target_uV, | ||
| 184 | rtc_max_uV, | ||
| 185 | PM_SUSPEND_ON); | ||
| 186 | if (err) | ||
| 187 | return err; | ||
| 188 | |||
| 189 | rtc_uV = rtc_target_uV; | ||
| 190 | } | ||
| 191 | |||
| 192 | return 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | static int tegra20_core_voltage_update(struct tegra_regulator_coupler *tegra, | ||
| 196 | struct regulator_dev *cpu_rdev, | ||
| 197 | struct regulator_dev *core_rdev, | ||
| 198 | struct regulator_dev *rtc_rdev) | ||
| 199 | { | ||
| 200 | int cpu_uV; | ||
| 201 | |||
| 202 | cpu_uV = regulator_get_voltage_rdev(cpu_rdev); | ||
| 203 | if (cpu_uV < 0) | ||
| 204 | return cpu_uV; | ||
| 205 | |||
| 206 | return tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, | ||
| 207 | cpu_uV, cpu_uV); | ||
| 208 | } | ||
| 209 | |||
| 210 | static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra, | ||
| 211 | struct regulator_dev *cpu_rdev, | ||
| 212 | struct regulator_dev *core_rdev, | ||
| 213 | struct regulator_dev *rtc_rdev) | ||
| 214 | { | ||
| 215 | int cpu_min_uV_consumers = 0; | ||
| 216 | int cpu_max_uV = INT_MAX; | ||
| 217 | int cpu_min_uV = 0; | ||
| 218 | int cpu_uV; | ||
| 219 | int err; | ||
| 220 | |||
| 221 | err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV); | ||
| 222 | if (err) | ||
| 223 | return err; | ||
| 224 | |||
| 225 | err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV, | ||
| 226 | PM_SUSPEND_ON); | ||
| 227 | if (err) | ||
| 228 | return err; | ||
| 229 | |||
| 230 | err = regulator_check_consumers(cpu_rdev, &cpu_min_uV_consumers, | ||
| 231 | &cpu_max_uV, PM_SUSPEND_ON); | ||
| 232 | if (err) | ||
| 233 | return err; | ||
| 234 | |||
| 235 | cpu_uV = regulator_get_voltage_rdev(cpu_rdev); | ||
| 236 | if (cpu_uV < 0) | ||
| 237 | return cpu_uV; | ||
| 238 | |||
| 239 | /* | ||
| 240 | * CPU's regulator may not have any consumers, hence the voltage | ||
| 241 | * must not be changed in that case because CPU simply won't | ||
| 242 | * survive the voltage drop if it's running on a higher frequency. | ||
| 243 | */ | ||
| 244 | if (!cpu_min_uV_consumers) | ||
| 245 | cpu_min_uV = cpu_uV; | ||
| 246 | |||
| 247 | if (cpu_min_uV > cpu_uV) { | ||
| 248 | err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, | ||
| 249 | cpu_uV, cpu_min_uV); | ||
| 250 | if (err) | ||
| 251 | return err; | ||
| 252 | |||
| 253 | err = regulator_set_voltage_rdev(cpu_rdev, cpu_min_uV, | ||
| 254 | cpu_max_uV, PM_SUSPEND_ON); | ||
| 255 | if (err) | ||
| 256 | return err; | ||
| 257 | } else if (cpu_min_uV < cpu_uV) { | ||
| 258 | err = regulator_set_voltage_rdev(cpu_rdev, cpu_min_uV, | ||
| 259 | cpu_max_uV, PM_SUSPEND_ON); | ||
| 260 | if (err) | ||
| 261 | return err; | ||
| 262 | |||
| 263 | err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, | ||
| 264 | cpu_uV, cpu_min_uV); | ||
| 265 | if (err) | ||
| 266 | return err; | ||
| 267 | } | ||
| 268 | |||
| 269 | return 0; | ||
| 270 | } | ||
| 271 | |||
| 272 | static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler, | ||
| 273 | struct regulator_dev *rdev, | ||
| 274 | suspend_state_t state) | ||
| 275 | { | ||
| 276 | struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); | ||
| 277 | struct regulator_dev *core_rdev = tegra->core_rdev; | ||
| 278 | struct regulator_dev *cpu_rdev = tegra->cpu_rdev; | ||
| 279 | struct regulator_dev *rtc_rdev = tegra->rtc_rdev; | ||
| 280 | |||
| 281 | if ((core_rdev != rdev && cpu_rdev != rdev && rtc_rdev != rdev) || | ||
| 282 | state != PM_SUSPEND_ON) { | ||
| 283 | pr_err("regulators are not coupled properly\n"); | ||
| 284 | return -EINVAL; | ||
| 285 | } | ||
| 286 | |||
| 287 | if (rdev == cpu_rdev) | ||
| 288 | return tegra20_cpu_voltage_update(tegra, cpu_rdev, | ||
| 289 | core_rdev, rtc_rdev); | ||
| 290 | |||
| 291 | if (rdev == core_rdev) | ||
| 292 | return tegra20_core_voltage_update(tegra, cpu_rdev, | ||
| 293 | core_rdev, rtc_rdev); | ||
| 294 | |||
| 295 | pr_err("changing %s voltage not permitted\n", rdev_get_name(rtc_rdev)); | ||
| 296 | |||
| 297 | return -EPERM; | ||
| 298 | } | ||
| 299 | |||
| 300 | static int tegra20_regulator_attach(struct regulator_coupler *coupler, | ||
| 301 | struct regulator_dev *rdev) | ||
| 302 | { | ||
| 303 | struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); | ||
| 304 | struct device_node *np = rdev->dev.of_node; | ||
| 305 | |||
| 306 | if (of_property_read_bool(np, "nvidia,tegra-core-regulator") && | ||
| 307 | !tegra->core_rdev) { | ||
| 308 | tegra->core_rdev = rdev; | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | if (of_property_read_bool(np, "nvidia,tegra-rtc-regulator") && | ||
| 313 | !tegra->rtc_rdev) { | ||
| 314 | tegra->rtc_rdev = rdev; | ||
| 315 | return 0; | ||
| 316 | } | ||
| 317 | |||
| 318 | if (of_property_read_bool(np, "nvidia,tegra-cpu-regulator") && | ||
| 319 | !tegra->cpu_rdev) { | ||
| 320 | tegra->cpu_rdev = rdev; | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | return -EINVAL; | ||
| 325 | } | ||
| 326 | |||
| 327 | static int tegra20_regulator_detach(struct regulator_coupler *coupler, | ||
| 328 | struct regulator_dev *rdev) | ||
| 329 | { | ||
| 330 | struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); | ||
| 331 | |||
| 332 | if (tegra->core_rdev == rdev) { | ||
| 333 | tegra->core_rdev = NULL; | ||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | if (tegra->rtc_rdev == rdev) { | ||
| 338 | tegra->rtc_rdev = NULL; | ||
| 339 | return 0; | ||
| 340 | } | ||
| 341 | |||
| 342 | if (tegra->cpu_rdev == rdev) { | ||
| 343 | tegra->cpu_rdev = NULL; | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | return -EINVAL; | ||
| 348 | } | ||
| 349 | |||
| 350 | static struct tegra_regulator_coupler tegra20_coupler = { | ||
| 351 | .coupler = { | ||
| 352 | .attach_regulator = tegra20_regulator_attach, | ||
| 353 | .detach_regulator = tegra20_regulator_detach, | ||
| 354 | .balance_voltage = tegra20_regulator_balance_voltage, | ||
| 355 | }, | ||
| 356 | }; | ||
| 357 | |||
| 358 | static int __init tegra_regulator_coupler_init(void) | ||
| 359 | { | ||
| 360 | if (!of_machine_is_compatible("nvidia,tegra20")) | ||
| 361 | return 0; | ||
| 362 | |||
| 363 | return regulator_coupler_register(&tegra20_coupler.coupler); | ||
| 364 | } | ||
| 365 | arch_initcall(tegra_regulator_coupler_init); | ||
| @@ -0,0 +1,317 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Voltage regulators coupler for NVIDIA Tegra30 | ||
| 4 | * Copyright (C) 2019 GRATE-DRIVER project | ||
| 5 | * | ||
| 6 | * Voltage constraints borrowed from downstream kernel sources | ||
| 7 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 8 | */ | ||
| 9 | |||
| 10 | #define pr_fmt(fmt) "tegra voltage-coupler: " fmt | ||
| 11 | |||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/of.h> | ||
| 15 | #include <linux/regulator/coupler.h> | ||
| 16 | #include <linux/regulator/driver.h> | ||
| 17 | #include <linux/regulator/machine.h> | ||
| 18 | |||
| 19 | #include <soc/tegra/fuse.h> | ||
| 20 | |||
| 21 | struct tegra_regulator_coupler { | ||
| 22 | struct regulator_coupler coupler; | ||
| 23 | struct regulator_dev *core_rdev; | ||
| 24 | struct regulator_dev *cpu_rdev; | ||
| 25 | int core_min_uV; | ||
| 26 | }; | ||
| 27 | |||
| 28 | static inline struct tegra_regulator_coupler * | ||
| 29 | to_tegra_coupler(struct regulator_coupler *coupler) | ||
| 30 | { | ||
| 31 | return container_of(coupler, struct tegra_regulator_coupler, coupler); | ||
| 32 | } | ||
| 33 | |||
| 34 | static int tegra30_core_limit(struct tegra_regulator_coupler *tegra, | ||
| 35 | struct regulator_dev *core_rdev) | ||
| 36 | { | ||
| 37 | int core_min_uV = 0; | ||
| 38 | int core_max_uV; | ||
| 39 | int core_cur_uV; | ||
| 40 | int err; | ||
| 41 | |||
| 42 | if (tegra->core_min_uV > 0) | ||
| 43 | return tegra->core_min_uV; | ||
| 44 | |||
| 45 | core_cur_uV = regulator_get_voltage_rdev(core_rdev); | ||
| 46 | if (core_cur_uV < 0) | ||
| 47 | return core_cur_uV; | ||
| 48 | |||
| 49 | core_max_uV = max(core_cur_uV, 1200000); | ||
| 50 | |||
| 51 | err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); | ||
| 52 | if (err) | ||
| 53 | return err; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * Limit minimum CORE voltage to a value left from bootloader or, | ||
| 57 | * if it's unreasonably low value, to the most common 1.2v or to | ||
| 58 | * whatever maximum value defined via board's device-tree. | ||
| 59 | */ | ||
| 60 | tegra->core_min_uV = core_max_uV; | ||
| 61 | |||
| 62 | pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV); | ||
| 63 | |||
| 64 | return tegra->core_min_uV; | ||
| 65 | } | ||
| 66 | |||
| 67 | static int tegra30_core_cpu_limit(int cpu_uV) | ||
| 68 | { | ||
| 69 | if (cpu_uV < 800000) | ||
| 70 | return 950000; | ||
| 71 | |||
| 72 | if (cpu_uV < 900000) | ||
| 73 | return 1000000; | ||
| 74 | |||
| 75 | if (cpu_uV < 1000000) | ||
| 76 | return 1100000; | ||
| 77 | |||
| 78 | if (cpu_uV < 1100000) | ||
| 79 | return 1200000; | ||
| 80 | |||
| 81 | if (cpu_uV < 1250000) { | ||
| 82 | switch (tegra_sku_info.cpu_speedo_id) { | ||
| 83 | case 0 ... 1: | ||
| 84 | case 4: | ||
| 85 | case 7 ... 8: | ||
| 86 | return 1200000; | ||
| 87 | |||
| 88 | default: | ||
| 89 | return 1300000; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | return -EINVAL; | ||
| 94 | } | ||
| 95 | |||
| 96 | static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, | ||
| 97 | struct regulator_dev *cpu_rdev, | ||
| 98 | struct regulator_dev *core_rdev) | ||
| 99 | { | ||
| 100 | int core_min_uV, core_max_uV = INT_MAX; | ||
| 101 | int cpu_min_uV, cpu_max_uV = INT_MAX; | ||
| 102 | int cpu_min_uV_consumers = 0; | ||
| 103 | int core_min_limited_uV; | ||
| 104 | int core_target_uV; | ||
| 105 | int cpu_target_uV; | ||
| 106 | int core_max_step; | ||
| 107 | int cpu_max_step; | ||
| 108 | int max_spread; | ||
| 109 | int core_uV; | ||
| 110 | int cpu_uV; | ||
| 111 | int err; | ||
| 112 | |||
| 113 | /* | ||
| 114 | * CPU voltage should not got lower than 300mV from the CORE. | ||
| 115 | * CPU voltage should stay below the CORE by 100mV+, depending | ||
| 116 | * by the CORE voltage. This applies to all Tegra30 SoC's. | ||
| 117 | */ | ||
| 118 | max_spread = cpu_rdev->constraints->max_spread[0]; | ||
| 119 | cpu_max_step = cpu_rdev->constraints->max_uV_step; | ||
| 120 | core_max_step = core_rdev->constraints->max_uV_step; | ||
| 121 | |||
| 122 | if (!max_spread) { | ||
| 123 | pr_err_once("cpu-core max-spread is undefined in device-tree\n"); | ||
| 124 | max_spread = 300000; | ||
| 125 | } | ||
| 126 | |||
| 127 | if (!cpu_max_step) { | ||
| 128 | pr_err_once("cpu max-step is undefined in device-tree\n"); | ||
| 129 | cpu_max_step = 150000; | ||
| 130 | } | ||
| 131 | |||
| 132 | if (!core_max_step) { | ||
| 133 | pr_err_once("core max-step is undefined in device-tree\n"); | ||
| 134 | core_max_step = 150000; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | * The CORE voltage scaling is currently not hooked up in drivers, | ||
| 139 | * hence we will limit the minimum CORE voltage to a reasonable value. | ||
| 140 | * This should be good enough for the time being. | ||
| 141 | */ | ||
| 142 | core_min_uV = tegra30_core_limit(tegra, core_rdev); | ||
| 143 | if (core_min_uV < 0) | ||
| 144 | return core_min_uV; | ||
| 145 | |||
| 146 | err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV, | ||
| 147 | PM_SUSPEND_ON); | ||
| 148 | if (err) | ||
| 149 | return err; | ||
| 150 | |||
| 151 | core_uV = regulator_get_voltage_rdev(core_rdev); | ||
| 152 | if (core_uV < 0) | ||
| 153 | return core_uV; | ||
| 154 | |||
| 155 | cpu_min_uV = core_min_uV - max_spread; | ||
| 156 | |||
| 157 | err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV, | ||
| 158 | PM_SUSPEND_ON); | ||
| 159 | if (err) | ||
| 160 | return err; | ||
| 161 | |||
| 162 | err = regulator_check_consumers(cpu_rdev, &cpu_min_uV_consumers, | ||
| 163 | &cpu_max_uV, PM_SUSPEND_ON); | ||
| 164 | if (err) | ||
| 165 | return err; | ||
| 166 | |||
| 167 | err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV); | ||
| 168 | if (err) | ||
| 169 | return err; | ||
| 170 | |||
| 171 | cpu_uV = regulator_get_voltage_rdev(cpu_rdev); | ||
| 172 | if (cpu_uV < 0) | ||
| 173 | return cpu_uV; | ||
| 174 | |||
| 175 | /* | ||
| 176 | * CPU's regulator may not have any consumers, hence the voltage | ||
| 177 | * must not be changed in that case because CPU simply won't | ||
| 178 | * survive the voltage drop if it's running on a higher frequency. | ||
| 179 | */ | ||
| 180 | if (!cpu_min_uV_consumers) | ||
| 181 | cpu_min_uV = cpu_uV; | ||
| 182 | |||
| 183 | /* | ||
| 184 | * Bootloader shall set up voltages correctly, but if it | ||
| 185 | * happens that there is a violation, then try to fix it | ||
| 186 | * at first. | ||
| 187 | */ | ||
| 188 | core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV); | ||
| 189 | if (core_min_limited_uV < 0) | ||
| 190 | return core_min_limited_uV; | ||
| 191 | |||
| 192 | core_min_uV = max(core_min_uV, tegra30_core_cpu_limit(cpu_min_uV)); | ||
| 193 | |||
| 194 | err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); | ||
| 195 | if (err) | ||
| 196 | return err; | ||
| 197 | |||
| 198 | if (core_min_limited_uV > core_uV) { | ||
| 199 | pr_err("core voltage constraint violated: %d %d %d\n", | ||
| 200 | core_uV, core_min_limited_uV, cpu_uV); | ||
| 201 | goto update_core; | ||
| 202 | } | ||
| 203 | |||
| 204 | while (cpu_uV != cpu_min_uV || core_uV != core_min_uV) { | ||
| 205 | if (cpu_uV < cpu_min_uV) { | ||
| 206 | cpu_target_uV = min(cpu_uV + cpu_max_step, cpu_min_uV); | ||
| 207 | } else { | ||
| 208 | cpu_target_uV = max(cpu_uV - cpu_max_step, cpu_min_uV); | ||
| 209 | cpu_target_uV = max(core_uV - max_spread, cpu_target_uV); | ||
| 210 | } | ||
| 211 | |||
| 212 | err = regulator_set_voltage_rdev(cpu_rdev, | ||
| 213 | cpu_target_uV, | ||
| 214 | cpu_max_uV, | ||
| 215 | PM_SUSPEND_ON); | ||
| 216 | if (err) | ||
| 217 | return err; | ||
| 218 | |||
| 219 | cpu_uV = cpu_target_uV; | ||
| 220 | update_core: | ||
| 221 | core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV); | ||
| 222 | if (core_min_limited_uV < 0) | ||
| 223 | return core_min_limited_uV; | ||
| 224 | |||
| 225 | core_target_uV = max(core_min_limited_uV, core_min_uV); | ||
| 226 | |||
| 227 | if (core_uV < core_target_uV) { | ||
| 228 | core_target_uV = min(core_target_uV, core_uV + core_max_step); | ||
| 229 | core_target_uV = min(core_target_uV, cpu_uV + max_spread); | ||
| 230 | } else { | ||
| 231 | core_target_uV = max(core_target_uV, core_uV - core_max_step); | ||
| 232 | } | ||
| 233 | |||
| 234 | err = regulator_set_voltage_rdev(core_rdev, | ||
| 235 | core_target_uV, | ||
| 236 | core_max_uV, | ||
| 237 | PM_SUSPEND_ON); | ||
| 238 | if (err) | ||
| 239 | return err; | ||
| 240 | |||
| 241 | core_uV = core_target_uV; | ||
| 242 | } | ||
| 243 | |||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | |||
| 247 | static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler, | ||
| 248 | struct regulator_dev *rdev, | ||
| 249 | suspend_state_t state) | ||
| 250 | { | ||
| 251 | struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); | ||
| 252 | struct regulator_dev *core_rdev = tegra->core_rdev; | ||
| 253 | struct regulator_dev *cpu_rdev = tegra->cpu_rdev; | ||
| 254 | |||
| 255 | if ((core_rdev != rdev && cpu_rdev != rdev) || state != PM_SUSPEND_ON) { | ||
| 256 | pr_err("regulators are not coupled properly\n"); | ||
| 257 | return -EINVAL; | ||
| 258 | } | ||
| 259 | |||
| 260 | return tegra30_voltage_update(tegra, cpu_rdev, core_rdev); | ||
| 261 | } | ||
| 262 | |||
| 263 | static int tegra30_regulator_attach(struct regulator_coupler *coupler, | ||
| 264 | struct regulator_dev *rdev) | ||
| 265 | { | ||
| 266 | struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); | ||
| 267 | struct device_node *np = rdev->dev.of_node; | ||
| 268 | |||
| 269 | if (of_property_read_bool(np, "nvidia,tegra-core-regulator") && | ||
| 270 | !tegra->core_rdev) { | ||
| 271 | tegra->core_rdev = rdev; | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | if (of_property_read_bool(np, "nvidia,tegra-cpu-regulator") && | ||
| 276 | !tegra->cpu_rdev) { | ||
| 277 | tegra->cpu_rdev = rdev; | ||
| 278 | return 0; | ||
| 279 | } | ||
| 280 | |||
| 281 | return -EINVAL; | ||
| 282 | } | ||
| 283 | |||
| 284 | static int tegra30_regulator_detach(struct regulator_coupler *coupler, | ||
| 285 | struct regulator_dev *rdev) | ||
| 286 | { | ||
| 287 | struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); | ||
| 288 | |||
| 289 | if (tegra->core_rdev == rdev) { | ||
| 290 | tegra->core_rdev = NULL; | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | if (tegra->cpu_rdev == rdev) { | ||
| 295 | tegra->cpu_rdev = NULL; | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | return -EINVAL; | ||
| 300 | } | ||
| 301 | |||
| 302 | static struct tegra_regulator_coupler tegra30_coupler = { | ||
| 303 | .coupler = { | ||
| 304 | .attach_regulator = tegra30_regulator_attach, | ||
| 305 | .detach_regulator = tegra30_regulator_detach, | ||
| 306 | .balance_voltage = tegra30_regulator_balance_voltage, | ||
| 307 | }, | ||
| 308 | }; | ||
| 309 | |||
| 310 | static int __init tegra_regulator_coupler_init(void) | ||
| 311 | { | ||
| 312 | if (!of_machine_is_compatible("nvidia,tegra30")) | ||
| 313 | return 0; | ||
| 314 | |||
| 315 | return regulator_coupler_register(&tegra30_coupler.coupler); | ||
| 316 | } | ||
| 317 | arch_initcall(tegra_regulator_coupler_init); | ||
| @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o · 5 unmodified lines | |||
| 6 | knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o | 6 | knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o |
| 7 | obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o | 7 | obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o |
| 8 | obj-$(CONFIG_AMX3_PM) += pm33xx.o | 8 | obj-$(CONFIG_AMX3_PM) += pm33xx.o |
| 9 | obj-$(CONFIG_ARCH_OMAP2PLUS) += omap_prm.o | ||
| 9 | obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o | 10 | obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o |
| 10 | obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o | 11 | obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o |
| 11 | obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o | 12 | obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o |
| @@ -0,0 +1,391 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * OMAP2+ PRM driver | ||
| 4 | * | ||
| 5 | * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ | ||
| 6 | * Tero Kristo <t-kristo@ti.com> | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <linux/device.h> | ||
| 11 | #include <linux/io.h> | ||
| 12 | #include <linux/iopoll.h> | ||
| 13 | #include <linux/of.h> | ||
| 14 | #include <linux/of_device.h> | ||
| 15 | #include <linux/platform_device.h> | ||
| 16 | #include <linux/reset-controller.h> | ||
| 17 | #include <linux/delay.h> | ||
| 18 | |||
| 19 | #include <linux/platform_data/ti-prm.h> | ||
| 20 | |||
| 21 | struct omap_rst_map { | ||
| 22 | s8 rst; | ||
| 23 | s8 st; | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct omap_prm_data { | ||
| 27 | u32 base; | ||
| 28 | const char *name; | ||
| 29 | const char *clkdm_name; | ||
| 30 | u16 rstctrl; | ||
| 31 | u16 rstst; | ||
| 32 | const struct omap_rst_map *rstmap; | ||
| 33 | u8 flags; | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct omap_prm { | ||
| 37 | const struct omap_prm_data *data; | ||
| 38 | void __iomem *base; | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct omap_reset_data { | ||
| 42 | struct reset_controller_dev rcdev; | ||
| 43 | struct omap_prm *prm; | ||
| 44 | u32 mask; | ||
| 45 | spinlock_t lock; | ||
| 46 | struct clockdomain *clkdm; | ||
| 47 | struct device *dev; | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) | ||
| 51 | |||
| 52 | #define OMAP_MAX_RESETS 8 | ||
| 53 | #define OMAP_RESET_MAX_WAIT 10000 | ||
| 54 | |||
| 55 | #define OMAP_PRM_HAS_RSTCTRL BIT(0) | ||
| 56 | #define OMAP_PRM_HAS_RSTST BIT(1) | ||
| 57 | #define OMAP_PRM_HAS_NO_CLKDM BIT(2) | ||
| 58 | |||
| 59 | #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) | ||
| 60 | |||
| 61 | static const struct omap_rst_map rst_map_0[] = { | ||
| 62 | { .rst = 0, .st = 0 }, | ||
| 63 | { .rst = -1 }, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static const struct omap_rst_map rst_map_01[] = { | ||
| 67 | { .rst = 0, .st = 0 }, | ||
| 68 | { .rst = 1, .st = 1 }, | ||
| 69 | { .rst = -1 }, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static const struct omap_rst_map rst_map_012[] = { | ||
| 73 | { .rst = 0, .st = 0 }, | ||
| 74 | { .rst = 1, .st = 1 }, | ||
| 75 | { .rst = 2, .st = 2 }, | ||
| 76 | { .rst = -1 }, | ||
| 77 | }; | ||
| 78 | |||
| 79 | static const struct omap_prm_data omap4_prm_data[] = { | ||
| 80 | { .name = "tesla", .base = 0x4a306400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 81 | { .name = "core", .base = 0x4a306700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati", .rstmap = rst_map_012 }, | ||
| 82 | { .name = "ivahd", .base = 0x4a306f00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, | ||
| 83 | { .name = "device", .base = 0x4a307b00, .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, | ||
| 84 | { }, | ||
| 85 | }; | ||
| 86 | |||
| 87 | static const struct omap_prm_data omap5_prm_data[] = { | ||
| 88 | { .name = "dsp", .base = 0x4ae06400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 89 | { .name = "core", .base = 0x4ae06700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu", .rstmap = rst_map_012 }, | ||
| 90 | { .name = "iva", .base = 0x4ae07200, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, | ||
| 91 | { .name = "device", .base = 0x4ae07c00, .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, | ||
| 92 | { }, | ||
| 93 | }; | ||
| 94 | |||
| 95 | static const struct omap_prm_data dra7_prm_data[] = { | ||
| 96 | { .name = "dsp1", .base = 0x4ae06400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 97 | { .name = "ipu", .base = 0x4ae06500, .rstctrl = 0x10, .rstst = 0x14, .clkdm_name = "ipu1", .rstmap = rst_map_012 }, | ||
| 98 | { .name = "core", .base = 0x4ae06700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu2", .rstmap = rst_map_012 }, | ||
| 99 | { .name = "iva", .base = 0x4ae06f00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, | ||
| 100 | { .name = "dsp2", .base = 0x4ae07b00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 101 | { .name = "eve1", .base = 0x4ae07b40, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 102 | { .name = "eve2", .base = 0x4ae07b80, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 103 | { .name = "eve3", .base = 0x4ae07bc0, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 104 | { .name = "eve4", .base = 0x4ae07c00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, | ||
| 105 | { }, | ||
| 106 | }; | ||
| 107 | |||
| 108 | static const struct omap_rst_map am3_per_rst_map[] = { | ||
| 109 | { .rst = 1 }, | ||
| 110 | { .rst = -1 }, | ||
| 111 | }; | ||
| 112 | |||
| 113 | static const struct omap_rst_map am3_wkup_rst_map[] = { | ||
| 114 | { .rst = 3, .st = 5 }, | ||
| 115 | { .rst = -1 }, | ||
| 116 | }; | ||
| 117 | |||
| 118 | static const struct omap_prm_data am3_prm_data[] = { | ||
| 119 | { .name = "per", .base = 0x44e00c00, .rstctrl = 0x0, .rstmap = am3_per_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" }, | ||
| 120 | { .name = "wkup", .base = 0x44e00d00, .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, | ||
| 121 | { .name = "device", .base = 0x44e00f00, .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, | ||
| 122 | { .name = "gfx", .base = 0x44e01100, .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3" }, | ||
| 123 | { }, | ||
| 124 | }; | ||
| 125 | |||
| 126 | static const struct omap_rst_map am4_per_rst_map[] = { | ||
| 127 | { .rst = 1, .st = 0 }, | ||
| 128 | { .rst = -1 }, | ||
| 129 | }; | ||
| 130 | |||
| 131 | static const struct omap_rst_map am4_device_rst_map[] = { | ||
| 132 | { .rst = 0, .st = 1 }, | ||
| 133 | { .rst = 1, .st = 0 }, | ||
| 134 | { .rst = -1 }, | ||
| 135 | }; | ||
| 136 | |||
| 137 | static const struct omap_prm_data am4_prm_data[] = { | ||
| 138 | { .name = "gfx", .base = 0x44df0400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3" }, | ||
| 139 | { .name = "per", .base = 0x44df0800, .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map, .clkdm_name = "pruss_ocp" }, | ||
| 140 | { .name = "wkup", .base = 0x44df2000, .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map, .flags = OMAP_PRM_HAS_NO_CLKDM }, | ||
| 141 | { .name = "device", .base = 0x44df4000, .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, | ||
| 142 | { }, | ||
| 143 | }; | ||
| 144 | |||
| 145 | static const struct of_device_id omap_prm_id_table[] = { | ||
| 146 | { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, | ||
| 147 | { .compatible = "ti,omap5-prm-inst", .data = omap5_prm_data }, | ||
| 148 | { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, | ||
| 149 | { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, | ||
| 150 | { .compatible = "ti,am4-prm-inst", .data = am4_prm_data }, | ||
| 151 | { }, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id) | ||
| 155 | { | ||
| 156 | if (reset->mask & BIT(id)) | ||
| 157 | return true; | ||
| 158 | |||
| 159 | return false; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int omap_reset_get_st_bit(struct omap_reset_data *reset, | ||
| 163 | unsigned long id) | ||
| 164 | { | ||
| 165 | const struct omap_rst_map *map = reset->prm->data->rstmap; | ||
| 166 | |||
| 167 | while (map->rst >= 0) { | ||
| 168 | if (map->rst == id) | ||
| 169 | return map->st; | ||
| 170 | |||
| 171 | map++; | ||
| 172 | } | ||
| 173 | |||
| 174 | return id; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int omap_reset_status(struct reset_controller_dev *rcdev, | ||
| 178 | unsigned long id) | ||
| 179 | { | ||
| 180 | struct omap_reset_data *reset = to_omap_reset_data(rcdev); | ||
| 181 | u32 v; | ||
| 182 | int st_bit = omap_reset_get_st_bit(reset, id); | ||
| 183 | bool has_rstst = reset->prm->data->rstst || | ||
| 184 | (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); | ||
| 185 | |||
| 186 | /* Check if we have rstst */ | ||
| 187 | if (!has_rstst) | ||
| 188 | return -ENOTSUPP; | ||
| 189 | |||
| 190 | /* Check if hw reset line is asserted */ | ||
| 191 | v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); | ||
| 192 | if (v & BIT(id)) | ||
| 193 | return 1; | ||
| 194 | |||
| 195 | /* | ||
| 196 | * Check reset status, high value means reset sequence has been | ||
| 197 | * completed successfully so we can return 0 here (reset deasserted) | ||
| 198 | */ | ||
| 199 | v = readl_relaxed(reset->prm->base + reset->prm->data->rstst); | ||
| 200 | v >>= st_bit; | ||
| 201 | v &= 1; | ||
| 202 | |||
| 203 | return !v; | ||
| 204 | } | ||
| 205 | |||
| 206 | static int omap_reset_assert(struct reset_controller_dev *rcdev, | ||
| 207 | unsigned long id) | ||
| 208 | { | ||
| 209 | struct omap_reset_data *reset = to_omap_reset_data(rcdev); | ||
| 210 | u32 v; | ||
| 211 | unsigned long flags; | ||
| 212 | |||
| 213 | /* assert the reset control line */ | ||
| 214 | spin_lock_irqsave(&reset->lock, flags); | ||
| 215 | v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); | ||
| 216 | v |= 1 << id; | ||
| 217 | writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); | ||
| 218 | spin_unlock_irqrestore(&reset->lock, flags); | ||
| 219 | |||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | |||
| 223 | static int omap_reset_deassert(struct reset_controller_dev *rcdev, | ||
| 224 | unsigned long id) | ||
| 225 | { | ||
| 226 | struct omap_reset_data *reset = to_omap_reset_data(rcdev); | ||
| 227 | u32 v; | ||
| 228 | int st_bit; | ||
| 229 | bool has_rstst; | ||
| 230 | unsigned long flags; | ||
| 231 | struct ti_prm_platform_data *pdata = dev_get_platdata(reset->dev); | ||
| 232 | int ret = 0; | ||
| 233 | |||
| 234 | has_rstst = reset->prm->data->rstst || | ||
| 235 | (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); | ||
| 236 | |||
| 237 | if (has_rstst) { | ||
| 238 | st_bit = omap_reset_get_st_bit(reset, id); | ||
| 239 | |||
| 240 | /* Clear the reset status by writing 1 to the status bit */ | ||
| 241 | v = 1 << st_bit; | ||
| 242 | writel_relaxed(v, reset->prm->base + reset->prm->data->rstst); | ||
| 243 | } | ||
| 244 | |||
| 245 | if (reset->clkdm) | ||
| 246 | pdata->clkdm_deny_idle(reset->clkdm); | ||
| 247 | |||
| 248 | /* de-assert the reset control line */ | ||
| 249 | spin_lock_irqsave(&reset->lock, flags); | ||
| 250 | v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); | ||
| 251 | v &= ~(1 << id); | ||
| 252 | writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); | ||
| 253 | spin_unlock_irqrestore(&reset->lock, flags); | ||
| 254 | |||
| 255 | if (!has_rstst) | ||
| 256 | goto exit; | ||
| 257 | |||
| 258 | /* wait for the status to be set */ | ||
| 259 | ret = readl_relaxed_poll_timeout(reset->prm->base + | ||
| 260 | reset->prm->data->rstst, | ||
| 261 | v, v & BIT(st_bit), 1, | ||
| 262 | OMAP_RESET_MAX_WAIT); | ||
| 263 | if (ret) | ||
| 264 | pr_err("%s: timedout waiting for %s:%lu\n", __func__, | ||
| 265 | reset->prm->data->name, id); | ||
| 266 | |||
| 267 | exit: | ||
| 268 | if (reset->clkdm) | ||
| 269 | pdata->clkdm_allow_idle(reset->clkdm); | ||
| 270 | |||
| 271 | return ret; | ||
| 272 | } | ||
| 273 | |||
| 274 | static const struct reset_control_ops omap_reset_ops = { | ||
| 275 | .assert = omap_reset_assert, | ||
| 276 | .deassert = omap_reset_deassert, | ||
| 277 | .status = omap_reset_status, | ||
| 278 | }; | ||
| 279 | |||
| 280 | static int omap_prm_reset_xlate(struct reset_controller_dev *rcdev, | ||
| 281 | const struct of_phandle_args *reset_spec) | ||
| 282 | { | ||
| 283 | struct omap_reset_data *reset = to_omap_reset_data(rcdev); | ||
| 284 | |||
| 285 | if (!_is_valid_reset(reset, reset_spec->args[0])) | ||
| 286 | return -EINVAL; | ||
| 287 | |||
| 288 | return reset_spec->args[0]; | ||
| 289 | } | ||
| 290 | |||
| 291 | static int omap_prm_reset_init(struct platform_device *pdev, | ||
| 292 | struct omap_prm *prm) | ||
| 293 | { | ||
| 294 | struct omap_reset_data *reset; | ||
| 295 | const struct omap_rst_map *map; | ||
| 296 | struct ti_prm_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
| 297 | char buf[32]; | ||
| 298 | |||
| 299 | /* | ||
| 300 | * Check if we have controllable resets. If either rstctrl is non-zero | ||
| 301 | * or OMAP_PRM_HAS_RSTCTRL flag is set, we have reset control register | ||
| 302 | * for the domain. | ||
| 303 | */ | ||
| 304 | if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL)) | ||
| 305 | return 0; | ||
| 306 | |||
| 307 | /* Check if we have the pdata callbacks in place */ | ||
| 308 | if (!pdata || !pdata->clkdm_lookup || !pdata->clkdm_deny_idle || | ||
| 309 | !pdata->clkdm_allow_idle) | ||
| 310 | return -EINVAL; | ||
| 311 | |||
| 312 | map = prm->data->rstmap; | ||
| 313 | if (!map) | ||
| 314 | return -EINVAL; | ||
| 315 | |||
| 316 | reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); | ||
| 317 | if (!reset) | ||
| 318 | return -ENOMEM; | ||
| 319 | |||
| 320 | reset->rcdev.owner = THIS_MODULE; | ||
| 321 | reset->rcdev.ops = &omap_reset_ops; | ||
| 322 | reset->rcdev.of_node = pdev->dev.of_node; | ||
| 323 | reset->rcdev.nr_resets = OMAP_MAX_RESETS; | ||
| 324 | reset->rcdev.of_xlate = omap_prm_reset_xlate; | ||
| 325 | reset->rcdev.of_reset_n_cells = 1; | ||
| 326 | reset->dev = &pdev->dev; | ||
| 327 | spin_lock_init(&reset->lock); | ||
| 328 | |||
| 329 | reset->prm = prm; | ||
| 330 | |||
| 331 | sprintf(buf, "%s_clkdm", prm->data->clkdm_name ? prm->data->clkdm_name : | ||
| 332 | prm->data->name); | ||
| 333 | |||
| 334 | if (!(prm->data->flags & OMAP_PRM_HAS_NO_CLKDM)) { | ||
| 335 | reset->clkdm = pdata->clkdm_lookup(buf); | ||
| 336 | if (!reset->clkdm) | ||
| 337 | return -EINVAL; | ||
| 338 | } | ||
| 339 | |||
| 340 | while (map->rst >= 0) { | ||
| 341 | reset->mask |= BIT(map->rst); | ||
| 342 | map++; | ||
| 343 | } | ||
| 344 | |||
| 345 | return devm_reset_controller_register(&pdev->dev, &reset->rcdev); | ||
| 346 | } | ||
| 347 | |||
| 348 | static int omap_prm_probe(struct platform_device *pdev) | ||
| 349 | { | ||
| 350 | struct resource *res; | ||
| 351 | const struct omap_prm_data *data; | ||
| 352 | struct omap_prm *prm; | ||
| 353 | const struct of_device_id *match; | ||
| 354 | |||
| 355 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 356 | if (!res) | ||
| 357 | return -ENODEV; | ||
| 358 | |||
| 359 | match = of_match_device(omap_prm_id_table, &pdev->dev); | ||
| 360 | if (!match) | ||
| 361 | return -ENOTSUPP; | ||
| 362 | |||
| 363 | prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL); | ||
| 364 | if (!prm) | ||
| 365 | return -ENOMEM; | ||
| 366 | |||
| 367 | data = match->data; | ||
| 368 | |||
| 369 | while (data->base != res->start) { | ||
| 370 | if (!data->base) | ||
| 371 | return -EINVAL; | ||
| 372 | data++; | ||
| 373 | } | ||
| 374 | |||
| 375 | prm->data = data; | ||
| 376 | |||
| 377 | prm->base = devm_ioremap_resource(&pdev->dev, res); | ||
| 378 | if (IS_ERR(prm->base)) | ||
| 379 | return PTR_ERR(prm->base); | ||
| 380 | |||
| 381 | return omap_prm_reset_init(pdev, prm); | ||
| 382 | } | ||
| 383 | |||
| 384 | static struct platform_driver omap_prm_driver = { | ||
| 385 | .probe = omap_prm_probe, | ||
| 386 | .driver = { | ||
| 387 | .name = KBUILD_MODNAME, | ||
| 388 | .of_match_table = omap_prm_id_table, | ||
| 389 | }, | ||
| 390 | }; | ||
| 391 | builtin_platform_driver(omap_prm_driver); | ||
| @@ -2,7 +2,7 @@ · 1 unmodified line | |||
| 2 | /* | 2 | /* |
| 3 | * ZynqMP Generic PM domain support | 3 | * ZynqMP Generic PM domain support |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2015-2018 Xilinx, Inc. | 5 | * Copyright (C) 2015-2019 Xilinx, Inc. |
| 6 | * | 6 | * |
| 7 | * Davorin Mista <davorin.mista@aggios.com> | 7 | * Davorin Mista <davorin.mista@aggios.com> |
| 8 | * Jolly Shah <jollys@xilinx.com> | 8 | * Jolly Shah <jollys@xilinx.com> |
| @@ -25,6 +25,8 @@ · 16 unmodified lines | |||
| 25 | 25 | ||
| 26 | static const struct zynqmp_eemi_ops *eemi_ops; | 26 | static const struct zynqmp_eemi_ops *eemi_ops; |
| 27 | 27 | ||
| 28 | static int min_capability; | ||
| 29 | |||
| 28 | /** | 30 | /** |
| 29 | * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain | 31 | * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain |
| 30 | * @gpd: Generic power domain | 32 | * @gpd: Generic power domain |
| @@ -106,7 +108,7 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) · 75 unmodified lines | |||
| 106 | int ret; | 108 | int ret; |
| 107 | struct pm_domain_data *pdd, *tmp; | 109 | struct pm_domain_data *pdd, *tmp; |
| 108 | struct zynqmp_pm_domain *pd; | 110 | struct zynqmp_pm_domain *pd; |
| 109 | u32 capabilities = 0; | 111 | u32 capabilities = min_capability; |
| 110 | bool may_wakeup; | 112 | bool may_wakeup; |
| 111 | 113 | ||
| 112 | if (!eemi_ops->set_requirement) | 114 | if (!eemi_ops->set_requirement) |
| @@ -283,6 +285,10 @@ static int zynqmp_gpd_probe(struct platform_device *pdev) · 170 unmodified lines | |||
| 283 | if (!domains) | 285 | if (!domains) |
| 284 | return -ENOMEM; | 286 | return -ENOMEM; |
| 285 | 287 | ||
| 288 | if (!of_device_is_compatible(dev->parent->of_node, | ||
| 289 | "xlnx,zynqmp-firmware")) | ||
| 290 | min_capability = ZYNQMP_PM_CAPABILITY_UNUSABLE; | ||
| 291 | |||
| 286 | for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) { | 292 | for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) { |
| 287 | pd->node_id = 0; | 293 | pd->node_id = 0; |
| 288 | pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i); | 294 | pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i); |
| @@ -27,6 +27,14 @@ · 26 unmodified lines | |||
| 27 | #define RPMH_REGULATOR_LEVEL_TURBO 384 | 27 | #define RPMH_REGULATOR_LEVEL_TURBO 384 |
| 28 | #define RPMH_REGULATOR_LEVEL_TURBO_L1 416 | 28 | #define RPMH_REGULATOR_LEVEL_TURBO_L1 416 |
| 29 | 29 | ||
| 30 | /* MSM8976 Power Domain Indexes */ | ||
| 31 | #define MSM8976_VDDCX 0 | ||
| 32 | #define MSM8976_VDDCX_AO 1 | ||
| 33 | #define MSM8976_VDDCX_VFL 2 | ||
| 34 | #define MSM8976_VDDMX 3 | ||
| 35 | #define MSM8976_VDDMX_AO 4 | ||
| 36 | #define MSM8976_VDDMX_VFL 5 | ||
| 37 | |||
| 30 | /* MSM8996 Power Domain Indexes */ | 38 | /* MSM8996 Power Domain Indexes */ |
| 31 | #define MSM8996_VDDCX 0 | 39 | #define MSM8996_VDDCX 0 |
| 32 | #define MSM8996_VDDCX_AO 1 | 40 | #define MSM8996_VDDCX_AO 1 |
| @@ -68,6 +76,7 @@ · 35 unmodified lines | |||
| 68 | #define RPM_SMD_LEVEL_NOM_PLUS 320 | 76 | #define RPM_SMD_LEVEL_NOM_PLUS 320 |
| 69 | #define RPM_SMD_LEVEL_TURBO 384 | 77 | #define RPM_SMD_LEVEL_TURBO 384 |
| 70 | #define RPM_SMD_LEVEL_TURBO_NO_CPR 416 | 78 | #define RPM_SMD_LEVEL_TURBO_NO_CPR 416 |
| 79 | #define RPM_SMD_LEVEL_TURBO_HIGH 448 | ||
| 71 | #define RPM_SMD_LEVEL_BINNING 512 | 80 | #define RPM_SMD_LEVEL_BINNING 512 |
| 72 | 81 | ||
| 73 | #endif | 82 | #endif |
| @@ -0,0 +1,74 @@ | |||
| 1 | /* SPDX-License-Identifier: (GPL-2.0+ OR MIT) | ||
| 2 | * | ||
| 3 | * Copyright (c) 2019 Amlogic, Inc. All rights reserved. | ||
| 4 | * Author: Xingyu Chen <xingyu.chen@amlogic.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef _DT_BINDINGS_AMLOGIC_MESON_A1_RESET_H | ||
| 9 | #define _DT_BINDINGS_AMLOGIC_MESON_A1_RESET_H | ||
| 10 | |||
| 11 | /* RESET0 */ | ||
| 12 | /* 0 */ | ||
| 13 | #define RESET_AM2AXI_VAD 1 | ||
| 14 | /* 2-3 */ | ||
| 15 | #define RESET_PSRAM 4 | ||
| 16 | #define RESET_PAD_CTRL 5 | ||
| 17 | /* 6 */ | ||
| 18 | #define RESET_TEMP_SENSOR 7 | ||
| 19 | #define RESET_AM2AXI_DEV 8 | ||
| 20 | /* 9 */ | ||
| 21 | #define RESET_SPICC_A 10 | ||
| 22 | #define RESET_MSR_CLK 11 | ||
| 23 | #define RESET_AUDIO 12 | ||
| 24 | #define RESET_ANALOG_CTRL 13 | ||
| 25 | #define RESET_SAR_ADC 14 | ||
| 26 | #define RESET_AUDIO_VAD 15 | ||
| 27 | #define RESET_CEC 16 | ||
| 28 | #define RESET_PWM_EF 17 | ||
| 29 | #define RESET_PWM_CD 18 | ||
| 30 | #define RESET_PWM_AB 19 | ||
| 31 | /* 20 */ | ||
| 32 | #define RESET_IR_CTRL 21 | ||
| 33 | #define RESET_I2C_S_A 22 | ||
| 34 | /* 23 */ | ||
| 35 | #define RESET_I2C_M_D 24 | ||
| 36 | #define RESET_I2C_M_C 25 | ||
| 37 | #define RESET_I2C_M_B 26 | ||
| 38 | #define RESET_I2C_M_A 27 | ||
| 39 | #define RESET_I2C_PROD_AHB 28 | ||
| 40 | #define RESET_I2C_PROD 29 | ||
| 41 | /* 30-31 */ | ||
| 42 | |||
| 43 | /* RESET1 */ | ||
| 44 | #define RESET_ACODEC 32 | ||
| 45 | #define RESET_DMA 33 | ||
| 46 | #define RESET_SD_EMMC_A 34 | ||
| 47 | /* 35 */ | ||
| 48 | #define RESET_USBCTRL 36 | ||
| 49 | /* 37 */ | ||
| 50 | #define RESET_USBPHY 38 | ||
| 51 | /* 39-41 */ | ||
| 52 | #define RESET_RSA 42 | ||
| 53 | #define RESET_DMC 43 | ||
| 54 | /* 44 */ | ||
| 55 | #define RESET_IRQ_CTRL 45 | ||
| 56 | /* 46 */ | ||
| 57 | #define RESET_NIC_VAD 47 | ||
| 58 | #define RESET_NIC_AXI 48 | ||
| 59 | #define RESET_RAMA 49 | ||
| 60 | #define RESET_RAMB 50 | ||
| 61 | /* 51-52 */ | ||
| 62 | #define RESET_ROM 53 | ||
| 63 | #define RESET_SPIFC 54 | ||
| 64 | #define RESET_GIC 55 | ||
| 65 | #define RESET_UART_C 56 | ||
| 66 | #define RESET_UART_B 57 | ||
| 67 | #define RESET_UART_A 58 | ||
| 68 | #define RESET_OSC_RING 59 | ||
| 69 | /* 60-63 */ | ||
| 70 | |||
| 71 | /* RESET2 */ | ||
| 72 | /* 64-95 */ | ||
| 73 | |||
| 74 | #endif | ||
| @@ -13,5 +13,7 @@ · 12 unmodified lines | |||
| 13 | #define AXG_ARB_FRDDR_A 3 | 13 | #define AXG_ARB_FRDDR_A 3 |
| 14 | #define AXG_ARB_FRDDR_B 4 | 14 | #define AXG_ARB_FRDDR_B 4 |
| 15 | #define AXG_ARB_FRDDR_C 5 | 15 | #define AXG_ARB_FRDDR_C 5 |
| 16 | #define AXG_ARB_TODDR_D 6 | ||
| 17 | #define AXG_ARB_FRDDR_D 7 | ||
| 16 | 18 | ||
| 17 | #endif /* _DT_BINDINGS_AMLOGIC_MESON_AXG_AUDIO_ARB_H */ | 19 | #endif /* _DT_BINDINGS_AMLOGIC_MESON_AXG_AUDIO_ARB_H */ |
| @@ -16,11 +16,14 @@ enum { · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | struct meson_sm_firmware; | 17 | struct meson_sm_firmware; |
| 18 | 18 | ||
| 19 | int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, u32 arg1, | 19 | int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index, |
| 20 | u32 arg2, u32 arg3, u32 arg4); | 20 | u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); |
| 21 | int meson_sm_call_write(void *buffer, unsigned int b_size, unsigned int cmd_index, | 21 | int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, |
| 22 | u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); | 22 | unsigned int b_size, unsigned int cmd_index, u32 arg0, |
| 23 | int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, | 23 | u32 arg1, u32 arg2, u32 arg3, u32 arg4); |
| 24 | u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); | 24 | int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, |
| 25 | unsigned int bsize, unsigned int cmd_index, u32 arg0, | ||
| 26 | u32 arg1, u32 arg2, u32 arg3, u32 arg4); | ||
| 27 | struct meson_sm_firmware *meson_sm_get(struct device_node *firmware_node); | ||
| 25 | 28 | ||
| 26 | #endif /* _MESON_SM_FW_H_ */ | 29 | #endif /* _MESON_SM_FW_H_ */ |
| @@ -2,7 +2,7 @@ · 1 unmodified line | |||
| 2 | /* | 2 | /* |
| 3 | * Xilinx Zynq MPSoC Firmware layer | 3 | * Xilinx Zynq MPSoC Firmware layer |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2014-2018 Xilinx | 5 | * Copyright (C) 2014-2019 Xilinx |
| 6 | * | 6 | * |
| 7 | * Michal Simek <michal.simek@xilinx.com> | 7 | * Michal Simek <michal.simek@xilinx.com> |
| 8 | * Davorin Mista <davorin.mista@aggios.com> | 8 | * Davorin Mista <davorin.mista@aggios.com> |
| @@ -46,6 +46,7 @@ · 37 unmodified lines | |||
| 46 | #define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U | 46 | #define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U |
| 47 | #define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U | 47 | #define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U |
| 48 | #define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U | 48 | #define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U |
| 49 | #define ZYNQMP_PM_CAPABILITY_UNUSABLE 0x8U | ||
| 49 | 50 | ||
| 50 | /* | 51 | /* |
| 51 | * Firmware FPGA Manager flags | 52 | * Firmware FPGA Manager flags |
| @@ -108,10 +108,10 @@ void logic_outsl(unsigned long addr, const void *buffer, unsigned int count); · 107 unmodified lines | |||
| 108 | * area by redefining the macro below. | 108 | * area by redefining the macro below. |
| 109 | */ | 109 | */ |
| 110 | #define PIO_INDIRECT_SIZE 0x4000 | 110 | #define PIO_INDIRECT_SIZE 0x4000 |
| 111 | #define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE) | ||
| 112 | #else | 111 | #else |
| 113 | #define MMIO_UPPER_LIMIT IO_SPACE_LIMIT | 112 | #define PIO_INDIRECT_SIZE 0 |
| 114 | #endif /* CONFIG_INDIRECT_PIO */ | 113 | #endif /* CONFIG_INDIRECT_PIO */ |
| 114 | #define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE) | ||
| 115 | 115 | ||
| 116 | struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode); | 116 | struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode); |
| 117 | unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, | 117 | unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, |
| @@ -106,7 +106,6 @@ · 105 unmodified lines | |||
| 106 | #define AT91_MATRIX_DDR_IOSR BIT(18) | 106 | #define AT91_MATRIX_DDR_IOSR BIT(18) |
| 107 | #define AT91_MATRIX_NFD0_SELECT BIT(24) | 107 | #define AT91_MATRIX_NFD0_SELECT BIT(24) |
| 108 | #define AT91_MATRIX_DDR_MP_EN BIT(25) | 108 | #define AT91_MATRIX_DDR_MP_EN BIT(25) |
| 109 | #define AT91_MATRIX_EBI_NUM_CS 8 | ||
| 110 | 109 | ||
| 111 | #define AT91_MATRIX_USBPUCR_PUON BIT(30) | 110 | #define AT91_MATRIX_USBPUCR_PUON BIT(30) |
| 112 | 111 | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | ||
| 2 | /* | ||
| 3 | * TI PRM (Power & Reset Manager) platform data | ||
| 4 | * | ||
| 5 | * Copyright (C) 2019 Texas Instruments, Inc. | ||
| 6 | * | ||
| 7 | * Tero Kristo <t-kristo@ti.com> | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef _LINUX_PLATFORM_DATA_TI_PRM_H | ||
| 11 | #define _LINUX_PLATFORM_DATA_TI_PRM_H | ||
| 12 | |||
| 13 | struct clockdomain; | ||
| 14 | |||
| 15 | struct ti_prm_platform_data { | ||
| 16 | void (*clkdm_deny_idle)(struct clockdomain *clkdm); | ||
| 17 | void (*clkdm_allow_idle)(struct clockdomain *clkdm); | ||
| 18 | struct clockdomain * (*clkdm_lookup)(const char *name); | ||
| 19 | }; | ||
| 20 | |||
| 21 | #endif /* _LINUX_PLATFORM_DATA_TI_PRM_H */ | ||
| @@ -63,6 +63,11 @@ struct wakeup_source { · 62 unmodified lines | |||
| 63 | bool autosleep_enabled:1; | 63 | bool autosleep_enabled:1; |
| 64 | }; | 64 | }; |
| 65 | 65 | ||
| 66 | #define for_each_wakeup_source(ws) \ | ||
| 67 | for ((ws) = wakeup_sources_walk_start(); \ | ||
| 68 | (ws); \ | ||
| 69 | (ws) = wakeup_sources_walk_next((ws))) | ||
| 70 | |||
| 66 | #ifdef CONFIG_PM_SLEEP | 71 | #ifdef CONFIG_PM_SLEEP |
| 67 | 72 | ||
| 68 | /* | 73 | /* |
| @@ -92,6 +97,10 @@ extern void wakeup_source_remove(struct wakeup_source *ws); · 23 unmodified lines | |||
| 92 | extern struct wakeup_source *wakeup_source_register(struct device *dev, | 97 | extern struct wakeup_source *wakeup_source_register(struct device *dev, |
| 93 | const char *name); | 98 | const char *name); |
| 94 | extern void wakeup_source_unregister(struct wakeup_source *ws); | 99 | extern void wakeup_source_unregister(struct wakeup_source *ws); |
| 100 | extern int wakeup_sources_read_lock(void); | ||
| 101 | extern void wakeup_sources_read_unlock(int idx); | ||
| 102 | extern struct wakeup_source *wakeup_sources_walk_start(void); | ||
| 103 | extern struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws); | ||
| 95 | extern int device_wakeup_enable(struct device *dev); | 104 | extern int device_wakeup_enable(struct device *dev); |
| 96 | extern int device_wakeup_disable(struct device *dev); | 105 | extern int device_wakeup_disable(struct device *dev); |
| 97 | extern void device_set_wakeup_capable(struct device *dev, bool capable); | 106 | extern void device_set_wakeup_capable(struct device *dev, bool capable); |
| @@ -62,7 +62,8 @@ struct reset_control_lookup { · 61 unmodified lines | |||
| 62 | * @of_node: corresponding device tree node as phandle target | 62 | * @of_node: corresponding device tree node as phandle target |
| 63 | * @of_reset_n_cells: number of cells in reset line specifiers | 63 | * @of_reset_n_cells: number of cells in reset line specifiers |
| 64 | * @of_xlate: translation function to translate from specifier as found in the | 64 | * @of_xlate: translation function to translate from specifier as found in the |
| 65 | * device tree to id as given to the reset control ops | 65 | * device tree to id as given to the reset control ops, defaults |
| 66 | * to :c:func:`of_reset_simple_xlate`. | ||
| 66 | * @nr_resets: number of reset controls in this reset controller device | 67 | * @nr_resets: number of reset controls in this reset controller device |
| 67 | */ | 68 | */ |
| 68 | struct reset_controller_dev { | 69 | struct reset_controller_dev { |
| @@ -203,12 +203,34 @@ static inline struct reset_control *reset_control_get_shared( · 202 unmodified lines | |||
| 203 | return __reset_control_get(dev, id, 0, true, false, false); | 203 | return __reset_control_get(dev, id, 0, true, false, false); |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | /** | ||
| 207 | * reset_control_get_optional_exclusive - optional reset_control_get_exclusive() | ||
| 208 | * @dev: device to be reset by the controller | ||
| 209 | * @id: reset line name | ||
| 210 | * | ||
| 211 | * Optional variant of reset_control_get_exclusive(). If the requested reset | ||
| 212 | * is not specified in the device tree, this function returns NULL instead of | ||
| 213 | * an error. | ||
| 214 | * | ||
| 215 | * See reset_control_get_exclusive() for more information. | ||
| 216 | */ | ||
| 206 | static inline struct reset_control *reset_control_get_optional_exclusive( | 217 | static inline struct reset_control *reset_control_get_optional_exclusive( |
| 207 | struct device *dev, const char *id) | 218 | struct device *dev, const char *id) |
| 208 | { | 219 | { |
| 209 | return __reset_control_get(dev, id, 0, false, true, true); | 220 | return __reset_control_get(dev, id, 0, false, true, true); |
| 210 | } | 221 | } |
| 211 | 222 | ||
| 223 | /** | ||
| 224 | * reset_control_get_optional_shared - optional reset_control_get_shared() | ||
| 225 | * @dev: device to be reset by the controller | ||
| 226 | * @id: reset line name | ||
| 227 | * | ||
| 228 | * Optional variant of reset_control_get_shared(). If the requested reset | ||
| 229 | * is not specified in the device tree, this function returns NULL instead of | ||
| 230 | * an error. | ||
| 231 | * | ||
| 232 | * See reset_control_get_shared() for more information. | ||
| 233 | */ | ||
| 212 | static inline struct reset_control *reset_control_get_optional_shared( | 234 | static inline struct reset_control *reset_control_get_optional_shared( |
| 213 | struct device *dev, const char *id) | 235 | struct device *dev, const char *id) |
| 214 | { | 236 | { |
| @@ -354,12 +376,36 @@ static inline struct reset_control *devm_reset_control_get_shared( · 139 unmodified lines | |||
| 354 | return __devm_reset_control_get(dev, id, 0, true, false, false); | 376 | return __devm_reset_control_get(dev, id, 0, true, false, false); |
| 355 | } | 377 | } |
| 356 | 378 | ||
| 379 | /** | ||
| 380 | * devm_reset_control_get_optional_exclusive - resource managed | ||
| 381 | * reset_control_get_optional_exclusive() | ||
| 382 | * @dev: device to be reset by the controller | ||
| 383 | * @id: reset line name | ||
| 384 | * | ||
| 385 | * Managed reset_control_get_optional_exclusive(). For reset controllers | ||
| 386 | * returned from this function, reset_control_put() is called automatically on | ||
| 387 | * driver detach. | ||
| 388 | * | ||
| 389 | * See reset_control_get_optional_exclusive() for more information. | ||
| 390 | */ | ||
| 357 | static inline struct reset_control *devm_reset_control_get_optional_exclusive( | 391 | static inline struct reset_control *devm_reset_control_get_optional_exclusive( |
| 358 | struct device *dev, const char *id) | 392 | struct device *dev, const char *id) |
| 359 | { | 393 | { |
| 360 | return __devm_reset_control_get(dev, id, 0, false, true, true); | 394 | return __devm_reset_control_get(dev, id, 0, false, true, true); |
| 361 | } | 395 | } |
| 362 | 396 | ||
| 397 | /** | ||
| 398 | * devm_reset_control_get_optional_shared - resource managed | ||
| 399 | * reset_control_get_optional_shared() | ||
| 400 | * @dev: device to be reset by the controller | ||
| 401 | * @id: reset line name | ||
| 402 | * | ||
| 403 | * Managed reset_control_get_optional_shared(). For reset controllers returned | ||
| 404 | * from this function, reset_control_put() is called automatically on driver | ||
| 405 | * detach. | ||
| 406 | * | ||
| 407 | * See reset_control_get_optional_shared() for more information. | ||
| 408 | */ | ||
| 363 | static inline struct reset_control *devm_reset_control_get_optional_shared( | 409 | static inline struct reset_control *devm_reset_control_get_optional_shared( |
| 364 | struct device *dev, const char *id) | 410 | struct device *dev, const char *id) |
| 365 | { | 411 | { |
| @@ -2,7 +2,9 @@ · 1 unmodified line | |||
| 2 | #ifndef __ASM_MACH_CPUTYPE_H | 2 | #ifndef __ASM_MACH_CPUTYPE_H |
| 3 | #define __ASM_MACH_CPUTYPE_H | 3 | #define __ASM_MACH_CPUTYPE_H |
| 4 | 4 | ||
| 5 | #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) | ||
| 5 | #include <asm/cputype.h> | 6 | #include <asm/cputype.h> |
| 7 | #endif | ||
| 6 | 8 | ||
| 7 | /* | 9 | /* |
| 8 | * CPU Stepping CPU_ID CHIP_ID | 10 | * CPU Stepping CPU_ID CHIP_ID |
| @@ -38,33 +38,27 @@ struct llcc_slice_desc { · 37 unmodified lines | |||
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | * llcc_slice_config - Data associated with the llcc slice | 41 | * llcc_edac_reg_data - llcc edac registers data for each error type |
| 42 | * @usecase_id: usecase id for which the llcc slice is used | 42 | * @name: Name of the error |
| 43 | * @slice_id: llcc slice id assigned to each slice | 43 | * @synd_reg: Syndrome register address |
| 44 | * @max_cap: maximum capacity of the llcc slice | 44 | * @count_status_reg: Status register address to read the error count |
| 45 | * @priority: priority of the llcc slice | 45 | * @ways_status_reg: Status register address to read the error ways |
| 46 | * @fixed_size: whether the llcc slice can grow beyond its size | 46 | * @reg_cnt: Number of registers |
| 47 | * @bonus_ways: bonus ways associated with llcc slice | 47 | * @count_mask: Mask value to get the error count |
| 48 | * @res_ways: reserved ways associated with llcc slice | 48 | * @ways_mask: Mask value to get the error ways |
| 49 | * @cache_mode: mode of the llcc slice | 49 | * @count_shift: Shift value to get the error count |
| 50 | * @probe_target_ways: Probe only reserved and bonus ways on a cache miss | 50 | * @ways_shift: Shift value to get the error ways |
| 51 | * @dis_cap_alloc: Disable capacity based allocation | ||
| 52 | * @retain_on_pc: Retain through power collapse | ||
| 53 | * @activate_on_init: activate the slice on init | ||
| 54 | */ | 51 | */ |
| 55 | struct llcc_slice_config { | 52 | struct llcc_edac_reg_data { |
| 56 | u32 usecase_id; | 53 | char *name; |
| 57 | u32 slice_id; | 54 | u64 synd_reg; |
| 58 | u32 max_cap; | 55 | u64 count_status_reg; |
| 59 | u32 priority; | 56 | u64 ways_status_reg; |
| 60 | bool fixed_size; | 57 | u32 reg_cnt; |
| 61 | u32 bonus_ways; | 58 | u32 count_mask; |
| 62 | u32 res_ways; | 59 | u32 ways_mask; |
| 63 | u32 cache_mode; | 60 | u8 count_shift; |
| 64 | u32 probe_target_ways; | 61 | u8 ways_shift; |
| 65 | bool dis_cap_alloc; | ||
| 66 | bool retain_on_pc; | ||
| 67 | bool activate_on_init; | ||
| 68 | }; | 62 | }; |
| 69 | 63 | ||
| 70 | /** | 64 | /** |
| @@ -93,30 +87,6 @@ struct llcc_drv_data { · 22 unmodified lines | |||
| 93 | int ecc_irq; | 87 | int ecc_irq; |
| 94 | }; | 88 | }; |
| 95 | 89 | ||
| 96 | /** | ||
| 97 | * llcc_edac_reg_data - llcc edac registers data for each error type | ||
| 98 | * @name: Name of the error | ||
| 99 | * @synd_reg: Syndrome register address | ||
| 100 | * @count_status_reg: Status register address to read the error count | ||
| 101 | * @ways_status_reg: Status register address to read the error ways | ||
| 102 | * @reg_cnt: Number of registers | ||
| 103 | * @count_mask: Mask value to get the error count | ||
| 104 | * @ways_mask: Mask value to get the error ways | ||
| 105 | * @count_shift: Shift value to get the error count | ||
| 106 | * @ways_shift: Shift value to get the error ways | ||
| 107 | */ | ||
| 108 | struct llcc_edac_reg_data { | ||
| 109 | char *name; | ||
| 110 | u64 synd_reg; | ||
| 111 | u64 count_status_reg; | ||
| 112 | u64 ways_status_reg; | ||
| 113 | u32 reg_cnt; | ||
| 114 | u32 count_mask; | ||
| 115 | u32 ways_mask; | ||
| 116 | u8 count_shift; | ||
| 117 | u8 ways_shift; | ||
| 118 | }; | ||
| 119 | |||
| 120 | #if IS_ENABLED(CONFIG_QCOM_LLCC) | 90 | #if IS_ENABLED(CONFIG_QCOM_LLCC) |
| 121 | /** | 91 | /** |
| 122 | * llcc_slice_getd - get llcc slice descriptor | 92 | * llcc_slice_getd - get llcc slice descriptor |
| @@ -154,20 +124,6 @@ int llcc_slice_activate(struct llcc_slice_desc *desc); · 31 unmodified lines | |||
| 154 | */ | 124 | */ |
| 155 | int llcc_slice_deactivate(struct llcc_slice_desc *desc); | 125 | int llcc_slice_deactivate(struct llcc_slice_desc *desc); |
| 156 | 126 | ||
| 157 | /** | ||
| 158 | * qcom_llcc_probe - program the sct table | ||
| 159 | * @pdev: platform device pointer | ||
| 160 | * @table: soc sct table | ||
| 161 | * @sz: Size of the config table | ||
| 162 | */ | ||
| 163 | int qcom_llcc_probe(struct platform_device *pdev, | ||
| 164 | const struct llcc_slice_config *table, u32 sz); | ||
| 165 | |||
| 166 | /** | ||
| 167 | * qcom_llcc_remove - remove the sct table | ||
| 168 | * @pdev: Platform device pointer | ||
| 169 | */ | ||
| 170 | int qcom_llcc_remove(struct platform_device *pdev); | ||
| 171 | #else | 127 | #else |
| 172 | static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) | 128 | static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) |
| 173 | { | 129 | { |
| @@ -197,16 +153,6 @@ static inline int llcc_slice_deactivate(struct llcc_slice_desc *desc) · 23 unmodified lines | |||
| 197 | { | 153 | { |
| 198 | return -EINVAL; | 154 | return -EINVAL; |
| 199 | } | 155 | } |
| 200 | static inline int qcom_llcc_probe(struct platform_device *pdev, | ||
| 201 | const struct llcc_slice_config *table, u32 sz) | ||
| 202 | { | ||
| 203 | return -ENODEV; | ||
| 204 | } | ||
| 205 | |||
| 206 | static inline int qcom_llcc_remove(struct platform_device *pdev) | ||
| 207 | { | ||
| 208 | return -ENODEV; | ||
| 209 | } | ||
| 210 | #endif | 156 | #endif |
| 211 | 157 | ||
| 212 | #endif | 158 | #endif |
| @@ -181,7 +181,7 @@ struct tegra_mc { · 180 unmodified lines | |||
| 181 | spinlock_t lock; | 181 | spinlock_t lock; |
| 182 | }; | 182 | }; |
| 183 | 183 | ||
| 184 | void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate); | 184 | int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate); |
| 185 | unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc); | 185 | unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc); |
| 186 | 186 | ||
| 187 | #endif /* __SOC_TEGRA_MC_H__ */ | 187 | #endif /* __SOC_TEGRA_MC_H__ */ |
| @@ -109,7 +109,7 @@ obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o · 108 unmodified lines | |||
| 109 | obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o | 109 | obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o |
| 110 | obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o | 110 | obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o |
| 111 | 111 | ||
| 112 | obj-y += logic_pio.o | 112 | lib-y += logic_pio.o |
| 113 | 113 | ||
| 114 | obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o | 114 | obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o |
| 115 | 115 | ||
| @@ -3,6 +3,7 @@ · 2 unmodified lines | |||
| 3 | * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. | 3 | * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. |
| 4 | * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com> | 4 | * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com> |
| 5 | * Author: Zhichang Yuan <yuanzhichang@hisilicon.com> | 5 | * Author: Zhichang Yuan <yuanzhichang@hisilicon.com> |
| 6 | * Author: John Garry <john.garry@huawei.com> | ||
| 6 | */ | 7 | */ |
| 7 | 8 | ||
| 8 | #define pr_fmt(fmt) "LOGIC PIO: " fmt | 9 | #define pr_fmt(fmt) "LOGIC PIO: " fmt |
| @@ -39,7 +40,8 @@ int logic_pio_register_range(struct logic_pio_hwaddr *new_range) · 30 unmodified lines | |||
| 39 | resource_size_t iio_sz = MMIO_UPPER_LIMIT; | 40 | resource_size_t iio_sz = MMIO_UPPER_LIMIT; |
| 40 | int ret = 0; | 41 | int ret = 0; |
| 41 | 42 | ||
| 42 | if (!new_range || !new_range->fwnode || !new_range->size) | 43 | if (!new_range || !new_range->fwnode || !new_range->size || |
| 44 | (new_range->flags == LOGIC_PIO_INDIRECT && !new_range->ops)) | ||
| 43 | return -EINVAL; | 45 | return -EINVAL; |
| 44 | 46 | ||
| 45 | start = new_range->hw_start; | 47 | start = new_range->hw_start; |
| @@ -237,7 +239,7 @@ type logic_in##bw(unsigned long addr) \ · 191 unmodified lines | |||
| 237 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ | 239 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ |
| 238 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ | 240 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ |
| 239 | \ | 241 | \ |
| 240 | if (entry && entry->ops) \ | 242 | if (entry) \ |
| 241 | ret = entry->ops->in(entry->hostdata, \ | 243 | ret = entry->ops->in(entry->hostdata, \ |
| 242 | addr, sizeof(type)); \ | 244 | addr, sizeof(type)); \ |
| 243 | else \ | 245 | else \ |
| @@ -253,7 +255,7 @@ void logic_out##bw(type value, unsigned long addr) \ · 9 unmodified lines | |||
| 253 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ | 255 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ |
| 254 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ | 256 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ |
| 255 | \ | 257 | \ |
| 256 | if (entry && entry->ops) \ | 258 | if (entry) \ |
| 257 | entry->ops->out(entry->hostdata, \ | 259 | entry->ops->out(entry->hostdata, \ |
| 258 | addr, value, sizeof(type)); \ | 260 | addr, value, sizeof(type)); \ |
| 259 | else \ | 261 | else \ |
| @@ -261,7 +263,7 @@ void logic_out##bw(type value, unsigned long addr) \ · 1 unmodified line | |||
| 261 | } \ | 263 | } \ |
| 262 | } \ | 264 | } \ |
| 263 | \ | 265 | \ |
| 264 | void logic_ins##bw(unsigned long addr, void *buffer, \ | 266 | void logic_ins##bw(unsigned long addr, void *buffer, \ |
| 265 | unsigned int count) \ | 267 | unsigned int count) \ |
| 266 | { \ | 268 | { \ |
| 267 | if (addr < MMIO_UPPER_LIMIT) { \ | 269 | if (addr < MMIO_UPPER_LIMIT) { \ |
| @@ -269,7 +271,7 @@ void logic_ins##bw(unsigned long addr, void *buffer, \ · 1 unmodified line | |||
| 269 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ | 271 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ |
| 270 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ | 272 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ |
| 271 | \ | 273 | \ |
| 272 | if (entry && entry->ops) \ | 274 | if (entry) \ |
| 273 | entry->ops->ins(entry->hostdata, \ | 275 | entry->ops->ins(entry->hostdata, \ |
| 274 | addr, buffer, sizeof(type), count); \ | 276 | addr, buffer, sizeof(type), count); \ |
| 275 | else \ | 277 | else \ |
| @@ -286,7 +288,7 @@ void logic_outs##bw(unsigned long addr, const void *buffer, \ · 10 unmodified lines | |||
| 286 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ | 288 | } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ |
| 287 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ | 289 | struct logic_pio_hwaddr *entry = find_io_range(addr); \ |
| 288 | \ | 290 | \ |
| 289 | if (entry && entry->ops) \ | 291 | if (entry) \ |
| 290 | entry->ops->outs(entry->hostdata, \ | 292 | entry->ops->outs(entry->hostdata, \ |
| 291 | addr, buffer, sizeof(type), count); \ | 293 | addr, buffer, sizeof(type), count); \ |
| 292 | else \ | 294 | else \ |