mirror of
https://github.com/torvalds/linux.git
synced 2024-10-23 05:30:55 +00:00
I3C for 6.10
Core: - Allow device driver to trigger controller runtime PM Drivers: - dw: hot-join support - svc: better IBI handling -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmZRluEACgkQY6TcMGxw OjJuEw//eCoRPLtAinSBRrPirmWyryVIhMOjZFK+8752Ycau7sl/RIp+8N6QiW8C cOLugoONPq90nlVOxwYq8jtF+qaPTBkSXNbsJox0qnuZrJsLjyF1CxIYUVMClQ5/ f7RGIhZv7paF26qP0li3ewQ8L0F13dCbdSzC2b9tI/Yy/GJmpzgpC6nv18YrlGK3 1+L6wmz4rhR18pbjZm8myMoa//NG0lkmXhAwPYF+X9n9IJufBNhsijW1akFmqbvE +8DVWGLDqaY4AnotD6XBLsbFQsPF+Z0F1oVO1cL45Ned92yBsnyEKbnkH0K5SLj+ FMvkiPGAOp5m1KxPLJ3XfthBUJp14SPufaJTsJo3tzQ5lE+4CMUkAD1Z3YTPqAZB HfkW48trejyVf8irUTv7NzZFQ62DPUREIHWcjnLASymgIqSWgiNCKHbufo61sU6u FoqMgZdcvsGmxz7Ld7RnVeU2hxaGqMNrc5jLC70YtrsFSkQB3bIfkHKhUSqeKdeO 3cD1pyNfgnqIJ4uhb3fG1bm/MkNcOpupTNlP+XVGBYTIpIy/HWfgPCyQlO9qCXGd ZBbp+FojyAYb9UTbXhAVJn9U8kvqcRHI5jRuYXmxJuptSfa2semoPtQ6Ism7n/2+ geHMAUs9s4KT670zzbv7FsPxma/b4mTcqpeCDxzjCQrNkGyfvDA= =EmUy -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull i3c updates from Alexandre Belloni: "Runtime PM (power management) is improved and hot-join support has been added to the dw controller driver. Core: - Allow device driver to trigger controller runtime PM Drivers: - dw: hot-join support - svc: better IBI handling" * tag 'i3c/for-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: i3c: dw: Add hot-join support. i3c: master: Enable runtime PM for master controller i3c: master: svc: fix invalidate IBI type and miss call client IBI handler i3c: master: svc: change ENXIO to EAGAIN when IBI occurs during start frame i3c: Add comment for -EAGAIN in i3c_device_do_priv_xfers()
This commit is contained in:
commit
4286e1fceb
|
@ -27,6 +27,10 @@
|
||||||
* This function can sleep and thus cannot be called in atomic context.
|
* This function can sleep and thus cannot be called in atomic context.
|
||||||
*
|
*
|
||||||
* Return: 0 in case of success, a negative error core otherwise.
|
* Return: 0 in case of success, a negative error core otherwise.
|
||||||
|
* -EAGAIN: controller lost address arbitration. Target
|
||||||
|
* (IBI, HJ or controller role request) win the bus. Client
|
||||||
|
* driver needs to resend the 'xfers' some time later.
|
||||||
|
* See I3C spec ver 1.1.1 09-Jun-2021. Section: 5.1.2.2.3.
|
||||||
*/
|
*/
|
||||||
int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
int i3c_device_do_priv_xfers(struct i3c_device *dev,
|
||||||
struct i3c_priv_xfer *xfers,
|
struct i3c_priv_xfer *xfers,
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
@ -2812,6 +2813,10 @@ int i3c_master_register(struct i3c_master_controller *master,
|
||||||
|
|
||||||
i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
|
i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
|
||||||
|
|
||||||
|
pm_runtime_no_callbacks(&master->dev);
|
||||||
|
pm_suspend_ignore_children(&master->dev, true);
|
||||||
|
pm_runtime_enable(&master->dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We're done initializing the bus and the controller, we can now
|
* We're done initializing the bus and the controller, we can now
|
||||||
* register I3C devices discovered during the initial DAA.
|
* register I3C devices discovered during the initial DAA.
|
||||||
|
@ -2849,6 +2854,7 @@ void i3c_master_unregister(struct i3c_master_controller *master)
|
||||||
i3c_master_i2c_adapter_cleanup(master);
|
i3c_master_i2c_adapter_cleanup(master);
|
||||||
i3c_master_unregister_i3c_devs(master);
|
i3c_master_unregister_i3c_devs(master);
|
||||||
i3c_master_bus_cleanup(master);
|
i3c_master_bus_cleanup(master);
|
||||||
|
pm_runtime_disable(&master->dev);
|
||||||
device_unregister(&master->dev);
|
device_unregister(&master->dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i3c_master_unregister);
|
EXPORT_SYMBOL_GPL(i3c_master_unregister);
|
||||||
|
|
|
@ -1136,6 +1136,23 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
|
||||||
data->ibi_pool = NULL;
|
data->ibi_pool = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dw_i3c_master_enable_sir_signal(struct dw_i3c_master *master, bool enable)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(master->regs + INTR_STATUS_EN);
|
||||||
|
reg &= ~INTR_IBI_THLD_STAT;
|
||||||
|
if (enable)
|
||||||
|
reg |= INTR_IBI_THLD_STAT;
|
||||||
|
writel(reg, master->regs + INTR_STATUS_EN);
|
||||||
|
|
||||||
|
reg = readl(master->regs + INTR_SIGNAL_EN);
|
||||||
|
reg &= ~INTR_IBI_THLD_STAT;
|
||||||
|
if (enable)
|
||||||
|
reg |= INTR_IBI_THLD_STAT;
|
||||||
|
writel(reg, master->regs + INTR_SIGNAL_EN);
|
||||||
|
}
|
||||||
|
|
||||||
static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
|
static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
|
||||||
struct i3c_dev_desc *dev,
|
struct i3c_dev_desc *dev,
|
||||||
u8 idx, bool enable)
|
u8 idx, bool enable)
|
||||||
|
@ -1170,23 +1187,34 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
|
||||||
}
|
}
|
||||||
writel(reg, master->regs + IBI_SIR_REQ_REJECT);
|
writel(reg, master->regs + IBI_SIR_REQ_REJECT);
|
||||||
|
|
||||||
if (global) {
|
if (global)
|
||||||
reg = readl(master->regs + INTR_STATUS_EN);
|
dw_i3c_master_enable_sir_signal(master, enable);
|
||||||
reg &= ~INTR_IBI_THLD_STAT;
|
|
||||||
if (enable)
|
|
||||||
reg |= INTR_IBI_THLD_STAT;
|
|
||||||
writel(reg, master->regs + INTR_STATUS_EN);
|
|
||||||
|
|
||||||
reg = readl(master->regs + INTR_SIGNAL_EN);
|
|
||||||
reg &= ~INTR_IBI_THLD_STAT;
|
|
||||||
if (enable)
|
|
||||||
reg |= INTR_IBI_THLD_STAT;
|
|
||||||
writel(reg, master->regs + INTR_SIGNAL_EN);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&master->devs_lock, flags);
|
spin_unlock_irqrestore(&master->devs_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
|
||||||
|
{
|
||||||
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
|
|
||||||
|
dw_i3c_master_enable_sir_signal(master, true);
|
||||||
|
writel(readl(master->regs + DEVICE_CTRL) & ~DEV_CTRL_HOT_JOIN_NACK,
|
||||||
|
master->regs + DEVICE_CTRL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
|
||||||
|
{
|
||||||
|
struct dw_i3c_master *master = to_dw_i3c_master(m);
|
||||||
|
|
||||||
|
writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
|
||||||
|
master->regs + DEVICE_CTRL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
|
static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
|
||||||
{
|
{
|
||||||
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
|
||||||
|
@ -1326,6 +1354,8 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
|
||||||
|
|
||||||
if (IBI_TYPE_SIRQ(reg)) {
|
if (IBI_TYPE_SIRQ(reg)) {
|
||||||
dw_i3c_master_handle_ibi_sir(master, reg);
|
dw_i3c_master_handle_ibi_sir(master, reg);
|
||||||
|
} else if (IBI_TYPE_HJ(reg)) {
|
||||||
|
queue_work(master->base.wq, &master->hj_work);
|
||||||
} else {
|
} else {
|
||||||
len = IBI_QUEUE_STATUS_DATA_LEN(reg);
|
len = IBI_QUEUE_STATUS_DATA_LEN(reg);
|
||||||
dev_info(&master->base.dev,
|
dev_info(&master->base.dev,
|
||||||
|
@ -1393,6 +1423,8 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ibi_ops = {
|
||||||
.enable_ibi = dw_i3c_master_enable_ibi,
|
.enable_ibi = dw_i3c_master_enable_ibi,
|
||||||
.disable_ibi = dw_i3c_master_disable_ibi,
|
.disable_ibi = dw_i3c_master_disable_ibi,
|
||||||
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
|
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
|
||||||
|
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
|
||||||
|
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* default platform ops implementations */
|
/* default platform ops implementations */
|
||||||
|
@ -1412,6 +1444,14 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = {
|
||||||
.set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop,
|
.set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void dw_i3c_hj_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct dw_i3c_master *master =
|
||||||
|
container_of(work, typeof(*master), hj_work);
|
||||||
|
|
||||||
|
i3c_master_do_daa(&master->base);
|
||||||
|
}
|
||||||
|
|
||||||
int dw_i3c_common_probe(struct dw_i3c_master *master,
|
int dw_i3c_common_probe(struct dw_i3c_master *master,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
@ -1469,6 +1509,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
||||||
if (master->ibi_capable)
|
if (master->ibi_capable)
|
||||||
ops = &dw_mipi_i3c_ibi_ops;
|
ops = &dw_mipi_i3c_ibi_ops;
|
||||||
|
|
||||||
|
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
||||||
ret = i3c_master_register(&master->base, &pdev->dev, ops, false);
|
ret = i3c_master_register(&master->base, &pdev->dev, ops, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_assert_rst;
|
goto err_assert_rst;
|
||||||
|
|
|
@ -57,6 +57,8 @@ struct dw_i3c_master {
|
||||||
|
|
||||||
/* platform-specific data */
|
/* platform-specific data */
|
||||||
const struct dw_i3c_platform_ops *platform_ops;
|
const struct dw_i3c_platform_ops *platform_ops;
|
||||||
|
|
||||||
|
struct work_struct hj_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dw_i3c_platform_ops {
|
struct dw_i3c_platform_ops {
|
||||||
|
|
|
@ -415,6 +415,19 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&master->lock);
|
mutex_lock(&master->lock);
|
||||||
|
/*
|
||||||
|
* IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
|
||||||
|
* readl_relaxed_poll_timeout() to return immediately. Consequently,
|
||||||
|
* ibitype will be 0 since it was last updated only after the 8th SCL
|
||||||
|
* cycle, leading to missed client IBI handlers.
|
||||||
|
*
|
||||||
|
* A typical scenario is when IBIWON occurs and bus arbitration is lost
|
||||||
|
* at svc_i3c_master_priv_xfers().
|
||||||
|
*
|
||||||
|
* Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
|
||||||
|
*/
|
||||||
|
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
||||||
|
|
||||||
/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
|
/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
|
||||||
writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
|
writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
|
||||||
SVC_I3C_MCTRL_IBIRESP_AUTO,
|
SVC_I3C_MCTRL_IBIRESP_AUTO,
|
||||||
|
@ -429,9 +442,6 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||||
goto reenable_ibis;
|
goto reenable_ibis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the interrupt status */
|
|
||||||
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
|
||||||
|
|
||||||
status = readl(master->regs + SVC_I3C_MSTATUS);
|
status = readl(master->regs + SVC_I3C_MSTATUS);
|
||||||
ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
|
ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
|
||||||
ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
|
ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
|
||||||
|
@ -1080,7 +1090,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||||
* and yield the above events handler.
|
* and yield the above events handler.
|
||||||
*/
|
*/
|
||||||
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
||||||
ret = -ENXIO;
|
ret = -EAGAIN;
|
||||||
*actual_len = 0;
|
*actual_len = 0;
|
||||||
goto emit_stop;
|
goto emit_stop;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user