I3C for 6.11

Drivers:
  - dw: optional apb clock and power management support, IBI handling fixes
  - mipi-i3c-hci: IBI handling fixes
  - svc: few fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmajlukACgkQY6TcMGxw
 OjJ4iw//db7P6x0wmy5xkIRktjCY3rqDyBHvcuoVYuFFQqTyRcu7RybRL8HTiem7
 tE+dd9d2GyMaq2aG3P6lzwRozVUUmTnKECP3IGcPTw70n3y2xQEYC/Y/CCmnSNy4
 hY3m/e9Z0NT7g98rOz6iMJrw0O3TZZVv+va88EK2MvtU76xf2e8y0CZeOsA1c5SZ
 mGnICFtluV2i4Yj+zj6I5e8Dbn+rqpIz3MWtdLzJK9/q6BIZjBtoRiDFXlP5twTf
 4PjqCI0oq/oZmhq6zEPE7eRjr0xYZC7XLDpQPnDXrpJqsXvosW5tDPxHsx0rE3RY
 6q5RwJdSKwKrTT5Ttp3KYAnMjaxTScYoXuMx5f5JpdxjeJ0iTNi7cmoobqw5uJIh
 0RPBxPYmQlW/eHcvY/7Se+b4VwGM2upIhJRU+1Y0hRJYcF/qXA/KR2jQz0fIt7O2
 agWjcc3SW429YXfYfy7SSRLT790n913bypUoO6eUE8Hde9oyqSLAKWbrZsTj/nBx
 PhRszqmlVTWvVWxHIhy6fLS+NIDefWexL7zszQg9ME4AdafUBDOoabDSn1IjN1WC
 +/YPwkv9Q9gQC43vg/6Lu7r3IghsWLdRt66cSRok3KGfvQbPIUwbzgNAjHOace0h
 DS37RtB4LXVWMWJ2WbE3N6N9qmlsFAstimdoffUCGbb4cuWxnwU=
 =WFhg
 -----END PGP SIGNATURE-----

Merge tag 'i3c/for-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux

Pull i3c updates from Alexandre Belloni:
 "This cycle, there are new features for the Designware controller and
  fixes for the other IPs:

   - dw: optional apb clock and power management support, IBI handling
     fixes

   - mipi-i3c-hci: IBI handling fixes

   - svc: a few fixes"

* tag 'i3c/for-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  dt-bindings: i3c: add header for generic I3C flags
  i3c: master: svc: Fix error code in svc_i3c_master_do_daa_locked()
  i3c: master: Enhance i3c_bus_type visibility for device searching & event monitoring
  i3c: dw: Add power management support
  i3c: dw: Add some functions for reusability
  i3c: dw: Save timing registers and other values
  i3c: master: svc: Improve DAA STOP handle code logic
  i3c: dw: Add optional apb clock
  i3c: dw: Use new *_enabled clk API
  dt-bindings: i3c: dw: Add apb clock binding
  i3c: master: svc: Convert comma to semicolon
  i3c: mipi-i3c-hci: Round IBI data chunk size to HW supported value
  i3c: mipi-i3c-hci: Error out instead on BUG_ON() in IBI DMA setup
  i3c: mipi-i3c-hci: Set IBI Status and Data Ring base addresses
  i3c: mipi-i3c-hci: Switch to lower_32_bits()/upper_32_bits() helpers
  i3c: dw: Remove ibi_capable property
  i3c: dw: Fix IBI intr programming
  i3c: dw: Fix clearing queue thld
  i3c: mipi-i3c-hci: Fix number of DAT/DCT entries for HCI versions < 1.1
  i3c: master: svc: resend target address when get NACK
This commit is contained in:
Linus Torvalds 2024-07-27 10:53:06 -07:00
commit c85e1497dd
13 changed files with 431 additions and 143 deletions

View File

@ -91,6 +91,7 @@ patternProperties:
- const: 0
- description: |
Shall encode the I3C LVR (Legacy Virtual Register):
See include/dt-bindings/i3c/i3c.h
bit[31:8]: unused/ignored
bit[7:5]: I2C device index. Possible values:
* 0: I2C device has a 50 ns spike filter
@ -153,6 +154,8 @@ additionalProperties: true
examples:
- |
#include <dt-bindings/i3c/i3c.h>
i3c@d040000 {
compatible = "cdns,i3c-master";
clocks = <&coreclock>, <&i3csysclock>;
@ -166,7 +169,7 @@ examples:
/* I2C device. */
eeprom@57 {
compatible = "atmel,24c01";
reg = <0x57 0x0 0x10>;
reg = <0x57 0x0 (I2C_FM | I2C_FILTER)>;
pagesize = <0x8>;
};

View File

@ -20,7 +20,16 @@ properties:
maxItems: 1
clocks:
maxItems: 1
minItems: 1
items:
- description: Core clock
- description: APB clock
clock-names:
minItems: 1
items:
- const: core
- const: apb
interrupts:
maxItems: 1

View File

@ -10655,6 +10655,7 @@ F: Documentation/ABI/testing/sysfs-bus-i3c
F: Documentation/devicetree/bindings/i3c/
F: Documentation/driver-api/i3c
F: drivers/i3c/
F: include/dt-bindings/i3c/
F: include/linux/i3c/
IBM Operation Panel Input Driver

View File

@ -10,8 +10,6 @@
#include <linux/i3c/master.h>
extern const struct bus_type i3c_bus_type;
void i3c_bus_normaluse_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);

View File

@ -342,6 +342,7 @@ const struct bus_type i3c_bus_type = {
.probe = i3c_device_probe,
.remove = i3c_device_remove,
};
EXPORT_SYMBOL_GPL(i3c_bus_type);
static enum i3c_addr_slot_status
i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)

View File

@ -156,7 +156,6 @@ static int ast2600_i3c_probe(struct platform_device *pdev)
i3c->sda_pullup);
i3c->dw.platform_ops = &ast2600_i3c_ops;
i3c->dw.ibi_capable = true;
return dw_i3c_common_probe(&i3c->dw, pdev);
}

View File

@ -17,7 +17,9 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
@ -217,7 +219,7 @@
#define I3C_BUS_THIGH_MAX_NS 41
#define XFER_TIMEOUT (msecs_to_jiffies(1000))
#define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */
struct dw_i3c_cmd {
u32 cmd_lo;
u32 cmd_hi;
@ -300,7 +302,14 @@ 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,
u32 dev_ctrl;
dev_ctrl = readl(master->regs + DEVICE_CTRL);
/* For now don't support Hot-Join */
dev_ctrl |= DEV_CTRL_HOT_JOIN_NACK;
if (master->i2c_slv_prsnt)
dev_ctrl |= DEV_CTRL_I2C_SLAVE_PRESENT;
writel(dev_ctrl | DEV_CTRL_ENABLE,
master->regs + DEVICE_CTRL);
}
@ -521,6 +530,32 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
dw_i3c_master_start_xfer_locked(master);
}
static void dw_i3c_master_set_intr_regs(struct dw_i3c_master *master)
{
u32 thld_ctrl;
thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
thld_ctrl &= ~(QUEUE_THLD_CTRL_RESP_BUF_MASK |
QUEUE_THLD_CTRL_IBI_STAT_MASK |
QUEUE_THLD_CTRL_IBI_DATA_MASK);
thld_ctrl |= QUEUE_THLD_CTRL_IBI_STAT(1) |
QUEUE_THLD_CTRL_IBI_DATA(31);
writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
thld_ctrl = readl(master->regs + DATA_BUFFER_THLD_CTRL);
thld_ctrl &= ~DATA_BUFFER_THLD_CTRL_RX_BUF;
writel(thld_ctrl, master->regs + DATA_BUFFER_THLD_CTRL);
writel(INTR_ALL, master->regs + INTR_STATUS);
writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
master->sir_rej_mask = IBI_REQ_REJECT_ALL;
writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
}
static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
{
unsigned long core_rate, core_period;
@ -543,18 +578,22 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I3C_PP_TIMING);
master->i3c_pp_timing = scl_timing;
/*
* In pure i3c mode, MST_FREE represents tCAS. In shared mode, this
* will be set up by dw_i2c_clk_cfg as tLOW.
*/
if (master->base.bus.mode == I3C_BUS_MODE_PURE)
if (master->base.bus.mode == I3C_BUS_MODE_PURE) {
writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
master->bus_free_timing = BUS_I3C_MST_FREE(lcnt);
}
lcnt = max_t(u8,
DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period), lcnt);
scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
master->i3c_od_timing = scl_timing;
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt;
scl_timing = SCL_EXT_LCNT_1(lcnt);
@ -565,6 +604,7 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR4_SCL_RATE) - hcnt;
scl_timing |= SCL_EXT_LCNT_4(lcnt);
writel(scl_timing, master->regs + SCL_EXT_LCNT_TIMING);
master->ext_lcnt_timing = scl_timing;
return 0;
}
@ -586,16 +626,21 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
SCL_I2C_FMP_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
master->i2c_fmp_timing = scl_timing;
lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
SCL_I2C_FM_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
master->i2c_fm_timing = scl_timing;
writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
master->bus_free_timing = BUS_I3C_MST_FREE(lcnt);
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_I2C_SLAVE_PRESENT,
master->regs + DEVICE_CTRL);
master->i2c_slv_prsnt = true;
return 0;
}
@ -605,69 +650,58 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
struct dw_i3c_master *master = to_dw_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(m);
struct i3c_device_info info = { };
u32 thld_ctrl;
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return ret;
}
ret = master->platform_ops->init(master);
if (ret)
return ret;
goto rpm_out;
switch (bus->mode) {
case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED:
ret = dw_i2c_clk_cfg(master);
if (ret)
return ret;
goto rpm_out;
fallthrough;
case I3C_BUS_MODE_PURE:
ret = dw_i3c_clk_cfg(master);
if (ret)
return ret;
goto rpm_out;
break;
default:
return -EINVAL;
ret = -EINVAL;
goto rpm_out;
}
thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
thld_ctrl &= ~(QUEUE_THLD_CTRL_RESP_BUF_MASK |
QUEUE_THLD_CTRL_IBI_STAT_MASK |
QUEUE_THLD_CTRL_IBI_STAT_MASK);
thld_ctrl |= QUEUE_THLD_CTRL_IBI_STAT(1) |
QUEUE_THLD_CTRL_IBI_DATA(31);
writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
thld_ctrl = readl(master->regs + DATA_BUFFER_THLD_CTRL);
thld_ctrl &= ~DATA_BUFFER_THLD_CTRL_RX_BUF;
writel(thld_ctrl, master->regs + DATA_BUFFER_THLD_CTRL);
writel(INTR_ALL, master->regs + INTR_STATUS);
writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
goto rpm_out;
writel(DEV_ADDR_DYNAMIC_ADDR_VALID | DEV_ADDR_DYNAMIC(ret),
master->regs + DEVICE_ADDR);
master->dev_addr = ret;
memset(&info, 0, sizeof(info));
info.dyn_addr = ret;
ret = i3c_master_set_info(&master->base, &info);
if (ret)
return ret;
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
/* For now don't support Hot-Join */
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
master->regs + DEVICE_CTRL);
goto rpm_out;
dw_i3c_master_set_intr_regs(master);
dw_i3c_master_enable(master);
return 0;
rpm_out:
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
static void dw_i3c_master_bus_cleanup(struct i3c_master_controller *m)
@ -769,11 +803,21 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
if (ccc->id == I3C_CCC_ENTDAA)
return -EINVAL;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return ret;
}
if (ccc->rnw)
ret = dw_i3c_ccc_get(master, ccc);
else
ret = dw_i3c_ccc_set(master, ccc);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@ -786,6 +830,14 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
u8 p, last_addr = 0;
int ret, pos;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return ret;
}
olddevs = ~(master->free_pos);
/* Prepare DAT before launching DAA. */
@ -794,8 +846,10 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
continue;
ret = i3c_master_get_free_addr(m, last_addr + 1);
if (ret < 0)
return -ENOSPC;
if (ret < 0) {
ret = -ENOSPC;
goto rpm_out;
}
master->devs[pos].addr = ret;
p = even_parity(ret);
@ -805,16 +859,21 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(ret),
master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
ret = 0;
}
xfer = dw_i3c_master_alloc_xfer(master, 1);
if (!xfer)
return -ENOMEM;
if (!xfer) {
ret = -ENOMEM;
goto rpm_out;
}
pos = dw_i3c_master_get_free_pos(master);
if (pos < 0) {
dw_i3c_master_free_xfer(xfer);
return pos;
ret = pos;
goto rpm_out;
}
cmd = &xfer->cmds[0];
cmd->cmd_hi = 0x1;
@ -839,7 +898,10 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
dw_i3c_master_free_xfer(xfer);
return 0;
rpm_out:
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
@ -874,6 +936,14 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (!xfer)
return -ENOMEM;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return ret;
}
for (i = 0; i < i3c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
@ -915,6 +985,8 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
ret = xfer->ret;
dw_i3c_master_free_xfer(xfer);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@ -1025,6 +1097,14 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
if (!xfer)
return -ENOMEM;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return ret;
}
for (i = 0; i < i2c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
@ -1055,6 +1135,8 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
ret = xfer->ret;
dw_i3c_master_free_xfer(xfer);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
@ -1075,6 +1157,7 @@ static int dw_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
data->index = pos;
master->devs[pos].addr = dev->addr;
master->devs[pos].is_i2c_addr = true;
master->free_pos &= ~BIT(pos);
i2c_dev_set_master_data(dev, data);
@ -1175,17 +1258,16 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
master->platform_ops->set_dat_ibi(master, dev, enable, &reg);
writel(reg, master->regs + dat_entry);
reg = readl(master->regs + IBI_SIR_REQ_REJECT);
if (enable) {
global = reg == 0xffffffff;
reg &= ~BIT(idx);
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
master->sir_rej_mask &= ~BIT(idx);
} else {
bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
reg |= BIT(idx);
global = (reg == 0xffffffff) && hj_rejected;
master->sir_rej_mask |= BIT(idx);
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected;
}
writel(reg, master->regs + IBI_SIR_REQ_REJECT);
writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
if (global)
dw_i3c_master_enable_sir_signal(master, enable);
@ -1197,6 +1279,15 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
{
struct dw_i3c_master *master = to_dw_i3c_master(m);
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return ret;
}
dw_i3c_master_enable_sir_signal(master, true);
writel(readl(master->regs + DEVICE_CTRL) & ~DEV_CTRL_HOT_JOIN_NACK,
@ -1212,6 +1303,8 @@ static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
master->regs + DEVICE_CTRL);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return 0;
}
@ -1222,12 +1315,23 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
struct dw_i3c_master *master = to_dw_i3c_master(m);
int rc;
rc = pm_runtime_resume_and_get(master->dev);
if (rc < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, rc);
return rc;
}
dw_i3c_master_set_sir_enabled(master, dev, data->index, true);
rc = i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
if (rc)
if (rc) {
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
}
return rc;
}
@ -1245,6 +1349,8 @@ static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return 0;
}
@ -1403,21 +1509,6 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
.i2c_xfers = dw_i3c_master_i2c_xfers,
};
static const struct i3c_master_controller_ops dw_mipi_i3c_ibi_ops = {
.bus_init = dw_i3c_master_bus_init,
.bus_cleanup = dw_i3c_master_bus_cleanup,
.attach_i3c_dev = dw_i3c_master_attach_i3c_dev,
.reattach_i3c_dev = dw_i3c_master_reattach_i3c_dev,
.detach_i3c_dev = dw_i3c_master_detach_i3c_dev,
.do_daa = dw_i3c_master_daa,
.supports_ccc_cmd = dw_i3c_master_supports_ccc_cmd,
.send_ccc_cmd = dw_i3c_master_send_ccc_cmd,
.priv_xfers = dw_i3c_master_priv_xfers,
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
.i2c_xfers = dw_i3c_master_i2c_xfers,
.request_ibi = dw_i3c_master_request_ibi,
.free_ibi = dw_i3c_master_free_ibi,
.enable_ibi = dw_i3c_master_enable_ibi,
@ -1455,29 +1546,30 @@ static void dw_i3c_hj_work(struct work_struct *work)
int dw_i3c_common_probe(struct dw_i3c_master *master,
struct platform_device *pdev)
{
const struct i3c_master_controller_ops *ops;
int ret, irq;
if (!master->platform_ops)
master->platform_ops = &dw_i3c_platform_ops_default;
master->dev = &pdev->dev;
master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs))
return PTR_ERR(master->regs);
master->core_clk = devm_clk_get(&pdev->dev, NULL);
master->core_clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(master->core_clk))
return PTR_ERR(master->core_clk);
master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
if (IS_ERR(master->pclk))
return PTR_ERR(master->pclk);
master->core_rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
"core_rst");
if (IS_ERR(master->core_rst))
return PTR_ERR(master->core_rst);
ret = clk_prepare_enable(master->core_clk);
if (ret)
goto err_disable_core_clk;
reset_control_deassert(master->core_rst);
spin_lock_init(&master->xferqueue.lock);
@ -1493,6 +1585,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
platform_set_drvdata(pdev, master);
pm_runtime_set_autosuspend_delay(&pdev->dev, RPM_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
/* Information regarding the FIFOs/QUEUEs depth */
ret = readl(master->regs + QUEUE_STATUS_LEVEL);
master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret);
@ -1505,23 +1602,22 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
master->maxdevs = ret >> 16;
master->free_pos = GENMASK(master->maxdevs - 1, 0);
ops = &dw_mipi_i3c_ops;
if (master->ibi_capable)
ops = &dw_mipi_i3c_ibi_ops;
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
ret = i3c_master_register(&master->base, &pdev->dev, ops, false);
ret = i3c_master_register(&master->base, &pdev->dev,
&dw_mipi_i3c_ops, false);
if (ret)
goto err_assert_rst;
goto err_disable_pm;
return 0;
err_disable_pm:
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
err_assert_rst:
reset_control_assert(master->core_rst);
err_disable_core_clk:
clk_disable_unprepare(master->core_clk);
return ret;
}
EXPORT_SYMBOL_GPL(dw_i3c_common_probe);
@ -1530,9 +1626,9 @@ void dw_i3c_common_remove(struct dw_i3c_master *master)
{
i3c_master_unregister(&master->base);
reset_control_assert(master->core_rst);
clk_disable_unprepare(master->core_clk);
pm_runtime_disable(master->dev);
pm_runtime_set_suspended(master->dev);
pm_runtime_dont_use_autosuspend(master->dev);
}
EXPORT_SYMBOL_GPL(dw_i3c_common_remove);
@ -1556,6 +1652,96 @@ static void dw_i3c_remove(struct platform_device *pdev)
dw_i3c_common_remove(master);
}
static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
{
u32 pos, reg_val;
writel(DEV_ADDR_DYNAMIC_ADDR_VALID | DEV_ADDR_DYNAMIC(master->dev_addr),
master->regs + DEVICE_ADDR);
for (pos = 0; pos < master->maxdevs; pos++) {
if (master->free_pos & BIT(pos))
continue;
if (master->devs[pos].is_i2c_addr)
reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
else
reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
}
}
static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master)
{
writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING);
writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING);
writel(master->ext_lcnt_timing, master->regs + SCL_EXT_LCNT_TIMING);
if (master->i2c_slv_prsnt) {
writel(master->i2c_fmp_timing, master->regs + SCL_I2C_FMP_TIMING);
writel(master->i2c_fm_timing, master->regs + SCL_I2C_FM_TIMING);
}
}
static int dw_i3c_master_enable_clks(struct dw_i3c_master *master)
{
int ret = 0;
ret = clk_prepare_enable(master->core_clk);
if (ret)
return ret;
ret = clk_prepare_enable(master->pclk);
if (ret) {
clk_disable_unprepare(master->core_clk);
return ret;
}
return 0;
}
static inline void dw_i3c_master_disable_clks(struct dw_i3c_master *master)
{
clk_disable_unprepare(master->pclk);
clk_disable_unprepare(master->core_clk);
}
static int __maybe_unused dw_i3c_master_runtime_suspend(struct device *dev)
{
struct dw_i3c_master *master = dev_get_drvdata(dev);
dw_i3c_master_disable(master);
reset_control_assert(master->core_rst);
dw_i3c_master_disable_clks(master);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int __maybe_unused dw_i3c_master_runtime_resume(struct device *dev)
{
struct dw_i3c_master *master = dev_get_drvdata(dev);
pinctrl_pm_select_default_state(dev);
dw_i3c_master_enable_clks(master);
reset_control_deassert(master->core_rst);
dw_i3c_master_set_intr_regs(master);
dw_i3c_master_restore_timing_regs(master);
dw_i3c_master_restore_addrs(master);
dw_i3c_master_enable(master);
return 0;
}
static const struct dev_pm_ops dw_i3c_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(dw_i3c_master_runtime_suspend, dw_i3c_master_runtime_resume, NULL)
};
static const struct of_device_id dw_i3c_master_of_match[] = {
{ .compatible = "snps,dw-i3c-master-1.00a", },
{},
@ -1568,6 +1754,7 @@ static struct platform_driver dw_i3c_driver = {
.driver = {
.name = "dw-i3c-master",
.of_match_table = dw_i3c_master_of_match,
.pm = &dw_i3c_pm_ops,
},
};
module_platform_driver(dw_i3c_driver);

View File

@ -19,11 +19,13 @@ struct dw_i3c_master_caps {
struct dw_i3c_dat_entry {
u8 addr;
bool is_i2c_addr;
struct i3c_dev_desc *ibi_dev;
};
struct dw_i3c_master {
struct i3c_master_controller base;
struct device *dev;
u16 maxdevs;
u16 datstartaddr;
u32 free_pos;
@ -36,10 +38,18 @@ struct dw_i3c_master {
void __iomem *regs;
struct reset_control *core_rst;
struct clk *core_clk;
struct clk *pclk;
char version[5];
char type[5];
bool ibi_capable;
u32 sir_rej_mask;
bool i2c_slv_prsnt;
u32 dev_addr;
u32 i3c_pp_timing;
u32 i3c_od_timing;
u32 ext_lcnt_timing;
u32 bus_free_timing;
u32 i2c_fm_timing;
u32 i2c_fmp_timing;
/*
* Per-device hardware data, used to manage the device address table
* (DAT)

View File

@ -631,6 +631,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
static int i3c_hci_init(struct i3c_hci *hci)
{
u32 regval, offset;
bool size_in_dwords;
int ret;
/* Validate HCI hardware version */
@ -654,11 +655,16 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->caps = reg_read(HC_CAPABILITIES);
DBG("caps = %#x", hci->caps);
size_in_dwords = hci->version_major < 1 ||
(hci->version_major == 1 && hci->version_minor < 1);
regval = reg_read(DAT_SECTION);
offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
if (size_in_dwords)
hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size;
dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
hci->DAT_entries, hci->DAT_entry_size, offset);
@ -667,6 +673,8 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
if (size_in_dwords)
hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size;
dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
hci->DCT_entries, hci->DCT_entry_size, offset);

View File

@ -147,21 +147,6 @@ struct hci_dma_dev_ibi_data {
unsigned int max_len;
};
static inline u32 lo32(dma_addr_t physaddr)
{
return physaddr;
}
static inline u32 hi32(dma_addr_t physaddr)
{
/* trickery to avoid compiler warnings on 32-bit build targets */
if (sizeof(dma_addr_t) > 4) {
u64 hi = physaddr;
return hi >> 32;
}
return 0;
}
static void hci_dma_cleanup(struct i3c_hci *hci)
{
struct hci_rings_data *rings = hci->io_data;
@ -265,10 +250,10 @@ static int hci_dma_init(struct i3c_hci *hci)
if (!rh->xfer || !rh->resp || !rh->src_xfers)
goto err_out;
rh_reg_write(CMD_RING_BASE_LO, lo32(rh->xfer_dma));
rh_reg_write(CMD_RING_BASE_HI, hi32(rh->xfer_dma));
rh_reg_write(RESP_RING_BASE_LO, lo32(rh->resp_dma));
rh_reg_write(RESP_RING_BASE_HI, hi32(rh->resp_dma));
rh_reg_write(CMD_RING_BASE_LO, lower_32_bits(rh->xfer_dma));
rh_reg_write(CMD_RING_BASE_HI, upper_32_bits(rh->xfer_dma));
rh_reg_write(RESP_RING_BASE_LO, lower_32_bits(rh->resp_dma));
rh_reg_write(RESP_RING_BASE_HI, upper_32_bits(rh->resp_dma));
regval = FIELD_PREP(CR_RING_SIZE, rh->xfer_entries);
rh_reg_write(CR_SETUP, regval);
@ -294,7 +279,17 @@ static int hci_dma_init(struct i3c_hci *hci)
rh->ibi_chunk_sz = dma_get_cache_alignment();
rh->ibi_chunk_sz *= IBI_CHUNK_CACHELINES;
BUG_ON(rh->ibi_chunk_sz > 256);
/*
* Round IBI data chunk size to number of bytes supported by
* the HW. Chunk size can be 2^n number of DWORDs which is the
* same as 2^(n+2) bytes, where n is 0..6.
*/
rh->ibi_chunk_sz = umax(4, rh->ibi_chunk_sz);
rh->ibi_chunk_sz = roundup_pow_of_two(rh->ibi_chunk_sz);
if (rh->ibi_chunk_sz > 256) {
ret = -EINVAL;
goto err_out;
}
ibi_status_ring_sz = rh->ibi_status_sz * rh->ibi_status_entries;
ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total;
@ -315,6 +310,11 @@ static int hci_dma_init(struct i3c_hci *hci)
goto err_out;
}
rh_reg_write(IBI_STATUS_RING_BASE_LO, lower_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_STATUS_RING_BASE_HI, upper_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_DATA_RING_BASE_LO, lower_32_bits(rh->ibi_data_dma));
rh_reg_write(IBI_DATA_RING_BASE_HI, upper_32_bits(rh->ibi_data_dma));
regval = FIELD_PREP(IBI_STATUS_RING_SIZE,
rh->ibi_status_entries) |
FIELD_PREP(IBI_DATA_CHUNK_SIZE,
@ -404,8 +404,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
hci_dma_unmap_xfer(hci, xfer_list, i);
return -ENOMEM;
}
*ring_data++ = lo32(xfer->data_dma);
*ring_data++ = hi32(xfer->data_dma);
*ring_data++ = lower_32_bits(xfer->data_dma);
*ring_data++ = upper_32_bits(xfer->data_dma);
} else {
*ring_data++ = 0;
*ring_data++ = 0;

View File

@ -790,7 +790,20 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
int ret, i;
while (true) {
/* Enter/proceed with DAA */
/* SVC_I3C_MCTRL_REQUEST_PROC_DAA have two mode, ENTER DAA or PROCESS DAA.
*
* ENTER DAA:
* 1 will issue START, 7E, ENTDAA, and then emits 7E/R to process first target.
* 2 Stops just before the new Dynamic Address (DA) is to be emitted.
*
* PROCESS DAA:
* 1 The DA is written using MWDATAB or ADDR bits 6:0.
* 2 ProcessDAA is requested again to write the new address, and then starts the
* next (START, 7E, ENTDAA) unless marked to STOP; an MSTATUS indicating NACK
* means DA was not accepted (e.g. parity error). If PROCESSDAA is NACKed on the
* 7E/R, which means no more Slaves need a DA, then a COMPLETE will be signaled
* (along with DONE), and a STOP issued automatically.
*/
writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_NACK |
@ -807,7 +820,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_MCTRLDONE(reg),
1, 1000);
if (ret)
return ret;
break;
if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6];
@ -819,7 +832,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
*/
ret = svc_i3c_master_readb(master, data, 6);
if (ret)
return ret;
break;
for (i = 0; i < 6; i++)
prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - i));
@ -827,7 +840,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
/* We do not care about the BCR and DCR yet */
ret = svc_i3c_master_readb(master, data, 2);
if (ret)
return ret;
break;
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
SVC_I3C_MSTATUS_COMPLETE(reg)) {
@ -835,12 +848,23 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* All devices received and acked they dynamic
* address, this is the natural end of the DAA
* procedure.
*
* Hardware will auto emit STOP at this case.
*/
break;
*count = dev_nb;
return 0;
} else if (SVC_I3C_MSTATUS_NACKED(reg)) {
/* No I3C devices attached */
if (dev_nb == 0)
if (dev_nb == 0) {
/*
* Hardware can't treat first NACK for ENTAA as normal
* COMPLETE. So need manual emit STOP.
*/
ret = 0;
*count = 0;
break;
}
/*
* A slave device nacked the address, this is
@ -849,8 +873,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* answer again immediately and shall ack the
* address this time.
*/
if (prov_id[dev_nb] == nacking_prov_id)
return -EIO;
if (prov_id[dev_nb] == nacking_prov_id) {
ret = -EIO;
break;
}
dev_nb--;
nacking_prov_id = prov_id[dev_nb];
@ -858,7 +884,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
continue;
} else {
return -EIO;
break;
}
}
@ -870,12 +896,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_BETWEEN(reg),
0, 1000);
if (ret)
return ret;
break;
/* Give the slave device a suitable dynamic address */
ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
if (ret < 0)
return ret;
break;
addrs[dev_nb] = ret;
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n",
@ -885,9 +911,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
last_addr = addrs[dev_nb++];
}
*count = dev_nb;
return 0;
/* Need manual issue STOP except for Complete condition */
svc_i3c_master_emit_stop(master);
return ret;
}
static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
@ -961,11 +987,10 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
spin_lock_irqsave(&master->xferqueue.lock, flags);
ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
if (ret) {
svc_i3c_master_emit_stop(master);
svc_i3c_master_clear_merrwarn(master);
if (ret)
goto rpm_out;
}
/* Register all devices who participated to the core */
for (i = 0; i < dev_nb; i++) {
@ -1052,12 +1077,15 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
u8 *in, const u8 *out, unsigned int xfer_len,
unsigned int *actual_len, bool continued)
{
int retry = 2;
u32 reg;
int ret;
/* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
while (retry--) {
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
xfer_type |
SVC_I3C_MCTRL_IBIRESP_NACK |
@ -1072,10 +1100,37 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
goto emit_stop;
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
/*
* According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3.
* If the Controller chooses to start an I3C Message with an I3C Dynamic
* Address, then special provisions shall be made because that same I3C
* Target may be initiating an IBI or a Controller Role Request. So, one of
* three things may happen: (skip 1, 2)
*
* 3. The Addresses match and the RnW bits also match, and so neither
* Controller nor Target will ACK since both are expecting the other side to
* provide ACK. As a result, each side might think it had "won" arbitration,
* but neither side would continue, as each would subsequently see that the
* other did not provide ACK.
* ...
* For either value of RnW: Due to the NACK, the Controller shall defer the
* Private Write or Private Read, and should typically transmit the Target
* Address again after a Repeated START (i.e., the next one or any one prior
* to a STOP in the Frame). Since the Address Header following a Repeated
* START is not arbitrated, the Controller will always win (see Section
* 5.1.2.2.4).
*/
if (retry && addr != 0x7e) {
writel(SVC_I3C_MERRWARN_NACK, master->regs + SVC_I3C_MERRWARN);
} else {
ret = -ENXIO;
*actual_len = 0;
goto emit_stop;
}
} else {
break;
}
}
/*
* According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
@ -1321,7 +1376,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
cmd->addr = ccc->dests[0].addr;
cmd->rnw = ccc->rnw;
cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data;
cmd->len = xfer_len;
cmd->actual_len = actual_len;
cmd->continued = false;

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
/*
* Copyright 2024 NXP
*/
#ifndef _DT_BINDINGS_I3C_I3C_H
#define _DT_BINDINGS_I3C_I3C_H
#define I2C_FM (1 << 4)
#define I2C_FM_PLUS (0 << 4)
#define I2C_FILTER (0 << 5)
#define I2C_NO_FILTER_HIGH_FREQUENCY (1 << 5)
#define I2C_NO_FILTER_LOW_FREQUENCY (2 << 5)
#endif

View File

@ -33,6 +33,7 @@ enum {
struct i3c_master_controller;
struct i3c_bus;
struct i3c_device;
extern const struct bus_type i3c_bus_type;
/**
* struct i3c_i2c_dev_desc - Common part of the I3C/I2C device descriptor