i3c: Add HDR API support

Rename struct i3c_priv_xfer to struct i3c_xfer, since private xfer in the
I3C spec refers only to SDR transfers. Ref: i3c spec ver1.2, section 3,
Technical Overview.

i3c_xfer will be used for both SDR and HDR.

Rename enum i3c_hdr_mode to i3c_xfer_mode. Previous definition need match
CCC GET_CAP1 bit position. Use 31 as SDR transfer mode.

Add i3c_device_do_xfers() with an xfer mode argument, while keeping
i3c_device_do_priv_xfers() as a wrapper that calls i3c_device_do_xfers()
with I3C_SDR for backward compatibility.

Introduce a 'cmd' field in struct i3c_xfer as an anonymous union with
'rnw', since HDR mode uses read/write commands instead of the SDR address
bit.

Add .i3c_xfers() callback for master controllers. If not implemented, fall
back to SDR with .priv_xfers(). The .priv_xfers() API can be removed once
all controllers switch to .i3c_xfers().

Add 'mode_mask' bitmask to advertise controller capability.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20251106-i3c_ddr-v11-1-33a6a66ed095@nxp.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
Frank Li 2025-11-06 12:36:00 -05:00 committed by Alexandre Belloni
parent de53ad6ca4
commit 256a21743d
5 changed files with 70 additions and 26 deletions

View File

@ -15,12 +15,12 @@
#include "internals.h" #include "internals.h"
/** /**
* i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a * i3c_device_do_xfers() - do I3C transfers directed to a specific device
* specific device
* *
* @dev: device with which the transfers should be done * @dev: device with which the transfers should be done
* @xfers: array of transfers * @xfers: array of transfers
* @nxfers: number of transfers * @nxfers: number of transfers
* @mode: transfer mode
* *
* Initiate one or several private SDR transfers with @dev. * Initiate one or several private SDR transfers with @dev.
* *
@ -33,9 +33,8 @@
* 'xfers' some time later. See I3C spec ver 1.1.1 09-Jun-2021. Section: * 'xfers' some time later. See I3C spec ver 1.1.1 09-Jun-2021. Section:
* 5.1.2.2.3. * 5.1.2.2.3.
*/ */
int i3c_device_do_priv_xfers(struct i3c_device *dev, int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
struct i3c_priv_xfer *xfers, int nxfers, enum i3c_xfer_mode mode)
int nxfers)
{ {
int ret, i; int ret, i;
@ -48,12 +47,12 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev,
} }
i3c_bus_normaluse_lock(dev->bus); i3c_bus_normaluse_lock(dev->bus);
ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers); ret = i3c_dev_do_xfers_locked(dev->desc, xfers, nxfers, mode);
i3c_bus_normaluse_unlock(dev->bus); i3c_bus_normaluse_unlock(dev->bus);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(i3c_device_do_priv_xfers); EXPORT_SYMBOL_GPL(i3c_device_do_xfers);
/** /**
* i3c_device_do_setdasa() - do I3C dynamic address assignement with * i3c_device_do_setdasa() - do I3C dynamic address assignement with
@ -260,6 +259,20 @@ i3c_device_match_id(struct i3c_device *i3cdev,
} }
EXPORT_SYMBOL_GPL(i3c_device_match_id); EXPORT_SYMBOL_GPL(i3c_device_match_id);
/**
* i3c_device_get_supported_xfer_mode - Returns the supported transfer mode by
* connected master controller.
* @dev: I3C device
*
* Return: a bit mask, which supported transfer mode, bit position is defined at
* enum i3c_hdr_mode
*/
u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
{
return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR);
}
EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode);
/** /**
* i3c_driver_register_with_owner() - register an I3C device driver * i3c_driver_register_with_owner() - register an I3C device driver
* *

View File

@ -15,9 +15,9 @@ void i3c_bus_normaluse_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus); void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev); int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev);
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev, int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev,
struct i3c_priv_xfer *xfers, struct i3c_xfer *xfers,
int nxfers); int nxfers, enum i3c_xfer_mode mode);
int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev); int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev);
int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev); int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev);
int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev, int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,

View File

@ -2819,10 +2819,14 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops) static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
{ {
if (!ops || !ops->bus_init || !ops->priv_xfers || if (!ops || !ops->bus_init ||
!ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers) !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers)
return -EINVAL; return -EINVAL;
/* Must provide one of priv_xfers (SDR only) or i3c_xfers (all modes) */
if (!ops->priv_xfers && !ops->i3c_xfers)
return -EINVAL;
if (ops->request_ibi && if (ops->request_ibi &&
(!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi || (!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi ||
!ops->recycle_ibi_slot)) !ops->recycle_ibi_slot))
@ -3012,9 +3016,8 @@ int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev)
dev->boardinfo->init_dyn_addr); dev->boardinfo->init_dyn_addr);
} }
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev, int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
struct i3c_priv_xfer *xfers, int nxfers, enum i3c_xfer_mode mode)
int nxfers)
{ {
struct i3c_master_controller *master; struct i3c_master_controller *master;
@ -3025,9 +3028,15 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
if (!master || !xfers) if (!master || !xfers)
return -EINVAL; return -EINVAL;
if (!master->ops->priv_xfers) if (mode != I3C_SDR && !(master->this->info.hdr_cap & BIT(mode)))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (master->ops->i3c_xfers)
return master->ops->i3c_xfers(dev, xfers, nxfers, mode);
if (mode != I3C_SDR)
return -EINVAL;
return master->ops->priv_xfers(dev, xfers, nxfers); return master->ops->priv_xfers(dev, xfers, nxfers);
} }

View File

@ -39,20 +39,25 @@ enum i3c_error_code {
}; };
/** /**
* enum i3c_hdr_mode - HDR mode ids * enum i3c_xfer_mode - I3C xfer mode ids
* @I3C_HDR_DDR: DDR mode * @I3C_HDR_DDR: DDR mode
* @I3C_HDR_TSP: TSP mode * @I3C_HDR_TSP: TSP mode
* @I3C_HDR_TSL: TSL mode * @I3C_HDR_TSL: TSL mode
* @I3C_SDR: SDR mode (NOT HDR mode)
*/ */
enum i3c_hdr_mode { enum i3c_xfer_mode {
I3C_HDR_DDR, /* The below 3 value (I3C_HDR*) must match GETCAP1 Byte bit position */
I3C_HDR_TSP, I3C_HDR_DDR = 0,
I3C_HDR_TSL, I3C_HDR_TSP = 1,
I3C_HDR_TSL = 2,
/* Use for default SDR transfer mode */
I3C_SDR = 0x31,
}; };
/** /**
* struct i3c_priv_xfer - I3C SDR private transfer * struct i3c_xfer - I3C data transfer
* @rnw: encodes the transfer direction. true for a read, false for a write * @rnw: encodes the transfer direction. true for a read, false for a write
* @cmd: Read/Write command in HDR mode, read: 0x80 - 0xff, write: 0x00 - 0x7f
* @len: transfer length in bytes of the transfer * @len: transfer length in bytes of the transfer
* @actual_len: actual length in bytes are transferred by the controller * @actual_len: actual length in bytes are transferred by the controller
* @data: input/output buffer * @data: input/output buffer
@ -60,8 +65,11 @@ enum i3c_hdr_mode {
* @data.out: output buffer. Must point to a DMA-able buffer * @data.out: output buffer. Must point to a DMA-able buffer
* @err: I3C error code * @err: I3C error code
*/ */
struct i3c_priv_xfer { struct i3c_xfer {
u8 rnw; union {
u8 rnw;
u8 cmd;
};
u16 len; u16 len;
u16 actual_len; u16 actual_len;
union { union {
@ -71,6 +79,9 @@ struct i3c_priv_xfer {
enum i3c_error_code err; enum i3c_error_code err;
}; };
/* keep back compatible */
#define i3c_priv_xfer i3c_xfer
/** /**
* enum i3c_dcr - I3C DCR values * enum i3c_dcr - I3C DCR values
* @I3C_DCR_GENERIC_DEVICE: generic I3C device * @I3C_DCR_GENERIC_DEVICE: generic I3C device
@ -297,9 +308,15 @@ static __always_inline void i3c_i2c_driver_unregister(struct i3c_driver *i3cdrv,
i3c_i2c_driver_unregister, \ i3c_i2c_driver_unregister, \
__i2cdrv) __i2cdrv)
int i3c_device_do_priv_xfers(struct i3c_device *dev, int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
struct i3c_priv_xfer *xfers, int nxfers, enum i3c_xfer_mode mode);
int nxfers);
static inline int i3c_device_do_priv_xfers(struct i3c_device *dev,
struct i3c_priv_xfer *xfers,
int nxfers)
{
return i3c_device_do_xfers(dev, xfers, nxfers, I3C_SDR);
}
int i3c_device_do_setdasa(struct i3c_device *dev); int i3c_device_do_setdasa(struct i3c_device *dev);
@ -341,5 +358,6 @@ int i3c_device_request_ibi(struct i3c_device *dev,
void i3c_device_free_ibi(struct i3c_device *dev); void i3c_device_free_ibi(struct i3c_device *dev);
int i3c_device_enable_ibi(struct i3c_device *dev); int i3c_device_enable_ibi(struct i3c_device *dev);
int i3c_device_disable_ibi(struct i3c_device *dev); int i3c_device_disable_ibi(struct i3c_device *dev);
u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
#endif /* I3C_DEV_H */ #endif /* I3C_DEV_H */

View File

@ -474,9 +474,13 @@ struct i3c_master_controller_ops {
const struct i3c_ccc_cmd *cmd); const struct i3c_ccc_cmd *cmd);
int (*send_ccc_cmd)(struct i3c_master_controller *master, int (*send_ccc_cmd)(struct i3c_master_controller *master,
struct i3c_ccc_cmd *cmd); struct i3c_ccc_cmd *cmd);
/* Deprecated, please use i3c_xfers() */
int (*priv_xfers)(struct i3c_dev_desc *dev, int (*priv_xfers)(struct i3c_dev_desc *dev,
struct i3c_priv_xfer *xfers, struct i3c_priv_xfer *xfers,
int nxfers); int nxfers);
int (*i3c_xfers)(struct i3c_dev_desc *dev,
struct i3c_xfer *xfers,
int nxfers, enum i3c_xfer_mode mode);
int (*attach_i2c_dev)(struct i2c_dev_desc *dev); int (*attach_i2c_dev)(struct i2c_dev_desc *dev);
void (*detach_i2c_dev)(struct i2c_dev_desc *dev); void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
int (*i2c_xfers)(struct i2c_dev_desc *dev, int (*i2c_xfers)(struct i2c_dev_desc *dev,