Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/9] crypto: Add allwinner subdirectory
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

Since a second Allwinner crypto driver will be added, it is better to
create a dedicated subdirectory.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 MAINTAINERS                      | 6 ++++++
 drivers/crypto/Kconfig           | 2 ++
 drivers/crypto/Makefile          | 1 +
 drivers/crypto/allwinner/Kconfig | 6 ++++++
 4 files changed, 15 insertions(+)
 create mode 100644 drivers/crypto/allwinner/Kconfig

diff --git a/MAINTAINERS b/MAINTAINERS
index ee4e873c0f9a..d62ddf8ff262 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -681,6 +681,12 @@ L:	linux-crypto@vger.kernel.org
 S:	Maintained
 F:	drivers/crypto/sunxi-ss/
 
+ALLWINNER CRYPTO ENGINE
+M:	Corentin Labbe <clabbe.montjoie@gmail.com>
+L:	linux-crypto@vger.kernel.org
+S:	Maintained
+F:	drivers/crypto/allwinner/
+
 ALLWINNER VPU DRIVER
 M:	Maxime Ripard <mripard@kernel.org>
 M:	Paul Kocialkowski <paul.kocialkowski@bootlin.com>
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 83271d944a96..fe90dd74797c 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -11,6 +11,8 @@ menuconfig CRYPTO_HW
 
 if CRYPTO_HW
 
+source "drivers/crypto/allwinner/Kconfig"
+
 config CRYPTO_DEV_PADLOCK
 	tristate "Support for VIA PadLock ACE"
 	depends on X86 && !UML
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index afc4753b5d28..90d60eff5ecc 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CRYPTO_DEV_ALLWINNER) += allwinner/
 obj-$(CONFIG_CRYPTO_DEV_ATMEL_AES) += atmel-aes.o
 obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += atmel-sha.o
 obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o
diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig
new file mode 100644
index 000000000000..0c8a99f7959d
--- /dev/null
+++ b/drivers/crypto/allwinner/Kconfig
@@ -0,0 +1,6 @@
+config CRYPTO_DEV_ALLWINNER
+	bool "Support for Allwinner cryptographic offloader"
+	depends on ARCH_SUNXI || COMPILE_TEST
+	default y if ARCH_SUNXI
+	help
+	  Say Y here to get to see options for Allwinner hardware crypto devices
-- 
2.21.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 3/9] dt-bindings: crypto: Add DT bindings documentation for sun8i-ce Crypto Engine
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

This patch adds documentation for Device-Tree bindings for the
Crypto Engine cryptographic accelerator driver.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 .../bindings/crypto/allwinner,sun8i-ce.yaml   | 84 +++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml

diff --git a/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml b/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml
new file mode 100644
index 000000000000..bd8ccedd6059
--- /dev/null
+++ b/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/crypto/allwinner,sun8i-ce.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner Crypto Engine driver
+
+maintainers:
+  - Corentin Labbe <clabbe@baylibre.com>
+
+properties:
+  compatible:
+    oneOf:
+      - const: allwinner,sun8i-h3-crypto
+      - const: allwinner,sun8i-r40-crypto
+      - const: allwinner,sun50i-a64-crypto
+      - const: allwinner,sun50i-h5-crypto
+      - const: allwinner,sun50i-h6-crypto
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+if:
+  properties:
+    compatible:
+      contains:
+        const: allwinner,sun50i-h6-crypto
+then:
+  clocks:
+    items:
+      - description: Bus clock
+      - description: Module clock
+      - description: MBus clock
+
+  clock-names:
+    items:
+      - const: ahb
+      - const: mod
+      - const: mbus
+else:
+  clocks:
+    items:
+      - description: Bus clock
+      - description: Module clock
+
+  clock-names:
+    items:
+      - const: ahb
+      - const: mod
+
+  resets:
+    maxItems: 1
+
+  reset-names:
+    const: ahb
+
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sun50i-a64-ccu.h>
+    #include <dt-bindings/reset/sun50i-a64-ccu.h>
+
+    crypto: crypto@1c15000 {
+      compatible = "allwinner,sun8i-h3-crypto";
+      reg = <0x01c15000 0x1000>;
+      interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>;
+      clock-names = "ahb", "mod";
+      resets = <&ccu RST_BUS_CE>;
+      reset-names = "ahb";
+    };
+
-- 
2.21.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 2/9] crypto: Add Allwinner sun8i-ce Crypto Engine
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

The Crypto Engine is an hardware cryptographic offloader present
on all recent Allwinner SoCs H2+, H3, R40, A64, H5, H6

This driver supports AES cipher in CBC/ECB mode.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 drivers/crypto/allwinner/Kconfig              |  26 +
 drivers/crypto/allwinner/Makefile             |   1 +
 drivers/crypto/allwinner/sun8i-ce/Makefile    |   2 +
 .../allwinner/sun8i-ce/sun8i-ce-cipher.c      | 390 +++++++++++
 .../crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 630 ++++++++++++++++++
 drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h  | 256 +++++++
 6 files changed, 1305 insertions(+)
 create mode 100644 drivers/crypto/allwinner/Makefile
 create mode 100644 drivers/crypto/allwinner/sun8i-ce/Makefile
 create mode 100644 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c
 create mode 100644 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
 create mode 100644 drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h

diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig
index 0c8a99f7959d..742a08b64c90 100644
--- a/drivers/crypto/allwinner/Kconfig
+++ b/drivers/crypto/allwinner/Kconfig
@@ -4,3 +4,29 @@ config CRYPTO_DEV_ALLWINNER
 	default y if ARCH_SUNXI
 	help
 	  Say Y here to get to see options for Allwinner hardware crypto devices
+
+config CRYPTO_DEV_SUN8I_CE
+	tristate "Support for Allwinner Crypto Engine cryptographic offloader"
+	select CRYPTO_BLKCIPHER
+	select CRYPTO_ENGINE
+	select CRYPTO_ECB
+	select CRYPTO_CBC
+	select CRYPTO_AES
+	select CRYPTO_DES
+	depends on CRYPTO_DEV_ALLWINNER
+	help
+	  Select y here for having support for the crypto Engine availlable on
+	  Allwinner SoC H2+, H3, H5, H6, R40 and A64.
+	  The Crypto Engine handle AES/3DES ciphers in ECB/CBC mode.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called sun8i-ce.
+
+config CRYPTO_DEV_SUN8I_CE_DEBUG
+	bool "Enabled sun8i-ce stats"
+	depends on CRYPTO_DEV_SUN8I_CE
+	depends on DEBUG_FS
+	help
+	  Say y to enabled sun8i-ce debug stats.
+	  This will create /sys/kernel/debug/sun8i-ce/stats for displaying
+	  the number of requests per flow and per algorithm.
diff --git a/drivers/crypto/allwinner/Makefile b/drivers/crypto/allwinner/Makefile
new file mode 100644
index 000000000000..11f02db9ee06
--- /dev/null
+++ b/drivers/crypto/allwinner/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CRYPTO_DEV_SUN8I_CE) += sun8i-ce/
diff --git a/drivers/crypto/allwinner/sun8i-ce/Makefile b/drivers/crypto/allwinner/sun8i-ce/Makefile
new file mode 100644
index 000000000000..08b68c3c1ca9
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_SUN8I_CE) += sun8i-ce.o
+sun8i-ce-y += sun8i-ce-core.o sun8i-ce-cipher.o
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c
new file mode 100644
index 000000000000..c22f0592f168
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sun8i-ce-cipher.c - hardware cryptographic offloader for
+ * Allwinner H3/A64/H5/H2+/H6/R40 SoC
+ *
+ * Copyright (C) 2016-2019 Corentin LABBE <clabbe.montjoie@gmail.com>
+ *
+ * This file add support for AES cipher with 128,192,256 bits keysize in
+ * CBC and ECB mode.
+ *
+ * You could find a link for the datasheet in Documentation/arm/sunxi/README
+ */
+
+#include <linux/crypto.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/des.h>
+#include <crypto/internal/skcipher.h>
+#include "sun8i-ce.h"
+
+static int sun8i_ce_cipher(struct skcipher_request *areq)
+{
+	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
+	struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);
+	struct sun8i_ce_dev *ce = op->ce;
+	struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);
+	struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
+	struct sun8i_ce_alg_template *algt;
+	struct sun8i_ce_flow *chan;
+	struct ce_task *cet;
+	struct scatterlist *sg;
+	bool need_fallback = false;
+	unsigned int todo, len, offset, ivsize;
+	void *backup_iv = NULL;
+	int flow, i;
+	int nr_sgs = 0;
+	int nr_sgd = 0;
+	int err = 0;
+
+	algt = container_of(alg, struct sun8i_ce_alg_template, alg.skcipher);
+
+	dev_dbg(ce->dev, "%s %s %u %x IV(%p %u) key=%u\n", __func__,
+		crypto_tfm_alg_name(areq->base.tfm),
+		areq->cryptlen,
+		rctx->op_dir, areq->iv, crypto_skcipher_ivsize(tfm),
+		op->keylen);
+
+	if (sg_nents(areq->src) > MAX_SG || sg_nents(areq->dst) > MAX_SG)
+		need_fallback = true;
+
+	if (areq->cryptlen < crypto_skcipher_ivsize(tfm))
+		need_fallback = true;
+
+	if (areq->cryptlen == 0)
+		need_fallback = true;
+	if (areq->cryptlen % algt->alg.skcipher.base.cra_blocksize)
+		need_fallback = true;
+
+	sg = areq->src;
+	while (sg && !need_fallback) {
+		if (sg->length % 4 || !IS_ALIGNED(sg->offset, sizeof(u32))) {
+			need_fallback = true;
+			break;
+		}
+		sg = sg_next(sg);
+	}
+	sg = areq->dst;
+	while (sg && !need_fallback) {
+		if (sg->length % 4 || !IS_ALIGNED(sg->offset, sizeof(u32))) {
+			need_fallback = true;
+			break;
+		}
+		sg = sg_next(sg);
+	}
+
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	algt->stat_req++;
+#endif
+
+	if (need_fallback) {
+		SYNC_SKCIPHER_REQUEST_ON_STACK(subreq, op->fallback_tfm);
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+		algt->stat_fb++;
+#endif
+		skcipher_request_set_sync_tfm(subreq, op->fallback_tfm);
+		skcipher_request_set_callback(subreq, areq->base.flags, NULL,
+					      NULL);
+		skcipher_request_set_crypt(subreq, areq->src, areq->dst,
+					   areq->cryptlen, areq->iv);
+		if (rctx->op_dir & CE_DECRYPTION)
+			err = crypto_skcipher_decrypt(subreq);
+		else
+			err = crypto_skcipher_encrypt(subreq);
+		skcipher_request_zero(subreq);
+		return err;
+	}
+
+	flow = rctx->flow;
+
+	chan = &ce->chanlist[flow];
+	mutex_lock(&chan->lock);
+
+	cet = chan->tl;
+	memset(cet, 0, sizeof(struct ce_task));
+
+	cet->t_id = flow;
+	cet->t_common_ctl = ce->variant->alg_cipher[algt->ce_algo_id];
+	cet->t_common_ctl |= rctx->op_dir | CE_COMM_INT;
+	cet->t_dlen = areq->cryptlen / 4;
+	/* CTS and recent CE (H6) need length in bytes, in word otherwise */
+	if (ce->variant->model == CE_v2)
+		cet->t_dlen = areq->cryptlen;
+
+	cet->t_sym_ctl = ce->variant->op_mode[algt->ce_blockmode];
+	len = op->keylen;
+	switch (len) {
+	case 128 / 8:
+		cet->t_sym_ctl |= CE_AES_128BITS;
+		break;
+	case 192 / 8:
+		cet->t_sym_ctl |= CE_AES_192BITS;
+		break;
+	case 256 / 8:
+		cet->t_sym_ctl |= CE_AES_256BITS;
+		break;
+	}
+
+	cet->t_asym_ctl = 0;
+
+	chan->op_mode = ce->variant->op_mode[algt->ce_blockmode];
+	chan->op_dir = rctx->op_dir;
+	chan->method = ce->variant->alg_cipher[algt->ce_algo_id];
+	chan->keylen = op->keylen;
+
+	cet->t_key = dma_map_single(ce->dev, op->key, op->keylen,
+				    DMA_TO_DEVICE);
+	if (dma_mapping_error(ce->dev, cet->t_key)) {
+		dev_err(ce->dev, "Cannot DMA MAP KEY\n");
+		err = -EFAULT;
+		goto theend;
+	}
+
+	ivsize = crypto_skcipher_ivsize(tfm);
+	if (areq->iv && crypto_skcipher_ivsize(tfm) > 0) {
+		chan->ivlen = ivsize;
+		chan->bounce_iv = kzalloc(ivsize, GFP_KERNEL | GFP_DMA);
+		if (!chan->bounce_iv) {
+			err = -ENOMEM;
+			goto theend_key;
+		}
+		if (rctx->op_dir & CE_DECRYPTION) {
+			backup_iv = kzalloc(ivsize, GFP_KERNEL);
+			if (!backup_iv) {
+				err = -ENOMEM;
+				goto theend_key;
+			}
+			offset = areq->cryptlen - ivsize;
+			scatterwalk_map_and_copy(backup_iv, areq->src, offset,
+						 ivsize, 0);
+		}
+		memcpy(chan->bounce_iv, areq->iv, ivsize);
+	}
+
+	if (areq->src == areq->dst) {
+		nr_sgs = dma_map_sg(ce->dev, areq->src, sg_nents(areq->src),
+				    DMA_BIDIRECTIONAL);
+		if (nr_sgs <= 0 || nr_sgs > MAX_SG) {
+			dev_err(ce->dev, "Invalid sg number %d\n", nr_sgs);
+			err = -EINVAL;
+			goto theend_iv;
+		}
+		nr_sgd = nr_sgs;
+	} else {
+		nr_sgs = dma_map_sg(ce->dev, areq->src, sg_nents(areq->src),
+				    DMA_TO_DEVICE);
+		if (nr_sgs <= 0 || nr_sgs > MAX_SG) {
+			dev_err(ce->dev, "Invalid sg number %d\n", nr_sgs);
+			err = -EINVAL;
+			goto theend_iv;
+		}
+		nr_sgd = dma_map_sg(ce->dev, areq->dst, sg_nents(areq->dst),
+				    DMA_FROM_DEVICE);
+		if (nr_sgd <= 0 || nr_sgd > MAX_SG) {
+			dev_err(ce->dev, "Invalid sg number %d\n", nr_sgd);
+			err = -EINVAL;
+			goto theend_sgs;
+		}
+	}
+
+	len = areq->cryptlen;
+	for_each_sg(areq->src, sg, nr_sgs, i) {
+		cet->t_src[i].addr = sg_dma_address(sg);
+		todo = min(len, sg_dma_len(sg));
+		cet->t_src[i].len = todo / 4;
+		dev_dbg(ce->dev, "%s total=%u SG(%d %u off=%d) todo=%u\n", __func__,
+			areq->cryptlen, i, cet->t_src[i].len, sg->offset, todo);
+		len -= todo;
+	}
+	if (len > 0)
+		dev_err(ce->dev, "remaining len %d\n", len);
+
+	len = areq->cryptlen;
+	for_each_sg(areq->dst, sg, nr_sgd, i) {
+		cet->t_dst[i].addr = sg_dma_address(sg);
+		todo = min(len, sg_dma_len(sg));
+		cet->t_dst[i].len = todo / 4;
+		dev_dbg(ce->dev, "%s total=%u SG(%d %u off=%d) todo=%u\n", __func__,
+			areq->cryptlen, i, cet->t_dst[i].len, sg->offset, todo);
+		len -= todo;
+	}
+	if (len > 0)
+		dev_err(ce->dev, "remaining len %d\n", len);
+
+	chan->timeout = areq->cryptlen;
+	err = sun8i_ce_run_task(ce, flow, "cipher");
+	if (err)
+		dev_err(ce->dev, "Error with len=%u\n", areq->cryptlen);
+
+theend_sgs:
+	if (areq->src == areq->dst) {
+		dma_unmap_sg(ce->dev, areq->src, nr_sgs, DMA_BIDIRECTIONAL);
+	} else {
+		if (nr_sgs > 0)
+			dma_unmap_sg(ce->dev, areq->src, nr_sgs, DMA_TO_DEVICE);
+		dma_unmap_sg(ce->dev, areq->dst, nr_sgd, DMA_FROM_DEVICE);
+	}
+
+theend_iv:
+	if (areq->iv && ivsize > 0) {
+		offset = areq->cryptlen - ivsize;
+		if (rctx->op_dir & CE_DECRYPTION) {
+			memcpy(areq->iv, backup_iv, ivsize);
+			kzfree(backup_iv);
+		} else {
+			scatterwalk_map_and_copy(areq->iv, areq->dst, offset,
+						 ivsize, 0);
+		}
+		kfree(chan->bounce_iv);
+	}
+
+theend_key:
+	dma_unmap_single(ce->dev, cet->t_key, op->keylen, DMA_TO_DEVICE);
+
+theend:
+	mutex_unlock(&chan->lock);
+
+	return err;
+}
+
+static int handle_cipher_request(struct crypto_engine *engine, void *areq)
+{
+	int err;
+	struct skcipher_request *breq = container_of(areq, struct skcipher_request, base);
+
+	err = sun8i_ce_cipher(breq);
+	crypto_finalize_skcipher_request(engine, breq, err);
+
+	return 0;
+}
+
+int sun8i_ce_skdecrypt(struct skcipher_request *areq)
+{
+	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
+	struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);
+	struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);
+	int e = sun8i_ce_get_engine_number(op->ce);
+	struct crypto_engine *engine = op->ce->chanlist[e].engine;
+
+	rctx->op_dir = CE_DECRYPTION;
+	rctx->flow = e;
+
+	return crypto_transfer_skcipher_request_to_engine(engine, areq);
+}
+
+int sun8i_ce_skencrypt(struct skcipher_request *areq)
+{
+	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
+	struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);
+	struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);
+	int e = sun8i_ce_get_engine_number(op->ce);
+	struct crypto_engine *engine = op->ce->chanlist[e].engine;
+
+	rctx->op_dir = CE_ENCRYPTION;
+	rctx->flow = e;
+
+	return crypto_transfer_skcipher_request_to_engine(engine, areq);
+}
+
+int sun8i_ce_cipher_init(struct crypto_tfm *tfm)
+{
+	struct sun8i_cipher_tfm_ctx *op = crypto_tfm_ctx(tfm);
+	struct sun8i_ce_alg_template *algt;
+	const char *name = crypto_tfm_alg_name(tfm);
+	struct crypto_skcipher *sktfm = __crypto_skcipher_cast(tfm);
+	struct skcipher_alg *alg = crypto_skcipher_alg(sktfm);
+
+	memset(op, 0, sizeof(struct sun8i_cipher_tfm_ctx));
+
+	algt = container_of(alg, struct sun8i_ce_alg_template, alg.skcipher);
+	op->ce = algt->ce;
+
+	sktfm->reqsize = sizeof(struct sun8i_cipher_req_ctx);
+
+	op->fallback_tfm = crypto_alloc_sync_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK);
+	if (IS_ERR(op->fallback_tfm)) {
+		dev_err(op->ce->dev, "ERROR: Cannot allocate fallback for %s %ld\n",
+			name, PTR_ERR(op->fallback_tfm));
+		return PTR_ERR(op->fallback_tfm);
+	}
+
+	dev_info(op->ce->dev, "Fallback is %s\n", crypto_tfm_alg_driver_name(crypto_skcipher_tfm(&op->fallback_tfm->base)));
+
+	op->enginectx.op.do_one_request = handle_cipher_request;
+	op->enginectx.op.prepare_request = NULL;
+	op->enginectx.op.unprepare_request = NULL;
+
+	return 0;
+}
+
+void sun8i_ce_cipher_exit(struct crypto_tfm *tfm)
+{
+	struct sun8i_cipher_tfm_ctx *op = crypto_tfm_ctx(tfm);
+
+	if (op->key) {
+		memzero_explicit(op->key, op->keylen);
+		kfree(op->key);
+	}
+	crypto_free_sync_skcipher(op->fallback_tfm);
+}
+
+int sun8i_ce_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,
+			unsigned int keylen)
+{
+	struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);
+	struct sun8i_ce_dev *ce = op->ce;
+
+	switch (keylen) {
+	case 128 / 8:
+		break;
+	case 192 / 8:
+		break;
+	case 256 / 8:
+		break;
+	default:
+		dev_dbg(ce->dev, "ERROR: Invalid keylen %u\n", keylen);
+		crypto_skcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+		return -EINVAL;
+	}
+	if (op->key) {
+		memzero_explicit(op->key, op->keylen);
+		kfree(op->key);
+	}
+	op->keylen = keylen;
+	op->key = kmalloc(keylen, GFP_KERNEL | GFP_DMA);
+	if (!op->key)
+		return -ENOMEM;
+	memcpy(op->key, key, keylen);
+
+	crypto_sync_skcipher_clear_flags(op->fallback_tfm, CRYPTO_TFM_REQ_MASK);
+	crypto_sync_skcipher_set_flags(op->fallback_tfm, tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);
+
+	return crypto_sync_skcipher_setkey(op->fallback_tfm, key, keylen);
+}
+
+int sun8i_ce_des3_setkey(struct crypto_skcipher *tfm, const u8 *key,
+			 unsigned int keylen)
+{
+	struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);
+	int err;
+
+	err = verify_skcipher_des3_key(tfm, key);
+	if (err)
+		return err;
+
+	if (op->key) {
+		memzero_explicit(op->key, op->keylen);
+		kfree(op->key);
+	}
+	op->keylen = keylen;
+	op->key = kmalloc(keylen, GFP_KERNEL | GFP_DMA);
+	if (!op->key)
+		return -ENOMEM;
+	memcpy(op->key, key, keylen);
+
+	crypto_sync_skcipher_clear_flags(op->fallback_tfm, CRYPTO_TFM_REQ_MASK);
+	crypto_sync_skcipher_set_flags(op->fallback_tfm, tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);
+
+	return crypto_sync_skcipher_setkey(op->fallback_tfm, key, keylen);
+}
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
new file mode 100644
index 000000000000..c609b9943296
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
@@ -0,0 +1,630 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sun8i-ce-core.c - hardware cryptographic offloader for
+ * Allwinner H3/A64/H5/H2+/H6/R40 SoC
+ *
+ * Copyright (C) 2015-2019 Corentin Labbe <clabbe.montjoie@gmail.com>
+ *
+ * Core file which registers crypto algorithms supported by the CryptoEngine.
+ *
+ * You could find a link for the datasheet in Documentation/arm/sunxi/README
+ */
+#include <linux/clk.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <crypto/internal/skcipher.h>
+
+#include "sun8i-ce.h"
+
+static const struct ce_variant ce_h3_variant = {
+	.alg_cipher = { CE_ID_NOTSUPP, CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
+		CE_ID_NOTSUPP,
+	},
+	.op_mode = { CE_ID_NOTSUPP, CE_OP_ECB, CE_OP_CBC
+	},
+	.intreg = CE_ISR,
+	.maxflow = 4,
+	.ce_clks = {
+		{ "ahb", 200000000 },
+		{ "mod", 48000000 },
+		}
+};
+
+static const struct ce_variant ce_h5_variant = {
+	.alg_cipher = { CE_ID_NOTSUPP, CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
+		CE_ID_NOTSUPP,
+	},
+	.op_mode = { CE_ID_NOTSUPP, CE_OP_ECB, CE_OP_CBC
+	},
+	.intreg = CE_ISR,
+	.maxflow = 4,
+	.ce_clks = {
+		{ "ahb", 200000000 },
+		{ "mod", 300000000 },
+		}
+};
+
+static const struct ce_variant ce_h6_variant = {
+	.alg_cipher = { CE_ID_NOTSUPP, CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
+		CE_ALG_RAES,
+	},
+	.op_mode = { CE_ID_NOTSUPP, CE_OP_ECB, CE_OP_CBC
+	},
+	.model = CE_v2,
+	.intreg = CE_ISR,
+	.maxflow = 4,
+	.ce_clks = {
+		{ "ahb", 200000000 },
+		{ "mod", 300000000 },
+		{ "mbus", 400000000 },
+		}
+};
+
+static const struct ce_variant ce_a64_variant = {
+	.alg_cipher = { CE_ID_NOTSUPP, CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
+		CE_ID_NOTSUPP,
+	},
+	.op_mode = { CE_ID_NOTSUPP, CE_OP_ECB, CE_OP_CBC
+	},
+	.intreg = CE_ISR,
+	.maxflow = 4,
+	.ce_clks = {
+		{ "ahb", 200000000 },
+		{ "mod", 300000000 },
+		}
+};
+
+static const struct ce_variant ce_r40_variant = {
+	.alg_cipher = { CE_ID_NOTSUPP, CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
+		CE_ID_NOTSUPP,
+	},
+	.op_mode = { CE_ID_NOTSUPP, CE_OP_ECB, CE_OP_CBC
+	},
+	.intreg = CE_ISR,
+	.maxflow = 4,
+	.ce_clks = {
+		{ "ahb", 200000000 },
+		{ "mod", 300000000 },
+		}
+};
+
+int sun8i_ce_get_engine_number(struct sun8i_ce_dev *ce)
+{
+	return atomic_inc_return(&ce->flow) % ce->variant->maxflow;
+}
+
+int sun8i_ce_run_task(struct sun8i_ce_dev *ce, int flow, const char *name)
+{
+	u32 v;
+	int err = 0;
+	struct ce_task *cet = ce->chanlist[flow].tl;
+
+	if (ce->chanlist[flow].bounce_iv) {
+		cet->t_iv = dma_map_single(ce->dev,
+					   ce->chanlist[flow].bounce_iv,
+					   ce->chanlist[flow].ivlen,
+					   DMA_TO_DEVICE);
+		if (dma_mapping_error(ce->dev, cet->t_iv)) {
+			dev_err(ce->dev, "Cannot DMA MAP IV\n");
+			return -EFAULT;
+		}
+	}
+
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	ce->chanlist[flow].stat_req++;
+#endif
+
+	mutex_lock(&ce->mlock);
+
+	v = readl(ce->base + CE_ICR);
+	v |= 1 << flow;
+	writel(v, ce->base + CE_ICR);
+
+	reinit_completion(&ce->chanlist[flow].complete);
+	writel(ce->chanlist[flow].t_phy, ce->base + CE_TDQ);
+
+	ce->chanlist[flow].status = 0;
+	/* Be sure all data is written before enabling the task */
+	wmb();
+
+	v = 1 | (ce->chanlist[flow].tl->t_common_ctl & 0x7F) << 8;
+	writel(v, ce->base + CE_TLR);
+	mutex_unlock(&ce->mlock);
+
+	wait_for_completion_interruptible_timeout(&ce->chanlist[flow].complete,
+			msecs_to_jiffies(ce->chanlist[flow].timeout));
+
+	if (ce->chanlist[flow].status == 0) {
+		dev_err(ce->dev, "DMA timeout for %s\n", name);
+		err = -EFAULT;
+	}
+	/* No need to lock for this read, the channel is locked so
+	 * nothing could modify the error value for this channel
+	 */
+	v = readl(ce->base + CE_ESR);
+	if (v) {
+		v >>= (flow * 4);
+		v &= 0xFF;
+		if (v) {
+			dev_err(ce->dev, "CE ERROR: %x for flow %x\n", v, flow);
+			err = -EFAULT;
+		}
+		if (v & CE_ERR_ALGO_NOTSUP)
+			dev_err(ce->dev, "CE ERROR: algorithm not supported\n");
+		if (v & CE_ERR_DATALEN)
+			dev_err(ce->dev, "CE ERROR: data length error\n");
+		if (v & CE_ERR_KEYSRAM)
+			dev_err(ce->dev, "CE ERROR: keysram access error for AES\n");
+		if (v & CE_ERR_ADDR_INVALID)
+			dev_err(ce->dev, "CE ERROR: address invalid\n");
+		}
+
+	if (ce->chanlist[flow].bounce_iv) {
+		dma_unmap_single(ce->dev, cet->t_iv,
+				 ce->chanlist[flow].ivlen,
+				 DMA_TO_DEVICE);
+	}
+
+	return err;
+}
+
+static irqreturn_t ce_irq_handler(int irq, void *data)
+{
+	struct sun8i_ce_dev *ce = (struct sun8i_ce_dev *)data;
+	int flow = 0;
+	u32 p;
+
+	p = readl(ce->base + ce->variant->intreg);
+	for (flow = 0; flow < ce->variant->maxflow; flow++) {
+		if (p & (BIT(flow))) {
+			writel(BIT(flow), ce->base + ce->variant->intreg);
+			ce->chanlist[flow].status = 1;
+			complete(&ce->chanlist[flow].complete);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct sun8i_ce_alg_template ce_algs[] = {
+{
+	.type = CRYPTO_ALG_TYPE_SKCIPHER,
+	.ce_algo_id = CE_ID_CIPHER_AES,
+	.ce_blockmode = CE_ID_OP_CBC,
+	.alg.skcipher = {
+		.base = {
+			.cra_name = "cbc(aes)",
+			.cra_driver_name = "cbc-aes-sun8i-ce",
+			.cra_priority = 400,
+			.cra_blocksize = AES_BLOCK_SIZE,
+			.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER |
+				CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
+			.cra_ctxsize = sizeof(struct sun8i_cipher_tfm_ctx),
+			.cra_module = THIS_MODULE,
+			.cra_alignmask = 0xf,
+			.cra_init = sun8i_ce_cipher_init,
+			.cra_exit = sun8i_ce_cipher_exit,
+		},
+		.min_keysize	= AES_MIN_KEY_SIZE,
+		.max_keysize	= AES_MAX_KEY_SIZE,
+		.ivsize		= AES_BLOCK_SIZE,
+		.setkey		= sun8i_ce_aes_setkey,
+		.encrypt	= sun8i_ce_skencrypt,
+		.decrypt	= sun8i_ce_skdecrypt,
+	}
+},
+{
+	.type = CRYPTO_ALG_TYPE_SKCIPHER,
+	.ce_algo_id = CE_ID_CIPHER_AES,
+	.ce_blockmode = CE_ID_OP_ECB,
+	.alg.skcipher = {
+		.base = {
+			.cra_name = "ecb(aes)",
+			.cra_driver_name = "ecb-aes-sun8i-ce",
+			.cra_priority = 400,
+			.cra_blocksize = AES_BLOCK_SIZE,
+			.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER |
+				CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
+			.cra_ctxsize = sizeof(struct sun8i_cipher_tfm_ctx),
+			.cra_module = THIS_MODULE,
+			.cra_alignmask = 0xf,
+			.cra_init = sun8i_ce_cipher_init,
+			.cra_exit = sun8i_ce_cipher_exit,
+		},
+		.min_keysize	= AES_MIN_KEY_SIZE,
+		.max_keysize	= AES_MAX_KEY_SIZE,
+		.setkey		= sun8i_ce_aes_setkey,
+		.encrypt	= sun8i_ce_skencrypt,
+		.decrypt	= sun8i_ce_skdecrypt,
+	}
+},
+{
+	.type = CRYPTO_ALG_TYPE_SKCIPHER,
+	.ce_algo_id = CE_ID_CIPHER_DES3,
+	.ce_blockmode = CE_ID_OP_CBC,
+	.alg.skcipher = {
+		.base = {
+			.cra_name = "cbc(des3_ede)",
+			.cra_driver_name = "cbc-des3-sun8i-ce",
+			.cra_priority = 400,
+			.cra_blocksize = DES3_EDE_BLOCK_SIZE,
+			.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER |
+				CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
+			.cra_ctxsize = sizeof(struct sun8i_cipher_tfm_ctx),
+			.cra_module = THIS_MODULE,
+			.cra_alignmask = 0xf,
+			.cra_init = sun8i_ce_cipher_init,
+			.cra_exit = sun8i_ce_cipher_exit,
+		},
+		.min_keysize	= DES3_EDE_KEY_SIZE,
+		.max_keysize	= DES3_EDE_KEY_SIZE,
+		.ivsize		= DES3_EDE_BLOCK_SIZE,
+		.setkey		= sun8i_ce_des3_setkey,
+		.encrypt	= sun8i_ce_skencrypt,
+		.decrypt	= sun8i_ce_skdecrypt,
+	}
+},
+{
+	.type = CRYPTO_ALG_TYPE_SKCIPHER,
+	.ce_algo_id = CE_ID_CIPHER_DES3,
+	.ce_blockmode = CE_ID_OP_ECB,
+	.alg.skcipher = {
+		.base = {
+			.cra_name = "ecb(des3_ede)",
+			.cra_driver_name = "ecb-des3-sun8i-ce",
+			.cra_priority = 400,
+			.cra_blocksize = DES3_EDE_BLOCK_SIZE,
+			.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER |
+				CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK,
+			.cra_ctxsize = sizeof(struct sun8i_cipher_tfm_ctx),
+			.cra_module = THIS_MODULE,
+			.cra_alignmask = 0xf,
+			.cra_init = sun8i_ce_cipher_init,
+			.cra_exit = sun8i_ce_cipher_exit,
+		},
+		.min_keysize	= DES3_EDE_KEY_SIZE,
+		.max_keysize	= DES3_EDE_KEY_SIZE,
+		.setkey		= sun8i_ce_des3_setkey,
+		.encrypt	= sun8i_ce_skencrypt,
+		.decrypt	= sun8i_ce_skdecrypt,
+	}
+},
+};
+
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+static int sun8i_ce_dbgfs_read(struct seq_file *seq, void *v)
+{
+	struct sun8i_ce_dev *ce = seq->private;
+	int i;
+
+	for (i = 0; i < ce->variant->maxflow; i++)
+		seq_printf(seq, "Channel %d: req %lu\n", i, ce->chanlist[i].stat_req);
+
+	for (i = 0; i < ARRAY_SIZE(ce_algs); i++) {
+		if (!ce_algs[i].ce)
+			continue;
+		switch (ce_algs[i].type) {
+		case CRYPTO_ALG_TYPE_SKCIPHER:
+			seq_printf(seq, "%s %s %lu %lu\n",
+				   ce_algs[i].alg.skcipher.base.cra_driver_name,
+				   ce_algs[i].alg.skcipher.base.cra_name,
+				   ce_algs[i].stat_req, ce_algs[i].stat_fb);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int sun8i_ce_dbgfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, sun8i_ce_dbgfs_read, inode->i_private);
+}
+
+static const struct file_operations sun8i_ce_debugfs_fops = {
+	.owner = THIS_MODULE,
+	.open = sun8i_ce_dbgfs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+#endif
+
+static int sun8i_ce_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	u32 v;
+	int err, i, ce_method, id, irq;
+	unsigned long cr;
+	struct sun8i_ce_dev *ce;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	ce = devm_kzalloc(&pdev->dev, sizeof(*ce), GFP_KERNEL);
+	if (!ce)
+		return -ENOMEM;
+
+	ce->variant = of_device_get_match_data(&pdev->dev);
+	if (!ce->variant) {
+		dev_err(&pdev->dev, "Missing Crypto Engine variant\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ce->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ce->base)) {
+		err = PTR_ERR(ce->base);
+		dev_err(&pdev->dev, "Cannot request MMIO err=%d\n", err);
+		return err;
+	}
+
+	for (i = 0; i < CE_MAX_CLOCKS; i++) {
+		if (!ce->variant->ce_clks[i].name)
+			continue;
+		dev_info(&pdev->dev, "Get %s clock\n", ce->variant->ce_clks[i].name);
+		ce->ceclks[i] = devm_clk_get(&pdev->dev, ce->variant->ce_clks[i].name);
+		if (IS_ERR(ce->ceclks[i])) {
+			err = PTR_ERR(ce->ceclks[i]);
+			dev_err(&pdev->dev, "Cannot get %s CE clock err=%d\n",
+				ce->variant->ce_clks[i].name, err);
+		}
+		cr = clk_get_rate(ce->ceclks[i]);
+		if (ce->variant->ce_clks[i].freq) {
+			dev_info(&pdev->dev, "Set %s clock to %lu (%lu Mhz) from %lu (%lu Mhz)\n",
+				 ce->variant->ce_clks[i].name,
+				 ce->variant->ce_clks[i].freq,
+				 ce->variant->ce_clks[i].freq / 1000000,
+				 cr,
+				 cr / 1000000);
+			err = clk_set_rate(ce->ceclks[i], ce->variant->ce_clks[i].freq);
+			if (err)
+				dev_err(&pdev->dev, "Fail to set %s clk speed to %lu\n",
+					ce->variant->ce_clks[i].name,
+					ce->variant->ce_clks[i].freq);
+		} else {
+			dev_info(&pdev->dev, "%s run at %lu\n",
+				 ce->variant->ce_clks[i].name, cr);
+		}
+		err = clk_prepare_enable(ce->ceclks[i]);
+		if (err) {
+			dev_err(&pdev->dev, "Cannot prepare_enable %s\n",
+				ce->variant->ce_clks[i].name);
+			return err;
+		}
+	}
+
+	/* Get Non Secure IRQ */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(ce->dev, "Cannot get NS IRQ\n");
+		return irq;
+	}
+
+	err = devm_request_irq(&pdev->dev, irq, ce_irq_handler, 0,
+			       "sun8i-ce-ns", ce);
+	if (err < 0) {
+		dev_err(ce->dev, "Cannot request NS IRQ\n");
+		return err;
+	}
+
+	ce->reset = devm_reset_control_get_optional(&pdev->dev, "ahb");
+	if (IS_ERR(ce->reset)) {
+		if (PTR_ERR(ce->reset) == -EPROBE_DEFER)
+			return PTR_ERR(ce->reset);
+		dev_info(&pdev->dev, "No reset control found\n");
+		ce->reset = NULL;
+	}
+
+	err = reset_control_deassert(ce->reset);
+	if (err) {
+		dev_err(&pdev->dev, "Cannot deassert reset control\n");
+		goto error_clk;
+	}
+
+	v = readl(ce->base + CE_CTR);
+	v >>= 16;
+	v &= 0x07;
+	dev_info(&pdev->dev, "CE_NS Die ID %x\n", v);
+
+	ce->dev = &pdev->dev;
+	platform_set_drvdata(pdev, ce);
+
+	mutex_init(&ce->mlock);
+
+	ce->chanlist = devm_kcalloc(ce->dev, ce->variant->maxflow,
+				    sizeof(struct sun8i_ce_flow), GFP_KERNEL);
+	if (!ce->chanlist) {
+		err = -ENOMEM;
+		goto error_flow;
+	}
+
+	for (i = 0; i < ce->variant->maxflow; i++) {
+		init_completion(&ce->chanlist[i].complete);
+		mutex_init(&ce->chanlist[i].lock);
+
+		ce->chanlist[i].engine = crypto_engine_alloc_init(ce->dev, true);
+		if (!ce->chanlist[i].engine) {
+			dev_err(ce->dev, "Cannot allocate engine\n");
+			i--;
+			goto error_engine;
+		}
+		err = crypto_engine_start(ce->chanlist[i].engine);
+		if (err) {
+			dev_err(ce->dev, "Cannot start engine\n");
+			goto error_engine;
+		}
+		ce->chanlist[i].tl = dma_alloc_coherent(ce->dev,
+							sizeof(struct ce_task),
+							&ce->chanlist[i].t_phy,
+							GFP_KERNEL);
+		if (!ce->chanlist[i].tl) {
+			dev_err(ce->dev, "Cannot get DMA memory for task %d\n",
+				i);
+			err = -ENOMEM;
+			goto error_engine;
+		}
+	}
+
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	ce->dbgfs_dir = debugfs_create_dir("sun8i-ce", NULL);
+	if (IS_ERR_OR_NULL(ce->dbgfs_dir)) {
+		dev_err(ce->dev, "Fail to create debugfs dir");
+		err = -ENOMEM;
+		goto error_engine;
+	}
+	ce->dbgfs_stats = debugfs_create_file("stats", 0444,
+					      ce->dbgfs_dir, ce,
+					      &sun8i_ce_debugfs_fops);
+	if (IS_ERR_OR_NULL(ce->dbgfs_stats)) {
+		dev_err(ce->dev, "Fail to create debugfs stat");
+		err = -ENOMEM;
+		goto error_debugfs;
+	}
+#endif
+	for (i = 0; i < ARRAY_SIZE(ce_algs); i++) {
+		ce_algs[i].ce = ce;
+		switch (ce_algs[i].type) {
+		case CRYPTO_ALG_TYPE_SKCIPHER:
+			id = ce_algs[i].ce_algo_id;
+			ce_method = ce->variant->alg_cipher[id];
+			if (ce_method == CE_ID_NOTSUPP) {
+				dev_info(ce->dev,
+					 "DEBUG: Algo of %s not supported\n",
+					 ce_algs[i].alg.skcipher.base.cra_name);
+				ce_algs[i].ce = NULL;
+				break;
+			}
+			id = ce_algs[i].ce_blockmode;
+			ce_method = ce->variant->op_mode[id];
+			if (ce_method == CE_ID_NOTSUPP) {
+				dev_info(ce->dev, "DEBUG: Blockmode of %s not supported\n",
+					 ce_algs[i].alg.skcipher.base.cra_name);
+				ce_algs[i].ce = NULL;
+				break;
+			}
+			dev_info(ce->dev, "DEBUG: Register %s\n",
+				 ce_algs[i].alg.skcipher.base.cra_name);
+			err = crypto_register_skcipher(&ce_algs[i].alg.skcipher);
+			if (err) {
+				dev_err(ce->dev, "Fail to register %s\n",
+					ce_algs[i].alg.skcipher.base.cra_name);
+				ce_algs[i].ce = NULL;
+				goto error_alg;
+			}
+			break;
+		default:
+			dev_err(ce->dev, "ERROR: tryed to register an unknown algo\n");
+		}
+	}
+
+	return 0;
+error_alg:
+	i--;
+	for (; i >= 0; i--) {
+		switch (ce_algs[i].type) {
+		case CRYPTO_ALG_TYPE_SKCIPHER:
+			if (ce_algs[i].ce)
+				crypto_unregister_skcipher(&ce_algs[i].alg.skcipher);
+			break;
+		}
+	}
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+error_debugfs:
+	debugfs_remove_recursive(ce->dbgfs_dir);
+#endif
+	i = ce->variant->maxflow;
+error_engine:
+	while (i >= 0) {
+		crypto_engine_exit(ce->chanlist[i].engine);
+		if (ce->chanlist[i].tl)
+			dma_free_coherent(ce->dev, sizeof(struct ce_task),
+					  ce->chanlist[i].tl,
+					  ce->chanlist[i].t_phy);
+		i--;
+	}
+error_flow:
+	reset_control_assert(ce->reset);
+error_clk:
+	for (i = 0; i < CE_MAX_CLOCKS; i++)
+		clk_disable_unprepare(ce->ceclks[i]);
+	return err;
+}
+
+static int sun8i_ce_remove(struct platform_device *pdev)
+{
+	int i, timeout;
+	struct sun8i_ce_dev *ce = platform_get_drvdata(pdev);
+
+	for (i = 0; i < ARRAY_SIZE(ce_algs); i++) {
+		switch (ce_algs[i].type) {
+		case CRYPTO_ALG_TYPE_SKCIPHER:
+			if (ce_algs[i].ce) {
+				dev_dbg(ce->dev, "Unregister %d %s\n", i,
+					ce_algs[i].alg.skcipher.base.cra_name);
+				crypto_unregister_skcipher(&ce_algs[i].alg.skcipher);
+			}
+			break;
+		}
+	}
+
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	debugfs_remove_recursive(ce->dbgfs_dir);
+#endif
+
+	for (i = 0; i < ce->variant->maxflow; i++) {
+		crypto_engine_exit(ce->chanlist[i].engine);
+		timeout = 0;
+		while (mutex_is_locked(&ce->chanlist[i].lock) && timeout < 10) {
+			dev_info(ce->dev, "Wait for %d %d\n", i, timeout);
+			timeout++;
+			msleep(20);
+		}
+		dma_free_coherent(ce->dev, sizeof(struct ce_task),
+				  ce->chanlist[i].tl,
+				  ce->chanlist[i].t_phy);
+	}
+
+	reset_control_assert(ce->reset);
+	for (i = 0; i < CE_MAX_CLOCKS; i++)
+		clk_disable_unprepare(ce->ceclks[i]);
+	return 0;
+}
+
+static const struct of_device_id sun8i_ce_crypto_of_match_table[] = {
+	{ .compatible = "allwinner,sun8i-h3-crypto",
+	  .data = &ce_h3_variant },
+	{ .compatible = "allwinner,sun50i-h5-crypto",
+	  .data = &ce_h5_variant },
+	{ .compatible = "allwinner,sun50i-h6-crypto",
+	  .data = &ce_h6_variant },
+	{ .compatible = "allwinner,sun50i-a64-crypto",
+	  .data = &ce_a64_variant },
+	{ .compatible = "allwinner,sun8i-r40-crypto",
+	  .data = &ce_r40_variant },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sun8i_ce_crypto_of_match_table);
+
+static struct platform_driver sun8i_ce_driver = {
+	.probe		 = sun8i_ce_probe,
+	.remove		 = sun8i_ce_remove,
+	.driver		 = {
+		.name		   = "sun8i-ce",
+		.of_match_table	= sun8i_ce_crypto_of_match_table,
+	},
+};
+
+module_platform_driver(sun8i_ce_driver);
+
+MODULE_DESCRIPTION("Allwinner Crypto Engine cryptographic offloader");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Corentin Labbe <clabbe.montjoie@gmail.com>");
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
new file mode 100644
index 000000000000..fe51f9569fa0
--- /dev/null
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * sun8i-ce.h - hardware cryptographic offloader for
+ * Allwinner H3/A64/H5/H2+/H6 SoC
+ *
+ * Copyright (C) 2016-2019 Corentin LABBE <clabbe.montjoie@gmail.com>
+ */
+#include <crypto/aes.h>
+#include <crypto/des.h>
+#include <crypto/engine.h>
+#include <crypto/skcipher.h>
+#include <linux/atomic.h>
+#include <linux/debugfs.h>
+#include <linux/crypto.h>
+
+#define MAX_SG 8
+
+#define CE_STD 0
+#define CE_v2 1
+
+#define CE_MAX_CLOCKS 3
+
+/* CE Registers */
+#define CE_TDQ	0x00
+#define CE_CTR	0x04
+#define CE_ICR	0x08
+#define CE_ISR	0x0C
+#define CE_TLR	0x10
+#define CE_TSR	0x14
+#define CE_ESR	0x18
+#define CE_CSSGR	0x1C
+#define CE_CDSGR	0x20
+#define CE_CSAR	0x24
+#define CE_CDAR	0x28
+#define CE_TPR	0x2C
+
+/* Used in struct ce_task */
+/* ce_task common */
+#define CE_ENCRYPTION		0
+#define CE_DECRYPTION		BIT(8)
+
+#define CE_COMM_INT		BIT(31)
+
+/* ce_task symmetric */
+#define CE_AES_128BITS 0
+#define CE_AES_192BITS 1
+#define CE_AES_256BITS 2
+
+#define CE_OP_ECB	0
+#define CE_OP_CBC	(1 << 8)
+
+#define CE_ALG_AES		0
+#define CE_ALG_DES		1
+#define CE_ALG_3DES		2
+#define CE_ALG_RAES		48
+
+/* Used in ce_variant */
+#define CE_ID_NOTSUPP		0xFF
+
+#define CE_ID_CIPHER_AES	1
+#define CE_ID_CIPHER_DES	2
+#define CE_ID_CIPHER_DES3	3
+#define CE_ID_CIPHER_RAES	4
+#define CE_ID_CIPHER_MAX	5
+
+#define CE_ID_OP_ECB	1
+#define CE_ID_OP_CBC	2
+#define CE_ID_OP_MAX	3
+
+/* Used in CE registers */
+#define CE_ERR_ALGO_NOTSUP	BIT(0)
+#define CE_ERR_DATALEN		BIT(1)
+#define CE_ERR_KEYSRAM		BIT(2)
+#define CE_ERR_ADDR_INVALID	BIT(5)
+#define CE_ERR_KEYLADDER	BIT(6)
+
+/*
+ * struct ce_clock - Describe clocks used by sun8i-ce
+ * @name:	Name of clock needed by this variant
+ * @freq:	Maximum frequency for each clock
+ */
+struct ce_clock {
+	const char *name;
+	unsigned long freq;
+};
+
+/*
+ * struct ce_variant - Describe CE capability for each variant hardware
+ * @alg_cipher:	list of supported ciphers. for each CE_ID_ this will give the
+ *              coresponding CE_ALG_XXX/SS_ALG_XXX value
+ * @op_mode:	list of supported block modes
+ * @model:	The minor variant CE_STD/CE_SS/CE_v2
+ * @intreg:	reg offset for Interrupt register
+ * @maxflow:	Numbers of flow for the current engine
+ */
+struct ce_variant {
+	char alg_cipher[CE_ID_CIPHER_MAX];
+	u32 op_mode[CE_ID_OP_MAX];
+	int model;
+	u32 intreg;
+	unsigned int maxflow;
+	struct ce_clock ce_clks[CE_MAX_CLOCKS];
+};
+
+struct sginfo {
+	u32 addr;
+	u32 len;
+} __packed;
+
+/*
+ * struct ce_task - CE Task descriptor
+ * The structure of this descriptor could be found in the datasheet
+ */
+struct ce_task {
+	u32 t_id;
+	u32 t_common_ctl;
+	u32 t_sym_ctl;
+	u32 t_asym_ctl;
+	u32 t_key;
+	u32 t_iv;
+	u32 t_ctr;
+	u32 t_dlen;
+	struct sginfo t_src[MAX_SG];
+	struct sginfo t_dst[MAX_SG];
+	u32 next;
+	u32 reserved[3];
+} __packed __aligned(8);
+
+/*
+ * struct sun8i_ce_flow - Information used by each flow
+ * @lock:	lock protecting access of sun8i_ce_flow
+ * @engine:	ptr to the crypto_engine for this flow
+ * @bounce_iv:	buffer which contain the IV
+ * @ivlen:	size of bounce_iv
+ * @keylen:	keylen for this flow operation
+ * @complete:	completion for the current task on this flow
+ * @status:	set to 1 by interrupt if task is done
+ * @method:	current method for flow
+ * @op_dir:	direction (encrypt vs decrypt) of this flow
+ * @op_mode:	op_mode for this flow
+ * @t_phy:	Physical address of task
+ * @tl:		pointer to the current ce_task for this flow
+ * @stat_req:	number of request done by this flow
+ */
+struct sun8i_ce_flow {
+	struct mutex lock;
+	struct crypto_engine *engine;
+	void *bounce_iv;
+	unsigned int ivlen;
+	unsigned int keylen;
+	struct completion complete;
+	int status;
+	u32 method;
+	u32 op_dir;
+	u32 op_mode;
+	dma_addr_t t_phy;
+	int timeout;
+	struct ce_task *tl;
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	unsigned long stat_req;
+#endif
+};
+
+/*
+ * struct sun8i_ce_dev - main container for all this driver information
+ * @base:	base address of SS/CE
+ * @ceclks:	clocks used by SS/CE
+ * @reset:	pointer to reset controller
+ * @dev:	the platform device
+ * @mlock:	Control access to device registers
+ * @chanlist:	array of all flow
+ * @flow:	flow to use in next request
+ * @variant:	pointer to variant specific data
+ * @dbgfs_dir:	Debugfs dentry for statistic directory
+ * @dbgfs_stats: Debugfs dentry for statistic counters
+ */
+struct sun8i_ce_dev {
+	void __iomem *base;
+	struct clk *ceclks[CE_MAX_CLOCKS];
+	struct reset_control *reset;
+	struct device *dev;
+	struct mutex mlock;
+	struct sun8i_ce_flow *chanlist;
+	atomic_t flow;
+	const struct ce_variant *variant;
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	struct dentry *dbgfs_dir;
+	struct dentry *dbgfs_stats;
+#endif
+};
+
+/*
+ * struct sun8i_cipher_req_ctx - context for a skcipher request
+ * @op_dir:	direction (encrypt vs decrypt) for this request
+ * @flow:	the flow to use for this request
+ */
+struct sun8i_cipher_req_ctx {
+	u32 op_dir;
+	int flow;
+};
+
+/*
+ * struct sun8i_cipher_tfm_ctx - context for a skcipher TFM
+ * @enginectx:		crypto_engine used by this TFM
+ * @key:		pointer to key data
+ * @keylen:		len of the key
+ * @ce:			pointer to the private data of driver handling this TFM
+ * @fallback_tfm:	pointer to the fallback TFM
+ */
+struct sun8i_cipher_tfm_ctx {
+	struct crypto_engine_ctx enginectx;
+	u32 *key;
+	u32 keylen;
+	struct sun8i_ce_dev *ce;
+	struct crypto_sync_skcipher *fallback_tfm;
+};
+
+/*
+ * struct sun8i_ce_alg_template - crypto_alg template
+ * @type:		the CRYPTO_ALG_TYPE for this template
+ * @ce_algo_id:		the CE_ID for this template
+ * @ce_blockmode:	the type of block operation CE_ID
+ * @ce:			pointer to the sun8i_ce_dev structure associated with
+ *			this template
+ * @alg:		one of sub struct must be used
+ * @stat_req:		number of request done on this template
+ * @stat_fb:		total of all data len done on this template
+ */
+struct sun8i_ce_alg_template {
+	u32 type;
+	u32 ce_algo_id;
+	u32 ce_blockmode;
+	struct sun8i_ce_dev *ce;
+	union {
+		struct skcipher_alg skcipher;
+	} alg;
+#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
+	unsigned long stat_req;
+	unsigned long stat_fb;
+#endif
+};
+
+int sun8i_ce_enqueue(struct crypto_async_request *areq, u32 type);
+
+int sun8i_ce_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,
+			unsigned int keylen);
+int sun8i_ce_des3_setkey(struct crypto_skcipher *tfm, const u8 *key,
+			 unsigned int keylen);
+int sun8i_ce_cipher_init(struct crypto_tfm *tfm);
+void sun8i_ce_cipher_exit(struct crypto_tfm *tfm);
+int sun8i_ce_skdecrypt(struct skcipher_request *areq);
+int sun8i_ce_skencrypt(struct skcipher_request *areq);
+
+int sun8i_ce_get_engine_number(struct sun8i_ce_dev *ce);
+
+int sun8i_ce_run_task(struct sun8i_ce_dev *ce, int flow, const char *name);
-- 
2.21.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 4/9] ARM: dts: sun8i: r40: add crypto engine node
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

The Crypto Engine is a hardware cryptographic offloader that supports
many algorithms.
It could be found on most Allwinner SoCs.

This patch enables the Crypto Engine on the Allwinner R40 SoC Device-tree.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 arch/arm/boot/dts/sun8i-r40.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-r40.dtsi b/arch/arm/boot/dts/sun8i-r40.dtsi
index bde068111b85..7eb649cea163 100644
--- a/arch/arm/boot/dts/sun8i-r40.dtsi
+++ b/arch/arm/boot/dts/sun8i-r40.dtsi
@@ -266,6 +266,17 @@
 			#phy-cells = <1>;
 		};
 
+		crypto: crypto-engine@1c15000 {
+			compatible = "allwinner,sun8i-r40-crypto";
+			reg = <0x01c15000 0x1000>;
+			interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>;
+			clock-names = "ahb", "mod";
+			resets = <&ccu RST_BUS_CE>;
+			reset-names = "ahb";
+			status = "okay";
+		};
+
 		ehci1: usb@1c19000 {
 			compatible = "allwinner,sun8i-r40-ehci", "generic-ehci";
 			reg = <0x01c19000 0x100>;
-- 
2.21.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 5/9] ARM: dts: sun8i: h3: Add Crypto Engine node
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

The Crypto Engine is a hardware cryptographic accelerator that supports
many algorithms.
It could be found on most Allwinner SoCs.

This patch enables the Crypto Engine on the Allwinner H3 SoC Device-tree.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 arch/arm/boot/dts/sun8i-h3.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index e37c30e811d3..873abe1b279b 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -153,6 +153,17 @@
 			allwinner,sram = <&ve_sram 1>;
 		};
 
+		crypto: crypto@1c15000 {
+			compatible = "allwinner,sun8i-h3-crypto";
+			reg = <0x01c15000 0x1000>;
+			interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "ce_ns";
+			resets = <&ccu RST_BUS_CE>;
+			reset-names = "ahb";
+			clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>;
+			clock-names = "ahb", "mod";
+		};
+
 		mali: gpu@1c40000 {
 			compatible = "allwinner,sun8i-h3-mali", "arm,mali-400";
 			reg = <0x01c40000 0x10000>;
-- 
2.21.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 6/9] ARM64: dts: allwinner: sun50i: Add Crypto Engine node on A64
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

The Crypto Engine is a hardware cryptographic accelerator that supports
many algorithms.
It could be found on most Allwinner SoCs.

This patch enables the Crypto Engine on the Allwinner A64 SoC Device-tree.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 69128a6dfc46..c9e30d462ab1 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -487,6 +487,17 @@
 			reg = <0x1c14000 0x400>;
 		};
 
+		crypto: crypto@1c15000 {
+			compatible = "allwinner,sun50i-a64-crypto";
+			reg = <0x01c15000 0x1000>;
+			interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "ce_ns";
+			resets = <&ccu RST_BUS_CE>;
+			reset-names = "ahb";
+			clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>;
+			clock-names = "ahb", "mod";
+		};
+
 		usb_otg: usb@1c19000 {
 			compatible = "allwinner,sun8i-a33-musb";
 			reg = <0x01c19000 0x0400>;
-- 
2.21.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 7/9] ARM64: dts: allwinner: sun50i: Add crypto engine node on H5
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

The Crypto Engine is a hardware cryptographic accelerator that supports
many algorithms.
It could be found on most Allwinner SoCs.

This patch enables the Crypto Engine on the Allwinner H5 SoC Device-tree.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
index f002a496d7cb..174fb3dcb3f7 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
@@ -127,6 +127,17 @@
 			allwinner,sram = <&ve_sram 1>;
 		};
 
+		crypto: crypto@1c15000 {
+			compatible = "allwinner,sun50i-h5-crypto";
+			reg = <0x01c15000 0x1000>;
+			interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "ce_ns";
+			resets = <&ccu RST_BUS_CE>;
+			reset-names = "ahb";
+			clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>;
+			clock-names = "ahb", "mod";
+		};
+
 		mali: gpu@1e80000 {
 			compatible = "allwinner,sun50i-h5-mali", "arm,mali-450";
 			reg = <0x01e80000 0x30000>;
-- 
2.21.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 8/9] ARM64: dts: allwinner: sun50i: Add Crypto Engine node on H6
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

The Crypto Engine is a hardware cryptographic accelerator that supports
many algorithms.

This patch enables the Crypto Engine on the Allwinner H6 SoC Device-tree.

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 0754f01fd731..51762499ed06 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -149,6 +149,16 @@
 			allwinner,sram = <&ve_sram 1>;
 		};
 
+		crypto: crypto@1904000 {
+			compatible = "allwinner,sun50i-h6-crypto";
+			reg = <0x01904000 0x1000>;
+			clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>, <&ccu CLK_MBUS_CE>;
+			clock-names = "ahb", "mod", "mbus";
+			resets = <&ccu RST_BUS_CE>;
+			reset-names = "ahb";
+			interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
 		syscon: syscon@3000000 {
 			compatible = "allwinner,sun50i-h6-system-control",
 				     "allwinner,sun50i-a64-system-control";
-- 
2.21.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 9/9] sunxi_defconfig: add new crypto options
From: Corentin Labbe @ 2019-09-06 18:45 UTC (permalink / raw)
  To: davem, herbert, linux, mark.rutland, mripard, robh+dt, wens
  Cc: devicetree, linux-kernel, linux-sunxi, Corentin Labbe,
	linux-crypto, linux-arm-kernel
In-Reply-To: <20190906184551.17858-1-clabbe.montjoie@gmail.com>

This patch adds the new allwinner crypto configs to sunxi_defconfig

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 arch/arm/configs/sunxi_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index df433abfcb02..d0ab8ba7710a 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -150,4 +150,6 @@ CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_PRINTK_TIME=y
 CONFIG_DEBUG_FS=y
+CONFIG_CRYPTO_DEV_ALLWINNER=y
+CONFIG_CRYPTO_DEV_SUN8I_CE=y
 CONFIG_CRYPTO_DEV_SUN4I_SS=y
-- 
2.21.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] PCI: dwc: Use PTR_ERR_OR_ZERO() in five functions
From: Markus Elfring @ 2019-09-06 18:50 UTC (permalink / raw)
  To: linux-pci, linux-arm-kernel, linux-samsung-soc, linux-amlogic,
	Binghui Wang, Bjorn Helgaas, Jingoo Han, Kevin Hilman,
	Krzysztof Kozlowski, Kukjin Kim, Lorenzo Pieralisi, Yue Wang,
	Xiaowei Song
  Cc: YueHaibing, zhong jiang, kernel-janitors, LKML

From: Markus Elfring <elfring@users.sourceforge.net>
Date: Fri, 6 Sep 2019 20:40:06 +0200

Simplify these function implementations by using a known function.

Generated by: scripts/coccinelle/api/ptr_ret.cocci

Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
 drivers/pci/controller/dwc/pci-exynos.c |  5 +----
 drivers/pci/controller/dwc/pci-meson.c  | 10 ++--------
 drivers/pci/controller/dwc/pcie-kirin.c | 10 ++--------
 3 files changed, 5 insertions(+), 20 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
index cee5f2f590e2..b6ab1cc5d895 100644
--- a/drivers/pci/controller/dwc/pci-exynos.c
+++ b/drivers/pci/controller/dwc/pci-exynos.c
@@ -92,10 +92,7 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,

 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(ep->mem_res->elbi_base))
-		return PTR_ERR(ep->mem_res->elbi_base);
-
-	return 0;
+	return PTR_ERR_OR_ZERO(ep->mem_res->elbi_base);
 }

 static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index e35e9eaa50ee..713059918002 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -182,10 +182,7 @@ static int meson_pcie_get_mems(struct platform_device *pdev,

 	/* Meson SoC has two PCI controllers use same phy register*/
 	mp->mem_res.phy_base = meson_pcie_get_mem_shared(pdev, mp, "phy");
-	if (IS_ERR(mp->mem_res.phy_base))
-		return PTR_ERR(mp->mem_res.phy_base);
-
-	return 0;
+	return PTR_ERR_OR_ZERO(mp->mem_res.phy_base);
 }

 static void meson_pcie_power_on(struct meson_pcie *mp)
@@ -259,10 +256,7 @@ static int meson_pcie_probe_clocks(struct meson_pcie *mp)
 		return PTR_ERR(res->general_clk);

 	res->clk = meson_pcie_probe_clock(dev, "pcie", 0);
-	if (IS_ERR(res->clk))
-		return PTR_ERR(res->clk);
-
-	return 0;
+	return PTR_ERR_OR_ZERO(res->clk);
 }

 static inline void meson_elb_writel(struct meson_pcie *mp, u32 val, u32 reg)
diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
index c19617a912bd..75b1f1dde747 100644
--- a/drivers/pci/controller/dwc/pcie-kirin.c
+++ b/drivers/pci/controller/dwc/pcie-kirin.c
@@ -138,10 +138,7 @@ static long kirin_pcie_get_clk(struct kirin_pcie *kirin_pcie,
 		return PTR_ERR(kirin_pcie->apb_sys_clk);

 	kirin_pcie->pcie_aclk = devm_clk_get(dev, "pcie_aclk");
-	if (IS_ERR(kirin_pcie->pcie_aclk))
-		return PTR_ERR(kirin_pcie->pcie_aclk);
-
-	return 0;
+	return PTR_ERR_OR_ZERO(kirin_pcie->pcie_aclk);
 }

 static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
@@ -174,10 +171,7 @@ static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,

 	kirin_pcie->sysctrl =
 		syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl");
-	if (IS_ERR(kirin_pcie->sysctrl))
-		return PTR_ERR(kirin_pcie->sysctrl);
-
-	return 0;
+	return PTR_ERR_OR_ZERO(kirin_pcie->sysctrl);
 }

 static int kirin_pcie_phy_init(struct kirin_pcie *kirin_pcie)
--
2.23.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 v2] soc: fsl: dpio: Add support for QBMan ring bulk enqueue.
From: Roy Pledge @ 2019-09-06 18:55 UTC (permalink / raw)
  To: Youri Querry, Leo Li, linux-kernel@vger.kernel.org,
	linuxppc-dev@lists.ozlabs.org,
	linux-arm-kernel@lists.infradead.org, Ioana Ciocoi Radulescu,
	Ioana Ciornei
In-Reply-To: <1567710063-922-1-git-send-email-youri.querry_1@nxp.com>

On 9/5/2019 3:01 PM, Youri Querry wrote:
> The QBMan frame descriptor enqueuing is changed from array
>  mode (a single frame enqueue at a time) to bulk ring mode.
>
> This new mode allows the enqueuing of multiple frames in one operation.
> The original interface is kept but use the bulk enqueue of one frame
>
> Signed-off-by: Youri Querry <youri.querry_1@nxp.com>
Acked-by: Roy Pledge <roy.pledge@nxp.com>
> ---
>  drivers/soc/fsl/dpio/dpio-service.c |  69 +++-
>  drivers/soc/fsl/dpio/qbman-portal.c | 772 ++++++++++++++++++++++++++++++++----
>  drivers/soc/fsl/dpio/qbman-portal.h | 175 +++++++-
>  3 files changed, 935 insertions(+), 81 deletions(-)
>
> diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c
> index b9539ef..4eb53ee 100644
> --- a/drivers/soc/fsl/dpio/dpio-service.c
> +++ b/drivers/soc/fsl/dpio/dpio-service.c
> @@ -1,7 +1,7 @@
>  // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
>  /*
>   * Copyright 2014-2016 Freescale Semiconductor Inc.
> - * Copyright 2016 NXP
> + * Copyright 2016-2019 NXP
>   *
>   */
>  #include <linux/types.h>
> @@ -435,6 +435,69 @@ int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d,
>  EXPORT_SYMBOL(dpaa2_io_service_enqueue_fq);
>  
>  /**
> + * dpaa2_io_service_enqueue_multiple_fq() - Enqueue multiple frames
> + * to a frame queue using one fqid.
> + * @d: the given DPIO service.
> + * @fqid: the given frame queue id.
> + * @fd: the list of frame descriptors enqueued.
> + * @nb: number of frames to be enqueued
> + *
> + * Return the number of enqueued frames (0 if EQCR is busy)
> + * or -ENODEV if there is no dpio service.
> + */
> +int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d,
> +				u32 fqid,
> +				const struct dpaa2_fd *fd,
> +				int nb)
> +{
> +	struct qbman_eq_desc ed;
> +
> +	d = service_select(d);
> +	if (!d)
> +		return -ENODEV;
> +
> +	qbman_eq_desc_clear(&ed);
> +	qbman_eq_desc_set_no_orp(&ed, 0);
> +	qbman_eq_desc_set_fq(&ed, fqid);
> +
> +	return qbman_swp_enqueue_multiple(d->swp, &ed, fd, 0, nb);
> +}
> +EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_fq);
> +
> +/**
> + * dpaa2_io_service_enqueue_multiple_desc_fq() - Enqueue multiple frames
> + * to different frame queue using a list of fqids.
> + * @d: the given DPIO service.
> + * @fqid: the given list of frame queue ids.
> + * @fd: the list of frame descriptors enqueued.
> + * @nb: number of frames to be enqueued
> + *
> + * Return the number of enqueued frames (0 if EQCR is busy)
> + * or -ENODEV if there is no dpio service.
> + */
> +int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d,
> +				u32 *fqid,
> +				const struct dpaa2_fd *fd,
> +				int nb)
> +{
> +	int i;
> +	struct qbman_eq_desc_min ed[32];
> +
> +	d = service_select(d);
> +	if (!d)
> +		return -ENODEV;
> +
> +	for (i = 0; i < nb; i++) {
> +		qbman_eq_desc_min_clear(&ed[i]);
> +		qbman_eq_desc_set_no_orp_min(&ed[i], 0);
> +		qbman_eq_desc_set_min_fq(&ed[i], fqid[i]);
> +	}
> +
> +	return qbman_swp_enqueue_multiple_desc(d->swp, &ed[0], fd, nb);
> +}
> +EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_desc_fq);
> +
> +/**
>   * dpaa2_io_service_enqueue_qd() - Enqueue a frame to a QD.
>   * @d: the given DPIO service.
>   * @qdid: the given queuing destination id.
> @@ -528,7 +591,7 @@ EXPORT_SYMBOL_GPL(dpaa2_io_service_acquire);
>  
>  /**
>   * dpaa2_io_store_create() - Create the dma memory storage for dequeue result.
> - * @max_frames: the maximum number of dequeued result for frames, must be <= 16.
> + * @max_frames: the maximum number of dequeued result for frames, must be <= 32.
>   * @dev:        the device to allow mapping/unmapping the DMAable region.
>   *
>   * The size of the storage is "max_frames*sizeof(struct dpaa2_dq)".
> @@ -543,7 +606,7 @@ struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames,
>  	struct dpaa2_io_store *ret;
>  	size_t size;
>  
> -	if (!max_frames || (max_frames > 16))
> +	if (!max_frames || (max_frames > 32))
>  		return NULL;
>  
>  	ret = kmalloc(sizeof(*ret), GFP_KERNEL);
> diff --git a/drivers/soc/fsl/dpio/qbman-portal.c b/drivers/soc/fsl/dpio/qbman-portal.c
> index c66f5b7..0ed2c8f 100644
> --- a/drivers/soc/fsl/dpio/qbman-portal.c
> +++ b/drivers/soc/fsl/dpio/qbman-portal.c
> @@ -1,13 +1,14 @@
>  // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
>  /*
>   * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
> - * Copyright 2016 NXP
> + * Copyright 2016-2019 NXP
>   *
>   */
>  
>  #include <asm/cacheflush.h>
>  #include <linux/io.h>
>  #include <linux/slab.h>
> +#include <linux/spinlock.h>
>  #include <soc/fsl/dpaa2-global.h>
>  
>  #include "qbman-portal.h"
> @@ -28,6 +29,7 @@
>  
>  /* CINH register offsets */
>  #define QBMAN_CINH_SWP_EQCR_PI      0x800
> +#define QBMAN_CINH_SWP_EQCR_CI	    0x840
>  #define QBMAN_CINH_SWP_EQAR    0x8c0
>  #define QBMAN_CINH_SWP_CR_RT        0x900
>  #define QBMAN_CINH_SWP_VDQCR_RT     0x940
> @@ -51,6 +53,8 @@
>  #define QBMAN_CENA_SWP_CR      0x600
>  #define QBMAN_CENA_SWP_RR(vb)  (0x700 + ((u32)(vb) >> 1))
>  #define QBMAN_CENA_SWP_VDQCR   0x780
> +#define QBMAN_CENA_SWP_EQCR_CI 0x840
> +#define QBMAN_CENA_SWP_EQCR_CI_MEMBACK 0x1840
>  
>  /* CENA register offsets in memory-backed mode */
>  #define QBMAN_CENA_SWP_DQRR_MEM(n)  (0x800 + ((u32)(n) << 6))
> @@ -78,6 +82,12 @@
>  /* opaque token for static dequeues */
>  #define QMAN_SDQCR_TOKEN    0xbb
>  
> +#define QBMAN_EQCR_DCA_IDXMASK          0x0f
> +#define QBMAN_ENQUEUE_FLAG_DCA          (1ULL << 31)
> +
> +#define EQ_DESC_SIZE_WITHOUT_FD 29
> +#define EQ_DESC_SIZE_FD_START 32
> +
>  enum qbman_sdqcr_dct {
>  	qbman_sdqcr_dct_null = 0,
>  	qbman_sdqcr_dct_prio_ics,
> @@ -90,6 +100,82 @@ enum qbman_sdqcr_fc {
>  	qbman_sdqcr_fc_up_to_3 = 1
>  };
>  
> +/* Internal Function declaration */
> +static int qbman_swp_enqueue_ring_mode_direct(struct qbman_swp *s,
> +					      const struct qbman_eq_desc *d,
> +					      const struct dpaa2_fd *fd);
> +static int qbman_swp_enqueue_ring_mode_mem_back(struct qbman_swp *s,
> +						const struct qbman_eq_desc *d,
> +						const struct dpaa2_fd *fd);
> +static int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s,
> +					     const struct qbman_eq_desc *d,
> +					     const struct dpaa2_fd *fd,
> +					     uint32_t *flags,
> +					     int num_frames);
> +static int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s,
> +					       const struct qbman_eq_desc *d,
> +					       const struct dpaa2_fd *fd,
> +					       uint32_t *flags,
> +					       int num_frames);
> +static int
> +qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s,
> +				       const struct qbman_eq_desc_min *d,
> +				       const struct dpaa2_fd *fd,
> +				       int num_frames);
> +static
> +int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s,
> +					     const struct qbman_eq_desc_min *d,
> +					     const struct dpaa2_fd *fd,
> +					     int num_frames);
> +static int qbman_swp_pull_direct(struct qbman_swp *s,
> +				 struct qbman_pull_desc *d);
> +static int qbman_swp_pull_mem_back(struct qbman_swp *s,
> +				   struct qbman_pull_desc *d);
> +
> +const struct dpaa2_dq *qbman_swp_dqrr_next_direct(struct qbman_swp *s);
> +const struct dpaa2_dq *qbman_swp_dqrr_next_mem_back(struct qbman_swp *s);
> +
> +static int qbman_swp_release_direct(struct qbman_swp *s,
> +				    const struct qbman_release_desc *d,
> +				    const u64 *buffers,
> +				    unsigned int num_buffers);
> +static int qbman_swp_release_mem_back(struct qbman_swp *s,
> +				      const struct qbman_release_desc *d,
> +				      const u64 *buffers,
> +				      unsigned int num_buffers);
> +
> +/* Function pointers */
> +int (*qbman_swp_enqueue_ring_mode_ptr)(struct qbman_swp *s,
> +				       const struct qbman_eq_desc *d,
> +				       const struct dpaa2_fd *fd)
> +	= qbman_swp_enqueue_ring_mode_direct;
> +
> +int (*qbman_swp_enqueue_multiple_ptr)(struct qbman_swp *s,
> +				      const struct qbman_eq_desc *d,
> +				      const struct dpaa2_fd *fd,
> +				      uint32_t *flags,
> +					     int num_frames)
> +	= qbman_swp_enqueue_multiple_direct;
> +
> +int
> +(*qbman_swp_enqueue_multiple_desc_ptr)(struct qbman_swp *s,
> +				       const struct qbman_eq_desc_min *d,
> +				       const struct dpaa2_fd *fd,
> +				       int num_frames)
> +	= qbman_swp_enqueue_multiple_desc_direct;
> +
> +int (*qbman_swp_pull_ptr)(struct qbman_swp *s, struct qbman_pull_desc *d)
> +			= qbman_swp_pull_direct;
> +
> +const struct dpaa2_dq *(*qbman_swp_dqrr_next_ptr)(struct qbman_swp *s)
> +			= qbman_swp_dqrr_next_direct;
> +
> +int (*qbman_swp_release_ptr)(struct qbman_swp *s,
> +			     const struct qbman_release_desc *d,
> +			     const u64 *buffers,
> +			     unsigned int num_buffers)
> +			= qbman_swp_release_direct;
> +
>  /* Portal Access */
>  
>  static inline u32 qbman_read_register(struct qbman_swp *p, u32 offset)
> @@ -146,6 +232,15 @@ static inline u32 qbman_set_swp_cfg(u8 max_fill, u8 wn,	u8 est, u8 rpm, u8 dcm,
>  
>  #define QMAN_RT_MODE	   0x00000100
>  
> +static inline u8 qm_cyc_diff(u8 ringsize, u8 first, u8 last)
> +{
> +	/* 'first' is included, 'last' is excluded */
> +	if (first <= last)
> +		return last - first;
> +	else
> +		return (2 * ringsize) - (first - last);
> +}
> +
>  /**
>   * qbman_swp_init() - Create a functional object representing the given
>   *                    QBMan portal descriptor.
> @@ -156,8 +251,12 @@ static inline u32 qbman_set_swp_cfg(u8 max_fill, u8 wn,	u8 est, u8 rpm, u8 dcm,
>   */
>  struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d)
>  {
> -	struct qbman_swp *p = kmalloc(sizeof(*p), GFP_KERNEL);
> +	struct qbman_swp *p = kzalloc(sizeof(*p), GFP_KERNEL);
>  	u32 reg;
> +	u32 mask_size;
> +	u32 eqcr_pi;
> +
> +	spin_lock_init(&p->access_spinlock);
>  
>  	if (!p)
>  		return NULL;
> @@ -189,22 +288,39 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d)
>  	if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000)
>  		memset(p->addr_cena, 0, 64 * 1024);
>  
> -	reg = qbman_set_swp_cfg(p->dqrr.dqrr_size,
> -				1, /* Writes Non-cacheable */
> -				0, /* EQCR_CI stashing threshold */
> -				3, /* RPM: Valid bit mode, RCR in array mode */
> -				2, /* DCM: Discrete consumption ack mode */
> -				3, /* EPM: Valid bit mode, EQCR in array mode */
> -				1, /* mem stashing drop enable == TRUE */
> -				1, /* mem stashing priority == TRUE */
> -				1, /* mem stashing enable == TRUE */
> -				1, /* dequeue stashing priority == TRUE */
> -				0, /* dequeue stashing enable == FALSE */
> -				0); /* EQCR_CI stashing priority == FALSE */
> -	if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000)
> +	if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) {
> +
> +		reg = qbman_set_swp_cfg(p->dqrr.dqrr_size,
> +			0, /* Writes Non-cacheable */
> +			1, /* EQCR_CI stashing threshold */
> +			3, /* RPM: RCR in array mode */
> +			2, /* DCM: Discrete consumption ack */
> +			0, /* EPM: EQCR in ring mode */
> +			1, /* mem stashing drop enable */
> +			1, /* mem stashing priority enable */
> +			1, /* mem stashing enable */
> +			1, /* dequeue stashing priority enable */
> +			0, /* dequeue stashing enable */
> +			0); /* EQCR_CI stashing priority enable */
> +	} else {
> +		reg = qbman_set_swp_cfg(p->dqrr.dqrr_size,
> +			1, /* Writes Non-cacheable */
> +			0, /* EQCR_CI stashing threshold */
> +			3, /* RPM: RCR in array mode */
> +			2, /* DCM: Discrete consumption ack */
> +			2, /* EPM: EQCR in ring mode */
> +			1, /* mem stashing drop enable enable */
> +			1, /* mem stashing priority enable */
> +			1, /* mem stashing enable */
> +			1, /* dequeue stashing priority enable */
> +			0, /* dequeue stashing enable enable */
> +			0); /* EQCR_CI stashing priority enable */
> +	}
> +	if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) {
>  		reg |= 1 << SWP_CFG_CPBS_SHIFT | /* memory-backed mode */
>  		       1 << SWP_CFG_VPM_SHIFT |  /* VDQCR read triggered mode */
>  		       1 << SWP_CFG_CPM_SHIFT;   /* CR read triggered mode */
> +	}
>  
>  	qbman_write_register(p, QBMAN_CINH_SWP_CFG, reg);
>  	reg = qbman_read_register(p, QBMAN_CINH_SWP_CFG);
> @@ -225,6 +341,30 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d)
>  	 * applied when dequeues from a specific channel are enabled.
>  	 */
>  	qbman_write_register(p, QBMAN_CINH_SWP_SDQCR, 0);
> +
> +	p->eqcr.pi_ring_size = 8;
> +	if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) {
> +		p->eqcr.pi_ring_size = 32;
> +		qbman_swp_enqueue_ring_mode_ptr =
> +			qbman_swp_enqueue_ring_mode_mem_back;
> +		qbman_swp_enqueue_multiple_ptr =
> +			qbman_swp_enqueue_multiple_mem_back;
> +		qbman_swp_enqueue_multiple_desc_ptr =
> +			qbman_swp_enqueue_multiple_desc_mem_back;
> +		qbman_swp_pull_ptr = qbman_swp_pull_mem_back;
> +		qbman_swp_dqrr_next_ptr = qbman_swp_dqrr_next_mem_back;
> +		qbman_swp_release_ptr = qbman_swp_release_mem_back;
> +	}
> +
> +	for (mask_size = p->eqcr.pi_ring_size; mask_size > 0; mask_size >>= 1)
> +		p->eqcr.pi_ci_mask = (p->eqcr.pi_ci_mask << 1) + 1;
> +	eqcr_pi = qbman_read_register(p, QBMAN_CINH_SWP_EQCR_PI);
> +	p->eqcr.pi = eqcr_pi & p->eqcr.pi_ci_mask;
> +	p->eqcr.pi_vb = eqcr_pi & QB_VALID_BIT;
> +	p->eqcr.ci = qbman_read_register(p, QBMAN_CINH_SWP_EQCR_CI)
> +			& p->eqcr.pi_ci_mask;
> +	p->eqcr.available = p->eqcr.pi_ring_size;
> +
>  	return p;
>  }
>  
> @@ -378,6 +518,7 @@ enum qb_enqueue_commands {
>  #define QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT      2
>  #define QB_ENQUEUE_CMD_IRQ_ON_DISPATCH_SHIFT 3
>  #define QB_ENQUEUE_CMD_TARGET_TYPE_SHIFT     4
> +#define QB_ENQUEUE_CMD_DCA_EN_SHIFT          7
>  
>  /**
>   * qbman_eq_desc_clear() - Clear the contents of a descriptor to
> @@ -389,6 +530,16 @@ void qbman_eq_desc_clear(struct qbman_eq_desc *d)
>  }
>  
>  /**
> + * qbman_eq_desc_min_clear() - Clear the contents of a minimal
> + *			   enqueue descriptor to
> + *                         default/starting state.
> + */
> +void qbman_eq_desc_min_clear(struct qbman_eq_desc_min *d)
> +{
> +	memset(d, 0, sizeof(*d));
> +}
> +
> +/**
>   * qbman_eq_desc_set_no_orp() - Set enqueue descriptor without orp
>   * @d:                the enqueue descriptor.
>   * @response_success: 1 = enqueue with response always; 0 = enqueue with
> @@ -403,6 +554,22 @@ void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success)
>  		d->verb |= enqueue_rejects_to_fq;
>  }
>  
> +/**
> + * qbman_eq_desc_set_no_orp_min() - Set minimal enqueue descriptor without orp
> + * @d:                the enqueue descriptor.
> + * @response_success: 1 = enqueue with response always; 0 = enqueue with
> + *                    rejections returned on a FQ.
> + */
> +void qbman_eq_desc_set_no_orp_min(struct qbman_eq_desc_min *d,
> +				  int respond_success)
> +{
> +	d->verb &= ~(1 << QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT);
> +	if (respond_success)
> +		d->verb |= enqueue_response_always;
> +	else
> +		d->verb |= enqueue_rejects_to_fq;
> +}
> +
>  /*
>   * Exactly one of the following descriptor "targets" should be set. (Calling any
>   * one of these will replace the effect of any prior call to one of these.)
> @@ -422,6 +589,17 @@ void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, u32 fqid)
>  }
>  
>  /**
> + * qbman_eq_desc_set_min_fq() - set the FQ for the minimal enqueue command
> + * @d:    the enqueue descriptor
> + * @fqid: the id of the frame queue to be enqueued
> + */
> +void qbman_eq_desc_set_min_fq(struct qbman_eq_desc_min *d, u32 fqid)
> +{
> +	d->verb &= ~(1 << QB_ENQUEUE_CMD_TARGET_TYPE_SHIFT);
> +	d->tgtid = cpu_to_le32(fqid);
> +}
> +
> +/**
>   * qbman_eq_desc_set_qd() - Set Queuing Destination for the enqueue command
>   * @d:       the enqueue descriptor
>   * @qdid:    the id of the queuing destination to be enqueued
> @@ -453,41 +631,340 @@ static inline void qbman_write_eqcr_am_rt_register(struct qbman_swp *p,
>  				     QMAN_RT_MODE);
>  }
>  
> +#define QB_RT_BIT ((u32)0x100)
>  /**
> - * qbman_swp_enqueue() - Issue an enqueue command
> + * qbman_swp_enqueue_ring_mode_direct() - Issue an enqueue command
>   * @s:  the software portal used for enqueue
>   * @d:  the enqueue descriptor
>   * @fd: the frame descriptor to be enqueued
>   *
> - * Please note that 'fd' should only be NULL if the "action" of the
> - * descriptor is "orp_hole" or "orp_nesn".
> + * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready.
> + */
> +static
> +int qbman_swp_enqueue_ring_mode_direct(struct qbman_swp *s,
> +				       const struct qbman_eq_desc *d,
> +				       const struct dpaa2_fd *fd)
> +{
> +	int flags = 0;
> +	int ret = qbman_swp_enqueue_multiple_direct(s, d, fd, &flags, 1);
> +
> +	if (ret >= 0)
> +		ret = 0;
> +	else
> +		ret = -EBUSY;
> +	return  ret;
> +}
> +
> +/**
> + * qbman_swp_enqueue_ring_mode_mem_back() - Issue an enqueue command
> + * @s:  the software portal used for enqueue
> + * @d:  the enqueue descriptor
> + * @fd: the frame descriptor to be enqueued
>   *
>   * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready.
>   */
> -int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d,
> -		      const struct dpaa2_fd *fd)
> +static
> +int qbman_swp_enqueue_ring_mode_mem_back(struct qbman_swp *s,
> +					 const struct qbman_eq_desc *d,
> +					 const struct dpaa2_fd *fd)
>  {
> -	struct qbman_eq_desc *p;
> -	u32 eqar = qbman_read_register(s, QBMAN_CINH_SWP_EQAR);
> +	int flags = 0;
> +	int ret = qbman_swp_enqueue_multiple_mem_back(s, d, fd, &flags, 1);
>  
> -	if (!EQAR_SUCCESS(eqar))
> -		return -EBUSY;
> +	if (ret >= 0)
> +		ret = 0;
> +	else
> +		ret = -EBUSY;
> +	return  ret;
> +}
>  
> -	p = qbman_get_cmd(s, QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar)));
> -	memcpy(&p->dca, &d->dca, 31);
> -	memcpy(&p->fd, fd, sizeof(*fd));
> +/**
> + * qbman_swp_enqueue_multiple_direct() - Issue a multi enqueue command
> + * using one enqueue descriptor
> + * @s:  the software portal used for enqueue
> + * @d:  the enqueue descriptor
> + * @fd: table pointer of frame descriptor table to be enqueued
> + * @flags: table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL
> + * @num_frames: number of fd to be enqueued
> + *
> + * Return the number of fd enqueued, or a negative error number.
> + */
> +static
> +int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s,
> +				      const struct qbman_eq_desc *d,
> +				      const struct dpaa2_fd *fd,
> +				      uint32_t *flags,
> +				      int num_frames)
> +{
> +	uint32_t *p = NULL;
> +	const uint32_t *cl = (uint32_t *)d;
> +	uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask;
> +	int i, num_enqueued = 0;
> +
> +	half_mask = (s->eqcr.pi_ci_mask>>1);
> +	full_mask = s->eqcr.pi_ci_mask;
> +
> +	if (!s->eqcr.available) {
> +		eqcr_ci = s->eqcr.ci;
> +		p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI;
> +		s->eqcr.ci = __raw_readl(p) & full_mask;
> +
> +		s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size,
> +					eqcr_ci, s->eqcr.ci);
> +		if (!s->eqcr.available)
> +			return 0;
> +	}
>  
> -	if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) {
> -		/* Set the verb byte, have to substitute in the valid-bit */
> -		dma_wmb();
> -		p->verb = d->verb | EQAR_VB(eqar);
> -	} else {
> -		p->verb = d->verb | EQAR_VB(eqar);
> -		dma_wmb();
> -		qbman_write_eqcr_am_rt_register(s, EQAR_IDX(eqar));
> +	eqcr_pi = s->eqcr.pi;
> +	num_enqueued = (s->eqcr.available < num_frames) ?
> +			s->eqcr.available : num_frames;
> +	s->eqcr.available -= num_enqueued;
> +	/* Fill in the EQCR ring */
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		/* Skip copying the verb */
> +		memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1);
> +		memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)],
> +		       &fd[i], sizeof(*fd));
> +		eqcr_pi++;
>  	}
>  
> -	return 0;
> +	dma_wmb();
> +
> +	/* Set the verb byte, have to substitute in the valid-bit */
> +	eqcr_pi = s->eqcr.pi;
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		p[0] = cl[0] | s->eqcr.pi_vb;
> +		if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) {
> +			struct qbman_eq_desc *d = (struct qbman_eq_desc *)p;
> +
> +			d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
> +				((flags[i]) & QBMAN_EQCR_DCA_IDXMASK);
> +		}
> +		eqcr_pi++;
> +		if (!(eqcr_pi & half_mask))
> +			s->eqcr.pi_vb ^= QB_VALID_BIT;
> +	}
> +
> +	s->eqcr.pi = (s->eqcr.pi + num_enqueued) & full_mask;
> +
> +	return num_enqueued;
> +}
> +
> +/**
> + * qbman_swp_enqueue_multiple_mem_back() - Issue a multi enqueue command
> + * using one enqueue descriptor
> + * @s:  the software portal used for enqueue
> + * @d:  the enqueue descriptor
> + * @fd: table pointer of frame descriptor table to be enqueued
> + * @flags: table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL
> + * @num_frames: number of fd to be enqueued
> + *
> + * Return the number of fd enqueued, or a negative error number.
> + */
> +static
> +int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s,
> +					const struct qbman_eq_desc *d,
> +					const struct dpaa2_fd *fd,
> +					uint32_t *flags,
> +					int num_frames)
> +{
> +	uint32_t *p = NULL;
> +	const uint32_t *cl = (uint32_t *)(d);
> +	uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask;
> +	int i, num_enqueued = 0;
> +	unsigned long irq_flags;
> +
> +	spin_lock(&s->access_spinlock);
> +	local_irq_save(irq_flags);
> +
> +	half_mask = (s->eqcr.pi_ci_mask>>1);
> +	full_mask = s->eqcr.pi_ci_mask;
> +	if (!s->eqcr.available) {
> +		eqcr_ci = s->eqcr.ci;
> +		p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK;
> +		s->eqcr.ci = __raw_readl(p) & full_mask;
> +		s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size,
> +					eqcr_ci, s->eqcr.ci);
> +		if (!s->eqcr.available) {
> +			local_irq_restore(irq_flags);
> +			spin_unlock(&s->access_spinlock);
> +			return 0;
> +		}
> +	}
> +
> +	eqcr_pi = s->eqcr.pi;
> +	num_enqueued = (s->eqcr.available < num_frames) ?
> +			s->eqcr.available : num_frames;
> +	s->eqcr.available -= num_enqueued;
> +	/* Fill in the EQCR ring */
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		/* Skip copying the verb */
> +		memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1);
> +		memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)],
> +		       &fd[i], sizeof(*fd));
> +		eqcr_pi++;
> +	}
> +
> +	/* Set the verb byte, have to substitute in the valid-bit */
> +	eqcr_pi = s->eqcr.pi;
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		p[0] = cl[0] | s->eqcr.pi_vb;
> +		if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) {
> +			struct qbman_eq_desc *d = (struct qbman_eq_desc *)p;
> +
> +			d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
> +				((flags[i]) & QBMAN_EQCR_DCA_IDXMASK);
> +		}
> +		eqcr_pi++;
> +		if (!(eqcr_pi & half_mask))
> +			s->eqcr.pi_vb ^= QB_VALID_BIT;
> +	}
> +	s->eqcr.pi = eqcr_pi & full_mask;
> +
> +	dma_wmb();
> +	qbman_write_register(s, QBMAN_CINH_SWP_EQCR_PI,
> +				(QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb);
> +	local_irq_restore(irq_flags);
> +	spin_unlock(&s->access_spinlock);
> +
> +	return num_enqueued;
> +}
> +
> +/**
> + * qbman_swp_enqueue_multiple_desc_direct() - Issue a multi enqueue command
> + * using multiple enqueue descriptor
> + * @s:  the software portal used for enqueue
> + * @d:  table of minimal enqueue descriptor
> + * @fd: table pointer of frame descriptor table to be enqueued
> + * @num_frames: number of fd to be enqueued
> + *
> + * Return the number of fd enqueued, or a negative error number.
> + */
> +static
> +int qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s,
> +					   const struct qbman_eq_desc_min *d,
> +					   const struct dpaa2_fd *fd,
> +					   int num_frames)
> +{
> +	uint32_t *p;
> +	const uint32_t *cl;
> +	uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask;
> +	int i, num_enqueued = 0;
> +
> +	half_mask = (s->eqcr.pi_ci_mask>>1);
> +	full_mask = s->eqcr.pi_ci_mask;
> +	if (!s->eqcr.available) {
> +		eqcr_ci = s->eqcr.ci;
> +		p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI;
> +		s->eqcr.ci = __raw_readl(p) & full_mask;
> +		s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size,
> +					eqcr_ci, s->eqcr.ci);
> +		if (!s->eqcr.available)
> +			return 0;
> +	}
> +
> +	eqcr_pi = s->eqcr.pi;
> +	num_enqueued = (s->eqcr.available < num_frames) ?
> +			s->eqcr.available : num_frames;
> +	s->eqcr.available -= num_enqueued;
> +	/* Fill in the EQCR ring */
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		cl = (uint32_t *)(&d[i]);
> +		/* Skip copying the verb */
> +		memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1);
> +		memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)],
> +		       &fd[i], sizeof(*fd));
> +		eqcr_pi++;
> +	}
> +
> +	dma_wmb();
> +
> +	/* Set the verb byte, have to substitute in the valid-bit */
> +	eqcr_pi = s->eqcr.pi;
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		cl = (uint32_t *)(&d[i]);
> +		p[0] = cl[0] | s->eqcr.pi_vb;
> +		eqcr_pi++;
> +		if (!(eqcr_pi & half_mask))
> +			s->eqcr.pi_vb ^= QB_VALID_BIT;
> +	}
> +	s->eqcr.pi = (s->eqcr.pi + num_enqueued) & full_mask;
> +
> +	return num_enqueued;
> +}
> +
> +/**
> + * qbman_swp_enqueue_multiple_desc_mem_back() - Issue a multi enqueue command
> + * using multiple enqueue descriptor
> + * @s:  the software portal used for enqueue
> + * @d:  table of minimal enqueue descriptor
> + * @fd: table pointer of frame descriptor table to be enqueued
> + * @num_frames: number of fd to be enqueued
> + *
> + * Return the number of fd enqueued, or a negative error number.
> + */
> +static
> +int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s,
> +					     const struct qbman_eq_desc_min *d,
> +					     const struct dpaa2_fd *fd,
> +					     int num_frames)
> +{
> +	uint32_t *p;
> +	const uint32_t *cl;
> +	uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask;
> +	int i, num_enqueued = 0;
> +
> +	half_mask = (s->eqcr.pi_ci_mask>>1);
> +	full_mask = s->eqcr.pi_ci_mask;
> +	if (!s->eqcr.available) {
> +		eqcr_ci = s->eqcr.ci;
> +		p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK;
> +		s->eqcr.ci = __raw_readl(p) & full_mask;
> +		s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size,
> +					eqcr_ci, s->eqcr.ci);
> +		if (!s->eqcr.available)
> +			return 0;
> +	}
> +
> +	eqcr_pi = s->eqcr.pi;
> +	num_enqueued = (s->eqcr.available < num_frames) ?
> +			s->eqcr.available : num_frames;
> +	s->eqcr.available -= num_enqueued;
> +	/* Fill in the EQCR ring */
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		cl = (uint32_t *)(&d[i]);
> +		/* Skip copying the verb */
> +		memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1);
> +		memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)],
> +		       &fd[i], sizeof(*fd));
> +		eqcr_pi++;
> +	}
> +
> +	/* Set the verb byte, have to substitute in the valid-bit */
> +	eqcr_pi = s->eqcr.pi;
> +	for (i = 0; i < num_enqueued; i++) {
> +		p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
> +		cl = (uint32_t *)(&d[i]);
> +		p[0] = cl[0] | s->eqcr.pi_vb;
> +		eqcr_pi++;
> +		if (!(eqcr_pi & half_mask))
> +			s->eqcr.pi_vb ^= QB_VALID_BIT;
> +	}
> +
> +	s->eqcr.pi = eqcr_pi & full_mask;
> +
> +	dma_wmb();
> +	qbman_write_register(s, QBMAN_CINH_SWP_EQCR_PI,
> +				(QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb);
> +
> +	return num_enqueued;
>  }
>  
>  /* Static (push) dequeue */
> @@ -645,7 +1122,7 @@ void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, u32 chid,
>  }
>  
>  /**
> - * qbman_swp_pull() - Issue the pull dequeue command
> + * qbman_swp_pull_direct() - Issue the pull dequeue command
>   * @s: the software portal object
>   * @d: the software portal descriptor which has been configured with
>   *     the set of qbman_pull_desc_set_*() calls
> @@ -653,7 +1130,7 @@ void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, u32 chid,
>   * Return 0 for success, and -EBUSY if the software portal is not ready
>   * to do pull dequeue.
>   */
> -int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d)
> +static int qbman_swp_pull_direct(struct qbman_swp *s, struct qbman_pull_desc *d)
>  {
>  	struct qbman_pull_desc *p;
>  
> @@ -671,18 +1148,48 @@ int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d)
>  	p->dq_src = d->dq_src;
>  	p->rsp_addr = d->rsp_addr;
>  	p->rsp_addr_virt = d->rsp_addr_virt;
> +	dma_wmb();
> +	/* Set the verb byte, have to substitute in the valid-bit */
> +	p->verb = d->verb | s->vdq.valid_bit;
> +	s->vdq.valid_bit ^= QB_VALID_BIT;
>  
> -	if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) {
> -		dma_wmb();
> -		/* Set the verb byte, have to substitute in the valid-bit */
> -		p->verb = d->verb | s->vdq.valid_bit;
> -		s->vdq.valid_bit ^= QB_VALID_BIT;
> -	} else {
> -		p->verb = d->verb | s->vdq.valid_bit;
> -		s->vdq.valid_bit ^= QB_VALID_BIT;
> -		dma_wmb();
> -		qbman_write_register(s, QBMAN_CINH_SWP_VDQCR_RT, QMAN_RT_MODE);
> +	return 0;
> +}
> +
> +/**
> + * qbman_swp_pull_mem_back() - Issue the pull dequeue command
> + * @s: the software portal object
> + * @d: the software portal descriptor which has been configured with
> + *     the set of qbman_pull_desc_set_*() calls
> + *
> + * Return 0 for success, and -EBUSY if the software portal is not ready
> + * to do pull dequeue.
> + */
> +static int qbman_swp_pull_mem_back(struct qbman_swp *s,
> +				   struct qbman_pull_desc *d)
> +{
> +	struct qbman_pull_desc *p;
> +
> +	if (!atomic_dec_and_test(&s->vdq.available)) {
> +		atomic_inc(&s->vdq.available);
> +		return -EBUSY;
>  	}
> +	s->vdq.storage = (void *)(uintptr_t)d->rsp_addr_virt;
> +	if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000)
> +		p = qbman_get_cmd(s, QBMAN_CENA_SWP_VDQCR);
> +	else
> +		p = qbman_get_cmd(s, QBMAN_CENA_SWP_VDQCR_MEM);
> +	p->numf = d->numf;
> +	p->tok = QMAN_DQ_TOKEN_VALID;
> +	p->dq_src = d->dq_src;
> +	p->rsp_addr = d->rsp_addr;
> +	p->rsp_addr_virt = d->rsp_addr_virt;
> +
> +	/* Set the verb byte, have to substitute in the valid-bit */
> +	p->verb = d->verb | s->vdq.valid_bit;
> +	s->vdq.valid_bit ^= QB_VALID_BIT;
> +	dma_wmb();
> +	qbman_write_register(s, QBMAN_CINH_SWP_VDQCR_RT, QMAN_RT_MODE);
>  
>  	return 0;
>  }
> @@ -690,14 +1197,14 @@ int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d)
>  #define QMAN_DQRR_PI_MASK   0xf
>  
>  /**
> - * qbman_swp_dqrr_next() - Get an valid DQRR entry
> + * qbman_swp_dqrr_next_direct() - Get an valid DQRR entry
>   * @s: the software portal object
>   *
>   * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry
>   * only once, so repeated calls can return a sequence of DQRR entries, without
>   * requiring they be consumed immediately or in any particular order.
>   */
> -const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
> +const struct dpaa2_dq *qbman_swp_dqrr_next_direct(struct qbman_swp *s)
>  {
>  	u32 verb;
>  	u32 response_verb;
> @@ -740,10 +1247,7 @@ const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
>  				       QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
>  	}
>  
> -	if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000)
> -		p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
> -	else
> -		p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR_MEM(s->dqrr.next_idx));
> +	p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
>  	verb = p->dq.verb;
>  
>  	/*
> @@ -785,6 +1289,98 @@ const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
>  }
>  
>  /**
> + * qbman_swp_dqrr_next_mem_back() - Get an valid DQRR entry
> + * @s: the software portal object
> + *
> + * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry
> + * only once, so repeated calls can return a sequence of DQRR entries, without
> + * requiring they be consumed immediately or in any particular order.
> + */
> +const struct dpaa2_dq *qbman_swp_dqrr_next_mem_back(struct qbman_swp *s)
> +{
> +	u32 verb;
> +	u32 response_verb;
> +	u32 flags;
> +	struct dpaa2_dq *p;
> +
> +	/* Before using valid-bit to detect if something is there, we have to
> +	 * handle the case of the DQRR reset bug...
> +	 */
> +	if (unlikely(s->dqrr.reset_bug)) {
> +		/*
> +		 * We pick up new entries by cache-inhibited producer index,
> +		 * which means that a non-coherent mapping would require us to
> +		 * invalidate and read *only* once that PI has indicated that
> +		 * there's an entry here. The first trip around the DQRR ring
> +		 * will be much less efficient than all subsequent trips around
> +		 * it...
> +		 */
> +		u8 pi = qbman_read_register(s, QBMAN_CINH_SWP_DQPI) &
> +			QMAN_DQRR_PI_MASK;
> +
> +		/* there are new entries if pi != next_idx */
> +		if (pi == s->dqrr.next_idx)
> +			return NULL;
> +
> +		/*
> +		 * if next_idx is/was the last ring index, and 'pi' is
> +		 * different, we can disable the workaround as all the ring
> +		 * entries have now been DMA'd to so valid-bit checking is
> +		 * repaired. Note: this logic needs to be based on next_idx
> +		 * (which increments one at a time), rather than on pi (which
> +		 * can burst and wrap-around between our snapshots of it).
> +		 */
> +		if (s->dqrr.next_idx == (s->dqrr.dqrr_size - 1)) {
> +			pr_debug("next_idx=%d, pi=%d, clear reset bug\n",
> +				 s->dqrr.next_idx, pi);
> +			s->dqrr.reset_bug = 0;
> +		}
> +		prefetch(qbman_get_cmd(s,
> +				       QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
> +	}
> +
> +	p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR_MEM(s->dqrr.next_idx));
> +	verb = p->dq.verb;
> +
> +	/*
> +	 * If the valid-bit isn't of the expected polarity, nothing there. Note,
> +	 * in the DQRR reset bug workaround, we shouldn't need to skip these
> +	 * check, because we've already determined that a new entry is available
> +	 * and we've invalidated the cacheline before reading it, so the
> +	 * valid-bit behaviour is repaired and should tell us what we already
> +	 * knew from reading PI.
> +	 */
> +	if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) {
> +		prefetch(qbman_get_cmd(s,
> +			 QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
> +		return NULL;
> +	}
> +	/*
> +	 * There's something there. Move "next_idx" attention to the next ring
> +	 * entry (and prefetch it) before returning what we found.
> +	 */
> +	s->dqrr.next_idx++;
> +	s->dqrr.next_idx &= s->dqrr.dqrr_size - 1; /* Wrap around */
> +	if (!s->dqrr.next_idx)
> +		s->dqrr.valid_bit ^= QB_VALID_BIT;
> +
> +	/*
> +	 * If this is the final response to a volatile dequeue command
> +	 * indicate that the vdq is available
> +	 */
> +	flags = p->dq.stat;
> +	response_verb = verb & QBMAN_RESULT_MASK;
> +	if (response_verb == QBMAN_RESULT_DQ &&
> +	    (flags & DPAA2_DQ_STAT_VOLATILE) &&
> +	    (flags & DPAA2_DQ_STAT_EXPIRED))
> +		atomic_inc(&s->vdq.available);
> +
> +	prefetch(qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
> +
> +	return p;
> +}
> +
> +/**
>   * qbman_swp_dqrr_consume() -  Consume DQRR entries previously returned from
>   *                             qbman_swp_dqrr_next().
>   * @s: the software portal object
> @@ -872,7 +1468,7 @@ void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable)
>  #define RAR_SUCCESS(rar) ((rar) & 0x100)
>  
>  /**
> - * qbman_swp_release() - Issue a buffer release command
> + * qbman_swp_release_direct() - Issue a buffer release command
>   * @s:           the software portal object
>   * @d:           the release descriptor
>   * @buffers:     a pointer pointing to the buffer address to be released
> @@ -880,8 +1476,53 @@ void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable)
>   *
>   * Return 0 for success, -EBUSY if the release command ring is not ready.
>   */
> -int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d,
> -		      const u64 *buffers, unsigned int num_buffers)
> +int qbman_swp_release_direct(struct qbman_swp *s,
> +			     const struct qbman_release_desc *d,
> +			     const u64 *buffers, unsigned int num_buffers)
> +{
> +	int i;
> +	struct qbman_release_desc *p;
> +	u32 rar;
> +
> +	if (!num_buffers || num_buffers > 7)
> +		return -EINVAL;
> +
> +	rar = qbman_read_register(s, QBMAN_CINH_SWP_RAR);
> +	if (!RAR_SUCCESS(rar))
> +		return -EBUSY;
> +
> +	/* Start the release command */
> +	if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000)
> +		p = qbman_get_cmd(s, QBMAN_CENA_SWP_RCR(RAR_IDX(rar)));
> +	else
> +		p = qbman_get_cmd(s, QBMAN_CENA_SWP_RCR_MEM(RAR_IDX(rar)));
> +	/* Copy the caller's buffer pointers to the command */
> +	for (i = 0; i < num_buffers; i++)
> +		p->buf[i] = cpu_to_le64(buffers[i]);
> +	p->bpid = d->bpid;
> +
> +	/*
> +	 * Set the verb byte, have to substitute in the valid-bit
> +	 * and the number of buffers.
> +	 */
> +	dma_wmb();
> +	p->verb = d->verb | RAR_VB(rar) | num_buffers;
> +
> +	return 0;
> +}
> +
> +/**
> + * qbman_swp_release_mem_back() - Issue a buffer release command
> + * @s:           the software portal object
> + * @d:           the release descriptor
> + * @buffers:     a pointer pointing to the buffer address to be released
> + * @num_buffers: number of buffers to be released,  must be less than 8
> + *
> + * Return 0 for success, -EBUSY if the release command ring is not ready.
> + */
> +int qbman_swp_release_mem_back(struct qbman_swp *s,
> +			       const struct qbman_release_desc *d,
> +			       const u64 *buffers, unsigned int num_buffers)
>  {
>  	int i;
>  	struct qbman_release_desc *p;
> @@ -904,19 +1545,10 @@ int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d,
>  		p->buf[i] = cpu_to_le64(buffers[i]);
>  	p->bpid = d->bpid;
>  
> -	if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) {
> -		/*
> -		 * Set the verb byte, have to substitute in the valid-bit
> -		 * and the number of buffers.
> -		 */
> -		dma_wmb();
> -		p->verb = d->verb | RAR_VB(rar) | num_buffers;
> -	} else {
> -		p->verb = d->verb | RAR_VB(rar) | num_buffers;
> -		dma_wmb();
> -		qbman_write_register(s, QBMAN_CINH_SWP_RCR_AM_RT +
> -				     RAR_IDX(rar)  * 4, QMAN_RT_MODE);
> -	}
> +	p->verb = d->verb | RAR_VB(rar) | num_buffers;
> +	dma_wmb();
> +	qbman_write_register(s, QBMAN_CINH_SWP_RCR_AM_RT +
> +			     RAR_IDX(rar)  * 4, QMAN_RT_MODE);
>  
>  	return 0;
>  }
> diff --git a/drivers/soc/fsl/dpio/qbman-portal.h b/drivers/soc/fsl/dpio/qbman-portal.h
> index f3ec5d2..f4b6e47 100644
> --- a/drivers/soc/fsl/dpio/qbman-portal.h
> +++ b/drivers/soc/fsl/dpio/qbman-portal.h
> @@ -9,6 +9,13 @@
>  
>  #include <soc/fsl/dpaa2-fd.h>
>  
> +#define QMAN_REV_4000   0x04000000
> +#define QMAN_REV_4100   0x04010000
> +#define QMAN_REV_4101   0x04010001
> +#define QMAN_REV_5000   0x05000000
> +
> +#define QMAN_REV_MASK   0xffff0000
> +
>  struct dpaa2_dq;
>  struct qbman_swp;
>  
> @@ -67,6 +74,22 @@ enum qbman_pull_type_e {
>  #define QBMAN_FQ_XOFF		0x4e
>  
>  /* structure of enqueue descriptor */
> +struct qbman_eq_desc_min {
> +	u8 verb;
> +	u8 dca;
> +	__le16 seqnum;
> +	__le16 orpid;
> +	__le16 reserved1;
> +	__le32 tgtid;
> +	__le32 tag;
> +	__le16 qdbin;
> +	u8 qpri;
> +	u8 reserved[3];
> +	u8 wae;
> +	u8 rspid;
> +	__le64 rsp_addr;
> +};
> +
>  struct qbman_eq_desc {
>  	u8 verb;
>  	u8 dca;
> @@ -132,8 +155,48 @@ struct qbman_swp {
>  		u8 dqrr_size;
>  		int reset_bug; /* indicates dqrr reset workaround is needed */
>  	} dqrr;
> +
> +	struct {
> +		u32 pi;
> +		u32 pi_vb;
> +		u32 pi_ring_size;
> +		u32 pi_ci_mask;
> +		u32 ci;
> +		int available;
> +		u32 pend;
> +		u32 no_pfdr;
> +	} eqcr;
> +
> +	spinlock_t access_spinlock;
>  };
>  
> +/* Function pointers */
> +extern
> +int (*qbman_swp_enqueue_ring_mode_ptr)(struct qbman_swp *s,
> +				       const struct qbman_eq_desc *d,
> +				       const struct dpaa2_fd *fd);
> +extern
> +int (*qbman_swp_enqueue_multiple_ptr)(struct qbman_swp *s,
> +				      const struct qbman_eq_desc *d,
> +				      const struct dpaa2_fd *fd,
> +				      uint32_t *flags,
> +				      int num_frames);
> +extern
> +int (*qbman_swp_enqueue_multiple_desc_ptr)(struct qbman_swp *s,
> +					   const struct qbman_eq_desc_min *d,
> +					   const struct dpaa2_fd *fd,
> +					   int num_frames);
> +extern
> +int (*qbman_swp_pull_ptr)(struct qbman_swp *s, struct qbman_pull_desc *d);
> +extern
> +const struct dpaa2_dq *(*qbman_swp_dqrr_next_ptr)(struct qbman_swp *s);
> +extern
> +int (*qbman_swp_release_ptr)(struct qbman_swp *s,
> +			     const struct qbman_release_desc *d,
> +			     const u64 *buffers,
> +			     unsigned int num_buffers);
> +
> +/* Functions */
>  struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d);
>  void qbman_swp_finish(struct qbman_swp *p);
>  u32 qbman_swp_interrupt_read_status(struct qbman_swp *p);
> @@ -158,29 +221,25 @@ void qbman_pull_desc_set_wq(struct qbman_pull_desc *d, u32 wqid,
>  void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, u32 chid,
>  				 enum qbman_pull_type_e dct);
>  
> -int qbman_swp_pull(struct qbman_swp *p, struct qbman_pull_desc *d);
> -
> -const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s);
>  void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq);
>  
>  int qbman_result_has_new_result(struct qbman_swp *p, const struct dpaa2_dq *dq);
>  
>  void qbman_eq_desc_clear(struct qbman_eq_desc *d);
> +void qbman_eq_desc_min_clear(struct qbman_eq_desc_min *d);
>  void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success);
> +void qbman_eq_desc_set_no_orp_min(struct qbman_eq_desc_min *d,
> +				  int respond_success);
>  void qbman_eq_desc_set_token(struct qbman_eq_desc *d, u8 token);
>  void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, u32 fqid);
> +void qbman_eq_desc_set_min_fq(struct qbman_eq_desc_min *d, u32 fqid);
>  void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, u32 qdid,
>  			  u32 qd_bin, u32 qd_prio);
>  
> -int qbman_swp_enqueue(struct qbman_swp *p, const struct qbman_eq_desc *d,
> -		      const struct dpaa2_fd *fd);
> -
>  void qbman_release_desc_clear(struct qbman_release_desc *d);
>  void qbman_release_desc_set_bpid(struct qbman_release_desc *d, u16 bpid);
>  void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable);
>  
> -int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d,
> -		      const u64 *buffers, unsigned int num_buffers);
>  int qbman_swp_acquire(struct qbman_swp *s, u16 bpid, u64 *buffers,
>  		      unsigned int num_buffers);
>  int qbman_swp_alt_fq_state(struct qbman_swp *s, u32 fqid,
> @@ -194,6 +253,61 @@ void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, u8 cmd_verb);
>  void *qbman_swp_mc_result(struct qbman_swp *p);
>  
>  /**
> + * qbman_swp_enqueue() - Issue an enqueue command
> + * @s:  the software portal used for enqueue
> + * @d:  the enqueue descriptor
> + * @fd: the frame descriptor to be enqueued
> + *
> + * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready.
> + */
> +static inline int
> +qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d,
> +		  const struct dpaa2_fd *fd)
> +{
> +	return qbman_swp_enqueue_ring_mode_ptr(s, d, fd);
> +}
> +
> +/**
> + * qbman_swp_enqueue_multiple() - Issue a multi enqueue command
> + * using one enqueue descriptor
> + * @s:  the software portal used for enqueue
> + * @d:  the enqueue descriptor
> + * @fd: table pointer of frame descriptor table to be enqueued
> + * @flags: table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL
> + * @num_frames: number of fd to be enqueued
> + *
> + * Return the number of fd enqueued, or a negative error number.
> + */
> +static inline int
> +qbman_swp_enqueue_multiple(struct qbman_swp *s,
> +			   const struct qbman_eq_desc *d,
> +			   const struct dpaa2_fd *fd,
> +			   uint32_t *flags,
> +			   int num_frames)
> +{
> +	return qbman_swp_enqueue_multiple_ptr(s, d, fd, flags, num_frames);
> +}
> +
> +/**
> + * qbman_swp_enqueue_multiple_desc() - Issue a multi enqueue command
> + * using multiple enqueue descriptor
> + * @s:  the software portal used for enqueue
> + * @d:  table of minimal enqueue descriptor
> + * @fd: table pointer of frame descriptor table to be enqueued
> + * @num_frames: number of fd to be enqueued
> + *
> + * Return the number of fd enqueued, or a negative error number.
> + */
> +static inline int
> +qbman_swp_enqueue_multiple_desc(struct qbman_swp *s,
> +				const struct qbman_eq_desc_min *d,
> +				const struct dpaa2_fd *fd,
> +				int num_frames)
> +{
> +	return qbman_swp_enqueue_multiple_desc_ptr(s, d, fd, num_frames);
> +}
> +
> +/**
>   * qbman_result_is_DQ() - check if the dequeue result is a dequeue response
>   * @dq: the dequeue result to be checked
>   *
> @@ -504,4 +618,49 @@ int qbman_bp_query(struct qbman_swp *s, u16 bpid,
>  
>  u32 qbman_bp_info_num_free_bufs(struct qbman_bp_query_rslt *a);
>  
> +/**
> + * qbman_swp_release() - Issue a buffer release command
> + * @s:           the software portal object
> + * @d:           the release descriptor
> + * @buffers:     a pointer pointing to the buffer address to be released
> + * @num_buffers: number of buffers to be released,  must be less than 8
> + *
> + * Return 0 for success, -EBUSY if the release command ring is not ready.
> + */
> +static inline int qbman_swp_release(struct qbman_swp *s,
> +				    const struct qbman_release_desc *d,
> +				    const u64 *buffers,
> +				    unsigned int num_buffers)
> +{
> +	return qbman_swp_release_ptr(s, d, buffers, num_buffers);
> +}
> +
> +/**
> + * qbman_swp_pull() - Issue the pull dequeue command
> + * @s: the software portal object
> + * @d: the software portal descriptor which has been configured with
> + *     the set of qbman_pull_desc_set_*() calls
> + *
> + * Return 0 for success, and -EBUSY if the software portal is not ready
> + * to do pull dequeue.
> + */
> +static inline int qbman_swp_pull(struct qbman_swp *s,
> +				 struct qbman_pull_desc *d)
> +{
> +	return qbman_swp_pull_ptr(s, d);
> +}
> +
> +/**
> + * qbman_swp_dqrr_next() - Get an valid DQRR entry
> + * @s: the software portal object
> + *
> + * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry
> + * only once, so repeated calls can return a sequence of DQRR entries, without
> + * requiring they be consumed immediately or in any particular order.
> + */
> +static inline const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
> +{
> +	return qbman_swp_dqrr_next_ptr(s);
> +}
> +
>  #endif /* __FSL_QBMAN_PORTAL_H */



_______________________________________________
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 v3 08/17] arm64, trans_pgd: make trans_pgd_map_page generic
From: Pavel Tatashin @ 2019-09-06 18:58 UTC (permalink / raw)
  To: James Morse
  Cc: Sasha Levin, Mark Rutland, Vladimir Murzin, Jonathan Corbet,
	Marc Zyngier, Catalin Marinas, Bhupesh Sharma, kexec mailing list,
	LKML, James Morris, linux-mm, Eric W. Biederman, Matthias Brugger,
	will, Linux ARM
In-Reply-To: <62fc9ed9-1740-d40b-bc72-6d1911ef1f24@arm.com>

On Fri, Sep 6, 2019 at 11:20 AM James Morse <james.morse@arm.com> wrote:
>
> Hi Pavel,
>
> On 21/08/2019 19:31, Pavel Tatashin wrote:
> > Currently, trans_pgd_map_page has assumptions that are relevant to
> > hibernate. But, to make it generic we must allow it to use any allocator
>
> Sounds familiar: you removed this in patch 2.

Yes, will fix it. Thank  you.

>
>
> > and also, can't assume that entries do not exist in the page table
> > already.
>
> This thing creates a set of page tables to map one page: the relocation code.
> This is mapped in TTBR0_EL1.
> It can assume existing entries do not exist, because it creates the single-entry levels as
> it goes. Kexec also needs to map precisely one page for relocation. You don't need to
> generalise this.
>
> 'trans_pgd_create_copy()' is what creates a copy the linear map. This is mapped in TTBR1_EL1.
>
> There is no reason for kexec to behave differently here.

This is again left over from earlier version where I had a flag for
this assumption. But later redesigned kexec to never have conflicting
mapptings.
I will fix this commit log.

>
>
> > Also, we can't use init_mm here.
>
> Why not? arm64's pgd_populate() doesn't use the mm. It's only there to make it obvious
> this is an EL1 mapping we are creating. We use the kernel-asid with the new mapping.

I understand, and we can use "mm" pointer here, but the problem of
doing so in trans_pdg_* is that it makes the design look ugly. We are
creating page tables for context that runs "mm" because it is between
kernels when everything is overwritten. Yet, relying on "mm" to create
these page tables is odd.

>
> The __ version is a lot less readable.

The only __version of macros I am using is for "populate" calls: for
example, __pmd_populate  instead of pmd_populate etc. I will use non
'__' variants with NULL argument instead of mm.

> Please don't use the page tables as an array: this is what the offset helpers are for.

Sure, I can use:

pte_offset_kernel()
pmd_offset()
pud_offset()
pgd_offset_raw()

The code becomes a little less efficient, because offsets return
pointer to the entry after READ_ONCE, and we need to use another
READ_ONCE() to read its content to parse its value in for example
pud_table(), pud_none() etc . In my case we use READ_ONCE() only one
time  per entry and operate on the content multiple times. Also,
because of unfortunate differences in macro names, the code become a
little less symmetric. Still, I can change the code to use _offsets
here. Please let me know if you still think it is better to use them
here.

>
>
> > Also, add "flags" for trans_pgd_info, they are going to be used
> > in copy functions once they are generalized.
>
> You don't need to 'generalize' this to support hypothetical users.
> There are only two: hibernate and kexec, both of which are very specialised. Making these
> things top-level marionette strings will tangle the logic.

Will do that (see reply below)

>
> The copy_p?d() functions should decide if they should manipulate _this_ entry based on
> _this_ entry and the kernel configuration. This is only really done in _copy_pte(), which
> is where it should stay.

I am sorry, I do not understand this comment. Could you please
elaborate what would you like me to change.

>
>
> > diff --git a/arch/arm64/include/asm/trans_pgd.h b/arch/arm64/include/asm/trans_pgd.h
> > index c7b5402b7d87..e3d022b1b526 100644
> > --- a/arch/arm64/include/asm/trans_pgd.h
> > +++ b/arch/arm64/include/asm/trans_pgd.h
> > @@ -11,10 +11,45 @@
> >  #include <linux/bits.h>
> >  #include <asm/pgtable-types.h>
> >
> > +/*
> > + * trans_alloc_page
> > + *   - Allocator that should return exactly one uninitilaized page, if this
> > + *    allocator fails, trans_pgd returns -ENOMEM error.
> > + *
> > + * trans_alloc_arg
> > + *   - Passed to trans_alloc_page as an argument
>
> This is very familiar.

Sorry, What do you mean?

>
>
> > + * trans_flags
> > + *   - bitmap with flags that control how page table is filled.
> > + *     TRANS_MKWRITE: during page table copy make PTE, PME, and PUD page
> > + *                    writeable by removing RDONLY flag from PTE.
>
> Why would you ever keep the read-only flags in a set of page tables that exist to let you
> overwrite memory?

It meant to take care of this comment, and keep it in hibernate specific code:
329                 /*
330                  * Resume will overwrite areas that may be marked read only
331                  * (code, rodata). Clear the RDONLY bit from the temporary
332                  * mappings we use during restore.
333                  */
334                 .trans_flags            = TRANS_MKWRITE,
335         };

But, sure, this makes sense I will remove this flag, and will do
RDONLY unconditionally.

I re-evaluated "flags", and figured that they are indeed not needed.
So, I will embed them into the code directly.

>
>
> > + *     TRANS_MKVALID: during page table copy, if PTE present, but not valid,
> > + *                    make it valid.
>
> Please keep this logic together with the !pte_none(pte) and debug_pagealloc_enabled()
> check, where it is today.
>
> Making an entry valid without those checks should never be necessary.

Yes, will do that.

>
>
> > + *     TRANS_CHECKPFN: During page table copy, for every PTE entry check that
> > + *                     PFN that this PTE points to is valid. Otherwise return
> > + *                     -ENXIO
>
> Hibernate does this when inventing a new mapping. This is how we check the kernel
> should be able to read/write this page. If !pfn_valid(), the page should not be mapped.
>
> Why do you need to turn this off?
>
> It us only necessary at the leaf level, and only if debug-pagealloc is in use. Please keep
> all these bits together, as its much harder to understand why this entry needs inventing
> when its split up like this.
>
>
>

> > diff --git a/arch/arm64/mm/trans_pgd.c b/arch/arm64/mm/trans_pgd.c
> > index 00b62d8640c2..dbabccd78cc4 100644
> > --- a/arch/arm64/mm/trans_pgd.c
> > +++ b/arch/arm64/mm/trans_pgd.c
> > @@ -17,6 +17,16 @@
> >  #include <asm/pgtable.h>
> >  #include <linux/suspend.h>
> >
> > +static void *trans_alloc(struct trans_pgd_info *info)
> > +{
> > +     void *page = info->trans_alloc_page(info->trans_alloc_arg);
> > +
> > +     if (page)
> > +             clear_page(page);
>
> The hibernate allocator already does this. As your reason for doing this is to make this
> faster, it seems odd we do this twice.
>
> If zeroed pages are necessary, the allocator should do it. (It already needs to be a
> use-case specific allocator)

Makes sense, I will change the requirement for allocator to return
zeroed memory.

>
>
> > +
> > +     return page;
> > +}
> > +
> >  static void _copy_pte(pte_t *dst_ptep, pte_t *src_ptep, unsigned long addr)
> >  {
> >       pte_t pte = READ_ONCE(*src_ptep);
> > @@ -172,40 +182,64 @@ int trans_pgd_create_copy(pgd_t **dst_pgdp, unsigned long start,
> >       return rc;
> >  }
> >
> > -int trans_pgd_map_page(pgd_t *trans_pgd, void *page, unsigned long dst_addr,
> > -                    pgprot_t pgprot)
> > +int trans_pgd_map_page(struct trans_pgd_info *info, pgd_t *trans_pgd,
> > +                    void *page, unsigned long dst_addr, pgprot_t pgprot)
> >  {
> > -     pgd_t *pgdp;
> > -     pud_t *pudp;
> > -     pmd_t *pmdp;
> > -     pte_t *ptep;
> > -
> > -     pgdp = pgd_offset_raw(trans_pgd, dst_addr);
> > -     if (pgd_none(READ_ONCE(*pgdp))) {
> > -             pudp = (void *)get_safe_page(GFP_ATOMIC);
> > -             if (!pudp)
> > +     int pgd_idx = pgd_index(dst_addr);
> > +     int pud_idx = pud_index(dst_addr);
> > +     int pmd_idx = pmd_index(dst_addr);
> > +     int pte_idx = pte_index(dst_addr);
>
> Yuck.
>

What's wrong with pre-calculating indices? :)

>
>
> > +     pgd_t *pgdp = trans_pgd;
> > +     pgd_t pgd = READ_ONCE(pgdp[pgd_idx]);
> > +     pud_t *pudp, pud;
> > +     pmd_t *pmdp, pmd;
> > +     pte_t *ptep, pte;
> > +
> > +     if (pgd_none(pgd)) {
> > +             pud_t *t = trans_alloc(info);
> > +
> > +             if (!t)
> >                       return -ENOMEM;
>
> > -             pgd_populate(&init_mm, pgdp, pudp);
> > +
> > +             __pgd_populate(&pgdp[pgd_idx], __pa(t), PUD_TYPE_TABLE);
> > +             pgd = READ_ONCE(pgdp[pgd_idx]);
>
>
> Please keep the pgd_populate() call. If there is some reason we can't pass init_mm, we can
> pass NULL, or a fake mm pointer instead.\\

Hm, we could use NULL instead of "mm", I will do that, thanks.

>
> Going behind the page table helpers back to play with the table directly is a maintenance
> headache.
>
>
> >       }
> >
>
>
> > -     pudp = pud_offset(pgdp, dst_addr);
> > -     if (pud_none(READ_ONCE(*pudp))) {
> > -             pmdp = (void *)get_safe_page(GFP_ATOMIC);
> > -             if (!pmdp)
> > +     pudp = __va(pgd_page_paddr(pgd));
> > +     pud = READ_ONCE(pudp[pud_idx]);
> > +     if (pud_sect(pud)) {
> > +             return -ENXIO;
> > +     } else if (pud_none(pud) || pud_sect(pud)) {
> > +             pmd_t *t = trans_alloc(info);
> > +
> > +             if (!t)
> >                       return -ENOMEM;
>
> Choke on block mappings? This should never happen because this function should only create
> the tables necessary to map one page. Not a block mapping in sight.
>
> (see my comments on patch 6)

I can remove this, but what should I replace it with BUG() or silently
ignore, and assume no huge page hre? I thought the idea is not to use
BUG() calls in kernel code, and return errors instead. If, in the
future PUD size mappings are added, how is that going to be detected?

Thank you,
Pasha

_______________________________________________
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 v3 09/17] arm64, trans_pgd: add trans_pgd_create_empty
From: Pavel Tatashin @ 2019-09-06 19:00 UTC (permalink / raw)
  To: James Morse
  Cc: Sasha Levin, Mark Rutland, Vladimir Murzin, Jonathan Corbet,
	Marc Zyngier, Catalin Marinas, Bhupesh Sharma, kexec mailing list,
	LKML, James Morris, linux-mm, Eric W. Biederman, Matthias Brugger,
	will, Linux ARM
In-Reply-To: <2d9f7511-ce65-d5ca-653e-f4d43994a32d@arm.com>

On Fri, Sep 6, 2019 at 11:20 AM James Morse <james.morse@arm.com> wrote:
>
> Hi Pavel,
>
> On 21/08/2019 19:31, Pavel Tatashin wrote:
> > This functions returns a zeroed trans_pgd using the allocator that is
> > specified in the info argument.
> >
> > trans_pgds should be created by using this function.
>
> This function takes the allocator you give it, and calls it once.
>
> Given both users need one pgd, and have to provide the allocator, it seems strange that
> they aren't trusted to call it.
>
> I don't think this patch is necessary.
>
> Let the caller pass in the pgd_t to the helpers.

Ok.

Thank you,
Pasha

_______________________________________________
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 v3 10/17] arm64, trans_pgd: adjust trans_pgd_create_copy interface
From: Pavel Tatashin @ 2019-09-06 19:03 UTC (permalink / raw)
  To: James Morse
  Cc: Sasha Levin, Mark Rutland, Vladimir Murzin, Jonathan Corbet,
	Marc Zyngier, Catalin Marinas, Bhupesh Sharma, kexec mailing list,
	LKML, James Morris, linux-mm, Eric W. Biederman, Matthias Brugger,
	will, Linux ARM
In-Reply-To: <21f6eb6f-be3a-a715-a37c-2f59183ed183@arm.com>

> > -int trans_pgd_create_copy(pgd_t **dst_pgdp, unsigned long start,
> > +/*
> > + * Create trans_pgd and copy entries from from_table to trans_pgd in range
> > + * [start, end)
> > + */
> > +int trans_pgd_create_copy(struct trans_pgd_info *info, pgd_t **trans_pgd,
> > +                       pgd_t *from_table, unsigned long start,
> >                         unsigned long end);
>
> This creates a copy of the linear-map. Why does it need to be told from_table?

This what done as a generic page table entries copy, but I agree, will
remove the from_table.

>
>
> > diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
> > index 8c2641a9bb09..8bb602e91065 100644
> > --- a/arch/arm64/kernel/hibernate.c
> > +++ b/arch/arm64/kernel/hibernate.c
> > @@ -323,15 +323,42 @@ int swsusp_arch_resume(void)
> >       phys_addr_t phys_hibernate_exit;
> >       void __noreturn (*hibernate_exit)(phys_addr_t, phys_addr_t, void *,
> >                                         void *, phys_addr_t, phys_addr_t);
> > +     struct trans_pgd_info trans_info = {
> > +             .trans_alloc_page       = hibernate_page_alloc,
> > +             .trans_alloc_arg        = (void *)GFP_ATOMIC,
> > +             /*
> > +              * Resume will overwrite areas that may be marked read only
> > +              * (code, rodata). Clear the RDONLY bit from the temporary
> > +              * mappings we use during restore.
> > +              */
> > +             .trans_flags            = TRANS_MKWRITE,
> > +     };
>
>
> > +     /*
> > +      * debug_pagealloc will removed the PTE_VALID bit if the page isn't in
> > +      * use by the resume kernel. It may have been in use by the original
> > +      * kernel, in which case we need to put it back in our copy to do the
> > +      * restore.
> > +      *
> > +      * Before marking this entry valid, check the pfn should be mapped.
> > +      */
> > +     if (debug_pagealloc_enabled())
> > +             trans_info.trans_flags |= (TRANS_MKVALID | TRANS_CHECKPFN);
>
> The debug_pagealloc_enabled() check should be with the code that generates a different
> entry. Whether the different entry is correct needs to be considered with
> debug_pagealloc_enabled() in mind. You are making this tricky logic less clear.
>
> There is no way the existing code invents an entry for a !pfn_valid() page. With your
> 'checkpfn' flag, this thing can. You don't need to generalise this for hypothetical users.

Ok

>
>
> If kexec needs to create mappings for bogus pages, I'd like to know why.
>

It does not.

>
> >       /*
> >        * Restoring the memory image will overwrite the ttbr1 page tables.
> >        * Create a second copy of just the linear map, and use this when
> >        * restoring.
> >        */
> > -     rc = trans_pgd_create_copy(&tmp_pg_dir, PAGE_OFFSET, 0);
> > -     if (rc)
> > +     rc = trans_pgd_create_copy(&trans_info, &tmp_pg_dir, init_mm.pgd,
> > +                                PAGE_OFFSET, 0);
>
> > +     if (rc) {
> > +             if (rc == -ENOMEM)
> > +                     pr_err("Failed to allocate memory for temporary page tables.\n");
> > +             else if (rc == -ENXIO)
> > +                     pr_err("Tried to set PTE for PFN that does not exist\n");
> >               goto out;
> > +     }
>
> If you think the distinction for this error message is useful, it would be clearer to
> change it in the current hibernate code before you move it. (_copy_pte() to return an
> error, instead of silently failing). Done here, this is unrelated noise.
>

Ok, will do that.

_______________________________________________
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 1/1] mm/pgtable/debug: Add test validating architecture page table helpers
From: Gerald Schaefer @ 2019-09-06 19:03 UTC (permalink / raw)
  To: Anshuman Khandual
  Cc: Mark Rutland, linux-ia64, linux-sh, Peter Zijlstra, James Hogan,
	Tetsuo Handa, Heiko Carstens, Michal Hocko, linux-mm, Dave Hansen,
	Paul Mackerras, sparclinux, Thomas Gleixner, linux-s390,
	Michael Ellerman, x86, Russell King - ARM Linux, Matthew Wilcox,
	Steven Price, Jason Gunthorpe, linux-arm-kernel, linux-snps-arc,
	Kees Cook, Masahiro Yamada, Mark Brown, Dan Williams,
	Vlastimil Babka, Sri Krishna chowdary, Ard Biesheuvel,
	Greg Kroah-Hartman, linux-mips, Ralf Baechle, linux-kernel,
	Paul Burton, Mike Rapoport, Vineet Gupta, Martin Schwidefsky,
	Andrew Morton, linuxppc-dev, David S. Miller
In-Reply-To: <3c609e33-afbb-ffaf-481a-6d225a06d1d0@arm.com>

On Fri, 6 Sep 2019 11:58:59 +0530
Anshuman Khandual <anshuman.khandual@arm.com> wrote:

> On 09/05/2019 10:36 PM, Gerald Schaefer wrote:
> > On Thu, 5 Sep 2019 14:48:14 +0530
> > Anshuman Khandual <anshuman.khandual@arm.com> wrote:
> >   
> >>> [...]    
> >>>> +
> >>>> +#if !defined(__PAGETABLE_PMD_FOLDED) && !defined(__ARCH_HAS_4LEVEL_HACK)
> >>>> +static void pud_clear_tests(pud_t *pudp)
> >>>> +{
> >>>> +	memset(pudp, RANDOM_NZVALUE, sizeof(pud_t));
> >>>> +	pud_clear(pudp);
> >>>> +	WARN_ON(!pud_none(READ_ONCE(*pudp)));
> >>>> +}    
> >>>
> >>> For pgd/p4d/pud_clear(), we only clear if the page table level is present
> >>> and not folded. The memset() here overwrites the table type bits, so
> >>> pud_clear() will not clear anything on s390 and the pud_none() check will
> >>> fail.
> >>> Would it be possible to OR a (larger) random value into the table, so that
> >>> the lower 12 bits would be preserved?    
> >>
> >> So the suggestion is instead of doing memset() on entry with RANDOM_NZVALUE,
> >> it should OR a large random value preserving lower 12 bits. Hmm, this should
> >> still do the trick for other platforms, they just need non zero value. So on
> >> s390, the lower 12 bits on the page table entry already has valid value while
> >> entering this function which would make sure that pud_clear() really does
> >> clear the entry ?  
> > 
> > Yes, in theory the table entry on s390 would have the type set in the last
> > 4 bits, so preserving those would be enough. If it does not conflict with
> > others, I would still suggest preserving all 12 bits since those would contain
> > arch-specific flags in general, just to be sure. For s390, the pte/pmd tests
> > would also work with the memset, but for consistency I think the same logic
> > should be used in all pxd_clear_tests.  
> 
> Makes sense but..
> 
> There is a small challenge with this. Modifying individual bits on a given
> page table entry from generic code like this test case is bit tricky. That
> is because there are not enough helpers to create entries with an absolute
> value. This would have been easier if all the platforms provided functions
> like __pxx() which is not the case now. Otherwise something like this should
> have worked.
> 
> 
> pud_t pud = READ_ONCE(*pudp);
> pud = __pud(pud_val(pud) | RANDOM_VALUE (keeping lower 12 bits 0))
> WRITE_ONCE(*pudp, pud);
> 
> But __pud() will fail to build in many platforms.

Hmm, I simply used this on my system to make pud_clear_tests() work, not
sure if it works on all archs:

pud_val(*pudp) |= RANDOM_NZVALUE;

> 
> The other alternative will be to make sure memset() happens on all other
> bits except the lower 12 bits which will depend on endianness. If s390
> has a fixed endianness, we can still use either of them which will hold
> good for others as well.
> 
> memset(pudp, RANDOM_NZVALUE, sizeof(pud_t) - 3);
> 
> OR
> 
> memset(pudp + 3, RANDOM_NZVALUE, sizeof(pud_t) - 3);
> 
> > 
> > However, there is another issue on s390 which will make this only work
> > for pud_clear_tests(), and not for the p4d/pgd_tests. The problem is that
> > mm_alloc() will only give you a 3-level page table initially on s390.
> > This means that pudp == p4dp == pgdp, and so the p4d/pgd_tests will
> > both see the pud level (of course this also affects other tests).  
> 
> Got it.
> 
> > 
> > Not sure yet how to fix this, i.e. how to initialize/update the page table
> > to 5 levels. We can handle 5 level page tables, and it would be good if
> > all levels could be tested, but using mm_alloc() to establish the page
> > tables might not work on s390. One option could be to provide an arch-hook
> > or weak function to allocate/initialize the mm.  
> 
> Sure, got it. Though I plan to do add some arch specific tests or init sequence
> like the above later on but for now the idea is to get the smallest possible set
> of test cases which builds and runs on all platforms without requiring any arch
> specific hooks or special casing (#ifdef) to be agreed upon broadly and accepted.
> 
> Do you think this is absolutely necessary on s390 for the very first set of test
> cases or we can add this later on as an improvement ?

It can be added later, no problem. I did not expect this to work flawlessly
on s390 right from the start anyway, with all our peculiarities, so don't
let this hinder you. I might come up with an add-on patch later.

Actually, using get_unmapped_area() as suggested by Kirill could also
solve this issue. We do create a new mm with 3-level page tables on s390,
and the dynamic upgrade to 4 or 5 levels is then triggered exactly by
arch_get_unmapped_area(), depending on the addr. But I currently don't
see how / where arch_get_unmapped_area() is set up for such a dummy mm
created by mm_alloc().

Regards,
Gerald


_______________________________________________
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 v3 11/17] arm64, trans_pgd: add PUD_SECT_RDONLY
From: Pavel Tatashin @ 2019-09-06 19:04 UTC (permalink / raw)
  To: James Morse
  Cc: Sasha Levin, Mark Rutland, Vladimir Murzin, Jonathan Corbet,
	Marc Zyngier, Catalin Marinas, Bhupesh Sharma, kexec mailing list,
	LKML, James Morris, linux-mm, Eric W. Biederman, Matthias Brugger,
	will, Linux ARM
In-Reply-To: <d53d973c-17dc-2f4f-c052-83d6df15b002@arm.com>

On Fri, Sep 6, 2019 at 11:21 AM James Morse <james.morse@arm.com> wrote:
>
> Hi Pavel,
>
> On 21/08/2019 19:31, Pavel Tatashin wrote:
> > Thre is PMD_SECT_RDONLY that is used in pud_* function which is confusing.
>
> Nit: There
>
> I bet it was equally confusing before before you moved it! Could you do this earlier in
> the series with the rest of the cleanup?
>
> With that,
> Acked-by: James Morse <james.morse@arm.com>

Will move it earlier.

Thank you,
Pasha

_______________________________________________
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 v3 12/17] arm64, trans_pgd: complete generalization of trans_pgds
From: Pavel Tatashin @ 2019-09-06 19:06 UTC (permalink / raw)
  To: James Morse
  Cc: Sasha Levin, Mark Rutland, Vladimir Murzin, Jonathan Corbet,
	Marc Zyngier, Catalin Marinas, Bhupesh Sharma, kexec mailing list,
	LKML, James Morris, linux-mm, Eric W. Biederman, Matthias Brugger,
	will, Linux ARM
In-Reply-To: <d4a5bb7b-21c0-9f39-ad96-3fa43684c6c6@arm.com>

On Fri, Sep 6, 2019 at 11:23 AM James Morse <james.morse@arm.com> wrote:
>
> Hi Pavel,
>
> On 21/08/2019 19:31, Pavel Tatashin wrote:
> > Make the last private functions in page table copy path generlized for use
> > outside of hibernate.
> >
> > Switch to use the provided allocator, flags, and source page table. Also,
> > unify all copy function implementations to reduce the possibility of bugs.
>
> By changing it? No one has reported any problems. We're more likely to break it making
> unnecessary changes.
>
> Why is this necessary?

I tried to make it cleaner, but if you think the final version does
not make it better, I will keep the current versions.

Thank you,
Pasha

_______________________________________________
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] PCI: dwc: Use PTR_ERR_OR_ZERO() in five functions
From: Andrew Murray @ 2019-09-06 19:30 UTC (permalink / raw)
  To: Markus Elfring
  Cc: kernel-janitors, linux-samsung-soc, Xiaowei Song, linux-pci,
	Binghui Wang, YueHaibing, LKML, Krzysztof Kozlowski, Yue Wang,
	Kukjin Kim, Kevin Hilman, Jingoo Han, Bjorn Helgaas,
	linux-amlogic, zhong jiang, Lorenzo Pieralisi, linux-arm-kernel
In-Reply-To: <95c9dfae-af81-82ad-e989-1fdf5f29808e@web.de>

On Fri, Sep 06, 2019 at 08:50:07PM +0200, Markus Elfring wrote:
> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Fri, 6 Sep 2019 20:40:06 +0200
> 
> Simplify these function implementations by using a known function.
> 
> Generated by: scripts/coccinelle/api/ptr_ret.cocci
> 
> Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>

Thanks for this, looks good to me:

Reviewed-by: Andrew Murray <andrew.murray@arm.com>

> ---
>  drivers/pci/controller/dwc/pci-exynos.c |  5 +----
>  drivers/pci/controller/dwc/pci-meson.c  | 10 ++--------
>  drivers/pci/controller/dwc/pcie-kirin.c | 10 ++--------
>  3 files changed, 5 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c
> index cee5f2f590e2..b6ab1cc5d895 100644
> --- a/drivers/pci/controller/dwc/pci-exynos.c
> +++ b/drivers/pci/controller/dwc/pci-exynos.c
> @@ -92,10 +92,7 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
> 
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
> -	if (IS_ERR(ep->mem_res->elbi_base))
> -		return PTR_ERR(ep->mem_res->elbi_base);
> -
> -	return 0;
> +	return PTR_ERR_OR_ZERO(ep->mem_res->elbi_base);
>  }
> 
>  static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
> diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
> index e35e9eaa50ee..713059918002 100644
> --- a/drivers/pci/controller/dwc/pci-meson.c
> +++ b/drivers/pci/controller/dwc/pci-meson.c
> @@ -182,10 +182,7 @@ static int meson_pcie_get_mems(struct platform_device *pdev,
> 
>  	/* Meson SoC has two PCI controllers use same phy register*/
>  	mp->mem_res.phy_base = meson_pcie_get_mem_shared(pdev, mp, "phy");
> -	if (IS_ERR(mp->mem_res.phy_base))
> -		return PTR_ERR(mp->mem_res.phy_base);
> -
> -	return 0;
> +	return PTR_ERR_OR_ZERO(mp->mem_res.phy_base);
>  }
> 
>  static void meson_pcie_power_on(struct meson_pcie *mp)
> @@ -259,10 +256,7 @@ static int meson_pcie_probe_clocks(struct meson_pcie *mp)
>  		return PTR_ERR(res->general_clk);
> 
>  	res->clk = meson_pcie_probe_clock(dev, "pcie", 0);
> -	if (IS_ERR(res->clk))
> -		return PTR_ERR(res->clk);
> -
> -	return 0;
> +	return PTR_ERR_OR_ZERO(res->clk);
>  }
> 
>  static inline void meson_elb_writel(struct meson_pcie *mp, u32 val, u32 reg)
> diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c
> index c19617a912bd..75b1f1dde747 100644
> --- a/drivers/pci/controller/dwc/pcie-kirin.c
> +++ b/drivers/pci/controller/dwc/pcie-kirin.c
> @@ -138,10 +138,7 @@ static long kirin_pcie_get_clk(struct kirin_pcie *kirin_pcie,
>  		return PTR_ERR(kirin_pcie->apb_sys_clk);
> 
>  	kirin_pcie->pcie_aclk = devm_clk_get(dev, "pcie_aclk");
> -	if (IS_ERR(kirin_pcie->pcie_aclk))
> -		return PTR_ERR(kirin_pcie->pcie_aclk);
> -
> -	return 0;
> +	return PTR_ERR_OR_ZERO(kirin_pcie->pcie_aclk);
>  }
> 
>  static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
> @@ -174,10 +171,7 @@ static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie,
> 
>  	kirin_pcie->sysctrl =
>  		syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl");
> -	if (IS_ERR(kirin_pcie->sysctrl))
> -		return PTR_ERR(kirin_pcie->sysctrl);
> -
> -	return 0;
> +	return PTR_ERR_OR_ZERO(kirin_pcie->sysctrl);
>  }
> 
>  static int kirin_pcie_phy_init(struct kirin_pcie *kirin_pcie)
> --
> 2.23.0
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

_______________________________________________
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] clk: imx: pll14xx: Fix quick switch of S/K parameter
From: Leonard Crestez @ 2019-09-06 19:36 UTC (permalink / raw)
  To: Stephen Boyd, Peng Fan, Shawn Guo, Daniel Baluta
  Cc: Aisheng Dong, Jacky Bai, Michael Turquette,
	linux-clk@vger.kernel.org, dl-linux-imx, Viorel Suman,
	Fabio Estevam, kernel@pengutronix.de,
	linux-arm-kernel@lists.infradead.org, Abel Vesa
In-Reply-To: <20190906172452.D1ED620838@mail.kernel.org>

On 06.09.2019 20:24, Stephen Boyd wrote:
> Quoting Leonard Crestez (2019-09-04 02:49:18)
>> The PLL14xx on imx8m can change the S and K parameter without requiring
>> a reset and relock of the whole PLL.
>>
>> Fix clk_pll144xx_mp_change register reading and use it for pll1443 as
>> well since no reset+relock is required on K changes either.
>>
>> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
>> ---
>>   drivers/clk/imx/clk-pll14xx.c | 40 +++++++----------------------------
>>   1 file changed, 8 insertions(+), 32 deletions(-)
>>
>> The PLLs are currently table-based and none of the entries differ only
>> in S/K so further work would be required to make use of this. The
>> prospective user is audio doing tiny freq adjustments and there is no
>> standard API for that.
> 
> sub-Hz adjustments?

Maybe at the audio level? The PLL itself runs at ~400Mhz so wouldn't 
need sub-hz adjustment.

My understanding is that adjustments would be made based on an external 
clock so if CLK framework rounds to 1hz then it would just take longer 
for adjustment to kick in.

>> Lacking users is not a good reason to carry broken code around.
> 
> Maybe add a Fixes tag so if anyone wants to use it in LTS kernels there
> might be a chance that they'll find this patch mention code they're
> using.

It doesn't meet stable kernel rules because it doesn't "fix a real bug 
that bothers people" but it's still technically a fix:

Fixes: 8646d4dcc7fb ("clk: imx: Add PLLs driver for imx8mm soc")

--
Regards,
Leonard

_______________________________________________
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 v5 03/10] arm64: atomics: avoid out-of-line ll/sc atomics
From: Nick Desaulniers @ 2019-09-06 19:44 UTC (permalink / raw)
  To: Andrew Murray
  Cc: Mark Rutland, Peter Zijlstra, Catalin Marinas, Robin Murphy,
	clang-built-linux, Ard.Biesheuvel, Kristof Beyls,
	Nathan Chancellor, Will Deacon, Linux ARM
In-Reply-To: <20190905112519.GY9720@e119886-lin.cambridge.arm.com>

On Thu, Sep 5, 2019 at 4:25 AM Andrew Murray <andrew.murray@arm.com> wrote:
>
> On Wed, Sep 04, 2019 at 10:28:14AM -0700, Nick Desaulniers wrote:
> > On Tue, Sep 3, 2019 at 3:04 PM Andrew Murray <andrew.murray@arm.com> wrote:
> > >
> > > On Tue, Sep 03, 2019 at 05:37:55PM +0100, Will Deacon wrote:
> > > > On Tue, Sep 03, 2019 at 04:31:20PM +0100, Andrew Murray wrote:
> > > > > On Tue, Sep 03, 2019 at 04:15:44PM +0100, Andrew Murray wrote:
> > > > > > On Tue, Sep 03, 2019 at 03:45:34PM +0100, Will Deacon wrote:
> > > > > > > Does it work if the only thing you change is the toolchain, and use GCC
> > > > > > > instead?
> > > > > >
> > > > > > Yup.
> > > > >
> > > > > Also this is Clang generation:
> > > > >
> > > > > ffff8000100f2700 <__ptrace_link>:
> > > > > ffff8000100f2700:       f9426009        ldr     x9, [x0, #1216]
> > > > > ffff8000100f2704:       91130008        add     x8, x0, #0x4c0
> > > > > ffff8000100f2708:       eb09011f        cmp     x8, x9
> > > > > ffff8000100f270c:       540002a1        b.ne    ffff8000100f2760 <__ptrace_link+0x60>  // b.any
> > > > > ffff8000100f2710:       f9425829        ldr     x9, [x1, #1200]
> > > > > ffff8000100f2714:       9112c02a        add     x10, x1, #0x4b0
> > > > > ffff8000100f2718:       f9000528        str     x8, [x9, #8]
> > > > > ffff8000100f271c:       f9026009        str     x9, [x0, #1216]
> > > > > ffff8000100f2720:       f902640a        str     x10, [x0, #1224]
> > > > > ffff8000100f2724:       f9025828        str     x8, [x1, #1200]
> > > > > ffff8000100f2728:       f9024001        str     x1, [x0, #1152]
> > > > > ffff8000100f272c:       b4000162        cbz     x2, ffff8000100f2758 <__ptrace_link+0x58>
> > > > > ffff8000100f2730:       b900985f        str     wzr, [x2, #152]
> > > > > ffff8000100f2734:       14000004        b       ffff8000100f2744 <__ptrace_link+0x44>
> > > > > ffff8000100f2738:       14000001        b       ffff8000100f273c <__ptrace_link+0x3c>
> > > > > ffff8000100f273c:       14000006        b       ffff8000100f2754 <__ptrace_link+0x54>
> > > > > ffff8000100f2740:       14000001        b       ffff8000100f2744 <__ptrace_link+0x44>
> > > > > ffff8000100f2744:       52800028        mov     w8, #0x1                        // #1
> > > > > ffff8000100f2748:       b828005f        stadd   w8, [x2]
> > > > > ffff8000100f274c:       f9030002        str     x2, [x0, #1536]
> > > > > ffff8000100f2750:       d65f03c0        ret
> > > > > ffff8000100f2754:       140007fd        b       ffff8000100f4748 <ptrace_check_attach+0xf8>
> > > > > ...
> > > > >
> > > > > This looks like the default path (before we write over it) will take you to
> > > > > the LSE code (e.g. ffff8000100f2734). I'm pretty sure this is wrong, or at
> > > > > least not what we expected to see. Also why 4 branches?
> > > >
> > > > So I reproduced this with a silly atomic_inc wrapper:
> > > >
> > > > void will_atomic_inc(atomic_t *v)
> > > > {
> > > >         atomic_inc(v);
> > > > }
> > > >
> > > > Compiles to:
> > > >
> > > > 0000000000000018 <will_atomic_inc>:
> > > >   18: 14000004        b       28 <will_atomic_inc+0x10>
> > > >   1c: 14000001        b       20 <will_atomic_inc+0x8>
> > > >   20: 14000005        b       34 <will_atomic_inc+0x1c>
> > > >   24: 14000001        b       28 <will_atomic_inc+0x10>
> > > >   28: 52800028        mov     w8, #0x1                        // #1
> > > >   2c: b828001f        stadd   w8, [x0]
> > > >   30: d65f03c0        ret
> > > >   34: 14000027        b       d0 <dump_kernel_offset+0x60>
> > > >   38: d65f03c0        ret
> > > >
> > > > which is going to explode.
> > >
> > > I've come up with a simple reproducer for this issue:
> > >
> > > static bool branch_jump()
> > > {
> > >         asm_volatile_goto(
> > >                 "1: b %l[l_yes2]"
> > >                  : : : : l_yes2);
> > >
> > >         return false;
> > > l_yes2:
> > >         return true;
> > > }
> > >
> > > static bool branch_test()
> > > {
> > >         return (!branch_jump() && !branch_jump());
> > > }
> > >
> > > void andy_test(int *v)
> > > {
> > >         if (branch_test())
> > >                 *v = 0xff;
> > > }
> > >
> > > This leads to the following (it shouldn't do anything):
> > >
> > > 0000000000000000 <andy_test>:
> > >    0:   14000004        b       10 <andy_test+0x10>
> > >    4:   14000001        b       8 <andy_test+0x8>
> > >    8:   14000004        b       18 <andy_test+0x18>
> > >    c:   14000001        b       10 <andy_test+0x10>
> > >   10:   52801fe8        mov     w8, #0xff                       // #255
> > >   14:   b9000008        str     w8, [x0]
> > >   18:   d65f03c0        ret
> > >
> > > The issue goes away with any of the following hunks:
> > >
> > >
> > > @@ -55,7 +55,7 @@ static bool branch_jump()
> > >
> > >  static bool branch_test()
> > >  {
> > > -       return (!branch_jump() && !branch_jump());
> > > +       return (!branch_jump());
> > >  }
> > >
> > >  void andy_test(int *v)
> > >
> > >
> > > or:
> > >
> > >
> > > @@ -53,14 +53,10 @@ static bool branch_jump()
> > >          return true;
> > >  }
> > >
> > > -static bool branch_test()
> > > -{
> > > -       return (!branch_jump() && !branch_jump());
> > > -}
> > >
> > >  void andy_test(int *v)
> > >  {
> > > -       if (branch_test())
> > > +       if (!branch_jump() && !branch_jump())
> > >                 *v = 0xff;
> > >  }
> >
> > Indeed, playing with the definition of `__lse_ll_sc_body`, I can get
> > the kernel to boot again.
>
> Thanks for investigating this.
>
> Did it boot to a prompt? I played with the structure of the code and
> too was able to get it to boot, but I found that it hung later-on during
> boot. Thus I lost a bit of confidence in it.
>
> >
> > So I think your very helpful test cases are illustrating two different problems:
> > https://godbolt.org/z/dMf7x-
> > See the disassembly of `andy_test2`.  Reference to the correct label
> > is emitted in the inline asm, but there's some silly unconditional
> > branches to the next instruction.  That's issue #1 and part of the
> > reason you see superfluous branches.  With that fixed, `andy_test2`
> > would match between GCC and Clang.  I think that can be a very late
> > peephole optimization (and further, we could probably combine labels
> > that refer to the same location, oh and .Lfunc_endX could just use
> > `.`, too!). LLVM devs noted that the x86 backend doesn't have this
> > issue, but this is a curiously recurring pattern I'm noticing in LLVM
> > where some arch agnostic optimization is only implemented for x86...
> > I'm reading through our Branch Folding pass which I think should
> > handle this, but I'll need to fire up a debugger.
> >
> > Issue #2 is the more critical issue, but may be conflated with issue
> > #1.  Issue #2 is the nonsensical control flow with one level of
> > inlining.  See how in the disassembly of `andy_test`, the first label
> > referenced from inline assembly is *before* the mov/str when it should
> > have been *after*.  Not sure where we could be going wrong, but it's
> > straightforward for me to observe the code change as its transformed
> > through LLVM, and I've debugged and fixed issues related to inlining
> > asm goto before.
>
> You may also be interested in this:
>
> https://godbolt.org/z/8OthP2
>
> void andy_test3(int *v)
> {
>     if (!branch_jump())
>         return;
>
>     if (!branch_jump())
>         return;
>
>     *v = 0xff;
> }
>
> (I used a similar approach with system_uses_lse_atomics to get the
> kernel to boot a bit more).
>
> This generated code does the right thing here (in comparison to andy_test2).
> I felt like this gave an insight as to what is going on, but I don't
> have the knowledge to know what. It's as if the early return prevents the
> compiler from getting confused when it should otherwise jump to the second
> goto.

Thanks for all of these test cases.  It highlighted a bug in our
implementation that I have a fix in hand for (currently awaiting code
review):
https://reviews.llvm.org/D67252

Further, I wrote a check for this kind of bug in our verification
pass, so that this kind of bug doesn't creep back in:
https://reviews.llvm.org/D67196

I cleared https://reviews.llvm.org/D67252 w/ the clang-9 release
manager; assuming I land it today or early next week we'll likely be
able to pick it up for the clang-9 release.

I very much appreciate the help debugging and the reduced test cases.
It's been a pleasure!
-- 
Thanks,
~Nick Desaulniers

_______________________________________________
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 v3 2/5] gpio/aspeed: Fix incorrect number of banks
From: Joel Stanley @ 2019-09-06 19:46 UTC (permalink / raw)
  To: Rashmica Gupta
  Cc: moderated list:ARM/ASPEED MACHINE SUPPORT,
	open list:GPIO SUBSYSTEM, Andrew Jeffery, Linus Walleij,
	open list, Bartosz Golaszewski,
	moderated list:ARM/ASPEED MACHINE SUPPORT
In-Reply-To: <20190906062623.13354-1-rashmica.g@gmail.com>

On Fri, 6 Sep 2019 at 06:26, Rashmica Gupta <rashmica.g@gmail.com> wrote:
>
> The current calculation for the number of GPIO banks is only correct if
> the number of GPIOs is a multiple of 32 (if there were 31 GPIOs we would
> currently say there are 0 banks, which is incorrect).
>
> Fixes: 361b79119a4b7 ('gpio: Add Aspeed driver')
>
> Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

Reviewed-by: Joel Stanley <joel@jms.d.au>

> ---
>  drivers/gpio/gpio-aspeed.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index 9defe25d4721..b83e23aecd18 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -1165,7 +1165,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>         gpio->chip.base = -1;
>
>         /* Allocate a cache of the output registers */
> -       banks = gpio->config->nr_gpios >> 5;
> +       banks = DIV_ROUND_UP(gpio->config->nr_gpios, 32);
>         gpio->dcache = devm_kcalloc(&pdev->dev,
>                                     banks, sizeof(u32), GFP_KERNEL);
>         if (!gpio->dcache)
> --
> 2.20.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 v3 3/5] gpio/aspeed: Setup irqchip dynamically
From: Joel Stanley @ 2019-09-06 19:46 UTC (permalink / raw)
  To: Rashmica Gupta
  Cc: moderated list:ARM/ASPEED MACHINE SUPPORT,
	open list:GPIO SUBSYSTEM, Andrew Jeffery, Linus Walleij,
	open list, Bartosz Golaszewski,
	moderated list:ARM/ASPEED MACHINE SUPPORT
In-Reply-To: <20190906062644.13445-1-rashmica.g@gmail.com>

On Fri, 6 Sep 2019 at 06:26, Rashmica Gupta <rashmica.g@gmail.com> wrote:
>
> This is in preparation for adding ast2600 support. The ast2600 SoC
> requires two instances of the GPIO driver as it has two GPIO
> controllers. Each instance needs it's own irqchip.
>
> Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>

Reviewed-by: Joel Stanley <joel@jms.id.au>

> ---
>  drivers/gpio/gpio-aspeed.c | 16 +++++++---------
>  1 file changed, 7 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index b83e23aecd18..16c6eaf70857 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -52,6 +52,7 @@ struct aspeed_gpio_config {
>   */
>  struct aspeed_gpio {
>         struct gpio_chip chip;
> +       struct irq_chip irqc;
>         spinlock_t lock;
>         void __iomem *base;
>         int irq;
> @@ -681,14 +682,6 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc)
>         chained_irq_exit(ic, desc);
>  }
>
> -static struct irq_chip aspeed_gpio_irqchip = {
> -       .name           = "aspeed-gpio",
> -       .irq_ack        = aspeed_gpio_irq_ack,
> -       .irq_mask       = aspeed_gpio_irq_mask,
> -       .irq_unmask     = aspeed_gpio_irq_unmask,
> -       .irq_set_type   = aspeed_gpio_set_type,
> -};
> -
>  static void set_irq_valid_mask(struct aspeed_gpio *gpio)
>  {
>         const struct aspeed_bank_props *props = gpio->config->props;
> @@ -1192,7 +1185,12 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>
>                 gpio->irq = rc;
>                 girq = &gpio->chip.irq;
> -               girq->chip = &aspeed_gpio_irqchip;
> +               girq->chip = &gpio->irqc;
> +               girq->chip->name = dev_name(&pdev->dev);
> +               girq->chip->irq_ack = aspeed_gpio_irq_ack;
> +               girq->chip->irq_mask = aspeed_gpio_irq_mask;
> +               girq->chip->irq_unmask = aspeed_gpio_irq_unmask;
> +               girq->chip->irq_set_type = aspeed_gpio_set_type;
>                 girq->parent_handler = aspeed_gpio_irq_handler;
>                 girq->num_parents = 1;
>                 girq->parents = devm_kcalloc(&pdev->dev, 1,
> --
> 2.20.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 v3 4/5] gpios: Use ngpio property from device tree if available
From: Joel Stanley @ 2019-09-06 19:47 UTC (permalink / raw)
  To: Rashmica Gupta
  Cc: moderated list:ARM/ASPEED MACHINE SUPPORT,
	open list:GPIO SUBSYSTEM, Andrew Jeffery, Linus Walleij,
	open list, Bartosz Golaszewski,
	moderated list:ARM/ASPEED MACHINE SUPPORT
In-Reply-To: <20190906062727.13521-1-rashmica.g@gmail.com>

On Fri, 6 Sep 2019 at 06:27, Rashmica Gupta <rashmica.g@gmail.com> wrote:
>
> Use the ngpio property from the device tree if it exists. If it doesn't
> then fallback to the hardcoded value in the config.
>
> This is in preparation for adding ast2600 support. The ast2600 SoC has
> two GPIO controllers and so requires two instances of the GPIO driver.
> We use the ngpio property to different between them as they have
> different numbers of GPIOs.
>
> Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>

Reviewed-by: Joel Stanley <joel@jms.id.au>

> ---
>  drivers/gpio/gpio-aspeed.c | 18 +++++++++++-------
>  1 file changed, 11 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index 16c6eaf70857..c3d5ecba343b 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -694,7 +694,7 @@ static void set_irq_valid_mask(struct aspeed_gpio *gpio)
>                 for_each_clear_bit(offset, &input, 32) {
>                         unsigned int i = props->bank * 32 + offset;
>
> -                       if (i >= gpio->config->nr_gpios)
> +                       if (i >= gpio->chip.ngpio)
>                                 break;
>
>                         clear_bit(i, gpio->chip.irq.valid_mask);
> @@ -1007,10 +1007,10 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
>         unsigned long flags;
>
>         if (!gpio->cf_copro_bankmap)
> -               gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL);
> +               gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL);
>         if (!gpio->cf_copro_bankmap)
>                 return -ENOMEM;
> -       if (offset < 0 || offset > gpio->config->nr_gpios)
> +       if (offset < 0 || offset > gpio->chip.ngpio)
>                 return -EINVAL;
>         bindex = offset >> 3;
>
> @@ -1055,7 +1055,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
>         if (!gpio->cf_copro_bankmap)
>                 return -ENXIO;
>
> -       if (offset < 0 || offset > gpio->config->nr_gpios)
> +       if (offset < 0 || offset > gpio->chip.ngpio)
>                 return -EINVAL;
>         bindex = offset >> 3;
>
> @@ -1119,7 +1119,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>  {
>         const struct of_device_id *gpio_id;
>         struct aspeed_gpio *gpio;
> -       int rc, i, banks;
> +       int rc, i, banks, err;
> +       u32 ngpio;
>
>         gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
>         if (!gpio)
> @@ -1145,7 +1146,10 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>         gpio->config = gpio_id->data;
>
>         gpio->chip.parent = &pdev->dev;
> -       gpio->chip.ngpio = gpio->config->nr_gpios;
> +       err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio);
> +       gpio->chip.ngpio = (u16) ngpio;
> +       if (err)
> +               gpio->chip.ngpio = gpio->config->nr_gpios;
>         gpio->chip.direction_input = aspeed_gpio_dir_in;
>         gpio->chip.direction_output = aspeed_gpio_dir_out;
>         gpio->chip.get_direction = aspeed_gpio_get_direction;
> @@ -1158,7 +1162,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>         gpio->chip.base = -1;
>
>         /* Allocate a cache of the output registers */
> -       banks = DIV_ROUND_UP(gpio->config->nr_gpios, 32);
> +       banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
>         gpio->dcache = devm_kcalloc(&pdev->dev,
>                                     banks, sizeof(u32), GFP_KERNEL);
>         if (!gpio->dcache)
> --
> 2.20.1
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [GIT PULL] ARM: SoC fixes for -rc8
From: Arnd Bergmann @ 2019-09-06 19:48 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Geert Uytterhoeven, Linux Kernel Mailing List, SoC Team,
	Simon Horman, Andy Gross, Lee Jones, Linux ARM

The following changes since commit 089cf7f6ecb266b6a4164919a2e69bd2f938374a:

  Linux 5.3-rc7 (2019-09-02 09:57:40 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git tags/armsoc-fixes

for you to fetch changes up to 8928e917aeafaf38d65cc5cbc1f11e952dbed062:

  soc: qcom: geni: Provide parameter error checking (2019-09-06 11:08:08 +0200)

----------------------------------------------------------------
ARM: SoC fixes

There are three more fixes for this week:

- The Windows-on-ARM laptops require a workaround to
  prevent crashing at boot from ACPI
- The Renesas "draak" board needs one bugfix for
  the backlight regulator
- Also for Renesas, the "hihope" board accidentally
  had its eMMC turned off in the 5.3 merge window.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>

----------------------------------------------------------------
Arnd Bergmann (2):
      Merge tag 'renesas-fixes-for-v5.3' of
git://git.kernel.org/.../horms/renesas into arm/fixes
      Merge tag 'renesas-fixes2-for-v5.3' of
git://git.kernel.org/.../horms/renesas into arm/fixes

Fabrizio Castro (1):
      arm64: dts: renesas: hihope-common: Fix eMMC status

Geert Uytterhoeven (1):
      arm64: dts: renesas: r8a77995: draak: Fix backlight regulator name

Lee Jones (1):
      soc: qcom: geni: Provide parameter error checking

 arch/arm64/boot/dts/renesas/hihope-common.dtsi | 1 +
 arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 6 +++---
 drivers/soc/qcom/qcom-geni-se.c                | 6 ++++++
 3 files changed, 10 insertions(+), 3 deletions(-)

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [GIT PULL] ARM: SoC fixes for -rc8
From: pr-tracker-bot @ 2019-09-06 20:00 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Geert Uytterhoeven, Lee Jones, Linux Kernel Mailing List,
	SoC Team, Simon Horman, Andy Gross, Linus Torvalds, Linux ARM
In-Reply-To: <CAK8P3a0MsTFjqChoz+DLSC8nVnBuvqQdYx6V0SuCybg7MZ79mQ@mail.gmail.com>

The pull request you sent on Fri, 6 Sep 2019 21:48:09 +0200:

> git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git tags/armsoc-fixes

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/36daa831b55538dc2e4a906de20c5d91033ebb21

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.wiki.kernel.org/userdoc/prtracker

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox