Skip to content

Commit 4d6673b

Browse files
Shreehari-AlifSemicfriedt
authored andcommitted
drivers: i3c: dw: enhance open-drain timing configuration
This commit improves the I3C handling of open-drain timing configurations with the following changes: - Add OD minimum high_ns and low_ns DT properties - Implement dw_i3c_init_scl_timing for runtime timing updates - Initialize scl_od_min configuration from device tree - Maintain backward compatibility with existing configurations Requirement (MIPI Specification for I3C Basic v1.2 (16-Dec-2024), Section 4.3.11.2 & Table 49): - The MIPI I3C specification mandates different open-drain speeds during bus initialization as defined in "I3C Basic Open Drain Timing Parameters". - The first broadcast address (7'h7E+W) must be transmitted at a slower open-drain speed to ensure visibility to all devices on the I3C bus, including legacy I2C devices. This slow speed (minimum 200ns Thigh) allows I2C devices to properly detect the I3C mode transition and disable their spike filters before switching to I3C mode. After the initial broadcast, normal I3C open-drain speeds can be used for regular operation. Signed-off-by: Shreehari HK <[email protected]>
1 parent 7735be8 commit 4d6673b

File tree

3 files changed

+57
-37
lines changed

3 files changed

+57
-37
lines changed

drivers/i3c/i3c_dw.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,6 @@ struct dw_i3c_config {
365365
clock_control_subsys_t clock_subsys;
366366
uint32_t regs;
367367

368-
/* Initial clk configuration */
369-
/* Maximum OD high clk pulse length */
370-
uint32_t od_thigh_max_ns;
371-
/* Minimum OD low clk pulse length */
372-
uint32_t od_tlow_min_ns;
373-
374368
void (*irq_config_func)();
375369

376370
#if defined(CONFIG_PINCTRL)
@@ -1414,7 +1408,7 @@ static int i3c_dw_irq(const struct device *dev)
14141408
return 0;
14151409
}
14161410

1417-
static int init_scl_timing(const struct device *dev)
1411+
static int dw_i3c_init_scl_timing(const struct device *dev, struct i3c_config_controller *ctrl_cfg)
14181412
{
14191413
const struct dw_i3c_config *config = dev->config;
14201414
uint32_t core_rate, scl_timing;
@@ -1427,12 +1421,21 @@ static int init_scl_timing(const struct device *dev)
14271421
LOG_ERR("%s: get clock rate failed", dev->name);
14281422
return -EINVAL;
14291423
}
1424+
14301425
#ifdef CONFIG_I3C_CONTROLLER
1426+
1427+
__ASSERT((ctrl_cfg != NULL), "Controller configuration should not be NULL");
1428+
1429+
if (ctrl_cfg->scl_od_min.low_ns < I3C_OD_TLOW_MIN_NS) {
1430+
LOG_ERR("%s: Open Drain Low Period is out of range", dev->name);
1431+
return -EINVAL;
1432+
}
1433+
14311434
/* I3C_OD */
1432-
hcnt = DIV_ROUND_UP(config->od_thigh_max_ns * (uint64_t)core_rate, I3C_PERIOD_NS) - 1;
1435+
hcnt = DIV_ROUND_UP(ctrl_cfg->scl_od_min.high_ns * (uint64_t)core_rate, I3C_PERIOD_NS) - 1;
14331436
hcnt = CLAMP(hcnt, SCL_I3C_TIMING_CNT_MIN, SCL_I3C_TIMING_CNT_MAX);
14341437

1435-
lcnt = DIV_ROUND_UP(config->od_tlow_min_ns * (uint64_t)core_rate, I3C_PERIOD_NS);
1438+
lcnt = DIV_ROUND_UP(ctrl_cfg->scl_od_min.low_ns * (uint64_t)core_rate, I3C_PERIOD_NS);
14361439
lcnt = CLAMP(lcnt, SCL_I3C_TIMING_CNT_MIN, SCL_I3C_TIMING_CNT_MAX);
14371440

14381441
scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
@@ -1491,8 +1494,10 @@ static int init_scl_timing(const struct device *dev)
14911494
DIV_ROUND_UP(I3C_BUS_IDLE_TIME_NS * (uint64_t)core_rate, I3C_PERIOD_NS);
14921495
sys_write32(BUS_I3C_IDLE_TIME(scl_timing), config->regs + BUS_IDLE_TIMING);
14931496
#endif /* CONFIG_I3C_TARGET */
1497+
14941498
return 0;
14951499
}
1500+
14961501
#ifdef CONFIG_I3C_CONTROLLER
14971502
/**
14981503
* Determine I3C bus mode from the i2c devices on the bus
@@ -1634,6 +1639,7 @@ static int set_controller_info(const struct device *dev)
16341639
return 0;
16351640
}
16361641
#endif /* CONFIG_I3C_CONTROLLER */
1642+
16371643
static void enable_interrupts(const struct device *dev)
16381644
{
16391645
const struct dw_i3c_config *config = dev->config;
@@ -2072,17 +2078,28 @@ static int dw_i3c_config_get(const struct device *dev, enum i3c_config_type type
20722078
*/
20732079
static int dw_i3c_configure(const struct device *dev, enum i3c_config_type type, void *config)
20742080
{
2081+
#ifdef CONFIG_I3C_CONTROLLER
2082+
struct dw_i3c_data *data = dev->data;
2083+
int ret;
2084+
#endif /* CONFIG_I3C_CONTROLLER */
20752085
#ifdef CONFIG_I3C_TARGET
20762086
const struct dw_i3c_config *dev_config = dev->config;
20772087
#endif /* CONFIG_I3C_TARGET */
20782088

20792089
if (type == I3C_CONFIG_CONTROLLER) {
2080-
/* struct i3c_config_controller *ctrl_cfg = config; */
2081-
/* TODO: somehow determine i3c rate? snps is complicated */
2090+
#ifdef CONFIG_I3C_CONTROLLER
2091+
ret = dw_i3c_init_scl_timing(dev, config);
2092+
if (ret != 0) {
2093+
return ret;
2094+
}
2095+
(void)memcpy(&data->common.ctrl_config,
2096+
config, sizeof(data->common.ctrl_config));
2097+
#else
20822098
return -ENOTSUP;
2099+
#endif /* CONFIG_I3C_CONTROLLER */
20832100
} else if (type == I3C_CONFIG_TARGET) {
20842101
#ifdef CONFIG_I3C_TARGET
2085-
struct i3c_config_target *target_cfg = config;
2102+
struct i3c_config_target *target_cfg = (struct i3c_config_target *)config;
20862103
uint32_t val;
20872104

20882105
/* TODO: some how randomly generate pid */
@@ -2357,13 +2374,6 @@ static int dw_i3c_init(const struct device *dev)
23572374
__ASSERT_NO_MSG((IS_ENABLED(CONFIG_I3C_TARGET) && ctrl_config->is_secondary) ||
23582375
(IS_ENABLED(CONFIG_I3C_CONTROLLER) && !ctrl_config->is_secondary));
23592376

2360-
ret = init_scl_timing(dev);
2361-
if (ret != 0) {
2362-
return ret;
2363-
}
2364-
2365-
enable_interrupts(dev);
2366-
23672377
/* disable ibi */
23682378
sys_write32(IBI_REQ_REJECT_ALL, config->regs + IBI_SIR_REQ_REJECT);
23692379
sys_write32(IBI_REQ_REJECT_ALL, config->regs + IBI_MR_REQ_REJECT);
@@ -2385,6 +2395,15 @@ static int dw_i3c_init(const struct device *dev)
23852395
}
23862396
#endif /* CONFIG_I3C_CONTROLLER */
23872397
dw_i3c_enable_controller(config, true);
2398+
2399+
ret = dw_i3c_init_scl_timing(dev, ctrl_config);
2400+
if (ret != 0) {
2401+
LOG_ERR("%s: Clock setting failed", dev->name);
2402+
return ret;
2403+
}
2404+
2405+
enable_interrupts(dev);
2406+
23882407
#ifdef CONFIG_I3C_CONTROLLER
23892408
if (!(ctrl_config->is_secondary)) {
23902409
/* Perform bus initialization - skip if no I3C devices are known. */
@@ -2497,15 +2516,15 @@ static DEVICE_API(i3c, dw_i3c_api) = {
24972516
.common.ctrl_config.scl.i3c = \
24982517
DT_INST_PROP_OR(n, i3c_scl_hz, I3C_BUS_TYP_I3C_SCL_RATE), \
24992518
.common.ctrl_config.scl.i2c = DT_INST_PROP_OR(n, i2c_scl_hz, 0), \
2519+
.common.ctrl_config.scl_od_min.high_ns = DT_INST_PROP(n, od_thigh_min_ns), \
2520+
.common.ctrl_config.scl_od_min.low_ns = DT_INST_PROP(n, od_tlow_min_ns), \
25002521
}; \
25012522
static const struct dw_i3c_config dw_i3c_cfg_##n = { \
25022523
.regs = DT_INST_REG_ADDR(n), \
25032524
.clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
25042525
.clock_subsys = COND_CODE_1(DT_INST_PHA_HAS_CELL(n, clocks, clkid), \
25052526
((clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, clkid)), \
25062527
((clock_control_subsys_t)0)), \
2507-
.od_thigh_max_ns = DT_INST_PROP(n, od_thigh_max_ns), \
2508-
.od_tlow_min_ns = DT_INST_PROP(n, od_tlow_min_ns), \
25092528
.irq_config_func = &i3c_dw_irq_config_##n, \
25102529
IF_ENABLED(CONFIG_I3C_CONTROLLER, \
25112530
(.common.dev_list.i3c = dw_i3c_device_array_##n, \

dts/bindings/i3c/i3c-controller.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,20 @@ properties:
3838
is the address is used to communicate with itself after a handoff by
3939
a secondary controller. This is only used if the controller is a primary
4040
controller.
41+
42+
od-thigh-min-ns:
43+
type: int
44+
default: 24
45+
description: |
46+
Minimum high clock pulse length (ns) in Open-Drain mode for pure bus.
47+
The default value is from Section 4.3.11.2 & Table 49 of the I3C
48+
Basic Specifiction v1.2 (16-Dec-2024).
49+
50+
od-tlow-min-ns:
51+
type: int
52+
default: 200
53+
description: |
54+
Minimum low clock pulse length (ns) in Open-Drain mode.
55+
The default value is from Section 4.3.11.2 & Table 49 of the I3C
56+
Basic Specifiction v1.2 (16-Dec-2024).
57+
Specifiction.

dts/bindings/i3c/snps,designware-i3c.yaml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,3 @@ include: [i3c-controller.yaml, pinctrl-device.yaml]
1212
properties:
1313
clocks:
1414
required: true
15-
16-
od-thigh-max-ns:
17-
type: int
18-
default: 41
19-
description: |
20-
Maximum high clock pulse length (ns) in Open-Drain mode.
21-
The default is value from Section 6.2 of the I3C
22-
Specifiction.
23-
24-
od-tlow-min-ns:
25-
type: int
26-
default: 200
27-
description: |
28-
Minimum low clock pulse length (ns) in Open-Drain mode.
29-
The default is value from Section 6.2 of the I3C
30-
Specifiction.

0 commit comments

Comments
 (0)