summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Hung <dylan_hung@aspeedtech.com>2020-12-14 04:29:01 +0300
committerYoo, Jae Hyun <jae.hyun.yoo@intel.com>2021-01-08 05:22:52 +0300
commit9f8a78c6a5891daeefcc059e03d0088868ad9807 (patch)
treea5b432b410f6d1948369bd4c375c6f42f898893f
parentdb7e148ddb8b0c901e4aae943519f07d613d43fa (diff)
downloadlinux-9f8a78c6a5891daeefcc059e03d0088868ad9807.tar.xz
Update I3C drivers
This commit ports I3C updates from Aspeed SDK v00.06.00. Note: Should be refined to get upstreamed. Signed-off-by: Dylan Hung <dylan_hung@aspeedtech.com> Change-Id: Ic674bf5d6b5e72b389c739b136710915aabc6324
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts196
-rw-r--r--arch/arm/boot/dts/aspeed-g6.dtsi19
-rw-r--r--drivers/i3c/master.c82
-rw-r--r--drivers/i3c/master/Kconfig8
-rw-r--r--drivers/i3c/master/Makefile2
-rw-r--r--drivers/i3c/master/aspeed-i3c-global.c68
-rw-r--r--drivers/i3c/master/dw-i3c-master.c267
-rw-r--r--include/linux/i3c/ccc.h12
-rw-r--r--include/linux/i3c/master.h1
9 files changed, 602 insertions, 53 deletions
diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
index 2f509e34001f..cb1d1297991d 100644
--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
@@ -479,21 +479,217 @@
&i3c0 {
/* I3C_SPD_DDRABCD_CPU0_BMC */
status = "okay";
+ jdec-spd;
+
+ /* Renesas SPD5118 */
+ spd5118_0_0: spd@50,3C000000000 {
+ reg = <0x50 0x3C0 0x00000000>;
+ assigned-address = <0x50>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_1: spd@51,3C000000001 {
+ reg = <0x51 0x3C0 0x00000001>;
+ assigned-address = <0x51>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_2: spd@52,3C000000002 {
+ reg = <0x52 0x3C0 0x00000002>;
+ assigned-address = <0x52>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_3: spd@53,3C000000003 {
+ reg = <0x53 0x3C0 0x00000003>;
+ assigned-address = <0x53>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_4: spd@54,3C000000004 {
+ reg = <0x54 0x3C0 0x00000004>;
+ assigned-address = <0x54>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_5: spd@55,3C000000005 {
+ reg = <0x55 0x3C0 0x00000005>;
+ assigned-address = <0x55>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_6: spd@56,3C000000006 {
+ reg = <0x56 0x3C0 0x00000006>;
+ assigned-address = <0x56>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_0_7: spd@57,3C000000007 {
+ reg = <0x57 0x3C0 0x00000007>;
+ assigned-address = <0x57>;
+ };
};
&i3c1 {
/* I3C_SPD_DDREFGH_CPU0_BMC */
status = "okay";
+ jdec-spd;
+
+ /* Renesas SPD5118 */
+ spd5118_1_0: spd@50,3C000000000 {
+ reg = <0x50 0x3C0 0x00000000>;
+ assigned-address = <0x50>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_1: spd@51,3C000000001 {
+ reg = <0x51 0x3C0 0x00000001>;
+ assigned-address = <0x51>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_2: spd@52,3C000000002 {
+ reg = <0x52 0x3C0 0x00000002>;
+ assigned-address = <0x52>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_3: spd@53,3C000000003 {
+ reg = <0x53 0x3C0 0x00000003>;
+ assigned-address = <0x53>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_4: spd@54,3C000000004 {
+ reg = <0x54 0x3C0 0x00000004>;
+ assigned-address = <0x54>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_5: spd@55,3C000000005 {
+ reg = <0x55 0x3C0 0x00000005>;
+ assigned-address = <0x55>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_6: spd@56,3C000000006 {
+ reg = <0x56 0x3C0 0x00000006>;
+ assigned-address = <0x56>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_1_7: spd@57,3C000000007 {
+ reg = <0x57 0x3C0 0x00000007>;
+ assigned-address = <0x57>;
+ };
};
&i3c2 {
/* I3C_SPD_DDRABCD_CPU1_BMC */
status = "okay";
+ jdec-spd;
+
+ /* Renesas SPD5118 */
+ spd5118_2_0: spd@50,3C000000000 {
+ reg = <0x50 0x3C0 0x00000000>;
+ assigned-address = <0x50>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_1: spd@51,3C000000001 {
+ reg = <0x51 0x3C0 0x00000001>;
+ assigned-address = <0x51>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_2: spd@52,3C000000002 {
+ reg = <0x52 0x3C0 0x00000002>;
+ assigned-address = <0x52>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_3: spd@53,3C000000003 {
+ reg = <0x53 0x3C0 0x00000003>;
+ assigned-address = <0x53>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_4: spd@54,3C000000004 {
+ reg = <0x54 0x3C0 0x00000004>;
+ assigned-address = <0x54>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_5: spd@55,3C000000005 {
+ reg = <0x55 0x3C0 0x00000005>;
+ assigned-address = <0x55>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_6: spd@56,3C000000006 {
+ reg = <0x56 0x3C0 0x00000006>;
+ assigned-address = <0x56>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_2_7: spd@57,3C000000007 {
+ reg = <0x57 0x3C0 0x00000007>;
+ assigned-address = <0x57>;
+ };
};
&i3c3 {
/* I3C_SPD_DDREFGH_CPU1_BMC */
status = "okay";
+ jdec-spd;
+
+ /* Renesas SPD5118 */
+ spd5118_3_0: spd@50,3C000000000 {
+ reg = <0x50 0x3C0 0x00000000>;
+ assigned-address = <0x50>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_1: spd@51,3C000000001 {
+ reg = <0x51 0x3C0 0x00000001>;
+ assigned-address = <0x51>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_2: spd@52,3C000000002 {
+ reg = <0x52 0x3C0 0x00000002>;
+ assigned-address = <0x52>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_3: spd@53,3C000000003 {
+ reg = <0x53 0x3C0 0x00000003>;
+ assigned-address = <0x53>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_4: spd@54,3C000000004 {
+ reg = <0x54 0x3C0 0x00000004>;
+ assigned-address = <0x54>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_5: spd@55,3C000000005 {
+ reg = <0x55 0x3C0 0x00000005>;
+ assigned-address = <0x55>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_6: spd@56,3C000000006 {
+ reg = <0x56 0x3C0 0x00000006>;
+ assigned-address = <0x56>;
+ };
+
+ /* Renesas SPD5118 */
+ spd5118_3_7: spd@57,3C000000007 {
+ reg = <0x57 0x3C0 0x00000007>;
+ assigned-address = <0x57>;
+ };
};
&gfx {
diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index 26e5219f2642..e6b8cc9f6ae5 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -1109,6 +1109,7 @@
reg = <0x0 0x1000>;
compatible = "aspeed,ast2600-i3c-global";
resets = <&syscon ASPEED_RESET_I3C_DMA>;
+ ni3cs = <6>;
status = "disabled";
};
@@ -1120,7 +1121,8 @@
compatible = "snps,dw-i3c-master-1.00a";
clocks = <&syscon ASPEED_CLK_GATE_I3C0CLK>;
resets = <&syscon ASPEED_RESET_I3C0>;
- bus-frequency = <100000>;
+ i2c-scl-hz = <400000>;
+ i3c-scl-hz = <12500000>;
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i3c1_default>;
@@ -1135,7 +1137,8 @@
compatible = "snps,dw-i3c-master-1.00a";
clocks = <&syscon ASPEED_CLK_GATE_I3C1CLK>;
resets = <&syscon ASPEED_RESET_I3C1>;
- bus-frequency = <100000>;
+ i2c-scl-hz = <400000>;
+ i3c-scl-hz = <12500000>;
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i3c2_default>;
@@ -1150,7 +1153,8 @@
compatible = "snps,dw-i3c-master-1.00a";
clocks = <&syscon ASPEED_CLK_GATE_I3C2CLK>;
resets = <&syscon ASPEED_RESET_I3C2>;
- bus-frequency = <100000>;
+ i2c-scl-hz = <400000>;
+ i3c-scl-hz = <12500000>;
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i3c3_default>;
@@ -1165,7 +1169,8 @@
compatible = "snps,dw-i3c-master-1.00a";
clocks = <&syscon ASPEED_CLK_GATE_I3C3CLK>;
resets = <&syscon ASPEED_RESET_I3C3>;
- bus-frequency = <100000>;
+ i2c-scl-hz = <400000>;
+ i3c-scl-hz = <12500000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i3c4_default>;
@@ -1180,7 +1185,8 @@
compatible = "snps,dw-i3c-master-1.00a";
clocks = <&syscon ASPEED_CLK_GATE_I3C4CLK>;
resets = <&syscon ASPEED_RESET_I3C4>;
- bus-frequency = <100000>;
+ i2c-scl-hz = <400000>;
+ i3c-scl-hz = <12500000>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i3c5_default>;
@@ -1195,7 +1201,8 @@
compatible = "snps,dw-i3c-master-1.00a";
clocks = <&syscon ASPEED_CLK_GATE_I3C5CLK>;
resets = <&syscon ASPEED_RESET_I3C5>;
- bus-frequency = <100000>;
+ i2c-scl-hz = <400000>;
+ i3c-scl-hz = <12500000>;
interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i3c6_default>;
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 71bfb7e6763b..c8660166b932 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -994,6 +994,21 @@ static int i3c_master_setda_locked(struct i3c_master_controller *master,
return ret;
}
+static int i3c_master_setaasa_locked(struct i3c_master_controller *master)
+{
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0);
+ i3c_ccc_cmd_init(&cmd, false, I3C_CCC_SETAASA, &dest, 1);
+
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
static int i3c_master_setdasa_locked(struct i3c_master_controller *master,
u8 static_addr, u8 dyn_addr)
{
@@ -1006,6 +1021,26 @@ static int i3c_master_setnewda_locked(struct i3c_master_controller *master,
return i3c_master_setda_locked(master, oldaddr, newaddr, false);
}
+static int i3c_master_sethid_locked(struct i3c_master_controller *master)
+{
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ struct i3c_ccc_sethid *sethid;
+ int ret;
+
+ sethid = i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 1);
+ if (!sethid)
+ return -ENOMEM;
+
+ sethid->hid = 0;
+ i3c_ccc_cmd_init(&cmd, false, I3C_CCC_SETHID, &dest, 1);
+
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
static int i3c_master_getmrl_locked(struct i3c_master_controller *master,
struct i3c_device_info *info)
{
@@ -1240,6 +1275,11 @@ static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
slot_status == I3C_ADDR_SLOT_I2C_DEV)
return -EINVAL;
+ if (master->jdec_spd) {
+ dev->info.pid = dev->boardinfo->pid;
+ return 0;
+ }
+
ret = i3c_master_getpid_locked(master, &dev->info);
if (ret)
return ret;
@@ -1443,13 +1483,19 @@ static int i3c_master_pre_assign_dyn_addr(struct i3c_dev_desc *dev)
!dev->boardinfo->static_addr)
return -1;
- ret = i3c_master_setdasa_locked(master, dev->info.static_addr,
+ if (master->jdec_spd) {
+ dev->info.dyn_addr = dev->boardinfo->init_dyn_addr;
+ ret = i3c_master_reattach_i3c_dev(dev, dev->info.static_addr);
+ } else {
+ ret = i3c_master_setdasa_locked(master, dev->info.static_addr,
dev->boardinfo->init_dyn_addr);
- if (ret)
- return ret;
+ if (ret)
+ return ret;
+
+ dev->info.dyn_addr = dev->boardinfo->init_dyn_addr;
+ ret = i3c_master_reattach_i3c_dev(dev, 0);
+ }
- dev->info.dyn_addr = dev->boardinfo->init_dyn_addr;
- ret = i3c_master_reattach_i3c_dev(dev, 0);
if (ret)
goto err_rstdaa;
@@ -1519,9 +1565,14 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
{
int ret;
- i3c_bus_maintenance_lock(&master->bus);
- ret = master->ops->do_daa(master);
- i3c_bus_maintenance_unlock(&master->bus);
+ if (master->jdec_spd) {
+ ret = i3c_master_sethid_locked(master);
+ ret = i3c_master_setaasa_locked(master);
+ } else {
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = master->ops->do_daa(master);
+ i3c_bus_maintenance_unlock(&master->bus);
+ }
if (ret)
return ret;
@@ -1658,7 +1709,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
struct i3c_dev_boardinfo *i3cboardinfo;
struct i3c_dev_desc *i3cdev, *i3ctmp;
struct i2c_dev_desc *i2cdev;
- int ret;
+ int ret, n_i3cdev = 0;
/*
* First attach all devices with static definitions provided by the
@@ -1761,9 +1812,18 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
if (ret) {
i3c_master_detach_i3c_dev(i3cdev);
i3c_master_free_i3c_dev(i3cdev);
+ } else {
+ n_i3cdev++;
}
}
+ /*
+ * Since SPD devices are all with static address. Don't do DAA if we
+ * know it is a pure I2C bus.
+ */
+ if ((master->jdec_spd) && (n_i3cdev == 0))
+ return 0;
+
ret = i3c_master_do_daa(master);
if (ret)
goto err_rstdaa;
@@ -2096,6 +2156,10 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
if (!i3cbus_np)
return 0;
+ if (of_get_property(i3cbus_np, "jdec-spd", NULL)) {
+ master->jdec_spd = 1;
+ }
+
for_each_available_child_of_node(i3cbus_np, node) {
ret = of_i3c_master_add_dev(master, node);
if (ret) {
diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
index 693f9aba2b17..0eecba763754 100644
--- a/drivers/i3c/master/Kconfig
+++ b/drivers/i3c/master/Kconfig
@@ -22,7 +22,11 @@ config DW_I3C_MASTER
This driver can also be built as a module. If so, the module
will be called dw-i3c-master.
-config ASPEED_I3C_GLOBAL
- tristate "ASPEED I3C global driver"
+config ASPEED_I3C_MASTER
+ tristate "Aspeed I3C master driver"
depends on I3C
depends on MACH_ASPEED_G6
+ select DW_I3C_MASTER
+ help
+ Aspeed I3C master controller is a Synopsys DesignWare I3C controller
+ plus additional global control.
diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
index b5ec8e8dd622..2dbfed073cb8 100644
--- a/drivers/i3c/master/Makefile
+++ b/drivers/i3c/master/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_ASPEED_I3C_GLOBAL) += aspeed-i3c-global.o
obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o
obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
+obj-$(CONFIG_ASPEED_I3C_MASTER) += aspeed-i3c-global.o
diff --git a/drivers/i3c/master/aspeed-i3c-global.c b/drivers/i3c/master/aspeed-i3c-global.c
index 0283a3d7977f..feb6c9361f24 100644
--- a/drivers/i3c/master/aspeed-i3c-global.c
+++ b/drivers/i3c/master/aspeed-i3c-global.c
@@ -15,34 +15,57 @@
#include <linux/reset.h>
#include <linux/delay.h>
-#define ASPEED_I3CG_CTRL(x) (0x10 + (x*0x10))
-#define ASPEED_I3CG_SET(x) (0x14 + (x*0x10))
+#define ASPEED_I3CG_CTRL(x) (0x10 + (x * 0x10))
+#define ASPEED_I3CG_SET(x) (0x14 + (x * 0x10))
+
+#define DEF_SLV_INST_ID 0x4
+#define DEF_SLV_STATIC_ADDR 0x74
+
+union i3c_set_reg {
+ uint32_t value;
+ struct {
+ unsigned int i2c_mode : 1; /* bit[0] */
+ unsigned int test_mode : 1; /* bit[1] */
+ unsigned int act_mode : 2; /* bit[ 3: 2] */
+ unsigned int pending_int : 4; /* bit[ 7: 4] */
+ unsigned int sa : 7; /* bit[14: 8] */
+ unsigned int sa_en : 1; /* bit[15] */
+ unsigned int inst_id : 4; /* bit[19:16] */
+ unsigned int rsvd : 12; /* bit[31:20] */
+ } fields;
+};
+
struct aspeed_i3c_global {
void __iomem *base;
struct reset_control *rst;
};
+static const struct of_device_id aspeed_i3c_of_match[] = {
+ { .compatible = "aspeed,ast2600-i3c-global", },
+ {},
+};
+
static int aspeed_i3c_global_probe(struct platform_device *pdev)
{
struct aspeed_i3c_global *i3c_global;
struct device_node *node = pdev->dev.of_node;
- int i = 0;
+ union i3c_set_reg reg;
+ int i, ret;
+ u32 num_of_i3cs;
i3c_global = kzalloc(sizeof(*i3c_global), GFP_KERNEL);
if (!i3c_global)
return -ENOMEM;
i3c_global->base = of_iomap(node, 0);
- if (!i3c_global->base) {
+ if (!i3c_global->base)
return -ENOMEM;
- }
i3c_global->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
-
if (IS_ERR(i3c_global->rst)) {
- if (PTR_ERR(i3c_global->rst) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "missing or invalid reset controller device tree entry\n");
+ dev_err(&pdev->dev,
+ "missing or invalid reset controller device tree entry");
return PTR_ERR(i3c_global->rst);
}
@@ -50,19 +73,23 @@ static int aspeed_i3c_global_probe(struct platform_device *pdev)
udelay(3);
reset_control_deassert(i3c_global->rst);
- /* init */
- for(i = 0; i < 5; i++)
- writel(0x000474c4, i3c_global->base + ASPEED_I3CG_SET(i));
+ ret = of_property_read_u32(pdev->dev.of_node, "ni3cs", &num_of_i3cs);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to get number of i3c devices");
+ return -EINVAL;
+ }
+
+ reg.value = 0;
+ reg.fields.inst_id = DEF_SLV_INST_ID;
+ reg.fields.sa = DEF_SLV_STATIC_ADDR;
+ reg.fields.pending_int = 0xc;
+ reg.fields.act_mode = 0x1;
+ for (i = 0; i < num_of_i3cs; i++)
+ writel(reg.value, i3c_global->base + ASPEED_I3CG_SET(i));
return 0;
}
-static const struct of_device_id aspeed_i3c_of_match[] = {
- { .compatible = "aspeed,ast2600-i3c-global", },
- { },
-};
-MODULE_DEVICE_TABLE(of, aspeed_i3c_of_match);
-
static struct platform_driver aspeed_i3c_driver = {
.probe = aspeed_i3c_global_probe,
.driver = {
@@ -70,7 +97,12 @@ static struct platform_driver aspeed_i3c_driver = {
.of_match_table = aspeed_i3c_of_match,
},
};
-module_platform_driver(aspeed_i3c_driver);
+
+static int __init aspeed_i3c_global_init(void)
+{
+ return platform_driver_register(&aspeed_i3c_driver);
+}
+postcore_initcall(aspeed_i3c_global_init);
MODULE_AUTHOR("Ryan Chen");
MODULE_DESCRIPTION("ASPEED I3C Global Driver");
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index e23ebcdff6e9..437c616fe0d0 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -21,11 +21,14 @@
#include <linux/reset.h>
#include <linux/slab.h>
+//#define IBI_WIP
+#define CCC_WORKAROUND
#define DEVICE_CTRL 0x0
#define DEV_CTRL_ENABLE BIT(31)
#define DEV_CTRL_RESUME BIT(30)
#define DEV_CTRL_HOT_JOIN_NACK BIT(8)
#define DEV_CTRL_I2C_SLAVE_PRESENT BIT(7)
+#define DEV_CTRL_IBI_DATA_EN BIT(1)
#define DEVICE_ADDR 0x4
#define DEV_ADDR_DYNAMIC_ADDR_VALID BIT(31)
@@ -74,7 +77,14 @@
#define RX_TX_DATA_PORT 0x14
#define IBI_QUEUE_STATUS 0x18
+#define IBI_QUEUE_DATA 0x18
+#define IBI_QUEUE_DATA_STATUS_MASK GENMASK(31, 28)
+#define IBI_QUEUE_DATA_PAYLOAD_MASK GENMASK(15, 8)
#define QUEUE_THLD_CTRL 0x1c
+#define QUEUE_THLD_CTRL_IBI_STA_MASK GENMASK(31, 24)
+#define QUEUE_THLD_CTRL_IBI_STA(x) (((x) - 1) << 24)
+#define QUEUE_THLD_CTRL_IBI_DAT_MASK GENMASK(23, 16)
+#define QUEUE_THLD_CTRL_IBI_DAT(x) ((x) << 16)
#define QUEUE_THLD_CTRL_RESP_BUF_MASK GENMASK(15, 8)
#define QUEUE_THLD_CTRL_RESP_BUF(x) (((x) - 1) << 8)
@@ -125,9 +135,14 @@
INTR_IBI_THLD_STAT | \
INTR_TX_THLD_STAT | \
INTR_RX_THLD_STAT)
-
+#ifdef IBI_WIP
+#define INTR_MASTER_MASK (INTR_TRANSFER_ERR_STAT | \
+ INTR_RESP_READY_STAT | \
+ INTR_IBI_THLD_STAT)
+#else
#define INTR_MASTER_MASK (INTR_TRANSFER_ERR_STAT | \
INTR_RESP_READY_STAT)
+#endif
#define QUEUE_STATUS_LEVEL 0x4c
#define QUEUE_STATUS_IBI_STATUS_CNT(x) (((x) & GENMASK(28, 24)) >> 24)
@@ -185,7 +200,14 @@
#define SLAVE_CONFIG 0xec
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
+#ifdef IBI_WIP
+#define DEV_ADDR_TABLE_IBI_WITH_DATA BIT(12)
+#define DEV_ADDR_TABLE_IBI_PEC_EN BIT(11)
+#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) \
+ ((((x) << 16) & GENMASK(23, 16)) | DEV_ADDR_TABLE_IBI_WITH_DATA)
+#else
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
+#endif
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
@@ -198,6 +220,10 @@
#define I3C_BUS_I2C_FM_TLOW_MIN_NS 1300
#define I3C_BUS_I2C_FMP_TLOW_MIN_NS 500
#define I3C_BUS_THIGH_MAX_NS 41
+#define I3C_BUS_OP_TLOW_MIN_NS 500
+#define I3C_BUS_OP_THIGH_MIN_NS 260
+#define I3C_BUS_PP_TLOW_MIN_NS 35
+#define I3C_BUS_PP_THIGH_MIN_NS 35
#define XFER_TIMEOUT (msecs_to_jiffies(1000))
@@ -225,6 +251,7 @@ struct dw_i3c_xfer {
};
struct dw_i3c_master {
+ struct device *dev;
struct i3c_master_controller base;
u16 maxdevs;
u16 datstartaddr;
@@ -286,6 +313,8 @@ static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
case I3C_CCC_GETSTATUS:
case I3C_CCC_GETMXDS:
case I3C_CCC_GETHDRCAP:
+ case I3C_CCC_SETAASA:
+ case I3C_CCC_SETHID:
return true;
default:
return false;
@@ -306,7 +335,7 @@ static void dw_i3c_master_disable(struct dw_i3c_master *master)
static void dw_i3c_master_enable(struct dw_i3c_master *master)
{
- writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_ENABLE,
+ writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_ENABLE | DEV_CTRL_IBI_DATA_EN,
master->regs + DEVICE_CTRL);
}
@@ -339,6 +368,7 @@ static void dw_i3c_master_wr_tx_fifo(struct dw_i3c_master *master,
memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3);
writesl(master->regs + RX_TX_DATA_PORT, &tmp, 1);
+ dev_dbg(master->dev, "TX data = %08x\n", tmp);
}
}
@@ -454,6 +484,27 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
int i, ret = 0;
u32 nresp;
+#ifdef IBI_WIP
+ int j = 0;
+ u32 nibi;
+
+ /* consume the IBI data */
+ nibi = readl(master->regs + QUEUE_STATUS_LEVEL);
+ nibi = QUEUE_STATUS_IBI_BUF_BLR(nibi);
+
+ if ((isr & INTR_IBI_THLD_STAT) && nibi) {
+ u32 ibi;
+
+ for (i = 0; i < nibi; i++) {
+ ibi = readl(master->regs + IBI_QUEUE_DATA);
+ for (j = 0; j < (ibi & 0xff); j += 4)
+ dev_dbg(master->dev, "ibi: %08x\n",
+ readl(master->regs + IBI_QUEUE_DATA));
+ }
+ writel(RESET_CTRL_IBI_QUEUE, master->regs + RESET_CTRL);
+ }
+#endif
+
if (!xfer)
return;
@@ -517,7 +568,7 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
{
- unsigned long core_rate, core_period;
+ unsigned long core_rate, core_period, scl_period_h, scl_period_l;
u32 scl_timing;
u8 hcnt, lcnt;
@@ -527,23 +578,71 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
core_period = DIV_ROUND_UP(1000000000, core_rate);
- hcnt = DIV_ROUND_UP(I3C_BUS_THIGH_MAX_NS, core_period) - 1;
- if (hcnt < SCL_I3C_TIMING_CNT_MIN)
- hcnt = SCL_I3C_TIMING_CNT_MIN;
+ if (master->base.jdec_spd) {
+ /* set open-drain timing according to I2C SCL frequency */
+ if (master->base.bus.scl_rate.i2c) {
+ scl_period_h = scl_period_l =
+ DIV_ROUND_UP(1000000000,
+ master->base.bus.scl_rate.i2c) >> 1;
+ } else {
+ /* default: I2C SCL = 400kHz (fast mode) */
+ scl_period_h = scl_period_l =
+ DIV_ROUND_UP(1000000000, 400000) >> 1;
+ }
+
+ if (scl_period_h < I3C_BUS_OP_THIGH_MIN_NS)
+ scl_period_h = I3C_BUS_OP_THIGH_MIN_NS;
+ if (scl_period_l < I3C_BUS_OP_TLOW_MIN_NS)
+ scl_period_l = I3C_BUS_OP_TLOW_MIN_NS;
+ hcnt = DIV_ROUND_UP(scl_period_h, core_period) + 1;
+ lcnt = DIV_ROUND_UP(scl_period_l, core_period) + 1;
+ scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
+ writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
+ scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) | SCL_I2C_FM_TIMING_LCNT(lcnt);
+ writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
+ scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) | SCL_I2C_FMP_TIMING_LCNT(lcnt);
+ writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
+
+ if (!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_I2C_SLAVE_PRESENT))
+ writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
+
+ /* set push-pull timing according to I3C SCL frequency */
+ if (master->base.bus.scl_rate.i3c) {
+ scl_period_h = scl_period_l =
+ DIV_ROUND_UP(1000000000,
+ master->base.bus.scl_rate.i3c) >> 1;
+ } else {
+ /* default: I3C SCL = 12.5MHz */
+ scl_period_h = scl_period_l =
+ DIV_ROUND_UP(1000000000, 12500000) >> 1;
+ }
+ if (scl_period_h < I3C_BUS_PP_THIGH_MIN_NS)
+ scl_period_h = I3C_BUS_PP_THIGH_MIN_NS;
+ if (scl_period_l < I3C_BUS_PP_TLOW_MIN_NS)
+ scl_period_l = I3C_BUS_PP_TLOW_MIN_NS;
+ hcnt = DIV_ROUND_UP(scl_period_h, core_period) + 1;
+ lcnt = DIV_ROUND_UP(scl_period_l, core_period) + 1;
+ scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
+ writel(scl_timing, master->regs + SCL_I3C_PP_TIMING);
+ } else {
+ hcnt = DIV_ROUND_UP(I3C_BUS_THIGH_MAX_NS, core_period) - 1;
+ if (hcnt < SCL_I3C_TIMING_CNT_MIN)
+ hcnt = SCL_I3C_TIMING_CNT_MIN;
- lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_TYP_I3C_SCL_RATE) - hcnt;
- if (lcnt < SCL_I3C_TIMING_CNT_MIN)
- lcnt = SCL_I3C_TIMING_CNT_MIN;
+ lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_TYP_I3C_SCL_RATE) - hcnt;
+ if (lcnt < SCL_I3C_TIMING_CNT_MIN)
+ lcnt = SCL_I3C_TIMING_CNT_MIN;
- scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
- writel(scl_timing, master->regs + SCL_I3C_PP_TIMING);
+ scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
+ writel(scl_timing, master->regs + SCL_I3C_PP_TIMING);
- if (!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_I2C_SLAVE_PRESENT))
- writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
+ if (!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_I2C_SLAVE_PRESENT))
+ writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
- lcnt = DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period);
- scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
- writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
+ lcnt = DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period);
+ scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
+ writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
+ }
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt;
scl_timing = SCL_EXT_LCNT_1(lcnt);
@@ -639,8 +738,20 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
if (ret)
return ret;
+#ifdef IBI_WIP
+ thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
+ thld_ctrl &=
+ ~(QUEUE_THLD_CTRL_IBI_STA_MASK | QUEUE_THLD_CTRL_IBI_DAT_MASK);
+ thld_ctrl |= QUEUE_THLD_CTRL_IBI_STA(1);
+ thld_ctrl |= QUEUE_THLD_CTRL_IBI_DAT(1);
+ writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
+
+ writel(0, master->regs + IBI_SIR_REQ_REJECT);
+ writel(0, master->regs + IBI_MR_REQ_REJECT);
+#else
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
+#endif
/* For now don't support Hot-Join */
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
@@ -688,6 +799,9 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master,
COMMAND_PORT_TOC |
COMMAND_PORT_ROC;
+ dev_dbg(master->dev, "%s:cmd_hi=0x%08x cmd_lo=0x%08x tx_len=%d id=%x\n",
+ __func__, cmd->cmd_hi, cmd->cmd_lo, cmd->tx_len, ccc->id);
+
dw_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
dw_i3c_master_dequeue_xfer(master, xfer);
@@ -729,6 +843,9 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
COMMAND_PORT_TOC |
COMMAND_PORT_ROC;
+ dev_dbg(master->dev, "%s:cmd_hi=0x%08x cmd_lo=0x%08x rx_len=%d id=%x\n",
+ __func__, cmd->cmd_hi, cmd->cmd_lo, cmd->rx_len, ccc->id);
+
dw_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
dw_i3c_master_dequeue_xfer(master, xfer);
@@ -746,15 +863,26 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
{
struct dw_i3c_master *master = to_dw_i3c_master(m);
int ret = 0;
+ u32 i3c_pp_timing, i3c_od_timing;
if (ccc->id == I3C_CCC_ENTDAA)
return -EINVAL;
+ i3c_od_timing = readl(master->regs + SCL_I3C_OD_TIMING);
+ i3c_pp_timing = readl(master->regs + SCL_I3C_PP_TIMING);
+ if ((ccc->id == I3C_CCC_SETAASA) || (ccc->id == I3C_CCC_SETHID) ||
+ (ccc->id == I3C_CCC_DEVCTRL)) {
+ writel(i3c_od_timing, master->regs + SCL_I3C_PP_TIMING);
+ }
+
if (ccc->rnw)
ret = dw_i3c_ccc_get(master, ccc);
else
ret = dw_i3c_ccc_set(master, ccc);
+ if ((ccc->id == I3C_CCC_SETAASA) || (ccc->id == I3C_CCC_SETHID))
+ writel(i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
+
return ret;
}
@@ -815,15 +943,97 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
}
dw_i3c_master_free_xfer(xfer);
-
+#ifdef IBI_WIP
+ ret = i3c_master_enec_locked(m, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR);
+#else
i3c_master_disec_locked(m, I3C_BROADCAST_ADDR,
I3C_CCC_EVENT_HJ |
I3C_CCC_EVENT_MR |
I3C_CCC_EVENT_SIR);
+#endif
return 0;
}
+#ifdef CCC_WORKAROUND
+/*
+ * Provide an interface for sending CCC from userspace. Especially for the
+ * transfers with PEC and direct CCC.
+ */
+static int dw_i3c_master_ccc_xfers(struct i3c_dev_desc *dev,
+ struct i3c_priv_xfer *i3c_xfers,
+ int i3c_nxfers)
+{
+ struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ struct dw_i3c_xfer *xfer;
+ int i, ret = 0;
+ struct dw_i3c_cmd *cmd_ccc;
+ xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
+ if (!xfer)
+ return -ENOMEM;
+
+ /* i3c_xfers[0] handles the CCC data */
+ cmd_ccc = &xfer->cmds[0];
+ cmd_ccc->cmd_hi = COMMAND_PORT_ARG_DATA_LEN(i3c_xfers[0].len - 1) |
+ COMMAND_PORT_TRANSFER_ARG;
+ cmd_ccc->tx_buf = i3c_xfers[0].data.out + 1;
+ cmd_ccc->tx_len = i3c_xfers[0].len - 1;
+ cmd_ccc->cmd_lo = COMMAND_PORT_SPEED(dev->info.max_write_ds);
+ cmd_ccc->cmd_lo |= COMMAND_PORT_TID(0) |
+ COMMAND_PORT_DEV_INDEX(master->maxdevs - 1) |
+ COMMAND_PORT_ROC;
+ if (i3c_nxfers == 1)
+ cmd_ccc->cmd_lo |= COMMAND_PORT_TOC;
+
+ dev_dbg(master->dev,
+ "%s:cmd_ccc_hi=0x%08x cmd_ccc_lo=0x%08x tx_len=%d\n", __func__,
+ cmd_ccc->cmd_hi, cmd_ccc->cmd_lo, cmd_ccc->tx_len);
+
+ for (i = 1; i < i3c_nxfers; i++) {
+ struct dw_i3c_cmd *cmd = &xfer->cmds[i];
+
+ cmd->cmd_hi = COMMAND_PORT_ARG_DATA_LEN(i3c_xfers[i].len) |
+ COMMAND_PORT_TRANSFER_ARG;
+
+ if (i3c_xfers[i].rnw) {
+ cmd->rx_buf = i3c_xfers[i].data.in;
+ cmd->rx_len = i3c_xfers[i].len;
+ cmd->cmd_lo = COMMAND_PORT_READ_TRANSFER |
+ COMMAND_PORT_SPEED(dev->info.max_read_ds);
+
+ } else {
+ cmd->tx_buf = i3c_xfers[i].data.out;
+ cmd->tx_len = i3c_xfers[i].len;
+ cmd->cmd_lo =
+ COMMAND_PORT_SPEED(dev->info.max_write_ds);
+ }
+
+ cmd->cmd_lo |= COMMAND_PORT_TID(i) |
+ COMMAND_PORT_DEV_INDEX(data->index) |
+ COMMAND_PORT_ROC;
+
+ if (i == (i3c_nxfers - 1))
+ cmd->cmd_lo |= COMMAND_PORT_TOC;
+
+ dev_dbg(master->dev,
+ "%s:cmd_hi=0x%08x cmd_lo=0x%08x tx_len=%d rx_len=%d\n",
+ __func__, cmd->cmd_hi, cmd->cmd_lo, cmd->tx_len,
+ cmd->rx_len);
+ }
+
+ dw_i3c_master_enqueue_xfer(master, xfer);
+ if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+ dw_i3c_master_dequeue_xfer(master, xfer);
+
+ ret = xfer->ret;
+ dw_i3c_master_free_xfer(xfer);
+
+ return ret;
+}
+#endif
static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
struct i3c_priv_xfer *i3c_xfers,
int i3c_nxfers)
@@ -852,6 +1062,17 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
nrxwords > master->caps.datafifodepth)
return -ENOTSUPP;
+#ifdef CCC_WORKAROUND
+ if (i3c_xfers[0].rnw == 0) {
+ /* write command: check if hit special address */
+ u8 tmp;
+
+ memcpy(&tmp, i3c_xfers[0].data.out, 1);
+ if (tmp == 0xff)
+ return dw_i3c_master_ccc_xfers(dev, i3c_xfers, i3c_nxfers);
+ }
+#endif
+
xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
if (!xfer)
return -ENOMEM;
@@ -881,6 +1102,11 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (i == (i3c_nxfers - 1))
cmd->cmd_lo |= COMMAND_PORT_TOC;
+
+ dev_dbg(master->dev,
+ "%s:cmd_hi=0x%08x cmd_lo=0x%08x tx_len=%d rx_len=%d\n",
+ __func__, cmd->cmd_hi, cmd->cmd_lo, cmd->tx_len,
+ cmd->rx_len);
}
dw_i3c_master_enqueue_xfer(master, xfer);
@@ -1157,6 +1383,13 @@ static int dw_i3c_probe(struct platform_device *pdev)
master->datstartaddr = ret;
master->maxdevs = ret >> 16;
master->free_pos = GENMASK(master->maxdevs - 1, 0);
+#ifdef CCC_WORKAROUND
+ master->free_pos &= ~BIT(master->maxdevs - 1);
+ ret = (even_parity(I3C_BROADCAST_ADDR) << 7) | I3C_BROADCAST_ADDR;
+ master->addrs[master->maxdevs - 1] = ret;
+ writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(ret),
+ master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, master->maxdevs - 1));
+#endif
writel(INTR_ALL, master->regs + INTR_STATUS);
irq = platform_get_irq(pdev, 0);
diff --git a/include/linux/i3c/ccc.h b/include/linux/i3c/ccc.h
index 73b0982cc519..a7a19ebe6b6d 100644
--- a/include/linux/i3c/ccc.h
+++ b/include/linux/i3c/ccc.h
@@ -32,6 +32,9 @@
#define I3C_CCC_DEFSLVS I3C_CCC_ID(0x8, true)
#define I3C_CCC_ENTTM I3C_CCC_ID(0xb, true)
#define I3C_CCC_ENTHDR(x) I3C_CCC_ID(0x20 + (x), true)
+#define I3C_CCC_SETAASA I3C_CCC_ID(0x29, true)
+#define I3C_CCC_SETHID I3C_CCC_ID(0x61, true)
+#define I3C_CCC_DEVCTRL I3C_CCC_ID(0x62, true)
/* Unicast-only commands */
#define I3C_CCC_SETDASA I3C_CCC_ID(0x7, false)
@@ -243,6 +246,15 @@ struct i3c_ccc_setbrgtgt {
struct i3c_ccc_bridged_slave_desc bslaves[0];
} __packed;
+
+/**
+ * struct i3c_ccc_sethid - payload passed to SETHID CCC
+ *
+ * @hid: 3-bit HID
+ */
+struct i3c_ccc_sethid {
+ u8 hid;
+};
/**
* enum i3c_sdr_max_data_rate - max data rate values for private SDR transfers
*/
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 9cb39d901cd5..11cdcc062b3e 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -487,6 +487,7 @@ struct i3c_master_controller {
const struct i3c_master_controller_ops *ops;
unsigned int secondary : 1;
unsigned int init_done : 1;
+ unsigned int jdec_spd : 1;
struct {
struct list_head i3c;
struct list_head i2c;