* Re: [PATCH v4 3/3] input: misc: Add Qualcomm SPMI PMIC haptics driver
From: Fenglin Wu @ 2026-07-20 4:33 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-arm-msm, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lee Jones, Stephen Boyd, Bjorn Andersson, Konrad Dybcio,
David Collins, Subbaraman Narayanamurthy, Kamal Wadhwa,
linux-input, devicetree, linux-kernel
In-Reply-To: <alpWEL1hJgQ18I7d@google.com>
On 7/18/2026 12:51 AM, Dmitry Torokhov wrote:
...
Thank you for reviewing the change!
>> +
>> +static void haptics_fifo_irq_enable(struct qcom_haptics *h, bool enable)
>> +{
>> + if (h->irq_enabled == enable)
>> + return;
>
> Should t you know if given code runs with interrupts disabled or
> enabled? I believe this tracking and the wrapper should be removed.
>
>> +
>> + if (enable)
>> + enable_irq(h->fifo_empty_irq);
>> + else
>> + disable_irq(h->fifo_empty_irq);
>> +
>> + h->irq_enabled = enable;
>> +}
>> +
In normal handling, the code should be able to track the IRQ status. The
IRQ is not auto enabled after the registration. It's only enabled when
the upload data doesn't fit in the initial FIFO fill and requires to
refill based on the interrupt. And after play is done, the IRQ can be
disabled.
There are error paths or concurrency cases which might cause the IRQ
being disabled multiple times, for example:
1) IRQ is enabled when playing an effect with a long FIFO data
2) During the FIFO refill, if any SPMI write errors, and if the SPMI bus
issue persists, there would be an IRQ storm as the IRQ is still kept as
enabled and the HW FIFO is still 'empty'. So I need to disable the IRQ
to avoid the IRQ storming before the play is stopped in step 3) below.
3) Stop the play and disable the IRQ, either when the play is done, or
when userspace issues a stop command.
There are potential multiple times of IRQ disabling in such cases, I
created this helper function to track the IRQ status to prevent
disabling the IRQ permanently.
BTW, Sashiko AI flagged a deadlock issue on the 'disable_irq()' usage,
when a stop() command came 1st and acquired the 'fifo_lock' and then the
IRQ thread is scheduled, in 'disable_irq()" the IRQ handler would
compete the 'fifo_lock' and cause a deadlock. I will need to change it
to use the _nosync() version consider how to prevent the races of FIFO
resources being used by the IRQ handler after it is freed in stop().
>> +
>> +/*
>> + * haptics_fifo_empty_irq: Threaded IRQ handler for the FIFO-empty interrupt.
>> + *
>> + * While a FIFO play is in progress the hardware fires this interrupt when
>> + * the number of samples in the FIFO drops below the programmed threshold.
>> + * The handler refills the FIFO from the effect's data buffer. When all
>> + * samples have been written the threshold is set to zero. The HW would
>> + * stop the play automatically after all of the samples in FIFO memory are
>> + * played out.
>> + */
>> +static irqreturn_t haptics_fifo_empty_irq(int irq, void *dev_id)
>> +{
>> + struct qcom_haptics *h = dev_id;
>> + u32 sts, to_write;
>> + int ret;
>> +
>> + ret = regmap_read(h->regmap,
>> + h->cfg_base + HAP_CFG_INT_RT_STS_REG, &sts);
>> + if (ret || !(sts & FIFO_EMPTY_BIT))
>> + return IRQ_HANDLED;
>> +
>> + guard(mutex)(&h->fifo_lock);
>> +
>> + if (!h->fifo_data)
>> + return IRQ_HANDLED;
>> +
>> + /* Refill: write the next chunk */
>> + to_write = min_t(u32, h->data_len - h->data_written,
>> + h->fifo_len - FIFO_EMPTY_THRESH);
>> + ret = haptics_write_fifo_chunk(h, &h->fifo_data[h->data_written], to_write);
>> + if (ret) {
>> + dev_err(h->dev, "refill FIFO samples failed, ret=%d\n", ret);
>> + /*
>> + * If data refilling is failed,stop the HW play and disable the
>> + * IRQ to prevent the FIFO empty IRQ being fired continuously.
>> + */
>
> Is recovery possible after this?
It depends on how serious the problem is. Ideally, the SPMI write access
error above should only indicate a bus transaction fault but the haptics
HW should still work on its FIFO data which has already been programmed.
If the SPMI write fault just happens temporarily, it might only cause
the haptics driving waveform distortion and impact on the vibration
effect. If the SPMI write fault is caused by SPMI bus hung, then the
haptics module won't be functional anymore as all of the commands are
SPMI based. For safety, I stop the play (doesn't work in the latter
case) and disable the IRQ to prevent the IRQ being fired continuously.
Thanks
Fenglin
^ permalink raw reply
* Re: [PATCH v6 5/6] arm64: dts: qcom: Add device tree for Nord Ride board
From: Shawn Guo @ 2026-07-20 4:20 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bartosz Golaszewski, Deepti Jaggi, devicetree,
linux-arm-msm, linux-kernel
In-Reply-To: <90769e77-c311-4a4f-be37-09065e217f55@oss.qualcomm.com>
On Thu, Jul 16, 2026 at 07:14:56PM +0200, Konrad Dybcio wrote:
> On 7/16/26 4:26 PM, Shawn Guo wrote:
> > From: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
> >
> > Add initial device tree for the Qualcomm SA8797P Ride reference board,
> > which is built on Nord GearVM variant.
> >
> > - Configure UART15 as the primary console and UART4 as the secondary
> > serial port
> > - Enable UFS storage support
> > - Define thermal zones for PMIC dies, UFS, and two SDRAM sensors,
> > all sourced from SCMI sensor protocol on channel 23
> >
> > Signed-off-by: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
> > Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> > ---
>
> [...]
>
> > +&scmi3 {
> > + status = "okay";
> > +};
> > +
> > +&scmi11 {
> > + status = "okay";
> > +};
> > +
> > +&scmi15 {
> > + status = "okay";
> > +};
> > +
> > +&scmi23 {
> > + status = "okay";
> > +};
>
> Why only these ones? Can all of the SCMI servers be enabled?
These are the ones that have actual/enabled client/consumer devices,
e.g. scmi3 for ufs, scmi11 for uart, scmi23 for thermal sensors.
We can enable all the SCMI servers, but I'm afraid that cause kernel
to map shared memory and issue SMC calls to discover protocols (like
power domains, DVFS, resets) that have no consumers, which might waste
a small amount of memory and boot time.
scmi15 currently doesn't have any enabled consumers, so I will drop it
for now.
Shawn
^ permalink raw reply
* Re: [PATCH v5 02/16] device property: Add fwnode_graph_get_next_port_endpoint()
From: Chen-Yu Tsai @ 2026-07-20 3:58 UTC (permalink / raw)
To: Andy Shevchenko
Cc: sashiko-reviews, devicetree, driver-core, Bartosz Golaszewski
In-Reply-To: <alpZVUEOidfGMpfD@ashevche-desk.local>
On Sat, Jul 18, 2026 at 12:33 AM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Fri, Jul 17, 2026 at 07:05:56PM +0800, Chen-Yu Tsai wrote:
> > On Fri, Jul 17, 2026 at 5:04 AM Andy Shevchenko
> > <andriy.shevchenko@linux.intel.com> wrote:
> > > On Thu, Jul 16, 2026 at 04:52:27PM +0800, Chen-Yu Tsai wrote:
> > > > On Wed, Jul 15, 2026 at 5:09 PM <sashiko-bot@kernel.org> wrote:
>
> ...
>
> > > > > > +struct fwnode_handle *fwnode_graph_get_next_port_endpoint(const struct fwnode_handle *port,
> > > > > > + struct fwnode_handle *prev)
> > > > > > +{
> > > > > > + do {
> > > > > > + prev = fwnode_get_next_child_node(port, prev);
> > > > >
> > > > > [Severity: High]
> > > > > This is a pre-existing issue, but iterating over endpoints using
> > > > > fwnode_get_next_child_node() can cause an infinite loop and a use-after-free
> > > > > if a device has secondary fwnodes attached.
> > > > >
> > > > > If a device port has both primary children and a secondary fwnode, the primary
> > > > > ops finish and drop the reference to the last primary child, returning NULL.
> > > > > Then, fwnode_get_next_child_node() incorrectly passes this same dropped child
> > > > > pointer to the secondary ops:
> > > > >
> > > > > drivers/base/property.c:fwnode_get_next_child_node() {
> > > > > /* Try to find a child in primary fwnode */
> > > > > next = fwnode_call_ptr_op(fwnode, get_next_child_node, child);
> > > > > if (next)
> > > > > return next;
> > > > >
> > > > > /* When no more children in primary, continue with secondary */
> > > > > return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child);
> > > > > }
> > > > >
> > > > > This can result in a double-put. Furthermore, when traversing secondary
> > > > > children, the primary ops don't recognize the software node, treat it as NULL,
> > > > > and return the first primary child again, creating an infinite loop.
> > >
> > > Yeah, it has to be implemented in a way how fwnode_graph_get_next_endpoint() is done.
> >
> > If your patch series addresses the issue, I guess we can consider it
> > covered? Sashiko labeled this a pre-existing.
>
> I noticed that it addresses only main get_next case, but get_next_available is
> still with the same issue. It's not that critical, we don't use much that API,
> but still will be good to fix at some point.
>
> > > > > [Severity: Medium]
> > > > > Does this fail to unconditionally put the prev reference if the provided port
> > > > > is invalid?
> > > > >
> > > > > If a caller passes a valid prev node but port is NULL or an error pointer,
> > > > > fwnode_get_next_child_node() returns early without dropping prev:
> > > > >
> > > > > drivers/base/property.c:fwnode_get_next_child_node() {
> > > > > struct fwnode_handle *next;
> > > > >
> > > > > if (IS_ERR_OR_NULL(fwnode))
> > > > > return NULL;
> > > > > ...
> > > > > }
> > > > >
> > > > > Could this lead to a reference count leak, violating the documented API
> > > > > contract that prev is unconditionally put?
> > > >
> > > > This seems like what the other fwnode_get_next_*() functions are doing.
> > > > I'd say this is a pre-existing issue as well.
> > >
> > > See this series (not yet applied, you even have chance to review it!):
> > > 20260611203537.1786399-1-andriy.shevchenko@linux.intel.com
> >
> > I believe that series addresses the first issue, not this one, even
> > though it partially mitigates it.
> >
> > The contract says "puts a reference to @prev unconditionally." What it
> > doesn't say is that it requires @node to be valid, i.e. the function
> > is a no-op if node == NULL.
> >
> > "node != NULL" being an implicit requirement seems to be the norm for
> > the fwnode_* and of_* APIs. It's not explicitly spelled out and Sashiko
> > lacks the context for it.
> >
> > I'm not sure this should be changed within this patch. It would end up
> > as a mismatch between the various functions and possibly lead to more
> > confusion.
>
> How does node become invalid after that patch?
Sashiko is arguing that the kernel-doc says "reference to @prev is _always_
put". But if you look at all the *_get_next_*() functions, they all just
return directly if @node is NULL.
I'm saying that this is implicit across the full API, and adding text
explaining it in just one of the functions creates a discrepancy. I can
add a patch before this to clarify the existing documentation, and change
the text in this patch as well, to make Sashiko happy.
Does that make sense?
> > > > > > + if (fwnode_name_eq(prev, "endpoint"))
> > > > >
> > > > > [Severity: High]
> > > > > Does hardcoding this string match break the fwnode graph abstraction for
> > > > > non-DeviceTree platforms?
> > > > >
> > > > > By strictly matching the "endpoint" prefix, this bypasses the provider-specific
> > > > > fwnode_operations, which could silently ignore valid ACPI and software node
> > > > > endpoints that don't follow this exact naming scheme. Shouldn't this rely on
> > > > > the backend-specific graph_get_next_endpoint operations instead?
> > > >
> > > > From drivers/acpi/property.c it seems that ACPI graphs follow the same
> > > > structure. I don't have visibility into ACPI implementations though.
> > >
> > > Sashiko might be right. ACPI has device and data nodes, for device nodes the
> > > name will be FourCC, so never longer than 4 characters. For data nodes, it
> > > takes their names, which are arbitrary strings and seems should follow the given
> > > schema. You need Sakari Ailus to review this patch.
> >
> > OK. Will add Sakari in the next version.
>
> Hmm... This is interesting. For any fwnode API changes you should add the
> respective reviewers. Do you use tools or doing that manually? You should use
> tools. I know that `b4` is capable of doing that, but I use a script [1] I
> wrote a few years ago.
Actually Sakari is already in the list of recipients.
I'm running get_maintainers.pl on all the files I changed, which is not
the formatted patches, but I don't think that affects the output much.
It's a bit less automated, but I sometimes trim the recipient list
slightly as I know they probably won't look at it.
> > > > We also have the following in include/linux/fwnode.h:
> > > >
> > > > #define SWNODE_GRAPH_PORT_NAME_FMT "port@%u"
> > > > #define SWNODE_GRAPH_ENDPOINT_NAME_FMT "endpoint@%u"
> > > >
> > > > So this should not be a problem.
> > > >
> > > > > > + break;
> > > > > > + } while (prev);
> > > > > > +
> > > > > > + return prev;
> > > > > > +}
>
> [1]: https://github.com/andy-shev/home-bin-tools/blob/master/ge2maintainer.sh
Thanks for sharing!
ChenYu
^ permalink raw reply
* Re: [PATCH v6 3/6] arm64: dts: qcom: Add device tree for Nord Embedded variant
From: Shawn Guo @ 2026-07-20 3:50 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bartosz Golaszewski, Deepti Jaggi, devicetree, linux-arm-msm,
linux-kernel, Bartosz Golaszewski
In-Reply-To: <20260716142605.2398433-4-shengchao.guo@oss.qualcomm.com>
On Thu, Jul 16, 2026 at 10:25:58PM +0800, Shawn Guo wrote:
> Unlike the GearVM variant, Nord Embedded variant has platform resources
> (clocks, regulators, powerdomains, pins, etc.) directly controlled by
> the operating system. Add a separate dtsi file extending the existing
> top-level nord.dtsi with nodes representing these peripherals as well as
> describing how they are wired up with the already defined components.
>
> Co-developed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> ---
> arch/arm64/boot/dts/qcom/nord-embedded.dtsi | 1726 +++++++++++++++++++
> 1 file changed, 1726 insertions(+)
> create mode 100644 arch/arm64/boot/dts/qcom/nord-embedded.dtsi
>
> diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi
> new file mode 100644
> index 000000000000..1ec32c2f2308
> --- /dev/null
> +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi
> @@ -0,0 +1,1726 @@
> +// SPDX-License-Identifier: BSD-3-Clause
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <dt-bindings/clock/qcom,nord-gcc.h>
> +#include <dt-bindings/clock/qcom,nord-negcc.h>
> +#include <dt-bindings/clock/qcom,nord-nwgcc.h>
> +#include <dt-bindings/clock/qcom,nord-segcc.h>
> +#include <dt-bindings/clock/qcom,nord-tcsrcc.h>
> +#include <dt-bindings/clock/qcom,rpmh.h>
> +#include <dt-bindings/interconnect/qcom,icc.h>
> +#include <dt-bindings/interconnect/qcom,nord-rpmh.h>
> +#include <dt-bindings/power/qcom-rpmpd.h>
> +#include <dt-bindings/soc/qcom,rpmh-rsc.h>
> +
> +#include "nord.dtsi"
> +
> +/ {
> + clk_virt: interconnect-clk-virt {
> + compatible = "qcom,nord-clk-virt";
> + #interconnect-cells = <2>;
> + qcom,bcm-voters = <&apps_bcm_voter>;
> + };
> +
> + mc_virt: interconnect-mc-virt {
> + compatible = "qcom,nord-mc-virt";
> + #interconnect-cells = <2>;
> + qcom,bcm-voters = <&apps_bcm_voter>;
> + };
> +};
> +
> +&crypto {
> + interconnects = <&aggre1_noc_tile MASTER_CRYPTO_CORE0 QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
> + interconnect-names = "memory";
> +
> + status = "enabled";
Oops! This should be "okay". Will fix.
Shawn
> +};
^ permalink raw reply
* Re: [PATCH v5 3/3] arm64: dts: qcom: glymur: Wire PCIe3a/3b to shared Gen5x8 PHY
From: Qiang Yu @ 2026-07-20 3:44 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel,
Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-phy,
devicetree, linux-kernel
In-Reply-To: <5f20426c-d34e-4d68-ad80-d01e2fc4e33d@oss.qualcomm.com>
On Fri, Jul 17, 2026 at 06:50:23PM +0200, Konrad Dybcio wrote:
> On 7/17/26 11:58 AM, Qiang Yu wrote:
> > Glymur's PCIe3a and PCIe3b controllers share a single Gen5x8 QMP PHY block
> > that can be bifurcated into two independent x4 links, rather than each
> > controller owning its own dedicated PHY.
> >
> > Add a pcie3_phy node describing the shared PHY block, add the missing
> > PCIe3a controller node, and point both PCIe3a's and PCIe3b's port phys
> > at &pcie3_phy (index 0 and 1 respectively) so each controller picks up
> > its half of the bifurcated PHY. Update the GCC pipe clock parent array
> > to reference the new PHY's clock outputs instead of the placeholders.
> >
> > Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
> > ---
> > arch/arm64/boot/dts/qcom/glymur-crd.dtsi | 8 +
> > arch/arm64/boot/dts/qcom/glymur.dtsi | 336 ++++++++++++++++++++++++++++++-
>
> These changes really shouldn't be in the same commit
Ack, will split this into two patches: one for the glymur.dtsi PHY node
and GCC pipe clock changes, and a separate one for the glymur-crd.dtsi
board-specific supply wiring.
>
> > 2 files changed, 342 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/boot/dts/qcom/glymur-crd.dtsi b/arch/arm64/boot/dts/qcom/glymur-crd.dtsi
> > index c12808abbfe1..e8e81bf9a6a1 100644
> > --- a/arch/arm64/boot/dts/qcom/glymur-crd.dtsi
> > +++ b/arch/arm64/boot/dts/qcom/glymur-crd.dtsi
> > @@ -750,6 +750,14 @@ &pcie3b {
> > pinctrl-names = "default";
> > };
> >
> > +&pcie3_phy {
> > + vdda-phy-supply = <&vreg_l3c_e1_0p89>;
> > + vdda-pll-supply = <&vreg_l2c_e1_1p14>;
> > +
> > + vdda-refgen0p9-supply = <&vreg_l1c_e1_0p82>;
> > + vdda-refgen1p2-supply = <&vreg_l4f_e1_1p08>;
> > +};
>
> This node is disabled
Yes, it is disabled because pcie3b is not enabled by default.
See the fake pcie3b_phy be dropped
https://lore.kernel.org/all/20260420133616.88740-2-krzysztof.kozlowski@oss.qualcomm.com/
- Qiang Yu
^ permalink raw reply
* RE: [PATCH v3 1/4] dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples
From: Torreno, Alexis Czezar @ 2026-07-20 3:41 UTC (permalink / raw)
To: Torreno, Alexis Czezar, Guenter Roeck, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Shuah Khan
Cc: linux-hwmon@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
Krzysztof Kozlowski
In-Reply-To: <20260720-dev-max20830c-v3-1-9d06d27d6da3@analog.com>
> -----Original Message-----
> From: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
> Sent: Monday, July 20, 2026 11:15 AM
> To: Guenter Roeck <linux@roeck-us.net>; Rob Herring <robh@kernel.org>;
> Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley
> <conor+dt@kernel.org>; Jonathan Corbet <corbet@lwn.net>; Shuah Khan
> <skhan@linuxfoundation.org>
> Cc: linux-hwmon@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linux-doc@vger.kernel.org; Torreno, Alexis Czezar
> <AlexisCzezar.Torreno@analog.com>; Krzysztof Kozlowski
> <krzysztof.kozlowski@oss.qualcomm.com>
> Subject: [PATCH v3 1/4] dt-bindings: hwmon: (pmbus/max20830): add VOUT
> feedback resistor properties and complete examples
>
> Add adi,vout-rfb1-ohms and adi,vout-rfb2-ohms properties to support external
> voltage divider configuration for VOUT sensing. When the desired output
> voltage is higher than VREF, a resistor divider (RFB1 and RFB2) is required to
> reach the intended value.
>
> The properties use a dependency constraint to ensure both resistors are
> specified together, or neither. This prevents misconfiguration where only one
> resistor value is provided.
>
> This patch also added missing entries in the examples.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
> ---
> .../bindings/hwmon/pmbus/adi,max20830.yaml | 20
> ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git
> a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> index
> 1625dd59417f1b3ca689a9c86ca266da913d1217..214adf1a3b9e9efdeca7db3bd
> 7679c70656e9a7e 100644
> --- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> @@ -44,11 +44,26 @@ properties:
> GPIO connected to the power-good status output pin.
> maxItems: 1
>
> + adi,vout-rfb1-ohms:
> + description:
> + Top feedback resistor (RFB1) value in ohms for VOUT sensing divider.
> + When the desired output voltage is higher than VREF, a resistor divider
> + is required. VOUT = VREF × (1 + RFB1/RFB2)
> +
> + adi,vout-rfb2-ohms:
> + description:
> + Bottom feedback resistor (RFB2) value in ohms for VOUT sensing divider.
> + Datasheet recommends that RFB2 does not exceed 2.5kΩ.
> +
> required:
> - compatible
> - reg
> - vddh-supply
>
> +dependencies:
> + adi,vout-rfb1-ohms: ['adi,vout-rfb2-ohms']
> + adi,vout-rfb2-ohms: ['adi,vout-rfb1-ohms']
> +
> unevaluatedProperties: false
>
> examples:
> @@ -61,6 +76,11 @@ examples:
> compatible = "adi,max20830";
> reg = <0x30>;
> vddh-supply = <&vddh>;
> + avdd-supply = <&avdd>;
> + ldoin-supply = <&ldoin>;
> + pwr-good-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
> + adi,vout-rfb1-ohms = <10000>;
> + adi,vout-rfb2-ohms = <2000>;
> };
> };
Disregard please, I carried over a few info from a dropped patch to combine
with this patch and missed the include for gpio.
Apologies on carrying the review tag, should've removed it even if it was a
small edit.
Regards,
Alexis
> ...
>
> --
> 2.34.1
^ permalink raw reply
* Re: [PATCH v5 2/3] phy: qcom: qmp-pcie: Add QMP PCIe Multi-PHY driver
From: Qiang Yu @ 2026-07-20 3:36 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel, Konrad Dybcio,
linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <alqrbpi3EbvV9RnC@baldur>
On Fri, Jul 17, 2026 at 05:45:45PM -0500, Bjorn Andersson wrote:
> On Fri, Jul 17, 2026 at 02:58:34AM -0700, Qiang Yu wrote:
> [..]
> > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
> > new file mode 100644
> > index 000000000000..b79edf2b7d5f
> > --- /dev/null
> > +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
> > @@ -0,0 +1,770 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
>
> No, there should not be a year in there.
Ack, will drop the year:
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>
> > + */
> > +
> [..]
> > +static int qmp_pcie_pd_power_on(struct qmp_pcie *qmp)
> > +{
> > + const struct qmp_phy_cfg *cfg = qmp->cfg;
> > + int i, ret;
> > +
> > + for (i = 0; i < cfg->num_pds; i++) {
> > + ret = pm_runtime_get_sync(qmp->pd_devs[i]);
>
> In the error path you will put pd_devs[0..i-1], but
> pm_runtime_get_sync() requires that you put pd_devs[i] as well.
>
> Please use pm_runtime_resume_and_get() instead.
Okay, will switch to pm_runtime_resume_and_get() to avoid this imbalance.
- Qiang Yu
>
> > + if (ret < 0) {
> > + dev_err(qmp->dev, "failed to power on %s domain\n",
> > + cfg->pd_names[i]);
> > + goto err_power_off;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err_power_off:
> > + while (--i >= 0)
> > + pm_runtime_put(qmp->pd_devs[i]);
> > +
> > + return ret;
> > +}
>
> Regards,
> Bjorn
^ permalink raw reply
* Re: [PATCH v3 4/4] hwmon: (pmbus/max20830): add support for max20830c and max20840c
From: sashiko-bot @ 2026-07-20 3:29 UTC (permalink / raw)
To: Alexis Czezar Torreno; +Cc: robh, linux-hwmon, devicetree, conor+dt
In-Reply-To: <20260720-dev-max20830c-v3-4-9d06d27d6da3@analog.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Missing compatible strings for MAX20830C and MAX20840C in the of_match_table.
- [Medium] I2C block read path truncates the device ID for 9-character names, leading to potential false positive matches.
- [Low] struct i2c_device_id array uses positional instead of named initializers.
--
--- Patch [4]: [PATCH v3 4/4] hwmon: (pmbus/max20830): add support for max20830c and max20840c ---
commit dca1bbdb54e3f769bf22d26e97ead67754787a95
Author: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
hwmon: (pmbus/max20830): add support for max20830c and max20840c
Add support for MAX20830C and MAX20840 step-down DC-DC switching
regulator with PMBus interface. MAX20830C is a different packaging
for MAX20830, and MAX20840C supports 40A regulation compared to
MAX20830 that is only 30A.
> diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c
> index 7a5e67bd33c0a..73fe59685cf15 100644
> --- a/drivers/hwmon/pmbus/max20830.c
> +++ b/drivers/hwmon/pmbus/max20830.c
> @@ -98,13 +98,12 @@ static int max20830_probe(struct i2c_client *client)
[ ... ]
> } else {
> - /* Reads 1 length byte + 9 Data bytes from MAX20830 */
> + /* Reads 1 length byte + data bytes */
> ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID,
> MAX20830_IC_DEVICE_ID_LENGTH + 1,
> buf);
[Severity: Medium]
Since MAX20830C and MAX20840C have 9-character names, does this hardcoded
MAX20830_IC_DEVICE_ID_LENGTH + 1 truncate the newly added device IDs?
In max20830_probe(), if the I2C controller lacks SMBUS_READ_BLOCK_DATA support,
the fallback i2c_smbus_read_i2c_block_data() call uses
MAX20830_IC_DEVICE_ID_LENGTH. Because this length was initially sized for the
8-character "MAX20830" identifier, could it under-read the new longer device
IDs and cause false positive matches in the I2C fallback path?
[ ... ]
> @@ -120,26 +119,28 @@ static int max20830_probe(struct i2c_client *client)
[ ... ]
> static const struct i2c_device_id max20830_id[] = {
> - {"max20830"},
> + { "max20830" },
> + { "max20830c" },
> + { "max20840c" },
[Severity: High]
Are the compatible strings for max20830c and max20840c missing from the
max20830_of_match[] table?
Without adding them alongside the newly added i2c_device_id entries, could
this break module autoloading on systems using Device Tree when the OS attempts
to autoload the driver via the udev modalias?
[Severity: Low]
This isn't a bug, but do these entries violate the I2C subsystem coding style
by using positional initializers?
The I2C client guidelines require initialized arrays of type
struct i2c_device_id to use named initializers (e.g., { .name = "max20830c" }).
> { }
> };
> MODULE_DEVICE_TABLE(i2c, max20830_id);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com?part=4
^ permalink raw reply
* Re: [PATCH v5 0/3] Add QMP PCIe multiple link-mode PHY support
From: Qiang Yu @ 2026-07-20 3:29 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel,
Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260719-beautiful-pheasant-of-variation-61b8cd@quoll>
On Sun, Jul 19, 2026 at 08:43:43AM +0200, Krzysztof Kozlowski wrote:
> On Fri, Jul 17, 2026 at 02:58:32AM -0700, Qiang Yu wrote:
> > Hi all,
> >
> > Some QMP PCIe PHY hardware blocks can be configured through a link-mode
> > register into different link topologies, such as a single wide link, or
> > multiple independent narrower links (e.g. x8 or x4+x4 mode on the Glymur
> > PCIe3 PHY).
> >
> > Earlier revisions tried to extend the existing single-instance
> > phy-qcom-qmp-pcie.c driver to cover this hardware. That added a large
> > amount of conditional, multi-PHY logic to a driver whose data model
> > assumes one PHY per node.
> >
> > This series adds a dedicated PHY provider driver,
> > phy-qcom-qmp-pcie-multiphy.c:
> >
> > A single PHY provider node describes the shared hardware block. A new
> > "qcom,link-mode" property points at the TCSR syscon register that reports
> > the active topology; the driver reads it once at probe. #phy-cells = <1>
> > lets consumers pass a logical PHY index to obtain their sub-PHY. Match
> > data is indexed by link mode, and each link mode has its own array of
> > per-PHY config tables, so one shared provider exposes a different set of
> > logical PHYs depending on the active mode. The driver inherits the PHY
> > settings and link mode already programmed by UEFI, so only the no-CSR
> > reset is used, and no PHY setting tables or related structures are
> > defined. Each sub-PHY owns its register regions and power domain, so in a
> > bifurcated mode the links are brought up, and powered independently.
> >
> > The driver is implemented and validated on Glymur, and is intended to
> > be extensible to other multi-mode QMP PCIe PHYs.
> >
> > This series depends on a prerequisite patch by Krzysztof Kozlowski:
> > https://lore.kernel.org/r/20260420133616.88740-2-krzysztof.kozlowski@oss.qualcomm.com
>
> There was a v2, still not applied I think:
> https://lore.kernel.org/all/20260609141608.354186-2-krzysztof.kozlowski@oss.qualcomm.com/
Okay, will update the link to point to v2.
- Qiang Yu
^ permalink raw reply
* RE: [PATCH v3 1/4] dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples
From: Torreno, Alexis Czezar @ 2026-07-20 3:28 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev
Cc: devicetree@vger.kernel.org, linux-hwmon@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org
In-Reply-To: <20260720032207.E5A9A1F000E9@smtp.kernel.org>
>
> > @@ -61,6 +76,11 @@ examples:
> > compatible = "adi,max20830";
> > reg = <0x30>;
> > vddh-supply = <&vddh>;
> > + avdd-supply = <&avdd>;
> > + ldoin-supply = <&ldoin>;
> > + pwr-good-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
>
> [Severity: Low]
> Does the missing #include <dt-bindings/gpio/gpio.h> here cause make
> dt_binding_check to fail due to the unexpanded GPIO_ACTIVE_HIGH macro?
>
Missed something so basic...
Apologies for even carrying the review tag, I shouldn't do that.
^ permalink raw reply
* Re: [PATCH v5 1/3] dt-bindings: phy: qcom: Add Glymur QMP PCIe multiple link-mode PHY
From: Qiang Yu @ 2026-07-20 3:26 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel,
Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260719-sloppy-cornflower-hippo-8212e2@quoll>
On Sun, Jul 19, 2026 at 08:50:00AM +0200, Krzysztof Kozlowski wrote:
> On Fri, Jul 17, 2026 at 02:58:33AM -0700, Qiang Yu wrote:
> > diff --git a/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-pcie-multiphy.yaml b/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-pcie-multiphy.yaml
> > new file mode 100644
> > index 000000000000..73550f77e9b6
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-pcie-multiphy.yaml
>
> Filename: qcom,glymur-qmp-gen5x8-pcie-phy.yaml
>
Okay, will rename the file to qcom,glymur-qmp-gen5x8-pcie-phy.yaml.
> > @@ -0,0 +1,176 @@
> > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/phy/qcom,glymur-qmp-pcie-multiphy.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Qualcomm QMP PCIe multiple link-mode PHY controller (PCIe, Glymur)
> > +
> > +maintainers:
> > + - Qiang Yu <qiang.yu@oss.qualcomm.com>
> > + - Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> > +
> > +description: |
> > + The Glymur SoC uses a single PCIe Gen5 PHY hardware block for the
> > + PCIe3a/PCIe3b controllers. This block supports two link modes, selected
> > + at runtime via a TCSR syscon register:
> > +
> > + 1. x8 - a single 8-lane PHY instance is exposed (PCIe3a only)
> > + 2. x4+x4 - two independent 4-lane PHY instances are exposed (PCIe3a and
> > + PCIe3b)
> > +
> > + The node always describes both PHY instances ("port a" and "port b"),
> > + regardless of which link mode is active on the board.
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - qcom,glymur-qmp-gen5x8-pcie-phy
> > +
> > + reg:
> > + minItems: 2
>
> Dop
Will drop minItems: 2
>
> > + maxItems: 2
> > +
> > + reg-names:
> > + items:
> > + - const: port_a
> > + - const: port_b
> > +
> > + clocks:
> > + minItems: 10
>
> Drop
>
Will drop minItems: 10
> > + maxItems: 10
> > +
> > + clock-names:
> > + items:
> > + - const: aux
> > + - const: cfg_ahb
> > + - const: ref
> > + - const: rchng
> > + - const: pipe
> > + - const: phy_b_aux
> > + - const: cfg_ahb_b
> > + - const: rchng_b
> > + - const: pipe_b
> > + - const: pipediv2_b
> > +
> > + power-domains:
> > + minItems: 2
>
> Drop
Will drop minItems: 2
>
> > + maxItems: 2
> > +
> > + power-domain-names:
> > + items:
> > + - const: phy_a_gdsc
>
> port_a
Will rename it to port_a.
>
> > + - const: phy_b_gdsc
>
> port_b
Will rename it to port_b.
>
> > +
> > + resets:
> > + minItems: 4
>
> Drop
Will drop minItems: 4
>
> > + maxItems: 4
> > +
> > + reset-names:
> > + items:
> > + - const: phy_a
>
> port_a
Will rename it to port_a.
>
> > + - const: phy_a_nocsr
>
> port_a_nocsr
>
> etc.
Will rename them to port_a_nocsr, port_b and port_b_nocsr.
>
> > + - const: phy_b
> > + - const: phy_b_nocsr
> > +
> > + vdda-phy-supply: true
> > +
> > + vdda-pll-supply: true
> > +
> > + vdda-refgen0p9-supply: true
> > +
> > + vdda-refgen1p2-supply: true
> > +
> > + qcom,link-mode:
> > + description:
> > + Reference to a register in the TCSR syscon that reports the link
> > + mode the PCIe PHY is currently configured for, either a single x8
> > + link or two independent x4 links. The link mode is programmed by
> > + firmware before Linux boots; this property is only used to read
> > + the active link mode, specified as a phandle to the syscon and
> > + the register offset.
> > + $ref: /schemas/types.yaml#/definitions/phandle-array
> > + items:
> > + - items:
> > + - description: phandle of TCSR syscon
> > + - description: offset of link mode register
> > +
> > + "#clock-cells":
> > + const: 1
> > +
> > + clock-output-names:
> > + items:
> > + - description: Name of the PHY A pipe clock output.
> > + - description: Name of the PHY B pipe clock output.
> > +
> > + "#phy-cells":
> > + const: 1
>
> You should explain in description the meaning of cell value. Or provide
> filename of header with constants.
>
Okay, will add a description explaining the cell value in the next
version:
"#phy-cells":
const: 1
description:
The cell is the index of the sub-PHY within the active link
mode, e.g. on Glymur, 0 for "port a" and 1 for "port b" in
x4x4 mode, and only index 0 (the single wide PHY) is valid in
x8 mode.
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - reg-names
> > + - clocks
> > + - clock-names
> > + - power-domains
> > + - power-domain-names
> > + - resets
> > + - reset-names
> > + - vdda-phy-supply
> > + - vdda-pll-supply
> > + - vdda-refgen0p9-supply
> > + - vdda-refgen1p2-supply
> > + - qcom,link-mode
> > + - "#clock-cells"
> > + - clock-output-names
> > + - "#phy-cells"
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/qcom,glymur-gcc.h>
> > + #include <dt-bindings/clock/qcom,glymur-tcsr.h>
> > +
> > + pcie3_phy: phy@f00000 {
>
> Drop unused label.
>
Will drop the unused "pcie3_phy:" label from the example.
> The old 5x4 glymur compatible could be deprecated as well, but fine for
> me without doing so.
>
The old 5x4 glymur compatible is also required by PCIe5 PHY, so I'll leave
it as-is.
- Qiang Yu
^ permalink raw reply
* Re: [PATCH v3 2/4] hwmon: (pmbus/max20830): add VOUT feedback resistor scaling support
From: sashiko-bot @ 2026-07-20 3:25 UTC (permalink / raw)
To: Alexis Czezar Torreno; +Cc: linux-hwmon, conor+dt, devicetree, robh
In-Reply-To: <20260720-dev-max20830c-v3-2-9d06d27d6da3@analog.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The voltage divider scaling calculation suffers from a 32-bit addition overflow and an unsafe 64-bit to 32-bit signed integer truncation before clamping.
--
--- Patch [2]: [PATCH v3 2/4] hwmon: (pmbus/max20830): add VOUT feedback resistor scaling support ---
commit ac88893286286c633f117369db91c2cffd163afd
Author: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
hwmon: (pmbus/max20830): add VOUT feedback resistor scaling support
This commit implements support for external voltage divider scaling using
the adi,vout-rfb1-ohms and adi,vout-rfb2-ohms device tree properties. The
driver reads these values and applies the scaling formula to the raw VOUT
reading.
> diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c
> index cb2c23672166d..7a5e67bd33c0a 100644
> --- a/drivers/hwmon/pmbus/max20830.c
> +++ b/drivers/hwmon/pmbus/max20830.c
[ ... ]
> +static int max20830_read_word_data(struct i2c_client *client, int page,
> + int phase, int reg)
> +{
> + const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
> + const struct max20830_data *data = container_of(info, struct max20830_data, info);
> + int ret;
> +
> + switch (reg) {
> + case PMBUS_READ_VOUT:
> + ret = pmbus_read_word_data(client, page, phase, reg);
> + if (ret < 0)
> + return ret;
> +
> + /* Apply voltage divider scaling if resistors are non-zero */
> + if (data->vout_rfb1 && data->vout_rfb2) {
> + ret = DIV_ROUND_CLOSEST_ULL((u64)ret * (data->vout_rfb1 +
> + data->vout_rfb2), data->vout_rfb2);
> + ret = clamp_val(ret, 0, 0xFFFF);
[Severity: Medium]
Can this calculation suffer from a 32-bit addition overflow and an unsafe
64-bit to 32-bit signed integer truncation?
If data->vout_rfb1 and data->vout_rfb2 sum to more than 2^32 - 1, the 32-bit
addition will wrap around before the 64-bit multiplication.
Additionally, since ret is a signed 32-bit integer, if the 64-bit result
returned from DIV_ROUND_CLOSEST_ULL exceeds 2^31 - 1, the assignment to ret
will overflow into a negative integer. The clamp_val() macro will then
evaluate this negative number and incorrectly clamp it to 0 instead of
0xFFFF.
> + }
> + return ret;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com?part=2
^ permalink raw reply
* Re: [PATCH v2 1/6] dt-bindings: hwmon: add binding for adi,adt7470
From: Luiz Angelo Daros de Luca @ 2026-07-20 3:23 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-hwmon, devicetree, robh, conor+dt
In-Reply-To: <20260717210439.356FF1F000E9@smtp.kernel.org>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] The binding defines PWM output and thermal sensor provider capabilities but fails to include `#pwm-cells` and `#thermal-sensor-cells` in the `required` list.
> --
>
> --- Patch [1]: [PATCH v2 1/6] dt-bindings: hwmon: add binding for adi,adt7470 ---
> commit 6372aff696282f1ac0ec9a8f1372c186734704a2
> Author: Luiz Angelo Daros de Luca <luizluca@gmail.com>
>
> dt-bindings: hwmon: add binding for adi,adt7470
>
> The ADT7470 hardware monitoring chip supports PWM fan control and temperature
> monitoring. This commit introduces the YAML device tree binding schema for it,
> defining PWM and thermal sensor provider capabilities.
>
> > diff --git a/Documentation/devicetree/bindings/hwmon/adi,adt7470.yaml b/Documentation/devicetree/bindings/hwmon/adi,adt7470.yaml
> > new file mode 100644
> > index 0000000000000..14bb6359c91f2
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/hwmon/adi,adt7470.yaml
> [ ... ]
> > +properties:
> > + compatible:
> > + const: adi,adt7470
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + "#pwm-cells":
> > + const: 2
> > + description: |
> > + Number of cells in a PWM specifier.
> > + - The first cell is the PWM channel (0 to 3).
> > + - The second cell is the PWM period in nanoseconds.
> > +
> > + "#thermal-sensor-cells":
> > + const: 1
> > + description: |
> > + Number of cells required to uniquely identify the temperature sensors.
> > + Valid index values are 0 to 9, corresponding to temp1 through temp10.
> > +
> > +required:
> > + - compatible
> > + - reg
>
> [Severity: Medium]
> Should #pwm-cells and #thermal-sensor-cells be included in this required list?
>
> Because this binding defines the device as a PWM output and thermal sensor
> provider, omitting these cell properties from the required list allows
> incomplete device tree nodes to silently pass schema validation.
>
> If an adt7470 node is created without them, dependent drivers attempting to
> reference it as a provider may fail at runtime when parsing the provider
> specifier.
>
> Could both #pwm-cells and #thermal-sensor-cells be added to the required
> array to enforce complete node validation?
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260717-adt7470_thermalzone-v2-0-a55147958fad@gmail.com?part=1
It would be nice if Sashiko-bot could have previous submission threads
as input context. It would be a great way to avoid false-positives
like this.
The ADT7470 can be (and is, in real-world devices ) used as a PWM-only
device. Likewise, nothing prevents its use solely as an ADC for
temperature sensors. Because its use cases are modular, neither of
these provider properties should be strictly required in the binding.
Regards,
Luiz
^ permalink raw reply
* Re: [PATCH v13 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Wenmeng Liu @ 2026-07-20 3:23 UTC (permalink / raw)
To: Bryan O'Donoghue, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel
In-Reply-To: <20260720-x1e-csi2-phy-v13-2-160c31958863@linaro.org>
On 7/20/2026 9:11 AM, Bryan O'Donoghue wrote:
> Add a new MIPI CSI2 driver in DPHY mode initially. The entire set of
> existing CAMSS CSI PHY init sequences are imported in order to save time
> and effort in later patches.
>
> The following devices are supported in this drop:
> "qcom,x1e80100-csi2-phy"
>
> In-line with other PHY drivers the process node is included in the name.
> Data-lane and clock lane positioning and polarity selection via newly
> amended struct phy_configure_opts_mipi_dphy{} is supported.
>
> The Qualcomm 3PH class of PHYs can do both DPHY and CPHY mode. For now only
> DPHY is supported.
>
> In porting some of the logic over from camss-csiphy*.c to here its also
> possible to rationalise some of the code.
>
> In particular use of regulator_bulk and clk_bulk as well as dropping the
> seemingly useless and unused interrupt handler.
>
> The PHY sequences and a lot of the logic that goes with them are well
> proven in CAMSS and mature so the main thing to watch out for here is how
> to get the right sequencing of regulators, clocks and register-writes.
>
> The register init sequence table is imported verbatim from the existing
> CAMSS csiphy driver. A follow-up series will rework the table to extract
> the repetitive per-lane pattern into a loop.
>
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
> ---
> MAINTAINERS | 10 +
> drivers/phy/qualcomm/Kconfig | 15 +
> drivers/phy/qualcomm/Makefile | 5 +
> drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 385 ++++++++++++++++++
> drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 449 +++++++++++++++++++++
> drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 97 +++++
> 6 files changed, 961 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 15011f5752a99..a203b41475ea4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22296,6 +22296,16 @@ S: Maintained
> F: Documentation/devicetree/bindings/media/qcom,*-iris.yaml
> F: drivers/media/platform/qcom/iris/
>
> +QUALCOMM MIPI CSI2 PHY DRIVER
> +M: Bryan O'Donoghue <bod@kernel.org>
> +L: linux-phy@lists.infradead.org
> +L: linux-media@vger.kernel.org
> +L: linux-arm-msm@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/phy/qcom,*-csi2-phy.yaml
> +F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.c
> +F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.h
> +
> QUALCOMM NAND CONTROLLER DRIVER
> M: Manivannan Sadhasivam <mani@kernel.org>
> L: linux-mtd@lists.infradead.org
> diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
> index 60a0ead127fa9..cd6959e69bef7 100644
> --- a/drivers/phy/qualcomm/Kconfig
> +++ b/drivers/phy/qualcomm/Kconfig
> @@ -28,6 +28,21 @@ config PHY_QCOM_EDP
> Enable this driver to support the Qualcomm eDP PHY found in various
> Qualcomm chipsets.
>
> +config PHY_QCOM_MIPI_CSI2
> + tristate "Qualcomm MIPI CSI2 PHY driver"
> + depends on ARCH_QCOM || COMPILE_TEST
> + depends on OF
> + depends on PM
> + depends on PM_OPP
> + depends on COMMON_CLK
> + select GENERIC_PHY
> + select GENERIC_PHY_MIPI_DPHY
> + help
> + Enable this to support the MIPI CSI2 PHY driver found in various
> + Qualcomm chipsets. This PHY is used to connect MIPI CSI2
> + camera sensors to the CSI Decoder in the Qualcomm Camera Subsystem
> + CAMSS.
> +
> config PHY_QCOM_IPQ4019_USB
> tristate "Qualcomm IPQ4019 USB PHY driver"
> depends on OF && (ARCH_QCOM || COMPILE_TEST)
> diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
> index b71a6a0bed3f1..382cb594b06b6 100644
> --- a/drivers/phy/qualcomm/Makefile
> +++ b/drivers/phy/qualcomm/Makefile
> @@ -6,6 +6,11 @@ obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o
> obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
> obj-$(CONFIG_PHY_QCOM_M31_USB) += phy-qcom-m31.o
> obj-$(CONFIG_PHY_QCOM_M31_EUSB) += phy-qcom-m31-eusb2.o
> +
> +phy-qcom-mipi-csi2-objs += phy-qcom-mipi-csi2-core.o \
> + phy-qcom-mipi-csi2-3ph-dphy.o
> +obj-$(CONFIG_PHY_QCOM_MIPI_CSI2) += phy-qcom-mipi-csi2.o
> +
> obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o
>
> obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o phy-qcom-qmp-usbc.o
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> new file mode 100644
> index 0000000000000..966d79c98f9b2
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> @@ -0,0 +1,385 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0
> + *
> + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2016-2026 Linaro Ltd.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/time64.h>
> +
> +#include "phy-qcom-mipi-csi2.h"
> +
> +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n))
> +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL0_PHY_SW_RESET BIT(0)
> +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7)
> +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
> +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
> +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL10_IRQ_CLEAR_CMD BIT(0)
> +#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n))
> +
> +#define CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(n) ((0x200 * (n)) + 0x24)
> +
> +/*
> + * 3 phase CSI has 19 common status regs with only 0-10 being used
> + * and 11-18 being reserved.
> + */
> +#define CSI_COMMON_STATUS_NUM 11
> +/*
> + * There are a number of common control registers
> + * The offset to clear the CSIPHY IRQ status starts @ 22
> + * So to clear CSI_COMMON_STATUS0 this is CSI_COMMON_CONTROL22, STATUS1 is
> + * CONTROL23 and so on
> + */
> +#define CSI_CTRL_STATUS_INDEX 22
> +
> +/*
> + * There are 43 COMMON_CTRL registers with regs after # 33 being reserved
> + */
> +#define CSI_CTRL_MAX 33
> +
> +#define CSIPHY_DEFAULT_PARAMS 0
> +#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
> +#define CSIPHY_SKEW_CAL 7
> +
> +/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */
> +static const struct
> +mipi_csi2phy_lane_regs lane_regs_x1e80100[] = {
> + /* Power up lanes 2ph mode */
> + {.reg_addr = 0x101c, .reg_data = 0x7a, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x1018, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> + {.reg_addr = 0x0094, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x00a0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0090, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0098, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0094, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0030, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0000, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0038, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x002c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0034, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x001c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0014, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x003c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0004, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0020, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0008, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> + {.reg_addr = 0x0010, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0094, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x005c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0060, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0064, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
> +
> + {.reg_addr = 0x0e94, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0ea0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e90, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e98, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e94, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e30, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e28, .reg_data = 0x04, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e00, .reg_data = 0x80, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e0c, .reg_data = 0xff, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e38, .reg_data = 0x1f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e2c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e34, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e1c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e14, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e3c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e04, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e20, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0e08, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> + {.reg_addr = 0x0e10, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> + {.reg_addr = 0x0494, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x04a0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0490, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0498, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0494, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0430, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0400, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0438, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x042c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0434, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x041c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0414, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x043c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0404, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0420, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0408, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> + {.reg_addr = 0x0410, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0494, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x045c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0460, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0464, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
> +
> + {.reg_addr = 0x0894, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x08a0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0890, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0898, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0894, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0830, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0800, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0838, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x082c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0834, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x081c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0814, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x083c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0804, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0820, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0808, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> + {.reg_addr = 0x0810, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0894, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x085c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0860, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0864, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
> +
> + {.reg_addr = 0x0c94, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0ca0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c90, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c98, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c94, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c30, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c00, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c38, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c2c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c34, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c1c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c14, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c3c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c04, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c20, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c08, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> + {.reg_addr = 0x0c10, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
> + {.reg_addr = 0x0c94, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0c5c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0c60, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
> + {.reg_addr = 0x0c64, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
> +};
> +
> +static inline const struct mipi_csi2phy_device_regs *
> +csi2phy_dev_to_regs(struct mipi_csi2phy_device *csi2phy)
> +{
> + return &csi2phy->soc_cfg->reg_info;
> +}
> +
> +static void phy_qcom_mipi_csi2_hw_version_read(struct mipi_csi2phy_device *csi2phy)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> + u32 tmp;
> +
> + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
> +
> + tmp = readl_relaxed(csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 12));
> + csi2phy->hw_version = tmp;
> +
> + tmp = readl_relaxed(csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 13));
> + csi2phy->hw_version |= (tmp << 8) & 0xFF00;
> +
> + tmp = readl_relaxed(csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 14));
> + csi2phy->hw_version |= (tmp << 16) & 0xFF0000;
> +
> + tmp = readl_relaxed(csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 15));
> + csi2phy->hw_version |= (tmp << 24) & 0xFF000000;
> +
> + dev_dbg_once(csi2phy->dev, "CSIPHY 3PH HW Version = 0x%08x\n", csi2phy->hw_version);
> +}
> +
> +/*
> + * phy_qcom_mipi_csi2_reset - Perform software reset on CSIPHY module
> + * @phy_qcom_mipi_csi2: CSIPHY device
> + */
> +static void phy_qcom_mipi_csi2_reset(struct mipi_csi2phy_device *csi2phy)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> +
> + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL0_PHY_SW_RESET,
> + csi2phy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
> + usleep_range(5000, 8000);
> + writel(0x0, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
> +}
> +
> +/*
> + * phy_qcom_mipi_csi2_settle_cnt_calc - Calculate settle count value
> + *
> + * Helper function to calculate settle count value. This is
> + * based on the CSI2 T_hs_settle parameter which in turn
> + * is calculated based on the CSI2 transmitter link frequency.
> + *
> + * Return settle count.
> + */
> +static u8 phy_qcom_mipi_csi2_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
> +{
> + u32 t_hs_prepare_max_ps;
> + u32 timer_period_ps;
> + u32 t_hs_settle_ps;
> + u8 settle_cnt;
> + u32 ui_ps;
> +
> + ui_ps = div64_u64(PSEC_PER_SEC, link_freq);
> + ui_ps /= 2;
> + t_hs_prepare_max_ps = 85000 + 6 * ui_ps;
> + t_hs_settle_ps = t_hs_prepare_max_ps;
> +
> + timer_period_ps = div_u64(PSEC_PER_SEC, timer_clk_rate);
> +
> + if ((t_hs_settle_ps / timer_period_ps) < 6)
> + return 0;
> +
> + settle_cnt = t_hs_settle_ps / timer_period_ps - 6;
> +
> + return settle_cnt;
> +}
> +
> +static void
> +phy_qcom_mipi_csi2_gen2_config_lanes(struct mipi_csi2phy_device *csi2phy,
> + u8 settle_cnt)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> + const struct mipi_csi2phy_lane_regs *r = regs->init_seq;
> + int i, array_size = regs->lane_array_size;
> + u32 val;
> +
> + for (i = 0; i < array_size; i++, r++) {
> + switch (r->param_type) {
> + case CSIPHY_SETTLE_CNT_LOWER_BYTE:
> + val = settle_cnt & 0xff;
> + break;
> + case CSIPHY_SKEW_CAL:
> + /* TODO: support application of skew from dt flag */
> + continue;
> + default:
> + val = r->reg_data;
> + break;
> + }
> + writel(val, csi2phy->base + r->reg_addr);
> + if (r->delay_us)
> + udelay(r->delay_us);
> + }
> +}
> +
> +static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
> + struct mipi_csi2phy_stream_cfg *cfg)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> + struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
> + u8 settle_cnt;
> + u8 val;
> + int i;
> +
> + if (cfg->link_freq <= 0)
> + return -EINVAL;
> +
> + settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
> + if (!settle_cnt)
> + return -ENODEV;
> +
> + /*
> + * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
> + * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
> + * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
> + * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
> + */
> + val = BIT(lane_cfg->clk.pos);
> + for (i = 0; i < cfg->num_data_lanes; i++)
> + val |= BIT(lane_cfg->data[i].pos * 2);
> +
> + writel(val, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
> +
> + /* Lane configuration for polarity @ CSIPHY-base + CTRL9 */
> + for (i = 0; i < cfg->num_data_lanes; i++) {
> + if (lane_cfg->data[i].pol) {
> + u8 pos = lane_cfg->data[i].pos;
> +
> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos * 2));
> + }
> + }
> +
> + if (lane_cfg->clk.pol)
> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(lane_cfg->clk.pos));
> +
> + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
> + writel(val, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
> +
> + val = 0x02;
> + writel(val, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 7));
> +
> + val = 0x00;
> + writel(val, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
> +
> + phy_qcom_mipi_csi2_gen2_config_lanes(csi2phy, settle_cnt);
> +
> + /* IRQ_MASK registers - disable all interrupts */
> + for (i = CSI_COMMON_STATUS_NUM; i < CSI_CTRL_STATUS_INDEX; i++) {
> + writel(0, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, i));
> + }
> +
> + return 0;
> +}
> +
> +static void
> +phy_qcom_mipi_csi2_lanes_disable(struct mipi_csi2phy_device *csi2phy,
> + struct mipi_csi2phy_stream_cfg *cfg)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> +
> + writel(0, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
> +
> + writel(0, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
> +}
> +
> +static const struct mipi_csi2phy_hw_ops phy_qcom_mipi_csi2_ops_3ph_1_0 = {
> + .hw_version_read = phy_qcom_mipi_csi2_hw_version_read,
> + .reset = phy_qcom_mipi_csi2_reset,
> + .lanes_enable = phy_qcom_mipi_csi2_lanes_enable,
> + .lanes_disable = phy_qcom_mipi_csi2_lanes_disable,
> +};
> +
> +static const char * const x1e_clks[] = {
> + "core",
> + "timer",
> + "ahb"
> +};
> +
> +static const char * const x1e_supplies[] = {
> + "vdda-0p9",
> + "vdda-1p2"
> +};
> +
> +static struct mipi_csi2_genpd x1e_genpds[] = {
> + { .name = "top", .scaled = false },
> + { .name = "mmcx", .scaled = true },
> + { .name = "mx", .scaled = true },
> +};
> +
> +const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e = {
> + .ops = &phy_qcom_mipi_csi2_ops_3ph_1_0,
> + .reg_info = {
> + .init_seq = lane_regs_x1e80100,
> + .lane_array_size = ARRAY_SIZE(lane_regs_x1e80100),
> + .common_regs_offset = 0x1000,
> + },
> + .supply_names = (const char **)x1e_supplies,
> + .num_supplies = ARRAY_SIZE(x1e_supplies),
> + .clk_names = (const char **)x1e_clks,
> + .num_clk = ARRAY_SIZE(x1e_clks),
> + .genpds = x1e_genpds,
> + .num_genpds = ARRAY_SIZE(x1e_genpds),
> +};
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
> new file mode 100644
> index 0000000000000..aae049f0ec160
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
> @@ -0,0 +1,449 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2026, Linaro Ltd.
> + */
> +
> +#include <dt-bindings/phy/phy.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/pm_opp.h>
> +#include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include "phy-qcom-mipi-csi2.h"
> +
> +static int
> +phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
> + s64 link_freq)
> +{
> + struct device *dev = csi2phy->dev;
> + unsigned long opp_rate = link_freq / 4;
> + struct dev_pm_opp *opp;
> + long timer_rate;
> + int i, pstate;
> + int ret;
> +
> + opp = dev_pm_opp_find_freq_ceil(dev, &opp_rate);
> + if (IS_ERR(opp)) {
> + dev_err(csi2phy->dev, "Couldn't find ceiling for %lld Hz\n",
> + link_freq);
> + return PTR_ERR(opp);
> + }
> +
> + pstate = 0;
> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
> + unsigned int perf;
> +
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + perf = dev_pm_opp_get_required_pstate(opp, pstate);
> + pstate += 1;
> +
> + ret = dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], perf);
> + if (ret) {
> + dev_err(csi2phy->dev, "Couldn't set perf state %u\n",
> + perf);
> + dev_pm_opp_put(opp);
> + goto unset_pstate;
> + }
> + }
> + dev_pm_opp_put(opp);
> +
> + ret = dev_pm_opp_set_rate(dev, opp_rate);
> + if (ret) {
> + dev_err(csi2phy->dev, "dev_pm_opp_set_rate() fail\n");
> + goto unset_opp_rate;
> + }
> +
> + timer_rate = clk_round_rate(csi2phy->timer_clk, link_freq / 4);
> + if (timer_rate <= 0) {
> + ret = -ENODEV;
> + goto unset_opp_rate;
> + }
> +
> + ret = clk_set_rate(csi2phy->timer_clk, timer_rate);
> + if (ret)
> + goto unset_opp_rate;
> +
> + csi2phy->timer_clk_rate = timer_rate;
> +
> + return 0;
> +
> +unset_opp_rate:
> + dev_pm_opp_set_rate(dev, 0);
> +
> +unset_pstate:
> + while (i--) {
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
> + }
> +
> + return ret;
> +}
> +
> +static int phy_qcom_mipi_csi2_configure(struct phy *phy,
> + union phy_configure_opts *opts)
> +{
> + struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
> + struct phy_configure_opts_mipi_dphy *dphy_cfg = &opts->mipi_dphy;
> + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
> + int ret;
> +
> + ret = phy_mipi_dphy_config_validate(dphy_cfg);
> + if (ret)
> + return ret;
> +
> + if (dphy_cfg->lanes < 1 || dphy_cfg->lanes > CSI2_MAX_DATA_LANES)
> + return -EINVAL;
> +
> + stream_cfg->link_freq = dphy_cfg->hs_clk_rate;
> +
> + return 0;
> +}
> +
> +static int phy_qcom_mipi_csi2_power_on(struct phy *phy)
> +{
> + struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
> + const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops;
> + int i, ret;
> +
> + ret = regulator_bulk_enable(csi2phy->soc_cfg->num_supplies,
> + csi2phy->supplies);
> + if (ret)
> + return ret;
> +
> + ret = pm_runtime_resume_and_get(csi2phy->dev);
> + if (ret < 0)
> + goto disable_regulators;
> +
> + ret = phy_qcom_mipi_csi2_set_clock_rates(csi2phy, csi2phy->stream_cfg.link_freq);
> + if (ret)
> + goto poweroff_phy;
> +
> + ret = clk_bulk_prepare_enable(csi2phy->soc_cfg->num_clk,
> + csi2phy->clks);
> + if (ret) {
> + dev_err(csi2phy->dev, "failed to enable clocks, %d\n", ret);
> + goto unset_rate;
> + }
> +
> + ops->reset(csi2phy);
> +
> + ops->hw_version_read(csi2phy);
> +
> + ret = ops->lanes_enable(csi2phy, &csi2phy->stream_cfg);
> + if (ret)
> + goto unset_clocks;
> +
> + return 0;
> +
> +unset_clocks:
> + clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
> + csi2phy->clks);
> +
> +unset_rate:
> + dev_pm_opp_set_rate(csi2phy->dev, 0);
> +
> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
> + }
> +
> +poweroff_phy:
> + pm_runtime_put_sync(csi2phy->dev);
> +
> +disable_regulators:
> + regulator_bulk_disable(csi2phy->soc_cfg->num_supplies,
> + csi2phy->supplies);
> +
> + return ret;
> +}
> +
> +static int phy_qcom_mipi_csi2_power_off(struct phy *phy)
> +{
> + struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
> + const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops;
> + int i;
> +
> + ops->lanes_disable(csi2phy, &csi2phy->stream_cfg);
> +
> + clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
> + csi2phy->clks);
> +
> + dev_pm_opp_set_rate(csi2phy->dev, 0);
> +
> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
> + }
> +
> + pm_runtime_put_sync(csi2phy->dev);
> +
> + regulator_bulk_disable(csi2phy->soc_cfg->num_supplies,
> + csi2phy->supplies);
> +
> + return 0;
> +}
> +
> +static const struct phy_ops phy_qcom_mipi_csi2_ops = {
> + .configure = phy_qcom_mipi_csi2_configure,
> + .power_on = phy_qcom_mipi_csi2_power_on,
> + .power_off = phy_qcom_mipi_csi2_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct phy *qcom_csi2_phy_xlate(struct device *dev,
> + const struct of_phandle_args *args)
> +{
> + struct mipi_csi2phy_device *csi2phy = dev_get_drvdata(dev);
> +
> + if (args->args_count < 1 || args->args[0] != PHY_TYPE_DPHY) {
> + dev_err(csi2phy->dev, "invalid phy mode in DTB\n");
> + return ERR_PTR(-EOPNOTSUPP);
> + }
> +
> + csi2phy->phy_mode = args->args[0];
> +
> + return csi2phy->phy;
> +}
> +
> +static int phy_qcom_mipi_csi2_attach_pm_domains(struct mipi_csi2phy_device *csi2phy)
> +{
> + struct dev_pm_domain_attach_data pd_data = { 0 };
> + const char **pd_names;
> + int i;
> +
> + pd_names = devm_kzalloc(csi2phy->dev,
> + sizeof(char *) * csi2phy->soc_cfg->num_genpds,
> + GFP_KERNEL);
> + if (!pd_names)
> + return -ENOMEM;
> +
> + for (i = 0; i < csi2phy->soc_cfg->num_genpds; i++)
> + pd_names[i] = csi2phy->soc_cfg->genpds[i].name;
> +
> + pd_data.pd_names = pd_names;
> + pd_data.num_pd_names = csi2phy->soc_cfg->num_genpds;
> +
> + return devm_pm_domain_attach_list(csi2phy->dev, &pd_data,
> + &csi2phy->pd_list);
> +}
> +
> +static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
> +{
> + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
> + u32 lane_polarities[CSI2_MAX_DATA_LANES + 1];
> + u32 data_lanes[CSI2_MAX_DATA_LANES];
> + struct device *dev = csi2phy->dev;
> + struct fwnode_handle *ep;
> + int num_polarities;
> + int num_data_lanes;
> + int i, ret;
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 1, 0);
> + if (ep) {
> + fwnode_handle_put(ep);
> + dev_err(dev, "DPHY split mode is not supported\n");
> + return -EOPNOTSUPP;
> + }
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> + if (!ep) {
> + dev_err(dev, "Missing port@0\n");
> + return -ENODEV;
> + }
> +
> + num_data_lanes = fwnode_property_count_u32(ep, "data-lanes");
> + if (num_data_lanes < 1 || num_data_lanes > CSI2_MAX_DATA_LANES) {
> + ret = -EINVAL;
> + dev_err(dev, "Invalid data-lanes count: %d\n", num_data_lanes);
> + goto out_put;
> + }
> + stream_cfg->num_data_lanes = num_data_lanes;
> +
> + ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes,
> + stream_cfg->num_data_lanes);
> + if (ret) {
> + dev_err(dev, "Failed to read data-lanes: %d\n", ret);
> + goto out_put;
> + }
> +
> + /* lane-polarities: optional, up to num_data_lanes + 1 entries */
> + memset(lane_polarities, 0x00, sizeof(lane_polarities));
> + num_polarities = fwnode_property_count_u32(ep, "lane-polarities");
> + if (num_polarities > 0) {
> + if (num_polarities != stream_cfg->num_data_lanes + 1) {
> + ret = -EINVAL;
> + dev_err(dev, "clock+data-lane %d/polarities %d mismatch\n",
> + stream_cfg->num_data_lanes + 1, num_polarities);
> + goto out_put;
> + }
> +
> + ret = fwnode_property_read_u32_array(ep, "lane-polarities", lane_polarities,
> + num_polarities);
> + if (ret) {
> + dev_err(dev, "Failed to read lane-polarities: %d\n", ret);
> + goto out_put;
> + }
> + }
> +
> + csi2phy->stream_cfg.lane_cfg.clk.pos = CSI2_DEFAULT_CLK_LANE;
> + csi2phy->stream_cfg.lane_cfg.clk.pol = lane_polarities[0];
> +
> + for (i = 0; i < csi2phy->stream_cfg.num_data_lanes; i++) {
> + if (data_lanes[i] >= CSI2_MAX_DATA_LANES) {
> + dev_err(dev, "Invalid lane %d\n", data_lanes[i]);
> + ret = -EINVAL;
> + goto out_put;
> + }
> + csi2phy->stream_cfg.lane_cfg.data[i].pos = data_lanes[i];
> + csi2phy->stream_cfg.lane_cfg.data[i].pol = lane_polarities[i + 1];
> + }
> +
> + ret = 0;
> +
> +out_put:
> + fwnode_handle_put(ep);
> +
> + return ret;
> +}
> +
> +static int phy_qcom_mipi_csi2_probe(struct platform_device *pdev)
> +{
> + unsigned int i, num_clk, num_supplies;
> + struct mipi_csi2phy_device *csi2phy;
> + struct phy_provider *phy_provider;
> + struct device *dev = &pdev->dev;
> + struct phy *generic_phy;
> + int ret;
> +
> + csi2phy = devm_kzalloc(dev, sizeof(*csi2phy), GFP_KERNEL);
> + if (!csi2phy)
> + return -ENOMEM;
> +
> + csi2phy->dev = dev;
> + dev_set_drvdata(dev, csi2phy);
> +
> + csi2phy->soc_cfg = device_get_match_data(&pdev->dev);
> +
> + if (!csi2phy->soc_cfg)
> + return -EINVAL;
> +
> + num_clk = csi2phy->soc_cfg->num_clk;
> +
> + ret = phy_qcom_mipi_csi2_parse_routing(csi2phy);
> + if (ret)
> + return ret;
> +
> + ret = phy_qcom_mipi_csi2_attach_pm_domains(csi2phy);
> + if (ret < 0 || csi2phy->pd_list == NULL) {
> + if (ret == 0)
> + ret = -ENODEV;
> + return dev_err_probe(dev, ret, "Failed to attach power-domain list\n");
> + }
> +
> + ret = devm_pm_runtime_enable(dev);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
> +
> + ret = devm_clk_bulk_get_all(dev, &csi2phy->clks);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to get clocks\n");
> +
> + if (num_clk != ret) {
> + return dev_err_probe(dev, -ENODEV, "clock count %d expected %d\n",
> + ret, num_clk);
> + }
> +
> + for (i = 0; i < num_clk; i++) {
> + if (!csi2phy->clks[i].id)
> + return dev_err_probe(dev, -EINVAL, "Missing clock-names\n");
> +
> + if (!strcmp(csi2phy->clks[i].id, "timer")) {
> + csi2phy->timer_clk = csi2phy->clks[i].clk;
> + break;
> + }
> + }
> + if (!csi2phy->timer_clk)
> + return dev_err_probe(dev, -ENODEV, "no timer clock\n");
> +
> + ret = devm_pm_opp_set_clkname(dev, "core");
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set opp clkname\n");
> +
> + ret = devm_pm_opp_of_add_table(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "invalid OPP table in device tree\n");
> +
> + num_supplies = csi2phy->soc_cfg->num_supplies;
> + csi2phy->supplies = devm_kzalloc(dev, sizeof(*csi2phy->supplies) * num_supplies,
> + GFP_KERNEL);
> + if (!csi2phy->supplies)
> + return -ENOMEM;
> +
> + for (i = 0; i < num_supplies; i++)
> + csi2phy->supplies[i].supply = csi2phy->soc_cfg->supply_names[i];
> +
> + ret = devm_regulator_bulk_get(dev, num_supplies, csi2phy->supplies);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "failed to get regulator supplies\n");
> +
> + csi2phy->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(csi2phy->base))
> + return PTR_ERR(csi2phy->base);
> +
> + generic_phy = devm_phy_create(dev, NULL, &phy_qcom_mipi_csi2_ops);
> + if (IS_ERR(generic_phy)) {
> + ret = PTR_ERR(generic_phy);
> + return dev_err_probe(dev, ret, "failed to create phy\n");
> + }
> + csi2phy->phy = generic_phy;
> +
> + phy_set_drvdata(generic_phy, csi2phy);
> +
> + phy_provider = devm_of_phy_provider_register(dev, qcom_csi2_phy_xlate);
> + if (!IS_ERR(phy_provider))
> + dev_dbg(dev, "Registered MIPI CSI2 PHY device\n");
> +
> + return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static const struct of_device_id phy_qcom_mipi_csi2_of_match_table[] = {
> + { .compatible = "qcom,x1e80100-csi2-phy", .data = &mipi_csi2_dphy_4nm_x1e },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, phy_qcom_mipi_csi2_of_match_table);
> +
> +static struct platform_driver phy_qcom_mipi_csi2_driver = {
> + .probe = phy_qcom_mipi_csi2_probe,
> + .driver = {
> + .name = "qcom-mipi-csi2-phy",
> + .of_match_table = phy_qcom_mipi_csi2_of_match_table,
> + },
> +};
> +
> +module_platform_driver(phy_qcom_mipi_csi2_driver);
> +
> +MODULE_DESCRIPTION("Qualcomm MIPI CSI2 PHY driver");
> +MODULE_AUTHOR("Bryan O'Donoghue <bod@kernel.org>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
> new file mode 100644
> index 0000000000000..7e55ae0073704
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
> @@ -0,0 +1,97 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + *
> + * Qualcomm MIPI CSI2 CPHY/DPHY driver
> + *
> + * Copyright (C) 2025 Linaro Ltd.
> + */
> +#ifndef __PHY_QCOM_MIPI_CSI2_H__
> +#define __PHY_QCOM_MIPI_CSI2_H__
> +
> +#include <linux/phy/phy.h>
> +
> +#define CSI2_MAX_DATA_LANES 4
> +#define CSI2_DEFAULT_CLK_LANE 7
> +
> +struct mipi_csi2phy_lane {
> + u8 pos;
> + u8 pol;
> +};
> +
> +struct mipi_csi2phy_lanes_cfg {
> + struct mipi_csi2phy_lane data[CSI2_MAX_DATA_LANES];
> + struct mipi_csi2phy_lane clk;
> +};
> +
> +struct mipi_csi2phy_stream_cfg {
> + s64 link_freq;
> + u8 num_data_lanes;
> + struct mipi_csi2phy_lanes_cfg lane_cfg;
> +};
> +
> +struct mipi_csi2phy_device;
> +
> +struct mipi_csi2phy_hw_ops {
> + void (*hw_version_read)(struct mipi_csi2phy_device *csi2phy_dev);
> + void (*reset)(struct mipi_csi2phy_device *csi2phy_dev);
> + int (*lanes_enable)(struct mipi_csi2phy_device *csi2phy_dev,
> + struct mipi_csi2phy_stream_cfg *cfg);
> + void (*lanes_disable)(struct mipi_csi2phy_device *csi2phy_dev,
> + struct mipi_csi2phy_stream_cfg *cfg);
> +};
> +
> +struct mipi_csi2phy_lane_regs {
> + const s32 reg_addr;
> + const s32 reg_data;
> + const u32 delay_us;
> + const u32 param_type;
> +};
> +
> +struct mipi_csi2phy_device_regs {
> + const struct mipi_csi2phy_lane_regs *init_seq;
> + const int lane_array_size;
> + const u32 common_regs_offset;
> +};
> +
> +struct mipi_csi2_genpd {
> + const char *name;
> + bool scaled;
> +};
> +
> +struct mipi_csi2phy_soc_cfg {
> + const struct mipi_csi2phy_hw_ops *ops;
> + const struct mipi_csi2phy_device_regs reg_info;
> +
> + const char ** const supply_names;
> + const unsigned int num_supplies;
> +
> + const char ** const clk_names;
> + const unsigned int num_clk;
> +
> + const struct mipi_csi2_genpd *genpds;
> + const unsigned int num_genpds;
> +};
> +
> +struct mipi_csi2phy_device {
> + struct device *dev;
> + u8 phy_mode;
> +
> + struct phy *phy;
> + void __iomem *base;
> +
> + struct clk_bulk_data *clks;
> + struct clk *timer_clk;
> + u32 timer_clk_rate;
> +
> + struct regulator_bulk_data *supplies;
> + struct dev_pm_domain_list *pd_list;
> +
> + const struct mipi_csi2phy_soc_cfg *soc_cfg;
> + struct mipi_csi2phy_stream_cfg stream_cfg;
> +
> + u32 hw_version;
> +};
> +
> +extern const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e;
> +
> +#endif /* __PHY_QCOM_MIPI_CSI2_H__ */
>
Tested-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com> # Hamoa Evk
^ permalink raw reply
* Re: [PATCH v3 1/4] dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples
From: sashiko-bot @ 2026-07-20 3:22 UTC (permalink / raw)
To: Alexis Czezar Torreno; +Cc: devicetree, linux-hwmon, robh, conor+dt
In-Reply-To: <20260720-dev-max20830c-v3-1-9d06d27d6da3@analog.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing `#include <dt-bindings/gpio/gpio.h>` in the examples section causes `make dt_binding_check` to fail.
--
--- Patch [1]: [PATCH v3 1/4] dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples ---
commit f18524325452b02a7accd64c49a8b82f9362d94c
Author: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples
This commit adds properties for external voltage divider configuration for
VOUT sensing (adi,vout-rfb1-ohms and adi,vout-rfb2-ohms) to the MAX20830
binding. It also adds missing entries to the example device tree node.
> diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> --- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
[ ... ]
> @@ -61,6 +76,11 @@ examples:
> compatible = "adi,max20830";
> reg = <0x30>;
> vddh-supply = <&vddh>;
> + avdd-supply = <&avdd>;
> + ldoin-supply = <&ldoin>;
> + pwr-good-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
[Severity: Low]
Does the missing #include <dt-bindings/gpio/gpio.h> here cause make
dt_binding_check to fail due to the unexpanded GPIO_ACTIVE_HIGH macro?
> + adi,vout-rfb1-ohms = <10000>;
> + adi,vout-rfb2-ohms = <2000>;
> };
> };
> ...
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com?part=1
^ permalink raw reply
* Re: [PATCH v3 3/4] dt-bindings: hwmon: (pmbus/max20830): add max20830c and max20840c support
From: sashiko-bot @ 2026-07-20 3:21 UTC (permalink / raw)
To: Alexis Czezar Torreno; +Cc: linux-hwmon, devicetree, conor+dt, robh
In-Reply-To: <20260720-dev-max20830c-v3-3-9d06d27d6da3@analog.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The max20830 driver will reject the newly added MAX20840C device during probe due to a hardcoded IC_DEVICE_ID check.
--
--- Patch [3]: [PATCH v3 3/4] dt-bindings: hwmon: (pmbus/max20830): add max20830c and max20840c support ---
commit b032b17e50351faefc688ad31e1428a3c641197a
Author: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
dt-bindings: hwmon: (pmbus/max20830): add max20830c and max20840c support
This commit adds compatible strings for the MAX20830C and MAX20840C variants
to the max20830 device tree binding, using a fallback to adi,max20830.
> diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> index 214adf1a3b9e9..b7d3b6152b582 100644
> --- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
> @@ -22,7 +22,13 @@ allOf:
>
> properties:
> compatible:
> - const: adi,max20830
> + oneOf:
> + - const: adi,max20830
> + - items:
> + - enum:
> + - adi,max20830c
> + - adi,max20840c
[Severity: Medium]
Will the driver correctly probe for the 'adi,max20840c' variant?
Looking at max20830_probe() in drivers/hwmon/pmbus/max20830.c, there is a
hardcoded string comparison for the device ID:
drivers/hwmon/pmbus/max20830.c:max20830_probe() {
...
if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1))
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported device: '%s'\n", buf);
...
}
If a MAX20840C device is probed via the 'adi,max20830' fallback, it seems the
buffer will start with "MAX20840", causing this check to fail and return
-ENODEV.
Does the driver need to be updated to recognize this new device ID so it does
not reject the device?
> + - const: adi,max20830
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com?part=3
^ permalink raw reply
* Re: [PATCH v6 2/3] i2c: ma35d1: Add Nuvoton MA35D1 I2C driver support
From: sashiko-bot @ 2026-07-20 3:17 UTC (permalink / raw)
To: Zi-Yu Chen; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260720030616.1067660-3-zychennvt@gmail.com>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Lack of synchronization between the I2C transfer timeout path and the controller interrupt handler leads to Use-After-Free and data corruption.
- [High] Lockless target unregistration and unconditional dereferences of `i2c->target` crash the kernel on stray target interrupts.
- [High] Zero-length message transfers (`num == 0`) cause an immediate false-success return while leaving hardware running, leading to out-of-bounds memory access.
- [High] Hardware reset error path fails to restore the Interrupt Enable bit, permanently wedging the I2C controller.
- [High] Writing `MA35_CTL_SI_AA` at the end of a master transfer silently clears and drops pending target mode interrupts.
- [High] The IRQ handler accesses hardware registers without verifying the device PM state, risking an external abort on wake.
- [High] The driver relies exclusively on PM runtime to enable its clock but lacks a Kconfig dependency on `CONFIG_PM`.
--
--- Patch [2]: [PATCH v6 2/3] i2c: ma35d1: Add Nuvoton MA35D1 I2C driver support ---
commit ed59c57642f41c98381c1bf982fdec793de595c9
Author: Zi-Yu Chen <zychennvt@gmail.com>
i2c: ma35d1: Add Nuvoton MA35D1 I2C driver support
Add I2C support for Nuvoton MA35D1 SoC.
The controller supports standard, fast and fast-plus modes,
and provides controller/target functionality.
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index d354569942803..e6c8f17130dbb 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -1602,4 +1602,17 @@ config I2C_VIRTIO
> This driver can also be built as a module. If so, the module
> will be called i2c-virtio.
>
> +config I2C_MA35D1
> + tristate "Nuvoton MA35D1 I2C driver"
> + depends on ARCH_MA35 || COMPILE_TEST
[Severity: High]
Does this code need a depends on PM or a fallback for when CONFIG_PM is
disabled?
The driver relies exclusively on PM runtime in ma35d1_i2c_runtime_resume() to
enable its clock via clk_enable(). If the kernel is built without CONFIG_PM,
the pm_runtime_resume_and_get() calls succeed immediately without invoking
the callback, leaving the clock disabled and potentially causing hardware
faults on register access.
> + select I2C_SLAVE
> + help
[ ... ]
> diff --git a/drivers/i2c/busses/i2c-ma35d1.c b/drivers/i2c/busses/i2c-ma35d1.c
> new file mode 100644
> index 0000000000000..4ac49b1b22e00
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-ma35d1.c
> @@ -0,0 +1,780 @@
[ ... ]
> +static void ma35d1_i2c_reset(struct ma35d1_i2c *i2c)
> +{
> + unsigned int clkdiv, slvaddr;
> +
> + clkdiv = readl(i2c->regs + MA35_CLKDIV);
> + slvaddr = readl(i2c->regs + MA35_ADDR0);
> +
> + reset_control_assert(i2c->rst);
> + usleep_range(10, 20);
> + reset_control_deassert(i2c->rst);
> +
> + writel(clkdiv, (i2c->regs + MA35_CLKDIV));
> + ma35d1_i2c_write_ctl(i2c, MA35_CTL_I2CEN);
[Severity: High]
Does this code inadvertently leave interrupts disabled after a hardware reset?
When a transfer times out, ma35d1_i2c_reset() asserts hardware reset and
restores MA35_CLKDIV and MA35_CTL_I2CEN, but it fails to restore the
MA35_CTL_INTEN (Interrupt Enable) bit.
Because the device is already runtime active due to the autosuspend delay,
subsequent transfers will skip ma35d1_i2c_runtime_resume() (where INTEN is
normally set), causing all subsequent operations to time out permanently
and wedging the bus.
> +
> + if (i2c->target)
> + writel(slvaddr, i2c->regs + MA35_ADDR0);
> +}
[ ... ]
> +/* Check if this is the last message in the set */
> +static inline bool is_last_msg(struct ma35d1_i2c *i2c)
> +{
> + return i2c->msg_idx >= (i2c->msg_num - 1);
[Severity: High]
Can this calculation underflow when i2c->msg_num is 0?
The I2C core permits probing with 0 messages (such as from i2cdetect).
If msg_num is 0, the calculation 0 - 1 evaluates to UINT_MAX. This causes
the check to fail incorrectly and can lead to reading beyond the end of the
empty array during hardware interrupts.
> +}
> +
> +/* Check if this is the last byte in the current message */
[ ... ]
> +static void ma35d1_i2c_irq_target_trx(struct ma35d1_i2c *i2c,
> + unsigned long i2c_status)
> +{
> + unsigned char byte;
> +
> + switch (i2c_status) {
> + case MA35_S_RECE_ARB_LOST:
> + /*
> + * Arbitration lost during SLA+R/W (Address) phase.
> + * The hardware automatically switches to Target mode as our
> + * own slave address was detected.
> + */
> + i2c->err = -EAGAIN;
> + ma35d1_i2c_controller_complete(i2c);
> + i2c_slave_event(i2c->target, I2C_SLAVE_WRITE_REQUESTED, &byte);
[Severity: High]
Can i2c->target be NULL when it is passed to i2c_slave_event() here?
If a target event arrives when no target is registered, or if a race occurs
during unregistration, passing a NULL i2c->target pointer unconditionally
will cause a kernel panic when i2c_slave_event() dereferences it.
> + break;
> +
> + case MA35_S_RECE_ADDR_ACK:
[ ... ]
> +static void ma35d1_i2c_irq_controller_trx(struct ma35d1_i2c *i2c,
> + unsigned long i2c_status)
> +{
> + unsigned char byte;
> +
> + /* Ensuring messages are absolutely valid in Controller mode. */
> + if (unlikely(!i2c->msg)) {
> + dev_err(i2c->dev, "Spurious controller interrupt (status: 0x%02lx)\n", i2c_status);
> + ma35d1_i2c_write_ctl(i2c, MA35_CTL_SI);
> + return;
> + }
> +
> + switch (i2c_status) {
[ ... ]
> + case MA35_M_RECE_DATA_ACK:
> + case MA35_M_RECE_DATA_NACK:
> + /* DATA has been received and ACK/NACK has been received */
> + byte = readb(i2c->regs + MA35_DAT);
> + i2c->msg->buf[i2c->msg_ptr++] = byte;
[Severity: High]
Can this access to i2c->msg->buf race with the timeout teardown path?
If a transfer times out, ma35d1_i2c_doxfer() locklessly sets i2c->msg = NULL
and returns, after which the caller usually frees the msgs array.
If a delayed or spurious hardware interrupt fires concurrently on another CPU,
this function checks !i2c->msg at the start, but accesses i2c->msg->buf
locklessly later here. If the pointer was cleared or the array was freed
in between, this will result in a NULL pointer dereference or use-after-free.
> +
> + if (is_last_byte(i2c)) {
[ ... ]
> +static irqreturn_t ma35d1_i2c_irq(int irqno, void *dev_id)
> +{
> + struct ma35d1_i2c *i2c = dev_id;
> + unsigned long status;
> +
> + status = readl(i2c->regs + MA35_STATUS0);
[Severity: High]
Is it safe to access hardware registers here without verifying if the device
PM state is active?
When i2c->target is enabled, the driver enables IRQ wakeup in
ma35d1_i2c_suspend(). If a target event wakes the system from sleep, the
IRQ handler can execute early during the resume phase, before the device's
own pm_runtime or system resume has enabled the peripheral clock.
Accessing the registers while the clock is disabled triggers an external
bus abort. Using pm_runtime_get_if_active() before accessing registers would
prevent this.
> +
> + if (status == MA35_BUS_ERROR) {
> + dev_err(i2c->dev, "Bus error during transfer\n");
> + ma35d1_i2c_stop(i2c, -EIO);
> + goto out;
> + }
[ ... ]
> +static int ma35d1_i2c_doxfer(struct ma35d1_i2c *i2c, struct i2c_msg *msgs,
> + int num)
> +{
> + unsigned long timeout;
> + unsigned int val;
> + int ret, err;
> +
> + i2c->msg = msgs;
> + i2c->msg_num = num;
> + i2c->msg_ptr = 0;
> + i2c->msg_idx = 0;
> + i2c->err = 0;
> +
> + ma35d1_i2c_write_ctl(i2c, MA35_CTL_STA_SI);
> +
> + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
[Severity: High]
Does this code inadvertently return success immediately for zero-length
messages?
The I2C core can send zero-length messages (num == 0). If msgs_num is
initially 0, this wait_event_timeout() condition evaluates to true
immediately before the hardware executes the START condition.
The function will return successfully and the caller may destroy the msgs
array while the hardware is still running.
> + ret = i2c->msg_idx;
> +
> + if (timeout == 0) {
> + dev_dbg(i2c->dev, "xfer timeout\n");
> + i2c->msg = NULL;
[Severity: High]
Can clearing i2c->msg locklessly here cause a data race?
As noted above, if a delayed interrupt fires concurrently on another CPU,
setting this to NULL locklessly can cause the interrupt handler to access
freed memory or dereference a NULL pointer.
> + ret = -ETIMEDOUT;
> + goto reset;
> + }
> +
> + err = readl_poll_timeout(i2c->regs + MA35_CTL0, val,
[ ... ]
> +static int ma35d1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
> + int num)
> +{
> + struct ma35d1_i2c *i2c = i2c_get_adapdata(adap);
> + int ret;
> +
> + ret = pm_runtime_resume_and_get(i2c->dev);
> + if (ret)
> + return ret;
> +
> + ret = ma35d1_i2c_doxfer(i2c, msgs, num);
> +
> + if (i2c->target)
> + ma35d1_i2c_write_ctl(i2c, MA35_CTL_SI_AA);
[Severity: High]
Does writing MA35_CTL_SI_AA clear and drop pending target mode interrupts?
MA35_CTL_SI_AA includes the MA35_CTL_SI bit. Since I2C controllers often
use a Write-1-To-Clear model for the SI (Interrupt) bit, writing this
blindly acknowledges and clears any I2C target interrupt that might have
just arrived on the bus. This drops the event and stalls the external
master waiting for a response.
> + pm_runtime_put_autosuspend(i2c->dev);
> +
> + return ret;
> +}
> +
[ ... ]
> +static int ma35d1_unreg_target(struct i2c_client *target)
> +{
> + struct ma35d1_i2c *i2c = i2c_get_adapdata(target->adapter);
> + int ret;
> +
> + if (!i2c->target)
> + return -EINVAL;
> +
> + writel(0, i2c->regs + MA35_ADDR0);
> + ma35d1_i2c_write_ctl(i2c, MA35_CTL_SI);
> + i2c->target = NULL;
[Severity: High]
Can clearing i2c->target locklessly race with an executing target IRQ handler?
The target pointer is cleared here without holding a lock or using
synchronize_irq(). An actively executing target IRQ handler on another CPU
could crash when it attempts to use the pointer.
> +
> + ret = pm_runtime_put_sync(i2c->dev);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720030616.1067660-1-zychennvt@gmail.com?part=2
^ permalink raw reply
* [PATCH v3 4/4] hwmon: (pmbus/max20830): add support for max20830c and max20840c
From: Alexis Czezar Torreno @ 2026-07-20 3:14 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno
In-Reply-To: <20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com>
Add support for MAX20830C and MAX20840 step-down DC-DC switching
regulator with PMBus interface. MAX20830C is a different packaging
for MAX20830, and MAX20840C supports 40A regulation compared to
MAX20830 that is only 30A.
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
Documentation/hwmon/max20830.rst | 27 ++++++++++++++++++++++-----
drivers/hwmon/pmbus/max20830.c | 27 ++++++++++++++-------------
2 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/Documentation/hwmon/max20830.rst b/Documentation/hwmon/max20830.rst
index 936e409dcc5c0898dde27d782308d4a7e1357e73..b850f3b6e40d1f1d0cec944be40af02265aced59 100644
--- a/Documentation/hwmon/max20830.rst
+++ b/Documentation/hwmon/max20830.rst
@@ -13,6 +13,22 @@ Supported chips:
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20830.pdf
+ * Analog Devices MAX20830C
+
+ Prefix: 'max20830c'
+
+ Addresses scanned: -
+
+ Datasheet:
+
+ * Analog Devices MAX20840C
+
+ Prefix: 'max20840c'
+
+ Addresses scanned: -
+
+ Datasheet:
+
Author:
- Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
@@ -21,12 +37,13 @@ Author:
Description
-----------
-This driver supports hardware monitoring for Analog Devices MAX20830
-Step-Down Switching Regulator with PMBus Interface.
+This driver supports hardware monitoring for Analog Devices MAX20830, MAX20830C
+and MAX20840C. These are Step-Down Switching Regulator with PMBus Interface.
-The MAX20830 is a 2.7V to 16V, 30A fully integrated step-down DC-DC switching
-regulator. Through the PMBus interface, the device can monitor input/output
-voltages, output current and temperature.
+MAX20830, and MAX20830C are 2.7V to 16V, 30A fully integrated step-down DC-DC
+switching regulators. MAX20840C is similar but can reach 40A. Through the PMBus
+interface, these devices can monitor input/output voltages, output current and
+temperature.
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c
index 7a5e67bd33c0a8b84511d138ac5f5f48334a0a45..73fe59685cf157cc27c1011ad9cc045486bbeab0 100644
--- a/drivers/hwmon/pmbus/max20830.c
+++ b/drivers/hwmon/pmbus/max20830.c
@@ -98,13 +98,12 @@ static int max20830_probe(struct i2c_client *client)
* which do not support SMBus block reads.
*/
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
- /* Reads 9 Data bytes from MAX20830 */
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read IC_DEVICE_ID\n");
} else {
- /* Reads 1 length byte + 9 Data bytes from MAX20830 */
+ /* Reads 1 length byte + data bytes */
ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID,
MAX20830_IC_DEVICE_ID_LENGTH + 1,
buf);
@@ -120,26 +119,28 @@ static int max20830_probe(struct i2c_client *client)
ret = ret - 1;
}
- /*
- * MAX20830 IC_DEVICE_ID sends string data "MAX20830\0".
- * Return value should at least be 9 bytes of data.
- */
+ /* Verify we read the expected number of bytes */
if (ret < MAX20830_IC_DEVICE_ID_LENGTH)
return dev_err_probe(&client->dev, -ENODEV,
- "IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n",
- ret);
+ "IC_DEVICE_ID too short: expected %d bytes, got %d\n",
+ MAX20830_IC_DEVICE_ID_LENGTH, ret);
+
+ /* Null-terminate the string */
+ buf[ret] = '\0';
- /* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */
- buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0';
- if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1))
+ /* Verify the device ID matches what we expect */
+ if ((strcmp(buf, "MAX20830") && strcmp(buf, "MAX20830C") &&
+ strcmp(buf, "MAX20840C")))
return dev_err_probe(&client->dev, -ENODEV,
- "Unsupported device: '%s'\n", buf);
+ "Unsupported device: '%*pE'\n", ret, buf);
return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id max20830_id[] = {
- {"max20830"},
+ { "max20830" },
+ { "max20830c" },
+ { "max20840c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max20830_id);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 3/4] dt-bindings: hwmon: (pmbus/max20830): add max20830c and max20840c support
From: Alexis Czezar Torreno @ 2026-07-20 3:14 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno, Krzysztof Kozlowski
In-Reply-To: <20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com>
Add compatible strings for variants of MAX20830 which are MAX20830C
and MAX20840C. These devices have the same register functionality with
MAX20830 but with a longer IC_DEVICE_ID.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
index 214adf1a3b9e9efdeca7db3bd7679c70656e9a7e..b7d3b6152b58273a4c35581bbd66ec293fb2e925 100644
--- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
@@ -22,7 +22,13 @@ allOf:
properties:
compatible:
- const: adi,max20830
+ oneOf:
+ - const: adi,max20830
+ - items:
+ - enum:
+ - adi,max20830c
+ - adi,max20840c
+ - const: adi,max20830
reg:
maxItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH v3 2/4] hwmon: (pmbus/max20830): add VOUT feedback resistor scaling support
From: Alexis Czezar Torreno @ 2026-07-20 3:14 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno
In-Reply-To: <20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com>
Implement support for external voltage divider scaling using the
adi,vout-rfb1-ohms and adi,vout-rfb2-ohms device tree properties.
When the desired output voltage exceeds VREF, a resistor divider
(RFB1 and RFB2) is used to scale down the feedback voltage. The
driver reads these resistor values from device tree and applies
the scaling formula: VOUT_actual = VOUT_measured × (1 + RFB1/RFB2)
The properties are optional. If not specified, the driver assumes
no voltage divider is present and reports the raw VOUT reading.
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
drivers/hwmon/pmbus/max20830.c | 59 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c
index cb2c23672166d641852199ca07eb716924f4f286..7a5e67bd33c0a8b84511d138ac5f5f48334a0a45 100644
--- a/drivers/hwmon/pmbus/max20830.c
+++ b/drivers/hwmon/pmbus/max20830.c
@@ -7,6 +7,7 @@
#include <linux/errno.h>
#include <linux/i2c.h>
+#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/string.h>
@@ -14,6 +15,50 @@
#define MAX20830_IC_DEVICE_ID_LENGTH 9
+struct max20830_data {
+ struct pmbus_driver_info info;
+ u32 vout_rfb1;
+ u32 vout_rfb2;
+};
+
+/*
+ * MAX20830 only supports READ_VOUT for VOUT monitoring.
+ *
+ * MAX20830 uses an external resistor divider for voltage sensing:
+ * - VOUT_COMMAND and VOUT_MAX set the reference voltage at the feedback pin
+ * - READ_VOUT reports the feedback voltage, which needs to be scaled for actual
+ * output voltage
+ *
+ * Scaling formula: vout_actual = vout_fb × (1 + RFB1 / RFB2)
+ *
+ * If regulator support is added in the future, some adjustments are needed to
+ * ensure correct feedback voltages are set.
+ */
+static int max20830_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct max20830_data *data = container_of(info, struct max20830_data, info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_READ_VOUT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Apply voltage divider scaling if resistors are non-zero */
+ if (data->vout_rfb1 && data->vout_rfb2) {
+ ret = DIV_ROUND_CLOSEST_ULL((u64)ret * (data->vout_rfb1 +
+ data->vout_rfb2), data->vout_rfb2);
+ ret = clamp_val(ret, 0, 0xFFFF);
+ }
+ return ret;
+ default:
+ return -ENODATA;
+ }
+}
+
static struct pmbus_driver_info max20830_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
@@ -24,13 +69,25 @@ static struct pmbus_driver_info max20830_info = {
PMBUS_HAVE_TEMP |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+ .read_word_data = max20830_read_word_data,
};
static int max20830_probe(struct i2c_client *client)
{
u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {};
+ struct max20830_data *data;
int ret;
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->info = max20830_info;
+
+ /* Read optional voltage divider resistor values */
+ device_property_read_u32(&client->dev, "adi,vout-rfb1-ohms", &data->vout_rfb1);
+ device_property_read_u32(&client->dev, "adi,vout-rfb2-ohms", &data->vout_rfb2);
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) &&
!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
return -ENODEV;
@@ -78,7 +135,7 @@ static int max20830_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported device: '%s'\n", buf);
- return pmbus_do_probe(client, &max20830_info);
+ return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id max20830_id[] = {
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/4] Add support for MAX20830C and MAX20840C step-down DC-DC switching regulator
From: Alexis Czezar Torreno @ 2026-07-20 3:14 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno, Krzysztof Kozlowski
This series adds a few features before adding support to MAX20830C and
MAX20840C.
Patches 1 and 2 adds new resistor feedback properties that is needed to
properly scale VOUT
Patches 3 and 4 adds support for devices MAX20830C and MAX20840C.
MAX20830C and MAX20840 are step-down DC-DC switching regulator with PMBus
interface. MAX20830C is a different packaging for MAX20830, and MAX20840C
supports 40A regulation compared to MAX20830 that is only 30A.
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
Changes in v3:
- Dropped patch that add EN-GPIO property in DTS
- discussed to be skippable until an actual use-case arise
- Patch 1/4:
- Carried over the completed examples from previously dropped patch
- Kept the review tag as the carried info from the dropped patched
was also reviewed.
- Patch 2/4:
- added clamping to read_vout during scaling
- improved comments regarding future regulator support
- Link to v2: https://lore.kernel.org/r/20260706-dev-max20830c-v2-0-37761e89bb5f@analog.com
Changes in v2:
- Patch 1/5: completing example
- Dropped patch adding GPIO enable in driver (no use case)
- Patch 3/5: added notes on READ_VOUT and related VOUT commands
- Patch 4/5: removed comment on fallback
- Patch 5/5: simplified device ID check handling
- Link to v1: https://lore.kernel.org/r/20260630-dev-max20830c-v1-0-a02786bde470@analog.com
---
Alexis Czezar Torreno (4):
dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples
hwmon: (pmbus/max20830): add VOUT feedback resistor scaling support
dt-bindings: hwmon: (pmbus/max20830): add max20830c and max20840c support
hwmon: (pmbus/max20830): add support for max20830c and max20840c
.../bindings/hwmon/pmbus/adi,max20830.yaml | 28 ++++++-
Documentation/hwmon/max20830.rst | 27 +++++--
drivers/hwmon/pmbus/max20830.c | 86 ++++++++++++++++++----
3 files changed, 121 insertions(+), 20 deletions(-)
---
base-commit: 1a42625604046790ea8ec61c2a749bbf76b28943
change-id: 20260630-dev-max20830c-9d38d8b0a765
Best regards,
--
Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
^ permalink raw reply
* [PATCH v3 1/4] dt-bindings: hwmon: (pmbus/max20830): add VOUT feedback resistor properties and complete examples
From: Alexis Czezar Torreno @ 2026-07-20 3:14 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan
Cc: linux-hwmon, devicetree, linux-kernel, linux-doc,
Alexis Czezar Torreno, Krzysztof Kozlowski
In-Reply-To: <20260720-dev-max20830c-v3-0-9d06d27d6da3@analog.com>
Add adi,vout-rfb1-ohms and adi,vout-rfb2-ohms properties to support
external voltage divider configuration for VOUT sensing. When the
desired output voltage is higher than VREF, a resistor divider (RFB1
and RFB2) is required to reach the intended value.
The properties use a dependency constraint to ensure both resistors
are specified together, or neither. This prevents misconfiguration
where only one resistor value is provided.
This patch also added missing entries in the examples.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
---
.../bindings/hwmon/pmbus/adi,max20830.yaml | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
index 1625dd59417f1b3ca689a9c86ca266da913d1217..214adf1a3b9e9efdeca7db3bd7679c70656e9a7e 100644
--- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
@@ -44,11 +44,26 @@ properties:
GPIO connected to the power-good status output pin.
maxItems: 1
+ adi,vout-rfb1-ohms:
+ description:
+ Top feedback resistor (RFB1) value in ohms for VOUT sensing divider.
+ When the desired output voltage is higher than VREF, a resistor divider
+ is required. VOUT = VREF × (1 + RFB1/RFB2)
+
+ adi,vout-rfb2-ohms:
+ description:
+ Bottom feedback resistor (RFB2) value in ohms for VOUT sensing divider.
+ Datasheet recommends that RFB2 does not exceed 2.5kΩ.
+
required:
- compatible
- reg
- vddh-supply
+dependencies:
+ adi,vout-rfb1-ohms: ['adi,vout-rfb2-ohms']
+ adi,vout-rfb2-ohms: ['adi,vout-rfb1-ohms']
+
unevaluatedProperties: false
examples:
@@ -61,6 +76,11 @@ examples:
compatible = "adi,max20830";
reg = <0x30>;
vddh-supply = <&vddh>;
+ avdd-supply = <&avdd>;
+ ldoin-supply = <&ldoin>;
+ pwr-good-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
+ adi,vout-rfb1-ohms = <10000>;
+ adi,vout-rfb2-ohms = <2000>;
};
};
...
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v6 2/6] arm64: dts: qcom: Add device tree for Nord GearVM variant
From: Shawn Guo @ 2026-07-20 3:11 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bartosz Golaszewski, Deepti Jaggi, devicetree,
linux-arm-msm, linux-kernel
In-Reply-To: <916e2f7d-6f9e-44db-aec8-95532d265dbf@oss.qualcomm.com>
On Thu, Jul 16, 2026 at 07:01:00PM +0200, Konrad Dybcio wrote:
> On 7/16/26 4:25 PM, Shawn Guo wrote:
> > From: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
> >
> > Add SoC-level device tree include for Nord GearVM variant, where a VM
> > controls platform resources (clocks, regulators, powerdomains, etc.)
> > as SCMI server. It currently covers:
> >
> > - 64 SCMI shared memory regions reserved at 0xd7600000-0xd763f000
> > for SMC-based firmware communication channels
> > - Three QUPV3 GENI SE QUP blocks (qupv3_0/1/2) with I2C/SPI/UART
> > controllers using SCMI power and performance domains via scmi11
> > - UFS host controller with SCMI power domain via scmi3
> >
> > Signed-off-by: Deepti Jaggi <deepti.jaggi@oss.qualcomm.com>
> > Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> > ---
> > arch/arm64/boot/dts/qcom/nord-gearvm.dtsi | 2911 +++++++++++++++++++++
> > 1 file changed, 2911 insertions(+)
> > create mode 100644 arch/arm64/boot/dts/qcom/nord-gearvm.dtsi
> >
> > diff --git a/arch/arm64/boot/dts/qcom/nord-gearvm.dtsi b/arch/arm64/boot/dts/qcom/nord-gearvm.dtsi
> > new file mode 100644
> > index 000000000000..8db93c361c2d
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/qcom/nord-gearvm.dtsi
> > @@ -0,0 +1,2911 @@
> > +// SPDX-License-Identifier: BSD-3-Clause
> > +/*
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > + */
> > +
> > +#include "nord.dtsi"
> > +
> > +&firmware {
> > + scmi0: scmi-0 {
> > + compatible = "qcom,scmi-smc";
> > + arm,smc-id = <0xc6008012>;
> > + shmem = <&shmem0>;
> > + /* We expect the bootloader to update the IRQ number */
>
> /* I expect this not to be pasted over each of the 64 instances */
Ok, I will keep it for scmi-0 only.
Shawn
^ permalink raw reply
* Re: [PATCH v2 1/2] dt-bindings: iio: adc: Add Nuvoton MA35D1 EADC
From: Chi-Wen Weng @ 2026-07-20 3:09 UTC (permalink / raw)
To: Rob Herring
Cc: Jonathan Cameron, jic23, krzk+dt, conor+dt, dlechner, nuno.sa,
andy, linux-arm-kernel, linux-iio, devicetree, linux-kernel,
cwweng
In-Reply-To: <CAL_Jsq+5N9AvzQ0cxUVY97Ad+CasR29Fan7sUuprDYjgqzWjMQ@mail.gmail.com>
Hi Rob,
Thank you for the clarification.
> No, document what the h/w has, not what a driver currently uses. The
> only exception I can think of here would be if only 1 interrupt pin is
> usable on a given system. Then it would be just 1 interrupt with
> interrupt-names to define which one is used.
Understood. I incorrectly based the binding on the current driver
implementation rather than the hardware resources.
MA35D1 has one EADC controller, EADC0, and all four interrupt outputs,
ADINT0
through ADINT3, are connected to separate GIC interrupt lines.
ADINT0, ADINT1 and ADINT2 are generated from the corresponding ADIF0,
ADIF1 and ADIF2 conversion interrupt flags. ADINT3 is a shared interrupt
output and can be generated by ADIF3 or by any of the ADCMPF0 through
ADCMPF3 result-compare flags.
I will therefore keep all four interrupt entries in the v3 binding and
add interrupt-names for "adint0", "adint1", "adint2" and "adint3". The
ADINT3 description will state that it is shared by the conversion and
result-compare interrupt sources.
The initial driver will continue to request and use only "adint0", but
that implementation limitation will not restrict the hardware
description in the binding.
I will also remove the redundant maxItems property because the fixed
items list already specifies the number of interrupt entries.
Thanks,
Chi-Wen
^ permalink raw reply
* [PATCH v6 3/3] arm64: dts: nuvoton: Add I2C nodes for MA35D1 SoC
From: Zi-Yu Chen @ 2026-07-20 3:06 UTC (permalink / raw)
To: Andi Shyti, linux-i2c, devicetree, linux-arm-kernel, linux-kernel
Cc: Jacky Huang, Shan-Chun Hung, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Zi-Yu Chen
In-Reply-To: <20260720030616.1067660-1-zychennvt@gmail.com>
Add I2C controller nodes to the MA35D1 SoC dtsi file.
Also, enable the I2C2 interface on the MA35D1 SOM board and
configure its pinctrl to allow communication with the onboard
NAU8822 audio codec
Signed-off-by: Zi-Yu Chen <zychennvt@gmail.com>
---
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 20 ++++++
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 72 +++++++++++++++++++
2 files changed, 92 insertions(+)
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
index a029b660e8dc..422468dbc3bf 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
@@ -57,6 +57,18 @@ &clk {
"integer";
};
+&i2c2 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c2>;
+
+ nau8822: audio-codec@1a {
+ compatible = "nuvoton,nau8822";
+ reg = <0x1a> ;
+ nuvoton,spk-btl;
+ };
+};
+
&pinctrl {
uart-grp {
pinctrl_uart0: uart0-pins {
@@ -100,6 +112,14 @@ pinctrl_uart16: uart16-pins {
power-source = <1>;
};
};
+
+ i2c-grp {
+ pinctrl_i2c2: i2c2-pins {
+ nuvoton,pins = <1 8 4>,
+ <1 9 4>;
+ bias-disable;
+ };
+ };
};
&uart0 {
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
index 7228ad4735b5..28488889f564 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
@@ -372,6 +372,78 @@ uart15: serial@407f0000 {
status = "disabled";
};
+ i2c0: i2c@40800000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40800000 0x0 0x1000>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C0_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C0>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c1: i2c@40810000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40810000 0x0 0x1000>;
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C1_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C1>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c2: i2c@40820000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40820000 0x0 0x1000>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C2_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C2>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c3: i2c@40830000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40830000 0x0 0x1000>;
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C3_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C3>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c4: i2c@40840000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40840000 0x0 0x1000>;
+ interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C4_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C4>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c5: i2c@40850000 {
+ compatible = "nuvoton,ma35d1-i2c";
+ reg = <0x0 0x40850000 0x0 0x1000>;
+ interrupts = <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk I2C5_GATE>;
+ clock-frequency = <100000>;
+ resets = <&sys MA35D1_RESET_I2C5>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
uart16: serial@40880000 {
compatible = "nuvoton,ma35d1-uart";
reg = <0x0 0x40880000 0x0 0x100>;
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox