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 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://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (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
  ...
This commit is contained in:
Linus Torvalds
2018-04-05 21:29:35 -07:00
79 changed files with 6861 additions and 2189 deletions
@@ -0,0 +1,179 @@
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
supported.
- 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
[0] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html
[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
Example:
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.
Example:
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
"aspeed,ast2400-lpc-reset"
- reg: offset and length of the IP in the LHC memory region
- #reset-controller indicates the number of reset cells expected
Example:
lpc_reset: reset-controller@18 {
compatible = "aspeed,ast2500-lpc-reset";
reg = <0x18 0x4>;
#reset-cells = <1>;
};
@@ -0,0 +1,6 @@
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:
+6 -5
View File
@@ -13491,15 +13491,16 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
S: Supported
F: drivers/mfd/syscon.c
SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers
SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE (SCPI/SCMI) Message Protocol drivers
M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-arm-kernel@lists.infradead.org
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
SYSTEM RESET/SHUTDOWN DRIVERS
M: Sebastian Reichel <sre@kernel.org>
-36
View File
@@ -8,25 +8,10 @@ menu "Bus devices"
config ARM_CCI
bool
config ARM_CCI_PMU
bool
select ARM_CCI
config ARM_CCI400_COMMON
bool
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
help
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.
config ARM_CCI400_PORT_CTRL
bool
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
help
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
help
PMU (perf) driver supporting the ARM CCN (Cache Coherent Network)
interconnect.
config BRCMSTB_GISB_ARB
bool "Broadcom STB GISB bus arbiter"
depends on ARM || ARM64 || MIPS
-2
View File
@@ -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
+14 -1749
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -62,6 +62,16 @@ config COMMON_CLK_HI655X
multi-function device has one fixed-rate oscillator, clocked
at 32KHz.
config COMMON_CLK_SCMI
tristate "Clock driver controlled via SCMI interface"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
---help---
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.
config COMMON_CLK_SCPI
tristate "Clock driver controlled via SCPI interface"
depends on ARM_SCPI_PROTOCOL || COMPILE_TEST
+1
View File
@@ -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
+194
View File
@@ -0,0 +1,194 @@
// 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 = {
.flags = CLK_GET_RATE_NOCACHE,
.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,
sclk->info->range.max_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);
continue;
}
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,
clk_data);
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_CLOCK },
{ },
};
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_scmi_driver(scmi_clocks_driver);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCMI clock driver");
MODULE_LICENSE("GPL v2");
+12
View File
@@ -239,6 +239,18 @@ config ARM_SA1100_CPUFREQ
config ARM_SA1110_CPUFREQ
bool
config ARM_SCMI_CPUFREQ
tristate "SCMI based CPUfreq driver"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
depends on !CPU_THERMAL || THERMAL
select PM_OPP
help
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.
config ARM_SPEAR_CPUFREQ
bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR
+1
View File
@@ -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
+264
View File
@@ -0,0 +1,264 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Power Interface (SCMI) based CPUFreq Interface driver
*
* Copyright (C) 2018 ARM Ltd.
* Sudeep Holla <sudeep.holla@arm.com>
*/
#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,
policy->cpuinfo.max_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,
policy->cpuinfo.max_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;
for_each_possible_cpu(cpu) {
if (cpu == cpu_dev->id)
continue;
tcpu_dev = get_cpu_device(cpu);
if (!tcpu_dev)
continue;
tdomain = handle->perf_ops->device_domain_id(tcpu_dev);
if (tdomain == domain)
cpumask_set_cpu(cpu, cpumask);
}
return 0;
}
static int scmi_cpufreq_init(struct cpufreq_policy *policy)
{
int ret;
unsigned int latency;
struct device *cpu_dev;
struct scmi_data *priv;
struct cpufreq_frequency_table *freq_table;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("failed to get cpu%d device\n", policy->cpu);
return -ENODEV;
}
ret = handle->perf_ops->add_opps_to_device(handle, cpu_dev);
if (ret) {
dev_warn(cpu_dev, "failed to add opps to the device\n");
return ret;
}
ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
if (ret) {
dev_warn(cpu_dev, "failed to get sharing cpumask\n");
return ret;
}
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
if (ret) {
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret);
return ret;
}
ret = dev_pm_opp_get_opp_count(cpu_dev);
if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
goto out_free_opp;
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out_free_opp;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_free_priv;
}
priv->cpu_dev = cpu_dev;
priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev);
policy->driver_data = priv;
ret = cpufreq_table_validate_and_show(policy, freq_table);
if (ret) {
dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
ret);
goto out_free_cpufreq_table;
}
/* SCMI allows DVFS request for any domain from any CPU */
policy->dvfs_possible_from_any_cpu = true;
latency = handle->perf_ops->get_transition_latency(handle, cpu_dev);
if (!latency)
latency = CPUFREQ_ETERNAL;
policy->cpuinfo.transition_latency = latency;
policy->fast_switch_possible = true;
return 0;
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_priv:
kfree(priv);
out_free_opp:
dev_pm_opp_cpumask_remove_table(policy->cpus);
return ret;
}
static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
{
struct scmi_data *priv = policy->driver_data;
cpufreq_cooling_unregister(priv->cdev);
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
kfree(priv);
dev_pm_opp_cpumask_remove_table(policy->related_cpus);
return 0;
}
static void scmi_cpufreq_ready(struct cpufreq_policy *policy)
{
struct scmi_data *priv = policy->driver_data;
priv->cdev = of_cpufreq_cooling_register(policy);
}
static struct cpufreq_driver scmi_cpufreq_driver = {
.name = "scmi",
.flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.attr = cpufreq_generic_attr,
.target_index = scmi_cpufreq_set_target,
.fast_switch = scmi_cpufreq_fast_switch,
.get = scmi_cpufreq_get_rate,
.init = scmi_cpufreq_init,
.exit = scmi_cpufreq_exit,
.ready = scmi_cpufreq_ready,
};
static int scmi_cpufreq_probe(struct scmi_device *sdev)
{
int ret;
handle = sdev->handle;
if (!handle || !handle->perf_ops)
return -ENODEV;
ret = cpufreq_register_driver(&scmi_cpufreq_driver);
if (ret) {
dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n",
__func__, ret);
}
return ret;
}
static void scmi_cpufreq_remove(struct scmi_device *sdev)
{
cpufreq_unregister_driver(&scmi_cpufreq_driver);
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_PERF },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_cpufreq_drv = {
.name = "scmi-cpufreq",
.probe = scmi_cpufreq_probe,
.remove = scmi_cpufreq_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_cpufreq_drv);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCMI CPUFreq interface driver");
MODULE_LICENSE("GPL v2");
+34
View File
@@ -19,6 +19,40 @@ config ARM_PSCI_CHECKER
on and off through hotplug, so for now torture tests and PSCI checker
are mutually exclusive.
config ARM_SCMI_PROTOCOL
bool "ARM System Control and Management Interface (SCMI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
depends on MAILBOX
help
ARM System Control and Management Interface (SCMI) protocol is a
set of operating system-independent software interfaces that are
used in system management. SCMI is extensible and currently provides
interfaces for: Discovery and self-description of the interfaces
it supports, Power domain management which is the ability to place
a given device or domain into the various power-saving states that
it supports, Performance management which is the ability to control
the performance of a domain that is composed of compute engines
such as application processors and other accelerators, Clock
management which is the ability to set and inquire rates on platform
managed clocks and Sensor management which is the ability to read
sensor data, and be notified of sensor value.
This protocol library provides interface for all the client drivers
making use of the features offered by the SCMI.
config ARM_SCMI_POWER_DOMAIN
tristate "SCMI power domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
default y
select PM_GENERIC_DOMAINS if PM
help
This enables support for the SCMI power domains which can be
enabled or disabled via the SCP firmware
This driver can also be built as a module. If so, the module
will be called scmi_pm_domain. Note this may needed early in boot
before rootfs may be available.
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
+1
View File
@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/
obj-y += broadcom/
obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/

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