From patchwork Mon May 7 03:52:48 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vinh Nguyen Huu Tuong X-Patchwork-Id: 157209 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [IPv6:::1]) by ozlabs.org (Postfix) with ESMTP id C9122B7130 for ; Mon, 7 May 2012 14:00:31 +1000 (EST) X-Greylist: delayed 372 seconds by postgrey-1.34 at bilbo; Mon, 07 May 2012 13:59:40 EST Received: from exprod5og105.obsmtp.com (exprod5og105.obsmtp.com [64.18.0.180]) by ozlabs.org (Postfix) with SMTP id 53CFAB6FB7 for ; Mon, 7 May 2012 13:59:39 +1000 (EST) Received: from mail-pb0-f49.google.com ([209.85.160.49]) (using TLSv1) by exprod5ob105.postini.com ([64.18.4.12]) with SMTP ID DSNKT6dIqvrwB2ammlg0iDeyTQ6uTaq2cT8t@postini.com; Sun, 06 May 2012 20:59:40 PDT Received: by pbbrq13 with SMTP id rq13so6992524pbb.22 for ; Sun, 06 May 2012 20:59:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:x-gm-message-state; bh=71gztHJoJK/aAfN3p6DdTibJyGwSLeYCSEGuYXv7RPk=; b=oNkyaQkPFFACb0CBl3++2yPm4GlY57bLRce/UTLBZGx+5sUGToZ8JajYpaQMP2z9oS 0AZQgM37aHIUgHgRY+2h2UZ+2Rcx8RK0Fv4tfGSNMWriY4qgiWATMgjRjdH9M3NrCEkd LQ7SBNjasOI6E+3cSr0Q2KnOXqZUbDT0hyEZig5bADKjc1Vp9MX+EAX12q7EDZQcIB6z 7tFAiiyW3SU03j5o+xuYIBj2xBChu3vmb+k6yth3pChbfuNG70+ov6ZjqvP8ZYnNOeDC KpAkhdY3p1sZmoKFuefLBK+ZTcWyqhy4+ENk860a/rp1mIcvcoiZhCL+24GjrMot77Ia JhBQ== Received: by 10.68.225.170 with SMTP id rl10mr1675443pbc.76.1336362805063; Sun, 06 May 2012 20:53:25 -0700 (PDT) Received: from localhost.localdomain ([118.69.219.194]) by mx.google.com with ESMTPS id og6sm16876233pbb.42.2012.05.06.20.53.20 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 06 May 2012 20:53:24 -0700 (PDT) From: Vinh Nguyen Huu Tuong 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 Subject: [PATCH] powerpc/44x: Support OCM(On Chip Memory) for APM821xx SoC and Bluestone board Date: Mon, 7 May 2012 10:52:48 +0700 Message-Id: <1336362768-31326-1-git-send-email-vhtnguyen@apm.com> X-Mailer: git-send-email 1.7.2.5 X-Gm-Message-State: ALoCoQmh3ZRwN+JTymQBeEAaO0ub/iWzOJJBM8tzhlckHF6JX2piE75rSw142aSdl7sUEUBSXY9W Cc: Vinh Nguyen Huu Tuong X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org This patch consists of: - Add driver for OCM component - Export OCM Information at /sys/class/ocm/ocminfo Signed-off-by: Vinh Nguyen Huu Tuong --- 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 + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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);