ARM: SoC drivers for 5.16

These are all the driver updates for SoC specific drivers. There
 are a couple of subsystems with individual maintainers picking up
 their patches here:
 
  - The reset controller subsystem add support for a few new SoC
    variants to existing drivers, along with other minor improvements
 
  - The OP-TEE subsystem gets a driver for the ARM FF-A transport
 
  - The memory controller subsystem has improvements for Tegra,
    Mediatek, Renesas, Freescale and Broadcom specific drivers.
 
  - The tegra cpuidle driver changes get merged through this
    tree this time. There are only minor changes, but they depend
    on other tegra driver updates here.
 
  - The ep93xx platform finally moves to using the drivers/clk/
    subsystem, moving the code out of arch/arm in the process.
    This depends on a small sound driver change that is included
    here as well.
 
  - There are some minor updates for Qualcomm and Tegra specific
    firmware drivers.
 
 The other driver updates are mainly for drivers/soc, which contains
 a mixture of vendor specific drivers that don't really fit elsewhere:
 
  - Mediatek drivers gain more support for MT8192, with new support for
    hw-mutex and mmsys routing, plus support for reset lines in the
    mmsys driver.
 
  - Qualcomm gains a new "sleep stats" driver, and support for
    the "Generic Packet Router" in the APR driver.
 
  - There is a new user interface for routing the UARTS on ASpeed
    BMCs, something that apparently nobody else has needed so far.
 
  - More drivers can now be built as loadable modules, in particular
    for Broadcom and Samsung platforms.
 
  - Lots of improvements to the TI sysc driver for better suspend/resume
    support
 
 Finally, there are lots of minor cleanups and new device IDs for
 amlogic, renesas, tegra, qualcomm, mediateka, samsung, imx, layerscape,
 allwinner, broadcom, and omap.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmGCvKgACgkQmmx57+YA
 GNnNfw/8DDTfMUycVvtaNslYlWri0/2O0nSqhNIIbTAcVuD/x5qo/McDhKuv+ldM
 BoTDMjRYZfQkrNXSEj3MaxB9E0o6Srva5SM8y4+Koe0VVtvEVovjYkXOhXqSEWWl
 aqVIe0S6Y1rF/KxJlvAfGxYHb5d+6aYqzdmhjURpXNGxqpSHb9/hqisY97Q9TpnD
 6lQZOz9d1JNDq0eOh1qjcfuMjg1EHZHDZJyioCvyX38KIl2q7p3ll2z/eqrrDhQZ
 TrvL/YVosTXqBcAfi47Oz+n/CX2i0MrjVO8nfPSGOq5UL4Al3SZD4XYY96IOIQrH
 +XGFigGGAkV2LfKSEPNJWaq7g+SiQUr2jc3p8b4Zxde8/+5M127/gotiPddyG2LX
 1OnFRnPskgRApGqHjGEcEzzJUTag7Hc+YVH82TMEHZhSDMq6i30k9UnyfXsziZDV
 8CrkOpjuSg+YxFv/83bfa1pIoYtFfjGr16mq4muajodnX7+b7My9iv+2Oo2iQM9y
 DwRUKj7+eap23SEUpi4et6HlNpoF6yJMbt5Ae1k+gTK2DvQ4Cx6n4QJz/I7WC1Wp
 BdVhvSH8XVppVLtQqODud+VWvLgLerRxUpGRdbS8r5VsnNUJTvaS4YGMpm9616G7
 TrgUSSvsyu1lLqbWMh+pOCk4l3r64vSUn581hrIw6jtioNGvMdE=
 =tUuj
 -----END PGP SIGNATURE-----

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

Pull ARM SoC driver updates from Arnd Bergmann:
 "These are all the driver updates for SoC specific drivers. There are a
  couple of subsystems with individual maintainers picking up their
  patches here:

   - The reset controller subsystem add support for a few new SoC
     variants to existing drivers, along with other minor improvements

   - The OP-TEE subsystem gets a driver for the ARM FF-A transport

   - The memory controller subsystem has improvements for Tegra,
     Mediatek, Renesas, Freescale and Broadcom specific drivers.

   - The tegra cpuidle driver changes get merged through this tree this
     time. There are only minor changes, but they depend on other tegra
     driver updates here.

   - The ep93xx platform finally moves to using the drivers/clk/
     subsystem, moving the code out of arch/arm in the process. This
     depends on a small sound driver change that is included here as
     well.

   - There are some minor updates for Qualcomm and Tegra specific
     firmware drivers.

  The other driver updates are mainly for drivers/soc, which contains a
  mixture of vendor specific drivers that don't really fit elsewhere:

   - Mediatek drivers gain more support for MT8192, with new support for
     hw-mutex and mmsys routing, plus support for reset lines in the
     mmsys driver.

   - Qualcomm gains a new "sleep stats" driver, and support for the
     "Generic Packet Router" in the APR driver.

   - There is a new user interface for routing the UARTS on ASpeed BMCs,
     something that apparently nobody else has needed so far.

   - More drivers can now be built as loadable modules, in particular
     for Broadcom and Samsung platforms.

   - Lots of improvements to the TI sysc driver for better
     suspend/resume support"

  Finally, there are lots of minor cleanups and new device IDs for
  amlogic, renesas, tegra, qualcomm, mediateka, samsung, imx,
  layerscape, allwinner, broadcom, and omap"

* tag 'drivers-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (179 commits)
  optee: Fix spelling mistake "reclain" -> "reclaim"
  Revert "firmware: qcom: scm: Add support for MC boot address API"
  qcom: spm: allow compile-testing
  firmware: arm_ffa: Remove unused 'compat_version' variable
  soc: samsung: exynos-chipid: add exynosautov9 SoC support
  firmware: qcom: scm: Don't break compile test on non-ARM platforms
  soc: qcom: smp2p: Add of_node_put() before goto
  soc: qcom: apr: Add of_node_put() before return
  soc: qcom: qcom_stats: Fix client votes offset
  soc: qcom: rpmhpd: fix sm8350_mxc's peer domain
  dt-bindings: arm: cpus: Document qcom,msm8916-smp enable-method
  ARM: qcom: Add qcom,msm8916-smp enable-method identical to MSM8226
  firmware: qcom: scm: Add support for MC boot address API
  soc: qcom: spm: Add 8916 SPM register data
  dt-bindings: soc: qcom: spm: Document qcom,msm8916-saw2-v3.0-cpu
  soc: qcom: socinfo: Add PM8150C and SMB2351 models
  firmware: qcom_scm: Fix error retval in __qcom_scm_is_call_available()
  soc: aspeed: Add UART routing support
  soc: fsl: dpio: rename the enqueue descriptor variable
  soc: fsl: dpio: use an explicit NULL instead of 0
  ...
This commit is contained in:
Linus Torvalds 2021-11-03 17:00:52 -07:00
commit d461e96cd2
146 changed files with 8007 additions and 3011 deletions

View File

@ -0,0 +1,27 @@
What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart*
Date: September 2021
Contact: Oskar Senft <osk@google.com>
Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Description: Selects the RX source of the UARTx device.
When read, each file shows the list of available options with currently
selected option marked by brackets "[]". The list of available options
depends on the selected file.
e.g.
cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
[io1] io2 io3 io4 uart2 uart3 uart4 io6
In this case, UART1 gets its input from IO1 (physical serial port 1).
Users: OpenBMC. Proposed changes should be mailed to
openbmc@lists.ozlabs.org
What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io*
Date: September 2021
Contact: Oskar Senft <osk@google.com>
Chia-Wei Wang <chiawei_wang@aspeedtech.com>
Description: Selects the RX source of IOx serial port. The current selection
will be marked by brackets "[]".
Users: OpenBMC. Proposed changes should be mailed to
openbmc@lists.ozlabs.org

View File

@ -211,6 +211,9 @@ properties:
- qcom,gcc-msm8660
- qcom,kpss-acc-v1
- qcom,kpss-acc-v2
- qcom,msm8226-smp
# Only valid on ARM 32-bit, see above for ARM v8 64-bit
- qcom,msm8916-smp
- renesas,apmu
- renesas,r9a06g032-smp
- rockchip,rk3036-smp
@ -297,7 +300,8 @@ properties:
Specifies the ACC* node associated with this CPU.
Required for systems that have an "enable-method" property
value of "qcom,kpss-acc-v1" or "qcom,kpss-acc-v2"
value of "qcom,kpss-acc-v1", "qcom,kpss-acc-v2", "qcom,msm8226-smp" or
"qcom,msm8916-smp".
* arm/msm/qcom,kpss-acc.txt

View File

@ -11,8 +11,9 @@ maintainers:
properties:
compatible:
items:
- const: samsung,exynos4210-chipid
enum:
- samsung,exynos4210-chipid
- samsung,exynos850-chipid
reg:
maxItems: 1

View File

@ -1,102 +0,0 @@
* LPDDR2 SDRAM memories compliant to JEDEC JESD209-2
Required properties:
- compatible : Should be one of - "jedec,lpddr2-nvm", "jedec,lpddr2-s2",
"jedec,lpddr2-s4"
"ti,jedec-lpddr2-s2" should be listed if the memory part is LPDDR2-S2 type
"ti,jedec-lpddr2-s4" should be listed if the memory part is LPDDR2-S4 type
"ti,jedec-lpddr2-nvm" should be listed if the memory part is LPDDR2-NVM type
- density : <u32> representing density in Mb (Mega bits)
- io-width : <u32> representing bus width. Possible values are 8, 16, and 32
Optional properties:
The following optional properties represent the minimum value of some AC
timing parameters of the DDR device in terms of number of clock cycles.
These values shall be obtained from the device data-sheet.
- tRRD-min-tck
- tWTR-min-tck
- tXP-min-tck
- tRTP-min-tck
- tCKE-min-tck
- tRPab-min-tck
- tRCD-min-tck
- tWR-min-tck
- tRASmin-min-tck
- tCKESR-min-tck
- tFAW-min-tck
Child nodes:
- The lpddr2 node may have one or more child nodes of type "lpddr2-timings".
"lpddr2-timings" provides AC timing parameters of the device for
a given speed-bin. The user may provide the timings for as many
speed-bins as is required. Please see Documentation/devicetree/
bindings/ddr/lpddr2-timings.txt for more information on "lpddr2-timings"
Example:
elpida_ECB240ABACN : lpddr2 {
compatible = "Elpida,ECB240ABACN","jedec,lpddr2-s4";
density = <2048>;
io-width = <32>;
tRPab-min-tck = <3>;
tRCD-min-tck = <3>;
tWR-min-tck = <3>;
tRASmin-min-tck = <3>;
tRRD-min-tck = <2>;
tWTR-min-tck = <2>;
tXP-min-tck = <2>;
tRTP-min-tck = <2>;
tCKE-min-tck = <3>;
tCKESR-min-tck = <3>;
tFAW-min-tck = <8>;
timings_elpida_ECB240ABACN_400mhz: lpddr2-timings@0 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <400000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <7500>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
timings_elpida_ECB240ABACN_200mhz: lpddr2-timings@1 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <200000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <10000>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
}

View File

@ -102,7 +102,6 @@ examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,dispcc-sc7180.h>
#include <dt-bindings/power/qcom-aoss-qmp.h>
#include <dt-bindings/power/qcom-rpmpd.h>
displayport-controller@ae90000 {

View File

@ -13,8 +13,10 @@ Required properties:
* "qcom,scm-ipq806x"
* "qcom,scm-ipq8074"
* "qcom,scm-mdm9607"
* "qcom,scm-msm8226"
* "qcom,scm-msm8660"
* "qcom,scm-msm8916"
* "qcom,scm-msm8953"
* "qcom,scm-msm8960"
* "qcom,scm-msm8974"
* "qcom,scm-msm8994"
@ -33,7 +35,7 @@ Required properties:
* core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and
"qcom,scm-msm8960"
* core, iface and bus clocks required for "qcom,scm-apq8084",
"qcom,scm-msm8916" and "qcom,scm-msm8974"
"qcom,scm-msm8916", "qcom,scm-msm8953" and "qcom,scm-msm8974"
- clock-names: Must contain "core" for the core clock, "iface" for the interface
clock and "bus" for the bus clock per the requirements of the compatible.
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the

View File

@ -0,0 +1,223 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,lpddr2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: LPDDR2 SDRAM compliant to JEDEC JESD209-2
maintainers:
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
properties:
compatible:
oneOf:
- items:
- enum:
- elpida,ECB240ABACN
- elpida,B8132B2PB-6D-F
- enum:
- jedec,lpddr2-s4
- items:
- enum:
- jedec,lpddr2-s2
- items:
- enum:
- jedec,lpddr2-nvm
revision-id1:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 255
description: |
Revision 1 value of SDRAM chip. Obtained from device datasheet.
revision-id2:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 255
description: |
Revision 2 value of SDRAM chip. Obtained from device datasheet.
density:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Density in megabits of SDRAM chip. Obtained from device datasheet.
enum:
- 64
- 128
- 256
- 512
- 1024
- 2048
- 4096
- 8192
- 16384
- 32768
io-width:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
IO bus width in bits of SDRAM chip. Obtained from device datasheet.
enum:
- 32
- 16
- 8
tRRD-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Active bank a to active bank b in terms of number of clock cycles.
Obtained from device datasheet.
tWTR-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Internal WRITE-to-READ command delay in terms of number of clock cycles.
Obtained from device datasheet.
tXP-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Exit power-down to next valid command delay in terms of number of clock
cycles. Obtained from device datasheet.
tRTP-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Internal READ to PRECHARGE command delay in terms of number of clock
cycles. Obtained from device datasheet.
tCKE-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
CKE minimum pulse width (HIGH and LOW pulse width) in terms of number
of clock cycles. Obtained from device datasheet.
tRPab-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Row precharge time (all banks) in terms of number of clock cycles.
Obtained from device datasheet.
tRCD-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
RAS-to-CAS delay in terms of number of clock cycles. Obtained from
device datasheet.
tWR-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
WRITE recovery time in terms of number of clock cycles. Obtained from
device datasheet.
tRASmin-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Row active time in terms of number of clock cycles. Obtained from device
datasheet.
tCKESR-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
CKE minimum pulse width during SELF REFRESH (low pulse width during
SELF REFRESH) in terms of number of clock cycles. Obtained from device
datasheet.
tFAW-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Four-bank activate window in terms of number of clock cycles. Obtained
from device datasheet.
patternProperties:
"^lpddr2-timings":
type: object
description: |
The lpddr2 node may have one or more child nodes of type "lpddr2-timings".
"lpddr2-timings" provides AC timing parameters of the device for
a given speed-bin. The user may provide the timings for as many
speed-bins as is required. Please see Documentation/devicetree/
bindings/memory-controllers/ddr/lpddr2-timings.txt for more information
on "lpddr2-timings".
required:
- compatible
- density
- io-width
additionalProperties: false
examples:
- |
elpida_ECB240ABACN: lpddr2 {
compatible = "elpida,ECB240ABACN", "jedec,lpddr2-s4";
density = <2048>;
io-width = <32>;
revision-id1 = <1>;
revision-id2 = <0>;
tRPab-min-tck = <3>;
tRCD-min-tck = <3>;
tWR-min-tck = <3>;
tRASmin-min-tck = <3>;
tRRD-min-tck = <2>;
tWTR-min-tck = <2>;
tXP-min-tck = <2>;
tRTP-min-tck = <2>;
tCKE-min-tck = <3>;
tCKESR-min-tck = <3>;
tFAW-min-tck = <8>;
timings_elpida_ECB240ABACN_400mhz: lpddr2-timings0 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <400000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <7500>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
timings_elpida_ECB240ABACN_200mhz: lpddr2-timings1 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <200000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <10000>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
};

View File

@ -43,8 +43,9 @@ These values shall be obtained from the device data-sheet.
Child nodes:
- The lpddr3 node may have one or more child nodes of type "lpddr3-timings".
"lpddr3-timings" provides AC timing parameters of the device for
a given speed-bin. Please see Documentation/devicetree/
bindings/ddr/lpddr3-timings.txt for more information on "lpddr3-timings"
a given speed-bin. Please see
Documentation/devicetree/bindings/memory-controllers/ddr/lpddr3-timings.txt
for more information on "lpddr3-timings"
Example:

View File

@ -16,7 +16,7 @@ description: |
MediaTek SMI have two generations of HW architecture, here is the list
which generation the SoCs use:
generation 1: mt2701 and mt7623.
generation 2: mt2712, mt6779, mt8167, mt8173, mt8183 and mt8192.
generation 2: mt2712, mt6779, mt8167, mt8173, mt8183, mt8192 and mt8195.
There's slight differences between the two SMI, for generation 2, the
register which control the iommu port is at each larb's register base. But
@ -36,6 +36,9 @@ properties:
- mediatek,mt8173-smi-common
- mediatek,mt8183-smi-common
- mediatek,mt8192-smi-common
- mediatek,mt8195-smi-common-vdo
- mediatek,mt8195-smi-common-vpp
- mediatek,mt8195-smi-sub-common
- description: for mt7623
items:
@ -65,6 +68,10 @@ properties:
minItems: 2
maxItems: 4
mediatek,smi:
$ref: /schemas/types.yaml#/definitions/phandle
description: a phandle to the smi-common node above. Only for sub-common.
required:
- compatible
- reg
@ -91,6 +98,29 @@ allOf:
- const: smi
- const: async
- if: # only for sub common
properties:
compatible:
contains:
enum:
- mediatek,mt8195-smi-sub-common
then:
required:
- mediatek,smi
properties:
clock:
items:
minItems: 3
maxItems: 3
clock-names:
items:
- const: apb
- const: smi
- const: gals0
else:
properties:
mediatek,smi: false
- if: # for gen2 HW that have gals
properties:
compatible:
@ -98,6 +128,8 @@ allOf:
- mediatek,mt6779-smi-common
- mediatek,mt8183-smi-common
- mediatek,mt8192-smi-common
- mediatek,mt8195-smi-common-vdo
- mediatek,mt8195-smi-common-vpp
then:
properties:

View File

@ -24,6 +24,7 @@ properties:
- mediatek,mt8173-smi-larb
- mediatek,mt8183-smi-larb
- mediatek,mt8192-smi-larb
- mediatek,mt8195-smi-larb
- description: for mt7623
items:
@ -74,6 +75,7 @@ allOf:
compatible:
enum:
- mediatek,mt8183-smi-larb
- mediatek,mt8195-smi-larb
then:
properties:
@ -108,6 +110,7 @@ allOf:
- mediatek,mt6779-smi-larb
- mediatek,mt8167-smi-larb
- mediatek,mt8192-smi-larb
- mediatek,mt8195-smi-larb
then:
required:

View File

@ -164,12 +164,20 @@ patternProperties:
"#size-cells":
const: 0
lpddr2:
$ref: "ddr/jedec,lpddr2.yaml#"
type: object
patternProperties:
"^emc-table@[0-9]+$":
$ref: "#/$defs/emc-table"
required:
- nvidia,ram-code
oneOf:
- required:
- nvidia,ram-code
- required:
- lpddr2
additionalProperties: false
@ -227,4 +235,15 @@ examples:
0x00000000 0x00000000 0x00000000 0x00000000>;
};
};
emc-tables@1 {
reg = <1>;
lpddr2 {
compatible = "elpida,B8132B2PB-6D-F", "jedec,lpddr2-s4";
revision-id1 = <1>;
density = <2048>;
io-width = <16>;
};
};
};

View File

@ -33,6 +33,7 @@ properties:
- renesas,r8a77970-rpc-if # R-Car V3M
- renesas,r8a77980-rpc-if # R-Car V3H
- renesas,r8a77995-rpc-if # R-Car D3
- renesas,r8a779a0-rpc-if # R-Car V3U
- const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2 device
reg:

View File

@ -51,7 +51,8 @@ properties:
$ref: '/schemas/types.yaml#/definitions/phandle'
description: |
phandle of the connected DRAM memory device. For more information please
refer to documentation file: Documentation/devicetree/bindings/ddr/lpddr3.txt
refer to documentation file:
Documentation/devicetree/bindings/memory-controllers/ddr/lpddr3.txt
operating-points-v2: true

View File

@ -19,6 +19,7 @@ properties:
- qcom,mdm9607-rpmpd
- qcom,msm8916-rpmpd
- qcom,msm8939-rpmpd
- qcom,msm8953-rpmpd
- qcom,msm8976-rpmpd
- qcom,msm8994-rpmpd
- qcom,msm8996-rpmpd
@ -31,6 +32,7 @@ properties:
- qcom,sdm845-rpmhpd
- qcom,sdx55-rpmhpd
- qcom,sm6115-rpmpd
- qcom,sm6350-rpmhpd
- qcom,sm8150-rpmhpd
- qcom,sm8250-rpmhpd
- qcom,sm8350-rpmhpd

View File

@ -20,7 +20,9 @@ properties:
pattern: "^reset-controller@[0-9a-f]+$"
compatible:
const: microchip,sparx5-switch-reset
enum:
- microchip,sparx5-switch-reset
- microchip,lan966x-switch-reset
reg:
items:

View File

@ -23,6 +23,7 @@ properties:
- socionext,uniphier-pxs2-usb3-reset
- socionext,uniphier-ld20-usb3-reset
- socionext,uniphier-pxs3-usb3-reset
- socionext,uniphier-nx1-usb3-reset
- socionext,uniphier-pro4-ahci-reset
- socionext,uniphier-pxs2-ahci-reset
- socionext,uniphier-pxs3-ahci-reset

View File

@ -23,6 +23,7 @@ properties:
- socionext,uniphier-ld11-reset
- socionext,uniphier-ld20-reset
- socionext,uniphier-pxs3-reset
- socionext,uniphier-nx1-reset
- description: Media I/O (MIO) reset, SD reset
enum:
- socionext,uniphier-ld4-mio-reset
@ -34,6 +35,7 @@ properties:
- socionext,uniphier-ld11-sd-reset
- socionext,uniphier-ld20-sd-reset
- socionext,uniphier-pxs3-sd-reset
- socionext,uniphier-nx1-sd-reset
- description: Peripheral reset
enum:
- socionext,uniphier-ld4-peri-reset
@ -44,6 +46,7 @@ properties:
- socionext,uniphier-ld11-peri-reset
- socionext,uniphier-ld20-peri-reset
- socionext,uniphier-pxs3-peri-reset
- socionext,uniphier-nx1-peri-reset
- description: Analog signal amplifier reset
enum:
- socionext,uniphier-ld11-adamv-reset

View File

@ -19,8 +19,7 @@ description:
The AOSS side channel exposes control over a set of resources, used to control
a set of debug related clocks and to affect the low power state of resources
related to the secondary subsystems. These resources are exposed as a set of
power-domains.
related to the secondary subsystems.
properties:
compatible:
@ -30,6 +29,7 @@ properties:
- qcom,sc7280-aoss-qmp
- qcom,sc8180x-aoss-qmp
- qcom,sdm845-aoss-qmp
- qcom,sm6350-aoss-qmp
- qcom,sm8150-aoss-qmp
- qcom,sm8250-aoss-qmp
- qcom,sm8350-aoss-qmp
@ -57,13 +57,6 @@ properties:
description:
The single clock represents the QDSS clock.
"#power-domain-cells":
const: 1
description: |
The provided power-domains are:
CDSP state (0), LPASS state (1), modem state (2), SLPI
state (3), SPSS state (4) and Venus state (5).
required:
- compatible
- reg
@ -101,7 +94,6 @@ examples:
mboxes = <&apss_shared 0>;
#clock-cells = <0>;
#power-domain-cells = <1>;
cx_cdev: cx {
#cooling-cells = <2>;

View File

@ -34,6 +34,7 @@ properties:
- qcom,rpm-ipq6018
- qcom,rpm-msm8226
- qcom,rpm-msm8916
- qcom,rpm-msm8953
- qcom,rpm-msm8974
- qcom,rpm-msm8976
- qcom,rpm-msm8996
@ -41,6 +42,7 @@ properties:
- qcom,rpm-sdm660
- qcom,rpm-sm6115
- qcom,rpm-sm6125
- qcom,rpm-qcm2290
- qcom,rpm-qcs404
qcom,smd-channels:
@ -57,6 +59,7 @@ if:
- qcom,rpm-apq8084
- qcom,rpm-msm8916
- qcom,rpm-msm8974
- qcom,rpm-msm8953
then:
required:
- qcom,smd-channels

View File

@ -10,14 +10,18 @@ maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
description: |
This binding describes the Qualcomm Shared Memory Manager, used to share data
between various subsystems and OSes in Qualcomm platforms.
description:
This binding describes the Qualcomm Shared Memory Manager, a region of
reserved-memory used to share data between various subsystems and OSes in
Qualcomm platforms.
properties:
compatible:
const: qcom,smem
reg:
maxItems: 1
memory-region:
maxItems: 1
description: handle to memory reservation for main SMEM memory region.
@ -29,11 +33,19 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle
description: handle to RPM message memory resource
no-map: true
required:
- compatible
- memory-region
- hwlocks
oneOf:
- required:
- reg
- no-map
- required:
- memory-region
additionalProperties: false
examples:
@ -43,6 +55,20 @@ examples:
#size-cells = <1>;
ranges;
smem@fa00000 {
compatible = "qcom,smem";
reg = <0xfa00000 0x200000>;
no-map;
hwlocks = <&tcsr_mutex 3>;
};
};
- |
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
smem_region: smem@fa00000 {
reg = <0xfa00000 0x200000>;
no-map;

View File

@ -0,0 +1,81 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Qualcomm Subsystem Power Manager binding
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
description: |
This binding describes the Qualcomm Subsystem Power Manager, used to control
the peripheral logic surrounding the application cores in Qualcomm platforms.
properties:
compatible:
items:
- enum:
- qcom,sdm660-gold-saw2-v4.1-l2
- qcom,sdm660-silver-saw2-v4.1-l2
- qcom,msm8998-gold-saw2-v4.1-l2
- qcom,msm8998-silver-saw2-v4.1-l2
- qcom,msm8916-saw2-v3.0-cpu
- qcom,msm8226-saw2-v2.1-cpu
- qcom,msm8974-saw2-v2.1-cpu
- qcom,apq8084-saw2-v2.1-cpu
- qcom,apq8064-saw2-v1.1-cpu
- const: qcom,saw2
reg:
description: Base address and size of the SPM register region
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
/* Example 1: SoC using SAW2 and kpss-acc-v2 CPUIdle */
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "qcom,kryo";
device_type = "cpu";
enable-method = "qcom,kpss-acc-v2";
qcom,saw = <&saw0>;
reg = <0x0>;
operating-points-v2 = <&cpu_opp_table>;
};
};
saw0: power-manager@f9089000 {
compatible = "qcom,msm8974-saw2-v2.1-cpu", "qcom,saw2";
reg = <0xf9089000 0x1000>;
};
- |
/*
* Example 2: New-gen multi cluster SoC using SAW only for L2;
* This does not require any cpuidle driver, nor any cpu phandle.
*/
power-manager@17812000 {
compatible = "qcom,msm8998-gold-saw2-v4.1-l2", "qcom,saw2";
reg = <0x17812000 0x1000>;
};
power-manager@17912000 {
compatible = "qcom,msm8998-silver-saw2-v4.1-l2", "qcom,saw2";
reg = <0x17912000 0x1000>;
};
...

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/soc/qcom/qcom-stats.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. (QTI) Stats bindings
maintainers:
- Maulik Shah <mkshah@codeaurora.org>
description:
Always On Processor/Resource Power Manager maintains statistics of the SoC
sleep modes involving powering down of the rails and oscillator clock.
Statistics includes SoC sleep mode type, number of times low power mode were
entered, time of last entry, time of last exit and accumulated sleep duration.
properties:
compatible:
enum:
- qcom,rpmh-stats
- qcom,rpm-stats
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
# Example of rpmh sleep stats
- |
sram@c3f0000 {
compatible = "qcom,rpmh-stats";
reg = <0x0c3f0000 0x400>;
};
# Example of rpm sleep stats
- |
sram@4690000 {
compatible = "qcom,rpm-stats";
reg = <0x04690000 0x10000>;
};
...

View File

@ -31,6 +31,7 @@ properties:
- amlogic,meson-gxbb-sram
- arm,juno-sram-ns
- atmel,sama5d2-securam
- qcom,rpm-msg-ram
- rockchip,rk3288-pmu-sram
reg:
@ -135,7 +136,9 @@ if:
properties:
compatible:
contains:
const: rockchip,rk3288-pmu-sram
enum:
- qcom,rpm-msg-ram
- rockchip,rk3288-pmu-sram
else:
required:

View File

@ -359,6 +359,8 @@ patternProperties:
description: Shenzhen Elida Technology Co., Ltd.
"^elimo,.*":
description: Elimo Engineering Ltd.
"^elpida,.*":
description: Elpida Memory, Inc.
"^embest,.*":
description: Shenzhen Embest Technology Co., Ltd.
"^emlid,.*":

View File

@ -11992,6 +11992,14 @@ M: Sean Wang <sean.wang@mediatek.com>
S: Maintained
F: drivers/char/hw_random/mtk-rng.c
MEDIATEK SMI DRIVER
M: Yong Wu <yong.wu@mediatek.com>
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
S: Supported
F: Documentation/devicetree/bindings/memory-controllers/mediatek,smi*
F: drivers/memory/mtk-smi.c
F: include/soc/mediatek/smi.h
MEDIATEK SWITCH DRIVER
M: Sean Wang <sean.wang@mediatek.com>
M: Landen Chao <Landen.Chao@mediatek.com>

View File

@ -351,7 +351,7 @@ config ARCH_EP93XX
select CLKSRC_MMIO
select CPU_ARM920T
select GPIOLIB
select HAVE_LEGACY_CLK
select COMMON_CLK
help
This enables support for the Cirrus EP93xx series of CPUs.
@ -480,7 +480,6 @@ config ARCH_S3C24XX
select GPIOLIB
select GENERIC_IRQ_MULTI_HANDLER
select HAVE_S3C2410_I2C if I2C
select HAVE_S3C_RTC if RTC_CLASS
select NEED_MACH_IO_H
select S3C2410_WATCHDOG
select SAMSUNG_ATAGS

File diff suppressed because it is too large Load Diff

View File

@ -214,7 +214,7 @@ static int ep93xx_ohci_power_on(struct platform_device *pdev)
return PTR_ERR(ep93xx_ohci_host_clock);
}
return clk_enable(ep93xx_ohci_host_clock);
return clk_prepare_enable(ep93xx_ohci_host_clock);
}
static void ep93xx_ohci_power_off(struct platform_device *pdev)

View File

@ -111,19 +111,19 @@
#define EP93XX_SYSCON_PWRCNT EP93XX_SYSCON_REG(0x04)
#define EP93XX_SYSCON_PWRCNT_FIR_EN (1<<31)
#define EP93XX_SYSCON_PWRCNT_UARTBAUD (1<<29)
#define EP93XX_SYSCON_PWRCNT_USH_EN (1<<28)
#define EP93XX_SYSCON_PWRCNT_DMA_M2M1 (1<<27)
#define EP93XX_SYSCON_PWRCNT_DMA_M2M0 (1<<26)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P8 (1<<25)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P9 (1<<24)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P6 (1<<23)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P7 (1<<22)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P4 (1<<21)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P5 (1<<20)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P2 (1<<19)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P3 (1<<18)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P0 (1<<17)
#define EP93XX_SYSCON_PWRCNT_DMA_M2P1 (1<<16)
#define EP93XX_SYSCON_PWRCNT_USH_EN 28
#define EP93XX_SYSCON_PWRCNT_DMA_M2M1 27
#define EP93XX_SYSCON_PWRCNT_DMA_M2M0 26
#define EP93XX_SYSCON_PWRCNT_DMA_M2P8 25
#define EP93XX_SYSCON_PWRCNT_DMA_M2P9 24
#define EP93XX_SYSCON_PWRCNT_DMA_M2P6 23
#define EP93XX_SYSCON_PWRCNT_DMA_M2P7 22
#define EP93XX_SYSCON_PWRCNT_DMA_M2P4 21
#define EP93XX_SYSCON_PWRCNT_DMA_M2P5 20
#define EP93XX_SYSCON_PWRCNT_DMA_M2P2 19
#define EP93XX_SYSCON_PWRCNT_DMA_M2P3 18
#define EP93XX_SYSCON_PWRCNT_DMA_M2P0 17
#define EP93XX_SYSCON_PWRCNT_DMA_M2P1 16
#define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08)
#define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c)
#define EP93XX_SYSCON_CLKSET1 EP93XX_SYSCON_REG(0x20)
@ -139,13 +139,13 @@
#define EP93XX_SYSCON_DEVCFG_GONK (1<<27)
#define EP93XX_SYSCON_DEVCFG_TONG (1<<26)
#define EP93XX_SYSCON_DEVCFG_MONG (1<<25)
#define EP93XX_SYSCON_DEVCFG_U3EN (1<<24)
#define EP93XX_SYSCON_DEVCFG_U3EN 24
#define EP93XX_SYSCON_DEVCFG_CPENA (1<<23)
#define EP93XX_SYSCON_DEVCFG_A2ONG (1<<22)
#define EP93XX_SYSCON_DEVCFG_A1ONG (1<<21)
#define EP93XX_SYSCON_DEVCFG_U2EN (1<<20)
#define EP93XX_SYSCON_DEVCFG_U2EN 20
#define EP93XX_SYSCON_DEVCFG_EXVC (1<<19)
#define EP93XX_SYSCON_DEVCFG_U1EN (1<<18)
#define EP93XX_SYSCON_DEVCFG_U1EN 18
#define EP93XX_SYSCON_DEVCFG_TIN (1<<17)
#define EP93XX_SYSCON_DEVCFG_HC3IN (1<<15)
#define EP93XX_SYSCON_DEVCFG_HC3EN (1<<14)
@ -163,12 +163,12 @@
#define EP93XX_SYSCON_DEVCFG_KEYS (1<<1)
#define EP93XX_SYSCON_DEVCFG_SHENA (1<<0)
#define EP93XX_SYSCON_VIDCLKDIV EP93XX_SYSCON_REG(0x84)
#define EP93XX_SYSCON_CLKDIV_ENABLE (1<<15)
#define EP93XX_SYSCON_CLKDIV_ENABLE 15
#define EP93XX_SYSCON_CLKDIV_ESEL (1<<14)
#define EP93XX_SYSCON_CLKDIV_PSEL (1<<13)
#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8
#define EP93XX_SYSCON_I2SCLKDIV EP93XX_SYSCON_REG(0x8c)
#define EP93XX_SYSCON_I2SCLKDIV_SENA (1<<31)
#define EP93XX_SYSCON_I2SCLKDIV_SENA 31
#define EP93XX_SYSCON_I2SCLKDIV_ORIDE (1<<29)
#define EP93XX_SYSCON_I2SCLKDIV_SPOL (1<<19)
#define EP93XX_I2SCLKDIV_SDIV (1 << 16)
@ -177,9 +177,9 @@
#define EP93XX_I2SCLKDIV_LRDIV128 (2 << 17)
#define EP93XX_I2SCLKDIV_LRDIV_MASK (3 << 17)
#define EP93XX_SYSCON_KEYTCHCLKDIV EP93XX_SYSCON_REG(0x90)
#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN (1<<31)
#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV (1<<16)
#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN (1<<15)
#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN 31
#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV 16
#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN 15
#define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV (1<<0)
#define EP93XX_SYSCON_SYSCFG EP93XX_SYSCON_REG(0x9c)
#define EP93XX_SYSCON_SYSCFG_REV_MASK (0xf0000000)

View File

@ -13,7 +13,6 @@ menuconfig ARCH_EXYNOS
select ARM_GIC
select EXYNOS_IRQ_COMBINER
select COMMON_CLK_SAMSUNG
select EXYNOS_CHIPID
select EXYNOS_THERMAL
select EXYNOS_PMU
select EXYNOS_SROM
@ -22,7 +21,6 @@ menuconfig ARCH_EXYNOS
select HAVE_ARM_ARCH_TIMER if ARCH_EXYNOS5
select HAVE_ARM_SCU if SMP
select HAVE_S3C2410_I2C if I2C
select HAVE_S3C_RTC if RTC_CLASS
select PINCTRL
select PINCTRL_EXYNOS
select PM_GENERIC_DOMAINS if PM

View File

@ -29,6 +29,7 @@
#define COREPOR_RST BIT(5)
#define CORE_RST BIT(4)
#define L2DT_SLP BIT(3)
#define CORE_MEM_CLAMP BIT(1)
#define CLAMP BIT(0)
#define APC_PWR_GATE_CTL 0x14
@ -75,6 +76,62 @@ static int scss_release_secondary(unsigned int cpu)
return 0;
}
static int cortex_a7_release_secondary(unsigned int cpu)
{
int ret = 0;
void __iomem *reg;
struct device_node *cpu_node, *acc_node;
u32 reg_val;
cpu_node = of_get_cpu_node(cpu, NULL);
if (!cpu_node)
return -ENODEV;
acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
if (!acc_node) {
ret = -ENODEV;
goto out_acc;
}
reg = of_iomap(acc_node, 0);
if (!reg) {
ret = -ENOMEM;
goto out_acc_map;
}
/* Put the CPU into reset. */
reg_val = CORE_RST | COREPOR_RST | CLAMP | CORE_MEM_CLAMP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
/* Turn on the BHS and set the BHS_CNT to 16 XO clock cycles */
writel(BHS_EN | (0x10 << BHS_CNT_SHIFT), reg + APC_PWR_GATE_CTL);
/* Wait for the BHS to settle */
udelay(2);
reg_val &= ~CORE_MEM_CLAMP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
reg_val |= L2DT_SLP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
udelay(2);
reg_val = (reg_val | BIT(17)) & ~CLAMP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
udelay(2);
/* Release CPU out of reset and bring it to life. */
reg_val &= ~(CORE_RST | COREPOR_RST);
writel(reg_val, reg + APCS_CPU_PWR_CTL);
reg_val |= CORE_PWRD_UP;
writel(reg_val, reg + APCS_CPU_PWR_CTL);
iounmap(reg);
out_acc_map:
of_node_put(acc_node);
out_acc:
of_node_put(cpu_node);
return ret;
}
static int kpssv1_release_secondary(unsigned int cpu)
{
int ret = 0;
@ -281,6 +338,11 @@ static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
return qcom_boot_secondary(cpu, scss_release_secondary);
}
static int cortex_a7_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
return qcom_boot_secondary(cpu, cortex_a7_release_secondary);
}
static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
return qcom_boot_secondary(cpu, kpssv1_release_secondary);
@ -315,6 +377,16 @@ static const struct smp_operations smp_msm8660_ops __initconst = {
};
CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
static const struct smp_operations qcom_smp_cortex_a7_ops __initconst = {
.smp_prepare_cpus = qcom_smp_prepare_cpus,
.smp_boot_secondary = cortex_a7_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = qcom_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp_msm8226, "qcom,msm8226-smp", &qcom_smp_cortex_a7_ops);
CPU_METHOD_OF_DECLARE(qcom_smp_msm8916, "qcom,msm8916-smp", &qcom_smp_cortex_a7_ops);
static const struct smp_operations qcom_smp_kpssv1_ops __initconst = {
.smp_prepare_cpus = qcom_smp_prepare_cpus,
.smp_boot_secondary = kpssv1_boot_secondary,

View File

@ -13,7 +13,6 @@ config ARCH_S5PV210
select COMMON_CLK_SAMSUNG
select GPIOLIB
select HAVE_S3C2410_I2C if I2C
select HAVE_S3C_RTC if RTC_CLASS
select PINCTRL
select PINCTRL_EXYNOS
select SOC_SAMSUNG

View File

@ -89,10 +89,8 @@ config ARCH_BRCMSTB
config ARCH_EXYNOS
bool "ARMv8 based Samsung Exynos SoC family"
select COMMON_CLK_SAMSUNG
select EXYNOS_CHIPID
select EXYNOS_PM_DOMAINS if PM_GENERIC_DOMAINS
select EXYNOS_PMU
select HAVE_S3C_RTC if RTC_CLASS
select PINCTRL
select PINCTRL_EXYNOS
select PM_GENERIC_DOMAINS if PM

View File

@ -30,7 +30,7 @@ config ARM_INTEGRATOR_LM
found on the ARM Integrator AP (Application Platform)
config BRCMSTB_GISB_ARB
bool "Broadcom STB GISB bus arbiter"
tristate "Broadcom STB GISB bus arbiter"
depends on ARM || ARM64 || MIPS
default ARCH_BRCMSTB || BMIPS_GENERIC
help

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014-2017 Broadcom
* Copyright (C) 2014-2021 Broadcom
*/
#include <linux/init.h>
@ -536,6 +536,7 @@ static struct platform_driver brcmstb_gisb_arb_driver = {
.name = "brcm-gisb-arb",
.of_match_table = brcmstb_gisb_arb_of_match,
.pm = &brcmstb_gisb_arb_pm_ops,
.suppress_bind_attrs = true,
},
};
@ -546,3 +547,7 @@ static int __init brcm_gisb_driver_init(void)
}
module_init(brcm_gisb_driver_init);
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("Broadcom STB GISB arbiter driver");
MODULE_LICENSE("GPL v2");

View File

@ -15,10 +15,9 @@ static int sun50i_de2_bus_probe(struct platform_device *pdev)
int ret;
ret = sunxi_sram_claim(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Error couldn't map SRAM to device\n");
return ret;
}
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Couldn't map SRAM to device\n");
of_platform_populate(np, NULL, NULL, &pdev->dev);

View File

@ -6,6 +6,7 @@
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/module.h>
@ -17,6 +18,7 @@
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/timekeeping.h>
#include <linux/iopoll.h>
#include <linux/platform_data/ti-sysc.h>
@ -51,11 +53,18 @@ struct sysc_address {
struct list_head node;
};
struct sysc_module {
struct sysc *ddata;
struct list_head node;
};
struct sysc_soc_info {
unsigned long general_purpose:1;
enum sysc_soc soc;
struct mutex list_lock; /* disabled modules list lock */
struct mutex list_lock; /* disabled and restored modules list lock */
struct list_head disabled_modules;
struct list_head restored_modules;
struct notifier_block nb;
};
enum sysc_clocks {
@ -131,6 +140,7 @@ struct sysc {
struct ti_sysc_cookie cookie;
const char *name;
u32 revision;
u32 sysconfig;
unsigned int reserved:1;
unsigned int enabled:1;
unsigned int needs_resume:1;
@ -147,6 +157,7 @@ struct sysc {
static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
bool is_child);
static int sysc_reset(struct sysc *ddata);
static void sysc_write(struct sysc *ddata, int offset, u32 value)
{
@ -223,37 +234,77 @@ static u32 sysc_read_sysstatus(struct sysc *ddata)
return sysc_read(ddata, offset);
}
/* Poll on reset status */
static int sysc_wait_softreset(struct sysc *ddata)
static int sysc_poll_reset_sysstatus(struct sysc *ddata)
{
u32 sysc_mask, syss_done, rstval;
int syss_offset, error = 0;
if (ddata->cap->regbits->srst_shift < 0)
return 0;
syss_offset = ddata->offsets[SYSC_SYSSTATUS];
sysc_mask = BIT(ddata->cap->regbits->srst_shift);
int error, retries;
u32 syss_done, rstval;
if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED)
syss_done = 0;
else
syss_done = ddata->cfg.syss_mask;
if (syss_offset >= 0) {
if (likely(!timekeeping_suspended)) {
error = readx_poll_timeout_atomic(sysc_read_sysstatus, ddata,
rstval, (rstval & ddata->cfg.syss_mask) ==
syss_done, 100, MAX_MODULE_SOFTRESET_WAIT);
} else {
retries = MAX_MODULE_SOFTRESET_WAIT;
while (retries--) {
rstval = sysc_read_sysstatus(ddata);
if ((rstval & ddata->cfg.syss_mask) == syss_done)
return 0;
udelay(2); /* Account for udelay flakeyness */
}
error = -ETIMEDOUT;
}
} else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) {
return error;
}
static int sysc_poll_reset_sysconfig(struct sysc *ddata)
{
int error, retries;
u32 sysc_mask, rstval;
sysc_mask = BIT(ddata->cap->regbits->srst_shift);
if (likely(!timekeeping_suspended)) {
error = readx_poll_timeout_atomic(sysc_read_sysconfig, ddata,
rstval, !(rstval & sysc_mask),
100, MAX_MODULE_SOFTRESET_WAIT);
} else {
retries = MAX_MODULE_SOFTRESET_WAIT;
while (retries--) {
rstval = sysc_read_sysconfig(ddata);
if (!(rstval & sysc_mask))
return 0;
udelay(2); /* Account for udelay flakeyness */
}
error = -ETIMEDOUT;
}
return error;
}
/* Poll on reset status */
static int sysc_wait_softreset(struct sysc *ddata)
{
int syss_offset, error = 0;
if (ddata->cap->regbits->srst_shift < 0)
return 0;
syss_offset = ddata->offsets[SYSC_SYSSTATUS];
if (syss_offset >= 0)
error = sysc_poll_reset_sysstatus(ddata);
else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS)
error = sysc_poll_reset_sysconfig(ddata);
return error;
}
static int sysc_add_named_clock_from_child(struct sysc *ddata,
const char *name,
const char *optfck_name)
@ -1094,7 +1145,8 @@ set_midle:
best_mode = fls(ddata->cfg.midlemodes) - 1;
if (best_mode > SYSC_IDLE_MASK) {
dev_err(dev, "%s: invalid midlemode\n", __func__);
return -EINVAL;
error = -EINVAL;
goto save_context;
}
if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY)
@ -1112,13 +1164,16 @@ set_autoidle:
sysc_write_sysconfig(ddata, reg);
}
/* Flush posted write */
sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
error = 0;
save_context:
/* Save context and flush posted write */
ddata->sysconfig = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
if (ddata->module_enable_quirk)
ddata->module_enable_quirk(ddata);
return 0;
return error;
}
static int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode)
@ -1175,8 +1230,10 @@ static int sysc_disable_module(struct device *dev)
set_sidle:
/* Set SIDLE mode */
idlemodes = ddata->cfg.sidlemodes;
if (!idlemodes || regbits->sidle_shift < 0)
return 0;
if (!idlemodes || regbits->sidle_shift < 0) {
ret = 0;
goto save_context;
}
if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE) {
best_mode = SYSC_IDLE_FORCE;
@ -1184,7 +1241,8 @@ set_sidle:
ret = sysc_best_idle_mode(idlemodes, &best_mode);
if (ret) {
dev_err(dev, "%s: invalid sidlemode\n", __func__);
return ret;
ret = -EINVAL;
goto save_context;
}
}
@ -1195,10 +1253,13 @@ set_sidle:
reg |= 1 << regbits->autoidle_shift;
sysc_write_sysconfig(ddata, reg);
/* Flush posted write */
sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
ret = 0;
return 0;
save_context:
/* Save context and flush posted write */
ddata->sysconfig = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
return ret;
}
static int __maybe_unused sysc_runtime_suspend_legacy(struct device *dev,
@ -1336,13 +1397,40 @@ err_allow_idle:
return error;
}
/*
* Checks if device context was lost. Assumes the sysconfig register value
* after lost context is different from the configured value. Only works for
* enabled devices.
*
* Eventually we may want to also add support to using the context lost
* registers that some SoCs have.
*/
static int sysc_check_context(struct sysc *ddata)
{
u32 reg;
if (!ddata->enabled)
return -ENODATA;
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
if (reg == ddata->sysconfig)
return 0;
return -EACCES;
}
static int sysc_reinit_module(struct sysc *ddata, bool leave_enabled)
{
struct device *dev = ddata->dev;
int error;
/* Disable target module if it is enabled */
if (ddata->enabled) {
/* Nothing to do if enabled and context not lost */
error = sysc_check_context(ddata);
if (!error)
return 0;
/* Disable target module if it is enabled */
error = sysc_runtime_suspend(dev);
if (error)
dev_warn(dev, "reinit suspend failed: %i\n", error);
@ -1353,6 +1441,15 @@ static int sysc_reinit_module(struct sysc *ddata, bool leave_enabled)
if (error)
dev_warn(dev, "reinit resume failed: %i\n", error);
/* Some modules like am335x gpmc need reset and restore of sysconfig */
if (ddata->cfg.quirks & SYSC_QUIRK_RESET_ON_CTX_LOST) {
error = sysc_reset(ddata);
if (error)
dev_warn(dev, "reinit reset failed: %i\n", error);
sysc_write_sysconfig(ddata, ddata->sysconfig);
}
if (leave_enabled)
return error;
@ -1442,10 +1539,6 @@ struct sysc_revision_quirk {
static const struct sysc_revision_quirk sysc_revision_quirks[] = {
/* These drivers need to be fixed to not use pm_runtime_irq_safe() */
SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff00ff,
SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
@ -1479,7 +1572,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK_CLKDM_NOAUTO),
SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff,
SYSC_QUIRK_CLKDM_NOAUTO),
SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff00ff,
SYSC_QUIRK_OPT_CLKS_IN_RESET),
SYSC_QUIRK("gpmc", 0, 0, 0x10, 0x14, 0x00000060, 0xffffffff,
SYSC_QUIRK_REINIT_ON_CTX_LOST | SYSC_QUIRK_RESET_ON_CTX_LOST |
SYSC_QUIRK_GPMC_DEBUG),
SYSC_QUIRK("hdmi", 0, 0, 0x10, -ENODEV, 0x50030200, 0xffffffff,
SYSC_QUIRK_OPT_CLKS_NEEDED),
@ -1515,10 +1611,11 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
SYSC_MODULE_QUIRK_OTG),
SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -ENODEV, 0x4ea2080d, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
SYSC_QUIRK_REINIT_ON_RESUME),
SYSC_QUIRK_REINIT_ON_CTX_LOST),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0,
SYSC_MODULE_QUIRK_WDT),
/* PRUSS on am3, am4 and am5 */
@ -1583,6 +1680,7 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("sdio", 0, 0, 0x10, -ENODEV, 0x40202301, 0xffff0ff0, 0),
SYSC_QUIRK("sdio", 0, 0x2fc, 0x110, 0x114, 0x31010000, 0xffffffff, 0),
SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0),
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff, 0),
SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40000902, 0xffffffff, 0),
SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0),
SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff, 0),
@ -1874,6 +1972,22 @@ static void sysc_module_lock_quirk_rtc(struct sysc *ddata)
sysc_quirk_rtc(ddata, true);
}
/* OTG omap2430 glue layer up to omap4 needs OTG_FORCESTDBY configured */
static void sysc_module_enable_quirk_otg(struct sysc *ddata)
{
int offset = 0x414; /* OTG_FORCESTDBY */
sysc_write(ddata, offset, 0);
}
static void sysc_module_disable_quirk_otg(struct sysc *ddata)
{
int offset = 0x414; /* OTG_FORCESTDBY */
u32 val = BIT(0); /* ENABLEFORCE */
sysc_write(ddata, offset, val);
}
/* 36xx SGX needs a quirk for to bypass OCP IPG interrupt logic */
static void sysc_module_enable_quirk_sgx(struct sysc *ddata)
{
@ -1956,6 +2070,11 @@ static void sysc_init_module_quirks(struct sysc *ddata)
return;
}
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_OTG) {
ddata->module_enable_quirk = sysc_module_enable_quirk_otg;
ddata->module_disable_quirk = sysc_module_disable_quirk_otg;
}
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_SGX)
ddata->module_enable_quirk = sysc_module_enable_quirk_sgx;
@ -2401,6 +2520,78 @@ static struct dev_pm_domain sysc_child_pm_domain = {
}
};
/* Caller needs to take list_lock if ever used outside of cpu_pm */
static void sysc_reinit_modules(struct sysc_soc_info *soc)
{
struct sysc_module *module;
struct list_head *pos;
struct sysc *ddata;
list_for_each(pos, &sysc_soc->restored_modules) {
module = list_entry(pos, struct sysc_module, node);
ddata = module->ddata;
sysc_reinit_module(ddata, ddata->enabled);
}
}
/**
* sysc_context_notifier - optionally reset and restore module after idle
* @nb: notifier block
* @cmd: unused
* @v: unused
*
* Some interconnect target modules need to be restored, or reset and restored
* on CPU_PM CPU_PM_CLUSTER_EXIT notifier. This is needed at least for am335x
* OTG and GPMC target modules even if the modules are unused.
*/
static int sysc_context_notifier(struct notifier_block *nb, unsigned long cmd,
void *v)
{
struct sysc_soc_info *soc;
soc = container_of(nb, struct sysc_soc_info, nb);
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
break;
case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */
break;
case CPU_CLUSTER_PM_EXIT:
sysc_reinit_modules(soc);
break;
}
return NOTIFY_OK;
}
/**
* sysc_add_restored - optionally add reset and restore quirk hanlling
* @ddata: device data
*/
static void sysc_add_restored(struct sysc *ddata)
{
struct sysc_module *restored_module;
restored_module = kzalloc(sizeof(*restored_module), GFP_KERNEL);
if (!restored_module)
return;
restored_module->ddata = ddata;
mutex_lock(&sysc_soc->list_lock);
list_add(&restored_module->node, &sysc_soc->restored_modules);
if (sysc_soc->nb.notifier_call)
goto out_unlock;
sysc_soc->nb.notifier_call = sysc_context_notifier;
cpu_pm_register_notifier(&sysc_soc->nb);
out_unlock:
mutex_unlock(&sysc_soc->list_lock);
}
/**
* sysc_legacy_idle_quirk - handle children in omap_device compatible way
* @ddata: device driver data
@ -2900,12 +3091,14 @@ static int sysc_add_disabled(unsigned long base)
}
/*
* One time init to detect the booted SoC and disable unavailable features.
* One time init to detect the booted SoC, disable unavailable features
* and initialize list for optional cpu_pm notifier.
*
* Note that we initialize static data shared across all ti-sysc instances
* so ddata is only used for SoC type. This can be called from module_init
* once we no longer need to rely on platform data.
*/
static int sysc_init_soc(struct sysc *ddata)
static int sysc_init_static_data(struct sysc *ddata)
{
const struct soc_device_attribute *match;
struct ti_sysc_platform_data *pdata;
@ -2921,6 +3114,7 @@ static int sysc_init_soc(struct sysc *ddata)
mutex_init(&sysc_soc->list_lock);
INIT_LIST_HEAD(&sysc_soc->disabled_modules);
INIT_LIST_HEAD(&sysc_soc->restored_modules);
sysc_soc->general_purpose = true;
pdata = dev_get_platdata(ddata->dev);
@ -2985,15 +3179,24 @@ static int sysc_init_soc(struct sysc *ddata)
return 0;
}
static void sysc_cleanup_soc(void)
static void sysc_cleanup_static_data(void)
{
struct sysc_module *restored_module;
struct sysc_address *disabled_module;
struct list_head *pos, *tmp;
if (!sysc_soc)
return;
if (sysc_soc->nb.notifier_call)
cpu_pm_unregister_notifier(&sysc_soc->nb);
mutex_lock(&sysc_soc->list_lock);
list_for_each_safe(pos, tmp, &sysc_soc->restored_modules) {
restored_module = list_entry(pos, struct sysc_module, node);
list_del(pos);
kfree(restored_module);
}
list_for_each_safe(pos, tmp, &sysc_soc->disabled_modules) {
disabled_module = list_entry(pos, struct sysc_address, node);
list_del(pos);
@ -3061,7 +3264,7 @@ static int sysc_probe(struct platform_device *pdev)
ddata->dev = &pdev->dev;
platform_set_drvdata(pdev, ddata);
error = sysc_init_soc(ddata);
error = sysc_init_static_data(ddata);
if (error)
return error;
@ -3159,6 +3362,9 @@ static int sysc_probe(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
}
if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_CTX_LOST)
sysc_add_restored(ddata);
return 0;
err:
@ -3240,7 +3446,7 @@ static void __exit sysc_exit(void)
{
bus_unregister_notifier(&platform_bus_type, &sysc_nb);
platform_driver_unregister(&sysc_driver);
sysc_cleanup_soc();
sysc_cleanup_static_data();
}
module_exit(sysc_exit);

View File

@ -99,7 +99,7 @@ config ARM_MVEBU_V7_CPUIDLE
config ARM_TEGRA_CPUIDLE
bool "CPU Idle Driver for NVIDIA Tegra SoCs"
depends on ARCH_TEGRA && !ARM64
depends on (ARCH_TEGRA || COMPILE_TEST) && !ARM64 && MMU
select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
select ARM_CPU_SUSPEND
help
@ -112,6 +112,7 @@ config ARM_QCOM_SPM_CPUIDLE
select CPU_IDLE_MULTIPLE_DRIVERS
select DT_IDLE_STATES
select QCOM_SCM
select QCOM_SPM
help
Select this to enable cpuidle for Qualcomm processors.
The Subsystem Power Manager (SPM) controls low power modes for the

View File

@ -18,158 +18,18 @@
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/qcom_scm.h>
#include <soc/qcom/spm.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h>
#include "dt_idle_states.h"
#define MAX_PMIC_DATA 2
#define MAX_SEQ_DATA 64
#define SPM_CTL_INDEX 0x7f
#define SPM_CTL_INDEX_SHIFT 4
#define SPM_CTL_EN BIT(0)
enum pm_sleep_mode {
PM_SLEEP_MODE_STBY,
PM_SLEEP_MODE_RET,
PM_SLEEP_MODE_SPC,
PM_SLEEP_MODE_PC,
PM_SLEEP_MODE_NR,
};
enum spm_reg {
SPM_REG_CFG,
SPM_REG_SPM_CTL,
SPM_REG_DLY,
SPM_REG_PMIC_DLY,
SPM_REG_PMIC_DATA_0,
SPM_REG_PMIC_DATA_1,
SPM_REG_VCTL,
SPM_REG_SEQ_ENTRY,
SPM_REG_SPM_STS,
SPM_REG_PMIC_STS,
SPM_REG_NR,
};
struct spm_reg_data {
const u8 *reg_offset;
u32 spm_cfg;
u32 spm_dly;
u32 pmic_dly;
u32 pmic_data[MAX_PMIC_DATA];
u8 seq[MAX_SEQ_DATA];
u8 start_index[PM_SLEEP_MODE_NR];
};
struct spm_driver_data {
struct cpuidle_qcom_spm_data {
struct cpuidle_driver cpuidle_driver;
void __iomem *reg_base;
const struct spm_reg_data *reg_data;
struct spm_driver_data *spm;
};
static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_SPM_CTL] = 0x30,
[SPM_REG_DLY] = 0x34,
[SPM_REG_SEQ_ENTRY] = 0x80,
};
/* SPM register data for 8974, 8084 */
static const struct spm_reg_data spm_reg_8974_8084_cpu = {
.reg_offset = spm_reg_offset_v2_1,
.spm_cfg = 0x1,
.spm_dly = 0x3C102800,
.seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 3,
};
/* SPM register data for 8226 */
static const struct spm_reg_data spm_reg_8226_cpu = {
.reg_offset = spm_reg_offset_v2_1,
.spm_cfg = 0x0,
.spm_dly = 0x3C102800,
.seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
0x80, 0x10, 0x26, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 5,
};
static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_SPM_CTL] = 0x20,
[SPM_REG_PMIC_DLY] = 0x24,
[SPM_REG_PMIC_DATA_0] = 0x28,
[SPM_REG_PMIC_DATA_1] = 0x2C,
[SPM_REG_SEQ_ENTRY] = 0x80,
};
/* SPM register data for 8064 */
static const struct spm_reg_data spm_reg_8064_cpu = {
.reg_offset = spm_reg_offset_v1_1,
.spm_cfg = 0x1F,
.pmic_dly = 0x02020004,
.pmic_data[0] = 0x0084009C,
.pmic_data[1] = 0x00A4001C,
.seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 2,
};
static inline void spm_register_write(struct spm_driver_data *drv,
enum spm_reg reg, u32 val)
{
if (drv->reg_data->reg_offset[reg])
writel_relaxed(val, drv->reg_base +
drv->reg_data->reg_offset[reg]);
}
/* Ensure a guaranteed write, before return */
static inline void spm_register_write_sync(struct spm_driver_data *drv,
enum spm_reg reg, u32 val)
{
u32 ret;
if (!drv->reg_data->reg_offset[reg])
return;
do {
writel_relaxed(val, drv->reg_base +
drv->reg_data->reg_offset[reg]);
ret = readl_relaxed(drv->reg_base +
drv->reg_data->reg_offset[reg]);
if (ret == val)
break;
cpu_relax();
} while (1);
}
static inline u32 spm_register_read(struct spm_driver_data *drv,
enum spm_reg reg)
{
return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
}
static void spm_set_low_power_mode(struct spm_driver_data *drv,
enum pm_sleep_mode mode)
{
u32 start_index;
u32 ctl_val;
start_index = drv->reg_data->start_index[mode];
ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
ctl_val |= SPM_CTL_EN;
spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
}
static int qcom_pm_collapse(unsigned long int unused)
{
qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON);
@ -201,10 +61,10 @@ static int qcom_cpu_spc(struct spm_driver_data *drv)
static int spm_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
struct spm_driver_data *data = container_of(drv, struct spm_driver_data,
cpuidle_driver);
struct cpuidle_qcom_spm_data *data = container_of(drv, struct cpuidle_qcom_spm_data,
cpuidle_driver);
return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data);
return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data->spm);
}
static struct cpuidle_driver qcom_spm_idle_driver = {
@ -225,134 +85,92 @@ static const struct of_device_id qcom_idle_state_match[] = {
{ },
};
static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu)
static int spm_cpuidle_register(struct device *cpuidle_dev, int cpu)
{
struct platform_device *pdev = NULL;
struct device_node *cpu_node, *saw_node;
struct cpuidle_qcom_spm_data *data = NULL;
int ret;
memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv));
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return -ENODEV;
/* Parse idle states from device tree */
ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1);
saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
if (!saw_node)
return -ENODEV;
pdev = of_find_device_by_node(saw_node);
of_node_put(saw_node);
of_node_put(cpu_node);
if (!pdev)
return -ENODEV;
data = devm_kzalloc(cpuidle_dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->spm = dev_get_drvdata(&pdev->dev);
if (!data->spm)
return -EINVAL;
data->cpuidle_driver = qcom_spm_idle_driver;
data->cpuidle_driver.cpumask = (struct cpumask *)cpumask_of(cpu);
ret = dt_init_idle_driver(&data->cpuidle_driver,
qcom_idle_state_match, 1);
if (ret <= 0)
return ret ? : -ENODEV;
/* We have atleast one power down mode */
return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask);
ret = qcom_scm_set_warm_boot_addr(cpu_resume_arm, cpumask_of(cpu));
if (ret)
return ret;
return cpuidle_register(&data->cpuidle_driver, NULL);
}
static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
int *spm_cpu)
static int spm_cpuidle_drv_probe(struct platform_device *pdev)
{
struct spm_driver_data *drv = NULL;
struct device_node *cpu_node, *saw_node;
int cpu;
bool found = 0;
for_each_possible_cpu(cpu) {
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
continue;
saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
found = (saw_node == pdev->dev.of_node);
of_node_put(saw_node);
of_node_put(cpu_node);
if (found)
break;
}
if (found) {
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (drv)
*spm_cpu = cpu;
}
return drv;
}
static const struct of_device_id spm_match_table[] = {
{ .compatible = "qcom,msm8226-saw2-v2.1-cpu",
.data = &spm_reg_8226_cpu },
{ .compatible = "qcom,msm8974-saw2-v2.1-cpu",
.data = &spm_reg_8974_8084_cpu },
{ .compatible = "qcom,apq8084-saw2-v2.1-cpu",
.data = &spm_reg_8974_8084_cpu },
{ .compatible = "qcom,apq8064-saw2-v1.1-cpu",
.data = &spm_reg_8064_cpu },
{ },
};
static int spm_dev_probe(struct platform_device *pdev)
{
struct spm_driver_data *drv;
struct resource *res;
const struct of_device_id *match_id;
void __iomem *addr;
int cpu, ret;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
drv = spm_get_drv(pdev, &cpu);
if (!drv)
return -EINVAL;
platform_set_drvdata(pdev, drv);
for_each_possible_cpu(cpu) {
ret = spm_cpuidle_register(&pdev->dev, cpu);
if (ret && ret != -ENODEV) {
dev_err(&pdev->dev,
"Cannot register for CPU%d: %d\n", cpu, ret);
}
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(drv->reg_base))
return PTR_ERR(drv->reg_base);
match_id = of_match_node(spm_match_table, pdev->dev.of_node);
if (!match_id)
return -ENODEV;
drv->reg_data = match_id->data;
ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu);
if (ret)
return ret;
/* Write the SPM sequences first.. */
addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
__iowrite32_copy(addr, drv->reg_data->seq,
ARRAY_SIZE(drv->reg_data->seq) / 4);
/*
* ..and then the control registers.
* On some SoC if the control registers are written first and if the
* CPU was held in reset, the reset signal could trigger the SPM state
* machine, before the sequences are completely written.
*/
spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
spm_register_write(drv, SPM_REG_PMIC_DATA_0,
drv->reg_data->pmic_data[0]);
spm_register_write(drv, SPM_REG_PMIC_DATA_1,
drv->reg_data->pmic_data[1]);
/* Set up Standby as the default low power mode */
spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
return cpuidle_register(&drv->cpuidle_driver, NULL);
}
static int spm_dev_remove(struct platform_device *pdev)
{
struct spm_driver_data *drv = platform_get_drvdata(pdev);
cpuidle_unregister(&drv->cpuidle_driver);
return 0;
}
static struct platform_driver spm_driver = {
.probe = spm_dev_probe,
.remove = spm_dev_remove,
static struct platform_driver spm_cpuidle_driver = {
.probe = spm_cpuidle_drv_probe,
.driver = {
.name = "saw",
.of_match_table = spm_match_table,
.name = "qcom-spm-cpuidle",
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(spm_driver);
static int __init qcom_spm_cpuidle_init(void)
{
struct platform_device *pdev;
int ret;
ret = platform_driver_register(&spm_cpuidle_driver);
if (ret)
return ret;
pdev = platform_device_register_simple("qcom-spm-cpuidle",
-1, NULL, 0);
if (IS_ERR(pdev)) {
platform_driver_unregister(&spm_cpuidle_driver);
return PTR_ERR(pdev);
}
return 0;
}
device_initcall(qcom_spm_cpuidle_init);

View File

@ -337,6 +337,9 @@ static void tegra_cpuidle_setup_tegra114_c7_state(void)
static int tegra_cpuidle_probe(struct platform_device *pdev)
{
if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NOT_READY)
return -EPROBE_DEFER;
/* LP2 could be disabled in device-tree */
if (tegra_pmc_get_suspend_mode() < TEGRA_SUSPEND_LP2)
tegra_cpuidle_disable_state(TEGRA_CC6);

View File

@ -167,6 +167,27 @@ struct ffa_drv_info {
static struct ffa_drv_info *drv_info;
/*
* The driver must be able to support all the versions from the earliest
* supported FFA_MIN_VERSION to the latest supported FFA_DRIVER_VERSION.
* The specification states that if firmware supports a FFA implementation
* that is incompatible with and at a greater version number than specified
* by the caller(FFA_DRIVER_VERSION passed as parameter to FFA_VERSION),
* it must return the NOT_SUPPORTED error code.
*/
static u32 ffa_compatible_version_find(u32 version)
{
u16 major = MAJOR_VERSION(version), minor = MINOR_VERSION(version);
u16 drv_major = MAJOR_VERSION(FFA_DRIVER_VERSION);
u16 drv_minor = MINOR_VERSION(FFA_DRIVER_VERSION);
if ((major < drv_major) || (major == drv_major && minor <= drv_minor))
return version;
pr_info("Firmware version higher than driver version, downgrading\n");
return FFA_DRIVER_VERSION;
}
static int ffa_version_check(u32 *version)
{
ffa_value_t ver;
@ -180,15 +201,20 @@ static int ffa_version_check(u32 *version)
return -EOPNOTSUPP;
}
if (ver.a0 < FFA_MIN_VERSION || ver.a0 > FFA_DRIVER_VERSION) {
pr_err("Incompatible version %d.%d found\n",
MAJOR_VERSION(ver.a0), MINOR_VERSION(ver.a0));
if (ver.a0 < FFA_MIN_VERSION) {
pr_err("Incompatible v%d.%d! Earliest supported v%d.%d\n",
MAJOR_VERSION(ver.a0), MINOR_VERSION(ver.a0),
MAJOR_VERSION(FFA_MIN_VERSION),
MINOR_VERSION(FFA_MIN_VERSION));
return -EINVAL;
}
*version = ver.a0;
pr_info("Version %d.%d found\n", MAJOR_VERSION(ver.a0),
pr_info("Driver version %d.%d\n", MAJOR_VERSION(FFA_DRIVER_VERSION),
MINOR_VERSION(FFA_DRIVER_VERSION));
pr_info("Firmware version %d.%d found\n", MAJOR_VERSION(ver.a0),
MINOR_VERSION(ver.a0));
*version = ffa_compatible_version_find(ver.a0);
return 0;
}
@ -586,6 +612,22 @@ ffa_memory_share(struct ffa_device *dev, struct ffa_mem_ops_args *args)
return ffa_memory_ops(FFA_FN_NATIVE(MEM_SHARE), args);
}
static int
ffa_memory_lend(struct ffa_device *dev, struct ffa_mem_ops_args *args)
{
/* Note that upon a successful MEM_LEND request the caller
* must ensure that the memory region specified is not accessed
* until a successful MEM_RECALIM call has been made.
* On systems with a hypervisor present this will been enforced,
* however on systems without a hypervisor the responsibility
* falls to the calling kernel driver to prevent access.
*/
if (dev->mode_32bit)
return ffa_memory_ops(FFA_MEM_LEND, args);
return ffa_memory_ops(FFA_FN_NATIVE(MEM_LEND), args);
}
static const struct ffa_dev_ops ffa_ops = {
.api_version_get = ffa_api_version_get,
.partition_info_get = ffa_partition_info_get,
@ -593,6 +635,7 @@ static const struct ffa_dev_ops ffa_ops = {
.sync_send_receive = ffa_sync_send_receive,
.memory_reclaim = ffa_memory_reclaim,
.memory_share = ffa_memory_share,
.memory_lend = ffa_memory_lend,
};
const struct ffa_dev_ops *ffa_dev_ops_get(struct ffa_device *dev)

View File

@ -252,7 +252,7 @@ static bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
break;
default:
pr_err("Unknown SMC convention being used\n");
return -EINVAL;
return false;
}
ret = qcom_scm_call(dev, &desc, &res);
@ -1348,6 +1348,10 @@ static const struct of_device_id qcom_scm_dt_match[] = {
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
},
{ .compatible = "qcom,scm-msm8953", .data = (void *)(SCM_HAS_CORE_CLK |
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
},
{ .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)

View File

@ -74,28 +74,36 @@ static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset)
static const char *get_filename(struct tegra_bpmp *bpmp,
const struct file *file, char *buf, int size)
{
char root_path_buf[512];
const char *root_path;
const char *filename;
const char *root_path, *filename = NULL;
char *root_path_buf;
size_t root_len;
root_path_buf = kzalloc(512, GFP_KERNEL);
if (!root_path_buf)
goto out;
root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
sizeof(root_path_buf));
if (IS_ERR(root_path))
return NULL;
goto out;
root_len = strlen(root_path);
filename = dentry_path(file->f_path.dentry, buf, size);
if (IS_ERR(filename))
return NULL;
if (IS_ERR(filename)) {
filename = NULL;
goto out;
}
if (strlen(filename) < root_len ||
strncmp(filename, root_path, root_len))
return NULL;
if (strlen(filename) < root_len || strncmp(filename, root_path, root_len)) {
filename = NULL;
goto out;
}
filename += root_len;
out:
kfree(root_path_buf);
return filename;
}

View File

@ -162,7 +162,6 @@ static int tegra210_bpmp_init(struct tegra_bpmp *bpmp)
{
struct platform_device *pdev = to_platform_device(bpmp->dev);
struct tegra210_bpmp *priv;
struct resource *res;
unsigned int i;
int err;
@ -172,13 +171,11 @@ static int tegra210_bpmp_init(struct tegra_bpmp *bpmp)
bpmp->priv = priv;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->atomics = devm_ioremap_resource(&pdev->dev, res);
priv->atomics = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->atomics))
return PTR_ERR(priv->atomics);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->arb_sema = devm_ioremap_resource(&pdev->dev, res);
priv->arb_sema = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(priv->arb_sema))
return PTR_ERR(priv->arb_sema);

View File

@ -11,6 +11,7 @@
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <video/mipi_display.h>
#include <video/videomode.h>
@ -980,8 +981,10 @@ static int mtk_dsi_bind(struct device *dev, struct device *master, void *data)
struct mtk_dsi *dsi = dev_get_drvdata(dev);
ret = mtk_dsi_encoder_init(drm, dsi);
if (ret)
return ret;
return ret;
return device_reset_optional(dev);
}
static void mtk_dsi_unbind(struct device *dev, struct device *master,

View File

@ -55,8 +55,8 @@ config ATMEL_EBI
SRAMs, ATA devices, etc.
config BRCMSTB_DPFE
bool "Broadcom STB DPFE driver" if COMPILE_TEST
default y if ARCH_BRCMSTB
tristate "Broadcom STB DPFE driver"
default ARCH_BRCMSTB
depends on ARCH_BRCMSTB || COMPILE_TEST
help
This driver provides access to the DPFE interface of Broadcom
@ -210,6 +210,7 @@ config RENESAS_RPCIF
tristate "Renesas RPC-IF driver"
depends on ARCH_RENESAS || COMPILE_TEST
select REGMAP_MMIO
select RESET_CONTROLLER
help
This supports Renesas R-Car Gen3 or RZ/G2 RPC-IF which provides
either SPI host or HyperFlash. You'll have to select individual

View File

@ -263,7 +263,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev);
if (ret < 0)
goto err;
goto err_unmap_nandirq;
init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait);
@ -272,7 +272,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_ifc_ctrl_dev->irq);
goto err_irq;
goto err_unmap_nandirq;
}
if (fsl_ifc_ctrl_dev->nand_irq) {
@ -281,17 +281,16 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_ifc_ctrl_dev->nand_irq);
goto err_nandirq;
goto err_free_irq;
}
}
return 0;
err_nandirq:
free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev);
irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
err_irq:
err_free_irq:
free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev);
err_unmap_nandirq:
irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
irq_dispose_mapping(fsl_ifc_ctrl_dev->irq);
err:
iounmap(fsl_ifc_ctrl_dev->gregs);

View File

@ -112,6 +112,26 @@
#define NUM_DDR_ADDR_TABLE_ENTRIES 11
#define NUM_DDR_TIMING_TABLE_ENTRIES 4
#define LPDDR2_MANID_SAMSUNG 1
#define LPDDR2_MANID_QIMONDA 2
#define LPDDR2_MANID_ELPIDA 3
#define LPDDR2_MANID_ETRON 4
#define LPDDR2_MANID_NANYA 5
#define LPDDR2_MANID_HYNIX 6
#define LPDDR2_MANID_MOSEL 7
#define LPDDR2_MANID_WINBOND 8
#define LPDDR2_MANID_ESMT 9
#define LPDDR2_MANID_SPANSION 11
#define LPDDR2_MANID_SST 12
#define LPDDR2_MANID_ZMOS 13
#define LPDDR2_MANID_INTEL 14
#define LPDDR2_MANID_NUMONYX 254
#define LPDDR2_MANID_MICRON 255
#define LPDDR2_TYPE_S4 0
#define LPDDR2_TYPE_S2 1
#define LPDDR2_TYPE_NVM 2
/* Structure for DDR addressing info from the JEDEC spec */
struct lpddr2_addressing {
u32 num_banks;
@ -170,6 +190,33 @@ extern const struct lpddr2_timings
lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES];
extern const struct lpddr2_min_tck lpddr2_jedec_min_tck;
/* Structure of MR8 */
union lpddr2_basic_config4 {
u32 value;
struct {
unsigned int arch_type : 2;
unsigned int density : 4;
unsigned int io_width : 2;
} __packed;
};
/*
* Structure for information about LPDDR2 chip. All parameters are
* matching raw values of standard mode register bitfields or set to
* -ENOENT if info unavailable.
*/
struct lpddr2_info {
int arch_type;
int density;
int io_width;
int manufacturer_id;
int revision_id1;
int revision_id2;
};
const char *lpddr2_jedec_manufacturer(unsigned int manufacturer_id);
/*
* Structure for timings for LPDDR3 based on LPDDR2 plus additional fields.
* All parameters are in pico seconds(ps) excluding max_freq, min_freq which

View File

@ -131,3 +131,44 @@ const struct lpddr2_min_tck lpddr2_jedec_min_tck = {
.tFAW = 8
};
EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck);
const char *lpddr2_jedec_manufacturer(unsigned int manufacturer_id)
{
switch (manufacturer_id) {
case LPDDR2_MANID_SAMSUNG:
return "Samsung";
case LPDDR2_MANID_QIMONDA:
return "Qimonda";
case LPDDR2_MANID_ELPIDA:
return "Elpida";
case LPDDR2_MANID_ETRON:
return "Etron";
case LPDDR2_MANID_NANYA:
return "Nanya";
case LPDDR2_MANID_HYNIX:
return "Hynix";
case LPDDR2_MANID_MOSEL:
return "Mosel";
case LPDDR2_MANID_WINBOND:
return "Winbond";
case LPDDR2_MANID_ESMT:
return "ESMT";
case LPDDR2_MANID_SPANSION:
return "Spansion";
case LPDDR2_MANID_SST:
return "SST";
case LPDDR2_MANID_ZMOS:
return "ZMOS";
case LPDDR2_MANID_INTEL:
return "Intel";
case LPDDR2_MANID_NUMONYX:
return "Numonyx";
case LPDDR2_MANID_MICRON:
return "Micron";
default:
break;
}
return "invalid";
}
EXPORT_SYMBOL_GPL(lpddr2_jedec_manufacturer);

View File

@ -17,13 +17,33 @@
#include <dt-bindings/memory/mt2701-larb-port.h>
#include <dt-bindings/memory/mtk-memory-port.h>
/* mt8173 */
#define SMI_LARB_MMU_EN 0xf00
/* SMI COMMON */
#define SMI_L1LEN 0x100
/* mt8167 */
#define MT8167_SMI_LARB_MMU_EN 0xfc0
#define SMI_BUS_SEL 0x220
#define SMI_BUS_LARB_SHIFT(larbid) ((larbid) << 1)
/* All are MMU0 defaultly. Only specialize mmu1 here. */
#define F_MMU1_LARB(larbid) (0x1 << SMI_BUS_LARB_SHIFT(larbid))
/* mt2701 */
#define SMI_M4U_TH 0x234
#define SMI_FIFO_TH1 0x238
#define SMI_FIFO_TH2 0x23c
#define SMI_DCM 0x300
#define SMI_DUMMY 0x444
/* SMI LARB */
#define SMI_LARB_CMD_THRT_CON 0x24
#define SMI_LARB_THRT_RD_NU_LMT_MSK GENMASK(7, 4)
#define SMI_LARB_THRT_RD_NU_LMT (5 << 4)
#define SMI_LARB_SW_FLAG 0x40
#define SMI_LARB_SW_FLAG_1 0x1
#define SMI_LARB_OSTDL_PORT 0x200
#define SMI_LARB_OSTDL_PORTx(id) (SMI_LARB_OSTDL_PORT + (((id) & 0x1f) << 2))
/* Below are about mmu enable registers, they are different in SoCs */
/* gen1: mt2701 */
#define REG_SMI_SECUR_CON_BASE 0x5c0
/* every register control 8 port, register offset 0x4 */
@ -41,99 +61,94 @@
/* mt2701 domain should be set to 3 */
#define SMI_SECUR_CON_VAL_DOMAIN(id) (0x3 << ((((id) & 0x7) << 2) + 1))
/* mt2712 */
#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
#define F_MMU_EN BIT(0)
#define BANK_SEL(id) ({ \
/* gen2: */
/* mt8167 */
#define MT8167_SMI_LARB_MMU_EN 0xfc0
/* mt8173 */
#define MT8173_SMI_LARB_MMU_EN 0xf00
/* general */
#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
#define F_MMU_EN BIT(0)
#define BANK_SEL(id) ({ \
u32 _id = (id) & 0x3; \
(_id << 8 | _id << 10 | _id << 12 | _id << 14); \
})
/* SMI COMMON */
#define SMI_BUS_SEL 0x220
#define SMI_BUS_LARB_SHIFT(larbid) ((larbid) << 1)
/* All are MMU0 defaultly. Only specialize mmu1 here. */
#define F_MMU1_LARB(larbid) (0x1 << SMI_BUS_LARB_SHIFT(larbid))
#define SMI_COMMON_INIT_REGS_NR 6
#define SMI_LARB_PORT_NR_MAX 32
enum mtk_smi_gen {
MTK_SMI_GEN1,
MTK_SMI_GEN2
#define MTK_SMI_FLAG_THRT_UPDATE BIT(0)
#define MTK_SMI_FLAG_SW_FLAG BIT(1)
#define MTK_SMI_CAPS(flags, _x) (!!((flags) & (_x)))
struct mtk_smi_reg_pair {
unsigned int offset;
u32 value;
};
enum mtk_smi_type {
MTK_SMI_GEN1,
MTK_SMI_GEN2, /* gen2 smi common */
MTK_SMI_GEN2_SUB_COMM, /* gen2 smi sub common */
};
#define MTK_SMI_CLK_NR_MAX 4
/* larbs: Require apb/smi clocks while gals is optional. */
static const char * const mtk_smi_larb_clks[] = {"apb", "smi", "gals"};
#define MTK_SMI_LARB_REQ_CLK_NR 2
#define MTK_SMI_LARB_OPT_CLK_NR 1
/*
* common: Require these four clocks in has_gals case. Otherwise, only apb/smi are required.
* sub common: Require apb/smi/gals0 clocks in has_gals case. Otherwise, only apb/smi are required.
*/
static const char * const mtk_smi_common_clks[] = {"apb", "smi", "gals0", "gals1"};
#define MTK_SMI_COM_REQ_CLK_NR 2
#define MTK_SMI_COM_GALS_REQ_CLK_NR MTK_SMI_CLK_NR_MAX
#define MTK_SMI_SUB_COM_GALS_REQ_CLK_NR 3
struct mtk_smi_common_plat {
enum mtk_smi_gen gen;
bool has_gals;
u32 bus_sel; /* Balance some larbs to enter mmu0 or mmu1 */
enum mtk_smi_type type;
bool has_gals;
u32 bus_sel; /* Balance some larbs to enter mmu0 or mmu1 */
const struct mtk_smi_reg_pair *init;
};
struct mtk_smi_larb_gen {
int port_in_larb[MTK_LARB_NR_MAX + 1];
void (*config_port)(struct device *dev);
unsigned int larb_direct_to_common_mask;
bool has_gals;
unsigned int flags_general;
const u8 (*ostd)[SMI_LARB_PORT_NR_MAX];
};
struct mtk_smi {
struct device *dev;
struct clk *clk_apb, *clk_smi;
struct clk *clk_gals0, *clk_gals1;
unsigned int clk_num;
struct clk_bulk_data clks[MTK_SMI_CLK_NR_MAX];
struct clk *clk_async; /*only needed by mt2701*/
union {
void __iomem *smi_ao_base; /* only for gen1 */
void __iomem *base; /* only for gen2 */
};
struct device *smi_common_dev; /* for sub common */
const struct mtk_smi_common_plat *plat;
};
struct mtk_smi_larb { /* larb: local arbiter */
struct mtk_smi smi;
void __iomem *base;
struct device *smi_common_dev;
struct device *smi_common_dev; /* common or sub-common dev */
const struct mtk_smi_larb_gen *larb_gen;
int larbid;
u32 *mmu;
unsigned char *bank;
};
static int mtk_smi_clk_enable(const struct mtk_smi *smi)
{
int ret;
ret = clk_prepare_enable(smi->clk_apb);
if (ret)
return ret;
ret = clk_prepare_enable(smi->clk_smi);
if (ret)
goto err_disable_apb;
ret = clk_prepare_enable(smi->clk_gals0);
if (ret)
goto err_disable_smi;
ret = clk_prepare_enable(smi->clk_gals1);
if (ret)
goto err_disable_gals0;
return 0;
err_disable_gals0:
clk_disable_unprepare(smi->clk_gals0);
err_disable_smi:
clk_disable_unprepare(smi->clk_smi);
err_disable_apb:
clk_disable_unprepare(smi->clk_apb);
return ret;
}
static void mtk_smi_clk_disable(const struct mtk_smi *smi)
{
clk_disable_unprepare(smi->clk_gals1);
clk_disable_unprepare(smi->clk_gals0);
clk_disable_unprepare(smi->clk_smi);
clk_disable_unprepare(smi->clk_apb);
}
int mtk_smi_larb_get(struct device *larbdev)
{
int ret = pm_runtime_resume_and_get(larbdev);
@ -166,36 +181,16 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
return -ENODEV;
}
static void mtk_smi_larb_config_port_gen2_general(struct device *dev)
static void
mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
u32 reg;
int i;
if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask)
return;
for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
reg |= F_MMU_EN;
reg |= BANK_SEL(larb->bank[i]);
writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
}
/* Do nothing as the iommu is always enabled. */
}
static void mtk_smi_larb_config_port_mt8173(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
}
static void mtk_smi_larb_config_port_mt8167(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
writel(*larb->mmu, larb->base + MT8167_SMI_LARB_MMU_EN);
}
static const struct component_ops mtk_smi_larb_component_ops = {
.bind = mtk_smi_larb_bind,
.unbind = mtk_smi_larb_unbind,
};
static void mtk_smi_larb_config_port_gen1(struct device *dev)
{
@ -228,25 +223,94 @@ static void mtk_smi_larb_config_port_gen1(struct device *dev)
}
}
static void
mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
static void mtk_smi_larb_config_port_mt8167(struct device *dev)
{
/* Do nothing as the iommu is always enabled. */
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
writel(*larb->mmu, larb->base + MT8167_SMI_LARB_MMU_EN);
}
static const struct component_ops mtk_smi_larb_component_ops = {
.bind = mtk_smi_larb_bind,
.unbind = mtk_smi_larb_unbind,
};
static void mtk_smi_larb_config_port_mt8173(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
/* mt8173 do not need the port in larb */
.config_port = mtk_smi_larb_config_port_mt8173,
};
writel(*larb->mmu, larb->base + MT8173_SMI_LARB_MMU_EN);
}
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
/* mt8167 do not need the port in larb */
.config_port = mtk_smi_larb_config_port_mt8167,
static void mtk_smi_larb_config_port_gen2_general(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
u32 reg, flags_general = larb->larb_gen->flags_general;
const u8 *larbostd = larb->larb_gen->ostd[larb->larbid];
int i;
if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask)
return;
if (MTK_SMI_CAPS(flags_general, MTK_SMI_FLAG_THRT_UPDATE)) {
reg = readl_relaxed(larb->base + SMI_LARB_CMD_THRT_CON);
reg &= ~SMI_LARB_THRT_RD_NU_LMT_MSK;
reg |= SMI_LARB_THRT_RD_NU_LMT;
writel_relaxed(reg, larb->base + SMI_LARB_CMD_THRT_CON);
}
if (MTK_SMI_CAPS(flags_general, MTK_SMI_FLAG_SW_FLAG))
writel_relaxed(SMI_LARB_SW_FLAG_1, larb->base + SMI_LARB_SW_FLAG);
for (i = 0; i < SMI_LARB_PORT_NR_MAX && larbostd && !!larbostd[i]; i++)
writel_relaxed(larbostd[i], larb->base + SMI_LARB_OSTDL_PORTx(i));
for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
reg |= F_MMU_EN;
reg |= BANK_SEL(larb->bank[i]);
writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
}
}
static const u8 mtk_smi_larb_mt8195_ostd[][SMI_LARB_PORT_NR_MAX] = {
[0] = {0x0a, 0xc, 0x22, 0x22, 0x01, 0x0a,}, /* larb0 */
[1] = {0x0a, 0xc, 0x22, 0x22, 0x01, 0x0a,}, /* larb1 */
[2] = {0x12, 0x12, 0x12, 0x12, 0x0a,}, /* ... */
[3] = {0x12, 0x12, 0x12, 0x12, 0x28, 0x28, 0x0a,},
[4] = {0x06, 0x01, 0x17, 0x06, 0x0a,},
[5] = {0x06, 0x01, 0x17, 0x06, 0x06, 0x01, 0x06, 0x0a,},
[6] = {0x06, 0x01, 0x06, 0x0a,},
[7] = {0x0c, 0x0c, 0x12,},
[8] = {0x0c, 0x0c, 0x12,},
[9] = {0x0a, 0x08, 0x04, 0x06, 0x01, 0x01, 0x10, 0x18, 0x11, 0x0a,
0x08, 0x04, 0x11, 0x06, 0x02, 0x06, 0x01, 0x11, 0x11, 0x06,},
[10] = {0x18, 0x08, 0x01, 0x01, 0x20, 0x12, 0x18, 0x06, 0x05, 0x10,
0x08, 0x08, 0x10, 0x08, 0x08, 0x18, 0x0c, 0x09, 0x0b, 0x0d,
0x0d, 0x06, 0x10, 0x10,},
[11] = {0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x01, 0x01, 0x01, 0x01,},
[12] = {0x09, 0x09, 0x05, 0x05, 0x0c, 0x18, 0x02, 0x02, 0x04, 0x02,},
[13] = {0x02, 0x02, 0x12, 0x12, 0x02, 0x02, 0x02, 0x02, 0x08, 0x01,},
[14] = {0x12, 0x12, 0x02, 0x02, 0x02, 0x02, 0x16, 0x01, 0x16, 0x01,
0x01, 0x02, 0x02, 0x08, 0x02,},
[15] = {},
[16] = {0x28, 0x02, 0x02, 0x12, 0x02, 0x12, 0x10, 0x02, 0x02, 0x0a,
0x12, 0x02, 0x0a, 0x16, 0x02, 0x04,},
[17] = {0x1a, 0x0e, 0x0a, 0x0a, 0x0c, 0x0e, 0x10,},
[18] = {0x12, 0x06, 0x12, 0x06,},
[19] = {0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x04, 0x01,
0x01, 0x01, 0x04, 0x0a, 0x06, 0x01, 0x01, 0x01, 0x0a, 0x06,
0x01, 0x01, 0x05, 0x03, 0x03, 0x04, 0x01,},
[20] = {0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x04, 0x01,
0x01, 0x01, 0x04, 0x0a, 0x06, 0x01, 0x01, 0x01, 0x0a, 0x06,
0x01, 0x01, 0x05, 0x03, 0x03, 0x04, 0x01,},
[21] = {0x28, 0x19, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04,},
[22] = {0x28, 0x19, 0x0c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04,},
[23] = {0x18, 0x01,},
[24] = {0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01,
0x01, 0x01,},
[25] = {0x02, 0x02, 0x02, 0x28, 0x16, 0x02, 0x02, 0x02, 0x12, 0x16,
0x02, 0x01,},
[26] = {0x02, 0x02, 0x02, 0x28, 0x16, 0x02, 0x02, 0x02, 0x12, 0x16,
0x02, 0x01,},
[27] = {0x02, 0x02, 0x02, 0x28, 0x16, 0x02, 0x02, 0x02, 0x12, 0x16,
0x02, 0x01,},
[28] = {0x1a, 0x0e, 0x0a, 0x0a, 0x0c, 0x0e, 0x10,},
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
@ -269,8 +333,17 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = {
/* DUMMY | IPU0 | IPU1 | CCU | MDLA */
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
/* mt8167 do not need the port in larb */
.config_port = mtk_smi_larb_config_port_mt8167,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
/* mt8173 do not need the port in larb */
.config_port = mtk_smi_larb_config_port_mt8173,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
.has_gals = true,
.config_port = mtk_smi_larb_config_port_gen2_general,
.larb_direct_to_common_mask = BIT(2) | BIT(3) | BIT(7),
/* IPU0 | IPU1 | CCU */
@ -280,99 +353,114 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt8192 = {
.config_port = mtk_smi_larb_config_port_gen2_general,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8195 = {
.config_port = mtk_smi_larb_config_port_gen2_general,
.flags_general = MTK_SMI_FLAG_THRT_UPDATE | MTK_SMI_FLAG_SW_FLAG,
.ostd = mtk_smi_larb_mt8195_ostd,
};
static const struct of_device_id mtk_smi_larb_of_ids[] = {
{
.compatible = "mediatek,mt8167-smi-larb",
.data = &mtk_smi_larb_mt8167
},
{
.compatible = "mediatek,mt8173-smi-larb",
.data = &mtk_smi_larb_mt8173
},
{
.compatible = "mediatek,mt2701-smi-larb",
.data = &mtk_smi_larb_mt2701
},
{
.compatible = "mediatek,mt2712-smi-larb",
.data = &mtk_smi_larb_mt2712
},
{
.compatible = "mediatek,mt6779-smi-larb",
.data = &mtk_smi_larb_mt6779
},
{
.compatible = "mediatek,mt8183-smi-larb",
.data = &mtk_smi_larb_mt8183
},
{
.compatible = "mediatek,mt8192-smi-larb",
.data = &mtk_smi_larb_mt8192
},
{.compatible = "mediatek,mt2701-smi-larb", .data = &mtk_smi_larb_mt2701},
{.compatible = "mediatek,mt2712-smi-larb", .data = &mtk_smi_larb_mt2712},
{.compatible = "mediatek,mt6779-smi-larb", .data = &mtk_smi_larb_mt6779},
{.compatible = "mediatek,mt8167-smi-larb", .data = &mtk_smi_larb_mt8167},
{.compatible = "mediatek,mt8173-smi-larb", .data = &mtk_smi_larb_mt8173},
{.compatible = "mediatek,mt8183-smi-larb", .data = &mtk_smi_larb_mt8183},
{.compatible = "mediatek,mt8192-smi-larb", .data = &mtk_smi_larb_mt8192},
{.compatible = "mediatek,mt8195-smi-larb", .data = &mtk_smi_larb_mt8195},
{}
};
static int mtk_smi_device_link_common(struct device *dev, struct device **com_dev)
{
struct platform_device *smi_com_pdev;
struct device_node *smi_com_node;
struct device *smi_com_dev;
struct device_link *link;
smi_com_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
if (!smi_com_node)
return -EINVAL;
smi_com_pdev = of_find_device_by_node(smi_com_node);
of_node_put(smi_com_node);
if (smi_com_pdev) {
/* smi common is the supplier, Make sure it is ready before */
if (!platform_get_drvdata(smi_com_pdev))
return -EPROBE_DEFER;
smi_com_dev = &smi_com_pdev->dev;
link = device_link_add(dev, smi_com_dev,
DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
if (!link) {
dev_err(dev, "Unable to link smi-common dev\n");
return -ENODEV;
}
*com_dev = smi_com_dev;
} else {
dev_err(dev, "Failed to get the smi_common device\n");
return -EINVAL;
}
return 0;
}
static int mtk_smi_dts_clk_init(struct device *dev, struct mtk_smi *smi,
const char * const clks[],
unsigned int clk_nr_required,
unsigned int clk_nr_optional)
{
int i, ret;
for (i = 0; i < clk_nr_required; i++)
smi->clks[i].id = clks[i];
ret = devm_clk_bulk_get(dev, clk_nr_required, smi->clks);
if (ret)
return ret;
for (i = clk_nr_required; i < clk_nr_required + clk_nr_optional; i++)
smi->clks[i].id = clks[i];
ret = devm_clk_bulk_get_optional(dev, clk_nr_optional,
smi->clks + clk_nr_required);
smi->clk_num = clk_nr_required + clk_nr_optional;
return ret;
}
static int mtk_smi_larb_probe(struct platform_device *pdev)
{
struct mtk_smi_larb *larb;
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *smi_node;
struct platform_device *smi_pdev;
struct device_link *link;
int ret;
larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
if (!larb)
return -ENOMEM;
larb->larb_gen = of_device_get_match_data(dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
larb->base = devm_ioremap_resource(dev, res);
larb->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(larb->base))
return PTR_ERR(larb->base);
larb->smi.clk_apb = devm_clk_get(dev, "apb");
if (IS_ERR(larb->smi.clk_apb))
return PTR_ERR(larb->smi.clk_apb);
ret = mtk_smi_dts_clk_init(dev, &larb->smi, mtk_smi_larb_clks,
MTK_SMI_LARB_REQ_CLK_NR, MTK_SMI_LARB_OPT_CLK_NR);
if (ret)
return ret;
larb->smi.clk_smi = devm_clk_get(dev, "smi");
if (IS_ERR(larb->smi.clk_smi))
return PTR_ERR(larb->smi.clk_smi);
if (larb->larb_gen->has_gals) {
/* The larbs may still haven't gals even if the SoC support.*/
larb->smi.clk_gals0 = devm_clk_get(dev, "gals");
if (PTR_ERR(larb->smi.clk_gals0) == -ENOENT)
larb->smi.clk_gals0 = NULL;
else if (IS_ERR(larb->smi.clk_gals0))
return PTR_ERR(larb->smi.clk_gals0);
}
larb->smi.dev = dev;
smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
if (!smi_node)
return -EINVAL;
smi_pdev = of_find_device_by_node(smi_node);
of_node_put(smi_node);
if (smi_pdev) {
if (!platform_get_drvdata(smi_pdev))
return -EPROBE_DEFER;
larb->smi_common_dev = &smi_pdev->dev;
link = device_link_add(dev, larb->smi_common_dev,
DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
if (!link) {
dev_err(dev, "Unable to link smi-common dev\n");
return -ENODEV;
}
} else {
dev_err(dev, "Failed to get the smi_common device\n");
return -EINVAL;
}
ret = mtk_smi_device_link_common(dev, &larb->smi_common_dev);
if (ret < 0)
return ret;
pm_runtime_enable(dev);
platform_set_drvdata(pdev, larb);
return component_add(dev, &mtk_smi_larb_component_ops);
ret = component_add(dev, &mtk_smi_larb_component_ops);
if (ret)
goto err_pm_disable;
return 0;
err_pm_disable:
pm_runtime_disable(dev);
device_link_remove(dev, larb->smi_common_dev);
return ret;
}
static int mtk_smi_larb_remove(struct platform_device *pdev)
@ -391,11 +479,9 @@ static int __maybe_unused mtk_smi_larb_resume(struct device *dev)
const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
int ret;
ret = mtk_smi_clk_enable(&larb->smi);
if (ret < 0) {
dev_err(dev, "Failed to enable clock(%d).\n", ret);
ret = clk_bulk_prepare_enable(larb->smi.clk_num, larb->smi.clks);
if (ret < 0)
return ret;
}
/* Configure the basic setting for this larb */
larb_gen->config_port(dev);
@ -407,7 +493,7 @@ static int __maybe_unused mtk_smi_larb_suspend(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
mtk_smi_clk_disable(&larb->smi);
clk_bulk_disable_unprepare(larb->smi.clk_num, larb->smi.clks);
return 0;
}
@ -427,64 +513,75 @@ static struct platform_driver mtk_smi_larb_driver = {
}
};
static const struct mtk_smi_reg_pair mtk_smi_common_mt8195_init[SMI_COMMON_INIT_REGS_NR] = {
{SMI_L1LEN, 0xb},
{SMI_M4U_TH, 0xe100e10},
{SMI_FIFO_TH1, 0x506090a},
{SMI_FIFO_TH2, 0x506090a},
{SMI_DCM, 0x4f1},
{SMI_DUMMY, 0x1},
};
static const struct mtk_smi_common_plat mtk_smi_common_gen1 = {
.gen = MTK_SMI_GEN1,
.type = MTK_SMI_GEN1,
};
static const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
.gen = MTK_SMI_GEN2,
.type = MTK_SMI_GEN2,
};
static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = {
.gen = MTK_SMI_GEN2,
.has_gals = true,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
F_MMU1_LARB(5) | F_MMU1_LARB(6) | F_MMU1_LARB(7),
.type = MTK_SMI_GEN2,
.has_gals = true,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
F_MMU1_LARB(5) | F_MMU1_LARB(6) | F_MMU1_LARB(7),
};
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
.gen = MTK_SMI_GEN2,
.type = MTK_SMI_GEN2,
.has_gals = true,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) |
F_MMU1_LARB(7),
};
static const struct mtk_smi_common_plat mtk_smi_common_mt8192 = {
.gen = MTK_SMI_GEN2,
.type = MTK_SMI_GEN2,
.has_gals = true,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) |
F_MMU1_LARB(6),
};
static const struct mtk_smi_common_plat mtk_smi_common_mt8195_vdo = {
.type = MTK_SMI_GEN2,
.has_gals = true,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(3) | F_MMU1_LARB(5) |
F_MMU1_LARB(7),
.init = mtk_smi_common_mt8195_init,
};
static const struct mtk_smi_common_plat mtk_smi_common_mt8195_vpp = {
.type = MTK_SMI_GEN2,
.has_gals = true,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(7),
.init = mtk_smi_common_mt8195_init,
};
static const struct mtk_smi_common_plat mtk_smi_sub_common_mt8195 = {
.type = MTK_SMI_GEN2_SUB_COMM,
.has_gals = true,
};
static const struct of_device_id mtk_smi_common_of_ids[] = {
{
.compatible = "mediatek,mt8173-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt8167-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt2701-smi-common",
.data = &mtk_smi_common_gen1,
},
{
.compatible = "mediatek,mt2712-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt6779-smi-common",
.data = &mtk_smi_common_mt6779,
},
{
.compatible = "mediatek,mt8183-smi-common",
.data = &mtk_smi_common_mt8183,
},
{
.compatible = "mediatek,mt8192-smi-common",
.data = &mtk_smi_common_mt8192,
},
{.compatible = "mediatek,mt2701-smi-common", .data = &mtk_smi_common_gen1},
{.compatible = "mediatek,mt2712-smi-common", .data = &mtk_smi_common_gen2},
{.compatible = "mediatek,mt6779-smi-common", .data = &mtk_smi_common_mt6779},
{.compatible = "mediatek,mt8167-smi-common", .data = &mtk_smi_common_gen2},
{.compatible = "mediatek,mt8173-smi-common", .data = &mtk_smi_common_gen2},
{.compatible = "mediatek,mt8183-smi-common", .data = &mtk_smi_common_mt8183},
{.compatible = "mediatek,mt8192-smi-common", .data = &mtk_smi_common_mt8192},
{.compatible = "mediatek,mt8195-smi-common-vdo", .data = &mtk_smi_common_mt8195_vdo},
{.compatible = "mediatek,mt8195-smi-common-vpp", .data = &mtk_smi_common_mt8195_vpp},
{.compatible = "mediatek,mt8195-smi-sub-common", .data = &mtk_smi_sub_common_mt8195},
{}
};
@ -492,8 +589,7 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_smi *common;
struct resource *res;
int ret;
int ret, clk_required = MTK_SMI_COM_REQ_CLK_NR;
common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
if (!common)
@ -501,23 +597,15 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
common->dev = dev;
common->plat = of_device_get_match_data(dev);
common->clk_apb = devm_clk_get(dev, "apb");
if (IS_ERR(common->clk_apb))
return PTR_ERR(common->clk_apb);
common->clk_smi = devm_clk_get(dev, "smi");
if (IS_ERR(common->clk_smi))
return PTR_ERR(common->clk_smi);
if (common->plat->has_gals) {
common->clk_gals0 = devm_clk_get(dev, "gals0");
if (IS_ERR(common->clk_gals0))
return PTR_ERR(common->clk_gals0);
common->clk_gals1 = devm_clk_get(dev, "gals1");
if (IS_ERR(common->clk_gals1))
return PTR_ERR(common->clk_gals1);
if (common->plat->type == MTK_SMI_GEN2)
clk_required = MTK_SMI_COM_GALS_REQ_CLK_NR;
else if (common->plat->type == MTK_SMI_GEN2_SUB_COMM)
clk_required = MTK_SMI_SUB_COM_GALS_REQ_CLK_NR;
}
ret = mtk_smi_dts_clk_init(dev, common, mtk_smi_common_clks, clk_required, 0);
if (ret)
return ret;
/*
* for mtk smi gen 1, we need to get the ao(always on) base to config
@ -525,9 +613,8 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
* clock into emi clock domain, but for mtk smi gen2, there's no smi ao
* base.
*/
if (common->plat->gen == MTK_SMI_GEN1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
common->smi_ao_base = devm_ioremap_resource(dev, res);
if (common->plat->type == MTK_SMI_GEN1) {
common->smi_ao_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(common->smi_ao_base))
return PTR_ERR(common->smi_ao_base);
@ -539,11 +626,18 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
if (ret)
return ret;
} else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
common->base = devm_ioremap_resource(dev, res);
common->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
}
/* link its smi-common if this is smi-sub-common */
if (common->plat->type == MTK_SMI_GEN2_SUB_COMM) {
ret = mtk_smi_device_link_common(dev, &common->smi_common_dev);
if (ret < 0)
return ret;
}
pm_runtime_enable(dev);
platform_set_drvdata(pdev, common);
return 0;
@ -551,6 +645,10 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
static int mtk_smi_common_remove(struct platform_device *pdev)
{
struct mtk_smi *common = dev_get_drvdata(&pdev->dev);
if (common->plat->type == MTK_SMI_GEN2_SUB_COMM)
device_link_remove(&pdev->dev, common->smi_common_dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
@ -558,17 +656,21 @@ static int mtk_smi_common_remove(struct platform_device *pdev)
static int __maybe_unused mtk_smi_common_resume(struct device *dev)
{
struct mtk_smi *common = dev_get_drvdata(dev);
u32 bus_sel = common->plat->bus_sel;
int ret;
const struct mtk_smi_reg_pair *init = common->plat->init;
u32 bus_sel = common->plat->bus_sel; /* default is 0 */
int ret, i;
ret = mtk_smi_clk_enable(common);
if (ret) {
dev_err(common->dev, "Failed to enable clock(%d).\n", ret);
ret = clk_bulk_prepare_enable(common->clk_num, common->clks);
if (ret)
return ret;
}
if (common->plat->gen == MTK_SMI_GEN2 && bus_sel)
writel(bus_sel, common->base + SMI_BUS_SEL);
if (common->plat->type != MTK_SMI_GEN2)
return 0;
for (i = 0; i < SMI_COMMON_INIT_REGS_NR && init && init[i].offset; i++)
writel_relaxed(init[i].value, common->base + init[i].offset);
writel(bus_sel, common->base + SMI_BUS_SEL);
return 0;
}
@ -576,7 +678,7 @@ static int __maybe_unused mtk_smi_common_suspend(struct device *dev)
{
struct mtk_smi *common = dev_get_drvdata(dev);
mtk_smi_clk_disable(common);
clk_bulk_disable_unprepare(common->clk_num, common->clks);
return 0;
}

View File

@ -298,3 +298,90 @@ default_timings:
return NULL;
}
EXPORT_SYMBOL(of_lpddr3_get_ddr_timings);
/**
* of_lpddr2_get_info() - extracts information about the lpddr2 chip.
* @np: Pointer to device tree node containing lpddr2 info
* @dev: Device requesting info
*
* Populates lpddr2_info structure by extracting data from device
* tree node. Returns pointer to populated structure. If error
* happened while populating, returns NULL. If property is missing
* in a device-tree, then the corresponding value is set to -ENOENT.
*/
const struct lpddr2_info
*of_lpddr2_get_info(struct device_node *np, struct device *dev)
{
struct lpddr2_info *ret_info, info = {};
struct property *prop;
const char *cp;
int err;
err = of_property_read_u32(np, "revision-id1", &info.revision_id1);
if (err)
info.revision_id1 = -ENOENT;
err = of_property_read_u32(np, "revision-id2", &info.revision_id2);
if (err)
info.revision_id2 = -ENOENT;
err = of_property_read_u32(np, "io-width", &info.io_width);
if (err)
return NULL;
info.io_width = 32 / info.io_width - 1;
err = of_property_read_u32(np, "density", &info.density);
if (err)
return NULL;
info.density = ffs(info.density) - 7;
if (of_device_is_compatible(np, "jedec,lpddr2-s4"))
info.arch_type = LPDDR2_TYPE_S4;
else if (of_device_is_compatible(np, "jedec,lpddr2-s2"))
info.arch_type = LPDDR2_TYPE_S2;
else if (of_device_is_compatible(np, "jedec,lpddr2-nvm"))
info.arch_type = LPDDR2_TYPE_NVM;
else
return NULL;
prop = of_find_property(np, "compatible", NULL);
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp)) {
#define OF_LPDDR2_VENDOR_CMP(compat, ID) \
if (!of_compat_cmp(cp, compat ",", strlen(compat ","))) { \
info.manufacturer_id = LPDDR2_MANID_##ID; \
break; \
}
OF_LPDDR2_VENDOR_CMP("samsung", SAMSUNG)
OF_LPDDR2_VENDOR_CMP("qimonda", QIMONDA)
OF_LPDDR2_VENDOR_CMP("elpida", ELPIDA)
OF_LPDDR2_VENDOR_CMP("etron", ETRON)
OF_LPDDR2_VENDOR_CMP("nanya", NANYA)
OF_LPDDR2_VENDOR_CMP("hynix", HYNIX)
OF_LPDDR2_VENDOR_CMP("mosel", MOSEL)
OF_LPDDR2_VENDOR_CMP("winbond", WINBOND)
OF_LPDDR2_VENDOR_CMP("esmt", ESMT)
OF_LPDDR2_VENDOR_CMP("spansion", SPANSION)
OF_LPDDR2_VENDOR_CMP("sst", SST)
OF_LPDDR2_VENDOR_CMP("zmos", ZMOS)
OF_LPDDR2_VENDOR_CMP("intel", INTEL)
OF_LPDDR2_VENDOR_CMP("numonyx", NUMONYX)
OF_LPDDR2_VENDOR_CMP("micron", MICRON)
#undef OF_LPDDR2_VENDOR_CMP
}
if (!info.manufacturer_id)
info.manufacturer_id = -ENOENT;
ret_info = devm_kzalloc(dev, sizeof(*ret_info), GFP_KERNEL);
if (ret_info)
*ret_info = info;
return ret_info;
}
EXPORT_SYMBOL(of_lpddr2_get_info);

View File

@ -20,6 +20,9 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
const struct lpddr3_timings *
of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
struct device *dev, u32 device_type, u32 *nr_frequencies);
const struct lpddr2_info *of_lpddr2_get_info(struct device_node *np,
struct device *dev);
#else
static inline const struct lpddr2_min_tck
*of_get_min_tck(struct device_node *np, struct device *dev)
@ -46,6 +49,12 @@ static inline const struct lpddr3_timings
{
return NULL;
}
static inline const struct lpddr2_info
*of_lpddr2_get_info(struct device_node *np, struct device *dev)
{
return NULL;
}
#endif /* CONFIG_OF && CONFIG_DDR */
#endif /* __LINUX_MEMORY_OF_REG_ */

View File

@ -160,10 +160,61 @@ static const struct regmap_access_table rpcif_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(rpcif_volatile_ranges),
};
/*
* Custom accessor functions to ensure SMRDR0 and SMWDR0 are always accessed
* with proper width. Requires SMENR_SPIDE to be correctly set before!
*/
static int rpcif_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct rpcif *rpc = context;
if (reg == RPCIF_SMRDR0 || reg == RPCIF_SMWDR0) {
u32 spide = readl(rpc->base + RPCIF_SMENR) & RPCIF_SMENR_SPIDE(0xF);
if (spide == 0x8) {
*val = readb(rpc->base + reg);
return 0;
} else if (spide == 0xC) {
*val = readw(rpc->base + reg);
return 0;
} else if (spide != 0xF) {
return -EILSEQ;
}
}
*val = readl(rpc->base + reg);
return 0;
}
static int rpcif_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct rpcif *rpc = context;
if (reg == RPCIF_SMRDR0 || reg == RPCIF_SMWDR0) {
u32 spide = readl(rpc->base + RPCIF_SMENR) & RPCIF_SMENR_SPIDE(0xF);
if (spide == 0x8) {
writeb(val, rpc->base + reg);
return 0;
} else if (spide == 0xC) {
writew(val, rpc->base + reg);
return 0;
} else if (spide != 0xF) {
return -EILSEQ;
}
}
writel(val, rpc->base + reg);
return 0;
}
static const struct regmap_config rpcif_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.reg_read = rpcif_reg_read,
.reg_write = rpcif_reg_write,
.fast_io = true,
.max_register = RPCIF_PHYINT,
.volatile_table = &rpcif_volatile_table,
@ -173,17 +224,15 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
void __iomem *base;
rpc->dev = dev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
rpc->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(rpc->base))
return PTR_ERR(rpc->base);
rpc->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&rpcif_regmap_config);
rpc->regmap = devm_regmap_init(&pdev->dev, NULL, rpc, &rpcif_regmap_config);
if (IS_ERR(rpc->regmap)) {
dev_err(&pdev->dev,
"failed to init regmap for rpcif, error %ld\n",
@ -354,20 +403,16 @@ void rpcif_prepare(struct rpcif *rpc, const struct rpcif_op *op, u64 *offs,
nbytes = op->data.nbytes;
rpc->xferlen = nbytes;
rpc->enable |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)) |
RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth));
rpc->enable |= RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth));
}
}
EXPORT_SYMBOL(rpcif_prepare);
int rpcif_manual_xfer(struct rpcif *rpc)
{
u32 smenr, smcr, pos = 0, max = 4;
u32 smenr, smcr, pos = 0, max = rpc->bus_size == 2 ? 8 : 4;
int ret = 0;
if (rpc->bus_size == 2)
max = 8;
pm_runtime_get_sync(rpc->dev);
regmap_update_bits(rpc->regmap, RPCIF_PHYCNT,
@ -378,37 +423,36 @@ int rpcif_manual_xfer(struct rpcif *rpc)
regmap_write(rpc->regmap, RPCIF_SMOPR, rpc->option);
regmap_write(rpc->regmap, RPCIF_SMDMCR, rpc->dummy);
regmap_write(rpc->regmap, RPCIF_SMDRENR, rpc->ddr);
regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr);
smenr = rpc->enable;
switch (rpc->dir) {
case RPCIF_DATA_OUT:
while (pos < rpc->xferlen) {
u32 nbytes = rpc->xferlen - pos;
u32 data[2];
u32 bytes_left = rpc->xferlen - pos;
u32 nbytes, data[2];
smcr = rpc->smcr | RPCIF_SMCR_SPIE;
if (nbytes > max) {
nbytes = max;
/* nbytes may only be 1, 2, 4, or 8 */
nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left));
if (bytes_left > nbytes)
smcr |= RPCIF_SMCR_SSLKP;
}
smenr |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes));
regmap_write(rpc->regmap, RPCIF_SMENR, smenr);
memcpy(data, rpc->buffer + pos, nbytes);
if (nbytes > 4) {
if (nbytes == 8) {
regmap_write(rpc->regmap, RPCIF_SMWDR1,
data[0]);
regmap_write(rpc->regmap, RPCIF_SMWDR0,
data[1]);
} else if (nbytes > 2) {
} else {
regmap_write(rpc->regmap, RPCIF_SMWDR0,
data[0]);
} else {
regmap_write(rpc->regmap, RPCIF_SMWDR0,
data[0] << 16);
}
regmap_write(rpc->regmap, RPCIF_SMADR,
rpc->smadr + pos);
regmap_write(rpc->regmap, RPCIF_SMENR, smenr);
regmap_write(rpc->regmap, RPCIF_SMCR, smcr);
ret = wait_msg_xfer_end(rpc);
if (ret)
@ -448,14 +492,16 @@ int rpcif_manual_xfer(struct rpcif *rpc)
break;
}
while (pos < rpc->xferlen) {
u32 nbytes = rpc->xferlen - pos;
u32 data[2];
u32 bytes_left = rpc->xferlen - pos;
u32 nbytes, data[2];
if (nbytes > max)
nbytes = max;
/* nbytes may only be 1, 2, 4, or 8 */
nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left));
regmap_write(rpc->regmap, RPCIF_SMADR,
rpc->smadr + pos);
smenr &= ~RPCIF_SMENR_SPIDE(0xF);
smenr |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes));
regmap_write(rpc->regmap, RPCIF_SMENR, smenr);
regmap_write(rpc->regmap, RPCIF_SMCR,
rpc->smcr | RPCIF_SMCR_SPIE);
@ -463,18 +509,14 @@ int rpcif_manual_xfer(struct rpcif *rpc)
if (ret)
goto err_out;
if (nbytes > 4) {
if (nbytes == 8) {
regmap_read(rpc->regmap, RPCIF_SMRDR1,
&data[0]);
regmap_read(rpc->regmap, RPCIF_SMRDR0,
&data[1]);
} else if (nbytes > 2) {
} else {
regmap_read(rpc->regmap, RPCIF_SMRDR0,
&data[0]);
} else {
regmap_read(rpc->regmap, RPCIF_SMRDR0,
&data[0]);
data[0] >>= 16;
}
memcpy(rpc->buffer + pos, data, nbytes);
@ -502,6 +544,48 @@ err_out:
}
EXPORT_SYMBOL(rpcif_manual_xfer);
static void memcpy_fromio_readw(void *to,
const void __iomem *from,
size_t count)
{
const int maxw = (IS_ENABLED(CONFIG_64BIT)) ? 8 : 4;
u8 buf[2];
if (count && ((unsigned long)from & 1)) {
*(u16 *)buf = __raw_readw((void __iomem *)((unsigned long)from & ~1));
*(u8 *)to = buf[1];
from++;
to++;
count--;
}
while (count >= 2 && !IS_ALIGNED((unsigned long)from, maxw)) {
*(u16 *)to = __raw_readw(from);
from += 2;
to += 2;
count -= 2;
}
while (count >= maxw) {
#ifdef CONFIG_64BIT
*(u64 *)to = __raw_readq(from);
#else
*(u32 *)to = __raw_readl(from);
#endif
from += maxw;
to += maxw;
count -= maxw;
}
while (count >= 2) {
*(u16 *)to = __raw_readw(from);
from += 2;
to += 2;
count -= 2;
}
if (count) {
*(u16 *)buf = __raw_readw(from);
*(u8 *)to = buf[0];
}
}
ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf)
{
loff_t from = offs & (RPCIF_DIRMAP_SIZE - 1);
@ -523,7 +607,10 @@ ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf)
regmap_write(rpc->regmap, RPCIF_DRDMCR, rpc->dummy);
regmap_write(rpc->regmap, RPCIF_DRDRENR, rpc->ddr);
memcpy_fromio(buf, rpc->dirmap + from, len);
if (rpc->bus_size == 2)
memcpy_fromio_readw(buf, rpc->dirmap + from, len);
else
memcpy_fromio(buf, rpc->dirmap + from, len);
pm_runtime_put(rpc->dev);

View File

@ -14,11 +14,12 @@ config EXYNOS5422_DMC
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.
This adds driver for Samsung Exynos5422 SoC 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.
If unsure, say Y on devices with Samsung Exynos SoCs.
config EXYNOS_SROM
bool "Exynos SROM controller driver" if COMPILE_TEST
@ -29,6 +30,6 @@ config EXYNOS_SROM
during suspend. If however appropriate device tree configuration
is provided, the driver enables support for external memory
or external devices.
If unsure, say Y on devices with Samsung Exynos SocS.
If unsure, say Y on devices with Samsung Exynos SoCs.
endif

View File

@ -16,6 +16,7 @@ config TEGRA20_EMC
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ
select DDR
help
This driver is for the External Memory Controller (EMC) found on
Tegra20 chips. The EMC controls the external DRAM on the board.

View File

@ -87,11 +87,9 @@ struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev)
return ERR_PTR(-EPROBE_DEFER);
}
err = devm_add_action(dev, tegra_mc_devm_action_put_device, mc);
if (err) {
put_device(mc->dev);
err = devm_add_action_or_reset(dev, tegra_mc_devm_action_put_device, mc);
if (err)
return ERR_PTR(err);
}
return mc;
}
@ -706,15 +704,6 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
goto remove_nodes;
}
/*
* MC driver is registered too early, so early that generic driver
* syncing doesn't work for the MC. But it doesn't really matter
* since syncing works for the EMC drivers, hence we can sync the
* MC driver by ourselves and then EMC will complete syncing of
* the whole ICC state.
*/
icc_sync_state(mc->dev);
return 0;
remove_nodes:
@ -835,6 +824,15 @@ static int __maybe_unused tegra_mc_resume(struct device *dev)
return 0;
}
static void tegra_mc_sync_state(struct device *dev)
{
struct tegra_mc *mc = dev_get_drvdata(dev);
/* check whether ICC provider is registered */
if (mc->provider.dev == dev)
icc_sync_state(dev);
}
static const struct dev_pm_ops tegra_mc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tegra_mc_suspend, tegra_mc_resume)
};
@ -845,6 +843,7 @@ static struct platform_driver tegra_mc_driver = {
.of_match_table = tegra_mc_of_match,
.pm = &tegra_mc_pm_ops,
.suppress_bind_attrs = true,
.sync_state = tegra_mc_sync_state,
},
.prevent_deferred_probe = true,
.probe = tegra_mc_probe,

View File

@ -197,6 +197,11 @@ static int tegra186_emc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err);
goto put_bpmp;
}
if (msg.rx.ret < 0) {
err = -EINVAL;
dev_err(&pdev->dev, "EMC DVFS MRQ failed: %d (BPMP error code)\n", msg.rx.ret);
goto put_bpmp;
}
emc->debugfs.min_rate = ULONG_MAX;
emc->debugfs.max_rate = 0;

View File

@ -5,6 +5,7 @@
* Author: Dmitry Osipenko <digetx@gmail.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk/tegra.h>
#include <linux/debugfs.h>
@ -27,11 +28,15 @@
#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>
#include "../jedec_ddr.h"
#include "../of_memory.h"
#include "mc.h"
#define EMC_INTSTATUS 0x000
#define EMC_INTMASK 0x004
#define EMC_DBG 0x008
#define EMC_ADR_CFG_0 0x010
#define EMC_TIMING_CONTROL 0x028
#define EMC_RC 0x02c
#define EMC_RFC 0x030
@ -68,6 +73,7 @@
#define EMC_QUSE_EXTRA 0x0ac
#define EMC_ODT_WRITE 0x0b0
#define EMC_ODT_READ 0x0b4
#define EMC_MRR 0x0ec
#define EMC_FBIO_CFG5 0x104
#define EMC_FBIO_CFG6 0x114
#define EMC_STAT_CONTROL 0x160
@ -94,6 +100,7 @@
#define EMC_REFRESH_OVERFLOW_INT BIT(3)
#define EMC_CLKCHANGE_COMPLETE_INT BIT(4)
#define EMC_MRR_DIVLD_INT BIT(5)
#define EMC_DBG_READ_MUX_ASSEMBLY BIT(0)
#define EMC_DBG_WRITE_MUX_ACTIVE BIT(1)
@ -102,11 +109,25 @@
#define EMC_DBG_CFG_PRIORITY BIT(24)
#define EMC_FBIO_CFG5_DRAM_WIDTH_X16 BIT(4)
#define EMC_FBIO_CFG5_DRAM_TYPE GENMASK(1, 0)
#define EMC_MRR_DEV_SELECTN GENMASK(31, 30)
#define EMC_MRR_MRR_MA GENMASK(23, 16)
#define EMC_MRR_MRR_DATA GENMASK(15, 0)
#define EMC_ADR_CFG_0_EMEM_NUMDEV GENMASK(25, 24)
#define EMC_PWR_GATHER_CLEAR (1 << 8)
#define EMC_PWR_GATHER_DISABLE (2 << 8)
#define EMC_PWR_GATHER_ENABLE (3 << 8)
enum emc_dram_type {
DRAM_TYPE_RESERVED,
DRAM_TYPE_DDR1,
DRAM_TYPE_LPDDR2,
DRAM_TYPE_DDR2,
};
static const u16 emc_timing_registers[] = {
EMC_RC,
EMC_RFC,
@ -201,6 +222,14 @@ struct tegra_emc {
struct mutex rate_lock;
struct devfreq_simple_ondemand_data ondemand_data;
/* memory chip identity information */
union lpddr2_basic_config4 basic_conf4;
unsigned int manufacturer_id;
unsigned int revision_id1;
unsigned int revision_id2;
bool mrr_error;
};
static irqreturn_t tegra_emc_isr(int irq, void *data)
@ -397,15 +426,19 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
if (!emc->timings)
return -ENOMEM;
emc->num_timings = child_count;
timing = emc->timings;
for_each_child_of_node(node, child) {
if (of_node_name_eq(child, "lpddr2"))
continue;
err = load_one_timing_from_dt(emc, timing++, child);
if (err) {
of_node_put(child);
return err;
}
emc->num_timings++;
}
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
@ -422,12 +455,18 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
}
static struct device_node *
tegra_emc_find_node_by_ram_code(struct device *dev)
tegra_emc_find_node_by_ram_code(struct tegra_emc *emc)
{
struct device *dev = emc->dev;
struct device_node *np;
u32 value, ram_code;
int err;
if (emc->mrr_error) {
dev_warn(dev, "memory timings skipped due to MRR error\n");
return NULL;
}
if (of_get_child_count(dev->of_node) == 0) {
dev_info_once(dev, "device-tree doesn't have memory timings\n");
return NULL;
@ -442,8 +481,49 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
np = of_find_node_by_name(np, "emc-tables")) {
err = of_property_read_u32(np, "nvidia,ram-code", &value);
if (err || value != ram_code) {
of_node_put(np);
continue;
struct device_node *lpddr2_np;
bool cfg_mismatches = false;
lpddr2_np = of_find_node_by_name(np, "lpddr2");
if (lpddr2_np) {
const struct lpddr2_info *info;
info = of_lpddr2_get_info(lpddr2_np, dev);
if (info) {
if (info->manufacturer_id >= 0 &&
info->manufacturer_id != emc->manufacturer_id)
cfg_mismatches = true;
if (info->revision_id1 >= 0 &&
info->revision_id1 != emc->revision_id1)
cfg_mismatches = true;
if (info->revision_id2 >= 0 &&
info->revision_id2 != emc->revision_id2)
cfg_mismatches = true;
if (info->density != emc->basic_conf4.density)
cfg_mismatches = true;
if (info->io_width != emc->basic_conf4.io_width)
cfg_mismatches = true;
if (info->arch_type != emc->basic_conf4.arch_type)
cfg_mismatches = true;
} else {
dev_err(dev, "failed to parse %pOF\n", lpddr2_np);
cfg_mismatches = true;
}
of_node_put(lpddr2_np);
} else {
cfg_mismatches = true;
}
if (cfg_mismatches) {
of_node_put(np);
continue;
}
}
return np;
@ -455,10 +535,72 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
return NULL;
}
static int emc_read_lpddr_mode_register(struct tegra_emc *emc,
unsigned int emem_dev,
unsigned int register_addr,
unsigned int *register_data)
{
u32 memory_dev = emem_dev + 1;
u32 val, mr_mask = 0xff;
int err;
/* clear data-valid interrupt status */
writel_relaxed(EMC_MRR_DIVLD_INT, emc->regs + EMC_INTSTATUS);
/* issue mode register read request */
val = FIELD_PREP(EMC_MRR_DEV_SELECTN, memory_dev);
val |= FIELD_PREP(EMC_MRR_MRR_MA, register_addr);
writel_relaxed(val, emc->regs + EMC_MRR);
/* wait for the LPDDR2 data-valid interrupt */
err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, val,
val & EMC_MRR_DIVLD_INT,
1, 100);
if (err) {
dev_err(emc->dev, "mode register %u read failed: %d\n",
register_addr, err);
emc->mrr_error = true;
return err;
}
/* read out mode register data */
val = readl_relaxed(emc->regs + EMC_MRR);
*register_data = FIELD_GET(EMC_MRR_MRR_DATA, val) & mr_mask;
return 0;
}
static void emc_read_lpddr_sdram_info(struct tegra_emc *emc,
unsigned int emem_dev,
bool print_out)
{
/* these registers are standard for all LPDDR JEDEC memory chips */
emc_read_lpddr_mode_register(emc, emem_dev, 5, &emc->manufacturer_id);
emc_read_lpddr_mode_register(emc, emem_dev, 6, &emc->revision_id1);
emc_read_lpddr_mode_register(emc, emem_dev, 7, &emc->revision_id2);
emc_read_lpddr_mode_register(emc, emem_dev, 8, &emc->basic_conf4.value);
if (!print_out)
return;
dev_info(emc->dev, "SDRAM[dev%u]: manufacturer: 0x%x (%s) rev1: 0x%x rev2: 0x%x prefetch: S%u density: %uMbit iowidth: %ubit\n",
emem_dev, emc->manufacturer_id,
lpddr2_jedec_manufacturer(emc->manufacturer_id),
emc->revision_id1, emc->revision_id2,
4 >> emc->basic_conf4.arch_type,
64 << emc->basic_conf4.density,
32 >> emc->basic_conf4.io_width);
}
static int emc_setup_hw(struct tegra_emc *emc)
{
u32 emc_cfg, emc_dbg, emc_fbio, emc_adr_cfg;
u32 intmask = EMC_REFRESH_OVERFLOW_INT;
u32 emc_cfg, emc_dbg, emc_fbio;
static bool print_sdram_info_once;
enum emc_dram_type dram_type;
const char *dram_type_str;
unsigned int emem_numdev;
emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
@ -496,7 +638,36 @@ static int emc_setup_hw(struct tegra_emc *emc)
else
emc->dram_bus_width = 32;
dev_info_once(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);
dram_type = FIELD_GET(EMC_FBIO_CFG5_DRAM_TYPE, emc_fbio);
switch (dram_type) {
case DRAM_TYPE_RESERVED:
dram_type_str = "INVALID";
break;
case DRAM_TYPE_DDR1:
dram_type_str = "DDR1";
break;
case DRAM_TYPE_LPDDR2:
dram_type_str = "LPDDR2";
break;
case DRAM_TYPE_DDR2:
dram_type_str = "DDR2";
break;
}
emc_adr_cfg = readl_relaxed(emc->regs + EMC_ADR_CFG_0);
emem_numdev = FIELD_GET(EMC_ADR_CFG_0_EMEM_NUMDEV, emc_adr_cfg) + 1;
dev_info_once(emc->dev, "%ubit DRAM bus, %u %s %s attached\n",
emc->dram_bus_width, emem_numdev, dram_type_str,
emem_numdev == 2 ? "devices" : "device");
if (dram_type == DRAM_TYPE_LPDDR2) {
while (emem_numdev--)
emc_read_lpddr_sdram_info(emc, emem_numdev,
!print_sdram_info_once);
print_sdram_info_once = true;
}
return 0;
}
@ -1049,14 +1220,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
emc->clk_nb.notifier_call = tegra_emc_clk_change_notify;
emc->dev = &pdev->dev;
np = tegra_emc_find_node_by_ram_code(&pdev->dev);
if (np) {
err = tegra_emc_load_timings_from_dt(emc, np);
of_node_put(np);
if (err)
return err;
}
emc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(emc->regs))
return PTR_ERR(emc->regs);
@ -1065,6 +1228,14 @@ static int tegra_emc_probe(struct platform_device *pdev)
if (err)
return err;
np = tegra_emc_find_node_by_ram_code(emc);
if (np) {
err = tegra_emc_load_timings_from_dt(emc, np);
of_node_put(np);
if (err)
return err;
}
err = devm_request_irq(&pdev->dev, irq, tegra_emc_isr, 0,
dev_name(&pdev->dev), emc);
if (err) {
@ -1117,4 +1288,5 @@ module_platform_driver(tegra_emc_driver);
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_DESCRIPTION("NVIDIA Tegra20 EMC driver");
MODULE_SOFTDEP("pre: governor_simpleondemand");
MODULE_LICENSE("GPL v2");

View File

@ -478,7 +478,7 @@ static u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type,
static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc)
{
u32 emc_cfg, emc_cfg_o, emc_cfg_update, del, value;
u32 list[] = {
static const u32 list[] = {
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2,

View File

@ -1662,7 +1662,7 @@ static int tegra210_emc_debug_min_rate_set(void *data, u64 rate)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra210_emc_debug_min_rate_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra210_emc_debug_min_rate_fops,
tegra210_emc_debug_min_rate_get,
tegra210_emc_debug_min_rate_set, "%llu\n");
@ -1692,7 +1692,7 @@ static int tegra210_emc_debug_max_rate_set(void *data, u64 rate)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra210_emc_debug_max_rate_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra210_emc_debug_max_rate_fops,
tegra210_emc_debug_max_rate_get,
tegra210_emc_debug_max_rate_set, "%llu\n");
@ -1723,7 +1723,7 @@ static int tegra210_emc_debug_temperature_set(void *data, u64 temperature)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra210_emc_debug_temperature_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra210_emc_debug_temperature_fops,
tegra210_emc_debug_temperature_get,
tegra210_emc_debug_temperature_set, "%llu\n");

View File

@ -1289,7 +1289,7 @@ static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_min_rate_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra_emc_debug_min_rate_fops,
tegra_emc_debug_min_rate_get,
tegra_emc_debug_min_rate_set, "%llu\n");
@ -1319,7 +1319,7 @@ static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_max_rate_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra_emc_debug_max_rate_fops,
tegra_emc_debug_max_rate_get,
tegra_emc_debug_max_rate_set, "%llu\n");

View File

@ -505,6 +505,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_populate);
static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "qcom,rmtfs-mem" },
{ .compatible = "qcom,cmd-db" },
{ .compatible = "qcom,smem" },
{ .compatible = "ramoops" },
{ .compatible = "nvmem-rmem" },
{}

View File

@ -58,7 +58,7 @@ config RESET_BRCMSTB
a SUN_TOP_CTRL_SW_INIT style controller.
config RESET_BRCMSTB_RESCAL
bool "Broadcom STB RESCAL reset controller"
tristate "Broadcom STB RESCAL reset controller"
depends on HAS_IOMEM
depends on ARCH_BRCMSTB || COMPILE_TEST
default ARCH_BRCMSTB
@ -116,7 +116,7 @@ config RESET_LPC18XX
config RESET_MCHP_SPARX5
bool "Microchip Sparx5 reset driver"
depends on ARCH_SPARX5 || COMPILE_TEST
depends on ARCH_SPARX5 || SOC_LAN966 || COMPILE_TEST
default y if SPARX5_SWITCH
select MFD_SYSCON
help

View File

@ -13,15 +13,18 @@
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#define PROTECT_REG 0x84
#define PROTECT_BIT BIT(10)
#define SOFT_RESET_REG 0x00
#define SOFT_RESET_BIT BIT(1)
struct reset_props {
u32 protect_reg;
u32 protect_bit;
u32 reset_reg;
u32 reset_bit;
};
struct mchp_reset_context {
struct regmap *cpu_ctrl;
struct regmap *gcb_ctrl;
struct reset_controller_dev rcdev;
const struct reset_props *props;
};
static struct regmap_config sparx5_reset_regmap_config = {
@ -38,14 +41,16 @@ static int sparx5_switch_reset(struct reset_controller_dev *rcdev,
u32 val;
/* Make sure the core is PROTECTED from reset */
regmap_update_bits(ctx->cpu_ctrl, PROTECT_REG, PROTECT_BIT, PROTECT_BIT);
regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg,
ctx->props->protect_bit, ctx->props->protect_bit);
/* Start soft reset */
regmap_write(ctx->gcb_ctrl, SOFT_RESET_REG, SOFT_RESET_BIT);
regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg,
ctx->props->reset_bit);
/* Wait for soft reset done */
return regmap_read_poll_timeout(ctx->gcb_ctrl, SOFT_RESET_REG, val,
(val & SOFT_RESET_BIT) == 0,
return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg, val,
(val & ctx->props->reset_bit) == 0,
1, 100);
}
@ -115,13 +120,32 @@ static int mchp_sparx5_reset_probe(struct platform_device *pdev)
ctx->rcdev.nr_resets = 1;
ctx->rcdev.ops = &sparx5_reset_ops;
ctx->rcdev.of_node = dn;
ctx->props = device_get_match_data(&pdev->dev);
return devm_reset_controller_register(&pdev->dev, &ctx->rcdev);
}
static const struct reset_props reset_props_sparx5 = {
.protect_reg = 0x84,
.protect_bit = BIT(10),
.reset_reg = 0x0,
.reset_bit = BIT(1),
};
static const struct reset_props reset_props_lan966x = {
.protect_reg = 0x88,
.protect_bit = BIT(5),
.reset_reg = 0x0,
.reset_bit = BIT(1),
};
static const struct of_device_id mchp_sparx5_reset_of_match[] = {
{
.compatible = "microchip,sparx5-switch-reset",
.data = &reset_props_sparx5,
}, {
.compatible = "microchip,lan966x-switch-reset",
.data = &reset_props_lan966x,
},
{ }
};

View File

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

View File

@ -136,6 +136,21 @@ static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
UNIPHIER_RESETX(28, 0x200c, 7), /* SATA0 */
UNIPHIER_RESETX(29, 0x200c, 8), /* SATA1 */
UNIPHIER_RESETX(30, 0x200c, 21), /* SATA-PHY */
UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */
UNIPHIER_RESETX(42, 0x2010, 2), /* EXIV */
UNIPHIER_RESET_END,
};
static const struct uniphier_reset_data uniphier_nx1_sys_reset_data[] = {
UNIPHIER_RESETX(4, 0x2008, 8), /* eMMC */
UNIPHIER_RESETX(6, 0x200c, 0), /* Ether */
UNIPHIER_RESETX(12, 0x200c, 16), /* USB30 link */
UNIPHIER_RESETX(16, 0x200c, 24), /* USB30-PHY0 */
UNIPHIER_RESETX(17, 0x200c, 25), /* USB30-PHY1 */
UNIPHIER_RESETX(18, 0x200c, 26), /* USB30-PHY2 */
UNIPHIER_RESETX(24, 0x200c, 8), /* PCIe */
UNIPHIER_RESETX(52, 0x2010, 0), /* VOC */
UNIPHIER_RESETX(58, 0x2010, 8), /* HDMI-Tx */
UNIPHIER_RESET_END,
};
@ -400,6 +415,10 @@ static const struct of_device_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-pxs3-reset",
.data = uniphier_pxs3_sys_reset_data,
},
{
.compatible = "socionext,uniphier-nx1-reset",
.data = uniphier_nx1_sys_reset_data,
},
/* Media I/O reset, SD reset */
{
.compatible = "socionext,uniphier-ld4-mio-reset",
@ -437,6 +456,10 @@ static const struct of_device_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-pxs3-sd-reset",
.data = uniphier_pro5_sd_reset_data,
},
{
.compatible = "socionext,uniphier-nx1-sd-reset",
.data = uniphier_pro5_sd_reset_data,
},
/* Peripheral reset */
{
.compatible = "socionext,uniphier-ld4-peri-reset",
@ -470,6 +493,10 @@ static const struct of_device_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-pxs3-peri-reset",
.data = uniphier_pro4_peri_reset_data,
},
{
.compatible = "socionext,uniphier-nx1-peri-reset",
.data = uniphier_pro4_peri_reset_data,
},
/* Analog signal amplifiers reset */
{
.compatible = "socionext,uniphier-ld11-adamv-reset",

View File

@ -1404,16 +1404,10 @@ config RTC_DRV_OMAP
This driver can also be built as a module, if so, module
will be called rtc-omap.
config HAVE_S3C_RTC
bool
help
This will include RTC support for Samsung SoCs. If
you want to include RTC support for any machine, kindly
select this in the respective mach-XXXX/Kconfig file.
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
depends on ARCH_S3C64XX || HAVE_S3C_RTC || COMPILE_TEST
depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S3C24XX || ARCH_S5PV210 || \
COMPILE_TEST
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic

View File

@ -168,7 +168,6 @@ EXPORT_SYMBOL_GPL(meson_canvas_free);
static int meson_canvas_probe(struct platform_device *pdev)
{
struct resource *res;
struct meson_canvas *canvas;
struct device *dev = &pdev->dev;
@ -176,8 +175,7 @@ static int meson_canvas_probe(struct platform_device *pdev)
if (!canvas)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
canvas->reg_base = devm_ioremap_resource(dev, res);
canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(canvas->reg_base))
return PTR_ERR(canvas->reg_base);

View File

@ -606,7 +606,6 @@ static int meson_msr_probe(struct platform_device *pdev)
{
const struct meson_msr_id *match_data;
struct meson_msr *priv;
struct resource *res;
struct dentry *root, *clks;
void __iomem *base;
int i;
@ -624,8 +623,7 @@ static int meson_msr_probe(struct platform_device *pdev)
memcpy(priv->msr_table, match_data, sizeof(priv->msr_table));
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);

View File

@ -65,6 +65,7 @@ static const struct meson_gx_package_id {
{ "A113X", 0x25, 0x37, 0xff },
{ "A113D", 0x25, 0x22, 0xff },
{ "S905D2", 0x28, 0x10, 0xf0 },
{ "S905Y2", 0x28, 0x30, 0xf0 },
{ "S905X2", 0x28, 0x40, 0xf0 },
{ "A311D", 0x29, 0x10, 0xf0 },
{ "S922X", 0x29, 0x40, 0xf0 },

View File

@ -24,6 +24,16 @@ config ASPEED_LPC_SNOOP
allows the BMC to listen on and save the data written by
the host to an arbitrary LPC I/O port.
config ASPEED_UART_ROUTING
tristate "ASPEED uart routing control"
select REGMAP
select MFD_SYSCON
default ARCH_ASPEED
help
Provides a driver to control the UART routing paths, allowing
users to perform runtime configuration of the RX muxes among
the UART controllers and I/O pins.
config ASPEED_P2A_CTRL
tristate "ASPEED P2A (VGA MMIO to BMC) bridge control"
select REGMAP

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o

View File

@ -0,0 +1,603 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2018 Google LLC
* Copyright (c) 2021 Aspeed Technology Inc.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
/* register offsets */
#define HICR9 0x98
#define HICRA 0x9c
/* attributes options */
#define UART_ROUTING_IO1 "io1"
#define UART_ROUTING_IO2 "io2"
#define UART_ROUTING_IO3 "io3"
#define UART_ROUTING_IO4 "io4"
#define UART_ROUTING_IO5 "io5"
#define UART_ROUTING_IO6 "io6"
#define UART_ROUTING_IO10 "io10"
#define UART_ROUTING_UART1 "uart1"
#define UART_ROUTING_UART2 "uart2"
#define UART_ROUTING_UART3 "uart3"
#define UART_ROUTING_UART4 "uart4"
#define UART_ROUTING_UART5 "uart5"
#define UART_ROUTING_UART6 "uart6"
#define UART_ROUTING_UART10 "uart10"
#define UART_ROUTING_RES "reserved"
struct aspeed_uart_routing {
struct regmap *map;
struct attribute_group const *attr_grp;
};
struct aspeed_uart_routing_selector {
struct device_attribute dev_attr;
uint8_t reg;
uint8_t mask;
uint8_t shift;
const char *const options[];
};
#define to_routing_selector(_dev_attr) \
container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr)
static ssize_t aspeed_uart_routing_show(struct device *dev,
struct device_attribute *attr,
char *buf);
static ssize_t aspeed_uart_routing_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
#define ROUTING_ATTR(_name) { \
.attr = {.name = _name, \
.mode = VERIFY_OCTAL_PERMISSIONS(0644) }, \
.show = aspeed_uart_routing_show, \
.store = aspeed_uart_routing_store, \
}
/* routing selector for AST25xx */
static struct aspeed_uart_routing_selector ast2500_io6_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO6),
.reg = HICR9,
.shift = 8,
.mask = 0xf,
.options = {
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART5,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO5,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_uart5_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART5),
.reg = HICRA,
.shift = 28,
.mask = 0xf,
.options = {
UART_ROUTING_IO5,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_uart4_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART4),
.reg = HICRA,
.shift = 25,
.mask = 0x7,
.options = {
UART_ROUTING_IO4,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_uart3_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART3),
.reg = HICRA,
.shift = 22,
.mask = 0x7,
.options = {
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_UART4,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_uart2_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART2),
.reg = HICRA,
.shift = 19,
.mask = 0x7,
.options = {
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO1,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART1,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_uart1_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART1),
.reg = HICRA,
.shift = 16,
.mask = 0x7,
.options = {
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_io5_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO5),
.reg = HICRA,
.shift = 12,
.mask = 0x7,
.options = {
UART_ROUTING_UART5,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_IO1,
UART_ROUTING_IO3,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_io4_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO4),
.reg = HICRA,
.shift = 9,
.mask = 0x7,
.options = {
UART_ROUTING_UART4,
UART_ROUTING_UART5,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_io3_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO3),
.reg = HICRA,
.shift = 6,
.mask = 0x7,
.options = {
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART5,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_io2_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO2),
.reg = HICRA,
.shift = 3,
.mask = 0x7,
.options = {
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART5,
UART_ROUTING_UART1,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO6,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2500_io1_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO1),
.reg = HICRA,
.shift = 0,
.mask = 0x7,
.options = {
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART5,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO6,
NULL,
},
};
static struct attribute *ast2500_uart_routing_attrs[] = {
&ast2500_io6_sel.dev_attr.attr,
&ast2500_uart5_sel.dev_attr.attr,
&ast2500_uart4_sel.dev_attr.attr,
&ast2500_uart3_sel.dev_attr.attr,
&ast2500_uart2_sel.dev_attr.attr,
&ast2500_uart1_sel.dev_attr.attr,
&ast2500_io5_sel.dev_attr.attr,
&ast2500_io4_sel.dev_attr.attr,
&ast2500_io3_sel.dev_attr.attr,
&ast2500_io2_sel.dev_attr.attr,
&ast2500_io1_sel.dev_attr.attr,
NULL,
};
static const struct attribute_group ast2500_uart_routing_attr_group = {
.attrs = ast2500_uart_routing_attrs,
};
/* routing selector for AST26xx */
static struct aspeed_uart_routing_selector ast2600_uart10_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART10),
.reg = HICR9,
.shift = 12,
.mask = 0xf,
.options = {
UART_ROUTING_IO10,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_RES,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_io10_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO10),
.reg = HICR9,
.shift = 8,
.mask = 0xf,
.options = {
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_RES,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_RES,
UART_ROUTING_UART10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_uart4_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART4),
.reg = HICRA,
.shift = 25,
.mask = 0x7,
.options = {
UART_ROUTING_IO4,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_uart3_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART3),
.reg = HICRA,
.shift = 22,
.mask = 0x7,
.options = {
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_UART4,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_uart2_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART2),
.reg = HICRA,
.shift = 19,
.mask = 0x7,
.options = {
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO1,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART1,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_uart1_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART1),
.reg = HICRA,
.shift = 16,
.mask = 0x7,
.options = {
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_io4_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO4),
.reg = HICRA,
.shift = 9,
.mask = 0x7,
.options = {
UART_ROUTING_UART4,
UART_ROUTING_UART10,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_io3_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO3),
.reg = HICRA,
.shift = 6,
.mask = 0x7,
.options = {
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART10,
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_IO1,
UART_ROUTING_IO2,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_io2_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO2),
.reg = HICRA,
.shift = 3,
.mask = 0x7,
.options = {
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART10,
UART_ROUTING_UART1,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO10,
NULL,
},
};
static struct aspeed_uart_routing_selector ast2600_io1_sel = {
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO1),
.reg = HICRA,
.shift = 0,
.mask = 0x7,
.options = {
UART_ROUTING_UART1,
UART_ROUTING_UART2,
UART_ROUTING_UART3,
UART_ROUTING_UART4,
UART_ROUTING_UART10,
UART_ROUTING_IO3,
UART_ROUTING_IO4,
UART_ROUTING_IO10,
NULL,
},
};
static struct attribute *ast2600_uart_routing_attrs[] = {
&ast2600_uart10_sel.dev_attr.attr,
&ast2600_io10_sel.dev_attr.attr,
&ast2600_uart4_sel.dev_attr.attr,
&ast2600_uart3_sel.dev_attr.attr,
&ast2600_uart2_sel.dev_attr.attr,
&ast2600_uart1_sel.dev_attr.attr,
&ast2600_io4_sel.dev_attr.attr,
&ast2600_io3_sel.dev_attr.attr,
&ast2600_io2_sel.dev_attr.attr,
&ast2600_io1_sel.dev_attr.attr,
NULL,
};
static const struct attribute_group ast2600_uart_routing_attr_group = {
.attrs = ast2600_uart_routing_attrs,
};
static ssize_t aspeed_uart_routing_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
int val, pos, len;
regmap_read(uart_routing->map, sel->reg, &val);
val = (val >> sel->shift) & sel->mask;
len = 0;
for (pos = 0; sel->options[pos] != NULL; ++pos) {
if (pos == val)
len += sysfs_emit_at(buf, len, "[%s] ", sel->options[pos]);
else
len += sysfs_emit_at(buf, len, "%s ", sel->options[pos]);
}
if (val >= pos)
len += sysfs_emit_at(buf, len, "[unknown(%d)]", val);
len += sysfs_emit_at(buf, len, "\n");
return len;
}
static ssize_t aspeed_uart_routing_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
int val;
val = match_string(sel->options, -1, buf);
if (val < 0) {
dev_err(dev, "invalid value \"%s\"\n", buf);
return -EINVAL;
}
regmap_update_bits(uart_routing->map, sel->reg,
(sel->mask << sel->shift),
(val & sel->mask) << sel->shift);
return count;
}
static int aspeed_uart_routing_probe(struct platform_device *pdev)
{
int rc;
struct device *dev = &pdev->dev;
struct aspeed_uart_routing *uart_routing;
uart_routing = devm_kzalloc(&pdev->dev, sizeof(*uart_routing), GFP_KERNEL);
if (!uart_routing)
return -ENOMEM;
uart_routing->map = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(uart_routing->map)) {
dev_err(dev, "cannot get regmap\n");
return PTR_ERR(uart_routing->map);
}
uart_routing->attr_grp = of_device_get_match_data(dev);
rc = sysfs_create_group(&dev->kobj, uart_routing->attr_grp);
if (rc < 0)
return rc;
dev_set_drvdata(dev, uart_routing);
dev_info(dev, "module loaded\n");
return 0;
}
static int aspeed_uart_routing_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev);
sysfs_remove_group(&dev->kobj, uart_routing->attr_grp);
return 0;
}
static const struct of_device_id aspeed_uart_routing_table[] = {
{ .compatible = "aspeed,ast2400-uart-routing",
.data = &ast2500_uart_routing_attr_group },
{ .compatible = "aspeed,ast2500-uart-routing",
.data = &ast2500_uart_routing_attr_group },
{ .compatible = "aspeed,ast2600-uart-routing",
.data = &ast2600_uart_routing_attr_group },
{ },
};
static struct platform_driver aspeed_uart_routing_driver = {
.driver = {
.name = "aspeed-uart-routing",
.of_match_table = aspeed_uart_routing_table,
},
.probe = aspeed_uart_routing_probe,
.remove = aspeed_uart_routing_remove,
};
module_platform_driver(aspeed_uart_routing_driver);
MODULE_AUTHOR("Oskar Senft <osk@google.com>");
MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Driver to configure Aspeed UART routing");

View File

@ -276,7 +276,6 @@ static int bcm_pmb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct bcm_pmb_pd_data *table;
const struct bcm_pmb_pd_data *e;
struct resource *res;
struct bcm_pmb *pmb;
int max_id;
int err;
@ -287,8 +286,7 @@ static int bcm_pmb_probe(struct platform_device *pdev)
pmb->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pmb->base = devm_ioremap_resource(&pdev->dev, res);
pmb->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pmb->base))
return PTR_ERR(pmb->base);

View File

@ -91,7 +91,6 @@ static int bcm63xx_power_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct resource *res;
const struct bcm63xx_power_data *entry, *table;
struct bcm63xx_power *power;
unsigned int ndom;
@ -102,8 +101,7 @@ static int bcm63xx_power_probe(struct platform_device *pdev)
if (!power)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
power->base = devm_ioremap_resource(&pdev->dev, res);
power->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(power->base))
return PTR_ERR(power->base);

View File

@ -136,6 +136,8 @@ static int __init mcp_write_pairing_set(void)
static const u32 a72_b53_mach_compat[] = {
0x7211,
0x72113,
0x72116,
0x7216,
0x72164,
0x72165,

View File

@ -500,7 +500,7 @@ int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d,
qbman_eq_desc_set_no_orp(&ed, 0);
qbman_eq_desc_set_fq(&ed, fqid);
return qbman_swp_enqueue_multiple(d->swp, &ed, fd, 0, nb);
return qbman_swp_enqueue_multiple(d->swp, &ed, fd, NULL, nb);
}
EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_fq);

View File

@ -693,9 +693,9 @@ int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s,
p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
p[0] = cl[0] | s->eqcr.pi_vb;
if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) {
struct qbman_eq_desc *d = (struct qbman_eq_desc *)p;
struct qbman_eq_desc *eq_desc = (struct qbman_eq_desc *)p;
d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
eq_desc->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
((flags[i]) & QBMAN_EQCR_DCA_IDXMASK);
}
eqcr_pi++;
@ -775,9 +775,9 @@ int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s,
p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
p[0] = cl[0] | s->eqcr.pi_vb;
if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) {
struct qbman_eq_desc *d = (struct qbman_eq_desc *)p;
struct qbman_eq_desc *eq_desc = (struct qbman_eq_desc *)p;
d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
eq_desc->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
((flags[i]) & QBMAN_EQCR_DCA_IDXMASK);
}
eqcr_pi++;

View File

@ -140,7 +140,6 @@ static int fsl_guts_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct resource *res;
const struct fsl_soc_die_attr *soc_die;
const char *machine;
u32 svr;
@ -152,8 +151,7 @@ static int fsl_guts_probe(struct platform_device *pdev)
guts->little_endian = of_property_read_bool(np, "little-endian");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
guts->regs = devm_ioremap_resource(dev, res);
guts->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(guts->regs))
return PTR_ERR(guts->regs);

View File

@ -146,7 +146,6 @@ static const struct dev_pm_ops rcpm_pm_ops = {
static int rcpm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *r;
struct rcpm *rcpm;
int ret;
@ -154,11 +153,7 @@ static int rcpm_probe(struct platform_device *pdev)
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);
rcpm->ippdexpcr_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rcpm->ippdexpcr_base)) {
ret = PTR_ERR(rcpm->ippdexpcr_base);
return ret;

View File

@ -6,6 +6,7 @@ config IMX_GPCV2_PM_DOMAINS
depends on ARCH_MXC || (COMPILE_TEST && OF)
depends on PM
select PM_GENERIC_DOMAINS
select REGMAP_MMIO
default y if SOC_IMX7D
config SOC_IMX8M

View File

@ -5,3 +5,4 @@ endif
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o
obj-$(CONFIG_SOC_IMX8M) += imx8m-blk-ctrl.o

View File

@ -192,7 +192,7 @@ struct imx_pgc_domain {
struct clk_bulk_data *clks;
int num_clks;
unsigned int pgc;
unsigned long pgc;
const struct {
u32 pxx;
@ -202,6 +202,7 @@ struct imx_pgc_domain {
} bits;
const int voltage;
const bool keep_clocks;
struct device *dev;
};
@ -220,7 +221,7 @@ to_imx_pgc_domain(struct generic_pm_domain *genpd)
static int imx_pgc_power_up(struct generic_pm_domain *genpd)
{
struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
u32 reg_val;
u32 reg_val, pgc;
int ret;
ret = pm_runtime_get_sync(domain->dev);
@ -244,6 +245,8 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
goto out_regulator_disable;
}
reset_control_assert(domain->reset);
if (domain->bits.pxx) {
/* request the domain to power up */
regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PUP_REQ,
@ -262,12 +265,12 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
}
/* disable power control */
regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
GPC_PGC_CTRL_PCR);
for_each_set_bit(pgc, &domain->pgc, 32) {
regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc),
GPC_PGC_CTRL_PCR);
}
}
reset_control_assert(domain->reset);
/* delay for reset to propagate */
udelay(5);
@ -293,7 +296,8 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
}
/* Disable reset clocks for all devices in the domain */
clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
if (!domain->keep_clocks)
clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
return 0;
@ -311,14 +315,16 @@ out_put_pm:
static int imx_pgc_power_down(struct generic_pm_domain *genpd)
{
struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
u32 reg_val;
u32 reg_val, pgc;
int ret;
/* Enable reset clocks for all devices in the domain */
ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
if (ret) {
dev_err(domain->dev, "failed to enable reset clocks\n");
return ret;
if (!domain->keep_clocks) {
ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
if (ret) {
dev_err(domain->dev, "failed to enable reset clocks\n");
return ret;
}
}
/* request the ADB400 to power down */
@ -338,8 +344,10 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
if (domain->bits.pxx) {
/* enable power control */
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
for_each_set_bit(pgc, &domain->pgc, 32) {
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc),
GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
}
/* request the domain to power down */
regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PDN_REQ,
@ -389,7 +397,7 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.map = IMX7_MIPI_PHY_A_CORE_DOMAIN,
},
.voltage = 1000000,
.pgc = IMX7_PGC_MIPI,
.pgc = BIT(IMX7_PGC_MIPI),
},
[IMX7_POWER_DOMAIN_PCIE_PHY] = {
@ -401,7 +409,7 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.map = IMX7_PCIE_PHY_A_CORE_DOMAIN,
},
.voltage = 1000000,
.pgc = IMX7_PGC_PCIE,
.pgc = BIT(IMX7_PGC_PCIE),
},
[IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
@ -413,7 +421,7 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN,
},
.voltage = 1200000,
.pgc = IMX7_PGC_USB_HSIC,
.pgc = BIT(IMX7_PGC_USB_HSIC),
},
};
@ -448,7 +456,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_MIPI_SW_Pxx_REQ,
.map = IMX8M_MIPI_A53_DOMAIN,
},
.pgc = IMX8M_PGC_MIPI,
.pgc = BIT(IMX8M_PGC_MIPI),
},
[IMX8M_POWER_DOMAIN_PCIE1] = {
@ -459,7 +467,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_PCIE1_SW_Pxx_REQ,
.map = IMX8M_PCIE1_A53_DOMAIN,
},
.pgc = IMX8M_PGC_PCIE1,
.pgc = BIT(IMX8M_PGC_PCIE1),
},
[IMX8M_POWER_DOMAIN_USB_OTG1] = {
@ -470,7 +478,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_OTG1_SW_Pxx_REQ,
.map = IMX8M_OTG1_A53_DOMAIN,
},
.pgc = IMX8M_PGC_OTG1,
.pgc = BIT(IMX8M_PGC_OTG1),
},
[IMX8M_POWER_DOMAIN_USB_OTG2] = {
@ -481,7 +489,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_OTG2_SW_Pxx_REQ,
.map = IMX8M_OTG2_A53_DOMAIN,
},
.pgc = IMX8M_PGC_OTG2,
.pgc = BIT(IMX8M_PGC_OTG2),
},
[IMX8M_POWER_DOMAIN_DDR1] = {
@ -492,7 +500,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_DDR1_SW_Pxx_REQ,
.map = IMX8M_DDR2_A53_DOMAIN,
},
.pgc = IMX8M_PGC_DDR1,
.pgc = BIT(IMX8M_PGC_DDR1),
},
[IMX8M_POWER_DOMAIN_GPU] = {
@ -505,7 +513,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.hskreq = IMX8M_GPU_HSK_PWRDNREQN,
.hskack = IMX8M_GPU_HSK_PWRDNACKN,
},
.pgc = IMX8M_PGC_GPU,
.pgc = BIT(IMX8M_PGC_GPU),
},
[IMX8M_POWER_DOMAIN_VPU] = {
@ -518,7 +526,8 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.hskreq = IMX8M_VPU_HSK_PWRDNREQN,
.hskack = IMX8M_VPU_HSK_PWRDNACKN,
},
.pgc = IMX8M_PGC_VPU,
.pgc = BIT(IMX8M_PGC_VPU),
.keep_clocks = true,
},
[IMX8M_POWER_DOMAIN_DISP] = {
@ -531,7 +540,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.hskreq = IMX8M_DISP_HSK_PWRDNREQN,
.hskack = IMX8M_DISP_HSK_PWRDNACKN,
},
.pgc = IMX8M_PGC_DISP,
.pgc = BIT(IMX8M_PGC_DISP),
},
[IMX8M_POWER_DOMAIN_MIPI_CSI1] = {
@ -542,7 +551,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ,
.map = IMX8M_MIPI_CSI1_A53_DOMAIN,
},
.pgc = IMX8M_PGC_MIPI_CSI1,
.pgc = BIT(IMX8M_PGC_MIPI_CSI1),
},
[IMX8M_POWER_DOMAIN_MIPI_CSI2] = {
@ -553,7 +562,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ,
.map = IMX8M_MIPI_CSI2_A53_DOMAIN,
},
.pgc = IMX8M_PGC_MIPI_CSI2,
.pgc = BIT(IMX8M_PGC_MIPI_CSI2),
},
[IMX8M_POWER_DOMAIN_PCIE2] = {
@ -564,7 +573,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_PCIE2_SW_Pxx_REQ,
.map = IMX8M_PCIE2_A53_DOMAIN,
},
.pgc = IMX8M_PGC_PCIE2,
.pgc = BIT(IMX8M_PGC_PCIE2),
},
};
@ -617,6 +626,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_HSIO_HSK_PWRDNREQN,
.hskack = IMX8MM_HSIO_HSK_PWRDNACKN,
},
.keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_PCIE] = {
@ -627,7 +637,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_PCIE_SW_Pxx_REQ,
.map = IMX8MM_PCIE_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_PCIE,
.pgc = BIT(IMX8MM_PGC_PCIE),
},
[IMX8MM_POWER_DOMAIN_OTG1] = {
@ -638,7 +648,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_OTG1_SW_Pxx_REQ,
.map = IMX8MM_OTG1_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_OTG1,
.pgc = BIT(IMX8MM_PGC_OTG1),
},
[IMX8MM_POWER_DOMAIN_OTG2] = {
@ -649,7 +659,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_OTG2_SW_Pxx_REQ,
.map = IMX8MM_OTG2_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_OTG2,
.pgc = BIT(IMX8MM_PGC_OTG2),
},
[IMX8MM_POWER_DOMAIN_GPUMIX] = {
@ -662,7 +672,8 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN,
.hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN,
},
.pgc = IMX8MM_PGC_GPUMIX,
.pgc = BIT(IMX8MM_PGC_GPUMIX),
.keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_GPU] = {
@ -675,7 +686,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_GPU_HSK_PWRDNREQN,
.hskack = IMX8MM_GPU_HSK_PWRDNACKN,
},
.pgc = IMX8MM_PGC_GPU2D,
.pgc = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D),
},
[IMX8MM_POWER_DOMAIN_VPUMIX] = {
@ -688,7 +699,8 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN,
.hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN,
},
.pgc = IMX8MM_PGC_VPUMIX,
.pgc = BIT(IMX8MM_PGC_VPUMIX),
.keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_VPUG1] = {
@ -699,7 +711,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_VPUG1_SW_Pxx_REQ,
.map = IMX8MM_VPUG1_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_VPUG1,
.pgc = BIT(IMX8MM_PGC_VPUG1),
},
[IMX8MM_POWER_DOMAIN_VPUG2] = {
@ -710,7 +722,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_VPUG2_SW_Pxx_REQ,
.map = IMX8MM_VPUG2_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_VPUG2,
.pgc = BIT(IMX8MM_PGC_VPUG2),
},
[IMX8MM_POWER_DOMAIN_VPUH1] = {
@ -721,7 +733,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_VPUH1_SW_Pxx_REQ,
.map = IMX8MM_VPUH1_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_VPUH1,
.pgc = BIT(IMX8MM_PGC_VPUH1),
},
[IMX8MM_POWER_DOMAIN_DISPMIX] = {
@ -734,7 +746,8 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN,
.hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN,
},
.pgc = IMX8MM_PGC_DISPMIX,
.pgc = BIT(IMX8MM_PGC_DISPMIX),
.keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_MIPI] = {
@ -745,7 +758,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_MIPI_SW_Pxx_REQ,
.map = IMX8MM_MIPI_A53_DOMAIN,
},
.pgc = IMX8MM_PGC_MIPI,
.pgc = BIT(IMX8MM_PGC_MIPI),
},
};
@ -802,6 +815,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.hskreq = IMX8MN_HSIO_HSK_PWRDNREQN,
.hskack = IMX8MN_HSIO_HSK_PWRDNACKN,
},
.keep_clocks = true,
},
[IMX8MN_POWER_DOMAIN_OTG1] = {
@ -812,7 +826,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.pxx = IMX8MN_OTG1_SW_Pxx_REQ,
.map = IMX8MN_OTG1_A53_DOMAIN,
},
.pgc = IMX8MN_PGC_OTG1,
.pgc = BIT(IMX8MN_PGC_OTG1),
},
[IMX8MN_POWER_DOMAIN_GPUMIX] = {
@ -825,7 +839,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN,
.hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN,
},
.pgc = IMX8MN_PGC_GPUMIX,
.pgc = BIT(IMX8MN_PGC_GPUMIX),
},
};
@ -894,6 +908,10 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
goto out_domain_unmap;
}
if (IS_ENABLED(CONFIG_LOCKDEP) &&
of_property_read_bool(domain->dev->of_node, "power-domains"))
lockdep_set_subclass(&domain->genpd.mlock, 1);
ret = of_genpd_add_provider_simple(domain->dev->of_node,
&domain->genpd);
if (ret) {
@ -930,6 +948,36 @@ static int imx_pgc_domain_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int imx_pgc_domain_suspend(struct device *dev)
{
int ret;
/*
* This may look strange, but is done so the generic PM_SLEEP code
* can power down our domain and more importantly power it up again
* after resume, without tripping over our usage of runtime PM to
* power up/down the nested domains.
*/
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
return ret;
}
return 0;
}
static int imx_pgc_domain_resume(struct device *dev)
{
return pm_runtime_put(dev);
}
#endif
static const struct dev_pm_ops imx_pgc_domain_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(imx_pgc_domain_suspend, imx_pgc_domain_resume)
};
static const struct platform_device_id imx_pgc_domain_id[] = {
{ "imx-pgc-domain", },
{ },
@ -938,6 +986,7 @@ static const struct platform_device_id imx_pgc_domain_id[] = {
static struct platform_driver imx_pgc_domain_driver = {
.driver = {
.name = "imx-pgc",
.pm = &imx_pgc_domain_pm_ops,
},
.probe = imx_pgc_domain_probe,
.remove = imx_pgc_domain_remove,
@ -986,6 +1035,9 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
struct imx_pgc_domain *domain;
u32 domain_index;
if (!of_device_is_available(np))
continue;
ret = of_property_read_u32(np, "reg", &domain_index);
if (ret) {
dev_err(dev, "Failed to read 'reg' property\n");

View File

@ -0,0 +1,523 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2021 Pengutronix, Lucas Stach <kernel@pengutronix.de>
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/clk.h>
#include <dt-bindings/power/imx8mm-power.h>
#define BLK_SFT_RSTN 0x0
#define BLK_CLK_EN 0x4
struct imx8m_blk_ctrl_domain;
struct imx8m_blk_ctrl {
struct device *dev;
struct notifier_block power_nb;
struct device *bus_power_dev;
struct regmap *regmap;
struct imx8m_blk_ctrl_domain *domains;
struct genpd_onecell_data onecell_data;
};
struct imx8m_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
int num_clks;
const char *gpc_name;
u32 rst_mask;
u32 clk_mask;
};
#define DOMAIN_MAX_CLKS 3
struct imx8m_blk_ctrl_domain {
struct generic_pm_domain genpd;
const struct imx8m_blk_ctrl_domain_data *data;
struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
struct device *power_dev;
struct imx8m_blk_ctrl *bc;
};
struct imx8m_blk_ctrl_data {
int max_reg;
notifier_fn_t power_notifier_fn;
const struct imx8m_blk_ctrl_domain_data *domains;
int num_domains;
};
static inline struct imx8m_blk_ctrl_domain *
to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd)
{
return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd);
}
static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
{
struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
const struct imx8m_blk_ctrl_domain_data *data = domain->data;
struct imx8m_blk_ctrl *bc = domain->bc;
int ret;
/* make sure bus domain is awake */
ret = pm_runtime_get_sync(bc->bus_power_dev);
if (ret < 0) {
pm_runtime_put_noidle(bc->bus_power_dev);
dev_err(bc->dev, "failed to power up bus domain\n");
return ret;
}
/* put devices into reset */
regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
/* enable upstream and blk-ctrl clocks to allow reset to propagate */
ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
if (ret) {
dev_err(bc->dev, "failed to enable clocks\n");
goto bus_put;
}
regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
/* power up upstream GPC domain */
ret = pm_runtime_get_sync(domain->power_dev);
if (ret < 0) {
dev_err(bc->dev, "failed to power up peripheral domain\n");
goto clk_disable;
}
/* wait for reset to propagate */
udelay(5);
/* release reset */
regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
/* disable upstream clocks */
clk_bulk_disable_unprepare(data->num_clks, domain->clks);
return 0;
clk_disable:
clk_bulk_disable_unprepare(data->num_clks, domain->clks);
bus_put:
pm_runtime_put(bc->bus_power_dev);
return ret;
}
static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd)
{
struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
const struct imx8m_blk_ctrl_domain_data *data = domain->data;
struct imx8m_blk_ctrl *bc = domain->bc;
/* put devices into reset and disable clocks */
regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
/* power down upstream GPC domain */
pm_runtime_put(domain->power_dev);
/* allow bus domain to suspend */
pm_runtime_put(bc->bus_power_dev);
return 0;
}
static struct generic_pm_domain *
imx8m_blk_ctrl_xlate(struct of_phandle_args *args, void *data)
{
struct genpd_onecell_data *onecell_data = data;
unsigned int index = args->args[0];
if (args->args_count != 1 ||
index >= onecell_data->num_domains)
return ERR_PTR(-EINVAL);
return onecell_data->domains[index];
}
static struct lock_class_key blk_ctrl_genpd_lock_class;
static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
{
const struct imx8m_blk_ctrl_data *bc_data;
struct device *dev = &pdev->dev;
struct imx8m_blk_ctrl *bc;
void __iomem *base;
int i, ret;
struct regmap_config regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
if (!bc)
return -ENOMEM;
bc->dev = dev;
bc_data = of_device_get_match_data(dev);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
regmap_config.max_register = bc_data->max_reg;
bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
if (IS_ERR(bc->regmap))
return dev_err_probe(dev, PTR_ERR(bc->regmap),
"failed to init regmap\n");
bc->domains = devm_kcalloc(dev, bc_data->num_domains,
sizeof(struct imx8m_blk_ctrl_domain),
GFP_KERNEL);
if (!bc->domains)
return -ENOMEM;
bc->onecell_data.num_domains = bc_data->num_domains;
bc->onecell_data.xlate = imx8m_blk_ctrl_xlate;
bc->onecell_data.domains =
devm_kcalloc(dev, bc_data->num_domains,
sizeof(struct generic_pm_domain *), GFP_KERNEL);
if (!bc->onecell_data.domains)
return -ENOMEM;
bc->bus_power_dev = genpd_dev_pm_attach_by_name(dev, "bus");
if (IS_ERR(bc->bus_power_dev))
return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
"failed to attach power domain\n");
for (i = 0; i < bc_data->num_domains; i++) {
const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i];
struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
int j;
domain->data = data;
for (j = 0; j < data->num_clks; j++)
domain->clks[j].id = data->clk_names[j];
ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
if (ret) {
dev_err_probe(dev, ret, "failed to get clock\n");
goto cleanup_pds;
}
domain->power_dev =
dev_pm_domain_attach_by_name(dev, data->gpc_name);
if (IS_ERR(domain->power_dev)) {
dev_err_probe(dev, PTR_ERR(domain->power_dev),
"failed to attach power domain\n");
ret = PTR_ERR(domain->power_dev);
goto cleanup_pds;
}
domain->genpd.name = data->name;
domain->genpd.power_on = imx8m_blk_ctrl_power_on;
domain->genpd.power_off = imx8m_blk_ctrl_power_off;
domain->bc = bc;
ret = pm_genpd_init(&domain->genpd, NULL, true);
if (ret) {
dev_err_probe(dev, ret, "failed to init power domain\n");
dev_pm_domain_detach(domain->power_dev, true);
goto cleanup_pds;
}
/*
* We use runtime PM to trigger power on/off of the upstream GPC
* domain, as a strict hierarchical parent/child power domain
* setup doesn't allow us to meet the sequencing requirements.
* This means we have nested locking of genpd locks, without the
* nesting being visible at the genpd level, so we need a
* separate lock class to make lockdep aware of the fact that
* this are separate domain locks that can be nested without a
* self-deadlock.
*/
lockdep_set_class(&domain->genpd.mlock,
&blk_ctrl_genpd_lock_class);
bc->onecell_data.domains[i] = &domain->genpd;
}
ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
if (ret) {
dev_err_probe(dev, ret, "failed to add power domain provider\n");
goto cleanup_pds;
}
bc->power_nb.notifier_call = bc_data->power_notifier_fn;
ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
if (ret) {
dev_err_probe(dev, ret, "failed to add power notifier\n");
goto cleanup_provider;
}
dev_set_drvdata(dev, bc);
return 0;
cleanup_provider:
of_genpd_del_provider(dev->of_node);
cleanup_pds:
for (i--; i >= 0; i--) {
pm_genpd_remove(&bc->domains[i].genpd);
dev_pm_domain_detach(bc->domains[i].power_dev, true);
}
dev_pm_domain_detach(bc->bus_power_dev, true);
return ret;
}
static int imx8m_blk_ctrl_remove(struct platform_device *pdev)
{
struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
int i;
of_genpd_del_provider(pdev->dev.of_node);
for (i = 0; bc->onecell_data.num_domains; i++) {
struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
pm_genpd_remove(&domain->genpd);
dev_pm_domain_detach(domain->power_dev, true);
}
dev_pm_genpd_remove_notifier(bc->bus_power_dev);
dev_pm_domain_detach(bc->bus_power_dev, true);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int imx8m_blk_ctrl_suspend(struct device *dev)
{
struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
int ret, i;
/*
* This may look strange, but is done so the generic PM_SLEEP code
* can power down our domains and more importantly power them up again
* after resume, without tripping over our usage of runtime PM to
* control the upstream GPC domains. Things happen in the right order
* in the system suspend/resume paths due to the device parent/child
* hierarchy.
*/
ret = pm_runtime_get_sync(bc->bus_power_dev);
if (ret < 0) {
pm_runtime_put_noidle(bc->bus_power_dev);
return ret;
}
for (i = 0; i < bc->onecell_data.num_domains; i++) {
struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
ret = pm_runtime_get_sync(domain->power_dev);
if (ret < 0) {
pm_runtime_put_noidle(domain->power_dev);
goto out_fail;
}
}
return 0;
out_fail:
for (i--; i >= 0; i--)
pm_runtime_put(bc->domains[i].power_dev);
pm_runtime_put(bc->bus_power_dev);
return ret;
}
static int imx8m_blk_ctrl_resume(struct device *dev)
{
struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
int i;
for (i = 0; i < bc->onecell_data.num_domains; i++)
pm_runtime_put(bc->domains[i].power_dev);
pm_runtime_put(bc->bus_power_dev);
return 0;
}
#endif
static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume)
};
static int imx8mm_vpu_power_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
power_nb);
if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
return NOTIFY_OK;
/*
* The ADB in the VPUMIX domain has no separate reset and clock
* enable bits, but is ungated together with the VPU clocks. To
* allow the handshake with the GPC to progress we put the VPUs
* in reset and ungate the clocks.
*/
regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2));
regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2));
if (action == GENPD_NOTIFY_ON) {
/*
* On power up we have no software backchannel to the GPC to
* wait for the ADB handshake to happen, so we just delay for a
* bit. On power down the GPC driver waits for the handshake.
*/
udelay(5);
/* set "fuse" bits to enable the VPUs */
regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
}
return NOTIFY_OK;
}
static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = {
[IMX8MM_VPUBLK_PD_G1] = {
.name = "vpublk-g1",
.clk_names = (const char *[]){ "g1", },
.num_clks = 1,
.gpc_name = "g1",
.rst_mask = BIT(1),
.clk_mask = BIT(1),
},
[IMX8MM_VPUBLK_PD_G2] = {
.name = "vpublk-g2",
.clk_names = (const char *[]){ "g2", },
.num_clks = 1,
.gpc_name = "g2",
.rst_mask = BIT(0),
.clk_mask = BIT(0),
},
[IMX8MM_VPUBLK_PD_H1] = {
.name = "vpublk-h1",
.clk_names = (const char *[]){ "h1", },
.num_clks = 1,
.gpc_name = "h1",
.rst_mask = BIT(2),
.clk_mask = BIT(2),
},
};
static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = {
.max_reg = 0x18,
.power_notifier_fn = imx8mm_vpu_power_notifier,
.domains = imx8mm_vpu_blk_ctl_domain_data,
.num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data),
};
static int imx8mm_disp_power_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
power_nb);
if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
return NOTIFY_OK;
/* Enable bus clock and deassert bus reset */
regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12));
regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6));
/*
* On power up we have no software backchannel to the GPC to
* wait for the ADB handshake to happen, so we just delay for a
* bit. On power down the GPC driver waits for the handshake.
*/
if (action == GENPD_NOTIFY_ON)
udelay(5);
return NOTIFY_OK;
}
static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = {
[IMX8MM_DISPBLK_PD_CSI_BRIDGE] = {
.name = "dispblk-csi-bridge",
.clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb",
"csi-bridge-core", },
.num_clks = 3,
.gpc_name = "csi-bridge",
.rst_mask = BIT(0) | BIT(1) | BIT(2),
.clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
},
[IMX8MM_DISPBLK_PD_LCDIF] = {
.name = "dispblk-lcdif",
.clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
.num_clks = 3,
.gpc_name = "lcdif",
.clk_mask = BIT(6) | BIT(7),
},
[IMX8MM_DISPBLK_PD_MIPI_DSI] = {
.name = "dispblk-mipi-dsi",
.clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
.num_clks = 2,
.gpc_name = "mipi-dsi",
.rst_mask = BIT(5),
.clk_mask = BIT(8) | BIT(9),
},
[IMX8MM_DISPBLK_PD_MIPI_CSI] = {
.name = "dispblk-mipi-csi",
.clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
.num_clks = 2,
.gpc_name = "mipi-csi",
.rst_mask = BIT(3) | BIT(4),
.clk_mask = BIT(10) | BIT(11),
},
};
static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = {
.max_reg = 0x2c,
.power_notifier_fn = imx8mm_disp_power_notifier,
.domains = imx8mm_disp_blk_ctl_domain_data,
.num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data),
};
static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
{
.compatible = "fsl,imx8mm-vpu-blk-ctrl",
.data = &imx8mm_vpu_blk_ctl_dev_data
}, {
.compatible = "fsl,imx8mm-disp-blk-ctrl",
.data = &imx8mm_disp_blk_ctl_dev_data
} ,{
/* Sentinel */
}
};
MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match);
static struct platform_driver imx8m_blk_ctrl_driver = {
.probe = imx8m_blk_ctrl_probe,
.remove = imx8m_blk_ctrl_remove,
.driver = {
.name = "imx8m-blk-ctrl",
.pm = &imx8m_blk_ctrl_pm_ops,
.of_match_table = imx8m_blk_ctrl_of_match,
},
};
module_platform_driver(imx8m_blk_ctrl_driver);

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_MEDIATEK_MT8192_MMSYS_H
#define __SOC_MEDIATEK_MT8192_MMSYS_H
#define MT8192_MMSYS_OVL_MOUT_EN 0xf04
#define MT8192_DISP_OVL1_2L_MOUT_EN 0xf08
#define MT8192_DISP_OVL0_2L_MOUT_EN 0xf18
#define MT8192_DISP_OVL0_MOUT_EN 0xf1c
#define MT8192_DISP_RDMA0_SEL_IN 0xf2c
#define MT8192_DISP_RDMA0_SOUT_SEL 0xf30
#define MT8192_DISP_CCORR0_SOUT_SEL 0xf34
#define MT8192_DISP_AAL0_SEL_IN 0xf38
#define MT8192_DISP_DITHER0_MOUT_EN 0xf3c
#define MT8192_DISP_DSI0_SEL_IN 0xf40
#define MT8192_DISP_OVL2_2L_MOUT_EN 0xf4c
#define MT8192_DISP_OVL0_GO_BLEND BIT(0)
#define MT8192_DITHER0_MOUT_IN_DSI0 BIT(0)
#define MT8192_OVL0_MOUT_EN_DISP_RDMA0 BIT(0)
#define MT8192_OVL2_2L_MOUT_EN_RDMA4 BIT(0)
#define MT8192_DISP_OVL0_GO_BG BIT(1)
#define MT8192_DISP_OVL0_2L_GO_BLEND BIT(2)
#define MT8192_DISP_OVL0_2L_GO_BG BIT(3)
#define MT8192_OVL1_2L_MOUT_EN_RDMA1 BIT(4)
#define MT8192_OVL0_MOUT_EN_OVL0_2L BIT(4)
#define MT8192_RDMA0_SEL_IN_OVL0_2L 0x3
#define MT8192_RDMA0_SOUT_COLOR0 0x1
#define MT8192_CCORR0_SOUT_AAL0 0x1
#define MT8192_AAL0_SEL_IN_CCORR0 0x1
#define MT8192_DSI0_SEL_IN_DITHER0 0x1
static const struct mtk_mmsys_routes mmsys_mt8192_routing_table[] = {
{
DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
MT8192_DISP_OVL0_2L_MOUT_EN, MT8192_OVL0_MOUT_EN_DISP_RDMA0,
MT8192_OVL0_MOUT_EN_DISP_RDMA0
}, {
DDP_COMPONENT_OVL_2L2, DDP_COMPONENT_RDMA4,
MT8192_DISP_OVL2_2L_MOUT_EN, MT8192_OVL2_2L_MOUT_EN_RDMA4,
MT8192_OVL2_2L_MOUT_EN_RDMA4
}, {
DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
MT8192_DISP_DITHER0_MOUT_EN, MT8192_DITHER0_MOUT_IN_DSI0,
MT8192_DITHER0_MOUT_IN_DSI0
}, {
DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
MT8192_DISP_RDMA0_SEL_IN, MT8192_RDMA0_SEL_IN_OVL0_2L,
MT8192_RDMA0_SEL_IN_OVL0_2L
}, {
DDP_COMPONENT_CCORR, DDP_COMPONENT_AAL0,
MT8192_DISP_AAL0_SEL_IN, MT8192_AAL0_SEL_IN_CCORR0,
MT8192_AAL0_SEL_IN_CCORR0
}, {
DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
MT8192_DISP_DSI0_SEL_IN, MT8192_DSI0_SEL_IN_DITHER0
}, {
DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
MT8192_DISP_RDMA0_SOUT_SEL, MT8192_RDMA0_SOUT_COLOR0,
MT8192_RDMA0_SOUT_COLOR0
}, {
DDP_COMPONENT_CCORR, DDP_COMPONENT_AAL0,
MT8192_DISP_CCORR0_SOUT_SEL, MT8192_CCORR0_SOUT_AAL0,
MT8192_CCORR0_SOUT_AAL0
}, {
DDP_COMPONENT_OVL0, DDP_COMPONENT_OVL_2L0,
MT8192_MMSYS_OVL_MOUT_EN, MT8192_DISP_OVL0_GO_BG,
MT8192_DISP_OVL0_GO_BG
}, {
DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
MT8192_MMSYS_OVL_MOUT_EN, MT8192_DISP_OVL0_2L_GO_BLEND,
MT8192_DISP_OVL0_2L_GO_BLEND
}
};
#endif /* __SOC_MEDIATEK_MT8192_MMSYS_H */

View File

@ -4,15 +4,18 @@
* Author: James Liao <jamesjj.liao@mediatek.com>
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include "mtk-mmsys.h"
#include "mt8167-mmsys.h"
#include "mt8183-mmsys.h"
#include "mt8192-mmsys.h"
#include "mt8365-mmsys.h"
static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
@ -53,6 +56,12 @@ static const struct mtk_mmsys_driver_data mt8183_mmsys_driver_data = {
.num_routes = ARRAY_SIZE(mmsys_mt8183_routing_table),
};
static const struct mtk_mmsys_driver_data mt8192_mmsys_driver_data = {
.clk_driver = "clk-mt8192-mm",
.routes = mmsys_mt8192_routing_table,
.num_routes = ARRAY_SIZE(mmsys_mt8192_routing_table),
};
static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = {
.clk_driver = "clk-mt8365-mm",
.routes = mt8365_mmsys_routing_table,
@ -62,6 +71,8 @@ static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = {
struct mtk_mmsys {
void __iomem *regs;
const struct mtk_mmsys_driver_data *data;
spinlock_t lock; /* protects mmsys_sw_rst_b reg */
struct reset_controller_dev rcdev;
};
void mtk_mmsys_ddp_connect(struct device *dev,
@ -101,6 +112,58 @@ void mtk_mmsys_ddp_disconnect(struct device *dev,
}
EXPORT_SYMBOL_GPL(mtk_mmsys_ddp_disconnect);
static int mtk_mmsys_reset_update(struct reset_controller_dev *rcdev, unsigned long id,
bool assert)
{
struct mtk_mmsys *mmsys = container_of(rcdev, struct mtk_mmsys, rcdev);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&mmsys->lock, flags);
reg = readl_relaxed(mmsys->regs + MMSYS_SW0_RST_B);
if (assert)
reg &= ~BIT(id);
else
reg |= BIT(id);
writel_relaxed(reg, mmsys->regs + MMSYS_SW0_RST_B);
spin_unlock_irqrestore(&mmsys->lock, flags);
return 0;
}
static int mtk_mmsys_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
return mtk_mmsys_reset_update(rcdev, id, true);
}
static int mtk_mmsys_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
return mtk_mmsys_reset_update(rcdev, id, false);
}
static int mtk_mmsys_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
int ret;
ret = mtk_mmsys_reset_assert(rcdev, id);
if (ret)
return ret;
usleep_range(1000, 1100);
return mtk_mmsys_reset_deassert(rcdev, id);
}
static const struct reset_control_ops mtk_mmsys_reset_ops = {
.assert = mtk_mmsys_reset_assert,
.deassert = mtk_mmsys_reset_deassert,
.reset = mtk_mmsys_reset,
};
static int mtk_mmsys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -120,6 +183,18 @@ static int mtk_mmsys_probe(struct platform_device *pdev)
return ret;
}
spin_lock_init(&mmsys->lock);
mmsys->rcdev.owner = THIS_MODULE;
mmsys->rcdev.nr_resets = 32;
mmsys->rcdev.ops = &mtk_mmsys_reset_ops;
mmsys->rcdev.of_node = pdev->dev.of_node;
ret = devm_reset_controller_register(&pdev->dev, &mmsys->rcdev);
if (ret) {
dev_err(&pdev->dev, "Couldn't register mmsys reset controller: %d\n", ret);
return ret;
}
mmsys->data = of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, mmsys);
@ -167,6 +242,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = {
.compatible = "mediatek,mt8183-mmsys",
.data = &mt8183_mmsys_driver_data,
},
{
.compatible = "mediatek,mt8192-mmsys",
.data = &mt8192_mmsys_driver_data,
},
{
.compatible = "mediatek,mt8365-mmsys",
.data = &mt8365_mmsys_driver_data,

View File

@ -78,6 +78,8 @@
#define DSI_SEL_IN_RDMA 0x1
#define DSI_SEL_IN_MASK 0x1
#define MMSYS_SW0_RST_B 0x140
struct mtk_mmsys_routes {
u32 from_comp;
u32 to_comp;

View File

@ -39,6 +39,18 @@
#define MT8167_MUTEX_MOD_DISP_DITHER 15
#define MT8167_MUTEX_MOD_DISP_UFOE 16
#define MT8192_MUTEX_MOD_DISP_OVL0 0
#define MT8192_MUTEX_MOD_DISP_OVL0_2L 1
#define MT8192_MUTEX_MOD_DISP_RDMA0 2
#define MT8192_MUTEX_MOD_DISP_COLOR0 4
#define MT8192_MUTEX_MOD_DISP_CCORR0 5
#define MT8192_MUTEX_MOD_DISP_AAL0 6
#define MT8192_MUTEX_MOD_DISP_GAMMA0 7
#define MT8192_MUTEX_MOD_DISP_POSTMASK0 8
#define MT8192_MUTEX_MOD_DISP_DITHER0 9
#define MT8192_MUTEX_MOD_DISP_OVL2_2L 16
#define MT8192_MUTEX_MOD_DISP_RDMA4 17
#define MT8183_MUTEX_MOD_DISP_RDMA0 0
#define MT8183_MUTEX_MOD_DISP_RDMA1 1
#define MT8183_MUTEX_MOD_DISP_OVL0 9
@ -214,6 +226,20 @@ static const unsigned int mt8183_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_WDMA0] = MT8183_MUTEX_MOD_DISP_WDMA0,
};
static const unsigned int mt8192_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_AAL0] = MT8192_MUTEX_MOD_DISP_AAL0,
[DDP_COMPONENT_CCORR] = MT8192_MUTEX_MOD_DISP_CCORR0,
[DDP_COMPONENT_COLOR0] = MT8192_MUTEX_MOD_DISP_COLOR0,
[DDP_COMPONENT_DITHER] = MT8192_MUTEX_MOD_DISP_DITHER0,
[DDP_COMPONENT_GAMMA] = MT8192_MUTEX_MOD_DISP_GAMMA0,
[DDP_COMPONENT_POSTMASK0] = MT8192_MUTEX_MOD_DISP_POSTMASK0,
[DDP_COMPONENT_OVL0] = MT8192_MUTEX_MOD_DISP_OVL0,
[DDP_COMPONENT_OVL_2L0] = MT8192_MUTEX_MOD_DISP_OVL0_2L,
[DDP_COMPONENT_OVL_2L2] = MT8192_MUTEX_MOD_DISP_OVL2_2L,
[DDP_COMPONENT_RDMA0] = MT8192_MUTEX_MOD_DISP_RDMA0,
[DDP_COMPONENT_RDMA4] = MT8192_MUTEX_MOD_DISP_RDMA4,
};
static const unsigned int mt2712_mutex_sof[MUTEX_SOF_DSI3 + 1] = {
[MUTEX_SOF_SINGLE_MODE] = MUTEX_SOF_SINGLE_MODE,
[MUTEX_SOF_DSI0] = MUTEX_SOF_DSI0,
@ -275,6 +301,13 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = {
.no_clk = true,
};
static const struct mtk_mutex_data mt8192_mutex_driver_data = {
.mutex_mod = mt8192_mutex_mod,
.mutex_sof = mt8183_mutex_sof,
.mutex_mod_reg = MT8183_MUTEX0_MOD0,
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
};
struct mtk_mutex *mtk_mutex_get(struct device *dev)
{
struct mtk_mutex_ctx *mtx = dev_get_drvdata(dev);
@ -507,6 +540,8 @@ static const struct of_device_id mutex_driver_dt_match[] = {
.data = &mt8173_mutex_driver_data},
{ .compatible = "mediatek,mt8183-disp-mutex",
.data = &mt8183_mutex_driver_data},
{ .compatible = "mediatek,mt8192-disp-mutex",
.data = &mt8192_mutex_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mutex_driver_dt_match);

View File

@ -190,6 +190,25 @@ config QCOM_SOCINFO
Say yes here to support the Qualcomm socinfo driver, providing
information about the SoC to user space.
config QCOM_SPM
tristate "Qualcomm Subsystem Power Manager (SPM)"
depends on ARCH_QCOM || COMPILE_TEST
select QCOM_SCM
help
Enable the support for the Qualcomm Subsystem Power Manager, used
to manage cores, L2 low power modes and to configure the internal
Adaptive Voltage Scaler parameters, where supported.
config QCOM_STATS
tristate "Qualcomm Technologies, Inc. (QTI) Sleep stats driver"
depends on (ARCH_QCOM && DEBUG_FS) || COMPILE_TEST
depends on QCOM_SMEM
help
Qualcomm Technologies, Inc. (QTI) Sleep stats driver to read
the shared memory exported by the remote processor related to
various SoC level low power modes statistics and export to debugfs
interface.
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on ARCH_QCOM || COMPILE_TEST

View File

@ -20,6 +20,8 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
obj-$(CONFIG_QCOM_SPM) += spm.o
obj-$(CONFIG_QCOM_STATS) += qcom_stats.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
obj-$(CONFIG_QCOM_APR) += apr.o
obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o

View File

@ -492,12 +492,14 @@ static int of_apr_add_pd_lookups(struct device *dev)
1, &service_path);
if (ret < 0) {
dev_err(dev, "pdr service path missing: %d\n", ret);
of_node_put(node);
return ret;
}
pds = pdr_add_lookup(apr->pdr, service_name, service_path);
if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) {
dev_err(dev, "pdr add lookup failed: %ld\n", PTR_ERR(pds));
of_node_put(node);
return PTR_ERR(pds);
}
}

View File

@ -1614,7 +1614,6 @@ static void cpr_debugfs_init(struct cpr_drv *drv)
static int cpr_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
struct cpr_drv *drv;
int irq, ret;
@ -1648,8 +1647,7 @@ static int cpr_probe(struct platform_device *pdev)
if (IS_ERR(drv->tcsr))
return PTR_ERR(drv->tcsr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->base = devm_ioremap_resource(dev, res);
drv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(drv->base))
return PTR_ERR(drv->base);

View File

@ -115,7 +115,7 @@ static const struct llcc_slice_config sc7280_data[] = {
{ LLCC_CMPT, 10, 768, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_GPUHTW, 11, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_GPU, 12, 512, 1, 0, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 1, 0},
{ LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 0, 1, 0},
{ LLCC_MDMPNG, 21, 768, 0, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_WLHW, 24, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_MODPE, 29, 64, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
@ -142,6 +142,16 @@ static const struct llcc_slice_config sdm845_data[] = {
{ LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 },
};
static const struct llcc_slice_config sm6350_data[] = {
{ LLCC_CPUSS, 1, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 1 },
{ LLCC_MDM, 8, 512, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_GPUHTW, 11, 256, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_GPU, 12, 512, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MDMPNG, 21, 768, 0, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_NPU, 23, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MODPE, 29, 64, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
};
static const struct llcc_slice_config sm8150_data[] = {
{ LLCC_CPUSS, 1, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 1 },
{ LLCC_VIDSC0, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
@ -203,6 +213,11 @@ static const struct qcom_llcc_config sdm845_cfg = {
.need_llcc_cfg = false,
};
static const struct qcom_llcc_config sm6350_cfg = {
.sct_data = sm6350_data,
.size = ARRAY_SIZE(sm6350_data),
};
static const struct qcom_llcc_config sm8150_cfg = {
.sct_data = sm8150_data,
.size = ARRAY_SIZE(sm8150_data),
@ -626,6 +641,7 @@ static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg },
{ .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg },
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg },
{ .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg },
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg },
{ .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg },
{ }

View File

@ -300,7 +300,6 @@ static int ocmem_dev_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
unsigned long reg, region_size;
int i, j, ret, num_banks;
struct resource *res;
struct ocmem *ocmem;
if (!qcom_scm_is_available())
@ -321,8 +320,7 @@ static int ocmem_dev_probe(struct platform_device *pdev)
return ret;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
ocmem->mmio = devm_ioremap_resource(&pdev->dev, res);
ocmem->mmio = devm_platform_ioremap_resource_byname(pdev, "ctrl");
if (IS_ERR(ocmem->mmio)) {
dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
return PTR_ERR(ocmem->mmio);

View File

@ -131,7 +131,7 @@ static int pdr_register_listener(struct pdr_handle *pdr,
return ret;
req.enable = enable;
strcpy(req.service_path, pds->service_path);
strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
&txn, SERVREG_REGISTER_LISTENER_REQ,
@ -257,7 +257,7 @@ static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
return ret;
req.transaction_id = tid;
strcpy(req.service_path, pds->service_path);
strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
&txn, SERVREG_SET_ACK_REQ,
@ -406,7 +406,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
return -ENOMEM;
/* Prepare req message */
strcpy(req.service_name, pds->service_name);
strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
req.domain_offset_valid = true;
req.domain_offset = 0;
@ -531,8 +531,8 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
return ERR_PTR(-ENOMEM);
pds->service = SERVREG_NOTIFIER_SERVICE;
strcpy(pds->service_name, service_name);
strcpy(pds->service_path, service_path);
strscpy(pds->service_name, service_name, sizeof(pds->service_name));
strscpy(pds->service_path, service_path, sizeof(pds->service_path));
pds->need_locator_lookup = true;
mutex_lock(&pdr->list_lock);
@ -587,7 +587,7 @@ int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
break;
/* Prepare req message */
strcpy(req.service_path, pds->service_path);
strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
addr = pds->addr;
break;
}

View File

@ -871,7 +871,6 @@ EXPORT_SYMBOL(geni_icc_disable);
static int geni_se_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct geni_wrapper *wrapper;
int ret;
@ -880,8 +879,7 @@ static int geni_se_probe(struct platform_device *pdev)
return -ENOMEM;
wrapper->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
wrapper->base = devm_ioremap_resource(dev, res);
wrapper->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wrapper->base))
return PTR_ERR(wrapper->base);

View File

@ -2,16 +2,16 @@
/*
* Copyright (c) 2019, Linaro Ltd
*/
#include <dt-bindings/power/qcom-aoss-qmp.h>
#include <linux/clk-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/thermal.h>
#include <linux/slab.h>
#include <linux/soc/qcom/qcom_aoss.h>
#define QMP_DESC_MAGIC 0x0
#define QMP_DESC_VERSION 0x4
@ -64,7 +64,6 @@ struct qmp_cooling_device {
* @event: wait_queue for synchronization with the IRQ
* @tx_lock: provides synchronization between multiple callers of qmp_send()
* @qdss_clk: QDSS clock hw struct
* @pd_data: genpd data
* @cooling_devs: thermal cooling devices
*/
struct qmp {
@ -82,17 +81,9 @@ struct qmp {
struct mutex tx_lock;
struct clk_hw qdss_clk;
struct genpd_onecell_data pd_data;
struct qmp_cooling_device *cooling_devs;
};
struct qmp_pd {
struct qmp *qmp;
struct generic_pm_domain pd;
};
#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
static void qmp_kick(struct qmp *qmp)
{
mbox_send_message(qmp->mbox_chan, NULL);
@ -223,11 +214,14 @@ static bool qmp_message_empty(struct qmp *qmp)
*
* Return: 0 on success, negative errno on failure
*/
static int qmp_send(struct qmp *qmp, const void *data, size_t len)
int qmp_send(struct qmp *qmp, const void *data, size_t len)
{
long time_left;
int ret;
if (WARN_ON(IS_ERR_OR_NULL(qmp) || !data))
return -EINVAL;
if (WARN_ON(len + sizeof(u32) > qmp->size))
return -EINVAL;
@ -261,6 +255,7 @@ static int qmp_send(struct qmp *qmp, const void *data, size_t len)
return ret;
}
EXPORT_SYMBOL(qmp_send);
static int qmp_qdss_clk_prepare(struct clk_hw *hw)
{
@ -314,95 +309,6 @@ static void qmp_qdss_clk_remove(struct qmp *qmp)
clk_hw_unregister(&qmp->qdss_clk);
}
static int qmp_pd_power_toggle(struct qmp_pd *res, bool enable)
{
char buf[QMP_MSG_LEN] = {};
snprintf(buf, sizeof(buf),
"{class: image, res: load_state, name: %s, val: %s}",
res->pd.name, enable ? "on" : "off");
return qmp_send(res->qmp, buf, sizeof(buf));
}
static int qmp_pd_power_on(struct generic_pm_domain *domain)
{
return qmp_pd_power_toggle(to_qmp_pd_resource(domain), true);
}
static int qmp_pd_power_off(struct generic_pm_domain *domain)
{
return qmp_pd_power_toggle(to_qmp_pd_resource(domain), false);
}
static const char * const sdm845_resources[] = {
[AOSS_QMP_LS_CDSP] = "cdsp",
[AOSS_QMP_LS_LPASS] = "adsp",
[AOSS_QMP_LS_MODEM] = "modem",
[AOSS_QMP_LS_SLPI] = "slpi",
[AOSS_QMP_LS_SPSS] = "spss",
[AOSS_QMP_LS_VENUS] = "venus",
};
static int qmp_pd_add(struct qmp *qmp)
{
struct genpd_onecell_data *data = &qmp->pd_data;
struct device *dev = qmp->dev;
struct qmp_pd *res;
size_t num = ARRAY_SIZE(sdm845_resources);
int ret;
int i;
res = devm_kcalloc(dev, num, sizeof(*res), GFP_KERNEL);
if (!res)
return -ENOMEM;
data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
GFP_KERNEL);
if (!data->domains)
return -ENOMEM;
for (i = 0; i < num; i++) {
res[i].qmp = qmp;
res[i].pd.name = sdm845_resources[i];
res[i].pd.power_on = qmp_pd_power_on;
res[i].pd.power_off = qmp_pd_power_off;
ret = pm_genpd_init(&res[i].pd, NULL, true);
if (ret < 0) {
dev_err(dev, "failed to init genpd\n");
goto unroll_genpds;
}
data->domains[i] = &res[i].pd;
}
data->num_domains = i;
ret = of_genpd_add_provider_onecell(dev->of_node, data);
if (ret < 0)
goto unroll_genpds;
return 0;
unroll_genpds:
for (i--; i >= 0; i--)
pm_genpd_remove(data->domains[i]);
return ret;
}
static void qmp_pd_remove(struct qmp *qmp)
{
struct genpd_onecell_data *data = &qmp->pd_data;
struct device *dev = qmp->dev;
int i;
of_genpd_del_provider(dev->of_node);
for (i = 0; i < data->num_domains; i++)
pm_genpd_remove(data->domains[i]);
}
static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
@ -519,9 +425,53 @@ static void qmp_cooling_devices_remove(struct qmp *qmp)
thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev);
}
/**
* qmp_get() - get a qmp handle from a device
* @dev: client device pointer
*
* Return: handle to qmp device on success, ERR_PTR() on failure
*/
struct qmp *qmp_get(struct device *dev)
{
struct platform_device *pdev;
struct device_node *np;
struct qmp *qmp;
if (!dev || !dev->of_node)
return ERR_PTR(-EINVAL);
np = of_parse_phandle(dev->of_node, "qcom,qmp", 0);
if (!np)
return ERR_PTR(-ENODEV);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-EINVAL);
qmp = platform_get_drvdata(pdev);
return qmp ? qmp : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(qmp_get);
/**
* qmp_put() - release a qmp handle
* @qmp: qmp handle obtained from qmp_get()
*/
void qmp_put(struct qmp *qmp)
{
/*
* Match get_device() inside of_find_device_by_node() in
* qmp_get()
*/
if (!IS_ERR_OR_NULL(qmp))
put_device(qmp->dev);
}
EXPORT_SYMBOL(qmp_put);
static int qmp_probe(struct platform_device *pdev)
{
struct resource *res;
struct qmp *qmp;
int irq;
int ret;
@ -534,8 +484,7 @@ static int qmp_probe(struct platform_device *pdev)
init_waitqueue_head(&qmp->event);
mutex_init(&qmp->tx_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
qmp->msgram = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qmp->msgram))
return PTR_ERR(qmp->msgram);
@ -563,10 +512,6 @@ static int qmp_probe(struct platform_device *pdev)
if (ret)
goto err_close_qmp;
ret = qmp_pd_add(qmp);
if (ret)
goto err_remove_qdss_clk;
ret = qmp_cooling_devices_register(qmp);
if (ret)
dev_err(&pdev->dev, "failed to register aoss cooling devices\n");
@ -575,8 +520,6 @@ static int qmp_probe(struct platform_device *pdev)
return 0;
err_remove_qdss_clk:
qmp_qdss_clk_remove(qmp);
err_close_qmp:
qmp_close(qmp);
err_free_mbox:
@ -590,7 +533,6 @@ static int qmp_remove(struct platform_device *pdev)
struct qmp *qmp = platform_get_drvdata(pdev);
qmp_qdss_clk_remove(qmp);
qmp_pd_remove(qmp);
qmp_cooling_devices_remove(qmp);
qmp_close(qmp);
@ -615,6 +557,7 @@ static struct platform_driver qmp_driver = {
.driver = {
.name = "qcom_aoss_qmp",
.of_match_table = qmp_dt_match,
.suppress_bind_attrs = true,
},
.probe = qmp_probe,
.remove = qmp_remove,

View File

@ -127,7 +127,6 @@ static int gsbi_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct device_node *tcsr_node;
const struct of_device_id *match;
struct resource *res;
void __iomem *base;
struct gsbi_info *gsbi;
int i, ret;
@ -139,8 +138,7 @@ static int gsbi_probe(struct platform_device *pdev)
if (!gsbi)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);

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