* Re: [PATCH v3 2/7] gpio: regmap: add gpio_regmap_get_gpiochip() accessor
From: Michael Walle @ 2026-06-17 8:44 UTC (permalink / raw)
To: Yu-Chun Lin [林祐君], Bartosz Golaszewski,
Andy Shevchenko
Cc: linusw@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, afaerber@suse.com, wbg@kernel.org,
mathieu.dubois-briand@bootlin.com, lars@metafoo.de,
Michael.Hennerich@analog.com, jic23@kernel.org,
nuno.sa@analog.com, andy@kernel.org, dlechner@baylibre.com,
TY_Chang[張子逸], linux-gpio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-realtek-soc@lists.infradead.org, linux-iio@vger.kernel.org,
CY_Huang[黃鉦晏],
Stanley Chang[昌育德],
James Tai [戴志峰]
In-Reply-To: <39de4d4ada5446e7a33e48c43f410439@realtek.com>
[-- Attachment #1: Type: text/plain, Size: 839 bytes --]
Hi,
On Wed Jun 17, 2026 at 10:36 AM CEST, Yu-Chun Lin [林祐君] wrote:
>>>>> Without an accessor like gpio_regmap_get_gpiochip(), we cannot
>>>>> retrieve the gpio_chip instantiated inside gpio-regmap.c to fulfill
>>>>> these requirements in our
>>>>> map() function.
>>
>> Why is gpiochip_irq_reqres() called in the first place? Isn't that only
>> called if the irq handling is set up via gc->irq.chip and not via
>> gpiochip_irqchip_add_domain() like in gpio-regmap?
>>
>
> The panic was caused by my driver including 'GPIOCHIP_IRQ_RESOURCE_HELPERS',
> which forced the call to 'gpiochip_irq_reqres()' and crashed.
But why did you use it if your irq domain isn't managed by the
gpiolib, but rather your own irq domain? Before going with option #3
I'd double check if that is correct in your driver.
-michael
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply
* [PATCH RESEND v3] wifi: mt76: mt7921: fix resource leak in probe error path
From: Hongling Zeng @ 2026-06-17 8:58 UTC (permalink / raw)
To: nbd, lorenzo, ryder.lee, shayne.chen, sean.wang, matthias.bgg,
angelogioacchino.delregno, xiong.huang, madhurkumar004
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
zhongling0719, Hongling Zeng
When pcim_iomap_region() or devm_kmemdup() fail, the code returns
directly without cleaning up previously allocated resources:
- mt76_device allocated by mt76_alloc_device()
- pci irq vectors allocated by pci_alloc_irq_vectors()
Fix this by jumping to the existing error cleanup path instead of
returning directly.
To avoid using an uninitialized variable in the error path, move the
dev initialization before the error checks.
Fixes: ee5bb35d2b83 ("wifi: mt76: mt7921: Replace deprecated PCI function")
Fixes: 222606f43b58 ("wifi: mt76: mt7921: handle MT7902 irq_map quirk with mutable copy")
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
---
Change in v3
- Fix incorrect Fixes: tag as pointed out by Sean
- Correct tag from unrelated phy/ti-pipe3 commit to the actual mt76 commit
that introduced the resource leak
---
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 7a790ddf43bb..49a37185f056 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -343,11 +343,14 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mdev);
+ dev = container_of(mdev, struct mt792x_dev, mt76);
+
regs = pcim_iomap_region(pdev, 0, pci_name(pdev));
- if (IS_ERR(regs))
- return PTR_ERR(regs);
+ if (IS_ERR(regs)) {
+ ret = PTR_ERR(regs);
+ goto err_free_dev;
+ }
- dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &mt7921_pcie_ops;
dev->irq_map = &irq_map;
@@ -359,8 +362,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
/* MT7902 needs a mutable copy because wm2_complete_mask differs */
map = devm_kmemdup(&pdev->dev, &irq_map,
sizeof(irq_map), GFP_KERNEL);
- if (!map)
- return -ENOMEM;
+ if (!map) {
+ ret = -ENOMEM;
+ goto err_free_dev;
+ }
map->rx.wm2_complete_mask = 0;
dev->irq_map = map;
--
2.25.1
^ permalink raw reply related
* [GIT PULL] pmdomain updates for v7.2
From: Ulf Hansson @ 2026-06-17 9:05 UTC (permalink / raw)
To: Linus, linux-pm, linux-kernel; +Cc: Ulf Hansson, linux-arm-kernel
Hi Linus,
Here's the pull-request with pmdomain updates for v7.2. Details about the
highlights are as usual found in the signed tag.
Please pull this in!
Kind regards
Ulf Hansson
The following changes since commit fba0510cd62666951dcc0221527edc0c47ae6599:
pmdomain: imx: fix OF node refcount (2026-06-03 11:49:37 +0200)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm.git tags/pmdomain-v7.2
for you to fetch changes up to 528ad521a433cf873724893bda339df95d8ac1e0:
pmdomain: core: fix unused variable warning with !PM_GENERIC_DOMAINS_OF (2026-06-15 21:12:35 +0200)
----------------------------------------------------------------
pmdomain core:
- Add OF helpers for parsing the power-domains-child-ids property
- Extend the power domain DT binding with power-domains-child-ids
- Switch to use the dynamic root device
pmdomain providers:
- arm: Add support for domain hierarchies to SCMI power domains
- qcom: Add power domains for the Shikra and Nord SoCs
- sunxi: Fix GPU support on Radxa Cubie A7Z by keeping power domain on
----------------------------------------------------------------
Johan Hovold (3):
pmdomain: core: switch to dynamic root device
pmdomain: core: fix early domain registration
pmdomain: core: fix unused variable warning with !PM_GENERIC_DOMAINS_OF
Kamal Wadhwa (2):
dt-bindings: power: qcom,rpmhpd: Add RPMh power domain for Nord SoC
pmdomain: qcom: rpmhpd: Add power domains for Nord SoC
Kevin Hilman (TI) (3):
dt-bindings: power: Add power-domains-child-ids property
pmdomain: core: add support for power-domains-child-ids
pmdomain: arm_scmi: add support for domain hierarchies
Krzysztof Kozlowski (1):
pmdomain: qcom: Unify user-visible "Qualcomm" name
Rakesh Kota (2):
dt-bindings: power: qcom,rpmpd: document the Shikra RPM Power Domains
pmdomain: qcom: rpmpd: Add Shikra RPM Power Domains
Rosen Penev (1):
pmdomain: mediatek: mfg: move __packed after struct name to fix kernel-doc
Shawn Guo (1):
dt-bindings: power: qcom,rpmhpd: Fix whitespace in RPMHPD defines
Ulf Hansson (2):
pmdomain: Merge branch dt into next
pmdomain: Merge branch fixes into next
Yuanshen Cao (1):
pmdomain: sunxi: support power domain flags for pck600
.../devicetree/bindings/power/power-domain.yaml | 34 ++++
.../devicetree/bindings/power/qcom,rpmpd.yaml | 2 +
drivers/pmdomain/arm/scmi_pm_domain.c | 15 +-
drivers/pmdomain/core.c | 189 +++++++++++++++++++--
drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c | 4 +-
drivers/pmdomain/qcom/Kconfig | 2 +-
drivers/pmdomain/qcom/rpmhpd.c | 35 ++++
drivers/pmdomain/qcom/rpmpd.c | 7 +
drivers/pmdomain/sunxi/sun55i-pck600.c | 31 ++--
include/dt-bindings/power/qcom,rpmhpd.h | 18 +-
include/linux/pm_domain.h | 16 ++
11 files changed, 318 insertions(+), 35 deletions(-)
^ permalink raw reply
* Re: [PATCH] firmware: arm_ffa: Fix NULL dereference in ffa_partition_info_get()
From: Sudeep Holla @ 2026-06-17 9:06 UTC (permalink / raw)
To: Unnathi Chalicheemala
Cc: Jens Wiklander, Sudeep Holla, linux-arm-kernel, linux-kernel,
linux-arm-msm, kernel, Trilok Soni,
Satya Durga Srinivasu Prabhala
In-Reply-To: <eb50b45d-ad04-4da7-8cfa-6e1abb1ffba4@oss.qualcomm.com>
On Tue, Jun 16, 2026 at 02:14:59PM -0700, Unnathi Chalicheemala wrote:
> On 6/12/2026 3:55 AM, Sudeep Holla wrote:
> >
> >> Per the FF-A spec, the all-zeros UUID is the defined wildcard that
> >> instructs the SPMC to return information for all partitions. Map NULL
> >> and empty string to uuid_null rather than crashing in uuid_parse(),
> >> preserving the intended "return all partitions" semantics for callers
> >> that pass NULL.
> >>
> >
> > Agreed on the spec part but not w.r.t the interface. Where is the driver
> > using this call and why is it sending null or wants to extract all the
> > partition information ?
> >
>
> A developer wanting all partitions might reasonably pass the all-zeros string
> "00000000-0000-0000-0000-000000000000"?
I understand that and the core driver does exactly this when initialising
to enumerate all the partitions on the system. But you didn't answer my
question as where is the FF-A client driver pass NULL ? You just expressed
the possibility here.
[...]
> > I object to make it uuid_null. Below check is enough to check NULL
> > dereference.
> >
> > - if (uuid_parse(uuid_str, &uuid)) {
> > + if (!uuid_str || uuid_parse(uuid_str, &uuid)) {
> >
> >
> > I don't think we need to service NULL as valid argument via this interface
> > as the callee driver needs to pass its partition UUID here.
> >
> I agree with you, NULL doesn't seem like a valid use case.
>
> Will send another version with your suggestion, thank you for the review.
>
Thanks!
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH v3] i2c: imx-lpi2c: mark I2C adapter when hardware is powered down
From: Andi Shyti @ 2026-06-17 9:10 UTC (permalink / raw)
To: Carlos Song (OSS)
Cc: aisheng.dong, Frank.Li, s.hauer, kernel, festevam, carlos.song,
linux-i2c, imx, linux-arm-kernel, linux-kernel, stable
In-Reply-To: <20260525031450.3183421-1-carlos.song@oss.nxp.com>
Hi Carlos,
On Mon, May 25, 2026 at 11:14:50AM +0800, Carlos Song (OSS) wrote:
> From: Carlos Song <carlos.song@nxp.com>
>
> On some i.MX platforms, certain I2C client drivers keep a periodic
> workqueue which continues to trigger I2C transfers.
>
> During system suspend/resume, there exists a time window between:
> - suspend_noirq and the system entering suspend
> - the system starting to resume and resume_noirq
>
> In this window, the I2C controller resources such as clock and pinctrl
> may already be disabled or not yet restored.
>
> If a workqueue triggers an I2C transfer in this period, the driver
> attempts to access I2C registers while the hardware resources are
> unavailable, which may lead to system hang.
>
> Mark the I2C adapter as suspended during noirq suspend and block new
> transfers until resume, ensuring that I2C transfers are only issued
> when hardware resources are available.
>
> Fixes: 1ee867e465c1 ("i2c: imx-lpi2c: add target mode support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Carlos Song <carlos.song@nxp.com>
merged to i2c/i2c-host.
Slowly I will check more carefully all your fixes. I'm sorry for
the delay in this period.
Thanks,
Andi
^ permalink raw reply
* Re: [PATCH] i2c: stm32f7: truncate clock period instead of rounding it
From: Guillermo Rodriguez Garcia @ 2026-06-17 9:13 UTC (permalink / raw)
To: Andi Shyti
Cc: Pierre-Yves MORDRET, Alain Volmat, Maxime Coquelin,
Alexandre Torgue, M'boumba Cedric Madianga, Wolfram Sang,
Pierre-Yves MORDRET, linux-i2c, linux-stm32, linux-arm-kernel,
linux-kernel
In-Reply-To: <ajJcxgVejqzlwo9A@zenone.zhora.eu>
Hi,
Thank you everyone.
A possible improvement would be to derive the clock period in
picoseconds rather than ns and do all the timing calculations based on
that.
This would reduce the rounding error (from max 1ns to max 1ps) and
help the solver find TIMINGR values closer to the target bus
frequency, while still keeping both freq and tLOW within spec.
I wanted to keep this patch minimal to address the spec violation
itself, and also to make it easier to backport to stable. The
suggested improvement is a bit more involved, but I wanted to mention
it in case you want to consider it.
Thanks,
Guillermo
El mié, 17 jun 2026 a las 10:38, Andi Shyti (<andi.shyti@kernel.org>) escribió:
>
> Hi Guillermo,
>
> On Thu, Jun 11, 2026 at 12:48:56PM +0200, Guillermo Rodríguez wrote:
> > stm32f7_i2c_compute_timing() derives the I2C clock source period
> > (i2cclk) with DIV_ROUND_CLOSEST, which may round it up. When the
> > period is overestimated, all timings computed from it (SCLDEL,
> > SDADEL, SCLL, SCLH) come out shorter on the wire than calculated,
> > and the resulting bus rate can exceed the requested speed, violating
> > the I2C specification minimums for tLOW and tHIGH.
> >
> > For example, with a 104.45 MHz clock source (e.g. PCLK1, the
> > reset-default I2C clock source on STM32MP1), i2cclk is rounded from
> > 9.574 ns up to 10 ns. Requesting a 400 kHz fast mode bus with
> > 72/27 ns rise/fall times and no analog/digital filters then produces
> > an actual bus rate of 415.6 kHz with tLOW = 1254 ns, violating both
> > the 400 kHz maximum rate and the 1300 ns tLOW minimum of the
> > specification.
> >
> > Truncate the period instead, so that it can only be underestimated.
> > The error then falls on the safe side: the programmed timings come
> > out slightly longer than computed and the bus runs marginally below
> > the target rate (375.3 kHz in the example above) while meeting the
> > specification.
> >
> > i2cbus is left rounded-to-closest: it is only used as the target of
> > the clk_error comparison and is never multiplied into the programmed
> > timings, so nearest rounding remains accurate there.
> >
> > Fixes: aeb068c57214 ("i2c: i2c-stm32f7: add driver")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Guillermo Rodríguez <guille.rodriguez@gmail.com>
>
> Merged to i2c/i2c-host.
>
> Thanks to Alain and Pierre-Yves for their review!
>
> Andi
--
Guillermo Rodriguez Garcia
guille.rodriguez@gmail.com
^ permalink raw reply
* Re: [PATCH net] net: airoha: Fix TX scheduler queue mask loop upper bound
From: Lorenzo Bianconi @ 2026-06-17 9:17 UTC (permalink / raw)
To: Wayen Yan; +Cc: netdev, nbd, linux-arm-kernel, linux-mediatek
In-Reply-To: <178168650178.2224380.3950331731013129336@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2931 bytes --]
> On Tue, Jun 17, 2026, Lorenzo Bianconi wrote:
> > Even if the current codebase supports just AIROHA_NUM_QOS_CHANNEL (4), the hw
> > exposes 32 hw QoS channels (AIROHA_NUM_TX_RING). Here we are just clearing the
> > configuration, so I guess the current implementation is correct.
>
> Hi Lorenzo,
>
> You are right that there is no functional impact, and I agree this
> should not go to net. Let me explain the register layout I was worried
> about, and you can decide whether it is worth a net-next cleanup or
> should just be dropped.
>
> The two macros are:
>
> REG_QUEUE_CLOSE_CFG(_n) = 0x00a0 + ((_n) & 0xfc)
> TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) = BIT((_m) + (((_n) & 0x3) << 3))
>
> REG_QUEUE_CLOSE_CFG() masks the channel with 0xfc, and the bit macro
> folds the channel with & 0x3 (mod 4) shifted by 3. So one 32-bit
> register holds 4 channels x 8 queues, 8 queue bits per channel:
>
> channel 0 -> reg 0x00a0, bits 0..7
> channel 1 -> reg 0x00a0, bits 8..15
> channel 2 -> reg 0x00a0, bits 16..23
> channel 3 -> reg 0x00a0, bits 24..31
> channel 4 -> reg 0x00a4, bits 0..7
> ...
>
> In airoha_qdma_set_chan_tx_sched() the loop variable 'i' is passed as
> the *queue* argument _m, not as a channel:
>
> for (i = 0; i < AIROHA_NUM_TX_RING; i++) // i = 0..31
> airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
> TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
>
> Since each channel only has AIROHA_NUM_QOS_QUEUES (8) queues, the correct
> logic is to clear the 8 queue bits belonging to 'channel'. With i running
> up to 31 the BIT() shift instead walks past those 8 bits and into the bit
> ranges of the other channels folded into the same register. For channel 0
> the accumulated mask becomes 0xffffffff, i.e. it touches channels 1..3 as
> well.
>
> This is harmless today only because REG_QUEUE_CLOSE_CFG is written
> exclusively here, via airoha_qdma_clear() (RMW clear), and the register
> resets to 0 and is never set anywhere -- so clearing extra bits is a
> no-op. Functionally the current code is fine, as you say.
>
> The point is just the loop-bound semantics: 'i' is a per-channel queue
> index, so the bound should be AIROHA_NUM_QOS_QUEUES (8), not
> AIROHA_NUM_TX_RING (32). The two happen to be related (32 == 4 channels *
> 8 queues) but mean different things.
>
> Since there is no functional change, feel free to drop this if you would
> rather not carry a cosmetic patch. If you think the clarity is worth it I
> can resend against net-next without the Fixes tag.
>
> Thanks,
> Wayen
>
Sorry you are right, I misread the code, your patch is correct. Since as you
pointed out REG_QUEUE_CLOSE_CFG() is actually never set at the moment and the
default register value is 0, I would repost this patch for net-next as soon as
it is opened (this will avoid merge conflicts).
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH RFC 8/9] arm64: dts: qcom: shikra-cqs-evk: Enable ethernet0
From: Konrad Dybcio @ 2026-06-17 9:42 UTC (permalink / raw)
To: Mohd Ayaan Anwar
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King, linux-arm-msm, netdev, devicetree,
linux-kernel, linux-stm32, linux-arm-kernel
In-Reply-To: <ajF+xlipLuZtf4HL@oss.qualcomm.com>
On 6/16/26 6:50 PM, Mohd Ayaan Anwar wrote:
> On Tue, Jun 16, 2026 at 11:50:26AM +0200, Konrad Dybcio wrote:
>> On 6/11/26 8:37 PM, Mohd Ayaan Anwar wrote:
>>
>>> +&tlmm {
>>> + ethernet0_defaults: ethernet0-defaults-state {
>>
>> s/defaults/default
>>
>> Please move this definition to shikra.dtsi
>>
>
> The CQM and CQS variants have identical GPIO mapping but the IQS is
> different. So should I keep this in shikra.dtsi and overwrite for IQS in
> shikra-iqs-evk.dts?
>
>
>>> +
>>> + emac0_phy_en_hog: emac0-phy-en-hog {
>>> + gpio-hog;
>>> + gpios = <149 GPIO_ACTIVE_HIGH>;
>>> + output-high;
>>> + line-name = "emac0-phy-en";
>>> + };
>>
>> This looks like a hack - what does this pin actually do?
>>
>
> The power supply to both PHYs on Shikra is gated by a GPIO pin. I am
> unsure whether they should be modelled as a fixed, enable-on-boot
> regulator or just like this. They need to be powered on early so that
> MDIO can detect them.
If it's a regulator, then it should be described as a regulator. There
was some discussion regarding the power resources of PHYs over here:
https://lore.kernel.org/linux-arm-msm/SN7PR19MB67369F7DD02F702437C0F1919D1B2@SN7PR19MB6736.namprd19.prod.outlook.com/
Konrad
^ permalink raw reply
* Re: [PATCH v3 2/4] iio: adc: mt6323-auxadc: add mt6323 PMIC AUXADC driver
From: Andy Shevchenko @ 2026-06-17 9:43 UTC (permalink / raw)
To: rva333
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Lee Jones, linux-iio, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek, Ben Grisdale
In-Reply-To: <20260616-mt6323-adc-v3-2-1c27c588185d@protonmail.com>
On Tue, Jun 16, 2026 at 05:15:40PM +0300, Roman Vivchar via B4 Relay wrote:
> The mt6323 AUXADC is a 15-bit ADC used for system monitoring. This driver
> provides support for reading various channels including battery and
> charger voltages, battery and chip temperature, current sensing and
> accessory detection.
>
> Add a driver for the AUXADC found in the MediaTek mt6323 PMIC.
...
> +static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc)
> +{
> + struct regmap *map = auxadc->regmap;
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(map, MT6323_AUXADC_CON19, &val);
> + if (ret)
> + return ret;
> +
> + /* The ADC is idle. */
> + if (!(val & AUXADC_CON19_DECI_GDLY_MASK))
> + return 0;
> + ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19, val,
> + !(val & AUXADC_ADC19_BUSY_MASK),
> + 10, 500);
Still can be amended.
ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19,
val, !(val & AUXADC_ADC19_BUSY_MASK),
10, 500);
(no need to resend just for this).
> + if (ret)
> + return ret;
> +
> + return regmap_clear_bits(map, MT6323_AUXADC_CON19,
> + AUXADC_CON19_DECI_GDLY_MASK);
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v2 4/4] ARM: dts: mediatek: mt6323: add EFUSE support
From: Roman Vivchar via B4 Relay @ 2026-06-17 9:48 UTC (permalink / raw)
To: Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Srinivas Kandagatla, Roman Vivchar
Cc: Andy Shevchenko, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Ben Grisdale
In-Reply-To: <20260617-mt6323-nvmem-v2-0-4f30e36aa0f4@protonmail.com>
From: Roman Vivchar <rva333@protonmail.com>
Add the devicetree node for the mt6323 efuse.
Tested-by: Ben Grisdale <bengris32@protonmail.ch> # Amazon Echo Dot (2nd Generation)
Signed-off-by: Roman Vivchar <rva333@protonmail.com>
---
arch/arm/boot/dts/mediatek/mt6323.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/mediatek/mt6323.dtsi b/arch/arm/boot/dts/mediatek/mt6323.dtsi
index c230c865116d..807e000a7ff6 100644
--- a/arch/arm/boot/dts/mediatek/mt6323.dtsi
+++ b/arch/arm/boot/dts/mediatek/mt6323.dtsi
@@ -14,6 +14,10 @@ pmic: mt6323 {
interrupt-controller;
#interrupt-cells = <2>;
+ efuse {
+ compatible = "mediatek,mt6323-efuse";
+ };
+
mt6323_leds: leds {
compatible = "mediatek,mt6323-led";
#address-cells = <1>;
--
2.54.0
^ permalink raw reply related
* [PATCH v2 0/4] nvmem: add support for the MediaTek mt6323 PMIC
From: Roman Vivchar via B4 Relay @ 2026-06-17 9:48 UTC (permalink / raw)
To: Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Srinivas Kandagatla, Roman Vivchar
Cc: Andy Shevchenko, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Ben Grisdale, Conor Dooley
This series adds support for the EFUSE found on the MediaTek mt6323 PMIC.
The previous version of the series for all AUXADC, EFUSE and thermal
drivers was split after Krzysztof's comment [1].
Tested on the MediaTek mt6572 and mt8163 SoCs (Ben), both paired with a
mt6323.
[1]: https://lore.kernel.org/linux-mediatek/20260504-mt6323-v1-0-799b58b355ff@protonmail.com/T/#med30fad67a090be35f549231336b2dec295233f6
Tested-by: Ben Grisdale <bengris32@protonmail.ch> # Amazon Echo Dot (2nd Generation)
Signed-off-by: Roman Vivchar <rva333@protonmail.com>
---
Changes in v2:
- EFUSE driver: Sort variables in the mt6323_efuse_read (Andy)
- Link to v1: https://patch.msgid.link/20260611-mt6323-nvmem-v1-0-b5e1b9ce51f2@protonmail.com
Changes after split:
- EFUSE driver:
- Remove 'linux/errno.h' header (Andy)
- Remove explicit cast to u16 in the 'mt6323_efuse_read' (Andy)
- Reword comment in the 'mt6323_efuse_read'
- Capitalize MediaTek in the module description
- Link to a previous series: https://patch.msgid.link/20260512-mt6323-v2-0-3efcba579e88@protonmail.com
---
Roman Vivchar (4):
dt-bindings: mfd: mediatek: mt6397: add mt6323 PMIC EFUSE
nvmem: add mt6323 PMIC EFUSE driver
mfd: mt6397-core: add mt6323 EFUSE support
ARM: dts: mediatek: mt6323: add EFUSE support
.../devicetree/bindings/mfd/mediatek,mt6397.yaml | 21 ++++++
MAINTAINERS | 5 ++
arch/arm/boot/dts/mediatek/mt6323.dtsi | 4 +
drivers/mfd/mt6397-core.c | 3 +
drivers/nvmem/Kconfig | 11 +++
drivers/nvmem/Makefile | 2 +
drivers/nvmem/mt6323-efuse.c | 85 ++++++++++++++++++++++
7 files changed, 131 insertions(+)
---
base-commit: 028ef9c96e96197026887c0f092424679298aae8
change-id: 20260611-mt6323-nvmem-0c54a0f2fa9f
Best regards,
--
Roman Vivchar <rva333@protonmail.com>
^ permalink raw reply
* [PATCH v2 2/4] nvmem: add mt6323 PMIC EFUSE driver
From: Roman Vivchar via B4 Relay @ 2026-06-17 9:48 UTC (permalink / raw)
To: Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Srinivas Kandagatla, Roman Vivchar
Cc: Andy Shevchenko, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Ben Grisdale
In-Reply-To: <20260617-mt6323-nvmem-v2-0-4f30e36aa0f4@protonmail.com>
From: Roman Vivchar <rva333@protonmail.com>
Add support for the EFUSE controller found in the Mediatek MT6323 PMIC.
The MT6323 EFUSE stores 24 bytes of hardware-related data, such as
thermal sensor calibration values.
Tested-by: Ben Grisdale <bengris32@protonmail.ch> # Amazon Echo Dot (2nd Generation)
Reviewed-by: Andy Shevchenko <andy@kernel.org>
Signed-off-by: Roman Vivchar <rva333@protonmail.com>
---
MAINTAINERS | 5 +++
drivers/nvmem/Kconfig | 11 ++++++
drivers/nvmem/Makefile | 2 ++
drivers/nvmem/mt6323-efuse.c | 85 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f..910360f148c4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16256,6 +16256,11 @@ S: Maintained
F: Documentation/devicetree/bindings/mmc/mtk-sd.yaml
F: drivers/mmc/host/mtk-sd.c
+MEDIATEK MT6323 PMIC NVMEM DRIVER
+M: Roman Vivchar <rva333@protonmail.com>
+S: Maintained
+F: drivers/nvmem/mt6323-efuse.c
+
MEDIATEK MT6735 CLOCK & RESET DRIVERS
M: Yassine Oudjana <y.oudjana@protonmail.com>
L: linux-clk@vger.kernel.org
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 74ddbd0f79b0..db248a3c4e87 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -227,6 +227,17 @@ config NVMEM_MTK_EFUSE
This driver can also be built as a module. If so, the module
will be called efuse-mtk.
+config NVMEM_MT6323_EFUSE
+ tristate "Mediatek MT6323 PMIC EFUSE support"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on MFD_MT6397
+ help
+ This is a driver to access hardware related data like sensor
+ calibration, etc.
+
+ This driver can also be built as a module. If so, the module
+ will be called efuse-mt6323.
+
config NVMEM_MXS_OCOTP
tristate "Freescale MXS On-Chip OTP Memory Support"
depends on ARCH_MXS || COMPILE_TEST
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index 7252b8ec88d4..0e2b73f42b25 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -48,6 +48,8 @@ obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
nvmem-microchip-otpc-y := microchip-otpc.o
obj-$(CONFIG_NVMEM_MTK_EFUSE) += nvmem_mtk-efuse.o
nvmem_mtk-efuse-y := mtk-efuse.o
+obj-$(CONFIG_NVMEM_MT6323_EFUSE) += nvmem_mt6323-efuse.o
+nvmem_mt6323-efuse-y := mt6323-efuse.o
obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o
nvmem-mxs-ocotp-y := mxs-ocotp.o
obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o
diff --git a/drivers/nvmem/mt6323-efuse.c b/drivers/nvmem/mt6323-efuse.c
new file mode 100644
index 000000000000..7a0ce4c7f565
--- /dev/null
+++ b/drivers/nvmem/mt6323-efuse.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2026 Roman Vivchar <rva333@protonmail.com>
+ */
+
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <linux/mfd/mt6323/registers.h>
+
+#define MT6323_EFUSE_DOUT_BASE MT6323_EFUSE_DOUT_0_15
+#define MT6323_EFUSE_SIZE 24
+
+static int mt6323_efuse_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct regmap *map = context;
+ u16 *buf = val;
+ u32 tmp;
+ int ret;
+
+ /*
+ * A manual loop using regmap_read is required because PWRAP is not
+ * a continuous MMIO space, but rather a FSM that doesn't implement the
+ * necessary read callback for the regmap_read_raw and regmap_read_bulk
+ * functions.
+ */
+ for (size_t i = 0; i < bytes; i += sizeof(*buf)) {
+ ret = regmap_read(map, MT6323_EFUSE_DOUT_BASE + offset + i, &tmp);
+ if (ret)
+ return ret;
+
+ *buf++ = tmp;
+ }
+
+ return 0;
+}
+
+static int mt6323_efuse_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nvmem_config config = {
+ .name = "mt6323-efuse",
+ .stride = 2,
+ .word_size = 2,
+ .size = MT6323_EFUSE_SIZE,
+ .reg_read = mt6323_efuse_read,
+ };
+ struct nvmem_device *nvmem;
+ struct regmap *regmap;
+
+ /* efuse -> mfd -> pwrap */
+ regmap = dev_get_regmap(dev->parent->parent, NULL);
+ if (!regmap)
+ return dev_err_probe(dev, -ENODEV, "failed to get regmap\n");
+
+ config.dev = dev;
+ config.priv = regmap;
+
+ nvmem = devm_nvmem_register(dev, &config);
+ return PTR_ERR_OR_ZERO(nvmem);
+}
+
+static const struct of_device_id mt6323_efuse_of_match[] = {
+ { .compatible = "mediatek,mt6323-efuse" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt6323_efuse_of_match);
+
+static struct platform_driver mt6323_efuse_driver = {
+ .probe = mt6323_efuse_probe,
+ .driver = {
+ .name = "mt6323-efuse",
+ .of_match_table = mt6323_efuse_of_match,
+ },
+};
+module_platform_driver(mt6323_efuse_driver);
+
+MODULE_DESCRIPTION("MediaTek MT6323 PMIC EFUSE driver");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply related
* [PATCH v2 3/4] mfd: mt6397-core: add mt6323 EFUSE support
From: Roman Vivchar via B4 Relay @ 2026-06-17 9:48 UTC (permalink / raw)
To: Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Srinivas Kandagatla, Roman Vivchar
Cc: Andy Shevchenko, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Ben Grisdale
In-Reply-To: <20260617-mt6323-nvmem-v2-0-4f30e36aa0f4@protonmail.com>
From: Roman Vivchar <rva333@protonmail.com>
The mt6323 PMIC includes an EFUSE. Register the EFUSE in the mt6323
devices array to allow the corresponding driver to probe using compatible
string.
Tested-by: Ben Grisdale <bengris32@protonmail.ch> # Amazon Echo Dot (2nd Generation)
Signed-off-by: Roman Vivchar <rva333@protonmail.com>
---
drivers/mfd/mt6397-core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index 3e58d0764c7e..362737a1c4a9 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -125,6 +125,9 @@ static const struct resource mt6323_pwrc_resources[] = {
static const struct mfd_cell mt6323_devs[] = {
{
+ .name = "mt6323-efuse",
+ .of_compatible = "mediatek,mt6323-efuse",
+ }, {
.name = "mt6323-rtc",
.num_resources = ARRAY_SIZE(mt6323_rtc_resources),
.resources = mt6323_rtc_resources,
--
2.54.0
^ permalink raw reply related
* [PATCH v2 1/4] dt-bindings: mfd: mediatek: mt6397: add mt6323 PMIC EFUSE
From: Roman Vivchar via B4 Relay @ 2026-06-17 9:48 UTC (permalink / raw)
To: Sen Chu, Sean Wang, Macpaul Lin, Lee Jones, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Srinivas Kandagatla, Roman Vivchar
Cc: Andy Shevchenko, linux-pm, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Ben Grisdale, Conor Dooley
In-Reply-To: <20260617-mt6323-nvmem-v2-0-4f30e36aa0f4@protonmail.com>
From: Roman Vivchar <rva333@protonmail.com>
The MediaTek mt6323 PMIC includes an EFUSE used for storing calibration
data.
Add the devicetree binding documentation for the MediaTek mt6323 EFUSE.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Roman Vivchar <rva333@protonmail.com>
---
.../devicetree/bindings/mfd/mediatek,mt6397.yaml | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
index 05c121b0cb3d..beaa67bf0df2 100644
--- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
+++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml
@@ -145,6 +145,23 @@ properties:
required:
- compatible
+ efuse:
+ type: object
+ unevaluatedProperties: false
+ description:
+ The efuse is responsible for storing calibration data, such as thermal
+ sensor calibration.
+
+ properties:
+ compatible:
+ const: mediatek,mt6323-efuse
+
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
+
+ required:
+ - compatible
+
leds:
type: object
additionalProperties: false
@@ -243,6 +260,10 @@ examples:
interrupt-controller;
#interrupt-cells = <2>;
+ efuse {
+ compatible = "mediatek,mt6323-efuse";
+ };
+
leds {
compatible = "mediatek,mt6323-led";
#address-cells = <1>;
--
2.54.0
^ permalink raw reply related
* Re: [PATCH RFC 8/9] arm64: dts: qcom: shikra-cqs-evk: Enable ethernet0
From: Andrew Lunn @ 2026-06-17 9:48 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Mohd Ayaan Anwar, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Richard Cochran, Bjorn Andersson, Konrad Dybcio,
Maxime Coquelin, Alexandre Torgue, Russell King, linux-arm-msm,
netdev, devicetree, linux-kernel, linux-stm32, linux-arm-kernel
In-Reply-To: <4f3c6bee-3ccb-467e-a466-89fece0e6a7f@oss.qualcomm.com>
> >>> + emac0_phy_en_hog: emac0-phy-en-hog {
> >>> + gpio-hog;
> >>> + gpios = <149 GPIO_ACTIVE_HIGH>;
> >>> + output-high;
> >>> + line-name = "emac0-phy-en";
> >>> + };
> >>
> >> This looks like a hack - what does this pin actually do?
> >>
> >
> > The power supply to both PHYs on Shikra is gated by a GPIO pin. I am
> > unsure whether they should be modelled as a fixed, enable-on-boot
> > regulator or just like this. They need to be powered on early so that
> > MDIO can detect them.
>
> If it's a regulator, then it should be described as a regulator.
Agreed.
> There
> was some discussion regarding the power resources of PHYs over here:
>
> https://lore.kernel.org/linux-arm-msm/SN7PR19MB67369F7DD02F702437C0F1919D1B2@SN7PR19MB6736.namprd19.prod.outlook.com/
MDIO detection is nice to have, but only works well on simple
boards. I would suggest hard coding the PHY ID in the compatible.
Andrew
^ permalink raw reply
* Re: [PATCH net] net: airoha: Fix TX scheduler queue mask loop upper bound
From: Wayen Yan @ 2026-06-17 8:55 UTC (permalink / raw)
To: lorenzo; +Cc: netdev, nbd, linux-arm-kernel, linux-mediatek
In-Reply-To: <178166704952.2212140.11002626760717132754@gmail.com>
On Tue, Jun 17, 2026, Lorenzo Bianconi wrote:
> Even if the current codebase supports just AIROHA_NUM_QOS_CHANNEL (4), the hw
> exposes 32 hw QoS channels (AIROHA_NUM_TX_RING). Here we are just clearing the
> configuration, so I guess the current implementation is correct.
Hi Lorenzo,
You are right that there is no functional impact, and I agree this
should not go to net. Let me explain the register layout I was worried
about, and you can decide whether it is worth a net-next cleanup or
should just be dropped.
The two macros are:
REG_QUEUE_CLOSE_CFG(_n) = 0x00a0 + ((_n) & 0xfc)
TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) = BIT((_m) + (((_n) & 0x3) << 3))
REG_QUEUE_CLOSE_CFG() masks the channel with 0xfc, and the bit macro
folds the channel with & 0x3 (mod 4) shifted by 3. So one 32-bit
register holds 4 channels x 8 queues, 8 queue bits per channel:
channel 0 -> reg 0x00a0, bits 0..7
channel 1 -> reg 0x00a0, bits 8..15
channel 2 -> reg 0x00a0, bits 16..23
channel 3 -> reg 0x00a0, bits 24..31
channel 4 -> reg 0x00a4, bits 0..7
...
In airoha_qdma_set_chan_tx_sched() the loop variable 'i' is passed as
the *queue* argument _m, not as a channel:
for (i = 0; i < AIROHA_NUM_TX_RING; i++) // i = 0..31
airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
Since each channel only has AIROHA_NUM_QOS_QUEUES (8) queues, the correct
logic is to clear the 8 queue bits belonging to 'channel'. With i running
up to 31 the BIT() shift instead walks past those 8 bits and into the bit
ranges of the other channels folded into the same register. For channel 0
the accumulated mask becomes 0xffffffff, i.e. it touches channels 1..3 as
well.
This is harmless today only because REG_QUEUE_CLOSE_CFG is written
exclusively here, via airoha_qdma_clear() (RMW clear), and the register
resets to 0 and is never set anywhere -- so clearing extra bits is a
no-op. Functionally the current code is fine, as you say.
The point is just the loop-bound semantics: 'i' is a per-channel queue
index, so the bound should be AIROHA_NUM_QOS_QUEUES (8), not
AIROHA_NUM_TX_RING (32). The two happen to be related (32 == 4 channels *
8 queues) but mean different things.
Since there is no functional change, feel free to drop this if you would
rather not carry a cosmetic patch. If you think the clarity is worth it I
can resend against net-next without the Fixes tag.
Thanks,
Wayen
^ permalink raw reply
* [PATCH] KVM: arm64: Add missing hyp_enter when trapping sysreg
From: Vincent Donnefort @ 2026-06-17 9:52 UTC (permalink / raw)
To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, kernel-team, tabba, Vincent Donnefort
Add a missing hypervisor event call for hyp_enter on sysreg trapping,
causing an unbalanced hyp_enter/hyp_exit.
The enum hyp_enter_exit_reason is not ABI, so we can keep the ERET
reasons at the end for clarity.
Fixes: 696dfec22b8e ("KVM: arm64: Add hyp_enter/hyp_exit events to nVHE/pKVM hyp")
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h
index 743c49bd878f..5f6e6789d121 100644
--- a/arch/arm64/include/asm/kvm_hypevents.h
+++ b/arch/arm64/include/asm/kvm_hypevents.h
@@ -12,6 +12,7 @@
enum hyp_enter_exit_reason {
HYP_REASON_SMC,
HYP_REASON_HVC,
+ HYP_REASON_SYS,
HYP_REASON_PSCI,
HYP_REASON_HOST_ABORT,
HYP_REASON_GUEST_EXIT,
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 06db299c37a8..45a4abb9619d 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -913,6 +913,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt)
handle_host_mem_abort(host_ctxt);
break;
case ESR_ELx_EC_SYS64:
+ trace_hyp_enter(host_ctxt, HYP_REASON_SYS);
if (handle_host_mte(esr))
break;
fallthrough;
diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
index c4b3ee552131..c84434e2349a 100644
--- a/arch/arm64/kvm/hyp_trace.c
+++ b/arch/arm64/kvm/hyp_trace.c
@@ -398,6 +398,7 @@ static const char *__hyp_enter_exit_reason_str(u8 reason)
static const char strs[][12] = {
"smc",
"hvc",
+ "sys",
"psci",
"host_abort",
"guest_exit",
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* Re: [PATCH v4 resend 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: Krzysztof Kozlowski @ 2026-06-17 9:58 UTC (permalink / raw)
To: joakim.zhang
Cc: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang, cix-kernel-upstream, linux-clk, devicetree,
linux-kernel, linux-arm-kernel
In-Reply-To: <20260617064100.1504617-4-joakim.zhang@cixtech.com>
On Wed, Jun 17, 2026 at 02:40:58PM +0800, joakim.zhang@cixtech.com wrote:
> From: Joakim Zhang <joakim.zhang@cixtech.com>
>
> The AUDSS CRU contains an internal clock tree of muxes, dividers and
> gates for DSP, I2S, HDA, DMAC and related blocks. The clock provider is
> a child node of the cix,sky1-audss-system-control syscon and accesses
> registers through the parent MMIO region.
>
> Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 resend 1/5] dt-bindings: soc: cix,sky1-system-control: add audss system control
From: Krzysztof Kozlowski @ 2026-06-17 9:58 UTC (permalink / raw)
To: joakim.zhang
Cc: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang, cix-kernel-upstream, linux-clk, devicetree,
linux-kernel, linux-arm-kernel
In-Reply-To: <20260617064100.1504617-2-joakim.zhang@cixtech.com>
On Wed, Jun 17, 2026 at 02:40:56PM +0800, joakim.zhang@cixtech.com wrote:
> From: Joakim Zhang <joakim.zhang@cixtech.com>
>
> The Cix Sky1 Audio Subsystem (AUDSS) groups audio-related clock, reset
> and control registers in a dedicated CRU block. Software reset lines are
> exposed on the syscon parent via #reset-cells, following the same model
> as the existing Sky1 FCH and S5 system control bindings.
>
> A clock-controller child node is required under the audss syscon. It has
> no reg property of its own and accesses the parent register block for mux,
> divider and gate fields.
>
> The AUDSS is also controlled by one power domain and reset part.
>
> Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
> ---
> .../soc/cix/cix,sky1-system-control.yaml | 48 +++++++++++++++++++
> .../reset/cix,sky1-audss-system-control.h | 25 ++++++++++
> 2 files changed, 73 insertions(+)
> create mode 100644 include/dt-bindings/reset/cix,sky1-audss-system-control.h
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v7 01/13] powercap: Add enable disable control-type
From: Philip Radford @ 2026-06-17 9:58 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
Add functionality to disable or enable the Powercap control-type by writing
directly into sys/class/powercap/arm-scmi/enabled.
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
drivers/powercap/arm_scmi_powercap.c | 155 ++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 1 deletion(-)
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index ab66e9a3b1e2..e33829d5c551 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -33,13 +33,151 @@ struct scmi_powercap_zone {
struct scmi_powercap_root {
unsigned int num_zones;
+ bool enabled;
+ struct list_head node;
struct scmi_powercap_zone *spzones;
struct list_head *registered_zones;
struct list_head scmi_zones;
};
+static LIST_HEAD(scmi_powercap_roots);
+static DEFINE_MUTEX(scmi_powercap_roots_lock);
+
static struct powercap_control_type *scmi_top_pcntrl;
+static bool scmi_powercap_is_control_type_child(const struct scmi_powercap_zone *spz)
+{
+ return spz->registered && !spz->invalid &&
+ spz->info->parent_id == SCMI_POWERCAP_ROOT_ZONE_ID;
+}
+
+static int
+scmi_powercap_read_root_children_enable_state(struct scmi_powercap_root *pr, bool *mode)
+{
+ struct scmi_powercap_zone *spz;
+ bool enabled;
+ int i, ret;
+
+ *mode = true;
+
+ for (i = 0; i < pr->num_zones; i++) {
+ spz = &pr->spzones[i];
+
+ if (!scmi_powercap_is_control_type_child(spz))
+ continue;
+
+ ret = powercap_ops->cap_enable_get(spz->ph, spz->info->id, &enabled);
+ if (ret)
+ return ret;
+
+ if (!enabled) {
+ *mode = false;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int
+scmi_powercap_set_root_children_enable_state(struct scmi_powercap_root *pr, bool enable)
+{
+ struct scmi_powercap_zone *spz;
+ bool *prev_state;
+ int i, ret;
+
+ prev_state = kcalloc(pr->num_zones, sizeof(*prev_state), GFP_KERNEL);
+ if (!prev_state)
+ return -ENOMEM;
+
+ for (i = 0; i < pr->num_zones; i++) {
+ spz = &pr->spzones[i];
+
+ if (!scmi_powercap_is_control_type_child(spz))
+ continue;
+
+ ret = powercap_ops->cap_enable_get(spz->ph, spz->info->id,
+ &prev_state[i]);
+
+ if (ret)
+ goto revert;
+
+ if (prev_state[i] == enable)
+ continue;
+
+ ret = powercap_ops->cap_enable_set(spz->ph, spz->info->id, enable);
+ if (ret)
+ goto revert;
+ }
+
+ pr->enabled = enable;
+ kfree(prev_state);
+ return 0;
+
+revert:
+ while (--i >= 0) {
+ spz = &pr->spzones[i];
+
+ if (!scmi_powercap_is_control_type_child(spz))
+ continue;
+ if (!spz->info->powercap_cap_config)
+ continue;
+ if (prev_state[i] == enable)
+ continue;
+
+ powercap_ops->cap_enable_set(spz->ph, spz->info->id, prev_state[i]);
+ }
+
+ kfree(prev_state);
+ return ret;
+}
+
+static int
+scmi_powercap_control_type_set_enable(struct powercap_control_type *pct, bool mode)
+{
+ struct scmi_powercap_root *pr;
+ int ret = 0;
+
+ mutex_lock(&scmi_powercap_roots_lock);
+ list_for_each_entry(pr, &scmi_powercap_roots, node) {
+ ret = scmi_powercap_set_root_children_enable_state(pr, mode);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&scmi_powercap_roots_lock);
+
+ return ret;
+}
+
+static int
+scmi_powercap_control_type_get_enable(struct powercap_control_type *pct, bool *mode)
+{
+ struct scmi_powercap_root *pr;
+ int ret = 0;
+
+ *mode = true;
+
+ mutex_lock(&scmi_powercap_roots_lock);
+ list_for_each_entry(pr, &scmi_powercap_roots, node) {
+ ret = scmi_powercap_read_root_children_enable_state(pr, &pr->enabled);
+
+ if (ret)
+ break;
+ if (!pr->enabled) {
+ *mode = false;
+ break;
+ }
+ }
+ mutex_unlock(&scmi_powercap_roots_lock);
+
+ return ret;
+}
+
+static const struct powercap_control_type_ops scmi_powercap_control_type_ops = {
+ .set_enable = scmi_powercap_control_type_set_enable,
+ .get_enable = scmi_powercap_control_type_get_enable,
+};
+
static int scmi_powercap_zone_release(struct powercap_zone *pz)
{
return 0;
@@ -495,6 +633,16 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
if (ret)
return ret;
+ INIT_LIST_HEAD(&pr->node);
+
+ ret = scmi_powercap_read_root_children_enable_state(pr, &pr->enabled);
+ if (ret)
+ return ret;
+
+ mutex_lock(&scmi_powercap_roots_lock);
+ list_add_tail(&pr->node, &scmi_powercap_roots);
+ mutex_unlock(&scmi_powercap_roots_lock);
+
dev_set_drvdata(dev, pr);
return ret;
@@ -505,6 +653,10 @@ static void scmi_powercap_remove(struct scmi_device *sdev)
struct device *dev = &sdev->dev;
struct scmi_powercap_root *pr = dev_get_drvdata(dev);
+ mutex_lock(&scmi_powercap_roots_lock);
+ list_del(&pr->node);
+ mutex_unlock(&scmi_powercap_roots_lock);
+
scmi_powercap_unregister_all_zones(pr);
}
@@ -525,7 +677,8 @@ static int __init scmi_powercap_init(void)
{
int ret;
- scmi_top_pcntrl = powercap_register_control_type(NULL, "arm-scmi", NULL);
+ scmi_top_pcntrl = powercap_register_control_type(NULL, "arm-scmi",
+ &scmi_powercap_control_type_ops);
if (IS_ERR(scmi_top_pcntrl))
return PTR_ERR(scmi_top_pcntrl);
--
2.47.3
^ permalink raw reply related
* [PATCH v7 00/13] Add support for SCMIv4.0 Powercap Extensions
From: Philip Radford @ 2026-06-17 9:58 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Philip Radford
Hi all,
I will be taking over this series from Cristian and in doing so I have
addressed a couple of issues raised by the first version and added six
additional patches since Cristian's original series:
[1/13] addresses an omission from the original powercap functionality. The
ABI documentation describes allowing enabling and disabling power capping
for a control type via /sys/class/powercap/<control type>/enabled. This
first patch implements this functionality but as stated before, this was an
omission rather than a bug that requires fixing. Disabling the control type
disables any immediate children of the control type.
The logic for this patch is based on the logic I was using for enabling and
disabling the synthetic node in the final patch of this series. As a result,
the synthetic node enable/disable patch has now been significantly reduced.
[7/13] exposes the measurement averaging interval (MAI) value when the
agent has registered to receive power measurement change notifications
for the power capping domain.
[10/13] adds MAI get and set support for the powercap protocol.
[11/13] introduces a synthetic root zone to act as a common parent for all
top-level domains.
[12/13] adds get_power_uw to synthetic root zone, summing the per-zone
power of immediate child zones.
[13/13] adds enable/disable functionality to synthetic root zone to
enable/disable immediate children.
The original series was based on v6.17-rc1 whereas this version has been
based on v7.1-rc7.
The rest of Cristian's series is explained below;
SCMIv4.0 [1] introduces some new features and commands into the Powercap
protocol. In a nutshell, such protocol changes add support for:
- setting multiple powercap limit/interval constraints for each SCMI
powercap domain which supports multiple Concurrent Power Limit
- enabling more Powercap commands to use Fastchannels mechanism
- adding multiple constraints support to the existing notifications
After a bit of needed updates in the SCMI core this series adds:
- support for the idea of optional multiple Concurrent Power Limit (CPLs)
- support for the new FCs
- support for extended notifications
- enable usage of such multiple constraint in the ARM SCMI Powercap driver
Note that the public SCMIv4.0 spec at [1] is currently still BETA0, so
this series could anyway need some minor rework along the way and
definitely will need to wait for a final public release before being
possibly merged.
Tested on a single and multi-instance scenario on an emulated setup
implementing the new protocol extensions.
Based on v7.1-rc7.
Thanks,
Phil
---
V6->V7
- Added control type enable/disable patch
- Refactored Synthetic zone enable disable patch
- Corrected naming of define V2/V3 payload sizes
- Prevent possible cpli[0] out-of-bounds access
- Added cleanup for zones during scmi_powercap_probe
- Prevent instance_root_get_enable reporting a stale state
- Rebased on v7.1-rc7
V5->V6
- Re-worded existing comment for POWERCAP_MEASUREMENTS_NOTIFY
- Added define for V2/V3 payload sizes
- Used new definitions for payload sizes
- Fixed comment length
- Changed warning message warning
- Fixed line lengths and alignment
- Updated docs for new fields
- Amended omission of spz initialization when unregistering powercap zones
- Tested unloading and loading powercap module
- Re-wrote commit message
- Added use of to_scmi_powercap_root macro
- Changed instance_root_set_enable_state to bail out on any error
V4->V5
- Added enable/disable functionality to synthetic node
- Rebased on v7.1-rc1
V3->V4
- Rebased on v7.0-rc4
- Added sythentic parent node and functionality
- Moved fastchannel inits outside of loop
- Renamed arguments for consistency
V2->V3
- Added powercap MAI get/set support
V1->V2
- Rebased on sudeep/for-next/scmi/updates
- Amended Copyright to include 2026
- Added patch to extend powercap report to include MAI
- Removed creation of pi powercap_info struct due to legacy code change
- Amended references to pi->version and similar, which were based on
legacy code
- Wrapped two variables in le32_to_cpu() to appease Sparse warnings
- Amended comparing operator value in response to feedback
Cristian Marussi (7):
firmware: arm_scmi: Add an optional custom parameter to fastchannel
helpers
firmware: arm_scmi: Refactor powercap domain layout
firmware: arm_scmi: Add SCMIv4.0 Powercap basic support
firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support
firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support
include: trace: Add new parameter to trace_scmi_fc_call
powercap: arm_scmi: Enable multiple constraints support
Philip Radford (6):
powercap: Add enable disable control-type
firmware: arm_scmi: Extend powercap report to include MAI
firmware: arm_scmi: add Powercap MAI get/set support
powercap: arm_scmi: Create synthetic parent node for multi-instance
powercap: arm_scmi: Add get_power_uw to synthetic node
powercap: arm_scmi: Synthetic zone enable/disable
drivers/firmware/arm_scmi/driver.c | 12 +-
drivers/firmware/arm_scmi/perf.c | 16 +-
drivers/firmware/arm_scmi/powercap.c | 1001 ++++++++++++++++++++-----
drivers/firmware/arm_scmi/protocols.h | 2 +-
drivers/powercap/arm_scmi_powercap.c | 355 ++++++++-
include/linux/scmi_protocol.h | 97 ++-
include/trace/events/scmi.h | 12 +-
7 files changed, 1214 insertions(+), 281 deletions(-)
--
2.47.3
^ permalink raw reply
* [PATCH v7 02/13] firmware: arm_scmi: Add an optional custom parameter to fastchannel helpers
From: Philip Radford @ 2026-06-17 9:58 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
Starting from SCMIv4.0 the protocols DESCRIBE_FASTCHANNEL commands allow
to specify one additional per-protocol custom field in the outgoing message
request in order to, optionally, further narrow down the scope of the
fastchannel discovery request; the related message-reply format is instead
unchanged.
Add an optional custom protocol parameter to the common fastchannel helper
so as to enable the caller to choose the kind of message to send based on
the detected protocol version.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
drivers/firmware/arm_scmi/driver.c | 12 ++++++++++--
drivers/firmware/arm_scmi/perf.c | 8 ++++----
drivers/firmware/arm_scmi/powercap.c | 8 ++++----
drivers/firmware/arm_scmi/protocols.h | 2 +-
4 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index f247e19cff87..53ae4cfa10b1 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1872,6 +1872,11 @@ static int scmi_iterator_run(void *iter)
struct scmi_msg_get_fc_info {
__le32 domain;
__le32 message_id;
+ __le32 custom;
+#define MSG_FC_INFO_SZ_EXTENDED \
+ (sizeof(struct scmi_msg_get_fc_info))
+#define MSG_FC_INFO_SZ \
+ (sizeof(struct scmi_msg_get_fc_info) - sizeof(__le32))
};
struct scmi_msg_resp_desc_fc {
@@ -1900,7 +1905,7 @@ struct scmi_msg_resp_desc_fc {
static void
scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
u8 describe_id, u32 message_id, u32 valid_size,
- u32 domain, void __iomem **p_addr,
+ u32 domain, u32 *custom, void __iomem **p_addr,
struct scmi_fc_db_info **p_db, u32 *rate_limit)
{
int ret;
@@ -1931,13 +1936,16 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
}
ret = ph->xops->xfer_get_init(ph, describe_id,
- sizeof(*info), sizeof(*resp), &t);
+ custom ? MSG_FC_INFO_SZ_EXTENDED :
+ MSG_FC_INFO_SZ, sizeof(*resp), &t);
if (ret)
goto err_out;
info = t->tx.buf;
info->domain = cpu_to_le32(domain);
info->message_id = cpu_to_le32(message_id);
+ if (custom)
+ info->custom = cpu_to_le32(*custom);
/*
* Bail out on error leaving fc_info addresses zeroed; this includes
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index 4583d02bee1c..7f283f457e02 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -835,25 +835,25 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
return;
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LEVEL_GET, 4, dom->id,
+ PERF_LEVEL_GET, 4, dom->id, NULL,
&fc[PERF_FC_LEVEL].get_addr, NULL,
&fc[PERF_FC_LEVEL].rate_limit);
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LIMITS_GET, 8, dom->id,
+ PERF_LIMITS_GET, 8, dom->id, NULL,
&fc[PERF_FC_LIMIT].get_addr, NULL,
&fc[PERF_FC_LIMIT].rate_limit);
if (dom->info.set_perf)
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LEVEL_SET, 4, dom->id,
+ PERF_LEVEL_SET, 4, dom->id, NULL,
&fc[PERF_FC_LEVEL].set_addr,
&fc[PERF_FC_LEVEL].set_db,
&fc[PERF_FC_LEVEL].rate_limit);
if (dom->set_limits)
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LIMITS_SET, 8, dom->id,
+ PERF_LIMITS_SET, 8, dom->id, NULL,
&fc[PERF_FC_LIMIT].set_addr,
&fc[PERF_FC_LIMIT].set_db,
&fc[PERF_FC_LIMIT].rate_limit);
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index ab9733f4458b..22aff71c75e9 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -716,24 +716,24 @@ static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
return;
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_CAP_SET, 4, domain,
+ POWERCAP_CAP_SET, 4, domain, NULL,
&fc[POWERCAP_FC_CAP].set_addr,
&fc[POWERCAP_FC_CAP].set_db,
&fc[POWERCAP_FC_CAP].rate_limit);
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_CAP_GET, 4, domain,
+ POWERCAP_CAP_GET, 4, domain, NULL,
&fc[POWERCAP_FC_CAP].get_addr, NULL,
&fc[POWERCAP_FC_CAP].rate_limit);
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_PAI_SET, 4, domain,
+ POWERCAP_PAI_SET, 4, domain, NULL,
&fc[POWERCAP_FC_PAI].set_addr,
&fc[POWERCAP_FC_PAI].set_db,
&fc[POWERCAP_FC_PAI].rate_limit);
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_PAI_GET, 4, domain,
+ POWERCAP_PAI_GET, 4, domain, NULL,
&fc[POWERCAP_FC_PAI].get_addr, NULL,
&fc[POWERCAP_FC_PAI].rate_limit);
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index f51245aca259..e618d3141c95 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -280,7 +280,7 @@ struct scmi_proto_helpers_ops {
u32 message_id, u32 *attributes);
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
u8 describe_id, u32 message_id,
- u32 valid_size, u32 domain,
+ u32 valid_size, u32 domain, u32 *custom,
void __iomem **p_addr,
struct scmi_fc_db_info **p_db,
u32 *rate_limit);
--
2.47.3
^ permalink raw reply related
* [PATCH v7 03/13] firmware: arm_scmi: Refactor powercap domain layout
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
SCMIv4.0 introduces the idea of an optional Concurrent Power Limit (CPL)
for each powercap domain, where CPL0 coincides with the one and only
per-domain constraint limit that was available in pre-v4.0 SCMI Powercap.
Refactor the powercap domain descriptors and powercap operations to allow
future v4.0 extensions to cope with multiple CPLs.
While at that generalize the powercap protocol API to drop PAI references
in favour of a more generic avg_ivl naming, since from v4.0 the number and
types of averaging intervals will change in a non-backward compatible way,
so let's bury these changes within the protocol layer.
Last but not least, make the necessary changes to the ARM SCMI Powwercap
driver in order to support all of these new capabilities.
No functional change.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
[Philip: Adjusted domain_id comparitor in scmi_powercap_pai_get]
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
drivers/firmware/arm_scmi/powercap.c | 182 +++++++++++++++++----------
drivers/powercap/arm_scmi_powercap.c | 52 ++++----
include/linux/scmi_protocol.h | 74 +++++++----
3 files changed, 189 insertions(+), 119 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 22aff71c75e9..47aa6dde4a52 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -2,7 +2,7 @@
/*
* System Control and Management Interface (SCMI) Powercap Protocol
*
- * Copyright (C) 2022 ARM Ltd.
+ * Copyright (C) 2022-2026 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
@@ -20,6 +20,8 @@
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+#define CPL0 0
+
enum scmi_powercap_protocol_cmd {
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
POWERCAP_CAP_GET = 0x4,
@@ -192,27 +194,26 @@ scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
static int
scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
- struct powercap_info *pinfo, u32 domain)
+ struct powercap_info *pinfo,
+ struct scmi_powercap_info *dom_info)
{
int ret;
u32 flags;
struct scmi_xfer *t;
- struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
struct scmi_msg_resp_powercap_domain_attributes *resp;
ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
- sizeof(domain), sizeof(*resp), &t);
+ sizeof(dom_info->id), sizeof(*resp), &t);
if (ret)
return ret;
- put_unaligned_le32(domain, t->tx.buf);
+ put_unaligned_le32(dom_info->id, t->tx.buf);
resp = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
flags = le32_to_cpu(resp->attributes);
- dom_info->id = domain;
if (pinfo->notify_cap_cmd)
dom_info->notify_powercap_cap_change =
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
@@ -221,12 +222,9 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
dom_info->async_powercap_cap_set =
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
- dom_info->powercap_cap_config =
- SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
+
dom_info->powercap_monitoring =
SUPPORTS_POWERCAP_MONITORING(flags);
- dom_info->powercap_pai_config =
- SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
dom_info->powercap_scale_mw =
SUPPORTS_POWER_UNITS_MW(flags);
dom_info->powercap_scale_uw =
@@ -236,13 +234,29 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
- dom_info->min_pai = le32_to_cpu(resp->min_pai);
- dom_info->max_pai = le32_to_cpu(resp->max_pai);
- dom_info->pai_step = le32_to_cpu(resp->pai_step);
- ret = scmi_powercap_validate(dom_info->min_pai,
- dom_info->max_pai,
- dom_info->pai_step,
- dom_info->powercap_pai_config);
+ dom_info->sustainable_power =
+ le32_to_cpu(resp->sustainable_power);
+ dom_info->accuracy = le32_to_cpu(resp->accuracy);
+
+ dom_info->parent_id = le32_to_cpu(resp->parent_id);
+ if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
+ (dom_info->parent_id >= pinfo->num_domains ||
+ dom_info->parent_id == dom_info->id)) {
+ dev_err(ph->dev,
+ "Platform reported inconsistent parent ID for domain %d - %s\n",
+ dom_info->id, dom_info->name);
+ ret = -ENODEV;
+ }
+
+ dom_info->cpli[0].avg_ivl_config =
+ SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
+ dom_info->cpli[0].min_avg_ivl = le32_to_cpu(resp->min_pai);
+ dom_info->cpli[0].max_avg_ivl = le32_to_cpu(resp->max_pai);
+ dom_info->cpli[0].avg_ivl_step = le32_to_cpu(resp->pai_step);
+ ret = scmi_powercap_validate(dom_info->cpli[0].min_avg_ivl,
+ dom_info->cpli[0].max_avg_ivl,
+ dom_info->cpli[0].avg_ivl_step,
+ dom_info->cpli[0].avg_ivl_config);
if (ret) {
dev_err(ph->dev,
"Platform reported inconsistent PAI config for domain %d - %s\n",
@@ -250,13 +264,15 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
goto clean;
}
- dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
- dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
- dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
- ret = scmi_powercap_validate(dom_info->min_power_cap,
- dom_info->max_power_cap,
- dom_info->power_cap_step,
- dom_info->powercap_cap_config);
+ dom_info->cpli[0].cap_config =
+ SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
+ dom_info->cpli[0].min_power_cap = le32_to_cpu(resp->min_power_cap);
+ dom_info->cpli[0].max_power_cap = le32_to_cpu(resp->max_power_cap);
+ dom_info->cpli[0].power_cap_step = le32_to_cpu(resp->power_cap_step);
+ ret = scmi_powercap_validate(dom_info->cpli[0].min_power_cap,
+ dom_info->cpli[0].max_power_cap,
+ dom_info->cpli[0].power_cap_step,
+ dom_info->cpli[0].cap_config);
if (ret) {
dev_err(ph->dev,
"Platform reported inconsistent CAP config for domain %d - %s\n",
@@ -264,19 +280,9 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
goto clean;
}
- dom_info->sustainable_power =
- le32_to_cpu(resp->sustainable_power);
- dom_info->accuracy = le32_to_cpu(resp->accuracy);
-
- dom_info->parent_id = le32_to_cpu(resp->parent_id);
- if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
- (dom_info->parent_id >= pinfo->num_domains ||
- dom_info->parent_id == dom_info->id)) {
- dev_err(ph->dev,
- "Platform reported inconsistent parent ID for domain %d - %s\n",
- dom_info->id, dom_info->name);
- ret = -ENODEV;
- }
+ /* Just using same short name */
+ strscpy(dom_info->cpli[0].name, dom_info->name,
+ SCMI_SHORT_NAME_MAX_SIZE);
}
clean:
@@ -288,12 +294,30 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
*/
if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
- domain, NULL, dom_info->name,
+ dom_info->id, NULL, dom_info->name,
SCMI_MAX_STR_SIZE);
return ret;
}
+static int
+scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph,
+ struct powercap_info *pinfo, u32 domain)
+{
+ struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
+
+ dom_info->num_cpli = 1;
+ dom_info->cpli = devm_kcalloc(ph->dev, dom_info->num_cpli,
+ sizeof(*dom_info->cpli), GFP_KERNEL);
+ if (!dom_info->cpli)
+ return -ENOMEM;
+
+ dom_info->id = domain;
+ dom_info->cpli[0].id = CPL0;
+
+ return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info);
+}
+
static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
{
struct powercap_info *pi = ph->get_priv(ph);
@@ -335,10 +359,11 @@ static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_info *dom,
- u32 *power_cap)
+ u32 cpl_id, u32 *power_cap)
{
- if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
- *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
+ if (dom->cpli[cpl_id].fc_info &&
+ dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr) {
+ *power_cap = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].get_addr);
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
dom->id, *power_cap, 0);
return 0;
@@ -348,7 +373,7 @@ static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
}
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *power_cap)
+ u32 domain_id, u32 cpl_id, u32 *power_cap)
{
const struct scmi_powercap_info *dom;
@@ -359,12 +384,13 @@ static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
if (!dom)
return -EINVAL;
- return __scmi_powercap_cap_get(ph, dom, power_cap);
+ return __scmi_powercap_cap_get(ph, dom, cpl_id, power_cap);
}
static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_info *pc,
- u32 power_cap, bool ignore_dresp)
+ u32 cpl_id, u32 power_cap,
+ bool ignore_dresp)
{
int ret;
struct scmi_xfer *t;
@@ -406,21 +432,23 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
struct powercap_info *pi, u32 domain_id,
- u32 power_cap, bool ignore_dresp)
+ u32 cpl_id, u32 power_cap, bool ignore_dresp)
{
int ret = -EINVAL;
const struct scmi_powercap_info *pc;
pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_cap_config)
+ if (!pc || !pc->cpli[cpl_id].cap_config)
return ret;
if (power_cap &&
- (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
+ (power_cap < pc->cpli[cpl_id].min_power_cap ||
+ power_cap > pc->cpli[cpl_id].max_power_cap))
return ret;
- if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
- struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
+ if (pc->cpli[cpl_id].fc_info &&
+ pc->cpli[cpl_id].fc_info[POWERCAP_FC_CAP].set_addr) {
+ struct scmi_fc_info *fci = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_CAP];
iowrite32(power_cap, fci->set_addr);
ph->hops->fastchannel_db_ring(fci->set_db);
@@ -428,7 +456,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
domain_id, power_cap, 0);
ret = 0;
} else {
- ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
+ ret = scmi_powercap_xfer_cap_set(ph, pc, cpl_id, power_cap,
ignore_dresp);
}
@@ -440,7 +468,7 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
}
static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 power_cap,
+ u32 domain_id, u32 cpl_id, u32 power_cap,
bool ignore_dresp)
{
struct powercap_info *pi = ph->get_priv(ph);
@@ -459,7 +487,7 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
return 0;
}
- return __scmi_powercap_cap_set(ph, pi, domain_id,
+ return __scmi_powercap_cap_set(ph, pi, domain_id, cpl_id,
power_cap, ignore_dresp);
}
@@ -485,7 +513,7 @@ static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
}
static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *pai)
+ u32 domain_id, u32 cpl_id, u32 *pai)
{
struct scmi_powercap_info *dom;
struct powercap_info *pi = ph->get_priv(ph);
@@ -494,8 +522,11 @@ static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
return -EINVAL;
dom = pi->powercaps + domain_id;
- if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
- *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
+ if (cpl_id >= dom->num_cpli)
+ return -EINVAL;
+
+ if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr) {
+ *pai = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr);
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
domain_id, *pai, 0);
return 0;
@@ -504,6 +535,12 @@ static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
}
+static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *val)
+{
+ return scmi_powercap_pai_get(ph, domain_id, cpl_id, val);
+}
+
static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
u32 domain_id, u32 pai)
{
@@ -528,17 +565,18 @@ static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
}
static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 pai)
+ u32 domain_id, u32 cpl_id, u32 pai)
{
const struct scmi_powercap_info *pc;
pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_pai_config || !pai ||
- pai < pc->min_pai || pai > pc->max_pai)
+ if (!pc || cpl_id >= pc->num_cpli || !pc->cpli[cpl_id].avg_ivl_config ||
+ !pai || pai < pc->cpli[cpl_id].min_avg_ivl ||
+ pai > pc->cpli[cpl_id].max_avg_ivl)
return -EINVAL;
- if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
- struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
+ if (pc->cpli[cpl_id].fc_info && pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].set_addr) {
+ struct scmi_fc_info *fci = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI];
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
domain_id, pai, 0);
@@ -550,6 +588,12 @@ static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
}
+static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 val)
+{
+ return scmi_powercap_pai_set(ph, domain_id, cpl_id, val);
+}
+
static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
u32 domain_id, u32 *average_power,
u32 *pai)
@@ -645,11 +689,11 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
if (!pi->states[domain_id].last_pcap)
return -EINVAL;
- ret = __scmi_powercap_cap_set(ph, pi, domain_id,
+ ret = __scmi_powercap_cap_set(ph, pi, domain_id, CPL0,
pi->states[domain_id].last_pcap,
true);
} else {
- ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
+ ret = __scmi_powercap_cap_set(ph, pi, domain_id, CPL0, 0, true);
}
if (ret)
@@ -660,7 +704,7 @@ static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
* server could have ignored a disable request and kept enforcing some
* powercap limit requested by other agents.
*/
- ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
+ ret = scmi_powercap_cap_get(ph, domain_id, CPL0, &power_cap);
if (!ret)
pi->states[domain_id].enabled = !!power_cap;
@@ -682,7 +726,7 @@ static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
* Report always real platform state; platform could have ignored
* a previous disable request. Default true on any error.
*/
- ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
+ ret = scmi_powercap_cap_get(ph, domain_id, CPL0, &power_cap);
if (!ret)
*enable = !!power_cap;
@@ -699,8 +743,8 @@ static const struct scmi_powercap_proto_ops powercap_proto_ops = {
.cap_set = scmi_powercap_cap_set,
.cap_enable_set = scmi_powercap_cap_enable_set,
.cap_enable_get = scmi_powercap_cap_enable_get,
- .pai_get = scmi_powercap_pai_get,
- .pai_set = scmi_powercap_pai_set,
+ .avg_interval_get = scmi_powercap_avg_interval_get,
+ .avg_interval_set = scmi_powercap_avg_interval_set,
.measurements_get = scmi_powercap_measurements_get,
.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
@@ -991,18 +1035,18 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
* formed and correlated by sane parent-child relationship (if any).
*/
for (domain = 0; domain < pinfo->num_domains; domain++) {
- ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
+ ret = scmi_powercap_domain_initialize(ph, pinfo, domain);
if (ret)
return ret;
if (pinfo->powercaps[domain].fastchannels)
scmi_powercap_domain_init_fc(ph, domain,
- &pinfo->powercaps[domain].fc_info);
+ &pinfo->powercaps[domain].cpli[CPL0].fc_info);
/* Grab initial state when disable is supported. */
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
ret = __scmi_powercap_cap_get(ph,
- &pinfo->powercaps[domain],
+ &pinfo->powercaps[domain], CPL0,
&pinfo->states[domain].last_pcap);
if (ret)
return ret;
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index e33829d5c551..90d1fa70b1d4 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -120,7 +120,7 @@ scmi_powercap_set_root_children_enable_state(struct scmi_powercap_root *pr, bool
if (!scmi_powercap_is_control_type_child(spz))
continue;
- if (!spz->info->powercap_cap_config)
+ if (!spz->info->cpli[0].cap_config)
continue;
if (prev_state[i] == enable)
continue;
@@ -235,7 +235,7 @@ static const struct powercap_zone_ops zone_ops = {
};
static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *spz,
- u64 power_limit_uw, u32 *norm)
+ u64 power_limit_uw, int cid, u32 *norm)
{
bool scale_mw = spz->info->powercap_scale_mw;
u64 val;
@@ -246,9 +246,9 @@ static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *spz,
* the range [min_power_cap, max_power_cap] whose bounds are assured to
* be two unsigned 32bits quantities.
*/
- *norm = clamp_t(u32, val, spz->info->min_power_cap,
- spz->info->max_power_cap);
- *norm = rounddown(*norm, spz->info->power_cap_step);
+ *norm = clamp_t(u32, val, spz->info->cpli[cid].min_power_cap,
+ spz->info->cpli[cid].max_power_cap);
+ *norm = rounddown(*norm, spz->info->cpli[cid].power_cap_step);
val = (scale_mw) ? *norm * 1000 : *norm;
if (power_limit_uw != val)
@@ -263,12 +263,12 @@ static int scmi_powercap_set_power_limit_uw(struct powercap_zone *pz, int cid,
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
u32 norm_power;
- if (!spz->info->powercap_cap_config)
+ if (!spz->info->cpli[cid].cap_config)
return -EINVAL;
- scmi_powercap_normalize_cap(spz, power_uw, &norm_power);
+ scmi_powercap_normalize_cap(spz, power_uw, cid, &norm_power);
- return powercap_ops->cap_set(spz->ph, spz->info->id, norm_power, false);
+ return powercap_ops->cap_set(spz->ph, spz->info->id, cid, norm_power, false);
}
static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid,
@@ -278,7 +278,7 @@ static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid,
u32 power;
int ret;
- ret = powercap_ops->cap_get(spz->ph, spz->info->id, &power);
+ ret = powercap_ops->cap_get(spz->ph, spz->info->id, cid, &power);
if (ret)
return ret;
@@ -290,19 +290,20 @@ static int scmi_powercap_get_power_limit_uw(struct powercap_zone *pz, int cid,
}
static void scmi_powercap_normalize_time(const struct scmi_powercap_zone *spz,
- u64 time_us, u32 *norm)
+ u64 time_us, int cid, u32 *norm)
{
/*
* This cast is lossless since here @time_us is certain to be within the
- * range [min_pai, max_pai] whose bounds are assured to be two unsigned
- * 32bits quantities.
+ * range [min_avg_ivl, max_avg_ivl] whose bounds are assured to be two
+ * unsigned 32bits quantities.
*/
- *norm = clamp_t(u32, time_us, spz->info->min_pai, spz->info->max_pai);
- *norm = rounddown(*norm, spz->info->pai_step);
+ *norm = clamp_t(u32, time_us, spz->info->cpli[cid].min_avg_ivl,
+ spz->info->cpli[cid].max_avg_ivl);
+ *norm = rounddown(*norm, spz->info->cpli[cid].avg_ivl_step);
if (time_us != *norm)
dev_dbg(spz->dev,
- "Normalized %s:PAI - requested:%llu - normalized:%u\n",
+ "Normalized %s:AVG_IVL - requested:%llu - normalized:%u\n",
spz->info->name, time_us, *norm);
}
@@ -312,12 +313,13 @@ static int scmi_powercap_set_time_window_us(struct powercap_zone *pz, int cid,
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
u32 norm_pai;
- if (!spz->info->powercap_pai_config)
+ if (!spz->info->cpli[cid].avg_ivl_config)
return -EINVAL;
- scmi_powercap_normalize_time(spz, time_window_us, &norm_pai);
+ scmi_powercap_normalize_time(spz, time_window_us, cid, &norm_pai);
- return powercap_ops->pai_set(spz->ph, spz->info->id, norm_pai);
+ return powercap_ops->avg_interval_set(spz->ph, spz->info->id,
+ cid, norm_pai);
}
static int scmi_powercap_get_time_window_us(struct powercap_zone *pz, int cid,
@@ -327,7 +329,7 @@ static int scmi_powercap_get_time_window_us(struct powercap_zone *pz, int cid,
int ret;
u32 pai;
- ret = powercap_ops->pai_get(spz->ph, spz->info->id, &pai);
+ ret = powercap_ops->avg_interval_get(spz->ph, spz->info->id, cid, &pai);
if (ret)
return ret;
@@ -341,7 +343,7 @@ static int scmi_powercap_get_max_power_uw(struct powercap_zone *pz, int cid,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *max_power_uw = spz->info->max_power_cap;
+ *max_power_uw = spz->info->cpli[cid].max_power_cap;
if (spz->info->powercap_scale_mw)
*max_power_uw *= 1000;
@@ -353,7 +355,7 @@ static int scmi_powercap_get_min_power_uw(struct powercap_zone *pz, int cid,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *min_power_uw = spz->info->min_power_cap;
+ *min_power_uw = spz->info->cpli[cid].min_power_cap;
if (spz->info->powercap_scale_mw)
*min_power_uw *= 1000;
@@ -365,7 +367,7 @@ static int scmi_powercap_get_max_time_window_us(struct powercap_zone *pz,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *time_window_us = spz->info->max_pai;
+ *time_window_us = spz->info->cpli[cid].max_avg_ivl;
return 0;
}
@@ -375,14 +377,16 @@ static int scmi_powercap_get_min_time_window_us(struct powercap_zone *pz,
{
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
- *time_window_us = (u64)spz->info->min_pai;
+ *time_window_us = (u64)spz->info->cpli[cid].min_avg_ivl;
return 0;
}
static const char *scmi_powercap_get_name(struct powercap_zone *pz, int cid)
{
- return "SCMI power-cap";
+ struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
+
+ return spz->info->cpli[cid].name;
}
static const struct powercap_zone_constraint_ops constraint_ops = {
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index aafaac1496b0..9918fb30100c 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -2,7 +2,7 @@
/*
* SCMI Message Protocol driver header
*
- * Copyright (C) 2018-2021 ARM Ltd.
+ * Copyright (C) 2018-2026 ARM Ltd.
*/
#ifndef _LINUX_SCMI_PROTOCOL_H
@@ -609,6 +609,35 @@ struct scmi_voltage_proto_ops {
s32 *volt_uV);
};
+/**
+ * struct scmi_powercap_cpl_info - Describe one CPL - Concurrent Powercap Limit
+ *
+ * @id: CPL ID as advertised by the platform.
+ * @cap_config: CAP configuration support for this CPL.
+ * @min_power_cap: Minimum configurable CAP.
+ * @max_power_cap: Maximum configurable CAP.
+ * @power_cap_step: Step size between two consecutive CAP values.
+ * @avg_ivl_config: Powercap averaging interval configuration support.
+ * @min_avg_ivl: Minimum configurable powercap averaging interval.
+ * @max_avg_ivl: Maximum configurable powercap averaging interval.
+ * @avg_ivl_step: Step size between two consecutive averaging intervals.
+ * @name: name assigned to the Powercap Domain by platform.
+ * @fc_info: Reference to the FastChannels descriptors supported by this CPL
+ */
+struct scmi_powercap_cpl_info {
+ unsigned int id;
+ bool cap_config;
+ unsigned int min_power_cap;
+ unsigned int max_power_cap;
+ unsigned int power_cap_step;
+ bool avg_ivl_config;
+ unsigned int min_avg_ivl;
+ unsigned int max_avg_ivl;
+ unsigned int avg_ivl_step;
+ char name[SCMI_SHORT_NAME_MAX_SIZE];
+ struct scmi_fc_info *fc_info;
+};
+
/**
* struct scmi_powercap_info - Describe one available Powercap domain
*
@@ -617,21 +646,15 @@ struct scmi_voltage_proto_ops {
* @notify_powercap_measurement_change: MEASUREMENTS change notifications
* support.
* @async_powercap_cap_set: Asynchronous CAP set support.
- * @powercap_cap_config: CAP configuration support.
* @powercap_monitoring: Monitoring (measurements) support.
- * @powercap_pai_config: PAI configuration support.
* @powercap_scale_mw: Domain reports power data in milliwatt units.
* @powercap_scale_uw: Domain reports power data in microwatt units.
* Note that, when both @powercap_scale_mw and
* @powercap_scale_uw are set to false, the domain
* reports power data on an abstract linear scale.
+ * @extended_names: Support for long names.
+ * @fastchannels: Support for at least one fastchannel,
* @name: name assigned to the Powercap Domain by platform.
- * @min_pai: Minimum configurable PAI.
- * @max_pai: Maximum configurable PAI.
- * @pai_step: Step size between two consecutive PAI values.
- * @min_power_cap: Minimum configurable CAP.
- * @max_power_cap: Maximum configurable CAP.
- * @power_cap_step: Step size between two consecutive CAP values.
* @sustainable_power: Maximum sustainable power consumption for this domain
* under normal conditions.
* @accuracy: The accuracy with which the power is measured and reported in
@@ -639,30 +662,25 @@ struct scmi_voltage_proto_ops {
* @parent_id: Identifier of the containing parent power capping domain, or the
* value 0xFFFFFFFF if this powercap domain is a root domain not
* contained in any other domain.
+ * @num_cpli: Number of discovered CPLs.
+ * @cpli: Reference to an array holding descriptors to all the discovered CPLs.
*/
struct scmi_powercap_info {
unsigned int id;
bool notify_powercap_cap_change;
bool notify_powercap_measurement_change;
bool async_powercap_cap_set;
- bool powercap_cap_config;
bool powercap_monitoring;
- bool powercap_pai_config;
bool powercap_scale_mw;
bool powercap_scale_uw;
bool fastchannels;
char name[SCMI_MAX_STR_SIZE];
- unsigned int min_pai;
- unsigned int max_pai;
- unsigned int pai_step;
- unsigned int min_power_cap;
- unsigned int max_power_cap;
- unsigned int power_cap_step;
unsigned int sustainable_power;
unsigned int accuracy;
#define SCMI_POWERCAP_ROOT_ZONE_ID 0xFFFFFFFFUL
unsigned int parent_id;
- struct scmi_fc_info *fc_info;
+ unsigned int num_cpli;
+ struct scmi_powercap_cpl_info *cpli;
};
/**
@@ -691,8 +709,12 @@ struct scmi_powercap_info {
* on the system: for this reason @cap_get and @cap_enable_get
* will always report the final platform view of the powercaps.
* @cap_enable_get: get the current CAP enable status for the specified domain.
- * @pai_get: get the current PAI value for the specified domain.
- * @pai_set: set the PAI value for the specified domain to the provided value.
+ * @avg_interval_get: get the current averaging interval value for the specified
+ * domain. This will get the PAI or CAI depending on the used
+ * protocol version.
+ * @avg_interval_set: set the current averaging interval value for the specified
+ * domain. This will set the PAI or CAI depending on the used
+ * protocol version.
* @measurements_get: retrieve the current average power measurements for the
* specified domain and the related PAI upon which is
* calculated.
@@ -716,17 +738,17 @@ struct scmi_powercap_proto_ops {
const struct scmi_powercap_info __must_check *(*info_get)
(const struct scmi_protocol_handle *ph, u32 domain_id);
int (*cap_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
- u32 *power_cap);
+ u32 cpl_id, u32 *power_cap);
int (*cap_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
- u32 power_cap, bool ignore_dresp);
+ u32 cpl_id, u32 power_cap, bool ignore_dresp);
int (*cap_enable_set)(const struct scmi_protocol_handle *ph,
u32 domain_id, bool enable);
int (*cap_enable_get)(const struct scmi_protocol_handle *ph,
u32 domain_id, bool *enable);
- int (*pai_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
- u32 *pai);
- int (*pai_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
- u32 pai);
+ int (*avg_interval_get)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *val);
+ int (*avg_interval_set)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 val);
int (*measurements_get)(const struct scmi_protocol_handle *ph,
u32 domain_id, u32 *average_power, u32 *pai);
int (*measurements_threshold_set)(const struct scmi_protocol_handle *ph,
--
2.47.3
^ permalink raw reply related
* [PATCH v7 05/13] firmware: arm_scmi: Add SCMIv4.0 Powercap FCs support
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
Add support for new SCMIv4.0 Powercap Fastchannels.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
[Philip: removed reference to old versioning logic]
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
V2->V4
- moved fastchannel inits outside of loop
- renamed arguments for consistency
---
drivers/firmware/arm_scmi/powercap.c | 331 ++++++++++++++++++---------
1 file changed, 229 insertions(+), 102 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index 51771c833183..ba037e906640 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -27,19 +27,25 @@ enum scmi_powercap_protocol_cmd {
POWERCAP_CAP_GET = 0x4,
POWERCAP_CAP_SET = 0x5,
POWERCAP_PAI_GET = 0x6,
+ POWERCAP_MAI_GET = POWERCAP_PAI_GET,
POWERCAP_PAI_SET = 0x7,
+ POWERCAP_MAI_SET = POWERCAP_PAI_SET,
POWERCAP_DOMAIN_NAME_GET = 0x8,
POWERCAP_MEASUREMENTS_GET = 0x9,
POWERCAP_CAP_NOTIFY = 0xa,
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
POWERCAP_CPC_ATTRIBUTES = 0xd,
+ POWERCAP_CAI_GET = 0xe,
+ POWERCAP_CAI_SET = 0xf,
};
enum {
POWERCAP_FC_CAP,
- POWERCAP_FC_PAI,
- POWERCAP_FC_MAX,
+ POWERCAP_FC_XAI,
+ POWERCAP_FC_MAI,
+ POWERCAP_FC_MEASUREMENT,
+ POWERCAP_FC_MAX
};
struct scmi_msg_resp_powercap_domain_attributes {
@@ -91,12 +97,12 @@ struct scmi_msg_resp_powercap_domain_attributes_v3 {
__le32 cai_step;
};
-struct scmi_msg_powercap_get_v3 {
+struct scmi_msg_powercap_cap_or_cai_get_v3 {
__le32 domain_id;
__le32 cpli;
};
-struct scmi_msg_powercap_set_cap_or_pai {
+struct scmi_msg_powercap_cap_or_pai_set {
__le32 domain_id;
__le32 flags;
#define CAP_SET_ASYNC BIT(1)
@@ -104,13 +110,20 @@ struct scmi_msg_powercap_set_cap_or_pai {
__le32 value;
};
-struct scmi_msg_powercap_set_cap_v3 {
+struct scmi_msg_powercap_cap_set_v3 {
__le32 domain_id;
__le32 cpli;
__le32 flags;
__le32 power_cap;
};
+struct scmi_msg_powercap_cai_set {
+ __le32 domain_id;
+ __le32 flags;
+ __le32 cai;
+ __le32 cpli;
+};
+
struct scmi_msg_resp_powercap_cap_set_complete {
__le32 domain_id;
__le32 power_cap;
@@ -201,6 +214,10 @@ struct powercap_info {
int (*xfer_cap_set)(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_info *pc,
u32 cpl_id, u32 power_cap, bool ignore_dresp);
+ int (*xfer_avg_ivl_get)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *ivl);
+ int (*xfer_avg_ivl_set)(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 ivl);
};
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
@@ -505,17 +522,6 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
return ret;
}
-static int
-scmi_powercap_domain_initialize(const struct scmi_protocol_handle *ph,
- struct powercap_info *pinfo, u32 domain)
-{
- struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
-
- dom_info->id = domain;
-
- return scmi_powercap_domain_attributes_get(ph, pinfo, dom_info);
-}
-
static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
{
struct powercap_info *pi = ph->get_priv(ph);
@@ -562,7 +568,7 @@ static int scmi_powercap_xfer_cap_get_v3(const struct scmi_protocol_handle *ph,
{
int ret;
struct scmi_xfer *t;
- struct scmi_msg_powercap_get_v3 *msg;
+ struct scmi_msg_powercap_cap_or_cai_get_v3 *msg;
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(*msg),
sizeof(u32), &t);
@@ -621,7 +627,7 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
{
int ret;
struct scmi_xfer *t;
- struct scmi_msg_powercap_set_cap_or_pai *msg;
+ struct scmi_msg_powercap_cap_or_pai_set *msg;
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
sizeof(*msg), 0, &t);
@@ -664,7 +670,7 @@ static int scmi_powercap_xfer_cap_set_v3(const struct scmi_protocol_handle *ph,
{
int ret;
struct scmi_xfer *t;
- struct scmi_msg_powercap_set_cap_v3 *msg;
+ struct scmi_msg_powercap_cap_set_v3 *msg;
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
sizeof(*msg), 0, &t);
@@ -763,8 +769,9 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
power_cap, ignore_dresp);
}
-static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *pai)
+static int
+scmi_powercap_xfer_avg_interval_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *ivl)
{
int ret;
struct scmi_xfer *t;
@@ -777,58 +784,105 @@ static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
put_unaligned_le32(domain_id, t->tx.buf);
ret = ph->xops->do_xfer(ph, t);
if (!ret)
- *pai = get_unaligned_le32(t->rx.buf);
+ *ivl = get_unaligned_le32(t->rx.buf);
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
+static int
+scmi_powercap_xfer_avg_interval_get_v3(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *ivl)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_powercap_cap_or_cai_get_v3 *msg;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_CAI_GET, sizeof(*msg),
+ sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->cpli = cpu_to_le32(cpl_id);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret)
+ *ivl = get_unaligned_le32(t->rx.buf);
ph->xops->xfer_put(ph, t);
return ret;
}
-static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 cpl_id, u32 *pai)
+static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 *val)
{
struct scmi_powercap_info *dom;
struct powercap_info *pi = ph->get_priv(ph);
- if (!pai || domain_id >= pi->num_domains)
+ if (!val || domain_id >= pi->num_domains)
return -EINVAL;
dom = pi->powercaps + domain_id;
if (cpl_id >= dom->num_cpli)
return -EINVAL;
- if (dom->cpli[cpl_id].fc_info && dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr) {
- *pai = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].get_addr);
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
- domain_id, *pai, 0);
+ if (dom->cpli[cpl_id].fc_info &&
+ dom->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].get_addr) {
+ int trace_cmd = (PROTOCOL_REV_MAJOR(ph->version) < 0x3) ?
+ POWERCAP_PAI_GET : POWERCAP_CAI_GET;
+
+ *val = ioread32(dom->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].get_addr);
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, *val, 0);
return 0;
}
- return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
+ return pi->xfer_avg_ivl_get(ph, domain_id, cpl_id, val);
}
-static int scmi_powercap_avg_interval_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 cpl_id, u32 *val)
+static int
+scmi_powercap_xfer_avg_interval_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 ivl)
{
- return scmi_powercap_pai_get(ph, domain_id, cpl_id, val);
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_powercap_cap_or_pai_set *msg;
+
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, sizeof(*msg), 0, &t);
+ if (ret)
+ return ret;
+
+ msg = t->tx.buf;
+ msg->domain_id = cpu_to_le32(domain_id);
+ msg->flags = cpu_to_le32(0);
+ msg->value = cpu_to_le32(ivl);
+
+ ret = ph->xops->do_xfer(ph, t);
+
+ ph->xops->xfer_put(ph, t);
+ return ret;
}
-static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 pai)
+static int
+scmi_powercap_xfer_avg_interval_set_v3(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 ivl)
{
int ret;
struct scmi_xfer *t;
- struct scmi_msg_powercap_set_cap_or_pai *msg;
+ struct scmi_msg_powercap_cai_set *msg;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
- sizeof(*msg), 0, &t);
+ ret = ph->xops->xfer_get_init(ph, POWERCAP_CAI_SET, sizeof(*msg), 0, &t);
if (ret)
return ret;
msg = t->tx.buf;
msg->domain_id = cpu_to_le32(domain_id);
msg->flags = cpu_to_le32(0);
- msg->value = cpu_to_le32(pai);
+ msg->cai = cpu_to_le32(ivl);
+ msg->cpli = cpu_to_le32(cpl_id);
ret = ph->xops->do_xfer(ph, t);
@@ -836,48 +890,42 @@ static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
return ret;
}
-static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 cpl_id, u32 pai)
+static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 cpl_id, u32 ivl)
{
const struct scmi_powercap_info *pc;
+ struct powercap_info *pi = ph->get_priv(ph);
pc = scmi_powercap_dom_info_get(ph, domain_id);
if (!pc || cpl_id >= pc->num_cpli || !pc->cpli[cpl_id].avg_ivl_config ||
- !pai || pai < pc->cpli[cpl_id].min_avg_ivl ||
- pai > pc->cpli[cpl_id].max_avg_ivl)
+ !ivl || ivl < pc->cpli[cpl_id].min_avg_ivl ||
+ ivl > pc->cpli[cpl_id].max_avg_ivl)
return -EINVAL;
- if (pc->cpli[cpl_id].fc_info && pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI].set_addr) {
- struct scmi_fc_info *fci = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_PAI];
+ /* Note that fc_info descriptors for any unsupported FC will be NULL */
+ if (pc->cpli[cpl_id].fc_info &&
+ pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI].set_addr) {
+ int trace_cmd = (PROTOCOL_REV_MAJOR(ph->version) < 0x3) ?
+ POWERCAP_PAI_SET : POWERCAP_CAI_SET;
+ struct scmi_fc_info *fci = &pc->cpli[cpl_id].fc_info[POWERCAP_FC_XAI];
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
- domain_id, pai, 0);
- iowrite32(pai, fci->set_addr);
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, trace_cmd, domain_id, ivl, 0);
+ iowrite32(ivl, fci->set_addr);
ph->hops->fastchannel_db_ring(fci->set_db);
return 0;
}
- return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
-}
-
-static int scmi_powercap_avg_interval_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 cpl_id, u32 val)
-{
- return scmi_powercap_pai_set(ph, domain_id, cpl_id, val);
+ return pi->xfer_avg_ivl_set(ph, domain_id, cpl_id, ivl);
}
-static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *average_power,
- u32 *pai)
+static int
+scmi_powercap_xfer_measurements_get(const struct scmi_protocol_handle *ph,
+ const struct scmi_powercap_info *pc,
+ u32 *avg_power, u32 *avg_ivl)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_resp_powercap_meas_get *resp;
- const struct scmi_powercap_info *pc;
-
- pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_monitoring || !pai || !average_power)
- return -EINVAL;
ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
sizeof(u32), sizeof(*resp), &t);
@@ -885,17 +933,42 @@ static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
return ret;
resp = t->rx.buf;
- put_unaligned_le32(domain_id, t->tx.buf);
+ put_unaligned_le32(pc->id, t->tx.buf);
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
- *average_power = le32_to_cpu(resp->power);
- *pai = le32_to_cpu(resp->pai);
+ *avg_power = le32_to_cpu(resp->power);
+ *avg_ivl = le32_to_cpu(resp->pai);
}
ph->xops->xfer_put(ph, t);
return ret;
}
+static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
+ u32 domain_id, u32 *avg_power,
+ u32 *avg_ivl)
+{
+ const struct scmi_powercap_info *pc;
+ struct scmi_fc_info *fci;
+
+ pc = scmi_powercap_dom_info_get(ph, domain_id);
+ if (!pc || !pc->powercap_monitoring || !avg_ivl || !avg_power)
+ return -EINVAL;
+
+ /* Note that fc_info descriptors for any unsupported FC will be NULL */
+ fci = pc->cpli[CPL0].fc_info;
+ if (fci && fci[POWERCAP_FC_MEASUREMENT].get_addr) {
+ *avg_power = ioread32(fci[POWERCAP_FC_MEASUREMENT].get_addr);
+ /* See SCMIv4.0 3.10.2 - Payload is 32bit ONLY avg_power */
+ *avg_ivl = 0;
+ trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_MEASUREMENTS_GET,
+ pc->id, *avg_power, *avg_ivl);
+ return 0;
+ }
+
+ return scmi_powercap_xfer_measurements_get(ph, pc, avg_power, avg_ivl);
+}
+
static int
scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
u32 domain_id, u32 *power_thresh_low,
@@ -1023,37 +1096,85 @@ static const struct scmi_powercap_proto_ops powercap_proto_ops = {
};
static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
- u32 domain, struct scmi_fc_info **p_fc)
+ struct scmi_powercap_info *dom_info)
{
- struct scmi_fc_info *fc;
-
- fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
- if (!fc)
- return;
-
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_CAP_SET, 4, domain, NULL,
- &fc[POWERCAP_FC_CAP].set_addr,
- &fc[POWERCAP_FC_CAP].set_db,
- &fc[POWERCAP_FC_CAP].rate_limit);
-
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_CAP_GET, 4, domain, NULL,
- &fc[POWERCAP_FC_CAP].get_addr, NULL,
- &fc[POWERCAP_FC_CAP].rate_limit);
-
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_PAI_SET, 4, domain, NULL,
- &fc[POWERCAP_FC_PAI].set_addr,
- &fc[POWERCAP_FC_PAI].set_db,
- &fc[POWERCAP_FC_PAI].rate_limit);
-
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_PAI_GET, 4, domain, NULL,
- &fc[POWERCAP_FC_PAI].get_addr, NULL,
- &fc[POWERCAP_FC_PAI].rate_limit);
-
- *p_fc = fc;
+ struct scmi_fc_info *fc_cpl0;
+
+ for (int id = 0; id < dom_info->num_cpli; id++) {
+ struct scmi_fc_info *fc;
+ u32 *cpl_id, zero_cpl_id = 0;
+
+ fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
+ if (!fc)
+ return;
+
+ /* NOTE THAT when num_cpli == 1 the arg *cpl_id is 0 */
+ cpl_id = (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) ? &id : NULL;
+
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_CAP_SET, 4, dom_info->id,
+ cpl_id,
+ &fc[POWERCAP_FC_CAP].set_addr,
+ &fc[POWERCAP_FC_CAP].set_db,
+ &fc[POWERCAP_FC_CAP].rate_limit);
+
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_CAP_GET, 4, dom_info->id,
+ cpl_id,
+ &fc[POWERCAP_FC_CAP].get_addr, NULL,
+ &fc[POWERCAP_FC_CAP].rate_limit);
+
+ if (PROTOCOL_REV_MAJOR(ph->version) >= 0x3) {
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_CAI_SET, 4,
+ dom_info->id, cpl_id,
+ &fc[POWERCAP_FC_XAI].set_addr,
+ &fc[POWERCAP_FC_XAI].set_db,
+ &fc[POWERCAP_FC_XAI].rate_limit);
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_CAI_GET, 4,
+ dom_info->id, cpl_id,
+ &fc[POWERCAP_FC_XAI].get_addr, NULL,
+ &fc[POWERCAP_FC_XAI].rate_limit);
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_MAI_SET, 4,
+ dom_info->id, &zero_cpl_id,
+ &fc[POWERCAP_FC_MAI].set_addr,
+ &fc[POWERCAP_FC_MAI].set_db,
+ &fc[POWERCAP_FC_MAI].rate_limit);
+
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_MAI_GET, 4,
+ dom_info->id, &zero_cpl_id,
+ &fc[POWERCAP_FC_MAI].get_addr, NULL,
+ &fc[POWERCAP_FC_MAI].rate_limit);
+
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_MEASUREMENTS_GET, 4,
+ dom_info->id, &zero_cpl_id,
+ &fc[POWERCAP_FC_MEASUREMENT].get_addr, NULL,
+ &fc[POWERCAP_FC_MEASUREMENT].rate_limit);
+ }
+
+ dom_info->cpli[id].fc_info = fc;
+ }
+
+ if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) {
+ fc_cpl0 = dom_info->cpli[CPL0].fc_info;
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_PAI_SET, 4,
+ dom_info->id, NULL,
+ &fc_cpl0[POWERCAP_FC_XAI].set_addr,
+ &fc_cpl0[POWERCAP_FC_XAI].set_db,
+ &fc_cpl0[POWERCAP_FC_XAI].rate_limit);
+
+ ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
+ POWERCAP_PAI_GET, 4,
+ dom_info->id, NULL,
+ &fc_cpl0[POWERCAP_FC_XAI].get_addr, NULL,
+ &fc_cpl0[POWERCAP_FC_XAI].rate_limit);
+ }
+
}
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
@@ -1290,9 +1411,14 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
if (PROTOCOL_REV_MAJOR(ph->version) < 0x3) {
pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get;
pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set;
+ pinfo->xfer_avg_ivl_get = scmi_powercap_xfer_avg_interval_get;
+ pinfo->xfer_avg_ivl_set = scmi_powercap_xfer_avg_interval_set;
+
} else {
pinfo->xfer_cap_get = scmi_powercap_xfer_cap_get_v3;
pinfo->xfer_cap_set = scmi_powercap_xfer_cap_set_v3;
+ pinfo->xfer_avg_ivl_get = scmi_powercap_xfer_avg_interval_get_v3;
+ pinfo->xfer_avg_ivl_set = scmi_powercap_xfer_avg_interval_set_v3;
}
ret = scmi_powercap_attributes_get(ph, pinfo);
@@ -1317,18 +1443,19 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
* formed and correlated by sane parent-child relationship (if any).
*/
for (domain = 0; domain < pinfo->num_domains; domain++) {
- ret = scmi_powercap_domain_initialize(ph, pinfo, domain);
+ struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
+
+ dom_info->id = domain;
+ ret = scmi_powercap_domain_attributes_get(ph, pinfo, dom_info);
if (ret)
return ret;
- if (pinfo->powercaps[domain].fastchannels)
- scmi_powercap_domain_init_fc(ph, domain,
- &pinfo->powercaps[domain].cpli[CPL0].fc_info);
+ if (dom_info->fastchannels)
+ scmi_powercap_domain_init_fc(ph, dom_info);
/* Grab initial state when disable is supported. */
if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
- ret = __scmi_powercap_cap_get(ph,
- &pinfo->powercaps[domain], CPL0,
+ ret = __scmi_powercap_cap_get(ph, dom_info, CPL0,
&pinfo->states[domain].last_pcap);
if (ret)
return ret;
--
2.47.3
^ permalink raw reply related
* [PATCH v7 06/13] firmware: arm_scmi: Add SCMIV4.0 Powercap notifications support
From: Philip Radford @ 2026-06-17 9:59 UTC (permalink / raw)
To: linux-kernel, linux-arm-kernel, arm-scmi, linux-pm
Cc: sudeep.holla, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, quic_sibis,
dan.carpenter, d-gole, souvik.chakravarty, Cristian Marussi,
Philip Radford
In-Reply-To: <20260617095910.1963578-1-philip.radford@arm.com>
From: Cristian Marussi <cristian.marussi@arm.com>
Extend notification support to the new SCMIv4.0 Powercap format that carry
also a CPL identifier where specified.
Since this addition completes SCMIv4.0 Powercap support bump also the
protocol version define.
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Philip Radford <philip.radford@arm.com>
---
drivers/firmware/arm_scmi/powercap.c | 13 +++++++++----
include/linux/scmi_protocol.h | 3 ++-
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index ba037e906640..7ed6b6467813 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -18,7 +18,7 @@
#include "notify.h"
/* Updated only after ALL the mandatory features for that version are merged */
-#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
#define CPL0 0
@@ -156,7 +156,8 @@ struct scmi_powercap_cap_changed_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 power_cap;
- __le32 pai;
+ __le32 avg_ivl;
+ __le32 cpli;
};
struct scmi_powercap_meas_changed_notify_payld {
@@ -1316,14 +1317,18 @@ scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
const struct scmi_powercap_cap_changed_notify_payld *p = payld;
struct scmi_powercap_cap_changed_report *r = report;
- if (sizeof(*p) != payld_sz)
+ if (sizeof(*p) > payld_sz)
break;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
r->power_cap = le32_to_cpu(p->power_cap);
- r->pai = le32_to_cpu(p->pai);
+ r->avg_ivl = le32_to_cpu(p->avg_ivl);
+ if (sizeof(*p) == payld_sz)
+ r->cpli = le32_to_cpu(p->cpli);
+ else
+ r->cpli = 0;
*src_id = r->domain_id;
rep = r;
break;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 547ab4763a63..299fa8499b3f 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -1125,7 +1125,8 @@ struct scmi_powercap_cap_changed_report {
unsigned int agent_id;
unsigned int domain_id;
unsigned int power_cap;
- unsigned int pai;
+ unsigned int avg_ivl;
+ unsigned int cpli;
};
struct scmi_powercap_meas_changed_report {
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox