Commit a7419587 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull i3c updates from Alexandre Belloni:

 - svc: fix suspend/resume on some platforms, fix locking issues

* tag 'i3c/for-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  i3c: master: svc: add NACK check after start byte sent
  i3c: master: svc: fix cpu schedule in spin lock
  i3c: master: svc: fix i3c suspend/resume issue
parents ae80b404 49b472eb
......@@ -92,6 +92,7 @@
#define SVC_I3C_MINTCLR 0x094
#define SVC_I3C_MINTMASKED 0x098
#define SVC_I3C_MERRWARN 0x09C
#define SVC_I3C_MERRWARN_NACK BIT(2)
#define SVC_I3C_MDMACTRL 0x0A0
#define SVC_I3C_MDATACTRL 0x0AC
#define SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
......@@ -145,6 +146,11 @@ struct svc_i3c_xfer {
struct svc_i3c_cmd cmds[];
};
struct svc_i3c_regs_save {
u32 mconfig;
u32 mdynaddr;
};
/**
* struct svc_i3c_master - Silvaco I3C Master structure
* @base: I3C master controller
......@@ -173,6 +179,7 @@ struct svc_i3c_master {
struct i3c_master_controller base;
struct device *dev;
void __iomem *regs;
struct svc_i3c_regs_save saved_regs;
u32 free_slots;
u8 addrs[SVC_I3C_MAX_DEVS];
struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
......@@ -1008,6 +1015,11 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
if (ret)
goto emit_stop;
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
ret = -ENXIO;
goto emit_stop;
}
if (rnw)
ret = svc_i3c_master_read(master, in, xfer_len);
else
......@@ -1090,12 +1102,6 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
if (!xfer)
return;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
return;
}
svc_i3c_master_clear_merrwarn(master);
svc_i3c_master_flush_fifo(master);
......@@ -1110,9 +1116,6 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
break;
}
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
xfer->ret = ret;
complete(&xfer->comp);
......@@ -1133,6 +1136,13 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
struct svc_i3c_xfer *xfer)
{
unsigned long flags;
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
return;
}
init_completion(&xfer->comp);
spin_lock_irqsave(&master->xferqueue.lock, flags);
......@@ -1143,6 +1153,9 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
svc_i3c_master_start_xfer_locked(master);
}
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
pm_runtime_mark_last_busy(master->dev);
pm_runtime_put_autosuspend(master->dev);
}
static bool
......@@ -1579,10 +1592,28 @@ static void svc_i3c_master_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
static void svc_i3c_save_regs(struct svc_i3c_master *master)
{
master->saved_regs.mconfig = readl(master->regs + SVC_I3C_MCONFIG);
master->saved_regs.mdynaddr = readl(master->regs + SVC_I3C_MDYNADDR);
}
static void svc_i3c_restore_regs(struct svc_i3c_master *master)
{
if (readl(master->regs + SVC_I3C_MDYNADDR) !=
master->saved_regs.mdynaddr) {
writel(master->saved_regs.mconfig,
master->regs + SVC_I3C_MCONFIG);
writel(master->saved_regs.mdynaddr,
master->regs + SVC_I3C_MDYNADDR);
}
}
static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
{
struct svc_i3c_master *master = dev_get_drvdata(dev);
svc_i3c_save_regs(master);
svc_i3c_master_unprepare_clks(master);
pinctrl_pm_select_sleep_state(dev);
......@@ -1596,6 +1627,8 @@ static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
svc_i3c_master_prepare_clks(master);
svc_i3c_restore_regs(master);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment