diff options
author | Dylan Hung <dylan_hung@aspeedtech.com> | 2020-12-14 04:29:01 +0300 |
---|---|---|
committer | Jae Hyun Yoo <jae.hyun.yoo@intel.com> | 2021-07-14 20:04:18 +0300 |
commit | 75d76e1f7122d4268100abf5c461a49947bf72e2 (patch) | |
tree | 0dcbd58c9b6c050f5d8ae647421e08ff7c1dac72 | |
parent | f134969f5c63b4711e752b9d73fa7281b85f6085 (diff) | |
download | linux-75d76e1f7122d4268100abf5c461a49947bf72e2.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>
-rw-r--r-- | arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 196 | ||||
-rw-r--r-- | arch/arm/boot/dts/aspeed-g6.dtsi | 19 | ||||
-rw-r--r-- | drivers/i3c/i3cdev.c | 2 | ||||
-rw-r--r-- | drivers/i3c/master.c | 89 | ||||
-rw-r--r-- | drivers/i3c/master/Kconfig | 8 | ||||
-rw-r--r-- | drivers/i3c/master/Makefile | 2 | ||||
-rw-r--r-- | drivers/i3c/master/aspeed-i3c-global.c | 68 | ||||
-rw-r--r-- | drivers/i3c/master/dw-i3c-master.c | 270 | ||||
-rw-r--r-- | include/linux/i3c/ccc.h | 12 | ||||
-rw-r--r-- | include/linux/i3c/master.h | 1 |
10 files changed, 610 insertions, 57 deletions
diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts index a22ffbc63c97..505abd1f76b0 100644 --- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts @@ -484,21 +484,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>; + }; }; &pcieh { diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index 7a861440ec1d..f09c7d3cfb76 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -1088,6 +1088,7 @@ reg = <0x0 0x1000>; compatible = "aspeed,ast2600-i3c-global"; resets = <&syscon ASPEED_RESET_I3C_DMA>; + ni3cs = <6>; status = "disabled"; }; @@ -1099,7 +1100,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>; @@ -1114,7 +1116,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>; @@ -1129,7 +1132,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>; @@ -1144,7 +1148,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>; @@ -1159,7 +1164,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>; @@ -1174,7 +1180,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/i3cdev.c b/drivers/i3c/i3cdev.c index 07f5641a902d..949715a9dfdb 100644 --- a/drivers/i3c/i3cdev.c +++ b/drivers/i3c/i3cdev.c @@ -32,7 +32,7 @@ struct i3cdev_data { static DEFINE_IDA(i3cdev_ida); static dev_t i3cdev_number; -#define I3C_MINORS 16 /* 16 I3C devices supported for now */ +#define I3C_MINORS 32 /* 32 I3C devices supported for now */ static LIST_HEAD(i3cdev_list); static DEFINE_SPINLOCK(i3cdev_list_lock); diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 0d531f111fdb..a08d052f9580 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; @@ -1455,13 +1495,20 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, if (ret) goto err_free_dev; - ret = i3c_master_setdasa_locked(master, i3cdev->info.static_addr, + if (master->jdec_spd) { + i3cdev->info.dyn_addr = i3cdev->boardinfo->init_dyn_addr; + ret = i3c_master_reattach_i3c_dev(i3cdev, + i3cdev->info.static_addr); + } else { + ret = i3c_master_setdasa_locked(master, + i3cdev->info.static_addr, i3cdev->boardinfo->init_dyn_addr); - if (ret) - goto err_detach_dev; + if (ret) + goto err_detach_dev; - i3cdev->info.dyn_addr = i3cdev->boardinfo->init_dyn_addr; - ret = i3c_master_reattach_i3c_dev(i3cdev, 0); + i3cdev->info.dyn_addr = i3cdev->boardinfo->init_dyn_addr; + ret = i3c_master_reattach_i3c_dev(i3cdev, 0); + } if (ret) goto err_rstdaa; @@ -1536,9 +1583,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; @@ -1677,7 +1729,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 @@ -1765,9 +1817,10 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) goto err_rstdaa; } - i3c_bus_set_addr_slot_status(&master->bus, - i3cboardinfo->init_dyn_addr, - I3C_ADDR_SLOT_I3C_DEV); + if (i3cboardinfo->static_addr != i3cboardinfo->init_dyn_addr) + i3c_bus_set_addr_slot_status(&master->bus, + i3cboardinfo->init_dyn_addr, + I3C_ADDR_SLOT_I3C_DEV); /* * Only try to create/attach devices that have a static @@ -1779,8 +1832,17 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) if (i3cboardinfo->static_addr) i3c_master_early_i3c_dev_add(master, i3cboardinfo); + + 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; @@ -2113,6 +2175,9 @@ 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 7a5e280c580f..9563246f97fc 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); @@ -1171,6 +1404,9 @@ static int dw_i3c_probe(struct platform_device *pdev) if (ret) goto err_assert_rst; + dev_info(&pdev->dev, "i3c bus %d registered, irq %d\n", + master->base.bus.id, irq); + return 0; err_assert_rst: 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; |