* [PATCH v8 1/3] dt-bindings: pwm: dwc: Document optional resets property
From: dongxuyang @ 2026-06-23 7:14 UTC (permalink / raw)
To: ukleinek, robh, krzk+dt, conor+dt, ben-linux, ben.dooks, p.zabel,
linux-pwm, devicetree, linux-kernel
Cc: ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela,
Xuyang Dong
In-Reply-To: <20260623071329.2034-1-dongxuyang@eswincomputing.com>
From: Xuyang Dong <dongxuyang@eswincomputing.com>
The DesignWare PWM IP has two active-low reset inputs: presetn resets
the register interface logic in the pclk (bus) domain, and
timer_N_resetn resets the counter/timer logic in the timer_N_clk
domain. The existing snps,dw-apb-timers-pwm2 binding does not
describe either of these lines.
Add the resets property and describe the function of each reset to
support future use of resets.
Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
---
.../devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
index 7523a89a1773..213fdaef25d9 100644
--- a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
+++ b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
@@ -43,6 +43,11 @@ properties:
- const: bus
- const: timer
+ resets:
+ items:
+ - description: Interface bus (presetn) reset
+ - description: PWM timer logic (timer_N_resetn) reset
+
snps,pwm-number:
$ref: /schemas/types.yaml#/definitions/uint32
description: The number of PWM channels configured for this instance
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v5 1/1] arm64: dts: add tqma9596la-mba95xxca
From: Alexander Stein @ 2026-06-23 7:14 UTC (permalink / raw)
To: Frank Li
Cc: Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Markus Niebel, imx,
linux-arm-kernel, devicetree, linux-kernel, linux,
linux-renesas-soc
In-Reply-To: <ajVN4nbmOW-D_Ec9@SMW015318>
[-- Attachment #1: Type: text/plain, Size: 4206 bytes --]
Am Freitag, 19. Juni 2026, 16:10:42 CEST schrieb Frank Li:
> On Fri, Jun 19, 2026 at 01:24:30PM +0200, Alexander Stein wrote:
> > From: Markus Niebel <Markus.Niebel@ew.tq-group.com>
> >
> > This adds support for TQMa95xxLA modules, designed to be soldered
> > on a carrier board. MBa95xxCA is a carrier reference board / starter kit
> > design.
> >
> > There is a common device tree for all variants with e.g. reduced
> > CPU core / feature count.
> >
> > Enable the external accessible PCIe controllers as host,
> > add clocking and reset GPIO. While at it, add hogs for GPIO
> > lines from the M.2 slots until M.2 connector driver is available.
> >
> > Signed-off-by: Markus Niebel <Markus.Niebel@ew.tq-group.com>
> > Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> > ---
> > Changes in v5:
> > * Limit LPSPI4 max frequency
> > * Replace PCIe M2 Key-E GPIO hogs with dedicated connector node
> > * Fix PCIe clock configuration
> >
> > Changes in v4:
> > * Fix LPSPI4 pad muxing and control
> >
> > Changes in v3:
> > * Moved reserved-memory to board-lebel
> > * Remove VPU reserved memory (unused for now)
> > * Fix typo in connector comment
> >
> > Changes in v2:
> > * removed useless regulator
> > * added USB PD source configuration
> > * Removed unused uart-has-rtscts properties (unused by LPUART)
> > * Fixed RTS/CTS pullups in pinctrl
> > * Added thermalzone on module
> >
> > arch/arm64/boot/dts/freescale/Makefile | 1 +
> > .../freescale/imx95-tqma9596la-mba95xxca.dts | 963 ++++++++++++++++++
> > .../boot/dts/freescale/imx95-tqma9596la.dtsi | 278 +++++
> > 3 files changed, 1242 insertions(+)
> > create mode 100644 arch/arm64/boot/dts/freescale/imx95-tqma9596la-mba95xxca.dts
> > create mode 100644 arch/arm64/boot/dts/freescale/imx95-tqma9596la.dtsi
> >
> > diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
> > index 8ddaab127ab9c..43e1dc51b11d7 100644
> > --- a/arch/arm64/boot/dts/freescale/Makefile
> > +++ b/arch/arm64/boot/dts/freescale/Makefile
> > @@ -649,6 +649,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-frdm-pro.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx95-aquila-clover.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx95-aquila-dev.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx95-toradex-smarc-dev.dtb
> > +dtb-$(CONFIG_ARCH_MXC) += imx95-tqma9596la-mba95xxca.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx95-tqma9596sa-mb-smarc-2.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx95-var-dart-sonata.dtb
> > dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-dahlia.dtb
> ...
> > +
> > + ptn5110: usb-typec@50 {
> > + compatible = "nxp,ptn5110", "tcpci";
> > + reg = <0x50>;
> > + pinctrl-names = "default";
> > + pinctrl-0 = <&pinctrl_typec>;
> > + interrupt-parent = <&gpio2>;
> > + interrupts = <28 IRQ_TYPE_LEVEL_LOW>;
> > +
> > + typec_con: connector {
> > + compatible = "usb-c-connector";
> > + label = "X9";
> > + power-role = "source";
> > + data-role = "dual";
> > + source-pdos = <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM)>;
> > + self-powered;
> > +
> > + port {
> > + typec_con_hs: endpoint {
> > + remote-endpoint = <&typec_hs>;
> > + };
> > + };
> > + };
> > + };
> > +
> > + sensor_mb: temperature-sensor@1e {
>
> please order by hex address value
Ah, thanks for the catch.
> > + compatible = "nxp,se97b", "jedec,jc-42.4-temp";
> > + reg = <0x1e>;
> > + };
> > +
> ...
> > + m2-keye-dev_bt-wake-hog {
> > + gpio-hog;
> > + gpios = <4 GPIO_ACTIVE_LOW>;
> > + input;
> > + line-name = "M2_KEYE_DEV_BT_WAKE#";
> > + };
>
> Now Key E connector already be upstreamed.
>
> Documentation/devicetree/bindings/connector/pcie-m2-e-connector.yaml,
> which already define these gpios.
Thanks for the hint. But these two wake signals are vendor specific
pins 40 and 42 on key-e. In this case these are for the default module
JODY-W3 module.
Thanks and best regards,
Alexander
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
http://www.tq-group.com/
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v8 0/3] Update designware pwm driver
From: dongxuyang @ 2026-06-23 7:13 UTC (permalink / raw)
To: ukleinek, robh, krzk+dt, conor+dt, ben-linux, ben.dooks, p.zabel,
linux-pwm, devicetree, linux-kernel
Cc: ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela,
Xuyang Dong
From: Xuyang Dong <dongxuyang@eswincomputing.com>
There is already a patch [1] for the DesignWare PWM driver,
which is posted by Ben and still under review.
Based on this patch, this series is a continuation of [1]
to add support for IP versions 2.11a and later, which
includes support for "Pulse Width Modulation with 0%
and 100% Duty Cycle".
Supported chips:
ESWIN EIC7700 series SoC.
Test:
Tested this patch on the Sifive HiFive Premier P550 (which uses the EIC7700
SoC).
[1] https://lore.kernel.org/lkml/20230907161242.67190-1-ben.dooks@codethink.co.uk/
Updates:
Changes in v8:
- YAML:
- Split the v7 binding into two patches.
Patch 1 explains why to add the resets property.
Patch 2 adds the eswin compatible string and specified reset.
- Driver:
- Use mul_u64_u64_div_u64() to safely scale the values and avoid
64-bit multiplication overflow in __dwc_pwm_configure_timer()
and dwc_pwm_get_state().
Add the include for linux/math64.h (Sashiko review of v7).
- Keep the current usage of pwm->args.polarity until a better solution
is available.
- Use pm_runtime_resume_and_get() in dwc_pwm_get_state() instead of
pm_runtime_get_sync(), so that register access is skipped if the
device fails to resume (Sashiko review of v7).
- Replace devm_pwmchip_add() with pwmchip_add() and move it after
pm_runtime_enable(), so that the PWM chip is registered only after
runtime PM has been fully initialized (Sashiko review of v7).
- Remove the reset_assert label and reset_control_assert()
(Sashiko review of v7).
- Remove the pm_runtime_status_suspended() check and unconditionally
use pm_runtime_get_sync() instead (Sashiko review of v7).
- Remove the pwm_en flag, but keep the pm_runtime_put_noidle() call
(Sashiko review of v7; see email for explanation)
- Use pm_runtime_status_suspended() to check the runtime PM status.
If the device is not suspended (i.e., active), call
clk_disable_unprepare(). If it is suspended, skip this block
(Sashiko review of v7).
- Use an explicit pwmchip_remove() as the first step of .remove(),
instead of relying on devm_pwmchip_add() to unregister the chip
after .remove() returns. This prevents the hardware teardown that
follows from racing against a still-registered chip
(Sashiko review of v7).
- Add a check for dwc->rst before asserting reset in the remove path
(Sashiko review of v7).
- Drop the return value check from pm_runtime_put_sync()
(Sashiko review of v7).
- Link to v7: https://lore.kernel.org/all/20260605082242.1541-1-dongxuyang@eswincomputing.com/
Changes in v7:
- YAML:
- Dropped Conor's Acked-by due to significant schema changes.
- Rename patch 1 from "dt-bindings: pwm: dwc: add optional reset" to
"dt-bindings: pwm: dwc: Add eswin compatible and resets property".
- Update the commit message to explain why the EIC7700 supports only
one reset.
- Add constraints 'minItems: 1' and 'maxItems: 1' for the 'resets'
property of eswin,eic7700-pwm.
- Add an example for eswin,eic7700-pwm.
- Link to v6: https://lore.kernel.org/all/20260424094529.1691-1-dongxuyang@eswincomputing.com/
Changes in v6:
- YAML:
- Drop properties resets and its items description for eswin,eic7700-pwm.
- Link to v5: https://lore.kernel.org/all/20260423083644.1168-1-dongxuyang@eswincomputing.com/
Changes in v5:
- YAML:
- Add 'eswin,eic7700-pwm' compatible string.
- Add the items description for the resets property and set minItems to 1.
- Require resets property with exactly 1 reset for eswin,eic7700-pwm compatible.
- Driver:
- Add support for 'eswin,eic7700-pwm' compatible.
- Add structure dwc_pwm_plat_data to manage the API for obtaining resets.
- Link to v4: https://lore.kernel.org/all/20260415094908.1539-1-dongxuyang@eswincomputing.com/
Changes in v4:
- YAML:
- Change maxItems from 1 to 2. As there is a corresponding reset signal
for each clock domain, the effective maxItems of the resets property
is set to 2.
- Update the YAML commit message to describe the hardware.
- Driver:
- Replace devm_reset_control_get_optional_exclusive() with
devm_reset_control_array_get_optional_exclusive(). Since the number
of reset signals has increased from one to two, we need to use the
array API to acquire them.
- Link to v3: https://lore.kernel.org/all/20260402091718.1608-1-dongxuyang@eswincomputing.com/
Changes in v3:
- YAML:
- Added a clear justification for the optional resets property. It is
required to support proper controller initialization when no PWM
channel is active at boot time, while allowing the driver to skip
reset deassertion if any channel is already enabled.
- Driver:
- Update the boundary value check of tmp in __dwc_pwm_configure_timer()
for DWC_TIM_CTRL_0N100PWM_EN.
- Replace 'sizeof(struct dwc_pwm_drvdata)' with
'struct_size(data, chips, 1)'.
- Drop devm_clk_get_enabled() in favor of devm_clk_get() with explicit
clk_prepare_enable() and clk_disable_unprepare() allowing runtime PM
to manage clock state.
- Replace devm_reset_control_get_optional_exclusive_deasserted() with
devm_reset_control_get_optional_exclusive() and issue a full reset via
reset_control_reset() only when no PWM channel is active at probe time.
- Detect bootloader-enabled PWM channels by reading the enable bit, and
initialize runtime PM as active for those channels by calling
pm_runtime_set_active() and pm_runtime_get_noresume().
- Remove autosuspend as it is not required for this driver.
- Use explicit pm_runtime_enable() and pm_runtime_disable() instead of
the managed devm_pm_runtime_enable() variant to ensure correct cleanup.
- On device removal, recheck the channel enable status. If any channel
remains active, call pm_runtime_put_noidle() before disabling clocks
via clk_disable_unprepare().
Resume device before register access during removal if it is runtime
suspended, and re-suspend it afterward.
- If device is suspended, resume it before register access during system
resume/suspend.
- Use pm_ptr() instead of pm_sleep_ptr() for correct PM operation.
- Link to v2: https://lore.kernel.org/all/20260306093000.2065-1-dongxuyang@eswincomputing.com/
Changes in v2:
- YAML:
- Remove eswin,eic7700-pwm.yaml. Use snps,dw-apb-timers-pwm2.yaml.
The description in snps,dw-apb-timers-pwm2.yaml is better.
- Add the resets property as optional, as defined in the databook.
- Remove snps,pwm-full-range-enable as no additional property is needed.
- Driver:
- Change the file from pwm-dwc-eic7700.c to pwm-dwc-of.c from [1].
- Define DWC_TIM_VERSION_ID_2_11A 2.11a as the baseline version.
- Enable the 0% and 100% duty cycle mode by setting dwc->feature if
the version read from the TIMERS_COMP_VERSION register is later
than or equal to DWC_TIM_VERSION_ID_2_11A.
- Use the DIV_ROUND_UP_ULL() to calculate width in the .apply and
.get_state.
- Additionally, Power Management (PM) support has been added to the
pwm-dwc-of.c driver.
- Drop the headers that are not used.
- Use devm_clk_get_enabled() instead of devm_clk_get().
- Drop of_match_ptr.
- Fix build error with 1ULL << 32.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202512061720.j31AsgM7-lkp@intel.com/
- Link to v1: https://lore.kernel.org/all/20251205090411.1388-1-dongxuyang@eswincomputing.com/
- Link to v9: https://lore.kernel.org/lkml/20230907161242.67190-1-ben.dooks@codethink.co.uk/
Xuyang Dong (3):
dt-bindings: pwm: dwc: Document optional resets property
dt-bindings: pwm: dwc: Add eswin compatible
pwm: dwc: add of/platform support
.../bindings/pwm/snps,dw-apb-timers-pwm2.yaml | 37 +-
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-dwc-core.c | 103 ++++--
drivers/pwm/pwm-dwc-of.c | 346 ++++++++++++++++++
drivers/pwm/pwm-dwc.h | 25 +-
6 files changed, 488 insertions(+), 34 deletions(-)
create mode 100644 drivers/pwm/pwm-dwc-of.c
--
2.34.1
^ permalink raw reply
* Re: [PATCH 1/3] dt-bindings: arm: qcom: Add Asus Zenbook A14 (UX3407NA)
From: Krzysztof Kozlowski @ 2026-06-23 7:12 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Bjorn Andersson
In-Reply-To: <20260623-zenbook-dts-v1-1-3f80f680381d@oss.qualcomm.com>
On 23/06/2026 03:31, Bjorn Andersson wrote:
> From: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
>
> UX3407NA is a variant of the Asus Zenbook A14 built on the Qualcomm
> Glymur platform. It comes with an 18-core X2 Elite SoC, 32GB DDR, NVMe
> storage, a WUXGA OLED screen, and the other typical Glymur platform
> capabilities.
>
> Add a compatible for this machine.
>
> [1] https://www.asus.com/us/laptops/for-home/zenbook/asus-zenbook-a14-ux3407/techspec/
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
> ---
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v8 0/5] media: iris: add support for purwa platform
From: Krzysztof Kozlowski @ 2026-06-23 7:11 UTC (permalink / raw)
To: Wangao Wang, Vikash Garodia, Bryan O'Donoghue,
Dikshita Agarwal, Abhinav Kumar, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio
Cc: linux-media, linux-arm-msm, devicetree, linux-kernel,
Bryan O'Donoghue, Konrad Dybcio, Dmitry Baryshkov
In-Reply-To: <45fe0be2-a397-4677-a7b1-527574c7578b@oss.qualcomm.com>
On 23/06/2026 08:15, Wangao Wang wrote:
>
>
> On 2026/6/4 14:48, Vikash Garodia wrote:
>> Hi Bjorn,
>>
>> On 5/29/2026 1:04 PM, Wangao Wang wrote:
>>> This series enables the Iris video codec on purwa, allowing purwa to
>>> use hardware‑accelerated video encoding and decoding.
>>>
>>> The Iris codec on purwa is nearly identical to the one on hamoa(X1E),
>>> except that it requires one additional clock and uses a different OPP
>>> table.
>>>
>>> Therefore, purwa can reuse the Iris node from hamoa, but the clocks
>>> and OPP table need to be redefined.
>>>
>>> All patches have been tested with v4l2-compliance and v4l2-ctl on
>>> purwa. And it does not affect existing targets.
>>>
>>> Dependencies:
>>> https://lore.kernel.org/all/20260409-purwa-videocc-camcc-
>>> v4-0-5a8e5f2dd4b2@oss.qualcomm.com/
>>>
>>
>> <snip>
>>>
>>> ---
>>> Wangao Wang (5):
>>> dt-bindings: media: qcom,sm8550-iris: Add X1P42100 compatible
>>> media: iris: Add hardware power on/off ops for X1P42100
>>> media: iris: Add platform data for X1P42100
>>> arm64: dts: qcom: purwa: Override Iris clocks and operating points
>>> arm64: dts: qcom: purwa-iot-som: enable video
>>>
>>
>> Media patches in this series are now applied. Do you plan to apply the
>> DTS ones ?
>>
>> Regards,
>> Vikash
>>
>
> Hi Bjorn, is everything going well? If you encounter any issues, please
> feel free to give us feedback. Thank you.
Now is merge window, so usually better not to ping - patches won't move
anyway. In case you wonder what is with the patches, I suggest to check
the status in Patchwork.
However that said considering how old is this patchset (1 month) and
after looking at cover letter with:
change-id: 20260209-enable_iris_on_purwa-a000527a098d
prerequisite-change-id: 20260331-purwa-videocc-camcc-d9700d0f797d:v4
prerequisite-patch-id: 61bdb45446193b72dd8a4b093e4ab2f78db2f066
prerequisite-patch-id: b5be9dcbb612a14108f890b2782860847edfcbe4
prerequisite-patch-id: 2f4d4c5c118e057c76e6d2785479df01d5bc1c7b
prerequisite-patch-id: 026db5dd71d5b0472225ba72c8ba2781334143a9
prerequisite-patch-id: 615e6f38e528de35dc206f1c7f3eaf78ff04afe2
prerequisite-patch-id: 66096b909debe4d942eee972948d5a138a5be427
prerequisite-patch-id: ee26e00cdde21ddb070af713230082ad3454422c
the most effective way to move it forward is to send a new version after
rebasing, which will make it obvious and explicit that there are no
dependencies.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v6 4/4] arm64: dts: cix: sky1: add audss cru
From: joakim.zhang @ 2026-06-23 7:08 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260623070805.211019-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
Add the AUDSS CRU device node providing clocks and software resets
for audio subsystem peripherals.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
arch/arm64/boot/dts/cix/sky1.dtsi | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index bb5cfb1f2113..6d045d7216e6 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -6,6 +6,10 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/cix,sky1.h>
+#include <dt-bindings/clock/cix,sky1-audss-cru.h>
+#include <dt-bindings/reset/cix,sky1-system-control.h>
+#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+#include <dt-bindings/reset/cix,sky1-audss-cru.h>
#include "sky1-power.h"
/ {
@@ -488,6 +492,20 @@ mbox_pm2ap: mailbox@65a0080 {
cix,mbox-dir = "rx";
};
+ audss_cru: clock-controller@7110000 {
+ compatible = "cix,sky1-audss-cru";
+ reg = <0x0 0x07110000 0x0 0x10000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK2>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK4>,
+ <&scmi_clk CLK_TREE_AUDIO_CLK5>;
+ clock-names = "x8k", "x11k", "sys", "48m";
+ power-domains = <&smc_devpd SKY1_PD_AUDIO>;
+ resets = <&s5_syscon SKY1_AUDIO_HIFI5_NOC_RESET_N>;
+ };
+
mbox_sfh2ap: mailbox@8090000 {
compatible = "cix,sky1-mbox";
reg = <0x0 0x08090000 0x0 0x10000>;
--
2.50.1
^ permalink raw reply related
* [PATCH v6 2/4] clk: cix: add sky1 audss clock controller
From: joakim.zhang @ 2026-06-23 7:08 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260623070805.211019-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
Add a platform driver for the Cix Sky1 AUDSS CRU. The driver maps
the CRU registers and registers mux, divider and gate clocks for
DSP, SRAM, HDA, DMAC, I2S, mailbox, watchdog and timer blocks.
Four SoC-level audio reference clocks are enabled as inputs to the
internal clock tree. The driver releases the AUDSS NOC reset, enables
runtime PM and instantiates the auxiliary reset device.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/cix/Kconfig | 16 +
drivers/clk/cix/Makefile | 3 +
drivers/clk/cix/clk-sky1-audss.c | 1201 ++++++++++++++++++++++++++++++
5 files changed, 1222 insertions(+)
create mode 100644 drivers/clk/cix/Kconfig
create mode 100644 drivers/clk/cix/Makefile
create mode 100644 drivers/clk/cix/clk-sky1-audss.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1717ce75a907..cfcaab39068a 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -509,6 +509,7 @@ source "drivers/clk/actions/Kconfig"
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/aspeed/Kconfig"
source "drivers/clk/bcm/Kconfig"
+source "drivers/clk/cix/Kconfig"
source "drivers/clk/eswin/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/imgtec/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cc108a75a900..87c992f0df54 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -119,6 +119,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
+obj-y += cix/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
diff --git a/drivers/clk/cix/Kconfig b/drivers/clk/cix/Kconfig
new file mode 100644
index 000000000000..c92a9a873893
--- /dev/null
+++ b/drivers/clk/cix/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# Audio subsystem clock support for Cixtech SoC family
+menu "Clock support for Cixtech audss"
+
+config CLK_SKY1_AUDSS
+ tristate "Cixtech Sky1 Audio Subsystem Clock Driver"
+ depends on ARCH_CIX || COMPILE_TEST
+ select AUXILIARY_BUS
+ select REGMAP_MMIO
+ select RESET_CONTROLLER
+ help
+ Support for the Audio Subsystem clock controller present on
+ Cixtech Sky1 SoC. This driver provides mux, divider and gate
+ clocks for DSP, I2S, HDA and related blocks in the audio
+ subsystem. Say M or Y here if you want to build this driver.
+endmenu
diff --git a/drivers/clk/cix/Makefile b/drivers/clk/cix/Makefile
new file mode 100644
index 000000000000..bc612f1d08b2
--- /dev/null
+++ b/drivers/clk/cix/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_CLK_SKY1_AUDSS) += clk-sky1-audss.o
diff --git a/drivers/clk/cix/clk-sky1-audss.c b/drivers/clk/cix/clk-sky1-audss.c
new file mode 100644
index 000000000000..25341be4f5bf
--- /dev/null
+++ b/drivers/clk/cix/clk-sky1-audss.c
@@ -0,0 +1,1201 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright 2026 Cix Technology Group Co., Ltd.
+
+#include <linux/auxiliary_bus.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/clock/cix,sky1-audss-cru.h>
+
+#define INFO_HIFI0 0x00
+#define INFO_CLK_GATE 0x10
+#define INFO_CLK_DIV 0x14
+#define INFO_CLK_MUX 0x18
+#define INFO_MCLK 0x70
+
+#define SKY1_AUDSS_CLK_PARENTS_CNT 4
+#define SKY1_AUDSS_NUM_CLKS (CLK_MCLK4 + 1)
+
+static u32 sky1_reg_save[][2] = {
+ { INFO_HIFI0, 0 },
+ { INFO_CLK_GATE, 0 },
+ { INFO_CLK_DIV, 0 },
+ { INFO_CLK_MUX, 0 },
+ { INFO_MCLK, 0 },
+};
+
+static const char * const sky1_audss_clk_names[SKY1_AUDSS_CLK_PARENTS_CNT] = {
+ "x8k", "x11k", "sys", "48m",
+};
+
+static const u32 sky1_clk_rate_default[SKY1_AUDSS_CLK_PARENTS_CNT] = {
+ 294912000,
+ 270950400,
+ 800000000,
+ 48000000,
+};
+
+static const char * const dsp_clk_parent[] = {
+ "audio_clk4"
+};
+
+static const char * const dsp_bclk_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const dsp_pbclk_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const sram_axi_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const hda_sys_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const hda_hda_parent[] = {
+ "audio_clk5"
+};
+
+static const char * const dmac_axi_parent[] = {
+ "audio_clk4_div2"
+};
+
+static const char * const wdg_apb_parent[] = {
+ "audio_clk5_div2"
+};
+
+static const char * const wdg_wdg_parent[] = {
+ "audio_clk5_div2"
+};
+
+static const char * const timer_apb_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const timer_timer_parent[] = {
+ "audio_clk5_div2"
+};
+
+static const char * const mailbox_apb_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const i2s_apb_parent[] = {
+ "audio_clk4_div4"
+};
+
+static const char * const i2s0_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s1_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s2_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s3_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s4_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s5_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s6_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s7_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s8_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const i2s9_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const char * const mclk_parents[] = {
+ "audio_clk0", "audio_clk2"
+};
+
+static const u32 i2s3_mux_table[] = { 0, 2 };
+static const u32 i2s4_mux_table[] = { 0, 2 };
+
+/*
+ * audss composite clock definition
+ */
+struct muxdiv_cfg {
+ int offset;
+ u8 shift;
+ u8 width;
+ u8 flags;
+};
+
+struct gate_cfg {
+ int offset;
+ u8 shift;
+ u8 flags;
+};
+
+struct composite_clk_cfg {
+ u32 id;
+ const char * const name;
+ const char * const *parent_names;
+ int num_parents;
+ const u32 *mux_table;
+ struct muxdiv_cfg *mux_cfg;
+ struct muxdiv_cfg *div_cfg;
+ struct gate_cfg *gate_cfg;
+ unsigned long flags;
+};
+
+#define CFG(_id,\
+ _name,\
+ _parent_names,\
+ _mux_table,\
+ _mux_offset, _mux_shift, _mux_width, _mux_flags,\
+ _div_offset, _div_shift, _div_width, _div_flags,\
+ _gate_offset, _gate_shift, _gate_flags,\
+ _flags)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_names = _parent_names,\
+ .num_parents = ARRAY_SIZE(_parent_names),\
+ .mux_table = _mux_table,\
+ .mux_cfg = &(struct muxdiv_cfg) { _mux_offset, _mux_shift, _mux_width, _mux_flags },\
+ .div_cfg = &(struct muxdiv_cfg) { _div_offset, _div_shift, _div_width, _div_flags },\
+ .gate_cfg = &(struct gate_cfg) { _gate_offset, _gate_shift, _gate_flags },\
+ .flags = _flags,\
+}
+
+static const struct composite_clk_cfg sky1_audss_clks[] = {
+ /* dsp */
+ CFG(CLK_DSP_CLK,
+ "audss_dsp_clk",
+ dsp_clk_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_HIFI0, 0, 0,
+ 0),
+ CFG(CLK_DSP_BCLK,
+ "audss_dsp_bclk",
+ dsp_bclk_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ -1, 0, 0,
+ 0),
+ CFG(CLK_DSP_PBCLK,
+ "audss_dsp_pbclk",
+ dsp_pbclk_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ -1, 0, 0,
+ 0),
+ /* sram */
+ CFG(CLK_SRAM_AXI,
+ "audss_sram_axi",
+ sram_axi_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 16, 0,
+ 0),
+ /* hda */
+ CFG(CLK_HDA_SYS,
+ "audss_hda_sys",
+ hda_sys_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 14, 0,
+ 0),
+ CFG(CLK_HDA_HDA,
+ "audss_hda_hda",
+ hda_hda_parent,
+ NULL,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 14, 0,
+ 0),
+ /* dmac */
+ CFG(CLK_DMAC_AXI,
+ "audss_dmac_axi",
+ dmac_axi_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 15, 0,
+ 0),
+ /* wdg */
+ CFG(CLK_WDG_APB,
+ "audss_wdg_apb",
+ wdg_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 10, 0,
+ 0),
+ CFG(CLK_WDG_WDG,
+ "audss_wdg_wdg",
+ wdg_wdg_parent,
+ NULL,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 10, 0,
+ 0),
+ /* timer */
+ CFG(CLK_TIMER_APB,
+ "audss_timer_apb",
+ timer_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 11, 0,
+ 0),
+ CFG(CLK_TIMER_TIMER,
+ "audss_timer_timer",
+ timer_timer_parent,
+ NULL,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 11, 0,
+ 0),
+ /* mailbox: mb0(ap->dsp), mb1(dsp->ap) */
+ CFG(CLK_MB_0_APB,
+ "audss_mb_0_apb",
+ mailbox_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 12, 0,
+ 0),
+ CFG(CLK_MB_1_APB,
+ "audss_mb_1_apb",
+ mailbox_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ INFO_CLK_GATE, 13, 0,
+ 0),
+ /* i2s */
+ CFG(CLK_I2S0_APB,
+ "audss_i2s0_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 0, 0,
+ 0),
+ CFG(CLK_I2S1_APB,
+ "audss_i2s1_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 1, 0,
+ 0),
+ CFG(CLK_I2S2_APB,
+ "audss_i2s2_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 2, 0,
+ 0),
+ CFG(CLK_I2S3_APB,
+ "audss_i2s3_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 3, 0,
+ 0),
+ CFG(CLK_I2S4_APB,
+ "audss_i2s4_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 4, 0,
+ 0),
+ CFG(CLK_I2S5_APB,
+ "audss_i2s5_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 5, 0,
+ 0),
+ CFG(CLK_I2S6_APB,
+ "audss_i2s6_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 6, 0,
+ 0),
+ CFG(CLK_I2S7_APB,
+ "audss_i2s7_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 7, 0,
+ 0),
+ CFG(CLK_I2S8_APB,
+ "audss_i2s8_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 8, 0,
+ 0),
+ CFG(CLK_I2S9_APB,
+ "audss_i2s9_apb",
+ i2s_apb_parent,
+ NULL,
+ -1, 0, 0, 0,
+ INFO_CLK_DIV, 0, 2, 0,
+ INFO_CLK_GATE, 9, 0,
+ 0),
+ CFG(CLK_I2S0,
+ "audss_i2s0",
+ i2s0_parents,
+ NULL,
+ INFO_CLK_MUX, 0, 2, 0,
+ INFO_CLK_DIV, 2, 2, 0,
+ INFO_CLK_GATE, 0, 0,
+ 0),
+ CFG(CLK_I2S1,
+ "audss_i2s1",
+ i2s1_parents,
+ NULL,
+ INFO_CLK_MUX, 2, 2, 0,
+ INFO_CLK_DIV, 4, 2, 0,
+ INFO_CLK_GATE, 1, 0,
+ 0),
+ CFG(CLK_I2S2,
+ "audss_i2s2",
+ i2s2_parents,
+ NULL,
+ INFO_CLK_MUX, 4, 2, 0,
+ INFO_CLK_DIV, 6, 2, 0,
+ INFO_CLK_GATE, 2, 0,
+ 0),
+ CFG(CLK_I2S3,
+ "audss_i2s3",
+ i2s3_parents,
+ i2s3_mux_table,
+ INFO_CLK_MUX, 6, 2, 0,
+ INFO_CLK_DIV, 8, 2, 0,
+ INFO_CLK_GATE, 3, 0,
+ 0),
+ CFG(CLK_I2S4,
+ "audss_i2s4",
+ i2s4_parents,
+ i2s4_mux_table,
+ INFO_CLK_MUX, 8, 2, 0,
+ INFO_CLK_DIV, 10, 2, 0,
+ INFO_CLK_GATE, 4, 0,
+ 0),
+ CFG(CLK_I2S5,
+ "audss_i2s5",
+ i2s5_parents,
+ NULL,
+ INFO_CLK_MUX, 10, 2, 0,
+ INFO_CLK_DIV, 12, 2, 0,
+ INFO_CLK_GATE, 5, 0,
+ 0),
+ CFG(CLK_I2S6,
+ "audss_i2s6",
+ i2s6_parents,
+ NULL,
+ INFO_CLK_MUX, 12, 2, 0,
+ INFO_CLK_DIV, 14, 2, 0,
+ INFO_CLK_GATE, 6, 0,
+ 0),
+ CFG(CLK_I2S7,
+ "audss_i2s7",
+ i2s7_parents,
+ NULL,
+ INFO_CLK_MUX, 14, 2, 0,
+ INFO_CLK_DIV, 16, 2, 0,
+ INFO_CLK_GATE, 7, 0,
+ 0),
+ CFG(CLK_I2S8,
+ "audss_i2s8",
+ i2s8_parents,
+ NULL,
+ INFO_CLK_MUX, 16, 2, 0,
+ INFO_CLK_DIV, 18, 2, 0,
+ INFO_CLK_GATE, 8, 0,
+ 0),
+ CFG(CLK_I2S9,
+ "audss_i2s9",
+ i2s9_parents,
+ NULL,
+ INFO_CLK_MUX, 18, 2, 0,
+ INFO_CLK_DIV, 20, 2, 0,
+ INFO_CLK_GATE, 9, 0,
+ 0),
+ /* mclk */
+ CFG(CLK_MCLK0,
+ "audss_mclk0",
+ mclk_parents,
+ NULL,
+ INFO_MCLK, 5, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 0, 0,
+ 0),
+ CFG(CLK_MCLK1,
+ "audss_mclk1",
+ mclk_parents,
+ NULL,
+ INFO_MCLK, 6, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 1, 0,
+ 0),
+ CFG(CLK_MCLK2,
+ "audss_mclk2",
+ mclk_parents,
+ NULL,
+ INFO_MCLK, 7, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 2, 0,
+ 0),
+ CFG(CLK_MCLK3,
+ "audss_mclk3",
+ mclk_parents,
+ NULL,
+ INFO_MCLK, 8, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 3, 0,
+ 0),
+ CFG(CLK_MCLK4,
+ "audss_mclk4",
+ mclk_parents,
+ NULL,
+ INFO_MCLK, 9, 1, 0,
+ -1, 0, 0, 0,
+ INFO_MCLK, 4, 0,
+ 0),
+};
+
+struct sky1_audss_clks_devtype_data {
+ u32 (*reg_save)[2];
+ size_t reg_save_size;
+ const char * const *clk_names;
+ size_t clk_num;
+ const u32 *clk_rate_default;
+ const struct composite_clk_cfg *clk_cfg;
+ size_t clk_cfg_size;
+};
+
+static const struct regmap_config sky1_audss_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+struct sky1_audss_clks_priv {
+ struct device *dev;
+ struct regmap *regmap_cru;
+ struct reset_control *rst_noc;
+ struct clk *clks[SKY1_AUDSS_CLK_PARENTS_CNT];
+ const struct sky1_audss_clks_devtype_data *devtype_data;
+ spinlock_t lock;
+ struct clk_hw_onecell_data *clk_data;
+};
+
+#if IS_ENABLED(CONFIG_RESET_SKY1_AUDSS)
+
+static int sky1_audss_reset_controller_register(struct device *dev)
+{
+ struct auxiliary_device *adev;
+
+ if (!of_property_present(dev->of_node, "#reset-cells"))
+ return 0;
+
+ adev = devm_auxiliary_device_create(dev, "reset", NULL);
+ if (!adev)
+ return -ENODEV;
+
+ return 0;
+}
+
+#else
+
+static int sky1_audss_reset_controller_register(struct device *dev)
+{
+ return 0;
+}
+
+#endif
+
+/*
+ * clk_ops for audss clock mux/divider/gate
+ */
+struct sky1_clk_divider {
+ struct clk_divider div;
+ struct regmap *regmap;
+ int offset;
+};
+
+struct sky1_clk_gate {
+ struct clk_gate gate;
+ struct regmap *regmap;
+ int offset;
+};
+
+struct sky1_clk_mux {
+ struct clk_mux mux;
+ struct regmap *regmap;
+ int offset;
+};
+
+static inline struct sky1_clk_mux *to_sky1_clk_mux(struct clk_mux *mux)
+{
+ return container_of(mux, struct sky1_clk_mux, mux);
+}
+
+static u8 sky1_audss_clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux);
+ u32 val;
+
+ regmap_read(sky1_mux->regmap, sky1_mux->offset, &val);
+ val = val >> mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
+}
+
+static int sky1_audss_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
+ struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux);
+ unsigned long flags = 0;
+ u32 reg;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+ else
+ __acquire(mux->lock);
+
+ if (mux->flags & CLK_MUX_HIWORD_MASK) {
+ reg = mux->mask << (mux->shift + 16);
+ } else {
+ regmap_read(sky1_mux->regmap, sky1_mux->offset, ®);
+ reg &= ~(mux->mask << mux->shift);
+ }
+ val = val << mux->shift;
+ reg |= val;
+ regmap_write(sky1_mux->regmap, sky1_mux->offset, reg);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+ else
+ __release(mux->lock);
+
+ return 0;
+}
+
+static int sky1_audss_clk_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+
+ return clk_mux_determine_rate_flags(hw, req, mux->flags);
+}
+
+static const struct clk_ops sky1_audss_clk_mux_ops = {
+ .get_parent = sky1_audss_clk_mux_get_parent,
+ .set_parent = sky1_audss_clk_mux_set_parent,
+ .determine_rate = sky1_audss_clk_mux_determine_rate,
+};
+
+static inline struct sky1_clk_divider *to_sky1_clk_divider(struct clk_divider *div)
+{
+ return container_of(div, struct sky1_clk_divider, div);
+}
+
+static unsigned long sky1_audss_clk_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider);
+ unsigned int val;
+
+ regmap_read(sky1_div->regmap, sky1_div->offset, &val);
+ val = val >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_recalc_rate(hw, parent_rate, val, divider->table,
+ divider->flags, divider->width);
+}
+
+static int sky1_audss_clk_divider_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider);
+
+ /* if read only, just return current value */
+ if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+ u32 val;
+
+ regmap_read(sky1_div->regmap, sky1_div->offset, &val);
+ val = val >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_ro_determine_rate(hw, req, divider->table,
+ divider->width,
+ divider->flags, val);
+ }
+
+ return divider_determine_rate(hw, req, divider->table, divider->width,
+ divider->flags);
+}
+
+static int sky1_audss_clk_divider_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider);
+ int value;
+ unsigned long flags = 0;
+ u32 val;
+
+ value = divider_get_val(rate, parent_rate, divider->table,
+ divider->width, divider->flags);
+ if (value < 0)
+ return value;
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+ else
+ __acquire(divider->lock);
+
+ if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
+ val = clk_div_mask(divider->width) << (divider->shift + 16);
+ } else {
+ regmap_read(sky1_div->regmap, sky1_div->offset, &val);
+ val &= ~(clk_div_mask(divider->width) << divider->shift);
+ }
+ val |= (u32)value << divider->shift;
+ regmap_write(sky1_div->regmap, sky1_div->offset, val);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+ else
+ __release(divider->lock);
+
+ return 0;
+}
+
+static const struct clk_ops sky1_audss_clk_divider_ops = {
+ .recalc_rate = sky1_audss_clk_divider_recalc_rate,
+ .determine_rate = sky1_audss_clk_divider_determine_rate,
+ .set_rate = sky1_audss_clk_divider_set_rate,
+};
+
+static inline struct sky1_clk_gate *to_sky1_clk_gate(struct clk_gate *gate)
+{
+ return container_of(gate, struct sky1_clk_gate, gate);
+}
+
+static void sky1_audss_clk_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate);
+ int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
+ unsigned long flags = 0;
+ u32 reg;
+
+ set ^= enable;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+ else
+ __acquire(gate->lock);
+
+ if (gate->flags & CLK_GATE_HIWORD_MASK) {
+ reg = BIT(gate->bit_idx + 16);
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ } else {
+ regmap_read(sky1_gate->regmap, sky1_gate->offset, ®);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ else
+ reg &= ~BIT(gate->bit_idx);
+ }
+
+ regmap_write(sky1_gate->regmap, sky1_gate->offset, reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+ else
+ __release(gate->lock);
+}
+
+static int sky1_audss_clk_gate_enable(struct clk_hw *hw)
+{
+ sky1_audss_clk_gate_endisable(hw, 1);
+
+ return 0;
+}
+
+static void sky1_audss_clk_gate_disable(struct clk_hw *hw)
+{
+ sky1_audss_clk_gate_endisable(hw, 0);
+}
+
+static int sky1_audss_clk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate);
+ u32 reg;
+
+ regmap_read(sky1_gate->regmap, sky1_gate->offset, ®);
+
+ /* if a set bit disables this clk, flip it before masking */
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ reg ^= BIT(gate->bit_idx);
+
+ reg &= BIT(gate->bit_idx);
+
+ return !!reg;
+}
+
+static const struct clk_ops sky1_audss_clk_gate_ops = {
+ .enable = sky1_audss_clk_gate_enable,
+ .disable = sky1_audss_clk_gate_disable,
+ .is_enabled = sky1_audss_clk_gate_is_enabled,
+};
+
+static struct clk_hw *sky1_audss_clk_register(struct device *dev,
+ const char *name,
+ const char * const *parent_names,
+ int num_parents,
+ struct regmap *regmap,
+ const u32 *mux_table,
+ struct muxdiv_cfg *mux_cfg,
+ struct muxdiv_cfg *div_cfg,
+ struct gate_cfg *gate_cfg,
+ unsigned long flags,
+ spinlock_t *lock)
+{
+ const struct clk_ops *sky1_mux_ops = NULL;
+ const struct clk_ops *sky1_div_ops = NULL;
+ const struct clk_ops *sky1_gate_ops = NULL;
+ struct clk_hw *hw = ERR_PTR(-ENOMEM);
+ struct sky1_clk_divider *sky1_div = NULL;
+ struct sky1_clk_gate *sky1_gate = NULL;
+ struct sky1_clk_mux *sky1_mux = NULL;
+
+ if (mux_cfg->offset >= 0) {
+ sky1_mux = devm_kzalloc(dev, sizeof(*sky1_mux), GFP_KERNEL);
+ if (!sky1_mux)
+ return ERR_PTR(-ENOMEM);
+
+ sky1_mux->mux.reg = NULL;
+ sky1_mux->mux.shift = mux_cfg->shift;
+ sky1_mux->mux.mask = BIT(mux_cfg->width) - 1;
+ sky1_mux->mux.flags = mux_cfg->flags;
+ sky1_mux->mux.table = mux_table;
+ sky1_mux->mux.lock = lock;
+ sky1_mux_ops = &sky1_audss_clk_mux_ops;
+ sky1_mux->regmap = regmap;
+ sky1_mux->offset = mux_cfg->offset;
+ }
+
+ if (div_cfg->offset >= 0) {
+ sky1_div = devm_kzalloc(dev, sizeof(*sky1_div), GFP_KERNEL);
+ if (!sky1_div)
+ return ERR_PTR(-ENOMEM);
+
+ sky1_div->div.reg = NULL;
+ sky1_div->div.shift = div_cfg->shift;
+ sky1_div->div.width = div_cfg->width;
+ sky1_div->div.flags = div_cfg->flags | CLK_DIVIDER_POWER_OF_TWO;
+ sky1_div->div.lock = lock;
+ sky1_div_ops = &sky1_audss_clk_divider_ops;
+ sky1_div->regmap = regmap;
+ sky1_div->offset = div_cfg->offset;
+ }
+
+ if (gate_cfg->offset >= 0) {
+ sky1_gate = devm_kzalloc(dev, sizeof(*sky1_gate), GFP_KERNEL);
+ if (!sky1_gate)
+ return ERR_PTR(-ENOMEM);
+
+ sky1_gate->gate.reg = NULL;
+ sky1_gate->gate.bit_idx = gate_cfg->shift;
+ sky1_gate->gate.flags = gate_cfg->flags;
+ sky1_gate->gate.lock = lock;
+ sky1_gate_ops = &sky1_audss_clk_gate_ops;
+ sky1_gate->regmap = regmap;
+ sky1_gate->offset = gate_cfg->offset;
+ }
+
+ hw = clk_hw_register_composite(dev, name, parent_names, num_parents,
+ sky1_mux ? &sky1_mux->mux.hw : NULL, sky1_mux_ops,
+ sky1_div ? &sky1_div->div.hw : NULL, sky1_div_ops,
+ sky1_gate ? &sky1_gate->gate.hw : NULL, sky1_gate_ops,
+ flags);
+ if (IS_ERR(hw)) {
+ dev_err(dev, "register %s clock failed with err = %ld\n",
+ name, PTR_ERR(hw));
+ return hw;
+ }
+
+ return hw;
+}
+
+static int sky1_audss_clks_get(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i;
+
+ for (i = 0; i < devtype_data->clk_num; i++) {
+ priv->clks[i] = devm_clk_get(priv->dev, devtype_data->clk_names[i]);
+ if (IS_ERR(priv->clks[i]))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->clks[i]),
+ "failed to get clock %s", devtype_data->clk_names[i]);
+ }
+
+ return 0;
+}
+
+static int sky1_audss_clks_enable(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i, err;
+
+ for (i = 0; i < devtype_data->clk_num; i++) {
+ err = clk_prepare_enable(priv->clks[i]);
+ if (err) {
+ dev_err(priv->dev, "failed to enable clock %s\n",
+ devtype_data->clk_names[i]);
+ goto err_clks;
+ }
+ }
+
+ return 0;
+
+err_clks:
+ while (--i >= 0)
+ clk_disable_unprepare(priv->clks[i]);
+
+ return err;
+}
+
+static void sky1_audss_clks_disable(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i;
+
+ for (i = 0; i < devtype_data->clk_num; i++)
+ clk_disable_unprepare(priv->clks[i]);
+}
+
+static int sky1_audss_clks_set_rate(struct sky1_audss_clks_priv *priv)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i, err;
+
+ for (i = 0; i < devtype_data->clk_num; i++) {
+ err = clk_set_rate(priv->clks[i], devtype_data->clk_rate_default[i]);
+ if (err) {
+ dev_err(priv->dev, "failed to set clock rate %s\n",
+ devtype_data->clk_names[i]);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* register sky1 audio subsystem clocks */
+static int sky1_audss_clk_probe(struct platform_device *pdev)
+{
+ const struct sky1_audss_clks_devtype_data *devtype_data;
+ struct sky1_audss_clks_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct clk_hw **clk_table;
+ void __iomem *base;
+ int i, ret;
+
+ devtype_data = device_get_match_data(dev);
+ if (!devtype_data)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ priv->clk_data = devm_kzalloc(dev,
+ struct_size(priv->clk_data, hws, SKY1_AUDSS_NUM_CLKS),
+ GFP_KERNEL);
+ if (!priv->clk_data)
+ return -ENOMEM;
+
+ priv->clk_data->num = SKY1_AUDSS_NUM_CLKS;
+ clk_table = priv->clk_data->hws;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap_cru = devm_regmap_init_mmio(dev, base, &sky1_audss_regmap_config);
+ if (IS_ERR(priv->regmap_cru))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap_cru),
+ "failed to initialize regmap\n");
+
+ priv->dev = dev;
+ priv->devtype_data = devtype_data;
+
+ priv->rst_noc = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(priv->rst_noc))
+ return dev_err_probe(dev, PTR_ERR(priv->rst_noc),
+ "failed to get audss noc reset");
+
+ reset_control_deassert(priv->rst_noc);
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = sky1_audss_clks_get(priv);
+ if (ret)
+ goto err_pm;
+
+ ret = sky1_audss_clks_enable(priv);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ goto err_pm;
+ }
+
+ ret = sky1_audss_clks_set_rate(priv);
+ if (ret) {
+ dev_err(dev, "failed to set clocks rate\n");
+ goto fail_clks_set;
+ }
+
+ /* audio_clk4 clock fixed divider */
+ clk_table[CLK_AUD_CLK4_DIV2] =
+ devm_clk_hw_register_fixed_factor(dev,
+ "audio_clk4_div2",
+ "audio_clk4",
+ 0,
+ 1, 2);
+ if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV2])) {
+ ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV2]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV2, ret);
+ goto fail_fixed_clk;
+ }
+
+ clk_table[CLK_AUD_CLK4_DIV4] =
+ devm_clk_hw_register_fixed_factor(dev,
+ "audio_clk4_div4",
+ "audio_clk4",
+ 0,
+ 1, 4);
+ if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV4])) {
+ ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV4]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV4, ret);
+ goto fail_fixed_clk;
+ }
+
+ /* audio_clk5 clock fixed divider */
+ clk_table[CLK_AUD_CLK5_DIV2] =
+ devm_clk_hw_register_fixed_factor(dev,
+ "audio_clk5_div2",
+ "audio_clk5",
+ 0,
+ 1, 2);
+ if (IS_ERR(clk_table[CLK_AUD_CLK5_DIV2])) {
+ ret = PTR_ERR(clk_table[CLK_AUD_CLK5_DIV2]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK5_DIV2, ret);
+ goto fail_fixed_clk;
+ }
+
+ for (i = 0; i < devtype_data->clk_cfg_size; i++) {
+ clk_table[devtype_data->clk_cfg[i].id] =
+ sky1_audss_clk_register(dev,
+ devtype_data->clk_cfg[i].name,
+ devtype_data->clk_cfg[i].parent_names,
+ devtype_data->clk_cfg[i].num_parents,
+ priv->regmap_cru,
+ devtype_data->clk_cfg[i].mux_table,
+ devtype_data->clk_cfg[i].mux_cfg,
+ devtype_data->clk_cfg[i].div_cfg,
+ devtype_data->clk_cfg[i].gate_cfg,
+ devtype_data->clk_cfg[i].flags,
+ &priv->lock);
+ if (IS_ERR(clk_table[devtype_data->clk_cfg[i].id])) {
+ ret = PTR_ERR(clk_table[devtype_data->clk_cfg[i].id]);
+ dev_err(dev, "failed to register clock %d, ret:%d\n",
+ devtype_data->clk_cfg[i].id, ret);
+ goto fail_array_clk;
+ }
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv->clk_data);
+ if (ret) {
+ dev_err(dev, "failed to add clock provider: %d\n", ret);
+ goto fail_register;
+ }
+
+ ret = sky1_audss_reset_controller_register(dev);
+ if (ret)
+ goto fail_register;
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+
+fail_register:
+fail_array_clk:
+ while (i--)
+ clk_hw_unregister_composite(clk_table[devtype_data->clk_cfg[i].id]);
+fail_fixed_clk:
+fail_clks_set:
+ pm_runtime_put_sync(dev);
+err_pm:
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void sky1_audss_clk_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev);
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ int i = 0;
+
+ for (i = 0; i < devtype_data->clk_cfg_size; i++)
+ clk_hw_unregister_composite(priv->clk_data->hws[devtype_data->clk_cfg[i].id]);
+
+ if (!pm_runtime_status_suspended(dev))
+ pm_runtime_force_suspend(dev);
+
+ pm_runtime_disable(dev);
+}
+
+static int __maybe_unused sky1_audss_clk_runtime_suspend(struct device *dev)
+{
+ struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev);
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ for (i = 0; i < devtype_data->reg_save_size; i++)
+ regmap_read(priv->regmap_cru,
+ devtype_data->reg_save[i][0], &devtype_data->reg_save[i][1]);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ sky1_audss_clks_disable(priv);
+
+ return reset_control_assert(priv->rst_noc);
+}
+
+static int __maybe_unused sky1_audss_clk_runtime_resume(struct device *dev)
+{
+ struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev);
+ const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
+ unsigned long flags;
+ int i, ret;
+
+ ret = reset_control_deassert(priv->rst_noc);
+ if (ret)
+ return ret;
+
+ ret = sky1_audss_clks_enable(priv);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ for (i = 0; i < devtype_data->reg_save_size; i++)
+ regmap_write(priv->regmap_cru,
+ devtype_data->reg_save[i][0], devtype_data->reg_save[i][1]);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static const struct dev_pm_ops sky1_audss_clk_pm_ops = {
+ SET_RUNTIME_PM_OPS(sky1_audss_clk_runtime_suspend,
+ sky1_audss_clk_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct sky1_audss_clks_devtype_data sky1_devtype_data = {
+ .reg_save = sky1_reg_save,
+ .reg_save_size = ARRAY_SIZE(sky1_reg_save),
+ .clk_names = sky1_audss_clk_names,
+ .clk_num = ARRAY_SIZE(sky1_audss_clk_names),
+ .clk_rate_default = sky1_clk_rate_default,
+ .clk_cfg = sky1_audss_clks,
+ .clk_cfg_size = ARRAY_SIZE(sky1_audss_clks),
+};
+
+static const struct of_device_id sky1_audss_clk_of_match[] = {
+ { .compatible = "cix,sky1-audss-cru", .data = &sky1_devtype_data, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sky1_audss_clk_of_match);
+
+static struct platform_driver sky1_audss_clk_driver = {
+ .probe = sky1_audss_clk_probe,
+ .remove = sky1_audss_clk_remove,
+ .driver = {
+ .name = "sky1-audss-clk",
+ .suppress_bind_attrs = true,
+ .of_match_table = sky1_audss_clk_of_match,
+ .pm = &sky1_audss_clk_pm_ops,
+ },
+};
+module_platform_driver(sky1_audss_clk_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joakim Zhang <joakim.zhang@cixtech.com>");
+MODULE_DESCRIPTION("Cixtech Sky1 Audio Subsystem Clock Controller Driver");
--
2.50.1
^ permalink raw reply related
* [PATCH v6 1/4] dt-bindings: soc: cix: add sky1 audss cru controller
From: joakim.zhang @ 2026-06-23 7:08 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260623070805.211019-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU)
groups clock muxing, gating and block-level software reset control
in a single register block.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
.../bindings/soc/cix/cix,sky1-audss-cru.yaml | 92 +++++++++++++++++++
.../dt-bindings/clock/cix,sky1-audss-cru.h | 60 ++++++++++++
.../dt-bindings/reset/cix,sky1-audss-cru.h | 25 +++++
3 files changed, 177 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
create mode 100644 include/dt-bindings/clock/cix,sky1-audss-cru.h
create mode 100644 include/dt-bindings/reset/cix,sky1-audss-cru.h
diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
new file mode 100644
index 000000000000..50dd0593e1d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/cix/cix,sky1-audss-cru.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cix Sky1 audio subsystem clock and reset unit
+
+maintainers:
+ - Joakim Zhang <joakim.zhang@cixtech.com>
+
+description: |
+ The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU) groups
+ audio-related clock muxing, gating and block-level software reset control
+ in a single register block.
+
+ A single device node exposes both the clock controller and software reset
+ lines. The clock driver registers as a platform driver; the reset controller
+ is registered by an auxiliary driver bound from the clock driver.
+
+ Four SoC-level reference clocks listed in clocks/clock-names feed the AUDSS
+ clock tree. Internal AUDSS clocks are exposed via #clock-cells; indices are
+ defined in include/dt-bindings/clock/cix,sky1-audss-cru.h.
+
+ Block-level software reset indices are exposed via #reset-cells; indices
+ are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h.
+
+ The SoC syscon NoC (or bus) reset is described via resets. The audio
+ subsystem power domain is described via power-domains.
+
+properties:
+ compatible:
+ const: cix,sky1-audss-cru
+
+ reg:
+ maxItems: 1
+
+ '#clock-cells':
+ const: 1
+ description:
+ Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss-cru.h.
+
+ '#reset-cells':
+ const: 1
+ description:
+ Reset indices are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h.
+
+ clocks:
+ items:
+ - description: I2S parent clock for sampling rates multiple of 8kHz.
+ - description: I2S parent clock for sampling rates multiple of 11.025kHz.
+ - description: Clock feeding most devices in AUDSS (NOC, DSP, SRAM, HDA, DMAC, I2S, and mailbox).
+ - description: Clock feeding HDA, timer and watchdog, which is a dedicated 48 MHz clock.
+
+ clock-names:
+ items:
+ - const: x8k
+ - const: x11k
+ - const: sys
+ - const: 48m
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - '#clock-cells'
+ - '#reset-cells'
+ - clocks
+ - clock-names
+ - power-domains
+ - resets
+
+additionalProperties: false
+
+examples:
+ - |
+ audss_cru: clock-controller@7110000 {
+ compatible = "cix,sky1-audss-cru";
+ reg = <0x7110000 0x10000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ clocks = <&scmi_clk 76>, <&scmi_clk 78>,
+ <&scmi_clk 70>, <&scmi_clk 71>;
+ clock-names = "x8k", "x11k", "sys", "48m";
+ power-domains = <&smc_devpd 0>;
+ resets = <&s5_syscon 31>;
+ };
diff --git a/include/dt-bindings/clock/cix,sky1-audss-cru.h b/include/dt-bindings/clock/cix,sky1-audss-cru.h
new file mode 100644
index 000000000000..8c58ef8bf682
--- /dev/null
+++ b/include/dt-bindings/clock/cix,sky1-audss-cru.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright 2026 Cix Technology Group Co., Ltd.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_CIX_SKY1_AUDSS_CRU_H
+#define _DT_BINDINGS_CLOCK_CIX_SKY1_AUDSS_CRU_H
+
+#define CLK_AUD_CLK4_DIV2 0
+#define CLK_AUD_CLK4_DIV4 1
+#define CLK_AUD_CLK5_DIV2 2
+
+#define CLK_DSP_CLK 3
+#define CLK_DSP_BCLK 4
+#define CLK_DSP_PBCLK 5
+
+#define CLK_SRAM_AXI 6
+
+#define CLK_HDA_SYS 7
+#define CLK_HDA_HDA 8
+
+#define CLK_DMAC_AXI 9
+
+#define CLK_WDG_APB 10
+#define CLK_WDG_WDG 11
+
+#define CLK_TIMER_APB 12
+#define CLK_TIMER_TIMER 13
+
+#define CLK_MB_0_APB 14 /* MB0: ap->dsp */
+#define CLK_MB_1_APB 15 /* MB1: dsp->ap */
+
+#define CLK_I2S0_APB 16
+#define CLK_I2S1_APB 17
+#define CLK_I2S2_APB 18
+#define CLK_I2S3_APB 19
+#define CLK_I2S4_APB 20
+#define CLK_I2S5_APB 21
+#define CLK_I2S6_APB 22
+#define CLK_I2S7_APB 23
+#define CLK_I2S8_APB 24
+#define CLK_I2S9_APB 25
+#define CLK_I2S0 26
+#define CLK_I2S1 27
+#define CLK_I2S2 28
+#define CLK_I2S3 29
+#define CLK_I2S4 30
+#define CLK_I2S5 31
+#define CLK_I2S6 32
+#define CLK_I2S7 33
+#define CLK_I2S8 34
+#define CLK_I2S9 35
+
+#define CLK_MCLK0 36
+#define CLK_MCLK1 37
+#define CLK_MCLK2 38
+#define CLK_MCLK3 39
+#define CLK_MCLK4 40
+
+#endif
diff --git a/include/dt-bindings/reset/cix,sky1-audss-cru.h b/include/dt-bindings/reset/cix,sky1-audss-cru.h
new file mode 100644
index 000000000000..55e9f3797b30
--- /dev/null
+++ b/include/dt-bindings/reset/cix,sky1-audss-cru.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright 2026 Cix Technology Group Co., Ltd.
+ */
+#ifndef DT_BINDINGS_RESET_CIX_SKY1_AUDSS_CRU_H
+#define DT_BINDINGS_RESET_CIX_SKY1_AUDSS_CRU_H
+
+#define AUDSS_I2S0_SW_RST 0
+#define AUDSS_I2S1_SW_RST 1
+#define AUDSS_I2S2_SW_RST 2
+#define AUDSS_I2S3_SW_RST 3
+#define AUDSS_I2S4_SW_RST 4
+#define AUDSS_I2S5_SW_RST 5
+#define AUDSS_I2S6_SW_RST 6
+#define AUDSS_I2S7_SW_RST 7
+#define AUDSS_I2S8_SW_RST 8
+#define AUDSS_I2S9_SW_RST 9
+#define AUDSS_WDT_SW_RST 10
+#define AUDSS_TIMER_SW_RST 11
+#define AUDSS_MB0_SW_RST 12
+#define AUDSS_MB1_SW_RST 13
+#define AUDSS_HDA_SW_RST 14
+#define AUDSS_DMAC_SW_RST 15
+
+#endif
--
2.50.1
^ permalink raw reply related
* [PATCH v6 3/4] reset: cix: add sky1 audss auxiliary reset driver
From: joakim.zhang @ 2026-06-23 7:08 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
In-Reply-To: <20260623070805.211019-1-joakim.zhang@cixtech.com>
From: Joakim Zhang <joakim.zhang@cixtech.com>
Add an auxiliary reset controller driver for the AUDSS CRU. Sixteen
software reset lines for audio subsystem peripherals are controlled
through one register in the CRU register map.
The driver is created by the AUDSS clock platform driver and registers
the reset controller on the CRU device node.
Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
---
drivers/reset/Kconfig | 14 +++
drivers/reset/Makefile | 1 +
drivers/reset/reset-sky1-audss.c | 192 +++++++++++++++++++++++++++++++
3 files changed, 207 insertions(+)
create mode 100644 drivers/reset/reset-sky1-audss.c
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index d009eb0849a3..f74859b292ae 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -300,6 +300,20 @@ config RESET_SKY1
help
This enables the reset controller for Cix Sky1.
+config RESET_SKY1_AUDSS
+ tristate "Cix Sky1 Audio Subsystem reset controller"
+ depends on ARCH_CIX || COMPILE_TEST
+ select AUXILIARY_BUS
+ select REGMAP_MMIO
+ default CLK_SKY1_AUDSS
+ help
+ Support for block-level software reset lines in the Cix Sky1
+ Audio Subsystem (AUDSS) Clock and Reset Unit. Sixteen reset
+ outputs for audio peripherals are controlled through the CRU
+ register map. The driver binds as an auxiliary device from
+ the AUDSS clock driver. Say M or Y here if you want to build
+ this driver.
+
config RESET_SOCFPGA
bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA)
default ARM && ARCH_INTEL_SOCFPGA
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 3e52569bd276..e81407ea3e29 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_SKY1) += reset-sky1.o
+obj-$(CONFIG_RESET_SKY1_AUDSS) += reset-sky1-audss.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-sky1-audss.c b/drivers/reset/reset-sky1-audss.c
new file mode 100644
index 000000000000..20870f37d7d7
--- /dev/null
+++ b/drivers/reset/reset-sky1-audss.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cix Sky1 Audio Subsystem reset controller driver
+ *
+ * Copyright 2026 Cix Technology Group Co., Ltd.
+ */
+
+#include <dt-bindings/reset/cix,sky1-audss-cru.h>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#define SKY1_RESET_SLEEP_MIN_US 50
+#define SKY1_RESET_SLEEP_MAX_US 100
+
+#define AUDSS_SW_RST 0x78
+
+struct sky1_audss_reset_map {
+ unsigned int offset;
+ unsigned int mask;
+};
+
+struct sky1_audss_reset {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+ const struct sky1_audss_reset_map *map;
+};
+
+static const struct sky1_audss_reset_map sky1_audss_reset_map[] = {
+ [AUDSS_I2S0_SW_RST] = { AUDSS_SW_RST, BIT(0) },
+ [AUDSS_I2S1_SW_RST] = { AUDSS_SW_RST, BIT(1) },
+ [AUDSS_I2S2_SW_RST] = { AUDSS_SW_RST, BIT(2) },
+ [AUDSS_I2S3_SW_RST] = { AUDSS_SW_RST, BIT(3) },
+ [AUDSS_I2S4_SW_RST] = { AUDSS_SW_RST, BIT(4) },
+ [AUDSS_I2S5_SW_RST] = { AUDSS_SW_RST, BIT(5) },
+ [AUDSS_I2S6_SW_RST] = { AUDSS_SW_RST, BIT(6) },
+ [AUDSS_I2S7_SW_RST] = { AUDSS_SW_RST, BIT(7) },
+ [AUDSS_I2S8_SW_RST] = { AUDSS_SW_RST, BIT(8) },
+ [AUDSS_I2S9_SW_RST] = { AUDSS_SW_RST, BIT(9) },
+ [AUDSS_WDT_SW_RST] = { AUDSS_SW_RST, BIT(10) },
+ [AUDSS_TIMER_SW_RST] = { AUDSS_SW_RST, BIT(11) },
+ [AUDSS_MB0_SW_RST] = { AUDSS_SW_RST, BIT(12) },
+ [AUDSS_MB1_SW_RST] = { AUDSS_SW_RST, BIT(13) },
+ [AUDSS_HDA_SW_RST] = { AUDSS_SW_RST, BIT(14) },
+ [AUDSS_DMAC_SW_RST] = { AUDSS_SW_RST, BIT(15) },
+};
+
+static struct sky1_audss_reset *to_sky1_audss_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct sky1_audss_reset, rcdev);
+}
+
+static int sky1_audss_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct sky1_audss_reset *priv = to_sky1_audss_reset(rcdev);
+ const struct sky1_audss_reset_map *signal = &priv->map[id];
+ unsigned int value = assert ? 0 : signal->mask;
+
+ return regmap_update_bits(priv->regmap, signal->offset, signal->mask, value);
+}
+
+static int sky1_audss_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ sky1_audss_reset_set(rcdev, id, true);
+ usleep_range(SKY1_RESET_SLEEP_MIN_US, SKY1_RESET_SLEEP_MAX_US);
+ return 0;
+}
+
+static int sky1_audss_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ sky1_audss_reset_set(rcdev, id, false);
+ usleep_range(SKY1_RESET_SLEEP_MIN_US, SKY1_RESET_SLEEP_MAX_US);
+ return 0;
+}
+
+static int sky1_audss_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ sky1_audss_reset_assert(rcdev, id);
+ sky1_audss_reset_deassert(rcdev, id);
+ return 0;
+}
+
+static int sky1_audss_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct sky1_audss_reset *priv = to_sky1_audss_reset(rcdev);
+ const struct sky1_audss_reset_map *signal = &priv->map[id];
+ unsigned int value;
+
+ regmap_read(priv->regmap, signal->offset, &value);
+ return !!(value & signal->mask);
+}
+
+static const struct reset_control_ops sky1_audss_reset_ops = {
+ .reset = sky1_audss_reset,
+ .assert = sky1_audss_reset_assert,
+ .deassert = sky1_audss_reset_deassert,
+ .status = sky1_audss_reset_status,
+};
+
+static const struct regmap_config sky1_audss_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static void sky1_audss_reset_iounmap(void *data)
+{
+ iounmap(data);
+}
+
+static int sky1_audss_reset_get_regmap(struct sky1_audss_reset *priv)
+{
+ struct device *dev = priv->rcdev.dev;
+ void __iomem *base;
+ int ret;
+
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (priv->regmap)
+ return 0;
+
+ base = of_iomap(dev->parent->of_node, 0);
+ if (!base)
+ return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
+
+ ret = devm_add_action_or_reset(dev, sky1_audss_reset_iounmap, base);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register iounmap action\n");
+
+ priv->regmap = devm_regmap_init_mmio(dev, base, &sky1_audss_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "failed to initialize regmap\n");
+
+ return 0;
+}
+
+static int sky1_audss_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct sky1_audss_reset *priv;
+ struct device *dev = &adev->dev;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->map = sky1_audss_reset_map;
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = ARRAY_SIZE(sky1_audss_reset_map);
+ priv->rcdev.ops = &sky1_audss_reset_ops;
+ priv->rcdev.of_node = dev->parent->of_node;
+ priv->rcdev.dev = dev;
+ priv->rcdev.of_reset_n_cells = 1;
+
+ dev_set_drvdata(dev, priv);
+
+ ret = sky1_audss_reset_get_regmap(priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regmap\n");
+
+ return devm_reset_controller_register(dev, &priv->rcdev);
+}
+
+static const struct auxiliary_device_id sky1_audss_reset_ids[] = {
+ { .name = "clk_sky1_audss.reset" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, sky1_audss_reset_ids);
+
+static struct auxiliary_driver sky1_audss_reset_driver = {
+ .probe = sky1_audss_reset_probe,
+ .id_table = sky1_audss_reset_ids,
+};
+
+module_auxiliary_driver(sky1_audss_reset_driver);
+
+MODULE_AUTHOR("Joakim Zhang <joakim.zhang@cixtech.com>");
+MODULE_DESCRIPTION("Cix Sky1 Audio Subsystem reset driver");
+MODULE_LICENSE("GPL");
--
2.50.1
^ permalink raw reply related
* [PATCH v6 0/4] Add Cix Sky1 AUDSS clock and reset support
From: joakim.zhang @ 2026-06-23 7:08 UTC (permalink / raw)
To: mturquette, sboyd, bmasney, robh, krzk+dt, conor+dt, p.zabel,
gary.yang
Cc: cix-kernel-upstream, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Joakim Zhang
From: Joakim Zhang <joakim.zhang@cixtech.com>
The Cix Sky1 Audio Subsystem (AUDSS) groups audio-related blocks such as
HDA, I2S, DSP, DMA, mailboxes, watchdog and timer behind one Clock and
Reset Unit (CRU). The CRU is a single MMIO register block that provides
clock muxing, gating and block-level software reset lines for those
peripherals.
Clock and reset support are submitted in one series because they belong
to the same hardware block and share one devicetree node
(cix,sky1-audss-cru). The binding, clock indices and reset indices are
defined together; the clock driver maps the CRU and instantiates the
reset controller as an auxiliary driver on that node. Splitting clk and
reset across separate series would leave neither side self-contained: the
DTS node needs both providers, and the reset driver has no standalone
probe path without the clock driver.
---
ChangeLogs:
v5->v6:
* rename dt-bindings headers to cix,sky1-audss-cru.h to match compatible
* drop status = "okay" from audss_cru node in sky1.dtsi
v4->v5:
* refactor the driver, using platform_driver for clk and auxiliary_driver
for reset.
v3->v4:
* move both power domain and resets into parset node (audss_cru)
* remove "simple-mfd", and change to populate the child node
* cix,sky1-audss.h -> cix,sky1-audss-clock.h
v2->v3:
* clk part:
* devm_reset_control_get()->devm_reset_control_get_exclusive()
* assert noc reset from suspend
* clock parents changes from 6 to 4, and rename the clock names,
explain more about this: confirm with our designer, In fact,
there are 6 clock sources going into the audio subsystem. audio_clk1
and audio_clk3 are redundant in design and are not actually needed
in practice, so they are not shown here.
* refine clocks and clock-names property
* add detailed description of clocks
* drop parent node from clk binding
* drop define AUDSS_MAX_CLKS
* reset part:
* rename reset signal macro, remove _N
* drop SKY1_AUDSS_SW_RESET_NUM
* switching to compatible-style of defining subnodes in parent schema
v1->v2:
* remove audss_rst device node since it doesn't has resource, and
move to reset-sky1.c driver.
* remove hda related which would be sent after this patch set accepted
* soc componnet is okay by default from dtsi
* fix for audss clk driver:
* remove "comment "Clock options for Cixtech audss:""
* add select MFD_SYSCON
* move lock and clk_data into struct sky1_audss_clks_priv
* const char *name -> const char * const * name
* remove CLK_GET_RATE_NOCACHE
* divicer -> divider
* Reverse Christmas tree order
* return reg ? 1 : 0; -> return !!reg;
* return ERR_CAST(hw); -> return hw;
* of_device_get_match_data(dev) -> device_get_match_data()
* add lock from runtime_suspend/resume
* loop to more mailing lists
Joakim Zhang (4):
dt-bindings: soc: cix: add sky1 audss cru controller
clk: cix: add sky1 audss clock controller
reset: cix: add sky1 audss auxiliary reset driver
arm64: dts: cix: sky1: add audss cru
.../bindings/soc/cix/cix,sky1-audss-cru.yaml | 92 ++
arch/arm64/boot/dts/cix/sky1.dtsi | 18 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/cix/Kconfig | 16 +
drivers/clk/cix/Makefile | 3 +
drivers/clk/cix/clk-sky1-audss.c | 1201 +++++++++++++++++
drivers/reset/Kconfig | 14 +
drivers/reset/Makefile | 1 +
drivers/reset/reset-sky1-audss.c | 192 +++
.../dt-bindings/clock/cix,sky1-audss-cru.h | 60 +
.../dt-bindings/reset/cix,sky1-audss-cru.h | 25 +
12 files changed, 1624 insertions(+)
create mode 100644 Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
create mode 100644 drivers/clk/cix/Kconfig
create mode 100644 drivers/clk/cix/Makefile
create mode 100644 drivers/clk/cix/clk-sky1-audss.c
create mode 100644 drivers/reset/reset-sky1-audss.c
create mode 100644 include/dt-bindings/clock/cix,sky1-audss-cru.h
create mode 100644 include/dt-bindings/reset/cix,sky1-audss-cru.h
--
2.50.1
^ permalink raw reply
* RE: [PATCH v5 4/4] arm64: dts: cix: sky1: add audss cru
From: Joakim Zhang @ 2026-06-23 7:07 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: mturquette@baylibre.com, sboyd@kernel.org, bmasney@redhat.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
p.zabel@pengutronix.de, Gary Yang, cix-kernel-upstream,
linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20260622-dramatic-worm-of-radiance-adf731@quoll>
Hi,
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Monday, June 22, 2026 5:03 PM
> To: Joakim Zhang <joakim.zhang@cixtech.com>
> Cc: mturquette@baylibre.com; sboyd@kernel.org; bmasney@redhat.com;
> robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org;
> p.zabel@pengutronix.de; Gary Yang <gary.yang@cixtech.com>; cix-kernel-
> upstream <cix-kernel-upstream@cixtech.com>; linux-clk@vger.kernel.org;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH v5 4/4] arm64: dts: cix: sky1: add audss cru
>
> EXTERNAL EMAIL
>
> On Mon, Jun 22, 2026 at 10:25:20AM +0800, joakim.zhang@cixtech.com wrote:
> >
> > + audss_cru: clock-controller@7110000 {
> > + compatible = "cix,sky1-audss-cru";
> > + reg = <0x0 0x07110000 0x0 0x10000>;
> > + #clock-cells = <1>;
> > + #reset-cells = <1>;
> > + clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>,
> > + <&scmi_clk CLK_TREE_AUDIO_CLK2>,
> > + <&scmi_clk CLK_TREE_AUDIO_CLK4>,
> > + <&scmi_clk CLK_TREE_AUDIO_CLK5>;
> > + clock-names = "x8k", "x11k", "sys", "48m";
> > + power-domains = <&smc_devpd SKY1_PD_AUDIO>;
> > + resets = <&s5_syscon SKY1_AUDIO_HIFI5_NOC_RESET_N>;
>
> > + status = "okay";
>
> Drop.
Ok.
Thanks,
Joakim
^ permalink raw reply
* RE: [PATCH v5 1/4] dt-bindings: soc: cix: add sky1 audss cru controller
From: Joakim Zhang @ 2026-06-23 7:07 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: mturquette@baylibre.com, sboyd@kernel.org, bmasney@redhat.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
p.zabel@pengutronix.de, Gary Yang, cix-kernel-upstream,
linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20260622-handsome-kagu-from-jupiter-8ebe05@quoll>
Hi,
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Monday, June 22, 2026 5:02 PM
> To: Joakim Zhang <joakim.zhang@cixtech.com>
> Cc: mturquette@baylibre.com; sboyd@kernel.org; bmasney@redhat.com;
> robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org;
> p.zabel@pengutronix.de; Gary Yang <gary.yang@cixtech.com>; cix-kernel-
> upstream <cix-kernel-upstream@cixtech.com>; linux-clk@vger.kernel.org;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH v5 1/4] dt-bindings: soc: cix: add sky1 audss cru controller
>
> EXTERNAL EMAIL
>
> On Mon, Jun 22, 2026 at 10:25:17AM +0800, joakim.zhang@cixtech.com wrote:
> > From: Joakim Zhang <joakim.zhang@cixtech.com>
> >
> > The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU) groups
> > clock muxing, gating and block-level software reset control in a
> > single register block.
> >
> > Signed-off-by: Joakim Zhang <joakim.zhang@cixtech.com>
> > ---
> > .../bindings/soc/cix/cix,sky1-audss-cru.yaml | 92
> > +++++++++++++++++++ .../dt-bindings/clock/cix,sky1-audss-clock.h |
> > 60 ++++++++++++ .../dt-bindings/reset/cix,sky1-audss-reset.h | 25
> > +++++
> > 3 files changed, 177 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
> > create mode 100644 include/dt-bindings/clock/cix,sky1-audss-clock.h
> > create mode 100644 include/dt-bindings/reset/cix,sky1-audss-reset.h
>
> Both headers should have the same name as the compatible. I already
> requested this some time ago, I think.
Sorry, will update.
Joakim
^ permalink raw reply
* Re: [PATCH 2/3] arm64: dts: qcom: glymur: Add Asus Zenbook A14 (UX3407NA)
From: Konrad Dybcio @ 2026-06-23 7:06 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, Bjorn Andersson
In-Reply-To: <20260623-zenbook-dts-v1-2-3f80f680381d@oss.qualcomm.com>
On 6/23/26 3:31 AM, Bjorn Andersson wrote:
> From: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
>
> UX3407NA is a variant of the Asus Zenbook A14 built on the Qualcomm
> Glymur platform. It comes with an 18-core X2 Elite SoC, 32GB DDR, and
> the other typical Glymur platform capabilities.
>
> The Asus Zenbook uses &pcie3b for NVMe storage, the screen is WUXGA
> OLED, it has two USB Type-C ports, one USB Type-A, and one HDMI port.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
> ---
[...]
> +/ {
> + compatible = "asus,zenbook-a14-ux3407na", "qcom,glymur";
> + model = "ASUS Zenbook A14 (UX3407NA)";
unusual order!
> +
> + aliases {
> + i2c0 = &i2c0;
> + i2c8 = &i2c8;
> + i2c10 = &i2c10;
> + i2c19 = &i2c19;
> + serial0 = &uart14;
> + };
> +
> + chosen {
> + };
Do we need this node?
[...]
> +&i2c10 {
> + clock-frequency = <400000>;
> +
> + status = "okay";
> +
> + hid@17 {
We've already got touchscreen, touchpad, the keyboard's below - what's
this one?
[...]
> +&tlmm {
> + gpio-reserved-ranges = <4 4>, /* EC Secure */
> + <10 2>, /* OOB UART */
> + <44 4>, /* TPM */
> + <90 2>; /* TPM */
Is there no EC reset pin? I wouldn't want others to have to open up
a laptop after trying to find out what it does..
[...]
> + ts_default: ts-default-state {
> + reset-n-pins {
> + pins = "gpio48";
> + function = "gpio";
> + bias-disable;
> + output-high;
You can drop this property
[...]
> +&uart21 {
> + status = "disabled";
hm?
Konrad
^ permalink raw reply
* Re: [PATCH V13 8/9] iio: imu: inv_icm42607: Add Gyroscope to icm42607
From: Andy Shevchenko @ 2026-06-23 6:52 UTC (permalink / raw)
To: Chris Morgan
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner, jic23,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh
In-Reply-To: <PH0PR19MB997338E86152468CE26F60953FA5E42@PH0PR19MB997338.namprd19.prod.outlook.com>
On Wed, Jun 17, 2026 at 04:10:49PM -0500, Chris Morgan wrote:
> On Tue, Jun 16, 2026 at 01:13:03PM +0300, Andy Shevchenko wrote:
> > On Mon, Jun 15, 2026 at 12:25:51PM -0500, Chris Morgan wrote:
...
> > Can be some of the code deduplicated between gyro and accel?
>
> Probably a fair amount, but the deduplication will likely need to be
> undone somewhat if we get buffer, WoM or apex support added back
> (I don't have any devices with such functionality, so if anyone will
> do it then it won't be me). I can refactor more if you want, or we
> can keep it split like this to make it easy if someone else wants to
> tackle the buffers/IRQs stuff later? Your call.
Just asking. Jonathan, David, Nuno, what's your opinion on this?
Personal opinion is to avoid solving the issues that do not exist.
If you are not committing into those features, let's not prepare
driver for them right now.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v3 1/3] spi: dt-bindings: snps,dw-apb-ssi: add 'power-domains' property
From: Herve Codina @ 2026-06-23 6:50 UTC (permalink / raw)
To: Wolfram Sang
Cc: linux-renesas-soc, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-spi, devicetree
In-Reply-To: <20260622174806.74450-6-wsa+renesas@sang-engineering.com>
Hi Wolfram,
On Mon, 22 Jun 2026 19:48:07 +0200
Wolfram Sang <wsa+renesas@sang-engineering.com> wrote:
> On the Renesas RZ/N1D SoC, this SPI controller belongs to a power
> domain. Enable the property to describe it in DTs.
>
> Reported-by: Herve Codina <herve.codina@bootlin.com>
> Closes: https://lore.kernel.org/r/20260622132842.7e0d772c@bootlin.com
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
>
> Change since v2:
> * new patch (Thanks, Herve!)
>
> Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
> index 8ebebcebca16..3896ee02d7b6 100644
> --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
> +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
> @@ -88,6 +88,9 @@ properties:
> - const: ssi_clk
> - const: pclk
>
> + power-domains:
> + maxItems: 1
> +
> resets:
> maxItems: 1
>
Reviewed-by: Herve Codina <herve.codina@bootlin.com>
Best regards,
Hervé
^ permalink raw reply
* Re: [PATCH v6 2/2] leds: ltc3208: Add driver for LTC3208 Multidisplay LED Driver
From: sashiko-bot @ 2026-06-23 6:43 UTC (permalink / raw)
To: Jan Carlo Roleda; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260623-upstream-ltc3208-v6-2-fc6bd9749166@analog.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The regmap configuration lacks `.reg_defaults`, which will cause `regmap_field_write()` to attempt a hardware read for shared registers. This can fail entirely on write-only I2C devices.
- [Medium] The driver unconditionally overwrites the CPO (Charge Pump Oscillator) mode bits in the options register, destroying bootloader state.
- [Low] Minor code quality defects: format string mismatches, dead code, and typos.
--
commit 347e80a37d34f3436d81f8c496280febd37eb8a5
Author: Jan Carlo Roleda <jancarlo.roleda@analog.com>
leds: ltc3208: Add driver for LTC3208 Multidisplay LED Driver
Kernel driver implementation for the LTC3208 Multidisplay LED Driver.
The LTC3208 controls up to 7 distinct LED channels and is configured via I2C.
> The CAM LED channel is configured as 2 separate CAMHI and CAMLO register
> sub-channels, which currnet is selected via the CAMHL pin, or set to
> CAMHI register only via setting the S_CAMHILO bit high in register G (0x7).
[Severity: Low]
This isn't a bug, but there's a typo in the commit message: 'currnet' instead
of 'current'.
> diff --git a/drivers/leds/leds-ltc3208.c b/drivers/leds/leds-ltc3208.c
> new file mode 100644
> index 0000000000000..3d6bec99e9ea9
> --- /dev/null
> +++ b/drivers/leds/leds-ltc3208.c
> @@ -0,0 +1,244 @@
[ ... ]
> +#define LTC3208_REG_F_CAM 0x6 /* CAM (High and Low) current DAC*/
> +#define LTC3208_REG_G_OPT 0x7 /* Device Options */
> +#define LTC3208_OPT_CPO_MASK GENMASK(7, 6)
[Severity: Low]
Is this macro needed? It appears to be defined but never used in the driver.
The same applies to LTC3208_MAX_CPO_OPT defined later.
[ ... ]
> +struct ltc3208_led {
> + struct led_classdev cdev;
> + struct i2c_client *client;
> + struct regmap_field *rfield;
> + enum ltc3208_channel channel;
> +};
> +
> +struct ltc3208 {
> + struct ltc3208_led leds[LTC3208_NUM_LED_GRPS];
> + struct regmap *regmap;
> +};
[Severity: Low]
Is the channel member in struct ltc3208_led used anywhere? It gets assigned
during probe but doesn't seem to be read later.
Similarly, the regmap pointer in struct ltc3208 appears to only be used
locally in ltc3208_probe() and isn't needed in the struct.
[ ... ]
> +static const struct regmap_config ltc3208_regmap_cfg = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = LTC3208_REG_G_OPT,
> + .cache_type = REGCACHE_FLAT_S,
> +};
[Severity: High]
Does this regmap configuration need a .reg_defaults array to seed the cache?
Since .reg_defaults is missing, the cache won't be seeded. Later, when
ltc3208_led_set_brightness() calls regmap_field_write() for shared registers
(like LTC3208_REG_A_GRNRED), it will trigger a read-modify-write operation
that issues an I2C read.
If the hardware is write-only, the read will fail and break LED functionality.
If it supports reads, this still introduces unnecessary I2C read latency on a
fast path.
[ ... ]
> +static int ltc3208_probe(struct i2c_client *client)
> +{
[ ... ]
> + reg_val = FIELD_PREP(LTC3208_OPT_EN_RGBS, set_sub_control_pin) |
> + FIELD_PREP(LTC3208_OPT_DIS_CAMHILO, disable_camhl_pin) |
> + FIELD_PREP(LTC3208_OPT_DIS_RGBDROP,
> + disable_rgb_aux4_dropout_signal);
> +
> + ret = regmap_write(regmap, LTC3208_REG_G_OPT, reg_val);
[Severity: Medium]
Does this unconditional write wipe out the existing CPO bits (bits 7:6)?
By using regmap_write() here, any initial hardware configuration done by
the bootloader or hardware defaults (like Charge Pump mode) might be permanently
erased. Would regmap_update_bits() be safer here to apply only the
explicitly configured fields?
[ ... ]
> + ret = fwnode_property_read_u32(child, "reg", &chan);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "Failed to get reg value of LED\n");
> + else if (chan >= LTC3208_NUM_LED_GRPS)
> + return dev_err_probe(&client->dev, -EINVAL,
> + "%d is an invalid LED ID\n", chan);
[Severity: Low]
Could this dev_err_probe() use %u instead of %d?
The chan variable is an unsigned u32, so %d produces a format string mismatch.
> + else if (ddata->leds[chan].client)
> + return dev_err_probe(&client->dev, -EINVAL,
> + "%d is already registered\n", chan);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260623-upstream-ltc3208-v6-0-fc6bd9749166@analog.com?part=2
^ permalink raw reply
* Re: [PATCH v1 1/4] dt-bindings: media: qcom: Add JPEG encoder binding
From: Luca Weiss @ 2026-06-23 6:41 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: mchehab, bod, robh, krzk+dt, conor+dt, andersson, konradybcio,
linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260612194417.1737009-2-atanas.filipov@oss.qualcomm.com>
Hi Atanas,
On Fri Jun 12, 2026 at 9:44 PM CEST, Atanas Filipov wrote:
> Add device-tree binding for the standalone Qualcomm JPEG encoder
> hardware block (separate from CAMSS media pipelines).
>
> Document required resources briefly and scope initial support to
> currently used compatibles in this series, including SM8250,
> QCM6490, and SM8550 class platforms.
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> .../bindings/media/qcom,jpeg-encoder.yaml | 142 ++++++++++++++++++
> 1 file changed, 142 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> new file mode 100644
> index 000000000000..83f19719a869
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> @@ -0,0 +1,142 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/qcom,jpeg-encoder.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm JPEG Encoder
> +
> +maintainers:
> + - Azam Sadiq Pasha Kapatrala Syed <akapatra@quicinc.com>
> + - Hariram Purushothaman <hariramp@quicinc.com>
> +
> +description: |
> + Qualcomm JPEG Encoder is the JPEG encode hardware present in Qualcomm SoCs.
> +
> + The JPEG hardware IP version 4.2.0 is shared across SM8250, QCM6490, SM8550
> + and related derivatives. Platforms based on the SM8550 register layout and
> + interrupt wiring can reuse the qcom,sm8550-jenc fallback compatible, provided
> + the correct register base address and IOMMU stream IDs are supplied in the
> + DTS node.
> +
> +properties:
> + compatible:
> + oneOf:
> + - items:
> + - const: qcom,sm8250-jenc
> + - items:
> + - enum:
> + - qcom,qcm6490-jenc
Any reason why here and in other places you specifically reference
qcm6490 instead of sc7280 which is what essentially any other compatible
and files for Kodiak is named after? It makes it sound like this is not
applicable to SC7280/SM7325 and *specific* to QCM6490 inside the Kodiak
family.
I supposed qcom,kodiak-jenc could also work but not sure we should
change up the naming scheme halfway.
Regards
Luca
^ permalink raw reply
* [PATCH v6 2/2] leds: ltc3208: Add driver for LTC3208 Multidisplay LED Driver
From: Jan Carlo Roleda @ 2026-06-23 6:32 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel, linux-leds, devicetree, Uwe Kleine-König,
Jan Carlo Roleda
In-Reply-To: <20260623-upstream-ltc3208-v6-0-fc6bd9749166@analog.com>
Kernel driver implementation for LTC3208 Multidisplay LED Driver.
The LTC3208 is a Multi-display LED driver, designed to control up to
7 distinct LED channels (MAIN, SUB, AUX, CAMHI, CAMLO, RED, GREEN, BLUE),
each configurable with its own current level that is equally set to its
respective output current source pins for external LEDs.
It is programmed via the I2C serial interface.
MAIN and SUB support 8-bit current level resolution,
while AUX, CAMHI/LO, RED, GREEN, and BLUE support 4-bit levels.
The AUX LED channel can be configured to mirror the CAM, SUB, and MAIN
channel current levels, or as its own independent AUX channel.
The CAM LED channel is configured as 2 separate CAMHI and CAMLO register
sub-channels, which currnet is selected via the CAMHL pin, or set to
CAMHI register only via setting the S_CAMHILO bit high in register G (0x7).
Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
---
MAINTAINERS | 1 +
drivers/leds/Kconfig | 12 +++
drivers/leds/Makefile | 1 +
drivers/leds/leds-ltc3208.c | 244 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 258 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fd6ffdaaf04..e3b59485ecb3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15229,6 +15229,7 @@ L: linux-leds@vger.kernel.org
S: Maintained
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
+F: drivers/leds/leds-ltc3208.c
LTC4282 HARDWARE MONITOR DRIVER
M: Nuno Sa <nuno.sa@analog.com>
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index f4a0a3c8c870..d917ce3b72f4 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -1028,6 +1028,18 @@ config LEDS_ACER_A500
This option enables support for the Power Button LED of
Acer Iconia Tab A500.
+config LEDS_LTC3208
+ tristate "LED Driver for Analog Devices LTC3208"
+ depends on LEDS_CLASS && I2C
+ select REGMAP_I2C
+ help
+ Say Y to enable the LTC3208 LED driver.
+ This enables the LED device LTC3208, a 7-channel, 17-current source
+ multidisplay high-current LED driver, configured via I2C.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ltc3208.
+
source "drivers/leds/blink/Kconfig"
comment "Flash and Torch LED drivers"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 7db3768912ca..0148b87e16ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
obj-$(CONFIG_LEDS_LP8864) += leds-lp8864.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
+obj-$(CONFIG_LEDS_LTC3208) += leds-ltc3208.o
obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o
obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o
obj-$(CONFIG_LEDS_MAX77705) += leds-max77705.o
diff --git a/drivers/leds/leds-ltc3208.c b/drivers/leds/leds-ltc3208.c
new file mode 100644
index 000000000000..3d6bec99e9ea
--- /dev/null
+++ b/drivers/leds/leds-ltc3208.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LED driver for Analog Devices LTC3208 Multi-Display Driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ *
+ * Author: Jan Carlo Roleda <jancarlo.roleda@analog.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+/* Registers */
+#define LTC3208_REG_A_GRNRED 0x1 /* Green and Red current DAC*/
+#define LTC3208_REG_B_AUXBLU 0x2 /* AUX and Blue current DAC*/
+#define LTC3208_REG_C_MAIN 0x3 /* Main current DAC */
+#define LTC3208_REG_D_SUB 0x4 /* Sub current DAC */
+#define LTC3208_REG_E_AUX_SELECT 0x5 /* AUX DAC Select */
+#define LTC3208_AUX1_MASK GENMASK(1, 0)
+#define LTC3208_AUX2_MASK GENMASK(3, 2)
+#define LTC3208_AUX3_MASK GENMASK(5, 4)
+#define LTC3208_AUX4_MASK GENMASK(7, 6)
+#define LTC3208_REG_F_CAM 0x6 /* CAM (High and Low) current DAC*/
+#define LTC3208_REG_G_OPT 0x7 /* Device Options */
+#define LTC3208_OPT_CPO_MASK GENMASK(7, 6)
+#define LTC3208_OPT_DIS_RGBDROP BIT(3)
+#define LTC3208_OPT_DIS_CAMHILO BIT(2)
+#define LTC3208_OPT_EN_RGBS BIT(1)
+
+#define LTC3208_MAX_BRIGHTNESS_4BIT 0xF
+#define LTC3208_MAX_BRIGHTNESS_8BIT 0xFF
+
+#define LTC3208_NUM_LED_GRPS 8
+#define LTC3208_NUM_AUX_LEDS 4
+
+#define LTC3208_NUM_AUX_OPT 4
+#define LTC3208_MAX_CPO_OPT 3
+
+enum ltc3208_aux_channel {
+ LTC3208_AUX_CHAN_AUX = 0,
+ LTC3208_AUX_CHAN_MAIN,
+ LTC3208_AUX_CHAN_SUB,
+ LTC3208_AUX_CHAN_CAM
+};
+
+enum ltc3208_channel {
+ LTC3208_CHAN_MAIN = 0,
+ LTC3208_CHAN_SUB,
+ LTC3208_CHAN_AUX,
+ LTC3208_CHAN_CAML,
+ LTC3208_CHAN_CAMH,
+ LTC3208_CHAN_RED,
+ LTC3208_CHAN_BLUE,
+ LTC3208_CHAN_GREEN,
+ LTC3208_CHAN_N_COUNT,
+};
+
+static const char *const ltc3208_dt_aux_channels[] = { "adi,aux1-channel",
+ "adi,aux2-channel",
+ "adi,aux3-channel",
+ "adi,aux4-channel" };
+
+static const char *const ltc3208_aux_opt[] = { "aux", "main", "sub", "cam" };
+
+struct ltc3208_led {
+ struct led_classdev cdev;
+ struct i2c_client *client;
+ struct regmap_field *rfield;
+ enum ltc3208_channel channel;
+};
+
+struct ltc3208 {
+ struct ltc3208_led leds[LTC3208_NUM_LED_GRPS];
+ struct regmap *regmap;
+};
+
+static const struct regmap_config ltc3208_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LTC3208_REG_G_OPT,
+ .cache_type = REGCACHE_FLAT_S,
+};
+
+static const struct reg_field ltc3208_led_reg_field[LTC3208_CHAN_N_COUNT] = {
+ [LTC3208_CHAN_MAIN] = REG_FIELD(LTC3208_REG_C_MAIN, 0, 7),
+ [LTC3208_CHAN_SUB] = REG_FIELD(LTC3208_REG_D_SUB, 0, 7),
+ [LTC3208_CHAN_BLUE] = REG_FIELD(LTC3208_REG_B_AUXBLU, 0, 3),
+ [LTC3208_CHAN_AUX] = REG_FIELD(LTC3208_REG_B_AUXBLU, 4, 7),
+ [LTC3208_CHAN_CAML] = REG_FIELD(LTC3208_REG_F_CAM, 0, 3),
+ [LTC3208_CHAN_CAMH] = REG_FIELD(LTC3208_REG_F_CAM, 4, 7),
+ [LTC3208_CHAN_RED] = REG_FIELD(LTC3208_REG_A_GRNRED, 0, 3),
+ [LTC3208_CHAN_GREEN] = REG_FIELD(LTC3208_REG_A_GRNRED, 4, 7),
+};
+
+static int ltc3208_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct ltc3208_led *led =
+ container_of(led_cdev, struct ltc3208_led, cdev);
+ u8 current_level = brightness;
+
+ return regmap_field_write(led->rfield, current_level);
+}
+
+static int ltc3208_probe(struct i2c_client *client)
+{
+ enum ltc3208_aux_channel aux_channels[LTC3208_NUM_AUX_LEDS];
+ struct ltc3208 *ddata;
+ struct regmap *regmap;
+ bool disable_rgb_aux4_dropout_signal;
+ bool disable_camhl_pin;
+ bool set_sub_control_pin;
+ int ret;
+ u8 reg_val;
+
+ regmap = devm_regmap_init_i2c(client, <c3208_regmap_cfg);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Failed to initialize regmap\n");
+
+ ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ ddata->regmap = regmap;
+
+ disable_camhl_pin = device_property_read_bool(&client->dev,
+ "adi,disable-camhl-pin");
+ set_sub_control_pin =
+ device_property_read_bool(&client->dev, "adi,cfg-enrgbs-pin");
+ disable_rgb_aux4_dropout_signal =
+ device_property_read_bool(&client->dev,
+ "adi,disable-rgb-aux4-dropout");
+
+ reg_val = FIELD_PREP(LTC3208_OPT_EN_RGBS, set_sub_control_pin) |
+ FIELD_PREP(LTC3208_OPT_DIS_CAMHILO, disable_camhl_pin) |
+ FIELD_PREP(LTC3208_OPT_DIS_RGBDROP,
+ disable_rgb_aux4_dropout_signal);
+
+ ret = regmap_write(regmap, LTC3208_REG_G_OPT, reg_val);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "error writing to options register\n");
+
+ /* Initialize aux channel configurations */
+ for (int i = 0; i < LTC3208_NUM_AUX_LEDS; i++) {
+ ret = device_property_match_property_string(
+ &client->dev, ltc3208_dt_aux_channels[i],
+ ltc3208_aux_opt, LTC3208_NUM_AUX_OPT);
+ /* Fallback to default value (AUX) if not found */
+ if (ret == -ENODATA || ret == -EINVAL)
+ aux_channels[i] = LTC3208_AUX_CHAN_AUX;
+ else if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Error reading AUX Channel %d", i);
+ else if (ret >= 0)
+ aux_channels[i] = ret;
+ }
+
+ reg_val = FIELD_PREP(LTC3208_AUX1_MASK, aux_channels[0]) |
+ FIELD_PREP(LTC3208_AUX2_MASK, aux_channels[1]) |
+ FIELD_PREP(LTC3208_AUX3_MASK, aux_channels[2]) |
+ FIELD_PREP(LTC3208_AUX4_MASK, aux_channels[3]);
+
+ ret = regmap_write(regmap, LTC3208_REG_E_AUX_SELECT, reg_val);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "error writing to aux channel register.\n");
+
+ device_for_each_child_node_scoped(&client->dev, child) {
+ struct ltc3208_led *led;
+ struct led_init_data init_data = {};
+ u32 chan;
+
+ ret = fwnode_property_read_u32(child, "reg", &chan);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to get reg value of LED\n");
+ else if (chan >= LTC3208_NUM_LED_GRPS)
+ return dev_err_probe(&client->dev, -EINVAL,
+ "%d is an invalid LED ID\n", chan);
+ else if (ddata->leds[chan].client)
+ return dev_err_probe(&client->dev, -EINVAL,
+ "%d is already registered\n", chan);
+
+ led = &ddata->leds[chan];
+
+ led->rfield =
+ devm_regmap_field_alloc(&client->dev, ddata->regmap,
+ ltc3208_led_reg_field[chan]);
+ if (IS_ERR(led->rfield))
+ return dev_err_probe(&client->dev, PTR_ERR(led->rfield),
+ "cannot allocate regmap field\n");
+ led->client = client;
+ led->channel = chan;
+ led->cdev.brightness_set_blocking = ltc3208_led_set_brightness;
+ led->cdev.max_brightness = LTC3208_MAX_BRIGHTNESS_4BIT;
+
+ if (chan == LTC3208_CHAN_MAIN || chan == LTC3208_CHAN_SUB)
+ led->cdev.max_brightness = LTC3208_MAX_BRIGHTNESS_8BIT;
+
+ init_data.fwnode = child;
+
+ ret = devm_led_classdev_register_ext(&client->dev, &led->cdev,
+ &init_data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "LED %u Register failed.\n", chan);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ltc3208_match_table[] = {
+ { .compatible = "adi,ltc3208" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc3208_match_table);
+
+static const struct i2c_device_id ltc3208_idtable[] = {
+ { .name = "ltc3208" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3208_idtable);
+
+static struct i2c_driver ltc3208_driver = {
+ .driver = {
+ .name = "ltc3208",
+ .of_match_table = ltc3208_match_table,
+ },
+ .id_table = ltc3208_idtable,
+ .probe = ltc3208_probe,
+};
+module_i2c_driver(ltc3208_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jan Carlo Roleda <jancarlo.roleda@analog.com>");
+MODULE_DESCRIPTION("LTC3208 LED Driver");
--
2.43.0
^ permalink raw reply related
* [PATCH v6 1/2] dt-bindings: leds: Document LTC3208 Multidisplay LED Driver
From: Jan Carlo Roleda @ 2026-06-23 6:32 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel, linux-leds, devicetree, Uwe Kleine-König,
Jan Carlo Roleda, Krzysztof Kozlowski
In-Reply-To: <20260623-upstream-ltc3208-v6-0-fc6bd9749166@analog.com>
Add Devicetree Documentation for LTC3208 Multidisplay LED Driver.
Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../devicetree/bindings/leds/adi,ltc3208.yaml | 181 +++++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 188 insertions(+)
diff --git a/Documentation/devicetree/bindings/leds/adi,ltc3208.yaml b/Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
new file mode 100644
index 000000000000..0a01e07e0ab7
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
@@ -0,0 +1,181 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (c) 2026 Analog Devices, Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/adi,ltc3208.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LTC3208 Multidisplay LED Controller from Linear Technologies (Now Analog Devices)
+
+maintainers:
+ - Jan Carlo Roleda <jancarlo.roleda@analog.com>
+
+description:
+ The LTC3208 is a multidisplay LED controller that can support up to 1A to all
+ connected LEDs.
+
+ The datasheet for this device can be found in
+ https://www.analog.com/en/products/ltc3208.html
+
+properties:
+ compatible:
+ const: adi,ltc3208
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ adi,disable-camhl-pin:
+ type: boolean
+ description:
+ Configures whether the external CAMHL pin is disabled.
+ If disabled then the output pins associated with CAM will always select
+ the CAM register's high half-byte brightness.
+
+ adi,cfg-enrgbs-pin:
+ type: boolean
+ description:
+ Configures which channel the ENRGBS pin toggles when it receives a signal.
+ ENRGBS pin controls the SUB channel's output pins if this is set,
+ or RGB channel's output pins if this is unset.
+
+ adi,disable-rgb-aux4-dropout:
+ type: boolean
+ description:
+ Configures the RGB and AUX4 dropout signals to be disabled.
+
+ adi,aux1-channel:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ LED Channel that the AUX1 output pin mirrors its brightness level from.
+ enum: [aux, main, sub, cam]
+ default: aux
+
+ adi,aux2-channel:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ LED Channel that the AUX2 output pin mirrors its brightness level from.
+ enum: [aux, main, sub, cam]
+ default: aux
+
+ adi,aux3-channel:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ LED Channel that the AUX3 output pin mirrors its brightness level from.
+ enum: [aux, main, sub, cam]
+ default: aux
+
+ adi,aux4-channel:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ LED Channel that the AUX4 output pin mirrors its brightness level from.
+ enum: [aux, main, sub, cam]
+ default: aux
+
+patternProperties:
+ "^led@[0-7]$":
+ type: object
+ $ref: /schemas/leds/common.yaml#
+ unevaluatedProperties: false
+ properties:
+ reg:
+ description:
+ LED Channel Number. each channel maps to a specific channel group used
+ to configure the brightness level of the output pins corresponding to
+ the channel.
+ enum:
+ - 0 # Main Channel (8-bit brightness)
+ - 1 # Sub Channel (8-bit brightness)
+ - 2 # AUX Channel (4-bit brightness)
+ - 3 # Camera Channel, Low-side byte (4-bit brightness)
+ - 4 # Camera Channel, High-side byte (4-bit brightness)
+ - 5 # Red Channel (4-bit brightness)
+ - 6 # Blue Channel (4-bit brightness)
+ - 7 # Green Channel (4-bit brightness)
+ required:
+ - reg
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/leds/common.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led-controller@1b {
+ compatible = "adi,ltc3208";
+ reg = <0x1b>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adi,disable-camhl-pin;
+ adi,cfg-enrgbs-pin;
+ adi,disable-rgb-aux4-dropout;
+
+ /* MAIN */
+ led@0 {
+ reg = <0>;
+ function = LED_FUNCTION_ACTIVITY;
+ color = <LED_COLOR_ID_WHITE>;
+ };
+
+ /* SUB */
+ led@1 {
+ reg = <1>;
+ function = LED_FUNCTION_ACTIVITY;
+ color = <LED_COLOR_ID_WHITE>;
+ };
+
+ /* AUX */
+ led@2 {
+ reg = <2>;
+ function = LED_FUNCTION_ACTIVITY;
+ color = <LED_COLOR_ID_WHITE>;
+ };
+
+ /* CAMLO */
+ led@3 {
+ reg = <3>;
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+ };
+
+ /* CAMHI */
+ led@4 {
+ reg = <4>;
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+ };
+
+ /* RED */
+ led@5 {
+ reg = <5>;
+ function = LED_FUNCTION_INDICATOR;
+ color = <LED_COLOR_ID_RED>;
+ };
+
+ /* BLUE */
+ led@6 {
+ reg = <6>;
+ function = LED_FUNCTION_INDICATOR;
+ color = <LED_COLOR_ID_BLUE>;
+ };
+
+ /* GREEN */
+ led@7 {
+ reg = <7>;
+ function = LED_FUNCTION_INDICATOR;
+ color = <LED_COLOR_ID_GREEN>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..2fd6ffdaaf04 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15223,6 +15223,13 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
F: drivers/iio/temperature/ltc2983.c
+LTC3208 LED DRIVER
+M: Jan Carlo Roleda <jancarlo.roleda@analog.com>
+L: linux-leds@vger.kernel.org
+S: Maintained
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/leds/adi,ltc3208.yaml
+
LTC4282 HARDWARE MONITOR DRIVER
M: Nuno Sa <nuno.sa@analog.com>
L: linux-hwmon@vger.kernel.org
--
2.43.0
^ permalink raw reply related
* [PATCH v6 0/2] Add support for LTC3208 multi-display driver
From: Jan Carlo Roleda @ 2026-06-23 6:32 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel, linux-leds, devicetree, Uwe Kleine-König,
Jan Carlo Roleda, Krzysztof Kozlowski
The LTC3208 is a multi-display LED driver, using a high-efficiency, low
noise charge pump to provide power to 5 channels (MAIN, SUB, RGB, CAM,
AUX). Current for each LED is controlled by the I2C serial interface.
Four AUX current sources can be independently assigned via the I2C port
to the CAM, SUB, MAIN, or AUX DAC controlled displays
Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
---
Changes in v6:
- Fixed code conventions (spaces in brackets, awkward line breaks)
- Removed unused `i2c_set_clientdata` from probe function
- Added guard for potential errors in AUX channel configurations
- Added guards to prevent duplicate LED registrations to the same ID
- Corrected `dev_err_probe` return values
- Link to v5: https://lore.kernel.org/r/20260619-upstream-ltc3208-v5-0-075d18060606@analog.com
Changes in v5:
- Fixed MAINTAINERS commit ordering
- removed i2c_client from ltc3208_dev
- renamed ltc3208_dev struct to ltc3208
- refactored brightness_set function to use regmap_field
- updated leds attribute to use constant size array
- updated regmap_config to use REGCACHE_FLAT_S cache type
- added max_register to regmap_config
- renamed variables in probe function
- renamed map to regmap for regmap instances
- moved AUX channel configuration inline to probe
- Link to v4: https://lore.kernel.org/r/20260416-upstream-ltc3208-v4-0-3884ed3e49f5@analog.com
Changes in v4:
- Reordered commit order to match dependency order
- Updated Kconfig to be more descriptive of device
- Added led@0-7 with more complete example properties (function and
color)
- Driver changes:
-- Removed unnecessary include headers
-- Formatted macros
-- Created helper `write_current_level` functions for LED current
configuration, using `regmap_update_bits()`
-- Adjusted awkward tabbing issues
-- Updated variable names in probe to be more descriptive
-- Updated inline comment capitalization
-- Initialized `i` within the for loop in AUX configuration in probe
-- Refactored `update_aux_dac` function to use array pointer
-- Fixed error messages in probe
- Link to v3: https://lore.kernel.org/r/20260406-upstream-ltc3208-v3-0-7f0b1d20ee7a@analog.com
Changes in v3:
- Edited device bindings descriptions
-- removed full stop in title
-- replaced quotes with double quotes for consistency
-- removed <dt-bindings/gpio/gpio.h> from example
-- removed led1-7 in example for brevity
- squashed maintainers commit to driver commit
- Link to v2: https://lore.kernel.org/r/20260326-upstream-ltc3208-v2-0-3dbc992b6098@analog.com
Changes in v2:
- Addressed DTSchema bot warnings and errors
-- removed extra blank lines
-- fixed $id to match current naming
- Addressed Kernel test warnings
-- fixed bounds for aux channel configurations
- Link to v0: https://lore.kernel.org/r/20260318-upstream-ltc3208-v1-0-015f1f1e9065@analog.com
---
Jan Carlo Roleda (2):
dt-bindings: leds: Document LTC3208 Multidisplay LED Driver
leds: ltc3208: Add driver for LTC3208 Multidisplay LED Driver
.../devicetree/bindings/leds/adi,ltc3208.yaml | 181 +++++++++++++++
MAINTAINERS | 8 +
drivers/leds/Kconfig | 12 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-ltc3208.c | 244 +++++++++++++++++++++
5 files changed, 446 insertions(+)
---
base-commit: d43f1d792902ba0a53fd311bff2cf96095c7606d
change-id: 20260318-upstream-ltc3208-7cc8968bf69e
Best regards,
--
Jan Carlo Roleda <jancarlo.roleda@analog.com>
^ permalink raw reply
* Re: [PATCH v1] arm64: dts: freescale: imx95-toradex-smarc: add alias for lpuart5
From: Peng Fan @ 2026-06-23 6:21 UTC (permalink / raw)
To: Francesco Dolcini
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Francesco Dolcini, devicetree, imx, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260622093507.44132-1-francesco@dolcini.it>
On Mon, Jun 22, 2026 at 11:35:06AM +0200, Francesco Dolcini wrote:
>From: Francesco Dolcini <francesco.dolcini@toradex.com>
>
>Add alias for lpuart5 so the UART gets a stable line number.
>Without this alias, the lpuart driver fails:
>
> fsl-lpuart 42590000.serial: failed to get alias id, errno -19
>
>This prevents the Bluetooth controller connected to this UART from
>working.
>
>Fixes: 104a391bb6ff ("arm64: dts: freescale: imx95-toradex-smarc: Enable bluetooth on lpuart5")
>Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
Acked-by: Peng Fan <peng.fan@nxp.com>
^ permalink raw reply
* Re: [PATCH 3/9] firmware: imx: ele: Add API functions for OCOTP fuse access
From: Peng Fan @ 2026-06-23 6:19 UTC (permalink / raw)
To: Frieder Schrempf
Cc: Frank Li, Pankaj Gupta, Frieder Schrempf, Srinivas Kandagatla,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Shawn Guo,
devicetree, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <4f4cc156-7534-4f11-8a45-e4caf083c865@kontron.de>
On Thu, Jun 18, 2026 at 07:52:16AM +0200, Frieder Schrempf wrote:
>On 17.06.26 21:56, Frank Li wrote:
>> On Wed, Jun 17, 2026 at 08:54:35AM +0200, Frieder Schrempf wrote:
>>> On 16.06.26 22:05, Frank Li wrote:
>>>> On Tue, Jun 16, 2026 at 07:59:54PM +0200, Frieder Schrempf wrote:
>>>>> On 16.06.26 17:36, Frank Li wrote:
>>>>>> On Tue, Jun 16, 2026 at 01:52:18PM +0200, Frieder Schrempf wrote:
>>>>>>> From: Frieder Schrempf <frieder.schrempf@kontron.de>
>>>>>>>
>>>>>>> The ELE S400 API provides read and write access to the OCOTP fuse
>>>>>>> registers. This adds the necessary API functions imx_se_read_fuse()
>>>>>>> and imx_se_write_fuse() to be used by other drivers such as the
>>>>>>> OCOTP S400 NVMEM driver.
>>>>>>>
>>>>>>> This is ported from the downstream vendor kernel.
>>>>>>>
>>>>>>> Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
>>>>>>> ---
>>>>>>> drivers/firmware/imx/ele_base_msg.c | 122 ++++++++++++++++++++++++++++++++++++
>>>>>>> drivers/firmware/imx/ele_base_msg.h | 6 ++
>>>>>>> include/linux/firmware/imx/se_api.h | 3 +
>>>>>>> 3 files changed, 131 insertions(+)
>>>>>>>
>>>>>> ...
>>>>>>> +++ b/include/linux/firmware/imx/se_api.h
>>>>>>> @@ -11,4 +11,7 @@
>>>>>>> #define SOC_ID_OF_IMX8ULP 0x084d
>>>>>>> #define SOC_ID_OF_IMX93 0x9300
>>>>>>>
>>>>>>> +int imx_se_read_fuse(void *se_if_data, uint16_t fuse_id, u32 *value);
>>>>>>> +int imx_se_write_fuse(void *se_if_data, uint16_t fuse_id, u32 value);
>>>>>>> +
>>>>>>
>>>>>> This API should implement in fuse drivers. Other consume should use standard
>>>>>> fuse API to get value. If put here, it may bypass fuse driver.
>>>>>
>>>>> The reason this is here, is the downstream implementation in linux-imx
>>>>> and the current code organization.
>>>>
>>>> Downstream may not good enough, sometime, it is quick solution.
>>>
>>> Ok, but the code structure and API design has been upstreamed like this
>>> and the refactoring could have been done before, if downstream is known
>>> to not be well organized.
>>>
>>>>
>>>>> I thought there is some good reason
>>>>> to have shared functions and it looks like Pankaj structured it like
>>>>> this so all API functions live in ele_base_msg.c and the internal
>>>>> structs and defines in ele_base_msg.h and se_ctrl.h are not exposed to
>>>>> other drivers.
>>>>>
>>>>> If I would move this into imx-ocotp-ele.c, then I would also need to
>>>>> change how the code is organized and make the internal se_api functions
>>>>> exposed to other drivers. I don't know if that is really a good idea.
>>>>>
>>>>> I get your point but it looks like this contradicts the intention of
>>>>> having a clean API in the firmware driver.
>>>>
>>>> You can refer imx-ocotp-scu.c, structure should be similar, only difference
>>>> is that lower transfer APIs.
>>> Ok, this would mean that I expose the generic SE functions and structs
>>> required for fuse handling. In practice, I would remove
>>> imx_se_read_fuse() and imx_se_write_fuse() from se_api.h and instead add
>>> the following:
>>>
>>> struct se_msg_hdr { ... };
>>> struct se_api_msg { ... };
>>> struct se_if_priv;
>>> se_fill_cmd_msg_hdr( ... );
>>> se_msg_send_rcv( ... );
>>> se_val_rsp_hdr_n_status( ... );
>>>
>>> Then I would export the functions in ele_common.c and put the fuse
>>> read/write functions in the NVMEM driver.
>>>
>>> Is that what you want me to do?
>>
>> Yes, Idealy, it should be children device under ele, ELE like a bus, which
>> previous lower level data transfer, ocotp should be base on top then it.
>> like spi/i2c, which provide low level data transfer.
>
>Ok, please also see the discussion with Krzysztof on the bindings patch.
>The problem is that this driver uses both, MMIO and firmware interface.
>Therefore putting a child node in the ELE device node is probably not
>correct, either!?
>
>In general I think it's a good idea as the fuses actually live inside
>the ELE block so this would properly describe the hardware, but again
>this would create a hard dependency on the closed source ELE firmware
>which I don't like that much.
The current ele driver does not export API for others to fill the structure
as i.MX8 imx_scu_call_rpc(). Actually I agree with Frank's idea regarding
ELE firmware provides low level data transfer, such as ELE firmware driver
providing an API such as imx_se_call_rpc() or imx_ele_call_rpc().
Regards
Peng
^ permalink raw reply
* Re: [PATCH v8 0/5] media: iris: add support for purwa platform
From: Wangao Wang @ 2026-06-23 6:15 UTC (permalink / raw)
To: Vikash Garodia, Bryan O'Donoghue, Dikshita Agarwal,
Abhinav Kumar, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio
Cc: wangao.wang, linux-media, linux-arm-msm, devicetree, linux-kernel,
Bryan O'Donoghue, Krzysztof Kozlowski, Konrad Dybcio,
Dmitry Baryshkov
In-Reply-To: <82834dc4-14da-4a92-a1bf-4b8e744094c7@oss.qualcomm.com>
On 2026/6/4 14:48, Vikash Garodia wrote:
> Hi Bjorn,
>
> On 5/29/2026 1:04 PM, Wangao Wang wrote:
>> This series enables the Iris video codec on purwa, allowing purwa to
>> use hardware‑accelerated video encoding and decoding.
>>
>> The Iris codec on purwa is nearly identical to the one on hamoa(X1E),
>> except that it requires one additional clock and uses a different OPP
>> table.
>>
>> Therefore, purwa can reuse the Iris node from hamoa, but the clocks
>> and OPP table need to be redefined.
>>
>> All patches have been tested with v4l2-compliance and v4l2-ctl on
>> purwa. And it does not affect existing targets.
>>
>> Dependencies:
>> https://lore.kernel.org/all/20260409-purwa-videocc-camcc-
>> v4-0-5a8e5f2dd4b2@oss.qualcomm.com/
>>
>
> <snip>
>>
>> ---
>> Wangao Wang (5):
>> dt-bindings: media: qcom,sm8550-iris: Add X1P42100 compatible
>> media: iris: Add hardware power on/off ops for X1P42100
>> media: iris: Add platform data for X1P42100
>> arm64: dts: qcom: purwa: Override Iris clocks and operating points
>> arm64: dts: qcom: purwa-iot-som: enable video
>>
>
> Media patches in this series are now applied. Do you plan to apply the
> DTS ones ?
>
> Regards,
> Vikash
>
Hi Bjorn, is everything going well? If you encounter any issues, please
feel free to give us feedback. Thank you.
--
Best Regards,
Wangao
^ permalink raw reply
* Re: [PATCH v4 01/13] clk: qcom: gcc-qcm2290: Keep the critical clocks always-on from probe
From: Imran Shaik @ 2026-06-23 6:14 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio, Loic Poulain,
Brian Masney, Ajit Pandey, Taniya Das, Jagadeesh Kona,
linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <CAO9ioeUtpSoorLUHXXLEjS+N_AWUtdX2ZD0X+Wss6O0+gwq55g@mail.gmail.com>
On 22-06-2026 08:42 pm, Dmitry Baryshkov wrote:
> On Sun, 21 Jun 2026 at 16:28, Imran Shaik <imran.shaik@oss.qualcomm.com> wrote:
>>
>>
>>
>> On 06-06-2026 04:54 pm, Dmitry Baryshkov wrote:
>>> On Thu, Jun 04, 2026 at 10:56:07AM +0530, Imran Shaik wrote:
>>>> Some GCC branch clocks are required to be kept always-on due to the
>>>> hardware requirements. Drop the modelling of those always-on QCM2290 GCC
>>>> clocks and use the latest .clk_cbcr convention to keep them enabled from
>>>> probe.
>>>
>>> You got the feedback, but it got ignored. There is no explanation about
>>> gcc_gpu_iref_clk and several other clocks becoming always on.
>>>
>>
>> Apologies for the late reply.
>>
>> I have updated generically that few clocks are required to be kept ON
>> due to the hardware requirements. For gcc_gpu_iref_clk clock the
>> Power-On-Reset value is default ON, and such clocks are kept ON from
>> probe. As Agatti is working functionally, I will keep this clock
>> modelling same as before.
>>
>> And I will update commit text that the GCC video ahb/xo clocks are
>> required to be kept ON similar to other camera/disp ahb/xo clocks.
>
> Why? What is _wrong_ with thow they are modelled now?
>
Usually this is required for the register access, hence we mark them
critical and keep them ON. As it is working on Agatti with the
modelling, will keep them as is for now.
Thanks,
Imran
^ permalink raw reply
* Re: [PATCH 3/3] hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
From: sashiko-bot @ 2026-06-23 6:07 UTC (permalink / raw)
To: Selvamani Rajagopal via B4 Relay; +Cc: conor+dt, linux-hwmon, devicetree, robh
In-Reply-To: <20260622-support-fd5121-from-onsemi-v1-3-b31767689c65@onsemi.com>
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] Directly exposing PMBUS_PAGE and paged PMBus registers via custom lockless sysfs attributes bypasses PMBus core locking and page caching, causing race conditions and hardware state corruption.
- [High] Incorrect mathematical conversion of READ_VOUT to AMD 6.25mV VIDs causes voltage read failures > 1.55V and applies dangerously incorrect voltages on writes.
- [High] The `svr_go` sysfs attribute ignores its mapped register index, allowing userspace to execute arbitrary PMBus/I2C Send Byte commands.
- [High] The I2C block read fallback in `fd5121_read_block_data` corrupts the payload by reading one byte too few and shifting uninitialized memory into the result.
- [High] The I2C block write fallback in `fd5121_write_block_data` omits the required PMBus length byte, corrupting write transactions.
- [Medium] Attempting to read 164/165 bytes for OTP/RAM using standard SMBus/I2C block read functions results in silent truncation to 32 bytes.
- [Medium] `fd5121_word_store` parses user input as a signed 16-bit integer, preventing valid unsigned PMBus values from being written.
- [Medium] The custom `clear_faults` sysfs attribute sends an invalid data payload for a standard PMBus Send Byte command.
- [Medium] The driver violates the hwmon subsystem ABI by exposing over 80 standard PMBus registers as custom raw sysfs attributes.
- [Medium] `fd5121_probe` enforces an overly restrictive `I2C_FUNC_I2C` check, breaking compatibility with standard SMBus controllers.
--
commit 1e218fd27c0a9b6bb8fade016af8d29cbdb6daa5
Author: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
hwmon: (pmbus/fd5121): Add support FD5121, FD5123 and FD5125
FD5121 is a dual-rail, multi-phase, digital controller that offers
full telemtry options including input/output voltage, current as
well as fault handling and identifications.
These controllers are compliant with PMBus specification.
> diff --git a/drivers/hwmon/pmbus/fd5121.c b/drivers/hwmon/pmbus/fd5121.c
> new file mode 100644
> index 0000000000000..e68c6d6cabbd9
> --- /dev/null
> +++ b/drivers/hwmon/pmbus/fd5121.c
[ ... ]
> +static s32 fd5121_read_block_data(const struct i2c_client *client,
> + u8 cmd_code, u8 len, u8 *pbuf)
> +{
> + s32 ret = 0;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
> +
> + /* Payload length is in the first byte. */
> + ret = i2c_smbus_read_i2c_block_data(client, cmd_code,
> + len, pbuf);
> + if (ret < 0)
> + return ret;
> + ret = pbuf[0];
> + if (ret > len)
> + ret = len;
> + for (int idx = 0; idx < ret; idx++)
> + pbuf[idx] = pbuf[idx + 1];
> + return ret;
[Severity: High]
Does this fallback logic corrupt the payload by reading one byte too few?
Since the device replies with an SMBus block read format (a length byte
followed by the payload), requesting exactly len bytes retrieves the length
byte and only len - 1 data bytes.
This means the last byte of the payload is dropped, and the subsequent
left-shift copies a trailing uninitialized byte into the final payload
position. Should it request len + 1 bytes instead?
[ ... ]
> +static ssize_t fd5121_send_byte_store(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 val = 0;
> + int ret;
> +
> + ret = kstrtou8(buf, 10, &val);
> + if (ret < 0)
> + return ret;
> + ret = i2c_smbus_write_byte(client, val);
[Severity: High]
Does this allow userspace command injection?
This attribute is mapped to PMBUS_REG_MFR_SVR_GO, but it parses the
user-provided string into val and directly sends that as the SMBus command
code, completely ignoring attr->index. This allows any arbitrary command
code to be broadcast instead of SVR_GO.
[ ... ]
> +static ssize_t fd5121_byte_store(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 reg = attr->index;
> + int ret = 0;
> + u8 val = 0;
> +
> + switch (reg) {
> + case PMBUS_REG_MFR_CONFIG_ACTIVATE:
> + ret = fd5121_config_activate(client);
> + if (ret < 0)
> + return ret;
> + return count;
> + default:
> + ret = kstrtou8(buf, 10, &val);
> + if (ret < 0)
> + return ret;
> + break;
> + }
> + if (reg == PMBUS_PAGE && ((val != 0 && val != 1 &&
> + val != GENMASK(7, 0))))
> + return -EINVAL;
> + ret = i2c_smbus_write_byte_data(client, reg, val);
[Severity: High]
Can writing to the PMBUS_PAGE attribute directly corrupt the PMBus core's
page tracking?
By modifying the physical hardware's page using raw i2c_smbus_write_byte_data
instead of the PMBus core accessors, the core's cached data->currpage
becomes out of sync. Subsequent accesses by the PMBus core could execute
on the wrong hardware rail.
[Severity: Medium]
Additionally, if this is used to send PMBUS_CLEAR_FAULTS, does it violate
the PMBus specification?
CLEAR_FAULTS is strictly a Send Byte command with no data payload, but
i2c_smbus_write_byte_data() transmits an extra data byte.
[ ... ]
> +static ssize_t fd5121_word_store(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 reg = attr->index;
> + s16 val = 0;
> + int ret = 0;
> +
> + switch (reg) {
> + case PMBUS_REG_MFR_PWD_PROGRAM_RAM:
> + val = 0xC93F;
> + break;
> + default:
> + ret = kstrtos16(buf, 10, &val);
[Severity: Medium]
Does using kstrtos16() prevent configuring valid unsigned PMBus word values?
PMBus word registers represent 16-bit unsigned integers, but parsing
them as a signed s16 causes any valid input between 32768 and 65535 to
be rejected with an -ERANGE error.
[ ... ]
> +static ssize_t fd5121_word_show(struct device *dev,
> + struct device_attribute *da, char *buf)
> +{
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 };
> + u8 reg = attr->index;
> + s32 ret = 0;
> +
> + if (fd5121_blk_rd_reg(reg)) {
> + ret = fd5121_read_block_data(client, reg, 2, data);
> + if (ret >= 0)
> + ret = get_unaligned_le16(data);
> + } else
> + ret = i2c_smbus_read_word_data(client, reg);
[Severity: High]
Does this direct sysfs read lead to a race condition with the PMBus core?
Since these paged registers are read directly via i2c_smbus_read_word_data
without acquiring data->update_lock or ensuring the correct page is active,
concurrent PMBus operations could cause this to read from the wrong rail.
[ ... ]
> +static s32 fd5121_write_block_data(const struct i2c_client *client,
> + u8 cmd_code, u8 len, u8 *pbuf)
> +{
> + s32 ret = 0;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
> + ret = i2c_smbus_write_i2c_block_data(client, cmd_code,
> + len, pbuf);
[Severity: High]
Does this I2C block write fallback omit the mandatory PMBus length byte?
Unlike the SMBus equivalent, the I2C block write API does not prepend
a length byte on the wire. Without this length byte, the hardware will
misinterpret the first data byte (pbuf[0]) as the length, corrupting
the data payload.
[ ... ]
> +static ssize_t fd5121_block_show(struct device *dev,
> + struct device_attribute *da, char *buf)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
> + u8 reg = attr->index;
> + int printed = 0;
> + s32 ret = 0;
> + u8 len = 0;
> + int i = 0;
> +
> + if (reg == PMBUS_REG_MFR_FAULTY_SPS) {
> + int to_print = 0;
> +
> + len = 7;
> + ret = fd5121_read_block_data(client, reg, len, buffer);
> + if (ret < 0)
> + return ret;
> + printed = 0;
> + to_print = (ret < len) ? ret : len;
> + for (i = 0; i < to_print; i++)
> + printed += scnprintf(buf + printed,
> + PAGE_SIZE - printed,
> + "%02x", buffer[i]);
> + printed += scnprintf(buf + printed,
> + PAGE_SIZE - printed, "\n");
> + return printed;
> + } else if (reg == PMBUS_REG_BBR_RAM ||
> + reg == PMBUS_REG_BBR_OTP) {
> + u32 len = (reg == PMBUS_REG_BBR_OTP) ? 165 : 164;
> +
> + /* Extra byte may be needed in case we need to store
> + * the length of the data
> + */
> + u8 *tmp_in = kcalloc(len+1, sizeof(u8), GFP_KERNEL);
> +
> + if (tmp_in == NULL)
> + return -ENOMEM;
> + ret = fd5121_read_block_data(client, reg, len, tmp_in);
[Severity: Medium]
Will this operation silently truncate the read to 32 bytes?
The requested lengths are 164 and 165, but the underlying I2C/SMBus
block read functions (i2c_smbus_read_block_data and its I2C fallback)
are strictly capped at I2C_SMBUS_BLOCK_MAX (32 bytes).
[ ... ]
> +static struct attribute *fd5121_paged_attrs[] = {
> + &sensor_dev_attr_operation.dev_attr.attr,
> + &sensor_dev_attr_vout_raw.dev_attr.attr,
> + &sensor_dev_attr_on_off_config.dev_attr.attr,
> + &sensor_dev_attr_clear_faults.dev_attr.attr,
> + &sensor_dev_attr_write_protect.dev_attr.attr,
> + &sensor_dev_attr_smbalert_mask.dev_attr.attr,
> + &sensor_dev_attr_vout_mode.dev_attr.attr,
> + &sensor_dev_attr_vout_command.dev_attr.attr,
[Severity: Medium]
Is it intentional to expose standard PMBus registers as raw custom sysfs
files here?
Defining over 80 standard PMBus commands (like vout_command, operation,
clear_faults) as custom attributes bypasses the standardized hwmon ABI
(e.g., in1_input). The PMBus core framework automatically translates
these standard features into the required ABI format.
[ ... ]
> +static int fd5121_read_word_data(struct i2c_client *client, int page,
> + int phase, int reg)
> +{
> + int ret;
> +
> + ret = fd5121_valid_reg(client, reg);
> + if (ret < 0)
> + return ret;
> +
> + ret = pmbus_read_word_data(client, page, phase, reg);
> + if (ret < 0)
> + return ret;
> +
> + /* Chip reports VOUT_MODE as vid. But gives raw value 1mV per bit.
> + * So, encode the READ_VOUT value so that it gets decoded and
> + * reported correctly.
> + */
> + if (reg == PMBUS_READ_VOUT)
> + ret = DIV_ROUND_CLOSEST(155000 - ret * 100, 625);
> + return ret;
> +}
[Severity: High]
Does this calculation fail for voltages above 1.55V?
Since the raw VOUT is 1mV per bit, if ret is greater than 1550, the
numerator (155000 - ret * 100) evaluates to a negative number. The PMBus
core will interpret this negative value as an error code (e.g., -72),
causing sysfs reads to fail.
> +static int fd5121_write_word_data(struct i2c_client *client, int page,
> + int reg, u16 word)
> +{
> + int ret;
> +
> + ret = fd5121_valid_reg(client, reg);
> + if (ret < 0)
> + return ret;
> + ret = pmbus_write_word_data(client, page, reg, word);
> + return ret;
> +}
[Severity: High]
Is there a missing reverse translation for PMBUS_VOUT_COMMAND?
Because info->vrm_version[idx] = amd625mv is declared, the PMBus core
translates requested voltages into AMD VIDs and sends them here. Without
an inverse translation to the hardware's native 1mV/bit format, a VID
like 88 would be written directly, severely undervolting the hardware
to 88mV.
[ ... ]
> +static int fd5121_probe(struct i2c_client *client)
> +{
> + struct pmbus_driver_info *info;
> + struct fd5121_data *pdata;
> + u32 id;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> + return -EOPNOTSUPP;
[Severity: Medium]
Does this unconditionally break compatibility with pure SMBus controllers?
Many standard PC and server host controllers only support SMBus operations
and do not set the I2C_FUNC_I2C flag. Since the driver heavily utilizes
SMBus commands, checking for full I2C functionality might prevent the driver
from probing on common hardware.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-support-fd5121-from-onsemi-v1-0-b31767689c65@onsemi.com?part=3
^ permalink raw reply
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