1
0
Fork 0

ARM: SoC driver updates for 4.17

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.
 
 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJb1zEhAAoJEGCrR//JCVInnYQP/1pPXWsR/DV4COf4kGJFSAFn
 EfHXJM1vKtb7AWl6SClpHFlUMt+fvL+dzDNJ9aeRr2GjcuWfzKDcrBM1ZvM70I31
 C1Oc3b6OXEERCozDpRg/Vt8OpIvvWnVpaVffS9E5y6KqF8KZ0UbpWIxUJ87ik44D
 UvNXYOU/LUGPxR1UFm5rm2zWF4i+rBvqnpVaXbeOsXsLElzxXVfv2ymhhqIpo2ws
 o6e00DSjUImg8hLL4HCGFs2EX1KSD+oFzYaOHIE0/DEaiOnxVOpMSRhX2tZ+tRRb
 DekbjL+wz5gOAKJTQfQ2sNNkOuK8WFqmE5G0RJ0iYPXuNsB/17UNb2bhTJeqGdcD
 dqCQBLQuDUD2iHJ/d4RK5Kx3a8h2X63n5bdefgF5UX/2RBpXwFk1QtHr8X0DuY8c
 o/dPGFNBOn3egzMyXrD5VEtnaTwK1Y6/h09qfuOOF1ZuYDmELKRkWMV9l8dIsvd8
 ANjaw5B8MOUAf8DccBmPgUGu0XLCDyuFGqNVd9Kj5u3az+tyggIsgkEjWg1pxTv0
 7dDDyv4Ara1V1HVDZ23l3CgmYCZQx2R/vdpX/DjuDPGEHGjZ5s2TW8P6oegdxtIh
 LcTonNoTsRYzMrGD/aqhG/8fYsAScXePa3CLKl1Hrl+wFVV0XcaggH23GwD/k+7S
 eDBrEzLkOTxM+WXvsvKY
 =c/PQ
 -----END PGP SIGNATURE-----

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2018-10-29 15:16:01 -07:00
commit b22b6beae6
129 changed files with 7174 additions and 785 deletions

View File

@ -0,0 +1,183 @@
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>;
};

View File

@ -45,11 +45,15 @@ Optional Properties:
debug_messages - Map the Debug message region
- reg: register space corresponding to the debug_messages
- 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):
-------------
pmmc: pmmc {
compatible = "ti,k2g-sci";
ti,host-id = <2>;
mbox-names = "rx", "tx";
mboxes= <&msgmgr &msgmgr_proxy_pmmc_rx>,
<&msgmgr &msgmgr_proxy_pmmc_tx>;

View File

@ -16,11 +16,26 @@ Properties:
- reg:
Usage: required
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:
cache-controller@1100000 {
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>;
};

View File

@ -7,16 +7,23 @@ assorted actions.
Required properties:
- compatible: must contain one of the following:
* "qcom,scm-apq8064" for APQ8064 platforms
* "qcom,scm-msm8660" for MSM8660 platforms
* "qcom,scm-msm8690" for MSM8690 platforms
* "qcom,scm-msm8996" for MSM8996 platforms
* "qcom,scm-ipq4019" for IPQ4019 platforms
* "qcom,scm" for later processors (MSM8916, APQ8084, MSM8974, etc)
- clocks: One to three clocks may be required based on compatible.
* No clock required for "qcom,scm-msm8996", "qcom,scm-ipq4019"
* Only core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660", and "qcom,scm-msm8960"
* Core, iface, and bus clocks required for "qcom,scm"
* "qcom,scm-apq8064"
* "qcom,scm-apq8084"
* "qcom,scm-msm8660"
* "qcom,scm-msm8916"
* "qcom,scm-msm8960"
* "qcom,scm-msm8974"
* "qcom,scm-msm8996"
* "qcom,scm-msm8998"
* "qcom,scm-ipq4019"
* "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 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
@ -26,8 +33,10 @@ Example for MSM8916:
firmware {
scm {
compatible = "qcom,scm";
clocks = <&gcc GCC_CRYPTO_CLK> , <&gcc GCC_CRYPTO_AXI_CLK>, <&gcc GCC_CRYPTO_AHB_CLK>;
compatible = "qcom,msm8916", "qcom,scm";
clocks = <&gcc GCC_CRYPTO_CLK> ,
<&gcc GCC_CRYPTO_AXI_CLK>,
<&gcc GCC_CRYPTO_AHB_CLK>;
clock-names = "core", "bus", "iface";
};
};

View File

@ -0,0 +1,82 @@
-----------------------------------------------------------------
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";
};
};
};

View File

@ -8,7 +8,9 @@ Required properties:
- compatible: Should be "renesas,<soctype>-apmu", "renesas,apmu" as fallback.
Examples with soctypes are:
- "renesas,r8a7743-apmu" (RZ/G1M)
- "renesas,r8a7744-apmu" (RZ/G1N)
- "renesas,r8a7745-apmu" (RZ/G1E)
- "renesas,r8a77470-apmu" (RZ/G1C)
- "renesas,r8a7790-apmu" (R-Car H2)
- "renesas,r8a7791-apmu" (R-Car M2-W)
- "renesas,r8a7792-apmu" (R-Car V2H)

View File

@ -8,8 +8,11 @@ and various coprocessors.
Required properties:
- compatible: Must contain exactly one of the following:
- "renesas,r8a7743-sysc" (RZ/G1M)
- "renesas,r8a7744-sysc" (RZ/G1N)
- "renesas,r8a7745-sysc" (RZ/G1E)
- "renesas,r8a77470-sysc" (RZ/G1C)
- "renesas,r8a774a1-sysc" (RZ/G2M)
- "renesas,r8a774c0-sysc" (RZ/G2E)
- "renesas,r8a7779-sysc" (R-Car H1)
- "renesas,r8a7790-sysc" (R-Car H2)
- "renesas,r8a7791-sysc" (R-Car M2-W)

View File

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

View File

@ -16,8 +16,11 @@ Required properties:
- "renesas,<soctype>-rst" for R-Car Gen2 and Gen3, and RZ/G
Examples with soctypes are:
- "renesas,r8a7743-rst" (RZ/G1M)
- "renesas,r8a7744-rst" (RZ/G1N)
- "renesas,r8a7745-rst" (RZ/G1E)
- "renesas,r8a77470-rst" (RZ/G1C)
- "renesas,r8a774a1-rst" (RZ/G2M)
- "renesas,r8a774c0-rst" (RZ/G2E)
- "renesas,r8a7778-reset-wdt" (R-Car M1A)
- "renesas,r8a7779-reset-wdt" (R-Car H1)
- "renesas,r8a7790-rst" (R-Car H2)

View File

@ -0,0 +1,29 @@
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>;
};

View File

@ -19,10 +19,12 @@ IP Pairing
Required properties in pwrap device node.
- compatible:
"mediatek,mt2701-pwrap" for MT2701/7623 SoCs
"mediatek,mt6765-pwrap" for MT6765 SoCs
"mediatek,mt6797-pwrap" for MT6797 SoCs
"mediatek,mt7622-pwrap" for MT7622 SoCs
"mediatek,mt8135-pwrap" for MT8135 SoCs
"mediatek,mt8173-pwrap" for MT8173 SoCs
"mediatek,mt8183-pwrap" for MT8183 SoCs
- interrupts: IRQ for pwrap in SOC
- reg-names: Must include the following entries:
"pwrap": Main registers base

View File

@ -18,6 +18,7 @@ Required properties:
- "allwinner,sun8i-h3-system-control"
- "allwinner,sun50i-a64-sram-controller" (deprecated)
- "allwinner,sun50i-a64-system-control"
- "allwinner,sun50i-h6-system-control", "allwinner,sun50i-a64-system-control"
- reg : sram controller register offset + length
SRAM nodes
@ -54,6 +55,9 @@ The valid sections compatible for H3 are:
The valid sections compatible for A64 are:
- 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
---------------------------

View File

@ -12,6 +12,8 @@ Required Properties:
- "renesas,tmu-r8a7740" for the r8a7740 TMU
- "renesas,tmu-r8a7778" for the r8a7778 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.
This is a fallback for the above renesas,tmu-* entries

View File

@ -0,0 +1,67 @@
---------------------------------------------------------------------
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

View File

@ -1471,7 +1471,9 @@ F: arch/arm/mach-mxs/
F: arch/arm/boot/dts/imx*
F: arch/arm/configs/imx*_defconfig
F: drivers/clk/imx/
F: drivers/firmware/imx/
F: drivers/soc/imx/
F: include/linux/firmware/imx/
F: include/soc/imx/
ARM/FREESCALE VYBRID ARM ARCHITECTURE
@ -5401,6 +5403,14 @@ L: linux-edac@vger.kernel.org
S: Maintained
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
M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)

View File

@ -55,6 +55,12 @@ config ARCH_R7S72100
select SYS_SUPPORTS_SH_MTU2
select RENESAS_OSTM
config ARCH_R7S9210
bool "RZ/A2 (R7S9210)"
select PM
select PM_GENERIC_DOMAINS
select RENESAS_OSTM
config ARCH_R8A73A4
bool "R-Mobile APE6 (R8A73A40)"
select ARCH_RMOBILE

View File

@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o
obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o
obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o
obj-$(CONFIG_ARCH_R7S72100) += setup-r7s72100.o
obj-$(CONFIG_ARCH_R7S9210) += setup-r7s9210.o
# CPU reset vector handling objects
cpu-y := platsmp.o headsmp.o

View File

@ -0,0 +1,27 @@
// 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

View File

@ -302,6 +302,7 @@ config ARCH_ZX
config ARCH_ZYNQMP
bool "Xilinx ZynqMP Family"
select ZYNQMP_FIRMWARE
help
This enables support for Xilinx ZynqMP Family

View File

@ -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) {
if (!child->name)
continue;
ret = weim_timing_setup(child, base, devtype);
if (ret)
dev_warn(&pdev->dev, "%pOF set timing failed.\n",

View File

@ -701,69 +701,7 @@ awake:
return error;
}
#ifdef CONFIG_PM_SLEEP
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)
static int __maybe_unused sysc_noirq_suspend(struct device *dev)
{
struct sysc *ddata;
@ -772,21 +710,10 @@ static int sysc_noirq_suspend(struct device *dev)
if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
return 0;
if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER))
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);
return pm_runtime_force_suspend(dev);
}
static int sysc_noirq_resume(struct device *dev)
static int __maybe_unused sysc_noirq_resume(struct device *dev)
{
struct sysc *ddata;
@ -795,24 +722,10 @@ static int sysc_noirq_resume(struct device *dev)
if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
return 0;
if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER))
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;
return pm_runtime_force_resume(dev);
}
#endif
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_RUNTIME_PM_OPS(sysc_runtime_suspend,
sysc_runtime_resume,
@ -845,28 +758,8 @@ struct sysc_revision_quirk {
}
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() */
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("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
@ -881,38 +774,70 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
/* 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("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
/* 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),
/* 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("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
#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("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("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("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, 0x44307b02, 0xffffffff, 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("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, 0x40002903, 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("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, 0),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 0),
SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0),
#endif
};
@ -1221,8 +1146,8 @@ static int sysc_child_suspend_noirq(struct device *dev)
if (!pm_runtime_status_suspended(dev)) {
error = pm_generic_runtime_suspend(dev);
if (error) {
dev_warn(dev, "%s busy at %i: %i\n",
__func__, __LINE__, error);
dev_dbg(dev, "%s busy at %i: %i\n",
__func__, __LINE__, error);
return 0;
}

View File

@ -299,5 +299,6 @@ source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig"
source "drivers/clk/zynqmp/Kconfig"
endmenu

View File

@ -108,3 +108,4 @@ obj-$(CONFIG_X86) += x86/
endif
obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/

View File

@ -0,0 +1,10 @@
# 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.

View File

@ -0,0 +1,4 @@
# 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

View File

@ -0,0 +1,144 @@
// 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;
}

View File

@ -0,0 +1,141 @@
// 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);

View File

@ -0,0 +1,68 @@
/* 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

View File

@ -0,0 +1,716 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Zynq UltraScale+ MPSoC clock controller
*
* Copyright (C) 2016-2018 Xilinx
*
* Based on drivers/clk/zynq/clkc.c
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "clk-zynqmp.h"
#define MAX_PARENT 100
#define MAX_NODES 6
#define MAX_NAME_LEN 50
#define CLK_TYPE_SHIFT 2
#define PM_API_PAYLOAD_LEN 3
#define NA_PARENT 0xFFFFFFFF
#define DUMMY_PARENT 0xFFFFFFFE
#define CLK_TYPE_FIELD_LEN 4
#define CLK_TOPOLOGY_NODE_OFFSET 16
#define NODES_PER_RESP 3
#define CLK_TYPE_FIELD_MASK 0xF
#define CLK_FLAG_FIELD_MASK GENMASK(21, 8)
#define CLK_TYPE_FLAG_FIELD_MASK GENMASK(31, 24)
#define CLK_PARENTS_ID_LEN 16
#define CLK_PARENTS_ID_MASK 0xFFFF
/* Flags for parents */
#define PARENT_CLK_SELF 0
#define PARENT_CLK_NODE1 1
#define PARENT_CLK_NODE2 2
#define PARENT_CLK_NODE3 3
#define PARENT_CLK_NODE4 4
#define PARENT_CLK_EXTERNAL 5
#define END_OF_CLK_NAME "END_OF_CLK"
#define END_OF_TOPOLOGY_NODE 1
#define END_OF_PARENTS 1
#define RESERVED_CLK_NAME ""
#define CLK_VALID_MASK 0x1
enum clk_type {
CLK_TYPE_OUTPUT,
CLK_TYPE_EXTERNAL,
};
/**
* struct clock_parent - Clock parent
* @name: Parent name
* @id: Parent clock ID
* @flag: Parent flags
*/
struct clock_parent {
char name[MAX_NAME_LEN];
int id;
u32 flag;
};
/**
* struct zynqmp_clock - Clock
* @clk_name: Clock name
* @valid: Validity flag of clock
* @type: Clock type (Output/External)
* @node: Clock topology nodes
* @num_nodes: Number of nodes present in topology
* @parent: Parent of clock
* @num_parents: Number of parents of clock
*/
struct zynqmp_clock {
char clk_name[MAX_NAME_LEN];
u32 valid;
enum clk_type type;
struct clock_topology node[MAX_NODES];
u32 num_nodes;
struct clock_parent parent[MAX_PARENT];
u32 num_parents;
};
static const char clk_type_postfix[][10] = {
[TYPE_INVALID] = "",
[TYPE_MUX] = "_mux",
[TYPE_GATE] = "",
[TYPE_DIV1] = "_div1",
[TYPE_DIV2] = "_div2",
[TYPE_FIXEDFACTOR] = "_ff",
[TYPE_PLL] = ""
};
static struct clk_hw *(* const clk_topology[]) (const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
= {
[TYPE_INVALID] = NULL,
[TYPE_MUX] = zynqmp_clk_register_mux,
[TYPE_PLL] = zynqmp_clk_register_pll,
[TYPE_FIXEDFACTOR] = zynqmp_clk_register_fixed_factor,
[TYPE_DIV1] = zynqmp_clk_register_divider,
[TYPE_DIV2] = zynqmp_clk_register_divider,
[TYPE_GATE] = zynqmp_clk_register_gate
};
static struct zynqmp_clock *clock;
static struct clk_hw_onecell_data *zynqmp_data;
static unsigned int clock_max_idx;
static const struct zynqmp_eemi_ops *eemi_ops;
/**
* zynqmp_is_valid_clock() - Check whether clock is valid or not
* @clk_id: Clock index
*
* Return: 1 if clock is valid, 0 if clock is invalid else error code
*/
static inline int zynqmp_is_valid_clock(u32 clk_id)
{
if (clk_id > clock_max_idx)
return -ENODEV;
return clock[clk_id].valid;
}
/**
* zynqmp_get_clock_name() - Get name of clock from Clock index
* @clk_id: Clock index
* @clk_name: Name of clock
*
* Return: 0 on success else error code
*/
static int zynqmp_get_clock_name(u32 clk_id, char *clk_name)
{
int ret;
ret = zynqmp_is_valid_clock(clk_id);
if (ret == 1) {
strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN);
return 0;
}
return ret == 0 ? -EINVAL : ret;
}
/**
* zynqmp_get_clock_type() - Get type of clock
* @clk_id: Clock index
* @type: Clock type: CLK_TYPE_OUTPUT or CLK_TYPE_EXTERNAL
*
* Return: 0 on success else error code
*/
static int zynqmp_get_clock_type(u32 clk_id, u32 *type)
{
int ret;
ret = zynqmp_is_valid_clock(clk_id);
if (ret == 1) {
*type = clock[clk_id].type;
return 0;
}
return ret == 0 ? -EINVAL : ret;
}
/**
* zynqmp_pm_clock_get_num_clocks() - Get number of clocks in system
* @nclocks: Number of clocks in system/board.
*
* Call firmware API to get number of clocks.
*
* Return: 0 on success else error code.
*/
static int zynqmp_pm_clock_get_num_clocks(u32 *nclocks)
{
struct zynqmp_pm_query_data qdata = {0};
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
qdata.qid = PM_QID_CLOCK_GET_NUM_CLOCKS;
ret = eemi_ops->query_data(qdata, ret_payload);
*nclocks = ret_payload[1];
return ret;
}
/**
* zynqmp_pm_clock_get_name() - Get the name of clock for given id
* @clock_id: ID of the clock to be queried
* @name: Name of given clock
*
* This function is used to get name of clock specified by given
* clock ID.
*
* Return: Returns 0, in case of error name would be 0
*/
static int zynqmp_pm_clock_get_name(u32 clock_id, char *name)
{
struct zynqmp_pm_query_data qdata = {0};
u32 ret_payload[PAYLOAD_ARG_CNT];
qdata.qid = PM_QID_CLOCK_GET_NAME;
qdata.arg1 = clock_id;
eemi_ops->query_data(qdata, ret_payload);
memcpy(name, ret_payload, CLK_GET_NAME_RESP_LEN);
return 0;
}
/**
* zynqmp_pm_clock_get_topology() - Get the topology of clock for given id
* @clock_id: ID of the clock to be queried
* @index: Node index of clock topology
* @topology: Buffer to store nodes in topology and flags
*
* This function is used to get topology information for the clock
* specified by given clock ID.
*
* This API will return 3 node of topology with a single response. To get
* other nodes, master should call same API in loop with new
* index till error is returned. E.g First call should have
* index 0 which will return nodes 0,1 and 2. Next call, index
* should be 3 which will return nodes 3,4 and 5 and so on.
*
* Return: 0 on success else error+reason
*/
static int zynqmp_pm_clock_get_topology(u32 clock_id, u32 index, u32 *topology)
{
struct zynqmp_pm_query_data qdata = {0};
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY;
qdata.arg1 = clock_id;
qdata.arg2 = index;
ret = eemi_ops->query_data(qdata, ret_payload);
memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4);
return ret;
}
/**
* zynqmp_clk_register_fixed_factor() - Register fixed factor with the
* clock framework
* @name: Name of this clock
* @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_fixed_factor(const char *name, u32 clk_id,
const char * const *parents,
u8 num_parents,
const struct clock_topology *nodes)
{
u32 mult, div;
struct clk_hw *hw;
struct zynqmp_pm_query_data qdata = {0};
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS;
qdata.arg1 = clk_id;
ret = eemi_ops->query_data(qdata, ret_payload);
mult = ret_payload[1];
div = ret_payload[2];
hw = clk_hw_register_fixed_factor(NULL, name,
parents[0],
nodes->flag, mult,
div);
return hw;
}
/**
* zynqmp_pm_clock_get_parents() - Get the first 3 parents of clock for given id
* @clock_id: Clock ID
* @index: Parent index
* @parents: 3 parents of the given clock
*
* This function is used to get 3 parents for the clock specified by
* given clock ID.
*
* This API will return 3 parents with a single response. To get
* other parents, master should call same API in loop with new
* parent index till error is returned. E.g First call should have
* index 0 which will return parents 0,1 and 2. Next call, index
* should be 3 which will return parent 3,4 and 5 and so on.
*
* Return: 0 on success else error+reason
*/
static int zynqmp_pm_clock_get_parents(u32 clock_id, u32 index, u32 *parents)
{
struct zynqmp_pm_query_data qdata = {0};
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
qdata.qid = PM_QID_CLOCK_GET_PARENTS;
qdata.arg1 = clock_id;
qdata.arg2 = index;
ret = eemi_ops->query_data(qdata, ret_payload);
memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4);
return ret;
}
/**
* zynqmp_pm_clock_get_attributes() - Get the attributes of clock for given id
* @clock_id: Clock ID
* @attr: Clock attributes
*
* This function is used to get clock's attributes(e.g. valid, clock type, etc).
*
* Return: 0 on success else error+reason
*/
static int zynqmp_pm_clock_get_attributes(u32 clock_id, u32 *attr)
{
struct zynqmp_pm_query_data qdata = {0};
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES;
qdata.arg1 = clock_id;
ret = eemi_ops->query_data(qdata, ret_payload);
memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4);
return ret;
}
/**
* __zynqmp_clock_get_topology() - Get topology data of clock from firmware
* response data
* @topology: Clock topology
* @data: Clock topology data received from firmware
* @nnodes: Number of nodes
*
* Return: 0 on success else error+reason
*/
static int __zynqmp_clock_get_topology(struct clock_topology *topology,
u32 *data, u32 *nnodes)
{
int i;
for (i = 0; i < PM_API_PAYLOAD_LEN; i++) {
if (!(data[i] & CLK_TYPE_FIELD_MASK))
return END_OF_TOPOLOGY_NODE;
topology[*nnodes].type = data[i] & CLK_TYPE_FIELD_MASK;
topology[*nnodes].flag = FIELD_GET(CLK_FLAG_FIELD_MASK,
data[i]);
topology[*nnodes].type_flag =
FIELD_GET(CLK_TYPE_FLAG_FIELD_MASK, data[i]);
(*nnodes)++;
}
return 0;
}
/**
* zynqmp_clock_get_topology() - Get topology of clock from firmware using
* PM_API
* @clk_id: Clock index
* @topology: Clock topology
* @num_nodes: Number of nodes
*
* Return: 0 on success else error+reason
*/
static int zynqmp_clock_get_topology(u32 clk_id,
struct clock_topology *topology,
u32 *num_nodes)
{
int j, ret;
u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
*num_nodes = 0;
for (j = 0; j <= MAX_NODES; j += 3) {
ret = zynqmp_pm_clock_get_topology(clk_id, j, pm_resp);
if (ret)
return ret;
ret = __zynqmp_clock_get_topology(topology, pm_resp, num_nodes);
if (ret == END_OF_TOPOLOGY_NODE)
return 0;
}
return 0;
}
/**
* __zynqmp_clock_get_topology() - Get parents info of clock from firmware
* response data
* @parents: Clock parents
* @data: Clock parents data received from firmware
* @nparent: Number of parent
*
* Return: 0 on success else error+reason
*/
static int __zynqmp_clock_get_parents(struct clock_parent *parents, u32 *data,
u32 *nparent)
{
int i;
struct clock_parent *parent;
for (i = 0; i < PM_API_PAYLOAD_LEN; i++) {
if (data[i] == NA_PARENT)
return END_OF_PARENTS;
parent = &parents[i];
parent->id = data[i] & CLK_PARENTS_ID_MASK;
if (data[i] == DUMMY_PARENT) {
strcpy(parent->name, "dummy_name");
parent->flag = 0;
} else {
parent->flag = data[i] >> CLK_PARENTS_ID_LEN;
if (zynqmp_get_clock_name(parent->id, parent->name))
continue;
}
*nparent += 1;
}
return 0;
}
/**
* zynqmp_clock_get_parents() - Get parents info from firmware using PM_API
* @clk_id: Clock index
* @parents: Clock parents
* @num_parents: Total number of parents
*
* Return: 0 on success else error+reason
*/
static int zynqmp_clock_get_parents(u32 clk_id, struct clock_parent *parents,
u32 *num_parents)
{
int j = 0, ret;
u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
*num_parents = 0;
do {
/* Get parents from firmware */
ret = zynqmp_pm_clock_get_parents(clk_id, j, pm_resp);
if (ret)
return ret;
ret = __zynqmp_clock_get_parents(&parents[j], pm_resp,
num_parents);
if (ret == END_OF_PARENTS)
return 0;
j += PM_API_PAYLOAD_LEN;
} while (*num_parents <= MAX_PARENT);
return 0;
}
/**
* zynqmp_get_parent_list() - Create list of parents name
* @np: Device node
* @clk_id: Clock index
* @parent_list: List of parent's name
* @num_parents: Total number of parents
*
* Return: 0 on success else error+reason
*/
static int zynqmp_get_parent_list(struct device_node *np, u32 clk_id,
const char **parent_list, u32 *num_parents)
{
int i = 0, ret;
u32 total_parents = clock[clk_id].num_parents;
struct clock_topology *clk_nodes;
struct clock_parent *parents;
clk_nodes = clock[clk_id].node;
parents = clock[clk_id].parent;
for (i = 0; i < total_parents; i++) {
if (!parents[i].flag) {
parent_list[i] = parents[i].name;
} else if (parents[i].flag == PARENT_CLK_EXTERNAL) {
ret = of_property_match_string(np, "clock-names",
parents[i].name);
if (ret < 0)
strcpy(parents[i].name, "dummy_name");
parent_list[i] = parents[i].name;
} else {
strcat(parents[i].name,
clk_type_postfix[clk_nodes[parents[i].flag - 1].
type]);
parent_list[i] = parents[i].name;
}
}
*num_parents = total_parents;
return 0;
}
/**
* zynqmp_register_clk_topology() - Register clock topology
* @clk_id: Clock index
* @clk_name: Clock Name
* @num_parents: Total number of parents
* @parent_names: List of parents name
*
* Return: Returns either clock hardware or error+reason
*/
static struct clk_hw *zynqmp_register_clk_topology(int clk_id, char *clk_name,
int num_parents,
const char **parent_names)
{
int j;
u32 num_nodes;
char *clk_out = NULL;
struct clock_topology *nodes;
struct clk_hw *hw = NULL;
nodes = clock[clk_id].node;
num_nodes = clock[clk_id].num_nodes;
for (j = 0; j < num_nodes; j++) {
/*
* Clock name received from firmware is output clock name.
* Intermediate clock names are postfixed with type of clock.
*/
if (j != (num_nodes - 1)) {
clk_out = kasprintf(GFP_KERNEL, "%s%s", clk_name,
clk_type_postfix[nodes[j].type]);
} else {
clk_out = kasprintf(GFP_KERNEL, "%s", clk_name);
}
if (!clk_topology[nodes[j].type])
continue;
hw = (*clk_topology[nodes[j].type])(clk_out, clk_id,
parent_names,
num_parents,
&nodes[j]);
if (IS_ERR(hw))
pr_warn_once("%s() %s register fail with %ld\n",
__func__, clk_name, PTR_ERR(hw));
parent_names[0] = clk_out;
}
kfree(clk_out);
return hw;
}
/**
* zynqmp_register_clocks() - Register clocks
* @np: Device node
*
* Return: 0 on success else error code
*/
static int zynqmp_register_clocks(struct device_node *np)
{
int ret;
u32 i, total_parents = 0, type = 0;
const char *parent_names[MAX_PARENT];
for (i = 0; i < clock_max_idx; i++) {
char clk_name[MAX_NAME_LEN];
/* get clock name, continue to next clock if name not found */
if (zynqmp_get_clock_name(i, clk_name))
continue;
/* Check if clock is valid and output clock.
* Do not register invalid or external clock.
*/
ret = zynqmp_get_clock_type(i, &type);
if (ret || type != CLK_TYPE_OUTPUT)
continue;
/* Get parents of clock*/
if (zynqmp_get_parent_list(np, i, parent_names,
&total_parents)) {
WARN_ONCE(1, "No parents found for %s\n",
clock[i].clk_name);
continue;
}
zynqmp_data->hws[i] =
zynqmp_register_clk_topology(i, clk_name,
total_parents,
parent_names);
}
for (i = 0; i < clock_max_idx; i++) {
if (IS_ERR(zynqmp_data->hws[i])) {
pr_err("Zynq Ultrascale+ MPSoC clk %s: register failed with %ld\n",
clock[i].clk_name, PTR_ERR(zynqmp_data->hws[i]));
WARN_ON(1);
}
}
return 0;
}
/**
* zynqmp_get_clock_info() - Get clock information from firmware using PM_API
*/
static void zynqmp_get_clock_info(void)
{
int i, ret;
u32 attr, type = 0;
for (i = 0; i < clock_max_idx; i++) {
zynqmp_pm_clock_get_name(i, clock[i].clk_name);
if (!strcmp(clock[i].clk_name, RESERVED_CLK_NAME))
continue;
ret = zynqmp_pm_clock_get_attributes(i, &attr);
if (ret)
continue;
clock[i].valid = attr & CLK_VALID_MASK;
clock[i].type = attr >> CLK_TYPE_SHIFT ? CLK_TYPE_EXTERNAL :
CLK_TYPE_OUTPUT;
}
/* Get topology of all clock */
for (i = 0; i < clock_max_idx; i++) {
ret = zynqmp_get_clock_type(i, &type);
if (ret || type != CLK_TYPE_OUTPUT)
continue;
ret = zynqmp_clock_get_topology(i, clock[i].node,
&clock[i].num_nodes);
if (ret)
continue;
ret = zynqmp_clock_get_parents(i, clock[i].parent,
&clock[i].num_parents);
if (ret)
continue;
}
}
/**
* zynqmp_clk_setup() - Setup the clock framework and register clocks
* @np: Device node
*
* Return: 0 on success else error code
*/
static int zynqmp_clk_setup(struct device_node *np)
{
int ret;
ret = zynqmp_pm_clock_get_num_clocks(&clock_max_idx);
if (ret)
return ret;
zynqmp_data = kzalloc(sizeof(*zynqmp_data) + sizeof(*zynqmp_data) *
clock_max_idx, GFP_KERNEL);
if (!zynqmp_data)
return -ENOMEM;
clock = kcalloc(clock_max_idx, sizeof(*clock), GFP_KERNEL);
if (!clock) {
kfree(zynqmp_data);
return -ENOMEM;
}
zynqmp_get_clock_info();
zynqmp_register_clocks(np);
zynqmp_data->num = clock_max_idx;
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, zynqmp_data);
return 0;
}
static int zynqmp_clock_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
eemi_ops = zynqmp_pm_get_eemi_ops();
if (!eemi_ops)
return -ENXIO;
ret = zynqmp_clk_setup(dev->of_node);
return ret;
}
static const struct of_device_id zynqmp_clock_of_match[] = {
{.compatible = "xlnx,zynqmp-clk"},
{},
};
MODULE_DEVICE_TABLE(of, zynqmp_clock_of_match);
static struct platform_driver zynqmp_clock_driver = {
.driver = {
.name = "zynqmp_clock",
.of_match_table = zynqmp_clock_of_match,
},
.probe = zynqmp_clock_probe,
};
module_platform_driver(zynqmp_clock_driver);

View File

@ -0,0 +1,217 @@
// 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);

View File

@ -0,0 +1,335 @@
// 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;
}

View File

@ -460,4 +460,18 @@ config EDAC_TI
Support for error detection and correction on the
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

View File

@ -77,3 +77,4 @@ obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o
obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o
obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
obj-$(CONFIG_EDAC_TI) += ti_edac.o
obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o

View File

@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#include <linux/edac.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/soc/qcom/llcc-qcom.h>
#include "edac_mc.h"
#include "edac_device.h"
#define EDAC_LLCC "qcom_llcc"
#define LLCC_ERP_PANIC_ON_UE 1
#define TRP_SYN_REG_CNT 6
#define DRP_SYN_REG_CNT 8
#define LLCC_COMMON_STATUS0 0x0003000c
#define LLCC_LB_CNT_MASK GENMASK(31, 28)
#define LLCC_LB_CNT_SHIFT 28
/* Single & double bit syndrome register offsets */
#define TRP_ECC_SB_ERR_SYN0 0x0002304c
#define TRP_ECC_DB_ERR_SYN0 0x00020370
#define DRP_ECC_SB_ERR_SYN0 0x0004204c
#define DRP_ECC_DB_ERR_SYN0 0x00042070
/* Error register offsets */
#define TRP_ECC_ERROR_STATUS1 0x00020348
#define TRP_ECC_ERROR_STATUS0 0x00020344
#define DRP_ECC_ERROR_STATUS1 0x00042048
#define DRP_ECC_ERROR_STATUS0 0x00042044
/* TRP, DRP interrupt register offsets */
#define DRP_INTERRUPT_STATUS 0x00041000
#define TRP_INTERRUPT_0_STATUS 0x00020480
#define DRP_INTERRUPT_CLEAR 0x00041008
#define DRP_ECC_ERROR_CNTR_CLEAR 0x00040004
#define TRP_INTERRUPT_0_CLEAR 0x00020484
#define TRP_ECC_ERROR_CNTR_CLEAR 0x00020440
/* Mask and shift macros */
#define ECC_DB_ERR_COUNT_MASK GENMASK(4, 0)
#define ECC_DB_ERR_WAYS_MASK GENMASK(31, 16)
#define ECC_DB_ERR_WAYS_SHIFT BIT(4)
#define ECC_SB_ERR_COUNT_MASK GENMASK(23, 16)
#define ECC_SB_ERR_COUNT_SHIFT BIT(4)
#define ECC_SB_ERR_WAYS_MASK GENMASK(15, 0)
#define SB_ECC_ERROR BIT(0)
#define DB_ECC_ERROR BIT(1)
#define DRP_TRP_INT_CLEAR GENMASK(1, 0)
#define DRP_TRP_CNT_CLEAR GENMASK(1, 0)
/* Config registers offsets*/
#define DRP_ECC_ERROR_CFG 0x00040000
/* Tag RAM, Data RAM interrupt register offsets */
#define CMN_INTERRUPT_0_ENABLE 0x0003001c
#define CMN_INTERRUPT_2_ENABLE 0x0003003c
#define TRP_INTERRUPT_0_ENABLE 0x00020488
#define DRP_INTERRUPT_ENABLE 0x0004100c
#define SB_ERROR_THRESHOLD 0x1
#define SB_ERROR_THRESHOLD_SHIFT 24
#define SB_DB_TRP_INTERRUPT_ENABLE 0x3
#define TRP0_INTERRUPT_ENABLE 0x1
#define DRP0_INTERRUPT_ENABLE BIT(6)
#define SB_DB_DRP_INTERRUPT_ENABLE 0x3
enum {
LLCC_DRAM_CE = 0,
LLCC_DRAM_UE,
LLCC_TRAM_CE,
LLCC_TRAM_UE,
};
static const struct llcc_edac_reg_data edac_reg_data[] = {
[LLCC_DRAM_CE] = {
.name = "DRAM Single-bit",
.synd_reg = DRP_ECC_SB_ERR_SYN0,
.count_status_reg = DRP_ECC_ERROR_STATUS1,
.ways_status_reg = DRP_ECC_ERROR_STATUS0,
.reg_cnt = DRP_SYN_REG_CNT,
.count_mask = ECC_SB_ERR_COUNT_MASK,
.ways_mask = ECC_SB_ERR_WAYS_MASK,
.count_shift = ECC_SB_ERR_COUNT_SHIFT,
},
[LLCC_DRAM_UE] = {
.name = "DRAM Double-bit",
.synd_reg = DRP_ECC_DB_ERR_SYN0,
.count_status_reg = DRP_ECC_ERROR_STATUS1,
.ways_status_reg = DRP_ECC_ERROR_STATUS0,
.reg_cnt = DRP_SYN_REG_CNT,
.count_mask = ECC_DB_ERR_COUNT_MASK,
.ways_mask = ECC_DB_ERR_WAYS_MASK,
.ways_shift = ECC_DB_ERR_WAYS_SHIFT,
},
[LLCC_TRAM_CE] = {
.name = "TRAM Single-bit",
.synd_reg = TRP_ECC_SB_ERR_SYN0,
.count_status_reg = TRP_ECC_ERROR_STATUS1,
.ways_status_reg = TRP_ECC_ERROR_STATUS0,
.reg_cnt = TRP_SYN_REG_CNT,
.count_mask = ECC_SB_ERR_COUNT_MASK,
.ways_mask = ECC_SB_ERR_WAYS_MASK,
.count_shift = ECC_SB_ERR_COUNT_SHIFT,
},
[LLCC_TRAM_UE] = {
.name = "TRAM Double-bit",
.synd_reg = TRP_ECC_DB_ERR_SYN0,
.count_status_reg = TRP_ECC_ERROR_STATUS1,
.ways_status_reg = TRP_ECC_ERROR_STATUS0,
.reg_cnt = TRP_SYN_REG_CNT,
.count_mask = ECC_DB_ERR_COUNT_MASK,
.ways_mask = ECC_DB_ERR_WAYS_MASK,
.ways_shift = ECC_DB_ERR_WAYS_SHIFT,
},
};
static int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap)
{
u32 sb_err_threshold;
int ret;
/*
* Configure interrupt enable registers such that Tag, Data RAM related
* interrupts are propagated to interrupt controller for servicing
*/
ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
TRP0_INTERRUPT_ENABLE,
TRP0_INTERRUPT_ENABLE);
if (ret)
return ret;
ret = regmap_update_bits(llcc_bcast_regmap, TRP_INTERRUPT_0_ENABLE,
SB_DB_TRP_INTERRUPT_ENABLE,
SB_DB_TRP_INTERRUPT_ENABLE);
if (ret)
return ret;
sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT);
ret = regmap_write(llcc_bcast_regmap, DRP_ECC_ERROR_CFG,
sb_err_threshold);
if (ret)
return ret;
ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
DRP0_INTERRUPT_ENABLE,
DRP0_INTERRUPT_ENABLE);
if (ret)
return ret;
ret = regmap_write(llcc_bcast_regmap, DRP_INTERRUPT_ENABLE,
SB_DB_DRP_INTERRUPT_ENABLE);
return ret;
}
/* Clear the error interrupt and counter registers */
static int
qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv)
{
int ret = 0;
switch (err_type) {
case LLCC_DRAM_CE:
case LLCC_DRAM_UE:
ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR,
DRP_TRP_INT_CLEAR);
if (ret)
return ret;
ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR,
DRP_TRP_CNT_CLEAR);
if (ret)
return ret;
break;
case LLCC_TRAM_CE:
case LLCC_TRAM_UE:
ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR,
DRP_TRP_INT_CLEAR);
if (ret)
return ret;
ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR,
DRP_TRP_CNT_CLEAR);
if (ret)
return ret;
break;
default:
ret = -EINVAL;
edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
err_type);
}
return ret;
}
/* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/
static int
dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
{
struct llcc_edac_reg_data reg_data = edac_reg_data[err_type];
int err_cnt, err_ways, ret, i;
u32 synd_reg, synd_val;
for (i = 0; i < reg_data.reg_cnt; i++) {
synd_reg = reg_data.synd_reg + (i * 4);
ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg,
&synd_val);
if (ret)
goto clear;
edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n",
reg_data.name, i, synd_val);
}
ret = regmap_read(drv->regmap,
drv->offsets[bank] + reg_data.count_status_reg,
&err_cnt);
if (ret)
goto clear;
err_cnt &= reg_data.count_mask;
err_cnt >>= reg_data.count_shift;
edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n",
reg_data.name, err_cnt);
ret = regmap_read(drv->regmap,
drv->offsets[bank] + reg_data.ways_status_reg,
&err_ways);
if (ret)
goto clear;
err_ways &= reg_data.ways_mask;
err_ways >>= reg_data.ways_shift;
edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n",
reg_data.name, err_ways);
clear:
return qcom_llcc_clear_error_status(err_type, drv);
}
static int
dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank)
{
struct llcc_drv_data *drv = edev_ctl->pvt_info;
int ret;
ret = dump_syn_reg_values(drv, bank, err_type);
if (ret)
return ret;
switch (err_type) {
case LLCC_DRAM_CE:
edac_device_handle_ce(edev_ctl, 0, bank,
"LLCC Data RAM correctable Error");
break;
case LLCC_DRAM_UE:
edac_device_handle_ue(edev_ctl, 0, bank,
"LLCC Data RAM uncorrectable Error");
break;
case LLCC_TRAM_CE:
edac_device_handle_ce(edev_ctl, 0, bank,
"LLCC Tag RAM correctable Error");
break;
case LLCC_TRAM_UE:
edac_device_handle_ue(edev_ctl, 0, bank,
"LLCC Tag RAM uncorrectable Error");
break;
default:
ret = -EINVAL;
edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
err_type);
}
return ret;
}
static irqreturn_t
llcc_ecc_irq_handler(int irq, void *edev_ctl)
{
struct edac_device_ctl_info *edac_dev_ctl = edev_ctl;
struct llcc_drv_data *drv = edac_dev_ctl->pvt_info;
irqreturn_t irq_rc = IRQ_NONE;
u32 drp_error, trp_error, i;
bool irq_handled;
int ret;
/* Iterate over the banks and look for Tag RAM or Data RAM errors */
for (i = 0; i < drv->num_banks; i++) {
ret = regmap_read(drv->regmap,
drv->offsets[i] + DRP_INTERRUPT_STATUS,
&drp_error);
if (!ret && (drp_error & SB_ECC_ERROR)) {
edac_printk(KERN_CRIT, EDAC_LLCC,
"Single Bit Error detected in Data RAM\n");
ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
} else if (!ret && (drp_error & DB_ECC_ERROR)) {
edac_printk(KERN_CRIT, EDAC_LLCC,
"Double Bit Error detected in Data RAM\n");
ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
}
if (!ret)
irq_handled = true;
ret = regmap_read(drv->regmap,
drv->offsets[i] + TRP_INTERRUPT_0_STATUS,
&trp_error);
if (!ret && (trp_error & SB_ECC_ERROR)) {
edac_printk(KERN_CRIT, EDAC_LLCC,
"Single Bit Error detected in Tag RAM\n");
ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
} else if (!ret && (trp_error & DB_ECC_ERROR)) {
edac_printk(KERN_CRIT, EDAC_LLCC,
"Double Bit Error detected in Tag RAM\n");
ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
}
if (!ret)
irq_handled = true;
}
if (irq_handled)
irq_rc = IRQ_HANDLED;
return irq_rc;
}
static int qcom_llcc_edac_probe(struct platform_device *pdev)
{
struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data;
struct edac_device_ctl_info *edev_ctl;
struct device *dev = &pdev->dev;
int ecc_irq;
int rc;
rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap);
if (rc)
return rc;
/* Allocate edac control info */
edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank",
llcc_driv_data->num_banks, 1,
NULL, 0,
edac_device_alloc_index());
if (!edev_ctl)
return -ENOMEM;
edev_ctl->dev = dev;
edev_ctl->mod_name = dev_name(dev);
edev_ctl->dev_name = dev_name(dev);
edev_ctl->ctl_name = "llcc";
edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
edev_ctl->pvt_info = llcc_driv_data;
rc = edac_device_add_device(edev_ctl);
if (rc)
goto out_mem;
platform_set_drvdata(pdev, edev_ctl);
/* Request for ecc irq */
ecc_irq = llcc_driv_data->ecc_irq;
if (ecc_irq < 0) {
rc = -ENODEV;
goto out_dev;
}
rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,
IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
if (rc)
goto out_dev;
return rc;
out_dev:
edac_device_del_device(edev_ctl->dev);
out_mem:
edac_device_free_ctl_info(edev_ctl);
return rc;
}
static int qcom_llcc_edac_remove(struct platform_device *pdev)
{
struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
edac_device_del_device(edev_ctl->dev);
edac_device_free_ctl_info(edev_ctl);
return 0;
}
static struct platform_driver qcom_llcc_edac_driver = {
.probe = qcom_llcc_edac_probe,
.remove = qcom_llcc_edac_remove,
.driver = {
.name = "qcom_llcc_edac",
},
};
module_platform_driver(qcom_llcc_edac_driver);
MODULE_DESCRIPTION("QCOM EDAC driver");
MODULE_LICENSE("GPL v2");

View File

@ -289,7 +289,9 @@ config HAVE_ARM_SMCCC
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
source "drivers/firmware/imx/Kconfig"
source "drivers/firmware/meson/Kconfig"
source "drivers/firmware/tegra/Kconfig"
source "drivers/firmware/xilinx/Kconfig"
endmenu

View File

@ -31,4 +31,6 @@ obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_UEFI_CPER) += efi/
obj-y += imx/
obj-y += tegra/
obj-y += xilinx/

View File

@ -208,7 +208,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
ret = scmi_do_xfer(handle, t);
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);

View File

@ -111,7 +111,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
ret = scmi_do_xfer(handle, t);
if (!ret)
memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
else
clk->name[0] = '\0';

View File

@ -174,7 +174,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
dom_info->mult_factor =
(dom_info->sustained_freq_khz * 1000) /
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);
@ -427,6 +427,33 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
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 = {
.limits_set = scmi_perf_limits_set,
.limits_get = scmi_perf_limits_get,
@ -437,6 +464,7 @@ static struct scmi_perf_ops perf_ops = {
.device_opps_add = scmi_dvfs_device_opps_add,
.freq_set = scmi_dvfs_freq_set,
.freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
};
static int scmi_perf_protocol_init(struct scmi_handle *handle)

View File

@ -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_async = SUPPORTS_STATE_SET_ASYNC(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);

View File

@ -140,7 +140,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
s = &si->sensors[desc_index + cnt];
s->id = le32_to_cpu(buf->desc[cnt].id);
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;

View File

@ -0,0 +1,11 @@
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.

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o

View File

@ -0,0 +1,270 @@
// 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");

View File

@ -0,0 +1,99 @@
// 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);

View File

@ -24,6 +24,7 @@
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/firmware/meson/meson_sm.h>
@ -48,6 +49,7 @@ struct meson_sm_chip gxbb_chip = {
CMD(SM_EFUSE_READ, 0x82000030),
CMD(SM_EFUSE_WRITE, 0x82000031),
CMD(SM_EFUSE_USER_MAX, 0x82000033),
CMD(SM_GET_CHIP_ID, 0x82000044),
{ /* sentinel */ },
},
};
@ -214,6 +216,57 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
}
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[] = {
{ .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
{ /* sentinel */ },
@ -242,6 +295,9 @@ static int __init meson_sm_probe(struct platform_device *pdev)
fw.chip = chip;
pr_info("secure-monitor enabled\n");
if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
goto out_in_base;
return 0;
out_in_base:

View File

@ -525,34 +525,44 @@ static int qcom_scm_probe(struct platform_device *pdev)
return ret;
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");
if (IS_ERR(scm->core_clk)) {
if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to acquire core clk\n");
scm->core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(scm->core_clk)) {
if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
return PTR_ERR(scm->core_clk);
if (clks & SCM_HAS_CORE_CLK) {
dev_err(&pdev->dev, "failed to acquire core clk\n");
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");
if (IS_ERR(scm->iface_clk)) {
if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to acquire iface clk\n");
scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(scm->iface_clk)) {
if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER)
return PTR_ERR(scm->iface_clk);
if (clks & SCM_HAS_IFACE_CLK) {
dev_err(&pdev->dev, "failed to acquire iface clk\n");
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");
if (IS_ERR(scm->bus_clk)) {
if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to acquire bus clk\n");
scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(scm->bus_clk)) {
if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER)
return PTR_ERR(scm->bus_clk);
if (clks & SCM_HAS_BUS_CLK) {
dev_err(&pdev->dev, "failed to acquire bus clk\n");
return PTR_ERR(scm->bus_clk);
}
scm->bus_clk = NULL;
}
scm->reset.ops = &qcom_scm_pas_reset_ops;
@ -594,23 +604,23 @@ static const struct of_device_id qcom_scm_dt_match[] = {
{ .compatible = "qcom,scm-apq8064",
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
},
{ .compatible = "qcom,scm-msm8660",
.data = (void *) SCM_HAS_CORE_CLK,
{ .compatible = "qcom,scm-apq8084", .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-ipq4019" },
{ .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-msm8996",
.data = NULL, /* no clocks */
},
{ .compatible = "qcom,scm-ipq4019",
.data = NULL, /* no clocks */
},
{ .compatible = "qcom,scm",
.data = (void *)(SCM_HAS_CORE_CLK
| SCM_HAS_IFACE_CLK
| SCM_HAS_BUS_CLK),
{ .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
},
{ .compatible = "qcom,scm-msm8996" },
{ .compatible = "qcom,scm" },
{}
};

View File

@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/semaphore.h>
#include <linux/sched/clock.h>
@ -843,6 +844,23 @@ free_tx:
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 = {
.channels = {
.cpu_tx = {
@ -871,6 +889,7 @@ static struct platform_driver tegra_bpmp_driver = {
.driver = {
.name = "tegra-bpmp",
.of_match_table = tegra_bpmp_match,
.pm = &tegra_bpmp_pm_ops,
},
.probe = tegra_bpmp_probe,
};

View File

@ -66,14 +66,14 @@ struct ti_sci_xfers_info {
/**
* 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_msgs: Maximum number of messages that can be pending
* simultaneously in the system
* @max_msg_size: Maximum size of data per message that can be handled.
*/
struct ti_sci_desc {
u8 host_id;
u8 default_host_id;
int max_rx_timeout_ms;
int max_msgs;
int max_msg_size;
@ -94,6 +94,7 @@ struct ti_sci_desc {
* @chan_rx: Receive mailbox channel
* @minfo: Message info
* @node: list head
* @host_id: Host ID
* @users: Number of users of this instance
*/
struct ti_sci_info {
@ -110,6 +111,7 @@ struct ti_sci_info {
struct mbox_chan *chan_rx;
struct ti_sci_xfers_info minfo;
struct list_head node;
u8 host_id;
/* protected by ti_sci_list_mutex */
int users;
@ -370,7 +372,7 @@ static struct ti_sci_xfer *ti_sci_get_one_xfer(struct ti_sci_info *info,
hdr->seq = xfer_id;
hdr->type = msg_type;
hdr->host = info->desc->host_id;
hdr->host = info->host_id;
hdr->flags = msg_flags;
return xfer;
@ -1793,7 +1795,7 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode,
/* Description for K2G */
static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
.host_id = 2,
.default_host_id = 2,
/* Conservative duration */
.max_rx_timeout_ms = 1000,
/* 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)
int ret = -EINVAL;
int i;
int reboot = 0;
u32 h_id;
of_id = of_match_device(ti_sci_of_match, dev);
if (!of_id) {
@ -1833,6 +1836,19 @@ static int ti_sci_probe(struct platform_device *pdev)
info->dev = dev;
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,
"ti,system-reboot-controller");
INIT_LIST_HEAD(&info->node);

View File

@ -0,0 +1,23 @@
# 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

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for Xilinx firmwares
obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o
obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o

View File

@ -0,0 +1,250 @@
// 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);
}

View File

@ -0,0 +1,24 @@
/* 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__ */

View File

@ -0,0 +1,565 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx Zynq MPSoC Firmware layer
*
* 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/arm-smccc.h>
#include <linux/compiler.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/firmware/xlnx-zynqmp.h>
#include "zynqmp-debug.h"
/**
* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
* @ret_status: PMUFW return code
*
* Return: corresponding Linux error code
*/
static int zynqmp_pm_ret_code(u32 ret_status)
{
switch (ret_status) {
case XST_PM_SUCCESS:
case XST_PM_DOUBLE_REQ:
return 0;
case XST_PM_NO_ACCESS:
return -EACCES;
case XST_PM_ABORT_SUSPEND:
return -ECANCELED;
case XST_PM_INTERNAL:
case XST_PM_CONFLICT:
case XST_PM_INVALID_NODE:
default:
return -EINVAL;
}
}
static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2,
u32 *ret_payload)
{
return -ENODEV;
}
/*
* PM function call wrapper
* Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration
*/
static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail;
/**
* do_fw_call_smc() - Call system-level platform management layer (SMC)
* @arg0: Argument 0 to SMC call
* @arg1: Argument 1 to SMC call
* @arg2: Argument 2 to SMC call
* @ret_payload: Returned value array
*
* Invoke platform management function via SMC call (no hypervisor present).
*
* Return: Returns status, either success or error+reason
*/
static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2,
u32 *ret_payload)
{
struct arm_smccc_res res;
arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
if (ret_payload) {
ret_payload[0] = lower_32_bits(res.a0);
ret_payload[1] = upper_32_bits(res.a0);
ret_payload[2] = lower_32_bits(res.a1);
ret_payload[3] = upper_32_bits(res.a1);
}
return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
}
/**
* do_fw_call_hvc() - Call system-level platform management layer (HVC)
* @arg0: Argument 0 to HVC call
* @arg1: Argument 1 to HVC call
* @arg2: Argument 2 to HVC call
* @ret_payload: Returned value array
*
* Invoke platform management function via HVC
* HVC-based for communication through hypervisor
* (no direct communication with ATF).
*
* Return: Returns status, either success or error+reason
*/
static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
u32 *ret_payload)
{
struct arm_smccc_res res;
arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
if (ret_payload) {
ret_payload[0] = lower_32_bits(res.a0);
ret_payload[1] = upper_32_bits(res.a0);
ret_payload[2] = lower_32_bits(res.a1);
ret_payload[3] = upper_32_bits(res.a1);
}
return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
}
/**
* zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
* caller function depending on the configuration
* @pm_api_id: Requested PM-API call
* @arg0: Argument 0 to requested PM-API call
* @arg1: Argument 1 to requested PM-API call
* @arg2: Argument 2 to requested PM-API call
* @arg3: Argument 3 to requested PM-API call
* @ret_payload: Returned value array
*
* Invoke platform management function for SMC or HVC call, depending on
* configuration.
* Following SMC Calling Convention (SMCCC) for SMC64:
* Pm Function Identifier,
* PM_SIP_SVC + PM_API_ID =
* ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)
* ((SMC_64) << FUNCID_CC_SHIFT)
* ((SIP_START) << FUNCID_OEN_SHIFT)
* ((PM_API_ID) & FUNCID_NUM_MASK))
*
* PM_SIP_SVC - Registered ZynqMP SIP Service Call.
* PM_API_ID - Platform Management API ID.
*
* Return: Returns status, either success or error+reason
*/
int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
u32 arg2, u32 arg3, u32 *ret_payload)
{
/*
* Added SIP service call Function Identifier
* Make sure to stay in x0 register
*/
u64 smc_arg[4];
smc_arg[0] = PM_SIP_SVC | pm_api_id;
smc_arg[1] = ((u64)arg1 << 32) | arg0;
smc_arg[2] = ((u64)arg3 << 32) | arg2;
return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
}
static u32 pm_api_version;
static u32 pm_tz_version;
/**
* zynqmp_pm_get_api_version() - Get version number of PMU PM firmware
* @version: Returned version value
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_get_api_version(u32 *version)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
if (!version)
return -EINVAL;
/* Check is PM API version already verified */
if (pm_api_version > 0) {
*version = pm_api_version;
return 0;
}
ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload);
*version = ret_payload[1];
return ret;
}
/**
* zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
* @version: Returned version value
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_get_trustzone_version(u32 *version)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
if (!version)
return -EINVAL;
/* Check is PM trustzone version already verified */
if (pm_tz_version > 0) {
*version = pm_tz_version;
return 0;
}
ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, 0, 0,
0, 0, ret_payload);
*version = ret_payload[1];
return ret;
}
/**
* get_set_conduit_method() - Choose SMC or HVC based communication
* @np: Pointer to the device_node structure
*
* Use SMC or HVC-based functions to communicate with EL2/EL3.
*
* Return: Returns 0 on success or error code
*/
static int get_set_conduit_method(struct device_node *np)
{
const char *method;
if (of_property_read_string(np, "method", &method)) {
pr_warn("%s missing \"method\" property\n", __func__);
return -ENXIO;
}
if (!strcmp("hvc", method)) {
do_fw_call = do_fw_call_hvc;
} else if (!strcmp("smc", method)) {
do_fw_call = do_fw_call_smc;
} else {
pr_warn("%s Invalid \"method\" property: %s\n",
__func__, method);
return -EINVAL;
}
return 0;
}
/**
* zynqmp_pm_query_data() - Get query data from firmware
* @qdata: Variable to the zynqmp_pm_query_data structure
* @out: Returned output value
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
{
int ret;
ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
qdata.arg2, qdata.arg3, out);
/*
* For clock name query, all bytes in SMC response are clock name
* characters and return code is always success. For invalid clocks,
* clock name bytes would be zeros.
*/
return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;
}
/**
* zynqmp_pm_clock_enable() - Enable the clock for given id
* @clock_id: ID of the clock to be enabled
*
* This function is used by master to enable the clock
* including peripherals and PLL clocks.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_enable(u32 clock_id)
{
return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, clock_id, 0, 0, 0, NULL);
}
/**
* zynqmp_pm_clock_disable() - Disable the clock for given id
* @clock_id: ID of the clock to be disable
*
* This function is used by master to disable the clock
* including peripherals and PLL clocks.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_disable(u32 clock_id)
{
return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, clock_id, 0, 0, 0, NULL);
}
/**
* zynqmp_pm_clock_getstate() - Get the clock state for given id
* @clock_id: ID of the clock to be queried
* @state: 1/0 (Enabled/Disabled)
*
* This function is used by master to get the state of clock
* including peripherals and PLL clocks.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0,
0, 0, ret_payload);
*state = ret_payload[1];
return ret;
}
/**
* zynqmp_pm_clock_setdivider() - Set the clock divider for given id
* @clock_id: ID of the clock
* @divider: divider value
*
* This function is used by master to set divider for any clock
* to achieve desired rate.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider)
{
return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, clock_id, divider,
0, 0, NULL);
}
/**
* zynqmp_pm_clock_getdivider() - Get the clock divider for given id
* @clock_id: ID of the clock
* @divider: divider value
*
* This function is used by master to get divider values
* for any clock.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, clock_id, 0,
0, 0, ret_payload);
*divider = ret_payload[1];
return ret;
}
/**
* zynqmp_pm_clock_setrate() - Set the clock rate for given id
* @clock_id: ID of the clock
* @rate: rate value in hz
*
* This function is used by master to set rate for any clock.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_setrate(u32 clock_id, u64 rate)
{
return zynqmp_pm_invoke_fn(PM_CLOCK_SETRATE, clock_id,
lower_32_bits(rate),
upper_32_bits(rate),
0, NULL);
}
/**
* zynqmp_pm_clock_getrate() - Get the clock rate for given id
* @clock_id: ID of the clock
* @rate: rate value in hz
*
* This function is used by master to get rate
* for any clock.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_getrate(u32 clock_id, u64 *rate)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETRATE, clock_id, 0,
0, 0, ret_payload);
*rate = ((u64)ret_payload[2] << 32) | ret_payload[1];
return ret;
}
/**
* zynqmp_pm_clock_setparent() - Set the clock parent for given id
* @clock_id: ID of the clock
* @parent_id: parent id
*
* This function is used by master to set parent for any clock.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id)
{
return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, clock_id,
parent_id, 0, 0, NULL);
}
/**
* zynqmp_pm_clock_getparent() - Get the clock parent for given id
* @clock_id: ID of the clock
* @parent_id: parent id
*
* This function is used by master to get parent index
* for any clock.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, clock_id, 0,
0, 0, ret_payload);
*parent_id = ret_payload[1];
return ret;
}
/**
* zynqmp_is_valid_ioctl() - Check whether IOCTL ID is valid or not
* @ioctl_id: IOCTL ID
*
* Return: 1 if IOCTL is valid else 0
*/
static inline int zynqmp_is_valid_ioctl(u32 ioctl_id)
{
switch (ioctl_id) {
case IOCTL_SET_PLL_FRAC_MODE:
case IOCTL_GET_PLL_FRAC_MODE:
case IOCTL_SET_PLL_FRAC_DATA:
case IOCTL_GET_PLL_FRAC_DATA:
return 1;
default:
return 0;
}
}
/**
* zynqmp_pm_ioctl() - PM IOCTL API for device control and configs
* @node_id: Node ID of the device
* @ioctl_id: ID of the requested IOCTL
* @arg1: Argument 1 to requested IOCTL call
* @arg2: Argument 2 to requested IOCTL call
* @out: Returned output value
*
* This function calls IOCTL to firmware for device control and configuration.
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
u32 *out)
{
if (!zynqmp_is_valid_ioctl(ioctl_id))
return -EINVAL;
return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, ioctl_id,
arg1, arg2, out);
}
static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
.query_data = zynqmp_pm_query_data,
.clock_enable = zynqmp_pm_clock_enable,
.clock_disable = zynqmp_pm_clock_disable,
.clock_getstate = zynqmp_pm_clock_getstate,
.clock_setdivider = zynqmp_pm_clock_setdivider,
.clock_getdivider = zynqmp_pm_clock_getdivider,
.clock_setrate = zynqmp_pm_clock_setrate,
.clock_getrate = zynqmp_pm_clock_getrate,
.clock_setparent = zynqmp_pm_clock_setparent,
.clock_getparent = zynqmp_pm_clock_getparent,
.ioctl = zynqmp_pm_ioctl,
};
/**
* zynqmp_pm_get_eemi_ops - Get eemi ops functions
*
* Return: Pointer of eemi_ops structure
*/
const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
{
return &eemi_ops;
}
EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops);
static int zynqmp_firmware_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np;
int ret;
np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
if (!np)
return 0;
of_node_put(np);
ret = get_set_conduit_method(dev->of_node);
if (ret)
return ret;
/* Check PM API version number */
zynqmp_pm_get_api_version(&pm_api_version);
if (pm_api_version < ZYNQMP_PM_VERSION) {
panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
__func__,
ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
pm_api_version >> 16, pm_api_version & 0xFFFF);
}
pr_info("%s Platform Management API v%d.%d\n", __func__,
pm_api_version >> 16, pm_api_version & 0xFFFF);
/* Check trustzone version number */
ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);
if (ret)
panic("Legacy trustzone found without version support\n");
if (pm_tz_version < ZYNQMP_TZ_VERSION)
panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
__func__,
ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,
pm_tz_version >> 16, pm_tz_version & 0xFFFF);
pr_info("%s Trustzone version v%d.%d\n", __func__,
pm_tz_version >> 16, pm_tz_version & 0xFFFF);
zynqmp_pm_api_debugfs_init();
return of_platform_populate(dev->of_node, NULL, NULL, dev);
}
static int zynqmp_firmware_remove(struct platform_device *pdev)
{
zynqmp_pm_api_debugfs_exit();
return 0;
}
static const struct of_device_id zynqmp_firmware_of_match[] = {
{.compatible = "xlnx,zynqmp-firmware"},
{},
};
MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);
static struct platform_driver zynqmp_firmware_driver = {
.driver = {
.name = "zynqmp_firmware",
.of_match_table = zynqmp_firmware_of_match,
},
.probe = zynqmp_firmware_probe,
.remove = zynqmp_firmware_remove,
};
module_platform_driver(zynqmp_firmware_driver);

View File

@ -327,8 +327,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
return -EINVAL;
}
ebid = devm_kzalloc(ebi->dev,
sizeof(*ebid) + (numcs * sizeof(*ebid->configs)),
ebid = devm_kzalloc(ebi->dev, struct_size(ebid, configs, numcs),
GFP_KERNEL);
if (!ebid)
return -ENOMEM;

View File

@ -98,6 +98,15 @@ config RESET_QCOM_AOSS
reset signals provided by AOSS for Modem, Venus, ADSP,
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
bool "Simple Reset Controller Driver" if COMPILE_TEST
default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED

View File

@ -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_PISTACHIO) += reset-pistachio.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_STM32MP157) += reset-stm32mp1.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o

View File

@ -496,28 +496,29 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
break;
}
}
of_node_put(args.np);
if (!rcdev) {
mutex_unlock(&reset_list_mutex);
return ERR_PTR(-EPROBE_DEFER);
rstc = ERR_PTR(-EPROBE_DEFER);
goto out;
}
if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
mutex_unlock(&reset_list_mutex);
return ERR_PTR(-EINVAL);
rstc = ERR_PTR(-EINVAL);
goto out;
}
rstc_id = rcdev->of_xlate(rcdev, &args);
if (rstc_id < 0) {
mutex_unlock(&reset_list_mutex);
return ERR_PTR(rstc_id);
rstc = ERR_PTR(rstc_id);
goto out;
}
/* reset_list_mutex also protects the rcdev's reset_control list */
rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
out:
mutex_unlock(&reset_list_mutex);
of_node_put(args.np);
return rstc;
}

View File

@ -0,0 +1,124 @@
// 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");

View File

@ -18,7 +18,7 @@ obj-y += qcom/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-y += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_SOC_TI) += ti/
obj-$(CONFIG_ARCH_U8500) += ux500/

View File

@ -10,7 +10,7 @@ config OWL_PM_DOMAINS
select PM_GENERIC_DOMAINS
help
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'.
endif

View File

@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o
obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Actions Semi Owl Smart Power System (SPS) shared helpers
*
@ -5,11 +6,6 @@
* Author: Actions Semi, Inc.
*
* 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>

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Actions Semi Owl Smart Power System (SPS)
*
@ -5,11 +6,6 @@
* Author: Actions Semi, Inc.
*
* 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>
@ -18,6 +14,7 @@
#include <linux/soc/actions/owl-sps.h>
#include <dt-bindings/power/owl-s500-powergate.h>
#include <dt-bindings/power/owl-s700-powergate.h>
#include <dt-bindings/power/owl-s900-powergate.h>
struct owl_sps_domain_info {
const char *name;
@ -244,9 +241,66 @@ static const struct owl_sps_info s700_sps_info = {
.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[] = {
{ .compatible = "actions,s500-sps", .data = &s500_sps_info },
{ .compatible = "actions,s700-sps", .data = &s700_sps_info },
{ .compatible = "actions,s900-sps", .data = &s900_sps_info },
{ }
};

View File

@ -1,5 +1,12 @@
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
bool "Amlogic Meson GX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST

View File

@ -1,3 +1,4 @@
obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o

View File

@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 BayLibre, SAS
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
* Copyright (C) 2014 Endless Mobile
*/
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soc/amlogic/meson-canvas.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#define NUM_CANVAS 256
/* DMC Registers */
#define DMC_CAV_LUT_DATAL 0x00
#define CANVAS_WIDTH_LBIT 29
#define CANVAS_WIDTH_LWID 3
#define DMC_CAV_LUT_DATAH 0x04
#define CANVAS_WIDTH_HBIT 0
#define CANVAS_HEIGHT_BIT 9
#define CANVAS_WRAP_BIT 22
#define CANVAS_BLKMODE_BIT 24
#define CANVAS_ENDIAN_BIT 26
#define DMC_CAV_LUT_ADDR 0x08
#define CANVAS_LUT_WR_EN BIT(9)
#define CANVAS_LUT_RD_EN BIT(8)
struct meson_canvas {
struct device *dev;
void __iomem *reg_base;
spinlock_t lock; /* canvas device lock */
u8 used[NUM_CANVAS];
};
static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
{
writel_relaxed(val, canvas->reg_base + reg);
}
static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
{
return readl_relaxed(canvas->reg_base + reg);
}
struct meson_canvas *meson_canvas_get(struct device *dev)
{
struct device_node *canvas_node;
struct platform_device *canvas_pdev;
canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
if (!canvas_node)
return ERR_PTR(-ENODEV);
canvas_pdev = of_find_device_by_node(canvas_node);
if (!canvas_pdev)
return ERR_PTR(-EPROBE_DEFER);
return dev_get_drvdata(&canvas_pdev->dev);
}
EXPORT_SYMBOL_GPL(meson_canvas_get);
int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
u32 addr, u32 stride, u32 height,
unsigned int wrap,
unsigned int blkmode,
unsigned int endian)
{
unsigned long flags;
spin_lock_irqsave(&canvas->lock, flags);
if (!canvas->used[canvas_index]) {
dev_err(canvas->dev,
"Trying to setup non allocated canvas %u\n",
canvas_index);
spin_unlock_irqrestore(&canvas->lock, flags);
return -EINVAL;
}
canvas_write(canvas, DMC_CAV_LUT_DATAL,
((addr + 7) >> 3) |
(((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
canvas_write(canvas, DMC_CAV_LUT_DATAH,
((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
CANVAS_WIDTH_HBIT) |
(height << CANVAS_HEIGHT_BIT) |
(wrap << CANVAS_WRAP_BIT) |
(blkmode << CANVAS_BLKMODE_BIT) |
(endian << CANVAS_ENDIAN_BIT));
canvas_write(canvas, DMC_CAV_LUT_ADDR,
CANVAS_LUT_WR_EN | canvas_index);
/* Force a read-back to make sure everything is flushed. */
canvas_read(canvas, DMC_CAV_LUT_DATAH);
spin_unlock_irqrestore(&canvas->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(meson_canvas_config);
int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
{
int i;
unsigned long flags;
spin_lock_irqsave(&canvas->lock, flags);
for (i = 0; i < NUM_CANVAS; ++i) {
if (!canvas->used[i]) {
canvas->used[i] = 1;
spin_unlock_irqrestore(&canvas->lock, flags);
*canvas_index = i;
return 0;
}
}
spin_unlock_irqrestore(&canvas->lock, flags);
dev_err(canvas->dev, "No more canvas available\n");
return -ENODEV;
}
EXPORT_SYMBOL_GPL(meson_canvas_alloc);
int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
{
unsigned long flags;
spin_lock_irqsave(&canvas->lock, flags);
if (!canvas->used[canvas_index]) {
dev_err(canvas->dev,
"Trying to free unused canvas %u\n", canvas_index);
spin_unlock_irqrestore(&canvas->lock, flags);
return -EINVAL;
}
canvas->used[canvas_index] = 0;
spin_unlock_irqrestore(&canvas->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(meson_canvas_free);
static int meson_canvas_probe(struct platform_device *pdev)
{
struct resource *res;
struct meson_canvas *canvas;
struct device *dev = &pdev->dev;
canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
if (!canvas)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
canvas->reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(canvas->reg_base))
return PTR_ERR(canvas->reg_base);
canvas->dev = dev;
spin_lock_init(&canvas->lock);
dev_set_drvdata(dev, canvas);
return 0;
}
static const struct of_device_id canvas_dt_match[] = {
{ .compatible = "amlogic,canvas" },
{}
};
MODULE_DEVICE_TABLE(of, canvas_dt_match);
static struct platform_driver meson_canvas_driver = {
.probe = meson_canvas_probe,
.driver = {
.name = "amlogic-canvas",
.of_match_table = canvas_dt_match,
},
};
module_platform_driver(meson_canvas_driver);
MODULE_DESCRIPTION("Amlogic Canvas driver");
MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
MODULE_LICENSE("GPL");

View File

@ -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)
{
struct dpio_priv *priv;
int error;
struct fsl_mc_device_irq *irq;
cpumask_t mask;
priv = dev_get_drvdata(&dpio_dev->dev);
irq = dpio_dev->irqs[0];
error = devm_request_irq(&dpio_dev->dev,
irq->msi_desc->irq,

View File

@ -1,6 +1,6 @@
menuconfig FSL_DPAA
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
help
The Freescale Data Path Acceleration Architecture (DPAA) is a set of

View File

@ -562,11 +562,9 @@ static int bman_create_portal(struct bman_portal *portal,
dev_err(c->dev, "request_irq() failed\n");
goto fail_irq;
}
if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
irq_set_affinity(c->irq, cpumask_of(c->cpu))) {
dev_err(c->dev, "irq_set_affinity() failed\n");
if (dpaa_set_portal_irq_affinity(c->dev, c->irq, c->cpu))
goto fail_affinity;
}
/* Need RCR to be empty before continuing */
ret = bm_rcr_get_fill(p);

View File

@ -65,7 +65,9 @@ static int bman_offline_cpu(unsigned int cpu)
if (!pcfg)
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;
}
@ -91,7 +93,15 @@ static int bman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct bm_portal_config *pcfg;
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);
if (!pcfg)

View File

@ -111,4 +111,24 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
#define QBMAN_MEMREMAP_ATTR MEMREMAP_WC
#endif
static inline int dpaa_set_portal_irq_affinity(struct device *dev,
int irq, int cpu)
{
int ret = 0;
if (!irq_can_set_affinity(irq)) {
dev_err(dev, "unable to set IRQ affinity\n");
return -EINVAL;
}
if (cpu == -1 || !cpu_online(cpu))
cpu = cpumask_any(cpu_online_mask);
ret = irq_set_affinity(irq, cpumask_of(cpu));
if (ret)
dev_err(dev, "irq_set_affinity() on CPU %d failed\n", cpu);
return ret;
}
#endif /* __DPAA_SYS_H */

View File

@ -850,12 +850,24 @@ static inline void qm_mr_set_ithresh(struct qm_portal *portal, u8 ithresh)
static inline int qm_mc_init(struct qm_portal *portal)
{
u8 rr0, rr1;
struct qm_mc *mc = &portal->mc;
mc->cr = portal->addr.ce + QM_CL_CR;
mc->rr = portal->addr.ce + QM_CL_RR0;
mc->rridx = (mc->cr->_ncw_verb & QM_MCC_VERB_VBIT)
? 0 : 1;
/*
* The expected valid bit polarity for the next CR command is 0
* if RR1 contains a valid response, and is 1 if RR0 contains a
* valid response. If both RR contain all 0, this indicates either
* that no command has been executed since reset (in which case the
* expected valid bit polarity is 1)
*/
rr0 = mc->rr->verb;
rr1 = (mc->rr+1)->verb;
if ((rr0 == 0 && rr1 == 0) || rr0 != 0)
mc->rridx = 1;
else
mc->rridx = 0;
mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0;
#ifdef CONFIG_FSL_DPAA_CHECKING
mc->state = qman_mc_idle;
@ -1000,6 +1012,37 @@ static inline void put_affine_portal(void)
static struct workqueue_struct *qm_portal_wq;
void qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
{
if (!portal)
return;
qm_dqrr_set_ithresh(&portal->p, ithresh);
portal->p.dqrr.ithresh = ithresh;
}
EXPORT_SYMBOL(qman_dqrr_set_ithresh);
void qman_dqrr_get_ithresh(struct qman_portal *portal, u8 *ithresh)
{
if (portal && ithresh)
*ithresh = portal->p.dqrr.ithresh;
}
EXPORT_SYMBOL(qman_dqrr_get_ithresh);
void qman_portal_get_iperiod(struct qman_portal *portal, u32 *iperiod)
{
if (portal && iperiod)
*iperiod = qm_in(&portal->p, QM_REG_ITPR);
}
EXPORT_SYMBOL(qman_portal_get_iperiod);
void qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod)
{
if (portal)
qm_out(&portal->p, QM_REG_ITPR, iperiod);
}
EXPORT_SYMBOL(qman_portal_set_iperiod);
int qman_wq_alloc(void)
{
qm_portal_wq = alloc_workqueue("qman_portal_wq", 0, 1);
@ -1210,11 +1253,9 @@ static int qman_create_portal(struct qman_portal *portal,
dev_err(c->dev, "request_irq() failed\n");
goto fail_irq;
}
if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
irq_set_affinity(c->irq, cpumask_of(c->cpu))) {
dev_err(c->dev, "irq_set_affinity() failed\n");
if (dpaa_set_portal_irq_affinity(c->dev, c->irq, c->cpu))
goto fail_affinity;
}
/* Need EQCR to be empty before continuing */
isdr &= ~QM_PIRQ_EQCI;

View File

@ -195,8 +195,10 @@ static int qman_offline_cpu(unsigned int cpu)
if (p) {
pcfg = qman_get_qm_portal_config(p);
if (pcfg) {
irq_set_affinity(pcfg->irq, cpumask_of(0));
qman_portal_update_sdest(pcfg, 0);
/* select any other online CPU */
cpu = cpumask_any_but(cpu_online_mask, cpu);
irq_set_affinity(pcfg->irq, cpumask_of(cpu));
qman_portal_update_sdest(pcfg, cpu);
}
}
return 0;

View File

@ -588,11 +588,7 @@ struct qe_firmware_info *qe_get_firmware_info(void)
}
/* Find the 'firmware' child node */
for_each_child_of_node(qe, fw) {
if (strcmp(fw->name, "firmware") == 0)
break;
}
fw = of_get_child_by_name(qe, "firmware");
of_node_put(qe);
/* Did we find the 'firmware' node? */

View File

@ -1,13 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
* Copyright 2011-2013 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
@ -69,7 +63,7 @@ static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
u32 val;
/* Read ISO and ISO2SW power down delays */
regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val);
regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val);
iso = val & 0x3f;
iso2sw = (val >> 8) & 0x3f;
@ -247,6 +241,7 @@ builtin_platform_driver(imx_pgc_power_domain_driver)
#define GPC_PGC_DOMAIN_ARM 0
#define GPC_PGC_DOMAIN_PU 1
#define GPC_PGC_DOMAIN_DISPLAY 2
#define GPC_PGC_DOMAIN_PCI 3
static struct genpd_power_state imx6_pm_domain_pu_state = {
.power_off_latency_ns = 25000,
@ -254,12 +249,13 @@ static struct genpd_power_state imx6_pm_domain_pu_state = {
};
static struct imx_pm_domain imx_gpc_domains[] = {
{
[GPC_PGC_DOMAIN_ARM] {
.base = {
.name = "ARM",
.flags = GENPD_FLAG_ALWAYS_ON,
},
}, {
},
[GPC_PGC_DOMAIN_PU] {
.base = {
.name = "PU",
.power_off = imx6_pm_domain_power_off,
@ -269,7 +265,8 @@ static struct imx_pm_domain imx_gpc_domains[] = {
},
.reg_offs = 0x260,
.cntr_pdn_bit = 0,
}, {
},
[GPC_PGC_DOMAIN_DISPLAY] {
.base = {
.name = "DISPLAY",
.power_off = imx6_pm_domain_power_off,
@ -277,7 +274,8 @@ static struct imx_pm_domain imx_gpc_domains[] = {
},
.reg_offs = 0x240,
.cntr_pdn_bit = 4,
}, {
},
[GPC_PGC_DOMAIN_PCI] {
.base = {
.name = "PCI",
.power_off = imx6_pm_domain_power_off,
@ -348,8 +346,8 @@ static const struct regmap_config imx_gpc_regmap_config = {
};
static struct generic_pm_domain *imx_gpc_onecell_domains[] = {
&imx_gpc_domains[0].base,
&imx_gpc_domains[1].base,
&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base,
&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base,
};
static struct genpd_onecell_data imx_gpc_onecell_data = {

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017 Impinj, Inc
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
@ -5,29 +6,23 @@
* Based on the code of analogus driver:
*
* Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <dt-bindings/power/imx7-power.h>
#define GPC_LPCR_A7_BSC 0x000
#define GPC_LPCR_A_CORE_BSC 0x000
#define GPC_PGC_CPU_MAPPING 0x0ec
#define USB_HSIC_PHY_A7_DOMAIN BIT(6)
#define USB_OTG2_PHY_A7_DOMAIN BIT(5)
#define USB_OTG1_PHY_A7_DOMAIN BIT(4)
#define PCIE_PHY_A7_DOMAIN BIT(3)
#define MIPI_PHY_A7_DOMAIN BIT(2)
#define USB_HSIC_PHY_A_CORE_DOMAIN BIT(6)
#define USB_OTG2_PHY_A_CORE_DOMAIN BIT(5)
#define USB_OTG1_PHY_A_CORE_DOMAIN BIT(4)
#define PCIE_PHY_A_CORE_DOMAIN BIT(3)
#define MIPI_PHY_A_CORE_DOMAIN BIT(2)
#define GPC_PU_PGC_SW_PUP_REQ 0x0f8
#define GPC_PU_PGC_SW_PDN_REQ 0x104
@ -53,7 +48,7 @@
#define GPC_PGC_CTRL_PCR BIT(0)
struct imx7_pgc_domain {
struct imx_pgc_domain {
struct generic_pm_domain genpd;
struct regmap *regmap;
struct regulator *regulator;
@ -69,11 +64,16 @@ struct imx7_pgc_domain {
struct device *dev;
};
static int imx7_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
struct imx_pgc_domain_data {
const struct imx_pgc_domain *domains;
size_t domains_num;
};
static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
bool on)
{
struct imx7_pgc_domain *domain = container_of(genpd,
struct imx7_pgc_domain,
struct imx_pgc_domain *domain = container_of(genpd,
struct imx_pgc_domain,
genpd);
unsigned int offset = on ?
GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ;
@ -150,24 +150,24 @@ unmap:
return ret;
}
static int imx7_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd)
static int imx_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd)
{
return imx7_gpc_pu_pgc_sw_pxx_req(genpd, true);
return imx_gpc_pu_pgc_sw_pxx_req(genpd, true);
}
static int imx7_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd)
static int imx_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd)
{
return imx7_gpc_pu_pgc_sw_pxx_req(genpd, false);
return imx_gpc_pu_pgc_sw_pxx_req(genpd, false);
}
static const struct imx7_pgc_domain imx7_pgc_domains[] = {
static const struct imx_pgc_domain imx7_pgc_domains[] = {
[IMX7_POWER_DOMAIN_MIPI_PHY] = {
.genpd = {
.name = "mipi-phy",
},
.bits = {
.pxx = MIPI_PHY_SW_Pxx_REQ,
.map = MIPI_PHY_A7_DOMAIN,
.map = MIPI_PHY_A_CORE_DOMAIN,
},
.voltage = 1000000,
.pgc = PGC_MIPI,
@ -179,7 +179,7 @@ static const struct imx7_pgc_domain imx7_pgc_domains[] = {
},
.bits = {
.pxx = PCIE_PHY_SW_Pxx_REQ,
.map = PCIE_PHY_A7_DOMAIN,
.map = PCIE_PHY_A_CORE_DOMAIN,
},
.voltage = 1000000,
.pgc = PGC_PCIE,
@ -191,16 +191,21 @@ static const struct imx7_pgc_domain imx7_pgc_domains[] = {
},
.bits = {
.pxx = USB_HSIC_PHY_SW_Pxx_REQ,
.map = USB_HSIC_PHY_A7_DOMAIN,
.map = USB_HSIC_PHY_A_CORE_DOMAIN,
},
.voltage = 1200000,
.pgc = PGC_USB_HSIC,
},
};
static int imx7_pgc_domain_probe(struct platform_device *pdev)
static const struct imx_pgc_domain_data imx7_pgc_domain_data = {
.domains = imx7_pgc_domains,
.domains_num = ARRAY_SIZE(imx7_pgc_domains),
};
static int imx_pgc_domain_probe(struct platform_device *pdev)
{
struct imx7_pgc_domain *domain = pdev->dev.platform_data;
struct imx_pgc_domain *domain = pdev->dev.platform_data;
int ret;
domain->dev = &pdev->dev;
@ -233,9 +238,9 @@ static int imx7_pgc_domain_probe(struct platform_device *pdev)
return ret;
}
static int imx7_pgc_domain_remove(struct platform_device *pdev)
static int imx_pgc_domain_remove(struct platform_device *pdev)
{
struct imx7_pgc_domain *domain = pdev->dev.platform_data;
struct imx_pgc_domain *domain = pdev->dev.platform_data;
of_genpd_del_provider(domain->dev->of_node);
pm_genpd_remove(&domain->genpd);
@ -243,25 +248,26 @@ static int imx7_pgc_domain_remove(struct platform_device *pdev)
return 0;
}
static const struct platform_device_id imx7_pgc_domain_id[] = {
{ "imx7-pgc-domain", },
static const struct platform_device_id imx_pgc_domain_id[] = {
{ "imx-pgc-domain", },
{ },
};
static struct platform_driver imx7_pgc_domain_driver = {
static struct platform_driver imx_pgc_domain_driver = {
.driver = {
.name = "imx7-pgc",
.name = "imx-pgc",
},
.probe = imx7_pgc_domain_probe,
.remove = imx7_pgc_domain_remove,
.id_table = imx7_pgc_domain_id,
.probe = imx_pgc_domain_probe,
.remove = imx_pgc_domain_remove,
.id_table = imx_pgc_domain_id,
};
builtin_platform_driver(imx7_pgc_domain_driver)
builtin_platform_driver(imx_pgc_domain_driver)
static int imx_gpcv2_probe(struct platform_device *pdev)
{
static const struct imx_pgc_domain_data *domain_data;
static const struct regmap_range yes_ranges[] = {
regmap_reg_range(GPC_LPCR_A7_BSC,
regmap_reg_range(GPC_LPCR_A_CORE_BSC,
GPC_M4_PU_PDN_FLG),
regmap_reg_range(GPC_PGC_CTRL(PGC_MIPI),
GPC_PGC_SR(PGC_MIPI)),
@ -307,9 +313,11 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
return ret;
}
domain_data = of_device_get_match_data(&pdev->dev);
for_each_child_of_node(pgc_np, np) {
struct platform_device *pd_pdev;
struct imx7_pgc_domain *domain;
struct imx_pgc_domain *domain;
u32 domain_index;
ret = of_property_read_u32(np, "reg", &domain_index);
@ -319,14 +327,14 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
return ret;
}
if (domain_index >= ARRAY_SIZE(imx7_pgc_domains)) {
if (domain_index >= domain_data->domains_num) {
dev_warn(dev,
"Domain index %d is out of bounds\n",
domain_index);
continue;
}
pd_pdev = platform_device_alloc("imx7-pgc-domain",
pd_pdev = platform_device_alloc("imx-pgc-domain",
domain_index);
if (!pd_pdev) {
dev_err(dev, "Failed to allocate platform device\n");
@ -335,8 +343,8 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
}
ret = platform_device_add_data(pd_pdev,
&imx7_pgc_domains[domain_index],
sizeof(imx7_pgc_domains[domain_index]));
&domain_data->domains[domain_index],
sizeof(domain_data->domains[domain_index]));
if (ret) {
platform_device_put(pd_pdev);
of_node_put(np);
@ -345,8 +353,8 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
domain = pd_pdev->dev.platform_data;
domain->regmap = regmap;
domain->genpd.power_on = imx7_gpc_pu_pgc_sw_pup_req;
domain->genpd.power_off = imx7_gpc_pu_pgc_sw_pdn_req;
domain->genpd.power_on = imx_gpc_pu_pgc_sw_pup_req;
domain->genpd.power_off = imx_gpc_pu_pgc_sw_pdn_req;
pd_pdev->dev.parent = dev;
pd_pdev->dev.of_node = np;
@ -363,7 +371,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
}
static const struct of_device_id imx_gpcv2_dt_ids[] = {
{ .compatible = "fsl,imx7d-gpc" },
{ .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, },
{ }
};

View File

@ -76,6 +76,13 @@
#define PWRAP_SLV_CAP_SECURITY BIT(2)
#define HAS_CAP(_c, _x) (((_c) & (_x)) == (_x))
/* Group of bits used for shown pwrap capability */
#define PWRAP_CAP_BRIDGE BIT(0)
#define PWRAP_CAP_RESET BIT(1)
#define PWRAP_CAP_DCM BIT(2)
#define PWRAP_CAP_INT1_EN BIT(3)
#define PWRAP_CAP_WDT_SRC1 BIT(4)
/* defines for slave device wrapper registers */
enum dew_regs {
PWRAP_DEW_BASE,
@ -91,6 +98,27 @@ enum dew_regs {
PWRAP_DEW_CIPHER_MODE,
PWRAP_DEW_CIPHER_SWRST,
/* MT6323 only regs */
PWRAP_DEW_CIPHER_EN,
PWRAP_DEW_RDDMY_NO,
/* MT6358 only regs */
PWRAP_SMT_CON1,
PWRAP_DRV_CON1,
PWRAP_FILTER_CON0,
PWRAP_GPIO_PULLEN0_CLR,
PWRAP_RG_SPI_CON0,
PWRAP_RG_SPI_RECORD0,
PWRAP_RG_SPI_CON2,
PWRAP_RG_SPI_CON3,
PWRAP_RG_SPI_CON4,
PWRAP_RG_SPI_CON5,
PWRAP_RG_SPI_CON6,
PWRAP_RG_SPI_CON7,
PWRAP_RG_SPI_CON8,
PWRAP_RG_SPI_CON13,
PWRAP_SPISLV_KEY,
/* MT6397 only regs */
PWRAP_DEW_EVENT_OUT_EN,
PWRAP_DEW_EVENT_SRC_EN,
@ -100,10 +128,6 @@ enum dew_regs {
PWRAP_DEW_EVENT_TEST,
PWRAP_DEW_CIPHER_LOAD,
PWRAP_DEW_CIPHER_START,
/* MT6323 only regs */
PWRAP_DEW_CIPHER_EN,
PWRAP_DEW_RDDMY_NO,
};
static const u32 mt6323_regs[] = {
@ -123,6 +147,64 @@ static const u32 mt6323_regs[] = {
[PWRAP_DEW_RDDMY_NO] = 0x01a4,
};
static const u32 mt6351_regs[] = {
[PWRAP_DEW_DIO_EN] = 0x02F2,
[PWRAP_DEW_READ_TEST] = 0x02F4,
[PWRAP_DEW_WRITE_TEST] = 0x02F6,
[PWRAP_DEW_CRC_EN] = 0x02FA,
[PWRAP_DEW_CRC_VAL] = 0x02FC,
[PWRAP_DEW_CIPHER_KEY_SEL] = 0x0300,
[PWRAP_DEW_CIPHER_IV_SEL] = 0x0302,
[PWRAP_DEW_CIPHER_EN] = 0x0304,
[PWRAP_DEW_CIPHER_RDY] = 0x0306,
[PWRAP_DEW_CIPHER_MODE] = 0x0308,
[PWRAP_DEW_CIPHER_SWRST] = 0x030A,
[PWRAP_DEW_RDDMY_NO] = 0x030C,
};
static const u32 mt6357_regs[] = {
[PWRAP_DEW_DIO_EN] = 0x040A,
[PWRAP_DEW_READ_TEST] = 0x040C,
[PWRAP_DEW_WRITE_TEST] = 0x040E,
[PWRAP_DEW_CRC_EN] = 0x0412,
[PWRAP_DEW_CRC_VAL] = 0x0414,
[PWRAP_DEW_CIPHER_KEY_SEL] = 0x0418,
[PWRAP_DEW_CIPHER_IV_SEL] = 0x041A,
[PWRAP_DEW_CIPHER_EN] = 0x041C,
[PWRAP_DEW_CIPHER_RDY] = 0x041E,
[PWRAP_DEW_CIPHER_MODE] = 0x0420,
[PWRAP_DEW_CIPHER_SWRST] = 0x0422,
[PWRAP_DEW_RDDMY_NO] = 0x0424,
};
static const u32 mt6358_regs[] = {
[PWRAP_SMT_CON1] = 0x0030,
[PWRAP_DRV_CON1] = 0x0038,
[PWRAP_FILTER_CON0] = 0x0040,
[PWRAP_GPIO_PULLEN0_CLR] = 0x0098,
[PWRAP_RG_SPI_CON0] = 0x0408,
[PWRAP_RG_SPI_RECORD0] = 0x040a,
[PWRAP_DEW_DIO_EN] = 0x040c,
[PWRAP_DEW_READ_TEST] = 0x040e,
[PWRAP_DEW_WRITE_TEST] = 0x0410,
[PWRAP_DEW_CRC_EN] = 0x0414,
[PWRAP_DEW_CIPHER_KEY_SEL] = 0x041a,
[PWRAP_DEW_CIPHER_IV_SEL] = 0x041c,
[PWRAP_DEW_CIPHER_EN] = 0x041e,
[PWRAP_DEW_CIPHER_RDY] = 0x0420,
[PWRAP_DEW_CIPHER_MODE] = 0x0422,
[PWRAP_DEW_CIPHER_SWRST] = 0x0424,
[PWRAP_RG_SPI_CON2] = 0x0432,
[PWRAP_RG_SPI_CON3] = 0x0434,
[PWRAP_RG_SPI_CON4] = 0x0436,
[PWRAP_RG_SPI_CON5] = 0x0438,
[PWRAP_RG_SPI_CON6] = 0x043a,
[PWRAP_RG_SPI_CON7] = 0x043c,
[PWRAP_RG_SPI_CON8] = 0x043e,
[PWRAP_RG_SPI_CON13] = 0x0448,
[PWRAP_SPISLV_KEY] = 0x044a,
};
static const u32 mt6397_regs[] = {
[PWRAP_DEW_BASE] = 0xbc00,
[PWRAP_DEW_EVENT_OUT_EN] = 0xbc00,
@ -146,21 +228,6 @@ static const u32 mt6397_regs[] = {
[PWRAP_DEW_CIPHER_SWRST] = 0xbc24,
};
static const u32 mt6351_regs[] = {
[PWRAP_DEW_DIO_EN] = 0x02F2,
[PWRAP_DEW_READ_TEST] = 0x02F4,
[PWRAP_DEW_WRITE_TEST] = 0x02F6,
[PWRAP_DEW_CRC_EN] = 0x02FA,
[PWRAP_DEW_CRC_VAL] = 0x02FC,
[PWRAP_DEW_CIPHER_KEY_SEL] = 0x0300,
[PWRAP_DEW_CIPHER_IV_SEL] = 0x0302,
[PWRAP_DEW_CIPHER_EN] = 0x0304,
[PWRAP_DEW_CIPHER_RDY] = 0x0306,
[PWRAP_DEW_CIPHER_MODE] = 0x0308,
[PWRAP_DEW_CIPHER_SWRST] = 0x030A,
[PWRAP_DEW_RDDMY_NO] = 0x030C,
};
enum pwrap_regs {
PWRAP_MUX_SEL,
PWRAP_WRAP_EN,
@ -221,6 +288,8 @@ enum pwrap_regs {
PWRAP_CIPHER_SWRST,
PWRAP_DCM_EN,
PWRAP_DCM_DBC_PRD,
PWRAP_EINT_STA0_ADR,
PWRAP_EINT_STA1_ADR,
/* MT2701 only regs */
PWRAP_ADC_CMD_ADDR,
@ -230,8 +299,6 @@ enum pwrap_regs {
PWRAP_ADC_RDATA_ADDR2,
/* MT7622 only regs */
PWRAP_EINT_STA0_ADR,
PWRAP_EINT_STA1_ADR,
PWRAP_STA,
PWRAP_CLR,
PWRAP_DVFS_ADR8,
@ -293,6 +360,27 @@ enum pwrap_regs {
PWRAP_DVFS_WDATA7,
PWRAP_SPMINF_STA,
PWRAP_CIPHER_EN,
/* MT8183 only regs */
PWRAP_SI_SAMPLE_CTRL,
PWRAP_CSLEXT_WRITE,
PWRAP_CSLEXT_READ,
PWRAP_EXT_CK_WRITE,
PWRAP_STAUPD_CTRL,
PWRAP_WACS_P2P_EN,
PWRAP_INIT_DONE_P2P,
PWRAP_WACS_MD32_EN,
PWRAP_INIT_DONE_MD32,
PWRAP_INT1_EN,
PWRAP_INT1_FLG,
PWRAP_INT1_CLR,
PWRAP_WDT_SRC_EN_1,
PWRAP_INT_GPS_AUXADC_CMD_ADDR,
PWRAP_INT_GPS_AUXADC_CMD,
PWRAP_INT_GPS_AUXADC_RDATA_ADDR,
PWRAP_EXT_GPS_AUXADC_RDATA_ADDR,
PWRAP_GPSINF_0_STA,
PWRAP_GPSINF_1_STA,
};
static int mt2701_regs[] = {
@ -381,6 +469,38 @@ static int mt2701_regs[] = {
[PWRAP_ADC_RDATA_ADDR2] = 0x154,
};
static int mt6765_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
[PWRAP_DIO_EN] = 0x8,
[PWRAP_RDDMY] = 0x20,
[PWRAP_CSHEXT_WRITE] = 0x24,
[PWRAP_CSHEXT_READ] = 0x28,
[PWRAP_CSLEXT_START] = 0x2C,
[PWRAP_CSLEXT_END] = 0x30,
[PWRAP_STAUPD_PRD] = 0x3C,
[PWRAP_HARB_HPRIO] = 0x68,
[PWRAP_HIPRIO_ARB_EN] = 0x6C,
[PWRAP_MAN_EN] = 0x7C,
[PWRAP_MAN_CMD] = 0x80,
[PWRAP_WACS0_EN] = 0x8C,
[PWRAP_WACS1_EN] = 0x94,
[PWRAP_WACS2_EN] = 0x9C,
[PWRAP_INIT_DONE2] = 0xA0,
[PWRAP_WACS2_CMD] = 0xC20,
[PWRAP_WACS2_RDATA] = 0xC24,
[PWRAP_WACS2_VLDCLR] = 0xC28,
[PWRAP_INT_EN] = 0xB4,
[PWRAP_INT_FLG_RAW] = 0xB8,
[PWRAP_INT_FLG] = 0xBC,
[PWRAP_INT_CLR] = 0xC0,
[PWRAP_TIMER_EN] = 0xE8,
[PWRAP_WDT_UNIT] = 0xF0,
[PWRAP_WDT_SRC_EN] = 0xF4,
[PWRAP_DCM_EN] = 0x1DC,
[PWRAP_DCM_DBC_PRD] = 0x1E0,
};
static int mt6797_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
@ -526,6 +646,79 @@ static int mt7622_regs[] = {
[PWRAP_SPI2_CTRL] = 0x244,
};
static int mt8135_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
[PWRAP_DIO_EN] = 0x8,
[PWRAP_SIDLY] = 0xc,
[PWRAP_CSHEXT] = 0x10,
[PWRAP_CSHEXT_WRITE] = 0x14,
[PWRAP_CSHEXT_READ] = 0x18,
[PWRAP_CSLEXT_START] = 0x1c,
[PWRAP_CSLEXT_END] = 0x20,
[PWRAP_STAUPD_PRD] = 0x24,
[PWRAP_STAUPD_GRPEN] = 0x28,
[PWRAP_STAUPD_MAN_TRIG] = 0x2c,
[PWRAP_STAUPD_STA] = 0x30,
[PWRAP_EVENT_IN_EN] = 0x34,
[PWRAP_EVENT_DST_EN] = 0x38,
[PWRAP_WRAP_STA] = 0x3c,
[PWRAP_RRARB_INIT] = 0x40,
[PWRAP_RRARB_EN] = 0x44,
[PWRAP_RRARB_STA0] = 0x48,
[PWRAP_RRARB_STA1] = 0x4c,
[PWRAP_HARB_INIT] = 0x50,
[PWRAP_HARB_HPRIO] = 0x54,
[PWRAP_HIPRIO_ARB_EN] = 0x58,
[PWRAP_HARB_STA0] = 0x5c,
[PWRAP_HARB_STA1] = 0x60,
[PWRAP_MAN_EN] = 0x64,
[PWRAP_MAN_CMD] = 0x68,
[PWRAP_MAN_RDATA] = 0x6c,
[PWRAP_MAN_VLDCLR] = 0x70,
[PWRAP_WACS0_EN] = 0x74,
[PWRAP_INIT_DONE0] = 0x78,
[PWRAP_WACS0_CMD] = 0x7c,
[PWRAP_WACS0_RDATA] = 0x80,
[PWRAP_WACS0_VLDCLR] = 0x84,
[PWRAP_WACS1_EN] = 0x88,
[PWRAP_INIT_DONE1] = 0x8c,
[PWRAP_WACS1_CMD] = 0x90,
[PWRAP_WACS1_RDATA] = 0x94,
[PWRAP_WACS1_VLDCLR] = 0x98,
[PWRAP_WACS2_EN] = 0x9c,
[PWRAP_INIT_DONE2] = 0xa0,
[PWRAP_WACS2_CMD] = 0xa4,
[PWRAP_WACS2_RDATA] = 0xa8,
[PWRAP_WACS2_VLDCLR] = 0xac,
[PWRAP_INT_EN] = 0xb0,
[PWRAP_INT_FLG_RAW] = 0xb4,
[PWRAP_INT_FLG] = 0xb8,
[PWRAP_INT_CLR] = 0xbc,
[PWRAP_SIG_ADR] = 0xc0,
[PWRAP_SIG_MODE] = 0xc4,
[PWRAP_SIG_VALUE] = 0xc8,
[PWRAP_SIG_ERRVAL] = 0xcc,
[PWRAP_CRC_EN] = 0xd0,
[PWRAP_EVENT_STA] = 0xd4,
[PWRAP_EVENT_STACLR] = 0xd8,
[PWRAP_TIMER_EN] = 0xdc,
[PWRAP_TIMER_STA] = 0xe0,
[PWRAP_WDT_UNIT] = 0xe4,
[PWRAP_WDT_SRC_EN] = 0xe8,
[PWRAP_WDT_FLG] = 0xec,
[PWRAP_DEBUG_INT_SEL] = 0xf0,
[PWRAP_CIPHER_KEY_SEL] = 0x134,
[PWRAP_CIPHER_IV_SEL] = 0x138,
[PWRAP_CIPHER_LOAD] = 0x13c,
[PWRAP_CIPHER_START] = 0x140,
[PWRAP_CIPHER_RDY] = 0x144,
[PWRAP_CIPHER_MODE] = 0x148,
[PWRAP_CIPHER_SWRST] = 0x14c,
[PWRAP_DCM_EN] = 0x15c,
[PWRAP_DCM_DBC_PRD] = 0x160,
};
static int mt8173_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
@ -608,92 +801,74 @@ static int mt8173_regs[] = {
[PWRAP_DCM_DBC_PRD] = 0x148,
};
static int mt8135_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
[PWRAP_DIO_EN] = 0x8,
[PWRAP_SIDLY] = 0xc,
[PWRAP_CSHEXT] = 0x10,
[PWRAP_CSHEXT_WRITE] = 0x14,
[PWRAP_CSHEXT_READ] = 0x18,
[PWRAP_CSLEXT_START] = 0x1c,
[PWRAP_CSLEXT_END] = 0x20,
[PWRAP_STAUPD_PRD] = 0x24,
[PWRAP_STAUPD_GRPEN] = 0x28,
[PWRAP_STAUPD_MAN_TRIG] = 0x2c,
[PWRAP_STAUPD_STA] = 0x30,
[PWRAP_EVENT_IN_EN] = 0x34,
[PWRAP_EVENT_DST_EN] = 0x38,
[PWRAP_WRAP_STA] = 0x3c,
[PWRAP_RRARB_INIT] = 0x40,
[PWRAP_RRARB_EN] = 0x44,
[PWRAP_RRARB_STA0] = 0x48,
[PWRAP_RRARB_STA1] = 0x4c,
[PWRAP_HARB_INIT] = 0x50,
[PWRAP_HARB_HPRIO] = 0x54,
[PWRAP_HIPRIO_ARB_EN] = 0x58,
[PWRAP_HARB_STA0] = 0x5c,
[PWRAP_HARB_STA1] = 0x60,
[PWRAP_MAN_EN] = 0x64,
[PWRAP_MAN_CMD] = 0x68,
[PWRAP_MAN_RDATA] = 0x6c,
[PWRAP_MAN_VLDCLR] = 0x70,
[PWRAP_WACS0_EN] = 0x74,
[PWRAP_INIT_DONE0] = 0x78,
[PWRAP_WACS0_CMD] = 0x7c,
[PWRAP_WACS0_RDATA] = 0x80,
[PWRAP_WACS0_VLDCLR] = 0x84,
[PWRAP_WACS1_EN] = 0x88,
[PWRAP_INIT_DONE1] = 0x8c,
[PWRAP_WACS1_CMD] = 0x90,
[PWRAP_WACS1_RDATA] = 0x94,
[PWRAP_WACS1_VLDCLR] = 0x98,
[PWRAP_WACS2_EN] = 0x9c,
[PWRAP_INIT_DONE2] = 0xa0,
[PWRAP_WACS2_CMD] = 0xa4,
[PWRAP_WACS2_RDATA] = 0xa8,
[PWRAP_WACS2_VLDCLR] = 0xac,
[PWRAP_INT_EN] = 0xb0,
[PWRAP_INT_FLG_RAW] = 0xb4,
[PWRAP_INT_FLG] = 0xb8,
[PWRAP_INT_CLR] = 0xbc,
[PWRAP_SIG_ADR] = 0xc0,
[PWRAP_SIG_MODE] = 0xc4,
[PWRAP_SIG_VALUE] = 0xc8,
[PWRAP_SIG_ERRVAL] = 0xcc,
[PWRAP_CRC_EN] = 0xd0,
[PWRAP_EVENT_STA] = 0xd4,
[PWRAP_EVENT_STACLR] = 0xd8,
[PWRAP_TIMER_EN] = 0xdc,
[PWRAP_TIMER_STA] = 0xe0,
[PWRAP_WDT_UNIT] = 0xe4,
[PWRAP_WDT_SRC_EN] = 0xe8,
[PWRAP_WDT_FLG] = 0xec,
[PWRAP_DEBUG_INT_SEL] = 0xf0,
[PWRAP_CIPHER_KEY_SEL] = 0x134,
[PWRAP_CIPHER_IV_SEL] = 0x138,
[PWRAP_CIPHER_LOAD] = 0x13c,
[PWRAP_CIPHER_START] = 0x140,
[PWRAP_CIPHER_RDY] = 0x144,
[PWRAP_CIPHER_MODE] = 0x148,
[PWRAP_CIPHER_SWRST] = 0x14c,
[PWRAP_DCM_EN] = 0x15c,
[PWRAP_DCM_DBC_PRD] = 0x160,
static int mt8183_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
[PWRAP_DIO_EN] = 0x8,
[PWRAP_SI_SAMPLE_CTRL] = 0xC,
[PWRAP_RDDMY] = 0x14,
[PWRAP_CSHEXT_WRITE] = 0x18,
[PWRAP_CSHEXT_READ] = 0x1C,
[PWRAP_CSLEXT_WRITE] = 0x20,
[PWRAP_CSLEXT_READ] = 0x24,
[PWRAP_EXT_CK_WRITE] = 0x28,
[PWRAP_STAUPD_CTRL] = 0x30,
[PWRAP_STAUPD_GRPEN] = 0x34,
[PWRAP_EINT_STA0_ADR] = 0x38,
[PWRAP_HARB_HPRIO] = 0x5C,
[PWRAP_HIPRIO_ARB_EN] = 0x60,
[PWRAP_MAN_EN] = 0x70,
[PWRAP_MAN_CMD] = 0x74,
[PWRAP_WACS0_EN] = 0x80,
[PWRAP_INIT_DONE0] = 0x84,
[PWRAP_WACS1_EN] = 0x88,
[PWRAP_INIT_DONE1] = 0x8C,
[PWRAP_WACS2_EN] = 0x90,
[PWRAP_INIT_DONE2] = 0x94,
[PWRAP_WACS_P2P_EN] = 0xA0,
[PWRAP_INIT_DONE_P2P] = 0xA4,
[PWRAP_WACS_MD32_EN] = 0xA8,
[PWRAP_INIT_DONE_MD32] = 0xAC,
[PWRAP_INT_EN] = 0xB0,
[PWRAP_INT_FLG] = 0xB8,
[PWRAP_INT_CLR] = 0xBC,
[PWRAP_INT1_EN] = 0xC0,
[PWRAP_INT1_FLG] = 0xC8,
[PWRAP_INT1_CLR] = 0xCC,
[PWRAP_SIG_ADR] = 0xD0,
[PWRAP_CRC_EN] = 0xE0,
[PWRAP_TIMER_EN] = 0xE4,
[PWRAP_WDT_UNIT] = 0xEC,
[PWRAP_WDT_SRC_EN] = 0xF0,
[PWRAP_WDT_SRC_EN_1] = 0xF4,
[PWRAP_INT_GPS_AUXADC_CMD_ADDR] = 0x1DC,
[PWRAP_INT_GPS_AUXADC_CMD] = 0x1E0,
[PWRAP_INT_GPS_AUXADC_RDATA_ADDR] = 0x1E4,
[PWRAP_EXT_GPS_AUXADC_RDATA_ADDR] = 0x1E8,
[PWRAP_GPSINF_0_STA] = 0x1EC,
[PWRAP_GPSINF_1_STA] = 0x1F0,
[PWRAP_WACS2_CMD] = 0xC20,
[PWRAP_WACS2_RDATA] = 0xC24,
[PWRAP_WACS2_VLDCLR] = 0xC28,
};
enum pmic_type {
PMIC_MT6323,
PMIC_MT6351,
PMIC_MT6357,
PMIC_MT6358,
PMIC_MT6380,
PMIC_MT6397,
};
enum pwrap_type {
PWRAP_MT2701,
PWRAP_MT6765,
PWRAP_MT6797,
PWRAP_MT7622,
PWRAP_MT8135,
PWRAP_MT8173,
PWRAP_MT8183,
};
struct pmic_wrapper;
@ -731,9 +906,11 @@ struct pmic_wrapper_type {
enum pwrap_type type;
u32 arb_en_all;
u32 int_en_all;
u32 int1_en_all;
u32 spi_w;
u32 wdt_src;
unsigned int has_bridge:1;
/* Flags indicating the capability for the target pwrap */
u32 caps;
int (*init_reg_clock)(struct pmic_wrapper *wrp);
int (*init_soc_specific)(struct pmic_wrapper *wrp);
};
@ -1096,7 +1273,7 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp)
ret = pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_RDY],
&rdata);
if (ret)
return 0;
return false;
return rdata == 1;
}
@ -1117,6 +1294,7 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_CIPHER_START);
break;
case PWRAP_MT2701:
case PWRAP_MT6765:
case PWRAP_MT6797:
case PWRAP_MT8173:
pwrap_writel(wrp, 1, PWRAP_CIPHER_EN);
@ -1124,6 +1302,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
case PWRAP_MT7622:
pwrap_writel(wrp, 0, PWRAP_CIPHER_EN);
break;
case PWRAP_MT8183:
break;
}
/* Config cipher mode @PMIC */
@ -1141,6 +1321,7 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
break;
case PMIC_MT6323:
case PMIC_MT6351:
case PMIC_MT6357:
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_EN],
0x1);
break;
@ -1276,6 +1457,23 @@ static int pwrap_mt7622_init_soc_specific(struct pmic_wrapper *wrp)
return 0;
}
static int pwrap_mt8183_init_soc_specific(struct pmic_wrapper *wrp)
{
pwrap_writel(wrp, 0xf5, PWRAP_STAUPD_GRPEN);
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_EN], 0x1);
pwrap_writel(wrp, 1, PWRAP_CRC_EN);
pwrap_writel(wrp, 0x416, PWRAP_SIG_ADR);
pwrap_writel(wrp, 0x42e, PWRAP_EINT_STA0_ADR);
pwrap_writel(wrp, 1, PWRAP_WACS_P2P_EN);
pwrap_writel(wrp, 1, PWRAP_WACS_MD32_EN);
pwrap_writel(wrp, 1, PWRAP_INIT_DONE_P2P);
pwrap_writel(wrp, 1, PWRAP_INIT_DONE_MD32);
return 0;
}
static int pwrap_init(struct pmic_wrapper *wrp)
{
int ret;
@ -1348,7 +1546,7 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 1, PWRAP_INIT_DONE0);
pwrap_writel(wrp, 1, PWRAP_INIT_DONE1);
if (wrp->master->has_bridge) {
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_BRIDGE)) {
writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE3);
writel(1, wrp->bridge_base + PWRAP_MT8135_BRIDGE_INIT_DONE4);
}
@ -1362,11 +1560,15 @@ static irqreturn_t pwrap_interrupt(int irqno, void *dev_id)
struct pmic_wrapper *wrp = dev_id;
rdata = pwrap_readl(wrp, PWRAP_INT_FLG);
dev_err(wrp->dev, "unexpected interrupt int=0x%x\n", rdata);
pwrap_writel(wrp, 0xffffffff, PWRAP_INT_CLR);
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_INT1_EN)) {
rdata = pwrap_readl(wrp, PWRAP_INT1_FLG);
dev_err(wrp->dev, "unexpected interrupt int1=0x%x\n", rdata);
pwrap_writel(wrp, 0xffffffff, PWRAP_INT1_CLR);
}
return IRQ_HANDLED;
}
@ -1398,6 +1600,33 @@ static const struct pwrap_slv_type pmic_mt6323 = {
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6351 = {
.dew_regs = mt6351_regs,
.type = PMIC_MT6351,
.regmap = &pwrap_regmap_config16,
.caps = 0,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6357 = {
.dew_regs = mt6357_regs,
.type = PMIC_MT6357,
.regmap = &pwrap_regmap_config16,
.caps = 0,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6358 = {
.dew_regs = mt6358_regs,
.type = PMIC_MT6358,
.regmap = &pwrap_regmap_config16,
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6380 = {
.dew_regs = NULL,
.type = PMIC_MT6380,
@ -1417,19 +1646,19 @@ static const struct pwrap_slv_type pmic_mt6397 = {
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6351 = {
.dew_regs = mt6351_regs,
.type = PMIC_MT6351,
.regmap = &pwrap_regmap_config16,
.caps = 0,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct of_device_id of_slave_match_tbl[] = {
{
.compatible = "mediatek,mt6323",
.data = &pmic_mt6323,
}, {
.compatible = "mediatek,mt6351",
.data = &pmic_mt6351,
}, {
.compatible = "mediatek,mt6357",
.data = &pmic_mt6357,
}, {
.compatible = "mediatek,mt6358",
.data = &pmic_mt6358,
}, {
/* The MT6380 PMIC only implements a regulator, so we bind it
* directly instead of using a MFD.
@ -1439,9 +1668,6 @@ static const struct of_device_id of_slave_match_tbl[] = {
}, {
.compatible = "mediatek,mt6397",
.data = &pmic_mt6397,
}, {
.compatible = "mediatek,mt6351",
.data = &pmic_mt6351,
}, {
/* sentinel */
}
@ -1453,21 +1679,35 @@ static const struct pmic_wrapper_type pwrap_mt2701 = {
.type = PWRAP_MT2701,
.arb_en_all = 0x3f,
.int_en_all = ~(u32)(BIT(31) | BIT(2)),
.int1_en_all = 0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE_NEW,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.has_bridge = 0,
.caps = PWRAP_CAP_RESET | PWRAP_CAP_DCM,
.init_reg_clock = pwrap_mt2701_init_reg_clock,
.init_soc_specific = pwrap_mt2701_init_soc_specific,
};
static const struct pmic_wrapper_type pwrap_mt6765 = {
.regs = mt6765_regs,
.type = PWRAP_MT6765,
.arb_en_all = 0x3fd35,
.int_en_all = 0xffffffff,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.caps = PWRAP_CAP_RESET | PWRAP_CAP_DCM,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = NULL,
};
static const struct pmic_wrapper_type pwrap_mt6797 = {
.regs = mt6797_regs,
.type = PWRAP_MT6797,
.arb_en_all = 0x01fff,
.int_en_all = 0xffffffc6,
.int1_en_all = 0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.has_bridge = 0,
.caps = PWRAP_CAP_RESET | PWRAP_CAP_DCM,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = NULL,
};
@ -1477,9 +1717,10 @@ static const struct pmic_wrapper_type pwrap_mt7622 = {
.type = PWRAP_MT7622,
.arb_en_all = 0xff,
.int_en_all = ~(u32)BIT(31),
.int1_en_all = 0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.has_bridge = 0,
.caps = PWRAP_CAP_RESET | PWRAP_CAP_DCM,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt7622_init_soc_specific,
};
@ -1489,9 +1730,10 @@ static const struct pmic_wrapper_type pwrap_mt8135 = {
.type = PWRAP_MT8135,
.arb_en_all = 0x1ff,
.int_en_all = ~(u32)(BIT(31) | BIT(1)),
.int1_en_all = 0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.has_bridge = 1,
.caps = PWRAP_CAP_BRIDGE | PWRAP_CAP_RESET | PWRAP_CAP_DCM,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt8135_init_soc_specific,
};
@ -1501,17 +1743,34 @@ static const struct pmic_wrapper_type pwrap_mt8173 = {
.type = PWRAP_MT8173,
.arb_en_all = 0x3f,
.int_en_all = ~(u32)(BIT(31) | BIT(1)),
.int1_en_all = 0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_NO_STAUPD,
.has_bridge = 0,
.caps = PWRAP_CAP_RESET | PWRAP_CAP_DCM,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt8173_init_soc_specific,
};
static const struct pmic_wrapper_type pwrap_mt8183 = {
.regs = mt8183_regs,
.type = PWRAP_MT8183,
.arb_en_all = 0x3fa75,
.int_en_all = 0xffffffff,
.int1_en_all = 0xeef7ffff,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_WDT_SRC1,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt8183_init_soc_specific,
};
static const struct of_device_id of_pwrap_match_tbl[] = {
{
.compatible = "mediatek,mt2701-pwrap",
.data = &pwrap_mt2701,
}, {
.compatible = "mediatek,mt6765-pwrap",
.data = &pwrap_mt6765,
}, {
.compatible = "mediatek,mt6797-pwrap",
.data = &pwrap_mt6797,
@ -1524,6 +1783,9 @@ static const struct of_device_id of_pwrap_match_tbl[] = {
}, {
.compatible = "mediatek,mt8173-pwrap",
.data = &pwrap_mt8173,
}, {
.compatible = "mediatek,mt8183-pwrap",
.data = &pwrap_mt8183,
}, {
/* sentinel */
}
@ -1561,14 +1823,16 @@ static int pwrap_probe(struct platform_device *pdev)
if (IS_ERR(wrp->base))
return PTR_ERR(wrp->base);
wrp->rstc = devm_reset_control_get(wrp->dev, "pwrap");
if (IS_ERR(wrp->rstc)) {
ret = PTR_ERR(wrp->rstc);
dev_dbg(wrp->dev, "cannot get pwrap reset: %d\n", ret);
return ret;
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_RESET)) {
wrp->rstc = devm_reset_control_get(wrp->dev, "pwrap");
if (IS_ERR(wrp->rstc)) {
ret = PTR_ERR(wrp->rstc);
dev_dbg(wrp->dev, "cannot get pwrap reset: %d\n", ret);
return ret;
}
}
if (wrp->master->has_bridge) {
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_BRIDGE)) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"pwrap-bridge");
wrp->bridge_base = devm_ioremap_resource(wrp->dev, res);
@ -1608,8 +1872,10 @@ static int pwrap_probe(struct platform_device *pdev)
goto err_out1;
/* Enable internal dynamic clock */
pwrap_writel(wrp, 1, PWRAP_DCM_EN);
pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD);
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_DCM)) {
pwrap_writel(wrp, 1, PWRAP_DCM_EN);
pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD);
}
/*
* The PMIC could already be initialized by the bootloader.
@ -1636,8 +1902,17 @@ static int pwrap_probe(struct platform_device *pdev)
* so STAUPD of WDT_SRC which should be turned off
*/
pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN);
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_WDT_SRC1))
pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN_1);
pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN);
/*
* We add INT1 interrupt to handle starvation and request exception
* If we support it, we should enable it here.
*/
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_INT1_EN))
pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN);
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt,

View File

@ -33,7 +33,7 @@ config QCOM_GLINK_SSR
config QCOM_GSBI
tristate "QCOM General Serial Bus Interface"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
select MFD_SYSCON
help
Say y here to enable GSBI support. The GSBI provides control
@ -42,7 +42,7 @@ config QCOM_GSBI
config QCOM_LLCC
tristate "Qualcomm Technologies, Inc. LLCC driver"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
help
Qualcomm Technologies, Inc. platform specific
Last Level Cache Controller(LLCC) driver. This provides interfaces
@ -73,7 +73,8 @@ config QCOM_PM
config QCOM_QMI_HELPERS
tristate
depends on (ARCH_QCOM || COMPILE_TEST) && NET
depends on ARCH_QCOM || COMPILE_TEST
depends on NET
help
Helper library for handling QMI encoded messages. QMI encoded
messages are used in communication between the majority of QRTR
@ -94,7 +95,7 @@ config QCOM_RMTFS_MEM
config QCOM_RPMH
bool "Qualcomm RPM-Hardened (RPMH) Communication"
depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST
depends on ARCH_QCOM && ARM64 || COMPILE_TEST
help
Support for communication with the hardened-RPM blocks in
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
@ -104,7 +105,7 @@ config QCOM_RPMH
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
depends on HWSPINLOCK
help
Say y here to enable support for the Qualcomm Shared Memory Manager.
@ -113,8 +114,8 @@ config QCOM_SMEM
config QCOM_SMD_RPM
tristate "Qualcomm Resource Power Manager (RPM) over SMD"
depends on ARCH_QCOM
depends on RPMSG && OF
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
help
If you say yes to this option, support will be included for the
Resource Power Manager system found in the Qualcomm 8974 based
@ -134,6 +135,7 @@ config QCOM_SMP2P
depends on MAILBOX
depends on QCOM_SMEM
select QCOM_SMEM_STATE
select IRQ_DOMAIN
help
Say yes here to support the Qualcomm Shared Memory Point to Point
protocol.
@ -142,13 +144,14 @@ config QCOM_SMSM
tristate "Qualcomm Shared Memory State Machine"
depends on QCOM_SMEM
select QCOM_SMEM_STATE
select IRQ_DOMAIN
help
Say yes here to support the Qualcomm Shared Memory State Machine.
The state machine is represented by bits in shared memory.
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
help
Client driver for the WCNSS_CTRL SMD channel, used to download nv
@ -156,7 +159,7 @@ config QCOM_WCNSS_CTRL
config QCOM_APR
tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
help
Enable APR IPC protocol support between

View File

@ -87,7 +87,7 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
}
if (hdr->pkt_size < APR_HDR_SIZE || hdr->pkt_size != len) {
dev_err(apr->dev, "APR: Wrong paket size\n");
dev_err(apr->dev, "APR: Wrong packet size\n");
return -EINVAL;
}
@ -221,7 +221,7 @@ static int apr_add_device(struct device *dev, struct device_node *np,
if (np)
snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np);
else
strncpy(adev->name, id->name, APR_NAME_SIZE);
strscpy(adev->name, id->name, APR_NAME_SIZE);
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
id->domain_id, id->svc_id);

View File

@ -13,6 +13,7 @@
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/soc/qcom/llcc-qcom.h>
@ -106,22 +107,24 @@ static int llcc_update_act_ctrl(u32 sid,
u32 slice_status;
int ret;
act_ctrl_reg = drv_data->bcast_off + LLCC_TRP_ACT_CTRLn(sid);
status_reg = drv_data->bcast_off + LLCC_TRP_STATUSn(sid);
act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
status_reg = LLCC_TRP_STATUSn(sid);
/* Set the ACTIVE trigger */
act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val);
ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
act_ctrl_reg_val);
if (ret)
return ret;
/* Clear the ACTIVE trigger */
act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val);
ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
act_ctrl_reg_val);
if (ret)
return ret;
ret = regmap_read_poll_timeout(drv_data->regmap, status_reg,
ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
slice_status, !(slice_status & status),
0, LLCC_STATUS_READ_DELAY);
return ret;
@ -223,19 +226,16 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
u32 attr0_val;
u32 max_cap_cacheline;
u32 sz;
int ret;
int ret = 0;
const struct llcc_slice_config *llcc_table;
struct llcc_slice_desc desc;
u32 bcast_off = drv_data->bcast_off;
sz = drv_data->cfg_size;
llcc_table = drv_data->cfg;
for (i = 0; i < sz; i++) {
attr1_cfg = bcast_off +
LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
attr0_cfg = bcast_off +
LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
attr1_val = llcc_table[i].cache_mode;
attr1_val |= llcc_table[i].probe_target_ways <<
@ -260,10 +260,12 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
ret = regmap_write(drv_data->regmap, attr1_cfg, attr1_val);
ret = regmap_write(drv_data->bcast_regmap, attr1_cfg,
attr1_val);
if (ret)
return ret;
ret = regmap_write(drv_data->regmap, attr0_cfg, attr0_val);
ret = regmap_write(drv_data->bcast_regmap, attr0_cfg,
attr0_val);
if (ret)
return ret;
if (llcc_table[i].activate_on_init) {
@ -279,24 +281,37 @@ int qcom_llcc_probe(struct platform_device *pdev,
{
u32 num_banks;
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *base;
struct resource *llcc_banks_res, *llcc_bcast_res;
void __iomem *llcc_banks_base, *llcc_bcast_base;
int ret, i;
struct platform_device *llcc_edac;
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
if (!drv_data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"llcc_base");
llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res);
if (IS_ERR(llcc_banks_base))
return PTR_ERR(llcc_banks_base);
drv_data->regmap = devm_regmap_init_mmio(dev, base,
&llcc_regmap_config);
drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base,
&llcc_regmap_config);
if (IS_ERR(drv_data->regmap))
return PTR_ERR(drv_data->regmap);
llcc_bcast_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"llcc_broadcast_base");
llcc_bcast_base = devm_ioremap_resource(&pdev->dev, llcc_bcast_res);
if (IS_ERR(llcc_bcast_base))
return PTR_ERR(llcc_bcast_base);
drv_data->bcast_regmap = devm_regmap_init_mmio(dev, llcc_bcast_base,
&llcc_regmap_config);
if (IS_ERR(drv_data->bcast_regmap))
return PTR_ERR(drv_data->bcast_regmap);
ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
&num_banks);
if (ret)
@ -318,8 +333,6 @@ int qcom_llcc_probe(struct platform_device *pdev,
for (i = 0; i < num_banks; i++)
drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
drv_data->bcast_off = num_banks * BANK_OFFSET_STRIDE;
drv_data->bitmap = devm_kcalloc(dev,
BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
GFP_KERNEL);
@ -331,7 +344,20 @@ int qcom_llcc_probe(struct platform_device *pdev,
mutex_init(&drv_data->lock);
platform_set_drvdata(pdev, drv_data);
return qcom_llcc_cfg_program(pdev);
ret = qcom_llcc_cfg_program(pdev);
if (ret)
return ret;
drv_data->ecc_irq = platform_get_irq(pdev, 0);
if (drv_data->ecc_irq >= 0) {
llcc_edac = platform_device_register_data(&pdev->dev,
"qcom_llcc_edac", -1, drv_data,
sizeof(*drv_data));
if (IS_ERR(llcc_edac))
dev_err(dev, "Failed to register llcc edac driver\n");
}
return ret;
}
EXPORT_SYMBOL_GPL(qcom_llcc_probe);

View File

@ -212,6 +212,11 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
goto remove_cdev;
} else if (!ret) {
if (!qcom_scm_is_available()) {
ret = -EPROBE_DEFER;
goto remove_cdev;
}
perms[0].vmid = QCOM_SCM_VMID_HLOS;
perms[0].perm = QCOM_SCM_PERM_RW;
perms[1].vmid = vmid;

View File

@ -121,6 +121,7 @@ static int tcs_invalidate(struct rsc_drv *drv, int type)
return -EAGAIN;
}
write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0);
write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, m, 0);
}
bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
spin_unlock(&tcs->lock);
@ -239,6 +240,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
skip:
/* Reclaim the TCS */
write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0);
write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, i, 0);
write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i));
spin_lock(&drv->lock);
clear_bit(i, drv->tcs_in_use);

View File

@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smem.h>
@ -277,7 +278,7 @@ struct qcom_smem {
u32 item_count;
unsigned num_regions;
struct smem_region regions[0];
struct smem_region regions[];
};
static void *
@ -489,7 +490,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
size_t *size)
{
struct smem_header *header;
struct smem_region *area;
struct smem_region *region;
struct smem_global_entry *entry;
u32 aux_base;
unsigned i;
@ -502,12 +503,12 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
for (i = 0; i < smem->num_regions; i++) {
area = &smem->regions[i];
region = &smem->regions[i];
if (area->aux_base == aux_base || !aux_base) {
if (region->aux_base == aux_base || !aux_base) {
if (size != NULL)
*size = le32_to_cpu(entry->size);
return area->virt_base + le32_to_cpu(entry->offset);
return region->virt_base + le32_to_cpu(entry->offset);
}
}
@ -722,12 +723,59 @@ static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
return le16_to_cpu(info->num_items);
}
/*
* Validate the partition header for a partition whose partition
* table entry is supplied. Returns a pointer to its header if
* valid, or a null pointer otherwise.
*/
static struct smem_partition_header *
qcom_smem_partition_header(struct qcom_smem *smem,
struct smem_ptable_entry *entry, u16 host0, u16 host1)
{
struct smem_partition_header *header;
u32 size;
header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
dev_err(smem->dev, "bad partition magic %02x %02x %02x %02x\n",
header->magic[0], header->magic[1],
header->magic[2], header->magic[3]);
return NULL;
}
if (host0 != le16_to_cpu(header->host0)) {
dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
host0, le16_to_cpu(header->host0));
return NULL;
}
if (host1 != le16_to_cpu(header->host1)) {
dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
host1, le16_to_cpu(header->host1));
return NULL;
}
size = le32_to_cpu(header->size);
if (size != le32_to_cpu(entry->size)) {
dev_err(smem->dev, "bad partition size (%u != %u)\n",
size, le32_to_cpu(entry->size));
return NULL;
}
if (le32_to_cpu(header->offset_free_uncached) > size) {
dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
le32_to_cpu(header->offset_free_uncached), size);
return NULL;
}
return header;
}
static int qcom_smem_set_global_partition(struct qcom_smem *smem)
{
struct smem_partition_header *header;
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
u32 host0, host1, size;
bool found = false;
int i;
@ -742,10 +790,15 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
host0 = le16_to_cpu(entry->host0);
host1 = le16_to_cpu(entry->host1);
if (!le32_to_cpu(entry->offset))
continue;
if (!le32_to_cpu(entry->size))
continue;
if (host0 == SMEM_GLOBAL_HOST && host0 == host1) {
if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
continue;
if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) {
found = true;
break;
}
@ -756,36 +809,10 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
return -EINVAL;
}
if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
dev_err(smem->dev, "Invalid entry for global partition\n");
header = qcom_smem_partition_header(smem, entry,
SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
if (!header)
return -EINVAL;
}
header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
host0 = le16_to_cpu(header->host0);
host1 = le16_to_cpu(header->host1);
if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
dev_err(smem->dev, "Global partition has invalid magic\n");
return -EINVAL;
}
if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
dev_err(smem->dev, "Global partition hosts are invalid\n");
return -EINVAL;
}
if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
dev_err(smem->dev, "Global partition has invalid size\n");
return -EINVAL;
}
size = le32_to_cpu(header->offset_free_uncached);
if (size > le32_to_cpu(header->size)) {
dev_err(smem->dev,
"Global partition has invalid free pointer\n");
return -EINVAL;
}
smem->global_partition = header;
smem->global_cacheline = le32_to_cpu(entry->cacheline);
@ -793,14 +820,14 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
return 0;
}
static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
unsigned int local_host)
static int
qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
{
struct smem_partition_header *header;
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
unsigned int remote_host;
u32 host0, host1;
u16 host0, host1;
int i;
ptable = qcom_smem_get_ptable(smem);
@ -809,71 +836,33 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
host0 = le16_to_cpu(entry->host0);
host1 = le16_to_cpu(entry->host1);
if (host0 != local_host && host1 != local_host)
continue;
if (!le32_to_cpu(entry->offset))
continue;
if (!le32_to_cpu(entry->size))
continue;
host0 = le16_to_cpu(entry->host0);
host1 = le16_to_cpu(entry->host1);
if (host0 == local_host)
remote_host = host1;
else
else if (host1 == local_host)
remote_host = host0;
else
continue;
if (remote_host >= SMEM_HOST_COUNT) {
dev_err(smem->dev,
"Invalid remote host %d\n",
remote_host);
dev_err(smem->dev, "bad host %hu\n", remote_host);
return -EINVAL;
}
if (smem->partitions[remote_host]) {
dev_err(smem->dev,
"Already found a partition for host %d\n",
remote_host);
dev_err(smem->dev, "duplicate host %hu\n", remote_host);
return -EINVAL;
}
header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
host0 = le16_to_cpu(header->host0);
host1 = le16_to_cpu(header->host1);
if (memcmp(header->magic, SMEM_PART_MAGIC,
sizeof(header->magic))) {
dev_err(smem->dev,
"Partition %d has invalid magic\n", i);
header = qcom_smem_partition_header(smem, entry, host0, host1);
if (!header)
return -EINVAL;
}
if (host0 != local_host && host1 != local_host) {
dev_err(smem->dev,
"Partition %d hosts are invalid\n", i);
return -EINVAL;
}
if (host0 != remote_host && host1 != remote_host) {
dev_err(smem->dev,
"Partition %d hosts are invalid\n", i);
return -EINVAL;
}
if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
dev_err(smem->dev,
"Partition %d has invalid size\n", i);
return -EINVAL;
}
if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
dev_err(smem->dev,
"Partition %d has invalid free pointer\n", i);
return -EINVAL;
}
smem->partitions[remote_host] = header;
smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
@ -887,6 +876,7 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
{
struct device_node *np;
struct resource r;
resource_size_t size;
int ret;
np = of_parse_phandle(dev->of_node, name, 0);
@ -899,12 +889,13 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
of_node_put(np);
if (ret)
return ret;
size = resource_size(&r);
smem->regions[i].aux_base = (u32)r.start;
smem->regions[i].size = resource_size(&r);
smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r));
smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size);
if (!smem->regions[i].virt_base)
return -ENOMEM;
smem->regions[i].aux_base = (u32)r.start;
smem->regions[i].size = size;
return 0;
}
@ -962,6 +953,7 @@ static int qcom_smem_probe(struct platform_device *pdev)
return -EINVAL;
}
BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
if (ret < 0 && ret != -ENOENT)
return ret;

View File

@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu)
cpumask_t mask;
bool use_scm_power_down = false;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
for (i = 0; ; i++) {
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
if (!state_node)

View File

@ -281,7 +281,7 @@ struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rp
struct rpmsg_channel_info chinfo;
struct wcnss_ctrl *_wcnss = wcnss;
strncpy(chinfo.name, name, sizeof(chinfo.name));
strscpy(chinfo.name, name, sizeof(chinfo.name));
chinfo.src = RPMSG_ADDR_ANY;
chinfo.dst = RPMSG_ADDR_ANY;

View File

@ -1,14 +1,17 @@
# SPDX-License-Identifier: GPL-2.0
config SOC_RENESAS
bool "Renesas SoC driver support" if COMPILE_TEST && !ARCH_RENESAS
default y if ARCH_RENESAS
select SOC_BUS
select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \
ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77965 || \
ARCH_R8A77970 || ARCH_R8A77980 || ARCH_R8A77990 || \
ARCH_R8A77995
select SYSC_R8A7743 if ARCH_R8A7743
ARCH_R8A774A1 || ARCH_R8A774C0 || ARCH_R8A7795 || \
ARCH_R8A7796 || ARCH_R8A77965 || ARCH_R8A77970 || \
ARCH_R8A77980 || ARCH_R8A77990 || ARCH_R8A77995
select SYSC_R8A7743 if ARCH_R8A7743 || ARCH_R8A7744
select SYSC_R8A7745 if ARCH_R8A7745
select SYSC_R8A77470 if ARCH_R8A77470
select SYSC_R8A774A1 if ARCH_R8A774A1
select SYSC_R8A774C0 if ARCH_R8A774C0
select SYSC_R8A7779 if ARCH_R8A7779
select SYSC_R8A7790 if ARCH_R8A7790
select SYSC_R8A7791 if ARCH_R8A7791 || ARCH_R8A7793
@ -37,6 +40,14 @@ config SYSC_R8A77470
bool "RZ/G1C System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774A1
bool "RZ/G2M System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A774C0
bool "RZ/G2E System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7779
bool "R-Car H1 System Controller support" if COMPILE_TEST
select SYSC_RCAR

View File

@ -6,6 +6,8 @@ obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o
obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o
obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o
obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o
obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o
obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o
obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o
obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o
obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G1M System Controller
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation; of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G1E System Controller
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation; of the License.
*/
#include <linux/bug.h>

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G2M System Controller
* Copyright (C) 2018 Renesas Electronics Corp.
*
* Based on Renesas R-Car M3-W System Controller
* Copyright (C) 2016 Glider bvba
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a774a1-sysc.h>
#include "rcar-sysc.h"
static const struct rcar_sysc_area r8a774a1_areas[] __initconst = {
{ "always-on", 0, 0, R8A774A1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca57-scu", 0x1c0, 0, R8A774A1_PD_CA57_SCU, R8A774A1_PD_ALWAYS_ON,
PD_SCU },
{ "ca57-cpu0", 0x80, 0, R8A774A1_PD_CA57_CPU0, R8A774A1_PD_CA57_SCU,
PD_CPU_NOCR },
{ "ca57-cpu1", 0x80, 1, R8A774A1_PD_CA57_CPU1, R8A774A1_PD_CA57_SCU,
PD_CPU_NOCR },
{ "ca53-scu", 0x140, 0, R8A774A1_PD_CA53_SCU, R8A774A1_PD_ALWAYS_ON,
PD_SCU },
{ "ca53-cpu0", 0x200, 0, R8A774A1_PD_CA53_CPU0, R8A774A1_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu1", 0x200, 1, R8A774A1_PD_CA53_CPU1, R8A774A1_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu2", 0x200, 2, R8A774A1_PD_CA53_CPU2, R8A774A1_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu3", 0x200, 3, R8A774A1_PD_CA53_CPU3, R8A774A1_PD_CA53_SCU,
PD_CPU_NOCR },
{ "a3vc", 0x380, 0, R8A774A1_PD_A3VC, R8A774A1_PD_ALWAYS_ON },
{ "a2vc0", 0x3c0, 0, R8A774A1_PD_A2VC0, R8A774A1_PD_A3VC },
{ "a2vc1", 0x3c0, 1, R8A774A1_PD_A2VC1, R8A774A1_PD_A3VC },
{ "3dg-a", 0x100, 0, R8A774A1_PD_3DG_A, R8A774A1_PD_ALWAYS_ON },
{ "3dg-b", 0x100, 1, R8A774A1_PD_3DG_B, R8A774A1_PD_3DG_A },
};
const struct rcar_sysc_info r8a774a1_sysc_info __initconst = {
.areas = r8a774a1_areas,
.num_areas = ARRAY_SIZE(r8a774a1_areas),
};

View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G2E System Controller
* Copyright (C) 2018 Renesas Electronics Corp.
*
* Based on Renesas R-Car E3 System Controller
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/sys_soc.h>
#include <dt-bindings/power/r8a774c0-sysc.h>
#include "rcar-sysc.h"
static struct rcar_sysc_area r8a774c0_areas[] __initdata = {
{ "always-on", 0, 0, R8A774C0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca53-scu", 0x140, 0, R8A774C0_PD_CA53_SCU, R8A774C0_PD_ALWAYS_ON,
PD_SCU },
{ "ca53-cpu0", 0x200, 0, R8A774C0_PD_CA53_CPU0, R8A774C0_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu1", 0x200, 1, R8A774C0_PD_CA53_CPU1, R8A774C0_PD_CA53_SCU,
PD_CPU_NOCR },
{ "a3vc", 0x380, 0, R8A774C0_PD_A3VC, R8A774C0_PD_ALWAYS_ON },
{ "a2vc1", 0x3c0, 1, R8A774C0_PD_A2VC1, R8A774C0_PD_A3VC },
{ "3dg-a", 0x100, 0, R8A774C0_PD_3DG_A, R8A774C0_PD_ALWAYS_ON },
{ "3dg-b", 0x100, 1, R8A774C0_PD_3DG_B, R8A774C0_PD_3DG_A },
};
static void __init rcar_sysc_fix_parent(struct rcar_sysc_area *areas,
unsigned int num_areas, u8 id,
int new_parent)
{
unsigned int i;
for (i = 0; i < num_areas; i++)
if (areas[i].isr_bit == id) {
areas[i].parent = new_parent;
return;
}
}
/* Fixups for RZ/G2E ES1.0 revision */
static const struct soc_device_attribute r8a774c0[] __initconst = {
{ .soc_id = "r8a774c0", .revision = "ES1.0" },
{ /* sentinel */ }
};
static int __init r8a774c0_sysc_init(void)
{
if (soc_device_match(r8a774c0)) {
rcar_sysc_fix_parent(r8a774c0_areas,
ARRAY_SIZE(r8a774c0_areas),
R8A774C0_PD_3DG_A, R8A774C0_PD_3DG_B);
rcar_sysc_fix_parent(r8a774c0_areas,
ARRAY_SIZE(r8a774c0_areas),
R8A774C0_PD_3DG_B, R8A774C0_PD_ALWAYS_ON);
}
return 0;
}
const struct rcar_sysc_info r8a774c0_sysc_info __initconst = {
.init = r8a774c0_sysc_init,
.areas = r8a774c0_areas,
.num_areas = ARRAY_SIZE(r8a774c0_areas),
};

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car H1 System Controller
*
* Copyright (C) 2016 Glider bvba
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car H2 System Controller
*
* Copyright (C) 2016 Glider bvba
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car M2-W/N System Controller
*
* Copyright (C) 2016 Glider bvba
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car V2H (R8A7792) System Controller
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car E2 System Controller
*
* Copyright (C) 2016 Glider bvba
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car H3 System Controller
*
* Copyright (C) 2016-2017 Glider bvba
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car M3-W System Controller
*
* Copyright (C) 2016 Glider bvba
*
* 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; version 2 of the License.
*/
#include <linux/bug.h>

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car V3M System Controller
*
* Copyright (C) 2017 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bug.h>

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