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>,
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,
dri-devel@lists.freedesktop.org, linux-doc@vger.kernel.org
Subject: [RFC PATCH v1 4/8] misc/arm-cla: Initialize devices on CPU bringup
Date: Fri, 17 Jul 2026 11:47:48 +0100 [thread overview]
Message-ID: <20260717104759.123203-5-ryan.roberts@arm.com> (raw)
In-Reply-To: <20260717104759.123203-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 | 178 ++++++++++++++++++++++++++++++++
drivers/misc/arm-cla/cla-ops.c | 19 ++++
3 files changed, 211 insertions(+)
diff --git a/drivers/misc/arm-cla/arm-cla.h b/drivers/misc/arm-cla/arm-cla.h
index 1853daa200e4..57d0acd01d3a 100644
--- a/drivers/misc/arm-cla/arm-cla.h
+++ b/drivers/misc/arm-cla/arm-cla.h
@@ -28,16 +28,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, only accessed under @lock:
+ * @lock: Protects the following members.
+ * @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;
+
+ struct mutex lock;
+ bool broken;
};
/**
@@ -62,6 +71,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, ...) \
@@ -97,6 +110,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 731ca08c9b79..ba7552a2d886 100644
--- a/drivers/misc/arm-cla/cla-init.c
+++ b/drivers/misc/arm-cla/cla-init.c
@@ -17,18 +17,192 @@
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, ®);
+ 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, ®);
+ if (!ret) {
+ reg = FIELD_PREP(CLA_TSCTRL_TS, CLA_TSCTRL_PHYSICAL);
+ ret = cla_op_regwrite(dev, accid, CLA_REG_TSCTRL, 1, ®);
+ if (ret)
+ return ret;
+ }
+
+ reg = FIELD_PREP(CLA_TSOFFOWNER_PL, cla_kernel_pl);
+ ret = cla_op_regread(dev, accid, CLA_REG_TSOFFOWNER, 1, ®);
+ if (!ret) {
+ reg = 0;
+ ret = cla_op_regwrite(dev, accid, CLA_REG_TSVOFF, 1, ®);
+ if (ret)
+ return ret;
+ ret = cla_op_regwrite(dev, accid, CLA_REG_TSPOFF, 1, ®);
+ 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, ®);
+ if (!ret) {
+ reg = 0;
+ ret = cla_op_regwrite(dev, accid, CLA_REG_PMURESET, 1, ®);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * 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;
+
+ /*
+ * Probe and reset. Return 1 if no accelerator is attached, happy days.
+ * If the accelerator is unavailable (masked by higher PL with PLxCTRL),
+ * return an error. Individual accelerators cannot be owned by a higher
+ * PL, since the MTC is shared between all accelerators attached to this
+ * CLA.
+ *
+ * Some accelerators will be masked due to returning 1 further down this
+ * function. If we end up with no dev->accelerators because of that we
+ * won't setup the MTC, but as long as this reset succeeds, the
+ * accelerator is not issuing memory transactions.
+ */
+ ret = cla_op_reset(dev, accid);
+ 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);
+
+ return 0;
+}
+
+/* Clean the device before releasing it */
+static void cla_dev_reinit(struct cla_dev *dev)
+{
+ int i;
+ bool broken;
+
+ mutex_lock(&dev->lock);
+ broken = dev->broken;
+ mutex_unlock(&dev->lock);
+ if (broken)
+ return;
+
+ if (WARN_ON(cla_op_reset_all(dev))) {
+ mutex_lock(&dev->lock);
+ dev->broken = true;
+ mutex_unlock(&dev->lock);
+ 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;
+ bool broken;
+ unsigned int accid;
struct cla_dev *dev;
+ u64 plxctrl_val = 0;
dev = cla_lut_cpu[cpu];
if (!dev)
return 0;
+ mutex_lock(&dev->lock);
+ broken = dev->broken;
+ mutex_unlock(&dev->lock);
+ if (broken)
+ return 0;
+
if (WARN_ON(smp_processor_id() != cpu || dev->cpu != cpu))
return -EINVAL;
+ /* 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;
+
+ dev->accelerators |= (1 << accid);
+ 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);
+
return 0;
+err:
+ cla_dev_reinit(dev);
+ return ret;
}
static int cla_dev_teardown(unsigned int cpu)
@@ -43,6 +217,8 @@ static int cla_dev_teardown(unsigned int cpu)
if (!dev)
return 0;
+ cla_dev_reinit(dev);
+
return 0;
}
@@ -84,6 +260,8 @@ static struct cla_dev *cla_dev_alloc(struct device *parent, int cpu,
dev->cpu = cpu;
dev->dev = parent;
+ mutex_init(&dev->lock);
+
/* 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 d594344a2eb4..f9e5570e89c1 100644
--- a/drivers/misc/arm-cla/cla-ops.c
+++ b/drivers/misc/arm-cla/cla-ops.c
@@ -143,6 +143,25 @@ int cla_op_reset(struct cla_dev *dev, unsigned int accid)
return ret;
}
+/**
+ * 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;
+ unsigned int accid;
+
+ cla_for_each_accid(dev, accid) {
+ ret = cla_op_reset(dev, accid);
+ if (ret)
+ return ret < 0 ? ret : -ENODEV;
+ }
+ return 0;
+}
+
static int cla_op_access_reg(struct cla_dev *dev, u8 op,
enum cla_launch_data_mode data_mode,
unsigned int accid, unsigned int regidx,
--
2.43.0
next prev parent reply other threads:[~2026-07-17 10:58 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 10:47 [RFC PATCH v1 0/8] Arm Core Local Accelerator Driver Ryan Roberts
2026-07-17 10:47 ` [RFC PATCH v1 1/8] misc/arm-cla: Add driver skeleton and documentation Ryan Roberts
2026-07-17 10:56 ` sashiko-bot
2026-07-17 13:49 ` Arnd Bergmann
2026-07-17 15:44 ` Ryan Roberts
2026-07-17 16:10 ` Arnd Bergmann
2026-07-17 10:47 ` [RFC PATCH v1 2/8] misc/arm-cla: Add launch operation helpers Ryan Roberts
2026-07-17 11:01 ` sashiko-bot
2026-07-17 12:16 ` Arnd Bergmann
2026-07-17 10:47 ` [RFC PATCH v1 3/8] misc/arm-cla: Probe firmware-described devices Ryan Roberts
2026-07-17 10:57 ` sashiko-bot
2026-07-17 12:25 ` Arnd Bergmann
2026-07-17 12:36 ` Ryan Roberts
2026-07-17 10:47 ` Ryan Roberts [this message]
2026-07-17 10:58 ` [RFC PATCH v1 4/8] misc/arm-cla: Initialize devices on CPU bringup sashiko-bot
2026-07-17 10:47 ` [RFC PATCH v1 5/8] misc/arm-cla: Accelerator context save and restore Ryan Roberts
2026-07-17 10:58 ` sashiko-bot
2026-07-17 10:47 ` [RFC PATCH v1 6/8] misc/arm-cla: Set up memory translation context Ryan Roberts
2026-07-17 11:02 ` sashiko-bot
2026-07-17 10:47 ` [RFC PATCH v1 7/8] misc/arm-cla: Manage domain contexts Ryan Roberts
2026-07-17 11:01 ` sashiko-bot
2026-07-17 10:47 ` [RFC PATCH v1 8/8] misc/arm-cla: Add userspace interface Ryan Roberts
2026-07-17 11:10 ` sashiko-bot
2026-07-17 12:54 ` Arnd Bergmann
2026-07-17 14:35 ` Ryan Roberts
2026-07-17 15:31 ` Arnd Bergmann
2026-07-17 16:21 ` Ryan Roberts
2026-07-17 11:33 ` [RFC PATCH v1 0/8] Arm Core Local Accelerator Driver Will Deacon
2026-07-17 12:09 ` Marc Zyngier
2026-07-17 12:33 ` Ryan Roberts
2026-07-17 12:30 ` Ryan Roberts
2026-07-17 13:32 ` Jason Gunthorpe
2026-07-17 13:42 ` Ryan Roberts
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=20260717104759.123203-5-ryan.roberts@arm.com \
--to=ryan.roberts@arm.com \
--cc=arnd@arndb.de \
--cc=catalin.marinas@arm.com \
--cc=corbet@lwn.net \
--cc=dri-devel@lists.freedesktop.org \
--cc=gregkh@linuxfoundation.org \
--cc=jpb@kernel.org \
--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=ogabbay@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.