diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml index e25fa72fd7857..5603f2e7807db 100644 --- a/Documentation/devicetree/bindings/i3c/i3c.yaml +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml @@ -31,10 +31,12 @@ properties: described in the device tree, which in turn means we have to describe I3C devices. - Another use case for describing an I3C device in the device tree is when - this I3C device has a static I2C address and we want to assign it a - specific I3C dynamic address before the DAA takes place (so that other - devices on the bus can't take this dynamic address). + Other use-cases for describing an I3C device in the device tree are: + - When the I3C device has a static I2C address and we want to assign + it a specific I3C dynamic address before the DAA takes place (so + that other devices on the bus can't take this dynamic address). + - When the I3C device requires SETAASA for its discovery and uses a + pre-defined static address. "#size-cells": const: 0 @@ -145,7 +147,31 @@ patternProperties: Dynamic address to be assigned to this device. In case static address is present (first cell of the reg property != 0), this address is assigned through SETDASA. If static address is not present, this address is assigned - through SETNEWDA after assigning a temporary address via ENTDAA. + through SETNEWDA after assigning a temporary address via ENTDAA. If + SETAASA is used, this property is not used, and the static address itself + becomes the dynamic address. + + mipi-i3c-static-method: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0x1 + maximum: 0x7 + default: 1 + description: | + Bitmap describing which methods of Dynamic Address Assignment from a + static address are supported by this I3C Target. For each defined bit + position, a set bit indicates support for that method and a cleared + bit indicates lack of support. + + Bit 0: SETDASA CCC (Direct) + Bit 1: SETAASA CCC (Broadcast) + Bit 2: Other CCC (vendor / standards extension) + All other bits are reserved. + + This property follows the MIPI I3C specification. The primary use + of this property is to indicate support for SETAASA, i.e Bit 1, but + will allow other values mentioned in the specification so that it + mirrors the specification. SETDASA will remain as the default method + even if this property is not present. required: - reg diff --git a/debian.nvidia-bos/config/annotations b/debian.nvidia-bos/config/annotations index cf5b2907fa1b0..cb5fa04089144 100644 --- a/debian.nvidia-bos/config/annotations +++ b/debian.nvidia-bos/config/annotations @@ -171,6 +171,9 @@ CONFIG_DRM_NOUVEAU_SIL164 note<'Disable nouveau for NVIDIA CONFIG_DRM_NOUVEAU_SVM policy<{'amd64': '-', 'arm64': '-'}> CONFIG_DRM_NOUVEAU_SVM note<'Disable nouveau for NVIDIA kernels'> +CONFIG_DW_I3C_MASTER policy<{'arm64': 'm'}> +CONFIG_DW_I3C_MASTER note<'Enable DesignWare I3C master controller for Tegra410'> + CONFIG_EFI_CAPSULE_LOADER policy<{'amd64': 'm', 'arm64': 'y'}> CONFIG_EFI_CAPSULE_LOADER note<'LP: #2067111'> @@ -186,6 +189,9 @@ CONFIG_GENERIC_CPU_CACHE_MAINTENANCE note<'Selected by arm64 via arch CONFIG_GPIO_AAEON policy<{'amd64': '-'}> CONFIG_GPIO_AAEON note<'Disable all Ubuntu ODM drivers'> +CONFIG_I3C policy<{'arm64': 'm'}> +CONFIG_I3C note<'Enable I3C bus support for Tegra410 and SPD5118 temperature sensors'> + CONFIG_IOMMUFD_VFIO_CONTAINER policy<{'arm64': 'y'}> CONFIG_IOMMUFD_VFIO_CONTAINER note<'LP: #2095028'> @@ -264,6 +270,9 @@ CONFIG_SAMPLE_CORESIGHT_SYSCFG note<'Required for Grace enablem CONFIG_SENSORS_AAEON policy<{'amd64': '-'}> CONFIG_SENSORS_AAEON note<'Disable all Ubuntu ODM drivers'> +CONFIG_SENSORS_SPD5118 policy<{'arm64': 'm'}> +CONFIG_SENSORS_SPD5118 note<'Enable SPD5118 temperature sensor support for DDR5 memory modules'> + CONFIG_SFC_CXL policy<{'amd64': 'n', 'arm64': 'n'}> CONFIG_SFC_CXL note<'Solarflare SFC9100-family CXL Type-2 device support; not needed for NVIDIA platforms'> diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 279bfa27da94d..5ab41e9b90390 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -315,7 +315,7 @@ struct acpi_rsconvert_info acpi_rs_convert_csi2_serial_bus[14] = { * ******************************************************************************/ -struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = { +struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = { {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)}, @@ -391,6 +391,11 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = { AML_OFFSET(i2c_serial_bus.type_specific_flags), 0}, + /* Read LVR from Type Specific Flags, bits[15:8] */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), + AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1, + 1}, + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed), AML_OFFSET(i2c_serial_bus.connection_speed), 1}, diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 4f203fc74512b..247ca3b061e55 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2320,12 +2320,15 @@ config SENSORS_INA3221 config SENSORS_SPD5118 tristate "SPD5118 Compliant Temperature Sensors" - depends on I2C + depends on I3C_OR_I2C select REGMAP_I2C + select REGMAP_I3C if I3C help If you say yes here you get support for SPD5118 (JEDEC JESD300) - compliant temperature sensors. Such sensors are found on DDR5 memory - modules. + compliant temperature sensors using I2C or I3C bus interface. + Such sensors are found on DDR5 memory modules. + + This driver supports both I2C and I3C interfaces. This driver can also be built as a module. If so, the module will be called spd5118. diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index 5da44571b6a0c..2cdeed743ecb1 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -66,9 +67,6 @@ static const unsigned short normal_i2c[] = { #define SPD5118_EEPROM_BASE 0x80 #define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES) -#define PAGE_ADDR0(page) (((page) & BIT(0)) << 6) -#define PAGE_ADDR1_4(page) (((page) & GENMASK(4, 1)) >> 1) - /* Temperature unit in millicelsius */ #define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4) /* Representable temperature range in millicelsius */ @@ -78,7 +76,6 @@ static const unsigned short normal_i2c[] = { struct spd5118_data { struct regmap *regmap; struct mutex nvmem_lock; - bool is_16bit; }; /* hwmon */ @@ -348,12 +345,7 @@ static ssize_t spd5118_nvmem_read_page(struct spd5118_data *data, char *buf, if (offset + count > SPD5118_PAGE_SIZE) count = SPD5118_PAGE_SIZE - offset; - if (data->is_16bit) { - addr = SPD5118_EEPROM_BASE | PAGE_ADDR0(page) | - (PAGE_ADDR1_4(page) << 8); - } else { - addr = page * 0x100 + SPD5118_EEPROM_BASE; - } + addr = page * 0x100 + SPD5118_EEPROM_BASE; err = regmap_bulk_read(regmap, addr + offset, buf, count); if (err) return err; @@ -473,10 +465,22 @@ static const struct regmap_config spd5118_regmap8_config = { .num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg), }; -static const struct regmap_config spd5118_regmap16_config = { +/* + * SPD5118 2-byte register address format (JESD300-5, Tables 7 & 20): + * Byte 1 (on wire first): MemReg | BlkAddr[0] | Address[5:0] + * Byte 2 (on wire second): 0000 | BlkAddr[4:1] + * + * The address byte (with MemReg and lower address bits) must be sent first, + * followed by the upper block address byte. With regmap 16-bit register + * format, this maps to little-endian: the low byte of the 16-bit value is + * transmitted first. No range config is needed since I3C does not use MR11 + * page switching. + */ +static const struct regmap_config spd5118_regmap_i3c_config = { .reg_bits = 16, .val_bits = 8, .max_register = 0x7ff, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, .writeable_reg = spd5118_writeable_reg, .volatile_reg = spd5118_volatile_reg, .cache_type = REGCACHE_MAPLE, @@ -519,8 +523,7 @@ static int spd5118_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume); -static int spd5118_common_probe(struct device *dev, struct regmap *regmap, - bool is_16bit) +static int spd5118_common_probe(struct device *dev, struct regmap *regmap) { unsigned int capability, revision, vendor, bank; struct spd5118_data *data; @@ -537,8 +540,6 @@ static int spd5118_common_probe(struct device *dev, struct regmap *regmap, if (!(capability & SPD5118_CAP_TS_SUPPORT)) return -ENODEV; - data->is_16bit = is_16bit; - err = regmap_read(regmap, SPD5118_REG_REVISION, &revision); if (err) return err; @@ -680,69 +681,21 @@ static int spd5118_i2c_init(struct i2c_client *client) return 0; } -/* - * 16-bit addressing note: - * - * If I2C_FUNC_I2C is not supported by an I2C adapter driver, regmap uses - * SMBus operations as alternative. To simulate a read operation with a 16-bit - * address, it writes the address using i2c_smbus_write_byte_data(), followed - * by one or more calls to i2c_smbus_read_byte() to read the data. - * Per spd5118 standard, a read operation after writing the address must start - * with (Repeat Start). However, a SMBus read byte operation starts with - * (Start). This resets the register address in the spd5118 chip. As result, - * i2c_smbus_read_byte() always returns data from register address 0x00. - * - * A working alternative to access chips with 16-bit register addresses in the - * absence of I2C_FUNC_I2C support is not known. - * - * For this reason, 16-bit addressing can only be supported with I2C if the - * adapter supports I2C_FUNC_I2C. - * - * For I2C, the addressing mode selected by the BIOS must not be changed. - * Experiments show that at least some PC BIOS versions will not change the - * addressing mode on a soft reboot and end up in setup, claiming that some - * configuration change happened. This will happen again after a power cycle, - * which does reset the addressing mode. To prevent this from happening, - * detect if 16-bit addressing is enabled and always use the currently - * configured addressing mode. - */ - static int spd5118_i2c_probe(struct i2c_client *client) { - const struct regmap_config *config; struct device *dev = &client->dev; struct regmap *regmap; - int err, mode; - bool is_16bit; + int err; err = spd5118_i2c_init(client); if (err) return err; - mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE); - if (mode < 0) - return mode; - - is_16bit = mode & SPD5118_LEGACY_MODE_ADDR; - if (is_16bit) { - /* - * See 16-bit addressing note above explaining why it is - * necessary to check for I2C_FUNC_I2C support here. - */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(dev, "Adapter does not support 16-bit register addresses\n"); - return -ENODEV; - } - config = &spd5118_regmap16_config; - } else { - config = &spd5118_regmap8_config; - } - - regmap = devm_regmap_init_i2c(client, config); + regmap = devm_regmap_init_i2c(client, &spd5118_regmap8_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n"); - return spd5118_common_probe(dev, regmap, is_16bit); + return spd5118_common_probe(dev, regmap); } static const struct i2c_device_id spd5118_i2c_id[] = { @@ -770,7 +723,39 @@ static struct i2c_driver spd5118_i2c_driver = { .address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL, }; -module_i2c_driver(spd5118_i2c_driver); +/* I3C */ + +static int spd5118_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + struct regmap *regmap; + u8 regval[2]; + int err; + + regmap = devm_regmap_init_i3c(i3cdev, &spd5118_regmap_i3c_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n"); + + err = regmap_bulk_read(regmap, SPD5118_REG_TYPE, regval, 2); + if (err) + return dev_err_probe(dev, err, "failed to read device type\n"); + + if (regval[0] != 0x51 || regval[1] != 0x18) + return -ENODEV; + + return spd5118_common_probe(dev, regmap); +} + +static struct i3c_driver spd5118_i3c_driver = { + .driver = { + .name = "spd5118_i3c", + .of_match_table = spd5118_of_ids, + .pm = pm_sleep_ptr(&spd5118_pm_ops), + }, + .probe = spd5118_i3c_probe, +}; + +module_i3c_i2c_driver(spd5118_i3c_driver, &spd5118_i2c_driver); MODULE_AUTHOR("RenĂ© Rebe "); MODULE_AUTHOR("Guenter Roeck "); diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 9e6be49bebb2c..4f00cdc8d236e 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -5,16 +5,21 @@ * Author: Boris Brezillon */ +#include +#include #include #include #include #include #include #include +#include #include #include #include +#include #include +#include #include #include #include @@ -281,10 +286,29 @@ 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; + struct i3c_device_info devinfo = {}; + u8 static_addr_method = 0; u16 manuf, part, ext; + int ret; + + i3c_bus_normaluse_lock(i3c->bus); + if (i3c->desc) { + devinfo = i3c->desc->info; + if (i3c->desc->boardinfo) + static_addr_method = i3c->desc->boardinfo->static_addr_method; + } + i3c_bus_normaluse_unlock(i3c->bus); + + if ((static_addr_method & I3C_ADDR_METHOD_SETAASA) && !devinfo.pid) { + ret = of_device_modalias(dev, buf, PAGE_SIZE); + if (ret != -ENODEV) + return ret; + + ret = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); + if (ret != -ENODEV) + return ret; + } - 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); @@ -312,11 +336,27 @@ 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; + struct i3c_device_info devinfo = {}; + u8 static_addr_method = 0; u16 manuf, part, ext; + int ret; - if (i3cdev->desc) + if (i3cdev->desc) { devinfo = i3cdev->desc->info; + if (i3cdev->desc->boardinfo) + static_addr_method = i3cdev->desc->boardinfo->static_addr_method; + } + + if ((static_addr_method & I3C_ADDR_METHOD_SETAASA) && !devinfo.pid) { + ret = of_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + + ret = acpi_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + } + manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); @@ -339,15 +379,32 @@ 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); - if (i3c_device_match_id(i3cdev, i3cdrv->id_table)) + + if (i3cdev->desc && i3cdev->desc->boardinfo) + static_addr_method = i3cdev->desc->boardinfo->static_addr_method; + + /* + * 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; } @@ -497,7 +554,7 @@ static void i3c_bus_cleanup(struct i3c_bus *i3cbus) mutex_unlock(&i3c_core_lock); } -static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np) +static int i3c_bus_init(struct i3c_bus *i3cbus, struct fwnode_handle *fwnode) { int ret, start, end, id = -1; @@ -507,8 +564,8 @@ static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np) i3c_bus_init_addrslots(i3cbus); i3cbus->mode = I3C_BUS_MODE_PURE; - if (np) - id = of_alias_get_id(np, "i3c"); + if (fwnode && is_of_node(fwnode)) + id = of_alias_get_id(to_of_node(fwnode), "i3c"); mutex_lock(&i3c_core_lock); if (id >= 0) { @@ -784,7 +841,7 @@ static void i3c_masterdev_release(struct device *dev) WARN_ON(!list_empty(&bus->devs.i2c) || !list_empty(&bus->devs.i3c)); i3c_bus_cleanup(bus); - of_node_put(dev->of_node); + fwnode_handle_put(dev->fwnode); } static const struct device_type i3c_masterdev_type = { @@ -968,7 +1025,7 @@ static void i3c_device_release(struct device *dev) WARN_ON(i3cdev->desc); - of_node_put(i3cdev->dev.of_node); + fwnode_handle_put(dev->fwnode); kfree(i3cdev); } @@ -1019,6 +1076,51 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, return ret; } +/** + * i3c_master_setaasa_locked() - start a SETAASA procedure (Set All Addresses to Static Address) + * @master: I3C master object + * + * Send a SETAASA CCC command to set all attached I3C devices' dynamic addresses to + * their static address. + * + * This function must be called with the bus lock held in write mode. + * + * First, the SETHID CCC command is sent, followed by the SETAASA CCC. + * + * Return: 0 in case of success, a positive I3C error code if the error is + * one of the official Mx error codes, and a negative error code otherwise. + */ +static int i3c_master_setaasa_locked(struct i3c_master_controller *master) +{ + struct i3c_ccc_cmd_dest dest; + struct i3c_ccc_cmd cmd; + int ret; + + /* + * Send SETHID CCC command. Though it is a standard CCC command specified + * in JESD300-5, we are not defining a separate macro to be explicit that + * the value falls under the vendor specific range. + */ + i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0); + i3c_ccc_cmd_init(&cmd, false, I3C_CCC_VENDOR(0, true), &dest, 1); + ret = i3c_master_send_ccc_cmd_locked(master, &cmd); + i3c_ccc_cmd_dest_cleanup(&dest); + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + if (ret) + return ret; + + /* Send SETAASA CCC command */ + i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0); + i3c_ccc_cmd_init(&cmd, false, I3C_CCC_SETAASA, &dest, 1); + ret = i3c_master_send_ccc_cmd_locked(master, &cmd); + i3c_ccc_cmd_dest_cleanup(&dest); + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + + return ret; +} + /** * i3c_master_entdaa_locked() - start a DAA (Dynamic Address Assignment) * procedure @@ -1694,8 +1796,10 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, int ret; i3cdev = i3c_master_alloc_i3c_dev(master, &info); - if (IS_ERR(i3cdev)) - return -ENOMEM; + if (IS_ERR(i3cdev)) { + ret = -ENOMEM; + goto err_reserve_addr; + } i3cdev->boardinfo = boardinfo; @@ -1703,6 +1807,22 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, if (ret) goto err_free_dev; + /* + * For devices using SETAASA instead of ENTDAA, the address is statically + * assigned. Update the dynamic address to the provided static address. + * Reattach the I3C device after updating the dynamic address with the same + * static address. It is not mandatory for such devices to implement CCC + * commands like GETPID, GETDCR etc. Hence, we can return after reattaching. + */ + if (i3cdev->boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) { + i3cdev->info.dyn_addr = i3cdev->boardinfo->static_addr; + ret = i3c_master_reattach_i3c_dev(i3cdev, 0); + if (ret) + goto err_detach_dev; + + return 0; + } + ret = i3c_master_setdasa_locked(master, i3cdev->info.static_addr, i3cdev->boardinfo->init_dyn_addr); if (ret) @@ -1725,6 +1845,16 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, i3c_master_detach_i3c_dev(i3cdev); err_free_dev: i3c_master_free_i3c_dev(i3cdev); +err_reserve_addr: + /* + * A target using SETAASA may still get the static address on the + * SETAASA broadcast even if attach fails here. Keep the address + * reserved so that it is not assigned to another device during DAA. + */ + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) + i3c_bus_set_addr_slot_status(&master->bus, + boardinfo->static_addr, + I3C_ADDR_SLOT_RSVD); return ret; } @@ -1752,11 +1882,20 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) desc->dev->dev.type = &i3c_device_type; desc->dev->dev.bus = &i3c_bus_type; desc->dev->dev.release = i3c_device_release; - dev_set_name(&desc->dev->dev, "%d-%llx", master->bus.id, - desc->info.pid); + + /* + * For devices without PID (e.g., SETAASA devices), use + * static address for naming instead. + */ + if (desc->info.pid) + dev_set_name(&desc->dev->dev, "%d-%llx", master->bus.id, + desc->info.pid); + else + dev_set_name(&desc->dev->dev, "%d-%02x", master->bus.id, + desc->info.static_addr); if (desc->boardinfo) - desc->dev->dev.of_node = desc->boardinfo->of_node; + device_set_node(&desc->dev->dev, desc->boardinfo->fwnode); ret = device_register(&desc->dev->dev); if (ret) { @@ -2146,8 +2285,25 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) * addressable. */ - if (i3cboardinfo->static_addr) - i3c_master_early_i3c_dev_add(master, i3cboardinfo); + if (i3cboardinfo->static_addr) { + ret = i3c_master_early_i3c_dev_add(master, i3cboardinfo); + if (ret && (i3cboardinfo->static_addr_method & + I3C_ADDR_METHOD_SETAASA)) + goto err_rstdaa; + } + } + + /* + * SETAASA is a broadcast CCC. Issue it after SETDASA so that devices + * configured for SETDASA (or supporting both methods) are assigned + * first, matching MIPI DISCO guidance to prefer SETDASA when both are + * available. Targets that already have a dynamic address ignore the + * later SETAASA broadcast. + */ + if (master->addr_method & I3C_ADDR_METHOD_SETAASA) { + ret = i3c_master_setaasa_locked(master); + if (ret) + goto err_rstdaa; } ret = i3c_master_do_daa(master); @@ -2192,8 +2348,18 @@ static void i3c_master_attach_boardinfo(struct i3c_dev_desc *i3cdev) struct i3c_dev_boardinfo *i3cboardinfo; list_for_each_entry(i3cboardinfo, &master->boardinfo.i3c, node) { - if (i3cdev->info.pid != i3cboardinfo->pid) - continue; + /* + * For devices without PID (e.g., SETAASA devices), match by + * static address. For devices with PID, match by PID. + */ + if (i3cboardinfo->pid) { + if (i3cdev->info.pid != i3cboardinfo->pid) + continue; + } else { + if (!i3cboardinfo->static_addr || + i3cdev->info.static_addr != i3cboardinfo->static_addr) + continue; + } i3cdev->boardinfo = i3cboardinfo; i3cdev->info.static_addr = i3cboardinfo->static_addr; @@ -2207,8 +2373,12 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev) struct i3c_master_controller *master = i3c_dev_get_master(refdev); struct i3c_dev_desc *i3cdev; + if (!refdev->info.pid) + return NULL; + i3c_bus_for_each_i3cdev(&master->bus, i3cdev) { - if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid) + if (i3cdev != refdev && i3cdev->info.pid && + i3cdev->info.pid == refdev->info.pid) return i3cdev; } @@ -2374,9 +2544,58 @@ EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); #define OF_I3C_REG1_IS_I2C_DEV BIT(31) +#ifdef CONFIG_ACPI +static int i3c_acpi_get_i2c_resource(struct acpi_resource *ares, void *data) +{ + struct i2c_dev_boardinfo *boardinfo = data; + struct acpi_resource_i2c_serialbus *sb; + + if (boardinfo->base.addr || !i2c_acpi_get_i2c_resource(ares, &sb)) + return 1; + + boardinfo->base.addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + boardinfo->base.flags |= I2C_CLIENT_TEN; + + boardinfo->lvr = sb->lvr; + + return 1; +} + +static int i3c_acpi_add_i2c_boardinfo(struct i2c_dev_boardinfo *boardinfo, + struct fwnode_handle *fwnode) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + LIST_HEAD(resources); + int ret; + + boardinfo->base.fwnode = acpi_fwnode_handle(adev); + acpi_set_modalias(adev, dev_name(&adev->dev), boardinfo->base.type, + sizeof(boardinfo->base.type)); + + ret = acpi_dev_get_resources(adev, &resources, + i3c_acpi_get_i2c_resource, boardinfo); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resources); + + if (!boardinfo->base.addr) + return -ENODEV; + + return 0; +} +#else +static inline int i3c_acpi_add_i2c_boardinfo(struct i2c_dev_boardinfo *boardinfo, + struct fwnode_handle *fwnode) +{ + return -ENODEV; +} +#endif + static int -of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, - struct device_node *node, u32 *reg) +i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, + struct fwnode_handle *fwnode, u32 *reg) { struct i2c_dev_boardinfo *boardinfo; struct device *dev = &master->dev; @@ -2386,9 +2605,22 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, if (!boardinfo) return -ENOMEM; - ret = of_i2c_get_board_info(dev, node, &boardinfo->base); - if (ret) - return ret; + if (is_of_node(fwnode)) { + ret = of_i2c_get_board_info(dev, to_of_node(fwnode), &boardinfo->base); + if (ret) + return ret; + + /* LVR is encoded in reg[2] for Device Tree. */ + boardinfo->lvr = reg[2]; + } else if (is_acpi_device_node(fwnode)) { + ret = i3c_acpi_add_i2c_boardinfo(boardinfo, fwnode); + if (ret) { + devm_kfree(dev, boardinfo); + return ret; + } + } else { + return -EINVAL; + } /* * The I3C Specification does not clearly say I2C devices with 10-bit @@ -2400,23 +2632,20 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, return -EOPNOTSUPP; } - /* LVR is encoded in reg[2]. */ - boardinfo->lvr = reg[2]; - list_add_tail(&boardinfo->node, &master->boardinfo.i2c); - of_node_get(node); + fwnode_handle_get(fwnode); return 0; } static int -of_i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, - struct device_node *node, u32 *reg) +i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, + struct fwnode_handle *fwnode, u32 *reg) { struct i3c_dev_boardinfo *boardinfo; struct device *dev = &master->dev; enum i3c_addr_slot_status addrstatus; - u32 init_dyn_addr = 0; + u32 init_dyn_addr = 0, static_addr_method = 0; boardinfo = devm_kzalloc(dev, sizeof(*boardinfo), GFP_KERNEL); if (!boardinfo) @@ -2434,7 +2663,19 @@ of_i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, boardinfo->static_addr = reg[0]; - if (!of_property_read_u32(node, "assigned-address", &init_dyn_addr)) { + if (!fwnode_property_read_u32(fwnode, "mipi-i3c-static-method", &static_addr_method)) + boardinfo->static_addr_method = static_addr_method & + (I3C_ADDR_METHOD_SETDASA | I3C_ADDR_METHOD_SETAASA); + + if (!fwnode_property_read_u32(fwnode, "assigned-address", &init_dyn_addr)) { + /* + * When a device advertises both SETDASA and SETAASA, an explicit + * dynamic address selects SETDASA (MIPI DISCO prefers it); drop + * SETAASA so it is not used for this device. + */ + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETDASA) + boardinfo->static_addr_method &= ~I3C_ADDR_METHOD_SETAASA; + if (init_dyn_addr > I3C_MAX_ADDR) return -EINVAL; @@ -2444,21 +2685,36 @@ of_i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, return -EINVAL; } + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) { + /* For SETAASA, static address is taken as the dynamic address. */ + init_dyn_addr = boardinfo->static_addr; + } + + /* Update the address methods required for device discovery */ + master->addr_method |= boardinfo->static_addr_method; + boardinfo->pid = ((u64)reg[1] << 32) | reg[2]; - if ((boardinfo->pid & GENMASK_ULL(63, 48)) || - I3C_PID_RND_LOWER_32BITS(boardinfo->pid)) - return -EINVAL; + /* For SETAASA devices, validate the static address instead of PID */ + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) { + if (!boardinfo->static_addr) + return -EINVAL; + } else { + if (!I3C_PID_MANUF_ID(boardinfo->pid) || + (boardinfo->pid & GENMASK_ULL(63, 48)) || + I3C_PID_RND_LOWER_32BITS(boardinfo->pid)) + return -EINVAL; + } boardinfo->init_dyn_addr = init_dyn_addr; - boardinfo->of_node = of_node_get(node); + boardinfo->fwnode = fwnode_handle_get(fwnode); list_add_tail(&boardinfo->node, &master->boardinfo.i3c); return 0; } -static int of_i3c_master_add_dev(struct i3c_master_controller *master, - struct device_node *node) +static int i3c_master_add_of_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) { u32 reg[3]; int ret; @@ -2466,34 +2722,108 @@ static int of_i3c_master_add_dev(struct i3c_master_controller *master, if (!master) return -EINVAL; - ret = of_property_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg)); + ret = fwnode_property_read_u32_array(fwnode, "reg", reg, ARRAY_SIZE(reg)); if (ret) return ret; /* - * The manufacturer ID can't be 0. If reg[1] == 0 that means we're - * dealing with an I2C device. + * I3C device should have either the manufacturer ID specified or the + * address discovery method specified. Else treat it as an I2C device. */ - if (!reg[1]) - ret = of_i3c_master_add_i2c_boardinfo(master, node, reg); + if (!reg[1] && !fwnode_property_present(fwnode, "mipi-i3c-static-method")) + ret = i3c_master_add_i2c_boardinfo(master, fwnode, reg); else - ret = of_i3c_master_add_i3c_boardinfo(master, node, reg); + ret = i3c_master_add_i3c_boardinfo(master, fwnode, reg); return ret; } -static int of_populate_i3c_bus(struct i3c_master_controller *master) +#ifdef CONFIG_ACPI +static int i3c_master_add_acpi_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + acpi_bus_address adr; + u32 reg[3] = { 0 }; + int ret; + + /* + * If the ACPI table entry has _ADR method, it's an I3C device. + * Otherwise it may be an I2C device described by an I2cSerialBus + * resource. If no I2cSerialBus resource is found, ignore the entry. + */ + if (!acpi_has_method(adev->handle, "_ADR")) { + ret = i3c_master_add_i2c_boardinfo(master, fwnode, reg); + if (ret == -ENODEV) + return 0; + + return ret; + } + + adr = acpi_device_adr(adev); + + /* For I3C devices, _ADR will have the 48 bit PID of the device */ + reg[1] = upper_32_bits(adr); + reg[2] = lower_32_bits(adr); + + fwnode_property_read_u32(fwnode, "mipi-i3c-static-address", ®[0]); + + return i3c_master_add_i3c_boardinfo(master, fwnode, reg); +} + +static u8 i3c_acpi_i2c_get_lvr(struct i2c_client *client) +{ + struct acpi_device *adev = to_acpi_device_node(client->dev.fwnode); + struct i2c_dev_boardinfo boardinfo = {}; + LIST_HEAD(resources); + int ret; + u8 lvr; + + lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; + + ret = acpi_dev_get_resources(adev, &resources, + i3c_acpi_get_i2c_resource, &boardinfo); + if (ret < 0) + return lvr; + + if (boardinfo.base.addr) + lvr = boardinfo.lvr; + + acpi_dev_free_resource_list(&resources); + + return lvr; +} +#else +static inline int i3c_master_add_acpi_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) +{ + return -ENODEV; +} + +static inline u8 i3c_acpi_i2c_get_lvr(struct i2c_client *client) +{ + return I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; +} +#endif + +static int fwnode_populate_i3c_bus(struct i3c_master_controller *master) { struct device *dev = &master->dev; - struct device_node *i3cbus_np = dev->of_node; + struct fwnode_handle *fwnode = dev_fwnode(dev); int ret; u32 val; - if (!i3cbus_np) + if (!fwnode) return 0; - for_each_available_child_of_node_scoped(i3cbus_np, node) { - ret = of_i3c_master_add_dev(master, node); + fwnode_for_each_available_child_node_scoped(fwnode, child) { + if (is_of_node(child)) + ret = i3c_master_add_of_dev(master, child); + else if (is_acpi_device_node(child)) + ret = i3c_master_add_acpi_dev(master, child); + else + continue; + if (ret) return ret; } @@ -2503,10 +2833,10 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master) * on the bus are not supporting typical rates, or if the bus topology * prevents it from using max possible rate. */ - if (!of_property_read_u32(i3cbus_np, "i2c-scl-hz", &val)) + if (!device_property_read_u32(dev, "i2c-scl-hz", &val)) master->bus.scl_rate.i2c = val; - if (!of_property_read_u32(i3cbus_np, "i3c-scl-hz", &val)) + if (!device_property_read_u32(dev, "i3c-scl-hz", &val)) master->bus.scl_rate.i3c = val; return 0; @@ -2561,8 +2891,13 @@ static u8 i3c_master_i2c_get_lvr(struct i2c_client *client) u8 lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; u32 reg[3]; - if (!of_property_read_u32_array(client->dev.of_node, "reg", reg, ARRAY_SIZE(reg))) - lvr = reg[2]; + if (is_of_node(client->dev.fwnode)) { + if (!fwnode_property_read_u32_array(client->dev.fwnode, "reg", + reg, ARRAY_SIZE(reg))) + lvr = reg[2]; + } else if (is_acpi_device_node(client->dev.fwnode)) { + lvr = i3c_acpi_i2c_get_lvr(client); + } return lvr; } @@ -2680,7 +3015,8 @@ 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, id; + struct fwnode_handle *fwnode = dev_fwnode(&master->dev); + int ret, id = -1; adap->dev.parent = master->dev.parent; adap->owner = master->dev.parent->driver->owner; @@ -2689,7 +3025,9 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master) adap->timeout = HZ; adap->retries = 3; - id = of_alias_get_id(master->dev.of_node, "i2c"); + if (fwnode && is_of_node(fwnode)) + id = of_alias_get_id(to_of_node(fwnode), "i2c"); + if (id >= 0) { adap->nr = id; ret = i2c_add_numbered_adapter(adap); @@ -2732,11 +3070,12 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master) if (!i3cdev->dev) continue; - i3cdev->dev->desc = NULL; - if (device_is_registered(&i3cdev->dev->dev)) + if (device_is_registered(&i3cdev->dev->dev)) { + get_device(&i3cdev->dev->dev); device_unregister(&i3cdev->dev->dev); - else - put_device(&i3cdev->dev->dev); + } + i3cdev->dev->desc = NULL; + put_device(&i3cdev->dev->dev); i3cdev->dev = NULL; } } @@ -2996,12 +3335,13 @@ int i3c_master_register(struct i3c_master_controller *master, return ret; master->dev.parent = parent; - master->dev.of_node = of_node_get(parent->of_node); + device_set_node(&master->dev, fwnode_handle_get(dev_fwnode(parent))); master->dev.bus = &i3c_bus_type; master->dev.type = &i3c_masterdev_type; master->dev.release = i3c_masterdev_release; master->ops = ops; master->secondary = secondary; + master->addr_method = I3C_ADDR_METHOD_SETDASA; INIT_LIST_HEAD(&master->boardinfo.i2c); INIT_LIST_HEAD(&master->boardinfo.i3c); @@ -3015,13 +3355,13 @@ int i3c_master_register(struct i3c_master_controller *master, 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); + ret = i3c_bus_init(i3cbus, dev_fwnode(&master->dev)); if (ret) goto err_put_dev; dev_set_name(&master->dev, "i3c-%d", i3cbus->id); - ret = of_populate_i3c_bus(master); + ret = fwnode_populate_i3c_bus(master); if (ret) goto err_put_dev; diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 259e4f5276655..aec9212fb1674 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -242,6 +242,7 @@ /* List of quirks */ #define AMD_I3C_OD_PP_TIMING BIT(1) #define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2) +#define DW_I3C_ACPI_SKIP_CLK_RST BIT(3) struct dw_i3c_cmd { u32 cmd_lo; @@ -309,6 +310,8 @@ static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m, case I3C_CCC_GETSTATUS: case I3C_CCC_GETMXDS: case I3C_CCC_GETHDRCAP: + case I3C_CCC_SETAASA: + case I3C_CCC_VENDOR(0, true): /* SETHID */ return true; default: return false; @@ -559,13 +562,28 @@ static void dw_i3c_master_set_intr_regs(struct dw_i3c_master *master) writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT); } +static unsigned long dw_i3c_master_get_core_rate(struct dw_i3c_master *master) +{ + unsigned int core_rate_prop; + + if (master->core_clk) + return clk_get_rate(master->core_clk); + + if (device_property_read_u32(master->dev, "clock-frequency", &core_rate_prop)) { + dev_err(master->dev, "missing clock-frequency property\n"); + return 0; + } + + return core_rate_prop; +} + static int dw_i3c_clk_cfg(struct dw_i3c_master *master) { unsigned long core_rate, core_period; u32 scl_timing; u8 hcnt, lcnt; - core_rate = clk_get_rate(master->core_clk); + core_rate = dw_i3c_master_get_core_rate(master); if (!core_rate) return -EINVAL; @@ -618,7 +636,7 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master) u16 hcnt, lcnt; u32 scl_timing; - core_rate = clk_get_rate(master->core_clk); + core_rate = dw_i3c_master_get_core_rate(master); if (!core_rate) return -EINVAL; @@ -902,7 +920,15 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) dw_i3c_master_dequeue_xfer(master, xfer); - newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0); + /* + * cmd->rx_len holds the number of addresses ENTDAA left unassigned. + * On an empty bus rx_len == maxdevs, so avoid GENMASK(-1, 0). + */ + if (cmd->rx_len >= master->maxdevs) + newdevs = 0; + else + newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0); + newdevs &= ~olddevs; for (pos = 0; pos < master->maxdevs; pos++) { @@ -1593,14 +1619,28 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->dev = &pdev->dev; + if (has_acpi_companion(&pdev->dev)) { + quirks = (unsigned long)device_get_match_data(&pdev->dev); + } else if (pdev->dev.of_node) { + drvdata = device_get_match_data(&pdev->dev); + if (drvdata) + quirks = drvdata->flags; + } + master->quirks = quirks; + master->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(master->regs)) return PTR_ERR(master->regs); - master->core_clk = devm_clk_get_enabled(&pdev->dev, NULL); + master->core_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(master->core_clk)) return PTR_ERR(master->core_clk); + if (!master->core_clk && !(master->quirks & DW_I3C_ACPI_SKIP_CLK_RST)) { + dev_err(&pdev->dev, "missing core clock\n"); + return -EINVAL; + } + master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk"); if (IS_ERR(master->pclk)) return PTR_ERR(master->pclk); @@ -1642,15 +1682,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->maxdevs = ret >> 16; master->free_pos = GENMASK(master->maxdevs - 1, 0); - if (has_acpi_companion(&pdev->dev)) { - quirks = (unsigned long)device_get_match_data(&pdev->dev); - } else if (pdev->dev.of_node) { - drvdata = device_get_match_data(&pdev->dev); - if (drvdata) - quirks = drvdata->flags; - } - master->quirks = quirks; - /* Keep controller enabled by preventing runtime suspend */ if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) pm_runtime_get_noresume(&pdev->dev); @@ -1845,11 +1876,12 @@ static const struct of_device_id dw_i3c_master_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match); -static const struct acpi_device_id amd_i3c_device_match[] = { +static const struct acpi_device_id dw_i3c_master_acpi_match[] = { { "AMDI0015", AMD_I3C_OD_PP_TIMING }, + { "NVDA2018", DW_I3C_ACPI_SKIP_CLK_RST }, { } }; -MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match); +MODULE_DEVICE_TABLE(acpi, dw_i3c_master_acpi_match); static struct platform_driver dw_i3c_driver = { .probe = dw_i3c_probe, @@ -1858,7 +1890,7 @@ static struct platform_driver dw_i3c_driver = { .driver = { .name = "dw-i3c-master", .of_match_table = dw_i3c_master_of_match, - .acpi_match_table = amd_i3c_device_match, + .acpi_match_table = dw_i3c_master_acpi_match, .pm = &dw_i3c_pm_ops, }, }; diff --git a/include/acpi/acrestyp.h b/include/acpi/acrestyp.h index 842f932e2c2bc..38a19b1d19ace 100644 --- a/include/acpi/acrestyp.h +++ b/include/acpi/acrestyp.h @@ -423,6 +423,7 @@ struct acpi_resource_i2c_serialbus { ACPI_RESOURCE_SERIAL_COMMON u8 access_mode; u16 slave_address; u32 connection_speed; + u8 lvr; }; /* Values for access_mode field above */ diff --git a/include/dt-bindings/i3c/i3c.h b/include/dt-bindings/i3c/i3c.h index 373439218bba6..78b8c634aad84 100644 --- a/include/dt-bindings/i3c/i3c.h +++ b/include/dt-bindings/i3c/i3c.h @@ -13,4 +13,8 @@ #define I2C_NO_FILTER_HIGH_FREQUENCY (1 << 5) #define I2C_NO_FILTER_LOW_FREQUENCY (2 << 5) +#define I3C_ADDR_METHOD_SETDASA (1 << 0) +#define I3C_ADDR_METHOD_SETAASA (1 << 1) +#define I3C_ADDR_METHOD_VENDOR (1 << 2) + #endif diff --git a/include/linux/i3c/ccc.h b/include/linux/i3c/ccc.h index ad59a4ae60d12..a145d766ab6f7 100644 --- a/include/linux/i3c/ccc.h +++ b/include/linux/i3c/ccc.h @@ -32,6 +32,7 @@ #define I3C_CCC_DEFSLVS I3C_CCC_ID(0x8, true) #define I3C_CCC_ENTTM I3C_CCC_ID(0xb, true) #define I3C_CCC_ENTHDR(x) I3C_CCC_ID(0x20 + (x), true) +#define I3C_CCC_SETAASA I3C_CCC_ID(0x29, true) /* Unicast-only commands */ #define I3C_CCC_SETDASA I3C_CCC_ID(0x7, false) diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 592b646f6134b..47af05f747a6c 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -174,10 +174,19 @@ struct i3c_device_ibi_info { * assigned a dynamic address by the master. Will be used during * bus initialization to assign it a specific dynamic address * before starting DAA (Dynamic Address Assignment) + * @static_addr_method: Bitmap describing which methods of Dynamic Address + * Assignment from a Static Address are supported by this I3C Target. + * A value of 1 in a bit position indicates that the I3C target + * supports that method, and a value of 0 indicates that the I3C + * target does not support that method. + * Bit 0: SETDASA + * Bit 1: SETAASA + * All other bits are reserved. * @pid: I3C Provisioned ID exposed by the device. This is a unique identifier * that may be used to attach boardinfo to i3c_dev_desc when the device * does not have a static address - * @of_node: optional DT node in case the device has been described in the DT + * @fwnode: Firmware node (DT or ACPI) in case the device has been + * described in firmware * * This structure is used to attach board-level information to an I3C device. * Not all I3C devices connected on the bus will have a boardinfo. It's only @@ -188,8 +197,9 @@ struct i3c_dev_boardinfo { struct list_head node; u8 init_dyn_addr; u8 static_addr; + u8 static_addr_method; u64 pid; - struct device_node *of_node; + struct fwnode_handle *fwnode; }; /** @@ -515,6 +525,11 @@ struct i3c_master_controller_ops { * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus * @bus: I3C bus exposed by this master + * @addr_method: Bitmap describing which methods of Address Assignment required + * to be run for discovering all the devices on the bus. + * Bit 0: SETDASA + * Bit 1: SETAASA + * All other bits are reserved. * @wq: workqueue which can be used by master * drivers if they need to postpone operations that need to take place * in a thread context. Typical examples are Hot Join processing which @@ -542,6 +557,7 @@ struct i3c_master_controller { struct list_head i2c; } boardinfo; struct i3c_bus bus; + u8 addr_method; struct workqueue_struct *wq; unsigned int dev_nack_retry_count; };