]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
zynq: Add OCM driver
authorMichal Simek <michal.simek@xilinx.com>
Mon, 25 Nov 2013 19:05:17 +0000 (20:05 +0100)
committerMichal Simek <michal.simek@xilinx.com>
Wed, 27 Nov 2013 12:08:12 +0000 (13:08 +0100)
The driver provide memory allocator which can
be used by others drivers to allocate memory inside OCM.
All location for 64kB blocks are supported
and driver is trying to allocate the largest continuous
block of memory.

Checking mpcore addressing filterring is not done here
but could be added in future.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
14 files changed:
Documentation/devicetree/bindings/arm/zynq/xlnx,zynq-ocm.txt [new file with mode: 0644]
arch/arm/boot/dts/zynq-afx-nand.dts
arch/arm/boot/dts/zynq-afx-nor.dts
arch/arm/boot/dts/zynq-cc108.dts
arch/arm/boot/dts/zynq-zc702.dts
arch/arm/boot/dts/zynq-zc706.dts
arch/arm/boot/dts/zynq-zc770-xm010.dts
arch/arm/boot/dts/zynq-zc770-xm011.dts
arch/arm/boot/dts/zynq-zc770-xm012.dts
arch/arm/boot/dts/zynq-zc770-xm013.dts
arch/arm/boot/dts/zynq-zed.dts
arch/arm/mach-zynq/Makefile
arch/arm/mach-zynq/pm.c
arch/arm/mach-zynq/zynq_ocm.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/arm/zynq/xlnx,zynq-ocm.txt b/Documentation/devicetree/bindings/arm/zynq/xlnx,zynq-ocm.txt
new file mode 100644 (file)
index 0000000..22f6bf9
--- /dev/null
@@ -0,0 +1,17 @@
+Device tree bindings for Zynq's OCM
+
+The OCM is divided to 4 64kB segments which can be separately configured
+to low or high location. Location is controlled via SLCR.
+
+Required properties:
+ compatible: Compatibility string. Must be "xlnx,zynq-ocm-1.0".
+ reg: Specify the base and size of the OCM registers in the memory map.
+      E.g.: reg = <0xf800c000 0x1000>;
+
+Example:
+ps7_ram_0: ps7-ram@f800c000 {
+       compatible = "xlnx,zynq-ocm-1.0";
+       reg = <0xf800c000 0x1000>;
+       interrupt-parent = <&ps7_scugic_0>;
+       interrupts = <0 3 4>;
+} ;
index fb478968ace2df6794aeca9b1681259fb5d639c0..45688f5fa407ce372a7d593bfdfb8c80153a601e 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        interrupts = <0 2 4>;
                        reg = <0xf8f02000 0x1000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index 47da945410a00cedb0f5f98996c599aba7b3c54b..3f2c4d4a2c5e07e09863e3019830388527ea8ca7 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        interrupts = <0 2 4>;
                        reg = <0xf8f02000 0x1000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index 012e0f3c6726c03890d18d6c7063333be487b24b..3cc44abbb1ddb8794935c2a4a1b4fac5b22c26fa 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        compatible = "xlnx,ps7-qspi-linear-1.00.a";
                        reg = <0xfc000000 0x1000000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index d6840b3a15fcb530e90a6b12530a6bb267653a41..a9e30fbf9cd183e8dea8ce8503f038a7564a2e30 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        compatible = "xlnx,ps7-qspi-linear-1.00.a";
                        reg = <0xfc000000 0x1000000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index 7a6d7e80d77692fa2ac8a3ceb81a8c417016cbe2..fb5bcccd1e815f3bfd2782cb4fd7fd1f49299a8c 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        compatible = "xlnx,ps7-qspi-linear-1.00.a";
                        reg = <0xfc000000 0x2000000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index 7db3291a2d5327fca1d74d0af8510f9bcb041584..7e32335c38269873963c5fcd6624377152a25b2f 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        compatible = "xlnx,ps7-qspi-linear-1.00.a";
                        reg = <0xfc000000 0x1000000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index 28367c34aec03dd399bea247748c5f0f115aa55c..f9e603b86d913ba22477a3a9ad1a4ca1d57a74eb 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        interrupts = <0 2 4>;
                        reg = <0xf8f02000 0x1000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index fa2afc422d43d6ceeb1fda30814be825c6574539..c52847122b727f9790d2958bd30549bc5f97fed8 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        interrupts = <0 2 4>;
                        reg = <0xf8f02000 0x1000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index c7129bfdd3f7e0f335c47ad75e1896c6d8dbacc1..33bdc5d364ac4c6df136cc4d37b158a04ee7bb74 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        compatible = "xlnx,ps7-qspi-linear-1.00.a";
                        reg = <0xfc000000 0x2000000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index ecacadbba68e659f67b5d410db0f521169c569e4..5d0de0ce742e06a2a445890b432c27852dbfd90f 100644 (file)
                        compatible = "xlnx,ps7-iop-bus-config-1.00.a";
                        reg = <0xe0200000 0x1000>;
                } ;
+               ps7_ocmc_0: ps7-ocmc@f800c000 {
+                       compatible = "xlnx,ps7-ocmc-1.00.a", "xlnx,zynq-ocm-1.0";
+                       interrupt-parent = <&ps7_scugic_0>;
+                       interrupts = <0 3 4>;
+                       reg = <0xf800c000 0x1000>;
+               } ;
                ps7_pl310_0: ps7-pl310@f8f02000 {
                        arm,data-latency = <3 2 2>;
                        arm,tag-latency = <2 2 2>;
                        compatible = "xlnx,ps7-qspi-linear-1.00.a";
                        reg = <0xfc000000 0x1000000>;
                } ;
-               ps7_ram_0: ps7-ram@0 {
-                       compatible = "xlnx,ps7-ram-1.00.a", "xlnx,ps7-ocm";
-                       interrupt-parent = <&ps7_scugic_0>;
-                       interrupts = <0 3 4>;
-                       reg = <0xfffc0000 0x40000>;
-               } ;
                ps7_scugic_0: ps7-scugic@f8f01000 {
                        #address-cells = <2>;
                        #interrupt-cells = <3>;
index ed72b71e45582053fe13d5394101878e232baed5..322309bb3955a5bfe17737649fd72d549cee962b 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 # Common support
-obj-y                          := common.o slcr.o
+obj-y                          := common.o slcr.o zynq_ocm.o
 
 obj-$(CONFIG_PCI_MSI)           += xaxipcie-msi.o
 CFLAGS_REMOVE_hotplug.o                =-march=armv6k
index 01301df6914f2ac679328d4f2c43816ac325f9fc..a3a864fcfa0b5939d7ca92ff673cd9dadd060692 100644 (file)
 #include <linux/clk.h>
 #include <linux/clk/zynq.h>
 #include <linux/err.h>
+#include <linux/genalloc.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/slab.h>
 #include <linux/suspend.h>
 #include <asm/cacheflush.h>
@@ -60,14 +62,8 @@ static int zynq_pm_suspend(unsigned long arg)
 {
        u32 reg;
        int (*zynq_suspend_ptr)(void __iomem *, void __iomem *);
-       void *ocm_swap_area;
        int do_ddrpll_bypass = 1;
 
-       /* Allocate some space for temporary OCM storage */
-       ocm_swap_area = kmalloc(zynq_sys_suspend_sz, GFP_ATOMIC);
-       if (!ocm_swap_area)
-               do_ddrpll_bypass = 0;
-
        /* Enable DDR self-refresh and clock stop */
        if (ddrc_base) {
                reg = readl(ddrc_base + DDRC_CTRL_REG1_OFFS);
@@ -99,12 +95,7 @@ static int zynq_pm_suspend(unsigned long arg)
                      : /* no inputs */
                      : "r12");
 
-
-       if (ocm_swap_area && ocm_base) {
-               /* Backup a small area of OCM used for the suspend code */
-               memcpy(ocm_swap_area, (__force void *)ocm_base,
-                       zynq_sys_suspend_sz);
-
+       if (ocm_base) {
                /*
                 * Copy code to suspend system into OCM. The suspend code
                 * needs to run from OCM as DRAM may no longer be available
@@ -135,13 +126,6 @@ static int zynq_pm_suspend(unsigned long arg)
                cpu_do_idle();
        }
 
-       /* Restore original OCM contents */
-       if (do_ddrpll_bypass) {
-               memcpy((__force void *)ocm_base, ocm_swap_area,
-                       zynq_sys_suspend_sz);
-               kfree(ocm_swap_area);
-       }
-
        /* Topswitch clock stop enable */
        zynq_clk_topswitch_enable();
 
@@ -230,18 +214,45 @@ static void __iomem *zynq_pm_ioremap(const char *comp)
 static void __iomem *zynq_pm_remap_ocm(void)
 {
        struct device_node *np;
-       struct resource res;
-       const char *comp = "xlnx,ps7-ocm";
+       const char *comp = "xlnx,zynq-ocm-1.0";
        void __iomem *base = NULL;
 
        np = of_find_compatible_node(NULL, NULL, comp);
        if (np) {
-               if (of_address_to_resource(np, 0, &res))
-                       return NULL;
-               WARN_ON(!request_mem_region(res.start, resource_size(&res),
-                                       "OCM"));
-               base = __arm_ioremap(res.start, resource_size(&res), MT_MEMORY);
+               struct device *dev;
+               unsigned long pool_addr;
+               unsigned long pool_addr_virt;
+               struct gen_pool *pool;
+
                of_node_put(np);
+
+               dev = &(of_find_device_by_node(np)->dev);
+
+               /* Get OCM pool from device tree or platform data */
+               pool = dev_get_gen_pool(dev);
+               if (!pool) {
+                       pr_warn("%s: OCM pool is not available\n", __func__);
+                       return NULL;
+               }
+
+               pool_addr_virt = gen_pool_alloc(pool, zynq_sys_suspend_sz);
+               if (!pool_addr_virt) {
+                       pr_warn("%s: Can't get OCM poll\n", __func__);
+                       return NULL;
+               }
+               pool_addr = gen_pool_virt_to_phys(pool, pool_addr_virt);
+               if (!pool_addr) {
+                       pr_warn("%s: Can't get physical address of OCM pool\n",
+                               __func__);
+                       return NULL;
+               }
+               base = __arm_ioremap(pool_addr, zynq_sys_suspend_sz, MT_MEMORY);
+               if (!base) {
+                       pr_warn("%s: IOremap OCM pool failed\n", __func__);
+                       return NULL;
+               }
+               pr_debug("%s: Remap OCM %s from %lx to %lx\n", __func__, comp,
+                        pool_addr_virt, (unsigned long)base);
        } else {
                pr_warn("%s: no compatible node found for '%s'\n", __func__,
                                comp);
diff --git a/arch/arm/mach-zynq/zynq_ocm.c b/arch/arm/mach-zynq/zynq_ocm.c
new file mode 100644 (file)
index 0000000..034a65b
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2013 Xilinx
+ *
+ * Based on "Generic on-chip SRAM allocation driver"
+ *
+ * Copyright (C) 2012 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/genalloc.h>
+
+#include "common.h"
+
+#define ZYNQ_OCM_HIGHADDR      0xfffc0000
+#define ZYNQ_OCM_LOWADDR       0x0
+#define ZYNQ_OCM_BLOCK_SIZE    0x10000
+#define ZYNQ_OCM_BLOCKS                4
+#define ZYNQ_OCM_GRANULARITY   32
+
+#define ZYNQ_OCM_PARITY_CTRL   0x0
+#define ZYNQ_OCM_PARITY_ENABLE 0x1e
+
+#define ZYNQ_OCM_PARITY_ERRADDRESS     0x4
+
+#define ZYNQ_OCM_IRQ_STS               0x8
+#define ZYNQ_OCM_IRQ_STS_ERR_MASK      0x7
+
+struct zynq_ocm_dev {
+       void __iomem *base;
+       int irq;
+       struct gen_pool *pool;
+       struct resource res[ZYNQ_OCM_BLOCKS];
+};
+
+/**
+ * zynq_ocm_irq_handler - Interrupt service routine of the OCM controller
+ * @irq:        IRQ number
+ * @data:     Pointer to the zynq_ocm_dev structure
+ *
+ * returns:     IRQ_HANDLED always
+ */
+static irqreturn_t zynq_ocm_irq_handler(int irq, void *data)
+{
+       u32 sts;
+       u32 err_addr;
+       struct zynq_ocm_dev *zynq_ocm = data;
+
+       /* check status */
+       sts = readl(zynq_ocm->base + ZYNQ_OCM_IRQ_STS);
+       if (sts & ZYNQ_OCM_IRQ_STS_ERR_MASK) {
+               /* check error address */
+               err_addr = readl(zynq_ocm->base + ZYNQ_OCM_PARITY_ERRADDRESS);
+               pr_err("%s: OCM err intr generated at 0x%04x (stat: 0x%08x).",
+                      __func__, err_addr, sts & ZYNQ_OCM_IRQ_STS_ERR_MASK);
+       }
+       pr_warn("%s: Interrupt generated by OCM, but no error is found.",
+               __func__);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * zynq_ocm_probe - Probe method for the OCM driver
+ * @pdev:       Pointer to the platform_device structure
+ *
+ * This function initializes the driver data structures and the hardware.
+ *
+ * returns:     0 on success and error value on failure
+ */
+static int zynq_ocm_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct zynq_ocm_dev *zynq_ocm;
+       u32 i, ocm_config, curr;
+       struct resource *res;
+
+       ocm_config = zynq_slcr_get_ocm_config();
+
+       zynq_ocm = devm_kzalloc(&pdev->dev, sizeof(*zynq_ocm), GFP_KERNEL);
+       if (!zynq_ocm)
+               return -ENOMEM;
+
+       zynq_ocm->pool = devm_gen_pool_create(&pdev->dev,
+                                             ilog2(ZYNQ_OCM_GRANULARITY), -1);
+       if (!zynq_ocm->pool)
+               return -ENOMEM;
+
+       curr = 0; /* For storing current struct resource for OCM */
+       for (i = 0; i < ZYNQ_OCM_BLOCKS; i++) {
+               u32 base, start, end;
+
+               /* Setup base address for 64kB OCM block */
+               if (ocm_config & BIT(i))
+                       base = ZYNQ_OCM_HIGHADDR;
+               else
+                       base = ZYNQ_OCM_LOWADDR;
+
+               /* Calculate start and end block addresses */
+               start = i * ZYNQ_OCM_BLOCK_SIZE + base;
+               end = start + (ZYNQ_OCM_BLOCK_SIZE - 1);
+
+               /* Concatenate OCM blocks together to get bigger pool */
+               if (i > 0 && start == (zynq_ocm->res[curr - 1].end + 1)) {
+                       zynq_ocm->res[curr - 1].end = end;
+               } else {
+#ifdef CONFIG_SMP
+                       /*
+                        * OCM block if placed at 0x0 has special meaning
+                        * for SMP because jump trampoline is added there.
+                        * Ensure that this address won't be allocated.
+                        */
+                       if (!base) {
+                               u32 trampoline_code_size =
+                                       &zynq_secondary_trampoline_end -
+                                       &zynq_secondary_trampoline;
+                               dev_dbg(&pdev->dev,
+                                       "Allocate reset vector table %dB\n",
+                                       trampoline_code_size);
+                               /* postpone start offset */
+                               start += trampoline_code_size;
+                       }
+#endif
+                       /* First resource is always initialized */
+                       zynq_ocm->res[curr].start = start;
+                       zynq_ocm->res[curr].end = end;
+                       zynq_ocm->res[curr].flags = IORESOURCE_MEM;
+                       curr++; /* Increment curr value */
+               }
+               dev_dbg(&pdev->dev, "OCM block %d, start %x, end %x\n",
+                       i, start, end);
+       }
+
+       /*
+        * Separate pool allocation from OCM block detection to ensure
+        * the biggest possible pool.
+        */
+       for (i = 0; i < ZYNQ_OCM_BLOCKS; i++) {
+               unsigned long size;
+               void __iomem *virt_base;
+
+               /* Skip all zero size resources */
+               if (zynq_ocm->res[i].end == 0)
+                       break;
+               dev_dbg(&pdev->dev, "OCM resources %d, start %x, end %x\n",
+                       i, zynq_ocm->res[i].start, zynq_ocm->res[i].end);
+               size = resource_size(&zynq_ocm->res[i]);
+               virt_base = devm_ioremap_resource(&pdev->dev,
+                                                 &zynq_ocm->res[i]);
+               if (IS_ERR(virt_base))
+                       return PTR_ERR(virt_base);
+
+               ret = gen_pool_add_virt(zynq_ocm->pool,
+                                       (unsigned long)virt_base,
+                                       zynq_ocm->res[i].start, size, -1);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Gen pool failed\n");
+                       return ret;
+               }
+               dev_info(&pdev->dev, "ZYNQ OCM pool: %ld KiB @ 0x%p\n",
+                        size / 1024, virt_base);
+       }
+
+       /* Get OCM config space */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       zynq_ocm->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(zynq_ocm->base))
+               return PTR_ERR(zynq_ocm->base);
+
+       /* Allocate OCM parity IRQ */
+       zynq_ocm->irq = platform_get_irq(pdev, 0);
+       if (zynq_ocm->irq < 0) {
+               dev_err(&pdev->dev, "irq resource not found\n");
+               return zynq_ocm->irq;
+       }
+       ret = devm_request_irq(&pdev->dev, zynq_ocm->irq, zynq_ocm_irq_handler,
+                              0, pdev->name, zynq_ocm);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "request_irq failed\n");
+               return ret;
+       }
+
+       /* Enable parity errors */
+       writel(ZYNQ_OCM_PARITY_ENABLE, zynq_ocm->base + ZYNQ_OCM_PARITY_CTRL);
+
+       platform_set_drvdata(pdev, zynq_ocm);
+
+       return 0;
+}
+
+/**
+ * zynq_ocm_remove - Remove method for the OCM driver
+ * @pdev:       Pointer to the platform_device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees all resources allocated to
+ * the device.
+ *
+ * returns:     0 on success and error value on failure
+ */
+static int zynq_ocm_remove(struct platform_device *pdev)
+{
+       struct zynq_ocm_dev *zynq_ocm = platform_get_drvdata(pdev);
+
+       if (gen_pool_avail(zynq_ocm->pool) < gen_pool_size(zynq_ocm->pool))
+               dev_dbg(&pdev->dev, "removed while SRAM allocated\n");
+
+       return 0;
+}
+
+static struct of_device_id zynq_ocm_dt_ids[] = {
+       { .compatible = "xlnx,zynq-ocm-1.0" },
+       { /* end of table */ }
+};
+
+static struct platform_driver zynq_ocm_driver = {
+       .driver = {
+               .name = "zynq_ocm",
+               .of_match_table = zynq_ocm_dt_ids,
+       },
+       .probe = zynq_ocm_probe,
+       .remove = zynq_ocm_remove,
+};
+
+static int __init zynq_ocm_init(void)
+{
+       return platform_driver_register(&zynq_ocm_driver);
+}
+
+arch_initcall(zynq_ocm_init);