Commit eab35405 authored by Linus Torvalds's avatar Linus Torvalds

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

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

   - Nvidia: Fuse support for Tegra194, continued memory controller
     pieces for Tegra30

   - NXP/FSL: Refactorings of QuickEngine drivers to support
     ARM/ARM64/PPC

   - NXP/FSL: i.MX8MP SoC driver pieces

   - TI Keystone: ring accelerator driver

   - Qualcomm: SCM driver cleanup/refactoring + support for new SoCs.

   - Xilinx ZynqMP: feature checking interface for firmware. Mailbox
     communication for power management

   - Overall support patch set for cpuidle on more complex hierarchies
     (PSCI-based)

  and misc cleanups, refactorings of Marvell, TI, other platforms"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (166 commits)
  drivers: soc: xilinx: Use mailbox IPI callback
  dt-bindings: power: reset: xilinx: Add bindings for ipi mailbox
  drivers: soc: ti: knav_qmss_queue: Pass lockdep expression to RCU lists
  MAINTAINERS: Add brcmstb PCIe controller entry
  soc/tegra: fuse: Unmap registers once they are not needed anymore
  soc/tegra: fuse: Correct straps' address for older Tegra124 device trees
  soc/tegra: fuse: Warn if straps are not ready
  soc/tegra: fuse: Cache values of straps and Chip ID registers
  memory: tegra30-emc: Correct error message for timed out auto calibration
  memory: tegra30-emc: Firm up hardware programming sequence
  memory: tegra30-emc: Firm up suspend/resume sequence
  soc/tegra: regulators: Do nothing if voltage is unchanged
  memory: tegra: Correct reset value of xusb_hostr
  soc/tegra: fuse: Add APB DMA dependency for Tegra20
  bus: tegra-aconnect: Remove PM_CLK dependency
  dt-bindings: mediatek: add MT6765 power dt-bindings
  soc: mediatek: cmdq: delete not used define
  memory: tegra: Add support for the Tegra194 memory controller
  memory: tegra: Only include support for enabled SoCs
  memory: tegra: Support DVFS on Tegra186 and later
  ...
parents 1afa9c3b 88b47501
......@@ -242,6 +242,21 @@ properties:
where voltage is in V, frequency is in MHz.
power-domains:
$ref: '/schemas/types.yaml#/definitions/phandle-array'
description:
List of phandles and PM domain specifiers, as defined by bindings of the
PM domain provider (see also ../power_domain.txt).
power-domain-names:
$ref: '/schemas/types.yaml#/definitions/string-array'
description:
A list of power domain name strings sorted in the same order as the
power-domains property.
For PSCI based platforms, the name corresponding to the index of the PSCI
PM domain provider, must be "psci".
qcom,saw:
$ref: '/schemas/types.yaml#/definitions/phandle'
description: |
......
......@@ -47,7 +47,7 @@ examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
cache-controller@1100000 {
system-cache-controller@1100000 {
compatible = "qcom,sdm845-llcc";
reg = <0x1100000 0x200000>, <0x1300000 0x50000> ;
reg-names = "llcc_base", "llcc_broadcast_base";
......
......@@ -102,6 +102,34 @@ properties:
[1] Kernel documentation - ARM idle states bindings
Documentation/devicetree/bindings/arm/idle-states.txt
"#power-domain-cells":
description:
The number of cells in a PM domain specifier as per binding in [3].
Must be 0 as to represent a single PM domain.
ARM systems can have multiple cores, sometimes in an hierarchical
arrangement. This often, but not always, maps directly to the processor
power topology of the system. Individual nodes in a topology have their
own specific power states and can be better represented hierarchically.
For these cases, the definitions of the idle states for the CPUs and the
CPU topology, must conform to the binding in [3]. The idle states
themselves must conform to the binding in [4] and must specify the
arm,psci-suspend-param property.
It should also be noted that, in PSCI firmware v1.0 the OS-Initiated
(OSI) CPU suspend mode is introduced. Using a hierarchical representation
helps to implement support for OSI mode and OS implementations may choose
to mandate it.
[3] Documentation/devicetree/bindings/power/power_domain.txt
[4] Documentation/devicetree/bindings/power/domain-idle-state.txt
power-domains:
$ref: '/schemas/types.yaml#/definitions/phandle-array'
description:
List of phandles and PM domain specifiers, as defined by bindings of the
PM domain provider.
required:
- compatible
......@@ -160,4 +188,80 @@ examples:
cpu_on = <0x95c10002>;
cpu_off = <0x95c10001>;
};
- |+
// Case 4: CPUs and CPU idle states described using the hierarchical model.
cpus {
#size-cells = <0>;
#address-cells = <1>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a53", "arm,armv8";
reg = <0x0>;
enable-method = "psci";
power-domains = <&CPU_PD0>;
power-domain-names = "psci";
};
CPU1: cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a57", "arm,armv8";
reg = <0x100>;
enable-method = "psci";
power-domains = <&CPU_PD1>;
power-domain-names = "psci";
};
idle-states {
CPU_PWRDN: cpu-power-down {
compatible = "arm,idle-state";
arm,psci-suspend-param = <0x0000001>;
entry-latency-us = <10>;
exit-latency-us = <10>;
min-residency-us = <100>;
};
CLUSTER_RET: cluster-retention {
compatible = "domain-idle-state";
arm,psci-suspend-param = <0x1000011>;
entry-latency-us = <500>;
exit-latency-us = <500>;
min-residency-us = <2000>;
};
CLUSTER_PWRDN: cluster-power-down {
compatible = "domain-idle-state";
arm,psci-suspend-param = <0x1000031>;
entry-latency-us = <2000>;
exit-latency-us = <2000>;
min-residency-us = <6000>;
};
};
};
psci {
compatible = "arm,psci-1.0";
method = "smc";
CPU_PD0: cpu-pd0 {
#power-domain-cells = <0>;
domain-idle-states = <&CPU_PWRDN>;
power-domains = <&CLUSTER_PD>;
};
CPU_PD1: cpu-pd1 {
#power-domain-cells = <0>;
domain-idle-states = <&CPU_PWRDN>;
power-domains = <&CLUSTER_PD>;
};
CLUSTER_PD: cluster-pd {
#power-domain-cells = <0>;
domain-idle-states = <&CLUSTER_RET>, <&CLUSTER_PWRDN>;
};
};
...
Qualcomm RPM/RPMh Power domains
For RPM/RPMh Power domains, we communicate a performance state to RPM/RPMh
which then translates it into a corresponding voltage on a rail
Required Properties:
- compatible: Should be one of the following
* qcom,msm8976-rpmpd: RPM Power domain for the msm8976 family of SoC
* qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC
* qcom,msm8998-rpmpd: RPM Power domain for the msm8998 family of SoC
* qcom,qcs404-rpmpd: RPM Power domain for the qcs404 family of SoC
* qcom,sdm845-rpmhpd: RPMh Power domain for the sdm845 family of SoC
- #power-domain-cells: number of cells in Power domain specifier
must be 1.
- operating-points-v2: Phandle to the OPP table for the Power domain.
Refer to Documentation/devicetree/bindings/power/power_domain.txt
and Documentation/devicetree/bindings/opp/opp.txt for more details
Refer to <dt-bindings/power/qcom-rpmpd.h> for the level values for
various OPPs for different platforms as well as Power domain indexes
Example: rpmh power domain controller and OPP table
#include <dt-bindings/power/qcom-rpmhpd.h>
opp-level values specified in the OPP tables for RPMh power domains
should use the RPMH_REGULATOR_LEVEL_* constants from
<dt-bindings/power/qcom-rpmhpd.h>
rpmhpd: power-controller {
compatible = "qcom,sdm845-rpmhpd";
#power-domain-cells = <1>;
operating-points-v2 = <&rpmhpd_opp_table>;
rpmhpd_opp_table: opp-table {
compatible = "operating-points-v2";
rpmhpd_opp_ret: opp1 {
opp-level = <RPMH_REGULATOR_LEVEL_RETENTION>;
};
rpmhpd_opp_min_svs: opp2 {
opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>;
};
rpmhpd_opp_low_svs: opp3 {
opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
};
rpmhpd_opp_svs: opp4 {
opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
};
rpmhpd_opp_svs_l1: opp5 {
opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
};
rpmhpd_opp_nom: opp6 {
opp-level = <RPMH_REGULATOR_LEVEL_NOM>;
};
rpmhpd_opp_nom_l1: opp7 {
opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>;
};
rpmhpd_opp_nom_l2: opp8 {
opp-level = <RPMH_REGULATOR_LEVEL_NOM_L2>;
};
rpmhpd_opp_turbo: opp9 {
opp-level = <RPMH_REGULATOR_LEVEL_TURBO>;
};
rpmhpd_opp_turbo_l1: opp10 {
opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>;
};
};
};
Example: rpm power domain controller and OPP table
rpmpd: power-controller {
compatible = "qcom,msm8996-rpmpd";
#power-domain-cells = <1>;
operating-points-v2 = <&rpmpd_opp_table>;
rpmpd_opp_table: opp-table {
compatible = "operating-points-v2";
rpmpd_opp_low: opp1 {
opp-level = <1>;
};
rpmpd_opp_ret: opp2 {
opp-level = <2>;
};
rpmpd_opp_svs: opp3 {
opp-level = <3>;
};
rpmpd_opp_normal: opp4 {
opp-level = <4>;
};
rpmpd_opp_high: opp5 {
opp-level = <5>;
};
rpmpd_opp_turbo: opp6 {
opp-level = <6>;
};
};
};
Example: Client/Consumer device using OPP table
leaky-device0@12350000 {
compatible = "foo,i-leak-current";
reg = <0x12350000 0x1000>;
power-domains = <&rpmhpd SDM845_MX>;
operating-points-v2 = <&leaky_opp_table>;
};
leaky_opp_table: opp-table {
compatible = "operating-points-v2";
opp1 {
opp-hz = /bits/ 64 <144000>;
required-opps = <&rpmhpd_opp_low>;
};
opp2 {
opp-hz = /bits/ 64 <400000>;
required-opps = <&rpmhpd_opp_ret>;
};
opp3 {
opp-hz = /bits/ 64 <20000000>;
required-opps = <&rpmpd_opp_svs>;
};
opp4 {
opp-hz = /bits/ 64 <25000000>;
required-opps = <&rpmpd_opp_normal>;
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/qcom,rpmpd.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm RPM/RPMh Power domains
maintainers:
- Rajendra Nayak <rnayak@codeaurora.org>
description:
For RPM/RPMh Power domains, we communicate a performance state to RPM/RPMh
which then translates it into a corresponding voltage on a rail.
properties:
compatible:
enum:
- qcom,msm8976-rpmpd
- qcom,msm8996-rpmpd
- qcom,msm8998-rpmpd
- qcom,qcs404-rpmpd
- qcom,sc7180-rpmhpd
- qcom,sdm845-rpmhpd
- qcom,sm8150-rpmhpd
'#power-domain-cells':
const: 1
operating-points-v2: true
opp-table:
type: object
required:
- compatible
- '#power-domain-cells'
- operating-points-v2
additionalProperties: false
examples:
- |
// Example 1 (rpmh power domain controller and OPP table):
#include <dt-bindings/power/qcom-rpmpd.h>
rpmhpd: power-controller {
compatible = "qcom,sdm845-rpmhpd";
#power-domain-cells = <1>;
operating-points-v2 = <&rpmhpd_opp_table>;
rpmhpd_opp_table: opp-table {
compatible = "operating-points-v2";
rpmhpd_opp_ret: opp1 {
opp-level = <RPMH_REGULATOR_LEVEL_RETENTION>;
};
rpmhpd_opp_min_svs: opp2 {
opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>;
};
rpmhpd_opp_low_svs: opp3 {
opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
};
rpmhpd_opp_svs: opp4 {
opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
};
rpmhpd_opp_svs_l1: opp5 {
opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
};
rpmhpd_opp_nom: opp6 {
opp-level = <RPMH_REGULATOR_LEVEL_NOM>;
};
rpmhpd_opp_nom_l1: opp7 {
opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>;
};
rpmhpd_opp_nom_l2: opp8 {
opp-level = <RPMH_REGULATOR_LEVEL_NOM_L2>;
};
rpmhpd_opp_turbo: opp9 {
opp-level = <RPMH_REGULATOR_LEVEL_TURBO>;
};
rpmhpd_opp_turbo_l1: opp10 {
opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>;
};
};
};
- |
// Example 2 (rpm power domain controller and OPP table):
rpmpd: power-controller {
compatible = "qcom,msm8996-rpmpd";
#power-domain-cells = <1>;
operating-points-v2 = <&rpmpd_opp_table>;
rpmpd_opp_table: opp-table {
compatible = "operating-points-v2";
rpmpd_opp_low: opp1 {
opp-level = <1>;
};
rpmpd_opp_ret: opp2 {
opp-level = <2>;
};
rpmpd_opp_svs: opp3 {
opp-level = <3>;
};
rpmpd_opp_normal: opp4 {
opp-level = <4>;
};
rpmpd_opp_high: opp5 {
opp-level = <5>;
};
rpmpd_opp_turbo: opp6 {
opp-level = <6>;
};
};
};
- |
// Example 3 (Client/Consumer device using OPP table):
leaky-device0@12350000 {
compatible = "foo,i-leak-current";
reg = <0x12350000 0x1000>;
power-domains = <&rpmhpd 0>;
operating-points-v2 = <&leaky_opp_table>;
};
leaky_opp_table: opp-table {
compatible = "operating-points-v2";
opp1 {
opp-hz = /bits/ 64 <144000>;
required-opps = <&rpmhpd_opp_low>;
};
opp2 {
opp-hz = /bits/ 64 <400000>;
required-opps = <&rpmhpd_opp_ret>;
};
opp3 {
opp-hz = /bits/ 64 <20000000>;
required-opps = <&rpmpd_opp_svs>;
};
opp4 {
opp-hz = /bits/ 64 <25000000>;
required-opps = <&rpmpd_opp_normal>;
};
};
...
......@@ -8,9 +8,41 @@ Required properties:
- compatible: Must contain: "xlnx,zynqmp-power"
- interrupts: Interrupt specifier
-------
Example
-------
Optional properties:
- mbox-names : Name given to channels seen in the 'mboxes' property.
"tx" - Mailbox corresponding to transmit path
"rx" - Mailbox corresponding to receive path
- mboxes : Standard property to specify a Mailbox. Each value of
the mboxes property should contain a phandle to the
mailbox controller device node and an args specifier
that will be the phandle to the intended sub-mailbox
child node to be used for communication. See
Documentation/devicetree/bindings/mailbox/mailbox.txt
for more details about the generic mailbox controller
and client driver bindings. Also see
Documentation/devicetree/bindings/mailbox/ \
xlnx,zynqmp-ipi-mailbox.txt for typical controller that
is used to communicate with this System controllers.
--------
Examples
--------
Example with interrupt method:
firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
zynqmp_power: zynqmp-power {
compatible = "xlnx,zynqmp-power";
interrupts = <0 35 4>;
};
};
};
Example with IPI mailbox method:
firmware {
zynqmp_firmware: zynqmp-firmware {
......@@ -19,7 +51,11 @@ firmware {
zynqmp_power: zynqmp-power {
compatible = "xlnx,zynqmp-power";
interrupt-parent = <&gic>;
interrupts = <0 35 4>;
mboxes = <&ipi_mailbox_pmu0 0>,
<&ipi_mailbox_pmu0 1>;
mbox-names = "tx", "rx";
};
};
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2020 Broadcom
%YAML 1.2
---
$id: "http://devicetree.org/schemas/reset/brcm,bcm7216-pcie-sata-rescal.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: BCM7216 RESCAL reset controller
description: This document describes the BCM7216 RESCAL reset controller which is responsible for controlling the reset of the SATA and PCIe0/1 instances on BCM7216.
maintainers:
- Florian Fainelli <f.fainelli@gmail.com>
- Jim Quinlan <jim2101024@gmail.com>
properties:
compatible:
const: brcm,bcm7216-pcie-sata-rescal
reg:
maxItems: 1
"#reset-cells":
const: 0
required:
- compatible
- reg
- "#reset-cells"
examples:
- |
reset-controller@8b2c800 {
compatible = "brcm,bcm7216-pcie-sata-rescal";
reg = <0x8b2c800 0x10>;
#reset-cells = <0>;
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/reset/intel,rcu-gw.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: System Reset Controller on Intel Gateway SoCs
maintainers:
- Dilip Kota <eswara.kota@linux.intel.com>
properties:
compatible:
enum:
- intel,rcu-lgm
- intel,rcu-xrx200
reg:
description: Reset controller registers.
maxItems: 1
intel,global-reset:
description: Global reset register offset and bit offset.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32-array
- maxItems: 2
"#reset-cells":
minimum: 2
maximum: 3
description: |
First cell is reset request register offset.
Second cell is bit offset in reset request register.
Third cell is bit offset in reset status register.
For LGM SoC, reset cell count is 2 as bit offset in
reset request and reset status registers is same. Whereas
3 for legacy SoCs as bit offset differs.
required:
- compatible
- reg
- intel,global-reset
- "#reset-cells"
additionalProperties: false
examples:
- |
rcu0: reset-controller@e0000000 {
compatible = "intel,rcu-lgm";
reg = <0xe0000000 0x20000>;
intel,global-reset = <0x10 30>;
#reset-cells = <2>;
};
pwm: pwm@e0d00000 {
status = "disabled";
compatible = "intel,lgm-pwm";
reg = <0xe0d00000 0x30>;
clocks = <&cgu0 1>;
#pwm-cells = <2>;
resets = <&rcu0 0x30 21>;
};
Nuvoton NPCM Reset controller
Required properties:
- compatible : "nuvoton,npcm750-reset" for NPCM7XX BMC
- reg : specifies physical base address and size of the register.
- #reset-cells: must be set to 2
Optional property:
- nuvoton,sw-reset-number - Contains the software reset number to restart the SoC.
NPCM7xx contain four software reset that represent numbers 1 to 4.
If 'nuvoton,sw-reset-number' is not specfied software reset is disabled.
Example:
rstc: rstc@f0801000 {
compatible = "nuvoton,npcm750-reset";
reg = <0xf0801000 0x70>;
#reset-cells = <2>;
nuvoton,sw-reset-number = <2>;
};
Specifying reset lines connected to IP NPCM7XX modules
======================================================
example:
spi0: spi@..... {
...
resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>;
...
};
The index could be found in <dt-bindings/reset/nuvoton,npcm7xx-reset.h>.
......@@ -11,6 +11,7 @@ The driver implements the Generic PM domain bindings described in
power/power-domain.yaml. It provides the power domains defined in
- include/dt-bindings/power/mt8173-power.h
- include/dt-bindings/power/mt6797-power.h
- include/dt-bindings/power/mt6765-power.h
- include/dt-bindings/power/mt2701-power.h
- include/dt-bindings/power/mt2712-power.h
- include/dt-bindings/power/mt7622-power.h
......@@ -19,6 +20,7 @@ Required properties:
- compatible: Should be one of:
- "mediatek,mt2701-scpsys"
- "mediatek,mt2712-scpsys"
- "mediatek,mt6765-scpsys"
- "mediatek,mt6797-scpsys"
- "mediatek,mt7622-scpsys"
- "mediatek,mt7623-scpsys", "mediatek,mt2701-scpsys": For MT7623 SoC
......@@ -33,6 +35,10 @@ Required properties:
enabled before enabling certain power domains.
Required clocks for MT2701 or MT7623: "mm", "mfg", "ethif"
Required clocks for MT2712: "mm", "mfg", "venc", "jpgdec", "audio", "vdec"
Required clocks for MT6765: MUX: "mm", "mfg"
CG: "mm-0", "mm-1", "mm-2", "mm-3", "isp-0",
"isp-1", "cam-0", "cam-1", "cam-2",
"cam-3","cam-4"
Required clocks for MT6797: "mm", "mfg", "vdec"
Required clocks for MT7622 or MT7629: "hif_sel"
Required clocks for MT7623A: "ethif"
......
......@@ -3289,6 +3289,8 @@ S: Maintained
N: bcm2711
N: bcm2835
F: drivers/staging/vc04_services
F: Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
F: drivers/pci/controller/pcie-brcmstb.c
BROADCOM BCM47XX MIPS ARCHITECTURE
M: Hauke Mehrtens <hauke@hauke-m.de>
......@@ -3344,6 +3346,8 @@ F: drivers/bus/brcmstb_gisb.c
F: arch/arm/mm/cache-b15-rac.c
F: arch/arm/include/asm/hardware/cache-b15-rac.h
N: brcmstb
F: Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
F: drivers/pci/controller/pcie-brcmstb.c
BROADCOM BMIPS CPUFREQ DRIVER
M: Markus Mayer <mmayer@broadcom.com>
......@@ -16148,6 +16152,7 @@ F: drivers/firmware/arm_scpi.c
F: drivers/firmware/arm_scmi/
F: drivers/reset/reset-scmi.c
F: include/linux/sc[mp]i_protocol.h
F: include/trace/events/scmi.h
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <sre@kernel.org>
......
......@@ -102,10 +102,11 @@ CPU0: cpu@0 {
reg = <0x0>;
next-level-cache = <&L2_0>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
clocks = <&apcs>;
operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>;
power-domains = <&CPU_PD0>;
power-domain-names = "psci";
};
CPU1: cpu@1 {
......@@ -114,10 +115,11 @@ CPU1: cpu@1 {
reg = <0x1>;
next-level-cache = <&L2_0>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
clocks = <&apcs>;
operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>;
power-domains = <&CPU_PD1>;
power-domain-names = "psci";
};
CPU2: cpu@2 {
......@@ -126,10 +128,11 @@ CPU2: cpu@2 {
reg = <0x2>;
next-level-cache = <&L2_0>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
clocks = <&apcs>;
operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>;
power-domains = <&CPU_PD2>;
power-domain-names = "psci";
};
CPU3: cpu@3 {
......@@ -138,10 +141,11 @@ CPU3: cpu@3 {
reg = <0x3>;
next-level-cache = <&L2_0>;
enable-method = "psci";
cpu-idle-states = <&CPU_SLEEP_0>;
clocks = <&apcs>;
operating-points-v2 = <&cpu_opp_table>;
#cooling-cells = <2>;
power-domains = <&CPU_PD3>;
power-domain-names = "psci";
};
L2_0: l2-cache {
......@@ -161,12 +165,57 @@ CPU_SLEEP_0: cpu-sleep-0 {
min-residency-us = <2000>;
local-timer-stop;
};
CLUSTER_RET: cluster-retention {
compatible = "domain-idle-state";
arm,psci-suspend-param = <0x41000012>;
entry-latency-us = <500>;
exit-latency-us = <500>;
min-residency-us = <2000>;
};
CLUSTER_PWRDN: cluster-gdhs {
compatible = "domain-idle-state";
arm,psci-suspend-param = <0x41000032>;
entry-latency-us = <2000>;
exit-latency-us = <2000>;
min-residency-us = <6000>;
};
};
};
psci {
compatible = "arm,psci-1.0";
method = "smc";
CPU_PD0: cpu-pd0 {
#power-domain-cells = <0>;
power-domains = <&CLUSTER_PD>;
domain-idle-states = <&CPU_SLEEP_0>;
};
CPU_PD1: cpu-pd1 {
#power-domain-cells = <0>;
power-domains = <&CLUSTER_PD>;
domain-idle-states = <&CPU_SLEEP_0>;
};
CPU_PD2: cpu-pd2 {
#power-domain-cells = <0>;
power-domains = <&CLUSTER_PD>;
domain-idle-states = <&CPU_SLEEP_0>;
};
CPU_PD3: cpu-pd3 {
#power-domain-cells = <0>;
power-domains = <&CLUSTER_PD>;
domain-idle-states = <&CPU_SLEEP_0>;
};
CLUSTER_PD: cluster-pd {
#power-domain-cells = <0>;
domain-idle-states = <&CLUSTER_RET>, <&CLUSTER_PWRDN>;
};
};
pmu {
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CPM_H
#define __CPM_H
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <soc/fsl/qe/qe.h>
/*
* SPI Parameter RAM common to QE and CPM.
*/
struct spi_pram {
__be16 rbase; /* Rx Buffer descriptor base address */
__be16 tbase; /* Tx Buffer descriptor base address */
u8 rfcr; /* Rx function code */
u8 tfcr; /* Tx function code */
__be16 mrblr; /* Max receive buffer length */
__be32 rstate; /* Internal */
__be32 rdp; /* Internal */
__be16 rbptr; /* Internal */
__be16 rbc; /* Internal */
__be32 rxtmp; /* Internal */
__be32 tstate; /* Internal */
__be32 tdp; /* Internal */
__be16 tbptr; /* Internal */
__be16 tbc; /* Internal */
__be32 txtmp; /* Internal */
__be32 res; /* Tx temp. */
__be16 rpbase; /* Relocation pointer (CPM1 only) */
__be16 res1; /* Reserved */
};
/*
* USB Controller pram common to QE and CPM.
*/
struct usb_ctlr {
u8 usb_usmod;
u8 usb_usadr;
u8 usb_uscom;
u8 res1[1];
__be16 usb_usep[4];
u8 res2[4];
__be16 usb_usber;
u8 res3[2];
__be16 usb_usbmr;
u8 res4[1];
u8 usb_usbs;
/* Fields down below are QE-only */
__be16 usb_ussft;
u8 res5[2];
__be16 usb_usfrn;
u8 res6[0x22];
} __attribute__ ((packed));
/*
* Function code bits, usually generic to devices.
*/
#ifdef CONFIG_CPM1
#define CPMFCR_GBL ((u_char)0x00) /* Flag doesn't exist in CPM1 */
#define CPMFCR_TC2 ((u_char)0x00) /* Flag doesn't exist in CPM1 */
#define CPMFCR_DTB ((u_char)0x00) /* Flag doesn't exist in CPM1 */
#define CPMFCR_BDB ((u_char)0x00) /* Flag doesn't exist in CPM1 */
#else
#define CPMFCR_GBL ((u_char)0x20) /* Set memory snooping */
#define CPMFCR_TC2 ((u_char)0x04) /* Transfer code 2 value */
#define CPMFCR_DTB ((u_char)0x02) /* Use local bus for data when set */
#define CPMFCR_BDB ((u_char)0x01) /* Use local bus for BD when set */
#endif
#define CPMFCR_EB ((u_char)0x10) /* Set big endian byte order */
/* Opcodes common to CPM1 and CPM2
*/
#define CPM_CR_INIT_TRX ((ushort)0x0000)
#define CPM_CR_INIT_RX ((ushort)0x0001)
#define CPM_CR_INIT_TX ((ushort)0x0002)
#define CPM_CR_HUNT_MODE ((ushort)0x0003)
#define CPM_CR_STOP_TX ((ushort)0x0004)
#define CPM_CR_GRA_STOP_TX ((ushort)0x0005)
#define CPM_CR_RESTART_TX ((ushort)0x0006)
#define CPM_CR_CLOSE_RX_BD ((ushort)0x0007)
#define CPM_CR_SET_GADDR ((ushort)0x0008)
#define CPM_CR_SET_TIMER ((ushort)0x0008)
#define CPM_CR_STOP_IDMA ((ushort)0x000b)
/* Buffer descriptors used by many of the CPM protocols. */
typedef struct cpm_buf_desc {
ushort cbd_sc; /* Status and Control */
ushort cbd_datlen; /* Data length in buffer */
uint cbd_bufaddr; /* Buffer address in host memory */
} cbd_t;
/* Buffer descriptor control/status used by serial
*/
#define BD_SC_EMPTY (0x8000) /* Receive is empty */
#define BD_SC_READY (0x8000) /* Transmit is ready */
#define BD_SC_WRAP (0x2000) /* Last buffer descriptor */
#define BD_SC_INTRPT (0x1000) /* Interrupt on change */
#define BD_SC_LAST (0x0800) /* Last buffer in frame */
#define BD_SC_TC (0x0400) /* Transmit CRC */
#define BD_SC_CM (0x0200) /* Continuous mode */
#define BD_SC_ID (0x0100) /* Rec'd too many idles */
#define BD_SC_P (0x0100) /* xmt preamble */
#define BD_SC_BR (0x0020) /* Break received */
#define BD_SC_FR (0x0010) /* Framing error */
#define BD_SC_PR (0x0008) /* Parity error */
#define BD_SC_NAK (0x0004) /* NAK - did not respond */
#define BD_SC_OV (0x0002) /* Overrun */
#define BD_SC_UN (0x0002) /* Underrun */
#define BD_SC_CD (0x0001) /* */
#define BD_SC_CL (0x0001) /* Collision */
/* Buffer descriptor control/status used by Ethernet receive.
* Common to SCC and FCC.
*/
#define BD_ENET_RX_EMPTY (0x8000)
#define BD_ENET_RX_WRAP (0x2000)
#define BD_ENET_RX_INTR (0x1000)
#define BD_ENET_RX_LAST (0x0800)
#define BD_ENET_RX_FIRST (0x0400)
#define BD_ENET_RX_MISS (0x0100)
#define BD_ENET_RX_BC (0x0080) /* FCC Only */
#define BD_ENET_RX_MC (0x0040) /* FCC Only */
#define BD_ENET_RX_LG (0x0020)
#define BD_ENET_RX_NO (0x0010)
#define BD_ENET_RX_SH (0x0008)
#define BD_ENET_RX_CR (0x0004)
#define BD_ENET_RX_OV (0x0002)
#define BD_ENET_RX_CL (0x0001)
#define BD_ENET_RX_STATS (0x01ff) /* All status bits */
/* Buffer descriptor control/status used by Ethernet transmit.
* Common to SCC and FCC.
*/
#define BD_ENET_TX_READY (0x8000)
#define BD_ENET_TX_PAD (0x4000)
#define BD_ENET_TX_WRAP (0x2000)
#define BD_ENET_TX_INTR (0x1000)
#define BD_ENET_TX_LAST (0x0800)
#define BD_ENET_TX_TC (0x0400)
#define BD_ENET_TX_DEF (0x0200)
#define BD_ENET_TX_HB (0x0100)
#define BD_ENET_TX_LC (0x0080)
#define BD_ENET_TX_RL (0x0040)
#define BD_ENET_TX_RCMASK (0x003c)
#define BD_ENET_TX_UN (0x0002)
#define BD_ENET_TX_CSL (0x0001)
#define BD_ENET_TX_STATS (0x03ff) /* All status bits */
/* Buffer descriptor control/status used by Transparent mode SCC.
*/
#define BD_SCC_TX_LAST (0x0800)
/* Buffer descriptor control/status used by I2C.
*/
#define BD_I2C_START (0x0400)
#ifdef CONFIG_CPM
int cpm_command(u32 command, u8 opcode);
#else
static inline int cpm_command(u32 command, u8 opcode)
{
return -ENOSYS;
}
#endif /* CONFIG_CPM */
int cpm2_gpiochip_add32(struct device *dev);
#endif
#include <soc/fsl/cpm.h>
......@@ -34,7 +34,6 @@
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include "mpc83xx.h"
......@@ -178,7 +177,7 @@ define_machine(mpc83xx_km) {
.name = "mpc83xx-km-platform",
.probe = mpc83xx_km_probe,
.setup_arch = mpc83xx_km_setup_arch,
.init_IRQ = mpc83xx_ipic_and_qe_init_IRQ,
.init_IRQ = mpc83xx_ipic_init_IRQ,
.get_irq = ipic_get_irq,
.restart = mpc83xx_restart,
.time_init = mpc83xx_time_init,
......
......@@ -14,7 +14,6 @@
#include <asm/io.h>
#include <asm/hw_irq.h>
#include <asm/ipic.h>
#include <soc/fsl/qe/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
......@@ -91,28 +90,6 @@ void __init mpc83xx_ipic_init_IRQ(void)
ipic_set_default_priority();
}
#ifdef CONFIG_QUICC_ENGINE
void __init mpc83xx_qe_init_IRQ(void)
{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
if (!np) {
np = of_find_node_by_type(NULL, "qeic");
if (!np)
return;
}
qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic);
of_node_put(np);
}
void __init mpc83xx_ipic_and_qe_init_IRQ(void)
{
mpc83xx_ipic_init_IRQ();
mpc83xx_qe_init_IRQ();
}
#endif /* CONFIG_QUICC_ENGINE */
static const struct of_device_id of_bus_ids[] __initconst = {
{ .type = "soc", },
{ .compatible = "soc", },
......
......@@ -33,7 +33,6 @@
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include "mpc83xx.h"
......@@ -102,7 +101,7 @@ define_machine(mpc832x_mds) {
.name = "MPC832x MDS",
.probe = mpc832x_sys_probe,
.setup_arch = mpc832x_sys_setup_arch,
.init_IRQ = mpc83xx_ipic_and_qe_init_IRQ,
.init_IRQ = mpc83xx_ipic_init_IRQ,
.get_irq = ipic_get_irq,
.restart = mpc83xx_restart,
.time_init = mpc83xx_time_init,
......
......@@ -22,7 +22,6 @@
#include <asm/ipic.h>
#include <asm/udbg.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
......@@ -220,7 +219,7 @@ define_machine(mpc832x_rdb) {
.name = "MPC832x RDB",
.probe = mpc832x_rdb_probe,
.setup_arch = mpc832x_rdb_setup_arch,
.init_IRQ = mpc83xx_ipic_and_qe_init_IRQ,
.init_IRQ = mpc83xx_ipic_init_IRQ,
.get_irq = ipic_get_irq,
.restart = mpc83xx_restart,
.time_init = mpc83xx_time_init,
......
......@@ -40,7 +40,6 @@
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include "mpc83xx.h"
......@@ -202,7 +201,7 @@ define_machine(mpc836x_mds) {
.name = "MPC836x MDS",
.probe = mpc836x_mds_probe,
.setup_arch = mpc836x_mds_setup_arch,
.init_IRQ = mpc83xx_ipic_and_qe_init_IRQ,
.init_IRQ = mpc83xx_ipic_init_IRQ,
.get_irq = ipic_get_irq,
.restart = mpc83xx_restart,
.time_init = mpc83xx_time_init,
......
......@@ -17,7 +17,6 @@
#include <asm/ipic.h>
#include <asm/udbg.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
......@@ -42,7 +41,7 @@ define_machine(mpc836x_rdk) {
.name = "MPC836x RDK",
.probe = mpc836x_rdk_probe,
.setup_arch = mpc836x_rdk_setup_arch,
.init_IRQ = mpc83xx_ipic_and_qe_init_IRQ,
.init_IRQ = mpc83xx_ipic_init_IRQ,
.get_irq = ipic_get_irq,
.restart = mpc83xx_restart,
.time_init = mpc83xx_time_init,
......
......@@ -72,13 +72,6 @@ extern int mpc837x_usb_cfg(void);
extern int mpc834x_usb_cfg(void);
extern int mpc831x_usb_cfg(void);
extern void mpc83xx_ipic_init_IRQ(void);
#ifdef CONFIG_QUICC_ENGINE
extern void mpc83xx_qe_init_IRQ(void);
extern void mpc83xx_ipic_and_qe_init_IRQ(void);
#else
static inline void __init mpc83xx_qe_init_IRQ(void) {}
#define mpc83xx_ipic_and_qe_init_IRQ mpc83xx_ipic_init_IRQ
#endif /* CONFIG_QUICC_ENGINE */
#ifdef CONFIG_PCI
extern void mpc83xx_setup_pci(void);
......
......@@ -24,7 +24,6 @@
#include <asm/mpic.h>
#include <asm/ehv_pic.h>
#include <asm/swiotlb.h>
#include <soc/fsl/qe/qe_ic.h>
#include <linux/of_platform.h>
#include <sysdev/fsl_soc.h>
......@@ -38,8 +37,6 @@ void __init corenet_gen_pic_init(void)
unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU |
MPIC_NO_RESET;
struct device_node *np;
if (ppc_md.get_irq == mpic_get_coreint_irq)
flags |= MPIC_ENABLE_COREINT;
......@@ -47,13 +44,6 @@ void __init corenet_gen_pic_init(void)
BUG_ON(mpic == NULL);
mpic_init(mpic);
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
if (np) {
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
qe_ic_cascade_high_mpic);
of_node_put(np);
}
}
/*
......
......@@ -44,7 +44,6 @@
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include <asm/mpic.h>
#include <asm/swiotlb.h>
#include "smp.h"
......@@ -268,33 +267,8 @@ static void __init mpc85xx_mds_qe_init(void)
}
}
static void __init mpc85xx_mds_qeic_init(void)
{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (!of_device_is_available(np)) {
of_node_put(np);
return;
}
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
if (!np) {
np = of_find_node_by_type(NULL, "qeic");
if (!np)
return;
}
if (machine_is(p1021_mds))
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
qe_ic_cascade_high_mpic);
else
qe_ic_init(np, 0, qe_ic_cascade_muxed_mpic, NULL);
of_node_put(np);
}
#else
static void __init mpc85xx_mds_qe_init(void) { }
static void __init mpc85xx_mds_qeic_init(void) { }
#endif /* CONFIG_QUICC_ENGINE */
static void __init mpc85xx_mds_setup_arch(void)
......@@ -364,7 +338,6 @@ static void __init mpc85xx_mds_pic_init(void)
BUG_ON(mpic == NULL);
mpic_init(mpic);
mpc85xx_mds_qeic_init();
}
static int __init mpc85xx_mds_probe(void)
......
......@@ -23,7 +23,6 @@
#include <asm/udbg.h>
#include <asm/mpic.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
......@@ -44,10 +43,6 @@ void __init mpc85xx_rdb_pic_init(void)
{
struct mpic *mpic;
#ifdef CONFIG_QUICC_ENGINE
struct device_node *np;
#endif
if (of_machine_is_compatible("fsl,MPC85XXRDB-CAMP")) {
mpic = mpic_alloc(NULL, 0, MPIC_NO_RESET |
MPIC_BIG_ENDIAN |
......@@ -62,18 +57,6 @@ void __init mpc85xx_rdb_pic_init(void)
BUG_ON(mpic == NULL);
mpic_init(mpic);
#ifdef CONFIG_QUICC_ENGINE
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
if (np) {
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
qe_ic_cascade_high_mpic);
of_node_put(np);
} else
pr_err("%s: Could not find qe-ic node\n", __func__);
#endif
}
/*
......
......@@ -19,7 +19,6 @@
#include <asm/udbg.h>
#include <asm/mpic.h>
#include <soc/fsl/qe/qe.h>
#include <soc/fsl/qe/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
......@@ -31,26 +30,12 @@ static void __init twr_p1025_pic_init(void)
{
struct mpic *mpic;
#ifdef CONFIG_QUICC_ENGINE
struct device_node *np;
#endif
mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN |
MPIC_SINGLE_DEST_CPU,
0, 256, " OpenPIC ");
BUG_ON(mpic == NULL);
mpic_init(mpic);
#ifdef CONFIG_QUICC_ENGINE
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
if (np) {
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
qe_ic_cascade_high_mpic);
of_node_put(np);
} else
pr_err("Could not find qe-ic node\n");
#endif
}
/* ************************************************************************
......
......@@ -2302,6 +2302,44 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
}
EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
/**
* of_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
* @parent_spec: OF phandle args to use for parent PM domain look-up
* @subdomain_spec: OF phandle args to use for subdomain look-up
*
* Looks-up a parent PM domain and subdomain based upon phandle args
* provided and removes the subdomain from the parent PM domain. Returns a
* negative error code on failure.
*/
int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec)
{
struct generic_pm_domain *parent, *subdomain;
int ret;
mutex_lock(&gpd_list_lock);
parent = genpd_get_from_provider(parent_spec);
if (IS_ERR(parent)) {
ret = PTR_ERR(parent);
goto out;
}
subdomain = genpd_get_from_provider(subdomain_spec);
if (IS_ERR(subdomain)) {
ret = PTR_ERR(subdomain);
goto out;
}
ret = pm_genpd_remove_subdomain(parent, subdomain);
out:
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain);
/**
* of_genpd_remove_last - Remove the last PM domain registered for a provider
* @provider: Pointer to device structure associated with provider
......
......@@ -139,7 +139,6 @@ config TEGRA_ACONNECT
tristate "Tegra ACONNECT Bus Driver"
depends on ARCH_TEGRA_210_SOC
depends on OF && PM
select PM_CLK
help
Driver for the Tegra ACONNECT bus which is used to interface with
the devices inside the Audio Processing Engine (APE) for Tegra210.
......
......@@ -102,12 +102,11 @@ static int moxtet_match(struct device *dev, struct device_driver *drv)
return 0;
}
struct bus_type moxtet_bus_type = {
static struct bus_type moxtet_bus_type = {
.name = "moxtet",
.dev_groups = moxtet_dev_groups,
.match = moxtet_match,
};
EXPORT_SYMBOL_GPL(moxtet_bus_type);
int __moxtet_register_driver(struct module *owner,
struct moxtet_driver *mdrv)
......
......@@ -479,7 +479,7 @@ static void sysc_clkdm_deny_idle(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata;
if (ddata->legacy_mode)
if (ddata->legacy_mode || (ddata->cfg.quirks & SYSC_QUIRK_CLKDM_NOAUTO))
return;
pdata = dev_get_platdata(ddata->dev);
......@@ -491,7 +491,7 @@ static void sysc_clkdm_allow_idle(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata;
if (ddata->legacy_mode)
if (ddata->legacy_mode || (ddata->cfg.quirks & SYSC_QUIRK_CLKDM_NOAUTO))
return;
pdata = dev_get_platdata(ddata->dev);
......@@ -509,10 +509,8 @@ static int sysc_init_resets(struct sysc *ddata)
{
ddata->rsts =
devm_reset_control_get_optional_shared(ddata->dev, "rstctrl");
if (IS_ERR(ddata->rsts))
return PTR_ERR(ddata->rsts);
return 0;
return PTR_ERR_OR_ZERO(ddata->rsts);
}
/**
......@@ -1216,10 +1214,6 @@ 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("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000030, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("smartreflex", 0, -1, 0x24, -1, 0x00000000, 0xffffffff,
......@@ -1251,6 +1245,12 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
/* Quirks that need to be set based on detected module */
SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff,
SYSC_MODULE_QUIRK_AESS),
SYSC_QUIRK("dcan", 0x48480000, 0x20, -1, -1, 0xa3170504, 0xffffffff,
SYSC_QUIRK_CLKDM_NOAUTO),
SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -1, 0x500a0200, 0xffffffff,
SYSC_QUIRK_CLKDM_NOAUTO),
SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -1, 0x500a0200, 0xffffffff,
SYSC_QUIRK_CLKDM_NOAUTO),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff,
SYSC_MODULE_QUIRK_HDQ1W),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff,
......
......@@ -176,7 +176,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_CLOCK },
{ SCMI_PROTOCOL_CLOCK, "clocks" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -261,7 +261,7 @@ static void scmi_cpufreq_remove(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_PERF },
{ SCMI_PROTOCOL_PERF, "cpufreq" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -21,7 +21,9 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o
obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.o
obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle-psci.o
obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle_psci.o
cpuidle_psci-y := cpuidle-psci.o
cpuidle_psci-$(CONFIG_PM_GENERIC_DOMAINS_OF) += cpuidle-psci-domain.o
###############################################################################
# MIPS drivers
......
// SPDX-License-Identifier: GPL-2.0
/*
* PM domains for CPUs via genpd - managed by cpuidle-psci.
*
* Copyright (C) 2019 Linaro Ltd.
* Author: Ulf Hansson <ulf.hansson@linaro.org>
*
*/
#define pr_fmt(fmt) "CPUidle PSCI: " fmt
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/psci.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "cpuidle-psci.h"
struct psci_pd_provider {
struct list_head link;
struct device_node *node;
};
static LIST_HEAD(psci_pd_providers);
static bool osi_mode_enabled __initdata;
static int psci_pd_power_off(struct generic_pm_domain *pd)
{
struct genpd_power_state *state = &pd->states[pd->state_idx];
u32 *pd_state;
if (!state->data)
return 0;
/* OSI mode is enabled, set the corresponding domain state. */
pd_state = state->data;
psci_set_domain_state(*pd_state);
return 0;
}
static int __init psci_pd_parse_state_nodes(struct genpd_power_state *states,
int state_count)
{
int i, ret;
u32 psci_state, *psci_state_buf;
for (i = 0; i < state_count; i++) {
ret = psci_dt_parse_state_node(to_of_node(states[i].fwnode),
&psci_state);
if (ret)
goto free_state;
psci_state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
if (!psci_state_buf) {
ret = -ENOMEM;
goto free_state;
}
*psci_state_buf = psci_state;
states[i].data = psci_state_buf;
}
return 0;
free_state:
i--;
for (; i >= 0; i--)
kfree(states[i].data);
return ret;
}
static int __init psci_pd_parse_states(struct device_node *np,
struct genpd_power_state **states, int *state_count)
{
int ret;
/* Parse the domain idle states. */
ret = of_genpd_parse_idle_states(np, states, state_count);
if (ret)
return ret;
/* Fill out the PSCI specifics for each found state. */
ret = psci_pd_parse_state_nodes(*states, *state_count);
if (ret)
kfree(*states);
return ret;
}
static void psci_pd_free_states(struct genpd_power_state *states,
unsigned int state_count)
{
int i;
for (i = 0; i < state_count; i++)
kfree(states[i].data);
kfree(states);
}
static int __init psci_pd_init(struct device_node *np)
{
struct generic_pm_domain *pd;
struct psci_pd_provider *pd_provider;
struct dev_power_governor *pd_gov;
struct genpd_power_state *states = NULL;
int ret = -ENOMEM, state_count = 0;
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd)
goto out;
pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL);
if (!pd_provider)
goto free_pd;
pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
if (!pd->name)
goto free_pd_prov;
/*
* Parse the domain idle states and let genpd manage the state selection
* for those being compatible with "domain-idle-state".
*/
ret = psci_pd_parse_states(np, &states, &state_count);
if (ret)
goto free_name;
pd->free_states = psci_pd_free_states;
pd->name = kbasename(pd->name);
pd->power_off = psci_pd_power_off;
pd->states = states;
pd->state_count = state_count;
pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
/* Use governor for CPU PM domains if it has some states to manage. */
pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL;
ret = pm_genpd_init(pd, pd_gov, false);
if (ret) {
psci_pd_free_states(states, state_count);
goto free_name;
}
ret = of_genpd_add_provider_simple(np, pd);
if (ret)
goto remove_pd;
pd_provider->node = of_node_get(np);
list_add(&pd_provider->link, &psci_pd_providers);
pr_debug("init PM domain %s\n", pd->name);
return 0;
remove_pd:
pm_genpd_remove(pd);
free_name:
kfree(pd->name);
free_pd_prov:
kfree(pd_provider);
free_pd:
kfree(pd);
out:
pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
return ret;
}
static void __init psci_pd_remove(void)
{
struct psci_pd_provider *pd_provider, *it;
struct generic_pm_domain *genpd;
list_for_each_entry_safe(pd_provider, it, &psci_pd_providers, link) {
of_genpd_del_provider(pd_provider->node);
genpd = of_genpd_remove_last(pd_provider->node);
if (!IS_ERR(genpd))
kfree(genpd);
of_node_put(pd_provider->node);
list_del(&pd_provider->link);
kfree(pd_provider);
}
}
static int __init psci_pd_init_topology(struct device_node *np, bool add)
{
struct device_node *node;
struct of_phandle_args child, parent;
int ret;
for_each_child_of_node(np, node) {
if (of_parse_phandle_with_args(node, "power-domains",
"#power-domain-cells", 0, &parent))
continue;
child.np = node;
child.args_count = 0;
ret = add ? of_genpd_add_subdomain(&parent, &child) :
of_genpd_remove_subdomain(&parent, &child);
of_node_put(parent.np);
if (ret) {
of_node_put(node);
return ret;
}
}
return 0;
}
static int __init psci_pd_add_topology(struct device_node *np)
{
return psci_pd_init_topology(np, true);
}
static void __init psci_pd_remove_topology(struct device_node *np)
{
psci_pd_init_topology(np, false);
}
static const struct of_device_id psci_of_match[] __initconst = {
{ .compatible = "arm,psci-1.0" },
{}
};
static int __init psci_idle_init_domains(void)
{
struct device_node *np = of_find_matching_node(NULL, psci_of_match);
struct device_node *node;
int ret = 0, pd_count = 0;
if (!np)
return -ENODEV;
/* Currently limit the hierarchical topology to be used in OSI mode. */
if (!psci_has_osi_support())
goto out;
/*
* Parse child nodes for the "#power-domain-cells" property and
* initialize a genpd/genpd-of-provider pair when it's found.
*/
for_each_child_of_node(np, node) {
if (!of_find_property(node, "#power-domain-cells", NULL))
continue;
ret = psci_pd_init(node);
if (ret)
goto put_node;
pd_count++;
}
/* Bail out if not using the hierarchical CPU topology. */
if (!pd_count)
goto out;
/* Link genpd masters/subdomains to model the CPU topology. */
ret = psci_pd_add_topology(np);
if (ret)
goto remove_pd;
/* Try to enable OSI mode. */
ret = psci_set_osi_mode();
if (ret) {
pr_warn("failed to enable OSI mode: %d\n", ret);
psci_pd_remove_topology(np);
goto remove_pd;
}
osi_mode_enabled = true;
of_node_put(np);
pr_info("Initialized CPU PM domain topology\n");
return pd_count;
put_node:
of_node_put(node);
remove_pd:
if (pd_count)
psci_pd_remove();
pr_err("failed to create CPU PM domains ret=%d\n", ret);
out:
of_node_put(np);
return ret;
}
subsys_initcall(psci_idle_init_domains);
struct device __init *psci_dt_attach_cpu(int cpu)
{
struct device *dev;
if (!osi_mode_enabled)
return NULL;
dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci");
if (IS_ERR_OR_NULL(dev))
return dev;
pm_runtime_irq_safe(dev);
if (cpu_online(cpu))
pm_runtime_get_sync(dev);
return dev;
}
......@@ -8,6 +8,7 @@
#define pr_fmt(fmt) "CPUidle PSCI: " fmt
#include <linux/cpuhotplug.h>
#include <linux/cpuidle.h>
#include <linux/cpumask.h>
#include <linux/cpu_pm.h>
......@@ -16,21 +17,107 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/psci.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <asm/cpuidle.h>
#include "cpuidle-psci.h"
#include "dt_idle_states.h"
static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
struct psci_cpuidle_data {
u32 *psci_states;
struct device *dev;
};
static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);
static DEFINE_PER_CPU(u32, domain_state);
static bool psci_cpuidle_use_cpuhp __initdata;
void psci_set_domain_state(u32 state)
{
__this_cpu_write(domain_state, state);
}
static inline u32 psci_get_domain_state(void)
{
return __this_cpu_read(domain_state);
}
static inline int psci_enter_state(int idx, u32 state)
{
return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter, idx, state);
}
static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
struct psci_cpuidle_data *data = this_cpu_ptr(&psci_cpuidle_data);
u32 *states = data->psci_states;
struct device *pd_dev = data->dev;
u32 state;
int ret;
/* Do runtime PM to manage a hierarchical CPU toplogy. */
pm_runtime_put_sync_suspend(pd_dev);
state = psci_get_domain_state();
if (!state)
state = states[idx];
ret = psci_enter_state(idx, state);
pm_runtime_get_sync(pd_dev);
/* Clear the domain state to start fresh when back from idle. */
psci_set_domain_state(0);
return ret;
}
static int psci_idle_cpuhp_up(unsigned int cpu)
{
struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
if (pd_dev)
pm_runtime_get_sync(pd_dev);
return 0;
}
static int psci_idle_cpuhp_down(unsigned int cpu)
{
struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
if (pd_dev) {
pm_runtime_put_sync(pd_dev);
/* Clear domain state to start fresh at next online. */
psci_set_domain_state(0);
}
return 0;
}
static void __init psci_idle_init_cpuhp(void)
{
int err;
if (!psci_cpuidle_use_cpuhp)
return;
err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING,
"cpuidle/psci:online",
psci_idle_cpuhp_up,
psci_idle_cpuhp_down);
if (err)
pr_warn("Failed %d while setup cpuhp state\n", err);
}
static int psci_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
u32 *state = __this_cpu_read(psci_power_state);
u32 *state = __this_cpu_read(psci_cpuidle_data.psci_states);
return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter,
idx, state[idx - 1]);
return psci_enter_state(idx, state[idx]);
}
static struct cpuidle_driver psci_idle_driver __initdata = {
......@@ -56,7 +143,7 @@ static const struct of_device_id psci_idle_state_match[] __initconst = {
{ },
};
static int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
{
int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
......@@ -73,28 +160,25 @@ static int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
return 0;
}
static int __init psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
struct device_node *cpu_node,
unsigned int state_count, int cpu)
{
int i, ret = 0, count = 0;
int i, ret = 0;
u32 *psci_states;
struct device_node *state_node;
struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
/* Count idle states */
while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
count))) {
count++;
of_node_put(state_node);
}
if (!count)
return -ENODEV;
psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
state_count++; /* Add WFI state too */
psci_states = kcalloc(state_count, sizeof(*psci_states), GFP_KERNEL);
if (!psci_states)
return -ENOMEM;
for (i = 0; i < count; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
for (i = 1; i < state_count; i++) {
state_node = of_get_cpu_state_node(cpu_node, i - 1);
if (!state_node)
break;
ret = psci_dt_parse_state_node(state_node, &psci_states[i]);
of_node_put(state_node);
......@@ -104,8 +188,33 @@ static int __init psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
}
/* Idle states parsed correctly, initialize per-cpu pointer */
per_cpu(psci_power_state, cpu) = psci_states;
if (i != state_count) {
ret = -ENODEV;
goto free_mem;
}
/* Currently limit the hierarchical topology to be used in OSI mode. */
if (psci_has_osi_support()) {
data->dev = psci_dt_attach_cpu(cpu);
if (IS_ERR(data->dev)) {
ret = PTR_ERR(data->dev);
goto free_mem;
}
/*
* Using the deepest state for the CPU to trigger a potential
* selection of a shared state for the domain, assumes the
* domain states are all deeper states.
*/
if (data->dev) {
drv->states[state_count - 1].enter =
psci_enter_domain_idle_state;
psci_cpuidle_use_cpuhp = true;
}
}
/* Idle states parsed correctly, store them in the per-cpu struct. */
data->psci_states = psci_states;
return 0;
free_mem:
......@@ -113,7 +222,8 @@ static int __init psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
return ret;
}
static __init int psci_cpu_init_idle(unsigned int cpu)
static __init int psci_cpu_init_idle(struct cpuidle_driver *drv,
unsigned int cpu, unsigned int state_count)
{
struct device_node *cpu_node;
int ret;
......@@ -129,7 +239,7 @@ static __init int psci_cpu_init_idle(unsigned int cpu)
if (!cpu_node)
return -ENODEV;
ret = psci_dt_cpu_init_idle(cpu_node, cpu);
ret = psci_dt_cpu_init_idle(drv, cpu_node, state_count, cpu);
of_node_put(cpu_node);
......@@ -185,7 +295,7 @@ static int __init psci_idle_init_cpu(int cpu)
/*
* Initialize PSCI idle states.
*/
ret = psci_cpu_init_idle(cpu);
ret = psci_cpu_init_idle(drv, cpu, ret);
if (ret) {
pr_err("CPU %d failed to PSCI idle\n", cpu);
goto out_kfree_drv;
......@@ -221,6 +331,7 @@ static int __init psci_idle_init(void)
goto out_fail;
}
psci_idle_init_cpuhp();
return 0;
out_fail:
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CPUIDLE_PSCI_H
#define __CPUIDLE_PSCI_H
struct device_node;
void psci_set_domain_state(u32 state);
int __init psci_dt_parse_state_node(struct device_node *np, u32 *state);
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
struct device __init *psci_dt_attach_cpu(int cpu);
#else
static inline struct device __init *psci_dt_attach_cpu(int cpu) { return NULL; }
#endif
#endif /* __CPUIDLE_PSCI_H */
......@@ -111,8 +111,7 @@ static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
cpu_node = of_cpu_device_node_get(cpu);
curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
idx);
curr_state_node = of_get_cpu_state_node(cpu_node, idx);
if (state_node != curr_state_node)
valid = false;
......@@ -170,7 +169,7 @@ int dt_init_idle_driver(struct cpuidle_driver *drv,
cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
state_node = of_get_cpu_state_node(cpu_node, i);
if (!state_node)
break;
......
......@@ -239,14 +239,6 @@ config QCOM_SCM
depends on ARM || ARM64
select RESET_CONTROLLER
config QCOM_SCM_32
def_bool y
depends on QCOM_SCM && ARM
config QCOM_SCM_64
def_bool y
depends on QCOM_SCM && ARM64
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
bool "Qualcomm download mode enabled by default"
depends on QCOM_SCM
......
......@@ -17,10 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o
obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o
......
......@@ -28,8 +28,12 @@ scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
return NULL;
for (; id->protocol_id; id++)
if (id->protocol_id == scmi_dev->protocol_id)
return id;
if (id->protocol_id == scmi_dev->protocol_id) {
if (!id->name)
return id;
else if (!strcmp(id->name, scmi_dev->name))
return id;
}
return NULL;
}
......@@ -56,6 +60,11 @@ static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
return fn(handle);
}
static int scmi_protocol_dummy_init(struct scmi_handle *handle)
{
return 0;
}
static int scmi_dev_probe(struct device *dev)
{
struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
......@@ -74,6 +83,10 @@ static int scmi_dev_probe(struct device *dev)
if (ret)
return ret;
/* Skip protocol initialisation for additional devices */
idr_replace(&scmi_protocols, &scmi_protocol_dummy_init,
scmi_dev->protocol_id);
return scmi_drv->probe(scmi_dev);
}
......@@ -125,7 +138,8 @@ static void scmi_device_release(struct device *dev)
}
struct scmi_device *
scmi_device_create(struct device_node *np, struct device *parent, int protocol)
scmi_device_create(struct device_node *np, struct device *parent, int protocol,
const char *name)
{
int id, retval;
struct scmi_device *scmi_dev;
......@@ -134,8 +148,15 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
if (!scmi_dev)
return NULL;
scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL);
if (!scmi_dev->name) {
kfree(scmi_dev);
return NULL;
}
id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL);
if (id < 0) {
kfree_const(scmi_dev->name);
kfree(scmi_dev);
return NULL;
}
......@@ -154,6 +175,7 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
return scmi_dev;
put_dev:
kfree_const(scmi_dev->name);
put_device(&scmi_dev->dev);
ida_simple_remove(&scmi_bus_id, id);
return NULL;
......@@ -161,6 +183,7 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
void scmi_device_destroy(struct scmi_device *scmi_dev)
{
kfree_const(scmi_dev->name);
scmi_handle_put(scmi_dev->handle);
ida_simple_remove(&scmi_bus_id, scmi_dev->id);
device_unregister(&scmi_dev->dev);
......
......@@ -65,6 +65,7 @@ struct scmi_clock_set_rate {
};
struct clock_info {
u32 version;
int num_clocks;
int max_async_req;
atomic_t cur_async_req;
......@@ -340,6 +341,7 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle)
scmi_clock_describe_rates_get(handle, clkid, clk);
}
cinfo->version = version;
handle->clk_ops = &clk_ops;
handle->clk_priv = cinfo;
......
......@@ -81,6 +81,7 @@ struct scmi_msg {
/**
* struct scmi_xfer - Structure representing a message flow
*
* @transfer_id: Unique ID for debug & profiling purpose
* @hdr: Transmit message header
* @tx: Transmit message
* @rx: Receive message, the buffer should be pre-allocated to store
......@@ -90,6 +91,7 @@ struct scmi_msg {
* @async: pointer to delayed response message received event completion
*/
struct scmi_xfer {
int transfer_id;
struct scmi_msg_hdr hdr;
struct scmi_msg tx;
struct scmi_msg rx;
......
......@@ -29,6 +29,9 @@
#include "common.h"
#define CREATE_TRACE_POINTS
#include <trace/events/scmi.h>
#define MSG_ID_MASK GENMASK(7, 0)
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
#define MSG_TYPE_MASK GENMASK(9, 8)
......@@ -61,6 +64,8 @@ enum scmi_error_codes {
static LIST_HEAD(scmi_list);
/* Protection for the entire list */
static DEFINE_MUTEX(scmi_list_mutex);
/* Track the unique id for the transfers for debug & profiling purpose */
static atomic_t transfer_last_id;
/**
* struct scmi_xfers_info - Structure to manage transfer information
......@@ -304,6 +309,7 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
xfer = &minfo->xfer_block[xfer_id];
xfer->hdr.seq = xfer_id;
reinit_completion(&xfer->done);
xfer->transfer_id = atomic_inc_return(&transfer_last_id);
return xfer;
}
......@@ -374,6 +380,10 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m)
scmi_fetch_response(xfer, mem);
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
msg_type);
if (msg_type == MSG_TYPE_DELAYED_RESP)
complete(xfer->async_done);
else
......@@ -439,6 +449,10 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
if (unlikely(!cinfo))
return -EINVAL;
trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.poll_completion);
ret = mbox_send_message(cinfo->chan, xfer);
if (ret < 0) {
dev_dbg(dev, "mbox send fail %d\n", ret);
......@@ -478,6 +492,10 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
*/
mbox_client_txdone(cinfo->chan, ret);
trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.status);
return ret;
}
......@@ -735,6 +753,11 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev,
idx = tx ? 0 : 1;
idr = tx ? &info->tx_idr : &info->rx_idr;
/* check if already allocated, used for multiple device per protocol */
cinfo = idr_find(idr, prot_id);
if (cinfo)
return 0;
if (scmi_mailbox_check(np, idx)) {
cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
......@@ -803,11 +826,11 @@ scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id)
static inline void
scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
int prot_id)
int prot_id, const char *name)
{
struct scmi_device *sdev;
sdev = scmi_device_create(np, info->dev, prot_id);
sdev = scmi_device_create(np, info->dev, prot_id, name);
if (!sdev) {
dev_err(info->dev, "failed to create %d protocol device\n",
prot_id);
......@@ -824,6 +847,40 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
scmi_set_handle(sdev);
}
#define MAX_SCMI_DEV_PER_PROTOCOL 2
struct scmi_prot_devnames {
int protocol_id;
char *names[MAX_SCMI_DEV_PER_PROTOCOL];
};
static struct scmi_prot_devnames devnames[] = {
{ SCMI_PROTOCOL_POWER, { "genpd" },},
{ SCMI_PROTOCOL_PERF, { "cpufreq" },},
{ SCMI_PROTOCOL_CLOCK, { "clocks" },},
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
{ SCMI_PROTOCOL_RESET, { "reset" },},
};
static inline void
scmi_create_protocol_devices(struct device_node *np, struct scmi_info *info,
int prot_id)
{
int loop, cnt;
for (loop = 0; loop < ARRAY_SIZE(devnames); loop++) {
if (devnames[loop].protocol_id != prot_id)
continue;
for (cnt = 0; cnt < ARRAY_SIZE(devnames[loop].names); cnt++) {
const char *name = devnames[loop].names[cnt];
if (name)
scmi_create_protocol_device(np, info, prot_id,
name);
}
}
}
static int scmi_probe(struct platform_device *pdev)
{
int ret;
......@@ -892,7 +949,7 @@ static int scmi_probe(struct platform_device *pdev)
continue;
}
scmi_create_protocol_device(child, info, prot_id);
scmi_create_protocol_devices(child, info, prot_id);
}
return 0;
......@@ -940,6 +997,52 @@ static int scmi_remove(struct platform_device *pdev)
return ret;
}
static ssize_t protocol_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%u.%u\n", info->version.major_ver,
info->version.minor_ver);
}
static DEVICE_ATTR_RO(protocol_version);
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "0x%x\n", info->version.impl_ver);
}
static DEVICE_ATTR_RO(firmware_version);
static ssize_t vendor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", info->version.vendor_id);
}
static DEVICE_ATTR_RO(vendor_id);
static ssize_t sub_vendor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scmi_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", info->version.sub_vendor_id);
}
static DEVICE_ATTR_RO(sub_vendor_id);
static struct attribute *versions_attrs[] = {
&dev_attr_firmware_version.attr,
&dev_attr_protocol_version.attr,
&dev_attr_vendor_id.attr,
&dev_attr_sub_vendor_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(versions);
static const struct scmi_desc scmi_generic_desc = {
.max_rx_timeout_ms = 30, /* We may increase this if required */
.max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */
......@@ -958,6 +1061,7 @@ static struct platform_driver scmi_driver = {
.driver = {
.name = "arm-scmi",
.of_match_table = scmi_of_match,
.dev_groups = versions_groups,
},
.probe = scmi_probe,
.remove = scmi_remove,
......
......@@ -145,6 +145,7 @@ struct perf_dom_info {
};
struct scmi_perf_info {
u32 version;
int num_domains;
bool power_scale_mw;
u64 stats_addr;
......@@ -736,6 +737,7 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
}
pinfo->version = version;
handle->perf_ops = &perf_ops;
handle->perf_priv = pinfo;
......
......@@ -50,6 +50,7 @@ struct power_dom_info {
};
struct scmi_power_info {
u32 version;
int num_domains;
u64 stats_addr;
u32 stats_size;
......@@ -207,6 +208,7 @@ static int scmi_power_protocol_init(struct scmi_handle *handle)
scmi_power_domain_attributes_get(handle, domain, dom);
}
pinfo->version = version;
handle->power_ops = &power_ops;
handle->power_priv = pinfo;
......
......@@ -48,6 +48,7 @@ struct reset_dom_info {
};
struct scmi_reset_info {
u32 version;
int num_domains;
struct reset_dom_info *dom_info;
};
......@@ -217,6 +218,7 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle)
scmi_reset_domain_attributes_get(handle, domain, dom);
}
pinfo->version = version;
handle->reset_ops = &reset_ops;
handle->reset_priv = pinfo;
......
......@@ -112,7 +112,7 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_POWER },
{ SCMI_PROTOCOL_POWER, "genpd" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -68,6 +68,7 @@ struct scmi_msg_sensor_reading_get {
};
struct sensors_info {
u32 version;
int num_sensors;
int max_requests;
u64 reg_addr;
......@@ -294,6 +295,7 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle)
scmi_sensor_description_get(handle, sinfo);
sinfo->version = version;
handle->sensor_ops = &sensor_ops;
handle->sensor_priv = sinfo;
......
# SPDX-License-Identifier: GPL-2.0-only
config IMX_DSP
bool "IMX DSP Protocol driver"
tristate "IMX DSP Protocol driver"
depends on IMX_MBOX
help
This enables DSP IPC protocol between host AP (Linux)
......
......@@ -97,7 +97,7 @@ static inline bool psci_has_ext_power_state(void)
PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
}
static inline bool psci_has_osi_support(void)
bool psci_has_osi_support(void)
{
return psci_cpu_suspend_feature & PSCI_1_0_OS_INITIATED;
}
......@@ -162,6 +162,15 @@ static u32 psci_get_version(void)
return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
}
int psci_set_osi_mode(void)
{
int err;
err = invoke_psci_fn(PSCI_1_0_FN_SET_SUSPEND_MODE,
PSCI_1_0_SUSPEND_MODE_OSI, 0, 0);
return psci_to_linux_errno(err);
}
static int psci_cpu_suspend(u32 state, unsigned long entry_point)
{
int err;
......@@ -544,9 +553,14 @@ static int __init psci_1_0_init(struct device_node *np)
if (err)
return err;
if (psci_has_osi_support())
if (psci_has_osi_support()) {
pr_info("OSI mode supported.\n");
/* Default to PC mode. */
invoke_psci_fn(PSCI_1_0_FN_SET_SUSPEND_MODE,
PSCI_1_0_SUSPEND_MODE_PC, 0, 0);
}
return 0;
}
......
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
* Copyright (C) 2015 Linaro Ltd.
*/
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/qcom_scm.h>
#include <linux/arm-smccc.h>
#include <linux/dma-mapping.h>
#include "qcom_scm.h"
static DEFINE_MUTEX(qcom_scm_lock);
/**
* struct arm_smccc_args
* @args: The array of values used in registers in smc instruction
*/
struct arm_smccc_args {
unsigned long args[8];
};
/**
* struct scm_legacy_command - one SCM command buffer
* @len: total available memory for command and response
* @buf_offset: start of command buffer
* @resp_hdr_offset: start of response buffer
* @id: command to be executed
* @buf: buffer returned from scm_legacy_get_command_buffer()
*
* An SCM command is laid out in memory as follows:
*
* ------------------- <--- struct scm_legacy_command
* | command header |
* ------------------- <--- scm_legacy_get_command_buffer()
* | command buffer |
* ------------------- <--- struct scm_legacy_response and
* | response header | scm_legacy_command_to_response()
* ------------------- <--- scm_legacy_get_response_buffer()
* | response buffer |
* -------------------
*
* There can be arbitrary padding between the headers and buffers so
* you should always use the appropriate scm_legacy_get_*_buffer() routines
* to access the buffers in a safe manner.
*/
struct scm_legacy_command {
__le32 len;
__le32 buf_offset;
__le32 resp_hdr_offset;
__le32 id;
__le32 buf[0];
};
/**
* struct scm_legacy_response - one SCM response buffer
* @len: total available memory for response
* @buf_offset: start of response data relative to start of scm_legacy_response
* @is_complete: indicates if the command has finished processing
*/
struct scm_legacy_response {
__le32 len;
__le32 buf_offset;
__le32 is_complete;
};
/**
* scm_legacy_command_to_response() - Get a pointer to a scm_legacy_response
* @cmd: command
*
* Returns a pointer to a response for a command.
*/
static inline struct scm_legacy_response *scm_legacy_command_to_response(
const struct scm_legacy_command *cmd)
{
return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset);
}
/**
* scm_legacy_get_command_buffer() - Get a pointer to a command buffer
* @cmd: command
*
* Returns a pointer to the command buffer of a command.
*/
static inline void *scm_legacy_get_command_buffer(
const struct scm_legacy_command *cmd)
{
return (void *)cmd->buf;
}
/**
* scm_legacy_get_response_buffer() - Get a pointer to a response buffer
* @rsp: response
*
* Returns a pointer to a response buffer of a response.
*/
static inline void *scm_legacy_get_response_buffer(
const struct scm_legacy_response *rsp)
{
return (void *)rsp + le32_to_cpu(rsp->buf_offset);
}
static void __scm_legacy_do(const struct arm_smccc_args *smc,
struct arm_smccc_res *res)
{
do {
arm_smccc_smc(smc->args[0], smc->args[1], smc->args[2],
smc->args[3], smc->args[4], smc->args[5],
smc->args[6], smc->args[7], res);
} while (res->a0 == QCOM_SCM_INTERRUPTED);
}
/**
* qcom_scm_call() - Sends a command to the SCM and waits for the command to
* finish processing.
*
* A note on cache maintenance:
* Note that any buffers that are expected to be accessed by the secure world
* must be flushed before invoking qcom_scm_call and invalidated in the cache
* immediately after qcom_scm_call returns. Cache maintenance on the command
* and response buffers is taken care of by qcom_scm_call; however, callers are
* responsible for any other cached buffers passed over to the secure world.
*/
int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
struct qcom_scm_res *res)
{
u8 arglen = desc->arginfo & 0xf;
int ret = 0, context_id;
unsigned int i;
struct scm_legacy_command *cmd;
struct scm_legacy_response *rsp;
struct arm_smccc_args smc = {0};
struct arm_smccc_res smc_res;
const size_t cmd_len = arglen * sizeof(__le32);
const size_t resp_len = MAX_QCOM_SCM_RETS * sizeof(__le32);
size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
dma_addr_t cmd_phys;
__le32 *arg_buf;
const __le32 *res_buf;
cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->len = cpu_to_le32(alloc_len);
cmd->buf_offset = cpu_to_le32(sizeof(*cmd));
cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len);
cmd->id = cpu_to_le32(SCM_LEGACY_FNID(desc->svc, desc->cmd));
arg_buf = scm_legacy_get_command_buffer(cmd);
for (i = 0; i < arglen; i++)
arg_buf[i] = cpu_to_le32(desc->args[i]);
rsp = scm_legacy_command_to_response(cmd);
cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, cmd_phys)) {
kfree(cmd);
return -ENOMEM;
}
smc.args[0] = 1;
smc.args[1] = (unsigned long)&context_id;
smc.args[2] = cmd_phys;
mutex_lock(&qcom_scm_lock);
__scm_legacy_do(&smc, &smc_res);
if (smc_res.a0)
ret = qcom_scm_remap_error(smc_res.a0);
mutex_unlock(&qcom_scm_lock);
if (ret)
goto out;
do {
dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len,
sizeof(*rsp), DMA_FROM_DEVICE);
} while (!rsp->is_complete);
dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
le32_to_cpu(rsp->buf_offset),
resp_len, DMA_FROM_DEVICE);
if (res) {
res_buf = scm_legacy_get_response_buffer(rsp);
for (i = 0; i < MAX_QCOM_SCM_RETS; i++)
res->result[i] = le32_to_cpu(res_buf[i]);
}
out:
dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
kfree(cmd);
return ret;
}
#define SCM_LEGACY_ATOMIC_N_REG_ARGS 5
#define SCM_LEGACY_ATOMIC_FIRST_REG_IDX 2
#define SCM_LEGACY_CLASS_REGISTER (0x2 << 8)
#define SCM_LEGACY_MASK_IRQS BIT(5)
#define SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \
((SCM_LEGACY_FNID(svc, cmd) << 12) | \
SCM_LEGACY_CLASS_REGISTER | \
SCM_LEGACY_MASK_IRQS | \
(n & 0xf))
/**
* qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments
* and 3 return values
* @desc: SCM call descriptor containing arguments
* @res: SCM call return values
*
* This shall only be used with commands that are guaranteed to be
* uninterruptable, atomic and SMP safe.
*/
int scm_legacy_call_atomic(struct device *unused,
const struct qcom_scm_desc *desc,
struct qcom_scm_res *res)
{
int context_id;
struct arm_smccc_res smc_res;
size_t arglen = desc->arginfo & 0xf;
BUG_ON(arglen > SCM_LEGACY_ATOMIC_N_REG_ARGS);
arm_smccc_smc(SCM_LEGACY_ATOMIC_ID(desc->svc, desc->cmd, arglen),
(unsigned long)&context_id,
desc->args[0], desc->args[1], desc->args[2],
desc->args[3], desc->args[4], 0, &smc_res);
if (res) {
res->result[0] = smc_res.a1;
res->result[1] = smc_res.a2;
res->result[2] = smc_res.a3;
}
return smc_res.a0;
}
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved.
*/
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/qcom_scm.h>
#include <linux/arm-smccc.h>
#include <linux/dma-mapping.h>
#include "qcom_scm.h"
/**
* struct arm_smccc_args
* @args: The array of values used in registers in smc instruction
*/
struct arm_smccc_args {
unsigned long args[8];
};
static DEFINE_MUTEX(qcom_scm_lock);
#define QCOM_SCM_EBUSY_WAIT_MS 30
#define QCOM_SCM_EBUSY_MAX_RETRY 20
#define SCM_SMC_N_REG_ARGS 4
#define SCM_SMC_FIRST_EXT_IDX (SCM_SMC_N_REG_ARGS - 1)
#define SCM_SMC_N_EXT_ARGS (MAX_QCOM_SCM_ARGS - SCM_SMC_N_REG_ARGS + 1)
#define SCM_SMC_FIRST_REG_IDX 2
#define SCM_SMC_LAST_REG_IDX (SCM_SMC_FIRST_REG_IDX + SCM_SMC_N_REG_ARGS - 1)
static void __scm_smc_do_quirk(const struct arm_smccc_args *smc,
struct arm_smccc_res *res)
{
unsigned long a0 = smc->args[0];
struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
quirk.state.a6 = 0;
do {
arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2],
smc->args[3], smc->args[4], smc->args[5],
quirk.state.a6, smc->args[7], res, &quirk);
if (res->a0 == QCOM_SCM_INTERRUPTED)
a0 = res->a0;
} while (res->a0 == QCOM_SCM_INTERRUPTED);
}
static void __scm_smc_do(const struct arm_smccc_args *smc,
struct arm_smccc_res *res, bool atomic)
{
int retry_count = 0;
if (atomic) {
__scm_smc_do_quirk(smc, res);
return;
}
do {
mutex_lock(&qcom_scm_lock);
__scm_smc_do_quirk(smc, res);
mutex_unlock(&qcom_scm_lock);
if (res->a0 == QCOM_SCM_V2_EBUSY) {
if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
break;
msleep(QCOM_SCM_EBUSY_WAIT_MS);
}
} while (res->a0 == QCOM_SCM_V2_EBUSY);
}
int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
struct qcom_scm_res *res, bool atomic)
{
int arglen = desc->arginfo & 0xf;
int i;
dma_addr_t args_phys = 0;
void *args_virt = NULL;
size_t alloc_len;
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
u32 qcom_smccc_convention =
(qcom_scm_convention == SMC_CONVENTION_ARM_32) ?
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
struct arm_smccc_res smc_res;
struct arm_smccc_args smc = {0};
smc.args[0] = ARM_SMCCC_CALL_VAL(
smccc_call_type,
qcom_smccc_convention,
desc->owner,
SCM_SMC_FNID(desc->svc, desc->cmd));
smc.args[1] = desc->arginfo;
for (i = 0; i < SCM_SMC_N_REG_ARGS; i++)
smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i];
if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) {
alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64);
args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag);
if (!args_virt)
return -ENOMEM;
if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
__le32 *args = args_virt;
for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
args[i] = cpu_to_le32(desc->args[i +
SCM_SMC_FIRST_EXT_IDX]);
} else {
__le64 *args = args_virt;
for (i = 0; i < SCM_SMC_N_EXT_ARGS; i++)
args[i] = cpu_to_le64(desc->args[i +
SCM_SMC_FIRST_EXT_IDX]);
}
args_phys = dma_map_single(dev, args_virt, alloc_len,
DMA_TO_DEVICE);
if (dma_mapping_error(dev, args_phys)) {
kfree(args_virt);
return -ENOMEM;
}
smc.args[SCM_SMC_LAST_REG_IDX] = args_phys;
}
__scm_smc_do(&smc, &smc_res, atomic);
if (args_virt) {
dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
kfree(args_virt);
}
if (res) {
res->result[0] = smc_res.a1;
res->result[1] = smc_res.a2;
res->result[2] = smc_res.a3;
}
return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
}
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2015,2019 The Linux Foundation. All rights reserved.
*/
#ifndef __QCOM_SCM_INT_H
#define __QCOM_SCM_INT_H
#define QCOM_SCM_SVC_BOOT 0x1
#define QCOM_SCM_BOOT_ADDR 0x1
#define QCOM_SCM_SET_DLOAD_MODE 0x10
#define QCOM_SCM_BOOT_ADDR_MC 0x11
#define QCOM_SCM_SET_REMOTE_STATE 0xa
extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id);
extern int __qcom_scm_set_dload_mode(struct device *dev, bool enable);
#define QCOM_SCM_FLAG_HLOS 0x01
#define QCOM_SCM_FLAG_COLDBOOT_MC 0x02
#define QCOM_SCM_FLAG_WARMBOOT_MC 0x04
extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
const cpumask_t *cpus);
extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus);
#define QCOM_SCM_CMD_TERMINATE_PC 0x2
enum qcom_scm_convention {
SMC_CONVENTION_UNKNOWN,
SMC_CONVENTION_LEGACY,
SMC_CONVENTION_ARM_32,
SMC_CONVENTION_ARM_64,
};
extern enum qcom_scm_convention qcom_scm_convention;
#define MAX_QCOM_SCM_ARGS 10
#define MAX_QCOM_SCM_RETS 3
enum qcom_scm_arg_types {
QCOM_SCM_VAL,
QCOM_SCM_RO,
QCOM_SCM_RW,
QCOM_SCM_BUFVAL,
};
#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\
(((a) & 0x3) << 4) | \
(((b) & 0x3) << 6) | \
(((c) & 0x3) << 8) | \
(((d) & 0x3) << 10) | \
(((e) & 0x3) << 12) | \
(((f) & 0x3) << 14) | \
(((g) & 0x3) << 16) | \
(((h) & 0x3) << 18) | \
(((i) & 0x3) << 20) | \
(((j) & 0x3) << 22) | \
((num) & 0xf))
#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
/**
* struct qcom_scm_desc
* @arginfo: Metadata describing the arguments in args[]
* @args: The array of arguments for the secure syscall
*/
struct qcom_scm_desc {
u32 svc;
u32 cmd;
u32 arginfo;
u64 args[MAX_QCOM_SCM_ARGS];
u32 owner;
};
/**
* struct qcom_scm_res
* @result: The values returned by the secure syscall
*/
struct qcom_scm_res {
u64 result[MAX_QCOM_SCM_RETS];
};
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
extern int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
struct qcom_scm_res *res, bool atomic);
#define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff))
extern int scm_legacy_call_atomic(struct device *dev,
const struct qcom_scm_desc *desc,
struct qcom_scm_res *res);
extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
struct qcom_scm_res *res);
#define QCOM_SCM_SVC_BOOT 0x01
#define QCOM_SCM_BOOT_SET_ADDR 0x01
#define QCOM_SCM_BOOT_TERMINATE_PC 0x02
#define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10
#define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a
#define QCOM_SCM_FLUSH_FLAG_MASK 0x3
#define QCOM_SCM_CMD_CORE_HOTPLUGGED 0x10
extern void __qcom_scm_cpu_power_down(u32 flags);
#define QCOM_SCM_SVC_IO 0x5
#define QCOM_SCM_IO_READ 0x1
#define QCOM_SCM_IO_WRITE 0x2
extern int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, unsigned int *val);
extern int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val);
#define QCOM_SCM_SVC_PIL 0x02
#define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01
#define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02
#define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
#define QCOM_SCM_SVC_IO 0x05
#define QCOM_SCM_IO_READ 0x01
#define QCOM_SCM_IO_WRITE 0x02
#define QCOM_SCM_SVC_INFO 0x06
#define QCOM_SCM_INFO_IS_CALL_AVAIL 0x01
#define QCOM_SCM_SVC_INFO 0x6
#define QCOM_IS_CALL_AVAIL_CMD 0x1
extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
u32 cmd_id);
#define QCOM_SCM_SVC_MP 0x0c
#define QCOM_SCM_MP_RESTORE_SEC_CFG 0x02
#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE 0x03
#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT 0x04
#define QCOM_SCM_MP_ASSIGN 0x16
#define QCOM_SCM_SVC_OCMEM 0x0f
#define QCOM_SCM_OCMEM_LOCK_CMD 0x01
#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02
#define QCOM_SCM_SVC_HDCP 0x11
#define QCOM_SCM_CMD_HDCP 0x01
extern int __qcom_scm_hdcp_req(struct device *dev,
struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);
#define QCOM_SCM_HDCP_INVOKE 0x01
extern void __qcom_scm_init(void);
#define QCOM_SCM_SVC_SMMU_PROGRAM 0x15
#define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03
#define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02
#define QCOM_SCM_OCMEM_SVC 0xf
#define QCOM_SCM_OCMEM_LOCK_CMD 0x1
#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x2
extern int __qcom_scm_ocmem_lock(struct device *dev, u32 id, u32 offset,
u32 size, u32 mode);
extern int __qcom_scm_ocmem_unlock(struct device *dev, u32 id, u32 offset,
u32 size);
#define QCOM_SCM_SVC_PIL 0x2
#define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1
#define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2
#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5
#define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6
#define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7
#define QCOM_SCM_PAS_MSS_RESET 0xa
extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral);
extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
dma_addr_t metadata_phys);
extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
phys_addr_t addr, phys_addr_t size);
extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral);
extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral);
extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset);
extern void __qcom_scm_init(void);
/* common error codes */
#define QCOM_SCM_V2_EBUSY -12
......@@ -94,25 +139,4 @@ static inline int qcom_scm_remap_error(int err)
return -EINVAL;
}
#define QCOM_SCM_SVC_MP 0xc
#define QCOM_SCM_RESTORE_SEC_CFG 2
extern int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id,
u32 spare);
#define QCOM_SCM_IOMMU_SECURE_PTBL_SIZE 3
#define QCOM_SCM_IOMMU_SECURE_PTBL_INIT 4
#define QCOM_SCM_SVC_SMMU_PROGRAM 0x15
#define QCOM_SCM_CONFIG_ERRATA1 0x3
#define QCOM_SCM_CONFIG_ERRATA1_CLIENT_ALL 0x2
extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare,
size_t *size);
extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr,
u32 size, u32 spare);
extern int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev,
bool enable);
#define QCOM_MEM_PROT_ASSIGN_ID 0x16
extern int __qcom_scm_assign_mem(struct device *dev,
phys_addr_t mem_region, size_t mem_sz,
phys_addr_t src, size_t src_sz,
phys_addr_t dest, size_t dest_sz);
#endif
......@@ -197,7 +197,7 @@ static int mox_get_board_info(struct mox_rwtm *rwtm)
rwtm->serial_number = reply->status[1];
rwtm->serial_number <<= 32;
rwtm->serial_number |= reply->status[0];
rwtm->board_version = reply->status[2];
rwtm->board_version = reply->status[2];
rwtm->ram_size = reply->status[3];
reply_to_mac_addr(rwtm->mac_address1, reply->status[4],
reply->status[5]);
......
......@@ -26,6 +26,9 @@
static const struct zynqmp_eemi_ops *eemi_ops_tbl;
static bool feature_check_enabled;
static u32 zynqmp_pm_features[PM_API_MAX];
static const struct mfd_cell firmware_devs[] = {
{
.name = "zynqmp_power_controller",
......@@ -44,6 +47,8 @@ static int zynqmp_pm_ret_code(u32 ret_status)
case XST_PM_SUCCESS:
case XST_PM_DOUBLE_REQ:
return 0;
case XST_PM_NO_FEATURE:
return -ENOTSUPP;
case XST_PM_NO_ACCESS:
return -EACCES;
case XST_PM_ABORT_SUSPEND:
......@@ -128,6 +133,39 @@ static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
}
/**
* zynqmp_pm_feature() - Check weather given feature is supported or not
* @api_id: API ID to check
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_feature(u32 api_id)
{
int ret;
u32 ret_payload[PAYLOAD_ARG_CNT];
u64 smc_arg[2];
if (!feature_check_enabled)
return 0;
/* Return value if feature is already checked */
if (zynqmp_pm_features[api_id] != PM_FEATURE_UNCHECKED)
return zynqmp_pm_features[api_id];
smc_arg[0] = PM_SIP_SVC | PM_FEATURE_CHECK;
smc_arg[1] = api_id;
ret = do_fw_call(smc_arg[0], smc_arg[1], 0, ret_payload);
if (ret) {
zynqmp_pm_features[api_id] = PM_FEATURE_INVALID;
return PM_FEATURE_INVALID;
}
zynqmp_pm_features[api_id] = ret_payload[1];
return zynqmp_pm_features[api_id];
}
/**
* zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
* caller function depending on the configuration
......@@ -162,6 +200,9 @@ int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
*/
u64 smc_arg[4];
if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
return -ENOTSUPP;
smc_arg[0] = PM_SIP_SVC | pm_api_id;
smc_arg[1] = ((u64)arg1 << 32) | arg0;
smc_arg[2] = ((u64)arg3 << 32) | arg2;
......@@ -717,6 +758,8 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
np = of_find_compatible_node(NULL, NULL, "xlnx,versal");
if (!np)
return 0;
feature_check_enabled = true;
}
of_node_put(np);
......
......@@ -259,7 +259,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_SENSOR },
{ SCMI_PROTOCOL_SENSOR, "hwmon" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
......
......@@ -143,7 +143,6 @@ static const struct mbox_chan_ops a37xx_mbox_ops = {
static int armada_37xx_mbox_probe(struct platform_device *pdev)
{
struct a37xx_mbox *mbox;
struct resource *regs;
struct mbox_chan *chans;
int ret;
......@@ -156,9 +155,7 @@ static int armada_37xx_mbox_probe(struct platform_device *pdev)
if (!chans)
return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mbox->base = devm_ioremap_resource(&pdev->dev, regs);
mbox->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->base)) {
dev_err(&pdev->dev, "ioremap failed\n");
return PTR_ERR(mbox->base);
......
......@@ -267,7 +267,6 @@ static int mvebu_devbus_probe(struct platform_device *pdev)
struct devbus_read_params r;
struct devbus_write_params w;
struct devbus *devbus;
struct resource *res;
struct clk *clk;
unsigned long rate;
int err;
......@@ -277,8 +276,7 @@ static int mvebu_devbus_probe(struct platform_device *pdev)
return -ENOMEM;
devbus->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
devbus->base = devm_ioremap_resource(&pdev->dev, res);
devbus->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(devbus->base))
return PTR_ERR(devbus->base);
......
......@@ -8,7 +8,7 @@ config SAMSUNG_MC
if SAMSUNG_MC
config EXYNOS5422_DMC
tristate "EXYNOS5422 Dynamic Memory Controller driver"
tristate "Exynos5422 Dynamic Memory Controller driver"
depends on ARCH_EXYNOS || (COMPILE_TEST && HAS_IOMEM)
select DDR
depends on DEVFREQ_GOV_SIMPLE_ONDEMAND
......
......@@ -3,7 +3,7 @@
// Copyright (c) 2015 Samsung Electronics Co., Ltd.
// http://www.samsung.com/
//
// EXYNOS - SROM Controller support
// Exynos - SROM Controller support
// Author: Pankaj Dubey <pankaj.dubey@samsung.com>
#include <linux/io.h>
......
......@@ -1374,7 +1374,6 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct exynos5_dmc *dmc;
struct resource *res;
int irq[2];
dmc = devm_kzalloc(dev, sizeof(*dmc), GFP_KERNEL);
......@@ -1386,13 +1385,11 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
dmc->dev = dev;
platform_set_drvdata(pdev, dmc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dmc->base_drexi0 = devm_ioremap_resource(dev, res);
dmc->base_drexi0 = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dmc->base_drexi0))
return PTR_ERR(dmc->base_drexi0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dmc->base_drexi1 = devm_ioremap_resource(dev, res);
dmc->base_drexi1 = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(dmc->base_drexi1))
return PTR_ERR(dmc->base_drexi1);
......
......@@ -13,4 +13,5 @@ obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra186-emc.o
......@@ -467,12 +467,20 @@ struct tegra_emc {
void __iomem *regs;
struct clk *clk;
enum emc_dram_type dram_type;
unsigned int dram_num;
struct emc_timing last_timing;
struct emc_timing *timings;
unsigned int num_timings;
struct {
struct dentry *root;
unsigned long min_rate;
unsigned long max_rate;
} debugfs;
};
/* Timing change sequence functions */
......@@ -998,38 +1006,51 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
return NULL;
}
/* Debugfs entry */
/*
* debugfs interface
*
* The memory controller driver exposes some files in debugfs that can be used
* to control the EMC frequency. The top-level directory can be found here:
*
* /sys/kernel/debug/emc
*
* It contains the following files:
*
* - available_rates: This file contains a list of valid, space-separated
* EMC frequencies.
*
* - min_rate: Writing a value to this file sets the given frequency as the
* floor of the permitted range. If this is higher than the currently
* configured EMC frequency, this will cause the frequency to be
* increased so that it stays within the valid range.
*
* - max_rate: Similarily to the min_rate file, writing a value to this file
* sets the given frequency as the ceiling of the permitted range. If
* the value is lower than the currently configured EMC frequency, this
* will cause the frequency to be decreased so that it stays within the
* valid range.
*/
static int emc_debug_rate_get(void *data, u64 *rate)
static bool tegra_emc_validate_rate(struct tegra_emc *emc, unsigned long rate)
{
struct clk *c = data;
*rate = clk_get_rate(c);
return 0;
}
unsigned int i;
static int emc_debug_rate_set(void *data, u64 rate)
{
struct clk *c = data;
for (i = 0; i < emc->num_timings; i++)
if (rate == emc->timings[i].rate)
return true;
return clk_set_rate(c, rate);
return false;
}
DEFINE_SIMPLE_ATTRIBUTE(emc_debug_rate_fops, emc_debug_rate_get,
emc_debug_rate_set, "%lld\n");
static int emc_debug_supported_rates_show(struct seq_file *s, void *data)
static int tegra_emc_debug_available_rates_show(struct seq_file *s,
void *data)
{
struct tegra_emc *emc = s->private;
const char *prefix = "";
unsigned int i;
for (i = 0; i < emc->num_timings; i++) {
struct emc_timing *timing = &emc->timings[i];
seq_printf(s, "%s%lu", prefix, timing->rate);
seq_printf(s, "%s%lu", prefix, emc->timings[i].rate);
prefix = " ";
}
......@@ -1038,46 +1059,126 @@ static int emc_debug_supported_rates_show(struct seq_file *s, void *data)
return 0;
}
static int emc_debug_supported_rates_open(struct inode *inode,
struct file *file)
static int tegra_emc_debug_available_rates_open(struct inode *inode,
struct file *file)
{
return single_open(file, emc_debug_supported_rates_show,
return single_open(file, tegra_emc_debug_available_rates_show,
inode->i_private);
}
static const struct file_operations emc_debug_supported_rates_fops = {
.open = emc_debug_supported_rates_open,
static const struct file_operations tegra_emc_debug_available_rates_fops = {
.open = tegra_emc_debug_available_rates_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int tegra_emc_debug_min_rate_get(void *data, u64 *rate)
{
struct tegra_emc *emc = data;
*rate = emc->debugfs.min_rate;
return 0;
}
static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
{
struct tegra_emc *emc = data;
int err;
if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL;
err = clk_set_min_rate(emc->clk, rate);
if (err < 0)
return err;
emc->debugfs.min_rate = rate;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_min_rate_fops,
tegra_emc_debug_min_rate_get,
tegra_emc_debug_min_rate_set, "%llu\n");
static int tegra_emc_debug_max_rate_get(void *data, u64 *rate)
{
struct tegra_emc *emc = data;
*rate = emc->debugfs.max_rate;
return 0;
}
static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
{
struct tegra_emc *emc = data;
int err;
if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL;
err = clk_set_max_rate(emc->clk, rate);
if (err < 0)
return err;
emc->debugfs.max_rate = rate;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_max_rate_fops,
tegra_emc_debug_max_rate_get,
tegra_emc_debug_max_rate_set, "%llu\n");
static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
{
struct dentry *root, *file;
struct clk *clk;
unsigned int i;
int err;
root = debugfs_create_dir("emc", NULL);
if (!root) {
dev_err(dev, "failed to create debugfs directory\n");
return;
emc->clk = devm_clk_get(dev, "emc");
if (IS_ERR(emc->clk)) {
if (PTR_ERR(emc->clk) != -ENODEV) {
dev_err(dev, "failed to get EMC clock: %ld\n",
PTR_ERR(emc->clk));
return;
}
}
clk = clk_get_sys("tegra-clk-debug", "emc");
if (IS_ERR(clk)) {
dev_err(dev, "failed to get debug clock: %ld\n", PTR_ERR(clk));
emc->debugfs.min_rate = ULONG_MAX;
emc->debugfs.max_rate = 0;
for (i = 0; i < emc->num_timings; i++) {
if (emc->timings[i].rate < emc->debugfs.min_rate)
emc->debugfs.min_rate = emc->timings[i].rate;
if (emc->timings[i].rate > emc->debugfs.max_rate)
emc->debugfs.max_rate = emc->timings[i].rate;
}
err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate,
emc->debugfs.max_rate);
if (err < 0) {
dev_err(dev, "failed to set rate range [%lu-%lu] for %pC\n",
emc->debugfs.min_rate, emc->debugfs.max_rate,
emc->clk);
return;
}
file = debugfs_create_file("rate", S_IRUGO | S_IWUSR, root, clk,
&emc_debug_rate_fops);
if (!file)
dev_err(dev, "failed to create debugfs entry\n");
emc->debugfs.root = debugfs_create_dir("emc", NULL);
if (!emc->debugfs.root) {
dev_err(dev, "failed to create debugfs directory\n");
return;
}
file = debugfs_create_file("supported_rates", S_IRUGO, root, emc,
&emc_debug_supported_rates_fops);
if (!file)
dev_err(dev, "failed to create debugfs entry\n");
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, emc,
&tegra_emc_debug_available_rates_fops);
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
emc, &tegra_emc_debug_min_rate_fops);
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
emc, &tegra_emc_debug_max_rate_fops);
}
static int tegra_emc_probe(struct platform_device *pdev)
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <soc/tegra/bpmp.h>
struct tegra186_emc_dvfs {
unsigned long latency;
unsigned long rate;
};
struct tegra186_emc {
struct tegra_bpmp *bpmp;
struct device *dev;
struct clk *clk;
struct tegra186_emc_dvfs *dvfs;
unsigned int num_dvfs;
struct {
struct dentry *root;
unsigned long min_rate;
unsigned long max_rate;
} debugfs;
};
/*
* debugfs interface
*
* The memory controller driver exposes some files in debugfs that can be used
* to control the EMC frequency. The top-level directory can be found here:
*
* /sys/kernel/debug/emc
*
* It contains the following files:
*
* - available_rates: This file contains a list of valid, space-separated
* EMC frequencies.
*
* - min_rate: Writing a value to this file sets the given frequency as the
* floor of the permitted range. If this is higher than the currently
* configured EMC frequency, this will cause the frequency to be
* increased so that it stays within the valid range.
*
* - max_rate: Similarily to the min_rate file, writing a value to this file
* sets the given frequency as the ceiling of the permitted range. If
* the value is lower than the currently configured EMC frequency, this
* will cause the frequency to be decreased so that it stays within the
* valid range.
*/
static bool tegra186_emc_validate_rate(struct tegra186_emc *emc,
unsigned long rate)
{
unsigned int i;
for (i = 0; i < emc->num_dvfs; i++)
if (rate == emc->dvfs[i].rate)
return true;
return false;
}
static int tegra186_emc_debug_available_rates_show(struct seq_file *s,
void *data)
{
struct tegra186_emc *emc = s->private;
const char *prefix = "";
unsigned int i;
for (i = 0; i < emc->num_dvfs; i++) {
seq_printf(s, "%s%lu", prefix, emc->dvfs[i].rate);
prefix = " ";
}
seq_puts(s, "\n");
return 0;
}
static int tegra186_emc_debug_available_rates_open(struct inode *inode,
struct file *file)
{
return single_open(file, tegra186_emc_debug_available_rates_show,
inode->i_private);
}
static const struct file_operations tegra186_emc_debug_available_rates_fops = {
.open = tegra186_emc_debug_available_rates_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int tegra186_emc_debug_min_rate_get(void *data, u64 *rate)
{
struct tegra186_emc *emc = data;
*rate = emc->debugfs.min_rate;
return 0;
}
static int tegra186_emc_debug_min_rate_set(void *data, u64 rate)
{
struct tegra186_emc *emc = data;
int err;
if (!tegra186_emc_validate_rate(emc, rate))
return -EINVAL;
err = clk_set_min_rate(emc->clk, rate);
if (err < 0)
return err;
emc->debugfs.min_rate = rate;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra186_emc_debug_min_rate_fops,
tegra186_emc_debug_min_rate_get,
tegra186_emc_debug_min_rate_set, "%llu\n");
static int tegra186_emc_debug_max_rate_get(void *data, u64 *rate)
{
struct tegra186_emc *emc = data;
*rate = emc->debugfs.max_rate;
return 0;
}
static int tegra186_emc_debug_max_rate_set(void *data, u64 rate)
{
struct tegra186_emc *emc = data;
int err;
if (!tegra186_emc_validate_rate(emc, rate))
return -EINVAL;
err = clk_set_max_rate(emc->clk, rate);
if (err < 0)
return err;
emc->debugfs.max_rate = rate;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra186_emc_debug_max_rate_fops,
tegra186_emc_debug_max_rate_get,
tegra186_emc_debug_max_rate_set, "%llu\n");
static int tegra186_emc_probe(struct platform_device *pdev)
{
struct mrq_emc_dvfs_latency_response response;
struct tegra_bpmp_message msg;
struct tegra186_emc *emc;
unsigned int i;
int err;
emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
if (!emc)
return -ENOMEM;
emc->bpmp = tegra_bpmp_get(&pdev->dev);
if (IS_ERR(emc->bpmp)) {
err = PTR_ERR(emc->bpmp);
if (err != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to get BPMP: %d\n", err);
return err;
}
emc->clk = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(emc->clk)) {
err = PTR_ERR(emc->clk);
dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err);
return err;
}
platform_set_drvdata(pdev, emc);
emc->dev = &pdev->dev;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_EMC_DVFS_LATENCY;
msg.tx.data = NULL;
msg.tx.size = 0;
msg.rx.data = &response;
msg.rx.size = sizeof(response);
err = tegra_bpmp_transfer(emc->bpmp, &msg);
if (err < 0) {
dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err);
return err;
}
emc->debugfs.min_rate = ULONG_MAX;
emc->debugfs.max_rate = 0;
emc->num_dvfs = response.num_pairs;
emc->dvfs = devm_kmalloc_array(&pdev->dev, emc->num_dvfs,
sizeof(*emc->dvfs), GFP_KERNEL);
if (!emc->dvfs)
return -ENOMEM;
dev_dbg(&pdev->dev, "%u DVFS pairs:\n", emc->num_dvfs);
for (i = 0; i < emc->num_dvfs; i++) {
emc->dvfs[i].rate = response.pairs[i].freq * 1000;
emc->dvfs[i].latency = response.pairs[i].latency;
if (emc->dvfs[i].rate < emc->debugfs.min_rate)
emc->debugfs.min_rate = emc->dvfs[i].rate;
if (emc->dvfs[i].rate > emc->debugfs.max_rate)
emc->debugfs.max_rate = emc->dvfs[i].rate;
dev_dbg(&pdev->dev, " %2u: %lu Hz -> %lu us\n", i,
emc->dvfs[i].rate, emc->dvfs[i].latency);
}
err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate,
emc->debugfs.max_rate);
if (err < 0) {
dev_err(&pdev->dev,
"failed to set rate range [%lu-%lu] for %pC\n",
emc->debugfs.min_rate, emc->debugfs.max_rate,
emc->clk);
return err;
}
emc->debugfs.root = debugfs_create_dir("emc", NULL);
if (!emc->debugfs.root) {
dev_err(&pdev->dev, "failed to create debugfs directory\n");
return 0;
}
debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
emc, &tegra186_emc_debug_available_rates_fops);
debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
emc, &tegra186_emc_debug_min_rate_fops);
debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
emc, &tegra186_emc_debug_max_rate_fops);
return 0;
}
static int tegra186_emc_remove(struct platform_device *pdev)
{
struct tegra186_emc *emc = platform_get_drvdata(pdev);
debugfs_remove_recursive(emc->debugfs.root);
tegra_bpmp_put(emc->bpmp);
return 0;
}
static const struct of_device_id tegra186_emc_of_match[] = {
#if defined(CONFIG_ARCH_TEGRA186_SOC)
{ .compatible = "nvidia,tegra186-emc" },
#endif
#if defined(CONFIG_ARCH_TEGRA194_SOC)
{ .compatible = "nvidia,tegra194-emc" },
#endif
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra186_emc_of_match);
static struct platform_driver tegra186_emc_driver = {
.driver = {
.name = "tegra186-emc",
.of_match_table = tegra186_emc_of_match,
.suppress_bind_attrs = true,
},
.probe = tegra186_emc_probe,
.remove = tegra186_emc_remove,
};
module_platform_driver(tegra186_emc_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 External Memory Controller driver");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
This diff is collapsed.
......@@ -436,7 +436,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x37c,
.shift = 0,
.mask = 0xff,
.def = 0x39,
.def = 0x7a,
},
}, {
.id = 0x4b,
......
This diff is collapsed.
......@@ -74,7 +74,7 @@ config FSL_XGMAC_MDIO
config UCC_GETH
tristate "Freescale QE Gigabit Ethernet"
depends on QUICC_ENGINE
depends on QUICC_ENGINE && PPC32
select FSL_PQ_MDIO
select PHYLIB
---help---
......
......@@ -84,8 +84,8 @@ static int uhdlc_init(struct ucc_hdlc_private *priv)
int ret, i;
void *bd_buffer;
dma_addr_t bd_dma_addr;
u32 riptr;
u32 tiptr;
s32 riptr;
s32 tiptr;
u32 gumr;
ut_info = priv->ut_info;
......@@ -195,7 +195,7 @@ static int uhdlc_init(struct ucc_hdlc_private *priv)
priv->ucc_pram_offset = qe_muram_alloc(sizeof(struct ucc_hdlc_param),
ALIGNMENT_OF_UCC_HDLC_PRAM);
if (IS_ERR_VALUE(priv->ucc_pram_offset)) {
if (priv->ucc_pram_offset < 0) {
dev_err(priv->dev, "Can not allocate MURAM for hdlc parameter.\n");
ret = -ENOMEM;
goto free_tx_bd;
......@@ -233,18 +233,23 @@ static int uhdlc_init(struct ucc_hdlc_private *priv)
/* Alloc riptr, tiptr */
riptr = qe_muram_alloc(32, 32);
if (IS_ERR_VALUE(riptr)) {
if (riptr < 0) {
dev_err(priv->dev, "Cannot allocate MURAM mem for Receive internal temp data pointer\n");
ret = -ENOMEM;
goto free_tx_skbuff;
}
tiptr = qe_muram_alloc(32, 32);
if (IS_ERR_VALUE(tiptr)) {
if (tiptr < 0) {
dev_err(priv->dev, "Cannot allocate MURAM mem for Transmit internal temp data pointer\n");
ret = -ENOMEM;
goto free_riptr;
}
if (riptr != (u16)riptr || tiptr != (u16)tiptr) {
dev_err(priv->dev, "MURAM allocation out of addressable range\n");
ret = -ENOMEM;
goto free_tiptr;
}
/* Set RIPTR, TIPTR */
iowrite16be(riptr, &priv->ucc_pram->riptr);
......@@ -623,8 +628,8 @@ static int ucc_hdlc_poll(struct napi_struct *napi, int budget)
if (howmany < budget) {
napi_complete_done(napi, howmany);
qe_setbits32(priv->uccf->p_uccm,
(UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16);
qe_setbits_be32(priv->uccf->p_uccm,
(UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16);
}
return howmany;
......@@ -730,8 +735,8 @@ static int uhdlc_open(struct net_device *dev)
static void uhdlc_memclean(struct ucc_hdlc_private *priv)
{
qe_muram_free(priv->ucc_pram->riptr);
qe_muram_free(priv->ucc_pram->tiptr);
qe_muram_free(ioread16be(&priv->ucc_pram->riptr));
qe_muram_free(ioread16be(&priv->ucc_pram->tiptr));
if (priv->rx_bd_base) {
dma_free_coherent(priv->dev,
......
......@@ -98,7 +98,7 @@ struct ucc_hdlc_private {
unsigned short tx_ring_size;
unsigned short rx_ring_size;
u32 ucc_pram_offset;
s32 ucc_pram_offset;
unsigned short encoding;
unsigned short parity;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment