Selaa lähdekoodia

Merged TI feature platform_base into ti-linux-4.19.y

* 'platform-ti-linux-4.19.y' of ssh://bitbucket.itg.ti.com/lcpdpublicdom/platform:
  HACK: ti_config_fragments: v8_baseport: Enable DMA-BUF export driver
  HACK: ti_config_fragments: baseport: Enable DMA-BUF export driver
  HACK: misc: Add dma-buf to physical address exporter
  HACK: dt-bindings: misc: Add ti,dma_buf_phys binding doc
  ti_config_fragments: v8_baseport: Enable PAT driver
  soc: ti: Add Support for the TI Page-based Address Translator (PAT)
  arm64: dts: ti: k3-j721e-main: Add MAIN domain PAT nodes
  dt-bindings: soc: ti: Add TI PAT bindings
  dma-buf: Make mmap callback actually optional
  dma-buf: Remove requirement for ops->map() from dma_buf_export
  crypto: sa2ul: Correct the length of payload that is transferred to rx_chan2
  crypto: sa2ul: Add support for basic sha1/sha256 algorithms
  crypto: tcrypt: Fix speed test case for hash to set up sg buffer size equal to payload
  crypto: sa2ul: Add software fallback support

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger 6 vuotta sitten
vanhempi
commit
971e654d3f

+ 17 - 0
Documentation/devicetree/bindings/misc/ti,dma_buf_phys.txt

@@ -0,0 +1,17 @@
+DMA-BUF contiguous buffer physical address user-space exporter binding
+
+Driver allowing user-space attaching of DMA-BUFs returning CPU physical
+addresses. The reasoning for making this act like a regular device
+described by DT is so the virtual device that binds the buffer can be
+made to act as if it is out on a bus or behind an IOMMU, for example.
+
+Required Properties:
+ - compatible:		Must be "ti,dma_buf_phys"
+
+Example:
+
+dma_buf_phys {
+	compatible = "ti,dma_buf_phys";
+	iommus = <&some_iommu>;
+	dma-coherent;
+};

+ 35 - 0
Documentation/devicetree/bindings/misc/ti,pat.txt

@@ -0,0 +1,35 @@
+Texas Instruments Page-based Address Translator (PAT) driver binding
+--------------------------------------------------------------------
+
+A Page-based Address Translator (PAT) device performs address translation
+using tables stored in an internal SRAM. Each PAT supports a set number of
+pages, each occupying a programmable 4KB, 16KB, 64KB, or 1MB of addresses
+in a window for which an incoming transaction will be translated.
+
+TI-PAT controller Device Node
+=============================
+
+The TI-PAT node describes the Texas Instrument's Page-based Address
+Translator (PAT).
+
+Required properties:
+-------------------
+- compatible: should be "ti,j721e-pat"
+- reg-names:
+	mmrs - Memory mapped registers region
+	table - Location of the table of translation pages
+- reg: register addresses corresponding to the above
+- ti,pat-window-base: base address of window translated by this PAT
+- ti,pat-window-size: size of window translated by this PAT
+
+Example:
+
+navss_pat0: pat@31010000 {
+	compatible = "ti,j721e-pat";
+	reg = <0x00 0x31010000 0x00 0x00000100>,
+	      <0x00 0x36400000 0x00 0x00040000>;
+	reg-names = "mmrs",
+	            "table";
+	ti,pat-window-base = <0x48 0x00000000>;
+	ti,pat-window-size = <0x00 0x40000000>;
+};

+ 50 - 0
arch/arm64/boot/dts/ti/k3-j721e-main.dtsi

@@ -356,6 +356,56 @@
 			ti,cpts-periodic-outputs = <6>;
 			ti,cpts-ext-ts-inputs = <8>;
 		};
+
+		navss_pat0: pat@31010000 {
+			compatible = "ti,j721e-pat";
+			reg = <0x00 0x31010000 0x00 0x00000100>,
+			      <0x00 0x36400000 0x00 0x00040000>;
+			reg-names = "mmrs",
+				    "table";
+			ti,pat-window-base = <0x48 0x00000000>;
+			ti,pat-window-size = <0x00 0x40000000>;
+		};
+
+		navss_pat1: pat@31011000 {
+			compatible = "ti,j721e-pat";
+			reg = <0x00 0x31011000 0x00 0x00000100>,
+			      <0x00 0x36440000 0x00 0x00040000>;
+			reg-names = "mmrs",
+				    "table";
+			ti,pat-window-base = <0x48 0x40000000>;
+			ti,pat-window-size = <0x00 0x40000000>;
+		};
+
+		navss_pat2: pat@31012000 {
+			compatible = "ti,j721e-pat";
+			reg = <0x00 0x31012000 0x00 0x00000100>,
+			      <0x00 0x36480000 0x00 0x00040000>;
+			reg-names = "mmrs",
+				    "table";
+			ti,pat-window-base = <0x48 0x80000000>;
+			ti,pat-window-size = <0x00 0x40000000>;
+		};
+
+		navss_pat3: pat@31013000 {
+			compatible = "ti,j721e-pat";
+			reg = <0x00 0x31013000 0x00 0x00000100>,
+			      <0x00 0x364C0000 0x00 0x00008000>;
+			reg-names = "mmrs",
+				    "table";
+			ti,pat-window-base = <0x49 0x00000000>;
+			ti,pat-window-size = <0x00 0x80000000>;
+		};
+
+		navss_pat4: pat@31014000 {
+			compatible = "ti,j721e-pat";
+			reg = <0x00 0x31014000 0x00 0x00000100>,
+			      <0x00 0x36500000 0x00 0x00008000>;
+			reg-names = "mmrs",
+				    "table";
+			ti,pat-window-base = <0x49 0x80000000>;
+			ti,pat-window-size = <0x00 0x80000000>;
+		};
 	};
 
 	psilss@3400000 {

+ 1 - 0
crypto/tcrypt.c

@@ -1110,6 +1110,7 @@ static void test_ahash_speed_common(const char *algo, unsigned int secs,
 			"(%5u byte blocks,%5u bytes per update,%4u updates): ",
 			i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen);
 
+		sg_set_buf(sg, tvmem[0], speed[i].plen);
 		ahash_request_set_crypt(req, sg, output, speed[i].plen);
 
 		if (secs) {

+ 289 - 43
drivers/crypto/sa2ul.c

@@ -53,6 +53,7 @@
 #define SA_SW0_ENG_ID_MASK	0x3E000000
 #define SA_SW0_DEST_INFO_PRESENT	BIT(30)
 #define SA_SW2_EGRESS_LENGTH		0xFF000000
+#define SA_BASIC_HASH		0x10
 
 #define SHA256_DIGEST_WORDS    8
 /* Make 32-bit word from 4 bytes */
@@ -415,6 +416,9 @@ static void sa_set_sc_auth(struct algo_data *ad, const u8 *key, u16 key_sz,
 		memcpy(&sc_buf[32], ipad, ad->hash_size);
 		/* Copy opad to Aux-1 */
 		memcpy(&sc_buf[64], opad, ad->hash_size);
+	} else {
+		/* basic hash */
+		sc_buf[1] |= SA_BASIC_HASH;
 	}
 }
 
@@ -1466,42 +1470,6 @@ static int sa_aead_decrypt(struct aead_request *req)
 	return sa_aead_run(req, req->iv, 0);
 }
 
-static int sa_sham_cra_init_alg(struct crypto_tfm *tfm, const char *alg_base)
-{
-	struct sa_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
-	struct crypto_alg *alg = tfm->__crt_alg;
-	struct sa_crypto_data *data = dev_get_drvdata(sa_k3_dev);
-	int ret;
-
-	if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
-	    CRYPTO_ALG_TYPE_AHASH) {
-		memset(ctx, 0, sizeof(*ctx));
-		ctx->dev_data = data;
-		ret = sa_init_ctx_info(&ctx->enc, data);
-		if (ret)
-			return ret;
-	}
-
-	if (alg_base) {
-		ctx->shash = crypto_alloc_shash(alg_base, 0,
-						CRYPTO_ALG_NEED_FALLBACK);
-		if (IS_ERR(ctx->shash)) {
-			pr_err("base driver %s couldn't be loaded\n", alg_base);
-			return PTR_ERR(ctx->shash);
-		}
-	}
-
-	dev_dbg(sa_k3_dev, "%s(0x%p) sc-ids(0x%x(0x%pad), 0x%x(0x%pad))\n",
-		__func__, tfm, ctx->enc.sc_id, &ctx->enc.sc_phys,
-		ctx->dec.sc_id, &ctx->dec.sc_phys);
-
-	crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
-				 sizeof(struct sa_dma_req_ctx) +
-				 SHA512_BLOCK_SIZE);
-
-	return 0;
-}
-
 static void sa_sham_dma_in_callback(void *data)
 {
 	struct sa_rx_data *rxd = (struct sa_rx_data *)data;
@@ -1526,6 +1494,30 @@ static void sa_sham_dma_in_callback(void *data)
 	ahash_request_complete(req, 0);
 }
 
+/*
+ * IC can not process zero message hash,
+ * so we put the fixed hash out when met zero message.
+ */
+
+static int zero_message_process(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	int sa_digest_size = crypto_ahash_digestsize(tfm);
+
+	switch (sa_digest_size) {
+	case SHA1_DIGEST_SIZE:
+		memcpy(req->result, sha1_zero_message_hash, sa_digest_size);
+		break;
+	case SHA256_DIGEST_SIZE:
+		memcpy(req->result, sha256_zero_message_hash, sa_digest_size);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int sa_sham_digest(struct ahash_request *req)
 {
 	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
@@ -1556,7 +1548,10 @@ static int sa_sham_digest(struct ahash_request *req)
 	auth_len = req->nbytes;
 	enc_offset = 0;
 
-	if (enc_len > 256)
+	if (!req->nbytes)
+		return zero_message_process(req);
+
+	if (auth_len >= 256)
 		dma_rx = pdata->dma_rx2;
 	else
 		dma_rx = pdata->dma_rx1;
@@ -1635,14 +1630,109 @@ static int sa_sham_digest(struct ahash_request *req)
 	return -EINPROGRESS;
 }
 
+static int sa_sha_setup(struct sa_tfm_ctx *ctx, struct  algo_data *ad)
+{
+	int bs = crypto_shash_blocksize(ctx->shash);
+	int cmdl_len;
+	struct sa_cmdl_cfg cfg;
+
+	memset(ctx->authkey, 0, bs);
+	memset(&cfg, 0, sizeof(cfg));
+	cfg.enc1st = 0;
+	cfg.aalg = ad->aalg_id;
+	cfg.enc_eng_id = ad->enc_eng.eng_id;
+	cfg.auth_eng_id = ad->auth_eng.eng_id;
+	cfg.iv_size = 0;
+	cfg.akey = NULL;
+	cfg.akey_len = 0;
+
+	/* Setup Encryption Security Context & Command label template */
+	if (sa_init_sc(&ctx->enc, NULL, 0, NULL, 0, ad, 0,
+		       &ctx->enc.epib[1], true))
+		goto badkey;
+
+	cmdl_len = sa_format_cmdl_gen(&cfg,
+				      (u8 *)ctx->enc.cmdl,
+				      &ctx->enc.cmdl_upd_info);
+	if (cmdl_len <= 0 || (cmdl_len > SA_MAX_CMDL_WORDS * sizeof(u32)))
+		goto badkey;
+
+	ctx->enc.cmdl_size = cmdl_len;
+
+	return 0;
+badkey:
+	dev_err(sa_k3_dev, "%s: badkey\n", __func__);
+	return -EINVAL;
+}
+
+static int sa_sham_cra_init_alg(struct crypto_tfm *tfm, const char *alg_base)
+{
+	struct sa_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_alg *alg = tfm->__crt_alg;
+	struct sa_crypto_data *data = dev_get_drvdata(sa_k3_dev);
+	int ret;
+
+	if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+	    CRYPTO_ALG_TYPE_AHASH) {
+		memset(ctx, 0, sizeof(*ctx));
+		ctx->dev_data = data;
+		ret = sa_init_ctx_info(&ctx->enc, data);
+		if (ret)
+			return ret;
+	}
+
+	if (alg_base) {
+		ctx->shash = crypto_alloc_shash(alg_base, 0,
+						CRYPTO_ALG_NEED_FALLBACK);
+		if (IS_ERR(ctx->shash)) {
+			pr_err("base driver %s couldn't be loaded\n", alg_base);
+			return PTR_ERR(ctx->shash);
+		}
+		/* for fallback */
+		ctx->fallback_tfm = crypto_alloc_ahash(alg_base, 0,
+						CRYPTO_ALG_NEED_FALLBACK);
+		if (IS_ERR(ctx->fallback_tfm)) {
+			dev_err(ctx->dev_data->dev,
+				"Could not load fallback driver\n");
+			return PTR_ERR(ctx->fallback_tfm);
+		}
+	}
+
+	dev_dbg(sa_k3_dev, "%s(0x%p) sc-ids(0x%x(0x%pad), 0x%x(0x%pad))\n",
+		__func__, tfm, ctx->enc.sc_id, &ctx->enc.sc_phys,
+		ctx->dec.sc_id, &ctx->dec.sc_phys);
+
+	crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+				 sizeof(struct sa_dma_req_ctx) +
+				 crypto_ahash_reqsize(ctx->fallback_tfm));
+
+	return 0;
+}
+
+static int sa_sha1_digest(struct ahash_request *req)
+{
+	return sa_sham_digest(req);
+}
+
+static int sa_sha256_digest(struct ahash_request *req)
+{
+	return sa_sham_digest(req);
+}
+
 static int sa_sham_init(struct ahash_request *req)
 {
 	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct sa_dma_req_ctx *rctx = ahash_request_ctx(req);
+	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(tfm);
 
 	dev_dbg(sa_k3_dev, "init: digest size: %d\n",
 		crypto_ahash_digestsize(tfm));
 
-	return 0;
+	ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
+	rctx->fallback_req.base.flags = req->base.flags &
+					CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	return crypto_ahash_init(&rctx->fallback_req);
 }
 
 static int sa_sham_shash_digest(struct crypto_shash *tfm, u32 flags,
@@ -1677,6 +1767,9 @@ static int sa_sham_setkey(struct crypto_ahash *tfm, const u8 *key,
 		memcpy(ctx->authkey, key, keylen);
 	}
 
+	/* Set up the fallback algorithm with key as well */
+	crypto_ahash_setkey(ctx->fallback_tfm, key, keylen);
+
 	memset(ctx->authkey + keylen, 0, bs - keylen);
 	memset(&cfg, 0, sizeof(cfg));
 	cfg.enc1st = 0;
@@ -1754,12 +1847,56 @@ static int sa_sham_sha256_setkey(struct crypto_ahash *tfm, const u8 *key,
 
 static int sa_sham_cra_sha1_init(struct crypto_tfm *tfm)
 {
-	return sa_sham_cra_init_alg(tfm, "sha1");
+	struct algo_data *ad = kzalloc(sizeof(*ad), GFP_KERNEL);
+	struct sa_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	sa_sham_cra_init_alg(tfm, "sha1");
+
+	ad->enc_eng.eng_id = SA_ENG_ID_NONE;
+	ad->enc_eng.sc_size = SA_CTX_ENC_TYPE1_SZ;
+	ad->auth_eng.eng_id = SA_ENG_ID_AM1;
+	ad->auth_eng.sc_size = SA_CTX_AUTH_TYPE2_SZ;
+	ad->mci_enc = NULL;
+	ad->mci_dec = NULL;
+	ad->inv_key = false;
+	ad->keyed_mac = false;
+	ad->ealg_id = SA_EALG_ID_NONE;
+	ad->aalg_id = SA_AALG_ID_SHA1;
+	ad->hash_size = SHA1_DIGEST_SIZE;
+	ad->auth_ctrl = 0x2;
+
+	sa_sha_setup(ctx, ad);
+
+	kfree(ad);
+
+	return 0;
 }
 
 static int sa_sham_cra_sha256_init(struct crypto_tfm *tfm)
 {
-	return sa_sham_cra_init_alg(tfm, "sha256");
+	struct algo_data *ad = kzalloc(sizeof(*ad), GFP_KERNEL);
+	struct sa_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	sa_sham_cra_init_alg(tfm, "sha256");
+
+	ad->enc_eng.eng_id = SA_ENG_ID_NONE;
+	ad->enc_eng.sc_size = SA_CTX_ENC_TYPE1_SZ;
+	ad->auth_eng.eng_id = SA_ENG_ID_AM1;
+	ad->auth_eng.sc_size = SA_CTX_AUTH_TYPE2_SZ;
+	ad->mci_enc = NULL;
+	ad->mci_dec = NULL;
+	ad->inv_key = false;
+	ad->keyed_mac = false;
+	ad->ealg_id = SA_EALG_ID_NONE;
+	ad->aalg_id = SA_AALG_ID_SHA2_256;
+	ad->hash_size = SHA256_DIGEST_SIZE;
+	ad->auth_ctrl = 0x4;
+
+	sa_sha_setup(ctx, ad);
+
+	kfree(ad);
+
+	return 0;
 }
 
 static void sa_sham_cra_exit(struct crypto_tfm *tfm)
@@ -1780,17 +1917,74 @@ static void sa_sham_cra_exit(struct crypto_tfm *tfm)
 
 static int sa_sham_update(struct ahash_request *req)
 {
-	return 0;
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct sa_dma_req_ctx *rctx = ahash_request_ctx(req);
+	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
+	rctx->fallback_req.base.flags = req->base.flags &
+					CRYPTO_TFM_REQ_MAY_SLEEP;
+	rctx->fallback_req.nbytes = req->nbytes;
+	rctx->fallback_req.src = req->src;
+
+	return crypto_ahash_update(&rctx->fallback_req);
 }
 
 static int sa_sham_final(struct ahash_request *req)
 {
-	return sa_sham_digest(req);
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct sa_dma_req_ctx *rctx = ahash_request_ctx(req);
+	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
+	rctx->fallback_req.base.flags = req->base.flags &
+					CRYPTO_TFM_REQ_MAY_SLEEP;
+	rctx->fallback_req.result = req->result;
+
+	return crypto_ahash_final(&rctx->fallback_req);
 }
 
 static int sa_sham_finup(struct ahash_request *req)
 {
-	return sa_sham_digest(req);
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct sa_dma_req_ctx *rctx = ahash_request_ctx(req);
+	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
+	rctx->fallback_req.base.flags = req->base.flags &
+					CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	rctx->fallback_req.nbytes = req->nbytes;
+	rctx->fallback_req.src = req->src;
+	rctx->fallback_req.result = req->result;
+
+	return crypto_ahash_finup(&rctx->fallback_req);
+}
+
+static int sa_sham_import(struct ahash_request *req, const void *in)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct sa_dma_req_ctx *rctx = ahash_request_ctx(req);
+	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
+	rctx->fallback_req.base.flags = req->base.flags &
+					CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	return crypto_ahash_import(&rctx->fallback_req, in);
+}
+
+static int sa_sham_export(struct ahash_request *req, void *out)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct sa_dma_req_ctx *rctx = ahash_request_ctx(req);
+	struct sa_tfm_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
+	rctx->fallback_req.base.flags = req->base.flags &
+					CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	return crypto_ahash_export(&rctx->fallback_req, out);
 }
 
 static struct sa_alg_tmpl sa_algs[] = {
@@ -1948,6 +2142,54 @@ static struct sa_alg_tmpl sa_algs[] = {
 };
 
 static struct ahash_alg algs_sha[] = {
+{
+	.init		= sa_sham_init,
+	.update		= sa_sham_update,
+	.final		= sa_sham_final,
+	.finup		= sa_sham_finup,
+	.digest		= sa_sha1_digest,
+	.export		= sa_sham_export,
+	.import		= sa_sham_import,
+	.halg.digestsize	= SHA1_DIGEST_SIZE,
+	.halg.statesize		= 128,
+	.halg.base	= {
+		.cra_name		= "sha1",
+		.cra_driver_name	= "sa-sha1",
+		.cra_priority		= 400,
+		.cra_flags		= CRYPTO_ALG_ASYNC |
+						CRYPTO_ALG_NEED_FALLBACK,
+		.cra_blocksize		= SHA1_BLOCK_SIZE,
+		.cra_ctxsize		= sizeof(struct sa_tfm_ctx),
+		.cra_alignmask		= SA_ALIGN_MASK,
+		.cra_module		= THIS_MODULE,
+		.cra_init		= sa_sham_cra_sha1_init,
+		.cra_exit		= sa_sham_cra_exit,
+	}
+},
+{
+	.init		= sa_sham_init,
+	.update		= sa_sham_update,
+	.final		= sa_sham_final,
+	.finup		= sa_sham_finup,
+	.digest		= sa_sha256_digest,
+	.export		= sa_sham_export,
+	.import		= sa_sham_import,
+	.halg.digestsize	= SHA256_DIGEST_SIZE,
+	.halg.statesize		= 128,
+	.halg.base	= {
+		.cra_name		= "sha256",
+		.cra_driver_name	= "sa-sha256",
+		.cra_priority		= 400,
+		.cra_flags		= CRYPTO_ALG_ASYNC |
+						CRYPTO_ALG_NEED_FALLBACK,
+		.cra_blocksize		= SHA256_BLOCK_SIZE,
+		.cra_ctxsize		= sizeof(struct sa_tfm_ctx),
+		.cra_alignmask		= SA_ALIGN_MASK,
+		.cra_module		= THIS_MODULE,
+		.cra_init		= sa_sham_cra_sha256_init,
+		.cra_exit		= sa_sham_cra_exit,
+	}
+},
 {
 	.init		= sa_sham_init,
 	.update		= sa_sham_update,
@@ -1955,6 +2197,8 @@ static struct ahash_alg algs_sha[] = {
 	.finup		= sa_sham_finup,
 	.digest		= sa_sham_digest,
 	.setkey		= sa_sham_sha1_setkey,
+	.export		= sa_sham_export,
+	.import		= sa_sham_import,
 	.halg.digestsize	= SHA1_DIGEST_SIZE,
 	.halg.statesize		= 128,
 	.halg.base	= {
@@ -1980,6 +2224,8 @@ static struct ahash_alg algs_sha[] = {
 	.finup		= sa_sham_finup,
 	.digest		= sa_sham_digest,
 	.setkey		= sa_sham_sha256_setkey,
+	.export		= sa_sham_export,
+	.import		= sa_sham_import,
 	.halg.digestsize	= SHA256_DIGEST_SIZE,
 	.halg.statesize		= 128,
 	.halg.base	= {

+ 5 - 0
drivers/crypto/sa2ul.h

@@ -287,6 +287,7 @@ struct sa_sham_hmac_ctx {
  * @key: encryption key
  * @shash: software hash crypto_hash
  * @authkey: authentication key
+ * @fallback_tfm: SW fallback ahash algorithm
  */
 struct sa_tfm_ctx {
 	struct sa_crypto_data *dev_data;
@@ -298,6 +299,8 @@ struct sa_tfm_ctx {
 	struct sa_sham_hmac_ctx base[0];
 	struct crypto_shash	*shash;
 	u8 authkey[SHA512_BLOCK_SIZE];
+	/* for fallback */
+	struct crypto_ahash	*fallback_tfm;
 };
 
 /**
@@ -314,6 +317,8 @@ struct sa_dma_req_ctx {
 	struct scatterlist *src;
 	unsigned int	src_nents;
 	bool		pkt;
+	struct ahash_request fallback_req;
+	u32 mode;
 };
 
 enum sa_submode {

+ 9 - 3
drivers/dma-buf/dma-buf.c

@@ -90,6 +90,10 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
 
 	dmabuf = file->private_data;
 
+	/* check if buffer supports mmap */
+	if (!dmabuf->ops->mmap)
+		return -EINVAL;
+
 	/* check for overflowing the buffer's size */
 	if (vma->vm_pgoff + vma_pages(vma) >
 	    dmabuf->size >> PAGE_SHIFT)
@@ -404,9 +408,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
 			  || !exp_info->ops
 			  || !exp_info->ops->map_dma_buf
 			  || !exp_info->ops->unmap_dma_buf
-			  || !exp_info->ops->release
-			  || !exp_info->ops->map
-			  || !exp_info->ops->mmap)) {
+			  || !exp_info->ops->release)) {
 		return ERR_PTR(-EINVAL);
 	}
 
@@ -907,6 +909,10 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
 	if (WARN_ON(!dmabuf || !vma))
 		return -EINVAL;
 
+	/* check if buffer supports mmap */
+	if (!dmabuf->ops->mmap)
+		return -EINVAL;
+
 	/* check for offset overflow */
 	if (pgoff + vma_pages(vma) < pgoff)
 		return -EOVERFLOW;

+ 5 - 0
drivers/misc/Kconfig

@@ -513,6 +513,11 @@ config MISC_RTSX
 	tristate
 	default MISC_RTSX_PCI || MISC_RTSX_USB
 
+config DMA_BUF_PHYS
+	tristate "DMA-BUF physical address user-space exporter"
+	help
+	  Exports CPU physical address of DMA-BUF to user-space.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"

+ 1 - 0
drivers/misc/Makefile

@@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-$(CONFIG_MISC_RTSX)		+= cardreader/
+obj-$(CONFIG_DMA_BUF_PHYS)	+= dma-buf-phys.o

+ 217 - 0
drivers/misc/dma-buf-phys.c

@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DMA-BUF contiguous buffer physical address user-space exporter
+ *
+ * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/dma_buf_phys.h>
+
+#define DEVICE_NAME "dma-buf-phys"
+
+struct dma_buf_phys_priv {
+	struct miscdevice miscdev;
+};
+
+struct dma_buf_phys_file {
+	struct device *dev;
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attachment;
+	struct sg_table *sgt;
+};
+
+static int dma_buf_phys_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *miscdev = file->private_data;
+	struct device *dev = miscdev->this_device;
+	struct dma_buf_phys_file *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->dev = dev;
+	file->private_data = (void *)priv;
+
+	return 0;
+}
+
+static int dma_buf_phys_release(struct inode *inode, struct file *file)
+{
+	struct dma_buf_phys_file *priv = file->private_data;
+
+	if (priv->attachment && priv->sgt)
+		dma_buf_unmap_attachment(priv->attachment, priv->sgt, DMA_BIDIRECTIONAL);
+	if (priv->dma_buf && priv->attachment)
+		dma_buf_detach(priv->dma_buf, priv->attachment);
+	if (priv->dma_buf)
+		dma_buf_put(priv->dma_buf);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static int dma_buf_phys_convert(struct dma_buf_phys_file *priv, int fd, u64 *phys)
+{
+	struct device *dev = priv->dev;
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attachment;
+	struct sg_table *sgt;
+	dma_addr_t dma_addr;
+	int ret;
+
+	dma_buf = dma_buf_get(fd);
+	if (IS_ERR(dma_buf))
+		return PTR_ERR(dma_buf);
+
+	/* Attach as the parent device as it will have the correct DMA ops set */
+	attachment = dma_buf_attach(dma_buf, dev->parent);
+	if (IS_ERR(attachment)) {
+		ret = PTR_ERR(attachment);
+		goto fail_put;
+	}
+
+	sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
+	if (IS_ERR(sgt)) {
+		ret = PTR_ERR(sgt);
+		goto fail_detach;
+	}
+
+	/* Without PAT only physically contiguous buffers can be supported */
+	if (sgt->orig_nents != 1) {
+		dev_err(dev, "DMA-BUF not contiguous\n");
+		ret = -EINVAL;
+		goto fail_unmap;
+	}
+
+	dma_addr = sg_dma_address(sgt->sgl);
+
+	*phys = dma_addr;
+
+	priv->dma_buf = dma_buf;
+	priv->attachment = attachment;
+	priv->sgt = sgt;
+
+	return 0;
+
+fail_unmap:
+	dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL);
+fail_detach:
+	dma_buf_detach(dma_buf, attachment);
+fail_put:
+	dma_buf_put(dma_buf);
+
+	return ret;
+}
+
+static long dma_buf_phys_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct dma_buf_phys_file *priv = file->private_data;
+
+	switch (cmd) {
+	case DMA_BUF_PHYS_IOC_CONVERT:
+	{
+		struct dma_buf_phys_data data;
+		int ret;
+
+		/*
+		 * TODO: this should likely be properly serialized, but I
+		 * see no reason this file would ever need to be shared.
+		 */
+		/* one attachment per file */
+		if (priv->dma_buf)
+			return -EFAULT;
+
+		if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+		ret = dma_buf_phys_convert(priv, data.fd, &data.phys);
+		if (ret)
+			return ret;
+
+		if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static const struct file_operations dma_buf_phys_fops = {
+	.owner = THIS_MODULE,
+	.open = dma_buf_phys_open,
+	.release = dma_buf_phys_release,
+	.unlocked_ioctl = dma_buf_phys_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= dma_buf_phys_ioctl,
+#endif
+};
+
+static int dma_buf_phys_probe(struct platform_device *pdev)
+{
+	struct dma_buf_phys_priv *priv;
+	struct device *dev = &pdev->dev;
+	int err;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	dev_set_drvdata(dev, priv);
+
+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+	priv->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", DEVICE_NAME);
+	priv->miscdev.fops = &dma_buf_phys_fops;
+	priv->miscdev.parent = dev;
+	err = misc_register(&priv->miscdev);
+	if (err) {
+		dev_err(dev, "unable to register DMA-BUF to Phys misc device\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int dma_buf_phys_remove(struct platform_device *pdev)
+{
+	struct dma_buf_phys_priv *priv = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&priv->miscdev);
+
+	return 0;
+}
+
+static const struct of_device_id dma_buf_phys_of_match[] = {
+	{ .compatible = "ti,dma_buf_phys", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dma_buf_phys_of_match);
+
+static struct platform_driver dma_buf_phys_driver = {
+	.probe = dma_buf_phys_probe,
+	.remove = dma_buf_phys_remove,
+	.driver = {
+		.name = "dma_buf_phys",
+		.of_match_table = dma_buf_phys_of_match,
+	}
+};
+module_platform_driver(dma_buf_phys_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("DMA-BUF contiguous buffer physical address user-space exporter");
+MODULE_LICENSE("GPL v2");

+ 10 - 0
drivers/soc/ti/Kconfig

@@ -125,4 +125,14 @@ config TI_K3_UDMA_DESC_POOL
 	  pool of descriptors usable with CPPI5 DMA. Such pools are
 	  required for networking usecases.
 
+config TI_PAT
+	tristate "TI PAT DMA-BUF exporter"
+	depends on ARCH_K3
+	select REGMAP
+	help
+	  Driver for TI Page-based Address Translator (PAT). This driver
+	  provides the an API allowing the remapping of a non-contiguous
+	  DMA-BUF into a contiguous one that is sutable for devices needing
+	  contiguous memory.
+
 endif # SOC_TI

+ 1 - 0
drivers/soc/ti/Makefile

@@ -12,3 +12,4 @@ obj-$(CONFIG_TI_SCI_PM_DOMAINS)		+= ti_sci_pm_domains.o
 obj-$(CONFIG_TI_PRUSS)			+= pruss_soc_bus.o pruss.o
 obj-$(CONFIG_TI_K3_RINGACC)		+= k3-ringacc.o
 obj-$(CONFIG_TI_K3_UDMA_DESC_POOL)	+= k3-navss-desc-pool.o
+obj-$(CONFIG_TI_PAT)			+= ti-pat.o

+ 627 - 0
drivers/soc/ti/ti-pat.c

@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI PAT mapped DMA-BUF memory re-exporter
+ *
+ * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/fs.h>
+#include <linux/genalloc.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ti-pat.h>
+
+/* TI PAT MMRS registers */
+#define TI_PAT_MMRS_PID		0x0 /* Revision Register */
+#define TI_PAT_MMRS_CONFIG	0x4 /* Config Register */
+#define TI_PAT_MMRS_CONTROL	0x10 /* Control Register */
+
+/* TI PAT CONTROL register field values */
+#define TI_PAT_CONTROL_ARB_MODE_UF	0x0 /* Updates first */
+#define TI_PAT_CONTROL_ARB_MODE_RR	0x2 /* Round-robin */
+
+#define TI_PAT_CONTROL_PAGE_SIZE_4KB	0x0
+#define TI_PAT_CONTROL_PAGE_SIZE_16KB	0x1
+#define TI_PAT_CONTROL_PAGE_SIZE_64KB	0x2
+#define TI_PAT_CONTROL_PAGE_SIZE_1MB	0x3
+
+/* TI PAT TABLE registers */
+#define TI_PAT_TABLE_ADDRL	0x0 /* Low address Register */
+#define TI_PAT_TABLE_ADDRH	0x4 /* High address and enable Register */
+
+static unsigned int ti_pat_page_sizes[] = {
+	[TI_PAT_CONTROL_PAGE_SIZE_4KB]  = 4 * 1024,
+	[TI_PAT_CONTROL_PAGE_SIZE_16KB] = 16 * 1024,
+	[TI_PAT_CONTROL_PAGE_SIZE_64KB] = 64 * 1024,
+	[TI_PAT_CONTROL_PAGE_SIZE_1MB]  = 1024 * 1024,
+};
+
+enum ti_pat_fields {
+	/* Revision */
+	F_PID_MAJOR,
+	F_PID_MINOR,
+
+	/* Controls */
+	F_CONTROL_ARB_MODE,
+	F_CONTROL_PAGE_SIZE,
+	F_CONTROL_REPLACE_OID_EN,
+	F_CONTROL_EN,
+
+	/* sentinel */
+	F_MMRS_FIELDS,
+
+	/* Table */
+	F_TABLE_ADDRL,
+	F_TABLE_ADDRH,
+	F_TABLE_ENABLE,
+
+	/* sentinel */
+	F_MAX_FIELDS
+};
+
+static struct reg_field ti_pat_reg_fields[] = {
+	/* Revision */
+	[F_PID_MAJOR]			= REG_FIELD(TI_PAT_MMRS_PID, 8, 10),
+	[F_PID_MINOR]			= REG_FIELD(TI_PAT_MMRS_PID, 0, 5),
+	/* Controls */
+	[F_CONTROL_ARB_MODE]		= REG_FIELD(TI_PAT_MMRS_CONTROL, 6, 7),
+	[F_CONTROL_PAGE_SIZE]		= REG_FIELD(TI_PAT_MMRS_CONTROL, 4, 5),
+	[F_CONTROL_REPLACE_OID_EN]	= REG_FIELD(TI_PAT_MMRS_CONTROL, 1, 1),
+	[F_CONTROL_EN]			= REG_FIELD(TI_PAT_MMRS_CONTROL, 0, 0),
+	/* Table */
+	[F_TABLE_ADDRL]			= REG_FIELD(TI_PAT_TABLE_ADDRL, 0, 31),
+	[F_TABLE_ADDRH]			= REG_FIELD(TI_PAT_TABLE_ADDRH, 0, 3),
+	[F_TABLE_ENABLE]		= REG_FIELD(TI_PAT_TABLE_ADDRH, 31, 31),
+};
+
+/**
+ * struct ti_pat_data - PAT device instance data
+ * @dev: PAT device structure
+ * @mdev: misc device
+ * @mmrs_fields: Register fields for both MMRS and TABLE
+ * @page_count: Total number of pages in this PAT
+ * @page_size: Size of region mapped by each page in bytes
+ * @window_base: Base address of WINDOW region
+ * @pool: Pool for managing translation space
+ */
+struct ti_pat_data {
+	struct device *dev;
+	struct miscdevice mdev;
+	struct regmap_field *fields[F_MAX_FIELDS];
+	unsigned int page_count;
+	unsigned int page_size;
+	phys_addr_t window_base;
+	struct gen_pool *pool;
+};
+
+struct ti_pat_dma_buf_attachment {
+	struct device *dev;
+	struct sg_table *table;
+	struct ti_pat_buffer *buffer;
+	struct list_head list;
+};
+
+/**
+ * struct ti_pat_buffer - Single buffer instance data
+ * @pat: PAT instance for whom this buffer belongs
+ * @i_dma_buf: Imported DMA-BUF buffer
+ * @size: Total allocated size of this buffer
+ * @offset: Allocated offset into the PAT window
+ * @e_dma_buf: Exported DMA-BUF buffer
+ * @attachment: Our attachment to the imported buffer
+ * @sgt: DMA map of our imported buffer
+ * @attachments: Attachments to this buffer
+ * @map_count: Reference count of mappings to this buffer
+ * @lock: Protect the attach list and map count
+ */
+struct ti_pat_buffer {
+	struct ti_pat_data *pat;
+	struct dma_buf *i_dma_buf;
+	size_t size;
+	unsigned long offset;
+	struct dma_buf *e_dma_buf;
+
+	struct dma_buf_attachment *attachment;
+	struct sg_table *sgt;
+
+	struct list_head attachments;
+	int map_count;
+
+	struct mutex lock;
+};
+
+static const struct regmap_config ti_pat_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int ti_pat_dma_buf_attach(struct dma_buf *dmabuf,
+				 struct dma_buf_attachment *attachment)
+{
+	struct ti_pat_dma_buf_attachment *a;
+	struct ti_pat_buffer *buffer = dmabuf->priv;
+
+	a = kzalloc(sizeof(*a), GFP_KERNEL);
+	if (!a)
+		return -ENOMEM;
+
+	a->dev = attachment->dev;
+	a->buffer = buffer;
+	INIT_LIST_HEAD(&a->list);
+
+	a->table = kzalloc(sizeof(*a->table), GFP_KERNEL);
+	if (!a->table) {
+		kfree(a);
+		return -ENOMEM;
+	}
+
+	if (sg_alloc_table(a->table, 1, GFP_KERNEL)) {
+		kfree(a->table);
+		kfree(a);
+		return -ENOMEM;
+	}
+
+	sg_set_page(a->table->sgl, pfn_to_page(PFN_DOWN(buffer->offset)), buffer->size, 0);
+
+	attachment->priv = a;
+
+	mutex_lock(&buffer->lock);
+	/* First time attachment we attach to parent */
+	if (list_empty(&buffer->attachments)) {
+		buffer->attachment = dma_buf_attach(buffer->i_dma_buf, buffer->pat->dev);
+		if (IS_ERR(buffer->attachment)) {
+			dev_err(buffer->pat->dev, "Unable to attach to parent DMA-BUF\n");
+			mutex_unlock(&buffer->lock);
+			kfree(a->table);
+			kfree(a);
+			return PTR_ERR(buffer->attachment);
+		}
+	}
+	list_add(&a->list, &buffer->attachments);
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static void ti_pat_dma_buf_detach(struct dma_buf *dmabuf,
+				  struct dma_buf_attachment *attachment)
+{
+	struct ti_pat_dma_buf_attachment *a = attachment->priv;
+	struct ti_pat_buffer *buffer = dmabuf->priv;
+
+	mutex_lock(&buffer->lock);
+	list_del(&a->list);
+	/* Last attachment we detach from parent */
+	if (list_empty(&buffer->attachments)) {
+		dma_buf_detach(buffer->i_dma_buf, buffer->attachment);
+		buffer->attachment = NULL;
+	}
+	mutex_unlock(&buffer->lock);
+
+	kfree(a->table);
+	kfree(a);
+}
+
+static unsigned int ti_pat_table_index_from_page(size_t page)
+{
+	/* Every 256 pages start on a 4k boundary */
+	unsigned int boundery = page / 256;
+	unsigned int offset = page % 256;
+	/* Each page occupies 8 bytes in the table */
+	return (boundery * (4096 / 8)) + offset;
+}
+
+static void ti_pat_set_page(struct ti_pat_data *pat, size_t page_id, dma_addr_t dma_address)
+{
+	unsigned int index = ti_pat_table_index_from_page(page_id);
+
+	/*
+	 * Addresses will always be at least 4K aligned, so both high and low
+	 * addresses are shifted by an additional 12 bits before being written
+	 * to the PAT.
+	 */
+	u32 base_l = dma_address >> 12;
+	u32 base_h = dma_address >> 44;
+
+	dev_dbg(pat->dev, "Enabling PAT index: %zu pointing to %pad\n", page_id, &dma_address);
+
+	regmap_fields_write(pat->fields[F_TABLE_ADDRL], index, base_l);
+	regmap_fields_write(pat->fields[F_TABLE_ADDRH], index, base_h);
+	regmap_fields_write(pat->fields[F_TABLE_ENABLE], index, 1);
+}
+
+static void ti_pat_unset_page(struct ti_pat_data *pat, size_t page_id)
+{
+	unsigned int index = ti_pat_table_index_from_page(page_id);
+
+	dev_dbg(pat->dev, "Disabling PAT index: %zu\n", page_id);
+
+	regmap_fields_write(pat->fields[F_TABLE_ENABLE], index, 0);
+}
+
+static struct sg_table *ti_pat_map_dma_buf(struct dma_buf_attachment *attachment,
+					   enum dma_data_direction direction)
+{
+	struct ti_pat_dma_buf_attachment *a = attachment->priv;
+	struct ti_pat_buffer *buffer = a->buffer;
+	struct ti_pat_data *pat = buffer->pat;
+	struct sg_table *table = a->table;
+	struct scatterlist *s;
+	unsigned int i, s_len;
+	size_t page_id;
+	int ret;
+
+	mutex_lock(&buffer->lock);
+	/* First time mapping we map to parent */
+	if (!buffer->map_count) {
+		buffer->sgt = dma_buf_map_attachment(buffer->attachment, DMA_BIDIRECTIONAL);
+		if (IS_ERR(buffer->sgt)) {
+			dev_err(pat->dev, "Unable to map parent DMA-BUF\n");
+			return buffer->sgt;
+		}
+
+		/* And program PAT area for this set of pages */
+		page_id = (buffer->offset - pat->window_base) / pat->page_size;
+		for_each_sg(buffer->sgt->sgl, s, buffer->sgt->nents, i) {
+			if (s->offset) {
+				dev_err(pat->dev, "Cannot use offset buffers\n");
+				ret = -EINVAL;
+				goto unmap;
+			}
+
+			if (s->length % pat->page_size) {
+				dev_err(pat->dev, "Cannot use buffers not a multiple of page size\n");
+				ret = -EINVAL;
+				goto unmap;
+			}
+
+			for (s_len = 0; s_len < s->length; s_len += pat->page_size)
+				ti_pat_set_page(pat, page_id++, s->dma_address + s_len);
+		}
+	}
+	buffer->map_count++;
+	mutex_unlock(&buffer->lock);
+
+	/* Map the attached device's table to get DMA addresses */
+	if (!dma_map_sg_attrs(attachment->dev, table->sgl, table->nents, direction, DMA_ATTR_SKIP_CPU_SYNC))
+		return ERR_PTR(-ENOMEM);
+
+	return table;
+
+unmap:
+	dma_buf_unmap_attachment(buffer->attachment, buffer->sgt, DMA_BIDIRECTIONAL);
+	mutex_unlock(&buffer->lock);
+	return ERR_PTR(ret);
+}
+
+static void ti_pat_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				 struct sg_table *table,
+				 enum dma_data_direction direction)
+{
+	struct ti_pat_dma_buf_attachment *a = attachment->priv;
+	struct ti_pat_buffer *buffer = a->buffer;
+	struct ti_pat_data *pat = buffer->pat;
+
+	/* Unmap the attached device's table */
+	dma_unmap_sg_attrs(attachment->dev, table->sgl, table->nents, direction, DMA_ATTR_SKIP_CPU_SYNC);
+
+	mutex_lock(&buffer->lock);
+	buffer->map_count--;
+	/* Last mapping we unmap from parent */
+	if (!buffer->map_count) {
+		/* Disable PAT pages for this area */
+		size_t page_start = (buffer->offset - pat->window_base) / pat->page_size;
+		size_t page_end = page_start + (buffer->size / pat->page_size);
+
+		for (; page_start < page_end; page_start++)
+			ti_pat_unset_page(pat, page_start);
+
+		dma_buf_unmap_attachment(buffer->attachment, buffer->sgt, DMA_BIDIRECTIONAL);
+		buffer->sgt = NULL;
+	}
+	mutex_unlock(&buffer->lock);
+}
+
+static void ti_pat_dma_buf_release(struct dma_buf *dmabuf)
+{
+	struct ti_pat_buffer *buffer = dmabuf->priv;
+
+	if (buffer->attachment && buffer->sgt)
+		dma_buf_unmap_attachment(buffer->attachment, buffer->sgt, DMA_BIDIRECTIONAL);
+	if (buffer->i_dma_buf && !IS_ERR_OR_NULL(buffer->attachment))
+		dma_buf_detach(buffer->i_dma_buf, buffer->attachment);
+	if (buffer->i_dma_buf)
+		dma_buf_put(buffer->i_dma_buf);
+
+	if (buffer->offset)
+		gen_pool_free(buffer->pat->pool, buffer->offset, buffer->size);
+
+	kfree(buffer);
+}
+
+static const struct dma_buf_ops dma_buf_ops = {
+	.attach = ti_pat_dma_buf_attach,
+	.detach = ti_pat_dma_buf_detach,
+
+	.map_dma_buf = ti_pat_map_dma_buf,
+	.unmap_dma_buf = ti_pat_unmap_dma_buf,
+
+	.release = ti_pat_dma_buf_release,
+};
+
+int ti_pat_export(struct ti_pat_data *pat,
+		  struct dma_buf *i_dma_buf,
+		  struct dma_buf **e_dma_buf)
+{
+	struct ti_pat_buffer *buffer;
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	int ret;
+
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer->pat = pat;
+	buffer->i_dma_buf = i_dma_buf;
+	buffer->size = buffer->i_dma_buf->size;
+	mutex_init(&buffer->lock);
+	INIT_LIST_HEAD(&buffer->attachments);
+	buffer->map_count = 0;
+
+	/* Reserve PAT space */
+	buffer->offset = gen_pool_alloc(buffer->pat->pool, buffer->size);
+	if (!buffer->offset) {
+		ret = -ENOMEM;
+		goto free_buffer;
+	}
+
+	exp_info.ops = &dma_buf_ops;
+	exp_info.size = buffer->size;
+	exp_info.flags = O_RDWR;
+	exp_info.priv = buffer;
+
+	*e_dma_buf = dma_buf_export(&exp_info);
+	if (IS_ERR(*e_dma_buf)) {
+		ret = PTR_ERR(*e_dma_buf);
+		goto free_pool;
+	}
+
+	return 0;
+
+free_pool:
+	gen_pool_free(buffer->pat->pool, buffer->offset, buffer->size);
+free_buffer:
+	kfree(buffer);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ti_pat_export);
+
+static long ti_pat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ti_pat_data *pat = container_of(file->private_data, struct ti_pat_data, mdev);
+
+	switch (cmd) {
+	case TI_PAT_IOC_EXPORT:
+	{
+		struct ti_pat_export_data export;
+		struct dma_buf *i_dma_buf;
+		struct dma_buf *e_dma_buf;
+		int ret;
+
+		if (_IOC_SIZE(cmd) > sizeof(export))
+			return -EINVAL;
+
+		if (copy_from_user(&export, (void __user *)arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+		i_dma_buf = dma_buf_get(export.fd);
+		if (IS_ERR(i_dma_buf))
+			return PTR_ERR(i_dma_buf);
+
+		ret = ti_pat_export(pat, i_dma_buf, &e_dma_buf);
+		if (ret) {
+			dma_buf_put(i_dma_buf);
+			return ret;
+		}
+
+		export.fd = dma_buf_fd(e_dma_buf, O_CLOEXEC);
+		if (export.fd < 0) {
+			dma_buf_put(e_dma_buf);
+			dma_buf_put(i_dma_buf);
+			return export.fd;
+		}
+
+		if (copy_to_user((void __user *)arg, &export, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static const struct file_operations ti_pat_fops = {
+	.owner          = THIS_MODULE,
+	.unlocked_ioctl = ti_pat_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= ti_pat_ioctl,
+#endif
+};
+
+static const struct of_device_id ti_pat_of_match[] = {
+	{ .compatible = "ti,j721e-pat", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ti_pat_of_match);
+
+static int ti_pat_probe(struct platform_device *pdev)
+{
+	struct ti_pat_data *pat;
+	struct resource *res;
+	void __iomem *base;
+	struct regmap *mmrs_map;
+	struct regmap *table_map;
+	unsigned int revision_major;
+	unsigned int revision_minor;
+	resource_size_t size;
+	size_t page_size;
+	int i, ret;
+
+	pat = devm_kzalloc(&pdev->dev, sizeof(*pat), GFP_KERNEL);
+	if (!pat)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, pat);
+	pat->dev = &pdev->dev;
+
+	/* Set DMA mask to 64 bits */
+	ret = dma_set_mask_and_coherent(pat->dev, DMA_BIT_MASK(64));
+	if (ret) {
+		dev_err(pat->dev, "Unable to set coherent mask to 64");
+		return ret;
+	}
+
+	/* MMRS */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmrs");
+	if (!res) {
+		dev_err(pat->dev, "Unable to find MMRS IO resource\n");
+		return -ENOENT;
+	}
+	base = devm_ioremap_resource(pat->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mmrs_map = devm_regmap_init_mmio(pat->dev, base, &ti_pat_regmap_config);
+	if (IS_ERR(mmrs_map)) {
+		dev_err(pat->dev, "Unable to allocate MMRS register map\n");
+		return PTR_ERR(mmrs_map);
+	}
+
+	for (i = 0; i < F_MMRS_FIELDS; i++) {
+		pat->fields[i] = devm_regmap_field_alloc(pat->dev, mmrs_map, ti_pat_reg_fields[i]);
+		if (IS_ERR(pat->fields[i])) {
+			dev_err(pat->dev, "Unable to allocate Regmap fields\n");
+			return PTR_ERR(pat->fields[i]);
+		}
+	}
+
+	ret = regmap_read(mmrs_map, TI_PAT_MMRS_CONFIG, &pat->page_count);
+	if (ret) {
+		dev_err(pat->dev, "Unable to read device page count\n");
+		return ret;
+	}
+
+	ret = regmap_field_read(pat->fields[F_PID_MAJOR], &revision_major);
+	if (ret) {
+		dev_err(pat->dev, "Unable to read device major revision\n");
+		return ret;
+	}
+
+	ret = regmap_field_read(pat->fields[F_PID_MINOR], &revision_minor);
+	if (ret) {
+		dev_err(pat->dev, "Unable to read device minor revision\n");
+		return ret;
+	}
+
+	dev_info(pat->dev, "Found PAT Rev %d.%d with %d pages\n", revision_major, revision_minor, pat->page_count);
+
+	/* TABLE */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "table");
+	if (!res) {
+		dev_err(pat->dev, "Unable to find TABLE IO resource\n");
+		return -ENOENT;
+	}
+	base = devm_ioremap_resource(pat->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	/* 256 pages per 4KB of table space */
+	size = resource_size(res);
+	if (size != (pat->page_count << 4))
+		dev_warn(pat->dev, "TABLE region size (%llu) does not match reported page count\n", size);
+
+	table_map = devm_regmap_init_mmio(pat->dev, base, &ti_pat_regmap_config);
+	if (IS_ERR(table_map)) {
+		dev_err(pat->dev, "Unable to allocate TABLE register map\n");
+		return PTR_ERR(table_map);
+	}
+
+	for (i = F_MMRS_FIELDS + 1; i < F_MAX_FIELDS; i++) {
+		ti_pat_reg_fields[i].id_size = ti_pat_table_index_from_page(pat->page_count);
+		ti_pat_reg_fields[i].id_offset = 8; /* 8 bytes per entry */
+		pat->fields[i] = devm_regmap_field_alloc(pat->dev, table_map, ti_pat_reg_fields[i]);
+		if (IS_ERR(pat->fields[i])) {
+			dev_err(pat->dev, "Unable to allocate Regmap fields\n");
+			return PTR_ERR(pat->fields[i]);
+		}
+	}
+
+	/* WINDOW */
+	ret = device_property_read_u64(pat->dev, "ti,pat-window-base", &pat->window_base);
+	if (ret) {
+		dev_err(pat->dev, "Unable to find ti,pat-window-base\n");
+		return -ENOENT;
+	}
+
+	ret = device_property_read_u64(pat->dev, "ti,pat-window-size", &size);
+	if (ret) {
+		dev_err(pat->dev, "Unable to find ti,pat-window-size\n");
+		return -ENOENT;
+	}
+
+	pat->page_size = PAGE_SIZE;
+	for (page_size = 0; page_size < ARRAY_SIZE(ti_pat_page_sizes); page_size++)
+		if (ti_pat_page_sizes[page_size] == pat->page_size)
+			break;
+	if (page_size == ARRAY_SIZE(ti_pat_page_sizes)) {
+		dev_err(pat->dev, "Unsupported PAGE_SIZE (%d)\n", pat->page_size);
+		return -EINVAL;
+	}
+	regmap_field_write(pat->fields[F_CONTROL_PAGE_SIZE], page_size);
+
+	/* Enable this PAT module */
+	regmap_field_write(pat->fields[F_CONTROL_EN], 1);
+
+	pat->pool = gen_pool_create(PAGE_SHIFT, -1);
+	if (!pat->pool)
+		return -ENOMEM;
+	gen_pool_add(pat->pool, pat->window_base, size, -1);
+
+	pat->mdev.minor = MISC_DYNAMIC_MINOR;
+	pat->mdev.name = pdev->name;
+	pat->mdev.fops = &ti_pat_fops;
+	pat->mdev.parent = NULL;
+	ret = misc_register(&pat->mdev);
+	if (ret) {
+		dev_err(pat->dev, "Unable to register misc device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver ti_pat_driver = {
+	.probe = ti_pat_probe,
+	.driver = {
+		.name = "ti-pat",
+		.of_match_table = ti_pat_of_match,
+	},
+};
+module_platform_driver(ti_pat_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TI PAT mapped DMA-BUF memory exporter");
+MODULE_LICENSE("GPL v2");

+ 35 - 0
include/uapi/linux/dma_buf_phys.h

@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * DMA-BUF contiguous buffer physical address user-space exporter
+ *
+ * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef DMA_BUF_PHYS_H
+#define DMA_BUF_PHYS_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct dma_buf_phys_data - metadata passed from userspace for conversion
+ * @fd:		DMA-BUF fd for conversion
+ * @phys:	populated with CPU physical address of DMA-BUF
+ */
+struct dma_buf_phys_data {
+	__u32 fd;
+	__u64 phys;
+};
+
+#define DMA_BUF_PHYS_IOC_MAGIC 'D'
+
+/**
+ * DOC: DMA_BUF_PHYS_IOC_CONVERT - Convert DMA-BUF to physical address
+ *
+ * Takes a dma_buf_phys_data struct containing a fd for a physicaly contigous
+ * buffer. Pins this buffer and populates phys field with the CPU physical address.
+ */
+#define DMA_BUF_PHYS_IOC_CONVERT _IOWR(DMA_BUF_PHYS_IOC_MAGIC, 0, struct dma_buf_phys_data)
+
+#endif /* DMA_BUF_PHYS_H */

+ 44 - 0
include/uapi/linux/ti-pat.h

@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * TI PAT mapped DMA-BUF memory exporter UAPI
+ *
+ * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef _UAPI_LINUX_TI_PAT_H
+#define _UAPI_LINUX_TI_PAT_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * DOC: TI PAT Userspace API
+ *
+ * create a client by opening /dev/ti-pat
+ * most operations handled via following ioctls
+ */
+
+/**
+ * struct ti_pat_allocation_data - metadata passed from userspace for allocations
+ * @fd:			populated with DMA-BUF FD for this allocation
+ * @flags:		flags for the allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ti_pat_export_data {
+	__u32 fd;
+	__u32 flags;
+};
+
+#define TI_PAT_IOC_MAGIC 'P'
+
+/**
+ * DOC: TI_PAT_IOC_EXPORT - Re-export DMA-BUF through TI PAT
+ *
+ * Takes an ti_pat_export_data struct and returns it with the fd field
+ * populated with the DMA-BUF handle for the new export.
+ */
+#define TI_PAT_IOC_EXPORT _IOWR(TI_PAT_IOC_MAGIC, 0, struct ti_pat_export_data)
+
+#endif /* _UAPI_LINUX_TI_PAT_H */

+ 3 - 0
ti_config_fragments/baseport.cfg

@@ -588,3 +588,6 @@ CONFIG_ION_SYSTEM_HEAP=y
 CONFIG_ION_CARVEOUT_HEAP=y
 CONFIG_ION_CHUNK_HEAP=y
 CONFIG_ION_TI=y
+
+# DMA-BUF exporter
+CONFIG_DMA_BUF_PHYS=y

+ 6 - 0
ti_config_fragments/v8_baseport.cfg

@@ -145,8 +145,14 @@ CONFIG_ION_CARVEOUT_HEAP=y
 CONFIG_ION_CHUNK_HEAP=y
 CONFIG_ION_TI=y
 
+# DMA-BUF exporter
+CONFIG_DMA_BUF_PHYS=y
+
 # PMIC
 CONFIG_MFD_PALMAS=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_PALMAS=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
+
+# TI PAT
+CONFIG_TI_PAT=y