mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge tag 'soc-drivers-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull SoC driver updates from Arnd Bergmann:
"There are a few added drivers, but mostly the normal maintenance to
drivers for firmware, memory controller and other soc specific
hardware:
- The NXP QuickEngine gets modern MSI support, which allows some
cleanups to the GICv3 irqchip chip driver
- A new SoC specific driver for the Renesas R-Car MFIS unit is added,
encapsulating support for the on-chip mailbox and hwspinlock
implementations that are not easily separated into individual
drivers
- The Qualcomm SoC drivers add support for additional SoC
implementations, and flexibility around power management for the
serial-engine driver as well as probing the LLCC driver using
custom hardware descriptions inside of the device itself.
- Added support for the Samsung thermal management unit
- A cleanup to the Tegra 'PMC' driver interfaces to remove legacy
APIs and allow multiple PMC instances everywhere.
- Updates to the TI SCI and KNAS drivers to improve suspend/resume
support.
- Minor driver changes for mediatek, xilinx, allwinner, aspeed,
tegra, broadcom, amd, microchip and starfive specific drivers
- Memory controller updates for Tegra and Renesas for additional SoC
types and other improvements.
- Firmware driver updates for Arm FF-A, SMCCC and SCMI interfaces, to
update driver probing, object lifetimes and address minor bugs"
* tag 'soc-drivers-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (189 commits)
Revert "firmware: zynqmp: Add dynamic CSU register discovery and sysfs interface"
Revert "Documentation: ABI: add sysfs interface for ZynqMP CSU registers"
memory: tegra234: drop dead NULL check in tegra234_mc_icc_aggregate()
memory: tegra264: drop redundant tegra264_mc_icc_aggregate()
memory: tegra186-emc: stop borrowing MC aggregate hook for EMC
soc: aspeed: cleanup dead default for ASPEED_SOCINFO
firmware: tegra: bpmp: Add support for multi-socket platforms
firmware: tegra: bpmp: Propagate debugfs errors
soc/tegra: pmc: Add Tegra238 support
soc/tegra: pmc: Restrict power-off handler to Nexus 7
soc/tegra: pmc: Populate powergate debugfs only when needed
soc/tegra: pmc: Move legacy code behind CONFIG_ARM guard
soc/tegra: pmc: Remove unused legacy functions
soc/tegra: pmc: Create PMC context dynamically
firmware: samsung: acpm: remove compile-testing stubs
firmware: samsung: acpm: Add devm_acpm_get_by_phandle helper
firmware: samsung: acpm: Add TMU protocol support
firmware: samsung: acpm: Make acpm_ops const and access via pointer
firmware: samsung: acpm: Drop redundant _ops suffix in acpm_ops members
firmware: samsung: acpm: Annotate rx_data->cmd with __counted_by_ptr
...
This commit is contained in:
10
CREDITS
10
CREDITS
@@ -3672,7 +3672,17 @@ D: Macintosh IDE Driver
|
||||
|
||||
N: Peter De Schrijver
|
||||
E: stud11@cc4.kuleuven.ac.be
|
||||
E: p2@mind.be
|
||||
E: peter.de-schrijver@nokia.com
|
||||
E: pdeschrijver@nvidia.com
|
||||
E: p2@psychaos.be
|
||||
D: Apollo Domain workstations
|
||||
D: Ariadne and Hydra Amiga Ethernet drivers
|
||||
D: IBM PS/2, Microchannel, and Token Ring support
|
||||
D: Mitsumi CD-ROM driver patches March version
|
||||
D: TWL4030 power management and audio codec driver
|
||||
D: OMAP power management
|
||||
D: NVIDIA Tegra clock and BPMP drivers, among many other things
|
||||
S: Molenbaan 29
|
||||
S: B2240 Zandhoven
|
||||
S: Belgium
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
What: /sys/devices/*/<our-device>/fuse
|
||||
Date: February 2014
|
||||
Contact: Peter De Schrijver <pdeschrijver@nvidia.com>
|
||||
Contact: Thierry Reding <thierry.reding@kernel.org>
|
||||
Description: read-only access to the efuses on Tegra20, Tegra30, Tegra114
|
||||
and Tegra124 SoC's from NVIDIA. The efuses contain write once
|
||||
data programmed at the factory. The data is laid out in 32bit
|
||||
|
||||
@@ -20,7 +20,9 @@ description: |
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,eliza-llcc
|
||||
- qcom,glymur-llcc
|
||||
- qcom,hawi-llcc
|
||||
- qcom,ipq5424-llcc
|
||||
- qcom,kaanapali-llcc
|
||||
- qcom,qcs615-llcc
|
||||
@@ -35,6 +37,7 @@ properties:
|
||||
- qcom,sc8280xp-llcc
|
||||
- qcom,sdm670-llcc
|
||||
- qcom,sdm845-llcc
|
||||
- qcom,shikra-llcc
|
||||
- qcom,sm6350-llcc
|
||||
- qcom,sm7150-llcc
|
||||
- qcom,sm8150-llcc
|
||||
@@ -57,6 +60,11 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
memory-region:
|
||||
maxItems: 1
|
||||
description: handle to a reserved-memory node used for firmware-populated
|
||||
SLC/SCT shared memory.
|
||||
|
||||
nvmem-cells:
|
||||
items:
|
||||
- description: Reference to an nvmem node for multi channel DDR
|
||||
@@ -206,6 +214,7 @@ allOf:
|
||||
enum:
|
||||
- qcom,sc7280-llcc
|
||||
- qcom,sdm670-llcc
|
||||
- qcom,shikra-llcc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
@@ -318,6 +327,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,kaanapali-llcc
|
||||
- qcom,hawi-llcc
|
||||
- qcom,sm8450-llcc
|
||||
- qcom,sm8550-llcc
|
||||
- qcom,sm8650-llcc
|
||||
@@ -340,6 +350,39 @@ allOf:
|
||||
- const: llcc3_base
|
||||
- const: llcc_broadcast_base
|
||||
- const: llcc_broadcast_and_base
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,hawi-llcc
|
||||
then:
|
||||
required:
|
||||
- memory-region
|
||||
else:
|
||||
properties:
|
||||
memory-region: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,eliza-llcc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: LLCC0 base register region
|
||||
- description: LLCC2 base register region
|
||||
- description: LLCC broadcast OR register region
|
||||
- description: LLCC broadcast AND register region
|
||||
reg-names:
|
||||
items:
|
||||
- const: llcc0_base
|
||||
- const: llcc2_base
|
||||
- const: llcc_broadcast_base
|
||||
- const: llcc_broadcast_and_base
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ properties:
|
||||
- qcom,scm-apq8084
|
||||
- qcom,scm-eliza
|
||||
- qcom,scm-glymur
|
||||
- qcom,scm-hawi
|
||||
- qcom,scm-ipq4019
|
||||
- qcom,scm-ipq5018
|
||||
- qcom,scm-ipq5210
|
||||
@@ -49,6 +50,7 @@ properties:
|
||||
- qcom,scm-msm8994
|
||||
- qcom,scm-msm8996
|
||||
- qcom,scm-msm8998
|
||||
- qcom,scm-nord
|
||||
- qcom,scm-qcm2290
|
||||
- qcom,scm-qcs615
|
||||
- qcom,scm-qcs8300
|
||||
@@ -208,6 +210,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,scm-eliza
|
||||
- qcom,scm-hawi
|
||||
- qcom,scm-kaanapali
|
||||
- qcom,scm-milos
|
||||
- qcom,scm-sm8450
|
||||
|
||||
@@ -15,6 +15,9 @@ description: |
|
||||
represented by CTRLMMR_xxx_JTAGID register which contains information about
|
||||
SoC id and revision.
|
||||
|
||||
On some SoCs like AM62P, the silicon revision is determined by reading
|
||||
alternative registers via NVMEM cells.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^chipid@[0-9a-f]+$"
|
||||
@@ -26,6 +29,14 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
nvmem-cells:
|
||||
items:
|
||||
- description: Alternate silicon revision register
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: gpsw1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -32,6 +32,7 @@ properties:
|
||||
- nvidia,tegra186-mc
|
||||
- nvidia,tegra194-mc
|
||||
- nvidia,tegra234-mc
|
||||
- nvidia,tegra238-mc
|
||||
- nvidia,tegra264-mc
|
||||
|
||||
reg:
|
||||
@@ -266,6 +267,36 @@ allOf:
|
||||
|
||||
interrupt-names: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: nvidia,tegra238-mc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 10
|
||||
maxItems: 10
|
||||
description: 9 memory controller channels and 1 for stream-id registers
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: sid
|
||||
- const: broadcast
|
||||
- const: ch0
|
||||
- const: ch1
|
||||
- const: ch2
|
||||
- const: ch3
|
||||
- const: ch4
|
||||
- const: ch5
|
||||
- const: ch6
|
||||
- const: ch7
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: MC general interrupt
|
||||
|
||||
interrupt-names: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
||||
@@ -30,6 +30,8 @@ properties:
|
||||
- enum:
|
||||
- renesas,r9a09g056-xspi # RZ/V2N
|
||||
- renesas,r9a09g057-xspi # RZ/V2H(P)
|
||||
- renesas,r9a09g077-xspi # RZ/T2H
|
||||
- renesas,r9a09g087-xspi # RZ/N2H
|
||||
- const: renesas,r9a09g047-xspi
|
||||
|
||||
reg:
|
||||
@@ -53,28 +55,38 @@ properties:
|
||||
- const: err_pulse
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: AHB clock
|
||||
- description: AXI clock
|
||||
- description: SPI clock
|
||||
- description: Double speed SPI clock
|
||||
oneOf:
|
||||
- items:
|
||||
- description: AHB clock
|
||||
- description: AXI clock
|
||||
- description: SPI clock
|
||||
- description: Double speed SPI clock
|
||||
- items:
|
||||
- description: AHB clock
|
||||
- description: SPI clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ahb
|
||||
- const: axi
|
||||
- const: spi
|
||||
- const: spix2
|
||||
oneOf:
|
||||
- items:
|
||||
- const: ahb
|
||||
- const: axi
|
||||
- const: spi
|
||||
- const: spix2
|
||||
- items:
|
||||
- const: ahb
|
||||
- const: spi
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Hardware reset
|
||||
- description: AXI reset
|
||||
|
||||
reset-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: hresetn
|
||||
- const: aresetn
|
||||
@@ -109,6 +121,34 @@ required:
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,r9a09g077-xspi
|
||||
- renesas,r9a09g087-xspi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
maxItems: 2
|
||||
resets:
|
||||
maxItems: 1
|
||||
reset-names:
|
||||
maxItems: 1
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
clock-names:
|
||||
minItems: 4
|
||||
resets:
|
||||
minItems: 2
|
||||
reset-names:
|
||||
minItems: 2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
@@ -26,7 +26,11 @@ description: |
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,mpfs-irqmux
|
||||
oneOf:
|
||||
- items:
|
||||
- const: microchip,pic64gx-irqmux
|
||||
- const: microchip,mpfs-irqmux
|
||||
- const: microchip,mpfs-irqmux
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -25,7 +25,9 @@ properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,eliza-aoss-qmp
|
||||
- qcom,glymur-aoss-qmp
|
||||
- qcom,hawi-aoss-qmp
|
||||
- qcom,kaanapali-aoss-qmp
|
||||
- qcom,milos-aoss-qmp
|
||||
- qcom,qcs615-aoss-qmp
|
||||
|
||||
@@ -32,6 +32,11 @@ properties:
|
||||
- qcom,sm8450-pmic-glink
|
||||
- qcom,sm8550-pmic-glink
|
||||
- const: qcom,pmic-glink
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,hawi-pmic-glink
|
||||
- const: qcom,kaanapali-pmic-glink
|
||||
- const: qcom,pmic-glink
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm7325-pmic-glink
|
||||
@@ -39,6 +44,7 @@ properties:
|
||||
- const: qcom,pmic-glink
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,eliza-pmic-glink
|
||||
- qcom,milos-pmic-glink
|
||||
- qcom,sm8650-pmic-glink
|
||||
- qcom,sm8750-pmic-glink
|
||||
|
||||
@@ -19,7 +19,11 @@ description:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sa8255p-geni-se-qup
|
||||
oneOf:
|
||||
- const: qcom,sa8255p-geni-se-qup
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-se-qup
|
||||
- const: qcom,sa8255p-geni-se-qup
|
||||
|
||||
reg:
|
||||
description: QUP wrapper common register address and length.
|
||||
@@ -49,7 +53,11 @@ patternProperties:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sa8255p-geni-spi
|
||||
oneOf:
|
||||
- const: qcom,sa8255p-geni-spi
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-spi
|
||||
- const: qcom,sa8255p-geni-spi
|
||||
|
||||
"i2c@[0-9a-f]+$":
|
||||
type: object
|
||||
@@ -58,7 +66,11 @@ patternProperties:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sa8255p-geni-i2c
|
||||
oneOf:
|
||||
- const: qcom,sa8255p-geni-i2c
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-i2c
|
||||
- const: qcom,sa8255p-geni-i2c
|
||||
|
||||
"serial@[0-9a-f]+$":
|
||||
type: object
|
||||
@@ -67,9 +79,16 @@ patternProperties:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sa8255p-geni-uart
|
||||
- qcom,sa8255p-geni-debug-uart
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,sa8255p-geni-uart
|
||||
- qcom,sa8255p-geni-debug-uart
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-uart
|
||||
- const: qcom,sa8255p-geni-uart
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-debug-uart
|
||||
- const: qcom,sa8255p-geni-debug-uart
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
@@ -19,25 +19,29 @@ description:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- renesas,r8a779g0-mfis # R-Car V4H
|
||||
- renesas,r8a779h0-mfis # R-Car V4M
|
||||
- renesas,r8a78000-mfis # R-Car X5H (AP<->AP, with PRR)
|
||||
- renesas,r8a78000-mfis-scp # R-Car X5H (AP<->SCP, without PRR)
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: common
|
||||
- const: mboxes
|
||||
|
||||
interrupts:
|
||||
minItems: 32
|
||||
minItems: 12
|
||||
maxItems: 128
|
||||
description:
|
||||
The interrupts raised by the remote doorbells.
|
||||
|
||||
interrupt-names:
|
||||
minItems: 32
|
||||
minItems: 12
|
||||
maxItems: 128
|
||||
description:
|
||||
An interrupt name is constructed with the prefix 'ch'. Then, the
|
||||
@@ -56,6 +60,26 @@ properties:
|
||||
<dt-bindings/soc/renesas,r8a78000-mfis.h>.
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,r8a779g0-mfis
|
||||
- renesas,r8a779h0-mfis
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
reg-names:
|
||||
maxItems: 1
|
||||
interrupts:
|
||||
maxItems: 12
|
||||
interrupt-names:
|
||||
maxItems: 12
|
||||
items:
|
||||
pattern: "^ch[0-9]+e$"
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -63,6 +87,10 @@ allOf:
|
||||
const: renesas,r8a78000-mfis
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
reg-names:
|
||||
minItems: 2
|
||||
interrupts:
|
||||
minItems: 128
|
||||
interrupt-names:
|
||||
@@ -77,9 +105,15 @@ allOf:
|
||||
const: renesas,r8a78000-mfis-scp
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
reg-names:
|
||||
minItems: 2
|
||||
interrupts:
|
||||
minItems: 32
|
||||
maxItems: 32
|
||||
interrupt-names:
|
||||
minItems: 32
|
||||
maxItems: 32
|
||||
items:
|
||||
pattern: "^ch[0-9]+i$"
|
||||
|
||||
@@ -80,6 +80,7 @@ patternProperties:
|
||||
- const: allwinner,sun4i-a10-sram-c1
|
||||
- const: allwinner,sun4i-a10-sram-d
|
||||
- const: allwinner,sun50i-a64-sram-c
|
||||
- const: allwinner,sun50i-h616-ve-sram
|
||||
- items:
|
||||
- enum:
|
||||
- allwinner,sun5i-a13-sram-a3-a4
|
||||
@@ -103,7 +104,9 @@ patternProperties:
|
||||
- allwinner,sun7i-a20-sram-d
|
||||
- const: allwinner,sun4i-a10-sram-d
|
||||
- items:
|
||||
- const: allwinner,sun50i-h6-sram-c
|
||||
- enum:
|
||||
- allwinner,sun50i-h6-sram-c
|
||||
- allwinner,sun50i-h616-sram-c
|
||||
- const: allwinner,sun50i-a64-sram-c
|
||||
|
||||
required:
|
||||
|
||||
@@ -83,6 +83,7 @@ patternProperties:
|
||||
- allwinner,sun4i-a10-sram-d
|
||||
- allwinner,sun9i-a80-smp-sram
|
||||
- allwinner,sun50i-a64-sram-c
|
||||
- allwinner,sun50i-h616-ve-sram
|
||||
- amlogic,meson8-ao-arc-sram
|
||||
- amlogic,meson8b-ao-arc-sram
|
||||
- amlogic,meson8-smp-sram
|
||||
|
||||
@@ -26361,7 +26361,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git
|
||||
N: [^a-z]tegra
|
||||
|
||||
TEGRA CLOCK DRIVER
|
||||
M: Peter De Schrijver <pdeschrijver@nvidia.com>
|
||||
M: Prashant Gaikwad <pgaikwad@nvidia.com>
|
||||
S: Supported
|
||||
F: drivers/clk/tegra/
|
||||
|
||||
@@ -175,6 +175,7 @@ struct tegra_ahci_priv {
|
||||
struct reset_control *sata_cold_rst;
|
||||
/* Needs special handling, cannot use ahci_platform */
|
||||
struct clk *sata_clk;
|
||||
struct tegra_pmc *pmc;
|
||||
struct regulator_bulk_data *supplies;
|
||||
const struct tegra_ahci_soc *soc;
|
||||
};
|
||||
@@ -246,9 +247,10 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)
|
||||
return ret;
|
||||
|
||||
if (!tegra->pdev->dev.pm_domain) {
|
||||
ret = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_SATA,
|
||||
tegra->sata_clk,
|
||||
tegra->sata_rst);
|
||||
ret = tegra_pmc_powergate_sequence_power_up(tegra->pmc,
|
||||
TEGRA_POWERGATE_SATA,
|
||||
tegra->sata_clk,
|
||||
tegra->sata_rst);
|
||||
if (ret)
|
||||
goto disable_regulators;
|
||||
}
|
||||
@@ -269,7 +271,7 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)
|
||||
clk_disable_unprepare(tegra->sata_clk);
|
||||
|
||||
if (!tegra->pdev->dev.pm_domain)
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_SATA);
|
||||
tegra_pmc_powergate_power_off(tegra->pmc, TEGRA_POWERGATE_SATA);
|
||||
|
||||
disable_regulators:
|
||||
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
|
||||
@@ -289,7 +291,7 @@ static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)
|
||||
|
||||
clk_disable_unprepare(tegra->sata_clk);
|
||||
if (!tegra->pdev->dev.pm_domain)
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_SATA);
|
||||
tegra_pmc_powergate_power_off(tegra->pmc, TEGRA_POWERGATE_SATA);
|
||||
|
||||
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
|
||||
}
|
||||
@@ -571,6 +573,11 @@ static int tegra_ahci_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(tegra->sata_clk);
|
||||
}
|
||||
|
||||
tegra->pmc = devm_tegra_pmc_get(&pdev->dev);
|
||||
if (IS_ERR(tegra->pmc))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(tegra->pmc),
|
||||
"failed to get PMC\n");
|
||||
|
||||
tegra->supplies = devm_kcalloc(&pdev->dev,
|
||||
tegra->soc->num_supplies,
|
||||
sizeof(*tegra->supplies), GFP_KERNEL);
|
||||
|
||||
@@ -61,10 +61,6 @@ static const struct msi_domain_template platform_msi_template = {
|
||||
* parent. The parent domain sets up the new domain. The domain has
|
||||
* a fixed size of @nvec. The domain is managed by devres and will
|
||||
* be removed when the device is removed.
|
||||
*
|
||||
* Note: For migration purposes this falls back to the original platform_msi code
|
||||
* up to the point where all platforms have been converted to the MSI
|
||||
* parent model.
|
||||
*/
|
||||
int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg)
|
||||
|
||||
@@ -609,9 +609,8 @@ int dprc_setup(struct fsl_mc_device *mc_dev)
|
||||
{
|
||||
struct device *parent_dev = mc_dev->dev.parent;
|
||||
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
|
||||
struct irq_domain *mc_msi_domain;
|
||||
struct irq_domain *mc_msi_domain = NULL;
|
||||
bool mc_io_created = false;
|
||||
bool msi_domain_set = false;
|
||||
bool uapi_created = false;
|
||||
u16 major_ver, minor_ver;
|
||||
size_t region_size;
|
||||
@@ -652,14 +651,12 @@ int dprc_setup(struct fsl_mc_device *mc_dev)
|
||||
uapi_created = true;
|
||||
}
|
||||
|
||||
mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev);
|
||||
if (!mc_msi_domain) {
|
||||
mc_msi_domain = fsl_mc_get_msi_parent(&mc_dev->dev);
|
||||
if (!mc_msi_domain)
|
||||
dev_warn(&mc_dev->dev,
|
||||
"WARNING: MC bus without interrupt support\n");
|
||||
} else {
|
||||
else
|
||||
dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
|
||||
msi_domain_set = true;
|
||||
}
|
||||
|
||||
error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
|
||||
&mc_dev->mc_handle);
|
||||
@@ -699,8 +696,7 @@ int dprc_setup(struct fsl_mc_device *mc_dev)
|
||||
(void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
|
||||
|
||||
error_cleanup_msi_domain:
|
||||
if (msi_domain_set)
|
||||
dev_set_msi_domain(&mc_dev->dev, NULL);
|
||||
dev_set_msi_domain(&mc_dev->dev, NULL);
|
||||
|
||||
if (mc_io_created) {
|
||||
fsl_destroy_mc_io(mc_dev->mc_io);
|
||||
|
||||
@@ -66,6 +66,13 @@ struct fsl_mc_addr_translation_range {
|
||||
#define GCR1_P1_STOP BIT(31)
|
||||
#define GCR1_P2_STOP BIT(30)
|
||||
|
||||
#define FSL_MC_GSR 0x8
|
||||
#define FSL_MC_GSR_BOOT_DONE BIT(0)
|
||||
#define FSL_MC_GSR_MCS_MASK GENMASK(7, 0)
|
||||
#define FSL_MC_GSR_MCS_ERR_MASK GENMASK(7, 1)
|
||||
#define FSL_MC_GSR_BC_MASK GENMASK(15, 8)
|
||||
#define FSL_MC_GSR_BC_SHIFT 8
|
||||
|
||||
#define FSL_MC_FAPR 0x28
|
||||
#define MC_FAPR_PL BIT(18)
|
||||
#define MC_FAPR_BMT BIT(17)
|
||||
@@ -828,14 +835,12 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
|
||||
} else {
|
||||
/*
|
||||
* A non-DPRC object has to be a child of a DPRC, use the
|
||||
* parent's ICID and interrupt domain.
|
||||
* parent's ICID.
|
||||
*/
|
||||
mc_dev->icid = parent_mc_dev->icid;
|
||||
mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK;
|
||||
mc_dev->dev.dma_mask = &mc_dev->dma_mask;
|
||||
mc_dev->dev.coherent_dma_mask = mc_dev->dma_mask;
|
||||
dev_set_msi_domain(&mc_dev->dev,
|
||||
dev_get_msi_domain(&parent_mc_dev->dev));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -992,6 +997,41 @@ static int get_mc_addr_translation_ranges(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 fsl_mc_read_gsr(struct fsl_mc *mc)
|
||||
{
|
||||
return readl(mc->fsl_mc_regs + FSL_MC_GSR);
|
||||
}
|
||||
|
||||
static int fsl_mc_firmware_check(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_mc *mc = platform_get_drvdata(pdev);
|
||||
u32 gsr, boot_done, boot_code, mcs;
|
||||
|
||||
gsr = fsl_mc_read_gsr(mc);
|
||||
boot_code = (gsr & FSL_MC_GSR_BC_MASK) >> FSL_MC_GSR_BC_SHIFT;
|
||||
if (boot_code == 0xDD) {
|
||||
dev_err(&pdev->dev,
|
||||
"fsl-mc: DPL processing was not started, DPAA2 will not work!\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
boot_done = gsr & FSL_MC_GSR_BOOT_DONE;
|
||||
if (!boot_done) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"fsl-mc: DPL processing in progress, defer probe\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
mcs = gsr & FSL_MC_GSR_MCS_MASK;
|
||||
if (mcs & FSL_MC_GSR_MCS_ERR_MASK) {
|
||||
dev_err(&pdev->dev,
|
||||
"fsl-mc: MC boot completed with error 0x%x\n", mcs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fsl_mc_bus_probe - callback invoked when the root MC bus is being
|
||||
* added
|
||||
@@ -1056,6 +1096,10 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
|
||||
mc->fsl_mc_regs + FSL_MC_GCR1);
|
||||
}
|
||||
|
||||
error = fsl_mc_firmware_check(pdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Get physical address of MC portal for the root DPRC:
|
||||
*/
|
||||
|
||||
@@ -15,53 +15,16 @@
|
||||
|
||||
#include "fsl-mc-private.h"
|
||||
|
||||
#ifdef GENERIC_MSI_DOMAIN_OPS
|
||||
/*
|
||||
* Generate a unique ID identifying the interrupt (only used within the MSI
|
||||
* irqdomain. Combine the icid with the interrupt index.
|
||||
*/
|
||||
static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev,
|
||||
struct msi_desc *desc)
|
||||
static void fsl_mc_write_msi_msg(struct msi_desc *msi_desc, struct msi_msg *msg)
|
||||
{
|
||||
/*
|
||||
* Make the base hwirq value for ICID*10000 so it is readable
|
||||
* as a decimal value in /proc/interrupts.
|
||||
*/
|
||||
return (irq_hw_number_t)(desc->msi_index + (dev->icid * 10000));
|
||||
}
|
||||
|
||||
static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg,
|
||||
struct msi_desc *desc)
|
||||
{
|
||||
arg->desc = desc;
|
||||
arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev),
|
||||
desc);
|
||||
}
|
||||
#else
|
||||
#define fsl_mc_msi_set_desc NULL
|
||||
#endif
|
||||
|
||||
static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info)
|
||||
{
|
||||
struct msi_domain_ops *ops = info->ops;
|
||||
|
||||
if (!ops)
|
||||
return;
|
||||
|
||||
/*
|
||||
* set_desc should not be set by the caller
|
||||
*/
|
||||
if (!ops->set_desc)
|
||||
ops->set_desc = fsl_mc_msi_set_desc;
|
||||
}
|
||||
|
||||
static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev,
|
||||
struct fsl_mc_device_irq *mc_dev_irq,
|
||||
struct msi_desc *msi_desc)
|
||||
{
|
||||
int error;
|
||||
struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev);
|
||||
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
|
||||
struct fsl_mc_device_irq *mc_dev_irq = &mc_bus->irq_resources[msi_desc->msi_index];
|
||||
struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev;
|
||||
struct dprc_irq_cfg irq_cfg;
|
||||
int error;
|
||||
|
||||
msi_desc->msg = *msg;
|
||||
|
||||
/*
|
||||
* msi_desc->msg.address is 0x0 when this function is invoked in
|
||||
@@ -110,118 +73,27 @@ static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: This function is invoked with interrupts disabled
|
||||
*/
|
||||
static void fsl_mc_msi_write_msg(struct irq_data *irq_data,
|
||||
struct msi_msg *msg)
|
||||
{
|
||||
struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
|
||||
struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev);
|
||||
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
|
||||
struct fsl_mc_device_irq *mc_dev_irq =
|
||||
&mc_bus->irq_resources[msi_desc->msi_index];
|
||||
|
||||
msi_desc->msg = *msg;
|
||||
|
||||
/*
|
||||
* Program the MSI (paddr, value) pair in the device:
|
||||
*/
|
||||
__fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq, msi_desc);
|
||||
}
|
||||
|
||||
static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info)
|
||||
{
|
||||
struct irq_chip *chip = info->chip;
|
||||
|
||||
if (!chip)
|
||||
return;
|
||||
|
||||
/*
|
||||
* irq_write_msi_msg should not be set by the caller
|
||||
*/
|
||||
if (!chip->irq_write_msi_msg)
|
||||
chip->irq_write_msi_msg = fsl_mc_msi_write_msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain
|
||||
* @fwnode: Optional firmware node of the interrupt controller
|
||||
* @info: MSI domain info
|
||||
* @parent: Parent irq domain
|
||||
*
|
||||
* Updates the domain and chip ops and creates a fsl-mc MSI
|
||||
* interrupt domain.
|
||||
*
|
||||
* Returns:
|
||||
* A domain pointer or NULL in case of failure.
|
||||
*/
|
||||
struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
|
||||
struct msi_domain_info *info,
|
||||
struct irq_domain *parent)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE)))
|
||||
info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
|
||||
if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
|
||||
fsl_mc_msi_update_dom_ops(info);
|
||||
if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
|
||||
fsl_mc_msi_update_chip_ops(info);
|
||||
info->flags |= MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS;
|
||||
|
||||
domain = msi_create_irq_domain(fwnode, info, parent);
|
||||
if (domain)
|
||||
irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI);
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
struct irq_domain *fsl_mc_find_msi_domain(struct device *dev)
|
||||
struct irq_domain *fsl_mc_get_msi_parent(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
struct device *root_dprc_dev;
|
||||
struct device *bus_dev;
|
||||
struct irq_domain *msi_domain;
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
|
||||
fsl_mc_get_root_dprc(dev, &root_dprc_dev);
|
||||
bus_dev = root_dprc_dev->parent;
|
||||
|
||||
if (bus_dev->of_node) {
|
||||
msi_domain = of_msi_map_get_device_domain(dev,
|
||||
mc_dev->icid,
|
||||
DOMAIN_BUS_FSL_MC_MSI);
|
||||
|
||||
/*
|
||||
* if the msi-map property is missing assume that all the
|
||||
* child containers inherit the domain from the parent
|
||||
*/
|
||||
if (!msi_domain)
|
||||
|
||||
msi_domain = of_msi_get_domain(bus_dev,
|
||||
bus_dev->of_node,
|
||||
DOMAIN_BUS_FSL_MC_MSI);
|
||||
} else {
|
||||
msi_domain = iort_get_device_domain(dev, mc_dev->icid,
|
||||
DOMAIN_BUS_FSL_MC_MSI);
|
||||
}
|
||||
|
||||
return msi_domain;
|
||||
return (bus_dev->of_node ?
|
||||
of_msi_get_domain(bus_dev, bus_dev->of_node, DOMAIN_BUS_NEXUS) :
|
||||
iort_get_device_domain(bus_dev, mc_dev->icid, DOMAIN_BUS_NEXUS));
|
||||
}
|
||||
|
||||
int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count)
|
||||
{
|
||||
int error = msi_setup_device_data(dev);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* NOTE: Calling this function will trigger the invocation of the
|
||||
* its_fsl_mc_msi_prepare() callback
|
||||
*/
|
||||
error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1);
|
||||
|
||||
error = platform_device_msi_init_and_alloc_irqs(dev, irq_count, fsl_mc_write_msi_msg);
|
||||
if (error)
|
||||
dev_err(dev, "Failed to allocate IRQs\n");
|
||||
return error;
|
||||
@@ -231,3 +103,15 @@ void fsl_mc_msi_domain_free_irqs(struct device *dev)
|
||||
{
|
||||
msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
|
||||
}
|
||||
|
||||
u32 fsl_mc_get_msi_id(struct device *dev)
|
||||
{
|
||||
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
|
||||
struct device *root_dprc_dev;
|
||||
|
||||
fsl_mc_get_root_dprc(dev, &root_dprc_dev);
|
||||
|
||||
return (root_dprc_dev->parent->of_node ?
|
||||
of_msi_xlate(dev, NULL, mc_dev->icid) :
|
||||
iort_msi_map_id(dev, mc_dev->icid));
|
||||
}
|
||||
|
||||
@@ -641,7 +641,7 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
|
||||
|
||||
void fsl_mc_msi_domain_free_irqs(struct device *dev);
|
||||
|
||||
struct irq_domain *fsl_mc_find_msi_domain(struct device *dev);
|
||||
struct irq_domain *fsl_mc_get_msi_parent(struct device *dev);
|
||||
|
||||
int __must_check fsl_create_mc_io(struct device *dev,
|
||||
phys_addr_t mc_portal_phys_addr,
|
||||
|
||||
@@ -445,6 +445,9 @@ static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg,
|
||||
struct sunxi_rsb_ctx *ctx = context;
|
||||
struct sunxi_rsb_device *rdev = ctx->rdev;
|
||||
|
||||
if (reg > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,16 +32,14 @@ static int tegra_aconnect_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
aconnect->ape_clk = devm_clk_get(&pdev->dev, "ape");
|
||||
if (IS_ERR(aconnect->ape_clk)) {
|
||||
dev_err(&pdev->dev, "Can't retrieve ape clock\n");
|
||||
return PTR_ERR(aconnect->ape_clk);
|
||||
}
|
||||
if (IS_ERR(aconnect->ape_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(aconnect->ape_clk),
|
||||
"can't retrieve ape clock\n");
|
||||
|
||||
aconnect->apb2ape_clk = devm_clk_get(&pdev->dev, "apb2ape");
|
||||
if (IS_ERR(aconnect->apb2ape_clk)) {
|
||||
dev_err(&pdev->dev, "Can't retrieve apb2ape clock\n");
|
||||
return PTR_ERR(aconnect->apb2ape_clk);
|
||||
}
|
||||
if (IS_ERR(aconnect->apb2ape_clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(aconnect->apb2ape_clk),
|
||||
"can't retrieve apb2ape clock\n");
|
||||
|
||||
dev_set_drvdata(&pdev->dev, aconnect);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
2
drivers/cache/sifive_ccache.c
vendored
2
drivers/cache/sifive_ccache.c
vendored
@@ -124,6 +124,8 @@ static const struct of_device_id sifive_ccache_ids[] = {
|
||||
{ .compatible = "sifive,fu740-c000-ccache" },
|
||||
{ .compatible = "starfive,jh7100-ccache",
|
||||
.data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS | QUIRK_BROKEN_DATA_UNCORR) },
|
||||
{ .compatible = "starfive,jh7110-ccache",
|
||||
.data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS) },
|
||||
{ .compatible = "sifive,ccache0" },
|
||||
{ /* end of table */ }
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
#define NOT_ATOMIC false
|
||||
#define ATOMIC true
|
||||
@@ -57,35 +56,17 @@ static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
|
||||
static int scmi_clk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
u64 fmin, fmax, ftmp;
|
||||
int ret;
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
/*
|
||||
* We can't figure out what rate it will be, so just return the
|
||||
* rate back to the caller. scmi_clk_recalc_rate() will be called
|
||||
* after the rate is set and we'll know what rate the clock is
|
||||
* If we could not get a better rate scmi_clk_recalc_rate() will be
|
||||
* called after the rate is set and we'll know what rate the clock is
|
||||
* running at then.
|
||||
*/
|
||||
if (clk->info->rate_discrete)
|
||||
return 0;
|
||||
|
||||
fmin = clk->info->range.min_rate;
|
||||
fmax = clk->info->range.max_rate;
|
||||
if (req->rate <= fmin) {
|
||||
req->rate = fmin;
|
||||
|
||||
return 0;
|
||||
} else if (req->rate >= fmax) {
|
||||
req->rate = fmax;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ftmp = req->rate - fmin;
|
||||
ftmp += clk->info->range.step_size - 1; /* to round up */
|
||||
do_div(ftmp, clk->info->range.step_size);
|
||||
|
||||
req->rate = ftmp * clk->info->range.step_size + fmin;
|
||||
ret = scmi_proto_clk_ops->determine_rate(clk->ph, clk->id, &req->rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -221,7 +202,6 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
|
||||
const struct clk_ops *scmi_ops)
|
||||
{
|
||||
int ret;
|
||||
unsigned long min_rate, max_rate;
|
||||
|
||||
struct clk_init_data init = {
|
||||
.flags = CLK_GET_RATE_NOCACHE,
|
||||
@@ -236,20 +216,8 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (sclk->info->rate_discrete) {
|
||||
int num_rates = sclk->info->list.num_rates;
|
||||
|
||||
if (num_rates <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
min_rate = sclk->info->list.rates[0];
|
||||
max_rate = sclk->info->list.rates[num_rates - 1];
|
||||
} else {
|
||||
min_rate = sclk->info->range.min_rate;
|
||||
max_rate = sclk->info->range.max_rate;
|
||||
}
|
||||
|
||||
clk_hw_set_rate_range(&sclk->hw, min_rate, max_rate);
|
||||
clk_hw_set_rate_range(&sclk->hw, sclk->info->min_rate,
|
||||
sclk->info->max_rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ static void scpi_clocks_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(np, child)
|
||||
of_clk_del_provider(np);
|
||||
of_clk_del_provider(child);
|
||||
}
|
||||
|
||||
static int scpi_clocks_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -47,6 +47,8 @@ struct sci_clk_provider {
|
||||
* @node: Link for handling clocks probed via DT
|
||||
* @cached_req: Cached requested freq for determine rate calls
|
||||
* @cached_res: Cached result freq for determine rate calls
|
||||
* @parent_id: Parent index for this clock
|
||||
* @rate: Clock rate
|
||||
*/
|
||||
struct sci_clk {
|
||||
struct clk_hw hw;
|
||||
@@ -58,6 +60,8 @@ struct sci_clk {
|
||||
struct list_head node;
|
||||
unsigned long cached_req;
|
||||
unsigned long cached_res;
|
||||
int parent_id;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
#define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw)
|
||||
@@ -150,6 +154,8 @@ static unsigned long sci_clk_recalc_rate(struct clk_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
clk->rate = freq;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
@@ -210,10 +216,15 @@ static int sci_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sci_clk *clk = to_sci_clk(hw);
|
||||
int ret;
|
||||
|
||||
return clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id,
|
||||
clk->clk_id, rate / 10 * 9, rate,
|
||||
rate / 10 * 11);
|
||||
ret = clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id,
|
||||
clk->clk_id, rate / 10 * 9, rate,
|
||||
rate / 10 * 11);
|
||||
if (!ret)
|
||||
clk->rate = rate;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,12 +245,13 @@ static u8 sci_clk_get_parent(struct clk_hw *hw)
|
||||
dev_err(clk->provider->dev,
|
||||
"get-parent failed for dev=%d, clk=%d, ret=%d\n",
|
||||
clk->dev_id, clk->clk_id, ret);
|
||||
clk->parent_id = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
parent_id = parent_id - clk->clk_id - 1;
|
||||
clk->parent_id = parent_id - clk->clk_id - 1;
|
||||
|
||||
return (u8)parent_id;
|
||||
return (u8)clk->parent_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,12 +264,28 @@ static u8 sci_clk_get_parent(struct clk_hw *hw)
|
||||
static int sci_clk_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct sci_clk *clk = to_sci_clk(hw);
|
||||
int ret;
|
||||
|
||||
clk->cached_req = 0;
|
||||
|
||||
return clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id,
|
||||
clk->clk_id,
|
||||
index + 1 + clk->clk_id);
|
||||
ret = clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id,
|
||||
clk->clk_id,
|
||||
index + 1 + clk->clk_id);
|
||||
if (!ret)
|
||||
clk->parent_id = index;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sci_clk_restore_context(struct clk_hw *hw)
|
||||
{
|
||||
struct sci_clk *clk = to_sci_clk(hw);
|
||||
|
||||
if (clk->num_parents > 1 && clk->parent_id >= 0)
|
||||
sci_clk_set_parent(hw, (u8)clk->parent_id);
|
||||
|
||||
if (clk->rate)
|
||||
sci_clk_set_rate(hw, clk->rate, 0);
|
||||
}
|
||||
|
||||
static const struct clk_ops sci_clk_ops = {
|
||||
@@ -269,6 +297,7 @@ static const struct clk_ops sci_clk_ops = {
|
||||
.set_rate = sci_clk_set_rate,
|
||||
.get_parent = sci_clk_get_parent,
|
||||
.set_parent = sci_clk_set_parent,
|
||||
.restore_context = sci_clk_restore_context,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,7 +97,7 @@ config EXYNOS_CLKOUT
|
||||
|
||||
config EXYNOS_ACPM_CLK
|
||||
tristate "Clock driver controlled via ACPM interface"
|
||||
depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL)
|
||||
depends on EXYNOS_ACPM_PROTOCOL
|
||||
help
|
||||
This driver provides support for clocks that are controlled by
|
||||
firmware that implements the ACPM interface.
|
||||
|
||||
@@ -68,8 +68,8 @@ static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw,
|
||||
{
|
||||
struct acpm_clk *clk = to_acpm_clk(hw);
|
||||
|
||||
return clk->handle->ops.dvfs_ops.get_rate(clk->handle,
|
||||
clk->mbox_chan_id, clk->id);
|
||||
return clk->handle->ops->dvfs.get_rate(clk->handle, clk->mbox_chan_id,
|
||||
clk->id);
|
||||
}
|
||||
|
||||
static int acpm_clk_determine_rate(struct clk_hw *hw,
|
||||
@@ -89,8 +89,8 @@ static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
{
|
||||
struct acpm_clk *clk = to_acpm_clk(hw);
|
||||
|
||||
return clk->handle->ops.dvfs_ops.set_rate(clk->handle,
|
||||
clk->mbox_chan_id, clk->id, rate);
|
||||
return clk->handle->ops->dvfs.set_rate(clk->handle, clk->mbox_chan_id,
|
||||
clk->id, rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops acpm_clk_ops = {
|
||||
|
||||
@@ -114,7 +114,10 @@ config ISCSI_IBFT
|
||||
|
||||
config RASPBERRYPI_FIRMWARE
|
||||
tristate "Raspberry Pi Firmware Driver"
|
||||
depends on BCM2835_MBOX
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
depends on ARM || ARM64
|
||||
depends on MAILBOX
|
||||
default ARCH_BCM2835
|
||||
help
|
||||
This option enables support for communicating with the firmware on the
|
||||
Raspberry Pi.
|
||||
|
||||
@@ -192,7 +192,7 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev)
|
||||
|
||||
struct ffa_device *
|
||||
ffa_device_register(const struct ffa_partition_info *part_info,
|
||||
const struct ffa_ops *ops)
|
||||
const struct ffa_ops *ops, struct device *parent)
|
||||
{
|
||||
int id, ret;
|
||||
struct device *dev;
|
||||
@@ -212,6 +212,7 @@ ffa_device_register(const struct ffa_partition_info *part_info,
|
||||
}
|
||||
|
||||
dev = &ffa_dev->dev;
|
||||
dev->parent = parent;
|
||||
dev->bus = &ffa_bus_type;
|
||||
dev->release = ffa_release_device;
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
|
||||
@@ -18,9 +18,9 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev);
|
||||
void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid);
|
||||
|
||||
#ifdef CONFIG_ARM_FFA_SMCCC
|
||||
int __init ffa_transport_init(ffa_fn **invoke_ffa_fn);
|
||||
int ffa_transport_init(ffa_fn **invoke_ffa_fn);
|
||||
#else
|
||||
static inline int __init ffa_transport_init(ffa_fn **invoke_ffa_fn)
|
||||
static inline int ffa_transport_init(ffa_fn **invoke_ffa_fn)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@@ -36,16 +36,20 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include <asm/virt.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define FFA_DRIVER_VERSION FFA_VERSION_1_2
|
||||
#define FFA_MIN_VERSION FFA_VERSION_1_0
|
||||
#define FFA_PLATFORM_NAME "arm-ffa"
|
||||
|
||||
#define SENDER_ID_MASK GENMASK(31, 16)
|
||||
#define RECEIVER_ID_MASK GENMASK(15, 0)
|
||||
@@ -115,6 +119,7 @@ struct ffa_drv_info {
|
||||
};
|
||||
|
||||
static struct ffa_drv_info *drv_info;
|
||||
static struct platform_device *ffa_pdev;
|
||||
|
||||
/*
|
||||
* The driver must be able to support all the versions from the earliest
|
||||
@@ -324,11 +329,9 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
#define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16)
|
||||
#define PART_INFO_PROPS_MASK GENMASK(63, 32)
|
||||
#define FFA_PART_INFO_GET_REGS_FIRST_REG 3
|
||||
#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3
|
||||
#define FFA_PART_INFO_GET_REGS_MAX_DESC \
|
||||
(((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \
|
||||
FFA_PART_INFO_GET_REGS_FIRST_REG) / \
|
||||
FFA_PART_INFO_GET_REGS_REGS_PER_DESC)
|
||||
#define FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC 3
|
||||
#define FFA_PART_INFO_GET_REGS_NUM_REGS \
|
||||
(sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0))
|
||||
#define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x))))
|
||||
#define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x))))
|
||||
#define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x))))
|
||||
@@ -342,7 +345,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
|
||||
do {
|
||||
__le64 *regs;
|
||||
int idx, nr_desc, buf_idx;
|
||||
int idx, nr_desc, buf_idx, regs_per_desc, max_desc;
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_PARTITION_INFO_GET_REGS,
|
||||
@@ -365,8 +368,18 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
if (cur_idx < start_idx || cur_idx >= count)
|
||||
return -EINVAL;
|
||||
|
||||
buf_sz = PARTITION_INFO_SZ(partition_info.a2);
|
||||
if (buf_sz % sizeof(*regs))
|
||||
return -EINVAL;
|
||||
|
||||
regs_per_desc = buf_sz / sizeof(*regs);
|
||||
if (regs_per_desc < FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC)
|
||||
return -EINVAL;
|
||||
|
||||
nr_desc = cur_idx - start_idx + 1;
|
||||
if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC)
|
||||
max_desc = (FFA_PART_INFO_GET_REGS_NUM_REGS -
|
||||
FFA_PART_INFO_GET_REGS_FIRST_REG) / regs_per_desc;
|
||||
if (nr_desc > max_desc)
|
||||
return -EINVAL;
|
||||
|
||||
buf_idx = buf - buffer;
|
||||
@@ -374,9 +387,6 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
return -EINVAL;
|
||||
|
||||
tag = UUID_INFO_TAG(partition_info.a2);
|
||||
buf_sz = PARTITION_INFO_SZ(partition_info.a2);
|
||||
if (buf_sz > sizeof(*buffer))
|
||||
buf_sz = sizeof(*buffer);
|
||||
|
||||
regs = (void *)&partition_info.a3;
|
||||
for (idx = 0; idx < nr_desc; idx++, buf++) {
|
||||
@@ -395,7 +405,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
|
||||
buf->exec_ctxt = PART_INFO_EXEC_CXT(val);
|
||||
buf->properties = PART_INFO_PROPERTIES(val);
|
||||
uuid_copy(&buf->uuid, &uuid_regs.uuid);
|
||||
regs += 3;
|
||||
regs += regs_per_desc;
|
||||
}
|
||||
start_idx = cur_idx + 1;
|
||||
|
||||
@@ -1735,7 +1745,7 @@ static int ffa_setup_host_partition(int vm_id)
|
||||
int ret;
|
||||
|
||||
buf.id = vm_id;
|
||||
ffa_dev = ffa_device_register(&buf, &ffa_drv_ops);
|
||||
ffa_dev = ffa_device_register(&buf, &ffa_drv_ops, &ffa_pdev->dev);
|
||||
if (!ffa_dev) {
|
||||
pr_err("%s: failed to register host partition ID 0x%x\n",
|
||||
__func__, vm_id);
|
||||
@@ -1810,7 +1820,8 @@ static int ffa_setup_partitions(void)
|
||||
* provides UUID here for each partition as part of the
|
||||
* discovery API and the same is passed.
|
||||
*/
|
||||
ffa_dev = ffa_device_register(tpbuf, &ffa_drv_ops);
|
||||
ffa_dev = ffa_device_register(tpbuf, &ffa_drv_ops,
|
||||
&ffa_pdev->dev);
|
||||
if (!ffa_dev) {
|
||||
pr_err("%s: failed to register partition ID 0x%x\n",
|
||||
__func__, tpbuf->id);
|
||||
@@ -2086,23 +2097,31 @@ static void ffa_notifications_setup(void)
|
||||
ffa_notifications_cleanup();
|
||||
}
|
||||
|
||||
static int __init ffa_init(void)
|
||||
static int ffa_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
u32 buf_sz;
|
||||
size_t rxtx_bufsz = SZ_4K;
|
||||
|
||||
if (IS_BUILTIN(CONFIG_ARM_FFA_TRANSPORT) &&
|
||||
is_protected_kvm_enabled() && !is_pkvm_initialized())
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = ffa_transport_init(&invoke_ffa_fn);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ret == -EOPNOTSUPP ? -ENODEV : ret;
|
||||
|
||||
drv_info = kzalloc_obj(*drv_info);
|
||||
if (!drv_info)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, drv_info);
|
||||
|
||||
ret = ffa_version_check(&drv_info->version);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret == -EOPNOTSUPP)
|
||||
ret = -ENODEV;
|
||||
goto free_drv_info;
|
||||
}
|
||||
|
||||
if (ffa_id_get(&drv_info->vm_id)) {
|
||||
pr_err("failed to obtain VM id for self\n");
|
||||
@@ -2161,19 +2180,56 @@ static int __init ffa_init(void)
|
||||
free_pages_exact(drv_info->tx_buffer, rxtx_bufsz);
|
||||
free_pages_exact(drv_info->rx_buffer, rxtx_bufsz);
|
||||
free_drv_info:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(drv_info);
|
||||
drv_info = NULL;
|
||||
return ret;
|
||||
}
|
||||
rootfs_initcall(ffa_init);
|
||||
|
||||
static void __exit ffa_exit(void)
|
||||
static void ffa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ffa_drv_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
ffa_notifications_cleanup();
|
||||
ffa_partitions_cleanup();
|
||||
ffa_rxtx_unmap();
|
||||
free_pages_exact(drv_info->tx_buffer, drv_info->rxtx_bufsz);
|
||||
free_pages_exact(drv_info->rx_buffer, drv_info->rxtx_bufsz);
|
||||
kfree(drv_info);
|
||||
free_pages_exact(info->tx_buffer, info->rxtx_bufsz);
|
||||
free_pages_exact(info->rx_buffer, info->rxtx_bufsz);
|
||||
kfree(info);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
drv_info = NULL;
|
||||
}
|
||||
|
||||
static struct platform_driver ffa_driver = {
|
||||
.probe = ffa_probe,
|
||||
.remove = ffa_remove,
|
||||
.driver = {
|
||||
.name = FFA_PLATFORM_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ffa_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ffa_pdev = platform_device_register_simple(FFA_PLATFORM_NAME,
|
||||
PLATFORM_DEVID_NONE,
|
||||
NULL, 0);
|
||||
if (IS_ERR(ffa_pdev))
|
||||
return PTR_ERR(ffa_pdev);
|
||||
|
||||
ret = platform_driver_register(&ffa_driver);
|
||||
if (ret)
|
||||
platform_device_unregister(ffa_pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(ffa_init);
|
||||
|
||||
static void __exit ffa_exit(void)
|
||||
{
|
||||
platform_device_unregister(ffa_pdev);
|
||||
platform_driver_unregister(&ffa_driver);
|
||||
}
|
||||
module_exit(ffa_exit);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ static void __arm_ffa_fn_hvc(ffa_value_t args, ffa_value_t *res)
|
||||
arm_smccc_1_2_hvc(&args, res);
|
||||
}
|
||||
|
||||
int __init ffa_transport_init(ffa_fn **invoke_ffa_fn)
|
||||
int ffa_transport_init(ffa_fn **invoke_ffa_fn)
|
||||
{
|
||||
enum arm_smccc_conduit conduit;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ static int scmi_base_attributes_get(const struct scmi_protocol_handle *ph)
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_base_attributes *attr_info;
|
||||
struct scmi_revision_info *rev = ph->get_priv(ph);
|
||||
struct scmi_base_info *rev = ph->get_priv(ph);
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
|
||||
0, sizeof(*attr_info), &t);
|
||||
@@ -103,7 +103,7 @@ scmi_base_vendor_id_get(const struct scmi_protocol_handle *ph, bool sub_vendor)
|
||||
int ret, size;
|
||||
char *vendor_id;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_revision_info *rev = ph->get_priv(ph);
|
||||
struct scmi_base_info *rev = ph->get_priv(ph);
|
||||
|
||||
if (sub_vendor) {
|
||||
cmd = BASE_DISCOVER_SUB_VENDOR;
|
||||
@@ -143,7 +143,7 @@ scmi_base_implementation_version_get(const struct scmi_protocol_handle *ph)
|
||||
int ret;
|
||||
__le32 *impl_ver;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_revision_info *rev = ph->get_priv(ph);
|
||||
struct scmi_base_info *rev = ph->get_priv(ph);
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_IMPLEMENT_VERSION,
|
||||
0, sizeof(*impl_ver), &t);
|
||||
@@ -180,7 +180,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
|
||||
__le32 *num_skip, *num_ret;
|
||||
u32 tot_num_ret = 0, loop_num_ret;
|
||||
struct device *dev = ph->dev;
|
||||
struct scmi_revision_info *rev = ph->get_priv(ph);
|
||||
struct scmi_base_info *rev = ph->get_priv(ph);
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS,
|
||||
sizeof(*num_skip), 0, &t);
|
||||
@@ -325,6 +325,8 @@ static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
void *report, u32 *src_id)
|
||||
{
|
||||
int i;
|
||||
u32 error_status;
|
||||
size_t expected_sz;
|
||||
const struct scmi_base_error_notify_payld *p = payld;
|
||||
struct scmi_base_error_report *r = report;
|
||||
|
||||
@@ -338,10 +340,19 @@ static void *scmi_base_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
if (evt_id != SCMI_EVENT_BASE_ERROR_EVENT || sizeof(*p) < payld_sz)
|
||||
return NULL;
|
||||
|
||||
expected_sz = offsetof(typeof(*p), msg_reports);
|
||||
if (payld_sz < expected_sz)
|
||||
return NULL;
|
||||
|
||||
r->timestamp = timestamp;
|
||||
r->agent_id = le32_to_cpu(p->agent_id);
|
||||
r->fatal = IS_FATAL_ERROR(le32_to_cpu(p->error_status));
|
||||
r->cmd_count = ERROR_CMD_COUNT(le32_to_cpu(p->error_status));
|
||||
error_status = le32_to_cpu(p->error_status);
|
||||
r->fatal = IS_FATAL_ERROR(error_status);
|
||||
r->cmd_count = ERROR_CMD_COUNT(error_status);
|
||||
expected_sz += r->cmd_count * sizeof(p->msg_reports[0]);
|
||||
if (payld_sz < expected_sz)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < r->cmd_count; i++)
|
||||
r->reports[i] = le64_to_cpu(p->msg_reports[i]);
|
||||
*src_id = 0;
|
||||
@@ -377,7 +388,7 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
u8 *prot_imp;
|
||||
char name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
struct device *dev = ph->dev;
|
||||
struct scmi_revision_info *rev = scmi_revision_area_get(ph);
|
||||
struct scmi_base_info *rev = scmi_revision_area_get(ph);
|
||||
|
||||
rev->major_ver = PROTOCOL_REV_MAJOR(ph->version);
|
||||
rev->minor_ver = PROTOCOL_REV_MINOR(ph->version);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copyright (C) 2018-2022 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/sort.h>
|
||||
@@ -156,13 +157,26 @@ struct scmi_clock_rate_notify_payld {
|
||||
__le32 rate_high;
|
||||
};
|
||||
|
||||
struct scmi_clock_desc {
|
||||
u32 id;
|
||||
unsigned int tot_rates;
|
||||
struct scmi_clock_rates r;
|
||||
#define RATE_MIN 0
|
||||
#define RATE_MAX 1
|
||||
#define RATE_STEP 2
|
||||
struct scmi_clock_info info;
|
||||
};
|
||||
|
||||
#define to_desc(p) (container_of(p, struct scmi_clock_desc, info))
|
||||
|
||||
struct clock_info {
|
||||
int num_clocks;
|
||||
int max_async_req;
|
||||
bool notify_rate_changed_cmd;
|
||||
bool notify_rate_change_requested_cmd;
|
||||
atomic_t cur_async_req;
|
||||
struct scmi_clock_info *clk;
|
||||
struct scmi_clock_desc *clkds;
|
||||
#define CLOCK_INFO(c, i) (&(((c)->clkds + (i))->info))
|
||||
int (*clock_config_set)(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, enum clk_state state,
|
||||
enum scmi_clock_oem_config oem_type,
|
||||
@@ -184,7 +198,7 @@ scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id)
|
||||
if (clk_id >= ci->num_clocks)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return ci->clk + clk_id;
|
||||
return CLOCK_INFO(ci, clk_id);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -225,8 +239,7 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
|
||||
struct scmi_clk_ipriv {
|
||||
struct device *dev;
|
||||
u32 clk_id;
|
||||
struct scmi_clock_info *clk;
|
||||
struct scmi_clock_desc *clkd;
|
||||
};
|
||||
|
||||
static void iter_clk_possible_parents_prepare_message(void *message, unsigned int desc_index,
|
||||
@@ -235,7 +248,7 @@ static void iter_clk_possible_parents_prepare_message(void *message, unsigned in
|
||||
struct scmi_msg_clock_possible_parents *msg = message;
|
||||
const struct scmi_clk_ipriv *p = priv;
|
||||
|
||||
msg->id = cpu_to_le32(p->clk_id);
|
||||
msg->id = cpu_to_le32(p->clkd->id);
|
||||
/* Set the number of OPPs to be skipped/already read */
|
||||
msg->skip_parents = cpu_to_le32(desc_index);
|
||||
}
|
||||
@@ -245,7 +258,6 @@ static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st
|
||||
{
|
||||
const struct scmi_msg_resp_clock_possible_parents *r = response;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
struct device *dev = ((struct scmi_clk_ipriv *)p)->dev;
|
||||
u32 flags;
|
||||
|
||||
flags = le32_to_cpu(r->num_parent_flags);
|
||||
@@ -257,14 +269,15 @@ static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st
|
||||
* assume it's returned+remaining on first call.
|
||||
*/
|
||||
if (!st->max_resources) {
|
||||
p->clk->num_parents = st->num_returned + st->num_remaining;
|
||||
p->clk->parents = devm_kcalloc(dev, p->clk->num_parents,
|
||||
sizeof(*p->clk->parents),
|
||||
GFP_KERNEL);
|
||||
if (!p->clk->parents) {
|
||||
p->clk->num_parents = 0;
|
||||
int num_parents = st->num_returned + st->num_remaining;
|
||||
|
||||
p->clkd->info.parents = devm_kcalloc(p->dev, num_parents,
|
||||
sizeof(*p->clkd->info.parents),
|
||||
GFP_KERNEL);
|
||||
if (!p->clkd->info.parents)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* max_resources is used by the iterators to control bounds */
|
||||
st->max_resources = st->num_returned + st->num_remaining;
|
||||
}
|
||||
|
||||
@@ -279,29 +292,29 @@ static int iter_clk_possible_parents_process_response(const struct scmi_protocol
|
||||
const struct scmi_msg_resp_clock_possible_parents *r = response;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
|
||||
u32 *parent = &p->clk->parents[st->desc_index + st->loop_idx];
|
||||
p->clkd->info.parents[st->desc_index + st->loop_idx] =
|
||||
le32_to_cpu(r->possible_parents[st->loop_idx]);
|
||||
|
||||
*parent = le32_to_cpu(r->possible_parents[st->loop_idx]);
|
||||
/* Count only effectively discovered parents */
|
||||
p->clkd->info.num_parents++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
struct scmi_clock_info *clk)
|
||||
static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, struct clock_info *cinfo)
|
||||
{
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_clk_possible_parents_prepare_message,
|
||||
.update_state = iter_clk_possible_parents_update_state,
|
||||
.process_response = iter_clk_possible_parents_process_response,
|
||||
};
|
||||
|
||||
struct scmi_clock_desc *clkd = &cinfo->clkds[clk_id];
|
||||
struct scmi_clk_ipriv ppriv = {
|
||||
.clk_id = clk_id,
|
||||
.clk = clk,
|
||||
.clkd = clkd,
|
||||
.dev = ph->dev,
|
||||
};
|
||||
void *iter;
|
||||
int ret;
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, 0,
|
||||
CLOCK_POSSIBLE_PARENTS_GET,
|
||||
@@ -310,9 +323,7 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
ret = ph->hops->iter_response_run(iter);
|
||||
|
||||
return ret;
|
||||
return ph->hops->iter_response_run(iter);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -351,7 +362,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 attributes;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_clock_attributes *attr;
|
||||
struct scmi_clock_info *clk = cinfo->clk + clk_id;
|
||||
struct scmi_clock_info *clk = CLOCK_INFO(cinfo, clk_id);
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
|
||||
sizeof(clk_id), sizeof(*attr), &t);
|
||||
@@ -393,7 +404,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
clk->rate_change_requested_notifications = true;
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
|
||||
if (SUPPORTS_PARENT_CLOCK(attributes))
|
||||
scmi_clock_possible_parents(ph, clk_id, clk);
|
||||
scmi_clock_possible_parents(ph, clk_id, cinfo);
|
||||
if (SUPPORTS_GET_PERMISSIONS(attributes))
|
||||
scmi_clock_get_permissions(ph, clk_id, clk);
|
||||
if (SUPPORTS_EXTENDED_CONFIG(attributes))
|
||||
@@ -423,7 +434,7 @@ static void iter_clk_describe_prepare_message(void *message,
|
||||
struct scmi_msg_clock_describe_rates *msg = message;
|
||||
const struct scmi_clk_ipriv *p = priv;
|
||||
|
||||
msg->id = cpu_to_le32(p->clk_id);
|
||||
msg->id = cpu_to_le32(p->clkd->id);
|
||||
/* Set the number of rates to be skipped/already read */
|
||||
msg->rate_index = cpu_to_le32(desc_index);
|
||||
}
|
||||
@@ -456,20 +467,33 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st,
|
||||
flags = le32_to_cpu(r->num_rates_flags);
|
||||
st->num_remaining = NUM_REMAINING(flags);
|
||||
st->num_returned = NUM_RETURNED(flags);
|
||||
p->clk->rate_discrete = RATE_DISCRETE(flags);
|
||||
p->clkd->r.rate_discrete = RATE_DISCRETE(flags);
|
||||
|
||||
/* Warn about out of spec replies ... */
|
||||
if (!p->clk->rate_discrete &&
|
||||
if (!p->clkd->r.rate_discrete &&
|
||||
(st->num_returned != 3 || st->num_remaining != 0)) {
|
||||
dev_warn(p->dev,
|
||||
"Out-of-spec CLOCK_DESCRIBE_RATES reply for %s - returned:%d remaining:%d rx_len:%zd\n",
|
||||
p->clk->name, st->num_returned, st->num_remaining,
|
||||
p->clkd->info.name, st->num_returned, st->num_remaining,
|
||||
st->rx_len);
|
||||
|
||||
SCMI_QUIRK(clock_rates_triplet_out_of_spec,
|
||||
QUIRK_OUT_OF_SPEC_TRIPLET);
|
||||
}
|
||||
|
||||
if (!st->max_resources) {
|
||||
unsigned int tot_rates = st->num_returned + st->num_remaining;
|
||||
|
||||
p->clkd->r.rates = devm_kcalloc(p->dev, tot_rates,
|
||||
sizeof(*p->clkd->r.rates), GFP_KERNEL);
|
||||
if (!p->clkd->r.rates)
|
||||
return -ENOMEM;
|
||||
|
||||
/* max_resources is used by the iterators to control bounds */
|
||||
p->clkd->tot_rates = tot_rates;
|
||||
st->max_resources = tot_rates;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -478,38 +502,20 @@ iter_clk_describe_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st, void *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
const struct scmi_msg_resp_clock_describe_rates *r = response;
|
||||
|
||||
if (!p->clk->rate_discrete) {
|
||||
switch (st->desc_index + st->loop_idx) {
|
||||
case 0:
|
||||
p->clk->range.min_rate = RATE_TO_U64(r->rate[0]);
|
||||
break;
|
||||
case 1:
|
||||
p->clk->range.max_rate = RATE_TO_U64(r->rate[1]);
|
||||
break;
|
||||
case 2:
|
||||
p->clk->range.step_size = RATE_TO_U64(r->rate[2]);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
u64 *rate = &p->clk->list.rates[st->desc_index + st->loop_idx];
|
||||
p->clkd->r.rates[p->clkd->r.num_rates] = RATE_TO_U64(r->rate[st->loop_idx]);
|
||||
|
||||
*rate = RATE_TO_U64(r->rate[st->loop_idx]);
|
||||
p->clk->list.num_rates++;
|
||||
}
|
||||
/* Count only effectively discovered rates */
|
||||
p->clkd->r.num_rates++;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
struct scmi_clock_info *clk)
|
||||
scmi_clock_describe_rates_get_full(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_clock_desc *clkd)
|
||||
{
|
||||
int ret;
|
||||
void *iter;
|
||||
@@ -519,12 +525,16 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
.process_response = iter_clk_describe_process_response,
|
||||
};
|
||||
struct scmi_clk_ipriv cpriv = {
|
||||
.clk_id = clk_id,
|
||||
.clk = clk,
|
||||
.clkd = clkd,
|
||||
.dev = ph->dev,
|
||||
};
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, SCMI_MAX_NUM_RATES,
|
||||
/*
|
||||
* Using tot_rates as max_resources parameter here so as to trigger
|
||||
* the dynamic allocation only when strictly needed: when trying a
|
||||
* full enumeration after a lazy one tot_rates will be non-zero.
|
||||
*/
|
||||
iter = ph->hops->iter_response_init(ph, &ops, clkd->tot_rates,
|
||||
CLOCK_DESCRIBE_RATES,
|
||||
sizeof(struct scmi_msg_clock_describe_rates),
|
||||
&cpriv);
|
||||
@@ -535,18 +545,99 @@ scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!clk->rate_discrete) {
|
||||
dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
|
||||
clk->range.min_rate, clk->range.max_rate,
|
||||
clk->range.step_size);
|
||||
} else if (clk->list.num_rates) {
|
||||
sort(clk->list.rates, clk->list.num_rates,
|
||||
sizeof(clk->list.rates[0]), rate_cmp_func, NULL);
|
||||
/* empty set ? */
|
||||
if (!clkd->r.num_rates)
|
||||
return 0;
|
||||
|
||||
if (clkd->r.rate_discrete && PROTOCOL_REV_MAJOR(ph->version) == 0x1)
|
||||
sort(clkd->r.rates, clkd->r.num_rates,
|
||||
sizeof(clkd->r.rates[0]), rate_cmp_func, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph,
|
||||
struct scmi_clock_desc *clkd)
|
||||
{
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_clk_describe_prepare_message,
|
||||
.update_state = iter_clk_describe_update_state,
|
||||
.process_response = iter_clk_describe_process_response,
|
||||
};
|
||||
struct scmi_clk_ipriv cpriv = {
|
||||
.clkd = clkd,
|
||||
.dev = ph->dev,
|
||||
};
|
||||
unsigned int first, last;
|
||||
void *iter;
|
||||
int ret;
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, 0, CLOCK_DESCRIBE_RATES,
|
||||
sizeof(struct scmi_msg_clock_describe_rates),
|
||||
&cpriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
/* Try to grab a triplet, so that in case is NON-discrete we are done */
|
||||
first = 0;
|
||||
last = 2;
|
||||
ret = ph->hops->iter_response_run_bound(iter, &first, &last);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If discrete and we don't already have it, grab the last value, which
|
||||
* should be the max
|
||||
*/
|
||||
if (clkd->r.rate_discrete && clkd->tot_rates > clkd->r.num_rates) {
|
||||
first = clkd->tot_rates - 1;
|
||||
last = clkd->tot_rates - 1;
|
||||
ret = ph->hops->iter_response_run_bound(iter, &first, &last);
|
||||
}
|
||||
|
||||
out:
|
||||
ph->hops->iter_response_bound_cleanup(iter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, struct clock_info *cinfo)
|
||||
{
|
||||
struct scmi_clock_desc *clkd = &cinfo->clkds[clk_id];
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Since only after SCMI Clock v1.0 the returned rates are guaranteed to
|
||||
* be discovered in ascending order, lazy enumeration cannot be use for
|
||||
* SCMI Clock v1.0 protocol.
|
||||
*/
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) > 0x1)
|
||||
ret = scmi_clock_describe_rates_get_lazy(ph, clkd);
|
||||
else
|
||||
ret = scmi_clock_describe_rates_get_full(ph, clkd);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clkd->info.min_rate = clkd->r.rates[RATE_MIN];
|
||||
if (!clkd->r.rate_discrete) {
|
||||
clkd->info.max_rate = clkd->r.rates[RATE_MAX];
|
||||
dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n",
|
||||
clkd->r.rates[RATE_MIN], clkd->r.rates[RATE_MAX],
|
||||
clkd->r.rates[RATE_STEP]);
|
||||
} else {
|
||||
clkd->info.max_rate = clkd->r.rates[clkd->r.num_rates - 1];
|
||||
dev_dbg(ph->dev, "Clock:%s Num_Rates:%u -> Min %llu Max %llu\n",
|
||||
clkd->info.name, clkd->tot_rates,
|
||||
clkd->info.min_rate, clkd->info.max_rate);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_rate_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u64 *value)
|
||||
@@ -624,6 +715,75 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, unsigned long *rate)
|
||||
{
|
||||
u64 fmin, fmax, ftmp;
|
||||
struct scmi_clock_info *clk;
|
||||
struct scmi_clock_desc *clkd;
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
clkd = to_desc(clk);
|
||||
|
||||
/*
|
||||
* If we can't figure out what rate it will be, so just return the
|
||||
* rate back to the caller.
|
||||
*/
|
||||
if (clkd->r.rate_discrete)
|
||||
return 0;
|
||||
|
||||
fmin = clk->min_rate;
|
||||
fmax = clk->max_rate;
|
||||
if (*rate <= fmin) {
|
||||
*rate = fmin;
|
||||
return 0;
|
||||
} else if (*rate >= fmax) {
|
||||
*rate = fmax;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ftmp = *rate - fmin;
|
||||
ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */
|
||||
ftmp = div64_ul(ftmp, clkd->r.rates[RATE_STEP]);
|
||||
|
||||
*rate = ftmp * clkd->r.rates[RATE_STEP] + fmin;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_clock_rates *
|
||||
scmi_clock_all_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_desc *clkd;
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
clk = scmi_clock_domain_lookup(ci, clk_id);
|
||||
if (IS_ERR(clk) || !clk->name[0])
|
||||
return NULL;
|
||||
|
||||
clkd = to_desc(clk);
|
||||
/* Needs full enumeration ? */
|
||||
if (clkd->r.rate_discrete && clkd->tot_rates != clkd->r.num_rates) {
|
||||
int ret;
|
||||
|
||||
/* rates[] is already allocated BUT we need to re-enumerate */
|
||||
clkd->r.num_rates = 0;
|
||||
ret = scmi_clock_describe_rates_get_full(ph, clkd);
|
||||
if (ret)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &clkd->r;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
enum clk_state state,
|
||||
@@ -936,6 +1096,8 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
|
||||
.info_get = scmi_clock_info_get,
|
||||
.rate_get = scmi_clock_rate_get,
|
||||
.rate_set = scmi_clock_rate_set,
|
||||
.determine_rate = scmi_clock_determine_rate,
|
||||
.all_rates_get = scmi_clock_all_rates_get,
|
||||
.enable = scmi_clock_enable,
|
||||
.disable = scmi_clock_disable,
|
||||
.state_get = scmi_clock_state_get,
|
||||
@@ -1080,17 +1242,16 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks,
|
||||
sizeof(*cinfo->clk), GFP_KERNEL);
|
||||
if (!cinfo->clk)
|
||||
cinfo->clkds = devm_kcalloc(ph->dev, cinfo->num_clocks,
|
||||
sizeof(*cinfo->clkds), GFP_KERNEL);
|
||||
if (!cinfo->clkds)
|
||||
return -ENOMEM;
|
||||
|
||||
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
|
||||
struct scmi_clock_info *clk = cinfo->clk + clkid;
|
||||
|
||||
cinfo->clkds[clkid].id = clkid;
|
||||
ret = scmi_clock_attributes_get(ph, clkid, cinfo);
|
||||
if (!ret)
|
||||
scmi_clock_describe_rates_get(ph, clkid, clk);
|
||||
scmi_clock_describe_rates_get(ph, clkid, cinfo);
|
||||
}
|
||||
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -138,7 +139,7 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
|
||||
xfer_; \
|
||||
})
|
||||
|
||||
struct scmi_revision_info *
|
||||
struct scmi_base_info *
|
||||
scmi_revision_area_get(const struct scmi_protocol_handle *ph);
|
||||
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
|
||||
u8 *prot_imp);
|
||||
@@ -462,6 +463,28 @@ struct scmi_transport_core_operations {
|
||||
const struct scmi_message_operations *msg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_transport_handle - Transport instance handle
|
||||
* @supplier_get: A helper to retrieve the device descriptor, identifying the
|
||||
* transport driver serving this SCMI instance, which will be
|
||||
* used as a supplier for the core SCMI driver: returning an
|
||||
* error here causes the probe sequence to be interrupted and
|
||||
* return that same error code, so that each transport can decide
|
||||
* which policy to implement by choosing an appropriate error.
|
||||
* @supplier_put: A helper to signal that the specified transport supplier is
|
||||
* no more being used and it is made available again.
|
||||
*
|
||||
* Note that these helpers are needed and provided only by those transports
|
||||
* whose initialization relies on some other subsystem and whose relations to
|
||||
* the core SCMI driver is not tracked by firmware descriptions.
|
||||
*/
|
||||
struct scmi_transport_handle {
|
||||
struct device __must_check *(*supplier_get)
|
||||
(const struct scmi_transport_handle *th);
|
||||
int (*supplier_put)(const struct scmi_transport_handle *th,
|
||||
struct device *dev);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_transport - A structure representing a configured transport
|
||||
*
|
||||
@@ -470,35 +493,163 @@ struct scmi_transport_core_operations {
|
||||
* @desc: Transport descriptor
|
||||
* @core_ops: A pointer to a pointer used by the core SCMI stack to make the
|
||||
* core transport operations accessible to the transports.
|
||||
* @th: An optional pointer to the transport handle
|
||||
*/
|
||||
struct scmi_transport {
|
||||
struct device *supplier;
|
||||
struct scmi_desc desc;
|
||||
struct scmi_transport_core_operations **core_ops;
|
||||
const struct scmi_transport_handle *th;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_transport_supplier - Transport descriptor
|
||||
* @mtx: A mutex to protect @available
|
||||
* @available: A reference to an initialized transport device, when available.
|
||||
* This reference is implicitly used to track the status of the
|
||||
* supplier and it can cycle through the following 3 states:
|
||||
* 1. NOT_READY - PTR_ERR(-EPROBE_DEFER): no supplier available;
|
||||
* this is the transport initial state.
|
||||
* 2. AVAILABLE - <supplier_dev>: a transport supplier has been
|
||||
* initialized and it is available, ready to use.
|
||||
* 3. BUSY _ PTR_ERR(-EBUSY): transport supplier is currently in use.
|
||||
* @th: An embedded transport handle object that embeds the helpers
|
||||
* implementing the above mentioned logic
|
||||
*
|
||||
* Note that this transport driver enforces single instance probing.
|
||||
*/
|
||||
struct scmi_transport_supplier {
|
||||
/* Protect @available */
|
||||
struct mutex mtx;
|
||||
struct device *available;
|
||||
const struct scmi_transport_handle th;
|
||||
};
|
||||
|
||||
#define to_sup(t) container_of(t, struct scmi_transport_supplier, th)
|
||||
|
||||
/**
|
||||
* scmi_transport_supplier_put - A helper to dispose of a supplier
|
||||
* @th: A reference to the transport handle to use
|
||||
* @supplier: A reference to the device supplier to manage, cannot be NULL
|
||||
* or ERR_PTR.
|
||||
*
|
||||
* Note that putting a supplier will have different effect based on the
|
||||
* current state of scmi_transport_supplier.available:
|
||||
* - NOT_READY/BUSY: @supplier will be set as the new available device: this
|
||||
* can be used to made available a supplier OR stop using one.
|
||||
* - AVAILABLE: if the @supplier we are disposing of matches the currently
|
||||
* available one, roll back to NOT_READY state.
|
||||
* Any other attempt to override an available supplier with a
|
||||
* new one is rejected, effectively enforcing one single supplier.
|
||||
*
|
||||
* Return: 0 on Success, errno otherwise.
|
||||
*/
|
||||
static inline int
|
||||
scmi_transport_supplier_put(const struct scmi_transport_handle *th,
|
||||
struct device *supplier)
|
||||
{
|
||||
struct scmi_transport_supplier *sup = to_sup(th);
|
||||
|
||||
/* Nothing to do when the provided supplier was never real */
|
||||
if (IS_ERR_OR_NULL(supplier))
|
||||
return 0;
|
||||
|
||||
guard(mutex)(&sup->mtx);
|
||||
switch (PTR_ERR_OR_ZERO(sup->available)) {
|
||||
case -EPROBE_DEFER:
|
||||
case -EBUSY:
|
||||
sup->available = supplier;
|
||||
break;
|
||||
case 0:
|
||||
/* Putting a supplier when in the AVAILABLE state causes a
|
||||
* transition back to the NOT_READY state, BUT only if the
|
||||
* supplier we are disposing of was exactly the device that was
|
||||
* previously made readily available.
|
||||
*/
|
||||
if (supplier != sup->available)
|
||||
return -EINVAL;
|
||||
sup->available = ERR_PTR(-EPROBE_DEFER);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_transport_supplier_get - A helper to get hold of a supplier
|
||||
* @th: A reference to the transport handle to use
|
||||
*
|
||||
* Note that, trying to get a supplier device can return:
|
||||
* - a ready to use supplier device, (subsequently made unavailable)
|
||||
* - PTR_ERR(-EPROBE_DEFER): no supplier is available
|
||||
* - PTR_ERR(-BUSY): supplier was already taken by a previous get
|
||||
*
|
||||
* This allows the probe to defer and wait when a possible device can
|
||||
* be reasonably expected to appear.
|
||||
*
|
||||
* Return: a usable supplier device on Success or PTR_ERR on Failure.
|
||||
*/
|
||||
static inline struct device *
|
||||
scmi_transport_supplier_get(const struct scmi_transport_handle *th)
|
||||
{
|
||||
struct scmi_transport_supplier *sup = to_sup(th);
|
||||
struct device *supplier;
|
||||
|
||||
guard(mutex)(&sup->mtx);
|
||||
supplier = sup->available;
|
||||
if (!IS_ERR(sup->available))
|
||||
sup->available = ERR_PTR(-EBUSY);
|
||||
|
||||
return supplier;
|
||||
}
|
||||
|
||||
#define DEFINE_SCMI_TRANSPORT_SUPPLIER(__supplier) \
|
||||
struct scmi_transport_supplier __supplier = { \
|
||||
.mtx = __MUTEX_INITIALIZER(__supplier.mtx), \
|
||||
.available = INIT_ERR_PTR(-EPROBE_DEFER), \
|
||||
.th.supplier_get = scmi_transport_supplier_get, \
|
||||
.th.supplier_put = scmi_transport_supplier_put, \
|
||||
}
|
||||
|
||||
#define DEFINE_SCMI_TRANSPORT_DRIVER(__tag, __drv, __desc, __match, __core_ops)\
|
||||
static void __tag##_dev_free(void *data) \
|
||||
{ \
|
||||
struct platform_device *spdev = data; \
|
||||
struct scmi_transport *strans; \
|
||||
\
|
||||
strans = dev_get_platdata(&spdev->dev); \
|
||||
if (strans && strans->th) \
|
||||
strans->th->supplier_put(strans->th, strans->supplier); \
|
||||
\
|
||||
platform_device_unregister(spdev); \
|
||||
} \
|
||||
\
|
||||
static int __tag##_probe(struct platform_device *pdev) \
|
||||
{ \
|
||||
struct device *dev = &pdev->dev; \
|
||||
struct device *dev = &pdev->dev, *supplier; \
|
||||
struct platform_device *spdev; \
|
||||
struct scmi_transport strans; \
|
||||
int ret; \
|
||||
\
|
||||
supplier = dev; \
|
||||
strans.th = device_get_match_data(dev); \
|
||||
if (strans.th) { \
|
||||
supplier = strans.th->supplier_get(strans.th); \
|
||||
if (IS_ERR(supplier)) \
|
||||
return PTR_ERR(supplier); \
|
||||
} \
|
||||
\
|
||||
spdev = platform_device_alloc("arm-scmi", PLATFORM_DEVID_AUTO); \
|
||||
if (!spdev) \
|
||||
return -ENOMEM; \
|
||||
if (!spdev) { \
|
||||
ret = -ENOMEM; \
|
||||
goto err_mem; \
|
||||
} \
|
||||
\
|
||||
device_set_of_node_from_dev(&spdev->dev, dev); \
|
||||
\
|
||||
strans.supplier = dev; \
|
||||
strans.supplier = supplier; \
|
||||
memcpy(&strans.desc, &(__desc), sizeof(strans.desc)); \
|
||||
strans.core_ops = &(__core_ops); \
|
||||
\
|
||||
@@ -515,6 +666,10 @@ static int __tag##_probe(struct platform_device *pdev) \
|
||||
\
|
||||
err: \
|
||||
platform_device_put(spdev); \
|
||||
err_mem: \
|
||||
if (strans.th) \
|
||||
strans.th->supplier_put(strans.th, supplier); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
@@ -133,7 +134,7 @@ struct scmi_protocol_instance {
|
||||
* usage.
|
||||
* @protocols_mtx: A mutex to protect protocols instances initialization.
|
||||
* @protocols_imp: List of protocols implemented, currently maximum of
|
||||
* scmi_revision_info.num_protocols elements allocated by the
|
||||
* scmi_base_info.num_protocols elements allocated by the
|
||||
* base protocol
|
||||
* @active_protocols: IDR storing device_nodes for protocols actually defined
|
||||
* in the DT and confirmed as implemented by fw.
|
||||
@@ -151,7 +152,7 @@ struct scmi_info {
|
||||
int id;
|
||||
struct device *dev;
|
||||
const struct scmi_desc *desc;
|
||||
struct scmi_revision_info version;
|
||||
struct scmi_base_info version;
|
||||
struct scmi_handle handle;
|
||||
struct scmi_xfers_info tx_minfo;
|
||||
struct scmi_xfers_info rx_minfo;
|
||||
@@ -265,7 +266,7 @@ scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
|
||||
}
|
||||
|
||||
static const struct scmi_protocol *
|
||||
scmi_vendor_protocol_get(int protocol_id, struct scmi_revision_info *version)
|
||||
scmi_vendor_protocol_get(int protocol_id, struct scmi_base_info *version)
|
||||
{
|
||||
const struct scmi_protocol *proto;
|
||||
|
||||
@@ -303,7 +304,7 @@ scmi_vendor_protocol_get(int protocol_id, struct scmi_revision_info *version)
|
||||
}
|
||||
|
||||
static const struct scmi_protocol *
|
||||
scmi_protocol_get(int protocol_id, struct scmi_revision_info *version)
|
||||
scmi_protocol_get(int protocol_id, struct scmi_base_info *version)
|
||||
{
|
||||
const struct scmi_protocol *proto = NULL;
|
||||
|
||||
@@ -1789,86 +1790,115 @@ static void *scmi_iterator_init(const struct scmi_protocol_handle *ph,
|
||||
size_t tx_size, void *priv)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_iterator *i;
|
||||
|
||||
i = devm_kzalloc(ph->dev, sizeof(*i), GFP_KERNEL);
|
||||
struct scmi_iterator *i __free(kfree) = kzalloc(sizeof(*i), GFP_KERNEL);
|
||||
if (!i)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!ops || !ph)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
i->ph = ph;
|
||||
i->ops = ops;
|
||||
i->priv = priv;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, msg_id, tx_size, 0, &i->t);
|
||||
if (ret) {
|
||||
devm_kfree(ph->dev, i);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
i->state.max_resources = max_resources;
|
||||
i->msg = i->t->tx.buf;
|
||||
i->resp = i->t->rx.buf;
|
||||
|
||||
return i;
|
||||
return no_free_ptr(i);
|
||||
}
|
||||
|
||||
static int scmi_iterator_run(void *iter)
|
||||
static int __scmi_iterator_run(void *iter, unsigned int *start, unsigned int *end)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
struct scmi_iterator_ops *iops;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
struct scmi_iterator_state *st;
|
||||
struct scmi_iterator *i = iter;
|
||||
struct scmi_iterator *i;
|
||||
unsigned int n;
|
||||
|
||||
if (!i || !i->ops || !i->ph)
|
||||
return ret;
|
||||
if (!iter)
|
||||
return -EINVAL;
|
||||
|
||||
i = iter;
|
||||
iops = i->ops;
|
||||
ph = i->ph;
|
||||
st = &i->state;
|
||||
|
||||
/* Reinitialize state for next run */
|
||||
st->num_returned = 0;
|
||||
st->num_remaining = 0;
|
||||
st->desc_index = start ? *start : 0;
|
||||
|
||||
do {
|
||||
iops->prepare_message(i->msg, st->desc_index, i->priv);
|
||||
ret = ph->xops->do_xfer(ph, i->t);
|
||||
if (ret)
|
||||
break;
|
||||
return ret;
|
||||
|
||||
st->rx_len = i->t->rx.len;
|
||||
ret = iops->update_state(st, i->resp, i->priv);
|
||||
if (ret)
|
||||
break;
|
||||
return ret;
|
||||
|
||||
if (st->num_returned > st->max_resources - st->desc_index) {
|
||||
dev_err(ph->dev,
|
||||
"No. of resources can't exceed %d\n",
|
||||
st->max_resources);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (st->loop_idx = 0; st->loop_idx < st->num_returned;
|
||||
st->loop_idx++) {
|
||||
if (end)
|
||||
n = min(st->num_returned, *end - st->desc_index + 1);
|
||||
else
|
||||
n = st->num_returned;
|
||||
for (st->loop_idx = 0; st->loop_idx < n; st->loop_idx++) {
|
||||
ret = iops->process_response(ph, i->resp, st, i->priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->desc_index += st->num_returned;
|
||||
st->desc_index += n;
|
||||
ph->xops->reset_rx_to_maxsz(ph, i->t);
|
||||
/*
|
||||
* check for both returned and remaining to avoid infinite
|
||||
* loop due to buggy firmware
|
||||
*/
|
||||
} while (st->num_returned && st->num_remaining);
|
||||
} while (st->num_returned && st->num_remaining &&
|
||||
(!end || (st->desc_index <= min(*end, st->max_resources - 1))));
|
||||
|
||||
out:
|
||||
/* Finalize and destroy iterator */
|
||||
ph->xops->xfer_put(ph, i->t);
|
||||
devm_kfree(ph->dev, i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scmi_iterator_bound_cleanup(void *iter)
|
||||
{
|
||||
struct scmi_iterator *i = iter;
|
||||
|
||||
i->ph->xops->xfer_put(i->ph, i->t);
|
||||
kfree(i);
|
||||
}
|
||||
|
||||
static int scmi_iterator_run(void *iter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __scmi_iterator_run(iter, NULL, NULL);
|
||||
scmi_iterator_bound_cleanup(iter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_iterator_run_bound(void *iter, unsigned int *start,
|
||||
unsigned int *end)
|
||||
{
|
||||
return __scmi_iterator_run(iter, start, end);
|
||||
}
|
||||
|
||||
struct scmi_msg_get_fc_info {
|
||||
__le32 domain;
|
||||
__le32 message_id;
|
||||
@@ -2047,6 +2077,8 @@ static const struct scmi_proto_helpers_ops helpers_ops = {
|
||||
.get_max_msg_size = scmi_common_get_max_msg_size,
|
||||
.iter_response_init = scmi_iterator_init,
|
||||
.iter_response_run = scmi_iterator_run,
|
||||
.iter_response_run_bound = scmi_iterator_run_bound,
|
||||
.iter_response_bound_cleanup = scmi_iterator_bound_cleanup,
|
||||
.protocol_msg_check = scmi_protocol_msg_check,
|
||||
.fastchannel_init = scmi_common_fastchannel_init,
|
||||
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
|
||||
@@ -2063,7 +2095,7 @@ static const struct scmi_proto_helpers_ops helpers_ops = {
|
||||
* Return: A reference to the version memory area associated to the SCMI
|
||||
* instance underlying this protocol handle.
|
||||
*/
|
||||
struct scmi_revision_info *
|
||||
struct scmi_base_info *
|
||||
scmi_revision_area_get(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
const struct scmi_protocol_instance *pi = ph_to_pi(ph);
|
||||
@@ -2376,7 +2408,7 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
|
||||
{
|
||||
int i;
|
||||
struct scmi_info *info = handle_to_scmi_info(handle);
|
||||
struct scmi_revision_info *rev = handle->version;
|
||||
struct scmi_base_info *rev = handle->version;
|
||||
|
||||
if (!info->protocols_imp)
|
||||
return false;
|
||||
@@ -2552,13 +2584,11 @@ static bool scmi_is_transport_atomic(const struct scmi_handle *handle,
|
||||
*/
|
||||
static struct scmi_handle *scmi_handle_get(struct device *dev)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct scmi_info *info;
|
||||
struct scmi_handle *handle = NULL;
|
||||
|
||||
mutex_lock(&scmi_list_mutex);
|
||||
list_for_each(p, &scmi_list) {
|
||||
info = list_entry(p, struct scmi_info, node);
|
||||
list_for_each_entry(info, &scmi_list, node) {
|
||||
if (dev->parent == info->dev) {
|
||||
info->users++;
|
||||
handle = &info->handle;
|
||||
@@ -3171,7 +3201,7 @@ static const struct scmi_desc *scmi_transport_setup(struct device *dev)
|
||||
|
||||
static void scmi_enable_matching_quirks(struct scmi_info *info)
|
||||
{
|
||||
struct scmi_revision_info *rev = &info->version;
|
||||
struct scmi_base_info *rev = &info->version;
|
||||
|
||||
dev_dbg(info->dev, "Looking for quirks matching: %s/%s/0x%08X\n",
|
||||
rev->vendor_id, rev->sub_vendor_id, rev->impl_ver);
|
||||
|
||||
@@ -204,8 +204,12 @@ scmi_power_name_get(const struct scmi_protocol_handle *ph,
|
||||
u32 domain)
|
||||
{
|
||||
struct scmi_power_info *pi = ph->get_priv(ph);
|
||||
struct power_dom_info *dom = pi->dom_info + domain;
|
||||
struct power_dom_info *dom;
|
||||
|
||||
if (domain >= pi->num_domains)
|
||||
return "unknown";
|
||||
|
||||
dom = pi->dom_info + domain;
|
||||
return dom->name;
|
||||
}
|
||||
|
||||
|
||||
@@ -453,10 +453,14 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
|
||||
return -EINVAL;
|
||||
|
||||
/* Just log the last set request if acting on a disabled domain */
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 &&
|
||||
!pi->states[domain_id].enabled) {
|
||||
pi->states[domain_id].last_pcap = power_cap;
|
||||
return 0;
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
|
||||
if (!scmi_powercap_dom_info_get(ph, domain_id))
|
||||
return -EINVAL;
|
||||
|
||||
if (!pi->states[domain_id].enabled) {
|
||||
pi->states[domain_id].last_pcap = power_cap;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return __scmi_powercap_cap_set(ph, pi, domain_id,
|
||||
@@ -637,6 +641,9 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
|
||||
return -EINVAL;
|
||||
|
||||
if (!scmi_powercap_dom_info_get(ph, domain_id))
|
||||
return -EINVAL;
|
||||
|
||||
if (enable == pi->states[domain_id].enabled)
|
||||
return 0;
|
||||
|
||||
@@ -678,6 +685,9 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
|
||||
if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
|
||||
return 0;
|
||||
|
||||
if (!scmi_powercap_dom_info_get(ph, domain_id))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Report always real platform state; platform could have ignored
|
||||
* a previous disable request. Default true on any error.
|
||||
|
||||
@@ -259,7 +259,15 @@ struct scmi_fc_info {
|
||||
* multi-part responses using the custom operations
|
||||
* provided in @ops.
|
||||
* @iter_response_run: A common helper to trigger the run of a previously
|
||||
* initialized iterator.
|
||||
* initialized iterator. Note that unbound iterators are
|
||||
* automatically cleaned up.
|
||||
* @iter_response_run_bound: A common helper to trigger the run of a previously
|
||||
* initialized iterator, but only within the
|
||||
* specified, optional, @start and @end resource
|
||||
* indexes. Note that these bound-iterators need
|
||||
* explicit cleanup via @iter_response_bound_cleanup.
|
||||
* @iter_response_bound_cleanup: A common helper to finally release the iterator
|
||||
* for bound iterators.
|
||||
* @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
|
||||
@@ -276,6 +284,9 @@ 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 (*iter_response_run_bound)(void *iter,
|
||||
unsigned int *start, unsigned int *end);
|
||||
void (*iter_response_bound_cleanup)(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,
|
||||
|
||||
@@ -219,9 +219,9 @@ static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend)
|
||||
static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
|
||||
{
|
||||
const char *last, *first __free(kfree) = NULL;
|
||||
int ret = 0;
|
||||
size_t len;
|
||||
char *sep;
|
||||
int ret;
|
||||
|
||||
quirk->start_range = 0;
|
||||
quirk->end_range = 0xFFFFFFFF;
|
||||
@@ -238,16 +238,15 @@ static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
|
||||
if (sep == first) /* -X */
|
||||
ret = kstrtouint(first + 1, 0, &quirk->end_range);
|
||||
else /* X OR X- OR X-y */
|
||||
if (sep != first) /* X OR X- OR X-y */ {
|
||||
ret = kstrtouint(first, 0, &quirk->start_range);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!sep)
|
||||
quirk->end_range = quirk->start_range;
|
||||
else if (sep != last) /* x-Y */
|
||||
else if (sep != last) /* -X OR x-Y */
|
||||
ret = kstrtouint(sep + 1, 0, &quirk->end_range);
|
||||
|
||||
if (quirk->start_range > quirk->end_range)
|
||||
@@ -259,10 +258,8 @@ static int scmi_quirk_range_parse(struct scmi_quirk *quirk)
|
||||
void scmi_quirks_initialize(void)
|
||||
{
|
||||
struct scmi_quirk *quirk;
|
||||
int i;
|
||||
|
||||
for (i = 0, quirk = scmi_quirks_table[0]; quirk;
|
||||
i++, quirk = scmi_quirks_table[i]) {
|
||||
for (unsigned int i = 0; (quirk = scmi_quirks_table[i]); i++) {
|
||||
int ret;
|
||||
|
||||
ret = scmi_quirk_range_parse(quirk);
|
||||
|
||||
@@ -793,7 +793,7 @@ static int scmi_sensor_config_get(const struct scmi_protocol_handle *ph,
|
||||
if (!ret) {
|
||||
struct scmi_sensor_info *s = si->sensors + sensor_id;
|
||||
|
||||
*sensor_config = get_unaligned_le64(t->rx.buf);
|
||||
*sensor_config = get_unaligned_le32(t->rx.buf);
|
||||
s->sensor_config = *sensor_config;
|
||||
}
|
||||
|
||||
@@ -1072,12 +1072,15 @@ scmi_sensor_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
case SCMI_EVENT_SENSOR_UPDATE:
|
||||
{
|
||||
int i;
|
||||
size_t expected_sz;
|
||||
struct scmi_sensor_info *s;
|
||||
const struct scmi_sensor_update_notify_payld *p = payld;
|
||||
struct scmi_sensor_update_report *r = report;
|
||||
struct sensors_info *sinfo = ph->get_priv(ph);
|
||||
|
||||
/* payld_sz is variable for this event */
|
||||
if (payld_sz < sizeof(*p))
|
||||
break;
|
||||
|
||||
r->sensor_id = le32_to_cpu(p->sensor_id);
|
||||
if (r->sensor_id >= sinfo->num_sensors)
|
||||
break;
|
||||
@@ -1091,6 +1094,11 @@ scmi_sensor_fill_custom_report(const struct scmi_protocol_handle *ph,
|
||||
* readings defined for this sensor or 1 for scalar sensors.
|
||||
*/
|
||||
r->readings_count = s->num_axis ?: 1;
|
||||
expected_sz = sizeof(*p) + r->readings_count *
|
||||
sizeof(p->readings[0]);
|
||||
if (payld_sz < expected_sz)
|
||||
break;
|
||||
|
||||
for (i = 0; i < r->readings_count; i++)
|
||||
scmi_parse_sensor_readings(&r->readings[i],
|
||||
&p->readings[i]);
|
||||
|
||||
@@ -154,6 +154,8 @@ static struct scmi_transport_core_operations *core;
|
||||
/* There can be only 1 SCMI service in OP-TEE we connect to */
|
||||
static struct scmi_optee_agent *scmi_optee_private;
|
||||
|
||||
static DEFINE_SCMI_TRANSPORT_SUPPLIER(scmi_optee_supplier);
|
||||
|
||||
/* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */
|
||||
static int open_session(struct scmi_optee_agent *agent, u32 *tee_session)
|
||||
{
|
||||
@@ -522,7 +524,7 @@ static struct scmi_desc scmi_optee_desc = {
|
||||
};
|
||||
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "linaro,scmi-optee" },
|
||||
{ .compatible = "linaro,scmi-optee", .data = &scmi_optee_supplier.th},
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
@@ -561,18 +563,20 @@ static int scmi_optee_service_probe(struct tee_client_device *scmi_pta)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Ensure agent resources are all visible before scmi_optee_private is */
|
||||
/* Ensure initialized scmi_optee_private is visible */
|
||||
smp_mb();
|
||||
scmi_optee_private = agent;
|
||||
|
||||
ret = platform_driver_register(&scmi_optee_driver);
|
||||
if (ret) {
|
||||
scmi_optee_private = NULL;
|
||||
goto err;
|
||||
}
|
||||
ret = scmi_transport_supplier_put(&scmi_optee_supplier.th, agent->dev);
|
||||
if (ret)
|
||||
goto err_put;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put:
|
||||
/* Ensure cleared reference is visible before resources are released */
|
||||
smp_store_mb(scmi_optee_private, NULL);
|
||||
|
||||
err:
|
||||
tee_client_close_context(tee_ctx);
|
||||
|
||||
@@ -586,13 +590,12 @@ static void scmi_optee_service_remove(struct tee_client_device *scmi_pta)
|
||||
if (!scmi_optee_private)
|
||||
return;
|
||||
|
||||
platform_driver_unregister(&scmi_optee_driver);
|
||||
|
||||
if (!list_empty(&scmi_optee_private->channel_list))
|
||||
return;
|
||||
|
||||
/* Ensure cleared reference is visible before resources are released */
|
||||
smp_store_mb(scmi_optee_private, NULL);
|
||||
scmi_transport_supplier_put(&scmi_optee_supplier.th, agent->dev);
|
||||
|
||||
tee_client_close_context(agent->tee_ctx);
|
||||
}
|
||||
@@ -616,7 +619,30 @@ static struct tee_client_driver scmi_optee_service_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
module_tee_client_driver(scmi_optee_service_driver);
|
||||
static int __init scmi_transport_optee_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tee_client_driver_register(&scmi_optee_service_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&scmi_optee_driver);
|
||||
if (ret) {
|
||||
tee_client_driver_unregister(&scmi_optee_service_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(scmi_transport_optee_init);
|
||||
|
||||
static void __exit scmi_transport_optee_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&scmi_optee_driver);
|
||||
tee_client_driver_unregister(&scmi_optee_service_driver);
|
||||
}
|
||||
module_exit(scmi_transport_optee_exit);
|
||||
|
||||
MODULE_AUTHOR("Etienne Carriere <etienne.carriere@foss.st.com>");
|
||||
MODULE_DESCRIPTION("SCMI OPTEE Transport driver");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* (SCMI).
|
||||
*
|
||||
* Copyright (C) 2020-2022 OpenSynergy.
|
||||
* Copyright (C) 2021-2024 ARM Ltd.
|
||||
* Copyright (C) 2021-2026 ARM Ltd.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -116,6 +116,8 @@ static struct scmi_transport_core_operations *core;
|
||||
/* Only one SCMI VirtIO device can possibly exist */
|
||||
static struct virtio_device *scmi_vdev;
|
||||
|
||||
static DEFINE_SCMI_TRANSPORT_SUPPLIER(scmi_virtio_supplier);
|
||||
|
||||
static void scmi_vio_channel_ready(struct scmi_vio_channel *vioch,
|
||||
struct scmi_chan_info *cinfo)
|
||||
{
|
||||
@@ -394,6 +396,10 @@ static bool virtio_chan_available(struct device_node *of_node, int idx)
|
||||
return false;
|
||||
}
|
||||
|
||||
dev_dbg(&scmi_vdev->dev, "%s Channel %sAVAILABLE on SCMI Virtio device.\n",
|
||||
idx == VIRTIO_SCMI_VQ_TX ? "TX" : "RX",
|
||||
(vioch && !vioch->cinfo) ? "" : "NOT ");
|
||||
|
||||
return vioch && !vioch->cinfo;
|
||||
}
|
||||
|
||||
@@ -410,7 +416,7 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
int i;
|
||||
|
||||
if (!scmi_vdev)
|
||||
return -EPROBE_DEFER;
|
||||
return -EINVAL;
|
||||
|
||||
vioch = &((struct scmi_vio_channel *)scmi_vdev->priv)[index];
|
||||
|
||||
@@ -460,6 +466,9 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
|
||||
scmi_vio_channel_ready(vioch, cinfo);
|
||||
|
||||
dev_dbg(&scmi_vdev->dev, "%s Channel SETUP on SCMI Virtio device.\n",
|
||||
tx ? "TX" : "RX");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -801,7 +810,7 @@ static struct scmi_desc scmi_virtio_desc = {
|
||||
};
|
||||
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi-virtio" },
|
||||
{ .compatible = "arm,scmi-virtio", .data = &scmi_virtio_supplier.th},
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
@@ -864,33 +873,33 @@ static int scmi_vio_probe(struct virtio_device *vdev)
|
||||
sz = MSG_TOKEN_MAX;
|
||||
}
|
||||
channels[i].max_msg = sz;
|
||||
dev_dbg(dev, "VQ%d initialized with max_msg: %d\n", i, sz);
|
||||
}
|
||||
|
||||
vdev->priv = channels;
|
||||
|
||||
/* Ensure initialized scmi_vdev is visible */
|
||||
smp_store_mb(scmi_vdev, vdev);
|
||||
|
||||
/* Set device ready */
|
||||
virtio_device_ready(vdev);
|
||||
|
||||
ret = platform_driver_register(&scmi_virtio_driver);
|
||||
ret = scmi_transport_supplier_put(&scmi_virtio_supplier.th, &vdev->dev);
|
||||
if (ret) {
|
||||
virtio_reset_device(vdev);
|
||||
vdev->priv = NULL;
|
||||
vdev->config->del_vqs(vdev);
|
||||
/* Ensure NULLified scmi_vdev is visible */
|
||||
smp_store_mb(scmi_vdev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Probed and initialized SCMI Virtio device.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scmi_vio_remove(struct virtio_device *vdev)
|
||||
{
|
||||
platform_driver_unregister(&scmi_virtio_driver);
|
||||
|
||||
/*
|
||||
* Once we get here, virtio_chan_free() will have already been called by
|
||||
* the SCMI core for any existing channel and, as a consequence, all the
|
||||
@@ -900,8 +909,10 @@ static void scmi_vio_remove(struct virtio_device *vdev)
|
||||
*/
|
||||
virtio_reset_device(vdev);
|
||||
vdev->config->del_vqs(vdev);
|
||||
|
||||
/* Ensure scmi_vdev is visible as NULL */
|
||||
smp_store_mb(scmi_vdev, NULL);
|
||||
scmi_transport_supplier_put(&scmi_virtio_supplier.th, &vdev->dev);
|
||||
}
|
||||
|
||||
static int scmi_vio_validate(struct virtio_device *vdev)
|
||||
@@ -936,7 +947,30 @@ static struct virtio_driver virtio_scmi_driver = {
|
||||
.validate = scmi_vio_validate,
|
||||
};
|
||||
|
||||
module_virtio_driver(virtio_scmi_driver);
|
||||
static int __init scmi_transport_virtio_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_virtio_driver(&virtio_scmi_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&scmi_virtio_driver);
|
||||
if (ret) {
|
||||
unregister_virtio_driver(&virtio_scmi_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(scmi_transport_virtio_init);
|
||||
|
||||
static void __exit scmi_transport_virtio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&scmi_virtio_driver);
|
||||
unregister_virtio_driver(&virtio_scmi_driver);
|
||||
}
|
||||
module_exit(scmi_transport_virtio_exit);
|
||||
|
||||
MODULE_AUTHOR("Igor Skalkin <igor.skalkin@opensynergy.com>");
|
||||
MODULE_AUTHOR("Peter Hilber <peter.hilber@opensynergy.com>");
|
||||
|
||||
@@ -27,6 +27,7 @@ enum scmi_imx_misc_protocol_cmd {
|
||||
SCMI_IMX_MISC_CTRL_GET = 0x4,
|
||||
SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6,
|
||||
SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
|
||||
SCMI_IMX_MISC_RESET_REASON_GET = 0xA,
|
||||
SCMI_IMX_MISC_CFG_INFO_GET = 0xC,
|
||||
SCMI_IMX_MISC_SYSLOG_GET = 0xD,
|
||||
SCMI_IMX_MISC_BOARD_INFO = 0xE,
|
||||
@@ -89,6 +90,37 @@ struct scmi_imx_misc_cfg_info_out {
|
||||
u8 cfgname[MISC_MAX_CFGNAME];
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_reset_reason_in {
|
||||
#define MISC_REASON_FLAG_SYSTEM BIT(0)
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_reset_reason_out {
|
||||
/* Boot reason flags */
|
||||
#define MISC_BOOT_FLAG_VLD BIT(31)
|
||||
#define MISC_BOOT_FLAG_ORG_VLD BIT(28)
|
||||
#define MISC_BOOT_FLAG_ORIGIN GENMASK(27, 24)
|
||||
#define MISC_BOOT_FLAG_O_SHIFT 24
|
||||
#define MISC_BOOT_FLAG_ERR_VLD BIT(23)
|
||||
#define MISC_BOOT_FLAG_ERR_ID GENMASK(22, 8)
|
||||
#define MISC_BOOT_FLAG_E_SHIFT 8
|
||||
#define MISC_BOOT_FLAG_REASON GENMASK(7, 0)
|
||||
__le32 b_flags;
|
||||
/* Shutdown reason flags */
|
||||
#define MISC_SHUTDOWN_FLAG_VLD BIT(31)
|
||||
#define MISC_SHUTDOWN_FLAG_EXT_LEN GENMASK(30, 29)
|
||||
#define MISC_SHUTDOWN_FLAG_ORG_VLD BIT(28)
|
||||
#define MISC_SHUTDOWN_FLAG_ORIGIN GENMASK(27, 24)
|
||||
#define MISC_SHUTDOWN_FLAG_O_SHIFT 24
|
||||
#define MISC_SHUTDOWN_FLAG_ERR_VLD BIT(23)
|
||||
#define MISC_SHUTDOWN_FLAG_ERR_ID GENMASK(22, 8)
|
||||
#define MISC_SHUTDOWN_FLAG_E_SHIFT 8
|
||||
#define MISC_SHUTDOWN_FLAG_REASON GENMASK(7, 0)
|
||||
__le32 s_flags;
|
||||
/* Array of extended info words */
|
||||
__le32 extinfo[MISC_EXT_INFO_LEN_MAX];
|
||||
};
|
||||
|
||||
struct scmi_imx_misc_syslog_in {
|
||||
__le32 flags;
|
||||
__le32 index;
|
||||
@@ -452,11 +484,65 @@ static int scmi_imx_misc_syslog_get(const struct scmi_protocol_handle *ph, u16 *
|
||||
return ph->hops->iter_response_run(iter);
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_reset_reason(const struct scmi_protocol_handle *ph, bool system,
|
||||
struct scmi_imx_misc_reset_reason *boot_r,
|
||||
struct scmi_imx_misc_reset_reason *shut_r,
|
||||
u32 *extinfo)
|
||||
{
|
||||
struct scmi_imx_misc_reset_reason_in *in;
|
||||
struct scmi_imx_misc_reset_reason_out *out;
|
||||
struct scmi_xfer *t;
|
||||
int ret;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_RESET_REASON_GET, sizeof(*in),
|
||||
sizeof(*out), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = t->tx.buf;
|
||||
if (system)
|
||||
in->flags = le32_encode_bits(1, MISC_REASON_FLAG_SYSTEM);
|
||||
else
|
||||
in->flags = cpu_to_le32(0);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
out = t->rx.buf;
|
||||
if (boot_r) {
|
||||
boot_r->valid = le32_get_bits(out->b_flags, MISC_BOOT_FLAG_VLD);
|
||||
boot_r->orig_valid = le32_get_bits(out->b_flags, MISC_BOOT_FLAG_ORG_VLD);
|
||||
boot_r->err_valid = le32_get_bits(out->b_flags, MISC_BOOT_FLAG_ERR_VLD);
|
||||
boot_r->reason = le32_get_bits(out->b_flags, MISC_BOOT_FLAG_REASON);
|
||||
boot_r->origin = le32_get_bits(out->b_flags, MISC_BOOT_FLAG_ORIGIN);
|
||||
boot_r->errid = le32_get_bits(out->b_flags, MISC_BOOT_FLAG_ERR_ID);
|
||||
}
|
||||
|
||||
if (shut_r) {
|
||||
shut_r->valid = le32_get_bits(out->s_flags, MISC_SHUTDOWN_FLAG_VLD);
|
||||
shut_r->orig_valid = le32_get_bits(out->s_flags,
|
||||
MISC_SHUTDOWN_FLAG_ORG_VLD);
|
||||
shut_r->err_valid = le32_get_bits(out->s_flags,
|
||||
MISC_SHUTDOWN_FLAG_ERR_VLD);
|
||||
shut_r->reason = le32_get_bits(out->s_flags, MISC_SHUTDOWN_FLAG_REASON);
|
||||
shut_r->origin = le32_get_bits(out->s_flags, MISC_SHUTDOWN_FLAG_ORIGIN);
|
||||
shut_r->errid = le32_get_bits(out->s_flags, MISC_SHUTDOWN_FLAG_ERR_ID);
|
||||
}
|
||||
|
||||
if (extinfo)
|
||||
memcpy_from_le32(extinfo, out->extinfo, MISC_EXT_INFO_LEN_MAX);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
|
||||
.misc_ctrl_set = scmi_imx_misc_ctrl_set,
|
||||
.misc_ctrl_get = scmi_imx_misc_ctrl_get,
|
||||
.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
|
||||
.misc_syslog = scmi_imx_misc_syslog_get,
|
||||
.misc_reset_reason = scmi_imx_misc_reset_reason,
|
||||
};
|
||||
|
||||
static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
|
||||
@@ -18,6 +18,29 @@ static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops;
|
||||
static struct scmi_protocol_handle *ph;
|
||||
struct notifier_block scmi_imx_misc_ctrl_nb;
|
||||
|
||||
static const char * const rst_imx95[] = {
|
||||
"cm33_lockup", "cm33_swreq", "cm7_lockup", "cm7_swreq", "fccu",
|
||||
"jtag_sw", "ele", "tempsense", "wdog1", "wdog2", "wdog3", "wdog4",
|
||||
"wdog5", "jtag", "cm33_exc", "bbm", "sw", "sm_err", "fusa_sreco",
|
||||
"pmic", "unused", "unused", "unused", "unused", "unused", "unused",
|
||||
"unused", "unused", "unused", "unused", "unused", "por",
|
||||
};
|
||||
|
||||
static const char * const rst_imx94[] = {
|
||||
"cm33_lockup", "cm33_swreq", "cm70_lockup", "cm70_swreq", "fccu",
|
||||
"jtag_sw", "ele", "tempsense", "wdog1", "wdog2", "wdog3", "wdog4",
|
||||
"wdog5", "jtag", "wdog6", "wdog7", "wdog8", "wo_netc", "cm33s_lockup",
|
||||
"cm33s_swreq", "cm71_lockup", "cm71_swreq", "cm33_exc", "bbm", "sw",
|
||||
"sm_err", "fusa_sreco", "pmic", "unused", "unused", "unused", "por",
|
||||
};
|
||||
|
||||
static const struct of_device_id allowlist[] = {
|
||||
{ .compatible = "fsl,imx952", .data = rst_imx95 },
|
||||
{ .compatible = "fsl,imx95", .data = rst_imx95 },
|
||||
{ .compatible = "fsl,imx94", .data = rst_imx94 },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
|
||||
int scmi_imx_misc_ctrl_set(u32 id, u32 val)
|
||||
{
|
||||
if (!ph)
|
||||
@@ -75,6 +98,54 @@ static void scmi_imx_misc_put(void *p)
|
||||
debugfs_remove((struct dentry *)p);
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_get_reason(struct scmi_device *sdev)
|
||||
{
|
||||
struct scmi_imx_misc_reset_reason boot, shutdown;
|
||||
const char **rst;
|
||||
bool system = true;
|
||||
int ret;
|
||||
|
||||
if (!of_machine_device_match(allowlist))
|
||||
return 0;
|
||||
|
||||
rst = (const char **)of_machine_get_match_data(allowlist);
|
||||
|
||||
ret = imx_misc_ctrl_ops->misc_reset_reason(ph, system, &boot, &shutdown, NULL);
|
||||
if (!ret) {
|
||||
if (boot.valid)
|
||||
dev_info(&sdev->dev, "%s Boot reason: %s, origin: %d, errid: %d\n",
|
||||
system ? "SYS" : "LM", rst[boot.reason],
|
||||
boot.orig_valid ? boot.origin : -1,
|
||||
boot.err_valid ? boot.errid : -1);
|
||||
if (shutdown.valid)
|
||||
dev_info(&sdev->dev, "%s shutdown reason: %s, origin: %d, errid: %d\n",
|
||||
system ? "SYS" : "LM", rst[shutdown.reason],
|
||||
shutdown.orig_valid ? shutdown.origin : -1,
|
||||
shutdown.err_valid ? shutdown.errid : -1);
|
||||
} else {
|
||||
dev_err(&sdev->dev, "Failed to get system reset reason: %d\n", ret);
|
||||
}
|
||||
|
||||
system = false;
|
||||
ret = imx_misc_ctrl_ops->misc_reset_reason(ph, system, &boot, &shutdown, NULL);
|
||||
if (!ret) {
|
||||
if (boot.valid)
|
||||
dev_info(&sdev->dev, "%s Boot reason: %s, origin: %d, errid: %d\n",
|
||||
system ? "SYS" : "LM", rst[boot.reason],
|
||||
boot.orig_valid ? boot.origin : -1,
|
||||
boot.err_valid ? boot.errid : -1);
|
||||
if (shutdown.valid)
|
||||
dev_info(&sdev->dev, "%s shutdown reason: %s, origin: %d, errid: %d\n",
|
||||
system ? "SYS" : "LM", rst[shutdown.reason],
|
||||
shutdown.orig_valid ? shutdown.origin : -1,
|
||||
shutdown.err_valid ? shutdown.errid : -1);
|
||||
} else {
|
||||
dev_err(&sdev->dev, "Failed to get lm reset reason: %d\n", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
@@ -133,6 +204,8 @@ static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev)
|
||||
scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL);
|
||||
debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops);
|
||||
|
||||
scmi_imx_misc_get_reason(sdev);
|
||||
|
||||
return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry);
|
||||
}
|
||||
|
||||
|
||||
@@ -2306,6 +2306,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
|
||||
{ .compatible = "lenovo,yoga-slim7x" },
|
||||
{ .compatible = "medion,sprchrgd14s1" },
|
||||
{ .compatible = "microsoft,arcata", },
|
||||
{ .compatible = "microsoft,surface-pro-12in", },
|
||||
{ .compatible = "microsoft,blackrock" },
|
||||
{ .compatible = "microsoft,denali", },
|
||||
{ .compatible = "microsoft,romulus13", },
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
acpm-protocol-objs := exynos-acpm.o
|
||||
acpm-protocol-objs += exynos-acpm-pmic.o
|
||||
acpm-protocol-objs += exynos-acpm-dvfs.o
|
||||
acpm-protocol-objs += exynos-acpm-tmu.o
|
||||
obj-$(CONFIG_EXYNOS_ACPM_PROTOCOL) += acpm-protocol.o
|
||||
|
||||
@@ -21,22 +21,6 @@
|
||||
#define ACPM_DVFS_FREQ_REQ 0
|
||||
#define ACPM_DVFS_FREQ_GET 1
|
||||
|
||||
static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
|
||||
unsigned int acpm_chan_id, bool response)
|
||||
{
|
||||
xfer->acpm_chan_id = acpm_chan_id;
|
||||
xfer->txcnt = cmdlen;
|
||||
xfer->txd = cmd;
|
||||
|
||||
if (response) {
|
||||
xfer->rxcnt = cmdlen;
|
||||
xfer->rxd = cmd;
|
||||
} else {
|
||||
xfer->rxcnt = 0;
|
||||
xfer->rxd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id,
|
||||
unsigned long rate)
|
||||
{
|
||||
@@ -54,7 +38,7 @@ int acpm_dvfs_set_rate(struct acpm_handle *handle,
|
||||
u32 cmd[4];
|
||||
|
||||
acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate);
|
||||
acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false);
|
||||
|
||||
return acpm_do_xfer(handle, &xfer);
|
||||
}
|
||||
@@ -74,7 +58,7 @@ unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_dvfs_init_get_rate_cmd(cmd, clk_id);
|
||||
acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
|
||||
@@ -58,16 +58,6 @@ static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)
|
||||
return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;
|
||||
}
|
||||
|
||||
static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
|
||||
unsigned int acpm_chan_id)
|
||||
{
|
||||
xfer->txd = cmd;
|
||||
xfer->rxd = cmd;
|
||||
xfer->txcnt = cmdlen;
|
||||
xfer->rxcnt = cmdlen;
|
||||
xfer->acpm_chan_id = acpm_chan_id;
|
||||
}
|
||||
|
||||
static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan)
|
||||
{
|
||||
cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |
|
||||
@@ -86,7 +76,7 @@ int acpm_pmic_read_reg(struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_read_cmd(cmd, type, reg, chan);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -119,7 +109,7 @@ int acpm_pmic_bulk_read(struct acpm_handle *handle,
|
||||
return -EINVAL;
|
||||
|
||||
acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -159,7 +149,7 @@ int acpm_pmic_write_reg(struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -199,7 +189,7 @@ int acpm_pmic_bulk_write(struct acpm_handle *handle,
|
||||
return -EINVAL;
|
||||
|
||||
acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -229,7 +219,7 @@ int acpm_pmic_update_reg(struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
|
||||
acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
|
||||
239
drivers/firmware/samsung/exynos-acpm-tmu.c
Normal file
239
drivers/firmware/samsung/exynos-acpm-tmu.c
Normal file
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2020 Samsung Electronics Co., Ltd.
|
||||
* Copyright 2020 Google LLC.
|
||||
* Copyright 2026 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "exynos-acpm.h"
|
||||
#include "exynos-acpm-tmu.h"
|
||||
|
||||
/* IPC Request Types */
|
||||
#define ACPM_TMU_INIT 0x01
|
||||
#define ACPM_TMU_READ_TEMP 0x02
|
||||
#define ACPM_TMU_SUSPEND 0x04
|
||||
#define ACPM_TMU_RESUME 0x10
|
||||
#define ACPM_TMU_THRESHOLD 0x11
|
||||
#define ACPM_TMU_INTEN 0x12
|
||||
#define ACPM_TMU_CONTROL 0x13
|
||||
#define ACPM_TMU_IRQ_CLEAR 0x14
|
||||
|
||||
#define ACPM_TMU_TX_DATA_LEN 8
|
||||
#define ACPM_TMU_RX_DATA_LEN 7
|
||||
|
||||
struct acpm_tmu_tx {
|
||||
u16 ctx;
|
||||
u16 fw_use;
|
||||
u8 type;
|
||||
u8 rsvd0;
|
||||
u8 tzid;
|
||||
u8 rsvd1;
|
||||
u8 data[ACPM_TMU_TX_DATA_LEN];
|
||||
} __packed;
|
||||
|
||||
struct acpm_tmu_rx {
|
||||
u16 ctx;
|
||||
u16 fw_use;
|
||||
u8 type;
|
||||
s8 ret;
|
||||
u8 tzid;
|
||||
s8 temp;
|
||||
u8 rsvd;
|
||||
u8 data[ACPM_TMU_RX_DATA_LEN];
|
||||
} __packed;
|
||||
|
||||
union acpm_tmu_msg {
|
||||
u32 data[4];
|
||||
struct acpm_tmu_tx tx;
|
||||
struct acpm_tmu_rx rx;
|
||||
};
|
||||
|
||||
static int acpm_tmu_to_linux_err(s8 fw_err)
|
||||
{
|
||||
/*
|
||||
* ACPM_TMU_INIT uses BIT(0) and BIT(1) of msg.rx.ret to flag APM
|
||||
* capabilities. Treat zero and all positive values as success.
|
||||
*/
|
||||
if (fw_err >= 0)
|
||||
return 0;
|
||||
|
||||
if (fw_err == -1)
|
||||
return -EACCES;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int acpm_tmu_init(struct acpm_handle *handle, unsigned int acpm_chan_id)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_INIT;
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
|
||||
int acpm_tmu_read_temp(struct acpm_handle *handle, unsigned int acpm_chan_id,
|
||||
u8 tz, int *temp)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_READ_TEMP;
|
||||
msg.tx.tzid = tz;
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*temp = msg.rx.temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpm_tmu_set_threshold(struct acpm_handle *handle,
|
||||
unsigned int acpm_chan_id, u8 tz,
|
||||
const u8 temperature[8], size_t tlen)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
if (tlen > ACPM_TMU_TX_DATA_LEN)
|
||||
return -EINVAL;
|
||||
|
||||
msg.tx.type = ACPM_TMU_THRESHOLD;
|
||||
msg.tx.tzid = tz;
|
||||
memcpy(msg.tx.data, temperature, tlen);
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
|
||||
int acpm_tmu_set_interrupt_enable(struct acpm_handle *handle,
|
||||
unsigned int acpm_chan_id, u8 tz, u8 inten)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_INTEN;
|
||||
msg.tx.tzid = tz;
|
||||
msg.tx.data[0] = inten;
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
|
||||
int acpm_tmu_tz_control(struct acpm_handle *handle, unsigned int acpm_chan_id,
|
||||
u8 tz, bool enable)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_CONTROL;
|
||||
msg.tx.tzid = tz;
|
||||
msg.tx.data[0] = enable ? 1 : 0;
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
|
||||
int acpm_tmu_clear_tz_irq(struct acpm_handle *handle, unsigned int acpm_chan_id,
|
||||
u8 tz)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_IRQ_CLEAR;
|
||||
msg.tx.tzid = tz;
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
|
||||
int acpm_tmu_suspend(struct acpm_handle *handle, unsigned int acpm_chan_id)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_SUSPEND;
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
|
||||
int acpm_tmu_resume(struct acpm_handle *handle, unsigned int acpm_chan_id)
|
||||
{
|
||||
union acpm_tmu_msg msg = {0};
|
||||
struct acpm_xfer xfer;
|
||||
int ret;
|
||||
|
||||
msg.tx.type = ACPM_TMU_RESUME;
|
||||
|
||||
acpm_set_xfer(&xfer, msg.data, ARRAY_SIZE(msg.data), acpm_chan_id,
|
||||
true);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return acpm_tmu_to_linux_err(msg.rx.ret);
|
||||
}
|
||||
28
drivers/firmware/samsung/exynos-acpm-tmu.h
Normal file
28
drivers/firmware/samsung/exynos-acpm-tmu.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2020 Samsung Electronics Co., Ltd.
|
||||
* Copyright 2020 Google LLC.
|
||||
* Copyright 2026 Linaro Ltd.
|
||||
*/
|
||||
#ifndef __EXYNOS_ACPM_TMU_H__
|
||||
#define __EXYNOS_ACPM_TMU_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct acpm_handle;
|
||||
|
||||
int acpm_tmu_init(struct acpm_handle *handle, unsigned int acpm_chan_id);
|
||||
int acpm_tmu_read_temp(struct acpm_handle *handle, unsigned int acpm_chan_id,
|
||||
u8 tz, int *temp);
|
||||
int acpm_tmu_set_threshold(struct acpm_handle *handle,
|
||||
unsigned int acpm_chan_id, u8 tz,
|
||||
const u8 temperature[8], size_t tlen);
|
||||
int acpm_tmu_set_interrupt_enable(struct acpm_handle *handle,
|
||||
unsigned int acpm_chan_id, u8 tz, u8 inten);
|
||||
int acpm_tmu_tz_control(struct acpm_handle *handle, unsigned int acpm_chan_id,
|
||||
u8 tz, bool enable);
|
||||
int acpm_tmu_clear_tz_irq(struct acpm_handle *handle, unsigned int acpm_chan_id,
|
||||
u8 tz);
|
||||
int acpm_tmu_suspend(struct acpm_handle *handle, unsigned int acpm_chan_id);
|
||||
int acpm_tmu_resume(struct acpm_handle *handle, unsigned int acpm_chan_id);
|
||||
#endif /* __EXYNOS_ACPM_TMU_H__ */
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "exynos-acpm.h"
|
||||
#include "exynos-acpm-dvfs.h"
|
||||
#include "exynos-acpm-pmic.h"
|
||||
#include "exynos-acpm-tmu.h"
|
||||
|
||||
#define ACPM_PROTOCOL_SEQNUM GENMASK(21, 16)
|
||||
|
||||
@@ -104,14 +105,14 @@ struct acpm_queue {
|
||||
* struct acpm_rx_data - RX queue data.
|
||||
*
|
||||
* @cmd: pointer to where the data shall be saved.
|
||||
* @n_cmd: number of 32-bit commands.
|
||||
* @cmdcnt: allocated capacity of the @cmd buffer in 32-bit words.
|
||||
* @rxcnt: expected length of the response in 32-bit words.
|
||||
* @completed: flag indicating if the firmware response has been fully
|
||||
* processed.
|
||||
*/
|
||||
struct acpm_rx_data {
|
||||
u32 *cmd;
|
||||
size_t n_cmd;
|
||||
u32 *cmd __counted_by_ptr(cmdcnt);
|
||||
size_t cmdcnt;
|
||||
size_t rxcnt;
|
||||
bool completed;
|
||||
};
|
||||
@@ -428,7 +429,7 @@ static int acpm_prepare_xfer(struct acpm_chan *achan,
|
||||
/* Clear data for upcoming responses */
|
||||
rx_data = &achan->rx_data[bit];
|
||||
rx_data->completed = false;
|
||||
memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd);
|
||||
memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->cmdcnt);
|
||||
/* zero means no response expected */
|
||||
rx_data->rxcnt = xfer->rxcnt;
|
||||
|
||||
@@ -513,6 +514,32 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer)
|
||||
return acpm_wait_for_message_response(achan, xfer);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_set_xfer() - initialize an ACPM IPC transfer structure.
|
||||
* @xfer: pointer to the ACPM transfer structure that is being initialized.
|
||||
* @cmd: pointer to the buffer containing the command to be transmitted
|
||||
* to the ACPM firmware.
|
||||
* @cmdcnt: length of the command in 32-bit words.
|
||||
* @acpm_chan_id: mailbox channel identifier.
|
||||
* @response: boolean flag indicating whether the kernel expects the ACPM
|
||||
* firmware to send a reply to this specific command.
|
||||
*/
|
||||
void acpm_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdcnt,
|
||||
unsigned int acpm_chan_id, bool response)
|
||||
{
|
||||
xfer->acpm_chan_id = acpm_chan_id;
|
||||
xfer->txcnt = cmdcnt;
|
||||
xfer->txd = cmd;
|
||||
|
||||
if (response) {
|
||||
xfer->rxcnt = cmdcnt;
|
||||
xfer->rxd = cmd;
|
||||
} else {
|
||||
xfer->rxcnt = 0;
|
||||
xfer->rxd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_chan_shmem_get_params() - get channel parameters and addresses of the
|
||||
* TX/RX queues.
|
||||
@@ -554,19 +581,19 @@ static int acpm_achan_alloc_cmds(struct acpm_chan *achan)
|
||||
{
|
||||
struct device *dev = achan->acpm->dev;
|
||||
struct acpm_rx_data *rx_data;
|
||||
size_t cmd_size, n_cmd;
|
||||
size_t cmd_size, cmdcnt;
|
||||
int i;
|
||||
|
||||
if (achan->mlen == 0)
|
||||
return 0;
|
||||
|
||||
cmd_size = sizeof(*(achan->rx_data[0].cmd));
|
||||
n_cmd = DIV_ROUND_UP_ULL(achan->mlen, cmd_size);
|
||||
cmdcnt = DIV_ROUND_UP_ULL(achan->mlen, cmd_size);
|
||||
|
||||
for (i = 0; i < ACPM_SEQNUM_MAX; i++) {
|
||||
rx_data = &achan->rx_data[i];
|
||||
rx_data->n_cmd = n_cmd;
|
||||
rx_data->cmd = devm_kcalloc(dev, n_cmd, cmd_size, GFP_KERNEL);
|
||||
rx_data->cmdcnt = cmdcnt;
|
||||
rx_data->cmd = devm_kcalloc(dev, cmdcnt, cmd_size, GFP_KERNEL);
|
||||
if (!rx_data->cmd)
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -640,30 +667,37 @@ static int acpm_channels_init(struct acpm_info *acpm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_setup_ops() - setup the operations structures.
|
||||
* @acpm: pointer to the driver data.
|
||||
*/
|
||||
static void acpm_setup_ops(struct acpm_info *acpm)
|
||||
{
|
||||
struct acpm_dvfs_ops *dvfs_ops = &acpm->handle.ops.dvfs_ops;
|
||||
struct acpm_pmic_ops *pmic_ops = &acpm->handle.ops.pmic_ops;
|
||||
|
||||
dvfs_ops->set_rate = acpm_dvfs_set_rate;
|
||||
dvfs_ops->get_rate = acpm_dvfs_get_rate;
|
||||
|
||||
pmic_ops->read_reg = acpm_pmic_read_reg;
|
||||
pmic_ops->bulk_read = acpm_pmic_bulk_read;
|
||||
pmic_ops->write_reg = acpm_pmic_write_reg;
|
||||
pmic_ops->bulk_write = acpm_pmic_bulk_write;
|
||||
pmic_ops->update_reg = acpm_pmic_update_reg;
|
||||
}
|
||||
|
||||
static void acpm_clk_pdev_unregister(void *data)
|
||||
{
|
||||
platform_device_unregister(data);
|
||||
}
|
||||
|
||||
static const struct acpm_ops exynos_acpm_driver_ops = {
|
||||
.dvfs = {
|
||||
.set_rate = acpm_dvfs_set_rate,
|
||||
.get_rate = acpm_dvfs_get_rate,
|
||||
},
|
||||
|
||||
.pmic = {
|
||||
.read_reg = acpm_pmic_read_reg,
|
||||
.bulk_read = acpm_pmic_bulk_read,
|
||||
.write_reg = acpm_pmic_write_reg,
|
||||
.bulk_write = acpm_pmic_bulk_write,
|
||||
.update_reg = acpm_pmic_update_reg,
|
||||
},
|
||||
|
||||
.tmu = {
|
||||
.init = acpm_tmu_init,
|
||||
.read_temp = acpm_tmu_read_temp,
|
||||
.set_threshold = acpm_tmu_set_threshold,
|
||||
.set_interrupt_enable = acpm_tmu_set_interrupt_enable,
|
||||
.tz_control = acpm_tmu_tz_control,
|
||||
.clear_tz_irq = acpm_tmu_clear_tz_irq,
|
||||
.suspend = acpm_tmu_suspend,
|
||||
.resume = acpm_tmu_resume,
|
||||
},
|
||||
};
|
||||
|
||||
static int acpm_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct acpm_match_data *match_data;
|
||||
@@ -704,7 +738,7 @@ static int acpm_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
acpm_setup_ops(acpm);
|
||||
acpm->handle.ops = &exynos_acpm_driver_ops;
|
||||
|
||||
platform_set_drvdata(pdev, acpm);
|
||||
|
||||
@@ -819,6 +853,29 @@ struct acpm_handle *devm_acpm_get_by_node(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_acpm_get_by_node);
|
||||
|
||||
/**
|
||||
* devm_acpm_get_by_phandle - Resource managed lookup of the standardized
|
||||
* "samsung,acpm-ipc" handle.
|
||||
* @dev: consumer device
|
||||
*
|
||||
* Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
|
||||
*/
|
||||
struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev)
|
||||
{
|
||||
struct acpm_handle *handle;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "samsung,acpm-ipc", 0);
|
||||
if (!np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
handle = devm_acpm_get_by_node(dev, np);
|
||||
of_node_put(np);
|
||||
|
||||
return handle;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_acpm_get_by_phandle);
|
||||
|
||||
static const struct acpm_match_data acpm_gs101 = {
|
||||
.initdata_base = ACPM_GS101_INITDATA_BASE,
|
||||
.acpm_clk_dev_name = "gs101-acpm-clk",
|
||||
|
||||
@@ -17,6 +17,8 @@ struct acpm_xfer {
|
||||
|
||||
struct acpm_handle;
|
||||
|
||||
void acpm_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdcnt,
|
||||
unsigned int acpm_chan_id, bool response);
|
||||
int acpm_do_xfer(struct acpm_handle *handle,
|
||||
const struct acpm_xfer *xfer);
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ static char __init *smccc_soc_name_init(void)
|
||||
* to the ARM_SMCCC_ARCH_SOC_ID function. Fetch it if
|
||||
* available.
|
||||
*/
|
||||
args.a0 = ARM_SMCCC_ARCH_SOC_ID;
|
||||
args.a0 = ARM_SMCCC_ARCH_SOC_ID64;
|
||||
args.a1 = 2; /* SOC_ID name */
|
||||
arm_smccc_1_2_invoke(&args, &res);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
menu "Tegra firmware driver"
|
||||
|
||||
config TEGRA_IVC
|
||||
bool "Tegra IVC protocol" if COMPILE_TEST
|
||||
bool
|
||||
depends on ARCH_TEGRA
|
||||
help
|
||||
IVC (Inter-VM Communication) protocol is part of the IPC
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
/*
|
||||
* Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -468,7 +470,7 @@ static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp,
|
||||
dentry = debugfs_create_file(name, mode, parent, bpmp,
|
||||
&bpmp_debug_fops);
|
||||
if (IS_ERR(dentry)) {
|
||||
err = -ENOMEM;
|
||||
err = PTR_ERR(dentry);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -719,7 +721,7 @@ static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
|
||||
if (t & DEBUGFS_S_ISDIR) {
|
||||
dentry = debugfs_create_dir(name, parent);
|
||||
if (IS_ERR(dentry))
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(dentry);
|
||||
err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@@ -732,7 +734,7 @@ static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
|
||||
parent, bpmp,
|
||||
&debugfs_fops);
|
||||
if (IS_ERR(dentry))
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,9 +771,21 @@ static int bpmp_populate_debugfs_shmem(struct tegra_bpmp *bpmp)
|
||||
return err;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(bpmp_debugfs_root_lock);
|
||||
static struct dentry *bpmp_debugfs_root;
|
||||
|
||||
static struct dentry *bpmp_debugfs_get_root(void)
|
||||
{
|
||||
guard(mutex)(&bpmp_debugfs_root_lock);
|
||||
if (!bpmp_debugfs_root)
|
||||
bpmp_debugfs_root = debugfs_create_dir("bpmp", NULL);
|
||||
return bpmp_debugfs_root;
|
||||
}
|
||||
|
||||
int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct dentry *root, *d;
|
||||
char name[32];
|
||||
bool inband;
|
||||
int err;
|
||||
|
||||
@@ -780,13 +794,24 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
|
||||
if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
|
||||
return 0;
|
||||
|
||||
root = debugfs_create_dir("bpmp", NULL);
|
||||
root = bpmp_debugfs_get_root();
|
||||
if (IS_ERR(root))
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(root);
|
||||
|
||||
bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
|
||||
if (dev_to_node(bpmp->dev) == NUMA_NO_NODE) {
|
||||
d = root;
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "%d-bpmp", dev_to_node(bpmp->dev));
|
||||
d = debugfs_create_dir(name, root);
|
||||
if (IS_ERR(d)) {
|
||||
err = PTR_ERR(d);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
bpmp->debugfs_mirror = debugfs_create_dir("debug", d);
|
||||
if (IS_ERR(bpmp->debugfs_mirror)) {
|
||||
err = -ENOMEM;
|
||||
err = PTR_ERR(bpmp->debugfs_mirror);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -797,8 +822,18 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
|
||||
err = bpmp_populate_debugfs_shmem(bpmp);
|
||||
|
||||
out:
|
||||
if (err < 0)
|
||||
debugfs_remove_recursive(root);
|
||||
if (err < 0) {
|
||||
if (!IS_ERR(d))
|
||||
debugfs_remove_recursive(d);
|
||||
|
||||
guard(mutex)(&bpmp_debugfs_root_lock);
|
||||
if (root == d) {
|
||||
bpmp_debugfs_root = NULL;
|
||||
} else if (simple_empty(root)) {
|
||||
debugfs_remove(root);
|
||||
bpmp_debugfs_root = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -87,6 +90,16 @@ struct ti_sci_desc {
|
||||
int max_msg_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_sci_irq - Description of allocated irqs
|
||||
* @node: Link to hash table
|
||||
* @desc: Description of the irq
|
||||
*/
|
||||
struct ti_sci_irq {
|
||||
struct hlist_node node;
|
||||
struct ti_sci_msg_req_manage_irq desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_sci_info - Structure representing a TI SCI instance
|
||||
* @dev: Device pointer
|
||||
@@ -101,6 +114,8 @@ struct ti_sci_desc {
|
||||
* @chan_rx: Receive mailbox channel
|
||||
* @minfo: Message info
|
||||
* @node: list head
|
||||
* @irqs: List of allocated irqs
|
||||
* @irq_lock: Protection for irq hash list
|
||||
* @host_id: Host ID
|
||||
* @fw_caps: FW/SoC low power capabilities
|
||||
* @users: Number of users of this instance
|
||||
@@ -118,6 +133,8 @@ struct ti_sci_info {
|
||||
struct mbox_chan *chan_rx;
|
||||
struct ti_sci_xfers_info minfo;
|
||||
struct list_head node;
|
||||
DECLARE_HASHTABLE(irqs, 8);
|
||||
struct mutex irq_lock;
|
||||
u8 host_id;
|
||||
u64 fw_caps;
|
||||
/* protected by ti_sci_list_mutex */
|
||||
@@ -2299,6 +2316,32 @@ static int ti_sci_manage_irq(const struct ti_sci_handle *handle,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_irq_hash() - Helper API to compute irq hash for the hash table.
|
||||
* @irq: irq to hash
|
||||
*
|
||||
* Return: the computed hash value.
|
||||
*/
|
||||
static int ti_sci_irq_hash(struct ti_sci_msg_req_manage_irq *irq)
|
||||
{
|
||||
return irq->src_id ^ irq->src_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_irq_equal() - Helper API to compare two irqs (generic headers are not
|
||||
* compared)
|
||||
* @irq_a: irq_a to compare
|
||||
* @irq_b: irq_b to compare
|
||||
*
|
||||
* Return: true if the two irqs are equal, else false.
|
||||
*/
|
||||
static bool ti_sci_irq_equal(struct ti_sci_msg_req_manage_irq *irq_a,
|
||||
struct ti_sci_msg_req_manage_irq *irq_b)
|
||||
{
|
||||
return !memcmp(&irq_a->valid_params, &irq_b->valid_params,
|
||||
sizeof(*irq_a) - sizeof(irq_a->hdr));
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_set_irq() - Helper api to configure the irq route between the
|
||||
* requested source and destination
|
||||
@@ -2322,15 +2365,53 @@ static int ti_sci_set_irq(const struct ti_sci_handle *handle, u32 valid_params,
|
||||
u16 dst_host_irq, u16 ia_id, u16 vint,
|
||||
u16 global_event, u8 vint_status_bit, u8 s_host)
|
||||
{
|
||||
struct ti_sci_info *info = handle_to_ti_sci_info(handle);
|
||||
struct ti_sci_msg_req_manage_irq *desc;
|
||||
struct ti_sci_irq *irq;
|
||||
int ret;
|
||||
|
||||
/* Lock for set_irq() and free_irq() to keep the IRQ hash list consistent */
|
||||
guard(mutex)(&info->irq_lock);
|
||||
|
||||
pr_debug("%s: IRQ set with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n",
|
||||
__func__, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint, global_event,
|
||||
vint_status_bit);
|
||||
|
||||
return ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_SET_IRQ);
|
||||
ret = ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_SET_IRQ);
|
||||
|
||||
if (ret || !(info->fw_caps & MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST))
|
||||
goto end;
|
||||
|
||||
irq = kzalloc_obj(*irq, GFP_KERNEL);
|
||||
if (!irq) {
|
||||
ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_FREE_IRQ);
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
desc = &irq->desc;
|
||||
desc->valid_params = valid_params;
|
||||
desc->src_id = src_id;
|
||||
desc->src_index = src_index;
|
||||
desc->dst_id = dst_id;
|
||||
desc->dst_host_irq = dst_host_irq;
|
||||
desc->ia_id = ia_id;
|
||||
desc->vint = vint;
|
||||
desc->global_event = global_event;
|
||||
desc->vint_status_bit = vint_status_bit;
|
||||
desc->secondary_host = s_host;
|
||||
|
||||
hash_add(info->irqs, &irq->node, ti_sci_irq_hash(desc));
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2356,15 +2437,53 @@ static int ti_sci_free_irq(const struct ti_sci_handle *handle, u32 valid_params,
|
||||
u16 dst_host_irq, u16 ia_id, u16 vint,
|
||||
u16 global_event, u8 vint_status_bit, u8 s_host)
|
||||
{
|
||||
struct ti_sci_info *info = handle_to_ti_sci_info(handle);
|
||||
struct ti_sci_msg_req_manage_irq irq_desc;
|
||||
struct device *dev = info->dev;
|
||||
struct ti_sci_irq *this_irq;
|
||||
struct hlist_node *tmp_node;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&info->irq_lock);
|
||||
|
||||
pr_debug("%s: IRQ release with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n",
|
||||
__func__, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint, global_event,
|
||||
vint_status_bit);
|
||||
|
||||
return ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_FREE_IRQ);
|
||||
ret = ti_sci_manage_irq(handle, valid_params, src_id, src_index,
|
||||
dst_id, dst_host_irq, ia_id, vint,
|
||||
global_event, vint_status_bit, s_host,
|
||||
TI_SCI_MSG_FREE_IRQ);
|
||||
|
||||
if (ret || !(info->fw_caps & MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST))
|
||||
goto end;
|
||||
|
||||
irq_desc.valid_params = valid_params;
|
||||
irq_desc.src_id = src_id;
|
||||
irq_desc.src_index = src_index;
|
||||
irq_desc.dst_id = dst_id;
|
||||
irq_desc.dst_host_irq = dst_host_irq;
|
||||
irq_desc.ia_id = ia_id;
|
||||
irq_desc.vint = vint;
|
||||
irq_desc.global_event = global_event;
|
||||
irq_desc.vint_status_bit = vint_status_bit;
|
||||
irq_desc.secondary_host = s_host;
|
||||
|
||||
hash_for_each_possible_safe(info->irqs, this_irq, tmp_node, node,
|
||||
ti_sci_irq_hash(&irq_desc)) {
|
||||
if (ti_sci_irq_equal(&irq_desc, &this_irq->desc)) {
|
||||
hlist_del(&this_irq->node);
|
||||
kfree(this_irq);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
dev_warn(dev, "%s: should not be here, IRQ was not found in hash list\n",
|
||||
__func__);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3770,8 +3889,11 @@ static int ti_sci_prepare_system_suspend(struct ti_sci_info *info)
|
||||
return ti_sci_cmd_prepare_sleep(&info->handle,
|
||||
TISCI_MSG_VALUE_SLEEP_MODE_DM_MANAGED,
|
||||
0, 0, 0);
|
||||
} else if (info->fw_caps & MSG_FLAG_CAPS_LPM_BOARDCFG_MANAGED) {
|
||||
/* Nothing to do in the BOARDCFG_MANAGED mode */
|
||||
return 0;
|
||||
} else {
|
||||
/* DM Managed is not supported by the firmware. */
|
||||
/* DM Managed and BoardCfg Managed are not supported by the firmware. */
|
||||
dev_err(info->dev, "Suspend to memory is not supported by the firmware\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@@ -3842,7 +3964,10 @@ static int ti_sci_suspend_noirq(struct device *dev)
|
||||
static int ti_sci_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct ti_sci_info *info = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
struct ti_sci_msg_req_manage_irq *irq_desc;
|
||||
struct ti_sci_irq *irq;
|
||||
struct hlist_node *tmp_node;
|
||||
int ret = 0, err = 0, i;
|
||||
u32 source;
|
||||
u64 time;
|
||||
u8 pin;
|
||||
@@ -3854,13 +3979,53 @@ static int ti_sci_resume_noirq(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (pm_suspend_target_state) {
|
||||
case PM_SUSPEND_MEM:
|
||||
if (info->fw_caps & MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST) {
|
||||
hash_for_each_safe(info->irqs, i, tmp_node, irq, node) {
|
||||
irq_desc = &irq->desc;
|
||||
ret = ti_sci_manage_irq(&info->handle,
|
||||
irq_desc->valid_params,
|
||||
irq_desc->src_id,
|
||||
irq_desc->src_index,
|
||||
irq_desc->dst_id,
|
||||
irq_desc->dst_host_irq,
|
||||
irq_desc->ia_id,
|
||||
irq_desc->vint,
|
||||
irq_desc->global_event,
|
||||
irq_desc->vint_status_bit,
|
||||
irq_desc->secondary_host,
|
||||
TI_SCI_MSG_SET_IRQ);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to restore IRQ with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d, via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n",
|
||||
irq_desc->valid_params,
|
||||
irq_desc->src_id,
|
||||
irq_desc->src_index,
|
||||
irq_desc->dst_id,
|
||||
irq_desc->dst_host_irq,
|
||||
irq_desc->ia_id,
|
||||
irq_desc->vint,
|
||||
irq_desc->global_event,
|
||||
irq_desc->vint_status_bit);
|
||||
err = ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info->fw_caps & MSG_FLAG_CAPS_LPM_CLK_CONTEXT_LOST)
|
||||
clk_restore_context();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ti_sci_msg_cmd_lpm_wake_reason(&info->handle, &source, &time, &pin, &mode);
|
||||
/* Do not fail to resume on error as the wake reason is not critical */
|
||||
if (!ret)
|
||||
dev_info(dev, "ti_sci: wakeup source:0x%x, pin:0x%x, mode:0x%x\n",
|
||||
source, pin, mode);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ti_sci_pm_complete(struct device *dev)
|
||||
@@ -4009,12 +4174,15 @@ static int ti_sci_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ti_sci_msg_cmd_query_fw_caps(&info->handle, &info->fw_caps);
|
||||
dev_dbg(dev, "Detected firmware capabilities: %s%s%s%s%s\n",
|
||||
dev_dbg(dev, "Detected firmware capabilities: %s%s%s%s%s%s%s%s\n",
|
||||
info->fw_caps & MSG_FLAG_CAPS_GENERIC ? "Generic" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_LPM_PARTIAL_IO ? " Partial-IO" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED ? " DM-Managed" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_LPM_ABORT ? " LPM-Abort" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_IO_ISOLATION ? " IO-Isolation" : ""
|
||||
info->fw_caps & MSG_FLAG_CAPS_IO_ISOLATION ? " IO-Isolation" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_LPM_BOARDCFG_MANAGED ? " BoardConfig-Managed" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST ? " IRQ-Context-Lost" : "",
|
||||
info->fw_caps & MSG_FLAG_CAPS_LPM_CLK_CONTEXT_LOST ? " Clk-Context-Lost" : ""
|
||||
);
|
||||
|
||||
ti_sci_setup_ops(info);
|
||||
@@ -4047,6 +4215,13 @@ static int ti_sci_probe(struct platform_device *pdev)
|
||||
list_add_tail(&info->node, &ti_sci_list);
|
||||
mutex_unlock(&ti_sci_list_mutex);
|
||||
|
||||
ret = devm_mutex_init(dev, &info->irq_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (info->fw_caps & MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST)
|
||||
hash_init(info->irqs);
|
||||
|
||||
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "platform_populate failed %pe\n", ERR_PTR(ret));
|
||||
|
||||
@@ -150,6 +150,12 @@ struct ti_sci_msg_req_reboot {
|
||||
* MSG_FLAG_CAPS_LPM_DM_MANAGED: LPM can be managed by DM
|
||||
* MSG_FLAG_CAPS_LPM_ABORT: Abort entry to LPM
|
||||
* MSG_FLAG_CAPS_IO_ISOLATION: IO Isolation support
|
||||
* MSG_FLAG_CAPS_LPM_BOARDCFG_MANAGED: LPM config done statically
|
||||
* for the DM via boardcfg
|
||||
* MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST: DM is not able to restore IRQ
|
||||
* context
|
||||
* MSG_FLAG_CAPS_LPM_CLK_CONTEXT_LOST: DM is not able to restore
|
||||
* Clock context
|
||||
*
|
||||
* Response to a generic message with message type TI_SCI_MSG_QUERY_FW_CAPS
|
||||
* providing currently available SOC/firmware capabilities. SoC that don't
|
||||
@@ -162,6 +168,9 @@ struct ti_sci_msg_resp_query_fw_caps {
|
||||
#define MSG_FLAG_CAPS_LPM_DM_MANAGED TI_SCI_MSG_FLAG(5)
|
||||
#define MSG_FLAG_CAPS_LPM_ABORT TI_SCI_MSG_FLAG(9)
|
||||
#define MSG_FLAG_CAPS_IO_ISOLATION TI_SCI_MSG_FLAG(7)
|
||||
#define MSG_FLAG_CAPS_LPM_BOARDCFG_MANAGED TI_SCI_MSG_FLAG(12)
|
||||
#define MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST TI_SCI_MSG_FLAG(14)
|
||||
#define MSG_FLAG_CAPS_LPM_CLK_CONTEXT_LOST TI_SCI_MSG_FLAG(15)
|
||||
#define MSG_MASK_CAPS_LPM GENMASK_ULL(4, 1)
|
||||
u64 fw_caps;
|
||||
} __packed;
|
||||
|
||||
@@ -18,6 +18,8 @@ struct nvkm_device_tegra {
|
||||
|
||||
struct regulator *vdd;
|
||||
|
||||
struct tegra_pmc *pmc;
|
||||
|
||||
struct {
|
||||
/*
|
||||
* Protects accesses to mm from subsystems
|
||||
|
||||
@@ -54,7 +54,8 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev)
|
||||
reset_control_assert(tdev->rst);
|
||||
udelay(10);
|
||||
|
||||
ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
|
||||
ret = tegra_pmc_powergate_remove_clamping(tdev->pmc,
|
||||
TEGRA_POWERGATE_3D);
|
||||
if (ret)
|
||||
goto err_clamp;
|
||||
udelay(10);
|
||||
@@ -307,6 +308,12 @@ nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func,
|
||||
goto free;
|
||||
}
|
||||
|
||||
tdev->pmc = devm_tegra_pmc_get(&pdev->dev);
|
||||
if (IS_ERR(tdev->pmc)) {
|
||||
ret = PTR_ERR(tdev->pmc);
|
||||
goto free;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IOMMU bit defines the upper limit of the GPU-addressable space.
|
||||
*/
|
||||
|
||||
@@ -2879,7 +2879,7 @@ static int tegra_dc_runtime_suspend(struct host1x_client *client)
|
||||
}
|
||||
|
||||
if (dc->soc->has_powergate)
|
||||
tegra_powergate_power_off(dc->powergate);
|
||||
tegra_pmc_powergate_power_off(dc->pmc, dc->powergate);
|
||||
|
||||
clk_disable_unprepare(dc->clk);
|
||||
pm_runtime_put_sync(dev);
|
||||
@@ -2900,8 +2900,9 @@ static int tegra_dc_runtime_resume(struct host1x_client *client)
|
||||
}
|
||||
|
||||
if (dc->soc->has_powergate) {
|
||||
err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
|
||||
dc->rst);
|
||||
err = tegra_pmc_powergate_sequence_power_up(dc->pmc,
|
||||
dc->powergate,
|
||||
dc->clk, dc->rst);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to power partition: %d\n", err);
|
||||
goto put_rpm;
|
||||
@@ -3330,12 +3331,17 @@ static int tegra_dc_probe(struct platform_device *pdev)
|
||||
clk_disable_unprepare(dc->clk);
|
||||
|
||||
if (dc->soc->has_powergate) {
|
||||
dc->pmc = devm_tegra_pmc_get(dc->dev);
|
||||
if (IS_ERR(dc->pmc))
|
||||
return dev_err_probe(dc->dev, PTR_ERR(dc->pmc),
|
||||
"failed to get PMC\n");
|
||||
|
||||
if (dc->pipe == 0)
|
||||
dc->powergate = TEGRA_POWERGATE_DIS;
|
||||
else
|
||||
dc->powergate = TEGRA_POWERGATE_DISB;
|
||||
|
||||
tegra_powergate_power_off(dc->powergate);
|
||||
tegra_pmc_powergate_power_off(dc->pmc, dc->powergate);
|
||||
}
|
||||
|
||||
err = tegra_dc_init_opp_table(dc);
|
||||
|
||||
@@ -94,6 +94,7 @@ struct tegra_dc {
|
||||
int irq;
|
||||
|
||||
struct tegra_output *rgb;
|
||||
struct tegra_pmc *pmc;
|
||||
|
||||
struct tegra_dc_stats stats;
|
||||
struct list_head list;
|
||||
|
||||
@@ -46,6 +46,7 @@ struct gr3d {
|
||||
unsigned int nclocks;
|
||||
struct reset_control_bulk_data resets[RST_GR3D_MAX];
|
||||
unsigned int nresets;
|
||||
struct tegra_pmc *pmc;
|
||||
struct dev_pm_domain_list *pd_list;
|
||||
|
||||
DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
|
||||
@@ -350,7 +351,8 @@ static int gr3d_power_up_legacy_domain(struct device *dev, const char *name,
|
||||
if (err) {
|
||||
dev_err(dev, "failed to acquire %s reset: %d\n", name, err);
|
||||
} else {
|
||||
err = tegra_powergate_sequence_power_up(id, clk, reset);
|
||||
err = tegra_pmc_powergate_sequence_power_up(gr3d->pmc, id,
|
||||
clk, reset);
|
||||
reset_control_release(reset);
|
||||
}
|
||||
|
||||
@@ -382,6 +384,11 @@ static int gr3d_init_power(struct device *dev, struct gr3d *gr3d)
|
||||
if (err != -ENOENT)
|
||||
return err;
|
||||
|
||||
gr3d->pmc = devm_tegra_pmc_get(dev);
|
||||
if (IS_ERR(gr3d->pmc))
|
||||
return dev_err_probe(dev, PTR_ERR(gr3d->pmc),
|
||||
"failed to get PMC\n");
|
||||
|
||||
/*
|
||||
* Older device-trees don't use GENPD. In this case we should
|
||||
* toggle power domain manually.
|
||||
|
||||
@@ -423,6 +423,8 @@ struct tegra_sor {
|
||||
struct clk *clk_dp;
|
||||
struct clk *clk;
|
||||
|
||||
struct tegra_pmc *pmc;
|
||||
|
||||
u8 xbar_cfg[5];
|
||||
|
||||
struct drm_dp_link link;
|
||||
@@ -2238,7 +2240,7 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to power down SOR: %d\n", err);
|
||||
|
||||
err = tegra_io_pad_power_disable(sor->pad);
|
||||
err = tegra_pmc_io_pad_power_disable(sor->pmc, sor->pad);
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
|
||||
|
||||
@@ -2278,7 +2280,7 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
|
||||
|
||||
div = clk_get_rate(sor->clk) / 1000000 * 4;
|
||||
|
||||
err = tegra_io_pad_power_enable(sor->pad);
|
||||
err = tegra_pmc_io_pad_power_enable(sor->pmc, sor->pad);
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to power on I/O pad: %d\n", err);
|
||||
|
||||
@@ -2713,7 +2715,7 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to power down SOR: %d\n", err);
|
||||
|
||||
err = tegra_io_pad_power_disable(sor->pad);
|
||||
err = tegra_pmc_io_pad_power_disable(sor->pmc, sor->pad);
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
|
||||
|
||||
@@ -2755,7 +2757,7 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
|
||||
|
||||
err = tegra_io_pad_power_enable(sor->pad);
|
||||
err = tegra_pmc_io_pad_power_enable(sor->pmc, sor->pad);
|
||||
if (err < 0)
|
||||
dev_err(sor->dev, "failed to power on LVDS rail: %d\n", err);
|
||||
|
||||
@@ -3761,6 +3763,12 @@ static int tegra_sor_probe(struct platform_device *pdev)
|
||||
|
||||
sor->num_settings = sor->soc->num_settings;
|
||||
|
||||
sor->pmc = devm_tegra_pmc_get(&pdev->dev);
|
||||
if (IS_ERR(sor->pmc)) {
|
||||
err = PTR_ERR(sor->pmc);
|
||||
goto put_aux;
|
||||
}
|
||||
|
||||
np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
|
||||
if (np) {
|
||||
sor->aux = drm_dp_aux_find_by_of_node(np);
|
||||
|
||||
@@ -51,12 +51,6 @@ config ARM_GIC_V3_ITS
|
||||
default ARM_GIC_V3
|
||||
select IRQ_MSI_IOMMU
|
||||
|
||||
config ARM_GIC_V3_ITS_FSL_MC
|
||||
bool
|
||||
depends on ARM_GIC_V3_ITS
|
||||
depends on FSL_MC_BUS
|
||||
default ARM_GIC_V3_ITS
|
||||
|
||||
config ARM_GIC_V5
|
||||
bool
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
@@ -35,7 +35,6 @@ obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
|
||||
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o
|
||||
obj-$(CONFIG_ARM_GIC_ITS_PARENT) += irq-gic-its-msi-parent.o
|
||||
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v4.o
|
||||
obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o
|
||||
obj-$(CONFIG_ARM_GIC_V5) += irq-gic-v5.o irq-gic-v5-irs.o irq-gic-v5-its.o \
|
||||
irq-gic-v5-iwb.o
|
||||
obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright (C) 2022 Intel
|
||||
|
||||
#include <linux/acpi_iort.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
@@ -187,9 +188,11 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
{
|
||||
struct msi_domain_info *msi_info;
|
||||
u32 dev_id;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (dev->of_node)
|
||||
if (dev_is_fsl_mc(dev))
|
||||
dev_id = fsl_mc_get_msi_id(dev);
|
||||
else if (dev->of_node)
|
||||
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
|
||||
else
|
||||
ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Freescale Management Complex (MC) bus driver MSI support
|
||||
*
|
||||
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
|
||||
* Author: German Rivera <German.Rivera@freescale.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_iort.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
|
||||
static struct irq_chip its_msi_irq_chip = {
|
||||
.name = "ITS-fMSI",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_set_affinity = msi_domain_set_affinity
|
||||
};
|
||||
|
||||
static u32 fsl_mc_msi_domain_get_msi_id(struct irq_domain *domain,
|
||||
struct fsl_mc_device *mc_dev)
|
||||
{
|
||||
struct device_node *of_node;
|
||||
u32 out_id;
|
||||
|
||||
of_node = irq_domain_get_of_node(domain);
|
||||
out_id = of_node ? of_msi_xlate(&mc_dev->dev, &of_node, mc_dev->icid) :
|
||||
iort_msi_map_id(&mc_dev->dev, mc_dev->icid);
|
||||
|
||||
return out_id;
|
||||
}
|
||||
|
||||
static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,
|
||||
struct device *dev,
|
||||
int nvec, msi_alloc_info_t *info)
|
||||
{
|
||||
struct fsl_mc_device *mc_bus_dev;
|
||||
struct msi_domain_info *msi_info;
|
||||
|
||||
if (!dev_is_fsl_mc(dev))
|
||||
return -EINVAL;
|
||||
|
||||
mc_bus_dev = to_fsl_mc_device(dev);
|
||||
if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Set the device Id to be passed to the GIC-ITS:
|
||||
*
|
||||
* NOTE: This device id corresponds to the IOMMU stream ID
|
||||
* associated with the DPRC object (ICID).
|
||||
*/
|
||||
info->scratchpad[0].ul = fsl_mc_msi_domain_get_msi_id(msi_domain,
|
||||
mc_bus_dev);
|
||||
msi_info = msi_get_domain_info(msi_domain->parent);
|
||||
|
||||
/* Allocate at least 32 MSIs, and always as a power of 2 */
|
||||
nvec = max_t(int, 32, roundup_pow_of_two(nvec));
|
||||
return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
|
||||
}
|
||||
|
||||
static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = {
|
||||
.msi_prepare = its_fsl_mc_msi_prepare,
|
||||
};
|
||||
|
||||
static struct msi_domain_info its_fsl_mc_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
|
||||
.ops = &its_fsl_mc_msi_ops,
|
||||
.chip = &its_msi_irq_chip,
|
||||
};
|
||||
|
||||
static const struct of_device_id its_device_id[] = {
|
||||
{ .compatible = "arm,gic-v3-its", },
|
||||
{},
|
||||
};
|
||||
|
||||
static void __init its_fsl_mc_msi_init_one(struct fwnode_handle *handle,
|
||||
const char *name)
|
||||
{
|
||||
struct irq_domain *parent;
|
||||
struct irq_domain *mc_msi_domain;
|
||||
|
||||
parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
|
||||
if (!parent || !msi_get_domain_info(parent)) {
|
||||
pr_err("%s: unable to locate ITS domain\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
mc_msi_domain = fsl_mc_msi_create_irq_domain(handle,
|
||||
&its_fsl_mc_msi_domain_info,
|
||||
parent);
|
||||
if (!mc_msi_domain) {
|
||||
pr_err("%s: unable to create fsl-mc domain\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("fsl-mc MSI: %s domain created\n", name);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static int __init
|
||||
its_fsl_mc_msi_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_generic_translator *its_entry;
|
||||
struct fwnode_handle *dom_handle;
|
||||
const char *node_name;
|
||||
int err = 0;
|
||||
|
||||
its_entry = (struct acpi_madt_generic_translator *)header;
|
||||
node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
|
||||
(long)its_entry->base_address);
|
||||
|
||||
dom_handle = iort_find_domain_token(its_entry->translation_id);
|
||||
if (!dom_handle) {
|
||||
pr_err("%s: Unable to locate ITS domain handle\n", node_name);
|
||||
err = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
its_fsl_mc_msi_init_one(dom_handle, node_name);
|
||||
|
||||
out:
|
||||
kfree(node_name);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void __init its_fsl_mc_acpi_msi_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
|
||||
its_fsl_mc_msi_parse_madt, 0);
|
||||
}
|
||||
#else
|
||||
static inline void its_fsl_mc_acpi_msi_init(void) { }
|
||||
#endif
|
||||
|
||||
static void __init its_fsl_mc_of_msi_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
for (np = of_find_matching_node(NULL, its_device_id); np;
|
||||
np = of_find_matching_node(np, its_device_id)) {
|
||||
if (!of_device_is_available(np))
|
||||
continue;
|
||||
if (!of_property_read_bool(np, "msi-controller"))
|
||||
continue;
|
||||
|
||||
its_fsl_mc_msi_init_one(of_fwnode_handle(np),
|
||||
np->full_name);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init its_fsl_mc_msi_init(void)
|
||||
{
|
||||
its_fsl_mc_of_msi_init();
|
||||
its_fsl_mc_acpi_msi_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_initcall(its_fsl_mc_msi_init);
|
||||
@@ -161,7 +161,8 @@ static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev)
|
||||
int err;
|
||||
|
||||
if (!dev->pm_domain) {
|
||||
err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
|
||||
err = tegra_pmc_powergate_power_off(vde->pmc,
|
||||
TEGRA_POWERGATE_VDEC);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to power down HW: %d\n", err);
|
||||
return err;
|
||||
@@ -193,15 +194,16 @@ static __maybe_unused int tegra_vde_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
if (!dev->pm_domain) {
|
||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
|
||||
vde->clk, vde->rst);
|
||||
err = tegra_pmc_powergate_sequence_power_up(vde->pmc,
|
||||
TEGRA_POWERGATE_VDEC,
|
||||
vde->clk, vde->rst);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to power up HW : %d\n", err);
|
||||
goto release_reset;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* tegra_powergate_sequence_power_up() leaves clocks enabled,
|
||||
* tegra_pmc_powergate_sequence_power_up() leaves clocks enabled,
|
||||
* while GENPD not.
|
||||
*/
|
||||
err = clk_prepare_enable(vde->clk);
|
||||
@@ -293,6 +295,11 @@ static int tegra_vde_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
vde->pmc = devm_tegra_pmc_get(dev);
|
||||
if (IS_ERR(vde->pmc))
|
||||
return dev_err_probe(dev, PTR_ERR(vde->pmc),
|
||||
"failed to get PMC\n");
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "sync-token");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
@@ -107,6 +107,7 @@ struct tegra_vde {
|
||||
struct list_head map_list;
|
||||
struct reset_control *rst;
|
||||
struct reset_control *rst_mc;
|
||||
struct tegra_pmc *pmc;
|
||||
struct gen_pool *iram_pool;
|
||||
struct completion decode_completion;
|
||||
struct clk *clk;
|
||||
|
||||
@@ -432,13 +432,9 @@ static unsigned int gpmc_clk_ticks_to_ns(unsigned int ticks, int cs,
|
||||
*
|
||||
*/
|
||||
static int get_gpmc_timing_reg(
|
||||
/* timing specifiers */
|
||||
int cs, int reg, int st_bit, int end_bit, int max,
|
||||
const char *name, const enum gpmc_clk_domain cd,
|
||||
/* value transform */
|
||||
int shift,
|
||||
/* format specifiers */
|
||||
bool raw, bool noval)
|
||||
int shift, bool raw, bool noval)
|
||||
{
|
||||
u32 l;
|
||||
int nr_bits;
|
||||
|
||||
@@ -1034,7 +1034,7 @@ static int rpcif_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, PTR_ERR(rpc->spi_clk),
|
||||
"cannot get enabled spi clk\n");
|
||||
|
||||
vdev = platform_device_alloc(name, pdev->id);
|
||||
vdev = platform_device_alloc(name, PLATFORM_DEVID_AUTO);
|
||||
if (!vdev)
|
||||
return -ENOMEM;
|
||||
vdev->dev.parent = dev;
|
||||
|
||||
@@ -17,6 +17,7 @@ config TEGRA20_EMC
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_DEVFREQ
|
||||
select DDR
|
||||
select TEGRA_EMC_COMMON
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra20 chips. The EMC controls the external DRAM on the board.
|
||||
@@ -29,18 +30,33 @@ config TEGRA30_EMC
|
||||
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
|
||||
select PM_OPP
|
||||
select DDR
|
||||
select TEGRA_EMC_COMMON
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra30 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA114_EMC
|
||||
tristate "NVIDIA Tegra114 External Memory Controller driver"
|
||||
default ARCH_TEGRA_114_SOC
|
||||
depends on ARCH_TEGRA_114_SOC || COMPILE_TEST
|
||||
select TEGRA124_CLK_EMC if ARCH_TEGRA
|
||||
select PM_OPP
|
||||
select TEGRA_EMC_COMMON
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra114 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA124_EMC
|
||||
tristate "NVIDIA Tegra124 External Memory Controller driver"
|
||||
default ARCH_TEGRA_124_SOC
|
||||
depends on ARCH_TEGRA_124_SOC || COMPILE_TEST
|
||||
select TEGRA124_CLK_EMC if ARCH_TEGRA
|
||||
select PM_OPP
|
||||
select TEGRA_EMC_COMMON
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra124 chips. The EMC controls the external DRAM on the board.
|
||||
@@ -61,4 +77,7 @@ config TEGRA210_EMC
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA_EMC_COMMON
|
||||
tristate
|
||||
|
||||
endif
|
||||
|
||||
@@ -10,12 +10,15 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_238_SOC) += tegra186.o tegra234.o tegra238.o
|
||||
tegra-mc-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186.o tegra264.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_EMC_COMMON) += tegra-emc-common.o
|
||||
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
|
||||
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
|
||||
obj-$(CONFIG_TEGRA114_EMC) += tegra114-emc.o
|
||||
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
|
||||
obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o
|
||||
obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/tegra-icc.h>
|
||||
@@ -49,6 +50,9 @@ static const struct of_device_id tegra_mc_of_match[] = {
|
||||
#ifdef CONFIG_ARCH_TEGRA_234_SOC
|
||||
{ .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_238_SOC
|
||||
{ .compatible = "nvidia,tegra238-mc", .data = &tegra238_mc_soc },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_TEGRA_264_SOC
|
||||
{ .compatible = "nvidia,tegra264-mc", .data = &tegra264_mc_soc },
|
||||
#endif
|
||||
@@ -910,6 +914,19 @@ static void tegra_mc_num_channel_enabled(struct tegra_mc *mc)
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_mc_setup_intmask(struct tegra_mc *mc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < mc->soc->num_intmasks; i++) {
|
||||
if (mc->soc->num_channels)
|
||||
mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmasks[i].mask,
|
||||
mc->soc->intmasks[i].reg);
|
||||
else
|
||||
mc_writel(mc, mc->soc->intmasks[i].mask, mc->soc->intmasks[i].reg);
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_mc *mc;
|
||||
@@ -970,13 +987,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < mc->soc->num_intmasks; i++) {
|
||||
if (mc->soc->num_channels)
|
||||
mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmasks[i].mask,
|
||||
mc->soc->intmasks[i].reg);
|
||||
else
|
||||
mc_writel(mc, mc->soc->intmasks[i].mask, mc->soc->intmasks[i].reg);
|
||||
}
|
||||
tegra_mc_setup_intmask(mc);
|
||||
}
|
||||
|
||||
if (mc->soc->reset_ops) {
|
||||
@@ -1010,10 +1021,25 @@ static void tegra_mc_sync_state(struct device *dev)
|
||||
icc_sync_state(dev);
|
||||
}
|
||||
|
||||
static int tegra_mc_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
if (mc->soc->ops && mc->soc->ops->resume)
|
||||
mc->soc->ops->resume(mc);
|
||||
|
||||
tegra_mc_setup_intmask(mc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(tegra_mc_pm_ops, NULL, tegra_mc_resume);
|
||||
|
||||
static struct platform_driver tegra_mc_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-mc",
|
||||
.of_match_table = tegra_mc_of_match,
|
||||
.pm = pm_sleep_ptr(&tegra_mc_pm_ops),
|
||||
.suppress_bind_attrs = true,
|
||||
.sync_state = tegra_mc_sync_state,
|
||||
},
|
||||
|
||||
@@ -240,6 +240,14 @@ extern const struct tegra_mc_soc tegra194_mc_soc;
|
||||
extern const struct tegra_mc_soc tegra234_mc_soc;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_234_SOC) || defined(CONFIG_ARCH_TEGRA_238_SOC)
|
||||
extern const struct tegra_mc_icc_ops tegra234_mc_icc_ops;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_238_SOC
|
||||
extern const struct tegra_mc_soc tegra238_mc_soc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_264_SOC
|
||||
extern const struct tegra_mc_soc tegra264_mc_soc;
|
||||
#endif
|
||||
@@ -256,6 +264,7 @@ extern const struct tegra_mc_ops tegra30_mc_ops;
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_234_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_238_SOC) || \
|
||||
defined(CONFIG_ARCH_TEGRA_264_SOC)
|
||||
extern const struct tegra_mc_ops tegra186_mc_ops;
|
||||
#endif
|
||||
|
||||
143
drivers/memory/tegra/tegra-emc-common.c
Normal file
143
drivers/memory/tegra/tegra-emc-common.c
Normal file
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_opp.h>
|
||||
|
||||
#include "tegra-emc-common.h"
|
||||
|
||||
/**
|
||||
* tegra_emc_rate_requests_init() - Initialize EMC rate request tracking
|
||||
* @reqs: struct tegra_emc_rate_requests to initialize.
|
||||
* @dev: EMC device.
|
||||
*
|
||||
* Initializes the rate request tracking state with default state
|
||||
* (no active requests). Must be called before using @reqs with
|
||||
* other functions.
|
||||
*/
|
||||
void tegra_emc_rate_requests_init(struct tegra_emc_rate_requests *reqs,
|
||||
struct device *dev)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
mutex_init(&reqs->rate_lock);
|
||||
reqs->dev = dev;
|
||||
|
||||
for (i = 0; i < TEGRA_EMC_RATE_TYPE_MAX; i++) {
|
||||
reqs->requested_rate[i].min_rate = 0;
|
||||
reqs->requested_rate[i].max_rate = ULONG_MAX;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_emc_rate_requests_init);
|
||||
|
||||
/* Caller must hold reqs->rate_lock. */
|
||||
static int tegra_emc_request_rate(struct tegra_emc_rate_requests *reqs,
|
||||
unsigned long new_min_rate,
|
||||
unsigned long new_max_rate,
|
||||
enum tegra_emc_rate_request_type type)
|
||||
{
|
||||
struct tegra_emc_rate_request *req = reqs->requested_rate;
|
||||
unsigned long min_rate = 0, max_rate = ULONG_MAX;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
lockdep_assert_held(&reqs->rate_lock);
|
||||
|
||||
/* select minimum and maximum rates among the requested rates */
|
||||
for (i = 0; i < TEGRA_EMC_RATE_TYPE_MAX; i++, req++) {
|
||||
if (i == type) {
|
||||
min_rate = max(new_min_rate, min_rate);
|
||||
max_rate = min(new_max_rate, max_rate);
|
||||
} else {
|
||||
min_rate = max(req->min_rate, min_rate);
|
||||
max_rate = min(req->max_rate, max_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if (min_rate > max_rate) {
|
||||
dev_err_ratelimited(reqs->dev, "%s: type %u: out of range: %lu %lu\n",
|
||||
__func__, type, min_rate, max_rate);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* EMC rate-changes should go via OPP API because it manages voltage
|
||||
* changes.
|
||||
*/
|
||||
err = dev_pm_opp_set_rate(reqs->dev, min_rate);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
reqs->requested_rate[type].min_rate = new_min_rate;
|
||||
reqs->requested_rate[type].max_rate = new_max_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_emc_set_min_rate() - Update minimum rate request for a request type
|
||||
* @reqs: rate request tracking state
|
||||
* @rate: new minimum rate in Hz requested by @type
|
||||
* @type: type of request
|
||||
*
|
||||
* Records @rate as the new minimum rate request for @type, recalculates target
|
||||
* rate based on all requests and applies new rate through the OPP API.
|
||||
*
|
||||
* Context: Sleeps. Requests to same @reqs are synchronized via mutex.
|
||||
*
|
||||
* Return:
|
||||
* * %0 - success
|
||||
* * %-ERANGE - request would cause minimum rate request to be higher than
|
||||
* maximum rate request
|
||||
* * other - setting new rate failed
|
||||
*/
|
||||
int tegra_emc_set_min_rate(struct tegra_emc_rate_requests *reqs,
|
||||
unsigned long rate,
|
||||
enum tegra_emc_rate_request_type type)
|
||||
{
|
||||
struct tegra_emc_rate_request *req = &reqs->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&reqs->rate_lock);
|
||||
ret = tegra_emc_request_rate(reqs, rate, req->max_rate, type);
|
||||
mutex_unlock(&reqs->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_emc_set_min_rate);
|
||||
|
||||
/**
|
||||
* tegra_emc_set_max_rate() - Update maximum rate request for a request type
|
||||
* @reqs: rate request tracking state
|
||||
* @rate: new maximum rate in Hz requested by @type
|
||||
* @type: type of request
|
||||
*
|
||||
* Records @rate as the new maximum rate request for @type, recalculates target
|
||||
* rate based on all requests and applies new rate through the OPP API.
|
||||
*
|
||||
* Context: Sleeps. Requests to same @reqs are synchronized via mutex.
|
||||
*
|
||||
* Return:
|
||||
* * %0 - success
|
||||
* * %-ERANGE - request would cause minimum rate request to be higher than
|
||||
* maximum rate request
|
||||
* * other - setting new rate failed
|
||||
*/
|
||||
int tegra_emc_set_max_rate(struct tegra_emc_rate_requests *reqs,
|
||||
unsigned long rate,
|
||||
enum tegra_emc_rate_request_type type)
|
||||
{
|
||||
struct tegra_emc_rate_request *req = &reqs->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&reqs->rate_lock);
|
||||
ret = tegra_emc_request_rate(reqs, req->min_rate, rate, type);
|
||||
mutex_unlock(&reqs->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_emc_set_max_rate);
|
||||
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra EMC common code");
|
||||
MODULE_LICENSE("GPL");
|
||||
46
drivers/memory/tegra/tegra-emc-common.h
Normal file
46
drivers/memory/tegra/tegra-emc-common.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef TEGRA_EMC_COMMON_H
|
||||
#define TEGRA_EMC_COMMON_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/**
|
||||
* enum tegra_emc_rate_request_type - source of rate request
|
||||
* @TEGRA_EMC_RATE_DEVFREQ: rate requested by devfreq governor
|
||||
* @TEGRA_EMC_RATE_DEBUG: rate requested through debugfs knobs
|
||||
* @TEGRA_EMC_RATE_ICC: rate requested by ICC framework
|
||||
* @TEGRA_EMC_RATE_TYPE_MAX: number of valid request types
|
||||
*/
|
||||
enum tegra_emc_rate_request_type {
|
||||
TEGRA_EMC_RATE_DEVFREQ,
|
||||
TEGRA_EMC_RATE_DEBUG,
|
||||
TEGRA_EMC_RATE_ICC,
|
||||
TEGRA_EMC_RATE_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct tegra_emc_rate_request {
|
||||
unsigned long min_rate;
|
||||
unsigned long max_rate;
|
||||
};
|
||||
|
||||
struct tegra_emc_rate_requests {
|
||||
struct tegra_emc_rate_request requested_rate[TEGRA_EMC_RATE_TYPE_MAX];
|
||||
/* Protects @requested_rate. */
|
||||
struct mutex rate_lock;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
void tegra_emc_rate_requests_init(struct tegra_emc_rate_requests *reqs,
|
||||
struct device *dev);
|
||||
|
||||
int tegra_emc_set_min_rate(struct tegra_emc_rate_requests *reqs,
|
||||
unsigned long rate,
|
||||
enum tegra_emc_rate_request_type type);
|
||||
|
||||
int tegra_emc_set_max_rate(struct tegra_emc_rate_requests *reqs,
|
||||
unsigned long rate,
|
||||
enum tegra_emc_rate_request_type type);
|
||||
|
||||
#endif /* TEGRA_EMC_COMMON_H */
|
||||
1351
drivers/memory/tegra/tegra114-emc.c
Normal file
1351
drivers/memory/tegra/tegra114-emc.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
@@ -1101,6 +1102,195 @@ static const struct tegra_mc_reset tegra114_mc_resets[] = {
|
||||
TEGRA114_MC_RESET(VI, 0x200, 0x204, 17),
|
||||
};
|
||||
|
||||
static void tegra114_mc_tune_client_latency(struct tegra_mc *mc,
|
||||
const struct tegra_mc_client *client,
|
||||
unsigned int bandwidth_mbytes_sec)
|
||||
{
|
||||
u32 arb_tolerance_compensation_nsec, arb_tolerance_compensation_div;
|
||||
unsigned int fifo_size = client->fifo_size;
|
||||
u32 arb_nsec, la_ticks, value;
|
||||
|
||||
/* see 20.3.1.1 Client Configuration in Tegra4 TRM v01p */
|
||||
if (bandwidth_mbytes_sec)
|
||||
arb_nsec = fifo_size * NSEC_PER_USEC / bandwidth_mbytes_sec;
|
||||
else
|
||||
arb_nsec = U32_MAX;
|
||||
|
||||
/*
|
||||
* Latency allowness should be set with consideration for the module's
|
||||
* latency tolerance and internal buffering capabilities.
|
||||
*
|
||||
* Display memory clients use isochronous transfers and have very low
|
||||
* tolerance to a belated transfers. Hence we need to compensate the
|
||||
* memory arbitration imperfection for them in order to prevent FIFO
|
||||
* underflow condition when memory bus is busy.
|
||||
*
|
||||
* VI clients also need a stronger compensation.
|
||||
*/
|
||||
switch (client->swgroup) {
|
||||
case TEGRA_SWGROUP_MPCORE:
|
||||
case TEGRA_SWGROUP_PTC:
|
||||
/*
|
||||
* We always want lower latency for these clients, hence
|
||||
* don't touch them.
|
||||
*/
|
||||
return;
|
||||
|
||||
case TEGRA_SWGROUP_DC:
|
||||
case TEGRA_SWGROUP_DCB:
|
||||
arb_tolerance_compensation_nsec = 1050;
|
||||
arb_tolerance_compensation_div = 2;
|
||||
break;
|
||||
|
||||
case TEGRA_SWGROUP_VI:
|
||||
arb_tolerance_compensation_nsec = 1050;
|
||||
arb_tolerance_compensation_div = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
arb_tolerance_compensation_nsec = 150;
|
||||
arb_tolerance_compensation_div = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (arb_nsec > arb_tolerance_compensation_nsec)
|
||||
arb_nsec -= arb_tolerance_compensation_nsec;
|
||||
else
|
||||
arb_nsec = 0;
|
||||
|
||||
arb_nsec /= arb_tolerance_compensation_div;
|
||||
|
||||
/*
|
||||
* Latency allowance is a number of ticks a request from a particular
|
||||
* client may wait in the EMEM arbiter before it becomes a high-priority
|
||||
* request.
|
||||
*/
|
||||
la_ticks = arb_nsec / mc->tick;
|
||||
la_ticks = min(la_ticks, client->regs.la.mask);
|
||||
|
||||
value = mc_readl(mc, client->regs.la.reg);
|
||||
value &= ~(client->regs.la.mask << client->regs.la.shift);
|
||||
value |= la_ticks << client->regs.la.shift;
|
||||
mc_writel(mc, value, client->regs.la.reg);
|
||||
}
|
||||
|
||||
static int tegra114_mc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
{
|
||||
struct tegra_mc *mc = icc_provider_to_tegra_mc(src->provider);
|
||||
const struct tegra_mc_client *client = &mc->soc->clients[src->id];
|
||||
u64 peak_bandwidth = icc_units_to_bps(src->peak_bw);
|
||||
|
||||
/*
|
||||
* Skip pre-initialization that is done by icc_node_add(), which sets
|
||||
* bandwidth to maximum for all clients before drivers are loaded.
|
||||
*
|
||||
* This doesn't make sense for us because we don't have drivers for all
|
||||
* clients and it's okay to keep configuration left from bootloader
|
||||
* during boot, at least for today.
|
||||
*/
|
||||
if (src == dst)
|
||||
return 0;
|
||||
|
||||
/* convert bytes/sec to megabytes/sec */
|
||||
do_div(peak_bandwidth, 1000000);
|
||||
|
||||
tegra114_mc_tune_client_latency(mc, client, peak_bandwidth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra114_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
|
||||
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
|
||||
{
|
||||
/*
|
||||
* ISO clients need to reserve extra bandwidth up-front because
|
||||
* there could be high bandwidth pressure during initial filling
|
||||
* of the client's FIFO buffers. Secondly, we need to take into
|
||||
* account impurities of the memory subsystem.
|
||||
*/
|
||||
if (tag & TEGRA_MC_ICC_TAG_ISO)
|
||||
peak_bw = tegra_mc_scale_percents(peak_bw, 400);
|
||||
|
||||
*agg_avg += avg_bw;
|
||||
*agg_peak = max(*agg_peak, peak_bw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct icc_node_data *
|
||||
tegra114_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)
|
||||
{
|
||||
struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
|
||||
const struct tegra_mc_client *client;
|
||||
unsigned int i, idx = spec->args[0];
|
||||
struct icc_node_data *ndata;
|
||||
struct icc_node *node;
|
||||
|
||||
list_for_each_entry(node, &mc->provider.nodes, node_list) {
|
||||
if (node->id != idx)
|
||||
continue;
|
||||
|
||||
ndata = kzalloc_obj(*ndata);
|
||||
if (!ndata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
client = &mc->soc->clients[idx];
|
||||
ndata->node = node;
|
||||
|
||||
switch (client->swgroup) {
|
||||
case TEGRA_SWGROUP_DC:
|
||||
case TEGRA_SWGROUP_DCB:
|
||||
case TEGRA_SWGROUP_PTC:
|
||||
case TEGRA_SWGROUP_VI:
|
||||
/* these clients are isochronous by default */
|
||||
ndata->tag = TEGRA_MC_ICC_TAG_ISO;
|
||||
break;
|
||||
|
||||
default:
|
||||
ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return ndata;
|
||||
}
|
||||
|
||||
for (i = 0; i < mc->soc->num_clients; i++) {
|
||||
if (mc->soc->clients[i].id == idx)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static const struct tegra_mc_icc_ops tegra114_mc_icc_ops = {
|
||||
.xlate_extended = tegra114_mc_of_icc_xlate_extended,
|
||||
.aggregate = tegra114_mc_icc_aggreate,
|
||||
.set = tegra114_mc_icc_set,
|
||||
};
|
||||
|
||||
static const unsigned long tegra114_mc_emem_regs[] = {
|
||||
MC_EMEM_ARB_CFG,
|
||||
MC_EMEM_ARB_OUTSTANDING_REQ,
|
||||
MC_EMEM_ARB_TIMING_RCD,
|
||||
MC_EMEM_ARB_TIMING_RP,
|
||||
MC_EMEM_ARB_TIMING_RC,
|
||||
MC_EMEM_ARB_TIMING_RAS,
|
||||
MC_EMEM_ARB_TIMING_FAW,
|
||||
MC_EMEM_ARB_TIMING_RRD,
|
||||
MC_EMEM_ARB_TIMING_RAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_WAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_R2R,
|
||||
MC_EMEM_ARB_TIMING_W2W,
|
||||
MC_EMEM_ARB_TIMING_R2W,
|
||||
MC_EMEM_ARB_TIMING_W2R,
|
||||
MC_EMEM_ARB_DA_TURNS,
|
||||
MC_EMEM_ARB_DA_COVERS,
|
||||
MC_EMEM_ARB_MISC0,
|
||||
MC_EMEM_ARB_RING1_THROTTLE,
|
||||
};
|
||||
|
||||
static const struct tegra_mc_intmask tegra114_mc_intmasks[] = {
|
||||
{
|
||||
.reg = MC_INTMASK,
|
||||
@@ -1116,11 +1306,14 @@ const struct tegra_mc_soc tegra114_mc_soc = {
|
||||
.atom_size = 32,
|
||||
.client_id_mask = 0x7f,
|
||||
.smmu = &tegra114_smmu_soc,
|
||||
.emem_regs = tegra114_mc_emem_regs,
|
||||
.num_emem_regs = ARRAY_SIZE(tegra114_mc_emem_regs),
|
||||
.intmasks = tegra114_mc_intmasks,
|
||||
.num_intmasks = ARRAY_SIZE(tegra114_mc_intmasks),
|
||||
.reset_ops = &tegra_mc_reset_ops_common,
|
||||
.resets = tegra114_mc_resets,
|
||||
.num_resets = ARRAY_SIZE(tegra114_mc_resets),
|
||||
.icc_ops = &tegra114_mc_icc_ops,
|
||||
.ops = &tegra30_mc_ops,
|
||||
.regs = &tegra20_mc_regs,
|
||||
.handle_irq = tegra30_mc_irq_handlers,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <soc/tegra/mc.h>
|
||||
|
||||
#include "mc.h"
|
||||
#include "tegra-emc-common.h"
|
||||
|
||||
#define EMC_FBIO_CFG5 0x104
|
||||
#define EMC_FBIO_CFG5_DRAM_TYPE_MASK 0x3
|
||||
@@ -467,17 +468,6 @@ struct emc_timing {
|
||||
u32 emc_zcal_interval;
|
||||
};
|
||||
|
||||
enum emc_rate_request_type {
|
||||
EMC_RATE_DEBUG,
|
||||
EMC_RATE_ICC,
|
||||
EMC_RATE_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct emc_rate_request {
|
||||
unsigned long min_rate;
|
||||
unsigned long max_rate;
|
||||
};
|
||||
|
||||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
|
||||
@@ -503,14 +493,7 @@ struct tegra_emc {
|
||||
|
||||
struct icc_provider provider;
|
||||
|
||||
/*
|
||||
* There are multiple sources in the EMC driver which could request
|
||||
* a min/max clock rate, these rates are contained in this array.
|
||||
*/
|
||||
struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
|
||||
|
||||
/* protect shared rate-change code path */
|
||||
struct mutex rate_lock;
|
||||
struct tegra_emc_rate_requests reqs;
|
||||
};
|
||||
|
||||
/* Timing change sequence functions */
|
||||
@@ -1041,83 +1024,6 @@ tegra124_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void tegra124_emc_rate_requests_init(struct tegra_emc *emc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
|
||||
emc->requested_rate[i].min_rate = 0;
|
||||
emc->requested_rate[i].max_rate = ULONG_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc_request_rate(struct tegra_emc *emc,
|
||||
unsigned long new_min_rate,
|
||||
unsigned long new_max_rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = emc->requested_rate;
|
||||
unsigned long min_rate = 0, max_rate = ULONG_MAX;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* select minimum and maximum rates among the requested rates */
|
||||
for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
|
||||
if (i == type) {
|
||||
min_rate = max(new_min_rate, min_rate);
|
||||
max_rate = min(new_max_rate, max_rate);
|
||||
} else {
|
||||
min_rate = max(req->min_rate, min_rate);
|
||||
max_rate = min(req->max_rate, max_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if (min_rate > max_rate) {
|
||||
dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
|
||||
__func__, type, min_rate, max_rate);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* EMC rate-changes should go via OPP API because it manages voltage
|
||||
* changes.
|
||||
*/
|
||||
err = dev_pm_opp_set_rate(emc->dev, min_rate);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
emc->requested_rate[type].min_rate = new_min_rate;
|
||||
emc->requested_rate[type].max_rate = new_max_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = &emc->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emc->rate_lock);
|
||||
ret = emc_request_rate(emc, rate, req->max_rate, type);
|
||||
mutex_unlock(&emc->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = &emc->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emc->rate_lock);
|
||||
ret = emc_request_rate(emc, req->min_rate, rate, type);
|
||||
mutex_unlock(&emc->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs interface
|
||||
*
|
||||
@@ -1190,7 +1096,7 @@ static int tegra124_emc_debug_min_rate_set(void *data, u64 rate)
|
||||
if (!tegra124_emc_validate_rate(emc, rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
|
||||
err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -1220,7 +1126,7 @@ static int tegra124_emc_debug_max_rate_set(void *data, u64 rate)
|
||||
if (!tegra124_emc_validate_rate(emc, rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
|
||||
err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -1327,7 +1233,7 @@ static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
do_div(rate, ddr * dram_data_bus_width_bytes);
|
||||
rate = min_t(u64, rate, U32_MAX);
|
||||
|
||||
err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
|
||||
err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1441,7 +1347,6 @@ static int tegra124_emc_probe(struct platform_device *pdev)
|
||||
if (!emc)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&emc->rate_lock);
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
emc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
@@ -1487,7 +1392,7 @@ static int tegra124_emc_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
tegra124_emc_rate_requests_init(emc);
|
||||
tegra_emc_rate_requests_init(&emc->reqs, &pdev->dev);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
emc_debugfs_init(&pdev->dev, emc);
|
||||
|
||||
@@ -258,15 +258,13 @@ static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pe
|
||||
|
||||
static int tegra186_emc_interconnect_init(struct tegra186_emc *emc)
|
||||
{
|
||||
struct tegra_mc *mc = dev_get_drvdata(emc->dev->parent);
|
||||
const struct tegra_mc_soc *soc = mc->soc;
|
||||
struct icc_node *node;
|
||||
int err;
|
||||
|
||||
emc->provider.dev = emc->dev;
|
||||
emc->provider.set = tegra186_emc_icc_set_bw;
|
||||
emc->provider.data = &emc->provider;
|
||||
emc->provider.aggregate = soc->icc_ops->aggregate;
|
||||
emc->provider.aggregate = icc_std_aggregate;
|
||||
emc->provider.xlate = tegra186_emc_of_icc_xlate;
|
||||
emc->provider.get_bw = tegra186_emc_icc_get_init_bw;
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_mc_resume(struct tegra_mc *mc)
|
||||
static void tegra186_mc_resume(struct tegra_mc *mc)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_IOMMU_API)
|
||||
unsigned int i;
|
||||
@@ -165,8 +165,6 @@ static int tegra186_mc_resume(struct tegra_mc *mc)
|
||||
tegra186_mc_client_sid_override(mc, client, client->sid);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct tegra_mc_ops tegra186_mc_ops = {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "../of_memory.h"
|
||||
|
||||
#include "mc.h"
|
||||
#include "tegra-emc-common.h"
|
||||
|
||||
#define EMC_INTSTATUS 0x000
|
||||
#define EMC_INTMASK 0x004
|
||||
@@ -182,18 +183,6 @@ struct emc_timing {
|
||||
u32 data[ARRAY_SIZE(emc_timing_registers)];
|
||||
};
|
||||
|
||||
enum emc_rate_request_type {
|
||||
EMC_RATE_DEVFREQ,
|
||||
EMC_RATE_DEBUG,
|
||||
EMC_RATE_ICC,
|
||||
EMC_RATE_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct emc_rate_request {
|
||||
unsigned long min_rate;
|
||||
unsigned long max_rate;
|
||||
};
|
||||
|
||||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct tegra_mc *mc;
|
||||
@@ -212,14 +201,7 @@ struct tegra_emc {
|
||||
unsigned long max_rate;
|
||||
} debugfs;
|
||||
|
||||
/*
|
||||
* There are multiple sources in the EMC driver which could request
|
||||
* a min/max clock rate, these rates are contained in this array.
|
||||
*/
|
||||
struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
|
||||
|
||||
/* protect shared rate-change code path */
|
||||
struct mutex rate_lock;
|
||||
struct tegra_emc_rate_requests reqs;
|
||||
|
||||
struct devfreq_simple_ondemand_data ondemand_data;
|
||||
|
||||
@@ -710,83 +692,6 @@ static long emc_round_rate(unsigned long rate,
|
||||
return timing->rate;
|
||||
}
|
||||
|
||||
static void tegra20_emc_rate_requests_init(struct tegra_emc *emc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
|
||||
emc->requested_rate[i].min_rate = 0;
|
||||
emc->requested_rate[i].max_rate = ULONG_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc_request_rate(struct tegra_emc *emc,
|
||||
unsigned long new_min_rate,
|
||||
unsigned long new_max_rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = emc->requested_rate;
|
||||
unsigned long min_rate = 0, max_rate = ULONG_MAX;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* select minimum and maximum rates among the requested rates */
|
||||
for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
|
||||
if (i == type) {
|
||||
min_rate = max(new_min_rate, min_rate);
|
||||
max_rate = min(new_max_rate, max_rate);
|
||||
} else {
|
||||
min_rate = max(req->min_rate, min_rate);
|
||||
max_rate = min(req->max_rate, max_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if (min_rate > max_rate) {
|
||||
dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
|
||||
__func__, type, min_rate, max_rate);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* EMC rate-changes should go via OPP API because it manages voltage
|
||||
* changes.
|
||||
*/
|
||||
err = dev_pm_opp_set_rate(emc->dev, min_rate);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
emc->requested_rate[type].min_rate = new_min_rate;
|
||||
emc->requested_rate[type].max_rate = new_max_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = &emc->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emc->rate_lock);
|
||||
ret = emc_request_rate(emc, rate, req->max_rate, type);
|
||||
mutex_unlock(&emc->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = &emc->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emc->rate_lock);
|
||||
ret = emc_request_rate(emc, req->min_rate, rate, type);
|
||||
mutex_unlock(&emc->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs interface
|
||||
*
|
||||
@@ -857,7 +762,7 @@ static int tegra20_emc_debug_min_rate_set(void *data, u64 rate)
|
||||
if (!tegra20_emc_validate_rate(emc, rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
|
||||
err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -887,7 +792,7 @@ static int tegra20_emc_debug_max_rate_set(void *data, u64 rate)
|
||||
if (!tegra20_emc_validate_rate(emc, rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
|
||||
err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -993,7 +898,7 @@ static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
do_div(rate, dram_data_bus_width_bytes);
|
||||
rate = min_t(u64, rate, U32_MAX);
|
||||
|
||||
err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
|
||||
err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1111,7 +1016,7 @@ static int tegra20_emc_devfreq_target(struct device *dev, unsigned long *freq,
|
||||
rate = dev_pm_opp_get_freq(opp);
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
return emc_set_min_rate(emc, rate, EMC_RATE_DEVFREQ);
|
||||
return tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEVFREQ);
|
||||
}
|
||||
|
||||
static int tegra20_emc_devfreq_get_dev_status(struct device *dev,
|
||||
@@ -1190,7 +1095,6 @@ static int tegra20_emc_probe(struct platform_device *pdev)
|
||||
if (!emc)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&emc->rate_lock);
|
||||
emc->clk_nb.notifier_call = tegra20_emc_clk_change_notify;
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
@@ -1228,7 +1132,7 @@ static int tegra20_emc_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
|
||||
platform_set_drvdata(pdev, emc);
|
||||
tegra20_emc_rate_requests_init(emc);
|
||||
tegra_emc_rate_requests_init(&emc->reqs, &pdev->dev);
|
||||
tegra20_emc_debugfs_init(emc);
|
||||
tegra20_emc_interconnect_init(emc);
|
||||
tegra20_emc_devfreq_init(emc);
|
||||
|
||||
@@ -1106,10 +1106,8 @@ static int tegra234_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
|
||||
|
||||
if (node->id == TEGRA_ICC_MC_CPU_CLUSTER0 ||
|
||||
node->id == TEGRA_ICC_MC_CPU_CLUSTER1 ||
|
||||
node->id == TEGRA_ICC_MC_CPU_CLUSTER2) {
|
||||
if (mc)
|
||||
peak_bw = peak_bw * mc->num_channels;
|
||||
}
|
||||
node->id == TEGRA_ICC_MC_CPU_CLUSTER2)
|
||||
peak_bw = peak_bw * mc->num_channels;
|
||||
|
||||
*agg_avg += avg_bw;
|
||||
*agg_peak = max(*agg_peak, peak_bw);
|
||||
@@ -1125,7 +1123,7 @@ static int tegra234_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pea
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tegra_mc_icc_ops tegra234_mc_icc_ops = {
|
||||
const struct tegra_mc_icc_ops tegra234_mc_icc_ops = {
|
||||
.xlate = tegra_mc_icc_xlate,
|
||||
.aggregate = tegra234_mc_icc_aggregate,
|
||||
.get_bw = tegra234_mc_icc_get_init_bw,
|
||||
|
||||
391
drivers/memory/tegra/tegra238.c
Normal file
391
drivers/memory/tegra/tegra238.c
Normal file
@@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <soc/tegra/mc.h>
|
||||
|
||||
#include <dt-bindings/memory/tegra234-mc.h>
|
||||
#include <dt-bindings/memory/nvidia,tegra238-mc.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/tegra-icc.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include "mc.h"
|
||||
|
||||
static const struct tegra_mc_client tegra238_mc_clients[] = {
|
||||
{
|
||||
.id = TEGRA234_MEMORY_CLIENT_HDAR,
|
||||
.name = "hdar",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_HDA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.sid = TEGRA238_SID_HDA,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0xa8,
|
||||
.security = 0xac,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_HDAW,
|
||||
.name = "hdaw",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_HDA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.sid = TEGRA238_SID_HDA,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x1a8,
|
||||
.security = 0x1ac,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_SDMMCRAB,
|
||||
.name = "sdmmcrab",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_SDMMC_4,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_SDMMC4A,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x318,
|
||||
.security = 0x31c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_SDMMCWAB,
|
||||
.name = "sdmmcwab",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_SDMMC_4,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_SDMMC4A,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x338,
|
||||
.security = 0x33c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_APER,
|
||||
.name = "aper",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_APE,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.sid = TEGRA238_SID_ISO_APE0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x3d0,
|
||||
.security = 0x3d4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_APEW,
|
||||
.name = "apew",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_APE,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.sid = TEGRA238_SID_ISO_APE0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x3d8,
|
||||
.security = 0x3dc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVDISPLAYR,
|
||||
.name = "nvdisplayr",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DISPLAY,
|
||||
.type = TEGRA_ICC_ISO_DISPLAY,
|
||||
.sid = TEGRA238_SID_ISO_NVDISPLAY,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x490,
|
||||
.security = 0x494,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVDISPLAYR1,
|
||||
.name = "nvdisplayr1",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_DISPLAY,
|
||||
.type = TEGRA_ICC_ISO_DISPLAY,
|
||||
.sid = TEGRA238_SID_ISO_NVDISPLAY,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x508,
|
||||
.security = 0x50c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPR,
|
||||
.name = "bpmpr",
|
||||
.sid = TEGRA238_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x498,
|
||||
.security = 0x49c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPW,
|
||||
.name = "bpmpw",
|
||||
.sid = TEGRA238_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4a0,
|
||||
.security = 0x4a4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPDMAR,
|
||||
.name = "bpmpdmar",
|
||||
.sid = TEGRA238_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4a8,
|
||||
.security = 0x4ac,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_BPMPDMAW,
|
||||
.name = "bpmpdmaw",
|
||||
.sid = TEGRA238_SID_BPMP,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4b0,
|
||||
.security = 0x4b4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_APEDMAR,
|
||||
.name = "apedmar",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_APEDMA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.sid = TEGRA238_SID_ISO_APE1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x4f8,
|
||||
.security = 0x4fc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_APEDMAW,
|
||||
.name = "apedmaw",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_APEDMA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.sid = TEGRA238_SID_ISO_APE1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x500,
|
||||
.security = 0x504,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_VICSRD,
|
||||
.name = "vicsrd",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_VIC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_VIC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x360,
|
||||
.security = 0x364,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_VICSWR,
|
||||
.name = "vicswr",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_VIC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_VIC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x368,
|
||||
.security = 0x36c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVDECSRD,
|
||||
.name = "nvdecsrd",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_NVDEC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_NVDEC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x3c0,
|
||||
.security = 0x3c4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVDECSWR,
|
||||
.name = "nvdecswr",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_NVDEC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_NVDEC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x3c8,
|
||||
.security = 0x3cc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVENCSRD,
|
||||
.name = "nvencsrd",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_NVENC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_NVENC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0xe0,
|
||||
.security = 0xe4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVENCSWR,
|
||||
.name = "nvencswr",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_NVENC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_NVENC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x158,
|
||||
.security = 0x15c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE0R,
|
||||
.name = "pcie0r",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6c0,
|
||||
.security = 0x6c4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE0W,
|
||||
.name = "pcie0w",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE0,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6c8,
|
||||
.security = 0x6cc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE1R,
|
||||
.name = "pcie1r",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6d0,
|
||||
.security = 0x6d4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE1W,
|
||||
.name = "pcie1w",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE1,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6d8,
|
||||
.security = 0x6dc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE2AR,
|
||||
.name = "pcie2ar",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_2,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE2,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6e0,
|
||||
.security = 0x6e4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE2AW,
|
||||
.name = "pcie2aw",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_2,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE2,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6e8,
|
||||
.security = 0x6ec,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE3R,
|
||||
.name = "pcie3r",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_3,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE3,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6f0,
|
||||
.security = 0x6f4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE3W,
|
||||
.name = "pcie3w",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_PCIE_3,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA238_SID_PCIE3,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x6f8,
|
||||
.security = 0x6fc,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA_ICC_MC_CPU_CLUSTER0,
|
||||
.name = "sw_cluster0",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_CPU_CLUSTER0,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVL1R,
|
||||
.name = "nvl1r",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_GPU,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVL1W,
|
||||
.name = "nvl1w",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_GPU,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct tegra_mc_intmask tegra238_mc_intmasks[] = {
|
||||
{
|
||||
.reg = MC_INTMASK,
|
||||
.mask = MC_INT_DECERR_ROUTE_SANITY | MC_INT_DECERR_GENERALIZED_CARVEOUT |
|
||||
MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
|
||||
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
|
||||
},
|
||||
};
|
||||
|
||||
const struct tegra_mc_soc tegra238_mc_soc = {
|
||||
.num_clients = ARRAY_SIZE(tegra238_mc_clients),
|
||||
.clients = tegra238_mc_clients,
|
||||
.num_address_bits = 40,
|
||||
.num_channels = 8,
|
||||
.client_id_mask = 0x1ff,
|
||||
.intmasks = tegra238_mc_intmasks,
|
||||
.num_intmasks = ARRAY_SIZE(tegra238_mc_intmasks),
|
||||
.has_addr_hi_reg = true,
|
||||
.ops = &tegra186_mc_ops,
|
||||
.icc_ops = &tegra234_mc_icc_ops,
|
||||
.ch_intmask = 0x0000ff00,
|
||||
.global_intstatus_channel_shift = 8,
|
||||
.num_carveouts = 32,
|
||||
.regs = &tegra20_mc_regs,
|
||||
.handle_irq = tegra30_mc_irq_handlers,
|
||||
.num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
|
||||
.mc_addr_hi_mask = 0x3,
|
||||
.mc_err_status_type_mask = (0x7 << 28),
|
||||
};
|
||||
@@ -21,45 +21,62 @@
|
||||
*/
|
||||
static const struct tegra_mc_client tegra264_mc_clients[] = {
|
||||
{
|
||||
.id = TEGRA264_MEMORY_CLIENT_HDAR,
|
||||
.name = "hdar",
|
||||
.bpmp_id = TEGRA264_BWMGR_HDA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_PTCR,
|
||||
.name = "ptcr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HDAW,
|
||||
.name = "hdaw",
|
||||
.bpmp_id = TEGRA264_BWMGR_HDA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_HOST1XR,
|
||||
.name = "host1xr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE0R,
|
||||
.name = "mgbe0r",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_MPCORER,
|
||||
.name = "mpcorer",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE0W,
|
||||
.name = "mgbe0w",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_PSCR,
|
||||
.name = "pscr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE1R,
|
||||
.name = "mgbe1r",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_PSCW,
|
||||
.name = "pscw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE1W,
|
||||
.name = "mgbe1w",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISP0R,
|
||||
.name = "isp0r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SDMMC0R,
|
||||
.name = "sdmmc0r",
|
||||
.bpmp_id = TEGRA264_BWMGR_SDMMC_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_MPCOREW,
|
||||
.name = "mpcorew",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SDMMC0W,
|
||||
.name = "sdmmc0w",
|
||||
.bpmp_id = TEGRA264_BWMGR_SDMMC_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISP0W,
|
||||
.name = "isp0w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISP1W,
|
||||
.name = "isp1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISPFALCONR,
|
||||
.name = "ispfalconr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISPFALCONW,
|
||||
.name = "ispfalconw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE2R,
|
||||
.name = "mgbe2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_OFAR2MC,
|
||||
.name = "ofar2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_OFAW2MC,
|
||||
.name = "ofaw2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE2W,
|
||||
.name = "mgbe2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE3R,
|
||||
.name = "mgbe3r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE3W,
|
||||
.name = "mgbe3w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEU1RD,
|
||||
.name = "seu1rd",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEU1WR,
|
||||
.name = "seu1wr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_VICR,
|
||||
.name = "vicr",
|
||||
@@ -70,6 +87,15 @@ static const struct tegra_mc_client tegra264_mc_clients[] = {
|
||||
.name = "vicw",
|
||||
.bpmp_id = TEGRA264_BWMGR_VIC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_VIW,
|
||||
.name = "viw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XSPI0R,
|
||||
.name = "xspi0r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XSPI0W,
|
||||
.name = "xspi0w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_APER,
|
||||
.name = "aper",
|
||||
@@ -80,6 +106,48 @@ static const struct tegra_mc_client tegra264_mc_clients[] = {
|
||||
.name = "apew",
|
||||
.bpmp_id = TEGRA264_BWMGR_APE,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SER,
|
||||
.name = "ser",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEW,
|
||||
.name = "sew",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_AXIAPR,
|
||||
.name = "axiapr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_AXIAPW,
|
||||
.name = "axiapw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ETRR,
|
||||
.name = "etrr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ETRW,
|
||||
.name = "etrw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_TSECR,
|
||||
.name = "tsecr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_TSECW,
|
||||
.name = "tsecw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_BPMPR,
|
||||
.name = "bpmpr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_BPMPW,
|
||||
.name = "bpmpw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_AONR,
|
||||
.name = "aonr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_AONW,
|
||||
.name = "aonw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_GPCDMAR,
|
||||
.name = "gpcdmar",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_GPCDMAW,
|
||||
.name = "gpcdmaw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_APEDMAR,
|
||||
.name = "apedmar",
|
||||
@@ -90,6 +158,36 @@ static const struct tegra_mc_client tegra264_mc_clients[] = {
|
||||
.name = "apedmaw",
|
||||
.bpmp_id = TEGRA264_BWMGR_APEDMA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU0R,
|
||||
.name = "miu0r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU0W,
|
||||
.name = "miu0w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU1R,
|
||||
.name = "miu1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU1W,
|
||||
.name = "miu1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU2R,
|
||||
.name = "miu2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU2W,
|
||||
.name = "miu2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU3R,
|
||||
.name = "miu3r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU3W,
|
||||
.name = "miu3w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU4R,
|
||||
.name = "miu4r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU4W,
|
||||
.name = "miu4w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_VIFALCONR,
|
||||
.name = "vifalconr",
|
||||
@@ -110,6 +208,12 @@ static const struct tegra_mc_client tegra264_mc_clients[] = {
|
||||
.name = "rcew",
|
||||
.bpmp_id = TEGRA264_BWMGR_RCE,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_NVENC1SRD2MC,
|
||||
.name = "nvenc1srd2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_NVENC1SWR2MC,
|
||||
.name = "nvenc1swr2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PCIE0W,
|
||||
.name = "pcie0w",
|
||||
@@ -185,6 +289,402 @@ static const struct tegra_mc_client tegra264_mc_clients[] = {
|
||||
.name = "nvdecswr2mc",
|
||||
.bpmp_id = TEGRA264_BWMGR_NVDEC,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU5R,
|
||||
.name = "miu5r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU5W,
|
||||
.name = "miu5w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU6W,
|
||||
.name = "miu6w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_RISTR,
|
||||
.name = "ristr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_RISTW,
|
||||
.name = "ristw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_OESPR,
|
||||
.name = "oespr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_OESPW,
|
||||
.name = "oespw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU7W,
|
||||
.name = "miu7w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU8R,
|
||||
.name = "miu8r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU8W,
|
||||
.name = "miu8w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU9R,
|
||||
.name = "miu9r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MIU9W,
|
||||
.name = "miu9w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PMA0AWR,
|
||||
.name = "pma0awr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_NVJPG1SRD2MC,
|
||||
.name = "nvjpg1srd2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_NVJPG1SWR2MC,
|
||||
.name = "nvjpg1swr2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU0CTWR,
|
||||
.name = "smmu0ctwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU0CMDQVR,
|
||||
.name = "smmu0cmdqvr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU0CMDQVW,
|
||||
.name = "smmu0cmdqvw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU0EVNTQW,
|
||||
.name = "smmu0evntqw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU1PTWR,
|
||||
.name = "smmu1ptwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU1CTWR,
|
||||
.name = "smmu1ctwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU1CMDQVR,
|
||||
.name = "smmu1cmdqvr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU1CMDQVW,
|
||||
.name = "smmu1cmdqvw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU1EVNTQW,
|
||||
.name = "smmu1evntqw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU2PTWR,
|
||||
.name = "smmu2ptwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU2CTWR,
|
||||
.name = "smmu2ctwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU2CMDQVR,
|
||||
.name = "smmu2cmdqvr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU2CMDQVW,
|
||||
.name = "smmu2cmdqvw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU2EVNTQW,
|
||||
.name = "smmu2evntqw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU0CMDQR,
|
||||
.name = "smmu0cmdqr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU1CMDQR,
|
||||
.name = "smmu1cmdqr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU2CMDQR,
|
||||
.name = "smmu2cmdqr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_APE1R,
|
||||
.name = "ape1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_APE1W,
|
||||
.name = "ape1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_UFSR,
|
||||
.name = "ufsr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_UFSW,
|
||||
.name = "ufsw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEVR,
|
||||
.name = "xusb_devr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEVW,
|
||||
.name = "xusb_devw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV1R,
|
||||
.name = "xusb_dev1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV2W,
|
||||
.name = "xusb_dev2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV3R,
|
||||
.name = "xusb_dev3r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV3W,
|
||||
.name = "xusb_dev3w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV4R,
|
||||
.name = "xusb_dev4r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV4W,
|
||||
.name = "xusb_dev4w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV5R,
|
||||
.name = "xusb_dev5r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV5W,
|
||||
.name = "xusb_dev5w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_DCER,
|
||||
.name = "dcer",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_DCEW,
|
||||
.name = "dcew",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HDAR,
|
||||
.name = "hdar",
|
||||
.bpmp_id = TEGRA264_BWMGR_HDA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HDAW,
|
||||
.name = "hdaw",
|
||||
.bpmp_id = TEGRA264_BWMGR_HDA,
|
||||
.type = TEGRA_ICC_ISO_AUDIO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_DISPNISOR,
|
||||
.name = "dispnisor",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_DISPNISOW,
|
||||
.name = "dispnisow",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV1W,
|
||||
.name = "xusb_dev1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_XUSB_DEV2R,
|
||||
.name = "xusb_dev2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_DISPR,
|
||||
.name = "dispr",
|
||||
.bpmp_id = TEGRA264_BWMGR_DISPLAY,
|
||||
.type = TEGRA_ICC_ISO_DISPLAY,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MSSSEQR,
|
||||
.name = "mssseqr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MSSSEQW,
|
||||
.name = "mssseqw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU3PTWR,
|
||||
.name = "smmu3ptwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU3CTWR,
|
||||
.name = "smmu3ctwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU3CMDQVR,
|
||||
.name = "smmu3cmdqvr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU3CMDQVW,
|
||||
.name = "smmu3cmdqvw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU3EVNTQW,
|
||||
.name = "smmu3evntqw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU3CMDQR,
|
||||
.name = "smmu3cmdqr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU4PTWR,
|
||||
.name = "smmu4ptwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU4CTWR,
|
||||
.name = "smmu4ctwr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU4CMDQVR,
|
||||
.name = "smmu4cmdqvr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU4CMDQVW,
|
||||
.name = "smmu4cmdqvw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU4EVNTQW,
|
||||
.name = "smmu4evntqw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SMMU4CMDQR,
|
||||
.name = "smmu4cmdqr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE0R,
|
||||
.name = "mgbe0r",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE0W,
|
||||
.name = "mgbe0w",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE1R,
|
||||
.name = "mgbe1r",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_MGBE1W,
|
||||
.name = "mgbe1w",
|
||||
.bpmp_id = TEGRA264_BWMGR_EQOS,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_VI1W,
|
||||
.name = "vi1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_VIFALCON1R,
|
||||
.name = "vifalcon1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_VIFALCON1W,
|
||||
.name = "vifalcon1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISPFALCON1R,
|
||||
.name = "ispfalcon1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISPFALCON1W,
|
||||
.name = "ispfalcon1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_RCE1R,
|
||||
.name = "rce1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_RCE1W,
|
||||
.name = "rce1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEU2R,
|
||||
.name = "seu2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEU2W,
|
||||
.name = "seu2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEU3R,
|
||||
.name = "seu3r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SEU3W,
|
||||
.name = "seu3w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PVA0R,
|
||||
.name = "pva0r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PVA0W,
|
||||
.name = "pva0w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PVA1R,
|
||||
.name = "pva1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PVA1W,
|
||||
.name = "pva1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PVA2R,
|
||||
.name = "pva2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_PVA2W,
|
||||
.name = "pva2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISP3W,
|
||||
.name = "isp3w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISP2R,
|
||||
.name = "isp2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_ISP2W,
|
||||
.name = "isp2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_EQOSR,
|
||||
.name = "eqosr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_EQOSW,
|
||||
.name = "eqosw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_FSI0R,
|
||||
.name = "fsi0r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_FSI0W,
|
||||
.name = "fsi0w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_FSI1R,
|
||||
.name = "fsi1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_FSI1W,
|
||||
.name = "fsi1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SDMMC0R,
|
||||
.name = "sdmmc0r",
|
||||
.bpmp_id = TEGRA264_BWMGR_SDMMC_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SDMMC0W,
|
||||
.name = "sdmmc0w",
|
||||
.bpmp_id = TEGRA264_BWMGR_SDMMC_1,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SBR,
|
||||
.name = "sbr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SBW,
|
||||
.name = "sbw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU0R,
|
||||
.name = "hss_miu0r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU0W,
|
||||
.name = "hss_miu0w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU1R,
|
||||
.name = "hss_miu1r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU1W,
|
||||
.name = "hss_miu1w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU2R,
|
||||
.name = "hss_miu2r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU2W,
|
||||
.name = "hss_miu2w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU3R,
|
||||
.name = "hss_miu3r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU3W,
|
||||
.name = "hss_miu3w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU4R,
|
||||
.name = "hss_miu4r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU4W,
|
||||
.name = "hss_miu4w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU5R,
|
||||
.name = "hss_miu5r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU5W,
|
||||
.name = "hss_miu5w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU6R,
|
||||
.name = "hss_miu6r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU6W,
|
||||
.name = "hss_miu6w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU7R,
|
||||
.name = "hss_miu7r",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_HSS_MIU7W,
|
||||
.name = "hss_miu7w",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_GMMUR2MC,
|
||||
.name = "gmmur2mc",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_UCFELAR,
|
||||
.name = "ucfelar",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_UCFELAW,
|
||||
.name = "ucfelaw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SLCR,
|
||||
.name = "slcr",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_SLCW,
|
||||
.name = "slcw",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_REMOTER,
|
||||
.name = "remoter",
|
||||
}, {
|
||||
.id = TEGRA264_MEMORY_CLIENT_REMOTEW,
|
||||
.name = "remotew",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -262,6 +762,10 @@ static int tegra264_mc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Skip forwarding bw requests to BPMP from clients without bpmp_id/type. */
|
||||
if (pclient->type == TEGRA_ICC_NONE || !pclient->bpmp_id)
|
||||
return 0;
|
||||
|
||||
if (pclient->type == TEGRA_ICC_NISO)
|
||||
bwmgr_req.bwmgr_calc_set_req.niso_bw = src->avg_bw;
|
||||
else
|
||||
@@ -295,21 +799,6 @@ static int tegra264_mc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra264_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
|
||||
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
|
||||
{
|
||||
struct icc_provider *p = node->provider;
|
||||
struct tegra_mc *mc = icc_provider_to_tegra_mc(p);
|
||||
|
||||
if (!mc->bwmgr_mrq_supported)
|
||||
return 0;
|
||||
|
||||
*agg_avg += avg_bw;
|
||||
*agg_peak = max(*agg_peak, peak_bw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra264_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
|
||||
{
|
||||
*avg = 0;
|
||||
@@ -626,7 +1115,7 @@ static const irq_handler_t tegra264_mc_irq_handlers[8] = {
|
||||
|
||||
static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = {
|
||||
.xlate = tegra_mc_icc_xlate,
|
||||
.aggregate = tegra264_mc_icc_aggregate,
|
||||
.aggregate = icc_std_aggregate,
|
||||
.get_bw = tegra264_mc_icc_get_init_bw,
|
||||
.set = tegra264_mc_icc_set,
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "../of_memory.h"
|
||||
|
||||
#include "mc.h"
|
||||
#include "tegra-emc-common.h"
|
||||
|
||||
#define EMC_INTSTATUS 0x000
|
||||
#define EMC_INTMASK 0x004
|
||||
@@ -341,17 +342,6 @@ struct emc_timing {
|
||||
bool emc_cfg_dyn_self_ref;
|
||||
};
|
||||
|
||||
enum emc_rate_request_type {
|
||||
EMC_RATE_DEBUG,
|
||||
EMC_RATE_ICC,
|
||||
EMC_RATE_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct emc_rate_request {
|
||||
unsigned long min_rate;
|
||||
unsigned long max_rate;
|
||||
};
|
||||
|
||||
struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct tegra_mc *mc;
|
||||
@@ -383,14 +373,7 @@ struct tegra_emc {
|
||||
unsigned long max_rate;
|
||||
} debugfs;
|
||||
|
||||
/*
|
||||
* There are multiple sources in the EMC driver which could request
|
||||
* a min/max clock rate, these rates are contained in this array.
|
||||
*/
|
||||
struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
|
||||
|
||||
/* protect shared rate-change code path */
|
||||
struct mutex rate_lock;
|
||||
struct tegra_emc_rate_requests reqs;
|
||||
|
||||
bool mrr_error;
|
||||
};
|
||||
@@ -1228,83 +1211,6 @@ static long emc_round_rate(unsigned long rate,
|
||||
return timing->rate;
|
||||
}
|
||||
|
||||
static void tegra30_emc_rate_requests_init(struct tegra_emc *emc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
|
||||
emc->requested_rate[i].min_rate = 0;
|
||||
emc->requested_rate[i].max_rate = ULONG_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc_request_rate(struct tegra_emc *emc,
|
||||
unsigned long new_min_rate,
|
||||
unsigned long new_max_rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = emc->requested_rate;
|
||||
unsigned long min_rate = 0, max_rate = ULONG_MAX;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* select minimum and maximum rates among the requested rates */
|
||||
for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
|
||||
if (i == type) {
|
||||
min_rate = max(new_min_rate, min_rate);
|
||||
max_rate = min(new_max_rate, max_rate);
|
||||
} else {
|
||||
min_rate = max(req->min_rate, min_rate);
|
||||
max_rate = min(req->max_rate, max_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if (min_rate > max_rate) {
|
||||
dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
|
||||
__func__, type, min_rate, max_rate);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* EMC rate-changes should go via OPP API because it manages voltage
|
||||
* changes.
|
||||
*/
|
||||
err = dev_pm_opp_set_rate(emc->dev, min_rate);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
emc->requested_rate[type].min_rate = new_min_rate;
|
||||
emc->requested_rate[type].max_rate = new_max_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = &emc->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emc->rate_lock);
|
||||
ret = emc_request_rate(emc, rate, req->max_rate, type);
|
||||
mutex_unlock(&emc->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
|
||||
enum emc_rate_request_type type)
|
||||
{
|
||||
struct emc_rate_request *req = &emc->requested_rate[type];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emc->rate_lock);
|
||||
ret = emc_request_rate(emc, req->min_rate, rate, type);
|
||||
mutex_unlock(&emc->rate_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs interface
|
||||
*
|
||||
@@ -1375,7 +1281,7 @@ static int tegra30_emc_debug_min_rate_set(void *data, u64 rate)
|
||||
if (!tegra30_emc_validate_rate(emc, rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
|
||||
err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -1405,7 +1311,7 @@ static int tegra30_emc_debug_max_rate_set(void *data, u64 rate)
|
||||
if (!tegra30_emc_validate_rate(emc, rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
|
||||
err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -1511,7 +1417,7 @@ static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
do_div(rate, ddr * dram_data_bus_width_bytes);
|
||||
rate = min_t(u64, rate, U32_MAX);
|
||||
|
||||
err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
|
||||
err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1622,7 +1528,6 @@ static int tegra30_emc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(emc->mc))
|
||||
return PTR_ERR(emc->mc);
|
||||
|
||||
mutex_init(&emc->rate_lock);
|
||||
emc->clk_nb.notifier_call = emc_clk_change_notify;
|
||||
emc->dev = &pdev->dev;
|
||||
|
||||
@@ -1664,7 +1569,7 @@ static int tegra30_emc_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
|
||||
platform_set_drvdata(pdev, emc);
|
||||
tegra30_emc_rate_requests_init(emc);
|
||||
tegra_emc_rate_requests_init(&emc->reqs, &pdev->dev);
|
||||
tegra30_emc_debugfs_init(emc);
|
||||
tegra30_emc_interconnect_init(emc);
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ static int sec_pmic_acpm_bus_write(void *context, const void *data,
|
||||
{
|
||||
struct sec_pmic_acpm_bus_context *ctx = context;
|
||||
struct acpm_handle *acpm = ctx->shared->acpm;
|
||||
const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
|
||||
const struct acpm_pmic_ops *pmic_ops = &acpm->ops->pmic;
|
||||
size_t val_count = count - BITS_TO_BYTES(ACPM_ADDR_BITS);
|
||||
const u8 *d = data;
|
||||
const u8 *vals = &d[BITS_TO_BYTES(ACPM_ADDR_BITS)];
|
||||
@@ -411,7 +411,7 @@ static int sec_pmic_acpm_bus_read(void *context, const void *reg_buf, size_t reg
|
||||
{
|
||||
struct sec_pmic_acpm_bus_context *ctx = context;
|
||||
struct acpm_handle *acpm = ctx->shared->acpm;
|
||||
const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
|
||||
const struct acpm_pmic_ops *pmic_ops = &acpm->ops->pmic;
|
||||
const u8 *r = reg_buf;
|
||||
u8 reg;
|
||||
|
||||
@@ -430,7 +430,7 @@ static int sec_pmic_acpm_bus_reg_update_bits(void *context, unsigned int reg, un
|
||||
{
|
||||
struct sec_pmic_acpm_bus_context *ctx = context;
|
||||
struct acpm_handle *acpm = ctx->shared->acpm;
|
||||
const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
|
||||
const struct acpm_pmic_ops *pmic_ops = &acpm->ops->pmic;
|
||||
|
||||
return pmic_ops->update_reg(acpm, ctx->shared->acpm_chan_id, ctx->type, reg & 0xff,
|
||||
ctx->shared->speedy_channel, val, mask);
|
||||
|
||||
@@ -340,6 +340,8 @@ struct tegra_pcie {
|
||||
struct reset_control *afi_rst;
|
||||
struct reset_control *pcie_xrst;
|
||||
|
||||
struct tegra_pmc *pmc;
|
||||
|
||||
bool legacy_phy;
|
||||
struct phy *phy;
|
||||
|
||||
@@ -1165,7 +1167,7 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
||||
clk_disable_unprepare(pcie->afi_clk);
|
||||
|
||||
if (!dev->pm_domain)
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
||||
tegra_pmc_powergate_power_off(pcie->pmc, TEGRA_POWERGATE_PCIE);
|
||||
|
||||
err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
|
||||
if (err < 0)
|
||||
@@ -1183,7 +1185,7 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||
reset_control_assert(pcie->pex_rst);
|
||||
|
||||
if (!dev->pm_domain)
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
||||
tegra_pmc_powergate_power_off(pcie->pmc, TEGRA_POWERGATE_PCIE);
|
||||
|
||||
/* enable regulators */
|
||||
err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
|
||||
@@ -1191,12 +1193,14 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||
dev_err(dev, "failed to enable regulators: %d\n", err);
|
||||
|
||||
if (!dev->pm_domain) {
|
||||
err = tegra_powergate_power_on(TEGRA_POWERGATE_PCIE);
|
||||
err = tegra_pmc_powergate_power_on(pcie->pmc,
|
||||
TEGRA_POWERGATE_PCIE);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to power ungate: %d\n", err);
|
||||
goto regulator_disable;
|
||||
}
|
||||
err = tegra_powergate_remove_clamping(TEGRA_POWERGATE_PCIE);
|
||||
err = tegra_pmc_powergate_remove_clamping(pcie->pmc,
|
||||
TEGRA_POWERGATE_PCIE);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to remove clamp: %d\n", err);
|
||||
goto powergate;
|
||||
@@ -1234,7 +1238,7 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||
clk_disable_unprepare(pcie->afi_clk);
|
||||
powergate:
|
||||
if (!dev->pm_domain)
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
||||
tegra_pmc_powergate_power_off(pcie->pmc, TEGRA_POWERGATE_PCIE);
|
||||
regulator_disable:
|
||||
regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
|
||||
|
||||
@@ -1432,6 +1436,11 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
|
||||
return err;
|
||||
}
|
||||
|
||||
pcie->pmc = devm_tegra_pmc_get(dev);
|
||||
if (IS_ERR(pcie->pmc))
|
||||
return dev_err_probe(dev, PTR_ERR(pcie->pmc),
|
||||
"failed to get PMC\n");
|
||||
|
||||
if (soc->program_uphy) {
|
||||
err = tegra_pcie_phys_get(pcie);
|
||||
if (err < 0) {
|
||||
|
||||
@@ -46,7 +46,6 @@ config ASPEED_P2A_CTRL
|
||||
|
||||
config ASPEED_SOCINFO
|
||||
bool "ASPEED SoC Information driver"
|
||||
default ARCH_ASPEED
|
||||
select SOC_BUS
|
||||
default ARCH_ASPEED
|
||||
help
|
||||
|
||||
@@ -338,6 +338,7 @@ static const struct of_device_id aspeed_lpc_ctrl_match[] = {
|
||||
{ .compatible = "aspeed,ast2600-lpc-ctrl" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match);
|
||||
|
||||
static struct platform_driver aspeed_lpc_ctrl_driver = {
|
||||
.driver = {
|
||||
@@ -350,7 +351,6 @@ static struct platform_driver aspeed_lpc_ctrl_driver = {
|
||||
|
||||
module_platform_driver(aspeed_lpc_ctrl_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
|
||||
MODULE_DESCRIPTION("Control for ASPEED LPC HOST to BMC mappings");
|
||||
|
||||
@@ -366,6 +366,7 @@ static const struct of_device_id aspeed_lpc_snoop_match[] = {
|
||||
.data = &ast2500_model_data },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_lpc_snoop_match);
|
||||
|
||||
static struct platform_driver aspeed_lpc_snoop_driver = {
|
||||
.driver = {
|
||||
@@ -378,7 +379,6 @@ static struct platform_driver aspeed_lpc_snoop_driver = {
|
||||
|
||||
module_platform_driver(aspeed_lpc_snoop_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(of, aspeed_lpc_snoop_match);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Robert Lippert <rlippert@google.com>");
|
||||
MODULE_DESCRIPTION("Linux driver to control Aspeed LPC snoop functionality");
|
||||
|
||||
@@ -416,6 +416,7 @@ static const struct of_device_id aspeed_p2a_ctrl_match[] = {
|
||||
.data = &ast2500_model_data },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
|
||||
|
||||
static struct platform_driver aspeed_p2a_ctrl_driver = {
|
||||
.driver = {
|
||||
@@ -428,7 +429,6 @@ static struct platform_driver aspeed_p2a_ctrl_driver = {
|
||||
|
||||
module_platform_driver(aspeed_p2a_ctrl_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Patrick Venture <venture@google.com>");
|
||||
MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings");
|
||||
|
||||
@@ -11,18 +11,22 @@
|
||||
#include <linux/soc/brcmstb/brcmstb.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
static u32 family_id;
|
||||
static u32 product_id;
|
||||
struct brcmstb_soc_info {
|
||||
u32 family_id;
|
||||
u32 product_id;
|
||||
};
|
||||
|
||||
static struct brcmstb_soc_info *soc_info;
|
||||
|
||||
u32 brcmstb_get_family_id(void)
|
||||
{
|
||||
return family_id;
|
||||
return soc_info ? soc_info->family_id : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(brcmstb_get_family_id);
|
||||
|
||||
u32 brcmstb_get_product_id(void)
|
||||
{
|
||||
return product_id;
|
||||
return soc_info ? soc_info->product_id : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(brcmstb_get_product_id);
|
||||
|
||||
@@ -40,38 +44,11 @@ static const struct of_device_id sun_top_ctrl_match[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static int __init brcmstb_soc_device_early_init(void)
|
||||
{
|
||||
struct device_node *sun_top_ctrl;
|
||||
void __iomem *sun_top_ctrl_base;
|
||||
int ret = 0;
|
||||
|
||||
/* We could be on a multi-platform kernel, don't make this fatal but
|
||||
* bail out early
|
||||
*/
|
||||
sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match);
|
||||
if (!sun_top_ctrl)
|
||||
return ret;
|
||||
|
||||
sun_top_ctrl_base = of_iomap(sun_top_ctrl, 0);
|
||||
if (!sun_top_ctrl_base) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
family_id = readl(sun_top_ctrl_base);
|
||||
product_id = readl(sun_top_ctrl_base + 0x4);
|
||||
iounmap(sun_top_ctrl_base);
|
||||
out:
|
||||
of_node_put(sun_top_ctrl);
|
||||
return ret;
|
||||
}
|
||||
early_initcall(brcmstb_soc_device_early_init);
|
||||
|
||||
static int __init brcmstb_soc_device_init(void)
|
||||
{
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
struct device_node *sun_top_ctrl;
|
||||
void __iomem *sun_top_ctrl_base;
|
||||
struct soc_device *soc_dev;
|
||||
int ret = 0;
|
||||
|
||||
@@ -80,34 +57,61 @@ static int __init brcmstb_soc_device_init(void)
|
||||
*/
|
||||
sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match);
|
||||
if (!sun_top_ctrl)
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
sun_top_ctrl_base = of_iomap(sun_top_ctrl, 0);
|
||||
if (!sun_top_ctrl_base) {
|
||||
ret = -ENODEV;
|
||||
goto out_put_node;
|
||||
}
|
||||
|
||||
soc_info = kzalloc(sizeof(*soc_info), GFP_KERNEL);
|
||||
if (!soc_info) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
soc_info->family_id = readl(sun_top_ctrl_base);
|
||||
soc_info->product_id = readl(sun_top_ctrl_base + 0x4);
|
||||
|
||||
soc_dev_attr = kzalloc_obj(*soc_dev_attr);
|
||||
if (!soc_dev_attr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
goto out_free_info;
|
||||
}
|
||||
|
||||
soc_dev_attr->family = kasprintf(GFP_KERNEL, "%x",
|
||||
family_id >> 28 ?
|
||||
family_id >> 16 : family_id >> 8);
|
||||
soc_info->family_id >> 28 ?
|
||||
soc_info->family_id >> 16 : soc_info->family_id >> 8);
|
||||
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%x",
|
||||
product_id >> 28 ?
|
||||
product_id >> 16 : product_id >> 8);
|
||||
soc_info->product_id >> 28 ?
|
||||
soc_info->product_id >> 16 : soc_info->product_id >> 8);
|
||||
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c%d",
|
||||
((product_id & 0xf0) >> 4) + 'A',
|
||||
product_id & 0xf);
|
||||
((soc_info->product_id & 0xf0) >> 4) + 'A',
|
||||
soc_info->product_id & 0xf);
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr->family);
|
||||
kfree(soc_dev_attr->soc_id);
|
||||
kfree(soc_dev_attr->revision);
|
||||
kfree(soc_dev_attr);
|
||||
ret = -ENOMEM;
|
||||
ret = PTR_ERR(soc_dev);
|
||||
goto out_free_attr;
|
||||
}
|
||||
out:
|
||||
|
||||
iounmap(sun_top_ctrl_base);
|
||||
of_node_put(sun_top_ctrl);
|
||||
return 0;
|
||||
|
||||
out_free_attr:
|
||||
kfree(soc_dev_attr->revision);
|
||||
kfree(soc_dev_attr->soc_id);
|
||||
kfree(soc_dev_attr->family);
|
||||
kfree(soc_dev_attr);
|
||||
out_free_info:
|
||||
kfree(soc_info);
|
||||
soc_info = NULL;
|
||||
out_unmap:
|
||||
iounmap(sun_top_ctrl_base);
|
||||
out_put_node:
|
||||
of_node_put(sun_top_ctrl);
|
||||
return ret;
|
||||
}
|
||||
arch_initcall(brcmstb_soc_device_init);
|
||||
early_initcall(brcmstb_soc_device_init);
|
||||
|
||||
@@ -89,6 +89,9 @@ void qe_reset(void)
|
||||
if (qe_immr == NULL)
|
||||
qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE);
|
||||
|
||||
if (!qe_immr)
|
||||
panic("QE:ioremap failed!");
|
||||
|
||||
qe_snums_init();
|
||||
|
||||
qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
struct qepic_data {
|
||||
void __iomem *reg;
|
||||
struct irq_domain *host;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static void qepic_mask(struct irq_data *d)
|
||||
@@ -92,11 +93,18 @@ static const struct irq_domain_ops qepic_host_ops = {
|
||||
.map = qepic_host_map,
|
||||
};
|
||||
|
||||
static void qepic_remove(void *res)
|
||||
{
|
||||
struct qepic_data *data = res;
|
||||
|
||||
irq_set_chained_handler_and_data(data->irq, NULL, NULL);
|
||||
irq_domain_remove(data->host);
|
||||
}
|
||||
|
||||
static int qepic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct qepic_data *data;
|
||||
int irq;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
@@ -106,17 +114,18 @@ static int qepic_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(data->reg))
|
||||
return PTR_ERR(data->reg);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (data->irq < 0)
|
||||
return data->irq;
|
||||
|
||||
data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data);
|
||||
data->host = irq_domain_create_linear(dev_fwnode(dev), 32, &qepic_host_ops, data);
|
||||
if (!data->host)
|
||||
return -ENODEV;
|
||||
|
||||
irq_set_chained_handler_and_data(irq, qepic_cascade, data);
|
||||
irq_set_chained_handler_and_data(data->irq, qepic_cascade, data);
|
||||
|
||||
return devm_add_action_or_reset(dev, qepic_remove, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qepic_match[] = {
|
||||
|
||||
@@ -10,24 +10,29 @@
|
||||
#define MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN 0x06c
|
||||
|
||||
#define MT8167_DITHER_MOUT_EN_RDMA0 0x1
|
||||
#define MT8167_DITHER_MOUT_EN_MASK 0x7
|
||||
|
||||
#define MT8167_RDMA0_SOUT_DSI0 0x2
|
||||
#define MT8167_RDMA0_SOUT_MASK 0x3
|
||||
|
||||
#define MT8167_DSI0_SEL_IN_RDMA0 0x1
|
||||
#define MT8167_DSI0_SEL_IN_MASK 0x3
|
||||
|
||||
static const struct mtk_mmsys_routes mt8167_mmsys_routing_table[] = {
|
||||
MMSYS_ROUTE(OVL0, COLOR0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0,
|
||||
OVL0_MOUT_EN_COLOR0),
|
||||
MMSYS_ROUTE(DITHER0, RDMA0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_RDMA0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_MASK,
|
||||
MT8167_DITHER_MOUT_EN_RDMA0),
|
||||
MMSYS_ROUTE(OVL0, COLOR0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0,
|
||||
COLOR0_SEL_IN_OVL0),
|
||||
MMSYS_ROUTE(RDMA0, DSI0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_RDMA0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_MASK,
|
||||
MT8167_DSI0_SEL_IN_RDMA0),
|
||||
MMSYS_ROUTE(RDMA0, DSI0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_DSI0,
|
||||
MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_MASK,
|
||||
MT8167_RDMA0_SOUT_DSI0),
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user