* Re: [PATCH] i3c: master: dw: Clamp GETMRL/GETMWL to controller FIFO limits [not found] ` <44e2c06b-7742-47a7-b2fa-2284867d4f32@amd.com> @ 2026-10-06 19:35 ` Meagan Lloyd 2026-10-07 11:43 ` Patil, Shubham Sanjay 0 siblings, 1 reply; 2+ messages in thread From: Meagan Lloyd @ 2026-10-06 19:35 UTC (permalink / raw) To: Patil, Shubham Sanjay Cc: Frank Li, Shubham Patil, Alexandre Belloni, Frank Li, linux-i3c, devicetree, linux-kernel, meaganlloyd, git, jk, matt, netdev On Tue, Oct 06, 2026 at 06:05:16PM +0530, Patil, Shubham Sanjay wrote: > On 9/24/2026 8:57 PM, Frank Li wrote: > > Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding. > > > > > > On Thu, Sep 24, 2026 at 10:25:44AM +0530, Patil, Shubham Sanjay wrote: > > > > > > > > > On 9/11/2026 12:04 AM, Frank Li wrote: > > > > Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding. > > > > > > > > > > > > On Tue, Sep 08, 2026 at 03:57:24PM +0530, Shubham Patil wrote: > > > > > The DW master rejects private SDR transfers larger than > > > > > caps.datafifodepth with -EOPNOTSUPP. Targets often report MRL/MWL > > > > > values larger than that FIFO, so the core stores limits the controller > > > > > cannot meet. > > > > > > > > > > After a successful GETMRL/GETMWL, issue Direct SETMRL/SETMWL to the > > > > > same target with lengths capped to the data FIFO (in bytes), then > > > > > rewrite the GET payload so the core keeps the same values. Only update > > > > > the GET buffer once SET is acked, so a failed SET does not leave the > > > > > core and the target disagreeing. > > > > > > > > I think i3c device driver should know these information choose > > > > min value dring each xfer. even though you set devcie's MRL/MXL, device > > > > driver still issue a longer transfer. > > > > > > > > Frank > > > > > > Understood - I will drop the SETMRL/SETMWL and stop rewriting the GET > > > payload, and instead expose the controller limit so the min is taken > > > per transfer. Two questions on how you want that done: > > > 1) Where should the min be taken? > > > a) In the core: the controller driver sets max_read_len / > > > max_write_len / max_ibi_len in struct i3c_master_controller, and > > > the core caps i3c_device_info to min(target, controller) after > > > GETMRL/GETMWL. Device drivers then use i3c_device_get_info() > > > as-is and cannot forget. > > > b) In each device driver: the core keeps reporting the raw target > > > values, and drivers do the min themselves. > > > > We can provide APIs for device driver to get whole data path required > > max_read/write_len. > > Thanks. Next v2 will be: > > Patch 1 - core: add max_read_len/max_write_len to struct > i3c_master_controller, set by the controller driver before > i3c_master_register(), plus two helpers for client drivers: > u16 i3c_device_get_max_read_len(const struct i3c_device *dev); > u16 i3c_device_get_max_write_len(const struct i3c_device *dev); > > Each returns the smallest limit along the whole data path, i.e. > min_not_zero() of the target's GETMRL/GETMWL value and the controller > limit, and U16_MAX when nothing limits it. i3c_device_info keeps the raw > target values untouched. > > Patch 2 - dw: advertise the data FIFO depth to the core, by setting > base.max_read_len/base.max_write_len in dw_i3c_common_probe() before > i3c_master_register(). > > Thanks, > Shubham I'd vote to avoid overloading the terms 'max_read_len' and 'max_write_len' as it currently refers to the device's MRL and MWL values retrieved through the GETMRL, GETMWL CCCs. So, maybe something like: struct i3c_master_controller fields rx_fifo_bytes, tx_fifo_bytes i3c_device_get_max_read_xfer_bytes() - min(rx_fifo_bytes, device mrl) i3c_device_get_max_write_xfer_bytes() - min(tx_fifo_bytes, device mwl) - The first meaningful user of this patch would be mctp-i3c driver. Where the i3c_xfer.len would need to be updated from using the device's mrl/mwl to whatever these new API functions return. I think that the mctp-i3c driver update should be part of this series since it'll be the first user. Unless, Alexandre and the MCTP maintainers disagree. Thanks, Meagan > > > > > > 2) Either way, a driver that ignores these limits still gets > > > -EOPNOTSUPP from dw_i3c_master_i3c_xfers() when the transfer does > > > not fit the data FIFO. Should the driver keep returning that, or > > > would you consider splitting an oversized private SDR transfer into > > > FIFO-sized chunks in the controller driver? My understanding is > > > no - splitting changes what the target sees on the bus - but I > > > want to be sure before v2. > > > > the decision about split transfer should be decided by device drivers. > > Not all device treat two continue repeat START as continue write/read. > > > > Frank > > > > > > Thanks, > > > Shubham> > > > > > > > > > > GETMRL is variable length: the optional third byte is max IBI payload > > > > > and is only present if the target returned it. Clamp that IBI byte to > > > > > the IBI queue depth from QUEUE_SIZE_CAPABILITY.IBI_BUF_SIZE (bits 19:16 > > > > > at 0xe8, encoded as 2^(n+1) dwords). > > > > > > > > > > Rename the unused EXTENDED_CAPABILITY macro at 0xe8 to the databook > > > > > name QUEUE_SIZE_CAPABILITY. > > > > > > > > > > Signed-off-by: Shubham Patil <shubhamsanjay.patil@amd.com> > > > > > --- > > > > > drivers/i3c/master/dw-i3c-master.c | 149 ++++++++++++++++++++++++++++- > > > > > drivers/i3c/master/dw-i3c-master.h | 1 + > > > > > 2 files changed, 149 insertions(+), 1 deletion(-) > > > > > > > > > > diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c > > > > > index 4563d8761ba0..51defcb57761 100644 > > > > > --- a/drivers/i3c/master/dw-i3c-master.c > > > > > +++ b/drivers/i3c/master/dw-i3c-master.c > > > > > @@ -203,7 +203,13 @@ > > > > > #define BUS_IDLE_TIMING 0xd8 > > > > > #define I3C_VER_ID 0xe0 > > > > > #define I3C_VER_TYPE 0xe4 > > > > > -#define EXTENDED_CAPABILITY 0xe8 > > > > > +#define QUEUE_SIZE_CAPABILITY 0xe8 > > > > > +#define QUEUE_SIZE_CAPABILITY_IBI_BUF(x) (((x) & GENMASK(19, 16)) >> 16) > > > > > +/* > > > > > + * IBI_BUF_SIZE is encoded as 2^(field + 1) dwords: the smallest buffer is > > > > > + * 2 dwords and each increment of the field doubles the depth. > > > > > + */ > > > > > +#define QUEUE_SIZE_IBI_BUF_MIN_DWORDS 2 > > > > > #define SLAVE_CONFIG 0xec > > > > > > > > > > #define DYN_ADDR_LO_MASK GENMASK(4, 0) > > > > > @@ -844,6 +850,130 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) > > > > > return ret; > > > > > } > > > > > > > > > > +/* > > > > > + * Cap the limits a target reported through GETMRL to what this controller can > > > > > + * actually transfer, so the core never asks for a private read the data FIFO > > > > > + * cannot hold. The optional IBI payload byte is capped to the IBI queue depth > > > > > + * instead; since that byte is a u8, the IBI cap only ever applies to > > > > > + * controllers whose IBI queue is smaller than 255 bytes. > > > > > + * > > > > > + * Direct SETMRL is optional, so a target may implement GETMRL and NACK the SET. > > > > > + * Clamp the values handed back to the core either way: a failed SET only means > > > > > + * the target keeps its own larger limit, which is harmless as long as the core > > > > > + * stays within ours. > > > > > + */ > > > > > +static int dw_i3c_master_clamp_mrl(struct dw_i3c_master *master, > > > > > + struct i3c_ccc_cmd *ccc) > > > > > +{ > > > > > + u16 max_fifo_bytes = master->caps.datafifodepth * sizeof(u32); > > > > > + u32 max_ibi_bytes = master->caps.ibififodepth * sizeof(u32); > > > > > + u16 actual_len = ccc->dests[0].payload.actual_len; > > > > > + struct i3c_ccc_cmd_dest set_dest = { }; > > > > > + struct i3c_ccc_cmd set_cmd = { }; > > > > > + struct i3c_ccc_mrl set_mrl; > > > > > + struct i3c_ccc_mrl *mrl; > > > > > + bool clamp_ibi = false; > > > > > + bool clamp_read; > > > > > + u8 ibi_len = 0; > > > > > + u16 read_len; > > > > > + int ret; > > > > > + > > > > > + /* Need at least the 2-byte max read length field to act on. */ > > > > > + if (actual_len < 2) > > > > > + return 0; > > > > > + > > > > > + mrl = ccc->dests[0].payload.data; > > > > > + read_len = be16_to_cpu(mrl->read_len); > > > > > + clamp_read = read_len > max_fifo_bytes; > > > > > + > > > > > + /* Optional third byte is valid only if the target returned it. */ > > > > > + if (actual_len > 2) { > > > > > + ibi_len = mrl->ibi_len; > > > > > + clamp_ibi = max_ibi_bytes && ibi_len > max_ibi_bytes; > > > > > + } > > > > > + > > > > > + if (!clamp_read && !clamp_ibi) > > > > > + return 0; > > > > > + > > > > > + set_mrl.read_len = cpu_to_be16(clamp_read ? max_fifo_bytes : read_len); > > > > > + if (actual_len > 2) > > > > > + set_mrl.ibi_len = clamp_ibi ? max_ibi_bytes : ibi_len; > > > > > + > > > > > + set_dest.addr = ccc->dests[0].addr; > > > > > + set_dest.payload.data = &set_mrl; > > > > > + set_dest.payload.len = actual_len; > > > > > + > > > > > + set_cmd.rnw = 0; > > > > > + set_cmd.id = I3C_CCC_SETMRL(false); > > > > > + set_cmd.ndests = 1; > > > > > + set_cmd.dests = &set_dest; > > > > > + > > > > > + ret = dw_i3c_ccc_set(master, &set_cmd); > > > > > + if (ret) > > > > > + dev_dbg(&master->base.dev, > > > > > + "SETMRL not accepted by target: %d\n", ret); > > > > > + > > > > > + if (clamp_read) { > > > > > + mrl->read_len = cpu_to_be16(max_fifo_bytes); > > > > > + dev_dbg(&master->base.dev, > > > > > + "clamped target MRL from %u to %u bytes (FIFO depth limit)\n", > > > > > + read_len, max_fifo_bytes); > > > > > + } > > > > > + if (clamp_ibi) { > > > > > + mrl->ibi_len = max_ibi_bytes; > > > > > + dev_dbg(&master->base.dev, > > > > > + "clamped target IBI len from %u to %u bytes (IBI buffer limit)\n", > > > > > + ibi_len, max_ibi_bytes); > > > > > + } > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +/* Same contract as dw_i3c_master_clamp_mrl(), for the write direction. */ > > > > > +static int dw_i3c_master_clamp_mwl(struct dw_i3c_master *master, > > > > > + struct i3c_ccc_cmd *ccc) > > > > > +{ > > > > > + u16 max_fifo_bytes = master->caps.datafifodepth * sizeof(u32); > > > > > + struct i3c_ccc_cmd_dest set_dest = { }; > > > > > + struct i3c_ccc_cmd set_cmd = { }; > > > > > + struct i3c_ccc_mwl set_mwl; > > > > > + struct i3c_ccc_mwl *mwl; > > > > > + u16 write_len; > > > > > + int ret; > > > > > + > > > > > + if (ccc->dests[0].payload.actual_len < 2) > > > > > + return 0; > > > > > + > > > > > + mwl = ccc->dests[0].payload.data; > > > > > + write_len = be16_to_cpu(mwl->len); > > > > > + > > > > > + if (write_len <= max_fifo_bytes) > > > > > + return 0; > > > > > + > > > > > + set_mwl.len = cpu_to_be16(max_fifo_bytes); > > > > > + > > > > > + set_dest.addr = ccc->dests[0].addr; > > > > > + set_dest.payload.data = &set_mwl; > > > > > + set_dest.payload.len = sizeof(set_mwl); > > > > > + > > > > > + set_cmd.rnw = 0; > > > > > + set_cmd.id = I3C_CCC_SETMWL(false); > > > > > + set_cmd.ndests = 1; > > > > > + set_cmd.dests = &set_dest; > > > > > + > > > > > + ret = dw_i3c_ccc_set(master, &set_cmd); > > > > > + if (ret) > > > > > + dev_dbg(&master->base.dev, > > > > > + "SETMWL not accepted by target: %d\n", ret); > > > > > + > > > > > + mwl->len = cpu_to_be16(max_fifo_bytes); > > > > > + dev_dbg(&master->base.dev, > > > > > + "clamped target MWL from %u to %u bytes (FIFO depth limit)\n", > > > > > + write_len, max_fifo_bytes); > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, > > > > > struct i3c_ccc_cmd *ccc) > > > > > { > > > > > @@ -866,6 +996,18 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, > > > > > else > > > > > ret = dw_i3c_ccc_set(master, ccc); > > > > > > > > > > + /* > > > > > + * Clamp GETMRL/GETMWL responses to the data FIFO depth, and the > > > > > + * optional GETMRL IBI byte to the IBI queue depth. The GET itself has > > > > > + * already succeeded, so its result is never overridden here. > > > > > + */ > > > > > + if (!ret && ccc->rnw) { > > > > > + if (ccc->id == I3C_CCC_GETMRL) > > > > > + dw_i3c_master_clamp_mrl(master, ccc); > > > > > + else if (ccc->id == I3C_CCC_GETMWL) > > > > > + dw_i3c_master_clamp_mwl(master, ccc); > > > > > + } > > > > > + > > > > > pm_runtime_put_autosuspend(master->dev); > > > > > return ret; > > > > > } > > > > > @@ -1728,6 +1870,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, > > > > > ret = readl(master->regs + DATA_BUFFER_STATUS_LEVEL); > > > > > master->caps.datafifodepth = DATA_BUFFER_STATUS_LEVEL_TX(ret); > > > > > > > > > > + /* Read the IBI data buffer size advertised by the controller. */ > > > > > + ret = readl(master->regs + QUEUE_SIZE_CAPABILITY); > > > > > + master->caps.ibififodepth = QUEUE_SIZE_IBI_BUF_MIN_DWORDS << > > > > > + QUEUE_SIZE_CAPABILITY_IBI_BUF(ret); > > > > > + > > > > > ret = readl(master->regs + DEVICE_ADDR_TABLE_POINTER); > > > > > master->datstartaddr = ret; > > > > > master->maxdevs = ret >> 16; > > > > > diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h > > > > > index 17ad817d1f8e..54c3912374c8 100644 > > > > > --- a/drivers/i3c/master/dw-i3c-master.h > > > > > +++ b/drivers/i3c/master/dw-i3c-master.h > > > > > @@ -15,6 +15,7 @@ > > > > > struct dw_i3c_master_caps { > > > > > u8 cmdfifodepth; > > > > > u8 datafifodepth; > > > > > + u32 ibififodepth; > > > > > }; > > > > > > > > > > struct dw_i3c_dat_entry { > > > > > -- > > > > > 2.34.1 > > > > > > > > > > > -- > linux-i3c mailing list > linux-i3c@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-i3c ^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] i3c: master: dw: Clamp GETMRL/GETMWL to controller FIFO limits 2026-10-06 19:35 ` [PATCH] i3c: master: dw: Clamp GETMRL/GETMWL to controller FIFO limits Meagan Lloyd @ 2026-10-07 11:43 ` Patil, Shubham Sanjay 0 siblings, 0 replies; 2+ messages in thread From: Patil, Shubham Sanjay @ 2026-10-07 11:43 UTC (permalink / raw) To: Meagan Lloyd, Frank Li Cc: Frank Li, Shubham Patil, Alexandre Belloni, Frank Li, linux-i3c, devicetree, linux-kernel, git, jk, matt, netdev On 10/7/2026 1:05 AM, Meagan Lloyd wrote: > Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding. > > > On Tue, Oct 06, 2026 at 06:05:16PM +0530, Patil, Shubham Sanjay wrote: >> On 9/24/2026 8:57 PM, Frank Li wrote: >>> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding. >>> >>> >>> On Thu, Sep 24, 2026 at 10:25:44AM +0530, Patil, Shubham Sanjay wrote: >>>> >>>> >>>> On 9/11/2026 12:04 AM, Frank Li wrote: >>>>> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding. >>>>> >>>>> >>>>> On Tue, Sep 08, 2026 at 03:57:24PM +0530, Shubham Patil wrote: >>>>>> The DW master rejects private SDR transfers larger than >>>>>> caps.datafifodepth with -EOPNOTSUPP. Targets often report MRL/MWL >>>>>> values larger than that FIFO, so the core stores limits the controller >>>>>> cannot meet. >>>>>> >>>>>> After a successful GETMRL/GETMWL, issue Direct SETMRL/SETMWL to the >>>>>> same target with lengths capped to the data FIFO (in bytes), then >>>>>> rewrite the GET payload so the core keeps the same values. Only update >>>>>> the GET buffer once SET is acked, so a failed SET does not leave the >>>>>> core and the target disagreeing. >>>>> >>>>> I think i3c device driver should know these information choose >>>>> min value dring each xfer. even though you set devcie's MRL/MXL, device >>>>> driver still issue a longer transfer. >>>>> >>>>> Frank >>>> >>>> Understood - I will drop the SETMRL/SETMWL and stop rewriting the GET >>>> payload, and instead expose the controller limit so the min is taken >>>> per transfer. Two questions on how you want that done: >>>> 1) Where should the min be taken? >>>> a) In the core: the controller driver sets max_read_len / >>>> max_write_len / max_ibi_len in struct i3c_master_controller, and >>>> the core caps i3c_device_info to min(target, controller) after >>>> GETMRL/GETMWL. Device drivers then use i3c_device_get_info() >>>> as-is and cannot forget. >>>> b) In each device driver: the core keeps reporting the raw target >>>> values, and drivers do the min themselves. >>> >>> We can provide APIs for device driver to get whole data path required >>> max_read/write_len. >> >> Thanks. Next v2 will be: >> >> Patch 1 - core: add max_read_len/max_write_len to struct >> i3c_master_controller, set by the controller driver before >> i3c_master_register(), plus two helpers for client drivers: >> u16 i3c_device_get_max_read_len(const struct i3c_device *dev); >> u16 i3c_device_get_max_write_len(const struct i3c_device *dev); >> >> Each returns the smallest limit along the whole data path, i.e. >> min_not_zero() of the target's GETMRL/GETMWL value and the controller >> limit, and U16_MAX when nothing limits it. i3c_device_info keeps the raw >> target values untouched. >> >> Patch 2 - dw: advertise the data FIFO depth to the core, by setting >> base.max_read_len/base.max_write_len in dw_i3c_common_probe() before >> i3c_master_register(). >> >> Thanks, >> Shubham > > I'd vote to avoid overloading the terms 'max_read_len' and > 'max_write_len' as it currently refers to the device's MRL and MWL > values retrieved through the GETMRL, GETMWL CCCs. > > So, maybe something like: > > struct i3c_master_controller fields rx_fifo_bytes, tx_fifo_bytes > > i3c_device_get_max_read_xfer_bytes() - min(rx_fifo_bytes, device mrl) > i3c_device_get_max_write_xfer_bytes() - min(tx_fifo_bytes, device mwl) > > - > > The first meaningful user of this patch would be mctp-i3c driver. Where > the i3c_xfer.len would need to be updated from using the device's mrl/mwl > to whatever these new API functions return. > > I think that the mctp-i3c driver update should be part of this series since > it'll be the first user. Unless, Alexandre and the MCTP maintainers disagree. > > Thanks, > Meagan Agreed on both, I will use those names as-is. v2 will be: Patch 1 - core: add rx_fifo_bytes and tx_fifo_bytes to struct i3c_master_controller, set by the controller driver before i3c_master_register(), plus the two helpers above in drivers/i3c/device.c. Patch 2 - dw: read QUEUE_SIZE_CAPABILITY for TX_BUF_SIZE and RX_BUF_SIZE and publish both limits. The driver currently has that offset defined under the wrong name and unused, and derives a single depth from the TX data buffer status level, which patch 2 corrects to use the right depth for each direction. Patch 3 - mctp-i3c: use the helpers for mi->mrl and mi->mwl. The helpers will use min_not_zero() and return 0 when neither side reports a limit, so "unknown" stays encoded the same way it already is in i3c_device_info and mctp-i3c behaves exactly as it does today in that case. That keeps patch 3 a straight substitution of the two helpers for info.max_read_len/info.max_write_len. The value returned by i3c_device_get_max_read_xfer_bytes() and i3c_device_get_max_write_xfer_bytes() will be documented as the limit for a single message in that direction. Thanks, Shubham> >> >>> >>>> 2) Either way, a driver that ignores these limits still gets >>>> -EOPNOTSUPP from dw_i3c_master_i3c_xfers() when the transfer does >>>> not fit the data FIFO. Should the driver keep returning that, or >>>> would you consider splitting an oversized private SDR transfer into >>>> FIFO-sized chunks in the controller driver? My understanding is >>>> no - splitting changes what the target sees on the bus - but I >>>> want to be sure before v2. >>> >>> the decision about split transfer should be decided by device drivers. >>> Not all device treat two continue repeat START as continue write/read. >>> >>> Frank >>>> >>>> Thanks, >>>> Shubham> >>>>>> >>>>>> GETMRL is variable length: the optional third byte is max IBI payload >>>>>> and is only present if the target returned it. Clamp that IBI byte to >>>>>> the IBI queue depth from QUEUE_SIZE_CAPABILITY.IBI_BUF_SIZE (bits 19:16 >>>>>> at 0xe8, encoded as 2^(n+1) dwords). >>>>>> >>>>>> Rename the unused EXTENDED_CAPABILITY macro at 0xe8 to the databook >>>>>> name QUEUE_SIZE_CAPABILITY. >>>>>> >>>>>> Signed-off-by: Shubham Patil <shubhamsanjay.patil@amd.com> >>>>>> --- >>>>>> drivers/i3c/master/dw-i3c-master.c | 149 ++++++++++++++++++++++++++++- >>>>>> drivers/i3c/master/dw-i3c-master.h | 1 + >>>>>> 2 files changed, 149 insertions(+), 1 deletion(-) >>>>>> >>>>>> diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c >>>>>> index 4563d8761ba0..51defcb57761 100644 >>>>>> --- a/drivers/i3c/master/dw-i3c-master.c >>>>>> +++ b/drivers/i3c/master/dw-i3c-master.c >>>>>> @@ -203,7 +203,13 @@ >>>>>> #define BUS_IDLE_TIMING 0xd8 >>>>>> #define I3C_VER_ID 0xe0 >>>>>> #define I3C_VER_TYPE 0xe4 >>>>>> -#define EXTENDED_CAPABILITY 0xe8 >>>>>> +#define QUEUE_SIZE_CAPABILITY 0xe8 >>>>>> +#define QUEUE_SIZE_CAPABILITY_IBI_BUF(x) (((x) & GENMASK(19, 16)) >> 16) >>>>>> +/* >>>>>> + * IBI_BUF_SIZE is encoded as 2^(field + 1) dwords: the smallest buffer is >>>>>> + * 2 dwords and each increment of the field doubles the depth. >>>>>> + */ >>>>>> +#define QUEUE_SIZE_IBI_BUF_MIN_DWORDS 2 >>>>>> #define SLAVE_CONFIG 0xec >>>>>> >>>>>> #define DYN_ADDR_LO_MASK GENMASK(4, 0) >>>>>> @@ -844,6 +850,130 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) >>>>>> return ret; >>>>>> } >>>>>> >>>>>> +/* >>>>>> + * Cap the limits a target reported through GETMRL to what this controller can >>>>>> + * actually transfer, so the core never asks for a private read the data FIFO >>>>>> + * cannot hold. The optional IBI payload byte is capped to the IBI queue depth >>>>>> + * instead; since that byte is a u8, the IBI cap only ever applies to >>>>>> + * controllers whose IBI queue is smaller than 255 bytes. >>>>>> + * >>>>>> + * Direct SETMRL is optional, so a target may implement GETMRL and NACK the SET. >>>>>> + * Clamp the values handed back to the core either way: a failed SET only means >>>>>> + * the target keeps its own larger limit, which is harmless as long as the core >>>>>> + * stays within ours. >>>>>> + */ >>>>>> +static int dw_i3c_master_clamp_mrl(struct dw_i3c_master *master, >>>>>> + struct i3c_ccc_cmd *ccc) >>>>>> +{ >>>>>> + u16 max_fifo_bytes = master->caps.datafifodepth * sizeof(u32); >>>>>> + u32 max_ibi_bytes = master->caps.ibififodepth * sizeof(u32); >>>>>> + u16 actual_len = ccc->dests[0].payload.actual_len; >>>>>> + struct i3c_ccc_cmd_dest set_dest = { }; >>>>>> + struct i3c_ccc_cmd set_cmd = { }; >>>>>> + struct i3c_ccc_mrl set_mrl; >>>>>> + struct i3c_ccc_mrl *mrl; >>>>>> + bool clamp_ibi = false; >>>>>> + bool clamp_read; >>>>>> + u8 ibi_len = 0; >>>>>> + u16 read_len; >>>>>> + int ret; >>>>>> + >>>>>> + /* Need at least the 2-byte max read length field to act on. */ >>>>>> + if (actual_len < 2) >>>>>> + return 0; >>>>>> + >>>>>> + mrl = ccc->dests[0].payload.data; >>>>>> + read_len = be16_to_cpu(mrl->read_len); >>>>>> + clamp_read = read_len > max_fifo_bytes; >>>>>> + >>>>>> + /* Optional third byte is valid only if the target returned it. */ >>>>>> + if (actual_len > 2) { >>>>>> + ibi_len = mrl->ibi_len; >>>>>> + clamp_ibi = max_ibi_bytes && ibi_len > max_ibi_bytes; >>>>>> + } >>>>>> + >>>>>> + if (!clamp_read && !clamp_ibi) >>>>>> + return 0; >>>>>> + >>>>>> + set_mrl.read_len = cpu_to_be16(clamp_read ? max_fifo_bytes : read_len); >>>>>> + if (actual_len > 2) >>>>>> + set_mrl.ibi_len = clamp_ibi ? max_ibi_bytes : ibi_len; >>>>>> + >>>>>> + set_dest.addr = ccc->dests[0].addr; >>>>>> + set_dest.payload.data = &set_mrl; >>>>>> + set_dest.payload.len = actual_len; >>>>>> + >>>>>> + set_cmd.rnw = 0; >>>>>> + set_cmd.id = I3C_CCC_SETMRL(false); >>>>>> + set_cmd.ndests = 1; >>>>>> + set_cmd.dests = &set_dest; >>>>>> + >>>>>> + ret = dw_i3c_ccc_set(master, &set_cmd); >>>>>> + if (ret) >>>>>> + dev_dbg(&master->base.dev, >>>>>> + "SETMRL not accepted by target: %d\n", ret); >>>>>> + >>>>>> + if (clamp_read) { >>>>>> + mrl->read_len = cpu_to_be16(max_fifo_bytes); >>>>>> + dev_dbg(&master->base.dev, >>>>>> + "clamped target MRL from %u to %u bytes (FIFO depth limit)\n", >>>>>> + read_len, max_fifo_bytes); >>>>>> + } >>>>>> + if (clamp_ibi) { >>>>>> + mrl->ibi_len = max_ibi_bytes; >>>>>> + dev_dbg(&master->base.dev, >>>>>> + "clamped target IBI len from %u to %u bytes (IBI buffer limit)\n", >>>>>> + ibi_len, max_ibi_bytes); >>>>>> + } >>>>>> + >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> +/* Same contract as dw_i3c_master_clamp_mrl(), for the write direction. */ >>>>>> +static int dw_i3c_master_clamp_mwl(struct dw_i3c_master *master, >>>>>> + struct i3c_ccc_cmd *ccc) >>>>>> +{ >>>>>> + u16 max_fifo_bytes = master->caps.datafifodepth * sizeof(u32); >>>>>> + struct i3c_ccc_cmd_dest set_dest = { }; >>>>>> + struct i3c_ccc_cmd set_cmd = { }; >>>>>> + struct i3c_ccc_mwl set_mwl; >>>>>> + struct i3c_ccc_mwl *mwl; >>>>>> + u16 write_len; >>>>>> + int ret; >>>>>> + >>>>>> + if (ccc->dests[0].payload.actual_len < 2) >>>>>> + return 0; >>>>>> + >>>>>> + mwl = ccc->dests[0].payload.data; >>>>>> + write_len = be16_to_cpu(mwl->len); >>>>>> + >>>>>> + if (write_len <= max_fifo_bytes) >>>>>> + return 0; >>>>>> + >>>>>> + set_mwl.len = cpu_to_be16(max_fifo_bytes); >>>>>> + >>>>>> + set_dest.addr = ccc->dests[0].addr; >>>>>> + set_dest.payload.data = &set_mwl; >>>>>> + set_dest.payload.len = sizeof(set_mwl); >>>>>> + >>>>>> + set_cmd.rnw = 0; >>>>>> + set_cmd.id = I3C_CCC_SETMWL(false); >>>>>> + set_cmd.ndests = 1; >>>>>> + set_cmd.dests = &set_dest; >>>>>> + >>>>>> + ret = dw_i3c_ccc_set(master, &set_cmd); >>>>>> + if (ret) >>>>>> + dev_dbg(&master->base.dev, >>>>>> + "SETMWL not accepted by target: %d\n", ret); >>>>>> + >>>>>> + mwl->len = cpu_to_be16(max_fifo_bytes); >>>>>> + dev_dbg(&master->base.dev, >>>>>> + "clamped target MWL from %u to %u bytes (FIFO depth limit)\n", >>>>>> + write_len, max_fifo_bytes); >>>>>> + >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, >>>>>> struct i3c_ccc_cmd *ccc) >>>>>> { >>>>>> @@ -866,6 +996,18 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, >>>>>> else >>>>>> ret = dw_i3c_ccc_set(master, ccc); >>>>>> >>>>>> + /* >>>>>> + * Clamp GETMRL/GETMWL responses to the data FIFO depth, and the >>>>>> + * optional GETMRL IBI byte to the IBI queue depth. The GET itself has >>>>>> + * already succeeded, so its result is never overridden here. >>>>>> + */ >>>>>> + if (!ret && ccc->rnw) { >>>>>> + if (ccc->id == I3C_CCC_GETMRL) >>>>>> + dw_i3c_master_clamp_mrl(master, ccc); >>>>>> + else if (ccc->id == I3C_CCC_GETMWL) >>>>>> + dw_i3c_master_clamp_mwl(master, ccc); >>>>>> + } >>>>>> + >>>>>> pm_runtime_put_autosuspend(master->dev); >>>>>> return ret; >>>>>> } >>>>>> @@ -1728,6 +1870,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, >>>>>> ret = readl(master->regs + DATA_BUFFER_STATUS_LEVEL); >>>>>> master->caps.datafifodepth = DATA_BUFFER_STATUS_LEVEL_TX(ret); >>>>>> >>>>>> + /* Read the IBI data buffer size advertised by the controller. */ >>>>>> + ret = readl(master->regs + QUEUE_SIZE_CAPABILITY); >>>>>> + master->caps.ibififodepth = QUEUE_SIZE_IBI_BUF_MIN_DWORDS << >>>>>> + QUEUE_SIZE_CAPABILITY_IBI_BUF(ret); >>>>>> + >>>>>> ret = readl(master->regs + DEVICE_ADDR_TABLE_POINTER); >>>>>> master->datstartaddr = ret; >>>>>> master->maxdevs = ret >> 16; >>>>>> diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h >>>>>> index 17ad817d1f8e..54c3912374c8 100644 >>>>>> --- a/drivers/i3c/master/dw-i3c-master.h >>>>>> +++ b/drivers/i3c/master/dw-i3c-master.h >>>>>> @@ -15,6 +15,7 @@ >>>>>> struct dw_i3c_master_caps { >>>>>> u8 cmdfifodepth; >>>>>> u8 datafifodepth; >>>>>> + u32 ibififodepth; >>>>>> }; >>>>>> >>>>>> struct dw_i3c_dat_entry { >>>>>> -- >>>>>> 2.34.1 >>>>>> >>>> >> >> >> -- >> linux-i3c mailing list >> linux-i3c@lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/linux-i3c ^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-07 11:44 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260908102724.3232660-1-shubhamsanjay.patil@amd.com>
[not found] ` <aqL4HaQ1gjeaJYUQ@lizhi-Precision-Tower-5810>
[not found] ` <eff031f6-e212-43a8-98c0-5f52a4b9317b@amd.com>
[not found] ` <arVBZ0j2noe0g0Wi@lizhi-Precision-Tower-5810>
[not found] ` <44e2c06b-7742-47a7-b2fa-2284867d4f32@amd.com>
2026-10-06 19:35 ` [PATCH] i3c: master: dw: Clamp GETMRL/GETMWL to controller FIFO limits Meagan Lloyd
2026-10-07 11:43 ` Patil, Shubham Sanjay
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox