[{"id":3675995,"web_url":"http://patchwork.ozlabs.org/comment/3675995/","msgid":"<adkrHkANCzxO8KUP@monoceros>","list_archive_url":null,"date":"2026-04-10T17:31:18","subject":"Re: [PATCH v2 2/3] pwm: rp1: Add RP1 PWM controller driver","submitter":{"id":88416,"url":"http://patchwork.ozlabs.org/api/people/88416/","name":"Uwe Kleine-König","email":"ukleinek@kernel.org"},"content":"Hello Andrea,\n\nnice work for a v2!\n\nOn Fri, Apr 10, 2026 at 04:09:58PM +0200, Andrea della Porta wrote:\n> From: Naushir Patuck <naush@raspberrypi.com>\n> \n> The Raspberry Pi RP1 southbridge features an embedded PWM\n> controller with 4 output channels, alongside an RPM interface\n> to read the fan speed on the Raspberry Pi 5.\n> \n> Add the supporting driver.\n> \n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>\n> Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>\n> Signed-off-by: Andrea della Porta <andrea.porta@suse.com>\n> ---\n>  drivers/pwm/Kconfig   |   9 ++\n>  drivers/pwm/Makefile  |   1 +\n>  drivers/pwm/pwm-rp1.c | 344 ++++++++++++++++++++++++++++++++++++++++++\n>  3 files changed, 354 insertions(+)\n>  create mode 100644 drivers/pwm/pwm-rp1.c\n> \n> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig\n> index 6f3147518376a..32031f2af75af 100644\n> --- a/drivers/pwm/Kconfig\n> +++ b/drivers/pwm/Kconfig\n> @@ -625,6 +625,15 @@ config PWM_ROCKCHIP\n>  \t  Generic PWM framework driver for the PWM controller found on\n>  \t  Rockchip SoCs.\n>  \n> +config PWM_RASPBERRYPI_RP1\n> +\tbool \"RP1 PWM support\"\n> +\tdepends on MISC_RP1 || COMPILE_TEST\n> +\tdepends on HAS_IOMEM\n> +\tselect REGMAP_MMIO\n> +\tselect MFD_SYSCON\n> +\thelp\n> +\t  PWM framework driver for Raspberry Pi RP1 controller.\n> +\n>  config PWM_SAMSUNG\n>  \ttristate \"Samsung PWM support\"\n>  \tdepends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST\n> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile\n> index 0dc0d2b69025d..59f29f60f9123 100644\n> --- a/drivers/pwm/Makefile\n> +++ b/drivers/pwm/Makefile\n> @@ -56,6 +56,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT)\t+= pwm-rzg2l-gpt.o\n>  obj-$(CONFIG_PWM_RENESAS_RZ_MTU3)\t+= pwm-rz-mtu3.o\n>  obj-$(CONFIG_PWM_RENESAS_TPU)\t+= pwm-renesas-tpu.o\n>  obj-$(CONFIG_PWM_ROCKCHIP)\t+= pwm-rockchip.o\n> +obj-$(CONFIG_PWM_RASPBERRYPI_RP1)\t+= pwm-rp1.o\n>  obj-$(CONFIG_PWM_SAMSUNG)\t+= pwm-samsung.o\n>  obj-$(CONFIG_PWM_SIFIVE)\t+= pwm-sifive.o\n>  obj-$(CONFIG_PWM_SL28CPLD)\t+= pwm-sl28cpld.o\n> diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c\n> new file mode 100644\n> index 0000000000000..b88c697d9567e\n> --- /dev/null\n> +++ b/drivers/pwm/pwm-rp1.c\n> @@ -0,0 +1,344 @@\n> +// SPDX-License-Identifier: GPL-2.0\n> +/*\n> + * pwm-rp1.c\n> + *\n> + * Raspberry Pi RP1 PWM.\n> + *\n> + * Copyright © 2026 Raspberry Pi Ltd.\n> + *\n> + * Author: Naushir Patuck (naush@raspberrypi.com)\n> + *\n> + * Based on the pwm-bcm2835 driver by:\n> + * Bart Tanghe <bart.tanghe@thomasmore.be>\n> + *\n> + * Datasheet: https://pip-assets.raspberrypi.com/categories/892-raspberry-pi-5/documents/RP-008370-DS-1-rp1-peripherals.pdf?disposition=inline\n> + *\n> + * Limitations:\n> + * - Channels can be enabled/disabled and their duty cycle and period can\n> + *   be updated glitchlessly. Update are synchronized with the next strobe\n> + *   at the end of the current period of the respective channel, once the\n> + *   update bit is set. The update flag is global, not per-channel.\n> + * - Channels are phase-capable, but on RPi5, the firmware can use a channel\n> + *   phase register to report the RPM of the fan connected to that PWM\n> + *   channel. As a result, phase control will be ignored for now.\n> + */\n> +\n> +#include <linux/bitops.h>\n> +#include <linux/clk.h>\n> +#include <linux/err.h>\n> +#include <linux/io.h>\n> +#include <linux/module.h>\n> +#include <linux/of.h>\n> +#include <linux/platform_device.h>\n> +#include <linux/pwm.h>\n> +#include <linux/regmap.h>\n> +#include <linux/mfd/syscon.h>\n> +\n> +#define RP1_PWM_GLOBAL_CTRL\t0x000\n> +#define RP1_PWM_CHANNEL_CTRL(x)\t(0x014 + ((x) * 0x10))\n> +#define RP1_PWM_RANGE(x)\t(0x018 + ((x) * 0x10))\n> +#define RP1_PWM_PHASE(x)\t(0x01C + ((x) * 0x10))\n> +#define RP1_PWM_DUTY(x)\t\t(0x020 + ((x) * 0x10))\n> +\n> +/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */\n> +#define RP1_PWM_CHANNEL_DEFAULT\t\t(BIT(8) + BIT(0))\n\nPlease add a #define for BIT(8) and then use that and\nFIELD_PREP(RP1_PWM_MODE, RP1_PWM_MODE_SOMENICENAME) to define the\nconstant. Also I would define it below the register defines.\n\n> +#define RP1_PWM_CHANNEL_ENABLE(x)\tBIT(x)\n> +#define RP1_PWM_POLARITY\t\tBIT(3)\n> +#define RP1_PWM_SET_UPDATE\t\tBIT(31)\n> +#define RP1_PWM_MODE_MASK\t\tGENMASK(1, 0)\n\ns/_MASK// please\n\nIt would be great if the bitfield's names started with the register\nname.\n\n> +\n> +#define RP1_PWM_NUM_PWMS\t4\n> +\n> +struct rp1_pwm {\n> +\tstruct regmap\t*regmap;\n> +\tstruct clk\t*clk;\n> +\tunsigned long\tclk_rate;\n> +\tbool\t\tclk_enabled;\n> +};\n> +\n> +struct rp1_pwm_waveform {\n> +\tu32\tperiod_ticks;\n> +\tu32\tduty_ticks;\n> +\tbool\tenabled;\n> +\tbool\tinverted_polarity;\n> +};\n> +\n> +static const struct regmap_config rp1_pwm_regmap_config = {\n> +\t.reg_bits    = 32,\n> +\t.val_bits    = 32,\n> +\t.reg_stride  = 4,\n> +\t.max_register = 0x60,\n\nI'm not a fan of aligning the = in a struct, still more if it fails like\nhere. Please consistently align all =s, or even better, use a single\nspace before each =. (Same for the struct definitions above, but I won't\ninsist.)\n\n> +};\n> +\n> +static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)\n> +{\n> +\tstruct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);\n> +\tu32 value;\n> +\n> +\t/* update the changed registers on the next strobe to avoid glitches */\n> +\tregmap_read(rp1->regmap, RP1_PWM_GLOBAL_CTRL, &value);\n> +\tvalue |= RP1_PWM_SET_UPDATE;\n> +\tregmap_write(rp1->regmap, RP1_PWM_GLOBAL_CTRL, value);\n\nI assume there is a glitch if I update two channels and the old\nconfiguration of the first channel ends while I'm in the middle of\nconfiguring the second?\n\n> +}\n> +\n> +static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)\n> +{\n> +\tstruct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);\n> +\n> +\t/* init channel to reset defaults */\n> +\tregmap_write(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), RP1_PWM_CHANNEL_DEFAULT);\n> +\treturn 0;\n> +}\n> +\n> +static int rp1_pwm_round_waveform_tohw(struct pwm_chip *chip,\n> +\t\t\t\t       struct pwm_device *pwm,\n> +\t\t\t\t       const struct pwm_waveform *wf,\n> +\t\t\t\t       void *_wfhw)\n> +{\n> +\tstruct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);\n> +\tstruct rp1_pwm_waveform *wfhw = _wfhw;\n> +\tu64 clk_rate = rp1->clk_rate;\n> +\tu64 ticks;\n\n\tif (!wf->period_length_ns)\n\t\twfhw->enabled = false\n\t\treturn 0;\n\n> +\tticks = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC);\n\nTo ensure this doesn't overflow please fail to probe the driver if\nclk_rate > 1 GHz with an explaining comment. (Or alternatively calculate\nthe length of period_ticks = U32_MAX and skip the calculation if\nwf->period_length_ns is bigger.)\n\n> +\tif (ticks > U32_MAX)\n> +\t\tticks = U32_MAX;\n> +\twfhw->period_ticks = ticks;\n\nWhat happens if wf->period_length_ns > 0 but ticks == 0?\n\n> +\tif (wf->duty_offset_ns + wf->duty_length_ns >= wf->period_length_ns) {\n\nThe maybe surprising effect here is that in the two cases\n\n\twf->duty_offset_ns == wf->period_length_ns and wf->duty_length_ns == 0\n\nand\n\t\n\twf->duty_length_ns == wf->period_length_ns and wf->duty_offset_ns == 0\n\nyou're configuring inverted polarity. I doesn't matter technically\nbecause the result is the same, but for consumers still using pwm_state\nthis is irritating. That's why pwm-stm32 uses inverted polarity only if\nalso wf->duty_length_ns and wf->duty_offset_ns are non-zero.\n\n> +\t\tticks = mul_u64_u64_div_u64(wf->period_length_ns - wf->duty_length_ns,\n> +\t\t\t\t\t    clk_rate, NSEC_PER_SEC);\n\nThe rounding is wrong here. You should pick the biggest duty_length not\nbigger than wf->duty_length_ns, so you have to use\n\n\tticks = wfhw->period_ticks - mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC):\n\n. I see this is a hole in the pwmtestperf coverage.\n\n> +\t\twfhw->inverted_polarity = true;\n> +\t} else {\n> +\t\tticks = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC);\n> +\t\twfhw->inverted_polarity = false;\n> +\t}\n> +\n> +\tif (ticks > wfhw->period_ticks)\n> +\t\tticks = wfhw->period_ticks;\n\nYou can and should assume that wf->duty_length_ns <=\nwf->period_length_ns. Then the if condition can never become true.\n\n> +\twfhw->duty_ticks = ticks;\n> +\n> +\twfhw->enabled = !!wfhw->duty_ticks;\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int rp1_pwm_round_waveform_fromhw(struct pwm_chip *chip,\n> +\t\t\t\t\t struct pwm_device *pwm,\n> +\t\t\t\t\t const void *_wfhw,\n> +\t\t\t\t\t struct pwm_waveform *wf)\n> +{\n> +\tstruct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);\n> +\tconst struct rp1_pwm_waveform *wfhw = _wfhw;\n> +\tu64 clk_rate = rp1->clk_rate;\n> +\tu32 ticks;\n> +\n> +\tmemset(wf, 0, sizeof(*wf));\n\n\twf = (struct pwm_waveform){ };\n\nis usually more efficient.\n\n> +\tif (!wfhw->enabled)\n> +\t\treturn 0;\n> +\n> +\twf->period_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->period_ticks * NSEC_PER_SEC, clk_rate);\n> +\n> +\tif (wfhw->inverted_polarity) {\n> +\t\twf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,\n> +\t\t\t\t\t\t      clk_rate);\n> +\t} else {\n> +\t\twf->duty_offset_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,\n> +\t\t\t\t\t\t      clk_rate);\n> +\t\tticks = wfhw->period_ticks - wfhw->duty_ticks;\n> +\t\twf->duty_length_ns = DIV_ROUND_UP_ULL((u64)ticks * NSEC_PER_SEC, clk_rate);\n> +\t}\n\nThis needs adaption after the rounding issue in tohw is fixed.\n\n> +\treturn 0;\n> +}\n> +\n> +static int rp1_pwm_write_waveform(struct pwm_chip *chip,\n> +\t\t\t\t  struct pwm_device *pwm,\n> +\t\t\t\t  const void *_wfhw)\n> +{\n> +\tstruct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);\n> +\tconst struct rp1_pwm_waveform *wfhw = _wfhw;\n> +\tu32 value;\n> +\n> +\t/* set period and duty cycle */\n> +\tregmap_write(rp1->regmap,\n> +\t\t     RP1_PWM_RANGE(pwm->hwpwm), wfhw->period_ticks);\n> +\tregmap_write(rp1->regmap,\n> +\t\t     RP1_PWM_DUTY(pwm->hwpwm), wfhw->duty_ticks);\n> +\n> +\t/* set polarity */\n> +\tregmap_read(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), &value);\n> +\tif (!wfhw->inverted_polarity)\n> +\t\tvalue &= ~RP1_PWM_POLARITY;\n> +\telse\n> +\t\tvalue |= RP1_PWM_POLARITY;\n> +\tregmap_write(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), value);\n> +\n> +\t/* enable/disable */\n> +\tregmap_read(rp1->regmap, RP1_PWM_GLOBAL_CTRL, &value);\n> +\tif (wfhw->enabled)\n> +\t\tvalue |= RP1_PWM_CHANNEL_ENABLE(pwm->hwpwm);\n> +\telse\n> +\t\tvalue &= ~RP1_PWM_CHANNEL_ENABLE(pwm->hwpwm);\n> +\tregmap_write(rp1->regmap, RP1_PWM_GLOBAL_CTRL, value);\n\nYou can exit early if wfhw->enabled is false after clearing the channel\nenable bit.\n\n> +\trp1_pwm_apply_config(chip, pwm);\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int rp1_pwm_read_waveform(struct pwm_chip *chip,\n> +\t\t\t\t struct pwm_device *pwm,\n> +\t\t\t\t void *_wfhw)\n> +{\n> +\tstruct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);\n> +\tstruct rp1_pwm_waveform *wfhw = _wfhw;\n> +\tu32 value;\n> +\n> +\tregmap_read(rp1->regmap, RP1_PWM_GLOBAL_CTRL, &value);\n> +\twfhw->enabled = !!(value & RP1_PWM_CHANNEL_ENABLE(pwm->hwpwm));\n> +\n> +\tregmap_read(rp1->regmap, RP1_PWM_CHANNEL_CTRL(pwm->hwpwm), &value);\n> +\twfhw->inverted_polarity = !!(value & RP1_PWM_POLARITY);\n> +\n> +\tif (wfhw->enabled) {\n> +\t\tregmap_read(rp1->regmap, RP1_PWM_RANGE(pwm->hwpwm), &wfhw->period_ticks);\n> +\t\tregmap_read(rp1->regmap, RP1_PWM_DUTY(pwm->hwpwm), &wfhw->duty_ticks);\n> +\t} else {\n> +\t\twfhw->period_ticks = 0;\n> +\t\twfhw->duty_ticks = 0;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +static const struct pwm_ops rp1_pwm_ops = {\n> +\t.sizeof_wfhw = sizeof(struct rp1_pwm_waveform),\n> +\t.request = rp1_pwm_request,\n> +\t.round_waveform_tohw = rp1_pwm_round_waveform_tohw,\n> +\t.round_waveform_fromhw = rp1_pwm_round_waveform_fromhw,\n> +\t.read_waveform = rp1_pwm_read_waveform,\n> +\t.write_waveform = rp1_pwm_write_waveform,\n> +};\n> +\n> +static int rp1_pwm_probe(struct platform_device *pdev)\n> +{\n> +\tstruct device *dev = &pdev->dev;\n> +\tstruct device_node *np = dev->of_node;\n> +\tunsigned long clk_rate;\n> +\tstruct pwm_chip *chip;\n> +\tvoid __iomem\t*base;\n> +\tstruct rp1_pwm *rp1;\n> +\tint ret;\n> +\n> +\tchip = devm_pwmchip_alloc(dev, RP1_PWM_NUM_PWMS, sizeof(*rp1));\n> +\tif (IS_ERR(chip))\n> +\t\treturn PTR_ERR(chip);\n> +\n> +\trp1 = pwmchip_get_drvdata(chip);\n> +\n> +\tbase = devm_platform_ioremap_resource(pdev, 0);\n> +\tif (IS_ERR(base))\n> +\t\treturn PTR_ERR(base);\n> +\n> +\trp1->regmap = devm_regmap_init_mmio(dev, base, &rp1_pwm_regmap_config);\n> +\tif (IS_ERR(rp1->regmap))\n> +\t\treturn dev_err_probe(dev, PTR_ERR(rp1->regmap), \"Cannot initialize regmap\\n\");\n> +\n> +\tret = of_syscon_register_regmap(np, rp1->regmap);\n> +\tif (ret)\n> +\t\treturn dev_err_probe(dev, ret, \"Failed to register syscon\\n\");\n> +\n> +\trp1->clk = devm_clk_get(dev, NULL);\n> +\tif (IS_ERR(rp1->clk))\n> +\t\treturn dev_err_probe(dev, PTR_ERR(rp1->clk), \"Clock not found\\n\");\n> +\n> +\tret = clk_prepare_enable(rp1->clk);\n> +\tif (ret)\n> +\t\treturn dev_err_probe(dev, ret, \"Failed to enable clock\\n\");\n> +\trp1->clk_enabled = true;\n> +\n> +\tret = devm_clk_rate_exclusive_get(dev, rp1->clk);\n> +\tif (ret) {\n> +\t\tdev_err_probe(dev, ret, \"Fail to get exclusive rate\\n\");\n\ns/Fail/Failed/\n\n> +\t\tgoto err_disable_clk;\n> +\t}\n> +\n> +\tclk_rate = clk_get_rate(rp1->clk);\n> +\tif (!clk_rate) {\n> +\t\tret = dev_err_probe(dev, -EINVAL, \"Failed to get clock rate\\n\");\n> +\t\tgoto err_disable_clk;\n> +\t}\n> +\trp1->clk_rate = clk_rate;\n> +\n> +\tchip->ops = &rp1_pwm_ops;\n> +\n> +\tplatform_set_drvdata(pdev, chip);\n> +\n> +\tret = devm_pwmchip_add(dev, chip);\n> +\tif (ret) {\n> +\t\tdev_err_probe(dev, ret, \"Failed to register PWM chip\\n\");\n> +\t\tgoto err_disable_clk;\n> +\t}\n> +\n> +\treturn 0;\n> +\n> +err_disable_clk:\n> +\tclk_disable_unprepare(rp1->clk);\n> +\n> +\treturn ret;\n> +}\n\nOn remove you miss to balance the call to clk_prepare_enable() (if no\nfailed call to clk_prepare_enable() in rp1_pwm_resume() happend).\n\n> +\n> +static int rp1_pwm_suspend(struct device *dev)\n> +{\n> +\tstruct rp1_pwm *rp1 = dev_get_drvdata(dev);\n> +\n> +\tif (rp1->clk_enabled) {\n> +\t\tclk_disable_unprepare(rp1->clk);\n> +\t\trp1->clk_enabled = false;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int rp1_pwm_resume(struct device *dev)\n> +{\n> +\tstruct rp1_pwm *rp1 = dev_get_drvdata(dev);\n> +\tint ret;\n> +\n> +\tret = clk_prepare_enable(rp1->clk);\n> +\tif (ret) {\n> +\t\tdev_err(dev, \"Failed to enable clock on resume: %d\\n\", ret);\n\nPlease use %pe for error codes.\n\n> +\t\treturn ret;\n> +\t}\n> +\n> +\trp1->clk_enabled = true;\n> +\n> +\treturn 0;\n> +}\n\nBest regards\nUwe","headers":{"Return-Path":"\n <linux-pwm+bounces-8550-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-pwm@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=C8rvFum1;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=linux-pwm+bounces-8550-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"C8rvFum1\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201"],"Received":["from tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fskPB5CVhz1yGS\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 11 Apr 2026 03:31:26 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 3BF8F300F163\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 10 Apr 2026 17:31:23 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id BDEDC3D3309;\n\tFri, 10 Apr 2026 17:31:21 +0000 (UTC)","from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A29717A305;\n\tFri, 10 Apr 2026 17:31:21 +0000 (UTC)","by smtp.kernel.org (Postfix) with ESMTPSA id 95EA7C19421;\n\tFri, 10 Apr 2026 17:31:20 +0000 (UTC)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775842281; cv=none;\n b=pfhKOqedUQQqHJLWH1Cfy8mtslGXzyglg5Kc3cEG2vx/rcxQ7mi6Vzb9b+hj8zIyCg9mkm1pVp5wx932AUk/X/yVA6p4uc6lVnPvLgJRxtK9kTFEaRec8LhGDNz+G7u4zvThpNTBTBZndje+v9S0u8JQOTym2kjNAL8VeN7b/Hg=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775842281; c=relaxed/simple;\n\tbh=o8dVBEqQuZ5eT0MB2sJSiDn/K4QNVAm7ZQiuW/IByCk=;\n\th=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version:\n\t Content-Type:Content-Disposition:In-Reply-To;\n b=SKyBOdY1MQvgQtpHy3Afwtv3QmHG9qOC/ZvQ+SkDK2NDm7INeX9VywzDC8yMI/Hfq2rMA2Wf4lv+vRuA9wZdGFSJXp3Sd0w5Yk9+/whyGkLwLQB3Q8pBLgyQD2Cwd5B2ifqQIgxzx4qv8KbtVZtuPzyyjm9KTL7TnTASZfoj9js=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=C8rvFum1; arc=none smtp.client-ip=10.30.226.201","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1775842281;\n\tbh=o8dVBEqQuZ5eT0MB2sJSiDn/K4QNVAm7ZQiuW/IByCk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=C8rvFum1oDZTa1EpKY0OE7qkWVAY/n8Qpw5qj9z4uJgLc+eR12jfSFthSUu5XjS86\n\t ERsWj9IqBTnHYsiGcBdu2CMuY0axjOKhfJTJRp/intXmnNcDQRTnw+NdvRN70DbRPU\n\t mFkgL0H2uRk1ksAKVXl68cgWQidmEDNOXrCtFLfICA4p9hcPhNE4dXVfD3D3uKNMbT\n\t ntEWtOcQyGUSu5dN97V0ujLp9WMtSf85ilgT5TVJRHU9zqKiHeBFfc+Etn3yfQ9Ciq\n\t HzDVZtdd1E09HhUNzZVqkFZrjHPSLjZLVkw18CCp3KBBc9qq+oBeVRzDXpznCQ/iPF\n\t AHbHJCSxBsLEg==","Date":"Fri, 10 Apr 2026 19:31:18 +0200","From":"Uwe =?utf-8?q?Kleine-K=C3=B6nig?= <ukleinek@kernel.org>","To":"Andrea della Porta <andrea.porta@suse.com>","Cc":"linux-pwm@vger.kernel.org, Rob Herring <robh@kernel.org>,\n\tKrzysztof Kozlowski <krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>,\n\tFlorian Fainelli <florian.fainelli@broadcom.com>,\n\tBroadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>,\n devicetree@vger.kernel.org, linux-rpi-kernel@lists.infradead.org,\n\tlinux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,\n\tNaushir Patuck <naush@raspberrypi.com>,\n Stanimir Varbanov <svarbanov@suse.de>, mbrugger@suse.com","Subject":"Re: [PATCH v2 2/3] pwm: rp1: Add RP1 PWM controller driver","Message-ID":"<adkrHkANCzxO8KUP@monoceros>","References":"<cover.1775829499.git.andrea.porta@suse.com>\n <0d99317b9150310dfbd98de1cb2a890f0bffe7cd.1775829499.git.andrea.porta@suse.com>","Precedence":"bulk","X-Mailing-List":"linux-pwm@vger.kernel.org","List-Id":"<linux-pwm.vger.kernel.org>","List-Subscribe":"<mailto:linux-pwm+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-pwm+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha512;\n\tprotocol=\"application/pgp-signature\"; boundary=\"d5bgry64vqq6zout\"","Content-Disposition":"inline","In-Reply-To":"\n <0d99317b9150310dfbd98de1cb2a890f0bffe7cd.1775829499.git.andrea.porta@suse.com>"}}]