I3C for 7.1

Subsystem:
  - add sysfs option to rescan bus via entdaa
  - fix error code handling for send_ccc_cmd
 
 Drivers:
  - mipi-i3c-hci-pci: Intel Nova Lake-H I3C support
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmnkAa8ACgkQY6TcMGxw
 OjKwAhAAs1dbW2vzIaz6Y4zfyyhbVeMAkfx1yj/ekegTRQW/1MiR7k4C8GF0aQQR
 p3gbchFDrXdGzmGKwT7Y8Gcol4ml3hw4/6yrlHojRAVcfoIdKgYfF2+ffqDjl0Md
 FKRBjP8IzJ1i9GKA1PyDzw8YvWKbcl14Tb0PgctVTLcsTfPF8NHIJ3i04hBEA2vW
 Ro0NbRwBeBypXTAkldB0ZOXaS9qyOqoxZskFe1kmGCG05Rlo92kMCMRjjX5H0TVv
 laWMLEv34MsoIArTi77KZtJGYhDBckdrETFIiJf5xWVTyVyIH1PILIIgsy+nBO4L
 0LsBnuhMyIA5AAUctIj63oZxLSIkTkNqk/Ii178dr/K6UfXtSt93q8Cgzdkh8Khy
 PLDVi8HBuLBmHqXmhtTatt8eerFuyJUUP3X3PnHXfJqTgHNfJN1yLYsLIX52S2h4
 c1GxuacDruX5stCvRn0DGiXozKruS45ce1EhbUOMUtImbWbau0DBoujqCZRQpSCW
 LEZo+DMQ+GCNb8hqHW6seV/c4N8ul/R84INUBswaDFIJArsy8KPPIcgG+7l8fGC+
 yHjbcxlR2gri7aaNjSjU36deNaOcSqcQoKJ97Ylhuba1HE2bCV4oV3ngI9k7hUbR
 aMxuN83r8ImiUvLKRh42IxjG6wAeVuYGVe6ohl3iK0uzgLL2Ux8=
 =7ohQ
 -----END PGP SIGNATURE-----

Merge tag 'i3c/for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux

Pull i3c updates from Alexandre Belloni:
 "Subsystem:
   - add sysfs option to rescan bus via entdaa
   - fix error code handling for send_ccc_cmd

  Drivers:
   - mipi-i3c-hci-pci: Intel Nova Lake-H I3C support"

* tag 'i3c/for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (22 commits)
  i3c: mipi-i3c-hci: fix IBI payload length calculation for final status
  i3c: master: adi: Fix error propagation for CCCs
  i3c: master: Fix error codes at send_ccc_cmd
  i3c: master: Move bus_init error suppression
  i3c: master: Move entdaa error suppression
  i3c: master: Move rstdaa error suppression
  i3c: dw: Simplify xfer cleanup with __free(kfree)
  i3c: dw: Fix memory leak in dw_i3c_master_i3c_xfers()
  i3c: master: renesas: Use __free(kfree) for xfer cleanup in renesas_i3c_send_ccc_cmd()
  i3c: master: renesas: Fix memory leak in renesas_i3c_i3c_xfers()
  i3c: master: dw-i3c: Balance PM runtime usage count on probe failure
  i3c: master: dw-i3c: Fix missing reset assertion in remove() callback
  i3c: mipi-i3c-hci-pci: Enable IBI while runtime suspended for Intel controllers
  i3c: mipi-i3c-hci-pci: Add optional ability to manage child runtime PM
  i3c: mipi-i3c-hci: Allow parent to manage runtime PM
  i3c: mipi-i3c-hci: Add quirk to allow IBI while runtime suspended
  i3c: mipi-i3c-hci-pci: Set d3hot_delay to 0 for Intel controllers
  i3c: fix missing newline in dev_err messages
  i3c: master: use kzalloc_flex
  i3c: mipi-i3c-hci-pci: Add support for Intel Nova Lake-H I3C
  ...
master
Linus Torvalds 2026-04-18 16:48:13 -07:00
commit 401b0e0bc9
11 changed files with 293 additions and 100 deletions

View File

@ -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-<bus-id>/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.

View File

@ -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;

View File

@ -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 */

View File

@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/err.h>
@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -9,6 +9,7 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/i3c/master.h>
#include <linux/idr.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@ -20,16 +21,24 @@
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#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);

View File

@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/err.h>
@ -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;

View File

@ -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);