Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v2 21/21] gpio: add GPIO controller found on Waveshare DSI TOUCH panels
From: Jie Gan @ 2026-04-12  1:06 UTC (permalink / raw)
  To: Dmitry Baryshkov, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Cong Yang, Ondrej Jirman, Javier Martinez Canillas, Jagan Teki,
	Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski
  Cc: dri-devel, devicetree, linux-kernel, linux-gpio, Riccardo Mereu
In-Reply-To: <20260411-waveshare-dsi-touch-v2-21-75cdbeac5156@oss.qualcomm.com>



On 4/11/2026 8:10 PM, Dmitry Baryshkov wrote:
> The Waveshare DSI TOUCH family of panels has separate on-board GPIO
> controller, which controls power supplies to the panel and the touch
> screen and provides reset pins for both the panel and the touchscreen.
> Also it provides a simple PWM controller for panel backlight. Add
> support for this GPIO controller.
> 
> Tested-by: Riccardo Mereu <r.mereu@arduino.cc>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
>   drivers/gpio/Kconfig              |  10 ++
>   drivers/gpio/Makefile             |   1 +
>   drivers/gpio/gpio-waveshare-dsi.c | 208 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 219 insertions(+)
> 
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index dbe7c6e63eab..1b210c451151 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -805,6 +805,16 @@ config GPIO_VISCONTI
>   	help
>   	  Say yes here to support GPIO on Tohisba Visconti.
>   
> +config GPIO_WAVESHARE_DSI_TOUCH
> +	tristate "Waveshare GPIO controller for DSI panels"
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	  Enable support for the GPIO and PWM controller found on Waveshare DSI
> +	  TOUCH panel kits. It provides GPIOs (used for regulator control and
> +          resets) and backlight support.
> +
>   config GPIO_WCD934X
>   	tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver"
>   	depends on MFD_WCD934X
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 20d4a57afdaa..75ce89fc3b93 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -207,6 +207,7 @@ obj-$(CONFIG_GPIO_VIRTUSER)		+= gpio-virtuser.o
>   obj-$(CONFIG_GPIO_VIRTIO)		+= gpio-virtio.o
>   obj-$(CONFIG_GPIO_VISCONTI)		+= gpio-visconti.o
>   obj-$(CONFIG_GPIO_VX855)		+= gpio-vx855.o
> +obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH)	+= gpio-waveshare-dsi.o
>   obj-$(CONFIG_GPIO_WCD934X)		+= gpio-wcd934x.o
>   obj-$(CONFIG_GPIO_WHISKEY_COVE)		+= gpio-wcove.o
>   obj-$(CONFIG_GPIO_WINBOND)		+= gpio-winbond.o
> diff --git a/drivers/gpio/gpio-waveshare-dsi.c b/drivers/gpio/gpio-waveshare-dsi.c
> new file mode 100644
> index 000000000000..f4a1d4d3b872
> --- /dev/null
> +++ b/drivers/gpio/gpio-waveshare-dsi.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2024 Waveshare International Limited
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/err.h>
> +#include <linux/fb.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +
> +/* I2C registers of the microcontroller. */
> +#define REG_TP		0x94
> +#define REG_LCD		0x95
> +#define REG_PWM		0x96
> +#define REG_SIZE	0x97
> +#define REG_ID		0x98
> +#define REG_VERSION	0x99
> +
> +enum {
> +	GPIO_AVDD = 0,
> +	GPIO_PANEL_RESET = 1,
> +	GPIO_BL_ENABLE = 2,
> +	GPIO_IOVCC = 4,
> +	GPIO_VCC = 8,
> +	GPIO_TS_RESET = 9,
> +};
> +
> +#define NUM_GPIO 16
> +
> +struct waveshare_gpio {
> +	struct mutex dir_lock;
> +	struct mutex pwr_lock;
> +	struct regmap *regmap;
> +	u16 poweron_state;
> +
> +	struct gpio_chip gc;
> +};
> +
> +static const struct regmap_config waveshare_gpio_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = REG_PWM,

.max_register = REG_VERSION,

check comments in probe

> +};
> +
> +static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset)
> +{
> +	u16 pwr_state;
> +
> +	guard(mutex)(&state->pwr_lock);
> +	pwr_state = state->poweron_state & BIT(offset);
> +
> +	return !!pwr_state;
> +}
> +
> +static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value)
> +{
> +	u16 last_val;
> +	int err;
> +
> +	guard(mutex)(&state->pwr_lock);
> +
> +	last_val = state->poweron_state;
> +	if (value)
> +		last_val |= BIT(offset);
> +	else
> +		last_val &= ~BIT(offset);
> +
> +	state->poweron_state = last_val;
> +
> +	err = regmap_write(state->regmap, REG_TP, last_val >> 8);
> +	if (!err)
> +		err = regmap_write(state->regmap, REG_LCD, last_val & 0xff);
> +
> +	return err;
> +}
> +
> +static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
> +{
> +	return GPIO_LINE_DIRECTION_OUT;
> +}
> +
> +static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset)
> +{
> +	struct waveshare_gpio *state = gpiochip_get_data(gc);
> +
> +	return waveshare_gpio_get(state, offset);
> +}
> +
> +static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
> +{
> +	struct waveshare_gpio *state = gpiochip_get_data(gc);
> +
> +	return waveshare_gpio_set(state, offset, value);
> +}
> +
> +static int waveshare_gpio_update_status(struct backlight_device *bl)
> +{
> +	struct waveshare_gpio *state = bl_get_data(bl);
> +	int brightness = backlight_get_brightness(bl);
> +
> +	waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness);
> +
> +	return regmap_write(state->regmap, REG_PWM, brightness);
> +}
> +
> +static const struct backlight_ops waveshare_gpio_bl = {
> +	.update_status = waveshare_gpio_update_status,
> +};
> +
> +static int waveshare_gpio_probe(struct i2c_client *i2c)
> +{
> +	struct backlight_properties props = {};
> +	struct waveshare_gpio *state;
> +	struct device *dev = &i2c->dev;
> +	struct backlight_device *bl;
> +	struct regmap *regmap;
> +	unsigned int data;
> +	int ret;
> +
> +	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return -ENOMEM;
> +
> +	ret = devm_mutex_init(dev, &state->dir_lock);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_mutex_init(dev, &state->pwr_lock);
> +	if (ret)
> +		return ret;
> +
> +	regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config);

for waveshare_gpio_regmap_config: .max_register = REG_PWM,

> +	if (IS_ERR(regmap))
> +		return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
> +
> +	state->regmap = regmap;
> +	i2c_set_clientdata(i2c, state);
> +
> +	ret = regmap_read(regmap, REG_ID, &data);

REG_ID here is 0x98,

in _regmap_read -> regmap_readable will check the max_register, then 
return -EIO.

> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to read register\n");
> +
> +	dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data);
> +
> +	ret = regmap_read(regmap, REG_SIZE, &data);

ditto.

Thanks,
Jie

> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to read register\n");
> +
> +	dev_dbg(dev, "waveshare panel size = %d\n", data);
> +
> +	ret = regmap_read(regmap, REG_VERSION, &data);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to read register\n");
> +
> +	dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data);
> +
> +	ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to program GPIOs\n");
> +
> +	msleep(20);
> +
> +	state->gc.parent = dev;
> +	state->gc.label = i2c->name;
> +	state->gc.owner = THIS_MODULE;
> +	state->gc.base = -1;
> +	state->gc.ngpio = NUM_GPIO;
> +
> +	/* it is output only */
> +	state->gc.get = waveshare_gpio_gpio_get;
> +	state->gc.set = waveshare_gpio_gpio_set;
> +	state->gc.get_direction = waveshare_gpio_gpio_get_direction;
> +	state->gc.can_sleep = true;
> +
> +	ret = devm_gpiochip_add_data(dev, &state->gc, state);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to create gpiochip\n");
> +
> +	props.type = BACKLIGHT_RAW;
> +	props.max_brightness = 255;
> +	props.brightness = 255;
> +	bl = devm_backlight_device_register(dev, dev_name(dev), dev, state,
> +					    &waveshare_gpio_bl, &props);
> +	return PTR_ERR_OR_ZERO(bl);
> +}
> +
> +static const struct of_device_id waveshare_gpio_dt_ids[] = {
> +	{ .compatible = "waveshare,dsi-touch-gpio" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids);
> +
> +static struct i2c_driver waveshare_gpio_regulator_driver = {
> +	.driver = {
> +		.name = "waveshare-regulator",
> +		.of_match_table = of_match_ptr(waveshare_gpio_dt_ids),
> +	},
> +	.probe = waveshare_gpio_probe,
> +};
> +
> +module_i2c_driver(waveshare_gpio_regulator_driver);
> +
> +MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels");
> +MODULE_LICENSE("GPL");
> 


^ permalink raw reply

* Re: [PATCH v2 12/21] drm/panel: jadard-jd9365da-h3: support variable DSI configuration
From: Jie Gan @ 2026-04-12  0:55 UTC (permalink / raw)
  To: Dmitry Baryshkov, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Cong Yang, Ondrej Jirman, Javier Martinez Canillas, Jagan Teki,
	Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski
  Cc: dri-devel, devicetree, linux-kernel, linux-gpio, Riccardo Mereu
In-Reply-To: <20260411-waveshare-dsi-touch-v2-12-75cdbeac5156@oss.qualcomm.com>



On 4/11/2026 8:10 PM, Dmitry Baryshkov wrote:
> Several panels support attachment either using 4 DSI lanes or just 2. In
> some cases, this requires a different panel mode to fulfill clock
> requirements. Extend the driver to handle such cases by letting the
> panel description to omit lanes specification and parsing number of
> lanes from the DT.
> 
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Tested-by: Riccardo Mereu <r.mereu@arduino.cc>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
>   drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c | 32 +++++++++++++++++-------
>   1 file changed, 23 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
> index 1884ad2404cd..5d9db2e1f28f 100644
> --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
> +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
> @@ -10,6 +10,7 @@
>   
>   #include <drm/drm_mipi_dsi.h>
>   #include <drm/drm_modes.h>
> +#include <drm/drm_of.h>
>   #include <drm/drm_panel.h>
>   #include <drm/drm_print.h>
>   #include <drm/drm_probe_helper.h>
> @@ -23,7 +24,8 @@
>   struct jadard;
>   
>   struct jadard_panel_desc {
> -	const struct drm_display_mode mode;
> +	const struct drm_display_mode *mode_4ln;
> +	const struct drm_display_mode *mode_2ln;
>   	unsigned int lanes;
>   	enum mipi_dsi_pixel_format format;
>   	int (*init)(struct jadard *jadard);
> @@ -57,7 +59,10 @@ static void jadard_enable_standard_cmds(struct mipi_dsi_multi_context *dsi_ctx)
>   	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe1, 0x93);
>   	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe2, 0x65);
>   	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe3, 0xf8);
> -	mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x03);
> +	if (dsi_ctx->dsi->lanes == 2)
> +		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x01);
> +	else
> +		mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x03);
>   }
>   
>   static inline struct jadard *panel_to_jadard(struct drm_panel *panel)
> @@ -151,7 +156,10 @@ static int jadard_get_modes(struct drm_panel *panel,
>   {
>   	struct jadard *jadard = panel_to_jadard(panel);
>   
> -	return drm_connector_helper_get_modes_fixed(connector, &jadard->desc->mode);
> +	if (jadard->dsi->lanes == 2)

Worth to add a check here:
if (jadard->dsi->lanes == 2 && jadard->desc->mode_2ln)

Some pre-existing panel desc(e.g. radxa_display_8hd_ad002_desc) only set 
mode_4ln, leaving mode_2ln = NULL. So if such a panel attaches to a 
2-lane DSI host, the NULL pointer dereference causes a kernel oops.

Thanks,
Jie

> +		return drm_connector_helper_get_modes_fixed(connector, jadard->desc->mode_2ln);
> +	else
> +		return drm_connector_helper_get_modes_fixed(connector, jadard->desc->mode_4ln);
>   }
>   
>   static enum drm_panel_orientation jadard_panel_get_orientation(struct drm_panel *panel)
> @@ -354,7 +362,7 @@ static int radxa_display_8hd_ad002_init_cmds(struct jadard *jadard)
>   };
>   
>   static const struct jadard_panel_desc radxa_display_8hd_ad002_desc = {
> -	.mode = {
> +	.mode_4ln = &(const struct drm_display_mode) {
>   		.clock		= 70000,
>   
>   		.hdisplay	= 800,
> @@ -586,7 +594,7 @@ static int cz101b4001_init_cmds(struct jadard *jadard)
>   };
>   
>   static const struct jadard_panel_desc cz101b4001_desc = {
> -	.mode = {
> +	.mode_4ln = &(const struct drm_display_mode) {
>   		.clock		= 70000,
>   
>   		.hdisplay	= 800,
> @@ -819,7 +827,7 @@ static int kingdisplay_kd101ne3_init_cmds(struct jadard *jadard)
>   };
>   
>   static const struct jadard_panel_desc kingdisplay_kd101ne3_40ti_desc = {
> -	.mode = {
> +	.mode_4ln = &(const struct drm_display_mode) {
>   		.clock		= (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
>   
>   		.hdisplay	= 800,
> @@ -1070,7 +1078,7 @@ static int melfas_lmfbx101117480_init_cmds(struct jadard *jadard)
>   };
>   
>   static const struct jadard_panel_desc melfas_lmfbx101117480_desc = {
> -	.mode = {
> +	.mode_4ln = &(const struct drm_display_mode) {
>   		.clock		= (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
>   
>   		.hdisplay	= 800,
> @@ -1326,7 +1334,7 @@ static int anbernic_rgds_init_cmds(struct jadard *jadard)
>   };
>   
>   static const struct jadard_panel_desc anbernic_rgds_display_desc = {
> -	.mode = {
> +	.mode_4ln = &(const struct drm_display_mode) {
>   		.clock		= (640 + 260 + 220 + 260) * (480 + 10 + 2 + 16) * 60 / 1000,
>   
>   		.hdisplay	= 640,
> @@ -1562,7 +1570,7 @@ static int taiguan_xti05101_01a_init_cmds(struct jadard *jadard)
>   };
>   
>   static const struct jadard_panel_desc taiguan_xti05101_01a_desc = {
> -	.mode = {
> +	.mode_4ln = &(const struct drm_display_mode) {
>   		.clock		= (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
>   
>   		.hdisplay	= 800,
> @@ -1614,6 +1622,12 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
>   
>   	dsi->format = desc->format;
>   	dsi->lanes = desc->lanes;
> +	if (!dsi->lanes) {
> +		dsi->lanes = drm_of_get_data_lanes_count_remote(dsi->dev.of_node, 0, -1, 2, 4);
> +		if (dsi->lanes < 0)
> +			return dsi->lanes;
> +	}
> +	dev_dbg(&dsi->dev, "lanes: %d\n", dsi->lanes);
>   
>   	jadard->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
>   	if (IS_ERR(jadard->reset))
> 


^ permalink raw reply

* Re: [PATCH v2 05/21] dt-bindings: dipslay/panel: describe panels using Focaltech OTA7290B
From: Jie Gan @ 2026-04-12  0:44 UTC (permalink / raw)
  To: Dmitry Baryshkov, Neil Armstrong, Jessica Zhang, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Cong Yang, Ondrej Jirman, Javier Martinez Canillas, Jagan Teki,
	Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski
  Cc: dri-devel, devicetree, linux-kernel, linux-gpio
In-Reply-To: <20260411-waveshare-dsi-touch-v2-5-75cdbeac5156@oss.qualcomm.com>



On 4/11/2026 8:10 PM, Dmitry Baryshkov wrote:
> Add schema for the panels using Focaltech OTA7290B controller. For now
> there is only one such panel, from the Waveshare 8.8 DSI TOUCH-A kit.
> 
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
>   .../bindings/display/panel/focaltech,ota7290b.yaml | 70 ++++++++++++++++++++++
>   1 file changed, 70 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml b/Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml
> new file mode 100644
> index 000000000000..f2f6e254b7de
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml
> @@ -0,0 +1,70 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/panel/focaltech,ota7290b.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Focaltech OTA7290B DSI panels
> +
> +maintainers:
> +  - Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> +
> +allOf:
> +  - $ref: panel-common.yaml#
> +
> +properties:
> +  compatible:
> +    const: waveshare,8.88-dsi-touch-a

mismatch with the compatible in the example.

> +
> +  reg:
> +    maxItems: 1
> +
> +  vdd-supply:
> +    description: supply regulator for VDD, usually 3.3V
> +
> +  vdda-supply:
> +    description: supply regulator for VDDA, 7-10V
> +
> +  vcc-supply:
> +    description: supply regulator for VCCIO, usually 1.5V
> +
> +  reset-gpios: true
> +  backlight: true
> +  rotation: true
> +  port: true
> +
> +required:
> +  - compatible
> +  - reg
> +  - vdd-supply
> +  - vccio-supply

defined as vcc-supply in property block.

Thanks,
Jie

> +  - reset-gpios
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    dsi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        panel@0 {
> +            compatible = "waveshare,8.8-dsi-touch-a";
> +            reg = <0>;
> +            vdd-supply = <&vdd>;
> +            vccio-supply = <&vccio>;
> +            reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
> +            backlight = <&backlight>;
> +
> +            port {
> +                endpoint {
> +                    remote-endpoint = <&mipi_out_panel>;
> +                };
> +            };
> +        };
> +    };
> +
> +...
> +
> 


^ permalink raw reply

* Re: [PATCH 3/4] arm64: dts: qcom: sdm845: Add missing MDSS reset
From: Dmitry Baryshkov @ 2026-04-12  0:41 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: David Heidelberg, Bjorn Andersson, Michael Turquette,
	Stephen Boyd, Ulf Hansson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-arm-msm, linux-clk,
	linux-kernel, devicetree
In-Reply-To: <5b45a40d-54a6-40b5-b4b6-eac344827dc9@oss.qualcomm.com>

On Fri, Apr 10, 2026 at 10:55:53AM +0200, Konrad Dybcio wrote:
> On 4/9/26 11:24 PM, Dmitry Baryshkov wrote:
> > On Thu, Apr 09, 2026 at 10:38:15PM +0200, David Heidelberg wrote:
> >> On 18/02/2026 16:59, Dmitry Baryshkov wrote:
> >>> On Wed, Feb 18, 2026 at 03:28:01PM +0100, Konrad Dybcio wrote:
> >>>>
> >>>>
> >>>> On 18-Feb-26 12:58, Dmitry Baryshkov wrote:
> >>>>> On Wed, Feb 18, 2026 at 12:24:26PM +0100, Konrad Dybcio wrote:
> >>>>>> On 2/18/26 12:18 PM, David Heidelberg wrote:
> >>>>>>> On 18/02/2026 11:30, Konrad Dybcio wrote:
> >>>>>>>> On 2/17/26 10:20 PM, Dmitry Baryshkov wrote:
> >>>>>>>>> From: David Heidelberg <david@ixit.cz>
> >>>>>>>>>
> >>>>>>>>> If the OS does not support recovering the state left by the
> >>>>>>>>> bootloader it needs a way to reset display hardware, so that it can
> >>>>>>>>> start from a clean state. Add a reference to the relevant reset.
> >>>>>>>>
> >>>>>>>> This is not the relevant reset
> >>>>>>>>
> >>>>>>>> You want MDSS_CORE_BCR @ 0xaf0_2000
> >>>>>>>
> >>>>>>> Thanks, I prepared the fixes [1].
> >>>>>>>
> >>>>>>> I'll try to test it if it's not breaking anything for us and send as v2 of [2].
> >>>>>>>
> >>>>>>> David
> >>>>>>>
> >>>>>>> [1] https://codeberg.org/sdm845/linux/commits/branch/b4/mdss-reset
> >>>>>>> [2] https://patchwork.kernel.org/project/linux-arm-msm/patch/20260112-mdss-reset-v1-1-af7c572204d3@ixit.cz/
> >>>>>>
> >>>>>> Please don't alter the contents of dt-bindings, it really doesn't matter
> >>>>>> if on sdm845 it's reset0 or reset1, that's why we define them in the first
> >>>>>> place
> >>>>>
> >>>>> I dpn't think that will pass. Current reset is defined as RSCC, we can't
> >>>>> change that to CORE behind the scene. I'd prefer David's approach.
> >>>>
> >>>> Back when I replied, David had a patch that removed the current RSCC
> >>>> reset definition in dt-bindings (at index 0) and re-used that index
> >>>> for CORE, putting RSCC at index 1. Perhaps it's better to link to
> >>>> specific commits when making comments, note to self :P
> >>>
> >>> Yes, I saw the commit having two resets. Anyway, as we saw, it doesn't
> >>> work.
> >>
> >> So, finally I spent "so much effort" (read throwing it at LLM) looking at:
> >>
> >> arm-smmu 15000000.iommu: Unhandled context fault: fsr=0x402,
> >> iova=0x9d4bb500, fsynr=0x170021, cbfrsynra=0xc88, cb=11
> >> arm-smmu 15000000.iommu: FSR    = 00000402 [Format=2 TF], SID=0xc88
> >> arm-smmu 15000000.iommu: FSYNR0 = 00170021 [S1CBNDX=23 PNU PLVL=1]
> > 
> > [...]
> > 
> >>
> >> These (or very similar warnings) are around sdm845 definitely 6.19+ /
> >> linux-next kernels for some time, but pretty harmless.
> >>
> >> LLM suggested multiple fixes, but when presenting possibility of
> >> implementing mdss reset it found it as most preferable [1].
> >>
> >> Adding MDSS reset would most likely solve it. It's not critical, but not
> >> nice to see many red lines in the dmesg.
> >>
> >> Is there something I could experiment with to get closer to have proper MDSS reset?
> > 
> > I don't have a sensible solution at this point. We tried using the MDSS
> > reset on several SDM845 devices, but they just reset. So... I don't have
> > any possible solution.
> 
> The older context talks about altering the existing dt-bindings values
> and now we're at hardware (mis)behaving? What is the issue here?

The HDK and DB845c reset if I try touching MDSS core reset.

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH v6 6/6] clk: fsl-sai: Add MCLK generation support
From: Stephen Boyd @ 2026-04-12  0:37 UTC (permalink / raw)
  To: Marek Vasut, linux-clk
  Cc: Marek Vasut, Michael Walle, Brian Masney, Conor Dooley,
	Krzysztof Kozlowski, Michael Turquette, Michael Walle,
	Rob Herring, devicetree, linux-kernel
In-Reply-To: <20260409002952.319668-6-marex@nabladev.com>

Quoting Marek Vasut (2026-04-08 17:29:06)
> The driver currently supports generating BCLK. There are systems which
> require generation of MCLK instead. Register new MCLK clock and handle
> clock-cells = <1> to differentiate between BCLK and MCLK. In case of a
> legacy system with clock-cells = <0>, the driver behaves as before, i.e.
> always returns BCLK.
> 
> Note that it is not possible re-use the current SAI audio driver to
> generate MCLK and correctly enable and disable the MCLK.
> 
> If SAI (audio driver) is used to control the MCLK enablement, then MCLK
> clock is not always enabled, and it is not necessarily enabled when the
> codec may need the clock to be enabled. There is also no way for the
> codec node to specify phandle to clock provider in DT, because the SAI
> (audio driver) is not clock provider.
> 
> If SAI (clock driver) is used to control the MCLK enablement, then MCLK
> clock is enabled when the codec needs the clock enabled, because the
> codec is the clock consumer and the SAI (clock driver) is the clock
> provider, and the codec driver can request the clock to be enabled when
> needed. There is also the usual phandle to clock provider in DT, because
> the SAI (clock driver) is clock provider.
> 
> Acked-by: Michael Walle <mwalle@kernel.org>
> Signed-off-by: Marek Vasut <marex@nabladev.com>
> ---

Applied to clk-next

^ permalink raw reply

* Re: [PATCH v6 5/6] clk: fsl-sai: Extract clock setup into fsl_sai_clk_register()
From: Stephen Boyd @ 2026-04-12  0:37 UTC (permalink / raw)
  To: Marek Vasut, linux-clk
  Cc: Marek Vasut, Brian Masney, Conor Dooley, Krzysztof Kozlowski,
	Michael Turquette, Michael Walle, Rob Herring, devicetree,
	linux-kernel
In-Reply-To: <20260409002952.319668-5-marex@nabladev.com>

Quoting Marek Vasut (2026-04-08 17:29:05)
> Create helper function fsl_sai_clk_register() to set up and register
> SAI clock. Rename BCLK specific struct fsl_sai_clk members with bclk_
> prefix. Use of_node_full_name(dev->of_node) and clock name to register
> uniquely named clock. This is done in preparation for the follow up
> patch, which adds MCLK support.
> 
> Signed-off-by: Marek Vasut <marex@nabladev.com>
> ---

Applied to clk-next

^ permalink raw reply

* Re: [PATCH v6 3/6] clk: fsl-sai: Add i.MX8M support with 8 byte register offset
From: Stephen Boyd @ 2026-04-12  0:37 UTC (permalink / raw)
  To: Marek Vasut, linux-clk
  Cc: Marek Vasut, Brian Masney, Peng Fan, Conor Dooley,
	Krzysztof Kozlowski, Michael Turquette, Michael Walle,
	Rob Herring, devicetree, linux-kernel
In-Reply-To: <20260409002952.319668-3-marex@nabladev.com>

Quoting Marek Vasut (2026-04-08 17:29:03)
> The i.MX8M/Mini/Nano/Plus variant of the SAI IP has control registers
> shifted by +8 bytes and requires additional bus clock. Add support for
> the i.MX8M variant of the IP with this register shift and additional
> clock.
> 
> Reviewed-by: Brian Masney <bmasney@redhat.com>
> Reviewed-by: Peng Fan <peng.fan@nxp.com>
> Signed-off-by: Marek Vasut <marex@nabladev.com>
> ---

Applied to clk-next

^ permalink raw reply

* Re: [PATCH v6 4/6] dt-bindings: clock: fsl-sai: Document clock-cells = <1> support
From: Stephen Boyd @ 2026-04-12  0:37 UTC (permalink / raw)
  To: Marek Vasut, linux-clk
  Cc: Marek Vasut, Conor Dooley, Brian Masney, Conor Dooley,
	Krzysztof Kozlowski, Michael Turquette, Michael Walle,
	Rob Herring, devicetree, linux-kernel
In-Reply-To: <20260409002952.319668-4-marex@nabladev.com>

Quoting Marek Vasut (2026-04-08 17:29:04)
> The driver now supports generation of both BCLK and MCLK, document
> support for #clock-cells = <0> for legacy case and #clock-cells = <1>
> for the new case which can differentiate between BCLK and MCLK.
> 
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Marek Vasut <marex@nabladev.com>
> ---

Applied to clk-next

^ permalink raw reply

* Re: [PATCH v6 2/6] clk: fsl-sai: Sort the headers
From: Stephen Boyd @ 2026-04-12  0:37 UTC (permalink / raw)
  To: Marek Vasut, linux-clk
  Cc: Marek Vasut, Brian Masney, Conor Dooley, Krzysztof Kozlowski,
	Michael Turquette, Michael Walle, Rob Herring, devicetree,
	linux-kernel
In-Reply-To: <20260409002952.319668-2-marex@nabladev.com>

Quoting Marek Vasut (2026-04-08 17:29:02)
> Sort the headers. No functional change.
> 
> Reviewed-by: Brian Masney <bmasney@redhat.com>
> Signed-off-by: Marek Vasut <marex@nabladev.com>
> ---

Applied to clk-next

^ permalink raw reply

* Re: [PATCH v2 5/5] arm64: dts: qcom: sdm845-xiaomi-beryllium-tianma: Disable MDSS
From: Dmitry Baryshkov @ 2026-04-12  0:36 UTC (permalink / raw)
  To: David Heidelberg
  Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sam Day, linux-arm-msm, devicetree, linux-kernel,
	phone-devel, Petr Hodina
In-Reply-To: <c746d310-5925-4656-9487-3521cb87793a@ixit.cz>

On Fri, Apr 10, 2026 at 03:51:12PM +0200, David Heidelberg wrote:
> On 18/03/2026 15:47, Dmitry Baryshkov wrote:
> > On Tue, Mar 17, 2026 at 11:12:35PM +0100, David Heidelberg via B4 Relay wrote:
> > > From: Petr Hodina <petr.hodina@protonmail.com>
> > > 
> > > Currently the panel driver for tianma is broken.
> > > Disable MDSS to prevent DRM taking over the framebuffer.
> > 
> > I'd still hope that somebody can get it to work. I'd start by using
> > prepare_prev_first. Sumit reported the panel as working in 2020 and I
> > have no doubts that it was the case. Another option migt be something
> > related to the dispcc (see my [1]).
> 
> Sadly, the prepare_prev_first didn't solve the issue. The framebuffer ->
> MDSS takover happens, but at greetd (login) loading screen goes black and
> never restores.
> 
> Will need more work.

Do you see a console _while_ it boots, after the handover? I'd check the
mode programming. Maybe, disabling panel reset would also (temporarily)
help.

> 
> David>
> > [1] https://lore.kernel.org/linux-arm-msm/20260217-sdm845-hdk-v1-1-866f1965fef7@oss.qualcomm.com/

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH v6 1/6] dt-bindings: clock: fsl-sai: Document i.MX8M support
From: Stephen Boyd @ 2026-04-12  0:36 UTC (permalink / raw)
  To: Marek Vasut, linux-clk
  Cc: Marek Vasut, Conor Dooley, Brian Masney, Conor Dooley,
	Krzysztof Kozlowski, Michael Turquette, Michael Walle,
	Rob Herring, devicetree, linux-kernel
In-Reply-To: <20260409002952.319668-1-marex@nabladev.com>

Quoting Marek Vasut (2026-04-08 17:29:01)
> The i.MX8M/Mini/Nano/Plus variant of the SAI IP has control registers
> shifted by +8 bytes and requires additional bus clock. Document support
> for the i.MX8M variant of the IP with this register shift and additional
> clock. Update the description slightly.
> 
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Marek Vasut <marex@nabladev.com>
> ---

Applied to clk-next

^ permalink raw reply

* Re: [PATCH v5 0/5] Add support for AAEON SRG-IMX8P MCU
From: Guenter Roeck @ 2026-04-12  0:12 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,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski
In-Reply-To: <20260408-dev-b4-aaeon-mcu-driver-v5-0-ad98bd481668@bootlin.com>

On 4/8/26 10:21, Thomas Perrot (Schneider Electric) wrote:
> This patch series introduces support for the AAEON SRG-IMX8P embedded
> controller (MCU). The MCU is connected via I2C and provides GPIO and
> watchdog functionality for the SRG-IMX8P board.
> 
> The series includes:
> - Device tree binding for the MFD driver
> - MFD driver that serves as the core driver for the MCU
> - GPIO driver implementing the GPIO functionality
> - Watchdog driver for system monitoring
> - MAINTAINERS entry for the new drivers
> 
> The drivers follow the standard Linux kernel subsystem patterns, with
> the MFD driver registering the sub-devices (GPIO and watchdog) which
> are then handled by their respective subsystem drivers.
> 
> Signed-off-by: Thomas Perrot (Schneider Electric) <thomas.perrot@bootlin.com>

Sashiko has some interesting feedback that might be worth looking into.

https://sashiko.dev/#/patchset/20260408-dev-b4-aaeon-mcu-driver-v5-0-ad98bd481668%40bootlin.com

Guenter


^ permalink raw reply

* Re: [GIT PULL] clk: tenstorrent: Add Atlantis PRCM for v7.1
From: Stephen Boyd @ 2026-04-11 23:00 UTC (permalink / raw)
  To: Drew Fustini
  Cc: Brian Masney, Philipp Zabel, Anirudh Srinivasan, Drew Fustini,
	linux-riscv, devicetree, linux-kernel, linux-clk
In-Reply-To: <abghFg5INVslFFcc@x1>

Quoting Drew Fustini (2026-03-16 08:26:14)
> Hi Stephen,
> 
> This pull request introduces support for the Tenstorrent Atlantis RISC-V
> SoC in the form of a clk driver, reset driver and the associated DT
> binding. The reset driver was reviewed by Philipp, who agreed that it
> could go via the clk tree. Brian reviewed the clk driver but there is no
> review/ack from you. Please let me know if you'd like to see any changes
> and we will post a new revision.
> 
> The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
> 
>   Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
> 
> are available in the Git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/tenstorrent/linux.git tags/tenstorrent-clk-for-v7.1
> 
> for you to fetch changes up to 23c8ebc952849b3ba47d04d0ec95daf5cc136061:
> 
>   clk: tenstorrent: Add Atlantis clock controller driver (2026-03-09 20:10:26 -0700)
> 
> ----------------------------------------------------------------

Thanks. Pulled into clk-next

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: mmc: dw-mshc-common: add option for configuring DMA threshold
From: Krzysztof Kozlowski @ 2026-04-11 20:27 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jaehoon Chung, Shawn Lin
  Cc: linux-mmc, devicetree, linux-kernel
In-Reply-To: <20260412-dwmmc-dma-thr-v1-1-75a2f658eee3@disroot.org>

On 11/04/2026 21:43, Kaustabh Chakraborty wrote:
> Some controllers, such as certain Exynos SDIO ones, are unable to
> perform DMA transfers of small amount of bytes properly. Add a property
> to configure the DMA transfer threshold.
> 
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> ---
>  .../devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml          | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml
> index 6f11b2adf1036..1693277957a91 100644
> --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml
> +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml
> @@ -57,6 +57,14 @@ properties:
>        force fifo watermark setting accordingly.
>      $ref: /schemas/types.yaml#/definitions/flag
>  
> +  dma-threshold-bytes:
> +    description:
> +      DMA transfer takes place only if the transfer size is equal to or greater
> +      than the number of bytes defined by the threshold value. Some controllers are
> +      unable to perform the DMA transfer properly with small amounts of data.

Looks like controller specific, thus should be deducible from the
compatible.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 2/2] mmc: dw_mmc: implement option for configuring DMA threshold
From: Kaustabh Chakraborty @ 2026-04-11 19:43 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin
  Cc: linux-mmc, devicetree, linux-kernel, Kaustabh Chakraborty
In-Reply-To: <20260412-dwmmc-dma-thr-v1-0-75a2f658eee3@disroot.org>

Some controllers, such as certain Exynos SDIO ones, are unable to
perform DMA transfers of small amount of bytes properly. Following the
device tree schema, implement the property to define the DMA transfer
threshold (from a hard coded value of 16 bytes) so that lesser number of
bytes can be transferred safely skipping DMA in such controllers. The
value of 16 bytes stays as the default for controllers which do not
define it.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/mmc/host/dw_mmc.c | 6 ++++--
 drivers/mmc/host/dw_mmc.h | 1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 20193ee7b73eb..0c0d269b5e033 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -40,7 +40,6 @@
 				 SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
 #define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
 				 DW_MCI_CMD_ERROR_FLAGS)
-#define DW_MCI_DMA_THRESHOLD	16
 
 #define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */
 #define DW_MCI_FREQ_MIN	100000		/* unit: HZ */
@@ -821,7 +820,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
 	 * non-word-aligned buffers or lengths. Also, we don't bother
 	 * with all the DMA setup overhead for short transfers.
 	 */
-	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+	if (data->blocks * data->blksz < host->dma_threshold)
 		return -EINVAL;
 
 	if (data->blksz & 3)
@@ -3137,6 +3136,9 @@ static int dw_mci_parse_dt(struct dw_mci *host)
 	if (!host->data_addr_override)
 		device_property_read_u32(dev, "data-addr", &host->data_addr_override);
 
+	if (device_property_read_u32(dev, "dma-threshold-bytes", &host->dma_threshold) < 0)
+		host->dma_threshold = 16;
+
 	if (device_property_present(dev, "fifo-watermark-aligned"))
 		host->wm_aligned = true;
 
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 42e58be74ce09..5cdd342d01b68 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -163,6 +163,7 @@ struct dw_mci {
 	void __iomem		*regs;
 	void __iomem		*fifo_reg;
 	u32			data_addr_override;
+	u32			dma_threshold;
 	bool			wm_aligned;
 
 	struct scatterlist	*sg;

-- 
2.53.0


^ permalink raw reply related

* [PATCH 1/2] dt-bindings: mmc: dw-mshc-common: add option for configuring DMA threshold
From: Kaustabh Chakraborty @ 2026-04-11 19:43 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin
  Cc: linux-mmc, devicetree, linux-kernel, Kaustabh Chakraborty
In-Reply-To: <20260412-dwmmc-dma-thr-v1-0-75a2f658eee3@disroot.org>

Some controllers, such as certain Exynos SDIO ones, are unable to
perform DMA transfers of small amount of bytes properly. Add a property
to configure the DMA transfer threshold.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 .../devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml          | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml
index 6f11b2adf1036..1693277957a91 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml
@@ -57,6 +57,14 @@ properties:
       force fifo watermark setting accordingly.
     $ref: /schemas/types.yaml#/definitions/flag
 
+  dma-threshold-bytes:
+    description:
+      DMA transfer takes place only if the transfer size is equal to or greater
+      than the number of bytes defined by the threshold value. Some controllers are
+      unable to perform the DMA transfer properly with small amounts of data.
+      This option allows to configure the threshold up to a comfortable value.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
   dmas:
     maxItems: 1
 

-- 
2.53.0


^ permalink raw reply related

* [PATCH 0/2] Configuring DMA threshold value for DW-MMC controllers
From: Kaustabh Chakraborty @ 2026-04-11 19:43 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin
  Cc: linux-mmc, devicetree, linux-kernel, Kaustabh Chakraborty

In Samsung Exynos 7870 devices with Broadcom Wi-Fi, it has been observed
that small sized DMA transfers are unreliable and are not written
properly, which renders the cache incoherent.

Experimental observations say that DMA transfer sizes of somewhere
around 64 to 512 are intolerable. We must thus implement a mechanism to
fall back to PIO transfer in this case. One such approach, which this
series implements is allowing the DMA transfer threshold, which is
already defined in the driver, to be configurable.

Note that this patch is likely to be labelled as a workaround. These
smaller transfers seem to be successful from downstream kernels,
however efforts to figure out how so went in vain. It is also very
possible that the downstream Broadcom Wi-Fi SDIO driver uses PIO
transfers as well.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
Kaustabh Chakraborty (2):
      dt-bindings: mmc: dw-mshc-common: add option for configuring DMA threshold
      mmc: dw_mmc: implement option for configuring DMA threshold

 .../devicetree/bindings/mmc/synopsys-dw-mshc-common.yaml          | 8 ++++++++
 drivers/mmc/host/dw_mmc.c                                         | 6 ++++--
 drivers/mmc/host/dw_mmc.h                                         | 1 +
 3 files changed, 13 insertions(+), 2 deletions(-)
---
base-commit: 66672af7a095d89f082c5327f3b15bc2f93d558e
change-id: 20260412-dwmmc-dma-thr-1090d8285ea7

Best regards,
-- 
Kaustabh Chakraborty <kauschluss@disroot.org>


^ permalink raw reply

* Re: [PATCH v3 2/5] dt-bindings: iio: adc: ad4130: Add new supported parts
From: David Lechner @ 2026-04-11 19:08 UTC (permalink / raw)
  To: Jonathan Santos, linux-iio, linux-kernel, devicetree
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, andy, robh, krzk+dt,
	conor+dt, Krzysztof Kozlowski
In-Reply-To: <7bf387e8ad20e2f2bb14534fb03608bbadfa47d2.1774996100.git.Jonathan.Santos@analog.com>

On 4/1/26 6:58 AM, Jonathan Santos wrote:
> Extend driver support for AD4129-4/8, AD4130-4, and AD4131-4/8 ADC
> variants.
> 
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> Changes in v3:
> * None.
> 
> Changes in v2:
> * None.
> ---
>  .../devicetree/bindings/iio/adc/adi,ad4130.yaml   | 15 +++++++++++++--
>  1 file changed, 13 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
> index fcc00e5cfd54..f4cad68fa04d 100644
> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
> @@ -5,19 +5,30 @@
>  $id: http://devicetree.org/schemas/iio/adc/adi,ad4130.yaml#
>  $schema: http://devicetree.org/meta-schemas/core.yaml#
>  
> -title: Analog Devices AD4130 ADC device driver
> +title: Analog Devices AD4130 family ADC device driver

I'm surprised that on one said anything about the word "driver"
in a devicetree binding. :-)


^ permalink raw reply

* [PATCH v2] dt-bindings: ARM: arm,vexpress-scc: convert to DT schema
From: Khushal Chitturi @ 2026-04-11 18:33 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, liviu.dudau, sudeep.holla, lpieralisi
  Cc: pawel.moll, devicetree, linux-arm-kernel, linux-kernel,
	Khushal Chitturi

Convert the ARM Versatile Express Serial Configuration Controller
bindings to DT schema.

Signed-off-by: Khushal Chitturi <khushalchitturi@gmail.com>
---
Changelog:
v1 -> v2:
- Modified compatible string to use an enum instead of a generic pattern.
- Updated maintainers list.

 .../bindings/arm/arm,vexpress-scc.yaml        | 53 +++++++++++++++++++
 .../devicetree/bindings/arm/vexpress-scc.txt  | 33 ------------
 2 files changed, 53 insertions(+), 33 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml
 delete mode 100644 Documentation/devicetree/bindings/arm/vexpress-scc.txt

diff --git a/Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml b/Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml
new file mode 100644
index 000000000000..9b8f7e0c4ea0
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/arm,vexpress-scc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Versatile Express Serial Configuration Controller
+
+maintainers:
+  - Liviu Dudau <liviu.dudau@arm.com>
+  - Sudeep Holla <sudeep.holla@arm.com>
+
+description: |
+  Test chips for ARM Versatile Express platform implement SCC (Serial
+  Configuration Controller) interface, used to set initial conditions
+  for the test chip.
+
+  In some cases its registers are also mapped in normal address space
+  and can be used to obtain runtime information about the chip internals
+  (like silicon temperature sensors) and as interface to other subsystems
+  like platform configuration control and power management.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - arm,vexpress-scc,v2p-ca15_a7
+      - const: arm,vexpress-scc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    bus {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        scc@7fff0000 {
+            compatible = "arm,vexpress-scc,v2p-ca15_a7", "arm,vexpress-scc";
+            reg = <0 0x7fff0000 0 0x1000>;
+            interrupts = <0 95 4>;
+        };
+    };
+...
diff --git a/Documentation/devicetree/bindings/arm/vexpress-scc.txt b/Documentation/devicetree/bindings/arm/vexpress-scc.txt
deleted file mode 100644
index ae5043e42e5d..000000000000
--- a/Documentation/devicetree/bindings/arm/vexpress-scc.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-ARM Versatile Express Serial Configuration Controller
------------------------------------------------------
-
-Test chips for ARM Versatile Express platform implement SCC (Serial
-Configuration Controller) interface, used to set initial conditions
-for the test chip.
-
-In some cases its registers are also mapped in normal address space
-and can be used to obtain runtime information about the chip internals
-(like silicon temperature sensors) and as interface to other subsystems
-like platform configuration control and power management.
-
-Required properties:
-
-- compatible value: "arm,vexpress-scc,<model>", "arm,vexpress-scc";
-		    where <model> is the full tile model name (as used
-		    in the tile's Technical Reference Manual),
-		    eg. for Coretile Express A15x2 A7x3 (V2P-CA15_A7):
-	compatible = "arm,vexpress-scc,v2p-ca15_a7", "arm,vexpress-scc";
-
-Optional properties:
-
-- reg: when the SCC is memory mapped, physical address and size of the
-       registers window
-- interrupts: when the SCC can generate a system-level interrupt
-
-Example:
-
-	scc@7fff0000 {
-		compatible = "arm,vexpress-scc,v2p-ca15_a7", "arm,vexpress-scc";
-		reg = <0 0x7fff0000 0 0x1000>;
-		interrupts = <0 95 4>;
-	};
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v6 3/3] arm64: dts: rockchip: Add Orange Pi 5 Pro board support
From: Alexey Charkov @ 2026-04-11 18:00 UTC (permalink / raw)
  To: dennis
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
	FUKAUMI Naoki, Hsun Lai, Jonas Karlman, Chaoyi Chen, John Clark,
	Michael Opdenacker, Quentin Schulz, Andrew Lunn, Chukun Pan,
	Peter Robinson, Michael Riesch, Mykola Kvach, Jimmy Hon,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <20260411024743.195385-4-dennis@ausil.us>

On Sat, Apr 11, 2026 at 6:47 AM <dennis@ausil.us> wrote:
>
> From: Dennis Gilmore <dennis@ausil.us>
>
> Add device tree for the Xunlong Orange Pi 5 Pro (RK3588S).
>
> - eMMC module, you can optionally solder a SPI NOR in place and turn
>  off the eMMC
> - PCIe-attached NIC (pcie2x1l1)
> - PCIe NVMe slot (pcie2x1l2)
> - AP6256 WiFi (BCM43456) via SDIO with mmc-pwrseq
> - BCM4345C5 Bluetooth
> - es8388 audio
> - USB 2.0 and USB 3.0
> - Two HDMI ports, the second is connected to the SoC's DP controller
>   driven by a transparent LT8711UXD bridge that has firmware onboard and
>   needs no node defined.
>
> Vendors description and links to schematics available:
> http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-5-Pro.html

Hi Dennis,

The most useful of these is the schematic, so it's best to include a
direct link to that in a dedicated Link: tag

Link: https://drive.google.com/file/d/1qs1DratHuh7C6J6MEtQIwUsiSrg8qgTi/view
[schematic]

> Signed-off-by: Dennis Gilmore <dennis@ausil.us>
> ---
>  .../display/rockchip/rockchip,dw-dp.yaml      |   7 +
>  arch/arm64/boot/dts/rockchip/Makefile         |   1 +
>  .../dts/rockchip/rk3588s-orangepi-5-pro.dts   | 352 ++++++++++++++++++
>  drivers/gpu/drm/bridge/synopsys/dw-dp.c       |  12 +

These should be three separate patches, never lumped together. First
the binding change, next the driver change. They go together via the
subsystem tree (likely DRM in this case). Then the DTS addition (or
change) separately (it goes via the SoC tree).

>  4 files changed, 372 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5-pro.dts
>
> diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
> index 6345f0132d43..079a912d97f1 100644
> --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
> +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
> @@ -57,6 +57,13 @@ properties:
>        - const: i2s
>        - const: spdif
>
> +  hpd-gpios:
> +    maxItems: 1
> +    description:
> +      GPIO used for hot plug detection when the controller's native HPD
> +      input is not connected. If not specified, the controller uses its
> +      internal HPD detection mechanism.

Do you actually need this change? According to the schematic, the
DP_HPDIN line from the DP-HDMI bridge is routed to the native
DP0_HPDIN_M0 pin of the DP controller, so it shouldn't require this
GPIO trick if the pinctrl is configured properly.

>    phys:
>      maxItems: 1
>
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
> index 4d384f153c13..c99dca2ae9e7 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> @@ -214,6 +214,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-nanopi-r6c.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-odroid-m2.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-orangepi-5.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-orangepi-5b.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-orangepi-5-pro.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-orangepi-cm5-base.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-radxa-cm5-io.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-roc-pc.dtb
> diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5-pro.dts b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5-pro.dts
> new file mode 100644
> index 000000000000..84c83aa69f63
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5-pro.dts
> @@ -0,0 +1,352 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +
> +/dts-v1/;
> +
> +#include "rk3588s-orangepi-5.dtsi"
> +
> +/ {
> +       model = "Xunlong Orange Pi 5 Pro";
> +       compatible = "xunlong,orangepi-5-pro", "rockchip,rk3588s";
> +
> +       aliases {
> +               mmc0 = &sdhci;
> +               mmc1 = &sdmmc;
> +               mmc2 = &sdio;
> +       };
> +
> +       dp-con {
> +               compatible = "dp-connector";

You don't have a physical DP connector on the board, so this node
doesn't describe actual hardware, and is thus a no-go. What you have
instead is an HDMI type A connector routed via an onboard DP to HDMI
bridge, so you should describe exactly that in the device tree (a node
for the HDMI connector, a node for the bridge, a node for the DP
controller, and endpoints connected from the controller to the bridge,
from the bridge to the connector). Please refer to the device tree for
Radxa Rock 5 ITX, which has a similar setup (but a different bridge
IC).

I don't think your LT8711UXD has existing binding or driver entry, so
a one-line patch will likely be needed to
Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml,
and a separate one to drivers/gpu/drm/bridge/simple-bridge.c. Separate
ones :)

> +               port {
> +                       dp_con_in: endpoint {
> +                               remote-endpoint = <&dp0_out_con>;
> +                       };
> +               };
> +       };
> +
> +       analog-sound {
> +               compatible = "simple-audio-card";
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&hp_detect>;
> +               simple-audio-card,bitclock-master = <&masterdai>;
> +               simple-audio-card,format = "i2s";
> +               simple-audio-card,frame-master = <&masterdai>;
> +               simple-audio-card,hp-det-gpios = <&gpio1 RK_PD5 GPIO_ACTIVE_HIGH>;
> +               simple-audio-card,mclk-fs = <256>;
> +               simple-audio-card,name = "rockchip,es8388";
> +               simple-audio-card,routing =
> +                       "Headphones", "LOUT1",
> +                       "Headphones", "ROUT1",
> +                       "LINPUT1", "Microphone Jack",
> +                       "RINPUT1", "Microphone Jack",
> +                       "LINPUT2", "Onboard Microphone",
> +                       "RINPUT2", "Onboard Microphone";
> +               simple-audio-card,widgets =
> +                       "Microphone", "Microphone Jack",
> +                       "Microphone", "Onboard Microphone",
> +                       "Headphone", "Headphones";
> +
> +               simple-audio-card,cpu {
> +                       sound-dai = <&i2s2_2ch>;
> +               };
> +
> +               masterdai: simple-audio-card,codec {
> +                       sound-dai = <&es8388>;
> +                       system-clock-frequency = <12288000>;
> +               };
> +       };
> +
> +       pwm-leds {
> +               compatible = "pwm-leds";
> +
> +               led-0 {
> +                       color = <LED_COLOR_ID_BLUE>;
> +                       function = LED_FUNCTION_STATUS;
> +                       linux,default-trigger = "heartbeat";
> +                       max-brightness = <255>;
> +                       pwms = <&pwm15 0 1000000 0>;
> +               };
> +
> +               led-1 {
> +                       color = <LED_COLOR_ID_GREEN>;
> +                       function = LED_FUNCTION_ACTIVITY;
> +                       linux,default-trigger = "heartbeat";
> +                       max-brightness = <255>;
> +                       pwms = <&pwm3 0 1000000 0>;
> +               };
> +       };
> +
> +       fan: pwm-fan {
> +               compatible = "pwm-fan";
> +               #cooling-cells = <2>;
> +               cooling-levels = <0 50 100 150 200 255>;
> +               fan-supply = <&vcc5v0_sys>;
> +               pwms = <&pwm2 0 20000000 0>;
> +       };
> +
> +       vcc3v3_dp: regulator-vcc3v3-dp {
> +               compatible = "regulator-fixed";
> +               enable-active-high;
> +               gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_HIGH>;

Please don't forget to add explicit pinctrl nodes for each GPIO pin
you use (here and in other places like this). These GPIOs happen to
work on Linux without configuring their pin control first, but that is
pure luck and coincidence due to how the respective Linux subsystems
are wired together, and if you ever need to use this device tree in
e.g. U-boot (which also derives its DTS from the Linux kernel tree) it
will break there.

> +               regulator-always-on;
> +               regulator-boot-on;

Does it have to be always-on, boot-on? This looks like a hack to work
around the fact that you didn't define the bridge node, which uses
this as its supply. Please model the dependencies explicitly - most
likely that will let you drop these attributes.

> +               regulator-max-microvolt = <3300000>;
> +               regulator-min-microvolt = <3300000>;

It's two separate regulators on your schematic, one DCDC at 1.25V and
the other a load switch at 3.3V, driving six separate voltage inputs
of the DP bridge. They are both controlled by the same GPIO pin
though, so _maybe_ it's okay to have just one "virtual" node like this
to model them together. Would be great for the DT maintainers to weigh
in on this.

> +               regulator-name = "vcc3v3_dp";
> +               vin-supply = <&vcc_3v3_s3>;
> +       };
> +
> +       vcc3v3_phy1: regulator-vcc3v3-phy1 {
> +               compatible = "regulator-fixed";
> +               enable-active-high;
> +               gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>;
> +               regulator-boot-on;

See above

> +               regulator-max-microvolt = <3300000>;
> +               regulator-min-microvolt = <3300000>;
> +               regulator-name = "vcc3v3_phy1";
> +               startup-delay-us = <50000>;
> +               vin-supply = <&vcc_3v3_s3>;
> +       };
> +
> +       vcc5v0_otg: regulator-vcc5v0-otg {
> +               compatible = "regulator-fixed";
> +               enable-active-high;
> +               gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>;
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&vcc5v0_otg_en>;
> +               regulator-max-microvolt = <5000000>;
> +               regulator-min-microvolt = <5000000>;
> +               regulator-name = "vcc5v0_otg";
> +               vin-supply = <&vcc5v0_sys>;
> +       };
> +
> +       sdio_pwrseq: sdio-pwrseq {
> +               compatible = "mmc-pwrseq-simple";
> +               clocks = <&hym8563>;
> +               clock-names = "ext_clock";
> +               post-power-on-delay-ms = <200>;
> +               reset-gpios = <&gpio0 RK_PD0 GPIO_ACTIVE_LOW>;

This GPIO also needs a pinctrl

> +       };
> +
> +       typea_con: usb-a-connector {
> +               compatible = "usb-a-connector";
> +               data-role = "host";
> +               label = "USB3 Type-A";
> +               power-role = "source";
> +               vbus-supply = <&vcc5v0_otg>;
> +       };
> +};
> +
> +&dp0 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&dp0m0_pins>;

This switches your HPD pin to the native DP controller handling
(DP0_HPDIN_M0), so the GPIO bits you've patched into the controller
driver aren't even used, and it doesn't look like you tested that code
path.

> +       status = "okay";
> +};
> +
> +&dp0_in {
> +       dp0_in_vp1: endpoint {
> +               remote-endpoint = <&vp1_out_dp0>;
> +       };
> +};
> +
> +&dp0_out {
> +       dp0_out_con: endpoint {
> +               remote-endpoint = <&dp_con_in>;

This will need to be rewritten once you add the proper bridge chain
leading up to the HDMI type A connector.

> +       };
> +};
> +
> +&i2c1 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&i2c1m4_xfer>;
> +       status = "okay";
> +};
> +
> +&i2c3 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&i2c3m0_xfer>;
> +       status = "okay";
> +
> +       es8388: audio-codec@11 {
> +               compatible = "everest,es8388", "everest,es8328";
> +               reg = <0x11>;
> +               #sound-dai-cells = <0>;
> +               AVDD-supply = <&vcc_3v3_s0>;

Are you sure? Schematic says VCCA_3V3_S0, which is a different
regulator (PLDO4 output of the PMIC)

> +               DVDD-supply = <&vcc_1v8_s0>;

Schematic says VCCA_1V8_S0, which is a different regulator (PLDO1
output of the PMIC)

> +               HPVDD-supply = <&vcc_3v3_s0>;

Schematic says VCCA_3V3_S0

> +               PVDD-supply = <&vcc_3v3_s0>;

Schematic says VCCA_1V8_S0

> +               assigned-clock-rates = <12288000>;
> +               assigned-clocks = <&cru I2S2_2CH_MCLKOUT>;
> +               clocks = <&cru I2S2_2CH_MCLKOUT>;
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&i2s2m1_mclk>;
> +       };
> +};
> +
> +&i2c4 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&i2c4m3_xfer>;
> +       status = "okay";
> +};
> +
> +&i2s2_2ch {
> +       pinctrl-0 = <&i2s2m1_lrck &i2s2m1_sclk
> +                    &i2s2m1_sdi &i2s2m1_sdo>;
> +       status = "okay";
> +};
> +
> +&package_thermal {
> +       polling-delay = <1000>;
> +
> +       cooling-maps {
> +               map0 {
> +                       trip = <&package_fan0>;
> +                       cooling-device = <&fan THERMAL_NO_LIMIT 1>;
> +               };
> +
> +               map1 {
> +                       trip = <&package_fan1>;
> +                       cooling-device = <&fan 2 THERMAL_NO_LIMIT>;
> +               };
> +       };
> +
> +       trips {
> +               package_fan0: package-fan0 {
> +                       hysteresis = <2000>;
> +                       temperature = <55000>;
> +                       type = "active";
> +               };
> +
> +               package_fan1: package-fan1 {
> +                       hysteresis = <2000>;
> +                       temperature = <65000>;
> +                       type = "active";
> +               };
> +       };
> +};
> +
> +/* NVMe */
> +&pcie2x1l1 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&pcie30x1m1_1_clkreqn &pcie30x1m1_1_waken>;
> +       reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>;

The GPIO also needs a pinctrl

> +       supports-clkreq;
> +       vpcie3v3-supply = <&vcc_3v3_s3>;
> +       status = "okay";
> +};
> +
> +/* NIC */
> +&pcie2x1l2 {
> +       reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>;

The GPIO also needs a pinctrl

> +       vpcie3v3-supply = <&vcc3v3_phy1>;
> +       status = "okay";
> +};
> +
> +&pinctrl {
> +       bluetooth {
> +               bt_wake_gpio: bt-wake-pin {
> +                       rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
> +               };
> +
> +               bt_wake_host_irq: bt-wake-host-irq {
> +                       rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_down>;
> +               };
> +       };
> +
> +       usb {
> +               vcc5v0_otg_en: vcc5v0-otg-en {
> +                       rockchip,pins = <0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
> +               };
> +       };
> +
> +       wlan {
> +               wifi_host_wake_irq: wifi-host-wake-irq {
> +                       rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_down>;
> +               };
> +       };
> +};
> +
> +&pwm15 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&pwm15m2_pins>;
> +       status = "okay";
> +};
> +
> +&pwm2 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&pwm2m1_pins>;
> +       status = "okay";
> +};
> +
> +&pwm3 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&pwm3m2_pins>;
> +       status = "okay";
> +};
> +
> +&sdhci {
> +       status = "okay";
> +};
> +
> +&sdio {
> +       #address-cells = <1>;
> +       #size-cells = <0>;
> +       bus-width = <4>;
> +       cap-sd-highspeed;
> +       cap-sdio-irq;
> +       keep-power-in-suspend;
> +       max-frequency = <150000000>;
> +       mmc-pwrseq = <&sdio_pwrseq>;
> +       no-mmc;
> +       no-sd;
> +       non-removable;
> +       sd-uhs-sdr104;
> +       status = "okay";
> +
> +       ap6256: wifi@1 {
> +               compatible = "brcm,bcm43456-fmac", "brcm,bcm4329-fmac";
> +               reg = <1>;
> +               interrupt-names = "host-wake";
> +               interrupt-parent = <&gpio0>;
> +               interrupts = <RK_PA0 IRQ_TYPE_LEVEL_HIGH>;
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&wifi_host_wake_irq>;
> +       };
> +};
> +
> +&uart9 {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&uart9m2_xfer &uart9m2_ctsn &uart9m2_rtsn>;
> +       uart-has-rtscts;
> +       status = "okay";
> +
> +       bluetooth {
> +               compatible = "brcm,bcm4345c5";
> +               clocks = <&hym8563>;
> +               clock-names = "lpo";
> +               device-wakeup-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>;
> +               interrupt-names = "host-wakeup";
> +               interrupt-parent = <&gpio0>;
> +               interrupts = <RK_PC5 IRQ_TYPE_LEVEL_HIGH>;
> +               max-speed = <1500000>;
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&bt_wake_host_irq &bt_wake_gpio>;
> +               shutdown-gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
> +               vbat-supply = <&vcc_3v3_s3>;
> +               vddio-supply = <&vcc_1v8_s3>;
> +       };
> +};
> +
> +&usb_host0_xhci {
> +       dr_mode = "host";
> +};
> +
> +&usbdp_phy0 {
> +       rockchip,dp-lane-mux = <0 1>;

I'm wondering if the DP controller's "out" endpoint should go to the
PHY instead of directly to the connector/bridge. That would describe
the hardware better.

> +};
> +
> +&vp1 {
> +       vp1_out_dp0: endpoint@a {
> +               reg = <ROCKCHIP_VOP2_EP_DP0>;
> +               remote-endpoint = <&dp0_in_vp1>;
> +       };
> +};
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index fd23ca2834b0..b58f57b69b22 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -8,6 +8,7 @@
>   */
>  #include <linux/bitfield.h>
>  #include <linux/clk.h>
> +#include <linux/gpio/consumer.h>
>  #include <linux/iopoll.h>
>  #include <linux/irq.h>
>  #include <linux/media-bus-format.h>
> @@ -330,6 +331,8 @@ struct dw_dp {
>         u8 pixel_mode;
>
>         DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE);
> +
> +       struct gpio_desc *hpd_gpiod;
>  };
>
>  enum {
> @@ -481,6 +484,9 @@ static bool dw_dp_hpd_detect(struct dw_dp *dp)
>  {
>         u32 value;
>
> +       if (dp->hpd_gpiod)
> +               return gpiod_get_value_cansleep(dp->hpd_gpiod);
> +
>         regmap_read(dp->regmap, DW_DP_HPD_STATUS, &value);
>
>         return FIELD_GET(HPD_STATE, value) == DW_DP_HPD_STATE_PLUG;
> @@ -2002,6 +2008,12 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
>                 return ERR_CAST(dp->regmap);
>         }
>
> +       dp->hpd_gpiod = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);

Not tested, not needed, why bother?..

Best regards,
Alexey

^ permalink raw reply

* Re: [PATCH v7 1/2] platform: Add initial synology microp driver
From: Onur Özkan @ 2026-04-11 16:55 UTC (permalink / raw)
  To: Markus Probst via B4 Relay
  Cc: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman, platform-driver-x86, linux-leds,
	devicetree, linux-kernel, rust-for-linux, Markus Probst
In-Reply-To: <20260411-synology_microp_initial-v7-1-9a3a094e763a@posteo.de>

On Sat, 11 Apr 2026 17:27:34 +0200
Markus Probst via B4 Relay <devnull+markus.probst.posteo.de@kernel.org> wrote:

> From: Markus Probst <markus.probst@posteo.de>
> 
> Add a initial synology microp driver, written in Rust.
> The driver targets a microcontroller found in Synology NAS devices. It
> currently only supports controlling of the power led, status led, alert
> led and usb led. Other components such as fan control or handling
> on-device buttons will be added once the required rust abstractions are
> there.
> 
> This driver can be used both on arm and x86, thus it goes into the root
> directory of drivers/platform.
> 
> Tested successfully on a Synology DS923+.
> 
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> ---
>  MAINTAINERS                                        |   5 +
>  drivers/platform/Kconfig                           |   2 +
>  drivers/platform/Makefile                          |   1 +
>  drivers/platform/synology_microp/Kconfig           |  13 +
>  drivers/platform/synology_microp/Makefile          |   3 +
>  drivers/platform/synology_microp/TODO              |   7 +
>  drivers/platform/synology_microp/command.rs        |  55 ++++
>  drivers/platform/synology_microp/led.rs            | 276 +++++++++++++++++++++
>  drivers/platform/synology_microp/model.rs          |  49 ++++
>  .../platform/synology_microp/synology_microp.rs    | 109 ++++++++
>  10 files changed, 520 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a667f4efb66e..78c99d831431 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -25554,6 +25554,11 @@ F:	drivers/dma-buf/sync_*
>  F:	include/linux/sync_file.h
>  F:	include/uapi/linux/sync_file.h
>  
> +SYNOLOGY MICROP DRIVER
> +M:	Markus Probst <markus.probst@posteo.de>
> +S:	Maintained
> +F:	drivers/platform/synology_microp/
> +
>  SYNOPSYS ARC ARCHITECTURE
>  M:	Vineet Gupta <vgupta@kernel.org>
>  L:	linux-snps-arc@lists.infradead.org
> diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
> index 312788f249c9..996050566a4a 100644
> --- a/drivers/platform/Kconfig
> +++ b/drivers/platform/Kconfig
> @@ -22,3 +22,5 @@ source "drivers/platform/arm64/Kconfig"
>  source "drivers/platform/raspberrypi/Kconfig"
>  
>  source "drivers/platform/wmi/Kconfig"
> +
> +source "drivers/platform/synology_microp/Kconfig"
> diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
> index fa322e7f8716..2381872e9133 100644
> --- a/drivers/platform/Makefile
> +++ b/drivers/platform/Makefile
> @@ -15,3 +15,4 @@ obj-$(CONFIG_SURFACE_PLATFORMS)	+= surface/
>  obj-$(CONFIG_ARM64_PLATFORM_DEVICES)	+= arm64/
>  obj-$(CONFIG_BCM2835_VCHIQ)	+= raspberrypi/
>  obj-$(CONFIG_ACPI_WMI)		+= wmi/
> +obj-$(CONFIG_SYNOLOGY_MICROP)	+= synology_microp/
> diff --git a/drivers/platform/synology_microp/Kconfig b/drivers/platform/synology_microp/Kconfig
> new file mode 100644
> index 000000000000..7c4d8f2808f0
> --- /dev/null
> +++ b/drivers/platform/synology_microp/Kconfig
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config SYNOLOGY_MICROP
> +	tristate "Synology Microp driver"
> +	depends on LEDS_CLASS && LEDS_CLASS_MULTICOLOR
> +	depends on RUST_SERIAL_DEV_BUS_ABSTRACTIONS
> +	help
> +	  Enable support for the MCU found in Synology NAS devices.
> +
> +	  This is needed to properly shutdown and reboot the device, as well as
> +	  additional functionality like fan and LED control.
> +
> +	  This driver is work in progress and may not be fully functional.
> diff --git a/drivers/platform/synology_microp/Makefile b/drivers/platform/synology_microp/Makefile
> new file mode 100644
> index 000000000000..63585ccf76e4
> --- /dev/null
> +++ b/drivers/platform/synology_microp/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-y += synology_microp.o
> diff --git a/drivers/platform/synology_microp/TODO b/drivers/platform/synology_microp/TODO
> new file mode 100644
> index 000000000000..1961a33115db
> --- /dev/null
> +++ b/drivers/platform/synology_microp/TODO
> @@ -0,0 +1,7 @@
> +TODO:
> +- add missing components:
> +  - handle on-device buttons (Power, Factory reset, "USB Copy")
> +  - handle fan failure
> +  - beeper
> +  - fan speed control
> +  - correctly perform device power-off and restart on Synology devices
> diff --git a/drivers/platform/synology_microp/command.rs b/drivers/platform/synology_microp/command.rs
> new file mode 100644
> index 000000000000..5b3dd715afac
> --- /dev/null
> +++ b/drivers/platform/synology_microp/command.rs
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use kernel::{
> +    device::Bound,
> +    error::Result,
> +    serdev, //
> +};
> +
> +use crate::led;
> +
> +#[derive(Copy, Clone)]

Copy and Clone seem never used, please drop them (also see [1]).

[1]: https://rust-for-linux.zulipchat.com/#narrow/channel/509436-Nova/topic/clone.2Fcopy.20additions

> +#[expect(
> +    clippy::enum_variant_names,
> +    reason = "future variants will not end with Led"
> +)]
> +pub(crate) enum Command {
> +    PowerLed(led::State),
> +    StatusLed(led::StatusLedColor, led::State),
> +    AlertLed(led::State),
> +    UsbLed(led::State),
> +    EsataLed(led::State),
> +}
> +
> +impl Command {
> +    pub(crate) fn write(self, dev: &serdev::Device<Bound>) -> Result {
> +        dev.write_all(
> +            match self {
> +                Self::PowerLed(led::State::On) => &[0x34],
> +                Self::PowerLed(led::State::Blink) => &[0x35],
> +                Self::PowerLed(led::State::Off) => &[0x36],
> +
> +                Self::StatusLed(_, led::State::Off) => &[0x37],
> +                Self::StatusLed(led::StatusLedColor::Green, led::State::On) => &[0x38],
> +                Self::StatusLed(led::StatusLedColor::Green, led::State::Blink) => &[0x39],
> +                Self::StatusLed(led::StatusLedColor::Orange, led::State::On) => &[0x3A],
> +                Self::StatusLed(led::StatusLedColor::Orange, led::State::Blink) => &[0x3B],
> +
> +                Self::AlertLed(led::State::On) => &[0x4C, 0x41, 0x31],
> +                Self::AlertLed(led::State::Blink) => &[0x4C, 0x41, 0x32],
> +                Self::AlertLed(led::State::Off) => &[0x4C, 0x41, 0x33],
> +
> +                Self::UsbLed(led::State::On) => &[0x40],
> +                Self::UsbLed(led::State::Blink) => &[0x41],
> +                Self::UsbLed(led::State::Off) => &[0x42],
> +
> +                Self::EsataLed(led::State::On) => &[0x4C, 0x45, 0x31],
> +                Self::EsataLed(led::State::Blink) => &[0x4C, 0x45, 0x32],
> +                Self::EsataLed(led::State::Off) => &[0x4C, 0x45, 0x33],
> +            },
> +            serdev::Timeout::Max,
> +        )?;
> +        dev.wait_until_sent(serdev::Timeout::Max);
> +        Ok(())
> +    }
> +}
> diff --git a/drivers/platform/synology_microp/led.rs b/drivers/platform/synology_microp/led.rs
> new file mode 100644
> index 000000000000..a78a95588456
> --- /dev/null
> +++ b/drivers/platform/synology_microp/led.rs
> @@ -0,0 +1,276 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use kernel::{
> +    device::Bound,
> +    devres::{
> +        self,
> +        Devres, //
> +    },
> +    led::{
> +        self,
> +        LedOps,
> +        MultiColorSubLed, //
> +    },
> +    new_mutex,
> +    prelude::*,
> +    serdev,
> +    str::CString,
> +    sync::Mutex, //
> +};
> +use pin_init::pin_init_scope;
> +
> +use crate::{
> +    command::Command,
> +    model::Model, //
> +};
> +
> +#[pin_data]
> +pub(crate) struct Data {
> +    #[pin]
> +    status: Devres<led::MultiColorDevice<StatusLedHandler>>,
> +    power_name: CString,
> +    #[pin]
> +    power: Devres<led::Device<LedHandler>>,
> +}
> +
> +impl Data {
> +    pub(super) fn register<'a>(
> +        dev: &'a serdev::Device<Bound>,
> +        model: &'a Model,
> +    ) -> impl PinInit<Self, Error> + 'a {
> +        pin_init_scope(move || {
> +            if let Some(color) = model.led_alert {
> +                let name = CString::try_from_fmt(fmt!("{}:alarm", color.as_c_str().to_str()?))?;
> +                devres::register(
> +                    dev.as_ref(),
> +                    led::DeviceBuilder::new().color(color).name(&name).build(
> +                        dev,
> +                        try_pin_init!(LedHandler {
> +                            blink <- new_mutex!(false),
> +                            command: Command::AlertLed,
> +                        }),
> +                    ),
> +                    GFP_KERNEL,
> +                )?;
> +            }
> +
> +            if model.led_usb_copy {
> +                devres::register(
> +                    dev.as_ref(),
> +                    led::DeviceBuilder::new()
> +                        .color(led::Color::Green)
> +                        .name(c"green:usb")
> +                        .build(
> +                            dev,
> +                            try_pin_init!(LedHandler {
> +                                blink <- new_mutex!(false),
> +                                command: Command::UsbLed,
> +                            }),
> +                        ),
> +                    GFP_KERNEL,
> +                )?;
> +            }
> +
> +            if model.led_esata {
> +                devres::register(
> +                    dev.as_ref(),
> +                    led::DeviceBuilder::new()
> +                        .color(led::Color::Green)
> +                        .name(c"green:esata")
> +                        .build(
> +                            dev,
> +                            try_pin_init!(LedHandler {
> +                                blink <- new_mutex!(false),
> +                                command: Command::EsataLed,
> +                            }),
> +                        ),
> +                    GFP_KERNEL,
> +                )?;
> +            }
> +
> +            Ok(try_pin_init!(Self {
> +                status <- led::DeviceBuilder::new()
> +                    .color(led::Color::Multi)
> +                    .name(c"multicolor:status")
> +                    .build_multicolor(
> +                        dev,
> +                        try_pin_init!(StatusLedHandler {
> +                            blink <- new_mutex!(false),
> +                        }),
> +                        StatusLedHandler::SUBLEDS,
> +                    ),
> +                power_name: CString::try_from_fmt(fmt!(
> +                    "{}:power",
> +                    model.led_power.as_c_str().to_str()?
> +                ))?,
> +                power <- led::DeviceBuilder::new()
> +                    .color(model.led_power)
> +                    .name(power_name)
> +                    .build(
> +                        dev,
> +                        try_pin_init!(LedHandler {
> +                            blink <- new_mutex!(true),
> +                            command: Command::PowerLed,
> +                        }),
> +                    ),
> +            }))
> +        })
> +    }
> +}
> +
> +#[derive(Copy, Clone)]
> +pub(crate) enum StatusLedColor {
> +    Green,
> +    Orange,
> +}
> +
> +#[derive(Copy, Clone)]
> +pub(crate) enum State {
> +    On,
> +    Blink,
> +    Off,
> +}
> +
> +#[pin_data]
> +struct LedHandler {
> +    #[pin]
> +    blink: Mutex<bool>,
> +    command: fn(State) -> Command,
> +}
> +
> +#[vtable]
> +impl LedOps for LedHandler {
> +    type Bus = serdev::Device<Bound>;
> +    type Mode = led::Normal;
> +    const BLOCKING: bool = true;
> +    const MAX_BRIGHTNESS: u32 = 1;
> +
> +    fn brightness_set(
> +        &self,
> +        dev: &Self::Bus,
> +        _classdev: &led::Device<Self>,
> +        brightness: u32,
> +    ) -> Result<()> {
> +        let mut blink = self.blink.lock();
> +        (self.command)(if brightness == 0 {
> +            *blink = false;
> +            State::Off
> +        } else if *blink {
> +            State::Blink
> +        } else {
> +            State::On
> +        })
> +        .write(dev)?;
> +
> +        Ok(())
> +    }
> +
> +    fn blink_set(
> +        &self,
> +        dev: &Self::Bus,
> +        _classdev: &led::Device<Self>,
> +        delay_on: &mut usize,
> +        delay_off: &mut usize,
> +    ) -> Result<()> {
> +        let mut blink = self.blink.lock();
> +
> +        (self.command)(if *delay_on == 0 && *delay_off != 0 {
> +            State::Off
> +        } else if *delay_on != 0 && *delay_off == 0 {
> +            State::On
> +        } else {
> +            *blink = true;
> +            *delay_on = 167;
> +            *delay_off = 167;

Perhaps using a named constant with a simple comment instead of a hard-coded
integer would help to clarify what 167 is.

- Onur

> +
> +            State::Blink
> +        })
> +        .write(dev)
> +    }
> +}
> +
> +#[pin_data]
> +struct StatusLedHandler {
> +    #[pin]
> +    blink: Mutex<bool>,
> +}
> +
> +impl StatusLedHandler {
> +    const SUBLEDS: &[MultiColorSubLed] = &[
> +        MultiColorSubLed::new(led::Color::Green).initial_intensity(1),
> +        MultiColorSubLed::new(led::Color::Orange),
> +    ];
> +}
> +
> +#[vtable]
> +impl LedOps for StatusLedHandler {
> +    type Bus = serdev::Device<Bound>;
> +    type Mode = led::MultiColor;
> +    const BLOCKING: bool = true;
> +    const MAX_BRIGHTNESS: u32 = 1;
> +
> +    fn brightness_set(
> +        &self,
> +        dev: &Self::Bus,
> +        classdev: &led::MultiColorDevice<Self>,
> +        brightness: u32,
> +    ) -> Result<()> {
> +        let mut blink = self.blink.lock();
> +        if brightness == 0 {
> +            *blink = false;
> +        }
> +
> +        let (color, subled_brightness) = if classdev.subleds()[1].intensity == 0 {
> +            (StatusLedColor::Green, classdev.subleds()[0].brightness)
> +        } else {
> +            (StatusLedColor::Orange, classdev.subleds()[1].brightness)
> +        };
> +
> +        Command::StatusLed(
> +            color,
> +            if subled_brightness == 0 {
> +                State::Off
> +            } else if *blink {
> +                State::Blink
> +            } else {
> +                State::On
> +            },
> +        )
> +        .write(dev)
> +    }
> +
> +    fn blink_set(
> +        &self,
> +        dev: &Self::Bus,
> +        classdev: &led::MultiColorDevice<Self>,
> +        delay_on: &mut usize,
> +        delay_off: &mut usize,
> +    ) -> Result<()> {
> +        let mut blink = self.blink.lock();
> +        *blink = true;
> +
> +        let (color, subled_intensity) = if classdev.subleds()[1].intensity == 0 {
> +            (StatusLedColor::Green, classdev.subleds()[0].intensity)
> +        } else {
> +            (StatusLedColor::Orange, classdev.subleds()[1].intensity)
> +        };
> +        Command::StatusLed(
> +            color,
> +            if *delay_on == 0 && *delay_off != 0 {
> +                *blink = false;
> +                State::Off
> +            } else if subled_intensity == 0 {
> +                State::Off
> +            } else if *delay_on != 0 && *delay_off == 0 {
> +                *blink = false;
> +                State::On
> +            } else {
> +                *delay_on = 167;
> +                *delay_off = 167;
> +
> +                State::Blink
> +            },
> +        )
> +        .write(dev)
> +    }
> +}
> diff --git a/drivers/platform/synology_microp/model.rs b/drivers/platform/synology_microp/model.rs
> new file mode 100644
> index 000000000000..715d8840f56b
> --- /dev/null
> +++ b/drivers/platform/synology_microp/model.rs
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use kernel::led::Color;
> +
> +pub(crate) struct Model {
> +    pub(crate) led_power: Color,
> +    pub(crate) led_alert: Option<Color>,
> +    pub(crate) led_usb_copy: bool,
> +    pub(crate) led_esata: bool,
> +}
> +
> +impl Model {
> +    pub(super) const fn new() -> Self {
> +        Self {
> +            led_power: Color::Blue,
> +            led_alert: None,
> +            led_usb_copy: false,
> +            led_esata: false,
> +        }
> +    }
> +
> +    pub(super) const fn led_power(self, color: Color) -> Self {
> +        Self {
> +            led_power: color,
> +            ..self
> +        }
> +    }
> +
> +    pub(super) const fn led_alert(self, color: Color) -> Self {
> +        Self {
> +            led_alert: Some(color),
> +            ..self
> +        }
> +    }
> +
> +    pub(super) const fn led_esata(self) -> Self {
> +        Self {
> +            led_esata: true,
> +            ..self
> +        }
> +    }
> +
> +    pub(super) const fn led_usb_copy(self) -> Self {
> +        Self {
> +            led_usb_copy: true,
> +            ..self
> +        }
> +    }
> +}
> diff --git a/drivers/platform/synology_microp/synology_microp.rs b/drivers/platform/synology_microp/synology_microp.rs
> new file mode 100644
> index 000000000000..f02c4dade76c
> --- /dev/null
> +++ b/drivers/platform/synology_microp/synology_microp.rs
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Synology Microp driver
> +
> +use kernel::{
> +    device,
> +    led::Color,
> +    of::{
> +        DeviceId,
> +        IdTable, //
> +    },
> +    of_device_table,
> +    prelude::*,
> +    serdev, //
> +};
> +use pin_init::pin_init_scope;
> +
> +use crate::model::Model;
> +
> +pub(crate) mod command;
> +mod led;
> +mod model;
> +
> +kernel::module_serdev_device_driver! {
> +    type: SynologyMicropDriver,
> +    name: "synology_microp",
> +    authors: ["Markus Probst <markus.probst@posteo.de>"],
> +    description: "Synology Microp driver",
> +    license: "GPL v2",
> +}
> +
> +#[rustfmt::skip]
> +of_device_table!(
> +    OF_TABLE,
> +    MODULE_OF_TABLE,
> +    Model,
> +    [
> +        // apollolake
> +        (DeviceId::new(c"synology,ds918p-microp"), Model::new()),
> +
> +        // evansport
> +        (DeviceId::new(c"synology,ds214play-microp"), Model::new()),
> +
> +        // geminilakenk
> +        (DeviceId::new(c"synology,ds225p-microp"), Model::new().led_usb_copy()),
> +        (DeviceId::new(c"synology,ds425p-microp"), Model::new()),
> +
> +        // pineview
> +        (DeviceId::new(c"synology,ds710p-microp"), Model::new().led_esata()),
> +        (DeviceId::new(c"synology,ds1010p-microp"), Model::new().led_alert(Color::Orange)),
> +
> +        // r1000
> +        (DeviceId::new(c"synology,ds923p-microp"), Model::new()),
> +        (DeviceId::new(c"synology,ds723p-microp"), Model::new()),
> +        (DeviceId::new(c"synology,ds1522p-microp"), Model::new()),
> +        (DeviceId::new(c"synology,rs422p-microp"), Model::new().led_power(Color::Green)),
> +
> +        // r1000nk
> +        (DeviceId::new(c"synology,ds725p-microp"), Model::new()),
> +
> +        // rtd1296
> +        (DeviceId::new(c"synology,ds118-microp"), Model::new()),
> +
> +        // rtd1619b
> +        (DeviceId::new(c"synology,ds124-microp"), Model::new()),
> +        (DeviceId::new(c"synolody,ds223-microp"), Model::new().led_usb_copy()),
> +        (DeviceId::new(c"synology,ds223j-microp"), Model::new()),
> +
> +        // v1000
> +        (DeviceId::new(c"synology,ds1823xsp-microp"), Model::new()),
> +        (DeviceId::new(c"synology,rs822p-microp"), Model::new().led_power(Color::Green)),
> +        (DeviceId::new(c"synology,rs1221p-microp"), Model::new().led_power(Color::Green)),
> +        (DeviceId::new(c"synology,rs1221rpp-microp"), Model::new().led_power(Color::Green)),
> +
> +        // v1000nk
> +        (DeviceId::new(c"synology,ds925p-microp"), Model::new()),
> +        (DeviceId::new(c"synology,ds1525p-microp"), Model::new()),
> +        (DeviceId::new(c"synology,ds1825p-microp"), Model::new()),
> +    ]
> +);
> +
> +#[pin_data]
> +struct SynologyMicropDriver {
> +    #[pin]
> +    led: led::Data,
> +}
> +
> +#[vtable]
> +impl serdev::Driver for SynologyMicropDriver {
> +    type IdInfo = Model;
> +    const OF_ID_TABLE: Option<IdTable<Self::IdInfo>> = Some(&OF_TABLE);
> +
> +    fn probe(
> +        dev: &serdev::Device<device::Core>,
> +        model: Option<&Model>,
> +    ) -> impl PinInit<Self, kernel::error::Error> {
> +        pin_init_scope(move || {
> +            let model = model.ok_or(EINVAL)?;
> +
> +            dev.set_baudrate(9600).map_err(|_| EINVAL)?;
> +            dev.set_flow_control(false);
> +            dev.set_parity(serdev::Parity::None)?;
> +
> +            Ok(try_pin_init!(Self {
> +                led <- led::Data::register(dev, model),
> +            }))
> +        })
> +    }
> +}
> 
> -- 
> 2.52.0
> 
> 

^ permalink raw reply

* Re: [PATCH v9 2/3] hwmon: ltc4283: Add support for the LTC4283 Swap Controller
From: Guenter Roeck @ 2026-04-11 15:54 UTC (permalink / raw)
  To: Nuno Sá, nuno.sa, linux-gpio, linux-hwmon, devicetree,
	linux-doc
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Linus Walleij, Bartosz Golaszewski
In-Reply-To: <2653dc70f42fd015b88e2744da257f6200603b50.camel@gmail.com>

On 4/11/26 05:38, Nuno Sá wrote:
> On Fri, 2026-04-10 at 16:27 -0700, Guenter Roeck wrote:
>> On 4/6/26 07:31, Nuno Sá via B4 Relay wrote:
>>> From: Nuno Sá <nuno.sa@analog.com>
>>>
>>> Support the LTC4283 Hot Swap Controller. The device features programmable
>>> current limit with foldback and independently adjustable inrush current to
>>> optimize the MOSFET safe operating area (SOA). The SOA timer limits MOSFET
>>> temperature rise for reliable protection against overstresses.
>>>
>>> An I2C interface and onboard ADC allow monitoring of board current,
>>> voltage, power, energy, and fault status.
>>>
>>> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
>>
>> The patch still has some issues. Please see
>>
>> https://sashiko.dev/#/patchset/20260406-ltc4283-support-v9-0-b66cfc749261%40analog.com
>>
>> Specifically:
>>
>> - regmap_clear_bits() may not cause problems, but it is not the best
>>     choice either because the register was already read.
>>     It might be better to just write the value to be masked since
>>     both the register value and the mask are known.
> 
> Fair enough.
> 
>>
>> - I can't comment on the energy accuracy lost. That is your call.
>>
> 
> The AI might have a point. Maybe you know better but if I understood correctly,
> mul_u64_u64_div_u64() will handle the multiplication by using 128bits (when
> available) or if not, using clever tricks. And it should also handle overflows.
> 
> So my feeling is that we can simplify all of those check_overflow paths with the
> suggested API.
> 
>> - Clamping before multiplying is indeed wrong.
>>     You'll need to clamp before multiplying (and then possibly
>>     clamp again).
> 
> Yeah, the clamp change was just nonsense from me. What about about
> 
> val = clamp_val((u64)val * MILLI, ...)
> 
> ?
> 

I don't think that will work on systems where sizeof(long) == 64.
I'd suggest to just bite the bullet and clamp against LONG_MAX/MILLI
first.

> 
>> -  %*ph: The AI seems to have a point.
> 
> Indeed!
> 
> FWIW, I was already aware of the AI feedback but I'll just setup things locally and
> run the review before submitting again.
> 

The AI now copies you on new revisions. Please feel free to rely on that
(unless you have tokens to burn, of course ;-). Those AI reviews are cheap
for what they do, but they are expensive in absolute terms.

Thanks,
Guenter


^ permalink raw reply

* [PATCH v7 1/2] platform: Add initial synology microp driver
From: Markus Probst via B4 Relay @ 2026-04-11 15:27 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, Markus Probst
In-Reply-To: <20260411-synology_microp_initial-v7-0-9a3a094e763a@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Add a initial synology microp driver, written in Rust.
The driver targets a microcontroller found in Synology NAS devices. It
currently only supports controlling of the power led, status led, alert
led and usb led. Other components such as fan control or handling
on-device buttons will be added once the required rust abstractions are
there.

This driver can be used both on arm and x86, thus it goes into the root
directory of drivers/platform.

Tested successfully on a Synology DS923+.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                                        |   5 +
 drivers/platform/Kconfig                           |   2 +
 drivers/platform/Makefile                          |   1 +
 drivers/platform/synology_microp/Kconfig           |  13 +
 drivers/platform/synology_microp/Makefile          |   3 +
 drivers/platform/synology_microp/TODO              |   7 +
 drivers/platform/synology_microp/command.rs        |  55 ++++
 drivers/platform/synology_microp/led.rs            | 276 +++++++++++++++++++++
 drivers/platform/synology_microp/model.rs          |  49 ++++
 .../platform/synology_microp/synology_microp.rs    | 109 ++++++++
 10 files changed, 520 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index a667f4efb66e..78c99d831431 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25554,6 +25554,11 @@ F:	drivers/dma-buf/sync_*
 F:	include/linux/sync_file.h
 F:	include/uapi/linux/sync_file.h
 
+SYNOLOGY MICROP DRIVER
+M:	Markus Probst <markus.probst@posteo.de>
+S:	Maintained
+F:	drivers/platform/synology_microp/
+
 SYNOPSYS ARC ARCHITECTURE
 M:	Vineet Gupta <vgupta@kernel.org>
 L:	linux-snps-arc@lists.infradead.org
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 312788f249c9..996050566a4a 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -22,3 +22,5 @@ source "drivers/platform/arm64/Kconfig"
 source "drivers/platform/raspberrypi/Kconfig"
 
 source "drivers/platform/wmi/Kconfig"
+
+source "drivers/platform/synology_microp/Kconfig"
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index fa322e7f8716..2381872e9133 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_SURFACE_PLATFORMS)	+= surface/
 obj-$(CONFIG_ARM64_PLATFORM_DEVICES)	+= arm64/
 obj-$(CONFIG_BCM2835_VCHIQ)	+= raspberrypi/
 obj-$(CONFIG_ACPI_WMI)		+= wmi/
+obj-$(CONFIG_SYNOLOGY_MICROP)	+= synology_microp/
diff --git a/drivers/platform/synology_microp/Kconfig b/drivers/platform/synology_microp/Kconfig
new file mode 100644
index 000000000000..7c4d8f2808f0
--- /dev/null
+++ b/drivers/platform/synology_microp/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config SYNOLOGY_MICROP
+	tristate "Synology Microp driver"
+	depends on LEDS_CLASS && LEDS_CLASS_MULTICOLOR
+	depends on RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	help
+	  Enable support for the MCU found in Synology NAS devices.
+
+	  This is needed to properly shutdown and reboot the device, as well as
+	  additional functionality like fan and LED control.
+
+	  This driver is work in progress and may not be fully functional.
diff --git a/drivers/platform/synology_microp/Makefile b/drivers/platform/synology_microp/Makefile
new file mode 100644
index 000000000000..63585ccf76e4
--- /dev/null
+++ b/drivers/platform/synology_microp/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += synology_microp.o
diff --git a/drivers/platform/synology_microp/TODO b/drivers/platform/synology_microp/TODO
new file mode 100644
index 000000000000..1961a33115db
--- /dev/null
+++ b/drivers/platform/synology_microp/TODO
@@ -0,0 +1,7 @@
+TODO:
+- add missing components:
+  - handle on-device buttons (Power, Factory reset, "USB Copy")
+  - handle fan failure
+  - beeper
+  - fan speed control
+  - correctly perform device power-off and restart on Synology devices
diff --git a/drivers/platform/synology_microp/command.rs b/drivers/platform/synology_microp/command.rs
new file mode 100644
index 000000000000..5b3dd715afac
--- /dev/null
+++ b/drivers/platform/synology_microp/command.rs
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    error::Result,
+    serdev, //
+};
+
+use crate::led;
+
+#[derive(Copy, Clone)]
+#[expect(
+    clippy::enum_variant_names,
+    reason = "future variants will not end with Led"
+)]
+pub(crate) enum Command {
+    PowerLed(led::State),
+    StatusLed(led::StatusLedColor, led::State),
+    AlertLed(led::State),
+    UsbLed(led::State),
+    EsataLed(led::State),
+}
+
+impl Command {
+    pub(crate) fn write(self, dev: &serdev::Device<Bound>) -> Result {
+        dev.write_all(
+            match self {
+                Self::PowerLed(led::State::On) => &[0x34],
+                Self::PowerLed(led::State::Blink) => &[0x35],
+                Self::PowerLed(led::State::Off) => &[0x36],
+
+                Self::StatusLed(_, led::State::Off) => &[0x37],
+                Self::StatusLed(led::StatusLedColor::Green, led::State::On) => &[0x38],
+                Self::StatusLed(led::StatusLedColor::Green, led::State::Blink) => &[0x39],
+                Self::StatusLed(led::StatusLedColor::Orange, led::State::On) => &[0x3A],
+                Self::StatusLed(led::StatusLedColor::Orange, led::State::Blink) => &[0x3B],
+
+                Self::AlertLed(led::State::On) => &[0x4C, 0x41, 0x31],
+                Self::AlertLed(led::State::Blink) => &[0x4C, 0x41, 0x32],
+                Self::AlertLed(led::State::Off) => &[0x4C, 0x41, 0x33],
+
+                Self::UsbLed(led::State::On) => &[0x40],
+                Self::UsbLed(led::State::Blink) => &[0x41],
+                Self::UsbLed(led::State::Off) => &[0x42],
+
+                Self::EsataLed(led::State::On) => &[0x4C, 0x45, 0x31],
+                Self::EsataLed(led::State::Blink) => &[0x4C, 0x45, 0x32],
+                Self::EsataLed(led::State::Off) => &[0x4C, 0x45, 0x33],
+            },
+            serdev::Timeout::Max,
+        )?;
+        dev.wait_until_sent(serdev::Timeout::Max);
+        Ok(())
+    }
+}
diff --git a/drivers/platform/synology_microp/led.rs b/drivers/platform/synology_microp/led.rs
new file mode 100644
index 000000000000..a78a95588456
--- /dev/null
+++ b/drivers/platform/synology_microp/led.rs
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    devres::{
+        self,
+        Devres, //
+    },
+    led::{
+        self,
+        LedOps,
+        MultiColorSubLed, //
+    },
+    new_mutex,
+    prelude::*,
+    serdev,
+    str::CString,
+    sync::Mutex, //
+};
+use pin_init::pin_init_scope;
+
+use crate::{
+    command::Command,
+    model::Model, //
+};
+
+#[pin_data]
+pub(crate) struct Data {
+    #[pin]
+    status: Devres<led::MultiColorDevice<StatusLedHandler>>,
+    power_name: CString,
+    #[pin]
+    power: Devres<led::Device<LedHandler>>,
+}
+
+impl Data {
+    pub(super) fn register<'a>(
+        dev: &'a serdev::Device<Bound>,
+        model: &'a Model,
+    ) -> impl PinInit<Self, Error> + 'a {
+        pin_init_scope(move || {
+            if let Some(color) = model.led_alert {
+                let name = CString::try_from_fmt(fmt!("{}:alarm", color.as_c_str().to_str()?))?;
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new().color(color).name(&name).build(
+                        dev,
+                        try_pin_init!(LedHandler {
+                            blink <- new_mutex!(false),
+                            command: Command::AlertLed,
+                        }),
+                    ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            if model.led_usb_copy {
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new()
+                        .color(led::Color::Green)
+                        .name(c"green:usb")
+                        .build(
+                            dev,
+                            try_pin_init!(LedHandler {
+                                blink <- new_mutex!(false),
+                                command: Command::UsbLed,
+                            }),
+                        ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            if model.led_esata {
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new()
+                        .color(led::Color::Green)
+                        .name(c"green:esata")
+                        .build(
+                            dev,
+                            try_pin_init!(LedHandler {
+                                blink <- new_mutex!(false),
+                                command: Command::EsataLed,
+                            }),
+                        ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            Ok(try_pin_init!(Self {
+                status <- led::DeviceBuilder::new()
+                    .color(led::Color::Multi)
+                    .name(c"multicolor:status")
+                    .build_multicolor(
+                        dev,
+                        try_pin_init!(StatusLedHandler {
+                            blink <- new_mutex!(false),
+                        }),
+                        StatusLedHandler::SUBLEDS,
+                    ),
+                power_name: CString::try_from_fmt(fmt!(
+                    "{}:power",
+                    model.led_power.as_c_str().to_str()?
+                ))?,
+                power <- led::DeviceBuilder::new()
+                    .color(model.led_power)
+                    .name(power_name)
+                    .build(
+                        dev,
+                        try_pin_init!(LedHandler {
+                            blink <- new_mutex!(true),
+                            command: Command::PowerLed,
+                        }),
+                    ),
+            }))
+        })
+    }
+}
+
+#[derive(Copy, Clone)]
+pub(crate) enum StatusLedColor {
+    Green,
+    Orange,
+}
+
+#[derive(Copy, Clone)]
+pub(crate) enum State {
+    On,
+    Blink,
+    Off,
+}
+
+#[pin_data]
+struct LedHandler {
+    #[pin]
+    blink: Mutex<bool>,
+    command: fn(State) -> Command,
+}
+
+#[vtable]
+impl LedOps for LedHandler {
+    type Bus = serdev::Device<Bound>;
+    type Mode = led::Normal;
+    const BLOCKING: bool = true;
+    const MAX_BRIGHTNESS: u32 = 1;
+
+    fn brightness_set(
+        &self,
+        dev: &Self::Bus,
+        _classdev: &led::Device<Self>,
+        brightness: u32,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        (self.command)(if brightness == 0 {
+            *blink = false;
+            State::Off
+        } else if *blink {
+            State::Blink
+        } else {
+            State::On
+        })
+        .write(dev)?;
+
+        Ok(())
+    }
+
+    fn blink_set(
+        &self,
+        dev: &Self::Bus,
+        _classdev: &led::Device<Self>,
+        delay_on: &mut usize,
+        delay_off: &mut usize,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+
+        (self.command)(if *delay_on == 0 && *delay_off != 0 {
+            State::Off
+        } else if *delay_on != 0 && *delay_off == 0 {
+            State::On
+        } else {
+            *blink = true;
+            *delay_on = 167;
+            *delay_off = 167;
+
+            State::Blink
+        })
+        .write(dev)
+    }
+}
+
+#[pin_data]
+struct StatusLedHandler {
+    #[pin]
+    blink: Mutex<bool>,
+}
+
+impl StatusLedHandler {
+    const SUBLEDS: &[MultiColorSubLed] = &[
+        MultiColorSubLed::new(led::Color::Green).initial_intensity(1),
+        MultiColorSubLed::new(led::Color::Orange),
+    ];
+}
+
+#[vtable]
+impl LedOps for StatusLedHandler {
+    type Bus = serdev::Device<Bound>;
+    type Mode = led::MultiColor;
+    const BLOCKING: bool = true;
+    const MAX_BRIGHTNESS: u32 = 1;
+
+    fn brightness_set(
+        &self,
+        dev: &Self::Bus,
+        classdev: &led::MultiColorDevice<Self>,
+        brightness: u32,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        if brightness == 0 {
+            *blink = false;
+        }
+
+        let (color, subled_brightness) = if classdev.subleds()[1].intensity == 0 {
+            (StatusLedColor::Green, classdev.subleds()[0].brightness)
+        } else {
+            (StatusLedColor::Orange, classdev.subleds()[1].brightness)
+        };
+
+        Command::StatusLed(
+            color,
+            if subled_brightness == 0 {
+                State::Off
+            } else if *blink {
+                State::Blink
+            } else {
+                State::On
+            },
+        )
+        .write(dev)
+    }
+
+    fn blink_set(
+        &self,
+        dev: &Self::Bus,
+        classdev: &led::MultiColorDevice<Self>,
+        delay_on: &mut usize,
+        delay_off: &mut usize,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        *blink = true;
+
+        let (color, subled_intensity) = if classdev.subleds()[1].intensity == 0 {
+            (StatusLedColor::Green, classdev.subleds()[0].intensity)
+        } else {
+            (StatusLedColor::Orange, classdev.subleds()[1].intensity)
+        };
+        Command::StatusLed(
+            color,
+            if *delay_on == 0 && *delay_off != 0 {
+                *blink = false;
+                State::Off
+            } else if subled_intensity == 0 {
+                State::Off
+            } else if *delay_on != 0 && *delay_off == 0 {
+                *blink = false;
+                State::On
+            } else {
+                *delay_on = 167;
+                *delay_off = 167;
+
+                State::Blink
+            },
+        )
+        .write(dev)
+    }
+}
diff --git a/drivers/platform/synology_microp/model.rs b/drivers/platform/synology_microp/model.rs
new file mode 100644
index 000000000000..715d8840f56b
--- /dev/null
+++ b/drivers/platform/synology_microp/model.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::led::Color;
+
+pub(crate) struct Model {
+    pub(crate) led_power: Color,
+    pub(crate) led_alert: Option<Color>,
+    pub(crate) led_usb_copy: bool,
+    pub(crate) led_esata: bool,
+}
+
+impl Model {
+    pub(super) const fn new() -> Self {
+        Self {
+            led_power: Color::Blue,
+            led_alert: None,
+            led_usb_copy: false,
+            led_esata: false,
+        }
+    }
+
+    pub(super) const fn led_power(self, color: Color) -> Self {
+        Self {
+            led_power: color,
+            ..self
+        }
+    }
+
+    pub(super) const fn led_alert(self, color: Color) -> Self {
+        Self {
+            led_alert: Some(color),
+            ..self
+        }
+    }
+
+    pub(super) const fn led_esata(self) -> Self {
+        Self {
+            led_esata: true,
+            ..self
+        }
+    }
+
+    pub(super) const fn led_usb_copy(self) -> Self {
+        Self {
+            led_usb_copy: true,
+            ..self
+        }
+    }
+}
diff --git a/drivers/platform/synology_microp/synology_microp.rs b/drivers/platform/synology_microp/synology_microp.rs
new file mode 100644
index 000000000000..f02c4dade76c
--- /dev/null
+++ b/drivers/platform/synology_microp/synology_microp.rs
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Synology Microp driver
+
+use kernel::{
+    device,
+    led::Color,
+    of::{
+        DeviceId,
+        IdTable, //
+    },
+    of_device_table,
+    prelude::*,
+    serdev, //
+};
+use pin_init::pin_init_scope;
+
+use crate::model::Model;
+
+pub(crate) mod command;
+mod led;
+mod model;
+
+kernel::module_serdev_device_driver! {
+    type: SynologyMicropDriver,
+    name: "synology_microp",
+    authors: ["Markus Probst <markus.probst@posteo.de>"],
+    description: "Synology Microp driver",
+    license: "GPL v2",
+}
+
+#[rustfmt::skip]
+of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    Model,
+    [
+        // apollolake
+        (DeviceId::new(c"synology,ds918p-microp"), Model::new()),
+
+        // evansport
+        (DeviceId::new(c"synology,ds214play-microp"), Model::new()),
+
+        // geminilakenk
+        (DeviceId::new(c"synology,ds225p-microp"), Model::new().led_usb_copy()),
+        (DeviceId::new(c"synology,ds425p-microp"), Model::new()),
+
+        // pineview
+        (DeviceId::new(c"synology,ds710p-microp"), Model::new().led_esata()),
+        (DeviceId::new(c"synology,ds1010p-microp"), Model::new().led_alert(Color::Orange)),
+
+        // r1000
+        (DeviceId::new(c"synology,ds923p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds723p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds1522p-microp"), Model::new()),
+        (DeviceId::new(c"synology,rs422p-microp"), Model::new().led_power(Color::Green)),
+
+        // r1000nk
+        (DeviceId::new(c"synology,ds725p-microp"), Model::new()),
+
+        // rtd1296
+        (DeviceId::new(c"synology,ds118-microp"), Model::new()),
+
+        // rtd1619b
+        (DeviceId::new(c"synology,ds124-microp"), Model::new()),
+        (DeviceId::new(c"synolody,ds223-microp"), Model::new().led_usb_copy()),
+        (DeviceId::new(c"synology,ds223j-microp"), Model::new()),
+
+        // v1000
+        (DeviceId::new(c"synology,ds1823xsp-microp"), Model::new()),
+        (DeviceId::new(c"synology,rs822p-microp"), Model::new().led_power(Color::Green)),
+        (DeviceId::new(c"synology,rs1221p-microp"), Model::new().led_power(Color::Green)),
+        (DeviceId::new(c"synology,rs1221rpp-microp"), Model::new().led_power(Color::Green)),
+
+        // v1000nk
+        (DeviceId::new(c"synology,ds925p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds1525p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds1825p-microp"), Model::new()),
+    ]
+);
+
+#[pin_data]
+struct SynologyMicropDriver {
+    #[pin]
+    led: led::Data,
+}
+
+#[vtable]
+impl serdev::Driver for SynologyMicropDriver {
+    type IdInfo = Model;
+    const OF_ID_TABLE: Option<IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+    fn probe(
+        dev: &serdev::Device<device::Core>,
+        model: Option<&Model>,
+    ) -> impl PinInit<Self, kernel::error::Error> {
+        pin_init_scope(move || {
+            let model = model.ok_or(EINVAL)?;
+
+            dev.set_baudrate(9600).map_err(|_| EINVAL)?;
+            dev.set_flow_control(false);
+            dev.set_parity(serdev::Parity::None)?;
+
+            Ok(try_pin_init!(Self {
+                led <- led::Data::register(dev, model),
+            }))
+        })
+    }
+}

-- 
2.52.0



^ permalink raw reply related

* [PATCH v7 2/2] dt-bindings: embedded-controller: Add synology microp devices
From: Markus Probst via B4 Relay @ 2026-04-11 15:27 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, Markus Probst
In-Reply-To: <20260411-synology_microp_initial-v7-0-9a3a094e763a@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Add the Synology Microp devicetree bindings. Those devices are
microcontrollers found on Synology NAS devices. They are connected to a
serial port on the host device.

Those devices are used to control certain LEDs, fan speeds, a beeper, to
handle buttons, fan failures and to properly shutdown and reboot the
device.

The device has a different feature set depending on the Synology NAS
model, like having different number of fans, buttons and leds. Depending
on the architecture of the model, they also need a different system
shutdown behaviour.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 .../synology,ds923p-microp.yaml                    | 92 ++++++++++++++++++++++
 MAINTAINERS                                        |  1 +
 2 files changed, 93 insertions(+)

diff --git a/Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml b/Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml
new file mode 100644
index 000000000000..0a8fb1d8f314
--- /dev/null
+++ b/Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/embedded-controller/synology,ds923p-microp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synology NAS on-board Microcontroller
+
+maintainers:
+  - Markus Probst <markus.probst@posteo.de>
+
+description: |
+  Synology Microp is a microcontroller found in Synology NAS devices.
+  It is connected to a serial port on the host device.
+
+  It is necessary to properly shutdown and reboot the NAS device and
+  provides additional functionality such as led control, fan speed control,
+  a beeper and buttons on the NAS device.
+
+properties:
+  compatible:
+    enum:
+      - synology,ds923p-microp
+      - synology,ds918p-microp
+      - synology,ds214play-microp
+      - synology,ds225p-microp
+      - synology,ds425p-microp
+      - synology,ds710p-microp
+      - synology,ds1010p-microp
+      - synology,ds723p-microp
+      - synology,ds1522p-microp
+      - synology,rs422p-microp
+      - synology,ds725p-microp
+      - synology,ds118-microp
+      - synology,ds124-microp
+      - synology,ds223-microp
+      - synology,ds223j-microp
+      - synology,ds1823xsp-microp
+      - synology,rs822p-microp
+      - synology,rs1221p-microp
+      - synology,rs1221rpp-microp
+      - synology,ds925p-microp
+      - synology,ds1525p-microp
+      - synology,ds1825p-microp
+
+  fan-failure-gpios:
+    description: GPIOs needed to determine which fans stopped working on a fan failure event.
+    minItems: 2
+    maxItems: 3
+
+required:
+  - compatible
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - synology,ds214play-microp
+              - synology,ds225p-microp
+              - synology,ds710p-microp
+              - synology,ds723p-microp
+              - synology,ds725p-microp
+              - synology,ds118-microp
+              - synology,ds124-microp
+              - synology,ds223-microp
+              - synology,ds223j-microp
+              - synology,ds1823xsp-microp
+              - synology,rs822p-microp
+              - synology,rs1221p-microp
+              - synology,rs1221rpp-microp
+              - synology,ds1825p-microp
+    then:
+      properties:
+        fan-failure-gpios: false
+    else:
+      required:
+        - fan-failure-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/leds/common.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    embedded-controller {
+      compatible = "synology,ds923p-microp";
+
+      fan-failure-gpios = <&gpio 68 GPIO_ACTIVE_HIGH>, <&gpio 69 GPIO_ACTIVE_HIGH>;
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 78c99d831431..72075c9a2016 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25557,6 +25557,7 @@ F:	include/uapi/linux/sync_file.h
 SYNOLOGY MICROP DRIVER
 M:	Markus Probst <markus.probst@posteo.de>
 S:	Maintained
+F:	Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml
 F:	drivers/platform/synology_microp/
 
 SYNOPSYS ARC ARCHITECTURE

-- 
2.52.0



^ permalink raw reply related

* [PATCH v7 0/2] Introduce Synology Microp driver
From: Markus Probst via B4 Relay @ 2026-04-11 15:27 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, Markus Probst

Synology uses a microcontroller in their NAS devices connected to a
serial port to control certain LEDs, fan speeds, a beeper, to handle
proper shutdown and restart, buttons and fan failures.

This patch series depends on the rust led abstraction [1] and the rust
serdev abstraction [2].

This is only a initial version of the driver able to control LEDs.
The following rust abstractions would be required, to implement the
remaining features:
- hwmon (include/linux/hwmon.h)
- input (include/linux/input.h)
- sysoff handler + hardware protection shutdown (include/linux/reboot.h)

[1] https://lore.kernel.org/rust-for-linux/20260329-rust_leds-v13-0-21a599c5b2d1@posteo.de/
[2] https://lore.kernel.org/rust-for-linux/20260411-rust_serdev-v4-0-845e960c6627@posteo.de/

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v7:
- remove list of compatible ids from commit msg
- explain what makes the different models not compatible in the commit msg
- remove unnecessary examples
- Link to v6: https://lore.kernel.org/r/20260405-synology_microp_initial-v6-0-08fde474b6c9@posteo.de

Changes in v6:
- moved devicetree bindings patch at the end of the set
- remove several patches
- move of id table from model.rs to synology_microp.rs
- remove the model! macro
- use if blocks in devicetree schema to narrow down the
  fan-failure-gpios property
- add multiple devicetree examples to test if blocks
- Link to v5: https://lore.kernel.org/r/20260329-synology_microp_initial-v5-0-27cb80bdf591@posteo.de

Changes in v5:
- add esata led support
- use different compatible for each model
- add visibility modifier to of_device_table macro
- fix match data missing when using PRP0001
- Link to v4: https://lore.kernel.org/r/20260320-synology_microp_initial-v4-0-0423ddb83ca4@posteo.de

Changes in v4:
- convert to monolithic driver and moved it into drivers/platform
- removed mfd rust abstraction
- moved dt-bindings to embedded-controller
- Link to v3: https://lore.kernel.org/r/20260313-synology_microp_initial-v3-0-ad6ac463a201@posteo.de

Changes in v3:
- remove `default n` from Kconfig entry, as n is the default already.
- select RUST_SERIAL_DEV_BUS_ABSTRACTIONS in Kconfig
- add mfd rust abstraction
- split core and led parts into their own driver. It should now be considered a
  MFD device.
- split led part of dt binding into its own file
- Link to v2: https://lore.kernel.org/r/20260308-synology_microp_initial-v2-0-9389963f31c5@posteo.de

Changes in v2:
- fix missing tabs in MAINTAINERS file
- remove word binding from patch subject
- add missing signed-off-by
- add missing help entry in Kconfig
- add missing spdx license headers
- remove no-check{,-cpu}-fan properties from the dt-bindings and replace
  them with the check_fan module parameter
- use patternProperties for leds in dt-bindings
- license dt-binding as GPL-2.0-only OR BSD-2-Clause
- move driver from staging tree into mfd tree and mark it as work in
  progress inside Kconfig
- only register alert and usb led if fwnode is present
- Link to v1: https://lore.kernel.org/r/20260306-synology_microp_initial-v1-0-fcffede6448c@posteo.de

---
Markus Probst (2):
      platform: Add initial synology microp driver
      dt-bindings: embedded-controller: Add synology microp devices

 .../synology,ds923p-microp.yaml                    |  92 +++++++
 MAINTAINERS                                        |   6 +
 drivers/platform/Kconfig                           |   2 +
 drivers/platform/Makefile                          |   1 +
 drivers/platform/synology_microp/Kconfig           |  13 +
 drivers/platform/synology_microp/Makefile          |   3 +
 drivers/platform/synology_microp/TODO              |   7 +
 drivers/platform/synology_microp/command.rs        |  55 ++++
 drivers/platform/synology_microp/led.rs            | 276 +++++++++++++++++++++
 drivers/platform/synology_microp/model.rs          |  49 ++++
 .../platform/synology_microp/synology_microp.rs    | 109 ++++++++
 11 files changed, 613 insertions(+)
---
base-commit: 0e5d0a0b5ca6ea4e391d6786266405c5871e0151
change-id: 20260306-synology_microp_initial-0f7dac7b7496
prerequisite-change-id: 20251217-rust_serdev-ee5481e9085c:v4
prerequisite-patch-id: 52b17274481cc770c257d8f95335293eca32a2c5
prerequisite-patch-id: eec47e5051640d08bcd34a9670b98804449cad52
prerequisite-patch-id: f24b68c71c3f69371e8ac0251efca0a023b31cc4
prerequisite-patch-id: d0686cf451ef899a06d468adfba51ccd84e6ff98
prerequisite-change-id: 20251114-rust_leds-a959f7c2f7f9:v13
prerequisite-patch-id: 818700f22dcb9676157c985f82762d7c607b861e
prerequisite-patch-id: b15ffa7d95d9260151bfb116b259c4473f721c82
prerequisite-patch-id: 8c47e0d107530f577a1be0b79f8ee791f95d3cbe



^ permalink raw reply


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