diff options
Diffstat (limited to 'drivers/i3c/master.c')
| -rw-r--r-- | drivers/i3c/master.c | 496 |
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; |
