Devicetree
 help / color / mirror / Atom feed
From: Ryan Roberts <ryan.roberts@arm.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Arnd Bergmann <arnd@arndb.de>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Will Deacon <will@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Marc Zyngier <maz@kernel.org>, Oliver Upton <oupton@kernel.org>,
	Alex Williamson <alex@shazbot.org>,
	Jason Gunthorpe <jgg@nvidia.com>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Jean-Philippe Brucker <jpb@kernel.org>,
	Oded Gabbay <ogabbay@kernel.org>,
	Jonathan Corbet <corbet@lwn.net>
Cc: Ryan Roberts <ryan.roberts@arm.com>,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	kvm@vger.kernel.org, devicetree@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org
Subject: [RFC PATCH v2 05/15] misc/arm-cla: Initialize devices on CPU bringup
Date: Thu, 15 Oct 2026 15:25:39 +0100	[thread overview]
Message-ID: <20261015142552.4128028-6-ryan.roberts@arm.com> (raw)
In-Reply-To: <20261015142552.4128028-1-ryan.roberts@arm.com>

From: Jean-Philippe Brucker <jpb@kernel.org>

CLA registers are only accessible from the CPU local to the CLA, so
perform device initialization from the CPU hotplug online callback.

Clear the DATA registers, reset each attached accelerator and verify
that it returns to the expected idle state. Record the set of usable
accelerators and expose only those through PL0CTRL, and through PL1CTRL
when the kernel is running at EL2.

Reset optional timestamp and PMU controls where available so userspace
starts from a predictable state, but do not fail probing if those
optional controls cannot be programmed.

On CPU teardown, reset all known accelerators and mark the device broken
if reset fails, since the hotplug teardown path cannot safely fail.

Signed-off-by: Jean-Philippe Brucker <jpb@kernel.org>
---
 drivers/misc/arm-cla/arm-cla.h  |  14 +++
 drivers/misc/arm-cla/cla-init.c | 191 +++++++++++++++++++++++++++++++-
 drivers/misc/arm-cla/cla-ops.c  |  23 ++++
 3 files changed, 227 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/arm-cla/arm-cla.h b/drivers/misc/arm-cla/arm-cla.h
index 61176c4fce0f..6890c813a5c3 100644
--- a/drivers/misc/arm-cla/arm-cla.h
+++ b/drivers/misc/arm-cla/arm-cla.h
@@ -27,16 +27,25 @@ struct cla_domain;
  * @regs:		Registers accessed by the kernel.
  * @dev:		The platform device.
  * @pfn:		Page of registers assigned to user.
+ * @accelerators:	Available accelerators.
  * @pg_offset:		Mmap offset of this device.
  * @domain:		The domain this CLA belongs to.
+ *
+ * Mutable, protected by CPU hotplug locking:
+ * @discovered:		Discovered state is cached.
+ * @broken:		Hardware failure.
  */
 struct cla_dev {
 	unsigned int cpu;
 	void __iomem *regs;
 	struct device *dev;
 	unsigned long pfn;
+	u8 accelerators;
 	unsigned long pg_offset;
 	struct cla_domain *domain;
+
+	bool discovered;
+	bool broken;
 };
 
 /**
@@ -61,6 +70,10 @@ extern struct cla_dev **cla_lut_cpu;
 extern struct cla_dev **cla_lut_pg;
 extern unsigned int cla_nr_devs;
 
+#define cla_for_each_accid(dev, accid) \
+	for ((accid) = 0; (accid) < CLA_NUM_ACC; (accid)++) \
+		for_each_if((dev)->accelerators & BIT(accid))
+
 #define cla_dbg(_dev, fmt, ...) \
 	dev_dbg((_dev)->dev, "[%u] " fmt, (_dev)->cpu, ##__VA_ARGS__)
 #define cla_info(_dev, fmt, ...) \
@@ -98,6 +111,7 @@ void cla_domains_free(void);
 
 int cla_op_wait_lresp(struct cla_dev *dev, u64 *lresp);
 int cla_op_reset(struct cla_dev *dev, unsigned int accid);
+int cla_op_reset_all(struct cla_dev *dev);
 int cla_op_regread(struct cla_dev *dev, unsigned int accid, unsigned int regidx,
 		   size_t nregs, u64 *regs);
 int cla_op_regwrite(struct cla_dev *dev, unsigned int accid,
diff --git a/drivers/misc/arm-cla/cla-init.c b/drivers/misc/arm-cla/cla-init.c
index 6818fb8c38ed..2974bf71033c 100644
--- a/drivers/misc/arm-cla/cla-init.c
+++ b/drivers/misc/arm-cla/cla-init.c
@@ -18,18 +18,204 @@
 
 static int cla_cpuhp_state = -1;
 
+static int cla_reset_ts(struct cla_dev *dev, unsigned int accid)
+{
+	int ret;
+	u64 reg;
+
+	ret = cla_op_regread(dev, accid, CLA_REG_ACAP, 1, &reg);
+	if (ret)
+		return ret;
+	if (!FIELD_GET(CLA_ACAP_TS, reg))
+		return 0;
+
+	/*
+	 * Disable TS control from userspace, provide TS from CNTP. If we do
+	 * have to provide a timer to userspace or a virtual offset to a guest,
+	 * we'll need to make sure we have access to both TSCTRLOWNER and
+	 * TSOFFOWNER. For now best effort.
+	 */
+	reg = FIELD_PREP(CLA_TSCTRLOWNER_PL, cla_kernel_pl);
+	ret = cla_op_regwrite(dev, accid, CLA_REG_TSCTRLOWNER, 1, &reg);
+	if (!ret) {
+		reg = FIELD_PREP(CLA_TSCTRL_TS, CLA_TSCTRL_PHYSICAL);
+		ret = cla_op_regwrite(dev, accid, CLA_REG_TSCTRL, 1, &reg);
+		if (ret)
+			return ret;
+	}
+
+	reg = FIELD_PREP(CLA_TSOFFOWNER_PL, cla_kernel_pl);
+	ret = cla_op_regwrite(dev, accid, CLA_REG_TSOFFOWNER, 1, &reg);
+	if (!ret) {
+		reg = 0;
+		ret = cla_op_regwrite(dev, accid, CLA_REG_TSVOFF, 1, &reg);
+		if (ret)
+			return ret;
+		ret = cla_op_regwrite(dev, accid, CLA_REG_TSPOFF, 1, &reg);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int cla_reset_pmu(struct cla_dev *dev, unsigned int accid)
+{
+	int ret;
+	u64 reg;
+
+	/* Disable PMU access */
+	reg = FIELD_PREP(CLA_PMUOWNER_PL, cla_kernel_pl);
+	ret = cla_op_regwrite(dev, accid, CLA_REG_PMUOWNER, 1, &reg);
+	if (!ret) {
+		reg = 0;
+		ret = cla_op_regwrite(dev, accid, CLA_REG_PMURESET, 1, &reg);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+/*
+ * Discover immutable accelerator properties the first time the CPU comes
+ * online. Return: 0 on success, 1 if the accelerator is not usable, or an
+ * error.
+ */
+static int cla_dev_discover_accel(struct cla_dev *dev, unsigned int accid)
+{
+	dev->accelerators |= (1 << accid);
+
+	return 0;
+}
+
+/*
+ * Reset hardware on every online, but discover its properties only once.
+ * Return: 0 on success, 1 if the accelerator is not attached or not usable, or
+ * an error.
+ */
+static int cla_dev_setup_accel(struct cla_dev *dev, unsigned int accid)
+{
+	u64 status;
+	int ret;
+
+	/*
+	 * Reset even accelerators excluded during discovery, so that they cannot
+	 * issue memory transactions when we set up the shared MTC. Only skip
+	 * absent accelerators that were not previously discovered. Fail if a
+	 * higher PL has masked access.
+	 */
+	ret = cla_op_reset(dev, accid);
+	if (ret > 0 && (dev->accelerators & (1 << accid))) {
+		cla_err(dev, "[%u] previously discovered accelerator is absent\n",
+			accid);
+		return -ENODEV;
+	}
+	if (ret)
+		return ret;
+
+	status = cla_reg_read(dev, CLA_REG_STATUS(accid));
+	if ((status & CLA_STATUS_STATE_MASK) != CLA_STATUS_STATE_IDLE) {
+		cla_err(dev, "unexpected status 0x%llx for accelerator %d\n",
+			status, accid);
+		return -EIO;
+	}
+
+	/*
+	 * The following are nice to have, but the accelerator should work
+	 * without them.
+	 */
+	ret = cla_reset_ts(dev, accid);
+	if (ret)
+		cla_err(dev, "[%u] could not reset TS: %d\n", accid, ret);
+
+	ret = cla_reset_pmu(dev, accid);
+	if (ret)
+		cla_err(dev, "[%u] could not reset PMU: %d\n", accid, ret);
+
+	if (!dev->discovered) {
+		ret = cla_dev_discover_accel(dev, accid);
+		if (ret)
+			return ret;
+	} else if (!(dev->accelerators & (1 << accid))) {
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Clean the device before releasing it */
+static void cla_dev_reinit(struct cla_dev *dev)
+{
+	int i;
+
+	if (dev->broken)
+		return;
+
+	if (WARN_ON(cla_op_reset_all(dev))) {
+		dev->broken = true;
+		return;
+	}
+
+	if (is_kernel_in_hyp_mode())
+		cla_reg_write(dev, CLA_REG_PL1CTRL, ~0ULL);
+	cla_reg_write(dev, CLA_REG_PL0CTRL, ~0ULL);
+	for (i = 0; i < CLA_NUM_DATA_REGS; i++)
+		cla_reg_write(dev, CLA_REG_DATA(i), 0);
+	cla_reg_write(dev, CLA_REG_LRESP, 0);
+}
+
 static int cla_dev_setup(unsigned int cpu)
 {
+	int i;
+	int ret;
+	unsigned int accid;
 	struct cla_dev *dev;
+	u64 plxctrl_val = 0;
 
 	dev = cla_lut_cpu[cpu];
-	if (!dev)
+	if (!dev || dev->broken)
 		return 0;
 
 	if (WARN_ON(smp_processor_id() != cpu || dev->cpu != cpu))
 		return -EINVAL;
 
+	if (!dev->discovered) {
+		/* Discard partial results from an earlier, failed discovery. */
+		dev->accelerators = 0;
+	}
+
+	/* Clear DATA and LRESP_DATANZ */
+	for (i = 0; i < CLA_NUM_DATA_REGS; i++)
+		cla_reg_write(dev, CLA_REG_DATA(i), 0);
+
+	/*
+	 * Reset all accelerators. We restrict PLxCTRL to the accelerators that
+	 * are attached and well behaved.
+	 */
+	for (accid = 0; accid < CLA_NUM_ACC; accid++) {
+		ret = cla_dev_setup_accel(dev, accid);
+		if (ret > 0)
+			continue;
+		else if (ret < 0)
+			goto err;
+
+		plxctrl_val |= CLA_PLxCTRL_PREP(accid,
+						FIELD_PREP(CLA_PLxCTRL_AVAIL, 1));
+	}
+
+	if (is_kernel_in_hyp_mode())
+		cla_reg_write(dev, CLA_REG_PL1CTRL, plxctrl_val);
+
+	cla_reg_write(dev, CLA_REG_PL0CTRL, plxctrl_val);
+
+	if (dev->accelerators)
+		cla_info(dev, "available accelerators: 0x%02x\n",
+			 dev->accelerators);
+
+	dev->discovered = true;
 	return 0;
+err:
+	cla_dev_reinit(dev);
+	return ret;
 }
 
 static int cla_dev_teardown(unsigned int cpu)
@@ -44,6 +230,8 @@ static int cla_dev_teardown(unsigned int cpu)
 	if (!dev)
 		return 0;
 
+	cla_dev_reinit(dev);
+
 	return 0;
 }
 
@@ -85,6 +273,7 @@ static struct cla_dev *cla_dev_alloc(struct device *parent, int cpu,
 	dev->cpu = cpu;
 	dev->dev = parent;
 
+
 	/* Attempt to find device domain, or allocate a new one */
 	dev->domain = cla_dev_domain_get(dev);
 	if (IS_ERR(dev->domain))
diff --git a/drivers/misc/arm-cla/cla-ops.c b/drivers/misc/arm-cla/cla-ops.c
index 46026304a057..5d053c6aa1f7 100644
--- a/drivers/misc/arm-cla/cla-ops.c
+++ b/drivers/misc/arm-cla/cla-ops.c
@@ -207,6 +207,29 @@ int cla_op_reset(struct cla_dev *dev, unsigned int accid)
 	return 0;
 }
 
+/**
+ * cla_op_reset_all - Reset all attached accelerators
+ * @dev: CLA device.
+ *
+ * Return: 0 on success, or an error
+ */
+int cla_op_reset_all(struct cla_dev *dev)
+{
+	int ret, err = 0;
+	unsigned int accid;
+
+	cla_for_each_accid(dev, accid) {
+		ret = cla_op_reset(dev, accid);
+		if (ret && !err)
+			err = ret;
+	}
+	/*
+	 * Since all accelerators in this loop are present, cla_op_reset()
+	 * returning 1 is an error.
+	 */
+	return err <= 0 ? err : -ENODEV;
+}
+
 /* regidx is unsigned long here to avoid overflow during calculations */
 static int cla_op_access_reg(struct cla_dev *dev, u8 op,
 			     enum cla_launch_data_mode data_mode,
-- 
2.43.0


  parent reply	other threads:[~2026-10-15 14:26 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15 14:25 [RFC PATCH v2 00/15] Arm Core Local Accelerator Driver Ryan Roberts
2026-10-15 14:25 ` [RFC PATCH v2 01/15] misc/arm-cla: Add driver skeleton and documentation Ryan Roberts
2026-10-15 14:35   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 02/15] misc/arm-cla: Add launch operation helpers Ryan Roberts
2026-10-15 14:42   ` sashiko-bot
2026-10-15 16:17   ` Greg Kroah-Hartman
2026-10-15 14:25 ` [RFC PATCH v2 03/15] dt-bindings: misc: Add Arm Core Local Accelerator Ryan Roberts
2026-10-15 14:25 ` [RFC PATCH v2 04/15] misc/arm-cla: Probe firmware-described devices Ryan Roberts
2026-10-15 14:37   ` sashiko-bot
2026-10-15 14:25 ` Ryan Roberts [this message]
2026-10-15 14:25 ` [RFC PATCH v2 06/15] misc/arm-cla: Accelerator context save and restore Ryan Roberts
2026-10-15 14:41   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 07/15] misc/arm-cla: Set up memory translation context Ryan Roberts
2026-10-15 14:25 ` [RFC PATCH v2 08/15] misc/arm-cla: Manage domain contexts Ryan Roberts
2026-10-15 14:38   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 09/15] misc/arm-cla: Add userspace interface Ryan Roberts
2026-10-15 14:47   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 10/15] KVM: arm64: Introduce support to pin VMIDs Ryan Roberts
2026-10-15 14:38   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 11/15] KVM: arm64: Export stage-2 MMU config Ryan Roberts
2026-10-15 14:43   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 12/15] vfio: Support VFIO devices that access the CPU MMU Ryan Roberts
2026-10-15 14:44   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 13/15] misc/arm-cla: Add stage-2 MTC and VM context switch Ryan Roberts
2026-10-15 14:40   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 14/15] misc/arm-cla: Support exclusive context assignment Ryan Roberts
2026-10-15 14:41   ` sashiko-bot
2026-10-15 14:25 ` [RFC PATCH v2 15/15] misc/arm-cla: Add vfio-mdev user interface Ryan Roberts
2026-10-15 14:49   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261015142552.4128028-6-ryan.roberts@arm.com \
    --to=ryan.roberts@arm.com \
    --cc=alex@shazbot.org \
    --cc=arnd@arndb.de \
    --cc=catalin.marinas@arm.com \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=jgg@nvidia.com \
    --cc=jpb@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=maz@kernel.org \
    --cc=ogabbay@kernel.org \
    --cc=oupton@kernel.org \
    --cc=robh@kernel.org \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox