diff options
| author | Mark Brown <broonie@kernel.org> | 2026-08-21 13:56:57 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-08-21 13:56:57 +0100 |
| commit | 1880a0161e3041ced1d6d628b1ff9477b5bcf214 (patch) | |
| tree | 47aaad9a0ed87032a101133e215289759c3a97a1 | |
| parent | bcb73257a64551021250fa9da8a160f07df7c989 (diff) | |
| parent | cab40cfc9e116acd4d60f95b4b1264cab78f3803 (diff) | |
| download | linux-next-1880a0161e3041ced1d6d628b1ff9477b5bcf214.tar.gz linux-next-1880a0161e3041ced1d6d628b1ff9477b5bcf214.zip | |
Merge branch 'i3c/next' of https://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux.git
27 files changed, 2468 insertions, 376 deletions
diff --git a/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml index 6fa3078074d0..280f0ada3c43 100644 --- a/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml +++ b/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml @@ -19,6 +19,7 @@ properties: - items: - enum: - axiado,ax3000-i3c + - axiado,ax3005-i3c - const: cdns,i3c-master reg: diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml index e25fa72fd785..5603f2e7807d 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/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml b/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml index e803457d3f55..dc7ee38001d6 100644 --- a/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml +++ b/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml @@ -38,6 +38,9 @@ properties: interrupts: maxItems: 1 + resets: + maxItems: 1 + power-domains: maxItems: 1 diff --git a/Documentation/devicetree/bindings/i3c/xlnx,axi-i3c-1.0.yaml b/Documentation/devicetree/bindings/i3c/xlnx,axi-i3c-1.0.yaml new file mode 100644 index 000000000000..2caa245a8656 --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/xlnx,axi-i3c-1.0.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i3c/xlnx,axi-i3c-1.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMD I3C master + +maintainers: + - Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> + - Shubham Patil <shubhamsanjay.patil@amd.com> + +description: + The AXI-I3C IP is an I3C Controller with an AXI4-Lite interface, compatible + with the MIPI I3C Specification v1.1.1. The design includes bidirectional I/O + buffers that implement open collector drivers for the SDA and SCL signals. + External pull-up resistors are required to properly hold the bus at a Logic-1 + level when the drivers are released. + + For more details, please see https://docs.amd.com/r/en-US/pg439-axi-i3c + +properties: + compatible: + const: xlnx,axi-i3c-1.0 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +allOf: + - $ref: i3c.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + i3c@80000000 { + compatible = "xlnx,axi-i3c-1.0"; + reg = <0x80000000 0x10000>; + clocks = <&zynqmp_clk 71>; + interrupt-parent = <&imux>; + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <3>; + #size-cells = <0>; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 44010b33264e..416769c0b194 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1043,6 +1043,14 @@ L: linux-sound@vger.kernel.org S: Supported F: sound/soc/amd/ +AMD AXI I3C MASTER DRIVER +M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> +M: Shubham Patil <shubhamsanjay.patil@amd.com> +L: linux-i3c@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/i3c/xlnx,axi-i3c-1.0.yaml +F: drivers/i3c/master/amd-i3c-master.c + AMD AXI W1 DRIVER M: Kris Chaplin <kris.chaplin@amd.com> R: Thomas Delev <thomas.delev@amd.com> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 81a9a1d40eec..942282614c48 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2386,12 +2386,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 cc40661cab21..9724cf70b61d 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -18,6 +18,7 @@ #include <linux/bits.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/i3c/device.h> #include <linux/hwmon.h> #include <linux/module.h> #include <linux/mutex.h> @@ -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 <Sr> (Repeat Start). However, a SMBus read byte operation starts with - * <S> (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 <rene@exactcode.de>"); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index 101eaa77de68..f1ba363b22a1 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -101,8 +101,7 @@ void i3c_device_get_info(const struct i3c_device *dev, return; i3c_bus_normaluse_lock(dev->bus); - if (dev->desc) - *info = dev->desc->info; + *info = dev->desc->info; i3c_bus_normaluse_unlock(dev->bus); } EXPORT_SYMBOL_GPL(i3c_device_get_info); @@ -205,12 +204,14 @@ int i3c_device_request_ibi(struct i3c_device *dev, return ret; i3c_bus_normaluse_lock(dev->bus); - if (dev->desc) { + if (!dev->desc) { + ret = -ENOENT; + } else if (!(dev->desc->info.bcr & I3C_BCR_IBI_REQ_CAP)) { + ret = -EOPNOTSUPP; + } else { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_request_ibi_locked(dev->desc, req); mutex_unlock(&dev->desc->ibi_lock); - } else { - ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); @@ -309,7 +310,7 @@ EXPORT_SYMBOL_GPL(i3c_device_match_id); */ u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev) { - return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR); + return i3c_bus_to_i3c_master(dev->bus)->this->info.hdr_cap | BIT(I3C_SDR); } EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode); diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h index 0f1f3f766623..86a36b951e0d 100644 --- a/drivers/i3c/internals.h +++ b/drivers/i3c/internals.h @@ -72,4 +72,9 @@ static inline void i3c_readl_fifo(const void __iomem *addr, void *buf, } } +static inline struct i3c_master_controller *i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) +{ + return container_of(i3cbus, struct i3c_master_controller, bus); +} + #endif /* I3C_INTERNAL_H */ diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index f1be38a640ca..afcd7a21a3e6 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -5,6 +5,8 @@ * 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> @@ -13,10 +15,13 @@ #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> @@ -97,12 +102,6 @@ void i3c_bus_normaluse_unlock(struct i3c_bus *bus) up_read(&bus->lock); } -static struct i3c_master_controller * -i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) -{ - return container_of(i3cbus, struct i3c_master_controller, bus); -} - static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) { return container_of(dev, struct i3c_master_controller, dev); @@ -317,8 +316,7 @@ static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *e struct i3c_device_info devinfo; u16 manuf, part, ext; - if (i3cdev->desc) - devinfo = i3cdev->desc->info; + 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); @@ -341,15 +339,34 @@ 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)) + + 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; } @@ -491,7 +508,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; @@ -501,8 +518,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) { @@ -826,6 +843,11 @@ static struct attribute *i3c_masterdev_attrs[] = { }; ATTRIBUTE_GROUPS(i3c_masterdev); +static void i3c_master_free_i3c_dev(struct i3c_dev_desc *dev) +{ + kfree(dev); +} + static void i3c_masterdev_release(struct device *dev) { struct i3c_master_controller *master = dev_to_i3cmaster(dev); @@ -837,7 +859,9 @@ 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); + + i3c_master_free_i3c_dev(master->this); } static const struct device_type i3c_masterdev_type = { @@ -952,6 +976,8 @@ static void *i3c_ccc_cmd_dest_init(struct i3c_ccc_cmd_dest *dest, u8 addr, { dest->addr = addr; dest->payload.len = payloadlen; + dest->payload.actual_len = 0; + dest->payload.optional_bytes = 0; if (payloadlen) dest->payload.data = kzalloc(payloadlen, GFP_KERNEL); else @@ -965,17 +991,54 @@ static void i3c_ccc_cmd_dest_cleanup(struct i3c_ccc_cmd_dest *dest) kfree(dest->payload.data); } -static void i3c_ccc_cmd_init(struct i3c_ccc_cmd *cmd, bool rnw, u8 id, - struct i3c_ccc_cmd_dest *dests, - unsigned int ndests) +static void i3c_ccc_cmd_init_retries(struct i3c_ccc_cmd *cmd, bool rnw, u8 id, + struct i3c_ccc_cmd_dest *dests, + unsigned int ndests, unsigned int retries) { cmd->rnw = rnw ? 1 : 0; cmd->id = id; cmd->dests = dests; cmd->ndests = ndests; + cmd->retries = retries; cmd->err = I3C_ERROR_UNKNOWN; } +static void i3c_ccc_cmd_init(struct i3c_ccc_cmd *cmd, bool rnw, u8 id, + struct i3c_ccc_cmd_dest *dests, + unsigned int ndests) +{ + i3c_ccc_cmd_init_retries(cmd, rnw, id, dests, ndests, + rnw ? I3C_CCC_RETRIES : 0); +} + +static int i3c_ccc_validate_payload_len(struct i3c_ccc_cmd *cmd) +{ + unsigned int i; + + if (!cmd->rnw) + return 0; + + for (i = 0; i < cmd->ndests; i++) { + struct i3c_ccc_cmd_payload *p = &cmd->dests[i].payload; + u16 min_len; + + if (p->optional_bytes > p->len) + return -EINVAL; + + if (p->actual_len > p->len) + return -EIO; + + if (!p->len) + continue; + + min_len = p->len - p->optional_bytes; + if (p->actual_len < min_len) + return -EIO; + } + + return 0; +} + /** * i3c_master_send_ccc_cmd_locked() - send a CCC (Common Command Codes) * @master: master used to send frames on the bus @@ -987,6 +1050,9 @@ static void i3c_ccc_cmd_init(struct i3c_ccc_cmd *cmd, bool rnw, u8 id, static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master, struct i3c_ccc_cmd *cmd) { + unsigned int attempt, max_attempts; + int ret; + if (!cmd || !master) return -EINVAL; @@ -997,14 +1063,33 @@ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master, if (!master->ops->send_ccc_cmd) return -EOPNOTSUPP; - if ((cmd->id & I3C_CCC_DIRECT) && (!cmd->dests || !cmd->ndests)) + if ((cmd->rnw || (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 -EOPNOTSUPP; - return master->ops->send_ccc_cmd(master, cmd); + max_attempts = cmd->retries + 1; + ret = -EIO; + for (attempt = 0; attempt < max_attempts; attempt++) { + unsigned int i; + + if (cmd->rnw) + for (i = 0; i < cmd->ndests; i++) + cmd->dests[i].payload.actual_len = 0; + + cmd->err = I3C_ERROR_UNKNOWN; + ret = master->ops->send_ccc_cmd(master, cmd); + if (!ret && cmd->err == I3C_ERROR_UNKNOWN) + break; + } + + if (!ret) + ret = i3c_ccc_validate_payload_len(cmd); + + return ret; } static struct i2c_dev_desc * @@ -1044,15 +1129,10 @@ 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); } -static void i3c_master_free_i3c_dev(struct i3c_dev_desc *dev) -{ - kfree(dev); -} - static struct i3c_dev_desc * i3c_master_alloc_i3c_dev(struct i3c_master_controller *master, const struct i3c_device_info *info) @@ -1100,6 +1180,51 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, } /** + * 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 * @master: master used to send frames on the bus @@ -1363,10 +1488,14 @@ static int i3c_master_getmrl_locked(struct i3c_master_controller *master, return -ENOMEM; /* - * When the device does not have IBI payload GETMRL only returns 2 - * bytes of data. + * GETMRL returns 2 bytes (max read length) when the device does not + * advertise IBI payload, or 2 or 3 bytes when it does (the optional + * third byte is max IBI length). Use optional_bytes to allow either + * length when IBI payload is supported. */ - if (!(info->bcr & I3C_BCR_IBI_PAYLOAD)) + if (info->bcr & I3C_BCR_IBI_PAYLOAD) + dest.payload.optional_bytes = 1; + else dest.payload.len -= 1; i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1); @@ -1374,7 +1503,7 @@ static int i3c_master_getmrl_locked(struct i3c_master_controller *master, if (ret) goto out; - switch (dest.payload.len) { + switch (dest.payload.actual_len) { case 3: info->max_ibi_len = mrl->ibi_len; fallthrough; @@ -1409,7 +1538,7 @@ static int i3c_master_getmwl_locked(struct i3c_master_controller *master, if (ret) goto out; - if (dest.payload.len != sizeof(*mwl)) { + if (dest.payload.actual_len != sizeof(*mwl)) { ret = -EIO; goto out; } @@ -1435,27 +1564,32 @@ static int i3c_master_getmxds_locked(struct i3c_master_controller *master, if (!getmaxds) return -ENOMEM; + dest.payload.optional_bytes = 3; + i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1); ret = i3c_master_send_ccc_cmd_locked(master, &cmd); if (ret) { /* - * Retry when the device does not support max read turnaround - * while expecting shorter length from this CCC command. + * optional_bytes = 3 accepts a 2-byte response on the first + * attempt, so this fallback runs only when the 5-byte request + * fails rather than returning a short read. */ dest.payload.len -= 3; + dest.payload.optional_bytes = 0; + 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 (dest.payload.len != 2 && dest.payload.len != 5) { + if (dest.payload.actual_len != 2 && dest.payload.actual_len != 5) { ret = -EIO; goto out; } info->max_read_ds = getmaxds->maxrd; info->max_write_ds = getmaxds->maxwr; - if (dest.payload.len == 5) + if (dest.payload.actual_len == 5) info->max_read_turnaround = getmaxds->maxrdturn[0] | ((u32)getmaxds->maxrdturn[1] << 8) | ((u32)getmaxds->maxrdturn[2] << 16); @@ -1484,7 +1618,7 @@ static int i3c_master_gethdrcap_locked(struct i3c_master_controller *master, if (ret) goto out; - if (dest.payload.len != 1) { + if (dest.payload.actual_len != 1) { ret = -EIO; goto out; } @@ -1866,8 +2000,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; @@ -1875,6 +2011,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_locked(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) @@ -1897,6 +2049,16 @@ err_detach_dev: 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; } @@ -1904,12 +2066,21 @@ err_free_dev: static void i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) { + struct i3c_device *i3cdev, *tmp; struct i3c_dev_desc *desc; + LIST_HEAD(i3c_unreg_devs); int ret; if (!master->init_done) return; + i3c_bus_maintenance_lock(&master->bus); + + if (master->shutting_down) { + i3c_bus_maintenance_unlock(&master->bus); + return; + } + i3c_bus_for_each_i3cdev(&master->bus, desc) { if (desc->dev || !desc->info.dyn_addr || desc == master->this) continue; @@ -1924,30 +2095,92 @@ 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) { - dev_err(&master->dev, - "Failed to add I3C device (err = %d)\n", ret); - put_device(&desc->dev->dev); - } + /* If the device has IBI capability, set as wakeup capable */ + if (master->ibi_wakeup && (desc->info.bcr & I3C_BCR_IBI_REQ_CAP)) + device_set_wakeup_capable(&desc->dev->dev, true); + + list_add_tail(&desc->dev->node, &i3c_unreg_devs); + } + + i3c_bus_maintenance_unlock(&master->bus); + + list_for_each_entry_safe(i3cdev, tmp, &i3c_unreg_devs, node) { + ret = device_register(&i3cdev->dev); + if (ret) + dev_err(&master->dev, "Failed to add I3C device (err = %d)\n", ret); + else + list_del_init(&i3cdev->node); } + + i3c_bus_maintenance_lock(&master->bus); + + list_for_each_entry_safe(i3cdev, tmp, &i3c_unreg_devs, node) { + list_del(&i3cdev->node); + desc = i3cdev->desc; + i3cdev->desc = NULL; + put_device(&i3cdev->dev); + desc->dev = NULL; + } + + i3c_bus_maintenance_unlock(&master->bus); } static void i3c_master_reg_work_fn(struct work_struct *work) { struct i3c_master_controller *master = container_of(work, typeof(*master), reg_work); + i3c_master_register_new_i3c_devs(master); +} + +/** + * i3c_master_has_wakeup_enabled_devs() - check if any device can wake the system + * @master: I3C master controller + * + * Iterate over devices on the bus and return true if any device has + * system wakeup enabled and IBI enabled. + * + * Whether a device is enabled for system wakeup is user space policy, + * settable at any time through the device's power/wakeup sysfs attribute, + * so the answer is only stable once user space is frozen. Call this from + * a system suspend callback. + * + * Return: true if any device may wake the system via IBI, false otherwise. + */ +bool i3c_master_has_wakeup_enabled_devs(struct i3c_master_controller *master) +{ + struct i3c_dev_desc *desc; + bool wakeup = false; + i3c_bus_normaluse_lock(&master->bus); - if (!master->shutting_down) - i3c_master_register_new_i3c_devs(master); + i3c_bus_for_each_i3cdev(&master->bus, desc) { + if (!desc->dev || desc == master->this || !device_may_wakeup(&desc->dev->dev)) + continue; + guard(mutex)(&desc->ibi_lock); + if (desc->ibi && desc->ibi->enabled) { + wakeup = true; + break; + } + } i3c_bus_normaluse_unlock(&master->bus); + + return wakeup; } +EXPORT_SYMBOL_GPL(i3c_master_has_wakeup_enabled_devs); /** * i3c_master_dma_map_single() - Map buffer for single DMA transfer @@ -2075,6 +2308,8 @@ int i3c_master_set_info(struct i3c_master_controller *master, return 0; err_free_dev: + master->bus.cur_master = NULL; + master->this = NULL; i3c_master_free_i3c_dev(i3cdev); return ret; @@ -2095,7 +2330,8 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master) i3cdev->boardinfo->init_dyn_addr, I3C_ADDR_SLOT_FREE); - i3c_master_free_i3c_dev(i3cdev); + if (i3cdev != master->this) + i3c_master_free_i3c_dev(i3cdev); } list_for_each_entry_safe(i2cdev, i2ctmp, &master->bus.devs.i2c, @@ -2269,6 +2505,19 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) i3c_master_early_i3c_dev_add(master, i3cboardinfo); } + /* + * 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); if (ret) goto err_rstdaa; @@ -2311,8 +2560,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; @@ -2326,8 +2585,13 @@ 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 && + i3cdev != master->this) return i3cdev; } @@ -2619,9 +2883,58 @@ EXPORT_SYMBOL_GPL(i3c_master_do_daa); #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; @@ -2631,9 +2944,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 @@ -2645,23 +2971,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) @@ -2679,7 +3002,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; @@ -2689,21 +3024,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; @@ -2711,34 +3061,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; } @@ -2748,10 +3172,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; @@ -2806,8 +3230,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; } @@ -2925,7 +3354,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; @@ -2934,7 +3364,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); @@ -2977,15 +3409,19 @@ 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; } } +/* Approximate time for IBI handler to run */ +#define I3C_WAKEUP_PROCESSING_TIME_MS 100 + /** * i3c_master_queue_ibi() - Queue an IBI * @dev: the device this IBI is coming from @@ -2999,6 +3435,9 @@ void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot) if (!dev->ibi || !slot) return; + if (device_may_wakeup(&dev->dev->dev)) + pm_wakeup_event(&dev->dev->dev, I3C_WAKEUP_PROCESSING_TIME_MS); + atomic_inc(&dev->ibi->pending_ibis); queue_work(dev->ibi->wq, &slot->work); } @@ -3235,12 +3674,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); @@ -3254,13 +3694,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/Kconfig b/drivers/i3c/master/Kconfig index 2609f2b18e0a..da96d2aaa399 100644 --- a/drivers/i3c/master/Kconfig +++ b/drivers/i3c/master/Kconfig @@ -86,3 +86,18 @@ config RENESAS_I3C This driver can also be built as a module. If so, the module will be called renesas-i3c. + +config AMD_AXI_I3C_MASTER + tristate "AMD AXI I3C Master driver" + depends on HAS_IOMEM + help + Support for the AMD AXI I3C master controller, a soft IP used on + AMD (Xilinx) FPGAs and adaptive SoCs with ARM or MicroBlaze + processors. + + The controller currently supports Standard Data Rate (SDR) mode. + Features include Dynamic Address Assignment, private transfers, + and CCC transfers in both broadcast and direct modes. + + This driver can also be built as a module. If so, the module + will be called amd-i3c-master. diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile index 816a227b6f7a..8d82196dcf83 100644 --- a/drivers/i3c/master/Makefile +++ b/drivers/i3c/master/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/ obj-$(CONFIG_RENESAS_I3C) += renesas-i3c.o +obj-$(CONFIG_AMD_AXI_I3C_MASTER) += amd-i3c-master.o diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c index 047081c9f064..b35386260350 100644 --- a/drivers/i3c/master/adi-i3c-master.c +++ b/drivers/i3c/master/adi-i3c-master.c @@ -246,6 +246,7 @@ static void adi_i3c_master_end_xfer_locked(struct adi_i3c_master *master, if (cmd->cmd0 & REG_CMD_FIFO_0_RNW) { rx_len = min_t(u32, REG_CMDR_FIFO_XFER_BYTES(cmdr), cmd->rx_len); adi_i3c_master_rd_from_rx_fifo(master, cmd->rx_buf, rx_len); + cmd->rx_len = rx_len; } cmd->error = REG_CMDR_FIFO_ERROR(cmdr); } @@ -360,6 +361,8 @@ static int adi_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, adi_i3c_master_unqueue_xfer(master, xfer); cmd->err = adi_i3c_cmd_get_err(&xfer->cmds[0]); + if (!xfer->ret && cmd->rnw) + cmd->dests[0].payload.actual_len = ccmd->rx_len; return xfer->ret; } @@ -929,6 +932,7 @@ static const struct of_device_id adi_i3c_master_of_match[] = { { .compatible = "adi,i3c-master-v1" }, {} }; +MODULE_DEVICE_TABLE(of, adi_i3c_master_of_match); static int adi_i3c_master_probe(struct platform_device *pdev) { @@ -964,17 +968,9 @@ static int adi_i3c_master_probe(struct platform_device *pdev) writel(0x00, master->regs + REG_ENABLE); writel(0x00, master->regs + REG_IRQ_MASK); - ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0, - dev_name(&pdev->dev), master); - if (ret) - return ret; - platform_set_drvdata(pdev, master); master->free_rr_slots = GENMASK(ADI_MAX_DEVS, 1); - - writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK); - spin_lock_init(&master->ibi.lock); master->ibi.num_slots = 15; master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots, @@ -986,6 +982,13 @@ static int adi_i3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); + ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0, + dev_name(&pdev->dev), master); + if (ret) + return ret; + + writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK); + return i3c_master_register(&master->base, &pdev->dev, &adi_i3c_master_ops, false); } diff --git a/drivers/i3c/master/amd-i3c-master.c b/drivers/i3c/master/amd-i3c-master.c new file mode 100644 index 000000000000..ef5ad5abb788 --- /dev/null +++ b/drivers/i3c/master/amd-i3c-master.c @@ -0,0 +1,1124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I3C master driver for the AMD I3C controller. + * + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i3c/master.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/unaligned.h> + +#define XI3C_VERSION_OFFSET 0x00 /* Version Register */ +#define XI3C_RESET_OFFSET 0x04 /* Soft Reset Register */ +#define XI3C_CR_OFFSET 0x08 /* Control Register */ +#define XI3C_ADDRESS_OFFSET 0x0C /* Target Address Register */ +#define XI3C_SR_OFFSET 0x10 /* Status Register */ +#define XI3C_CMD_FIFO_OFFSET 0x20 /* I3C Command FIFO Register */ +#define XI3C_WR_FIFO_OFFSET 0x24 /* I3C Write Data FIFO Register */ +#define XI3C_RD_FIFO_OFFSET 0x28 /* I3C Read Data FIFO Register */ +#define XI3C_RESP_STATUS_FIFO_OFFSET 0x2C /* I3C Response status FIFO Register */ +#define XI3C_FIFO_LVL_STATUS_OFFSET 0x30 /* CMD slots free | WR-FIFO free (words) */ +#define XI3C_FIFO_LVL_STATUS_1_OFFSET 0x34 /* RESP fill | RD-FIFO fill level (words) */ +#define XI3C_SCL_HIGH_TIME_OFFSET 0x38 /* I3C SCL HIGH Register */ +#define XI3C_SCL_LOW_TIME_OFFSET 0x3C /* I3C SCL LOW Register */ +#define XI3C_SDA_HOLD_TIME_OFFSET 0x40 /* I3C SDA HOLD Register */ +#define XI3C_TSU_START_OFFSET 0x48 /* I3C START SETUP Register */ +#define XI3C_THD_START_OFFSET 0x4C /* I3C START HOLD Register */ +#define XI3C_TSU_STOP_OFFSET 0x50 /* I3C STOP Setup Register */ +#define XI3C_OD_SCL_HIGH_TIME_OFFSET 0x54 /* I3C OD SCL HIGH Register */ +#define XI3C_OD_SCL_LOW_TIME_OFFSET 0x58 /* I3C OD SCL LOW Register */ +#define XI3C_PID0_OFFSET 0x6C /* LSB 4 bytes of the PID */ +#define XI3C_PID1_BCR_DCR 0x70 /* MSB 2 bytes of the PID, BCR and DCR */ + +#define XI3C_CR_EN_MASK BIT(0) /* Core Enable */ +#define XI3C_CR_RESUME_MASK BIT(2) /* Core Resume */ +#define XI3C_SR_RESP_NOT_EMPTY_MASK BIT(4) /* Resp Fifo not empty status mask */ +#define XI3C_RD_FIFO_NOT_EMPTY_MASK BIT(15) /* Read Fifo not empty status mask */ + +#define XI3C_BCR_MASK GENMASK(23, 16) +#define XI3C_DCR_MASK GENMASK(31, 24) +#define XI3C_PID_MASK GENMASK_ULL(63, 16) +#define XI3C_TIMING_MASK GENMASK(17, 0) +#define XI3C_REV_NUM_MASK GENMASK(15, 8) +#define XI3C_PID1_MASK GENMASK(15, 0) +#define XI3C_FIFO_LEVEL_MASK GENMASK(15, 0) +#define XI3C_RESP_CODE_MASK GENMASK(8, 5) + +/* Controller response codes; PG439 page 34, Table 46 */ +#define XI3C_RESP_CODE_SUCCESS 0 /* Transfer completed OK */ +#define XI3C_RESP_CODE_NO_TARGET 2 /* 7E NACK: no target on bus */ +#define XI3C_RESP_CODE_NACK 3 /* Target NACK / DAA end */ +#define XI3C_RESP_CODE_READ_EARLY_TERM 5 /* Target ended read early (T-bit), short read */ + +/* xi3c_get_response() private return: read ended early with valid data; resume needed */ +#define XI3C_XFER_SHORT_READ 1 + +#define XI3C_RESP_BYTES_MASK GENMASK(20, 9) /* NUM_BYTES processed */ +#define XI3C_ADDR_MASK GENMASK(6, 0) +#define XI3C_FIFOS_RST_MASK GENMASK(4, 1) + +/* Command FIFO word layout (bit ranges encoded in the GENMASK/BIT args) */ +#define XI3C_CMD_TYPE GENMASK(3, 0) /* command type */ +#define XI3C_CMD_TERMINATE BIT(4) /* terminate (last cmd of xfer) */ +#define XI3C_CMD_ADDR GENMASK(15, 8) /* target address << 1 | RnW */ +#define XI3C_CMD_LEN GENMASK(27, 16) /* payload length in bytes */ +#define XI3C_CMD_TID GENMASK(31, 28) /* transfer ID */ + +/* tLOW_OD open-drain SCL low; MIPI I3C v1.1.1 Table 74 min 200 ns, 500 ns chosen conservatively */ +#define XI3C_OD_TLOW_NS 500 +/* Open-drain SCL high (tHIGH) max; MIPI I3C v1.1.1 Table 74 (41 ns) */ +#define XI3C_OD_THIGH_NS 41 +/* + * tSU_STA/tHD_STA/tSU_STO min, 400 kHz/Fm; MIPI I3C v1.1.1 Table 73 + * (mixed bus with legacy I2C device) + */ +#define XI3C_I2C_TCASMIN_NS 600 +/* tSU_STA/tHD_STA/tSU_STO min, 1 MHz/Fm+; MIPI I3C v1.1.1 Table 73 (pure I3C bus) */ +#define XI3C_TCASMIN_NS 260 +/* Max payload per transfer: 12-bit CMD length field (XI3C_CMD_LEN); PG439 page 32, Table 42 */ +#define XI3C_MAXDATA_LENGTH 4095 +/* Max enumerated devices; PG439 page 27, AXI_I3C_IBI_TARGET_ADDR register detail */ +#define XI3C_MAX_DEVS 128 +/* DAA target response = 48-bit PID + BCR + DCR = 8 bytes; PG439 page 28 */ +#define XI3C_DAA_SLAVEINFO_READ_BYTECOUNT 8 + +/* + * Min SDA hold cycles, rev 0 IP. Revision-specific value, PG439 page 24, + * AXI_I3C_SDA_HOLD_TIME register detail + */ +#define XI3C_THOLD_MIN_REV0 5 +/* + * Min SDA hold cycles, rev >= 1 IP. Revision-specific value, PG439 page 24, + * AXI_I3C_SDA_HOLD_TIME register detail + */ +#define XI3C_THOLD_MIN_REV1 6 +/* + * SCL/SDA pre-bias to account for the HW pipeline. PG439 page 24, + * AXI_I3C_SDA_HOLD_TIME register detail + */ +#define XI3C_CYCLE_ADJUST 2 +/* Short settling delay so the FIFO reset assert/de-assert takes effect before the FIFOs are used */ +#define XI3C_FIFO_RESET_DELAY_US 10 +/* + * Poll/sleep slice for FIFO and response waits: small enough to stay + * responsive, avoids busy-waiting + */ +#define XI3C_POLL_INTERVAL_US 10 + +#define XI3C_I2C_MODE 0 +#define XI3C_I2C_TID 0 +#define XI3C_SDR_MODE 1 +#define XI3C_SDR_TID 1 + +#define XI3C_WORD_LEN 4 + +/* Software guard: 500 ms (us, for readl_poll_timeout) to bail out if no response word arrives */ +#define XI3C_RESP_TIMEOUT_US 500000 +/* Software guard: 1 s (ms, for msecs_to_jiffies) to bail out if a transfer never completes */ +#define XI3C_XFER_TIMEOUT_MS 1000 + +struct xi3c_cmd { + const void *tx_buf; + void *rx_buf; + u16 tx_len; + u16 rx_len; + u16 rx_actual; + u8 addr; + u8 type; + u8 tid; + bool rnw; + bool is_daa; + bool continued; + enum i3c_error_code err; +}; + +struct xi3c_xfer { + unsigned int ncmds; + unsigned int nissued; + struct xi3c_cmd cmds[] __counted_by(ncmds); +}; + +/** + * struct xi3c_master - I3C master controller state. + * @base: I3C master controller embedded by the framework. + * @dev: Pointer to the backing device structure. + * @membase: Memory base of the HW registers. + * @pclk: Input clock driving the controller. + * @lock: Serializes transfers and CCC submission. + * @daa: ENTDAA enumeration state. + * @daa.addrs: Dynamic addresses assigned in enumeration order. + * @daa.index: Number of responders enumerated so far. + */ +struct xi3c_master { + struct i3c_master_controller base; + struct device *dev; + void __iomem *membase; + struct clk *pclk; + struct mutex lock; /* serializes transfers and CCC submission */ + struct { + u8 addrs[XI3C_MAX_DEVS]; + u8 index; + } daa; +}; + +static inline struct xi3c_master * +to_xi3c_master(struct i3c_master_controller *master) +{ + return container_of(master, struct xi3c_master, base); +} + +static inline u8 xi3c_get_revision_number(struct xi3c_master *master) +{ + return FIELD_GET(XI3C_REV_NUM_MASK, + ioread32(master->membase + XI3C_VERSION_OFFSET)); +} + +static inline u16 xi3c_wr_fifo_level(struct xi3c_master *master) +{ + return ioread32(master->membase + XI3C_FIFO_LVL_STATUS_OFFSET) & + XI3C_FIFO_LEVEL_MASK; +} + +static inline u16 xi3c_rd_fifo_level(struct xi3c_master *master) +{ + return ioread32(master->membase + XI3C_FIFO_LVL_STATUS_1_OFFSET) & + XI3C_FIFO_LEVEL_MASK; +} + +static inline bool xi3c_is_resp_available(struct xi3c_master *master) +{ + return FIELD_GET(XI3C_SR_RESP_NOT_EMPTY_MASK, + ioread32(master->membase + XI3C_SR_OFFSET)); +} + +static int xi3c_get_response(struct xi3c_master *master, struct xi3c_cmd *cmd) +{ + u32 response_data; + u32 resp_reg; + u8 code; + int ret; + + ret = readl_poll_timeout(master->membase + XI3C_SR_OFFSET, + resp_reg, + resp_reg & XI3C_SR_RESP_NOT_EMPTY_MASK, + XI3C_POLL_INTERVAL_US, XI3C_RESP_TIMEOUT_US); + if (ret) { + dev_err(master->dev, "XI3C response timeout\n"); + return ret; + } + + response_data = ioread32(master->membase + XI3C_RESP_STATUS_FIFO_OFFSET); + code = FIELD_GET(XI3C_RESP_CODE_MASK, response_data); + + switch (code) { + case XI3C_RESP_CODE_SUCCESS: + cmd->err = I3C_ERROR_UNKNOWN; + cmd->rx_actual = FIELD_GET(XI3C_RESP_BYTES_MASK, response_data); + return 0; + case XI3C_RESP_CODE_READ_EARLY_TERM: + /* Short read: valid data, but controller parked in STOP and must be resumed */ + cmd->err = I3C_ERROR_UNKNOWN; + cmd->rx_actual = FIELD_GET(XI3C_RESP_BYTES_MASK, response_data); + return XI3C_XFER_SHORT_READ; + case XI3C_RESP_CODE_NO_TARGET: + case XI3C_RESP_CODE_NACK: + cmd->err = I3C_ERROR_M2; + return cmd->is_daa ? -ENODEV : -EIO; + default: + cmd->err = I3C_ERROR_M0; + dev_err(master->dev, "XI3C transfer error, response code %u\n", + code); + return -EIO; + } +} + +static inline void xi3c_writesl_be(void __iomem *addr, const void *buffer, + unsigned int count) +{ + const u32 *buf = buffer; + + while (count--) + iowrite32be(get_unaligned(buf++), addr); +} + +static inline void xi3c_readsl_be(const void __iomem *addr, void *buffer, + unsigned int count) +{ + u32 *buf = buffer; + + while (count--) + put_unaligned(ioread32be(addr), buf++); +} + +static inline void xi3c_writel_fifo(void __iomem *addr, const void *buf, + int nbytes) +{ + xi3c_writesl_be(addr, buf, nbytes / 4); + if (nbytes & 3) { + u32 tmp = 0; + + memcpy(&tmp, (const u8 *)buf + (nbytes & ~3), nbytes & 3); + xi3c_writesl_be(addr, &tmp, 1); + } +} + +static inline void xi3c_readl_fifo(const void __iomem *addr, void *buf, + int nbytes) +{ + xi3c_readsl_be(addr, buf, nbytes / 4); + if (nbytes & 3) { + u32 tmp; + + xi3c_readsl_be(addr, &tmp, 1); + memcpy((u8 *)buf + (nbytes & ~3), &tmp, nbytes & 3); + } +} + +static void xi3c_master_write_to_cmdfifo(struct xi3c_master *master, + struct xi3c_cmd *cmd, u16 len) +{ + u32 transfer_cmd; + u8 addr; + + addr = ((cmd->addr & XI3C_ADDR_MASK) << 1) | (u8)cmd->rnw; + + transfer_cmd = FIELD_PREP(XI3C_CMD_TYPE, cmd->type); + transfer_cmd |= FIELD_PREP(XI3C_CMD_TERMINATE, !cmd->continued); + transfer_cmd |= FIELD_PREP(XI3C_CMD_ADDR, addr); + transfer_cmd |= FIELD_PREP(XI3C_CMD_TID, cmd->tid); + + /* + * For dynamic addressing, an additional 1-byte length must be added + * to the command FIFO to account for the address present in the TX FIFO + */ + if (cmd->is_daa) { + xi3c_writel_fifo(master->membase + XI3C_WR_FIFO_OFFSET, + cmd->tx_buf, cmd->tx_len); + + len++; + } + + transfer_cmd |= FIELD_PREP(XI3C_CMD_LEN, len); + iowrite32(transfer_cmd, master->membase + XI3C_CMD_FIFO_OFFSET); +} + +static inline void xi3c_master_enable(struct xi3c_master *master) +{ + iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) | XI3C_CR_EN_MASK, + master->membase + XI3C_CR_OFFSET); +} + +static inline void xi3c_master_disable(struct xi3c_master *master) +{ + iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) & ~XI3C_CR_EN_MASK, + master->membase + XI3C_CR_OFFSET); +} + +static inline void xi3c_master_resume(struct xi3c_master *master) +{ + iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) | + XI3C_CR_RESUME_MASK, master->membase + XI3C_CR_OFFSET); +} + +static void xi3c_master_reset_fifos(struct xi3c_master *master) +{ + u32 data; + + /* Assert FIFO reset. */ + data = ioread32(master->membase + XI3C_RESET_OFFSET); + data |= XI3C_FIFOS_RST_MASK; + iowrite32(data, master->membase + XI3C_RESET_OFFSET); + /* Read-back flushes the posted write before the settling delay below. */ + ioread32(master->membase + XI3C_RESET_OFFSET); + fsleep(XI3C_FIFO_RESET_DELAY_US); + + /* De-assert FIFO reset, then wait for the FIFOs to come back up. */ + data &= ~XI3C_FIFOS_RST_MASK; + iowrite32(data, master->membase + XI3C_RESET_OFFSET); + ioread32(master->membase + XI3C_RESET_OFFSET); + fsleep(XI3C_FIFO_RESET_DELAY_US); +} + +static inline void xi3c_master_init(struct xi3c_master *master) +{ + /* Reset fifos */ + xi3c_master_reset_fifos(master); + + /* Enable controller */ + xi3c_master_enable(master); +} + +static inline void xi3c_master_reinit(struct xi3c_master *master) +{ + /* Reset fifos */ + xi3c_master_reset_fifos(master); + + /* Resume controller */ + xi3c_master_resume(master); +} + +static struct xi3c_xfer *xi3c_master_alloc_xfer(unsigned int ncmds) +{ + struct xi3c_xfer *xfer; + + xfer = kzalloc_flex(*xfer, cmds, ncmds, GFP_KERNEL); + if (!xfer) + return NULL; + + xfer->ncmds = ncmds; + + return xfer; +} + +static void xi3c_master_rd_from_rx_fifo(struct xi3c_master *master, + struct xi3c_cmd *cmd) +{ + u16 rx_data_available; + u16 copy_len; + u16 len; + + rx_data_available = xi3c_rd_fifo_level(master); + len = rx_data_available * XI3C_WORD_LEN; + + if (!len) + return; + + copy_len = min(len, cmd->rx_len); + xi3c_readl_fifo(master->membase + XI3C_RD_FIFO_OFFSET, + (u8 *)cmd->rx_buf, copy_len); + + cmd->rx_buf = (u8 *)cmd->rx_buf + copy_len; + cmd->rx_len -= copy_len; +} + +static int xi3c_master_read(struct xi3c_master *master, struct xi3c_cmd *cmd) +{ + unsigned long timeout; + u32 status_reg; + int ret; + + if (cmd->rx_len > XI3C_MAXDATA_LENGTH) + return -EINVAL; + /* + * Zero-length probes (e.g. i2cdetect) legitimately pass a NULL + * buffer; only a non-zero length requires one. + */ + if (cmd->rx_len && !cmd->rx_buf) + return -EINVAL; + + /* Fill command fifo */ + xi3c_master_write_to_cmdfifo(master, cmd, cmd->rx_len); + + if (!cmd->rx_len) + return 0; + + ret = readl_poll_timeout(master->membase + XI3C_SR_OFFSET, + status_reg, + status_reg & (XI3C_RD_FIFO_NOT_EMPTY_MASK | + XI3C_SR_RESP_NOT_EMPTY_MASK), + XI3C_POLL_INTERVAL_US, XI3C_RESP_TIMEOUT_US); + if (ret) { + dev_err(master->dev, "XI3C read timeout\n"); + return ret; + } + + if (!(status_reg & XI3C_RD_FIFO_NOT_EMPTY_MASK)) + return 0; + + timeout = jiffies + msecs_to_jiffies(XI3C_XFER_TIMEOUT_MS); + + /* Read data from rx fifo */ + while (cmd->rx_len > 0 && !xi3c_is_resp_available(master)) { + if (time_after(jiffies, timeout)) { + dev_err(master->dev, "XI3C read timeout\n"); + return -EIO; + } + xi3c_master_rd_from_rx_fifo(master, cmd); + usleep_range(XI3C_POLL_INTERVAL_US, 2 * XI3C_POLL_INTERVAL_US); + } + + /* Read remaining data */ + xi3c_master_rd_from_rx_fifo(master, cmd); + + return 0; +} + +static void xi3c_master_wr_to_tx_fifo(struct xi3c_master *master, + struct xi3c_cmd *cmd) +{ + u16 wrfifo_space; + u16 len; + + wrfifo_space = xi3c_wr_fifo_level(master); + if (cmd->tx_len > wrfifo_space * XI3C_WORD_LEN) + len = wrfifo_space * XI3C_WORD_LEN; + else + len = cmd->tx_len; + + if (len) { + xi3c_writel_fifo(master->membase + XI3C_WR_FIFO_OFFSET, cmd->tx_buf, + len); + + cmd->tx_buf = (const u8 *)cmd->tx_buf + len; + cmd->tx_len -= len; + } +} + +static int xi3c_master_write(struct xi3c_master *master, struct xi3c_cmd *cmd) +{ + unsigned long timeout; + u16 cmd_len; + + if (cmd->tx_len > XI3C_MAXDATA_LENGTH) + return -EINVAL; + /* + * Zero-length probes (e.g. i2cdetect) legitimately pass a NULL + * buffer; only a non-zero length requires one. + */ + if (cmd->tx_len && !cmd->tx_buf) + return -EINVAL; + + cmd_len = cmd->tx_len; + + /* Fill Tx fifo */ + xi3c_master_wr_to_tx_fifo(master, cmd); + + /* Write to command fifo */ + xi3c_master_write_to_cmdfifo(master, cmd, cmd_len); + + timeout = jiffies + msecs_to_jiffies(XI3C_XFER_TIMEOUT_MS); + /* Fill if any remaining data to tx fifo */ + while (cmd->tx_len > 0 && !xi3c_is_resp_available(master)) { + if (time_after(jiffies, timeout)) { + dev_err(master->dev, "XI3C write timeout\n"); + return -EIO; + } + + xi3c_master_wr_to_tx_fifo(master, cmd); + usleep_range(XI3C_POLL_INTERVAL_US, 2 * XI3C_POLL_INTERVAL_US); + } + + return 0; +} + +static int xi3c_master_xfer(struct xi3c_master *master, struct xi3c_cmd *cmd) +{ + int ret; + + if (cmd->rnw) + ret = xi3c_master_read(master, cmd); + else + ret = xi3c_master_write(master, cmd); + + if (ret) + goto err_xfer_out; + + ret = xi3c_get_response(master, cmd); + if (ret < 0) + goto err_xfer_out; + + /* Short read leaves the controller parked in STOP; resume it for the next command */ + if (ret == XI3C_XFER_SHORT_READ) + xi3c_master_resume(master); + + return 0; + +err_xfer_out: + xi3c_master_reinit(master); + return ret; +} + +static int xi3c_master_common_xfer(struct xi3c_master *master, + struct xi3c_xfer *xfer) +{ + unsigned int i; + int ret; + + guard(mutex)(&master->lock); + + for (i = 0; i < xfer->ncmds; i++) { + ret = xi3c_master_xfer(master, &xfer->cmds[i]); + if (ret) { + /* Count commands sent on the bus; the rest never ran */ + xfer->nissued = i + 1; + return ret; + } + } + + xfer->nissued = xfer->ncmds; + + return 0; +} + +static int xi3c_master_do_daa(struct i3c_master_controller *m) +{ + u8 (*pid_bufs)[XI3C_DAA_SLAVEINFO_READ_BYTECOUNT]; + struct xi3c_master *master = to_xi3c_master(m); + struct xi3c_cmd *daa_cmd; + struct xi3c_xfer *xfer; + int addr, ret, i; + u8 last_addr = 0; + u8 *pid_buf; + u8 ccc_id; + + xfer = xi3c_master_alloc_xfer(1); + if (!xfer) + return -ENOMEM; + + pid_bufs = kcalloc(XI3C_MAX_DEVS, XI3C_DAA_SLAVEINFO_READ_BYTECOUNT, + GFP_KERNEL); + if (!pid_bufs) { + ret = -ENOMEM; + goto out; + } + + /* Fill ENTDAA CCC */ + ccc_id = I3C_CCC_ENTDAA; + daa_cmd = &xfer->cmds[0]; + daa_cmd->addr = I3C_BROADCAST_ADDR; + daa_cmd->rnw = false; + daa_cmd->tx_buf = &ccc_id; + daa_cmd->tx_len = 1; + daa_cmd->type = XI3C_SDR_MODE; + daa_cmd->tid = XI3C_SDR_TID; + daa_cmd->continued = true; + + ret = xi3c_master_common_xfer(master, xfer); + if (ret) { + /* NACK on ENTDAA broadcast means no devices to enumerate */ + if (daa_cmd->err == I3C_ERROR_M2) + ret = 0; + goto err_daa; + } + + master->daa.index = 0; + + while (true) { + struct xi3c_cmd *cmd = &xfer->cmds[0]; + u8 daa_byte; + + /* Out of device slots; stop and keep what was enumerated */ + if (master->daa.index >= XI3C_MAX_DEVS) { + dev_warn(master->dev, + "DAA: reached %d devices, stopping enumeration\n", + XI3C_MAX_DEVS); + xi3c_master_reinit(master); + break; + } + + addr = i3c_master_get_free_addr(m, last_addr + 1); + if (addr < 0) { + dev_warn(master->dev, + "DAA: no free dynamic address, stopping enumeration\n"); + xi3c_master_reinit(master); + break; + } + + pid_buf = pid_bufs[master->daa.index]; + + daa_byte = (addr << 1) | (parity8(addr) ^ 1); + + cmd->tx_buf = &daa_byte; + cmd->tx_len = 1; + cmd->addr = I3C_BROADCAST_ADDR; + cmd->rnw = true; + cmd->rx_buf = pid_buf; + cmd->rx_len = XI3C_DAA_SLAVEINFO_READ_BYTECOUNT; + cmd->is_daa = true; + cmd->type = XI3C_SDR_MODE; + cmd->tid = XI3C_SDR_TID; + cmd->continued = true; + + ret = xi3c_master_common_xfer(master, xfer); + + /* -ENODEV: no more responders, enumeration complete */ + if (ret == -ENODEV) { + ret = 0; + break; + } + if (ret) + goto err_daa; + + master->daa.addrs[master->daa.index] = addr; + last_addr = addr; + master->daa.index++; + } + + for (i = 0; i < master->daa.index; i++) { + u64 pid; + + /* Ignore per-device add errors so one failure doesn't abort the rest */ + i3c_master_add_i3c_dev_locked(m, master->daa.addrs[i]); + + pid = FIELD_GET(XI3C_PID_MASK, + get_unaligned_be64(pid_bufs[i])); + dev_dbg(master->dev, "Client %d: PID: 0x%llx\n", i, pid); + } + + ret = 0; + goto out; + +err_daa: + xi3c_master_reinit(master); +out: + kfree(pid_bufs); + kfree(xfer); + return ret; +} + +static bool +xi3c_master_supports_ccc_cmd(struct i3c_master_controller *master, + const struct i3c_ccc_cmd *cmd) +{ + if (cmd->ndests > 1) + return false; + + switch (cmd->id) { + case I3C_CCC_ENEC(true): + case I3C_CCC_ENEC(false): + case I3C_CCC_DISEC(true): + case I3C_CCC_DISEC(false): + case I3C_CCC_ENTAS(0, true): + case I3C_CCC_ENTAS(0, false): + case I3C_CCC_RSTDAA(true): + case I3C_CCC_RSTDAA(false): + case I3C_CCC_ENTDAA: + case I3C_CCC_SETMWL(true): + case I3C_CCC_SETMWL(false): + case I3C_CCC_SETMRL(true): + case I3C_CCC_SETMRL(false): + case I3C_CCC_SETDASA: + case I3C_CCC_SETNEWDA: + case I3C_CCC_GETMWL: + case I3C_CCC_GETMRL: + case I3C_CCC_GETPID: + case I3C_CCC_GETBCR: + case I3C_CCC_GETDCR: + case I3C_CCC_GETSTATUS: + case I3C_CCC_GETMXDS: + return true; + default: + return false; + } +} + +static int xi3c_master_send_bdcast_ccc_cmd(struct xi3c_master *master, + struct i3c_ccc_cmd *ccc) +{ + struct xi3c_xfer *xfer __free(kfree) = NULL; + u8 *buf __free(kfree) = NULL; + struct xi3c_cmd *cmd; + u16 xfer_len; + int ret; + + if (ccc->dests[0].payload.len >= XI3C_MAXDATA_LENGTH) + return -EINVAL; + + xfer_len = ccc->dests[0].payload.len + 1; + + xfer = xi3c_master_alloc_xfer(1); + if (!xfer) + return -ENOMEM; + + buf = kmalloc_objs(*buf, xfer_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = ccc->id; + memcpy(&buf[1], ccc->dests[0].payload.data, ccc->dests[0].payload.len); + + cmd = &xfer->cmds[0]; + cmd->addr = ccc->dests[0].addr; + cmd->rnw = ccc->rnw; + cmd->tx_buf = buf; + cmd->tx_len = xfer_len; + cmd->type = XI3C_SDR_MODE; + cmd->tid = XI3C_SDR_TID; + cmd->continued = false; + + ret = xi3c_master_common_xfer(master, xfer); + ccc->err = cmd->err; + + return ret; +} + +static int xi3c_master_send_direct_ccc_cmd(struct xi3c_master *master, + struct i3c_ccc_cmd *ccc) +{ + struct xi3c_xfer *xfer __free(kfree) = NULL; + struct xi3c_cmd *cmd; + int ret; + + if (ccc->dests[0].payload.len > XI3C_MAXDATA_LENGTH) + return -EINVAL; + + xfer = xi3c_master_alloc_xfer(2); + if (!xfer) + return -ENOMEM; + + /* Broadcasted message */ + cmd = &xfer->cmds[0]; + cmd->addr = I3C_BROADCAST_ADDR; + cmd->rnw = false; + cmd->tx_buf = &ccc->id; + cmd->tx_len = 1; + cmd->type = XI3C_SDR_MODE; + cmd->tid = XI3C_SDR_TID; + cmd->continued = true; + + /* Directed message */ + cmd = &xfer->cmds[1]; + cmd->addr = ccc->dests[0].addr; + cmd->rnw = ccc->rnw; + if (cmd->rnw) { + cmd->rx_buf = ccc->dests[0].payload.data; + cmd->rx_len = ccc->dests[0].payload.len; + } else { + cmd->tx_buf = ccc->dests[0].payload.data; + cmd->tx_len = ccc->dests[0].payload.len; + } + cmd->type = XI3C_SDR_MODE; + cmd->tid = XI3C_SDR_TID; + cmd->continued = false; + + ret = xi3c_master_common_xfer(master, xfer); + + /* + * Report broadcast error if any, else the directed one, so either + * NACK reaches the caller + */ + ccc->err = xfer->cmds[0].err ? xfer->cmds[0].err : xfer->cmds[1].err; + + /* Report actual byte count so the core sees the right length on short reads */ + if (!ret && ccc->rnw) + ccc->dests[0].payload.len = min(xfer->cmds[1].rx_actual, + ccc->dests[0].payload.len); + + return ret; +} + +static int xi3c_master_send_ccc_cmd(struct i3c_master_controller *m, + struct i3c_ccc_cmd *cmd) +{ + struct xi3c_master *master = to_xi3c_master(m); + + if (cmd->id & I3C_CCC_DIRECT) + return xi3c_master_send_direct_ccc_cmd(master, cmd); + + return xi3c_master_send_bdcast_ccc_cmd(master, cmd); +} + +static int xi3c_master_i3c_xfers(struct i3c_dev_desc *dev, + struct i3c_xfer *xfers, + int nxfers, enum i3c_xfer_mode mode) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct xi3c_master *master = to_xi3c_master(m); + struct xi3c_xfer *xfer __free(kfree) = NULL; + int i, ret; + + if (!nxfers) + return 0; + + if (mode != I3C_SDR) + return -EOPNOTSUPP; + + for (i = 0; i < nxfers; i++) + if (xfers[i].len > XI3C_MAXDATA_LENGTH) + return -EINVAL; + + xfer = xi3c_master_alloc_xfer(nxfers); + if (!xfer) + return -ENOMEM; + + for (i = 0; i < nxfers; i++) { + struct xi3c_cmd *cmd = &xfer->cmds[i]; + + cmd->addr = dev->info.dyn_addr; + cmd->rnw = xfers[i].rnw; + + if (cmd->rnw) { + cmd->rx_buf = xfers[i].data.in; + cmd->rx_len = xfers[i].len; + } else { + cmd->tx_buf = xfers[i].data.out; + cmd->tx_len = xfers[i].len; + } + + cmd->type = XI3C_SDR_MODE; + cmd->tid = XI3C_SDR_TID; + cmd->continued = (i + 1) < nxfers; + } + + ret = xi3c_master_common_xfer(master, xfer); + + for (i = 0; i < xfer->nissued; i++) { + xfers[i].err = xfer->cmds[i].err; + if (xfers[i].rnw) + xfers[i].actual_len = min(xfer->cmds[i].rx_actual, + xfers[i].len); + } + + return ret; +} + +static int xi3c_master_i2c_xfers(struct i2c_dev_desc *dev, + struct i2c_msg *xfers, + int nxfers) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct xi3c_master *master = to_xi3c_master(m); + struct xi3c_xfer *xfer __free(kfree) = NULL; + int i; + + if (!nxfers) + return 0; + + for (i = 0; i < nxfers; i++) + if (xfers[i].len > XI3C_MAXDATA_LENGTH) + return -EINVAL; + + xfer = xi3c_master_alloc_xfer(nxfers); + if (!xfer) + return -ENOMEM; + + for (i = 0; i < nxfers; i++) { + struct xi3c_cmd *cmd = &xfer->cmds[i]; + + cmd->addr = xfers[i].addr & XI3C_ADDR_MASK; + cmd->rnw = !!(xfers[i].flags & I2C_M_RD); + + if (cmd->rnw) { + cmd->rx_buf = xfers[i].buf; + cmd->rx_len = xfers[i].len; + } else { + cmd->tx_buf = xfers[i].buf; + cmd->tx_len = xfers[i].len; + } + + cmd->type = XI3C_I2C_MODE; + cmd->tid = XI3C_I2C_TID; + cmd->continued = (i + 1) < nxfers; + } + + return xi3c_master_common_xfer(master, xfer); +} + +static int xi3c_clk_cfg(struct xi3c_master *master, unsigned long sclhz, u8 mode) +{ + unsigned long core_rate, core_periodns; + u32 tcasmin, tsustart, tsustop, thdstart; + u32 thigh, tlow, thold; + u32 odthigh, odtlow; + + core_rate = clk_get_rate(master->pclk); + if (!core_rate) + return -EINVAL; + + if (!sclhz) + return -EINVAL; + + core_periodns = DIV_ROUND_UP(NSEC_PER_SEC, core_rate); + + thigh = DIV_ROUND_UP(core_rate, sclhz) >> 1; + tlow = thigh; + + /* Reject rates whose timing exceeds the 18-bit registers (would wrap) */ + if (thigh <= XI3C_CYCLE_ADJUST || + (thigh - XI3C_CYCLE_ADJUST) > XI3C_TIMING_MASK) + return -EINVAL; + + /* Hold time : 40% of tlow time */ + thold = (tlow * 4) / 10; + + if (xi3c_get_revision_number(master) == 0) + thold = max_t(u32, thold, XI3C_THOLD_MIN_REV0); + else + thold = max_t(u32, thold, XI3C_THOLD_MIN_REV1); + + iowrite32((thigh - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_SCL_HIGH_TIME_OFFSET); + iowrite32((tlow - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_SCL_LOW_TIME_OFFSET); + iowrite32((thold - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_SDA_HOLD_TIME_OFFSET); + + if (mode == XI3C_I2C_MODE) { + iowrite32((thigh - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_OD_SCL_HIGH_TIME_OFFSET); + iowrite32((tlow - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_OD_SCL_LOW_TIME_OFFSET); + + tcasmin = DIV_ROUND_UP(XI3C_I2C_TCASMIN_NS, core_periodns); + } else { + odtlow = DIV_ROUND_UP(XI3C_OD_TLOW_NS, core_periodns); + odthigh = DIV_ROUND_UP(XI3C_OD_THIGH_NS, core_periodns); + + odtlow = max(tlow, odtlow); + odthigh = min(thigh, odthigh); + + if (odthigh <= XI3C_CYCLE_ADJUST) + return -EINVAL; + + iowrite32((odthigh - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_OD_SCL_HIGH_TIME_OFFSET); + iowrite32((odtlow - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_OD_SCL_LOW_TIME_OFFSET); + + tcasmin = DIV_ROUND_UP(XI3C_TCASMIN_NS, core_periodns); + } + + thdstart = max(thigh, tcasmin); + tsustart = max(tlow, tcasmin); + tsustop = max(tlow, tcasmin); + + iowrite32((tsustart - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_TSU_START_OFFSET); + iowrite32((thdstart - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_THD_START_OFFSET); + iowrite32((tsustop - XI3C_CYCLE_ADJUST) & XI3C_TIMING_MASK, + master->membase + XI3C_TSU_STOP_OFFSET); + + return 0; +} + +static int xi3c_master_bus_init(struct i3c_master_controller *m) +{ + struct xi3c_master *master = to_xi3c_master(m); + struct i3c_bus *bus = i3c_master_get_bus(m); + struct i3c_device_info info = {}; + unsigned long sclhz; + u32 pid1_bcr_dcr; + u8 mode; + int ret; + + switch (bus->mode) { + case I3C_BUS_MODE_MIXED_FAST: + case I3C_BUS_MODE_MIXED_LIMITED: + case I3C_BUS_MODE_MIXED_SLOW: + mode = XI3C_I2C_MODE; + sclhz = bus->scl_rate.i2c; + break; + case I3C_BUS_MODE_PURE: + mode = XI3C_SDR_MODE; + sclhz = bus->scl_rate.i3c; + break; + default: + return -EINVAL; + } + + ret = xi3c_clk_cfg(master, sclhz, mode); + if (ret) + return ret; + + xi3c_master_init(master); + + /* Get an address for the master. */ + ret = i3c_master_get_free_addr(m, 0); + if (ret < 0) + return ret; + + info.dyn_addr = ret; + + /* Write the dynamic address value to the address register. */ + iowrite32(info.dyn_addr, master->membase + XI3C_ADDRESS_OFFSET); + + /* Read PID, BCR and DCR values, and assign to i3c device info. */ + pid1_bcr_dcr = ioread32(master->membase + XI3C_PID1_BCR_DCR); + info.pid = ((u64)FIELD_GET(XI3C_PID1_MASK, pid1_bcr_dcr) << 32) | + ioread32(master->membase + XI3C_PID0_OFFSET); + info.bcr = FIELD_GET(XI3C_BCR_MASK, pid1_bcr_dcr); + info.dcr = FIELD_GET(XI3C_DCR_MASK, pid1_bcr_dcr); + + return i3c_master_set_info(&master->base, &info); +} + +static void xi3c_master_bus_cleanup(struct i3c_master_controller *m) +{ + struct xi3c_master *master = to_xi3c_master(m); + + xi3c_master_disable(master); +} + +static const struct i3c_master_controller_ops xi3c_master_ops = { + .bus_init = xi3c_master_bus_init, + .bus_cleanup = xi3c_master_bus_cleanup, + .do_daa = xi3c_master_do_daa, + .supports_ccc_cmd = xi3c_master_supports_ccc_cmd, + .send_ccc_cmd = xi3c_master_send_ccc_cmd, + .i3c_xfers = xi3c_master_i3c_xfers, + .i2c_xfers = xi3c_master_i2c_xfers, +}; + +static int xi3c_master_probe(struct platform_device *pdev) +{ + struct xi3c_master *master; + int ret; + + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + master->dev = &pdev->dev; + + master->membase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(master->membase)) + return dev_err_probe(master->dev, PTR_ERR(master->membase), + "Failed to map registers\n"); + + master->pclk = devm_clk_get_enabled(master->dev, NULL); + if (IS_ERR(master->pclk)) + return dev_err_probe(master->dev, PTR_ERR(master->pclk), + "Failed to get and enable clock\n"); + + ret = devm_mutex_init(master->dev, &master->lock); + if (ret) + return ret; + + platform_set_drvdata(pdev, master); + + return i3c_master_register(&master->base, master->dev, + &xi3c_master_ops, false); +} + +static void xi3c_master_remove(struct platform_device *pdev) +{ + struct xi3c_master *master = platform_get_drvdata(pdev); + + i3c_master_unregister(&master->base); +} + +static const struct of_device_id xi3c_master_of_ids[] = { + { .compatible = "xlnx,axi-i3c-1.0" }, + { }, +}; +MODULE_DEVICE_TABLE(of, xi3c_master_of_ids); + +static struct platform_driver xi3c_master_driver = { + .probe = xi3c_master_probe, + .remove = xi3c_master_remove, + .driver = { + .name = "axi-i3c-master", + .of_match_table = xi3c_master_of_ids, + }, +}; +module_platform_driver(xi3c_master_driver); + +MODULE_AUTHOR("Manikanta Guntupalli <manikanta.guntupalli@amd.com>"); +MODULE_DESCRIPTION("AMD AXI I3C master driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 2f8c0c4683e0..4563d8761ba0 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -242,15 +242,16 @@ /* 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; u32 cmd_hi; u16 tx_len; - const void *tx_buf; u16 rx_len; - void *rx_buf; u8 error; + const void *tx_buf; + void *rx_buf; }; struct dw_i3c_xfer { @@ -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; @@ -493,6 +496,7 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr) break; case RESPONSE_ERROR_PARITY: case RESPONSE_ERROR_IBA_NACK: + case RESPONSE_ERROR_ADDRESS_NACK: case RESPONSE_ERROR_TRANSF_ABORT: case RESPONSE_ERROR_CRC: case RESPONSE_ERROR_FRAME: @@ -502,7 +506,6 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr) ret = -ENOSPC; break; case RESPONSE_ERROR_I2C_W_NACK_ERR: - case RESPONSE_ERROR_ADDRESS_NACK: default: ret = -EINVAL; break; @@ -554,13 +557,35 @@ 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 void amd_configure_od_pp_quirk(struct dw_i3c_master *master) +{ + master->i3c_od_timing = AMD_I3C_OD_TIMING; + master->i3c_od_timing_normal = AMD_I3C_OD_TIMING; + master->i3c_pp_timing = AMD_I3C_PP_TIMING; +} + 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; @@ -592,6 +617,18 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master) scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt); writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); master->i3c_od_timing = scl_timing; + master->i3c_od_timing_normal = scl_timing; + + /* + * AMD legacy platforms need fixed OD/PP timings. Cache them as the + * normal OD baseline so set_speed(NORMAL) restores AMD values, and + * set_speed(SLOW) can stretch HCNT while keeping the AMD LCNT. + */ + if (master->quirks & AMD_I3C_OD_PP_TIMING) { + amd_configure_od_pp_quirk(master); + writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); + writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); + } lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt; scl_timing = SCL_EXT_LCNT_1(lcnt); @@ -613,7 +650,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; @@ -708,12 +745,29 @@ static void dw_i3c_master_bus_cleanup(struct i3c_master_controller *m) dw_i3c_master_disable(master); } +static enum i3c_error_code dw_i3c_ccc_map_err(u8 dw_err) +{ + switch (dw_err) { + case RESPONSE_ERROR_IBA_NACK: + return I3C_ERROR_M2; + case RESPONSE_ERROR_CRC: + case RESPONSE_ERROR_PARITY: + case RESPONSE_ERROR_FRAME: + case RESPONSE_ERROR_TRANSF_ABORT: + return I3C_ERROR_M0; + default: + return I3C_ERROR_UNKNOWN; + } +} + static int dw_i3c_ccc_set(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) { struct dw_i3c_cmd *cmd; int ret, pos = 0; + ccc->err = I3C_ERROR_UNKNOWN; + if (ccc->id & I3C_CCC_DIRECT) { pos = dw_i3c_master_get_addr_pos(master, ccc->dests[0].addr); if (pos < 0) @@ -742,8 +796,8 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master, dw_i3c_master_dequeue_xfer(master, xfer); ret = xfer->ret; - if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK) - ccc->err = I3C_ERROR_M2; + cmd = &xfer->cmds[0]; + ccc->err = dw_i3c_ccc_map_err(cmd->error); return ret; } @@ -753,6 +807,8 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) struct dw_i3c_cmd *cmd; int ret, pos; + ccc->err = I3C_ERROR_UNKNOWN; + pos = dw_i3c_master_get_addr_pos(master, ccc->dests[0].addr); if (pos < 0) return pos; @@ -780,18 +836,14 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) dw_i3c_master_dequeue_xfer(master, xfer); ret = xfer->ret; - if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK) - ccc->err = I3C_ERROR_M2; + cmd = &xfer->cmds[0]; + ccc->err = dw_i3c_ccc_map_err(cmd->error); + if (!ret) + ccc->dests[0].payload.actual_len = cmd->rx_len; return ret; } -static void amd_configure_od_pp_quirk(struct dw_i3c_master *master) -{ - master->i3c_od_timing = AMD_I3C_OD_TIMING; - master->i3c_pp_timing = AMD_I3C_PP_TIMING; -} - static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, struct i3c_ccc_cmd *ccc) { @@ -801,13 +853,6 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, if (ccc->id == I3C_CCC_ENTDAA) return -EINVAL; - /* AMD platform specific OD and PP timings */ - if (master->quirks & AMD_I3C_OD_PP_TIMING) { - amd_configure_od_pp_quirk(master); - writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); - writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); - } - ret = pm_runtime_resume_and_get(master->dev); if (ret < 0) { dev_err(master->dev, @@ -876,7 +921,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) goto rpm_out; } cmd = &xfer->cmds[0]; - cmd->cmd_hi = 0x1; + cmd->rx_len = master->maxdevs; + cmd->cmd_hi = COMMAND_PORT_TRANSFER_ARG; cmd->cmd_lo = COMMAND_PORT_DEV_COUNT(master->maxdevs - pos) | COMMAND_PORT_DEV_INDEX(pos) | COMMAND_PORT_CMD(I3C_CCC_ENTDAA) | @@ -888,7 +934,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++) { @@ -1484,6 +1538,50 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int dw_i3c_master_set_speed(struct i3c_master_controller *m, + enum i3c_open_drain_speed speed) +{ + struct dw_i3c_master *master = to_dw_i3c_master(m); + unsigned long core_rate; + u32 scl_timing, od_hcnt; + u8 lcnt; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(master->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + switch (speed) { + case I3C_OPEN_DRAIN_SLOW_SPEED: + core_rate = dw_i3c_master_get_core_rate(master); + if (!core_rate) + return -EINVAL; + + lcnt = SCL_I3C_TIMING_LCNT(master->i3c_od_timing_normal); + od_hcnt = DIV_ROUND_UP_ULL((u64)I3C_BUS_THIGH_INIT_OD_MIN_NS * + core_rate, NSEC_PER_SEC) - 1; + if (od_hcnt < SCL_I3C_TIMING_CNT_MIN) + od_hcnt = SCL_I3C_TIMING_CNT_MIN; + else if (od_hcnt > U8_MAX) + od_hcnt = U8_MAX; + scl_timing = SCL_I3C_TIMING_HCNT(od_hcnt) | + SCL_I3C_TIMING_LCNT(lcnt); + writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); + master->i3c_od_timing = scl_timing; + break; + + case I3C_OPEN_DRAIN_NORMAL_SPEED: + writel(master->i3c_od_timing_normal, + master->regs + SCL_I3C_OD_TIMING); + master->i3c_od_timing = master->i3c_od_timing_normal; + break; + + default: + return -EINVAL; + } + + return 0; +} + static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m, unsigned int dev_nack_retry_cnt) { @@ -1538,6 +1636,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { .recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot, .enable_hotjoin = dw_i3c_master_enable_hotjoin, .disable_hotjoin = dw_i3c_master_disable_hotjoin, + .set_speed = dw_i3c_master_set_speed, .set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry, }; @@ -1571,20 +1670,34 @@ 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); - master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk"); + 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, "apb"); if (IS_ERR(master->pclk)) return PTR_ERR(master->pclk); master->core_rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, - "core_rst"); + NULL); if (IS_ERR(master->core_rst)) return PTR_ERR(master->core_rst); @@ -1634,15 +1747,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->has_ibi_data = true; writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL); - 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); @@ -1728,10 +1832,7 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master) static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master) { - /* AMD platform specific OD and PP timings */ - if (master->quirks & AMD_I3C_OD_PP_TIMING) - amd_configure_od_pp_quirk(master); - + /* Preserve cached OD timing; it may be the SLOW setting from set_speed(). */ writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING); writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); @@ -1832,11 +1933,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, @@ -1845,7 +1947,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/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index 28e9348f2153..17ad817d1f8e 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -46,6 +46,7 @@ struct dw_i3c_master { u32 dev_addr; u32 i3c_pp_timing; u32 i3c_od_timing; + u32 i3c_od_timing_normal; u32 ext_lcnt_timing; u32 bus_free_timing; u32 i2c_fm_timing; diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 6d221596ea35..2d98c1ce9b12 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -573,6 +573,7 @@ static void cdns_i3c_master_end_xfer_locked(struct cdns_i3c_master *master, cmd = &xfer->cmds[CMDR_CMDID(cmdr)]; rx_len = min_t(u32, CMDR_XFER_BYTES(cmdr), cmd->rx_len); cdns_i3c_master_rd_from_rx_fifo(master, cmd->rx_buf, rx_len); + cmd->rx_len = rx_len; cmd->error = CMDR_ERROR(cmdr); } @@ -714,6 +715,8 @@ static int cdns_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, ret = xfer->ret; cmd->err = cdns_i3c_cmd_get_err(&xfer->cmds[0]); + if (!ret && cmd->rnw) + cmd->dests[0].payload.actual_len = ccmd->rx_len; cdns_i3c_master_free_xfer(xfer); return ret; diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index e80aa1f5722e..dadf049bd4b5 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/module.h> +#include <linux/pci.h> #include <linux/platform_data/mipi-i3c-hci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -117,6 +118,22 @@ static inline struct i3c_hci *to_i3c_hci(struct i3c_master_controller *m) return container_of(m, struct i3c_hci, master); } +/** + * i3c_hci_sysdev() - Get the device to use for DMA and system PM + * @dev: Device the HCI controller is bound to + * + * When an IOMMU is enabled, DMA API calls must use the device that IOMMU + * setup was done for. Under PCI enumeration that is the PCI device, not + * the "mipi-i3c-hci" platform device below it. The same device owns + * system PM and wakeup configuration. + * + * Return: @dev's parent if it is a PCI device, otherwise @dev. + */ +struct device *i3c_hci_sysdev(struct device *dev) +{ + return dev->parent && dev_is_pci(dev->parent) ? dev->parent : dev; +} + static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci) { reg_write(MASTER_DEVICE_ADDR, @@ -375,7 +392,7 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, goto out; for (i = prefixed; i < nxfers; i++) { if (ccc->rnw) - ccc->dests[i - prefixed].payload.len = + ccc->dests[i - prefixed].payload.actual_len = RESP_DATA_LENGTH(xfer[i].response); switch (RESP_STATUS(xfer[i].response)) { case RESP_SUCCESS: @@ -392,7 +409,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, if (ccc->rnw) dev_dbg(&hci->master.dev, "got: %*ph", - ccc->dests[0].payload.len, ccc->dests[0].payload.data); + ccc->dests[0].payload.actual_len, + ccc->dests[0].payload.data); out: hci_free_xfer(xfer, nxfers); @@ -1031,6 +1049,7 @@ static int i3c_hci_init(struct i3c_hci *hci) switch (regval & ~0xf) { case 0x100: /* version 1.0 */ case 0x110: /* version 1.1 */ + case 0x120: /* version 1.2 */ case 0x200: /* version 2.0 */ break; default: @@ -1163,6 +1182,9 @@ static int i3c_hci_probe(struct platform_device *pdev) if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED) hci->master.rpm_ibi_allowed = true; + if (device_can_wakeup(i3c_hci_sysdev(&pdev->dev))) + hci->master.ibi_wakeup = true; + return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false); } @@ -1197,6 +1219,10 @@ static const struct platform_device_id i3c_hci_driver_ids[] = { HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET | HCI_QUIRK_DMA_REQUIRES_HC_ABORT, }, + { + .name = "amd-pt-i3c-hci", + .driver_data = HCI_QUIRK_RPM_ALLOWED, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 0672ed1132f8..7c2b20474130 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -15,7 +15,6 @@ #include <linux/errno.h> #include <linux/i3c/master.h> #include <linux/io.h> -#include <linux/pci.h> #include "hci.h" #include "cmd.h" @@ -301,23 +300,11 @@ static int hci_dma_init(struct i3c_hci *hci) { struct hci_rings_data *rings; struct hci_rh_data *rh; - struct device *sysdev; u32 regval; unsigned int i, nr_rings, xfers_sz, resps_sz; unsigned int ibi_status_ring_sz, ibi_data_ring_sz; int ret; - /* - * Set pointer to a physical device that does DMA and has IOMMU setup - * done for it in case of enabled IOMMU and use it with the DMA API. - * Here such device is either - * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or - * grandparent (PCI enumeration). - */ - sysdev = hci->master.dev.parent; - if (sysdev->parent && dev_is_pci(sysdev->parent)) - sysdev = sysdev->parent; - regval = rhs_reg_read(CONTROL); nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval); dev_dbg(&hci->master.dev, "%d DMA rings available\n", nr_rings); @@ -332,7 +319,7 @@ static int hci_dma_init(struct i3c_hci *hci) return -ENOMEM; hci->io_data = rings; rings->total = nr_rings; - rings->sysdev = sysdev; + rings->sysdev = i3c_hci_sysdev(hci->master.dev.parent); for (i = 0; i < rings->total; i++) { u32 offset = rhs_reg_read(RHn_OFFSET(i)); diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index b3d9803b1968..ee73f6e6756a 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -30,6 +30,11 @@ #define reg_set(r, v) reg_write(r, reg_read(r) | (v)) #define reg_clear(r, v) reg_write(r, reg_read(r) & ~(v)) +/* helper macro for HCI version check */ +#define hci_version_at_least(hci, maj, min) \ + ((hci)->version_major > (maj) || \ + ((hci)->version_major == (maj) && (hci)->version_minor >= (min))) + struct hci_cmd_ops; struct dat_words { @@ -184,6 +189,8 @@ void amd_set_resp_buf_thld(struct i3c_hci *hci); void i3c_hci_sync_irq_inactive(struct i3c_hci *hci); int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n); +struct device *i3c_hci_sysdev(struct device *dev); + #define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 int i3c_hci_rpm_suspend(struct device *dev); diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c index 5a9e2a43eff8..ab595661db1e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +++ b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c @@ -223,6 +223,13 @@ static const struct mipi_i3c_hci_pci_info intel_si_2_info = { .control_instance_pm = true, }; +static const struct mipi_i3c_hci_pci_info amd_pt_info = { + .name = "amd-pt-i3c-hci", + .id = {0}, + .instance_offset = {0}, + .instance_count = 1, +}; + static int mipi_i3c_hci_pci_find_instance(struct mipi_i3c_hci_pci *hci, struct device *dev) { for (int i = 0; i < INST_MAX; i++) { @@ -265,6 +272,8 @@ static bool mipi_i3c_hci_pci_is_operational(struct device *dev, bool update) struct mipi_i3c_hci_pci_pm_data { struct device *dev[INST_MAX]; int dev_cnt; + bool can_wakeup; + bool may_wakeup; }; static bool mipi_i3c_hci_pci_is_mfd(struct device *dev) @@ -272,6 +281,13 @@ static bool mipi_i3c_hci_pci_is_mfd(struct device *dev) return dev_is_platform(dev) && mfd_get_cell(to_platform_device(dev)); } +static bool mipi_i3c_hci_pci_any_wakeup_enabled(struct device *dev) +{ + struct i3c_hci *hci = dev_get_drvdata(dev); + + return i3c_master_has_wakeup_enabled_devs(&hci->master); +} + static int mipi_i3c_hci_pci_suspend_instance(struct device *dev, void *data) { struct mipi_i3c_hci_pci_pm_data *pm_data = data; @@ -287,6 +303,9 @@ static int mipi_i3c_hci_pci_suspend_instance(struct device *dev, void *data) pm_data->dev[pm_data->dev_cnt++] = dev; + if (pm_data->can_wakeup && mipi_i3c_hci_pci_any_wakeup_enabled(dev)) + pm_data->may_wakeup = true; + return 0; } @@ -317,12 +336,19 @@ static int mipi_i3c_hci_pci_suspend(struct device *dev) if (!hci->info->control_instance_pm) return 0; + pm_data.can_wakeup = device_can_wakeup(dev); + ret = device_for_each_child_reverse(dev, &pm_data, mipi_i3c_hci_pci_suspend_instance); - if (ret) + if (ret) { for (int i = 0; i < pm_data.dev_cnt; i++) i3c_hci_rpm_resume(pm_data.dev[i]); + return ret; + } - return ret; + if (device_may_wakeup(dev) != pm_data.may_wakeup) + device_set_wakeup_enable(dev, pm_data.may_wakeup); + + return 0; } static int mipi_i3c_hci_pci_resume(struct device *dev) @@ -475,6 +501,8 @@ static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { /* Nova Lake-H */ { PCI_VDEVICE(INTEL, 0xd37c), .driver_data = (kernel_ulong_t)&intel_mi_1_info }, { PCI_VDEVICE(INTEL, 0xd36f), .driver_data = (kernel_ulong_t)&intel_mi_2_info }, + /* AMD_PT */ + { PCI_VDEVICE(AMD, 0x444c), .driver_data = (kernel_ulong_t)&amd_pt_info }, { } }; MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices); diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index ff2657ee220b..439578a6eb54 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -45,6 +45,11 @@ #define IBI_STATUS_SIZE GENMASK(15, 8) #define CR_QUEUE_SIZE GENMASK(7, 0) +#define PIO_ALT_QUEUE_SIZE 0x1C +#define EXT_IBI_QUEUE_EN BIT(28) +#define ALT_RESP_QUEUE_EN BIT(24) +#define ALT_RESP_QUEUE_SIZE GENMASK(7, 0) + #define PIO_INTR_STATUS 0x20 #define PIO_INTR_STATUS_ENABLE 0x24 #define PIO_INTR_SIGNAL_ENABLE 0x28 @@ -72,6 +77,11 @@ #define STAT_RX_THLD BIT(1) #define STAT_TX_THLD BIT(0) +#define PIO_CONTROL 0x30 +#define PIO_CONTROL_ABORT BIT(2) +#define PIO_CONTROL_RS BIT(1) +#define PIO_CONTROL_ENABLE BIT(0) + #define PIO_QUEUE_CUR_STATUS 0x38 #define CUR_IBI_Q_LEVEL GENMASK(28, 20) #define CUR_RESP_Q_LEVEL GENMASK(18, 10) @@ -173,6 +183,14 @@ static void __hci_pio_init(struct i3c_hci *hci, u32 *size_val_ptr) * IBI queue size within allowed bounds. */ ibi_val = FIELD_GET(IBI_STATUS_SIZE, size_val); + /* Adjust actual IBI queue size based on v1.2 ALT_QUEUE_SIZE */ + if (hci_version_at_least(hci, 1, 2)) { + u32 alt_val = pio_reg_read(ALT_QUEUE_SIZE); + + if (alt_val & EXT_IBI_QUEUE_EN) + ibi_val *= 8; + } + pio->max_ibi_thresh = clamp_val(ibi_val/2, 1, 63); val = FIELD_PREP(QUEUE_IBI_STATUS_THLD, 1) | FIELD_PREP(QUEUE_IBI_DATA_THLD, pio->max_ibi_thresh) | @@ -185,8 +203,22 @@ static void __hci_pio_init(struct i3c_hci *hci, u32 *size_val_ptr) pio_reg_write(INTR_SIGNAL_ENABLE, 0x0); pio_reg_write(INTR_STATUS_ENABLE, 0xffffffff); - /* Always accept error interrupts (will be activated on first xfer) */ - pio->enabled_irqs = STAT_ALL_ERRORS; + /* + * Always accept error interrupts and IBI threshold interrupt + * (will be activated on first xfer). + */ + pio->enabled_irqs = STAT_ALL_ERRORS | STAT_IBI_STATUS_THLD; + + /* MIPI I3C HCI v1.2 requires explicitly enabling and starting PIO queues */ + if (hci_version_at_least(hci, 1, 2)) { + u32 ctl_val = pio_reg_read(CONTROL); + + if (!(ctl_val & PIO_CONTROL_ENABLE)) { + ctl_val |= PIO_CONTROL_ENABLE; + pio_reg_write(CONTROL, ctl_val); + } + pio_reg_write(CONTROL, ctl_val | PIO_CONTROL_RS); + } } static void hci_pio_suspend(struct i3c_hci *hci) @@ -205,6 +237,7 @@ static int hci_pio_init(struct i3c_hci *hci) { struct hci_pio_data *pio; u32 size_val; + u32 cmd_sz, resp_sz, ibi_val; pio = devm_kzalloc(hci->master.dev.parent, sizeof(*pio), GFP_KERNEL); if (!pio) @@ -214,10 +247,24 @@ static int hci_pio_init(struct i3c_hci *hci) __hci_pio_init(hci, &size_val); - dev_dbg(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n", - FIELD_GET(CR_QUEUE_SIZE, size_val)); - dev_dbg(&hci->master.dev, "IBI FIFO = %ld bytes\n", - 4 * FIELD_GET(IBI_STATUS_SIZE, size_val)); + cmd_sz = FIELD_GET(CR_QUEUE_SIZE, size_val); + resp_sz = cmd_sz; + ibi_val = FIELD_GET(IBI_STATUS_SIZE, size_val); + + /* MIPI I3C HCI v1.2 supports alternate RESP/IBI queue size */ + if (hci_version_at_least(hci, 1, 2)) { + u32 alt_val = pio_reg_read(ALT_QUEUE_SIZE); + + if (alt_val & ALT_RESP_QUEUE_EN) + resp_sz = FIELD_GET(ALT_RESP_QUEUE_SIZE, alt_val); + if (alt_val & EXT_IBI_QUEUE_EN) + ibi_val *= 8; + } + + dev_dbg(&hci->master.dev, "CMD FIFO = %u, RESP FIFO = %u entries\n", + cmd_sz, resp_sz); + dev_dbg(&hci->master.dev, "IBI FIFO = %u bytes\n", + 4 * ibi_val); dev_dbg(&hci->master.dev, "RX data FIFO = %d bytes\n", 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val))); dev_dbg(&hci->master.dev, "TX data FIFO = %d bytes\n", @@ -241,6 +288,9 @@ static void hci_pio_cleanup(struct i3c_hci *hci) BUG_ON(pio->curr_rx); BUG_ON(pio->curr_tx); BUG_ON(pio->curr_resp); + /* MIPI I3C HCI v1.2 requires explicitly stopping and disabling PIO queues */ + if (hci_version_at_least(hci, 1, 2)) + pio_reg_write(CONTROL, 0x0); } } @@ -761,6 +811,18 @@ static void hci_pio_err(struct i3c_hci *hci, struct hci_pio_data *pio, hci_pio_dequeue_xfer_common(hci, pio, pio->curr_tx, 1); /* then reset the hardware */ mipi_i3c_hci_pio_reset(hci); + + /* MIPI I3C HCI v1.2 requires explicitly restarting PIO queues after error/abort */ + if (hci_version_at_least(hci, 1, 2)) { + u32 ctl_val = pio_reg_read(CONTROL); + + if (!(ctl_val & PIO_CONTROL_ENABLE)) { + ctl_val |= PIO_CONTROL_ENABLE; + pio_reg_write(CONTROL, ctl_val); + } + pio_reg_write(CONTROL, ctl_val | PIO_CONTROL_RS); + } + mipi_i3c_hci_resume(hci); dev_dbg(&hci->master.dev, "status=%#x/%#x", diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index f39c449922ca..28c0927a0179 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/slab.h> #include "../internals.h" @@ -109,7 +110,7 @@ #define NCMDQP_DATA_LENGTH(x) FIELD_PREP(GENMASK(31, 16), x) #define NRSPQP 0x154 /* Normal Respone Queue */ -#define NRSPQP_NO_ERROR 0 +#define NRSPQP_NO_ERROR 0 #define NRSPQP_ERROR_CRC 1 #define NRSPQP_ERROR_PARITY 2 #define NRSPQP_ERROR_FRAME 3 @@ -199,8 +200,6 @@ #define RENESAS_I3C_MAX_DEVS 8 #define I2C_INIT_MSG -1 -#define RENESAS_I3C_TCLK_IDX 1 - enum i3c_internal_state { I3C_INTERNAL_STATE_DISABLED, I3C_INTERNAL_STATE_CONTROLLER_IDLE, @@ -221,19 +220,19 @@ enum renesas_i3c_event { }; struct renesas_i3c_cmd { + const void *tx_buf; + void *rx_buf; + /* i2c xfer */ + u8 *i2c_buf; + const struct i2c_msg *msg; + int i2c_bytes_left; + int i2c_is_last; u32 cmd0; u32 len; - const void *tx_buf; u32 tx_count; - void *rx_buf; u32 rx_count; u32 err; u8 rnw; - /* i2c xfer */ - int i2c_bytes_left; - int i2c_is_last; - u8 *i2c_buf; - const struct i2c_msg *msg; }; struct renesas_i3c_xfer { @@ -253,22 +252,22 @@ struct renesas_i3c_xferqueue { }; struct renesas_i3c { + void __iomem *regs; + struct clk *tclk; + struct reset_control *presetn; + struct reset_control *tresetn; + struct device *dev; + struct renesas_i3c_xferqueue xferqueue; struct i3c_master_controller base; + u8 addrs[RENESAS_I3C_MAX_DEVS]; + unsigned long rate; enum i3c_internal_state internal_state; - u16 maxdevs; u32 free_pos; u32 dyn_addr; u32 i2c_STDBR; u32 i3c_STDBR; - unsigned long rate; - u8 addrs[RENESAS_I3C_MAX_DEVS]; - struct renesas_i3c_xferqueue xferqueue; - void __iomem *regs; - u32 *DATBASn; - struct clk_bulk_data *clks; - struct reset_control *presetn; - struct reset_control *tresetn; - u8 num_clks; + u32 extbr; + u16 maxdevs; u8 refclk_div; }; @@ -433,7 +432,29 @@ static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct renesas_i3c } } -static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer) +static void renesas_i3c_irqs_mask_and_clear_locked(struct renesas_i3c *i3c) +{ + /* Disable all the interrupts. */ + renesas_writel(i3c->regs, BIE, 0); + renesas_writel(i3c->regs, NTIE, 0); + + /* Clear normal transfer status flags. */ + renesas_writel(i3c->regs, NTST, 0); + + /* Clear bus status flags. */ + renesas_writel(i3c->regs, BST, 0); + /* Read back registers to confirm writes have fully propagated. */ + renesas_readl(i3c->regs, BST); +} + +static void renesas_i3c_irqs_mask_and_clear(struct renesas_i3c *i3c) +{ + guard(spinlock_irqsave)(&i3c->xferqueue.lock); + + renesas_i3c_irqs_mask_and_clear_locked(i3c); +} + +static unsigned long renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer) { unsigned long time_left; @@ -442,6 +463,8 @@ static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xf time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)); if (!time_left) renesas_i3c_dequeue_xfer(i3c, xfer); + + return time_left; } static void renesas_i3c_set_prts(struct renesas_i3c *i3c, u32 val) @@ -475,6 +498,12 @@ static void renesas_i3c_bus_enable(struct i3c_master_controller *m, bool i3c_mod static int renesas_i3c_reset(struct renesas_i3c *i3c) { u32 val; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; renesas_writel(i3c->regs, BCTL, 0); renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST); @@ -546,14 +575,10 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) int od_high_ticks, od_low_ticks, i2c_total_ticks; int ret; - i3c->rate = clk_get_rate(i3c->clks[RENESAS_I3C_TCLK_IDX].clk); + i3c->rate = clk_get_rate(i3c->tclk); if (!i3c->rate) return -EINVAL; - ret = renesas_i3c_reset(i3c); - if (ret) - return ret; - i2c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i2c); i3c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i3c); @@ -604,29 +629,36 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) STDBR_SBRHO(double_SBR, od_high_ticks) | STDBR_SBRLP(pp_low_ticks) | STDBR_SBRHP(pp_high_ticks); - renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); /* Extended Bit Rate setting */ - renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) | - EXTBR_EBRHO(od_high_ticks) | - EXTBR_EBRLP(pp_low_ticks) | - EXTBR_EBRHP(pp_high_ticks)); - - renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks)); - i3c->refclk_div = cks; - - /* I3C hw init*/ - renesas_i3c_hw_init(i3c); + i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) | + EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks); ret = i3c_master_get_free_addr(m, 0); if (ret < 0) return ret; + info.dyn_addr = ret; i3c->dyn_addr = ret; - renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV); + i3c->refclk_div = cks; + + ret = renesas_i3c_reset(i3c); + if (ret) + return ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); + renesas_writel(i3c->regs, EXTBR, i3c->extbr); + renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks)); + renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(i3c->dyn_addr) | MSDVAD_MDYADV); + + /* I3C hw init*/ + renesas_i3c_hw_init(i3c); - memset(&info, 0, sizeof(info)); - info.dyn_addr = ret; return i3c_master_set_info(&i3c->base, &info); } @@ -641,6 +673,7 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) { struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_cmd *cmd; + unsigned long time_left; u32 olddevs, newdevs; u8 last_addr = 0, pos; int ret; @@ -649,6 +682,15 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) if (!xfer) return -ENOMEM; + init_completion(&xfer->comp); + cmd = xfer->cmds; + cmd->rx_count = i3c->maxdevs; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + /* Enable I3C bus. */ renesas_i3c_bus_enable(m, true); @@ -670,10 +712,6 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) renesas_writel(i3c->regs, DATBAS(pos), datbas_dvdyad_with_parity(ret)); } - init_completion(&xfer->comp); - cmd = xfer->cmds; - cmd->rx_count = 0; - ret = renesas_i3c_get_free_pos(i3c); if (ret < 0) return ret; @@ -687,9 +725,15 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) | NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC; - renesas_i3c_wait_xfer(i3c, xfer); + time_left = renesas_i3c_wait_xfer(i3c, xfer); + if (!time_left) + renesas_i3c_irqs_mask_and_clear(i3c); + + if (cmd->rx_count >= i3c->maxdevs) + newdevs = 0; + else + newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0); - newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0); newdevs &= ~olddevs; for (pos = 0; pos < i3c->maxdevs; pos++) { @@ -749,6 +793,7 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, { struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_cmd *cmd; + unsigned long time_left; int ret, pos = 0; if (ccc->id & I3C_CCC_DIRECT) { @@ -761,13 +806,18 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, if (!xfer) return -ENOMEM; - renesas_i3c_bus_enable(m, true); - init_completion(&xfer->comp); cmd = xfer->cmds; cmd->rnw = ccc->rnw; cmd->cmd0 = 0; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + renesas_i3c_bus_enable(m, true); + /* Calculate the command descriptor. */ switch (ccc->id) { case I3C_CCC_SETDASA: @@ -800,11 +850,15 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, } } - renesas_i3c_wait_xfer(i3c, xfer); + time_left = renesas_i3c_wait_xfer(i3c, xfer); + if (!time_left) + renesas_i3c_irqs_mask_and_clear(i3c); ret = xfer->ret; if (ret) ccc->err = I3C_ERROR_M2; + else if (ccc->rnw) + ccc->dests[0].payload.actual_len = cmd->rx_count; return ret; } @@ -815,10 +869,9 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); - int i; - - /* Enable I3C bus. */ - renesas_i3c_bus_enable(m, true); + unsigned long time_left; + bool xfer_failed = false; + int i, ret; struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); if (!xfer) @@ -826,6 +879,14 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ init_completion(&xfer->comp); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + /* Enable I3C bus. */ + renesas_i3c_bus_enable(m, true); + for (i = 0; i < i3c_nxfers; i++) { struct renesas_i3c_cmd *cmd = xfer->cmds; @@ -854,9 +915,14 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0); } - renesas_i3c_wait_xfer(i3c, xfer); + time_left = renesas_i3c_wait_xfer(i3c, xfer); + if (!time_left) + xfer_failed = true; } + if (xfer_failed) + renesas_i3c_irqs_mask_and_clear(i3c); + return 0; } @@ -865,12 +931,17 @@ static int renesas_i3c_attach_i3c_dev(struct i3c_dev_desc *dev) struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data; - int pos; + int pos, ret; pos = renesas_i3c_get_free_pos(i3c); if (pos < 0) return pos; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + data = kzalloc_obj(*data); if (!data) return -ENOMEM; @@ -892,10 +963,31 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev, struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + int pos, ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + pos = renesas_i3c_get_free_pos(i3c); + + if (data->index > pos && pos >= 0) { + renesas_writel(i3c->regs, DATBAS(data->index), 0); + i3c->addrs[data->index] = 0; + i3c->free_pos |= BIT(data->index); + + data->index = pos; + i3c->free_pos &= ~BIT(data->index); + } i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr : dev->info.static_addr; + renesas_writel(i3c->regs, DATBAS(data->index), + DATBAS_DVSTAD(dev->info.static_addr) | + datbas_dvdyad_with_parity(i3c->addrs[data->index])); + return 0; } @@ -904,6 +996,12 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev) struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (!ret) + renesas_writel(i3c->regs, DATBAS(data->index), 0); i3c_dev_set_master_data(dev, NULL); i3c->addrs[data->index] = 0; @@ -919,21 +1017,28 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_cmd *cmd; u8 start_bit = CNDCTL_STCND; - int i; - - struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); - if (!xfer) - return -ENOMEM; + unsigned long time_left; + bool xfer_failed = false; + int i, ret; if (!i2c_nxfers) return 0; - renesas_i3c_bus_enable(m, false); + struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); + if (!xfer) + return -ENOMEM; init_completion(&xfer->comp); xfer->is_i2c_xfer = true; cmd = xfer->cmds; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + renesas_i3c_bus_enable(m, false); + if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) { cmd->err = -EBUSY; return cmd->err; @@ -958,7 +1063,9 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0); - wait_for_completion_timeout(&xfer->comp, m->i2c.timeout); + time_left = wait_for_completion_timeout(&xfer->comp, m->i2c.timeout); + if (!time_left) + xfer_failed = true; if (cmd->err) break; @@ -967,6 +1074,10 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, } renesas_i3c_dequeue_xfer(i3c, xfer); + + if (xfer_failed) + renesas_i3c_irqs_mask_and_clear(i3c); + return cmd->err; } @@ -1014,6 +1125,11 @@ static irqreturn_t renesas_i3c_tx_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1054,6 +1170,11 @@ static irqreturn_t renesas_i3c_resp_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; /* Clear the Respone Queue Full status flag*/ @@ -1072,10 +1193,16 @@ static irqreturn_t renesas_i3c_resp_isr(int irq, void *data) break; case I3C_INTERNAL_STATE_CONTROLLER_READ: case I3C_INTERNAL_STATE_CONTROLLER_COMMAND_READ: - if (NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) && !cmd->err) - bytes_remaining = data_len - cmd->rx_count; - - i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining); + if (!cmd->err) { + u32 rx_count = min(cmd->rx_count, data_len); + + bytes_remaining = data_len - rx_count; + if (bytes_remaining) + i3c_readl_fifo(i3c->regs + NTDTBP0, + cmd->rx_buf + rx_count, + bytes_remaining); + cmd->rx_count = data_len; + } renesas_clear_bit(i3c->regs, NTIE, NTIE_RDBFIE0); break; default: @@ -1138,6 +1265,11 @@ static irqreturn_t renesas_i3c_tend_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1184,6 +1316,11 @@ static irqreturn_t renesas_i3c_rx_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1234,15 +1371,13 @@ static irqreturn_t renesas_i3c_stop_isr(int irq, void *data) struct renesas_i3c_xfer *xfer; scoped_guard(spinlock, &i3c->xferqueue.lock) { - xfer = i3c->xferqueue.cur; - - /* read back registers to confirm writes have fully propagated */ - renesas_writel(i3c->regs, BST, 0); - renesas_readl(i3c->regs, BST); - renesas_writel(i3c->regs, BIE, 0); - renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0); + renesas_i3c_irqs_mask_and_clear_locked(i3c); renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE); + xfer = i3c->xferqueue.cur; + if (!xfer) + return IRQ_HANDLED; + xfer->ret = 0; complete(&xfer->comp); } @@ -1259,6 +1394,11 @@ static irqreturn_t renesas_i3c_start_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1325,12 +1465,16 @@ static int renesas_i3c_probe(struct platform_device *pdev) if (IS_ERR(i3c->regs)) return PTR_ERR(i3c->regs); - ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &i3c->clks); - if (ret <= RENESAS_I3C_TCLK_IDX) - return dev_err_probe(&pdev->dev, ret < 0 ? ret : -EINVAL, - "Failed to get clocks (need > %d, got %d)\n", - RENESAS_I3C_TCLK_IDX, ret); - i3c->num_clks = ret; + i3c->tclk = devm_clk_get(&pdev->dev, "tclk"); + if (IS_ERR(i3c->tclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i3c->tclk), "Failed to get tclk"); + + i3c->dev = &pdev->dev; + pm_runtime_set_autosuspend_delay(&pdev->dev, 300); + pm_runtime_use_autosuspend(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; i3c->tresetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn"); if (IS_ERR(i3c->tresetn)) @@ -1350,12 +1494,19 @@ static int renesas_i3c_probe(struct platform_device *pdev) return ret; for (i = 0; i < ARRAY_SIZE(renesas_i3c_irqs); i++) { + const char *irqname; + ret = platform_get_irq_byname(pdev, renesas_i3c_irqs[i].name); if (ret < 0) return ret; + irqname = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s:%s", dev_name(&pdev->dev), + renesas_i3c_irqs[i].desc); + if (!irqname) + return -ENOMEM; + ret = devm_request_irq(&pdev->dev, ret, renesas_i3c_irqs[i].isr, - 0, renesas_i3c_irqs[i].desc, i3c); + 0, irqname, i3c); if (ret) return ret; } @@ -1365,12 +1516,6 @@ static int renesas_i3c_probe(struct platform_device *pdev) i3c->maxdevs = RENESAS_I3C_MAX_DEVS; i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0); - /* Allocate dynamic Device Address Table backup. */ - i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs, - GFP_KERNEL); - if (!i3c->DATBASn) - return -ENOMEM; - return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false); } @@ -1381,81 +1526,90 @@ static void renesas_i3c_remove(struct platform_device *pdev) i3c_master_unregister(&i3c->base); } -static int renesas_i3c_suspend_noirq(struct device *dev) +static int renesas_i3c_suspend(struct device *dev) { struct renesas_i3c *i3c = dev_get_drvdata(dev); - int i, ret; + struct reset_control_bulk_data resets[] = { + { .rstc = i3c->presetn }, + { .rstc = i3c->tresetn }, + }; + int ret; i2c_mark_adapter_suspended(&i3c->base.i2c); - /* Store Device Address Table values. */ - for (i = 0; i < i3c->maxdevs; i++) - i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i)); - - ret = reset_control_assert(i3c->presetn); + ret = reset_control_bulk_assert(ARRAY_SIZE(resets), resets); if (ret) goto err_mark_resumed; - ret = reset_control_assert(i3c->tresetn); - if (ret) - goto err_presetn; - - clk_bulk_disable(i3c->num_clks, i3c->clks); - return 0; -err_presetn: - reset_control_deassert(i3c->presetn); err_mark_resumed: i2c_mark_adapter_resumed(&i3c->base.i2c); return ret; } -static int renesas_i3c_resume_noirq(struct device *dev) +static int renesas_i3c_resume(struct device *dev) { struct renesas_i3c *i3c = dev_get_drvdata(dev); - int i, ret; + struct reset_control_bulk_data resets[] = { + { .rstc = i3c->presetn }, + { .rstc = i3c->tresetn }, + }; + int ret; - ret = reset_control_deassert(i3c->presetn); + ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets); if (ret) return ret; - ret = reset_control_deassert(i3c->tresetn); + ret = renesas_i3c_reset(i3c); if (ret) - goto err_presetn; + goto err_resets_asserted; - ret = clk_bulk_enable(i3c->num_clks, i3c->clks); + ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_tresetn; + goto err_resets_asserted; /* Re-store I3C registers value. */ + renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); + renesas_writel(i3c->regs, EXTBR, i3c->extbr); renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(i3c->refclk_div)); renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV | MSDVAD_MDYAD(i3c->dyn_addr)); - /* Restore Device Address Table values. */ - for (i = 0; i < i3c->maxdevs; i++) - renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]); - /* I3C hw init. */ renesas_i3c_hw_init(i3c); + ret = i3c_master_do_daa_ext(&i3c->base, true); + if (ret) + dev_err(dev, "DAA failed on resume, ret=%d", ret); + i2c_mark_adapter_resumed(&i3c->base.i2c); + pm_runtime_put_autosuspend(dev); + + /* + * I3C devices may have retained their dynamic address anyway. Do not + * fail the resume because of DAA error. + */ return 0; -err_tresetn: - reset_control_assert(i3c->tresetn); -err_presetn: - reset_control_assert(i3c->presetn); +err_resets_asserted: + /* + * If this happens, there is no way to recover from this state without + * reloading the driver. We want to avoid keeping the reset line + * deasserted unnecessarily. The runtime paths will still work correctly + * even if the IP registers are accessed while reset is asserted (e.g. + * if a runtime path is triggered after a failed resume). Checked on + * RZ/G3S. + */ + reset_control_bulk_assert(ARRAY_SIZE(resets), resets); return ret; } static const struct dev_pm_ops renesas_i3c_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq, - renesas_i3c_resume_noirq) + SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend, renesas_i3c_resume) }; static const struct of_device_id renesas_i3c_of_ids[] = { diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 93805df8a940..e700392f0801 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -455,14 +455,22 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master, buf = slot->data; while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) && - slot->len < SVC_I3C_FIFO_SIZE) { + slot->len < dev->ibi->max_payload_len) { mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL); count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl); + count = min(count, dev->ibi->max_payload_len - slot->len); readsb(master->regs + SVC_I3C_MRDATAB, buf, count); slot->len += count; buf += count; } + /* + * The device may have sent more than the requested payload. Drop the + * extra bytes so they do not leak into the next transfer. + */ + if (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))) + writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL); + master->ibi.tbq_slot = slot; return 0; @@ -1488,8 +1496,11 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, svc_i3c_master_emit_force_exit(master); /* Wait idle if stop is sent. */ - readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg, - SVC_I3C_MSTATUS_STATE_IDLE(reg), 0, 1000); + ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg, + SVC_I3C_MSTATUS_STATE_IDLE(reg), + 0, 1000); + if (ret) + goto cleanup; } return 0; @@ -1500,6 +1511,7 @@ emit_stop: else svc_i3c_master_emit_force_exit(master); +cleanup: svc_i3c_master_clear_merrwarn(master); svc_i3c_master_flush_fifo(master); @@ -1713,8 +1725,8 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master, svc_i3c_master_dequeue_xfer(master, xfer); mutex_unlock(&master->lock); - if (cmd->actual_len != xfer_len) - ccc->dests[0].payload.len = cmd->actual_len; + if (ccc->rnw) + ccc->dests[0].payload.actual_len = cmd->actual_len; ret = xfer->ret; svc_i3c_master_free_xfer(xfer); diff --git a/include/dt-bindings/i3c/i3c.h b/include/dt-bindings/i3c/i3c.h index 373439218bba..78b8c634aad8 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 ad59a4ae60d1..c6947dcb0f57 100644 --- a/include/linux/i3c/ccc.h +++ b/include/linux/i3c/ccc.h @@ -12,6 +12,8 @@ #include <linux/i3c/device.h> /* I3C CCC (Common Command Codes) related definitions */ +#define I3C_CCC_RETRIES 1 + #define I3C_CCC_DIRECT BIT(7) #define I3C_CCC_ID(id, broadcast) \ @@ -32,6 +34,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) @@ -343,11 +346,15 @@ struct i3c_ccc_getxtime { /** * struct i3c_ccc_cmd_payload - CCC payload * - * @len: payload length + * @len: requested payload length + * @actual_len: number of bytes received on a GET CCC (filled by the driver) + * @optional_bytes: GET CCCs may return up to this many fewer bytes than @len * @data: payload data. This buffer must be DMA-able */ struct i3c_ccc_cmd_payload { u16 len; + u16 actual_len; + u16 optional_bytes; void *data; }; @@ -372,12 +379,15 @@ struct i3c_ccc_cmd_dest { * @ndests: number of destinations. Should always be one for broadcast commands * @dests: array of destinations and associated payload for this CCC. Most of * the time, only one destination is provided + * @retries: number of times to retry a failed Direct GET CCC (see + * &I3C_CCC_RETRIES) * @err: I3C error code */ struct i3c_ccc_cmd { u8 rnw; u8 id; unsigned int ndests; + unsigned int retries; struct i3c_ccc_cmd_dest *dests; enum i3c_error_code err; }; diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 4d2a68793324..f7ceec2b4477 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; }; /** @@ -228,6 +238,8 @@ struct i3c_dev_desc { * every time the I3C device is rediscovered with a different dynamic * address assigned * @bus: I3C bus this device is attached to + * @node: unregistered device list node, only for use by + * i3c_master_register_new_i3c_devs(), it is not protected by a lock * * I3C device object exposed to I3C device drivers. The takes care of linking * this object to the relevant &struct_i3c_dev_desc one. @@ -238,6 +250,7 @@ struct i3c_device { struct device dev; struct i3c_dev_desc *desc; struct i3c_bus *bus; + struct list_head node; }; /* @@ -259,6 +272,7 @@ struct i3c_device { #define I3C_BUS_THIGH_MIXED_MAX_NS 41 #define I3C_BUS_TIDLE_MIN_NS 200000 #define I3C_BUS_TLOW_OD_MIN_NS 200 +#define I3C_BUS_THIGH_INIT_OD_MIN_NS 200 /** * enum i3c_bus_mode - I3C bus mode @@ -511,11 +525,17 @@ struct i3c_master_controller_ops { * @hotjoin: true if the master support hotjoin * @rpm_allowed: true if Runtime PM allowed * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended + * @ibi_wakeup: IBI can wakeup the system * @shutting_down: set to true when master begins shutdown or unregister * @boardinfo.i3c: list of I3C boardinfo objects * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus * @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: freezable 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 @@ -545,12 +565,14 @@ struct i3c_master_controller { unsigned int hotjoin: 1; unsigned int rpm_allowed: 1; unsigned int rpm_ibi_allowed: 1; + unsigned int ibi_wakeup: 1; bool shutting_down; struct { struct list_head i3c; struct list_head i2c; } boardinfo; struct i3c_bus bus; + u8 addr_method; struct workqueue_struct *wq; struct work_struct hj_work; struct work_struct reg_work; @@ -744,6 +766,7 @@ void i3c_generic_ibi_recycle_slot(struct i3c_generic_ibi_pool *pool, struct i3c_ibi_slot *slot); void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot); +bool i3c_master_has_wakeup_enabled_devs(struct i3c_master_controller *master); struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev); |
