* Re: [PATCH v2 1/8] dt-bindings: mfd: khadas: Add new compatible for Khadas VIM4 MCU
From: Ronald Claveau @ 2026-04-16 8:25 UTC (permalink / raw)
To: Rob Herring
Cc: Neil Armstrong, Lee Jones, Krzysztof Kozlowski, Conor Dooley,
Andi Shyti, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Beniamino Galvani, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui,
Lukasz Luba, Liam Girdwood, Mark Brown, linux-amlogic, devicetree,
linux-kernel, linux-i2c, linux-arm-kernel, linux-pm
In-Reply-To: <20260415214815.GA602572-robh@kernel.org>
On 4/15/26 11:48 PM, Rob Herring wrote:
> On Fri, Apr 03, 2026 at 06:08:34PM +0200, Ronald Claveau wrote:
>> The Khadas VIM4 MCU register is slightly different
>> from previous boards' MCU.
>> This board also features a switchable power source for its fan.
>>
>> Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
>> ---
>> Documentation/devicetree/bindings/mfd/khadas,mcu.yaml | 5 +++++
>> 1 file changed, 5 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml b/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml
>> index 084960fd5a1fd..67769ef5d58b1 100644
>> --- a/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml
>> +++ b/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml
>> @@ -18,6 +18,7 @@ properties:
>> compatible:
>> enum:
>> - khadas,mcu # MCU revision is discoverable
>
> The revision is no longer discoverable as was claimed?
>
The firmware revision is still discoverable, and via the same register,
but the VIM4 MCU has a different register layout (eg: no DEVICE_NO
register). The new compatible is needed to describe a different MCU
variant, not a different revision of the same MCU.
I will remove the comment as it is confusing with new boards.
>> + - khadas,vim4-mcu
>>
>> "#cooling-cells": # Only needed for boards having FAN control feature
>> const: 2
>> @@ -25,6 +26,10 @@ properties:
>> reg:
>> maxItems: 1
>>
>> + fan-supply:
>> + description: Phandle to the regulator that powers the fan.
>> + $ref: /schemas/types.yaml#/definitions/phandle
>> +
>> required:
>> - compatible
>> - reg
>>
>> --
>> 2.49.0
>>
--
Best regards,
Ronald
^ permalink raw reply
* Re: [PATCH v4 1/2] dt-bindings: leds: Document LTC3208 Multidisplay LED Driver
From: Krzysztof Kozlowski @ 2026-04-16 8:41 UTC (permalink / raw)
To: Jan Carlo Roleda
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-kernel, linux-leds, devicetree
In-Reply-To: <20260416-upstream-ltc3208-v4-1-3884ed3e49f5@analog.com>
On Thu, Apr 16, 2026 at 10:39:06AM +0800, Jan Carlo Roleda wrote:
> Add Devicetree Documentation for LTC3208 Multidisplay LED Driver.
>
> Signed-off-by: Jan Carlo Roleda <jancarlo.roleda@analog.com>
> ---
> .../devicetree/bindings/leds/adi,ltc3208.yaml | 181 +++++++++++++++++++++
> MAINTAINERS | 7 +
> 2 files changed, 188 insertions(+)
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v7 0/8] Add support for handling PCIe M.2 Key E connectors in devicetree
From: Manivannan Sadhasivam @ 2026-04-16 8:34 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Andy Shevchenko, Manivannan Sadhasivam, Rob Herring,
Greg Kroah-Hartman, Jiri Slaby, Nathan Chancellor, Nicolas Schier,
Hans de Goede, Ilpo Järvinen, Mark Pearson, Derek J. Clark,
Krzysztof Kozlowski, Conor Dooley, Marcel Holtmann,
Luiz Augusto von Dentz, Bartosz Golaszewski, Bartosz Golaszewski,
linux-serial, linux-kernel, linux-kbuild, platform-driver-x86,
linux-pci, devicetree, linux-arm-msm, linux-bluetooth, linux-pm,
Stephan Gerhold, Dmitry Baryshkov, linux-acpi, Hans de Goede,
Bartosz Golaszewski, Luca Ceresoli
In-Reply-To: <CAGXv+5EPA29G-fsH=wWOD8AK6TZFezFhsE0NHPYj_Pt3nT+d_w@mail.gmail.com>
On Wed, Apr 15, 2026 at 04:31:24PM +0800, Chen-Yu Tsai wrote:
> On Tue, Apr 14, 2026 at 8:03 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> >
> > On Tue, Apr 14, 2026 at 06:29:02PM +0800, Chen-Yu Tsai wrote:
> > > On Tue, Apr 14, 2026 at 4:28 PM Andy Shevchenko
> > > <andriy.shevchenko@linux.intel.com> wrote:
> > > > On Tue, Apr 14, 2026 at 01:03:19PM +0800, Chen-Yu Tsai wrote:
> > > > > On Tue, Apr 14, 2026 at 12:08 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > > > > > On Mon, Apr 13, 2026 at 07:33:12PM +0530, Manivannan Sadhasivam wrote:
> > > > > > > On Mon, Apr 13, 2026 at 03:54:59PM +0800, Chen-Yu Tsai wrote:
> > > > > > > > On Thu, Mar 26, 2026 at 01:36:28PM +0530, Manivannan Sadhasivam wrote:
> >
> > ...
> >
> > > > > > > > - Given that this connector actually represents two devices, how do I
> > > > > > > > say I want the BT part to be a wakeup source, but not the WiFi part?
> > > > > > > > Does wakeup-source even work at this point?
> > > > > > >
> > > > > > > You can't use the DT property since the devices are not described in DT
> > > > > > > statically. But you can still use the per-device 'wakeup' sysfs knob to enable
> > > > > > > wakeup.
> > > > >
> > > > > I see. I think not being able to specify generic properties for the devices
> > > > > on the connector is going to be a bit problematic.
> > > >
> > > > This is nature of the open-connectors, especially on the busses that are
> > > > hotpluggable, like PCIe. We never know what is connected there _ahead_.
> > >
> > > I believe what you mean by "hotpluggable" is "user replaceable".
> >
> > From the OS perspective it's the same. From platform perspective
> > there is a difference, granted.
>
> Yes. I just wanted to clarify.
>
> > > > In other words you can't describe in DT something that may not exist.
> > >
> > > But this is actually doable with the PCIe slot representation. The
> > > properties are put in the device node for the slot. If no card is
> > > actually inserted in the slot, then no device is created, and the
> > > device node is left as not associated with anything.
> >
> > But you need to list all devices in the world if you want to support this
>
> Why would I need to? The PCIe slot representation just describes a
> PCIe bridge. Granted this might not be entirely correct, but it's
> what we currently have.
>
> And even then, there are properties like memory-region or wakeup-source
> that are generic and aren't tied to specific devices.
>
> > somehow. Yes, probably many of them (or majority) will be enumerated as is,
> > but some may need an assistance via (dynamic) properties or similar mechanisms.
>
> Even if we wanted to add dynamic properties, there is currently no proper
> device node to attach them to.
>
There are dynamic device nodes that we need to create for hotpluggable devices,
if we need to pass the DT properties to the driver. Like how we do it for PCIe,
serdev. You cannot describe static device nodes in DT for devices attached to
swappable cards like M.2.
> > > It's just that for this new M.2 E-key connector, there aren't separate
> > > nodes for each interface. And the system doesn't associate the device
> > > node with the device, because it's no longer a child node of the
> > > controller or hierarchy, but connected over the OF graph.
> > >
> > > Moving over to the E-key connector representation seems like one step
> > > forward and one step backward in descriptive ability. We gain proper
> > > power sequencing, but lose generic properties.
> >
> > The "key" is property of the connector. Hence if you have an idea what can be
> > common for ALL "key":s, that's probably can be abstracted. Note, I'm not
> > familiar with the connector framework in the Linux kernel, perhaps it's already
> > that kind of abstraction.
>
> I'm not arguing for a even more generic "M.2" connector. The "key" is
> already described in the compatible. I'm saying we should have some way
> of describing the individual interfaces (PCIe, SDIO, USB, UART, I2S, I2C)
> on the connector so further nodes or properties can be attached to them,
> either with overlays or dynamically within the kernel. Right now the
> are only described as individual ports, but we can't actually tie a
> device to a OF graph port.
>
If there are properties that apply to the interfaces, *not devices*, then you
can add them to the relevant endpoint node:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/connector/pcie-m2-e-connector.yaml#n167
> But maybe I'm overthinking the representation part. AFAICT for Qualcomm's
> UART-based BT bit part, Mani just had the driver create a device node
> under the UART (by traversing the OF graph to find the UART). If that's
> the desired way then the connector binding should mention it.
What do you mean by 'connector binding should mention it'? You cannot hardcode
the device node in the connector binding, because it is not part of the
connector binding itself. For example, the UART device node that is created for
the WCN7850, already has a binding:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml
What the driver does is, just creating that node under the serdev controller as
if the device was described statically in the DT.
For the wakeup property you asked earlier, it depends on the interface. If the
interface supports "in-band" wakeup like USB/SDIO, you can add the property in
the relevant controller node statically itself. For example, with USB XHCI
controller, you would know that the controller will support wakeup or not
statically. So you will add that property to the controller node. Then, if the
user has enabled the wakeup for a specific USB device like keyboard/mouse
through sysfs, then wakeup will just work. Here, we don't need to create the
USB device node at all.
But if the interface supports physical wakeup, like UART_WAKE# in M.2 SDIO Key
E, then based on the presence of that property, you would configure wakeup in
the connector driver itself. But this is not present as of now.
> And that
> works for me. But I think it's messier and also we're missing an
> opportunity to make the M.2 connector a standardized attachment point
> for overlays.
>
I don't envision using overlays for the M.2 connectors. It is not strictly
needed, unless the connector is non-standard.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v4] dt-bindings: display: ti, am65x-dss: Fix AM62L DSS reg and clock constraints
From: Krzysztof Kozlowski @ 2026-04-16 8:32 UTC (permalink / raw)
To: Swamil Jain
Cc: jyri.sarha, tomi.valkeinen, maarten.lankhorst, mripard,
tzimmermann, airlied, simona, robh, krzk+dt, conor+dt, devarsht,
dri-devel, devicetree, linux-kernel, praneeth, vigneshr
In-Reply-To: <20260415110409.2577633-1-s-jain1@ti.com>
On Wed, Apr 15, 2026 at 04:34:09PM +0530, Swamil Jain wrote:
> The AM62L DSS [1] support incorrectly used the same register and
> clock constraints as AM65x, but AM62L has a single video port
>
> Fix this by adding conditional constraints that properly define the
> register regions and clocks for AM62L DSS (single video port) versus
> other AM65x variants (dual video port).
>
> [1]: Section 12.7 (Display Subsystem and Peripherals)
> Link : https://www.ti.com/lit/pdf/sprujb4
>
> Fixes: cb8d4323302c ("dt-bindings: display: ti,am65x-dss: Add support for AM62L DSS")
> Cc: stable@vger.kernel.org
> Signed-off-by: Swamil Jain <s-jain1@ti.com>
> ---
> Validated the changes with some examples:
> https://gist.github.com/swamiljain/79f30568c9ece89f5a20218f52647486
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 1/2] dt-bindings: pwm: dwc: add reset optional
From: Krzysztof Kozlowski @ 2026-04-16 8:24 UTC (permalink / raw)
To: Conor Dooley
Cc: dongxuyang, ukleinek, robh, krzk+dt, conor+dt, ben-linux,
ben.dooks, p.zabel, linux-pwm, devicetree, linux-kernel, ningyu,
linmin, xuxiang, wangguosheng, pinkesh.vaghela
In-Reply-To: <20260415-reacquire-handstand-d1923af82c9d@spud>
On Wed, Apr 15, 2026 at 04:09:49PM +0100, Conor Dooley wrote:
> On Wed, Apr 15, 2026 at 05:50:20PM +0800, dongxuyang@eswincomputing.com wrote:
> > From: Xuyang Dong <dongxuyang@eswincomputing.com>
> >
> > The DesignWare PWM includes separate reset signals dedicated to each clock
> > domain:
> > The presetn signal resets logic in pclk domain.
> > The timer_N_resetn signal resets logic in the timer_N_clk domain.
> > The resets are active-low.
> >
> > Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
>
> This commit implies that your hardware differs from existing devices,
> I think you should add a device-specific compatible.
>
> > ---
> > .../devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml | 3 +++
> > 1 file changed, 3 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..a8bbad0360f8 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,9 @@ properties:
> > - const: bus
> > - const: timer
> >
> > + resets:
> > + maxItems: 2
And this should really be listed with description, because order is
fixed.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 02/13] dt-bindings: leds: document Samsung S2M series PMIC RGB LED device
From: Krzysztof Kozlowski @ 2026-04-16 8:23 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, MyungJoo Ham, Chanwoo Choi, Sebastian Reichel,
André Draszik, Alexandre Belloni, Jonathan Corbet,
Shuah Khan, Nam Tran, Łukasz Lebiedziński, linux-leds,
devicetree, linux-kernel, linux-pm, linux-samsung-soc, linux-rtc,
linux-doc
In-Reply-To: <DHTWNPSQ06IJ.24A9E1FL1RWER@disroot.org>
On Wed, Apr 15, 2026 at 11:00:16PM +0530, Kaustabh Chakraborty wrote:
> On 2026-04-15 09:03 +02:00, Krzysztof Kozlowski wrote:
> > On Tue, Apr 14, 2026 at 12:02:54PM +0530, Kaustabh Chakraborty wrote:
> >> +description: |
> >> + The Samsung S2M series PMIC RGB LED is a three-channel LED device with
> >> + 8-bit brightness control for each channel, typically used as status
> >> + indicators in mobile phones.
> >> +
> >> + This is a part of device tree bindings for S2M and S5M family of Power
> >> + Management IC (PMIC).
> >> +
> >> + See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
> >> + additional information and example.
> >> +
> >> +allOf:
> >> + - $ref: common.yaml#
> >
> > Rob's comment is still valid:
> > 1. How do you address one of three LEDs in non-RGB case?
> > 2. Where is multi-color?
>
> Yes, multi-color should have been added here.
>
> >
> > And based on this alone without other properties, I say this should be
> > part of top-level schema. Separate node is fine, but no need for
> > separate binding.
>
> BTW, for loading the sub-device driver via platform (as it won't be a
> separate binding) the driver *must* be built-in. Although not related to
> bindings, this seems counter-intuitive. I see the same problem with the
I don't understand that comment. If it has nothing to do with the
binding, what is the problem?
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 0/4] Introduce Allwinner H616 PWM controller
From: Richard GENOUD @ 2026-04-16 8:22 UTC (permalink / raw)
To: Paul Kocialkowski
Cc: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel, Thomas Petazzoni, John Stultz, Joao Schim,
linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel
In-Reply-To: <adfe2z2YeBxm_6oR@shepard>
Le 09/04/2026 à 19:16, Paul Kocialkowski a écrit :
> Hi Richard,
>
> On Thu 05 Mar 26, 10:19, Richard Genoud wrote:
>> Allwinner H616 PWM controller is quite different from the A10 one.
>
> As I've mentionned before, this PWM controller is not specific to the H616
> but also appears in other chips, so the name of the driver and registers
> should not mention H616.
>
> After further investigation, I can see multiple versions of this new PWM IP
> being used in different chips, starting with the R40/V40 (sun8iw11) in 2016.
>
> The latest downstream BSP driver has a list of the different generations:
> https://github.com/radxa/allwinner-bsp/blob/cubie-aiot-v1.4.6/drivers/pwm/pwm-sunxi.c#L1901
>
> We have a first generation called v100/v101 for the following chips:
> H616, R328 and R40. A second generation is called v200 and brings slight
> register layout differences for A133, D1/T113-S3 and V851. Subsequent
> iterations (v201-5) are used in more recent chips like A527 and A733 and
> seem register-compatible with v200 (from a quick look).
>
> So what I suggest here is to rename the driver "sun8i-pwm" and eventually add
> a list of generations to the driver and different registers when needed, with
> an appropriate suffix in their name.
>
> But since you're currently only dealing with H616, this work can be done later
> when introducing support for more chips.
ok, I'm fine with that :)
>
>> It can drive 6 PWM channels, and like for the A10, each channel has a
>> bypass that permits to output a clock, bypassing the PWM logic, when
>> enabled.
>>
>> But, the channels are paired 2 by 2, sharing a first set of
>> MUX/prescaler/gate.
>> Then, for each channel, there's another prescaler (that will be bypassed
>> if the bypass is enabled for this channel).
>>
>> It looks like that:
>> _____ ______ ________
>> OSC24M --->| | | | | |
>> APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> PWM_clock_src_xy
>> |_____| |______| |________|
>> ________
>> | |
>> +->| /div_k |---> PWM_clock_x
>> | |________|
>> | ______
>> | | |
>> +-->| Gate |----> PWM_bypass_clock_x
>> | |______|
>> PWM_clock_src_xy -----+ ________
>> | | |
>> +->| /div_k |---> PWM_clock_y
>> | |________|
>> | ______
>> | | |
>> +-->| Gate |----> PWM_bypass_clock_y
>> |______|
>>
>> Where xy can be 0/1, 2/3, 4/5
>>
>> PWM_clock_x/y serve for the PWM purpose.
>> PWM_bypass_clock_x/y serve for the clock-provider purpose.
>> The common clock framework has been used to manage those clocks.
>>
>> This PWM driver serves as a clock-provider for PWM_bypass_clocks.
>> This is needed for example by the embedded AC300 PHY which clock comes
>> from PMW5 pin (PB12).
>>
>> Usually, to get a clock from a PWM driver, we use the pwm-clock driver
>> so that the PWM driver doesn't need to be a clk-provider itself.
>> While this works in most cases, here it just doesn't.
>> That's because the pwm-clock request a period from the PWM driver,
>> without any clue that it actually wants a clock at a specific frequency,
>> and not a PWM signal with duty cycle capability.
>
> From what I understand the pwm-clock driver will either assume a fixed rate
> set in device-tree or deduce the rate from the pwm period. In any case it will
> check that the pwm period (which it cannot change) is the same as the requested
> clock period.
>
> So I agree that pwm-clock is unable to change the clock rate at runtime and will
> just use whatever frequency the pwm is running at (which is typically set
> in the device-tree consumer property).
>
>> So, the PWM driver doesn't know if it can use the bypass or not, it
>> doesn't even have the real accurate frequency information (23809524 Hz
>> instead of 24MHz) because PWM drivers only deal with periods.
>
> I agree that the driver needs to register as a proper clock provider in
> addition to pwm. But what happens if the same PWM clock is requested both from
> the clk side and the pwm side?
The first to request it is the winner :)
The other ones will receive a -EBUSY
In h616_pwm_request() and h616_pwm_of_clk_get(), the channel mode is
checked, and if it's free to use, it's set as either PWM or CLK mode so
that it can't be requested a second time.
>
>> With pwm-clock, we loose a precious information along the way (that we
>> actually want a clock and not a PWM signal).
>> That's ok with simple PWM drivers that don't have multiple input clocks,
>> but in this case, without this information, we can't know for sure which
>> clock to use.
>> And here, for instance, if we ask for a 24MHz clock, pwm-clock will
>> requests 42ns (assigned-clocks doesn't help for that matter). The logic
>> is to select the highest clock (100MHz) with no prescaler and a duty
>> cycle value of 2/4 => we have 25MHz instead of 24MHz.
>> And that's a perfectly fine choice for a PMW, because we still can
>> change the duty cycle in the range [0-4]/4.
>> But obviously for a clock, we don't care about the duty cycle, but more
>> about the clock accuracy.
>>
>> And actually, this PWM is really a PWM AND a real clock when the bypass
>> is set.
>
> Make sense to me.
>
>> This series is based onto v6.19-rc4
>>
>> NB: checkpatch is not happy with patch 2, but it's a false positive.
>> It doesn't detect that PWM_XY_SRC_MUX/GATE/DIV are structures, but as
>> it's more readable like that, I prefer keeping it that way.
>>
>> NB2: for geopolitical reasons, I didn't re-use the old series that Paul
>> was referring to.
>>
>> Changes since v3:
>> - gather Acked-by/Tested-by
>> - fix cast from pointer to integer of different size (kernel test robot
>> with arc platform)
>> - add devm_action for clk_hw_unregister_composite as suggested by Philipp
>> - remove now unused pwm_remove as suggested by Philipp
>>
>> Changes since v2:
>> - use U32_MAX instead of defining UINT32_MAX
>> - add a comment on U32_MAX usage in clk_round_rate()
>> - change clk_table_div_m (use macros)
>> - fix formatting (double space, superfluous comma, extra line feed)
>> - fix the parent clock order
>> - simplify code by using scoped_guard()
>> - add missing const in to_h616_pwm_chip() and rename to
>> h616_pwm_from_chip()
>> - add/remove missing/superflous error messages
>> - rename cnt->period_ticks, duty_cnt->duty_ticks
>> - fix PWM_PERIOD_MAX
>> - add .remove() callback
>> - fix DIV_ROUND_CLOSEST_ULL->DIV_ROUND_UP_ULL
>> - add H616_ prefix
>> - protect _reg in macros
>> - switch to waveforms instead of apply/get_state
>> - shrink struct h616_pwm_channel
>> - rebase on v6.19-rc4
>>
>> Changes since v1:
>> - rebase onto v6.19-rc1
>> - add missing headers
>> - remove MODULE_ALIAS (suggested by Krzysztof)
>> - use sun4i-pwm binding instead of creating a new one (suggested by Krzysztof)
>> - retrieve the parent clocks from the devicetree
>> - switch num_parents to unsigned int
>>
>> Richard Genoud (4):
>> dt-bindings: pwm: allwinner: add h616 pwm compatible
>> pwm: sun50i: Add H616 PWM support
>> arm64: dts: allwinner: h616: add PWM controller
>> MAINTAINERS: Add entry on Allwinner H616 PWM driver
>>
>> .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 19 +-
>> MAINTAINERS | 5 +
>> .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 +
>> drivers/pwm/Kconfig | 12 +
>> drivers/pwm/Makefile | 1 +
>> drivers/pwm/pwm-sun50i-h616.c | 936 ++++++++++++++++++
>> 6 files changed, 1019 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/pwm/pwm-sun50i-h616.c
>>
>>
>> base-commit: 11439c4635edd669ae435eec308f4ab8a0804808
>
Regards,
Richard
^ permalink raw reply
* Re: [PATCH v8 0/9] riscv: spacemit: enable SD card support with UHS modes for OrangePi RV2
From: Iker Pedrosa @ 2026-04-16 8:18 UTC (permalink / raw)
To: Troy Mitchell
Cc: Krzysztof Kozlowski, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Adrian Hunter, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Yixun Lan,
Michael Opdenacker, Javier Martinez Canillas, linux-mmc,
devicetree, linux-riscv, spacemit, linux-kernel, Anand Moon,
Trevor Gamblin, Vincent Legoll
In-Reply-To: <DHSQ6VG82QYX.1EVAYV9JTBCL7@linux.dev>
El mar, 14 abr 2026 a las 10:16, Troy Mitchell
(<troy.mitchell@linux.dev>) escribió:
>
> On Tue Apr 14, 2026 at 3:12 PM CST, Iker Pedrosa wrote:
> > El lun, 13 abr 2026 a las 10:07, Krzysztof Kozlowski
> > (<krzk@kernel.org>) escribió:
> >>
> >> On 13/04/2026 10:02, Iker Pedrosa wrote:
> >> > This series enables complete SD card support for the Spacemit K1-based
> >> > OrangePi RV2 board, including UHS (Ultra High Speed) modes for
> >> > high-performance SD card operation.
> >> >
> >> > Background
> >> >
> >> > The Spacemit K1 SoC includes an SDHCI controller capable of supporting
> >> > SD cards up to UHS-I speeds (SDR104 at 208MHz). However, mainline
> >> > currently lacks basic SD controller configuration, SDHCI driver
> >> > enhancements for voltage switching and tuning, and power management
> >> > infrastructure.
> >> >
> >> > Implementation
> >> >
> >> > The series enables SD card support through coordinated layers:
> >> >
> >> > - Hardware infrastructure (patches 1-2): Device tree bindings for voltage
> >> > switching hardware and essential clock infrastructure.
> >> > - SDHCI driver enhancements (patches 3-7): Regulator framework
> >> > integration, pinctrl state switching for voltage domains, AIB register
> >> > programming, and comprehensive SDR tuning support for reliable UHS
> >> > operation.
> >> > - SoC and board integration (patches 8-10): Complete K1 SoC controller
> >> > definitions, PMIC power infrastructure, and OrangePi RV2 board enablement
> >> > with full UHS support.
> >> >
> >> > This transforms the OrangePi RV2 from having no SD card support to full
> >> > UHS-I capability, enabling high-performance storage up to 208MHz.
> >> >
> >> > Tested-by: Michael Opdenacker <michael.opdenacker@rootcommit.com>
> >> > Signed-off-by: Iker Pedrosa <ikerpedrosam@gmail.com>
> >> > ---
> >> > Changes in v8:
> >> > - Resending the series as v8. The v7 submission failed due to an SMTP
> >> > error during transit, which resulted in a broken thread on the mailing
> >> > list.
> >>
> >> Hm? Everything is here:
> >> https://lore.kernel.org/all/20260413-orangepi-sd-card-uhs-v7-1-16650f49c022@gmail.com/
> >>
> >> You can send individual patches to fix up threading, use --in-reply-to.
> >
> > My apologies for the noise and the rapid resend.
> >
> > The reason for v8 was that the v7 cover letter (0/9) failed to reach
> > the mailing list due to an SMTP error on my end. This left the v7
> > thread "headless" in the archives without the changelog or the full
> > context of the series. I was attempting to fix the threading
> > immediately so that reviewers would have a complete set of patches to
> > look at, but I realize now that resending the entire series on the
> > same day was premature.
> So that's why Krzysztof said you should send individual patch with --in-reply-to.
I see, thanks for the clarification. Just to clarify for my future
workflow: is it acceptable for a series to be 'headless' (starting
with Patch 1) if the cover letter is lost, or is the cover letter
(Patch 0) strictly required as the thread root?
In such cases, would it be better to just send the missing cover
letter as a reply to Patch 1 afterward to complete the thread without
resending the whole series?
>
> - Troy
>
^ permalink raw reply
* Re: [PATCH 2/5] clk: qcom: add Global Clock controller (GCC) driver for IPQ9650 SoC
From: Kathiravan Thirumoorthy @ 2026-04-16 8:12 UTC (permalink / raw)
To: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260415-ipq9650_boot_to_shell-v1-2-b37eb4c3a1d1@oss.qualcomm.com>
On 4/15/2026 7:03 PM, Kathiravan Thirumoorthy wrote:
> Add support for the global clock controller found on IPQ9650 SoC.
>
> Signed-off-by: Kathiravan Thirumoorthy<kathiravan.thirumoorthy@oss.qualcomm.com>
I missed to cleanup the CLK_IGNORE_UNUSED flags. Sorry for that, will
take care of that in V2.
^ permalink raw reply
* Re: [PATCH V10 4/4] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring
From: Jishnu Prakash @ 2026-04-16 8:05 UTC (permalink / raw)
To: Daniel Lezcano
Cc: jic23, robh, krzk+dt, conor+dt, agross, andersson, lumag,
dmitry.baryshkov, konradybcio, daniel.lezcano, sboyd, amitk,
thara.gopinath, lee, rafael, subbaraman.narayanamurthy,
david.collins, anjelique.melendez, kamal.wadhwa, rui.zhang,
lukasz.luba, devicetree, linux-arm-msm, linux-iio, linux-kernel,
linux-pm, cros-qcom-dts-watchers, quic_kotarake, neil.armstrong,
stephan.gerhold
In-Reply-To: <addDTiI8MB2b_AzJ@mai.linaro.org>
Hi Daniel,
On 4/9/2026 11:42 AM, Daniel Lezcano wrote:
> On Fri, Jan 30, 2026 at 05:24:21PM +0530, Jishnu Prakash wrote:
>> Add support for ADC_TM part of PMIC5 Gen3.
>>
>> This is an auxiliary driver under the Gen3 ADC driver, which implements the
>> threshold setting and interrupt generating functionalities of QCOM ADC_TM
>> drivers, used to support thermal trip points.
>>
>> Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
...
>> +
>> +static irqreturn_t adctm5_gen3_isr(int irq, void *dev_id)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = dev_id;
>> + int ret, sdam_num;
>> + u8 tm_status[2];
>> + u8 status, val;
>> +
>> + sdam_num = get_sdam_from_irq(adc_tm5, irq);
>> + if (sdam_num < 0) {
>> + dev_err(adc_tm5->dev, "adc irq %d not associated with an sdam\n",
>> + irq);
>> + return IRQ_HANDLED;
>> + }
>> +
>> + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_STATUS1,
>> + &status, sizeof(status));
>> + if (ret) {
>> + dev_err(adc_tm5->dev, "adc read status1 failed with %d\n", ret);
>> + return IRQ_HANDLED;
>> + }
>> +
>> + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) {
>> + dev_err_ratelimited(adc_tm5->dev,
>> + "Unexpected conversion fault, status:%#x\n",
>> + status);
>> + val = ADC5_GEN3_CONV_ERR_CLR_REQ;
>> + adc5_gen3_status_clear(adc_tm5->dev_data, sdam_num,
>> + ADC5_GEN3_CONV_ERR_CLR, &val, 1);
>> + return IRQ_HANDLED;
>> + }
>> +
>> + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_TM_HIGH_STS,
>> + tm_status, sizeof(tm_status));
>> + if (ret) {
>> + dev_err(adc_tm5->dev, "adc read TM status failed with %d\n", ret);
>> + return IRQ_HANDLED;
>> + }
>> +
>> + if (tm_status[0] || tm_status[1])
>> + schedule_work(&adc_tm5->tm_handler_work);
>> +
>> + dev_dbg(adc_tm5->dev, "Interrupt status:%#x, high:%#x, low:%#x\n",
>> + status, tm_status[0], tm_status[1]);
>> +
>> + return IRQ_HANDLED;
>
> This ISR routine should be revisited:
>
> - no error message inside
I'll drop all the error messages, but does that also include the debug print at the end?
In addition, the print for conversion fault is ratelimited and may be useful as it
indicates a possible HW issue, can I keep that?
>
> - use a shared interrupt to split what is handled by the ADC and the
> TM drivers
I'll make the required updates in the main ADC driver and this driver to share the first
SDAM's interrupt.
>
> - do not return IRQ_HANDLED in case of error (cf. irqreturn.h doc)
>
I'll replace IRQ_HANDLED with IRQ_NONE at places where errors are returned.
But in the case of conversion fault, I think returning IRQ_HANDLED may be
more appropriate because we do handle it by clearing the status, to
allow subsequent conversion requests to be sent.
What do you think, is this fine?
> - do not use a dedicated workqueue but the threaded mechanism of the irq
>
I'll make this change.
>> +}
>> +
>> +static int adc5_gen3_tm_status_check(struct adc_tm5_gen3_chip *adc_tm5,
>> + int sdam_index, u8 *tm_status, u8 *buf)
>> +{
>> + int ret;
>> +
>> + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_index, ADC5_GEN3_TM_HIGH_STS,
>> + tm_status, 2);
>> + if (ret) {
>> + dev_err(adc_tm5->dev, "adc read TM status failed with %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = adc5_gen3_status_clear(adc_tm5->dev_data, sdam_index, ADC5_GEN3_TM_HIGH_STS_CLR,
>> + tm_status, 2);
>> + if (ret) {
>> + dev_err(adc_tm5->dev, "adc status clear conv_req failed with %d\n",
>> + ret);
>> + return ret;
>> + }
>> +
>> + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_index, ADC5_GEN3_CH_DATA0(0),
>> + buf, 16);
>> + if (ret)
>> + dev_err(adc_tm5->dev, "adc read data failed with %d\n", ret);
>> +
>> + return ret;
>> +}
>> +
>> +static void tm_handler_work(struct work_struct *work)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = container_of(work, struct adc_tm5_gen3_chip,
>> + tm_handler_work);
>> + int sdam_index = -1;
>> + u8 tm_status[2] = { };
>> + u8 buf[16] = { };
>> +
>> + for (int i = 0; i < adc_tm5->nchannels; i++) {
>> + struct adc_tm5_gen3_channel_props *chan_prop = &adc_tm5->chan_props[i];
>> + int offset = chan_prop->tm_chan_index;
>> + bool upper_set, lower_set;
>> + int ret, temp;
>> + u16 code;
>> +
>> + scoped_guard(adc5_gen3, adc_tm5) {
>> + if (chan_prop->sdam_index != sdam_index) {
>> + sdam_index = chan_prop->sdam_index;
>> + ret = adc5_gen3_tm_status_check(adc_tm5, sdam_index,
>> + tm_status, buf);
>> + if (ret)
>> + return;
>> + }
>> +
>> + upper_set = ((tm_status[0] & BIT(offset)) && chan_prop->high_thr_en);
>> + lower_set = ((tm_status[1] & BIT(offset)) && chan_prop->low_thr_en);
>> + }
>> +
>> + if (!(upper_set || lower_set))
>> + continue;
>> +
>> + code = get_unaligned_le16(&buf[2 * offset]);
>> + dev_dbg(adc_tm5->dev, "ADC_TM threshold code:%#x\n", code);
>
> Please avoid debug traces when possible
>
>> + ret = adc5_gen3_therm_code_to_temp(adc_tm5->dev,
>> + &chan_prop->common_props,
>> + code, &temp);
>> + if (ret) {
>> + dev_err(adc_tm5->dev,
>> + "Invalid temperature reading, ret = %d, code=%#x\n",
>> + ret, code);
>
> And avoid error traces in the runtime path
Will drop the above prints here and in other similar places if any.
>
>> + continue;
>> + }
>> +
>> + chan_prop->last_temp = temp;
>> + chan_prop->last_temp_set = true;
>> + thermal_zone_device_update(chan_prop->tzd, THERMAL_TRIP_VIOLATED);
>> + }
>> +}
>> +
>> +static int adc_tm5_gen3_get_temp(struct thermal_zone_device *tz, int *temp)
>> +{
>> + struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz);
>> + struct adc_tm5_gen3_chip *adc_tm5;
>> +
>> + if (!prop || !prop->chip)
>> + return -EINVAL;
>> +
>> + adc_tm5 = prop->chip;
>> +
>> + if (prop->last_temp_set) {
>> + pr_debug("last_temp: %d\n", prop->last_temp);
>> + prop->last_temp_set = false;
>> + *temp = prop->last_temp;
>> + return 0;
>> + }
>
> Why do you need to do that?
>
> The temperature should reflect the current situation even if the
> reading was triggered by a thermal trip violation.
>
This logic is needed to handle a corner case issue we have seen earlier.
In this case, the ADC_TM threshold violation interrupt gets triggered ,
but when get_temp() is subsequently called by the thermal framework, the
temperature has fluctuated and the value read now lies within the thresholds,
so the thresholds do not get updated by the thermal framework and the violation
interrupts get repeated several times, until there is a get_temp() call
which returns a temperature outside the threshold range.
In order to avoid this issue, when the interrupt handler runs, we find the actual
temperature read in ADC_TM that led to threshold violation by reading the ADC_TM
data registers and we cache it and return it when get_temp() is called in the flow
of thermal_zone_device_update(). Any subsequent calls to get_temp() would
return the actual channel temperature at the time.
This is only done to avoid delaying thermal mitigation due to temperature
fluctuations. Do you think this needs to be changed?
>> +
>> + return adc5_gen3_get_scaled_reading(adc_tm5->dev, &prop->common_props,
>> + temp);
>> +}
>> +
>> +static int adc_tm5_gen3_disable_channel(struct adc_tm5_gen3_channel_props *prop)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = prop->chip;
>> + int ret;
>> + u8 val;
>> +
>> + prop->high_thr_en = false;
>> + prop->low_thr_en = false;
>> +
>> + ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index);
>> + if (ret)
>> + return ret;
>> +
>> + val = BIT(prop->tm_chan_index);
>> + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
>> + ADC5_GEN3_TM_HIGH_STS_CLR, &val, sizeof(val));
>> + if (ret)
>> + return ret;
>> +
>> + val = MEAS_INT_DISABLE;
>> + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
>> + ADC5_GEN3_TIMER_SEL, &val, sizeof(val));
>> + if (ret)
>> + return ret;
>> +
>> + /* To indicate there is an actual conversion request */
>> + val = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index;
>> + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
>> + ADC5_GEN3_PERPH_CH, &val, sizeof(val));
>> + if (ret)
>> + return ret;
>> +
>> + val = ADC5_GEN3_CONV_REQ_REQ;
>> + return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
>> + ADC5_GEN3_CONV_REQ, &val, sizeof(val));
>> +}
>> +
>> +#define ADC_TM5_GEN3_CONFIG_REGS 12
>
> Please define at the top of the file
>
>> +static int adc_tm5_gen3_configure(struct adc_tm5_gen3_channel_props *prop,
>> + int low_temp, int high_temp)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = prop->chip;
>> + u8 buf[ADC_TM5_GEN3_CONFIG_REGS];
>> + u8 conv_req;
>> + u16 adc_code;
>> + int ret;
>> +
>> + ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = adc5_gen3_read(adc_tm5->dev_data, prop->sdam_index,
>> + ADC5_GEN3_SID, buf, sizeof(buf));
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Write SID */
>> + buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->common_props.sid);
>> +
>> + /* Select TM channel and indicate there is an actual conversion request */
>> + buf[1] = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index;
>> +
>> + buf[2] = prop->timer;
>> +
>> + /* Digital param selection */
>> + adc5_gen3_update_dig_param(&prop->common_props, &buf[3]);
>> +
>> + /* Update fast average sample value */
>> + buf[4] &= ~ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK;
>> + buf[4] |= prop->common_props.avg_samples | ADC5_GEN3_FAST_AVG_CTL_EN;
>> +
>> + /* Select ADC channel */
>> + buf[5] = prop->common_props.channel;
>> +
>> + /* Select HW settle delay for channel */
>> + buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK,
>> + prop->common_props.hw_settle_time_us);
>> +
>> + /* High temperature corresponds to low voltage threshold */
>> + prop->low_thr_en = (high_temp != INT_MAX);
>> + if (prop->low_thr_en) {
>> + adc_code = qcom_adc_tm5_gen2_temp_res_scale(high_temp);
>> + put_unaligned_le16(adc_code, &buf[8]);
>> + }
>> +
>> + /* Low temperature corresponds to high voltage threshold */
>> + prop->high_thr_en = (low_temp != -INT_MAX);
>> + if (prop->high_thr_en) {
>> + adc_code = qcom_adc_tm5_gen2_temp_res_scale(low_temp);
>> + put_unaligned_le16(adc_code, &buf[10]);
>> + }
>> +
>> + buf[7] = 0;
>> + if (prop->high_thr_en)
>> + buf[7] |= ADC5_GEN3_HIGH_THR_INT_EN;
>> + if (prop->low_thr_en)
>> + buf[7] |= ADC5_GEN3_LOW_THR_INT_EN;
>> +
>> + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, ADC5_GEN3_SID,
>> + buf, sizeof(buf));
>> + if (ret < 0)
>> + return ret;
>> +
>> + conv_req = ADC5_GEN3_CONV_REQ_REQ;
>> + return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index,
>> + ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req));
>> +}
>> +
>> +static int adc_tm5_gen3_set_trip_temp(struct thermal_zone_device *tz,
>> + int low_temp, int high_temp)
>> +{
>> + struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz);
>> + struct adc_tm5_gen3_chip *adc_tm5;
>> +
>> + if (!prop || !prop->chip)
>> + return -EINVAL;
>> +
>> + adc_tm5 = prop->chip;
>> +
>> + dev_dbg(adc_tm5->dev, "channel:%s, low_temp(mdegC):%d, high_temp(mdegC):%d\n",
>> + prop->common_props.label, low_temp, high_temp);
>> +
>> + guard(adc5_gen3)(adc_tm5);
>> + if (high_temp == INT_MAX && low_temp == -INT_MAX)
>> + return adc_tm5_gen3_disable_channel(prop);
>
> Why disable the channel instead of returning an errno ?
>
This is the convention we follow in our existing ADC_TM driver at
drivers/thermal/qcom/qcom-spmi-adc-tm5.c. If both upper and lower
thresholds are meant to be disabled, we disable the channel fully
in HW to save some power and it can be enabled later if this API
is called for it with valid thresholds.
Is it considered invalid in the thermal framework to try to disable
both thresholds? Should I both disable the channel and return some
error from here?
>> + return adc_tm5_gen3_configure(prop, low_temp, high_temp);
>> +}
>> +
>> +static const struct thermal_zone_device_ops adc_tm_ops = {
>> + .get_temp = adc_tm5_gen3_get_temp,
>> + .set_trips = adc_tm5_gen3_set_trip_temp,
>> +};
>> +
>> +static int adc_tm5_register_tzd(struct adc_tm5_gen3_chip *adc_tm5)
>> +{
>> + unsigned int i, channel;
>> + struct thermal_zone_device *tzd;
>> + int ret;
>> +
>> + for (i = 0; i < adc_tm5->nchannels; i++) {
>> + channel = ADC5_GEN3_V_CHAN(adc_tm5->chan_props[i].common_props);
>> + tzd = devm_thermal_of_zone_register(adc_tm5->dev, channel,
>> + &adc_tm5->chan_props[i],
>> + &adc_tm_ops);
>> +
>> + if (IS_ERR(tzd)) {
>> + if (PTR_ERR(tzd) == -ENODEV) {
>> + dev_warn(adc_tm5->dev,
>> + "thermal sensor on channel %d is not used\n",
>> + channel);
>> + continue;
>> + }
>> + return dev_err_probe(adc_tm5->dev, PTR_ERR(tzd),
>> + "Error registering TZ zone:%ld for channel:%d\n",
>> + PTR_ERR(tzd), channel);
>> + }
>> + adc_tm5->chan_props[i].tzd = tzd;
>> + ret = devm_thermal_add_hwmon_sysfs(adc_tm5->dev, tzd);
>> + if (ret)
>> + return ret;
>> + }
>> + return 0;
>> +}
>> +
>> +static void adc5_gen3_clear_work(void *data)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = data;
>> +
>> + cancel_work_sync(&adc_tm5->tm_handler_work);
>> +}
>> +
>> +static void adc5_gen3_disable(void *data)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = data;
>> + int i;
>> +
>> + guard(adc5_gen3)(adc_tm5);
>> + /* Disable all available TM channels */
>> + for (i = 0; i < adc_tm5->nchannels; i++)
>> + adc_tm5_gen3_disable_channel(&adc_tm5->chan_props[i]);
>> +}
>> +
>> +static void adctm_event_handler(struct auxiliary_device *adev)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5 = auxiliary_get_drvdata(adev);
>> +
>> + schedule_work(&adc_tm5->tm_handler_work);
>> +}
>> +
>> +static int adc_tm5_probe(struct auxiliary_device *aux_dev,
>> + const struct auxiliary_device_id *id)
>> +{
>> + struct adc_tm5_gen3_chip *adc_tm5;
>> + struct tm5_aux_dev_wrapper *aux_dev_wrapper;
>> + struct device *dev = &aux_dev->dev;
>> + int i, ret;
>> +
>> + adc_tm5 = devm_kzalloc(dev, sizeof(*adc_tm5), GFP_KERNEL);
>> + if (!adc_tm5)
>> + return -ENOMEM;
>> +
>> + aux_dev_wrapper = container_of(aux_dev, struct tm5_aux_dev_wrapper,
>> + aux_dev);
>> +
>> + adc_tm5->dev = dev;
>> + adc_tm5->dev_data = aux_dev_wrapper->dev_data;
>> + adc_tm5->nchannels = aux_dev_wrapper->n_tm_channels;
>> + adc_tm5->chan_props = devm_kcalloc(dev, aux_dev_wrapper->n_tm_channels,
>> + sizeof(*adc_tm5->chan_props), GFP_KERNEL);
>> + if (!adc_tm5->chan_props)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < adc_tm5->nchannels; i++) {
>> + adc_tm5->chan_props[i].common_props = aux_dev_wrapper->tm_props[i];
>> + adc_tm5->chan_props[i].timer = MEAS_INT_1S;
>> + adc_tm5->chan_props[i].sdam_index = (i + 1) / 8;
>> + adc_tm5->chan_props[i].tm_chan_index = (i + 1) % 8;
>> + adc_tm5->chan_props[i].chip = adc_tm5;
>> + }
>> +
>> + INIT_WORK(&adc_tm5->tm_handler_work, tm_handler_work);
>
> Why is it needed
I'll drop it as you suggested.
>
>> + /*
>> + * Skipping first SDAM IRQ as it is requested in parent driver.
>> + * If there is a TM violation on that IRQ, the parent driver calls
>> + * the notifier (adctm_event_handler) exposed from this driver to handle it.
>> + */
>> + for (i = 1; i < adc_tm5->dev_data->num_sdams; i++) {
>> + ret = devm_request_threaded_irq(dev,
>> + adc_tm5->dev_data->base[i].irq,
>> + NULL, adctm5_gen3_isr, IRQF_ONESHOT,
>> + adc_tm5->dev_data->base[i].irq_name,
>> + adc_tm5);
>
> The threaded interrupts set the isr in a thread and from the thread
> handling the event, there is a work queue scheduled. Why not use the
> top and bottom halves of the threaded interrupt ? Hopefully you should
> be able to remove the lock.
Yes, I can use the top and bottom halves of the threaded interrupt as you
suggested. But what exactly do you mean by removing the lock?
If you meant the mutex lock used in this driver, we cannot remove that.
This is because the ADC_TM driver needs to write into several registers
shared with the main ADC driver for setting new thresholds, so we
have to share a mutex between the drivers to prevent concurrency issues.
I'll address all your other comments too in the next version of this patch.
Thanks,
Jishnu
>
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + /*
>> + * This drvdata is only used in the function (adctm_event_handler)
>> + * called by parent ADC driver in case of TM violation on the first SDAM.
>> + */
>> + auxiliary_set_drvdata(aux_dev, adc_tm5);
>> +
>> + adc5_gen3_register_tm_event_notifier(dev, adctm_event_handler);
>> +
>> + /*
>> + * This is to cancel any instances of tm_handler_work scheduled by
>> + * TM interrupt, at the time of module removal.
>> + */
>> +
>
> Remove the extra line
>
>> + ret = devm_add_action(dev, adc5_gen3_clear_work, adc_tm5);
>> + if (ret)
>> + return ret;
>> +
>> + ret = adc_tm5_register_tzd(adc_tm5);
>> + if (ret)
>> + return ret;
>> +
>> + /* This is to disable all ADC_TM channels in case of probe failure. */
>> +
>
> Remove the extra line
>
>> + return devm_add_action(dev, adc5_gen3_disable, adc_tm5);
>> +}
>> +
>> +static const struct auxiliary_device_id adctm5_auxiliary_id_table[] = {
>> + { .name = "qcom_spmi_adc5_gen3.adc5_tm_gen3", },
>> + { }
>> +};
>> +
>> +MODULE_DEVICE_TABLE(auxiliary, adctm5_auxiliary_id_table);
>> +
>> +static struct auxiliary_driver adctm5gen3_auxiliary_driver = {
>> + .id_table = adctm5_auxiliary_id_table,
>> + .probe = adc_tm5_probe,
>> +};
>> +
>> +module_auxiliary_driver(adctm5gen3_auxiliary_driver);
>> +
>> +MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3");
>> --
>> 2.25.1
>>
>
^ permalink raw reply
* [PATCH v2 2/2] hwmon: (pmbus/max20830) add driver for max20830
From: Alexis Czezar Torreno @ 2026-04-16 7:59 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno
In-Reply-To: <20260416-dev_max20830-v2-0-2c7d676dc0bd@analog.com>
Add support for MAX20830 step-down DC-DC switching regulator with
PMBus interface. It allows monitoring of input/output voltage,
output current and temperature through the PMBus serial interface.
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/max20830.rst | 49 +++++++++++++++++++++++
MAINTAINERS | 2 +
drivers/hwmon/pmbus/Kconfig | 9 +++++
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/max20830.c | 86 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 148 insertions(+)
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 8b655e5d6b68b90c697a52c7bf526e81d370caf7..56f7eb761be76dd627a2f34135abad05203b0582 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -158,6 +158,7 @@ Hardware Monitoring Kernel Drivers
max197
max20730
max20751
+ max20830
max31722
max31730
max31760
diff --git a/Documentation/hwmon/max20830.rst b/Documentation/hwmon/max20830.rst
new file mode 100644
index 0000000000000000000000000000000000000000..936e409dcc5c0898dde27d782308d4a7e1357e73
--- /dev/null
+++ b/Documentation/hwmon/max20830.rst
@@ -0,0 +1,49 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver max20830
+======================
+
+Supported chips:
+
+ * Analog Devices MAX20830
+
+ Prefix: 'max20830'
+
+ Addresses scanned: -
+
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20830.pdf
+
+Author:
+
+ - Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
+
+
+Description
+-----------
+
+This driver supports hardware monitoring for Analog Devices MAX20830
+Step-Down Switching Regulator with PMBus Interface.
+
+The MAX20830 is a 2.7V to 16V, 30A fully integrated step-down DC-DC switching
+regulator. Through the PMBus interface, the device can monitor input/output
+voltages, output current and temperature.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
+
+Sysfs entries
+-------------
+
+================= ========================================
+in1_label "vin"
+in1_input Measured input voltage
+in1_alarm Input voltage alarm
+in2_label "vout1"
+in2_input Measured output voltage
+in2_alarm Output voltage alarm
+curr1_label "iout1"
+curr1_input Measured output current
+curr1_alarm Output current alarm
+temp1_input Measured temperature
+temp1_alarm Chip temperature alarm
+================= ========================================
diff --git a/MAINTAINERS b/MAINTAINERS
index 031c743e979521a92ed9ac67915c178ce31727bd..d6a6745e2dae29c3b8f80bbe61c54a2f5ecd9f47 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15585,6 +15585,8 @@ L: linux-hwmon@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
+F: Documentation/hwmon/max20830.rst
+F: drivers/hwmon/pmbus/max20830.c
MAX2175 SDR TUNER DRIVER
M: Ramesh Shanmugasundaram <rashanmu@gmail.com>
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 8f4bff375ecbc355f5ed3400855c2852ec2aa5ef..987705bf45b75b7b91ccc469247909f3c3f53d77 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -365,6 +365,15 @@ config SENSORS_MAX20751
This driver can also be built as a module. If so, the module will
be called max20751.
+config SENSORS_MAX20830
+ tristate "Analog Devices MAX20830"
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices MAX20830.
+
+ This driver can also be built as a module. If so, the module will
+ be called max20830.
+
config SENSORS_MAX31785
tristate "Maxim MAX31785 and compatibles"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 7129b62bc00f8a2e98de14004997752a856dfda2..bc52f930e0825a902a0dd1c9e2b44f2e8d577c35 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
obj-$(CONFIG_SENSORS_MAX17616) += max17616.o
obj-$(CONFIG_SENSORS_MAX20730) += max20730.o
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
+obj-$(CONFIG_SENSORS_MAX20830) += max20830.o
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c
new file mode 100644
index 0000000000000000000000000000000000000000..21ea8b59150cb0564f1776ee08131bad7fdef003
--- /dev/null
+++ b/drivers/hwmon/pmbus/max20830.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hardware monitoring driver for Analog Devices MAX20830
+ *
+ * Copyright (C) 2026 Analog Devices, Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+static struct pmbus_driver_info max20830_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int max20830_probe(struct i2c_client *client)
+{
+ u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+ u8 len;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return -ENODEV;
+
+ /*
+ * Use i2c_smbus_read_i2c_block_data() instead of
+ * i2c_smbus_read_block_data() to support I2C controllers
+ * which do not support SMBus block reads.
+ */
+ ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID,
+ I2C_SMBUS_BLOCK_MAX, buf);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to read IC_DEVICE_ID\n");
+
+ /* First byte is the block length. */
+ len = buf[0];
+ if (len != 9)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Unexpected IC_DEVICE_ID response\n");
+
+ buf[len] = '\0';
+ if (strncmp(buf + 1, "MAX20830", 8))
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Unsupported device: '%s'\n", buf + 1);
+
+ return pmbus_do_probe(client, &max20830_info);
+}
+
+static const struct i2c_device_id max20830_id[] = {
+ {"max20830"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max20830_id);
+
+static const struct of_device_id max20830_of_match[] = {
+ { .compatible = "adi,max20830" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max20830_of_match);
+
+static struct i2c_driver max20830_driver = {
+ .driver = {
+ .name = "max20830",
+ .of_match_table = max20830_of_match,
+ },
+ .probe = max20830_probe,
+ .id_table = max20830_id,
+};
+
+module_i2c_driver(max20830_driver);
+
+MODULE_AUTHOR("Alexis Czezar Torreno <alexisczezar.torreno@analog.com>");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20830");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
--
2.34.1
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: hwmon: pmbus: add max20830
From: Alexis Czezar Torreno @ 2026-04-16 7:59 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno
In-Reply-To: <20260416-dev_max20830-v2-0-2c7d676dc0bd@analog.com>
Add device tree documentation for MAX20830 step-down DC-DC switching
regulator with PMBus interface.
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
.../bindings/hwmon/pmbus/adi,max20830.yaml | 61 ++++++++++++++++++++++
MAINTAINERS | 7 +++
2 files changed, 68 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8b3ec1ffa0c9460de2122f6606ce3dcbcdfbbcc7
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20830.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX20830 Step-Down Switching Regulator with PMBus
+
+maintainers:
+ - Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
+
+description: |
+ The MAX20830 is a fully integrated step-down DC-DC switching regulator with
+ PMBus interface. It provides 2.7V to 16V input, 0.4V to 5.8V adjustable
+ output, and up to 30A output current. It allows monitoring of input/output
+ voltage, output current and temperature through the PMBus serial interface.
+ Datasheet:
+ https://www.analog.com/en/products/max20830.html
+
+allOf:
+ - $ref: /schemas/regulator/regulator.yaml#
+
+properties:
+ compatible:
+ const: adi,max20830
+
+ reg:
+ maxItems: 1
+
+ vddh-supply:
+ description:
+ Phandle to the regulator that provides the VDDH power supply.
+
+ avdd-supply:
+ description:
+ Phandle to the regulator that provides the AVDD power supply.
+
+ ldoin-supply:
+ description:
+ Optional 2.5V to 5.5V LDO input supply.
+
+required:
+ - compatible
+ - reg
+ - vddh-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ regulator@30 {
+ compatible = "adi,max20830";
+ reg = <0x30>;
+ vddh-supply = <&vddh>;
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 0a3991c10ade20dd79cc7d1bf2a1d307ba6bd19d..031c743e979521a92ed9ac67915c178ce31727bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15579,6 +15579,13 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml
F: Documentation/hwmon/max17616.rst
F: drivers/hwmon/pmbus/max17616.c
+MAX20830 HARDWARE MONITOR DRIVER
+M: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
+
MAX2175 SDR TUNER DRIVER
M: Ramesh Shanmugasundaram <rashanmu@gmail.com>
L: linux-media@vger.kernel.org
--
2.34.1
^ permalink raw reply related
* [PATCH v2 0/2] Add support for MAX20830 PMBUS
From: Alexis Czezar Torreno @ 2026-04-16 7:59 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno
This series adds support for the Analog Devices MAX20830 step-down
switching regulator with PMBus interface.
The MAX20830 provides 2.7V to 16V input, 0.4V to 5.8V output, and up
to 30A output current. It supports monitoring of input/output voltage,
output current, and temperature via PMBus.
Datasheet: https://www.analog.com/en/products/max20830.html
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
Changes in v2:
- bindings:
- did not add interrupt, smbalert pin does not exist in device.
- added allof with ref to regulator.yaml
- changed additionalprop to unevaluatedprop
- device node name in example changed to regulator
- driver:
- max20830.rst: Added missing in2_alarm
- max20830.c:
- added missing quotes in MODULE_IMPORT_NS
- added comment on why i2c_smbus_read_i2c_block_data is used
- first byte of buffer used as length instead of the return value
- "unsupported device" log now does not print first byte of buffer
- Link to v1: https://lore.kernel.org/r/20260414-dev_max20830-v1-0-210d3f82c571@analog.com
---
Alexis Czezar Torreno (2):
dt-bindings: hwmon: pmbus: add max20830
hwmon: (pmbus/max20830) add driver for max20830
.../bindings/hwmon/pmbus/adi,max20830.yaml | 61 +++++++++++++++
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/max20830.rst | 49 ++++++++++++
MAINTAINERS | 9 +++
drivers/hwmon/pmbus/Kconfig | 9 +++
drivers/hwmon/pmbus/Makefile | 1 +
drivers/hwmon/pmbus/max20830.c | 86 ++++++++++++++++++++++
7 files changed, 216 insertions(+)
---
base-commit: fb447217c59a13b2fff22d94de2498c185cd9032
change-id: 20260414-dev_max20830-9460b92cf6aa
Best regards,
--
Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
^ permalink raw reply
* Re: [PATCH v4 2/4] pwm: sun50i: Add H616 PWM support
From: Richard GENOUD @ 2026-04-16 7:57 UTC (permalink / raw)
To: bigunclemax
Cc: conor+dt, devicetree, jernej.skrabec, joao, jstultz, krzk+dt,
linux-arm-kernel, linux-kernel, linux-pwm, linux-sunxi, p.zabel,
paulk, robh, samuel, thomas.petazzoni, u.kleine-koenig, wens
In-Reply-To: <20260413123920.2459916-1-bigunclemax@gmail.com>
Le 13/04/2026 à 14:39, bigunclemax@gmail.com a écrit :
> Hi Richard,
>
>> +
>> +/* PWM Capture Fall Lock Register */
>> +#define H616_PWM_CFLR(x) (0x74 + (x) * 0x20)
>> +
>> +#define H616_PWM_PAIR_IDX(chan) ((chan) >> 2)
>> +
>
> It looks like there's a typo or a mistake in the PAIR_IDX calculation.
> It should be like ((chan) >> 1).
> For example, for the 5th channel the result will be 1, but it should be 2.
Good catch!
I mainly tested with PWM1 as it's the only one easily accessible with a
scope.
I'll change that in the next iteration.
Thanks!
>
> Best regards
> Maksim
>
^ permalink raw reply
* Re: [PATCH v4 2/4] pwm: sun50i: Add H616 PWM support
From: Richard GENOUD @ 2026-04-16 7:53 UTC (permalink / raw)
To: Paul Kocialkowski
Cc: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Philipp Zabel, Thomas Petazzoni, John Stultz, Joao Schim,
linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel
In-Reply-To: <adfiPo4Jq1IRMM0h@shepard>
Hi Paul,
Le 09/04/2026 à 19:30, Paul Kocialkowski a écrit :
> Hi Richard,
>
> On Thu 05 Mar 26, 10:19, Richard Genoud wrote:
>> +/* PWM IRQ Enable Register */
>> +#define H616_PWM_IER 0x0
>
> I think it would make more sense to keep the full register names from the
> manual after the suffix and stick to them. It makes things easier when
> comparing the code with documentation or the reference implementation.
>
> So something like SUN8I_PWM_PIER here.
Ok, that make sense.
>
>> +
>> +/* PWM IRQ Status Register */
>> +#define H616_PWM_ISR 0x4
>> +
>> +/* PWM Capture IRQ Enable Register */
>> +#define H616_PWM_CIER 0x10
>> +
>> +/* PWM Capture IRQ Status Register */
>> +#define H616_PWM_CISR 0x14
>> +
>> +/* PWMCC Pairs Clock Configuration Registers */
>> +#define H616_PWM_XY_CLK_CR(pair) (0x20 + ((pair) * 0x4))
>> +#define H616_PWM_XY_CLK_CR_SRC_SHIFT 7
>> +#define H616_PWM_XY_CLK_CR_SRC_MASK 1
>> +#define H616_PWM_XY_CLK_CR_GATE_BIT 4
>> +#define H616_PWM_XY_CLK_CR_BYPASS_BIT(chan) ((chan) % 2 + 5)
>> +#define H616_PWM_XY_CLK_CR_DIV_M_SHIFT 0
>> +
>> +/* PWMCC Pairs Dead Zone Control Registers */
>> +#define H616_PWM_XY_DZ(pair) (0x30 + ((pair) * 0x4))
>> +
>> +/* PWM Enable Register */
>> +#define H616_PWM_ENR 0x40
>> +#define H616_PWM_ENABLE(x) BIT(x)
>> +
>> +/* PWM Capture Enable Register */
>> +#define H616_PWM_CER 0x44
>> +
>> +/* PWM Control Register */
>> +#define H616_PWM_CTRL_REG(chan) (0x60 + (chan) * 0x20)
>
> You're sometimes calling the register offset _REG and sometimes not.
> Both options are fine but you need to keep it consistent across the whole
> definitions. I would be enclined to not use it after using the register names
> coming from the manual as suggested above.
I see what you mean, so H616_PWM_CTRL_REG() would become SUN8I_PWM_PCR()
>
> Also you're sometimes using "chan", sometimes "ch" for the argument to the
> register macros. This is inconsistent and you might as well just use "c"
> everywhere so it doesn't take too much space.
Indeed.
>
>> +#define H616_PWM_CTRL_PRESCAL_K_SHIFT 0
>> +#define H616_PWM_CTRL_PRESCAL_K_WIDTH 8
>> +#define H616_PWM_CTRL_ACTIVE_STATE BIT(8)
>> +
>> +/* PWM Period Register */
>> +#define H616_PWM_PERIOD_REG(ch) (0x64 + (ch) * 0x20)
>> +#define H616_PWM_PERIOD_MASK GENMASK(31, 16)
>> +#define H616_PWM_DUTY_MASK GENMASK(15, 0)
>> +#define H616_PWM_REG_PERIOD(reg) (FIELD_GET(H616_PWM_PERIOD_MASK, reg) + 1)
>> +#define H616_PWM_REG_DUTY(reg) FIELD_GET(H616_PWM_DUTY_MASK, reg)
>> +#define H616_PWM_PERIOD(prd) FIELD_PREP(H616_PWM_PERIOD_MASK, (prd) - 1)
>> +#define H616_PWM_DUTY(dty) FIELD_PREP(H616_PWM_DUTY_MASK, dty)
>> +#define H616_PWM_PERIOD_MAX (FIELD_MAX(H616_PWM_PERIOD_MASK) + 1)
>
> Using REG as a prefix feels a bit confusing here. I would rather see:
> #define SUN8I_PWM_PPR(c) (0x64 + (c) * 0x20)
> #define SUN8I_PWM_PPR_PERIOD(p) FIELD_PREP(...)
> #define SUN8I_PWM_PPR_PERIOD_VALUE(r) FIELD_GET(...)
> #define SUN8I_PWN_PPR_PERIOD_MAX FIELD_MAX(...)
> #define SUN8I_PWM_PPR_DUTY(d) FIELD_PREP(...)
> #define SUN8I_PWM_PPR_DUTY_VALUE(r) FIELD_GET(...)
That's right, that would be less confusing.
>
>> +
>> +/* PWM Count Register */
>> +#define H616_PWM_CNT_REG(x) (0x68 + (x) * 0x20)
>> +
>> +/* PWM Capture Control Register */
>> +#define H616_PWM_CCR(x) (0x6c + (x) * 0x20)
>> +
>> +/* PWM Capture Rise Lock Register */
>> +#define H616_PWM_CRLR(x) (0x70 + (x) * 0x20)
>> +
>> +/* PWM Capture Fall Lock Register */
>> +#define H616_PWM_CFLR(x) (0x74 + (x) * 0x20)
>> +
>> +#define H616_PWM_PAIR_IDX(chan) ((chan) >> 2)
>> +
>> +/*
>> + * Block diagram of the PWM clock controller:
>> + *
>> + * _____ ______ ________
>> + * OSC24M --->| | | | | |
>> + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> H616_PWM_clock_src_xy
>> + * |_____| |______| |________|
>> + * ________
>> + * | |
>> + * +->| /div_k |---> H616_PWM_clock_x
>> + * | |________|
>> + * | ______
>> + * | | |
>> + * +-->| Gate |----> H616_PWM_bypass_clock_x
>> + * | |______|
>> + * H616_PWM_clock_src_xy ----+ ________
>> + * | | |
>> + * +->| /div_k |---> H616_PWM_clock_y
>> + * | |________|
>> + * | ______
>> + * | | |
>> + * +-->| Gate |----> H616_PWM_bypass_clock_y
>> + * |______|
>> + *
>> + * NB: when the bypass is set, all the PWM logic is bypassed.
>> + * So, the duty cycle and polarity can't be modified (we just have a clock).
>> + * The bypass in PWM mode is used to achieve a 1/2 relative duty cycle with the
>> + * fastest clock.
>> + *
>> + * H616_PWM_clock_x/y serve for the PWM purpose.
>> + * H616_PWM_bypass_clock_x/y serve for the clock-provider purpose.
>> + *
>> + */
>> +
>> +/*
>> + * Table used for /div_m (diviser before obtaining H616_PWM_clock_src_xy)
>> + * It's actually CLK_DIVIDER_POWER_OF_TWO, but limited to /256
>> + */
>> +#define CLK_TABLE_DIV_M_ENTRY(i) { \
>> + .val = (i), .div = 1 << (i) \
>> +}
>> +
>> +static const struct clk_div_table clk_table_div_m[] = {
>> + CLK_TABLE_DIV_M_ENTRY(0),
>> + CLK_TABLE_DIV_M_ENTRY(1),
>> + CLK_TABLE_DIV_M_ENTRY(2),
>> + CLK_TABLE_DIV_M_ENTRY(3),
>> + CLK_TABLE_DIV_M_ENTRY(4),
>> + CLK_TABLE_DIV_M_ENTRY(5),
>> + CLK_TABLE_DIV_M_ENTRY(6),
>> + CLK_TABLE_DIV_M_ENTRY(7),
>> + CLK_TABLE_DIV_M_ENTRY(8),
>> + { /* sentinel */ }
>> +};
>> +
>> +#define H616_PWM_XY_SRC_GATE(_pair, _reg) \
>> +struct clk_gate gate_xy_##_pair = { \
>> + .reg = (void *)(_reg), \
>> + .bit_idx = H616_PWM_XY_CLK_CR_GATE_BIT, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_gate_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_XY_SRC_MUX(_pair, _reg) \
>> +struct clk_mux mux_xy_##_pair = { \
>> + .reg = (void *)(_reg), \
>> + .shift = H616_PWM_XY_CLK_CR_SRC_SHIFT, \
>> + .mask = H616_PWM_XY_CLK_CR_SRC_MASK, \
>> + .flags = CLK_MUX_ROUND_CLOSEST, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_mux_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_XY_SRC_DIV(_pair, _reg) \
>> +struct clk_divider rate_xy_##_pair = { \
>> + .reg = (void *)(_reg), \
>> + .shift = H616_PWM_XY_CLK_CR_DIV_M_SHIFT, \
>> + .table = clk_table_div_m, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_divider_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_X_DIV(_idx, _reg) \
>> +struct clk_divider rate_x_##_idx = { \
>> + .reg = (void *)(_reg), \
>> + .shift = H616_PWM_CTRL_PRESCAL_K_SHIFT, \
>> + .width = H616_PWM_CTRL_PRESCAL_K_WIDTH, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_divider_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_X_BYPASS_GATE(_idx) \
>> +struct clk_gate gate_x_bypass_##_idx = { \
>> + .reg = (void *)H616_PWM_ENR, \
>> + .bit_idx = _idx, \
>> + .hw.init = &(struct clk_init_data){ \
>> + .ops = &clk_gate_ops, \
>> + } \
>> +}
>> +
>> +#define H616_PWM_XY_CLK_SRC(_pair, _reg) \
>> + static H616_PWM_XY_SRC_MUX(_pair, _reg); \
>> + static H616_PWM_XY_SRC_GATE(_pair, _reg); \
>> + static H616_PWM_XY_SRC_DIV(_pair, _reg)
>> +
>> +#define H616_PWM_X_CLK(_idx) \
>> + static H616_PWM_X_DIV(_idx, H616_PWM_CTRL_REG(_idx))
>> +
>> +#define H616_PWM_X_BYPASS_CLK(_idx) \
>> + H616_PWM_X_BYPASS_GATE(_idx)
>> +
>> +#define REF_CLK_XY_SRC(_pair) \
>> + { \
>> + .name = "pwm-clk-src" #_pair, \
>> + .mux_hw = &mux_xy_##_pair.hw, \
>> + .gate_hw = &gate_xy_##_pair.hw, \
>> + .rate_hw = &rate_xy_##_pair.hw, \
>> + }
>> +
>> +#define REF_CLK_X(_idx, _pair) \
>> + { \
>> + .name = "pwm-clk" #_idx, \
>> + .parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
>> + .num_parents = 1, \
>> + .rate_hw = &rate_x_##_idx.hw, \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }
>> +
>> +#define REF_CLK_BYPASS(_idx, _pair) \
>> + { \
>> + .name = "pwm-clk-bypass" #_idx, \
>> + .parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
>> + .num_parents = 1, \
>> + .gate_hw = &gate_x_bypass_##_idx.hw, \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }
>> +
>> +/*
>> + * H616_PWM_clock_src_xy generation:
>> + * _____ ______ ________
>> + * OSC24M --->| | | | | |
>> + * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> H616_PWM_clock_src_xy
>> + * |_____| |______| |________|
>> + */
>> +H616_PWM_XY_CLK_SRC(01, H616_PWM_XY_CLK_CR(0));
>> +H616_PWM_XY_CLK_SRC(23, H616_PWM_XY_CLK_CR(1));
>> +H616_PWM_XY_CLK_SRC(45, H616_PWM_XY_CLK_CR(2));
>> +
>> +/*
>> + * H616_PWM_clock_x_div generation:
>> + * ________
>> + * | | H616_PWM_clock_x/y
>> + * H616_PWM_clock_src_xy --->| /div_k |--------------->
>> + * |________|
>> + */
>> +H616_PWM_X_CLK(0);
>> +H616_PWM_X_CLK(1);
>> +H616_PWM_X_CLK(2);
>> +H616_PWM_X_CLK(3);
>> +H616_PWM_X_CLK(4);
>> +H616_PWM_X_CLK(5);
>> +
>> +/*
>> + * H616_PWM_bypass_clock_xy generation:
>> + * ______
>> + * | |
>> + * H616_PWM_clock_src_xy ---->| Gate |-------> H616_PWM_bypass_clock_x
>> + * |______|
>> + *
>> + * The gate is actually H616_PWM_ENR register.
>> + */
>> +H616_PWM_X_BYPASS_CLK(0);
>> +H616_PWM_X_BYPASS_CLK(1);
>> +H616_PWM_X_BYPASS_CLK(2);
>> +H616_PWM_X_BYPASS_CLK(3);
>> +H616_PWM_X_BYPASS_CLK(4);
>> +H616_PWM_X_BYPASS_CLK(5);
>> +
>> +struct clk_pwm_data {
>> + const char *name;
>> + const char **parent_names;
>> + unsigned int num_parents;
>> + struct clk_hw *mux_hw;
>> + struct clk_hw *rate_hw;
>> + struct clk_hw *gate_hw;
>> + unsigned long flags;
>> +};
>> +
>> +#define CLK_BYPASS(h616chip, ch) ((h616chip)->data->npwm + (ch))
>> +#define CLK_XY_SRC_IDX(h616chip, ch) ((h616chip)->data->npwm * 2 + ((ch) >> 1))
>> +static struct clk_pwm_data pwmcc_data[] = {
>> + REF_CLK_X(0, 01),
>> + REF_CLK_X(1, 01),
>> + REF_CLK_X(2, 23),
>> + REF_CLK_X(3, 23),
>> + REF_CLK_X(4, 45),
>> + REF_CLK_X(5, 45),
>> + REF_CLK_BYPASS(0, 01),
>> + REF_CLK_BYPASS(1, 01),
>> + REF_CLK_BYPASS(2, 23),
>> + REF_CLK_BYPASS(3, 23),
>> + REF_CLK_BYPASS(4, 45),
>> + REF_CLK_BYPASS(5, 45),
>> + REF_CLK_XY_SRC(01),
>> + REF_CLK_XY_SRC(23),
>> + REF_CLK_XY_SRC(45),
>> + { /* sentinel */ }
>> +};
>
> We'll probably need a way to tie these static definitions to a particular
> instance of the unit for a given chip. But I guess that can be done later
> when adding more chips to the driver.
>
> I'm not too versed in the clk and pwm APIs but the rest generally looks good
> to me.
Thanks!
>
> All the best,
>
> Paul
>
^ permalink raw reply
* [PATCH v7 3/3] pinctrl: aspeed: Add AST2700 SoC0 support
From: Billy Tsai @ 2026-04-16 7:29 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
Bartosz Golaszewski, Ryan Chen
Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, linux-gpio, linux-clk
In-Reply-To: <20260416-upstream_pinctrl-v7-0-d72762253163@aspeedtech.com>
Add pinctrl support for the SoC0 instance of the ASPEED AST2700.
AST2700 consists of two interconnected SoC instances, each with its own
pinctrl register block.
The SoC0 pinctrl hardware closely follows the design found in previous
ASPEED BMC generations, allowing the driver to build upon the common
ASPEED pinctrl infrastructure.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
drivers/pinctrl/aspeed/Kconfig | 9 +
drivers/pinctrl/aspeed/Makefile | 1 +
drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c | 749 ++++++++++++++++++++++++
3 files changed, 759 insertions(+)
diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig
index 1a4e5b9ed471..f9672cca891e 100644
--- a/drivers/pinctrl/aspeed/Kconfig
+++ b/drivers/pinctrl/aspeed/Kconfig
@@ -31,3 +31,12 @@ config PINCTRL_ASPEED_G6
help
Say Y here to enable pin controller support for Aspeed's 6th
generation SoCs. GPIO is provided by a separate GPIO driver.
+
+config PINCTRL_ASPEED_G7_SOC0
+ bool "Aspeed G7 SoC pin control"
+ depends on (ARCH_ASPEED || COMPILE_TEST) && OF
+ select PINCTRL_ASPEED
+ help
+ Say Y here to enable pin controller support for the SoC0 instance
+ of Aspeed's 7th generation SoCs. GPIO is provided by a separate
+ GPIO driver.
diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile
index db2a7600ae2b..0de524ca2c72 100644
--- a/drivers/pinctrl/aspeed/Makefile
+++ b/drivers/pinctrl/aspeed/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_PINCTRL_ASPEED) += pinctrl-aspeed.o pinmux-aspeed.o
obj-$(CONFIG_PINCTRL_ASPEED_G4) += pinctrl-aspeed-g4.o
obj-$(CONFIG_PINCTRL_ASPEED_G5) += pinctrl-aspeed-g5.o
obj-$(CONFIG_PINCTRL_ASPEED_G6) += pinctrl-aspeed-g6.o
+obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC0) += pinctrl-aspeed-g7-soc0.o
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c
new file mode 100644
index 000000000000..35a28b677318
--- /dev/null
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c
@@ -0,0 +1,749 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "pinctrl-aspeed.h"
+#include "pinmux-aspeed.h"
+#include "../pinctrl-utils.h"
+
+#define SCU200 0x200 /* System Reset Control #1 */
+
+#define SCU010 0x010 /* Hardware Strap Register */
+#define SCU400 0x400 /* Multi-function Pin Control #1 */
+#define SCU404 0x404 /* Multi-function Pin Control #2 */
+#define SCU408 0x408 /* Multi-function Pin Control #3 */
+#define SCU40C 0x40C /* Multi-function Pin Control #3 */
+#define SCU410 0x410 /* USB Multi-function Control Register */
+#define SCU414 0x414 /* VGA Function Control Register */
+
+#define SCU480 0x480 /* GPIO18A0 IO Control Register */
+#define SCU484 0x484 /* GPIO18A1 IO Control Register */
+#define SCU488 0x488 /* GPIO18A2 IO Control Register */
+#define SCU48C 0x48c /* GPIO18A3 IO Control Register */
+#define SCU490 0x490 /* GPIO18A4 IO Control Register */
+#define SCU494 0x494 /* GPIO18A5 IO Control Register */
+#define SCU498 0x498 /* GPIO18A6 IO Control Register */
+#define SCU49C 0x49c /* GPIO18A7 IO Control Register */
+#define SCU4A0 0x4A0 /* GPIO18B0 IO Control Register */
+#define SCU4A4 0x4A4 /* GPIO18B1 IO Control Register */
+#define SCU4A8 0x4A8 /* GPIO18B2 IO Control Register */
+#define SCU4AC 0x4AC /* GPIO18B3 IO Control Register */
+
+enum {
+ AC14,
+ AE15,
+ AD14,
+ AE14,
+ AF14,
+ AB13,
+ AB14,
+ AF15,
+ AF13,
+ AC13,
+ AD13,
+ AE13,
+ JTAG_PORT,
+ PCIERC0_PERST,
+ PCIERC1_PERST,
+ PORTA_MODE,
+ PORTA_U2,
+ PORTB_MODE,
+ PORTB_U2,
+ PORTA_U2_PHY,
+ PORTB_U2_PHY,
+ PORTA_U3,
+ PORTB_U3,
+ PORTA_U3_PHY,
+ PORTB_U3_PHY,
+};
+
+SIG_EXPR_LIST_DECL_SEMG(AC14, EMMCCLK, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 0));
+SIG_EXPR_LIST_DECL_SESG(AC14, VB1CS, VB1, SIG_DESC_SET(SCU404, 0));
+PIN_DECL_2(AC14, GPIO18A0, EMMCCLK, VB1CS);
+
+SIG_EXPR_LIST_DECL_SEMG(AE15, EMMCCMD, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 1));
+SIG_EXPR_LIST_DECL_SESG(AE15, VB1CK, VB1, SIG_DESC_SET(SCU404, 1));
+PIN_DECL_2(AE15, GPIO18A1, EMMCCMD, VB1CK);
+
+SIG_EXPR_LIST_DECL_SEMG(AD14, EMMCDAT0, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 2));
+SIG_EXPR_LIST_DECL_SESG(AD14, VB1MOSI, VB1, SIG_DESC_SET(SCU404, 2));
+PIN_DECL_2(AD14, GPIO18A2, EMMCDAT0, VB1MOSI);
+
+SIG_EXPR_LIST_DECL_SEMG(AE14, EMMCDAT1, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 3));
+SIG_EXPR_LIST_DECL_SESG(AE14, VB1MISO, VB1, SIG_DESC_SET(SCU404, 3));
+PIN_DECL_2(AE14, GPIO18A3, EMMCDAT1, VB1MISO);
+
+SIG_EXPR_LIST_DECL_SEMG(AF14, EMMCDAT2, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 4));
+PIN_DECL_1(AF14, GPIO18A4, EMMCDAT2);
+
+SIG_EXPR_LIST_DECL_SEMG(AB13, EMMCDAT3, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 5));
+PIN_DECL_1(AB13, GPIO18A5, EMMCDAT3);
+
+SIG_EXPR_LIST_DECL_SEMG(AB14, EMMCCDN, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 6));
+SIG_EXPR_LIST_DECL_SESG(AB14, VB0CS, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_2(AB14, GPIO18A6, EMMCCDN, VB0CS);
+
+SIG_EXPR_LIST_DECL_SEMG(AF15, EMMCWPN, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 7));
+SIG_EXPR_LIST_DECL_SESG(AF15, VB0CK, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_2(AF15, GPIO18A7, EMMCWPN, VB0CK);
+
+SIG_EXPR_LIST_DECL_SESG(AF13, TSPRSTN, TSPRSTN, SIG_DESC_SET(SCU010, 9));
+SIG_EXPR_LIST_DECL_SEMG(AF13, EMMCDAT4, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 8));
+SIG_EXPR_LIST_DECL_SESG(AF13, VB0MOSI, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_3(AF13, GPIO18B0, TSPRSTN, EMMCDAT4, VB0MOSI);
+
+SIG_EXPR_LIST_DECL_SESG(AC13, UFSCLKI, UFSCLKI, SIG_DESC_SET(SCU010, 19));
+SIG_EXPR_LIST_DECL_SEMG(AC13, EMMCDAT5, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 9));
+SIG_EXPR_LIST_DECL_SESG(AC13, VB0MISO, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_3(AC13, GPIO18B1, UFSCLKI, EMMCDAT5, VB0MISO);
+
+SIG_EXPR_LIST_DECL_SEMG(AD13, EMMCDAT6, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 10));
+SIG_EXPR_LIST_DECL_SESG(AD13, DDCCLK, VGADDC, SIG_DESC_SET(SCU404, 10));
+PIN_DECL_2(AD13, GPIO18B2, EMMCDAT6, DDCCLK);
+
+SIG_EXPR_LIST_DECL_SEMG(AE13, EMMCDAT7, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 11));
+SIG_EXPR_LIST_DECL_SESG(AE13, DDCDAT, VGADDC, SIG_DESC_SET(SCU404, 11));
+PIN_DECL_2(AE13, GPIO18B3, EMMCDAT7, DDCDAT);
+
+GROUP_DECL(EMMCG1, AC14, AE15, AD14);
+GROUP_DECL(EMMCG4, AC14, AE15, AD14, AE14, AF14, AB13);
+GROUP_DECL(EMMCG8, AC14, AE15, AD14, AE14, AF14, AB13, AF13, AC13, AD13, AE13);
+GROUP_DECL(EMMCWPN, AF15);
+GROUP_DECL(EMMCCDN, AB14);
+FUNC_DECL_(EMMC, "EMMCG1", "EMMCG4", "EMMCG8", "EMMCWPN", "EMMCCDN");
+
+GROUP_DECL(VB1, AC14, AE15, AD14, AE14);
+GROUP_DECL(VB0, AF15, AB14, AF13, AC13);
+FUNC_DECL_2(VB, VB1, VB0);
+
+FUNC_GROUP_DECL(TSPRSTN, AF13);
+
+FUNC_GROUP_DECL(UFSCLKI, AC13);
+
+FUNC_GROUP_DECL(VGADDC, AD13, AE13);
+
+/* JTAG Port Selection */
+#define JTAG_PORT_PSP_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x0, 0 }
+#define JTAG_PORT_SSP_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x41, 0 }
+#define JTAG_PORT_TSP_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x42, 0 }
+#define JTAG_PORT_DDR_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x43, 0 }
+#define JTAG_PORT_USB3A_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x44, 0 }
+#define JTAG_PORT_USB3B_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x45, 0 }
+#define JTAG_PORT_PCIEA_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x46, 0 }
+#define JTAG_PORT_PCIEB_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x47, 0 }
+#define JTAG_PORT_JTAGM0_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x8, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPSP, JTAG0, JTAGPSP, JTAG_PORT_PSP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGSSP, JTAG0, JTAGSSP, JTAG_PORT_SSP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGTSP, JTAG0, JTAGTSP, JTAG_PORT_TSP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGDDR, JTAG0, JTAGDDR, JTAG_PORT_DDR_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGUSB3A, JTAG0, JTAGUSB3A, JTAG_PORT_USB3A_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGUSB3B, JTAG0, JTAGUSB3B, JTAG_PORT_USB3B_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPCIEA, JTAG0, JTAGPCIEA, JTAG_PORT_PCIEA_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPCIEB, JTAG0, JTAGPCIEB, JTAG_PORT_PCIEB_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGM0, JTAG0, JTAGM0, JTAG_PORT_JTAGM0_DESC);
+PIN_DECL_(JTAG_PORT, SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPSP), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGSSP),
+ SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGTSP), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGDDR),
+ SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGUSB3A), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGUSB3B),
+ SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPCIEA), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPCIEB),
+ SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGM0));
+
+GROUP_DECL(JTAG0, JTAG_PORT);
+
+FUNC_DECL_1(JTAGPSP, JTAG0);
+FUNC_DECL_1(JTAGSSP, JTAG0);
+FUNC_DECL_1(JTAGTSP, JTAG0);
+FUNC_DECL_1(JTAGDDR, JTAG0);
+FUNC_DECL_1(JTAGUSB3A, JTAG0);
+FUNC_DECL_1(JTAGUSB3B, JTAG0);
+FUNC_DECL_1(JTAGPCIEA, JTAG0);
+FUNC_DECL_1(JTAGPCIEB, JTAG0);
+FUNC_DECL_1(JTAGM0, JTAG0);
+
+/* PCIe Reset Control */
+SIG_EXPR_LIST_DECL_SESG(PCIERC0_PERST, PCIERC0PERST, PCIERC0PERST, SIG_DESC_SET(SCU200, 21));
+PIN_DECL_(PCIERC0_PERST, SIG_EXPR_LIST_PTR(PCIERC0_PERST, PCIERC0PERST));
+FUNC_GROUP_DECL(PCIERC0PERST, PCIERC0_PERST);
+
+SIG_EXPR_LIST_DECL_SESG(PCIERC1_PERST, PCIERC1PERST, PCIERC1PERST, SIG_DESC_SET(SCU200, 19));
+PIN_DECL_(PCIERC1_PERST, SIG_EXPR_LIST_PTR(PCIERC1_PERST, PCIERC1PERST));
+FUNC_GROUP_DECL(PCIERC1PERST, PCIERC1_PERST);
+
+#define PORTA_MODE_HPD0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 0, 0 }
+#define PORTA_MODE_D0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 1, 0 }
+#define PORTA_MODE_H_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 2, 0 }
+#define PORTA_MODE_HP_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AHPD0, USB2AH, USB2AHPD0, PORTA_MODE_HPD0_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AH, USB2AHAP, USB2AH, PORTA_MODE_H_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AHP, USB2AHAP, USB2AHP, PORTA_MODE_HP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AD0, USB2AHAP, USB2AD0, PORTA_MODE_D0_DESC);
+PIN_DECL_(PORTA_MODE, SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AHPD0),
+ SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AH), SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AHP),
+ SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AD0));
+
+#define PORTA_U2_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 0, 0 }
+#define PORTA_U2_D1_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 1, 0 }
+#define PORTA_U2_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 2, 0 }
+#define PORTA_U2_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHD1, USB2A, USB2AXHD1, PORTA_U2_XHD_DESC,
+ SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHPD1, USB2A, USB2AXHPD1, PORTA_U2_XHD_DESC,
+ SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXH, USB2AAP, USB2AXH, PORTA_U2_XH_DESC,
+ SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHP, USB2AAP, USB2AXHP, PORTA_U2_XH_DESC,
+ SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXH2B, USB2ABP, USB2AXH2B, PORTA_U2_XH2E_DESC,
+ SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHP2B, USB2ABP, USB2AXHP2B, PORTA_U2_XH2E_DESC,
+ SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AD1, USB2ADAP, USB2AD1, PORTA_U2_D1_DESC);
+PIN_DECL_(PORTA_U2, SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHD1), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHPD1),
+ SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXH), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHP),
+ SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXH2B), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHP2B),
+ SIG_EXPR_LIST_PTR(PORTA_U2, USB2AD1));
+
+#define PORTB_MODE_HPD0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 0, 0 }
+#define PORTB_MODE_D0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 1, 0 }
+#define PORTB_MODE_H_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 2, 0 }
+#define PORTB_MODE_HP_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BHPD0, USB2BH, USB2BHPD0, PORTB_MODE_HPD0_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BH, USB2BHBP, USB2BH, PORTB_MODE_H_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BHP, USB2BHBP, USB2BHP, PORTB_MODE_HP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BD0, USB2BHBP, USB2BD0, PORTB_MODE_D0_DESC);
+PIN_DECL_(PORTB_MODE, SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BHPD0),
+ SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BH), SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BHP),
+ SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BD0));
+
+#define PORTB_U2_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 0, 0 }
+#define PORTB_U2_D1_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 1, 0 }
+#define PORTB_U2_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 2, 0 }
+#define PORTB_U2_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHD1, USB2B, USB2BXHD1, PORTB_U2_XHD_DESC,
+ SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHPD1, USB2B, USB2BXHPD1, PORTB_U2_XHD_DESC,
+ SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXH, USB2BBP, USB2BXH, PORTB_U2_XH_DESC,
+ SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHP, USB2BBP, USB2BXHP, PORTB_U2_XH_DESC,
+ SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXH2A, USB2BAP, USB2BXH2A, PORTB_U2_XH2E_DESC,
+ SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHP2A, USB2BAP, USB2BXHP2A, PORTB_U2_XH2E_DESC,
+ SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BD1, USB2BDBP, USB2BD1, PORTB_U2_D1_DESC);
+PIN_DECL_(PORTB_U2, SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHD1), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHPD1),
+ SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXH), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHP),
+ SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXH2A), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHP2A),
+ SIG_EXPR_LIST_PTR(PORTB_U2, USB2BD1));
+/*
+ * USB2 virtual PHY pins.
+ *
+ * PORTA_U2_PHY and PORTB_U2_PHY are logical endpoints, not package pins.
+ * They alias existing USB2 expressions so pin groups can model direct and
+ * cross-coupled routing for host and mode paths.
+ *
+ * - USB2AAP/USB2ADAP/USB2AHAP: use PORTA_U2_PHY
+ * - USB2ABP : use PORTB_U2_PHY
+ * - USB2BBP/USB2BDBP/USB2BHBP: use PORTB_U2_PHY
+ * - USB2BAP : use PORTA_U2_PHY
+ *
+ * They do not have any registers to configure this behaviour; the goal is
+ * simply for the driver to prevent conflicting selections. For example,
+ * selecting group USB2ABP and USB2BBP at the same time should not be
+ * allowed.
+ */
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AXH, USB2AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AXHP, USB2AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2BXH2A, USB2BAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2BXHP2A, USB2BAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AD1, USB2ADAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AH, USB2AHAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AHP, USB2AHAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AD0, USB2AHAP);
+PIN_DECL_(PORTA_U2_PHY, SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AXH),
+ SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AXHP), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2BXH2A),
+ SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2BXHP2A), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AD1),
+ SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AH), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AHP),
+ SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AD0));
+
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2AXH2B, USB2ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2AXHP2B, USB2ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BXH, USB2BBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BXHP, USB2BBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BD1, USB2BDBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BH, USB2BHBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BHP, USB2BHBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BD0, USB2BHBP);
+PIN_DECL_(PORTB_U2_PHY, SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2AXH2B),
+ SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2AXHP2B), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BXH),
+ SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BXHP), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BD1),
+ SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BH), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BHP),
+ SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BD0));
+
+GROUP_DECL(USB2A, PORTA_U2);
+GROUP_DECL(USB2AAP, PORTA_U2, PORTA_U2_PHY);
+GROUP_DECL(USB2ABP, PORTA_U2, PORTB_U2_PHY);
+GROUP_DECL(USB2ADAP, PORTA_U2, PORTA_U2_PHY);
+GROUP_DECL(USB2AH, PORTA_MODE);
+GROUP_DECL(USB2AHAP, PORTA_MODE, PORTA_U2_PHY);
+
+FUNC_DECL_1(USB2AXHD1, USB2A);
+FUNC_DECL_1(USB2AXHPD1, USB2A);
+FUNC_DECL_1(USB2AXH, USB2AAP);
+FUNC_DECL_1(USB2AXHP, USB2AAP);
+FUNC_DECL_1(USB2AXH2B, USB2ABP);
+FUNC_DECL_1(USB2AXHP2B, USB2ABP);
+FUNC_DECL_1(USB2AD1, USB2ADAP);
+FUNC_DECL_1(USB2AHPD0, USB2AH);
+FUNC_DECL_1(USB2AH, USB2AHAP);
+FUNC_DECL_1(USB2AHP, USB2AHAP);
+FUNC_DECL_1(USB2AD0, USB2AHAP);
+
+GROUP_DECL(USB2B, PORTB_U2);
+GROUP_DECL(USB2BBP, PORTB_U2, PORTB_U2_PHY);
+GROUP_DECL(USB2BAP, PORTB_U2, PORTA_U2_PHY);
+GROUP_DECL(USB2BDBP, PORTB_U2, PORTB_U2_PHY);
+GROUP_DECL(USB2BH, PORTB_MODE);
+GROUP_DECL(USB2BHBP, PORTB_MODE, PORTB_U2_PHY);
+
+FUNC_DECL_1(USB2BXHD1, USB2B);
+FUNC_DECL_1(USB2BXHPD1, USB2B);
+FUNC_DECL_1(USB2BXH, USB2BBP);
+FUNC_DECL_1(USB2BXHP, USB2BBP);
+FUNC_DECL_1(USB2BXH2A, USB2BAP);
+FUNC_DECL_1(USB2BXHP2A, USB2BAP);
+FUNC_DECL_1(USB2BD1, USB2BDBP);
+FUNC_DECL_1(USB2BHPD0, USB2BH);
+FUNC_DECL_1(USB2BH, USB2BHBP);
+FUNC_DECL_1(USB2BHP, USB2BHBP);
+FUNC_DECL_1(USB2BD0, USB2BHBP);
+
+#define PORTA_U3_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 0, 0 }
+#define PORTA_U3_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 2, 0 }
+#define PORTA_U3_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHD, USB3A, USB3AXHD, PORTA_U3_XHD_DESC,
+ SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHPD, USB3A, USB3AXHPD, PORTA_U3_XHD_DESC,
+ SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXH, USB3AAP, USB3AXH, PORTA_U3_XH_DESC,
+ SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHP, USB3AAP, USB3AXHP, PORTA_U3_XH_DESC,
+ SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXH2B, USB3ABP, USB3AXH2B, PORTA_U3_XH2E_DESC,
+ SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHP2B, USB3ABP, USB3AXHP2B, PORTA_U3_XH2E_DESC,
+ SIG_DESC_CLEAR(SCU410, 9));
+PIN_DECL_(PORTA_U3, SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHD), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHPD),
+ SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXH), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHP),
+ SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXH2B), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHP2B));
+
+#define PORTB_U3_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 0, 0 }
+#define PORTB_U3_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 2, 0 }
+#define PORTB_U3_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHD, USB3B, USB3BXHD, PORTB_U3_XHD_DESC,
+ SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHPD, USB3B, USB3BXHPD, PORTB_U3_XHD_DESC,
+ SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXH, USB3BBP, USB3BXH, PORTB_U3_XH_DESC,
+ SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHP, USB3BBP, USB3BXHP, PORTB_U3_XH_DESC,
+ SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXH2A, USB3BAP, USB3BXH2A, PORTB_U3_XH2E_DESC,
+ SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHP2A, USB3BAP, USB3BXHP2A, PORTB_U3_XH2E_DESC,
+ SIG_DESC_CLEAR(SCU410, 10));
+PIN_DECL_(PORTB_U3, SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHD), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHPD),
+ SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXH), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHP),
+ SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXH2A), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHP2A));
+
+/*
+ * USB3 virtual PHY pins.
+ *
+ * PORTA_U3_PHY and PORTB_U3_PHY are logical endpoints, not package pins.
+ * They alias existing USB3 expressions so pin groups can model both direct and
+ * cross-coupled routing to PHY A/B.
+ *
+ * - USB3AAP: PORTA_U3 + PORTA_U3_PHY (A -> PHY A)
+ * - USB3ABP: PORTA_U3 + PORTB_U3_PHY (A -> PHY B)
+ * - USB3BBP: PORTB_U3 + PORTB_U3_PHY (B -> PHY B)
+ * - USB3BAP: PORTB_U3 + PORTA_U3_PHY (B -> PHY A)
+ *
+ * They do not have any registers to configure this behavior; the goal is
+ * simply for the driver to prevent conflicting selections. For example,
+ * selecting group USB3ABP and USB3BBP at the same time should not be
+ * allowed.
+ */
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3AXH, USB3AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3AXHP, USB3AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3BXH2A, USB3BAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3BXHP2A, USB3BAP);
+PIN_DECL_(PORTA_U3_PHY, SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3AXH),
+ SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3AXHP), SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3BXH2A),
+ SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3BXHP2A));
+
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3AXH2B, USB3ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3AXHP2B, USB3ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3BXH, USB3BBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3BXHP, USB3BBP);
+PIN_DECL_(PORTB_U3_PHY, SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3AXH2B),
+ SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3AXHP2B), SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3BXH),
+ SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3BXHP));
+
+/* USB3A xHCI to vHUB */
+GROUP_DECL(USB3A, PORTA_U3);
+/* USB3A xHCI to USB3A PHY */
+GROUP_DECL(USB3AAP, PORTA_U3, PORTA_U3_PHY);
+/* USB3A xHCI to USB3B PHY */
+GROUP_DECL(USB3ABP, PORTA_U3, PORTB_U3_PHY);
+
+FUNC_DECL_1(USB3AXHD, USB3A);
+FUNC_DECL_1(USB3AXHPD, USB3A);
+FUNC_DECL_1(USB3AXH, USB3AAP);
+FUNC_DECL_1(USB3AXHP, USB3AAP);
+FUNC_DECL_1(USB3AXH2B, USB3ABP);
+FUNC_DECL_1(USB3AXHP2B, USB3ABP);
+
+/* USB3B xHCI to vHUB */
+GROUP_DECL(USB3B, PORTB_U3);
+/* USB3B xHCI to USB3A PHY */
+GROUP_DECL(USB3BAP, PORTB_U3, PORTA_U3_PHY);
+/* USB3B xHCI to USB3B PHY */
+GROUP_DECL(USB3BBP, PORTB_U3, PORTB_U3_PHY);
+
+FUNC_DECL_1(USB3BXHD, USB3B);
+FUNC_DECL_1(USB3BXHPD, USB3B);
+FUNC_DECL_1(USB3BXH, USB3BBP);
+FUNC_DECL_1(USB3BXHP, USB3BBP);
+FUNC_DECL_1(USB3BXH2A, USB3BAP);
+FUNC_DECL_1(USB3BXHP2A, USB3BAP);
+
+static const struct pinctrl_pin_desc aspeed_g7_soc0_pins[] = {
+ ASPEED_PINCTRL_PIN(AC14),
+ ASPEED_PINCTRL_PIN(AE15),
+ ASPEED_PINCTRL_PIN(AD14),
+ ASPEED_PINCTRL_PIN(AE14),
+ ASPEED_PINCTRL_PIN(AF14),
+ ASPEED_PINCTRL_PIN(AB13),
+ ASPEED_PINCTRL_PIN(AB14),
+ ASPEED_PINCTRL_PIN(AF15),
+ ASPEED_PINCTRL_PIN(AF13),
+ ASPEED_PINCTRL_PIN(AC13),
+ ASPEED_PINCTRL_PIN(AD13),
+ ASPEED_PINCTRL_PIN(AE13),
+ ASPEED_PINCTRL_PIN(JTAG_PORT),
+ ASPEED_PINCTRL_PIN(PCIERC0_PERST),
+ ASPEED_PINCTRL_PIN(PCIERC1_PERST),
+ ASPEED_PINCTRL_PIN(PORTA_MODE),
+ ASPEED_PINCTRL_PIN(PORTA_U2),
+ ASPEED_PINCTRL_PIN(PORTA_U3),
+ ASPEED_PINCTRL_PIN(PORTA_U2_PHY),
+ ASPEED_PINCTRL_PIN(PORTA_U3_PHY),
+ ASPEED_PINCTRL_PIN(PORTB_MODE),
+ ASPEED_PINCTRL_PIN(PORTB_U2),
+ ASPEED_PINCTRL_PIN(PORTB_U3),
+ ASPEED_PINCTRL_PIN(PORTB_U2_PHY),
+ ASPEED_PINCTRL_PIN(PORTB_U3_PHY),
+};
+
+static const struct aspeed_pin_group aspeed_g7_soc0_groups[] = {
+ ASPEED_PINCTRL_GROUP(EMMCCDN),
+ ASPEED_PINCTRL_GROUP(EMMCG1),
+ ASPEED_PINCTRL_GROUP(EMMCG4),
+ ASPEED_PINCTRL_GROUP(EMMCG8),
+ ASPEED_PINCTRL_GROUP(EMMCWPN),
+ ASPEED_PINCTRL_GROUP(TSPRSTN),
+ ASPEED_PINCTRL_GROUP(UFSCLKI),
+ ASPEED_PINCTRL_GROUP(VB0),
+ ASPEED_PINCTRL_GROUP(VB1),
+ ASPEED_PINCTRL_GROUP(VGADDC),
+ /* JTAG groups */
+ ASPEED_PINCTRL_GROUP(JTAG0),
+ /* PCIE RC groups */
+ ASPEED_PINCTRL_GROUP(PCIERC0PERST),
+ ASPEED_PINCTRL_GROUP(PCIERC1PERST),
+ /* USB3A groups */
+ ASPEED_PINCTRL_GROUP(USB3A),
+ ASPEED_PINCTRL_GROUP(USB3AAP),
+ ASPEED_PINCTRL_GROUP(USB3ABP),
+ /* USB3B groups */
+ ASPEED_PINCTRL_GROUP(USB3B),
+ ASPEED_PINCTRL_GROUP(USB3BAP),
+ ASPEED_PINCTRL_GROUP(USB3BBP),
+ /* USB2A groups */
+ ASPEED_PINCTRL_GROUP(USB2A),
+ ASPEED_PINCTRL_GROUP(USB2AAP),
+ ASPEED_PINCTRL_GROUP(USB2ABP),
+ ASPEED_PINCTRL_GROUP(USB2ADAP),
+ ASPEED_PINCTRL_GROUP(USB2AH),
+ ASPEED_PINCTRL_GROUP(USB2AHAP),
+ /* USB2B groups */
+ ASPEED_PINCTRL_GROUP(USB2B),
+ ASPEED_PINCTRL_GROUP(USB2BAP),
+ ASPEED_PINCTRL_GROUP(USB2BBP),
+ ASPEED_PINCTRL_GROUP(USB2BDBP),
+ ASPEED_PINCTRL_GROUP(USB2BH),
+ ASPEED_PINCTRL_GROUP(USB2BHBP),
+};
+
+static const struct aspeed_pin_function aspeed_g7_soc0_functions[] = {
+ ASPEED_PINCTRL_FUNC(EMMC),
+ ASPEED_PINCTRL_FUNC(TSPRSTN),
+ ASPEED_PINCTRL_FUNC(UFSCLKI),
+ ASPEED_PINCTRL_FUNC(VB),
+ ASPEED_PINCTRL_FUNC(VGADDC),
+ /* JTAG functions */
+ ASPEED_PINCTRL_FUNC(JTAGDDR),
+ ASPEED_PINCTRL_FUNC(JTAGM0),
+ ASPEED_PINCTRL_FUNC(JTAGPCIEA),
+ ASPEED_PINCTRL_FUNC(JTAGPCIEB),
+ ASPEED_PINCTRL_FUNC(JTAGPSP),
+ ASPEED_PINCTRL_FUNC(JTAGSSP),
+ ASPEED_PINCTRL_FUNC(JTAGTSP),
+ ASPEED_PINCTRL_FUNC(JTAGUSB3A),
+ ASPEED_PINCTRL_FUNC(JTAGUSB3B),
+ /* PCIE RC functions */
+ ASPEED_PINCTRL_FUNC(PCIERC0PERST),
+ ASPEED_PINCTRL_FUNC(PCIERC1PERST),
+ /* USB3A functions */
+ ASPEED_PINCTRL_FUNC(USB3AXH),
+ ASPEED_PINCTRL_FUNC(USB3AXH2B),
+ ASPEED_PINCTRL_FUNC(USB3AXHD),
+ ASPEED_PINCTRL_FUNC(USB3AXHP),
+ ASPEED_PINCTRL_FUNC(USB3AXHP2B),
+ ASPEED_PINCTRL_FUNC(USB3AXHPD),
+ /* USB3B functions */
+ ASPEED_PINCTRL_FUNC(USB3BXH),
+ ASPEED_PINCTRL_FUNC(USB3BXH2A),
+ ASPEED_PINCTRL_FUNC(USB3BXHD),
+ ASPEED_PINCTRL_FUNC(USB3BXHP),
+ ASPEED_PINCTRL_FUNC(USB3BXHP2A),
+ ASPEED_PINCTRL_FUNC(USB3BXHPD),
+ /* USB2A functions */
+ ASPEED_PINCTRL_FUNC(USB2AD0),
+ ASPEED_PINCTRL_FUNC(USB2AD1),
+ ASPEED_PINCTRL_FUNC(USB2AH),
+ ASPEED_PINCTRL_FUNC(USB2AHP),
+ ASPEED_PINCTRL_FUNC(USB2AHPD0),
+ ASPEED_PINCTRL_FUNC(USB2AXH),
+ ASPEED_PINCTRL_FUNC(USB2AXH2B),
+ ASPEED_PINCTRL_FUNC(USB2AXHD1),
+ ASPEED_PINCTRL_FUNC(USB2AXHP),
+ ASPEED_PINCTRL_FUNC(USB2AXHP2B),
+ ASPEED_PINCTRL_FUNC(USB2AXHPD1),
+ /* USB2B functions */
+ ASPEED_PINCTRL_FUNC(USB2BD0),
+ ASPEED_PINCTRL_FUNC(USB2BD1),
+ ASPEED_PINCTRL_FUNC(USB2BH),
+ ASPEED_PINCTRL_FUNC(USB2BHP),
+ ASPEED_PINCTRL_FUNC(USB2BHPD0),
+ ASPEED_PINCTRL_FUNC(USB2BXH),
+ ASPEED_PINCTRL_FUNC(USB2BXH2A),
+ ASPEED_PINCTRL_FUNC(USB2BXHD1),
+ ASPEED_PINCTRL_FUNC(USB2BXHP),
+ ASPEED_PINCTRL_FUNC(USB2BXHP2A),
+ ASPEED_PINCTRL_FUNC(USB2BXHPD1),
+};
+
+static const struct pinmux_ops aspeed_g7_soc0_pinmux_ops = {
+ .get_functions_count = aspeed_pinmux_get_fn_count,
+ .get_function_name = aspeed_pinmux_get_fn_name,
+ .get_function_groups = aspeed_pinmux_get_fn_groups,
+ .set_mux = aspeed_pinmux_set_mux,
+ .gpio_request_enable = aspeed_gpio_request_enable,
+ .strict = true,
+};
+
+static const struct pinctrl_ops aspeed_g7_soc0_pinctrl_ops = {
+ .get_groups_count = aspeed_pinctrl_get_groups_count,
+ .get_group_name = aspeed_pinctrl_get_group_name,
+ .get_group_pins = aspeed_pinctrl_get_group_pins,
+ .pin_dbg_show = aspeed_pinctrl_pin_dbg_show,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+static const struct pinconf_ops aspeed_g7_soc0_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = aspeed_pin_config_get,
+ .pin_config_set = aspeed_pin_config_set,
+ .pin_config_group_get = aspeed_pin_config_group_get,
+ .pin_config_group_set = aspeed_pin_config_group_set,
+};
+
+/* pinctrl_desc */
+static const struct pinctrl_desc aspeed_g7_soc0_pinctrl_desc = {
+ .name = "aspeed-g7-soc0-pinctrl",
+ .pins = aspeed_g7_soc0_pins,
+ .npins = ARRAY_SIZE(aspeed_g7_soc0_pins),
+ .pctlops = &aspeed_g7_soc0_pinctrl_ops,
+ .pmxops = &aspeed_g7_soc0_pinmux_ops,
+ .confops = &aspeed_g7_soc0_pinconf_ops,
+};
+
+static const struct aspeed_pin_config aspeed_g7_soc0_configs[] = {
+ /* GPIO18A */
+ { PIN_CONFIG_DRIVE_STRENGTH, { AC14, AC14 }, SCU480, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AC14, AC14 }, SCU480, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AC14, AC14 }, SCU480, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AC14, AC14 }, SCU480, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AE15, AE15 }, SCU484, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AE15, AE15 }, SCU484, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AE15, AE15 }, SCU484, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AE15, AE15 }, SCU484, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AD14, AD14 }, SCU488, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AD14, AD14 }, SCU488, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AD14, AD14 }, SCU488, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AD14, AD14 }, SCU488, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AE14, AE14 }, SCU48C, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AE14, AE14 }, SCU48C, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AE14, AE14 }, SCU48C, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AE14, AE14 }, SCU48C, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AF14, AF14 }, SCU490, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AF14, AF14 }, SCU490, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AF14, AF14 }, SCU490, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AF14, AF14 }, SCU490, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AB13, AB13 }, SCU494, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AB13, AB13 }, SCU494, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AB13, AB13 }, SCU494, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AB13, AB13 }, SCU494, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AB14, AB14 }, SCU498, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AB14, AB14 }, SCU498, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AB14, AB14 }, SCU498, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AB14, AB14 }, SCU498, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AF15, AF15 }, SCU49C, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AF15, AF15 }, SCU49C, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AF15, AF15 }, SCU49C, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AF15, AF15 }, SCU49C, BIT(5) },
+ /* GPIO18B */
+ { PIN_CONFIG_DRIVE_STRENGTH, { AF13, AF13 }, SCU4A0, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AF13, AF13 }, SCU4A0, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AF13, AF13 }, SCU4A0, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AF13, AF13 }, SCU4A0, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AC13, AC13 }, SCU4A4, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AC13, AC13 }, SCU4A4, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AC13, AC13 }, SCU4A4, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AC13, AC13 }, SCU4A4, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AD13, AD13 }, SCU4A8, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AD13, AD13 }, SCU4A8, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AD13, AD13 }, SCU4A8, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AD13, AD13 }, SCU4A8, BIT(5) },
+ { PIN_CONFIG_DRIVE_STRENGTH, { AE13, AE13 }, SCU4AC, GENMASK(3, 0) },
+ { PIN_CONFIG_BIAS_PULL_DOWN, { AE13, AE13 }, SCU4AC, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_PULL_UP, { AE13, AE13 }, SCU4AC, GENMASK(5, 4) },
+ { PIN_CONFIG_BIAS_DISABLE, { AE13, AE13 }, SCU4AC, BIT(5) },
+};
+
+static const struct aspeed_pin_config_map aspeed_g7_soc0_pin_config_map[] = {
+ { PIN_CONFIG_BIAS_PULL_DOWN, -1, 2, GENMASK(1, 0) },
+ { PIN_CONFIG_BIAS_PULL_UP, -1, 3, GENMASK(1, 0) },
+ { PIN_CONFIG_BIAS_DISABLE, -1, 0, BIT_MASK(0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 3, 0, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 6, 1, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 8, 2, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 11, 3, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 16, 4, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 18, 5, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 20, 6, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 23, 7, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 30, 8, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 32, 9, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 33, 10, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 35, 11, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 37, 12, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 38, 13, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 39, 14, GENMASK(3, 0) },
+ { PIN_CONFIG_DRIVE_STRENGTH, 41, 15, GENMASK(3, 0) },
+
+};
+
+static int aspeed_g7_soc0_sig_expr_set(struct aspeed_pinmux_data *ctx,
+ const struct aspeed_sig_expr *expr, bool enable)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < expr->ndescs; i++) {
+ const struct aspeed_sig_desc *desc = &expr->descs[i];
+ u32 pattern = enable ? desc->enable : desc->disable;
+ u32 val = (pattern << __ffs(desc->mask));
+
+ if (!ctx->maps[desc->ip])
+ return -ENODEV;
+
+ WARN_ON_ONCE(desc->ip != ASPEED_IP_SCU);
+
+ ret = regmap_update_bits(ctx->maps[desc->ip], desc->reg,
+ desc->mask, val);
+ if (ret)
+ return ret;
+ }
+
+ ret = aspeed_sig_expr_eval(ctx, expr, enable);
+ if (ret < 0)
+ return ret;
+
+ return ret ? 0 : -EPERM;
+}
+
+static const struct aspeed_pinmux_ops aspeed_g7_soc0_ops = {
+ .set = aspeed_g7_soc0_sig_expr_set,
+};
+
+static struct aspeed_pinctrl_data aspeed_g7_soc0_pinctrl_data = {
+ .pins = aspeed_g7_soc0_pins,
+ .npins = ARRAY_SIZE(aspeed_g7_soc0_pins),
+ .pinmux = {
+ .ops = &aspeed_g7_soc0_ops,
+ .groups = aspeed_g7_soc0_groups,
+ .ngroups = ARRAY_SIZE(aspeed_g7_soc0_groups),
+ .functions = aspeed_g7_soc0_functions,
+ .nfunctions = ARRAY_SIZE(aspeed_g7_soc0_functions),
+ },
+ .configs = aspeed_g7_soc0_configs,
+ .nconfigs = ARRAY_SIZE(aspeed_g7_soc0_configs),
+ .confmaps = aspeed_g7_soc0_pin_config_map,
+ .nconfmaps = ARRAY_SIZE(aspeed_g7_soc0_pin_config_map),
+};
+
+static int aspeed_g7_soc0_pinctrl_probe(struct platform_device *pdev)
+{
+ return aspeed_pinctrl_probe(pdev, &aspeed_g7_soc0_pinctrl_desc,
+ &aspeed_g7_soc0_pinctrl_data);
+}
+
+static const struct of_device_id aspeed_g7_soc0_pinctrl_match[] = {
+ { .compatible = "aspeed,ast2700-soc0-pinctrl" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aspeed_g7_soc0_pinctrl_match);
+
+static struct platform_driver aspeed_g7_soc0_pinctrl_driver = {
+ .probe = aspeed_g7_soc0_pinctrl_probe,
+ .driver = {
+ .name = "aspeed-g7-soc0-pinctrl",
+ .of_match_table = aspeed_g7_soc0_pinctrl_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init aspeed_g7_soc0_pinctrl_init(void)
+{
+ return platform_driver_register(&aspeed_g7_soc0_pinctrl_driver);
+}
+arch_initcall(aspeed_g7_soc0_pinctrl_init);
--
2.34.1
^ permalink raw reply related
* [PATCH v7 2/3] dt-bindings: mfd: aspeed,ast2x00-scu: Describe AST2700 SCU0
From: Billy Tsai @ 2026-04-16 7:29 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
Bartosz Golaszewski, Ryan Chen
Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, linux-gpio, linux-clk
In-Reply-To: <20260416-upstream_pinctrl-v7-0-d72762253163@aspeedtech.com>
AST2700 consists of two interconnected SoC instances, each with its own
System Control Unit (SCU). The SCU0 provides pin control, interrupt
controllers, clocks, resets, and address-space mappings for the
Secondary and Tertiary Service Processors (SSP and TSP).
Describe the SSP/TSP address mappings using the standard
memory-region and memory-region-names properties.
Disallow legacy child nodes that are not present on AST2700, including
p2a-control and smp-memram. The latter is unnecessary as software can
access the scratch registers via the SCU syscon.
Also allow the AST2700 SoC0 pin controller to be described as a child
node of the SCU0, and add an example illustrating the SCU0 layout,
including reserved-memory, interrupt controllers, and pinctrl.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
.../bindings/mfd/aspeed,ast2x00-scu.yaml | 113 +++++++++++++++++++++
1 file changed, 113 insertions(+)
diff --git a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
index a87f31fce019..215ff59b38ea 100644
--- a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
+++ b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
@@ -46,6 +46,17 @@ properties:
'#reset-cells':
const: 1
+ memory-region:
+ items:
+ - description: Region mapped through the first SSP address window.
+ - description: Region mapped through the second SSP address window.
+ - description: Region mapped through the TSP address window.
+ memory-region-names:
+ items:
+ - const: ssp-0
+ - const: ssp-1
+ - const: tsp
+
patternProperties:
'^p2a-control@[0-9a-f]+$':
description: >
@@ -87,6 +98,7 @@ patternProperties:
- aspeed,ast2400-pinctrl
- aspeed,ast2500-pinctrl
- aspeed,ast2600-pinctrl
+ - aspeed,ast2700-soc0-pinctrl
required:
- compatible
@@ -156,6 +168,30 @@ required:
- '#clock-cells'
- '#reset-cells'
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ anyOf:
+ - const: aspeed,ast2700-scu0
+ - const: aspeed,ast2700-scu1
+ then:
+ patternProperties:
+ '^p2a-control@[0-9a-f]+$': false
+ '^smp-memram@[0-9a-f]+$': false
+
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ const: aspeed,ast2700-scu0
+ then:
+ properties:
+ memory-region: false
+ memory-region-names: false
+
additionalProperties: false
examples:
@@ -180,4 +216,81 @@ examples:
reg = <0x7c 0x4>, <0x150 0x8>;
};
};
+
+ - |
+ / {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ ssp_region_0: memory@400000000 {
+ reg = <0x4 0x00000000 0x0 0x01000000>;
+ no-map;
+ };
+
+ ssp_region_1: memory@401000000 {
+ reg = <0x4 0x01000000 0x0 0x01000000>;
+ no-map;
+ };
+
+ tsp_region: memory@402000000 {
+ reg = <0x4 0x02000000 0x0 0x01000000>;
+ no-map;
+ };
+ };
+
+ bus {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ syscon@12c02000 {
+ compatible = "aspeed,ast2700-scu0", "syscon", "simple-mfd";
+ reg = <0 0x12c02000 0 0x1000>;
+ ranges = <0x0 0x0 0x12c02000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ memory-region = <&ssp_region_0>, <&ssp_region_1>,
+ <&tsp_region>;
+ memory-region-names = "ssp-0", "ssp-1", "tsp";
+
+ silicon-id@0 {
+ compatible = "aspeed,ast2700-silicon-id", "aspeed,silicon-id";
+ reg = <0x0 0x4>;
+ };
+
+ interrupt-controller@1b0 {
+ compatible = "aspeed,ast2700-scu-ic0";
+ reg = <0x1b0 0x4>;
+ #interrupt-cells = <1>;
+ interrupts-extended = <&intc0 97>;
+ interrupt-controller;
+ };
+
+ interrupt-controller@1e0 {
+ compatible = "aspeed,ast2700-scu-ic1";
+ reg = <0x1e0 0x4>;
+ #interrupt-cells = <1>;
+ interrupts-extended = <&intc0 98>;
+ interrupt-controller;
+ };
+
+ pinctrl@400 {
+ compatible = "aspeed,ast2700-soc0-pinctrl";
+ reg = <0x400 0x318>;
+ emmc-state {
+ function = "EMMC";
+ groups = "EMMCG1";
+ };
+ };
+ };
+ };
+ };
+
...
--
2.34.1
^ permalink raw reply related
* [PATCH v7 1/3] dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl
From: Billy Tsai @ 2026-04-16 7:29 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
Bartosz Golaszewski, Ryan Chen
Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, linux-gpio, linux-clk
In-Reply-To: <20260416-upstream_pinctrl-v7-0-d72762253163@aspeedtech.com>
Add a device tree binding for the pin controller found in the
ASPEED AST2700 SoC0.
The controller manages various peripheral functions such as eMMC, USB,
VGA DDC, JTAG, and PCIe root complex signals.
Describe the AST2700 SoC0 pin controller using standard pin multiplexing
and configuration properties.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
.../pinctrl/aspeed,ast2700-soc0-pinctrl.yaml | 162 +++++++++++++++++++++
1 file changed, 162 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml
new file mode 100644
index 000000000000..947f3cd09fcc
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED AST2700 SoC0 Pin Controller
+
+maintainers:
+ - Billy Tsai <billy_tsai@aspeedtech.com>
+
+description:
+ The AST2700 features a dual-SoC architecture with two interconnected SoCs,
+ each having its own System Control Unit (SCU) for independent pin control.
+ This pin controller manages the pin multiplexing for SoC0.
+
+ The SoC0 pin controller manages pin functions including eMMC, VGA DDC,
+ dual USB3/USB2 ports (A and B), JTAG, and PCIe root complex interfaces.
+
+properties:
+ compatible:
+ const: aspeed,ast2700-soc0-pinctrl
+ reg:
+ maxItems: 1
+
+patternProperties:
+ '-state$':
+ type: object
+ allOf:
+ - $ref: pinmux-node.yaml#
+ - $ref: pincfg-node.yaml#
+
+ additionalProperties: false
+
+ properties:
+ function:
+ enum:
+ - EMMC
+ - JTAGDDR
+ - JTAGM0
+ - JTAGPCIEA
+ - JTAGPCIEB
+ - JTAGPSP
+ - JTAGSSP
+ - JTAGTSP
+ - JTAGUSB3A
+ - JTAGUSB3B
+ - PCIERC0PERST
+ - PCIERC1PERST
+ - TSPRSTN
+ - UFSCLKI
+ - USB2AD0
+ - USB2AD1
+ - USB2AH
+ - USB2AHP
+ - USB2AHPD0
+ - USB2AXH
+ - USB2AXH2B
+ - USB2AXHD1
+ - USB2AXHP
+ - USB2AXHP2B
+ - USB2AXHPD1
+ - USB2BD0
+ - USB2BD1
+ - USB2BH
+ - USB2BHP
+ - USB2BHPD0
+ - USB2BXH
+ - USB2BXH2A
+ - USB2BXHD1
+ - USB2BXHP
+ - USB2BXHP2A
+ - USB2BXHPD1
+ - USB3AXH
+ - USB3AXH2B
+ - USB3AXHD
+ - USB3AXHP
+ - USB3AXHP2B
+ - USB3AXHPD
+ - USB3BXH
+ - USB3BXH2A
+ - USB3BXHD
+ - USB3BXHP
+ - USB3BXHP2A
+ - USB3BXHPD
+ - VB
+ - VGADDC
+
+ groups:
+ enum:
+ - EMMCCDN
+ - EMMCG1
+ - EMMCG4
+ - EMMCG8
+ - EMMCWPN
+ - JTAG0
+ - PCIERC0PERST
+ - PCIERC1PERST
+ - TSPRSTN
+ - UFSCLKI
+ - USB2A
+ - USB2AAP
+ - USB2ABP
+ - USB2ADAP
+ - USB2AH
+ - USB2AHAP
+ - USB2B
+ - USB2BAP
+ - USB2BBP
+ - USB2BDBP
+ - USB2BH
+ - USB2BHBP
+ - USB3A
+ - USB3AAP
+ - USB3ABP
+ - USB3B
+ - USB3BAP
+ - USB3BBP
+ - VB0
+ - VB1
+ - VGADDC
+ pins:
+ enum:
+ - AB13
+ - AB14
+ - AC13
+ - AC14
+ - AD13
+ - AD14
+ - AE13
+ - AE14
+ - AE15
+ - AF13
+ - AF14
+ - AF15
+
+ drive-strength:
+ enum: [3, 6, 8, 11, 16, 18, 20, 23, 30, 32, 33, 35, 37, 38, 39, 41]
+
+ bias-disable: true
+ bias-pull-up: true
+ bias-pull-down: true
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: pinctrl.yaml#
+
+additionalProperties: false
+
+examples:
+ - |
+ pinctrl@400 {
+ compatible = "aspeed,ast2700-soc0-pinctrl";
+ reg = <0x400 0x318>;
+ emmc-state {
+ function = "EMMC";
+ groups = "EMMCG1";
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v7 0/3] pinctrl: aspeed: Add AST2700 SoC0 support
From: Billy Tsai @ 2026-04-16 7:29 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
Bartosz Golaszewski, Ryan Chen
Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, openbmc, linux-gpio, linux-clk
AST2700 is composed of two interconnected SoC instances, each providing
its own pin control hardware. This series introduces bindings describing
the AST2700 pinctrl architecture and adds pinctrl driver support for the
SoC0 instance.
The bindings document the AST2700 dual-SoC design and follow common
pinctrl conventions, while the SoC0 driver implementation builds upon
the existing ASPEED pinctrl infrastructure.
---
Changes in v7:
- Fix schema validation error by ensuring if/then/else keywords are used
correctly.
- Remove unnecessary restrictions on AST2700 SoC0 pinctrl pin configuration
properties.
- Link to v6: https://lore.kernel.org/r/20260414-upstream_pinctrl-v6-0-709f2127da33@aspeedtech.com
Changes in v6:
- Restrict AST2700 SoC0 pinctrl pin configuration properties
(`drive-strength` and `bias-*`) to `pins`-based state nodes in the
binding schema.
- Move `memory-region` and `memory-region-names` in the AST2x00 SCU
binding to top-level descriptions, and keep the conditional schema
only to disallow them for non-AST2700 SCU0 compatibles.
- Add bias pull-up, pull-down, and disable support for AST2700 SoC0
GPIO18A/GPIO18B pins in the pinctrl driver.
- Fix the USB2 Port B XH/XHP mux selector definitions to use the
correct `PORTB_U2_XH_DESC` setting.
- Link to v5: https://lore.kernel.org/r/20260331-upstream_pinctrl-v5-0-8994f59ff367@aspeedtech.com
Changes in v5:
- Complete the AST2700 SCU0 binding and disallow child nodes that are
not relevant for the hardware (p2a-control and smp-memram).
- Add examples for both the AST2700 SCU0 binding and the pinctrl binding,
ensuring they are valid against the schema.
- Rework the pinctrl binding example to be self-contained and independent
of the SCU binding.
- Reorder the binding patches so the pinctrl binding is introduced before
the SCU binding update, allowing the SCU example to be added cleanly.
- Adjust the binding accordingly to restrict drive-strength to the
supported values.
- Update the drive-strength table to match hardware-defined values.
- Link to v4: https://lore.kernel.org/r/20260306-upstream_pinctrl-v4-0-ad4e8ab8b489@aspeedtech.com
Changes in v4:
- Rename series title to "pinctrl: aspeed: Add AST2700 SoC0 support"
to make it specific to SoC0.
- Remove unnecessary SCU example from bindings.
- Fix Makefile newline to avoid patch warning.
- Make pinctrl data structures const and align with existing Aspeed drivers.
- Sort the arrays and enums alphabetically.
- Minor cleanups for consistency, no functional changes.
- Link to v3: https://lore.kernel.org/r/20260120-upstream_pinctrl-v3-0-868fbf8413b5@aspeedtech.com
Changes in v3:
dt-bindings: pinctrl: aspeed: AST2700 pinctrl improvements
- Improved binding descriptions for SoC0 and SoC1 to better explain the
AST2700 dual-SoC architecture with independent pin control blocks
- Switched from additionalProperties to patternProperties using the
'-state$' suffix to restrict child node naming
- Removed per-binding examples based on review feedback
- Added additionalProperties: false at the top level for stricter schema
validation
- Dropped the aspeed,ast2700-soc1-pinctrl binding, as the SoC1 pinctrl
registers follow a regular layout and can be described using an
existing generic pinctrl binding
- Updated the function and group enum lists to match the definitions
used by the AST2700 pinctrl driver
dt-bindings: mfd: aspeed: Add AST2700 SCU example with pinctrl
- Added a complete AST2700 SCU0 example demonstrating pinctrl integration
- Example covers both pin function/group configuration and pin
drive-strength settings
- Updated child node naming to use the '-state' suffix, following common
pinctrl conventions
pinctrl: aspeed: AST2700 SoC0 driver improvements
- Refactored pin and signal declarations to use common ASPEED pinmux
macros (SIG_EXPR_LIST_DECL_SEMG, SIG_EXPR_LIST_DECL_SESG, PIN_DECL_*)
- Added SCU010 register definition for hardware strap control
- Reworked code structure to better align with existing ASPEED pinctrl
drivers
- Link to v2: https://lore.kernel.org/r/20250904103401.88287-1-billy_tsai@aspeedtech.com
Changes in v2:
- Update pinctrl aspeed binding files.
- Update the commit message for pinctrl binding patch.
- Link to v1: https://lore.kernel.org/r/20250829073030.2749482-1-billy_tsai@aspeedtech.com
---
Billy Tsai (3):
dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl
dt-bindings: mfd: aspeed,ast2x00-scu: Describe AST2700 SCU0
pinctrl: aspeed: Add AST2700 SoC0 support
.../bindings/mfd/aspeed,ast2x00-scu.yaml | 113 ++++
.../pinctrl/aspeed,ast2700-soc0-pinctrl.yaml | 162 +++++
drivers/pinctrl/aspeed/Kconfig | 9 +
drivers/pinctrl/aspeed/Makefile | 1 +
drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c | 749 +++++++++++++++++++++
5 files changed, 1034 insertions(+)
---
base-commit: af4e9ef3d78420feb8fe58cd9a1ab80c501b3c08
change-id: 20251215-upstream_pinctrl-8f195df0a975
Best regards,
--
Billy Tsai <billy_tsai@aspeedtech.com>
^ permalink raw reply
* [PATCH v2 2/5] irqchip/starfive: Rename jh8100 to jhb100
From: Changhuang Liang @ 2026-04-16 6:47 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
Philipp Zabel
Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
Changhuang Liang
In-Reply-To: <20260416064751.632138-1-changhuang.liang@starfivetech.com>
The StarFive JH8100 SoC was discontinued before production. The
newly taped-out JHB100 SoC uses the same interrupt controller IP.
Rename the driver file, Kconfig symbol, and internal references
from "jh8100" to "jhb100" to accurately reflect the supported
hardware.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
MAINTAINERS | 4 ++--
drivers/irqchip/Kconfig | 6 +++---
drivers/irqchip/Makefile | 2 +-
...arfive-jh8100-intc.c => irq-starfive-jhb100-intc.c} | 10 +++++-----
4 files changed, 11 insertions(+), 11 deletions(-)
rename drivers/irqchip/{irq-starfive-jh8100-intc.c => irq-starfive-jhb100-intc.c} (94%)
diff --git a/MAINTAINERS b/MAINTAINERS
index a2961727e3d1..93cbe852ac0b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25309,11 +25309,11 @@ F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml
F: drivers/phy/starfive/phy-jh7110-pcie.c
F: drivers/phy/starfive/phy-jh7110-usb.c
-STARFIVE JH8100 EXTERNAL INTERRUPT CONTROLLER DRIVER
+STARFIVE JHB100 EXTERNAL INTERRUPT CONTROLLER DRIVER
M: Changhuang Liang <changhuang.liang@starfivetech.com>
S: Supported
F: Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml
-F: drivers/irqchip/irq-starfive-jh8100-intc.c
+F: drivers/irqchip/irq-starfive-jhb100-intc.c
STATIC BRANCH/CALL
M: Peter Zijlstra <peterz@infradead.org>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index f07b00d7fef9..697c6b2e006c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -651,13 +651,13 @@ config SIFIVE_PLIC
select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
-config STARFIVE_JH8100_INTC
- bool "StarFive JH8100 External Interrupt Controller"
+config STARFIVE_JHB100_INTC
+ bool "StarFive JHB100 External Interrupt Controller"
depends on ARCH_STARFIVE || COMPILE_TEST
default ARCH_STARFIVE
select IRQ_DOMAIN_HIERARCHY
help
- This enables support for the INTC chip found in StarFive JH8100
+ This enables support for the INTC chip found in StarFive JHB100
SoC.
If you don't know what to do here, say Y.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 26aa3b6ec99f..c686caaa4451 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -108,7 +108,7 @@ obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o
obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o
obj-$(CONFIG_RISCV_RPMI_SYSMSI) += irq-riscv-rpmi-sysmsi.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
-obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o
+obj-$(CONFIG_STARFIVE_JHB100_INTC) += irq-starfive-jhb100-intc.o
obj-$(CONFIG_ACLINT_SSWI) += irq-aclint-sswi.o
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o
diff --git a/drivers/irqchip/irq-starfive-jh8100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c
similarity index 94%
rename from drivers/irqchip/irq-starfive-jh8100-intc.c
rename to drivers/irqchip/irq-starfive-jhb100-intc.c
index bb62ef363d0b..2c9cdad7f377 100644
--- a/drivers/irqchip/irq-starfive-jh8100-intc.c
+++ b/drivers/irqchip/irq-starfive-jhb100-intc.c
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * StarFive JH8100 External Interrupt Controller driver
+ * StarFive JHB100 External Interrupt Controller driver
*
* Copyright (C) 2023 StarFive Technology Co., Ltd.
*
* Author: Changhuang Liang <changhuang.liang@starfivetech.com>
*/
-#define pr_fmt(fmt) "irq-starfive-jh8100: " fmt
+#define pr_fmt(fmt) "irq-starfive-jhb100: " fmt
#include <linux/bitops.h>
#include <linux/clk.h>
@@ -71,7 +71,7 @@ static void starfive_intc_mask(struct irq_data *d)
}
static struct irq_chip intc_dev = {
- .name = "StarFive JH8100 INTC",
+ .name = "StarFive JHB100 INTC",
.irq_unmask = starfive_intc_unmask,
.irq_mask = starfive_intc_mask,
};
@@ -199,9 +199,9 @@ static int starfive_intc_probe(struct platform_device *pdev, struct device_node
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc)
-IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_probe)
+IRQCHIP_MATCH("starfive,jhb100-intc", starfive_intc_probe)
IRQCHIP_PLATFORM_DRIVER_END(starfive_intc)
-MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller");
+MODULE_DESCRIPTION("StarFive JHB100 External Interrupt Controller");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v4 2/2] drm/panel: Add panel driver for ChipWealth CH13726A based panels
From: Aaron Kling @ 2026-04-16 7:19 UTC (permalink / raw)
To: Neil Armstrong
Cc: Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, dri-devel, devicetree,
linux-kernel, Teguh Sobirin
In-Reply-To: <63870098-5e70-44af-ba18-1fd726b5ef5a@linaro.org>
On Mon, Apr 13, 2026 at 4:17 AM Neil Armstrong
<neil.armstrong@linaro.org> wrote:
>
> On 4/8/26 07:32, Aaron Kling via B4 Relay wrote:
> > From: Teguh Sobirin <teguh@sobir.in>
> >
> > This is used by the AYN Thor for the bottom panel.
> >
> > Signed-off-by: Teguh Sobirin <teguh@sobir.in>
> > Co-developed-by: Aaron Kling <webgeek1234@gmail.com>
> > Signed-off-by: Aaron Kling <webgeek1234@gmail.com>
> > ---
> > drivers/gpu/drm/panel/Kconfig | 11 +
> > drivers/gpu/drm/panel/Makefile | 1 +
> > drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c | 339 ++++++++++++++++++++++
> > 3 files changed, 351 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > index d6863b28ddc559..e2c00f08f4507d 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -105,6 +105,17 @@ config DRM_PANEL_BOE_TV101WUM_LL2
> > Say Y here if you want to support for BOE TV101WUM-LL2
> > WUXGA PANEL DSI Video Mode panel
> >
> > +config DRM_PANEL_CHIPWEALTH_CH13726A
> > + tristate "CHIPWEALTH CH13726A-based DSI panel"
> > + depends on OF
> > + depends on DRM_MIPI_DSI
> > + depends on BACKLIGHT_CLASS_DEVICE
> > + select DRM_DISPLAY_DP_HELPER
> > + select DRM_DISPLAY_HELPER
> > + help
> > + Say Y here if you want to enable support for ChipWealth
> > + CH13726A-based display panels.
> > +
> > config DRM_PANEL_EBBG_FT8719
> > tristate "EBBG FT8719 panel driver"
> > depends on OF
> > diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> > index a4291dc3905bed..343d283d1620fb 100644
> > --- a/drivers/gpu/drm/panel/Makefile
> > +++ b/drivers/gpu/drm/panel/Makefile
> > @@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o
> > obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o
> > obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o
> > obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
> > +obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o
> > obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
> > obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
> > obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
> > diff --git a/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c
> > new file mode 100644
> > index 00000000000000..48a5e20e07c487
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c
> > @@ -0,0 +1,339 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * ChipWealth CH13726A MIPI-DSI panel driver
> > + * Copyright (c) 2024, Teguh Sobirin <teguh@sobir.in>.
> > + */
> > +
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#include <drm/drm_mipi_dsi.h>
> > +#include <drm/drm_modes.h>
> > +#include <drm/drm_panel.h>
> > +
> > +#include <video/mipi_display.h>
> > +
> > +struct ch13726a_panel {
> > + struct drm_panel panel;
> > + struct mipi_dsi_device *dsi;
> > + struct regulator_bulk_data supplies[4];
> > + struct gpio_desc *reset_gpio;
> > + struct ch13726a_desc *desc;
> > + enum drm_panel_orientation orientation;
> > + bool prepared;
>
> Drop this, it's handled by the panel core now.
Ack.
>
> > +};
> > +
> > +struct ch13726a_desc {
> > + unsigned int width_mm;
> > + unsigned int height_mm;
> > + unsigned int bpc;
> > +
> > + const struct drm_display_mode *modes;
> > + unsigned int num_modes;
> > +};
> > +
> > +static inline struct ch13726a_panel *to_ch13726a_panel(struct drm_panel *panel)
> > +{
> > + return container_of(panel, struct ch13726a_panel, panel);
> > +}
> > +
> > +static void ch13726a_reset(struct ch13726a_panel *ctx)
> > +{
> > + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> > + usleep_range(10000, 11000);
> > + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> > + usleep_range(10000, 11000);
> > + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> > + usleep_range(10000, 11000);
> > +}
> > +
> > +static int ch13726a_on(struct ch13726a_panel *ctx)
> > +{
> > + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
> > +
> > + ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x50);
> > + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb9, 0x00);
> > +
> > + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
> > +
> > + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
> > +
> > + return dsi_ctx.accum_err;
> > +}
> > +
> > +static int ch13726a_disable(struct drm_panel *panel)
> > +{
> > + struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
> > + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
> > +
> > + ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
> > + mipi_dsi_msleep(&dsi_ctx, 50);
> > + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
> > +
> > + return dsi_ctx.accum_err;
> > +}
> > +
> > +static int ch13726a_prepare(struct drm_panel *panel)
> > +{
> > + struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
> > + struct device *dev = &ctx->dsi->dev;
> > + int ret;
> > +
> > + if (ctx->prepared)
> > + return 0;
> > +
> > + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to enable regulators: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + ch13726a_reset(ctx);
> > +
> > + ret = ch13726a_on(ctx);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to initialize panel: %d\n", ret);
> > + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> > + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > + return ret;
> > + }
> > +
> > + msleep(28);
> > +
> > + ctx->prepared = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int ch13726a_unprepare(struct drm_panel *panel)
> > +{
> > + struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
> > +
> > + if (!ctx->prepared)
> > + return 0;
> > +
> > + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> > + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > +
> > + ctx->prepared = false;
> > + return 0;
> > +}
> > +
> > +static const struct drm_display_mode thor_bottom_modes[] = {
> > + {
> > + /* 120Hz */
> > + .clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 120 / 1000,
> > + .hdisplay = 1080,
> > + .hsync_start = 1080 + 28,
> > + .hsync_end = 1080 + 28 + 4,
> > + .htotal = 1080 + 28 + 4 + 36,
> > + .vdisplay = 1240,
> > + .vsync_start = 1240 + 16,
> > + .vsync_end = 1240 + 16 + 4,
> > + .vtotal = 1240 + 16 + 4 + 8,
> > + },
> > + {
> > + /* 60Hz */
> > + .clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 60 / 1000,
> > + .hdisplay = 1080,
> > + .hsync_start = 1080 + 28,
> > + .hsync_end = 1080 + 28 + 4,
> > + .htotal = 1080 + 28 + 4 + 36,
> > + .vdisplay = 1240,
> > + .vsync_start = 1240 + 16,
> > + .vsync_end = 1240 + 16 + 4,
> > + .vtotal = 1240 + 16 + 4 + 8,
> > + }
> > +};
> > +
> > +static struct ch13726a_desc thor_bottom_desc = {
> > + .modes = thor_bottom_modes,
> > + .num_modes = ARRAY_SIZE(thor_bottom_modes),
> > + .width_mm = 65,
> > + .height_mm = 75,
> > + .bpc = 8,
> > +};
> > +
> > +static int ch13726a_get_modes(struct drm_panel *panel,
> > + struct drm_connector *connector)
> > +{
> > + struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
> > +
> > + for (uint8_t i = 0; i < ctx->desc->num_modes; i++) {
> > + const struct drm_display_mode *m = &ctx->desc->modes[i];
> > + struct drm_display_mode *mode;
> > +
> > + mode = drm_mode_duplicate(connector->dev, m);
> > + if (!mode) {
> > + dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
> > + m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
> > + return -ENOMEM;
> > + }
>
> Can you use drm_connector_helper_get_modes_fixed instead ?
Per the description for that function, it only works if there's a
single mode. This panel supports two modes, 60hz and 120hz.
>
> > +
> > + mode->type = DRM_MODE_TYPE_DRIVER;
> > + if (i == 0)
> > + mode->type |= DRM_MODE_TYPE_PREFERRED;
> > +
> > + drm_mode_set_name(mode);
> > + drm_mode_probed_add(connector, mode);
> > + }
> > +
> > + connector->display_info.width_mm = ctx->desc->width_mm;
> > + connector->display_info.height_mm = ctx->desc->height_mm;
> > + connector->display_info.bpc = ctx->desc->bpc;
> > +
> > + return ctx->desc->num_modes;
> > +}
> > +
> > +static enum drm_panel_orientation ch13726a_get_orientation(struct drm_panel *panel)
> > +{
> > + struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
> > +
> > + return ctx->orientation;
> > +}
> > +
> > +static const struct drm_panel_funcs ch13726a_panel_funcs = {
> > + .prepare = ch13726a_prepare,
> > + .unprepare = ch13726a_unprepare,
> > + .disable = ch13726a_disable,
> > + .get_modes = ch13726a_get_modes,
> > + .get_orientation = ch13726a_get_orientation,
> > +};
> > +
> > +static int ch13726a_bl_update_status(struct backlight_device *bl)
> > +{
> > + struct mipi_dsi_device *dsi = bl_get_data(bl);
> > + u16 brightness = backlight_get_brightness(bl);
> > + int ret;
> > +
> > + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > + ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
> > + if (ret < 0)
> > + return ret;
> > +
> > + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct backlight_ops ch13726a_bl_ops = {
> > + .update_status = ch13726a_bl_update_status,
> > +};
> > +
> > +static struct backlight_device *
> > +ch13726a_create_backlight(struct mipi_dsi_device *dsi)
> > +{
> > + struct device *dev = &dsi->dev;
> > + const struct backlight_properties props = {
> > + .type = BACKLIGHT_RAW,
> > + .brightness = 255,
> > + .max_brightness = 255,
> > + };
> > +
> > + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
> > + &ch13726a_bl_ops, &props);
> > +}
> > +
> > +static int ch13726a_probe(struct mipi_dsi_device *dsi)
> > +{
> > + struct device *dev = &dsi->dev;
> > + struct ch13726a_panel *ctx;
> > + int ret;
> > +
> > + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return -ENOMEM;
> > +
> > + ctx->desc = (struct ch13726a_desc *)of_device_get_match_data(dev);
> > + if (!ctx->desc)
> > + return -ENODEV;
> > +
> > + ctx->supplies[0].supply = "vdd1v2";
> > + ctx->supplies[1].supply = "vddio";
> > + ctx->supplies[2].supply = "vdd";
> > + ctx->supplies[3].supply = "avdd";
> > +
> > + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
> > + ctx->supplies);
> > + if (ret < 0)
> > + return dev_err_probe(dev, ret, "Failed to get regulators\n");
> > +
> Can you switch to devm_regulator_bulk_get_const ?
Looks like I can.
>
> > + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> > + if (IS_ERR(ctx->reset_gpio))
> > + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
> > + "Failed to get reset-gpios\n");
> > +
> > + ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
> > + if (ret < 0) {
> > + dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
> > + return ret;
> > + }
> > +
> > + ctx->dsi = dsi;
> > + mipi_dsi_set_drvdata(dsi, ctx);
> > +
> > + dsi->lanes = 4;
> > + dsi->format = MIPI_DSI_FMT_RGB888;
> > + dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
> > + MIPI_DSI_CLOCK_NON_CONTINUOUS;
> > +
> > + drm_panel_init(&ctx->panel, dev, &ch13726a_panel_funcs,
> > + DRM_MODE_CONNECTOR_DSI);
>
> Please use devm_drm_panel_alloc() instead.
Ack
>
> > + ctx->panel.prepare_prev_first = true;
> > +
> > + ctx->panel.backlight = ch13726a_create_backlight(dsi);
> > + if (IS_ERR(ctx->panel.backlight))
> > + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
> > + "Failed to create backlight\n");
> > +
> > + drm_panel_add(&ctx->panel);
> > +
> > + ret = mipi_dsi_attach(dsi);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
> > + drm_panel_remove(&ctx->panel);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void ch13726a_remove(struct mipi_dsi_device *dsi)
> > +{
> > + struct ch13726a_panel *ctx = mipi_dsi_get_drvdata(dsi);
> > + int ret;
> > +
> > + ret = mipi_dsi_detach(dsi);
> > + if (ret < 0)
> > + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
> > +
> > + drm_panel_remove(&ctx->panel);
> > +}
> > +
> > +static const struct of_device_id ch13726a_of_match[] = {
> > + { .compatible = "ayntec,thor-panel-bottom", .data = &thor_bottom_desc },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, ch13726a_of_match);
> > +
> > +static struct mipi_dsi_driver ch13726a_driver = {
> > + .probe = ch13726a_probe,
> > + .remove = ch13726a_remove,
> > + .driver = {
> > + .name = "panel-ch13726a-amoled",
> > + .of_match_table = ch13726a_of_match,
> > + },
> > +};
> > +module_mipi_dsi_driver(ch13726a_driver);
> > +
> > +MODULE_DESCRIPTION("DRM driver for CH13726A DSI panels");
> > +MODULE_LICENSE("GPL");
> >
>
Aaron
^ permalink raw reply
* [PATCH v2 0/5] Add interrupt controller for JHB100 SoC
From: Changhuang Liang @ 2026-04-16 6:47 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
Philipp Zabel
Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
Changhuang Liang
This patchset adds external interrupt controller driver for the StarFive
JHB100 SoC. It supports up to 64 interrupt sources, and both level and
edge trigger types.
changes since v1:
- irqchip: starfive -> irqchip/starfive
patch 1:
- Update commit title and add Conor's Acked-by tag
patch 3:
- Use __free(kfree) cleanup
- Replace dev_err() with dev_err_probe()
- Replace devm_reset_control_get_optional() + reset_control_deassert()
with devm_reset_control_get_optional_exclusive_deasserted()
patch 4:
- Use guard(raw_spinlock)
patch 5:
- Update starfive_intc_set_type()
v1: https://lore.kernel.org/all/20260410090106.622781-1-changhuang.liang@starfivetech.com/
Changhuang Liang (4):
dt-bindings: interrupt-controller: repurpose binding for unreleased
jh8100 for jhb100
irqchip/starfive: Rename jh8100 to jhb100
irqchip/starfive: Use devm_ interfaces to simplify resource release
irqchip/starfive: Implement irq_set_type() and irq_ack() callbacks
Mason Huo (1):
irqchip/starfive: Increase the interrupt source number up to 64
...00-intc.yaml => starfive,jhb100-intc.yaml} | 20 +-
MAINTAINERS | 6 +-
drivers/irqchip/Kconfig | 6 +-
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-starfive-jh8100-intc.c | 207 --------------
drivers/irqchip/irq-starfive-jhb100-intc.c | 254 ++++++++++++++++++
6 files changed, 265 insertions(+), 230 deletions(-)
rename Documentation/devicetree/bindings/interrupt-controller/{starfive,jh8100-intc.yaml => starfive,jhb100-intc.yaml} (68%)
delete mode 100644 drivers/irqchip/irq-starfive-jh8100-intc.c
create mode 100644 drivers/irqchip/irq-starfive-jhb100-intc.c
--
2.25.1
^ permalink raw reply
* [PATCH v2 5/5] irqchip/starfive: Implement irq_set_type() and irq_ack() callbacks
From: Changhuang Liang @ 2026-04-16 6:47 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
Philipp Zabel
Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
Changhuang Liang
In-Reply-To: <20260416064751.632138-1-changhuang.liang@starfivetech.com>
Add irq_set_type() callback to support configuring interrupt trigger types
(level high/low, edge rising/falling) for the JHB100 interrupt controller.
Also add irq_ack() callabck as required by handle_edge_irq().
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/irqchip/irq-starfive-jhb100-intc.c | 73 ++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/drivers/irqchip/irq-starfive-jhb100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c
index b3d86bd926ed..0d5914813afd 100644
--- a/drivers/irqchip/irq-starfive-jhb100-intc.c
+++ b/drivers/irqchip/irq-starfive-jhb100-intc.c
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
@@ -19,12 +20,20 @@
#include <linux/reset.h>
#include <linux/spinlock.h>
+#define STARFIVE_INTC_SRC_TYPE(n) (0x04 + ((n) * 0x20))
#define STARFIVE_INTC_SRC_CLEAR(n) (0x10 + ((n) * 0x20))
#define STARFIVE_INTC_SRC_MASK(n) (0x14 + ((n) * 0x20))
#define STARFIVE_INTC_SRC_INT(n) (0x1c + ((n) * 0x20))
+#define STARFIVE_INTC_TRIGGER_MASK 0x3
+#define STARFIVE_INTC_TRIGGER_HIGH 0
+#define STARFIVE_INTC_TRIGGER_LOW 1
+#define STARFIVE_INTC_TRIGGER_POSEDGE 2
+#define STARFIVE_INTC_TRIGGER_NEGEDGE 3
+
#define STARFIVE_INTC_NUM 2
#define STARFIVE_INTC_SRC_IRQ_NUM 32
+#define STARFIVE_INTC_TYPE_NUM 16
struct starfive_irq_chip {
void __iomem *base;
@@ -32,6 +41,16 @@ struct starfive_irq_chip {
raw_spinlock_t lock;
};
+static void starfive_intc_mod(struct starfive_irq_chip *irqc, u32 reg, u32 mask, u32 data)
+{
+ u32 value;
+
+ value = ioread32(irqc->base + reg) & ~mask;
+ data &= mask;
+ data |= value;
+ iowrite32(data, irqc->base + reg);
+}
+
static void starfive_intc_bit_set(struct starfive_irq_chip *irqc,
u32 reg, u32 bit_mask)
{
@@ -76,10 +95,64 @@ static void starfive_intc_mask(struct irq_data *d)
starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos));
}
+static void starfive_intc_ack(struct irq_data *d)
+{
+ /* for handle_edge_irq, nothing to do */
+}
+
+static int starfive_intc_set_type(struct irq_data *d, unsigned int type)
+{
+ struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
+ u32 i, bitpos, ty_pos, ty_shift, trigger, typeval;
+ irq_flow_handler_t handler;
+
+ i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
+ bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
+ ty_pos = bitpos / STARFIVE_INTC_TYPE_NUM;
+ ty_shift = (bitpos % STARFIVE_INTC_TYPE_NUM) * 2;
+
+ switch (type) {
+ case IRQF_TRIGGER_LOW:
+ trigger = STARFIVE_INTC_TRIGGER_LOW;
+ handler = handle_level_irq;
+ break;
+ case IRQF_TRIGGER_HIGH:
+ trigger = STARFIVE_INTC_TRIGGER_HIGH;
+ handler = handle_level_irq;
+ break;
+ case IRQF_TRIGGER_FALLING:
+ trigger = STARFIVE_INTC_TRIGGER_NEGEDGE;
+ handler = handle_edge_irq;
+ break;
+ case IRQF_TRIGGER_RISING:
+ trigger = STARFIVE_INTC_TRIGGER_POSEDGE;
+ handler = handle_edge_irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ irq_set_handler_locked(d, handler);
+ typeval = trigger << ty_shift;
+
+ guard(raw_spinlock)(&irqc->lock);
+
+ starfive_intc_mod(irqc, STARFIVE_INTC_SRC_TYPE(i) + 4 * ty_pos,
+ STARFIVE_INTC_TRIGGER_MASK << ty_shift, typeval);
+
+ /* Once the type is updated, clear interrupt can help to reset the type value */
+ starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos));
+ starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos));
+
+ return 0;
+}
+
static struct irq_chip intc_dev = {
.name = "StarFive JHB100 INTC",
.irq_unmask = starfive_intc_unmask,
.irq_mask = starfive_intc_mask,
+ .irq_ack = starfive_intc_ack,
+ .irq_set_type = starfive_intc_set_type,
};
static int starfive_intc_map(struct irq_domain *d, unsigned int irq,
--
2.25.1
^ permalink raw reply related
* [PATCH v2 4/5] irqchip/starfive: Increase the interrupt source number up to 64
From: Changhuang Liang @ 2026-04-16 6:47 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
Philipp Zabel
Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
Changhuang Liang
In-Reply-To: <20260416064751.632138-1-changhuang.liang@starfivetech.com>
From: Mason Huo <mason.huo@starfivetech.com>
StarFive JHB100 SoC interrupt controller actually supports 64 interrupt
sources, the original code only supported up to 32. now it is extended
to 64. Also use guard(raw_spinlock) to automatically release spinlocks.
Signed-off-by: Mason Huo <mason.huo@starfivetech.com>
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/irqchip/irq-starfive-jhb100-intc.c | 47 +++++++++++++---------
1 file changed, 29 insertions(+), 18 deletions(-)
diff --git a/drivers/irqchip/irq-starfive-jhb100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c
index c33229b39a40..b3d86bd926ed 100644
--- a/drivers/irqchip/irq-starfive-jhb100-intc.c
+++ b/drivers/irqchip/irq-starfive-jhb100-intc.c
@@ -19,10 +19,11 @@
#include <linux/reset.h>
#include <linux/spinlock.h>
-#define STARFIVE_INTC_SRC0_CLEAR 0x10
-#define STARFIVE_INTC_SRC0_MASK 0x14
-#define STARFIVE_INTC_SRC0_INT 0x1c
+#define STARFIVE_INTC_SRC_CLEAR(n) (0x10 + ((n) * 0x20))
+#define STARFIVE_INTC_SRC_MASK(n) (0x14 + ((n) * 0x20))
+#define STARFIVE_INTC_SRC_INT(n) (0x1c + ((n) * 0x20))
+#define STARFIVE_INTC_NUM 2
#define STARFIVE_INTC_SRC_IRQ_NUM 32
struct starfive_irq_chip {
@@ -54,19 +55,25 @@ static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc,
static void starfive_intc_unmask(struct irq_data *d)
{
struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
+ int i, bitpos;
- raw_spin_lock(&irqc->lock);
- starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq));
- raw_spin_unlock(&irqc->lock);
+ i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
+ bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
+
+ guard(raw_spinlock)(&irqc->lock);
+ starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos));
}
static void starfive_intc_mask(struct irq_data *d)
{
struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
+ int i, bitpos;
+
+ i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
+ bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
- raw_spin_lock(&irqc->lock);
- starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq));
- raw_spin_unlock(&irqc->lock);
+ guard(raw_spinlock)(&irqc->lock);
+ starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos));
}
static struct irq_chip intc_dev = {
@@ -98,16 +105,19 @@ static void starfive_intc_irq_handler(struct irq_desc *desc)
chained_irq_enter(chip, desc);
- value = ioread32(irqc->base + STARFIVE_INTC_SRC0_INT);
- while (value) {
- hwirq = ffs(value) - 1;
+ for (int i = 0; i < STARFIVE_INTC_NUM; i++) {
+ value = ioread32(irqc->base + STARFIVE_INTC_SRC_INT(i));
+ while (value) {
+ hwirq = ffs(value) - 1;
- generic_handle_domain_irq(irqc->domain, hwirq);
+ generic_handle_domain_irq(irqc->domain,
+ hwirq + i * STARFIVE_INTC_SRC_IRQ_NUM);
- starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq));
- starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq));
+ starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq));
+ starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq));
- __clear_bit(hwirq, &value);
+ __clear_bit(hwirq, &value);
+ }
}
chained_irq_exit(chip, desc);
@@ -140,7 +150,8 @@ static int starfive_intc_probe(struct platform_device *pdev, struct device_node
raw_spin_lock_init(&irqc->lock);
- irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), STARFIVE_INTC_SRC_IRQ_NUM,
+ irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc),
+ STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM,
&starfive_intc_domain_ops, irqc);
if (!irqc->domain)
return dev_err_probe(&pdev->dev, -EINVAL, "Unable to create IRQ domain\n");
@@ -155,7 +166,7 @@ static int starfive_intc_probe(struct platform_device *pdev, struct device_node
irqc);
dev_info(&pdev->dev, "Interrupt controller register, nr_irqs %d\n",
- STARFIVE_INTC_SRC_IRQ_NUM);
+ STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM);
retain_and_null_ptr(irqc);
return 0;
--
2.25.1
^ permalink raw reply related
* [PATCH v12 4/4] crypto: spacc - Add SPAcc Kconfig and Makefile
From: Pavitrakumar Managutte @ 2026-04-16 6:44 UTC (permalink / raw)
To: linux-crypto, linux-kernel, devicetree, herbert, robh
Cc: conor+dt, Ruud.Derwig, manjunath.hadli, adityak, navami.telsang,
bhoomikak, Pavitrakumar Managutte
In-Reply-To: <20260416064451.99886-1-pavitrakumarm@vayavyalabs.com>
Add Makefile and Kconfig for SPAcc driver.
Acked-by: Ruud Derwig <Ruud.Derwig@synopsys.com>
Signed-off-by: Pavitrakumar Managutte <pavitrakumarm@vayavyalabs.com>
---
drivers/crypto/Kconfig | 1 +
drivers/crypto/Makefile | 1 +
drivers/crypto/dwc-spacc/Kconfig | 88 +++++++++++++++++++++++++++++++
drivers/crypto/dwc-spacc/Makefile | 8 +++
4 files changed, 98 insertions(+)
create mode 100644 drivers/crypto/dwc-spacc/Kconfig
create mode 100644 drivers/crypto/dwc-spacc/Makefile
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 971f17a155435..2d10ef4321bc8 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -774,6 +774,7 @@ config CRYPTO_DEV_BCM_SPU
ahash, and aead algorithms with the kernel cryptographic API.
source "drivers/crypto/stm32/Kconfig"
+source "drivers/crypto/dwc-spacc/Kconfig"
config CRYPTO_DEV_SAFEXCEL
tristate "Inside Secure's SafeXcel cryptographic engine driver"
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 283bbc650b5b2..d106c1c729060 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/
obj-y += inside-secure/
obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/
obj-y += xilinx/
+obj-y += dwc-spacc/
obj-y += hisilicon/
obj-y += loongson/
obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/
diff --git a/drivers/crypto/dwc-spacc/Kconfig b/drivers/crypto/dwc-spacc/Kconfig
new file mode 100644
index 0000000000000..f9752e6f664b8
--- /dev/null
+++ b/drivers/crypto/dwc-spacc/Kconfig
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config CRYPTO_DEV_SPACC
+ tristate "Support for dwc_spacc Security Protocol Accelerator"
+ depends on HAS_DMA
+ select CRYPTO_ENGINE
+ default n
+
+ help
+ This enables support for SPAcc Hardware Accelerator.
+
+config CRYPTO_DEV_SPACC_HASH
+ bool "Enable HASH functionality"
+ depends on CRYPTO_DEV_SPACC
+ default y
+ select CRYPTO_HASH
+ select CRYPTO_SHA1
+ select CRYPTO_MD5
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ select CRYPTO_HMAC
+ select CRYPTO_SM3
+ select CRYPTO_CMAC
+ select CRYPTO_MICHAEL_MIC
+ select CRYPTO_XCBC
+ select CRYPTO_AES
+ select CRYPTO_SM4_GENERIC
+
+ help
+ Say y to enable Hash functionality of SPAcc.
+
+config CRYPTO_DEV_SPACC_AUTODETECT
+ bool "Enable Autodetect functionality"
+ depends on CRYPTO_DEV_SPACC
+ default y
+ help
+ Say y to enable Autodetect functionality of SPAcc.
+
+config CRYPTO_DEV_SPACC_DEBUG_TRACE_IO
+ bool "Enable Trace MMIO reads/writes stats"
+ depends on CRYPTO_DEV_SPACC
+ default n
+ help
+ Say y to enable Trace MMIO reads/writes stats.
+ To Debug and trace IO register read/write oprations.
+
+config CRYPTO_DEV_SPACC_DEBUG_TRACE_DDT
+ bool "Enable Trace DDT entries stats"
+ default n
+ depends on CRYPTO_DEV_SPACC
+ help
+ Say y to enable Enable DDT entry stats.
+ To Debug and trace DDT opration
+
+config CRYPTO_DEV_SPACC_SECURE_MODE
+ bool "Enable Spacc secure mode stats"
+ default n
+ depends on CRYPTO_DEV_SPACC
+ help
+ Say y to enable SPAcc secure modes stats.
+
+config CRYPTO_DEV_SPACC_PRIORITY
+ int "VSPACC priority value"
+ depends on CRYPTO_DEV_SPACC
+ range 0 15
+ default 1
+ help
+ Default arbitration priority weight for this Virtual SPAcc instance.
+ Hardware resets this to 1. Higher values means higher priority.
+
+config CRYPTO_DEV_SPACC_INTERNAL_COUNTER
+ int "SPAcc internal counter value"
+ depends on CRYPTO_DEV_SPACC
+ range 100000 1048575
+ default 100000
+ help
+ This value configures a hardware watchdog counter in the SPAcc engine.
+ The counter starts ticking when a completed cryptographic job is
+ sitting in the STATUS FIFO. If the job remains unprocessed for the
+ configured duration, an interrupt is triggered to ensure it is serviced.
+
+config CRYPTO_DEV_SPACC_CONFIG_DEBUG
+ bool "Enable SPAcc debug logs"
+ default n
+ depends on CRYPTO_DEV_SPACC
+ help
+ Say y to enable additional debug prints and diagnostics in the
+ SPAcc driver. Disable this for production builds.
diff --git a/drivers/crypto/dwc-spacc/Makefile b/drivers/crypto/dwc-spacc/Makefile
new file mode 100644
index 0000000000000..45d0166dfc8f7
--- /dev/null
+++ b/drivers/crypto/dwc-spacc/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CRYPTO_DEV_SPACC) += snps-spacc.o
+snps-spacc-objs = spacc_hal.o spacc_core.o \
+spacc_manager.o spacc_interrupt.o spacc_device.o
+
+ifeq ($(CONFIG_CRYPTO_DEV_SPACC_HASH),y)
+snps-spacc-objs += spacc_ahash.o
+endif
--
2.25.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox