Commit c85e1497 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 1fcaa5db 24168c5e
...@@ -91,6 +91,7 @@ patternProperties: ...@@ -91,6 +91,7 @@ patternProperties:
- const: 0 - const: 0
- description: | - description: |
Shall encode the I3C LVR (Legacy Virtual Register): Shall encode the I3C LVR (Legacy Virtual Register):
See include/dt-bindings/i3c/i3c.h
bit[31:8]: unused/ignored bit[31:8]: unused/ignored
bit[7:5]: I2C device index. Possible values: bit[7:5]: I2C device index. Possible values:
* 0: I2C device has a 50 ns spike filter * 0: I2C device has a 50 ns spike filter
...@@ -153,6 +154,8 @@ additionalProperties: true ...@@ -153,6 +154,8 @@ additionalProperties: true
examples: examples:
- | - |
#include <dt-bindings/i3c/i3c.h>
i3c@d040000 { i3c@d040000 {
compatible = "cdns,i3c-master"; compatible = "cdns,i3c-master";
clocks = <&coreclock>, <&i3csysclock>; clocks = <&coreclock>, <&i3csysclock>;
...@@ -166,7 +169,7 @@ examples: ...@@ -166,7 +169,7 @@ examples:
/* I2C device. */ /* I2C device. */
eeprom@57 { eeprom@57 {
compatible = "atmel,24c01"; compatible = "atmel,24c01";
reg = <0x57 0x0 0x10>; reg = <0x57 0x0 (I2C_FM | I2C_FILTER)>;
pagesize = <0x8>; pagesize = <0x8>;
}; };
......
...@@ -20,7 +20,16 @@ properties: ...@@ -20,7 +20,16 @@ properties:
maxItems: 1 maxItems: 1
clocks: clocks:
maxItems: 1 minItems: 1
items:
- description: Core clock
- description: APB clock
clock-names:
minItems: 1
items:
- const: core
- const: apb
interrupts: interrupts:
maxItems: 1 maxItems: 1
......
...@@ -10655,6 +10655,7 @@ F: Documentation/ABI/testing/sysfs-bus-i3c ...@@ -10655,6 +10655,7 @@ F: Documentation/ABI/testing/sysfs-bus-i3c
F: Documentation/devicetree/bindings/i3c/ F: Documentation/devicetree/bindings/i3c/
F: Documentation/driver-api/i3c F: Documentation/driver-api/i3c
F: drivers/i3c/ F: drivers/i3c/
F: include/dt-bindings/i3c/
F: include/linux/i3c/ F: include/linux/i3c/
IBM Operation Panel Input Driver IBM Operation Panel Input Driver
......
...@@ -10,8 +10,6 @@ ...@@ -10,8 +10,6 @@
#include <linux/i3c/master.h> #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_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus); void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
......
...@@ -342,6 +342,7 @@ const struct bus_type i3c_bus_type = { ...@@ -342,6 +342,7 @@ const struct bus_type i3c_bus_type = {
.probe = i3c_device_probe, .probe = i3c_device_probe,
.remove = i3c_device_remove, .remove = i3c_device_remove,
}; };
EXPORT_SYMBOL_GPL(i3c_bus_type);
static enum i3c_addr_slot_status static enum i3c_addr_slot_status
i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr) i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
......
...@@ -156,7 +156,6 @@ static int ast2600_i3c_probe(struct platform_device *pdev) ...@@ -156,7 +156,6 @@ static int ast2600_i3c_probe(struct platform_device *pdev)
i3c->sda_pullup); i3c->sda_pullup);
i3c->dw.platform_ops = &ast2600_i3c_ops; i3c->dw.platform_ops = &ast2600_i3c_ops;
i3c->dw.ibi_capable = true;
return dw_i3c_common_probe(&i3c->dw, pdev); return dw_i3c_common_probe(&i3c->dw, pdev);
} }
......
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -217,7 +219,7 @@ ...@@ -217,7 +219,7 @@
#define I3C_BUS_THIGH_MAX_NS 41 #define I3C_BUS_THIGH_MAX_NS 41
#define XFER_TIMEOUT (msecs_to_jiffies(1000)) #define XFER_TIMEOUT (msecs_to_jiffies(1000))
#define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */
struct dw_i3c_cmd { struct dw_i3c_cmd {
u32 cmd_lo; u32 cmd_lo;
u32 cmd_hi; u32 cmd_hi;
...@@ -300,7 +302,14 @@ static void dw_i3c_master_disable(struct dw_i3c_master *master) ...@@ -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) 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); master->regs + DEVICE_CTRL);
} }
...@@ -521,6 +530,32 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr) ...@@ -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); 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) static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
{ {
unsigned long core_rate, core_period; unsigned long core_rate, core_period;
...@@ -543,18 +578,22 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master) ...@@ -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); scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I3C_PP_TIMING); 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 * In pure i3c mode, MST_FREE represents tCAS. In shared mode, this
* will be set up by dw_i2c_clk_cfg as tLOW. * 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); writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
master->bus_free_timing = BUS_I3C_MST_FREE(lcnt);
}
lcnt = max_t(u8, lcnt = max_t(u8,
DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period), lcnt); DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period), lcnt);
scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt); scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); 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; lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt;
scl_timing = SCL_EXT_LCNT_1(lcnt); scl_timing = SCL_EXT_LCNT_1(lcnt);
...@@ -565,6 +604,7 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master) ...@@ -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; lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR4_SCL_RATE) - hcnt;
scl_timing |= SCL_EXT_LCNT_4(lcnt); scl_timing |= SCL_EXT_LCNT_4(lcnt);
writel(scl_timing, master->regs + SCL_EXT_LCNT_TIMING); writel(scl_timing, master->regs + SCL_EXT_LCNT_TIMING);
master->ext_lcnt_timing = scl_timing;
return 0; return 0;
} }
...@@ -586,16 +626,21 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master) ...@@ -586,16 +626,21 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) | scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
SCL_I2C_FMP_TIMING_LCNT(lcnt); SCL_I2C_FMP_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING); 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); 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; hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) | scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
SCL_I2C_FM_TIMING_LCNT(lcnt); SCL_I2C_FM_TIMING_LCNT(lcnt);
writel(scl_timing, master->regs + SCL_I2C_FM_TIMING); 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); 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, writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_I2C_SLAVE_PRESENT,
master->regs + DEVICE_CTRL); master->regs + DEVICE_CTRL);
master->i2c_slv_prsnt = true;
return 0; return 0;
} }
...@@ -605,69 +650,58 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) ...@@ -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 dw_i3c_master *master = to_dw_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(m); struct i3c_bus *bus = i3c_master_get_bus(m);
struct i3c_device_info info = { }; struct i3c_device_info info = { };
u32 thld_ctrl;
int ret; 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); ret = master->platform_ops->init(master);
if (ret) if (ret)
return ret; goto rpm_out;
switch (bus->mode) { switch (bus->mode) {
case I3C_BUS_MODE_MIXED_FAST: case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED: case I3C_BUS_MODE_MIXED_LIMITED:
ret = dw_i2c_clk_cfg(master); ret = dw_i2c_clk_cfg(master);
if (ret) if (ret)
return ret; goto rpm_out;
fallthrough; fallthrough;
case I3C_BUS_MODE_PURE: case I3C_BUS_MODE_PURE:
ret = dw_i3c_clk_cfg(master); ret = dw_i3c_clk_cfg(master);
if (ret) if (ret)
return ret; goto rpm_out;
break; break;
default: 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); ret = i3c_master_get_free_addr(m, 0);
if (ret < 0) if (ret < 0)
return ret; goto rpm_out;
writel(DEV_ADDR_DYNAMIC_ADDR_VALID | DEV_ADDR_DYNAMIC(ret), writel(DEV_ADDR_DYNAMIC_ADDR_VALID | DEV_ADDR_DYNAMIC(ret),
master->regs + DEVICE_ADDR); master->regs + DEVICE_ADDR);
master->dev_addr = ret;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
info.dyn_addr = ret; info.dyn_addr = ret;
ret = i3c_master_set_info(&master->base, &info); ret = i3c_master_set_info(&master->base, &info);
if (ret) if (ret)
return ret; goto rpm_out;
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);
dw_i3c_master_set_intr_regs(master);
dw_i3c_master_enable(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) 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, ...@@ -769,11 +803,21 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
if (ccc->id == I3C_CCC_ENTDAA) if (ccc->id == I3C_CCC_ENTDAA)
return -EINVAL; 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) if (ccc->rnw)
ret = dw_i3c_ccc_get(master, ccc); ret = dw_i3c_ccc_get(master, ccc);
else else
ret = dw_i3c_ccc_set(master, ccc); ret = dw_i3c_ccc_set(master, ccc);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret; return ret;
} }
...@@ -786,6 +830,14 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) ...@@ -786,6 +830,14 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
u8 p, last_addr = 0; u8 p, last_addr = 0;
int ret, pos; 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); olddevs = ~(master->free_pos);
/* Prepare DAT before launching DAA. */ /* Prepare DAT before launching DAA. */
...@@ -794,8 +846,10 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) ...@@ -794,8 +846,10 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
continue; continue;
ret = i3c_master_get_free_addr(m, last_addr + 1); ret = i3c_master_get_free_addr(m, last_addr + 1);
if (ret < 0) if (ret < 0) {
return -ENOSPC; ret = -ENOSPC;
goto rpm_out;
}
master->devs[pos].addr = ret; master->devs[pos].addr = ret;
p = even_parity(ret); p = even_parity(ret);
...@@ -805,16 +859,21 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) ...@@ -805,16 +859,21 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(ret), writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(ret),
master->regs + master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, pos)); DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
ret = 0;
} }
xfer = dw_i3c_master_alloc_xfer(master, 1); xfer = dw_i3c_master_alloc_xfer(master, 1);
if (!xfer) if (!xfer) {
return -ENOMEM; ret = -ENOMEM;
goto rpm_out;
}
pos = dw_i3c_master_get_free_pos(master); pos = dw_i3c_master_get_free_pos(master);
if (pos < 0) { if (pos < 0) {
dw_i3c_master_free_xfer(xfer); dw_i3c_master_free_xfer(xfer);
return pos; ret = pos;
goto rpm_out;
} }
cmd = &xfer->cmds[0]; cmd = &xfer->cmds[0];
cmd->cmd_hi = 0x1; cmd->cmd_hi = 0x1;
...@@ -839,7 +898,10 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) ...@@ -839,7 +898,10 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
dw_i3c_master_free_xfer(xfer); 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, 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, ...@@ -874,6 +936,14 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
if (!xfer) if (!xfer)
return -ENOMEM; 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++) { for (i = 0; i < i3c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[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, ...@@ -915,6 +985,8 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
ret = xfer->ret; ret = xfer->ret;
dw_i3c_master_free_xfer(xfer); dw_i3c_master_free_xfer(xfer);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret; return ret;
} }
...@@ -1025,6 +1097,14 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, ...@@ -1025,6 +1097,14 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
if (!xfer) if (!xfer)
return -ENOMEM; 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++) { for (i = 0; i < i2c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[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, ...@@ -1055,6 +1135,8 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
ret = xfer->ret; ret = xfer->ret;
dw_i3c_master_free_xfer(xfer); dw_i3c_master_free_xfer(xfer);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return ret; return ret;
} }
...@@ -1075,6 +1157,7 @@ static int dw_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev) ...@@ -1075,6 +1157,7 @@ static int dw_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
data->index = pos; data->index = pos;
master->devs[pos].addr = dev->addr; master->devs[pos].addr = dev->addr;
master->devs[pos].is_i2c_addr = true;
master->free_pos &= ~BIT(pos); master->free_pos &= ~BIT(pos);
i2c_dev_set_master_data(dev, data); i2c_dev_set_master_data(dev, data);
...@@ -1175,17 +1258,16 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master, ...@@ -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); master->platform_ops->set_dat_ibi(master, dev, enable, &reg);
writel(reg, master->regs + dat_entry); writel(reg, master->regs + dat_entry);
reg = readl(master->regs + IBI_SIR_REQ_REJECT);
if (enable) { if (enable) {
global = reg == 0xffffffff; global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
reg &= ~BIT(idx); master->sir_rej_mask &= ~BIT(idx);
} else { } else {
bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK); bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
reg |= BIT(idx); master->sir_rej_mask |= BIT(idx);
global = (reg == 0xffffffff) && hj_rejected; 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) if (global)
dw_i3c_master_enable_sir_signal(master, enable); 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, ...@@ -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) static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
{ {
struct dw_i3c_master *master = to_dw_i3c_master(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); dw_i3c_master_enable_sir_signal(master, true);
writel(readl(master->regs + DEVICE_CTRL) & ~DEV_CTRL_HOT_JOIN_NACK, 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) ...@@ -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, writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
master->regs + DEVICE_CTRL); master->regs + DEVICE_CTRL);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
return 0; return 0;
} }
...@@ -1222,12 +1315,23 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev) ...@@ -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); struct dw_i3c_master *master = to_dw_i3c_master(m);
int rc; 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); 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); 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); 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; return rc;
} }
...@@ -1245,6 +1349,8 @@ static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev) ...@@ -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); 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; return 0;
} }
...@@ -1403,21 +1509,6 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { ...@@ -1403,21 +1509,6 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev, .attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev, .detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
.i2c_xfers = dw_i3c_master_i2c_xfers, .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, .request_ibi = dw_i3c_master_request_ibi,
.free_ibi = dw_i3c_master_free_ibi, .free_ibi = dw_i3c_master_free_ibi,
.enable_ibi = dw_i3c_master_enable_ibi, .enable_ibi = dw_i3c_master_enable_ibi,
...@@ -1455,29 +1546,30 @@ static void dw_i3c_hj_work(struct work_struct *work) ...@@ -1455,29 +1546,30 @@ static void dw_i3c_hj_work(struct work_struct *work)
int dw_i3c_common_probe(struct dw_i3c_master *master, int dw_i3c_common_probe(struct dw_i3c_master *master,
struct platform_device *pdev) struct platform_device *pdev)
{ {
const struct i3c_master_controller_ops *ops;
int ret, irq; int ret, irq;
if (!master->platform_ops) if (!master->platform_ops)
master->platform_ops = &dw_i3c_platform_ops_default; master->platform_ops = &dw_i3c_platform_ops_default;
master->dev = &pdev->dev;
master->regs = devm_platform_ioremap_resource(pdev, 0); master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs)) if (IS_ERR(master->regs))
return PTR_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)) if (IS_ERR(master->core_clk))
return PTR_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, master->core_rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
"core_rst"); "core_rst");
if (IS_ERR(master->core_rst)) if (IS_ERR(master->core_rst))
return PTR_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); reset_control_deassert(master->core_rst);
spin_lock_init(&master->xferqueue.lock); spin_lock_init(&master->xferqueue.lock);
...@@ -1493,6 +1585,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, ...@@ -1493,6 +1585,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
platform_set_drvdata(pdev, 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 */ /* Information regarding the FIFOs/QUEUEs depth */
ret = readl(master->regs + QUEUE_STATUS_LEVEL); ret = readl(master->regs + QUEUE_STATUS_LEVEL);
master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret); master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret);
...@@ -1505,23 +1602,22 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, ...@@ -1505,23 +1602,22 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
master->maxdevs = ret >> 16; master->maxdevs = ret >> 16;
master->free_pos = GENMASK(master->maxdevs - 1, 0); 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); 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) if (ret)
goto err_assert_rst; goto err_disable_pm;
return 0; 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: err_assert_rst:
reset_control_assert(master->core_rst); reset_control_assert(master->core_rst);
err_disable_core_clk:
clk_disable_unprepare(master->core_clk);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(dw_i3c_common_probe); EXPORT_SYMBOL_GPL(dw_i3c_common_probe);
...@@ -1530,9 +1626,9 @@ void dw_i3c_common_remove(struct dw_i3c_master *master) ...@@ -1530,9 +1626,9 @@ void dw_i3c_common_remove(struct dw_i3c_master *master)
{ {
i3c_master_unregister(&master->base); i3c_master_unregister(&master->base);
reset_control_assert(master->core_rst); pm_runtime_disable(master->dev);
pm_runtime_set_suspended(master->dev);
clk_disable_unprepare(master->core_clk); pm_runtime_dont_use_autosuspend(master->dev);
} }
EXPORT_SYMBOL_GPL(dw_i3c_common_remove); EXPORT_SYMBOL_GPL(dw_i3c_common_remove);
...@@ -1556,6 +1652,96 @@ static void dw_i3c_remove(struct platform_device *pdev) ...@@ -1556,6 +1652,96 @@ static void dw_i3c_remove(struct platform_device *pdev)
dw_i3c_common_remove(master); 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[] = { static const struct of_device_id dw_i3c_master_of_match[] = {
{ .compatible = "snps,dw-i3c-master-1.00a", }, { .compatible = "snps,dw-i3c-master-1.00a", },
{}, {},
...@@ -1568,6 +1754,7 @@ static struct platform_driver dw_i3c_driver = { ...@@ -1568,6 +1754,7 @@ static struct platform_driver dw_i3c_driver = {
.driver = { .driver = {
.name = "dw-i3c-master", .name = "dw-i3c-master",
.of_match_table = dw_i3c_master_of_match, .of_match_table = dw_i3c_master_of_match,
.pm = &dw_i3c_pm_ops,
}, },
}; };
module_platform_driver(dw_i3c_driver); module_platform_driver(dw_i3c_driver);
......
...@@ -19,11 +19,13 @@ struct dw_i3c_master_caps { ...@@ -19,11 +19,13 @@ struct dw_i3c_master_caps {
struct dw_i3c_dat_entry { struct dw_i3c_dat_entry {
u8 addr; u8 addr;
bool is_i2c_addr;
struct i3c_dev_desc *ibi_dev; struct i3c_dev_desc *ibi_dev;
}; };
struct dw_i3c_master { struct dw_i3c_master {
struct i3c_master_controller base; struct i3c_master_controller base;
struct device *dev;
u16 maxdevs; u16 maxdevs;
u16 datstartaddr; u16 datstartaddr;
u32 free_pos; u32 free_pos;
...@@ -36,10 +38,18 @@ struct dw_i3c_master { ...@@ -36,10 +38,18 @@ struct dw_i3c_master {
void __iomem *regs; void __iomem *regs;
struct reset_control *core_rst; struct reset_control *core_rst;
struct clk *core_clk; struct clk *core_clk;
struct clk *pclk;
char version[5]; char version[5];
char type[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 * Per-device hardware data, used to manage the device address table
* (DAT) * (DAT)
......
...@@ -631,6 +631,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) ...@@ -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) static int i3c_hci_init(struct i3c_hci *hci)
{ {
u32 regval, offset; u32 regval, offset;
bool size_in_dwords;
int ret; int ret;
/* Validate HCI hardware version */ /* Validate HCI hardware version */
...@@ -654,11 +655,16 @@ static int i3c_hci_init(struct i3c_hci *hci) ...@@ -654,11 +655,16 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->caps = reg_read(HC_CAPABILITIES); hci->caps = reg_read(HC_CAPABILITIES);
DBG("caps = %#x", hci->caps); 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); regval = reg_read(DAT_SECTION);
offset = FIELD_GET(DAT_TABLE_OFFSET, regval); offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
hci->DAT_regs = offset ? hci->base_regs + offset : NULL; hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval); hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8; 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", dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
hci->DAT_entries, hci->DAT_entry_size, offset); hci->DAT_entries, hci->DAT_entry_size, offset);
...@@ -667,6 +673,8 @@ static int i3c_hci_init(struct i3c_hci *hci) ...@@ -667,6 +673,8 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->DCT_regs = offset ? hci->base_regs + offset : NULL; hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval); hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16; 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", dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
hci->DCT_entries, hci->DCT_entry_size, offset); hci->DCT_entries, hci->DCT_entry_size, offset);
......
...@@ -147,21 +147,6 @@ struct hci_dma_dev_ibi_data { ...@@ -147,21 +147,6 @@ struct hci_dma_dev_ibi_data {
unsigned int max_len; 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) static void hci_dma_cleanup(struct i3c_hci *hci)
{ {
struct hci_rings_data *rings = hci->io_data; struct hci_rings_data *rings = hci->io_data;
...@@ -265,10 +250,10 @@ static int hci_dma_init(struct i3c_hci *hci) ...@@ -265,10 +250,10 @@ static int hci_dma_init(struct i3c_hci *hci)
if (!rh->xfer || !rh->resp || !rh->src_xfers) if (!rh->xfer || !rh->resp || !rh->src_xfers)
goto err_out; goto err_out;
rh_reg_write(CMD_RING_BASE_LO, lo32(rh->xfer_dma)); rh_reg_write(CMD_RING_BASE_LO, lower_32_bits(rh->xfer_dma));
rh_reg_write(CMD_RING_BASE_HI, hi32(rh->xfer_dma)); rh_reg_write(CMD_RING_BASE_HI, upper_32_bits(rh->xfer_dma));
rh_reg_write(RESP_RING_BASE_LO, lo32(rh->resp_dma)); rh_reg_write(RESP_RING_BASE_LO, lower_32_bits(rh->resp_dma));
rh_reg_write(RESP_RING_BASE_HI, hi32(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); regval = FIELD_PREP(CR_RING_SIZE, rh->xfer_entries);
rh_reg_write(CR_SETUP, regval); rh_reg_write(CR_SETUP, regval);
...@@ -294,7 +279,17 @@ static int hci_dma_init(struct i3c_hci *hci) ...@@ -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 = dma_get_cache_alignment();
rh->ibi_chunk_sz *= IBI_CHUNK_CACHELINES; 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_status_ring_sz = rh->ibi_status_sz * rh->ibi_status_entries;
ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total; 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) ...@@ -315,6 +310,11 @@ static int hci_dma_init(struct i3c_hci *hci)
goto err_out; 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, regval = FIELD_PREP(IBI_STATUS_RING_SIZE,
rh->ibi_status_entries) | rh->ibi_status_entries) |
FIELD_PREP(IBI_DATA_CHUNK_SIZE, FIELD_PREP(IBI_DATA_CHUNK_SIZE,
...@@ -404,8 +404,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, ...@@ -404,8 +404,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
hci_dma_unmap_xfer(hci, xfer_list, i); hci_dma_unmap_xfer(hci, xfer_list, i);
return -ENOMEM; return -ENOMEM;
} }
*ring_data++ = lo32(xfer->data_dma); *ring_data++ = lower_32_bits(xfer->data_dma);
*ring_data++ = hi32(xfer->data_dma); *ring_data++ = upper_32_bits(xfer->data_dma);
} else { } else {
*ring_data++ = 0; *ring_data++ = 0;
*ring_data++ = 0; *ring_data++ = 0;
......
...@@ -790,7 +790,20 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -790,7 +790,20 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
int ret, i; int ret, i;
while (true) { 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 | writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
SVC_I3C_MCTRL_TYPE_I3C | SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_NACK | SVC_I3C_MCTRL_IBIRESP_NACK |
...@@ -807,7 +820,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -807,7 +820,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_MCTRLDONE(reg), SVC_I3C_MSTATUS_MCTRLDONE(reg),
1, 1000); 1, 1000);
if (ret) if (ret)
return ret; break;
if (SVC_I3C_MSTATUS_RXPEND(reg)) { if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6]; u8 data[6];
...@@ -819,7 +832,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -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); ret = svc_i3c_master_readb(master, data, 6);
if (ret) if (ret)
return ret; break;
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - 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, ...@@ -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 */ /* We do not care about the BCR and DCR yet */
ret = svc_i3c_master_readb(master, data, 2); ret = svc_i3c_master_readb(master, data, 2);
if (ret) if (ret)
return ret; break;
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) { } else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) && if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
SVC_I3C_MSTATUS_COMPLETE(reg)) { SVC_I3C_MSTATUS_COMPLETE(reg)) {
...@@ -835,12 +848,23 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -835,12 +848,23 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* All devices received and acked they dynamic * All devices received and acked they dynamic
* address, this is the natural end of the DAA * address, this is the natural end of the DAA
* procedure. * procedure.
*
* Hardware will auto emit STOP at this case.
*/ */
break; *count = dev_nb;
return 0;
} else if (SVC_I3C_MSTATUS_NACKED(reg)) { } else if (SVC_I3C_MSTATUS_NACKED(reg)) {
/* No I3C devices attached */ /* 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; break;
}
/* /*
* A slave device nacked the address, this is * 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, ...@@ -849,8 +873,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* answer again immediately and shall ack the * answer again immediately and shall ack the
* address this time. * address this time.
*/ */
if (prov_id[dev_nb] == nacking_prov_id) if (prov_id[dev_nb] == nacking_prov_id) {
return -EIO; ret = -EIO;
break;
}
dev_nb--; dev_nb--;
nacking_prov_id = prov_id[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, ...@@ -858,7 +884,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
continue; continue;
} else { } else {
return -EIO; break;
} }
} }
...@@ -870,12 +896,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -870,12 +896,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_BETWEEN(reg), SVC_I3C_MSTATUS_BETWEEN(reg),
0, 1000); 0, 1000);
if (ret) if (ret)
return ret; break;
/* Give the slave device a suitable dynamic address */ /* Give the slave device a suitable dynamic address */
ret = i3c_master_get_free_addr(&master->base, last_addr + 1); ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
if (ret < 0) if (ret < 0)
return ret; break;
addrs[dev_nb] = ret; addrs[dev_nb] = ret;
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n", 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, ...@@ -885,9 +911,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
last_addr = addrs[dev_nb++]; last_addr = addrs[dev_nb++];
} }
*count = dev_nb; /* Need manual issue STOP except for Complete condition */
svc_i3c_master_emit_stop(master);
return 0; return ret;
} }
static int svc_i3c_update_ibirules(struct svc_i3c_master *master) 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) ...@@ -961,11 +987,10 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
spin_lock_irqsave(&master->xferqueue.lock, flags); spin_lock_irqsave(&master->xferqueue.lock, flags);
ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb); ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
spin_unlock_irqrestore(&master->xferqueue.lock, flags); spin_unlock_irqrestore(&master->xferqueue.lock, flags);
if (ret) {
svc_i3c_master_emit_stop(master);
svc_i3c_master_clear_merrwarn(master); svc_i3c_master_clear_merrwarn(master);
if (ret)
goto rpm_out; goto rpm_out;
}
/* Register all devices who participated to the core */ /* Register all devices who participated to the core */
for (i = 0; i < dev_nb; i++) { for (i = 0; i < dev_nb; i++) {
...@@ -1052,12 +1077,15 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, ...@@ -1052,12 +1077,15 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
u8 *in, const u8 *out, unsigned int xfer_len, u8 *in, const u8 *out, unsigned int xfer_len,
unsigned int *actual_len, bool continued) unsigned int *actual_len, bool continued)
{ {
int retry = 2;
u32 reg; u32 reg;
int ret; int ret;
/* clean SVC_I3C_MINT_IBIWON w1c bits */ /* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS); writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
while (retry--) {
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR | writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
xfer_type | xfer_type |
SVC_I3C_MCTRL_IBIRESP_NACK | SVC_I3C_MCTRL_IBIRESP_NACK |
...@@ -1072,10 +1100,37 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, ...@@ -1072,10 +1100,37 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
goto emit_stop; goto emit_stop;
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) { 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; ret = -ENXIO;
*actual_len = 0; *actual_len = 0;
goto emit_stop; goto emit_stop;
} }
} else {
break;
}
}
/* /*
* According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame * 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, ...@@ -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->addr = ccc->dests[0].addr;
cmd->rnw = ccc->rnw; cmd->rnw = ccc->rnw;
cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL; 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->len = xfer_len;
cmd->actual_len = actual_len; cmd->actual_len = actual_len;
cmd->continued = false; cmd->continued = false;
......
/* 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
...@@ -33,6 +33,7 @@ enum { ...@@ -33,6 +33,7 @@ enum {
struct i3c_master_controller; struct i3c_master_controller;
struct i3c_bus; struct i3c_bus;
struct i3c_device; struct i3c_device;
extern const struct bus_type i3c_bus_type;
/** /**
* struct i3c_i2c_dev_desc - Common part of the I3C/I2C device descriptor * struct i3c_i2c_dev_desc - Common part of the I3C/I2C device descriptor
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment