Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v2 5/7] clk: qcom: Add Hawi TCSR clock controller driver
From: Mike Tipton @ 2026-04-10 15:57 UTC (permalink / raw)
  To: Vivek Aknurwar
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Taniya Das,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260409-clk-hawi-v2-5-c7a185389d9a@oss.qualcomm.com>

On Thu, Apr 09, 2026 at 01:51:39PM -0700, Vivek Aknurwar wrote:
> Add support for the TCSR clock controller found on the Qualcomm Hawi SoC.
> This controller provides reference clocks for various peripherals
> including PCIe, UFS, and USB.
> 
> Reviewed-by: Taniya Das <taniya.das@oss.qualcomm.com>
> Signed-off-by: Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com>
> ---
>  drivers/clk/qcom/Kconfig       |   7 ++
>  drivers/clk/qcom/Makefile      |   1 +
>  drivers/clk/qcom/tcsrcc-hawi.c | 158 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 166 insertions(+)

Reviewed-by: Mike Tipton <mike.tipton@oss.qualcomm.com>

Thanks,
Mike

^ permalink raw reply

* Re: [PATCH v2 4/7] clk: qcom: rpmh: Add support for Hawi RPMH clocks
From: Mike Tipton @ 2026-04-10 15:56 UTC (permalink / raw)
  To: Vivek Aknurwar
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Taniya Das,
	linux-arm-msm, linux-clk, devicetree, linux-kernel, Konrad Dybcio
In-Reply-To: <20260409-clk-hawi-v2-4-c7a185389d9a@oss.qualcomm.com>

On Thu, Apr 09, 2026 at 01:51:38PM -0700, Vivek Aknurwar wrote:
> Add RPMH clocks present in Qualcomm Hawi SoC.
> 
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> Reviewed-by: Taniya Das <taniya.das@oss.qualcomm.com>
> Signed-off-by: Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com>
> ---
>  drivers/clk/qcom/clk-rpmh.c | 33 +++++++++++++++++++++++++++++++++
>  1 file changed, 33 insertions(+)

Reviewed-by: Mike Tipton <mike.tipton@oss.qualcomm.com>

Thanks,
Mike

^ permalink raw reply

* Re: [PATCH v2 3/7] dt-bindings: clock: qcom: Add Hawi global clock controller
From: Mike Tipton @ 2026-04-10 15:56 UTC (permalink / raw)
  To: Vivek Aknurwar
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Taniya Das,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260409-clk-hawi-v2-3-c7a185389d9a@oss.qualcomm.com>

On Thu, Apr 09, 2026 at 01:51:37PM -0700, Vivek Aknurwar wrote:
> Add device tree bindings for the global clock controller on the
> Qualcomm Hawi SoC.
> 
> Acked-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com>
> ---
>  .../devicetree/bindings/clock/qcom,hawi-gcc.yaml   |  63 +++++
>  include/dt-bindings/clock/qcom,hawi-gcc.h          | 253 +++++++++++++++++++++
>  2 files changed, 316 insertions(+)

Reviewed-by: Mike Tipton <mike.tipton@oss.qualcomm.com>

Thanks,
Mike

^ permalink raw reply

* Re: [PATCH v2 2/7] dt-bindings: clock: qcom: Add Hawi TCSR clock controller
From: Mike Tipton @ 2026-04-10 15:55 UTC (permalink / raw)
  To: Vivek Aknurwar
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Taniya Das,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260409-clk-hawi-v2-2-c7a185389d9a@oss.qualcomm.com>

On Thu, Apr 09, 2026 at 01:51:36PM -0700, Vivek Aknurwar wrote:
> Add bindings documentation for TCSR clock controller on the
> Qualcomm Hawi SoC.
> 
> Acked-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com>
> ---
>  .../devicetree/bindings/clock/qcom,sm8550-tcsr.yaml      |  2 ++
>  include/dt-bindings/clock/qcom,hawi-tcsrcc.h             | 16 ++++++++++++++++
>  2 files changed, 18 insertions(+)

Reviewed-by: Mike Tipton <mike.tipton@oss.qualcomm.com>

Thanks,
Mike

^ permalink raw reply

* Re: [PATCH v2 1/7] dt-bindings: clock: qcom-rpmhcc: Add RPMHCC bindings for Hawi
From: Mike Tipton @ 2026-04-10 15:54 UTC (permalink / raw)
  To: Vivek Aknurwar
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Taniya Das,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260409-clk-hawi-v2-1-c7a185389d9a@oss.qualcomm.com>

On Thu, Apr 09, 2026 at 01:51:35PM -0700, Vivek Aknurwar wrote:
> Update documentation for the RPMH clock controller on the
> Qualcomm Hawi SoC.
> 
> Acked-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com>
> ---
>  Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml | 1 +
>  include/dt-bindings/clock/qcom,rpmh.h                    | 2 ++
>  2 files changed, 3 insertions(+)

Reviewed-by: Mike Tipton <mike.tipton@oss.qualcomm.com>

Thanks,
Mike

^ permalink raw reply

* Re: [PATCH v2 2/3] remoteproc: imx_rproc: Pass bootaddr to SM CPU/LMM reset vector
From: Mathieu Poirier @ 2026-04-10 15:52 UTC (permalink / raw)
  To: Peng Fan
  Cc: Peng Fan, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Daniel Baluta, linux-remoteproc@vger.kernel.org,
	devicetree@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <adbzPl7ydUvb+MIS@shlinux89>

On Thu, Apr 09, 2026 at 08:30:54AM +0800, Peng Fan wrote:
> On Wed, Apr 08, 2026 at 09:46:32AM -0600, Mathieu Poirier wrote:
> >On Wed, Apr 08, 2026 at 01:30:16AM +0000, Peng Fan wrote:
> >> > Subject: Re: [PATCH v2 2/3] remoteproc: imx_rproc: Pass bootaddr to
> >> > SM CPU/LMM reset vector
> >> > 
> >> [...]
> >> > 
> >> > >
> >> > > Aligning the ELF entry point with the hardware reset base on
> >> > Cortex‑M
> >> > > systems is possible, but it comes with several risks.
> >> > 
> >> > I'm not asking to align the ELF entry point with the hardware reset base.
> >> > All I want is to have the correct start address embedded in the ELF file
> >> > to avoid having to use a mask.
> >> 
> >> I see, per my understanding:
> >> FreeRTOS typically exposes __isr_vector, which corresponds to the hardware
> >> reset / vector table base.
> >> Zephyr (Cortex‑M) exposes _vector_table, which serves the same purpose.
> >> I am not certain about other RTOSes, but the pattern seems consistent:
> >> the vector table base is already available as a named ELF symbol.
> >> 
> >> Given that, if the preferred approach is to parse the ELF and explicitly
> >> retrieve the hardware reset base, I can update the implementation accordingly.
> >> If you prefer to parse the elf file to get the hardware reset base,
> >> I could update to use them.
> >> 
> >> Options1: Something as below:
> >> 1. Include rproc_elf_find_symbol in remoteproc_elf_loader.c
> >> 2. Use below in imx_rproc.c
> >> ret = rproc_elf_find_symbol(rproc, fw, "__isr_vector", &vector_base);
> >> if (ret)
> >>     ret = rproc_elf_find_symbol(rproc, fw, "__vector_table", &vector_base);
> >> 
> >> if (!ret)
> >>     rproc->bootaddr = vector_base
> >> else
> >>    dev_info(dev, "no __isr_vector or __vector_table\n")
> >
> >No
> 
> If your concern is about rproc->bootaddr, I could introduce
> imx_rproc->vector_base for i.MX.  Please help detail a bit.
> 
> >
> >> 
> >> This makes the hardware reset base explicit, avoids masking e_entry.
> >> 
> >> Option 2: User‑provided reset symbol via sysfs 
> >> As an alternative, we could expose a sysfs attribute,
> >> e.g. reset_symbol, allowing users to specify the symbol name
> >> to be used as the reset base:
> >> 
> >> echo __isr_vector > /sys/class/remoteproc/remoteprocX/reset_symbol
> >> 
> >
> >Definitely not.
> >
> >The definition of e_entry in the specification is clear, i.e "the address of the
> >entry point from where the process starts executing".  If masking is required
> >because the tool that puts the image together gets the wrong address, then it
> >should be fixed.
> 
> The hardware reset base is the address from which the hardware fetches the
> initial stack pointer and program counter values and loads them into the SP
> and PC registers.  In contrast, bootaddr (i.e. e_entry) represents the address
> at which the CPU starts executing code (the PC value after reset). As you
> pointed out earlier, this distinction is clear.
> 
> In our case, we need to obtain the hardware reset base and pass that value to
> the system firmware. However, e_entry should not be set to the hardware reset
> base. Doing so would introduce the issues I described in [1]. This means we
> should not modify the Zephyr or FreeRTOS build outputs to make e_entry equal
> to the hardware reset base.


As I said earlier, I am _not_ suggesting to make e_entry equal to the hardware
reset base.

We are going in circles here.

> 
> Given these constraints, the feasible solutions I can see are either:
> - option 1 (explicitly retrieving the hardware reset base), or
> - continuing to use masking.
> 
> Please suggest.
> 
> [1] https://lore.kernel.org/all/acs2PAZq2k3zjmDW@shlinux89/
> 
> Thanks,
> Peng
> 
> >
> >> The remoteproc core would then resolve that symbol from
> >> the ELF and set rproc->bootaddr accordingly.
> >> This provides maximum flexibility but does introduce a new user‑visible ABI,
> >> so I see it more as an opt‑in or fallback mechanism.
> >> 
> >> Please let me know which approach you prefer, and I will update
> >> this series accordingly in v3..
> >> 
> >> Thanks,
> >> Peng.
> >> 
> >> 
> >> > 
> >> > > 1, Semantic mismatch (ELF vs. hardware behavior) 2, Debuggers may
> >> > > attempt to set breakpoints or start execution at the entry symbol
> >> > >

^ permalink raw reply

* Re: [PATCH v9 0/2] Add support for Microchip EMC1812
From: Guenter Roeck @ 2026-04-10 15:51 UTC (permalink / raw)
  To: Marius Cristea, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet
  Cc: linux-hwmon, devicetree, linux-kernel, linux-doc, Conor Dooley
In-Reply-To: <20260403-hw_mon-emc1812-v9-0-1a798f31cf2e@microchip.com>

On 4/3/26 05:39, Marius Cristea wrote:
> This is the hwmon driver for EMC1812/13/14/15/33 multichannel Low-Voltage
> Remote Diode Sensor Family. The chips in the family have one internal
> and different numbers of external channels, ranging from 1 (EMC1812) to
> 4 channels (EMC1815).
> Reading diodes in anti-parallel connection is supported by EMC1814, EMC1815
> and EMC1833.
> 
> Signed-off-by: Marius Cristea <marius.cristea@microchip.com>

Sashiko still reports numberous issues which I consider valid:

https://sashiko.dev/#/patchset/20260403-hw_mon-emc1812-v9-0-1a798f31cf2e%40microchip.com

Please fix.

Thanks,
Guenter

> ---
> Changes in v9:
> - improve the wording in the Documentation/hwmon/emc1812.rst file
> - add const to variables in the driver
> - initialize the EXT2_BETA_CONFIG only for the pats that support it
> - update the writeble regmap table to exclude read-only registers
> - Link to v8: https://lore.kernel.org/r/20260310-hw_mon-emc1812-v8-0-bc155727e0d2@microchip.com
> 
> Changes in v8:
> - remove "address scan" from emc1812.rst documentation
> - change the second dimension of emc1812_limit_regs_low[][] to 2
> - clamp input value before doing math on it to avoid overflow
> - use rounding instead of truncation for 8 bits limit registers
> - fix misleading comment when HW ID is not recognized
> - Link to v7: https://lore.kernel.org/r/20260223-hw_mon-emc1812-v7-0-51e2676f4e20@microchip.com
> 
> Changes in v7:
> - driver
>    - fix an overflow emc1812_set_hyst
>    - remove unused parameter in emc1812_set_temp
> - devicetree binding:
>    - remove unneeded restrictions not to bloating the binding
> - Link to v6: https://lore.kernel.org/r/20260212-hw_mon-emc1812-v6-0-e37e9b38d898@microchip.com
> 
> Changes in v6:
> - driver
>    - fix an overflow when writing more then 191875 to limits stored on 8
>      bits register
>    - remove "i2c_set_clientdata" from probe
>    - fix discrepancy where writing 16ms and reading it back returns 15ms
>      at update interval
>    - skip setting the ideality factor for channels that are not available
>      on the device
> - devicetree binding:
>    - change the way interrupts are described/used
>    - add "microchip,enable-anti-parallel"
>    - rewrite "allOf" section to be more clear
> - Link to v5: https://lore.kernel.org/r/20260205-hw_mon-emc1812-v5-0-232835aefe8f@microchip.com
> 
> Changes in v5:
> - fix calculation in emc1812_get_limit_temp
> - use i2c_get_match_data cover the case when the driver is instantiated
>    via I2C ID table.
> - replace dev_info with dev_warn
> - remove some unnecessary truncation on 8 bits
> - remove clamping when reading the temerature with hyst
> - not change the conversion rate at probe time
> - use a generic define to remove duplicate channel_info entries
> - Link to v4: https://lore.kernel.org/r/20260127-hw_mon-emc1812-v4-0-6bf636b54847@microchip.com
> 
> Changes in v4:
> - fix file permissions for read only properties
> - fix calculation when the limits are written
> - remove the temp_min_hyst because the part doesn't support it
> - Link to v3: https://lore.kernel.org/r/20251218-hw_mon-emc1812-v3-0-a123ada7b859@microchip.com
> 
> Changes in v3:
> - remove mesages that are not helpfull
> - fix an issue related to NULL labels
> - fix sign/unsign calculation
> - replace E2BIG with EINVAL
> - use BIT() to create mask
> - Link to v2: https://lore.kernel.org/r/20251121-hw_mon-emc1812-v2-0-5b2070f8b778@microchip.com
> 
> Changes in v2:
> - update the interrupt section from yaml file
> - update index.rst
> - remove fault condition from internal sensor
> - remove unused members from structures
> - update the driver to work on systems without device tree or
>    firmware nodes
> - add missing include files
> - make NULL labels to be not visible
> - corect sign/unsign calculations
> - corect possible underflow for limits
> - Link to v1: https://lore.kernel.org/r/20251029-hw_mon-emc1812-v1-0-be4fd8af016a@microchip.com
> 
> ---
> Marius Cristea (2):
>        dt-bindings: hwmon: temperature: add support for EMC1812
>        hwmon: temperature: add support for EMC1812
> 
>   .../bindings/hwmon/microchip,emc1812.yaml          | 184 ++++
>   Documentation/hwmon/emc1812.rst                    |  67 ++
>   Documentation/hwmon/index.rst                      |   1 +
>   MAINTAINERS                                        |   8 +
>   drivers/hwmon/Kconfig                              |  11 +
>   drivers/hwmon/Makefile                             |   1 +
>   drivers/hwmon/emc1812.c                            | 965 +++++++++++++++++++++
>   7 files changed, 1237 insertions(+)
> ---
> base-commit: d2b2fea3503e5e12b2e28784152937e48bcca6ff
> change-id: 20251002-hw_mon-emc1812-f1b806487d10
> 
> Best regards,


^ permalink raw reply

* Re: [PATCH v2] dt-bindings: watchdog: rockchip: Add RV1103B compatible
From: Guenter Roeck @ 2026-04-10 15:49 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: wim, robh, krzk+dt, conor+dt, linux-watchdog, devicetree,
	linux-kernel, heiko, Fabio Estevam
In-Reply-To: <CAOMZO5DFWYUPN9sPzBr-W7fy8ZbrXP7u1sx=HrmUAQVCLrsdjQ@mail.gmail.com>

On 4/8/26 03:28, Fabio Estevam wrote:
> Hi Guenter,
> 
> On Mon, Mar 16, 2026 at 11:14 AM Guenter Roeck <linux@roeck-us.net> wrote:
>>
>> On 3/9/26 17:20, Fabio Estevam wrote:
>>> From: Fabio Estevam <festevam@nabladev.com>
>>>
>>> The RV1103B watchdog is compatible with the existing DesignWare Watchdog
>>> binding. Add the rockchip,rv1103b-wdt compatible string.
>>>
>>> Signed-off-by: Fabio Estevam <festevam@nabladev.com>
>>
>> Reviewed-by: Guenter Roeck <linux@roeck-us.net>
> 
> Could you please apply this one?

Wim applies watchdog subsystem patches.

Guenter


^ permalink raw reply

* Re: [PATCH v5 5/5] watchdog: aaeon: Add watchdog driver for SRG-IMX8P MCU
From: Guenter Roeck @ 2026-04-10 15:49 UTC (permalink / raw)
  To: Thomas Perrot (Schneider Electric), Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Shawn Guo, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam,
	Jérémie Dautheribes, Wim Van Sebroeck, Lee Jones
  Cc: devicetree, linux-kernel, linux-gpio, imx, linux-arm-kernel,
	linux-watchdog, Thomas Petazzoni, Miquel Raynal
In-Reply-To: <20260408-dev-b4-aaeon-mcu-driver-v5-5-ad98bd481668@bootlin.com>

On 4/8/26 10:21, Thomas Perrot (Schneider Electric) wrote:
> Add watchdog driver for the Aaeon SRG-IMX8P embedded controller.
> This driver provides system monitoring and recovery capabilities
> through the MCU's watchdog timer.
> 
> The watchdog supports start, stop, and ping operations with a maximum
> hardware heartbeat of 25 seconds and a default timeout of 240 seconds.
> 
> Co-developed-by: Jérémie Dautheribes (Schneider Electric) <jeremie.dautheribes@bootlin.com>
> Signed-off-by: Jérémie Dautheribes (Schneider Electric) <jeremie.dautheribes@bootlin.com>
> Signed-off-by: Thomas Perrot (Schneider Electric) <thomas.perrot@bootlin.com>
> ---
>   MAINTAINERS                      |   1 +
>   drivers/watchdog/Kconfig         |  10 +++
>   drivers/watchdog/Makefile        |   1 +
>   drivers/watchdog/aaeon_mcu_wdt.c | 132 +++++++++++++++++++++++++++++++++++++++
>   4 files changed, 144 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2538f8c4bc14..7b92af42c9fd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -193,6 +193,7 @@ S:	Maintained
>   F:	Documentation/devicetree/bindings/mfd/aaeon,srg-imx8p-mcu.yaml
>   F:	drivers/gpio/gpio-aaeon-mcu.c
>   F:	drivers/mfd/aaeon-mcu.c
> +F:	drivers/watchdog/aaeon_mcu_wdt.c
>   F:	include/linux/mfd/aaeon-mcu.h
>   
>   AAEON UPBOARD FPGA MFD DRIVER
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index d3b9df7d466b..f67a0b453316 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -420,6 +420,16 @@ config SL28CPLD_WATCHDOG
>   
>   # ARM Architecture
>   
> +config AAEON_MCU_WATCHDOG
> +	tristate "Aaeon MCU Watchdog"
> +	depends on MFD_AAEON_MCU
> +	select WATCHDOG_CORE
> +	help
> +	  Select this option to enable watchdog timer support for the Aaeon
> +	  SRG-IMX8P onboard microcontroller (MCU). This driver provides
> +	  watchdog functionality through the MCU, allowing system monitoring
> +	  and automatic recovery from system hangs.
> +
>   config AIROHA_WATCHDOG
>   	tristate "Airoha EN7581 Watchdog"
>   	depends on ARCH_AIROHA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index ba52099b1253..2deec425d3ea 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
>   # ALPHA Architecture
>   
>   # ARM Architecture
> +obj-$(CONFIG_AAEON_MCU_WATCHDOG) += aaeon_mcu_wdt.o
>   obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
>   obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
>   obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
> diff --git a/drivers/watchdog/aaeon_mcu_wdt.c b/drivers/watchdog/aaeon_mcu_wdt.c
> new file mode 100644
> index 000000000000..949b506d8194
> --- /dev/null
> +++ b/drivers/watchdog/aaeon_mcu_wdt.c
> @@ -0,0 +1,132 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Aaeon MCU Watchdog driver
> + *
> + * Copyright (C) 2026 Bootlin
> + * Author: Jérémie Dautheribes <jeremie.dautheribes@bootlin.com>
> + * Author: Thomas Perrot <thomas.perrot@bootlin.com>
> + */
> +
> +#include <linux/mfd/aaeon-mcu.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +#define AAEON_MCU_PING_WDT	0x73
> +
> +#define AAEON_MCU_WDT_TIMEOUT         240
> +#define AAEON_MCU_WDT_HEARTBEAT_MS    25000
> +
> +struct aaeon_mcu_wdt {
> +	struct watchdog_device wdt;
> +	struct regmap *regmap;
> +};
> +
> +static int aaeon_mcu_wdt_cmd(struct aaeon_mcu_wdt *data, u8 opcode, u8 arg)
> +{
> +	return regmap_write(data->regmap, AAEON_MCU_REG(opcode, arg), 0);
> +}
> +
> +static int aaeon_mcu_wdt_start(struct watchdog_device *wdt)
> +{
> +	struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt);
> +
> +	return aaeon_mcu_wdt_cmd(data, AAEON_MCU_CONTROL_WDT_OPCODE, 0x01);
> +}
> +
> +static int aaeon_mcu_wdt_status(struct watchdog_device *wdt, bool *enabled)
> +{
> +	struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt);
> +	unsigned int rsp;
> +	int ret;
> +
> +	ret = regmap_read(data->regmap,
> +			  AAEON_MCU_REG(AAEON_MCU_CONTROL_WDT_OPCODE, 0x02),
> +			  &rsp);
> +	if (ret)
> +		return ret;
> +
> +	*enabled = rsp == 0x01;
> +	return 0;
> +}
> +
> +static int aaeon_mcu_wdt_stop(struct watchdog_device *wdt)
> +{
> +	struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt);
> +
> +	return aaeon_mcu_wdt_cmd(data, AAEON_MCU_CONTROL_WDT_OPCODE, 0x00);
> +}
> +
> +static int aaeon_mcu_wdt_ping(struct watchdog_device *wdt)
> +{
> +	struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt);
> +
> +	return aaeon_mcu_wdt_cmd(data, AAEON_MCU_PING_WDT, 0x00);
> +}
> +
> +static const struct watchdog_info aaeon_mcu_wdt_info = {
> +	.identity	= "Aaeon MCU Watchdog",
> +	.options	= WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE
> +};
> +
> +static const struct watchdog_ops aaeon_mcu_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= aaeon_mcu_wdt_start,
> +	.stop		= aaeon_mcu_wdt_stop,
> +	.ping		= aaeon_mcu_wdt_ping,
> +};
> +
> +static int aaeon_mcu_wdt_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct watchdog_device *wdt;
> +	struct aaeon_mcu_wdt *data;
> +	bool enabled;
> +	int ret;
> +
> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->regmap = dev_get_regmap(dev->parent, NULL);
> +	if (!data->regmap)
> +		return -ENODEV;
> +
> +	wdt = &data->wdt;
> +	wdt->parent = dev;
> +	wdt->info = &aaeon_mcu_wdt_info;
> +	wdt->ops = &aaeon_mcu_wdt_ops;
> +	/*
> +	 * The MCU firmware has a fixed hardware timeout of 25 seconds that
> +	 * cannot be changed. The watchdog core will handle automatic pinging
> +	 * to support longer timeouts. The software timeout of 240 seconds is
> +	 * chosen arbitrarily as a reasonable value and is not user-configurable.
> +	 */

Odd, unusual, unnecessary, I would argue that most people would consider a fixed
timeout of 240s as anything but reasonable, and as the comment says arbitrary.
Since I am sure that I pointed this out before, you still insist, and I am
tired of arguing: Your funeral, so

Acked-by: Guenter Roeck <linux@roeck-us.net>

Guenter

> +	wdt->timeout = AAEON_MCU_WDT_TIMEOUT;
> +	wdt->max_hw_heartbeat_ms = AAEON_MCU_WDT_HEARTBEAT_MS;
> +
> +	watchdog_set_drvdata(wdt, data);
> +
> +	ret = aaeon_mcu_wdt_status(wdt, &enabled);
> +	if (ret)
> +		return ret;
> +
> +	if (enabled)
> +		set_bit(WDOG_HW_RUNNING, &wdt->status);
> +
> +	return devm_watchdog_register_device(dev, wdt);
> +}
> +
> +static struct platform_driver aaeon_mcu_wdt_driver = {
> +	.driver		= {
> +		.name	= "aaeon-mcu-wdt",
> +	},
> +	.probe		= aaeon_mcu_wdt_probe,
> +};
> +
> +module_platform_driver(aaeon_mcu_wdt_driver);
> +
> +MODULE_DESCRIPTION("Aaeon MCU Watchdog Driver");
> +MODULE_AUTHOR("Jérémie Dautheribes <jeremie.dautheribes@bootlin.com>");
> +MODULE_LICENSE("GPL");
> 


^ permalink raw reply

* Re: [PATCH v8 1/3] dt-bindings: spi: add SpacemiT K1 SPI support
From: Mark Brown @ 2026-04-10 15:44 UTC (permalink / raw)
  To: Guodong Xu
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Yixun Lan,
	Alex Elder, Philipp Zabel, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, linux-spi, devicetree, linux-riscv,
	spacemit, linux-kernel, Alex Elder, Conor Dooley, Troy Mitchell
In-Reply-To: <20260410-spi-spacemit-k1-v8-1-53ebb48a4146@riscstar.com>

[-- Attachment #1: Type: text/plain, Size: 517 bytes --]

On Fri, Apr 10, 2026 at 11:04:20PM -0400, Guodong Xu wrote:
> From: Alex Elder <elder@riscstar.com>
> 
> Add support for the SPI controller implemented by the SpacemiT K1 SoC.

Please submit patches using subject lines reflecting the style for the
subsystem, this makes it easier for people to identify relevant patches.
Look at what existing commits in the area you're changing are doing and
make sure your subject lines visually resemble what they're doing.
There's no need to resubmit to fix this alone.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: interconnect: qcom: document the RPMh NoC for Hawi SoC
From: Mike Tipton @ 2026-04-10 15:42 UTC (permalink / raw)
  To: Vivek Aknurwar
  Cc: Georgi Djakov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260409-icc-hawi-v3-1-851cac12a81d@oss.qualcomm.com>

On Thu, Apr 09, 2026 at 02:01:37PM -0700, Vivek Aknurwar wrote:
> Document the RPMh Network-On-Chip interconnect for the Qualcomm Hawi SoC.
> 
> Signed-off-by: Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com>
> ---
>  .../bindings/interconnect/qcom,hawi-rpmh.yaml      | 131 ++++++++++++++++
>  include/dt-bindings/interconnect/qcom,hawi-rpmh.h  | 164 +++++++++++++++++++++
>  2 files changed, 295 insertions(+)

Reviewed-by: Mike Tipton <mike.tipton@oss.qualcomm.com>

Thanks,
Mike

^ permalink raw reply

* Re: [PATCH v5 2/2] hwmon: pmbus: Add support for Sony APS-379
From: Guenter Roeck @ 2026-04-10 15:27 UTC (permalink / raw)
  To: Chris Packham
  Cc: robh, krzk+dt, conor+dt, devicetree, linux-hwmon, linux-kernel
In-Reply-To: <20260410012414.2818829-3-chris.packham@alliedtelesis.co.nz>

On Fri, Apr 10, 2026 at 01:24:12PM +1200, Chris Packham wrote:
> Add pmbus support for Sony APS-379 power supplies. There are a few PMBUS
> commands that return data that is undocumented/invalid so these need to
> be rejected with -ENXIO. The READ_VOUT command returns data in linear11
> format instead of linear16 so we need to workaround this.
> 
> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>

Applied.

Thanks,
Guenter

^ permalink raw reply

* Re: [PATCH v5 1/2] dt-bindings: trivial-devices: Add sony,aps-379
From: Guenter Roeck @ 2026-04-10 15:23 UTC (permalink / raw)
  To: Chris Packham
  Cc: robh, krzk+dt, conor+dt, devicetree, linux-hwmon, linux-kernel
In-Reply-To: <20260410012414.2818829-2-chris.packham@alliedtelesis.co.nz>

On Fri, Apr 10, 2026 at 01:24:11PM +1200, Chris Packham wrote:
> Add the compatible string for the sony,aps-379. This is a simple PMBus
> (I2C) device that requires no additional attributes.
> 
> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Applied.

Thanks,
Guenter

^ permalink raw reply

* [PATCH v8 3/3] riscv: dts: spacemit: define a SPI controller node
From: Guodong Xu @ 2026-04-11  3:04 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Alex Elder, Philipp Zabel, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-spi, devicetree, linux-riscv, spacemit, linux-kernel,
	Guodong Xu, Alex Elder, Yixun Lan
In-Reply-To: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@riscstar.com>

From: Alex Elder <elder@riscstar.com>

Define a node for the fourth SoC SPI controller (number 3) on the
SpacemiT K1 SoC.

Enable it on the Banana Pi BPI-F3 board, which exposes this feature
via its GPIO block:
  GPIO PIN 19:  MOSI
  GPIO PIN 21:  MISO
  GPIO PIN 23:  SCLK
  GPIO PIN 24:  SS (inverted)

Define pincontrol configurations for the pins as used on that board.

(This was tested using a GigaDevice GD25Q64E SPI NOR chip.)

Reviewed-by: Yixun Lan <dlan@gentoo.org>
Signed-off-by: Alex Elder <elder@riscstar.com>
Signed-off-by: Guodong Xu <guodong@riscstar.com>
---
 arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts |  7 +++++++
 arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi    | 20 ++++++++++++++++++++
 arch/riscv/boot/dts/spacemit/k1.dtsi            | 15 +++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
index 5971605754b35..61b93765f42cf 100644
--- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
@@ -14,6 +14,7 @@ aliases {
 		ethernet0 = &eth0;
 		ethernet1 = &eth1;
 		serial0 = &uart0;
+		spi3 = &spi3;
 		i2c2 = &i2c2;
 		i2c8 = &i2c8;
 	};
@@ -327,6 +328,12 @@ &pcie2 {
 	status = "okay";
 };
 
+&spi3 {
+	pinctrl-0 = <&ssp3_0_cfg>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
 &uart0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart0_2_cfg>;
diff --git a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi
index b13dcb10f4d66..34d88334e95e4 100644
--- a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi
+++ b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi
@@ -570,4 +570,24 @@ pwm14-1-pins {
 			drive-strength = <32>;
 		};
 	};
+
+	ssp3_0_cfg: ssp3-0-cfg {
+		ssp3-0-pins {
+			pinmux = <K1_PADCONF(75, 2)>,	/* SCLK */
+				 <K1_PADCONF(77, 2)>,	/* MOSI  */
+				 <K1_PADCONF(78, 2)>;	/* MISO */
+
+			bias-disable;
+			drive-strength = <19>;
+			power-source = <3300>;
+		};
+
+		ssp3-0-frm-pins {
+			pinmux = <K1_PADCONF(76, 2)>;	/* FRM (frame) */
+
+			bias-pull-up = <0>;
+			drive-strength = <19>;
+			power-source = <3300>;
+		};
+	};
 };
diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi
index 529ec68e9c23e..1ecb09e58042f 100644
--- a/arch/riscv/boot/dts/spacemit/k1.dtsi
+++ b/arch/riscv/boot/dts/spacemit/k1.dtsi
@@ -983,6 +983,21 @@ qspi: spi@d420c000 {
 				status = "disabled";
 			};
 
+			spi3: spi@d401c000 {
+				compatible = "spacemit,k1-spi";
+				reg = <0x0 0xd401c000 0x0 0x30>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				clocks = <&syscon_apbc CLK_SSP3>,
+					 <&syscon_apbc CLK_SSP3_BUS>;
+				clock-names = "core", "bus";
+				resets = <&syscon_apbc RESET_SSP3>;
+				interrupts = <55>;
+				dmas = <&pdma 20>, <&pdma 19>;
+				dma-names = "rx", "tx";
+				status = "disabled";
+			};
+
 			/* sec_uart1: 0xf0612000, not available from Linux */
 		};
 

-- 
2.43.0


^ permalink raw reply related

* [PATCH v8 2/3] spi: spacemit: introduce SpacemiT K1 SPI controller driver
From: Guodong Xu @ 2026-04-11  3:04 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Alex Elder, Philipp Zabel, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-spi, devicetree, linux-riscv, spacemit, linux-kernel,
	Guodong Xu, Alex Elder
In-Reply-To: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@riscstar.com>

From: Alex Elder <elder@riscstar.com>

This patch introduces the driver for the SPI controller found in the
SpacemiT K1 SoC.  Currently the driver supports master mode only.
The SPI hardware implements RX and TX FIFOs, 32 entries each, and
supports both PIO and DMA mode transfers.

Signed-off-by: Alex Elder <elder@riscstar.com>
Signed-off-by: Guodong Xu <guodong@riscstar.com>
---
v8: Addressing Mark Brown's v7 review:
  - Use C++ style (//) comments for the entire file header
  - Remove all open-coded DMA mapping; rely on the SPI core to
    handle DMA mapping via transfer->tx_sg/rx_sg
  - Implement can_dma() callback, replacing open-coded transfer
    length checks
  - Implement set_cs() callback for chip select control via the
    TOP_HOLD_FRAME_LOW register bit
  - Switch from transfer_one_message() to transfer_one(), letting
    the SPI core handle message-level flow control
  - DMA completion now calls spi_finalize_current_transfer()
    directly instead of using a completion
  - Add SSP_STATUS_BCE (bit count error) to error detection
  - Interrupt handler returns IRQ_NONE early if no transfer is
    active, before acknowledging interrupts
  - Update copyright year to 2026
---
 drivers/spi/Kconfig           |   9 +
 drivers/spi/Makefile          |   1 +
 drivers/spi/spi-spacemit-k1.c | 782 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 792 insertions(+)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c3b2f02f5912e..b50d9ae1a498b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -1085,6 +1085,15 @@ config SPI_SG2044_NOR
 	  also supporting 3Byte address devices and 4Byte address
 	  devices.
 
+config SPI_SPACEMIT_K1
+	tristate "K1 SPI Controller"
+	depends on ARCH_SPACEMIT || COMPILE_TEST
+	depends on OF
+	imply MMP_PDMA if ARCH_SPACEMIT
+	default m if ARCH_SPACEMIT
+	help
+	  Enable support for the SpacemiT K1 SPI controller.
+
 config SPI_SPRD
 	tristate "Spreadtrum SPI controller"
 	depends on ARCH_SPRD || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9d36190a98848..9fa12498ce8c0 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -143,6 +143,7 @@ obj-$(CONFIG_SPI_SIFIVE)		+= spi-sifive.o
 obj-$(CONFIG_SPI_SLAVE_MT27XX)          += spi-slave-mt27xx.o
 obj-$(CONFIG_SPI_SN_F_OSPI)		+= spi-sn-f-ospi.o
 obj-$(CONFIG_SPI_SG2044_NOR)	+= spi-sg2044-nor.o
+obj-$(CONFIG_SPI_SPACEMIT_K1)		+= spi-spacemit-k1.o
 obj-$(CONFIG_SPI_SPRD)			+= spi-sprd.o
 obj-$(CONFIG_SPI_SPRD_ADI)		+= spi-sprd-adi.o
 obj-$(CONFIG_SPI_STM32) 		+= spi-stm32.o
diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c
new file mode 100644
index 0000000000000..8cef633144954
--- /dev/null
+++ b/drivers/spi/spi-spacemit-k1.c
@@ -0,0 +1,782 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// SpacemiT K1 SPI controller driver
+//
+// Copyright (C) 2026, RISCstar Solutions Corporation
+// Copyright (C) 2023, SpacemiT Corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/scatterlist.h>
+#include <linux/sizes.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include "internals.h"
+
+/* This is the range of transfer rates supported by the K1 SoC */
+#define K1_SPI_MIN_SPEED_HZ		6250
+#define K1_SPI_MAX_SPEED_HZ		51200000
+
+/* DMA constraints */
+#define K1_SPI_DMA_ALIGNMENT		64
+#define K1_SPI_MAX_DMA_LEN		SZ_512K
+
+/* SSP Top Control Register */
+#define SSP_TOP_CTRL		0x00
+#define TOP_SSE				BIT(0)		/* Enable port */
+#define TOP_FRF_MASK			GENMASK(2, 1)	/* Frame format */
+#define TOP_FRF_MOTOROLA			0	/* Motorola SPI */
+#define TOP_DSS_MASK			GENMASK(9, 5)	/* Data size (1-32) */
+#define TOP_SPO				BIT(10)		/* Polarity: 0=low */
+#define TOP_SPH				BIT(11)		/* Half-cycle phase */
+#define TOP_LBM				BIT(12)		/* Loopback mode */
+#define TOP_TRAIL			BIT(13)		/* Trailing bytes */
+#define TOP_HOLD_FRAME_LOW		BIT(14)		/* Chip select */
+
+/* SSP FIFO Control Register */
+#define SSP_FIFO_CTRL		0x04
+#define FIFO_TFT_MASK			GENMASK(4, 0)	/* TX FIFO threshold */
+#define FIFO_RFT_MASK			GENMASK(9, 5)	/* RX FIFO threshold */
+#define FIFO_TSRE			BIT(10)		/* TX service request */
+#define FIFO_RSRE			BIT(11)		/* RX service request */
+
+/* SSP Interrupt Enable Register */
+#define SSP_INT_EN		0x08
+#define SSP_INT_EN_TINTE		BIT(1)		/* RX timeout */
+#define SSP_INT_EN_RIE			BIT(2)		/* RX FIFO */
+#define SSP_INT_EN_TIE			BIT(3)		/* TX FIFO */
+#define SSP_INT_EN_RIM			BIT(4)		/* RX FIFO overrun */
+#define SSP_INT_EN_TIM			BIT(5)		/* TX FIFO underrun */
+#define SSP_INT_EN_EBCEI		BIT(6)		/* Bit count error */
+
+/* TX interrupts, RX interrupts, and error interrupts */
+#define SSP_INT_EN_TX		SSP_INT_EN_TIE
+#define SSP_INT_EN_RX \
+		(SSP_INT_EN_TINTE | SSP_INT_EN_RIE)
+#define SSP_INT_EN_ERROR \
+		(SSP_INT_EN_RIM | SSP_INT_EN_TIM | SSP_INT_EN_EBCEI)
+
+/* SSP Time Out Register */
+#define SSP_TIMEOUT		0x0c
+#define SSP_TIMEOUT_MASK		GENMASK(23, 0)
+
+/* SSP Data Register */
+#define SSP_DATAR		0x10
+
+/* SSP Status Register */
+#define SSP_STATUS		0x14
+#define SSP_STATUS_BSY			BIT(0)		/* SPI/I2S busy */
+#define SSP_STATUS_TNF			BIT(6)		/* TX FIFO not full */
+#define SSP_STATUS_TFL			GENMASK(11, 7)	/* TX FIFO level */
+#define SSP_STATUS_TUR			BIT(12)		/* TX FIFO underrun */
+#define SSP_STATUS_RNE			BIT(14)		/* RX FIFO not empty */
+#define SSP_STATUS_RFL			GENMASK(19, 15)	/* RX FIFO level */
+#define SSP_STATUS_ROR			BIT(20)		/* RX FIFO overrun */
+#define SSP_STATUS_BCE			BIT(21)		/* Bit count error */
+
+/* Error status mask */
+#define SSP_STATUS_ERROR \
+		(SSP_STATUS_TUR | SSP_STATUS_ROR | SSP_STATUS_BCE)
+
+/* The FIFO sizes and thresholds are the same for RX and TX */
+#define K1_SPI_FIFO_SIZE	32
+#define K1_SPI_THRESH		(K1_SPI_FIFO_SIZE / 2)
+
+struct k1_spi_driver_data {
+	struct spi_controller *host;
+	void __iomem *base;
+	phys_addr_t base_addr;
+	unsigned long bus_rate;
+	struct clk *clk;
+	unsigned long rate;
+	int irq;
+
+	/* Current transfer information; not valid if message is null */
+	u32 bytes;			/* Bytes used for bits_per_word */
+	unsigned int rx_resid;		/* RX bytes left in transfer */
+	unsigned int tx_resid;		/* TX bytes left in transfer */
+	struct spi_transfer *transfer;	/* Current transfer */
+
+	bool dma_enabled;
+};
+
+/* Set our registers to a known initial state */
+static void
+k1_spi_register_reset(struct k1_spi_driver_data *drv_data, bool initial)
+{
+	u32 val = 0;
+
+	writel(0, drv_data->base + SSP_TOP_CTRL);
+
+	if (initial) {
+		/*
+		 * The TX and RX FIFO thresholds are the same no matter
+		 * what the speed or bits per word, so we can just set
+		 * them once.  The thresholds are one more than the values
+		 * in the register.
+		 */
+		val = FIELD_PREP(FIFO_RFT_MASK, K1_SPI_THRESH - 1);
+		val |= FIELD_PREP(FIFO_TFT_MASK, K1_SPI_THRESH - 1);
+	}
+	writel(val, drv_data->base + SSP_FIFO_CTRL);
+
+	writel(0, drv_data->base + SSP_INT_EN);
+	writel(0, drv_data->base + SSP_TIMEOUT);
+
+	/* Clear any pending interrupt conditions */
+	writel(~0, drv_data->base + SSP_STATUS);
+}
+
+/*
+ * The client can call the setup function multiple times, and each call
+ * can specify a different SPI mode (and transfer speed).  Each transfer
+ * can specify its own speed though, and the core code ensures each
+ * transfer's speed is set to something nonzero and supported by both
+ * the controller and the device.  We just set the speed for each transfer.
+ */
+static int k1_spi_setup(struct spi_device *spi)
+{
+	struct k1_spi_driver_data *drv_data;
+	u32 val;
+
+	drv_data = spi_controller_get_devdata(spi->controller);
+
+	/*
+	 * Configure the message format for this device.  We only
+	 * support Motorola SPI format in master mode.
+	 */
+	val = FIELD_PREP(TOP_FRF_MASK, TOP_FRF_MOTOROLA);
+
+	/* Translate the mode into the value used to program the hardware. */
+	if (spi->mode & SPI_CPHA)
+		val |= TOP_SPH;		/* 1/2 cycle */
+	if (spi->mode & SPI_CPOL)
+		val |= TOP_SPO;		/* active low */
+	if (spi->mode & SPI_LOOP)
+		val |= TOP_LBM;		/* enable loopback */
+	writel(val, drv_data->base + SSP_TOP_CTRL);
+
+	return 0;
+}
+
+static void k1_spi_cleanup(struct spi_device *spi)
+{
+	struct k1_spi_driver_data *drv_data;
+
+	drv_data = spi_controller_get_devdata(spi->controller);
+	k1_spi_register_reset(drv_data, false);
+}
+
+static bool k1_spi_can_dma(struct spi_controller *host, struct spi_device *spi,
+			   struct spi_transfer *transfer)
+{
+	struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
+	u32 burst_size;
+
+	if (!drv_data->dma_enabled)
+		return false;
+
+	if (transfer->len > SZ_2K)
+		return false;
+
+	/* Don't bother with DMA if we can't do even a single burst */
+	burst_size = K1_SPI_THRESH * spi_bpw_to_bytes(transfer->bits_per_word);
+
+	return transfer->len >= burst_size;
+}
+
+static void k1_spi_dma_callback(void *param)
+{
+	struct k1_spi_driver_data *drv_data = param;
+	u32 val;
+
+	val = readl(drv_data->base + SSP_FIFO_CTRL);
+	val &= ~(FIFO_TSRE | FIFO_RSRE);
+	writel(val, drv_data->base + SSP_FIFO_CTRL);
+
+	val = readl(drv_data->base + SSP_TOP_CTRL);
+	val &= ~TOP_TRAIL;
+	writel(val, drv_data->base + SSP_TOP_CTRL);
+
+	/* Check for any error conditions */
+	val = readl(drv_data->base + SSP_STATUS);
+	if (val & SSP_STATUS_ERROR)
+		drv_data->transfer->error |= SPI_TRANS_FAIL_IO;
+
+	/* Disable the port */
+	val = readl(drv_data->base + SSP_TOP_CTRL);
+	val &= ~TOP_SSE;
+	writel(val, drv_data->base + SSP_TOP_CTRL);
+
+	drv_data->transfer = NULL;
+
+	spi_finalize_current_transfer(drv_data->host);
+}
+
+/* Prepare a descriptor for TX or RX DMA */
+static struct dma_async_tx_descriptor *
+k1_spi_dma_prep(struct k1_spi_driver_data *drv_data,
+		struct spi_transfer *transfer, bool tx)
+{
+	phys_addr_t addr = drv_data->base_addr + SSP_DATAR;
+	u32 burst_size = K1_SPI_THRESH * drv_data->bytes;
+	struct dma_slave_config cfg = { };
+	enum dma_transfer_direction dir;
+	enum dma_slave_buswidth width;
+	struct dma_chan *chan;
+	struct sg_table *sgt;
+
+	width = drv_data->bytes == 1 ? DMA_SLAVE_BUSWIDTH_1_BYTE :
+		drv_data->bytes == 2 ? DMA_SLAVE_BUSWIDTH_2_BYTES :
+		/* bytes == 4 */       DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	if (tx) {
+		chan = drv_data->host->dma_tx;
+		sgt = &transfer->tx_sg;
+		dir = DMA_MEM_TO_DEV;
+
+		cfg.dst_addr = addr;
+		cfg.dst_addr_width = width;
+		cfg.dst_maxburst = burst_size;
+	} else {
+		chan = drv_data->host->dma_rx;
+		sgt = &transfer->rx_sg;
+		dir = DMA_DEV_TO_MEM;
+
+		cfg.src_addr = addr;
+		cfg.src_addr_width = width;
+		cfg.src_maxburst = burst_size;
+	}
+	cfg.direction = dir;
+
+	if (dmaengine_slave_config(chan, &cfg))
+		return NULL;
+
+	return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir,
+				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+}
+
+static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi,
+			  struct spi_transfer *transfer)
+{
+	struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
+	struct dma_async_tx_descriptor *desc;
+	u32 val;
+
+	/* Prepare the TX descriptor and submit it */
+	desc = k1_spi_dma_prep(drv_data, transfer, true);
+	if (!desc)
+		goto fallback;
+	dmaengine_submit(desc);
+
+	/* Prepare the RX descriptor and submit it */
+	desc = k1_spi_dma_prep(drv_data, transfer, false);
+	if (!desc)
+		goto fallback;
+
+	/* When RX is complete we also know TX has completed */
+	desc->callback = k1_spi_dma_callback;
+	desc->callback_param = drv_data;
+
+	dmaengine_submit(desc);
+
+	val = readl(drv_data->base + SSP_TOP_CTRL);
+	val |= TOP_TRAIL;		/* Trailing bytes handled by DMA */
+	writel(val, drv_data->base + SSP_TOP_CTRL);
+
+	val = readl(drv_data->base + SSP_FIFO_CTRL);
+	val |= FIFO_TSRE | FIFO_RSRE;
+	writel(val, drv_data->base + SSP_FIFO_CTRL);
+
+	/* Start RX first so we're ready the instant we start transmitting */
+	dma_async_issue_pending(host->dma_rx);
+	dma_async_issue_pending(host->dma_tx);
+
+	return 1;
+fallback:
+	transfer->error |= SPI_TRANS_FAIL_NO_START;
+
+	return -EAGAIN;
+}
+
+/* Flush the RX FIFO of any leftover data before processing a message */
+static int k1_spi_prepare_message(struct spi_controller *host,
+				  struct spi_message *message)
+{
+	struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
+	u32 val = readl(drv_data->base + SSP_STATUS);
+	u32 count;
+
+	/* If there's nothing in the FIFO, we're done */
+	if (!(val & SSP_STATUS_RNE))
+		return 0;
+
+	/* Read and discard what's there (one more than what the field says) */
+	count = FIELD_GET(SSP_STATUS_RFL, val) + 1;
+	do
+		(void)readl(drv_data->base + SSP_DATAR);
+	while (--count);
+
+	return 0;
+}
+
+/* Set logic level of chip select line (high=true means CS deasserted) */
+static void k1_spi_set_cs(struct spi_device *spi, bool high)
+{
+	struct k1_spi_driver_data *drv_data;
+	u32 val;
+
+	drv_data = spi_controller_get_devdata(spi->controller);
+
+	val = readl(drv_data->base + SSP_TOP_CTRL);
+	if (high)
+		val &= ~TOP_HOLD_FRAME_LOW;
+	else
+		val |= TOP_HOLD_FRAME_LOW;
+	writel(val, drv_data->base + SSP_TOP_CTRL);
+}
+
+/* Set the transfer speed; the SPI core code ensures it is supported */
+static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data,
+			    struct spi_transfer *transfer)
+{
+	struct clk *clk = drv_data->clk;
+	u64 nsec_per_word;
+	u64 bus_ticks;
+	u32 timeout;
+	u32 val;
+	int ret;
+
+	ret = clk_set_rate(clk, transfer->speed_hz);
+	if (ret)
+		return ret;
+
+	drv_data->rate = clk_get_rate(clk);
+
+	/* No need for RX FIFO timeout if we're not receiving anything */
+	if (!transfer->rx_buf)
+		return 0;
+
+	/*
+	 * Compute the RX FIFO inactivity timeout value that should be used.
+	 * The inactivity timer restarts with each word that lands in the
+	 * FIFO.  If several "word transfer times" pass without any new data
+	 * in the RX FIFO, we might as well read what's there.
+	 *
+	 * The rate at which words land in the FIFO is determined by the
+	 * word size and the transfer rate.  One bit is transferred per
+	 * clock tick, and 8 (or 16 or 32) bits are transferred per word.
+	 *
+	 * So we can get word transfer time (in nanoseconds) from:
+	 *   nsec_per_tick = NSEC_PER_SEC / drv_data->rate;
+	 *   ticks_per_word = BITS_PER_BYTE * drv_data->bytes;
+	 * We do the divide last for better accuracy.
+	 */
+	nsec_per_word = NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes;
+	nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate);
+
+	/*
+	 * The timeout (which we'll set to three word transfer times) is
+	 * expressed as a number of APB clock ticks.
+	 *   bus_ticks = 3 * nsec * (drv_data->bus_rate / NSEC_PER_SEC)
+	 */
+	bus_ticks = 3 * nsec_per_word * drv_data->bus_rate;
+	timeout = DIV_ROUND_UP_ULL(bus_ticks, NSEC_PER_SEC);
+
+	/* Set the RX timeout period (required for both DMA and PIO) */
+	val = FIELD_PREP(SSP_TIMEOUT_MASK, timeout);
+	writel(val, drv_data->base + SSP_TIMEOUT);
+
+	return 0;
+}
+
+static int k1_spi_transfer_one(struct spi_controller *host,
+			       struct spi_device *spi,
+			       struct spi_transfer *transfer)
+{
+	struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
+	u32 count;
+	u32 ctrl;
+	u32 val;
+	int ret;
+
+	/* Bits per word can change on a per-transfer basis */
+	drv_data->bytes = spi_bpw_to_bytes(transfer->bits_per_word);
+
+	/* Each transfer can also specify a different rate */
+	ret = k1_spi_set_speed(drv_data, transfer);
+	if (ret) {
+		dev_err(&host->dev,
+			"failed to set transfer speed: %d\n", ret);
+		return ret;
+	}
+
+	/* Record how many words the len bytes represent */
+	count = transfer->len / drv_data->bytes;
+	drv_data->rx_resid = count;
+	drv_data->tx_resid = count;
+
+	drv_data->transfer = transfer;
+
+	/* Clear any existing interrupt conditions */
+	writel(~0, drv_data->base + SSP_STATUS);
+
+	/* Set the data (word) size, and enable the port */
+	ctrl = readl(drv_data->base + SSP_TOP_CTRL);
+	ctrl &= ~TOP_DSS_MASK;
+	ctrl |= FIELD_PREP(TOP_DSS_MASK, transfer->bits_per_word - 1);
+	ctrl |= TOP_SSE;
+	writel(ctrl, drv_data->base + SSP_TOP_CTRL);
+
+	if (spi_xfer_is_dma_mapped(host, spi, transfer))
+		return k1_spi_dma_one(host, spi, transfer);
+
+	/* An interrupt will initiate the transfer */
+	val = SSP_INT_EN_TX | SSP_INT_EN_RX | SSP_INT_EN_ERROR;
+	writel(val, drv_data->base + SSP_INT_EN);
+
+	return 1;	/* We will call spi_finalize_current_transfer() */
+}
+
+static void
+k1_spi_handle_err(struct spi_controller *host, struct spi_message *message)
+{
+	struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
+
+	if (drv_data->dma_enabled) {
+		dmaengine_terminate_sync(host->dma_rx);
+		dmaengine_terminate_sync(host->dma_tx);
+	}
+}
+
+static void k1_spi_write_word(struct k1_spi_driver_data *drv_data)
+{
+	struct spi_transfer *transfer = drv_data->transfer;
+	u32 bytes = drv_data->bytes;
+	u32 val;
+
+	if (transfer->tx_buf) {
+		const void *buf;
+
+		buf = transfer->tx_buf + (transfer->len - drv_data->tx_resid);
+		if (bytes == 1)
+			val = *(u8 *)buf;
+		else if (bytes == 2)
+			val = *(u16 *)buf;
+		else	/* bytes == 4 */
+			val = *(u32 *)buf;
+	} else {
+		val = 0;	/* Null writer; write 1, 2, or 4 zero bytes */
+	}
+	/* Fill the next TX FIFO entry */
+	writel(val, drv_data->base + SSP_DATAR);
+
+	drv_data->tx_resid -= bytes;
+}
+
+/* The last-read status value is provided; we know SSP_STATUS_TNF is set */
+static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val)
+{
+	unsigned int count;
+
+	/* Get the number of open slots in the FIFO; zero means all */
+	count = FIELD_GET(SSP_STATUS_TFL, val) ? : K1_SPI_FIFO_SIZE;
+
+	/*
+	 * Limit how much we try to send at a time, to reduce the
+	 * chance the other side can overrun our RX FIFO.
+	 */
+	count = min3(count, K1_SPI_THRESH, drv_data->tx_resid);
+	do
+		k1_spi_write_word(drv_data);
+	while (--count);
+
+	return !drv_data->tx_resid;
+}
+
+static void k1_spi_read_word(struct k1_spi_driver_data *drv_data)
+{
+	struct spi_transfer *transfer = drv_data->transfer;
+	u32 bytes = drv_data->bytes;
+	u32 val;
+
+	/* Consume the next RX FIFO entry */
+	val = readl(drv_data->base + SSP_DATAR);
+	if (transfer->rx_buf) {
+		void *buf;
+
+		buf = transfer->rx_buf + (transfer->len - drv_data->rx_resid);
+
+		if (bytes == 1)
+			*(u8 *)buf = val;
+		else if (bytes == 2)
+			*(u16 *)buf = val;
+		else	/* bytes == 4 */
+			*(u32 *)buf = val;
+	}	/* Otherwise null reader: discard the data */
+
+	drv_data->rx_resid -= bytes;
+}
+
+/* The last-read status value is provided; we know SSP_STATUS_RNE is set */
+static bool k1_spi_read(struct k1_spi_driver_data *drv_data, u32 val)
+{
+	do {
+		unsigned int count = FIELD_GET(SSP_STATUS_RFL, val) + 1;
+
+		/* Only read what we need */
+		count = min(count, drv_data->rx_resid);
+		do
+			k1_spi_read_word(drv_data);
+		while (--count);
+
+		/* If there's no more to read, we're done */
+		if (!drv_data->rx_resid)
+			return true;
+
+		/* Check again in case more became available to read */
+		val = readl(drv_data->base + SSP_STATUS);
+		if (val & SSP_STATUS_RNE)
+			writel(SSP_STATUS_RNE, drv_data->base + SSP_STATUS);
+		else
+			return false;
+	} while (true);
+}
+
+static irqreturn_t k1_spi_ssp_isr(int irq, void *dev_id)
+{
+	struct k1_spi_driver_data *drv_data = dev_id;
+	u32 val;
+
+	/* Return immediately if we're not expecting any interrupts */
+	if (!drv_data->transfer)
+		return IRQ_NONE;
+
+	/* Get status and clear pending interrupts; all are handled below */
+	val = readl(drv_data->base + SSP_STATUS);
+	writel(val, drv_data->base + SSP_STATUS);
+
+	/* Check for any error conditions first */
+	if (val & SSP_STATUS_ERROR) {
+		drv_data->transfer->error |= SPI_TRANS_FAIL_IO;
+		goto done;
+	}
+
+	/*
+	 * For SPI, bytes are transferred in both directions equally, and
+	 * RX always follows TX.  Start by writing if there is anything to
+	 * write, then read.  Once there's no more to read, we're done.
+	 */
+	if (drv_data->tx_resid && (val & SSP_STATUS_TNF)) {
+		/* If we finish writing, disable TX interrupts */
+		if (k1_spi_write(drv_data, val)) {
+			val = SSP_INT_EN_RX | SSP_INT_EN_ERROR;
+			writel(val, drv_data->base + SSP_INT_EN);
+		}
+	}
+
+	/* We're not done unless we've read all that was requested */
+	if (drv_data->rx_resid) {
+		/* Read more if there FIFO is not empty */
+		if (val & SSP_STATUS_RNE)
+			if (k1_spi_read(drv_data, val))
+				goto done;
+
+		return IRQ_HANDLED;
+	}
+done:
+	/* Disable the port */
+	val = readl(drv_data->base + SSP_TOP_CTRL);
+	val &= ~TOP_SSE;
+	writel(val, drv_data->base + SSP_TOP_CTRL);
+
+	/* Disable all interrupts */
+	writel(0, drv_data->base + SSP_INT_EN);
+
+	drv_data->transfer = NULL;
+
+	spi_finalize_current_transfer(drv_data->host);
+
+	return IRQ_HANDLED;
+}
+
+static int
+k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev)
+{
+	struct spi_controller *host = drv_data->host;
+	struct dma_chan *chan;
+
+	chan = dma_request_chan(dev, "tx");
+	if (IS_ERR(chan))
+		return PTR_ERR(chan);
+	host->dma_tx = chan;
+
+	chan = dma_request_chan(dev, "rx");
+	if (IS_ERR(chan)) {
+		dma_release_channel(host->dma_tx);
+		host->dma_tx = NULL;
+		return PTR_ERR(chan);
+	}
+	host->dma_rx = chan;
+
+	drv_data->dma_enabled = true;
+
+	return 0;
+}
+
+static void k1_spi_dma_cleanup(struct device *dev, void *res)
+{
+	struct k1_spi_driver_data **ptr = res;
+	struct k1_spi_driver_data *drv_data = *ptr;
+	struct spi_controller *host = drv_data->host;
+
+	if (!drv_data->dma_enabled)
+		return;
+
+	drv_data->dma_enabled = false;
+
+	dma_release_channel(host->dma_rx);
+	host->dma_rx = NULL;
+	dma_release_channel(host->dma_tx);
+	host->dma_tx = NULL;
+}
+
+static int
+devm_k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev)
+{
+	struct k1_spi_driver_data **ptr;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_MMP_PDMA)) {
+		dev_info(dev, "DMA not available; using PIO\n");
+		return 0;
+	}
+
+	ptr = devres_alloc(k1_spi_dma_cleanup, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	ret = k1_spi_dma_setup(drv_data, dev);
+	if (ret) {
+		devres_free(ptr);
+		return ret;
+	}
+
+	*ptr = drv_data;
+	devres_add(dev, ptr);
+
+	return 0;
+}
+
+static int k1_spi_probe(struct platform_device *pdev)
+{
+	struct k1_spi_driver_data *drv_data;
+	struct device *dev = &pdev->dev;
+	struct reset_control *reset;
+	struct spi_controller *host;
+	struct resource *iores;
+	struct clk *clk_bus;
+	int ret;
+
+	host = devm_spi_alloc_host(dev, sizeof(*drv_data));
+	if (!host)
+		return -ENOMEM;
+	drv_data = spi_controller_get_devdata(host);
+	drv_data->host = host;
+	platform_set_drvdata(pdev, drv_data);
+
+	ret = devm_k1_spi_dma_setup(drv_data, dev);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+	if (ret)
+		dev_warn(dev, "DMA setup failed (%d), falling back to PIO\n", ret);
+
+	drv_data->base = devm_platform_get_and_ioremap_resource(pdev, 0,
+								&iores);
+	if (IS_ERR(drv_data->base))
+		return dev_err_probe(dev, PTR_ERR(drv_data->base),
+				     "error mapping memory\n");
+	drv_data->base_addr = iores->start;
+
+	clk_bus = devm_clk_get_enabled(dev, "bus");
+	if (IS_ERR(clk_bus))
+		return dev_err_probe(dev, PTR_ERR(clk_bus),
+				     "error getting/enabling bus clock\n");
+	drv_data->bus_rate = clk_get_rate(clk_bus);
+
+	drv_data->clk = devm_clk_get_enabled(dev, "core");
+	if (IS_ERR(drv_data->clk))
+		return dev_err_probe(dev, PTR_ERR(drv_data->clk),
+				     "error getting/enabling core clock\n");
+
+	reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+	if (IS_ERR(reset))
+		return dev_err_probe(dev, PTR_ERR(reset),
+				     "error getting/deasserting reset\n");
+
+	k1_spi_register_reset(drv_data, true);
+
+	drv_data->irq = platform_get_irq(pdev, 0);
+	if (drv_data->irq < 0)
+		return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n");
+
+	ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr,
+			       IRQF_SHARED, dev_name(dev), drv_data);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "error requesting IRQ\n");
+
+	/* Initialize the host structure, then register it */
+	host->dev.of_node = dev_of_node(dev);
+	host->dev.parent = dev;
+	host->num_chipselect = 1;
+	if (drv_data->dma_enabled)
+		host->dma_alignment = K1_SPI_DMA_ALIGNMENT;
+	host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
+	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
+	host->min_speed_hz = K1_SPI_MIN_SPEED_HZ;
+	host->max_speed_hz = K1_SPI_MAX_SPEED_HZ;
+	host->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX;
+	host->max_dma_len = K1_SPI_MAX_DMA_LEN;
+
+	host->setup = k1_spi_setup;
+	host->cleanup = k1_spi_cleanup;
+	host->can_dma = k1_spi_can_dma;
+	host->prepare_message = k1_spi_prepare_message;
+	host->set_cs = k1_spi_set_cs;
+	host->transfer_one = k1_spi_transfer_one;
+	host->handle_err = k1_spi_handle_err;
+
+	ret = devm_spi_register_controller(dev, host);
+	if (ret)
+		dev_err(dev, "error registering controller\n");
+
+	return ret;
+}
+
+static const struct of_device_id k1_spi_dt_ids[] = {
+	{ .compatible = "spacemit,k1-spi", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, k1_spi_dt_ids);
+
+static struct platform_driver k1_spi_driver = {
+	.probe = k1_spi_probe,
+	.driver = {
+		.name		= "k1-spi",
+		.of_match_table	= k1_spi_dt_ids,
+	},
+};
+module_platform_driver(k1_spi_driver);
+
+MODULE_DESCRIPTION("SpacemiT K1 SPI controller driver");
+MODULE_LICENSE("GPL");

-- 
2.43.0


^ permalink raw reply related

* [PATCH v8 1/3] dt-bindings: spi: add SpacemiT K1 SPI support
From: Guodong Xu @ 2026-04-11  3:04 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Alex Elder, Philipp Zabel, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-spi, devicetree, linux-riscv, spacemit, linux-kernel,
	Guodong Xu, Alex Elder, Conor Dooley, Troy Mitchell
In-Reply-To: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@riscstar.com>

From: Alex Elder <elder@riscstar.com>

Add support for the SPI controller implemented by the SpacemiT K1 SoC.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Acked-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Alex Elder <elder@riscstar.com>
Signed-off-by: Guodong Xu <guodong@riscstar.com>
---
 .../devicetree/bindings/spi/spacemit,k1-spi.yaml   | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml
new file mode 100644
index 0000000000000..e82c7f8d0b981
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/spacemit,k1-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SpacemiT K1 SoC Serial Peripheral Interface (SPI)
+
+maintainers:
+  - Alex Elder <elder@kernel.org>
+
+description:
+  The SpacemiT K1 SoC implements a SPI controller that has two 32-entry
+  FIFOs, for transmit and receive.  Details are currently available in
+  section 18.2.1 of the K1 User Manual, found in the SpacemiT Keystone
+  K1 Documentation[1].  The controller transfers words using PIO.  DMA
+  transfers are supported as well, if both TX and RX DMA channels are
+  specified,
+
+  [1] https://developer.spacemit.com/documentation
+
+allOf:
+  - $ref: /schemas/spi/spi-controller.yaml#
+
+properties:
+  compatible:
+    const: spacemit,k1-spi
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Core clock
+      - description: Bus clock
+
+  clock-names:
+    items:
+      - const: core
+      - const: bus
+
+  resets:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  dmas:
+    items:
+      - description: RX DMA channel
+      - description: TX DMA channel
+
+  dma-names:
+    items:
+      - const: rx
+      - const: tx
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - resets
+  - interrupts
+
+unevaluatedProperties: false
+
+examples:
+  - |
+
+    #include <dt-bindings/clock/spacemit,k1-syscon.h>
+    spi@d401c000 {
+        compatible = "spacemit,k1-spi";
+        reg = <0xd401c000 0x30>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        clocks = <&syscon_apbc CLK_SSP3>,
+                 <&syscon_apbc CLK_SSP3_BUS>;
+        clock-names = "core", "bus";
+        resets = <&syscon_apbc RESET_SSP3>;
+        interrupts = <55>;
+        dmas = <&pdma 20>, <&pdma 19>;
+        dma-names = "rx", "tx";
+    };

-- 
2.43.0


^ permalink raw reply related

* [PATCH v8 0/3] spi: support the SpacemiT K1 SPI controller
From: Guodong Xu @ 2026-04-11  3:04 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Alex Elder, Philipp Zabel, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-spi, devicetree, linux-riscv, spacemit, linux-kernel,
	Guodong Xu, Alex Elder, Conor Dooley, Troy Mitchell, Yixun Lan

This series adds support for the SPI controller found in the SpacemiT
K1 SoC.  The driver currently supports only master mode.  The controller
has two 32-entry FIFOs and supports PIO and DMA for transfers.

Starting with v8, I am taking over from Alex Elder to shepherd this
series upstream.  Alex developed versions 1 through 7.

Version 8 reworks the driver based on Mark Brown's review of v7, making
much better use of the SPI core framework.

(Note, this is a distinct series from the QSPI driver, which was
merged recently.)

 -
 Guodong

Between version 7 and version 8:
  - Use // comments for the file header (Mark Brown)
  - Remove open-coded DMA mapping (k1_spi_map_dma_buffer(),
    k1_spi_unmap_dma_buffer(), k1_spi_map_dma_buffers(), the dummy
    buffer, k1_spi_io struct); use SPI core DMA mapping via
    transfer->tx_sg/rx_sg instead
  - Add can_dma() callback, replacing open-coded transfer length
    checks
  - Add set_cs() callback for chip select control via the
    TOP_HOLD_FRAME_LOW bit
  - Switch from transfer_one_message() to transfer_one()
  - DMA completion calls spi_finalize_current_transfer() directly
    instead of using a completion
  - Add SSP_STATUS_BCE (bit count error) to error detection
  - Return IRQ_NONE early if no transfer is active, before
    acknowledging interrupts
  - Simplify k1_spi_driver_data struct
  - ~160 fewer lines of code

Here is version 7 of this series:
  https://lore.kernel.org/lkml/20251114185745.2838358-1-elder@riscstar.com/

Between version 6 and version 7:
  - DIV_ROUND_UP_ULL() is now used when setting the speed, to address
    two errors reported by the Intel kernel test robot on 32-bit builds
  - Fixed a bug interpreting the resource pointer in k1_spi_dma_cleanup()
  - The driver is now built as a module by default, if ARCH_SPACEMIT
    is defined

Here is version 6 of this series:
  https://lore.kernel.org/lkml/20251027125504.297033-1-elder@riscstar.com/

Between version 5 and version 6:
  - Rebase only

Here is version 5 of this series:
  https://lore.kernel.org/lkml/20251013123309.2252042-1-elder@riscstar.com/

Between version 4 and version 5:
  - Added Yixun's Reviewed-by tag on patch 3

Here is version 4 of this series:
  https://lore.kernel.org/lkml/20250925121714.2514932-1-elder@riscstar.com/

Between version 3 and version 4 (all suggested by Yixun):
  - Fixed an underrun/overrun comment error
  - Renamed a pinctrl node
  - Formatted dmas and dma-names properties on one line

Here is version 3 of this series:
  https://lore.kernel.org/lkml/20250922161717.1590690-1-elder@riscstar.com/

Between version 2 and version 3:
  - Add Conor's Acked-by to patch 1
  - Add Rob's Reviewed-by to patch 1
  - Added imply_PDMA to the SPI_SPACEMIT_K1 Kconfig option
  - Fixed a bug pointed out by Vivian (and Troy) in word-sized reads
  - Added a comment stating we use 1, 2, or 4 bytes per word
  - Cleaned up DMA channels properly in case of failure setting up
  - No longer use devm_*() for allocating DMA channels or buffer
  - Moved the SPI controller into the dma-bus memory region

Here is version 2 of this series:
  https://lore.kernel.org/lkml/20250919155914.935608-1-elder@riscstar.com/

Between version 1 and version 2:
  - Use enum rather than const for the binding compatible string
  - Omit the label and status property in the binding example
  - The spi-spacemit-k1.o make target is now added in sorted order
  - The SPI_SPACEMIT_K1 config option is added in sorted order
  - The SPI_SPACEMIT_K1 config does *not* depend on MMP_PDMA,
    however MMP_PDMA is checked at runtime, and if not enabled,
    DMA will not be used
  - Read/modify/writes of registers no longer use an additional
    "virt" variable to hold the address accessed
  - The k1_spi_driver_data->ioaddr field has been renamed base
  - The DMA address for the base address is maintained, rather than
    saving the DMA address of the data register
  - The spi-max-frequency property value is now bounds checked
  - A local variable is now initialized to 0 in k1_spi_write_word()
  - The driver name is now "k1-spi"
  - DT aliases are used rather than spacemit,k1-ssp-id for bus number
  - The order of two pin control properties was changed as requested
  - Clock names and DMA names are now on one line in the "k1.dtsi"
  - The interrupts property is used rather than interrupts-extended

Here is version 1 of this series:
  https://lore.kernel.org/lkml/20250917220724.288127-1-elder@riscstar.com/

Alex Elder (3):
  dt-bindings: spi: add SpacemiT K1 SPI support
  spi: spacemit: introduce SpacemiT K1 SPI controller driver
  riscv: dts: spacemit: define a SPI controller node

Signed-off-by: Guodong Xu <guodong@riscstar.com>
---
Alex Elder (3):
      dt-bindings: spi: add SpacemiT K1 SPI support
      spi: spacemit: introduce SpacemiT K1 SPI controller driver
      riscv: dts: spacemit: define a SPI controller node

 .../devicetree/bindings/spi/spacemit,k1-spi.yaml   |  84 +++
 arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts    |   7 +
 arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi       |  20 +
 arch/riscv/boot/dts/spacemit/k1.dtsi               |  15 +
 drivers/spi/Kconfig                                |   9 +
 drivers/spi/Makefile                               |   1 +
 drivers/spi/spi-spacemit-k1.c                      | 782 +++++++++++++++++++++
 7 files changed, 918 insertions(+)
---
base-commit: 7aaa8047eafd0bd628065b15757d9b48c5f9c07d
change-id: 20260407-spi-spacemit-k1-e0957c311152

Best regards,
--  
Guodong Xu <guodong@riscstar.com>


^ permalink raw reply

* Re: [PATCH v7 1/2] arm64: dts: qcom: glymur: Add USB related nodes
From: Rob Herring @ 2026-04-10 14:52 UTC (permalink / raw)
  To: Abel Vesa
  Cc: Bjorn Andersson, Konrad Dybcio, Krzysztof Kozlowski, Conor Dooley,
	Greg Kroah-Hartman, Wesley Cheng, Pankaj Patil, linux-arm-msm,
	devicetree, linux-kernel, linux-usb, Wesley Cheng, Konrad Dybcio,
	Dmitry Baryshkov
In-Reply-To: <20260320-dts-qcom-glymur-add-usb-support-v7-1-ba367eda6010@oss.qualcomm.com>

On Fri, Mar 20, 2026 at 12:56:52PM +0200, Abel Vesa wrote:
> From: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
> 
> The Glymur USB subsystem contains three USB 3.2 Gen 2 controllers,
> one USB 3.2 multi-port controller, and one USB 2.0-only controller.
> This includes five SS USB QMP PHYs (three combo and two UNI) and six M31
> eUSB2 PHYs.
> 
> All controllers are based on SNPS DWC3, so describe them as Qualcomm
> flattened DWC3 nodes.
> 
> Signed-off-by: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
> Co-developed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> Tested-by: Pankaj Patil <pankaj.patil@oss.qualcomm.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
> ---
>  arch/arm64/boot/dts/qcom/glymur.dtsi | 691 ++++++++++++++++++++++++++++++++++-
>  1 file changed, 686 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
> index bde287f645ee..641707ba1e78 100644
> --- a/arch/arm64/boot/dts/qcom/glymur.dtsi
> +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
> @@ -750,11 +750,11 @@ gcc: clock-controller@100000 {
>  				 <0>,				/* UFS PHY RX Symbol 0 */
>  				 <0>,				/* UFS PHY RX Symbol 1 */
>  				 <0>,				/* UFS PHY TX Symbol 0 */
> -				 <0>,				/* USB3 PHY 0 */
> -				 <0>,				/* USB3 PHY 1 */
> -				 <0>,				/* USB3 PHY 2 */
> -				 <0>,				/* USB3 UNI PHY pipe 0 */
> -				 <0>,				/* USB3 UNI PHY pipe 1 */
> +				 <&usb_0_qmpphy QMP_USB43DP_USB3_PIPE_CLK>,
> +				 <&usb_1_qmpphy QMP_USB43DP_USB3_PIPE_CLK>,
> +				 <&usb_2_qmpphy QMP_USB43DP_USB3_PIPE_CLK>,
> +				 <&usb_mp_qmpphy0 QMP_USB43DP_USB3_PIPE_CLK>,
> +				 <&usb_mp_qmpphy1 QMP_USB43DP_USB3_PIPE_CLK>,
>  				 <0>,				/* USB4 PHY 0 pcie pipe */
>  				 <0>,				/* USB4 PHY 0 Max pipe */
>  				 <0>,				/* USB4 PHY 1 pcie pipe */
> @@ -2264,6 +2264,254 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
>  			};
>  		};
>  
> +		usb_hs_phy: phy@fa0000 {
> +			compatible = "qcom,glymur-m31-eusb2-phy",
> +				     "qcom,sm8750-m31-eusb2-phy";
> +			reg = <0x0 0x00fa0000 0x0 0x154>;
> +			#phy-cells = <0>;
> +
> +			clocks = <&tcsr TCSR_USB2_1_CLKREF_EN>;
> +			clock-names = "ref";
> +
> +			resets = <&gcc GCC_QUSB2PHY_USB20_HS_BCR>;
> +
> +			status = "disabled";
> +		};
> +
> +		usb_mp_hsphy0: phy@fa1000 {
> +			compatible = "qcom,glymur-m31-eusb2-phy",
> +				     "qcom,sm8750-m31-eusb2-phy";
> +
> +			reg = <0x0 0x00fa1000 0x0 0x29c>;
> +			#phy-cells = <0>;
> +
> +			clocks = <&tcsr TCSR_USB2_1_CLKREF_EN>;
> +			clock-names = "ref";
> +
> +			resets = <&gcc GCC_QUSB2PHY_HS0_MP_BCR>;
> +
> +			status = "disabled";
> +		};
> +
> +		usb_mp_hsphy1: phy@fa2000  {
> +			compatible = "qcom,glymur-m31-eusb2-phy",
> +				     "qcom,sm8750-m31-eusb2-phy";
> +
> +			reg = <0x0 0x00fa2000 0x0 0x29c>;
> +			#phy-cells = <0>;
> +
> +			clocks = <&tcsr TCSR_USB2_2_CLKREF_EN>;
> +			clock-names = "ref";
> +
> +			resets = <&gcc GCC_QUSB2PHY_HS1_MP_BCR>;
> +
> +			status = "disabled";
> +		};
> +
> +		usb_mp_qmpphy0: phy@fa3000 {
> +			compatible = "qcom,glymur-qmp-usb3-uni-phy";
> +			reg = <0x0 0x00fa3000 0x0 0x2000>;
> +
> +			clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>,
> +				 <&tcsr TCSR_USB3_0_CLKREF_EN>,
> +				 <&rpmhcc RPMH_CXO_CLK>,
> +				 <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>,
> +				 <&gcc GCC_USB3_MP_PHY_PIPE_0_CLK>;
> +			clock-names = "aux",
> +				      "clkref",
> +				      "ref",
> +				      "com_aux",
> +				      "pipe";
> +
> +			power-domains = <&gcc GCC_USB3_MP_SS0_PHY_GDSC>;
> +
> +			resets = <&gcc GCC_USB3_MP_SS0_PHY_BCR>,
> +				 <&gcc GCC_USB3UNIPHY_PHY_MP0_BCR>;
> +			reset-names = "phy",
> +				      "phy_phy";
> +
> +			clock-output-names = "usb3_uni_phy_0_pipe_clk_src";
> +			#clock-cells = <0>;
> +			#phy-cells = <0>;
> +
> +			status = "disabled";
> +		};
> +
> +		usb_mp_qmpphy1: phy@fa5000 {
> +			compatible = "qcom,glymur-qmp-usb3-uni-phy";
> +			reg = <0x0 0x00fa5000 0x0 0x2000>;
> +
> +			clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>,
> +				 <&tcsr TCSR_USB3_1_CLKREF_EN>,
> +				 <&rpmhcc RPMH_CXO_CLK>,
> +				 <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>,
> +				 <&gcc GCC_USB3_MP_PHY_PIPE_1_CLK>;
> +			clock-names = "aux",
> +				      "clkref",
> +				      "ref",
> +				      "com_aux",
> +				      "pipe";

New warnings:

      4 (qcom,glymur-qmp-usb3-uni-phy): clock-names: ['aux', 'clkref', 'ref', 'com_aux', 'pipe'] is too long
      4 (qcom,glymur-qmp-usb3-uni-phy): clock-names:3: 'pipe' was expected
      4 (qcom,glymur-qmp-usb3-uni-phy): clock-names:2: 'com_aux' was expected
      4 (qcom,glymur-qmp-usb3-uni-phy): clock-names:1: 'ref' was expected
      1 (qcom,glymur-qmp-usb3-uni-phy): clocks: [[70, 329], [42, 9], [58, 0], [70, 331], [70, 332]] is too long
      1 (qcom,glymur-qmp-usb3-uni-phy): clocks: [[70, 329], [42, 10], [58, 0], [70, 331], [70, 334]] is too long
      1 (qcom,glymur-qmp-usb3-uni-phy): clocks: [[56, 329], [29, 9], [44, 0], [56, 331], [56, 332]] is too long
      1 (qcom,glymur-qmp-usb3-uni-phy): clocks: [[56, 329], [29, 10], [44, 0], [56, 331], [56, 334]] is too long


You did test this series for DT warnings before sending, right? Please 
fix and ensure they get into 7.1.

Rob

^ permalink raw reply

* Re: [PATCH v1 5/5] irqchip: starfive: Implement irq_set_type and irq_ack hooks
From: Thomas Gleixner @ 2026-04-10 14:46 UTC (permalink / raw)
  To: Changhuang Liang, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260410090106.622781-6-changhuang.liang@starfivetech.com>

On Fri, Apr 10 2026 at 02:01, Changhuang Liang wrote:

s/hooks/callbacks and please use function notation irq_set_type() and ....

> +static void starfive_intc_mod(struct starfive_irq_chip *irqc, u32 reg,
> +			      u32 mask, u32 data)

No line break required. You have 100 characters.

> +static int starfive_intc_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
> +	u32 i, bitpos, ty_pos, ty_shift, tmp;
> +
> +	i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
> +	bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
> +	ty_pos = bitpos / STARFIVE_INTC_TYPE_NUM;
> +	ty_shift = (bitpos % STARFIVE_INTC_TYPE_NUM) * 2;
> +
> +	switch (type) {
> +	case IRQF_TRIGGER_LOW:
> +		tmp = STARFIVE_INTC_TRIGGER_LOW << ty_shift;

tmp is not really an intuitive variable name.

> +		irq_set_handler_locked(d, handle_level_irq);
> +		break;
> +	case IRQF_TRIGGER_HIGH:
> +		tmp = STARFIVE_INTC_TRIGGER_HIGH << ty_shift;
> +		irq_set_handler_locked(d, handle_level_irq);
> +		break;
> +	case IRQF_TRIGGER_FALLING:
> +		tmp = STARFIVE_INTC_TRIGGER_NEGEDGE << ty_shift;
> +		irq_set_handler_locked(d, handle_edge_irq);
> +		break;
> +	case IRQF_TRIGGER_RISING:
> +		tmp = STARFIVE_INTC_TRIGGER_POSEDGE << ty_shift;
> +		irq_set_handler_locked(d, handle_edge_irq);

This can be simplified so it avoids to have a function in every case
statement:

        switch (type) {
        case IRQF_TRIGGER_LOW:
        	trigger = STARFIVE_INTC_TRIGGER_LOW;
                handler = handle_level_irq;
                break;
        case ...
        }
        
        irq_set_handler_locked(d, handler);
        typeval = trigger << ty_shift;

You get the idea.

> +	raw_spin_lock(&irqc->lock);

  guard(...)

Thanks,

        tglx

^ permalink raw reply

* Re: [PATCH v1 4/5] irqchip: starfive: Increase the interrupt source number up to 64
From: Thomas Gleixner @ 2026-04-10 14:37 UTC (permalink / raw)
  To: Changhuang Liang, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260410090106.622781-5-changhuang.liang@starfivetech.com>

On Fri, Apr 10 2026 at 02:01, Changhuang Liang wrote:

> From: Mason Huo <mason.huo@starfivetech.com>
>
> StarFive JHB100 SoC interrupt controller actually supports 64 interrupt
> sources, the original code only supported up to 32. now it is extended
> to 64.
>
> Signed-off-by: Mason Huo <mason.huo@starfivetech.com>
> Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
> ---
>  drivers/irqchip/irq-starfive-jhb100-intc.c | 43 ++++++++++++++--------
>  1 file changed, 28 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/irqchip/irq-starfive-jhb100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c
> index 312a4634870a..d5ecbb603a58 100644
> --- a/drivers/irqchip/irq-starfive-jhb100-intc.c
> +++ b/drivers/irqchip/irq-starfive-jhb100-intc.c
> @@ -18,10 +18,11 @@
>  #include <linux/reset.h>
>  #include <linux/spinlock.h>
>  
> -#define STARFIVE_INTC_SRC0_CLEAR	0x10
> -#define STARFIVE_INTC_SRC0_MASK		0x14
> -#define STARFIVE_INTC_SRC0_INT		0x1c
> +#define STARFIVE_INTC_SRC_CLEAR(n)	(0x10 + ((n) * 0x20))
> +#define STARFIVE_INTC_SRC_MASK(n)	(0x14 + ((n) * 0x20))
> +#define STARFIVE_INTC_SRC_INT(n)	(0x1c + ((n) * 0x20))
>  
> +#define STARFIVE_INTC_NUM		2
>  #define STARFIVE_INTC_SRC_IRQ_NUM	32
>  
>  struct starfive_irq_chip {
> @@ -53,18 +54,26 @@ static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc,
>  static void starfive_intc_unmask(struct irq_data *d)
>  {
>  	struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
> +	int i, bitpos;
> +
> +	i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
> +	bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
>  
>  	raw_spin_lock(&irqc->lock);
> -	starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq));
> +	starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos));
>  	raw_spin_unlock(&irqc->lock);
>  }

As you are touching this code, please convert the locking to guard()

  	guard(raw_spinlock)(&irqc->lock);
	starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq));


> +	for (i = 0; i < STARFIVE_INTC_NUM; i++) {

  for (int i = 0; ...)

Thanks,

        tglx

^ permalink raw reply

* Re: [PATCH v1 3/5] irqchip: starfive: Use devm_ interfaces to simplify resource release
From: Thomas Gleixner @ 2026-04-10 14:32 UTC (permalink / raw)
  To: Changhuang Liang, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260410090106.622781-4-changhuang.liang@starfivetech.com>

On Fri, Apr 10 2026 at 02:01, Changhuang Liang wrote:
> -	irqc->base = of_iomap(intc, 0);
> +	irqc->base = devm_platform_ioremap_resource(pdev, 0);
>  	if (!irqc->base) {
> -		pr_err("Unable to map registers\n");
> +		dev_err(&pdev->dev, "unable to map registers\n");
>  		ret = -ENXIO;
>  		goto err_free;

You can eliminate err_free as well by doing:

    struct startfive_irq_chip *irqc __free(kfree) = kzalloc_obj(*irqc);

    ....
    dev_info(..);

    retain_and_null_ptr(irqc);
    return 0;

Thanks,

        tglx

^ permalink raw reply

* Re: [PATCH v1 2/5] irqchip: starfive: Convert the word "jh8100" to "jhb100"
From: Thomas Gleixner @ 2026-04-10 14:26 UTC (permalink / raw)
  To: Changhuang Liang, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260410090106.622781-3-changhuang.liang@starfivetech.com>

On Fri, Apr 10 2026 at 02:01, Changhuang Liang wrote:

Please use the documented prefix scheme, i.e. irqchip/....:

Your subject line might be more descriptive when it says:

     Rename jh8100 to jhb100

Thanks,

        tglx

^ permalink raw reply

* [PATCH v2 3/3] arm64: dts: broadcom: rpi-5: Add RP1 PWM node
From: Andrea della Porta @ 2026-04-10 14:09 UTC (permalink / raw)
  To: Uwe Kleine-König, linux-pwm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Broadcom internal kernel review list, Andrea della Porta,
	devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
	Naushir Patuck, Stanimir Varbanov, mbrugger
In-Reply-To: <cover.1775829499.git.andrea.porta@suse.com>

From: Stanimir Varbanov <svarbanov@suse.de>

The RP1 chipset used on the Raspberry Pi 5 features an integrated
PWM controller to drive the cooling fan.

Add the corresponding DT node for this PWM controller.

Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Co-developed-by: Andrea della Porta <andrea.porta@suse.com>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
 arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 12 ++++++++++++
 arch/arm64/boot/dts/broadcom/rp1-common.dtsi     | 10 ++++++++++
 2 files changed, 22 insertions(+)

diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
index 2856082814462..a4e5ba23bf536 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
@@ -64,12 +64,24 @@ phy1: ethernet-phy@1 {
 };
 
 &rp1_gpio {
+	fan_pwm_default_state: fan-pwm-default-state {
+		function = "pwm1";
+		pins = "gpio45";
+		bias-pull-down;
+	};
+
 	usb_vbus_default_state: usb-vbus-default-state {
 		function = "vbus1";
 		groups = "vbus1";
 	};
 };
 
+&rp1_pwm {
+	pinctrl-0 = <&fan_pwm_default_state>;
+	pinctrl-names = "default";
+	status = "okay";
+};
+
 &rp1_usb0 {
 	pinctrl-0 = <&usb_vbus_default_state>;
 	pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
index 5a815c3797945..d0f4d6be75500 100644
--- a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
+++ b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
@@ -26,6 +26,16 @@ rp1_clocks: clocks@40018000 {
 				       <200000000>;  // RP1_CLK_SYS
 	};
 
+	rp1_pwm: pwm@4009c000 {
+		compatible = "raspberrypi,rp1-pwm";
+		reg = <0x00 0x4009c000  0x0 0x100>;
+		clocks = <&rp1_clocks RP1_CLK_PWM1>;
+		assigned-clocks = <&rp1_clocks RP1_CLK_PWM1>;
+		assigned-clock-rates = <50000000>;
+		#pwm-cells = <3>;
+		status = "disabled";
+	};
+
 	rp1_gpio: pinctrl@400d0000 {
 		compatible = "raspberrypi,rp1-gpio";
 		reg = <0x00 0x400d0000  0x0 0xc000>,
-- 
2.35.3


^ permalink raw reply related

* [PATCH v2 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Andrea della Porta @ 2026-04-10 14:09 UTC (permalink / raw)
  To: Uwe Kleine-König, linux-pwm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Broadcom internal kernel review list, Andrea della Porta,
	devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
	Naushir Patuck, Stanimir Varbanov, mbrugger
In-Reply-To: <cover.1775829499.git.andrea.porta@suse.com>

From: Naushir Patuck <naush@raspberrypi.com>

The Raspberry Pi RP1 southbridge features an embedded PWM
controller with 4 output channels, alongside an RPM interface
to read the fan speed on the Raspberry Pi 5.

Add the supporting driver.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
 drivers/pwm/Kconfig   |   9 ++
 drivers/pwm/Makefile  |   1 +
 drivers/pwm/pwm-rp1.c | 344 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 354 insertions(+)
 create mode 100644 drivers/pwm/pwm-rp1.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6f3147518376a..32031f2af75af 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -625,6 +625,15 @@ config PWM_ROCKCHIP
 	  Generic PWM framework driver for the PWM controller found on
 	  Rockchip SoCs.
 
+config PWM_RASPBERRYPI_RP1
+	bool "RP1 PWM support"
+	depends on MISC_RP1 || COMPILE_TEST
+	depends on HAS_IOMEM
+	select REGMAP_MMIO
+	select MFD_SYSCON
+	help
+	  PWM framework driver for Raspberry Pi RP1 controller.
+
 config PWM_SAMSUNG
 	tristate "Samsung PWM support"
 	depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0dc0d2b69025d..59f29f60f9123 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT)	+= pwm-rzg2l-gpt.o
 obj-$(CONFIG_PWM_RENESAS_RZ_MTU3)	+= pwm-rz-mtu3.o
 obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
 obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
+obj-$(CONFIG_PWM_RASPBERRYPI_RP1)	+= pwm-rp1.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_SIFIVE)	+= pwm-sifive.o
 obj-$(CONFIG_PWM_SL28CPLD)	+= pwm-sl28cpld.o
diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c
new file mode 100644
index 0000000000000..b88c697d9567e
--- /dev/null
+++ b/drivers/pwm/pwm-rp1.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pwm-rp1.c
+ *
+ * Raspberry Pi RP1 PWM.
+ *
+ * Copyright © 2026 Raspberry Pi Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ * Based on the pwm-bcm2835 driver by:
+ * Bart Tanghe <bart.tanghe@thomasmore.be>
+ *
+ * Datasheet: https://pip-assets.raspberrypi.com/categories/892-raspberry-pi-5/documents/RP-008370-DS-1-rp1-peripherals.pdf?disposition=inline
+ *
+ * Limitations:
+ * - Channels can be enabled/disabled and their duty cycle and period can
+ *   be updated glitchlessly. Update are synchronized with the next strobe
+ *   at the end of the current period of the respective channel, once the
+ *   update bit is set. The update flag is global, not per-channel.
+ * - Channels are phase-capable, but on RPi5, the firmware can use a channel
+ *   phase register to report the RPM of the fan connected to that PWM
+ *   channel. As a result, phase control will be ignored for now.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define RP1_PWM_GLOBAL_CTRL	0x000
+#define RP1_PWM_CHANNEL_CTRL(x)	(0x014 + ((x) * 0x10))
+#define RP1_PWM_RANGE(x)	(0x018 + ((x) * 0x10))
+#define RP1_PWM_PHASE(x)	(0x01C + ((x) * 0x10))
+#define RP1_PWM_DUTY(x)		(0x020 + ((x) * 0x10))
+
+/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */
+#define RP1_PWM_CHANNEL_DEFAULT		(BIT(8) + BIT(0))
+#define RP1_PWM_CHANNEL_ENABLE(x)	BIT(x)
+#define RP1_PWM_POLARITY		BIT(3)
+#define RP1_PWM_SET_UPDATE		BIT(31)
+#define RP1_PWM_MODE_MASK		GENMASK(1, 0)
+
+#define RP1_PWM_NUM_PWMS	4
+
+struct rp1_pwm {
+	struct regmap	*regmap;
+	struct clk	*clk;
+	unsigned long	clk_rate;
+	bool		clk_enabled;
+};
+
+struct rp1_pwm_waveform {
+	u32	period_ticks;
+	u32	duty_ticks;
+	bool	enabled;
+	bool	inverted_polarity;
+};
+
+static const struct regmap_config rp1_pwm_regmap_config = {
+	.reg_bits    = 32,
+	.val_bits    = 32,
+	.reg_stride  = 4,
+	.max_register = 0x60,
+};
+
+static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+	u32 value;
+
+	/* update the changed registers on the next strobe to avoid glitches */
+	regmap_read(rp1->regmap, RP1_PWM_GLOBAL_CTRL, &value);
+	value |= RP1_PWM_SET_UPDATE;
+	regmap_write(rp1->regmap, RP1_PWM_GLOBAL_CTRL, value);
+}
+
+static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+
+	/* init channel to reset defaults */
+	regmap_write(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), RP1_PWM_CHANNEL_DEFAULT);
+	return 0;
+}
+
+static int rp1_pwm_round_waveform_tohw(struct pwm_chip *chip,
+				       struct pwm_device *pwm,
+				       const struct pwm_waveform *wf,
+				       void *_wfhw)
+{
+	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+	struct rp1_pwm_waveform *wfhw = _wfhw;
+	u64 clk_rate = rp1->clk_rate;
+	u64 ticks;
+
+	ticks = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC);
+
+	if (ticks > U32_MAX)
+		ticks = U32_MAX;
+	wfhw->period_ticks = ticks;
+
+	if (wf->duty_offset_ns + wf->duty_length_ns >= wf->period_length_ns) {
+		ticks = mul_u64_u64_div_u64(wf->period_length_ns - wf->duty_length_ns,
+					    clk_rate, NSEC_PER_SEC);
+		wfhw->inverted_polarity = true;
+	} else {
+		ticks = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC);
+		wfhw->inverted_polarity = false;
+	}
+
+	if (ticks > wfhw->period_ticks)
+		ticks = wfhw->period_ticks;
+	wfhw->duty_ticks = ticks;
+
+	wfhw->enabled = !!wfhw->duty_ticks;
+
+	return 0;
+}
+
+static int rp1_pwm_round_waveform_fromhw(struct pwm_chip *chip,
+					 struct pwm_device *pwm,
+					 const void *_wfhw,
+					 struct pwm_waveform *wf)
+{
+	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+	const struct rp1_pwm_waveform *wfhw = _wfhw;
+	u64 clk_rate = rp1->clk_rate;
+	u32 ticks;
+
+	memset(wf, 0, sizeof(*wf));
+
+	if (!wfhw->enabled)
+		return 0;
+
+	wf->period_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->period_ticks * NSEC_PER_SEC, clk_rate);
+
+	if (wfhw->inverted_polarity) {
+		wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,
+						      clk_rate);
+	} else {
+		wf->duty_offset_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,
+						      clk_rate);
+		ticks = wfhw->period_ticks - wfhw->duty_ticks;
+		wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)ticks * NSEC_PER_SEC, clk_rate);
+	}
+
+	return 0;
+}
+
+static int rp1_pwm_write_waveform(struct pwm_chip *chip,
+				  struct pwm_device *pwm,
+				  const void *_wfhw)
+{
+	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+	const struct rp1_pwm_waveform *wfhw = _wfhw;
+	u32 value;
+
+	/* set period and duty cycle */
+	regmap_write(rp1->regmap,
+		     RP1_PWM_RANGE(pwm->hwpwm), wfhw->period_ticks);
+	regmap_write(rp1->regmap,
+		     RP1_PWM_DUTY(pwm->hwpwm), wfhw->duty_ticks);
+
+	/* set polarity */
+	regmap_read(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), &value);
+	if (!wfhw->inverted_polarity)
+		value &= ~RP1_PWM_POLARITY;
+	else
+		value |= RP1_PWM_POLARITY;
+	regmap_write(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), value);
+
+	/* enable/disable */
+	regmap_read(rp1->regmap, RP1_PWM_GLOBAL_CTRL, &value);
+	if (wfhw->enabled)
+		value |= RP1_PWM_CHANNEL_ENABLE(pwm->hwpwm);
+	else
+		value &= ~RP1_PWM_CHANNEL_ENABLE(pwm->hwpwm);
+	regmap_write(rp1->regmap, RP1_PWM_GLOBAL_CTRL, value);
+
+	rp1_pwm_apply_config(chip, pwm);
+
+	return 0;
+}
+
+static int rp1_pwm_read_waveform(struct pwm_chip *chip,
+				 struct pwm_device *pwm,
+				 void *_wfhw)
+{
+	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+	struct rp1_pwm_waveform *wfhw = _wfhw;
+	u32 value;
+
+	regmap_read(rp1->regmap, RP1_PWM_GLOBAL_CTRL, &value);
+	wfhw->enabled = !!(value & RP1_PWM_CHANNEL_ENABLE(pwm->hwpwm));
+
+	regmap_read(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), &value);
+	wfhw->inverted_polarity = !!(value & RP1_PWM_POLARITY);
+
+	if (wfhw->enabled) {
+		regmap_read(rp1->regmap, RP1_PWM_RANGE(pwm->hwpwm), &wfhw->period_ticks);
+		regmap_read(rp1->regmap, RP1_PWM_DUTY(pwm->hwpwm), &wfhw->duty_ticks);
+	} else {
+		wfhw->period_ticks = 0;
+		wfhw->duty_ticks = 0;
+	}
+
+	return 0;
+}
+
+static const struct pwm_ops rp1_pwm_ops = {
+	.sizeof_wfhw = sizeof(struct rp1_pwm_waveform),
+	.request = rp1_pwm_request,
+	.round_waveform_tohw = rp1_pwm_round_waveform_tohw,
+	.round_waveform_fromhw = rp1_pwm_round_waveform_fromhw,
+	.read_waveform = rp1_pwm_read_waveform,
+	.write_waveform = rp1_pwm_write_waveform,
+};
+
+static int rp1_pwm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	unsigned long clk_rate;
+	struct pwm_chip *chip;
+	void __iomem	*base;
+	struct rp1_pwm *rp1;
+	int ret;
+
+	chip = devm_pwmchip_alloc(dev, RP1_PWM_NUM_PWMS, sizeof(*rp1));
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);
+
+	rp1 = pwmchip_get_drvdata(chip);
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	rp1->regmap = devm_regmap_init_mmio(dev, base, &rp1_pwm_regmap_config);
+	if (IS_ERR(rp1->regmap))
+		return dev_err_probe(dev, PTR_ERR(rp1->regmap), "Cannot initialize regmap\n");
+
+	ret = of_syscon_register_regmap(np, rp1->regmap);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to register syscon\n");
+
+	rp1->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(rp1->clk))
+		return dev_err_probe(dev, PTR_ERR(rp1->clk), "Clock not found\n");
+
+	ret = clk_prepare_enable(rp1->clk);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable clock\n");
+	rp1->clk_enabled = true;
+
+	ret = devm_clk_rate_exclusive_get(dev, rp1->clk);
+	if (ret) {
+		dev_err_probe(dev, ret, "Fail to get exclusive rate\n");
+		goto err_disable_clk;
+	}
+
+	clk_rate = clk_get_rate(rp1->clk);
+	if (!clk_rate) {
+		ret = dev_err_probe(dev, -EINVAL, "Failed to get clock rate\n");
+		goto err_disable_clk;
+	}
+	rp1->clk_rate = clk_rate;
+
+	chip->ops = &rp1_pwm_ops;
+
+	platform_set_drvdata(pdev, chip);
+
+	ret = devm_pwmchip_add(dev, chip);
+	if (ret) {
+		dev_err_probe(dev, ret, "Failed to register PWM chip\n");
+		goto err_disable_clk;
+	}
+
+	return 0;
+
+err_disable_clk:
+	clk_disable_unprepare(rp1->clk);
+
+	return ret;
+}
+
+static int rp1_pwm_suspend(struct device *dev)
+{
+	struct rp1_pwm *rp1 = dev_get_drvdata(dev);
+
+	if (rp1->clk_enabled) {
+		clk_disable_unprepare(rp1->clk);
+		rp1->clk_enabled = false;
+	}
+
+	return 0;
+}
+
+static int rp1_pwm_resume(struct device *dev)
+{
+	struct rp1_pwm *rp1 = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(rp1->clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock on resume: %d\n", ret);
+		return ret;
+	}
+
+	rp1->clk_enabled = true;
+
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(rp1_pwm_pm_ops, rp1_pwm_suspend, rp1_pwm_resume);
+
+static const struct of_device_id rp1_pwm_of_match[] = {
+	{ .compatible = "raspberrypi,rp1-pwm" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
+
+static struct platform_driver rp1_pwm_driver = {
+	.probe = rp1_pwm_probe,
+	.driver = {
+		.name = "rp1-pwm",
+		.of_match_table = rp1_pwm_of_match,
+		.pm = pm_ptr(&rp1_pwm_pm_ops),
+		.suppress_bind_attrs = true,
+	},
+};
+module_platform_driver(rp1_pwm_driver);
+
+MODULE_DESCRIPTION("RP1 PWM driver");
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>");
+MODULE_LICENSE("GPL");
-- 
2.35.3


^ permalink raw reply related

* [PATCH v2 1/3] dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
From: Andrea della Porta @ 2026-04-10 14:09 UTC (permalink / raw)
  To: Uwe Kleine-König, linux-pwm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Broadcom internal kernel review list, Andrea della Porta,
	devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
	Naushir Patuck, Stanimir Varbanov, mbrugger
In-Reply-To: <cover.1775829499.git.andrea.porta@suse.com>

From: Naushir Patuck <naush@raspberrypi.com>

Add the devicetree binding documentation for the PWM
controller found in the Raspberry Pi RP1 chipset.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
 .../bindings/pwm/raspberrypi,rp1-pwm.yaml     | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml

diff --git a/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
new file mode 100644
index 0000000000000..6f8461d0454f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/raspberrypi,rp1-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Raspberry Pi RP1 PWM controller
+
+maintainers:
+  - Naushir Patuck <naush@raspberrypi.com>
+
+allOf:
+  - $ref: pwm.yaml#
+
+description: |
+  The PWM peripheral is a flexible waveform generator with a
+  variety of operational modes. It has the following features:
+   - four independent output channels
+   - 32-bit counter widths
+   - Seven output generation modes
+   - Optional per-channel output inversion
+   - Optional duty-cycle data FIFO with DMA support
+   - Optional sigma-delta noise shaping engine
+  Serves as a fan speed provider to other nodes for a PWM-connected
+  fan using shared registers (syscon).
+
+properties:
+  compatible:
+    const: raspberrypi,rp1-pwm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  "#pwm-cells":
+    const: 3
+
+required:
+  - compatible
+  - reg
+  - clocks
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    pwm@98000 {
+      compatible = "raspberrypi,rp1-pwm";
+      reg = <0x98000 0x100>;
+      clocks = <&rp1_clocks 17>;
+      #pwm-cells = <3>;
+    };
-- 
2.35.3


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox