Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 1807

kernel-2.6.18-128.1.10.el5.src.rpm

From: Scott Moser <smoser@redhat.com>
Date: Tue, 20 Nov 2007 17:34:38 -0500
Subject: [ppc64] cell: ddr2 memory driver for axon
Message-id: 11955980802359-do-send-email-smoser@redhat.com
O-Subject: [PATCH RHEL5u2] bz253212 Cell/B.E. I/O Enhancements [1/3]
Bugzilla: 253212

commit dbdf04c40161f81d74e27f04e201acb3a5dfad69

[CELL] driver for DDR2 memory on AXON

From: Christian Krafft <krafft@linux.ibm.com>
This patch puts the axon driver onto rhel-5-2 kernel.
It includes all fixes that made it into 2.6.23-rc7.

The Axon bridge chip used on new Cell/B.E. based blade servers
comes with a DDR2 memory controller that can be used to
attach cheap memory modules, as opposed to the high-speed
XDR memory that is used by the CPU itself.

Since the memory controller does not participate in the
cache coherency protocol, we can not use the memory direcly
for Linux applications, but by providing a block device
it can be used for swap space, temporary file storage and
through the use of the direct_access block device operation
for mapping into user addresses, when it is mounted with
an appropriate file system.

Signed-off-by: Maxim Shchetynin <maxim@de.ibm.com>
Signed-off-by: Christian Krafft <krafft@de.ibm.com>

--
 arch/powerpc/Kconfig          |   10 +
 arch/powerpc/sysdev/Makefile  |    1
 arch/powerpc/sysdev/axonram.c |  357 ++++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+)

Acked-by: David Howells <dhowells@redhat.com>

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 7295252..2ae6522 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -595,6 +595,16 @@ config PPC_TODC
 	---help---
 	  This adds support for many TODC/RTC chips.
 
+config AXON_RAM
+	tristate "Axon DDR2 memory device driver"
+	depends on PPC_IBM_CELL_BLADE
+	default m
+	help
+	  It registers one block device per Axon's DDR2 memory bank found
+	  on a system. Block devices are called axonram?, their major and
+	  minor numbers are available in /proc/devices, /proc/partitions or
+	  in /sys/block/axonram?/dev.
+
 endmenu
 
 source arch/powerpc/platforms/embedded6xx/Kconfig
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index b9ff95a..8477f9d 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_MMIO_NVRAM)	+= mmio_nvram.o
 obj-$(CONFIG_FSL_SOC)		+= fsl_soc.o
 obj-$(CONFIG_PPC_TODC)		+= todc.o
 obj-$(CONFIG_TSI108_BRIDGE)	+= tsi108_pci.o tsi108_dev.o
+obj-$(CONFIG_AXON_RAM)		+= axonram.o
 
 ifeq ($(CONFIG_PPC_MERGE),y)
 obj-$(CONFIG_PPC_I8259)		+= i8259.o
diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c
new file mode 100644
index 0000000..13387f3
--- /dev/null
+++ b/arch/powerpc/sysdev/axonram.c
@@ -0,0 +1,357 @@
+/*
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2006
+ *
+ * Author: Maxim Shchetynin <maxim@de.ibm.com>
+ *
+ * Axon DDR2 device driver.
+ * It registers one block device per Axon's DDR2 memory bank found on a system.
+ * Block devices are called axonram?, their major and minor numbers are
+ * available in /proc/devices, /proc/partitions or in /sys/block/axonram?/dev.
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/of_device.h>
+#include <asm/page.h>
+#include <asm/prom.h>
+
+#define AXON_RAM_MODULE_NAME		"axonram"
+#define AXON_RAM_DEVICE_NAME		"axonram"
+#define AXON_RAM_MINORS_PER_DISK	16
+#define AXON_RAM_BLOCK_SHIFT		PAGE_SHIFT
+#define AXON_RAM_BLOCK_SIZE		1 << AXON_RAM_BLOCK_SHIFT
+#define AXON_RAM_SECTOR_SHIFT		9
+#define AXON_RAM_SECTOR_SIZE		1 << AXON_RAM_SECTOR_SHIFT
+#define AXON_RAM_IRQ_FLAGS		IRQF_SHARED | IRQF_TRIGGER_RISING
+
+struct axon_ram_bank {
+	struct of_device	*device;
+	struct gendisk		*disk;
+	unsigned int		irq_id;
+	unsigned long		ph_addr;
+	unsigned long		io_addr;
+	unsigned long		size;
+	unsigned long		ecc_counter;
+};
+
+static ssize_t
+axon_ram_sysfs_ecc(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct of_device *device = to_of_device(dev);
+	struct axon_ram_bank *bank = device->dev.platform_data;
+
+	BUG_ON(!bank);
+
+	return sprintf(buf, "%ld\n", bank->ecc_counter);
+}
+
+static DEVICE_ATTR(ecc, S_IRUGO, axon_ram_sysfs_ecc, NULL);
+
+/**
+ * axon_ram_irq_handler - interrupt handler for Axon RAM ECC
+ * @irq: interrupt ID
+ * @dev: pointer to of_device
+ */
+static irqreturn_t
+axon_ram_irq_handler(int irq, void *dev, struct pt_regs *regs)
+{
+	struct of_device *device = dev;
+	struct axon_ram_bank *bank = device->dev.platform_data;
+
+	BUG_ON(!bank);
+
+	dev_err(&device->dev, "Correctable memory error occured\n");
+	bank->ecc_counter++;
+	return IRQ_HANDLED;
+}
+
+/**
+ * axon_ram_make_request - make_request() method for block device
+ * @queue, @bio: see blk_queue_make_request()
+ */
+static int
+axon_ram_make_request(struct request_queue *queue, struct bio *bio)
+{
+	struct axon_ram_bank *bank = bio->bi_bdev->bd_disk->private_data;
+	unsigned long phys_mem, phys_end;
+	void *user_mem;
+	struct bio_vec *vec;
+	unsigned int transfered;
+	unsigned short idx;
+	int rc = 0;
+
+	phys_mem = bank->io_addr + (bio->bi_sector << AXON_RAM_SECTOR_SHIFT);
+	phys_end = bank->io_addr + bank->size;
+	transfered = 0;
+	bio_for_each_segment(vec, bio, idx) {
+		if (unlikely(phys_mem + vec->bv_len > phys_end)) {
+			bio_io_error(bio, bio->bi_size);
+			rc = -ERANGE;
+			break;
+		}
+
+		user_mem = page_address(vec->bv_page) + vec->bv_offset;
+		if (bio_data_dir(bio) == READ)
+			memcpy(user_mem, (void *) phys_mem, vec->bv_len);
+		else
+			memcpy((void *) phys_mem, user_mem, vec->bv_len);
+
+		phys_mem += vec->bv_len;
+		transfered += vec->bv_len;
+	}
+	bio_endio(bio, transfered, 0);
+
+	return rc;
+}
+
+/**
+ * axon_ram_direct_access - direct_access() method for block device
+ * @device, @sector, @data: see block_device_operations method
+ */
+static int
+axon_ram_direct_access(struct block_device *device, sector_t sector,
+		       unsigned long *data)
+{
+	struct axon_ram_bank *bank = device->bd_disk->private_data;
+	loff_t offset;
+
+	offset = sector << AXON_RAM_SECTOR_SHIFT;
+	if (offset >= bank->size) {
+		dev_err(&bank->device->dev, "Access outside of address space\n");
+		return -ERANGE;
+	}
+
+	*data = bank->ph_addr + offset;
+
+	return 0;
+}
+
+static struct block_device_operations axon_ram_devops = {
+	.owner		= THIS_MODULE,
+	.direct_access	= axon_ram_direct_access
+};
+
+/**
+ * axon_ram_probe - probe() method for platform driver
+ * @device, @device_id: see of_platform_driver method
+ */
+static int
+axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
+{
+	static int axon_ram_bank_id = -1;
+	struct axon_ram_bank *bank;
+	struct resource resource;
+	int rc = 0;
+
+	axon_ram_bank_id++;
+
+	dev_info(&device->dev, "Found memory controller on %s\n",
+			device->node->full_name);
+
+	bank = kzalloc(sizeof(struct axon_ram_bank), GFP_KERNEL);
+	if (bank == NULL) {
+		dev_err(&device->dev, "Out of memory\n");
+		rc = -ENOMEM;
+		goto failed;
+	}
+
+	device->dev.platform_data = bank;
+
+	bank->device = device;
+
+	if (of_address_to_resource(device->node, 0, &resource) != 0) {
+		dev_err(&device->dev, "Cannot access device tree\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	bank->size = resource.end - resource.start + 1;
+
+	if (bank->size == 0) {
+		dev_err(&device->dev, "No DDR2 memory found for %s%d\n",
+				AXON_RAM_DEVICE_NAME, axon_ram_bank_id);
+		rc = -ENODEV;
+		goto failed;
+	}
+
+	dev_info(&device->dev, "Register DDR2 memory device %s%d with %luMB\n",
+			AXON_RAM_DEVICE_NAME, axon_ram_bank_id, bank->size >> 20);
+
+	bank->ph_addr = resource.start;
+	bank->io_addr = (unsigned long) __ioremap(
+			bank->ph_addr, bank->size, _PAGE_NO_CACHE);
+	if (bank->io_addr == 0) {
+		dev_err(&device->dev, "ioremap() failed\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	bank->disk = alloc_disk(AXON_RAM_MINORS_PER_DISK);
+	if (bank->disk == NULL) {
+		dev_err(&device->dev, "Cannot register disk\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	bank->disk->first_minor = 0;
+	bank->disk->fops = &axon_ram_devops;
+	bank->disk->private_data = bank;
+	bank->disk->driverfs_dev = &device->dev;
+
+	sprintf(bank->disk->disk_name, "%s%d",
+			AXON_RAM_DEVICE_NAME, axon_ram_bank_id);
+	bank->disk->major = register_blkdev(0, bank->disk->disk_name);
+	if (bank->disk->major < 0) {
+		dev_err(&device->dev, "Cannot register block device\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	bank->disk->queue = blk_alloc_queue(GFP_KERNEL);
+	if (bank->disk->queue == NULL) {
+		dev_err(&device->dev, "Cannot register disk queue\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	set_capacity(bank->disk, bank->size >> AXON_RAM_SECTOR_SHIFT);
+	blk_queue_make_request(bank->disk->queue, axon_ram_make_request);
+	blk_queue_hardsect_size(bank->disk->queue, AXON_RAM_SECTOR_SIZE);
+	add_disk(bank->disk);
+
+	bank->irq_id = irq_of_parse_and_map(device->node, 0);
+	if (bank->irq_id == NO_IRQ) {
+		dev_err(&device->dev, "Cannot access ECC interrupt ID\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	rc = request_irq(bank->irq_id, axon_ram_irq_handler,
+			AXON_RAM_IRQ_FLAGS, bank->disk->disk_name, device);
+	if (rc != 0) {
+		dev_err(&device->dev, "Cannot register ECC interrupt handler\n");
+		bank->irq_id = NO_IRQ;
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	rc = device_create_file(&device->dev, &dev_attr_ecc);
+	if (rc != 0) {
+		dev_err(&device->dev, "Cannot create sysfs file\n");
+		rc = -EFAULT;
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	if (bank != NULL) {
+		if (bank->irq_id != NO_IRQ)
+			free_irq(bank->irq_id, device);
+		if (bank->disk != NULL) {
+			if (bank->disk->major > 0)
+				unregister_blkdev(bank->disk->major,
+						bank->disk->disk_name);
+			del_gendisk(bank->disk);
+		}
+		device->dev.platform_data = NULL;
+		if (bank->io_addr != 0)
+			iounmap((void __iomem *) bank->io_addr);
+		kfree(bank);
+	}
+
+	return rc;
+}
+
+/**
+ * axon_ram_remove - remove() method for platform driver
+ * @device: see of_platform_driver method
+ */
+static int
+axon_ram_remove(struct of_device *device)
+{
+	struct axon_ram_bank *bank = device->dev.platform_data;
+
+	BUG_ON(!bank || !bank->disk);
+
+	device_remove_file(&device->dev, &dev_attr_ecc);
+	free_irq(bank->irq_id, device);
+	unregister_blkdev(bank->disk->major, bank->disk->disk_name);
+	del_gendisk(bank->disk);
+	iounmap((void __iomem *) bank->io_addr);
+	kfree(bank);
+
+	return 0;
+}
+
+static struct of_device_id axon_ram_device_id[] = {
+	{
+		.type	= "dma-memory"
+	},
+	{}
+};
+
+static struct of_platform_driver axon_ram_driver = {
+	.owner		= THIS_MODULE,
+	.name		= AXON_RAM_MODULE_NAME,
+	.match_table	= axon_ram_device_id,
+	.probe		= axon_ram_probe,
+	.remove		= axon_ram_remove
+};
+
+/**
+ * axon_ram_init
+ */
+static int __init
+axon_ram_init(void)
+{
+	return of_register_platform_driver(&axon_ram_driver);
+}
+
+/**
+ * axon_ram_exit
+ */
+static void __exit
+axon_ram_exit(void)
+{
+	of_unregister_platform_driver(&axon_ram_driver);
+}
+
+module_init(axon_ram_init);
+module_exit(axon_ram_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Shchetynin <maxim@de.ibm.com>");
+MODULE_DESCRIPTION("Axon DDR2 RAM device driver for IBM Cell BE");