diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c index c1e048957a01..19f5cf8b1b11 100644 --- a/Documentation/ABI/testing/sysfs-bus-i3c +++ b/Documentation/ABI/testing/sysfs-bus-i3c @@ -172,3 +172,23 @@ Description: the automatic retries. Exist only when I3C constroller supports this retry on nack feature. +What: /sys/bus/i3c/devices/i3c-/do_daa +KernelVersion: 7.0 +Contact: linux-i3c@vger.kernel.org +Description: + Write-only attribute that triggers a Dynamic Address Assignment + (DAA) procedure which discovers new I3C devices on the bus. + Writing a boolean true value (1, y, yes, true, on) to this + attribute causes the master controller to perform DAA, which + includes broadcasting an ENTDAA (Enter Dynamic Address Assignment) + Common Command Code (CCC) on the bus. Writing a false value + returns -EINVAL. + + This is useful for discovering I3C devices that were not present + during initial bus initialization and are unable to issue + Hot-Join. Only devices without a currently assigned dynamic address + will respond to the ENTDAA broadcast and be assigned addresses. + + Note that this mechanism is distinct from Hot-Join, since this is + controller-initiated discovery, while Hot-Join is device-initiated + method to provoke controller discovery procedure. diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 9e6be49bebb2..5cd4e5da2233 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -758,6 +758,32 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, static DEVICE_ATTR_RW(dev_nack_retry_count); +static ssize_t do_daa_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i3c_master_controller *master = dev_to_i3cmaster(dev); + bool val; + int ret; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + if (!val) + return -EINVAL; + + if (!master->init_done) + return -EAGAIN; + + ret = i3c_master_do_daa(master); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_WO(do_daa); + static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_mode.attr, &dev_attr_current_master.attr, @@ -769,6 +795,7 @@ static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_dynamic_address.attr, &dev_attr_hdrcap.attr, &dev_attr_hotjoin.attr, + &dev_attr_do_daa.attr, NULL, }; ATTRIBUTE_GROUPS(i3c_masterdev); @@ -898,11 +925,17 @@ static void i3c_ccc_cmd_init(struct i3c_ccc_cmd *cmd, bool rnw, u8 id, cmd->err = I3C_ERROR_UNKNOWN; } +/** + * i3c_master_send_ccc_cmd_locked() - send a CCC (Common Command Codes) + * @master: master used to send frames on the bus + * @cmd: command to send + * + * Return: 0 in case of success, or a negative error code otherwise. + * I3C Mx error codes are stored in cmd->err. + */ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master, struct i3c_ccc_cmd *cmd) { - int ret; - if (!cmd || !master) return -EINVAL; @@ -920,15 +953,7 @@ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master, !master->ops->supports_ccc_cmd(master, cmd)) return -EOPNOTSUPP; - ret = master->ops->send_ccc_cmd(master, cmd); - if (ret) { - if (cmd->err != I3C_ERROR_UNKNOWN) - return cmd->err; - - return ret; - } - - return 0; + return master->ops->send_ccc_cmd(master, cmd); } static struct i2c_dev_desc * @@ -1016,6 +1041,10 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + /* No active devices on the bus. */ + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } @@ -1032,8 +1061,7 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, * * This function must be called with the bus lock held in write mode. * - * 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. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_entdaa_locked(struct i3c_master_controller *master) { @@ -1046,12 +1074,17 @@ int i3c_master_entdaa_locked(struct i3c_master_controller *master) ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + /* No active devices need an address. */ + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } EXPORT_SYMBOL_GPL(i3c_master_entdaa_locked); static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, - u8 addr, bool enable, u8 evts) + u8 addr, bool enable, u8 evts, + bool suppress_m2) { struct i3c_ccc_events *events; struct i3c_ccc_cmd_dest dest; @@ -1071,6 +1104,9 @@ static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + if (suppress_m2 && ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } @@ -1085,13 +1121,12 @@ static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, * * This function must be called with the bus lock held in write mode. * - * 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. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_disec_locked(struct i3c_master_controller *master, u8 addr, u8 evts) { - return i3c_master_enec_disec_locked(master, addr, false, evts); + return i3c_master_enec_disec_locked(master, addr, false, evts, false); } EXPORT_SYMBOL_GPL(i3c_master_disec_locked); @@ -1106,13 +1141,12 @@ EXPORT_SYMBOL_GPL(i3c_master_disec_locked); * * This function must be called with the bus lock held in write mode. * - * 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. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_enec_locked(struct i3c_master_controller *master, u8 addr, u8 evts) { - return i3c_master_enec_disec_locked(master, addr, true, evts); + return i3c_master_enec_disec_locked(master, addr, true, evts, false); } EXPORT_SYMBOL_GPL(i3c_master_enec_locked); @@ -1132,8 +1166,7 @@ EXPORT_SYMBOL_GPL(i3c_master_enec_locked); * * This function must be called with the bus lock held in write mode. * - * 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. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_defslvs_locked(struct i3c_master_controller *master) { @@ -1794,11 +1827,8 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) { + if (rstdaa) rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - if (rstret == I3C_ERROR_M2) - rstret = 0; - } ret = master->ops->do_daa(master); @@ -2093,7 +2123,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) * (assigned by the bootloader for example). */ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - if (ret && ret != I3C_ERROR_M2) + if (ret) goto err_bus_cleanup; if (master->ops->set_speed) { @@ -2102,11 +2132,14 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) goto err_bus_cleanup; } - /* Disable all slave events before starting DAA. */ - ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR, - I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | - I3C_CCC_EVENT_HJ); - if (ret && ret != I3C_ERROR_M2) + /* + * Disable all slave events before starting DAA. When no active device + * is on the bus, returns Mx error code M2, this error is ignored. + */ + ret = i3c_master_enec_disec_locked(master, I3C_BROADCAST_ADDR, false, + I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | + I3C_CCC_EVENT_HJ, true); + if (ret) goto err_bus_cleanup; /* @@ -2396,7 +2429,7 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, * DEFSLVS command. */ if (boardinfo->base.flags & I2C_CLIENT_TEN) { - dev_err(dev, "I2C device with 10 bit address not supported."); + dev_err(dev, "I2C device with 10 bit address not supported.\n"); return -EOPNOTSUPP; } @@ -2793,10 +2826,10 @@ struct i3c_generic_ibi_slot { struct i3c_generic_ibi_pool { spinlock_t lock; unsigned int num_slots; - struct i3c_generic_ibi_slot *slots; void *payload_buf; struct list_head free_slots; struct list_head pending; + struct i3c_generic_ibi_slot slots[] __counted_by(num_slots); }; /** @@ -2824,7 +2857,6 @@ void i3c_generic_ibi_free_pool(struct i3c_generic_ibi_pool *pool) WARN_ON(nslots != pool->num_slots); kfree(pool->payload_buf); - kfree(pool->slots); kfree(pool); } EXPORT_SYMBOL_GPL(i3c_generic_ibi_free_pool); @@ -2847,20 +2879,16 @@ i3c_generic_ibi_alloc_pool(struct i3c_dev_desc *dev, unsigned int i; int ret; - pool = kzalloc_obj(*pool); + pool = kzalloc_flex(*pool, slots, req->num_slots); if (!pool) return ERR_PTR(-ENOMEM); + pool->num_slots = req->num_slots; + spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free_slots); INIT_LIST_HEAD(&pool->pending); - pool->slots = kzalloc_objs(*slot, req->num_slots); - if (!pool->slots) { - ret = -ENOMEM; - goto err_free_pool; - } - if (req->max_payload_len) { pool->payload_buf = kcalloc(req->num_slots, req->max_payload_len, GFP_KERNEL); @@ -2879,7 +2907,6 @@ i3c_generic_ibi_alloc_pool(struct i3c_dev_desc *dev, (i * req->max_payload_len); list_add_tail(&slot->node, &pool->free_slots); - pool->num_slots++; } return pool; diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c index 6616f751075a..047081c9f064 100644 --- a/drivers/i3c/master/adi-i3c-master.c +++ b/drivers/i3c/master/adi-i3c-master.c @@ -361,7 +361,7 @@ static int adi_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, cmd->err = adi_i3c_cmd_get_err(&xfer->cmds[0]); - return 0; + return xfer->ret; } static int adi_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, @@ -655,8 +655,7 @@ static int adi_i3c_master_do_daa(struct i3c_master_controller *m) writel(irq_mask, master->regs + REG_IRQ_MASK); - /* DAA always finishes with CE2_ERROR or NACK_RESP */ - if (ret && ret != I3C_ERROR_M2) + if (ret) return ret; /* Add I3C devices discovered */ diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index d6bdb32397fb..655693a2187e 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -393,11 +394,6 @@ dw_i3c_master_alloc_xfer(struct dw_i3c_master *master, unsigned int ncmds) return xfer; } -static void dw_i3c_master_free_xfer(struct dw_i3c_xfer *xfer) -{ - kfree(xfer); -} - static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master) { struct dw_i3c_xfer *xfer = master->xferqueue.cur; @@ -715,7 +711,6 @@ static void dw_i3c_master_bus_cleanup(struct i3c_master_controller *m) static int dw_i3c_ccc_set(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) { - struct dw_i3c_xfer *xfer; struct dw_i3c_cmd *cmd; int ret, pos = 0; @@ -725,7 +720,7 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master, return pos; } - xfer = dw_i3c_master_alloc_xfer(master, 1); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1); if (!xfer) return -ENOMEM; @@ -750,14 +745,11 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master, if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK) ccc->err = I3C_ERROR_M2; - dw_i3c_master_free_xfer(xfer); - return ret; } static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) { - struct dw_i3c_xfer *xfer; struct dw_i3c_cmd *cmd; int ret, pos; @@ -765,7 +757,7 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) if (pos < 0) return pos; - xfer = dw_i3c_master_alloc_xfer(master, 1); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1); if (!xfer) return -ENOMEM; @@ -790,7 +782,6 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) ret = xfer->ret; if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK) ccc->err = I3C_ERROR_M2; - dw_i3c_master_free_xfer(xfer); return ret; } @@ -837,12 +828,15 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, static int dw_i3c_master_daa(struct i3c_master_controller *m) { struct dw_i3c_master *master = to_dw_i3c_master(m); - struct dw_i3c_xfer *xfer; struct dw_i3c_cmd *cmd; u32 olddevs, newdevs; u8 last_addr = 0; int ret, pos; + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1); + if (!xfer) + return -ENOMEM; + ret = pm_runtime_resume_and_get(master->dev); if (ret < 0) { dev_err(master->dev, @@ -876,15 +870,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) ret = 0; } - xfer = dw_i3c_master_alloc_xfer(master, 1); - if (!xfer) { - ret = -ENOMEM; - goto rpm_out; - } - pos = dw_i3c_master_get_free_pos(master); if (pos < 0) { - dw_i3c_master_free_xfer(xfer); ret = pos; goto rpm_out; } @@ -909,8 +896,6 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) i3c_master_add_i3c_dev_locked(m, master->devs[pos].addr); } - dw_i3c_master_free_xfer(xfer); - rpm_out: pm_runtime_put_autosuspend(master->dev); return ret; @@ -924,7 +909,6 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_master_controller *m = i3c_dev_get_master(dev); struct dw_i3c_master *master = to_dw_i3c_master(m); unsigned int nrxwords = 0, ntxwords = 0; - struct dw_i3c_xfer *xfer; int i, ret = 0; if (!i3c_nxfers) @@ -944,7 +928,7 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, nrxwords > master->caps.datafifodepth) return -EOPNOTSUPP; - xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i3c_nxfers); if (!xfer) return -ENOMEM; @@ -995,7 +979,6 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, } ret = xfer->ret; - dw_i3c_master_free_xfer(xfer); pm_runtime_put_autosuspend(master->dev); return ret; @@ -1084,7 +1067,6 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, struct i3c_master_controller *m = i2c_dev_get_master(dev); struct dw_i3c_master *master = to_dw_i3c_master(m); unsigned int nrxwords = 0, ntxwords = 0; - struct dw_i3c_xfer *xfer; int i, ret = 0; if (!i2c_nxfers) @@ -1104,7 +1086,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, nrxwords > master->caps.datafifodepth) return -EOPNOTSUPP; - xfer = dw_i3c_master_alloc_xfer(master, i2c_nxfers); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i2c_nxfers); if (!xfer) return -ENOMEM; @@ -1113,7 +1095,6 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, dev_err(master->dev, "<%s> cannot resume i3c bus master, err: %d\n", __func__, ret); - dw_i3c_master_free_xfer(xfer); return ret; } @@ -1145,7 +1126,6 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, dw_i3c_master_dequeue_xfer(master, xfer); ret = xfer->ret; - dw_i3c_master_free_xfer(xfer); pm_runtime_put_autosuspend(master->dev); return ret; @@ -1606,13 +1586,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, if (IS_ERR(master->pclk)) return PTR_ERR(master->pclk); - master->core_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, - "core_rst"); + master->core_rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, + "core_rst"); if (IS_ERR(master->core_rst)) return PTR_ERR(master->core_rst); - reset_control_deassert(master->core_rst); - spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); @@ -1624,7 +1602,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, dw_i3c_master_irq_handler, 0, dev_name(&pdev->dev), master); if (ret) - goto err_assert_rst; + return ret; platform_set_drvdata(pdev, master); @@ -1669,13 +1647,12 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, return 0; err_disable_pm: + if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) + pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); -err_assert_rst: - reset_control_assert(master->core_rst); - return ret; } EXPORT_SYMBOL_GPL(dw_i3c_common_probe); diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index b78aebf6b2ca..5cfec6761494 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -1143,7 +1143,7 @@ static int cdns_i3c_master_do_daa(struct i3c_master_controller *m) } ret = i3c_master_entdaa_locked(&master->base); - if (ret && ret != I3C_ERROR_M2) + if (ret) return ret; newdevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 284f3ed7af8c..b781dbed2165 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -759,7 +759,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci) return 0; } -static int i3c_hci_runtime_suspend(struct device *dev) +int i3c_hci_rpm_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; @@ -776,8 +776,9 @@ static int i3c_hci_runtime_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(i3c_hci_rpm_suspend); -static int i3c_hci_runtime_resume(struct device *dev) +int i3c_hci_rpm_resume(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; @@ -800,6 +801,27 @@ static int i3c_hci_runtime_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(i3c_hci_rpm_resume); + +static int i3c_hci_runtime_suspend(struct device *dev) +{ + struct i3c_hci *hci = dev_get_drvdata(dev); + + if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) + return 0; + + return i3c_hci_rpm_suspend(dev); +} + +static int i3c_hci_runtime_resume(struct device *dev) +{ + struct i3c_hci *hci = dev_get_drvdata(dev); + + if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) + return 0; + + return i3c_hci_rpm_resume(dev); +} static int i3c_hci_suspend(struct device *dev) { @@ -844,8 +866,6 @@ static int i3c_hci_restore(struct device *dev) return i3c_hci_resume_common(dev, true); } -#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 - static void i3c_hci_rpm_enable(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); @@ -996,6 +1016,9 @@ static int i3c_hci_probe(struct platform_device *pdev) if (hci->quirks & HCI_QUIRK_RPM_ALLOWED) i3c_hci_rpm_enable(&pdev->dev); + if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED) + hci->master.rpm_ibi_allowed = true; + return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false); } @@ -1019,7 +1042,9 @@ static const struct acpi_device_id i3c_hci_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match); static const struct platform_device_id i3c_hci_driver_ids[] = { - { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED }, + { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED | + HCI_QUIRK_RPM_IBI_ALLOWED | + HCI_QUIRK_RPM_PARENT_MANAGED }, { /* 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 e487ef52f6b4..e4daaa612055 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -754,7 +754,10 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) if (!(ibi_status & IBI_LAST_STATUS)) { ibi_size += chunks * rh->ibi_chunk_sz; } else { - ibi_size += FIELD_GET(IBI_DATA_LENGTH, ibi_status); + if (chunks) { + ibi_size += (chunks - 1) * rh->ibi_chunk_sz; + ibi_size += FIELD_GET(IBI_DATA_LENGTH, ibi_status); + } last_ptr = ptr; break; } diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 9ac9d0e342f4..f17f43494c1b 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -150,6 +150,8 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */ #define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */ #define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */ +#define HCI_QUIRK_RPM_IBI_ALLOWED BIT(6) /* IBI and Hot-Join allowed while runtime suspended */ +#define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by parent device */ /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); @@ -160,4 +162,9 @@ 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); +#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 + +int i3c_hci_rpm_suspend(struct device *dev); +int i3c_hci_rpm_resume(struct device *dev); + #endif 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 30302e4d08e2..9468786fb853 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 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,16 +21,24 @@ #include #include +#include "hci.h" + /* * There can up to 15 instances, but implementations have at most 2 at this * time. */ #define INST_MAX 2 +struct mipi_i3c_hci_pci_instance { + struct device *dev; + bool operational; +}; + struct mipi_i3c_hci_pci { struct pci_dev *pci; void __iomem *base; const struct mipi_i3c_hci_pci_info *info; + struct mipi_i3c_hci_pci_instance instance[INST_MAX]; void *private; }; @@ -40,6 +49,7 @@ struct mipi_i3c_hci_pci_info { int id[INST_MAX]; u32 instance_offset[INST_MAX]; int instance_count; + bool control_instance_pm; }; #define INTEL_PRIV_OFFSET 0x2b0 @@ -164,6 +174,7 @@ static int intel_i3c_init(struct mipi_i3c_hci_pci *hci) dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64)); hci->pci->d3cold_delay = 0; + hci->pci->d3hot_delay = 0; hci->private = host; host->priv = priv; @@ -189,6 +200,7 @@ static const struct mipi_i3c_hci_pci_info intel_mi_1_info = { .id = {0, 1}, .instance_offset = {0, 0x400}, .instance_count = 2, + .control_instance_pm = true, }; static const struct mipi_i3c_hci_pci_info intel_mi_2_info = { @@ -198,6 +210,7 @@ static const struct mipi_i3c_hci_pci_info intel_mi_2_info = { .id = {2, 3}, .instance_offset = {0, 0x400}, .instance_count = 2, + .control_instance_pm = true, }; static const struct mipi_i3c_hci_pci_info intel_si_2_info = { @@ -207,8 +220,128 @@ static const struct mipi_i3c_hci_pci_info intel_si_2_info = { .id = {2}, .instance_offset = {0}, .instance_count = 1, + .control_instance_pm = true, }; +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++) { + if (!hci->instance[i].dev) + hci->instance[i].dev = dev; + if (hci->instance[i].dev == dev) + return i; + } + + return -1; +} + +#define HC_CONTROL 0x04 +#define HC_CONTROL_BUS_ENABLE BIT(31) + +static bool __mipi_i3c_hci_pci_is_operational(struct device *dev) +{ + const struct mipi_i3c_hci_platform_data *pdata = dev->platform_data; + u32 hc_control = readl(pdata->base_regs + HC_CONTROL); + + return hc_control & HC_CONTROL_BUS_ENABLE; +} + +static bool mipi_i3c_hci_pci_is_operational(struct device *dev, bool update) +{ + struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev->parent); + int pos = mipi_i3c_hci_pci_find_instance(hci, dev); + + if (pos < 0) { + dev_err(dev, "%s: I3C instance not found\n", __func__); + return false; + } + + if (update) + hci->instance[pos].operational = __mipi_i3c_hci_pci_is_operational(dev); + + return hci->instance[pos].operational; +} + +struct mipi_i3c_hci_pci_pm_data { + struct device *dev[INST_MAX]; + int dev_cnt; +}; + +static bool mipi_i3c_hci_pci_is_mfd(struct device *dev) +{ + return dev_is_platform(dev) && mfd_get_cell(to_platform_device(dev)); +} + +static int mipi_i3c_hci_pci_suspend_instance(struct device *dev, void *data) +{ + struct mipi_i3c_hci_pci_pm_data *pm_data = data; + int ret; + + if (!mipi_i3c_hci_pci_is_mfd(dev) || + !mipi_i3c_hci_pci_is_operational(dev, true)) + return 0; + + ret = i3c_hci_rpm_suspend(dev); + if (ret) + return ret; + + pm_data->dev[pm_data->dev_cnt++] = dev; + + return 0; +} + +static int mipi_i3c_hci_pci_resume_instance(struct device *dev, void *data) +{ + struct mipi_i3c_hci_pci_pm_data *pm_data = data; + int ret; + + if (!mipi_i3c_hci_pci_is_mfd(dev) || + !mipi_i3c_hci_pci_is_operational(dev, false)) + return 0; + + ret = i3c_hci_rpm_resume(dev); + if (ret) + return ret; + + pm_data->dev[pm_data->dev_cnt++] = dev; + + return 0; +} + +static int mipi_i3c_hci_pci_suspend(struct device *dev) +{ + struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev); + struct mipi_i3c_hci_pci_pm_data pm_data = {}; + int ret; + + if (!hci->info->control_instance_pm) + return 0; + + ret = device_for_each_child_reverse(dev, &pm_data, mipi_i3c_hci_pci_suspend_instance); + if (ret) + for (int i = 0; i < pm_data.dev_cnt; i++) + i3c_hci_rpm_resume(pm_data.dev[i]); + + return ret; +} + +static int mipi_i3c_hci_pci_resume(struct device *dev) +{ + struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev); + struct mipi_i3c_hci_pci_pm_data pm_data = {}; + int ret; + + if (!hci->info->control_instance_pm) + return 0; + + ret = device_for_each_child(dev, &pm_data, mipi_i3c_hci_pci_resume_instance); + if (ret) + for (int i = 0; i < pm_data.dev_cnt; i++) + i3c_hci_rpm_suspend(pm_data.dev[i]); + + return ret; +} + static void mipi_i3c_hci_pci_rpm_allow(struct device *dev) { pm_runtime_put(dev); @@ -322,6 +455,8 @@ static void mipi_i3c_hci_pci_remove(struct pci_dev *pci) /* PM ops must exist for PCI to put a device to a low power state */ static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = { + RUNTIME_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume) }; static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { @@ -337,6 +472,9 @@ static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { /* Nova Lake-S */ { PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_mi_1_info}, { PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_mi_2_info}, + /* Nova Lake-H */ + { PCI_VDEVICE(INTEL, 0xd37c), (kernel_ulong_t)&intel_mi_1_info}, + { PCI_VDEVICE(INTEL, 0xd36f), (kernel_ulong_t)&intel_mi_2_info}, { }, }; MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices); diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index d9f5b30a4b2f..f39c449922ca 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -747,7 +748,6 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, struct i3c_ccc_cmd *ccc) { struct renesas_i3c *i3c = to_renesas_i3c(m); - struct renesas_i3c_xfer *xfer; struct renesas_i3c_cmd *cmd; int ret, pos = 0; @@ -757,7 +757,7 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, return pos; } - xfer = renesas_i3c_alloc_xfer(i3c, 1); + struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); if (!xfer) return -ENOMEM; @@ -806,8 +806,6 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, if (ret) ccc->err = I3C_ERROR_M2; - kfree(xfer); - return ret; } @@ -817,13 +815,12 @@ 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); - struct renesas_i3c_xfer *xfer; int i; /* Enable I3C bus. */ renesas_i3c_bus_enable(m, true); - xfer = renesas_i3c_alloc_xfer(i3c, 1); + struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); if (!xfer) return -ENOMEM; diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index b84b324e4111..e2d99a3ac07d 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -344,7 +344,7 @@ static void svc_i3c_master_reset_fifo_trigger(struct svc_i3c_master *master) { u32 reg; - /* Set RX and TX tigger levels, flush FIFOs */ + /* Set RX and TX trigger levels, flush FIFOs */ reg = SVC_I3C_MDATACTRL_FLUSHTB | SVC_I3C_MDATACTRL_FLUSHRB | SVC_I3C_MDATACTRL_UNLOCK_TRIG | @@ -572,7 +572,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) * 3. IBI isr writes an AutoIBI request. * 4. The controller will not start AutoIBI process because SDA is not low. * 5. IBIWON polling times out. - * 6. Controller reamins in AutoIBI state and doesn't accept EmitStop request. + * 6. Controller remains in AutoIBI state and doesn't accept EmitStop request. */ writel(SVC_I3C_MCTRL_REQUEST_START_ADDR | SVC_I3C_MCTRL_TYPE_I3C | @@ -774,7 +774,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m) /* * Using I3C Open-Drain mode, target is 4.17MHz/240ns with a - * duty-cycle tuned so that high levels are filetered out by + * duty-cycle tuned so that high levels are filtered out by * the 50ns filter (target being 40ns). */ odhpp = 1; @@ -1268,7 +1268,7 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m) /* Configure IBI auto-rules */ ret = svc_i3c_update_ibirules(master); if (ret) - dev_err(master->dev, "Cannot handle such a list of devices"); + dev_err(master->dev, "Cannot handle such a list of devices\n"); rpm_out: pm_runtime_put_autosuspend(master->dev);