diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig
index 629039e7a8..1e452ad6d9 100644
--- a/drivers/cache/Kconfig
+++ b/drivers/cache/Kconfig
@@ -31,4 +31,12 @@ config V5L2_CACHE
 	  It will configure tag and data ram timing control from the
 	  device tree and enable L2 cache.
 
+config NCORE_CACHE
+	bool "Arteris Ncore cache coherent unit driver"
+	select CACHE
+	help
+	  This driver is for the Arteris Ncore cache coherent unit (CCU)
+	  controller. The driver initializes cache directories and coherent
+	  agent interfaces.
+
 endmenu
diff --git a/drivers/cache/Makefile b/drivers/cache/Makefile
index c1f766cfca..6be895a5f7 100644
--- a/drivers/cache/Makefile
+++ b/drivers/cache/Makefile
@@ -2,4 +2,5 @@
 obj-$(CONFIG_$(SPL_)CACHE) += cache-uclass.o
 obj-$(CONFIG_SANDBOX) += sandbox_cache.o
 obj-$(CONFIG_L2X0_CACHE) += cache-l2x0.o
+obj-$(CONFIG_NCORE_CACHE) += cache-ncore.o
 obj-$(CONFIG_V5L2_CACHE) += cache-v5l2.o
diff --git a/drivers/cache/cache-ncore.c b/drivers/cache/cache-ncore.c
new file mode 100644
index 0000000000..e3aca36071
--- /dev/null
+++ b/drivers/cache/cache-ncore.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Intel Corporation <www.intel.com>
+ *
+ */
+#include <dm.h>
+#include <wait_bit.h>
+
+#include <asm/io.h>
+
+/* Directory */
+#define DIRUSFER		0x80010
+#define DIRUCASER0		0x80040
+#define DIRUSFMCR		0x80080
+#define DIRUSFMAR		0x80084
+
+#define DIRUSFMCR_SFID_SHIFT	16
+
+/* Coherent cache agent interface */
+#define CAIUIDR			0x00ffc
+
+#define CAIUIDR_CA_GET(v)	(((v) & 0x00008000) >> 15)
+#define CAIUIDR_TYPE_GET(v)	(((v) & 0x000f0000) >> 16)
+#define CAIUIDR_TYPE_ACE_CAI_DVM_SUPPORT	0
+#define CAIUIDR_TYPE_ACELITE_CAI_DVM_SUPPORT	1
+
+/* Coherent subsystem */
+#define CSADSER0		0xff040
+#define CSUIDR			0xffff8
+#define CSIDR			0xffffc
+
+#define CSUIDR_NUMCAIUS_GET(v)	(((v) & 0x0000007f) >> 0)
+#define CSUIDR_NUMDIRUS_GET(v)	(((v) & 0x003f0000) >> 16)
+#define CSUIDR_NUMCMIUS_GET(v)	(((v) & 0x3f000000) >> 24)
+
+#define CSIDR_NUMSFS_GET(v)	(((v) & 0x007c0000) >> 18)
+
+#define DIR_REG_SZ		0x1000
+#define CAIU_REG_SZ		0x1000
+
+#define CCU_DIR_REG_ADDR(base, reg, dir)	\
+		((base) + (reg) + ((dir) * DIR_REG_SZ))
+
+/* OCRAM firewall register */
+#define OCRAM_FW_01			0x100204
+#define OCRAM_SECURE_REGIONS		4
+
+#define OCRAM_PRIVILEGED_MASK		BIT(29)
+#define OCRAM_SECURE_MASK		BIT(30)
+
+static void ncore_ccu_init_dirs(void __iomem *base)
+{
+	ulong i, f;
+	int ret;
+	u32 num_of_dirs;
+	u32 num_of_snoop_filters;
+	u32 reg;
+
+	num_of_dirs = CSUIDR_NUMDIRUS_GET(readl(base + CSUIDR));
+	num_of_snoop_filters =
+		CSIDR_NUMSFS_GET(readl(base + CSIDR)) + 1;
+
+	/* Initialize each snoop filter in each directory */
+	for (f = 0; f < num_of_snoop_filters; f++) {
+		reg = f << DIRUSFMCR_SFID_SHIFT;
+		for (i = 0; i < num_of_dirs; i++) {
+			/* Initialize all entries */
+			writel(reg, CCU_DIR_REG_ADDR(base, DIRUSFMCR, i));
+
+			/* Poll snoop filter maintenance operation active
+			 * bit become 0.
+			 */
+			ret = wait_for_bit_le32((const void *)
+						CCU_DIR_REG_ADDR(base,
+								 DIRUSFMAR, i),
+						BIT(0), false, 1000, false);
+			if (ret) {
+				puts("CCU: Directory initialization failed!\n");
+				hang();
+			}
+
+			/* Enable snoop filter, a bit per snoop filter */
+			setbits_le32((ulong)CCU_DIR_REG_ADDR(base, DIRUSFER, i),
+				     BIT(f));
+		}
+	}
+}
+
+static void ncore_ccu_init_coh_agent(void __iomem *base)
+{
+	u32 num_of_coh_agent_intf;
+	u32 num_of_dirs;
+	u32 reg;
+	u32 type;
+	u32 i, dir;
+
+	num_of_coh_agent_intf =
+		CSUIDR_NUMCAIUS_GET(readl(base + CSUIDR));
+	num_of_dirs = CSUIDR_NUMDIRUS_GET(readl(base + CSUIDR));
+
+	for (i = 0; i < num_of_coh_agent_intf; i++) {
+		reg = readl(base + CAIUIDR + (i * CAIU_REG_SZ));
+		if (CAIUIDR_CA_GET(reg)) {
+			/* Caching agent bit is enabled, enable caching agent
+			 * snoop in each directory
+			 */
+			for (dir = 0; dir < num_of_dirs; dir++) {
+				setbits_le32((ulong)
+					     CCU_DIR_REG_ADDR(base, DIRUCASER0,
+							      dir),
+					     BIT(i));
+			}
+		}
+
+		type = CAIUIDR_TYPE_GET(reg);
+		if (type == CAIUIDR_TYPE_ACE_CAI_DVM_SUPPORT ||
+		    type == CAIUIDR_TYPE_ACELITE_CAI_DVM_SUPPORT) {
+			/* DVM support is enabled, enable ACE DVM snoop*/
+			setbits_le32((ulong)(base + CSADSER0),
+				     BIT(i));
+		}
+	}
+}
+
+static void ocram_bypass_firewall(void __iomem *base)
+{
+	int i;
+
+	for (i = 0; i < OCRAM_SECURE_REGIONS; i++) {
+		clrbits_le32(base + OCRAM_FW_01 + (i * sizeof(u32)),
+			     OCRAM_PRIVILEGED_MASK | OCRAM_SECURE_MASK);
+	}
+}
+
+static int ncore_ccu_probe(struct udevice *dev)
+{
+	void __iomem *base;
+	fdt_addr_t addr;
+
+	addr = dev_read_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	base = (void __iomem *)addr;
+
+	ncore_ccu_init_dirs(base);
+	ncore_ccu_init_coh_agent(base);
+	ocram_bypass_firewall(base);
+
+	return 0;
+}
+
+static const struct udevice_id ncore_ccu_ids[] = {
+	{ .compatible = "arteris,ncore-ccu" },
+	{}
+};
+
+U_BOOT_DRIVER(ncore_ccu) = {
+	.name   = "ncore_ccu",
+	.id     = UCLASS_CACHE,
+	.of_match = ncore_ccu_ids,
+	.probe	= ncore_ccu_probe,
+	.flags  = DM_FLAG_PRE_RELOC,
+};
