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:
Linus Torvalds
2026-06-17 11:21:40 -07:00
153 changed files with 7340 additions and 2139 deletions

10
CREDITS
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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$"

View File

@@ -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:

View File

@@ -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

View File

@@ -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/

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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:
*/

View File

@@ -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));
}

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 */ }
};

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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,
};
/**

View File

@@ -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.

View File

@@ -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 = {

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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; \
} \
\

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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,

View File

@@ -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);

View File

@@ -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]);

View File

@@ -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");

View File

@@ -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>");

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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", },

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View 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);
}

View 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__ */

View File

@@ -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",

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -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;

View File

@@ -18,6 +18,8 @@ struct nvkm_device_tegra {
struct regulator *vdd;
struct tegra_pmc *pmc;
struct {
/*
* Protects accesses to mm from subsystems

View File

@@ -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.
*/

View File

@@ -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);

View File

@@ -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;

View File

@@ -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.

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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,
},

View File

@@ -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

View 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");

View 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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 = {

View File

@@ -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);

View File

@@ -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,

View 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),
};

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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

View File

@@ -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");

View File

@@ -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");

View File

@@ -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");

View File

@@ -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);

View File

@@ -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,

View File

@@ -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[] = {

View File

@@ -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