Commit 38c23685 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'armsoc-drivers' of git://

Pull ARM SoC driver updates from Arnd Bergmann:
 "The main addition this time around is the new ARM "SCMI" framework,
  which is the latest in a series of standards coming from ARM to do
  power management in a platform independent way.

  This has been through many review cycles, and it relies on a rather
  interesting way of using the mailbox subsystem, but in the end I
  agreed that Sudeep's version was the best we could do after all.

  Other changes include:

   - the ARM CCN driver is moved out of drivers/bus into drivers/perf,
     which makes more sense. Similarly, the performance monitoring
     portion of the CCI driver are moved the same way and cleaned up a
     little more.

   - a series of updates to the SCPI framework

   - support for the Mediatek mt7623a SoC in drivers/soc

   - support for additional NVIDIA Tegra hardware in drivers/soc

   - a new reset driver for Socionext Uniphier

   - lesser bug fixes in drivers/soc, drivers/tee, drivers/memory, and
     drivers/firmware and drivers/reset across platforms"

* tag 'armsoc-drivers' of git:// (87 commits)
  reset: uniphier: add ethernet reset control support for PXs3
  reset: stm32mp1: Enable stm32mp1 reset driver
  dt-bindings: reset: add STM32MP1 resets
  reset: uniphier: add Pro4/Pro5/PXs2 audio systems reset control
  reset: imx7: add 'depends on HAS_IOMEM' to fix unmet dependency
  reset: modify the way reset lookup works for board files
  reset: add support for non-DT systems
  clk: scmi: use devm_of_clk_add_hw_provider() API and drop scmi_clocks_remove
  firmware: arm_scmi: prevent accessing rate_discrete uninitialized
  hwmon: (scmi) return -EINVAL when sensor information is unavailable
  amlogic: meson-gx-socinfo: Update soc ids
  soc/tegra: pmc: Use the new reset APIs to manage reset controllers
  soc: mediatek: update power domain data of MT2712
  dt-bindings: soc: update MT2712 power dt-bindings
  cpufreq: scmi: add thermal dependency
  soc: mediatek: fix the mistaken pointer accessed when subdomains are added
  soc: mediatek: add SCPSYS power domain driver for MediaTek MT7623A SoC
  soc: mediatek: avoid hardcoded value with bus_prot_mask
  dt-bindings: soc: add header files required for MT7623A SCPSYS dt-binding
  dt-bindings: soc: add SCPSYS binding for MT7623 and MT7623A SoC
parents 16756934 7df3f0bb
System Control and Management Interface (SCMI) Message Protocol
The SCMI is intended to allow agents such as OSPM to manage various functions
that are provided by the hardware platform it is running on, including power
and performance functions.
This binding is intended to define the interface the firmware implementing
the SCMI as described in ARM document number ARM DUI 0922B ("ARM System Control
and Management Interface Platform Design Document")[0] provide for OSPM in
the device tree.
Required properties:
The scmi node with the following properties shall be under the /firmware/ node.
- compatible : shall be "arm,scmi"
- mboxes: List of phandle and mailbox channel specifiers. It should contain
exactly one or two mailboxes, one for transmitting messages("tx")
and another optional for receiving the notifications("rx") if
- shmem : List of phandle pointing to the shared memory(SHM) area as per
generic mailbox client binding.
- #address-cells : should be '1' if the device has sub-nodes, maps to
protocol identifier for a given sub-node.
- #size-cells : should be '0' as 'reg' property doesn't have any size
associated with it.
Optional properties:
- mbox-names: shall be "tx" or "rx" depending on mboxes entries.
See Documentation/devicetree/bindings/mailbox/mailbox.txt for more details
about the generic mailbox controller and client driver bindings.
The mailbox is the only permitted method of calling the SCMI firmware.
Mailbox doorbell is used as a mechanism to alert the presence of a
messages and/or notification.
Each protocol supported shall have a sub-node with corresponding compatible
as described in the following sections. If the platform supports dedicated
communication channel for a particular protocol, the 3 properties namely:
mboxes, mbox-names and shmem shall be present in the sub-node corresponding
to that protocol.
Clock/Performance bindings for the clocks/OPPs based on SCMI Message Protocol
This binding uses the common clock binding[1].
Required properties:
- #clock-cells : Should be 1. Contains the Clock ID value used by SCMI commands.
Power domain bindings for the power domains based on SCMI Message Protocol
This binding for the SCMI power domain providers uses the generic power
domain binding[2].
Required properties:
- #power-domain-cells : Should be 1. Contains the device or the power
domain ID value used by SCMI commands.
Sensor bindings for the sensors based on SCMI Message Protocol
SCMI provides an API to access the various sensors on the SoC.
Required properties:
- #thermal-sensor-cells: should be set to 1. This property follows the
thermal device tree bindings[3].
Valid cell values are raw identifiers (Sensor ID)
as used by the firmware. Refer to platform details
for your implementation for the IDs to use.
SRAM and Shared Memory for SCMI
A small area of SRAM is reserved for SCMI communication between application
processors and SCP.
The properties should follow the generic mmio-sram description found in [4]
Each sub-node represents the reserved area for SCMI.
Required sub-node properties:
- reg : The base offset and size of the reserved area with the SRAM
- compatible : should be "arm,scmi-shmem" for Non-secure SRAM based
shared memory
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Documentation/devicetree/bindings/power/power_domain.txt
[3] Documentation/devicetree/bindings/thermal/thermal.txt
[4] Documentation/devicetree/bindings/sram/sram.txt
sram@50000000 {
compatible = "mmio-sram";
reg = <0x0 0x50000000 0x0 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x0 0x50000000 0x10000>;
cpu_scp_lpri: scp-shmem@0 {
compatible = "arm,scmi-shmem";
reg = <0x0 0x200>;
cpu_scp_hpri: scp-shmem@200 {
compatible = "arm,scmi-shmem";
reg = <0x200 0x200>;
mailbox@40000000 {
#mbox-cells = <1>;
reg = <0x0 0x40000000 0x0 0x10000>;
firmware {
scmi {
compatible = "arm,scmi";
mboxes = <&mailbox 0 &mailbox 1>;
mbox-names = "tx", "rx";
shmem = <&cpu_scp_lpri &cpu_scp_hpri>;
#address-cells = <1>;
#size-cells = <0>;
scmi_devpd: protocol@11 {
reg = <0x11>;
#power-domain-cells = <1>;
scmi_dvfs: protocol@13 {
reg = <0x13>;
#clock-cells = <1>;
scmi_clk: protocol@14 {
reg = <0x14>;
#clock-cells = <1>;
scmi_sensors0: protocol@15 {
reg = <0x15>;
#thermal-sensor-cells = <1>;
cpu@0 {
reg = <0 0>;
clocks = <&scmi_dvfs 0>;
hdlcd@7ff60000 {
reg = <0 0x7ff60000 0 0x1000>;
clocks = <&scmi_clk 4>;
power-domains = <&scmi_devpd 1>;
thermal-zones {
soc_thermal {
polling-delay-passive = <100>;
polling-delay = <1000>;
/* sensor ID */
thermal-sensors = <&scmi_sensors0 3>;
......@@ -43,6 +43,12 @@ following properties:
- interrupt-parent: a phandle indicating which interrupt controller
this PMU signals interrupts to.
Optional nodes:
- nodes defining the restart and poweroff syscon children
Example :
pmu_system_controller: system-controller@10040000 {
compatible = "samsung,exynos5250-pmu", "syscon";
......@@ -23,6 +23,11 @@ Required property:
Optional property:
- mbox-names: List of identifier strings for each mailbox channel.
- shmem : List of phandle pointing to the shared memory(SHM) area between the
users of these mailboxes for IPC, one for each mailbox. This shared
memory can be part of any memory reserved for the purpose of this
communication between the mailbox client and the remote.
pwr_cntrl: power {
......@@ -30,3 +35,26 @@ Example:
mbox-names = "pwr-ctrl", "rpc";
mboxes = <&mailbox 0 &mailbox 1>;
Example with shared memory(shmem):
sram: sram@50000000 {
compatible = "mmio-sram";
reg = <0x50000000 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x50000000 0x10000>;
cl_shmem: shmem@0 {
compatible = "client-shmem";
reg = <0x0 0x200>;
client@2e000000 {
mboxes = <&mailbox 0>;
shmem = <&cl_shmem>;
......@@ -176,3 +176,24 @@ lhc: lhc@20 {
compatible = "aspeed,ast2500-lhc";
reg = <0x20 0x24 0x48 0x8>;
LPC reset control
The UARTs present in the ASPEED SoC can have their resets tied to the reset
state of the LPC bus. Some systems may chose to modify this configuration.
Required properties:
- compatible: "aspeed,ast2500-lpc-reset" or
- reg: offset and length of the IP in the LHC memory region
- #reset-controller indicates the number of reset cells expected
lpc_reset: reset-controller@18 {
compatible = "aspeed,ast2500-lpc-reset";
reg = <0x18 0x4>;
#reset-cells = <1>;
STMicroelectronics STM32MP1 Peripheral Reset Controller
The RCC IP is both a reset and a clock controller.
Please see Documentation/devicetree/bindings/clock/st,stm32mp1-rcc.txt
......@@ -21,6 +21,8 @@ Required properties:
- "mediatek,mt2712-scpsys"
- "mediatek,mt6797-scpsys"
- "mediatek,mt7622-scpsys"
- "mediatek,mt7623-scpsys", "mediatek,mt2701-scpsys": For MT7623 SoC
- "mediatek,mt7623a-scpsys": For MT7623A SoC
- "mediatek,mt8173-scpsys"
- #power-domain-cells: Must be 1
- reg: Address range of the SCPSYS unit
......@@ -28,10 +30,11 @@ Required properties:
- clock, clock-names: clocks according to the common clock binding.
These are clocks which hardware needs to be
enabled before enabling certain power domains.
Required clocks for MT2701: "mm", "mfg", "ethif"
Required clocks for MT2701 or MT7623: "mm", "mfg", "ethif"
Required clocks for MT2712: "mm", "mfg", "venc", "jpgdec", "audio", "vdec"
Required clocks for MT6797: "mm", "mfg", "vdec"
Required clocks for MT7622: "hif_sel"
Required clocks for MT7622A: "ethif"
Required clocks for MT8173: "mm", "mfg", "venc", "venc_lt"
Optional properties:
......@@ -13491,15 +13491,16 @@ T: git git://
S: Supported
F: drivers/mfd/syscon.c
M: Sudeep Holla <>
S: Maintained
F: Documentation/devicetree/bindings/arm/arm,scpi.txt
F: drivers/clk/clk-scpi.c
F: drivers/cpufreq/scpi-cpufreq.c
F: Documentation/devicetree/bindings/arm/arm,sc[mp]i.txt
F: drivers/clk/clk-sc[mp]i.c
F: drivers/cpufreq/sc[mp]i-cpufreq.c
F: drivers/firmware/arm_scpi.c
F: include/linux/scpi_protocol.h
F: drivers/firmware/arm_scmi/
F: include/linux/sc[mp]i_protocol.h
M: Sebastian Reichel <>
......@@ -8,25 +8,10 @@ menu "Bus devices"
config ARM_CCI
config ARM_CCI_PMU
select ARM_CCI
config ARM_CCI400_COMMON
select ARM_CCI
config ARM_CCI400_PMU
bool "ARM CCI400 PMU support"
depends on (ARM && CPU_V7) || ARM64
depends on PERF_EVENTS
select ARM_CCI400_COMMON
select ARM_CCI_PMU
Support for PMU events monitoring on the ARM CCI-400 (cache coherent
interconnect). CCI-400 supports counting events related to the
connected slave/master interfaces.
depends on ARM && OF && CPU_V7
......@@ -35,27 +20,6 @@ config ARM_CCI400_PORT_CTRL
Low level power management driver for CCI400 cache coherent
interconnect for ARM platforms.
config ARM_CCI5xx_PMU
bool "ARM CCI-500/CCI-550 PMU support"
depends on (ARM && CPU_V7) || ARM64
depends on PERF_EVENTS
select ARM_CCI_PMU
Support for PMU events monitoring on the ARM CCI-500/CCI-550 cache
coherent interconnects. Both of them provide 8 independent event counters,
which can count events pertaining to the slave/master interfaces as well
as the internal events to the CCI.
If unsure, say Y
config ARM_CCN
tristate "ARM CCN driver support"
depends on ARM || ARM64
depends on PERF_EVENTS
PMU (perf) driver supporting the ARM CCN (Cache Coherent Network)
bool "Broadcom STB GISB bus arbiter"
depends on ARM || ARM64 || MIPS
......@@ -5,8 +5,6 @@
# Interconnect bus drivers for ARM platforms
obj-$(CONFIG_ARM_CCI) += arm-cci.o
obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
# DPAA2 fsl-mc bus
This diff is collapsed.
......@@ -62,6 +62,16 @@ config COMMON_CLK_HI655X
multi-function device has one fixed-rate oscillator, clocked
at 32KHz.
tristate "Clock driver controlled via SCMI interface"
This driver provides support for clocks that are controlled
by firmware that implements the SCMI interface.
This driver uses SCMI Message Protocol to interact with the
firmware providing all the clock controls.
tristate "Clock driver controlled via SCPI interface"
......@@ -41,6 +41,7 @@ obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o
// SPDX-License-Identifier: GPL-2.0
* System Control and Power Interface (SCMI) Protocol based clock driver
* Copyright (C) 2018 ARM Ltd.
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/scmi_protocol.h>
#include <asm/div64.h>
struct scmi_clk {
u32 id;
struct clk_hw hw;
const struct scmi_clock_info *info;
const struct scmi_handle *handle;
#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
int ret;
u64 rate;
struct scmi_clk *clk = to_scmi_clk(hw);
ret = clk->handle->clk_ops->rate_get(clk->handle, clk->id, &rate);
if (ret)
return 0;
return rate;
static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
int step;
u64 fmin, fmax, ftmp;
struct scmi_clk *clk = to_scmi_clk(hw);
* We can't figure out what rate it will be, so just return the
* rate back to the caller. scmi_clk_recalc_rate() will be called
* after the rate is set and we'll know what rate the clock is
* running at then.
if (clk->info->rate_discrete)
return rate;
fmin = clk->info->range.min_rate;
fmax = clk->info->range.max_rate;
if (rate <= fmin)
return fmin;
else if (rate >= fmax)
return fmax;
ftmp = rate - fmin;
ftmp += clk->info->range.step_size - 1; /* to round up */
step = do_div(ftmp, clk->info->range.step_size);
return step * clk->info->range.step_size + fmin;
static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
struct scmi_clk *clk = to_scmi_clk(hw);
return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate);
static int scmi_clk_enable(struct clk_hw *hw)
struct scmi_clk *clk = to_scmi_clk(hw);
return clk->handle->clk_ops->enable(clk->handle, clk->id);
static void scmi_clk_disable(struct clk_hw *hw)
struct scmi_clk *clk = to_scmi_clk(hw);
clk->handle->clk_ops->disable(clk->handle, clk->id);
static const struct clk_ops scmi_clk_ops = {
.recalc_rate = scmi_clk_recalc_rate,
.round_rate = scmi_clk_round_rate,
.set_rate = scmi_clk_set_rate,
* We can't provide enable/disable callback as we can't perform the same
* in atomic context. Since the clock framework provides standard API
* clk_prepare_enable that helps cases using clk_enable in non-atomic
* context, it should be fine providing prepare/unprepare.
.prepare = scmi_clk_enable,
.unprepare = scmi_clk_disable,
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
int ret;
struct clk_init_data init = {
.num_parents = 0,
.ops = &scmi_clk_ops,
.name = sclk->info->name,
sclk->hw.init = &init;
ret = devm_clk_hw_register(dev, &sclk->hw);
if (!ret)
clk_hw_set_rate_range(&sclk->hw, sclk->info->range.min_rate,
return ret;
static int scmi_clocks_probe(struct scmi_device *sdev)
int idx, count, err;
struct clk_hw **hws;
struct clk_hw_onecell_data *clk_data;
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
const struct scmi_handle *handle = sdev->handle;
if (!handle || !handle->clk_ops)
return -ENODEV;
count = handle->clk_ops->count_get(handle);
if (count < 0) {
dev_err(dev, "%s: invalid clock output count\n", np->name);
return -EINVAL;
clk_data = devm_kzalloc(dev, sizeof(*clk_data) +
sizeof(*clk_data->hws) * count, GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->num = count;
hws = clk_data->hws;
for (idx = 0; idx < count; idx++) {
struct scmi_clk *sclk;
sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
if (!sclk)
return -ENOMEM;
sclk->info = handle->clk_ops->info_get(handle, idx);
if (!sclk->info) {
dev_dbg(dev, "invalid clock info for idx %d\n", idx);
sclk->id = idx;
sclk->handle = handle;
err = scmi_clk_ops_init(dev, sclk);
if (err) {
dev_err(dev, "failed to register clock %d\n", idx);
devm_kfree(dev, sclk);
hws[idx] = NULL;
} else {
dev_dbg(dev, "Registered clock:%s\n", sclk->info->name);
hws[idx] = &sclk->hw;
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
static const struct scmi_device_id scmi_id_table[] = {
{ },
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_clocks_driver = {
.name = "scmi-clocks",
.probe = scmi_clocks_probe,
.id_table = scmi_id_table,
MODULE_AUTHOR("Sudeep Holla <>");
......@@ -239,6 +239,18 @@ config ARM_SA1100_CPUFREQ
config ARM_SA1110_CPUFREQ
tristate "SCMI based CPUfreq driver"
depends on !CPU_THERMAL || THERMAL
select PM_OPP
This adds the CPUfreq driver support for ARM platforms using SCMI
protocol for CPU power management.
This driver uses SCMI Message Protocol driver to interact with the
firmware providing the CPU DVFS functionality.
bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR
......@@ -75,6 +75,7 @@ obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
obj-$(CONFIG_ARM_SCMI_CPUFREQ) += scmi-cpufreq.o
obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
// SPDX-License-Identifier: GPL-2.0
* System Control and Power Interface (SCMI) based CPUFreq Interface driver
* Copyright (C) 2018 ARM Ltd.
* Sudeep Holla <>
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cpu_cooling.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/scmi_protocol.h>
#include <linux/types.h>
struct scmi_data {
int domain_id;
struct device *cpu_dev;
struct thermal_cooling_device *cdev;
static const struct scmi_handle *handle;
static unsigned int scmi_cpufreq_get_rate(unsigned int cpu)
struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
struct scmi_perf_ops *perf_ops = handle->perf_ops;
struct scmi_data *priv = policy->driver_data;
unsigned long rate;
int ret;
ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false);
if (ret)
return 0;
return rate / 1000;
* perf_ops->freq_set is not a synchronous, the actual OPP change will
* happen asynchronously and can get notified if the events are
* subscribed for by the SCMI firmware
static int
scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
int ret;
struct scmi_data *priv = policy->driver_data;
struct scmi_perf_ops *perf_ops = handle->perf_ops;
u64 freq = policy->freq_table[index].frequency * 1000;
ret = perf_ops->freq_set(handle, priv->domain_id, freq, false);
if (!ret)
arch_set_freq_scale(policy->related_cpus, freq,
return ret;
static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
unsigned int target_freq)
struct scmi_data *priv = policy->driver_data;
struct scmi_perf_ops *perf_ops = handle->perf_ops;
if (!perf_ops->freq_set(handle, priv->domain_id,
target_freq * 1000, true)) {
arch_set_freq_scale(policy->related_cpus, target_freq,
return target_freq;
return 0;
static int
scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
int cpu, domain, tdomain;
struct device *tcpu_dev;
domain = handle->perf_ops->device_domain_id(cpu_dev);
if (domain < 0)
return domain;