Patchwork powerpc/44x: Support OCM(On Chip Memory) for APM821xx SoC and Bluestone board

login
register
mail settings
Submitter Vinh Nguyen Huu Tuong
Date May 7, 2012, 3:52 a.m.
Message ID <1336362768-31326-1-git-send-email-vhtnguyen@apm.com>
Download mbox | patch
Permalink /patch/157209/
State Superseded
Headers show

Comments

Vinh Nguyen Huu Tuong - May 7, 2012, 3:52 a.m.
This patch consists of:
- Add driver for OCM component
- Export OCM Information at /sys/class/ocm/ocminfo

Signed-off-by: Vinh Nguyen Huu Tuong <vhtnguyen@apm.com>
---
 arch/powerpc/boot/dts/bluestone.dts   |    8 +
 arch/powerpc/include/asm/ppc4xx_ocm.h |   47 ++++
 arch/powerpc/platforms/44x/Kconfig    |    8 +
 arch/powerpc/sysdev/Makefile          |    1 +
 arch/powerpc/sysdev/ppc4xx_ocm.c      |  420 +++++++++++++++++++++++++++++++++
 5 files changed, 484 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/include/asm/ppc4xx_ocm.h
 create mode 100644 arch/powerpc/sysdev/ppc4xx_ocm.c
Vinh Nguyen Huu Tuong - June 12, 2012, 10:26 a.m.
> -----Original Message-----
> From: Vinh Nguyen Huu Tuong [mailto:vhtnguyen@apm.com]
> Sent: Monday, May 07, 2012 10:53 AM
> To: Benjamin Herrenschmidt; Paul Mackerras; Josh Boyer; Matt Porter;
> Grant Likely; Rob Herring; Duc Dang; David S. Miller; Kumar Gala; Li
> Yang; Ashish Kalra; Anatolij Gustschin; Liu Gang; linuxppc-
> dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; devicetree-
> discuss@lists.ozlabs.org
> Cc: Vinh Nguyen Huu Tuong
> Subject: [PATCH] powerpc/44x: Support OCM(On Chip Memory) for APM821xx
> SoC and Bluestone board
>
> This patch consists of:
> - Add driver for OCM component
> - Export OCM Information at /sys/class/ocm/ocminfo
>
> Signed-off-by: Vinh Nguyen Huu Tuong <vhtnguyen@apm.com>
> ---
>  arch/powerpc/boot/dts/bluestone.dts   |    8 +
>  arch/powerpc/include/asm/ppc4xx_ocm.h |   47 ++++
>  arch/powerpc/platforms/44x/Kconfig    |    8 +
>  arch/powerpc/sysdev/Makefile          |    1 +
>  arch/powerpc/sysdev/ppc4xx_ocm.c      |  420
> +++++++++++++++++++++++++++++++++
>  5 files changed, 484 insertions(+), 0 deletions(-)  create mode 100644
> arch/powerpc/include/asm/ppc4xx_ocm.h
>  create mode 100644 arch/powerpc/sysdev/ppc4xx_ocm.c
>
> diff --git a/arch/powerpc/boot/dts/bluestone.dts
> b/arch/powerpc/boot/dts/bluestone.dts
> index 7bda373..2687c11 100644
> --- a/arch/powerpc/boot/dts/bluestone.dts
> +++ b/arch/powerpc/boot/dts/bluestone.dts
> @@ -107,6 +107,14 @@
>  		interrupt-parent = <&UIC0>;
>  	};
>
> +	OCM1: ocm@400040000 {
> +		compatible = "ibm,ocm";
> +		status = "ok";
> +		cell-index = <1>;
> +		/* configured in U-Boot */
> +		reg = <4 0x00040000 0x8000>; /* 32K */
> +	};
> +
>  	SDR0: sdr {
>  		compatible = "ibm,sdr-apm821xx";
>  		dcr-reg = <0x00e 0x002>;
> diff --git a/arch/powerpc/include/asm/ppc4xx_ocm.h
> b/arch/powerpc/include/asm/ppc4xx_ocm.h
> new file mode 100644
> index 0000000..ff7f386
> --- /dev/null
> +++ b/arch/powerpc/include/asm/ppc4xx_ocm.h
> @@ -0,0 +1,47 @@
> +/*
> + * PowerPC 4xx OCM memory allocation support
> + *
> + * (C) Copyright 2009, Applied Micro Circuits Corporation
> + * Victor Gallardo (vgallardo@amcc.com)
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#ifndef __ASM_POWERPC_PPC4xx_OCM_H__
> +#define __ASM_POWERPC_PPC4xx_OCM_H__
> +
> +#include <linux/types.h>
> +
> +#define OCM_NON_CACHED 0
> +#define OCM_CACHED     1
> +
> +#if defined(CONFIG_PPC4xx_OCM)
> +
> +void *ocm_alloc(phys_addr_t *phys, int size, int align,
> +		  int flags, const char *owner);
> +void ocm_free(const void *virt);
> +
> +#else
> +
> +#define ocm_alloc(phys, size, align, flags, owner)	NULL
> +#define ocm_free(addr)	((void)0)
> +
> +#endif /* CONFIG_PPC4xx_OCM */
> +
> +#endif  /* __ASM_POWERPC_PPC4xx_OCM_H__ */
> diff --git a/arch/powerpc/platforms/44x/Kconfig
> b/arch/powerpc/platforms/44x/Kconfig
> index 2e4e64a..6b1a64e 100644
> --- a/arch/powerpc/platforms/44x/Kconfig
> +++ b/arch/powerpc/platforms/44x/Kconfig
> @@ -250,6 +250,14 @@ config PPC4xx_GPIO
>  	help
>  	  Enable gpiolib support for ppc440 based boards
>
> +config PPC4xx_OCM
> +	bool "PPC4xx On Chip Memory (OCM) support"
> +	depends on 4xx
> +	select PPC_LIB_RHEAP
> +	help
> +	  Enable OCM support for PowerPC 4xx platforms with on chip
> memory,
> +	  OCM provides the fast place for memory access to improve
> performance.
> +
>  # 44x specific CPU modules, selected based on the board above.
>  config 440EP
>  	bool
> diff --git a/arch/powerpc/sysdev/Makefile
> b/arch/powerpc/sysdev/Makefile index 1bd7ecb..6f768e2 100644
> --- a/arch/powerpc/sysdev/Makefile
> +++ b/arch/powerpc/sysdev/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_PPC_INDIRECT_PCI)	+= indirect_pci.o
>  obj-$(CONFIG_PPC_I8259)		+= i8259.o
>  obj-$(CONFIG_IPIC)		+= ipic.o
>  obj-$(CONFIG_4xx)		+= uic.o
> +obj-$(CONFIG_PPC4xx_OCM)	+= ppc4xx_ocm.o
>  obj-$(CONFIG_4xx_SOC)		+= ppc4xx_soc.o
>  obj-$(CONFIG_XILINX_VIRTEX)	+= xilinx_intc.o
>  obj-$(CONFIG_XILINX_PCI)	+= xilinx_pci.o
> diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c
> b/arch/powerpc/sysdev/ppc4xx_ocm.c
> new file mode 100644
> index 0000000..ba3e450
> --- /dev/null
> +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c
> @@ -0,0 +1,420 @@
> +/*
> + * PowerPC 4xx OCM memory allocation support
> + *
> + * (C) Copyright 2009, Applied Micro Circuits Corporation
> + * Victor Gallardo (vgallardo@amcc.com)
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/proc_fs.h>
> +#include <linux/seq_file.h>
> +#include <linux/spinlock.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/list.h>
> +#include <asm/uaccess.h>
> +#include <asm/prom.h>
> +#include <asm/dcr.h>
> +#include <asm/rheap.h>
> +#include <asm/mmu.h>
> +#include <asm/ppc4xx_ocm.h>
> +#include <linux/export.h>
> +
> +#define OCM_DISABLED	0
> +#define OCM_ENABLED		1
> +
> +struct ocm_block {
> +	struct list_head	list;
> +	void __iomem		*addr;
> +	int					size;
> +	const char			*owner;
> +};
> +
> +/* non-cached or cached region */
> +struct ocm_region {
> +	phys_addr_t			phys;
> +	void __iomem		*virt;
> +
> +	int					memtotal;
> +	int					memfree;
> +
> +	rh_info_t			*rh;
> +	struct list_head	list;
> +};
> +
> +struct ocm_info {
> +	int			index;
> +	int			status;
> +	int			ready;
> +
> +	phys_addr_t		phys;
> +
> +	int			alignment;
> +	int			memtotal;
> +	int			cache_size;
> +
> +	struct ocm_region	nc;	/* non-cached region */
> +	struct ocm_region	c;	/* cached region */
> +};
> +
> +static struct ocm_info *ocm_nodes;
> +static int ocm_count;
> +
> +static struct ocm_info *ocm_get_node(unsigned int index) {
> +	if (index >= ocm_count) {
> +		printk(KERN_ERR "OCM: invalid index");
> +		return NULL;
> +	}
> +
> +	return &ocm_nodes[index];
> +}
> +
> +void *ocm_alloc(phys_addr_t *phys, int size, int align,
> +		  int flags, const char *owner)
> +{
> +	void __iomem *addr = NULL;
> +	unsigned long offset;
> +	struct ocm_info *ocm;
> +	struct ocm_region *ocm_reg;
> +	struct ocm_block *ocm_blk;
> +	int i;
> +
> +	for (i = 0; i < ocm_count; i++) {
> +		ocm = ocm_get_node(i);
> +
> +		if (!ocm || !ocm->ready)
> +			continue;
> +
> +		if (flags == OCM_NON_CACHED)
> +			ocm_reg = &ocm->nc;
> +		else
> +			ocm_reg = &ocm->c;
> +
> +		if (!ocm_reg->virt)
> +			continue;
> +
> +		if (align < ocm->alignment)
> +			align = ocm->alignment;
> +
> +		offset = rh_alloc_align(ocm_reg->rh, size, align, NULL);
> +
> +		if (IS_ERR_VALUE(offset))
> +			continue;
> +
> +		ocm_blk = kzalloc(sizeof(struct ocm_block *), GFP_KERNEL);
> +		if (!ocm_blk) {
> +			printk(KERN_ERR "OCM: could not allocate ocm
block");
> +			rh_free(ocm_reg->rh, offset);
> +			break;
> +		}
> +
> +		*phys = ocm_reg->phys + offset;
> +		addr = ocm_reg->virt + offset;
> +		size = ALIGN(size, align);
> +
> +		ocm_blk->addr = addr;
> +		ocm_blk->size = size;
> +		ocm_blk->owner = owner;
> +		list_add_tail(&ocm_blk->list, &ocm_reg->list);
> +
> +		ocm_reg->memfree -= size;
> +
> +		break;
> +	}
> +
> +	return addr;
> +}
> +
> +static int ocm_free_region(struct ocm_region *ocm_reg, const void
> +*addr) {
> +	struct ocm_block *blk, *tmp;
> +	unsigned long offset;
> +
> +	if (!ocm_reg->virt)
> +		return 0;
> +
> +	list_for_each_entry_safe(blk, tmp, &ocm_reg->list, list) {
> +		if (blk->addr == addr) {
> +			offset = addr - ocm_reg->virt;
> +			ocm_reg->memfree += blk->size;
> +			rh_free(ocm_reg->rh, offset);
> +			list_del(&blk->list);
> +			kfree(blk);
> +			return 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +void ocm_free(const void *addr)
> +{
> +	int i;
> +
> +	if (!addr)
> +		return;
> +
> +	for (i = 0; i < ocm_count; i++) {
> +		struct ocm_info *ocm = ocm_get_node(i);
> +
> +		if (!ocm || !ocm->ready)
> +			continue;
> +
> +		if (ocm_free_region(&ocm->nc, addr) ||
> +		    ocm_free_region(&ocm->c, addr))
> +			return;
> +	}
> +}
> +
> +static void __init ocm_init_node(int count, struct device_node *node)
> {
> +	struct ocm_info *ocm;
> +
> +	const unsigned int *cell_index;
> +	const unsigned int *cache_size;
> +	int len;
> +
> +	struct resource rsrc;
> +	int ioflags;
> +
> +	ocm = ocm_get_node(count);
> +
> +	cell_index = of_get_property(node, "cell-index", &len);
> +	if (!cell_index) {
> +		printk(KERN_ERR "OCM: missing cell-index property");
> +		return;
> +	}
> +	ocm->index = *cell_index;
> +
> +	if (of_device_is_available(node))
> +		ocm->status = OCM_ENABLED;
> +
> +	cache_size = of_get_property(node, "cached-region-size", &len);
> +	if (cache_size)
> +		ocm->cache_size = *cache_size;
> +
> +	if (of_address_to_resource(node, 0, &rsrc)) {
> +		printk(KERN_ERR "OCM%d: could not get resource address\n",
> +			ocm->index);
> +		return;
> +	}
> +
> +	ocm->phys = rsrc.start;
> +	ocm->memtotal = (rsrc.end - rsrc.start + 1);
> +
> +	printk(KERN_INFO "OCM%d: %d Bytes (%s)\n",
> +		ocm->index, ocm->memtotal,
> +		(ocm->status == OCM_DISABLED) ? "disabled" : "enabled");
> +
> +	if (ocm->status == OCM_DISABLED)
> +		return;
> +
> +	/* request region */
> +
> +	if (!request_mem_region(ocm->phys, ocm->memtotal, "ppc4xx_ocm"))
> {
> +		printk(KERN_ERR "OCM%d: could not request region\n",
> +			ocm->index);
> +		return;
> +	}
> +
> +	/* Configure non-cached and cached regions */
> +
> +	ocm->nc.phys = ocm->phys;
> +	ocm->nc.memtotal = ocm->memtotal - ocm->cache_size;
> +	ocm->nc.memfree = ocm->nc.memtotal;
> +
> +	ocm->c.phys = ocm->phys + ocm->nc.memtotal;
> +	ocm->c.memtotal = ocm->cache_size;
> +	ocm->c.memfree = ocm->c.memtotal;
> +
> +	if (ocm->nc.memtotal == 0)
> +		ocm->nc.phys = 0;
> +
> +	if (ocm->c.memtotal == 0)
> +		ocm->c.phys = 0;
> +
> +	printk(KERN_INFO "OCM%d: %d Bytes (non-cached)\n",
> +		ocm->index, ocm->nc.memtotal);
> +
> +	printk(KERN_INFO "OCM%d: %d Bytes (cached)\n",
> +		ocm->index, ocm->c.memtotal);
> +
> +	/* ioremap the non-cached region */
> +	if (ocm->nc.memtotal) {
> +		ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC;
> +		ocm->nc.virt = __ioremap(ocm->nc.phys, ocm->nc.memtotal,
> +					  ioflags);
> +
> +		if (!ocm->nc.virt) {
> +			printk(KERN_ERR
> +			       "OCM%d: failed to ioremap non-cached
> memory\n",
> +			       ocm->index);
> +			ocm->nc.memfree = 0;
> +			return;
> +		}
> +	}
> +
> +	/* ioremap the cached region */
> +
> +	if (ocm->c.memtotal) {
> +		ioflags = _PAGE_EXEC;
> +		ocm->c.virt = __ioremap(ocm->c.phys, ocm->c.memtotal,
> +					 ioflags);
> +
> +		if (!ocm->c.virt) {
> +			printk(KERN_ERR
> +			       "OCM%d: failed to ioremap cached memory\n",
> +			       ocm->index);
> +			ocm->c.memfree = 0;
> +			return;
> +		}
> +	}
> +
> +	/* Create Remote Heaps */
> +
> +	ocm->alignment = 4; /* default 4 byte alignment */
> +
> +	if (ocm->nc.virt) {
> +		ocm->nc.rh = rh_create(ocm->alignment);
> +		rh_attach_region(ocm->nc.rh, 0, ocm->nc.memtotal);
> +	}
> +
> +	if (ocm->c.virt) {
> +		ocm->c.rh = rh_create(ocm->alignment);
> +		rh_attach_region(ocm->c.rh, 0, ocm->c.memtotal);
> +	}
> +
> +	INIT_LIST_HEAD(&ocm->nc.list);
> +	INIT_LIST_HEAD(&ocm->c.list);
> +
> +	ocm->ready = 1;
> +
> +	return;
> +}
> +
> +static ssize_t ocm_sysfs_show(struct class *class,
> +			struct class_attribute *attr, char *buf) {
> +	struct ocm_block *blk, *tmp;
> +	unsigned int count, i;
> +
> +	count = 0;
> +	for (i = 0; i < ocm_count; i++) {
> +		struct ocm_info *ocm = ocm_get_node(i);
> +
> +		if (!ocm || !ocm->ready)
> +			continue;
> +
> +		count += sprintf(buf + count, "OCM          : %d\n",
> +							ocm->index);
> +		count += sprintf(buf + count, "PhysAddr     : 0x%llx\n",
> +							ocm->phys);
> +		count += sprintf(buf + count, "MemTotal     : %d Bytes\n",
> +							ocm->memtotal);
> +		count += sprintf(buf + count, "MemTotal(NC) : %d Bytes\n",
> +							ocm->nc.memtotal);
> +		count += sprintf(buf + count, "MemTotal(C)  : %d Bytes\n",
> +							ocm->c.memtotal);
> +
> +		count += sprintf(buf + count, "\n");
> +
> +		count += sprintf(buf + count, "NC.PhysAddr  : 0x%llx\n",
> +							ocm->nc.phys);
> +		count += sprintf(buf + count, "NC.VirtAddr  : 0x%p\n",
> +							ocm->nc.virt);
> +		count += sprintf(buf + count, "NC.MemTotal  : %d Bytes\n",
> +							ocm->nc.memtotal);
> +		count += sprintf(buf + count, "NC.MemFree   : %d Bytes\n",
> +							ocm->nc.memfree);
> +
> +		list_for_each_entry_safe(blk, tmp, &ocm->nc.list, list) {
> +			count += sprintf(buf + count, "NC.MemUsed   : %d
> Bytes (%s)\n",
> +							blk->size,
blk->owner);
> +		}
> +
> +		count += sprintf(buf + count, "\n");
> +
> +		count += sprintf(buf + count, "C.PhysAddr   : 0x%llx\n",
> +							ocm->c.phys);
> +		count += sprintf(buf + count, "C.VirtAddr   : 0x%p\n",
> +							ocm->c.virt);
> +		count += sprintf(buf + count, "C.MemTotal   : %d Bytes\n",
> +							ocm->c.memtotal);
> +		count += sprintf(buf + count, "C.MemFree    : %d Bytes\n",
> +							ocm->c.memfree);
> +
> +		list_for_each_entry_safe(blk, tmp, &ocm->c.list, list) {
> +			count += sprintf(buf + count, "C.MemUsed    : %d
> Bytes (%s)\n",
> +					blk->size, blk->owner);
> +		}
> +
> +		count += sprintf(buf + count, "\n");
> +	}
> +
> +	return count;
> +}
> +
> +static struct class *mem_class;
> +
> +static CLASS_ATTR(ocminfo, S_IRUGO, ocm_sysfs_show, NULL);
> +
> +static int ocm_sysfs_init(void)
> +{
> +	mem_class = class_create(THIS_MODULE, "ocm");
> +	if (IS_ERR(mem_class))
> +		return PTR_ERR(mem_class);
> +
> +	return class_create_file(mem_class, &class_attr_ocminfo); }
> +
> +static int __init ocm_init(void)
> +{
> +	struct device_node *np;
> +	int count;
> +
> +	count = 0;
> +	for_each_compatible_node(np, NULL, "ibm,ocm")
> +		count++;
> +
> +	if (!count)
> +		return 0;
> +
> +	ocm_nodes = kzalloc((count * sizeof(struct ocm_info)),
> GFP_KERNEL);
> +	if (!ocm_nodes) {
> +		printk(KERN_ERR "OCM: failed to allocate OCM nodes!\n");
> +		return -ENOMEM;
> +	}
> +
> +	ocm_count = count;
> +	count = 0;
> +
> +	for_each_compatible_node(np, NULL, "ibm,ocm") {
> +		ocm_init_node(count, np);
> +		count++;
> +	}
> +
> +	ocm_sysfs_init();
> +
> +	return 0;
> +}
> +
> +arch_initcall(ocm_init);
> --
> 1.7.2.5

Hi all reviewers,
One month pass from the day I sent out this patch, but I didn't receive
any comments from you. Could you please do me a favor to take a look on it
and give me your advices?

Best regards,
Vinh Nguyen.
Benjamin Herrenschmidt - June 13, 2012, 12:40 a.m.
On Tue, 2012-06-12 at 17:26 +0700, Vinh Huu Tuong Nguyen wrote:
> > -----Original Message-----
> > From: Vinh Nguyen Huu Tuong [mailto:vhtnguyen@apm.com]
> > Sent: Monday, May 07, 2012 10:53 AM
> > To: Benjamin Herrenschmidt; Paul Mackerras; Josh Boyer; Matt Porter;
> > Grant Likely; Rob Herring; Duc Dang; David S. Miller; Kumar Gala; Li
> > Yang; Ashish Kalra; Anatolij Gustschin; Liu Gang; linuxppc-
> > dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; devicetree-
> > discuss@lists.ozlabs.org
> > Cc: Vinh Nguyen Huu Tuong
> > Subject: [PATCH] powerpc/44x: Support OCM(On Chip Memory) for APM821xx
> > SoC and Bluestone board

Such a CC list won't get you any good will. Send it only to the relevant
mailing lists with possibly the -one- concerned maintainer on CC. I
doubt Dave Miller or Matt Porter give any damn about those patches.

As for why it wasn't reviewed yet, well, reviewers are a finite resource
with limited time dedicated to it. If the patch is properly formed and
was posted to the linuxppc-dev@ozlabs.org, it should be tracked on
patchwork.ozlabs.org and won't be lost. Double check it's there (if it's
not, then the patch was probably corrupt).

Cheers,
Ben.
Josh Boyer - June 13, 2012, 1:22 a.m.
On Tue, Jun 12, 2012 at 8:40 PM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On Tue, 2012-06-12 at 17:26 +0700, Vinh Huu Tuong Nguyen wrote:
>> > -----Original Message-----
>> > From: Vinh Nguyen Huu Tuong [mailto:vhtnguyen@apm.com]
>> > Sent: Monday, May 07, 2012 10:53 AM
>> > To: Benjamin Herrenschmidt; Paul Mackerras; Josh Boyer; Matt Porter;
>> > Grant Likely; Rob Herring; Duc Dang; David S. Miller; Kumar Gala; Li
>> > Yang; Ashish Kalra; Anatolij Gustschin; Liu Gang; linuxppc-
>> > dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; devicetree-
>> > discuss@lists.ozlabs.org
>> > Cc: Vinh Nguyen Huu Tuong
>> > Subject: [PATCH] powerpc/44x: Support OCM(On Chip Memory) for APM821xx
>> > SoC and Bluestone board
>
> Such a CC list won't get you any good will. Send it only to the relevant
> mailing lists with possibly the -one- concerned maintainer on CC. I
> doubt Dave Miller or Matt Porter give any damn about those patches.
>
> As for why it wasn't reviewed yet, well, reviewers are a finite resource
> with limited time dedicated to it. If the patch is properly formed and
> was posted to the linuxppc-dev@ozlabs.org, it should be tracked on
> patchwork.ozlabs.org and won't be lost. Double check it's there (if it's
> not, then the patch was probably corrupt).

It's there.  I just missed it until today.  My apologies Vinh.  I'll
do a more formal review tomorrow, but at first glance I'm concerned
with the sysfs usage.  The files in sysfs are supposed to be one file
per value, and you see to have something akin to /proc/meminfo in the
sysfs file being created.  Perhaps that would be better either split
into unique file names per field in sysfs, or in a debugfs file
instead?

Again, I'll review more closely tomrorow.

josh
Josh Boyer - June 14, 2012, 5:47 p.m.
On Sun, May 6, 2012 at 11:52 PM, Vinh Nguyen Huu Tuong
<vhtnguyen@apm.com> wrote:
> This patch consists of:
> - Add driver for OCM component
> - Export OCM Information at /sys/class/ocm/ocminfo

Again, apologies for the delay.  Aside from the incorrect sysfs usage
I pointed out in my other reply, I have just a few comments/questions
below.

> diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts
> index 7bda373..2687c11 100644
> --- a/arch/powerpc/boot/dts/bluestone.dts
> +++ b/arch/powerpc/boot/dts/bluestone.dts
> @@ -107,6 +107,14 @@
>                interrupt-parent = <&UIC0>;
>        };
>
> +       OCM1: ocm@400040000 {
> +               compatible = "ibm,ocm";
> +               status = "ok";
> +               cell-index = <1>;
> +               /* configured in U-Boot */
> +               reg = <4 0x00040000 0x8000>; /* 32K */
> +       };
> +
>        SDR0: sdr {
>                compatible = "ibm,sdr-apm821xx";
>                dcr-reg = <0x00e 0x002>;
> diff --git a/arch/powerpc/include/asm/ppc4xx_ocm.h b/arch/powerpc/include/asm/ppc4xx_ocm.h
> new file mode 100644
> index 0000000..ff7f386
> --- /dev/null
> +++ b/arch/powerpc/include/asm/ppc4xx_ocm.h
> @@ -0,0 +1,47 @@
> +/*
> + * PowerPC 4xx OCM memory allocation support
> + *
> + * (C) Copyright 2009, Applied Micro Circuits Corporation
> + * Victor Gallardo (vgallardo@amcc.com)
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#ifndef __ASM_POWERPC_PPC4xx_OCM_H__
> +#define __ASM_POWERPC_PPC4xx_OCM_H__
> +
> +#include <linux/types.h>
> +
> +#define OCM_NON_CACHED 0
> +#define OCM_CACHED     1
> +
> +#if defined(CONFIG_PPC4xx_OCM)
> +
> +void *ocm_alloc(phys_addr_t *phys, int size, int align,
> +                 int flags, const char *owner);
> +void ocm_free(const void *virt);
> +
> +#else
> +
> +#define ocm_alloc(phys, size, align, flags, owner)     NULL
> +#define ocm_free(addr) ((void)0)
> +
> +#endif /* CONFIG_PPC4xx_OCM */
> +
> +#endif  /* __ASM_POWERPC_PPC4xx_OCM_H__ */

I don't see any users of this header included in the patch.  I'm going
to guess that follow-on drivers/users are queued once this is in the
tree?  Also, you might want to name these 'ppc4xx_ocm_alloc' or
similar.  The concept of OCM isn't limited to ppc4xx or even SoCs, so
just using 'ocm' in the global kernel namespace might not be great.

> diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c
> new file mode 100644
> index 0000000..ba3e450
> --- /dev/null
> +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c
> @@ -0,0 +1,420 @@
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/proc_fs.h>

Why do you need proc_fs.h?

> +#include <linux/seq_file.h>
> +#include <linux/spinlock.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/list.h>
> +#include <asm/uaccess.h>
> +#include <asm/prom.h>
> +#include <asm/dcr.h>
> +#include <asm/rheap.h>
> +#include <asm/mmu.h>
> +#include <asm/ppc4xx_ocm.h>
> +#include <linux/export.h>
> +
> +#define OCM_DISABLED   0
> +#define OCM_ENABLED            1
> +
> +struct ocm_block {
> +       struct list_head        list;
> +       void __iomem            *addr;
> +       int                                     size;
> +       const char                      *owner;
> +};
> +
> +/* non-cached or cached region */
> +struct ocm_region {
> +       phys_addr_t                     phys;
> +       void __iomem            *virt;
> +
> +       int                                     memtotal;
> +       int                                     memfree;
> +
> +       rh_info_t                       *rh;
> +       struct list_head        list;
> +};

There's some interesting whitespace usage in these struct definitions.

josh
Vinh Nguyen Huu Tuong - June 15, 2012, 3:19 a.m.
> -----Original Message-----
> From: Josh Boyer [mailto:jwboyer@gmail.com]
> Sent: Friday, June 15, 2012 12:47 AM
> To: Vinh Nguyen Huu Tuong
> Cc: Benjamin Herrenschmidt; Paul Mackerras; Matt Porter; Grant Likely;
> Rob Herring; Duc Dang; David S. Miller; Kumar Gala; Li Yang; Ashish
> Kalra; Anatolij Gustschin; Liu Gang; linuxppc-dev@lists.ozlabs.org;
> linux-kernel@vger.kernel.org; devicetree-discuss@lists.ozlabs.org
> Subject: Re: [PATCH] powerpc/44x: Support OCM(On Chip Memory) for
> APM821xx SoC and Bluestone board
>
> On Sun, May 6, 2012 at 11:52 PM, Vinh Nguyen Huu Tuong
> <vhtnguyen@apm.com> wrote:
> > This patch consists of:
> > - Add driver for OCM component
> > - Export OCM Information at /sys/class/ocm/ocminfo
>
> Again, apologies for the delay.  Aside from the incorrect sysfs usage I
> pointed out in my other reply, I have just a few comments/questions
> below.
[Vinh Nguyen] You're welcome. About the files on sysfs, the first place of
ocm is in procfs, but procfs is deprecated and replaced by sysfs, then I
decided to move it to sysfs. With your comments, I think I can move it to
debugfs.
>
> > diff --git a/arch/powerpc/boot/dts/bluestone.dts
> > b/arch/powerpc/boot/dts/bluestone.dts
> > index 7bda373..2687c11 100644
> > --- a/arch/powerpc/boot/dts/bluestone.dts
> > +++ b/arch/powerpc/boot/dts/bluestone.dts
> > @@ -107,6 +107,14 @@
> >                interrupt-parent = <&UIC0>;
> >        };
> >
> > +       OCM1: ocm@400040000 {
> > +               compatible = "ibm,ocm";
> > +               status = "ok";
> > +               cell-index = <1>;
> > +               /* configured in U-Boot */
> > +               reg = <4 0x00040000 0x8000>; /* 32K */
> > +       };
> > +
> >        SDR0: sdr {
> >                compatible = "ibm,sdr-apm821xx";
> >                dcr-reg = <0x00e 0x002>; diff --git
> > a/arch/powerpc/include/asm/ppc4xx_ocm.h
> > b/arch/powerpc/include/asm/ppc4xx_ocm.h
> > new file mode 100644
> > index 0000000..ff7f386
> > --- /dev/null
> > +++ b/arch/powerpc/include/asm/ppc4xx_ocm.h
> > @@ -0,0 +1,47 @@
> > +/*
> > + * PowerPC 4xx OCM memory allocation support
> > + *
> > + * (C) Copyright 2009, Applied Micro Circuits Corporation
> > + * Victor Gallardo (vgallardo@amcc.com)
> > + *
> > + * See file CREDITS for list of people who contributed to this
> > + * project.
> > + *
> > + * 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.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> > + * MA 02111-1307 USA
> > + */
> > +
> > +#ifndef __ASM_POWERPC_PPC4xx_OCM_H__
> > +#define __ASM_POWERPC_PPC4xx_OCM_H__
> > +
> > +#include <linux/types.h>
> > +
> > +#define OCM_NON_CACHED 0
> > +#define OCM_CACHED     1
> > +
> > +#if defined(CONFIG_PPC4xx_OCM)
> > +
> > +void *ocm_alloc(phys_addr_t *phys, int size, int align,
> > +                 int flags, const char *owner); void ocm_free(const
> > +void *virt);
> > +
> > +#else
> > +
> > +#define ocm_alloc(phys, size, align, flags, owner)     NULL #define
> > +ocm_free(addr) ((void)0)
> > +
> > +#endif /* CONFIG_PPC4xx_OCM */
> > +
> > +#endif  /* __ASM_POWERPC_PPC4xx_OCM_H__ */
>
> I don't see any users of this header included in the patch.  I'm going
> to guess that follow-on drivers/users are queued once this is in the
> tree?  Also, you might want to name these 'ppc4xx_ocm_alloc' or
> similar.  The concept of OCM isn't limited to ppc4xx or even SoCs, so
> just using 'ocm' in the global kernel namespace might not be great.
>
[Vinh Nguyen] With our plan, the next submit of IBM NEW EMAC will use it
for speedup. About the naming convention, I will change as your comments.

> > diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c
> > b/arch/powerpc/sysdev/ppc4xx_ocm.c
> > new file mode 100644
> > index 0000000..ba3e450
> > --- /dev/null
> > +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c
> > @@ -0,0 +1,420 @@
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/proc_fs.h>
>
> Why do you need proc_fs.h?
[Vinh Nguyen] I will remove it.
>
> > +#include <linux/seq_file.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/list.h>
> > +#include <asm/uaccess.h>
> > +#include <asm/prom.h>
> > +#include <asm/dcr.h>
> > +#include <asm/rheap.h>
> > +#include <asm/mmu.h>
> > +#include <asm/ppc4xx_ocm.h>
> > +#include <linux/export.h>
> > +
> > +#define OCM_DISABLED   0
> > +#define OCM_ENABLED            1
> > +
> > +struct ocm_block {
> > +       struct list_head        list;
> > +       void __iomem            *addr;
> > +       int                                     size;
> > +       const char                      *owner; };
> > +
> > +/* non-cached or cached region */
> > +struct ocm_region {
> > +       phys_addr_t                     phys;
> > +       void __iomem            *virt;
> > +
> > +       int                                     memtotal;
> > +       int                                     memfree;
> > +
> > +       rh_info_t                       *rh;
> > +       struct list_head        list;
> > +};
>
> There's some interesting whitespace usage in these struct definitions.
[Vinh Nguyen] I'll check again and remove them. I used the scripts check
in kernel but it can't remove them all.
>
> josh

I'll update the patch soon but I need send out to internal review first
before sending out to community. It takes one or two week, please be
patient for the next review.

Best regards,
Vinh Nguyen

Patch

diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts
index 7bda373..2687c11 100644
--- a/arch/powerpc/boot/dts/bluestone.dts
+++ b/arch/powerpc/boot/dts/bluestone.dts
@@ -107,6 +107,14 @@ 
 		interrupt-parent = <&UIC0>;
 	};
 
+	OCM1: ocm@400040000 {
+		compatible = "ibm,ocm";
+		status = "ok";
+		cell-index = <1>;
+		/* configured in U-Boot */
+		reg = <4 0x00040000 0x8000>; /* 32K */
+	};
+
 	SDR0: sdr {
 		compatible = "ibm,sdr-apm821xx";
 		dcr-reg = <0x00e 0x002>;
diff --git a/arch/powerpc/include/asm/ppc4xx_ocm.h b/arch/powerpc/include/asm/ppc4xx_ocm.h
new file mode 100644
index 0000000..ff7f386
--- /dev/null
+++ b/arch/powerpc/include/asm/ppc4xx_ocm.h
@@ -0,0 +1,47 @@ 
+/*
+ * PowerPC 4xx OCM memory allocation support
+ *
+ * (C) Copyright 2009, Applied Micro Circuits Corporation
+ * Victor Gallardo (vgallardo@amcc.com)
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __ASM_POWERPC_PPC4xx_OCM_H__
+#define __ASM_POWERPC_PPC4xx_OCM_H__
+
+#include <linux/types.h>
+
+#define OCM_NON_CACHED 0
+#define OCM_CACHED     1
+
+#if defined(CONFIG_PPC4xx_OCM)
+
+void *ocm_alloc(phys_addr_t *phys, int size, int align,
+		  int flags, const char *owner);
+void ocm_free(const void *virt);
+
+#else
+
+#define ocm_alloc(phys, size, align, flags, owner)	NULL
+#define ocm_free(addr)	((void)0)
+
+#endif /* CONFIG_PPC4xx_OCM */
+
+#endif  /* __ASM_POWERPC_PPC4xx_OCM_H__ */
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index 2e4e64a..6b1a64e 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -250,6 +250,14 @@  config PPC4xx_GPIO
 	help
 	  Enable gpiolib support for ppc440 based boards
 
+config PPC4xx_OCM
+	bool "PPC4xx On Chip Memory (OCM) support"
+	depends on 4xx
+	select PPC_LIB_RHEAP
+	help
+	  Enable OCM support for PowerPC 4xx platforms with on chip memory,
+	  OCM provides the fast place for memory access to improve performance.
+
 # 44x specific CPU modules, selected based on the board above.
 config 440EP
 	bool
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 1bd7ecb..6f768e2 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -37,6 +37,7 @@  obj-$(CONFIG_PPC_INDIRECT_PCI)	+= indirect_pci.o
 obj-$(CONFIG_PPC_I8259)		+= i8259.o
 obj-$(CONFIG_IPIC)		+= ipic.o
 obj-$(CONFIG_4xx)		+= uic.o
+obj-$(CONFIG_PPC4xx_OCM)	+= ppc4xx_ocm.o
 obj-$(CONFIG_4xx_SOC)		+= ppc4xx_soc.o
 obj-$(CONFIG_XILINX_VIRTEX)	+= xilinx_intc.o
 obj-$(CONFIG_XILINX_PCI)	+= xilinx_pci.o
diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c
new file mode 100644
index 0000000..ba3e450
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_ocm.c
@@ -0,0 +1,420 @@ 
+/*
+ * PowerPC 4xx OCM memory allocation support
+ *
+ * (C) Copyright 2009, Applied Micro Circuits Corporation
+ * Victor Gallardo (vgallardo@amcc.com)
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <asm/prom.h>
+#include <asm/dcr.h>
+#include <asm/rheap.h>
+#include <asm/mmu.h>
+#include <asm/ppc4xx_ocm.h>
+#include <linux/export.h>
+
+#define OCM_DISABLED	0
+#define OCM_ENABLED		1
+
+struct ocm_block {
+	struct list_head	list;
+	void __iomem		*addr;
+	int					size;
+	const char			*owner;
+};
+
+/* non-cached or cached region */
+struct ocm_region {
+	phys_addr_t			phys;
+	void __iomem		*virt;
+
+	int					memtotal;
+	int					memfree;
+
+	rh_info_t			*rh;
+	struct list_head	list;
+};
+
+struct ocm_info {
+	int			index;
+	int			status;
+	int			ready;
+
+	phys_addr_t		phys;
+
+	int			alignment;
+	int			memtotal;
+	int			cache_size;
+
+	struct ocm_region	nc;	/* non-cached region */
+	struct ocm_region	c;	/* cached region */
+};
+
+static struct ocm_info *ocm_nodes;
+static int ocm_count;
+
+static struct ocm_info *ocm_get_node(unsigned int index)
+{
+	if (index >= ocm_count) {
+		printk(KERN_ERR "OCM: invalid index");
+		return NULL;
+	}
+
+	return &ocm_nodes[index];
+}
+
+void *ocm_alloc(phys_addr_t *phys, int size, int align,
+		  int flags, const char *owner)
+{
+	void __iomem *addr = NULL;
+	unsigned long offset;
+	struct ocm_info *ocm;
+	struct ocm_region *ocm_reg;
+	struct ocm_block *ocm_blk;
+	int i;
+
+	for (i = 0; i < ocm_count; i++) {
+		ocm = ocm_get_node(i);
+
+		if (!ocm || !ocm->ready)
+			continue;
+
+		if (flags == OCM_NON_CACHED)
+			ocm_reg = &ocm->nc;
+		else
+			ocm_reg = &ocm->c;
+
+		if (!ocm_reg->virt)
+			continue;
+
+		if (align < ocm->alignment)
+			align = ocm->alignment;
+
+		offset = rh_alloc_align(ocm_reg->rh, size, align, NULL);
+
+		if (IS_ERR_VALUE(offset))
+			continue;
+
+		ocm_blk = kzalloc(sizeof(struct ocm_block *), GFP_KERNEL);
+		if (!ocm_blk) {
+			printk(KERN_ERR "OCM: could not allocate ocm block");
+			rh_free(ocm_reg->rh, offset);
+			break;
+		}
+
+		*phys = ocm_reg->phys + offset;
+		addr = ocm_reg->virt + offset;
+		size = ALIGN(size, align);
+
+		ocm_blk->addr = addr;
+		ocm_blk->size = size;
+		ocm_blk->owner = owner;
+		list_add_tail(&ocm_blk->list, &ocm_reg->list);
+
+		ocm_reg->memfree -= size;
+
+		break;
+	}
+
+	return addr;
+}
+
+static int ocm_free_region(struct ocm_region *ocm_reg, const void *addr)
+{
+	struct ocm_block *blk, *tmp;
+	unsigned long offset;
+
+	if (!ocm_reg->virt)
+		return 0;
+
+	list_for_each_entry_safe(blk, tmp, &ocm_reg->list, list) {
+		if (blk->addr == addr) {
+			offset = addr - ocm_reg->virt;
+			ocm_reg->memfree += blk->size;
+			rh_free(ocm_reg->rh, offset);
+			list_del(&blk->list);
+			kfree(blk);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+void ocm_free(const void *addr)
+{
+	int i;
+
+	if (!addr)
+		return;
+
+	for (i = 0; i < ocm_count; i++) {
+		struct ocm_info *ocm = ocm_get_node(i);
+
+		if (!ocm || !ocm->ready)
+			continue;
+
+		if (ocm_free_region(&ocm->nc, addr) ||
+		    ocm_free_region(&ocm->c, addr))
+			return;
+	}
+}
+
+static void __init ocm_init_node(int count, struct device_node *node)
+{
+	struct ocm_info *ocm;
+
+	const unsigned int *cell_index;
+	const unsigned int *cache_size;
+	int len;
+
+	struct resource rsrc;
+	int ioflags;
+
+	ocm = ocm_get_node(count);
+
+	cell_index = of_get_property(node, "cell-index", &len);
+	if (!cell_index) {
+		printk(KERN_ERR "OCM: missing cell-index property");
+		return;
+	}
+	ocm->index = *cell_index;
+
+	if (of_device_is_available(node))
+		ocm->status = OCM_ENABLED;
+
+	cache_size = of_get_property(node, "cached-region-size", &len);
+	if (cache_size)
+		ocm->cache_size = *cache_size;
+
+	if (of_address_to_resource(node, 0, &rsrc)) {
+		printk(KERN_ERR "OCM%d: could not get resource address\n",
+			ocm->index);
+		return;
+	}
+
+	ocm->phys = rsrc.start;
+	ocm->memtotal = (rsrc.end - rsrc.start + 1);
+
+	printk(KERN_INFO "OCM%d: %d Bytes (%s)\n",
+		ocm->index, ocm->memtotal,
+		(ocm->status == OCM_DISABLED) ? "disabled" : "enabled");
+
+	if (ocm->status == OCM_DISABLED)
+		return;
+
+	/* request region */
+
+	if (!request_mem_region(ocm->phys, ocm->memtotal, "ppc4xx_ocm")) {
+		printk(KERN_ERR "OCM%d: could not request region\n",
+			ocm->index);
+		return;
+	}
+
+	/* Configure non-cached and cached regions */
+
+	ocm->nc.phys = ocm->phys;
+	ocm->nc.memtotal = ocm->memtotal - ocm->cache_size;
+	ocm->nc.memfree = ocm->nc.memtotal;
+
+	ocm->c.phys = ocm->phys + ocm->nc.memtotal;
+	ocm->c.memtotal = ocm->cache_size;
+	ocm->c.memfree = ocm->c.memtotal;
+
+	if (ocm->nc.memtotal == 0)
+		ocm->nc.phys = 0;
+
+	if (ocm->c.memtotal == 0)
+		ocm->c.phys = 0;
+
+	printk(KERN_INFO "OCM%d: %d Bytes (non-cached)\n",
+		ocm->index, ocm->nc.memtotal);
+
+	printk(KERN_INFO "OCM%d: %d Bytes (cached)\n",
+		ocm->index, ocm->c.memtotal);
+
+	/* ioremap the non-cached region */
+	if (ocm->nc.memtotal) {
+		ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC;
+		ocm->nc.virt = __ioremap(ocm->nc.phys, ocm->nc.memtotal,
+					  ioflags);
+
+		if (!ocm->nc.virt) {
+			printk(KERN_ERR
+			       "OCM%d: failed to ioremap non-cached memory\n",
+			       ocm->index);
+			ocm->nc.memfree = 0;
+			return;
+		}
+	}
+
+	/* ioremap the cached region */
+
+	if (ocm->c.memtotal) {
+		ioflags = _PAGE_EXEC;
+		ocm->c.virt = __ioremap(ocm->c.phys, ocm->c.memtotal,
+					 ioflags);
+
+		if (!ocm->c.virt) {
+			printk(KERN_ERR
+			       "OCM%d: failed to ioremap cached memory\n",
+			       ocm->index);
+			ocm->c.memfree = 0;
+			return;
+		}
+	}
+
+	/* Create Remote Heaps */
+
+	ocm->alignment = 4; /* default 4 byte alignment */
+
+	if (ocm->nc.virt) {
+		ocm->nc.rh = rh_create(ocm->alignment);
+		rh_attach_region(ocm->nc.rh, 0, ocm->nc.memtotal);
+	}
+
+	if (ocm->c.virt) {
+		ocm->c.rh = rh_create(ocm->alignment);
+		rh_attach_region(ocm->c.rh, 0, ocm->c.memtotal);
+	}
+
+	INIT_LIST_HEAD(&ocm->nc.list);
+	INIT_LIST_HEAD(&ocm->c.list);
+
+	ocm->ready = 1;
+
+	return;
+}
+
+static ssize_t ocm_sysfs_show(struct class *class,
+			struct class_attribute *attr, char *buf)
+{
+	struct ocm_block *blk, *tmp;
+	unsigned int count, i;
+
+	count = 0;
+	for (i = 0; i < ocm_count; i++) {
+		struct ocm_info *ocm = ocm_get_node(i);
+
+		if (!ocm || !ocm->ready)
+			continue;
+
+		count += sprintf(buf + count, "OCM          : %d\n",
+							ocm->index);
+		count += sprintf(buf + count, "PhysAddr     : 0x%llx\n",
+							ocm->phys);
+		count += sprintf(buf + count, "MemTotal     : %d Bytes\n",
+							ocm->memtotal);
+		count += sprintf(buf + count, "MemTotal(NC) : %d Bytes\n",
+							ocm->nc.memtotal);
+		count += sprintf(buf + count, "MemTotal(C)  : %d Bytes\n",
+							ocm->c.memtotal);
+
+		count += sprintf(buf + count, "\n");
+
+		count += sprintf(buf + count, "NC.PhysAddr  : 0x%llx\n",
+							ocm->nc.phys);
+		count += sprintf(buf + count, "NC.VirtAddr  : 0x%p\n",
+							ocm->nc.virt);
+		count += sprintf(buf + count, "NC.MemTotal  : %d Bytes\n",
+							ocm->nc.memtotal);
+		count += sprintf(buf + count, "NC.MemFree   : %d Bytes\n",
+							ocm->nc.memfree);
+
+		list_for_each_entry_safe(blk, tmp, &ocm->nc.list, list) {
+			count += sprintf(buf + count, "NC.MemUsed   : %d Bytes (%s)\n",
+							blk->size, blk->owner);
+		}
+
+		count += sprintf(buf + count, "\n");
+
+		count += sprintf(buf + count, "C.PhysAddr   : 0x%llx\n",
+							ocm->c.phys);
+		count += sprintf(buf + count, "C.VirtAddr   : 0x%p\n",
+							ocm->c.virt);
+		count += sprintf(buf + count, "C.MemTotal   : %d Bytes\n",
+							ocm->c.memtotal);
+		count += sprintf(buf + count, "C.MemFree    : %d Bytes\n",
+							ocm->c.memfree);
+
+		list_for_each_entry_safe(blk, tmp, &ocm->c.list, list) {
+			count += sprintf(buf + count, "C.MemUsed    : %d Bytes (%s)\n",
+					blk->size, blk->owner);
+		}
+
+		count += sprintf(buf + count, "\n");
+	}
+
+	return count;
+}
+
+static struct class *mem_class;
+
+static CLASS_ATTR(ocminfo, S_IRUGO, ocm_sysfs_show, NULL);
+
+static int ocm_sysfs_init(void)
+{
+	mem_class = class_create(THIS_MODULE, "ocm");
+	if (IS_ERR(mem_class))
+		return PTR_ERR(mem_class);
+
+	return class_create_file(mem_class, &class_attr_ocminfo);
+}
+
+static int __init ocm_init(void)
+{
+	struct device_node *np;
+	int count;
+
+	count = 0;
+	for_each_compatible_node(np, NULL, "ibm,ocm")
+		count++;
+
+	if (!count)
+		return 0;
+
+	ocm_nodes = kzalloc((count * sizeof(struct ocm_info)), GFP_KERNEL);
+	if (!ocm_nodes) {
+		printk(KERN_ERR "OCM: failed to allocate OCM nodes!\n");
+		return -ENOMEM;
+	}
+
+	ocm_count = count;
+	count = 0;
+
+	for_each_compatible_node(np, NULL, "ibm,ocm") {
+		ocm_init_node(count, np);
+		count++;
+	}
+
+	ocm_sysfs_init();
+
+	return 0;
+}
+
+arch_initcall(ocm_init);