* [PATCH v2] phy: freescale: imx8m-pcie: fix pcie link-up instability
From: Marcel Ziswiler @ 2024-03-27 7:19 UTC (permalink / raw)
To: linux-phy
Cc: linux-imx, linux-kernel, kernel, Richard Zhu, Lucas Stach,
linux-arm-kernel, stable, Marcel Ziswiler, Fabio Estevam,
Heiko Stuebner, Kishon Vijay Abraham I, Rob Herring, Sascha Hauer,
Sergio Paracuellos, Shawn Guo, Tim Harvey, Vinod Koul, Yang Li,
imx
From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
On the i.MX 8M Mini, the AUX_PLL_REFCLK_SEL has to be left at its reset
default of AUX_IN (PLL clock).
Background Information:
In our automated testing setup, we use Delock Mini-PCIe SATA cards [1].
While this setup has proven very stable overall we noticed upstream on
the i.MX 8M Mini fails quite regularly (about 50/50) to bring up the
PCIe link while with NXP's downstream BSP 5.15.71_2.2.2 it always works.
As that old downstream stuff was quite different, I first also tried
NXP's latest downstream BSP 6.1.55_2.2.0 which from a PCIe point of view
is fairly vanilla, however, also there the PCIe link-up was not stable.
Comparing and debugging I noticed that upstream explicitly configures
the AUX_PLL_REFCLK_SEL to I_PLL_REFCLK_FROM_SYSPLL while working
downstream [2] leaving it at reset defaults of AUX_IN (PLL clock).
Unfortunately, the TRM does not mention any further details about this
register (both for the i.MX 8M Mini as well as the Plus).
NXP confirmed their validation codes for the i.MX8MM PCIe doesn't
configure cmn_reg063 (offset: 0x18C).
BTW: On the i.MX 8M Plus we have not seen any issues with PCIe with the
exact same setup which is why I left it unchanged.
[1] https://www.delock.com/produkt/95233/merkmale.html
[2] https://github.com/nxp-imx/linux-imx/blob/lf-5.15.71-2.2.0/drivers/pci/controller/dwc/pci-imx6.c#L1548
Fixes: 1aa97b002258 ("phy: freescale: pcie: Initialize the imx8 pcie standalone phy driver")
Cc: stable@vger.kernel.org # 6.1.x: ca679c49: phy: freescale: imx8m-pcie: Refine i.MX8MM PCIe PHY driver
Cc: stable@vger.kernel.org # 6.1.x
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Reviewed-by: Richard Zhu <hongxing.zhu@nxp.com>
Link: https://lore.kernel.org/all/AS8PR04MB867661386FEA07649771FBE18C362@AS8PR04MB8676.eurprd04.prod.outlook.com
---
Changes in v2:
- Reword the commmit message.
- Meld the background information from the cover letter into the commit
message as suggested by Fabio. Thanks!
- Document NXP's confirmation from their validation codes and add
Richard Zhu's reviewed-by. Thanks!
drivers/phy/freescale/phy-fsl-imx8m-pcie.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
index b700f52b7b67..11fcb1867118 100644
--- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -110,8 +110,10 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
/* Source clock from SoC internal PLL */
writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062);
- writel(AUX_PLL_REFCLK_SEL_SYS_PLL,
- imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063);
+ if (imx8_phy->drvdata->variant != IMX8MM) {
+ writel(AUX_PLL_REFCLK_SEL_SYS_PLL,
+ imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063);
+ }
val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM;
writel(val | ANA_AUX_RX_TERM_GND_EN,
imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064);
--
2.44.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 3/6] KEYS: trusted: Introduce NXP DCP-backed trusted keys
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module, Richard Weinberger, David Oberhollenzer
In-Reply-To: <20240327082454.13729-1-david@sigma-star.at>
DCP (Data Co-Processor) is the little brother of NXP's CAAM IP.
Beside of accelerated crypto operations, it also offers support for
hardware-bound keys. Using this feature it is possible to implement a blob
mechanism similar to what CAAM offers. Unlike on CAAM, constructing and
parsing the blob has to happen in software (i.e. the kernel).
The software-based blob format used by DCP trusted keys encrypts
the payload using AES-128-GCM with a freshly generated random key and nonce.
The random key itself is AES-128-ECB encrypted using the DCP unique
or OTP key.
The DCP trusted key blob format is:
/*
* struct dcp_blob_fmt - DCP BLOB format.
*
* @fmt_version: Format version, currently being %1
* @blob_key: Random AES 128 key which is used to encrypt @payload,
* @blob_key itself is encrypted with OTP or UNIQUE device key in
* AES-128-ECB mode by DCP.
* @nonce: Random nonce used for @payload encryption.
* @payload_len: Length of the plain text @payload.
* @payload: The payload itself, encrypted using AES-128-GCM and @blob_key,
* GCM auth tag of size AES_BLOCK_SIZE is attached at the end of it.
*
* The total size of a DCP BLOB is sizeof(struct dcp_blob_fmt) + @payload_len +
* AES_BLOCK_SIZE.
*/
struct dcp_blob_fmt {
__u8 fmt_version;
__u8 blob_key[AES_KEYSIZE_128];
__u8 nonce[AES_KEYSIZE_128];
__le32 payload_len;
__u8 payload[];
} __packed;
By default the unique key is used. It is also possible to use the
OTP key. While the unique key should be unique it is not documented how
this key is derived. Therefore selection the OTP key is supported as
well via the use_otp_key module parameter.
Co-developed-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Richard Weinberger <richard@nod.at>
Co-developed-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Gstir <david@sigma-star.at>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
---
include/keys/trusted_dcp.h | 11 +
security/keys/trusted-keys/Kconfig | 8 +
security/keys/trusted-keys/Makefile | 2 +
security/keys/trusted-keys/trusted_core.c | 6 +-
security/keys/trusted-keys/trusted_dcp.c | 313 ++++++++++++++++++++++
5 files changed, 339 insertions(+), 1 deletion(-)
create mode 100644 include/keys/trusted_dcp.h
create mode 100644 security/keys/trusted-keys/trusted_dcp.c
diff --git a/include/keys/trusted_dcp.h b/include/keys/trusted_dcp.h
new file mode 100644
index 000000000000..9aaa42075b40
--- /dev/null
+++ b/include/keys/trusted_dcp.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 sigma star gmbh
+ */
+
+#ifndef TRUSTED_DCP_H
+#define TRUSTED_DCP_H
+
+extern struct trusted_key_ops dcp_trusted_key_ops;
+
+#endif
diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
index 553dc117f385..1fb8aa001995 100644
--- a/security/keys/trusted-keys/Kconfig
+++ b/security/keys/trusted-keys/Kconfig
@@ -39,6 +39,14 @@ config TRUSTED_KEYS_CAAM
Enable use of NXP's Cryptographic Accelerator and Assurance Module
(CAAM) as trusted key backend.
+config TRUSTED_KEYS_DCP
+ bool "DCP-based trusted keys"
+ depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS
+ default y
+ select HAVE_TRUSTED_KEYS
+ help
+ Enable use of NXP's DCP (Data Co-Processor) as trusted key backend.
+
if !HAVE_TRUSTED_KEYS
comment "No trust source selected!"
endif
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 735aa0bc08ef..f0f3b27f688b 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -14,3 +14,5 @@ trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o
trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o
+
+trusted-$(CONFIG_TRUSTED_KEYS_DCP) += trusted_dcp.o
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index fee1ab2c734d..5113aeae5628 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -10,6 +10,7 @@
#include <keys/trusted-type.h>
#include <keys/trusted_tee.h>
#include <keys/trusted_caam.h>
+#include <keys/trusted_dcp.h>
#include <keys/trusted_tpm.h>
#include <linux/capability.h>
#include <linux/err.h>
@@ -30,7 +31,7 @@ MODULE_PARM_DESC(rng, "Select trusted key RNG");
static char *trusted_key_source;
module_param_named(source, trusted_key_source, charp, 0);
-MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee or caam)");
+MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam or dcp)");
static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_TPM)
@@ -42,6 +43,9 @@ static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_CAAM)
{ "caam", &trusted_key_caam_ops },
#endif
+#if defined(CONFIG_TRUSTED_KEYS_DCP)
+ { "dcp", &dcp_trusted_key_ops },
+#endif
};
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c
new file mode 100644
index 000000000000..16c44aafeab3
--- /dev/null
+++ b/security/keys/trusted-keys/trusted_dcp.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 sigma star gmbh
+ */
+
+#include <crypto/aead.h>
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/gcm.h>
+#include <crypto/skcipher.h>
+#include <keys/trusted-type.h>
+#include <linux/key-type.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <soc/fsl/dcp.h>
+
+#define DCP_BLOB_VERSION 1
+#define DCP_BLOB_AUTHLEN 16
+
+/**
+ * struct dcp_blob_fmt - DCP BLOB format.
+ *
+ * @fmt_version: Format version, currently being %1.
+ * @blob_key: Random AES 128 key which is used to encrypt @payload,
+ * @blob_key itself is encrypted with OTP or UNIQUE device key in
+ * AES-128-ECB mode by DCP.
+ * @nonce: Random nonce used for @payload encryption.
+ * @payload_len: Length of the plain text @payload.
+ * @payload: The payload itself, encrypted using AES-128-GCM and @blob_key,
+ * GCM auth tag of size DCP_BLOB_AUTHLEN is attached at the end of it.
+ *
+ * The total size of a DCP BLOB is sizeof(struct dcp_blob_fmt) + @payload_len +
+ * DCP_BLOB_AUTHLEN.
+ */
+struct dcp_blob_fmt {
+ __u8 fmt_version;
+ __u8 blob_key[AES_KEYSIZE_128];
+ __u8 nonce[AES_KEYSIZE_128];
+ __le32 payload_len;
+ __u8 payload[];
+} __packed;
+
+static bool use_otp_key;
+module_param_named(dcp_use_otp_key, use_otp_key, bool, 0);
+MODULE_PARM_DESC(dcp_use_otp_key, "Use OTP instead of UNIQUE key for sealing");
+
+static bool skip_zk_test;
+module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0);
+MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed");
+
+static unsigned int calc_blob_len(unsigned int payload_len)
+{
+ return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN;
+}
+
+static int do_dcp_crypto(u8 *in, u8 *out, bool do_encrypt)
+{
+ struct skcipher_request *req = NULL;
+ struct scatterlist src_sg, dst_sg;
+ struct crypto_skcipher *tfm;
+ u8 paes_key[DCP_PAES_KEYSIZE];
+ DECLARE_CRYPTO_WAIT(wait);
+ int res = 0;
+
+ if (use_otp_key)
+ paes_key[0] = DCP_PAES_KEY_OTP;
+ else
+ paes_key[0] = DCP_PAES_KEY_UNIQUE;
+
+ tfm = crypto_alloc_skcipher("ecb-paes-dcp", CRYPTO_ALG_INTERNAL,
+ CRYPTO_ALG_INTERNAL);
+ if (IS_ERR(tfm)) {
+ res = PTR_ERR(tfm);
+ tfm = NULL;
+ goto out;
+ }
+
+ req = skcipher_request_alloc(tfm, GFP_NOFS);
+ if (!req) {
+ res = -ENOMEM;
+ goto out;
+ }
+
+ skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &wait);
+ res = crypto_skcipher_setkey(tfm, paes_key, sizeof(paes_key));
+ if (res < 0)
+ goto out;
+
+ sg_init_one(&src_sg, in, AES_KEYSIZE_128);
+ sg_init_one(&dst_sg, out, AES_KEYSIZE_128);
+ skcipher_request_set_crypt(req, &src_sg, &dst_sg, AES_KEYSIZE_128,
+ NULL);
+
+ if (do_encrypt)
+ res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
+ else
+ res = crypto_wait_req(crypto_skcipher_decrypt(req), &wait);
+
+out:
+ skcipher_request_free(req);
+ crypto_free_skcipher(tfm);
+
+ return res;
+}
+
+static int do_aead_crypto(u8 *in, u8 *out, size_t len, u8 *key, u8 *nonce,
+ bool do_encrypt)
+{
+ struct aead_request *aead_req = NULL;
+ struct scatterlist src_sg, dst_sg;
+ struct crypto_aead *aead;
+ int ret;
+
+ aead = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(aead)) {
+ ret = PTR_ERR(aead);
+ goto out;
+ }
+
+ ret = crypto_aead_setauthsize(aead, DCP_BLOB_AUTHLEN);
+ if (ret < 0) {
+ pr_err("Can't set crypto auth tag len: %d\n", ret);
+ goto free_aead;
+ }
+
+ aead_req = aead_request_alloc(aead, GFP_KERNEL);
+ if (!aead_req) {
+ ret = -ENOMEM;
+ goto free_aead;
+ }
+
+ sg_init_one(&src_sg, in, len);
+ if (do_encrypt) {
+ /*
+ * If we encrypt our buffer has extra space for the auth tag.
+ */
+ sg_init_one(&dst_sg, out, len + DCP_BLOB_AUTHLEN);
+ } else {
+ sg_init_one(&dst_sg, out, len);
+ }
+
+ aead_request_set_crypt(aead_req, &src_sg, &dst_sg, len, nonce);
+ aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL,
+ NULL);
+ aead_request_set_ad(aead_req, 0);
+
+ if (crypto_aead_setkey(aead, key, AES_KEYSIZE_128)) {
+ pr_err("Can't set crypto AEAD key\n");
+ ret = -EINVAL;
+ goto free_req;
+ }
+
+ if (do_encrypt)
+ ret = crypto_aead_encrypt(aead_req);
+ else
+ ret = crypto_aead_decrypt(aead_req);
+
+free_req:
+ aead_request_free(aead_req);
+free_aead:
+ crypto_free_aead(aead);
+out:
+ return ret;
+}
+
+static int decrypt_blob_key(u8 *key)
+{
+ return do_dcp_crypto(key, key, false);
+}
+
+static int encrypt_blob_key(u8 *key)
+{
+ return do_dcp_crypto(key, key, true);
+}
+
+static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+{
+ struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+ int blen, ret;
+
+ blen = calc_blob_len(p->key_len);
+ if (blen > MAX_BLOB_SIZE)
+ return -E2BIG;
+
+ b->fmt_version = DCP_BLOB_VERSION;
+ get_random_bytes(b->nonce, AES_KEYSIZE_128);
+ get_random_bytes(b->blob_key, AES_KEYSIZE_128);
+
+ ret = do_aead_crypto(p->key, b->payload, p->key_len, b->blob_key,
+ b->nonce, true);
+ if (ret) {
+ pr_err("Unable to encrypt blob payload: %i\n", ret);
+ return ret;
+ }
+
+ ret = encrypt_blob_key(b->blob_key);
+ if (ret) {
+ pr_err("Unable to encrypt blob key: %i\n", ret);
+ return ret;
+ }
+
+ b->payload_len = get_unaligned_le32(&p->key_len);
+ p->blob_len = blen;
+ return 0;
+}
+
+static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+{
+ struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+ int blen, ret;
+
+ if (b->fmt_version != DCP_BLOB_VERSION) {
+ pr_err("DCP blob has bad version: %i, expected %i\n",
+ b->fmt_version, DCP_BLOB_VERSION);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ p->key_len = le32_to_cpu(b->payload_len);
+ blen = calc_blob_len(p->key_len);
+ if (blen != p->blob_len) {
+ pr_err("DCP blob has bad length: %i != %i\n", blen,
+ p->blob_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = decrypt_blob_key(b->blob_key);
+ if (ret) {
+ pr_err("Unable to decrypt blob key: %i\n", ret);
+ goto out;
+ }
+
+ ret = do_aead_crypto(b->payload, p->key, p->key_len + DCP_BLOB_AUTHLEN,
+ b->blob_key, b->nonce, false);
+ if (ret) {
+ pr_err("Unwrap of DCP payload failed: %i\n", ret);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static int test_for_zero_key(void)
+{
+ /*
+ * Encrypting a plaintext of all 0x55 bytes will yield
+ * this ciphertext in case the DCP test key is used.
+ */
+ static const u8 bad[] = {0x9a, 0xda, 0xe0, 0x54, 0xf6, 0x3d, 0xfa, 0xff,
+ 0x5e, 0xa1, 0x8e, 0x45, 0xed, 0xf6, 0xea, 0x6f};
+ void *buf = NULL;
+ int ret = 0;
+
+ if (skip_zk_test)
+ goto out;
+
+ buf = kmalloc(AES_BLOCK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(buf, 0x55, AES_BLOCK_SIZE);
+
+ ret = do_dcp_crypto(buf, buf, true);
+ if (ret)
+ goto out;
+
+ if (memcmp(buf, bad, AES_BLOCK_SIZE) == 0) {
+ pr_warn("Device neither in secure nor trusted mode!\n");
+ ret = -EINVAL;
+ }
+out:
+ kfree(buf);
+ return ret;
+}
+
+static int trusted_dcp_init(void)
+{
+ int ret;
+
+ if (use_otp_key)
+ pr_info("Using DCP OTP key\n");
+
+ ret = test_for_zero_key();
+ if (ret) {
+ pr_warn("Test for zero'ed keys failed: %i\n", ret);
+
+ return -EINVAL;
+ }
+
+ return register_key_type(&key_type_trusted);
+}
+
+static void trusted_dcp_exit(void)
+{
+ unregister_key_type(&key_type_trusted);
+}
+
+struct trusted_key_ops dcp_trusted_key_ops = {
+ .exit = trusted_dcp_exit,
+ .init = trusted_dcp_init,
+ .seal = trusted_dcp_seal,
+ .unseal = trusted_dcp_unseal,
+ .migratable = 0,
+};
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 1/6] crypto: mxs-dcp: Add support for hardware-bound keys
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module, Richard Weinberger, David Oberhollenzer
In-Reply-To: <20240327082454.13729-1-david@sigma-star.at>
DCP (Data Co-Processor) is able to derive private keys for a fused
random seed, which can be referenced by handle but not accessed by
the CPU. Similarly, DCP is able to store arbitrary keys in four
dedicated key slots located in its secure memory area (internal SRAM).
These keys can be used to perform AES encryption.
Expose these derived keys and key slots through the crypto API via their
handle. The main purpose is to add DCP-backed trusted keys. Other
use cases are possible too (see similar existing paes implementations),
but these should carefully be evaluated as e.g. enabling AF_ALG will
give userspace full access to use keys. In scenarios with untrustworthy
userspace, this will enable en-/decryption oracles.
Co-developed-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Richard Weinberger <richard@nod.at>
Co-developed-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Gstir <david@sigma-star.at>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/crypto/mxs-dcp.c | 104 ++++++++++++++++++++++++++++++++++-----
include/soc/fsl/dcp.h | 20 ++++++++
2 files changed, 113 insertions(+), 11 deletions(-)
create mode 100644 include/soc/fsl/dcp.h
diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c
index 2b3ebe0db3a6..057d73c370b7 100644
--- a/drivers/crypto/mxs-dcp.c
+++ b/drivers/crypto/mxs-dcp.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/stmp_device.h>
#include <linux/clk.h>
+#include <soc/fsl/dcp.h>
#include <crypto/aes.h>
#include <crypto/sha1.h>
@@ -101,6 +102,7 @@ struct dcp_async_ctx {
struct crypto_skcipher *fallback;
unsigned int key_len;
uint8_t key[AES_KEYSIZE_128];
+ bool key_referenced;
};
struct dcp_aes_req_ctx {
@@ -155,6 +157,7 @@ static struct dcp *global_sdcp;
#define MXS_DCP_CONTROL0_HASH_TERM (1 << 13)
#define MXS_DCP_CONTROL0_HASH_INIT (1 << 12)
#define MXS_DCP_CONTROL0_PAYLOAD_KEY (1 << 11)
+#define MXS_DCP_CONTROL0_OTP_KEY (1 << 10)
#define MXS_DCP_CONTROL0_CIPHER_ENCRYPT (1 << 8)
#define MXS_DCP_CONTROL0_CIPHER_INIT (1 << 9)
#define MXS_DCP_CONTROL0_ENABLE_HASH (1 << 6)
@@ -168,6 +171,8 @@ static struct dcp *global_sdcp;
#define MXS_DCP_CONTROL1_CIPHER_MODE_ECB (0 << 4)
#define MXS_DCP_CONTROL1_CIPHER_SELECT_AES128 (0 << 0)
+#define MXS_DCP_CONTROL1_KEY_SELECT_SHIFT 8
+
static int mxs_dcp_start_dma(struct dcp_async_ctx *actx)
{
int dma_err;
@@ -224,13 +229,16 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
struct dcp *sdcp = global_sdcp;
struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan];
struct dcp_aes_req_ctx *rctx = skcipher_request_ctx(req);
+ bool key_referenced = actx->key_referenced;
int ret;
- key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
- 2 * AES_KEYSIZE_128, DMA_TO_DEVICE);
- ret = dma_mapping_error(sdcp->dev, key_phys);
- if (ret)
- return ret;
+ if (!key_referenced) {
+ key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
+ 2 * AES_KEYSIZE_128, DMA_TO_DEVICE);
+ ret = dma_mapping_error(sdcp->dev, key_phys);
+ if (ret)
+ return ret;
+ }
src_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_in_buf,
DCP_BUF_SZ, DMA_TO_DEVICE);
@@ -255,8 +263,12 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
MXS_DCP_CONTROL0_INTERRUPT |
MXS_DCP_CONTROL0_ENABLE_CIPHER;
- /* Payload contains the key. */
- desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
+ if (key_referenced)
+ /* Set OTP key bit to select the key via KEY_SELECT. */
+ desc->control0 |= MXS_DCP_CONTROL0_OTP_KEY;
+ else
+ /* Payload contains the key. */
+ desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
if (rctx->enc)
desc->control0 |= MXS_DCP_CONTROL0_CIPHER_ENCRYPT;
@@ -270,6 +282,9 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
else
desc->control1 |= MXS_DCP_CONTROL1_CIPHER_MODE_CBC;
+ if (key_referenced)
+ desc->control1 |= sdcp->coh->aes_key[0] << MXS_DCP_CONTROL1_KEY_SELECT_SHIFT;
+
desc->next_cmd_addr = 0;
desc->source = src_phys;
desc->destination = dst_phys;
@@ -284,9 +299,9 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
err_dst:
dma_unmap_single(sdcp->dev, src_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
err_src:
- dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
- DMA_TO_DEVICE);
-
+ if (!key_referenced)
+ dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
return ret;
}
@@ -453,7 +468,7 @@ static int mxs_dcp_aes_enqueue(struct skcipher_request *req, int enc, int ecb)
struct dcp_aes_req_ctx *rctx = skcipher_request_ctx(req);
int ret;
- if (unlikely(actx->key_len != AES_KEYSIZE_128))
+ if (unlikely(actx->key_len != AES_KEYSIZE_128 && !actx->key_referenced))
return mxs_dcp_block_fallback(req, enc);
rctx->enc = enc;
@@ -500,6 +515,7 @@ static int mxs_dcp_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,
* there can still be an operation in progress.
*/
actx->key_len = len;
+ actx->key_referenced = false;
if (len == AES_KEYSIZE_128) {
memcpy(actx->key, key, len);
return 0;
@@ -516,6 +532,32 @@ static int mxs_dcp_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,
return crypto_skcipher_setkey(actx->fallback, key, len);
}
+static int mxs_dcp_aes_setrefkey(struct crypto_skcipher *tfm, const u8 *key,
+ unsigned int len)
+{
+ struct dcp_async_ctx *actx = crypto_skcipher_ctx(tfm);
+
+ if (len != DCP_PAES_KEYSIZE)
+ return -EINVAL;
+
+ switch (key[0]) {
+ case DCP_PAES_KEY_SLOT0:
+ case DCP_PAES_KEY_SLOT1:
+ case DCP_PAES_KEY_SLOT2:
+ case DCP_PAES_KEY_SLOT3:
+ case DCP_PAES_KEY_UNIQUE:
+ case DCP_PAES_KEY_OTP:
+ memcpy(actx->key, key, len);
+ actx->key_len = len;
+ actx->key_referenced = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int mxs_dcp_aes_fallback_init_tfm(struct crypto_skcipher *tfm)
{
const char *name = crypto_tfm_alg_name(crypto_skcipher_tfm(tfm));
@@ -539,6 +581,13 @@ static void mxs_dcp_aes_fallback_exit_tfm(struct crypto_skcipher *tfm)
crypto_free_skcipher(actx->fallback);
}
+static int mxs_dcp_paes_init_tfm(struct crypto_skcipher *tfm)
+{
+ crypto_skcipher_set_reqsize(tfm, sizeof(struct dcp_aes_req_ctx));
+
+ return 0;
+}
+
/*
* Hashing (SHA1/SHA256)
*/
@@ -889,6 +938,39 @@ static struct skcipher_alg dcp_aes_algs[] = {
.ivsize = AES_BLOCK_SIZE,
.init = mxs_dcp_aes_fallback_init_tfm,
.exit = mxs_dcp_aes_fallback_exit_tfm,
+ }, {
+ .base.cra_name = "ecb(paes)",
+ .base.cra_driver_name = "ecb-paes-dcp",
+ .base.cra_priority = 401,
+ .base.cra_alignmask = 15,
+ .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_INTERNAL,
+ .base.cra_blocksize = AES_BLOCK_SIZE,
+ .base.cra_ctxsize = sizeof(struct dcp_async_ctx),
+ .base.cra_module = THIS_MODULE,
+
+ .min_keysize = DCP_PAES_KEYSIZE,
+ .max_keysize = DCP_PAES_KEYSIZE,
+ .setkey = mxs_dcp_aes_setrefkey,
+ .encrypt = mxs_dcp_aes_ecb_encrypt,
+ .decrypt = mxs_dcp_aes_ecb_decrypt,
+ .init = mxs_dcp_paes_init_tfm,
+ }, {
+ .base.cra_name = "cbc(paes)",
+ .base.cra_driver_name = "cbc-paes-dcp",
+ .base.cra_priority = 401,
+ .base.cra_alignmask = 15,
+ .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_INTERNAL,
+ .base.cra_blocksize = AES_BLOCK_SIZE,
+ .base.cra_ctxsize = sizeof(struct dcp_async_ctx),
+ .base.cra_module = THIS_MODULE,
+
+ .min_keysize = DCP_PAES_KEYSIZE,
+ .max_keysize = DCP_PAES_KEYSIZE,
+ .setkey = mxs_dcp_aes_setrefkey,
+ .encrypt = mxs_dcp_aes_cbc_encrypt,
+ .decrypt = mxs_dcp_aes_cbc_decrypt,
+ .ivsize = AES_BLOCK_SIZE,
+ .init = mxs_dcp_paes_init_tfm,
},
};
diff --git a/include/soc/fsl/dcp.h b/include/soc/fsl/dcp.h
new file mode 100644
index 000000000000..3ec335d8ca8b
--- /dev/null
+++ b/include/soc/fsl/dcp.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 sigma star gmbh
+ *
+ * Specifies paes key slot handles for NXP's DCP (Data Co-Processor) to be used
+ * with the crypto_skcipher_setkey().
+ */
+
+#ifndef MXS_DCP_H
+#define MXS_DCP_H
+
+#define DCP_PAES_KEYSIZE 1
+#define DCP_PAES_KEY_SLOT0 0x00
+#define DCP_PAES_KEY_SLOT1 0x01
+#define DCP_PAES_KEY_SLOT2 0x02
+#define DCP_PAES_KEY_SLOT3 0x03
+#define DCP_PAES_KEY_UNIQUE 0xfe
+#define DCP_PAES_KEY_OTP 0xff
+
+#endif /* MXS_DCP_H */
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 2/6] KEYS: trusted: improve scalability of trust source config
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module
In-Reply-To: <20240327082454.13729-1-david@sigma-star.at>
Enabling trusted keys requires at least one trust source implementation
(currently TPM, TEE or CAAM) to be enabled. Currently, this is
done by checking each trust source's config option individually.
This does not scale when more trust sources like the one for DCP
are added, because the condition will get long and hard to read.
Add config HAVE_TRUSTED_KEYS which is set to true by each trust source
once its enabled and adapt the check for having at least one active trust
source to use this option. Whenever a new trust source is added, it now
needs to select HAVE_TRUSTED_KEYS.
Signed-off-by: David Gstir <david@sigma-star.at>
---
security/keys/trusted-keys/Kconfig | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
index dbfdd8536468..553dc117f385 100644
--- a/security/keys/trusted-keys/Kconfig
+++ b/security/keys/trusted-keys/Kconfig
@@ -1,3 +1,6 @@
+config HAVE_TRUSTED_KEYS
+ bool
+
config TRUSTED_KEYS_TPM
bool "TPM-based trusted keys"
depends on TCG_TPM >= TRUSTED_KEYS
@@ -9,6 +12,7 @@ config TRUSTED_KEYS_TPM
select ASN1_ENCODER
select OID_REGISTRY
select ASN1
+ select HAVE_TRUSTED_KEYS
help
Enable use of the Trusted Platform Module (TPM) as trusted key
backend. Trusted keys are random number symmetric keys,
@@ -20,6 +24,7 @@ config TRUSTED_KEYS_TEE
bool "TEE-based trusted keys"
depends on TEE >= TRUSTED_KEYS
default y
+ select HAVE_TRUSTED_KEYS
help
Enable use of the Trusted Execution Environment (TEE) as trusted
key backend.
@@ -29,10 +34,11 @@ config TRUSTED_KEYS_CAAM
depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS
select CRYPTO_DEV_FSL_CAAM_BLOB_GEN
default y
+ select HAVE_TRUSTED_KEYS
help
Enable use of NXP's Cryptographic Accelerator and Assurance Module
(CAAM) as trusted key backend.
-if !TRUSTED_KEYS_TPM && !TRUSTED_KEYS_TEE && !TRUSTED_KEYS_CAAM
-comment "No trust source selected!"
+if !HAVE_TRUSTED_KEYS
+ comment "No trust source selected!"
endif
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 6/6] docs: trusted-encrypted: add DCP as new trust source
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module, Richard Weinberger, David Oberhollenzer
In-Reply-To: <20240327082454.13729-1-david@sigma-star.at>
Update the documentation for trusted and encrypted KEYS with DCP as new
trust source:
- Describe security properties of DCP trust source
- Describe key usage
- Document blob format
Co-developed-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Richard Weinberger <richard@nod.at>
Co-developed-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Gstir <david@sigma-star.at>
---
.../security/keys/trusted-encrypted.rst | 85 +++++++++++++++++++
1 file changed, 85 insertions(+)
diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index e989b9802f92..81fb3540bb20 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -42,6 +42,14 @@ safe.
randomly generated and fused into each SoC at manufacturing time.
Otherwise, a common fixed test key is used instead.
+ (4) DCP (Data Co-Processor: crypto accelerator of various i.MX SoCs)
+
+ Rooted to a one-time programmable key (OTP) that is generally burnt
+ in the on-chip fuses and is accessible to the DCP encryption engine only.
+ DCP provides two keys that can be used as root of trust: the OTP key
+ and the UNIQUE key. Default is to use the UNIQUE key, but selecting
+ the OTP key can be done via a module parameter (dcp_use_otp_key).
+
* Execution isolation
(1) TPM
@@ -57,6 +65,12 @@ safe.
Fixed set of operations running in isolated execution environment.
+ (4) DCP
+
+ Fixed set of cryptographic operations running in isolated execution
+ environment. Only basic blob key encryption is executed there.
+ The actual key sealing/unsealing is done on main processor/kernel space.
+
* Optional binding to platform integrity state
(1) TPM
@@ -79,6 +93,11 @@ safe.
Relies on the High Assurance Boot (HAB) mechanism of NXP SoCs
for platform integrity.
+ (4) DCP
+
+ Relies on Secure/Trusted boot process (called HAB by vendor) for
+ platform integrity.
+
* Interfaces and APIs
(1) TPM
@@ -94,6 +113,11 @@ safe.
Interface is specific to silicon vendor.
+ (4) DCP
+
+ Vendor-specific API that is implemented as part of the DCP crypto driver in
+ ``drivers/crypto/mxs-dcp.c``.
+
* Threat model
The strength and appropriateness of a particular trust source for a given
@@ -129,6 +153,13 @@ selected trust source:
CAAM HWRNG, enable CRYPTO_DEV_FSL_CAAM_RNG_API and ensure the device
is probed.
+ * DCP (Data Co-Processor: crypto accelerator of various i.MX SoCs)
+
+ The DCP hardware device itself does not provide a dedicated RNG interface,
+ so the kernel default RNG is used. SoCs with DCP like the i.MX6ULL do have
+ a dedicated hardware RNG that is independent from DCP which can be enabled
+ to back the kernel RNG.
+
Users may override this by specifying ``trusted.rng=kernel`` on the kernel
command-line to override the used RNG with the kernel's random number pool.
@@ -231,6 +262,19 @@ Usage::
CAAM-specific format. The key length for new keys is always in bytes.
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
+Trusted Keys usage: DCP
+-----------------------
+
+Usage::
+
+ keyctl add trusted name "new keylen" ring
+ keyctl add trusted name "load hex_blob" ring
+ keyctl print keyid
+
+"keyctl print" returns an ASCII hex copy of the sealed key, which is in format
+specific to this DCP key-blob implementation. The key length for new keys is
+always in bytes. Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
+
Encrypted Keys usage
--------------------
@@ -426,3 +470,44 @@ string length.
privkey is the binary representation of TPM2B_PUBLIC excluding the
initial TPM2B header which can be reconstructed from the ASN.1 octed
string length.
+
+DCP Blob Format
+---------------
+
+The Data Co-Processor (DCP) provides hardware-bound AES keys using its
+AES encryption engine only. It does not provide direct key sealing/unsealing.
+To make DCP hardware encryption keys usable as trust source, we define
+our own custom format that uses a hardware-bound key to secure the sealing
+key stored in the key blob.
+
+Whenever a new trusted key using DCP is generated, we generate a random 128-bit
+blob encryption key (BEK) and 128-bit nonce. The BEK and nonce are used to
+encrypt the trusted key payload using AES-128-GCM.
+
+The BEK itself is encrypted using the hardware-bound key using the DCP's AES
+encryption engine with AES-128-ECB. The encrypted BEK, generated nonce,
+BEK-encrypted payload and authentication tag make up the blob format together
+with a version number, payload length and authentication tag::
+
+ /*
+ * struct dcp_blob_fmt - DCP BLOB format.
+ *
+ * @fmt_version: Format version, currently being %1
+ * @blob_key: Random AES 128 key which is used to encrypt @payload,
+ * @blob_key itself is encrypted with OTP or UNIQUE device key in
+ * AES-128-ECB mode by DCP.
+ * @nonce: Random nonce used for @payload encryption.
+ * @payload_len: Length of the plain text @payload.
+ * @payload: The payload itself, encrypted using AES-128-GCM and @blob_key,
+ * GCM auth tag of size AES_BLOCK_SIZE is attached at the end of it.
+ *
+ * The total size of a DCP BLOB is sizeof(struct dcp_blob_fmt) + @payload_len +
+ * AES_BLOCK_SIZE.
+ */
+ struct dcp_blob_fmt {
+ __u8 fmt_version;
+ __u8 blob_key[AES_KEYSIZE_128];
+ __u8 nonce[AES_KEYSIZE_128];
+ __le32 payload_len;
+ __u8 payload[];
+ } __packed;
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 4/6] MAINTAINERS: add entry for DCP-based trusted keys
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module
In-Reply-To: <20240327082454.13729-1-david@sigma-star.at>
This covers trusted keys backed by NXP's DCP (Data Co-Processor) chip
found in smaller i.MX SoCs.
Signed-off-by: David Gstir <david@sigma-star.at>
Acked-by: Jarkko Sakkinen <jarkko@kernel.org>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 976a5cea1577..ca7f42ca9338 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12019,6 +12019,15 @@ S: Maintained
F: include/keys/trusted_caam.h
F: security/keys/trusted-keys/trusted_caam.c
+KEYS-TRUSTED-DCP
+M: David Gstir <david@sigma-star.at>
+R: sigma star Kernel Team <upstream+dcp@sigma-star.at>
+L: linux-integrity@vger.kernel.org
+L: keyrings@vger.kernel.org
+S: Supported
+F: include/keys/trusted_dcp.h
+F: security/keys/trusted-keys/trusted_dcp.c
+
KEYS-TRUSTED-TEE
M: Sumit Garg <sumit.garg@linaro.org>
L: linux-integrity@vger.kernel.org
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 5/6] docs: document DCP-backed trusted keys kernel params
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module, Richard Weinberger, David Oberhollenzer
In-Reply-To: <20240327082454.13729-1-david@sigma-star.at>
Document the kernel parameters trusted.dcp_use_otp_key
and trusted.dcp_skip_zk_test for DCP-backed trusted keys.
Co-developed-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Richard Weinberger <richard@nod.at>
Co-developed-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: David Gstir <david@sigma-star.at>
---
Documentation/admin-guide/kernel-parameters.txt | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 24c02c704049..b6944e57768a 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -6698,6 +6698,7 @@
- "tpm"
- "tee"
- "caam"
+ - "dcp"
If not specified then it defaults to iterating through
the trust source list starting with TPM and assigns the
first trust source as a backend which is initialized
@@ -6713,6 +6714,18 @@
If not specified, "default" is used. In this case,
the RNG's choice is left to each individual trust source.
+ trusted.dcp_use_otp_key
+ This is intended to be used in combination with
+ trusted.source=dcp and will select the DCP OTP key
+ instead of the DCP UNIQUE key blob encryption.
+
+ trusted.dcp_skip_zk_test
+ This is intended to be used in combination with
+ trusted.source=dcp and will disable the check if all
+ the blob key is zero'ed. This is helpful for situations where
+ having this key zero'ed is acceptable. E.g. in testing
+ scenarios.
+
tsc= Disable clocksource stability checks for TSC.
Format: <string>
[x86] reliable: mark tsc clocksource as reliable, this
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 0/6] DCP as trusted keys backend
From: David Gstir @ 2024-03-27 8:24 UTC (permalink / raw)
To: Mimi Zohar, James Bottomley, Jarkko Sakkinen, Herbert Xu,
David S. Miller
Cc: David Gstir, Shawn Guo, Jonathan Corbet, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Ahmad Fatoum, sigma star Kernel Team, David Howells, Li Yang,
Paul Moore, James Morris, Serge E. Hallyn, Paul E. McKenney,
Randy Dunlap, Catalin Marinas, Rafael J. Wysocki, Tejun Heo,
Steven Rostedt (Google), linux-doc, linux-kernel, linux-integrity,
keyrings, linux-crypto, linux-arm-kernel, linuxppc-dev,
linux-security-module
This is a revival of the previous patch set submitted by Richard Weinberger:
https://lore.kernel.org/linux-integrity/20210614201620.30451-1-richard@nod.at/
After having been thoroughly reviewed by Jarkko, it would be great if this
could go into 6.10. :-)
v6 is here (please ignore the incorrect version number in the cover letter):
https://lore.kernel.org/keyrings/20240307153842.80033-1-david@sigma-star.at/
v6 -> v7:
- Add Reviewed-by from Jarkko Sakkinen for patches #1 and #3
- Improved commit messages
- Changed log level for non-trusted/secure mode check from error to warning
v5 -> v6:
- Cleaned up coding style and commit messages to make the whole series more
coherent as suggested by Jarkko Sakkinen
- Added Acked-By from Jarkko Sakkinen to patch #4 - thanks!
- Rebased against next-20240307
v4 -> v5:
- Make Kconfig for trust source check scalable as suggested by Jarkko Sakkinen
- Add Acked-By from Herbert Xu to patch #1 - thanks!
v3 -> v4:
- Split changes on MAINTAINERS and documentation into dedicated patches
- Use more concise wording in commit messages as suggested by Jarkko Sakkinen
v2 -> v3:
- Addressed review comments from Jarkko Sakkinen
v1 -> v2:
- Revive and rebase to latest version
- Include review comments from Ahmad Fatoum
The Data Co-Processor (DCP) is an IP core built into many NXP SoCs such
as i.mx6ull.
Similar to the CAAM engine used in more powerful SoCs, DCP can AES-
encrypt/decrypt user data using a unique, never-disclosed,
device-specific key. Unlike CAAM though, it cannot directly wrap and
unwrap blobs in hardware. As DCP offers only the bare minimum feature
set and a blob mechanism needs aid from software. A blob in this case
is a piece of sensitive data (e.g. a key) that is encrypted and
authenticated using the device-specific key so that unwrapping can only
be done on the hardware where the blob was wrapped.
This patch series adds a DCP based, trusted-key backend and is similar
in spirit to the one by Ahmad Fatoum [0] that does the same for CAAM.
It is of interest for similar use cases as the CAAM patch set, but for
lower end devices, where CAAM is not available.
Because constructing and parsing the blob has to happen in software,
we needed to decide on a blob format and chose the following:
struct dcp_blob_fmt {
__u8 fmt_version;
__u8 blob_key[AES_KEYSIZE_128];
__u8 nonce[AES_KEYSIZE_128];
__le32 payload_len;
__u8 payload[];
} __packed;
The `fmt_version` is currently 1.
The encrypted key is stored in the payload area. It is AES-128-GCM
encrypted using `blob_key` and `nonce`, GCM auth tag is attached at
the end of the payload (`payload_len` does not include the size of
the auth tag).
The `blob_key` itself is encrypted in AES-128-ECB mode by DCP using
the OTP or UNIQUE device key. A new `blob_key` and `nonce` are generated
randomly, when sealing/exporting the DCP blob.
This patchset was tested with dm-crypt on an i.MX6ULL board.
[0] https://lore.kernel.org/keyrings/20220513145705.2080323-1-a.fatoum@pengutronix.de/
David Gstir (6):
crypto: mxs-dcp: Add support for hardware-bound keys
KEYS: trusted: improve scalability of trust source config
KEYS: trusted: Introduce NXP DCP-backed trusted keys
MAINTAINERS: add entry for DCP-based trusted keys
docs: document DCP-backed trusted keys kernel params
docs: trusted-encrypted: add DCP as new trust source
.../admin-guide/kernel-parameters.txt | 13 +
.../security/keys/trusted-encrypted.rst | 85 +++++
MAINTAINERS | 9 +
drivers/crypto/mxs-dcp.c | 104 +++++-
include/keys/trusted_dcp.h | 11 +
include/soc/fsl/dcp.h | 20 ++
security/keys/trusted-keys/Kconfig | 18 +-
security/keys/trusted-keys/Makefile | 2 +
security/keys/trusted-keys/trusted_core.c | 6 +-
security/keys/trusted-keys/trusted_dcp.c | 313 ++++++++++++++++++
10 files changed, 567 insertions(+), 14 deletions(-)
create mode 100644 include/keys/trusted_dcp.h
create mode 100644 include/soc/fsl/dcp.h
create mode 100644 security/keys/trusted-keys/trusted_dcp.c
--
2.35.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v11 8/8] PCI: endpoint: Remove "core_init_notifier" flag
From: Niklas Cassel @ 2024-03-27 8:24 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Jingoo Han, Gustavo Pimentel, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Marek Vasut, Yoshihiro Shimoda, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Vidya Sagar, Vignesh Raghavendra,
Richard Zhu, Lucas Stach, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Minghuan Lian, Mingkai Hu, Roy Zang, Kunihiko Hayashi,
Masami Hiramatsu, Kishon Vijay Abraham I, Jesper Nilsson,
Srikanth Thokala, Shawn Lin, Heiko Stuebner, linux-pci,
linux-kernel, linux-renesas-soc, linux-arm-msm, linux-tegra,
linux-omap, linux-arm-kernel, linuxppc-dev, linux-arm-kernel,
linux-rockchip
In-Reply-To: <20240327-pci-dbi-rework-v11-8-6f5259f90673@linaro.org>
Hello Mani,
On Wed, Mar 27, 2024 at 12:05:54PM +0530, Manivannan Sadhasivam wrote:
> "core_init_notifier" flag is set by the glue drivers requiring refclk from
> the host to complete the DWC core initialization. Also, those drivers will
> send a notification to the EPF drivers once the initialization is fully
> completed using the pci_epc_init_notify() API. Only then, the EPF drivers
> will start functioning.
>
> For the rest of the drivers generating refclk locally, EPF drivers will
> start functioning post binding with them. EPF drivers rely on the
> 'core_init_notifier' flag to differentiate between the drivers.
> Unfortunately, this creates two different flows for the EPF drivers.
>
> So to avoid that, let's get rid of the "core_init_notifier" flag and follow
> a single initialization flow for the EPF drivers. This is done by calling
> the dw_pcie_ep_init_notify() from all glue drivers after the completion of
> dw_pcie_ep_init_registers() API. This will allow all the glue drivers to
> send the notification to the EPF drivers once the initialization is fully
> completed.
>
> Only difference here is that, the drivers requiring refclk from host will
> send the notification once refclk is received, while others will send it
> during probe time itself.
>
> But this also requires the EPC core driver to deliver the notification
> after EPF driver bind. Because, the glue driver can send the notification
> before the EPF drivers bind() and in those cases the EPF drivers will miss
> the event. To accommodate this, EPC core is now caching the state of the
> EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the
> notification to EPF drivers based on that after each EPF driver bind.
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
> drivers/pci/controller/cadence/pcie-cadence-ep.c | 2 ++
> drivers/pci/controller/dwc/pci-dra7xx.c | 2 ++
> drivers/pci/controller/dwc/pci-imx6.c | 2 ++
> drivers/pci/controller/dwc/pci-keystone.c | 2 ++
> drivers/pci/controller/dwc/pci-layerscape-ep.c | 2 ++
> drivers/pci/controller/dwc/pcie-artpec6.c | 2 ++
> drivers/pci/controller/dwc/pcie-designware-ep.c | 1 +
> drivers/pci/controller/dwc/pcie-designware-plat.c | 2 ++
> drivers/pci/controller/dwc/pcie-keembay.c | 2 ++
> drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 -
> drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 ++
> drivers/pci/controller/dwc/pcie-tegra194.c | 1 -
> drivers/pci/controller/dwc/pcie-uniphier-ep.c | 2 ++
> drivers/pci/controller/pcie-rcar-ep.c | 2 ++
> drivers/pci/controller/pcie-rockchip-ep.c | 2 ++
> drivers/pci/endpoint/functions/pci-epf-test.c | 18 +++++-------------
> drivers/pci/endpoint/pci-ep-cfs.c | 9 +++++++++
> drivers/pci/endpoint/pci-epc-core.c | 22 ++++++++++++++++++++++
> include/linux/pci-epc.h | 7 ++++---
> 19 files changed, 65 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c
> index 2d0a8d78bffb..da67a06ee790 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c
> +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c
> @@ -734,6 +734,8 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
>
> spin_lock_init(&ep->lock);
>
> + dw_pcie_ep_init_notify(&pci->ep);
This looks wrong (and I think that you have not build tested this).
dw_* prefix indicates DWC, so it is a DWC specific function.
I don't think that you can use this function for the 3 non-DWC EPC drivers.
I think that you need to use call pci_epc_init_notify() directly.
(Also perhaps rebase your series on v6.9-rc1, I got conflicts when trying
to apply it to v6.9-rc1, because it looks like the series is still based
on v6.8-rc1.)
Kind regards,
Niklas
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 1/3] clk: samsung: introduce nMUX for MUX clks that can reparented
From: Tudor Ambarus @ 2024-03-27 8:09 UTC (permalink / raw)
To: André Draszik, peter.griffin, krzysztof.kozlowski
Cc: alim.akhtar, linux-arm-kernel, linux-samsung-soc, linux-kernel,
willmcvicker, kernel-team, s.nawrocki, cw00.choi, mturquette,
sboyd, semen.protsenko, linux-clk, jaewon02.kim
In-Reply-To: <c20452059e62d3b8c45efb8070223f10f0bd06ed.camel@linaro.org>
On 3/26/24 17:43, André Draszik wrote:
> Hi Tudor,
>
Hi, Andre'!
> On Tue, 2024-03-26 at 17:28 +0000, Tudor Ambarus wrote:
>> All samsung MUX clocks that are defined with MUX() set the
>> CLK_SET_RATE_NO_REPARENT flag in __MUX(), which prevents MUXes to be
>> reparented during clk_set_rate().
>>
>> Introduce nMUX() for MUX clocks that can be reparented.
>
> What does n in nMUX stand for?
I thought about using the common terminology, "n-to-1 multiplexer",
where n is the number of select lines. I'm open to other suggestions if
there are any. I should have specified the naming scheme in the commit
message, will do in the next version.
>
>> [...]
>>
>> +/* Used by MUX clocks where reparenting is allowed. */
>> +#define __nMUX(_id, cname, pnames, o, s, w, f, mf) \
>> + { \
>> + .id = _id, \
>> + .name = cname, \
>> + .parent_names = pnames, \
>> + .num_parents = ARRAY_SIZE(pnames), \
>> + .flags = f, \
>> + .offset = o, \
>> + .shift = s, \
>> + .width = w, \
>> + .mux_flags = mf, \
>> + }
>
> You've duplicated __MUX() and removed the CLK_SET_RATE_NO_REPARENT
> from flags - I think it would make sense to instead drop the flag
> from the existing __MUX(), and adjust the only two existing users
> of the macro, i.e. to add it in MUX() and MUX_F().
>
Yes, I find the suggestion good. Will do in v3.
Thanks,
ta
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 4/4] dt-bindings: arm: keystone: Remove ti,system-reboot-controller property
From: Krzysztof Kozlowski @ 2024-03-27 8:08 UTC (permalink / raw)
To: Andrew Davis, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Santosh Shilimkar, Nishanth Menon, Vignesh Raghavendra
Cc: devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20240326223730.54639-5-afd@ti.com>
On 26/03/2024 23:37, Andrew Davis wrote:
> This property was only ever used in one device. It is no longer needed as
> what it signaled is now default. Remove this unneeded/unused property.
>
> Signed-off-by: Andrew Davis <afd@ti.com>
> ---
> Documentation/devicetree/bindings/arm/keystone/ti,sci.yaml | 5 -----
> 1 file changed, 5 deletions(-)
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] ASoC: sun8i-codec: Fix build with CONFIG_SND_JACK_INPUT_DEV disabled
From: Ondřej Jirman @ 2024-03-27 8:05 UTC (permalink / raw)
To: linux-kernel
Cc: Ondrej Jirman, kernel test robot, Liam Girdwood, Mark Brown,
Jaroslav Kysela, Takashi Iwai, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, AngeloGioacchino Del Regno,
Claudiu Beznea, Arnaud Ferraris,
open list:SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEM...,
moderated list:ARM/Allwinner sunXi SoC support,
open list:ARM/Allwinner sunXi SoC support
From: Ondrej Jirman <megi@xff.cz>
When CONFIG_SND_JACK_INPUT_DEV is disabled, struct snd_jack doesn't
have 'type' field. We can't rely on this field being always present,
so store the jack_type in the codec driver itself.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202403261243.kcXCPAYs-lkp@intel.com/
---
This is a followup for https://lore.kernel.org/lkml/2vhd45kylttgonosdcfn7ugwyy2vx56gijwf3ealz4tel65iqr@cs3kxrl6d2hh/
sound/soc/sunxi/sun8i-codec.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 43795362fed0..b5dafb749c3f 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -232,6 +232,7 @@ struct sun8i_codec {
struct delayed_work jack_work;
int jack_irq;
int jack_status;
+ int jack_type;
int jack_last_sample;
ktime_t jack_hbias_ready;
struct mutex jack_mutex;
@@ -1352,7 +1353,6 @@ static void sun8i_codec_jack_work(struct work_struct *work)
struct sun8i_codec *scodec = container_of(work, struct sun8i_codec,
jack_work.work);
unsigned int mdata;
- int type_mask = scodec->jack->jack->type;
int type;
guard(mutex)(&scodec->jack_mutex);
@@ -1363,7 +1363,7 @@ static void sun8i_codec_jack_work(struct work_struct *work)
scodec->jack_last_sample = -1;
- if (type_mask & SND_JACK_MICROPHONE) {
+ if (scodec->jack_type & SND_JACK_MICROPHONE) {
/*
* If we were in disconnected state, we enable HBIAS and
* wait 600ms before reading initial HDATA value.
@@ -1376,7 +1376,7 @@ static void sun8i_codec_jack_work(struct work_struct *work)
scodec->jack_status = SUN8I_JACK_STATUS_WAITING_HBIAS;
} else {
snd_soc_jack_report(scodec->jack, SND_JACK_HEADPHONE,
- type_mask);
+ scodec->jack_type);
scodec->jack_status = SUN8I_JACK_STATUS_CONNECTED;
}
} else if (scodec->jack_status == SUN8I_JACK_STATUS_WAITING_HBIAS) {
@@ -1417,17 +1417,17 @@ static void sun8i_codec_jack_work(struct work_struct *work)
if (type == SND_JACK_HEADPHONE)
sun8i_codec_set_hmic_bias(scodec, false);
- snd_soc_jack_report(scodec->jack, type, type_mask);
+ snd_soc_jack_report(scodec->jack, type, scodec->jack_type);
scodec->jack_status = SUN8I_JACK_STATUS_CONNECTED;
} else if (scodec->jack_status == SUN8I_JACK_STATUS_CONNECTED) {
if (scodec->last_hmic_irq != SUN8I_HMIC_STS_JACK_OUT_IRQ_ST)
return;
scodec->jack_status = SUN8I_JACK_STATUS_DISCONNECTED;
- if (type_mask & SND_JACK_MICROPHONE)
+ if (scodec->jack_type & SND_JACK_MICROPHONE)
sun8i_codec_set_hmic_bias(scodec, false);
- snd_soc_jack_report(scodec->jack, 0, type_mask);
+ snd_soc_jack_report(scodec->jack, 0, scodec->jack_type);
}
}
@@ -1491,7 +1491,7 @@ static irqreturn_t sun8i_codec_jack_irq(int irq, void *dev_id)
if (scodec->jack_last_sample >= 0 &&
scodec->jack_last_sample == value)
snd_soc_jack_report(scodec->jack, type,
- scodec->jack->jack->type);
+ scodec->jack_type);
scodec->jack_last_sample = value;
}
--
2.44.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v5 1/1] dt-bindings: net: starfive,jh7110-dwmac: Add StarFive JH8100 support
From: Krzysztof Kozlowski @ 2024-03-27 7:54 UTC (permalink / raw)
To: Tan Chun Hau, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Emil Renner Berthing, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin,
Alexandre Torgue, Simon Horman, Bartosz Golaszewski,
Andrew Halaney, Jisheng Zhang, Uwe Kleine-König,
Russell King
Cc: Ley Foon Tan, Jee Heng Sia, netdev, devicetree, linux-kernel,
linux-stm32, linux-arm-kernel, linux-riscv
In-Reply-To: <20240327015750.226349-2-chunhau.tan@starfivetech.com>
On 27/03/2024 02:57, Tan Chun Hau wrote:
> Add StarFive JH8100 dwmac support.
> The JH8100 dwmac shares the same driver code as the JH7110 dwmac
> and has only one reset signal.
>
> Please refer to below:
>
> JH8100: reset-names = "stmmaceth";
> JH7110: reset-names = "stmmaceth", "ahb";
> JH7100: reset-names = "ahb";
>
> Example usage of JH8100 in the device tree:
>
> gmac0: ethernet@16030000 {
> compatible = "starfive,jh8100-dwmac",
> "starfive,jh7110-dwmac",
> "snps,dwmac-5.20";
> ...
> };
>
> Signed-off-by: Tan Chun Hau <chunhau.tan@starfivetech.com>
> ---
> .../devicetree/bindings/net/snps,dwmac.yaml | 1 +
> .../bindings/net/starfive,jh7110-dwmac.yaml | 29 +++++++++++++++----
> 2 files changed, 25 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
> index 6b0341a8e0ea..a6d596b7dcf4 100644
> --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml
> +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
> @@ -97,6 +97,7 @@ properties:
> - snps,dwxgmac-2.10
> - starfive,jh7100-dwmac
> - starfive,jh7110-dwmac
> + - starfive,jh8100-dwmac
I think that's not needed. You have there already your fallback.
>
> reg:
> minItems: 1
> diff --git a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml
> index 0d1962980f57..5805a58c55d1 100644
> --- a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml
> +++ b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml
> @@ -18,6 +18,7 @@ select:
> enum:
> - starfive,jh7100-dwmac
> - starfive,jh7110-dwmac
> + - starfive,jh8100-dwmac
Same here, even more obvious.
> required:
> - compatible
>
> @@ -30,6 +31,10 @@ properties:
> - items:
> - const: starfive,jh7110-dwmac
> - const: snps,dwmac-5.20
> + - items:
> + - const: starfive,jh8100-dwmac
> + - const: starfive,jh7110-dwmac
> + - const: snps,dwmac-5.20
>
> reg:
> maxItems: 1
> @@ -116,11 +121,25 @@ allOf:
> minItems: 3
> maxItems: 3
>
> - resets:
> - minItems: 2
> -
> - reset-names:
> - minItems: 2
> + if:
I would personally avoid nesting if within if. It gets unreadable.
Although Rob did not comment on this one, so I guess it is fine.
Best regards,
Krzysztof
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] ARM: vfp: use asm volatile for FP control register accesses
From: Ard Biesheuvel @ 2024-03-27 7:31 UTC (permalink / raw)
To: Nathan Chancellor; +Cc: linux-arm-kernel, linux, arnd, linus.walleij, stable
In-Reply-To: <CAMj1kXGeMJ2PuYUtzFxv723HX9aovvtE7w4uYJCjPX4cHYC6tw@mail.gmail.com>
On Wed, 27 Mar 2024 at 09:05, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Wed, 27 Mar 2024 at 01:55, Nathan Chancellor <nathan@kernel.org> wrote:
> >
> > On Mon, Mar 18, 2024 at 10:30:05AM +0100, Ard Biesheuvel wrote:
> > > From: Ard Biesheuvel <ardb@kernel.org>
> > >
> > > Clang may reorder FP control register reads and writes, due to the fact
> > > that the inline asm() blocks in the read/write wrappers are not volatile
> > > qualified, and the compiler has no idea that these reads and writes may
> > > have side effects.
> > >
> > > In particular, reads of FPSCR may generate an UNDEF exception if a
> > > floating point exception is pending, and the FP emulation code in
> > > VFP_bounce() explicitly clears FP exceptions temporarily in order to be
> > > able to perform the emulation on behalf of user space. This requires
> > > that the writes to FPEXC are never reordered with respect to accesses to
> > > other FP control registers, such as FPSCR.
> > >
> > > So use asm volatile for both the read and the write helpers.
> > >
> > > Cc: <stable@kernel.org>
> > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> >
> > This seems reasonable to me based on my understanding of GCC's
> > documentation. However, their documentation states "the compiler can
> > move even volatile asm instructions relative to other code, including
> > across jump instructions" and I feel like there was some discussion
> > around this sentence in the past but I can't remember what the
> > conclusion was, although I want to say Clang did not have the same
> > behavior.
>
> The only thing that matters here is whether two asm blocks are emitted
> in a different order than they appear in the program, and I would be
> very surprised if volatile permits that. Otherwise, we might introduce
> a fake input dependency or a memory clobber instead.
>
> > Regardless:
> >
> > Acked-by: Nathan Chancellor <nathan@kernel.org>
> >
>
> Thanks.
>
> > I am just curious, how was this discovered or noticed? Was there a
> > report I missed?
> >
>
> I noticed this when building a recent kernel for the original
> Raspberry Pi, which is ARMv6 not ARMv7, and has a VFP which partially
> relies on emulation. On more recent cores, we never hit the issue
> because emulation is never needed. On older cores, there is no VFP so
> we never reach this code path either.
An alternative approach might be to do the following, which tricks the
compiler into thinking fmxr might update *current, and fmrx might
access it. Given that current == current_thread_info(), which is used
all the time in the VFP code, it will already be available in a
register, and so it doesn't require the compiler to generate any
additional code.
--- a/arch/arm/vfp/vfpinstr.h
+++ b/arch/arm/vfp/vfpinstr.h
@@ -68,14 +68,14 @@
u32 __v; \
asm(".fpu vfpv2\n" \
"vmrs %0, " #_vfp_ \
- : "=r" (__v) : : "cc"); \
+ : "=r" (__v) : "Q" (*current)); \
__v; \
})
#define fmxr(_vfp_,_var_) \
asm(".fpu vfpv2\n" \
- "vmsr " #_vfp_ ", %0" \
- : : "r" (_var_) : "cc")
+ "vmsr " #_vfp_ ", %1" \
+ : "=Q" (*current) : "r" (_var_))
#else
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [RESEND PATCH v4 2/3] perf/marvell: perf/marvell: Odyssey DDR Performance
From: Gowthami Thiagarajan @ 2024-03-27 7:18 UTC (permalink / raw)
To: will, mark.rutland, linux-arm-kernel, linux-kernel
Cc: sgoutham, bbhushan2, gcherian, Gowthami Thiagarajan
In-Reply-To: <20240327071832.1556576-1-gthiagarajan@marvell.com>
Odyssey DRAM Subsystem supports eight counters for monitoring performance
and software can program those counters to monitor any of the defined
performance events. Supported performance events include those counted
at the interface between the DDR controller and the PHY, interface between
the DDR Controller and the CHI interconnect, or within the DDR Controller.
Additionally DSS also supports two fixed performance event counters, one
for ddr reads and the other for ddr writes.
Signed-off-by: Gowthami Thiagarajan <gthiagarajan@marvell.com>
---
drivers/perf/marvell_cn10k_ddr_pmu.c | 246 +++++++++++++++++++++++++++
1 file changed, 246 insertions(+)
diff --git a/drivers/perf/marvell_cn10k_ddr_pmu.c b/drivers/perf/marvell_cn10k_ddr_pmu.c
index d6485c4cf30f..1fd619262d45 100644
--- a/drivers/perf/marvell_cn10k_ddr_pmu.c
+++ b/drivers/perf/marvell_cn10k_ddr_pmu.c
@@ -16,23 +16,28 @@
/* Performance Counters Operating Mode Control Registers */
#define CN10K_DDRC_PERF_CNT_OP_MODE_CTRL 0x8020
+#define ODY_DDRC_PERF_CNT_OP_MODE_CTRL 0x20020
#define OP_MODE_CTRL_VAL_MANUAL 0x1
/* Performance Counters Start Operation Control Registers */
#define CN10K_DDRC_PERF_CNT_START_OP_CTRL 0x8028
+#define ODY_DDRC_PERF_CNT_START_OP_CTRL 0x200A0
#define START_OP_CTRL_VAL_START 0x1ULL
#define START_OP_CTRL_VAL_ACTIVE 0x2
/* Performance Counters End Operation Control Registers */
#define CN10K_DDRC_PERF_CNT_END_OP_CTRL 0x8030
+#define ODY_DDRC_PERF_CNT_END_OP_CTRL 0x200E0
#define END_OP_CTRL_VAL_END 0x1ULL
/* Performance Counters End Status Registers */
#define CN10K_DDRC_PERF_CNT_END_STATUS 0x8038
+#define ODY_DDRC_PERF_CNT_END_STATUS 0x20120
#define END_STATUS_VAL_END_TIMER_MODE_END 0x1
/* Performance Counters Configuration Registers */
#define CN10K_DDRC_PERF_CFG_BASE 0x8040
+#define ODY_DDRC_PERF_CFG_BASE 0x20160
/* 8 Generic event counter + 2 fixed event counters */
#define DDRC_PERF_NUM_GEN_COUNTERS 8
@@ -57,6 +62,13 @@
* program event bitmap in h/w.
*
*/
+#define EVENT_DFI_CMD_IS_RETRY 61
+#define EVENT_RD_UC_ECC_ERROR 60
+#define EVENT_RD_CRC_ERROR 59
+#define EVENT_CAPAR_ERROR 58
+#define EVENT_WR_CRC_ERROR 57
+#define EVENT_DFI_PARITY_POISON 56
+
#define EVENT_OP_IS_ZQLATCH 55
#define EVENT_OP_IS_ZQSTART 54
#define EVENT_OP_IS_TCR_MRR 53
@@ -106,6 +118,7 @@
/* Event counter value registers */
#define CN10K_DDRC_PERF_CNT_VALUE_BASE 0x8080
+#define ODY_DDRC_PERF_CNT_VALUE_BASE 0x201C0
/* Fixed event counter enable/disable register */
#define CN10K_DDRC_PERF_CNT_FREERUN_EN 0x80C0
@@ -114,18 +127,25 @@
/* Fixed event counter control register */
#define CN10K_DDRC_PERF_CNT_FREERUN_CTRL 0x80C8
+#define ODY_DDRC_PERF_CNT_FREERUN_CTRL 0x20240
#define DDRC_FREERUN_WRITE_CNT_CLR 0x1
#define DDRC_FREERUN_READ_CNT_CLR 0x2
+/* Fixed event counter clear register, defined only for Odyssey */
+#define ODY_DDRC_PERF_CNT_FREERUN_CLR 0x20248
+
#define DDRC_PERF_CNT_VALUE_OVERFLOW BIT_ULL(48)
#define DDRC_PERF_CNT_MAX_VALUE GENMASK_ULL(48, 0)
/* Fixed event counter value register */
#define CN10K_DDRC_PERF_CNT_VALUE_WR_OP 0x80D0
#define CN10K_DDRC_PERF_CNT_VALUE_RD_OP 0x80D8
+#define ODY_DDRC_PERF_CNT_VALUE_WR_OP 0x20250
+#define ODY_DDRC_PERF_CNT_VALUE_RD_OP 0x20258
enum mrvl_ddr_pmu_version {
DDR_PMU_V1 = 1,
+ DDR_PMU_V2,
};
struct ddr_pmu_data {
@@ -249,6 +269,85 @@ static struct attribute *cn10k_ddr_perf_events_attrs[] = {
NULL
};
+static struct attribute *odyssey_ddr_perf_events_attrs[] = {
+ /* Programmable */
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_or_wr_access, EVENT_HIF_RD_OR_WR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_wr_access, EVENT_HIF_WR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_access, EVENT_HIF_RD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rmw_access, EVENT_HIF_RMW),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_pri_rdaccess, EVENT_HIF_HI_PRI_RD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_rd_bypass_access, EVENT_READ_BYPASS),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_act_bypass_access, EVENT_ACT_BYPASS),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_wr_data_access,
+ EVENT_DFI_WR_DATA_CYCLES),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_rd_data_access,
+ EVENT_DFI_RD_DATA_CYCLES),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_hpri_sched_rd_crit_access,
+ EVENT_HPR_XACT_WHEN_CRITICAL),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_lpri_sched_rd_crit_access,
+ EVENT_LPR_XACT_WHEN_CRITICAL),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_wr_trxn_crit_access,
+ EVENT_WR_XACT_WHEN_CRITICAL),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_active_access, EVENT_OP_IS_ACTIVATE),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_or_wr_access,
+ EVENT_OP_IS_RD_OR_WR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_active_access,
+ EVENT_OP_IS_RD_ACTIVATE),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_read, EVENT_OP_IS_RD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_write, EVENT_OP_IS_WR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_mwr, EVENT_OP_IS_MWR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge, EVENT_OP_IS_PRECHARGE),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_rdwr,
+ EVENT_PRECHARGE_FOR_RDWR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_other,
+ EVENT_PRECHARGE_FOR_OTHER),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_rdwr_transitions, EVENT_RDWR_TRANSITIONS),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_write_combine, EVENT_WRITE_COMBINE),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_war_hazard, EVENT_WAR_HAZARD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_raw_hazard, EVENT_RAW_HAZARD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_waw_hazard, EVENT_WAW_HAZARD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_selfref, EVENT_OP_IS_ENTER_SELFREF),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_powerdown,
+ EVENT_OP_IS_ENTER_POWERDOWN),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_mpsm, EVENT_OP_IS_ENTER_MPSM),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_refresh, EVENT_OP_IS_REFRESH),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_crit_ref, EVENT_OP_IS_CRIT_REF),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_spec_ref, EVENT_OP_IS_SPEC_REF),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_load_mode, EVENT_OP_IS_LOAD_MODE),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_zqcl, EVENT_OP_IS_ZQCL),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_wr_access, EVENT_OP_IS_ZQCS),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_cycles, EVENT_DFI_OR_HPR_REQ_NOCRED),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_retry_fifo_full,
+ EVENT_RETRY_FIFO_FULL_OR_LPR_REQ_NOCRED),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_bsm_alloc, EVENT_BSM_ALLOC),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_bsm_starvation, EVENT_BSM_STARVATION),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_rd,
+ EVENT_VISIBLE_WIN_LIMIT_REACHED_RD),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_wr,
+ EVENT_VISIBLE_WIN_LIMIT_REACHED_WR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mpc, EVENT_OP_IS_DQSOSC_MPC),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mrr, EVENT_OP_IS_DQSOSC_MRR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_tcr_mrr, EVENT_OP_IS_TCR_MRR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_zqstart, EVENT_OP_IS_ZQSTART),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_zqlatch, EVENT_OP_IS_ZQLATCH),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_parity_poison,
+ EVENT_DFI_PARITY_POISON),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_wr_crc_error, EVENT_WR_CRC_ERROR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_capar_error, EVENT_CAPAR_ERROR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_rd_crc_error, EVENT_RD_CRC_ERROR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_rd_uc_ecc_error, EVENT_RD_UC_ECC_ERROR),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_cmd_is_retry, EVENT_DFI_CMD_IS_RETRY),
+ /* Free run event counters */
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_reads, EVENT_DDR_READS),
+ CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_writes, EVENT_DDR_WRITES),
+ NULL
+};
+
+static struct attribute_group odyssey_ddr_perf_events_attr_group = {
+ .name = "events",
+ .attrs = odyssey_ddr_perf_events_attrs,
+};
+
static struct attribute_group cn10k_ddr_perf_events_attr_group = {
.name = "events",
.attrs = cn10k_ddr_perf_events_attrs,
@@ -294,6 +393,13 @@ static const struct attribute_group *cn10k_attr_groups[] = {
NULL,
};
+static const struct attribute_group *odyssey_attr_groups[] = {
+ &odyssey_ddr_perf_events_attr_group,
+ &cn10k_ddr_perf_format_attr_group,
+ &cn10k_ddr_perf_cpumask_attr_group,
+ NULL
+};
+
/* Default poll timeout is 100 sec, which is very sufficient for
* 48 bit counter incremented max at 5.6 GT/s, which may take many
* hours to overflow.
@@ -314,6 +420,14 @@ static int ddr_perf_get_event_bitmap(int eventid, u64 *event_bitmap,
case EVENT_OP_IS_REFRESH ... EVENT_OP_IS_ZQLATCH:
*event_bitmap = (1ULL << (eventid - 1));
break;
+ case EVENT_DFI_PARITY_POISON ...EVENT_DFI_CMD_IS_RETRY:
+ if (ddr_pmu->version == DDR_PMU_V2) {
+ *event_bitmap = (1ULL << (eventid - 1));
+ } else {
+ pr_err("%s Invalid eventid %d\n", __func__, eventid);
+ return -EINVAL;
+ }
+ break;
case EVENT_OP_IS_ENTER_SELFREF:
case EVENT_OP_IS_ENTER_POWERDOWN:
case EVENT_OP_IS_ENTER_MPSM:
@@ -439,6 +553,7 @@ static u64 cn10k_ddr_perf_read_counter(struct cn10k_ddr_pmu *pmu, int counter)
val = readq_relaxed(pmu->base +
DDRC_PERF_REG(p_data->ddrc_perf_cnt_base, counter));
+
return val;
}
@@ -459,15 +574,43 @@ static void cn10k_ddr_perf_event_update(struct perf_event *event)
local64_add((new_count - prev_count) & mask, &event->count);
}
+static void cn10k_ddr_perf_counter_start(struct cn10k_ddr_pmu *ddr_pmu,
+ int counter)
+{
+ const struct ddr_pmu_platform_data *p_data = ddr_pmu->p_data;
+ u64 ctrl_reg = p_data->ddrc_perf_cnt_start_op_ctrl;
+
+ writeq_relaxed(START_OP_CTRL_VAL_START, ddr_pmu->base +
+ DDRC_PERF_REG(ctrl_reg, counter));
+}
+
+static void cn10k_ddr_perf_counter_stop(struct cn10k_ddr_pmu *ddr_pmu,
+ int counter)
+{
+ const struct ddr_pmu_platform_data *p_data = ddr_pmu->p_data;
+ u64 ctrl_reg = p_data->ddrc_perf_cnt_end_op_ctrl;
+
+ writeq_relaxed(END_OP_CTRL_VAL_END, ddr_pmu->base +
+ DDRC_PERF_REG(ctrl_reg, counter));
+}
+
static void cn10k_ddr_perf_event_start(struct perf_event *event, int flags)
{
struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu);
+ u64 ctrl_reg = pmu->p_data->ddrc_perf_cnt_op_mode_ctrl;
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
local64_set(&hwc->prev_count, 0);
cn10k_ddr_perf_counter_enable(pmu, counter, true);
+ if (pmu->version == DDR_PMU_V2) {
+ /* Setup the PMU counter to work in manual mode */
+ writeq_relaxed(OP_MODE_CTRL_VAL_MANUAL, pmu->base +
+ DDRC_PERF_REG(ctrl_reg, counter));
+
+ cn10k_ddr_perf_counter_start(pmu, counter);
+ }
hwc->state = 0;
}
@@ -526,6 +669,9 @@ static void cn10k_ddr_perf_event_stop(struct perf_event *event, int flags)
cn10k_ddr_perf_counter_enable(pmu, counter, false);
+ if (pmu->version == DDR_PMU_V2)
+ cn10k_ddr_perf_counter_stop(pmu, counter);
+
if (flags & PERF_EF_UPDATE)
cn10k_ddr_perf_event_update(event);
@@ -642,6 +788,61 @@ static void ddr_pmu_overflow_hander(struct cn10k_ddr_pmu *pmu, int evt_idx)
cn10k_ddr_perf_pmu_enable(&pmu->pmu);
}
+static void ddr_pmu_v2_enable_read_freerun(struct cn10k_ddr_pmu *pmu,
+ bool enable)
+{
+ const struct ddr_pmu_platform_data *p_data = pmu->p_data;
+ u64 val;
+
+ val = readq_relaxed(pmu->base + p_data->ddrc_perf_cnt_freerun_ctrl);
+ if (enable)
+ val |= DDRC_PERF_FREERUN_READ_EN;
+ else
+ val &= ~DDRC_PERF_FREERUN_READ_EN;
+
+ writeq_relaxed(val, pmu->base + p_data->ddrc_perf_cnt_freerun_ctrl);
+}
+
+static void ddr_pmu_v2_enable_write_freerun(struct cn10k_ddr_pmu *pmu,
+ bool enable)
+{
+ const struct ddr_pmu_platform_data *p_data = pmu->p_data;
+ u64 val;
+
+ val = readq_relaxed(pmu->base + p_data->ddrc_perf_cnt_freerun_ctrl);
+ if (enable)
+ val |= DDRC_PERF_FREERUN_WRITE_EN;
+ else
+ val &= ~DDRC_PERF_FREERUN_WRITE_EN;
+
+ writeq_relaxed(val, pmu->base + p_data->ddrc_perf_cnt_freerun_ctrl);
+}
+
+static void ddr_pmu_v2_read_clear_freerun(struct cn10k_ddr_pmu *pmu)
+{
+ const struct ddr_pmu_platform_data *p_data = pmu->p_data;
+ u64 val;
+
+ val = DDRC_FREERUN_READ_CNT_CLR;
+ writeq_relaxed(val, pmu->base + p_data->ddrc_perf_cnt_freerun_clr);
+}
+
+static void ddr_pmu_v2_write_clear_freerun(struct cn10k_ddr_pmu *pmu)
+{
+ const struct ddr_pmu_platform_data *p_data = pmu->p_data;
+ u64 val;
+
+ val = DDRC_FREERUN_WRITE_CNT_CLR;
+ writeq_relaxed(val, pmu->base + p_data->ddrc_perf_cnt_freerun_clr);
+}
+
+static void ddr_pmu_v2_overflow_hander(struct cn10k_ddr_pmu *pmu, int evt_idx)
+{
+ cn10k_ddr_perf_event_update(pmu->events[evt_idx]);
+ cn10k_ddr_perf_counter_stop(pmu, evt_idx);
+ cn10k_ddr_perf_counter_start(pmu, evt_idx);
+}
+
static irqreturn_t cn10k_ddr_pmu_overflow_handler(struct cn10k_ddr_pmu *pmu)
{
const struct ddr_pmu_platform_data *p_data = pmu->p_data;
@@ -753,6 +954,35 @@ static const struct ddr_pmu_data ddr_pmu_data = {
.id = DDR_PMU_V1,
};
+static const struct ddr_pmu_ops ddr_pmu_v2_ops = {
+ .enable_read_freerun_counter = ddr_pmu_v2_enable_read_freerun,
+ .enable_write_freerun_counter = ddr_pmu_v2_enable_write_freerun,
+ .clear_read_freerun_counter = ddr_pmu_v2_read_clear_freerun,
+ .clear_write_freerun_counter = ddr_pmu_v2_write_clear_freerun,
+ .pmu_overflow_handler = ddr_pmu_v2_overflow_hander,
+};
+
+static const struct ddr_pmu_platform_data odyssey_ddr_pmu_pdata = {
+ .counter_overflow_val = 0,
+ .counter_max_val = GENMASK_ULL(63, 0),
+ .ddrc_perf_cnt_base = ODY_DDRC_PERF_CNT_VALUE_BASE,
+ .ddrc_perf_cfg_base = ODY_DDRC_PERF_CFG_BASE,
+ .ddrc_perf_cnt_op_mode_ctrl = ODY_DDRC_PERF_CNT_OP_MODE_CTRL,
+ .ddrc_perf_cnt_start_op_ctrl = ODY_DDRC_PERF_CNT_START_OP_CTRL,
+ .ddrc_perf_cnt_end_op_ctrl = ODY_DDRC_PERF_CNT_END_OP_CTRL,
+ .ddrc_perf_cnt_end_status = ODY_DDRC_PERF_CNT_END_STATUS,
+ .ddrc_perf_cnt_freerun_en = 0,
+ .ddrc_perf_cnt_freerun_ctrl = ODY_DDRC_PERF_CNT_FREERUN_CTRL,
+ .ddrc_perf_cnt_freerun_clr = ODY_DDRC_PERF_CNT_FREERUN_CLR,
+ .ddrc_perf_cnt_value_wr_op = ODY_DDRC_PERF_CNT_VALUE_WR_OP,
+ .ddrc_perf_cnt_value_rd_op = ODY_DDRC_PERF_CNT_VALUE_RD_OP,
+ .ops = &ddr_pmu_v2_ops,
+};
+
+static const struct ddr_pmu_data ddr_pmu_v2_data = {
+ .id = DDR_PMU_V2,
+};
+
static int cn10k_ddr_perf_probe(struct platform_device *pdev)
{
const struct ddr_pmu_data *dev_data;
@@ -805,6 +1035,21 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev)
};
ddr_pmu->p_data = &cn10k_ddr_pmu_pdata;
+ } else {
+ ddr_pmu->pmu = (struct pmu) {
+ .module = THIS_MODULE,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE,
+ .task_ctx_nr = perf_invalid_context,
+ .attr_groups = odyssey_attr_groups,
+ .event_init = cn10k_ddr_perf_event_init,
+ .add = cn10k_ddr_perf_event_add,
+ .del = cn10k_ddr_perf_event_del,
+ .start = cn10k_ddr_perf_event_start,
+ .stop = cn10k_ddr_perf_event_stop,
+ .read = cn10k_ddr_perf_event_update,
+ };
+
+ ddr_pmu->p_data = &odyssey_ddr_pmu_pdata;
}
/* Choose this cpu to collect perf data */
@@ -858,6 +1103,7 @@ MODULE_DEVICE_TABLE(of, cn10k_ddr_pmu_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id cn10k_ddr_pmu_acpi_match[] = {
{"MRVL000A", (kernel_ulong_t)&ddr_pmu_data},
+ {"MRVL000C", (kernel_ulong_t)&ddr_pmu_v2_data},
{},
};
MODULE_DEVICE_TABLE(acpi, cn10k_ddr_pmu_acpi_match);
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RESEND PATCH v4 3/3] perf/marvell : Odyssey LLC-TAD performance monitor
From: Gowthami Thiagarajan @ 2024-03-27 7:18 UTC (permalink / raw)
To: will, mark.rutland, linux-arm-kernel, linux-kernel
Cc: sgoutham, bbhushan2, gcherian, Gowthami Thiagarajan
In-Reply-To: <20240327071832.1556576-1-gthiagarajan@marvell.com>
Each TAD provides eight 64-bit counters for monitoring
cache behavior.The driver always configures the same counter for
all the TADs. The user would end up effectively reserving one of
eight counters in every TAD to look across all TADs.
The occurrences of events are aggregated and presented to the user
at the end of running the workload. The driver does not provide a
way for the user to partition TADs so that different TADs are used for
different applications.
The performance events reflect various internal or interface activities.
By combining the values from multiple performance counters, cache
performance can be measured in terms such as: cache miss rate, cache
allocations, interface retry rate, internal resource occupancy, etc.
Each supported counter's event and formatting information is exposed
to sysfs at /sys/devices/tad/. Use perf tool stat command to measure
the pmu events. For instance:
perf stat -e tad_hit_ltg,tad_hit_dtg <workload>
Signed-off-by: Gowthami Thiagarajan <gthiagarajan@marvell.com>
---
drivers/perf/marvell_cn10k_tad_pmu.c | 62 ++++++++++++++++++++++++++--
1 file changed, 59 insertions(+), 3 deletions(-)
diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c b/drivers/perf/marvell_cn10k_tad_pmu.c
index fec8e82edb95..df6569f7a06f 100644
--- a/drivers/perf/marvell_cn10k_tad_pmu.c
+++ b/drivers/perf/marvell_cn10k_tad_pmu.c
@@ -37,6 +37,15 @@ struct tad_pmu {
DECLARE_BITMAP(counters_map, TAD_MAX_COUNTERS);
};
+enum mrvl_tad_pmu_version {
+ TAD_PMU_V1 = 1,
+ TAD_PMU_V2,
+};
+
+struct tad_pmu_data {
+ int id;
+};
+
static int tad_pmu_cpuhp_state;
static void tad_pmu_event_counter_read(struct perf_event *event)
@@ -214,6 +223,24 @@ static const struct attribute_group tad_pmu_events_attr_group = {
.attrs = tad_pmu_event_attrs,
};
+static struct attribute *ody_tad_pmu_event_attrs[] = {
+ TAD_PMU_EVENT_ATTR(tad_req_msh_in_exlmn, 0x3),
+ TAD_PMU_EVENT_ATTR(tad_alloc_dtg, 0x1a),
+ TAD_PMU_EVENT_ATTR(tad_alloc_ltg, 0x1b),
+ TAD_PMU_EVENT_ATTR(tad_alloc_any, 0x1c),
+ TAD_PMU_EVENT_ATTR(tad_hit_dtg, 0x1d),
+ TAD_PMU_EVENT_ATTR(tad_hit_ltg, 0x1e),
+ TAD_PMU_EVENT_ATTR(tad_hit_any, 0x1f),
+ TAD_PMU_EVENT_ATTR(tad_tag_rd, 0x20),
+ TAD_PMU_EVENT_ATTR(tad_tot_cycle, 0xFF),
+ NULL
+};
+
+static const struct attribute_group ody_tad_pmu_events_attr_group = {
+ .name = "events",
+ .attrs = ody_tad_pmu_event_attrs,
+};
+
PMU_FORMAT_ATTR(event, "config:0-7");
static struct attribute *tad_pmu_format_attrs[] = {
@@ -252,8 +279,16 @@ static const struct attribute_group *tad_pmu_attr_groups[] = {
NULL
};
+static const struct attribute_group *ody_tad_pmu_attr_groups[] = {
+ &ody_tad_pmu_events_attr_group,
+ &tad_pmu_format_attr_group,
+ &tad_pmu_cpumask_attr_group,
+ NULL
+};
+
static int tad_pmu_probe(struct platform_device *pdev)
{
+ const struct tad_pmu_data *dev_data;
struct device *dev = &pdev->dev;
struct tad_region *regions;
struct tad_pmu *tad_pmu;
@@ -261,6 +296,7 @@ static int tad_pmu_probe(struct platform_device *pdev)
u32 tad_pmu_page_size;
u32 tad_page_size;
u32 tad_cnt;
+ int version;
int i, ret;
char *name;
@@ -270,6 +306,13 @@ static int tad_pmu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, tad_pmu);
+ dev_data = device_get_match_data(&pdev->dev);
+ if (!dev_data) {
+ dev_err(&pdev->dev, "Error: No device match data found\n");
+ return -ENODEV;
+ }
+ version = dev_data->id;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Mem resource not found\n");
@@ -319,7 +362,6 @@ static int tad_pmu_probe(struct platform_device *pdev)
tad_pmu->pmu = (struct pmu) {
.module = THIS_MODULE,
- .attr_groups = tad_pmu_attr_groups,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE |
PERF_PMU_CAP_NO_INTERRUPT,
.task_ctx_nr = perf_invalid_context,
@@ -332,6 +374,11 @@ static int tad_pmu_probe(struct platform_device *pdev)
.read = tad_pmu_event_counter_read,
};
+ if (version == TAD_PMU_V1)
+ tad_pmu->pmu.attr_groups = tad_pmu_attr_groups;
+ else
+ tad_pmu->pmu.attr_groups = ody_tad_pmu_attr_groups;
+
tad_pmu->cpu = raw_smp_processor_id();
/* Register pmu instance for cpu hotplug */
@@ -362,16 +409,25 @@ static int tad_pmu_remove(struct platform_device *pdev)
return 0;
}
+static const struct tad_pmu_data tad_pmu_data = {
+ .id = TAD_PMU_V1,
+};
+
+static const struct tad_pmu_data tad_pmu_v2_data = {
+ .id = TAD_PMU_V2,
+};
+
#ifdef CONFIG_OF
static const struct of_device_id tad_pmu_of_match[] = {
- { .compatible = "marvell,cn10k-tad-pmu", },
+ { .compatible = "marvell,cn10k-tad-pmu", .data = &tad_pmu_data },
{},
};
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id tad_pmu_acpi_match[] = {
- {"MRVL000B", 0},
+ {"MRVL000B", (kernel_ulong_t)&tad_pmu_data},
+ {"MRVL000D", (kernel_ulong_t)&tad_pmu_v2_data},
{},
};
MODULE_DEVICE_TABLE(acpi, tad_pmu_acpi_match);
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [RESEND PATCH v4 0/3] Marvell Odyssey uncore performance monitor support
From: Gowthami Thiagarajan @ 2024-03-27 7:18 UTC (permalink / raw)
To: will, mark.rutland, linux-arm-kernel, linux-kernel
Cc: sgoutham, bbhushan2, gcherian, Gowthami Thiagarajan
Odyssey is a 64 bit ARM based SoC with multiple performance monitor
units for various blocks.
This series of patches introduces support for uncore performance monitor
units (PMUs) on the Marvell Odyssey platform. The PMUs covered in this
series include the DDR PMU and LLC-TAD PMU.
v3->v4:
- Refactored the existing ddr pmu driver to extract the platform data
Gowthami Thiagarajan (3):
perf/marvell: Refactor to extract platform data - no functional change
perf/marvell: perf/marvell: Odyssey DDR Performance monitor support
perf/marvell : Odyssey LLC-TAD performance monitor support
drivers/perf/marvell_cn10k_ddr_pmu.c | 528 +++++++++++++++++++++++----
drivers/perf/marvell_cn10k_tad_pmu.c | 62 +++-
2 files changed, 515 insertions(+), 75 deletions(-)
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 18/19] memory: pl353-smc: drop owner assignment
From: Miquel Raynal @ 2024-03-27 7:14 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Russell King, Suzuki K Poulose, Mike Leach, James Clark,
Alexander Shishkin, Maxime Coquelin, Alexandre Torgue,
Linus Walleij, Andi Shyti, Olivia Mackall, Herbert Xu, Vinod Koul,
Dmitry Torokhov, Michal Simek, Eric Auger, Alex Williamson,
linux-kernel, coresight, linux-arm-kernel, linux-stm32, linux-i2c,
linux-crypto, dmaengine, linux-input, kvm
In-Reply-To: <20240326-module-owner-amba-v1-18-4517b091385b@linaro.org>
Hi Krzysztof,
krzysztof.kozlowski@linaro.org wrote on Tue, 26 Mar 2024 21:23:48 +0100:
> Amba bus core already sets owner, so driver does not need to.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
>
> ---
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Thanks,
Miquèl
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] ARM: vfp: use asm volatile for FP control register accesses
From: Ard Biesheuvel @ 2024-03-27 7:05 UTC (permalink / raw)
To: Nathan Chancellor; +Cc: linux-arm-kernel, linux, arnd, linus.walleij, stable
In-Reply-To: <20240326235529.GA2025585@dev-arch.thelio-3990X>
On Wed, 27 Mar 2024 at 01:55, Nathan Chancellor <nathan@kernel.org> wrote:
>
> On Mon, Mar 18, 2024 at 10:30:05AM +0100, Ard Biesheuvel wrote:
> > From: Ard Biesheuvel <ardb@kernel.org>
> >
> > Clang may reorder FP control register reads and writes, due to the fact
> > that the inline asm() blocks in the read/write wrappers are not volatile
> > qualified, and the compiler has no idea that these reads and writes may
> > have side effects.
> >
> > In particular, reads of FPSCR may generate an UNDEF exception if a
> > floating point exception is pending, and the FP emulation code in
> > VFP_bounce() explicitly clears FP exceptions temporarily in order to be
> > able to perform the emulation on behalf of user space. This requires
> > that the writes to FPEXC are never reordered with respect to accesses to
> > other FP control registers, such as FPSCR.
> >
> > So use asm volatile for both the read and the write helpers.
> >
> > Cc: <stable@kernel.org>
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>
> This seems reasonable to me based on my understanding of GCC's
> documentation. However, their documentation states "the compiler can
> move even volatile asm instructions relative to other code, including
> across jump instructions" and I feel like there was some discussion
> around this sentence in the past but I can't remember what the
> conclusion was, although I want to say Clang did not have the same
> behavior.
The only thing that matters here is whether two asm blocks are emitted
in a different order than they appear in the program, and I would be
very surprised if volatile permits that. Otherwise, we might introduce
a fake input dependency or a memory clobber instead.
> Regardless:
>
> Acked-by: Nathan Chancellor <nathan@kernel.org>
>
Thanks.
> I am just curious, how was this discovered or noticed? Was there a
> report I missed?
>
I noticed this when building a recent kernel for the original
Raspberry Pi, which is ARMv6 not ARMv7, and has a VFP which partially
relies on emulation. On more recent cores, we never hit the issue
because emulation is never needed. On older cores, there is no VFP so
we never reach this code path either.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 0/3] dt-bindings: arm: bcm: raspberrypi,bcm2835-firmware: Drive-by fixes
From: Stefan Wahren @ 2024-03-27 6:49 UTC (permalink / raw)
To: Laurent Pinchart, Peter Robinson, Ivan T. Ivanov
Cc: Dave Stevenson, Naushir Patuck, Bartosz Golaszewski,
Broadcom internal kernel review list, Conor Dooley,
Florian Fainelli, Krzysztof Kozlowski, Linus Walleij,
Nicolas Saenz Julienne, Ray Jui, Rob Herring, Scott Branden,
linux-arm-kernel, devicetree, linux-rpi-kernel,
u-boot@lists.denx.de
In-Reply-To: <20240326195807.15163-1-laurent.pinchart@ideasonboard.com>
Hi,
[add Peter and Ivan]
Am 26.03.24 um 20:58 schrieb Laurent Pinchart:
> Hello,
>
> This small series includes a few drive-by fixes for DT validation
> errors.
>
> The first patch has been posted previously in v1 ([1], and now addresses
> a small review comment. I think it's good to go.
>
> The next two patches address the same issue as "[PATCH 1/2] dt-bindings:
> arm: bcm: raspberrypi,bcm2835-firmware: Add missing properties" ([2]),
> but this time with a (hopefully) correct approach. Patch 2/3 starts by
> fixing the raspberrypi-bcm2835-firmware driver, removing the need for DT
> properties that are specified in bcm2835-rpi.dtsi but not documented in
> the corresponding bindings. Patch 3/3 can then drop those properties,
> getting rid of the warnings.
since this series drops properties from the device tree, does anyone
have the chance to test it with a recent U-Boot?
>
> [1] https://lore.kernel.org/linux-arm-kernel/20240326004902.17054-3-laurent.pinchart@ideasonboard.com/
> [2] https://lore.kernel.org/linux-arm-kernel/20240326004902.17054-2-laurent.pinchart@ideasonboard.com/
>
> Laurent Pinchart (3):
> dt-bindings: arm: bcm: raspberrypi,bcm2835-firmware: Add gpio child
> node
> firmware: raspberrypi: Use correct device for DMA mappings
> ARM: dts: bcm283x: Drop unneeded properties in the bcm2835-firmware
> node
>
> .../arm/bcm/raspberrypi,bcm2835-firmware.yaml | 30 +++++++++++++++++++
> .../gpio/raspberrypi,firmware-gpio.txt | 30 -------------------
> arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi | 4 ---
> drivers/firmware/raspberrypi.c | 7 +++--
> 4 files changed, 34 insertions(+), 37 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/gpio/raspberrypi,firmware-gpio.txt
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 1/2] ACPI/AEST: Initial AEST driver
From: Shuai Xue @ 2024-03-27 5:46 UTC (permalink / raw)
To: Ruidong Tian, catalin.marinas, will, lpieralisi, guohanjun,
sudeep.holla, baolin.wang, linux-kernel, linux-acpi,
linux-arm-kernel, rafael, lenb, tony.luck, bp, linux-edac,
james.morse@arm.com
Cc: tianruidond, Tyler Baicar
In-Reply-To: <20240321025317.114621-2-tianruidong@linux.alibaba.com>
On 2024/3/21 10:53, Ruidong Tian wrote:
> From: Tyler Baicar <baicar@os.amperecomputing.com>
>
> Add support for parsing the ARM Error Source Table and basic handling of
> errors reported through both memory mapped and system register interfaces.
>
> Assume system register interfaces are only registered with private
> peripheral interrupts (PPIs); otherwise there is no guarantee the
> core handling the error is the core which took the error and has the
> syndrome info in its system registers.
>
> In kernel-first mode, all configuration is controlled by kernel, include
> CE ce_threshold and interrupt enable/disable.
>
> All detected errors will be processed as follow:
> - CE, DE: use a workqueue to log this hardware errors.
> - UER, UEO: log it and call memory_failure in workquee.
> - UC, UEU: panic in irq context.
>
> Signed-off-by: Tyler Baicar <baicar@os.amperecomputing.com>
> Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
> ---
> MAINTAINERS | 11 +
> arch/arm64/include/asm/ras.h | 71 +++
> drivers/acpi/arm64/Kconfig | 10 +
> drivers/acpi/arm64/Makefile | 1 +
> drivers/acpi/arm64/aest.c | 834 +++++++++++++++++++++++++++++++++++
> include/linux/acpi_aest.h | 92 ++++
> include/linux/cpuhotplug.h | 1 +
> 7 files changed, 1020 insertions(+)
> create mode 100644 arch/arm64/include/asm/ras.h
> create mode 100644 drivers/acpi/arm64/aest.c
> create mode 100644 include/linux/acpi_aest.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dd5de540ec0b..34900d4bb677 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -330,6 +330,17 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
> S: Maintained
> F: drivers/acpi/arm64
>
> +ACPI AEST
> +M: Tyler Baicar <baicar@os.amperecomputing.com>
> +M: Ruidong Tian <tianruidond@linux.alibaba.com>
> +L: linux-acpi@vger.kernel.org
> +L: linux-arm-kernel@lists.infradead.org
> +S: Supported
> +F: arch/arm64/include/asm/ras.h
> +F: drivers/acpi/arm64/aest.c
> +F: include/linux/acpi_aest.h
> +
> +
> ACPI FOR RISC-V (ACPI/riscv)
> M: Sunil V L <sunilvl@ventanamicro.com>
> L: linux-acpi@vger.kernel.org
> diff --git a/arch/arm64/include/asm/ras.h b/arch/arm64/include/asm/ras.h
> new file mode 100644
> index 000000000000..04667f0de30f
> --- /dev/null
> +++ b/arch/arm64/include/asm/ras.h
> @@ -0,0 +1,71 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __ASM_RAS_H
> +#define __ASM_RAS_H
> +
> +#include <linux/types.h>
> +#include <linux/bits.h>
> +
> +/* ERR<n>FR */
> +#define ERR_FR_RP BIT(15)
> +#define ERR_FR_CEC GENMASK_ULL(14, 12)
> +
> +#define ERR_FR_RP_SINGLE_COUNTER 0
> +#define ERR_FR_RP_DOUBLE_COUNTER 1
> +
> +#define ERR_FR_CEC_0B_COUNTER 0
> +#define ERR_FR_CEC_8B_COUNTER BIT(1)
> +#define ERR_FR_CEC_16B_COUNTER BIT(2)
> +
> +/* ERR<n>STATUS */
> +#define ERR_STATUS_AV BIT(31)
> +#define ERR_STATUS_V BIT(30)
> +#define ERR_STATUS_UE BIT(29)
> +#define ERR_STATUS_ER BIT(28)
> +#define ERR_STATUS_OF BIT(27)
> +#define ERR_STATUS_MV BIT(26)
> +#define ERR_STATUS_CE (BIT(25) | BIT(24))
> +#define ERR_STATUS_DE BIT(23)
> +#define ERR_STATUS_PN BIT(22)
> +#define ERR_STATUS_UET (BIT(21) | BIT(20))
> +#define ERR_STATUS_CI BIT(19)
> +#define ERR_STATUS_IERR GENMASK_ULL(15, 8)
> +#define ERR_STATUS_SERR GENMASK_ULL(7, 0)
> +
> +/* These bit is write-one-to-clear */
Typo: s/These bit is/These bits are
> +#define ERR_STATUS_W1TC (ERR_STATUS_AV | ERR_STATUS_V | ERR_STATUS_UE | \
> + ERR_STATUS_ER | ERR_STATUS_OF | ERR_STATUS_MV | \
> + ERR_STATUS_CE | ERR_STATUS_DE | ERR_STATUS_PN | \
> + ERR_STATUS_UET | ERR_STATUS_CI)
> +
> +#define ERR_STATUS_UET_UC 0
> +#define ERR_STATUS_UET_UEU 1
> +#define ERR_STATUS_UET_UER 2
> +#define ERR_STATUS_UET_UEO 3
> +
> +/* ERR<n>CTLR */
> +#define ERR_CTLR_FI BIT(3)
> +#define ERR_CTLR_UI BIT(2)
> +
> +/* ERR<n>ADDR */
> +#define ERR_ADDR_AI BIT(61)
> +#define ERR_ADDR_PADDR GENMASK_ULL(55, 0)
> +
> +/* ERR<n>MISC0 */
> +
> +/* ERR<n>FR.CEC == 0b010, ERR<n>FR.RP == 0 */
> +#define ERR_MISC0_8B_OF BIT(39)
> +#define ERR_MISC0_8B_CEC GENMASK_ULL(38, 32)
> +
> +/* ERR<n>FR.CEC == 0b100, ERR<n>FR.RP == 0 */
> +#define ERR_MISC0_16B_OF BIT(47)
> +#define ERR_MISC0_16B_CEC GENMASK_ULL(46, 32)
> +
> +struct ras_ext_regs {
> + u64 err_fr;
> + u64 err_ctlr;
> + u64 err_status;
> + u64 err_addr;
> + u64 err_misc[4];
> +};
> +
> +#endif /* __ASM_RAS_H */
> diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
> index b3ed6212244c..639db671c5cf 100644
> --- a/drivers/acpi/arm64/Kconfig
> +++ b/drivers/acpi/arm64/Kconfig
> @@ -21,3 +21,13 @@ config ACPI_AGDI
>
> config ACPI_APMT
> bool
> +
> +config ACPI_AEST
> + bool "ARM Error Source Table Support"
> +
> + help
> + The Arm Error Source Table (AEST) provides details on ACPI
> + extensions that enable kernel-first handling of errors in a
> + system that supports the Armv8 RAS extensions.
> +
> + If set, the kernel will report and log hardware errors.
> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
> index 143debc1ba4a..b5b740058c46 100644
> --- a/drivers/acpi/arm64/Makefile
> +++ b/drivers/acpi/arm64/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_ACPI_GTDT) += gtdt.o
> obj-$(CONFIG_ACPI_APMT) += apmt.o
> obj-$(CONFIG_ARM_AMBA) += amba.o
> obj-y += dma.o init.o
> +obj-$(CONFIG_ACPI_AEST) += aest.o
> diff --git a/drivers/acpi/arm64/aest.c b/drivers/acpi/arm64/aest.c
> new file mode 100644
> index 000000000000..ab17aa5f5997
> --- /dev/null
> +++ b/drivers/acpi/arm64/aest.c
> @@ -0,0 +1,834 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ARM Error Source Table Support
> + *
> + * Copyright (c) 2021, Ampere Computing LLC
> + * Copyright (c) 2021-2024, Alibaba Group.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/acpi_aest.h>
> +#include <linux/cpuhotplug.h>
> +#include <linux/kernel.h>
> +#include <linux/genalloc.h>
> +#include <linux/llist.h>
> +#include <acpi/actbl.h>
> +#include <asm/ras.h>
> +
> +#undef pr_fmt
> +#define pr_fmt(fmt) "ACPI AEST: " fmt
> +
> +#define CASE_READ(res, x) \
> + case (x): { \
> + res = read_sysreg_s(SYS_##x##_EL1); \
> + break; \
> + }
> +
> +#define CASE_WRITE(val, x) \
> + case (x): { \
> + write_sysreg_s((val), SYS_##x##_EL1); \
> + break; \
> + }
> +
> +#define for_each_implemented_record(index, node) \
> + for ((index) = node->interface.record_start; \
> + (index) < node->interface.record_end; \
> + (index)++)
> +
> +#define AEST_LOG_PREFIX_BUFFER 64
> +
> +/*
> + * This memory pool is only to be used to save AEST node in AEST irq context.
> + * There can be 500 AEST node at most.
> + */
> +#define AEST_NODE_ALLOCED_MAX 500
> +
> +static struct acpi_table_header *aest_table;
> +
> +static struct aest_node __percpu **aest_ppi_data;
> +
> +static int *ppi_irqs;
> +static u8 num_ppi;
> +static u8 ppi_idx;
> +
> +static struct work_struct aest_work;
> +
> +static struct gen_pool *aest_node_pool;
> +static struct llist_head aest_node_llist;
> +
> +static u64 aest_sysreg_read(u64 __unused, u32 offset)
> +{
> + u64 res;
> +
> + switch (offset) {
> + CASE_READ(res, ERXFR)
> + CASE_READ(res, ERXCTLR)
> + CASE_READ(res, ERXSTATUS)
> + CASE_READ(res, ERXADDR)
> + CASE_READ(res, ERXMISC0)
> + CASE_READ(res, ERXMISC1)
> + CASE_READ(res, ERXMISC2)
> + CASE_READ(res, ERXMISC3)
> + default :
> + res = 0;
> + }
> + return res;
> +}
> +
> +static void aest_sysreg_write(u64 base, u32 offset, u64 val)
> +{
> + switch (offset) {
> + CASE_WRITE(val, ERXFR)
> + CASE_WRITE(val, ERXCTLR)
> + CASE_WRITE(val, ERXSTATUS)
> + CASE_WRITE(val, ERXADDR)
> + CASE_WRITE(val, ERXMISC0)
> + CASE_WRITE(val, ERXMISC1)
> + CASE_WRITE(val, ERXMISC2)
> + CASE_WRITE(val, ERXMISC3)
> + default :
> + return;
> + }
> +}
> +
> +static u64 aest_iomem_read(u64 base, u32 offset)
> +{
> + return readq_relaxed((void *)(base + offset));
> +}
> +
> +static void aest_iomem_write(u64 base, u32 offset, u64 val)
> +{
> + writeq_relaxed(val, (void *)(base + offset));
> +}
> +
> +static void aest_print(struct aest_node_llist *lnode)
> +{
> + static atomic_t seqno = { 0 };
> + unsigned int curr_seqno;
> + char pfx_seq[AEST_LOG_PREFIX_BUFFER];
> + int index;
> + struct ras_ext_regs *regs;
> +
> + curr_seqno = atomic_inc_return(&seqno);
> + snprintf(pfx_seq, sizeof(pfx_seq), "{%u}" HW_ERR, curr_seqno);
> + pr_info("%sHardware error from %s\n", pfx_seq, lnode->node_name);
> +
> + switch (lnode->type) {
> + case ACPI_AEST_PROCESSOR_ERROR_NODE:
> + pr_err("%s Error from CPU%d\n", pfx_seq, lnode->id0);
> + break;
> + case ACPI_AEST_MEMORY_ERROR_NODE:
> + pr_err("%s Error from memory at SRAT proximity domain 0x%x\n",
> + pfx_seq, lnode->id0);
> + break;
> + case ACPI_AEST_SMMU_ERROR_NODE:
> + pr_err("%s Error from SMMU IORT node 0x%x subcomponent 0x%x\n",
> + pfx_seq, lnode->id0, lnode->id1);
> + break;
> + case ACPI_AEST_VENDOR_ERROR_NODE:
> + pr_err("%s Error from vendor hid 0x%x uid 0x%x\n",
> + pfx_seq, lnode->id0, lnode->id1);
> + break;
> + case ACPI_AEST_GIC_ERROR_NODE:
> + pr_err("%s Error from GIC type 0x%x instance 0x%x\n",
> + pfx_seq, lnode->id0, lnode->id1);
> + break;
> + default:
> + pr_err("%s Unknown AEST node type\n", pfx_seq);
> + return;
> + }
> +
> + index = lnode->index;
> + regs = lnode->regs;
> +
> + pr_err("%s ERR%uFR: 0x%llx\n", pfx_seq, index, regs->err_fr);
> + pr_err("%s ERR%uCTRL: 0x%llx\n", pfx_seq, index, regs->err_ctlr);
> + pr_err("%s ERR%uSTATUS: 0x%llx\n", pfx_seq, index, regs->err_status);
> + if (regs->err_status & ERR_STATUS_AV)
> + pr_err("%s ERR%uADDR: 0x%llx\n", pfx_seq, index, regs->err_addr);
> +
> + if (regs->err_status & ERR_STATUS_MV) {
> + pr_err("%s ERR%uMISC0: 0x%llx\n", pfx_seq, index, regs->err_misc[0]);
> + pr_err("%s ERR%uMISC1: 0x%llx\n", pfx_seq, index, regs->err_misc[1]);
> + pr_err("%s ERR%uMISC2: 0x%llx\n", pfx_seq, index, regs->err_misc[2]);
> + pr_err("%s ERR%uMISC3: 0x%llx\n", pfx_seq, index, regs->err_misc[3]);
> + }
> +}
> +
> +static void aest_handle_memory_failure(struct aest_node_llist *lnode)
> +{
> + unsigned long pfn;
> + u64 addr;
> +
> + if (test_bit(lnode->index, &lnode->addressing_mode) ||
> + (lnode->regs->err_addr & ERR_ADDR_AI))
> + return;
> +
> + addr = lnode->regs->err_addr & (1UL << CONFIG_ARM64_PA_BITS);
> + pfn = PHYS_PFN(addr);
> +
> + if (!pfn_valid(pfn)) {
> + pr_warn(HW_ERR "Invalid physical address: %#llx\n", addr);
> + return;
> + }
> +
> + memory_failure(pfn, 0);
> +}
> +
> +static void aest_node_pool_process(struct work_struct *__unused)
> +{
> + struct llist_node *head;
> + struct aest_node_llist *lnode, *tmp;
> + u64 status;
> +
> + head = llist_del_all(&aest_node_llist);
> + if (!head)
> + return;
> +
> + head = llist_reverse_order(head);
> + llist_for_each_entry_safe(lnode, tmp, head, llnode) {
Do we really need to pretect the llnode with _safe() here?
> + aest_print(lnode);
> +
> + status = lnode->regs->err_status;
> + if ((status & ERR_STATUS_UE) &&
> + (status & ERR_STATUS_UET) > ERR_STATUS_UET_UEU)
> + aest_handle_memory_failure(lnode);
> + gen_pool_free(aest_node_pool, (unsigned long)lnode,
> + sizeof(*lnode));
> + }
> +}
> +
> +static int aest_node_gen_pool_add(struct aest_node *node, int index,
> + struct ras_ext_regs *regs)
> +{
> + struct aest_node_llist *list;
I know name is alway a hard work, but list is meanless to aest.
> +
> + if (!aest_node_pool)
> + return -EINVAL;
> +
> + list = (void *)gen_pool_alloc(aest_node_pool, sizeof(*list));
> + if (!list)
> + return -ENOMEM;
> +
> + list->type = node->type;
> + list->node_name = node->name;
> + switch (node->type) {
> + case ACPI_AEST_PROCESSOR_ERROR_NODE:
> + list->id0 = node->spec.processor.processor_id;
> + if (node->spec.processor.flags & (ACPI_AEST_PROC_FLAG_SHARED |
> + ACPI_AEST_PROC_FLAG_GLOBAL))
> + list->id0 = smp_processor_id();
> +
> + list->id1 = node->spec.processor.resource_type;
> + break;
> + case ACPI_AEST_MEMORY_ERROR_NODE:
> + list->id0 = node->spec.memory.srat_proximity_domain;
> + break;
> + case ACPI_AEST_SMMU_ERROR_NODE:
> + list->id0 = node->spec.smmu.iort_node_reference;
> + list->id1 = node->spec.smmu.subcomponent_reference;
> + break;
> + case ACPI_AEST_VENDOR_ERROR_NODE:
> + list->id0 = node->spec.vendor.acpi_hid;
> + list->id1 = node->spec.vendor.acpi_uid;
> + break;
> + case ACPI_AEST_GIC_ERROR_NODE:
> + list->id0 = node->spec.gic.interface_type;
> + list->id1 = node->spec.gic.instance_id;
> + break;
> + default:
> + list->id0 = 0;
> + list->id1 = 0;
> + }
> +
> + list->regs = regs;
> + list->index = index;
> + list->addressing_mode = node->interface.addressing_mode;
> + llist_add(&list->llnode, &aest_node_llist);
> +
> + return 0;
> +}
> +
> +static int aest_node_pool_init(void)
> +{
> + unsigned long addr, size;
> + int rc;
> +
> + if (aest_node_pool)
> + return 0;
> +
> + size = ilog2(sizeof(struct aest_node_llist));
> + aest_node_pool = gen_pool_create(size, -1);
> + if (!aest_node_pool)
> + return -ENOMEM;
> +
> + addr = (unsigned long)vmalloc(PAGE_ALIGN(size * AEST_NODE_ALLOCED_MAX));
> + if (!addr)
> + goto err_pool_alloc;
> +
> + rc = gen_pool_add(aest_node_pool, addr, size, -1);
The size you added here is not equal to the size of buffer you alloced from vmalloc().
> + if (rc)
> + goto err_pool_add;
> +
> + return 0;
> +
> +err_pool_add:
> + vfree((void *)addr);
> +
> +err_pool_alloc:
> + gen_pool_destroy(aest_node_pool);
> +
> + return -ENOMEM;
> +}
> +
> +static void aest_log(struct aest_node *node, int index, struct ras_ext_regs *regs)
> +{
> + if (!aest_node_gen_pool_add(node, index, regs))
> + schedule_work(&aest_work);
> +}
> +
> +/*
> + * Each PE may has multi error record, you must selects an error record to
> + * be accessed through the Error Record System registers.
> + */
> +static inline void aest_select_record(struct aest_node *node, int i)
> +{
> + if (node->interface.type == ACPI_AEST_NODE_SYSTEM_REGISTER) {
> + write_sysreg_s(i, SYS_ERRSELR_EL1);
should check if ERRSELR_EL1.SEL is greater than or equal to ERRIDR_EL1.NUM,
> + isb();
> + }
> +}
> +
> +/* Ensure all writes has taken effect. */
> +static inline void aest_sync(struct aest_node *node)
> +{
> + if (node->interface.type == ACPI_AEST_NODE_SYSTEM_REGISTER)
> + isb();
> +}
> +
> +static int aest_proc(struct aest_node *node)
> +{
> + struct ras_ext_regs regs = {0};
> + struct aest_access *access;
> + int i, count = 0;
> + u64 regs_p;
> +
> + for_each_implemented_record(i, node) {
> +
> + /* 1b: Error record at i index is not implemented */
> + if (test_bit(i, &node->interface.record_implemented))
> + continue;
> +
> + aest_select_record(node, i);
> +
> + access = node->access;
> + regs_p = (u64)&node->interface.regs[i];
> +
> + regs.err_status = access->read(regs_p, ERXSTATUS);
> + if (!(regs.err_status & ERR_STATUS_V))
> + continue;
> +
> + count++;
> +
> + if (regs.err_status & ERR_STATUS_AV)
> + regs.err_addr = access->read(regs_p, ERXADDR);
> +
> + regs.err_fr = access->read(regs_p, ERXFR);
> + regs.err_ctlr = access->read(regs_p, ERXCTLR);
> +
> + if (regs.err_status & ERR_STATUS_MV) {
> + regs.err_misc[0] = access->read(regs_p, ERXMISC0);
> + regs.err_misc[1] = access->read(regs_p, ERXMISC1);
> + regs.err_misc[2] = access->read(regs_p, ERXMISC2);
> + regs.err_misc[3] = access->read(regs_p, ERXMISC3);
> + }
> +
> + if (node->interface.flags & ACPI_AEST_INTERFACE_CLEAR_MISC) {
> + access->write(regs_p, ERXMISC0, 0);
> + access->write(regs_p, ERXMISC1, 0);
> + access->write(regs_p, ERXMISC2, 0);
> + access->write(regs_p, ERXMISC3, 0);
> + } else
> + access->write(regs_p, ERXMISC0,
> + node->interface.ce_threshold[i]);
> +
> + aest_log(node, i, ®s);
aest_log() should be calledafter panic check?
> +
> + /* panic if unrecoverable and uncontainable error encountered */
> + if ((regs.err_status & ERR_STATUS_UE) &&
> + (regs.err_status & ERR_STATUS_UET) < ERR_STATUS_UET_UER)
> + panic("AEST: unrecoverable error encountered");
Is arm64_is_fatal_ras_serror() applicable here? @James, can you help to confirm?
> +
> + /* Write-one-to-clear the bits we've seen */
> + regs.err_status &= ERR_STATUS_W1TC;
> +
> + /* Multi bit filed need to write all-ones to clear. */
> + if (regs.err_status & ERR_STATUS_CE)
> + regs.err_status |= ERR_STATUS_CE;
> +
> + /* Multi bit filed need to write all-ones to clear. */
> + if (regs.err_status & ERR_STATUS_UET)
> + regs.err_status |= ERR_STATUS_UET;
> +
> + access->write(regs_p, ERXSTATUS, regs.err_status);
> +
> + aest_sync(node);
> + }
> +
> + return count;
> +}
> +
> +static irqreturn_t aest_irq_func(int irq, void *input)
> +{
> + struct aest_node *node = input;
> +
> + if (aest_proc(node))
> + return IRQ_HANDLED;
> +
> + return IRQ_NONE;
> +}
> +
> +static int __init aest_register_gsi(u32 gsi, int trigger, void *data,
> + irq_handler_t aest_irq_func)
> +{
> + int cpu, irq;
> +
> + irq = acpi_register_gsi(NULL, gsi, trigger, ACPI_ACTIVE_HIGH);
> +
> + if (irq == -EINVAL) {
> + pr_err("failed to map AEST GSI %d\n", gsi);
> + return -EINVAL;
> + }
> +
> + if (irq_is_percpu_devid(irq)) {
> + ppi_irqs[ppi_idx] = irq;
> + for_each_possible_cpu(cpu) {
> + memcpy(per_cpu_ptr(aest_ppi_data[ppi_idx], cpu), data,
> + sizeof(struct aest_node));
> + }
> + if (request_percpu_irq(irq, aest_irq_func, "AEST",
> + aest_ppi_data[ppi_idx++])) {
> + pr_err("failed to register AEST IRQ %d\n", irq);
> + return -EINVAL;
> + }
> + } else {
> + if (request_irq(irq, aest_irq_func, IRQF_SHARED, "AEST",
> + data)) {
> + pr_err("failed to register AEST IRQ %d\n", irq);
> + return -EINVAL;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int __init aest_init_interrupts(struct acpi_aest_hdr *hdr,
> + struct aest_node *node)
> +{
> + struct acpi_aest_node_interrupt *interrupt;
> + int i, trigger, ret = 0, err_ctlr, regs_p;
> +
> + interrupt = ACPI_ADD_PTR(struct acpi_aest_node_interrupt, hdr,
> + hdr->node_interrupt_offset);
> +
> + for (i = 0; i < hdr->node_interrupt_count; i++, interrupt++) {
> + trigger = (interrupt->flags & AEST_INTERRUPT_MODE) ?
> + ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
> + if (aest_register_gsi(interrupt->gsiv, trigger, node,
> + aest_irq_func))
> + ret = -EINVAL;
> + }
> +
> + /* Ensure RAS interrupt is enabled */
> + for_each_implemented_record(i, node) {
> + /* 1b: Error record at i index is not implemented */
> + if (test_bit(i, &node->interface.record_implemented))
> + continue;
> +
> + aest_select_record(node, i);
> +
> + regs_p = (u64)&node->interface.regs[i];
> +
> + err_ctlr = node->access->read(regs_p, ERXCTLR);
> +
> + if (interrupt->type == ACPI_AEST_NODE_FAULT_HANDLING)
> + err_ctlr |= ERR_CTLR_FI;
> + if (interrupt->type == ACPI_AEST_NODE_ERROR_RECOVERY)
> + err_ctlr |= ERR_CTLR_UI;
Fault handling interrupts (ERR<n>CTLR.CFI) on corrected errors should also be enabled.
> +
> + node->access->write(regs_p, ERXCTLR, err_ctlr);
> +
> + aest_sync(node);
> + }
> +
> + return ret;
> +}
> +
> +static void __init set_aest_node_name(struct aest_node *node)
> +{
> + switch (node->type) {
> + case ACPI_AEST_PROCESSOR_ERROR_NODE:
> + node->name = kasprintf(GFP_KERNEL, "AEST-CPU%d",
> + node->spec.processor.processor_id);
> + break;
> + case ACPI_AEST_MEMORY_ERROR_NODE:
> + case ACPI_AEST_SMMU_ERROR_NODE:
> + case ACPI_AEST_VENDOR_ERROR_NODE:
> + case ACPI_AEST_GIC_ERROR_NODE:
> + node->name = kasprintf(GFP_KERNEL, "AEST-%llx",
> + node->interface.phy_addr);
> + break;
> + default:
> + node->name = kasprintf(GFP_KERNEL, "AEST-Unkown-Node");
> + }
> +}
> +
> +/* access type is decided by AEST interface type. */
> +static struct aest_access aest_access[] = {
> + [ACPI_AEST_NODE_SYSTEM_REGISTER] = {
> + .read = aest_sysreg_read,
> + .write = aest_sysreg_write,
> + },
> +
> + [ACPI_AEST_NODE_MEMORY_MAPPED] = {
> + .read = aest_iomem_read,
> + .write = aest_iomem_write,
> + },
> + { }
> +};
> +
> +/* In kernel-first mode, kernel will report every CE by default. */
> +static void __init aest_set_ce_threshold(struct aest_node *node)
> +{
> + u64 regs_p, err_fr, err_fr_cec, err_fr_rp, err_misc0, ce_threshold;
> + int i;
> +
> + for_each_implemented_record(i, node) {
> + /* 1b: Error record at i index is not implemented */
> + if (test_bit(i, &node->interface.record_implemented))
> + continue;
> +
> + aest_select_record(node, i);
> + regs_p = (u64)&node->interface.regs[i];
> +
> + err_fr = node->access->read(regs_p, ERXFR);
> + err_fr_cec = FIELD_GET(ERR_FR_CEC, err_fr);
> + err_fr_rp = FIELD_GET(ERR_FR_RP, err_fr);
> + err_misc0 = node->access->read(regs_p, ERXMISC0);
> +
> + if (err_fr_cec == ERR_FR_CEC_0B_COUNTER)
> + pr_debug("%s-%d do not support CE threshold!\n",
> + node->name, i);
Quoted from ARM RAS spec:
If the node implements a corrected error counter or counters, then a corrected error event is defined as
follows:
• A corrected error event occurs when a counter overflows and sets a counter overflow flag to 0b1.
Otherwise, a corrected error event occurs when the error record records an error as a Corrected error.
So, if the node does not support CE threshold, it will report every CE and no ce_threshold should be set.
> + else if (err_fr_cec == ERR_FR_CEC_8B_COUNTER &&
> + err_fr_rp == ERR_FR_RP_SINGLE_COUNTER) {
> + pr_debug("%s-%d support 8 bit CE threshold!\n",
> + node->name, i);
> + ce_threshold = err_misc0 | ERR_MISC0_8B_CEC;
> + } else if (err_fr_cec == ERR_FR_CEC_16B_COUNTER &&
> + err_fr_rp == ERR_FR_RP_SINGLE_COUNTER) {
> + pr_debug("%s-%d support 16 bit CE threshold!\n",
> + node->name, i);
> + ce_threshold = err_misc0 | ERR_MISC0_16B_CEC;
> + } else
> + pr_debug("%s-%d do not support double counter yet!\n",
> + node->name, i);
> +
> + node->access->write(regs_p, ERXMISC0, ce_threshold);
ce_threshold may be uninited in some of above if-else branch.
> + node->interface.ce_threshold[i] = ce_threshold;
> +
> + aest_sync(node);
> + }
> +}
> +
> +static int __init aest_init_interface(struct acpi_aest_hdr *hdr,
> + struct aest_node *node)
> +{
> + struct acpi_aest_node_interface *interface;
> + struct resource *res;
> + int size;
> +
> + interface = ACPI_ADD_PTR(struct acpi_aest_node_interface, hdr,
> + hdr->node_interface_offset);
> +
> + if (interface->type >= ACPI_AEST_XFACE_RESERVED) {
> + pr_err("invalid interface type: %d\n", interface->type);
> + return -EINVAL;
> + }
> +
> + node->interface.type = interface->type;
> + node->interface.phy_addr = interface->address;
> + node->interface.record_start = interface->error_record_index;
> + node->interface.record_end = interface->error_record_index +
> + interface->error_record_count;
Why rename the field name here?
> + node->interface.flags = interface->flags;
> + node->interface.record_implemented = interface->error_record_implemented;
> + node->interface.status_reporting = interface->error_status_reporting;
> + node->interface.addressing_mode = interface->addressing_mode;
> + node->access = &aest_access[interface->type];
> +> +
> +
> + /*
> + * Currently SR based handling is done through the architected
> + * discovery exposed through SRs. That may change in the future
> + * if there is supplemental information in the AEST that is
> + * needed.
> + */
> + if (interface->type == ACPI_AEST_NODE_SYSTEM_REGISTER)
> + return 0;
> +
> + res = kzalloc(sizeof(struct resource), GFP_KERNEL);
> + if (!res)
> + return -ENOMEM;
> +
> + size = interface->error_record_count * sizeof(struct ras_ext_regs);
> + res->name = "AEST";
> + res->start = interface->address;
> + res->end = res->start + size;
> + res->flags = IORESOURCE_MEM;
> +
> + if (insert_resource(&iomem_resource, res)) {
> + pr_notice("request region conflict with %s\n",
> + res->name);
> + }
> +
> + node->interface.regs = ioremap(res->start, size);
> + if (!node->interface.regs) {
> + pr_err("Ioremap for %s failed!\n", node->name);
> + kfree(res);
> + return -EINVAL;
> + }
> +
> + node->interface.ce_threshold = kzalloc(sizeof(u64) *
> + interface->error_record_count, GFP_KERNEL);
> + if (!node->interface.ce_threshold)
> + return -ENOMEM;
> +
> + aest_set_ce_threshold(node);
> +
> + return 0;
> +}
> +
> +static int __init aest_init_common(struct acpi_aest_hdr *hdr,
> + struct aest_node *node)
> +{
> + int ret;
> +
> + set_aest_node_name(node);
> +
> + ret = aest_init_interface(hdr, node);
> + if (ret) {
> + pr_err("failed to init interface\n");
> + return ret;
> + }
> +
> + return aest_init_interrupts(hdr, node);
> +}
> +
> +static int __init aest_init_node_default(struct acpi_aest_hdr *hdr)
> +{
> + struct aest_node *node;
> + union aest_node_spec *node_spec;
> + int ret;
> +
> + node = kzalloc(sizeof(struct aest_node), GFP_KERNEL);
e.g. If the second node is failed to alloced, where you free the first node?
> + if (!node)
> + return -ENOMEM;
> +
> + node->type = hdr->type;
> + node_spec = ACPI_ADD_PTR(union aest_node_spec, hdr,
> + hdr->node_specific_offset);
> +
> + memcpy(&node->spec, node_spec,
> + hdr->node_interface_offset - hdr->node_specific_offset);
> +
> + ret = aest_init_common(hdr, node);
> + if (ret)
> + kfree(node);
> +
> + return ret;
> +}
> +
> +static int __init aest_init_processor_node(struct acpi_aest_hdr *hdr)
> +{
> + struct aest_node *node;
> + union aest_node_spec *node_spec;
> + union aest_node_processor *proc;
> + int ret;
> +
> + node = kzalloc(sizeof(struct aest_node), GFP_KERNEL);
> + if (!node)
> + return -ENOMEM;
> +
> + node->type = hdr->type;
> + node_spec = ACPI_ADD_PTR(union aest_node_spec, hdr,
> + hdr->node_specific_offset);
> +
> + memcpy(&node->spec, node_spec,
> + hdr->node_interface_offset - hdr->node_specific_offset);
> +
> + proc = ACPI_ADD_PTR(union aest_node_processor, node_spec,
> + sizeof(acpi_aest_processor));
> +
> + switch (node->spec.processor.resource_type) {
> + case ACPI_AEST_CACHE_RESOURCE:
> + memcpy(&node->proc, proc,
> + sizeof(struct acpi_aest_processor_cache));
> + break;
> + case ACPI_AEST_TLB_RESOURCE:
> + memcpy(&node->proc, proc,
> + sizeof(struct acpi_aest_processor_tlb));
> + break;
> + case ACPI_AEST_GENERIC_RESOURCE:
> + memcpy(&node->proc, proc,
> + sizeof(struct acpi_aest_processor_generic));
> + break;
> + }
> +
> + ret = aest_init_common(hdr, node);
> + if (ret)
> + kfree(node);
> +
> + return ret;
> +}
> +
> +static int __init aest_init_node(struct acpi_aest_hdr *node)
> +{
> + switch (node->type) {
> + case ACPI_AEST_PROCESSOR_ERROR_NODE:
> + return aest_init_processor_node(node);
> + case ACPI_AEST_MEMORY_ERROR_NODE:
> + case ACPI_AEST_VENDOR_ERROR_NODE:
> + case ACPI_AEST_SMMU_ERROR_NODE:
> + case ACPI_AEST_GIC_ERROR_NODE:
> + return aest_init_node_default(node);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static void __init aest_count_ppi(struct acpi_aest_hdr *header)
> +{
> + struct acpi_aest_node_interrupt *interrupt;
> + int i;
> +
> + interrupt = ACPI_ADD_PTR(struct acpi_aest_node_interrupt, header,
> + header->node_interrupt_offset);
> +
> + for (i = 0; i < header->node_interrupt_count; i++, interrupt++) {
> + if (interrupt->gsiv >= 16 && interrupt->gsiv < 32)
> + num_ppi++;
> + }
> +}
> +
> +static int aest_starting_cpu(unsigned int cpu)
> +{
> + int i;
> +
> + for (i = 0; i < num_ppi; i++)
> + enable_percpu_irq(ppi_irqs[i], IRQ_TYPE_NONE);
> +
> + return 0;
> +}
> +
> +static int aest_dying_cpu(unsigned int cpu)
> +{
> + int i;
> +
> + for (i = 0; i < num_ppi; i++)
> + disable_percpu_irq(ppi_irqs[i]);
> +
> + return 0;
> +}
> +
> +int __init acpi_aest_init(void)
> +{
> + struct acpi_aest_hdr *aest_node, *aest_end;
> + struct acpi_table_aest *aest;
> + int i, ret = 0;
> +
> + if (acpi_disabled)
> + return 0;
> +
> + if (!IS_ENABLED(CONFIG_ARM64_RAS_EXTN))
> + return 0;
> +
> + if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_AEST, 0, &aest_table)))
> + return -EINVAL;
> +
> + ret = aest_node_pool_init();
> + if (ret) {
> + pr_err("Failed init aest node pool.\n");
> + goto fail;
> + }
> +
> + INIT_WORK(&aest_work, aest_node_pool_process);
> +
> + aest = (struct acpi_table_aest *)aest_table;
> +
> + /* Get the first AEST node */
> + aest_node = ACPI_ADD_PTR(struct acpi_aest_hdr, aest,
> + sizeof(struct acpi_table_header));
> + /* Pointer to the end of the AEST table */
> + aest_end = ACPI_ADD_PTR(struct acpi_aest_hdr, aest,
> + aest_table->length);
> +
> + while (aest_node < aest_end) {
> + if (((u64)aest_node + aest_node->length) > (u64)aest_end) {
> + pr_err("AEST node pointer overflow, bad table.\n");
> + return -EINVAL;
> + }
> +
> + aest_count_ppi(aest_node);
> +
> + aest_node = ACPI_ADD_PTR(struct acpi_aest_hdr, aest_node,
> + aest_node->length);
> + }
> +
> + aest_ppi_data = kcalloc(num_ppi, sizeof(struct aest_node_data *),
> + GFP_KERNEL);
> + if (!aest_ppi_data) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + ppi_irqs = kcalloc(num_ppi, sizeof(int), GFP_KERNEL);
> + if (!ppi_irqs) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + for (i = 0; i < num_ppi; i++) {
> + aest_ppi_data[i] = alloc_percpu(struct aest_node);
> + if (!aest_ppi_data[i]) {
> + pr_err("Failed percpu allocation.\n");
> + ret = -ENOMEM;
> + goto fail;
> + }
> + }
> +
> + aest_node = ACPI_ADD_PTR(struct acpi_aest_hdr, aest,
> + sizeof(struct acpi_table_header));
> +
> + while (aest_node < aest_end) {
A macro for_each_aest_node() will be more readable.
> + ret = aest_init_node(aest_node);
> + if (ret) {
> + pr_err("failed to init node: %d", ret);
> + goto fail;
> + }
> +
> + aest_node = ACPI_ADD_PTR(struct acpi_aest_hdr, aest_node,
> + aest_node->length);
> + }
> +
> +
> +
> + return cpuhp_setup_state(CPUHP_AP_ARM_AEST_STARTING,
> + "drivers/acpi/arm64/aest:starting",
> + aest_starting_cpu, aest_dying_cpu);
> +
> +fail:
> + for (i = 0; i < num_ppi; i++)
> + free_percpu(aest_ppi_data[i]);
You should free the aest_ppi_data[0:i], aest_ppi_data[i:num_ppi] are not alloc_percpu().
> + kfree(aest_ppi_data);
> + return ret;
> +}
> +subsys_initcall(acpi_aest_init);
> diff --git a/include/linux/acpi_aest.h b/include/linux/acpi_aest.h
> new file mode 100644
> index 000000000000..679187505dc6
> --- /dev/null
> +++ b/include/linux/acpi_aest.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef AEST_H
> +#define AEST_H
> +
> +#include <acpi/actbl.h>
> +#include <asm/ras.h>
> +
> +#define AEST_INTERRUPT_MODE BIT(0)
> +
> +#define ACPI_AEST_PROC_FLAG_GLOBAL (1<<0)
> +#define ACPI_AEST_PROC_FLAG_SHARED (1<<1)
> +
> +#define ACPI_AEST_INTERFACE_CLEAR_MISC (1<<0)
> +
> +#define ERXFR 0x0
> +#define ERXCTLR 0x8
> +#define ERXSTATUS 0x10
> +#define ERXADDR 0x18
> +#define ERXMISC0 0x20
> +#define ERXMISC1 0x28
> +#define ERXMISC2 0x30
> +#define ERXMISC3 0x38
> +
> +struct aest_node_interface {
> + u8 type;
> + u64 phy_addr;
> + u16 record_start;
> + u16 record_end;
> + u32 flags;
> + unsigned long record_implemented;
> + unsigned long status_reporting;
> + unsigned long addressing_mode;
> + struct ras_ext_regs *regs;
> + u64 *ce_threshold;
> +};
> +
> +union aest_node_processor {
> + struct acpi_aest_processor_cache cache_data;
> + struct acpi_aest_processor_tlb tlb_data;
> + struct acpi_aest_processor_generic generic_data;
> +};
> +
> +union aest_node_spec {
> + struct acpi_aest_processor processor;
> + struct acpi_aest_memory memory;
> + struct acpi_aest_smmu smmu;
> + struct acpi_aest_vendor vendor;
> + struct acpi_aest_gic gic;
> +};
> +
> +struct aest_access {
> + u64 (*read)(u64 base, u32 offset);
> + void (*write)(u64 base, u32 offset, u64 val);
> +};
> +
> +struct aest_node {
> + char *name;
> + u8 type;
> + struct aest_node_interface interface;
> + union aest_node_spec spec;
> + union aest_node_processor proc;
> + struct aest_access *access;
> +};
> +
> +struct aest_node_llist {
> + struct llist_node llnode;
> + char *node_name;
> + int type;
> + /*
> + * Different nodes have different meanings:
> + * - Processor node : processor number.
> + * - Memory node : SRAT proximity domain.
> + * - SMMU node : IORT proximity domain.
> + * - Vendor node : hardware ID.
> + * - GIC node : interface type.
> + */
> + u32 id0;
> + /*
> + * Different nodes have different meanings:
> + * - Processor node : processor resource type.
> + * - Memory node : Non.
> + * - SMMU node : subcomponent reference.
> + * - Vendor node : Unique ID.
> + * - GIC node : instance identifier.
> + */
> + u32 id1;
> + int index;
> + unsigned long addressing_mode;
> + struct ras_ext_regs *regs;
> +};
> +
> +#endif /* AEST_H */
> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
> index 624d4a38c358..f0dda08dbad2 100644
> --- a/include/linux/cpuhotplug.h
> +++ b/include/linux/cpuhotplug.h
> @@ -186,6 +186,7 @@ enum cpuhp_state {
> CPUHP_AP_CSKY_TIMER_STARTING,
> CPUHP_AP_TI_GP_TIMER_STARTING,
> CPUHP_AP_HYPERV_TIMER_STARTING,
> + CPUHP_AP_ARM_AEST_STARTING,
> /* Must be the last timer callback */
> CPUHP_AP_DUMMY_TIMER_STARTING,
> CPUHP_AP_ARM_XEN_STARTING,
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v11 8/8] PCI: endpoint: Remove "core_init_notifier" flag
From: Manivannan Sadhasivam @ 2024-03-27 6:35 UTC (permalink / raw)
To: Jingoo Han, Gustavo Pimentel, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Marek Vasut, Yoshihiro Shimoda, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Vidya Sagar, Vignesh Raghavendra,
Richard Zhu, Lucas Stach, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Minghuan Lian, Mingkai Hu, Roy Zang, Kunihiko Hayashi,
Masami Hiramatsu, Kishon Vijay Abraham I, Jesper Nilsson,
Srikanth Thokala, Shawn Lin, Heiko Stuebner
Cc: linux-pci, linux-kernel, linux-renesas-soc, linux-arm-msm,
linux-tegra, linux-omap, linux-arm-kernel, linuxppc-dev,
Niklas Cassel, linux-arm-kernel, linux-rockchip,
Manivannan Sadhasivam
In-Reply-To: <20240327-pci-dbi-rework-v11-0-6f5259f90673@linaro.org>
"core_init_notifier" flag is set by the glue drivers requiring refclk from
the host to complete the DWC core initialization. Also, those drivers will
send a notification to the EPF drivers once the initialization is fully
completed using the pci_epc_init_notify() API. Only then, the EPF drivers
will start functioning.
For the rest of the drivers generating refclk locally, EPF drivers will
start functioning post binding with them. EPF drivers rely on the
'core_init_notifier' flag to differentiate between the drivers.
Unfortunately, this creates two different flows for the EPF drivers.
So to avoid that, let's get rid of the "core_init_notifier" flag and follow
a single initialization flow for the EPF drivers. This is done by calling
the dw_pcie_ep_init_notify() from all glue drivers after the completion of
dw_pcie_ep_init_registers() API. This will allow all the glue drivers to
send the notification to the EPF drivers once the initialization is fully
completed.
Only difference here is that, the drivers requiring refclk from host will
send the notification once refclk is received, while others will send it
during probe time itself.
But this also requires the EPC core driver to deliver the notification
after EPF driver bind. Because, the glue driver can send the notification
before the EPF drivers bind() and in those cases the EPF drivers will miss
the event. To accommodate this, EPC core is now caching the state of the
EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the
notification to EPF drivers based on that after each EPF driver bind.
Tested-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/pci/controller/cadence/pcie-cadence-ep.c | 2 ++
drivers/pci/controller/dwc/pci-dra7xx.c | 2 ++
drivers/pci/controller/dwc/pci-imx6.c | 2 ++
drivers/pci/controller/dwc/pci-keystone.c | 2 ++
drivers/pci/controller/dwc/pci-layerscape-ep.c | 2 ++
drivers/pci/controller/dwc/pcie-artpec6.c | 2 ++
drivers/pci/controller/dwc/pcie-designware-ep.c | 1 +
drivers/pci/controller/dwc/pcie-designware-plat.c | 2 ++
drivers/pci/controller/dwc/pcie-keembay.c | 2 ++
drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 -
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 ++
drivers/pci/controller/dwc/pcie-tegra194.c | 1 -
drivers/pci/controller/dwc/pcie-uniphier-ep.c | 2 ++
drivers/pci/controller/pcie-rcar-ep.c | 2 ++
drivers/pci/controller/pcie-rockchip-ep.c | 2 ++
drivers/pci/endpoint/functions/pci-epf-test.c | 18 +++++-------------
drivers/pci/endpoint/pci-ep-cfs.c | 9 +++++++++
drivers/pci/endpoint/pci-epc-core.c | 22 ++++++++++++++++++++++
include/linux/pci-epc.h | 7 ++++---
19 files changed, 65 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c
index 2d0a8d78bffb..da67a06ee790 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-ep.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c
@@ -734,6 +734,8 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
spin_lock_init(&ep->lock);
+ dw_pcie_ep_init_notify(&pci->ep);
+
return 0;
free_epc_mem:
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 395042b29ffc..d2d17d37d3e0 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -474,6 +474,8 @@ static int dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
return ret;
}
+ dw_pcie_ep_init_notify(ep);
+
return 0;
}
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index bfcafa440ddb..894b5de76e3a 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1144,6 +1144,8 @@ static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie,
return ret;
}
+ dw_pcie_ep_init_notify(ep);
+
/* Start LTSSM. */
imx6_pcie_ltssm_enable(dev);
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index 093dbb725e41..b7b30470b394 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -1293,6 +1293,8 @@ static int ks_pcie_probe(struct platform_device *pdev)
goto err_ep_init;
}
+ dw_pcie_ep_init_notify(&pci->ep);
+
break;
default:
dev_err(dev, "INVALID device type %d\n", mode);
diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index b712fdd06549..c513598a46d7 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -283,6 +283,8 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
return ret;
}
+ dw_pcie_ep_init_notify(&pci->ep);
+
return ls_pcie_ep_interrupt_init(pcie, pdev);
}
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index a6095561db4a..a4630b92489b 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -452,6 +452,8 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
return ret;
}
+ dw_pcie_ep_init_notify(&pci->ep);
+
break;
default:
dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode);
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index e0c654f242d2..5bfecc629bfc 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -630,6 +630,7 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
dw_pcie_edma_remove(pci);
+ ep->epc->init_complete = false;
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup);
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
index ca9b22e654cd..8490c5d6ff9f 100644
--- a/drivers/pci/controller/dwc/pcie-designware-plat.c
+++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
@@ -154,6 +154,8 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
dw_pcie_ep_deinit(&pci->ep);
}
+ dw_pcie_ep_init_notify(&pci->ep);
+
break;
default:
dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode);
diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
index 250d6acf16dc..9fa9354a5f48 100644
--- a/drivers/pci/controller/dwc/pcie-keembay.c
+++ b/drivers/pci/controller/dwc/pcie-keembay.c
@@ -438,6 +438,8 @@ static int keembay_pcie_probe(struct platform_device *pdev)
return ret;
}
+ dw_pcie_ep_init_notify(&pci->ep);
+
break;
default:
dev_err(dev, "Invalid device type %d\n", pcie->mode);
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 3697b4a944cc..2fb8c15e7a91 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -775,7 +775,6 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
static const struct pci_epc_features qcom_pcie_epc_features = {
.linkup_notifier = true,
- .core_init_notifier = true,
.msi_capable = true,
.msix_capable = false,
.align = SZ_4K,
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index fb7c03639a53..0448928017f3 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -435,6 +435,8 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
rcar_gen4_pcie_ep_deinit(rcar);
}
+ dw_pcie_ep_init_notify(ep);
+
return ret;
}
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 264ee76bf008..e02deb31a72d 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -2006,7 +2006,6 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
static const struct pci_epc_features tegra_pcie_epc_features = {
.linkup_notifier = true,
- .core_init_notifier = true,
.msi_capable = false,
.msix_capable = false,
.reserved_bar = 1 << BAR_2 | 1 << BAR_3 | 1 << BAR_4 | 1 << BAR_5,
diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
index 82ccaea089be..eb1d79fdb1f1 100644
--- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
+++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
@@ -410,6 +410,8 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
return ret;
}
+ dw_pcie_ep_init_notify(&priv->pci.ep);
+
return 0;
}
diff --git a/drivers/pci/controller/pcie-rcar-ep.c b/drivers/pci/controller/pcie-rcar-ep.c
index e6909271def7..45449171d71f 100644
--- a/drivers/pci/controller/pcie-rcar-ep.c
+++ b/drivers/pci/controller/pcie-rcar-ep.c
@@ -538,6 +538,8 @@ static int rcar_pcie_ep_probe(struct platform_device *pdev)
goto err_pm_put;
}
+ dw_pcie_ep_init_notify(&pci->ep);
+
return 0;
err_pm_put:
diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c
index c9046e97a1d2..d3412ce17440 100644
--- a/drivers/pci/controller/pcie-rockchip-ep.c
+++ b/drivers/pci/controller/pcie-rockchip-ep.c
@@ -609,6 +609,8 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev)
rockchip_pcie_write(rockchip, PCIE_CLIENT_CONF_ENABLE,
PCIE_CLIENT_CONFIG);
+ dw_pcie_ep_init_notify(&pci->ep);
+
return 0;
err_epc_mem_exit:
pci_epc_mem_exit(epc);
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 18c80002d3bd..fc0282b0d626 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -753,6 +753,7 @@ static int pci_epf_test_core_init(struct pci_epf *epf)
const struct pci_epc_features *epc_features;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
+ bool linkup_notifier = false;
bool msix_capable = false;
bool msi_capable = true;
int ret;
@@ -795,6 +796,10 @@ static int pci_epf_test_core_init(struct pci_epf *epf)
}
}
+ linkup_notifier = epc_features->linkup_notifier;
+ if (!linkup_notifier)
+ queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
+
return 0;
}
@@ -901,8 +906,6 @@ static int pci_epf_test_bind(struct pci_epf *epf)
const struct pci_epc_features *epc_features;
enum pci_barno test_reg_bar = BAR_0;
struct pci_epc *epc = epf->epc;
- bool linkup_notifier = false;
- bool core_init_notifier = false;
if (WARN_ON_ONCE(!epc))
return -EINVAL;
@@ -913,8 +916,6 @@ static int pci_epf_test_bind(struct pci_epf *epf)
return -EOPNOTSUPP;
}
- linkup_notifier = epc_features->linkup_notifier;
- core_init_notifier = epc_features->core_init_notifier;
test_reg_bar = pci_epc_get_first_free_bar(epc_features);
if (test_reg_bar < 0)
return -EINVAL;
@@ -927,21 +928,12 @@ static int pci_epf_test_bind(struct pci_epf *epf)
if (ret)
return ret;
- if (!core_init_notifier) {
- ret = pci_epf_test_core_init(epf);
- if (ret)
- return ret;
- }
-
epf_test->dma_supported = true;
ret = pci_epf_test_init_dma_chan(epf_test);
if (ret)
epf_test->dma_supported = false;
- if (!linkup_notifier && !core_init_notifier)
- queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
-
return 0;
}
diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c
index 0ea64e24ed61..3b21e28f9b59 100644
--- a/drivers/pci/endpoint/pci-ep-cfs.c
+++ b/drivers/pci/endpoint/pci-ep-cfs.c
@@ -64,6 +64,9 @@ static int pci_secondary_epc_epf_link(struct config_item *epf_item,
return ret;
}
+ /* Send any pending EPC initialization complete to the EPF driver */
+ pci_epc_notify_pending_init(epc, epf);
+
return 0;
}
@@ -125,6 +128,9 @@ static int pci_primary_epc_epf_link(struct config_item *epf_item,
return ret;
}
+ /* Send any pending EPC initialization complete to the EPF driver */
+ pci_epc_notify_pending_init(epc, epf);
+
return 0;
}
@@ -230,6 +236,9 @@ static int pci_epc_epf_link(struct config_item *epc_item,
return ret;
}
+ /* Send any pending EPC initialization complete to the EPF driver */
+ pci_epc_notify_pending_init(epc, epf);
+
return 0;
}
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index dcd4e66430c1..ba2ff037dfa6 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -753,10 +753,32 @@ void pci_epc_init_notify(struct pci_epc *epc)
epf->event_ops->core_init(epf);
mutex_unlock(&epf->lock);
}
+ epc->init_complete = true;
mutex_unlock(&epc->list_lock);
}
EXPORT_SYMBOL_GPL(pci_epc_init_notify);
+/**
+ * pci_epc_notify_pending_init() - Notify the pending EPC device initialization
+ * complete to the EPF device
+ * @epc: the EPC device whose core initialization is pending to be notified
+ * @epf: the EPF device to be notified
+ *
+ * Invoke to notify the pending EPC device initialization complete to the EPF
+ * device. This is used to deliver the notification if the EPC initialization
+ * got completed before the EPF driver bind.
+ */
+void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf)
+{
+ if (epc->init_complete) {
+ mutex_lock(&epf->lock);
+ if (epf->event_ops && epf->event_ops->core_init)
+ epf->event_ops->core_init(epf);
+ mutex_unlock(&epf->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(pci_epc_notify_pending_init);
+
/**
* pci_epc_bme_notify() - Notify the EPF device that the EPC device has received
* the BME event from the Root complex
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 40ea18f5aa02..adee6dbe4e45 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -128,6 +128,8 @@ struct pci_epc_mem {
* @group: configfs group representing the PCI EPC device
* @lock: mutex to protect pci_epc ops
* @function_num_map: bitmap to manage physical function number
+ * @init_complete: flag to indicate whether the EPC initialization is complete
+ * or not
*/
struct pci_epc {
struct device dev;
@@ -143,13 +145,12 @@ struct pci_epc {
/* mutex to protect against concurrent access of EP controller */
struct mutex lock;
unsigned long function_num_map;
+ bool init_complete;
};
/**
* struct pci_epc_features - features supported by a EPC device per function
* @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
- * @core_init_notifier: indicate cores that can notify about their availability
- * for initialization
* @msi_capable: indicate if the endpoint function has MSI capability
* @msix_capable: indicate if the endpoint function has MSI-X capability
* @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver
@@ -159,7 +160,6 @@ struct pci_epc {
*/
struct pci_epc_features {
unsigned int linkup_notifier : 1;
- unsigned int core_init_notifier : 1;
unsigned int msi_capable : 1;
unsigned int msix_capable : 1;
u8 reserved_bar;
@@ -198,6 +198,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
void pci_epc_linkup(struct pci_epc *epc);
void pci_epc_linkdown(struct pci_epc *epc);
void pci_epc_init_notify(struct pci_epc *epc);
+void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf);
void pci_epc_bme_notify(struct pci_epc *epc);
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type);
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v11 7/8] PCI: dwc: ep: Call dw_pcie_ep_init_registers() API directly from all glue drivers
From: Manivannan Sadhasivam @ 2024-03-27 6:35 UTC (permalink / raw)
To: Jingoo Han, Gustavo Pimentel, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Marek Vasut, Yoshihiro Shimoda, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Vidya Sagar, Vignesh Raghavendra,
Richard Zhu, Lucas Stach, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Minghuan Lian, Mingkai Hu, Roy Zang, Kunihiko Hayashi,
Masami Hiramatsu, Kishon Vijay Abraham I, Jesper Nilsson,
Srikanth Thokala, Shawn Lin, Heiko Stuebner
Cc: linux-pci, linux-kernel, linux-renesas-soc, linux-arm-msm,
linux-tegra, linux-omap, linux-arm-kernel, linuxppc-dev,
Niklas Cassel, linux-arm-kernel, linux-rockchip,
Manivannan Sadhasivam
In-Reply-To: <20240327-pci-dbi-rework-v11-0-6f5259f90673@linaro.org>
Currently, dw_pcie_ep_init_registers() API is directly called by the glue
drivers requiring active refclk from host. But for the other drivers, it is
getting called implicitly by dw_pcie_ep_init(). This is due to the fact
that this API initializes DWC EP specific registers and that requires an
active refclk (either from host or generated locally by endpoint itsef).
But, this causes a discrepancy among the glue drivers. So to avoid this
confusion, let's call this API directly from all glue drivers irrespective
of refclk dependency. Only difference here is that the drivers requiring
refclk from host will call this API only after the refclk is received and
other drivers without refclk dependency will call this API right after
dw_pcie_ep_init().
With this change, the check for 'core_init_notifier' flag can now be
dropped from dw_pcie_ep_init() API. This will also allow us to remove the
'core_init_notifier' flag completely in the later commits.
Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/pci/controller/dwc/pci-dra7xx.c | 7 +++++++
drivers/pci/controller/dwc/pci-imx6.c | 8 ++++++++
drivers/pci/controller/dwc/pci-keystone.c | 9 +++++++++
drivers/pci/controller/dwc/pci-layerscape-ep.c | 7 +++++++
drivers/pci/controller/dwc/pcie-artpec6.c | 13 ++++++++++++-
drivers/pci/controller/dwc/pcie-designware-ep.c | 22 ----------------------
drivers/pci/controller/dwc/pcie-designware-plat.c | 9 +++++++++
drivers/pci/controller/dwc/pcie-keembay.c | 16 +++++++++++++++-
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 12 +++++++++++-
drivers/pci/controller/dwc/pcie-uniphier-ep.c | 13 ++++++++++++-
10 files changed, 90 insertions(+), 26 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 0e406677060d..395042b29ffc 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -467,6 +467,13 @@ static int dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
return ret;
}
+ ret = dw_pcie_ep_init_registers(ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(ep);
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index dc2c036ab28c..bfcafa440ddb 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1136,6 +1136,14 @@ static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie,
dev_err(dev, "failed to initialize endpoint\n");
return ret;
}
+
+ ret = dw_pcie_ep_init_registers(ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(ep);
+ return ret;
+ }
+
/* Start LTSSM. */
imx6_pcie_ltssm_enable(dev);
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index c0c62533a3f1..093dbb725e41 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -1286,6 +1286,13 @@ static int ks_pcie_probe(struct platform_device *pdev)
ret = dw_pcie_ep_init(&pci->ep);
if (ret < 0)
goto err_get_sync;
+
+ ret = dw_pcie_ep_init_registers(&pci->ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ goto err_ep_init;
+ }
+
break;
default:
dev_err(dev, "INVALID device type %d\n", mode);
@@ -1295,6 +1302,8 @@ static int ks_pcie_probe(struct platform_device *pdev)
return 0;
+err_ep_init:
+ dw_pcie_ep_deinit(&pci->ep);
err_get_sync:
pm_runtime_put(dev);
pm_runtime_disable(dev);
diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index 2e398494e7c0..b712fdd06549 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -276,6 +276,13 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = dw_pcie_ep_init_registers(&pci->ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(&pci->ep);
+ return ret;
+ }
+
return ls_pcie_ep_interrupt_init(pcie, pdev);
}
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index 9ed0a9ba7619..a6095561db4a 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -441,7 +441,18 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
pci->ep.ops = &pcie_ep_ops;
- return dw_pcie_ep_init(&pci->ep);
+ ret = dw_pcie_ep_init(&pci->ep);
+ if (ret)
+ return ret;
+
+ ret = dw_pcie_ep_init_registers(&pci->ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(&pci->ep);
+ return ret;
+ }
+
+ break;
default:
dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode);
}
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 0c13fdb8554e..e0c654f242d2 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -814,7 +814,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
struct device *dev = pci->dev;
struct platform_device *pdev = to_platform_device(dev);
struct device_node *np = dev->of_node;
- const struct pci_epc_features *epc_features;
INIT_LIST_HEAD(&ep->func_list);
@@ -860,29 +859,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
goto err_exit_epc_mem;
}
- if (ep->ops->get_features) {
- epc_features = ep->ops->get_features(ep);
- if (epc_features->core_init_notifier)
- return 0;
- }
-
- /*
- * NOTE:- Avoid accessing the hardware (Ex:- DBI space) before this
- * step as platforms that implement 'core_init_notifier' feature may
- * not have the hardware ready (i.e. core initialized) for access
- * (Ex: tegra194). Any hardware access on such platforms result
- * in system hang.
- */
- ret = dw_pcie_ep_init_registers(ep);
- if (ret)
- goto err_free_epc_mem;
-
return 0;
-err_free_epc_mem:
- pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
- epc->mem->window.page_size);
-
err_exit_epc_mem:
pci_epc_mem_exit(epc);
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
index 778588b4be70..ca9b22e654cd 100644
--- a/drivers/pci/controller/dwc/pcie-designware-plat.c
+++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
@@ -145,6 +145,15 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
pci->ep.ops = &pcie_ep_ops;
ret = dw_pcie_ep_init(&pci->ep);
+ if (ret)
+ return ret;
+
+ ret = dw_pcie_ep_init_registers(&pci->ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(&pci->ep);
+ }
+
break;
default:
dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode);
diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
index 208d3b0ba196..250d6acf16dc 100644
--- a/drivers/pci/controller/dwc/pcie-keembay.c
+++ b/drivers/pci/controller/dwc/pcie-keembay.c
@@ -392,6 +392,7 @@ static int keembay_pcie_probe(struct platform_device *pdev)
struct keembay_pcie *pcie;
struct dw_pcie *pci;
enum dw_pcie_device_mode mode;
+ int ret;
data = device_get_match_data(dev);
if (!data)
@@ -426,11 +427,24 @@ static int keembay_pcie_probe(struct platform_device *pdev)
return -ENODEV;
pci->ep.ops = &keembay_pcie_ep_ops;
- return dw_pcie_ep_init(&pci->ep);
+ ret = dw_pcie_ep_init(&pci->ep);
+ if (ret)
+ return ret;
+
+ ret = dw_pcie_ep_init_registers(&pci->ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(&pci->ep);
+ return ret;
+ }
+
+ break;
default:
dev_err(dev, "Invalid device type %d\n", pcie->mode);
return -ENODEV;
}
+
+ return 0;
}
static const struct keembay_pcie_of_data keembay_pcie_rc_of_data = {
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 9d9d22e367bb..fb7c03639a53 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -414,6 +414,7 @@ static const struct dw_pcie_ep_ops pcie_ep_ops = {
static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie_ep *ep = &rcar->dw.ep;
+ struct device *dev = rcar->dw.dev;
int ret;
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_EP))
@@ -422,8 +423,17 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
ep->ops = &pcie_ep_ops;
ret = dw_pcie_ep_init(ep);
- if (ret)
+ if (ret) {
rcar_gen4_pcie_ep_deinit(rcar);
+ return ret;
+ }
+
+ ret = dw_pcie_ep_init_registers(ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(ep);
+ rcar_gen4_pcie_ep_deinit(rcar);
+ }
return ret;
}
diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
index 3fced0d3e851..82ccaea089be 100644
--- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
+++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
@@ -399,7 +399,18 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev)
return ret;
priv->pci.ep.ops = &uniphier_pcie_ep_ops;
- return dw_pcie_ep_init(&priv->pci.ep);
+ ret = dw_pcie_ep_init(&priv->pci.ep);
+ if (ret)
+ return ret;
+
+ ret = dw_pcie_ep_init_registers(&priv->pci.ep);
+ if (ret) {
+ dev_err(dev, "Failed to initialize DWC endpoint registers\n");
+ dw_pcie_ep_deinit(&priv->pci.ep);
+ return ret;
+ }
+
+ return 0;
}
static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v11 6/8] PCI: dwc: ep: Rename dw_pcie_ep_init_complete() to dw_pcie_ep_init_registers()
From: Manivannan Sadhasivam @ 2024-03-27 6:35 UTC (permalink / raw)
To: Jingoo Han, Gustavo Pimentel, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Marek Vasut, Yoshihiro Shimoda, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Vidya Sagar, Vignesh Raghavendra,
Richard Zhu, Lucas Stach, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Minghuan Lian, Mingkai Hu, Roy Zang, Kunihiko Hayashi,
Masami Hiramatsu, Kishon Vijay Abraham I, Jesper Nilsson,
Srikanth Thokala, Shawn Lin, Heiko Stuebner
Cc: linux-pci, linux-kernel, linux-renesas-soc, linux-arm-msm,
linux-tegra, linux-omap, linux-arm-kernel, linuxppc-dev,
Niklas Cassel, linux-arm-kernel, linux-rockchip,
Manivannan Sadhasivam, Frank Li
In-Reply-To: <20240327-pci-dbi-rework-v11-0-6f5259f90673@linaro.org>
The goal of the dw_pcie_ep_init_complete() API is to initialize the DWC
specific registers post registering the controller with the EP framework.
But the naming doesn't reflect its functionality and causes confusion. So,
let's rename it to dw_pcie_ep_init_registers() to make it clear that it
initializes the DWC specific registers.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 14 +++++++-------
drivers/pci/controller/dwc/pcie-designware.h | 4 ++--
drivers/pci/controller/dwc/pcie-qcom-ep.c | 2 +-
drivers/pci/controller/dwc/pcie-tegra194.c | 2 +-
4 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7509fa1d1e7d..0c13fdb8554e 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -672,14 +672,14 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
}
/**
- * dw_pcie_ep_init_complete - Complete DWC EP initialization
+ * dw_pcie_ep_init_registers - Initialize DWC EP specific registers
* @ep: DWC EP device
*
- * Complete the initialization of the registers (CSRs) specific to DWC EP. This
- * API should be called only when the endpoint receives an active refclk (either
- * from host or generated locally).
+ * Initialize the registers (CSRs) specific to DWC EP. This API should be called
+ * only when the endpoint receives an active refclk (either from host or
+ * generated locally).
*/
-int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
+int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct dw_pcie_ep_func *ep_func;
@@ -794,7 +794,7 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
return ret;
}
-EXPORT_SYMBOL_GPL(dw_pcie_ep_init_complete);
+EXPORT_SYMBOL_GPL(dw_pcie_ep_init_registers);
/**
* dw_pcie_ep_init - Initialize the endpoint device
@@ -873,7 +873,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
* (Ex: tegra194). Any hardware access on such platforms result
* in system hang.
*/
- ret = dw_pcie_ep_init_complete(ep);
+ ret = dw_pcie_ep_init_registers(ep);
if (ret)
goto err_free_epc_mem;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 351d2fe3ea4d..f8e5431a207b 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -669,7 +669,7 @@ static inline void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus,
#ifdef CONFIG_PCIE_DW_EP
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
int dw_pcie_ep_init(struct dw_pcie_ep *ep);
-int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep);
+int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep);
void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep);
void dw_pcie_ep_deinit(struct dw_pcie_ep *ep);
void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep);
@@ -693,7 +693,7 @@ static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep)
return 0;
}
-static inline int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
+static inline int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
{
return 0;
}
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 59b1c0110288..3697b4a944cc 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -463,7 +463,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
PARF_INT_ALL_LINK_UP | PARF_INT_ALL_EDMA;
writel_relaxed(val, pcie_ep->parf + PARF_INT_ALL_MASK);
- ret = dw_pcie_ep_init_complete(&pcie_ep->pci.ep);
+ ret = dw_pcie_ep_init_registers(&pcie_ep->pci.ep);
if (ret) {
dev_err(dev, "Failed to complete initialization: %d\n", ret);
goto err_disable_resources;
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 68bfeed3429b..264ee76bf008 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1897,7 +1897,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
val = (upper_32_bits(ep->msi_mem_phys) & MSIX_ADDR_MATCH_HIGH_OFF_MASK);
dw_pcie_writel_dbi(pci, MSIX_ADDR_MATCH_HIGH_OFF, val);
- ret = dw_pcie_ep_init_complete(ep);
+ ret = dw_pcie_ep_init_registers(ep);
if (ret) {
dev_err(dev, "Failed to complete initialization: %d\n", ret);
goto fail_init_complete;
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v11 5/8] PCI: dwc: ep: Introduce dw_pcie_ep_cleanup() API for drivers supporting PERST#
From: Manivannan Sadhasivam @ 2024-03-27 6:35 UTC (permalink / raw)
To: Jingoo Han, Gustavo Pimentel, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Marek Vasut, Yoshihiro Shimoda, Thierry Reding, Jonathan Hunter,
Kishon Vijay Abraham I, Vidya Sagar, Vignesh Raghavendra,
Richard Zhu, Lucas Stach, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Minghuan Lian, Mingkai Hu, Roy Zang, Kunihiko Hayashi,
Masami Hiramatsu, Kishon Vijay Abraham I, Jesper Nilsson,
Srikanth Thokala, Shawn Lin, Heiko Stuebner
Cc: linux-pci, linux-kernel, linux-renesas-soc, linux-arm-msm,
linux-tegra, linux-omap, linux-arm-kernel, linuxppc-dev,
Niklas Cassel, linux-arm-kernel, linux-rockchip,
Manivannan Sadhasivam, Frank Li
In-Reply-To: <20240327-pci-dbi-rework-v11-0-6f5259f90673@linaro.org>
For DWC glue drivers supporting PERST# (currently Qcom and Tegra194), some
of the DWC resources like eDMA should be cleaned up during the PERST#
assert time.
So let's introduce a dw_pcie_ep_cleanup() API that could be called by these
drivers to cleanup the DWC specific resources. Currently, it just removes
eDMA.
Reported-by: Niklas Cassel <cassel@kernel.org>
Closes: https://lore.kernel.org/linux-pci/ZWYmX8Y%2F7Q9WMxES@x1-carbon
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 19 +++++++++++++++++--
drivers/pci/controller/dwc/pcie-designware.h | 5 +++++
drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 +
drivers/pci/controller/dwc/pcie-tegra194.c | 2 ++
4 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7ff2015736ef..7509fa1d1e7d 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -617,6 +617,22 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
+/**
+ * dw_pcie_ep_cleanup - Cleanup DWC EP resources after fundamental reset
+ * @ep: DWC EP device
+ *
+ * Cleans up the DWC EP specific resources like eDMA etc... after fundamental
+ * reset like PERST#. Note that this API is only applicable for drivers
+ * supporting PERST# or any other methods of fundamental reset.
+ */
+void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ dw_pcie_edma_remove(pci);
+}
+EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup);
+
/**
* dw_pcie_ep_deinit - Deinitialize the endpoint device
* @ep: DWC EP device
@@ -626,10 +642,9 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
*/
void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
{
- struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct pci_epc *epc = ep->epc;
- dw_pcie_edma_remove(pci);
+ dw_pcie_ep_cleanup(ep);
pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
epc->mem->window.page_size);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 61465203bb60..351d2fe3ea4d 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -672,6 +672,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep);
int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep);
void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep);
void dw_pcie_ep_deinit(struct dw_pcie_ep *ep);
+void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep);
int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no);
int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
u8 interrupt_num);
@@ -705,6 +706,10 @@ static inline void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
{
}
+static inline void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
+{
+}
+
static inline int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no)
{
return 0;
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 36e5e80cd22f..59b1c0110288 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -507,6 +507,7 @@ static void qcom_pcie_perst_assert(struct dw_pcie *pci)
return;
}
+ dw_pcie_ep_cleanup(&pci->ep);
qcom_pcie_disable_resources(pcie_ep);
pcie_ep->link_status = QCOM_PCIE_EP_LINK_DISABLED;
}
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 7afa9e9aabe2..68bfeed3429b 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1715,6 +1715,8 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)
if (ret)
dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret);
+ dw_pcie_ep_cleanup(&pcie->pci.ep);
+
reset_control_assert(pcie->core_rst);
tegra_pcie_disable_phy(pcie);
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox