* [PATCH v3 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller
From: Yuanshen Cao @ 2026-06-22 1:36 UTC (permalink / raw)
To: conor+dt, mripard, krzk+dt, robh, samuel, wens, jernej.skrabec,
Frank.Li, vkoul
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
devicetree, linux-kernel, Frank Li
Hi everyone,
This patch series introduces support for the Allwinner A733 DMA
controller in the `sun6i-dma` driver.
The A733 DMA controller differs from previous generations in several key
ways:
1. It supports higher address (up to 32G).
2. It uses a different interrupt register layout and mapping.
3. It has a different number of channels per interrupt register.
To support these differences without introducing complex conditional
logic throughout the driver, this series first refactors the
`sun6i_dma_config` structure. By moving interrupt handling, register
dumping, and address configuration into function pointers within the
configuration structure. This allows the driver to support the A733
and future hardware revisions. It also aligns with the DMA drivers in
Radxa BSP Package[1].
The series is organized as follows:
- Refactors the configuration structure to include function pointers for
interrupt and register operations.
- Moves address setting logic into the configuration structure to handle
varying address widths.
- Adds support for variable channels per interrupt register.
- Updates the device tree bindings documentation.
- Implements the A733-specific configuration and register mappings.
Tested on Radxa Cubie A7Z.
[1] https://github.com/radxa/allwinner-bsp/blob/cubie-aiot-v1.4.8/drivers/dma/sunxi-dma.c
Thanks!
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
Changes in v3:
- Reword patches for clarity.
- Link to v2: https://patch.msgid.link/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com
---
Changes in v2:
- Implement SUN6I_DMA_IRQ_A31_COMMON_OPS macro to avoid duplicate.
- Move set_addr into helper function and revert back sun6i_dma_set_addr.
- Rename chan_num to irq_req to avoid misleading name as suggested by
sashiko.
- Reorder and reword the dtbinding patch for more clarity.
- Link to v1: https://patch.msgid.link/20260619-sun60i-a733-dma-v1-0-da4b649fc72a@gmail.com
---
Yuanshen Cao (5):
dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller
.../bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 +
drivers/dma/sun6i-dma.c | 197 +++++++++++++++++++--
2 files changed, 181 insertions(+), 18 deletions(-)
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260619-sun60i-a733-dma-c2455149165d
Best regards,
--
Yuanshen Cao <alex.caoys@gmail.com>
^ permalink raw reply
* Re: [PATCH 0/4] iio: adc: new ti-ads112c14 driver
From: Kurt Borja @ 2026-06-22 0:32 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner
Cc: Kurt Borja, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Nguyen Minh Tien, linux-iio,
devicetree, linux-kernel
In-Reply-To: <20260621201412.0ce54fa8@jic23-huawei>
On Sun Jun 21, 2026 at 2:14 PM -05, Jonathan Cameron wrote:
> On Tue, 16 Jun 2026 13:16:46 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
>> On 6/16/26 12:26 PM, Kurt Borja wrote:
>> > On Tue Jun 16, 2026 at 10:21 AM -05, David Lechner wrote:
>> >> On 6/15/26 7:18 PM, Kurt Borja wrote:
>> >>> On Mon Jun 15, 2026 at 4:59 PM -05, David Lechner (TI) wrote:
>>
>> ...
>>
>> >>>> All of these chips have in common that they are designed for use with
>> >>>> RTDs and thermocouples and so they look very similar to each other in
>> >>>> terms of wiring and feature set, even if the register maps are
>> >>>> different. They are in the gray area where we could either keep them
>> >>>> separate because they are just different enough, or we could do like
>> >>>> we've done before with ad_sigma_delta and have a bit of an abstraction
>> >>>> layer for the register differences and otherwise try to share as much
>> >>>> code as possible. Normally, I would lean towards keeping them separate,
>> >>>> but in this case, I'm considering trying to share code because the
>> >>>> devicetree bindings for the inputs is complex and is going to be mostly
>> >>>> the same across all of these chips.
>> >>>
>> >>> The channel configuration is indeed very similar for the three chips.
>> >>> All three have IDAC, BOC and VREF configurations.
>> >>
>> >> Hmm... I forgot to include the burnout current in the DT bindings. Following
>> >> the channel = "conditions for measurement" pattern that I have set out here
>> >> I guess that would mean that we would need to have the same inputs twice
>> >> when using the burnout. One "channel" would be the one used to do a "precision"
>> >> measurement and the other would be the one to do open/short circuit detection.
>> >>
>> >>
>> >> i2c {
>> >> #address-cells = <1>;
>> >> #size-cells = <0>;
>> >>
>> >> adc@40 {
>> >> compatible = "ti,ads112c14";
>> >> reg = <0x40>;
>> >>
>> >> avdd-supply = <&avdd>;
>> >> dvdd-supply = <&dvdd>;
>> >>
>> >> refp-supply = <&avdd>;
>> >>
>> >> #address-cells = <1>;
>> >> #size-cells = <0>;
>> >>
>> >> channel@0 {
>> >> reg = <0>;
>> >> diff-channels = <1>, <2>;
>> >> excitation-channels = <0>, <3>;
>> >> excitation-current-microamp = <500>;
>> >> current-chopping;
>> >> ti,vref-source = <ADS112C14_VREF_SOURCE_EXTERNAL>;
>> >> label = "rtd-precision";
>> >> };
>> >>
>> >> channel@1 {
>> >> reg = <0>;
>> >> diff-channels = <1>, <2>;
>> >> excitation-channels = <0>, <3>;
>> >> excitation-current-microamp = <500>;
> Maybe use an example with more stuff changing? Do we want same excitation
> for burn out? I've no idea.
>
>> >> burnout-current-nanoamp = <1000>;
>> >> ti,vref-source = <ADS112C14_VREF_SOURCE_EXTERNAL>;
>> >> label = "rtd-diagnostic";
>> >> };
>> >
>> > This would mean we wouldn't be able to use iio_chan_spec .channel and
>> > .channel2 to describe inputs because of duplicate sysfs attributes, no?
>> >
>>
>> Yes, that is a bit unfortunate. At least there the labels to tell them
>> apart. I guess we would just need to use consecutive channel and channel2
>> when dynamically allocating the channels to avoid conflict.
>
> From a very initial look, maybe do something similar to the folk have
> been looking at for the more complex DDS devices where we have lots
> of channels that are on the same 'wires'. Basically add a numbering
> scheme to keep them reasonably separate - channel numbers are cheap.
> Maybe first channel is 10->1f, second 20-2f etc. They are differential
> so it will get ugly. Perhaps have a play around and see if there is
> a reasonable channel naming scheme for this 'same inputs, different thing
> being measured case'
May I also suggest having some sort of IIO_VOLTAGE_DIAGNOSTIC channel
type? Would that be worth the trouble?
We could also maybe just drop burn-out current completely from
dt-bindings and add IIO_CHAN_INFO_BURNOUT_CURRENT. Given that this
feature is only used ocasionally for diagnostic purposes (I assume...).
>
> I'm not yet sure I'm convinced that a separate channel model makes sense.
> Even less in the DT given these are different settings for one channel.
> That doesn't mean we don't split them up in the driver if channels
> are the best implementation / ABI to userspace.
>
> Basically nothing actually says diff-channels numbers match the
> userspace ABI, so break that link.
>
>>
>> >>>> This makes things more flexible, but does make the driver a bit more
>> >>>> complex. For example, knowing when the current output needs to be
>> >>>> enabled or disabled. For now, I have chosen a lazy-enable where they
>> >>>> are not turned on until the first measurement is taken that requires
>> >>>> them, but then they stay on until another measurement is taken that
>> >>>> doesn't require them. This can lead to some oddness with the diagnostic
>> >>>> channels that may be measuring something that indirectly requires the
>> >>>> current output (i.e. the external reference voltage when it is connected
>> >>>> to a resistor rather than a power supply). This means you need to take
>> >>>> a measurement that requires the current output to be enabled before the
>> >>>> diagnostic channels will give accurate readings.
>> >>>
>> >>> This is the same approach I took around the BOC, it feels kinda hacky
>> >>> but it makes sense. Just an idea I thought about just now: What if we
>> >>> have an additional write-only "_enable" sysfs attribute for these
>> >>> channels?
>> >>
>> >> I would not want to make a write-only attribute, we always want to be
>> >> able to read back what the current state is.
>> >
>> > Yeah, I don't know why I said WO. Reading would be fine too.
>> >
>> >>
>> >> Do you mean an _enable for just the BOC? I think I would do it like I
>> >> suggested above instead.
>> >
>> > No, no just the BOC. The BOC, IDAC and rest of side effects. Thinking
>> > about it some more, it would be a bit redundant but clearer if proper
>> > documentation is provided.
>> >
>> I would be interested to see what Jonathan has to say about this too.
>> Generally, his advice has been to avoid attributes that power things
>> on and off if we can help it.
>>
> This is again a bit similar to the DDS case, but there we have more than
> one on at a time (as controlling different parts of the signal - freq
> / phase / amplitude). Here we at least avoid that complexity.
>
> I didn't really like that solution but we didn't come up with any other
> way to support the complex stuff going on. Hence I'm not necessarily
> suggesting to go that way here.
>
> I think this may be a case of laying out different options in an ABI
> doc then reviewing that to make sure we spot corner cases etc.
>
> Thanks,
>
> Jonathan
>
> p.s. Sometimes it feels like the world just keeps getting more complex!
--
Thanks,
~ Kurt
^ permalink raw reply
* Re: [PATCH 2/5] iio: adc: Add ti-ads1262 driver
From: Kurt Borja @ 2026-06-22 0:18 UTC (permalink / raw)
To: Jonathan Cameron, Krzysztof Kozlowski
Cc: Kurt Borja, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Bartosz Golaszewski, David Lechner, Nuno Sá,
Andy Shevchenko, linux-iio, devicetree, linux-kernel, linux-gpio
In-Reply-To: <20260621153318.4a723e3b@jic23-huawei>
On Sun Jun 21, 2026 at 9:33 AM -05, Jonathan Cameron wrote:
> On Mon, 15 Jun 2026 06:30:28 +0200
> Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
>> On 14/06/2026 22:56, Kurt Borja wrote:
>> > On Sat Jun 13, 2026 at 1:59 PM -05, Krzysztof Kozlowski wrote:
>> >
>> > [...]
>> >
>> >> Functions used by probe() should be before probe(), not somewhere in the
>> >> middle of the code. IOW, entire probe is together.
>> >
>> > I they all are, it's just that regmap stuff takes a huge chunk. I'll
>> > check how to reorganize.
>> >
>> > [...]
>> >
>> >>> +static const struct of_device_id ads1262_of_match[] = {
>> >>> + { .compatible = "ti,ads1262" },
>> >>> + { .compatible = "ti,ads1263" },
>> >>
>> >> So devices are fully compatible? Then it should be expressed in the
>> >> binding and drop one entry here.
>> >
>> > Not fully compatible as Jonathan said. One is a subset of the other.
>>
>> This is THE meaning of compatible!
>
> This one I'm in agreement with. It is a strict subset, so should be
> using a fallback. If the fallback is used, you just get support of the
> stuff in the simpler chip (or if you can override it with a chip ID
> you might still 'upgrade' to the more complex driver support).
> If you do end up with properties that only apply to 'new' parts of
> the more complex chip then they should be verified as part of the
> binding (assuming you can do that without the verifier complaining
> - I haven't checked!)
In v1 I had the "adc" subnode which was specific to ADS1263. Then I
agreed to drop the subnode but I'm having second thoughts...
If we dropped it, then we would still have some specific stuff.
#io-channel-cells would be "const: 2" in ADS1263 chips. Also ADS1263's
channels would have an extra ti,vref-adc2 prop, for ADC2 voltage
reference selection. I should maybe also add a vref-adc2-supply.
Maybe it's better to keep the subnode or, again, go for something like:
spi {
multi-adc@0 {
adc@0 {
...
vref-suppy = <&adc1-vref>;
channel@0 {
...
reference-source = <ADS1262_VREF_AIN0_AIN1>;
};
};
adc@1 {
...
vref-suppy = <&adc2-vref>;
channel@0 {
...
reference-source = <ADS1262_VREF_AIN2_AIN3>;
};
};
};
};
In this case we would have to kinda duplicate channel description, but I
don't think it's that bad.
Jonathan, Krzysztof, David, thoughts?
IMO the ADC2 specific voltage reference stuff is a strong argument for a
subnode or the above solution.
>
> The SLF3F discussion is about (to me) less obvious case of not a strict
> subset, but rather being detectable parts with different channel related
> properties. In that case the ID match is necessary for anything to work.
> Anyhow, that discussion is in a different thread and not really relevant
> here.
>
> Jonathan
>
>>
>>
>> Best regards,
>> Krzysztof
--
Thanks,
~ Kurt
^ permalink raw reply
* Re: [PATCH v2 2/2] iio: temperature: Add STS30 temperature sensor driver
From: Maxwell Doose @ 2026-06-22 0:09 UTC (permalink / raw)
To: Joshua Crofts
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
open list:IIO SUBSYSTEM AND DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <CAKqfh0HUih0q9PEh+5pwwMuBBkm6nsaZP9e2yPjHO3+Yccuvgw@mail.gmail.com>
On Sun, Jun 21, 2026 at 7:05 PM Maxwell Doose <m32285159@gmail.com> wrote:
>
> On Sun, Jun 21, 2026 at 1:33 PM Joshua Crofts <joshua.crofts1@gmail.com> wrote:
> >
> > On Sat, 20 Jun 2026 19:46:24 -0500
> > Maxwell Doose <m32285159@gmail.com> wrote:
> > > +#include <linux/array_size.h>
> > > +#include <linux/bits.h>
> > > +#include <linux/cleanup.h>
> > > +#include <linux/crc8.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/device.h>
> > > +#include <linux/errno.h>
> > > +#include <linux/export.h>
> > > +#include <linux/i2c.h>
> >
> > I am a numpty as I also forgot to mention a missing
> > mod_devicetable.h header.
> >
>
> D'oh, I ought to look at my includes a lot more closely as well.
Forgot to mention, I'll be away for 2 weeks starting tomorrow so it'll
have to wait :(
^ permalink raw reply
* Re: [PATCH v2 2/2] iio: temperature: Add STS30 temperature sensor driver
From: Maxwell Doose @ 2026-06-22 0:05 UTC (permalink / raw)
To: Joshua Crofts
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
open list:IIO SUBSYSTEM AND DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <20260621203303.0f8c5d08@systembl0wer>
On Sun, Jun 21, 2026 at 1:33 PM Joshua Crofts <joshua.crofts1@gmail.com> wrote:
>
> On Sat, 20 Jun 2026 19:46:24 -0500
> Maxwell Doose <m32285159@gmail.com> wrote:
> > +#include <linux/array_size.h>
> > +#include <linux/bits.h>
> > +#include <linux/cleanup.h>
> > +#include <linux/crc8.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/errno.h>
> > +#include <linux/export.h>
> > +#include <linux/i2c.h>
>
> I am a numpty as I also forgot to mention a missing
> mod_devicetable.h header.
>
D'oh, I ought to look at my includes a lot more closely as well.
^ permalink raw reply
* Re: [PATCH RFC 3/3] dt-bindings: iio: adc: Add burn-out current properties
From: Kurt Borja @ 2026-06-21 23:43 UTC (permalink / raw)
To: David Lechner, Kurt Borja, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel
In-Reply-To: <38c5e5eb-2113-4a5c-ad14-348dc7966a1c@baylibre.com>
Hi David,
On Sat Jun 20, 2026 at 10:57 AM -05, David Lechner wrote:
> On 6/19/26 11:46 AM, Kurt Borja wrote:
>> On Fri Jun 19, 2026 at 9:28 AM -05, David Lechner wrote:
>>> On 6/18/26 7:33 PM, Kurt Borja wrote:
>>>> Some ADCs incorporate burn-out current sources that provide current to
>>>> the channel's input pins for open-circuit or short-circuit detection.
>>>>
>>>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>>>> ---
>>>> Documentation/devicetree/bindings/iio/adc/adc.yaml | 14 ++++++++++++++
>>>> 1 file changed, 14 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/iio/adc/adc.yaml b/Documentation/devicetree/bindings/iio/adc/adc.yaml
>>>> index 106b1e317411d5..6b63aac9ac04dd 100644
>>>> --- a/Documentation/devicetree/bindings/iio/adc/adc.yaml
>>>> +++ b/Documentation/devicetree/bindings/iio/adc/adc.yaml
>>>> @@ -106,6 +106,20 @@ properties:
>>>> This array describes the current configuration of the excitation current
>>>> sources or the single matched current for all sources.
>>>>
>>>> + burn-out-current-microamp:
>>>> + description:
>>>> + Burn-out current sources provide current to the channel's input pins for
>>>> + open-circuit or short-circuit detection.
>>>> +
>>>> + burn-out-current-polarity:
>>>> + $ref: /schemas/types.yaml#/definitions/string
>>>> + description:
>>>> + Burn-out current sources provide current to the channel's input pins for
>>>> + open-circuit or short-circuit detection.
>>>> + enum:
>>>> + - pull-up
>>>> + - pull-down
>>>> +
>>>> anyOf:
>>>> - oneOf:
>>>> - required:
>>>>
>>>
>>> This doesn't really work with chips that just have and enable bit to
>>> enable or disable the feature.
>>
>> Maybe those devices can use burn-out-current-microamp with a const
>> value? Is that okay or should another approach be taken?
>>
>
> Maybe better to just leave this one out of the common file?
IMO burn-out-current-microamp would be nice to have. I don't know how
common it is for bindings to describe this kind of properties with a
single const value though.
On the other hand I do think we should drop burn-out-current-polarity,
maybe it's too specific.
--
Thanks,
~ Kurt
^ permalink raw reply
* Re: [PATCH v2 3/4] irqchip/gic-v3: Add Renesas R-Car Gen4 erratum workaround
From: Marek Vasut @ 2026-06-21 22:46 UTC (permalink / raw)
To: Thomas Gleixner, linux-pci
Cc: Yoshihiro Shimoda, Krzysztof Wilczyński, Bjorn Helgaas,
Catalin Marinas, Conor Dooley, Geert Uytterhoeven,
Krzysztof Kozlowski, Lorenzo Pieralisi, Manivannan Sadhasivam,
Marc Zyngier, Rob Herring, devicetree, linux-arm-kernel,
linux-doc, linux-kernel, linux-renesas-soc
In-Reply-To: <87cxxkma5p.ffs@fw13>
On 6/21/26 12:59 PM, Thomas Gleixner wrote:
> On Fri, Jun 19 2026 at 00:02, Marek Vasut wrote:
>> Renesas R-Car S4/V4H/V4M GIC600 integration has address width for AXI
>> or APB interface configured to 32 bit, it can therefore access only
>> the first 4 GiB of physical address space. This information comes from
>> R-Car V4H Interface Specification sheet, there is currently no technical
>> update number assigned to this limitation. Further input from hardware
>> engineer indicates that this limitation also applies to R-Car S4 and V4M.
>> Name the limitation GEN4GICITS1, and add a driver quirk to mitigate this
>> limitation.
>>
>> The quirk is keyed on the combination of the GIC implementation
>> and the platform identification in the device tree.
>>
>> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
>> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
>
> This SOB chain is broken.
Broken ? I don't understand , could you please elaborate ?
Should I sort this alphabetically or is that something else ?
Thank you for your help !
^ permalink raw reply
* RE: [PATCH v5 2/2] leds: ltc3208: Add driver for LTC3208 Multidisplay LED Driver
From: Roleda, Jan carlo @ 2026-06-21 22:37 UTC (permalink / raw)
To: Uwe Kleine König
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-kernel@vger.kernel.org,
linux-leds@vger.kernel.org, devicetree@vger.kernel.org
In-Reply-To: <ajVHk3__YAhZX5ao@monoceros>
Hello Kleine-Konig,
> -----Original Message-----
> From: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
> Sent: Friday, June 19, 2026 9:49 PM
> To: Roleda, Jan carlo <Jancarlo.Roleda@analog.com>
> Cc: Lee Jones <lee@kernel.org>; Pavel Machek <pavel@kernel.org>; Rob
> Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor
> Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-
> leds@vger.kernel.org; devicetree@vger.kernel.org
> Subject: Re: [PATCH v5 2/2] leds: ltc3208: Add driver for LTC3208 Multidisplay
> LED Driver
>
> [External]
>
> On Fri, Jun 19, 2026 at 06:45:09AM +0800, Jan Carlo Roleda wrote:
> > [...]
> > +static int ltc3208_probe(struct i2c_client *client) {
> > + enum ltc3208_aux_channel
> aux_channels[LTC3208_NUM_AUX_LEDS];
> > + struct ltc3208 *ddata;
> > + struct regmap *regmap;
> > + bool disable_rgb_aux4_dropout_signal;
> > + bool disable_camhl_pin;
> > + bool set_sub_control_pin;
> > + int ret;
> > + u8 reg_val;
> > +
> > + regmap = devm_regmap_init_i2c(client, <c3208_regmap_cfg);
> > + if (IS_ERR(regmap))
> > + return dev_err_probe(&client->dev, PTR_ERR(regmap),
> > + "Failed to initialize regmap\n");
> > +
> > + ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL);
> > + if (!ddata)
> > + return -ENOMEM;
> > +
> > + ddata->regmap = regmap;
> > +
> > + disable_camhl_pin = device_property_read_bool(&client->dev,
> > + "adi,disable-camhl-pin");
> > + set_sub_control_pin =
> > + device_property_read_bool(&client->dev, "adi,cfg-enrgbs-
> pin");
> > + disable_rgb_aux4_dropout_signal = device_property_read_bool(
> > + &client->dev, "adi,disable-rgb-aux4-dropout");
>
> Unusual line break. I'd write that as:
>
> disable_rgb_aux4_dropout_signal =
> device_property_read_bool(&client->dev,
> "adi,disable-rgb-aux4-dropout");
>
>
This is noted. I will rewrite this part accordingly.
> > +
> > + reg_val = FIELD_PREP(LTC3208_OPT_EN_RGBS, set_sub_control_pin) |
> > + FIELD_PREP(LTC3208_OPT_DIS_CAMHILO,
> disable_camhl_pin) |
> > + FIELD_PREP(LTC3208_OPT_DIS_RGBDROP,
> > + disable_rgb_aux4_dropout_signal);
> > +
> > + ret = regmap_write(regmap, LTC3208_REG_G_OPT, reg_val);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "error writing to options register\n");
> > +
> > + /* Initialize aux channel configurations */
> > + for (int i = 0; i < LTC3208_NUM_AUX_LEDS; i++) {
> > + ret = device_property_match_property_string(
> > + &client->dev, ltc3208_dt_aux_channels[i],
> > + ltc3208_aux_opt, LTC3208_NUM_AUX_OPT);
> > + /* Fallback to default value (AUX) if not found */
> > + if (ret == -EINVAL)
> > + aux_channels[i] = LTC3208_AUX_CHAN_AUX;
> > + else if (ret >= 0)
> > + aux_channels[i] = ret;
> > + }
> > +
> > + reg_val = FIELD_PREP(LTC3208_AUX1_MASK, aux_channels[0]) |
> > + FIELD_PREP(LTC3208_AUX2_MASK, aux_channels[1]) |
> > + FIELD_PREP(LTC3208_AUX3_MASK, aux_channels[2]) |
> > + FIELD_PREP(LTC3208_AUX4_MASK, aux_channels[3]);
> > +
> > + ret = regmap_write(regmap, LTC3208_REG_E_AUX_SELECT, reg_val);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "error writing to aux channel register.\n");
> > +
> > + i2c_set_clientdata(client, ddata);
>
> From a quick glance, this is unused.
>
You're right, I'll remove this then.
> > + device_for_each_child_node_scoped(&client->dev, child) {
> > + struct ltc3208_led *led;
> > + struct led_init_data init_data = {};
> > + u32 chan;
> > +
> > + ret = fwnode_property_read_u32(child, "reg", &chan);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Failed to get reg value of LED\n");
> > + else if (chan >= LTC3208_NUM_LED_GRPS)
> > + return dev_err_probe(&client->dev, ret,
> > + "%d is an invalid LED ID\n", chan);
> > +
> > + led = &ddata->leds[chan];
> > +
> > + led->rfield =
> > + devm_regmap_field_alloc(&client->dev, ddata-
> >regmap,
> > + ltc3208_led_reg_field[chan]);
> > + if (IS_ERR(led->rfield))
> > + return dev_err_probe(&client->dev, PTR_ERR(led-
> >rfield),
> > + "cannot allocate regmap field\n");
> > + led->client = client;
> > + led->channel = chan;
> > + led->cdev.brightness_set_blocking =
> ltc3208_led_set_brightness;
> > + led->cdev.max_brightness =
> LTC3208_MAX_BRIGHTNESS_4BIT;
> > +
> > + if (chan == LTC3208_CHAN_MAIN || chan ==
> LTC3208_CHAN_SUB)
> > + led->cdev.max_brightness =
> LTC3208_MAX_BRIGHTNESS_8BIT;
> > +
> > + init_data.fwnode = child;
> > +
> > + ret = devm_led_classdev_register_ext(&client->dev, &led-
> >cdev,
> > + &init_data);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "LED %u Register failed.\n", chan);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id ltc3208_match_table[] = {
> > + {.compatible = "adi,ltc3208"},
> > + {}
> > +};
>
> Please make this:
>
> static const struct of_device_id ltc3208_match_table[] = {
> { .compatible = "adi,ltc3208" },
> { }
> };
>
>
> > +MODULE_DEVICE_TABLE(of, ltc3208_match_table);
> > +
> > +static const struct i2c_device_id ltc3208_idtable[] = {
> > + { "ltc3208" },
> > + {}
> > +};
>
> Please make this:
>
> static const struct i2c_device_id ltc3208_idtable[] = {
> { .name = "ltc3208" },
> { }
> };
>
Noted. Thank you for the clarification on the spacing.
> > +MODULE_DEVICE_TABLE(i2c, ltc3208_idtable);
>
> Best regards
> Uwe
I will submit a new patch with these suggestions and those identified by Sashiko by June 24.
Thank you for the review!
Regards,
Carlo
^ permalink raw reply
* Re: [PATCH v2 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
From: Frank Li @ 2026-06-21 22:22 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-5-340f205891cc@gmail.com>
On Sun, Jun 21, 2026 at 09:40:58PM +0000, Yuanshen Cao wrote:
> This patch implements the actual support for the Allwinner A733 DMA
Avoid use words "this patch/commit",
Support Allwinner A733 DMA ...
Frank
> controller. It defines the new register offsets and bitfield mappings
> required for the A733, which slightly differs from the older `sun6i`
> series.
>
> Changes:
> - New register macros for A733 interrupt enable `DMA_IRQ_EN_A733` and
> status `DMA_IRQ_STAT_A733`.
> - New `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` macro to handle the
> 32G high-address field in the LLI.
> - Implemented `sun6i_dma_set_addr_a733` and A733-specific interrupt
> register accessors.
> - Added `sun60i_a733_dma_config`, which ties all the refactored
> functionality together for this specific hardware.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
> drivers/dma/sun6i-dma.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 87 insertions(+)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index 196a0d73b221..4808015934cc 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -52,6 +52,15 @@
> #define SUNXI_H3_SECURE_REG 0x20
> #define SUNXI_H3_DMA_GATE 0x28
> #define SUNXI_H3_DMA_GATE_ENABLE 0x4
> +
> +/*
> + * sun60i specific registers
> + */
> +#define DMA_IRQ_EN_A733(x) ((x) * 0x40 + 0x134)
> +#define DMA_IRQ_STAT_A733(x) ((x) * 0x40 + 0x138)
> +
> +#define DMA_IRQ_CHAN_NR_A733 1
> +
> /*
> * Channels specific registers
> */
> @@ -100,6 +109,8 @@
> */
> #define SRC_HIGH_ADDR(x) (((x) & 0x3U) << 16)
> #define DST_HIGH_ADDR(x) (((x) & 0x3U) << 18)
> +#define SRC_HIGH_ADDR_32G(x) (((x) & 0x7U) << 11)
> +#define DST_HIGH_ADDR_32G(x) (((x) & 0x7U) << 15)
>
> /*
> * Various hardware related defines
> @@ -257,6 +268,23 @@ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
> DMA_STAT, readl(sdev->base + DMA_STAT));
> }
>
> +static inline void sun6i_dma_dump_com_regs_a733(struct sun6i_dma_dev *sdev)
> +{
> + int i;
> +
> + for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
> + dev_dbg(sdev->slave.dev, "Common register:\n"
> + "chan num %d\n"
> + "\tmask(%04x): 0x%08x\n"
> + "\tpend(%04x): 0x%08x\n"
> + "\tstats(%04x): 0x%08x\n",
> + i,
> + DMA_IRQ_EN_A733(i), readl(sdev->base + DMA_IRQ_EN_A733(i)),
> + DMA_IRQ_STAT_A733(i), readl(sdev->base + DMA_IRQ_STAT_A733(i)),
> + DMA_STAT, readl(sdev->base + DMA_STAT));
> + }
> +}
> +
> static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
> struct sun6i_pchan *pchan)
> {
> @@ -360,21 +388,41 @@ static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg)
> return readl(sdev->base + DMA_IRQ_EN(irq_reg));
> }
>
> +static u32 sun6i_read_irq_en_a733(struct sun6i_dma_dev *sdev, u32 irq_reg)
> +{
> + return readl(sdev->base + DMA_IRQ_EN_A733(irq_reg));
> +}
> +
> static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
> {
> writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
> }
>
> +static void sun6i_write_irq_en_a733(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
> +{
> + writel(irq_val, sdev->base + DMA_IRQ_EN_A733(irq_reg));
> +}
> +
> static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg)
> {
> return readl(sdev->base + DMA_IRQ_STAT(irq_reg));
> }
>
> +static u32 sun6i_read_irq_stat_a733(struct sun6i_dma_dev *sdev, u32 irq_reg)
> +{
> + return readl(sdev->base + DMA_IRQ_STAT_A733(irq_reg));
> +}
> +
> static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
> {
> writel(status, sdev->base + DMA_IRQ_STAT(irq_reg));
> }
>
> +static void sun6i_write_irq_stat_a733(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
> +{
> + writel(status, sdev->base + DMA_IRQ_STAT_A733(irq_reg));
> +}
> +
> static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
> {
> struct sun6i_desc *txd = pchan->desc;
> @@ -695,6 +743,17 @@ static void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
> DST_HIGH_ADDR(upper_32_bits(dst));
> }
>
> +static void sun6i_dma_set_addr_a733(struct sun6i_dma_dev *sdev,
> + struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst)
> +{
> + v_lli->src = lower_32_bits(src);
> + v_lli->dst = lower_32_bits(dst);
> +
> + v_lli->para |= SRC_HIGH_ADDR_32G(upper_32_bits(src)) |
> + DST_HIGH_ADDR_32G(upper_32_bits(dst));
> +}
> +
> static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
> struct sun6i_dma_lli *v_lli,
> dma_addr_t src, dma_addr_t dst)
> @@ -1339,6 +1398,33 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> +/*
> + * The A733 binding uses the number of dma channels from the
> + * device tree node.
> + */
> +static struct sun6i_dma_config sun60i_a733_dma_cfg = {
> + .clock_autogate_enable = sun6i_enable_clock_autogate_h3,
> + .set_burst_length = sun6i_set_burst_length_h3,
> + .set_drq = sun6i_set_drq_h6,
> + .set_mode = sun6i_set_mode_h6,
> + .set_addr = sun6i_dma_set_addr_a733,
> + .dump_com_regs = sun6i_dma_dump_com_regs_a733,
> + .read_irq_en = sun6i_read_irq_en_a733,
> + .write_irq_en = sun6i_write_irq_en_a733,
> + .read_irq_stat = sun6i_read_irq_stat_a733,
> + .write_irq_stat = sun6i_write_irq_stat_a733,
> + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR_A733,
> + .has_mbus_clk = true,
> +};
> +
> /*
> * The V3s have only 8 physical channels, a maximum DRQ port id of 23,
> * and a total of 24 usable source and destination endpoints.
> @@ -1375,6 +1461,7 @@ static const struct of_device_id sun6i_dma_match[] = {
> { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
> { .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg },
> { .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg },
> + { .compatible = "allwinner,sun60i-a733-dma", .data = &sun60i_a733_dma_cfg },
> { /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(of, sun6i_dma_match);
>
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v2 4/5] dt-bindings: dma: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
From: Frank Li @ 2026-06-21 22:19 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-4-340f205891cc@gmail.com>
On Sun, Jun 21, 2026 at 09:40:57PM +0000, Yuanshen Cao wrote:
subject dt-bindings: dmaengine: ....
> Add `allwinner,sun60i-a733-dma` to the list of compatible strings for the
> `sun50i-a64-dma` dtbinding documentation.
>
> While the A733 DMA controller shares many similarities with the sun50i-a64
> DMA controller, it requires a specific configuration due to differences in:
> - Interrupt register layout and mapping.
> - Number of channels per interrupt register.
> - Support for higher (32G) address widths in LLI parameters.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
After fix subject tags,
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
> index c3e14eb6cfff..1cc3304b7414 100644
> --- a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
> +++ b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
> @@ -25,6 +25,7 @@ properties:
> - allwinner,sun50i-a64-dma
> - allwinner,sun50i-a100-dma
> - allwinner,sun50i-h6-dma
> + - allwinner,sun60i-a733-dma
> - items:
> - const: allwinner,sun8i-r40-dma
> - const: allwinner,sun50i-a64-dma
> @@ -70,6 +71,7 @@ if:
> - allwinner,sun20i-d1-dma
> - allwinner,sun50i-a100-dma
> - allwinner,sun50i-h6-dma
> + - allwinner,sun60i-a733-dma
>
> then:
> properties:
>
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v2 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
From: Frank Li @ 2026-06-21 22:18 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-3-340f205891cc@gmail.com>
On Sun, Jun 21, 2026 at 09:40:56PM +0000, Yuanshen Cao wrote:
> The previous implementation of `sun6i-dma` had some implicit assumptions
> about the number of channels per interrupt register. Specifically,
> functions like `sun6i_kill_tasklet` were hardcoded to only disable
> interrupts for IRQ 0 and 1. `DMA_MAX_CHANNELS` is also not in used in
> the past, and the old SoCs never has more than 16 channels.
>
> The A733 has a different interrupt structure where the number of
> channels per register may differ. This patch introduces
> `num_channels_per_reg` to the `sun6i_dma_config`, similar to BSP, to
> make the interrupt handling logic hardware-agnostic. It also sets
> `DMA_MAX_CHANNELS` to 16 to align with the new BSP code and ensure loops
> over interrupts are correctly bounded.
>
> Changes:
> - Change `DMA_MAX_CHANNELS` definition to 16.
> - Added `num_channels_per_reg` to `struct sun6i_dma_config`.
> - Replaced hardcoded IRQ register calculations with values from
> `sdev->cfg->num_channels_per_reg`.
> - Updated `sun6i_kill_tasklet` to loop through all possible interrupt
> registers based on `DMA_MAX_CHANNELS` and the configuration.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/sun6i-dma.c | 25 ++++++++++++++++++-------
> 1 file changed, 18 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index 9984b9033cbb..196a0d73b221 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -41,7 +41,7 @@
> #define DMA_STAT 0x30
>
> /* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */
> -#define DMA_MAX_CHANNELS (DMA_IRQ_CHAN_NR * 0x10 / 4)
> +#define DMA_MAX_CHANNELS 16
>
> /*
> * sun8i specific registers
> @@ -151,6 +151,7 @@ struct sun6i_dma_config {
> u32 src_addr_widths;
> u32 dst_addr_widths;
> bool has_mbus_clk;
> + u32 num_channels_per_reg;
> };
>
> /*
> @@ -482,8 +483,8 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
>
> sun6i_dma_dump_lli(vchan, pchan->desc->v_lli, pchan->desc->p_lli);
>
> - irq_reg = pchan->idx / DMA_IRQ_CHAN_NR;
> - irq_offset = pchan->idx % DMA_IRQ_CHAN_NR;
> + irq_reg = pchan->idx / sdev->cfg->num_channels_per_reg;
> + irq_offset = pchan->idx % sdev->cfg->num_channels_per_reg;
>
> vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
>
> @@ -575,7 +576,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
> int i, j, ret = IRQ_NONE;
> u32 status;
>
> - for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
> + for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
> status = sdev->cfg->read_irq_stat(sdev, i);
> if (!status)
> continue;
> @@ -585,7 +586,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
>
> sdev->cfg->write_irq_stat(sdev, i, status);
>
> - for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
> + for (j = 0; (j < sdev->cfg->num_channels_per_reg) && status; j++) {
> pchan = sdev->pchans + j;
> vchan = pchan->vchan;
> if (vchan && (status & vchan->irq_type)) {
> @@ -1116,9 +1117,11 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec,
>
> static inline void sun6i_kill_tasklet(struct sun6i_dma_dev *sdev)
> {
> + int i;
> +
> /* Disable all interrupts from DMA */
> - writel(0, sdev->base + DMA_IRQ_EN(0));
> - writel(0, sdev->base + DMA_IRQ_EN(1));
> + for (i = 0; i < DMA_MAX_CHANNELS / sdev->cfg->num_channels_per_reg; i++)
> + sdev->cfg->write_irq_en(sdev, i, 0);
>
> /* Prevent spurious interrupts from scheduling the tasklet */
> atomic_inc(&sdev->tasklet_shutdown);
> @@ -1181,6 +1184,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> @@ -1206,6 +1210,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> @@ -1226,6 +1231,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> @@ -1255,6 +1261,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> @@ -1278,6 +1285,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> @@ -1301,6 +1309,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> .has_mbus_clk = true,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
> @@ -1325,6 +1334,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> .has_mbus_clk = true,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
> @@ -1351,6 +1361,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
>
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v2 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
From: Frank Li @ 2026-06-21 22:16 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-2-340f205891cc@gmail.com>
On Sun, Jun 21, 2026 at 09:40:55PM +0000, Yuanshen Cao wrote:
> The A733 DMA controller supports higher address (up to 32G) compared to
> previous generations. The existing `sun6i_dma_set_addr` function uses a
> hardcoded logic for setting the high-address bits in the LLI parameters.
>
> By moving `set_addr` into the `sun6i_dma_config` structure, we can
> provide specialized implementations for different hardware. This allows
> the A733 to use a version of `set_addr` that correctly handles its
> specific `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` in the `set_addr`
> register later in the series.
>
> Changes:
> - Added `set_addr` function pointer to `struct sun6i_dma_config`.
> - Refactored `sun6i_dma_set_addr` and introduced
> `sun6i_dma_set_addr_a31/a100` (keeping the logic for previous
> generations).
> - Updated all existing configuration structs to include the new
> `set_addr` pointer.
> - Removed `has_high_addr` since the logic is replaced by
> `sun6i_dma_set_addr_a100`.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/sun6i-dma.c | 35 +++++++++++++++++++++++++++++------
> 1 file changed, 29 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index ef3052c4ab36..9984b9033cbb 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -112,6 +112,7 @@
>
> /* forward declaration */
> struct sun6i_dma_dev;
> +struct sun6i_dma_lli;
>
> /*
> * Hardware channels / ports representation
> @@ -138,6 +139,8 @@ struct sun6i_dma_config {
> void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
> void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
> void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
> + void (*set_addr)(struct sun6i_dma_dev *sdev, struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst);
> void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
> u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg);
> void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val);
> @@ -147,7 +150,6 @@ struct sun6i_dma_config {
> u32 dst_burst_lengths;
> u32 src_addr_widths;
> u32 dst_addr_widths;
> - bool has_high_addr;
> bool has_mbus_clk;
> };
>
> @@ -673,16 +675,30 @@ static int set_config(struct sun6i_dma_dev *sdev,
> return 0;
> }
>
> -static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
> +static void sun6i_dma_set_addr_a31(struct sun6i_dma_dev *sdev,
> + struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst)
> +{
> + v_lli->src = lower_32_bits(src);
> + v_lli->dst = lower_32_bits(dst);
> +}
> +
> +static void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
> struct sun6i_dma_lli *v_lli,
> dma_addr_t src, dma_addr_t dst)
> {
> v_lli->src = lower_32_bits(src);
> v_lli->dst = lower_32_bits(dst);
>
> - if (sdev->cfg->has_high_addr)
> - v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
> - DST_HIGH_ADDR(upper_32_bits(dst));
> + v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
> + DST_HIGH_ADDR(upper_32_bits(dst));
> +}
> +
> +static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
> + struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst)
> +{
> + sdev->cfg->set_addr(sdev, v_lli, src, dst);
> }
>
> static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
> @@ -1156,6 +1172,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1180,6 +1197,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1199,6 +1217,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1225,6 +1244,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1247,6 +1267,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1269,6 +1290,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_h6,
> .set_mode = sun6i_set_mode_h6,
> + .set_addr = sun6i_dma_set_addr_a100,
> .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1279,7 +1301,6 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> - .has_high_addr = true,
> .has_mbus_clk = true,
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
> @@ -1293,6 +1314,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_h6,
> .set_mode = sun6i_set_mode_h6,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1320,6 +1342,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .set_addr = sun6i_dma_set_addr_a31,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
>
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v2 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
From: Frank Li @ 2026-06-21 22:14 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-1-340f205891cc@gmail.com>
On Sun, Jun 21, 2026 at 09:40:54PM +0000, Yuanshen Cao wrote:
> Refactor to support the Allwinner A733 DMA controller. Currently, the
> `sun6i-dma` driver has several functions related to interrupt handling
> (reading/writing interrupt enable and status registers) and register
> dumping that are hardcoded.
>
> To support the A733, which has different register layouts and interrupt
> handling logic, these functions are being moved into the
> `sun6i_dma_config` structure as function pointers. This allows the
> driver to use a polymorphic approach where the specific implementation
> is determined by the hardware configuration assigned during device
> probing.
>
> Changes:
> - Added function pointers to `struct sun6i_dma_config` for:
> - `dump_com_regs`
> - `read_irq_en`
> - `write_irq_en`
> - `read_irq_stat`
> - `write_irq_stat`
> - Implemented generic `sun6i_read/write_irq_*` functions for existing
> hardware.
> - Added a macro and updated existing `sun6i_dma_config` instances (A31,
> A23, H3, A64, A100, H6, V3S) to use these new function pointers.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/sun6i-dma.c | 50 ++++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 45 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index a9a254dbf8cb..ef3052c4ab36 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -138,6 +138,11 @@ struct sun6i_dma_config {
> void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
> void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
> void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
> + void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
> + u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg);
> + void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val);
> + u32 (*read_irq_stat)(struct sun6i_dma_dev *sdev, u32 irq_reg);
> + void (*write_irq_stat)(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status);
> u32 src_burst_lengths;
> u32 dst_burst_lengths;
> u32 src_addr_widths;
> @@ -347,6 +352,26 @@ static void sun6i_set_mode_h6(u32 *p_cfg, s8 src_mode, s8 dst_mode)
> DMA_CHAN_CFG_DST_MODE_H6(dst_mode);
> }
>
> +static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg)
> +{
> + return readl(sdev->base + DMA_IRQ_EN(irq_reg));
> +}
> +
> +static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
> +{
> + writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
> +}
> +
> +static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg)
> +{
> + return readl(sdev->base + DMA_IRQ_STAT(irq_reg));
> +}
> +
> +static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
> +{
> + writel(status, sdev->base + DMA_IRQ_STAT(irq_reg));
> +}
> +
> static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
> {
> struct sun6i_desc *txd = pchan->desc;
> @@ -460,16 +485,16 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
>
> vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
>
> - irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg));
> + irq_val = sdev->cfg->read_irq_en(sdev, irq_reg);
> irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
> (irq_offset * DMA_IRQ_CHAN_WIDTH));
> irq_val |= vchan->irq_type << (irq_offset * DMA_IRQ_CHAN_WIDTH);
> - writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
> + sdev->cfg->write_irq_en(sdev, irq_reg, irq_val);
>
> writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR);
> writel(DMA_CHAN_ENABLE_START, pchan->base + DMA_CHAN_ENABLE);
>
> - sun6i_dma_dump_com_regs(sdev);
> + sdev->cfg->dump_com_regs(sdev);
> sun6i_dma_dump_chan_regs(sdev, pchan);
>
> return 0;
> @@ -549,14 +574,14 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
> u32 status;
>
> for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
> - status = readl(sdev->base + DMA_IRQ_STAT(i));
> + status = sdev->cfg->read_irq_stat(sdev, i);
> if (!status)
> continue;
>
> dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
> str_high_low(i), status);
>
> - writel(status, sdev->base + DMA_IRQ_STAT(i));
> + sdev->cfg->write_irq_stat(sdev, i, status);
>
> for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
> pchan = sdev->pchans + j;
> @@ -1101,6 +1126,13 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev)
> }
> }
>
> +#define SUN6I_DMA_IRQ_A31_COMMON_OPS \
> + .dump_com_regs = sun6i_dma_dump_com_regs, \
> + .read_irq_en = sun6i_read_irq_en, \
> + .write_irq_en = sun6i_write_irq_en, \
> + .read_irq_stat = sun6i_read_irq_stat, \
> + .write_irq_stat = sun6i_write_irq_stat,
> +
> /*
> * For A31:
> *
> @@ -1132,6 +1164,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> /*
> @@ -1155,6 +1188,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
> @@ -1173,6 +1207,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> /*
> @@ -1200,6 +1235,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> /*
> @@ -1221,6 +1257,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> /*
> @@ -1244,6 +1281,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> .has_high_addr = true,
> .has_mbus_clk = true,
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> /*
> @@ -1266,6 +1304,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
> .has_mbus_clk = true,
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> /*
> @@ -1289,6 +1328,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
> .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> static const struct of_device_id sun6i_dma_match[] = {
>
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v2 0/3] arm64: dts: qcom: kodiak: Enable 4-lane DP via QMP Combo PHY
From: Doug Anderson @ 2026-06-21 22:11 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Bjorn Andersson, Mahadevan P, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, cros-qcom-dts-watchers,
linux-arm-msm, devicetree, linux-kernel, Mahadevan P,
Stephen Boyd
In-Reply-To: <84ed2c32-16d0-4c29-a06a-9242eeb8e86e@oss.qualcomm.com>
Hi,
On Fri, Jun 19, 2026 at 8:34 AM Konrad Dybcio
<konrad.dybcio@oss.qualcomm.com> wrote:
>
> On 5/22/26 5:41 AM, Bjorn Andersson wrote:
> > On Wed, Apr 29, 2026 at 12:10:39PM +0530, Mahadevan P wrote:
> >> This series enables full 4-lane DisplayPort operation on SC7280/kodiak
> >> platforms by wiring up the QMP Combo PHY correctly and consolidating
> >> the DP endpoint configuration in the SoC dtsi.
> >>
> >> Patch 1 adds mode-switch to the QMP Combo PHY node in kodiak.dtsi,
> >> which is required for the PHY to respond to USB-C Alternate Mode
> >> negotiation and switch into 4-lane DP configuration.
> >>
> >> Patch 2 moves the data-lanes property from individual board files into
> >> kodiak.dtsi since the PHY-to-controller wiring is fixed in silicon.
> >> It also removes redundant remote-endpoint and orientation-switch
> >> overrides from qcs6490-rb3gen2 and qcs6490-thundercomm-rubikpi3 that
> >> are already covered by the SoC dtsi.
> >>
> >> Patch 3 updates data-lanes from <0 1> to <0 1 2 3> in kodiak.dtsi now
> >> that mode-switch is in place, enabling the full 4-lane DP link.
> >
> > Are you sure that herobrine has 4 lanes routed on the PCB?
>
> +Doug any chance you still have schematics for that old boy?
>
> Bjorn, perhaps we could switch to a model where we define the max
> capabilities (i.e. 4-lane 8.1 GHz link) in the SoC DTs and only limit
> them as necessary? Not meeting these is borderline a board defect anyway
Bleh, I'd forgotten what a pain it was to look at herobrine schematics
with the whole qcard "abstraction".
My memory and a quick glance at schematics makes me say that herobrine
only has 2 lanes of DP. The problem is that this SoC really wasn't
designed with a laptop in mind. I seem to remember there only being
one USB 3 port and it is muxed with two of the DP lanes (since the SoC
is designed to drive a single Type-C port). In order to support all of
the ports that a laptop should have, you pretty much need to feed that
one USB 3 port into a USB hub and hardcode the DP to always use two
lanes.
The two DP lanes then go to a mux where they can be routed either
towards the left Type C port or the right Type C port.
In terms of whether we can support the 8.1 GHz link speed, I remember
much debate during the project, but I don't recall all the details. I
think the discussion was that we were supposed to support the higher
speeds, but we had to disable them because they weren't working. From
my fuzzy memory, it was unclear whether the problem was known to be
hardware or software related. I can try to dig deeper if it's
relevant.
-Doug
^ permalink raw reply
* Re: [PATCH v2 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
From: sashiko-bot @ 2026-06-21 21:57 UTC (permalink / raw)
To: Yuanshen Cao
Cc: dmaengine, robh, devicetree, vkoul, Frank.Li, linux-sunxi,
conor+dt
In-Reply-To: <20260621-sun60i-a733-dma-v2-1-340f205891cc@gmail.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
Pre-existing issues:
- [High] The interrupt handler incorrectly handles SoCs with more than 8 channels due to loop truncation and incorrect physical channel index calculations, causing missed interrupts and state confusion.
- [High] The driver disables hardware clocks and asserts reset before killing the virtual channel tasklets, which can lead to a bus fault via Use-After-Free hardware access.
- [High] Potential deadlock in `sun6i_dma_terminate_all` due to acquiring `sdev->lock` with `spin_lock()` in process context while the lock is also used in softirq context.
- [High] A race condition between `sun6i_kill_tasklet` and `sun6i_dma_start_desc` can inadvertently re-enable hardware interrupts right before the IRQ handler is freed.
--
commit f22370211098277a88244ad75b31ec31efb6ead0
Author: Yuanshen Cao <alex.caoys@gmail.com>
Subject: dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
This commit refactors the sun6i-dma driver to support the Allwinner A733 DMA
controller. It abstracts hardcoded interrupt handling and register dumping
into function pointers within the sun6i_dma_config structure. This allows
a polymorphic approach determined by the hardware configuration.
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index a9a254dbf8cb9..ef3052c4ab363 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
[ ... ]
> @@ -460,16 +485,16 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
>
> vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
>
> - irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg));
> + irq_val = sdev->cfg->read_irq_en(sdev, irq_reg);
> irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
> (irq_offset * DMA_IRQ_CHAN_WIDTH));
> irq_val |= vchan->irq_type << (irq_offset * DMA_IRQ_CHAN_WIDTH);
> - writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
> + sdev->cfg->write_irq_en(sdev, irq_reg, irq_val);
[Severity: High]
This isn't a bug introduced by this patch, but is there a race condition
between tasklet teardown and interrupt enabling?
During driver teardown, sun6i_kill_tasklet() writes 0 to the hardware
registers without locks to serialize against the tasklet:
sun6i_kill_tasklet() {
for (i = 0; i < DMA_MAX_CHANNELS / sdev->cfg->num_channels_per_reg; i++)
sdev->cfg->write_irq_en(sdev, i, 0);
...
If the tasklet concurrently executes sun6i_dma_start_desc(), it performs a
read-modify-write on the same register here.
If the tasklet reads the register just before sun6i_kill_tasklet() writes 0,
and then writes back its modified value here, could it overwrite the 0 and
re-enable the hardware interrupt right before devm_free_irq() removes the
handler, leading to an unhandled interrupt storm?
>
> writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR);
> writel(DMA_CHAN_ENABLE_START, pchan->base + DMA_CHAN_ENABLE);
[ ... ]
> @@ -549,14 +574,14 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
> u32 status;
>
> for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
> - status = readl(sdev->base + DMA_IRQ_STAT(i));
> + status = sdev->cfg->read_irq_stat(sdev, i);
[Severity: High]
This is a pre-existing issue, but does this interrupt handler correctly handle
SoCs with more than 8 channels?
For SoCs with num_pchans > 8 (such as H3 with 12 channels), the outer loop
uses integer division.
For 12 channels, 12 / 8 evaluates to 1, causing the handler to ignore the
second interrupt register and miss all interrupts for channels 8-11.
Additionally, in the inner loop:
for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
pchan = sdev->pchans + j;
This calculates the physical channel pointer without accounting for the outer
loop index i. If an interrupt occurs on channel 8, it maps to physical
channel 0 instead of 8, which clears the wrong completion state and leaks
channel 8's descriptor.
> if (!status)
> continue;
[ ... ]
[Severity: High]
This is a pre-existing issue in the driver, but is there a potential deadlock
in sun6i_dma_terminate_all() regarding sdev->lock?
sun6i_dma_terminate_all() acquires the lock in process context without
disabling interrupts:
sun6i_dma_terminate_all() {
spin_lock(&sdev->lock);
list_del_init(&vchan->node);
spin_unlock(&sdev->lock);
If a softirq fires on the same CPU while this lock is held, it will execute
sun6i_dma_tasklet(), which attempts to acquire the exact same lock using
spin_lock_irq(&sdev->lock). This would cause the tasklet to spin forever.
Should sun6i_dma_terminate_all() use spin_lock_bh() or spin_lock_irqsave()
instead?
[Severity: High]
This is a pre-existing issue, but does the teardown sequence in
sun6i_dma_remove() safely handle tasklets?
The driver disables hardware clocks and asserts reset before calling
sun6i_dma_free(), which kills the virtual channel tasklets:
sun6i_dma_remove() {
clk_disable_unprepare(sdc->clk);
reset_control_assert(sdc->rstc);
sun6i_dma_free(sdc);
If a tasklet is scheduled by an interrupt just before removal, could it run
concurrently or immediately after the hardware is powered down? If the client
callback within the tasklet calls a DMA engine API, it will attempt to access
DMA hardware registers, which could trigger a bus fault since the clocks are
disabled and reset is asserted.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com?part=1
^ permalink raw reply
* Re: [PATCH v2 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
From: sashiko-bot @ 2026-06-21 21:55 UTC (permalink / raw)
To: Yuanshen Cao
Cc: robh, linux-sunxi, Frank.Li, dmaengine, conor+dt, devicetree,
vkoul
In-Reply-To: <20260621-sun60i-a733-dma-v2-3-340f205891cc@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] The interrupt handler calculates the physical channel pointer without accounting for the register index `i`, erroneously aliasing all interrupts from secondary registers to the first group of channels.
- [High] The interrupt handler uses integer division to determine the number of IRQ registers to poll, entirely missing trailing channels if `sdev->num_pchans` is not a multiple of `num_channels_per_reg`.
--
commit 8ce409f21992b8890893dbba369b1293cac22679
Author: Yuanshen Cao <alex.caoys@gmail.com>
dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
This commit introduces num_channels_per_reg to the sun6i_dma_config and
updates DMA interrupt handling to avoid hardcoded register limits. It also
changes DMA_MAX_CHANNELS to 16 to align with the new BSP code.
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index 9984b9033cbb9..196a0d73b2212 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
[ ... ]
> @@ -575,7 +576,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
> int i, j, ret = IRQ_NONE;
> u32 status;
>
> - for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
> + for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
[Severity: High]
This is a pre-existing issue, but does this loop boundary condition truncate
the number of required registers to check?
In drivers/dma/sun6i-dma.c:sun6i_dma_interrupt(), the loop boundary condition
sdev->num_pchans / sdev->cfg->num_channels_per_reg uses integer division.
For configurations like sun8i_h3_dma_cfg, num_pchans is 12 and
num_channels_per_reg is 8. The division evaluates to 1, meaning the handler
will only check the first register (channels 0-7) and skip checking the
secondary register (channels 8-11).
If an interrupt triggers on channels 8-11, could the level-sensitive IRQ
remain asserted without being cleared, causing a continuous interrupt storm
that freezes the CPU core? Would using DIV_ROUND_UP prevent this truncation?
> status = sdev->cfg->read_irq_stat(sdev, i);
> if (!status)
> continue;
> @@ -585,7 +586,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
>
> sdev->cfg->write_irq_stat(sdev, i, status);
>
> - for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
> + for (j = 0; (j < sdev->cfg->num_channels_per_reg) && status; j++) {
> pchan = sdev->pchans + j;
[Severity: Critical]
This is a pre-existing issue, but does this inner loop calculate the physical
channel pointer correctly for secondary registers?
In sun6i_dma_interrupt(), the pchan pointer is resolved using only the
inner loop index j. This ignores the outer loop's register index i. If an
interrupt fires for a channel in a secondary register (for example, channel
8, where i = 1 and j = 0), the handler appears to erroneously fetch
sdev->pchans[0] instead of sdev->pchans[8].
Could this premature completion of an active transfer on channel 0 lead to
a use-after-free if the client frees the buffer while hardware DMA is still
running, while the actual requesting channel hangs indefinitely? Should the
index calculation include the register offset?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com?part=3
^ permalink raw reply
* Re: [PATCH v2 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
From: sashiko-bot @ 2026-06-21 21:53 UTC (permalink / raw)
To: Yuanshen Cao
Cc: vkoul, Frank.Li, linux-sunxi, devicetree, dmaengine, robh,
conor+dt
In-Reply-To: <20260621-sun60i-a733-dma-v2-5-340f205891cc@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Incorrect physical channel indexing in the interrupt handler maps all A733 physical channel interrupts to `sdev->pchans[0]`.
- [High] Integer division truncation in the interrupt handler's outer loop condition causes upper DMA channels to be completely ignored.
--
commit 5f14cc2c94bb065f85b3b101369ba127ddd3a538
Author: Yuanshen Cao <alex.caoys@gmail.com>
dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
This patch adds support for the Allwinner A733 DMA controller by defining
its specific register macros and interrupt structures. It introduces
a new sun60i_a733_dma_cfg with A733-specific configuration, notably setting
the number of channels per interrupt register to 1.
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index 196a0d73b2212..4808015934ccb 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
[ ... ]
> @@ -1339,6 +1398,33 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
> SUN6I_DMA_IRQ_A31_COMMON_OPS
> };
>
> +/*
> + * The A733 binding uses the number of dma channels from the
> + * device tree node.
> + */
> +static struct sun6i_dma_config sun60i_a733_dma_cfg = {
> + .clock_autogate_enable = sun6i_enable_clock_autogate_h3,
> + .set_burst_length = sun6i_set_burst_length_h3,
> + .set_drq = sun6i_set_drq_h6,
> + .set_mode = sun6i_set_mode_h6,
> + .set_addr = sun6i_dma_set_addr_a733,
> + .dump_com_regs = sun6i_dma_dump_com_regs_a733,
> + .read_irq_en = sun6i_read_irq_en_a733,
> + .write_irq_en = sun6i_write_irq_en_a733,
> + .read_irq_stat = sun6i_read_irq_stat_a733,
> + .write_irq_stat = sun6i_write_irq_stat_a733,
> + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
> + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR_A733,
[Severity: High]
This is a pre-existing issue, but I wanted to ask about how the current
interrupt handling interacts with num_channels_per_reg.
In sun6i_dma_interrupt() in drivers/dma/sun6i-dma.c, the outer loop
determines the number of registers to check using integer division:
for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
status = sdev->cfg->read_irq_stat(sdev, i);
If the number of physical channels is not a perfectly divisible multiple
(e.g., a controller with 12 channels and 8 channels per register), the
division truncates to 1. Will this cause the handler to ignore the upper
registers entirely, leading to silent timeouts on upper channels?
[Severity: Critical]
This is also a pre-existing issue, but how does sun6i_dma_interrupt()
resolve the physical channel when i > 0?
for (j = 0; (j < sdev->cfg->num_channels_per_reg) && status; j++) {
pchan = sdev->pchans + j;
vchan = pchan->vchan;
The calculation pchan = sdev->pchans + j ignores the register index i.
Because DMA_IRQ_CHAN_NR_A733 is 1, j will always be 0. Doesn't this mean
every interrupt on the A733 controller will incorrectly map to channel 0,
causing channel 0 to complete spuriously while the actual triggering
channel hangs?
> + .has_mbus_clk = true,
> +};
> +
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com?part=5
^ permalink raw reply
* Re: [PATCH v2 4/5] dt-bindings: dma: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
From: sashiko-bot @ 2026-06-21 21:52 UTC (permalink / raw)
To: Yuanshen Cao
Cc: linux-sunxi, devicetree, Frank.Li, vkoul, robh, dmaengine,
conor+dt
In-Reply-To: <20260621-sun60i-a733-dma-v2-4-340f205891cc@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The IRQ handler loop skips trailing DMA channels if the total number of physical channels is not an exact multiple of the channels per register.
--
commit 1a8550cc9da2ef939f555f682f0d5ea9606e1907
Author: Yuanshen Cao <alex.caoys@gmail.com>
dt-bindings: dma: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
Add `allwinner,sun60i-a733-dma` to the list of compatible strings for the
`sun50i-a64-dma` dtbinding documentation.
> While the A733 DMA controller shares many similarities with the sun50i-a64
> DMA controller, it requires a specific configuration due to differences in:
> - Interrupt register layout and mapping.
> - Number of channels per interrupt register.
[Severity: High]
This isn't a bug introduced by this patch, but could this truncating
integer division skip trailing DMA channels if the total number of physical
channels is not an exact multiple of the channels per register?
In drivers/dma/sun6i-dma.c:sun6i_dma_interrupt(), the loop condition
determines which interrupt registers to poll:
for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
For a controller with 12 physical channels (e.g., sun8i_h3_dma_cfg sets
nr_max_channels = 12) and 8 channels per register, 12 / 8 evaluates to 1.
Consequently, the loop only iterates once (for i=0), checking interrupts
for the first 8 channels and completely ignoring the remaining 4 channels.
Any DMA transfers on channels 8-11 will never have their completion
interrupts handled, leading to system hangs or driver timeouts.
Could this be updated to use DIV_ROUND_UP() to ensure all applicable
interrupt registers are processed?
> - Support for higher (32G) address widths in LLI parameters.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com?part=4
^ permalink raw reply
* Re: [PATCH v2 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
From: sashiko-bot @ 2026-06-21 21:51 UTC (permalink / raw)
To: Yuanshen Cao
Cc: robh, vkoul, dmaengine, Frank.Li, linux-sunxi, conor+dt,
devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-2-340f205891cc@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-After-Free during error path cleanup in sun6i_dma_prep_slave_sg and sun6i_dma_prep_dma_cyclic.
--
commit 9bdc9020fe709f46ed0cba2bea4a233b8e23c603
Author: Yuanshen Cao <alex.caoys@gmail.com>
dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
This commit refactors the sun6i DMA driver by moving the address setting logic
into a function pointer within the configuration structure. This allows
handling of different hardware variations, specifically adding support for
the A733's 32G high-address bits while preserving legacy behavior for
older generations.
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index ef3052c4ab363..9984b9033cbb9 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -138,6 +139,8 @@ struct sun6i_dma_config {
> void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
> void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
> void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
> + void (*set_addr)(struct sun6i_dma_dev *sdev, struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst);
> void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
[ ... ]
[Severity: High]
This is a pre-existing issue, but I noticed a potential use-after-free
in the error cleanup paths.
Looking at sun6i_dma_prep_slave_sg():
err_lli_free:
for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
dma_pool_free(sdev->pool, v_lli, p_lli);
Does this loop cause a use-after-free? The dma_pool_free() call frees
v_lli, but the loop increment step then dereferences it via
v_lli = v_lli->v_lli_next.
This exact same pattern also appears in sun6i_dma_prep_dma_cyclic():
err_lli_free:
for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
dma_pool_free(sdev->pool, v_lli, p_lli);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com?part=2
^ permalink raw reply
* [PATCH v2 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
From: Yuanshen Cao @ 2026-06-21 21:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com>
This patch implements the actual support for the Allwinner A733 DMA
controller. It defines the new register offsets and bitfield mappings
required for the A733, which slightly differs from the older `sun6i`
series.
Changes:
- New register macros for A733 interrupt enable `DMA_IRQ_EN_A733` and
status `DMA_IRQ_STAT_A733`.
- New `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` macro to handle the
32G high-address field in the LLI.
- Implemented `sun6i_dma_set_addr_a733` and A733-specific interrupt
register accessors.
- Added `sun60i_a733_dma_config`, which ties all the refactored
functionality together for this specific hardware.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 196a0d73b221..4808015934cc 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -52,6 +52,15 @@
#define SUNXI_H3_SECURE_REG 0x20
#define SUNXI_H3_DMA_GATE 0x28
#define SUNXI_H3_DMA_GATE_ENABLE 0x4
+
+/*
+ * sun60i specific registers
+ */
+#define DMA_IRQ_EN_A733(x) ((x) * 0x40 + 0x134)
+#define DMA_IRQ_STAT_A733(x) ((x) * 0x40 + 0x138)
+
+#define DMA_IRQ_CHAN_NR_A733 1
+
/*
* Channels specific registers
*/
@@ -100,6 +109,8 @@
*/
#define SRC_HIGH_ADDR(x) (((x) & 0x3U) << 16)
#define DST_HIGH_ADDR(x) (((x) & 0x3U) << 18)
+#define SRC_HIGH_ADDR_32G(x) (((x) & 0x7U) << 11)
+#define DST_HIGH_ADDR_32G(x) (((x) & 0x7U) << 15)
/*
* Various hardware related defines
@@ -257,6 +268,23 @@ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
DMA_STAT, readl(sdev->base + DMA_STAT));
}
+static inline void sun6i_dma_dump_com_regs_a733(struct sun6i_dma_dev *sdev)
+{
+ int i;
+
+ for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
+ dev_dbg(sdev->slave.dev, "Common register:\n"
+ "chan num %d\n"
+ "\tmask(%04x): 0x%08x\n"
+ "\tpend(%04x): 0x%08x\n"
+ "\tstats(%04x): 0x%08x\n",
+ i,
+ DMA_IRQ_EN_A733(i), readl(sdev->base + DMA_IRQ_EN_A733(i)),
+ DMA_IRQ_STAT_A733(i), readl(sdev->base + DMA_IRQ_STAT_A733(i)),
+ DMA_STAT, readl(sdev->base + DMA_STAT));
+ }
+}
+
static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
struct sun6i_pchan *pchan)
{
@@ -360,21 +388,41 @@ static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg)
return readl(sdev->base + DMA_IRQ_EN(irq_reg));
}
+static u32 sun6i_read_irq_en_a733(struct sun6i_dma_dev *sdev, u32 irq_reg)
+{
+ return readl(sdev->base + DMA_IRQ_EN_A733(irq_reg));
+}
+
static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
{
writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
}
+static void sun6i_write_irq_en_a733(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
+{
+ writel(irq_val, sdev->base + DMA_IRQ_EN_A733(irq_reg));
+}
+
static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg)
{
return readl(sdev->base + DMA_IRQ_STAT(irq_reg));
}
+static u32 sun6i_read_irq_stat_a733(struct sun6i_dma_dev *sdev, u32 irq_reg)
+{
+ return readl(sdev->base + DMA_IRQ_STAT_A733(irq_reg));
+}
+
static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
{
writel(status, sdev->base + DMA_IRQ_STAT(irq_reg));
}
+static void sun6i_write_irq_stat_a733(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
+{
+ writel(status, sdev->base + DMA_IRQ_STAT_A733(irq_reg));
+}
+
static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
{
struct sun6i_desc *txd = pchan->desc;
@@ -695,6 +743,17 @@ static void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
DST_HIGH_ADDR(upper_32_bits(dst));
}
+static void sun6i_dma_set_addr_a733(struct sun6i_dma_dev *sdev,
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst)
+{
+ v_lli->src = lower_32_bits(src);
+ v_lli->dst = lower_32_bits(dst);
+
+ v_lli->para |= SRC_HIGH_ADDR_32G(upper_32_bits(src)) |
+ DST_HIGH_ADDR_32G(upper_32_bits(dst));
+}
+
static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
struct sun6i_dma_lli *v_lli,
dma_addr_t src, dma_addr_t dst)
@@ -1339,6 +1398,33 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
+/*
+ * The A733 binding uses the number of dma channels from the
+ * device tree node.
+ */
+static struct sun6i_dma_config sun60i_a733_dma_cfg = {
+ .clock_autogate_enable = sun6i_enable_clock_autogate_h3,
+ .set_burst_length = sun6i_set_burst_length_h3,
+ .set_drq = sun6i_set_drq_h6,
+ .set_mode = sun6i_set_mode_h6,
+ .set_addr = sun6i_dma_set_addr_a733,
+ .dump_com_regs = sun6i_dma_dump_com_regs_a733,
+ .read_irq_en = sun6i_read_irq_en_a733,
+ .write_irq_en = sun6i_write_irq_en_a733,
+ .read_irq_stat = sun6i_read_irq_stat_a733,
+ .write_irq_stat = sun6i_write_irq_stat_a733,
+ .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
+ .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
+ .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR_A733,
+ .has_mbus_clk = true,
+};
+
/*
* The V3s have only 8 physical channels, a maximum DRQ port id of 23,
* and a total of 24 usable source and destination endpoints.
@@ -1375,6 +1461,7 @@ static const struct of_device_id sun6i_dma_match[] = {
{ .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
{ .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg },
{ .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg },
+ { .compatible = "allwinner,sun60i-a733-dma", .data = &sun60i_a733_dma_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun6i_dma_match);
--
2.54.0
^ permalink raw reply related
* [PATCH v2 4/5] dt-bindings: dma: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
From: Yuanshen Cao @ 2026-06-21 21:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com>
Add `allwinner,sun60i-a733-dma` to the list of compatible strings for the
`sun50i-a64-dma` dtbinding documentation.
While the A733 DMA controller shares many similarities with the sun50i-a64
DMA controller, it requires a specific configuration due to differences in:
- Interrupt register layout and mapping.
- Number of channels per interrupt register.
- Support for higher (32G) address widths in LLI parameters.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
index c3e14eb6cfff..1cc3304b7414 100644
--- a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
@@ -25,6 +25,7 @@ properties:
- allwinner,sun50i-a64-dma
- allwinner,sun50i-a100-dma
- allwinner,sun50i-h6-dma
+ - allwinner,sun60i-a733-dma
- items:
- const: allwinner,sun8i-r40-dma
- const: allwinner,sun50i-a64-dma
@@ -70,6 +71,7 @@ if:
- allwinner,sun20i-d1-dma
- allwinner,sun50i-a100-dma
- allwinner,sun50i-h6-dma
+ - allwinner,sun60i-a733-dma
then:
properties:
--
2.54.0
^ permalink raw reply related
* [PATCH v2 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
From: Yuanshen Cao @ 2026-06-21 21:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com>
The previous implementation of `sun6i-dma` had some implicit assumptions
about the number of channels per interrupt register. Specifically,
functions like `sun6i_kill_tasklet` were hardcoded to only disable
interrupts for IRQ 0 and 1. `DMA_MAX_CHANNELS` is also not in used in
the past, and the old SoCs never has more than 16 channels.
The A733 has a different interrupt structure where the number of
channels per register may differ. This patch introduces
`num_channels_per_reg` to the `sun6i_dma_config`, similar to BSP, to
make the interrupt handling logic hardware-agnostic. It also sets
`DMA_MAX_CHANNELS` to 16 to align with the new BSP code and ensure loops
over interrupts are correctly bounded.
Changes:
- Change `DMA_MAX_CHANNELS` definition to 16.
- Added `num_channels_per_reg` to `struct sun6i_dma_config`.
- Replaced hardcoded IRQ register calculations with values from
`sdev->cfg->num_channels_per_reg`.
- Updated `sun6i_kill_tasklet` to loop through all possible interrupt
registers based on `DMA_MAX_CHANNELS` and the configuration.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 9984b9033cbb..196a0d73b221 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -41,7 +41,7 @@
#define DMA_STAT 0x30
/* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */
-#define DMA_MAX_CHANNELS (DMA_IRQ_CHAN_NR * 0x10 / 4)
+#define DMA_MAX_CHANNELS 16
/*
* sun8i specific registers
@@ -151,6 +151,7 @@ struct sun6i_dma_config {
u32 src_addr_widths;
u32 dst_addr_widths;
bool has_mbus_clk;
+ u32 num_channels_per_reg;
};
/*
@@ -482,8 +483,8 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
sun6i_dma_dump_lli(vchan, pchan->desc->v_lli, pchan->desc->p_lli);
- irq_reg = pchan->idx / DMA_IRQ_CHAN_NR;
- irq_offset = pchan->idx % DMA_IRQ_CHAN_NR;
+ irq_reg = pchan->idx / sdev->cfg->num_channels_per_reg;
+ irq_offset = pchan->idx % sdev->cfg->num_channels_per_reg;
vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
@@ -575,7 +576,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
int i, j, ret = IRQ_NONE;
u32 status;
- for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
+ for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
status = sdev->cfg->read_irq_stat(sdev, i);
if (!status)
continue;
@@ -585,7 +586,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
sdev->cfg->write_irq_stat(sdev, i, status);
- for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
+ for (j = 0; (j < sdev->cfg->num_channels_per_reg) && status; j++) {
pchan = sdev->pchans + j;
vchan = pchan->vchan;
if (vchan && (status & vchan->irq_type)) {
@@ -1116,9 +1117,11 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec,
static inline void sun6i_kill_tasklet(struct sun6i_dma_dev *sdev)
{
+ int i;
+
/* Disable all interrupts from DMA */
- writel(0, sdev->base + DMA_IRQ_EN(0));
- writel(0, sdev->base + DMA_IRQ_EN(1));
+ for (i = 0; i < DMA_MAX_CHANNELS / sdev->cfg->num_channels_per_reg; i++)
+ sdev->cfg->write_irq_en(sdev, i, 0);
/* Prevent spurious interrupts from scheduling the tasklet */
atomic_inc(&sdev->tasklet_shutdown);
@@ -1181,6 +1184,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1206,6 +1210,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1226,6 +1231,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1255,6 +1261,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1278,6 +1285,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1301,6 +1309,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
.has_mbus_clk = true,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1325,6 +1334,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
.has_mbus_clk = true,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1351,6 +1361,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
From: Yuanshen Cao @ 2026-06-21 21:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com>
The A733 DMA controller supports higher address (up to 32G) compared to
previous generations. The existing `sun6i_dma_set_addr` function uses a
hardcoded logic for setting the high-address bits in the LLI parameters.
By moving `set_addr` into the `sun6i_dma_config` structure, we can
provide specialized implementations for different hardware. This allows
the A733 to use a version of `set_addr` that correctly handles its
specific `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` in the `set_addr`
register later in the series.
Changes:
- Added `set_addr` function pointer to `struct sun6i_dma_config`.
- Refactored `sun6i_dma_set_addr` and introduced
`sun6i_dma_set_addr_a31/a100` (keeping the logic for previous
generations).
- Updated all existing configuration structs to include the new
`set_addr` pointer.
- Removed `has_high_addr` since the logic is replaced by
`sun6i_dma_set_addr_a100`.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 35 +++++++++++++++++++++++++++++------
1 file changed, 29 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index ef3052c4ab36..9984b9033cbb 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -112,6 +112,7 @@
/* forward declaration */
struct sun6i_dma_dev;
+struct sun6i_dma_lli;
/*
* Hardware channels / ports representation
@@ -138,6 +139,8 @@ struct sun6i_dma_config {
void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
+ void (*set_addr)(struct sun6i_dma_dev *sdev, struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst);
void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg);
void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val);
@@ -147,7 +150,6 @@ struct sun6i_dma_config {
u32 dst_burst_lengths;
u32 src_addr_widths;
u32 dst_addr_widths;
- bool has_high_addr;
bool has_mbus_clk;
};
@@ -673,16 +675,30 @@ static int set_config(struct sun6i_dma_dev *sdev,
return 0;
}
-static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
+static void sun6i_dma_set_addr_a31(struct sun6i_dma_dev *sdev,
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst)
+{
+ v_lli->src = lower_32_bits(src);
+ v_lli->dst = lower_32_bits(dst);
+}
+
+static void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
struct sun6i_dma_lli *v_lli,
dma_addr_t src, dma_addr_t dst)
{
v_lli->src = lower_32_bits(src);
v_lli->dst = lower_32_bits(dst);
- if (sdev->cfg->has_high_addr)
- v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
- DST_HIGH_ADDR(upper_32_bits(dst));
+ v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
+ DST_HIGH_ADDR(upper_32_bits(dst));
+}
+
+static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst)
+{
+ sdev->cfg->set_addr(sdev, v_lli, src, dst);
}
static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
@@ -1156,6 +1172,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1180,6 +1197,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1199,6 +1217,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1225,6 +1244,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1247,6 +1267,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1269,6 +1290,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_h6,
.set_mode = sun6i_set_mode_h6,
+ .set_addr = sun6i_dma_set_addr_a100,
.src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1279,7 +1301,6 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
- .has_high_addr = true,
.has_mbus_clk = true,
SUN6I_DMA_IRQ_A31_COMMON_OPS
};
@@ -1293,6 +1314,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_h6,
.set_mode = sun6i_set_mode_h6,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1320,6 +1342,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr_a31,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
--
2.54.0
^ permalink raw reply related
* [PATCH v2 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
From: Yuanshen Cao @ 2026-06-21 21:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
In-Reply-To: <20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com>
Refactor to support the Allwinner A733 DMA controller. Currently, the
`sun6i-dma` driver has several functions related to interrupt handling
(reading/writing interrupt enable and status registers) and register
dumping that are hardcoded.
To support the A733, which has different register layouts and interrupt
handling logic, these functions are being moved into the
`sun6i_dma_config` structure as function pointers. This allows the
driver to use a polymorphic approach where the specific implementation
is determined by the hardware configuration assigned during device
probing.
Changes:
- Added function pointers to `struct sun6i_dma_config` for:
- `dump_com_regs`
- `read_irq_en`
- `write_irq_en`
- `read_irq_stat`
- `write_irq_stat`
- Implemented generic `sun6i_read/write_irq_*` functions for existing
hardware.
- Added a macro and updated existing `sun6i_dma_config` instances (A31,
A23, H3, A64, A100, H6, V3S) to use these new function pointers.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 50 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index a9a254dbf8cb..ef3052c4ab36 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -138,6 +138,11 @@ struct sun6i_dma_config {
void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
+ void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
+ u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg);
+ void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val);
+ u32 (*read_irq_stat)(struct sun6i_dma_dev *sdev, u32 irq_reg);
+ void (*write_irq_stat)(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status);
u32 src_burst_lengths;
u32 dst_burst_lengths;
u32 src_addr_widths;
@@ -347,6 +352,26 @@ static void sun6i_set_mode_h6(u32 *p_cfg, s8 src_mode, s8 dst_mode)
DMA_CHAN_CFG_DST_MODE_H6(dst_mode);
}
+static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg)
+{
+ return readl(sdev->base + DMA_IRQ_EN(irq_reg));
+}
+
+static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
+{
+ writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
+}
+
+static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg)
+{
+ return readl(sdev->base + DMA_IRQ_STAT(irq_reg));
+}
+
+static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
+{
+ writel(status, sdev->base + DMA_IRQ_STAT(irq_reg));
+}
+
static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
{
struct sun6i_desc *txd = pchan->desc;
@@ -460,16 +485,16 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
- irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg));
+ irq_val = sdev->cfg->read_irq_en(sdev, irq_reg);
irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
(irq_offset * DMA_IRQ_CHAN_WIDTH));
irq_val |= vchan->irq_type << (irq_offset * DMA_IRQ_CHAN_WIDTH);
- writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
+ sdev->cfg->write_irq_en(sdev, irq_reg, irq_val);
writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR);
writel(DMA_CHAN_ENABLE_START, pchan->base + DMA_CHAN_ENABLE);
- sun6i_dma_dump_com_regs(sdev);
+ sdev->cfg->dump_com_regs(sdev);
sun6i_dma_dump_chan_regs(sdev, pchan);
return 0;
@@ -549,14 +574,14 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
u32 status;
for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
- status = readl(sdev->base + DMA_IRQ_STAT(i));
+ status = sdev->cfg->read_irq_stat(sdev, i);
if (!status)
continue;
dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
str_high_low(i), status);
- writel(status, sdev->base + DMA_IRQ_STAT(i));
+ sdev->cfg->write_irq_stat(sdev, i, status);
for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
pchan = sdev->pchans + j;
@@ -1101,6 +1126,13 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev)
}
}
+#define SUN6I_DMA_IRQ_A31_COMMON_OPS \
+ .dump_com_regs = sun6i_dma_dump_com_regs, \
+ .read_irq_en = sun6i_read_irq_en, \
+ .write_irq_en = sun6i_write_irq_en, \
+ .read_irq_stat = sun6i_read_irq_stat, \
+ .write_irq_stat = sun6i_write_irq_stat,
+
/*
* For A31:
*
@@ -1132,6 +1164,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
/*
@@ -1155,6 +1188,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
@@ -1173,6 +1207,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
/*
@@ -1200,6 +1235,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
/*
@@ -1221,6 +1257,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
/*
@@ -1244,6 +1281,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
.has_high_addr = true,
.has_mbus_clk = true,
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
/*
@@ -1266,6 +1304,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
.has_mbus_clk = true,
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
/*
@@ -1289,6 +1328,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_OPS
};
static const struct of_device_id sun6i_dma_match[] = {
--
2.54.0
^ permalink raw reply related
* [PATCH v2 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller
From: Yuanshen Cao @ 2026-06-21 21:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: Yuanshen Cao, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
Hi everyone,
This patch series introduces support for the Allwinner A733 DMA
controller in the `sun6i-dma` driver.
The A733 DMA controller differs from previous generations in several key
ways:
1. It supports higher address (up to 32G).
2. It uses a different interrupt register layout and mapping.
3. It has a different number of channels per interrupt register.
To support these differences without introducing complex conditional
logic throughout the driver, this series first refactors the
`sun6i_dma_config` structure. By moving interrupt handling, register
dumping, and address configuration into function pointers within the
configuration structure. This allows the driver to support the A733
and future hardware revisions. It also aligns with the DMA drivers in
Radxa BSP Package[1].
The series is organized as follows:
- Refactors the configuration structure to include function pointers for
interrupt and register operations.
- Moves address setting logic into the configuration structure to handle
varying address widths.
- Adds support for variable channels per interrupt register.
- Updates the device tree bindings documentation.
- Implements the A733-specific configuration and register mappings.
Tested on Radxa Cubie A7Z.
[1] https://github.com/radxa/allwinner-bsp/blob/cubie-aiot-v1.4.8/drivers/dma/sunxi-dma.c
Thanks!
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
Changes in v2:
- Implement SUN6I_DMA_IRQ_A31_COMMON_OPS macro to avoid duplicate.
- Move set_addr into helper function and revert back sun6i_dma_set_addr.
- Rename chan_num to irq_req to avoid misleading name as suggested by
sashiko.
- Reorder and reword the dtbinding patch for more clarity.
- Link to v1: https://patch.msgid.link/20260619-sun60i-a733-dma-v1-0-da4b649fc72a@gmail.com
To: Vinod Koul <vkoul@kernel.org>
To: Frank Li <Frank.Li@kernel.org>
To: Chen-Yu Tsai <wens@kernel.org>
To: Jernej Skrabec <jernej.skrabec@gmail.com>
To: Samuel Holland <samuel@sholland.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Maxime Ripard <mripard@kernel.org>
Cc: dmaengine@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-sunxi@lists.linux.dev
Cc: linux-kernel@vger.kernel.org
Cc: devicetree@vger.kernel.org
---
Yuanshen Cao (5):
dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
dt-bindings: dma: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
.../bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 +
drivers/dma/sun6i-dma.c | 197 +++++++++++++++++++--
2 files changed, 181 insertions(+), 18 deletions(-)
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260619-sun60i-a733-dma-c2455149165d
Best regards,
--
Yuanshen Cao <alex.caoys@gmail.com>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox