1
0
Fork 0

ARM: SoC-related driver updates

Various driver updates for platforms:
 
  - A larger set of work on Tegra 2/3 around memory controller and
  regulator features, some fuse cleanups, etc..
 
  - MMP platform drivers, in particular for USB PHY, and other smaller
  additions.
 
  - Samsung Exynos 5422 driver for DMC (dynamic memory configuration),
  and ASV (adaptive voltage), allowing the platform to run at more
  optimal operating points.
 
  - Misc refactorings and support for RZ/G2N and R8A774B1 from Renesas
 
  - Clock/reset control driver for TI/OMAP
 
  - Meson-A1 reset controller support
 
  - Qualcomm sdm845 and sda845 SoC IDs for socinfo
 -----BEGIN PGP SIGNATURE-----
 
 iQJDBAABCAAtFiEElf+HevZ4QCAJmMQ+jBrnPN6EHHcFAl3pORkPHG9sb2ZAbGl4
 b20ubmV0AAoJEIwa5zzehBx3FK0P/0EG4lK+il7nE3pd9yIGUjlcYuumIjoxvyC9
 9ef202POJLIO3yMlsNyGFR+aOknFO/GtGvDkDFhTtlsGCL40tVzVsyo7ZQo+8mXD
 abr+H74NmRXImc+SISYR8X1CD6vEi3oi/no1y5dRzknlBikfsdSLKXJSMYBJ2A6t
 DNLwu0h1IZhPk7XQQsxaElG/a9HN8eueMdP20J1IlhOh0GiOwm+rbsLSZNbA/W9m
 53XhFs3Ag39SDE0BfXsS+XOWTE7FheZsZk2XQrOwYm9PnxjpIWH7FE2sYsk6uUIc
 Pa1b6wB5zlRnxvVHP0m3GXhbTUJDYDK3oybHffI4Mzd0cyZQHC92LhUXFrlTxkaf
 6kyhJOTdd5KMlZ2LS7jkwLqb30ieXBPKAREjdbRt6hpvu5P6G+bZQphTEeNAZC61
 XnX8mQ/XeoHdoGY5MvS8ht6a1qDF29ebA0/02seicThGK6tS9Qsju6Zo0sg9H1NH
 weK6jDuzLq5jpv/LB1apigrDSx+zddRzrwkwy85hR5aWOQhG0xjOoFBProbTS0to
 wR46zCEkbGZv4uc0gRuIdp0NR/lguqgDWPeoLluoTqmcpKS6N3RyxD0bWzlvgDFA
 fpYxVNKavHneWjfZ7U5RbYXD6jycJcuLaCOs16nrtUbMgJ9pqclLIaZXn7ZTRIuT
 RW6NgfZV
 =dk7w
 -----END PGP SIGNATURE-----

Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc

Pull ARM SoC driver updates from Olof Johansson:
 "Various driver updates for platforms:

   - A larger set of work on Tegra 2/3 around memory controller and
     regulator features, some fuse cleanups, etc..

   - MMP platform drivers, in particular for USB PHY, and other smaller
     additions.

   - Samsung Exynos 5422 driver for DMC (dynamic memory configuration),
     and ASV (adaptive voltage), allowing the platform to run at more
     optimal operating points.

   - Misc refactorings and support for RZ/G2N and R8A774B1 from Renesas

   - Clock/reset control driver for TI/OMAP

   - Meson-A1 reset controller support

   - Qualcomm sdm845 and sda845 SoC IDs for socinfo"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (150 commits)
  firmware: arm_scmi: Fix doorbell ring logic for !CONFIG_64BIT
  soc: fsl: add RCPM driver
  dt-bindings: fsl: rcpm: Add 'little-endian' and update Chassis definition
  memory: tegra: Consolidate registers definition into common header
  memory: tegra: Ensure timing control debug features are disabled
  memory: tegra: Introduce Tegra30 EMC driver
  memory: tegra: Do not handle error from wait_for_completion_timeout()
  memory: tegra: Increase handshake timeout on Tegra20
  memory: tegra: Print a brief info message about EMC timings
  memory: tegra: Pre-configure debug register on Tegra20
  memory: tegra: Include io.h instead of iopoll.h
  memory: tegra: Adapt for Tegra20 clock driver changes
  memory: tegra: Don't set EMC rate to maximum on probe for Tegra20
  memory: tegra: Add gr2d and gr3d to DRM IOMMU group
  memory: tegra: Set DMA mask based on supported address bits
  soc: at91: Add Atmel SFR SN (Serial Number) support
  memory: atmel-ebi: switch to SPDX license identifiers
  memory: atmel-ebi: move NUM_CS definition inside EBI driver
  soc: mediatek: Refactor bus protection control
  soc: mediatek: Refactor sram control
  ...
alistair/sunxi64-5.5-dsi
Linus Torvalds 2019-12-05 11:43:31 -08:00
commit ec939e4c94
133 changed files with 7617 additions and 945 deletions

View File

@ -103,7 +103,7 @@ the Microchip website: http://www.microchip.com.
* Datasheet
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11121-32-bit-Cortex-A5-Microcontroller-SAMA5D3_Datasheet.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11121-32-bit-Cortex-A5-Microcontroller-SAMA5D3_Datasheet_B.pdf
* ARM Cortex-A5 + NEON based SoCs
- sama5d4 family
@ -167,7 +167,7 @@ the Microchip website: http://www.microchip.com.
* Datasheet
http://ww1.microchip.com/downloads/en/DeviceDoc/60001527A.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/SAM-E70-S70-V70-V71-Family-Data-Sheet-DS60001527D.pdf
Linux kernel information

View File

@ -1,41 +0,0 @@
== Introduction==
LLCC (Last Level Cache Controller) provides last level of cache memory in SOC,
that can be shared by multiple clients. Clients here are different cores in the
SOC, the idea is to minimize the local caches at the clients and migrate to
common pool of memory. Cache memory is divided into partitions called slices
which are assigned to clients. Clients can query the slice details, activate
and deactivate them.
Properties:
- compatible:
Usage: required
Value type: <string>
Definition: must be "qcom,sdm845-llcc"
- reg:
Usage: required
Value Type: <prop-encoded-array>
Definition: The first element specifies the llcc base start address and
the size of the register region. The second element specifies
the llcc broadcast base address and size of the register region.
- reg-names:
Usage: required
Value Type: <stringlist>
Definition: Register region names. Must be "llcc_base", "llcc_broadcast_base".
- interrupts:
Usage: required
Definition: The interrupt is associated with the llcc edac device.
It's used for llcc cache single and double bit error detection
and reporting.
Example:
cache-controller@1100000 {
compatible = "qcom,sdm845-llcc";
reg = <0x1100000 0x200000>, <0x1300000 0x50000> ;
reg-names = "llcc_base", "llcc_broadcast_base";
interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/msm/qcom,llcc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Last Level Cache Controller
maintainers:
- Rishabh Bhatnagar <rishabhb@codeaurora.org>
- Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
description: |
LLCC (Last Level Cache Controller) provides last level of cache memory in SoC,
that can be shared by multiple clients. Clients here are different cores in the
SoC, the idea is to minimize the local caches at the clients and migrate to
common pool of memory. Cache memory is divided into partitions called slices
which are assigned to clients. Clients can query the slice details, activate
and deactivate them.
properties:
compatible:
enum:
- qcom,sc7180-llcc
- qcom,sdm845-llcc
reg:
items:
- description: LLCC base register region
- description: LLCC broadcast base register region
reg-names:
items:
- const: llcc_base
- const: llcc_broadcast_base
interrupts:
maxItems: 1
required:
- compatible
- reg
- reg-names
- interrupts
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
cache-controller@1100000 {
compatible = "qcom,sdm845-llcc";
reg = <0x1100000 0x200000>, <0x1300000 0x50000> ;
reg-names = "llcc_base", "llcc_broadcast_base";
interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@ -0,0 +1,29 @@
OMAP PRM instance bindings
Power and Reset Manager is an IP block on OMAP family of devices which
handle the power domains and their current state, and provide reset
handling for the domains and/or separate IP blocks under the power domain
hierarchy.
Required properties:
- compatible: Must contain one of the following:
"ti,am3-prm-inst"
"ti,am4-prm-inst"
"ti,omap4-prm-inst"
"ti,omap5-prm-inst"
"ti,dra7-prm-inst"
and additionally must contain:
"ti,omap-prm-inst"
- reg: Contains PRM instance register address range
(base address and length)
Optional properties:
- #reset-cells: Should be 1 if the PRM instance in question supports resets.
Example:
prm_dsp2: prm@1b00 {
compatible = "ti,dra7-prm-inst", "ti,omap-prm-inst";
reg = <0x1b00 0x40>;
#reset-cells = <1>;
};

View File

@ -11,7 +11,9 @@ power management service, FPGA service and other platform management
services.
Required properties:
- compatible: Must contain: "xlnx,zynqmp-firmware"
- compatible: Must contain any of below:
"xlnx,zynqmp-firmware" for Zynq Ultrascale+ MPSoC
"xlnx,versal-firmware" for Versal
- method: The method of calling the PM-API firmware layer.
Permitted values are:
- "smc" : SMC #0, following the SMCCC
@ -21,6 +23,8 @@ Required properties:
Example
-------
Zynq Ultrascale+ MPSoC
----------------------
firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
@ -28,3 +32,13 @@ firmware {
...
};
};
Versal
------
firmware {
versal_firmware: versal-firmware {
compatible = "xlnx,versal-firmware";
method = "smc";
...
};
};

View File

@ -4,6 +4,7 @@ Required properties:
- compatible: should be "amlogic,meson-gxbb-efuse"
- clocks: phandle to the efuse peripheral clock provided by the
clock controller.
- secure-monitor: phandle to the secure-monitor node
= Data cells =
Are child nodes of eFuse, bindings of which as described in
@ -16,6 +17,7 @@ Example:
clocks = <&clkc CLKID_EFUSE>;
#address-cells = <1>;
#size-cells = <1>;
secure-monitor = <&sm>;
sn: sn@14 {
reg = <0x14 0x10>;
@ -30,6 +32,10 @@ Example:
};
};
sm: secure-monitor {
compatible = "amlogic,meson-gxbb-sm";
};
= Data consumers =
Are device nodes which consume nvmem data cells.

View File

@ -5,6 +5,7 @@ which then translates it into a corresponding voltage on a rail
Required Properties:
- compatible: Should be one of the following
* qcom,msm8976-rpmpd: RPM Power domain for the msm8976 family of SoC
* qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC
* qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC
* qcom,qcs404-rpmpd: RPM Power domain for the qcs404 family of SoC

View File

@ -4,7 +4,8 @@ The Amlogic Audio ARB is a simple device which enables or
disables the access of Audio FIFOs to DDR on AXG based SoC.
Required properties:
- compatible: 'amlogic,meson-axg-audio-arb'
- compatible: 'amlogic,meson-axg-audio-arb' or
'amlogic,meson-sm1-audio-arb'
- reg: physical base address of the controller and length of memory
mapped region.
- clocks: phandle to the fifo peripheral clock provided by the audio

View File

@ -16,6 +16,7 @@ properties:
- amlogic,meson8b-reset # Reset Controller on Meson8b and compatible SoCs
- amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs
- amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs
- amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs
reg:
maxItems: 1

View File

@ -1,52 +0,0 @@
Qualcomm AOSS Reset Controller
======================================
This binding describes a reset-controller found on AOSS-CC (always on subsystem)
for Qualcomm SDM845 SoCs.
Required properties:
- compatible:
Usage: required
Value type: <string>
Definition: must be:
"qcom,sdm845-aoss-cc"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: must specify the base address and size of the register
space.
- #reset-cells:
Usage: required
Value type: <uint>
Definition: must be 1; cell entry represents the reset index.
Example:
aoss_reset: reset-controller@c2a0000 {
compatible = "qcom,sdm845-aoss-cc";
reg = <0xc2a0000 0x31000>;
#reset-cells = <1>;
};
Specifying reset lines connected to IP modules
==============================================
Device nodes that need access to reset lines should
specify them as a reset phandle in their corresponding node as
specified in reset.txt.
For list of all valid reset indicies see
<dt-bindings/reset/qcom,sdm845-aoss.h>
Example:
modem-pil@4080000 {
...
resets = <&aoss_reset AOSS_CC_MSS_RESTART>;
reset-names = "mss_restart";
...
};

View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/reset/qcom,aoss-reset.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm AOSS Reset Controller
maintainers:
- Sibi Sankar <sibis@codeaurora.org>
description:
The bindings describe the reset-controller found on AOSS-CC (always on
subsystem) for Qualcomm Technologies Inc SoCs.
properties:
compatible:
oneOf:
- description: on SC7180 SoCs the following compatibles must be specified
items:
- const: "qcom,sc7180-aoss-cc"
- const: "qcom,sdm845-aoss-cc"
- description: on SDM845 SoCs the following compatibles must be specified
items:
- const: "qcom,sdm845-aoss-cc"
reg:
maxItems: 1
'#reset-cells':
const: 1
required:
- compatible
- reg
- '#reset-cells'
additionalProperties: false
examples:
- |
aoss_reset: reset-controller@c2a0000 {
compatible = "qcom,sdm845-aoss-cc";
reg = <0xc2a0000 0x31000>;
#reset-cells = <1>;
};

View File

@ -1,52 +0,0 @@
PDC Global
======================================
This binding describes a reset-controller found on PDC-Global (Power Domain
Controller) block for Qualcomm Technologies Inc SDM845 SoCs.
Required properties:
- compatible:
Usage: required
Value type: <string>
Definition: must be:
"qcom,sdm845-pdc-global"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: must specify the base address and size of the register
space.
- #reset-cells:
Usage: required
Value type: <uint>
Definition: must be 1; cell entry represents the reset index.
Example:
pdc_reset: reset-controller@b2e0000 {
compatible = "qcom,sdm845-pdc-global";
reg = <0xb2e0000 0x20000>;
#reset-cells = <1>;
};
PDC reset clients
======================================
Device nodes that need access to reset lines should
specify them as a reset phandle in their corresponding node as
specified in reset.txt.
For a list of all valid reset indices see
<dt-bindings/reset/qcom,sdm845-pdc.h>
Example:
modem-pil@4080000 {
...
resets = <&pdc_reset PDC_MODEM_SYNC_RESET>;
reset-names = "pdc_reset";
...
};

View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/reset/qcom,pdc-global.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm PDC Global
maintainers:
- Sibi Sankar <sibis@codeaurora.org>
description:
The bindings describes the reset-controller found on PDC-Global (Power Domain
Controller) block for Qualcomm Technologies Inc SoCs.
properties:
compatible:
oneOf:
- description: on SC7180 SoCs the following compatibles must be specified
items:
- const: "qcom,sc7180-pdc-global"
- const: "qcom,sdm845-pdc-global"
- description: on SDM845 SoCs the following compatibles must be specified
items:
- const: "qcom,sdm845-pdc-global"
reg:
maxItems: 1
'#reset-cells':
const: 1
required:
- compatible
- reg
- '#reset-cells'
additionalProperties: false
examples:
- |
pdc_reset: reset-controller@b2e0000 {
compatible = "qcom,sdm845-pdc-global";
reg = <0xb2e0000 0x20000>;
#reset-cells = <1>;
};

View File

@ -130,6 +130,7 @@ this layer. These clocks and resets should be described in each property.
Required properties:
- compatible: Should be
"socionext,uniphier-pro4-usb3-reset" - for Pro4 SoC USB3
"socionext,uniphier-pro5-usb3-reset" - for Pro5 SoC USB3
"socionext,uniphier-pxs2-usb3-reset" - for PXs2 SoC USB3
"socionext,uniphier-ld20-usb3-reset" - for LD20 SoC USB3
"socionext,uniphier-pxs3-usb3-reset" - for PXs3 SoC USB3
@ -141,12 +142,12 @@ Required properties:
- clocks: A list of phandles to the clock gate for the glue layer.
According to the clock-names, appropriate clocks are required.
- clock-names: Should contain
"gio", "link" - for Pro4 SoC
"gio", "link" - for Pro4 and Pro5 SoCs
"link" - for others
- resets: A list of phandles to the reset control for the glue layer.
According to the reset-names, appropriate resets are required.
- reset-names: Should contain
"gio", "link" - for Pro4 SoC
"gio", "link" - for Pro4 and Pro5 SoCs
"link" - for others
Example:

View File

@ -5,7 +5,7 @@ and power management.
Required properites:
- reg : Offset and length of the register set of the RCPM block.
- fsl,#rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the
- #fsl,rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the
fsl,rcpm-wakeup property.
- compatible : Must contain a chip-specific RCPM block compatible string
and (if applicable) may contain a chassis-version RCPM compatible
@ -20,6 +20,7 @@ Required properites:
* "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm
* "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm
* "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm
* "fsl,qoriq-rcpm-2.1+": for chassis 2.1+ rcpm
All references to "1.0" and "2.0" refer to the QorIQ chassis version to
which the chip complies.
@ -27,14 +28,19 @@ Chassis Version Example Chips
--------------- -------------------------------
1.0 p4080, p5020, p5040, p2041, p3041
2.0 t4240, b4860, b4420
2.1 t1040, ls1021
2.1 t1040,
2.1+ ls1021a, ls1012a, ls1043a, ls1046a
Optional properties:
- little-endian : RCPM register block is Little Endian. Without it RCPM
will be Big Endian (default case).
Example:
The RCPM node for T4240:
rcpm: global-utilities@e2000 {
compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0";
reg = <0xe2000 0x1000>;
fsl,#rcpm-wakeup-cells = <2>;
#fsl,rcpm-wakeup-cells = <2>;
};
* Freescale RCPM Wakeup Source Device Tree Bindings
@ -44,7 +50,7 @@ can be used as a wakeup source.
- fsl,rcpm-wakeup: Consists of a phandle to the rcpm node and the IPPDEXPCR
register cells. The number of IPPDEXPCR register cells is defined in
"fsl,#rcpm-wakeup-cells" in the rcpm node. The first register cell is
"#fsl,rcpm-wakeup-cells" in the rcpm node. The first register cell is
the bit mask that should be set in IPPDEXPCR0, and the second register
cell is for IPPDEXPCR1, and so on.

View File

@ -22,6 +22,7 @@ resources.
"qcom,rpm-apq8084"
"qcom,rpm-msm8916"
"qcom,rpm-msm8974"
"qcom,rpm-msm8976"
"qcom,rpm-msm8998"
"qcom,rpm-sdm660"
"qcom,rpm-qcs404"

View File

@ -2140,6 +2140,7 @@ S: Maintained
ARM/QUALCOMM SUPPORT
M: Andy Gross <agross@kernel.org>
M: Bjorn Andersson <bjorn.andersson@linaro.org>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/soc/qcom/
@ -5001,6 +5002,14 @@ F: include/linux/dma-direct.h
F: include/linux/dma-mapping.h
F: include/linux/dma-noncoherent.h
DMC FREQUENCY DRIVER FOR SAMSUNG EXYNOS5422
M: Lukasz Luba <l.luba@partner.samsung.com>
L: linux-pm@vger.kernel.org
L: linux-samsung-soc@vger.kernel.org
S: Maintained
F: drivers/memory/samsung/exynos5422-dmc.c
F: Documentation/devicetree/bindings/memory-controllers/exynos5422-dmc.txt
DME1737 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
L: linux-hwmon@vger.kernel.org
@ -11066,6 +11075,13 @@ F: arch/arm/boot/dts/mmp*
F: arch/arm/mach-mmp/
F: linux/soc/mmp/
MMP USB PHY DRIVERS
R: Lubomir Rintel <lkundrak@v3.sk>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: drivers/phy/marvell/phy-mmp3-usb.c
F: drivers/phy/marvell/phy-pxa-usb.c
MMU GATHER AND TLB INVALIDATION
M: Will Deacon <will@kernel.org>
M: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
@ -14033,6 +14049,7 @@ F: include/dt-bindings/reset/
F: include/linux/reset.h
F: include/linux/reset/
F: include/linux/reset-controller.h
K: \b(?:devm_|of_)?reset_control(?:ler_[a-z]+|_[a-z_]+)?\b
RESTARTABLE SEQUENCES SUPPORT
M: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

View File

@ -109,6 +109,7 @@ config ARCH_OMAP2PLUS
select TI_SYSC
select OMAP_IRQCHIP
select CLKSRC_TI_32K
select ARCH_HAS_RESET_CONTROLLER
help
Systems based on OMAP2, OMAP3, OMAP4 or OMAP5

View File

@ -247,6 +247,60 @@ void wakeup_source_unregister(struct wakeup_source *ws)
}
EXPORT_SYMBOL_GPL(wakeup_source_unregister);
/**
* wakeup_sources_read_lock - Lock wakeup source list for read.
*
* Returns an index of srcu lock for struct wakeup_srcu.
* This index must be passed to the matching wakeup_sources_read_unlock().
*/
int wakeup_sources_read_lock(void)
{
return srcu_read_lock(&wakeup_srcu);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_lock);
/**
* wakeup_sources_read_unlock - Unlock wakeup source list.
* @idx: return value from corresponding wakeup_sources_read_lock()
*/
void wakeup_sources_read_unlock(int idx)
{
srcu_read_unlock(&wakeup_srcu, idx);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock);
/**
* wakeup_sources_walk_start - Begin a walk on wakeup source list
*
* Returns first object of the list of wakeup sources.
*
* Note that to be safe, wakeup sources list needs to be locked by calling
* wakeup_source_read_lock() for this.
*/
struct wakeup_source *wakeup_sources_walk_start(void)
{
struct list_head *ws_head = &wakeup_sources;
return list_entry_rcu(ws_head->next, struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_start);
/**
* wakeup_sources_walk_next - Get next wakeup source from the list
* @ws: Previous wakeup source object
*
* Note that to be safe, wakeup sources list needs to be locked by calling
* wakeup_source_read_lock() for this.
*/
struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws)
{
struct list_head *ws_head = &wakeup_sources;
return list_next_or_null_rcu(ws_head, &ws->entry,
struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_next);
/**
* device_wakeup_attach - Attach a wakeup source object to a device object.
* @dev: Device to handle.

View File

@ -41,8 +41,9 @@ config MOXTET
config HISILICON_LPC
bool "Support for ISA I/O space on HiSilicon Hip06/7"
depends on ARM64 && (ARCH_HISI || COMPILE_TEST)
select INDIRECT_PIO
depends on (ARM64 && ARCH_HISI) || (COMPILE_TEST && !ALPHA && !HEXAGON && !PARISC && !C6X)
depends on HAS_IOMEM
select INDIRECT_PIO if ARM64
help
Driver to enable I/O access to devices attached to the Low Pin
Count bus on the HiSilicon Hip06/7 SoC.

View File

@ -74,7 +74,7 @@ struct hisi_lpc_dev {
/* About 10us. This is specific for single IO operations, such as inb */
#define LPC_PEROP_WAITCNT 100
static int wait_lpc_idle(unsigned char *mbase, unsigned int waitcnt)
static int wait_lpc_idle(void __iomem *mbase, unsigned int waitcnt)
{
u32 status;
@ -209,7 +209,7 @@ static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth)
struct hisi_lpc_dev *lpcdev = hostdata;
struct lpc_cycle_para iopara;
unsigned long addr;
u32 rd_data = 0;
__le32 rd_data = 0;
int ret;
if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
@ -244,13 +244,12 @@ static void hisi_lpc_comm_out(void *hostdata, unsigned long pio,
struct lpc_cycle_para iopara;
const unsigned char *buf;
unsigned long addr;
__le32 _val = cpu_to_le32(val);
if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
return;
val = cpu_to_le32(val);
buf = (const unsigned char *)&val;
buf = (const unsigned char *)&_val;
addr = hisi_lpc_pio_to_addr(lpcdev, pio);
iopara.opflags = FG_INCRADDR_LPC;

View File

@ -917,6 +917,9 @@ set_midle:
return -EINVAL;
}
if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY)
best_mode = SYSC_IDLE_NO;
reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift);
reg |= best_mode << regbits->midle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
@ -978,6 +981,9 @@ static int sysc_disable_module(struct device *dev)
return ret;
}
if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY)
best_mode = SYSC_IDLE_FORCE;
reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift);
reg |= best_mode << regbits->midle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
@ -1037,8 +1043,6 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev,
struct ti_sysc_platform_data *pdata;
int error;
reset_control_deassert(ddata->rsts);
pdata = dev_get_platdata(ddata->dev);
if (!pdata)
return 0;
@ -1051,6 +1055,8 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev,
dev_err(dev, "%s: could not enable: %i\n",
__func__, error);
reset_control_deassert(ddata->rsts);
return 0;
}
@ -1104,8 +1110,6 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
sysc_clkdm_deny_idle(ddata);
reset_control_deassert(ddata->rsts);
if (sysc_opt_clks_needed(ddata)) {
error = sysc_enable_opt_clocks(ddata);
if (error)
@ -1116,6 +1120,8 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
if (error)
goto err_opt_clocks;
reset_control_deassert(ddata->rsts);
if (ddata->legacy_mode) {
error = sysc_runtime_resume_legacy(dev, ddata);
if (error)
@ -1251,6 +1257,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("gpu", 0x50000000, 0x14, -1, -1, 0x00010201, 0xffffffff, 0),
SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff,
SYSC_MODULE_QUIRK_SGX),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -1, 0x4ea2080d, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0,
SYSC_MODULE_QUIRK_WDT),
/* Watchdog on am3 and am4 */
@ -1309,8 +1319,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, 0),
SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0),
#endif
};
@ -1532,37 +1540,6 @@ static int sysc_legacy_init(struct sysc *ddata)
return error;
}
/**
* sysc_rstctrl_reset_deassert - deassert rstctrl reset
* @ddata: device driver data
* @reset: reset before deassert
*
* A module can have both OCP softreset control and external rstctrl.
* If more complicated rstctrl resets are needed, please handle these
* directly from the child device driver and map only the module reset
* for the parent interconnect target module device.
*
* Automatic reset of the module on init can be skipped with the
* "ti,no-reset-on-init" device tree property.
*/
static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset)
{
int error;
if (!ddata->rsts)
return 0;
if (reset) {
error = reset_control_assert(ddata->rsts);
if (error)
return error;
}
reset_control_deassert(ddata->rsts);
return 0;
}
/*
* Note that the caller must ensure the interconnect target module is enabled
* before calling reset. Otherwise reset will not complete.
@ -1625,15 +1602,6 @@ static int sysc_reset(struct sysc *ddata)
static int sysc_init_module(struct sysc *ddata)
{
int error = 0;
bool manage_clocks = true;
error = sysc_rstctrl_reset_deassert(ddata, false);
if (error)
return error;
if (ddata->cfg.quirks &
(SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))
manage_clocks = false;
error = sysc_clockdomain_init(ddata);
if (error)
@ -1654,7 +1622,7 @@ static int sysc_init_module(struct sysc *ddata)
goto err_opt_clocks;
if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) {
error = sysc_rstctrl_reset_deassert(ddata, true);
error = reset_control_deassert(ddata->rsts);
if (error)
goto err_main_clocks;
}
@ -1666,28 +1634,32 @@ static int sysc_init_module(struct sysc *ddata)
if (ddata->legacy_mode) {
error = sysc_legacy_init(ddata);
if (error)
goto err_main_clocks;
goto err_reset;
}
if (!ddata->legacy_mode) {
error = sysc_enable_module(ddata->dev);
if (error)
goto err_main_clocks;
goto err_reset;
}
error = sysc_reset(ddata);
if (error)
dev_err(ddata->dev, "Reset failed with %d\n", error);
if (!ddata->legacy_mode && manage_clocks)
if (error && !ddata->legacy_mode)
sysc_disable_module(ddata->dev);
err_reset:
if (error && !(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT))
reset_control_assert(ddata->rsts);
err_main_clocks:
if (manage_clocks)
if (error)
sysc_disable_main_clocks(ddata);
err_opt_clocks:
/* No re-enable of clockdomain autoidle to prevent module autoidle */
if (manage_clocks) {
if (error) {
sysc_disable_opt_clocks(ddata);
sysc_clkdm_allow_idle(ddata);
}
@ -2460,10 +2432,17 @@ static int sysc_probe(struct platform_device *pdev)
goto unprepare;
}
/* Balance reset counts */
if (ddata->rsts)
/* Balance use counts as PM runtime should have enabled these all */
if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT))
reset_control_assert(ddata->rsts);
if (!(ddata->cfg.quirks &
(SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))) {
sysc_disable_main_clocks(ddata);
sysc_disable_opt_clocks(ddata);
sysc_clkdm_allow_idle(ddata);
}
sysc_show_registers(ddata);
ddata->dev->type = &sysc_device_type;

View File

@ -323,7 +323,7 @@ static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db)
if (db->mask)
val = ioread64_hi_lo(db->addr) & db->mask;
iowrite64_hi_lo(db->set, db->addr);
iowrite64_hi_lo(db->set | val, db->addr);
}
#endif
}

View File

@ -114,7 +114,7 @@ static int imx_dsp_probe(struct platform_device *pdev)
dev_info(dev, "NXP i.MX DSP IPC initialized\n");
return devm_of_platform_populate(dev);
return 0;
out:
kfree(chan_name);
for (j = 0; j < i; j++) {

View File

@ -8,6 +8,7 @@
#include <dt-bindings/firmware/imx/rsrc.h>
#include <linux/firmware/imx/ipc.h>
#include <linux/firmware/imx/sci.h>
#include <linux/mailbox_client.h>
#define IMX_SC_IRQ_FUNC_ENABLE 1

View File

@ -107,6 +107,12 @@ static void imx_scu_rx_callback(struct mbox_client *c, void *msg)
struct imx_sc_rpc_msg *hdr;
u32 *data = msg;
if (!sc_ipc->msg) {
dev_warn(sc_ipc->dev, "unexpected rx idx %d 0x%08x, ignore!\n",
sc_chan->idx, *data);
return;
}
if (sc_chan->idx == 0) {
hdr = msg;
sc_ipc->rx_size = hdr->size;
@ -156,6 +162,7 @@ static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg)
*/
int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
{
uint8_t saved_svc, saved_func;
struct imx_sc_rpc_msg *hdr;
int ret;
@ -165,7 +172,11 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
mutex_lock(&sc_ipc->lock);
reinit_completion(&sc_ipc->done);
sc_ipc->msg = msg;
if (have_resp) {
sc_ipc->msg = msg;
saved_svc = ((struct imx_sc_rpc_msg *)msg)->svc;
saved_func = ((struct imx_sc_rpc_msg *)msg)->func;
}
sc_ipc->count = 0;
ret = imx_scu_ipc_write(sc_ipc, msg);
if (ret < 0) {
@ -184,9 +195,20 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
/* response status is stored in hdr->func field */
hdr = msg;
ret = hdr->func;
/*
* Some special SCU firmware APIs do NOT have return value
* in hdr->func, but they do have response data, those special
* APIs are defined as void function in SCU firmware, so they
* should be treated as return success always.
*/
if ((saved_svc == IMX_SC_RPC_SVC_MISC) &&
(saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID ||
saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS))
ret = 0;
}
out:
sc_ipc->msg = NULL;
mutex_unlock(&sc_ipc->lock);
dev_dbg(sc_ipc->dev, "RPC SVC done\n");

View File

@ -35,7 +35,7 @@ struct meson_sm_chip {
struct meson_sm_cmd cmd[];
};
struct meson_sm_chip gxbb_chip = {
static const struct meson_sm_chip gxbb_chip = {
.shmem_size = SZ_4K,
.cmd_shmem_in_base = 0x82000020,
.cmd_shmem_out_base = 0x82000021,
@ -54,8 +54,6 @@ struct meson_sm_firmware {
void __iomem *sm_shmem_out_base;
};
static struct meson_sm_firmware fw;
static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip,
unsigned int cmd_index)
{
@ -90,6 +88,7 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
/**
* meson_sm_call - generic SMC32 call to the secure-monitor
*
* @fw: Pointer to secure-monitor firmware
* @cmd_index: Index of the SMC32 function ID
* @ret: Returned value
* @arg0: SMC32 Argument 0
@ -100,15 +99,15 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
*
* Return: 0 on success, a negative value on error
*/
int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0,
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index,
u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
{
u32 cmd, lret;
if (!fw.chip)
if (!fw->chip)
return -ENOENT;
cmd = meson_sm_get_cmd(fw.chip, cmd_index);
cmd = meson_sm_get_cmd(fw->chip, cmd_index);
if (!cmd)
return -EINVAL;
@ -124,6 +123,7 @@ EXPORT_SYMBOL(meson_sm_call);
/**
* meson_sm_call_read - retrieve data from secure-monitor
*
* @fw: Pointer to secure-monitor firmware
* @buffer: Buffer to store the retrieved data
* @bsize: Size of the buffer
* @cmd_index: Index of the SMC32 function ID
@ -137,22 +137,23 @@ EXPORT_SYMBOL(meson_sm_call);
* When 0 is returned there is no guarantee about the amount of
* data read and bsize bytes are copied in buffer.
*/
int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index,
u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer,
unsigned int bsize, unsigned int cmd_index, u32 arg0,
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
{
u32 size;
int ret;
if (!fw.chip)
if (!fw->chip)
return -ENOENT;
if (!fw.chip->cmd_shmem_out_base)
if (!fw->chip->cmd_shmem_out_base)
return -EINVAL;
if (bsize > fw.chip->shmem_size)
if (bsize > fw->chip->shmem_size)
return -EINVAL;
if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
return -EINVAL;
if (size > bsize)
@ -164,7 +165,7 @@ int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index,
size = bsize;
if (buffer)
memcpy(buffer, fw.sm_shmem_out_base, size);
memcpy(buffer, fw->sm_shmem_out_base, size);
return ret;
}
@ -173,6 +174,7 @@ EXPORT_SYMBOL(meson_sm_call_read);
/**
* meson_sm_call_write - send data to secure-monitor
*
* @fw: Pointer to secure-monitor firmware
* @buffer: Buffer containing data to send
* @size: Size of the data to send
* @cmd_index: Index of the SMC32 function ID
@ -184,23 +186,24 @@ EXPORT_SYMBOL(meson_sm_call_read);
*
* Return: size of sent data on success, a negative value on error
*/
int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer,
unsigned int size, unsigned int cmd_index, u32 arg0,
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
{
u32 written;
if (!fw.chip)
if (!fw->chip)
return -ENOENT;
if (size > fw.chip->shmem_size)
if (size > fw->chip->shmem_size)
return -EINVAL;
if (!fw.chip->cmd_shmem_in_base)
if (!fw->chip->cmd_shmem_in_base)
return -EINVAL;
memcpy(fw.sm_shmem_in_base, buffer, size);
memcpy(fw->sm_shmem_in_base, buffer, size);
if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
return -EINVAL;
if (!written)
@ -210,6 +213,24 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
}
EXPORT_SYMBOL(meson_sm_call_write);
/**
* meson_sm_get - get pointer to meson_sm_firmware structure.
*
* @sm_node: Pointer to the secure-monitor Device Tree node.
*
* Return: NULL is the secure-monitor device is not ready.
*/
struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node)
{
struct platform_device *pdev = of_find_device_by_node(sm_node);
if (!pdev)
return NULL;
return platform_get_drvdata(pdev);
}
EXPORT_SYMBOL_GPL(meson_sm_get);
#define SM_CHIP_ID_LENGTH 119
#define SM_CHIP_ID_OFFSET 4
#define SM_CHIP_ID_SIZE 12
@ -217,33 +238,25 @@ EXPORT_SYMBOL(meson_sm_call_write);
static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct meson_sm_firmware *fw;
uint8_t *id_buf;
int ret;
fw = platform_get_drvdata(pdev);
id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
if (!id_buf)
return -ENOMEM;
ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
0, 0, 0, 0, 0);
if (ret < 0) {
kfree(id_buf);
return ret;
}
ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
id_buf[SM_CHIP_ID_OFFSET + 0],
id_buf[SM_CHIP_ID_OFFSET + 1],
id_buf[SM_CHIP_ID_OFFSET + 2],
id_buf[SM_CHIP_ID_OFFSET + 3],
id_buf[SM_CHIP_ID_OFFSET + 4],
id_buf[SM_CHIP_ID_OFFSET + 5],
id_buf[SM_CHIP_ID_OFFSET + 6],
id_buf[SM_CHIP_ID_OFFSET + 7],
id_buf[SM_CHIP_ID_OFFSET + 8],
id_buf[SM_CHIP_ID_OFFSET + 9],
id_buf[SM_CHIP_ID_OFFSET + 10],
id_buf[SM_CHIP_ID_OFFSET + 11]);
ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]);
kfree(id_buf);
@ -268,25 +281,34 @@ static const struct of_device_id meson_sm_ids[] = {
static int __init meson_sm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct meson_sm_chip *chip;
struct meson_sm_firmware *fw;
chip = of_match_device(meson_sm_ids, &pdev->dev)->data;
fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
if (!fw)
return -ENOMEM;
chip = of_match_device(meson_sm_ids, dev)->data;
if (chip->cmd_shmem_in_base) {
fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
chip->shmem_size);
if (WARN_ON(!fw.sm_shmem_in_base))
fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
chip->shmem_size);
if (WARN_ON(!fw->sm_shmem_in_base))
goto out;
}
if (chip->cmd_shmem_out_base) {
fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
chip->shmem_size);
if (WARN_ON(!fw.sm_shmem_out_base))
fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
chip->shmem_size);
if (WARN_ON(!fw->sm_shmem_out_base))
goto out_in_base;
}
fw.chip = chip;
fw->chip = chip;
platform_set_drvdata(pdev, fw);
pr_info("secure-monitor enabled\n");
if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
@ -295,7 +317,7 @@ static int __init meson_sm_probe(struct platform_device *pdev)
return 0;
out_in_base:
iounmap(fw.sm_shmem_in_base);
iounmap(fw->sm_shmem_in_base);
out:
return -EINVAL;
}

View File

@ -804,7 +804,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev)
}
static const struct dev_pm_ops tegra_bpmp_pm_ops = {
.resume_early = tegra_bpmp_resume,
.resume_noirq = tegra_bpmp_resume,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \

View File

@ -711,8 +711,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
int ret;
np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
if (!np)
return 0;
if (!np) {
np = of_find_compatible_node(NULL, NULL, "xlnx,versal");
if (!np)
return 0;
}
of_node_put(np);
ret = get_set_conduit_method(dev->of_node);
@ -770,6 +773,7 @@ static int zynqmp_firmware_remove(struct platform_device *pdev)
static const struct of_device_id zynqmp_firmware_of_match[] = {
{.compatible = "xlnx,zynqmp-firmware"},
{.compatible = "xlnx,versal-firmware"},
{},
};
MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);

View File

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* EBI driver for Atmel chips
* inspired by the fsl weim bus driver
*
* Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
@ -19,6 +16,8 @@
#include <linux/regmap.h>
#include <soc/at91/atmel-sfr.h>
#define AT91_EBI_NUM_CS 8
struct atmel_ebi_dev_config {
int cs;
struct atmel_smc_cs_conf smcconf;
@ -314,7 +313,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
if (ret)
return ret;
if (cs >= AT91_MATRIX_EBI_NUM_CS ||
if (cs >= AT91_EBI_NUM_CS ||
!(ebi->caps->available_cs & BIT(cs))) {
dev_err(dev, "invalid reg property in %pOF\n", np);
return -EINVAL;
@ -344,7 +343,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
apply = true;
i = 0;
for_each_set_bit(cs, &cslines, AT91_MATRIX_EBI_NUM_CS) {
for_each_set_bit(cs, &cslines, AT91_EBI_NUM_CS) {
ebid->configs[i].cs = cs;
if (apply) {

View File

@ -127,7 +127,6 @@ enum dpfe_msg_fields {
MSG_COMMAND,
MSG_ARG_COUNT,
MSG_ARG0,
MSG_CHKSUM,
MSG_FIELD_MAX = 16 /* Max number of arguments */
};
@ -180,7 +179,7 @@ struct dpfe_api {
};
/* Things we need for as long as we are active. */
struct private_data {
struct brcmstb_dpfe_priv {
void __iomem *regs;
void __iomem *dmem;
void __iomem *imem;
@ -232,9 +231,13 @@ static struct attribute *dpfe_v3_attrs[] = {
};
ATTRIBUTE_GROUPS(dpfe_v3);
/* API v2 firmware commands */
static const struct dpfe_api dpfe_api_v2 = {
.version = 2,
/*
* Old API v2 firmware commands, as defined in the rev 0.61 specification, we
* use a version set to 1 to denote that it is not compatible with the new API
* v2 and onwards.
*/
static const struct dpfe_api dpfe_api_old_v2 = {
.version = 1,
.fw_name = "dpfe.bin",
.sysfs_attrs = dpfe_v2_groups,
.command = {
@ -243,21 +246,42 @@ static const struct dpfe_api dpfe_api_v2 = {
[MSG_COMMAND] = 1,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 4,
},
[DPFE_CMD_GET_REFRESH] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 2,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 5,
},
[DPFE_CMD_GET_VENDOR] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 2,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 2,
[MSG_CHKSUM] = 6,
},
}
};
/*
* API v2 firmware commands, as defined in the rev 0.8 specification, named new
* v2 here
*/
static const struct dpfe_api dpfe_api_new_v2 = {
.version = 2,
.fw_name = NULL, /* We expect the firmware to have been downloaded! */
.sysfs_attrs = dpfe_v2_groups,
.command = {
[DPFE_CMD_GET_INFO] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 0x101,
},
[DPFE_CMD_GET_REFRESH] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 0x201,
},
[DPFE_CMD_GET_VENDOR] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 0x202,
},
}
};
@ -273,49 +297,51 @@ static const struct dpfe_api dpfe_api_v3 = {
[MSG_COMMAND] = 0x0101,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 0x104,
},
[DPFE_CMD_GET_REFRESH] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 0x0202,
[MSG_ARG_COUNT] = 0,
/*
* This is a bit ugly. Without arguments, the checksum
* follows right after the argument count and not at
* offset MSG_CHKSUM.
*/
[MSG_ARG0] = 0x203,
},
/* There's no GET_VENDOR command in API v3. */
},
};
static bool is_dcpu_enabled(void __iomem *regs)
static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv)
{
u32 val;
val = readl_relaxed(regs + REG_DCPU_RESET);
mutex_lock(&priv->lock);
val = readl_relaxed(priv->regs + REG_DCPU_RESET);
mutex_unlock(&priv->lock);
return !(val & DCPU_RESET_MASK);
}
static void __disable_dcpu(void __iomem *regs)
static void __disable_dcpu(struct brcmstb_dpfe_priv *priv)
{
u32 val;
if (!is_dcpu_enabled(regs))
if (!is_dcpu_enabled(priv))
return;
mutex_lock(&priv->lock);
/* Put DCPU in reset if it's running. */
val = readl_relaxed(regs + REG_DCPU_RESET);
val = readl_relaxed(priv->regs + REG_DCPU_RESET);
val |= (1 << DCPU_RESET_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
writel_relaxed(val, priv->regs + REG_DCPU_RESET);
mutex_unlock(&priv->lock);
}
static void __enable_dcpu(void __iomem *regs)
static void __enable_dcpu(struct brcmstb_dpfe_priv *priv)
{
void __iomem *regs = priv->regs;
u32 val;
mutex_lock(&priv->lock);
/* Clear mailbox registers. */
writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
writel_relaxed(0, regs + REG_TO_HOST_MBOX);
@ -329,6 +355,8 @@ static void __enable_dcpu(void __iomem *regs)
val = readl_relaxed(regs + REG_DCPU_RESET);
val &= ~(1 << DCPU_RESET_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
mutex_unlock(&priv->lock);
}
static unsigned int get_msg_chksum(const u32 msg[], unsigned int max)
@ -343,7 +371,7 @@ static unsigned int get_msg_chksum(const u32 msg[], unsigned int max)
return sum;
}
static void __iomem *get_msg_ptr(struct private_data *priv, u32 response,
static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
char *buf, ssize_t *size)
{
unsigned int msg_type;
@ -382,7 +410,7 @@ static void __iomem *get_msg_ptr(struct private_data *priv, u32 response,
return ptr;
}
static void __finalize_command(struct private_data *priv)
static void __finalize_command(struct brcmstb_dpfe_priv *priv)
{
unsigned int release_mbox;
@ -390,12 +418,12 @@ static void __finalize_command(struct private_data *priv)
* It depends on the API version which MBOX register we have to write to
* to signal we are done.
*/
release_mbox = (priv->dpfe_api->version < 3)
release_mbox = (priv->dpfe_api->version < 2)
? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX;
writel_relaxed(0, priv->regs + release_mbox);
}
static int __send_command(struct private_data *priv, unsigned int cmd,
static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd,
u32 result[])
{
const u32 *msg = priv->dpfe_api->command[cmd];
@ -421,9 +449,17 @@ static int __send_command(struct private_data *priv, unsigned int cmd,
return -ETIMEDOUT;
}
/* Compute checksum over the message */
chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
chksum = get_msg_chksum(msg, chksum_idx);
/* Write command and arguments to message area */
for (i = 0; i < MSG_FIELD_MAX; i++)
writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
for (i = 0; i < MSG_FIELD_MAX; i++) {
if (i == chksum_idx)
writel_relaxed(chksum, regs + DCPU_MSG_RAM(i));
else
writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
}
/* Tell DCPU there is a command waiting */
writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
@ -517,7 +553,7 @@ static int __verify_firmware(struct init_data *init,
/* Verify checksum by reading back the firmware from co-processor RAM. */
static int __verify_fw_checksum(struct init_data *init,
struct private_data *priv,
struct brcmstb_dpfe_priv *priv,
const struct dpfe_firmware_header *header,
u32 checksum)
{
@ -571,26 +607,23 @@ static int __write_firmware(u32 __iomem *mem, const u32 *fw,
return 0;
}
static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
struct init_data *init)
static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv)
{
const struct dpfe_firmware_header *header;
unsigned int dmem_size, imem_size;
struct device *dev = &pdev->dev;
struct device *dev = priv->dev;
bool is_big_endian = false;
struct private_data *priv;
const struct firmware *fw;
const u32 *dmem, *imem;
struct init_data init;
const void *fw_blob;
int ret;
priv = platform_get_drvdata(pdev);
/*
* Skip downloading the firmware if the DCPU is already running and
* responding to commands.
*/
if (is_dcpu_enabled(priv->regs)) {
if (is_dcpu_enabled(priv)) {
u32 response[MSG_FIELD_MAX];
ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
@ -606,20 +639,23 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
if (!priv->dpfe_api->fw_name)
return -ENODEV;
ret = request_firmware(&fw, priv->dpfe_api->fw_name, dev);
/* request_firmware() prints its own error messages. */
ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev);
/*
* Defer the firmware download if the firmware file couldn't be found.
* The root file system may not be available yet.
*/
if (ret)
return ret;
return (ret == -ENOENT) ? -EPROBE_DEFER : ret;
ret = __verify_firmware(init, fw);
ret = __verify_firmware(&init, fw);
if (ret)
return -EFAULT;
__disable_dcpu(priv->regs);
__disable_dcpu(priv);
is_big_endian = init->is_big_endian;
dmem_size = init->dmem_len;
imem_size = init->imem_len;
is_big_endian = init.is_big_endian;
dmem_size = init.dmem_len;
imem_size = init.imem_len;
/* At the beginning of the firmware blob is a header. */
header = (struct dpfe_firmware_header *)fw->data;
@ -637,17 +673,17 @@ static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
if (ret)
return ret;
ret = __verify_fw_checksum(init, priv, header, init->chksum);
ret = __verify_fw_checksum(&init, priv, header, init.chksum);
if (ret)
return ret;
__enable_dcpu(priv->regs);
__enable_dcpu(priv);
return 0;
}
static ssize_t generic_show(unsigned int command, u32 response[],
struct private_data *priv, char *buf)
struct brcmstb_dpfe_priv *priv, char *buf)
{
int ret;
@ -665,7 +701,7 @@ static ssize_t show_info(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
struct brcmstb_dpfe_priv *priv;
unsigned int info;
ssize_t ret;
@ -688,7 +724,7 @@ static ssize_t show_refresh(struct device *dev,
{
u32 response[MSG_FIELD_MAX];
void __iomem *info;
struct private_data *priv;
struct brcmstb_dpfe_priv *priv;
u8 refresh, sr_abort, ppre, thermal_offs, tuf;
u32 mr4;
ssize_t ret;
@ -721,7 +757,7 @@ static ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
struct brcmstb_dpfe_priv *priv;
void __iomem *info;
unsigned long val;
int ret;
@ -747,7 +783,7 @@ static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
struct brcmstb_dpfe_priv *priv;
void __iomem *info;
ssize_t ret;
u32 mr5, mr6, mr7, mr8, err;
@ -778,7 +814,7 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
struct brcmstb_dpfe_priv *priv;
ssize_t ret;
u32 mr4, mr5, mr6, mr7, mr8, err;
@ -800,16 +836,15 @@ static ssize_t show_dram(struct device *dev, struct device_attribute *devattr,
static int brcmstb_dpfe_resume(struct platform_device *pdev)
{
struct init_data init;
struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev);
return brcmstb_dpfe_download_firmware(pdev, &init);
return brcmstb_dpfe_download_firmware(priv);
}
static int brcmstb_dpfe_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct private_data *priv;
struct init_data init;
struct brcmstb_dpfe_priv *priv;
struct resource *res;
int ret;
@ -817,6 +852,8 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
priv->dev = dev;
mutex_init(&priv->lock);
platform_set_drvdata(pdev, priv);
@ -851,9 +888,10 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
return -ENOENT;
}
ret = brcmstb_dpfe_download_firmware(pdev, &init);
ret = brcmstb_dpfe_download_firmware(priv);
if (ret) {
dev_err(dev, "Couldn't download firmware -- %d\n", ret);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Couldn't download firmware -- %d\n", ret);
return ret;
}
@ -867,7 +905,7 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
static int brcmstb_dpfe_remove(struct platform_device *pdev)
{
struct private_data *priv = dev_get_drvdata(&pdev->dev);
struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev);
sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
@ -876,10 +914,10 @@ static int brcmstb_dpfe_remove(struct platform_device *pdev)
static const struct of_device_id brcmstb_dpfe_of_match[] = {
/* Use legacy API v2 for a select number of chips */
{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_v2 },
{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_v2 },
{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_v2 },
{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_v2 },
{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 },
{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 },
{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 },
{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 },
/* API v3 is the default going forward */
{ .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 },
{}

View File

@ -1613,7 +1613,7 @@ static void emif_shutdown(struct platform_device *pdev)
static int get_emif_reg_values(struct emif_data *emif, u32 freq,
struct emif_regs *regs)
{
u32 cs1_used, ip_rev, phy_type;
u32 ip_rev, phy_type;
u32 cl, type;
const struct lpddr2_timings *timings;
const struct lpddr2_min_tck *min_tck;
@ -1621,7 +1621,6 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq,
const struct lpddr2_addressing *addressing;
struct emif_data *emif_for_calc;
struct device *dev;
const struct emif_custom_configs *custom_configs;
dev = emif->dev;
/*
@ -1639,12 +1638,10 @@ static int get_emif_reg_values(struct emif_data *emif, u32 freq,
device_info = emif_for_calc->plat_data->device_info;
type = device_info->type;
cs1_used = device_info->cs1_used;
ip_rev = emif_for_calc->plat_data->ip_rev;
phy_type = emif_for_calc->plat_data->phy_type;
min_tck = emif_for_calc->plat_data->min_tck;
custom_configs = emif_for_calc->plat_data->custom_configs;
set_ddr_clk_period(freq);

View File

@ -29,6 +29,7 @@
#define DDR_TYPE_LPDDR2_S4 3
#define DDR_TYPE_LPDDR2_S2 4
#define DDR_TYPE_LPDDR2_NVM 5
#define DDR_TYPE_LPDDR3 6
/* DDR IO width */
#define DDR_IO_WIDTH_4 1
@ -169,4 +170,64 @@ extern const struct lpddr2_timings
lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES];
extern const struct lpddr2_min_tck lpddr2_jedec_min_tck;
/*
* Structure for timings for LPDDR3 based on LPDDR2 plus additional fields.
* All parameters are in pico seconds(ps) excluding max_freq, min_freq which
* are in Hz.
*/
struct lpddr3_timings {
u32 max_freq;
u32 min_freq;
u32 tRFC;
u32 tRRD;
u32 tRPab;
u32 tRPpb;
u32 tRCD;
u32 tRC;
u32 tRAS;
u32 tWTR;
u32 tWR;
u32 tRTP;
u32 tW2W_C2C;
u32 tR2R_C2C;
u32 tWL;
u32 tDQSCK;
u32 tRL;
u32 tFAW;
u32 tXSR;
u32 tXP;
u32 tCKE;
u32 tCKESR;
u32 tMRD;
};
/*
* Min value for some parameters in terms of number of tCK cycles(nCK)
* Please set to zero parameters that are not valid for a given memory
* type
*/
struct lpddr3_min_tck {
u32 tRFC;
u32 tRRD;
u32 tRPab;
u32 tRPpb;
u32 tRCD;
u32 tRC;
u32 tRAS;
u32 tWTR;
u32 tWR;
u32 tRTP;
u32 tW2W_C2C;
u32 tR2R_C2C;
u32 tWL;
u32 tDQSCK;
u32 tRL;
u32 tFAW;
u32 tXSR;
u32 tXP;
u32 tCKE;
u32 tCKESR;
u32 tMRD;
};
#endif /* __JEDEC_DDR_H */

View File

@ -3,6 +3,7 @@
* OpenFirmware helpers for memory drivers
*
* Copyright (C) 2012 Texas Instruments, Inc.
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
*/
#include <linux/device.h>
@ -149,3 +150,151 @@ default_timings:
return lpddr2_jedec_timings;
}
EXPORT_SYMBOL(of_get_ddr_timings);
/**
* of_lpddr3_get_min_tck() - extract min timing values for lpddr3
* @np: pointer to ddr device tree node
* @device: device requesting for min timing values
*
* Populates the lpddr3_min_tck structure by extracting data
* from device tree node. Returns a pointer to the populated
* structure. If any error in populating the structure, returns NULL.
*/
const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
struct device *dev)
{
int ret = 0;
struct lpddr3_min_tck *min;
min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
if (!min)
goto default_min_tck;
ret |= of_property_read_u32(np, "tRFC-min-tck", &min->tRFC);
ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
ret |= of_property_read_u32(np, "tRPpb-min-tck", &min->tRPpb);
ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
ret |= of_property_read_u32(np, "tRC-min-tck", &min->tRC);
ret |= of_property_read_u32(np, "tRAS-min-tck", &min->tRAS);
ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
ret |= of_property_read_u32(np, "tW2W-C2C-min-tck", &min->tW2W_C2C);
ret |= of_property_read_u32(np, "tR2R-C2C-min-tck", &min->tR2R_C2C);
ret |= of_property_read_u32(np, "tWL-min-tck", &min->tWL);
ret |= of_property_read_u32(np, "tDQSCK-min-tck", &min->tDQSCK);
ret |= of_property_read_u32(np, "tRL-min-tck", &min->tRL);
ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
ret |= of_property_read_u32(np, "tXSR-min-tck", &min->tXSR);
ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD);
if (ret) {
dev_warn(dev, "%s: errors while parsing min-tck values\n",
__func__);
devm_kfree(dev, min);
goto default_min_tck;
}
return min;
default_min_tck:
dev_warn(dev, "%s: using default min-tck values\n", __func__);
return NULL;
}
EXPORT_SYMBOL(of_lpddr3_get_min_tck);
static int of_lpddr3_do_get_timings(struct device_node *np,
struct lpddr3_timings *tim)
{
int ret;
/* The 'reg' param required since DT has changed, used as 'max-freq' */
ret = of_property_read_u32(np, "reg", &tim->max_freq);
ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
ret |= of_property_read_u32(np, "tRFC", &tim->tRFC);
ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
ret |= of_property_read_u32(np, "tRPpb", &tim->tRPpb);
ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
ret |= of_property_read_u32(np, "tRC", &tim->tRC);
ret |= of_property_read_u32(np, "tRAS", &tim->tRAS);
ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
ret |= of_property_read_u32(np, "tWR", &tim->tWR);
ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
ret |= of_property_read_u32(np, "tW2W-C2C", &tim->tW2W_C2C);
ret |= of_property_read_u32(np, "tR2R-C2C", &tim->tR2R_C2C);
ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
ret |= of_property_read_u32(np, "tXSR", &tim->tXSR);
ret |= of_property_read_u32(np, "tXP", &tim->tXP);
ret |= of_property_read_u32(np, "tCKE", &tim->tCKE);
ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
ret |= of_property_read_u32(np, "tMRD", &tim->tMRD);
return ret;
}
/**
* of_lpddr3_get_ddr_timings() - extracts the lpddr3 timings and updates no of
* frequencies available.
* @np_ddr: Pointer to ddr device tree node
* @dev: Device requesting for ddr timings
* @device_type: Type of ddr
* @nr_frequencies: No of frequencies available for ddr
* (updated by this function)
*
* Populates lpddr3_timings structure by extracting data from device
* tree node. Returns pointer to populated structure. If any error
* while populating, returns NULL.
*/
const struct lpddr3_timings
*of_lpddr3_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies)
{
struct lpddr3_timings *timings = NULL;
u32 arr_sz = 0, i = 0;
struct device_node *np_tim;
char *tim_compat = NULL;
switch (device_type) {
case DDR_TYPE_LPDDR3:
tim_compat = "jedec,lpddr3-timings";
break;
default:
dev_warn(dev, "%s: un-supported memory type\n", __func__);
}
for_each_child_of_node(np_ddr, np_tim)
if (of_device_is_compatible(np_tim, tim_compat))
arr_sz++;
if (arr_sz)
timings = devm_kcalloc(dev, arr_sz, sizeof(*timings),
GFP_KERNEL);
if (!timings)
goto default_timings;
for_each_child_of_node(np_ddr, np_tim) {
if (of_device_is_compatible(np_tim, tim_compat)) {
if (of_lpddr3_do_get_timings(np_tim, &timings[i])) {
devm_kfree(dev, timings);
goto default_timings;
}
i++;
}
}
*nr_frequencies = arr_sz;
return timings;
default_timings:
dev_warn(dev, "%s: failed to get timings\n", __func__);
*nr_frequencies = 0;
return NULL;
}
EXPORT_SYMBOL(of_lpddr3_get_ddr_timings);

View File

@ -14,6 +14,11 @@ extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
extern const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies);
extern const struct lpddr3_min_tck
*of_lpddr3_get_min_tck(struct device_node *np, struct device *dev);
extern const struct lpddr3_timings
*of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
struct device *dev, u32 device_type, u32 *nr_frequencies);
#else
static inline const struct lpddr2_min_tck
*of_get_min_tck(struct device_node *np, struct device *dev)
@ -27,6 +32,19 @@ static inline const struct lpddr2_timings
{
return NULL;
}
static inline const struct lpddr3_min_tck
*of_lpddr3_get_min_tck(struct device_node *np, struct device *dev)
{
return NULL;
}
static inline const struct lpddr3_timings
*of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
struct device *dev, u32 device_type, u32 *nr_frequencies)
{
return NULL;
}
#endif /* CONFIG_OF && CONFIG_DDR */
#endif /* __LINUX_MEMORY_OF_REG_ */

View File

@ -7,6 +7,19 @@ config SAMSUNG_MC
if SAMSUNG_MC
config EXYNOS5422_DMC
tristate "EXYNOS5422 Dynamic Memory Controller driver"
depends on ARCH_EXYNOS || (COMPILE_TEST && HAS_IOMEM)
select DDR
depends on DEVFREQ_GOV_SIMPLE_ONDEMAND
depends on (PM_DEVFREQ && PM_DEVFREQ_EVENT)
help
This adds driver for Exynos5422 DMC (Dynamic Memory Controller).
The driver provides support for Dynamic Voltage and Frequency Scaling in
DMC and DRAM. It also supports changing timings of DRAM running with
different frequency. The timings are calculated based on DT memory
information.
config EXYNOS_SROM
bool "Exynos SROM controller driver" if COMPILE_TEST
depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM)

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_EXYNOS5422_DMC) += exynos5422-dmc.o
obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,16 @@ config TEGRA20_EMC
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA30_EMC
bool "NVIDIA Tegra30 External Memory Controller driver"
default y
depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
help
This driver is for the External Memory Controller (EMC) found on
Tegra30 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA124_EMC
bool "NVIDIA Tegra124 External Memory Controller driver"
default y

View File

@ -11,5 +11,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o

View File

@ -5,6 +5,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -18,39 +19,6 @@
#include "mc.h"
#define MC_INTSTATUS 0x000
#define MC_INTMASK 0x004
#define MC_ERR_STATUS 0x08
#define MC_ERR_STATUS_TYPE_SHIFT 28
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT)
#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT)
#define MC_ERR_STATUS_READABLE (1 << 27)
#define MC_ERR_STATUS_WRITABLE (1 << 26)
#define MC_ERR_STATUS_NONSECURE (1 << 25)
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
#define MC_ERR_STATUS_ADR_HI_MASK 0x3
#define MC_ERR_STATUS_SECURITY (1 << 17)
#define MC_ERR_STATUS_RW (1 << 16)
#define MC_ERR_ADR 0x0c
#define MC_GART_ERROR_REQ 0x30
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
#define MC_SECURITY_VIOLATION_STATUS 0x74
#define MC_EMEM_ARB_CFG 0x90
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0)
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
#define MC_EMEM_ARB_MISC0 0xd8
#define MC_EMEM_ADR_CFG 0x54
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
#define MC_TIMING_CONTROL 0xfc
#define MC_TIMING_UPDATE BIT(0)
static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
@ -307,7 +275,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
return 0;
}
void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
{
unsigned int i;
struct tegra_mc_timing *timing = NULL;
@ -322,11 +290,13 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
if (!timing) {
dev_err(mc->dev, "no memory timing registered for rate %lu\n",
rate);
return;
return -EINVAL;
}
for (i = 0; i < mc->soc->num_emem_regs; ++i)
mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
return 0;
}
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
@ -626,6 +596,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
struct resource *res;
struct tegra_mc *mc;
void *isr;
u64 mask;
int err;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
@ -637,6 +608,14 @@ static int tegra_mc_probe(struct platform_device *pdev)
mc->soc = of_device_get_match_data(&pdev->dev);
mc->dev = &pdev->dev;
mask = DMA_BIT_MASK(mc->soc->num_address_bits);
err = dma_coerce_mask_and_coherent(&pdev->dev, mask);
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
return err;
}
/* length of MC tick in nanoseconds */
mc->tick = 30;
@ -658,6 +637,9 @@ static int tegra_mc_probe(struct platform_device *pdev)
} else
#endif
{
/* ensure that debug features are disabled */
mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG);
err = tegra_mc_setup_latency_allowance(mc);
if (err < 0) {
dev_err(&pdev->dev,

View File

@ -6,20 +6,76 @@
#ifndef MEMORY_TEGRA_MC_H
#define MEMORY_TEGRA_MC_H
#include <linux/bits.h>
#include <linux/io.h>
#include <linux/types.h>
#include <soc/tegra/mc.h>
#define MC_INT_DECERR_MTS (1 << 16)
#define MC_INT_SECERR_SEC (1 << 13)
#define MC_INT_DECERR_VPR (1 << 12)
#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
#define MC_INT_ARBITRATION_EMEM (1 << 9)
#define MC_INT_SECURITY_VIOLATION (1 << 8)
#define MC_INT_INVALID_GART_PAGE (1 << 7)
#define MC_INT_DECERR_EMEM (1 << 6)
#define MC_INTSTATUS 0x00
#define MC_INTMASK 0x04
#define MC_ERR_STATUS 0x08
#define MC_ERR_ADR 0x0c
#define MC_GART_ERROR_REQ 0x30
#define MC_EMEM_ADR_CFG 0x54
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
#define MC_SECURITY_VIOLATION_STATUS 0x74
#define MC_EMEM_ARB_CFG 0x90
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
#define MC_EMEM_ARB_TIMING_RCD 0x98
#define MC_EMEM_ARB_TIMING_RP 0x9c
#define MC_EMEM_ARB_TIMING_RC 0xa0
#define MC_EMEM_ARB_TIMING_RAS 0xa4
#define MC_EMEM_ARB_TIMING_FAW 0xa8
#define MC_EMEM_ARB_TIMING_RRD 0xac
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
#define MC_EMEM_ARB_TIMING_R2R 0xb8
#define MC_EMEM_ARB_TIMING_W2W 0xbc
#define MC_EMEM_ARB_TIMING_R2W 0xc0
#define MC_EMEM_ARB_TIMING_W2R 0xc4
#define MC_EMEM_ARB_DA_TURNS 0xd0
#define MC_EMEM_ARB_DA_COVERS 0xd4
#define MC_EMEM_ARB_MISC0 0xd8
#define MC_EMEM_ARB_MISC1 0xdc
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
#define MC_EMEM_ARB_OVERRIDE 0xe8
#define MC_TIMING_CONTROL_DBG 0xf8
#define MC_TIMING_CONTROL 0xfc
#define MC_INT_DECERR_MTS BIT(16)
#define MC_INT_SECERR_SEC BIT(13)
#define MC_INT_DECERR_VPR BIT(12)
#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11)
#define MC_INT_INVALID_SMMU_PAGE BIT(10)
#define MC_INT_ARBITRATION_EMEM BIT(9)
#define MC_INT_SECURITY_VIOLATION BIT(8)
#define MC_INT_INVALID_GART_PAGE BIT(7)
#define MC_INT_DECERR_EMEM BIT(6)
#define MC_ERR_STATUS_TYPE_SHIFT 28
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28)
#define MC_ERR_STATUS_TYPE_MASK (0x7 << 28)
#define MC_ERR_STATUS_READABLE BIT(27)
#define MC_ERR_STATUS_WRITABLE BIT(26)
#define MC_ERR_STATUS_NONSECURE BIT(25)
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
#define MC_ERR_STATUS_ADR_HI_MASK 0x3
#define MC_ERR_STATUS_SECURITY BIT(17)
#define MC_ERR_STATUS_RW BIT(16)
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff)
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff
#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30)
#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31)
#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3
#define MC_TIMING_UPDATE BIT(0)
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
{

View File

@ -909,16 +909,18 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
{ .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
};
static const unsigned int tegra114_group_display[] = {
static const unsigned int tegra114_group_drm[] = {
TEGRA_SWGROUP_DC,
TEGRA_SWGROUP_DCB,
TEGRA_SWGROUP_G2,
TEGRA_SWGROUP_NV,
};
static const struct tegra_smmu_group_soc tegra114_groups[] = {
{
.name = "display",
.swgroups = tegra114_group_display,
.num_swgroups = ARRAY_SIZE(tegra114_group_display),
.name = "drm",
.swgroups = tegra114_group_drm,
.num_swgroups = ARRAY_SIZE(tegra114_group_drm),
},
};

View File

@ -10,26 +10,6 @@
#include "mc.h"
#define MC_EMEM_ARB_CFG 0x90
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
#define MC_EMEM_ARB_TIMING_RCD 0x98
#define MC_EMEM_ARB_TIMING_RP 0x9c
#define MC_EMEM_ARB_TIMING_RC 0xa0
#define MC_EMEM_ARB_TIMING_RAS 0xa4
#define MC_EMEM_ARB_TIMING_FAW 0xa8
#define MC_EMEM_ARB_TIMING_RRD 0xac
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
#define MC_EMEM_ARB_TIMING_R2R 0xb8
#define MC_EMEM_ARB_TIMING_W2W 0xbc
#define MC_EMEM_ARB_TIMING_R2W 0xc0
#define MC_EMEM_ARB_TIMING_W2R 0xc4
#define MC_EMEM_ARB_DA_TURNS 0xd0
#define MC_EMEM_ARB_DA_COVERS 0xd4
#define MC_EMEM_ARB_MISC0 0xd8
#define MC_EMEM_ARB_MISC1 0xdc
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
static const struct tegra_mc_client tegra124_mc_clients[] = {
{
.id = 0x00,
@ -974,16 +954,18 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
};
static const unsigned int tegra124_group_display[] = {
static const unsigned int tegra124_group_drm[] = {
TEGRA_SWGROUP_DC,
TEGRA_SWGROUP_DCB,
TEGRA_SWGROUP_GPU,
TEGRA_SWGROUP_VIC,
};
static const struct tegra_smmu_group_soc tegra124_groups[] = {
{
.name = "display",
.swgroups = tegra124_group_display,
.num_swgroups = ARRAY_SIZE(tegra124_group_display),
.name = "drm",
.swgroups = tegra124_group_drm,
.num_swgroups = ARRAY_SIZE(tegra124_group_drm),
},
};

View File

@ -6,10 +6,11 @@
*/
#include <linux/clk.h>
#include <linux/clk/tegra.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@ -21,6 +22,7 @@
#define EMC_INTSTATUS 0x000
#define EMC_INTMASK 0x004
#define EMC_DBG 0x008
#define EMC_TIMING_CONTROL 0x028
#define EMC_RC 0x02c
#define EMC_RFC 0x030
@ -79,6 +81,12 @@
#define EMC_REFRESH_OVERFLOW_INT BIT(3)
#define EMC_CLKCHANGE_COMPLETE_INT BIT(4)
#define EMC_DBG_READ_MUX_ASSEMBLY BIT(0)
#define EMC_DBG_WRITE_MUX_ACTIVE BIT(1)
#define EMC_DBG_FORCE_UPDATE BIT(2)
#define EMC_DBG_READ_DQM_CTRL BIT(9)
#define EMC_DBG_CFG_PRIORITY BIT(24)
static const u16 emc_timing_registers[] = {
EMC_RC,
EMC_RFC,
@ -137,9 +145,6 @@ struct tegra_emc {
struct device *dev;
struct completion clk_handshake_complete;
struct notifier_block clk_nb;
struct clk *backup_clk;
struct clk *emc_mux;
struct clk *pll_m;
struct clk *clk;
void __iomem *regs;
@ -219,7 +224,7 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
{
long timeout;
unsigned long timeout;
dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush);
@ -231,14 +236,10 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
}
timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
usecs_to_jiffies(100));
msecs_to_jiffies(100));
if (timeout == 0) {
dev_err(emc->dev, "EMC-CAR handshake failed\n");
return -EIO;
} else if (timeout < 0) {
dev_err(emc->dev, "failed to wait for EMC-CAR handshake: %ld\n",
timeout);
return timeout;
}
return 0;
@ -363,6 +364,13 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
NULL);
dev_info(emc->dev,
"got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
emc->num_timings,
tegra_read_ram_code(),
emc->timings[0].rate / 1000000,
emc->timings[emc->num_timings - 1].rate / 1000000);
return 0;
}
@ -398,7 +406,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
static int emc_setup_hw(struct tegra_emc *emc)
{
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
u32 emc_cfg;
u32 emc_cfg, emc_dbg;
emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
@ -421,42 +429,53 @@ static int emc_setup_hw(struct tegra_emc *emc)
writel_relaxed(intmask, emc->regs + EMC_INTMASK);
writel_relaxed(intmask, emc->regs + EMC_INTSTATUS);
/* ensure that unwanted debug features are disabled */
emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
emc_dbg |= EMC_DBG_CFG_PRIORITY;
emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY;
emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE;
emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
return 0;
}
static int emc_init(struct tegra_emc *emc, unsigned long rate)
static long emc_round_rate(unsigned long rate,
unsigned long min_rate,
unsigned long max_rate,
void *arg)
{
int err;
struct emc_timing *timing = NULL;
struct tegra_emc *emc = arg;
unsigned int i;
err = clk_set_parent(emc->emc_mux, emc->backup_clk);
if (err) {
dev_err(emc->dev,
"failed to reparent to backup source: %d\n", err);
return err;
min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate);
for (i = 0; i < emc->num_timings; i++) {
if (emc->timings[i].rate < rate && i != emc->num_timings - 1)
continue;
if (emc->timings[i].rate > max_rate) {
i = max(i, 1u) - 1;
if (emc->timings[i].rate < min_rate)
break;
}
if (emc->timings[i].rate < min_rate)
continue;
timing = &emc->timings[i];
break;
}
err = clk_set_rate(emc->pll_m, rate);
if (err) {
dev_err(emc->dev,
"failed to change pll_m rate: %d\n", err);
return err;
if (!timing) {
dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n",
rate, min_rate, max_rate);
return -EINVAL;
}
err = clk_set_parent(emc->emc_mux, emc->pll_m);
if (err) {
dev_err(emc->dev,
"failed to reparent to pll_m: %d\n", err);
return err;
}
err = clk_set_rate(emc->clk, rate);
if (err) {
dev_err(emc->dev,
"failed to change emc rate: %d\n", err);
return err;
}
return 0;
return timing->rate;
}
static int tegra_emc_probe(struct platform_device *pdev)
@ -515,57 +534,26 @@ static int tegra_emc_probe(struct platform_device *pdev)
return err;
}
tegra20_clk_set_emc_round_callback(emc_round_rate, emc);
emc->clk = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(emc->clk)) {
err = PTR_ERR(emc->clk);
dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
return err;
}
emc->pll_m = clk_get_sys(NULL, "pll_m");
if (IS_ERR(emc->pll_m)) {
err = PTR_ERR(emc->pll_m);
dev_err(&pdev->dev, "failed to get pll_m clock: %d\n", err);
return err;
}
emc->backup_clk = clk_get_sys(NULL, "pll_p");
if (IS_ERR(emc->backup_clk)) {
err = PTR_ERR(emc->backup_clk);
dev_err(&pdev->dev, "failed to get pll_p clock: %d\n", err);
goto put_pll_m;
}
emc->emc_mux = clk_get_parent(emc->clk);
if (IS_ERR(emc->emc_mux)) {
err = PTR_ERR(emc->emc_mux);
dev_err(&pdev->dev, "failed to get emc_mux clock: %d\n", err);
goto put_backup;
goto unset_cb;
}
err = clk_notifier_register(emc->clk, &emc->clk_nb);
if (err) {
dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
err);
goto put_backup;
}
/* set DRAM clock rate to maximum */
err = emc_init(emc, emc->timings[emc->num_timings - 1].rate);
if (err) {
dev_err(&pdev->dev, "failed to initialize EMC clock rate: %d\n",
err);
goto unreg_notifier;
goto unset_cb;
}
return 0;
unreg_notifier:
clk_notifier_unregister(emc->clk, &emc->clk_nb);
put_backup:
clk_put(emc->backup_clk);
put_pll_m:
clk_put(emc->pll_m);
unset_cb:
tegra20_clk_set_emc_round_callback(NULL, NULL);
return err;
}

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,27 @@
#include "mc.h"
static const unsigned long tegra30_mc_emem_regs[] = {
MC_EMEM_ARB_CFG,
MC_EMEM_ARB_OUTSTANDING_REQ,
MC_EMEM_ARB_TIMING_RCD,
MC_EMEM_ARB_TIMING_RP,
MC_EMEM_ARB_TIMING_RC,
MC_EMEM_ARB_TIMING_RAS,
MC_EMEM_ARB_TIMING_FAW,
MC_EMEM_ARB_TIMING_RRD,
MC_EMEM_ARB_TIMING_RAP2PRE,
MC_EMEM_ARB_TIMING_WAP2PRE,
MC_EMEM_ARB_TIMING_R2R,
MC_EMEM_ARB_TIMING_W2W,
MC_EMEM_ARB_TIMING_R2W,
MC_EMEM_ARB_TIMING_W2R,
MC_EMEM_ARB_DA_TURNS,
MC_EMEM_ARB_DA_COVERS,
MC_EMEM_ARB_MISC0,
MC_EMEM_ARB_RING1_THROTTLE,
};
static const struct tegra_mc_client tegra30_mc_clients[] = {
{
.id = 0x00,
@ -931,16 +952,19 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
{ .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
};
static const unsigned int tegra30_group_display[] = {
static const unsigned int tegra30_group_drm[] = {
TEGRA_SWGROUP_DC,
TEGRA_SWGROUP_DCB,
TEGRA_SWGROUP_G2,
TEGRA_SWGROUP_NV,
TEGRA_SWGROUP_NV2,
};
static const struct tegra_smmu_group_soc tegra30_groups[] = {
{
.name = "display",
.swgroups = tegra30_group_display,
.num_swgroups = ARRAY_SIZE(tegra30_group_display),
.name = "drm",
.swgroups = tegra30_group_drm,
.num_swgroups = ARRAY_SIZE(tegra30_group_drm),
},
};
@ -994,6 +1018,8 @@ const struct tegra_mc_soc tegra30_mc_soc = {
.atom_size = 16,
.client_id_mask = 0x7f,
.smmu = &tegra30_smmu_soc,
.emem_regs = tegra30_mc_emem_regs,
.num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs),
.intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
MC_INT_DECERR_EMEM,
.reset_ops = &tegra_mc_reset_ops_common,

View File

@ -17,14 +17,18 @@
static int meson_efuse_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
return meson_sm_call_read((u8 *)val, bytes, SM_EFUSE_READ, offset,
struct meson_sm_firmware *fw = context;
return meson_sm_call_read(fw, (u8 *)val, bytes, SM_EFUSE_READ, offset,
bytes, 0, 0, 0);
}
static int meson_efuse_write(void *context, unsigned int offset,
void *val, size_t bytes)
{
return meson_sm_call_write((u8 *)val, bytes, SM_EFUSE_WRITE, offset,
struct meson_sm_firmware *fw = context;
return meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset,
bytes, 0, 0, 0);
}
@ -37,12 +41,25 @@ MODULE_DEVICE_TABLE(of, meson_efuse_match);
static int meson_efuse_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct meson_sm_firmware *fw;
struct device_node *sm_np;
struct nvmem_device *nvmem;
struct nvmem_config *econfig;
struct clk *clk;
unsigned int size;
int ret;
sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0);
if (!sm_np) {
dev_err(&pdev->dev, "no secure-monitor node\n");
return -ENODEV;
}
fw = meson_sm_get(sm_np);
of_node_put(sm_np);
if (!fw)
return -EPROBE_DEFER;
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
@ -65,7 +82,7 @@ static int meson_efuse_probe(struct platform_device *pdev)
return ret;
}
if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) {
if (meson_sm_call(fw, SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) {
dev_err(dev, "failed to get max user");
return -EINVAL;
}
@ -81,6 +98,7 @@ static int meson_efuse_probe(struct platform_device *pdev)
econfig->reg_read = meson_efuse_read;
econfig->reg_write = meson_efuse_write;
econfig->size = size;
econfig->priv = fw;
nvmem = devm_nvmem_register(&pdev->dev, econfig);

View File

@ -103,3 +103,14 @@ config PHY_PXA_USB
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.
config PHY_MMP3_USB
tristate "Marvell MMP3 USB PHY Driver"
depends on MACH_MMP3_DT || COMPILE_TEST
select GENERIC_PHY
help
Enable this to support Marvell MMP3 USB PHY driver for Marvell
SoC. This driver will do the PHY initialization and shutdown.
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.

View File

@ -2,6 +2,7 @@
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_MMP3_USB) += phy-mmp3-usb.o
obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o
obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o
obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o

View File

@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
* Copyright (C) 2018,2019 Lubomir Rintel <lkundrak@v3.sk>
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/soc/mmp/cputype.h>
#define USB2_PLL_REG0 0x4
#define USB2_PLL_REG1 0x8
#define USB2_TX_REG0 0x10
#define USB2_TX_REG1 0x14
#define USB2_TX_REG2 0x18
#define USB2_RX_REG0 0x20
#define USB2_RX_REG1 0x24
#define USB2_RX_REG2 0x28
#define USB2_ANA_REG0 0x30
#define USB2_ANA_REG1 0x34
#define USB2_ANA_REG2 0x38
#define USB2_DIG_REG0 0x3C
#define USB2_DIG_REG1 0x40
#define USB2_DIG_REG2 0x44
#define USB2_DIG_REG3 0x48
#define USB2_TEST_REG0 0x4C
#define USB2_TEST_REG1 0x50
#define USB2_TEST_REG2 0x54
#define USB2_CHARGER_REG0 0x58
#define USB2_OTG_REG0 0x5C
#define USB2_PHY_MON0 0x60
#define USB2_RESETVE_REG0 0x64
#define USB2_ICID_REG0 0x78
#define USB2_ICID_REG1 0x7C
/* USB2_PLL_REG0 */
/* This is for Ax stepping */
#define USB2_PLL_FBDIV_SHIFT_MMP3 0
#define USB2_PLL_FBDIV_MASK_MMP3 (0xFF << 0)
#define USB2_PLL_REFDIV_SHIFT_MMP3 8
#define USB2_PLL_REFDIV_MASK_MMP3 (0xF << 8)
#define USB2_PLL_VDD12_SHIFT_MMP3 12
#define USB2_PLL_VDD18_SHIFT_MMP3 14
/* This is for B0 stepping */
#define USB2_PLL_FBDIV_SHIFT_MMP3_B0 0
#define USB2_PLL_REFDIV_SHIFT_MMP3_B0 9
#define USB2_PLL_VDD18_SHIFT_MMP3_B0 14
#define USB2_PLL_FBDIV_MASK_MMP3_B0 0x01FF
#define USB2_PLL_REFDIV_MASK_MMP3_B0 0x3E00
#define USB2_PLL_CAL12_SHIFT_MMP3 0
#define USB2_PLL_CALI12_MASK_MMP3 (0x3 << 0)
#define USB2_PLL_VCOCAL_START_SHIFT_MMP3 2
#define USB2_PLL_KVCO_SHIFT_MMP3 4
#define USB2_PLL_KVCO_MASK_MMP3 (0x7<<4)
#define USB2_PLL_ICP_SHIFT_MMP3 8
#define USB2_PLL_ICP_MASK_MMP3 (0x7<<8)
#define USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 12
#define USB2_PLL_PU_PLL_SHIFT_MMP3 13
#define USB2_PLL_PU_PLL_MASK (0x1 << 13)
#define USB2_PLL_READY_MASK_MMP3 (0x1 << 15)
/* USB2_TX_REG0 */
#define USB2_TX_IMPCAL_VTH_SHIFT_MMP3 8
#define USB2_TX_IMPCAL_VTH_MASK_MMP3 (0x7 << 8)
#define USB2_TX_RCAL_START_SHIFT_MMP3 13
/* USB2_TX_REG1 */
#define USB2_TX_CK60_PHSEL_SHIFT_MMP3 0
#define USB2_TX_CK60_PHSEL_MASK_MMP3 (0xf << 0)
#define USB2_TX_AMP_SHIFT_MMP3 4
#define USB2_TX_AMP_MASK_MMP3 (0x7 << 4)
#define USB2_TX_VDD12_SHIFT_MMP3 8
#define USB2_TX_VDD12_MASK_MMP3 (0x3 << 8)
/* USB2_TX_REG2 */
#define USB2_TX_DRV_SLEWRATE_SHIFT 10
/* USB2_RX_REG0 */
#define USB2_RX_SQ_THRESH_SHIFT_MMP3 4
#define USB2_RX_SQ_THRESH_MASK_MMP3 (0xf << 4)
#define USB2_RX_SQ_LENGTH_SHIFT_MMP3 10
#define USB2_RX_SQ_LENGTH_MASK_MMP3 (0x3 << 10)
/* USB2_ANA_REG1*/
#define USB2_ANA_PU_ANA_SHIFT_MMP3 14
/* USB2_OTG_REG0 */
#define USB2_OTG_PU_OTG_SHIFT_MMP3 3
struct mmp3_usb_phy {
struct phy *phy;
void __iomem *base;
};
static unsigned int u2o_get(void __iomem *base, unsigned int offset)
{
return readl_relaxed(base + offset);
}
static void u2o_set(void __iomem *base, unsigned int offset,
unsigned int value)
{
u32 reg;
reg = readl_relaxed(base + offset);
reg |= value;
writel_relaxed(reg, base + offset);
readl_relaxed(base + offset);
}
static void u2o_clear(void __iomem *base, unsigned int offset,
unsigned int value)
{
u32 reg;
reg = readl_relaxed(base + offset);
reg &= ~value;
writel_relaxed(reg, base + offset);
readl_relaxed(base + offset);
}
static int mmp3_usb_phy_init(struct phy *phy)
{
struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy);
void __iomem *base = mmp3_usb_phy->base;
if (cpu_is_mmp3_a0()) {
u2o_clear(base, USB2_PLL_REG0, (USB2_PLL_FBDIV_MASK_MMP3
| USB2_PLL_REFDIV_MASK_MMP3));
u2o_set(base, USB2_PLL_REG0,
0xd << USB2_PLL_REFDIV_SHIFT_MMP3
| 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3);
} else if (cpu_is_mmp3_b0()) {
u2o_clear(base, USB2_PLL_REG0, USB2_PLL_REFDIV_MASK_MMP3_B0
| USB2_PLL_FBDIV_MASK_MMP3_B0);
u2o_set(base, USB2_PLL_REG0,
0xd << USB2_PLL_REFDIV_SHIFT_MMP3_B0
| 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3_B0);
} else {
dev_err(&phy->dev, "unsupported silicon revision\n");
return -ENODEV;
}
u2o_clear(base, USB2_PLL_REG1, USB2_PLL_PU_PLL_MASK
| USB2_PLL_ICP_MASK_MMP3
| USB2_PLL_KVCO_MASK_MMP3
| USB2_PLL_CALI12_MASK_MMP3);
u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_PU_PLL_SHIFT_MMP3
| 1 << USB2_PLL_LOCK_BYPASS_SHIFT_MMP3
| 3 << USB2_PLL_ICP_SHIFT_MMP3
| 3 << USB2_PLL_KVCO_SHIFT_MMP3
| 3 << USB2_PLL_CAL12_SHIFT_MMP3);
u2o_clear(base, USB2_TX_REG0, USB2_TX_IMPCAL_VTH_MASK_MMP3);
u2o_set(base, USB2_TX_REG0, 2 << USB2_TX_IMPCAL_VTH_SHIFT_MMP3);
u2o_clear(base, USB2_TX_REG1, USB2_TX_VDD12_MASK_MMP3
| USB2_TX_AMP_MASK_MMP3
| USB2_TX_CK60_PHSEL_MASK_MMP3);
u2o_set(base, USB2_TX_REG1, 3 << USB2_TX_VDD12_SHIFT_MMP3
| 4 << USB2_TX_AMP_SHIFT_MMP3
| 4 << USB2_TX_CK60_PHSEL_SHIFT_MMP3);
u2o_clear(base, USB2_TX_REG2, 3 << USB2_TX_DRV_SLEWRATE_SHIFT);
u2o_set(base, USB2_TX_REG2, 2 << USB2_TX_DRV_SLEWRATE_SHIFT);
u2o_clear(base, USB2_RX_REG0, USB2_RX_SQ_THRESH_MASK_MMP3);
u2o_set(base, USB2_RX_REG0, 0xa << USB2_RX_SQ_THRESH_SHIFT_MMP3);
u2o_set(base, USB2_ANA_REG1, 0x1 << USB2_ANA_PU_ANA_SHIFT_MMP3);
u2o_set(base, USB2_OTG_REG0, 0x1 << USB2_OTG_PU_OTG_SHIFT_MMP3);
return 0;
}
static int mmp3_usb_phy_calibrate(struct phy *phy)
{
struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy);
void __iomem *base = mmp3_usb_phy->base;
int loops;
/*
* PLL VCO and TX Impedance Calibration Timing:
*
* _____________________________________
* PU __________|
* _____________________________
* VCOCAL START _________|
* ___
* REG_RCAL_START ________________| |________|_______
* | 200us | 400us | 40| 400us | USB PHY READY
*/
udelay(200);
u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_VCOCAL_START_SHIFT_MMP3);
udelay(400);
u2o_set(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3);
udelay(40);
u2o_clear(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3);
udelay(400);
loops = 0;
while ((u2o_get(base, USB2_PLL_REG1) & USB2_PLL_READY_MASK_MMP3) == 0) {
mdelay(1);
loops++;
if (loops > 100) {
dev_err(&phy->dev, "PLL_READY not set after 100mS.\n");
return -ETIMEDOUT;
}
}
return 0;
}
static const struct phy_ops mmp3_usb_phy_ops = {
.init = mmp3_usb_phy_init,
.calibrate = mmp3_usb_phy_calibrate,
.owner = THIS_MODULE,
};
static const struct of_device_id mmp3_usb_phy_of_match[] = {
{ .compatible = "marvell,mmp3-usb-phy", },
{ },
};
MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match);
static int mmp3_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *resource;
struct mmp3_usb_phy *mmp3_usb_phy;
struct phy_provider *provider;
mmp3_usb_phy = devm_kzalloc(dev, sizeof(*mmp3_usb_phy), GFP_KERNEL);
if (!mmp3_usb_phy)
return -ENOMEM;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmp3_usb_phy->base = devm_ioremap_resource(dev, resource);
if (IS_ERR(mmp3_usb_phy->base)) {
dev_err(dev, "failed to remap PHY regs\n");
return PTR_ERR(mmp3_usb_phy->base);
}
mmp3_usb_phy->phy = devm_phy_create(dev, NULL, &mmp3_usb_phy_ops);
if (IS_ERR(mmp3_usb_phy->phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(mmp3_usb_phy->phy);
}
phy_set_drvdata(mmp3_usb_phy->phy, mmp3_usb_phy);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider)) {
dev_err(dev, "failed to register PHY provider\n");
return PTR_ERR(provider);
}
return 0;
}
static struct platform_driver mmp3_usb_phy_driver = {
.probe = mmp3_usb_phy_probe,
.driver = {
.name = "mmp3-usb-phy",
.of_match_table = mmp3_usb_phy_of_match,
},
};
module_platform_driver(mmp3_usb_phy_driver);
MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
MODULE_DESCRIPTION("Marvell MMP3 USB PHY Driver");
MODULE_LICENSE("GPL v2");

View File

@ -129,7 +129,7 @@ config RESET_SCMI
config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST
default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN || ARC
default ARCH_AGILEX || ARCH_ASPEED || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARC
help
This enables a simple reset controller driver for reset lines that
that can be asserted and deasserted by toggling bits in a contiguous,
@ -138,10 +138,11 @@ config RESET_SIMPLE
Currently this driver supports:
- Altera SoCFPGAs
- ASPEED BMC SoCs
- Bitmain BM1880 SoC
- Realtek SoCs
- RCC reset controller in STM32 MCUs
- Allwinner SoCs
- ZTE's zx2967 family
- Bitmain BM1880 SoC
config RESET_STM32MP157
bool "STM32MP157 Reset Driver" if COMPILE_TEST

View File

@ -77,8 +77,10 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev)
* @rcdev: a pointer to the reset controller device
* @reset_spec: reset line specifier as found in the device tree
*
* This simple translation function should be used for reset controllers
* with 1:1 mapping, where reset lines can be indexed by number without gaps.
* This static translation function is used by default if of_xlate in
* :c:type:`reset_controller_dev` is not set. It is useful for all reset
* controllers with 1:1 mapping, where reset lines can be indexed by number
* without gaps.
*/
static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
@ -333,7 +335,6 @@ EXPORT_SYMBOL_GPL(reset_control_reset);
* internal state to be reset, but must be prepared for this to happen.
* Consumers must not use reset_control_reset on shared reset lines when
* reset_control_(de)assert has been used.
* return 0.
*
* If rstc is NULL it is an optional reset and the function will just
* return 0.
@ -392,7 +393,6 @@ EXPORT_SYMBOL_GPL(reset_control_assert);
* After calling this function, the reset is guaranteed to be deasserted.
* Consumers must not use reset_control_reset on shared reset lines when
* reset_control_(de)assert has been used.
* return 0.
*
* If rstc is NULL it is an optional reset and the function will just
* return 0.

View File

@ -56,7 +56,7 @@ static int hi3660_reset_dev(struct reset_controller_dev *rcdev,
return hi3660_reset_deassert(rcdev, idx);
}
static struct reset_control_ops hi3660_reset_ops = {
static const struct reset_control_ops hi3660_reset_ops = {
.reset = hi3660_reset_dev,
.assert = hi3660_reset_assert,
.deassert = hi3660_reset_deassert,

View File

@ -19,6 +19,11 @@ struct meson_audio_arb_data {
spinlock_t lock;
};
struct meson_audio_arb_match_data {
const unsigned int *reset_bits;
unsigned int reset_num;
};
#define ARB_GENERAL_BIT 31
static const unsigned int axg_audio_arb_reset_bits[] = {
@ -30,6 +35,27 @@ static const unsigned int axg_audio_arb_reset_bits[] = {
[AXG_ARB_FRDDR_C] = 6,
};
static const struct meson_audio_arb_match_data axg_audio_arb_match = {
.reset_bits = axg_audio_arb_reset_bits,
.reset_num = ARRAY_SIZE(axg_audio_arb_reset_bits),
};
static const unsigned int sm1_audio_arb_reset_bits[] = {
[AXG_ARB_TODDR_A] = 0,
[AXG_ARB_TODDR_B] = 1,
[AXG_ARB_TODDR_C] = 2,
[AXG_ARB_FRDDR_A] = 4,
[AXG_ARB_FRDDR_B] = 5,
[AXG_ARB_FRDDR_C] = 6,
[AXG_ARB_TODDR_D] = 3,
[AXG_ARB_FRDDR_D] = 7,
};
static const struct meson_audio_arb_match_data sm1_audio_arb_match = {
.reset_bits = sm1_audio_arb_reset_bits,
.reset_num = ARRAY_SIZE(sm1_audio_arb_reset_bits),
};
static int meson_audio_arb_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
@ -82,7 +108,13 @@ static const struct reset_control_ops meson_audio_arb_rstc_ops = {
};
static const struct of_device_id meson_audio_arb_of_match[] = {
{ .compatible = "amlogic,meson-axg-audio-arb", },
{
.compatible = "amlogic,meson-axg-audio-arb",
.data = &axg_audio_arb_match,
}, {
.compatible = "amlogic,meson-sm1-audio-arb",
.data = &sm1_audio_arb_match,
},
{}
};
MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match);
@ -104,10 +136,15 @@ static int meson_audio_arb_remove(struct platform_device *pdev)
static int meson_audio_arb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct meson_audio_arb_match_data *data;
struct meson_audio_arb_data *arb;
struct resource *res;
int ret;
data = of_device_get_match_data(dev);
if (!data)
return -EINVAL;
arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL);
if (!arb)
return -ENOMEM;
@ -126,8 +163,8 @@ static int meson_audio_arb_probe(struct platform_device *pdev)
return PTR_ERR(arb->regs);
spin_lock_init(&arb->lock);
arb->reset_bits = axg_audio_arb_reset_bits;
arb->rstc.nr_resets = ARRAY_SIZE(axg_audio_arb_reset_bits);
arb->reset_bits = data->reset_bits;
arb->rstc.nr_resets = data->reset_num;
arb->rstc.ops = &meson_audio_arb_rstc_ops;
arb->rstc.of_node = dev->of_node;
arb->rstc.owner = THIS_MODULE;

View File

@ -15,12 +15,16 @@
#include <linux/types.h>
#include <linux/of_device.h>
#define REG_COUNT 8
#define BITS_PER_REG 32
#define LEVEL_OFFSET 0x7c
struct meson_reset_param {
int reg_count;
int level_offset;
};
struct meson_reset {
void __iomem *reg_base;
const struct meson_reset_param *param;
struct reset_controller_dev rcdev;
spinlock_t lock;
};
@ -46,10 +50,12 @@ static int meson_reset_level(struct reset_controller_dev *rcdev,
container_of(rcdev, struct meson_reset, rcdev);
unsigned int bank = id / BITS_PER_REG;
unsigned int offset = id % BITS_PER_REG;
void __iomem *reg_addr = data->reg_base + LEVEL_OFFSET + (bank << 2);
void __iomem *reg_addr;
unsigned long flags;
u32 reg;
reg_addr = data->reg_base + data->param->level_offset + (bank << 2);
spin_lock_irqsave(&data->lock, flags);
reg = readl(reg_addr);
@ -81,10 +87,21 @@ static const struct reset_control_ops meson_reset_ops = {
.deassert = meson_reset_deassert,
};
static const struct meson_reset_param meson8b_param = {
.reg_count = 8,
.level_offset = 0x7c,
};
static const struct meson_reset_param meson_a1_param = {
.reg_count = 3,
.level_offset = 0x40,
};
static const struct of_device_id meson_reset_dt_ids[] = {
{ .compatible = "amlogic,meson8b-reset" },
{ .compatible = "amlogic,meson-gxbb-reset" },
{ .compatible = "amlogic,meson-axg-reset" },
{ .compatible = "amlogic,meson8b-reset", .data = &meson8b_param},
{ .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
{ .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param},
{ .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param},
{ /* sentinel */ },
};
@ -102,12 +119,16 @@ static int meson_reset_probe(struct platform_device *pdev)
if (IS_ERR(data->reg_base))
return PTR_ERR(data->reg_base);
data->param = of_device_get_match_data(&pdev->dev);
if (!data->param)
return -ENODEV;
platform_set_drvdata(pdev, data);
spin_lock_init(&data->lock);
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG;
data->rcdev.nr_resets = data->param->reg_count * BITS_PER_REG;
data->rcdev.ops = &meson_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;

View File

@ -140,6 +140,10 @@ static const struct of_device_id uniphier_glue_reset_match[] = {
.compatible = "socionext,uniphier-pro4-usb3-reset",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-pro5-usb3-reset",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-pxs2-usb3-reset",
.data = &uniphier_pxs2_data,

View File

@ -64,7 +64,7 @@ static int zynqmp_reset_reset(struct reset_controller_dev *rcdev,
PM_RESET_ACTION_PULSE);
}
static struct reset_control_ops zynqmp_reset_ops = {
static const struct reset_control_ops zynqmp_reset_ops = {
.reset = zynqmp_reset_reset,
.assert = zynqmp_reset_assert,
.deassert = zynqmp_reset_deassert,

View File

@ -40,6 +40,7 @@ static const struct meson_gx_soc_id {
{ "G12A", 0x28 },
{ "G12B", 0x29 },
{ "SM1", 0x2b },
{ "A1", 0x2c },
};
static const struct meson_gx_package_id {
@ -68,6 +69,8 @@ static const struct meson_gx_package_id {
{ "S922X", 0x29, 0x40, 0xf0 },
{ "A311D", 0x29, 0x10, 0xf0 },
{ "S905X3", 0x2b, 0x5, 0xf },
{ "S905D3", 0x2b, 0xb0, 0xf0 },
{ "A113L", 0x2c, 0x0, 0xf8 },
};
static inline unsigned int socinfo_to_major(u32 socinfo)

View File

@ -5,3 +5,14 @@ config AT91_SOC_ID
default ARCH_AT91
help
Include support for the SoC bus on the Atmel ARM SoCs.
config AT91_SOC_SFR
tristate "Special Function Registers support"
depends on ARCH_AT91 || COMPILE_TEST
help
This is a driver for the Special Function Registers available on
Atmel SAMA5Dx SoCs, providing access to specific aspects of the
integrated memory, bridge implementations, processor etc.
This driver can also be built as a module. If so, the module
will be called sfr.

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_AT91_SOC_ID) += soc.o
obj-$(CONFIG_AT91_SOC_SFR) += sfr.o

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* sfr.c - driver for special function registers
*
* Copyright (C) 2019 Bootlin.
*
*/
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/nvmem-provider.h>
#include <linux/random.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define SFR_SN0 0x4c
#define SFR_SN_SIZE 8
struct atmel_sfr_priv {
struct regmap *regmap;
};
static int atmel_sfr_read(void *context, unsigned int offset,
void *buf, size_t bytes)
{
struct atmel_sfr_priv *priv = context;
return regmap_bulk_read(priv->regmap, SFR_SN0 + offset,
buf, bytes / 4);
}
static struct nvmem_config atmel_sfr_nvmem_config = {
.name = "atmel-sfr",
.read_only = true,
.word_size = 4,
.stride = 4,
.size = SFR_SN_SIZE,
.reg_read = atmel_sfr_read,
};
static int atmel_sfr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct nvmem_device *nvmem;
struct atmel_sfr_priv *priv;
u8 sn[SFR_SN_SIZE];
int ret;
priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regmap = syscon_node_to_regmap(np);
if (IS_ERR(priv->regmap)) {
dev_err(dev, "cannot get parent's regmap\n");
return PTR_ERR(priv->regmap);
}
atmel_sfr_nvmem_config.dev = dev;
atmel_sfr_nvmem_config.priv = priv;
nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config);
if (IS_ERR(nvmem)) {
dev_err(dev, "error registering nvmem config\n");
return PTR_ERR(nvmem);
}
ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE);
if (ret == 0)
add_device_randomness(sn, SFR_SN_SIZE);
return ret;
}
static const struct of_device_id atmel_sfr_dt_ids[] = {
{
.compatible = "atmel,sama5d2-sfr",
}, {
.compatible = "atmel,sama5d4-sfr",
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids);
static struct platform_driver atmel_sfr_driver = {
.probe = atmel_sfr_probe,
.driver = {
.name = "atmel-sfr",
.of_match_table = atmel_sfr_dt_ids,
},
};
module_platform_driver(atmel_sfr_driver);
MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
MODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family");
MODULE_LICENSE("GPL v2");

View File

@ -40,4 +40,14 @@ config DPAA2_CONSOLE
/dev/dpaa2_mc_console and /dev/dpaa2_aiop_console,
which can be used to dump the Management Complex and AIOP
firmware logs.
config FSL_RCPM
bool "Freescale RCPM support"
depends on PM_SLEEP && (ARM || ARM64)
help
The NXP QorIQ Processors based on ARM Core have RCPM module
(Run Control and Power Management), which performs all device-level
tasks associated with power management, such as wakeup source control.
Note that currently this driver will not support PowerPC based
QorIQ processor.
endmenu

View File

@ -6,6 +6,7 @@
obj-$(CONFIG_FSL_DPAA) += qbman/
obj-$(CONFIG_QUICC_ENGINE) += qe/
obj-$(CONFIG_CPM) += qe/
obj-$(CONFIG_FSL_RCPM) += rcpm.o
obj-$(CONFIG_FSL_GUTS) += guts.o
obj-$(CONFIG_FSL_MC_DPIO) += dpio/
obj-$(CONFIG_DPAA2_CONSOLE) += dpaa2-console.o

View File

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0
//
// rcpm.c - Freescale QorIQ RCPM driver
//
// Copyright 2019 NXP
//
// Author: Ran Wang <ran.wang_1@nxp.com>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/kernel.h>
#define RCPM_WAKEUP_CELL_MAX_SIZE 7
struct rcpm {
unsigned int wakeup_cells;
void __iomem *ippdexpcr_base;
bool little_endian;
};
/**
* rcpm_pm_prepare - performs device-level tasks associated with power
* management, such as programming related to the wakeup source control.
* @dev: Device to handle.
*
*/
static int rcpm_pm_prepare(struct device *dev)
{
int i, ret, idx;
void __iomem *base;
struct wakeup_source *ws;
struct rcpm *rcpm;
struct device_node *np = dev->of_node;
u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
rcpm = dev_get_drvdata(dev);
if (!rcpm)
return -EINVAL;
base = rcpm->ippdexpcr_base;
idx = wakeup_sources_read_lock();
/* Begin with first registered wakeup source */
for_each_wakeup_source(ws) {
/* skip object which is not attached to device */
if (!ws->dev || !ws->dev->parent)
continue;
ret = device_property_read_u32_array(ws->dev->parent,
"fsl,rcpm-wakeup", value,
rcpm->wakeup_cells + 1);
/* Wakeup source should refer to current rcpm device */
if (ret || (np->phandle != value[0]))
continue;
/* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
* number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
* of wakeup source IP contains an integer array: <phandle to
* RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
* IPPDEXPCR2 setting, etc>.
*
* So we will go thought them to collect setting data.
*/
for (i = 0; i < rcpm->wakeup_cells; i++)
setting[i] |= value[i + 1];
}
wakeup_sources_read_unlock(idx);
/* Program all IPPDEXPCRn once */
for (i = 0; i < rcpm->wakeup_cells; i++) {
u32 tmp = setting[i];
void __iomem *address = base + i * 4;
if (!tmp)
continue;
/* We can only OR related bits */
if (rcpm->little_endian) {
tmp |= ioread32(address);
iowrite32(tmp, address);
} else {
tmp |= ioread32be(address);
iowrite32be(tmp, address);
}
}
return 0;
}
static const struct dev_pm_ops rcpm_pm_ops = {
.prepare = rcpm_pm_prepare,
};
static int rcpm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *r;
struct rcpm *rcpm;
int ret;
rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
if (!rcpm)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENODEV;
rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(rcpm->ippdexpcr_base)) {
ret = PTR_ERR(rcpm->ippdexpcr_base);
return ret;
}
rcpm->little_endian = device_property_read_bool(
&pdev->dev, "little-endian");
ret = device_property_read_u32(&pdev->dev,
"#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
if (ret)
return ret;
dev_set_drvdata(&pdev->dev, rcpm);
return 0;
}
static const struct of_device_id rcpm_of_match[] = {
{ .compatible = "fsl,qoriq-rcpm-2.1+", },
{}
};
MODULE_DEVICE_TABLE(of, rcpm_of_match);
static struct platform_driver rcpm_driver = {
.driver = {
.name = "rcpm",
.of_match_table = rcpm_of_match,
.pm = &rcpm_pm_ops,
},
.probe = rcpm_probe,
};
module_platform_driver(rcpm_driver);

View File

@ -33,12 +33,10 @@ struct imx_sc_msg_misc_get_soc_uid {
u32 uid_high;
} __packed;
static ssize_t soc_uid_show(struct device *dev,
struct device_attribute *attr, char *buf)
static int imx_scu_soc_uid(u64 *soc_uid)
{
struct imx_sc_msg_misc_get_soc_uid msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
u64 soc_uid;
int ret;
hdr->ver = IMX_SC_RPC_VERSION;
@ -52,15 +50,13 @@ static ssize_t soc_uid_show(struct device *dev,
return ret;
}
soc_uid = msg.uid_high;
soc_uid <<= 32;
soc_uid |= msg.uid_low;
*soc_uid = msg.uid_high;
*soc_uid <<= 32;
*soc_uid |= msg.uid_low;
return sprintf(buf, "%016llX\n", soc_uid);
return 0;
}
static DEVICE_ATTR_RO(soc_uid);
static int imx_scu_soc_id(void)
{
struct imx_sc_msg_misc_get_soc_id msg;
@ -89,6 +85,7 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
int id, ret;
u64 uid = 0;
u32 val;
ret = imx_scu_get_handle(&soc_ipc_handle);
@ -112,6 +109,10 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
if (id < 0)
return -EINVAL;
ret = imx_scu_soc_uid(&uid);
if (ret < 0)
return -EINVAL;
/* format soc_id value passed from SCU firmware */
val = id & 0x1f;
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val);
@ -130,19 +131,22 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
goto free_soc_id;
}
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid);
if (!soc_dev_attr->serial_number) {
ret = -ENOMEM;
goto free_revision;
}
ret = device_create_file(soc_device_to_device(soc_dev),
&dev_attr_soc_uid);
if (ret)
goto free_revision;
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
goto free_serial_number;
}
return 0;
free_serial_number:
kfree(soc_dev_attr->serial_number);
free_revision:
kfree(soc_dev_attr->revision);
free_soc_id:

View File

@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/platform_device.h>
#include <linux/arm-smccc.h>
#include <linux/of.h>
#define REV_B1 0x21
@ -16,6 +17,8 @@
#define IMX8MQ_SW_INFO_B1 0x40
#define IMX8MQ_SW_MAGIC_B1 0xff0055aa
#define IMX_SIP_GET_SOC_INFO 0xc2000006
#define OCOTP_UID_LOW 0x410
#define OCOTP_UID_HIGH 0x420
@ -29,13 +32,21 @@ struct imx8_soc_data {
static u64 soc_uid;
static ssize_t soc_uid_show(struct device *dev,
struct device_attribute *attr, char *buf)
#ifdef CONFIG_HAVE_ARM_SMCCC
static u32 imx8mq_soc_revision_from_atf(void)
{
return sprintf(buf, "%016llX\n", soc_uid);
}
struct arm_smccc_res res;
static DEVICE_ATTR_RO(soc_uid);
arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
return 0;
else
return res.a0 & 0xff;
}
#else
static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; };
#endif
static u32 __init imx8mq_soc_revision(void)
{
@ -51,9 +62,16 @@ static u32 __init imx8mq_soc_revision(void)
ocotp_base = of_iomap(np, 0);
WARN_ON(!ocotp_base);
magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
if (magic == IMX8MQ_SW_MAGIC_B1)
rev = REV_B1;
/*
* SOC revision on older imx8mq is not available in fuses so query
* the value from ATF instead.
*/
rev = imx8mq_soc_revision_from_atf();
if (!rev) {
magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
if (magic == IMX8MQ_SW_MAGIC_B1)
rev = REV_B1;
}
soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
soc_uid <<= 32;
@ -174,22 +192,25 @@ static int __init imx8_soc_init(void)
goto free_soc;
}
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid);
if (!soc_dev_attr->serial_number) {
ret = -ENOMEM;
goto free_rev;
}
ret = device_create_file(soc_device_to_device(soc_dev),
&dev_attr_soc_uid);
if (ret)
goto free_rev;
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
goto free_serial_number;
}
if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
return 0;
free_serial_number:
kfree(soc_dev_attr->serial_number);
free_rev:
if (strcmp(soc_dev_attr->revision, "unknown"))
kfree(soc_dev_attr->revision);

View File

@ -21,7 +21,7 @@
#include <dt-bindings/power/mt8173-power.h>
#define MTK_POLL_DELAY_US 10
#define MTK_POLL_TIMEOUT (jiffies_to_usecs(HZ))
#define MTK_POLL_TIMEOUT USEC_PER_SEC
#define MTK_SCPD_ACTIVE_WAKEUP BIT(0)
#define MTK_SCPD_FWAIT_SRAM BIT(1)
@ -108,6 +108,17 @@ static const char * const clk_names[] = {
#define MAX_CLKS 3
/**
* struct scp_domain_data - scp domain data for power on/off flow
* @name: The domain name.
* @sta_mask: The mask for power on/off status bit.
* @ctl_offs: The offset for main power control register.
* @sram_pdn_bits: The mask for sram power control bits.
* @sram_pdn_ack_bits: The mask for sram power control acked bits.
* @bus_prot_mask: The mask for single step bus protection.
* @clk_id: The basic clocks required by this power domain.
* @caps: The flag for active wake-up action.
*/
struct scp_domain_data {
const char *name;
u32 sta_mask;
@ -180,32 +191,132 @@ static int scpsys_domain_is_on(struct scp_domain *scpd)
return -EINVAL;
}
static int scpsys_regulator_enable(struct scp_domain *scpd)
{
if (!scpd->supply)
return 0;
return regulator_enable(scpd->supply);
}
static int scpsys_regulator_disable(struct scp_domain *scpd)
{
if (!scpd->supply)
return 0;
return regulator_disable(scpd->supply);
}
static void scpsys_clk_disable(struct clk *clk[], int max_num)
{
int i;
for (i = max_num - 1; i >= 0; i--)
clk_disable_unprepare(clk[i]);
}
static int scpsys_clk_enable(struct clk *clk[], int max_num)
{
int i, ret = 0;
for (i = 0; i < max_num && clk[i]; i++) {
ret = clk_prepare_enable(clk[i]);
if (ret) {
scpsys_clk_disable(clk, i);
break;
}
}
return ret;
}
static int scpsys_sram_enable(struct scp_domain *scpd, void __iomem *ctl_addr)
{
u32 val;
u32 pdn_ack = scpd->data->sram_pdn_ack_bits;
int tmp;
val = readl(ctl_addr);
val &= ~scpd->data->sram_pdn_bits;
writel(val, ctl_addr);
/* Either wait until SRAM_PDN_ACK all 0 or have a force wait */
if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) {
/*
* Currently, MTK_SCPD_FWAIT_SRAM is necessary only for
* MT7622_POWER_DOMAIN_WB and thus just a trivial setup
* is applied here.
*/
usleep_range(12000, 12100);
} else {
/* Either wait until SRAM_PDN_ACK all 1 or 0 */
int ret = readl_poll_timeout(ctl_addr, tmp,
(tmp & pdn_ack) == 0,
MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
if (ret < 0)
return ret;
}
return 0;
}
static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr)
{
u32 val;
u32 pdn_ack = scpd->data->sram_pdn_ack_bits;
int tmp;
val = readl(ctl_addr);
val |= scpd->data->sram_pdn_bits;
writel(val, ctl_addr);
/* Either wait until SRAM_PDN_ACK all 1 or 0 */
return readl_poll_timeout(ctl_addr, tmp,
(tmp & pdn_ack) == pdn_ack,
MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
}
static int scpsys_bus_protect_enable(struct scp_domain *scpd)
{
struct scp *scp = scpd->scp;
if (!scpd->data->bus_prot_mask)
return 0;
return mtk_infracfg_set_bus_protection(scp->infracfg,
scpd->data->bus_prot_mask,
scp->bus_prot_reg_update);
}
static int scpsys_bus_protect_disable(struct scp_domain *scpd)
{
struct scp *scp = scpd->scp;
if (!scpd->data->bus_prot_mask)
return 0;
return mtk_infracfg_clear_bus_protection(scp->infracfg,
scpd->data->bus_prot_mask,
scp->bus_prot_reg_update);
}
static int scpsys_power_on(struct generic_pm_domain *genpd)
{
struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd);
struct scp *scp = scpd->scp;
void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs;
u32 pdn_ack = scpd->data->sram_pdn_ack_bits;
u32 val;
int ret, tmp;
int i;
if (scpd->supply) {
ret = regulator_enable(scpd->supply);
if (ret)
return ret;
}
ret = scpsys_regulator_enable(scpd);
if (ret < 0)
return ret;
for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) {
ret = clk_prepare_enable(scpd->clk[i]);
if (ret) {
for (--i; i >= 0; i--)
clk_disable_unprepare(scpd->clk[i]);
goto err_clk;
}
}
ret = scpsys_clk_enable(scpd->clk, MAX_CLKS);
if (ret)
goto err_clk;
/* subsys power on */
val = readl(ctl_addr);
val |= PWR_ON_BIT;
writel(val, ctl_addr);
@ -227,43 +338,20 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
val |= PWR_RST_B_BIT;
writel(val, ctl_addr);
val &= ~scpd->data->sram_pdn_bits;
writel(val, ctl_addr);
ret = scpsys_sram_enable(scpd, ctl_addr);
if (ret < 0)
goto err_pwr_ack;
/* Either wait until SRAM_PDN_ACK all 0 or have a force wait */
if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) {
/*
* Currently, MTK_SCPD_FWAIT_SRAM is necessary only for
* MT7622_POWER_DOMAIN_WB and thus just a trivial setup is
* applied here.
*/
usleep_range(12000, 12100);
} else {
ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == 0,
MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
if (ret < 0)
goto err_pwr_ack;
}
if (scpd->data->bus_prot_mask) {
ret = mtk_infracfg_clear_bus_protection(scp->infracfg,
scpd->data->bus_prot_mask,
scp->bus_prot_reg_update);
if (ret)
goto err_pwr_ack;
}
ret = scpsys_bus_protect_disable(scpd);
if (ret < 0)
goto err_pwr_ack;
return 0;
err_pwr_ack:
for (i = MAX_CLKS - 1; i >= 0; i--) {
if (scpd->clk[i])
clk_disable_unprepare(scpd->clk[i]);
}
scpsys_clk_disable(scpd->clk, MAX_CLKS);
err_clk:
if (scpd->supply)
regulator_disable(scpd->supply);
scpsys_regulator_disable(scpd);
dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name);
@ -275,29 +363,19 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd);
struct scp *scp = scpd->scp;
void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs;
u32 pdn_ack = scpd->data->sram_pdn_ack_bits;
u32 val;
int ret, tmp;
int i;
if (scpd->data->bus_prot_mask) {
ret = mtk_infracfg_set_bus_protection(scp->infracfg,
scpd->data->bus_prot_mask,
scp->bus_prot_reg_update);
if (ret)
goto out;
}
val = readl(ctl_addr);
val |= scpd->data->sram_pdn_bits;
writel(val, ctl_addr);
/* wait until SRAM_PDN_ACK all 1 */
ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack,
MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
ret = scpsys_bus_protect_enable(scpd);
if (ret < 0)
goto out;
ret = scpsys_sram_disable(scpd, ctl_addr);
if (ret < 0)
goto out;
/* subsys power off */
val = readl(ctl_addr);
val |= PWR_ISO_BIT;
writel(val, ctl_addr);
@ -319,11 +397,11 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
if (ret < 0)
goto out;
for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++)
clk_disable_unprepare(scpd->clk[i]);
scpsys_clk_disable(scpd->clk, MAX_CLKS);
if (scpd->supply)
regulator_disable(scpd->supply);
ret = scpsys_regulator_disable(scpd);
if (ret < 0)
goto out;
return 0;

View File

@ -58,17 +58,9 @@ config QCOM_LLCC
depends on ARCH_QCOM || COMPILE_TEST
help
Qualcomm Technologies, Inc. platform specific
Last Level Cache Controller(LLCC) driver. This provides interfaces
to clients that use the LLCC. Say yes here to enable LLCC slice
driver.
config QCOM_SDM845_LLCC
tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver"
depends on QCOM_LLCC
help
Say yes here to enable the LLCC driver for SDM845. This provides
data required to configure LLCC so that clients can start using the
LLCC slices.
Last Level Cache Controller(LLCC) driver for platforms such as,
SDM845. This provides interfaces to clients that use the LLCC.
Say yes here to enable LLCC slice driver.
config QCOM_MDT_LOADER
tristate

View File

@ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
obj-$(CONFIG_QCOM_APR) += apr.o
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
*
*/
@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
@ -46,15 +47,90 @@
#define BANK_OFFSET_STRIDE 0x80000
static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
static const struct regmap_config llcc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
/**
* llcc_slice_config - Data associated with the llcc slice
* @usecase_id: Unique id for the client's use case
* @slice_id: llcc slice id for each client
* @max_cap: The maximum capacity of the cache slice provided in KB
* @priority: Priority of the client used to select victim line for replacement
* @fixed_size: Boolean indicating if the slice has a fixed capacity
* @bonus_ways: Bonus ways are additional ways to be used for any slice,
* if client ends up using more than reserved cache ways. Bonus
* ways are allocated only if they are not reserved for some
* other client.
* @res_ways: Reserved ways for the cache slice, the reserved ways cannot
* be used by any other client than the one its assigned to.
* @cache_mode: Each slice operates as a cache, this controls the mode of the
* slice: normal or TCM(Tightly Coupled Memory)
* @probe_target_ways: Determines what ways to probe for access hit. When
* configured to 1 only bonus and reserved ways are probed.
* When configured to 0 all ways in llcc are probed.
* @dis_cap_alloc: Disable capacity based allocation for a client
* @retain_on_pc: If this bit is set and client has maintained active vote
* then the ways assigned to this client are not flushed on power
* collapse.
* @activate_on_init: Activate the slice immediately after it is programmed
*/
struct llcc_slice_config {
u32 usecase_id;
u32 slice_id;
u32 max_cap;
u32 priority;
bool fixed_size;
u32 bonus_ways;
u32 res_ways;
u32 cache_mode;
u32 probe_target_ways;
bool dis_cap_alloc;
bool retain_on_pc;
bool activate_on_init;
};
struct qcom_llcc_config {
const struct llcc_slice_config *sct_data;
int size;
};
static const struct llcc_slice_config sc7180_data[] = {
{ LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 },
{ LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
{ LLCC_GPUHTW, 11, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
{ LLCC_GPU, 12, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
};
static const struct llcc_slice_config sdm845_data[] = {
{ LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 },
{ LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 },
{ LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 },
{ LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 },
{ LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 },
{ LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 },
{ LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 },
{ LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
{ LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 },
{ LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 },
{ LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 },
};
static const struct qcom_llcc_config sc7180_cfg = {
.sct_data = sc7180_data,
.size = ARRAY_SIZE(sc7180_data),
};
static const struct qcom_llcc_config sdm845_cfg = {
.sct_data = sdm845_data,
.size = ARRAY_SIZE(sdm845_data),
};
static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
/**
* llcc_slice_getd - get llcc slice descriptor
* @uid: usecase_id for the client
@ -301,19 +377,24 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
return ret;
}
int qcom_llcc_remove(struct platform_device *pdev)
static int qcom_llcc_remove(struct platform_device *pdev)
{
/* Set the global pointer to a error code to avoid referencing it */
drv_data = ERR_PTR(-ENODEV);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_llcc_remove);
static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev,
const char *name)
{
struct resource *res;
void __iomem *base;
struct regmap_config llcc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
};
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
if (!res)
@ -323,16 +404,19 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev,
if (IS_ERR(base))
return ERR_CAST(base);
llcc_regmap_config.name = name;
return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config);
}
int qcom_llcc_probe(struct platform_device *pdev,
const struct llcc_slice_config *llcc_cfg, u32 sz)
static int qcom_llcc_probe(struct platform_device *pdev)
{
u32 num_banks;
struct device *dev = &pdev->dev;
int ret, i;
struct platform_device *llcc_edac;
const struct qcom_llcc_config *cfg;
const struct llcc_slice_config *llcc_cfg;
u32 sz;
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
if (!drv_data) {
@ -362,6 +446,10 @@ int qcom_llcc_probe(struct platform_device *pdev,
num_banks >>= LLCC_LB_CNT_SHIFT;
drv_data->num_banks = num_banks;
cfg = of_device_get_match_data(&pdev->dev);
llcc_cfg = cfg->sct_data;
sz = cfg->size;
for (i = 0; i < sz; i++)
if (llcc_cfg[i].slice_id > drv_data->max_slices)
drv_data->max_slices = llcc_cfg[i].slice_id;
@ -407,6 +495,22 @@ err:
drv_data = ERR_PTR(-ENODEV);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_llcc_probe);
MODULE_LICENSE("GPL v2");
static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg },
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg },
{ }
};
static struct platform_driver qcom_llcc_driver = {
.driver = {
.name = "qcom-llcc",
.of_match_table = qcom_llcc_of_match,
},
.probe = qcom_llcc_probe,
.remove = qcom_llcc_remove,
};
module_platform_driver(qcom_llcc_driver);
MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller");
MODULE_LICENSE("GPL v2");

View File

@ -1,100 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/soc/qcom/llcc-qcom.h>
/*
* SCT(System Cache Table) entry contains of the following members:
* usecase_id: Unique id for the client's use case
* slice_id: llcc slice id for each client
* max_cap: The maximum capacity of the cache slice provided in KB
* priority: Priority of the client used to select victim line for replacement
* fixed_size: Boolean indicating if the slice has a fixed capacity
* bonus_ways: Bonus ways are additional ways to be used for any slice,
* if client ends up using more than reserved cache ways. Bonus
* ways are allocated only if they are not reserved for some
* other client.
* res_ways: Reserved ways for the cache slice, the reserved ways cannot
* be used by any other client than the one its assigned to.
* cache_mode: Each slice operates as a cache, this controls the mode of the
* slice: normal or TCM(Tightly Coupled Memory)
* probe_target_ways: Determines what ways to probe for access hit. When
* configured to 1 only bonus and reserved ways are probed.
* When configured to 0 all ways in llcc are probed.
* dis_cap_alloc: Disable capacity based allocation for a client
* retain_on_pc: If this bit is set and client has maintained active vote
* then the ways assigned to this client are not flushed on power
* collapse.
* activate_on_init: Activate the slice immediately after the SCT is programmed
*/
#define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \
{ \
.usecase_id = uid, \
.slice_id = sid, \
.max_cap = mc, \
.priority = p, \
.fixed_size = fs, \
.bonus_ways = bway, \
.res_ways = rway, \
.cache_mode = cmod, \
.probe_target_ways = ptw, \
.dis_cap_alloc = dca, \
.retain_on_pc = rp, \
.activate_on_init = a, \
}
static struct llcc_slice_config sdm845_data[] = {
SCT_ENTRY(LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1),
SCT_ENTRY(LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0),
SCT_ENTRY(LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1),
SCT_ENTRY(LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0),
SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0),
};
static int sdm845_qcom_llcc_remove(struct platform_device *pdev)
{
return qcom_llcc_remove(pdev);
}
static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
{
return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data));
}
static const struct of_device_id sdm845_qcom_llcc_of_match[] = {
{ .compatible = "qcom,sdm845-llcc", },
{ }
};
static struct platform_driver sdm845_qcom_llcc_driver = {
.driver = {
.name = "sdm845-llcc",
.of_match_table = sdm845_qcom_llcc_of_match,
},
.probe = sdm845_qcom_llcc_probe,
.remove = sdm845_qcom_llcc_remove,
};
module_platform_driver(sdm845_qcom_llcc_driver);
MODULE_DESCRIPTION("QCOM sdm845 LLCC driver");
MODULE_LICENSE("GPL v2");

View File

@ -115,6 +115,28 @@ struct rpmpd_desc {
static DEFINE_MUTEX(rpmpd_lock);
/* msm8976 RPM Power Domains */
DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2);
DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6);
DEFINE_RPMPD_VFL(msm8976, vddcx_vfl, RWSC, 2);
DEFINE_RPMPD_VFL(msm8976, vddmx_vfl, RWSM, 6);
static struct rpmpd *msm8976_rpmpds[] = {
[MSM8976_VDDCX] = &msm8976_vddcx,
[MSM8976_VDDCX_AO] = &msm8976_vddcx_ao,
[MSM8976_VDDCX_VFL] = &msm8976_vddcx_vfl,
[MSM8976_VDDMX] = &msm8976_vddmx,
[MSM8976_VDDMX_AO] = &msm8976_vddmx_ao,
[MSM8976_VDDMX_VFL] = &msm8976_vddmx_vfl,
};
static const struct rpmpd_desc msm8976_desc = {
.rpmpds = msm8976_rpmpds,
.num_pds = ARRAY_SIZE(msm8976_rpmpds),
.max_state = RPM_SMD_LEVEL_TURBO_HIGH,
};
/* msm8996 RPM Power domains */
DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1);
DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2);
@ -198,6 +220,7 @@ static const struct rpmpd_desc qcs404_desc = {
};
static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc },
{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },
{ .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc },
{ .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc },

View File

@ -19,12 +19,14 @@
/**
* struct qcom_smd_rpm - state of the rpm device driver
* @rpm_channel: reference to the smd channel
* @icc: interconnect proxy device
* @ack: completion for acks
* @lock: mutual exclusion around the send/complete pair
* @ack_status: result of the rpm request
*/
struct qcom_smd_rpm {
struct rpmsg_endpoint *rpm_channel;
struct platform_device *icc;
struct device *dev;
struct completion ack;
@ -193,6 +195,7 @@ static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev,
static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev)
{
struct qcom_smd_rpm *rpm;
int ret;
rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL);
if (!rpm)
@ -205,11 +208,23 @@ static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev)
rpm->rpm_channel = rpdev->ept;
dev_set_drvdata(&rpdev->dev, rpm);
return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev);
rpm->icc = platform_device_register_data(&rpdev->dev, "icc_smd_rpm", -1,
NULL, 0);
if (IS_ERR(rpm->icc))
return PTR_ERR(rpm->icc);
ret = of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev);
if (ret)
platform_device_unregister(rpm->icc);
return ret;
}
static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev)
{
struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev);
platform_device_unregister(rpm->icc);
of_platform_depopulate(&rpdev->dev);
}
@ -217,6 +232,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-apq8084" },
{ .compatible = "qcom,rpm-msm8916" },
{ .compatible = "qcom,rpm-msm8974" },
{ .compatible = "qcom,rpm-msm8976" },
{ .compatible = "qcom,rpm-msm8996" },
{ .compatible = "qcom,rpm-msm8998" },
{ .compatible = "qcom,rpm-sdm660" },

View File

@ -198,6 +198,8 @@ static const struct soc_id soc_id[] = {
{ 310, "MSM8996AU" },
{ 311, "APQ8096AU" },
{ 312, "APQ8096SG" },
{ 321, "SDM845" },
{ 341, "SDA845" },
};
static const char *socinfo_machine(struct device *dev, unsigned int id)

View File

@ -178,6 +178,13 @@ config ARCH_R8A774A1
help
This enables support for the Renesas RZ/G2M SoC.
config ARCH_R8A774B1
bool "Renesas RZ/G2N SoC Platform"
select ARCH_RCAR_GEN3
select SYSC_R8A774B1
help
This enables support for the Renesas RZ/G2N SoC.
config ARCH_R8A774C0
bool "Renesas RZ/G2E SoC Platform"
select ARCH_RCAR_GEN3
@ -192,13 +199,24 @@ config ARCH_R8A7795
help
This enables support for the Renesas R-Car H3 SoC.
config ARCH_R8A77960
bool
select ARCH_RCAR_GEN3
select SYSC_R8A77960
config ARCH_R8A7796
bool "Renesas R-Car M3-W SoC Platform"
select ARCH_RCAR_GEN3
select SYSC_R8A7796
select ARCH_R8A77960
help
This enables support for the Renesas R-Car M3-W SoC.
config ARCH_R8A77961
bool "Renesas R-Car M3-W+ SoC Platform"
select ARCH_RCAR_GEN3
select SYSC_R8A77961
help
This enables support for the Renesas R-Car M3-W+ SoC.
config ARCH_R8A77965
bool "Renesas R-Car M3-N SoC Platform"
select ARCH_RCAR_GEN3
@ -253,6 +271,10 @@ config SYSC_R8A774A1
bool "RZ/G2M System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774B1
bool "RZ/G2N System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774C0
bool "RZ/G2E System Controller support" if COMPILE_TEST
select SYSC_RCAR
@ -281,10 +303,14 @@ config SYSC_R8A7795
bool "R-Car H3 System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7796
config SYSC_R8A77960
bool "R-Car M3-W System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77961
bool "R-Car M3-W+ System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77965
bool "R-Car M3-N System Controller support" if COMPILE_TEST
select SYSC_RCAR

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o
obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o
obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o
obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o
obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o
obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o
obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o
obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o
@ -14,7 +15,8 @@ obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o
obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o
obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o
obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o
obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o
obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o
obj-$(CONFIG_SYSC_R8A77961) += r8a7796-sysc.o
obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o
obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o
obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Cogent Embedded Inc.
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7743-sysc.h>

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Cogent Embedded Inc.
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7745-sysc.h>

View File

@ -5,7 +5,6 @@
* Copyright (C) 2018 Renesas Electronics Corp.
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a77470-sysc.h>

View File

@ -7,7 +7,6 @@
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a774a1-sysc.h>

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G2N System Controller
* Copyright (C) 2019 Renesas Electronics Corp.
*
* Based on Renesas R-Car M3-W System Controller
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bits.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a774b1-sysc.h>
#include "rcar-sysc.h"
static const struct rcar_sysc_area r8a774b1_areas[] __initconst = {
{ "always-on", 0, 0, R8A774B1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca57-scu", 0x1c0, 0, R8A774B1_PD_CA57_SCU, R8A774B1_PD_ALWAYS_ON,
PD_SCU },
{ "ca57-cpu0", 0x80, 0, R8A774B1_PD_CA57_CPU0, R8A774B1_PD_CA57_SCU,
PD_CPU_NOCR },
{ "ca57-cpu1", 0x80, 1, R8A774B1_PD_CA57_CPU1, R8A774B1_PD_CA57_SCU,
PD_CPU_NOCR },
{ "a3vc", 0x380, 0, R8A774B1_PD_A3VC, R8A774B1_PD_ALWAYS_ON },
{ "a3vp", 0x340, 0, R8A774B1_PD_A3VP, R8A774B1_PD_ALWAYS_ON },
{ "a2vc1", 0x3c0, 1, R8A774B1_PD_A2VC1, R8A774B1_PD_A3VC },
{ "3dg-a", 0x100, 0, R8A774B1_PD_3DG_A, R8A774B1_PD_ALWAYS_ON },
{ "3dg-b", 0x100, 1, R8A774B1_PD_3DG_B, R8A774B1_PD_3DG_A },
};
const struct rcar_sysc_info r8a774b1_sysc_info __initconst = {
.areas = r8a774b1_areas,
.num_areas = ARRAY_SIZE(r8a774b1_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};

View File

@ -6,7 +6,7 @@
* Based on Renesas R-Car E3 System Controller
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/sys_soc.h>
@ -50,4 +50,6 @@ const struct rcar_sysc_info r8a774c0_sysc_info __initconst = {
.init = r8a774c0_sysc_init,
.areas = r8a774c0_areas,
.num_areas = ARRAY_SIZE(r8a774c0_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7779-sysc.h>

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7790-sysc.h>

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7791-sysc.h>

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Cogent Embedded Inc.
*/
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/kernel.h>

View File

@ -5,7 +5,6 @@
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7794-sysc.h>

View File

@ -5,7 +5,7 @@
* Copyright (C) 2016-2017 Glider bvba
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/sys_soc.h>
@ -51,25 +51,46 @@ static struct rcar_sysc_area r8a7795_areas[] __initdata = {
/*
* Fixups for R-Car H3 revisions after ES1.x
* Fixups for R-Car H3 revisions
*/
static const struct soc_device_attribute r8a7795es1[] __initconst = {
{ .soc_id = "r8a7795", .revision = "ES1.*" },
#define HAS_A2VC0 BIT(0) /* Power domain A2VC0 is present */
#define NO_EXTMASK BIT(1) /* Missing SYSCEXTMASK register */
static const struct soc_device_attribute r8a7795_quirks_match[] __initconst = {
{
.soc_id = "r8a7795", .revision = "ES1.*",
.data = (void *)(HAS_A2VC0 | NO_EXTMASK),
}, {
.soc_id = "r8a7795", .revision = "ES2.*",
.data = (void *)(NO_EXTMASK),
},
{ /* sentinel */ }
};
static int __init r8a7795_sysc_init(void)
{
if (!soc_device_match(r8a7795es1))
const struct soc_device_attribute *attr;
u32 quirks = 0;
attr = soc_device_match(r8a7795_quirks_match);
if (attr)
quirks = (uintptr_t)attr->data;
if (!(quirks & HAS_A2VC0))
rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas),
R8A7795_PD_A2VC0);
if (quirks & NO_EXTMASK)
r8a7795_sysc_info.extmask_val = 0;
return 0;
}
const struct rcar_sysc_info r8a7795_sysc_info __initconst = {
struct rcar_sysc_info r8a7795_sysc_info __initdata = {
.init = r8a7795_sysc_init,
.areas = r8a7795_areas,
.num_areas = ARRAY_SIZE(r8a7795_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};

View File

@ -1,18 +1,19 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car M3-W System Controller
* Renesas R-Car M3-W/W+ System Controller
*
* Copyright (C) 2016 Glider bvba
* Copyright (C) 2018-2019 Renesas Electronics Corporation
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a7796-sysc.h>
#include "rcar-sysc.h"
static const struct rcar_sysc_area r8a7796_areas[] __initconst = {
static struct rcar_sysc_area r8a7796_areas[] __initdata = {
{ "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON,
PD_SCU },
@ -39,7 +40,28 @@ static const struct rcar_sysc_area r8a7796_areas[] __initconst = {
{ "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON },
};
const struct rcar_sysc_info r8a7796_sysc_info __initconst = {
#ifdef CONFIG_SYSC_R8A77960
const struct rcar_sysc_info r8a77960_sysc_info __initconst = {
.areas = r8a7796_areas,
.num_areas = ARRAY_SIZE(r8a7796_areas),
};
#endif /* CONFIG_SYSC_R8A77960 */
#ifdef CONFIG_SYSC_R8A77961
static int __init r8a77961_sysc_init(void)
{
rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas),
R8A7796_PD_A2VC0);
return 0;
}
const struct rcar_sysc_info r8a77961_sysc_info __initconst = {
.init = r8a77961_sysc_init,
.areas = r8a7796_areas,
.num_areas = ARRAY_SIZE(r8a7796_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};
#endif /* CONFIG_SYSC_R8A77961 */

View File

@ -7,7 +7,7 @@
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a77965-sysc.h>
@ -33,4 +33,6 @@ static const struct rcar_sysc_area r8a77965_areas[] __initconst = {
const struct rcar_sysc_info r8a77965_sysc_info __initconst = {
.areas = r8a77965_areas,
.num_areas = ARRAY_SIZE(r8a77965_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};

View File

@ -5,7 +5,7 @@
* Copyright (C) 2017 Cogent Embedded Inc.
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a77970-sysc.h>
@ -32,4 +32,6 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = {
const struct rcar_sysc_info r8a77970_sysc_info __initconst = {
.areas = r8a77970_areas,
.num_areas = ARRAY_SIZE(r8a77970_areas),
.extmask_offs = 0x1b0,
.extmask_val = BIT(0),
};

View File

@ -6,7 +6,7 @@
* Copyright (C) 2018 Cogent Embedded, Inc.
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a77980-sysc.h>
@ -49,4 +49,6 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = {
const struct rcar_sysc_info r8a77980_sysc_info __initconst = {
.areas = r8a77980_areas,
.num_areas = ARRAY_SIZE(r8a77980_areas),
.extmask_offs = 0x138,
.extmask_val = BIT(0),
};

View File

@ -5,7 +5,7 @@
* Copyright (C) 2018 Renesas Electronics Corp.
*/
#include <linux/bug.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/sys_soc.h>
@ -50,4 +50,6 @@ const struct rcar_sysc_info r8a77990_sysc_info __initconst = {
.init = r8a77990_sysc_init,
.areas = r8a77990_areas,
.num_areas = ARRAY_SIZE(r8a77990_areas),
.extmask_offs = 0x2f8,
.extmask_val = BIT(0),
};

View File

@ -5,7 +5,6 @@
* Copyright (C) 2017 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a77995-sysc.h>

View File

@ -45,6 +45,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
{ .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 },
/* RZ/G2 is handled like R-Car Gen3 */
{ .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 },
/* R-Car Gen1 */
{ .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 },
@ -58,6 +59,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
/* R-Car Gen3 */
{ .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 },

View File

@ -63,6 +63,7 @@ struct rcar_sysc_ch {
static void __iomem *rcar_sysc_base;
static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */
static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val;
static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on)
{
@ -105,6 +106,14 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on)
spin_lock_irqsave(&rcar_sysc_lock, flags);
/*
* Mask external power requests for CPU or 3DG domains
*/
if (rcar_sysc_extmask_val) {
iowrite32(rcar_sysc_extmask_val,
rcar_sysc_base + rcar_sysc_extmask_offs);
}
/*
* The interrupt source needs to be enabled, but masked, to prevent the
* CPU from receiving it.
@ -148,6 +157,9 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on)
iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
out:
if (rcar_sysc_extmask_val)
iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs);
spin_unlock_irqrestore(&rcar_sysc_lock, flags);
pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off",
@ -275,6 +287,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = {
#ifdef CONFIG_SYSC_R8A774A1
{ .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A774B1
{ .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A774C0
{ .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info },
#endif
@ -298,8 +313,11 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = {
#ifdef CONFIG_SYSC_R8A7795
{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A7796
{ .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
#ifdef CONFIG_SYSC_R8A77960
{ .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A77961
{ .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A77965
{ .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info },
@ -360,6 +378,10 @@ static int __init rcar_sysc_pd_init(void)
rcar_sysc_base = base;
/* Optional External Request Mask Register */
rcar_sysc_extmask_offs = info->extmask_offs;
rcar_sysc_extmask_val = info->extmask_val;
domains = kzalloc(sizeof(*domains), GFP_KERNEL);
if (!domains) {
error = -ENOMEM;

View File

@ -44,20 +44,25 @@ struct rcar_sysc_info {
int (*init)(void); /* Optional */
const struct rcar_sysc_area *areas;
unsigned int num_areas;
/* Optional External Request Mask Register */
u32 extmask_offs; /* SYSCEXTMASK register offset */
u32 extmask_val; /* SYSCEXTMASK register mask value */
};
extern const struct rcar_sysc_info r8a7743_sysc_info;
extern const struct rcar_sysc_info r8a7745_sysc_info;
extern const struct rcar_sysc_info r8a77470_sysc_info;
extern const struct rcar_sysc_info r8a774a1_sysc_info;
extern const struct rcar_sysc_info r8a774b1_sysc_info;
extern const struct rcar_sysc_info r8a774c0_sysc_info;
extern const struct rcar_sysc_info r8a7779_sysc_info;
extern const struct rcar_sysc_info r8a7790_sysc_info;
extern const struct rcar_sysc_info r8a7791_sysc_info;
extern const struct rcar_sysc_info r8a7792_sysc_info;
extern const struct rcar_sysc_info r8a7794_sysc_info;
extern const struct rcar_sysc_info r8a7795_sysc_info;
extern const struct rcar_sysc_info r8a7796_sysc_info;
extern struct rcar_sysc_info r8a7795_sysc_info;
extern const struct rcar_sysc_info r8a77960_sysc_info;
extern const struct rcar_sysc_info r8a77961_sysc_info;
extern const struct rcar_sysc_info r8a77965_sysc_info;
extern const struct rcar_sysc_info r8a77970_sysc_info;
extern const struct rcar_sysc_info r8a77980_sysc_info;

View File

@ -116,6 +116,11 @@ static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = {
.id = 0x52,
};
static const struct renesas_soc soc_rz_g2n __initconst __maybe_unused = {
.family = &fam_rzg2,
.id = 0x55,
};
static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = {
.family = &fam_rzg2,
.id = 0x57,
@ -227,6 +232,9 @@ static const struct of_device_id renesas_socs[] __initconst = {
#ifdef CONFIG_ARCH_R8A774A1
{ .compatible = "renesas,r8a774a1", .data = &soc_rz_g2m },
#endif
#ifdef CONFIG_ARCH_R8A774B1
{ .compatible = "renesas,r8a774b1", .data = &soc_rz_g2n },
#endif
#ifdef CONFIG_ARCH_R8A774C0
{ .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e },
#endif
@ -254,9 +262,12 @@ static const struct of_device_id renesas_socs[] __initconst = {
#ifdef CONFIG_ARCH_R8A7795
{ .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 },
#endif
#ifdef CONFIG_ARCH_R8A7796
#ifdef CONFIG_ARCH_R8A77960
{ .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77961
{ .compatible = "renesas,r8a77961", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77965
{ .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n },
#endif
@ -326,7 +337,7 @@ static int __init renesas_soc_init(void)
if (np) {
chipid = of_iomap(np, 0);
of_node_put(np);
} else if (soc->id) {
} else if (soc->id && family->reg) {
chipid = ioremap(family->reg, 4);
}
if (chipid) {

View File

@ -7,6 +7,16 @@ menuconfig SOC_SAMSUNG
if SOC_SAMSUNG
config EXYNOS_ASV
bool "Exynos Adaptive Supply Voltage support" if COMPILE_TEST
depends on (ARCH_EXYNOS && EXYNOS_CHIPID) || COMPILE_TEST
select EXYNOS_ASV_ARM if ARM && ARCH_EXYNOS
# There is no need to enable these drivers for ARMv8
config EXYNOS_ASV_ARM
bool "Exynos ASV ARMv7-specific driver extensions" if COMPILE_TEST
depends on EXYNOS_ASV
config EXYNOS_CHIPID
bool "Exynos Chipid controller driver" if COMPILE_TEST
depends on ARCH_EXYNOS || COMPILE_TEST

Some files were not shown because too many files have changed in this diff Show More