whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit b22b6beae6116e3a9c46ced312c626f6737a3fa6
parent 53b7a3b7ec00f207c18e71f58ef2bca48635c622
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Mon, 29 Oct 2018 15:16:01 -0700

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
  ...

Diffstat:
ADocumentation/devicetree/bindings/arm/freescale/fsl,scu.txt | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/arm/keystone/ti,sci.txt | 4++++
MDocumentation/devicetree/bindings/arm/msm/qcom,llcc.txt | 19+++++++++++++++++--
MDocumentation/devicetree/bindings/firmware/qcom,scm.txt | 33+++++++++++++++++++++------------
ADocumentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/power/renesas,apmu.txt | 2++
MDocumentation/devicetree/bindings/power/renesas,rcar-sysc.txt | 3+++
ADocumentation/devicetree/bindings/reset/qcom,pdc-global.txt | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/reset/renesas,rst.txt | 3+++
ADocumentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt | 29+++++++++++++++++++++++++++++
MDocumentation/devicetree/bindings/soc/mediatek/pwrap.txt | 2++
MDocumentation/devicetree/bindings/sram/sunxi-sram.txt | 4++++
MDocumentation/devicetree/bindings/timer/renesas,tmu.txt | 2++
ADocumentation/xilinx/eemi.txt | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MMAINTAINERS | 10++++++++++
March/arm/mach-shmobile/Kconfig | 6++++++
March/arm/mach-shmobile/Makefile | 1+
Aarch/arm/mach-shmobile/setup-r7s9210.c | 27+++++++++++++++++++++++++++
March/arm64/Kconfig.platforms | 1+
Mdrivers/bus/imx-weim.c | 3---
Mdrivers/bus/ti-sysc.c | 171++++++++++++++++++++++---------------------------------------------------------
Mdrivers/clk/Kconfig | 1+
Mdrivers/clk/Makefile | 1+
Adrivers/clk/zynqmp/Kconfig | 10++++++++++
Adrivers/clk/zynqmp/Makefile | 4++++
Adrivers/clk/zynqmp/clk-gate-zynqmp.c | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/clk/zynqmp/clk-mux-zynqmp.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/clk/zynqmp/clk-zynqmp.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/clk/zynqmp/clkc.c | 716+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/clk/zynqmp/divider.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/clk/zynqmp/pll.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/edac/Kconfig | 14++++++++++++++
Mdrivers/edac/Makefile | 1+
Adrivers/edac/qcom_edac.c | 414+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/firmware/Kconfig | 2++
Mdrivers/firmware/Makefile | 2++
Mdrivers/firmware/arm_scmi/base.c | 2+-
Mdrivers/firmware/arm_scmi/clock.c | 2+-
Mdrivers/firmware/arm_scmi/perf.c | 30+++++++++++++++++++++++++++++-
Mdrivers/firmware/arm_scmi/power.c | 2+-
Mdrivers/firmware/arm_scmi/sensors.c | 2+-
Adrivers/firmware/imx/Kconfig | 11+++++++++++
Adrivers/firmware/imx/Makefile | 2++
Adrivers/firmware/imx/imx-scu.c | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/firmware/imx/misc.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/firmware/meson/meson_sm.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/firmware/qcom_scm.c | 74++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mdrivers/firmware/tegra/bpmp.c | 19+++++++++++++++++++
Mdrivers/firmware/ti_sci.c | 24++++++++++++++++++++----
Adrivers/firmware/xilinx/Kconfig | 23+++++++++++++++++++++++
Adrivers/firmware/xilinx/Makefile | 5+++++
Adrivers/firmware/xilinx/zynqmp-debug.c | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adrivers/firmware/xilinx/zynqmp-debug.h | 24++++++++++++++++++++++++
Adrivers/firmware/xilinx/zynqmp.c | 565+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/memory/atmel-ebi.c | 3+--
Mdrivers/reset/Kconfig | 9+++++++++
Mdrivers/reset/Makefile | 1+
Mdrivers/reset/core.c | 15++++++++-------
Adrivers/reset/reset-qcom-pdc.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/Makefile | 2+-
Mdrivers/soc/actions/Kconfig | 2+-
Mdrivers/soc/actions/Makefile | 2++
Mdrivers/soc/actions/owl-sps-helper.c | 6+-----
Mdrivers/soc/actions/owl-sps.c | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mdrivers/soc/amlogic/Kconfig | 7+++++++
Mdrivers/soc/amlogic/Makefile | 1+
Adrivers/soc/amlogic/meson-canvas.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/fsl/dpio/dpio-driver.c | 3---
Mdrivers/soc/fsl/qbman/Kconfig | 2+-
Mdrivers/soc/fsl/qbman/bman.c | 6++----
Mdrivers/soc/fsl/qbman/bman_portal.c | 14++++++++++++--
Mdrivers/soc/fsl/qbman/dpaa_sys.h | 20++++++++++++++++++++
Mdrivers/soc/fsl/qbman/qman.c | 53+++++++++++++++++++++++++++++++++++++++++++++++------
Mdrivers/soc/fsl/qbman/qman_portal.c | 6++++--
Mdrivers/soc/fsl/qe/qe.c | 6+-----
Mdrivers/soc/imx/gpc.c | 26++++++++++++--------------
Mdrivers/soc/imx/gpcv2.c | 100+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mdrivers/soc/mediatek/mtk-pmic-wrap.c | 519++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mdrivers/soc/qcom/Kconfig | 21++++++++++++---------
Mdrivers/soc/qcom/apr.c | 4++--
Mdrivers/soc/qcom/llcc-slice.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mdrivers/soc/qcom/rmtfs_mem.c | 5+++++
Mdrivers/soc/qcom/rpmh-rsc.c | 2++
Mdrivers/soc/qcom/smem.c | 174++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mdrivers/soc/qcom/spm.c | 3+++
Mdrivers/soc/qcom/wcnss_ctrl.c | 2+-
Mdrivers/soc/renesas/Kconfig | 19+++++++++++++++----
Mdrivers/soc/renesas/Makefile | 2++
Mdrivers/soc/renesas/r8a7743-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7745-sysc.c | 5+----
Adrivers/soc/renesas/r8a774a1-sysc.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Adrivers/soc/renesas/r8a774c0-sysc.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/renesas/r8a7779-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7790-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7791-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7792-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7794-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7795-sysc.c | 5+----
Mdrivers/soc/renesas/r8a7796-sysc.c | 5+----
Mdrivers/soc/renesas/r8a77970-sysc.c | 5+----
Mdrivers/soc/renesas/r8a77995-sysc.c | 5+----
Mdrivers/soc/renesas/rcar-rst.c | 11++++++-----
Mdrivers/soc/renesas/rcar-sysc.c | 13+++++++++----
Mdrivers/soc/renesas/rcar-sysc.h | 9++++-----
Mdrivers/soc/renesas/renesas-soc.c | 100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mdrivers/soc/tegra/pmc.c | 513+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mdrivers/soc/ti/knav_dma.c | 4++--
Mdrivers/soc/ti/knav_qmss.h | 6+++---
Mdrivers/tee/optee/core.c | 2+-
Mdrivers/tee/tee_core.c | 113++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Ainclude/dt-bindings/clock/xlnx,zynqmp-clk.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/dt-bindings/power/r8a7744-sysc.h | 24++++++++++++++++++++++++
Ainclude/dt-bindings/power/r8a774a1-sysc.h | 31+++++++++++++++++++++++++++++++
Ainclude/dt-bindings/power/r8a774c0-sysc.h | 25+++++++++++++++++++++++++
Ainclude/dt-bindings/reset/qcom,sdm845-pdc.h | 20++++++++++++++++++++
Ainclude/linux/firmware/imx/ipc.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/linux/firmware/imx/sci.h | 17+++++++++++++++++
Ainclude/linux/firmware/imx/svc/misc.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/linux/firmware/imx/types.h | 617+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/linux/firmware/meson/meson_sm.h | 1+
Ainclude/linux/firmware/xlnx-zynqmp.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/linux/platform_data/ti-sysc.h | 1-
Minclude/linux/reset.h | 2+-
Minclude/linux/scmi_protocol.h | 4++++
Ainclude/linux/soc/amlogic/meson-canvas.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/linux/soc/qcom/llcc-qcom.h | 30++++++++++++++++++++++++++++--
Minclude/linux/tee_drv.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/soc/fsl/qman.h | 28++++++++++++++++++++++++++++
Minclude/soc/tegra/pmc.h | 20+-------------------
129 files changed, 7174 insertions(+), 785 deletions(-)

diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt b/Documentation/devicetree/bindings/arm/keystone/ti,sci.txt @@ -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>; diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt @@ -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>; }; diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -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"; }; }; diff --git a/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt @@ -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"; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/power/renesas,apmu.txt b/Documentation/devicetree/bindings/power/renesas,apmu.txt @@ -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) diff --git a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt @@ -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) diff --git a/Documentation/devicetree/bindings/reset/qcom,pdc-global.txt b/Documentation/devicetree/bindings/reset/qcom,pdc-global.txt @@ -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"; + + ... +}; diff --git a/Documentation/devicetree/bindings/reset/renesas,rst.txt b/Documentation/devicetree/bindings/reset/renesas,rst.txt @@ -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) diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt b/Documentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt b/Documentation/devicetree/bindings/soc/mediatek/pwrap.txt @@ -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 diff --git a/Documentation/devicetree/bindings/sram/sunxi-sram.txt b/Documentation/devicetree/bindings/sram/sunxi-sram.txt @@ -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 --------------------------- diff --git a/Documentation/devicetree/bindings/timer/renesas,tmu.txt b/Documentation/devicetree/bindings/timer/renesas,tmu.txt @@ -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 diff --git a/Documentation/xilinx/eemi.txt b/Documentation/xilinx/eemi.txt @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS @@ -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) diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig @@ -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 diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile @@ -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 diff --git a/arch/arm/mach-shmobile/setup-r7s9210.c b/arch/arm/mach-shmobile/setup-r7s9210.c @@ -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 diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms @@ -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 diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c @@ -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", diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c @@ -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; } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig @@ -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 diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile @@ -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/ diff --git a/drivers/clk/zynqmp/Kconfig b/drivers/clk/zynqmp/Kconfig @@ -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. diff --git a/drivers/clk/zynqmp/Makefile b/drivers/clk/zynqmp/Makefile @@ -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 diff --git a/drivers/clk/zynqmp/clk-gate-zynqmp.c b/drivers/clk/zynqmp/clk-gate-zynqmp.c @@ -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; +} diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c @@ -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); diff --git a/drivers/clk/zynqmp/clk-zynqmp.h b/drivers/clk/zynqmp/clk-zynqmp.h @@ -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 diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c @@ -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); diff --git a/drivers/clk/zynqmp/divider.c b/drivers/clk/zynqmp/divider.c @@ -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); diff --git a/drivers/clk/zynqmp/pll.c b/drivers/clk/zynqmp/pll.c @@ -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; +} diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig @@ -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 diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile @@ -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 diff --git a/drivers/edac/qcom_edac.c b/drivers/edac/qcom_edac.c @@ -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"); diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig @@ -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 diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile @@ -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/ diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c @@ -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); diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c @@ -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'; diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c @@ -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) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c @@ -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); diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c @@ -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; diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig @@ -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. diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c @@ -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"); diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c @@ -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); diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c @@ -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: diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c @@ -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-msm8960", - .data = (void *) SCM_HAS_CORE_CLK, - }, - { .compatible = "qcom,scm-msm8996", - .data = NULL, /* no clocks */ + { .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK | + SCM_HAS_IFACE_CLK | + SCM_HAS_BUS_CLK) }, - { .compatible = "qcom,scm-ipq4019", - .data = NULL, /* no clocks */ + { .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", - .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" }, {} }; diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c @@ -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, }; diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c @@ -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); diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig @@ -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 diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile @@ -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 diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c @@ -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); +} diff --git a/drivers/firmware/xilinx/zynqmp-debug.h b/drivers/firmware/xilinx/zynqmp-debug.h @@ -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__ */ diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c @@ -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); diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c @@ -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; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig @@ -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 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile @@ -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 diff --git a/drivers/reset/core.c b/drivers/reset/core.c @@ -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; } diff --git a/drivers/reset/reset-qcom-pdc.c b/drivers/reset/reset-qcom-pdc.c @@ -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"); diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile @@ -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/ diff --git a/drivers/soc/actions/Kconfig b/drivers/soc/actions/Kconfig @@ -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 diff --git a/drivers/soc/actions/Makefile b/drivers/soc/actions/Makefile @@ -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 diff --git a/drivers/soc/actions/owl-sps-helper.c b/drivers/soc/actions/owl-sps-helper.c @@ -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> diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c @@ -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 }, { } }; diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig @@ -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 diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile @@ -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 diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c @@ -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"); diff --git a/drivers/soc/fsl/dpio/dpio-driver.c b/drivers/soc/fsl/dpio/dpio-driver.c @@ -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, diff --git a/drivers/soc/fsl/qbman/Kconfig b/drivers/soc/fsl/qbman/Kconfig @@ -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 diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c @@ -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); diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c @@ -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) diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -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 */ diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c @@ -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; diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c @@ -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; diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c @@ -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? */ diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c @@ -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 = { diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c @@ -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, }, { } }; diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -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,20 +1646,20 @@ 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. */ @@ -1440,9 +1669,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,18 +1743,35 @@ 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, }, { @@ -1525,6 +1784,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, diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig @@ -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 diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c @@ -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); diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c @@ -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); diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c @@ -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; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c @@ -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); diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c @@ -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 (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST) + continue; - if (host0 == SMEM_GLOBAL_HOST && host0 == host1) { + 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); - 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); + dev_err(smem->dev, "duplicate host %hu\n", remote_host); return -EINVAL; } - if (host0 != local_host && host1 != local_host) { - dev_err(smem->dev, - "Partition %d hosts are invalid\n", i); + header = qcom_smem_partition_header(smem, entry, host0, host1); + if (!header) 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; diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c @@ -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) diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c @@ -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; diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig @@ -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 diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile @@ -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 diff --git a/drivers/soc/renesas/r8a7743-sysc.c b/drivers/soc/renesas/r8a7743-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7745-sysc.c b/drivers/soc/renesas/r8a7745-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a774a1-sysc.c b/drivers/soc/renesas/r8a774a1-sysc.c @@ -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), +}; diff --git a/drivers/soc/renesas/r8a774c0-sysc.c b/drivers/soc/renesas/r8a774c0-sysc.c @@ -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), +}; diff --git a/drivers/soc/renesas/r8a7779-sysc.c b/drivers/soc/renesas/r8a7779-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7790-sysc.c b/drivers/soc/renesas/r8a7790-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7791-sysc.c b/drivers/soc/renesas/r8a7791-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7792-sysc.c b/drivers/soc/renesas/r8a7792-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7794-sysc.c b/drivers/soc/renesas/r8a7794-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7795-sysc.c b/drivers/soc/renesas/r8a7795-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c @@ -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> diff --git a/drivers/soc/renesas/r8a77995-sysc.c b/drivers/soc/renesas/r8a77995-sysc.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas R-Car D3 System Controller * * Copyright (C) 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> diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R-Car Gen1 RESET/WDT, R-Car Gen2, Gen3, and RZ/G RST Driver * * Copyright (C) 2016 Glider bvba - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #include <linux/err.h> @@ -41,10 +38,14 @@ static const struct rst_config rcar_rst_gen3 __initconst = { }; static const struct of_device_id rcar_rst_matches[] __initconst = { - /* RZ/G is handled like R-Car Gen2 */ + /* RZ/G1 is handled like R-Car Gen2 */ { .compatible = "renesas,r8a7743-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7744-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7745-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 }, + /* RZ/G2 is handled like R-Car Gen3 */ + { .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, /* R-Car Gen1 */ { .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, { .compatible = "renesas,r8a7779-reset-wdt", .data = &rcar_rst_gen1 }, diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R-Car SYSC Power management support * * Copyright (C) 2014 Magnus Damm * Copyright (C) 2015-2017 Glider bvba - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #include <linux/clk/renesas.h> @@ -268,6 +265,8 @@ finalize: static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A7743 { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, + /* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */ + { .compatible = "renesas,r8a7744-sysc", .data = &r8a7743_sysc_info }, #endif #ifdef CONFIG_SYSC_R8A7745 { .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info }, @@ -275,6 +274,12 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A77470 { .compatible = "renesas,r8a77470-sysc", .data = &r8a77470_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A774A1 + { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A774C0 + { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A7779 { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h @@ -1,11 +1,8 @@ -/* +/* SPDX-License-Identifier: GPL-2.0 + * * Renesas R-Car 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. */ #ifndef __SOC_RENESAS_RCAR_SYSC_H__ #define __SOC_RENESAS_RCAR_SYSC_H__ @@ -52,6 +49,8 @@ struct rcar_sysc_info { extern const struct rcar_sysc_info r8a7743_sysc_info; extern const struct rcar_sysc_info r8a7745_sysc_info; extern const struct rcar_sysc_info r8a77470_sysc_info; +extern const struct rcar_sysc_info r8a774a1_sysc_info; +extern const struct rcar_sysc_info r8a774c0_sysc_info; extern const struct rcar_sysc_info r8a7779_sysc_info; extern const struct rcar_sysc_info r8a7790_sysc_info; extern const struct rcar_sysc_info r8a7791_sysc_info; diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas SoC Identification * * Copyright (C) 2014-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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/io.h> @@ -46,15 +38,24 @@ static const struct renesas_family fam_rmobile __initconst __maybe_unused = { .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ }; -static const struct renesas_family fam_rza __initconst __maybe_unused = { - .name = "RZ/A", +static const struct renesas_family fam_rza1 __initconst __maybe_unused = { + .name = "RZ/A1", +}; + +static const struct renesas_family fam_rza2 __initconst __maybe_unused = { + .name = "RZ/A2", }; -static const struct renesas_family fam_rzg __initconst __maybe_unused = { - .name = "RZ/G", +static const struct renesas_family fam_rzg1 __initconst __maybe_unused = { + .name = "RZ/G1", .reg = 0xff000044, /* PRR (Product Register) */ }; +static const struct renesas_family fam_rzg2 __initconst __maybe_unused = { + .name = "RZ/G2", + .reg = 0xfff00044, /* PRR (Product Register) */ +}; + static const struct renesas_family fam_shmobile __initconst __maybe_unused = { .name = "SH-Mobile", .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ @@ -67,7 +68,12 @@ struct renesas_soc { }; static const struct renesas_soc soc_rz_a1h __initconst __maybe_unused = { - .family = &fam_rza, + .family = &fam_rza1, +}; + +static const struct renesas_soc soc_rz_a2m __initconst __maybe_unused = { + .family = &fam_rza2, + .id = 0x3b, }; static const struct renesas_soc soc_rmobile_ape6 __initconst __maybe_unused = { @@ -81,30 +87,40 @@ static const struct renesas_soc soc_rmobile_a1 __initconst __maybe_unused = { }; static const struct renesas_soc soc_rz_g1h __initconst __maybe_unused = { - .family = &fam_rzg, + .family = &fam_rzg1, .id = 0x45, }; static const struct renesas_soc soc_rz_g1m __initconst __maybe_unused = { - .family = &fam_rzg, + .family = &fam_rzg1, .id = 0x47, }; static const struct renesas_soc soc_rz_g1n __initconst __maybe_unused = { - .family = &fam_rzg, + .family = &fam_rzg1, .id = 0x4b, }; static const struct renesas_soc soc_rz_g1e __initconst __maybe_unused = { - .family = &fam_rzg, + .family = &fam_rzg1, .id = 0x4c, }; static const struct renesas_soc soc_rz_g1c __initconst __maybe_unused = { - .family = &fam_rzg, + .family = &fam_rzg1, .id = 0x53, }; +static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x52, +}; + +static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x57, +}; + static const struct renesas_soc soc_rcar_m1a __initconst __maybe_unused = { .family = &fam_rcar_gen1, }; @@ -184,6 +200,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R7S72100 { .compatible = "renesas,r7s72100", .data = &soc_rz_a1h }, #endif +#ifdef CONFIG_ARCH_R7S9210 + { .compatible = "renesas,r7s9210", .data = &soc_rz_a2m }, +#endif #ifdef CONFIG_ARCH_R8A73A4 { .compatible = "renesas,r8a73a4", .data = &soc_rmobile_ape6 }, #endif @@ -205,6 +224,12 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A77470 { .compatible = "renesas,r8a77470", .data = &soc_rz_g1c }, #endif +#ifdef CONFIG_ARCH_R8A774A1 + { .compatible = "renesas,r8a774a1", .data = &soc_rz_g2m }, +#endif +#ifdef CONFIG_ARCH_R8A774C0 + { .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e }, +#endif #ifdef CONFIG_ARCH_R8A7778 { .compatible = "renesas,r8a7778", .data = &soc_rcar_m1a }, #endif @@ -262,7 +287,7 @@ static int __init renesas_soc_init(void) void __iomem *chipid = NULL; struct soc_device *soc_dev; struct device_node *np; - unsigned int product; + unsigned int product, eshi = 0, eslo; match = of_match_node(renesas_socs, of_root); if (!match) @@ -271,6 +296,31 @@ static int __init renesas_soc_init(void) soc = match->data; family = soc->family; + np = of_find_compatible_node(NULL, NULL, "renesas,bsid"); + if (np) { + chipid = of_iomap(np, 0); + of_node_put(np); + + if (chipid) { + product = readl(chipid); + iounmap(chipid); + + if (soc->id && ((product >> 16) & 0xff) != soc->id) { + pr_warn("SoC mismatch (product = 0x%x)\n", + product); + return -ENODEV; + } + } + + /* + * TODO: Upper 4 bits of BSID are for chip version, but the + * format is not known at this time so we don't know how to + * specify eshi and eslo + */ + + goto done; + } + /* Try PRR first, then hardcoded fallback */ np = of_find_compatible_node(NULL, NULL, "renesas,prr"); if (np) { @@ -289,8 +339,11 @@ static int __init renesas_soc_init(void) pr_warn("SoC mismatch (product = 0x%x)\n", product); return -ENODEV; } + eshi = ((product >> 4) & 0x0f) + 1; + eslo = product & 0xf; } +done: soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return -ENOMEM; @@ -302,10 +355,9 @@ static int __init renesas_soc_init(void) soc_dev_attr->family = kstrdup_const(family->name, GFP_KERNEL); soc_dev_attr->soc_id = kstrdup_const(strchr(match->compatible, ',') + 1, GFP_KERNEL); - if (chipid) - soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u", - ((product >> 4) & 0x0f) + 1, - product & 0xf); + if (eshi) + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u", eshi, + eslo); pr_info("Detected Renesas %s %s %s\n", soc_dev_attr->family, soc_dev_attr->soc_id, soc_dev_attr->revision ?: ""); diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c @@ -33,6 +33,9 @@ #include <linux/of_address.h> #include <linux/of_clk.h> #include <linux/of_platform.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/reboot.h> @@ -45,6 +48,8 @@ #include <soc/tegra/fuse.h> #include <soc/tegra/pmc.h> +#include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h> + #define PMC_CNTRL 0x0 #define PMC_CNTRL_INTR_POLARITY BIT(17) /* inverts INTR polarity */ #define PMC_CNTRL_CPU_PWRREQ_OE BIT(16) /* CPU pwr req enable */ @@ -65,6 +70,8 @@ #define PWRGATE_STATUS 0x38 +#define PMC_IMPL_E_33V_PWR 0x40 + #define PMC_PWR_DET 0x48 #define PMC_SCRATCH0_MODE_RECOVERY BIT(31) @@ -135,6 +142,7 @@ struct tegra_io_pad_soc { enum tegra_io_pad id; unsigned int dpd; unsigned int voltage; + const char *name; }; struct tegra_pmc_regs { @@ -154,10 +162,14 @@ struct tegra_pmc_soc { bool has_tsense_reset; bool has_gpu_clamps; bool needs_mbist_war; + bool has_impl_33v_pwr; const struct tegra_io_pad_soc *io_pads; unsigned int num_io_pads; + const struct pinctrl_pin_desc *pin_descs; + unsigned int num_pin_descs; + const struct tegra_pmc_regs *regs; void (*init)(struct tegra_pmc *pmc); void (*setup_irq_polarity)(struct tegra_pmc *pmc, @@ -216,6 +228,8 @@ struct tegra_pmc { DECLARE_BITMAP(powergates_available, TEGRA_POWERGATE_MAX); struct mutex powergates_lock; + + struct pinctrl_dev *pctl_dev; }; static struct tegra_pmc *pmc = &(struct tegra_pmc) { @@ -919,11 +933,12 @@ tegra_io_pad_find(struct tegra_pmc *pmc, enum tegra_io_pad id) return NULL; } -static int tegra_io_pad_prepare(enum tegra_io_pad id, unsigned long *request, - unsigned long *status, u32 *mask) +static int tegra_io_pad_get_dpd_register_bit(enum tegra_io_pad id, + unsigned long *request, + unsigned long *status, + u32 *mask) { const struct tegra_io_pad_soc *pad; - unsigned long rate, value; pad = tegra_io_pad_find(pmc, id); if (!pad) { @@ -944,6 +959,19 @@ static int tegra_io_pad_prepare(enum tegra_io_pad id, unsigned long *request, *request = pmc->soc->regs->dpd2_req; } + return 0; +} + +static int tegra_io_pad_prepare(enum tegra_io_pad id, unsigned long *request, + unsigned long *status, u32 *mask) +{ + unsigned long rate, value; + int err; + + err = tegra_io_pad_get_dpd_register_bit(id, request, status, mask); + if (err) + return err; + if (pmc->clk) { rate = clk_get_rate(pmc->clk); if (!rate) { @@ -1058,8 +1086,22 @@ unlock: } EXPORT_SYMBOL(tegra_io_pad_power_disable); -int tegra_io_pad_set_voltage(enum tegra_io_pad id, - enum tegra_io_pad_voltage voltage) +static int tegra_io_pad_is_powered(enum tegra_io_pad id) +{ + unsigned long request, status; + u32 mask, value; + int err; + + err = tegra_io_pad_get_dpd_register_bit(id, &request, &status, &mask); + if (err) + return err; + + value = tegra_pmc_readl(status); + + return !(value & mask); +} + +static int tegra_io_pad_set_voltage(enum tegra_io_pad id, int voltage) { const struct tegra_io_pad_soc *pad; u32 value; @@ -1073,20 +1115,31 @@ int tegra_io_pad_set_voltage(enum tegra_io_pad id, mutex_lock(&pmc->powergates_lock); - /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */ - value = tegra_pmc_readl(PMC_PWR_DET); - value |= BIT(pad->voltage); - tegra_pmc_writel(value, PMC_PWR_DET); + if (pmc->soc->has_impl_33v_pwr) { + value = tegra_pmc_readl(PMC_IMPL_E_33V_PWR); - /* update I/O voltage */ - value = tegra_pmc_readl(PMC_PWR_DET_VALUE); + if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) + value &= ~BIT(pad->voltage); + else + value |= BIT(pad->voltage); - if (voltage == TEGRA_IO_PAD_1800000UV) - value &= ~BIT(pad->voltage); - else + tegra_pmc_writel(value, PMC_IMPL_E_33V_PWR); + } else { + /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */ + value = tegra_pmc_readl(PMC_PWR_DET); value |= BIT(pad->voltage); + tegra_pmc_writel(value, PMC_PWR_DET); - tegra_pmc_writel(value, PMC_PWR_DET_VALUE); + /* update I/O voltage */ + value = tegra_pmc_readl(PMC_PWR_DET_VALUE); + + if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) + value &= ~BIT(pad->voltage); + else + value |= BIT(pad->voltage); + + tegra_pmc_writel(value, PMC_PWR_DET_VALUE); + } mutex_unlock(&pmc->powergates_lock); @@ -1094,9 +1147,8 @@ int tegra_io_pad_set_voltage(enum tegra_io_pad id, return 0; } -EXPORT_SYMBOL(tegra_io_pad_set_voltage); -int tegra_io_pad_get_voltage(enum tegra_io_pad id) +static int tegra_io_pad_get_voltage(enum tegra_io_pad id) { const struct tegra_io_pad_soc *pad; u32 value; @@ -1108,14 +1160,16 @@ int tegra_io_pad_get_voltage(enum tegra_io_pad id) if (pad->voltage == UINT_MAX) return -ENOTSUPP; - value = tegra_pmc_readl(PMC_PWR_DET_VALUE); + if (pmc->soc->has_impl_33v_pwr) + value = tegra_pmc_readl(PMC_IMPL_E_33V_PWR); + else + value = tegra_pmc_readl(PMC_PWR_DET_VALUE); if ((value & BIT(pad->voltage)) == 0) - return TEGRA_IO_PAD_1800000UV; + return TEGRA_IO_PAD_VOLTAGE_1V8; - return TEGRA_IO_PAD_3300000UV; + return TEGRA_IO_PAD_VOLTAGE_3V3; } -EXPORT_SYMBOL(tegra_io_pad_get_voltage); /** * tegra_io_rail_power_on() - enable power to I/O rail @@ -1288,7 +1342,7 @@ static void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) if (!pmc->soc->has_tsense_reset) return; - np = of_find_node_by_name(pmc->dev->of_node, "i2c-thermtrip"); + np = of_get_child_by_name(pmc->dev->of_node, "i2c-thermtrip"); if (!np) { dev_warn(dev, "i2c-thermtrip node not found, %s.\n", disabled); return; @@ -1353,6 +1407,142 @@ out: of_node_put(np); } +static int tegra_io_pad_pinctrl_get_groups_count(struct pinctrl_dev *pctl_dev) +{ + return pmc->soc->num_io_pads; +} + +static const char *tegra_io_pad_pinctrl_get_group_name( + struct pinctrl_dev *pctl, unsigned int group) +{ + return pmc->soc->io_pads[group].name; +} + +static int tegra_io_pad_pinctrl_get_group_pins(struct pinctrl_dev *pctl_dev, + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &pmc->soc->io_pads[group].id; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops tegra_io_pad_pinctrl_ops = { + .get_groups_count = tegra_io_pad_pinctrl_get_groups_count, + .get_group_name = tegra_io_pad_pinctrl_get_group_name, + .get_group_pins = tegra_io_pad_pinctrl_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static int tegra_io_pad_pinconf_get(struct pinctrl_dev *pctl_dev, + unsigned int pin, unsigned long *config) +{ + const struct tegra_io_pad_soc *pad = tegra_io_pad_find(pmc, pin); + enum pin_config_param param = pinconf_to_config_param(*config); + int ret; + u32 arg; + + if (!pad) + return -EINVAL; + + switch (param) { + case PIN_CONFIG_POWER_SOURCE: + ret = tegra_io_pad_get_voltage(pad->id); + if (ret < 0) + return ret; + arg = ret; + break; + case PIN_CONFIG_LOW_POWER_MODE: + ret = tegra_io_pad_is_powered(pad->id); + if (ret < 0) + return ret; + arg = !ret; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + const struct tegra_io_pad_soc *pad = tegra_io_pad_find(pmc, pin); + enum pin_config_param param; + unsigned int i; + int err; + u32 arg; + + if (!pad) + return -EINVAL; + + for (i = 0; i < num_configs; ++i) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_LOW_POWER_MODE: + if (arg) + err = tegra_io_pad_power_disable(pad->id); + else + err = tegra_io_pad_power_enable(pad->id); + if (err) + return err; + break; + case PIN_CONFIG_POWER_SOURCE: + if (arg != TEGRA_IO_PAD_VOLTAGE_1V8 && + arg != TEGRA_IO_PAD_VOLTAGE_3V3) + return -EINVAL; + err = tegra_io_pad_set_voltage(pad->id, arg); + if (err) + return err; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static const struct pinconf_ops tegra_io_pad_pinconf_ops = { + .pin_config_get = tegra_io_pad_pinconf_get, + .pin_config_set = tegra_io_pad_pinconf_set, + .is_generic = true, +}; + +static struct pinctrl_desc tegra_pmc_pctl_desc = { + .pctlops = &tegra_io_pad_pinctrl_ops, + .confops = &tegra_io_pad_pinconf_ops, +}; + +static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) +{ + int err = 0; + + if (!pmc->soc->num_pin_descs) + return 0; + + tegra_pmc_pctl_desc.name = dev_name(pmc->dev); + tegra_pmc_pctl_desc.pins = pmc->soc->pin_descs; + tegra_pmc_pctl_desc.npins = pmc->soc->num_pin_descs; + + pmc->pctl_dev = devm_pinctrl_register(pmc->dev, &tegra_pmc_pctl_desc, + pmc); + if (IS_ERR(pmc->pctl_dev)) { + err = PTR_ERR(pmc->pctl_dev); + dev_err(pmc->dev, "unable to register pinctrl, %d\n", err); + } + + return err; +} + static int tegra_pmc_probe(struct platform_device *pdev) { void __iomem *base; @@ -1430,18 +1620,27 @@ static int tegra_pmc_probe(struct platform_device *pdev) err = register_restart_handler(&tegra_pmc_restart_handler); if (err) { - debugfs_remove(pmc->debugfs); dev_err(&pdev->dev, "unable to register restart handler, %d\n", err); - return err; + goto cleanup_debugfs; } + err = tegra_pmc_pinctrl_init(pmc); + if (err) + goto cleanup_restart_handler; + mutex_lock(&pmc->powergates_lock); iounmap(pmc->base); pmc->base = base; mutex_unlock(&pmc->powergates_lock); return 0; + +cleanup_restart_handler: + unregister_restart_handler(&tegra_pmc_restart_handler); +cleanup_debugfs: + debugfs_remove(pmc->debugfs); + return err; } #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM) @@ -1531,6 +1730,8 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .has_gpu_clamps = false, .num_io_pads = 0, .io_pads = NULL, + .num_pin_descs = 0, + .pin_descs = NULL, .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, @@ -1567,8 +1768,11 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .cpu_powergates = tegra30_cpu_powergates, .has_tsense_reset = true, .has_gpu_clamps = false, + .has_impl_33v_pwr = false, .num_io_pads = 0, .io_pads = NULL, + .num_pin_descs = 0, + .pin_descs = NULL, .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, @@ -1609,8 +1813,11 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .cpu_powergates = tegra114_cpu_powergates, .has_tsense_reset = true, .has_gpu_clamps = false, + .has_impl_33v_pwr = false, .num_io_pads = 0, .io_pads = NULL, + .num_pin_descs = 0, + .pin_descs = NULL, .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, @@ -1649,37 +1856,59 @@ static const u8 tegra124_cpu_powergates[] = { TEGRA_POWERGATE_CPU3, }; +#define TEGRA_IO_PAD(_id, _dpd, _voltage, _name) \ + ((struct tegra_io_pad_soc) { \ + .id = (_id), \ + .dpd = (_dpd), \ + .voltage = (_voltage), \ + .name = (_name), \ + }) + +#define TEGRA_IO_PIN_DESC(_id, _dpd, _voltage, _name) \ + ((struct pinctrl_pin_desc) { \ + .number = (_id), \ + .name = (_name) \ + }) + +#define TEGRA124_IO_PAD_TABLE(_pad) \ + /* .id .dpd .voltage .name */ \ + _pad(TEGRA_IO_PAD_AUDIO, 17, UINT_MAX, "audio"), \ + _pad(TEGRA_IO_PAD_BB, 15, UINT_MAX, "bb"), \ + _pad(TEGRA_IO_PAD_CAM, 36, UINT_MAX, "cam"), \ + _pad(TEGRA_IO_PAD_COMP, 22, UINT_MAX, "comp"), \ + _pad(TEGRA_IO_PAD_CSIA, 0, UINT_MAX, "csia"), \ + _pad(TEGRA_IO_PAD_CSIB, 1, UINT_MAX, "csb"), \ + _pad(TEGRA_IO_PAD_CSIE, 44, UINT_MAX, "cse"), \ + _pad(TEGRA_IO_PAD_DSI, 2, UINT_MAX, "dsi"), \ + _pad(TEGRA_IO_PAD_DSIB, 39, UINT_MAX, "dsib"), \ + _pad(TEGRA_IO_PAD_DSIC, 40, UINT_MAX, "dsic"), \ + _pad(TEGRA_IO_PAD_DSID, 41, UINT_MAX, "dsid"), \ + _pad(TEGRA_IO_PAD_HDMI, 28, UINT_MAX, "hdmi"), \ + _pad(TEGRA_IO_PAD_HSIC, 19, UINT_MAX, "hsic"), \ + _pad(TEGRA_IO_PAD_HV, 38, UINT_MAX, "hv"), \ + _pad(TEGRA_IO_PAD_LVDS, 57, UINT_MAX, "lvds"), \ + _pad(TEGRA_IO_PAD_MIPI_BIAS, 3, UINT_MAX, "mipi-bias"), \ + _pad(TEGRA_IO_PAD_NAND, 13, UINT_MAX, "nand"), \ + _pad(TEGRA_IO_PAD_PEX_BIAS, 4, UINT_MAX, "pex-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK1, 5, UINT_MAX, "pex-clk1"), \ + _pad(TEGRA_IO_PAD_PEX_CLK2, 6, UINT_MAX, "pex-clk2"), \ + _pad(TEGRA_IO_PAD_PEX_CNTRL, 32, UINT_MAX, "pex-cntrl"), \ + _pad(TEGRA_IO_PAD_SDMMC1, 33, UINT_MAX, "sdmmc1"), \ + _pad(TEGRA_IO_PAD_SDMMC3, 34, UINT_MAX, "sdmmc3"), \ + _pad(TEGRA_IO_PAD_SDMMC4, 35, UINT_MAX, "sdmmc4"), \ + _pad(TEGRA_IO_PAD_SYS_DDC, 58, UINT_MAX, "sys_ddc"), \ + _pad(TEGRA_IO_PAD_UART, 14, UINT_MAX, "uart"), \ + _pad(TEGRA_IO_PAD_USB0, 9, UINT_MAX, "usb0"), \ + _pad(TEGRA_IO_PAD_USB1, 10, UINT_MAX, "usb1"), \ + _pad(TEGRA_IO_PAD_USB2, 11, UINT_MAX, "usb2"), \ + _pad(TEGRA_IO_PAD_USB_BIAS, 12, UINT_MAX, "usb_bias") + static const struct tegra_io_pad_soc tegra124_io_pads[] = { - { .id = TEGRA_IO_PAD_AUDIO, .dpd = 17, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_BB, .dpd = 15, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CAM, .dpd = 36, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_COMP, .dpd = 22, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIA, .dpd = 0, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIB, .dpd = 1, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIE, .dpd = 44, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSI, .dpd = 2, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSIB, .dpd = 39, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSIC, .dpd = 40, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSID, .dpd = 41, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI, .dpd = 28, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HSIC, .dpd = 19, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HV, .dpd = 38, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_LVDS, .dpd = 57, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_MIPI_BIAS, .dpd = 3, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_NAND, .dpd = 13, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_BIAS, .dpd = 4, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK1, .dpd = 5, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 6, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CNTRL, .dpd = 32, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC1, .dpd = 33, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC3, .dpd = 34, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC4, .dpd = 35, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SYS_DDC, .dpd = 58, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UART, .dpd = 14, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB0, .dpd = 9, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB1, .dpd = 10, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB2, .dpd = 11, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB_BIAS, .dpd = 12, .voltage = UINT_MAX }, + TEGRA124_IO_PAD_TABLE(TEGRA_IO_PAD) +}; + +static const struct pinctrl_pin_desc tegra124_pin_descs[] = { + TEGRA124_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) }; static const struct tegra_pmc_soc tegra124_pmc_soc = { @@ -1689,8 +1918,11 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .cpu_powergates = tegra124_cpu_powergates, .has_tsense_reset = true, .has_gpu_clamps = true, + .has_impl_33v_pwr = false, .num_io_pads = ARRAY_SIZE(tegra124_io_pads), .io_pads = tegra124_io_pads, + .num_pin_descs = ARRAY_SIZE(tegra124_pin_descs), + .pin_descs = tegra124_pin_descs, .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, @@ -1730,45 +1962,53 @@ static const u8 tegra210_cpu_powergates[] = { TEGRA_POWERGATE_CPU3, }; +#define TEGRA210_IO_PAD_TABLE(_pad) \ + /* .id .dpd .voltage .name */ \ + _pad(TEGRA_IO_PAD_AUDIO, 17, 5, "audio"), \ + _pad(TEGRA_IO_PAD_AUDIO_HV, 61, 18, "audio-hv"), \ + _pad(TEGRA_IO_PAD_CAM, 36, 10, "cam"), \ + _pad(TEGRA_IO_PAD_CSIA, 0, UINT_MAX, "csia"), \ + _pad(TEGRA_IO_PAD_CSIB, 1, UINT_MAX, "csib"), \ + _pad(TEGRA_IO_PAD_CSIC, 42, UINT_MAX, "csic"), \ + _pad(TEGRA_IO_PAD_CSID, 43, UINT_MAX, "csid"), \ + _pad(TEGRA_IO_PAD_CSIE, 44, UINT_MAX, "csie"), \ + _pad(TEGRA_IO_PAD_CSIF, 45, UINT_MAX, "csif"), \ + _pad(TEGRA_IO_PAD_DBG, 25, 19, "dbg"), \ + _pad(TEGRA_IO_PAD_DEBUG_NONAO, 26, UINT_MAX, "debug-nonao"), \ + _pad(TEGRA_IO_PAD_DMIC, 50, 20, "dmic"), \ + _pad(TEGRA_IO_PAD_DP, 51, UINT_MAX, "dp"), \ + _pad(TEGRA_IO_PAD_DSI, 2, UINT_MAX, "dsi"), \ + _pad(TEGRA_IO_PAD_DSIB, 39, UINT_MAX, "dsib"), \ + _pad(TEGRA_IO_PAD_DSIC, 40, UINT_MAX, "dsic"), \ + _pad(TEGRA_IO_PAD_DSID, 41, UINT_MAX, "dsid"), \ + _pad(TEGRA_IO_PAD_EMMC, 35, UINT_MAX, "emmc"), \ + _pad(TEGRA_IO_PAD_EMMC2, 37, UINT_MAX, "emmc2"), \ + _pad(TEGRA_IO_PAD_GPIO, 27, 21, "gpio"), \ + _pad(TEGRA_IO_PAD_HDMI, 28, UINT_MAX, "hdmi"), \ + _pad(TEGRA_IO_PAD_HSIC, 19, UINT_MAX, "hsic"), \ + _pad(TEGRA_IO_PAD_LVDS, 57, UINT_MAX, "lvds"), \ + _pad(TEGRA_IO_PAD_MIPI_BIAS, 3, UINT_MAX, "mipi-bias"), \ + _pad(TEGRA_IO_PAD_PEX_BIAS, 4, UINT_MAX, "pex-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK1, 5, UINT_MAX, "pex-clk1"), \ + _pad(TEGRA_IO_PAD_PEX_CLK2, 6, UINT_MAX, "pex-clk2"), \ + _pad(TEGRA_IO_PAD_PEX_CNTRL, UINT_MAX, 11, "pex-cntrl"), \ + _pad(TEGRA_IO_PAD_SDMMC1, 33, 12, "sdmmc1"), \ + _pad(TEGRA_IO_PAD_SDMMC3, 34, 13, "sdmmc3"), \ + _pad(TEGRA_IO_PAD_SPI, 46, 22, "spi"), \ + _pad(TEGRA_IO_PAD_SPI_HV, 47, 23, "spi-hv"), \ + _pad(TEGRA_IO_PAD_UART, 14, 2, "uart"), \ + _pad(TEGRA_IO_PAD_USB0, 9, UINT_MAX, "usb0"), \ + _pad(TEGRA_IO_PAD_USB1, 10, UINT_MAX, "usb1"), \ + _pad(TEGRA_IO_PAD_USB2, 11, UINT_MAX, "usb2"), \ + _pad(TEGRA_IO_PAD_USB3, 18, UINT_MAX, "usb3"), \ + _pad(TEGRA_IO_PAD_USB_BIAS, 12, UINT_MAX, "usb-bias") + static const struct tegra_io_pad_soc tegra210_io_pads[] = { - { .id = TEGRA_IO_PAD_AUDIO, .dpd = 17, .voltage = 5 }, - { .id = TEGRA_IO_PAD_AUDIO_HV, .dpd = 61, .voltage = 18 }, - { .id = TEGRA_IO_PAD_CAM, .dpd = 36, .voltage = 10 }, - { .id = TEGRA_IO_PAD_CSIA, .dpd = 0, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIB, .dpd = 1, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIC, .dpd = 42, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSID, .dpd = 43, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIE, .dpd = 44, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIF, .dpd = 45, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DBG, .dpd = 25, .voltage = 19 }, - { .id = TEGRA_IO_PAD_DEBUG_NONAO, .dpd = 26, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DMIC, .dpd = 50, .voltage = 20 }, - { .id = TEGRA_IO_PAD_DP, .dpd = 51, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSI, .dpd = 2, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSIB, .dpd = 39, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSIC, .dpd = 40, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSID, .dpd = 41, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_EMMC, .dpd = 35, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_EMMC2, .dpd = 37, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_GPIO, .dpd = 27, .voltage = 21 }, - { .id = TEGRA_IO_PAD_HDMI, .dpd = 28, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HSIC, .dpd = 19, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_LVDS, .dpd = 57, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_MIPI_BIAS, .dpd = 3, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_BIAS, .dpd = 4, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK1, .dpd = 5, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 6, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CNTRL, .dpd = UINT_MAX, .voltage = 11 }, - { .id = TEGRA_IO_PAD_SDMMC1, .dpd = 33, .voltage = 12 }, - { .id = TEGRA_IO_PAD_SDMMC3, .dpd = 34, .voltage = 13 }, - { .id = TEGRA_IO_PAD_SPI, .dpd = 46, .voltage = 22 }, - { .id = TEGRA_IO_PAD_SPI_HV, .dpd = 47, .voltage = 23 }, - { .id = TEGRA_IO_PAD_UART, .dpd = 14, .voltage = 2 }, - { .id = TEGRA_IO_PAD_USB0, .dpd = 9, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB1, .dpd = 10, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB2, .dpd = 11, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB3, .dpd = 18, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB_BIAS, .dpd = 12, .voltage = UINT_MAX }, + TEGRA210_IO_PAD_TABLE(TEGRA_IO_PAD) +}; + +static const struct pinctrl_pin_desc tegra210_pin_descs[] = { + TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) }; static const struct tegra_pmc_soc tegra210_pmc_soc = { @@ -1778,52 +2018,64 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .cpu_powergates = tegra210_cpu_powergates, .has_tsense_reset = true, .has_gpu_clamps = true, + .has_impl_33v_pwr = false, .needs_mbist_war = true, .num_io_pads = ARRAY_SIZE(tegra210_io_pads), .io_pads = tegra210_io_pads, + .num_pin_descs = ARRAY_SIZE(tegra210_pin_descs), + .pin_descs = tegra210_pin_descs, .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, }; +#define TEGRA186_IO_PAD_TABLE(_pad) \ + /* .id .dpd .voltage .name */ \ + _pad(TEGRA_IO_PAD_CSIA, 0, UINT_MAX, "csia"), \ + _pad(TEGRA_IO_PAD_CSIB, 1, UINT_MAX, "csib"), \ + _pad(TEGRA_IO_PAD_DSI, 2, UINT_MAX, "dsi"), \ + _pad(TEGRA_IO_PAD_MIPI_BIAS, 3, UINT_MAX, "mipi-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK_BIAS, 4, UINT_MAX, "pex-clk-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK3, 5, UINT_MAX, "pex-clk3"), \ + _pad(TEGRA_IO_PAD_PEX_CLK2, 6, UINT_MAX, "pex-clk2"), \ + _pad(TEGRA_IO_PAD_PEX_CLK1, 7, UINT_MAX, "pex-clk1"), \ + _pad(TEGRA_IO_PAD_USB0, 9, UINT_MAX, "usb0"), \ + _pad(TEGRA_IO_PAD_USB1, 10, UINT_MAX, "usb1"), \ + _pad(TEGRA_IO_PAD_USB2, 11, UINT_MAX, "usb2"), \ + _pad(TEGRA_IO_PAD_USB_BIAS, 12, UINT_MAX, "usb-bias"), \ + _pad(TEGRA_IO_PAD_UART, 14, UINT_MAX, "uart"), \ + _pad(TEGRA_IO_PAD_AUDIO, 17, UINT_MAX, "audio"), \ + _pad(TEGRA_IO_PAD_HSIC, 19, UINT_MAX, "hsic"), \ + _pad(TEGRA_IO_PAD_DBG, 25, UINT_MAX, "dbg"), \ + _pad(TEGRA_IO_PAD_HDMI_DP0, 28, UINT_MAX, "hdmi-dp0"), \ + _pad(TEGRA_IO_PAD_HDMI_DP1, 29, UINT_MAX, "hdmi-dp1"), \ + _pad(TEGRA_IO_PAD_PEX_CNTRL, 32, UINT_MAX, "pex-cntrl"), \ + _pad(TEGRA_IO_PAD_SDMMC2_HV, 34, 5, "sdmmc2-hv"), \ + _pad(TEGRA_IO_PAD_SDMMC4, 36, UINT_MAX, "sdmmc4"), \ + _pad(TEGRA_IO_PAD_CAM, 38, UINT_MAX, "cam"), \ + _pad(TEGRA_IO_PAD_DSIB, 40, UINT_MAX, "dsib"), \ + _pad(TEGRA_IO_PAD_DSIC, 41, UINT_MAX, "dsic"), \ + _pad(TEGRA_IO_PAD_DSID, 42, UINT_MAX, "dsid"), \ + _pad(TEGRA_IO_PAD_CSIC, 43, UINT_MAX, "csic"), \ + _pad(TEGRA_IO_PAD_CSID, 44, UINT_MAX, "csid"), \ + _pad(TEGRA_IO_PAD_CSIE, 45, UINT_MAX, "csie"), \ + _pad(TEGRA_IO_PAD_CSIF, 46, UINT_MAX, "csif"), \ + _pad(TEGRA_IO_PAD_SPI, 47, UINT_MAX, "spi"), \ + _pad(TEGRA_IO_PAD_UFS, 49, UINT_MAX, "ufs"), \ + _pad(TEGRA_IO_PAD_DMIC_HV, 52, 2, "dmic-hv"), \ + _pad(TEGRA_IO_PAD_EDP, 53, UINT_MAX, "edp"), \ + _pad(TEGRA_IO_PAD_SDMMC1_HV, 55, 4, "sdmmc1-hv"), \ + _pad(TEGRA_IO_PAD_SDMMC3_HV, 56, 6, "sdmmc3-hv"), \ + _pad(TEGRA_IO_PAD_CONN, 60, UINT_MAX, "conn"), \ + _pad(TEGRA_IO_PAD_AUDIO_HV, 61, 1, "audio-hv"), \ + _pad(TEGRA_IO_PAD_AO_HV, UINT_MAX, 0, "ao-hv") + static const struct tegra_io_pad_soc tegra186_io_pads[] = { - { .id = TEGRA_IO_PAD_CSIA, .dpd = 0, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIB, .dpd = 1, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSI, .dpd = 2, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_MIPI_BIAS, .dpd = 3, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK_BIAS, .dpd = 4, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK3, .dpd = 5, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 6, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK1, .dpd = 7, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB0, .dpd = 9, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB1, .dpd = 10, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB2, .dpd = 11, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_USB_BIAS, .dpd = 12, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UART, .dpd = 14, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_AUDIO, .dpd = 17, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HSIC, .dpd = 19, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DBG, .dpd = 25, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI_DP0, .dpd = 28, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI_DP1, .dpd = 29, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CNTRL, .dpd = 32, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC2_HV, .dpd = 34, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC4, .dpd = 36, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CAM, .dpd = 38, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSIB, .dpd = 40, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSIC, .dpd = 41, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DSID, .dpd = 42, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIC, .dpd = 43, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSID, .dpd = 44, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIE, .dpd = 45, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIF, .dpd = 46, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SPI, .dpd = 47, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UFS, .dpd = 49, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DMIC_HV, .dpd = 52, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_EDP, .dpd = 53, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC1_HV, .dpd = 55, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC3_HV, .dpd = 56, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CONN, .dpd = 60, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_AUDIO_HV, .dpd = 61, .voltage = UINT_MAX }, + TEGRA186_IO_PAD_TABLE(TEGRA_IO_PAD) +}; + +static const struct pinctrl_pin_desc tegra186_pin_descs[] = { + TEGRA186_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) }; static const struct tegra_pmc_regs tegra186_pmc_regs = { @@ -1876,8 +2128,11 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .cpu_powergates = NULL, .has_tsense_reset = false, .has_gpu_clamps = false, + .has_impl_33v_pwr = true, .num_io_pads = ARRAY_SIZE(tegra186_io_pads), .io_pads = tegra186_io_pads, + .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), + .pin_descs = tegra186_pin_descs, .regs = &tegra186_pmc_regs, .init = NULL, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c @@ -438,7 +438,7 @@ void *knav_dma_open_channel(struct device *dev, const char *name, chan_num = of_channel_match_helper(dev->of_node, name, &instance); if (chan_num < 0) { - dev_err(kdev->dev, "No DMA instace with name %s\n", name); + dev_err(kdev->dev, "No DMA instance with name %s\n", name); return (void *)-EINVAL; } @@ -461,7 +461,7 @@ void *knav_dma_open_channel(struct device *dev, const char *name, } } if (!found) { - dev_err(kdev->dev, "No DMA instace with name %s\n", instance); + dev_err(kdev->dev, "No DMA instance with name %s\n", instance); return (void *)-EINVAL; } diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h @@ -240,14 +240,14 @@ struct knav_pool { }; /** - * struct knav_queue_inst: qmss queue instace properties + * struct knav_queue_inst: qmss queue instance properties * @descs: descriptor pointer * @desc_head, desc_tail, desc_count: descriptor counters * @acc: accumulator channel pointer * @kdev: qmss device pointer * @range: range info * @qmgr: queue manager info - * @id: queue instace id + * @id: queue instance id * @irq_num: irq line number * @notify_needed: notifier needed based on queue type * @num_notifiers: total notifiers @@ -274,7 +274,7 @@ struct knav_queue_inst { /** * struct knav_queue: qmss queue properties * @reg_push, reg_pop, reg_peek: push, pop queue registers - * @inst: qmss queue instace properties + * @inst: qmss queue instance properties * @notifier_fn: notifier function * @notifier_fn_arg: notifier function argument * @notifier_enabled: notier enabled for a give queue diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c @@ -696,7 +696,7 @@ static int __init optee_driver_init(void) return -ENODEV; np = of_find_matching_node(fw_np, optee_match); - if (!np) + if (!np || !of_device_is_available(np)) return -ENODEV; optee = optee_probe(np); diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c @@ -38,15 +38,13 @@ static DEFINE_SPINLOCK(driver_lock); static struct class *tee_class; static dev_t tee_devt; -static int tee_open(struct inode *inode, struct file *filp) +static struct tee_context *teedev_open(struct tee_device *teedev) { int rc; - struct tee_device *teedev; struct tee_context *ctx; - teedev = container_of(inode->i_cdev, struct tee_device, cdev); if (!tee_device_get(teedev)) - return -EINVAL; + return ERR_PTR(-EINVAL); ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { @@ -57,16 +55,16 @@ static int tee_open(struct inode *inode, struct file *filp) kref_init(&ctx->refcount); ctx->teedev = teedev; INIT_LIST_HEAD(&ctx->list_shm); - filp->private_data = ctx; rc = teedev->desc->ops->open(ctx); if (rc) goto err; - return 0; + return ctx; err: kfree(ctx); tee_device_put(teedev); - return rc; + return ERR_PTR(rc); + } void teedev_ctx_get(struct tee_context *ctx) @@ -100,6 +98,18 @@ static void teedev_close_context(struct tee_context *ctx) teedev_ctx_put(ctx); } +static int tee_open(struct inode *inode, struct file *filp) +{ + struct tee_context *ctx; + + ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev)); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + filp->private_data = ctx; + return 0; +} + static int tee_release(struct inode *inode, struct file *filp) { teedev_close_context(filp->private_data); @@ -928,6 +938,95 @@ void *tee_get_drvdata(struct tee_device *teedev) } EXPORT_SYMBOL_GPL(tee_get_drvdata); +struct match_dev_data { + struct tee_ioctl_version_data *vers; + const void *data; + int (*match)(struct tee_ioctl_version_data *, const void *); +}; + +static int match_dev(struct device *dev, const void *data) +{ + const struct match_dev_data *match_data = data; + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + + teedev->desc->ops->get_version(teedev, match_data->vers); + return match_data->match(match_data->vers, match_data->data); +} + +struct tee_context * +tee_client_open_context(struct tee_context *start, + int (*match)(struct tee_ioctl_version_data *, + const void *), + const void *data, struct tee_ioctl_version_data *vers) +{ + struct device *dev = NULL; + struct device *put_dev = NULL; + struct tee_context *ctx = NULL; + struct tee_ioctl_version_data v; + struct match_dev_data match_data = { vers ? vers : &v, data, match }; + + if (start) + dev = &start->teedev->dev; + + do { + dev = class_find_device(tee_class, dev, &match_data, match_dev); + if (!dev) { + ctx = ERR_PTR(-ENOENT); + break; + } + + put_device(put_dev); + put_dev = dev; + + ctx = teedev_open(container_of(dev, struct tee_device, dev)); + } while (IS_ERR(ctx) && PTR_ERR(ctx) != -ENOMEM); + + put_device(put_dev); + return ctx; +} +EXPORT_SYMBOL_GPL(tee_client_open_context); + +void tee_client_close_context(struct tee_context *ctx) +{ + teedev_close_context(ctx); +} +EXPORT_SYMBOL_GPL(tee_client_close_context); + +void tee_client_get_version(struct tee_context *ctx, + struct tee_ioctl_version_data *vers) +{ + ctx->teedev->desc->ops->get_version(ctx->teedev, vers); +} +EXPORT_SYMBOL_GPL(tee_client_get_version); + +int tee_client_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param) +{ + if (!ctx->teedev->desc->ops->open_session) + return -EINVAL; + return ctx->teedev->desc->ops->open_session(ctx, arg, param); +} +EXPORT_SYMBOL_GPL(tee_client_open_session); + +int tee_client_close_session(struct tee_context *ctx, u32 session) +{ + if (!ctx->teedev->desc->ops->close_session) + return -EINVAL; + return ctx->teedev->desc->ops->close_session(ctx, session); +} +EXPORT_SYMBOL_GPL(tee_client_close_session); + +int tee_client_invoke_func(struct tee_context *ctx, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param) +{ + if (!ctx->teedev->desc->ops->invoke_func) + return -EINVAL; + return ctx->teedev->desc->ops->invoke_func(ctx, arg, param); +} +EXPORT_SYMBOL_GPL(tee_client_invoke_func); + static int __init tee_init(void) { int rc; diff --git a/include/dt-bindings/clock/xlnx,zynqmp-clk.h b/include/dt-bindings/clock/xlnx,zynqmp-clk.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Xilinx Zynq MPSoC Firmware layer + * + * Copyright (C) 2014-2018 Xilinx, Inc. + * + */ + +#ifndef _DT_BINDINGS_CLK_ZYNQMP_H +#define _DT_BINDINGS_CLK_ZYNQMP_H + +#define IOPLL 0 +#define RPLL 1 +#define APLL 2 +#define DPLL 3 +#define VPLL 4 +#define IOPLL_TO_FPD 5 +#define RPLL_TO_FPD 6 +#define APLL_TO_LPD 7 +#define DPLL_TO_LPD 8 +#define VPLL_TO_LPD 9 +#define ACPU 10 +#define ACPU_HALF 11 +#define DBF_FPD 12 +#define DBF_LPD 13 +#define DBG_TRACE 14 +#define DBG_TSTMP 15 +#define DP_VIDEO_REF 16 +#define DP_AUDIO_REF 17 +#define DP_STC_REF 18 +#define GDMA_REF 19 +#define DPDMA_REF 20 +#define DDR_REF 21 +#define SATA_REF 22 +#define PCIE_REF 23 +#define GPU_REF 24 +#define GPU_PP0_REF 25 +#define GPU_PP1_REF 26 +#define TOPSW_MAIN 27 +#define TOPSW_LSBUS 28 +#define GTGREF0_REF 29 +#define LPD_SWITCH 30 +#define LPD_LSBUS 31 +#define USB0_BUS_REF 32 +#define USB1_BUS_REF 33 +#define USB3_DUAL_REF 34 +#define USB0 35 +#define USB1 36 +#define CPU_R5 37 +#define CPU_R5_CORE 38 +#define CSU_SPB 39 +#define CSU_PLL 40 +#define PCAP 41 +#define IOU_SWITCH 42 +#define GEM_TSU_REF 43 +#define GEM_TSU 44 +#define GEM0_REF 45 +#define GEM1_REF 46 +#define GEM2_REF 47 +#define GEM3_REF 48 +#define GEM0_TX 49 +#define GEM1_TX 50 +#define GEM2_TX 51 +#define GEM3_TX 52 +#define QSPI_REF 53 +#define SDIO0_REF 54 +#define SDIO1_REF 55 +#define UART0_REF 56 +#define UART1_REF 57 +#define SPI0_REF 58 +#define SPI1_REF 59 +#define NAND_REF 60 +#define I2C0_REF 61 +#define I2C1_REF 62 +#define CAN0_REF 63 +#define CAN1_REF 64 +#define CAN0 65 +#define CAN1 66 +#define DLL_REF 67 +#define ADMA_REF 68 +#define TIMESTAMP_REF 69 +#define AMS_REF 70 +#define PL0_REF 71 +#define PL1_REF 72 +#define PL2_REF 73 +#define PL3_REF 74 +#define WDT 75 +#define IOPLL_INT 76 +#define IOPLL_PRE_SRC 77 +#define IOPLL_HALF 78 +#define IOPLL_INT_MUX 79 +#define IOPLL_POST_SRC 80 +#define RPLL_INT 81 +#define RPLL_PRE_SRC 82 +#define RPLL_HALF 83 +#define RPLL_INT_MUX 84 +#define RPLL_POST_SRC 85 +#define APLL_INT 86 +#define APLL_PRE_SRC 87 +#define APLL_HALF 88 +#define APLL_INT_MUX 89 +#define APLL_POST_SRC 90 +#define DPLL_INT 91 +#define DPLL_PRE_SRC 92 +#define DPLL_HALF 93 +#define DPLL_INT_MUX 94 +#define DPLL_POST_SRC 95 +#define VPLL_INT 96 +#define VPLL_PRE_SRC 97 +#define VPLL_HALF 98 +#define VPLL_INT_MUX 99 +#define VPLL_POST_SRC 100 +#define CAN0_MIO 101 +#define CAN1_MIO 102 + +#endif diff --git a/include/dt-bindings/power/r8a7744-sysc.h b/include/dt-bindings/power/r8a7744-sysc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Renesas Electronics Corp. + */ +#ifndef __DT_BINDINGS_POWER_R8A7744_SYSC_H__ +#define __DT_BINDINGS_POWER_R8A7744_SYSC_H__ + +/* + * These power domain indices match the numbers of the interrupt bits + * representing the power areas in the various Interrupt Registers + * (e.g. SYSCISR, Interrupt Status Register) + * + * Note that RZ/G1N is identical to RZ/G2M w.r.t. power domains. + */ + +#define R8A7744_PD_CA15_CPU0 0 +#define R8A7744_PD_CA15_CPU1 1 +#define R8A7744_PD_CA15_SCU 12 +#define R8A7744_PD_SGX 20 + +/* Always-on power area */ +#define R8A7744_PD_ALWAYS_ON 32 + +#endif /* __DT_BINDINGS_POWER_R8A7744_SYSC_H__ */ diff --git a/include/dt-bindings/power/r8a774a1-sysc.h b/include/dt-bindings/power/r8a774a1-sysc.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Renesas Electronics Corp. + */ +#ifndef __DT_BINDINGS_POWER_R8A774A1_SYSC_H__ +#define __DT_BINDINGS_POWER_R8A774A1_SYSC_H__ + +/* + * These power domain indices match the numbers of the interrupt bits + * representing the power areas in the various Interrupt Registers + * (e.g. SYSCISR, Interrupt Status Register) + */ + +#define R8A774A1_PD_CA57_CPU0 0 +#define R8A774A1_PD_CA57_CPU1 1 +#define R8A774A1_PD_CA53_CPU0 5 +#define R8A774A1_PD_CA53_CPU1 6 +#define R8A774A1_PD_CA53_CPU2 7 +#define R8A774A1_PD_CA53_CPU3 8 +#define R8A774A1_PD_CA57_SCU 12 +#define R8A774A1_PD_A3VC 14 +#define R8A774A1_PD_3DG_A 17 +#define R8A774A1_PD_3DG_B 18 +#define R8A774A1_PD_CA53_SCU 21 +#define R8A774A1_PD_A2VC0 25 +#define R8A774A1_PD_A2VC1 26 + +/* Always-on power area */ +#define R8A774A1_PD_ALWAYS_ON 32 + +#endif /* __DT_BINDINGS_POWER_R8A774A1_SYSC_H__ */ diff --git a/include/dt-bindings/power/r8a774c0-sysc.h b/include/dt-bindings/power/r8a774c0-sysc.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Renesas Electronics Corp. + */ +#ifndef __DT_BINDINGS_POWER_R8A774C0_SYSC_H__ +#define __DT_BINDINGS_POWER_R8A774C0_SYSC_H__ + +/* + * These power domain indices match the numbers of the interrupt bits + * representing the power areas in the various Interrupt Registers + * (e.g. SYSCISR, Interrupt Status Register) + */ + +#define R8A774C0_PD_CA53_CPU0 5 +#define R8A774C0_PD_CA53_CPU1 6 +#define R8A774C0_PD_A3VC 14 +#define R8A774C0_PD_3DG_A 17 +#define R8A774C0_PD_3DG_B 18 +#define R8A774C0_PD_CA53_SCU 21 +#define R8A774C0_PD_A2VC1 26 + +/* Always-on power area */ +#define R8A774C0_PD_ALWAYS_ON 32 + +#endif /* __DT_BINDINGS_POWER_R8A774C0_SYSC_H__ */ diff --git a/include/dt-bindings/reset/qcom,sdm845-pdc.h b/include/dt-bindings/reset/qcom,sdm845-pdc.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_RESET_PDC_SDM_845_H +#define _DT_BINDINGS_RESET_PDC_SDM_845_H + +#define PDC_APPS_SYNC_RESET 0 +#define PDC_SP_SYNC_RESET 1 +#define PDC_AUDIO_SYNC_RESET 2 +#define PDC_SENSORS_SYNC_RESET 3 +#define PDC_AOP_SYNC_RESET 4 +#define PDC_DEBUG_SYNC_RESET 5 +#define PDC_GPU_SYNC_RESET 6 +#define PDC_DISPLAY_SYNC_RESET 7 +#define PDC_COMPUTE_SYNC_RESET 8 +#define PDC_MODEM_SYNC_RESET 9 + +#endif diff --git a/include/linux/firmware/imx/ipc.h b/include/linux/firmware/imx/ipc.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 NXP + * + * Header file for the IPC implementation. + */ + +#ifndef _SC_IPC_H +#define _SC_IPC_H + +#include <linux/device.h> +#include <linux/types.h> + +#define IMX_SC_RPC_VERSION 1 +#define IMX_SC_RPC_MAX_MSG 8 + +struct imx_sc_ipc; + +enum imx_sc_rpc_svc { + IMX_SC_RPC_SVC_UNKNOWN = 0, + IMX_SC_RPC_SVC_RETURN = 1, + IMX_SC_RPC_SVC_PM = 2, + IMX_SC_RPC_SVC_RM = 3, + IMX_SC_RPC_SVC_TIMER = 5, + IMX_SC_RPC_SVC_PAD = 6, + IMX_SC_RPC_SVC_MISC = 7, + IMX_SC_RPC_SVC_IRQ = 8, + IMX_SC_RPC_SVC_ABORT = 9 +}; + +struct imx_sc_rpc_msg { + uint8_t ver; + uint8_t size; + uint8_t svc; + uint8_t func; +}; + +/* + * This is an function to send an RPC message over an IPC channel. + * It is called by client-side SCFW API function shims. + * + * @param[in] ipc IPC handle + * @param[in,out] msg handle to a message + * @param[in] have_resp response flag + * + * If have_resp is true then this function waits for a response + * and returns the result in msg. + */ +int imx_scu_call_rpc(struct imx_sc_ipc *ipc, void *msg, bool have_resp); + +/* + * This function gets the default ipc handle used by SCU + * + * @param[out] ipc sc ipc handle + * + * @return Returns an error code (0 = success, failed if < 0) + */ +int imx_scu_get_handle(struct imx_sc_ipc **ipc); +#endif /* _SC_IPC_H */ diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Header file containing the public System Controller Interface (SCI) + * definitions. + */ + +#ifndef _SC_SCI_H +#define _SC_SCI_H + +#include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/types.h> + +#include <linux/firmware/imx/svc/misc.h> +#endif /* _SC_SCI_H */ diff --git a/include/linux/firmware/imx/svc/misc.h b/include/linux/firmware/imx/svc/misc.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Header file containing the public API for the System Controller (SC) + * Miscellaneous (MISC) function. + * + * MISC_SVC (SVC) Miscellaneous Service + * + * Module for the Miscellaneous (MISC) service. + */ + +#ifndef _SC_MISC_API_H +#define _SC_MISC_API_H + +#include <linux/firmware/imx/sci.h> + +/* + * This type is used to indicate RPC MISC function calls. + */ +enum imx_misc_func { + IMX_SC_MISC_FUNC_UNKNOWN = 0, + IMX_SC_MISC_FUNC_SET_CONTROL = 1, + IMX_SC_MISC_FUNC_GET_CONTROL = 2, + IMX_SC_MISC_FUNC_SET_MAX_DMA_GROUP = 4, + IMX_SC_MISC_FUNC_SET_DMA_GROUP = 5, + IMX_SC_MISC_FUNC_SECO_IMAGE_LOAD = 8, + IMX_SC_MISC_FUNC_SECO_AUTHENTICATE = 9, + IMX_SC_MISC_FUNC_DEBUG_OUT = 10, + IMX_SC_MISC_FUNC_WAVEFORM_CAPTURE = 6, + IMX_SC_MISC_FUNC_BUILD_INFO = 15, + IMX_SC_MISC_FUNC_UNIQUE_ID = 19, + IMX_SC_MISC_FUNC_SET_ARI = 3, + IMX_SC_MISC_FUNC_BOOT_STATUS = 7, + IMX_SC_MISC_FUNC_BOOT_DONE = 14, + IMX_SC_MISC_FUNC_OTP_FUSE_READ = 11, + IMX_SC_MISC_FUNC_OTP_FUSE_WRITE = 17, + IMX_SC_MISC_FUNC_SET_TEMP = 12, + IMX_SC_MISC_FUNC_GET_TEMP = 13, + IMX_SC_MISC_FUNC_GET_BOOT_DEV = 16, + IMX_SC_MISC_FUNC_GET_BUTTON_STATUS = 18, +}; + +/* + * Control Functions + */ + +int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource, + u8 ctrl, u32 val); + +int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, + u8 ctrl, u32 *val); + +#endif /* _SC_MISC_API_H */ diff --git a/include/linux/firmware/imx/types.h b/include/linux/firmware/imx/types.h @@ -0,0 +1,617 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Header file containing types used across multiple service APIs. + */ + +#ifndef _SC_TYPES_H +#define _SC_TYPES_H + +/* + * This type is used to indicate a resource. Resources include peripherals + * and bus masters (but not memory regions). Note items from list should + * never be changed or removed (only added to at the end of the list). + */ +enum imx_sc_rsrc { + IMX_SC_R_A53 = 0, + IMX_SC_R_A53_0 = 1, + IMX_SC_R_A53_1 = 2, + IMX_SC_R_A53_2 = 3, + IMX_SC_R_A53_3 = 4, + IMX_SC_R_A72 = 5, + IMX_SC_R_A72_0 = 6, + IMX_SC_R_A72_1 = 7, + IMX_SC_R_A72_2 = 8, + IMX_SC_R_A72_3 = 9, + IMX_SC_R_CCI = 10, + IMX_SC_R_DB = 11, + IMX_SC_R_DRC_0 = 12, + IMX_SC_R_DRC_1 = 13, + IMX_SC_R_GIC_SMMU = 14, + IMX_SC_R_IRQSTR_M4_0 = 15, + IMX_SC_R_IRQSTR_M4_1 = 16, + IMX_SC_R_SMMU = 17, + IMX_SC_R_GIC = 18, + IMX_SC_R_DC_0_BLIT0 = 19, + IMX_SC_R_DC_0_BLIT1 = 20, + IMX_SC_R_DC_0_BLIT2 = 21, + IMX_SC_R_DC_0_BLIT_OUT = 22, + IMX_SC_R_DC_0_CAPTURE0 = 23, + IMX_SC_R_DC_0_CAPTURE1 = 24, + IMX_SC_R_DC_0_WARP = 25, + IMX_SC_R_DC_0_INTEGRAL0 = 26, + IMX_SC_R_DC_0_INTEGRAL1 = 27, + IMX_SC_R_DC_0_VIDEO0 = 28, + IMX_SC_R_DC_0_VIDEO1 = 29, + IMX_SC_R_DC_0_FRAC0 = 30, + IMX_SC_R_DC_0_FRAC1 = 31, + IMX_SC_R_DC_0 = 32, + IMX_SC_R_GPU_2_PID0 = 33, + IMX_SC_R_DC_0_PLL_0 = 34, + IMX_SC_R_DC_0_PLL_1 = 35, + IMX_SC_R_DC_1_BLIT0 = 36, + IMX_SC_R_DC_1_BLIT1 = 37, + IMX_SC_R_DC_1_BLIT2 = 38, + IMX_SC_R_DC_1_BLIT_OUT = 39, + IMX_SC_R_DC_1_CAPTURE0 = 40, + IMX_SC_R_DC_1_CAPTURE1 = 41, + IMX_SC_R_DC_1_WARP = 42, + IMX_SC_R_DC_1_INTEGRAL0 = 43, + IMX_SC_R_DC_1_INTEGRAL1 = 44, + IMX_SC_R_DC_1_VIDEO0 = 45, + IMX_SC_R_DC_1_VIDEO1 = 46, + IMX_SC_R_DC_1_FRAC0 = 47, + IMX_SC_R_DC_1_FRAC1 = 48, + IMX_SC_R_DC_1 = 49, + IMX_SC_R_GPU_3_PID0 = 50, + IMX_SC_R_DC_1_PLL_0 = 51, + IMX_SC_R_DC_1_PLL_1 = 52, + IMX_SC_R_SPI_0 = 53, + IMX_SC_R_SPI_1 = 54, + IMX_SC_R_SPI_2 = 55, + IMX_SC_R_SPI_3 = 56, + IMX_SC_R_UART_0 = 57, + IMX_SC_R_UART_1 = 58, + IMX_SC_R_UART_2 = 59, + IMX_SC_R_UART_3 = 60, + IMX_SC_R_UART_4 = 61, + IMX_SC_R_EMVSIM_0 = 62, + IMX_SC_R_EMVSIM_1 = 63, + IMX_SC_R_DMA_0_CH0 = 64, + IMX_SC_R_DMA_0_CH1 = 65, + IMX_SC_R_DMA_0_CH2 = 66, + IMX_SC_R_DMA_0_CH3 = 67, + IMX_SC_R_DMA_0_CH4 = 68, + IMX_SC_R_DMA_0_CH5 = 69, + IMX_SC_R_DMA_0_CH6 = 70, + IMX_SC_R_DMA_0_CH7 = 71, + IMX_SC_R_DMA_0_CH8 = 72, + IMX_SC_R_DMA_0_CH9 = 73, + IMX_SC_R_DMA_0_CH10 = 74, + IMX_SC_R_DMA_0_CH11 = 75, + IMX_SC_R_DMA_0_CH12 = 76, + IMX_SC_R_DMA_0_CH13 = 77, + IMX_SC_R_DMA_0_CH14 = 78, + IMX_SC_R_DMA_0_CH15 = 79, + IMX_SC_R_DMA_0_CH16 = 80, + IMX_SC_R_DMA_0_CH17 = 81, + IMX_SC_R_DMA_0_CH18 = 82, + IMX_SC_R_DMA_0_CH19 = 83, + IMX_SC_R_DMA_0_CH20 = 84, + IMX_SC_R_DMA_0_CH21 = 85, + IMX_SC_R_DMA_0_CH22 = 86, + IMX_SC_R_DMA_0_CH23 = 87, + IMX_SC_R_DMA_0_CH24 = 88, + IMX_SC_R_DMA_0_CH25 = 89, + IMX_SC_R_DMA_0_CH26 = 90, + IMX_SC_R_DMA_0_CH27 = 91, + IMX_SC_R_DMA_0_CH28 = 92, + IMX_SC_R_DMA_0_CH29 = 93, + IMX_SC_R_DMA_0_CH30 = 94, + IMX_SC_R_DMA_0_CH31 = 95, + IMX_SC_R_I2C_0 = 96, + IMX_SC_R_I2C_1 = 97, + IMX_SC_R_I2C_2 = 98, + IMX_SC_R_I2C_3 = 99, + IMX_SC_R_I2C_4 = 100, + IMX_SC_R_ADC_0 = 101, + IMX_SC_R_ADC_1 = 102, + IMX_SC_R_FTM_0 = 103, + IMX_SC_R_FTM_1 = 104, + IMX_SC_R_CAN_0 = 105, + IMX_SC_R_CAN_1 = 106, + IMX_SC_R_CAN_2 = 107, + IMX_SC_R_DMA_1_CH0 = 108, + IMX_SC_R_DMA_1_CH1 = 109, + IMX_SC_R_DMA_1_CH2 = 110, + IMX_SC_R_DMA_1_CH3 = 111, + IMX_SC_R_DMA_1_CH4 = 112, + IMX_SC_R_DMA_1_CH5 = 113, + IMX_SC_R_DMA_1_CH6 = 114, + IMX_SC_R_DMA_1_CH7 = 115, + IMX_SC_R_DMA_1_CH8 = 116, + IMX_SC_R_DMA_1_CH9 = 117, + IMX_SC_R_DMA_1_CH10 = 118, + IMX_SC_R_DMA_1_CH11 = 119, + IMX_SC_R_DMA_1_CH12 = 120, + IMX_SC_R_DMA_1_CH13 = 121, + IMX_SC_R_DMA_1_CH14 = 122, + IMX_SC_R_DMA_1_CH15 = 123, + IMX_SC_R_DMA_1_CH16 = 124, + IMX_SC_R_DMA_1_CH17 = 125, + IMX_SC_R_DMA_1_CH18 = 126, + IMX_SC_R_DMA_1_CH19 = 127, + IMX_SC_R_DMA_1_CH20 = 128, + IMX_SC_R_DMA_1_CH21 = 129, + IMX_SC_R_DMA_1_CH22 = 130, + IMX_SC_R_DMA_1_CH23 = 131, + IMX_SC_R_DMA_1_CH24 = 132, + IMX_SC_R_DMA_1_CH25 = 133, + IMX_SC_R_DMA_1_CH26 = 134, + IMX_SC_R_DMA_1_CH27 = 135, + IMX_SC_R_DMA_1_CH28 = 136, + IMX_SC_R_DMA_1_CH29 = 137, + IMX_SC_R_DMA_1_CH30 = 138, + IMX_SC_R_DMA_1_CH31 = 139, + IMX_SC_R_UNUSED1 = 140, + IMX_SC_R_UNUSED2 = 141, + IMX_SC_R_UNUSED3 = 142, + IMX_SC_R_UNUSED4 = 143, + IMX_SC_R_GPU_0_PID0 = 144, + IMX_SC_R_GPU_0_PID1 = 145, + IMX_SC_R_GPU_0_PID2 = 146, + IMX_SC_R_GPU_0_PID3 = 147, + IMX_SC_R_GPU_1_PID0 = 148, + IMX_SC_R_GPU_1_PID1 = 149, + IMX_SC_R_GPU_1_PID2 = 150, + IMX_SC_R_GPU_1_PID3 = 151, + IMX_SC_R_PCIE_A = 152, + IMX_SC_R_SERDES_0 = 153, + IMX_SC_R_MATCH_0 = 154, + IMX_SC_R_MATCH_1 = 155, + IMX_SC_R_MATCH_2 = 156, + IMX_SC_R_MATCH_3 = 157, + IMX_SC_R_MATCH_4 = 158, + IMX_SC_R_MATCH_5 = 159, + IMX_SC_R_MATCH_6 = 160, + IMX_SC_R_MATCH_7 = 161, + IMX_SC_R_MATCH_8 = 162, + IMX_SC_R_MATCH_9 = 163, + IMX_SC_R_MATCH_10 = 164, + IMX_SC_R_MATCH_11 = 165, + IMX_SC_R_MATCH_12 = 166, + IMX_SC_R_MATCH_13 = 167, + IMX_SC_R_MATCH_14 = 168, + IMX_SC_R_PCIE_B = 169, + IMX_SC_R_SATA_0 = 170, + IMX_SC_R_SERDES_1 = 171, + IMX_SC_R_HSIO_GPIO = 172, + IMX_SC_R_MATCH_15 = 173, + IMX_SC_R_MATCH_16 = 174, + IMX_SC_R_MATCH_17 = 175, + IMX_SC_R_MATCH_18 = 176, + IMX_SC_R_MATCH_19 = 177, + IMX_SC_R_MATCH_20 = 178, + IMX_SC_R_MATCH_21 = 179, + IMX_SC_R_MATCH_22 = 180, + IMX_SC_R_MATCH_23 = 181, + IMX_SC_R_MATCH_24 = 182, + IMX_SC_R_MATCH_25 = 183, + IMX_SC_R_MATCH_26 = 184, + IMX_SC_R_MATCH_27 = 185, + IMX_SC_R_MATCH_28 = 186, + IMX_SC_R_LCD_0 = 187, + IMX_SC_R_LCD_0_PWM_0 = 188, + IMX_SC_R_LCD_0_I2C_0 = 189, + IMX_SC_R_LCD_0_I2C_1 = 190, + IMX_SC_R_PWM_0 = 191, + IMX_SC_R_PWM_1 = 192, + IMX_SC_R_PWM_2 = 193, + IMX_SC_R_PWM_3 = 194, + IMX_SC_R_PWM_4 = 195, + IMX_SC_R_PWM_5 = 196, + IMX_SC_R_PWM_6 = 197, + IMX_SC_R_PWM_7 = 198, + IMX_SC_R_GPIO_0 = 199, + IMX_SC_R_GPIO_1 = 200, + IMX_SC_R_GPIO_2 = 201, + IMX_SC_R_GPIO_3 = 202, + IMX_SC_R_GPIO_4 = 203, + IMX_SC_R_GPIO_5 = 204, + IMX_SC_R_GPIO_6 = 205, + IMX_SC_R_GPIO_7 = 206, + IMX_SC_R_GPT_0 = 207, + IMX_SC_R_GPT_1 = 208, + IMX_SC_R_GPT_2 = 209, + IMX_SC_R_GPT_3 = 210, + IMX_SC_R_GPT_4 = 211, + IMX_SC_R_KPP = 212, + IMX_SC_R_MU_0A = 213, + IMX_SC_R_MU_1A = 214, + IMX_SC_R_MU_2A = 215, + IMX_SC_R_MU_3A = 216, + IMX_SC_R_MU_4A = 217, + IMX_SC_R_MU_5A = 218, + IMX_SC_R_MU_6A = 219, + IMX_SC_R_MU_7A = 220, + IMX_SC_R_MU_8A = 221, + IMX_SC_R_MU_9A = 222, + IMX_SC_R_MU_10A = 223, + IMX_SC_R_MU_11A = 224, + IMX_SC_R_MU_12A = 225, + IMX_SC_R_MU_13A = 226, + IMX_SC_R_MU_5B = 227, + IMX_SC_R_MU_6B = 228, + IMX_SC_R_MU_7B = 229, + IMX_SC_R_MU_8B = 230, + IMX_SC_R_MU_9B = 231, + IMX_SC_R_MU_10B = 232, + IMX_SC_R_MU_11B = 233, + IMX_SC_R_MU_12B = 234, + IMX_SC_R_MU_13B = 235, + IMX_SC_R_ROM_0 = 236, + IMX_SC_R_FSPI_0 = 237, + IMX_SC_R_FSPI_1 = 238, + IMX_SC_R_IEE = 239, + IMX_SC_R_IEE_R0 = 240, + IMX_SC_R_IEE_R1 = 241, + IMX_SC_R_IEE_R2 = 242, + IMX_SC_R_IEE_R3 = 243, + IMX_SC_R_IEE_R4 = 244, + IMX_SC_R_IEE_R5 = 245, + IMX_SC_R_IEE_R6 = 246, + IMX_SC_R_IEE_R7 = 247, + IMX_SC_R_SDHC_0 = 248, + IMX_SC_R_SDHC_1 = 249, + IMX_SC_R_SDHC_2 = 250, + IMX_SC_R_ENET_0 = 251, + IMX_SC_R_ENET_1 = 252, + IMX_SC_R_MLB_0 = 253, + IMX_SC_R_DMA_2_CH0 = 254, + IMX_SC_R_DMA_2_CH1 = 255, + IMX_SC_R_DMA_2_CH2 = 256, + IMX_SC_R_DMA_2_CH3 = 257, + IMX_SC_R_DMA_2_CH4 = 258, + IMX_SC_R_USB_0 = 259, + IMX_SC_R_USB_1 = 260, + IMX_SC_R_USB_0_PHY = 261, + IMX_SC_R_USB_2 = 262, + IMX_SC_R_USB_2_PHY = 263, + IMX_SC_R_DTCP = 264, + IMX_SC_R_NAND = 265, + IMX_SC_R_LVDS_0 = 266, + IMX_SC_R_LVDS_0_PWM_0 = 267, + IMX_SC_R_LVDS_0_I2C_0 = 268, + IMX_SC_R_LVDS_0_I2C_1 = 269, + IMX_SC_R_LVDS_1 = 270, + IMX_SC_R_LVDS_1_PWM_0 = 271, + IMX_SC_R_LVDS_1_I2C_0 = 272, + IMX_SC_R_LVDS_1_I2C_1 = 273, + IMX_SC_R_LVDS_2 = 274, + IMX_SC_R_LVDS_2_PWM_0 = 275, + IMX_SC_R_LVDS_2_I2C_0 = 276, + IMX_SC_R_LVDS_2_I2C_1 = 277, + IMX_SC_R_M4_0_PID0 = 278, + IMX_SC_R_M4_0_PID1 = 279, + IMX_SC_R_M4_0_PID2 = 280, + IMX_SC_R_M4_0_PID3 = 281, + IMX_SC_R_M4_0_PID4 = 282, + IMX_SC_R_M4_0_RGPIO = 283, + IMX_SC_R_M4_0_SEMA42 = 284, + IMX_SC_R_M4_0_TPM = 285, + IMX_SC_R_M4_0_PIT = 286, + IMX_SC_R_M4_0_UART = 287, + IMX_SC_R_M4_0_I2C = 288, + IMX_SC_R_M4_0_INTMUX = 289, + IMX_SC_R_M4_0_SIM = 290, + IMX_SC_R_M4_0_WDOG = 291, + IMX_SC_R_M4_0_MU_0B = 292, + IMX_SC_R_M4_0_MU_0A0 = 293, + IMX_SC_R_M4_0_MU_0A1 = 294, + IMX_SC_R_M4_0_MU_0A2 = 295, + IMX_SC_R_M4_0_MU_0A3 = 296, + IMX_SC_R_M4_0_MU_1A = 297, + IMX_SC_R_M4_1_PID0 = 298, + IMX_SC_R_M4_1_PID1 = 299, + IMX_SC_R_M4_1_PID2 = 300, + IMX_SC_R_M4_1_PID3 = 301, + IMX_SC_R_M4_1_PID4 = 302, + IMX_SC_R_M4_1_RGPIO = 303, + IMX_SC_R_M4_1_SEMA42 = 304, + IMX_SC_R_M4_1_TPM = 305, + IMX_SC_R_M4_1_PIT = 306, + IMX_SC_R_M4_1_UART = 307, + IMX_SC_R_M4_1_I2C = 308, + IMX_SC_R_M4_1_INTMUX = 309, + IMX_SC_R_M4_1_SIM = 310, + IMX_SC_R_M4_1_WDOG = 311, + IMX_SC_R_M4_1_MU_0B = 312, + IMX_SC_R_M4_1_MU_0A0 = 313, + IMX_SC_R_M4_1_MU_0A1 = 314, + IMX_SC_R_M4_1_MU_0A2 = 315, + IMX_SC_R_M4_1_MU_0A3 = 316, + IMX_SC_R_M4_1_MU_1A = 317, + IMX_SC_R_SAI_0 = 318, + IMX_SC_R_SAI_1 = 319, + IMX_SC_R_SAI_2 = 320, + IMX_SC_R_IRQSTR_SCU2 = 321, + IMX_SC_R_IRQSTR_DSP = 322, + IMX_SC_R_UNUSED5 = 323, + IMX_SC_R_UNUSED6 = 324, + IMX_SC_R_AUDIO_PLL_0 = 325, + IMX_SC_R_PI_0 = 326, + IMX_SC_R_PI_0_PWM_0 = 327, + IMX_SC_R_PI_0_PWM_1 = 328, + IMX_SC_R_PI_0_I2C_0 = 329, + IMX_SC_R_PI_0_PLL = 330, + IMX_SC_R_PI_1 = 331, + IMX_SC_R_PI_1_PWM_0 = 332, + IMX_SC_R_PI_1_PWM_1 = 333, + IMX_SC_R_PI_1_I2C_0 = 334, + IMX_SC_R_PI_1_PLL = 335, + IMX_SC_R_SC_PID0 = 336, + IMX_SC_R_SC_PID1 = 337, + IMX_SC_R_SC_PID2 = 338, + IMX_SC_R_SC_PID3 = 339, + IMX_SC_R_SC_PID4 = 340, + IMX_SC_R_SC_SEMA42 = 341, + IMX_SC_R_SC_TPM = 342, + IMX_SC_R_SC_PIT = 343, + IMX_SC_R_SC_UART = 344, + IMX_SC_R_SC_I2C = 345, + IMX_SC_R_SC_MU_0B = 346, + IMX_SC_R_SC_MU_0A0 = 347, + IMX_SC_R_SC_MU_0A1 = 348, + IMX_SC_R_SC_MU_0A2 = 349, + IMX_SC_R_SC_MU_0A3 = 350, + IMX_SC_R_SC_MU_1A = 351, + IMX_SC_R_SYSCNT_RD = 352, + IMX_SC_R_SYSCNT_CMP = 353, + IMX_SC_R_DEBUG = 354, + IMX_SC_R_SYSTEM = 355, + IMX_SC_R_SNVS = 356, + IMX_SC_R_OTP = 357, + IMX_SC_R_VPU_PID0 = 358, + IMX_SC_R_VPU_PID1 = 359, + IMX_SC_R_VPU_PID2 = 360, + IMX_SC_R_VPU_PID3 = 361, + IMX_SC_R_VPU_PID4 = 362, + IMX_SC_R_VPU_PID5 = 363, + IMX_SC_R_VPU_PID6 = 364, + IMX_SC_R_VPU_PID7 = 365, + IMX_SC_R_VPU_UART = 366, + IMX_SC_R_VPUCORE = 367, + IMX_SC_R_VPUCORE_0 = 368, + IMX_SC_R_VPUCORE_1 = 369, + IMX_SC_R_VPUCORE_2 = 370, + IMX_SC_R_VPUCORE_3 = 371, + IMX_SC_R_DMA_4_CH0 = 372, + IMX_SC_R_DMA_4_CH1 = 373, + IMX_SC_R_DMA_4_CH2 = 374, + IMX_SC_R_DMA_4_CH3 = 375, + IMX_SC_R_DMA_4_CH4 = 376, + IMX_SC_R_ISI_CH0 = 377, + IMX_SC_R_ISI_CH1 = 378, + IMX_SC_R_ISI_CH2 = 379, + IMX_SC_R_ISI_CH3 = 380, + IMX_SC_R_ISI_CH4 = 381, + IMX_SC_R_ISI_CH5 = 382, + IMX_SC_R_ISI_CH6 = 383, + IMX_SC_R_ISI_CH7 = 384, + IMX_SC_R_MJPEG_DEC_S0 = 385, + IMX_SC_R_MJPEG_DEC_S1 = 386, + IMX_SC_R_MJPEG_DEC_S2 = 387, + IMX_SC_R_MJPEG_DEC_S3 = 388, + IMX_SC_R_MJPEG_ENC_S0 = 389, + IMX_SC_R_MJPEG_ENC_S1 = 390, + IMX_SC_R_MJPEG_ENC_S2 = 391, + IMX_SC_R_MJPEG_ENC_S3 = 392, + IMX_SC_R_MIPI_0 = 393, + IMX_SC_R_MIPI_0_PWM_0 = 394, + IMX_SC_R_MIPI_0_I2C_0 = 395, + IMX_SC_R_MIPI_0_I2C_1 = 396, + IMX_SC_R_MIPI_1 = 397, + IMX_SC_R_MIPI_1_PWM_0 = 398, + IMX_SC_R_MIPI_1_I2C_0 = 399, + IMX_SC_R_MIPI_1_I2C_1 = 400, + IMX_SC_R_CSI_0 = 401, + IMX_SC_R_CSI_0_PWM_0 = 402, + IMX_SC_R_CSI_0_I2C_0 = 403, + IMX_SC_R_CSI_1 = 404, + IMX_SC_R_CSI_1_PWM_0 = 405, + IMX_SC_R_CSI_1_I2C_0 = 406, + IMX_SC_R_HDMI = 407, + IMX_SC_R_HDMI_I2S = 408, + IMX_SC_R_HDMI_I2C_0 = 409, + IMX_SC_R_HDMI_PLL_0 = 410, + IMX_SC_R_HDMI_RX = 411, + IMX_SC_R_HDMI_RX_BYPASS = 412, + IMX_SC_R_HDMI_RX_I2C_0 = 413, + IMX_SC_R_ASRC_0 = 414, + IMX_SC_R_ESAI_0 = 415, + IMX_SC_R_SPDIF_0 = 416, + IMX_SC_R_SPDIF_1 = 417, + IMX_SC_R_SAI_3 = 418, + IMX_SC_R_SAI_4 = 419, + IMX_SC_R_SAI_5 = 420, + IMX_SC_R_GPT_5 = 421, + IMX_SC_R_GPT_6 = 422, + IMX_SC_R_GPT_7 = 423, + IMX_SC_R_GPT_8 = 424, + IMX_SC_R_GPT_9 = 425, + IMX_SC_R_GPT_10 = 426, + IMX_SC_R_DMA_2_CH5 = 427, + IMX_SC_R_DMA_2_CH6 = 428, + IMX_SC_R_DMA_2_CH7 = 429, + IMX_SC_R_DMA_2_CH8 = 430, + IMX_SC_R_DMA_2_CH9 = 431, + IMX_SC_R_DMA_2_CH10 = 432, + IMX_SC_R_DMA_2_CH11 = 433, + IMX_SC_R_DMA_2_CH12 = 434, + IMX_SC_R_DMA_2_CH13 = 435, + IMX_SC_R_DMA_2_CH14 = 436, + IMX_SC_R_DMA_2_CH15 = 437, + IMX_SC_R_DMA_2_CH16 = 438, + IMX_SC_R_DMA_2_CH17 = 439, + IMX_SC_R_DMA_2_CH18 = 440, + IMX_SC_R_DMA_2_CH19 = 441, + IMX_SC_R_DMA_2_CH20 = 442, + IMX_SC_R_DMA_2_CH21 = 443, + IMX_SC_R_DMA_2_CH22 = 444, + IMX_SC_R_DMA_2_CH23 = 445, + IMX_SC_R_DMA_2_CH24 = 446, + IMX_SC_R_DMA_2_CH25 = 447, + IMX_SC_R_DMA_2_CH26 = 448, + IMX_SC_R_DMA_2_CH27 = 449, + IMX_SC_R_DMA_2_CH28 = 450, + IMX_SC_R_DMA_2_CH29 = 451, + IMX_SC_R_DMA_2_CH30 = 452, + IMX_SC_R_DMA_2_CH31 = 453, + IMX_SC_R_ASRC_1 = 454, + IMX_SC_R_ESAI_1 = 455, + IMX_SC_R_SAI_6 = 456, + IMX_SC_R_SAI_7 = 457, + IMX_SC_R_AMIX = 458, + IMX_SC_R_MQS_0 = 459, + IMX_SC_R_DMA_3_CH0 = 460, + IMX_SC_R_DMA_3_CH1 = 461, + IMX_SC_R_DMA_3_CH2 = 462, + IMX_SC_R_DMA_3_CH3 = 463, + IMX_SC_R_DMA_3_CH4 = 464, + IMX_SC_R_DMA_3_CH5 = 465, + IMX_SC_R_DMA_3_CH6 = 466, + IMX_SC_R_DMA_3_CH7 = 467, + IMX_SC_R_DMA_3_CH8 = 468, + IMX_SC_R_DMA_3_CH9 = 469, + IMX_SC_R_DMA_3_CH10 = 470, + IMX_SC_R_DMA_3_CH11 = 471, + IMX_SC_R_DMA_3_CH12 = 472, + IMX_SC_R_DMA_3_CH13 = 473, + IMX_SC_R_DMA_3_CH14 = 474, + IMX_SC_R_DMA_3_CH15 = 475, + IMX_SC_R_DMA_3_CH16 = 476, + IMX_SC_R_DMA_3_CH17 = 477, + IMX_SC_R_DMA_3_CH18 = 478, + IMX_SC_R_DMA_3_CH19 = 479, + IMX_SC_R_DMA_3_CH20 = 480, + IMX_SC_R_DMA_3_CH21 = 481, + IMX_SC_R_DMA_3_CH22 = 482, + IMX_SC_R_DMA_3_CH23 = 483, + IMX_SC_R_DMA_3_CH24 = 484, + IMX_SC_R_DMA_3_CH25 = 485, + IMX_SC_R_DMA_3_CH26 = 486, + IMX_SC_R_DMA_3_CH27 = 487, + IMX_SC_R_DMA_3_CH28 = 488, + IMX_SC_R_DMA_3_CH29 = 489, + IMX_SC_R_DMA_3_CH30 = 490, + IMX_SC_R_DMA_3_CH31 = 491, + IMX_SC_R_AUDIO_PLL_1 = 492, + IMX_SC_R_AUDIO_CLK_0 = 493, + IMX_SC_R_AUDIO_CLK_1 = 494, + IMX_SC_R_MCLK_OUT_0 = 495, + IMX_SC_R_MCLK_OUT_1 = 496, + IMX_SC_R_PMIC_0 = 497, + IMX_SC_R_PMIC_1 = 498, + IMX_SC_R_SECO = 499, + IMX_SC_R_CAAM_JR1 = 500, + IMX_SC_R_CAAM_JR2 = 501, + IMX_SC_R_CAAM_JR3 = 502, + IMX_SC_R_SECO_MU_2 = 503, + IMX_SC_R_SECO_MU_3 = 504, + IMX_SC_R_SECO_MU_4 = 505, + IMX_SC_R_HDMI_RX_PWM_0 = 506, + IMX_SC_R_A35 = 507, + IMX_SC_R_A35_0 = 508, + IMX_SC_R_A35_1 = 509, + IMX_SC_R_A35_2 = 510, + IMX_SC_R_A35_3 = 511, + IMX_SC_R_DSP = 512, + IMX_SC_R_DSP_RAM = 513, + IMX_SC_R_CAAM_JR1_OUT = 514, + IMX_SC_R_CAAM_JR2_OUT = 515, + IMX_SC_R_CAAM_JR3_OUT = 516, + IMX_SC_R_VPU_DEC_0 = 517, + IMX_SC_R_VPU_ENC_0 = 518, + IMX_SC_R_CAAM_JR0 = 519, + IMX_SC_R_CAAM_JR0_OUT = 520, + IMX_SC_R_PMIC_2 = 521, + IMX_SC_R_DBLOGIC = 522, + IMX_SC_R_HDMI_PLL_1 = 523, + IMX_SC_R_BOARD_R0 = 524, + IMX_SC_R_BOARD_R1 = 525, + IMX_SC_R_BOARD_R2 = 526, + IMX_SC_R_BOARD_R3 = 527, + IMX_SC_R_BOARD_R4 = 528, + IMX_SC_R_BOARD_R5 = 529, + IMX_SC_R_BOARD_R6 = 530, + IMX_SC_R_BOARD_R7 = 531, + IMX_SC_R_MJPEG_DEC_MP = 532, + IMX_SC_R_MJPEG_ENC_MP = 533, + IMX_SC_R_VPU_TS_0 = 534, + IMX_SC_R_VPU_MU_0 = 535, + IMX_SC_R_VPU_MU_1 = 536, + IMX_SC_R_VPU_MU_2 = 537, + IMX_SC_R_VPU_MU_3 = 538, + IMX_SC_R_VPU_ENC_1 = 539, + IMX_SC_R_VPU = 540, + IMX_SC_R_LAST +}; + +/* NOTE - please add by replacing some of the UNUSED from above! */ + +/* + * This type is used to indicate a control. + */ +enum imx_sc_ctrl { + IMX_SC_C_TEMP = 0, + IMX_SC_C_TEMP_HI = 1, + IMX_SC_C_TEMP_LOW = 2, + IMX_SC_C_PXL_LINK_MST1_ADDR = 3, + IMX_SC_C_PXL_LINK_MST2_ADDR = 4, + IMX_SC_C_PXL_LINK_MST_ENB = 5, + IMX_SC_C_PXL_LINK_MST1_ENB = 6, + IMX_SC_C_PXL_LINK_MST2_ENB = 7, + IMX_SC_C_PXL_LINK_SLV1_ADDR = 8, + IMX_SC_C_PXL_LINK_SLV2_ADDR = 9, + IMX_SC_C_PXL_LINK_MST_VLD = 10, + IMX_SC_C_PXL_LINK_MST1_VLD = 11, + IMX_SC_C_PXL_LINK_MST2_VLD = 12, + IMX_SC_C_SINGLE_MODE = 13, + IMX_SC_C_ID = 14, + IMX_SC_C_PXL_CLK_POLARITY = 15, + IMX_SC_C_LINESTATE = 16, + IMX_SC_C_PCIE_G_RST = 17, + IMX_SC_C_PCIE_BUTTON_RST = 18, + IMX_SC_C_PCIE_PERST = 19, + IMX_SC_C_PHY_RESET = 20, + IMX_SC_C_PXL_LINK_RATE_CORRECTION = 21, + IMX_SC_C_PANIC = 22, + IMX_SC_C_PRIORITY_GROUP = 23, + IMX_SC_C_TXCLK = 24, + IMX_SC_C_CLKDIV = 25, + IMX_SC_C_DISABLE_50 = 26, + IMX_SC_C_DISABLE_125 = 27, + IMX_SC_C_SEL_125 = 28, + IMX_SC_C_MODE = 29, + IMX_SC_C_SYNC_CTRL0 = 30, + IMX_SC_C_KACHUNK_CNT = 31, + IMX_SC_C_KACHUNK_SEL = 32, + IMX_SC_C_SYNC_CTRL1 = 33, + IMX_SC_C_DPI_RESET = 34, + IMX_SC_C_MIPI_RESET = 35, + IMX_SC_C_DUAL_MODE = 36, + IMX_SC_C_VOLTAGE = 37, + IMX_SC_C_PXL_LINK_SEL = 38, + IMX_SC_C_OFS_SEL = 39, + IMX_SC_C_OFS_AUDIO = 40, + IMX_SC_C_OFS_PERIPH = 41, + IMX_SC_C_OFS_IRQ = 42, + IMX_SC_C_RST0 = 43, + IMX_SC_C_RST1 = 44, + IMX_SC_C_SEL0 = 45, + IMX_SC_C_LAST +}; + +#endif /* _SC_TYPES_H */ diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h @@ -17,6 +17,7 @@ enum { SM_EFUSE_READ, SM_EFUSE_WRITE, SM_EFUSE_USER_MAX, + SM_GET_CHIP_ID, }; struct meson_sm_firmware; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h @@ -0,0 +1,116 @@ +/* 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_H__ +#define __FIRMWARE_ZYNQMP_H__ + +#define ZYNQMP_PM_VERSION_MAJOR 1 +#define ZYNQMP_PM_VERSION_MINOR 0 + +#define ZYNQMP_PM_VERSION ((ZYNQMP_PM_VERSION_MAJOR << 16) | \ + ZYNQMP_PM_VERSION_MINOR) + +#define ZYNQMP_TZ_VERSION_MAJOR 1 +#define ZYNQMP_TZ_VERSION_MINOR 0 + +#define ZYNQMP_TZ_VERSION ((ZYNQMP_TZ_VERSION_MAJOR << 16) | \ + ZYNQMP_TZ_VERSION_MINOR) + +/* SMC SIP service Call Function Identifier Prefix */ +#define PM_SIP_SVC 0xC2000000 +#define PM_GET_TRUSTZONE_VERSION 0xa03 + +/* Number of 32bits values in payload */ +#define PAYLOAD_ARG_CNT 4U + +enum pm_api_id { + PM_GET_API_VERSION = 1, + PM_IOCTL = 34, + PM_QUERY_DATA, + PM_CLOCK_ENABLE, + PM_CLOCK_DISABLE, + PM_CLOCK_GETSTATE, + PM_CLOCK_SETDIVIDER, + PM_CLOCK_GETDIVIDER, + PM_CLOCK_SETRATE, + PM_CLOCK_GETRATE, + PM_CLOCK_SETPARENT, + PM_CLOCK_GETPARENT, +}; + +/* PMU-FW return status codes */ +enum pm_ret_status { + XST_PM_SUCCESS = 0, + XST_PM_INTERNAL = 2000, + XST_PM_CONFLICT, + XST_PM_NO_ACCESS, + XST_PM_INVALID_NODE, + XST_PM_DOUBLE_REQ, + XST_PM_ABORT_SUSPEND, +}; + +enum pm_ioctl_id { + IOCTL_SET_PLL_FRAC_MODE = 8, + IOCTL_GET_PLL_FRAC_MODE, + IOCTL_SET_PLL_FRAC_DATA, + IOCTL_GET_PLL_FRAC_DATA, +}; + +enum pm_query_id { + PM_QID_INVALID, + PM_QID_CLOCK_GET_NAME, + PM_QID_CLOCK_GET_TOPOLOGY, + PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, + PM_QID_CLOCK_GET_PARENTS, + PM_QID_CLOCK_GET_ATTRIBUTES, + PM_QID_CLOCK_GET_NUM_CLOCKS = 12, +}; + +/** + * struct zynqmp_pm_query_data - PM query data + * @qid: query ID + * @arg1: Argument 1 of query data + * @arg2: Argument 2 of query data + * @arg3: Argument 3 of query data + */ +struct zynqmp_pm_query_data { + u32 qid; + u32 arg1; + u32 arg2; + u32 arg3; +}; + +struct zynqmp_eemi_ops { + int (*get_api_version)(u32 *version); + int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out); + int (*clock_enable)(u32 clock_id); + int (*clock_disable)(u32 clock_id); + int (*clock_getstate)(u32 clock_id, u32 *state); + int (*clock_setdivider)(u32 clock_id, u32 divider); + int (*clock_getdivider)(u32 clock_id, u32 *divider); + int (*clock_setrate)(u32 clock_id, u64 rate); + int (*clock_getrate)(u32 clock_id, u64 *rate); + int (*clock_setparent)(u32 clock_id, u32 parent_id); + int (*clock_getparent)(u32 clock_id, u32 *parent_id); + int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out); +}; + +#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) +const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); +#else +static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) +{ + return NULL; +} +#endif + +#endif /* __FIRMWARE_ZYNQMP_H__ */ diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h @@ -46,7 +46,6 @@ struct sysc_regbits { s8 emufree_shift; }; -#define SYSC_QUIRK_RESOURCE_PROVIDER BIT(9) #define SYSC_QUIRK_LEGACY_IDLE BIT(8) #define SYSC_QUIRK_RESET_STATUS BIT(7) #define SYSC_QUIRK_NO_IDLE_ON_INIT BIT(6) diff --git a/include/linux/reset.h b/include/linux/reset.h @@ -116,7 +116,7 @@ static inline int device_reset_optional(struct device *dev) * @id: reset line name * * Returns a struct reset_control or IS_ERR() condition containing errno. - * If this function is called more then once for the same reset_control it will + * If this function is called more than once for the same reset_control it will * return -EBUSY. * * See reset_control_get_shared for details on shared references to diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h @@ -91,6 +91,8 @@ struct scmi_clk_ops { * to sustained performance level mapping * @freq_get: gets the frequency for a given device using sustained frequency * to sustained performance level mapping + * @est_power_get: gets the estimated power cost for a given performance domain + * at a given frequency */ struct scmi_perf_ops { int (*limits_set)(const struct scmi_handle *handle, u32 domain, @@ -110,6 +112,8 @@ struct scmi_perf_ops { unsigned long rate, bool poll); int (*freq_get)(const struct scmi_handle *handle, u32 domain, unsigned long *rate, bool poll); + int (*est_power_get)(const struct scmi_handle *handle, u32 domain, + unsigned long *rate, unsigned long *power); }; /** diff --git a/include/linux/soc/amlogic/meson-canvas.h b/include/linux/soc/amlogic/meson-canvas.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + */ +#ifndef __SOC_MESON_CANVAS_H +#define __SOC_MESON_CANVAS_H + +#include <linux/kernel.h> + +#define MESON_CANVAS_WRAP_NONE 0x00 +#define MESON_CANVAS_WRAP_X 0x01 +#define MESON_CANVAS_WRAP_Y 0x02 + +#define MESON_CANVAS_BLKMODE_LINEAR 0x00 +#define MESON_CANVAS_BLKMODE_32x32 0x01 +#define MESON_CANVAS_BLKMODE_64x64 0x02 + +#define MESON_CANVAS_ENDIAN_SWAP16 0x1 +#define MESON_CANVAS_ENDIAN_SWAP32 0x3 +#define MESON_CANVAS_ENDIAN_SWAP64 0x7 +#define MESON_CANVAS_ENDIAN_SWAP128 0xf + +struct meson_canvas; + +/** + * meson_canvas_get() - get a canvas provider instance + * + * @dev: consumer device pointer + */ +struct meson_canvas *meson_canvas_get(struct device *dev); + +/** + * meson_canvas_alloc() - take ownership of a canvas + * + * @canvas: canvas provider instance retrieved from meson_canvas_get() + * @canvas_index: will be filled with the canvas ID + */ +int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index); + +/** + * meson_canvas_free() - remove ownership from a canvas + * + * @canvas: canvas provider instance retrieved from meson_canvas_get() + * @canvas_index: canvas ID that was obtained via meson_canvas_alloc() + */ +int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index); + +/** + * meson_canvas_config() - configure a canvas + * + * @canvas: canvas provider instance retrieved from meson_canvas_get() + * @canvas_index: canvas ID that was obtained via meson_canvas_alloc() + * @addr: physical address to the pixel buffer + * @stride: width of the buffer + * @height: height of the buffer + * @wrap: undocumented + * @blkmode: block mode (linear, 32x32, 64x64) + * @endian: byte swapping (swap16, swap32, swap64, swap128) + */ +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); + +#endif diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h @@ -70,25 +70,51 @@ struct llcc_slice_config { /** * llcc_drv_data - Data associated with the llcc driver * @regmap: regmap associated with the llcc device + * @bcast_regmap: regmap associated with llcc broadcast offset * @cfg: pointer to the data structure for slice configuration * @lock: mutex associated with each slice * @cfg_size: size of the config data table * @max_slices: max slices as read from device tree - * @bcast_off: Offset of the broadcast bank * @num_banks: Number of llcc banks * @bitmap: Bit map to track the active slice ids * @offsets: Pointer to the bank offsets array + * @ecc_irq: interrupt for llcc cache error detection and reporting */ struct llcc_drv_data { struct regmap *regmap; + struct regmap *bcast_regmap; const struct llcc_slice_config *cfg; struct mutex lock; u32 cfg_size; u32 max_slices; - u32 bcast_off; u32 num_banks; unsigned long *bitmap; u32 *offsets; + int ecc_irq; +}; + +/** + * llcc_edac_reg_data - llcc edac registers data for each error type + * @name: Name of the error + * @synd_reg: Syndrome register address + * @count_status_reg: Status register address to read the error count + * @ways_status_reg: Status register address to read the error ways + * @reg_cnt: Number of registers + * @count_mask: Mask value to get the error count + * @ways_mask: Mask value to get the error ways + * @count_shift: Shift value to get the error count + * @ways_shift: Shift value to get the error ways + */ +struct llcc_edac_reg_data { + char *name; + u64 synd_reg; + u64 count_status_reg; + u64 ways_status_reg; + u32 reg_cnt; + u32 count_mask; + u32 ways_mask; + u8 count_shift; + u8 ways_shift; }; #if IS_ENABLED(CONFIG_QCOM_LLCC) diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h @@ -453,6 +453,79 @@ static inline int tee_shm_get_id(struct tee_shm *shm) */ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id); +/** + * tee_client_open_context() - Open a TEE context + * @start: if not NULL, continue search after this context + * @match: function to check TEE device + * @data: data for match function + * @vers: if not NULL, version data of TEE device of the context returned + * + * This function does an operation similar to open("/dev/teeX") in user space. + * A returned context must be released with tee_client_close_context(). + * + * Returns a TEE context of the first TEE device matched by the match() + * callback or an ERR_PTR. + */ +struct tee_context * +tee_client_open_context(struct tee_context *start, + int (*match)(struct tee_ioctl_version_data *, + const void *), + const void *data, struct tee_ioctl_version_data *vers); + +/** + * tee_client_close_context() - Close a TEE context + * @ctx: TEE context to close + * + * Note that all sessions previously opened with this context will be + * closed when this function is called. + */ +void tee_client_close_context(struct tee_context *ctx); + +/** + * tee_client_get_version() - Query version of TEE + * @ctx: TEE context to TEE to query + * @vers: Pointer to version data + */ +void tee_client_get_version(struct tee_context *ctx, + struct tee_ioctl_version_data *vers); + +/** + * tee_client_open_session() - Open a session to a Trusted Application + * @ctx: TEE context + * @arg: Open session arguments, see description of + * struct tee_ioctl_open_session_arg + * @param: Parameters passed to the Trusted Application + * + * Returns < 0 on error else see @arg->ret for result. If @arg->ret + * is TEEC_SUCCESS the session identifier is available in @arg->session. + */ +int tee_client_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param); + +/** + * tee_client_close_session() - Close a session to a Trusted Application + * @ctx: TEE Context + * @session: Session id + * + * Return < 0 on error else 0, regardless the session will not be + * valid after this function has returned. + */ +int tee_client_close_session(struct tee_context *ctx, u32 session); + +/** + * tee_client_invoke_func() - Invoke a function in a Trusted Application + * @ctx: TEE Context + * @arg: Invoke arguments, see description of + * struct tee_ioctl_invoke_arg + * @param: Parameters passed to the Trusted Application + * + * Returns < 0 on error else see @arg->ret for result. + */ +int tee_client_invoke_func(struct tee_context *ctx, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param); + static inline bool tee_param_is_memref(struct tee_param *param) { switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { diff --git a/include/soc/fsl/qman.h b/include/soc/fsl/qman.h @@ -1194,4 +1194,32 @@ int qman_release_cgrid(u32 id); */ int qman_is_probed(void); +/** + * qman_dqrr_get_ithresh - Get coalesce interrupt threshold + * @portal: portal to get the value for + * @ithresh: threshold pointer + */ +void qman_dqrr_get_ithresh(struct qman_portal *portal, u8 *ithresh); + +/** + * qman_dqrr_set_ithresh - Set coalesce interrupt threshold + * @portal: portal to set the new value on + * @ithresh: new threshold value + */ +void qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh); + +/** + * qman_dqrr_get_iperiod - Get coalesce interrupt period + * @portal: portal to get the value for + * @iperiod: period pointer + */ +void qman_portal_get_iperiod(struct qman_portal *portal, u32 *iperiod); + +/** + * qman_dqrr_set_iperiod - Set coalesce interrupt period + * @portal: portal to set the new value on + * @ithresh: new period value + */ +void qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod); + #endif /* __FSL_QMAN_H */ diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h @@ -134,22 +134,13 @@ enum tegra_io_pad { TEGRA_IO_PAD_USB2, TEGRA_IO_PAD_USB3, TEGRA_IO_PAD_USB_BIAS, + TEGRA_IO_PAD_AO_HV, }; /* deprecated, use TEGRA_IO_PAD_{HDMI,LVDS} instead */ #define TEGRA_IO_RAIL_HDMI TEGRA_IO_PAD_HDMI #define TEGRA_IO_RAIL_LVDS TEGRA_IO_PAD_LVDS -/** - * enum tegra_io_pad_voltage - voltage level of the I/O pad's source rail - * @TEGRA_IO_PAD_1800000UV: 1.8 V - * @TEGRA_IO_PAD_3300000UV: 3.3 V - */ -enum tegra_io_pad_voltage { - TEGRA_IO_PAD_1800000UV, - TEGRA_IO_PAD_3300000UV, -}; - #ifdef CONFIG_SOC_TEGRA_PMC int tegra_powergate_is_powered(unsigned int id); int tegra_powergate_power_on(unsigned int id); @@ -162,9 +153,6 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, int tegra_io_pad_power_enable(enum tegra_io_pad id); int tegra_io_pad_power_disable(enum tegra_io_pad id); -int tegra_io_pad_set_voltage(enum tegra_io_pad id, - enum tegra_io_pad_voltage voltage); -int tegra_io_pad_get_voltage(enum tegra_io_pad id); /* deprecated, use tegra_io_pad_power_{enable,disable}() instead */ int tegra_io_rail_power_on(unsigned int id); @@ -212,12 +200,6 @@ static inline int tegra_io_pad_power_disable(enum tegra_io_pad id) return -ENOSYS; } -static inline int tegra_io_pad_set_voltage(enum tegra_io_pad id, - enum tegra_io_pad_voltage voltage) -{ - return -ENOSYS; -} - static inline int tegra_io_pad_get_voltage(enum tegra_io_pad id) { return -ENOSYS;