summaryrefslogtreecommitdiff
path: root/drivers/i3c/master.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i3c/master.c')
-rw-r--r--drivers/i3c/master.c496
1 files changed, 405 insertions, 91 deletions
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index d7e6f6c99aea..f88f7e19203a 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -8,11 +8,13 @@
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/device.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -21,6 +23,8 @@
static DEFINE_IDR(i3c_bus_idr);
static DEFINE_MUTEX(i3c_core_lock);
+static int __i3c_first_dynamic_bus_num;
+static BLOCKING_NOTIFIER_HEAD(i3c_bus_notifier);
/**
* i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
@@ -138,7 +142,7 @@ static ssize_t bcr_show(struct device *dev,
i3c_bus_normaluse_lock(bus);
desc = dev_to_i3cdesc(dev);
- ret = sprintf(buf, "%x\n", desc->info.bcr);
+ ret = sprintf(buf, "0x%02x\n", desc->info.bcr);
i3c_bus_normaluse_unlock(bus);
return ret;
@@ -155,7 +159,7 @@ static ssize_t dcr_show(struct device *dev,
i3c_bus_normaluse_lock(bus);
desc = dev_to_i3cdesc(dev);
- ret = sprintf(buf, "%x\n", desc->info.dcr);
+ ret = sprintf(buf, "0x%02x\n", desc->info.dcr);
i3c_bus_normaluse_unlock(bus);
return ret;
@@ -273,13 +277,14 @@ static struct attribute *i3c_device_attrs[] = {
};
ATTRIBUTE_GROUPS(i3c_device);
-static int i3c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct i3c_device *i3cdev = dev_to_i3cdev(dev);
+ const struct i3c_device *i3cdev = dev_to_i3cdev(dev);
struct i3c_device_info devinfo;
u16 manuf, part, ext;
- i3c_device_get_info(i3cdev, &devinfo);
+ if (i3cdev->desc)
+ devinfo = i3cdev->desc->info;
manuf = I3C_PID_MANUF_ID(devinfo.pid);
part = I3C_PID_PART_ID(devinfo.pid);
ext = I3C_PID_EXTRA_INFO(devinfo.pid);
@@ -298,10 +303,10 @@ static const struct device_type i3c_device_type = {
.uevent = i3c_device_uevent,
};
-static int i3c_device_match(struct device *dev, struct device_driver *drv)
+static int i3c_device_match(struct device *dev, const struct device_driver *drv)
{
struct i3c_device *i3cdev;
- struct i3c_driver *i3cdrv;
+ const struct i3c_driver *i3cdrv;
if (dev->type != &i3c_device_type)
return 0;
@@ -329,22 +334,21 @@ static void i3c_device_remove(struct device *dev)
if (driver->remove)
driver->remove(i3cdev);
-
- i3c_device_free_ibi(i3cdev);
}
-struct bus_type i3c_bus_type = {
+const struct bus_type i3c_bus_type = {
.name = "i3c",
.match = i3c_device_match,
.probe = i3c_device_probe,
.remove = i3c_device_remove,
};
+EXPORT_SYMBOL_GPL(i3c_bus_type);
static enum i3c_addr_slot_status
-i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
+i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask)
{
unsigned long status;
- int bitpos = addr * 2;
+ int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS;
if (addr > I2C_MAX_ADDR)
return I3C_ADDR_SLOT_RSVD;
@@ -352,22 +356,33 @@ i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
status = bus->addrslots[bitpos / BITS_PER_LONG];
status >>= bitpos % BITS_PER_LONG;
- return status & I3C_ADDR_SLOT_STATUS_MASK;
+ return status & mask;
}
-static void i3c_bus_set_addr_slot_status(struct i3c_bus *bus, u16 addr,
- enum i3c_addr_slot_status status)
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
+{
+ return i3c_bus_get_addr_slot_status_mask(bus, addr, I3C_ADDR_SLOT_STATUS_MASK);
+}
+
+static void i3c_bus_set_addr_slot_status_mask(struct i3c_bus *bus, u16 addr,
+ enum i3c_addr_slot_status status, u32 mask)
{
- int bitpos = addr * 2;
+ int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS;
unsigned long *ptr;
if (addr > I2C_MAX_ADDR)
return;
ptr = bus->addrslots + (bitpos / BITS_PER_LONG);
- *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
- (bitpos % BITS_PER_LONG));
- *ptr |= (unsigned long)status << (bitpos % BITS_PER_LONG);
+ *ptr &= ~((unsigned long)mask << (bitpos % BITS_PER_LONG));
+ *ptr |= ((unsigned long)status & mask) << (bitpos % BITS_PER_LONG);
+}
+
+static void i3c_bus_set_addr_slot_status(struct i3c_bus *bus, u16 addr,
+ enum i3c_addr_slot_status status)
+{
+ i3c_bus_set_addr_slot_status_mask(bus, addr, status, I3C_ADDR_SLOT_STATUS_MASK);
}
static bool i3c_bus_dev_addr_is_avail(struct i3c_bus *bus, u8 addr)
@@ -379,13 +394,44 @@ static bool i3c_bus_dev_addr_is_avail(struct i3c_bus *bus, u8 addr)
return status == I3C_ADDR_SLOT_FREE;
}
+/*
+ * ┌────┬─────────────┬───┬─────────┬───┐
+ * │S/Sr│ 7'h7E RnW=0 │ACK│ ENTDAA │ T ├────┐
+ * └────┴─────────────┴───┴─────────┴───┘ │
+ * ┌─────────────────────────────────────────┘
+ * │ ┌──┬─────────────┬───┬─────────────────┬────────────────┬───┬─────────┐
+ * └─►│Sr│7'h7E RnW=1 │ACK│48bit UID BCR DCR│Assign 7bit Addr│PAR│ ACK/NACK│
+ * └──┴─────────────┴───┴─────────────────┴────────────────┴───┴─────────┘
+ * Some master controllers (such as HCI) need to prepare the entire above transaction before
+ * sending it out to the I3C bus. This means that a 7-bit dynamic address needs to be allocated
+ * before knowing the target device's UID information.
+ *
+ * However, some I3C targets may request specific addresses (called as "init_dyn_addr"), which is
+ * typically specified by the DT-'s assigned-address property. Lower addresses having higher IBI
+ * priority. If it is available, i3c_bus_get_free_addr() preferably return a free address that is
+ * not in the list of desired addresses (called as "init_dyn_addr"). This allows the device with
+ * the "init_dyn_addr" to switch to its "init_dyn_addr" when it hot-joins the I3C bus. Otherwise,
+ * if the "init_dyn_addr" is already in use by another I3C device, the target device will not be
+ * able to switch to its desired address.
+ *
+ * If the previous step fails, fallback returning one of the remaining unassigned address,
+ * regardless of its state in the desired list.
+ */
static int i3c_bus_get_free_addr(struct i3c_bus *bus, u8 start_addr)
{
enum i3c_addr_slot_status status;
u8 addr;
for (addr = start_addr; addr < I3C_MAX_ADDR; addr++) {
- status = i3c_bus_get_addr_slot_status(bus, addr);
+ status = i3c_bus_get_addr_slot_status_mask(bus, addr,
+ I3C_ADDR_SLOT_EXT_STATUS_MASK);
+ if (status == I3C_ADDR_SLOT_FREE)
+ return addr;
+ }
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++) {
+ status = i3c_bus_get_addr_slot_status_mask(bus, addr,
+ I3C_ADDR_SLOT_STATUS_MASK);
if (status == I3C_ADDR_SLOT_FREE)
return addr;
}
@@ -419,9 +465,9 @@ static void i3c_bus_cleanup(struct i3c_bus *i3cbus)
mutex_unlock(&i3c_core_lock);
}
-static int i3c_bus_init(struct i3c_bus *i3cbus)
+static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np)
{
- int ret;
+ int ret, start, end, id = -1;
init_rwsem(&i3cbus->lock);
INIT_LIST_HEAD(&i3cbus->devs.i2c);
@@ -429,8 +475,19 @@ static int i3c_bus_init(struct i3c_bus *i3cbus)
i3c_bus_init_addrslots(i3cbus);
i3cbus->mode = I3C_BUS_MODE_PURE;
+ if (np)
+ id = of_alias_get_id(np, "i3c");
+
mutex_lock(&i3c_core_lock);
- ret = idr_alloc(&i3c_bus_idr, i3cbus, 0, 0, GFP_KERNEL);
+ if (id >= 0) {
+ start = id;
+ end = start + 1;
+ } else {
+ start = __i3c_first_dynamic_bus_num;
+ end = 0;
+ }
+
+ ret = idr_alloc(&i3c_bus_idr, i3cbus, start, end, GFP_KERNEL);
mutex_unlock(&i3c_core_lock);
if (ret < 0)
@@ -441,6 +498,36 @@ static int i3c_bus_init(struct i3c_bus *i3cbus)
return 0;
}
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+ void *data)
+{
+ struct i3c_bus *bus;
+ int id;
+
+ mutex_lock(&i3c_core_lock);
+ idr_for_each_entry(&i3c_bus_idr, bus, id)
+ fn(bus, data);
+ mutex_unlock(&i3c_core_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_for_each_bus_locked);
+
+int i3c_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_register_notifier);
+
+int i3c_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_unregister_notifier);
+
+static void i3c_bus_notify(struct i3c_bus *bus, unsigned int action)
+{
+ blocking_notifier_call_chain(&i3c_bus_notifier, action, bus);
+}
+
static const char * const i3c_bus_mode_strings[] = {
[I3C_BUS_MODE_PURE] = "pure",
[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
@@ -514,6 +601,88 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
}
static DEVICE_ATTR_RO(i2c_scl_frequency);
+static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
+{
+ int ret;
+
+ if (!master || !master->ops)
+ return -EINVAL;
+
+ if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin)
+ return -EINVAL;
+
+ i3c_bus_normaluse_lock(&master->bus);
+
+ if (enable)
+ ret = master->ops->enable_hotjoin(master);
+ else
+ ret = master->ops->disable_hotjoin(master);
+
+ master->hotjoin = enable;
+
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret;
+}
+
+static ssize_t hotjoin_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
+ int ret;
+ bool res;
+
+ if (!i3cbus->cur_master)
+ return -EINVAL;
+
+ if (kstrtobool(buf, &res))
+ return -EINVAL;
+
+ ret = i3c_set_hotjoin(i3cbus->cur_master->common.master, res);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+/*
+ * i3c_master_enable_hotjoin - Enable hotjoin
+ * @master: I3C master object
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_enable_hotjoin(struct i3c_master_controller *master)
+{
+ return i3c_set_hotjoin(master, true);
+}
+EXPORT_SYMBOL_GPL(i3c_master_enable_hotjoin);
+
+/*
+ * i3c_master_disable_hotjoin - Disable hotjoin
+ * @master: I3C master object
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_disable_hotjoin(struct i3c_master_controller *master)
+{
+ return i3c_set_hotjoin(master, false);
+}
+EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin);
+
+static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
+ ssize_t ret;
+
+ i3c_bus_normaluse_lock(i3cbus);
+ ret = sysfs_emit(buf, "%d\n", i3cbus->cur_master->common.master->hotjoin);
+ i3c_bus_normaluse_unlock(i3cbus);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(hotjoin);
+
static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_current_master.attr,
@@ -524,6 +693,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_pid.attr,
&dev_attr_dynamic_address.attr,
&dev_attr_hdrcap.attr,
+ &dev_attr_hotjoin.attr,
NULL,
};
ATTRIBUTE_GROUPS(i3c_masterdev);
@@ -556,12 +726,12 @@ static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
switch (i3cbus->mode) {
case I3C_BUS_MODE_PURE:
if (!i3cbus->scl_rate.i3c)
- i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ i3cbus->scl_rate.i3c = I3C_BUS_I3C_SCL_TYP_RATE;
break;
case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED:
if (!i3cbus->scl_rate.i3c)
- i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ i3cbus->scl_rate.i3c = I3C_BUS_I3C_SCL_TYP_RATE;
if (!i3cbus->scl_rate.i2c)
i3cbus->scl_rate.i2c = max_i2c_scl_rate;
break;
@@ -583,8 +753,8 @@ static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
* I3C/I2C frequency may have been overridden, check that user-provided
* values are not exceeding max possible frequency.
*/
- if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
- i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ if (i3cbus->scl_rate.i3c > I3C_BUS_I3C_SCL_MAX_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE)
return -EINVAL;
return 0;
@@ -666,14 +836,14 @@ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master,
return -EINVAL;
if (!master->ops->send_ccc_cmd)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if ((cmd->id & I3C_CCC_DIRECT) && (!cmd->dests || !cmd->ndests))
return -EINVAL;
if (master->ops->supports_ccc_cmd &&
!master->ops->supports_ccc_cmd(master, cmd))
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
ret = master->ops->send_ccc_cmd(master, cmd);
if (ret) {
@@ -1087,8 +1257,16 @@ static int i3c_master_getmxds_locked(struct i3c_master_controller *master,
i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
- if (ret)
- goto out;
+ if (ret) {
+ /*
+ * Retry when the device does not support max read turnaround
+ * while expecting shorter length from this CCC command.
+ */
+ dest.payload.len -= 3;
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ goto out;
+ }
if (dest.payload.len != 2 && dest.payload.len != 5) {
ret = -EIO;
@@ -1260,7 +1438,7 @@ static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
if (dev->info.bcr & I3C_BCR_HDR_CAP) {
ret = i3c_master_gethdrcap_locked(master, &dev->info);
- if (ret)
+ if (ret && ret != -EOPNOTSUPP)
return ret;
}
@@ -1281,7 +1459,7 @@ static void i3c_master_put_i3c_addrs(struct i3c_dev_desc *dev)
I3C_ADDR_SLOT_FREE);
if (dev->boardinfo && dev->boardinfo->init_dyn_addr)
- i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ i3c_bus_set_addr_slot_status(&master->bus, dev->boardinfo->init_dyn_addr,
I3C_ADDR_SLOT_FREE);
}
@@ -1296,7 +1474,11 @@ static int i3c_master_get_i3c_addrs(struct i3c_dev_desc *dev)
if (dev->info.static_addr) {
status = i3c_bus_get_addr_slot_status(&master->bus,
dev->info.static_addr);
- if (status != I3C_ADDR_SLOT_FREE)
+ /* Since static address and assigned dynamic address can be
+ * equal, allow this case to pass.
+ */
+ if (status != I3C_ADDR_SLOT_FREE &&
+ dev->info.static_addr != dev->boardinfo->init_dyn_addr)
return -EBUSY;
i3c_bus_set_addr_slot_status(&master->bus,
@@ -1366,16 +1548,9 @@ static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
u8 old_dyn_addr)
{
struct i3c_master_controller *master = i3c_dev_get_master(dev);
- enum i3c_addr_slot_status status;
int ret;
- if (dev->info.dyn_addr != old_dyn_addr &&
- (!dev->boardinfo ||
- dev->info.dyn_addr != dev->boardinfo->init_dyn_addr)) {
- status = i3c_bus_get_addr_slot_status(&master->bus,
- dev->info.dyn_addr);
- if (status != I3C_ADDR_SLOT_FREE)
- return -EBUSY;
+ if (dev->info.dyn_addr != old_dyn_addr) {
i3c_bus_set_addr_slot_status(&master->bus,
dev->info.dyn_addr,
I3C_ADDR_SLOT_I3C_DEV);
@@ -1438,6 +1613,7 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master,
{
struct i3c_device_info info = {
.static_addr = boardinfo->static_addr,
+ .pid = boardinfo->pid,
};
struct i3c_dev_desc *i3cdev;
int ret;
@@ -1508,9 +1684,11 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
desc->dev->dev.of_node = desc->boardinfo->of_node;
ret = device_register(&desc->dev->dev);
- if (ret)
+ if (ret) {
dev_err(&master->dev,
"Failed to add I3C device (err = %d)\n", ret);
+ put_device(&desc->dev->dev);
+ }
}
}
@@ -1549,6 +1727,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
EXPORT_SYMBOL_GPL(i3c_master_do_daa);
/**
+ * i3c_master_dma_map_single() - Map buffer for single DMA transfer
+ * @dev: device object of a device doing DMA
+ * @buf: destination/source buffer for DMA
+ * @len: length of transfer
+ * @force_bounce: true, force to use a bounce buffer,
+ * false, function will auto check is a bounce buffer required
+ * @dir: DMA direction
+ *
+ * Map buffer for a DMA transfer and allocate a bounce buffer if required.
+ *
+ * Return: I3C DMA transfer descriptor or NULL in case of error.
+ */
+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf,
+ size_t len, bool force_bounce, enum dma_data_direction dir)
+{
+ struct i3c_dma *dma_xfer __free(kfree) = NULL;
+ void *bounce __free(kfree) = NULL;
+ void *dma_buf = buf;
+
+ dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL);
+ if (!dma_xfer)
+ return NULL;
+
+ dma_xfer->dev = dev;
+ dma_xfer->buf = buf;
+ dma_xfer->dir = dir;
+ dma_xfer->len = len;
+ dma_xfer->map_len = len;
+
+ if (is_vmalloc_addr(buf))
+ force_bounce = true;
+
+ if (force_bounce) {
+ dma_xfer->map_len = ALIGN(len, cache_line_size());
+ if (dir == DMA_FROM_DEVICE)
+ bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL);
+ else
+ bounce = kmemdup(buf, dma_xfer->map_len, GFP_KERNEL);
+ if (!bounce)
+ return NULL;
+ dma_buf = bounce;
+ }
+
+ dma_xfer->addr = dma_map_single(dev, dma_buf, dma_xfer->map_len, dir);
+ if (dma_mapping_error(dev, dma_xfer->addr))
+ return NULL;
+
+ dma_xfer->bounce_buf = no_free_ptr(bounce);
+ return no_free_ptr(dma_xfer);
+}
+EXPORT_SYMBOL_GPL(i3c_master_dma_map_single);
+
+/**
+ * i3c_master_dma_unmap_single() - Unmap buffer after DMA
+ * @dma_xfer: DMA transfer and mapping descriptor
+ *
+ * Unmap buffer and cleanup DMA transfer descriptor.
+ */
+void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer)
+{
+ dma_unmap_single(dma_xfer->dev, dma_xfer->addr,
+ dma_xfer->map_len, dma_xfer->dir);
+ if (dma_xfer->bounce_buf) {
+ if (dma_xfer->dir == DMA_FROM_DEVICE)
+ memcpy(dma_xfer->buf, dma_xfer->bounce_buf,
+ dma_xfer->len);
+ kfree(dma_xfer->bounce_buf);
+ }
+ kfree(dma_xfer);
+}
+EXPORT_SYMBOL_GPL(i3c_master_dma_unmap_single);
+
+/**
* i3c_master_set_info() - set master device information
* @master: master used to send frames on the bus
* @info: I3C device information
@@ -1725,6 +1976,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
goto err_bus_cleanup;
}
+ if (master->ops->set_speed) {
+ ret = master->ops->set_speed(master, I3C_OPEN_DRAIN_SLOW_SPEED);
+ if (ret)
+ goto err_bus_cleanup;
+ }
+
/*
* Reset all dynamic address that may have been assigned before
* (assigned by the bootloader for example).
@@ -1733,6 +1990,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
if (ret && ret != I3C_ERROR_M2)
goto err_bus_cleanup;
+ if (master->ops->set_speed) {
+ ret = master->ops->set_speed(master, I3C_OPEN_DRAIN_NORMAL_SPEED);
+ if (ret)
+ goto err_bus_cleanup;
+ }
+
/* Disable all slave events before starting DAA. */
ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
@@ -1763,9 +2026,11 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
goto err_rstdaa;
}
- i3c_bus_set_addr_slot_status(&master->bus,
- i3cboardinfo->init_dyn_addr,
- I3C_ADDR_SLOT_I3C_DEV);
+ /* Do not mark as occupied until real device exist in bus */
+ i3c_bus_set_addr_slot_status_mask(&master->bus,
+ i3cboardinfo->init_dyn_addr,
+ I3C_ADDR_SLOT_EXT_DESIRED,
+ I3C_ADDR_SLOT_EXT_STATUS_MASK);
/*
* Only try to create/attach devices that have a static
@@ -1896,11 +2161,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
ibireq.max_payload_len = olddev->ibi->max_payload_len;
ibireq.num_slots = olddev->ibi->num_slots;
- if (olddev->ibi->enabled) {
+ if (olddev->ibi->enabled)
enable_ibi = true;
- i3c_dev_disable_ibi_locked(olddev);
- }
-
+ /*
+ * The olddev should not receive any commands on the
+ * i3c bus as it does not exist and has been assigned
+ * a new address. This will result in NACK or timeout.
+ * So, update the olddev->ibi->enabled flag to false
+ * to avoid DISEC with OldAddr.
+ */
+ olddev->ibi->enabled = false;
i3c_dev_free_ibi_locked(olddev);
}
mutex_unlock(&olddev->ibi_lock);
@@ -1928,7 +2198,8 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
else
expected_dyn_addr = newdev->info.dyn_addr;
- if (newdev->info.dyn_addr != expected_dyn_addr) {
+ if (newdev->info.dyn_addr != expected_dyn_addr &&
+ i3c_bus_get_addr_slot_status(&master->bus, expected_dyn_addr) == I3C_ADDR_SLOT_FREE) {
/*
* Try to apply the expected dynamic address. If it fails, keep
* the address assigned by the master.
@@ -2011,7 +2282,7 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master,
*/
if (boardinfo->base.flags & I2C_CLIENT_TEN) {
dev_err(dev, "I2C device with 10 bit address not supported.");
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
/* LVR is encoded in reg[2]. */
@@ -2077,7 +2348,7 @@ static int of_i3c_master_add_dev(struct i3c_master_controller *master,
u32 reg[3];
int ret;
- if (!master || !node)
+ if (!master)
return -EINVAL;
ret = of_property_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg));
@@ -2141,13 +2412,13 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
return -EINVAL;
if (!master->ops->i2c_xfers)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
/* Doing transfers to different devices is not supported. */
addr = xfers[0].addr;
for (i = 1; i < nxfers; i++) {
if (addr != xfers[i].addr)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
i3c_bus_normaluse_lock(&master->bus);
@@ -2170,14 +2441,10 @@ static u8 i3c_master_i2c_get_lvr(struct i2c_client *client)
{
/* Fall back to no spike filters and FM bus mode. */
u8 lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE;
+ u32 reg[3];
- if (client->dev.of_node) {
- u32 reg[3];
-
- if (!of_property_read_u32_array(client->dev.of_node, "reg",
- reg, ARRAY_SIZE(reg)))
- lvr = reg[2];
- }
+ if (!of_property_read_u32_array(client->dev.of_node, "reg", reg, ARRAY_SIZE(reg)))
+ lvr = reg[2];
return lvr;
}
@@ -2272,6 +2539,8 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action
case BUS_NOTIFY_DEL_DEVICE:
ret = i3c_master_i2c_detach(adap, client);
break;
+ default:
+ ret = -EINVAL;
}
i3c_bus_maintenance_unlock(&master->bus);
@@ -2287,18 +2556,22 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
struct i2c_adapter *adap = i3c_master_to_i2c_adapter(master);
struct i2c_dev_desc *i2cdev;
struct i2c_dev_boardinfo *i2cboardinfo;
- int ret;
+ int ret, id;
adap->dev.parent = master->dev.parent;
adap->owner = master->dev.parent->driver->owner;
adap->algo = &i3c_master_i2c_algo;
- strncpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
-
- /* FIXME: Should we allow i3c masters to override these values? */
- adap->timeout = 1000;
+ strscpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
+ adap->timeout = HZ;
adap->retries = 3;
- ret = i2c_add_adapter(adap);
+ id = of_alias_get_id(master->dev.of_node, "i2c");
+ if (id >= 0) {
+ adap->nr = id;
+ ret = i2c_add_numbered_adapter(adap);
+ } else {
+ ret = i2c_add_adapter(adap);
+ }
if (ret)
return ret;
@@ -2354,8 +2627,11 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
*/
void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
{
+ if (!dev->ibi || !slot)
+ return;
+
atomic_inc(&dev->ibi->pending_ibis);
- queue_work(dev->common.master->wq, &slot->work);
+ queue_work(dev->ibi->wq, &slot->work);
}
EXPORT_SYMBOL_GPL(i3c_master_queue_ibi);
@@ -2543,10 +2819,14 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
{
- if (!ops || !ops->bus_init || !ops->priv_xfers ||
+ if (!ops || !ops->bus_init ||
!ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers)
return -EINVAL;
+ /* Must provide one of priv_xfers (SDR only) or i3c_xfers (all modes) */
+ if (!ops->priv_xfers && !ops->i3c_xfers)
+ return -EINVAL;
+
if (ops->request_ibi &&
(!ops->enable_ibi || !ops->disable_ibi || !ops->free_ibi ||
!ops->recycle_ibi_slot))
@@ -2562,7 +2842,7 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
* controller)
* @ops: the master controller operations
* @secondary: true if you are registering a secondary master. Will return
- * -ENOTSUPP if set to true since secondary masters are not yet
+ * -EOPNOTSUPP if set to true since secondary masters are not yet
* supported
*
* This function takes care of everything for you:
@@ -2581,7 +2861,7 @@ int i3c_master_register(struct i3c_master_controller *master,
const struct i3c_master_controller_ops *ops,
bool secondary)
{
- unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_RATE;
+ unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_MAX_RATE;
struct i3c_bus *i3cbus = i3c_master_get_bus(master);
enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
struct i2c_dev_boardinfo *i2cbi;
@@ -2589,7 +2869,7 @@ int i3c_master_register(struct i3c_master_controller *master,
/* We do not support secondary masters yet. */
if (secondary)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
ret = i3c_master_check_ops(ops);
if (ret)
@@ -2605,13 +2885,17 @@ int i3c_master_register(struct i3c_master_controller *master,
INIT_LIST_HEAD(&master->boardinfo.i2c);
INIT_LIST_HEAD(&master->boardinfo.i3c);
- ret = i3c_bus_init(i3cbus);
- 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;
+ master->dev.dma_parms = parent->dma_parms;
+
+ ret = i3c_bus_init(i3cbus, master->dev.of_node);
+ if (ret)
+ goto err_put_dev;
+
ret = of_populate_i3c_bus(master);
if (ret)
goto err_put_dev;
@@ -2636,14 +2920,14 @@ int i3c_master_register(struct i3c_master_controller *master,
}
if (i2cbi->lvr & I3C_LVR_I2C_FM_MODE)
- i2c_scl_rate = I3C_BUS_I2C_FM_SCL_RATE;
+ i2c_scl_rate = I3C_BUS_I2C_FM_SCL_MAX_RATE;
}
ret = i3c_bus_set_mode(i3cbus, mode, i2c_scl_rate);
if (ret)
goto err_put_dev;
- master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent));
+ master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent));
if (!master->wq) {
ret = -ENOMEM;
goto err_put_dev;
@@ -2665,6 +2949,12 @@ int i3c_master_register(struct i3c_master_controller *master,
if (ret)
goto err_del_dev;
+ i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
+
+ pm_runtime_no_callbacks(&master->dev);
+ pm_suspend_ignore_children(&master->dev, true);
+ pm_runtime_enable(&master->dev);
+
/*
* We're done initializing the bus and the controller, we can now
* register I3C devices discovered during the initial DAA.
@@ -2694,17 +2984,16 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
* @master: master used to send frames on the bus
*
* Basically undo everything done in i3c_master_register().
- *
- * Return: 0 in case of success, a negative error code otherwise.
*/
-int i3c_master_unregister(struct i3c_master_controller *master)
+void i3c_master_unregister(struct i3c_master_controller *master)
{
+ i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+
i3c_master_i2c_adapter_cleanup(master);
i3c_master_unregister_i3c_devs(master);
i3c_master_bus_cleanup(master);
+ pm_runtime_disable(&master->dev);
device_unregister(&master->dev);
-
- return 0;
}
EXPORT_SYMBOL_GPL(i3c_master_unregister);
@@ -2727,9 +3016,8 @@ int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev)
dev->boardinfo->init_dyn_addr);
}
-int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
- struct i3c_priv_xfer *xfers,
- int nxfers)
+int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode)
{
struct i3c_master_controller *master;
@@ -2740,8 +3028,14 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
if (!master || !xfers)
return -EINVAL;
- if (!master->ops->priv_xfers)
- return -ENOTSUPP;
+ if (mode != I3C_SDR && !(master->this->info.hdr_cap & BIT(mode)))
+ return -EOPNOTSUPP;
+
+ if (master->ops->i3c_xfers)
+ return master->ops->i3c_xfers(dev, xfers, nxfers, mode);
+
+ if (mode != I3C_SDR)
+ return -EINVAL;
return master->ops->priv_xfers(dev, xfers, nxfers);
}
@@ -2791,7 +3085,7 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
int ret;
if (!master->ops->request_ibi)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if (dev->ibi)
return -EBUSY;
@@ -2800,6 +3094,12 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
if (!ibi)
return -ENOMEM;
+ ibi->wq = alloc_ordered_workqueue(dev_name(i3cdev_to_dev(dev->dev)), WQ_MEM_RECLAIM);
+ if (!ibi->wq) {
+ kfree(ibi);
+ return -ENOMEM;
+ }
+
atomic_set(&ibi->pending_ibis, 0);
init_completion(&ibi->all_ibis_handled);
ibi->handler = req->handler;
@@ -2827,14 +3127,28 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
WARN_ON(i3c_dev_disable_ibi_locked(dev));
master->ops->free_ibi(dev);
+
+ if (dev->ibi->wq) {
+ destroy_workqueue(dev->ibi->wq);
+ dev->ibi->wq = NULL;
+ }
+
kfree(dev->ibi);
dev->ibi = NULL;
}
static int __init i3c_init(void)
{
- int res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
+ int res;
+
+ res = of_alias_get_highest_id("i3c");
+ if (res >= 0) {
+ mutex_lock(&i3c_core_lock);
+ __i3c_first_dynamic_bus_num = res + 1;
+ mutex_unlock(&i3c_core_lock);
+ }
+ res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
return res;