* Re: [PATCH v3 2/3] media: i2c: add os02g10 image sensor driver
From: Vladimir Zapolskiy @ 2026-06-12 10:12 UTC (permalink / raw)
To: Elgin Perumbilly, sakari.ailus, laurent.pinchart
Cc: Tarang Raval, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
Mehdi Djait, Sylvain Petinot, Benjamin Mugnier,
Bryan O'Donoghue, Himanshu Bhavani, Heimir Thor Sverrisson,
Jingjing Xiong, Svyatoslav Ryhel, linux-media, devicetree,
linux-kernel
In-Reply-To: <20260424092554.26130-3-elgin.perumbilly@siliconsignals.io>
On 4/24/26 12:25, Elgin Perumbilly wrote:
> Add a v4l2 subdevice driver for the Omnivision os02g10 sensor.
>
> The Omnivision os02g10 is a CMOS image sensor with an active array size of
> 1920 x 1080.
>
> The following features are supported:
> - Manual exposure an gain control support
> - vblank/hblank control support
> - vflip/hflip control support
> - Test pattern control support
> - Supported resolution: 1920 x 1080 @ 30fps (SBGGR10)
>
> Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
> Reviewed-by: Tarang Raval <tarang.raval@siliconsignals.io>
> ---
> MAINTAINERS | 1 +
> drivers/media/i2c/Kconfig | 10 +
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/os02g10.c | 949 ++++++++++++++++++++++++++++++++++++
> 4 files changed, 961 insertions(+)
> create mode 100644 drivers/media/i2c/os02g10.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8a0a55073c30..693e71b51926 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19449,6 +19449,7 @@ M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
> L: linux-media@vger.kernel.org
> S: Maintained
> F: Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
> +F: drivers/media/i2c/os02g10.c
>
> OMNIVISION OS05B10 SENSOR DRIVER
> M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 5eb1e0e0a87a..dd6e9562acf6 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -372,6 +372,16 @@ config VIDEO_OG0VE1B
> To compile this driver as a module, choose M here: the
> module will be called og0ve1b.
>
> +config VIDEO_OS02G10
> + tristate "OmniVision OS02G10 sensor support"
> + select V4L2_CCI_I2C
> + help
> + This is a Video4Linux2 sensor driver for Omnivision
> + OS02G10 camera sensor.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called os02g10.
> +
> config VIDEO_OS05B10
> tristate "OmniVision OS05B10 sensor support"
> select V4L2_CCI_I2C
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index a3a6396df3c4..a7554d2eb140 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -84,6 +84,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
> obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
> obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
> obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o
> +obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o
> obj-$(CONFIG_VIDEO_OS05B10) += os05b10.o
> obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
> obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
> diff --git a/drivers/media/i2c/os02g10.c b/drivers/media/i2c/os02g10.c
> new file mode 100644
> index 000000000000..fad2dd0ad7aa
> --- /dev/null
> +++ b/drivers/media/i2c/os02g10.c
> @@ -0,0 +1,949 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * V4L2 Support for the OS02G10
> + *
> + * Copyright (C) 2026 Silicon Signals Pvt. Ltd.
> + *
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bitops.h>
> +#include <linux/cleanup.h>
> +#include <linux/clk.h>
> +#include <linux/container_of.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/property.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/units.h>
> +#include <linux/types.h>
> +#include <linux/time.h>
> +#include <linux/regmap.h>
Please sort the list of includes in alphabetical order, also you
may consider to shrink the list by removing quite many inherited
includes.
> +
> +#include <media/v4l2-cci.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-mediabus.h>
> +
> +#define OS02G10_XCLK_FREQ (24 * HZ_PER_MHZ)
Please put this "24MHZ" information into the macro name, like it to be
OS02G10_XCLK_FREQ_24MHZ or so.
> +
> +/* Page 0 */
> +#define OS02G10_REG_CHIPID CCI_REG24(0x002)
> +#define OS02G10_CHIPID 0x560247
> +
> +#define OS02G10_REG_PLL_DIV_CTRL CCI_REG8(0x030)
> +#define OS02G10_REG_PLL_DCTL_BIAS_CTRL CCI_REG8(0x035)
> +#define OS02G10_REG_GATE_EN_CTRL CCI_REG8(0x038)
> +#define OS02G10_REG_DPLL_NC CCI_REG8(0x041)
> +#define OS02G10_REG_MP_PHASE_CTRL CCI_REG8(0x044)
> +
> +/* Page 1 */
> +#define OS02G10_REG_STREAM_CTRL CCI_REG8(0x1b1)
> +#define OS02G10_STREAM_CTRL_ON 0x03
> +#define OS02G10_STREAM_CTRL_OFF 0x00
> +
> +#define OS02G10_REG_FRAME_SYNC CCI_REG8(0x101)
> +
> +#define OS02G10_REG_FRAME_LENGTH CCI_REG16(0x10e)
> +#define OS02G10_FRAME_LENGTH_MAX 0xffff
> +#define OS02G10_REG_HBLANK CCI_REG16(0x109)
> +
> +#define OS02G10_REG_FRAME_TEST_CTRL CCI_REG8(0x10d)
> +#define OS02G10_FRAME_EXP_SEPERATE_EN BIT(4)
> +#define OS02G10_TEST_PATTERN_ENABLE BIT(0)
> +
> +#define OS02G10_REG_ULP_PWD_DUMMY_CTRL CCI_REG8(0x13c)
> +#define OS02G10_REG_DC_LEVEL_LIMIT_EN CCI_REG8(0x146)
> +#define OS02G10_REG_DC_LEVEL_LIMIT_L CCI_REG8(0x147)
> +#define OS02G10_REG_BLC_DATA_LIMIT_L CCI_REG8(0x148)
> +#define OS02G10_REG_DC_BLC_LIMIT_H CCI_REG8(0x149)
> +
> +#define OS02G10_REG_HS_LP_CTRL CCI_REG8(0x192)
> +#define OS02G10_REG_HS_LEVEL CCI_REG8(0x19d)
> +#define OS02G10_REG_HS_DRV CCI_REG8(0x19e)
> +
> +#define OS02G10_REG_GB_SUBOFFSET CCI_REG8(0x1f0)
> +#define OS02G10_REG_BLUE_SUBOFFSET CCI_REG8(0x1f1)
> +#define OS02G10_REG_RED_SUBOFFSET CCI_REG8(0x1f2)
> +#define OS02G10_REG_GR_SUBOFFSET CCI_REG8(0x1f3)
> +
> +#define OS02G10_REG_ABL_TRIGGER CCI_REG8(0x1fa)
> +#define OS02G10_REG_ABL CCI_REG8(0x1fb)
> +
> +#define OS02G10_REG_H_SIZE_MIPI CCI_REG16(0x18e)
> +#define OS02G10_REG_V_SIZE_MIPI CCI_REG16(0x190)
> +#define OS02G10_REG_MIPI_TX_SPEED_CTRL CCI_REG8(0x1a1)
> +
> +#define OS02G10_REG_LONG_EXPOSURE CCI_REG16(0x103)
> +#define OS02G10_EXPOSURE_MIN 4
> +#define OS02G10_EXPOSURE_STEP 1
> +#define OS02G10_EXPOSURE_MARGIN 9
> +
> +#define OS02G10_REG_ANALOG_GAIN CCI_REG8(0x124)
> +#define OS02G10_ANALOG_GAIN_MIN 0x10
> +#define OS02G10_ANALOG_GAIN_MAX 0xf8
> +#define OS02G10_ANALOG_GAIN_STEP 1
> +#define OS02G10_ANALOG_GAIN_DEFAULT 0x10
> +
> +#define OS02G10_REG_DIGITAL_GAIN_H CCI_REG8(0x137)
> +#define OS02G10_REG_DIGITAL_GAIN_L CCI_REG8(0x139)
> +#define OS02G10_DIGITAL_GAIN_MIN 0x40
> +#define OS02G10_DIGITAL_GAIN_MAX 0x800
> +#define OS02G10_DIGITAL_GAIN_STEP 64
> +#define OS02G10_DIGITAL_GAIN_DEFAULT 0x40
> +
> +#define OS02G10_REG_FLIP_MIRROR CCI_REG8(0x13f)
> +#define OS02G10_FLIP BIT(1)
> +#define OS02G10_MIRROR BIT(0)
Please sort the registers by address value, then it will make clear
which position to insert any new added macro definitions.
> +
> +/* Page 2 */
> +#define OS02G10_REG_V_START CCI_REG16(0x2a0)
> +#define OS02G10_REG_V_SIZE CCI_REG16(0x2a2)
> +#define OS02G10_REG_H_START CCI_REG16(0x2a4)
> +#define OS02G10_REG_H_SIZE CCI_REG16(0x2a6)
> +
> +#define OS02G10_REG_SIF_CTRL CCI_REG8(0x25e)
> +#define OS02G10_ORIENTATION_BAYER_FIX 0x32
The same applies here.
> +
> +#define OS02G10_LINK_FREQ_720MHZ (720 * HZ_PER_MHZ)
> +
> +/* OS02G10 native and active pixel array size */
> +static const struct v4l2_rect os02g10_native_area = {
> + .top = 0,
> + .left = 0,
> + .width = 1928,
> + .height = 1088,
> +};
> +
> +static const struct v4l2_rect os02g10_active_area = {
> + .top = 4,
> + .left = 4,
> + .width = 1920,
> + .height = 1080,
> +};
> +
> +static const char * const os02g10_supply_name[] = {
> + "avdd", /* Analog power */
> + "dovdd", /* Digital I/O power */
> + "dvdd", /* Digital core power */
> +};
> +
> +struct os02g10 {
> + struct device *dev;
> + struct regmap *cci;
> + struct v4l2_subdev sd;
> + struct media_pad pad;
> + struct clk *xclk;
> + struct gpio_desc *reset_gpio;
> + struct regulator_bulk_data supplies[ARRAY_SIZE(os02g10_supply_name)];
> +
> + /* V4L2 Controls */
> + struct v4l2_ctrl_handler handler;
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_ctrl *vblank;
> + struct v4l2_ctrl *exposure;
> + struct v4l2_ctrl *vflip;
> + struct v4l2_ctrl *hflip;
> +};
> +
> +struct os02g10_mode {
> + u32 width;
> + u32 height;
> + u32 vts_def;
> + u32 exp_def;
> + u32 x_start;
> + u32 y_start;
> +};
> +
> +static const struct cci_reg_sequence os02g10_common_regs[] = {
> + { OS02G10_REG_PLL_DIV_CTRL, 0x0a},
> + { OS02G10_REG_PLL_DCTL_BIAS_CTRL, 0x04},
> + { OS02G10_REG_GATE_EN_CTRL, 0x11},
> + { OS02G10_REG_DPLL_NC, 0x06},
> + { OS02G10_REG_MP_PHASE_CTRL, 0x20},
> + { CCI_REG8(0x119), 0x50},
> + { CCI_REG8(0x11a), 0x0c},
> + { CCI_REG8(0x11b), 0x0d},
> + { CCI_REG8(0x11c), 0x00},
> + { CCI_REG8(0x11d), 0x75},
> + { CCI_REG8(0x11e), 0x52},
> + { CCI_REG8(0x122), 0x14},
> + { CCI_REG8(0x125), 0x44},
> + { CCI_REG8(0x126), 0x0f},
> + { OS02G10_REG_ULP_PWD_DUMMY_CTRL, 0xca},
> + { CCI_REG8(0x13d), 0x4a},
> + { CCI_REG8(0x140), 0x0f},
> + { CCI_REG8(0x143), 0x38},
> + { OS02G10_REG_DC_LEVEL_LIMIT_EN, 0x01},
> + { OS02G10_REG_DC_LEVEL_LIMIT_L, 0x00},
> + { OS02G10_REG_DC_BLC_LIMIT_H, 0x32},
> + { CCI_REG8(0x150), 0x01},
> + { CCI_REG8(0x151), 0x28},
> + { CCI_REG8(0x152), 0x20},
> + { CCI_REG8(0x153), 0x03},
> + { CCI_REG8(0x157), 0x16},
> + { CCI_REG8(0x159), 0x01},
> + { CCI_REG8(0x15a), 0x01},
> + { CCI_REG8(0x15d), 0x04},
> + { CCI_REG8(0x16a), 0x04},
> + { CCI_REG8(0x16b), 0x03},
> + { CCI_REG8(0x16e), 0x28},
> + { CCI_REG8(0x171), 0xc2},
> + { CCI_REG8(0x172), 0x04},
> + { CCI_REG8(0x173), 0x38},
> + { CCI_REG8(0x174), 0x04},
> + { CCI_REG8(0x179), 0x00},
> + { CCI_REG8(0x17a), 0xb2},
> + { CCI_REG8(0x17b), 0x10},
> + { OS02G10_REG_HS_LP_CTRL, 0x02},
> + { OS02G10_REG_HS_LEVEL, 0x03},
> + { OS02G10_REG_HS_DRV, 0x55},
> + { CCI_REG8(0x1b8), 0x70},
> + { CCI_REG8(0x1b9), 0x70},
> + { CCI_REG8(0x1ba), 0x70},
> + { CCI_REG8(0x1bb), 0x70},
> + { CCI_REG8(0x1bc), 0x00},
> + { CCI_REG8(0x1c4), 0x6d},
> + { CCI_REG8(0x1c5), 0x6d},
> + { CCI_REG8(0x1c6), 0x6d},
> + { CCI_REG8(0x1c7), 0x6d},
> + { CCI_REG8(0x1cc), 0x11},
> + { CCI_REG8(0x1cd), 0xe0},
> + { CCI_REG8(0x1d0), 0x1b},
> + { CCI_REG8(0x1d2), 0x76},
> + { CCI_REG8(0x1d3), 0x68},
> + { CCI_REG8(0x1d4), 0x68},
> + { CCI_REG8(0x1d5), 0x73},
> + { CCI_REG8(0x1d6), 0x73},
> + { CCI_REG8(0x1e8), 0x55},
> + { OS02G10_REG_GB_SUBOFFSET, 0x40},
> + { OS02G10_REG_BLUE_SUBOFFSET, 0x40},
> + { OS02G10_REG_RED_SUBOFFSET, 0x40},
> + { OS02G10_REG_GR_SUBOFFSET, 0x40},
> + { OS02G10_REG_ABL_TRIGGER, 0x1c},
> + { OS02G10_REG_ABL, 0x33},
> + { CCI_REG8(0x1fc), 0x80},
> + { CCI_REG8(0x1fe), 0x80},
> + { CCI_REG8(0x303), 0x67},
> + { CCI_REG8(0x300), 0x59},
> + { CCI_REG8(0x304), 0x11},
> + { CCI_REG8(0x305), 0x04},
> + { CCI_REG8(0x306), 0x0c},
> + { CCI_REG8(0x307), 0x08},
> + { CCI_REG8(0x308), 0x08},
> + { CCI_REG8(0x309), 0x4f},
> + { CCI_REG8(0x30b), 0x08},
> + { CCI_REG8(0x30d), 0x26},
> + { CCI_REG8(0x30f), 0x00},
> + { CCI_REG8(0x234), 0xfe},
> + { OS02G10_REG_MIPI_TX_SPEED_CTRL, 0x05},
> +};
> +
> +static const struct os02g10_mode supported_modes[] = {
> + {
> + .width = 1920,
> + .height = 1080,
> + .vts_def = 1246,
> + .exp_def = 1100,
> + .x_start = 2,
> + .y_start = 6,
> + },
> +};
> +
> +static const s64 link_freq_menu_items[] = {
> + OS02G10_LINK_FREQ_720MHZ,
> +};
> +
> +static const char * const os02g10_test_pattern_menu[] = {
> + "Disabled",
> + "Colorbar",
> +};
> +
> +static inline struct os02g10 *to_os02g10(struct v4l2_subdev *sd)
> +{
> + return container_of_const(sd, struct os02g10, sd);
> +}
> +
> +static u32 os02g10_get_format_code(struct os02g10 *os02g10)
> +{
> + static const u32 codes[2][2] = {
> + { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, },
> + { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, },
> + };
> + u32 code = codes[os02g10->vflip->val][os02g10->hflip->val];
> +
> + return code;
> +}
> +
> +static int os02g10_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct os02g10 *os02g10 = container_of_const(ctrl->handler,
> + struct os02g10, handler);
> + struct v4l2_subdev_state *state;
> + struct v4l2_mbus_framefmt *fmt;
> + int ret = 0;
> +
> + state = v4l2_subdev_get_locked_active_state(&os02g10->sd);
> + fmt = v4l2_subdev_state_get_format(state, 0);
> +
> + if (ctrl->id == V4L2_CID_VBLANK) {
> + /* Honour the VBLANK limits when setting exposure */
> + s64 max = fmt->height + ctrl->val - OS02G10_EXPOSURE_MARGIN;
> +
> + ret = __v4l2_ctrl_modify_range(os02g10->exposure,
> + os02g10->exposure->minimum, max,
> + os02g10->exposure->step,
> + os02g10->exposure->default_value);
> + if (ret)
> + return ret;
> + }
> +
> + if (pm_runtime_get_if_active(os02g10->dev) == 0)
> + return 0;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_EXPOSURE:
> + cci_write(os02g10->cci, OS02G10_REG_LONG_EXPOSURE,
> + ctrl->val, &ret);
> + break;
> + case V4L2_CID_ANALOGUE_GAIN:
> + cci_write(os02g10->cci, OS02G10_REG_ANALOG_GAIN,
> + ctrl->val, &ret);
> + break;
> + case V4L2_CID_DIGITAL_GAIN:
> + cci_write(os02g10->cci, OS02G10_REG_DIGITAL_GAIN_L,
> + (ctrl->val & 0xff), &ret);
> + cci_write(os02g10->cci, OS02G10_REG_DIGITAL_GAIN_H,
> + ((ctrl->val >> 8) & 0x7), &ret);
> + break;
> + case V4L2_CID_VBLANK:
> + u64 vts = ctrl->val + fmt->height;
> +
> + cci_update_bits(os02g10->cci, OS02G10_REG_FRAME_TEST_CTRL,
> + OS02G10_FRAME_EXP_SEPERATE_EN,
> + OS02G10_FRAME_EXP_SEPERATE_EN, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_FRAME_LENGTH, vts, &ret);
> + break;
> + case V4L2_CID_HFLIP:
> + case V4L2_CID_VFLIP:
> + cci_write(os02g10->cci, OS02G10_REG_FLIP_MIRROR,
> + os02g10->hflip->val | os02g10->vflip->val << 1,
> + &ret);
> + cci_write(os02g10->cci, OS02G10_REG_SIF_CTRL,
> + OS02G10_ORIENTATION_BAYER_FIX, &ret);
> + break;
> + case V4L2_CID_TEST_PATTERN:
> + cci_update_bits(os02g10->cci,
> + OS02G10_REG_FRAME_TEST_CTRL,
> + OS02G10_TEST_PATTERN_ENABLE,
> + ctrl->val ? OS02G10_TEST_PATTERN_ENABLE : 0,
> + &ret);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> + cci_write(os02g10->cci, OS02G10_REG_FRAME_SYNC, 0x01, &ret);
> +
> + pm_runtime_put(os02g10->dev);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops os02g10_ctrl_ops = {
> + .s_ctrl = os02g10_set_ctrl,
> +};
> +
> +static int os02g10_init_controls(struct os02g10 *os02g10)
> +{
> + const struct os02g10_mode *mode = &supported_modes[0];
> + struct v4l2_fwnode_device_properties props;
> + u64 vblank_def, exp_max, pixel_rate;
> + struct v4l2_ctrl_handler *ctrl_hdlr;
> + int ret;
> +
> + ctrl_hdlr = &os02g10->handler;
> + v4l2_ctrl_handler_init(ctrl_hdlr, 11);
> +
> + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
> + pixel_rate = div_u64(OS02G10_LINK_FREQ_720MHZ * 2 * 2, 10);
Please define a macro representing the fixed number of data lanes,
it'll be used in os02g10_parse_endpoint() also.
> + v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops, V4L2_CID_PIXEL_RATE, 0,
> + pixel_rate, 1, pixel_rate);
> +
> + os02g10->link_freq =
> + v4l2_ctrl_new_int_menu(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_LINK_FREQ,
> + ARRAY_SIZE(link_freq_menu_items) - 1,
> + 0, link_freq_menu_items);
> + if (os02g10->link_freq)
> + os02g10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + vblank_def = mode->vts_def - mode->height;
> + os02g10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_VBLANK, vblank_def,
> + OS02G10_FRAME_LENGTH_MAX - mode->height,
> + 1, vblank_def);
> +
> + exp_max = mode->vts_def - OS02G10_EXPOSURE_MARGIN;
> + os02g10->exposure =
> + v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_EXPOSURE,
> + OS02G10_EXPOSURE_MIN, exp_max,
> + OS02G10_EXPOSURE_STEP, mode->exp_def);
> +
> + v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_ANALOGUE_GAIN, OS02G10_ANALOG_GAIN_MIN,
> + OS02G10_ANALOG_GAIN_MAX, OS02G10_ANALOG_GAIN_STEP,
> + OS02G10_ANALOG_GAIN_DEFAULT);
> +
> + v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_DIGITAL_GAIN, OS02G10_DIGITAL_GAIN_MIN,
> + OS02G10_DIGITAL_GAIN_MAX, OS02G10_DIGITAL_GAIN_STEP,
> + OS02G10_DIGITAL_GAIN_DEFAULT);
> +
> + os02g10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_HFLIP, 0, 1, 1, 0);
> + if (os02g10->hflip)
> + os02g10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> + os02g10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_VFLIP, 0, 1, 1, 0);
> + if (os02g10->vflip)
> + os02g10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &os02g10_ctrl_ops,
> + V4L2_CID_TEST_PATTERN,
> + ARRAY_SIZE(os02g10_test_pattern_menu) - 1,
> + 0, 0, os02g10_test_pattern_menu);
> +
> + ret = v4l2_fwnode_device_parse(os02g10->dev, &props);
> + if (ret)
> + goto err_handler_free;
> +
> + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr,
> + &os02g10_ctrl_ops, &props);
> + if (ret)
> + goto err_handler_free;
> +
> + os02g10->sd.ctrl_handler = ctrl_hdlr;
> +
> + return 0;
> +
> +err_handler_free:
> + v4l2_ctrl_handler_free(ctrl_hdlr);
> +
> + return ret;
> +}
> +
> +static int os02g10_set_framefmt(struct os02g10 *os02g10,
> + struct v4l2_subdev_state *state)
> +{
> + const struct v4l2_mbus_framefmt *format;
> + const struct os02g10_mode *mode;
> + int ret = 0;
> +
> + format = v4l2_subdev_state_get_format(state, 0);
> + mode = v4l2_find_nearest_size(supported_modes,
> + ARRAY_SIZE(supported_modes), width,
> + height, format->width, format->height);
> +
> + cci_write(os02g10->cci, OS02G10_REG_V_START, mode->y_start, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_V_SIZE, mode->height, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_V_SIZE_MIPI, mode->height, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_H_START, mode->x_start, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_H_SIZE, mode->width, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_H_SIZE_MIPI, mode->width, &ret);
> +
> + return ret;
Just "return 0" here, and remove the local variable.
> +}
> +
> +static int os02g10_enable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state, u32 pad,
> + u64 streams_mask)
> +{
> + struct os02g10 *os02g10 = to_os02g10(sd);
> + int ret;
> +
> + ret = pm_runtime_resume_and_get(os02g10->dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = cci_multi_reg_write(os02g10->cci, os02g10_common_regs,
> + ARRAY_SIZE(os02g10_common_regs), NULL);
> + if (ret) {
> + dev_err(os02g10->dev, "failed to write common registers\n");
> + goto err_rpm_put;
> + }
> +
> + ret = os02g10_set_framefmt(os02g10, state);
> + if (ret) {
> + dev_err(os02g10->dev, "failed to set frame foramt\n");
> + goto err_rpm_put;
> + }
> +
> + /* Apply customized values from user */
> + ret = __v4l2_ctrl_handler_setup(os02g10->sd.ctrl_handler);
> + if (ret)
> + goto err_rpm_put;
> +
> + ret = cci_write(os02g10->cci, OS02G10_REG_STREAM_CTRL,
> + OS02G10_STREAM_CTRL_ON, NULL);
> + if (ret)
> + goto err_rpm_put;
> +
> + /* vflip and hflip cannot change during streaming */
> + __v4l2_ctrl_grab(os02g10->vflip, true);
> + __v4l2_ctrl_grab(os02g10->hflip, true);
> +
> + return 0;
> +
> +err_rpm_put:
> + pm_runtime_put(os02g10->dev);
> + return ret;
> +}
> +
> +static int os02g10_disable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state, u32 pad,
> + u64 streams_mask)
> +{
> + struct os02g10 *os02g10 = to_os02g10(sd);
> + int ret;
> +
> + ret = cci_write(os02g10->cci, OS02G10_REG_STREAM_CTRL,
> + OS02G10_STREAM_CTRL_OFF, NULL);
> + if (ret)
> + dev_err(os02g10->dev, "Failed to stop stream\n");
> +
> + __v4l2_ctrl_grab(os02g10->vflip, false);
> + __v4l2_ctrl_grab(os02g10->hflip, false);
> +
> + pm_runtime_put(os02g10->dev);
> +
> + return ret;
> +}
> +
> +static int os02g10_get_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_selection *sel)
> +{
> + switch (sel->target) {
> + case V4L2_SEL_TGT_CROP_BOUNDS:
> + case V4L2_SEL_TGT_NATIVE_SIZE:
> + sel->r = os02g10_native_area;
> + return 0;
> + case V4L2_SEL_TGT_CROP:
> + case V4L2_SEL_TGT_CROP_DEFAULT:
> + sel->r = os02g10_active_area;
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int os02g10_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + struct os02g10 *os02g10 = to_os02g10(sd);
> +
> + if (code->index)
> + return -EINVAL;
> +
> + code->code = os02g10_get_format_code(os02g10);
> +
> + return 0;
> +}
> +
> +static int os02g10_enum_frame_size(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_frame_size_enum *fse)
> +{
> + struct os02g10 *os02g10 = to_os02g10(sd);
> +
> + if (fse->index >= ARRAY_SIZE(supported_modes))
> + return -EINVAL;
> +
> + if (fse->code != os02g10_get_format_code(os02g10))
> + return -EINVAL;
> +
> + fse->min_width = supported_modes[fse->index].width;
> + fse->max_width = fse->min_width;
> + fse->min_height = supported_modes[fse->index].height;
> + fse->max_height = fse->min_height;
> +
> + return 0;
> +}
> +
> +static int os02g10_set_pad_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct os02g10 *os02g10 = to_os02g10(sd);
> + struct v4l2_mbus_framefmt *format;
> + const struct os02g10_mode *mode;
> +
> + format = v4l2_subdev_state_get_format(sd_state, 0);
> +
> + mode = v4l2_find_nearest_size(supported_modes,
> + ARRAY_SIZE(supported_modes),
> + width, height,
> + fmt->format.width, fmt->format.height);
> +
> + fmt->format.code = os02g10_get_format_code(os02g10);
> + fmt->format.width = mode->width;
> + fmt->format.height = mode->height;
> + fmt->format.field = V4L2_FIELD_NONE;
> + fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
> +
> + *format = fmt->format;
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> + u32 vblank_def = mode->vts_def - mode->height;
> +
> + int ret = __v4l2_ctrl_modify_range(os02g10->vblank, vblank_def,
> + OS02G10_FRAME_LENGTH_MAX -
> + mode->height, 1, vblank_def);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int os02g10_init_state(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state)
> +{
> + struct os02g10 *os02g10 = to_os02g10(sd);
> + struct v4l2_subdev_format fmt = {
> + .which = V4L2_SUBDEV_FORMAT_TRY,
> + .format = {
> + .code = os02g10_get_format_code(os02g10),
I'm uncertain that the default zero values of vflip/hflip represent
the initial state of a sensor correctly, it requires runtime testing.
Anyway you may consider to drop it, its immediately rewritten by
os02g10_set_pad_format().
> + .width = supported_modes[0].width,
> + .height = supported_modes[0].height,
> + },
> + };
> +
> + os02g10_set_pad_format(sd, state, &fmt);
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops os02g10_video_ops = {
> + .s_stream = v4l2_subdev_s_stream_helper,
> +};
> +
> +static const struct v4l2_subdev_pad_ops os02g10_pad_ops = {
> + .enum_mbus_code = os02g10_enum_mbus_code,
> + .get_fmt = v4l2_subdev_get_fmt,
> + .set_fmt = os02g10_set_pad_format,
> + .get_selection = os02g10_get_selection,
> + .enum_frame_size = os02g10_enum_frame_size,
Please group two enum callbacks on the list.
> + .enable_streams = os02g10_enable_streams,
> + .disable_streams = os02g10_disable_streams,
> +};
> +
> +static const struct v4l2_subdev_ops os02g10_subdev_ops = {
> + .video = &os02g10_video_ops,
> + .pad = &os02g10_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops os02g10_internal_ops = {
> + .init_state = os02g10_init_state,
> +};
> +
> +static int os02g10_power_on(struct device *dev)
> +{
> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
> + struct os02g10 *os02g10 = to_os02g10(sd);
> + int ret;
> +
> + ret = regulator_bulk_enable(ARRAY_SIZE(os02g10_supply_name),
> + os02g10->supplies);
> + if (ret) {
> + dev_err(os02g10->dev, "failed to enable regulators\n");
> + return ret;
> + }
> +
> + /* T4: delay from DOVDD stable to MCLK on */
> + fsleep(5 * USEC_PER_MSEC);
> +
> + ret = clk_prepare_enable(os02g10->xclk);
> + if (ret) {
> + dev_err(os02g10->dev, "failed to enable clock\n");
> + goto err_regulator_off;
> + }
> +
> + /* T3: delay from DVDD stable to sensor power up stable */
> + fsleep(5 * USEC_PER_MSEC);
> +
> + gpiod_set_value_cansleep(os02g10->reset_gpio, 0);
> +
> + /* T5: delay from sensor power up stable to SCCB initialization */
> + fsleep(5 * USEC_PER_MSEC);
> +
> + return 0;
> +
> +err_regulator_off:
> + regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
> + return ret;
> +}
> +
> +static int os02g10_power_off(struct device *dev)
> +{
> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
> + struct os02g10 *os02g10 = to_os02g10(sd);
> +
> + clk_disable_unprepare(os02g10->xclk);
> + gpiod_set_value_cansleep(os02g10->reset_gpio, 1);
> + regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
> +
> + return 0;
> +}
> +
> +static int os02g10_identify_module(struct os02g10 *os02g10)
> +{
> + u64 chip_id;
> + int ret;
> +
> + ret = cci_read(os02g10->cci, OS02G10_REG_CHIPID, &chip_id, NULL);
> + if (ret)
> + return dev_err_probe(os02g10->dev, ret,
> + "failed to read chip id %x\n",
> + OS02G10_CHIPID);
> +
> + if (chip_id != OS02G10_CHIPID)
> + return dev_err_probe(os02g10->dev, -EIO,
> + "chip id mismatch: %x!=%llx\n",
> + OS02G10_CHIPID, chip_id);
> +
> + return 0;
> +}
> +
> +static int os02g10_parse_endpoint(struct os02g10 *os02g10)
> +{
> + struct v4l2_fwnode_endpoint bus_cfg = {
> + .bus_type = V4L2_MBUS_CSI2_DPHY,
> + };
> + unsigned long link_freq_bitmap;
> + struct fwnode_handle *ep;
> + int ret;
> +
> + ep = fwnode_graph_get_next_endpoint(dev_fwnode(os02g10->dev), NULL);
> + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
> + fwnode_handle_put(ep);
> + if (ret)
> + return ret;
> +
> + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
Macro for the number of data lanes is wanted.
> + ret = dev_err_probe(os02g10->dev, -EINVAL,
> + "only 2 data lanes are supported\n");
> + goto error_out;
> + }
> +
> + ret = v4l2_link_freq_to_bitmap(os02g10->dev, bus_cfg.link_frequencies,
> + bus_cfg.nr_of_link_frequencies,
> + link_freq_menu_items,
> + ARRAY_SIZE(link_freq_menu_items),
> + &link_freq_bitmap);
> + if (ret) {
> + ret = dev_err_probe(os02g10->dev, -EINVAL,
> + "only 720MHz frequency is available\n");
> + goto error_out;
> + }
> +
> +error_out:
> + v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> + return ret;
> +};
> +
> +static const struct regmap_range_cfg os02g10_ranges[] = {
> + {
> + .range_min = 0x0000,
> + .range_max = 0x03ff,
> + .selector_reg = 0xfd,
> + .selector_mask = 0x03,
> + .selector_shift = 0,
> + .window_start = 0x00,
> + .window_len = 0x100,
> + },
> +};
> +
> +static const struct regmap_config os02g10_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .reg_format_endian = REGMAP_ENDIAN_BIG,
> + .max_register = 0x3ff,
> + .ranges = os02g10_ranges,
> + .num_ranges = ARRAY_SIZE(os02g10_ranges),
> + .disable_locking = true,
> +};
> +
> +static int os02g10_probe(struct i2c_client *client)
> +{
> + struct os02g10 *os02g10;
> + unsigned int xclk_freq;
> + int ret;
> +
> + os02g10 = devm_kzalloc(&client->dev, sizeof(*os02g10), GFP_KERNEL);
> + if (!os02g10)
> + return -ENOMEM;
> +
> + os02g10->dev = &client->dev;
> +
> + v4l2_i2c_subdev_init(&os02g10->sd, client, &os02g10_subdev_ops);
> + os02g10->sd.internal_ops = &os02g10_internal_ops;
> +
> + /*
> + * This is not using devm_cci_regmap_init_i2c(), because the driver
> + * makes use of regmap's pagination feature. The chosen settings are
> + * compatible with the CCI helpers.
> + */
> + os02g10->cci = devm_regmap_init_i2c(client, &os02g10_regmap_config);
> + if (IS_ERR(os02g10->cci))
> + return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->cci),
> + "failed to initialize CCI\n");
> +
> + ret = os02g10_parse_endpoint(os02g10);
> + if (ret)
> + return dev_err_probe(os02g10->dev, ret,
> + "failed to parse endpoint configuration\n");
> +
> + /* Get system clock (xvclk) */
> + os02g10->xclk = devm_v4l2_sensor_clk_get(os02g10->dev, NULL);
> + if (IS_ERR(os02g10->xclk))
> + return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->xclk),
> + "failed to get xclk\n");
> +
> + xclk_freq = clk_get_rate(os02g10->xclk);
> + if (xclk_freq != OS02G10_XCLK_FREQ)
> + return dev_err_probe(os02g10->dev, -EINVAL,
> + "xclk frequency not supported: %u Hz\n",
> + xclk_freq);
> +
> + for (unsigned int i = 0; i < ARRAY_SIZE(os02g10_supply_name); i++)
> + os02g10->supplies[i].supply = os02g10_supply_name[i];
> +
> + ret = devm_regulator_bulk_get(os02g10->dev,
> + ARRAY_SIZE(os02g10_supply_name),
> + os02g10->supplies);
> + if (ret)
> + return dev_err_probe(os02g10->dev, ret,
> + "failed to get regulators\n");
> +
> + os02g10->reset_gpio = devm_gpiod_get_optional(os02g10->dev,
> + "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(os02g10->reset_gpio))
> + return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->reset_gpio),
> + "failed to get reset GPIO\n");
> +
> + ret = os02g10_power_on(os02g10->dev);
> + if (ret)
> + return ret;
> +
> + ret = os02g10_identify_module(os02g10);
> + if (ret)
> + goto error_power_off;
> +
> + ret = os02g10_init_controls(os02g10);
> + if (ret)
> + goto error_power_off;
> +
> + /* Initialize subdev */
> + os02g10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + os02g10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> + os02g10->pad.flags = MEDIA_PAD_FL_SOURCE;
> +
> + ret = media_entity_pads_init(&os02g10->sd.entity, 1, &os02g10->pad);
> + if (ret) {
> + dev_err_probe(os02g10->dev, ret, "failed to init entity pads\n");
> + goto error_handler_free;
> + }
> +
> + os02g10->sd.state_lock = os02g10->handler.lock;
> + ret = v4l2_subdev_init_finalize(&os02g10->sd);
> + if (ret) {
> + dev_err_probe(os02g10->dev, ret, "subdev init error\n");
> + goto error_media_entity;
> + }
> +
> + pm_runtime_set_active(os02g10->dev);
> + pm_runtime_enable(os02g10->dev);
> +
> + ret = v4l2_async_register_subdev_sensor(&os02g10->sd);
> + if (ret) {
> + dev_err_probe(os02g10->dev, ret,
> + "failed to register os02g10 sub-device\n");
> + goto error_subdev_cleanup;
> + }
> +
> + pm_runtime_idle(os02g10->dev);
> +
> + return 0;
> +
> +error_subdev_cleanup:
> + v4l2_subdev_cleanup(&os02g10->sd);
> + pm_runtime_disable(os02g10->dev);
> + pm_runtime_set_suspended(os02g10->dev);
> +
> +error_media_entity:
> + media_entity_cleanup(&os02g10->sd.entity);
> +
> +error_handler_free:
> + v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
> +
> +error_power_off:
> + os02g10_power_off(os02g10->dev);
> +
> + return ret;
> +}
> +
> +static void os02g10_remove(struct i2c_client *client)
> +{
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct os02g10 *os02g10 = to_os02g10(sd);
> +
> + v4l2_async_unregister_subdev(sd);
> + v4l2_subdev_cleanup(&os02g10->sd);
> + media_entity_cleanup(&sd->entity);
> + v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
> +
> + pm_runtime_disable(&client->dev);
> + if (!pm_runtime_status_suspended(&client->dev)) {
> + os02g10_power_off(&client->dev);
> + pm_runtime_set_suspended(&client->dev);
> + }
> +}
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(os02g10_pm_ops,
> + os02g10_power_off, os02g10_power_on, NULL);
> +
> +static const struct of_device_id os02g10_id[] = {
> + { .compatible = "ovti,os02g10" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, os02g10_id);
> +
> +static struct i2c_driver os02g10_driver = {
> + .driver = {
> + .name = "os02g10",
> + .pm = pm_ptr(&os02g10_pm_ops),
> + .of_match_table = os02g10_id,
> + },
> + .probe = os02g10_probe,
> + .remove = os02g10_remove,
> +};
> +module_i2c_driver(os02g10_driver);
> +
> +MODULE_DESCRIPTION("OS02G10 Camera Sensor Driver");
> +MODULE_AUTHOR("Tarang Raval <tarang.raval@siliconsignals.io>");
> +MODULE_AUTHOR("Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>");
> +MODULE_LICENSE("GPL");
In my opinion the driver looks good, nothing beyond cosmetic changes is needed.
--
Best wishes,
Vladimir
^ permalink raw reply
* Re: [PATCH v2 1/3] perf: marvell: Add MPAM partid filtering to CN10K TAD PMU
From: sashiko-bot @ 2026-06-12 10:14 UTC (permalink / raw)
To: Geetha sowjanya; +Cc: robh, conor+dt, devicetree, linux-perf-users
In-Reply-To: <20260612095746.19679-2-gakula@marvell.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Inconsistent masking of `event->attr.config` causes valid MPAM partition ID filters to be silently bypassed (or hardware programming to be skipped entirely) when upper bits are set.
--
commit f4f2ecc176ec6e4641448761b6600d2344554e81
Author: Tanmay Jagdale <tanmay@marvell.com>
perf: marvell: Add MPAM partid filtering to CN10K TAD PMU
The TAD PMU exposes counters that can be filtered by MPAM partition id
for a subset of allocation and hit events.
> diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c b/drivers/perf/marvell_cn10k_tad_pmu.c
> index 51ccb0befa05d..af706b890bf17 100644
> --- a/drivers/perf/marvell_cn10k_tad_pmu.c
> +++ b/drivers/perf/marvell_cn10k_tad_pmu.c
[ ... ]
> @@ -27,30 +35,92 @@ struct tad_region {
[ ... ]
> +static void tad_pmu_start_counter(struct tad_pmu *pmu,
> + struct perf_event *event)
> +{
> + const struct tad_pmu_data *pdata = pmu->pdata;
> + struct hw_perf_event *hwc = &event->hw;
> + u32 event_idx = event->attr.config;
[Severity: Medium]
Does this cause valid MPAM partition ID filters to be silently bypassed if
upper bits are set in event->attr.config?
In tad_pmu_event_init(), the event_idx is masked to 8 bits:
u32 event_idx = (u32)(event->attr.config & GENMASK(7, 0));
Since it is masked there, an event with upper bits set successfully passes
the validation checks. However, because it is unmasked here in
tad_pmu_start_counter(), it will fail the bounds checks below while the
lower 8 bits are still eventually programmed into the hardware counter:
> + u32 counter_idx = hwc->idx;
> + u64 partid_filter = 0;
> + u64 reg_val;
> + u64 cfg1 = event->attr.config1;
> + bool use_mpam = cfg1 & TAD_PARTID_FILTER_EN;
> + u32 partid = (u32)(cfg1 & GENMASK(8, 0));
> + int i;
> +
> + for (i = 0; i < pmu->region_cnt; i++)
> + writeq_relaxed(0, pmu->regions[i].base +
> + TAD_PFC(pdata->tad_pfc_offset, counter_idx));
> +
> + if (use_mpam && event_idx > 0x19 && event_idx < 0x21) {
> + partid_filter = TAD_PRF_MATCH_PARTID | TAD_PRF_PARTID_NS |
> + ((u64)partid << 11);
> + }
> +
> +
> + for (i = 0; i < pmu->region_cnt; i++) {
> + reg_val = event_idx & 0xFF;
> + reg_val |= partid_filter;
> + writeq_relaxed(reg_val, pmu->regions[i].base +
> + TAD_PRF(pdata->tad_prf_offset, counter_idx));
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612095746.19679-1-gakula@marvell.com?part=1
^ permalink raw reply
* Re: [PATCH v4 13/16] media: iris: Introduce buffer size calculations for AR50LT
From: sashiko-bot @ 2026-06-12 10:22 UTC (permalink / raw)
To: Dmitry Baryshkov; +Cc: robh, devicetree, conor+dt
In-Reply-To: <20260612-iris-ar50lt-v4-13-0abfb74d5b3c@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The AR50LT line buffer calculations ignore the `is_opb` parameter and fail to allocate the required VPSS line buffer size when split mode is enabled.
- [High] AR50LT H264 and HEVC bin buffer size calculations lack a minimum lower-bound threshold.
--
commit 3d321c841d8d8ce0ec81cd70b48ab049db102de4
Author: Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>
media: iris: Introduce buffer size calculations for AR50LT
Introduces AR50LT buffer size calculation for both encoder and
decoder. Reuse the buffer size calculation which are common, while
adding the AR50LT specific ones separately.
> diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
> index 4a39b8fef52b0..ca03d65705136 100644
> --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
> +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
> @@ -50,6 +50,32 @@ static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_p
> return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
> }
>
> +static u32 size_h264d_hw_bin_buffer_ar50lt(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
> +{
> + u32 size_yuv, size_bin_hdr, size_bin_res;
> +
> + size_yuv = ((frame_width * frame_height * 3) >> 1);
> + if (size_yuv <= 1920 * 1088 * 3 / 2) {
[Severity: High]
Does this calculation lack a minimum safe threshold for size_yuv?
The generic implementations clamp the base pixel area against
BIN_BUFFER_THRESHOLD, and the AR50LT VP9 implementation correctly applies a
lower bound. If a stream with a very small resolution is provided, could
this allocate a dangerously small bin buffer and lead to hardware memory
corruption?
[ ... ]
> @@ -111,6 +152,32 @@ static u32 hfi_buffer_bin_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_p
> return size_h265d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
> }
>
> +static u32 size_h265d_hw_bin_buffer_ar50lt(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
> +{
> + u32 size_yuv, size_bin_hdr, size_bin_res;
> +
> + size_yuv = ((frame_width * frame_height * 3) >> 1);
> + if (size_yuv <= ((BIN_BUFFER_THRESHOLD * 3) >> 1)) {
[Severity: High]
Similar to the H.264 case above, does this calculation also need a
lower-bound threshold applied to prevent hardware issues with extremely
small stream resolutions?
[ ... ]
> @@ -404,6 +584,29 @@ u32 hfi_buffer_line_h265d(u32 frame_width, u32 frame_height, bool is_opb, u32 nu
> return ALIGN((_size + vpss_lb_size), DMA_ALIGNMENT);
> }
>
> +static inline
> +u32 hfi_buffer_line_h265d_ar50lt(u32 frame_width, u32 frame_height, bool is_opb, u32 num_vpp_pipes)
> +{
> + u32 size;
> +
> + size = ALIGN(size_h265d_lb_fe_top_data_ar50lt(frame_width, frame_height), DMA_ALIGNMENT) +
> + ALIGN(size_h265d_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
> + ALIGN(size_h265d_lb_fe_left_ctrl(frame_width, frame_height),
> + DMA_ALIGNMENT) * num_vpp_pipes +
> + ALIGN(size_h265d_lb_se_left_ctrl_ar50lt(frame_width, frame_height),
> + DMA_ALIGNMENT) * num_vpp_pipes +
> + ALIGN(size_h265d_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
> + ALIGN(size_h265d_lb_pe_top_data_ar50lt(frame_width, frame_height), DMA_ALIGNMENT) +
> + ALIGN(size_h265d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) +
> + ALIGN(size_h265d_lb_vsp_left(frame_width, frame_height),
> + DMA_ALIGNMENT) * num_vpp_pipes +
> + ALIGN(size_h265d_lb_recon_dma_metadata_wr(frame_width, frame_height),
> + DMA_ALIGNMENT) * 4 +
> + ALIGN(size_h265d_qp(frame_width, frame_height), DMA_ALIGNMENT);
> +
> + return ALIGN(size, DMA_ALIGNMENT);
> +}
[Severity: High]
Does this function completely ignore the is_opb parameter?
In the generic hfi_buffer_line_h265d(), is_opb is used to add the VPSS line
buffer size via size_vpss_lb(). Could missing this buffer size lead to
out-of-bounds DMA memory writes when split mode is enabled and hardware
expects the additional allocation?
[ ... ]
> @@ -507,6 +734,13 @@ u32 hfi_buffer_line_vp9d(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_mi
> return _lb_size + vpss_lb_size + 4096;
> }
>
> +static inline
> +u32 hfi_buffer_line_vp9d_ar50lt(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_min,
> + bool is_opb, u32 num_vpp_pipes)
> +{
> + return hfi_ar50lt_vp9d_lb_size(frame_width, frame_height, num_vpp_pipes);
> +}
[Severity: High]
Similar to the HEVC case, the is_opb parameter is accepted here but unused.
Should size_vpss_lb() be factored in for VP9 line buffers as well when
split mode is active?
[ ... ]
> @@ -529,6 +763,25 @@ static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height,
> return ALIGN((size + vpss_lb_size), DMA_ALIGNMENT);
> }
>
> +static u32 hfi_buffer_line_h264d_ar50lt(u32 frame_width, u32 frame_height,
> + bool is_opb, u32 num_vpp_pipes)
> +{
> + u32 size;
> +
> + size = ALIGN(size_h264d_lb_fe_top_data_ar50lt(frame_width), DMA_ALIGNMENT) +
> + ALIGN(size_h264d_lb_fe_top_ctrl_ar50lt(frame_width), DMA_ALIGNMENT) +
> + ALIGN(size_h264d_lb_fe_left_ctrl(frame_height), DMA_ALIGNMENT) * num_vpp_pipes +
> + ALIGN(size_h264d_lb_se_top_ctrl_ar50lt(frame_width), DMA_ALIGNMENT) +
> + ALIGN(size_h264d_lb_se_left_ctrl_ar50lt(frame_height), DMA_ALIGNMENT) *
> + num_vpp_pipes +
> + ALIGN(size_h264d_lb_pe_top_data_ar50lt(frame_width), DMA_ALIGNMENT) +
> + ALIGN(size_h264d_lb_vsp_top(frame_width), DMA_ALIGNMENT) +
> + ALIGN(size_h264d_lb_recon_dma_metadata_wr(frame_height), DMA_ALIGNMENT) * 2 +
> + ALIGN(size_h264d_qp(frame_width, frame_height), DMA_ALIGNMENT);
> +
> + return ALIGN(size, DMA_ALIGNMENT);
> +}
[Severity: High]
Once again, is_opb is ignored in this AR50LT H.264 line buffer calculation.
Does the hardware still expect the VPSS line buffer allocation when split
mode is enabled here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-iris-ar50lt-v4-0-0abfb74d5b3c@oss.qualcomm.com?part=13
^ permalink raw reply
* Re: [PATCH v4 12/16] media: iris: implement support for the Agatti platform
From: sashiko-bot @ 2026-06-12 10:25 UTC (permalink / raw)
To: Dmitry Baryshkov; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260612-iris-ar50lt-v4-12-0abfb74d5b3c@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Calling a synchronous HFI command from the ISR response thread causes a deadlock.
--
commit 960bbe7059755e03232b477bd06562c01b126fdd
Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
media: iris: implement support for the Agatti platform
Port support for the AR50Lt video codec core (present for example on the
Agatti platform) to the Iris driver. Unlike more recent cores this
generation doesn't have the PIPE property (as it always has only one
pipe). Also, unlike newer platforms, buffer sizes are requested from the
firmware instead of being calculated by the driver.
> diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
> index fb6f1016415e2..4a39b8fef52b0 100644
> --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
> +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
> @@ -2194,6 +2194,19 @@ u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ
> return size;
> }
>
> +u32 iris_vpu_ar50lt_gen1_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
> +{
> + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
> + int ret;
> +
> + /* return 0 on error to let the driver cope */
> + ret = hfi_ops->session_get_property(inst, HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS);
[Severity: High]
Could this lead to a regression by waiting for a response in the response
handler thread?
When iris_vpu_ar50lt_gen1_buf_size() calls hfi_ops->session_get_property(),
it synchronously blocks until the firmware responds:
drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c:iris_hfi_gen1_session_get_property() {
...
return iris_wait_for_session_response(inst, false);
}
However, iris_vpu_ar50lt_gen1_buf_size() (via iris_get_buffer_size()) is
invoked from iris_hfi_gen1_read_changed_params() during event processing:
drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c:iris_hfi_gen1_read_changed_params() {
...
pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
...
}
This event processing runs inside the driver's response handler:
drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c:iris_hfi_gen1_response_handler() {
...
iris_hfi_gen1_handle_response(core, core->response_packet);
...
}
If the response handler thread blocks itself waiting for a response, how will
that response ever be dequeued and processed? It appears this will stall the
response polling loop until the wait times out.
> + if (ret)
> + return 0;
> +
> + return inst->buffers[buffer_type].size;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-iris-ar50lt-v4-0-0abfb74d5b3c@oss.qualcomm.com?part=12
^ permalink raw reply
* Re: [PATCH v2 2/3] perf: marvell: Add CN20K LLC-TAD PMU support
From: sashiko-bot @ 2026-06-12 10:26 UTC (permalink / raw)
To: Geetha sowjanya; +Cc: robh, devicetree, linux-perf-users, conor+dt
In-Reply-To: <20260612095746.19679-3-gakula@marvell.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Inconsistent masking of `event_idx` between validation and execution allows userspace to induce an aborted hardware programming state or bypass MPAM filters.
--
commit 13a14a3d137a1e1e8ddf65c82eb4ff187ff64ca5
Author: Geetha sowjanya <gakula@marvell.com>
perf: marvell: Add CN20K LLC-TAD PMU support
This commit adds support for the LLC Tag-and-Data (TAD) PMU on Marvell
CN20K SoCs. It introduces a new PMU profile, extended event maps, and
specific register base offsets.
> diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c b/drivers/perf/marvell_cn10k_tad_pmu.c
> index af706b890bf17..e43598a52859e 100644
> --- a/drivers/perf/marvell_cn10k_tad_pmu.c
> +++ b/drivers/perf/marvell_cn10k_tad_pmu.c
[ ... ]
> @@ -85,8 +89,15 @@ static void tad_pmu_start_counter(struct tad_pmu *pmu,
> if (use_mpam && event_idx > 0x19 && event_idx < 0x21) {
> partid_filter = TAD_PRF_MATCH_PARTID | TAD_PRF_PARTID_NS |
> ((u64)partid << 11);
> +
> + if (pdata->id == TAD_PMU_V3)
> + partid_filter = TAD_PRF_MATCH_PARTID | TAD_PRF_MATCH_MPAMNS |
> + ((u64)partid << 10);
> }
>
> + /* CN10K support events 0:24*/
> + if (pdata->id == TAD_PMU_V1 && event_idx >= 0x25)
> + return;
[Severity: Medium]
In tad_pmu_event_init(), the event configuration is validated using a masked
value:
tad_pmu_event_init() {
...
u32 event_idx = (u32)(event->attr.config & GENMASK(7, 0));
...
}
However, in tad_pmu_start_counter(), event_idx is assigned directly from the
unmasked event->attr.config.
If a user provides an event configuration with upper bits set (e.g., 0x124),
it will pass the masked initialization checks, but won't it fail the unmasked
bounds checks here?
For V1 hardware, an unmasked config like 0x124 would evaluate as >= 0x25 and
trigger this new early return after clearing the counter but before
programming it, leaving the hardware in an aborted state.
Similarly, for V3 hardware, wouldn't the unmasked event_idx fail the
MPAM filter condition (event_idx > 0x19 && event_idx < 0x21) earlier in this
function, causing the counter to incorrectly bypass the filter and record all
partition IDs?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612095746.19679-1-gakula@marvell.com?part=2
^ permalink raw reply
* Re: [PATCH v4 15/16] media: venus: skip QCM2290 if Iris driver is enabled
From: sashiko-bot @ 2026-06-12 10:33 UTC (permalink / raw)
To: Dmitry Baryshkov; +Cc: devicetree, conor+dt, robh
In-Reply-To: <20260612-iris-ar50lt-v4-15-0abfb74d5b3c@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-after-free of `hdev` in the IRQ handler due to incorrect teardown sequence.
--
commit a490235af4b563cc6644730f4c262f25f52e330a
Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
media: venus: skip QCM2290 if Iris driver is enabled
As the Iris driver now supports the QCM2290 hardware too, there is a
race between Venus and Iris drivers on binding to the corresponding
device. Follow the approach used by other platforms and skip QCM2290 in
the Venus driver if Iris is enabled.
> diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
> index 243e342b0ae75..3c88594eb1d00 100644
> --- a/drivers/media/platform/qcom/venus/core.c
> +++ b/drivers/media/platform/qcom/venus/core.c
[ ... ]
> @@ -1169,13 +1168,16 @@ static const struct venus_resources qcm2290_res = {
> .enc_nodename = "video-encoder",
> .min_fw = &min_fw,
> };
> +#endif
>
> static const struct of_device_id venus_dt_match[] = {
> { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
> { .compatible = "qcom,msm8939-venus", .data = &msm8939_res, },
> { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
> { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, },
> +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS))
> { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, },
> +#endif
> { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, },
> { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, },
> { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
[Severity: High]
This is a pre-existing issue, but could there be a use-after-free risk during
teardown in venus_hfi_destroy()?
In drivers/media/platform/qcom/venus/hfi_venus.c:venus_hfi_destroy(), hdev is
freed and its lock is destroyed before the IRQ is disabled:
void venus_hfi_destroy(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
core->priv = NULL;
venus_interface_queues_release(hdev);
mutex_destroy(&hdev->lock);
kfree(hdev);
disable_irq(core->irq);
core->ops = NULL;
}
If a threaded interrupt (venus_isr_thread) is already running or scheduled
concurrently, could it pass the initial if (!hdev) check before core->priv
is set to NULL, and then access the freed hdev structure?
drivers/media/platform/qcom/venus/hfi_venus.c:venus_isr_thread()
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
...
if (!hdev)
return IRQ_NONE;
...
pkt = hdev->pkt_buf;
while (!venus_iface_msgq_read(hdev, pkt)) {
...
}
Because disable_irq() waits for currently executing handlers to complete,
should it be called before freeing the structures the handler depends on?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-iris-ar50lt-v4-0-0abfb74d5b3c@oss.qualcomm.com?part=15
^ permalink raw reply
* Re: [PATCH v3 3/3] media: i2c: os02g10: implement crop handling with set_selection
From: Vladimir Zapolskiy @ 2026-06-12 10:34 UTC (permalink / raw)
To: Elgin Perumbilly, sakari.ailus, laurent.pinchart
Cc: Tarang Raval, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
Mehdi Djait, Sylvain Petinot, Benjamin Mugnier,
Bryan O'Donoghue, Heimir Thor Sverrisson, Hardevsinh Palaniya,
linux-media, devicetree, linux-kernel
In-Reply-To: <20260424092554.26130-4-elgin.perumbilly@siliconsignals.io>
On 4/24/26 12:25, Elgin Perumbilly wrote:
> From: Tarang Raval <tarang.raval@siliconsignals.io>
>
> Add crop support to os02g10 by implementing .set_selection() and
> storing the crop rectangle in subdev state.
>
> Initialize the default crop to the active area, make set_fmt() use the
> current crop, and update the output format when the crop size changes.
> Also program the sensor window from the active crop/format state instead
> of using the fixed supported_modes entry.
>
> This allows userspace to configure the sensor crop window explicitly.
>
> Signed-off-by: Tarang Raval <tarang.raval@siliconsignals.io>
> Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
> ---
> drivers/media/i2c/os02g10.c | 166 ++++++++++++++++++++++--------------
> 1 file changed, 103 insertions(+), 63 deletions(-)
>
> diff --git a/drivers/media/i2c/os02g10.c b/drivers/media/i2c/os02g10.c
> index fad2dd0ad7aa..9bf8f5d1caea 100644
> --- a/drivers/media/i2c/os02g10.c
> +++ b/drivers/media/i2c/os02g10.c
> @@ -112,6 +112,11 @@
> #define OS02G10_ORIENTATION_BAYER_FIX 0x32
>
> #define OS02G10_LINK_FREQ_720MHZ (720 * HZ_PER_MHZ)
> +#define OS02G10_WINDOW_WIDTH_MIN 2
> +#define OS02G10_WINDOW_HEIGHT_MIN 2
Add a blank line before the new group of macro.
> +#define OS02G10_VBLANK_DEF 166
This one is computable, and it can be dropped.
> +#define OS02G10_VBLANK_MIN 25
This macro shall be added to the group of OS02G10_REG_FRAME_LENGTH
register, and it should be included into the previous change.
> +#define OS02G10_EXPOSURE_DEF 1100
This macro shall be added to the group of OS02G10_REG_LONG_EXPOSURE
register, and it should be included into the previous change.
>
> /* OS02G10 native and active pixel array size */
> static const struct v4l2_rect os02g10_native_area = {
> @@ -152,15 +157,6 @@ struct os02g10 {
> struct v4l2_ctrl *hflip;
> };
>
> -struct os02g10_mode {
> - u32 width;
> - u32 height;
> - u32 vts_def;
> - u32 exp_def;
> - u32 x_start;
> - u32 y_start;
> -};
> -
> static const struct cci_reg_sequence os02g10_common_regs[] = {
> { OS02G10_REG_PLL_DIV_CTRL, 0x0a},
> { OS02G10_REG_PLL_DCTL_BIAS_CTRL, 0x04},
> @@ -245,17 +241,6 @@ static const struct cci_reg_sequence os02g10_common_regs[] = {
> { OS02G10_REG_MIPI_TX_SPEED_CTRL, 0x05},
> };
>
> -static const struct os02g10_mode supported_modes[] = {
> - {
> - .width = 1920,
> - .height = 1080,
> - .vts_def = 1246,
> - .exp_def = 1100,
> - .x_start = 2,
> - .y_start = 6,
> - },
> -};
> -
> static const s64 link_freq_menu_items[] = {
> OS02G10_LINK_FREQ_720MHZ,
> };
> @@ -295,11 +280,12 @@ static int os02g10_set_ctrl(struct v4l2_ctrl *ctrl)
> if (ctrl->id == V4L2_CID_VBLANK) {
> /* Honour the VBLANK limits when setting exposure */
> s64 max = fmt->height + ctrl->val - OS02G10_EXPOSURE_MARGIN;
> + s64 def = (max < OS02G10_EXPOSURE_DEF) ? max
> + : OS02G10_EXPOSURE_DEF;
>
> ret = __v4l2_ctrl_modify_range(os02g10->exposure,
> os02g10->exposure->minimum, max,
> - os02g10->exposure->step,
> - os02g10->exposure->default_value);
> + os02g10->exposure->step, def);
> if (ret)
> return ret;
> }
> @@ -362,10 +348,9 @@ static const struct v4l2_ctrl_ops os02g10_ctrl_ops = {
>
> static int os02g10_init_controls(struct os02g10 *os02g10)
> {
> - const struct os02g10_mode *mode = &supported_modes[0];
> struct v4l2_fwnode_device_properties props;
> - u64 vblank_def, exp_max, pixel_rate;
> struct v4l2_ctrl_handler *ctrl_hdlr;
> + u64 exp_max, pixel_rate;
> int ret;
>
> ctrl_hdlr = &os02g10->handler;
> @@ -384,18 +369,19 @@ static int os02g10_init_controls(struct os02g10 *os02g10)
> if (os02g10->link_freq)
> os02g10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>
> - vblank_def = mode->vts_def - mode->height;
> os02g10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> - V4L2_CID_VBLANK, vblank_def,
> - OS02G10_FRAME_LENGTH_MAX - mode->height,
> - 1, vblank_def);
> + V4L2_CID_VBLANK, OS02G10_VBLANK_MIN,
> + OS02G10_FRAME_LENGTH_MAX -
> + os02g10_active_area.height,
> + 1, OS02G10_VBLANK_DEF);
>
> - exp_max = mode->vts_def - OS02G10_EXPOSURE_MARGIN;
> + exp_max = OS02G10_VBLANK_DEF + os02g10_active_area.height
> + - OS02G10_EXPOSURE_MARGIN;
> os02g10->exposure =
> v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> V4L2_CID_EXPOSURE,
> OS02G10_EXPOSURE_MIN, exp_max,
> - OS02G10_EXPOSURE_STEP, mode->exp_def);
> + OS02G10_EXPOSURE_STEP, OS02G10_EXPOSURE_DEF);
>
> v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
> V4L2_CID_ANALOGUE_GAIN, OS02G10_ANALOG_GAIN_MIN,
> @@ -445,20 +431,18 @@ static int os02g10_set_framefmt(struct os02g10 *os02g10,
> struct v4l2_subdev_state *state)
> {
> const struct v4l2_mbus_framefmt *format;
> - const struct os02g10_mode *mode;
> + const struct v4l2_rect *crop;
> int ret = 0;
>
> + crop = v4l2_subdev_state_get_crop(state, 0);
> format = v4l2_subdev_state_get_format(state, 0);
> - mode = v4l2_find_nearest_size(supported_modes,
> - ARRAY_SIZE(supported_modes), width,
> - height, format->width, format->height);
>
> - cci_write(os02g10->cci, OS02G10_REG_V_START, mode->y_start, &ret);
> - cci_write(os02g10->cci, OS02G10_REG_V_SIZE, mode->height, &ret);
> - cci_write(os02g10->cci, OS02G10_REG_V_SIZE_MIPI, mode->height, &ret);
> - cci_write(os02g10->cci, OS02G10_REG_H_START, mode->x_start, &ret);
> - cci_write(os02g10->cci, OS02G10_REG_H_SIZE, mode->width, &ret);
> - cci_write(os02g10->cci, OS02G10_REG_H_SIZE_MIPI, mode->width, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_V_START, crop->top, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_V_SIZE, crop->height, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_V_SIZE_MIPI, format->height, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_H_START, crop->left, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_H_SIZE, crop->width, &ret);
> + cci_write(os02g10->cci, OS02G10_REG_H_SIZE_MIPI, format->width, &ret);
>
> return ret;
> }
> @@ -528,16 +512,67 @@ static int os02g10_disable_streams(struct v4l2_subdev *sd,
> return ret;
> }
>
> +static int os02g10_set_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *sd_state,
> + struct v4l2_subdev_selection *sel)
> +{
> + struct v4l2_mbus_framefmt *format;
> + struct v4l2_rect *crop;
> + struct v4l2_rect rect;
> +
> + if (sel->target != V4L2_SEL_TGT_CROP)
> + return -EINVAL;
> +
> + rect.left = clamp_t(unsigned int, ALIGN(sel->r.left, 2),
> + os02g10_active_area.left,
> + os02g10_active_area.left +
> + os02g10_active_area.width -
> + OS02G10_WINDOW_WIDTH_MIN);
> + rect.top = clamp_t(unsigned int, ALIGN(sel->r.top, 2),
> + os02g10_active_area.top,
> + os02g10_active_area.top +
> + os02g10_active_area.height -
> + OS02G10_WINDOW_HEIGHT_MIN);
> + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
> + OS02G10_WINDOW_WIDTH_MIN,
> + os02g10_active_area.width);
> + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
> + OS02G10_WINDOW_HEIGHT_MIN,
> + os02g10_active_area.height);
> +
> + rect.width = min_t(unsigned int, rect.width,
> + os02g10_active_area.left +
> + os02g10_active_area.width - rect.left);
> + rect.height = min_t(unsigned int, rect.height,
> + os02g10_active_area.top +
> + os02g10_active_area.height - rect.top);
> +
> + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad);
> +
> + if (rect.width != crop->width || rect.height != crop->height) {
> + format = v4l2_subdev_state_get_format(sd_state, sel->pad);
> + format->width = rect.width;
> + format->height = rect.height;
> + }
> +
> + *crop = rect;
> + sel->r = rect;
> +
> + return 0;
> +}
> +
> static int os02g10_get_selection(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *sd_state,
> struct v4l2_subdev_selection *sel)
> {
> switch (sel->target) {
> + case V4L2_SEL_TGT_CROP:
> + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad);
> + return 0;
> case V4L2_SEL_TGT_CROP_BOUNDS:
> case V4L2_SEL_TGT_NATIVE_SIZE:
> sel->r = os02g10_native_area;
> return 0;
> - case V4L2_SEL_TGT_CROP:
> case V4L2_SEL_TGT_CROP_DEFAULT:
> sel->r = os02g10_active_area;
> return 0;
> @@ -566,16 +601,16 @@ static int os02g10_enum_frame_size(struct v4l2_subdev *sd,
> {
> struct os02g10 *os02g10 = to_os02g10(sd);
>
> - if (fse->index >= ARRAY_SIZE(supported_modes))
> + if (fse->index)
> return -EINVAL;
>
> if (fse->code != os02g10_get_format_code(os02g10))
> return -EINVAL;
>
> - fse->min_width = supported_modes[fse->index].width;
> - fse->max_width = fse->min_width;
> - fse->min_height = supported_modes[fse->index].height;
> - fse->max_height = fse->min_height;
> + fse->min_width = OS02G10_WINDOW_WIDTH_MIN;
> + fse->max_width = os02g10_active_area.width;
> + fse->min_height = OS02G10_WINDOW_HEIGHT_MIN;
> + fse->max_height = os02g10_active_area.height;
>
> return 0;
> }
> @@ -586,18 +621,14 @@ static int os02g10_set_pad_format(struct v4l2_subdev *sd,
> {
> struct os02g10 *os02g10 = to_os02g10(sd);
> struct v4l2_mbus_framefmt *format;
> - const struct os02g10_mode *mode;
> + struct v4l2_rect *crop;
>
> + crop = v4l2_subdev_state_get_crop(sd_state, 0);
> format = v4l2_subdev_state_get_format(sd_state, 0);
>
> - mode = v4l2_find_nearest_size(supported_modes,
> - ARRAY_SIZE(supported_modes),
> - width, height,
> - fmt->format.width, fmt->format.height);
> -
> fmt->format.code = os02g10_get_format_code(os02g10);
> - fmt->format.width = mode->width;
> - fmt->format.height = mode->height;
> + fmt->format.width = crop->width;
> + fmt->format.height = crop->height;
> fmt->format.field = V4L2_FIELD_NONE;
> fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> @@ -606,11 +637,19 @@ static int os02g10_set_pad_format(struct v4l2_subdev *sd,
> *format = fmt->format;
>
> if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> - u32 vblank_def = mode->vts_def - mode->height;
> + int ret, vblank;
>
> - int ret = __v4l2_ctrl_modify_range(os02g10->vblank, vblank_def,
> - OS02G10_FRAME_LENGTH_MAX -
> - mode->height, 1, vblank_def);
> + ret = __v4l2_ctrl_modify_range(os02g10->vblank, OS02G10_VBLANK_MIN,
> + OS02G10_FRAME_LENGTH_MAX -
> + fmt->format.height, 1,
> + OS02G10_VBLANK_DEF);
> + if (ret)
> + return ret;
> +
> + /* Set VBLANK to maintain 30 fps for the selected format. */
> + vblank = os02g10_active_area.height - fmt->format.height
> + + OS02G10_VBLANK_DEF;
> + ret = __v4l2_ctrl_s_ctrl(os02g10->vblank, vblank);
> if (ret)
> return ret;
> }
> @@ -626,14 +665,14 @@ static int os02g10_init_state(struct v4l2_subdev *sd,
> .which = V4L2_SUBDEV_FORMAT_TRY,
> .format = {
> .code = os02g10_get_format_code(os02g10),
> - .width = supported_modes[0].width,
> - .height = supported_modes[0].height,
> + .width = os02g10_active_area.width,
> + .height = os02g10_active_area.height,
> },
> };
> + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
> + *crop = os02g10_active_area;
>
> - os02g10_set_pad_format(sd, state, &fmt);
> -
> - return 0;
> + return os02g10_set_pad_format(sd, state, &fmt);
> }
>
> static const struct v4l2_subdev_video_ops os02g10_video_ops = {
> @@ -645,6 +684,7 @@ static const struct v4l2_subdev_pad_ops os02g10_pad_ops = {
> .get_fmt = v4l2_subdev_get_fmt,
> .set_fmt = os02g10_set_pad_format,
> .get_selection = os02g10_get_selection,
> + .set_selection = os02g10_set_selection,
> .enum_frame_size = os02g10_enum_frame_size,
> .enable_streams = os02g10_enable_streams,
> .disable_streams = os02g10_disable_streams,
I understand that this change is written by another person, and likely
it is not squashed with the previous one to preserve authorship, however
it significantly rewrites the change already found in the series.
I don't see information about the maximum supported frame height/width
or default VTS setting etc. anymore, for me it's hard to say, if
this kind of information can be dropped with no consequences in runtime.
Probably this 3/3 change will break a quick inclusion of the sensor
driver, you may consider to exlcude it from the series now, and publish
it afterwards.
--
Best wishes,
Vladimir
^ permalink raw reply
* Re: [PATCH v4 14/16] media: iris: add Gen2 firmware support on the Agatti platform
From: sashiko-bot @ 2026-06-12 10:39 UTC (permalink / raw)
To: Dmitry Baryshkov; +Cc: devicetree, conor+dt, robh
In-Reply-To: <20260612-iris-ar50lt-v4-14-0abfb74d5b3c@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `qcm2290_data` struct initialization assigns values to non-existent members `.firmware_desc_gen1` and `.firmware_desc_gen2`, causing a build failure. Additionally, the required `.firmware_desc` member is left uninitialized, leading to a NULL pointer dereference during probe.
--
commit 4f1d2454209dec3530526e228c0b4e78b6170ab3
Author: Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>
media: iris: add Gen2 firmware support on the Agatti platform
Agatti platform is using HFI Gen1 firmware, which is considered to be
legacy firmware branch. Follow the example of the SC7280 platform and
extend the driver with supporting both HFI Gen1 and Gen2 firmwares for
this platform. Like HFI Gen1 this firmware doesn't have PIPE property
(but unlike Gen1 buffer sizes are calculated on the driver side).
> diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu_ar50lt.c b/drivers/media/platform/qcom/iris/iris_platform_vpu_ar50lt.c
> index 393256f39112b..d9de7dcb59e3a 100644
> --- a/drivers/media/platform/qcom/iris/iris_platform_vpu_ar50lt.c
> +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu_ar50lt.c
[ ... ]
> @@ -78,7 +84,8 @@ static struct platform_inst_caps platform_inst_cap_ar50lt = {
> };
>
> const struct iris_platform_data qcm2290_data = {
> - .firmware_desc_gen1 = &iris_vpu_ar50lt_p1_gen1_s6_desc,
> + .firmware_desc_gen1 = &iris_vpu_ar50lt_p1_gen1_desc,
> + .firmware_desc_gen2 = &iris_vpu_ar50lt_p1_gen2_s6_desc,
[Severity: Low]
Does this cause a build failure? struct iris_platform_data does not
currently contain a firmware_desc_gen2 field (nor a firmware_desc_gen1
field).
Looking at drivers/media/platform/qcom/iris/iris_platform_common.h:
struct iris_platform_data {
/*
* XXX: replace with gen1 / gen2 pointers once we have platforms
* supporting both firmware kinds.
*/
const struct iris_firmware_desc *firmware_desc;
If these fields were added to bypass the build failure, wouldn't this
initialization leave the required firmware_desc field uninitialized?
This could result in a null pointer dereference during probe:
drivers/media/platform/qcom/iris/iris_probe.c:iris_probe()
core->iris_firmware_desc = core->iris_platform_data->firmware_desc;
core->iris_firmware_data = core->iris_firmware_desc->firmware_data;
> .vpu_ops = &iris_vpu_ar50lt_ops,
> .icc_tbl = iris_icc_info_ar50lt,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-iris-ar50lt-v4-0-0abfb74d5b3c@oss.qualcomm.com?part=14
^ permalink raw reply
* RE: [PATCH v5 2/4] arm64: dts: renesas: rzg3e-smarc-som: Add Versa3 clock generator
From: John Madieu @ 2026-06-12 10:40 UTC (permalink / raw)
To: Biju Das, geert+renesas@glider.be, magnus.damm, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org
Cc: linux-renesas-soc@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, john.madieu@gmail.com
In-Reply-To: <TY3PR01MB113467A63C78C21DDA14220BE861A2@TY3PR01MB11346.jpnprd01.prod.outlook.com>
Hi Biju,
Thank you for your review.
> -----Original Message-----
> From: Biju Das <biju.das.jz@bp.renesas.com>
> Sent: mercredi 10 juin 2026 10:14
> To: John Madieu <john.madieu.xa@bp.renesas.com>; geert+renesas@glider.be;
> magnus.damm <magnus.damm@gmail.com>; robh@kernel.org; krzk+dt@kernel.org;
> conor+dt@kernel.org
> Subject: RE: [PATCH v5 2/4] arm64: dts: renesas: rzg3e-smarc-som: Add
> Versa3 clock generator
>
> Hi John,
>
> > -----Original Message-----
> > From: John Madieu <john.madieu.xa@bp.renesas.com>
> > Sent: 10 June 2026 08:47
> > Subject: [PATCH v5 2/4] arm64: dts: renesas: rzg3e-smarc-som: Add
> > Versa3 clock generator
> >
> > Add the Renesas 5P35023 (Versa3) programmable clock generator on the
> > I2C2 bus along with its 24MHz input clock (x2 oscillator) to feed the
> audio subsystem.
> >
> > The Versa3 provides the following clock outputs:
> > - Output 0: 24MHz (reference)
> > - Output 1: 12.288MHz (audio, 48kHz family)
> > - Output 2: 11.2896MHz (audio, 44.1kHz family)
> > - Output 3: 12.288MHz (audio)
> > - Output 4: 25MHz (DIFF1, Ethernet)
> >
> > These clocks are required for the audio codec and the Ethernet
> > controller found on the RZ/G3E SMARC EVK.
> >
> > Output 5 (DIFF2) is left out, as it is not connected on this board.
> >
> > Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
> > ---
> >
> > Changes:
> >
> > v5:
> > - Drop Versa3 output 5 (DIFF2), as it is not connected.
> > - Document output 4 (DIFF1) in the commit message; it is needed for
> > Ethernet.
> >
> > v4: No changes
> > v3: No changes
> > v2: No changes
> >
> > .../boot/dts/renesas/rzg3e-smarc-som.dtsi | 20 +++++++++++++++++++
> > 1 file changed, 20 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi
> > b/arch/arm64/boot/dts/renesas/rzg3e-
> > smarc-som.dtsi
> > index 15c22dbf0ad3..e951609d39e9 100644
> > --- a/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi
> > +++ b/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi
> > @@ -78,6 +78,12 @@ reg_vdd0p8v_others: regulator-vdd0p8v-others {
> > regulator-always-on;
> > };
> >
> > + x2: x2-clock {
> > + compatible = "fixed-clock";
> > + #clock-cells = <0>;
> > + clock-frequency = <24000000>;
> > + };
> > +
> > /* 32.768kHz crystal */
> > x3: x3-clock {
> > compatible = "fixed-clock";
> > @@ -137,6 +143,20 @@ raa215300: pmic@12 {
> >
> > interrupts-extended = <&pinctrl RZG3E_GPIO(S, 1)
> IRQ_TYPE_EDGE_FALLING>;
> > };
> > +
> > + versa3: clock-generator@68 {
> > + compatible = "renesas,5p35023";
> > + reg = <0x68>;
> > + #clock-cells = <1>;
> > + clocks = <&x2>;
> > +
> > + assigned-clocks = <&versa3 0>, <&versa3 1>,
> > + <&versa3 2>, <&versa3 3>,
> > + <&versa3 4>, <&versa3 5>;
>
> No need to assign clock rate for unconnected output. So drop <&versa3 5>;
Noted.
>
> > + assigned-clock-rates = <24000000>, <12288000>,
> > + <11289600>, <12288000>,
> > + <25000000>, <25000000>;
>
>
> Drop <25000000>;
My bad. I missed that, these removal should have been handled in v5.
I'll address in v6.
Regards,
John
^ permalink raw reply
* Re: [PATCH V11 3/9] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
From: Jonathan Cameron @ 2026-06-12 10:42 UTC (permalink / raw)
To: Chris Morgan
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
In-Reply-To: <PH0PR19MB997338CE11110E16E7FB668E3BA51B2@PH0PR19MB997338.namprd19.prod.outlook.com>
On Thu, 11 Jun 2026 11:31:09 -0500
Chris Morgan <macromorgan@hotmail.com> wrote:
> On Thu, Jun 11, 2026 at 05:20:22PM +0100, Jonathan Cameron wrote:
> > On Thu, 11 Jun 2026 09:32:38 -0500
> > Chris Morgan <macromorgan@hotmail.com> wrote:
> >
> > > On Thu, Jun 11, 2026 at 12:09:43PM +0100, Jonathan Cameron wrote:
> > > > On Wed, 10 Jun 2026 12:54:47 -0500
> > > > Chris Morgan <macroalpha82@gmail.com> wrote:
> > > >
> > > > > From: Chris Morgan <macromorgan@hotmail.com>
> > > > >
> > > > > Add the core component of a new inv_icm42607 driver. This includes
> > > > > a few setup functions and the full register definition in the
> > > > > header file.
> > > > >
> > > > > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> > > > Hi Chris,
> > > >
> > > > A couple more minor things from a fresh read.
> > > >
> > > > Thanks
> > > >
> > > > Jonathan
> > > >
> > > > > ---
> > > > > drivers/iio/imu/inv_icm42607/inv_icm42607.h | 334 ++++++++++++++++++
> > > > > .../iio/imu/inv_icm42607/inv_icm42607_core.c | 197 +++++++++++
> > > > > 2 files changed, 531 insertions(+)
> > > > > create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
> > > > > create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> > > > >
> > > > > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> > > > > new file mode 100644
> > > > > index 000000000000..716fc0f1c3fd
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> > > >
> > > > > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> > > > > new file mode 100644
> > > > > index 000000000000..334264120b42
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> > > >
> > > > > +
> > > > > +static int inv_icm42607_setup(struct inv_icm42607_state *st,
> > > > > + inv_icm42607_bus_setup inv_icm42607_bus_setup)
> > > > > +{
> > > > > + const struct device *dev = regmap_get_device(st->map);
> > > > > + unsigned int val;
> > > > > + int ret;
> > > > > +
> > > > > + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > + /* Warn, but don't fail. */
> > > > > + if (val != st->hw->whoami)
> > > > > + dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n",
> > > > > + val, st->hw->whoami, st->hw->name);
> > > > > +
> > > > > + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> > > > > + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > + fsleep(INV_ICM42607_RESET_TIME_MS * USEC_PER_MSEC);
> > > > > +
> > > > > + /*
> > > > > + * No polling interval specified in datasheet, so use reset time as
> > > > > + * polling interval and 10x reset time as timeout period.
> > > > > + */
> > > > > + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
> > > > > + val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
> > > > > + (INV_ICM42607_RESET_TIME_MS * USEC_PER_MSEC),
> > > > > + (INV_ICM42607_RESET_TIME_MS * USEC_PER_MSEC * 10));
> > > > > + if (ret)
> > > > > + return dev_err_probe(dev, ret,
> > > > > + "reset error, reset done bit not set\n");
> > > > > +
> > > > > + /* Sync the regcache again after a reset. */
> > > > > + regcache_mark_dirty(st->map);
> > > > > + ret = regcache_sync(st->map);
> > > >
> > > > Sashiko raised the point that you don't have a writeable register list for the regmap
> > > > and so potentially we at very least write a bunch of stuff that isn't needed.
> > > > I doubt it's actually a problem or you would have seen it, but nice little optimization
> > > > to reduce what is written.
> > >
> > > I will add such a list, I'd put it in patch 4 unless you think I should
> > > merge patch 3 and 4 to make the driver compileable at the first commit
> > > of code (will push the number of lines to the ~850/900 range though).
> > >
> > I'm fine with them split but don't mind if Andy prefers them combined.
> > His comment on adding a COMPILE_TEST option would allow that to be
> > built, but it is a bit odd to do it just for that purpose.
> >
> > Jonathan
> >
>
> I'm going to add the i2c stuff to this series, and then do the SPI
> on the next patch. That should add only a little bit to this one but
> still ensure the very first (code) commit has something to compile.
> I'm going to avoid doing COMPILE_TEST unless collectively you all
> think I should.
>
> I'm still racing to try and get this one done before the merge
> window opens up, but I'm pretty sure it's already too late at
> this point. I'll refrain from submitting updates once the window opens
> though.
Unfortunately you are correct. IIO in practice closes around 10 days before
the merge window to give time to both expose new things to testing in linux-next
and to allow time for Greg KH to merge my pull request into char-misc (which
is the path IIO takes to upstream)
Sorry about that, and whilst no need to rush as a result, we also don't
stop merging IIO stuff in the merge window, it just sits in the testing
branch until I can rebase that on rc1 and then push it out for linux-next
to pick up.
Thanks,
Jonathan
>
> Thank you,
> Chris
>
^ permalink raw reply
* Re: [PATCH v2 2/5] dt-bindings: display: bridge: Document Renesas R-Car V4H DSC bindings
From: Tomi Valkeinen @ 2026-06-12 10:43 UTC (permalink / raw)
To: Conor Dooley, Laurent Pinchart
Cc: Geert Uytterhoeven, Michael Turquette, Stephen Boyd,
Andrzej Hajda, Neil Armstrong, Robert Foss, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Marek Vasut,
Laurent Pinchart, Kieran Bingham, Philipp Zabel,
linux-renesas-soc, linux-clk, linux-kernel, dri-devel, devicetree
In-Reply-To: <20260515-fraying-trickle-7511a2eeaf44@spud>
Hi,
On 15/05/2026 20:32, Conor Dooley wrote:
> On Fri, May 15, 2026 at 10:56:15AM +0300, Tomi Valkeinen wrote:
>> From: Marek Vasut <marek.vasut+renesas@mailbox.org>
>>
>> The Renesas DSC Display Stream Compression is a bridge embedded in the
>> Renesas R-Car V4H SoC. The bridge performs VESA DSC encoding of up to
>> 8k or 400 Mpixel/s .
>>
>> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
>> [tomi.valkeinen: fix the example]
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
>> ---
>> .../bindings/display/bridge/renesas,dsc.yaml | 96 ++++++++++++++++++++++
>> 1 file changed, 96 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,dsc.yaml b/Documentation/devicetree/bindings/display/bridge/renesas,dsc.yaml
>> new file mode 100644
>> index 000000000000..2918d592732b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/bridge/renesas,dsc.yaml
>
> Filename matching the compatible please.
All the other Documentation/devicetree/bindings/display/bridge/renesas,*
files follow the same style, where the file name is in a generic format,
but the actual compat strings are per SoC (and no generic compat string).
So is the fix here to just add "renesas,dsc" compat string (and perhaps
later fix the other binding files too)? I'd rather not name this file
"renesas,r8a779g0-dsc.yaml".
>
> pw-bot: changes-requested
>
>> @@ -0,0 +1,96 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/display/bridge/renesas,dsc.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Renesas R-Car DSC Display Stream Compression
>> +
>> +maintainers:
>> + - Marek Vasut <marek.vasut+renesas@mailbox.org>
>> +
>> +description: |
>> + This binding describes the VESA DSC Display Stream Compression encoder
>> + embedded in the Renesas R-Car V4H SoC. The encoder supports all DSC1.1
>> + encoding mechanisms, configurable bits-per-pixel, resolution up to 8k.
>> +
>> +properties:
>> + compatible:
>> + const: renesas,r8a779g0-dsc
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + clocks:
>> + maxItems: 1
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + power-domains:
>> + maxItems: 1
>> +
>> + resets:
>> + maxItems: 1
>> +
>> + ports:
>> + $ref: /schemas/graph.yaml#/properties/ports
>> +
>> + properties:
>> + port@0:
>> + $ref: /schemas/graph.yaml#/properties/port
>> + description: R-Car DU input port
>> +
>> + port@1:
>> + $ref: /schemas/graph.yaml#/properties/port
>> + description: R-Car DSI output port
>> +
>> + required:
>> + - port@0
>> + - port@1
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - clocks
>> + - interrupts
>> + - power-domains
>> + - resets
>> + - ports
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/clock/r8a779g0-cpg-mssr.h>
>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>> + #include <dt-bindings/power/r8a779g0-sysc.h>
>> +
>> + dsc@feb8d000 {
>
> Speaking of fixing the example, should this not be "decoder" or
> "bridge"?
Indeed, bridge or encoder. Both seem to be used in the existing bindings.
Tomi
> Not a big deal though.
> With the changed filename
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
>
>
> Cheers,
> Conor.
>
>> + compatible = "renesas,r8a779g0-dsc";
>> + reg = <0xfeb8d000 0x400>;
>> + interrupts = <GIC_SPI 559 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&cpg CPG_MOD 2819>;
>> + power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
>> + resets = <&cpg 2819>;
>> +
>> + ports {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + port@0 {
>> + reg = <0>;
>> + dsc_in_dsi1: endpoint {
>> + remote-endpoint = <&du_out_dsi1>;
>> + };
>> + };
>> +
>> + port@1 {
>> + reg = <1>;
>> + dsc_out_dsi1: endpoint {
>> + remote-endpoint = <&dsi1_in>;
>> + };
>> + };
>> + };
>> + };
>> +...
>>
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH 2/2] arm64: dts: rockchip: Fix devices suspend freeze on RK3568/RK3566
From: Diederik de Haas @ 2026-06-12 10:46 UTC (permalink / raw)
To: Jonas Karlman, Heiko Stuebner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Geis, Michael Riesch
Cc: Diederik de Haas, Greg Kroah-Hartman, devicetree, linux-rockchip,
linux-usb, linux-arm-kernel, linux-kernel
In-Reply-To: <20260609154124.445182-3-jonas@kwiboo.se>
Hi Jonas,
On Tue Jun 9, 2026 at 5:41 PM CEST, Jonas Karlman wrote:
> The EHCI/OHCI controller depends on clk_usbphy1_480m being enabled, or
> the system may freeze when registers are accessed, i.e. during suspend
> in ohci_suspend().
>
> Add the missing clk_usbphy1_480m clocks reference to EHCI/OHCI
> controllers to ensure the clock is enabled when ECHI/OHCI registers are
> accessed to prevent a system freeze.
>
> Fixes suspend pm_test issue with EHCI/OHCI devices due to the missing
> clk_usbphy1_480m reference and makes following pm_test modes work:
>
> echo N > /sys/module/printk/parameters/console_suspend
>
> echo devices > /sys/power/pm_test
> echo platform > /sys/power/pm_test
> echo processors > /sys/power/pm_test
> echo core > /sys/power/pm_test
>
> echo mem > /sys/power/state
I did quite a few of ``echo mem > /sys/power/state`` while switching between
the various pm_test options on my PineTab2.
On my 7.1-rc7 kernel without this patch that resulted at some point in endless
'rk_iommu' errors and the only way out of that was a hard power down via the
power button.
On my 7.1-rc7 kernel with this patch, I have not been able to trigger such an
issue. Over multiple boots.
So feel free to include my
Tested-by: Diederik de Haas <diederik@cknow-tech.com>
FTR: I'm still able to trigger resume issues, so more is needed to make my
original tests [1] succeed as well. But I always suspected there were several
issues at play including the display pipeline.
It looks like this patch fixes at least one of them.
Thanks :-)
Cheers,
Diederik
[1] https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/31265
> Fixes: 91c4c3e06a25 ("arm64: dts: rockchip: add usb2 nodes to rk3568 device tree")
> Fixes: 78f7186095db ("arm64: dts: rockchip: rename and sort the rk356x usb2 phy handles")
> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
> ---
> arch/arm64/boot/dts/rockchip/rk356x-base.dtsi | 16 ++++++++--------
> 1 file changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
> index a5832895bd39..c930a6fd6ea0 100644
> --- a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
> @@ -321,44 +321,44 @@ its: msi-controller@fd440000 {
> };
>
> usb_host0_ehci: usb@fd800000 {
> - compatible = "generic-ehci";
> + compatible = "rockchip,rk3568-ehci", "generic-ehci";
> reg = <0x0 0xfd800000 0x0 0x40000>;
> interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&cru HCLK_USB2HOST0>, <&cru HCLK_USB2HOST0_ARB>,
> - <&cru PCLK_USB>;
> + <&cru PCLK_USB>, <&usb2phy1>;
> phys = <&usb2phy1_otg>;
> phy-names = "usb";
> status = "disabled";
> };
>
> usb_host0_ohci: usb@fd840000 {
> - compatible = "generic-ohci";
> + compatible = "rockchip,rk3568-ohci", "generic-ohci";
> reg = <0x0 0xfd840000 0x0 0x40000>;
> interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&cru HCLK_USB2HOST0>, <&cru HCLK_USB2HOST0_ARB>,
> - <&cru PCLK_USB>;
> + <&cru PCLK_USB>, <&usb2phy1>;
> phys = <&usb2phy1_otg>;
> phy-names = "usb";
> status = "disabled";
> };
>
> usb_host1_ehci: usb@fd880000 {
> - compatible = "generic-ehci";
> + compatible = "rockchip,rk3568-ehci", "generic-ehci";
> reg = <0x0 0xfd880000 0x0 0x40000>;
> interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&cru HCLK_USB2HOST1>, <&cru HCLK_USB2HOST1_ARB>,
> - <&cru PCLK_USB>;
> + <&cru PCLK_USB>, <&usb2phy1>;
> phys = <&usb2phy1_host>;
> phy-names = "usb";
> status = "disabled";
> };
>
> usb_host1_ohci: usb@fd8c0000 {
> - compatible = "generic-ohci";
> + compatible = "rockchip,rk3568-ohci", "generic-ohci";
> reg = <0x0 0xfd8c0000 0x0 0x40000>;
> interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&cru HCLK_USB2HOST1>, <&cru HCLK_USB2HOST1_ARB>,
> - <&cru PCLK_USB>;
> + <&cru PCLK_USB>, <&usb2phy1>;
> phys = <&usb2phy1_host>;
> phy-names = "usb";
> status = "disabled";
^ permalink raw reply
* RE: [PATCH 1/2] dt-bindings: connector: pcie-m2-e: Add 3.3Vaux supply support
From: Sherry Sun @ 2026-06-12 10:52 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Krzysztof Kozlowski, sashiko-reviews@lists.linux.dev,
manivannan.sadhasivam@oss.qualcomm.com, linux-pci@vger.kernel.org,
robh@kernel.org, Frank.Li@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org, imx@lists.linux.dev, Hongxing Zhu
In-Reply-To: <6ouch5ybqk7gws3yrvc2sylq67nioqva6klc7o7wcgbpobs2lu@ioscmsg6ufyx>
> On Thu, Jun 11, 2026 at 03:59:28AM +0000, Sherry Sun wrote:
> > > On Wed, Jun 10, 2026 at 10:13:00AM +0000, Sherry Sun wrote:
> > > > > On Wed, Jun 10, 2026 at 08:40:54AM +0000, Sherry Sun wrote:
> > > > > > > On Tue, Jun 09, 2026 at 03:44:08AM +0000,
> > > > > > > sashiko-bot@kernel.org
> > > wrote:
> > > > > > > > Thank you for your contribution! Sashiko AI review found 1
> > > > > > > > potential
> > > > > > > issue(s) to consider:
> > > > > > > > - [Medium] The `vpcie3v3aux-supply` property describes a
> > > > > > > > non-existent
> > > > > > > hardware feature on the M.2 Key E connector to work around a
> > > > > > > software policy.
> > > > > > >
> > > > > > > Feels valid. Describe which pin on M2 connector are you
> representing.
> > > > > > >
> > > > > >
> > > > > > Refer to PCI Express M.2 Specification r5.1 sec3.1.1 Power
> > > > > > Sources and Grounds.
> > > > > >
> > > > > > PCI Express M.2 Socket 1 utilizes a 3.3 V power source. The
> > > > > > voltage source, 3.3 V, is expected to be available during the
> > > > > > system’s stand-by/suspend state to support wake event
> > > > > > processing on the communications card.
> > > > > >
> > > > > > But the current vpcie3v3-supply may be gated off during system
> > > suspend.
> > > > > > So I tried to add vpcie3v3aux-supply to let this 3.3 V power
> > > > > > source always on for PCIe M.2 Key E connector. That means
> > > > > > vpcie3v3aux-supply and vpcie3v3-supply actually refer to the
> > > > > > same 3.3 V
> > > power source.
> > > > > >
> > > > > > @Mani, do you think this is reasonable? Or do you have any
> > > > > > other better solutions? Thanks!
> > > > > >
> > > > >
> > > > > There is no Vaux defined in the M.2 spec. So you cannot define
> > > > > that supply in the binding. You can define the custom Vaux
> > > > > supply as a fixed regulator in DT and mark it always on so that
> > > > > it is keeps supplying
> > > 3.3v to the card.
> > > > >
> > > >
> > > > Hi Mani, thanks for the suggestion, but adding an always on
> > > > regulator may cause board power waste, current solution ensures
> > > > the 3.3v regulator is only enabled when M.2 connector needs to work .
> > > >
> > >
> > > What do you mean by 'need to work'? For getting the M.2 card to
> > > work, you already have 3.3v supply. If you want the board to be
> > > always ON, then you need to supply 3.3Vaux, which should be always ON
> too.
> > >
> > > But who is turning off vpcie3v3-supply? M.2 connector driver or the
> > > platform?
> > >
> >
> > Hi Mani,
> > I'm not sure if I understand your question correctly, actually it's
> > the platform driver that calls the M.2 connector driver to turn off vpcie3v3-
> supply.
> > Take i.MX pci device as an example,
> > pm_suspend() -> imx_pcie_suspend_noirq() -> imx_pcie_host_exit() ->
> > pci_pwrctrl_power_off_devices() -> pwrseq_power_off() ->
> > pwrseq_unit_disable() -> pwrseq_pcie_m2_vregs_disable()
> >
> > Taking back to M.2 Specification:
> > PCI Express M.2 Socket 1 utilizes a 3.3 V power source. The voltage
> > source, 3.3 V, is expected to be available during the system’s
> > stand-by/suspend state to support wake event processing on the
> > communications card.
> >
> > Per my understanding of the above, the M.2 Spec actually suggest the
> > 3.3 V power source should not be turned off.
> > Should we simply enable the 3.3 V power source in
> > pwrseq_pcie_m2_probe() and remove the
> pwrseq_pcie_m2_vregs_unit_data?
> >
>
> Just don't call pci_pwrctrl_power_off_devices() from the suspend path.
>
Hi Mani, do you mean use dw_pcie_rp::skip_pwrctrl_off flag to avoid powering
off devices during suspend and also not power on the devices in the init path?
Best Regards
Sherry
^ permalink raw reply
* [PATCH 0/2] Sort kodiak pinctrl nodes & add camera mclk definitions
From: Luca Weiss @ 2026-06-12 10:55 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: ~postmarketos/upstreaming, phone-devel, linux-arm-msm, devicetree,
linux-kernel, Luca Weiss
First patch is just cleanup with no functional change, the second patch
adds some camera mclk pinctrl definitions for preparation of adding more
cameras on different kodiak devices.
Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
---
Luca Weiss (2):
arm64: dts: qcom: kodiak: Sort pinctrl subnodes by pins
arm64: dts: qcom: kodiak: Add camera mclk pinctrl definitions
arch/arm64/boot/dts/qcom/kodiak.dtsi | 1413 +++++++++++++++++-----------------
1 file changed, 724 insertions(+), 689 deletions(-)
---
base-commit: ec039126b7fac4e3af35ebccaa7c6f9b6875ba81
change-id: 20260612-kodiak-cam-mclk-a7e87d5b1585
Best regards,
--
Luca Weiss <luca.weiss@fairphone.com>
^ permalink raw reply
* [PATCH 2/2] arm64: dts: qcom: kodiak: Add camera mclk pinctrl definitions
From: Luca Weiss @ 2026-06-12 10:55 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: ~postmarketos/upstreaming, phone-devel, linux-arm-msm, devicetree,
linux-kernel, Luca Weiss
In-Reply-To: <20260612-kodiak-cam-mclk-v1-0-fd294ff003a2@fairphone.com>
On different boards the cameras are connected to different pins with the
cam_mclk function. Add the definitions so that mclk0-5 are covered.
Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
---
arch/arm64/boot/dts/qcom/kodiak.dtsi | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
index 62daef726d32..903937947811 100644
--- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
+++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
@@ -6505,6 +6505,27 @@ qup_uart15_rx: qup-uart15-rx-state {
function = "qup17";
};
+ cam_mclk0_default: cam-mclk0-default-state {
+ pins = "gpio64";
+ function = "cam_mclk";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ cam_mclk1_default: cam-mclk1-default-state {
+ pins = "gpio65";
+ function = "cam_mclk";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
+ cam_mclk2_default: cam-mclk2-default-state {
+ pins = "gpio66";
+ function = "cam_mclk";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
cam_mclk3_default: cam-mclk3-default-state {
pins = "gpio67";
function = "cam_mclk";
@@ -6512,6 +6533,13 @@ cam_mclk3_default: cam-mclk3-default-state {
bias-disable;
};
+ cam_mclk4_default: cam-mclk4-default-state {
+ pins = "gpio68";
+ function = "cam_mclk";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
cci0_default: cci0-default-state {
pins = "gpio69", "gpio70";
function = "cci_i2c";
@@ -6578,6 +6606,13 @@ pcie0_clkreq_n: pcie0-clkreq-n-state {
function = "pcie0_clkreqn";
};
+ cam_mclk5_default: cam-mclk5-default-state {
+ pins = "gpio93";
+ function = "cam_mclk";
+ drive-strength = <2>;
+ bias-disable;
+ };
+
mi2s0_mclk: mi2s0-mclk-state {
pins = "gpio96";
function = "pri_mi2s";
--
2.54.0
^ permalink raw reply related
* [PATCH 1/2] arm64: dts: qcom: kodiak: Sort pinctrl subnodes by pins
From: Luca Weiss @ 2026-06-12 10:55 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: ~postmarketos/upstreaming, phone-devel, linux-arm-msm, devicetree,
linux-kernel, Luca Weiss
In-Reply-To: <20260612-kodiak-cam-mclk-v1-0-fd294ff003a2@fairphone.com>
As documented in the "Devicetree Sources (DTS) Coding Style" document,
pinctrl subnodes should be sorted by the pins property. Do this once for
kodiak.dtsi so that future additions can be added at the right places.
No functional change intended, verified with dtx_diff.
Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
---
arch/arm64/boot/dts/qcom/kodiak.dtsi | 1382 +++++++++++++++++-----------------
1 file changed, 691 insertions(+), 691 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
index fa540d8c2615..62daef726d32 100644
--- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
+++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
@@ -5835,6 +5835,676 @@ tlmm: pinctrl@f100000 {
gpio-ranges = <&tlmm 0 0 175>;
wakeup-parent = <&pdc>;
+ qup_i2c0_data_clk: qup-i2c0-data-clk-state {
+ pins = "gpio0", "gpio1";
+ function = "qup00";
+ };
+
+ qup_spi0_data_clk: qup-spi0-data-clk-state {
+ pins = "gpio0", "gpio1", "gpio2";
+ function = "qup00";
+ };
+
+ qup_uart0_cts: qup-uart0-cts-state {
+ pins = "gpio0";
+ function = "qup00";
+ };
+
+ qup_uart0_rts: qup-uart0-rts-state {
+ pins = "gpio1";
+ function = "qup00";
+ };
+
+ qup_uart0_tx: qup-uart0-tx-state {
+ pins = "gpio2";
+ function = "qup00";
+ };
+
+ qup_spi0_cs: qup-spi0-cs-state {
+ pins = "gpio3";
+ function = "qup00";
+ };
+
+ qup_spi0_cs_gpio: qup-spi0-cs-gpio-state {
+ pins = "gpio3";
+ function = "gpio";
+ };
+
+ qup_uart0_rx: qup-uart0-rx-state {
+ pins = "gpio3";
+ function = "qup00";
+ };
+
+ qup_i2c1_data_clk: qup-i2c1-data-clk-state {
+ pins = "gpio4", "gpio5";
+ function = "qup01";
+ };
+
+ qup_spi1_data_clk: qup-spi1-data-clk-state {
+ pins = "gpio4", "gpio5", "gpio6";
+ function = "qup01";
+ };
+
+ qup_uart1_cts: qup-uart1-cts-state {
+ pins = "gpio4";
+ function = "qup01";
+ };
+
+ qup_uart1_rts: qup-uart1-rts-state {
+ pins = "gpio5";
+ function = "qup01";
+ };
+
+ qup_uart1_tx: qup-uart1-tx-state {
+ pins = "gpio6";
+ function = "qup01";
+ };
+
+ qup_spi1_cs: qup-spi1-cs-state {
+ pins = "gpio7";
+ function = "qup01";
+ };
+
+ qup_spi1_cs_gpio: qup-spi1-cs-gpio-state {
+ pins = "gpio7";
+ function = "gpio";
+ };
+
+ qup_uart1_rx: qup-uart1-rx-state {
+ pins = "gpio7";
+ function = "qup01";
+ };
+
+ qup_i2c2_data_clk: qup-i2c2-data-clk-state {
+ pins = "gpio8", "gpio9";
+ function = "qup02";
+ };
+
+ qup_spi2_data_clk: qup-spi2-data-clk-state {
+ pins = "gpio8", "gpio9", "gpio10";
+ function = "qup02";
+ };
+
+ qup_uart2_cts: qup-uart2-cts-state {
+ pins = "gpio8";
+ function = "qup02";
+ };
+
+ qup_uart2_rts: qup-uart2-rts-state {
+ pins = "gpio9";
+ function = "qup02";
+ };
+
+ qup_uart2_tx: qup-uart2-tx-state {
+ pins = "gpio10";
+ function = "qup02";
+ };
+
+ qup_spi2_cs: qup-spi2-cs-state {
+ pins = "gpio11";
+ function = "qup02";
+ };
+
+ qup_spi2_cs_gpio: qup-spi2-cs-gpio-state {
+ pins = "gpio11";
+ function = "gpio";
+ };
+
+ qup_uart2_rx: qup-uart2-rx-state {
+ pins = "gpio11";
+ function = "qup02";
+ };
+
+ qspi_data0: qspi-data0-state {
+ pins = "gpio12";
+ function = "qspi_data";
+ };
+
+ qup_i2c3_data_clk: qup-i2c3-data-clk-state {
+ pins = "gpio12", "gpio13";
+ function = "qup03";
+ };
+
+ qup_spi3_data_clk: qup-spi3-data-clk-state {
+ pins = "gpio12", "gpio13", "gpio14";
+ function = "qup03";
+ };
+
+ qup_uart3_cts: qup-uart3-cts-state {
+ pins = "gpio12";
+ function = "qup03";
+ };
+
+ qspi_data1: qspi-data1-state {
+ pins = "gpio13";
+ function = "qspi_data";
+ };
+
+ qup_uart3_rts: qup-uart3-rts-state {
+ pins = "gpio13";
+ function = "qup03";
+ };
+
+ qspi_clk: qspi-clk-state {
+ pins = "gpio14";
+ function = "qspi_clk";
+ };
+
+ qup_uart3_tx: qup-uart3-tx-state {
+ pins = "gpio14";
+ function = "qup03";
+ };
+
+ qspi_cs0: qspi-cs0-state {
+ pins = "gpio15";
+ function = "qspi_cs";
+ };
+
+ qup_spi3_cs: qup-spi3-cs-state {
+ pins = "gpio15";
+ function = "qup03";
+ };
+
+ qup_spi3_cs_gpio: qup-spi3-cs-gpio-state {
+ pins = "gpio15";
+ function = "gpio";
+ };
+
+ qup_uart3_rx: qup-uart3-rx-state {
+ pins = "gpio15";
+ function = "qup03";
+ };
+
+ qspi_data23: qspi-data23-state {
+ pins = "gpio16", "gpio17";
+ function = "qspi_data";
+ };
+
+ qup_i2c4_data_clk: qup-i2c4-data-clk-state {
+ pins = "gpio16", "gpio17";
+ function = "qup04";
+ };
+
+ qup_spi4_data_clk: qup-spi4-data-clk-state {
+ pins = "gpio16", "gpio17", "gpio18";
+ function = "qup04";
+ };
+
+ qup_uart4_cts: qup-uart4-cts-state {
+ pins = "gpio16";
+ function = "qup04";
+ };
+
+ qup_uart4_rts: qup-uart4-rts-state {
+ pins = "gpio17";
+ function = "qup04";
+ };
+
+ qup_uart4_tx: qup-uart4-tx-state {
+ pins = "gpio18";
+ function = "qup04";
+ };
+
+ qspi_cs1: qspi-cs1-state {
+ pins = "gpio19";
+ function = "qspi_cs";
+ };
+
+ qup_spi4_cs: qup-spi4-cs-state {
+ pins = "gpio19";
+ function = "qup04";
+ };
+
+ qup_spi4_cs_gpio: qup-spi4-cs-gpio-state {
+ pins = "gpio19";
+ function = "gpio";
+ };
+
+ qup_uart4_rx: qup-uart4-rx-state {
+ pins = "gpio19";
+ function = "qup04";
+ };
+
+ qup_i2c5_data_clk: qup-i2c5-data-clk-state {
+ pins = "gpio20", "gpio21";
+ function = "qup05";
+ };
+
+ qup_spi5_data_clk: qup-spi5-data-clk-state {
+ pins = "gpio20", "gpio21", "gpio22";
+ function = "qup05";
+ };
+
+ qup_uart5_tx: qup-uart5-tx-state {
+ pins = "gpio22";
+ function = "qup05";
+ };
+
+ qup_spi5_cs: qup-spi5-cs-state {
+ pins = "gpio23";
+ function = "qup05";
+ };
+
+ qup_spi5_cs_gpio: qup-spi5-cs-gpio-state {
+ pins = "gpio23";
+ function = "gpio";
+ };
+
+ qup_uart5_rx: qup-uart5-rx-state {
+ pins = "gpio23";
+ function = "qup05";
+ };
+
+ qup_i2c6_data_clk: qup-i2c6-data-clk-state {
+ pins = "gpio24", "gpio25";
+ function = "qup06";
+ };
+
+ qup_spi6_data_clk: qup-spi6-data-clk-state {
+ pins = "gpio24", "gpio25", "gpio26";
+ function = "qup06";
+ };
+
+ qup_uart6_cts: qup-uart6-cts-state {
+ pins = "gpio24";
+ function = "qup06";
+ };
+
+ qup_uart6_rts: qup-uart6-rts-state {
+ pins = "gpio25";
+ function = "qup06";
+ };
+
+ qup_uart6_tx: qup-uart6-tx-state {
+ pins = "gpio26";
+ function = "qup06";
+ };
+
+ qup_spi6_cs: qup-spi6-cs-state {
+ pins = "gpio27";
+ function = "qup06";
+ };
+
+ qup_spi6_cs_gpio: qup-spi6-cs-gpio-state {
+ pins = "gpio27";
+ function = "gpio";
+ };
+
+ qup_uart6_rx: qup-uart6-rx-state {
+ pins = "gpio27";
+ function = "qup06";
+ };
+
+ qup_i2c7_data_clk: qup-i2c7-data-clk-state {
+ pins = "gpio28", "gpio29";
+ function = "qup07";
+ };
+
+ qup_spi7_data_clk: qup-spi7-data-clk-state {
+ pins = "gpio28", "gpio29", "gpio30";
+ function = "qup07";
+ };
+
+ qup_uart7_cts: qup-uart7-cts-state {
+ pins = "gpio28";
+ function = "qup07";
+ };
+
+ qup_uart7_rts: qup-uart7-rts-state {
+ pins = "gpio29";
+ function = "qup07";
+ };
+
+ qup_uart7_tx: qup-uart7-tx-state {
+ pins = "gpio30";
+ function = "qup07";
+ };
+
+ qup_spi7_cs: qup-spi7-cs-state {
+ pins = "gpio31";
+ function = "qup07";
+ };
+
+ qup_spi7_cs_gpio: qup-spi7-cs-gpio-state {
+ pins = "gpio31";
+ function = "gpio";
+ };
+
+ qup_uart7_rx: qup-uart7-rx-state {
+ pins = "gpio31";
+ function = "qup07";
+ };
+
+ qup_i2c8_data_clk: qup-i2c8-data-clk-state {
+ pins = "gpio32", "gpio33";
+ function = "qup10";
+ };
+
+ qup_spi8_data_clk: qup-spi8-data-clk-state {
+ pins = "gpio32", "gpio33", "gpio34";
+ function = "qup10";
+ };
+
+ qup_uart8_cts: qup-uart8-cts-state {
+ pins = "gpio32";
+ function = "qup10";
+ };
+
+ qup_uart8_rts: qup-uart8-rts-state {
+ pins = "gpio33";
+ function = "qup10";
+ };
+
+ qup_uart8_tx: qup-uart8-tx-state {
+ pins = "gpio34";
+ function = "qup10";
+ };
+
+ qup_spi8_cs: qup-spi8-cs-state {
+ pins = "gpio35";
+ function = "qup10";
+ };
+
+ qup_spi8_cs_gpio: qup-spi8-cs-gpio-state {
+ pins = "gpio35";
+ function = "gpio";
+ };
+
+ qup_uart8_rx: qup-uart8-rx-state {
+ pins = "gpio35";
+ function = "qup10";
+ };
+
+ qup_i2c9_data_clk: qup-i2c9-data-clk-state {
+ pins = "gpio36", "gpio37";
+ function = "qup11";
+ };
+
+ qup_spi9_data_clk: qup-spi9-data-clk-state {
+ pins = "gpio36", "gpio37", "gpio38";
+ function = "qup11";
+ };
+
+ qup_uart9_cts: qup-uart9-cts-state {
+ pins = "gpio36";
+ function = "qup11";
+ };
+
+ qup_uart9_rts: qup-uart9-rts-state {
+ pins = "gpio37";
+ function = "qup11";
+ };
+
+ qup_uart9_tx: qup-uart9-tx-state {
+ pins = "gpio38";
+ function = "qup11";
+ };
+
+ qup_spi9_cs: qup-spi9-cs-state {
+ pins = "gpio39";
+ function = "qup11";
+ };
+
+ qup_spi9_cs_gpio: qup-spi9-cs-gpio-state {
+ pins = "gpio39";
+ function = "gpio";
+ };
+
+ qup_uart9_rx: qup-uart9-rx-state {
+ pins = "gpio39";
+ function = "qup11";
+ };
+
+ qup_i2c10_data_clk: qup-i2c10-data-clk-state {
+ pins = "gpio40", "gpio41";
+ function = "qup12";
+ };
+
+ qup_spi10_data_clk: qup-spi10-data-clk-state {
+ pins = "gpio40", "gpio41", "gpio42";
+ function = "qup12";
+ };
+
+ qup_uart10_cts: qup-uart10-cts-state {
+ pins = "gpio40";
+ function = "qup12";
+ };
+
+ qup_uart10_rts: qup-uart10-rts-state {
+ pins = "gpio41";
+ function = "qup12";
+ };
+
+ qup_uart10_tx: qup-uart10-tx-state {
+ pins = "gpio42";
+ function = "qup12";
+ };
+
+ qup_spi10_cs: qup-spi10-cs-state {
+ pins = "gpio43";
+ function = "qup12";
+ };
+
+ qup_spi10_cs_gpio: qup-spi10-cs-gpio-state {
+ pins = "gpio43";
+ function = "gpio";
+ };
+
+ qup_uart10_rx: qup-uart10-rx-state {
+ pins = "gpio43";
+ function = "qup12";
+ };
+
+ qup_i2c11_data_clk: qup-i2c11-data-clk-state {
+ pins = "gpio44", "gpio45";
+ function = "qup13";
+ };
+
+ qup_spi11_data_clk: qup-spi11-data-clk-state {
+ pins = "gpio44", "gpio45", "gpio46";
+ function = "qup13";
+ };
+
+ qup_uart11_cts: qup-uart11-cts-state {
+ pins = "gpio44";
+ function = "qup13";
+ };
+
+ qup_uart11_rts: qup-uart11-rts-state {
+ pins = "gpio45";
+ function = "qup13";
+ };
+
+ qup_uart11_tx: qup-uart11-tx-state {
+ pins = "gpio46";
+ function = "qup13";
+ };
+
+ dp_hot_plug_det: dp-hot-plug-det-state {
+ pins = "gpio47";
+ function = "dp_hot";
+ };
+
+ qup_spi11_cs: qup-spi11-cs-state {
+ pins = "gpio47";
+ function = "qup13";
+ };
+
+ qup_spi11_cs_gpio: qup-spi11-cs-gpio-state {
+ pins = "gpio47";
+ function = "gpio";
+ };
+
+ qup_uart11_rx: qup-uart11-rx-state {
+ pins = "gpio47";
+ function = "qup13";
+ };
+
+ qup_i2c12_data_clk: qup-i2c12-data-clk-state {
+ pins = "gpio48", "gpio49";
+ function = "qup14";
+ };
+
+ qup_spi12_data_clk: qup-spi12-data-clk-state {
+ pins = "gpio48", "gpio49", "gpio50";
+ function = "qup14";
+ };
+
+ qup_uart12_cts: qup-uart12-cts-state {
+ pins = "gpio48";
+ function = "qup14";
+ };
+
+ qup_uart12_rts: qup-uart12-rts-state {
+ pins = "gpio49";
+ function = "qup14";
+ };
+
+ qup_uart12_tx: qup-uart12-tx-state {
+ pins = "gpio50";
+ function = "qup14";
+ };
+
+ qup_spi12_cs: qup-spi12-cs-state {
+ pins = "gpio51";
+ function = "qup14";
+ };
+
+ qup_spi12_cs_gpio: qup-spi12-cs-gpio-state {
+ pins = "gpio51";
+ function = "gpio";
+ };
+
+ qup_uart12_rx: qup-uart12-rx-state {
+ pins = "gpio51";
+ function = "qup14";
+ };
+
+ qup_i2c13_data_clk: qup-i2c13-data-clk-state {
+ pins = "gpio52", "gpio53";
+ function = "qup15";
+ };
+
+ qup_spi13_data_clk: qup-spi13-data-clk-state {
+ pins = "gpio52", "gpio53", "gpio54";
+ function = "qup15";
+ };
+
+ qup_uart13_cts: qup-uart13-cts-state {
+ pins = "gpio52";
+ function = "qup15";
+ };
+
+ qup_uart13_rts: qup-uart13-rts-state {
+ pins = "gpio53";
+ function = "qup15";
+ };
+
+ qup_uart13_tx: qup-uart13-tx-state {
+ pins = "gpio54";
+ function = "qup15";
+ };
+
+ qup_spi13_cs: qup-spi13-cs-state {
+ pins = "gpio55";
+ function = "qup15";
+ };
+
+ qup_spi13_cs_gpio: qup-spi13-cs-gpio-state {
+ pins = "gpio55";
+ function = "gpio";
+ };
+
+ qup_uart13_rx: qup-uart13-rx-state {
+ pins = "gpio55";
+ function = "qup15";
+ };
+
+ qup_i2c14_data_clk: qup-i2c14-data-clk-state {
+ pins = "gpio56", "gpio57";
+ function = "qup16";
+ };
+
+ qup_spi14_data_clk: qup-spi14-data-clk-state {
+ pins = "gpio56", "gpio57", "gpio58";
+ function = "qup16";
+ };
+
+ qup_uart14_cts: qup-uart14-cts-state {
+ pins = "gpio56";
+ function = "qup16";
+ };
+
+ qup_uart14_rts: qup-uart14-rts-state {
+ pins = "gpio57";
+ function = "qup16";
+ };
+
+ qup_uart14_tx: qup-uart14-tx-state {
+ pins = "gpio58";
+ function = "qup16";
+ };
+
+ qup_spi14_cs: qup-spi14-cs-state {
+ pins = "gpio59";
+ function = "qup16";
+ };
+
+ qup_spi14_cs_gpio: qup-spi14-cs-gpio-state {
+ pins = "gpio59";
+ function = "gpio";
+ };
+
+ qup_uart14_rx: qup-uart14-rx-state {
+ pins = "gpio59";
+ function = "qup16";
+ };
+
+ edp_hot_plug_det: edp-hot-plug-det-state {
+ pins = "gpio60";
+ function = "edp_hot";
+ };
+
+ qup_i2c15_data_clk: qup-i2c15-data-clk-state {
+ pins = "gpio60", "gpio61";
+ function = "qup17";
+ };
+
+ qup_spi15_data_clk: qup-spi15-data-clk-state {
+ pins = "gpio60", "gpio61", "gpio62";
+ function = "qup17";
+ };
+
+ qup_uart15_cts: qup-uart15-cts-state {
+ pins = "gpio60";
+ function = "qup17";
+ };
+
+ qup_uart15_rts: qup-uart15-rts-state {
+ pins = "gpio61";
+ function = "qup17";
+ };
+
+ qup_uart15_tx: qup-uart15-tx-state {
+ pins = "gpio62";
+ function = "qup17";
+ };
+
+ qup_spi15_cs: qup-spi15-cs-state {
+ pins = "gpio63";
+ function = "qup17";
+ };
+
+ qup_spi15_cs_gpio: qup-spi15-cs-gpio-state {
+ pins = "gpio63";
+ function = "gpio";
+ };
+
+ qup_uart15_rx: qup-uart15-rx-state {
+ pins = "gpio63";
+ function = "qup17";
+ };
+
cam_mclk3_default: cam-mclk3-default-state {
pins = "gpio67";
function = "cam_mclk";
@@ -5898,24 +6568,14 @@ cci3_sleep: cci3-sleep-state {
bias-pull-down;
};
- dp_hot_plug_det: dp-hot-plug-det-state {
- pins = "gpio47";
- function = "dp_hot";
+ pcie1_clkreq_n: pcie1-clkreq-n-state {
+ pins = "gpio79";
+ function = "pcie1_clkreqn";
};
- edp_hot_plug_det: edp-hot-plug-det-state {
- pins = "gpio60";
- function = "edp_hot";
- };
-
- mi2s0_data0: mi2s0-data0-state {
- pins = "gpio98";
- function = "mi2s0_data0";
- };
-
- mi2s0_data1: mi2s0-data1-state {
- pins = "gpio99";
- function = "mi2s0_data1";
+ pcie0_clkreq_n: pcie0-clkreq-n-state {
+ pins = "gpio88";
+ function = "pcie0_clkreqn";
};
mi2s0_mclk: mi2s0-mclk-state {
@@ -5928,696 +6588,36 @@ mi2s0_sclk: mi2s0-sclk-state {
function = "mi2s0_sck";
};
+ mi2s0_data0: mi2s0-data0-state {
+ pins = "gpio98";
+ function = "mi2s0_data0";
+ };
+
+ mi2s0_data1: mi2s0-data1-state {
+ pins = "gpio99";
+ function = "mi2s0_data1";
+ };
+
mi2s0_ws: mi2s0-ws-state {
pins = "gpio100";
function = "mi2s0_ws";
};
- mi2s1_data0: mi2s1-data0-state {
- pins = "gpio107";
- function = "mi2s1_data0";
- };
-
mi2s1_sclk: mi2s1-sclk-state {
pins = "gpio106";
function = "mi2s1_sck";
};
+ mi2s1_data0: mi2s1-data0-state {
+ pins = "gpio107";
+ function = "mi2s1_data0";
+ };
+
mi2s1_ws: mi2s1-ws-state {
pins = "gpio108";
function = "mi2s1_ws";
};
- pcie0_clkreq_n: pcie0-clkreq-n-state {
- pins = "gpio88";
- function = "pcie0_clkreqn";
- };
-
- pcie1_clkreq_n: pcie1-clkreq-n-state {
- pins = "gpio79";
- function = "pcie1_clkreqn";
- };
-
- qspi_clk: qspi-clk-state {
- pins = "gpio14";
- function = "qspi_clk";
- };
-
- qspi_cs0: qspi-cs0-state {
- pins = "gpio15";
- function = "qspi_cs";
- };
-
- qspi_cs1: qspi-cs1-state {
- pins = "gpio19";
- function = "qspi_cs";
- };
-
- qspi_data0: qspi-data0-state {
- pins = "gpio12";
- function = "qspi_data";
- };
-
- qspi_data1: qspi-data1-state {
- pins = "gpio13";
- function = "qspi_data";
- };
-
- qspi_data23: qspi-data23-state {
- pins = "gpio16", "gpio17";
- function = "qspi_data";
- };
-
- qup_i2c0_data_clk: qup-i2c0-data-clk-state {
- pins = "gpio0", "gpio1";
- function = "qup00";
- };
-
- qup_i2c1_data_clk: qup-i2c1-data-clk-state {
- pins = "gpio4", "gpio5";
- function = "qup01";
- };
-
- qup_i2c2_data_clk: qup-i2c2-data-clk-state {
- pins = "gpio8", "gpio9";
- function = "qup02";
- };
-
- qup_i2c3_data_clk: qup-i2c3-data-clk-state {
- pins = "gpio12", "gpio13";
- function = "qup03";
- };
-
- qup_i2c4_data_clk: qup-i2c4-data-clk-state {
- pins = "gpio16", "gpio17";
- function = "qup04";
- };
-
- qup_i2c5_data_clk: qup-i2c5-data-clk-state {
- pins = "gpio20", "gpio21";
- function = "qup05";
- };
-
- qup_i2c6_data_clk: qup-i2c6-data-clk-state {
- pins = "gpio24", "gpio25";
- function = "qup06";
- };
-
- qup_i2c7_data_clk: qup-i2c7-data-clk-state {
- pins = "gpio28", "gpio29";
- function = "qup07";
- };
-
- qup_i2c8_data_clk: qup-i2c8-data-clk-state {
- pins = "gpio32", "gpio33";
- function = "qup10";
- };
-
- qup_i2c9_data_clk: qup-i2c9-data-clk-state {
- pins = "gpio36", "gpio37";
- function = "qup11";
- };
-
- qup_i2c10_data_clk: qup-i2c10-data-clk-state {
- pins = "gpio40", "gpio41";
- function = "qup12";
- };
-
- qup_i2c11_data_clk: qup-i2c11-data-clk-state {
- pins = "gpio44", "gpio45";
- function = "qup13";
- };
-
- qup_i2c12_data_clk: qup-i2c12-data-clk-state {
- pins = "gpio48", "gpio49";
- function = "qup14";
- };
-
- qup_i2c13_data_clk: qup-i2c13-data-clk-state {
- pins = "gpio52", "gpio53";
- function = "qup15";
- };
-
- qup_i2c14_data_clk: qup-i2c14-data-clk-state {
- pins = "gpio56", "gpio57";
- function = "qup16";
- };
-
- qup_i2c15_data_clk: qup-i2c15-data-clk-state {
- pins = "gpio60", "gpio61";
- function = "qup17";
- };
-
- qup_spi0_data_clk: qup-spi0-data-clk-state {
- pins = "gpio0", "gpio1", "gpio2";
- function = "qup00";
- };
-
- qup_spi0_cs: qup-spi0-cs-state {
- pins = "gpio3";
- function = "qup00";
- };
-
- qup_spi0_cs_gpio: qup-spi0-cs-gpio-state {
- pins = "gpio3";
- function = "gpio";
- };
-
- qup_spi1_data_clk: qup-spi1-data-clk-state {
- pins = "gpio4", "gpio5", "gpio6";
- function = "qup01";
- };
-
- qup_spi1_cs: qup-spi1-cs-state {
- pins = "gpio7";
- function = "qup01";
- };
-
- qup_spi1_cs_gpio: qup-spi1-cs-gpio-state {
- pins = "gpio7";
- function = "gpio";
- };
-
- qup_spi2_data_clk: qup-spi2-data-clk-state {
- pins = "gpio8", "gpio9", "gpio10";
- function = "qup02";
- };
-
- qup_spi2_cs: qup-spi2-cs-state {
- pins = "gpio11";
- function = "qup02";
- };
-
- qup_spi2_cs_gpio: qup-spi2-cs-gpio-state {
- pins = "gpio11";
- function = "gpio";
- };
-
- qup_spi3_data_clk: qup-spi3-data-clk-state {
- pins = "gpio12", "gpio13", "gpio14";
- function = "qup03";
- };
-
- qup_spi3_cs: qup-spi3-cs-state {
- pins = "gpio15";
- function = "qup03";
- };
-
- qup_spi3_cs_gpio: qup-spi3-cs-gpio-state {
- pins = "gpio15";
- function = "gpio";
- };
-
- qup_spi4_data_clk: qup-spi4-data-clk-state {
- pins = "gpio16", "gpio17", "gpio18";
- function = "qup04";
- };
-
- qup_spi4_cs: qup-spi4-cs-state {
- pins = "gpio19";
- function = "qup04";
- };
-
- qup_spi4_cs_gpio: qup-spi4-cs-gpio-state {
- pins = "gpio19";
- function = "gpio";
- };
-
- qup_spi5_data_clk: qup-spi5-data-clk-state {
- pins = "gpio20", "gpio21", "gpio22";
- function = "qup05";
- };
-
- qup_spi5_cs: qup-spi5-cs-state {
- pins = "gpio23";
- function = "qup05";
- };
-
- qup_spi5_cs_gpio: qup-spi5-cs-gpio-state {
- pins = "gpio23";
- function = "gpio";
- };
-
- qup_spi6_data_clk: qup-spi6-data-clk-state {
- pins = "gpio24", "gpio25", "gpio26";
- function = "qup06";
- };
-
- qup_spi6_cs: qup-spi6-cs-state {
- pins = "gpio27";
- function = "qup06";
- };
-
- qup_spi6_cs_gpio: qup-spi6-cs-gpio-state {
- pins = "gpio27";
- function = "gpio";
- };
-
- qup_spi7_data_clk: qup-spi7-data-clk-state {
- pins = "gpio28", "gpio29", "gpio30";
- function = "qup07";
- };
-
- qup_spi7_cs: qup-spi7-cs-state {
- pins = "gpio31";
- function = "qup07";
- };
-
- qup_spi7_cs_gpio: qup-spi7-cs-gpio-state {
- pins = "gpio31";
- function = "gpio";
- };
-
- qup_spi8_data_clk: qup-spi8-data-clk-state {
- pins = "gpio32", "gpio33", "gpio34";
- function = "qup10";
- };
-
- qup_spi8_cs: qup-spi8-cs-state {
- pins = "gpio35";
- function = "qup10";
- };
-
- qup_spi8_cs_gpio: qup-spi8-cs-gpio-state {
- pins = "gpio35";
- function = "gpio";
- };
-
- qup_spi9_data_clk: qup-spi9-data-clk-state {
- pins = "gpio36", "gpio37", "gpio38";
- function = "qup11";
- };
-
- qup_spi9_cs: qup-spi9-cs-state {
- pins = "gpio39";
- function = "qup11";
- };
-
- qup_spi9_cs_gpio: qup-spi9-cs-gpio-state {
- pins = "gpio39";
- function = "gpio";
- };
-
- qup_spi10_data_clk: qup-spi10-data-clk-state {
- pins = "gpio40", "gpio41", "gpio42";
- function = "qup12";
- };
-
- qup_spi10_cs: qup-spi10-cs-state {
- pins = "gpio43";
- function = "qup12";
- };
-
- qup_spi10_cs_gpio: qup-spi10-cs-gpio-state {
- pins = "gpio43";
- function = "gpio";
- };
-
- qup_spi11_data_clk: qup-spi11-data-clk-state {
- pins = "gpio44", "gpio45", "gpio46";
- function = "qup13";
- };
-
- qup_spi11_cs: qup-spi11-cs-state {
- pins = "gpio47";
- function = "qup13";
- };
-
- qup_spi11_cs_gpio: qup-spi11-cs-gpio-state {
- pins = "gpio47";
- function = "gpio";
- };
-
- qup_spi12_data_clk: qup-spi12-data-clk-state {
- pins = "gpio48", "gpio49", "gpio50";
- function = "qup14";
- };
-
- qup_spi12_cs: qup-spi12-cs-state {
- pins = "gpio51";
- function = "qup14";
- };
-
- qup_spi12_cs_gpio: qup-spi12-cs-gpio-state {
- pins = "gpio51";
- function = "gpio";
- };
-
- qup_spi13_data_clk: qup-spi13-data-clk-state {
- pins = "gpio52", "gpio53", "gpio54";
- function = "qup15";
- };
-
- qup_spi13_cs: qup-spi13-cs-state {
- pins = "gpio55";
- function = "qup15";
- };
-
- qup_spi13_cs_gpio: qup-spi13-cs-gpio-state {
- pins = "gpio55";
- function = "gpio";
- };
-
- qup_spi14_data_clk: qup-spi14-data-clk-state {
- pins = "gpio56", "gpio57", "gpio58";
- function = "qup16";
- };
-
- qup_spi14_cs: qup-spi14-cs-state {
- pins = "gpio59";
- function = "qup16";
- };
-
- qup_spi14_cs_gpio: qup-spi14-cs-gpio-state {
- pins = "gpio59";
- function = "gpio";
- };
-
- qup_spi15_data_clk: qup-spi15-data-clk-state {
- pins = "gpio60", "gpio61", "gpio62";
- function = "qup17";
- };
-
- qup_spi15_cs: qup-spi15-cs-state {
- pins = "gpio63";
- function = "qup17";
- };
-
- qup_spi15_cs_gpio: qup-spi15-cs-gpio-state {
- pins = "gpio63";
- function = "gpio";
- };
-
- qup_uart0_cts: qup-uart0-cts-state {
- pins = "gpio0";
- function = "qup00";
- };
-
- qup_uart0_rts: qup-uart0-rts-state {
- pins = "gpio1";
- function = "qup00";
- };
-
- qup_uart0_tx: qup-uart0-tx-state {
- pins = "gpio2";
- function = "qup00";
- };
-
- qup_uart0_rx: qup-uart0-rx-state {
- pins = "gpio3";
- function = "qup00";
- };
-
- qup_uart1_cts: qup-uart1-cts-state {
- pins = "gpio4";
- function = "qup01";
- };
-
- qup_uart1_rts: qup-uart1-rts-state {
- pins = "gpio5";
- function = "qup01";
- };
-
- qup_uart1_tx: qup-uart1-tx-state {
- pins = "gpio6";
- function = "qup01";
- };
-
- qup_uart1_rx: qup-uart1-rx-state {
- pins = "gpio7";
- function = "qup01";
- };
-
- qup_uart2_cts: qup-uart2-cts-state {
- pins = "gpio8";
- function = "qup02";
- };
-
- qup_uart2_rts: qup-uart2-rts-state {
- pins = "gpio9";
- function = "qup02";
- };
-
- qup_uart2_tx: qup-uart2-tx-state {
- pins = "gpio10";
- function = "qup02";
- };
-
- qup_uart2_rx: qup-uart2-rx-state {
- pins = "gpio11";
- function = "qup02";
- };
-
- qup_uart3_cts: qup-uart3-cts-state {
- pins = "gpio12";
- function = "qup03";
- };
-
- qup_uart3_rts: qup-uart3-rts-state {
- pins = "gpio13";
- function = "qup03";
- };
-
- qup_uart3_tx: qup-uart3-tx-state {
- pins = "gpio14";
- function = "qup03";
- };
-
- qup_uart3_rx: qup-uart3-rx-state {
- pins = "gpio15";
- function = "qup03";
- };
-
- qup_uart4_cts: qup-uart4-cts-state {
- pins = "gpio16";
- function = "qup04";
- };
-
- qup_uart4_rts: qup-uart4-rts-state {
- pins = "gpio17";
- function = "qup04";
- };
-
- qup_uart4_tx: qup-uart4-tx-state {
- pins = "gpio18";
- function = "qup04";
- };
-
- qup_uart4_rx: qup-uart4-rx-state {
- pins = "gpio19";
- function = "qup04";
- };
-
- qup_uart5_tx: qup-uart5-tx-state {
- pins = "gpio22";
- function = "qup05";
- };
-
- qup_uart5_rx: qup-uart5-rx-state {
- pins = "gpio23";
- function = "qup05";
- };
-
- qup_uart6_cts: qup-uart6-cts-state {
- pins = "gpio24";
- function = "qup06";
- };
-
- qup_uart6_rts: qup-uart6-rts-state {
- pins = "gpio25";
- function = "qup06";
- };
-
- qup_uart6_tx: qup-uart6-tx-state {
- pins = "gpio26";
- function = "qup06";
- };
-
- qup_uart6_rx: qup-uart6-rx-state {
- pins = "gpio27";
- function = "qup06";
- };
-
- qup_uart7_cts: qup-uart7-cts-state {
- pins = "gpio28";
- function = "qup07";
- };
-
- qup_uart7_rts: qup-uart7-rts-state {
- pins = "gpio29";
- function = "qup07";
- };
-
- qup_uart7_tx: qup-uart7-tx-state {
- pins = "gpio30";
- function = "qup07";
- };
-
- qup_uart7_rx: qup-uart7-rx-state {
- pins = "gpio31";
- function = "qup07";
- };
-
- qup_uart8_cts: qup-uart8-cts-state {
- pins = "gpio32";
- function = "qup10";
- };
-
- qup_uart8_rts: qup-uart8-rts-state {
- pins = "gpio33";
- function = "qup10";
- };
-
- qup_uart8_tx: qup-uart8-tx-state {
- pins = "gpio34";
- function = "qup10";
- };
-
- qup_uart8_rx: qup-uart8-rx-state {
- pins = "gpio35";
- function = "qup10";
- };
-
- qup_uart9_cts: qup-uart9-cts-state {
- pins = "gpio36";
- function = "qup11";
- };
-
- qup_uart9_rts: qup-uart9-rts-state {
- pins = "gpio37";
- function = "qup11";
- };
-
- qup_uart9_tx: qup-uart9-tx-state {
- pins = "gpio38";
- function = "qup11";
- };
-
- qup_uart9_rx: qup-uart9-rx-state {
- pins = "gpio39";
- function = "qup11";
- };
-
- qup_uart10_cts: qup-uart10-cts-state {
- pins = "gpio40";
- function = "qup12";
- };
-
- qup_uart10_rts: qup-uart10-rts-state {
- pins = "gpio41";
- function = "qup12";
- };
-
- qup_uart10_tx: qup-uart10-tx-state {
- pins = "gpio42";
- function = "qup12";
- };
-
- qup_uart10_rx: qup-uart10-rx-state {
- pins = "gpio43";
- function = "qup12";
- };
-
- qup_uart11_cts: qup-uart11-cts-state {
- pins = "gpio44";
- function = "qup13";
- };
-
- qup_uart11_rts: qup-uart11-rts-state {
- pins = "gpio45";
- function = "qup13";
- };
-
- qup_uart11_tx: qup-uart11-tx-state {
- pins = "gpio46";
- function = "qup13";
- };
-
- qup_uart11_rx: qup-uart11-rx-state {
- pins = "gpio47";
- function = "qup13";
- };
-
- qup_uart12_cts: qup-uart12-cts-state {
- pins = "gpio48";
- function = "qup14";
- };
-
- qup_uart12_rts: qup-uart12-rts-state {
- pins = "gpio49";
- function = "qup14";
- };
-
- qup_uart12_tx: qup-uart12-tx-state {
- pins = "gpio50";
- function = "qup14";
- };
-
- qup_uart12_rx: qup-uart12-rx-state {
- pins = "gpio51";
- function = "qup14";
- };
-
- qup_uart13_cts: qup-uart13-cts-state {
- pins = "gpio52";
- function = "qup15";
- };
-
- qup_uart13_rts: qup-uart13-rts-state {
- pins = "gpio53";
- function = "qup15";
- };
-
- qup_uart13_tx: qup-uart13-tx-state {
- pins = "gpio54";
- function = "qup15";
- };
-
- qup_uart13_rx: qup-uart13-rx-state {
- pins = "gpio55";
- function = "qup15";
- };
-
- qup_uart14_cts: qup-uart14-cts-state {
- pins = "gpio56";
- function = "qup16";
- };
-
- qup_uart14_rts: qup-uart14-rts-state {
- pins = "gpio57";
- function = "qup16";
- };
-
- qup_uart14_tx: qup-uart14-tx-state {
- pins = "gpio58";
- function = "qup16";
- };
-
- qup_uart14_rx: qup-uart14-rx-state {
- pins = "gpio59";
- function = "qup16";
- };
-
- qup_uart15_cts: qup-uart15-cts-state {
- pins = "gpio60";
- function = "qup17";
- };
-
- qup_uart15_rts: qup-uart15-rts-state {
- pins = "gpio61";
- function = "qup17";
- };
-
- qup_uart15_tx: qup-uart15-tx-state {
- pins = "gpio62";
- function = "qup17";
- };
-
- qup_uart15_rx: qup-uart15-rx-state {
- pins = "gpio63";
- function = "qup17";
- };
-
sdc1_clk: sdc1-clk-state {
pins = "sdc1_clk";
};
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 02/11] ARM: dts: renesas: r8a7740: Add clocks for FSI
From: Bui Duc Phuc @ 2026-06-12 11:08 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Kuninori Morimoto, Mark Brown, Liam Girdwood, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, Jaroslav Kysela,
Takashi Iwai, linux-sound, linux-renesas-soc, devicetree,
linux-kernel
In-Reply-To: <CAMuHMdXku0BVRgwWWv1vNccKpRbvTu+1b0TjYacNJH94XBDOGg@mail.gmail.com>
Hi Geert,
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> i.e. will queue in renesas-devel for v7.3.
>
Thank you for the review and for the Reviewed-by tag.
> I have one more general question.
> arch/arm/boot/dts/renesas/r8a7740-armadillo800eva.dts configures
> audio for FSI (fsia_pins), but does not fill in a clock-frequency
> in fsiack_clk. Instead, it fills in 12.288 MHz in fsibck_clk, while
> the schematics call it FSIACK.
> Apparently the FSIACK pin is shared with FSIBCK on R-Mobile A1, so
> which function is used depends on pin control. However, the DTS does
> not perform any pin configuration for this pin?
>
I checked the schematic in more detail.
On sheet 12 (FSIA section), the FSIACK signal is connected to the
WM8978 MCLK pin (pin 11).
It is also connected to the X8 oscillator output (pin 3) through R121.
By default, the board populates R120 (0 ohm), which routes the signal
to R8A7740 pin K5 (FSIAOMC).
If R120 is removed and R250 is populated instead, the signal is routed
to pin G3 (FSIACK).
From my understanding, the default resistor configuration places FSIA
in slave mode.
In this configuration, FSIA receives BCLK and LRCLK from the WM8978,
while the codec receives the 12.288 MHz MCLK from oscillator X8.
This matches the current DTS configuration:
simple-audio-card,codec {
sound-dai = <&wm8978>;
bitclock-master;
frame-master;
system-clock-frequency = <12288000>;
};
Therefore, I do not think it is necessary to provide a 12.288 MHz
frequency for fsiack_clk (xcka) in the current configuration.
If the hardware were reconfigured to operate FSIA in master mode, that
might need to be revisited.
I have considered testing the alternative resistor configuration
(removing R120 and populating R250)
to switch FSIA into master mode, but I have not done so due to the
risk of damaging the board.
As for the relationship between FSIACK and FSIBCK, I am not sure.
The schematic I have does not appear to describe FSIB at all.
The HDMI section only documents video signals and does not mention audio,
so I do not have enough information to determine whether FSIACK and
FSIBCK share the same pin on this board.
> Note that I have never tried audio on Armadillo myself.
>
You should give it a try sometime.
The audio quality of this board is actually not bad for hardware that
is more than a decade old :-)
Best regards,
Phuc
^ permalink raw reply
* Re: [PATCH v4 2/3] clk: qcom: camcc-glymur: Add camera clock controller driver
From: Bryan O'Donoghue @ 2026-06-12 11:14 UTC (permalink / raw)
To: Konrad Dybcio, Jagadeesh Kona, Bjorn Andersson, Michael Turquette,
Stephen Boyd, Brian Masney, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Konrad Dybcio
Cc: linux-arm-msm, linux-clk, devicetree, linux-kernel, Taniya Das
In-Reply-To: <10c2e008-74fe-4dac-99bf-194a1767bc16@oss.qualcomm.com>
On 11/06/2026 09:51, Konrad Dybcio wrote:
>>> implementation simpler, avoids unnecessary abstraction, and makes debugging—through direct
>>> comparison with the hardware spec easier.
>> How are hex values in upstream code easier to debug ?
>>
>> Without the spec you can't change or understand hex values in upstream code, which is the whole point I'm making here.
> I get the 'understanding' part, but regarding change, as I said
> previously, these must remain as-is - any difference for a PLL
> impacts every single clock downstream of it. Some of them also
> correspond to specific electrical properties, just like with PHY
> init sequences. The existing values are a result of tuning and
> silicon validation across presumably many, many chip units.
That's an argument against changing the values, not naming the values.
Hexwork in upstream code is a public black box and should be avoided
where possible.
How about, take these fixed hex but someone on the clock-side in qcom
agrees to update the script to write defined bitfields not hexwork in
future deliveries. AFAIU its a script that mostly spits out these clock
descriptors so, it should be possible to fix that script once @ source,
without committing to fixing everything _currently_ in flight.
Qcom can then at its leisure update old controller descriptors by
running the script again.
> There may be updates (very rarely post the chip going into
> production), but I'd assume these would go through the same
> testing procedures
>
> Konrad
---
bod
^ permalink raw reply
* [PATCH v11 0/9] Add support for NXP P3H2x4x I3C hub driver
From: Lakshay Piplani @ 2026-06-12 11:18 UTC (permalink / raw)
To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
Cc: vikash.bansal, priyanka.jain, aman.kumarpandey, Lakshay Piplani
This series adds a driver for the NXP P3H2x4x family of multiport I3C hub
devices.
This is an MFD driver integrating I3C hub and on-die regulators.
The series introduces:
- Core I3C master enhancements required for hub support
- Generic I3C hub framework
- MFD driver with regulator and I3C hub child drivers for the P3H2x4x I3C hub
Changes in v11:
- Convert i3c_master_supports_ccc_cmd() to return bool and align
semantics with CCC support checks used by the I3C core
- Use MFD_CELL_NAME() for child device registration
- Rename driver names to follow subsystem conventions:
- Use '-' instead of '_' in driver names
- Drop the "_drv" suffix from driver names
- Fix virtual hub address reattach handling and parent bus locking
- Fix IBI request and cleanup error paths
- Improve SMBus slave mode payload validation and parsing
- Link to v10: https://lore.kernel.org/linux-i3c/20260525064209.2263045-1-lakshay.piplani@nxp.com/T/#u
Changes in v10:
- Rename i3c_master_reattach_i3c_dev() to *_locked to reflect required
bus locking
- Rename i3c_master_direct_attach_i3c_dev() and i3c_master_direct_detach_i3c_dev()
to *_locked, as these APIs must be called with the bus lock held for write
- Drop redundant is_p3h2x4x_in_i3c flag from p3h2840.h
- Remove unnecessary ibi_lock handling in request/enable/disable/free
IBI APIs
- Remove redundant parent pointer from struct i3c_hub and derive upstream
master from hub_dev
- Split SMBus target/slave mode support, including IBI and MCTP receive handling,
into a separate patch
- Link to v9: https://lore.kernel.org/linux-i3c/20260420105222.1562243-1-lakshay.piplani@nxp.com/T/#u
Changes in v9:
- Renamed macros to follow consistent uppercase naming conventions
- Made REGMAP selects in the P3H2X4X MFD Kconfig conditional, to avoid I3C/I2C dependency issues
- Referenced i3c.yaml and i2c-controller.yaml for child bus nodes
- Dropped unnecessary #address-cells and #size-cells from child nodes
- Added CONFIG_I2C_SLAVE guards where necessary to avoid build errors when I2C slave support is disabled
- Link to v8: https://lore.kernel.org/linux-i3c/20260323062737.886728-1-lakshay.piplani@nxp.com/T/#u
Changes in v8:
- Add compatible in i3c example
- Link to v7: https://lore.kernel.org/linux-i3c/20260319112441.3888957-1-lakshay.piplani@nxp.com/T/#u
Changes in v7:
- Fix kernel-doc warnings across I3C core and hub code
- Rework DT binding schema and examples to pass dt_binding_check
- Update MFD Kconfig to use I3C_OR_I2C
- Convert CONFIG_I3C_HUB to tristate
- Remove unnecessary CONFIG_I2C_SLAVE guards
- Replace custom helpers with find_closest()
- Use devm_regulator_get_enable_optional()
- Link to v6: https://lore.kernel.org/linux-i3c/64c5070c-aa9e-427a-933e-91e168f0510c@kernel.org/T/#u
Changes in v6:
- Update DT binding with vendor-prefixed properties
- Add generic I3C hub support
- Remove generic code from P3H2x4x driver
- Link to v5: https://lore.kernel.org/linux-i3c/20260206120121.856471-1-aman.kumarpandey@nxp.com/T/#u
Changes in v5:
- Update supply naming and descriptions
- Improve MFD Kconfig/Makefile ordering
- Link to v4: https://lore.kernel.org/linux-i3c/20260113114529.1692213-2-aman.kumarpandey@nxp.com/T/#u
Changes in v4:
- Split driver into MFD, regulator and I3C hub parts
- Update I3C master for hub support
- Fix DT binding issues
- Link to v3: https://lore.kernel.org/linux-i3c/20250811-bittern-of-abstract-prestige-aaeda9@kuoka/T/#u
Changes in v3:
- Add MFD support for hub and regulators
- Add regulator integration
- Link to v2: https://lore.kernel.org/linux-i3c/17145d2f-5d07-4939-8381-74e27cde303c@kernel.org/T/#u
Changes in v2:
- Fix DT binding warnings
- Refine DT parsing logic
- Link to v1: https://lore.kernel.org/linux-i3c/822d6dca-b2c6-4439-ade5-219620ebc435@kernel.org/T/#u
Aman Kumar Pandey (6):
i3c: master: Expose the APIs to support I3C hub
i3c: master: Add APIs for I3C hub support
dt-bindings: i3c: Add NXP P3H2x4x i3c-hub support
mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator
regulator: p3h2x4x: Add driver for on-die regulators in NXP P3H2x4x
i3c hub
i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality
Lakshay Piplani (3):
i3c: master: rename i3c_master_reattach_i3c_dev() to *_locked
i3c: hub: Add support for the I3C interface in the I3C hub
i3c: hub: p3h2x4x: Add SMBus slave mode support
.../devicetree/bindings/i3c/nxp,p3h2840.yaml | 291 +++++++++++
MAINTAINERS | 15 +
drivers/i3c/Kconfig | 16 +
drivers/i3c/Makefile | 2 +
drivers/i3c/hub.c | 484 ++++++++++++++++++
drivers/i3c/hub/Kconfig | 11 +
drivers/i3c/hub/Makefile | 4 +
drivers/i3c/hub/p3h2840_i3c_hub.h | 337 ++++++++++++
drivers/i3c/hub/p3h2840_i3c_hub_common.c | 359 +++++++++++++
drivers/i3c/hub/p3h2840_i3c_hub_i3c.c | 151 ++++++
drivers/i3c/hub/p3h2840_i3c_hub_smbus.c | 481 +++++++++++++++++
drivers/i3c/master.c | 173 ++++++-
drivers/mfd/Kconfig | 13 +
drivers/mfd/Makefile | 1 +
drivers/mfd/p3h2840.c | 119 +++++
drivers/regulator/Kconfig | 10 +
drivers/regulator/Makefile | 1 +
drivers/regulator/p3h2840_i3c_hub_regulator.c | 218 ++++++++
include/linux/i3c/device.h | 1 +
include/linux/i3c/hub.h | 99 ++++
include/linux/i3c/master.h | 9 +
include/linux/mfd/p3h2840.h | 26 +
22 files changed, 2818 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
create mode 100644 drivers/i3c/hub.c
create mode 100644 drivers/i3c/hub/Kconfig
create mode 100644 drivers/i3c/hub/Makefile
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub.h
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_common.c
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
create mode 100644 drivers/mfd/p3h2840.c
create mode 100644 drivers/regulator/p3h2840_i3c_hub_regulator.c
create mode 100644 include/linux/i3c/hub.h
create mode 100644 include/linux/mfd/p3h2840.h
--
2.25.1
^ permalink raw reply
* [PATCH v11 5/9] mfd: p3h2x4x: Add driver for NXP P3H2x4x i3c hub and on-die regulator
From: Lakshay Piplani @ 2026-06-12 11:18 UTC (permalink / raw)
To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
Cc: vikash.bansal, priyanka.jain, aman.kumarpandey, Lakshay Piplani
In-Reply-To: <20260612111816.3688240-1-lakshay.piplani@nxp.com>
From: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Add core MFD support for the NXP P3H2x4x (P3H2440/P3H2441/P3H2840/P3H2841)
family of multiport I3C hub devices. These devices connect to a host via
I3C/I2C/SMBus and expose multiple downstream target ports.
Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
Changes in v11:
- Use MFD_CELL_NAME() for child device registration
- Rename local variables for consistency
- Rename driver names to follow subsystem conventions:
- Use '-' instead of '_' in driver names
- Drop the "_drv" suffix from driver names
Changes in v10:
- Drop redundant is_p3h2x4x_in_i3c flag
Changes in v9:
- Renamed macros to follow consistent uppercase naming conventions
- Made REGMAP selects in the P3H2X4X MFD Kconfig conditional,
to avoid I3C/I2C dependency issues
Changes in v8:
- No change
Changes in v7:
- Use new config I3C_OR_I2C
Changes in v6:
- No change
Changes in v5:
- Corrected the ordering in the Makefile and Kconfig for MFD_P3H2X4X
- Updated dev_err_probe() for regmap_init failure.
- Updated module description
Changes in v4:
- Split the driver into three separate patches(mfd, regulator and I3C hub)
- Added support for NXP P3H2x4x MFD functionality
---
---
MAINTAINERS | 2 +
drivers/mfd/Kconfig | 13 ++++
drivers/mfd/Makefile | 1 +
drivers/mfd/p3h2840.c | 119 ++++++++++++++++++++++++++++++++++++
include/linux/i3c/device.h | 1 +
include/linux/mfd/p3h2840.h | 26 ++++++++
6 files changed, 162 insertions(+)
create mode 100644 drivers/mfd/p3h2840.c
create mode 100644 include/linux/mfd/p3h2840.h
diff --git a/MAINTAINERS b/MAINTAINERS
index ba65ae5a008b..3420701a75c5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19311,6 +19311,8 @@ L: linux-kernel@vger.kernel.org
L: linux-i3c@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
+F: drivers/mfd/p3h2840.c
+F: include/linux/mfd/p3h2840.h
NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
M: Woodrow Douglass <wdouglass@carnegierobotics.com>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7192c9d1d268..405b50c3c77b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -617,6 +617,19 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens.
+config MFD_P3H2X4X
+ tristate "NXP P3H2X4X I3C Hub Device"
+ depends on I3C_OR_I2C
+ select MFD_CORE
+ select REGMAP_I3C if I3C
+ select REGMAP_I2C if I2C
+ help
+ Enable Support for NXP P3H244x/P3H284x I3C HUB device using I3C/I2C
+ communication interface.
+
+ This driver provides support for I3C hub and regulator, each subdriver
+ can be enabled independently depending on the required functionality.
+
config MFD_PF1550
tristate "NXP PF1550 PMIC Support"
depends on I2C=y && OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..a284b22c7b13 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
+obj-$(CONFIG_MFD_P3H2X4X) += p3h2840.o
obj-$(CONFIG_MFD_PF1550) += pf1550.o
obj-$(CONFIG_MFD_NCT6694) += nct6694.o
diff --git a/drivers/mfd/p3h2840.c b/drivers/mfd/p3h2840.c
new file mode 100644
index 000000000000..0a80992d6250
--- /dev/null
+++ b/drivers/mfd/p3h2840.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025-2026 NXP
+ * P3H2X4X i3c hub and regulator device.
+ */
+
+#include <linux/i2c.h>
+#include <linux/i3c/master.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/p3h2840.h>
+#include <linux/regmap.h>
+
+static const struct mfd_cell p3h2x4x_devs[] = {
+ MFD_CELL_NAME("p3h2x4x-regulator"),
+ MFD_CELL_NAME("p3h2x4x-i3c-hub"),
+};
+
+static const struct regmap_config p3h2x4x_regmap_config = {
+ .reg_bits = P3H2X4X_REG_BITS,
+ .val_bits = P3H2X4X_VAL_BITS,
+ .max_register = 0xFF,
+};
+
+static int p3h2x4x_device_probe_i3c(struct i3c_device *i3cdev)
+{
+ struct p3h2x4x_dev *ddata;
+ int ret;
+
+ ddata = devm_kzalloc(&i3cdev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ i3cdev_set_drvdata(i3cdev, ddata);
+
+ ddata->regmap = devm_regmap_init_i3c(i3cdev, &p3h2x4x_regmap_config);
+ if (IS_ERR(ddata->regmap))
+ return dev_err_probe(&i3cdev->dev, PTR_ERR(ddata->regmap),
+ "Failed to register HUB regmap\n");
+
+ // The hub child driver retrieves information from i3cdev
+ ddata->i3cdev = i3cdev;
+
+ ret = devm_mfd_add_devices(&i3cdev->dev, PLATFORM_DEVID_AUTO,
+ p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
+ NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(&i3cdev->dev, ret, "Failed to add sub devices\n");
+
+ return 0;
+}
+
+static int p3h2x4x_device_probe_i2c(struct i2c_client *client)
+{
+ struct p3h2x4x_dev *ddata;
+ int ret;
+
+ ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ddata);
+
+ ddata->regmap = devm_regmap_init_i2c(client, &p3h2x4x_regmap_config);
+ if (IS_ERR(ddata->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(ddata->regmap),
+ "Failed to register HUB regmap\n");
+
+ ddata->i3cdev = NULL;
+
+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
+ p3h2x4x_devs, ARRAY_SIZE(p3h2x4x_devs),
+ NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "Failed to add sub devices\n");
+
+ return 0;
+}
+
+static const struct i3c_device_id p3h2x4x_i3c_ids[] = {
+ I3C_CLASS(I3C_DCR_HUB, NULL),
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, p3h2x4x_i3c_ids);
+
+static const struct i2c_device_id p3h2x4x_i2c_id_table[] = {
+ { "nxp-i3c-hub" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, p3h2x4x_i2c_id_table);
+
+static const struct of_device_id p3h2x4x_i2c_of_match[] = {
+ { .compatible = "nxp,p3h2840", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, p3h2x4x_i2c_of_match);
+
+static struct i3c_driver p3h2x4x_i3c = {
+ .driver = {
+ .name = "p3h2x4x-i3c",
+ },
+ .probe = p3h2x4x_device_probe_i3c,
+ .id_table = p3h2x4x_i3c_ids,
+};
+
+static struct i2c_driver p3h2x4x_i2c = {
+ .driver = {
+ .name = "p3h2x4x-i2c",
+ .of_match_table = p3h2x4x_i2c_of_match,
+ },
+ .probe = p3h2x4x_device_probe_i2c,
+ .id_table = p3h2x4x_i2c_id_table,
+};
+module_i3c_i2c_driver(p3h2x4x_i3c, &p3h2x4x_i2c);
+
+MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
+MODULE_AUTHOR("Vikash Bansal <vikash.bansal@nxp.com>");
+MODULE_AUTHOR("Lakshay Piplani <lakshay.piplani@nxp.com>");
+MODULE_DESCRIPTION("NXP P3H2X4X I3C HUB multi function driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 971d53349b6f..6188082599dd 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -85,6 +85,7 @@ struct i3c_xfer {
*/
enum i3c_dcr {
I3C_DCR_GENERIC_DEVICE = 0,
+ I3C_DCR_HUB = 194,
};
#define I3C_PID_MANUF_ID(pid) (((pid) & GENMASK_ULL(47, 33)) >> 33)
diff --git a/include/linux/mfd/p3h2840.h b/include/linux/mfd/p3h2840.h
new file mode 100644
index 000000000000..2d4fae2176ae
--- /dev/null
+++ b/include/linux/mfd/p3h2840.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2025-2026 NXP
+ * This header file contains register definitions and bit masks for the P3H2X4X.
+ */
+
+#ifndef _LINUX_MFD_P3H2840_H
+#define _LINUX_MFD_P3H2840_H
+
+#include <linux/types.h>
+
+/* Device Configuration Registers */
+#define P3H2X4X_DEV_REG_PROTECTION_CODE 0x10
+#define P3H2X4X_REGISTERS_LOCK_CODE 0x00
+#define P3H2X4X_REGISTERS_UNLOCK_CODE 0x69
+#define P3H2X4X_CP1_REGISTERS_UNLOCK_CODE 0x6a
+
+/* Reg config for Regmap */
+#define P3H2X4X_REG_BITS 8
+#define P3H2X4X_VAL_BITS 8
+
+struct p3h2x4x_dev {
+ struct i3c_device *i3cdev;
+ struct regmap *regmap;
+};
+#endif /* _LINUX_MFD_P3H2840_H */
--
2.25.1
^ permalink raw reply related
* [PATCH v11 1/9] i3c: master: rename i3c_master_reattach_i3c_dev() to *_locked
From: Lakshay Piplani @ 2026-06-12 11:18 UTC (permalink / raw)
To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
Cc: vikash.bansal, priyanka.jain, aman.kumarpandey, Lakshay Piplani
In-Reply-To: <20260612111816.3688240-1-lakshay.piplani@nxp.com>
Rename i3c_master_reattach_i3c_dev() to *_locked() to make the locking
requirement explicit and consistent with other I3C core helpers that
require the bus lock to be held by the caller.
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v11:
- No change, added Reviewed-By tag
Changes in v10:
- Rename i3c_master_reattach_i3c_dev() to *_locked to reflect required
bus locking
---
---
drivers/i3c/master.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 5cd4e5da2233..e89d73508b9a 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1652,8 +1652,8 @@ static int i3c_master_attach_i3c_dev(struct i3c_master_controller *master,
return 0;
}
-static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
- u8 old_dyn_addr)
+static int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev,
+ u8 old_dyn_addr)
{
struct i3c_master_controller *master = i3c_dev_get_master(dev);
int ret;
@@ -1742,7 +1742,7 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master,
goto err_detach_dev;
i3cdev->info.dyn_addr = i3cdev->boardinfo->init_dyn_addr;
- ret = i3c_master_reattach_i3c_dev(i3cdev, 0);
+ ret = i3c_master_reattach_i3c_dev_locked(i3cdev, 0);
if (ret)
goto err_rstdaa;
@@ -2358,7 +2358,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
if (!ret) {
old_dyn_addr = newdev->info.dyn_addr;
newdev->info.dyn_addr = expected_dyn_addr;
- i3c_master_reattach_i3c_dev(newdev, old_dyn_addr);
+ i3c_master_reattach_i3c_dev_locked(newdev, old_dyn_addr);
} else {
dev_err(&master->dev,
"Failed to assign reserved/old address to device %d%llx",
--
2.25.1
^ permalink raw reply related
* [PATCH v11 8/9] i3c: hub: p3h2x4x: Add support for NXP P3H2x4x I3C hub functionality
From: Lakshay Piplani @ 2026-06-12 11:18 UTC (permalink / raw)
To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
Cc: vikash.bansal, priyanka.jain, aman.kumarpandey, Lakshay Piplani
In-Reply-To: <20260612111816.3688240-1-lakshay.piplani@nxp.com>
From: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Add I3C hub functionality for the NXP P3H2x4x family of multiport hubs.
These devices support downstream target ports that can be configured
as I3C, I2C, or SMBus.
This driver enables:
- I3C/I2C communication between host and hub
- Transparent communication with downstream devices
- Target port configuration (I3C/I2C/SMBus)
P3H2440/P3H2441 support 4 target ports.
P3H2840/P3H2841 support 8 target ports.
Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
Changes in v11:
- Fix IBI resource cleanup on error paths
- Fix adapter unregister cleanup handling
Changes in v10:
- Split SMBus target/slave mode support, including IBI and MCTP receive
handling, into a separate patch
Changes in v9:
- Added CONFIG_I2C_SLAVE guards where necessary to avoid build issues
when I2C slave support is disabled.
Changes in v8:
- No change
Changes in v7:
- Remove CONFIG_I2C_SLAVE guards
- Use Kernel API find_closest instead of custom helper
- Use devm_regulator_get_enable_optional()
- Fix kernel-doc warnings
Changes in v6:
- Remove generic I3C code and keep reg dependent code only.
Changes in v5:
- Updated supply names.
Changes in v4:
- Split the driver into three separate patches (mfd, regulator and I3C hub)
- Added support for NXP P3H2x4x I3C hub functionality
- Integrated hub driver with its on-die regulator
Changes in v3:
- Added MFD (Multi-Function Device) support for I3C hub and on-die regulator
Changes in v2:
- Refined coding style and incorporated review feedback
- Updated directory structure
- Revised logic for parsing DTS nodes
---
---
MAINTAINERS | 1 +
drivers/i3c/Kconfig | 1 +
drivers/i3c/Makefile | 1 +
drivers/i3c/hub/Kconfig | 11 +
drivers/i3c/hub/Makefile | 4 +
drivers/i3c/hub/p3h2840_i3c_hub.h | 327 +++++++++++++++++++++
drivers/i3c/hub/p3h2840_i3c_hub_common.c | 352 +++++++++++++++++++++++
drivers/i3c/hub/p3h2840_i3c_hub_i3c.c | 124 ++++++++
drivers/i3c/hub/p3h2840_i3c_hub_smbus.c | 289 +++++++++++++++++++
9 files changed, 1110 insertions(+)
create mode 100644 drivers/i3c/hub/Kconfig
create mode 100644 drivers/i3c/hub/Makefile
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub.h
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_common.c
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
create mode 100644 drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a0b7fd85f8e3..718c5fca4746 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19312,6 +19312,7 @@ L: linux-i3c@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/i3c/nxp,p3h2840.yaml
F: drivers/i3c/hub.c
+F: drivers/i3c/hub/*
F: drivers/mfd/p3h2840.c
F: drivers/regulator/p3h2840_i3c_hub_regulator.c
F: include/linux/i3c/hub.h
diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 65304b416bb4..74727d614492 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -36,6 +36,7 @@ config I3C_HUB
Say Y here if your platform includes an I3C hub device
+source "drivers/i3c/hub/Kconfig"
endif # I3C
config I3C_OR_I2C
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 9ddee56a6338..2950820db9ea 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -3,3 +3,4 @@ i3c-y := device.o master.o
obj-$(CONFIG_I3C) += i3c.o
obj-$(CONFIG_I3C) += master/
obj-$(CONFIG_I3C_HUB) += hub.o
+obj-$(CONFIG_I3C_HUB) += hub/
diff --git a/drivers/i3c/hub/Kconfig b/drivers/i3c/hub/Kconfig
new file mode 100644
index 000000000000..f725f3e2bfbe
--- /dev/null
+++ b/drivers/i3c/hub/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2025 NXP
+config P3H2X4X_I3C_HUB
+ tristate "NXP P3H2X4X I3C HUB support"
+ depends on MFD_P3H2X4X
+ select I3C_HUB
+ help
+ This enables support for NXP P3H244x/P3H284x I3C HUB. These hubs
+ connect to a host via I3C/I2C/SMBus and allow communication with
+ multiple downstream peripherals. The Say Y or M here to use I3C
+ HUB driver to configure I3C HUB device.
diff --git a/drivers/i3c/hub/Makefile b/drivers/i3c/hub/Makefile
new file mode 100644
index 000000000000..9dbd8a7b4184
--- /dev/null
+++ b/drivers/i3c/hub/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2025 NXP
+p3h2840_i3c_hub-y := p3h2840_i3c_hub_common.o p3h2840_i3c_hub_i3c.o p3h2840_i3c_hub_smbus.o
+obj-$(CONFIG_P3H2X4X_I3C_HUB) += p3h2840_i3c_hub.o
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub.h b/drivers/i3c/hub/p3h2840_i3c_hub.h
new file mode 100644
index 000000000000..d69fafbac584
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub.h
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2025-2026 NXP
+ * This header file contain private device structure definition.
+ */
+
+#ifndef P3H2840_I3C_HUB_H
+#define P3H2840_I3C_HUB_H
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/hub.h>
+#include <linux/i3c/master.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+
+/* I3C HUB REGISTERS */
+
+/* Device Information Registers */
+#define P3H2X4X_DEV_INFO_0 0x00
+#define P3H2X4X_DEV_INFO_1 0x01
+#define P3H2X4X_PID_5 0x02
+#define P3H2X4X_PID_4 0x03
+#define P3H2X4X_PID_3 0x04
+#define P3H2X4X_PID_2 0x05
+#define P3H2X4X_PID_1 0x06
+#define P3H2X4X_PID_0 0x07
+#define P3H2X4X_BCR 0x08
+#define P3H2X4X_DCR 0x09
+#define P3H2X4X_DEV_CAPAB 0x0a
+#define P3H2X4X_DEV_REV 0x0b
+
+/* Device Configuration Registers */
+#define P3H2X4X_CP_CONF 0x11
+#define P3H2X4X_TP_ENABLE 0x12
+
+#define P3H2X4X_DEV_CONF 0x13
+#define P3H2X4X_IO_STRENGTH 0x14
+#define P3H2X4X_TP0145_IO_STRENGTH_MASK GENMASK(1, 0)
+#define P3H2X4X_TP0145_IO_STRENGTH(x) \
+ FIELD_PREP(P3H2X4X_TP0145_IO_STRENGTH_MASK, x)
+#define P3H2X4X_TP2367_IO_STRENGTH_MASK GENMASK(3, 2)
+#define P3H2X4X_TP2367_IO_STRENGTH(x) \
+ FIELD_PREP(P3H2X4X_TP2367_IO_STRENGTH_MASK, x)
+#define P3H2X4X_CP0_IO_STRENGTH_MASK GENMASK(5, 4)
+#define P3H2X4X_CP0_IO_STRENGTH(x) \
+ FIELD_PREP(P3H2X4X_CP0_IO_STRENGTH_MASK, x)
+#define P3H2X4X_CP1_IO_STRENGTH_MASK GENMASK(7, 6)
+#define P3H2X4X_CP1_IO_STRENGTH(x) \
+ FIELD_PREP(P3H2X4X_CP1_IO_STRENGTH_MASK, x)
+#define P3H2X4X_IO_STRENGTH_MASK GENMASK(7, 0)
+
+#define P3H2X4X_TP_IO_MODE_CONF 0x17
+#define P3H2X4X_TP_SMBUS_AGNT_EN 0x18
+
+#define P3H2X4X_LDO_AND_PULLUP_CONF 0x19
+
+#define P3H2X4X_TP0145_PULLUP_CONF_MASK GENMASK(7, 6)
+#define P3H2X4X_TP0145_PULLUP_CONF(x) \
+ FIELD_PREP(P3H2X4X_TP0145_PULLUP_CONF_MASK, x)
+#define P3H2X4X_TP2367_PULLUP_CONF_MASK GENMASK(5, 4)
+#define P3H2X4X_TP2367_PULLUP_CONF(x) \
+ FIELD_PREP(P3H2X4X_TP2367_PULLUP_CONF_MASK, x)
+#define P3H2X4X_PULLUP_CONF_MASK GENMASK(7, 4)
+
+#define P3H2X4X_CP_IBI_CONF 0x1a
+
+#define P3H2X4X_TP_SMBUS_AGNT_IBI_CONFIG 0x1b
+
+#define P3H2X4X_IBI_MDB_CUSTOM 0x1c
+#define P3H2X4X_JEDEC_CONTEXT_ID 0x1d
+#define P3H2X4X_TP_GPIO_MODE_EN 0x1e
+
+/* Device Status and IBI Registers */
+#define P3H2X4X_DEV_AND_IBI_STS 0x20
+#define P3H2X4X_TP_SMBUS_AGNT_IBI_STS 0x21
+#define P3H2X4X_SMBUS_AGENT_EVENT_FLAG_STATUS BIT(4)
+
+/* Controller Port Control/Status Registers */
+#define P3H2X4X_CP_MUX_SET 0x38
+#define P3H2X4X_CONTROLLER_PORT_MUX_REQ BIT(0)
+#define P3H2X4X_CP_MUX_STS 0x39
+#define P3H2X4X_CONTROLLER_PORT_MUX_CONNECTION_STATUS BIT(0)
+
+/* Target Ports Control Registers */
+#define P3H2X4X_TP_SMBUS_AGNT_TRANS_START 0x50
+#define P3H2X4X_TP_NET_CON_CONF 0x51
+
+#define P3H2X4X_TP_PULLUP_EN 0x53
+
+#define P3H2X4X_TP_SCL_OUT_EN 0x54
+#define P3H2X4X_TP_SDA_OUT_EN 0x55
+#define P3H2X4X_TP_SCL_OUT_LEVEL 0x56
+#define P3H2X4X_TP_SDA_OUT_LEVEL 0x57
+#define P3H2X4X_TP_IN_DETECT_MODE_CONF 0x58
+#define P3H2X4X_TP_SCL_IN_DETECT_IBI_EN 0x59
+#define P3H2X4X_TP_SDA_IN_DETECT_IBI_EN 0x5a
+
+/* Target Ports Status Registers */
+#define P3H2X4X_TP_SCL_IN_LEVEL_STS 0x60
+#define P3H2X4X_TP_SDA_IN_LEVEL_STS 0x61
+#define P3H2X4X_TP_SCL_IN_DETECT_FLG 0x62
+#define P3H2X4X_TP_SDA_IN_DETECT_FLG 0x63
+
+/* SMBus Agent Configuration and Status Registers */
+#define P3H2X4X_TP0_SMBUS_AGNT_STS 0x64
+#define P3H2X4X_TP1_SMBUS_AGNT_STS 0x65
+#define P3H2X4X_TP2_SMBUS_AGNT_STS 0x66
+#define P3H2X4X_TP3_SMBUS_AGNT_STS 0x67
+#define P3H2X4X_TP4_SMBUS_AGNT_STS 0x68
+#define P3H2X4X_TP5_SMBUS_AGNT_STS 0x69
+#define P3H2X4X_TP6_SMBUS_AGNT_STS 0x6a
+#define P3H2X4X_TP7_SMBUS_AGNT_STS 0x6b
+#define P3H2X4X_ONCHIP_TD_AND_SMBUS_AGNT_CONF 0x6c
+
+/* buf receive flag set */
+#define P3H2X4X_TARGET_BUF_CA_TF BIT(0)
+#define P3H2X4X_TARGET_BUF_0_RECEIVE BIT(1)
+#define P3H2X4X_TARGET_BUF_1_RECEIVE BIT(2)
+#define P3H2X4X_TARGET_BUF_0_1_RECEIVE GENMASK(2, 1)
+#define P3H2X4X_TARGET_BUF_OVRFL GENMASK(3, 1)
+#define BUF_RECEIVED_FLAG_MASK GENMASK(3, 1)
+#define BUF_RECEIVED_FLAG_TF_MASK GENMASK(3, 0)
+
+#define P3H2X4X_TARGET_AGENT_LOCAL_DEV 0x11
+#define P3H2X4X_TARGET_BUFF_0_PAGE 0x12
+#define P3H2X4X_TARGET_BUFF_1_PAGE 0x13
+
+/* Special Function Registers */
+#define P3H2X4X_LDO_AND_CPSEL_STS 0x79
+#define P3H2X4X_CP_SDA1_LEVEL BIT(7)
+#define P3H2X4X_CP_SCL1_LEVEL BIT(6)
+
+#define P3H2X4X_CP_SEL_PIN_INPUT_CODE_MASK GENMASK(5, 4)
+#define P3H2X4X_CP_SEL_PIN_INPUT_CODE_GET(x) \
+ (((x) & P3H2X4X_CP_SEL_PIN_INPUT_CODE_MASK) >> 4)
+#define P3H2X4X_CP_SDA1_SCL1_PINS_CODE_MASK GENMASK(7, 6)
+#define P3H2X4X_CP_SDA1_SCL1_PINS_CODE_GET(x) \
+ (((x) & P3H2X4X_CP_SDA1_SCL1_PINS_CODE_MASK) >> 6)
+#define P3H2X4X_VCCIO1_PWR_GOOD BIT(3)
+#define P3H2X4X_VCCIO0_PWR_GOOD BIT(2)
+#define P3H2X4X_CP1_VCCIO_PWR_GOOD BIT(1)
+#define P3H2X4X_CP0_VCCIO_PWR_GOOD BIT(0)
+
+#define P3H2X4X_BUS_RESET_SCL_TIMEOUT 0x7a
+#define P3H2X4X_ONCHIP_TD_PROTO_ERR_FLG 0x7b
+#define P3H2X4X_DEV_CMD 0x7c
+#define P3H2X4X_ONCHIP_TD_STS 0x7d
+#define P3H2X4X_ONCHIP_TD_ADDR_CONF 0x7e
+#define P3H2X4X_PAGE_PTR 0x7f
+
+/* Paged Transaction Registers */
+#define P3H2X4X_CONTROLLER_BUFFER_PAGE 0x10
+#define P3H2X4X_CONTROLLER_AGENT_BUFF 0x80
+#define P3H2X4X_CONTROLLER_AGENT_BUFF_DATA 0x84
+
+#define P3H2X4X_TARGET_BUFF_LENGTH 0x80
+#define P3H2X4X_TARGET_BUFF_ADDRESS 0x81
+#define P3H2X4X_TARGET_BUFF_DATA 0x82
+
+#define P3H2X4X_TP_MAX_COUNT 0x08
+#define P3H2X4X_CP_MAX_COUNT 0x02
+#define P3H2X4X_TP_LOCAL_DEV 0x08
+
+/* LDO Disable/Enable DT settings */
+#define P3H2X4X_LDO_VOLT_1_0V 0x00
+#define P3H2X4X_LDO_VOLT_1_1V 0x01
+#define P3H2X4X_LDO_VOLT_1_2V 0x02
+#define P3H2X4X_LDO_VOLT_1_8V 0x03
+
+#define P3H2X4X_LDO_DISABLED 0x00
+#define P3H2X4X_LDO_ENABLED 0x01
+
+#define P3H2X4X_IBI_DISABLED 0x00
+#define P3H2X4X_IBI_ENABLED 0x01
+
+/* Pullup selection DT settings */
+#define P3H2X4X_TP_PULLUP_250R 0x00
+#define P3H2X4X_TP_PULLUP_500R 0x01
+#define P3H2X4X_TP_PULLUP_1000R 0x02
+#define P3H2X4X_TP_PULLUP_2000R 0x03
+
+#define P3H2X4X_TP_PULLUP_DISABLED 0x00
+#define P3H2X4X_TP_PULLUP_ENABLED 0x01
+
+#define P3H2X4X_IO_STRENGTH_20_OHM 0x00
+#define P3H2X4X_IO_STRENGTH_30_OHM 0x01
+#define P3H2X4X_IO_STRENGTH_40_OHM 0x02
+#define P3H2X4X_IO_STRENGTH_50_OHM 0x03
+
+#define P3H2X4X_TP_MODE_I3C 0x00
+#define P3H2X4X_TP_MODE_SMBUS 0x01
+#define P3H2X4X_TP_MODE_GPIO 0x02
+#define P3H2X4X_TP_MODE_I2C 0x03
+
+#define ONE_BYTE_SIZE 0x01
+
+/* holding SDA low when both SMBus Target Agent received data buffers are full.
+ * This feature can be used as a flow-control mechanism for MCTP applications to
+ * avoid MCTP transmitters on Target Ports time out when the SMBus agent buffers
+ * are not serviced in time by upstream controller and only receives write message
+ * from its downstream ports.
+ * SMBUS_AGENT_TX_RX_LOOPBACK_EN/TARGET_AGENT_BUF_FULL_SDA_LOW_EN
+ */
+
+#define P3H2X4X_TARGET_AGENT_DFT_IBI_CONF 0x20
+#define P3H2X4X_TARGET_AGENT_DFT_IBI_CONF_MASK 0x21
+
+/* Transaction status checking mask */
+
+#define P3H2X4X_SMBUS_TRANSACTION_FINISH_FLAG 1
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_SHIFT 4
+
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_OK 0
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_ADDR_NAK 1
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_DATA_NAK 2
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_WTR_NAK 3
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_SYNC_RCV 4
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_SYNC_RCVCLR 5
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_FAULT 6
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_ARB_LOSS 7
+#define P3H2X4X_SMBUS_CNTRL_STATUS_TXN_SCL_TO 8
+
+#define P3H2X4X_TP_BUFFER_STATUS_MASK 0x0f
+#define P3H2X4X_TP_TRANSACTION_CODE_MASK 0xf0
+
+/* SMBus transaction types fields */
+#define P3H2X4X_SMBUS_400kHz BIT(2)
+
+/* SMBus polling */
+#define P3H2X4X_POLLING_ROLL_PERIOD_MS 10
+
+/* Hub buffer size */
+#define P3H2X4X_CONTROLLER_BUFFER_SIZE 88
+#define P3H2X4X_TARGET_BUFFER_SIZE 80
+#define P3H2X4X_SMBUS_DESCRIPTOR_SIZE 4
+#define P3H2X4X_SMBUS_PAYLOAD_SIZE \
+ (P3H2X4X_CONTROLLER_BUFFER_SIZE - P3H2X4X_SMBUS_DESCRIPTOR_SIZE)
+#define P3H2X4X_SMBUS_TARGET_PAYLOAD_SIZE (P3H2X4X_TARGET_BUFFER_SIZE - 2)
+
+/* Hub SMBus transaction time */
+#define P3H2X4X_SMBUS_400kHz_TRANSFER_TIMEOUT(x) ((20 * (x)) + 80)
+
+#define P3H2X4X_NO_PAGE_PER_TP 4
+
+#define P3H2X4X_MAX_PAYLOAD_LEN 2
+#define P3H2X4X_NUM_SLOTS 6
+
+#define P3H2X4X_HUB_ID 0
+
+#define P3H2X4X_SET_BIT(n) BIT(n)
+
+enum p3h2x4x_tp {
+ TP_0,
+ TP_1,
+ TP_2,
+ TP_3,
+ TP_4,
+ TP_5,
+ TP_6,
+ TP_7,
+};
+
+enum p3h2x4x_rcv_buf {
+ RCV_BUF_0,
+ RCV_BUF_1,
+ RCV_BUF_OF,
+};
+
+struct tp_configuration {
+ bool pullup_en;
+ bool ibi_en;
+ bool always_enable;
+ int mode;
+};
+
+struct hub_configuration {
+ int tp0145_pullup;
+ int tp2367_pullup;
+ int cp0_io_strength;
+ int cp1_io_strength;
+ int tp0145_io_strength;
+ int tp2367_io_strength;
+ struct tp_configuration tp_config[P3H2X4X_TP_MAX_COUNT];
+};
+
+struct tp_bus {
+ bool is_registered; /* bus was registered in the framework. */
+ u8 tp_mask;
+ u8 tp_port;
+ struct mutex port_mutex; /* per port mutex */
+ struct device_node *of_node;
+ struct i2c_client *tp_smbus_client;
+ struct i2c_adapter *tp_smbus_adapter;
+ struct i3c_hub_controller hub_controller;
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub;
+};
+
+struct p3h2x4x_i3c_hub_dev {
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex etx_mutex; /* all port mutex */
+ struct i3c_device *i3cdev;
+ struct i2c_client *i2c_client;
+ struct hub_configuration hub_config;
+ struct tp_bus tp_bus[P3H2X4X_TP_MAX_COUNT];
+ struct i3c_hub *hub;
+};
+
+/**
+ * p3h2x4x_tp_smbus_algo - add i2c adapter for target port configured as SMBus.
+ * @priv: p3h2x4x device structure.
+ * @tp: target port.
+ * Return: 0 in case of success, a negative EINVAL code if the error.
+ */
+int p3h2x4x_tp_smbus_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub);
+
+/**
+ * p3h2x4x_tp_i3c_algo - register i3c controller for target port configured as I3C.
+ * @priv: p3h2x4x device structure.
+ * @tp: target port.
+ * Return: 0 in case of success, a negative EINVAL code if the error.
+ */
+int p3h2x4x_tp_i3c_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub);
+
+#endif /* P3H2840_I3C_HUB_H */
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_common.c b/drivers/i3c/hub/p3h2840_i3c_hub_common.c
new file mode 100644
index 000000000000..fedcb978f07e
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_common.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025-2026 NXP
+ * This P3H2X4X driver file implements functions for Hub probe and DT parsing.
+ */
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/p3h2840.h>
+#include <linux/util_macros.h>
+
+#include "p3h2840_i3c_hub.h"
+
+/* LDO voltage DT settings */
+#define P3H2X4X_DT_LDO_VOLT_1_0V 1000000
+#define P3H2X4X_DT_LDO_VOLT_1_1V 1100000
+#define P3H2X4X_DT_LDO_VOLT_1_2V 1200000
+#define P3H2X4X_DT_LDO_VOLT_1_8V 1800000
+
+static const int p3h2x4x_pullup_tbl[] = {
+ 250, 500, 1000, 2000
+};
+
+static const int p3h2x4x_io_strength_tbl[] = {
+ 20, 30, 40, 50
+};
+
+static u8 p3h2x4x_pullup_dt_to_reg(int dt_value)
+{
+ return find_closest(dt_value, p3h2x4x_pullup_tbl,
+ ARRAY_SIZE(p3h2x4x_pullup_tbl));
+}
+
+static u8 p3h2x4x_io_strength_dt_to_reg(int dt_value)
+{
+ return find_closest(dt_value, p3h2x4x_io_strength_tbl,
+ ARRAY_SIZE(p3h2x4x_io_strength_tbl));
+}
+
+static int p3h2x4x_configure_pullup(struct device *dev)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+ u8 pullup;
+
+ pullup = P3H2X4X_TP0145_PULLUP_CONF(p3h2x4x_pullup_dt_to_reg
+ (p3h2x4x_i3c_hub->hub_config.tp0145_pullup));
+
+ pullup |= P3H2X4X_TP2367_PULLUP_CONF(p3h2x4x_pullup_dt_to_reg
+ (p3h2x4x_i3c_hub->hub_config.tp2367_pullup));
+
+ return regmap_update_bits(p3h2x4x_i3c_hub->regmap, P3H2X4X_LDO_AND_PULLUP_CONF,
+ P3H2X4X_PULLUP_CONF_MASK, pullup);
+}
+
+static int p3h2x4x_configure_io_strength(struct device *dev)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+ u8 io_strength;
+
+ io_strength = P3H2X4X_CP0_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+ (p3h2x4x_i3c_hub->hub_config.cp0_io_strength));
+
+ io_strength |= P3H2X4X_CP1_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+ (p3h2x4x_i3c_hub->hub_config.cp1_io_strength));
+
+ io_strength |= P3H2X4X_TP0145_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+ (p3h2x4x_i3c_hub->hub_config.tp0145_io_strength));
+
+ io_strength |= P3H2X4X_TP2367_IO_STRENGTH(p3h2x4x_io_strength_dt_to_reg
+ (p3h2x4x_i3c_hub->hub_config.tp2367_io_strength));
+
+ return regmap_update_bits(p3h2x4x_i3c_hub->regmap, P3H2X4X_IO_STRENGTH,
+ P3H2X4X_IO_STRENGTH_MASK, io_strength);
+}
+
+static int p3h2x4x_configure_ldo(struct device *dev)
+{
+ static const char * const supplies[] = {
+ "vcc1",
+ "vcc2",
+ "vcc3",
+ "vcc4"
+ };
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(supplies); i++) {
+ ret = devm_regulator_get_enable_optional(dev->parent, supplies[i]);
+ if (ret == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (ret && ret != -ENODEV)
+ dev_warn(dev, "Failed to enable %s (%d)\n",
+ supplies[i], ret);
+ }
+
+ /* This delay is required for the regulator to stabilize its output voltage */
+ fsleep(5000);
+
+ return 0;
+}
+
+static int p3h2x4x_configure_tp(struct device *dev)
+{
+ struct p3h2x4x_i3c_hub_dev *hub = dev_get_drvdata(dev);
+ u8 mode = 0, smbus = 0, pullup = 0, target_port = 0;
+ int tp, ret;
+
+ for (tp = 0; tp < P3H2X4X_TP_MAX_COUNT; tp++) {
+ pullup |= hub->hub_config.tp_config[tp].pullup_en ? P3H2X4X_SET_BIT(tp) : 0;
+ mode |= (hub->hub_config.tp_config[tp].mode != P3H2X4X_TP_MODE_I3C) ?
+ P3H2X4X_SET_BIT(tp) : 0;
+ smbus |= (hub->hub_config.tp_config[tp].mode == P3H2X4X_TP_MODE_SMBUS) ?
+ P3H2X4X_SET_BIT(tp) : 0;
+ target_port |= (hub->tp_bus[tp].tp_mask == P3H2X4X_SET_BIT(tp)) ?
+ hub->tp_bus[tp].tp_mask : 0;
+ }
+
+ ret = regmap_update_bits(hub->regmap, P3H2X4X_TP_PULLUP_EN, pullup, pullup);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(hub->regmap, P3H2X4X_TP_IO_MODE_CONF, mode, mode);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(hub->regmap, P3H2X4X_TP_SMBUS_AGNT_EN, smbus, smbus);
+ if (ret)
+ return ret;
+
+ if (target_port & ~smbus) {
+ ret = regmap_write(hub->regmap, P3H2X4X_CP_MUX_SET,
+ P3H2X4X_CONTROLLER_PORT_MUX_REQ);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_update_bits(hub->regmap, P3H2X4X_TP_ENABLE, target_port, target_port);
+}
+
+static int p3h2x4x_configure_hw(struct device *dev)
+{
+ int ret;
+
+ ret = p3h2x4x_configure_ldo(dev);
+ if (ret)
+ return ret;
+
+ ret = p3h2x4x_configure_pullup(dev);
+ if (ret)
+ return ret;
+
+ ret = p3h2x4x_configure_io_strength(dev);
+ if (ret)
+ return ret;
+
+ return p3h2x4x_configure_tp(dev);
+}
+
+static void p3h2x4x_get_target_port_dt_conf(struct device *dev,
+ const struct device_node *node)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+ u64 tp_port;
+
+ for_each_available_child_of_node_scoped(node, dev_node) {
+ if (of_property_read_reg(dev_node, 0, &tp_port, NULL))
+ continue;
+
+ if (tp_port < P3H2X4X_TP_MAX_COUNT) {
+ p3h2x4x_i3c_hub->tp_bus[tp_port].of_node = of_node_get(dev_node);
+ p3h2x4x_i3c_hub->tp_bus[tp_port].tp_mask = P3H2X4X_SET_BIT(tp_port);
+ p3h2x4x_i3c_hub->tp_bus[tp_port].p3h2x4x_i3c_hub = p3h2x4x_i3c_hub;
+ p3h2x4x_i3c_hub->tp_bus[tp_port].tp_port = tp_port;
+ }
+ }
+}
+
+static void p3h2x4x_parse_tp_dt_settings(struct device *dev,
+ const struct device_node *node,
+ struct tp_configuration tp_config[])
+{
+ u64 id;
+
+ for_each_available_child_of_node_scoped(node, tp_node) {
+ if (of_property_read_reg(tp_node, 0, &id, NULL))
+ continue;
+
+ if (id >= P3H2X4X_TP_MAX_COUNT) {
+ dev_warn(dev, "Invalid target port index found in DT: %lli\n", id);
+ continue;
+ }
+
+ if (strcmp(tp_node->name, "i3c") == 0)
+ tp_config[id].mode = P3H2X4X_TP_MODE_I3C;
+
+ if (strcmp(tp_node->name, "i2c") == 0)
+ tp_config[id].mode = P3H2X4X_TP_MODE_I2C;
+
+ if (strcmp(tp_node->name, "smbus") == 0)
+ tp_config[id].mode = P3H2X4X_TP_MODE_SMBUS;
+
+ tp_config[id].pullup_en =
+ of_property_read_bool(tp_node, "nxp,pullup-enable");
+ }
+}
+
+static void p3h2x4x_get_hub_dt_conf(struct device *dev,
+ const struct device_node *node)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+
+ of_property_read_u32(node, "nxp,tp0145-pullup-ohms",
+ &p3h2x4x_i3c_hub->hub_config.tp0145_pullup);
+ of_property_read_u32(node, "nxp,tp2367-pullup-ohms",
+ &p3h2x4x_i3c_hub->hub_config.tp2367_pullup);
+ of_property_read_u32(node, "nxp,cp0-io-strength-ohms",
+ &p3h2x4x_i3c_hub->hub_config.cp0_io_strength);
+ of_property_read_u32(node, "nxp,cp1-io-strength-ohms",
+ &p3h2x4x_i3c_hub->hub_config.cp1_io_strength);
+ of_property_read_u32(node, "nxp,tp0145-io-strength-ohms",
+ &p3h2x4x_i3c_hub->hub_config.tp0145_io_strength);
+ of_property_read_u32(node, "nxp,tp2367-io-strength-ohms",
+ &p3h2x4x_i3c_hub->hub_config.tp2367_io_strength);
+
+ p3h2x4x_parse_tp_dt_settings(dev, node, p3h2x4x_i3c_hub->hub_config.tp_config);
+}
+
+static void p3h2x4x_default_configuration(struct device *dev)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = dev_get_drvdata(dev);
+ int tp_count;
+
+ p3h2x4x_i3c_hub->hub_config.tp0145_pullup = P3H2X4X_TP_PULLUP_500R;
+ p3h2x4x_i3c_hub->hub_config.tp2367_pullup = P3H2X4X_TP_PULLUP_500R;
+ p3h2x4x_i3c_hub->hub_config.cp0_io_strength = P3H2X4X_IO_STRENGTH_20_OHM;
+ p3h2x4x_i3c_hub->hub_config.cp1_io_strength = P3H2X4X_IO_STRENGTH_20_OHM;
+ p3h2x4x_i3c_hub->hub_config.tp0145_io_strength = P3H2X4X_IO_STRENGTH_20_OHM;
+ p3h2x4x_i3c_hub->hub_config.tp2367_io_strength = P3H2X4X_IO_STRENGTH_20_OHM;
+
+ for (tp_count = 0; tp_count < P3H2X4X_TP_MAX_COUNT; ++tp_count)
+ p3h2x4x_i3c_hub->hub_config.tp_config[tp_count].mode = P3H2X4X_TP_MODE_I3C;
+}
+
+static int p3h2x4x_i3c_hub_probe(struct platform_device *pdev)
+{
+ struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub;
+ struct device *dev = &pdev->dev;
+ struct device_node *node;
+ int ret, i;
+
+ p3h2x4x_i3c_hub = devm_kzalloc(dev, sizeof(*p3h2x4x_i3c_hub), GFP_KERNEL);
+ if (!p3h2x4x_i3c_hub)
+ return -ENOMEM;
+
+ p3h2x4x_i3c_hub->regmap = p3h2x4x->regmap;
+ p3h2x4x_i3c_hub->dev = dev;
+
+ platform_set_drvdata(pdev, p3h2x4x_i3c_hub);
+
+ p3h2x4x_default_configuration(dev);
+
+ ret = devm_mutex_init(dev, &p3h2x4x_i3c_hub->etx_mutex);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < P3H2X4X_TP_MAX_COUNT; i++) {
+ ret = devm_mutex_init(dev, &p3h2x4x_i3c_hub->tp_bus[i].port_mutex);
+ if (ret)
+ return ret;
+ }
+
+ /* get hub node from DT */
+ node = dev->parent->of_node;
+ if (!node)
+ return dev_err_probe(dev, -ENODEV, "No Device Tree entry found\n");
+
+ p3h2x4x_get_hub_dt_conf(dev, node);
+ p3h2x4x_get_target_port_dt_conf(dev, node);
+
+ /* Unlock access to protected registers */
+ ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_DEV_REG_PROTECTION_CODE,
+ P3H2X4X_REGISTERS_UNLOCK_CODE);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to unlock HUB's protected registers\n");
+
+ ret = p3h2x4x_configure_hw(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to configure the HUB\n");
+
+ /* Register virtual I3C master controllers for I3C target ports */
+ if (p3h2x4x->i3cdev) {
+ p3h2x4x_i3c_hub->i3cdev = p3h2x4x->i3cdev;
+ i3cdev_set_drvdata(p3h2x4x->i3cdev, p3h2x4x_i3c_hub);
+ ret = p3h2x4x_tp_i3c_algo(p3h2x4x_i3c_hub);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register i3c bus\n");
+ }
+
+ /* Register virtual I2C adapters for SMBus target ports */
+ ret = p3h2x4x_tp_smbus_algo(p3h2x4x_i3c_hub);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add i2c adapter\n");
+
+ /* Lock access to protected registers */
+ ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_DEV_REG_PROTECTION_CODE,
+ P3H2X4X_REGISTERS_LOCK_CODE);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to lock HUB's protected registers\n");
+
+ return 0;
+}
+
+static void p3h2x4x_i3c_hub_remove(struct platform_device *pdev)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = platform_get_drvdata(pdev);
+ struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
+ u8 i;
+
+ for (i = 0; i < P3H2X4X_TP_MAX_COUNT; i++) {
+ if (p3h2x4x_i3c_hub->tp_bus[i].is_registered) {
+ if (p3h2x4x_i3c_hub->hub_config.tp_config[i].mode ==
+ P3H2X4X_TP_MODE_SMBUS) {
+ i2c_del_adapter(p3h2x4x_i3c_hub->tp_bus[i].tp_smbus_adapter);
+ p3h2x4x_i3c_hub->tp_bus[i].tp_smbus_adapter = NULL;
+ p3h2x4x_i3c_hub->tp_bus[i].is_registered = false;
+ }
+ }
+
+ if (p3h2x4x_i3c_hub->tp_bus[i].of_node) {
+ of_node_put(p3h2x4x_i3c_hub->tp_bus[i].of_node);
+ p3h2x4x_i3c_hub->tp_bus[i].of_node = NULL;
+ }
+ }
+}
+
+static struct platform_driver p3h2x4x_i3c_hub_driver = {
+ .driver = {
+ .name = "p3h2x4x-i3c-hub",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = p3h2x4x_i3c_hub_probe,
+ .remove = p3h2x4x_i3c_hub_remove,
+};
+module_platform_driver(p3h2x4x_i3c_hub_driver);
+
+MODULE_AUTHOR("Aman Kumar Pandey <aman.kumarpandey@nxp.com>");
+MODULE_AUTHOR("Vikash Bansal <vikash.bansal@nxp.com>");
+MODULE_AUTHOR("Lakshay Piplani <lakshay.piplani@nxp.com>");
+MODULE_DESCRIPTION("P3H2X4X I3C HUB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
new file mode 100644
index 000000000000..38505dda0e81
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025-2026 NXP
+ * This P3H2X4X driver file contain functions for I3C virtual Bus creation, connect/disconnect
+ * hub network and read/write.
+ */
+#include <linux/i3c/hub.h>
+#include <linux/mfd/p3h2840.h>
+#include <linux/regmap.h>
+
+#include "p3h2840_i3c_hub.h"
+
+static inline struct tp_bus *
+p3h2x4x_bus_from_controller(struct i3c_master_controller *controller)
+{
+ struct i3c_hub_controller *hub_controller;
+
+ hub_controller = container_of(controller, struct i3c_hub_controller, controller);
+
+ return container_of(hub_controller, struct tp_bus, hub_controller);
+}
+
+static void p3h2x4x_hub_enable_port(struct i3c_master_controller *controller)
+{
+ struct tp_bus *bus = p3h2x4x_bus_from_controller(controller);
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
+
+ if (p3h2x4x_i3c_hub->hub_config.tp_config[bus->tp_port].always_enable)
+ return;
+
+ regmap_set_bits(p3h2x4x_i3c_hub->regmap, P3H2X4X_TP_NET_CON_CONF, bus->tp_mask);
+}
+
+static void p3h2x4x_hub_disable_port(struct i3c_master_controller *controller)
+{
+ struct tp_bus *bus = p3h2x4x_bus_from_controller(controller);
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
+
+ if (p3h2x4x_i3c_hub->hub_config.tp_config[bus->tp_port].always_enable)
+ return;
+
+ regmap_clear_bits(p3h2x4x_i3c_hub->regmap, P3H2X4X_TP_NET_CON_CONF, bus->tp_mask);
+}
+
+static const struct i3c_hub_ops p3h2x4x_hub_ops = {
+ .enable_port = p3h2x4x_hub_enable_port,
+ .disable_port = p3h2x4x_hub_disable_port,
+};
+
+static void p3h2x4x_unregister_i3c_master(void *data)
+{
+ struct i3c_master_controller *controller = data;
+
+ i3c_master_unregister(controller);
+}
+
+/**
+ * p3h2x4x_tp_i3c_algo - register i3c master for target port who
+ * configured as i3c.
+ * @p3h2x4x_hub: p3h2x4x device structure.
+ * Return: 0 in case of success, negative error code on failur.
+ */
+int p3h2x4x_tp_i3c_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_hub)
+{
+ struct i3c_master_controller *parent = i3c_dev_get_master(p3h2x4x_hub->i3cdev->desc);
+ u8 tp, ntwk_mask = 0;
+ int ret;
+
+ p3h2x4x_hub->hub = devm_kzalloc(p3h2x4x_hub->dev,
+ sizeof(*p3h2x4x_hub->hub),
+ GFP_KERNEL);
+
+ if (!p3h2x4x_hub->hub)
+ return -ENOMEM;
+
+ i3c_hub_init(p3h2x4x_hub->hub,
+ &p3h2x4x_hub_ops,
+ p3h2x4x_hub->i3cdev);
+
+ if (IS_ERR(p3h2x4x_hub->hub))
+ return PTR_ERR(p3h2x4x_hub->hub);
+
+ for (tp = 0; tp < P3H2X4X_TP_MAX_COUNT; tp++) {
+ if (!p3h2x4x_hub->tp_bus[tp].of_node ||
+ p3h2x4x_hub->hub_config.tp_config[tp].mode != P3H2X4X_TP_MODE_I3C)
+ continue;
+
+ /* Assign DT node for this TP */
+ p3h2x4x_hub->dev->of_node = p3h2x4x_hub->tp_bus[tp].of_node;
+
+ struct i3c_hub_controller *hub_controller =
+ &p3h2x4x_hub->tp_bus[tp].hub_controller;
+ struct i3c_master_controller *controller = &hub_controller->controller;
+
+ hub_controller->parent = parent;
+ hub_controller->hub = p3h2x4x_hub->hub;
+
+ dev_set_drvdata(&controller->dev, hub_controller);
+
+ ret = i3c_master_register(controller,
+ p3h2x4x_hub->dev,
+ i3c_hub_master_ops(),
+ false);
+
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(p3h2x4x_hub->dev,
+ p3h2x4x_unregister_i3c_master,
+ controller);
+ if (ret)
+ return ret;
+
+ /* Perform DAA */
+ ret = i3c_master_do_daa(parent);
+ if (ret)
+ return ret;
+
+ ntwk_mask |= p3h2x4x_hub->tp_bus[tp].tp_mask;
+ p3h2x4x_hub->tp_bus[tp].is_registered = true;
+ p3h2x4x_hub->hub_config.tp_config[tp].always_enable = true;
+ }
+ return regmap_write(p3h2x4x_hub->regmap, P3H2X4X_TP_NET_CON_CONF, ntwk_mask);
+}
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c b/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
new file mode 100644
index 000000000000..edb75f790e92
--- /dev/null
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025-2026 NXP
+ * This P3H2X4X driver file contain functions for SMBus/I2C virtual Bus creation and read/write.
+ */
+#include <linux/mfd/p3h2840.h>
+#include <linux/regmap.h>
+
+#include "p3h2840_i3c_hub.h"
+
+enum p3h2x4x_smbus_desc_idx {
+ P3H2X4X_DESC_ADDR,
+ P3H2X4X_DESC_TYPE,
+ P3H2X4X_DESC_WRITE_LEN,
+ P3H2X4X_DESC_READ_LEN,
+};
+
+static int p3h2x4x_read_smbus_transaction_status(struct p3h2x4x_i3c_hub_dev *hub,
+ u8 target_port_status,
+ u8 data_length)
+{
+ u32 status_read;
+ u8 status;
+ int ret;
+
+ fsleep(P3H2X4X_SMBUS_400kHz_TRANSFER_TIMEOUT(data_length));
+
+ ret = regmap_read(hub->regmap, target_port_status, &status_read);
+ if (ret)
+ return ret;
+
+ status = (u8)status_read;
+
+ status = (status & P3H2X4X_TP_TRANSACTION_CODE_MASK)
+ >> P3H2X4X_SMBUS_CNTRL_STATUS_TXN_SHIFT;
+
+ switch (status) {
+ case P3H2X4X_SMBUS_CNTRL_STATUS_TXN_OK:
+ return 0;
+ case P3H2X4X_SMBUS_CNTRL_STATUS_TXN_ADDR_NAK:
+ return -ENXIO;
+ case P3H2X4X_SMBUS_CNTRL_STATUS_TXN_DATA_NAK:
+ return -EIO;
+ case P3H2X4X_SMBUS_CNTRL_STATUS_TXN_SCL_TO:
+ return -ETIMEDOUT;
+ case P3H2X4X_SMBUS_CNTRL_STATUS_TXN_ARB_LOSS:
+ return -EAGAIN;
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * p3h2x4x_tp_i2c_xfer_msg() - This starts a SMBus write transaction by writing a descriptor
+ * and a message to the p3h2x4x registers. Controller buffer page is determined by multiplying the
+ * target port index by four and adding the base page number to it.
+ */
+static int p3h2x4x_tp_i2c_xfer_msg(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub,
+ struct i2c_msg *xfers,
+ u8 target_port,
+ u8 nxfers_i, u8 rw)
+{
+ u8 controller_buffer_page = P3H2X4X_CONTROLLER_BUFFER_PAGE + 4 * target_port;
+ u8 target_port_status = P3H2X4X_TP0_SMBUS_AGNT_STS + target_port;
+ u8 desc[P3H2X4X_SMBUS_DESCRIPTOR_SIZE] = { 0 };
+ u8 transaction_type = P3H2X4X_SMBUS_400kHz;
+ int write_length, read_length;
+ u8 addr = xfers[nxfers_i].addr;
+ u8 rw_address = 2 * addr;
+ int ret, ret2;
+
+ if (rw == 2) { /* write and read */
+ write_length = xfers[nxfers_i].len;
+ read_length = xfers[nxfers_i + 1].len;
+ } else if (rw == 1) {
+ rw_address |= P3H2X4X_SET_BIT(0);
+ write_length = 0;
+ read_length = xfers[nxfers_i].len;
+ } else {
+ write_length = xfers[nxfers_i].len;
+ read_length = 0;
+ }
+
+ desc[P3H2X4X_DESC_ADDR] = rw_address;
+ if (rw == 2)
+ desc[P3H2X4X_DESC_TYPE] = transaction_type | P3H2X4X_SET_BIT(0);
+ else
+ desc[P3H2X4X_DESC_TYPE] = transaction_type;
+ desc[P3H2X4X_DESC_WRITE_LEN] = write_length;
+ desc[P3H2X4X_DESC_READ_LEN] = read_length;
+
+ ret = regmap_write(p3h2x4x_i3c_hub->regmap, target_port_status,
+ P3H2X4X_TP_BUFFER_STATUS_MASK);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_PAGE_PTR, controller_buffer_page);
+
+ if (ret)
+ goto out;
+
+ ret = regmap_bulk_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_CONTROLLER_AGENT_BUFF,
+ desc, P3H2X4X_SMBUS_DESCRIPTOR_SIZE);
+
+ if (ret)
+ goto out;
+
+ if (!(rw % 2) && xfers[nxfers_i].len) {
+ ret = regmap_bulk_write(p3h2x4x_i3c_hub->regmap,
+ P3H2X4X_CONTROLLER_AGENT_BUFF_DATA,
+ xfers[nxfers_i].buf, xfers[nxfers_i].len);
+ if (ret)
+ goto out;
+ }
+
+ ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_TP_SMBUS_AGNT_TRANS_START,
+ p3h2x4x_i3c_hub->tp_bus[target_port].tp_mask);
+
+ if (ret)
+ goto out;
+
+ ret = p3h2x4x_read_smbus_transaction_status(p3h2x4x_i3c_hub,
+ target_port_status,
+ (write_length + read_length));
+ if (ret)
+ goto out;
+
+ if (rw) {
+ if (rw == 2)
+ nxfers_i += 1;
+
+ if (xfers[nxfers_i].len) {
+ ret = regmap_bulk_read(p3h2x4x_i3c_hub->regmap,
+ P3H2X4X_CONTROLLER_AGENT_BUFF_DATA + write_length,
+ xfers[nxfers_i].buf, xfers[nxfers_i].len);
+ if (ret)
+ goto out;
+ }
+ }
+out:
+ ret2 = regmap_write(p3h2x4x_i3c_hub->regmap,
+ P3H2X4X_PAGE_PTR, 0x00);
+ if (!ret && ret2)
+ ret = ret2;
+
+ return ret;
+}
+
+/*
+ * This function will be called whenever you call I2C read, write APIs like
+ * i2c_master_send(), i2c_master_recv() etc.
+ */
+static s32 p3h2x4x_tp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ int ret_sum = 0, ret;
+ u8 msg_count, rw;
+
+ struct tp_bus *bus = i2c_get_adapdata(adap);
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = bus->p3h2x4x_i3c_hub;
+
+ guard(mutex)(&p3h2x4x_i3c_hub->etx_mutex);
+ guard(mutex)(&bus->port_mutex);
+
+ for (msg_count = 0; msg_count < num; msg_count++) {
+ if (msgs[msg_count].len > P3H2X4X_SMBUS_PAYLOAD_SIZE) {
+ dev_err(p3h2x4x_i3c_hub->dev,
+ "Message nr. %d not sent - length over %d bytes.\n",
+ msg_count, P3H2X4X_SMBUS_PAYLOAD_SIZE);
+ return -EINVAL;
+ }
+
+ rw = (msgs[msg_count].flags & I2C_M_RD) ? 1 : 0;
+ if (!rw) {
+ /* If a read message is immediately followed by a write message to
+ * the same address, consider combining them into a single transaction.
+ */
+ if (msg_count + 1 < num &&
+ msgs[msg_count].addr == msgs[msg_count + 1].addr &&
+ (msgs[msg_count + 1].flags & I2C_M_RD)) {
+ if (msgs[msg_count].len + msgs[msg_count + 1].len >
+ P3H2X4X_SMBUS_PAYLOAD_SIZE)
+ return -EINVAL;
+
+ rw = 2;
+ msg_count += 1;
+ ret_sum += 1;
+ }
+ }
+
+ ret = p3h2x4x_tp_i2c_xfer_msg(p3h2x4x_i3c_hub,
+ msgs,
+ bus->tp_port,
+ (rw == 2) ? (msg_count - 1) : msg_count,
+ rw);
+ if (ret)
+ return ret;
+
+ ret_sum++;
+ }
+ return ret_sum;
+}
+
+static u32 p3h2x4x_tp_smbus_funcs(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+/*
+ * I2C algorithm Structure
+ */
+static struct i2c_algorithm p3h2x4x_tp_i2c_algorithm = {
+ .master_xfer = p3h2x4x_tp_i2c_xfer,
+ .functionality = p3h2x4x_tp_smbus_funcs,
+};
+
+static void p3h2x4x_unregister_smbus_adapters(struct p3h2x4x_i3c_hub_dev *hub)
+{
+ u8 tp;
+
+ for (tp = 0; tp < P3H2X4X_TP_MAX_COUNT; tp++) {
+ if (!hub->tp_bus[tp].tp_smbus_adapter)
+ continue;
+
+ i2c_del_adapter(hub->tp_bus[tp].tp_smbus_adapter);
+ hub->tp_bus[tp].tp_smbus_adapter = NULL;
+ hub->tp_bus[tp].is_registered = false;
+ }
+}
+
+/**
+ * p3h2x4x_tp_smbus_algo - add i2c adapter for target port who
+ * configured as SMBus.
+ * @hub: p3h2x4x device structure.
+ * Return: 0 in case of success, negative error code on failur.
+ */
+int p3h2x4x_tp_smbus_algo(struct p3h2x4x_i3c_hub_dev *hub)
+{
+ int ret;
+ u8 tp;
+
+ for (tp = 0; tp < P3H2X4X_TP_MAX_COUNT; tp++) {
+ if (!hub->tp_bus[tp].of_node ||
+ hub->hub_config.tp_config[tp].mode != P3H2X4X_TP_MODE_SMBUS)
+ continue;
+
+ /* Allocate adapter */
+ struct i2c_adapter *smbus_adapter =
+ devm_kzalloc(hub->dev, sizeof(*smbus_adapter), GFP_KERNEL);
+ if (!smbus_adapter)
+ return -ENOMEM;
+
+ /* Initialize adapter */
+ smbus_adapter->owner = THIS_MODULE;
+ smbus_adapter->class = I2C_CLASS_HWMON;
+ smbus_adapter->algo = &p3h2x4x_tp_i2c_algorithm;
+ smbus_adapter->dev.parent = hub->dev;
+ smbus_adapter->dev.of_node = hub->tp_bus[tp].of_node;
+ snprintf(smbus_adapter->name, sizeof(smbus_adapter->name),
+ "p3h2x4x-i3c-hub.tp-port-%d", tp);
+
+ i2c_set_adapdata(smbus_adapter, &hub->tp_bus[tp]);
+
+ /* Register adapter */
+ ret = i2c_add_adapter(smbus_adapter);
+ if (ret) {
+ p3h2x4x_unregister_smbus_adapters(hub);
+ return ret;
+ }
+
+ hub->tp_bus[tp].is_registered = true;
+ hub->hub_config.tp_config[tp].ibi_en = false;
+ hub->tp_bus[tp].tp_smbus_adapter = smbus_adapter;
+ }
+
+ /*
+ * holding SDA low when both SMBus Target Agent received data buffers are full.
+ * This feature can be used as a flow-control mechanism for MCTP applications to
+ * avoid MCTP transmitters on Target Ports time out when the SMBus agent buffers
+ * are not serviced in time by upstream controller and only receives write message
+ * from its downstream ports.
+ */
+ ret = regmap_update_bits(hub->regmap, P3H2X4X_ONCHIP_TD_AND_SMBUS_AGNT_CONF,
+ P3H2X4X_TARGET_AGENT_DFT_IBI_CONF_MASK,
+ P3H2X4X_TARGET_AGENT_DFT_IBI_CONF);
+ if (ret)
+ return ret;
+
+ return regmap_write(hub->regmap, P3H2X4X_TP_SMBUS_AGNT_IBI_CONFIG, P3H2X4X_IBI_DISABLED);
+}
--
2.25.1
^ permalink raw reply related
* [PATCH v11 9/9] i3c: hub: p3h2x4x: Add SMBus slave mode support
From: Lakshay Piplani @ 2026-06-12 11:18 UTC (permalink / raw)
To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
Cc: vikash.bansal, priyanka.jain, aman.kumarpandey, Lakshay Piplani
In-Reply-To: <20260612111816.3688240-1-lakshay.piplani@nxp.com>
Add SMBus slave mode support for the P3H2x4x hub SMBus target ports.
The hub SMBus slave agent can receive downstream payloads into target
buffers and report receive events through IBI. Add CONFIG_I2C_SLAVE
to support the receive path and forward the received payloads to the
registered I2C slave client through i2c_slave_event().
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Vikash Bansal <vikash.bansal@nxp.com>
---
Changes in v11:
- Improve SMBus slave mode payload validation and parsing
Changes in v10:
- Split SMBus slave mode support into a separate patch
---
---
drivers/i3c/hub/p3h2840_i3c_hub.h | 10 ++
drivers/i3c/hub/p3h2840_i3c_hub_common.c | 7 +
drivers/i3c/hub/p3h2840_i3c_hub_i3c.c | 29 +++-
drivers/i3c/hub/p3h2840_i3c_hub_smbus.c | 192 +++++++++++++++++++++++
4 files changed, 237 insertions(+), 1 deletion(-)
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub.h b/drivers/i3c/hub/p3h2840_i3c_hub.h
index d69fafbac584..84d9c66547c6 100644
--- a/drivers/i3c/hub/p3h2840_i3c_hub.h
+++ b/drivers/i3c/hub/p3h2840_i3c_hub.h
@@ -324,4 +324,14 @@ int p3h2x4x_tp_smbus_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub);
*/
int p3h2x4x_tp_i3c_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub);
+/**
+ * p3h2x4x_ibi_handler - IBI handler.
+ * @i3cdev: i3c device.
+ * @payload: two byte IBI payload data.
+ */
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+void p3h2x4x_ibi_handler(struct i3c_device *i3cdev,
+ const struct i3c_ibi_payload *payload);
+#endif
+
#endif /* P3H2840_I3C_HUB_H */
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_common.c b/drivers/i3c/hub/p3h2840_i3c_hub_common.c
index fedcb978f07e..ca173afe1fa9 100644
--- a/drivers/i3c/hub/p3h2840_i3c_hub_common.c
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_common.c
@@ -318,6 +318,13 @@ static void p3h2x4x_i3c_hub_remove(struct platform_device *pdev)
struct p3h2x4x_dev *p3h2x4x = dev_get_drvdata(pdev->dev.parent);
u8 i;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (p3h2x4x->i3cdev) {
+ i3c_device_disable_ibi(p3h2x4x->i3cdev);
+ i3c_device_free_ibi(p3h2x4x->i3cdev);
+ }
+#endif
+
for (i = 0; i < P3H2X4X_TP_MAX_COUNT; i++) {
if (p3h2x4x_i3c_hub->tp_bus[i].is_registered) {
if (p3h2x4x_i3c_hub->hub_config.tp_config[i].mode ==
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
index 38505dda0e81..9bef8b8e557f 100644
--- a/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_i3c.c
@@ -10,6 +10,14 @@
#include "p3h2840_i3c_hub.h"
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static const struct i3c_ibi_setup p3h2x4x_ibireq = {
+ .handler = p3h2x4x_ibi_handler,
+ .max_payload_len = P3H2X4X_MAX_PAYLOAD_LEN,
+ .num_slots = P3H2X4X_NUM_SLOTS,
+};
+#endif
+
static inline struct tp_bus *
p3h2x4x_bus_from_controller(struct i3c_master_controller *controller)
{
@@ -120,5 +128,24 @@ int p3h2x4x_tp_i3c_algo(struct p3h2x4x_i3c_hub_dev *p3h2x4x_hub)
p3h2x4x_hub->tp_bus[tp].is_registered = true;
p3h2x4x_hub->hub_config.tp_config[tp].always_enable = true;
}
- return regmap_write(p3h2x4x_hub->regmap, P3H2X4X_TP_NET_CON_CONF, ntwk_mask);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ ret = i3c_device_request_ibi(p3h2x4x_hub->i3cdev, &p3h2x4x_ibireq);
+ if (ret)
+ return ret;
+
+ ret = i3c_device_enable_ibi(p3h2x4x_hub->i3cdev);
+ if (ret) {
+ i3c_device_free_ibi(p3h2x4x_hub->i3cdev);
+ return ret;
+ }
+#endif
+ ret = regmap_write(p3h2x4x_hub->regmap, P3H2X4X_TP_NET_CON_CONF, ntwk_mask);
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (ret) {
+ i3c_device_disable_ibi(p3h2x4x_hub->i3cdev);
+ i3c_device_free_ibi(p3h2x4x_hub->i3cdev);
+ }
+#endif
+ return ret;
}
diff --git a/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c b/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
index edb75f790e92..fba4345f86a7 100644
--- a/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
+++ b/drivers/i3c/hub/p3h2840_i3c_hub_smbus.c
@@ -15,6 +15,146 @@ enum p3h2x4x_smbus_desc_idx {
P3H2X4X_DESC_READ_LEN,
};
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void p3h2x4x_read_smbus_agent_rx_buf(struct i3c_device *i3cdev, enum p3h2x4x_rcv_buf rfbuf,
+ enum p3h2x4x_tp tp, bool is_of)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub = i3cdev_get_drvdata(i3cdev);
+ u8 slave_rx_buffer[P3H2X4X_SMBUS_TARGET_PAYLOAD_SIZE] = { 0 };
+ u8 target_buffer_page, flag_clear = 0x0f, temp = 0, i, addr;
+ u32 packet_len, slave_address, ret;
+ struct i2c_client *client;
+
+ target_buffer_page = (((rfbuf) ? P3H2X4X_TARGET_BUFF_1_PAGE : P3H2X4X_TARGET_BUFF_0_PAGE)
+ + (P3H2X4X_NO_PAGE_PER_TP * tp));
+ ret = regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_PAGE_PTR, target_buffer_page);
+ if (ret)
+ goto ibi_err;
+
+ /* read buffer length */
+ ret = regmap_read(p3h2x4x_i3c_hub->regmap, P3H2X4X_TARGET_BUFF_LENGTH, &packet_len);
+ if (ret)
+ goto ibi_err;
+
+ if (packet_len)
+ packet_len = packet_len - 1;
+
+ if (packet_len > P3H2X4X_SMBUS_TARGET_PAYLOAD_SIZE) {
+ dev_err(&i3cdev->dev, "Received message too big for p3h2x4x buffer\n");
+ goto ibi_err;
+ }
+
+ /* read slave address */
+ ret = regmap_read(p3h2x4x_i3c_hub->regmap, P3H2X4X_TARGET_BUFF_ADDRESS, &slave_address);
+ if (ret)
+ goto ibi_err;
+
+ /* read data */
+ if (packet_len) {
+ ret = regmap_bulk_read(p3h2x4x_i3c_hub->regmap, P3H2X4X_TARGET_BUFF_DATA,
+ slave_rx_buffer, packet_len);
+ if (ret)
+ goto ibi_err;
+ }
+
+ if (is_of)
+ flag_clear = BUF_RECEIVED_FLAG_TF_MASK;
+ else
+ flag_clear = (((rfbuf == RCV_BUF_0) ? P3H2X4X_TARGET_BUF_0_RECEIVE :
+ P3H2X4X_TARGET_BUF_1_RECEIVE));
+
+ /* notify slave driver about received data */
+ if ((p3h2x4x_i3c_hub->tp_bus[tp].tp_smbus_client->addr & 0x7f) == (slave_address >> 1)) {
+ client = p3h2x4x_i3c_hub->tp_bus[tp].tp_smbus_client;
+ if (!client)
+ goto ibi_err;
+
+ addr = slave_address >> 1;
+ i2c_slave_event(client,
+ I2C_SLAVE_WRITE_REQUESTED, &addr);
+ for (i = 0; i < packet_len; i++) {
+ temp = slave_rx_buffer[i];
+ i2c_slave_event(client,
+ I2C_SLAVE_WRITE_RECEIVED, &temp);
+ }
+ i2c_slave_event(client, I2C_SLAVE_STOP, &temp);
+ }
+
+ibi_err:
+ regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_PAGE_PTR, 0x00);
+ regmap_write(p3h2x4x_i3c_hub->regmap, P3H2X4X_TP0_SMBUS_AGNT_STS + tp, flag_clear);
+}
+
+/**
+ * p3h2x4x_ibi_handler - IBI handler.
+ * @i3cdev: i3c device.
+ * @payload: two byte IBI payload data.
+ *
+ */
+void p3h2x4x_ibi_handler(struct i3c_device *i3cdev,
+ const struct i3c_ibi_payload *payload)
+{
+ struct p3h2x4x_i3c_hub_dev *p3h2x4x_i3c_hub;
+ u8 payload_byte_one, payload_byte_two;
+ u32 target_port_status, ret, i;
+ const u8 *data;
+
+ if (!payload || payload->len < P3H2X4X_MAX_PAYLOAD_LEN)
+ return;
+
+ data = payload->data;
+ payload_byte_one = data[0];
+
+ if (!(payload_byte_one & P3H2X4X_SMBUS_AGENT_EVENT_FLAG_STATUS))
+ return;
+
+ p3h2x4x_i3c_hub = i3cdev_get_drvdata(i3cdev);
+
+ if (!p3h2x4x_i3c_hub || !p3h2x4x_i3c_hub->regmap)
+ return;
+
+ payload_byte_two = data[1];
+ guard(mutex)(&p3h2x4x_i3c_hub->etx_mutex);
+
+ for (i = 0; i < P3H2X4X_TP_MAX_COUNT; ++i) {
+ if (p3h2x4x_i3c_hub->tp_bus[i].is_registered && (payload_byte_two >> i) & 0x01) {
+ ret = regmap_read(p3h2x4x_i3c_hub->regmap, P3H2X4X_TP0_SMBUS_AGNT_STS + i,
+ &target_port_status);
+ if (ret) {
+ dev_err(&i3cdev->dev, "target port read status failed %d\n", ret);
+ return;
+ }
+
+ /* process data receive buffer */
+ switch (target_port_status & BUF_RECEIVED_FLAG_MASK) {
+ case P3H2X4X_TARGET_BUF_CA_TF:
+ break;
+ case P3H2X4X_TARGET_BUF_0_RECEIVE:
+ p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_0, i, false);
+ break;
+ case P3H2X4X_TARGET_BUF_1_RECEIVE:
+ p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_1, i, false);
+ break;
+ case P3H2X4X_TARGET_BUF_0_1_RECEIVE:
+ p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_0, i, false);
+ p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_1, i, false);
+ break;
+ case P3H2X4X_TARGET_BUF_OVRFL:
+ p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_0, i, false);
+ p3h2x4x_read_smbus_agent_rx_buf(i3cdev, RCV_BUF_1, i, true);
+ dev_err(&i3cdev->dev, "Overflow, reading buffer zero and one\n");
+ break;
+ default:
+ regmap_write(p3h2x4x_i3c_hub->regmap,
+ P3H2X4X_TP0_SMBUS_AGNT_STS + i,
+ BUF_RECEIVED_FLAG_TF_MASK);
+ break;
+ }
+ }
+ }
+}
+#endif
+
static int p3h2x4x_read_smbus_transaction_status(struct p3h2x4x_i3c_hub_dev *hub,
u8 target_port_status,
u8 data_length)
@@ -205,11 +345,63 @@ static u32 p3h2x4x_tp_smbus_funcs(struct i2c_adapter *adapter)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BLOCK_DATA;
}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static int p3h2x4x_tp_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct tp_bus *bus = i2c_get_adapdata(slave->adapter);
+ struct p3h2x4x_i3c_hub_dev *hub = bus->p3h2x4x_i3c_hub;
+ int ret;
+
+ guard(mutex)(&hub->etx_mutex);
+
+ if (bus->tp_smbus_client)
+ return -EBUSY;
+
+ ret = regmap_set_bits(hub->regmap,
+ P3H2X4X_TP_SMBUS_AGNT_IBI_CONFIG,
+ bus->tp_mask);
+ if (ret)
+ return ret;
+
+ bus->tp_smbus_client = slave;
+ hub->hub_config.tp_config[bus->tp_port].ibi_en = true;
+
+ return 0;
+}
+
+static int p3h2x4x_tp_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct tp_bus *bus = i2c_get_adapdata(slave->adapter);
+ struct p3h2x4x_i3c_hub_dev *hub = bus->p3h2x4x_i3c_hub;
+ int ret;
+
+ guard(mutex)(&hub->etx_mutex);
+
+ if (bus->tp_smbus_client != slave)
+ return -EINVAL;
+
+ ret = regmap_clear_bits(hub->regmap,
+ P3H2X4X_TP_SMBUS_AGNT_IBI_CONFIG,
+ bus->tp_mask);
+ if (ret)
+ return ret;
+
+ bus->tp_smbus_client = NULL;
+ hub->hub_config.tp_config[bus->tp_port].ibi_en = false;
+
+ return 0;
+}
+#endif
+
/*
* I2C algorithm Structure
*/
static struct i2c_algorithm p3h2x4x_tp_i2c_algorithm = {
.master_xfer = p3h2x4x_tp_i2c_xfer,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_slave = p3h2x4x_tp_i2c_reg_slave,
+ .unreg_slave = p3h2x4x_tp_i2c_unreg_slave,
+#endif
.functionality = p3h2x4x_tp_smbus_funcs,
};
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2 1/3] dt-bindings: media: qcom,qcm2290-venus: document shikra Iris compatible
From: Bryan O'Donoghue @ 2026-06-12 11:18 UTC (permalink / raw)
To: Vikash Garodia, Dikshita Agarwal, Mauro Carvalho Chehab,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jorge Ramirez-Ortiz, Bjorn Andersson, Konrad Dybcio
Cc: linux-arm-msm, linux-media, devicetree, linux-kernel
In-Reply-To: <20260612-shikra_vpu-v2-1-bf8727370a1e@oss.qualcomm.com>
On 11/06/2026 20:49, Vikash Garodia wrote:
> Document the iris video accelerator used on shikra platforms by adding
> the qcom,shikra-iris compatible.
>
> Although QCM2290 and shikra share the same video hardware and overall
> integration, their SMMU programming differs. QCM2290 exposes separate
> stream IDs for the video hardware and the Xtensa path, requiring two
> explicit IOMMU entries, whereas shikra uses a masked SMR to collapse
> equivalent stream IDs into a single mapping. Due to QCM2290’s SID layout
> and Xtensa isolation requirements, such SMR masking is not applicable on
> QCM2290 platforms.
> Since shikra uses the same video hardware as QCM2290 and shares the same
> programming model and capabilities, it is added as a fallback compatible
> to qcom,qcm2290-venus, with conditional handling to allow either one or
> two IOMMU entries.
>
> Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
> ---
> .../bindings/media/qcom,qcm2290-venus.yaml | 26 ++++++++++++++++------
> 1 file changed, 19 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml
> index 5977e7d0a71b4fb5681f1c2094439c251366f01f..dec7051224d1610b8b3dcb1750152eeda95d3703 100644
> --- a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml
> @@ -13,14 +13,13 @@ description:
> The Venus AR50_LITE IP is a video encode and decode accelerator present
> on Qualcomm platforms.
>
> -allOf:
> - - $ref: qcom,venus-common.yaml#
> -
> properties:
> compatible:
> oneOf:
> - items:
> - - const: qcom,sm6115-venus
> + - enum:
> + - qcom,shikra-iris
> + - qcom,sm6115-venus
I still think this is not an accurate name.
https://lore.kernel.org/linux-arm-msm/q3sr74ncqnmzsjrd6jdbkpplxpnsnifhnvxsdplvogr2kf25p3@taadnzd2qrcu/
Best practice if you _aren't_ acting on previous feedback is to document
it in your cover letter.
https://lore.kernel.org/linux-arm-msm/20260612-shikra_vpu-v2-0-bf8727370a1e@oss.qualcomm.com
Either way that naming comment still needs to be resolved.
---
bod
^ permalink raw reply
* [PATCH v11 3/9] i3c: master: Add APIs for I3C hub support
From: Lakshay Piplani @ 2026-06-12 11:18 UTC (permalink / raw)
To: linux-kernel, linux-i3c, alexandre.belloni, krzk+dt, robh,
conor+dt, devicetree, broonie, lee, Frank.Li, lgirdwood
Cc: vikash.bansal, priyanka.jain, aman.kumarpandey, Lakshay Piplani
In-Reply-To: <20260612111816.3688240-1-lakshay.piplani@nxp.com>
From: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Add helpers for attaching and detaching I3C devices and CCC helpers
to check CCC support and send CCC commands. These additions prepare
for I3C hub support.
The attach and detach helpers must be called with the bus lock held in
write mode.
1) i3c_master_direct_attach_i3c_dev_locked()
2) i3c_master_direct_detach_i3c_dev_locked()
3) i3c_master_send_ccc_cmd()
4) i3c_master_supports_ccc_cmd()
Signed-off-by: Aman Kumar Pandey <aman.kumarpandey@nxp.com>
Signed-off-by: Lakshay Piplani <lakshay.piplani@nxp.com>
---
Changes in v11:
- Convert i3c_master_supports_ccc_cmd() to return bool and align
semantics with CCC support checks used by the I3C core
Changes in v10:
- Rename i3c_master_direct_attach_i3c_dev and i3c_master_direct_detach_i3c_dev
APIs to *_locked, as these APIs must be called with the bus lock held in
write mode
Changes in v9:
- No change
Changes in v8:
- No change
Changes in v7:
- Update commit message to clarify purpose (prepare for I3C hub support)
Changes in v6:
- Split the patch into two parts:
1) expose the existing API
2) add new APIs.
---
---
drivers/i3c/master.c | 101 +++++++++++++++++++++++++++++++++++++
include/linux/i3c/master.h | 7 +++
2 files changed, 108 insertions(+)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 0636e3e21758..9cd2e9e3bd67 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1652,6 +1652,63 @@ static int i3c_master_attach_i3c_dev(struct i3c_master_controller *master,
return 0;
}
+/**
+ * i3c_master_direct_attach_i3c_dev_locked() - attach an I3C device to a master
+ * @master: I3C master controller to attach the device to
+ * @dev: I3C device descriptor representing the device
+ *
+ * This function attaches an I3C device to its master controller once the
+ * device has a valid address on the bus. Devices without an assigned address
+ * are ignored. The master device itself is never attached through this bus.
+ *
+ * Context: Caller must hold master->bus.lock in write mode.
+ *
+ * Return: 0 on success, or a negative error code if the attach operation
+ * fails in the master controller driver.
+ */
+int i3c_master_direct_attach_i3c_dev_locked(struct i3c_master_controller *master,
+ struct i3c_dev_desc *dev)
+{
+ int ret = 0;
+
+ /*
+ * We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.static_addr && !dev->info.dyn_addr)
+ return 0;
+
+ /* Do not attach the master device itself. */
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ ret = master->ops->attach_i3c_dev(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_direct_attach_i3c_dev_locked);
+
+/**
+ * i3c_master_direct_detach_i3c_dev_locked() - Detach an I3C device from its
+ * master controller.
+ * @dev: I3C device descriptor to be detached
+ *
+ * This function detaches an I3C device from its master controller.
+ * It ensures that the master itself is not detached. If the device is not
+ * the master and the master controller provides a detach operation,
+ * the detach callback is invoked to perform the actual removal.
+ *
+ * Context: Caller must hold master->bus.lock in write mode.
+ */
+void i3c_master_direct_detach_i3c_dev_locked(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ master->ops->detach_i3c_dev(dev);
+}
+EXPORT_SYMBOL_GPL(i3c_master_direct_detach_i3c_dev_locked);
+
/**
* i3c_master_reattach_i3c_dev_locked() - reattach an I3C device with a new address
* @dev: I3C device descriptor to reattach
@@ -1816,6 +1873,50 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
}
}
+/**
+ * i3c_master_supports_ccc_cmd() - check CCC command support
+ * @master: I3C master controller
+ * @cmd: CCC command to verify
+ *
+ * Return: true if @cmd is supported, false otherwise.
+ */
+bool i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
+ const struct i3c_ccc_cmd *cmd)
+{
+ if (!master || !cmd)
+ return false;
+
+ if (!master->ops->supports_ccc_cmd)
+ return true;
+
+ return master->ops->supports_ccc_cmd(master, cmd);
+}
+EXPORT_SYMBOL_GPL(i3c_master_supports_ccc_cmd);
+
+/**
+ * i3c_master_send_ccc_cmd() - send a CCC command
+ * @master: I3C master controller issuing the command
+ * @cmd: CCC command to be sent
+ *
+ * This function sends a Common Command Code (CCC) command to devices on the
+ * I3C bus. It acquires the bus maintenance lock, executes the command, and
+ * then releases the lock to ensure safe access to the bus.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int i3c_master_send_ccc_cmd(struct i3c_master_controller *master,
+ struct i3c_ccc_cmd *cmd)
+{
+ int ret;
+
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = i3c_master_send_ccc_cmd_locked(master, cmd);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_send_ccc_cmd);
+
/**
* i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
* @master: controller
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 355e9b3d9ae3..7e358b6774c5 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -615,6 +615,13 @@ DEFINE_FREE(i3c_master_dma_unmap_single, void *,
int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev,
u8 old_dyn_addr);
+int i3c_master_direct_attach_i3c_dev_locked(struct i3c_master_controller *master,
+ struct i3c_dev_desc *dev);
+void i3c_master_direct_detach_i3c_dev_locked(struct i3c_dev_desc *dev);
+int i3c_master_send_ccc_cmd(struct i3c_master_controller *master,
+ struct i3c_ccc_cmd *cmd);
+bool i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
+ const struct i3c_ccc_cmd *cmd);
int i3c_master_set_info(struct i3c_master_controller *master,
const struct i3c_device_info *info);
--
2.25.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox