Commit 2184dbcd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'soc-drivers-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "This is the usual mix of updates for drivers that are used on (mostly
  ARM) SoCs with no other top-level subsystem tree, including:

   - The SCMI firmware subsystem gains support for version 3.2 of the
     specification and updates to the notification code

   - Feature updates for Tegra and Qualcomm platforms for added hardware
     support

   - A number of platforms get soc_device additions for identifying
     newly added chips from Renesas, Qualcomm, Mediatek and Google

   - Trivial improvements for firmware and memory drivers amongst
     others, in particular 'const' annotations throughout multiple
     subsystems"

* tag 'soc-drivers-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (96 commits)
  tee: make tee_bus_type const
  soc: qcom: aoss: add missing kerneldoc for qmp members
  soc: qcom: geni-se: drop unused kerneldoc struct geni_wrapper param
  soc: qcom: spm: fix building with CONFIG_REGULATOR=n
  bus: ti-sysc: constify the struct device_type usage
  memory: stm32-fmc2-ebi: keep power domain on
  memory: stm32-fmc2-ebi: add MP25 RIF support
  memory: stm32-fmc2-ebi: add MP25 support
  memory: stm32-fmc2-ebi: check regmap_read return value
  dt-bindings: memory-controller: st,stm32: add MP25 support
  dt-bindings: bus: imx-weim: convert to YAML
  watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs
  soc: samsung: exynos-pmu: Add regmap support for SoCs that protect PMU regs
  MAINTAINERS: Update SCMI entry with HWMON driver
  MAINTAINERS: samsung: gs101: match patches touching Google Tensor SoC
  memory: tegra: Fix indentation
  memory: tegra: Add BPMP and ICC info for DLA clients
  memory: tegra: Correct DLA client names
  dt-bindings: memory: renesas,rpc-if: Document R-Car V4M support
  firmware: arm_scmi: Update the supported clock protocol version
  ...
parents 306bee64 049238d2
SPM AVS Wrapper 2 (SAW2)
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
power-controller that transitions a piece of hardware (like a processor or
subsystem) into and out of low power modes via a direct connection to
the PMIC. It can also be wired up to interact with other processors in the
system, notifying them when a low power state is entered or exited.
Multiple revisions of the SAW hardware are supported using these Device Nodes.
SAW2 revisions differ in the register offset and configuration data. Also, the
same revision of the SAW in different SoCs may have different configuration
data due the differences in hardware capabilities. Hence the SoC name, the
version of the SAW hardware in that SoC and the distinction between cpu (big
or Little) or cache, may be needed to uniquely identify the SAW register
configuration and initialization data. The compatible string is used to
indicate this parameter.
PROPERTIES
- compatible:
Usage: required
Value type: <string>
Definition: Must have
"qcom,saw2"
A more specific value could be one of:
"qcom,apq8064-saw2-v1.1-cpu"
"qcom,msm8226-saw2-v2.1-cpu"
"qcom,msm8974-saw2-v2.1-cpu"
"qcom,apq8084-saw2-v2.1-cpu"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: the first element specifies the base address and size of
the register region. An optional second element specifies
the base address and size of the alias register region.
- regulator:
Usage: optional
Value type: boolean
Definition: Indicates that this SPM device acts as a regulator device
device for the core (CPU or Cache) the SPM is attached
to.
Example 1:
power-controller@2099000 {
compatible = "qcom,saw2";
reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
regulator;
};
Example 2:
saw0: power-controller@f9089000 {
compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2";
reg = <0xf9089000 0x1000>, <0xf9009000 0x1000>;
};
Device tree bindings for i.MX Wireless External Interface Module (WEIM)
The term "wireless" does not imply that the WEIM is literally an interface
without wires. It simply means that this module was originally designed for
wireless and mobile applications that use low-power technology.
The actual devices are instantiated from the child nodes of a WEIM node.
Required properties:
- compatible: Should contain one of the following:
"fsl,imx1-weim"
"fsl,imx27-weim"
"fsl,imx51-weim"
"fsl,imx50-weim"
"fsl,imx6q-weim"
- reg: A resource specifier for the register space
(see the example below)
- clocks: the clock, see the example below.
- #address-cells: Must be set to 2 to allow memory address translation
- #size-cells: Must be set to 1 to allow CS address passing
- ranges: Must be set up to reflect the memory layout with four
integer values for each chip-select line in use:
<cs-number> 0 <physical address of mapping> <size>
Optional properties:
- fsl,weim-cs-gpr: For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
devices, it should be the phandle to the system General
Purpose Register controller that contains WEIM CS GPR
register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
should be set up as one of the following 4 possible
values depending on the CS space configuration.
IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
---------------------------------------------
05 128M 0M 0M 0M
033 64M 64M 0M 0M
0113 64M 32M 32M 0M
01111 32M 32M 32M 32M
In case that the property is absent, the reset value or
what bootloader sets up in IOMUXC_GPR1[11:0] will be
used.
- fsl,burst-clk-enable For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
devices, the presence of this property indicates that
the weim bus should operate in Burst Clock Mode.
- fsl,continuous-burst-clk Make Burst Clock to output continuous clock.
Without this option Burst Clock will output clock
only when necessary. This takes effect only if
"fsl,burst-clk-enable" is set.
Timing property for child nodes. It is mandatory, not optional.
- fsl,weim-cs-timing: The timing array, contains timing values for the
child node. We get the CS indexes from the address
ranges in the child node's "reg" property.
The number of registers depends on the selected chip:
For i.MX1, i.MX21 ("fsl,imx1-weim") there are two
registers: CSxU, CSxL.
For i.MX25, i.MX27, i.MX31 and i.MX35 ("fsl,imx27-weim")
there are three registers: CSCRxU, CSCRxL, CSCRxA.
For i.MX50, i.MX53 ("fsl,imx50-weim"),
i.MX51 ("fsl,imx51-weim") and i.MX6Q ("fsl,imx6q-weim")
there are six registers: CSxGCR1, CSxGCR2, CSxRCR1,
CSxRCR2, CSxWCR1, CSxWCR2.
Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM:
weim: weim@21b8000 {
compatible = "fsl,imx6q-weim";
reg = <0x021b8000 0x4000>;
clocks = <&clks 196>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x08000000>;
fsl,weim-cs-gpr = <&gpr>;
nor@0,0 {
compatible = "cfi-flash";
reg = <0 0 0x02000000>;
#address-cells = <1>;
#size-cells = <1>;
bank-width = <2>;
fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000
0x0000c000 0x1404a38e 0x00000000>;
};
};
Example for an imx6q-based board, a multi-chipselect device connected to WEIM:
In this case, both chip select 0 and 1 will be configured with the same timing
array values.
weim: weim@21b8000 {
compatible = "fsl,imx6q-weim";
reg = <0x021b8000 0x4000>;
clocks = <&clks 196>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x02000000
1 0 0x0a000000 0x02000000
2 0 0x0c000000 0x02000000
3 0 0x0e000000 0x02000000>;
fsl,weim-cs-gpr = <&gpr>;
acme@0 {
compatible = "acme,whatever";
reg = <0 0 0x100>, <0 0x400000 0x800>,
<1 0x400000 0x800>;
fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100
0x00000000 0xa0000240 0x00000000>;
};
};
......@@ -33,6 +33,7 @@ properties:
- const: samsung,exynos7-hsi2c
- items:
- enum:
- google,gs101-hsi2c
- samsung,exynos850-hsi2c
- const: samsung,exynosautov9-hsi2c
- const: samsung,exynos5-hsi2c # Exynos5250 and Exynos5420
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: i.MX WEIM Bus Peripheral Nodes
maintainers:
- Shawn Guo <shawnguo@kernel.org>
- Sascha Hauer <s.hauer@pengutronix.de>
description:
This binding is meant for the child nodes of the WEIM node. The node
represents any device connected to the WEIM bus. It may be a Flash chip,
RAM chip or Ethernet controller, etc. These properties are meant for
configuring the WEIM settings/timings and will accompany the bindings
supported by the respective device.
properties:
reg: true
fsl,weim-cs-timing:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
Timing values for the child node.
minItems: 2
maxItems: 6
# the WEIM child will have its own native properties
additionalProperties: true
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/fsl/fsl,imx-weim.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: i.MX Wireless External Interface Module (WEIM)
maintainers:
- Shawn Guo <shawnguo@kernel.org>
- Sascha Hauer <s.hauer@pengutronix.de>
description:
The term "wireless" does not imply that the WEIM is literally an interface
without wires. It simply means that this module was originally designed for
wireless and mobile applications that use low-power technology. The actual
devices are instantiated from the child nodes of a WEIM node.
properties:
$nodename:
pattern: "^memory-controller@[0-9a-f]+$"
compatible:
oneOf:
- enum:
- fsl,imx1-weim
- fsl,imx27-weim
- fsl,imx50-weim
- fsl,imx51-weim
- fsl,imx6q-weim
- items:
- enum:
- fsl,imx31-weim
- fsl,imx35-weim
- const: fsl,imx27-weim
- items:
- enum:
- fsl,imx6sx-weim
- fsl,imx6ul-weim
- const: fsl,imx6q-weim
"#address-cells":
const: 2
"#size-cells":
const: 1
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
ranges: true
fsl,weim-cs-gpr:
$ref: /schemas/types.yaml#/definitions/phandle
description: |
Phandle to the system General Purpose Register controller that contains
WEIM CS GPR register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
should be set up as one of the following 4 possible values depending on
the CS space configuration.
IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
---------------------------------------------
05 128M 0M 0M 0M
033 64M 64M 0M 0M
0113 64M 32M 32M 0M
01111 32M 32M 32M 32M
In case that the property is absent, the reset value or what bootloader
sets up in IOMUXC_GPR1[11:0] will be used.
fsl,burst-clk-enable:
type: boolean
description:
The presence of this property indicates that the weim bus should operate
in Burst Clock Mode.
fsl,continuous-burst-clk:
type: boolean
description:
Make Burst Clock to output continuous clock. Without this option Burst
Clock will output clock only when necessary.
patternProperties:
"^.*@[0-7],[0-9a-f]+$":
type: object
description: Devices attached to chip selects are represented as subnodes.
$ref: fsl,imx-weim-peripherals.yaml
additionalProperties: true
required:
- fsl,weim-cs-timing
required:
- compatible
- reg
- clocks
- "#address-cells"
- "#size-cells"
- ranges
allOf:
- if:
properties:
compatible:
not:
contains:
enum:
- fsl,imx50-weim
- fsl,imx6q-weim
then:
properties:
fsl,weim-cs-gpr: false
fsl,burst-clk-enable: false
- if:
not:
required:
- fsl,burst-clk-enable
then:
properties:
fsl,continuous-burst-clk: false
- if:
properties:
compatible:
contains:
const: fsl,imx1-weim
then:
patternProperties:
"^.*@[0-7],[0-9a-f]+$":
properties:
fsl,weim-cs-timing:
items:
items:
- description: CSxU
- description: CSxL
- if:
properties:
compatible:
contains:
enum:
- fsl,imx27-weim
- fsl,imx31-weim
- fsl,imx35-weim
then:
patternProperties:
"^.*@[0-7],[0-9a-f]+$":
properties:
fsl,weim-cs-timing:
items:
items:
- description: CSCRxU
- description: CSCRxL
- description: CSCRxA
- if:
properties:
compatible:
contains:
enum:
- fsl,imx50-weim
- fsl,imx51-weim
- fsl,imx6q-weim
- fsl,imx6sx-weim
- fsl,imx6ul-weim
then:
patternProperties:
"^.*@[0-7],[0-9a-f]+$":
properties:
fsl,weim-cs-timing:
items:
items:
- description: CSxGCR1
- description: CSxGCR2
- description: CSxRCR1
- description: CSxRCR2
- description: CSxWCR1
- description: CSxWCR2
additionalProperties: false
examples:
- |
memory-controller@21b8000 {
compatible = "fsl,imx6q-weim";
reg = <0x021b8000 0x4000>;
clocks = <&clks 196>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x08000000>;
fsl,weim-cs-gpr = <&gpr>;
flash@0,0 {
compatible = "cfi-flash";
reg = <0 0 0x02000000>;
#address-cells = <1>;
#size-cells = <1>;
bank-width = <2>;
fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000
0x0000c000 0x1404a38e 0x00000000>;
};
};
......@@ -37,5 +37,6 @@ allOf:
- $ref: ingenic,nemc-peripherals.yaml#
- $ref: intel,ixp4xx-expansion-peripheral-props.yaml#
- $ref: ti,gpmc-child.yaml#
- $ref: fsl/fsl,imx-weim-peripherals.yaml
additionalProperties: true
......@@ -145,7 +145,7 @@ patternProperties:
"^emc-table@[0-9]+$":
$ref: "#/$defs/emc-table"
"^emc-tables@[a-z0-9-]+$":
"^emc-tables@[a-f0-9-]+$":
type: object
properties:
reg:
......
......@@ -45,6 +45,7 @@ properties:
- items:
- enum:
- renesas,r8a779g0-rpc-if # R-Car V4H
- renesas,r8a779h0-rpc-if # R-Car V4M
- const: renesas,rcar-gen4-rpc-if # a generic R-Car gen4 device
- items:
......
......@@ -23,7 +23,9 @@ maintainers:
properties:
compatible:
const: st,stm32mp1-fmc2-ebi
enum:
- st,stm32mp1-fmc2-ebi
- st,stm32mp25-fmc2-ebi
reg:
maxItems: 1
......@@ -34,6 +36,9 @@ properties:
resets:
maxItems: 1
power-domains:
maxItems: 1
"#address-cells":
const: 2
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/qcom/qcom,pbs.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. Programmable Boot Sequencer
maintainers:
- Anjelique Melendez <quic_amelende@quicinc.com>
description: |
The Qualcomm Technologies, Inc. Programmable Boot Sequencer (PBS)
supports triggering power up and power down sequences for clients
upon request.
properties:
compatible:
items:
- enum:
- qcom,pmi632-pbs
- const: qcom,pbs
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/spmi/spmi.h>
pmic@0 {
reg = <0x0 SPMI_USID>;
#address-cells = <1>;
#size-cells = <0>;
pbs@7400 {
compatible = "qcom,pmi632-pbs", "qcom,pbs";
reg = <0x7400>;
};
};
......@@ -32,6 +32,7 @@ properties:
- items:
- enum:
- qcom,sm8650-pmic-glink
- qcom,x1e80100-pmic-glink
- const: qcom,sm8550-pmic-glink
- const: qcom,pmic-glink
......@@ -65,6 +66,7 @@ allOf:
enum:
- qcom,sm8450-pmic-glink
- qcom,sm8550-pmic-glink
- qcom,x1e80100-pmic-glink
then:
properties:
orientation-gpios: false
......
......@@ -35,6 +35,8 @@ properties:
description: Phandle to an RPM MSG RAM slice containing the master stats
minItems: 1
maxItems: 5
items:
maxItems: 1
qcom,master-names:
$ref: /schemas/types.yaml#/definitions/string-array
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/qcom/qcom,spm.yaml#
$id: http://devicetree.org/schemas/soc/qcom/qcom,saw2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Subsystem Power Manager
title: Qualcomm Subsystem Power Manager / SPM AVS Wrapper 2 (SAW2)
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
description: |
This binding describes the Qualcomm Subsystem Power Manager, used to control
the peripheral logic surrounding the application cores in Qualcomm platforms.
The Qualcomm Subsystem Power Manager is used to control the peripheral logic
surrounding the application cores in Qualcomm platforms.
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
power-controller that transitions a piece of hardware (like a processor or
subsystem) into and out of low power modes via a direct connection to
the PMIC. It can also be wired up to interact with other processors in the
system, notifying them when a low power state is entered or exited.
properties:
compatible:
items:
- enum:
- qcom,ipq4019-saw2-cpu
- qcom,ipq4019-saw2-l2
- qcom,ipq8064-saw2-cpu
- qcom,sdm660-gold-saw2-v4.1-l2
- qcom,sdm660-silver-saw2-v4.1-l2
- qcom,msm8998-gold-saw2-v4.1-l2
......@@ -26,16 +36,27 @@ properties:
- qcom,msm8916-saw2-v3.0-cpu
- qcom,msm8939-saw2-v3.0-cpu
- qcom,msm8226-saw2-v2.1-cpu
- qcom,msm8226-saw2-v2.1-l2
- qcom,msm8960-saw2-cpu
- qcom,msm8974-saw2-v2.1-cpu
- qcom,msm8974-saw2-v2.1-l2
- qcom,msm8976-gold-saw2-v2.3-l2
- qcom,msm8976-silver-saw2-v2.3-l2
- qcom,apq8084-saw2-v2.1-cpu
- qcom,apq8084-saw2-v2.1-l2
- qcom,apq8064-saw2-v1.1-cpu
- const: qcom,saw2
reg:
description: Base address and size of the SPM register region
maxItems: 1
items:
- description: Base address and size of the SPM register region
- description: Base address and size of the alias register region
minItems: 1
regulator:
$ref: /schemas/regulator/regulator.yaml#
description: Indicates that this SPM device acts as a regulator device
device for the core (CPU or Cache) the SPM is attached to.
required:
- compatible
......@@ -82,4 +103,17 @@ examples:
reg = <0x17912000 0x1000>;
};
- |
/*
* Example 3: SAW2 with the bundled regulator definition.
*/
power-manager@2089000 {
compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
regulator {
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <1300000>;
};
};
...
......@@ -72,6 +72,8 @@ allOf:
compatible:
contains:
enum:
- google,gs101-peric0-sysreg
- google,gs101-peric1-sysreg
- samsung,exynos850-cmgp-sysreg
- samsung,exynos850-peri-sysreg
- samsung,exynos850-sysreg
......
......@@ -9091,6 +9091,7 @@ F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
F: arch/arm64/boot/dts/exynos/google/
F: drivers/clk/samsung/clk-gs101.c
F: include/dt-bindings/clock/google,gs101.h
K: [gG]oogle.?[tT]ensor
GPD POCKET FAN DRIVER
M: Hans de Goede <hdegoede@redhat.com>
......@@ -17369,7 +17370,6 @@ F: Documentation/devicetree/bindings/pinctrl/renesas,*
F: drivers/pinctrl/renesas/
PIN CONTROLLER - SAMSUNG
M: Tomasz Figa <tomasz.figa@gmail.com>
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
R: Alim Akhtar <alim.akhtar@samsung.com>
......@@ -19428,7 +19428,6 @@ F: drivers/media/platform/samsung/exynos4-is/
SAMSUNG SOC CLOCK DRIVERS
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
M: Tomasz Figa <tomasz.figa@gmail.com>
M: Chanwoo Choi <cw00.choi@samsung.com>
R: Alim Akhtar <alim.akhtar@samsung.com>
L: linux-samsung-soc@vger.kernel.org
......@@ -21348,6 +21347,7 @@ F: drivers/clk/clk-sc[mp]i.c
F: drivers/cpufreq/sc[mp]i-cpufreq.c
F: drivers/firmware/arm_scmi/
F: drivers/firmware/arm_scpi.c
F: drivers/hwmon/scmi-hwmon.c
F: drivers/pmdomain/arm/
F: drivers/powercap/arm_scmi_powercap.c
F: drivers/regulator/scmi-regulator.c
......
......@@ -186,11 +186,12 @@ config SUNXI_RSB
config TEGRA_ACONNECT
tristate "Tegra ACONNECT Bus Driver"
depends on ARCH_TEGRA_210_SOC
depends on ARCH_TEGRA
depends on OF && PM
help
Driver for the Tegra ACONNECT bus which is used to interface with
the devices inside the Audio Processing Engine (APE) for Tegra210.
the devices inside the Audio Processing Engine (APE) for
Tegra210 and later.
config TEGRA_GMI
tristate "Tegra Generic Memory Interface bus driver"
......
......@@ -128,7 +128,7 @@ struct sunxi_rsb {
};
/* bus / slave device related functions */
static struct bus_type sunxi_rsb_bus;
static const struct bus_type sunxi_rsb_bus;
static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv)
{
......@@ -177,7 +177,7 @@ static int sunxi_rsb_device_modalias(const struct device *dev, struct kobj_ueven
return of_device_uevent_modalias(dev, env);
}
static struct bus_type sunxi_rsb_bus = {
static const struct bus_type sunxi_rsb_bus = {
.name = RSB_CTRL_NAME,
.match = sunxi_rsb_device_match,
.probe = sunxi_rsb_device_probe,
......
......@@ -2400,7 +2400,7 @@ static int sysc_child_add_clocks(struct sysc *ddata,
return 0;
}
static struct device_type sysc_device_type = {
static const struct device_type sysc_device_type = {
};
static struct sysc *sysc_child_to_parent(struct device *dev)
......
......@@ -105,7 +105,7 @@ static struct attribute *ffa_device_attributes_attrs[] = {
};
ATTRIBUTE_GROUPS(ffa_device_attributes);
struct bus_type ffa_bus_type = {
const struct bus_type ffa_bus_type = {
.name = "arm_ffa",
.match = ffa_device_match,
.probe = ffa_device_probe,
......
......@@ -141,6 +141,17 @@ static int scmi_protocol_device_request(const struct scmi_device_id *id_table)
return ret;
}
static int scmi_protocol_table_register(const struct scmi_device_id *id_table)
{
int ret = 0;
const struct scmi_device_id *entry;
for (entry = id_table; entry->name && ret == 0; entry++)
ret = scmi_protocol_device_request(entry);
return ret;
}
/**
* scmi_protocol_device_unrequest - Helper to unrequest a device
*
......@@ -186,6 +197,15 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table
mutex_unlock(&scmi_requested_devices_mtx);
}
static void
scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
{
const struct scmi_device_id *entry;
for (entry = id_table; entry->name; entry++)
scmi_protocol_device_unrequest(entry);
}
static const struct scmi_device_id *
scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
{
......@@ -263,7 +283,7 @@ static void scmi_dev_remove(struct device *dev)
scmi_drv->remove(scmi_dev);
}
struct bus_type scmi_bus_type = {
const struct bus_type scmi_bus_type = {
.name = "scmi_protocol",
.match = scmi_dev_match,
.probe = scmi_dev_probe,
......@@ -279,7 +299,7 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
if (!driver->probe)
return -EINVAL;
retval = scmi_protocol_device_request(driver->id_table);
retval = scmi_protocol_table_register(driver->id_table);
if (retval)
return retval;
......@@ -299,7 +319,7 @@ EXPORT_SYMBOL_GPL(scmi_driver_register);
void scmi_driver_unregister(struct scmi_driver *driver)
{
driver_unregister(&driver->driver);
scmi_protocol_device_unrequest(driver->id_table);
scmi_protocol_table_unregister(driver->id_table);
}
EXPORT_SYMBOL_GPL(scmi_driver_unregister);
......
This diff is collapsed.
......@@ -141,7 +141,7 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph);
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
u8 *prot_imp);
extern struct bus_type scmi_bus_type;
extern const struct bus_type scmi_bus_type;
#define SCMI_BUS_NOTIFY_DEVICE_REQUEST 0
#define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST 1
......
......@@ -86,6 +86,12 @@ struct scmi_xfers_info {
* @users: A refcount to track effective users of this protocol.
* @priv: Reference for optional protocol private data.
* @version: Protocol version supported by the platform as detected at runtime.
* @negotiated_version: When the platform supports a newer protocol version,
* the agent will try to negotiate with the platform the
* usage of the newest version known to it, since
* backward compatibility is NOT automatically assured.
* This field is NON-zero when a successful negotiation
* has completed.
* @ph: An embedded protocol handle that will be passed down to protocol
* initialization code to identify this instance.
*
......@@ -99,6 +105,7 @@ struct scmi_protocol_instance {
refcount_t users;
void *priv;
unsigned int version;
unsigned int negotiated_version;
struct scmi_protocol_handle ph;
};
......@@ -1754,10 +1761,44 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
#endif
}
/**
* scmi_protocol_msg_check - Check protocol message attributes
*
* @ph: A reference to the protocol handle.
* @message_id: The ID of the message to check.
* @attributes: A parameter to optionally return the retrieved message
* attributes, in case of Success.
*
* An helper to check protocol message attributes for a specific protocol
* and message pair.
*
* Return: 0 on SUCCESS
*/
static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
u32 message_id, u32 *attributes)
{
int ret;
struct scmi_xfer *t;
ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
sizeof(__le32), 0, &t);
if (ret)
return ret;
put_unaligned_le32(message_id, t->tx.buf);
ret = do_xfer(ph, t);
if (!ret && attributes)
*attributes = get_unaligned_le32(t->rx.buf);
xfer_put(ph, t);
return ret;
}
static const struct scmi_proto_helpers_ops helpers_ops = {
.extended_name_get = scmi_common_extended_name_get,
.iter_response_init = scmi_iterator_init,
.iter_response_run = scmi_iterator_run,
.protocol_msg_check = scmi_protocol_msg_check,
.fastchannel_init = scmi_common_fastchannel_init,
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
};
......@@ -1781,6 +1822,44 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph)
return pi->handle->version;
}
/**
* scmi_protocol_version_negotiate - Negotiate protocol version
*
* @ph: A reference to the protocol handle.
*
* An helper to negotiate a protocol version different from the latest
* advertised as supported from the platform: on Success backward
* compatibility is assured by the platform.
*
* Return: 0 on Success
*/
static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
{
int ret;
struct scmi_xfer *t;
struct scmi_protocol_instance *pi = ph_to_pi(ph);
/* At first check if NEGOTIATE_PROTOCOL_VERSION is supported ... */
ret = scmi_protocol_msg_check(ph, NEGOTIATE_PROTOCOL_VERSION, NULL);
if (ret)
return ret;
/* ... then attempt protocol version negotiation */
ret = xfer_get_init(ph, NEGOTIATE_PROTOCOL_VERSION,
sizeof(__le32), 0, &t);
if (ret)
return ret;
put_unaligned_le32(pi->proto->supported_version, t->tx.buf);
ret = do_xfer(ph, t);
if (!ret)
pi->negotiated_version = pi->proto->supported_version;
xfer_put(ph, t);
return ret;
}
/**
* scmi_alloc_init_protocol_instance - Allocate and initialize a protocol
* instance descriptor.
......@@ -1853,11 +1932,21 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
devres_close_group(handle->dev, pi->gid);
dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
if (pi->version > proto->supported_version)
dev_warn(handle->dev,
"Detected UNSUPPORTED higher version 0x%X for protocol 0x%X."
"Backward compatibility is NOT assured.\n",
pi->version, pi->proto->id);
if (pi->version > proto->supported_version) {
ret = scmi_protocol_version_negotiate(&pi->ph);
if (!ret) {
dev_info(handle->dev,
"Protocol 0x%X successfully negotiated version 0x%X\n",
proto->id, pi->negotiated_version);
} else {
dev_warn(handle->dev,
"Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
pi->version, pi->proto->id);
dev_warn(handle->dev,
"Trying version 0x%X. Backward compatibility is NOT assured.\n",
pi->proto->supported_version);
}
}
return pi;
......
......@@ -99,6 +99,7 @@
#define PROTO_ID_MASK GENMASK(31, 24)
#define EVT_ID_MASK GENMASK(23, 16)
#define SRC_ID_MASK GENMASK(15, 0)
#define NOTIF_UNSUPP -1
/*
* Builds an unsigned 32bit key from the given input tuple to be used
......@@ -788,6 +789,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
pd->ph = ph;
for (i = 0; i < ee->num_events; i++, evt++) {
int id;
struct scmi_registered_event *r_evt;
r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt),
......@@ -809,6 +811,11 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
if (!r_evt->report)
return -ENOMEM;
for (id = 0; id < r_evt->num_sources; id++)
if (ee->ops->is_notify_supported &&
!ee->ops->is_notify_supported(ph, r_evt->evt->id, id))
refcount_set(&r_evt->sources[id], NOTIF_UNSUPP);
pd->registered_events[i] = r_evt;
/* Ensure events are updated */
smp_wmb();
......@@ -1166,7 +1173,13 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt,
int ret = 0;
sid = &r_evt->sources[src_id];
if (refcount_read(sid) == 0) {
if (refcount_read(sid) == NOTIF_UNSUPP) {
dev_dbg(r_evt->proto->ph->dev,
"Notification NOT supported - proto_id:%d evt_id:%d src_id:%d",
r_evt->proto->id, r_evt->evt->id,
src_id);
ret = -EOPNOTSUPP;
} else if (refcount_read(sid) == 0) {
ret = REVT_NOTIFY_ENABLE(r_evt, r_evt->evt->id,
src_id);
if (!ret)
......@@ -1179,6 +1192,8 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt,
} else {
for (; num_sources; src_id++, num_sources--) {
sid = &r_evt->sources[src_id];
if (refcount_read(sid) == NOTIF_UNSUPP)
continue;
if (refcount_dec_and_test(sid))
REVT_NOTIFY_DISABLE(r_evt,
r_evt->evt->id, src_id);
......
......@@ -35,6 +35,8 @@ struct scmi_protocol_handle;
/**
* struct scmi_event_ops - Protocol helpers called by the notification core.
* @is_notify_supported: Return 0 if the specified notification for the
* specified resource (src_id) is supported.
* @get_num_sources: Returns the number of possible events' sources for this
* protocol
* @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
......@@ -50,6 +52,8 @@ struct scmi_protocol_handle;
* process context.
*/
struct scmi_event_ops {
bool (*is_notify_supported)(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id);
int (*get_num_sources)(const struct scmi_protocol_handle *ph);
int (*set_notify_enabled)(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enabled);
......
......@@ -109,8 +109,10 @@ enum scmi_optee_pta_cmd {
* @rx_len: Response size
* @mu: Mutex protection on channel access
* @cinfo: SCMI channel information
* @shmem: Virtual base address of the shared memory
* @req: Shared memory protocol handle for SCMI request and synchronous response
* @req: union for SCMI interface
* @req.shmem: Virtual base address of the shared memory
* @req.msg: Shared memory protocol handle for SCMI request and
* synchronous response
* @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem
* @link: Reference in agent's channel list
*/
......
......@@ -182,6 +182,8 @@ struct scmi_perf_info {
enum scmi_power_scale power_scale;
u64 stats_addr;
u32 stats_size;
bool notify_lvl_cmd;
bool notify_lim_cmd;
struct perf_dom_info *dom_info;
};
......@@ -222,6 +224,15 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
}
ph->xops->xfer_put(ph, t);
if (!ret) {
if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LEVEL, NULL))
pi->notify_lvl_cmd = true;
if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LIMITS, NULL))
pi->notify_lim_cmd = true;
}
return ret;
}
......@@ -239,6 +250,7 @@ static void scmi_perf_xa_destroy(void *data)
static int
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
struct perf_dom_info *dom_info,
bool notify_lim_cmd, bool notify_lvl_cmd,
u32 version)
{
int ret;
......@@ -260,8 +272,12 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
dom_info->info.set_perf = SUPPORTS_SET_PERF_LVL(flags);
dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
if (notify_lim_cmd)
dom_info->perf_limit_notify =
SUPPORTS_PERF_LIMIT_NOTIFY(flags);
if (notify_lvl_cmd)
dom_info->perf_level_notify =
SUPPORTS_PERF_LEVEL_NOTIFY(flags);
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
if (PROTOCOL_REV_MAJOR(version) >= 0x4)
dom_info->level_indexing_mode =
......@@ -270,15 +286,30 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
le32_to_cpu(attr->sustained_freq_khz);
dom_info->sustained_perf_level =
le32_to_cpu(attr->sustained_perf_level);
/*
* sustained_freq_khz = mult_factor * sustained_perf_level
* mult_factor must be non zero positive integer(not fraction)
*/
if (!dom_info->sustained_freq_khz ||
!dom_info->sustained_perf_level ||
dom_info->level_indexing_mode)
dom_info->level_indexing_mode) {
/* CPUFreq converts to kHz, hence default 1000 */
dom_info->mult_factor = 1000;
else
} else {
dom_info->mult_factor =
(dom_info->sustained_freq_khz * 1000UL)
/ dom_info->sustained_perf_level;
if ((dom_info->sustained_freq_khz * 1000UL) %
dom_info->sustained_perf_level)
dev_warn(ph->dev,
"multiplier for domain %d rounded\n",
dom_info->id);
}
if (!dom_info->mult_factor)
dev_warn(ph->dev,
"Wrong sustained perf/frequency(domain %d)\n",
dom_info->id);
strscpy(dom_info->info.name, attr->name,
SCMI_SHORT_NAME_MAX_SIZE);
}
......@@ -295,9 +326,9 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
dom_info->id, NULL, dom_info->info.name,
SCMI_MAX_STR_SIZE);
xa_init(&dom_info->opps_by_lvl);
if (dom_info->level_indexing_mode) {
xa_init(&dom_info->opps_by_idx);
xa_init(&dom_info->opps_by_lvl);
hash_init(dom_info->opps_by_freq);
}
......@@ -340,13 +371,21 @@ static int iter_perf_levels_update_state(struct scmi_iterator_state *st,
}
static inline void
process_response_opp(struct scmi_opp *opp, unsigned int loop_idx,
process_response_opp(struct device *dev, struct perf_dom_info *dom,
struct scmi_opp *opp, unsigned int loop_idx,
const struct scmi_msg_resp_perf_describe_levels *r)
{
int ret;
opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
opp->power = le32_to_cpu(r->opp[loop_idx].power);
opp->trans_latency_us =
le16_to_cpu(r->opp[loop_idx].transition_latency_us);
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
if (ret)
dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
opp->perf, ret);
}
static inline void
......@@ -354,16 +393,21 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
struct scmi_opp *opp, unsigned int loop_idx,
const struct scmi_msg_resp_perf_describe_levels_v4 *r)
{
int ret;
opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
opp->power = le32_to_cpu(r->opp[loop_idx].power);
opp->trans_latency_us =
le16_to_cpu(r->opp[loop_idx].transition_latency_us);
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
if (ret)
dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
opp->perf, ret);
/* Note that PERF v4 reports always five 32-bit words */
opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq);
if (dom->level_indexing_mode) {
int ret;
opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index);
ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp,
......@@ -373,12 +417,6 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
"Failed to add opps_by_idx at %d - ret:%d\n",
opp->level_index, ret);
ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
if (ret)
dev_warn(dev,
"Failed to add opps_by_lvl at %d - ret:%d\n",
opp->perf, ret);
hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq);
}
}
......@@ -393,7 +431,8 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
process_response_opp(opp, st->loop_idx, response);
process_response_opp(ph->dev, p->perf_dom, opp, st->loop_idx,
response);
else
process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx,
response);
......@@ -978,6 +1017,27 @@ static const struct scmi_perf_proto_ops perf_proto_ops = {
.power_scale_get = scmi_power_scale_get,
};
static bool scmi_perf_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
bool supported;
struct perf_dom_info *dom;
if (evt_id >= ARRAY_SIZE(evt_2_cmd))
return false;
dom = scmi_perf_domain_lookup(ph, src_id);
if (IS_ERR(dom))
return false;
if (evt_id == SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED)
supported = dom->perf_limit_notify;
else
supported = dom->perf_level_notify;
return supported;
}
static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
......@@ -995,18 +1055,47 @@ static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
return ret;
}
static int
scmi_perf_xlate_opp_to_freq(struct perf_dom_info *dom,
unsigned int index, unsigned long *freq)
{
struct scmi_opp *opp;
if (!dom || !freq)
return -EINVAL;
if (!dom->level_indexing_mode) {
opp = xa_load(&dom->opps_by_lvl, index);
if (!opp)
return -ENODEV;
*freq = opp->perf * dom->mult_factor;
} else {
opp = xa_load(&dom->opps_by_idx, index);
if (!opp)
return -ENODEV;
*freq = opp->indicative_freq * dom->mult_factor;
}
return 0;
}
static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
int ret;
void *rep = NULL;
struct perf_dom_info *dom;
switch (evt_id) {
case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED:
{
const struct scmi_perf_limits_notify_payld *p = payld;
struct scmi_perf_limits_report *r = report;
unsigned long freq_min, freq_max;
if (sizeof(*p) != payld_sz)
break;
......@@ -1016,14 +1105,36 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
r->domain_id = le32_to_cpu(p->domain_id);
r->range_max = le32_to_cpu(p->range_max);
r->range_min = le32_to_cpu(p->range_min);
/* Check if the reported domain exist at all */
dom = scmi_perf_domain_lookup(ph, r->domain_id);
if (IS_ERR(dom))
break;
/*
* Event will be reported from this point on...
* ...even if, later, xlated frequencies were not retrieved.
*/
*src_id = r->domain_id;
rep = r;
ret = scmi_perf_xlate_opp_to_freq(dom, r->range_max, &freq_max);
if (ret)
break;
ret = scmi_perf_xlate_opp_to_freq(dom, r->range_min, &freq_min);
if (ret)
break;
/* Report translated freqs ONLY if both available */
r->range_max_freq = freq_max;
r->range_min_freq = freq_min;
break;
}
case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED:
{
const struct scmi_perf_level_notify_payld *p = payld;
struct scmi_perf_level_report *r = report;
unsigned long freq;
if (sizeof(*p) != payld_sz)
break;
......@@ -1031,9 +1142,27 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
/* Report translated freqs ONLY if available */
r->performance_level = le32_to_cpu(p->performance_level);
/* Check if the reported domain exist at all */
dom = scmi_perf_domain_lookup(ph, r->domain_id);
if (IS_ERR(dom))
break;
/*
* Event will be reported from this point on...
* ...even if, later, xlated frequencies were not retrieved.
*/
*src_id = r->domain_id;
rep = r;
/* Report translated freqs ONLY if available */
ret = scmi_perf_xlate_opp_to_freq(dom, r->performance_level,
&freq);
if (ret)
break;
r->performance_level_freq = freq;
break;
}
default:
......@@ -1067,6 +1196,7 @@ static const struct scmi_event perf_events[] = {
};
static const struct scmi_event_ops perf_event_ops = {
.is_notify_supported = scmi_perf_notify_supported,
.get_num_sources = scmi_perf_get_num_sources,
.set_notify_enabled = scmi_perf_set_notify_enabled,
.fill_custom_report = scmi_perf_fill_custom_report,
......@@ -1111,7 +1241,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
struct perf_dom_info *dom = pinfo->dom_info + domain;
dom->id = domain;
scmi_perf_domain_attributes_get(ph, dom, version);
scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd,
pinfo->notify_lvl_cmd, version);
scmi_perf_describe_levels_get(ph, dom, version);
if (dom->perf_fastchannels)
......
......@@ -68,6 +68,7 @@ struct power_dom_info {
struct scmi_power_info {
u32 version;
bool notify_state_change_cmd;
int num_domains;
u64 stats_addr;
u32 stats_size;
......@@ -97,13 +98,18 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
}
ph->xops->xfer_put(ph, t);
if (!ret)
if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL))
pi->notify_state_change_cmd = true;
return ret;
}
static int
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct power_dom_info *dom_info,
u32 version)
u32 version, bool notify_state_change_cmd)
{
int ret;
u32 flags;
......@@ -122,7 +128,9 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
if (!ret) {
flags = le32_to_cpu(attr->flags);
dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
if (notify_state_change_cmd)
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);
strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
......@@ -231,6 +239,20 @@ static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
return ret;
}
static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
struct power_dom_info *dom;
struct scmi_power_info *pinfo = ph->get_priv(ph);
if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED ||
src_id >= pinfo->num_domains)
return false;
dom = pinfo->dom_info + src_id;
return dom->state_set_notify;
}
static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
......@@ -285,6 +307,7 @@ static const struct scmi_event power_events[] = {
};
static const struct scmi_event_ops power_event_ops = {
.is_notify_supported = scmi_power_notify_supported,
.get_num_sources = scmi_power_get_num_sources,
.set_notify_enabled = scmi_power_set_notify_enabled,
.fill_custom_report = scmi_power_fill_custom_report,
......@@ -326,7 +349,8 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
for (domain = 0; domain < pinfo->num_domains; domain++) {
struct power_dom_info *dom = pinfo->dom_info + domain;
scmi_power_domain_attributes_get(ph, domain, dom, version);
scmi_power_domain_attributes_get(ph, domain, dom, version,
pinfo->notify_state_change_cmd);
}
pinfo->version = version;
......
......@@ -124,6 +124,8 @@ struct scmi_powercap_state {
struct powercap_info {
u32 version;
int num_domains;
bool notify_cap_cmd;
bool notify_measurements_cmd;
struct scmi_powercap_state *states;
struct scmi_powercap_info *powercaps;
};
......@@ -157,6 +159,18 @@ scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
}
ph->xops->xfer_put(ph, t);
if (!ret) {
if (!ph->hops->protocol_msg_check(ph,
POWERCAP_CAP_NOTIFY, NULL))
pi->notify_cap_cmd = true;
if (!ph->hops->protocol_msg_check(ph,
POWERCAP_MEASUREMENTS_NOTIFY,
NULL))
pi->notify_measurements_cmd = true;
}
return ret;
}
......@@ -200,10 +214,12 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
flags = le32_to_cpu(resp->attributes);
dom_info->id = domain;
dom_info->notify_powercap_cap_change =
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
dom_info->notify_powercap_measurement_change =
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
if (pinfo->notify_cap_cmd)
dom_info->notify_powercap_cap_change =
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
if (pinfo->notify_measurements_cmd)
dom_info->notify_powercap_measurement_change =
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
dom_info->async_powercap_cap_set =
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
dom_info->powercap_cap_config =
......@@ -788,6 +804,26 @@ static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
return ret;
}
static bool
scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
bool supported = false;
const struct scmi_powercap_info *dom_info;
struct powercap_info *pi = ph->get_priv(ph);
if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
return false;
dom_info = pi->powercaps + src_id;
if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)
supported = dom_info->notify_powercap_cap_change;
else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)
supported = dom_info->notify_powercap_measurement_change;
return supported;
}
static int
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
......@@ -904,6 +940,7 @@ static const struct scmi_event powercap_events[] = {
};
static const struct scmi_event_ops powercap_event_ops = {
.is_notify_supported = scmi_powercap_notify_supported,
.get_num_sources = scmi_powercap_get_num_sources,
.set_notify_enabled = scmi_powercap_set_notify_enabled,
.fill_custom_report = scmi_powercap_fill_custom_report,
......
......@@ -33,6 +33,7 @@ enum scmi_common_cmd {
PROTOCOL_VERSION = 0x0,
PROTOCOL_ATTRIBUTES = 0x1,
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
NEGOTIATE_PROTOCOL_VERSION = 0x10,
};
/**
......@@ -251,6 +252,8 @@ struct scmi_fc_info {
* provided in @ops.
* @iter_response_run: A common helper to trigger the run of a previously
* initialized iterator.
* @protocol_msg_check: A common helper to check is a specific protocol message
* is supported.
* @fastchannel_init: A common helper used to initialize FC descriptors by
* gathering FC descriptions from the SCMI platform server.
* @fastchannel_db_ring: A common helper to ring a FC doorbell.
......@@ -264,6 +267,8 @@ struct scmi_proto_helpers_ops {
unsigned int max_resources, u8 msg_id,
size_t tx_size, void *priv);
int (*iter_response_run)(void *iter);
int (*protocol_msg_check)(const struct scmi_protocol_handle *ph,
u32 message_id, u32 *attributes);
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
u8 describe_id, u32 message_id,
u32 valid_size, u32 domain,
......
......@@ -67,6 +67,7 @@ struct reset_dom_info {
struct scmi_reset_info {
u32 version;
int num_domains;
bool notify_reset_cmd;
struct reset_dom_info *dom_info;
};
......@@ -89,18 +90,24 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
}
ph->xops->xfer_put(ph, t);
if (!ret)
if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))
pi->notify_reset_cmd = true;
return ret;
}
static int
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct reset_dom_info *dom_info,
u32 version)
struct scmi_reset_info *pinfo,
u32 domain, u32 version)
{
int ret;
u32 attributes;
struct scmi_xfer *t;
struct scmi_msg_resp_reset_domain_attributes *attr;
struct reset_dom_info *dom_info = pinfo->dom_info + domain;
ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
sizeof(domain), sizeof(*attr), &t);
......@@ -115,7 +122,9 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
attributes = le32_to_cpu(attr->attributes);
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
if (pinfo->notify_reset_cmd)
dom_info->reset_notify =
SUPPORTS_NOTIFY_RESET(attributes);
dom_info->latency_us = le32_to_cpu(attr->latency);
if (dom_info->latency_us == U32_MAX)
dom_info->latency_us = 0;
......@@ -226,6 +235,20 @@ static const struct scmi_reset_proto_ops reset_proto_ops = {
.deassert = scmi_reset_domain_deassert,
};
static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
struct reset_dom_info *dom;
struct scmi_reset_info *pi = ph->get_priv(ph);
if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)
return false;
dom = pi->dom_info + src_id;
return dom->reset_notify;
}
static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
u32 domain_id, bool enable)
{
......@@ -301,6 +324,7 @@ static const struct scmi_event reset_events[] = {
};
static const struct scmi_event_ops reset_event_ops = {
.is_notify_supported = scmi_reset_notify_supported,
.get_num_sources = scmi_reset_get_num_sources,
.set_notify_enabled = scmi_reset_set_notify_enabled,
.fill_custom_report = scmi_reset_fill_custom_report,
......@@ -339,11 +363,8 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
if (!pinfo->dom_info)
return -ENOMEM;
for (domain = 0; domain < pinfo->num_domains; domain++) {
struct reset_dom_info *dom = pinfo->dom_info + domain;
scmi_reset_domain_attributes_get(ph, domain, dom, version);
}
for (domain = 0; domain < pinfo->num_domains; domain++)
scmi_reset_domain_attributes_get(ph, pinfo, domain, version);
pinfo->version = version;
return ph->set_priv(ph, pinfo, version);
......
......@@ -215,6 +215,8 @@ struct scmi_sensor_update_notify_payld {
struct sensors_info {
u32 version;
bool notify_trip_point_cmd;
bool notify_continuos_update_cmd;
int num_sensors;
int max_requests;
u64 reg_addr;
......@@ -246,6 +248,18 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph,
}
ph->xops->xfer_put(ph, t);
if (!ret) {
if (!ph->hops->protocol_msg_check(ph,
SENSOR_TRIP_POINT_NOTIFY, NULL))
si->notify_trip_point_cmd = true;
if (!ph->hops->protocol_msg_check(ph,
SENSOR_CONTINUOUS_UPDATE_NOTIFY,
NULL))
si->notify_continuos_update_cmd = true;
}
return ret;
}
......@@ -594,7 +608,8 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
* Such bitfields are assumed to be zeroed on non
* relevant fw versions...assuming fw not buggy !
*/
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
if (si->notify_continuos_update_cmd)
s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
if (s->timestamped)
s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
......@@ -988,6 +1003,25 @@ static const struct scmi_sensor_proto_ops sensor_proto_ops = {
.config_set = scmi_sensor_config_set,
};
static bool scmi_sensor_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
bool supported = false;
const struct scmi_sensor_info *s;
struct sensors_info *sinfo = ph->get_priv(ph);
s = scmi_sensor_info_get(ph, src_id);
if (!s)
return false;
if (evt_id == SCMI_EVENT_SENSOR_TRIP_POINT_EVENT)
supported = sinfo->notify_trip_point_cmd;
else if (evt_id == SCMI_EVENT_SENSOR_UPDATE)
supported = s->update;
return supported;
}
static int scmi_sensor_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
......@@ -1099,6 +1133,7 @@ static const struct scmi_event sensor_events[] = {
};
static const struct scmi_event_ops sensor_event_ops = {
.is_notify_supported = scmi_sensor_notify_supported,
.get_num_sources = scmi_sensor_get_num_sources,
.set_notify_enabled = scmi_sensor_set_notify_enabled,
.fill_custom_report = scmi_sensor_fill_custom_report,
......
......@@ -214,6 +214,13 @@ static int smc_chan_free(int id, void *p, void *data)
struct scmi_chan_info *cinfo = p;
struct scmi_smc *scmi_info = cinfo->transport_info;
/*
* Different protocols might share the same chan info, so a previous
* smc_chan_free call might have already freed the structure.
*/
if (!scmi_info)
return 0;
/* Ignore any possible further reception on the IRQ path */
if (scmi_info->irq > 0)
free_irq(scmi_info->irq, scmi_info);
......
......@@ -36,8 +36,20 @@ struct scmi_system_power_state_notifier_payld {
struct scmi_system_info {
u32 version;
bool graceful_timeout_supported;
bool power_state_notify_cmd;
};
static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id)
{
struct scmi_system_info *pinfo = ph->get_priv(ph);
if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER)
return false;
return pinfo->power_state_notify_cmd;
}
static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
bool enable)
{
......@@ -114,6 +126,7 @@ static const struct scmi_event system_events[] = {
};
static const struct scmi_event_ops system_event_ops = {
.is_notify_supported = scmi_system_notify_supported,
.set_notify_enabled = scmi_system_set_notify_enabled,
.fill_custom_report = scmi_system_fill_custom_report,
};
......@@ -147,6 +160,9 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
pinfo->graceful_timeout_supported = true;
if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
pinfo->power_state_notify_cmd = true;
return ph->set_priv(ph, pinfo, version);
}
......
......@@ -77,7 +77,7 @@ static const char *get_filename(struct tegra_bpmp *bpmp,
root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL);
if (!root_path_buf)
goto out;
return NULL;
root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
root_path_buf_len);
......
......@@ -72,7 +72,6 @@ static DEFINE_SPINLOCK(emif_lock);
static unsigned long irq_state;
static LIST_HEAD(device_list);
#ifdef CONFIG_DEBUG_FS
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
struct emif_regs *regs)
{
......@@ -140,31 +139,24 @@ static int emif_mr4_show(struct seq_file *s, void *unused)
DEFINE_SHOW_ATTRIBUTE(emif_mr4);
static int __init_or_module emif_debugfs_init(struct emif_data *emif)
static void emif_debugfs_init(struct emif_data *emif)
{
emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
&emif_regdump_fops);
debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
&emif_mr4_fops);
return 0;
}
static void __exit emif_debugfs_exit(struct emif_data *emif)
{
debugfs_remove_recursive(emif->debugfs_root);
emif->debugfs_root = NULL;
}
#else
static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
{
return 0;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
&emif_regdump_fops);
debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
&emif_mr4_fops);
}
}
static inline void __exit emif_debugfs_exit(struct emif_data *emif)
static void emif_debugfs_exit(struct emif_data *emif)
{
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
debugfs_remove_recursive(emif->debugfs_root);
emif->debugfs_root = NULL;
}
}
#endif
/*
* Get bus width used by EMIF. Note that this may be different from the
......@@ -679,7 +671,7 @@ static void disable_and_clear_all_interrupts(struct emif_data *emif)
clear_all_interrupts(emif);
}
static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
static int setup_interrupts(struct emif_data *emif, u32 irq)
{
u32 interrupts, type;
void __iomem *base = emif->base;
......@@ -710,7 +702,7 @@ static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
}
static void __init_or_module emif_onetime_settings(struct emif_data *emif)
static void emif_onetime_settings(struct emif_data *emif)
{
u32 pwr_mgmt_ctrl, zq, temp_alert_cfg;
void __iomem *base = emif->base;
......@@ -834,8 +826,7 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
return valid;
}
#if defined(CONFIG_OF)
static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
static void of_get_custom_configs(struct device_node *np_emif,
struct emif_data *emif)
{
struct emif_custom_configs *cust_cfgs = NULL;
......@@ -884,7 +875,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
emif->plat_data->custom_configs = cust_cfgs;
}
static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
static void of_get_ddr_info(struct device_node *np_emif,
struct device_node *np_ddr,
struct ddr_device_info *dev_info)
{
......@@ -918,7 +909,7 @@ static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
dev_info->io_width = __fls(io_width) - 1;
}
static struct emif_data * __init_or_module of_get_memory_device_details(
static struct emif_data *of_get_memory_device_details(
struct device_node *np_emif, struct device *dev)
{
struct emif_data *emif = NULL;
......@@ -991,16 +982,7 @@ static struct emif_data * __init_or_module of_get_memory_device_details(
return emif;
}
#else
static struct emif_data * __init_or_module of_get_memory_device_details(
struct device_node *np_emif, struct device *dev)
{
return NULL;
}
#endif
static struct emif_data *__init_or_module get_device_details(
static struct emif_data *get_device_details(
struct platform_device *pdev)
{
u32 size;
......@@ -1104,7 +1086,7 @@ static struct emif_data *__init_or_module get_device_details(
return NULL;
}
static int __init_or_module emif_probe(struct platform_device *pdev)
static int emif_probe(struct platform_device *pdev)
{
struct emif_data *emif;
int irq, ret;
......@@ -1159,7 +1141,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
return -ENODEV;
}
static void __exit emif_remove(struct platform_device *pdev)
static void emif_remove(struct platform_device *pdev)
{
struct emif_data *emif = platform_get_drvdata(pdev);
......@@ -1183,7 +1165,8 @@ MODULE_DEVICE_TABLE(of, emif_of_match);
#endif
static struct platform_driver emif_driver = {
.remove_new = __exit_p(emif_remove),
.probe = emif_probe,
.remove_new = emif_remove,
.shutdown = emif_shutdown,
.driver = {
.name = "emif",
......@@ -1191,7 +1174,7 @@ static struct platform_driver emif_driver = {
},
};
module_platform_driver_probe(emif_driver, emif_probe);
module_platform_driver(emif_driver);
MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
MODULE_LICENSE("GPL");
......
This diff is collapsed.
......@@ -92,6 +92,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0RDB,
.name = "dla0rdb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -102,6 +104,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0RDB1,
.name = "dla0rdb1",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -112,6 +116,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0WRB,
.name = "dla0wrb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -121,7 +127,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1RDB,
.name = "dla0rdb",
.name = "dla1rdb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -407,7 +415,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1RDB1,
.name = "dla0rdb1",
.name = "dla1rdb1",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -417,7 +427,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1WRB,
.name = "dla0wrb",
.name = "dla1wrb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -539,7 +551,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
.bpmp_id = TEGRA_ICC_BPMP_NVJPG_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVJPG,
.regs = {
.regs = {
.sid = {
.override = 0x3f8,
.security = 0x3fc,
......@@ -660,6 +672,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0RDA,
.name = "dla0rda",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -670,6 +684,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0FALRDB,
.name = "dla0falrdb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -680,6 +696,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0WRA,
.name = "dla0wra",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -690,6 +708,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0FALWRB,
.name = "dla0falwrb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -699,7 +719,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1RDA,
.name = "dla0rda",
.name = "dla1rda",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -709,7 +731,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1FALRDB,
.name = "dla0falrdb",
.name = "dla1falrdb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -719,7 +743,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1WRA,
.name = "dla0wra",
.name = "dla1wra",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -729,7 +755,9 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1FALWRB,
.name = "dla0falwrb",
.name = "dla1falwrb",
.bpmp_id = TEGRA_ICC_BPMP_DLA_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......@@ -908,6 +936,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA0RDA1,
.name = "dla0rda1",
.bpmp_id = TEGRA_ICC_BPMP_DLA_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA234_SID_NVDLA0,
.regs = {
.sid = {
......@@ -917,7 +947,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
},
}, {
.id = TEGRA234_MEMORY_CLIENT_DLA1RDA1,
.name = "dla0rda1",
.name = "dla1rda1",
.sid = TEGRA234_SID_NVDLA1,
.regs = {
.sid = {
......
......@@ -217,7 +217,6 @@ static struct rpmhpd *sa8540p_rpmhpds[] = {
[SC8280XP_CX] = &cx,
[SC8280XP_CX_AO] = &cx_ao,
[SC8280XP_EBI] = &ebi,
[SC8280XP_GFX] = &gfx,
[SC8280XP_LCX] = &lcx,
[SC8280XP_LMX] = &lmx,
[SC8280XP_MMCX] = &mmcx,
......
......@@ -68,4 +68,13 @@ config MTK_SVS
chip process corner, temperatures and other factors. Then DVFS
driver could apply SVS bank voltage to PMIC/Buck.
config MTK_SOCINFO
tristate "MediaTek SoC Information"
default y
depends on NVMEM_MTK_EFUSE
help
The MediaTek SoC Information (mtk-socinfo) driver provides
information about the SoC to the userspace including the
manufacturer name, marketing name and soc name.
endmenu
......@@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_REGULATOR_COUPLER) += mtk-regulator-coupler.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
obj-$(CONFIG_MTK_SVS) += mtk-svs.o
obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2023 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/nvmem-consumer.h>
#include <linux/device.h>
#include <linux/device/bus.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/sys_soc.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#define MTK_SOCINFO_ENTRY(_soc_name, _segment_name, _marketing_name, _cell_data1, _cell_data2) {\
.soc_name = _soc_name, \
.segment_name = _segment_name, \
.marketing_name = _marketing_name, \
.cell_data = {_cell_data1, _cell_data2} \
}
#define CELL_NOT_USED (0xFFFFFFFF)
#define MAX_CELLS (2)
struct mtk_socinfo {
struct device *dev;
struct name_data *name_data;
struct socinfo_data *socinfo_data;
struct soc_device *soc_dev;
};
struct socinfo_data {
char *soc_name;
char *segment_name;
char *marketing_name;
u32 cell_data[MAX_CELLS];
};
static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"};
static struct socinfo_data socinfo_data_table[] = {
MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000),
MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840),
MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940),
MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010),
MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 830", 0x81880000, 0x00000011),
MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080),
MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0),
MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
};
static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop)
{
struct soc_device_attribute *attrs;
static char machine[30] = {0};
static const char *soc_manufacturer = "MediaTek";
attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
snprintf(machine, sizeof(machine), "%s (%s)", mtk_socinfop->socinfo_data->marketing_name,
mtk_socinfop->socinfo_data->soc_name);
attrs->family = soc_manufacturer;
attrs->machine = machine;
mtk_socinfop->soc_dev = soc_device_register(attrs);
if (IS_ERR(mtk_socinfop->soc_dev))
return PTR_ERR(mtk_socinfop->soc_dev);
dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", soc_manufacturer, attrs->machine);
return 0;
}
static u32 mtk_socinfo_read_cell(struct device *dev, const char *name)
{
struct nvmem_device *nvmemp;
struct device_node *np, *nvmem_node = dev->parent->of_node;
u32 offset;
u32 cell_val = CELL_NOT_USED;
/* should never fail since the nvmem driver registers this child */
nvmemp = nvmem_device_find(nvmem_node, device_match_of_node);
if (IS_ERR(nvmemp))
goto out;
np = of_get_child_by_name(nvmem_node, name);
if (!np)
goto put_device;
if (of_property_read_u32_index(np, "reg", 0, &offset))
goto put_node;
nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val);
put_node:
of_node_put(np);
put_device:
nvmem_device_put(nvmemp);
out:
return cell_val;
}
static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop)
{
unsigned int i, j;
unsigned int num_cell_data = 0;
u32 cell_data[MAX_CELLS] = {0};
bool match_socinfo;
int match_socinfo_index = -1;
for (i = 0; i < MAX_CELLS; i++) {
cell_data[i] = mtk_socinfo_read_cell(mtk_socinfop->dev, cell_names[i]);
if (cell_data[i] != CELL_NOT_USED)
num_cell_data++;
else
break;
}
if (!num_cell_data)
return -ENOENT;
for (i = 0; i < ARRAY_SIZE(socinfo_data_table); i++) {
match_socinfo = true;
for (j = 0; j < num_cell_data; j++) {
if (cell_data[j] != socinfo_data_table[i].cell_data[j]) {
match_socinfo = false;
break;
}
}
if (match_socinfo) {
mtk_socinfop->socinfo_data = &(socinfo_data_table[i]);
match_socinfo_index = i;
break;
}
}
return match_socinfo_index >= 0 ? match_socinfo_index : -ENOENT;
}
static int mtk_socinfo_probe(struct platform_device *pdev)
{
struct mtk_socinfo *mtk_socinfop;
int ret;
mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL);
if (!mtk_socinfop)
return -ENOMEM;
mtk_socinfop->dev = &pdev->dev;
ret = mtk_socinfo_get_socinfo_data(mtk_socinfop);
if (ret < 0)
return dev_err_probe(mtk_socinfop->dev, ret, "Failed to get socinfo data\n");
ret = mtk_socinfo_create_socinfo_node(mtk_socinfop);
if (ret)
return dev_err_probe(mtk_socinfop->dev, ret, "Cannot create node\n");
platform_set_drvdata(pdev, mtk_socinfop);
return 0;
}
static void mtk_socinfo_remove(struct platform_device *pdev)
{
struct mtk_socinfo *mtk_socinfop = platform_get_drvdata(pdev);
soc_device_unregister(mtk_socinfop->soc_dev);
}
static struct platform_driver mtk_socinfo = {
.probe = mtk_socinfo_probe,
.remove_new = mtk_socinfo_remove,
.driver = {
.name = "mtk-socinfo",
},
};
module_platform_driver(mtk_socinfo);
MODULE_AUTHOR("William-TW LIN <william-tw.lin@mediatek.com>");
MODULE_DESCRIPTION("MediaTek socinfo driver");
MODULE_LICENSE("GPL");
......@@ -268,4 +268,13 @@ config QCOM_INLINE_CRYPTO_ENGINE
tristate
select QCOM_SCM
config QCOM_PBS
tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS"
depends on SPMI
help
This driver supports configuring software programmable boot sequencer (PBS)
trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs.
This module provides the APIs to the client drivers that wants to send the
PBS trigger event to the PBS RAM.
endmenu
# SPDX-License-Identifier: GPL-2.0
CFLAGS_rpmh-rsc.o := -I$(src)
CFLAGS_qcom_aoss.o := -I$(src)
obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
......@@ -34,3 +35,4 @@ obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o
obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o
qcom_ice-objs += ice.o
obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) += qcom_ice.o
obj-$(CONFIG_QCOM_PBS) += qcom-pbs.o
......@@ -399,7 +399,7 @@ static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env)
return add_uevent_var(env, "MODALIAS=apr:%s", adev->name);
}
struct bus_type aprbus = {
const struct bus_type aprbus = {
.name = "aprbus",
.match = apr_device_match,
.probe = apr_device_probe,
......
......@@ -859,6 +859,8 @@ static int llcc_update_act_ctrl(u32 sid,
ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
slice_status, !(slice_status & status),
0, LLCC_STATUS_READ_DELAY);
if (ret)
return ret;
if (drv_data->version >= LLCC_VERSION_4_1_0_0)
ret = regmap_write(drv_data->bcast_regmap, act_clear_reg,
......
......@@ -89,7 +89,6 @@
* @base: Base address of this instance of QUP wrapper core
* @clks: Handle to the primary & optional secondary AHB clocks
* @num_clks: Count of clocks
* @to_core: Core ICC path
*/
struct geni_wrapper {
struct device *dev;
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/spmi.h>
#include <linux/soc/qcom/qcom-pbs.h>
#define PBS_CLIENT_TRIG_CTL 0x42
#define PBS_CLIENT_SW_TRIG_BIT BIT(7)
#define PBS_CLIENT_SCRATCH1 0x50
#define PBS_CLIENT_SCRATCH2 0x51
#define PBS_CLIENT_SCRATCH2_ERROR 0xFF
#define RETRIES 2000
#define DELAY 1100
struct pbs_dev {
struct device *dev;
struct regmap *regmap;
struct mutex lock;
struct device_link *link;
u32 base;
};
static int qcom_pbs_wait_for_ack(struct pbs_dev *pbs, u8 bit_pos)
{
unsigned int val;
int ret;
ret = regmap_read_poll_timeout(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2,
val, val & BIT(bit_pos), DELAY, DELAY * RETRIES);
if (ret < 0) {
dev_err(pbs->dev, "Timeout for PBS ACK/NACK for bit %u\n", bit_pos);
return -ETIMEDOUT;
}
if (val == PBS_CLIENT_SCRATCH2_ERROR) {
ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0);
dev_err(pbs->dev, "NACK from PBS for bit %u\n", bit_pos);
return -EINVAL;
}
dev_dbg(pbs->dev, "PBS sequence for bit %u executed!\n", bit_pos);
return 0;
}
/**
* qcom_pbs_trigger_event() - Trigger the PBS RAM sequence
* @pbs: Pointer to PBS device
* @bitmap: bitmap
*
* This function is used to trigger the PBS RAM sequence to be
* executed by the client driver.
*
* The PBS trigger sequence involves
* 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1
* 2. Initiating the SW PBS trigger
* 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the
* completion of the sequence.
* 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute
*
* Return: 0 on success, < 0 on failure
*/
int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap)
{
unsigned int val;
u16 bit_pos;
int ret;
if (WARN_ON(!bitmap))
return -EINVAL;
if (IS_ERR_OR_NULL(pbs))
return -EINVAL;
mutex_lock(&pbs->lock);
ret = regmap_read(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, &val);
if (ret < 0)
goto out;
if (val == PBS_CLIENT_SCRATCH2_ERROR) {
/* PBS error - clear SCRATCH2 register */
ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0);
if (ret < 0)
goto out;
}
for (bit_pos = 0; bit_pos < 8; bit_pos++) {
if (!(bitmap & BIT(bit_pos)))
continue;
/* Clear the PBS sequence bit position */
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2,
BIT(bit_pos), 0);
if (ret < 0)
goto out_clear_scratch1;
/* Set the PBS sequence bit position */
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1,
BIT(bit_pos), BIT(bit_pos));
if (ret < 0)
goto out_clear_scratch1;
/* Initiate the SW trigger */
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_TRIG_CTL,
PBS_CLIENT_SW_TRIG_BIT, PBS_CLIENT_SW_TRIG_BIT);
if (ret < 0)
goto out_clear_scratch1;
ret = qcom_pbs_wait_for_ack(pbs, bit_pos);
if (ret < 0)
goto out_clear_scratch1;
/* Clear the PBS sequence bit position */
regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0);
regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
}
out_clear_scratch1:
/* Clear all the requested bitmap */
ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0);
out:
mutex_unlock(&pbs->lock);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_pbs_trigger_event);
/**
* get_pbs_client_device() - Get the PBS device used by client
* @dev: Client device
*
* This function is used to get the PBS device that is being
* used by the client.
*
* Return: pbs_dev on success, ERR_PTR on failure
*/
struct pbs_dev *get_pbs_client_device(struct device *dev)
{
struct device_node *pbs_dev_node;
struct platform_device *pdev;
struct pbs_dev *pbs;
pbs_dev_node = of_parse_phandle(dev->of_node, "qcom,pbs", 0);
if (!pbs_dev_node) {
dev_err(dev, "Missing qcom,pbs property\n");
return ERR_PTR(-ENODEV);
}
pdev = of_find_device_by_node(pbs_dev_node);
if (!pdev) {
dev_err(dev, "Unable to find PBS dev_node\n");
pbs = ERR_PTR(-EPROBE_DEFER);
goto out;
}
pbs = platform_get_drvdata(pdev);
if (!pbs) {
dev_err(dev, "Cannot get pbs instance from %s\n", dev_name(&pdev->dev));
platform_device_put(pdev);
pbs = ERR_PTR(-EPROBE_DEFER);
goto out;
}
pbs->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
if (!pbs->link) {
dev_err(&pdev->dev, "Failed to create device link to consumer %s\n", dev_name(dev));
platform_device_put(pdev);
pbs = ERR_PTR(-EINVAL);
goto out;
}
out:
of_node_put(pbs_dev_node);
return pbs;
}
EXPORT_SYMBOL_GPL(get_pbs_client_device);
static int qcom_pbs_probe(struct platform_device *pdev)
{
struct pbs_dev *pbs;
u32 val;
int ret;
pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL);
if (!pbs)
return -ENOMEM;
pbs->dev = &pdev->dev;
pbs->regmap = dev_get_regmap(pbs->dev->parent, NULL);
if (!pbs->regmap) {
dev_err(pbs->dev, "Couldn't get parent's regmap\n");
return -EINVAL;
}
ret = device_property_read_u32(pbs->dev, "reg", &val);
if (ret < 0) {
dev_err(pbs->dev, "Couldn't find reg, ret = %d\n", ret);
return ret;
}
pbs->base = val;
mutex_init(&pbs->lock);
platform_set_drvdata(pdev, pbs);
return 0;
}
static const struct of_device_id qcom_pbs_match_table[] = {
{ .compatible = "qcom,pbs" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_pbs_match_table);
static struct platform_driver qcom_pbs_driver = {
.driver = {
.name = "qcom-pbs",
.of_match_table = qcom_pbs_match_table,
},
.probe = qcom_pbs_probe,
};
module_platform_driver(qcom_pbs_driver)
MODULE_DESCRIPTION("QCOM PBS DRIVER");
MODULE_LICENSE("GPL");
......@@ -3,6 +3,7 @@
* Copyright (c) 2019, Linaro Ltd
*/
#include <linux/clk-provider.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_client.h>
......@@ -13,6 +14,9 @@
#include <linux/slab.h>
#include <linux/soc/qcom/qcom_aoss.h>
#define CREATE_TRACE_POINTS
#include "trace-aoss.h"
#define QMP_DESC_MAGIC 0x0
#define QMP_DESC_VERSION 0x4
#define QMP_DESC_FEATURES 0x8
......@@ -44,6 +48,8 @@
#define QMP_NUM_COOLING_RESOURCES 2
#define QMP_DEBUGFS_FILES 4
static bool qmp_cdev_max_state = 1;
struct qmp_cooling_device {
......@@ -65,6 +71,8 @@ struct qmp_cooling_device {
* @tx_lock: provides synchronization between multiple callers of qmp_send()
* @qdss_clk: QDSS clock hw struct
* @cooling_devs: thermal cooling devices
* @debugfs_root: directory for the developer/tester interface
* @debugfs_files: array of individual debugfs entries under debugfs_root
*/
struct qmp {
void __iomem *msgram;
......@@ -82,6 +90,8 @@ struct qmp {
struct clk_hw qdss_clk;
struct qmp_cooling_device *cooling_devs;
struct dentry *debugfs_root;
struct dentry *debugfs_files[QMP_DEBUGFS_FILES];
};
static void qmp_kick(struct qmp *qmp)
......@@ -214,7 +224,7 @@ static bool qmp_message_empty(struct qmp *qmp)
*
* Return: 0 on success, negative errno on failure
*/
int qmp_send(struct qmp *qmp, const char *fmt, ...)
int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...)
{
char buf[QMP_MSG_LEN];
long time_left;
......@@ -235,6 +245,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
mutex_lock(&qmp->tx_lock);
trace_aoss_send(buf);
/* The message RAM only implements 32-bit accesses */
__iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32),
buf, sizeof(buf) / sizeof(u32));
......@@ -256,6 +268,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
ret = 0;
}
trace_aoss_send_done(buf, ret);
mutex_unlock(&qmp->tx_lock);
return ret;
......@@ -475,6 +489,91 @@ void qmp_put(struct qmp *qmp)
}
EXPORT_SYMBOL_GPL(qmp_put);
struct qmp_debugfs_entry {
const char *name;
const char *fmt;
bool is_bool;
const char *true_val;
const char *false_val;
};
static const struct qmp_debugfs_entry qmp_debugfs_entries[QMP_DEBUGFS_FILES] = {
{ "ddr_frequency_mhz", "{class: ddr, res: fixed, val: %u}", false },
{ "prevent_aoss_sleep", "{class: aoss_slp, res: sleep: %s}", true, "enable", "disable" },
{ "prevent_cx_collapse", "{class: cx_mol, res: cx, val: %s}", true, "mol", "off" },
{ "prevent_ddr_collapse", "{class: ddr_mol, res: ddr, val: %s}", true, "mol", "off" },
};
static ssize_t qmp_debugfs_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *pos)
{
const struct qmp_debugfs_entry *entry = NULL;
struct qmp *qmp = file->private_data;
char buf[QMP_MSG_LEN];
unsigned int uint_val;
const char *str_val;
bool bool_val;
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) {
if (qmp->debugfs_files[i] == file->f_path.dentry) {
entry = &qmp_debugfs_entries[i];
break;
}
}
if (WARN_ON(!entry))
return -EFAULT;
if (entry->is_bool) {
ret = kstrtobool_from_user(user_buf, count, &bool_val);
if (ret)
return ret;
str_val = bool_val ? entry->true_val : entry->false_val;
ret = snprintf(buf, sizeof(buf), entry->fmt, str_val);
if (ret >= sizeof(buf))
return -EINVAL;
} else {
ret = kstrtou32_from_user(user_buf, count, 0, &uint_val);
if (ret)
return ret;
ret = snprintf(buf, sizeof(buf), entry->fmt, uint_val);
if (ret >= sizeof(buf))
return -EINVAL;
}
ret = qmp_send(qmp, buf);
if (ret < 0)
return ret;
return count;
}
static const struct file_operations qmp_debugfs_fops = {
.open = simple_open,
.write = qmp_debugfs_write,
};
static void qmp_debugfs_create(struct qmp *qmp)
{
const struct qmp_debugfs_entry *entry;
int i;
qmp->debugfs_root = debugfs_create_dir("qcom_aoss", NULL);
for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) {
entry = &qmp_debugfs_entries[i];
qmp->debugfs_files[i] = debugfs_create_file(entry->name, 0200,
qmp->debugfs_root,
qmp,
&qmp_debugfs_fops);
}
}
static int qmp_probe(struct platform_device *pdev)
{
struct qmp *qmp;
......@@ -523,6 +622,8 @@ static int qmp_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, qmp);
qmp_debugfs_create(qmp);
return 0;
err_close_qmp:
......@@ -537,6 +638,8 @@ static void qmp_remove(struct platform_device *pdev)
{
struct qmp *qmp = platform_get_drvdata(pdev);
debugfs_remove_recursive(qmp->debugfs_root);
qmp_qdss_clk_remove(qmp);
qmp_cooling_devices_remove(qmp);
......
......@@ -655,8 +655,6 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
{
struct smem_partition *part;
unsigned long flags;
int ret;
void *ptr = ERR_PTR(-EPROBE_DEFER);
if (!__smem)
......@@ -665,12 +663,6 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
if (WARN_ON(item >= __smem->item_count))
return ERR_PTR(-EINVAL);
ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
HWSPINLOCK_TIMEOUT,
&flags);
if (ret)
return ERR_PTR(ret);
if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
part = &__smem->partitions[host];
ptr = qcom_smem_get_private(__smem, part, item, size);
......@@ -681,10 +673,7 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
ptr = qcom_smem_get_global(__smem, item, size);
}
hwspin_unlock_irqrestore(__smem->hwlock, &flags);
return ptr;
}
EXPORT_SYMBOL_GPL(qcom_smem_get);
......
......@@ -58,8 +58,8 @@
* @valid_entries: number of allocated entries
* @flags:
* @entries: individual communication entries
* @name: name of the entry
* @value: content of the entry
* @entries.name: name of the entry
* @entries.value: content of the entry
*/
struct smp2p_smem_item {
u32 magic;
......@@ -275,6 +275,8 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
*
* Handle notifications from the remote side to handle newly allocated entries
* or any changes to the state bits of existing entries.
*
* Return: %IRQ_HANDLED
*/
static irqreturn_t qcom_smp2p_intr(int irq, void *data)
{
......
......@@ -124,7 +124,7 @@ static const char *const pmic_models[] = {
[50] = "PM8350B",
[51] = "PMR735A",
[52] = "PMR735B",
[55] = "PM2250",
[55] = "PM4125",
[58] = "PM8450",
[65] = "PM8010",
[69] = "PM8550VS",
......@@ -424,8 +424,11 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(IPQ9510) },
{ qcom_board_id(QRB4210) },
{ qcom_board_id(QRB2210) },
{ qcom_board_id(SM8475) },
{ qcom_board_id(SM8475P) },
{ qcom_board_id(SA8775P) },
{ qcom_board_id(QRU1000) },
{ qcom_board_id(SM8475_2) },
{ qcom_board_id(QDU1000) },
{ qcom_board_id(SM8650) },
{ qcom_board_id(SM4450) },
......@@ -437,6 +440,8 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(IPQ5322) },
{ qcom_board_id(IPQ5312) },
{ qcom_board_id(IPQ5302) },
{ qcom_board_id(QCS8550) },
{ qcom_board_id(QCM8550) },
{ qcom_board_id(IPQ5300) },
};
......
......@@ -6,20 +6,40 @@
* SAW power controller driver
*/
#include <linux/kernel.h>
#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/linear_range.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/regulator/driver.h>
#include <soc/qcom/spm.h>
#define FIELD_SET(current, mask, val) \
(((current) & ~(mask)) | FIELD_PREP((mask), (val)))
#define SPM_CTL_INDEX 0x7f
#define SPM_CTL_INDEX_SHIFT 4
#define SPM_CTL_EN BIT(0)
/* These registers might be specific to SPM 1.1 */
#define SPM_VCTL_VLVL GENMASK(7, 0)
#define SPM_PMIC_DATA_0_VLVL GENMASK(7, 0)
#define SPM_PMIC_DATA_1_MIN_VSEL GENMASK(5, 0)
#define SPM_PMIC_DATA_1_MAX_VSEL GENMASK(21, 16)
#define SPM_1_1_AVS_CTL_AVS_ENABLED BIT(27)
#define SPM_AVS_CTL_MAX_VLVL GENMASK(22, 17)
#define SPM_AVS_CTL_MIN_VLVL GENMASK(15, 10)
enum spm_reg {
SPM_REG_CFG,
SPM_REG_SPM_CTL,
......@@ -29,13 +49,44 @@ enum spm_reg {
SPM_REG_PMIC_DATA_1,
SPM_REG_VCTL,
SPM_REG_SEQ_ENTRY,
SPM_REG_SPM_STS,
SPM_REG_STS0,
SPM_REG_STS1,
SPM_REG_PMIC_STS,
SPM_REG_AVS_CTL,
SPM_REG_AVS_LIMIT,
SPM_REG_RST,
SPM_REG_NR,
};
#define MAX_PMIC_DATA 2
#define MAX_SEQ_DATA 64
struct spm_reg_data {
const u16 *reg_offset;
u32 spm_cfg;
u32 spm_dly;
u32 pmic_dly;
u32 pmic_data[MAX_PMIC_DATA];
u32 avs_ctl;
u32 avs_limit;
u8 seq[MAX_SEQ_DATA];
u8 start_index[PM_SLEEP_MODE_NR];
smp_call_func_t set_vdd;
/* for now we support only a single range */
struct linear_range *range;
unsigned int ramp_delay;
unsigned int init_uV;
};
struct spm_driver_data {
void __iomem *reg_base;
const struct spm_reg_data *reg_data;
struct device *dev;
unsigned int volt_sel;
int reg_cpu;
};
static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = {
[SPM_REG_AVS_CTL] = 0x904,
[SPM_REG_AVS_LIMIT] = 0x908,
......@@ -169,6 +220,10 @@ static const struct spm_reg_data spm_reg_8226_cpu = {
static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
[SPM_REG_CFG] = 0x08,
[SPM_REG_STS0] = 0x0c,
[SPM_REG_STS1] = 0x10,
[SPM_REG_VCTL] = 0x14,
[SPM_REG_AVS_CTL] = 0x18,
[SPM_REG_SPM_CTL] = 0x20,
[SPM_REG_PMIC_DLY] = 0x24,
[SPM_REG_PMIC_DATA_0] = 0x28,
......@@ -176,7 +231,12 @@ static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
[SPM_REG_SEQ_ENTRY] = 0x80,
};
static void smp_set_vdd_v1_1(void *data);
/* SPM register data for 8064 */
static struct linear_range spm_v1_1_regulator_range =
REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500);
static const struct spm_reg_data spm_reg_8064_cpu = {
.reg_offset = spm_reg_offset_v1_1,
.spm_cfg = 0x1F,
......@@ -187,6 +247,10 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
.start_index[PM_SLEEP_MODE_STBY] = 0,
.start_index[PM_SLEEP_MODE_SPC] = 2,
.set_vdd = smp_set_vdd_v1_1,
.range = &spm_v1_1_regulator_range,
.init_uV = 1300000,
.ramp_delay = 1250,
};
static inline void spm_register_write(struct spm_driver_data *drv,
......@@ -238,6 +302,178 @@ void spm_set_low_power_mode(struct spm_driver_data *drv,
spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
}
static int spm_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
{
struct spm_driver_data *drv = rdev_get_drvdata(rdev);
drv->volt_sel = selector;
/* Always do the SAW register writes on the corresponding CPU */
return smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true);
}
static int spm_get_voltage_sel(struct regulator_dev *rdev)
{
struct spm_driver_data *drv = rdev_get_drvdata(rdev);
return drv->volt_sel;
}
static const struct regulator_ops spm_reg_ops = {
.set_voltage_sel = spm_set_voltage_sel,
.get_voltage_sel = spm_get_voltage_sel,
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
static void smp_set_vdd_v1_1(void *data)
{
struct spm_driver_data *drv = data;
unsigned int vctl, data0, data1, avs_ctl, sts;
unsigned int vlevel, volt_sel;
bool avs_enabled;
volt_sel = drv->volt_sel;
vlevel = volt_sel | 0x80; /* band */
avs_ctl = spm_register_read(drv, SPM_REG_AVS_CTL);
vctl = spm_register_read(drv, SPM_REG_VCTL);
data0 = spm_register_read(drv, SPM_REG_PMIC_DATA_0);
data1 = spm_register_read(drv, SPM_REG_PMIC_DATA_1);
avs_enabled = avs_ctl & SPM_1_1_AVS_CTL_AVS_ENABLED;
/* If AVS is enabled, switch it off during the voltage change */
if (avs_enabled) {
avs_ctl &= ~SPM_1_1_AVS_CTL_AVS_ENABLED;
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
}
/* Kick the state machine back to idle */
spm_register_write(drv, SPM_REG_RST, 1);
vctl = FIELD_SET(vctl, SPM_VCTL_VLVL, vlevel);
data0 = FIELD_SET(data0, SPM_PMIC_DATA_0_VLVL, vlevel);
data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MIN_VSEL, volt_sel);
data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MAX_VSEL, volt_sel);
spm_register_write(drv, SPM_REG_VCTL, vctl);
spm_register_write(drv, SPM_REG_PMIC_DATA_0, data0);
spm_register_write(drv, SPM_REG_PMIC_DATA_1, data1);
if (read_poll_timeout_atomic(spm_register_read,
sts, sts == vlevel,
1, 200, false,
drv, SPM_REG_STS1)) {
dev_err_ratelimited(drv->dev, "timeout setting the voltage (%x %x)!\n", sts, vlevel);
goto enable_avs;
}
if (avs_enabled) {
unsigned int max_avs = volt_sel;
unsigned int min_avs = max(max_avs, 4U) - 4;
avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MIN_VLVL, min_avs);
avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MAX_VLVL, max_avs);
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
}
enable_avs:
if (avs_enabled) {
avs_ctl |= SPM_1_1_AVS_CTL_AVS_ENABLED;
spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl);
}
}
static int spm_get_cpu(struct device *dev)
{
int cpu;
bool found;
for_each_possible_cpu(cpu) {
struct device_node *cpu_node, *saw_node;
cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
continue;
saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
found = (saw_node == dev->of_node);
of_node_put(saw_node);
of_node_put(cpu_node);
if (found)
return cpu;
}
/* L2 SPM is not bound to any CPU, voltage setting is not supported */
return -EOPNOTSUPP;
}
static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv)
{
struct regulator_config config = {
.dev = dev,
.driver_data = drv,
};
struct regulator_desc *rdesc;
struct regulator_dev *rdev;
int ret;
bool found;
if (!drv->reg_data->set_vdd)
return 0;
rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL);
if (!rdesc)
return -ENOMEM;
rdesc->name = "spm";
rdesc->of_match = of_match_ptr("regulator");
rdesc->type = REGULATOR_VOLTAGE;
rdesc->owner = THIS_MODULE;
rdesc->ops = &spm_reg_ops;
rdesc->linear_ranges = drv->reg_data->range;
rdesc->n_linear_ranges = 1;
rdesc->n_voltages = rdesc->linear_ranges[rdesc->n_linear_ranges - 1].max_sel + 1;
rdesc->ramp_delay = drv->reg_data->ramp_delay;
ret = spm_get_cpu(dev);
if (ret < 0)
return ret;
drv->reg_cpu = ret;
dev_dbg(dev, "SAW2 bound to CPU %d\n", drv->reg_cpu);
/*
* Program initial voltage, otherwise registration will also try
* setting the voltage, which might result in undervolting the CPU.
*/
drv->volt_sel = DIV_ROUND_UP(drv->reg_data->init_uV - rdesc->min_uV,
rdesc->uV_step);
ret = linear_range_get_selector_high(drv->reg_data->range,
drv->reg_data->init_uV,
&drv->volt_sel,
&found);
if (ret) {
dev_err(dev, "Initial uV value out of bounds\n");
return ret;
}
/* Always do the SAW register writes on the corresponding CPU */
smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true);
rdev = devm_regulator_register(dev, rdesc, &config);
if (IS_ERR(rdev)) {
dev_err(dev, "failed to register regulator\n");
return PTR_ERR(rdev);
}
return 0;
}
static const struct of_device_id spm_match_table[] = {
{ .compatible = "qcom,sdm660-gold-saw2-v4.1-l2",
.data = &spm_reg_660_gold_l2 },
......@@ -288,6 +524,7 @@ static int spm_dev_probe(struct platform_device *pdev)
return -ENODEV;
drv->reg_data = match_id->data;
drv->dev = &pdev->dev;
platform_set_drvdata(pdev, drv);
/* Write the SPM sequences first.. */
......@@ -315,6 +552,9 @@ static int spm_dev_probe(struct platform_device *pdev)
if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL])
spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
if (IS_ENABLED(CONFIG_REGULATOR))
return spm_register_regulator(&pdev->dev, drv);
return 0;
}
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM qcom_aoss
#if !defined(_TRACE_QCOM_AOSS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_QCOM_AOSS_H
#include <linux/tracepoint.h>
TRACE_EVENT(aoss_send,
TP_PROTO(const char *msg),
TP_ARGS(msg),
TP_STRUCT__entry(
__string(msg, msg)
),
TP_fast_assign(
__assign_str(msg, msg);
),
TP_printk("%s", __get_str(msg))
);
TRACE_EVENT(aoss_send_done,
TP_PROTO(const char *msg, int ret),
TP_ARGS(msg, ret),
TP_STRUCT__entry(
__string(msg, msg)
__field(int, ret)
),
TP_fast_assign(
__assign_str(msg, msg);
__entry->ret = ret;
),
TP_printk("%s: %d", __get_str(msg), __entry->ret)
);
#endif /* _TRACE_QCOM_AOSS_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace-aoss
#include <trace/define_trace.h>
......@@ -34,6 +34,10 @@ config ARCH_RCAR_GEN3
select SYS_SUPPORTS_SH_CMT
select SYS_SUPPORTS_SH_TMU
config ARCH_RCAR_GEN4
bool
select ARCH_RCAR_GEN3
config ARCH_RMOBILE
bool
select PM
......@@ -240,7 +244,7 @@ config ARCH_R8A77961
config ARCH_R8A779F0
bool "ARM64 Platform support for R-Car S4-8"
select ARCH_RCAR_GEN3
select ARCH_RCAR_GEN4
select SYSC_R8A779F0
help
This enables support for the Renesas R-Car S4-8 SoC.
......@@ -261,18 +265,25 @@ config ARCH_R8A77970
config ARCH_R8A779A0
bool "ARM64 Platform support for R-Car V3U"
select ARCH_RCAR_GEN3
select ARCH_RCAR_GEN4
select SYSC_R8A779A0
help
This enables support for the Renesas R-Car V3U SoC.
config ARCH_R8A779G0
bool "ARM64 Platform support for R-Car V4H"
select ARCH_RCAR_GEN3
select ARCH_RCAR_GEN4
select SYSC_R8A779G0
help
This enables support for the Renesas R-Car V4H SoC.
config ARCH_R8A779H0
bool "ARM64 Platform support for R-Car V4M"
select ARCH_RCAR_GEN4
select SYSC_R8A779H0
help
This enables support for the Renesas R-Car V4M SoC.
config ARCH_R8A774C0
bool "ARM64 Platform support for RZ/G2E"
select ARCH_RCAR_GEN3
......
......@@ -117,6 +117,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u },
{ .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
{ .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 },
{ .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 },
{ /* sentinel */ }
};
......
......@@ -270,6 +270,11 @@ static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = {
.id = 0x5c,
};
static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = {
.family = &fam_rcar_gen4,
.id = 0x5d,
};
static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = {
.family = &fam_shmobile,
.id = 0x37,
......@@ -380,6 +385,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = {
#ifdef CONFIG_ARCH_R8A779G0
{ .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h },
#endif
#ifdef CONFIG_ARCH_R8A779H0
{ .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m },
#endif
#ifdef CONFIG_ARCH_R9A07G043
#ifdef CONFIG_RISCV
{ .compatible = "renesas,r9a07g043", .data = &soc_rz_five },
......
......@@ -42,6 +42,7 @@ config EXYNOS_PMU
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS
select MFD_CORE
select REGMAP_MMIO
# There is no need to enable these drivers for ARMv8
config EXYNOS_PMU_ARM_DRIVERS
......
......@@ -5,6 +5,7 @@
//
// Exynos - CPU PMU(Power Management Unit) support
#include <linux/arm-smccc.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mfd/core.h>
......@@ -12,19 +13,134 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/soc/samsung/exynos-pmu.h>
#include "exynos-pmu.h"
#define PMUALIVE_MASK GENMASK(13, 0)
#define TENSOR_SET_BITS (BIT(15) | BIT(14))
#define TENSOR_CLR_BITS BIT(15)
#define TENSOR_SMC_PMU_SEC_REG 0x82000504
#define TENSOR_PMUREG_READ 0
#define TENSOR_PMUREG_WRITE 1
#define TENSOR_PMUREG_RMW 2
struct exynos_pmu_context {
struct device *dev;
const struct exynos_pmu_data *pmu_data;
struct regmap *pmureg;
};
void __iomem *pmu_base_addr;
static struct exynos_pmu_context *pmu_context;
/* forward declaration */
static struct platform_driver exynos_pmu_driver;
/*
* Tensor SoCs are configured so that PMU_ALIVE registers can only be written
* from EL3, but are still read accessible. As Linux needs to write some of
* these registers, the following functions are provided and exposed via
* regmap.
*
* Note: This SMC interface is known to be implemented on gs101 and derivative
* SoCs.
*/
/* Write to a protected PMU register. */
static int tensor_sec_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct arm_smccc_res res;
unsigned long pmu_base = (unsigned long)context;
arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg,
TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res);
/* returns -EINVAL if access isn't allowed or 0 */
if (res.a0)
pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0);
return (int)res.a0;
}
/* Read/Modify/Write a protected PMU register. */
static int tensor_sec_reg_rmw(void *context, unsigned int reg,
unsigned int mask, unsigned int val)
{
struct arm_smccc_res res;
unsigned long pmu_base = (unsigned long)context;
arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg,
TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res);
/* returns -EINVAL if access isn't allowed or 0 */
if (res.a0)
pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0);
return (int)res.a0;
}
/*
* Read a protected PMU register. All PMU registers can be read by Linux.
* Note: The SMC read register is not used, as only registers that can be
* written are readable via SMC.
*/
static int tensor_sec_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
*val = pmu_raw_readl(reg);
return 0;
}
/*
* For SoCs that have set/clear bit hardware this function can be used when
* the PMU register will be accessed by multiple masters.
*
* For example, to set bits 13:8 in PMU reg offset 0x3e80
* tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00);
*
* Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80
* tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00);
*/
static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val,
u32 mask)
{
int ret;
unsigned int i;
for (i = 0; i < 32; i++) {
if (!(mask & BIT(i)))
continue;
offset &= ~TENSOR_SET_BITS;
if (val & BIT(i))
offset |= TENSOR_SET_BITS;
else
offset |= TENSOR_CLR_BITS;
ret = tensor_sec_reg_write(ctx, offset, i);
if (ret)
return ret;
}
return ret;
}
static int tensor_sec_update_bits(void *ctx, unsigned int reg,
unsigned int mask, unsigned int val)
{
/*
* Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF)
* as the target registers can be accessed by multiple masters.
*/
if (reg > PMUALIVE_MASK)
return tensor_sec_reg_rmw(ctx, reg, mask, val);
return tensor_set_bits_atomic(ctx, reg, val, mask);
}
void pmu_raw_writel(u32 val, u32 offset)
{
......@@ -75,11 +191,41 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
#define exynos_pmu_data_arm_ptr(data) NULL
#endif
static const struct regmap_config regmap_smccfg = {
.name = "pmu_regs",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
.use_single_read = true,
.use_single_write = true,
.reg_read = tensor_sec_reg_read,
.reg_write = tensor_sec_reg_write,
.reg_update_bits = tensor_sec_update_bits,
};
static const struct regmap_config regmap_mmiocfg = {
.name = "pmu_regs",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
.use_single_read = true,
.use_single_write = true,
};
static const struct exynos_pmu_data gs101_pmu_data = {
.pmu_secure = true
};
/*
* PMU platform driver and devicetree bindings.
*/
static const struct of_device_id exynos_pmu_of_device_ids[] = {
{
.compatible = "google,gs101-pmu",
.data = &gs101_pmu_data,
}, {
.compatible = "samsung,exynos3250-pmu",
.data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
}, {
......@@ -113,19 +259,75 @@ static const struct mfd_cell exynos_pmu_devs[] = {
{ .name = "exynos-clkout", },
};
/**
* exynos_get_pmu_regmap() - Obtain pmureg regmap
*
* Find the pmureg regmap previously configured in probe() and return regmap
* pointer.
*
* Return: A pointer to regmap if found or ERR_PTR error value.
*/
struct regmap *exynos_get_pmu_regmap(void)
{
struct device_node *np = of_find_matching_node(NULL,
exynos_pmu_of_device_ids);
if (np)
return syscon_node_to_regmap(np);
return exynos_get_pmu_regmap_by_phandle(np, NULL);
return ERR_PTR(-ENODEV);
}
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap);
/**
* exynos_get_pmu_regmap_by_phandle() - Obtain pmureg regmap via phandle
* @np: Device node holding PMU phandle property
* @propname: Name of property holding phandle value
*
* Find the pmureg regmap previously configured in probe() and return regmap
* pointer.
*
* Return: A pointer to regmap if found or ERR_PTR error value.
*/
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
const char *propname)
{
struct exynos_pmu_context *ctx;
struct device_node *pmu_np;
struct device *dev;
if (propname)
pmu_np = of_parse_phandle(np, propname, 0);
else
pmu_np = np;
if (!pmu_np)
return ERR_PTR(-ENODEV);
/*
* Determine if exynos-pmu device has probed and therefore regmap
* has been created and can be returned to the caller. Otherwise we
* return -EPROBE_DEFER.
*/
dev = driver_find_device_by_of_node(&exynos_pmu_driver.driver,
(void *)pmu_np);
if (propname)
of_node_put(pmu_np);
if (!dev)
return ERR_PTR(-EPROBE_DEFER);
ctx = dev_get_drvdata(dev);
return ctx->pmureg;
}
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle);
static int exynos_pmu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regmap_config pmu_regmcfg;
struct regmap *regmap;
struct resource *res;
int ret;
pmu_base_addr = devm_platform_ioremap_resource(pdev, 0);
......@@ -137,9 +339,38 @@ static int exynos_pmu_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!pmu_context)
return -ENOMEM;
pmu_context->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
pmu_context->pmu_data = of_device_get_match_data(dev);
/* For SoCs that secure PMU register writes use custom regmap */
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_secure) {
pmu_regmcfg = regmap_smccfg;
pmu_regmcfg.max_register = resource_size(res) -
pmu_regmcfg.reg_stride;
/* Need physical address for SMC call */
regmap = devm_regmap_init(dev, NULL,
(void *)(uintptr_t)res->start,
&pmu_regmcfg);
} else {
/* All other SoCs use a MMIO regmap */
pmu_regmcfg = regmap_mmiocfg;
pmu_regmcfg.max_register = resource_size(res) -
pmu_regmcfg.reg_stride;
regmap = devm_regmap_init_mmio(dev, pmu_base_addr,
&pmu_regmcfg);
}
if (IS_ERR(regmap))
return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
"regmap init failed\n");
pmu_context->pmureg = regmap;
pmu_context->dev = dev;
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init)
pmu_context->pmu_data->pmu_init();
......
......@@ -21,6 +21,7 @@ struct exynos_pmu_conf {
struct exynos_pmu_data {
const struct exynos_pmu_conf *pmu_config;
const struct exynos_pmu_conf *pmu_config_extra;
bool pmu_secure;
void (*pmu_init)(void);
void (*powerdown_conf)(enum sys_powerdown);
......
......@@ -133,6 +133,11 @@ config ARCH_TEGRA_234_SOC
help
Enable support for the NVIDIA Tegra234 SoC.
config ARCH_TEGRA_241_SOC
bool "NVIDIA Tegra241 SoC"
help
Enable support for the NVIDIA Tegra241 SoC.
endif
endif
......
This diff is collapsed.
......@@ -38,7 +38,8 @@
defined(CONFIG_ARCH_TEGRA_210_SOC) || \
defined(CONFIG_ARCH_TEGRA_186_SOC) || \
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
defined(CONFIG_ARCH_TEGRA_234_SOC)
defined(CONFIG_ARCH_TEGRA_234_SOC) || \
defined(CONFIG_ARCH_TEGRA_241_SOC)
static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset)
{
if (WARN_ON(!fuse->base))
......@@ -678,3 +679,23 @@ const struct tegra_fuse_soc tegra234_fuse_soc = {
.clk_suspend_on = false,
};
#endif
#if defined(CONFIG_ARCH_TEGRA_241_SOC)
static const struct tegra_fuse_info tegra241_fuse_info = {
.read = tegra30_fuse_read,
.size = 0x16008,
.spare = 0xcf0,
};
static const struct nvmem_keepout tegra241_fuse_keepouts[] = {
{ .start = 0xc, .end = 0x1600c }
};
const struct tegra_fuse_soc tegra241_fuse_soc = {
.init = tegra30_fuse_init,
.info = &tegra241_fuse_info,
.keepouts = tegra241_fuse_keepouts,
.num_keepouts = ARRAY_SIZE(tegra241_fuse_keepouts),
.soc_attr_group = &tegra194_soc_attr_group,
};
#endif
......@@ -69,6 +69,7 @@ struct tegra_fuse {
void tegra_init_revision(void);
void tegra_init_apbmisc(void);
void tegra_acpi_init_apbmisc(void);
u32 __init tegra_fuse_read_spare(unsigned int spare);
u32 __init tegra_fuse_read_early(unsigned int offset);
......@@ -123,7 +124,8 @@ extern const struct tegra_fuse_soc tegra186_fuse_soc;
#endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
extern const struct attribute_group tegra194_soc_attr_group;
#endif
......@@ -135,4 +137,8 @@ extern const struct tegra_fuse_soc tegra194_fuse_soc;
extern const struct tegra_fuse_soc tegra234_fuse_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_241_SOC
extern const struct tegra_fuse_soc tegra241_fuse_soc;
#endif
#endif
This diff is collapsed.
This diff is collapsed.
......@@ -48,7 +48,7 @@ Example of usage:
-----------------
This example places the bridge on top of the i.MX WEIM parallel bus, see:
Documentation/devicetree/bindings/bus/imx-weim.txt
Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml
&weim {
controller@0,0 {
......
......@@ -1226,7 +1226,7 @@ static int tee_client_device_uevent(const struct device *dev,
return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id);
}
struct bus_type tee_bus_type = {
const struct bus_type tee_bus_type = {
.name = "tee",
.match = tee_client_device_match,
.uevent = tee_client_device_uevent,
......
......@@ -512,7 +512,6 @@ config S3C2410_WATCHDOG
tristate "S3C6410/S5Pv210/Exynos Watchdog"
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select WATCHDOG_CORE
select MFD_SYSCON if ARCH_EXYNOS
help
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
SoCs. This will reboot the system when the timer expires with
......
......@@ -24,9 +24,9 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/soc/samsung/exynos-pmu.h>
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
......@@ -699,11 +699,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
return ret;
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(wdt->pmureg))
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
"syscon regmap lookup failed.\n");
"PMU regmap lookup failed.\n");
}
wdt_irq = platform_get_irq(pdev, 0);
......
......@@ -252,8 +252,11 @@
#define QCOM_ID_IPQ9510 521
#define QCOM_ID_QRB4210 523
#define QCOM_ID_QRB2210 524
#define QCOM_ID_SM8475 530
#define QCOM_ID_SM8475P 531
#define QCOM_ID_SA8775P 534
#define QCOM_ID_QRU1000 539
#define QCOM_ID_SM8475_2 540
#define QCOM_ID_QDU1000 545
#define QCOM_ID_SM8650 557
#define QCOM_ID_SM4450 568
......@@ -265,6 +268,8 @@
#define QCOM_ID_IPQ5322 593
#define QCOM_ID_IPQ5312 594
#define QCOM_ID_IPQ5302 595
#define QCOM_ID_QCS8550 603
#define QCOM_ID_QCM8550 604
#define QCOM_ID_IPQ5300 624
/*
......
......@@ -209,7 +209,7 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev) { return false; }
#define module_ffa_driver(__ffa_driver) \
module_driver(__ffa_driver, ffa_register, ffa_unregister)
extern struct bus_type ffa_bus_type;
extern const struct bus_type ffa_bus_type;
/* FFA transport related */
struct ffa_partition_info {
......
This diff is collapsed.
......@@ -9,7 +9,7 @@
#include <dt-bindings/soc/qcom,apr.h>
#include <dt-bindings/soc/qcom,gpr.h>
extern struct bus_type aprbus;
extern const struct bus_type aprbus;
#define APR_HDR_LEN(hdr_len) ((hdr_len)/4)
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _QCOM_PBS_H
#define _QCOM_PBS_H
#include <linux/errno.h>
#include <linux/types.h>
struct device_node;
struct pbs_dev;
#if IS_ENABLED(CONFIG_QCOM_PBS)
int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap);
struct pbs_dev *get_pbs_client_device(struct device *client_dev);
#else
static inline int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap)
{
return -ENODEV;
}
static inline struct pbs_dev *get_pbs_client_device(struct device *client_dev)
{
return ERR_PTR(-ENODEV);
}
#endif
#endif
This diff is collapsed.
......@@ -217,6 +217,7 @@ extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
extern void *kvmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp);
extern void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp);
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
extern void argv_free(char **argv);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment