mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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:
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user