This is a simple DDR mapping driver to allow the SPUs to target the DDR attached to the Axon with their DMA. Signed-Off-by: Jean-Christophe DUBOIS -- Index: linux-2.6.21/drivers/axon/usr/ddr/axon_usr_ddr.c =================================================================== --- /dev/null +++ linux-2.6.21/drivers/axon/usr/ddr/axon_usr_ddr.c @@ -0,0 +1,334 @@ +/****************************************************************** + * Copyright (C) 2006 Mercury Computer Systems, Inc. + * 199 Riverneck Road + * Chelmsford, MA 01824-2820 + * (978) 256-1300 + * webinfo@mc.com + * + * 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. + * http://www.gnu.org/copyleft/gpl.html + ******************************************************************/ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +struct axon_usr_ddr_t { + + struct cdev cdev; + + + dev_t cdev_num; + + + u64 plb_addr; + + + u64 processor_offset; + + + u64 size; +}; + +MODULE_DESCRIPTION("user space DDR mapping driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Jean-Christophe Dubois (jdubois@mc.com)"); + +#include + +static int axon_usr_ddr_release(struct inode *inode, struct file *file) +{ + int ret = 0; + + dbg_log("begin\n"); + + file->private_data = NULL; + + module_put(THIS_MODULE); + + return ret; +} + + +static int axon_usr_ddr_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + dbg_log("begin\n"); + + if (try_module_get(THIS_MODULE)) { + + file->private_data = + container_of(inode->i_cdev, struct axon_usr_ddr_t, cdev); + } else { + dbg_err("failed in try_module_get()\n"); + ret = -ENODEV; + } + + return ret; +} + + +static int axon_usr_ddr_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long offset = (vma->vm_pgoff << PAGE_SHIFT); + + struct axon_usr_ddr_t *p_axon_ddr = + (struct axon_usr_ddr_t *)(file->private_data); + + int prot = pgprot_val(vma->vm_page_prot); + + dbg_log(" for 0x%08lx\n", offset); + + + if ((offset + vma->vm_end - vma->vm_start) > p_axon_ddr->size) { + dbg_err("mapping request is out of range\n"); + return -EINVAL; + } + + + offset += + (p_axon_ddr->plb_addr & AXON_PLB_MASK_FROM_BE) + + p_axon_ddr->processor_offset; + + dbg_log("maping offset 0x%016lx for %ld bytes\n", offset, + vma->vm_end - vma->vm_start); + + prot |= _PAGE_GUARDED; + + prot |= _PAGE_NO_CACHE; + + vma->vm_page_prot = __pgprot(prot); + + + + vma->vm_flags |= VM_IO; + + vma->vm_flags |= VM_RESERVED; + + if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + dbg_err("remap_page_range FAILED\n"); + return -EAGAIN; + } + + return 0; +} + +static struct file_operations fops = { + .open = axon_usr_ddr_open, + .release = axon_usr_ddr_release, + .mmap = axon_usr_ddr_mmap, +}; + +static __init struct device_node +*axon_usr_ddr_of_find_parent_by_name(struct device_node *from, char *name) +{ + struct device_node *dn; + + of_node_get(from); + + + while ((dn = of_get_parent(from))) { + char *node_name = (char *)get_property(dn, "name", NULL); + + of_node_put(from); + from = dn; + + + if ((node_name) && (strcmp(node_name, name) == 0)) { + of_node_put(dn); + return dn; + } + } + + of_node_put(from); + + return NULL; +} + +static unsigned num_ddr; +static dev_t ddr_dev; + + +static int axon_usr_ddr_remove(struct of_device *of_dev) +{ + struct axon_usr_ddr_t *p_cur_ddr; + + dbg_log("begin\n"); + + + p_cur_ddr = of_dev->dev.driver_data; + + class_device_destroy(axon_get_class(), p_cur_ddr->cdev_num); + + + cdev_del(&p_cur_ddr->cdev); + + + kfree(p_cur_ddr); +} + + +static __devinit int axon_usr_ddr_probe(struct of_device *of_dev, + const struct of_device_id *match) +{ + int ret = 0; + static int i_board; + struct axon_usr_ddr_t *p_axon_ddr; + struct device_node *axon_node; + unsigned long *axon_reg; + unsigned long *reg; + + dbg_log("begin\n"); + + reg = (unsigned long *)get_property(of_dev->node, "reg", NULL); + + if (reg == NULL) { + dbg_inf("Can't get 'reg' property for DDR node\n"); + return -ENODEV; + } + + dbg_inf("DDR memory at 0x%lx, size = 0x%lx\n", reg[0], reg[1]); + + if (reg[1] == 0) { + dbg_inf("DDR memory at 0x%lx is empty?\n", reg[0]); + return -ENODEV; + } + + + + + + + axon_node = axon_usr_ddr_of_find_parent_by_name(of_dev->node, "axon"); + + if (axon_node == NULL) { + dbg_inf("Can't find Axon parent node\n"); + return -ENODEV; + } + + + axon_reg = (unsigned long *)get_property(axon_node, "reg", NULL); + + if (axon_reg == NULL) { + dbg_inf("Can't get 'reg' property for Axon node\n"); + return -ENODEV; + } + + + if ((axon_reg[0] == 0) || axon_reg[1] == 0) { + dbg_inf("Bad reg values for the Axon node\n"); + return -ENODEV; + } + + + p_axon_ddr = kzalloc(sizeof(struct axon_usr_ddr_t), GFP_KERNEL); + + if (p_axon_ddr == NULL) { + dbg_err("failed to allocate memory\n"); + return -ENOMEM; + } + + + p_axon_ddr->plb_addr = reg[0]; + + p_axon_ddr->size = reg[1]; + + if (p_axon_ddr->size == 0x1000000000) { + + dbg_inf("Bogus 64GB DDR size, setting it to 2GB\n"); + p_axon_ddr->size = 0x80000000; + } + + p_axon_ddr->processor_offset = axon_reg[0]; + + p_axon_ddr->cdev_num = MKDEV(MAJOR(ddr_dev), i_board); + + cdev_init(&p_axon_ddr->cdev, &fops); + + ret = cdev_add(&p_axon_ddr->cdev, p_axon_ddr->cdev_num, 1); + + class_device_create(axon_get_class(), NULL, + p_axon_ddr->cdev_num, NULL, "ddr%d", i_board); + + of_dev->dev.driver_data = p_axon_ddr; + + i_board++; + + return ret; +} + +static struct of_device_id axon_usr_ddr_of_ids[] = { + { + .name = "mc", + .type = "dma-memory", + }, + {} +}; + +static struct of_platform_driver axon_usr_ddr_driver = { + .name = "axon_ddr_map", + .match_table = axon_usr_ddr_of_ids, + .probe = axon_usr_ddr_probe, + .driver = { + .multithread_probe = 0, + }, + .remove = axon_usr_ddr_remove, +}; + +static int __init axon_usr_ddr_module_init(void) +{ + dbg_inf("Loading Axon DDR mapping driver\n"); + + + alloc_chrdev_region(&ddr_dev, 0, 4, "ddr"); + + return of_register_platform_driver(&axon_usr_ddr_driver); +} + +static void __exit axon_usr_ddr_module_cleanup(void) +{ + dbg_inf("Unloading Axon DDR mapping driver\n"); + + unregister_chrdev_region(ddr_dev, 4); + + return of_unregister_platform_driver(&axon_usr_ddr_driver); +} + +module_init(axon_usr_ddr_module_init); +module_exit(axon_usr_ddr_module_cleanup); Index: linux-2.6.21/drivers/axon/usr/ddr/axon_usr_ddr.c~ =================================================================== --- /dev/null +++ linux-2.6.21/drivers/axon/usr/ddr/axon_usr_ddr.c~ @@ -0,0 +1,408 @@ +/****************************************************************** + * Copyright (C) 2006 Mercury Computer Systems, Inc. + * 199 Riverneck Road + * Chelmsford, MA 01824-2820 + * (978) 256-1300 + * webinfo@mc.com + * + * 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. + * http://www.gnu.org/copyleft/gpl.html + ******************************************************************/ +/*************************************************************************** + * + * This driver implement the mapping driver for the DDR memory + * + * This driver is not supported over PCI-E as it would require to lock + * some PIM for an application. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +/** + * This is a per DDR controller structure. It holds reference to various + * board specific objects + */ +struct axon_usr_ddr_t { + /** + * Char device providing driver entry points + */ + struct cdev cdev; + + /** + * Associated Major/minor + */ + dev_t cdev_num; + + /** + * PLB address for the DDR memory controller + */ + u64 plb_addr; + + /** + * Processor offset for the Axon + */ + u64 processor_offset; + + /** + * size of the DDR memory + */ + u64 size; +}; + +MODULE_DESCRIPTION("user space DDR mapping driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Jean-Christophe Dubois (jdubois@mc.com)"); + +// #define AXON_DEBUG +#include + +static int axon_usr_ddr_release(struct inode *inode, struct file *file) +{ + int ret = 0; + + dbg_log("begin\n"); + + file->private_data = NULL; + + module_put(THIS_MODULE); + + return ret; +} + +/** + * open entry + */ +static int axon_usr_ddr_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + dbg_log("begin\n"); + + if (try_module_get(THIS_MODULE)) { + /* + * We retrieve the handle to the parent driver of the instance + */ + file->private_data = + container_of(inode->i_cdev, struct axon_usr_ddr_t, cdev); + } else { + dbg_err("failed in try_module_get()\n"); + ret = -ENODEV; + } + + return ret; +} + +/** + * mmap entry + */ +static int axon_usr_ddr_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long offset = (vma->vm_pgoff << PAGE_SHIFT); + + struct axon_usr_ddr_t *p_axon_ddr = + (struct axon_usr_ddr_t *)(file->private_data); + + int prot = pgprot_val(vma->vm_page_prot); + + dbg_log(" for 0x%08lx\n", offset); + + /* + * Check if this is not out of range + */ + if ((offset + vma->vm_end - vma->vm_start) > p_axon_ddr->size) { + dbg_err("mapping request is out of range\n"); + return -EINVAL; + } + + /* + * We need to compute the processor physical address + */ + offset += + (p_axon_ddr->plb_addr & AXON_PLB_MASK_FROM_BE) + + p_axon_ddr->processor_offset; + + dbg_log("maping offset 0x%016lx for %ld bytes\n", offset, + vma->vm_end - vma->vm_start); + + prot |= _PAGE_GUARDED; + + prot |= _PAGE_NO_CACHE; + + vma->vm_page_prot = __pgprot(prot); + + /* + * Don't dump addresses that are not real memory to a core file. + * Mark all memory as surreal memory + */ + /* + * Mark memory as fake + */ + vma->vm_flags |= VM_IO; + /* + * prevents swap out + */ + vma->vm_flags |= VM_RESERVED; + + if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + dbg_err("remap_page_range FAILED\n"); + return -EAGAIN; + } + + return 0; +} + +static struct file_operations fops = { + .open = axon_usr_ddr_open, + .release = axon_usr_ddr_release, + .mmap = axon_usr_ddr_mmap, +}; + +static __init struct device_node +*axon_usr_ddr_of_find_parent_by_name(struct device_node *from, char *name) +{ + struct device_node *dn; + + of_node_get(from); + + /* + * go through parent nodes + */ + while ((dn = of_get_parent(from))) { + char *node_name = (char *)get_property(dn, "name", NULL); + + of_node_put(from); + from = dn; + + /* + * until we find the requested node name + */ + if ((node_name) && (strcmp(node_name, name) == 0)) { + of_node_put(dn); + return dn; + } + } + + of_node_put(from); + + return NULL; +} + +static unsigned num_ddr; +static dev_t ddr_dev; + +/** + * module clean up + */ +static int axon_usr_ddr_remove(struct of_device *of_dev) +{ + struct axon_usr_ddr_t *p_cur_ddr; + + dbg_log("begin\n"); + + /* + * go through the list of DDR controllers + */ + p_cur_ddr = of_dev->dev.driver_data; + + class_device_destroy(axon_get_class(), p_cur_ddr->cdev_num); + + /* + * remove the device + */ + cdev_del(&p_cur_ddr->cdev); + + /* + * free the board structure + */ + kfree(p_cur_ddr); +} + +/** + * module init + */ +static __devinit int axon_usr_ddr_probe(struct of_device *of_dev, const struct of_device_id *match) +{ + int ret = 0; + static int i_board; + struct axon_usr_ddr_t *p_axon_ddr; + struct device_node *axon_node; + unsigned long *axon_reg; + unsigned long *reg; + + dbg_log("begin\n"); + + reg = (unsigned long *)get_property(of_dev->node, "reg", NULL); + + if (reg == NULL) { + dbg_inf("Can't get 'reg' property for DDR node\n"); + return -ENODEV; + } + + dbg_inf("DDR memory at 0x%lx, size = 0x%lx\n", reg[0], reg[1]); + + if (reg[1] == 0) { + dbg_inf("DDR memory at 0x%lx is empty?\n", reg[0]); + return -ENODEV; + } + + /* + * OK we got a DDR controller and it has a size + */ + /* + * its PLB address is in reg[0], its size in reg[1] + */ + + /* + * we now need to find its Cell physical address + */ + /* + * So find the parent Axon + */ + axon_node = axon_usr_ddr_of_find_parent_by_name(of_dev->node, "axon"); + + if (axon_node == NULL) { + dbg_inf("Can't find Axon parent node\n"); + return -ENODEV; + } + + /* + * Get its reg + */ + axon_reg = (unsigned long *)get_property(axon_node, "reg", NULL); + + if (axon_reg == NULL) { + dbg_inf("Can't get 'reg' property for Axon node\n"); + return -ENODEV; + } + + /* + * neither the address nor the size should be 0 + */ + if ((axon_reg[0] == 0) || axon_reg[1] == 0) { + dbg_inf("Bad reg values for the Axon node\n"); + return -ENODEV; + } + + /* + * Now we need to allocate a struct for the DDR controller + */ + p_axon_ddr = kzalloc(sizeof(struct axon_usr_ddr_t), GFP_KERNEL); + + if (p_axon_ddr == NULL) { + dbg_err("failed to allocate memory\n"); + return -ENOMEM; + } + + /* + * fill the structure + */ + p_axon_ddr->plb_addr = reg[0]; + + p_axon_ddr->size = reg[1]; + + if (p_axon_ddr->size == 0x1000000000) { + /* + * Bogus value. SLOF is reporting max size instead + * of actual size. + * We hardcode it to 2GB (for CAB and Getaway + */ + dbg_inf("Bogus 64GB DDR size, setting it to 2GB\n"); + p_axon_ddr->size = 0x80000000; + } + + p_axon_ddr->processor_offset = axon_reg[0]; + + p_axon_ddr->cdev_num = MKDEV(MAJOR(ddr_dev), i_board); + + cdev_init(&p_axon_ddr->cdev, &fops); + + ret = cdev_add(&p_axon_ddr->cdev, p_axon_ddr->cdev_num, 1); + + class_device_create(axon_get_class(), NULL, + p_axon_ddr->cdev_num, NULL, "ddr%d", + i_board); + + of_dev->dev.driver_data = p_axon_ddr; + + i_board++; + + return ret; +} + +static struct of_device_id axon_usr_ddr_of_ids[] = { + { + .name = "mc", + .type = "dma-memory", + }, + {} +}; + +static struct of_platform_driver axon_usr_ddr_driver = { + .name = "axon_ddr_map", + .match_table = axon_usr_ddr_of_ids, + .probe = axon_usr_ddr_probe, + .driver = { + .multithread_probe = 0, /* TODO : Multi ? */ + }, + .remove = axon_usr_ddr_remove, +}; + +static int __init axon_usr_ddr_module_init(void) +{ + dbg_inf("Loading Axon DDR mapping driver\n"); + + /* + * Lets allocate a range of char device major/minor + * 4 memory controller on the bladeII + */ + alloc_chrdev_region(&ddr_dev, 0, 4, "ddr"); + + return of_register_platform_driver(&axon_usr_ddr_driver); +} + +static void __exit axon_usr_ddr_module_cleanup(void) +{ + dbg_inf("Unloading Axon DDR mapping driver\n"); + + unregister_chrdev_region(ddr_dev, 4); + + return of_unregister_platform_driver(&axon_usr_ddr_driver); +} + +module_init(axon_usr_ddr_module_init); +module_exit(axon_usr_ddr_module_cleanup); --