Commit b22b6bea authored by Linus Torvalds's avatar Linus Torvalds

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

Pull ARM SoC driver updates from Arnd Bergmann:
 "The most noteworthy SoC driver changes this time include:

   - The TEE subsystem gains an in-kernel interface to access the TEE
     from device drivers.

   - The reset controller subsystem gains a driver for the Qualcomm
     Snapdragon 845 Power Domain Controller.

   - The Xilinx Zynq platform now has a firmware interface for its
     platform management unit. This contains a firmware "ioctl"
     interface that was a little controversial at first, but the version
     we merged solved that by not exposing arbitrary firmware calls to
     user space.

   - The Amlogic Meson platform gains a "canvas" driver that is used for
     video processing and shared between different high-level drivers.

  The rest is more of the usual, mostly related to SoC specific power
  management support and core drivers in drivers/soc:

   - Several Renesas SoCs (RZ/G1N, RZ/G2M, R-Car V3M, RZ/A2M) gain new
     features related to power and reset control.

   - The Mediatek mt8183 and mt6765 SoC platforms gain support for their
     respective power management chips.

   - A new driver for NXP i.MX8, which need a firmware interface for
     power management.

   - The SCPI firmware interface now contains support estimating power
     usage of performance states

   - The NVIDIA Tegra "pmc" driver gains a few new features, in
     particular a pinctrl interface for configuring the pads.

   - Lots of small changes for Qualcomm, in particular the "smem" device
     driver.

   - Some cleanups for the TI OMAP series related to their sysc
     controller.

  Additional cleanups and bugfixes in SoC specific drivers include the
  Meson, Keystone, NXP, AT91, Sunxi, Actions, and Tegra platforms"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (129 commits)
  firmware: tegra: bpmp: Implement suspend/resume support
  drivers: clk: Add ZynqMP clock driver
  dt-bindings: clock: Add bindings for ZynqMP clock driver
  firmware: xilinx: Add zynqmp IOCTL API for device control
  Documentation: xilinx: Add documentation for eemi APIs
  MAINTAINERS: imx: include drivers/firmware/imx path
  firmware: imx: add misc svc support
  firmware: imx: add SCU firmware driver support
  reset: Fix potential use-after-free in __of_reset_control_get()
  dt-bindings: arm: fsl: add scu binding doc
  soc: fsl: qbman: add interrupt coalesce changing APIs
  soc: fsl: bman_portals: defer probe after bman's probe
  soc: fsl: qbman: Use last response to determine valid bit
  soc: fsl: qbman: Add 64 bit DMA addressing requirement to QBMan
  soc: fsl: qbman: replace CPU 0 with any online CPU in hotplug handlers
  soc: fsl: qbman: Check if CPU is offline when initializing portals
  reset: qcom: PDC Global (Power Domain Controller) reset controller
  dt-bindings: reset: Add PDC Global binding for SDM845 SoCs
  reset: Grammar s/more then once/more than once/
  bus: ti-sysc: Just use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS
  ...
parents 53b7a3b7 c1a92909
NXP i.MX System Controller Firmware (SCFW)
--------------------------------------------------------------------
The System Controller Firmware (SCFW) is a low-level system function
which runs on a dedicated Cortex-M core to provide power, clock, and
resource management. It exists on some i.MX8 processors. e.g. i.MX8QM
(QM, QP), and i.MX8QX (QXP, DX).
The AP communicates with the SC using a multi-ported MU module found
in the LSIO subsystem. The current definition of this MU module provides
5 remote AP connections to the SC to support up to 5 execution environments
(TZ, HV, standard Linux, etc.). The SC side of this MU module interfaces
with the LSIO DSC IP bus. The SC firmware will communicate with this MU
using the MSI bus.
System Controller Device Node:
============================================================
The scu node with the following properties shall be under the /firmware/ node.
Required properties:
-------------------
- compatible: should be "fsl,imx-scu".
- mbox-names: should include "tx0", "tx1", "tx2", "tx3",
"rx0", "rx1", "rx2", "rx3".
- mboxes: List of phandle of 4 MU channels for tx and 4 MU channels
for rx. All 8 MU channels must be in the same MU instance.
Cross instances are not allowed. The MU instance can only
be one of LSIO MU0~M4 for imx8qxp and imx8qm. Users need
to make sure use the one which is not conflict with other
execution environments. e.g. ATF.
Note:
Channel 0 must be "tx0" or "rx0".
Channel 1 must be "tx1" or "rx1".
Channel 2 must be "tx2" or "rx2".
Channel 3 must be "tx3" or "rx3".
e.g.
mboxes = <&lsio_mu1 0 0
&lsio_mu1 0 1
&lsio_mu1 0 2
&lsio_mu1 0 3
&lsio_mu1 1 0
&lsio_mu1 1 1
&lsio_mu1 1 2
&lsio_mu1 1 3>;
See Documentation/devicetree/bindings/mailbox/fsl,mu.txt
for detailed mailbox binding.
i.MX SCU Client Device Node:
============================================================
Client nodes are maintained as children of the relevant IMX-SCU device node.
Power domain bindings based on SCU Message Protocol
------------------------------------------------------------
This binding for the SCU power domain providers uses the generic power
domain binding[2].
Required properties:
- compatible: Should be "fsl,scu-pd".
- #address-cells: Should be 1.
- #size-cells: Should be 0.
Required properties for power domain sub nodes:
- #power-domain-cells: Must be 0.
Optional Properties:
- reg: Resource ID of this power domain.
No exist means uncontrollable by user.
See detailed Resource ID list from:
include/dt-bindings/power/imx-rsrc.h
- power-domains: phandle pointing to the parent power domain.
Clock bindings based on SCU Message Protocol
------------------------------------------------------------
This binding uses the common clock binding[1].
Required properties:
- compatible: Should be "fsl,imx8qxp-clock".
- #clock-cells: Should be 1. Contains the Clock ID value.
- clocks: List of clock specifiers, must contain an entry for
each required entry in clock-names
- clock-names: Should include entries "xtal_32KHz", "xtal_24MHz"
The clock consumer should specify the desired clock by having the clock
ID in its "clocks" phandle cell.
See the full list of clock IDs from:
include/dt-bindings/clock/imx8qxp-clock.h
Pinctrl bindings based on SCU Message Protocol
------------------------------------------------------------
This binding uses the i.MX common pinctrl binding[3].
Required properties:
- compatible: Should be "fsl,imx8qxp-iomuxc".
Required properties for Pinctrl sub nodes:
- fsl,pins: Each entry consists of 3 integers which represents
the mux and config setting for one pin. The first 2
integers <pin_id mux_mode> are specified using a
PIN_FUNC_ID macro, which can be found in
<dt-bindings/pinctrl/pads-imx8qxp.h>.
The last integer CONFIG is the pad setting value like
pull-up on this pin.
Please refer to i.MX8QXP Reference Manual for detailed
CONFIG settings.
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Documentation/devicetree/bindings/power/power_domain.txt
[3] Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
Example (imx8qxp):
-------------
lsio_mu1: mailbox@5d1c0000 {
...
#mbox-cells = <2>;
};
firmware {
scu {
compatible = "fsl,imx-scu";
mbox-names = "tx0", "tx1", "tx2", "tx3",
"rx0", "rx1", "rx2", "rx3";
mboxes = <&lsio_mu1 0 0
&lsio_mu1 0 1
&lsio_mu1 0 2
&lsio_mu1 0 3
&lsio_mu1 1 0
&lsio_mu1 1 1
&lsio_mu1 1 2
&lsio_mu1 1 3>;
clk: clk {
compatible = "fsl,imx8qxp-clk";
#clock-cells = <1>;
};
iomuxc {
compatible = "fsl,imx8qxp-iomuxc";
pinctrl_lpuart0: lpuart0grp {
fsl,pins = <
SC_P_UART0_RX_ADMA_UART0_RX 0x06000020
SC_P_UART0_TX_ADMA_UART0_TX 0x06000020
>;
};
...
};
imx8qx-pm {
compatible = "fsl,scu-pd";
#address-cells = <1>;
#size-cells = <0>;
pd_dma: dma-power-domain {
#power-domain-cells = <0>;
pd_dma_lpuart0: dma-lpuart0@57 {
reg = <SC_R_UART_0>;
#power-domain-cells = <0>;
power-domains = <&pd_dma>;
};
...
};
...
};
};
};
serial@5a060000 {
...
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lpuart0>;
clocks = <&clk IMX8QXP_UART0_CLK>,
<&clk IMX8QXP_UART0_IPG_CLK>;
clock-names = "per", "ipg";
power-domains = <&pd_dma_lpuart0>;
};
...@@ -45,11 +45,15 @@ Optional Properties: ...@@ -45,11 +45,15 @@ Optional Properties:
debug_messages - Map the Debug message region debug_messages - Map the Debug message region
- reg: register space corresponding to the debug_messages - reg: register space corresponding to the debug_messages
- ti,system-reboot-controller: If system reboot can be triggered by SoC reboot - ti,system-reboot-controller: If system reboot can be triggered by SoC reboot
- ti,host-id: Integer value corresponding to the host ID assigned by Firmware
for identification of host processing entities such as virtual
machines
Example (K2G): Example (K2G):
------------- -------------
pmmc: pmmc { pmmc: pmmc {
compatible = "ti,k2g-sci"; compatible = "ti,k2g-sci";
ti,host-id = <2>;
mbox-names = "rx", "tx"; mbox-names = "rx", "tx";
mboxes= <&msgmgr &msgmgr_proxy_pmmc_rx>, mboxes= <&msgmgr &msgmgr_proxy_pmmc_rx>,
<&msgmgr &msgmgr_proxy_pmmc_tx>; <&msgmgr &msgmgr_proxy_pmmc_tx>;
......
...@@ -16,11 +16,26 @@ Properties: ...@@ -16,11 +16,26 @@ Properties:
- reg: - reg:
Usage: required Usage: required
Value Type: <prop-encoded-array> Value Type: <prop-encoded-array>
Definition: Start address and the the size of the register region. Definition: The first element specifies the llcc base start address and
the size of the register region. The second element specifies
the llcc broadcast base address and size of the register region.
- reg-names:
Usage: required
Value Type: <stringlist>
Definition: Register region names. Must be "llcc_base", "llcc_broadcast_base".
- interrupts:
Usage: required
Definition: The interrupt is associated with the llcc edac device.
It's used for llcc cache single and double bit error detection
and reporting.
Example: Example:
cache-controller@1100000 { cache-controller@1100000 {
compatible = "qcom,sdm845-llcc"; compatible = "qcom,sdm845-llcc";
reg = <0x1100000 0x250000>; reg = <0x1100000 0x200000>, <0x1300000 0x50000> ;
reg-names = "llcc_base", "llcc_broadcast_base";
interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>;
}; };
...@@ -7,16 +7,23 @@ assorted actions. ...@@ -7,16 +7,23 @@ assorted actions.
Required properties: Required properties:
- compatible: must contain one of the following: - compatible: must contain one of the following:
* "qcom,scm-apq8064" for APQ8064 platforms * "qcom,scm-apq8064"
* "qcom,scm-msm8660" for MSM8660 platforms * "qcom,scm-apq8084"
* "qcom,scm-msm8690" for MSM8690 platforms * "qcom,scm-msm8660"
* "qcom,scm-msm8996" for MSM8996 platforms * "qcom,scm-msm8916"
* "qcom,scm-ipq4019" for IPQ4019 platforms * "qcom,scm-msm8960"
* "qcom,scm" for later processors (MSM8916, APQ8084, MSM8974, etc) * "qcom,scm-msm8974"
- clocks: One to three clocks may be required based on compatible. * "qcom,scm-msm8996"
* No clock required for "qcom,scm-msm8996", "qcom,scm-ipq4019" * "qcom,scm-msm8998"
* Only core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660", and "qcom,scm-msm8960" * "qcom,scm-ipq4019"
* Core, iface, and bus clocks required for "qcom,scm" * "qcom,scm-sdm845"
and:
* "qcom,scm"
- clocks: Specifies clocks needed by the SCM interface, if any:
* core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and
"qcom,scm-msm8960"
* core, iface and bus clocks required for "qcom,scm-apq8084",
"qcom,scm-msm8916" and "qcom,scm-msm8974"
- clock-names: Must contain "core" for the core clock, "iface" for the interface - clock-names: Must contain "core" for the core clock, "iface" for the interface
clock and "bus" for the bus clock per the requirements of the compatible. clock and "bus" for the bus clock per the requirements of the compatible.
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the - qcom,dload-mode: phandle to the TCSR hardware block and offset of the
...@@ -26,8 +33,10 @@ Example for MSM8916: ...@@ -26,8 +33,10 @@ Example for MSM8916:
firmware { firmware {
scm { scm {
compatible = "qcom,scm"; compatible = "qcom,msm8916", "qcom,scm";
clocks = <&gcc GCC_CRYPTO_CLK> , <&gcc GCC_CRYPTO_AXI_CLK>, <&gcc GCC_CRYPTO_AHB_CLK>; clocks = <&gcc GCC_CRYPTO_CLK> ,
<&gcc GCC_CRYPTO_AXI_CLK>,
<&gcc GCC_CRYPTO_AHB_CLK>;
clock-names = "core", "bus", "iface"; clock-names = "core", "bus", "iface";
}; };
}; };
-----------------------------------------------------------------
Device Tree Bindings for the Xilinx Zynq MPSoC Firmware Interface
-----------------------------------------------------------------
The zynqmp-firmware node describes the interface to platform firmware.
ZynqMP has an interface to communicate with secure firmware. Firmware
driver provides an interface to firmware APIs. Interface APIs can be
used by any driver to communicate to PMUFW(Platform Management Unit).
These requests include clock management, pin control, device control,
power management service, FPGA service and other platform management
services.
Required properties:
- compatible: Must contain: "xlnx,zynqmp-firmware"
- method: The method of calling the PM-API firmware layer.
Permitted values are:
- "smc" : SMC #0, following the SMCCC
- "hvc" : HVC #0, following the SMCCC
--------------------------------------------------------------------------
Device Tree Clock bindings for the Zynq Ultrascale+ MPSoC controlled using
Zynq MPSoC firmware interface
--------------------------------------------------------------------------
The clock controller is a h/w block of Zynq Ultrascale+ MPSoC clock
tree. It reads required input clock frequencies from the devicetree and acts
as clock provider for all clock consumers of PS clocks.
See clock_bindings.txt for more information on the generic clock bindings.
Required properties:
- #clock-cells: Must be 1
- compatible: Must contain: "xlnx,zynqmp-clk"
- clocks: List of clock specifiers which are external input
clocks to the given clock controller. Please refer
the next section to find the input clocks for a
given controller.
- clock-names: List of clock names which are exteral input clocks
to the given clock controller. Please refer to the
clock bindings for more details.
Input clocks for zynqmp Ultrascale+ clock controller:
The Zynq UltraScale+ MPSoC has one primary and four alternative reference clock
inputs. These required clock inputs are:
- pss_ref_clk (PS reference clock)
- video_clk (reference clock for video system )
- pss_alt_ref_clk (alternative PS reference clock)
- aux_ref_clk
- gt_crx_ref_clk (transceiver reference clock)
The following strings are optional parameters to the 'clock-names' property in
order to provide an optional (E)MIO clock source:
- swdt0_ext_clk
- swdt1_ext_clk
- gem0_emio_clk
- gem1_emio_clk
- gem2_emio_clk
- gem3_emio_clk
- mio_clk_XX # with XX = 00..77
- mio_clk_50_or_51 #for the mux clock to gem tsu from 50 or 51
Output clocks are registered based on clock information received
from firmware. Output clocks indexes are mentioned in
include/dt-bindings/clock/xlnx,zynqmp-clk.h.
-------
Example
-------
firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
zynqmp_clk: clock-controller {
#clock-cells = <1>;
compatible = "xlnx,zynqmp-clk";
clocks = <&pss_ref_clk>, <&video_clk>, <&pss_alt_ref_clk>, <&aux_ref_clk>, <&gt_crx_ref_clk>;
clock-names = "pss_ref_clk", "video_clk", "pss_alt_ref_clk","aux_ref_clk", "gt_crx_ref_clk";
};
};
};
...@@ -8,7 +8,9 @@ Required properties: ...@@ -8,7 +8,9 @@ Required properties:
- compatible: Should be "renesas,<soctype>-apmu", "renesas,apmu" as fallback. - compatible: Should be "renesas,<soctype>-apmu", "renesas,apmu" as fallback.
Examples with soctypes are: Examples with soctypes are:
- "renesas,r8a7743-apmu" (RZ/G1M) - "renesas,r8a7743-apmu" (RZ/G1M)
- "renesas,r8a7744-apmu" (RZ/G1N)
- "renesas,r8a7745-apmu" (RZ/G1E) - "renesas,r8a7745-apmu" (RZ/G1E)
- "renesas,r8a77470-apmu" (RZ/G1C)
- "renesas,r8a7790-apmu" (R-Car H2) - "renesas,r8a7790-apmu" (R-Car H2)
- "renesas,r8a7791-apmu" (R-Car M2-W) - "renesas,r8a7791-apmu" (R-Car M2-W)
- "renesas,r8a7792-apmu" (R-Car V2H) - "renesas,r8a7792-apmu" (R-Car V2H)
......
...@@ -8,8 +8,11 @@ and various coprocessors. ...@@ -8,8 +8,11 @@ and various coprocessors.
Required properties: Required properties:
- compatible: Must contain exactly one of the following: - compatible: Must contain exactly one of the following:
- "renesas,r8a7743-sysc" (RZ/G1M) - "renesas,r8a7743-sysc" (RZ/G1M)
- "renesas,r8a7744-sysc" (RZ/G1N)
- "renesas,r8a7745-sysc" (RZ/G1E) - "renesas,r8a7745-sysc" (RZ/G1E)
- "renesas,r8a77470-sysc" (RZ/G1C) - "renesas,r8a77470-sysc" (RZ/G1C)
- "renesas,r8a774a1-sysc" (RZ/G2M)
- "renesas,r8a774c0-sysc" (RZ/G2E)
- "renesas,r8a7779-sysc" (R-Car H1) - "renesas,r8a7779-sysc" (R-Car H1)
- "renesas,r8a7790-sysc" (R-Car H2) - "renesas,r8a7790-sysc" (R-Car H2)
- "renesas,r8a7791-sysc" (R-Car M2-W) - "renesas,r8a7791-sysc" (R-Car M2-W)
......
PDC Global
======================================
This binding describes a reset-controller found on PDC-Global (Power Domain
Controller) block for Qualcomm Technologies Inc SDM845 SoCs.
Required properties:
- compatible:
Usage: required
Value type: <string>
Definition: must be:
"qcom,sdm845-pdc-global"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: must specify the base address and size of the register
space.
- #reset-cells:
Usage: required
Value type: <uint>
Definition: must be 1; cell entry represents the reset index.
Example:
pdc_reset: reset-controller@b2e0000 {
compatible = "qcom,sdm845-pdc-global";
reg = <0xb2e0000 0x20000>;
#reset-cells = <1>;
};
PDC reset clients
======================================
Device nodes that need access to reset lines should
specify them as a reset phandle in their corresponding node as
specified in reset.txt.
For a list of all valid reset indices see
<dt-bindings/reset/qcom,sdm845-pdc.h>
Example:
modem-pil@4080000 {
...
resets = <&pdc_reset PDC_MODEM_SYNC_RESET>;
reset-names = "pdc_reset";
...
};
...@@ -16,8 +16,11 @@ Required properties: ...@@ -16,8 +16,11 @@ Required properties:
- "renesas,<soctype>-rst" for R-Car Gen2 and Gen3, and RZ/G - "renesas,<soctype>-rst" for R-Car Gen2 and Gen3, and RZ/G
Examples with soctypes are: Examples with soctypes are:
- "renesas,r8a7743-rst" (RZ/G1M) - "renesas,r8a7743-rst" (RZ/G1M)
- "renesas,r8a7744-rst" (RZ/G1N)
- "renesas,r8a7745-rst" (RZ/G1E) - "renesas,r8a7745-rst" (RZ/G1E)
- "renesas,r8a77470-rst" (RZ/G1C) - "renesas,r8a77470-rst" (RZ/G1C)
- "renesas,r8a774a1-rst" (RZ/G2M)
- "renesas,r8a774c0-rst" (RZ/G2E)
- "renesas,r8a7778-reset-wdt" (R-Car M1A) - "renesas,r8a7778-reset-wdt" (R-Car M1A)
- "renesas,r8a7779-reset-wdt" (R-Car H1) - "renesas,r8a7779-reset-wdt" (R-Car H1)
- "renesas,r8a7790-rst" (R-Car H2) - "renesas,r8a7790-rst" (R-Car H2)
......
Amlogic Canvas
================================
A canvas is a collection of metadata that describes a pixel buffer.
Those metadata include: width, height, phyaddr, wrapping, block mode
and endianness.
Many IPs within Amlogic SoCs rely on canvas indexes to read/write pixel data
rather than use the phy addresses directly. For instance, this is the case for
the video decoders and the display.
Amlogic SoCs have 256 canvas.
Device Tree Bindings:
---------------------
Video Lookup Table
--------------------------
Required properties:
- compatible: "amlogic,canvas"
- reg: Base physical address and size of the canvas registers.
Example:
canvas: video-lut@48 {
compatible = "amlogic,canvas";
reg = <0x0 0x48 0x0 0x14>;
};
...@@ -19,10 +19,12 @@ IP Pairing ...@@ -19,10 +19,12 @@ IP Pairing
Required properties in pwrap device node. Required properties in pwrap device node.
- compatible: - compatible:
"mediatek,mt2701-pwrap" for MT2701/7623 SoCs "mediatek,mt2701-pwrap" for MT2701/7623 SoCs
"mediatek,mt6765-pwrap" for MT6765 SoCs
"mediatek,mt6797-pwrap" for MT6797 SoCs "mediatek,mt6797-pwrap" for MT6797 SoCs
"mediatek,mt7622-pwrap" for MT7622 SoCs "mediatek,mt7622-pwrap" for MT7622 SoCs
"mediatek,mt8135-pwrap" for MT8135 SoCs "mediatek,mt8135-pwrap" for MT8135 SoCs
"mediatek,mt8173-pwrap" for MT8173 SoCs "mediatek,mt8173-pwrap" for MT8173 SoCs
"mediatek,mt8183-pwrap" for MT8183 SoCs
- interrupts: IRQ for pwrap in SOC - interrupts: IRQ for pwrap in SOC
- reg-names: Must include the following entries: - reg-names: Must include the following entries:
"pwrap": Main registers base "pwrap": Main registers base
......
...@@ -18,6 +18,7 @@ Required properties: ...@@ -18,6 +18,7 @@ Required properties:
- "allwinner,sun8i-h3-system-control" - "allwinner,sun8i-h3-system-control"
- "allwinner,sun50i-a64-sram-controller" (deprecated) - "allwinner,sun50i-a64-sram-controller" (deprecated)
- "allwinner,sun50i-a64-system-control" - "allwinner,sun50i-a64-system-control"
- "allwinner,sun50i-h6-system-control", "allwinner,sun50i-a64-system-control"
- reg : sram controller register offset + length - reg : sram controller register offset + length
SRAM nodes SRAM nodes
...@@ -54,6 +55,9 @@ The valid sections compatible for H3 are: ...@@ -54,6 +55,9 @@ The valid sections compatible for H3 are:
The valid sections compatible for A64 are: The valid sections compatible for A64 are:
- allwinner,sun50i-a64-sram-c - allwinner,sun50i-a64-sram-c
The valid sections compatible for H6 are:
- allwinner,sun50i-h6-sram-c, allwinner,sun50i-a64-sram-c
Devices using SRAM sections Devices using SRAM sections
--------------------------- ---------------------------
......
...@@ -12,6 +12,8 @@ Required Properties: ...@@ -12,6 +12,8 @@ Required Properties:
- "renesas,tmu-r8a7740" for the r8a7740 TMU - "renesas,tmu-r8a7740" for the r8a7740 TMU
- "renesas,tmu-r8a7778" for the r8a7778 TMU - "renesas,tmu-r8a7778" for the r8a7778 TMU
- "renesas,tmu-r8a7779" for the r8a7779 TMU - "renesas,tmu-r8a7779" for the r8a7779 TMU
- "renesas,tmu-r8a77970" for the r8a77970 TMU
- "renesas,tmu-r8a77980" for the r8a77980 TMU
- "renesas,tmu" for any TMU. - "renesas,tmu" for any TMU.
This is a fallback for the above renesas,tmu-* entries This is a fallback for the above renesas,tmu-* entries
......
---------------------------------------------------------------------
Xilinx Zynq MPSoC EEMI Documentation
---------------------------------------------------------------------
Xilinx Zynq MPSoC Firmware Interface
-------------------------------------
The zynqmp-firmware node describes the interface to platform firmware.
ZynqMP has an interface to communicate with secure firmware. Firmware
driver provides an interface to firmware APIs. Interface APIs can be
used by any driver to communicate with PMC(Platform Management Controller).
Embedded Energy Management Interface (EEMI)
----------------------------------------------
The embedded energy management interface is used to allow software
components running across different processing clusters on a chip or
device to communicate with a power management controller (PMC) on a
device to issue or respond to power management requests.
EEMI ops is a structure containing all eemi APIs supported by Zynq MPSoC.
The zynqmp-firmware driver maintain all EEMI APIs in zynqmp_eemi_ops
structure. Any driver who want to communicate with PMC using EEMI APIs
can call zynqmp_pm_get_eemi_ops().
Example of EEMI ops:
/* zynqmp-firmware driver maintain all EEMI APIs */
struct zynqmp_eemi_ops {
int (*get_api_version)(u32 *version);
int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
};
static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
.query_data = zynqmp_pm_query_data,
};
Example of EEMI ops usage:
static const struct zynqmp_eemi_ops *eemi_ops;
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
eemi_ops = zynqmp_pm_get_eemi_ops();
if (!eemi_ops)
return -ENXIO;
ret = eemi_ops->query_data(qdata, ret_payload);
IOCTL
------
IOCTL API is for device control and configuration. It is not a system
IOCTL but it is an EEMI API. This API can be used by master to control
any device specific configuration. IOCTL definitions can be platform
specific. This API also manage shared device configuration.
The following IOCTL IDs are valid for device control:
- IOCTL_SET_PLL_FRAC_MODE 8
- IOCTL_GET_PLL_FRAC_MODE 9
- IOCTL_SET_PLL_FRAC_DATA 10
- IOCTL_GET_PLL_FRAC_DATA 11
Refer EEMI API guide [0] for IOCTL specific parameters and other EEMI APIs.
References
----------
[0] Embedded Energy Management Interface (EEMI) API guide:
https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf
...@@ -1471,7 +1471,9 @@ F: arch/arm/mach-mxs/ ...@@ -1471,7 +1471,9 @@ F: arch/arm/mach-mxs/
F: arch/arm/boot/dts/imx* F: arch/arm/boot/dts/imx*
F: arch/arm/configs/imx*_defconfig F: arch/arm/configs/imx*_defconfig
F: drivers/clk/imx/ F: drivers/clk/imx/
F: drivers/firmware/imx/
F: drivers/soc/imx/ F: drivers/soc/imx/
F: include/linux/firmware/imx/
F: include/soc/imx/ F: include/soc/imx/
ARM/FREESCALE VYBRID ARM ARCHITECTURE ARM/FREESCALE VYBRID ARM ARCHITECTURE
...@@ -5401,6 +5403,14 @@ L: linux-edac@vger.kernel.org ...@@ -5401,6 +5403,14 @@ L: linux-edac@vger.kernel.org
S: Maintained S: Maintained
F: drivers/edac/ti_edac.c F: drivers/edac/ti_edac.c
EDAC-QCOM
M: Channagoud Kadabi <ckadabi@codeaurora.org>
M: Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org>
L: linux-arm-msm@vger.kernel.org
L: linux-edac@vger.kernel.org
S: Maintained
F: drivers/edac/qcom_edac.c
EDIROL UA-101/UA-1000 DRIVER EDIROL UA-101/UA-1000 DRIVER
M: Clemens Ladisch <clemens@ladisch.de> M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
......
...@@ -55,6 +55,12 @@ config ARCH_R7S72100 ...@@ -55,6 +55,12 @@ config ARCH_R7S72100
select SYS_SUPPORTS_SH_MTU2 select SYS_SUPPORTS_SH_MTU2
select RENESAS_OSTM select RENESAS_OSTM
config ARCH_R7S9210
bool "RZ/A2 (R7S9210)"
select PM
select PM_GENERIC_DOMAINS
select RENESAS_OSTM
config ARCH_R8A73A4 config ARCH_R8A73A4
bool "R-Mobile APE6 (R8A73A40)" bool "R-Mobile APE6 (R8A73A40)"
select ARCH_RMOBILE select ARCH_RMOBILE
......
...@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o
obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o
obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o
obj-$(CONFIG_ARCH_R7S72100) += setup-r7s72100.o obj-$(CONFIG_ARCH_R7S72100) += setup-r7s72100.o
obj-$(CONFIG_ARCH_R7S9210) += setup-r7s9210.o
# CPU reset vector handling objects # CPU reset vector handling objects
cpu-y := platsmp.o headsmp.o cpu-y := platsmp.o headsmp.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* r7s9210 processor support
*
* Copyright (C) 2018 Renesas Electronics Corporation
* Copyright (C) 2018 Chris Brandt
*
*/
#include <linux/kernel.h>
#include <asm/mach/arch.h>
#include "common.h"
static const char *const r7s9210_boards_compat_dt[] __initconst = {
"renesas,r7s9210",
NULL,
};
DT_MACHINE_START(R7S72100_DT, "Generic R7S9210 (Flattened Device Tree)")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
.init_early = shmobile_init_delay,
.init_late = shmobile_init_late,
.dt_compat = r7s9210_boards_compat_dt,
MACHINE_END
...@@ -302,6 +302,7 @@ config ARCH_ZX ...@@ -302,6 +302,7 @@ config ARCH_ZX
config ARCH_ZYNQMP config ARCH_ZYNQMP
bool "Xilinx ZynqMP Family" bool "Xilinx ZynqMP Family"
select ZYNQMP_FIRMWARE
help help
This enables support for Xilinx ZynqMP Family This enables support for Xilinx ZynqMP Family
......
...@@ -156,9 +156,6 @@ static int __init weim_parse_dt(struct platform_device *pdev, ...@@ -156,9 +156,6 @@ static int __init weim_parse_dt(struct platform_device *pdev,
} }
for_each_available_child_of_node(pdev->dev.of_node, child) { for_each_available_child_of_node(pdev->dev.of_node, child) {
if (!child->name)
continue;
ret = weim_timing_setup(child, base, devtype); ret = weim_timing_setup(child, base, devtype);
if (ret) if (ret)
dev_warn(&pdev->dev, "%pOF set timing failed.\n", dev_warn(&pdev->dev, "%pOF set timing failed.\n",
......
...@@ -701,69 +701,7 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) ...@@ -701,69 +701,7 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
return error; return error;
} }
#ifdef CONFIG_PM_SLEEP static int __maybe_unused sysc_noirq_suspend(struct device *dev)
static int sysc_suspend(struct device *dev)
{
struct sysc *ddata;
int error;
ddata = dev_get_drvdata(dev);
if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER |
SYSC_QUIRK_LEGACY_IDLE))
return 0;
if (!ddata->enabled)
return 0;
dev_dbg(ddata->dev, "%s %s\n", __func__,
ddata->name ? ddata->name : "");
error = pm_runtime_put_sync_suspend(dev);
if (error < 0) {
dev_warn(ddata->dev, "%s not idle %i %s\n",
__func__, error,
ddata->name ? ddata->name : "");
return 0;
}
ddata->needs_resume = true;
return 0;
}
static int sysc_resume(struct device *dev)
{
struct sysc *ddata;
int error;
ddata = dev_get_drvdata(dev);
if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER |
SYSC_QUIRK_LEGACY_IDLE))
return 0;
if (ddata->needs_resume) {
dev_dbg(ddata->dev, "%s %s\n", __func__,
ddata->name ? ddata->name : "");
error = pm_runtime_get_sync(dev);
if (error < 0) {
dev_err(ddata->dev, "%s error %i %s\n",
__func__, error,
ddata->name ? ddata->name : "");
return error;
}
ddata->needs_resume = false;
}
return 0;
}
static int sysc_noirq_suspend(struct device *dev)
{ {
struct sysc *ddata; struct sysc *ddata;
...@@ -772,21 +710,10 @@ static int sysc_noirq_suspend(struct device *dev) ...@@ -772,21 +710,10 @@ static int sysc_noirq_suspend(struct device *dev)
if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
return 0; return 0;
if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER)) return pm_runtime_force_suspend(dev);
return 0;
if (!ddata->enabled)
return 0;
dev_dbg(ddata->dev, "%s %s\n", __func__,
ddata->name ? ddata->name : "");
ddata->needs_resume = true;
return sysc_runtime_suspend(dev);
} }
static int sysc_noirq_resume(struct device *dev) static int __maybe_unused sysc_noirq_resume(struct device *dev)
{ {
struct sysc *ddata; struct sysc *ddata;
...@@ -795,24 +722,10 @@ static int sysc_noirq_resume(struct device *dev) ...@@ -795,24 +722,10 @@ static int sysc_noirq_resume(struct device *dev)
if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
return 0; return 0;
if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER)) return pm_runtime_force_resume(dev);
return 0;
if (ddata->needs_resume) {
dev_dbg(ddata->dev, "%s %s\n", __func__,
ddata->name ? ddata->name : "");
ddata->needs_resume = false;
return sysc_runtime_resume(dev);
}
return 0;
} }
#endif
static const struct dev_pm_ops sysc_pm_ops = { static const struct dev_pm_ops sysc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sysc_suspend, sysc_resume)
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_noirq_suspend, sysc_noirq_resume) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_noirq_suspend, sysc_noirq_resume)
SET_RUNTIME_PM_OPS(sysc_runtime_suspend, SET_RUNTIME_PM_OPS(sysc_runtime_suspend,
sysc_runtime_resume, sysc_runtime_resume,
...@@ -845,28 +758,8 @@ struct sysc_revision_quirk { ...@@ -845,28 +758,8 @@ struct sysc_revision_quirk {
} }
static const struct sysc_revision_quirk sysc_revision_quirks[] = { static const struct sysc_revision_quirk sysc_revision_quirks[] = {
/* These need to use noirq_suspend */
SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff,
SYSC_QUIRK_RESOURCE_PROVIDER),
/* These drivers need to be fixed to not use pm_runtime_irq_safe() */ /* These drivers need to be fixed to not use pm_runtime_irq_safe() */
SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff, SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff00ff,
SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET), SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff, SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_LEGACY_IDLE),
...@@ -881,38 +774,70 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { ...@@ -881,38 +774,70 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff, SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_LEGACY_IDLE),
/* Some timers on omap4 and later */ /* Some timers on omap4 and later */
SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x4fff1301, 0xffffffff, SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x50002100, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x4fff1301, 0xffff00ff,
SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_LEGACY_IDLE),
/* Uarts on omap4 and later */ /* Uarts on omap4 and later */
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffffffff, SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
/* These devices don't yet suspend properly without legacy setting */
SYSC_QUIRK("sdio", 0, 0, 0x10, -1, 0x40202301, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0d00, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_LEGACY_IDLE),
#ifdef DEBUG #ifdef DEBUG
SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0),
SYSC_QUIRK("atl", 0, 0, -1, -1, 0x0a070100, 0xffffffff, 0),
SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff, 0), SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff, 0),
SYSC_QUIRK("cm", 0, 0, -1, -1, 0x40000301, 0xffffffff, 0),
SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0),
SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902,
0xffff00f0, 0),
SYSC_QUIRK("dcan", 0, 0, -1, -1, 0xffffffff, 0xffffffff, 0),
SYSC_QUIRK("dcan", 0, 0, -1, -1, 0x00001401, 0xffffffff, 0),
SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0),
SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0),
SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0), SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, 0), SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, 0),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, 0),
SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0), SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0),
SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0), SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0),
SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0, 0),
SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, 0),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0), SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44307b02, 0xffffffff, 0),
SYSC_QUIRK("mcbsp", 0, -1, 0x8c, -1, 0, 0, 0), SYSC_QUIRK("mcbsp", 0, -1, 0x8c, -1, 0, 0, 0),
SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffff00ff, 0),
SYSC_QUIRK("mcspi", 0, 0, 0x110, 0x114, 0x40300a0b, 0xffffffff, 0),
SYSC_QUIRK("mailbox", 0, 0, 0x10, -1, 0x00000400, 0xffffffff, 0), SYSC_QUIRK("mailbox", 0, 0, 0x10, -1, 0x00000400, 0xffffffff, 0),
SYSC_QUIRK("m3", 0, 0, -1, -1, 0x5f580105, 0x0fff0f00, 0),
SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xfffffff0, 0),
SYSC_QUIRK("ocp2scp", 0, 0, -1, -1, 0x50060007, 0xffffffff, 0),
SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff, 0),
SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff, 0),
SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x00004102, 0xffffffff, 0),
SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000400, 0xffffffff, 0),
SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0),
SYSC_QUIRK("scm", 0, 0, -1, -1, 0x4e8b0100, 0xffffffff, 0),
SYSC_QUIRK("scm", 0, 0, -1, -1, 0x4f000100, 0xffffffff, 0),
SYSC_QUIRK("scm", 0, 0, -1, -1, 0x40000900, 0xffffffff, 0),
SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff, 0),
SYSC_QUIRK("sdio", 0, 0, 0x10, -1, 0x40202301, 0xffff0ff0, 0),
SYSC_QUIRK("sdio", 0, 0x2fc, 0x110, 0x114, 0x31010000, 0xffffffff, 0),
SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0),
SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40000902, 0xffffffff, 0), SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40000902, 0xffffffff, 0),
SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40002903, 0xffffffff, 0), SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40002903, 0xffffffff, 0),
SYSC_QUIRK("spinlock", 0, 0, 0x10, -1, 0x50020000, 0xffffffff, 0), SYSC_QUIRK("spinlock", 0, 0, 0x10, -1, 0x50020000, 0xffffffff, 0),
SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -1, 0x00000020, 0xffffffff, 0),
SYSC_QUIRK("rtc", 0, 0x74, 0x78, -1, 0x4eb01908, 0xffff00f0, 0),
SYSC_QUIRK("timer32k", 0, 0, 0x4, -1, 0x00000060, 0xffffffff, 0),
SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0), SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0), SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, 0), 0xffffffff, 0),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 0),
SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0),
#endif #endif
}; };
...@@ -1221,8 +1146,8 @@ static int sysc_child_suspend_noirq(struct device *dev) ...@@ -1221,8 +1146,8 @@ static int sysc_child_suspend_noirq(struct device *dev)
if (!pm_runtime_status_suspended(dev)) { if (!pm_runtime_status_suspended(dev)) {
error = pm_generic_runtime_suspend(dev); error = pm_generic_runtime_suspend(dev);
if (error) { if (error) {
dev_warn(dev, "%s busy at %i: %i\n", dev_dbg(dev, "%s busy at %i: %i\n",
__func__, __LINE__, error); __func__, __LINE__, error);
return 0; return 0;
} }
......
...@@ -299,5 +299,6 @@ source "drivers/clk/sunxi-ng/Kconfig" ...@@ -299,5 +299,6 @@ source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig" source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig" source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig" source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/zynqmp/Kconfig"
endmenu endmenu
...@@ -108,3 +108,4 @@ obj-$(CONFIG_X86) += x86/ ...@@ -108,3 +108,4 @@ obj-$(CONFIG_X86) += x86/
endif endif
obj-$(CONFIG_ARCH_ZX) += zte/ obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/ obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/
# SPDX-License-Identifier: GPL-2.0
config COMMON_CLK_ZYNQMP
bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
depends on ARCH_ZYNQMP || COMPILE_TEST
depends on ZYNQMP_FIRMWARE
help
Support for the Zynqmp Ultrascale clock controller.
It has a dependency on the PMU firmware.
Say Y if you want to include clock support.
# SPDX-License-Identifier: GPL-2.0
# Zynq Ultrascale+ MPSoC clock specific Makefile
obj-$(CONFIG_ARCH_ZYNQMP) += pll.o clk-gate-zynqmp.o divider.o clk-mux-zynqmp.o clkc.o
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC clock controller
*
* Copyright (C) 2016-2018 Xilinx
*
* Gated clock implementation
*/
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-zynqmp.h"
/**
* struct clk_gate - gating clock
* @hw: handle between common and hardware-specific interfaces
* @flags: hardware-specific flags
* @clk_id: Id of clock
*/
struct zynqmp_clk_gate {
struct clk_hw hw;
u8 flags;
u32 clk_id;
};
#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw)
/**
* zynqmp_clk_gate_enable() - Enable clock
* @hw: handle between common and hardware-specific interfaces
*
* Return: 0 on success else error code
*/
static int zynqmp_clk_gate_enable(struct clk_hw *hw)
{
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = gate->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_enable(clk_id);
if (ret)
pr_warn_once("%s() clock enabled failed for %s, ret = %d\n",
__func__, clk_name, ret);
return ret;
}
/*
* zynqmp_clk_gate_disable() - Disable clock
* @hw: handle between common and hardware-specific interfaces
*/
static void zynqmp_clk_gate_disable(struct clk_hw *hw)
{
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = gate->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_disable(clk_id);
if (ret)
pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
__func__, clk_name, ret);
}
/**
* zynqmp_clk_gate_is_enable() - Check clock state
* @hw: handle between common and hardware-specific interfaces
*
* Return: 1 if enabled, 0 if disabled else error code
*/
static int zynqmp_clk_gate_is_enabled(struct clk_hw *hw)
{
struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = gate->clk_id;
int state, ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_getstate(clk_id, &state);
if (ret) {
pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
__func__, clk_name, ret);
return -EIO;
}
return state ? 1 : 0;
}
static const struct clk_ops zynqmp_clk_gate_ops = {
.enable = zynqmp_clk_gate_enable,
.disable = zynqmp_clk_gate_disable,
.is_enabled = zynqmp_clk_gate_is_enabled,
};
/**
* zynqmp_clk_register_gate() - Register a gate clock with the clock framework
* @name: Name of this clock
* @clk_id: Id of this clock
* @parents: Name of this clock's parents
* @num_parents: Number of parents
* @nodes: Clock topology node
*
* Return: clock hardware of the registered clock gate
*/
struct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
struct zynqmp_clk_gate *gate;
struct clk_hw *hw;
int ret;
struct clk_init_data init;
/* allocate the gate */
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &zynqmp_clk_gate_ops;
init.flags = nodes->flag;
init.parent_names = parents;
init.num_parents = 1;
/* struct clk_gate assignments */
gate->flags = nodes->type_flag;
gate->hw.init = &init;
gate->clk_id = clk_id;
hw = &gate->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(gate);
hw = ERR_PTR(ret);
}
return hw;
}
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC mux
*
* Copyright (C) 2016-2018 Xilinx
*/
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-zynqmp.h"
/*
* DOC: basic adjustable multiplexer clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is only affected by parent switching. No clk_set_rate support
* parent - parent is adjustable through clk_set_parent
*/
/**
* struct zynqmp_clk_mux - multiplexer clock
*
* @hw: handle between common and hardware-specific interfaces
* @flags: hardware-specific flags
* @clk_id: Id of clock
*/
struct zynqmp_clk_mux {
struct clk_hw hw;
u8 flags;
u32 clk_id;
};
#define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)
/**
* zynqmp_clk_mux_get_parent() - Get parent of clock
* @hw: handle between common and hardware-specific interfaces
*
* Return: Parent index
*/
static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
{
struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = mux->clk_id;
u32 val;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_getparent(clk_id, &val);
if (ret)
pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n",
__func__, clk_name, ret);
return val;
}
/**
* zynqmp_clk_mux_set_parent() - Set parent of clock
* @hw: handle between common and hardware-specific interfaces
* @index: Parent index
*
* Return: 0 on success else error+reason
*/
static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = mux->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_setparent(clk_id, index);
if (ret)
pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n",
__func__, clk_name, ret);
return ret;
}
static const struct clk_ops zynqmp_clk_mux_ops = {
.get_parent = zynqmp_clk_mux_get_parent,
.set_parent = zynqmp_clk_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
};
static const struct clk_ops zynqmp_clk_mux_ro_ops = {
.get_parent = zynqmp_clk_mux_get_parent,
};
/**
* zynqmp_clk_register_mux() - Register a mux table with the clock
* framework
* @name: Name of this clock
* @clk_id: Id of this clock
* @parents: Name of this clock's parents
* @num_parents: Number of parents
* @nodes: Clock topology node
*
* Return: clock hardware of the registered clock mux
*/
struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
struct zynqmp_clk_mux *mux;
struct clk_hw *hw;
struct clk_init_data init;
int ret;
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
init.name = name;
if (nodes->type_flag & CLK_MUX_READ_ONLY)
init.ops = &zynqmp_clk_mux_ro_ops;
else
init.ops = &zynqmp_clk_mux_ops;
init.flags = nodes->flag;
init.parent_names = parents;
init.num_parents = num_parents;
mux->flags = nodes->type_flag;
mux->hw.init = &init;
mux->clk_id = clk_id;
hw = &mux->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(hw);
hw = ERR_PTR(ret);
}
return hw;
}
EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016-2018 Xilinx
*/
#ifndef __LINUX_CLK_ZYNQMP_H_
#define __LINUX_CLK_ZYNQMP_H_
#include <linux/spinlock.h>
#include <linux/firmware/xlnx-zynqmp.h>
/* Clock APIs payload parameters */
#define CLK_GET_NAME_RESP_LEN 16
#define CLK_GET_TOPOLOGY_RESP_WORDS 3
#define CLK_GET_PARENTS_RESP_WORDS 3
#define CLK_GET_ATTR_RESP_WORDS 1
enum topology_type {
TYPE_INVALID,
TYPE_MUX,
TYPE_PLL,
TYPE_FIXEDFACTOR,
TYPE_DIV1,
TYPE_DIV2,
TYPE_GATE,
};
/**
* struct clock_topology - Clock topology
* @type: Type of topology
* @flag: Topology flags
* @type_flag: Topology type specific flag
*/
struct clock_topology {
u32 type;
u32 flag;
u32 type_flag;
};
struct clk_hw *zynqmp_clk_register_pll(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);
struct clk_hw *zynqmp_clk_register_gate(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);
struct clk_hw *zynqmp_clk_register_divider(const char *name,
u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);
struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);
struct clk_hw *zynqmp_clk_register_fixed_factor(const char *name,
u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes);
#endif
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC Divider support
*
* Copyright (C) 2016-2018 Xilinx
*
* Adjustable divider clock implementation
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-zynqmp.h"
/*
* DOC: basic adjustable divider clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable. clk->rate = ceiling(parent->rate / divisor)
* parent - fixed parent. No clk_set_parent support
*/
#define to_zynqmp_clk_divider(_hw) \
container_of(_hw, struct zynqmp_clk_divider, hw)
#define CLK_FRAC BIT(13) /* has a fractional parent */
/**
* struct zynqmp_clk_divider - adjustable divider clock
* @hw: handle between common and hardware-specific interfaces
* @flags: Hardware specific flags
* @clk_id: Id of clock
* @div_type: divisor type (TYPE_DIV1 or TYPE_DIV2)
*/
struct zynqmp_clk_divider {
struct clk_hw hw;
u8 flags;
u32 clk_id;
u32 div_type;
};
static inline int zynqmp_divider_get_val(unsigned long parent_rate,
unsigned long rate)
{
return DIV_ROUND_CLOSEST(parent_rate, rate);
}
/**
* zynqmp_clk_divider_recalc_rate() - Recalc rate of divider clock
* @hw: handle between common and hardware-specific interfaces
* @parent_rate: rate of parent clock
*
* Return: 0 on success else error+reason
*/
static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = divider->clk_id;
u32 div_type = divider->div_type;
u32 div, value;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_getdivider(clk_id, &div);
if (ret)
pr_warn_once("%s() get divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
if (div_type == TYPE_DIV1)
value = div & 0xFFFF;
else
value = div >> 16;
return DIV_ROUND_UP_ULL(parent_rate, value);
}
/**
* zynqmp_clk_divider_round_rate() - Round rate of divider clock
* @hw: handle between common and hardware-specific interfaces
* @rate: rate of clock to be set
* @prate: rate of parent clock
*
* Return: 0 on success else error+reason
*/
static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *prate)
{
struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = divider->clk_id;
u32 div_type = divider->div_type;
u32 bestdiv;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
ret = eemi_ops->clock_getdivider(clk_id, &bestdiv);
if (ret)
pr_warn_once("%s() get divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
if (div_type == TYPE_DIV1)
bestdiv = bestdiv & 0xFFFF;
else
bestdiv = bestdiv >> 16;
return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
}
bestdiv = zynqmp_divider_get_val(*prate, rate);
if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) &&
(divider->flags & CLK_FRAC))
bestdiv = rate % *prate ? 1 : bestdiv;
*prate = rate * bestdiv;
return rate;
}
/**
* zynqmp_clk_divider_set_rate() - Set rate of divider clock
* @hw: handle between common and hardware-specific interfaces
* @rate: rate of clock to be set
* @parent_rate: rate of parent clock
*
* Return: 0 on success else error+reason
*/
static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = divider->clk_id;
u32 div_type = divider->div_type;
u32 value, div;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
value = zynqmp_divider_get_val(parent_rate, rate);
if (div_type == TYPE_DIV1) {
div = value & 0xFFFF;
div |= 0xffff << 16;
} else {
div = 0xffff;
div |= value << 16;
}
ret = eemi_ops->clock_setdivider(clk_id, div);
if (ret)
pr_warn_once("%s() set divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
return ret;
}
static const struct clk_ops zynqmp_clk_divider_ops = {
.recalc_rate = zynqmp_clk_divider_recalc_rate,
.round_rate = zynqmp_clk_divider_round_rate,
.set_rate = zynqmp_clk_divider_set_rate,
};
/**
* zynqmp_clk_register_divider() - Register a divider clock
* @name: Name of this clock
* @clk_id: Id of clock
* @parents: Name of this clock's parents
* @num_parents: Number of parents
* @nodes: Clock topology node
*
* Return: clock hardware to registered clock divider
*/
struct clk_hw *zynqmp_clk_register_divider(const char *name,
u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
struct zynqmp_clk_divider *div;
struct clk_hw *hw;
struct clk_init_data init;
int ret;
/* allocate the divider */
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &zynqmp_clk_divider_ops;
init.flags = nodes->flag;
init.parent_names = parents;
init.num_parents = 1;
/* struct clk_divider assignments */
div->flags = nodes->type_flag;
div->hw.init = &init;
div->clk_id = clk_id;
div->div_type = nodes->type;
hw = &div->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(div);
hw = ERR_PTR(ret);
}
return hw;
}
EXPORT_SYMBOL_GPL(zynqmp_clk_register_divider);
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC PLL driver
*
* Copyright (C) 2016-2018 Xilinx
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include "clk-zynqmp.h"
/**
* struct zynqmp_pll - PLL clock
* @hw: Handle between common and hardware-specific interfaces
* @clk_id: PLL clock ID
*/
struct zynqmp_pll {
struct clk_hw hw;
u32 clk_id;
};
#define to_zynqmp_pll(_hw) container_of(_hw, struct zynqmp_pll, hw)
#define PLL_FBDIV_MIN 25
#define PLL_FBDIV_MAX 125
#define PS_PLL_VCO_MIN 1500000000
#define PS_PLL_VCO_MAX 3000000000UL
enum pll_mode {
PLL_MODE_INT,
PLL_MODE_FRAC,
};
#define FRAC_OFFSET 0x8
#define PLLFCFG_FRAC_EN BIT(31)
#define FRAC_DIV BIT(16) /* 2^16 */
/**
* zynqmp_pll_get_mode() - Get mode of PLL
* @hw: Handle between common and hardware-specific interfaces
*
* Return: Mode of PLL
*/
static inline enum pll_mode zynqmp_pll_get_mode(struct clk_hw *hw)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id;
const char *clk_name = clk_hw_get_name(hw);
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_MODE, clk_id, 0,
ret_payload);
if (ret)
pr_warn_once("%s() PLL get frac mode failed for %s, ret = %d\n",
__func__, clk_name, ret);
return ret_payload[1];
}
/**
* zynqmp_pll_set_mode() - Set the PLL mode
* @hw: Handle between common and hardware-specific interfaces
* @on: Flag to determine the mode
*/
static inline void zynqmp_pll_set_mode(struct clk_hw *hw, bool on)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id;
const char *clk_name = clk_hw_get_name(hw);
int ret;
u32 mode;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
if (on)
mode = PLL_MODE_FRAC;
else
mode = PLL_MODE_INT;
ret = eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode, NULL);
if (ret)
pr_warn_once("%s() PLL set frac mode failed for %s, ret = %d\n",
__func__, clk_name, ret);
}
/**
* zynqmp_pll_round_rate() - Round a clock frequency
* @hw: Handle between common and hardware-specific interfaces
* @rate: Desired clock frequency
* @prate: Clock frequency of parent clock
*
* Return: Frequency closest to @rate the hardware can generate
*/
static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
u32 fbdiv;
long rate_div, f;
/* Enable the fractional mode if needed */
rate_div = (rate * FRAC_DIV) / *prate;
f = rate_div % FRAC_DIV;
zynqmp_pll_set_mode(hw, !!f);
if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
if (rate > PS_PLL_VCO_MAX) {
fbdiv = rate / PS_PLL_VCO_MAX;
rate = rate / (fbdiv + 1);
}
if (rate < PS_PLL_VCO_MIN) {
fbdiv = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate);
rate = rate * fbdiv;
}
return rate;
}
fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
return *prate * fbdiv;
}
/**
* zynqmp_pll_recalc_rate() - Recalculate clock frequency
* @hw: Handle between common and hardware-specific interfaces
* @parent_rate: Clock frequency of parent clock
*
* Return: Current clock frequency
*/
static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id;
const char *clk_name = clk_hw_get_name(hw);
u32 fbdiv, data;
unsigned long rate, frac;
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_getdivider(clk_id, &fbdiv);
if (ret)
pr_warn_once("%s() get divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
rate = parent_rate * fbdiv;
if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_DATA, clk_id, 0,
ret_payload);
data = ret_payload[1];
frac = (parent_rate * data) / FRAC_DIV;
rate = rate + frac;
}
return rate;
}
/**
* zynqmp_pll_set_rate() - Set rate of PLL
* @hw: Handle between common and hardware-specific interfaces
* @rate: Frequency of clock to be set
* @parent_rate: Clock frequency of parent clock
*
* Set PLL divider to set desired rate.
*
* Returns: rate which is set on success else error code
*/
static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
u32 clk_id = clk->clk_id;
const char *clk_name = clk_hw_get_name(hw);
u32 fbdiv;
long rate_div, frac, m, f;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
rate_div = (rate * FRAC_DIV) / parent_rate;
m = rate_div / FRAC_DIV;
f = rate_div % FRAC_DIV;
m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
rate = parent_rate * m;
frac = (parent_rate * f) / FRAC_DIV;
ret = eemi_ops->clock_setdivider(clk_id, m);
if (ret)
pr_warn_once("%s() set divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, f, NULL);
return rate + frac;
}
fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
ret = eemi_ops->clock_setdivider(clk_id, fbdiv);
if (ret)
pr_warn_once("%s() set divider failed for %s, ret = %d\n",
__func__, clk_name, ret);
return parent_rate * fbdiv;
}
/**
* zynqmp_pll_is_enabled() - Check if a clock is enabled
* @hw: Handle between common and hardware-specific interfaces
*
* Return: 1 if the clock is enabled, 0 otherwise
*/
static int zynqmp_pll_is_enabled(struct clk_hw *hw)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = clk->clk_id;
unsigned int state;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
ret = eemi_ops->clock_getstate(clk_id, &state);
if (ret) {
pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
__func__, clk_name, ret);
return -EIO;
}
return state ? 1 : 0;
}
/**
* zynqmp_pll_enable() - Enable clock
* @hw: Handle between common and hardware-specific interfaces
*
* Return: 0 on success else error code
*/
static int zynqmp_pll_enable(struct clk_hw *hw)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = clk->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
if (zynqmp_pll_is_enabled(hw))
return 0;
ret = eemi_ops->clock_enable(clk_id);
if (ret)
pr_warn_once("%s() clock enable failed for %s, ret = %d\n",
__func__, clk_name, ret);
return ret;
}
/**
* zynqmp_pll_disable() - Disable clock
* @hw: Handle between common and hardware-specific interfaces
*/
static void zynqmp_pll_disable(struct clk_hw *hw)
{
struct zynqmp_pll *clk = to_zynqmp_pll(hw);
const char *clk_name = clk_hw_get_name(hw);
u32 clk_id = clk->clk_id;
int ret;
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
if (!zynqmp_pll_is_enabled(hw))
return;
ret = eemi_ops->clock_disable(clk_id);
if (ret)
pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
__func__, clk_name, ret);
}
static const struct clk_ops zynqmp_pll_ops = {
.enable = zynqmp_pll_enable,
.disable = zynqmp_pll_disable,
.is_enabled = zynqmp_pll_is_enabled,
.round_rate = zynqmp_pll_round_rate,
.recalc_rate = zynqmp_pll_recalc_rate,
.set_rate = zynqmp_pll_set_rate,
};
/**
* zynqmp_clk_register_pll() - Register PLL with the clock framework
* @name: PLL name
* @clk_id: Clock ID
* @parents: Name of this clock's parents
* @num_parents: Number of parents
* @nodes: Clock topology node
*
* Return: clock hardware to the registered clock
*/
struct clk_hw *zynqmp_clk_register_pll(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
struct zynqmp_pll *pll;
struct clk_hw *hw;
struct clk_init_data init;
int ret;
init.name = name;
init.ops = &zynqmp_pll_ops;
init.flags = nodes->flag;
init.parent_names = parents;
init.num_parents = 1;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
pll->hw.init = &init;
pll->clk_id = clk_id;
hw = &pll->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(pll);
return ERR_PTR(ret);
}
clk_hw_set_rate_range(hw, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
if (ret < 0)
pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, ret);
return hw;
}
...@@ -460,4 +460,18 @@ config EDAC_TI ...@@ -460,4 +460,18 @@ config EDAC_TI
Support for error detection and correction on the Support for error detection and correction on the
TI SoCs. TI SoCs.
config EDAC_QCOM
tristate "QCOM EDAC Controller"
depends on ARCH_QCOM && QCOM_LLCC
help
Support for error detection and correction on the
Qualcomm Technologies, Inc. SoCs.
This driver reports Single Bit Errors (SBEs) and Double Bit Errors (DBEs).
As of now, it supports error reporting for Last Level Cache Controller (LLCC)
of Tag RAM and Data RAM.
For debugging issues having to do with stability and overall system
health, you should probably say 'Y' here.
endif # EDAC endif # EDAC
...@@ -77,3 +77,4 @@ obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o ...@@ -77,3 +77,4 @@ obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o
obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o
obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
obj-$(CONFIG_EDAC_TI) += ti_edac.o obj-$(CONFIG_EDAC_TI) += ti_edac.o
obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o
This diff is collapsed.
...@@ -289,7 +289,9 @@ config HAVE_ARM_SMCCC ...@@ -289,7 +289,9 @@ config HAVE_ARM_SMCCC
source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig" source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig" source "drivers/firmware/efi/Kconfig"
source "drivers/firmware/imx/Kconfig"
source "drivers/firmware/meson/Kconfig" source "drivers/firmware/meson/Kconfig"
source "drivers/firmware/tegra/Kconfig" source "drivers/firmware/tegra/Kconfig"
source "drivers/firmware/xilinx/Kconfig"
endmenu endmenu
...@@ -31,4 +31,6 @@ obj-y += meson/ ...@@ -31,4 +31,6 @@ obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_UEFI_CPER) += efi/ obj-$(CONFIG_UEFI_CPER) += efi/
obj-y += imx/
obj-y += tegra/ obj-y += tegra/
obj-y += xilinx/
...@@ -208,7 +208,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle, ...@@ -208,7 +208,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
ret = scmi_do_xfer(handle, t); ret = scmi_do_xfer(handle, t);
if (!ret) if (!ret)
memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE); strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
scmi_xfer_put(handle, t); scmi_xfer_put(handle, t);
......
...@@ -111,7 +111,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle, ...@@ -111,7 +111,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
ret = scmi_do_xfer(handle, t); ret = scmi_do_xfer(handle, t);
if (!ret) if (!ret)
memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
else else
clk->name[0] = '\0'; clk->name[0] = '\0';
......
...@@ -174,7 +174,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain, ...@@ -174,7 +174,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
dom_info->mult_factor = dom_info->mult_factor =
(dom_info->sustained_freq_khz * 1000) / (dom_info->sustained_freq_khz * 1000) /
dom_info->sustained_perf_level; dom_info->sustained_perf_level;
memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
} }
scmi_xfer_put(handle, t); scmi_xfer_put(handle, t);
...@@ -427,6 +427,33 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain, ...@@ -427,6 +427,33 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
return ret; return ret;
} }
static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain,
unsigned long *freq, unsigned long *power)
{
struct scmi_perf_info *pi = handle->perf_priv;
struct perf_dom_info *dom;
unsigned long opp_freq;
int idx, ret = -EINVAL;
struct scmi_opp *opp;
dom = pi->dom_info + domain;
if (!dom)
return -EIO;
for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) {
opp_freq = opp->perf * dom->mult_factor;
if (opp_freq < *freq)
continue;
*freq = opp_freq;
*power = opp->power;
ret = 0;
break;
}
return ret;
}
static struct scmi_perf_ops perf_ops = { static struct scmi_perf_ops perf_ops = {
.limits_set = scmi_perf_limits_set, .limits_set = scmi_perf_limits_set,
.limits_get = scmi_perf_limits_get, .limits_get = scmi_perf_limits_get,
...@@ -437,6 +464,7 @@ static struct scmi_perf_ops perf_ops = { ...@@ -437,6 +464,7 @@ static struct scmi_perf_ops perf_ops = {
.device_opps_add = scmi_dvfs_device_opps_add, .device_opps_add = scmi_dvfs_device_opps_add,
.freq_set = scmi_dvfs_freq_set, .freq_set = scmi_dvfs_freq_set,
.freq_get = scmi_dvfs_freq_get, .freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
}; };
static int scmi_perf_protocol_init(struct scmi_handle *handle) static int scmi_perf_protocol_init(struct scmi_handle *handle)
......
...@@ -106,7 +106,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain, ...@@ -106,7 +106,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags); dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags); dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
} }
scmi_xfer_put(handle, t); scmi_xfer_put(handle, t);
......
...@@ -140,7 +140,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, ...@@ -140,7 +140,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
s = &si->sensors[desc_index + cnt]; s = &si->sensors[desc_index + cnt];
s->id = le32_to_cpu(buf->desc[cnt].id); s->id = le32_to_cpu(buf->desc[cnt].id);
s->type = SENSOR_TYPE(attrh); s->type = SENSOR_TYPE(attrh);
memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE); strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
} }
desc_index += num_returned; desc_index += num_returned;
......
config IMX_SCU
bool "IMX SCU Protocol driver"
depends on IMX_MBOX
help
The System Controller Firmware (SCFW) is a low-level system function
which runs on a dedicated Cortex-M core to provide power, clock, and
resource management. It exists on some i.MX8 processors. e.g. i.MX8QM
(QM, QP), and i.MX8QX (QXP, DX).
This driver manages the IPC interface between host CPU and the
SCU firmware running on M4.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
* Author: Dong Aisheng <aisheng.dong@nxp.com>
*
* Implementation of the SCU IPC functions using MUs (client side).
*
*/
#include <linux/err.h>
#include <linux/firmware/imx/types.h>
#include <linux/firmware/imx/ipc.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#define SCU_MU_CHAN_NUM 8
#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
struct imx_sc_chan {
struct imx_sc_ipc *sc_ipc;
struct mbox_client cl;
struct mbox_chan *ch;
int idx;
};
struct imx_sc_ipc {
/* SCU uses 4 Tx and 4 Rx channels */
struct imx_sc_chan chans[SCU_MU_CHAN_NUM];
struct device *dev;
struct mutex lock;
struct completion done;
/* temporarily store the SCU msg */
u32 *msg;
u8 rx_size;
u8 count;
};
/*
* This type is used to indicate error response for most functions.
*/
enum imx_sc_error_codes {
IMX_SC_ERR_NONE = 0, /* Success */
IMX_SC_ERR_VERSION = 1, /* Incompatible API version */
IMX_SC_ERR_CONFIG = 2, /* Configuration error */
IMX_SC_ERR_PARM = 3, /* Bad parameter */
IMX_SC_ERR_NOACCESS = 4, /* Permission error (no access) */
IMX_SC_ERR_LOCKED = 5, /* Permission error (locked) */
IMX_SC_ERR_UNAVAILABLE = 6, /* Unavailable (out of resources) */
IMX_SC_ERR_NOTFOUND = 7, /* Not found */
IMX_SC_ERR_NOPOWER = 8, /* No power */
IMX_SC_ERR_IPC = 9, /* Generic IPC error */
IMX_SC_ERR_BUSY = 10, /* Resource is currently busy/active */
IMX_SC_ERR_FAIL = 11, /* General I/O failure */
IMX_SC_ERR_LAST
};
static int imx_sc_linux_errmap[IMX_SC_ERR_LAST] = {
0, /* IMX_SC_ERR_NONE */
-EINVAL, /* IMX_SC_ERR_VERSION */
-EINVAL, /* IMX_SC_ERR_CONFIG */
-EINVAL, /* IMX_SC_ERR_PARM */
-EACCES, /* IMX_SC_ERR_NOACCESS */
-EACCES, /* IMX_SC_ERR_LOCKED */
-ERANGE, /* IMX_SC_ERR_UNAVAILABLE */
-EEXIST, /* IMX_SC_ERR_NOTFOUND */
-EPERM, /* IMX_SC_ERR_NOPOWER */
-EPIPE, /* IMX_SC_ERR_IPC */
-EBUSY, /* IMX_SC_ERR_BUSY */
-EIO, /* IMX_SC_ERR_FAIL */
};
static struct imx_sc_ipc *imx_sc_ipc_handle;
static inline int imx_sc_to_linux_errno(int errno)
{
if (errno >= IMX_SC_ERR_NONE && errno < IMX_SC_ERR_LAST)
return imx_sc_linux_errmap[errno];
return -EIO;
}
/*
* Get the default handle used by SCU
*/
int imx_scu_get_handle(struct imx_sc_ipc **ipc)
{
if (!imx_sc_ipc_handle)
return -EPROBE_DEFER;
*ipc = imx_sc_ipc_handle;
return 0;
}
EXPORT_SYMBOL(imx_scu_get_handle);
static void imx_scu_rx_callback(struct mbox_client *c, void *msg)
{
struct imx_sc_chan *sc_chan = container_of(c, struct imx_sc_chan, cl);
struct imx_sc_ipc *sc_ipc = sc_chan->sc_ipc;
struct imx_sc_rpc_msg *hdr;
u32 *data = msg;
if (sc_chan->idx == 0) {
hdr = msg;
sc_ipc->rx_size = hdr->size;
dev_dbg(sc_ipc->dev, "msg rx size %u\n", sc_ipc->rx_size);
if (sc_ipc->rx_size > 4)
dev_warn(sc_ipc->dev, "RPC does not support receiving over 4 words: %u\n",
sc_ipc->rx_size);
}
sc_ipc->msg[sc_chan->idx] = *data;
sc_ipc->count++;
dev_dbg(sc_ipc->dev, "mu %u msg %u 0x%x\n", sc_chan->idx,
sc_ipc->count, *data);
if ((sc_ipc->rx_size != 0) && (sc_ipc->count == sc_ipc->rx_size))
complete(&sc_ipc->done);
}
static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg)
{
struct imx_sc_rpc_msg *hdr = msg;
struct imx_sc_chan *sc_chan;
u32 *data = msg;
int ret;
int i;
/* Check size */
if (hdr->size > IMX_SC_RPC_MAX_MSG)
return -EINVAL;
dev_dbg(sc_ipc->dev, "RPC SVC %u FUNC %u SIZE %u\n", hdr->svc,
hdr->func, hdr->size);
for (i = 0; i < hdr->size; i++) {
sc_chan = &sc_ipc->chans[i % 4];
ret = mbox_send_message(sc_chan->ch, &data[i]);
if (ret < 0)
return ret;
}
return 0;
}
/*
* RPC command/response
*/
int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
{
struct imx_sc_rpc_msg *hdr;
int ret;
if (WARN_ON(!sc_ipc || !msg))
return -EINVAL;
mutex_lock(&sc_ipc->lock);
reinit_completion(&sc_ipc->done);
sc_ipc->msg = msg;
sc_ipc->count = 0;
ret = imx_scu_ipc_write(sc_ipc, msg);
if (ret < 0) {
dev_err(sc_ipc->dev, "RPC send msg failed: %d\n", ret);
goto out;
}
if (have_resp) {
if (!wait_for_completion_timeout(&sc_ipc->done,
MAX_RX_TIMEOUT)) {
dev_err(sc_ipc->dev, "RPC send msg timeout\n");
mutex_unlock(&sc_ipc->lock);
return -ETIMEDOUT;
}
/* response status is stored in hdr->func field */
hdr = msg;
ret = hdr->func;
}
out:
mutex_unlock(&sc_ipc->lock);
dev_dbg(sc_ipc->dev, "RPC SVC done\n");
return imx_sc_to_linux_errno(ret);
}
EXPORT_SYMBOL(imx_scu_call_rpc);
static int imx_scu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct imx_sc_ipc *sc_ipc;
struct imx_sc_chan *sc_chan;
struct mbox_client *cl;
char *chan_name;
int ret;
int i;
sc_ipc = devm_kzalloc(dev, sizeof(*sc_ipc), GFP_KERNEL);
if (!sc_ipc)
return -ENOMEM;
for (i = 0; i < SCU_MU_CHAN_NUM; i++) {
if (i < 4)
chan_name = kasprintf(GFP_KERNEL, "tx%d", i);
else
chan_name = kasprintf(GFP_KERNEL, "rx%d", i - 4);
if (!chan_name)
return -ENOMEM;
sc_chan = &sc_ipc->chans[i];
cl = &sc_chan->cl;
cl->dev = dev;
cl->tx_block = false;
cl->knows_txdone = true;
cl->rx_callback = imx_scu_rx_callback;
sc_chan->sc_ipc = sc_ipc;
sc_chan->idx = i % 4;
sc_chan->ch = mbox_request_channel_byname(cl, chan_name);
if (IS_ERR(sc_chan->ch)) {
ret = PTR_ERR(sc_chan->ch);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to request mbox chan %s ret %d\n",
chan_name, ret);
return ret;
}
dev_dbg(dev, "request mbox chan %s\n", chan_name);
/* chan_name is not used anymore by framework */
kfree(chan_name);
}
sc_ipc->dev = dev;
mutex_init(&sc_ipc->lock);
init_completion(&sc_ipc->done);
imx_sc_ipc_handle = sc_ipc;
dev_info(dev, "NXP i.MX SCU Initialized\n");
return devm_of_platform_populate(dev);
}
static const struct of_device_id imx_scu_match[] = {
{ .compatible = "fsl,imx-scu", },
{ /* Sentinel */ }
};
static struct platform_driver imx_scu_driver = {
.driver = {
.name = "imx-scu",
.of_match_table = imx_scu_match,
},
.probe = imx_scu_probe,
};
builtin_platform_driver(imx_scu_driver);
MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
MODULE_DESCRIPTION("IMX SCU firmware protocol driver");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Copyright 2017~2018 NXP
* Author: Dong Aisheng <aisheng.dong@nxp.com>
*
* File containing client-side RPC functions for the MISC service. These
* function are ported to clients that communicate to the SC.
*
*/
#include <linux/firmware/imx/svc/misc.h>
struct imx_sc_msg_req_misc_set_ctrl {
struct imx_sc_rpc_msg hdr;
u32 ctrl;
u32 val;
u16 resource;
} __packed;
struct imx_sc_msg_req_misc_get_ctrl {
struct imx_sc_rpc_msg hdr;
u32 ctrl;
u16 resource;
} __packed;
struct imx_sc_msg_resp_misc_get_ctrl {
struct imx_sc_rpc_msg hdr;
u32 val;
} __packed;
/*
* This function sets a miscellaneous control value.
*
* @param[in] ipc IPC handle
* @param[in] resource resource the control is associated with
* @param[in] ctrl control to change
* @param[in] val value to apply to the control
*
* @return Returns 0 for success and < 0 for errors.
*/
int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource,
u8 ctrl, u32 val)
{
struct imx_sc_msg_req_misc_set_ctrl msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = (uint8_t)IMX_SC_RPC_SVC_MISC;
hdr->func = (uint8_t)IMX_SC_MISC_FUNC_SET_CONTROL;
hdr->size = 4;
msg.ctrl = ctrl;
msg.val = val;
msg.resource = resource;
return imx_scu_call_rpc(ipc, &msg, true);
}
EXPORT_SYMBOL(imx_sc_misc_set_control);
/*
* This function gets a miscellaneous control value.
*
* @param[in] ipc IPC handle
* @param[in] resource resource the control is associated with
* @param[in] ctrl control to get
* @param[out] val pointer to return the control value
*
* @return Returns 0 for success and < 0 for errors.
*/
int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource,
u8 ctrl, u32 *val)
{
struct imx_sc_msg_req_misc_get_ctrl msg;
struct imx_sc_msg_resp_misc_get_ctrl *resp;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
int ret;
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = (uint8_t)IMX_SC_RPC_SVC_MISC;
hdr->func = (uint8_t)IMX_SC_MISC_FUNC_GET_CONTROL;
hdr->size = 3;
msg.ctrl = ctrl;
msg.resource = resource;
ret = imx_scu_call_rpc(ipc, &msg, true);
if (ret)
return ret;
resp = (struct imx_sc_msg_resp_misc_get_ctrl *)&msg;
if (val != NULL)
*val = resp->val;
return 0;
}
EXPORT_SYMBOL(imx_sc_misc_get_control);
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/firmware/meson/meson_sm.h> #include <linux/firmware/meson/meson_sm.h>
...@@ -48,6 +49,7 @@ struct meson_sm_chip gxbb_chip = { ...@@ -48,6 +49,7 @@ struct meson_sm_chip gxbb_chip = {
CMD(SM_EFUSE_READ, 0x82000030), CMD(SM_EFUSE_READ, 0x82000030),
CMD(SM_EFUSE_WRITE, 0x82000031), CMD(SM_EFUSE_WRITE, 0x82000031),
CMD(SM_EFUSE_USER_MAX, 0x82000033), CMD(SM_EFUSE_USER_MAX, 0x82000033),
CMD(SM_GET_CHIP_ID, 0x82000044),
{ /* sentinel */ }, { /* sentinel */ },
}, },
}; };
...@@ -214,6 +216,57 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, ...@@ -214,6 +216,57 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
} }
EXPORT_SYMBOL(meson_sm_call_write); EXPORT_SYMBOL(meson_sm_call_write);
#define SM_CHIP_ID_LENGTH 119
#define SM_CHIP_ID_OFFSET 4
#define SM_CHIP_ID_SIZE 12
static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
uint8_t *id_buf;
int ret;
id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
if (!id_buf)
return -ENOMEM;
ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
0, 0, 0, 0, 0);
if (ret < 0) {
kfree(id_buf);
return ret;
}
ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
id_buf[SM_CHIP_ID_OFFSET + 0],
id_buf[SM_CHIP_ID_OFFSET + 1],
id_buf[SM_CHIP_ID_OFFSET + 2],
id_buf[SM_CHIP_ID_OFFSET + 3],
id_buf[SM_CHIP_ID_OFFSET + 4],
id_buf[SM_CHIP_ID_OFFSET + 5],
id_buf[SM_CHIP_ID_OFFSET + 6],
id_buf[SM_CHIP_ID_OFFSET + 7],
id_buf[SM_CHIP_ID_OFFSET + 8],
id_buf[SM_CHIP_ID_OFFSET + 9],
id_buf[SM_CHIP_ID_OFFSET + 10],
id_buf[SM_CHIP_ID_OFFSET + 11]);
kfree(id_buf);
return ret;
}
static DEVICE_ATTR_RO(serial);
static struct attribute *meson_sm_sysfs_attributes[] = {
&dev_attr_serial.attr,
NULL,
};
static const struct attribute_group meson_sm_sysfs_attr_group = {
.attrs = meson_sm_sysfs_attributes,
};
static const struct of_device_id meson_sm_ids[] = { static const struct of_device_id meson_sm_ids[] = {
{ .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip }, { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
{ /* sentinel */ }, { /* sentinel */ },
...@@ -242,6 +295,9 @@ static int __init meson_sm_probe(struct platform_device *pdev) ...@@ -242,6 +295,9 @@ static int __init meson_sm_probe(struct platform_device *pdev)
fw.chip = chip; fw.chip = chip;
pr_info("secure-monitor enabled\n"); pr_info("secure-monitor enabled\n");
if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
goto out_in_base;
return 0; return 0;
out_in_base: out_in_base:
......
...@@ -525,34 +525,44 @@ static int qcom_scm_probe(struct platform_device *pdev) ...@@ -525,34 +525,44 @@ static int qcom_scm_probe(struct platform_device *pdev)
return ret; return ret;
clks = (unsigned long)of_device_get_match_data(&pdev->dev); clks = (unsigned long)of_device_get_match_data(&pdev->dev);
if (clks & SCM_HAS_CORE_CLK) {
scm->core_clk = devm_clk_get(&pdev->dev, "core"); scm->core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(scm->core_clk)) { if (IS_ERR(scm->core_clk)) {
if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER) if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
dev_err(&pdev->dev, return PTR_ERR(scm->core_clk);
"failed to acquire core clk\n");
if (clks & SCM_HAS_CORE_CLK) {
dev_err(&pdev->dev, "failed to acquire core clk\n");
return PTR_ERR(scm->core_clk); return PTR_ERR(scm->core_clk);
} }
scm->core_clk = NULL;
} }
if (clks & SCM_HAS_IFACE_CLK) { scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(scm->iface_clk)) {
if (IS_ERR(scm->iface_clk)) { if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER)
if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER) return PTR_ERR(scm->iface_clk);
dev_err(&pdev->dev,
"failed to acquire iface clk\n"); if (clks & SCM_HAS_IFACE_CLK) {
dev_err(&pdev->dev, "failed to acquire iface clk\n");
return PTR_ERR(scm->iface_clk); return PTR_ERR(scm->iface_clk);
} }
scm->iface_clk = NULL;
} }
if (clks & SCM_HAS_BUS_CLK) { scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); if (IS_ERR(scm->bus_clk)) {
if (IS_ERR(scm->bus_clk)) { if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER)
if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER) return PTR_ERR(scm->bus_clk);
dev_err(&pdev->dev,
"failed to acquire bus clk\n"); if (clks & SCM_HAS_BUS_CLK) {
dev_err(&pdev->dev, "failed to acquire bus clk\n");
return PTR_ERR(scm->bus_clk); return PTR_ERR(scm->bus_clk);
} }
scm->bus_clk = NULL;
} }
scm->reset.ops = &qcom_scm_pas_reset_ops; scm->reset.ops = &qcom_scm_pas_reset_ops;
...@@ -594,23 +604,23 @@ static const struct of_device_id qcom_scm_dt_match[] = { ...@@ -594,23 +604,23 @@ static const struct of_device_id qcom_scm_dt_match[] = {
{ .compatible = "qcom,scm-apq8064", { .compatible = "qcom,scm-apq8064",
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
}, },
{ .compatible = "qcom,scm-msm8660", { .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK |
.data = (void *) SCM_HAS_CORE_CLK, SCM_HAS_IFACE_CLK |
}, SCM_HAS_BUS_CLK)
{ .compatible = "qcom,scm-msm8960",
.data = (void *) SCM_HAS_CORE_CLK,
},
{ .compatible = "qcom,scm-msm8996",
.data = NULL, /* no clocks */
}, },
{ .compatible = "qcom,scm-ipq4019", { .compatible = "qcom,scm-ipq4019" },
.data = NULL, /* no clocks */ { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK },
{ .compatible = "qcom,scm-msm8960", .data = (void *) SCM_HAS_CORE_CLK },
{ .compatible = "qcom,scm-msm8916", .data = (void *)(SCM_HAS_CORE_CLK |
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
}, },
{ .compatible = "qcom,scm", { .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
.data = (void *)(SCM_HAS_CORE_CLK SCM_HAS_IFACE_CLK |
| SCM_HAS_IFACE_CLK SCM_HAS_BUS_CLK)
| SCM_HAS_BUS_CLK),
}, },
{ .compatible = "qcom,scm-msm8996" },
{ .compatible = "qcom,scm" },
{} {}
}; };
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/semaphore.h> #include <linux/semaphore.h>
#include <linux/sched/clock.h> #include <linux/sched/clock.h>
...@@ -843,6 +844,23 @@ static int tegra_bpmp_probe(struct platform_device *pdev) ...@@ -843,6 +844,23 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
return err; return err;
} }
static int __maybe_unused tegra_bpmp_resume(struct device *dev)
{
struct tegra_bpmp *bpmp = dev_get_drvdata(dev);
unsigned int i;
/* reset message channels */
tegra_bpmp_channel_reset(bpmp->tx_channel);
tegra_bpmp_channel_reset(bpmp->rx_channel);
for (i = 0; i < bpmp->threaded.count; i++)
tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]);
return 0;
}
static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume);
static const struct tegra_bpmp_soc tegra186_soc = { static const struct tegra_bpmp_soc tegra186_soc = {
.channels = { .channels = {
.cpu_tx = { .cpu_tx = {
...@@ -871,6 +889,7 @@ static struct platform_driver tegra_bpmp_driver = { ...@@ -871,6 +889,7 @@ static struct platform_driver tegra_bpmp_driver = {
.driver = { .driver = {
.name = "tegra-bpmp", .name = "tegra-bpmp",
.of_match_table = tegra_bpmp_match, .of_match_table = tegra_bpmp_match,
.pm = &tegra_bpmp_pm_ops,
}, },
.probe = tegra_bpmp_probe, .probe = tegra_bpmp_probe,
}; };
......
...@@ -66,14 +66,14 @@ struct ti_sci_xfers_info { ...@@ -66,14 +66,14 @@ struct ti_sci_xfers_info {
/** /**
* struct ti_sci_desc - Description of SoC integration * struct ti_sci_desc - Description of SoC integration
* @host_id: Host identifier representing the compute entity * @default_host_id: Host identifier representing the compute entity
* @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
* @max_msgs: Maximum number of messages that can be pending * @max_msgs: Maximum number of messages that can be pending
* simultaneously in the system * simultaneously in the system
* @max_msg_size: Maximum size of data per message that can be handled. * @max_msg_size: Maximum size of data per message that can be handled.
*/ */
struct ti_sci_desc { struct ti_sci_desc {
u8 host_id; u8 default_host_id;
int max_rx_timeout_ms; int max_rx_timeout_ms;
int max_msgs; int max_msgs;
int max_msg_size; int max_msg_size;
...@@ -94,6 +94,7 @@ struct ti_sci_desc { ...@@ -94,6 +94,7 @@ struct ti_sci_desc {
* @chan_rx: Receive mailbox channel * @chan_rx: Receive mailbox channel
* @minfo: Message info * @minfo: Message info
* @node: list head * @node: list head
* @host_id: Host ID
* @users: Number of users of this instance * @users: Number of users of this instance
*/ */
struct ti_sci_info { struct ti_sci_info {
...@@ -110,6 +111,7 @@ struct ti_sci_info { ...@@ -110,6 +111,7 @@ struct ti_sci_info {
struct mbox_chan *chan_rx; struct mbox_chan *chan_rx;
struct ti_sci_xfers_info minfo; struct ti_sci_xfers_info minfo;
struct list_head node; struct list_head node;
u8 host_id;
/* protected by ti_sci_list_mutex */ /* protected by ti_sci_list_mutex */
int users; int users;
...@@ -370,7 +372,7 @@ static struct ti_sci_xfer *ti_sci_get_one_xfer(struct ti_sci_info *info, ...@@ -370,7 +372,7 @@ static struct ti_sci_xfer *ti_sci_get_one_xfer(struct ti_sci_info *info,
hdr->seq = xfer_id; hdr->seq = xfer_id;
hdr->type = msg_type; hdr->type = msg_type;
hdr->host = info->desc->host_id; hdr->host = info->host_id;
hdr->flags = msg_flags; hdr->flags = msg_flags;
return xfer; return xfer;
...@@ -1793,7 +1795,7 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, ...@@ -1793,7 +1795,7 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
/* Description for K2G */ /* Description for K2G */
static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
.host_id = 2, .default_host_id = 2,
/* Conservative duration */ /* Conservative duration */
.max_rx_timeout_ms = 1000, .max_rx_timeout_ms = 1000,
/* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */
...@@ -1819,6 +1821,7 @@ static int ti_sci_probe(struct platform_device *pdev) ...@@ -1819,6 +1821,7 @@ static int ti_sci_probe(struct platform_device *pdev)
int ret = -EINVAL; int ret = -EINVAL;
int i; int i;
int reboot = 0; int reboot = 0;
u32 h_id;
of_id = of_match_device(ti_sci_of_match, dev); of_id = of_match_device(ti_sci_of_match, dev);
if (!of_id) { if (!of_id) {
...@@ -1833,6 +1836,19 @@ static int ti_sci_probe(struct platform_device *pdev) ...@@ -1833,6 +1836,19 @@ static int ti_sci_probe(struct platform_device *pdev)
info->dev = dev; info->dev = dev;
info->desc = desc; info->desc = desc;
ret = of_property_read_u32(dev->of_node, "ti,host-id", &h_id);
/* if the property is not present in DT, use a default from desc */
if (ret < 0) {
info->host_id = info->desc->default_host_id;
} else {
if (!h_id) {
dev_warn(dev, "Host ID 0 is reserved for firmware\n");
info->host_id = info->desc->default_host_id;
} else {
info->host_id = h_id;
}
}
reboot = of_property_read_bool(dev->of_node, reboot = of_property_read_bool(dev->of_node,
"ti,system-reboot-controller"); "ti,system-reboot-controller");
INIT_LIST_HEAD(&info->node); INIT_LIST_HEAD(&info->node);
......
# SPDX-License-Identifier: GPL-2.0
# Kconfig for Xilinx firmwares
menu "Zynq MPSoC Firmware Drivers"
depends on ARCH_ZYNQMP
config ZYNQMP_FIRMWARE
bool "Enable Xilinx Zynq MPSoC firmware interface"
help
Firmware interface driver is used by different
drivers to communicate with the firmware for
various platform management services.
Say yes to enable ZynqMP firmware interface driver.
If in doubt, say N.
config ZYNQMP_FIRMWARE_DEBUG
bool "Enable Xilinx Zynq MPSoC firmware debug APIs"
depends on ZYNQMP_FIRMWARE && DEBUG_FS
help
Say yes to enable ZynqMP firmware interface debug APIs.
If in doubt, say N.
endmenu
# SPDX-License-Identifier: GPL-2.0
# Makefile for Xilinx firmwares
obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o
obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx Zynq MPSoC Firmware layer for debugfs APIs
*
* Copyright (C) 2014-2018 Xilinx, Inc.
*
* Michal Simek <michal.simek@xilinx.com>
* Davorin Mista <davorin.mista@aggios.com>
* Jolly Shah <jollys@xilinx.com>
* Rajan Vaja <rajanv@xilinx.com>
*/
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/firmware/xlnx-zynqmp.h>
#include "zynqmp-debug.h"
#define PM_API_NAME_LEN 50
struct pm_api_info {
u32 api_id;
char api_name[PM_API_NAME_LEN];
char api_name_len;
};
static char debugfs_buf[PAGE_SIZE];
#define PM_API(id) {id, #id, strlen(#id)}
static struct pm_api_info pm_api_list[] = {
PM_API(PM_GET_API_VERSION),
PM_API(PM_QUERY_DATA),
};
struct dentry *firmware_debugfs_root;
/**
* zynqmp_pm_argument_value() - Extract argument value from a PM-API request
* @arg: Entered PM-API argument in string format
*
* Return: Argument value in unsigned integer format on success
* 0 otherwise
*/
static u64 zynqmp_pm_argument_value(char *arg)
{
u64 value;
if (!arg)
return 0;
if (!kstrtou64(arg, 0, &value))
return value;
return 0;
}
/**
* get_pm_api_id() - Extract API-ID from a PM-API request
* @pm_api_req: Entered PM-API argument in string format
* @pm_id: API-ID
*
* Return: 0 on success else error code
*/
static int get_pm_api_id(char *pm_api_req, u32 *pm_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {
if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,
pm_api_list[i].api_name_len)) {
*pm_id = pm_api_list[i].api_id;
break;
}
}
/* If no name was entered look for PM-API ID instead */
if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))
return -EINVAL;
return 0;
}
static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
{
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
u32 pm_api_version;
int ret;
struct zynqmp_pm_query_data qdata = {0};
if (!eemi_ops)
return -ENXIO;
switch (pm_id) {
case PM_GET_API_VERSION:
ret = eemi_ops->get_api_version(&pm_api_version);
sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
pm_api_version >> 16, pm_api_version & 0xffff);
break;
case PM_QUERY_DATA:
qdata.qid = pm_api_arg[0];
qdata.arg1 = pm_api_arg[1];
qdata.arg2 = pm_api_arg[2];
qdata.arg3 = pm_api_arg[3];
ret = eemi_ops->query_data(qdata, pm_api_ret);
if (ret)
break;
switch (qdata.qid) {
case PM_QID_CLOCK_GET_NAME:
sprintf(debugfs_buf, "Clock name = %s\n",
(char *)pm_api_ret);
break;
case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:
sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
pm_api_ret[1], pm_api_ret[2]);
break;
default:
sprintf(debugfs_buf,
"data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
pm_api_ret[0], pm_api_ret[1],
pm_api_ret[2], pm_api_ret[3]);
}
break;
default:
sprintf(debugfs_buf, "Unsupported PM-API request\n");
ret = -EINVAL;
}
return ret;
}
/**
* zynqmp_pm_debugfs_api_write() - debugfs write function
* @file: User file
* @ptr: User entered PM-API string
* @len: Length of the userspace buffer
* @off: Offset within the file
*
* Used for triggering pm api functions by writing
* echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or
* echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power
*
* Return: Number of bytes copied if PM-API request succeeds,
* the corresponding error code otherwise
*/
static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
const char __user *ptr, size_t len,
loff_t *off)
{
char *kern_buff, *tmp_buff;
char *pm_api_req;
u32 pm_id = 0;
u64 pm_api_arg[4] = {0, 0, 0, 0};
/* Return values from PM APIs calls */
u32 pm_api_ret[4] = {0, 0, 0, 0};
int ret;
int i = 0;
strcpy(debugfs_buf, "");
if (*off != 0 || len == 0)
return -EINVAL;
kern_buff = kzalloc(len, GFP_KERNEL);
if (!kern_buff)
return -ENOMEM;
tmp_buff = kern_buff;
ret = strncpy_from_user(kern_buff, ptr, len);
if (ret < 0) {
ret = -EFAULT;
goto err;
}
/* Read the API name from a user request */
pm_api_req = strsep(&kern_buff, " ");
ret = get_pm_api_id(pm_api_req, &pm_id);
if (ret < 0)
goto err;
/* Read node_id and arguments from the PM-API request */
pm_api_req = strsep(&kern_buff, " ");
while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
pm_api_req = strsep(&kern_buff, " ");
}
ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
err:
kfree(tmp_buff);
if (ret)
return ret;
return len;
}
/**
* zynqmp_pm_debugfs_api_read() - debugfs read function
* @file: User file
* @ptr: Requested pm_api_version string
* @len: Length of the userspace buffer
* @off: Offset within the file
*
* Return: Length of the version string on success
* else error code
*/
static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
size_t len, loff_t *off)
{
return simple_read_from_buffer(ptr, len, off, debugfs_buf,
strlen(debugfs_buf));
}
/* Setup debugfs fops */
static const struct file_operations fops_zynqmp_pm_dbgfs = {
.owner = THIS_MODULE,
.write = zynqmp_pm_debugfs_api_write,
.read = zynqmp_pm_debugfs_api_read,
};
/**
* zynqmp_pm_api_debugfs_init - Initialize debugfs interface
*
* Return: None
*/
void zynqmp_pm_api_debugfs_init(void)
{
/* Initialize debugfs interface */
firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);
debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,
&fops_zynqmp_pm_dbgfs);
}
/**
* zynqmp_pm_api_debugfs_exit - Remove debugfs interface
*
* Return: None
*/
void zynqmp_pm_api_debugfs_exit(void)
{
debugfs_remove_recursive(firmware_debugfs_root);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Xilinx Zynq MPSoC Firmware layer
*
* Copyright (C) 2014-2018 Xilinx
*
* Michal Simek <michal.simek@xilinx.com>
* Davorin Mista <davorin.mista@aggios.com>
* Jolly Shah <jollys@xilinx.com>
* Rajan Vaja <rajanv@xilinx.com>
*/
#ifndef __FIRMWARE_ZYNQMP_DEBUG_H__
#define __FIRMWARE_ZYNQMP_DEBUG_H__
#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE_DEBUG)
void zynqmp_pm_api_debugfs_init(void);
void zynqmp_pm_api_debugfs_exit(void);
#else
static inline void zynqmp_pm_api_debugfs_init(void) { }
static inline void zynqmp_pm_api_debugfs_exit(void) { }
#endif
#endif /* __FIRMWARE_ZYNQMP_DEBUG_H__ */
This diff is collapsed.
...@@ -327,8 +327,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, ...@@ -327,8 +327,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
return -EINVAL; return -EINVAL;
} }
ebid = devm_kzalloc(ebi->dev, ebid = devm_kzalloc(ebi->dev, struct_size(ebid, configs, numcs),
sizeof(*ebid) + (numcs * sizeof(*ebid->configs)),
GFP_KERNEL); GFP_KERNEL);
if (!ebid) if (!ebid)
return -ENOMEM; return -ENOMEM;
......
...@@ -98,6 +98,15 @@ config RESET_QCOM_AOSS ...@@ -98,6 +98,15 @@ config RESET_QCOM_AOSS
reset signals provided by AOSS for Modem, Venus, ADSP, reset signals provided by AOSS for Modem, Venus, ADSP,
GPU, Camera, Wireless, Display subsystem. Otherwise, say N. GPU, Camera, Wireless, Display subsystem. Otherwise, say N.
config RESET_QCOM_PDC
tristate "Qualcomm PDC Reset Driver"
depends on ARCH_QCOM || COMPILE_TEST
help
This enables the PDC (Power Domain Controller) reset driver
for Qualcomm Technologies Inc SDM845 SoCs. Say Y if you want
to control reset signals provided by PDC for Modem, Compute,
Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS.
config RESET_SIMPLE config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST bool "Simple Reset Controller Driver" if COMPILE_TEST
default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED
......
...@@ -16,6 +16,7 @@ obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o ...@@ -16,6 +16,7 @@ obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
......
...@@ -496,28 +496,29 @@ struct reset_control *__of_reset_control_get(struct device_node *node, ...@@ -496,28 +496,29 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
break; break;
} }
} }
of_node_put(args.np);
if (!rcdev) { if (!rcdev) {
mutex_unlock(&reset_list_mutex); rstc = ERR_PTR(-EPROBE_DEFER);
return ERR_PTR(-EPROBE_DEFER); goto out;
} }
if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
mutex_unlock(&reset_list_mutex); rstc = ERR_PTR(-EINVAL);
return ERR_PTR(-EINVAL); goto out;
} }
rstc_id = rcdev->of_xlate(rcdev, &args); rstc_id = rcdev->of_xlate(rcdev, &args);
if (rstc_id < 0) { if (rstc_id < 0) {
mutex_unlock(&reset_list_mutex); rstc = ERR_PTR(rstc_id);
return ERR_PTR(rstc_id); goto out;
} }
/* reset_list_mutex also protects the rcdev's reset_control list */ /* reset_list_mutex also protects the rcdev's reset_control list */
rstc = __reset_control_get_internal(rcdev, rstc_id, shared); rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
out:
mutex_unlock(&reset_list_mutex); mutex_unlock(&reset_list_mutex);
of_node_put(args.np);
return rstc; return rstc;
} }
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include <dt-bindings/reset/qcom,sdm845-pdc.h>
#define RPMH_PDC_SYNC_RESET 0x100
struct qcom_pdc_reset_map {
u8 bit;
};
struct qcom_pdc_reset_data {
struct reset_controller_dev rcdev;
struct regmap *regmap;
};
static const struct regmap_config sdm845_pdc_regmap_config = {
.name = "pdc-reset",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x20000,
.fast_io = true,
};
static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = {
[PDC_APPS_SYNC_RESET] = {0},
[PDC_SP_SYNC_RESET] = {1},
[PDC_AUDIO_SYNC_RESET] = {2},
[PDC_SENSORS_SYNC_RESET] = {3},
[PDC_AOP_SYNC_RESET] = {4},
[PDC_DEBUG_SYNC_RESET] = {5},
[PDC_GPU_SYNC_RESET] = {6},
[PDC_DISPLAY_SYNC_RESET] = {7},
[PDC_COMPUTE_SYNC_RESET] = {8},
[PDC_MODEM_SYNC_RESET] = {9},
};
static inline struct qcom_pdc_reset_data *to_qcom_pdc_reset_data(
struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct qcom_pdc_reset_data, rcdev);
}
static int qcom_pdc_control_assert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
BIT(sdm845_pdc_resets[idx].bit),
BIT(sdm845_pdc_resets[idx].bit));
}
static int qcom_pdc_control_deassert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
BIT(sdm845_pdc_resets[idx].bit), 0);
}
static const struct reset_control_ops qcom_pdc_reset_ops = {
.assert = qcom_pdc_control_assert,
.deassert = qcom_pdc_control_deassert,
};
static int qcom_pdc_reset_probe(struct platform_device *pdev)
{
struct qcom_pdc_reset_data *data;
struct device *dev = &pdev->dev;
void __iomem *base;
struct resource *res;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
data->regmap = devm_regmap_init_mmio(dev, base,
&sdm845_pdc_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "Unable to initialize regmap\n");
return PTR_ERR(data->regmap);
}
data->rcdev.owner = THIS_MODULE;
data->rcdev.ops = &qcom_pdc_reset_ops;
data->rcdev.nr_resets = ARRAY_SIZE(sdm845_pdc_resets);
data->rcdev.of_node = dev->of_node;
return devm_reset_controller_register(dev, &data->rcdev);
}
static const struct of_device_id qcom_pdc_reset_of_match[] = {
{ .compatible = "qcom,sdm845-pdc-global" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_pdc_reset_of_match);
static struct platform_driver qcom_pdc_reset_driver = {
.probe = qcom_pdc_reset_probe,
.driver = {
.name = "qcom_pdc_reset",
.of_match_table = qcom_pdc_reset_of_match,
},
};
module_platform_driver(qcom_pdc_reset_driver);
MODULE_DESCRIPTION("Qualcomm PDC Reset Driver");
MODULE_LICENSE("GPL v2");
...@@ -18,7 +18,7 @@ obj-y += qcom/ ...@@ -18,7 +18,7 @@ obj-y += qcom/
obj-y += renesas/ obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-y += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_SOC_TI) += ti/ obj-$(CONFIG_SOC_TI) += ti/
obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_U8500) += ux500/
......
...@@ -10,7 +10,7 @@ config OWL_PM_DOMAINS ...@@ -10,7 +10,7 @@ config OWL_PM_DOMAINS
select PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS
help help
Say 'y' here to enable support for Smart Power System (SPS) Say 'y' here to enable support for Smart Power System (SPS)
power-gating on Actions Semiconductor S500 SoC. power-gating on Actions Semiconductor S500, S700 and S900 SoCs.
If unsure, say 'n'. If unsure, say 'n'.
endif endif
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o
obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o
// SPDX-License-Identifier: GPL-2.0+
/* /*
* Actions Semi Owl Smart Power System (SPS) shared helpers * Actions Semi Owl Smart Power System (SPS) shared helpers
* *
...@@ -5,11 +6,6 @@ ...@@ -5,11 +6,6 @@
* Author: Actions Semi, Inc. * Author: Actions Semi, Inc.
* *
* Copyright (c) 2017 Andreas Färber * Copyright (c) 2017 Andreas Färber
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/ */
#include <linux/delay.h> #include <linux/delay.h>
......
// SPDX-License-Identifier: GPL-2.0+
/* /*
* Actions Semi Owl Smart Power System (SPS) * Actions Semi Owl Smart Power System (SPS)
* *
...@@ -5,11 +6,6 @@ ...@@ -5,11 +6,6 @@
* Author: Actions Semi, Inc. * Author: Actions Semi, Inc.
* *
* Copyright (c) 2017 Andreas Färber * Copyright (c) 2017 Andreas Färber
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/ */
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -18,6 +14,7 @@ ...@@ -18,6 +14,7 @@
#include <linux/soc/actions/owl-sps.h> #include <linux/soc/actions/owl-sps.h>
#include <dt-bindings/power/owl-s500-powergate.h> #include <dt-bindings/power/owl-s500-powergate.h>
#include <dt-bindings/power/owl-s700-powergate.h> #include <dt-bindings/power/owl-s700-powergate.h>
#include <dt-bindings/power/owl-s900-powergate.h>
struct owl_sps_domain_info { struct owl_sps_domain_info {
const char *name; const char *name;
...@@ -244,9 +241,66 @@ static const struct owl_sps_info s700_sps_info = { ...@@ -244,9 +241,66 @@ static const struct owl_sps_info s700_sps_info = {
.domains = s700_sps_domains, .domains = s700_sps_domains,
}; };
static const struct owl_sps_domain_info s900_sps_domains[] = {
[S900_PD_GPU_B] = {
.name = "GPU_B",
.pwr_bit = 3,
},
[S900_PD_VCE] = {
.name = "VCE",
.pwr_bit = 4,
},
[S900_PD_SENSOR] = {
.name = "SENSOR",
.pwr_bit = 5,
},
[S900_PD_VDE] = {
.name = "VDE",
.pwr_bit = 6,
},
[S900_PD_HDE] = {
.name = "HDE",
.pwr_bit = 7,
},
[S900_PD_USB3] = {
.name = "USB3",
.pwr_bit = 8,
},
[S900_PD_DDR0] = {
.name = "DDR0",
.pwr_bit = 9,
},
[S900_PD_DDR1] = {
.name = "DDR1",
.pwr_bit = 10,
},
[S900_PD_DE] = {
.name = "DE",
.pwr_bit = 13,
},
[S900_PD_NAND] = {
.name = "NAND",
.pwr_bit = 14,
},
[S900_PD_USB2_H0] = {
.name = "USB2_H0",
.pwr_bit = 15,
},
[S900_PD_USB2_H1] = {
.name = "USB2_H1",
.pwr_bit = 16,
},
};
static const struct owl_sps_info s900_sps_info = {
.num_domains = ARRAY_SIZE(s900_sps_domains),
.domains = s900_sps_domains,
};
static const struct of_device_id owl_sps_of_matches[] = { static const struct of_device_id owl_sps_of_matches[] = {
{ .compatible = "actions,s500-sps", .data = &s500_sps_info }, { .compatible = "actions,s500-sps", .data = &s500_sps_info },
{ .compatible = "actions,s700-sps", .data = &s700_sps_info }, { .compatible = "actions,s700-sps", .data = &s700_sps_info },
{ .compatible = "actions,s900-sps", .data = &s900_sps_info },
{ } { }
}; };
......
menu "Amlogic SoC drivers" menu "Amlogic SoC drivers"
config MESON_CANVAS
tristate "Amlogic Meson Canvas driver"
depends on ARCH_MESON || COMPILE_TEST
default n
help
Say yes to support the canvas IP for Amlogic SoCs.
config MESON_GX_SOCINFO config MESON_GX_SOCINFO
bool "Amlogic Meson GX SoC Information driver" bool "Amlogic Meson GX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST depends on ARCH_MESON || COMPILE_TEST
......
obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
This diff is collapsed.
...@@ -50,13 +50,10 @@ static void unregister_dpio_irq_handlers(struct fsl_mc_device *dpio_dev) ...@@ -50,13 +50,10 @@ static void unregister_dpio_irq_handlers(struct fsl_mc_device *dpio_dev)
static int register_dpio_irq_handlers(struct fsl_mc_device *dpio_dev, int cpu) static int register_dpio_irq_handlers(struct fsl_mc_device *dpio_dev, int cpu)
{ {
struct dpio_priv *priv;
int error; int error;
struct fsl_mc_device_irq *irq; struct fsl_mc_device_irq *irq;
cpumask_t mask; cpumask_t mask;
priv = dev_get_drvdata(&dpio_dev->dev);
irq = dpio_dev->irqs[0]; irq = dpio_dev->irqs[0];
error = devm_request_irq(&dpio_dev->dev, error = devm_request_irq(&dpio_dev->dev,
irq->msi_desc->irq, irq->msi_desc->irq,
......
menuconfig FSL_DPAA menuconfig FSL_DPAA
bool "QorIQ DPAA1 framework support" bool "QorIQ DPAA1 framework support"
depends on (FSL_SOC_BOOKE || ARCH_LAYERSCAPE) depends on ((FSL_SOC_BOOKE || ARCH_LAYERSCAPE) && ARCH_DMA_ADDR_T_64BIT)
select GENERIC_ALLOCATOR select GENERIC_ALLOCATOR
help help
The Freescale Data Path Acceleration Architecture (DPAA) is a set of The Freescale Data Path Acceleration Architecture (DPAA) is a set of
......
...@@ -562,11 +562,9 @@ static int bman_create_portal(struct bman_portal *portal, ...@@ -562,11 +562,9 @@ static int bman_create_portal(struct bman_portal *portal,
dev_err(c->dev, "request_irq() failed\n"); dev_err(c->dev, "request_irq() failed\n");
goto fail_irq; goto fail_irq;
} }
if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
irq_set_affinity(c->irq, cpumask_of(c->cpu))) { if (dpaa_set_portal_irq_affinity(c->dev, c->irq, c->cpu))
dev_err(c->dev, "irq_set_affinity() failed\n");
goto fail_affinity; goto fail_affinity;
}
/* Need RCR to be empty before continuing */ /* Need RCR to be empty before continuing */
ret = bm_rcr_get_fill(p); ret = bm_rcr_get_fill(p);
......
...@@ -65,7 +65,9 @@ static int bman_offline_cpu(unsigned int cpu) ...@@ -65,7 +65,9 @@ static int bman_offline_cpu(unsigned int cpu)
if (!pcfg) if (!pcfg)
return 0; return 0;
irq_set_affinity(pcfg->irq, cpumask_of(0)); /* use any other online CPU */
cpu = cpumask_any_but(cpu_online_mask, cpu);
irq_set_affinity(pcfg->irq, cpumask_of(cpu));
return 0; return 0;
} }
...@@ -91,7 +93,15 @@ static int bman_portal_probe(struct platform_device *pdev) ...@@ -91,7 +93,15 @@ static int bman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node; struct device_node *node = dev->of_node;
struct bm_portal_config *pcfg; struct bm_portal_config *pcfg;
struct resource *addr_phys[2]; struct resource *addr_phys[2];
int irq, cpu; int irq, cpu, err;
err = bman_is_probed();
if (!err)
return -EPROBE_DEFER;
if (err < 0) {
dev_err(&pdev->dev, "failing probe due to bman probe error\n");
return -ENODEV;
}
pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL); pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
if (!pcfg) if (!pcfg)
......
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.
...@@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu) ...@@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu)
cpumask_t mask; cpumask_t mask;
bool use_scm_power_down = false; bool use_scm_power_down = false;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
for (i = 0; ; i++) { for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
if (!state_node) if (!state_node)
......
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