// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2018 Cadence Design Systems Inc. * * Author: Boris Brezillon <boris.brezillon@bootlin.com> */ #include <dt-bindings/i3c/i3c.h>#include <linux/acpi.h>#include <linux/atomic.h>#include <linux/bitmap.h>#include <linux/bug.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <linux/err.h>#include <linux/export.h>#include <linux/i2c.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/of.h>#include <linux/of_device.h>#include <linux/pm_runtime.h>#include <linux/property.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/workqueue.h> #include "internals.h" 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 * @bus: I3C bus to take the lock on * * This function takes the bus lock so that no other operations can occur on * the bus. This is needed for all kind of bus maintenance operation, like * - enabling/disabling slave events * - re-triggering DAA * - changing the dynamic address of a device * - relinquishing mastership * - ... * * The reason for this kind of locking is that we don't want drivers and core * logic to rely on I3C device information that could be changed behind their * back. */static void i3c_bus_maintenance_lock(struct i3c_bus *bus){ down_write(&bus->lock);} /** * i3c_bus_maintenance_unlock - Release the bus lock after a maintenance * operation * @bus: I3C bus to release the lock on * * Should be called when the bus maintenance operation is done. See * i3c_bus_maintenance_lock() for more details on what these maintenance * operations are. */static void i3c_bus_maintenance_unlock(struct i3c_bus *bus){ up_write(&bus->lock);} /** * i3c_bus_normaluse_lock - Lock the bus for a normal operation * @bus: I3C bus to take the lock on * * This function takes the bus lock for any operation that is not a maintenance * operation (see i3c_bus_maintenance_lock() for a non-exhaustive list of * maintenance operations). Basically all communications with I3C devices are * normal operations (HDR, SDR transfers or CCC commands that do not change bus * state or I3C dynamic address). * * Note that this lock is not guaranteeing serialization of normal operations. * In other words, transfer requests passed to the I3C master can be submitted * in parallel and I3C master drivers have to use their own locking to make * sure two different communications are not inter-mixed, or access to the * output/input queue is not done while the engine is busy. */void i3c_bus_normaluse_lock(struct i3c_bus *bus){ down_read(&bus->lock);} /** * i3c_bus_normaluse_unlock - Release the bus lock after a normal operation * @bus: I3C bus to release the lock on * * Should be called when a normal operation is done. See * i3c_bus_normaluse_lock() for more details on what these normal operations * are. */void i3c_bus_normaluse_unlock(struct i3c_bus *bus){ up_read(&bus->lock);} 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){ struct i3c_master_controller *master; if (dev->type == &i3c_device_type) return dev_to_i3cdev(dev)->bus; master = dev_to_i3cmaster(dev); return &master->bus;} static struct i3c_dev_desc *dev_to_i3cdesc(struct device *dev){ struct i3c_master_controller *master; if (dev->type == &i3c_device_type) return dev_to_i3cdev(dev)->desc; master = dev_to_i3cmaster(dev); return master->this;} static ssize_t bcr_show(struct device *dev, struct device_attribute *da, char *buf){ struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "0x%02x\n", desc->info.bcr); i3c_bus_normaluse_unlock(bus); return ret;}static DEVICE_ATTR_RO(bcr); static ssize_t dcr_show(struct device *dev, struct device_attribute *da, char *buf){ struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "0x%02x\n", desc->info.dcr); i3c_bus_normaluse_unlock(bus); return ret;}static DEVICE_ATTR_RO(dcr); static ssize_t pid_show(struct device *dev, struct device_attribute *da, char *buf){ struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "%llx\n", desc->info.pid); i3c_bus_normaluse_unlock(bus); return ret;}static DEVICE_ATTR_RO(pid); static ssize_t dynamic_address_show(struct device *dev, struct device_attribute *da, char *buf){ struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "%02x\n", desc->info.dyn_addr); i3c_bus_normaluse_unlock(bus); return ret;}static DEVICE_ATTR_RO(dynamic_address); static const char * const hdrcap_strings[] = { "hdr-ddr", "hdr-tsp", "hdr-tsl",}; static ssize_t hdrcap_show(struct device *dev, struct device_attribute *da, char *buf){ struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t offset = 0, ret; unsigned long caps; int mode; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); caps = desc->info.hdr_cap; for_each_set_bit(mode, &caps, 8) { if (mode >= ARRAY_SIZE(hdrcap_strings)) break; if (!hdrcap_strings[mode]) continue; ret = sysfs_emit_at(buf, offset, offset ? " %s" : "%s", hdrcap_strings[mode]); if (ret < 0) goto out; offset += ret; } ret = sysfs_emit_at(buf, offset, "\n"); if (ret < 0) goto out; ret = offset + ret; out: i3c_bus_normaluse_unlock(bus); return ret;}static DEVICE_ATTR_RO(hdrcap); static ssize_t modalias_show(struct device *dev, struct device_attribute *da, char *buf){ struct i3c_device *i3c = dev_to_i3cdev(dev); struct i3c_device_info devinfo; u16 manuf, part, ext; i3c_device_get_info(i3c, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); if (I3C_PID_RND_LOWER_32BITS(devinfo.pid)) return sysfs_emit(buf, "i3c:dcr%02Xmanuf%04X\n", devinfo.dcr, manuf); return sysfs_emit(buf, "i3c:dcr%02Xmanuf%04Xpart%04Xext%04X\n", devinfo.dcr, manuf, part, ext);}static DEVICE_ATTR_RO(modalias); static struct attribute *i3c_device_attrs[] = { &dev_attr_bcr.attr, &dev_attr_dcr.attr, &dev_attr_pid.attr, &dev_attr_dynamic_address.attr, &dev_attr_hdrcap.attr, &dev_attr_modalias.attr, NULL,};ATTRIBUTE_GROUPS(i3c_device); static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *env){ const struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_device_info devinfo; u16 manuf, part, ext; i3c_device_get_info(i3cdev, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); if (I3C_PID_RND_LOWER_32BITS(devinfo.pid)) return add_uevent_var(env, "MODALIAS=i3c:dcr%02Xmanuf%04X", devinfo.dcr, manuf); return add_uevent_var(env, "MODALIAS=i3c:dcr%02Xmanuf%04Xpart%04Xext%04X", devinfo.dcr, manuf, part, ext);} static const struct device_type i3c_device_type = { .groups = i3c_device_groups, .uevent = i3c_device_uevent,}; static int i3c_device_match(struct device *dev, const struct device_driver *drv){ struct i3c_device *i3cdev; const struct i3c_driver *i3cdrv; u8 static_addr_method = 0; if (dev->type != &i3c_device_type) return 0; i3cdev = dev_to_i3cdev(dev); i3cdrv = drv_to_i3cdrv(drv); i3c_bus_normaluse_lock(i3cdev->bus); if (i3cdev->desc->boardinfo) static_addr_method = i3cdev->desc->boardinfo->static_addr_method; i3c_bus_normaluse_unlock(i3cdev->bus); /* * SETAASA-based devices need not always have a matching ID since * it is not mandatory for such devices to implement deviceinfo * CCC commands. Allow them to register through DT or ACPI. */ if (i3cdrv->id_table && i3c_device_match_id(i3cdev, i3cdrv->id_table)) return 1; if (static_addr_method & I3C_ADDR_METHOD_SETAASA) { if (of_driver_match_device(dev, drv)) return 1; if (acpi_driver_match_device(dev, drv)) return 1; } return 0;} static int i3c_device_probe(struct device *dev){ struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_driver *driver = drv_to_i3cdrv(dev->driver); return driver->probe(i3cdev);} static void i3c_device_remove(struct device *dev){ struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_driver *driver = drv_to_i3cdrv(dev->driver); if (driver->remove) driver->remove(i3cdev);} static enum i3c_addr_slot_statusi3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask){ unsigned long status; int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS; if (addr > I2C_MAX_ADDR) return I3C_ADDR_SLOT_RSVD; status = bus->addrslots[bitpos / BITS_PER_LONG]; status >>= bitpos % BITS_PER_LONG; return status & mask;} static enum i3c_addr_slot_statusi3c_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 * I3C_ADDR_SLOT_STATUS_BITS; unsigned long *ptr; if (addr > I2C_MAX_ADDR) return; ptr = bus->addrslots + (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){ enum i3c_addr_slot_status status; status = i3c_bus_get_addr_slot_status(bus, 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_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; } return -ENOMEM;} static void i3c_bus_init_addrslots(struct i3c_bus *bus){ int i; /* Addresses 0 to 7 are reserved. */ for (i = 0; i < 8; i++) i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD); /* * Reserve broadcast address and all addresses that might collide * with the broadcast address when facing a single bit error. */ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR, I3C_ADDR_SLOT_RSVD); for (i = 0; i < 7; i++) i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ BIT(i),Lines 1–500 of 4051. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 Cadence Design Systems Inc. * * Author: Boris Brezillon <boris.brezillon@bootlin.com> */ #include <dt-bindings/i3c/i3c.h> #include <linux/acpi.h> #include <linux/atomic.h> #include <linux/bitmap.h> #include <linux/bug.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/export.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include "internals.h" 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 * @bus: I3C bus to take the lock on * * This function takes the bus lock so that no other operations can occur on * the bus. This is needed for all kind of bus maintenance operation, like * - enabling/disabling slave events * - re-triggering DAA * - changing the dynamic address of a device * - relinquishing mastership * - ... * * The reason for this kind of locking is that we don't want drivers and core * logic to rely on I3C device information that could be changed behind their * back. */ static void i3c_bus_maintenance_lock(struct i3c_bus *bus) { down_write(&bus->lock); } /** * i3c_bus_maintenance_unlock - Release the bus lock after a maintenance * operation * @bus: I3C bus to release the lock on * * Should be called when the bus maintenance operation is done. See * i3c_bus_maintenance_lock() for more details on what these maintenance * operations are. */ static void i3c_bus_maintenance_unlock(struct i3c_bus *bus) { up_write(&bus->lock); } /** * i3c_bus_normaluse_lock - Lock the bus for a normal operation * @bus: I3C bus to take the lock on * * This function takes the bus lock for any operation that is not a maintenance * operation (see i3c_bus_maintenance_lock() for a non-exhaustive list of * maintenance operations). Basically all communications with I3C devices are * normal operations (HDR, SDR transfers or CCC commands that do not change bus * state or I3C dynamic address). * * Note that this lock is not guaranteeing serialization of normal operations. * In other words, transfer requests passed to the I3C master can be submitted * in parallel and I3C master drivers have to use their own locking to make * sure two different communications are not inter-mixed, or access to the * output/input queue is not done while the engine is busy. */ void i3c_bus_normaluse_lock(struct i3c_bus *bus) { down_read(&bus->lock); } /** * i3c_bus_normaluse_unlock - Release the bus lock after a normal operation * @bus: I3C bus to release the lock on * * Should be called when a normal operation is done. See * i3c_bus_normaluse_lock() for more details on what these normal operations * are. */ void i3c_bus_normaluse_unlock(struct i3c_bus *bus) { up_read(&bus->lock); } 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) { struct i3c_master_controller *master; if (dev->type == &i3c_device_type) return dev_to_i3cdev(dev)->bus; master = dev_to_i3cmaster(dev); return &master->bus; } static struct i3c_dev_desc *dev_to_i3cdesc(struct device *dev) { struct i3c_master_controller *master; if (dev->type == &i3c_device_type) return dev_to_i3cdev(dev)->desc; master = dev_to_i3cmaster(dev); return master->this; } static ssize_t bcr_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "0x%02x\n", desc->info.bcr); i3c_bus_normaluse_unlock(bus); return ret; } static DEVICE_ATTR_RO(bcr); static ssize_t dcr_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "0x%02x\n", desc->info.dcr); i3c_bus_normaluse_unlock(bus); return ret; } static DEVICE_ATTR_RO(dcr); static ssize_t pid_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "%llx\n", desc->info.pid); i3c_bus_normaluse_unlock(bus); return ret; } static DEVICE_ATTR_RO(pid); static ssize_t dynamic_address_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t ret; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); ret = sysfs_emit(buf, "%02x\n", desc->info.dyn_addr); i3c_bus_normaluse_unlock(bus); return ret; } static DEVICE_ATTR_RO(dynamic_address); static const char * const hdrcap_strings[] = { "hdr-ddr", "hdr-tsp", "hdr-tsl", }; static ssize_t hdrcap_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *bus = dev_to_i3cbus(dev); struct i3c_dev_desc *desc; ssize_t offset = 0, ret; unsigned long caps; int mode; i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); caps = desc->info.hdr_cap; for_each_set_bit(mode, &caps, 8) { if (mode >= ARRAY_SIZE(hdrcap_strings)) break; if (!hdrcap_strings[mode]) continue; ret = sysfs_emit_at(buf, offset, offset ? " %s" : "%s", hdrcap_strings[mode]); if (ret < 0) goto out; offset += ret; } ret = sysfs_emit_at(buf, offset, "\n"); if (ret < 0) goto out; ret = offset + ret; out: i3c_bus_normaluse_unlock(bus); return ret; } static DEVICE_ATTR_RO(hdrcap); static ssize_t modalias_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_device *i3c = dev_to_i3cdev(dev); struct i3c_device_info devinfo; u16 manuf, part, ext; i3c_device_get_info(i3c, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); if (I3C_PID_RND_LOWER_32BITS(devinfo.pid)) return sysfs_emit(buf, "i3c:dcr%02Xmanuf%04X\n", devinfo.dcr, manuf); return sysfs_emit(buf, "i3c:dcr%02Xmanuf%04Xpart%04Xext%04X\n", devinfo.dcr, manuf, part, ext); } static DEVICE_ATTR_RO(modalias); static struct attribute *i3c_device_attrs[] = { &dev_attr_bcr.attr, &dev_attr_dcr.attr, &dev_attr_pid.attr, &dev_attr_dynamic_address.attr, &dev_attr_hdrcap.attr, &dev_attr_modalias.attr, NULL, }; ATTRIBUTE_GROUPS(i3c_device); static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *env) { const struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_device_info devinfo; u16 manuf, part, ext; i3c_device_get_info(i3cdev, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); if (I3C_PID_RND_LOWER_32BITS(devinfo.pid)) return add_uevent_var(env, "MODALIAS=i3c:dcr%02Xmanuf%04X", devinfo.dcr, manuf); return add_uevent_var(env, "MODALIAS=i3c:dcr%02Xmanuf%04Xpart%04Xext%04X", devinfo.dcr, manuf, part, ext); } static const struct device_type i3c_device_type = { .groups = i3c_device_groups, .uevent = i3c_device_uevent, }; static int i3c_device_match(struct device *dev, const struct device_driver *drv) { struct i3c_device *i3cdev; const struct i3c_driver *i3cdrv; u8 static_addr_method = 0; if (dev->type != &i3c_device_type) return 0; i3cdev = dev_to_i3cdev(dev); i3cdrv = drv_to_i3cdrv(drv); i3c_bus_normaluse_lock(i3cdev->bus); if (i3cdev->desc->boardinfo) static_addr_method = i3cdev->desc->boardinfo->static_addr_method; i3c_bus_normaluse_unlock(i3cdev->bus); /* * SETAASA-based devices need not always have a matching ID since * it is not mandatory for such devices to implement deviceinfo * CCC commands. Allow them to register through DT or ACPI. */ if (i3cdrv->id_table && i3c_device_match_id(i3cdev, i3cdrv->id_table)) return 1; if (static_addr_method & I3C_ADDR_METHOD_SETAASA) { if (of_driver_match_device(dev, drv)) return 1; if (acpi_driver_match_device(dev, drv)) return 1; } return 0; } static int i3c_device_probe(struct device *dev) { struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_driver *driver = drv_to_i3cdrv(dev->driver); return driver->probe(i3cdev); } static void i3c_device_remove(struct device *dev) { struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_driver *driver = drv_to_i3cdrv(dev->driver); if (driver->remove) driver->remove(i3cdev); } static enum i3c_addr_slot_status i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask) { unsigned long status; int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS; if (addr > I2C_MAX_ADDR) return I3C_ADDR_SLOT_RSVD; status = bus->addrslots[bitpos / BITS_PER_LONG]; status >>= bitpos % BITS_PER_LONG; return status & mask; } 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 * I3C_ADDR_SLOT_STATUS_BITS; unsigned long *ptr; if (addr > I2C_MAX_ADDR) return; ptr = bus->addrslots + (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) { enum i3c_addr_slot_status status; status = i3c_bus_get_addr_slot_status(bus, 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_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; } return -ENOMEM; } static void i3c_bus_init_addrslots(struct i3c_bus *bus) { int i; /* Addresses 0 to 7 are reserved. */ for (i = 0; i < 8; i++) i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD); /* * Reserve broadcast address and all addresses that might collide * with the broadcast address when facing a single bit error. */ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR, I3C_ADDR_SLOT_RSVD); for (i = 0; i < 7; i++) i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ BIT(i),