Commit a7419587 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+42 −9
Original line number Diff line number Diff line
@@ -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;
}