* [PATCH v9 1/6] i3c: Add HDR API support
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
@ 2025-10-31 16:39 ` Frank Li
2025-10-31 16:39 ` [PATCH v9 2/6] i3c: Switch to use new i3c_xfer from i3c_priv_xfer Frank Li
` (5 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2025-10-31 16:39 UTC (permalink / raw)
To: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Frank Li
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>
---
Why not add hdr mode in struct i3c_priv_xfer because mode can't be mixed in
one i3c transfer. for example, can't send a HDR follow one SDR between
START and STOP.
i3c_priv_xfer should be treat as whole i3c transactions. If user want send
HDR follow SDR, should be call i3c_device_do_priv_xfers_mode() twice,
instead put into a big i3c_priv_xfer[n].
change in v9
- fix typo Deprecated
- remove reduntant master->ops->priv_xfers check.
change in v8
- new API use i3c_xfer instead of i3c_priv_xfer.
change in v7
- explicit set enum I3C_HDR_* to value, which spec required.
- add comments about check priv_xfers and i3c_xfers
change in v5-v6
- none
change in v4
- Rename enum i3c_hdr_mode to i3c_xfer_mode.
change in v3
- Add Deprecated comment for priv_xfers.
change in v2
- don't use 'priv_' since it is refer to sdr mode transfer in spec.
- add 'mode_mask' indicate controller's capibility.
- add helper function to check master's supported transfer mode.
---
drivers/i3c/device.c | 27 ++++++++++++++++++++-------
drivers/i3c/internals.h | 6 +++---
drivers/i3c/master.c | 19 ++++++++++++++-----
include/linux/i3c/device.h | 40 +++++++++++++++++++++++++++++-----------
include/linux/i3c/master.h | 4 ++++
5 files changed, 70 insertions(+), 26 deletions(-)
diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
index 2396545763ff853097d9f0173787e087f7a6e688..8a156f5ad6929402eb92b152d2e80754dd5a2387 100644
--- a/drivers/i3c/device.c
+++ b/drivers/i3c/device.c
@@ -15,12 +15,12 @@
#include "internals.h"
/**
- * i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a
- * specific device
+ * i3c_device_do_xfers() - do I3C transfers directed to a specific device
*
* @dev: device with which the transfers should be done
* @xfers: array of transfers
* @nxfers: number of transfers
+ * @mode: transfer mode
*
* 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:
* 5.1.2.2.3.
*/
-int i3c_device_do_priv_xfers(struct i3c_device *dev,
- struct i3c_priv_xfer *xfers,
- int nxfers)
+int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode)
{
int ret, i;
@@ -48,12 +47,12 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev,
}
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);
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
@@ -260,6 +259,20 @@ i3c_device_match_id(struct i3c_device *i3cdev,
}
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
*
diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
index 79ceaa5f5afd6f8772db114472cfad99d4dd4341..f609e5098137c1b00db1830a176bb44c2802eb6f 100644
--- a/drivers/i3c/internals.h
+++ b/drivers/i3c/internals.h
@@ -15,9 +15,9 @@ void i3c_bus_normaluse_lock(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_do_priv_xfers_locked(struct i3c_dev_desc *dev,
- struct i3c_priv_xfer *xfers,
- int nxfers);
+int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev,
+ struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode);
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_request_ibi_locked(struct i3c_dev_desc *dev,
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 66513a27e6e776d251203b286bcaecb9d8fc67b9..30c5e5de7963c78735e96605367e9a762d286e86 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2821,10 +2821,14 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
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)
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 &&
(!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi ||
!ops->recycle_ibi_slot))
@@ -3014,9 +3018,8 @@ int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev)
dev->boardinfo->init_dyn_addr);
}
-int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
- struct i3c_priv_xfer *xfers,
- int nxfers)
+int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode)
{
struct i3c_master_controller *master;
@@ -3027,9 +3030,15 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
if (!master || !xfers)
return -EINVAL;
- if (!master->ops->priv_xfers)
+ if (mode != I3C_SDR && !(master->this->info.hdr_cap & BIT(mode)))
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);
}
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 7f136de4b73ef839fb4a1837a87b1aebbddbfe93..7f7738041f3809e538816e94f90b99e58eb806f9 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -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_TSP: TSP mode
* @I3C_HDR_TSL: TSL mode
+ * @I3C_SDR: SDR mode (NOT HDR mode)
*/
-enum i3c_hdr_mode {
- I3C_HDR_DDR,
- I3C_HDR_TSP,
- I3C_HDR_TSL,
+enum i3c_xfer_mode {
+ /* The below 3 value (I3C_HDR*) must match GETCAP1 Byte bit position */
+ I3C_HDR_DDR = 0,
+ 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
+ * @cmd: Read/Write command in HDR mode, read: 0x80 - 0xff, write: 0x00 - 0x7f
* @len: transfer length in bytes of the transfer
* @actual_len: actual length in bytes are transferred by the controller
* @data: input/output buffer
@@ -60,8 +65,11 @@ enum i3c_hdr_mode {
* @data.out: output buffer. Must point to a DMA-able buffer
* @err: I3C error code
*/
-struct i3c_priv_xfer {
- u8 rnw;
+struct i3c_xfer {
+ union {
+ u8 rnw;
+ u8 cmd;
+ };
u16 len;
u16 actual_len;
union {
@@ -71,6 +79,9 @@ struct i3c_priv_xfer {
enum i3c_error_code err;
};
+/* keep back compatible */
+#define i3c_priv_xfer i3c_xfer
+
/**
* enum i3c_dcr - I3C DCR values
* @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, \
__i2cdrv)
-int i3c_device_do_priv_xfers(struct i3c_device *dev,
- struct i3c_priv_xfer *xfers,
- int nxfers);
+int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode);
+
+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);
@@ -341,5 +358,6 @@ int i3c_device_request_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_disable_ibi(struct i3c_device *dev);
+u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
#endif /* I3C_DEV_H */
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index c52a82dd79a63436c1de6a01c11df9e295c1660e..d0d5b3a9049f0b5ff65ae6c5a7d59444b373edec 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -474,9 +474,13 @@ struct i3c_master_controller_ops {
const struct i3c_ccc_cmd *cmd);
int (*send_ccc_cmd)(struct i3c_master_controller *master,
struct i3c_ccc_cmd *cmd);
+ /* Deprecated, please use i3c_xfers() */
int (*priv_xfers)(struct i3c_dev_desc *dev,
struct i3c_priv_xfer *xfers,
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);
void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
int (*i2c_xfers)(struct i2c_dev_desc *dev,
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v9 2/6] i3c: Switch to use new i3c_xfer from i3c_priv_xfer
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
2025-10-31 16:39 ` [PATCH v9 1/6] i3c: Add HDR API support Frank Li
@ 2025-10-31 16:39 ` Frank Li
2025-10-31 16:39 ` [PATCH v9 3/6] i3c: master: svc: Replace bool rnw with union for HDR support Frank Li
` (4 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2025-10-31 16:39 UTC (permalink / raw)
To: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Frank Li
Switch to use i3c_xfer instead of i3c_priv_xfer because framework update to
support HDR mode. i3c_priv_xfer is now an alias of i3c_xfer.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v8
- new patch
---
include/linux/i3c/device.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 7f7738041f3809e538816e94f90b99e58eb806f9..ae0662d9d77eb3fa0c976de1803e9c2ff9547451 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -27,7 +27,7 @@
* These are the standard error codes as defined by the I3C specification.
* When -EIO is returned by the i3c_device_do_priv_xfers() or
* i3c_device_send_hdr_cmds() one can check the error code in
- * &struct_i3c_priv_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
+ * &struct_i3c_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
* what went wrong.
*
*/
@@ -312,7 +312,7 @@ int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
int nxfers, enum i3c_xfer_mode mode);
static inline int i3c_device_do_priv_xfers(struct i3c_device *dev,
- struct i3c_priv_xfer *xfers,
+ struct i3c_xfer *xfers,
int nxfers)
{
return i3c_device_do_xfers(dev, xfers, nxfers, I3C_SDR);
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v9 3/6] i3c: master: svc: Replace bool rnw with union for HDR support
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
2025-10-31 16:39 ` [PATCH v9 1/6] i3c: Add HDR API support Frank Li
2025-10-31 16:39 ` [PATCH v9 2/6] i3c: Switch to use new i3c_xfer from i3c_priv_xfer Frank Li
@ 2025-10-31 16:39 ` Frank Li
2025-10-31 16:39 ` [PATCH v9 4/6] i3c: master: svc: Add basic HDR mode support Frank Li
` (3 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2025-10-31 16:39 UTC (permalink / raw)
To: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Frank Li
Replace the bool rnw field with a union in preparation for adding HDR
support. HDR uses a cmd field instead of the rnw bit to indicate read or
write direction.
Add helper function svc_cmd_is_read() to check transfer direction.
Add a local variable 'rnw' in svc_i3c_master_priv_xfers() to avoid
repeatedly accessing xfers[i].rnw.
No functional change.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v7
none
---
drivers/i3c/master/svc-i3c-master.c | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 9641e66a4e5f2da3bd84b30fa741e5e19d87465d..7c516e05d0a1a118479ee3d8ea8ae37ae19fea57 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -165,7 +165,11 @@
struct svc_i3c_cmd {
u8 addr;
- bool rnw;
+ union {
+ bool rnw;
+ u8 cmd;
+ u32 rnw_cmd;
+ };
u8 *in;
const void *out;
unsigned int len;
@@ -383,6 +387,11 @@ svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
return master->descs[i];
}
+static bool svc_cmd_is_read(u32 rnw_cmd, u32 type)
+{
+ return rnw_cmd;
+}
+
static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
{
writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL);
@@ -1293,10 +1302,11 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
}
static int svc_i3c_master_xfer(struct svc_i3c_master *master,
- bool rnw, unsigned int xfer_type, u8 addr,
+ u32 rnw_cmd, unsigned int xfer_type, u8 addr,
u8 *in, const u8 *out, unsigned int xfer_len,
unsigned int *actual_len, bool continued, bool repeat_start)
{
+ bool rnw = svc_cmd_is_read(rnw_cmd, xfer_type);
int retry = repeat_start ? 1 : 2;
u32 reg;
int ret;
@@ -1484,7 +1494,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
for (i = 0; i < xfer->ncmds; i++) {
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
- ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
+ ret = svc_i3c_master_xfer(master, cmd->rnw_cmd, xfer->type,
cmd->addr, cmd->in, cmd->out,
cmd->len, &cmd->actual_len,
cmd->continued, i > 0);
@@ -1677,14 +1687,15 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
for (i = 0; i < nxfers; i++) {
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
+ bool rnw = xfers[i].rnw;
cmd->xfer = &xfers[i];
cmd->addr = master->addrs[data->index];
- cmd->rnw = xfers[i].rnw;
- cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
- cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
+ cmd->rnw = rnw;
+ cmd->in = rnw ? xfers[i].data.in : NULL;
+ cmd->out = rnw ? NULL : xfers[i].data.out;
cmd->len = xfers[i].len;
- cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
+ cmd->actual_len = rnw ? xfers[i].len : 0;
cmd->continued = (i + 1) < nxfers;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v9 4/6] i3c: master: svc: Add basic HDR mode support
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
` (2 preceding siblings ...)
2025-10-31 16:39 ` [PATCH v9 3/6] i3c: master: svc: Replace bool rnw with union for HDR support Frank Li
@ 2025-10-31 16:39 ` Frank Li
2025-11-03 8:03 ` Andy Shevchenko
2025-10-31 16:39 ` [PATCH v9 5/6] dt-bindings: trivial-devices: add MEMSIC 3-axis magnetometer Frank Li
` (2 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Frank Li @ 2025-10-31 16:39 UTC (permalink / raw)
To: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Frank Li, Carlos Song
Add basic HDR mode support for the svs I3C master driver.
Only support for private transfers and does not support sending CCC
commands in HDR mode.
Key differences:
- HDR uses commands (0x00-0x7F for write, 0x80-0xFF for read) to
distinguish transfer direction.
- HDR read/write commands must be written to FIFO before issuing the I3C
address command. The hardware automatically sends the standard CCC command
to enter HDR mode.
- HDR exit pattern must be sent instead of send a stop after transfer
completion.
- Read/write data size must be an even number.
Co-developed-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v7:
- add comment about why need check return value readl_poll_timeout()
in svc_i3c_master_emit_force_exit()
- add comment about why need udelay(1)
- remove reg = 0;
- chagne to use readl_poll_timeout_atomic();
- replace all i3c_priv_xfer with new i3c_xfer.
change in v4
- use hdr_cap.
change in v3
- rename to svc_cmd_is_read()
- rename to i3c_mode_to_svc_type()
- use local varible bool rnw to reduce change
change in v2
- support HDR DDR write
- rdterm unit is byte, not words (RM is wrong).
---
drivers/i3c/master/svc-i3c-master.c | 96 ++++++++++++++++++++++++++++++++-----
1 file changed, 83 insertions(+), 13 deletions(-)
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 7c516e05d0a1a118479ee3d8ea8ae37ae19fea57..a732443caaf15a2f6e54de46bbafdeb3fc9a9296 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -40,11 +40,13 @@
#define SVC_I3C_MCTRL_REQUEST_NONE 0
#define SVC_I3C_MCTRL_REQUEST_START_ADDR 1
#define SVC_I3C_MCTRL_REQUEST_STOP 2
+#define SVC_I3C_MCTRL_REQUEST_FORCE_EXIT 6
#define SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK 3
#define SVC_I3C_MCTRL_REQUEST_PROC_DAA 4
#define SVC_I3C_MCTRL_REQUEST_AUTO_IBI 7
#define SVC_I3C_MCTRL_TYPE_I3C 0
#define SVC_I3C_MCTRL_TYPE_I2C BIT(4)
+#define SVC_I3C_MCTRL_TYPE_DDR BIT(5)
#define SVC_I3C_MCTRL_IBIRESP_AUTO 0
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7)
@@ -95,6 +97,7 @@
#define SVC_I3C_MINTMASKED 0x098
#define SVC_I3C_MERRWARN 0x09C
#define SVC_I3C_MERRWARN_NACK BIT(2)
+#define SVC_I3C_MERRWARN_CRC BIT(10)
#define SVC_I3C_MERRWARN_TIMEOUT BIT(20)
#define SVC_I3C_MDMACTRL 0x0A0
#define SVC_I3C_MDATACTRL 0x0AC
@@ -174,7 +177,7 @@ struct svc_i3c_cmd {
const void *out;
unsigned int len;
unsigned int actual_len;
- struct i3c_priv_xfer *xfer;
+ struct i3c_xfer *xfer;
bool continued;
};
@@ -389,7 +392,32 @@ svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
static bool svc_cmd_is_read(u32 rnw_cmd, u32 type)
{
- return rnw_cmd;
+ return (type == SVC_I3C_MCTRL_TYPE_DDR) ? !!(rnw_cmd & 0x80) : rnw_cmd;
+}
+
+static void svc_i3c_master_emit_force_exit(struct svc_i3c_master *master)
+{
+ u32 reg;
+
+ writel(SVC_I3C_MCTRL_REQUEST_FORCE_EXIT, master->regs + SVC_I3C_MCTRL);
+
+ /*
+ * Not need check error here because it is never happen at hardware. IP
+ * just wait for few fclk cycle to complete DDR exit pattern. Even
+ * though fclk stop, timeout happen here, the whole data actually
+ * already finish transfer. The next command will be timeout because
+ * wrong hardware state.
+ */
+ readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
+ SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
+
+ /*
+ * This delay is necessary after the emission of a stop, otherwise eg.
+ * repeating IBIs do not get detected. There is a note in the manual
+ * about it, stating that the stop condition might not be settled
+ * correctly if a start condition follows too rapidly.
+ */
+ udelay(1);
}
static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
@@ -521,7 +549,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
* cycle, leading to missed client IBI handlers.
*
* A typical scenario is when IBIWON occurs and bus arbitration is lost
- * at svc_i3c_master_priv_xfers().
+ * at svc_i3c_master_i3c_xfers().
*
* Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
*/
@@ -801,6 +829,8 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
info.dyn_addr = ret;
+ info.hdr_cap = I3C_CCC_HDR_MODE(I3C_HDR_DDR);
+
writel(SVC_MDYNADDR_VALID | SVC_MDYNADDR_ADDR(info.dyn_addr),
master->regs + SVC_I3C_MDYNADDR);
@@ -1314,6 +1344,16 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
/* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+ if (xfer_type == SVC_I3C_MCTRL_TYPE_DDR) {
+ /* DDR command need prefill into FIFO */
+ writel(rnw_cmd, master->regs + SVC_I3C_MWDATAB);
+ if (!rnw) {
+ /* write data also need prefill into FIFO */
+ ret = svc_i3c_master_write(master, out, xfer_len);
+ if (ret)
+ goto emit_stop;
+ }
+ }
while (retry--) {
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
@@ -1407,7 +1447,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
if (rnw)
ret = svc_i3c_master_read(master, in, xfer_len);
- else
+ else if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
ret = svc_i3c_master_write(master, out, xfer_len);
if (ret < 0)
goto emit_stop;
@@ -1420,10 +1460,19 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
if (ret)
goto emit_stop;
+ if (xfer_type == SVC_I3C_MCTRL_TYPE_DDR &&
+ (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_CRC)) {
+ ret = -ENXIO;
+ goto emit_stop;
+ }
+
writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
if (!continued) {
- svc_i3c_master_emit_stop(master);
+ if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
+ svc_i3c_master_emit_stop(master);
+ else
+ svc_i3c_master_emit_force_exit(master);
/* Wait idle if stop is sent. */
readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -1433,7 +1482,11 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
return 0;
emit_stop:
- svc_i3c_master_emit_stop(master);
+ if (xfer_type != SVC_I3C_MCTRL_TYPE_DDR)
+ svc_i3c_master_emit_stop(master);
+ else
+ svc_i3c_master_emit_force_exit(master);
+
svc_i3c_master_clear_merrwarn(master);
svc_i3c_master_flush_fifo(master);
@@ -1480,6 +1533,11 @@ static void svc_i3c_master_dequeue_xfer(struct svc_i3c_master *master,
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
}
+static int i3c_mode_to_svc_type(enum i3c_xfer_mode mode)
+{
+ return (mode == I3C_SDR) ? SVC_I3C_MCTRL_TYPE_I3C : SVC_I3C_MCTRL_TYPE_DDR;
+}
+
static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
{
struct svc_i3c_xfer *xfer = master->xferqueue.cur;
@@ -1669,9 +1727,8 @@ static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
return ret;
}
-static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
- struct i3c_priv_xfer *xfers,
- int nxfers)
+static int svc_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode)
{
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct svc_i3c_master *master = to_svc_i3c_master(m);
@@ -1679,19 +1736,32 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
struct svc_i3c_xfer *xfer;
int ret, i;
+ if (mode != I3C_SDR) {
+ /*
+ * Only support data size less than FIFO SIZE when using DDR
+ * mode. First entry is cmd in FIFO, so actual available FIFO
+ * for data is SVC_I3C_FIFO_SIZE - 2 since DDR only supports
+ * even length.
+ */
+ for (i = 0; i < nxfers; i++)
+ if (xfers[i].len > SVC_I3C_FIFO_SIZE - 2)
+ return -EINVAL;
+ }
+
xfer = svc_i3c_master_alloc_xfer(master, nxfers);
if (!xfer)
return -ENOMEM;
- xfer->type = SVC_I3C_MCTRL_TYPE_I3C;
+ xfer->type = i3c_mode_to_svc_type(mode);
for (i = 0; i < nxfers; i++) {
+ u32 rnw_cmd = (mode == I3C_SDR) ? xfers[i].rnw : xfers[i].cmd;
+ bool rnw = svc_cmd_is_read(rnw_cmd, xfer->type);
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
- bool rnw = xfers[i].rnw;
cmd->xfer = &xfers[i];
cmd->addr = master->addrs[data->index];
- cmd->rnw = rnw;
+ cmd->rnw_cmd = rnw_cmd;
cmd->in = rnw ? xfers[i].data.in : NULL;
cmd->out = rnw ? NULL : xfers[i].data.out;
cmd->len = xfers[i].len;
@@ -1890,7 +1960,7 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
.do_daa = svc_i3c_master_do_daa,
.supports_ccc_cmd = svc_i3c_master_supports_ccc_cmd,
.send_ccc_cmd = svc_i3c_master_send_ccc_cmd,
- .priv_xfers = svc_i3c_master_priv_xfers,
+ .i3c_xfers = svc_i3c_master_i3c_xfers,
.i2c_xfers = svc_i3c_master_i2c_xfers,
.request_ibi = svc_i3c_master_request_ibi,
.free_ibi = svc_i3c_master_free_ibi,
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v9 4/6] i3c: master: svc: Add basic HDR mode support
2025-10-31 16:39 ` [PATCH v9 4/6] i3c: master: svc: Add basic HDR mode support Frank Li
@ 2025-11-03 8:03 ` Andy Shevchenko
0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2025-11-03 8:03 UTC (permalink / raw)
To: Frank Li
Cc: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i3c, linux-kernel, imx, linux-iio,
joshua.yeong, devicetree, linux, Carlos Song
On Fri, Oct 31, 2025 at 12:39:16PM -0400, Frank Li wrote:
> Add basic HDR mode support for the svs I3C master driver.
>
> Only support for private transfers and does not support sending CCC
> commands in HDR mode.
>
> Key differences:
> - HDR uses commands (0x00-0x7F for write, 0x80-0xFF for read) to
> distinguish transfer direction.
> - HDR read/write commands must be written to FIFO before issuing the I3C
> address command. The hardware automatically sends the standard CCC command
> to enter HDR mode.
> - HDR exit pattern must be sent instead of send a stop after transfer
> completion.
> - Read/write data size must be an even number.
...
> static bool svc_cmd_is_read(u32 rnw_cmd, u32 type)
> {
> - return rnw_cmd;
> + return (type == SVC_I3C_MCTRL_TYPE_DDR) ? !!(rnw_cmd & 0x80) : rnw_cmd;
This seems confusing. Either !! is redundant (which is actually the case) or
I don't know what the idea behind this.
> +}
...
> +static void svc_i3c_master_emit_force_exit(struct svc_i3c_master *master)
> +{
> + u32 reg;
> +
> + writel(SVC_I3C_MCTRL_REQUEST_FORCE_EXIT, master->regs + SVC_I3C_MCTRL);
> +
> + /*
> + * Not need check error here because it is never happen at hardware. IP
If you move 'IP' to the next line it will be better to read.
> + * just wait for few fclk cycle to complete DDR exit pattern. Even
> + * though fclk stop, timeout happen here, the whole data actually
> + * already finish transfer. The next command will be timeout because
> + * wrong hardware state.
> + */
> + readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
> + SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
> +
> + /*
> + * This delay is necessary after the emission of a stop, otherwise eg.
> + * repeating IBIs do not get detected. There is a note in the manual
> + * about it, stating that the stop condition might not be settled
> + * correctly if a start condition follows too rapidly.
> + */
> + udelay(1);
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v9 5/6] dt-bindings: trivial-devices: add MEMSIC 3-axis magnetometer
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
` (3 preceding siblings ...)
2025-10-31 16:39 ` [PATCH v9 4/6] i3c: master: svc: Add basic HDR mode support Frank Li
@ 2025-10-31 16:39 ` Frank Li
2025-10-31 16:39 ` [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor Frank Li
2025-11-01 16:25 ` [PATCH v9 0/6] i3c: Add basic HDR mode support Jonathan Cameron
6 siblings, 0 replies; 13+ messages in thread
From: Frank Li @ 2025-10-31 16:39 UTC (permalink / raw)
To: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Frank Li, Conor Dooley
Add compatible string 'memsic,mmc5603' and 'memsic,mmc5633' for
MEMSIC 3-axis magnetometer.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v7
- none
Changes in v6:
- add Conor Dooley ack tag.
Changes in v5
- none
Changes in v4
- add memsic,mmc5603
Changes from v1 .. v3
- None
---
Documentation/devicetree/bindings/trivial-devices.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 2eff6f274302add1ef8f6ae9ec9672697bc812ea..94fc8ff4504b5dc2c0fd7b384f6acaae3d5f80a4 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -227,6 +227,10 @@ properties:
- meas,tsys01
# MEMSIC magnetometer
- memsic,mmc35240
+ # MEMSIC 3-axis magnetometer
+ - memsic,mmc5603
+ # MEMSIC 3-axis magnetometer (Support I3C HDR)
+ - memsic,mmc5633
# MEMSIC 3-axis accelerometer
- memsic,mxc4005
# MEMSIC 2-axis 8-bit digital accelerometer
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
` (4 preceding siblings ...)
2025-10-31 16:39 ` [PATCH v9 5/6] dt-bindings: trivial-devices: add MEMSIC 3-axis magnetometer Frank Li
@ 2025-10-31 16:39 ` Frank Li
2025-11-03 8:13 ` Andy Shevchenko
2025-11-01 16:25 ` [PATCH v9 0/6] i3c: Add basic HDR mode support Jonathan Cameron
6 siblings, 1 reply; 13+ messages in thread
From: Frank Li @ 2025-10-31 16:39 UTC (permalink / raw)
To: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Frank Li, Carlos Song, Adrian Fluturel
Add mmc5633 sensor basic support.
- Support read 20 bits X/Y/Z magnetic.
- Support I3C HDR mode to send start measurememt command.
- Support I3C HDR mode to read all sensors data by one command.
Co-developed-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Co-developed-by: Adrian Fluturel <fluturel.adrian@gmail.com>
Signed-off-by: Adrian Fluturel <fluturel.adrian@gmail.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v9
- add time.h
- remove dev from mmc5633_data
- remove struct {val, val2}
- regmap return value check use if (ret) ...
- 1 -> ARRAY_SIZE()
- use guard() replace scoped_guard()
- use regmap stored dev
- i3c device use bus assigned name.
- use devm_kasprintf() to combine friend name with device ID from i3c bus
and it will avoid build warning to discard const return from dev_name().
Change in v7
- add missed *.h file
- remove reduntant empty line
- add comments about delay 1us after SET
- use USEC_PER_MSEC for timeout value
Change in v6:
- remove acpi part
- return 0 for success path at mmc5633_write_raw
Change in V4
- use { 1, 2000 }
- Add _US for timeout
- Use GEN_MASK for MMC5633_CTRL1_BW_MASK
- Use { } for terminator.
- remove !!
- fix mix tab and space
- add mmc5603 (merge https://lore.kernel.org/all/20251003000731.22927-1-fluturel.adrian@gmail.com/)
- add tempature measure support
Change in v3
- remove mmc5633_hw_set
- make -> Make
- change indention for mmc5633_samp_freq
- use u8 arrary to handle dword data
- get_unaligned_be16() to get raw data
- add helper function to check if i3c support hdr
- use read_avail() callback
change in v2
- new patch
---
drivers/iio/magnetometer/Kconfig | 12 +
drivers/iio/magnetometer/Makefile | 1 +
drivers/iio/magnetometer/mmc5633.c | 598 +++++++++++++++++++++++++++++++++++++
3 files changed, 611 insertions(+)
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 81b812a29044e2b0b9ff84889c21aa3ebc20be35..cfb74a4a083630678a1db1132a14264de451a31a 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -139,6 +139,18 @@ config MMC35240
To compile this driver as a module, choose M here: the module
will be called mmc35240.
+config MMC5633
+ tristate "MEMSIC MMC5633 3-axis magnetic sensor"
+ select REGMAP_I2C
+ select REGMAP_I3C
+ depends on I2C || I3C
+ help
+ Say yes here to build support for the MEMSIC MMC5633 3-axis
+ magnetic sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mmc5633
+
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index dfe970fcacb8664b293af84893f7d3e3e8d7bf7e..5bd227f8c1204bdd8b8a43da180833eedca1457b 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o
+obj-$(CONFIG_MMC5633) += mmc5633.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o
diff --git a/drivers/iio/magnetometer/mmc5633.c b/drivers/iio/magnetometer/mmc5633.c
new file mode 100644
index 0000000000000000000000000000000000000000..55b5bd52abe85cd120ce2249ad2a2cfaad6a6a21
--- /dev/null
+++ b/drivers/iio/magnetometer/mmc5633.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MMC5633 - MEMSIC 3-axis Magnetic Sensor
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2025, NXP
+ *
+ * IIO driver for MMC5633, base on mmc35240.c
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/i3c/device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#define MMC5633_REG_XOUT_L 0x00
+#define MMC5633_REG_XOUT_H 0x01
+#define MMC5633_REG_YOUT_L 0x02
+#define MMC5633_REG_YOUT_H 0x03
+#define MMC5633_REG_ZOUT_L 0x04
+#define MMC5633_REG_ZOUT_H 0x05
+#define MMC5633_REG_XOUT_2 0x06
+#define MMC5633_REG_YOUT_2 0x07
+#define MMC5633_REG_ZOUT_2 0x08
+#define MMC5633_REG_TOUT 0x09
+
+#define MMC5633_REG_STATUS1 0x18
+#define MMC5633_REG_STATUS0 0x19
+#define MMC5633_REG_CTRL0 0x1b
+#define MMC5633_REG_CTRL1 0x1c
+#define MMC5633_REG_CTRL2 0x1d
+
+#define MMC5633_REG_ID 0x39
+
+#define MMC5633_STATUS1_MEAS_T_DONE_BIT BIT(7)
+#define MMC5633_STATUS1_MEAS_M_DONE_BIT BIT(6)
+
+#define MMC5633_CTRL0_CMM_FREQ_EN BIT(7)
+#define MMC5633_CTRL0_AUTO_ST_EN BIT(6)
+#define MMC5633_CTRL0_AUTO_SR_EN BIT(5)
+#define MMC5633_CTRL0_RESET BIT(4)
+#define MMC5633_CTRL0_SET BIT(3)
+#define MMC5633_CTRL0_MEAS_T BIT(1)
+#define MMC5633_CTRL0_MEAS_M BIT(0)
+
+#define MMC5633_CTRL1_BW_MASK GENMASK(1, 0)
+
+#define MMC5633_WAIT_SET_RESET_US (1 * USEC_PER_MSEC)
+
+#define MMC5633_HDR_CTRL0_MEAS_M 0x01
+#define MMC5633_HDR_CTRL0_MEAS_T 0x03
+#define MMC5633_HDR_CTRL0_SET 0x05
+#define MMC5633_HDR_CTRL0_RESET 0x07
+
+enum mmc5633_axis {
+ MMC5633_AXIS_X,
+ MMC5633_AXIS_Y,
+ MMC5633_AXIS_Z,
+ MMC5633_TEMPERATURE,
+};
+
+struct mmc5633_data {
+ struct i3c_device *i3cdev;
+ struct mutex mutex; /* protect to finish one whole measurement */
+ struct regmap *regmap;
+};
+
+int mmc5633_samp_freq[][2] = {
+ { 1, 200000 },
+ { 2, 0 },
+ { 3, 500000 },
+ { 6, 600000 },
+};
+
+#define MMC5633_CHANNEL(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _axis, \
+ .address = MMC5633_AXIS_ ## _axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mmc5633_channels[] = {
+ MMC5633_CHANNEL(X),
+ MMC5633_CHANNEL(Y),
+ MMC5633_CHANNEL(Z),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = MMC5633_TEMPERATURE,
+ },
+};
+
+static int mmc5633_get_samp_freq_index(struct mmc5633_data *data,
+ int val, int val2)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mmc5633_samp_freq); i++)
+ if (mmc5633_samp_freq[i][0] == val &&
+ mmc5633_samp_freq[i][1] == val2)
+ return i;
+ return -EINVAL;
+}
+
+static int mmc5633_init(struct mmc5633_data *data)
+{
+ unsigned int reg_id;
+ int ret;
+
+ ret = regmap_read(data->regmap, MMC5633_REG_ID, ®_id);
+ if (ret)
+ return dev_err_probe(regmap_get_device(data->regmap), ret,
+ "Error reading product id\n");
+
+ /*
+ * Make sure we restore sensor characteristics, by doing
+ * a SET/RESET sequence, the axis polarity being naturally
+ * aligned after RESET.
+ */
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_SET);
+ if (ret)
+ return ret;
+
+ /*
+ * Minimum time interval between SET or RESET to other operations is
+ * 1ms according to Operating Timing Diagram in datasheet.
+ */
+ fsleep(MMC5633_WAIT_SET_RESET_US);
+
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_RESET);
+ if (ret)
+ return ret;
+
+ /* set default sampling frequency */
+ return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
+ MMC5633_CTRL1_BW_MASK,
+ FIELD_PREP(MMC5633_CTRL1_BW_MASK, 0));
+}
+
+static int mmc5633_take_measurement(struct mmc5633_data *data, int address)
+{
+ unsigned int reg_status;
+ int ret;
+ int val;
+
+ val = (address == MMC5633_TEMPERATURE) ? MMC5633_CTRL0_MEAS_T : MMC5633_CTRL0_MEAS_M;
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, val);
+ if (ret < 0)
+ return ret;
+
+ val = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
+ ret = regmap_read_poll_timeout(data->regmap, MMC5633_REG_STATUS1, reg_status,
+ reg_status & val,
+ 10 * USEC_PER_MSEC,
+ 100 * 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(regmap_get_device(data->regmap), "data not ready\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool mmc5633_is_support_hdr(struct mmc5633_data *data)
+{
+ if (!data->i3cdev)
+ return false;
+
+ return i3c_device_get_supported_xfer_mode(data->i3cdev) & BIT(I3C_HDR_DDR);
+}
+
+static int mmc5633_read_measurement(struct mmc5633_data *data, int address, void *buf, size_t sz)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ u8 data_cmd[2], status[2];
+ int ret, val, ready;
+
+ if (mmc5633_is_support_hdr(data)) {
+ struct i3c_xfer xfers_wr_cmd[] = {
+ {
+ .cmd = 0x3b,
+ .len = 2,
+ .data.out = data_cmd,
+ }
+ };
+ struct i3c_xfer xfers_rd_sta_cmd[] = {
+ {
+ .cmd = 0x23 | BIT(7), /* RDSTA CMD */
+ .len = 2,
+ .data.in = status,
+ },
+ };
+ struct i3c_xfer xfers_rd_data_cmd[] = {
+ {
+ .cmd = 0x22 | BIT(7), /* RDLONG CMD */
+ .len = sz,
+ .data.in = buf,
+ },
+ };
+
+ data_cmd[0] = 0;
+ data_cmd[1] = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_HDR_CTRL0_MEAS_T : MMC5633_HDR_CTRL0_MEAS_M;
+
+ ret = i3c_device_do_xfers(data->i3cdev, xfers_wr_cmd,
+ ARRAY_SIZE(xfers_wr_cmd), I3C_HDR_DDR);
+ if (ret < 0)
+ return ret;
+
+ ready = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
+ ret = read_poll_timeout(i3c_device_do_xfers, val,
+ val ||
+ status[0] & ready,
+ 10 * USEC_PER_MSEC,
+ 100 * 10 * USEC_PER_MSEC, 0,
+ data->i3cdev, xfers_rd_sta_cmd,
+ ARRAY_SIZE(xfers_rd_sta_cmd), I3C_HDR_DDR);
+ if (ret) {
+ dev_err(dev, "data not ready\n");
+ return ret;
+ }
+ if (val) {
+ dev_err(dev, "i3c transfer error\n");
+ return val;
+ }
+ return i3c_device_do_xfers(data->i3cdev, xfers_rd_data_cmd,
+ ARRAY_SIZE(xfers_rd_data_cmd), I3C_HDR_DDR);
+ }
+
+ /* Fallback to use SDR/I2C mode */
+ ret = mmc5633_take_measurement(data, address);
+ if (ret < 0)
+ return ret;
+
+ if (address == MMC5633_TEMPERATURE)
+ /*
+ * Put tempeature to last byte of buff to align HDR case.
+ * I3C will early terminate data read if previous data is not
+ * available.
+ */
+ return regmap_bulk_read(data->regmap, MMC5633_REG_TOUT, buf + sz - 1, 1);
+
+ return regmap_bulk_read(data->regmap, MMC5633_REG_XOUT_L, buf, sz);
+}
+
+/* X,Y,Z 3 channels, each channel has 3 byte and TEMP */
+#define MMC5633_ALL_SIZE (3 * 3 + 1)
+
+static int mmc5633_get_raw(struct mmc5633_data *data, int index, unsigned char *buf, int *val)
+{
+ if (index == MMC5633_TEMPERATURE) {
+ *val = buf[MMC5633_ALL_SIZE - 1];
+ return 0;
+ }
+ /*
+ * X[19..12] X[11..4] Y[19..12] Y[11..4] Z[19..12] Z[11..4] X[3..0] Y[3..0] Z[3..0]
+ */
+ *val = get_unaligned_be16(buf + 2 * index) << 4;
+ *val |= buf[index + 6] >> 4;
+
+ return 0;
+}
+
+static int mmc5633_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct mmc5633_data *data = iio_priv(indio_dev);
+ char buf[MMC5633_ALL_SIZE];
+ unsigned int reg, i;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ scoped_guard(mutex, &data->mutex) {
+ ret = mmc5633_read_measurement(data, chan->address, buf, MMC5633_ALL_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = mmc5633_get_raw(data, chan->address, buf, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = 0;
+ *val2 = 62500;
+ } else {
+ *val = 0;
+ *val2 = 800000000; /* 0.8C */
+ }
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ *val = -75;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ scoped_guard(mutex, &data->mutex) {
+ ret = regmap_read(data->regmap, MMC5633_REG_CTRL1, ®);
+ if (ret < 0)
+ return ret;
+ }
+
+ i = FIELD_GET(MMC5633_CTRL1_BW_MASK, reg);
+ if (i >= ARRAY_SIZE(mmc5633_samp_freq))
+ return -EINVAL;
+
+ *val = mmc5633_samp_freq[i][0];
+ *val2 = mmc5633_samp_freq[i][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc5633_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct mmc5633_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = mmc5633_get_samp_freq_index(data, val, val2);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&data->mutex);
+
+ return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
+ MMC5633_CTRL1_BW_MASK,
+ FIELD_PREP(MMC5633_CTRL1_BW_MASK, ret));
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc5633_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type,
+ int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)mmc5633_samp_freq;
+ *length = ARRAY_SIZE(mmc5633_samp_freq) * 2;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mmc5633_info = {
+ .read_raw = mmc5633_read_raw,
+ .write_raw = mmc5633_write_raw,
+ .read_avail = mmc5633_read_avail,
+};
+
+static bool mmc5633_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_CTRL0:
+ case MMC5633_REG_CTRL1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc5633_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_XOUT_L:
+ case MMC5633_REG_XOUT_H:
+ case MMC5633_REG_YOUT_L:
+ case MMC5633_REG_YOUT_H:
+ case MMC5633_REG_ZOUT_L:
+ case MMC5633_REG_ZOUT_H:
+ case MMC5633_REG_XOUT_2:
+ case MMC5633_REG_YOUT_2:
+ case MMC5633_REG_ZOUT_2:
+ case MMC5633_REG_TOUT:
+ case MMC5633_REG_STATUS1:
+ case MMC5633_REG_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc5633_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_CTRL0:
+ case MMC5633_REG_CTRL1:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct reg_default mmc5633_reg_defaults[] = {
+ { MMC5633_REG_CTRL0, 0x00 },
+ { MMC5633_REG_CTRL1, 0x00 },
+};
+
+static const struct regmap_config mmc5633_regmap_config = {
+ .name = "mmc5633_regmap",
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MMC5633_REG_ID,
+ .cache_type = REGCACHE_MAPLE,
+
+ .writeable_reg = mmc5633_is_writeable_reg,
+ .readable_reg = mmc5633_is_readable_reg,
+ .volatile_reg = mmc5633_is_volatile_reg,
+
+ .reg_defaults = mmc5633_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(mmc5633_reg_defaults),
+};
+
+static int mmc5633_common_probe(struct regmap *regmap, char *name,
+ struct i3c_device *i3cdev)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct mmc5633_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ data->regmap = regmap;
+ data->i3cdev = i3cdev;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &mmc5633_info;
+ indio_dev->name = name;
+ indio_dev->channels = mmc5633_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mmc5633_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = mmc5633_init(data);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "mmc5633 chip init failed\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int mmc5633_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, true);
+
+ return 0;
+}
+
+static int mmc5633_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ regcache_mark_dirty(regmap);
+ ret = regcache_sync_region(regmap, MMC5633_REG_CTRL0, MMC5633_REG_CTRL1);
+ if (ret)
+ dev_err(dev, "Failed to restore control registers\n");
+
+ regcache_cache_only(regmap, false);
+
+ return 0;
+}
+
+static int mmc5633_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed");
+
+ ret = regmap_attach_dev(dev, regmap, &mmc5633_regmap_config);
+ if (ret)
+ return ret;
+
+ return mmc5633_common_probe(regmap, client->name, NULL);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mmc5633_pm_ops, mmc5633_suspend, mmc5633_resume);
+
+static const struct of_device_id mmc5633_of_match[] = {
+ { .compatible = "memsic,mmc5603" },
+ { .compatible = "memsic,mmc5633" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mmc5633_of_match);
+
+static const struct i2c_device_id mmc5633_i2c_id[] = {
+ { "mmc5603" },
+ { "mmc5633" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id);
+
+static struct i2c_driver mmc5633_i2c_driver = {
+ .driver = {
+ .name = "mmc5633_i2c",
+ .of_match_table = mmc5633_of_match,
+ .pm = pm_sleep_ptr(&mmc5633_pm_ops),
+ },
+ .probe = mmc5633_i2c_probe,
+ .id_table = mmc5633_i2c_id,
+};
+
+static const struct i3c_device_id mmc5633_i3c_ids[] = {
+ I3C_DEVICE(0x0251, 0x0000, NULL),
+ { }
+};
+MODULE_DEVICE_TABLE(i3c, mmc5633_i3c_ids);
+
+static int mmc5633_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct device *dev = i3cdev_to_dev(i3cdev);
+ struct regmap *regmap;
+ char *name;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "mmc5633(%s)", dev_name(dev));
+ if (!name)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i3c(i3cdev, &mmc5633_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register i3c regmap\n");
+
+ ret = regmap_attach_dev(dev, regmap, &mmc5633_regmap_config);
+ if (ret)
+ return ret;
+
+ return mmc5633_common_probe(regmap, name, i3cdev);
+}
+
+static struct i3c_driver mmc5633_i3c_driver = {
+ .driver = {
+ .name = "mmc5633_i3c",
+ },
+ .probe = mmc5633_i3c_probe,
+ .id_table = mmc5633_i3c_ids,
+};
+module_i3c_i2c_driver(mmc5633_i3c_driver, &mmc5633_i2c_driver)
+
+MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>");
+MODULE_DESCRIPTION("MEMSIC MMC5633 magnetic sensor driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor
2025-10-31 16:39 ` [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor Frank Li
@ 2025-11-03 8:13 ` Andy Shevchenko
2025-11-03 15:57 ` Frank Li
0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2025-11-03 8:13 UTC (permalink / raw)
To: Frank Li
Cc: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i3c, linux-kernel, imx, linux-iio,
joshua.yeong, devicetree, linux, Carlos Song, Adrian Fluturel
On Fri, Oct 31, 2025 at 12:39:18PM -0400, Frank Li wrote:
> Add mmc5633 sensor basic support.
> - Support read 20 bits X/Y/Z magnetic.
> - Support I3C HDR mode to send start measurememt command.
> - Support I3C HDR mode to read all sensors data by one command.
...
> - 1 -> ARRAY_SIZE()
Maybe I missed the answer, but why are the arrays to begin with?
...
> +#define MMC5633_REG_YOUT_H 0x03
> +#define MMC5633_REG_ZOUT_L 0x04
> +#define MMC5633_REG_ZOUT_H 0x05
> +#define MMC5633_REG_XOUT_2 0x06
> +#define MMC5633_REG_YOUT_2 0x07
> +#define MMC5633_REG_ZOUT_2 0x08
> +#define MMC5633_REG_TOUT 0x09
Are those _L, _H, _2 come from the datasheet?
...
> +struct mmc5633_data {
> + struct i3c_device *i3cdev;
> + struct mutex mutex; /* protect to finish one whole measurement */
> + struct regmap *regmap;
Btw, have you experimented to shuffle this to put regmap to be the first
member, for example? Does it affect the binary (compiled object) size?
> +};
...
> + regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
> + if (IS_ERR(regmap))
> + return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed");
Missing trailing \n. Please, double check all messages for this.
...
> + ret = regmap_attach_dev(dev, regmap, &mmc5633_regmap_config);
> + if (ret)
> + return ret;
Why?
...
> + ret = regmap_attach_dev(dev, regmap, &mmc5633_regmap_config);
> + if (ret)
> + return ret;
Ditto.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor
2025-11-03 8:13 ` Andy Shevchenko
@ 2025-11-03 15:57 ` Frank Li
2025-11-03 18:10 ` Andy Shevchenko
0 siblings, 1 reply; 13+ messages in thread
From: Frank Li @ 2025-11-03 15:57 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i3c, linux-kernel, imx, linux-iio,
joshua.yeong, devicetree, linux, Carlos Song, Adrian Fluturel
On Mon, Nov 03, 2025 at 10:13:58AM +0200, Andy Shevchenko wrote:
> On Fri, Oct 31, 2025 at 12:39:18PM -0400, Frank Li wrote:
> > Add mmc5633 sensor basic support.
> > - Support read 20 bits X/Y/Z magnetic.
> > - Support I3C HDR mode to send start measurememt command.
> > - Support I3C HDR mode to read all sensors data by one command.
>
> ...
>
> > - 1 -> ARRAY_SIZE()
>
> Maybe I missed the answer, but why are the arrays to begin with?
>
I3C/I2C transfer API required pass down one array. Keep the same coding
style with existed one, like
source/drivers/base/regmap/regmap-i3c.c
> ...
>
> > +#define MMC5633_REG_YOUT_H 0x03
> > +#define MMC5633_REG_ZOUT_L 0x04
> > +#define MMC5633_REG_ZOUT_H 0x05
> > +#define MMC5633_REG_XOUT_2 0x06
> > +#define MMC5633_REG_YOUT_2 0x07
> > +#define MMC5633_REG_ZOUT_2 0x08
> > +#define MMC5633_REG_TOUT 0x09
>
> Are those _L, _H, _2 come from the datasheet
I will align datasheet, which use 0, 1, 2.
>
>
> > + ret = regmap_attach_dev(dev, regmap, &mmc5633_regmap_config);
> > + if (ret)
> > + return ret;
>
> Why?
I double check code, it should already called in __regmap_init(). It should
be reduntant.
Frank
>
> ...
>
> > + ret = regmap_attach_dev(dev, regmap, &mmc5633_regmap_config);
> > + if (ret)
> > + return ret;
>
>
> Ditto.
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor
2025-11-03 15:57 ` Frank Li
@ 2025-11-03 18:10 ` Andy Shevchenko
0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2025-11-03 18:10 UTC (permalink / raw)
To: Frank Li
Cc: Alexandre Belloni, Miquel Raynal, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i3c, linux-kernel, imx, linux-iio,
joshua.yeong, devicetree, linux, Carlos Song, Adrian Fluturel
On Mon, Nov 03, 2025 at 10:57:22AM -0500, Frank Li wrote:
> On Mon, Nov 03, 2025 at 10:13:58AM +0200, Andy Shevchenko wrote:
> > On Fri, Oct 31, 2025 at 12:39:18PM -0400, Frank Li wrote:
...
> > > - 1 -> ARRAY_SIZE()
> >
> > Maybe I missed the answer, but why are the arrays to begin with?
>
> I3C/I2C transfer API required pass down one array. Keep the same coding
> style with existed one, like
>
> source/drivers/base/regmap/regmap-i3c.c
I can understand why it's done there (there are read and write and they use
different amount of items in the arrays). But here in your case IIRC there
is only one function that uses predefined arrays out of a single entry. So
I do not see a justification for having an array. OTOH it's not a big deal
and I leave it to Jonathan to decide.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v9 0/6] i3c: Add basic HDR mode support
2025-10-31 16:39 [PATCH v9 0/6] i3c: Add basic HDR mode support Frank Li
` (5 preceding siblings ...)
2025-10-31 16:39 ` [PATCH v9 6/6] iio: magnetometer: Add mmc5633 sensor Frank Li
@ 2025-11-01 16:25 ` Jonathan Cameron
2025-11-03 8:15 ` Andy Shevchenko
6 siblings, 1 reply; 13+ messages in thread
From: Jonathan Cameron @ 2025-11-01 16:25 UTC (permalink / raw)
To: Frank Li
Cc: Alexandre Belloni, Miquel Raynal, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-i3c, linux-kernel, imx, linux-iio, joshua.yeong, devicetree,
linux, Carlos Song, Conor Dooley, Adrian Fluturel
On Fri, 31 Oct 2025 12:39:12 -0400
Frank Li <Frank.Li@nxp.com> wrote:
> Add basic HDR mode support, only support private transfer, not support
> CCC command.
>
> Update i3c framework API to allow pass down mode and extend driver callback
> function.
>
> Implement HDR transfer in svc i3c master driver.
>
> Simplifed HDR flow is (ref i3c spec line 5514) Figure 129
>
> <-- SDR ---> | <--- HDR
> START 0x7E RnW(0) ACK CCC(ENTHDR0) T HDR-CMD(00-7f write, 80--ff read)
>
> ----> |
> HDR-DATA HDR-CRC HDR-RESTART .... HDR-EXIT
>
> Note: HDR-CMD is 16bit data, which included 7bit slave address and 8bit
> read/write command.
>
> svc hardware can auto issue SDR part.
>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Hi. Assuming everyone is happy with this version, the next question is how
we expect to merge this. I think someone (either myself or Alexandre most likely)
probably needs to spin an immutable branch with the i3c parts in it.
Then we can merge that into both trees and I can apply the driver on top.
Jonathan
> ---
> Changes in v9:
> - remove reducated check for ops->priv_xfer.
> - improve mmc5633 (see patch's change log for detail)
> - Link to v8: https://lore.kernel.org/r/20251028-i3c_ddr-v8-0-795ded2db8c2@nxp.com
>
> Changes in v8:
> - Add cleanup patch replace framework's i3c_priv_xfer with i3c_xfer
> - Link to v7: https://lore.kernel.org/r/20251027-i3c_ddr-v7-0-866a0ff7fc46@nxp.com
>
> Changes in v7:
> - add explicit define for I3C_HDR_*
> - add missed include files.
> - detail see each patches' change log
> - CONFIG_DEBUG_ATOMIC_SLEEP=y
> - Link to v6: https://lore.kernel.org/r/20251014-i3c_ddr-v6-0-3afe49773107@nxp.com
>
> Changes in v6:
> - remove acpi part
> - collect Conor Dooley ack tags
> - Link to v5: https://lore.kernel.org/r/20251007-i3c_ddr-v5-0-444184f7725e@nxp.com
>
> Changes in v5:
> - Just realized missed CC mail list devicetree@vger.kernel.org and resend
> - Link to v4: https://lore.kernel.org/r/20251007-i3c_ddr-v4-0-3afea5105775@nxp.com
>
> Changes in v4:
> - use master's hdr_cap to check HDR cap.
> - add mmc5603 support.
> - Link to v3: https://lore.kernel.org/r/20250930-i3c_ddr-v3-0-b627dc2ef172@nxp.com
>
> Changes in v3:
> - Add new patch for change rnw to union for svc.
> - Detial changes see each patch's change log.
> - Link to v2: https://lore.kernel.org/r/20250924-i3c_ddr-v2-0-682a0eb32572@nxp.com
>
> Changes in v2:
> - Add sensor driver, which use HDR mode read/write data.
> - change priv_xfer to i3c_xfer.
> - Link to v1: https://lore.kernel.org/r/20250129-i3c_ddr-v1-0-028a7a5d4324@nxp.com
>
> ---
> Frank Li (6):
> i3c: Add HDR API support
> i3c: Switch to use new i3c_xfer from i3c_priv_xfer
> i3c: master: svc: Replace bool rnw with union for HDR support
> i3c: master: svc: Add basic HDR mode support
> dt-bindings: trivial-devices: add MEMSIC 3-axis magnetometer
> iio: magnetometer: Add mmc5633 sensor
>
> .../devicetree/bindings/trivial-devices.yaml | 4 +
> drivers/i3c/device.c | 27 +-
> drivers/i3c/internals.h | 6 +-
> drivers/i3c/master.c | 19 +-
> drivers/i3c/master/svc-i3c-master.c | 115 +++-
> drivers/iio/magnetometer/Kconfig | 12 +
> drivers/iio/magnetometer/Makefile | 1 +
> drivers/iio/magnetometer/mmc5633.c | 598 +++++++++++++++++++++
> include/linux/i3c/device.h | 42 +-
> include/linux/i3c/master.h | 4 +
> 10 files changed, 784 insertions(+), 44 deletions(-)
> ---
> base-commit: df05ef50ada6a8e2fe758adf1b8fa35eea801b2d
> change-id: 20250129-i3c_ddr-b15488901eb8
>
> Best regards,
> --
> Frank Li <Frank.Li@nxp.com>
>
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v9 0/6] i3c: Add basic HDR mode support
2025-11-01 16:25 ` [PATCH v9 0/6] i3c: Add basic HDR mode support Jonathan Cameron
@ 2025-11-03 8:15 ` Andy Shevchenko
0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2025-11-03 8:15 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Frank Li, Alexandre Belloni, Miquel Raynal, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i3c, linux-kernel, imx, linux-iio,
joshua.yeong, devicetree, linux, Carlos Song, Conor Dooley,
Adrian Fluturel
On Sat, Nov 01, 2025 at 04:25:25PM +0000, Jonathan Cameron wrote:
> On Fri, 31 Oct 2025 12:39:12 -0400
> Frank Li <Frank.Li@nxp.com> wrote:
> Assuming everyone is happy with this version,
Almost. Some nit-picks, but the main comments are:
1) arrays out of a single entry;
2) seems unneeded calls to regmap_dev_attach().
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread