summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-i3c11
-rw-r--r--drivers/i3c/device.c46
-rw-r--r--drivers/i3c/internals.h4
-rw-r--r--drivers/i3c/master.c195
-rw-r--r--drivers/i3c/master/Kconfig1
-rw-r--r--drivers/i3c/master/dw-i3c-master.c121
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/cmd_v1.c2
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/cmd_v2.c2
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/core.c435
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dat.h2
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dat_v1.c46
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dma.c169
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/ext_caps.c58
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/ext_caps.h1
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/hci.h17
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c184
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/pio.c64
-rw-r--r--drivers/i3c/master/renesas-i3c.c259
-rw-r--r--drivers/i3c/master/svc-i3c-master.c4
-rw-r--r--include/linux/i3c/device.h22
-rw-r--r--include/linux/i3c/master.h11
-rw-r--r--include/linux/platform_data/mipi-i3c-hci.h15
22 files changed, 1212 insertions, 457 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c
index c812ab180ff4..c1e048957a01 100644
--- a/Documentation/ABI/testing/sysfs-bus-i3c
+++ b/Documentation/ABI/testing/sysfs-bus-i3c
@@ -161,3 +161,14 @@ Contact: linux-i3c@vger.kernel.org
Description:
These directories are just symbolic links to
/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>.
+
+What: /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/dev_nack_retry_count
+KernelVersion: 6.18
+Contact: linux-i3c@vger.kernel.org
+Description:
+ Expose the dev_nak_retry_count which controls the number of
+ automatic retries that will be performed by the controller when
+ the target device returns a NACK response. A value of 0 disables
+ the automatic retries. Exist only when I3C constroller supports
+ this retry on nack feature.
+
diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
index 8a156f5ad692..101eaa77de68 100644
--- a/drivers/i3c/device.c
+++ b/drivers/i3c/device.c
@@ -46,10 +46,16 @@ int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
return -EINVAL;
}
+ ret = i3c_bus_rpm_get(dev->bus);
+ if (ret)
+ return ret;
+
i3c_bus_normaluse_lock(dev->bus);
ret = i3c_dev_do_xfers_locked(dev->desc, xfers, nxfers, mode);
i3c_bus_normaluse_unlock(dev->bus);
+ i3c_bus_rpm_put(dev->bus);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_do_xfers);
@@ -66,10 +72,16 @@ int i3c_device_do_setdasa(struct i3c_device *dev)
{
int ret;
+ ret = i3c_bus_rpm_get(dev->bus);
+ if (ret)
+ return ret;
+
i3c_bus_normaluse_lock(dev->bus);
ret = i3c_dev_setdasa_locked(dev->desc);
i3c_bus_normaluse_unlock(dev->bus);
+ i3c_bus_rpm_put(dev->bus);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_do_setdasa);
@@ -106,16 +118,27 @@ EXPORT_SYMBOL_GPL(i3c_device_get_info);
*/
int i3c_device_disable_ibi(struct i3c_device *dev)
{
- int ret = -ENOENT;
+ int ret;
+
+ if (i3c_bus_rpm_ibi_allowed(dev->bus)) {
+ ret = i3c_bus_rpm_get(dev->bus);
+ if (ret)
+ return ret;
+ }
i3c_bus_normaluse_lock(dev->bus);
if (dev->desc) {
mutex_lock(&dev->desc->ibi_lock);
ret = i3c_dev_disable_ibi_locked(dev->desc);
mutex_unlock(&dev->desc->ibi_lock);
+ } else {
+ ret = -ENOENT;
}
i3c_bus_normaluse_unlock(dev->bus);
+ if (!ret || i3c_bus_rpm_ibi_allowed(dev->bus))
+ i3c_bus_rpm_put(dev->bus);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
@@ -135,16 +158,25 @@ EXPORT_SYMBOL_GPL(i3c_device_disable_ibi);
*/
int i3c_device_enable_ibi(struct i3c_device *dev)
{
- int ret = -ENOENT;
+ int ret;
+
+ ret = i3c_bus_rpm_get(dev->bus);
+ if (ret)
+ return ret;
i3c_bus_normaluse_lock(dev->bus);
if (dev->desc) {
mutex_lock(&dev->desc->ibi_lock);
ret = i3c_dev_enable_ibi_locked(dev->desc);
mutex_unlock(&dev->desc->ibi_lock);
+ } else {
+ ret = -ENOENT;
}
i3c_bus_normaluse_unlock(dev->bus);
+ if (ret || i3c_bus_rpm_ibi_allowed(dev->bus))
+ i3c_bus_rpm_put(dev->bus);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
@@ -163,19 +195,27 @@ EXPORT_SYMBOL_GPL(i3c_device_enable_ibi);
int i3c_device_request_ibi(struct i3c_device *dev,
const struct i3c_ibi_setup *req)
{
- int ret = -ENOENT;
+ int ret;
if (!req->handler || !req->num_slots)
return -EINVAL;
+ ret = i3c_bus_rpm_get(dev->bus);
+ if (ret)
+ return ret;
+
i3c_bus_normaluse_lock(dev->bus);
if (dev->desc) {
mutex_lock(&dev->desc->ibi_lock);
ret = i3c_dev_request_ibi_locked(dev->desc, req);
mutex_unlock(&dev->desc->ibi_lock);
+ } else {
+ ret = -ENOENT;
}
i3c_bus_normaluse_unlock(dev->bus);
+ i3c_bus_rpm_put(dev->bus);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_device_request_ibi);
diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
index f609e5098137..0f1f3f766623 100644
--- a/drivers/i3c/internals.h
+++ b/drivers/i3c/internals.h
@@ -11,6 +11,10 @@
#include <linux/i3c/master.h>
#include <linux/io.h>
+int __must_check i3c_bus_rpm_get(struct i3c_bus *bus);
+void i3c_bus_rpm_put(struct i3c_bus *bus);
+bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus);
+
void i3c_bus_normaluse_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 7f606c871648..0eae19b3823d 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -106,6 +106,38 @@ static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
return container_of(dev, struct i3c_master_controller, dev);
}
+static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master)
+{
+ int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->dev.parent) : 0;
+
+ if (ret < 0) {
+ dev_err(master->dev.parent, "runtime resume failed, error %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void i3c_master_rpm_put(struct i3c_master_controller *master)
+{
+ if (master->rpm_allowed)
+ pm_runtime_put_autosuspend(master->dev.parent);
+}
+
+int i3c_bus_rpm_get(struct i3c_bus *bus)
+{
+ return i3c_master_rpm_get(i3c_bus_to_i3c_master(bus));
+}
+
+void i3c_bus_rpm_put(struct i3c_bus *bus)
+{
+ i3c_master_rpm_put(i3c_bus_to_i3c_master(bus));
+}
+
+bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus)
+{
+ return i3c_bus_to_i3c_master(bus)->rpm_ibi_allowed;
+}
+
static const struct device_type i3c_device_type;
static struct i3c_bus *dev_to_i3cbus(struct device *dev)
@@ -611,6 +643,12 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin)
return -EINVAL;
+ if (enable || master->rpm_ibi_allowed) {
+ ret = i3c_master_rpm_get(master);
+ if (ret)
+ return ret;
+ }
+
i3c_bus_normaluse_lock(&master->bus);
if (enable)
@@ -618,10 +656,14 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
else
ret = master->ops->disable_hotjoin(master);
- master->hotjoin = enable;
+ if (!ret)
+ master->hotjoin = enable;
i3c_bus_normaluse_unlock(&master->bus);
+ if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed)
+ i3c_master_rpm_put(master);
+
return ret;
}
@@ -683,6 +725,39 @@ static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, cha
static DEVICE_ATTR_RW(hotjoin);
+static ssize_t dev_nack_retry_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count);
+}
+
+static ssize_t dev_nack_retry_count_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
+ struct i3c_master_controller *master = dev_to_i3cmaster(dev);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ i3c_bus_maintenance_lock(i3cbus);
+ ret = master->ops->set_dev_nack_retry(master, val);
+ i3c_bus_maintenance_unlock(i3cbus);
+
+ if (ret)
+ return ret;
+
+ master->dev_nack_retry_count = val;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(dev_nack_retry_count);
+
static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_current_master.attr,
@@ -1693,36 +1768,68 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
}
/**
- * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
- * @master: master doing the DAA
- *
- * This function is instantiating an I3C device object and adding it to the
- * I3C device list. All device information are automatically retrieved using
- * standard CCC commands.
+ * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
+ * @master: controller
+ * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA)
*
- * The I3C device object is returned in case the master wants to attach
- * private data to it using i3c_dev_set_master_data().
+ * Perform Dynamic Address Assignment with optional support for System
+ * Hibernation (@rstdaa is true).
*
- * This function must be called with the bus lock held in write mode.
+ * After System Hibernation, Dynamic Addresses can have been reassigned at boot
+ * time to different values. A simple strategy is followed to handle that.
+ * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA
+ * procedure which has provision for reassigning addresses that differ from the
+ * previously recorded addresses.
*
* Return: a 0 in case of success, an negative error code otherwise.
*/
-int i3c_master_do_daa(struct i3c_master_controller *master)
+int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
{
+ int rstret = 0;
int ret;
+ ret = i3c_master_rpm_get(master);
+ if (ret)
+ return ret;
+
i3c_bus_maintenance_lock(&master->bus);
+
+ if (rstdaa) {
+ rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (rstret == I3C_ERROR_M2)
+ rstret = 0;
+ }
+
ret = master->ops->do_daa(master);
+
i3c_bus_maintenance_unlock(&master->bus);
if (ret)
- return ret;
+ goto out;
i3c_bus_normaluse_lock(&master->bus);
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
+out:
+ i3c_master_rpm_put(master);
- return 0;
+ return rstret ?: ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext);
+
+/**
+ * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
+ * @master: master doing the DAA
+ *
+ * This function instantiates I3C device objects and adds them to the
+ * I3C device list. All device information is automatically retrieved using
+ * standard CCC commands.
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_do_daa(struct i3c_master_controller *master)
+{
+ return i3c_master_do_daa_ext(master, false);
}
EXPORT_SYMBOL_GPL(i3c_master_do_daa);
@@ -2064,8 +2171,17 @@ err_detach_devs:
static void i3c_master_bus_cleanup(struct i3c_master_controller *master)
{
- if (master->ops->bus_cleanup)
- master->ops->bus_cleanup(master);
+ if (master->ops->bus_cleanup) {
+ int ret = i3c_master_rpm_get(master);
+
+ if (ret) {
+ dev_err(&master->dev,
+ "runtime resume error: master bus_cleanup() not done\n");
+ } else {
+ master->ops->bus_cleanup(master);
+ i3c_master_rpm_put(master);
+ }
+ }
i3c_master_detach_free_devs(master);
}
@@ -2370,19 +2486,16 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
{
struct device *dev = &master->dev;
struct device_node *i3cbus_np = dev->of_node;
- struct device_node *node;
int ret;
u32 val;
if (!i3cbus_np)
return 0;
- for_each_available_child_of_node(i3cbus_np, node) {
+ for_each_available_child_of_node_scoped(i3cbus_np, node) {
ret = of_i3c_master_add_dev(master, node);
- if (ret) {
- of_node_put(node);
+ if (ret)
return ret;
- }
}
/*
@@ -2420,6 +2533,10 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
return -EOPNOTSUPP;
}
+ ret = i3c_master_rpm_get(master);
+ if (ret)
+ return ret;
+
i3c_bus_normaluse_lock(&master->bus);
dev = i3c_master_find_i2c_dev_by_addr(master, addr);
if (!dev)
@@ -2428,6 +2545,8 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
ret = master->ops->i2c_xfers(dev, xfers, nxfers);
i3c_bus_normaluse_unlock(&master->bus);
+ i3c_master_rpm_put(master);
+
return ret ? ret : nxfers;
}
@@ -2530,6 +2649,10 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action
master = i2c_adapter_to_i3c_master(adap);
+ ret = i3c_master_rpm_get(master);
+ if (ret)
+ return ret;
+
i3c_bus_maintenance_lock(&master->bus);
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
@@ -2543,6 +2666,8 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action
}
i3c_bus_maintenance_unlock(&master->bus);
+ i3c_master_rpm_put(master);
+
return ret;
}
@@ -2880,8 +3005,11 @@ int i3c_master_register(struct i3c_master_controller *master,
INIT_LIST_HEAD(&master->boardinfo.i2c);
INIT_LIST_HEAD(&master->boardinfo.i3c);
+ ret = i3c_master_rpm_get(master);
+ if (ret)
+ return ret;
+
device_initialize(&master->dev);
- dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
master->dev.dma_mask = parent->dma_mask;
master->dev.coherent_dma_mask = parent->coherent_dma_mask;
@@ -2891,6 +3019,8 @@ int i3c_master_register(struct i3c_master_controller *master,
if (ret)
goto err_put_dev;
+ dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
+
ret = of_populate_i3c_bus(master);
if (ret)
goto err_put_dev;
@@ -2959,6 +3089,11 @@ int i3c_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
+ if (master->ops->set_dev_nack_retry)
+ device_create_file(&master->dev, &dev_attr_dev_nack_retry_count);
+
+ i3c_master_rpm_put(master);
+
return 0;
err_del_dev:
@@ -2968,6 +3103,7 @@ err_cleanup_bus:
i3c_master_bus_cleanup(master);
err_put_dev:
+ i3c_master_rpm_put(master);
put_device(&master->dev);
return ret;
@@ -2984,6 +3120,9 @@ void i3c_master_unregister(struct i3c_master_controller *master)
{
i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+ if (master->ops->set_dev_nack_retry)
+ device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count);
+
i3c_master_i2c_adapter_cleanup(master);
i3c_master_unregister_i3c_devs(master);
i3c_master_bus_cleanup(master);
@@ -3112,8 +3251,18 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
if (!dev->ibi)
return;
- if (WARN_ON(dev->ibi->enabled))
- WARN_ON(i3c_dev_disable_ibi_locked(dev));
+ if (dev->ibi->enabled) {
+ int ret;
+
+ dev_err(&master->dev, "Freeing IBI that is still enabled\n");
+ ret = i3c_master_rpm_get(master);
+ if (!ret) {
+ ret = i3c_dev_disable_ibi_locked(dev);
+ i3c_master_rpm_put(master);
+ }
+ if (ret)
+ dev_err(&master->dev, "Failed to disable IBI before freeing\n");
+ }
master->ops->free_ibi(dev);
diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
index 82cf330778d5..2609f2b18e0a 100644
--- a/drivers/i3c/master/Kconfig
+++ b/drivers/i3c/master/Kconfig
@@ -69,6 +69,7 @@ config MIPI_I3C_HCI_PCI
tristate "MIPI I3C Host Controller Interface PCI support"
depends on MIPI_I3C_HCI
depends on PCI
+ select MFD_CORE
help
Support for MIPI I3C Host Controller Interface compatible hardware
on the PCI bus.
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 889e2ed5bc83..7eb09ad10171 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -5,6 +5,7 @@
* Author: Vitor Soares <vitor.soares@synopsys.com>
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
@@ -204,12 +205,23 @@
#define EXTENDED_CAPABILITY 0xe8
#define SLAVE_CONFIG 0xec
+#define DYN_ADDR_LO_MASK GENMASK(4, 0)
+#define DYN_ADDR_HI_MASK GENMASK(6, 5)
+#define IBI_SIR_BIT_MOD 32 /* 32-bit vector */
+
+#define DW_I3C_DEV_NACK_RETRY_CNT_MAX 0x3
+#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK GENMASK(30, 29)
+#define DEV_ADDR_TABLE_DYNAMIC_MASK GENMASK(23, 16)
+#define DEV_ADDR_TABLE_STATIC_MASK GENMASK(6, 0)
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
+#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \
+ FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x))
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
-#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
-#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
+#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
+#define DEV_ADDR_TABLE_STATIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_STATIC_MASK, x)
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
+#define DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(x) FIELD_GET(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
#define I3C_BUS_SDR1_SCL_RATE 8000000
#define I3C_BUS_SDR2_SCL_RATE 6000000
@@ -257,6 +269,14 @@ struct dw_i3c_drvdata {
u32 flags;
};
+static inline u32 get_ibi_sir_bit_index(u8 addr)
+{
+ u32 lo = FIELD_GET(DYN_ADDR_LO_MASK, addr);
+ u32 hi = FIELD_GET(DYN_ADDR_HI_MASK, addr);
+
+ return (lo + hi) % IBI_SIR_BIT_MOD;
+}
+
static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
const struct i3c_ccc_cmd *cmd)
{
@@ -409,17 +429,14 @@ static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master,
struct dw_i3c_xfer *xfer)
{
- unsigned long flags;
-
init_completion(&xfer->comp);
- spin_lock_irqsave(&master->xferqueue.lock, flags);
+ guard(spinlock_irqsave)(&master->xferqueue.lock);
if (master->xferqueue.cur) {
list_add_tail(&xfer->node, &master->xferqueue.list);
} else {
master->xferqueue.cur = xfer;
dw_i3c_master_start_xfer_locked(master);
}
- spin_unlock_irqrestore(&master->xferqueue.lock, flags);
}
static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
@@ -444,11 +461,8 @@ static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
struct dw_i3c_xfer *xfer)
{
- unsigned long flags;
-
- spin_lock_irqsave(&master->xferqueue.lock, flags);
+ guard(spinlock_irqsave)(&master->xferqueue.lock);
dw_i3c_master_dequeue_xfer_locked(master, xfer);
- spin_unlock_irqrestore(&master->xferqueue.lock, flags);
}
static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
@@ -1099,6 +1113,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
+ dw_i3c_master_free_xfer(xfer);
return ret;
}
@@ -1187,15 +1202,13 @@ static int dw_i3c_master_request_ibi(struct i3c_dev_desc *dev,
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct dw_i3c_master *master = to_dw_i3c_master(m);
- unsigned long flags;
data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
if (IS_ERR(data->ibi_pool))
return PTR_ERR(data->ibi_pool);
- spin_lock_irqsave(&master->devs_lock, flags);
+ guard(spinlock_irqsave)(&master->devs_lock);
master->devs[data->index].ibi_dev = dev;
- spin_unlock_irqrestore(&master->devs_lock, flags);
return 0;
}
@@ -1205,11 +1218,10 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct dw_i3c_master *master = to_dw_i3c_master(m);
- unsigned long flags;
- spin_lock_irqsave(&master->devs_lock, flags);
- master->devs[data->index].ibi_dev = NULL;
- spin_unlock_irqrestore(&master->devs_lock, flags);
+ scoped_guard(spinlock_irqsave, &master->devs_lock) {
+ master->devs[data->index].ibi_dev = NULL;
+ }
i3c_generic_ibi_free_pool(data->ibi_pool);
data->ibi_pool = NULL;
@@ -1236,14 +1248,21 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
struct i3c_dev_desc *dev,
u8 idx, bool enable)
{
- unsigned long flags;
u32 dat_entry, reg;
bool global;
+ u8 dynamic_addr;
dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx);
- spin_lock_irqsave(&master->devs_lock, flags);
+ guard(spinlock_irqsave)(&master->devs_lock);
reg = readl(master->regs + dat_entry);
+ dynamic_addr = DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(reg);
+
+ if (!dynamic_addr)
+ dev_warn(master->dev,
+ "<%s> unassigned slave device, dynamic addr:%x\n",
+ __func__, dynamic_addr);
+
if (enable) {
reg &= ~DEV_ADDR_TABLE_SIR_REJECT;
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
@@ -1256,20 +1275,17 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
if (enable) {
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL);
- master->sir_rej_mask &= ~BIT(idx);
+ master->sir_rej_mask &= ~BIT(get_ibi_sir_bit_index(dynamic_addr));
} else {
bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
- master->sir_rej_mask |= BIT(idx);
+ master->sir_rej_mask |= BIT(get_ibi_sir_bit_index(dynamic_addr));
global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected;
}
writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT);
if (global)
dw_i3c_master_enable_sir_signal(master, enable);
-
-
- spin_unlock_irqrestore(&master->devs_lock, flags);
}
static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
@@ -1370,7 +1386,6 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
struct dw_i3c_i2c_dev_data *data;
struct i3c_ibi_slot *slot;
struct i3c_dev_desc *dev;
- unsigned long flags;
u8 addr, len;
int idx;
@@ -1388,7 +1403,7 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
* a new platform op to validate it.
*/
- spin_lock_irqsave(&master->devs_lock, flags);
+ guard(spinlock_irqsave)(&master->devs_lock);
idx = dw_i3c_master_get_addr_pos(master, addr);
if (idx < 0) {
dev_dbg_ratelimited(&master->base.dev,
@@ -1424,14 +1439,10 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
}
i3c_master_queue_ibi(dev, slot);
- spin_unlock_irqrestore(&master->devs_lock, flags);
-
return;
err_drain:
dw_i3c_master_drain_ibi_queue(master, len);
-
- spin_unlock_irqrestore(&master->devs_lock, flags);
}
/* "ibis": referring to In-Band Interrupts, and not
@@ -1489,6 +1500,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m,
+ unsigned long dev_nack_retry_cnt)
+{
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ u32 reg;
+ int i;
+
+ if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) {
+ dev_err(&master->base.dev,
+ "Value %ld exceeds maximum %d\n",
+ dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX);
+ return -ERANGE;
+ }
+
+ /*
+ * Update DAT entries for all currently attached devices.
+ * We directly iterate through the master's device array.
+ */
+ for (i = 0; i < master->maxdevs; i++) {
+ /* Skip free/empty slots */
+ if (master->free_pos & BIT(i))
+ continue;
+
+ reg = readl(master->regs +
+ DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
+ reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK;
+ reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt);
+ writel(reg, master->regs +
+ DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
+ }
+
+ return 0;
+}
+
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.bus_init = dw_i3c_master_bus_init,
.bus_cleanup = dw_i3c_master_bus_cleanup,
@@ -1509,6 +1554,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
+ .set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry,
};
/* default platform ops implementations */
@@ -1570,6 +1616,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
spin_lock_init(&master->xferqueue.lock);
INIT_LIST_HEAD(&master->xferqueue.list);
+ spin_lock_init(&master->devs_lock);
+
writel(INTR_ALL, master->regs + INTR_STATUS);
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq,
@@ -1676,11 +1724,16 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
if (master->free_pos & BIT(pos))
continue;
- if (master->devs[pos].is_i2c_addr)
- reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
+ reg_val = readl(master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
+
+ if (master->devs[pos].is_i2c_addr) {
+ reg_val &= ~DEV_ADDR_TABLE_STATIC_MASK;
+ 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);
+ } else {
+ reg_val &= ~DEV_ADDR_TABLE_DYNAMIC_MASK;
+ reg_val |= DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
+ }
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
}
diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
index eb8a3ae2990d..fe260461e7e6 100644
--- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
@@ -15,7 +15,6 @@
#include "dat.h"
#include "dct.h"
-
/*
* Address Assignment Command
*/
@@ -100,7 +99,6 @@
#define CMD_M0_VENDOR_INFO_PRESENT W0_BIT_( 7)
#define CMD_M0_TID(v) FIELD_PREP(W0_MASK( 6, 3), v)
-
/* Data Transfer Speed and Mode */
enum hci_cmd_mode {
MODE_I3C_SDR0 = 0x0,
diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
index efb4326a25b7..3729e6419581 100644
--- a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
@@ -16,7 +16,6 @@
#include "cmd.h"
#include "xfer_mode_rate.h"
-
/*
* Unified Data Transfer Command
*/
@@ -62,7 +61,6 @@
#define CMD_A0_ASSIGN_ADDRESS(v) FIELD_PREP(W0_MASK( 14, 8), v)
#define CMD_A0_TID(v) FIELD_PREP(W0_MASK( 6, 3), v)
-
static unsigned int get_i3c_rate_idx(struct i3c_hci *hci)
{
struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index 607d77ab0e54..e925584113d1 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -14,14 +14,15 @@
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/module.h>
+#include <linux/platform_data/mipi-i3c-hci.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include "hci.h"
#include "ext_caps.h"
#include "cmd.h"
#include "dat.h"
-
/*
* Host Controller Capabilities and Operation Registers
*/
@@ -109,12 +110,17 @@
#define DEV_CTX_BASE_LO 0x60
#define DEV_CTX_BASE_HI 0x64
-
static inline struct i3c_hci *to_i3c_hci(struct i3c_master_controller *m)
{
return container_of(m, struct i3c_hci, master);
}
+static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci)
+{
+ reg_write(MASTER_DEVICE_ADDR,
+ MASTER_DYNAMIC_ADDR(hci->dyn_addr) | MASTER_DYNAMIC_ADDR_VALID);
+}
+
static int i3c_hci_bus_init(struct i3c_master_controller *m)
{
struct i3c_hci *hci = to_i3c_hci(m);
@@ -130,10 +136,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
- reg_write(MASTER_DEVICE_ADDR,
- MASTER_DYNAMIC_ADDR(ret) | MASTER_DYNAMIC_ADDR_VALID);
+ hci->dyn_addr = ret;
+ i3c_hci_set_master_dyn_addr(hci);
memset(&info, 0, sizeof(info));
- info.dyn_addr = ret;
+ info.dyn_addr = hci->dyn_addr;
ret = i3c_master_set_info(m, &info);
if (ret)
return ret;
@@ -152,16 +158,41 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
return 0;
}
+/* Bus disable should never fail, so be generous with the timeout */
+#define BUS_DISABLE_TIMEOUT_US (500 * USEC_PER_MSEC)
+
+static int i3c_hci_bus_disable(struct i3c_hci *hci)
+{
+ u32 regval;
+ int ret;
+
+ reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
+
+ /* Ensure controller is disabled */
+ ret = readx_poll_timeout(reg_read, HC_CONTROL, regval,
+ !(regval & HC_CONTROL_BUS_ENABLE), 0, BUS_DISABLE_TIMEOUT_US);
+ if (ret)
+ dev_err(&hci->master.dev, "%s: Failed to disable bus\n", __func__);
+
+ return ret;
+}
+
+void i3c_hci_sync_irq_inactive(struct i3c_hci *hci)
+{
+ struct platform_device *pdev = to_platform_device(hci->master.dev.parent);
+ int irq = platform_get_irq(pdev, 0);
+
+ reg_write(INTR_SIGNAL_ENABLE, 0x0);
+ hci->irq_inactive = true;
+ synchronize_irq(irq);
+}
+
static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
{
struct i3c_hci *hci = to_i3c_hci(m);
- struct platform_device *pdev = to_platform_device(m->dev.parent);
- reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
- synchronize_irq(platform_get_irq(pdev, 0));
+ i3c_hci_bus_disable(hci);
hci->io->cleanup(hci);
- if (hci->cmd == &mipi_i3c_hci_cmd_v1)
- mipi_i3c_hci_dat_v1.cleanup(hci);
}
void mipi_i3c_hci_resume(struct i3c_hci *hci)
@@ -535,6 +566,14 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
irqreturn_t result = IRQ_NONE;
u32 val;
+ /*
+ * The IRQ can be shared, so the handler may be called when the IRQ is
+ * due to a different device. That could happen when runtime suspended,
+ * so exit immediately if IRQs are not expected for this device.
+ */
+ if (hci->irq_inactive)
+ return IRQ_NONE;
+
val = reg_read(INTR_STATUS);
reg_write(INTR_STATUS, val);
dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val);
@@ -562,9 +601,233 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
return result;
}
+static int i3c_hci_software_reset(struct i3c_hci *hci)
+{
+ u32 regval;
+ int ret;
+
+ /*
+ * SOFT_RST must be clear before we write to it.
+ * Then we must wait until it clears again.
+ */
+ ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
+ !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__);
+ return ret;
+ }
+
+ reg_write(RESET_CONTROL, SOFT_RST);
+
+ ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
+ !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline bool is_version_1_1_or_newer(struct i3c_hci *hci)
+{
+ return hci->version_major > 1 || (hci->version_major == 1 && hci->version_minor > 0);
+}
+
+static int i3c_hci_set_io_mode(struct i3c_hci *hci, bool dma)
+{
+ bool pio_mode;
+
+ if (dma)
+ reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
+ else
+ reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
+
+ if (!is_version_1_1_or_newer(hci))
+ return 0;
+
+ pio_mode = reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE;
+ if ((dma && pio_mode) || (!dma && !pio_mode)) {
+ dev_err(&hci->master.dev, "%s mode is stuck\n", pio_mode ? "PIO" : "DMA");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int i3c_hci_reset_and_init(struct i3c_hci *hci)
+{
+ u32 regval;
+ int ret;
+
+ ret = i3c_hci_software_reset(hci);
+ if (ret)
+ return -ENXIO;
+
+ /* Disable all interrupts */
+ reg_write(INTR_SIGNAL_ENABLE, 0x0);
+ /*
+ * Only allow bit 31:10 signal updates because
+ * Bit 0:9 are reserved in IP version >= 0.8
+ * Bit 0:5 are defined in IP version < 0.8 but not handled by PIO code
+ */
+ reg_write(INTR_STATUS_ENABLE, GENMASK(31, 10));
+
+ /* Make sure our data ordering fits the host's */
+ regval = reg_read(HC_CONTROL);
+ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
+ if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) {
+ regval |= HC_CONTROL_DATA_BIG_ENDIAN;
+ reg_write(HC_CONTROL, regval);
+ regval = reg_read(HC_CONTROL);
+ if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) {
+ dev_err(&hci->master.dev, "cannot set BE mode\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ } else {
+ if (regval & HC_CONTROL_DATA_BIG_ENDIAN) {
+ regval &= ~HC_CONTROL_DATA_BIG_ENDIAN;
+ reg_write(HC_CONTROL, regval);
+ regval = reg_read(HC_CONTROL);
+ if (regval & HC_CONTROL_DATA_BIG_ENDIAN) {
+ dev_err(&hci->master.dev, "cannot clear BE mode\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ }
+
+ if (hci->io) {
+ ret = i3c_hci_set_io_mode(hci, hci->io == &mipi_i3c_hci_dma);
+ } else {
+ /* Try activating DMA operations first */
+ if (hci->RHS_regs) {
+ ret = i3c_hci_set_io_mode(hci, true);
+ if (!ret) {
+ hci->io = &mipi_i3c_hci_dma;
+ dev_dbg(&hci->master.dev, "Using DMA\n");
+ }
+ }
+
+ /* If no DMA, try PIO */
+ if (!hci->io && hci->PIO_regs) {
+ ret = i3c_hci_set_io_mode(hci, false);
+ if (!ret) {
+ hci->io = &mipi_i3c_hci_pio;
+ dev_dbg(&hci->master.dev, "Using PIO\n");
+ }
+ }
+
+ if (!hci->io) {
+ dev_err(&hci->master.dev, "neither DMA nor PIO can be used\n");
+ ret = ret ?: -EINVAL;
+ }
+ }
+ if (ret)
+ return ret;
+
+ /* Configure OD and PP timings for AMD platforms */
+ if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
+ amd_set_od_pp_timing(hci);
+
+ return 0;
+}
+
+static int i3c_hci_runtime_suspend(struct device *dev)
+{
+ struct i3c_hci *hci = dev_get_drvdata(dev);
+ int ret;
+
+ ret = i3c_hci_bus_disable(hci);
+ if (ret)
+ return ret;
+
+ hci->io->suspend(hci);
+
+ return 0;
+}
+
+static int i3c_hci_runtime_resume(struct device *dev)
+{
+ struct i3c_hci *hci = dev_get_drvdata(dev);
+ int ret;
+
+ ret = i3c_hci_reset_and_init(hci);
+ if (ret)
+ return -EIO;
+
+ i3c_hci_set_master_dyn_addr(hci);
+
+ mipi_i3c_hci_dat_v1.restore(hci);
+
+ hci->irq_inactive = false;
+
+ hci->io->resume(hci);
+
+ reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
+
+ return 0;
+}
+
+static int i3c_hci_suspend(struct device *dev)
+{
+ struct i3c_hci *hci = dev_get_drvdata(dev);
+
+ if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+ return 0;
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int i3c_hci_resume_common(struct device *dev, bool rstdaa)
+{
+ struct i3c_hci *hci = dev_get_drvdata(dev);
+ int ret;
+
+ if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED))
+ return 0;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ ret = i3c_master_do_daa_ext(&hci->master, rstdaa);
+ if (ret)
+ dev_err(dev, "Dynamic Address Assignment failed on resume, error %d\n", ret);
+
+ /*
+ * I3C devices may have retained their dynamic address anyway. Do not
+ * fail the resume because of DAA error.
+ */
+ return 0;
+}
+
+static int i3c_hci_resume(struct device *dev)
+{
+ return i3c_hci_resume_common(dev, false);
+}
+
+static int i3c_hci_restore(struct device *dev)
+{
+ return i3c_hci_resume_common(dev, true);
+}
+
+#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000
+
+static void i3c_hci_rpm_enable(struct device *dev)
+{
+ struct i3c_hci *hci = dev_get_drvdata(dev);
+
+ pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ devm_pm_runtime_set_active_enabled(dev);
+
+ hci->master.rpm_allowed = true;
+}
+
static int i3c_hci_init(struct i3c_hci *hci)
{
- bool size_in_dwords, mode_selector;
+ bool size_in_dwords;
u32 regval, offset;
int ret;
@@ -599,8 +862,8 @@ static int i3c_hci_init(struct i3c_hci *hci)
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);
+ dev_dbg(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
+ hci->DAT_entries, hci->DAT_entry_size, offset);
regval = reg_read(DCT_SECTION);
offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
@@ -609,76 +872,28 @@ static int i3c_hci_init(struct i3c_hci *hci)
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);
+ dev_dbg(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
+ hci->DCT_entries, hci->DCT_entry_size, offset);
regval = reg_read(RING_HEADERS_SECTION);
offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
hci->RHS_regs = offset ? hci->base_regs + offset : NULL;
- dev_info(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
+ dev_dbg(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
regval = reg_read(PIO_SECTION);
offset = FIELD_GET(PIO_REGS_OFFSET, regval);
hci->PIO_regs = offset ? hci->base_regs + offset : NULL;
- dev_info(&hci->master.dev, "PIO section at offset %#x\n", offset);
+ dev_dbg(&hci->master.dev, "PIO section at offset %#x\n", offset);
regval = reg_read(EXT_CAPS_SECTION);
offset = FIELD_GET(EXT_CAPS_OFFSET, regval);
hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL;
- dev_info(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
+ dev_dbg(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
ret = i3c_hci_parse_ext_caps(hci);
if (ret)
return ret;
- /*
- * Now let's reset the hardware.
- * SOFT_RST must be clear before we write to it.
- * Then we must wait until it clears again.
- */
- ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
- !(regval & SOFT_RST), 1, 10000);
- if (ret)
- return -ENXIO;
- reg_write(RESET_CONTROL, SOFT_RST);
- ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
- !(regval & SOFT_RST), 1, 10000);
- if (ret)
- return -ENXIO;
-
- /* Disable all interrupts */
- reg_write(INTR_SIGNAL_ENABLE, 0x0);
- /*
- * Only allow bit 31:10 signal updates because
- * Bit 0:9 are reserved in IP version >= 0.8
- * Bit 0:5 are defined in IP version < 0.8 but not handled by PIO code
- */
- reg_write(INTR_STATUS_ENABLE, GENMASK(31, 10));
-
- /* Make sure our data ordering fits the host's */
- regval = reg_read(HC_CONTROL);
- if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
- if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) {
- regval |= HC_CONTROL_DATA_BIG_ENDIAN;
- reg_write(HC_CONTROL, regval);
- regval = reg_read(HC_CONTROL);
- if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) {
- dev_err(&hci->master.dev, "cannot set BE mode\n");
- return -EOPNOTSUPP;
- }
- }
- } else {
- if (regval & HC_CONTROL_DATA_BIG_ENDIAN) {
- regval &= ~HC_CONTROL_DATA_BIG_ENDIAN;
- reg_write(HC_CONTROL, regval);
- regval = reg_read(HC_CONTROL);
- if (regval & HC_CONTROL_DATA_BIG_ENDIAN) {
- dev_err(&hci->master.dev, "cannot clear BE mode\n");
- return -EOPNOTSUPP;
- }
- }
- }
-
/* Select our command descriptor model */
switch (FIELD_GET(HC_CAP_CMD_SIZE, hci->caps)) {
case 0:
@@ -692,68 +907,44 @@ static int i3c_hci_init(struct i3c_hci *hci)
return -EINVAL;
}
- mode_selector = hci->version_major > 1 ||
- (hci->version_major == 1 && hci->version_minor > 0);
-
/* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */
if (hci->quirks & HCI_QUIRK_PIO_MODE)
hci->RHS_regs = NULL;
- /* Try activating DMA operations first */
- if (hci->RHS_regs) {
- reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
- if (mode_selector && (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
- dev_err(&hci->master.dev, "PIO mode is stuck\n");
- ret = -EIO;
- } else {
- hci->io = &mipi_i3c_hci_dma;
- dev_info(&hci->master.dev, "Using DMA\n");
- }
- }
-
- /* If no DMA, try PIO */
- if (!hci->io && hci->PIO_regs) {
- reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
- if (mode_selector && !(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
- dev_err(&hci->master.dev, "DMA mode is stuck\n");
- ret = -EIO;
- } else {
- hci->io = &mipi_i3c_hci_pio;
- dev_info(&hci->master.dev, "Using PIO\n");
- }
- }
-
- if (!hci->io) {
- dev_err(&hci->master.dev, "neither DMA nor PIO can be used\n");
- if (!ret)
- ret = -EINVAL;
- return ret;
- }
-
- /* Configure OD and PP timings for AMD platforms */
- if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
- amd_set_od_pp_timing(hci);
-
- return 0;
+ return i3c_hci_reset_and_init(hci);
}
static int i3c_hci_probe(struct platform_device *pdev)
{
+ const struct mipi_i3c_hci_platform_data *pdata = pdev->dev.platform_data;
struct i3c_hci *hci;
int irq, ret;
hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL);
if (!hci)
return -ENOMEM;
- hci->base_regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(hci->base_regs))
- return PTR_ERR(hci->base_regs);
+
+ /*
+ * Multi-bus instances share the same MMIO address range, but not
+ * necessarily in separate contiguous sub-ranges. To avoid overlapping
+ * mappings, provide base_regs from the parent mapping.
+ */
+ if (pdata)
+ hci->base_regs = pdata->base_regs;
+
+ if (!hci->base_regs) {
+ hci->base_regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hci->base_regs))
+ return PTR_ERR(hci->base_regs);
+ }
platform_set_drvdata(pdev, hci);
/* temporary for dev_printk's, to be replaced in i3c_master_register */
hci->master.dev.init_name = dev_name(&pdev->dev);
hci->quirks = (unsigned long)device_get_match_data(&pdev->dev);
+ if (!hci->quirks && platform_get_device_id(pdev))
+ hci->quirks = platform_get_device_id(pdev)->driver_data;
ret = i3c_hci_init(hci);
if (ret)
@@ -761,16 +952,14 @@ static int i3c_hci_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler,
- 0, NULL, hci);
+ IRQF_SHARED, NULL, hci);
if (ret)
return ret;
- ret = i3c_master_register(&hci->master, &pdev->dev,
- &i3c_hci_ops, false);
- if (ret)
- return ret;
+ if (hci->quirks & HCI_QUIRK_RPM_ALLOWED)
+ i3c_hci_rpm_enable(&pdev->dev);
- return 0;
+ return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false);
}
static void i3c_hci_remove(struct platform_device *pdev)
@@ -792,13 +981,31 @@ static const struct acpi_device_id i3c_hci_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
+static const struct platform_device_id i3c_hci_driver_ids[] = {
+ { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids);
+
+static const struct dev_pm_ops i3c_hci_pm_ops = {
+ .suspend = pm_sleep_ptr(i3c_hci_suspend),
+ .resume = pm_sleep_ptr(i3c_hci_resume),
+ .freeze = pm_sleep_ptr(i3c_hci_suspend),
+ .thaw = pm_sleep_ptr(i3c_hci_resume),
+ .poweroff = pm_sleep_ptr(i3c_hci_suspend),
+ .restore = pm_sleep_ptr(i3c_hci_restore),
+ RUNTIME_PM_OPS(i3c_hci_runtime_suspend, i3c_hci_runtime_resume, NULL)
+};
+
static struct platform_driver i3c_hci_driver = {
.probe = i3c_hci_probe,
.remove = i3c_hci_remove,
+ .id_table = i3c_hci_driver_ids,
.driver = {
.name = "mipi-i3c-hci",
.of_match_table = of_match_ptr(i3c_hci_of_match),
.acpi_match_table = i3c_hci_acpi_match,
+ .pm = pm_ptr(&i3c_hci_pm_ops),
},
};
module_platform_driver(i3c_hci_driver);
diff --git a/drivers/i3c/master/mipi-i3c-hci/dat.h b/drivers/i3c/master/mipi-i3c-hci/dat.h
index 1f0f345c3daf..6881f19da77f 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dat.h
+++ b/drivers/i3c/master/mipi-i3c-hci/dat.h
@@ -17,7 +17,6 @@
struct hci_dat_ops {
int (*init)(struct i3c_hci *hci);
- void (*cleanup)(struct i3c_hci *hci);
int (*alloc_entry)(struct i3c_hci *hci);
void (*free_entry)(struct i3c_hci *hci, unsigned int dat_idx);
void (*set_dynamic_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 addr);
@@ -25,6 +24,7 @@ struct hci_dat_ops {
void (*set_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 w1);
void (*clear_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 w1);
int (*get_index)(struct i3c_hci *hci, u8 address);
+ void (*restore)(struct i3c_hci *hci);
};
extern const struct hci_dat_ops mipi_i3c_hci_dat_v1;
diff --git a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
index 85c4916972e4..852966aa20d9 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
@@ -15,7 +15,6 @@
#include "hci.h"
#include "dat.h"
-
/*
* Device Address Table Structure
*/
@@ -35,13 +34,26 @@
/* DAT_0_IBI_PAYLOAD W0_BIT_(12) */
#define DAT_0_STATIC_ADDRESS W0_MASK(6, 0)
-#define dat_w0_read(i) readl(hci->DAT_regs + (i) * 8)
-#define dat_w1_read(i) readl(hci->DAT_regs + (i) * 8 + 4)
-#define dat_w0_write(i, v) writel(v, hci->DAT_regs + (i) * 8)
-#define dat_w1_write(i, v) writel(v, hci->DAT_regs + (i) * 8 + 4)
+#define dat_w0_read(i) hci->DAT[i].w0
+#define dat_w1_read(i) hci->DAT[i].w1
+#define dat_w0_write(i, v) hci_dat_w0_write(hci, i, v)
+#define dat_w1_write(i, v) hci_dat_w1_write(hci, i, v)
+
+static inline void hci_dat_w0_write(struct i3c_hci *hci, int i, u32 v)
+{
+ hci->DAT[i].w0 = v;
+ writel(v, hci->DAT_regs + i * 8);
+}
+
+static inline void hci_dat_w1_write(struct i3c_hci *hci, int i, u32 v)
+{
+ hci->DAT[i].w1 = v;
+ writel(v, hci->DAT_regs + i * 8 + 4);
+}
static int hci_dat_v1_init(struct i3c_hci *hci)
{
+ struct device *dev = hci->master.dev.parent;
unsigned int dat_idx;
if (!hci->DAT_regs) {
@@ -55,9 +67,15 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
return -EOPNOTSUPP;
}
+ if (!hci->DAT) {
+ hci->DAT = devm_kcalloc(dev, hci->DAT_entries, hci->DAT_entry_size, GFP_KERNEL);
+ if (!hci->DAT)
+ return -ENOMEM;
+ }
+
if (!hci->DAT_data) {
/* use a bitmap for faster free slot search */
- hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
+ hci->DAT_data = devm_bitmap_zalloc(dev, hci->DAT_entries, GFP_KERNEL);
if (!hci->DAT_data)
return -ENOMEM;
@@ -71,12 +89,6 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
return 0;
}
-static void hci_dat_v1_cleanup(struct i3c_hci *hci)
-{
- bitmap_free(hci->DAT_data);
- hci->DAT_data = NULL;
-}
-
static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
{
unsigned int dat_idx;
@@ -169,9 +181,16 @@ static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
return -ENODEV;
}
+static void hci_dat_v1_restore(struct i3c_hci *hci)
+{
+ for (int i = 0; i < hci->DAT_entries; i++) {
+ writel(hci->DAT[i].w0, hci->DAT_regs + i * 8);
+ writel(hci->DAT[i].w1, hci->DAT_regs + i * 8 + 4);
+ }
+}
+
const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
.init = hci_dat_v1_init,
- .cleanup = hci_dat_v1_cleanup,
.alloc_entry = hci_dat_v1_alloc_entry,
.free_entry = hci_dat_v1_free_entry,
.set_dynamic_addr = hci_dat_v1_set_dynamic_addr,
@@ -179,4 +198,5 @@ const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
.set_flags = hci_dat_v1_set_flags,
.clear_flags = hci_dat_v1_clear_flags,
.get_index = hci_dat_v1_get_index,
+ .restore = hci_dat_v1_restore,
};
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index c401a9425cdc..2e5b4974d431 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -20,7 +20,6 @@
#include "cmd.h"
#include "ibi.h"
-
/*
* Software Parameter Values (somewhat arb itrary for now).
* Some of them could be determined at run time eventually.
@@ -124,7 +123,6 @@
#define DATA_BUF_IOC BIT(30) /* Interrupt on Completion */
#define DATA_BUF_BLOCK_SIZE GENMASK(15, 0)
-
struct hci_rh_data {
void __iomem *regs;
void *xfer, *resp, *ibi_status, *ibi_data;
@@ -162,8 +160,31 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
rh_reg_write(INTR_SIGNAL_ENABLE, 0);
rh_reg_write(RING_CONTROL, 0);
+ }
+
+ i3c_hci_sync_irq_inactive(hci);
+
+ for (i = 0; i < rings->total; i++) {
+ rh = &rings->headers[i];
+
rh_reg_write(CR_SETUP, 0);
rh_reg_write(IBI_SETUP, 0);
+ }
+
+ rhs_reg_write(CONTROL, 0);
+}
+
+static void hci_dma_free(void *data)
+{
+ struct i3c_hci *hci = data;
+ struct hci_rings_data *rings = hci->io_data;
+ struct hci_rh_data *rh;
+
+ if (!rings)
+ return;
+
+ for (int i = 0; i < rings->total; i++) {
+ rh = &rings->headers[i];
if (rh->xfer)
dma_free_coherent(rings->sysdev,
@@ -185,12 +206,98 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
kfree(rh->ibi_data);
}
- rhs_reg_write(CONTROL, 0);
-
kfree(rings);
hci->io_data = NULL;
}
+static void hci_dma_init_rh(struct i3c_hci *hci, struct hci_rh_data *rh, int i)
+{
+ u32 regval;
+
+ 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);
+
+ rh_reg_write(INTR_STATUS_ENABLE, 0xffffffff);
+ rh_reg_write(INTR_SIGNAL_ENABLE, INTR_IBI_READY |
+ INTR_TRANSFER_COMPLETION |
+ INTR_RING_OP |
+ INTR_TRANSFER_ERR |
+ INTR_IBI_RING_FULL |
+ INTR_TRANSFER_ABORT);
+
+ if (i >= IBI_RINGS)
+ goto ring_ready;
+
+ 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, ilog2(rh->ibi_chunk_sz) - 2) |
+ FIELD_PREP(IBI_DATA_CHUNK_COUNT, rh->ibi_chunks_total);
+ rh_reg_write(IBI_SETUP, regval);
+
+ regval = rh_reg_read(INTR_SIGNAL_ENABLE);
+ regval |= INTR_IBI_READY;
+ rh_reg_write(INTR_SIGNAL_ENABLE, regval);
+
+ring_ready:
+ /*
+ * The MIPI I3C HCI specification does not document reset values for
+ * RING_OPERATION1 fields and some controllers (e.g. Intel controllers)
+ * do not reset the values, so ensure the ring pointers are set to zero
+ * here.
+ */
+ rh_reg_write(RING_OPERATION1, 0);
+
+ rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
+ rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP);
+
+ rh->done_ptr = 0;
+ rh->ibi_chunk_ptr = 0;
+}
+
+static void hci_dma_init_rings(struct i3c_hci *hci)
+{
+ struct hci_rings_data *rings = hci->io_data;
+ u32 regval;
+
+ regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
+ rhs_reg_write(CONTROL, regval);
+
+ for (int i = 0; i < rings->total; i++)
+ hci_dma_init_rh(hci, &rings->headers[i], i);
+}
+
+static void hci_dma_suspend(struct i3c_hci *hci)
+{
+ struct hci_rings_data *rings = hci->io_data;
+ int n = rings ? rings->total : 0;
+
+ for (int i = 0; i < n; i++) {
+ struct hci_rh_data *rh = &rings->headers[i];
+
+ rh_reg_write(INTR_SIGNAL_ENABLE, 0);
+ rh_reg_write(RING_CONTROL, 0);
+ }
+
+ i3c_hci_sync_irq_inactive(hci);
+}
+
+static void hci_dma_resume(struct i3c_hci *hci)
+{
+ struct hci_rings_data *rings = hci->io_data;
+
+ if (rings)
+ hci_dma_init_rings(hci);
+}
+
static int hci_dma_init(struct i3c_hci *hci)
{
struct hci_rings_data *rings;
@@ -214,7 +321,7 @@ static int hci_dma_init(struct i3c_hci *hci)
regval = rhs_reg_read(CONTROL);
nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval);
- dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings);
+ dev_dbg(&hci->master.dev, "%d DMA rings available\n", nr_rings);
if (unlikely(nr_rings > 8)) {
dev_err(&hci->master.dev, "number of rings should be <= 8\n");
nr_rings = 8;
@@ -228,13 +335,10 @@ static int hci_dma_init(struct i3c_hci *hci)
rings->total = nr_rings;
rings->sysdev = sysdev;
- regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
- rhs_reg_write(CONTROL, regval);
-
for (i = 0; i < rings->total; i++) {
u32 offset = rhs_reg_read(RHn_OFFSET(i));
- dev_info(&hci->master.dev, "Ring %d at offset %#x\n", i, offset);
+ dev_dbg(&hci->master.dev, "Ring %d at offset %#x\n", i, offset);
ret = -EINVAL;
if (!offset)
goto err_out;
@@ -265,26 +369,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, 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);
-
- rh_reg_write(INTR_STATUS_ENABLE, 0xffffffff);
- rh_reg_write(INTR_SIGNAL_ENABLE, INTR_IBI_READY |
- INTR_TRANSFER_COMPLETION |
- INTR_RING_OP |
- INTR_TRANSFER_ERR |
- INTR_IBI_RING_FULL |
- INTR_TRANSFER_ABORT);
-
/* IBIs */
if (i >= IBI_RINGS)
- goto ring_ready;
+ continue;
regval = rh_reg_read(IBI_SETUP);
rh->ibi_status_sz = FIELD_GET(IBI_STATUS_STRUCT_SIZE, regval);
@@ -323,33 +411,18 @@ static int hci_dma_init(struct i3c_hci *hci)
ret = -ENOMEM;
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,
- ilog2(rh->ibi_chunk_sz) - 2) |
- FIELD_PREP(IBI_DATA_CHUNK_COUNT,
- rh->ibi_chunks_total);
- rh_reg_write(IBI_SETUP, regval);
-
- regval = rh_reg_read(INTR_SIGNAL_ENABLE);
- regval |= INTR_IBI_READY;
- rh_reg_write(INTR_SIGNAL_ENABLE, regval);
+ ret = devm_add_action(hci->master.dev.parent, hci_dma_free, hci);
+ if (ret)
+ goto err_out;
-ring_ready:
- rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE |
- RING_CTRL_RUN_STOP);
- }
+ hci_dma_init_rings(hci);
return 0;
err_out:
- hci_dma_cleanup(hci);
+ hci_dma_free(hci);
return ret;
}
@@ -815,4 +888,6 @@ const struct hci_io_ops mipi_i3c_hci_dma = {
.request_ibi = hci_dma_request_ibi,
.free_ibi = hci_dma_free_ibi,
.recycle_ibi_slot = hci_dma_recycle_ibi_slot,
+ .suspend = hci_dma_suspend,
+ .resume = hci_dma_resume,
};
diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
index 7714f00ea9cc..77840fd4aa51 100644
--- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
+++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
@@ -16,7 +16,6 @@
#include "ext_caps.h"
#include "xfer_mode_rate.h"
-
/* Extended Capability Header */
#define CAP_HEADER_LENGTH GENMASK(23, 8)
#define CAP_HEADER_ID GENMASK(7, 0)
@@ -27,9 +26,9 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base)
hci->vendor_version_id = readl(base + 0x08);
hci->vendor_product_id = readl(base + 0x0c);
- dev_info(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
- dev_info(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
- dev_info(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
+ dev_dbg(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
+ dev_dbg(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
+ dev_dbg(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
/* ought to go in a table if this grows too much */
switch (hci->vendor_mipi_id) {
@@ -49,7 +48,7 @@ static int hci_extcap_master_config(struct i3c_hci *hci, void __iomem *base)
static const char * const functionality[] = {
"(unknown)", "master only", "target only",
"primary/secondary master" };
- dev_info(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
+ dev_dbg(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
if (operation_mode & 0x1)
return 0;
dev_err(&hci->master.dev, "only master mode is currently supported\n");
@@ -61,7 +60,7 @@ static int hci_extcap_multi_bus(struct i3c_hci *hci, void __iomem *base)
u32 bus_instance = readl(base + 0x04);
unsigned int count = FIELD_GET(GENMASK(3, 0), bus_instance);
- dev_info(&hci->master.dev, "%d bus instances\n", count);
+ dev_dbg(&hci->master.dev, "%d bus instances\n", count);
return 0;
}
@@ -71,8 +70,7 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base)
u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
unsigned int index;
- dev_info(&hci->master.dev, "transfer mode table has %d entries\n",
- entries);
+ dev_dbg(&hci->master.dev, "transfer mode table has %d entries\n", entries);
base += 4; /* skip header */
for (index = 0; index < entries; index++) {
u32 mode_entry = readl(base);
@@ -95,7 +93,7 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
base += 4; /* skip header */
- dev_info(&hci->master.dev, "available data rates:\n");
+ dev_dbg(&hci->master.dev, "available data rates:\n");
for (index = 0; index < entries; index++) {
rate_entry = readl(base);
dev_dbg(&hci->master.dev, "entry %d: 0x%08x",
@@ -103,12 +101,12 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry);
rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry);
mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry);
- dev_info(&hci->master.dev, "rate %d for %s = %d kHz\n",
- rate_id,
- mode_id == XFERRATE_MODE_I3C ? "I3C" :
- mode_id == XFERRATE_MODE_I2C ? "I2C" :
- "unknown mode",
- rate);
+ dev_dbg(&hci->master.dev, "rate %d for %s = %d kHz\n",
+ rate_id,
+ mode_id == XFERRATE_MODE_I3C ? "I3C" :
+ mode_id == XFERRATE_MODE_I2C ? "I2C" :
+ "unknown mode",
+ rate);
base += 4;
}
@@ -122,8 +120,8 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
u32 autocmd_ext_config = readl(base + 0x08);
unsigned int count = FIELD_GET(GENMASK(3, 0), autocmd_ext_config);
- dev_info(&hci->master.dev, "%d/%d active auto-command entries\n",
- count, max_count);
+ dev_dbg(&hci->master.dev, "%d/%d active auto-command entries\n",
+ count, max_count);
/* remember auto-command register location for later use */
hci->AUTOCMD_regs = base;
return 0;
@@ -131,46 +129,46 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
static int hci_extcap_debug(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "debug registers present\n");
+ dev_dbg(&hci->master.dev, "debug registers present\n");
hci->DEBUG_regs = base;
return 0;
}
static int hci_extcap_scheduled_cmd(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "scheduled commands available\n");
+ dev_dbg(&hci->master.dev, "scheduled commands available\n");
/* hci->schedcmd_regs = base; */
return 0;
}
static int hci_extcap_non_curr_master(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "Non-Current Master support available\n");
+ dev_dbg(&hci->master.dev, "Non-Current Master support available\n");
/* hci->NCM_regs = base; */
return 0;
}
static int hci_extcap_ccc_resp_conf(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "CCC Response Configuration available\n");
+ dev_dbg(&hci->master.dev, "CCC Response Configuration available\n");
return 0;
}
static int hci_extcap_global_DAT(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "Global DAT available\n");
+ dev_dbg(&hci->master.dev, "Global DAT available\n");
return 0;
}
static int hci_extcap_multilane(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "Master Multi-Lane support available\n");
+ dev_dbg(&hci->master.dev, "Master Multi-Lane support available\n");
return 0;
}
static int hci_extcap_ncm_multilane(struct i3c_hci *hci, void __iomem *base)
{
- dev_info(&hci->master.dev, "NCM Multi-Lane support available\n");
+ dev_dbg(&hci->master.dev, "NCM Multi-Lane support available\n");
return 0;
}
@@ -203,7 +201,7 @@ static const struct hci_ext_caps ext_capabilities[] = {
static int hci_extcap_vendor_NXP(struct i3c_hci *hci, void __iomem *base)
{
hci->vendor_data = (__force void *)base;
- dev_info(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1*4));
+ dev_dbg(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1 * 4));
/* reset the FPGA */
writel(0xdeadbeef, base + 1*4);
return 0;
@@ -241,9 +239,8 @@ static int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *base,
}
if (!vendor_cap_entry) {
- dev_notice(&hci->master.dev,
- "unknown ext_cap 0x%02x for vendor 0x%02x\n",
- cap_id, hci->vendor_mipi_id);
+ dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x for vendor 0x%02x\n",
+ cap_id, hci->vendor_mipi_id);
return 0;
}
if (cap_length < vendor_cap_entry->min_length) {
@@ -272,7 +269,7 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header);
dev_dbg(&hci->master.dev, "id=0x%02x length=%d",
cap_id, cap_length);
- if (!cap_length)
+ if (!cap_id || !cap_length)
break;
if (curr_cap + cap_length * 4 >= end) {
dev_err(&hci->master.dev,
@@ -296,8 +293,7 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
}
}
if (!cap_entry) {
- dev_notice(&hci->master.dev,
- "unknown ext_cap 0x%02x\n", cap_id);
+ dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x\n", cap_id);
} else if (cap_length < cap_entry->min_length) {
dev_err(&hci->master.dev,
"ext_cap 0x%02x has size %d (expecting >= %d)\n",
diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.h b/drivers/i3c/master/mipi-i3c-hci/ext_caps.h
index 9df17822fdb4..b15e629951f0 100644
--- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.h
+++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.h
@@ -13,7 +13,6 @@
/* MIPI vendor IDs */
#define MIPI_VENDOR_NXP 0x11b
-
int i3c_hci_parse_ext_caps(struct i3c_hci *hci);
#endif
diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
index 249ccb13c909..6035f74212db 100644
--- a/drivers/i3c/master/mipi-i3c-hci/hci.h
+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
@@ -31,6 +31,11 @@
struct hci_cmd_ops;
+struct dat_words {
+ u32 w0;
+ u32 w1;
+};
+
/* Our main structure */
struct i3c_hci {
struct i3c_master_controller master;
@@ -46,23 +51,25 @@ struct i3c_hci {
void *io_data;
const struct hci_cmd_ops *cmd;
atomic_t next_cmd_tid;
+ bool irq_inactive;
u32 caps;
unsigned int quirks;
unsigned int DAT_entries;
unsigned int DAT_entry_size;
void *DAT_data;
+ struct dat_words *DAT;
unsigned int DCT_entries;
unsigned int DCT_entry_size;
u8 version_major;
u8 version_minor;
u8 revision;
+ u8 dyn_addr;
u32 vendor_mipi_id;
u32 vendor_version_id;
u32 vendor_product_id;
void *vendor_data;
};
-
/*
* Structure to represent a master initiated transfer.
* The rnw, data and data_len fields must be initialized before calling any
@@ -108,7 +115,6 @@ static inline void hci_free_xfer(struct hci_xfer *xfer, unsigned int n)
kfree(xfer);
}
-
/* This abstracts PIO vs DMA operations */
struct hci_io_ops {
bool (*irq_handler)(struct i3c_hci *hci);
@@ -121,25 +127,25 @@ struct hci_io_ops {
struct i3c_ibi_slot *slot);
int (*init)(struct i3c_hci *hci);
void (*cleanup)(struct i3c_hci *hci);
+ void (*suspend)(struct i3c_hci *hci);
+ void (*resume)(struct i3c_hci *hci);
};
extern const struct hci_io_ops mipi_i3c_hci_pio;
extern const struct hci_io_ops mipi_i3c_hci_dma;
-
/* Our per device master private data */
struct i3c_hci_dev_data {
int dat_idx;
void *ibi_data;
};
-
/* list of quirks */
#define HCI_QUIRK_RAW_CCC BIT(1) /* CCC framing must be explicit */
#define HCI_QUIRK_PIO_MODE BIT(2) /* Set PIO mode for AMD platforms */
#define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */
#define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */
-
+#define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */
/* global functions */
void mipi_i3c_hci_resume(struct i3c_hci *hci);
@@ -147,5 +153,6 @@ void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
void amd_set_od_pp_timing(struct i3c_hci *hci);
void amd_set_resp_buf_thld(struct i3c_hci *hci);
+void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
#endif
diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
index dc8ede0f8ad8..0f05a15c14c7 100644
--- a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
+++ b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
@@ -12,14 +12,23 @@
#include <linux/idr.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_data/mipi-i3c-hci.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
+#include <linux/pm_runtime.h>
+
+/*
+ * There can up to 15 instances, but implementations have at most 2 at this
+ * time.
+ */
+#define INST_MAX 2
struct mipi_i3c_hci_pci {
struct pci_dev *pci;
- struct platform_device *pdev;
+ void __iomem *base;
const struct mipi_i3c_hci_pci_info *info;
void *private;
};
@@ -27,12 +36,13 @@ struct mipi_i3c_hci_pci {
struct mipi_i3c_hci_pci_info {
int (*init)(struct mipi_i3c_hci_pci *hci);
void (*exit)(struct mipi_i3c_hci_pci *hci);
+ const char *name;
+ int id[INST_MAX];
+ u32 instance_offset[INST_MAX];
+ int instance_count;
};
-static DEFINE_IDA(mipi_i3c_hci_pci_ida);
-
#define INTEL_PRIV_OFFSET 0x2b0
-#define INTEL_PRIV_SIZE 0x28
#define INTEL_RESETS 0x04
#define INTEL_RESETS_RESET BIT(0)
#define INTEL_RESETS_RESET_DONE BIT(1)
@@ -143,19 +153,12 @@ static void intel_reset(void __iomem *priv)
writel(INTEL_RESETS_RESET, priv + INTEL_RESETS);
}
-static void __iomem *intel_priv(struct pci_dev *pci)
-{
- resource_size_t base = pci_resource_start(pci, 0);
-
- return devm_ioremap(&pci->dev, base + INTEL_PRIV_OFFSET, INTEL_PRIV_SIZE);
-}
-
static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
{
struct intel_host *host = devm_kzalloc(&hci->pci->dev, sizeof(*host), GFP_KERNEL);
- void __iomem *priv = intel_priv(hci->pci);
+ void __iomem *priv = hci->base + INTEL_PRIV_OFFSET;
- if (!host || !priv)
+ if (!host)
return -ENOMEM;
dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
@@ -179,17 +182,89 @@ static void intel_i3c_exit(struct mipi_i3c_hci_pci *hci)
intel_ltr_hide(&hci->pci->dev);
}
-static const struct mipi_i3c_hci_pci_info intel_info = {
+static const struct mipi_i3c_hci_pci_info intel_mi_1_info = {
+ .init = intel_i3c_init,
+ .exit = intel_i3c_exit,
+ .name = "intel-lpss-i3c",
+ .id = {0, 1},
+ .instance_offset = {0, 0x400},
+ .instance_count = 2,
+};
+
+static const struct mipi_i3c_hci_pci_info intel_mi_2_info = {
+ .init = intel_i3c_init,
+ .exit = intel_i3c_exit,
+ .name = "intel-lpss-i3c",
+ .id = {2, 3},
+ .instance_offset = {0, 0x400},
+ .instance_count = 2,
+};
+
+static const struct mipi_i3c_hci_pci_info intel_si_2_info = {
.init = intel_i3c_init,
.exit = intel_i3c_exit,
+ .name = "intel-lpss-i3c",
+ .id = {2},
+ .instance_offset = {0},
+ .instance_count = 1,
};
+static void mipi_i3c_hci_pci_rpm_allow(struct device *dev)
+{
+ pm_runtime_put(dev);
+ pm_runtime_allow(dev);
+}
+
+static void mipi_i3c_hci_pci_rpm_forbid(struct device *dev)
+{
+ pm_runtime_forbid(dev);
+ pm_runtime_get_sync(dev);
+}
+
+struct mipi_i3c_hci_pci_cell_data {
+ struct mipi_i3c_hci_platform_data pdata;
+ struct resource res;
+};
+
+static void mipi_i3c_hci_pci_setup_cell(struct mipi_i3c_hci_pci *hci, int idx,
+ struct mipi_i3c_hci_pci_cell_data *data,
+ struct mfd_cell *cell)
+{
+ data->pdata.base_regs = hci->base + hci->info->instance_offset[idx];
+
+ data->res = DEFINE_RES_IRQ(0);
+
+ cell->name = hci->info->name;
+ cell->id = hci->info->id[idx];
+ cell->platform_data = &data->pdata;
+ cell->pdata_size = sizeof(data->pdata);
+ cell->num_resources = 1;
+ cell->resources = &data->res;
+}
+
+#define mipi_i3c_hci_pci_alloc(h, x) kcalloc((h)->info->instance_count, sizeof(*(x)), GFP_KERNEL)
+
+static int mipi_i3c_hci_pci_add_instances(struct mipi_i3c_hci_pci *hci)
+{
+ struct mipi_i3c_hci_pci_cell_data *data __free(kfree) = mipi_i3c_hci_pci_alloc(hci, data);
+ struct mfd_cell *cells __free(kfree) = mipi_i3c_hci_pci_alloc(hci, cells);
+ int irq = pci_irq_vector(hci->pci, 0);
+ int nr = hci->info->instance_count;
+
+ if (!cells || !data)
+ return -ENOMEM;
+
+ for (int i = 0; i < nr; i++)
+ mipi_i3c_hci_pci_setup_cell(hci, i, data + i, cells + i);
+
+ return mfd_add_devices(&hci->pci->dev, 0, cells, nr, NULL, irq, NULL);
+}
+
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct mipi_i3c_hci_pci *hci;
- struct resource res[2];
- int dev_id, ret;
+ int ret;
hci = devm_kzalloc(&pci->dev, sizeof(*hci), GFP_KERNEL);
if (!hci)
@@ -203,81 +278,65 @@ static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
pci_set_master(pci);
- memset(&res, 0, sizeof(res));
-
- res[0].flags = IORESOURCE_MEM;
- res[0].start = pci_resource_start(pci, 0);
- res[0].end = pci_resource_end(pci, 0);
-
- res[1].flags = IORESOURCE_IRQ;
- res[1].start = pci->irq;
- res[1].end = pci->irq;
+ hci->base = pcim_iomap_region(pci, 0, pci_name(pci));
+ if (IS_ERR(hci->base))
+ return PTR_ERR(hci->base);
- dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
- if (dev_id < 0)
- return dev_id;
-
- hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
- if (!hci->pdev)
- return -ENOMEM;
+ ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ return ret;
- hci->pdev->dev.parent = &pci->dev;
- device_set_node(&hci->pdev->dev, dev_fwnode(&pci->dev));
+ hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data;
- ret = platform_device_add_resources(hci->pdev, res, ARRAY_SIZE(res));
+ ret = hci->info->init ? hci->info->init(hci) : 0;
if (ret)
- goto err;
-
- hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data;
- if (hci->info && hci->info->init) {
- ret = hci->info->init(hci);
- if (ret)
- goto err;
- }
+ return ret;
- ret = platform_device_add(hci->pdev);
+ ret = mipi_i3c_hci_pci_add_instances(hci);
if (ret)
goto err_exit;
pci_set_drvdata(pci, hci);
+ mipi_i3c_hci_pci_rpm_allow(&pci->dev);
+
return 0;
err_exit:
- if (hci->info && hci->info->exit)
+ if (hci->info->exit)
hci->info->exit(hci);
-err:
- platform_device_put(hci->pdev);
- ida_free(&mipi_i3c_hci_pci_ida, dev_id);
return ret;
}
static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
{
struct mipi_i3c_hci_pci *hci = pci_get_drvdata(pci);
- struct platform_device *pdev = hci->pdev;
- int dev_id = pdev->id;
- if (hci->info && hci->info->exit)
+ if (hci->info->exit)
hci->info->exit(hci);
- platform_device_unregister(pdev);
- ida_free(&mipi_i3c_hci_pci_ida, dev_id);
+ mipi_i3c_hci_pci_rpm_forbid(&pci->dev);
+
+ mfd_remove_devices(&pci->dev);
}
+/* PM ops must exist for PCI to put a device to a low power state */
+static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = {
+};
+
static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
/* Wildcat Lake-U */
- { PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_info},
- { PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_info},
+ { PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_mi_1_info},
+ { PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_si_2_info},
/* Panther Lake-H */
- { PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
- { PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},
+ { PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_mi_1_info},
+ { PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_si_2_info},
/* Panther Lake-P */
- { PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
- { PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
+ { PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_mi_1_info},
+ { PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_si_2_info},
/* Nova Lake-S */
- { PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_info},
- { PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_info},
+ { PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_mi_1_info},
+ { PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_mi_2_info},
{ },
};
MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);
@@ -287,6 +346,9 @@ static struct pci_driver mipi_i3c_hci_pci_driver = {
.id_table = mipi_i3c_hci_pci_devices,
.probe = mipi_i3c_hci_pci_probe,
.remove = mipi_i3c_hci_pci_remove,
+ .driver = {
+ .pm = pm_ptr(&mipi_i3c_hci_pci_pm_ops)
+ },
};
module_pci_driver(mipi_i3c_hci_pci_driver);
diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c
index 710faa46a00f..8e868e81acda 100644
--- a/drivers/i3c/master/mipi-i3c-hci/pio.c
+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c
@@ -15,7 +15,6 @@
#include "cmd.h"
#include "ibi.h"
-
/*
* PIO Access Area
*/
@@ -136,27 +135,14 @@ struct hci_pio_data {
u32 enabled_irqs;
};
-static int hci_pio_init(struct i3c_hci *hci)
+static void __hci_pio_init(struct i3c_hci *hci, u32 *size_val_ptr)
{
- struct hci_pio_data *pio;
u32 val, size_val, rx_thresh, tx_thresh, ibi_val;
-
- pio = kzalloc(sizeof(*pio), GFP_KERNEL);
- if (!pio)
- return -ENOMEM;
-
- hci->io_data = pio;
- spin_lock_init(&pio->lock);
+ struct hci_pio_data *pio = hci->io_data;
size_val = pio_reg_read(QUEUE_SIZE);
- dev_info(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
- FIELD_GET(CR_QUEUE_SIZE, size_val));
- dev_info(&hci->master.dev, "IBI FIFO = %ld bytes\n",
- 4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
- dev_info(&hci->master.dev, "RX data FIFO = %d bytes\n",
- 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
- dev_info(&hci->master.dev, "TX data FIFO = %d bytes\n",
- 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
+ if (size_val_ptr)
+ *size_val_ptr = size_val;
/*
* Let's initialize data thresholds to half of the actual FIFO size.
@@ -202,6 +188,42 @@ static int hci_pio_init(struct i3c_hci *hci)
/* Always accept error interrupts (will be activated on first xfer) */
pio->enabled_irqs = STAT_ALL_ERRORS;
+}
+
+static void hci_pio_suspend(struct i3c_hci *hci)
+{
+ pio_reg_write(INTR_SIGNAL_ENABLE, 0);
+
+ i3c_hci_sync_irq_inactive(hci);
+}
+
+static void hci_pio_resume(struct i3c_hci *hci)
+{
+ __hci_pio_init(hci, NULL);
+}
+
+static int hci_pio_init(struct i3c_hci *hci)
+{
+ struct hci_pio_data *pio;
+ u32 size_val;
+
+ pio = devm_kzalloc(hci->master.dev.parent, sizeof(*pio), GFP_KERNEL);
+ if (!pio)
+ return -ENOMEM;
+
+ hci->io_data = pio;
+ spin_lock_init(&pio->lock);
+
+ __hci_pio_init(hci, &size_val);
+
+ dev_dbg(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n",
+ FIELD_GET(CR_QUEUE_SIZE, size_val));
+ dev_dbg(&hci->master.dev, "IBI FIFO = %ld bytes\n",
+ 4 * FIELD_GET(IBI_STATUS_SIZE, size_val));
+ dev_dbg(&hci->master.dev, "RX data FIFO = %d bytes\n",
+ 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val)));
+ dev_dbg(&hci->master.dev, "TX data FIFO = %d bytes\n",
+ 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val)));
return 0;
}
@@ -212,6 +234,8 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
+ i3c_hci_sync_irq_inactive(hci);
+
if (pio) {
dev_dbg(&hci->master.dev, "status = %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
@@ -219,8 +243,6 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
BUG_ON(pio->curr_rx);
BUG_ON(pio->curr_tx);
BUG_ON(pio->curr_resp);
- kfree(pio);
- hci->io_data = NULL;
}
}
@@ -1049,4 +1071,6 @@ const struct hci_io_ops mipi_i3c_hci_pio = {
.request_ibi = hci_pio_request_ibi,
.free_ibi = hci_pio_free_ibi,
.recycle_ibi_slot = hci_pio_recycle_ibi_slot,
+ .suspend = hci_pio_suspend,
+ .resume = hci_pio_resume,
};
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 426a418f29b6..528bccc2c68e 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -198,6 +198,8 @@
#define RENESAS_I3C_MAX_DEVS 8
#define I2C_INIT_MSG -1
+#define RENESAS_I3C_TCLK_IDX 1
+
enum i3c_internal_state {
I3C_INTERNAL_STATE_DISABLED,
I3C_INTERNAL_STATE_CONTROLLER_IDLE,
@@ -254,12 +256,19 @@ struct renesas_i3c {
enum i3c_internal_state internal_state;
u16 maxdevs;
u32 free_pos;
+ u32 dyn_addr;
u32 i2c_STDBR;
u32 i3c_STDBR;
+ unsigned long rate;
u8 addrs[RENESAS_I3C_MAX_DEVS];
struct renesas_i3c_xferqueue xferqueue;
void __iomem *regs;
- struct clk *tclk;
+ u32 *DATBASn;
+ struct clk_bulk_data *clks;
+ struct reset_control *presetn;
+ struct reset_control *tresetn;
+ u8 num_clks;
+ u8 refclk_div;
};
struct renesas_i3c_i2c_dev_data {
@@ -272,10 +281,6 @@ struct renesas_i3c_irq_desc {
const char *desc;
};
-struct renesas_i3c_config {
- unsigned int has_pclkrw:1;
-};
-
static inline void renesas_i3c_reg_update(void __iomem *reg, u32 mask, u32 val)
{
u32 data = readl(reg);
@@ -477,28 +482,79 @@ static int renesas_i3c_reset(struct renesas_i3c *i3c)
0, 1000, false, i3c->regs, RSTCTL);
}
+static void renesas_i3c_hw_init(struct renesas_i3c *i3c)
+{
+ u32 val;
+
+ /* Disable Slave Mode */
+ renesas_writel(i3c->regs, SVCTL, 0);
+
+ /* Initialize Queue/Buffer threshold */
+ renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) |
+ NQTHCTL_CMDQTH(1));
+
+ /* The only supported configuration is two entries*/
+ renesas_writel(i3c->regs, NTBTHCTL0, 0);
+ /* Interrupt when there is one entry in the queue */
+ renesas_writel(i3c->regs, NRQTHCTL, 0);
+
+ /* Enable all Bus/Transfer Status Flags */
+ renesas_writel(i3c->regs, BSTE, BSTE_ALL_FLAG);
+ renesas_writel(i3c->regs, NTSTE, NTSTE_ALL_FLAG);
+
+ /* Interrupt enable settings */
+ renesas_writel(i3c->regs, BIE, BIE_NACKDIE | BIE_TENDIE);
+ renesas_writel(i3c->regs, NTIE, 0);
+
+ /* Clear Status register */
+ renesas_writel(i3c->regs, NTST, 0);
+ renesas_writel(i3c->regs, INST, 0);
+ renesas_writel(i3c->regs, BST, 0);
+
+ /* Hot-Join Acknowlege setting. */
+ renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL);
+
+ renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL |
+ IBINCTL_NRSIRCTL);
+
+ renesas_writel(i3c->regs, SCSTLCTL, 0);
+ renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_ACKTWE);
+
+ /* Bus condition timing */
+ val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS,
+ NSEC_PER_SEC / i3c->rate);
+ renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val));
+
+ val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS,
+ NSEC_PER_SEC / i3c->rate);
+ renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val));
+
+ val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS,
+ NSEC_PER_SEC / i3c->rate);
+ renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val));
+}
+
static int renesas_i3c_bus_init(struct i3c_master_controller *m)
{
struct renesas_i3c *i3c = to_renesas_i3c(m);
struct i3c_bus *bus = i3c_master_get_bus(m);
struct i3c_device_info info = {};
struct i2c_timings t;
- unsigned long rate;
- u32 double_SBR, val;
+ u32 double_SBR;
int cks, pp_high_ticks, pp_low_ticks, i3c_total_ticks;
int od_high_ticks, od_low_ticks, i2c_total_ticks;
int ret;
- rate = clk_get_rate(i3c->tclk);
- if (!rate)
+ i3c->rate = clk_get_rate(i3c->clks[RENESAS_I3C_TCLK_IDX].clk);
+ if (!i3c->rate)
return -EINVAL;
ret = renesas_i3c_reset(i3c);
if (ret)
return ret;
- i2c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i2c);
- i3c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i3c);
+ i2c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i2c);
+ i3c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i3c);
i2c_parse_fw_timings(&m->dev, &t, true);
@@ -511,7 +567,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
pp_high_ticks = ((i3c_total_ticks * 5) / 10);
else
pp_high_ticks = DIV_ROUND_UP(I3C_BUS_THIGH_MIXED_MAX_NS,
- NSEC_PER_SEC / rate);
+ NSEC_PER_SEC / i3c->rate);
pp_low_ticks = i3c_total_ticks - pp_high_ticks;
if ((od_low_ticks / 2) <= 0xFF && pp_low_ticks < 0x3F)
@@ -519,7 +575,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
i2c_total_ticks /= 2;
i3c_total_ticks /= 2;
- rate /= 2;
+ i3c->rate /= 2;
}
/* SCL clock period calculation in Open-drain mode */
@@ -540,8 +596,8 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
STDBR_SBRLP(pp_low_ticks) |
STDBR_SBRHP(pp_high_ticks);
- od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / rate) + 1;
- od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / rate) + 1;
+ od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / i3c->rate) + 1;
+ od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / i3c->rate) + 1;
i3c->i2c_STDBR = (double_SBR ? STDBR_DSBRPO : 0) |
STDBR_SBRLO(double_SBR, od_low_ticks) |
STDBR_SBRHO(double_SBR, od_high_ticks) |
@@ -556,55 +612,16 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m)
EXTBR_EBRHP(pp_high_ticks));
renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
+ i3c->refclk_div = cks;
- /* Disable Slave Mode */
- renesas_writel(i3c->regs, SVCTL, 0);
-
- /* Initialize Queue/Buffer threshold */
- renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) |
- NQTHCTL_CMDQTH(1));
-
- /* The only supported configuration is two entries*/
- renesas_writel(i3c->regs, NTBTHCTL0, 0);
- /* Interrupt when there is one entry in the queue */
- renesas_writel(i3c->regs, NRQTHCTL, 0);
-
- /* Enable all Bus/Transfer Status Flags */
- renesas_writel(i3c->regs, BSTE, BSTE_ALL_FLAG);
- renesas_writel(i3c->regs, NTSTE, NTSTE_ALL_FLAG);
-
- /* Interrupt enable settings */
- renesas_writel(i3c->regs, BIE, BIE_NACKDIE | BIE_TENDIE);
- renesas_writel(i3c->regs, NTIE, 0);
-
- /* Clear Status register */
- renesas_writel(i3c->regs, NTST, 0);
- renesas_writel(i3c->regs, INST, 0);
- renesas_writel(i3c->regs, BST, 0);
-
- /* Hot-Join Acknowlege setting. */
- renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL);
-
- renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL |
- IBINCTL_NRSIRCTL);
-
- renesas_writel(i3c->regs, SCSTLCTL, 0);
- renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_ACKTWE);
-
- /* Bus condition timing */
- val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS, NSEC_PER_SEC / rate);
- renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val));
-
- val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS, NSEC_PER_SEC / rate);
- renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val));
-
- val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS, NSEC_PER_SEC / rate);
- renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val));
+ /* I3C hw init*/
+ renesas_i3c_hw_init(i3c);
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
+ i3c->dyn_addr = ret;
renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV);
memset(&info, 0, sizeof(info));
@@ -1301,14 +1318,8 @@ static const struct renesas_i3c_irq_desc renesas_i3c_irqs[] = {
static int renesas_i3c_probe(struct platform_device *pdev)
{
struct renesas_i3c *i3c;
- struct reset_control *reset;
- struct clk *clk;
- const struct renesas_i3c_config *config = of_device_get_match_data(&pdev->dev);
int ret, i;
- if (!config)
- return -ENODATA;
-
i3c = devm_kzalloc(&pdev->dev, sizeof(*i3c), GFP_KERNEL);
if (!i3c)
return -ENOMEM;
@@ -1317,28 +1328,21 @@ static int renesas_i3c_probe(struct platform_device *pdev)
if (IS_ERR(i3c->regs))
return PTR_ERR(i3c->regs);
- clk = devm_clk_get_enabled(&pdev->dev, "pclk");
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &i3c->clks);
+ if (ret <= RENESAS_I3C_TCLK_IDX)
+ return dev_err_probe(&pdev->dev, ret < 0 ? ret : -EINVAL,
+ "Failed to get clocks (need > %d, got %d)\n",
+ RENESAS_I3C_TCLK_IDX, ret);
+ i3c->num_clks = ret;
- if (config->has_pclkrw) {
- clk = devm_clk_get_enabled(&pdev->dev, "pclkrw");
- if (IS_ERR(clk))
- return PTR_ERR(clk);
- }
-
- i3c->tclk = devm_clk_get_enabled(&pdev->dev, "tclk");
- if (IS_ERR(i3c->tclk))
- return PTR_ERR(i3c->tclk);
-
- reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn");
- if (IS_ERR(reset))
- return dev_err_probe(&pdev->dev, PTR_ERR(reset),
+ i3c->tresetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn");
+ if (IS_ERR(i3c->tresetn))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i3c->tresetn),
"Error: missing tresetn ctrl\n");
- reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn");
- if (IS_ERR(reset))
- return dev_err_probe(&pdev->dev, PTR_ERR(reset),
+ i3c->presetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn");
+ if (IS_ERR(i3c->presetn))
+ return dev_err_probe(&pdev->dev, PTR_ERR(i3c->presetn),
"Error: missing presetn ctrl\n");
spin_lock_init(&i3c->xferqueue.lock);
@@ -1364,6 +1368,12 @@ static int renesas_i3c_probe(struct platform_device *pdev)
i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
+ /* Allocate dynamic Device Address Table backup. */
+ i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs,
+ GFP_KERNEL);
+ if (!i3c->DATBASn)
+ return -ENOMEM;
+
return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
}
@@ -1374,16 +1384,86 @@ static void renesas_i3c_remove(struct platform_device *pdev)
i3c_master_unregister(&i3c->base);
}
-static const struct renesas_i3c_config empty_i3c_config = {
-};
+static int renesas_i3c_suspend_noirq(struct device *dev)
+{
+ struct renesas_i3c *i3c = dev_get_drvdata(dev);
+ int i, ret;
+
+ i2c_mark_adapter_suspended(&i3c->base.i2c);
+
+ /* Store Device Address Table values. */
+ for (i = 0; i < i3c->maxdevs; i++)
+ i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i));
+
+ ret = reset_control_assert(i3c->presetn);
+ if (ret)
+ goto err_mark_resumed;
+
+ ret = reset_control_assert(i3c->tresetn);
+ if (ret)
+ goto err_presetn;
+
+ clk_bulk_disable(i3c->num_clks, i3c->clks);
+
+ return 0;
+
+err_presetn:
+ reset_control_deassert(i3c->presetn);
+err_mark_resumed:
+ i2c_mark_adapter_resumed(&i3c->base.i2c);
+
+ return ret;
+}
+
+static int renesas_i3c_resume_noirq(struct device *dev)
+{
+ struct renesas_i3c *i3c = dev_get_drvdata(dev);
+ int i, ret;
+
+ ret = reset_control_deassert(i3c->presetn);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(i3c->tresetn);
+ if (ret)
+ goto err_presetn;
+
+ ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
+ if (ret)
+ goto err_tresetn;
+
+ /* Re-store I3C registers value. */
+ renesas_writel(i3c->regs, REFCKCTL,
+ REFCKCTL_IREFCKS(i3c->refclk_div));
+ renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
+ MSDVAD_MDYAD(i3c->dyn_addr));
+
+ /* Restore Device Address Table values. */
+ for (i = 0; i < i3c->maxdevs; i++)
+ renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]);
+
+ /* I3C hw init. */
+ renesas_i3c_hw_init(i3c);
+
+ i2c_mark_adapter_resumed(&i3c->base.i2c);
+
+ return 0;
+
+err_tresetn:
+ reset_control_assert(i3c->tresetn);
+err_presetn:
+ reset_control_assert(i3c->presetn);
+ return ret;
+}
-static const struct renesas_i3c_config r9a09g047_i3c_config = {
- .has_pclkrw = 1,
+static const struct dev_pm_ops renesas_i3c_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
+ renesas_i3c_resume_noirq)
};
static const struct of_device_id renesas_i3c_of_ids[] = {
- { .compatible = "renesas,r9a08g045-i3c", .data = &empty_i3c_config },
- { .compatible = "renesas,r9a09g047-i3c", .data = &r9a09g047_i3c_config },
+ { .compatible = "renesas,r9a08g045-i3c" },
+ { .compatible = "renesas,r9a09g047-i3c" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, renesas_i3c_of_ids);
@@ -1394,6 +1474,7 @@ static struct platform_driver renesas_i3c = {
.driver = {
.name = "renesas-i3c",
.of_match_table = renesas_i3c_of_ids,
+ .pm = pm_sleep_ptr(&renesas_i3c_pm_ops),
},
};
module_platform_driver(renesas_i3c);
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index a62f22ff8b57..857504d36e18 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -533,8 +533,8 @@ static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 msta
static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
{
struct svc_i3c_i2c_dev_data *data;
+ struct i3c_dev_desc *dev = NULL;
unsigned int ibitype, ibiaddr;
- struct i3c_dev_desc *dev;
u32 status, val;
int ret;
@@ -627,7 +627,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
* for the slave to interrupt again.
*/
if (svc_i3c_master_error(master)) {
- if (master->ibi.tbq_slot) {
+ if (master->ibi.tbq_slot && dev) {
data = i3c_dev_get_master_data(dev);
i3c_generic_ibi_recycle_slot(data->ibi_pool,
master->ibi.tbq_slot);
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 9fcb6410a584..971d53349b6f 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -25,7 +25,7 @@
* @I3C_ERROR_M2: M2 error
*
* These are the standard error codes as defined by the I3C specification.
- * When -EIO is returned by the i3c_device_do_priv_xfers() or
+ * When -EIO is returned by the i3c_device_do_i3c_xfers() or
* i3c_device_send_hdr_cmds() one can check the error code in
* &struct_i3c_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
* what went wrong.
@@ -79,9 +79,6 @@ struct i3c_xfer {
enum i3c_error_code err;
};
-/* keep back compatible */
-#define i3c_priv_xfer i3c_xfer
-
/**
* enum i3c_dcr - I3C DCR values
* @I3C_DCR_GENERIC_DEVICE: generic I3C device
@@ -308,15 +305,23 @@ static __always_inline void i3c_i2c_driver_unregister(struct i3c_driver *i3cdrv,
i3c_i2c_driver_unregister, \
__i2cdrv)
+#if IS_ENABLED(CONFIG_I3C)
int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
int nxfers, enum i3c_xfer_mode mode);
+u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
+#else
+static inline int
+i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode)
+{
+ return -EOPNOTSUPP;
+}
-static inline int i3c_device_do_priv_xfers(struct i3c_device *dev,
- struct i3c_xfer *xfers,
- int nxfers)
+static inline u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
{
- return i3c_device_do_xfers(dev, xfers, nxfers, I3C_SDR);
+ return 0;
}
+#endif
int i3c_device_do_setdasa(struct i3c_device *dev);
@@ -358,6 +363,5 @@ int i3c_device_request_ibi(struct i3c_device *dev,
void i3c_device_free_ibi(struct i3c_device *dev);
int i3c_device_enable_ibi(struct i3c_device *dev);
int i3c_device_disable_ibi(struct i3c_device *dev);
-u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
#endif /* I3C_DEV_H */
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 58d01ed4cce7..592b646f6134 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -462,6 +462,8 @@ struct i3c_bus {
* @enable_hotjoin: enable hot join event detect.
* @disable_hotjoin: disable hot join event detect.
* @set_speed: adjust I3C open drain mode timing.
+ * @set_dev_nack_retry: configure device NACK retry count for the master
+ * controller.
*/
struct i3c_master_controller_ops {
int (*bus_init)(struct i3c_master_controller *master);
@@ -491,6 +493,8 @@ struct i3c_master_controller_ops {
int (*enable_hotjoin)(struct i3c_master_controller *master);
int (*disable_hotjoin)(struct i3c_master_controller *master);
int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed);
+ int (*set_dev_nack_retry)(struct i3c_master_controller *master,
+ unsigned long dev_nack_retry_cnt);
};
/**
@@ -505,6 +509,8 @@ struct i3c_master_controller_ops {
* @secondary: true if the master is a secondary master
* @init_done: true when the bus initialization is done
* @hotjoin: true if the master support hotjoin
+ * @rpm_allowed: true if Runtime PM allowed
+ * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended
* @boardinfo.i3c: list of I3C boardinfo objects
* @boardinfo.i2c: list of I2C boardinfo objects
* @boardinfo: board-level information attached to devices connected on the bus
@@ -514,6 +520,7 @@ struct i3c_master_controller_ops {
* in a thread context. Typical examples are Hot Join processing which
* requires taking the bus lock in maintenance, which in turn, can only
* be done from a sleep-able context
+ * @dev_nack_retry_count: retry count when slave device nack
*
* A &struct i3c_master_controller has to be registered to the I3C subsystem
* through i3c_master_register(). None of &struct i3c_master_controller fields
@@ -528,12 +535,15 @@ struct i3c_master_controller {
unsigned int secondary : 1;
unsigned int init_done : 1;
unsigned int hotjoin: 1;
+ unsigned int rpm_allowed: 1;
+ unsigned int rpm_ibi_allowed: 1;
struct {
struct list_head i3c;
struct list_head i2c;
} boardinfo;
struct i3c_bus bus;
struct workqueue_struct *wq;
+ unsigned int dev_nack_retry_count;
};
/**
@@ -595,6 +605,7 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master,
int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
u8 addr);
int i3c_master_do_daa(struct i3c_master_controller *master);
+int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa);
struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
size_t len, bool force_bounce,
enum dma_data_direction dir);
diff --git a/include/linux/platform_data/mipi-i3c-hci.h b/include/linux/platform_data/mipi-i3c-hci.h
new file mode 100644
index 000000000000..ab7395f455f9
--- /dev/null
+++ b/include/linux/platform_data/mipi-i3c-hci.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H
+#define INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H
+
+#include <linux/compiler_types.h>
+
+/**
+ * struct mipi_i3c_hci_platform_data - Platform-dependent data for mipi_i3c_hci
+ * @base_regs: Register set base address (to support multi-bus instances)
+ */
+struct mipi_i3c_hci_platform_data {
+ void __iomem *base_regs;
+};
+
+#endif