aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorMaxim Shchetynin <maxim@de.ibm.com>2007-07-20 15:39:26 -0400
committerArnd Bergmann <arnd@klappe.arndb.de>2007-07-20 15:41:42 -0400
commitdbdf04c40161f81d74e27f04e201acb3a5dfad69 (patch)
treeaae342989f0431245bea9a0f47c0896950d76a17 /arch/powerpc
parentb86ce01c7700cfc74665799355a46dcadf920ebd (diff)
[CELL] driver for DDR2 memory on AXON
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: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/platforms/Kconfig10
-rw-r--r--arch/powerpc/sysdev/Makefile1
-rw-r--r--arch/powerpc/sysdev/axonram.c381
3 files changed, 392 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 33545d352e92..932538a93c2b 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -272,4 +272,14 @@ config CPM2
272 you wish to build a kernel for a machine with a CPM2 coprocessor 272 you wish to build a kernel for a machine with a CPM2 coprocessor
273 on it (826x, 827x, 8560). 273 on it (826x, 827x, 8560).
274 274
275config AXON_RAM
276 tristate "Axon DDR2 memory device driver"
277 depends on PPC_IBM_CELL_BLADE
278 default m
279 help
280 It registers one block device per Axon's DDR2 memory bank found
281 on a system. Block devices are called axonram?, their major and
282 minor numbers are available in /proc/devices, /proc/partitions or
283 in /sys/block/axonram?/dev.
284
275endmenu 285endmenu
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index f65078c3d3b3..484eb4e0e9db 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_QUICC_ENGINE) += qe_lib/
17mv64x60-$(CONFIG_PCI) += mv64x60_pci.o 17mv64x60-$(CONFIG_PCI) += mv64x60_pci.o
18obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o 18obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o
19obj-$(CONFIG_RTC_DRV_CMOS) += rtc_cmos_setup.o 19obj-$(CONFIG_RTC_DRV_CMOS) += rtc_cmos_setup.o
20obj-$(CONFIG_AXON_RAM) += axonram.o
20 21
21# contains only the suspend handler for time 22# contains only the suspend handler for time
22ifeq ($(CONFIG_RTC_CLASS),) 23ifeq ($(CONFIG_RTC_CLASS),)
diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c
new file mode 100644
index 000000000000..2326d5dc5752
--- /dev/null
+++ b/arch/powerpc/sysdev/axonram.c
@@ -0,0 +1,381 @@
1/*
2 * (C) Copyright IBM Deutschland Entwicklung GmbH 2006
3 *
4 * Author: Maxim Shchetynin <maxim@de.ibm.com>
5 *
6 * Axon DDR2 device driver.
7 * It registers one block device per Axon's DDR2 memory bank found on a system.
8 * Block devices are called axonram?, their major and minor numbers are
9 * available in /proc/devices, /proc/partitions or in /sys/block/axonram?/dev.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
14 * any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26#include <linux/bio.h>
27#include <linux/blkdev.h>
28#include <linux/buffer_head.h>
29#include <linux/device.h>
30#include <linux/errno.h>
31#include <linux/fs.h>
32#include <linux/genhd.h>
33#include <linux/interrupt.h>
34#include <linux/io.h>
35#include <linux/ioport.h>
36#include <linux/irq.h>
37#include <linux/irqreturn.h>
38#include <linux/kernel.h>
39#include <linux/mm.h>
40#include <linux/mod_devicetable.h>
41#include <linux/module.h>
42#include <linux/slab.h>
43#include <linux/string.h>
44#include <linux/types.h>
45#include <asm/of_device.h>
46#include <asm/of_platform.h>
47#include <asm/page.h>
48#include <asm/prom.h>
49
50#define AXON_RAM_MODULE_NAME "axonram"
51#define AXON_RAM_DEVICE_NAME "axonram"
52#define AXON_RAM_MINORS_PER_DISK 16
53#define AXON_RAM_BLOCK_SHIFT PAGE_SHIFT
54#define AXON_RAM_BLOCK_SIZE 1 << AXON_RAM_BLOCK_SHIFT
55#define AXON_RAM_SECTOR_SHIFT 9
56#define AXON_RAM_SECTOR_SIZE 1 << AXON_RAM_SECTOR_SHIFT
57#define AXON_RAM_IRQ_FLAGS IRQF_SHARED | IRQF_TRIGGER_RISING
58
59struct axon_ram_bank {
60 struct of_device *device;
61 struct gendisk *disk;
62 unsigned int irq_correctable;
63 unsigned int irq_uncorrectable;
64 unsigned long ph_addr;
65 unsigned long io_addr;
66 unsigned long size;
67 unsigned long ecc_counter;
68};
69
70static ssize_t
71axon_ram_sysfs_ecc(struct device *dev, struct device_attribute *attr, char *buf)
72{
73 struct of_device *device = to_of_device(dev);
74 struct axon_ram_bank *bank = device->dev.platform_data;
75
76 BUG_ON(!bank);
77
78 return sprintf(buf, "%ld\n", bank->ecc_counter);
79}
80
81static DEVICE_ATTR(ecc, S_IRUGO, axon_ram_sysfs_ecc, NULL);
82
83/**
84 * axon_ram_irq_handler - interrupt handler for Axon RAM ECC
85 * @irq: interrupt ID
86 * @dev: pointer to of_device
87 */
88static irqreturn_t
89axon_ram_irq_handler(int irq, void *dev)
90{
91 struct of_device *device = dev;
92 struct axon_ram_bank *bank = device->dev.platform_data;
93
94 BUG_ON(!bank);
95
96 if (irq == bank->irq_correctable) {
97 dev_err(&device->dev, "Correctable memory error occured\n");
98 bank->ecc_counter++;
99 return IRQ_HANDLED;
100 } else if (irq == bank->irq_uncorrectable) {
101 dev_err(&device->dev, "Uncorrectable memory error occured\n");
102 panic("Critical ECC error on %s", device->node->full_name);
103 }
104
105 return IRQ_NONE;
106}
107
108/**
109 * axon_ram_make_request - make_request() method for block device
110 * @queue, @bio: see blk_queue_make_request()
111 */
112static int
113axon_ram_make_request(struct request_queue *queue, struct bio *bio)
114{
115 struct axon_ram_bank *bank = bio->bi_bdev->bd_disk->private_data;
116 unsigned long phys_mem, phys_end;
117 void *user_mem;
118 struct bio_vec *vec;
119 unsigned int transfered;
120 unsigned short idx;
121 int rc = 0;
122
123 phys_mem = bank->io_addr + (bio->bi_sector << AXON_RAM_SECTOR_SHIFT);
124 phys_end = bank->io_addr + bank->size;
125 transfered = 0;
126 bio_for_each_segment(vec, bio, idx) {
127 if (unlikely(phys_mem + vec->bv_len > phys_end)) {
128 bio_io_error(bio, bio->bi_size);
129 rc = -ERANGE;
130 break;
131 }
132
133 user_mem = page_address(vec->bv_page) + vec->bv_offset;
134 if (bio_data_dir(bio) == READ)
135 memcpy(user_mem, (void *) phys_mem, vec->bv_len);
136 else
137 memcpy((void *) phys_mem, user_mem, vec->bv_len);
138
139 phys_mem += vec->bv_len;
140 transfered += vec->bv_len;
141 }
142 bio_endio(bio, transfered, 0);
143
144 return rc;
145}
146
147/**
148 * axon_ram_direct_access - direct_access() method for block device
149 * @device, @sector, @data: see block_device_operations method
150 */
151static int
152axon_ram_direct_access(struct block_device *device, sector_t sector,
153 unsigned long *data)
154{
155 struct axon_ram_bank *bank = device->bd_disk->private_data;
156 loff_t offset;
157
158 offset = sector << AXON_RAM_SECTOR_SHIFT;
159 if (offset >= bank->size) {
160 dev_err(&bank->device->dev, "Access outside of address space\n");
161 return -ERANGE;
162 }
163
164 *data = bank->ph_addr + offset;
165
166 return 0;
167}
168
169static struct block_device_operations axon_ram_devops = {
170 .owner = THIS_MODULE,
171 .direct_access = axon_ram_direct_access
172};
173
174/**
175 * axon_ram_probe - probe() method for platform driver
176 * @device, @device_id: see of_platform_driver method
177 */
178static int
179axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
180{
181 static int axon_ram_bank_id = -1;
182 struct axon_ram_bank *bank;
183 struct resource resource;
184 int rc = 0;
185
186 axon_ram_bank_id++;
187
188 dev_info(&device->dev, "Found memory controller on %s\n",
189 device->node->full_name);
190
191 bank = kzalloc(sizeof(struct axon_ram_bank), GFP_KERNEL);
192 if (bank == NULL) {
193 dev_err(&device->dev, "Out of memory\n");
194 rc = -ENOMEM;
195 goto failed;
196 }
197
198 device->dev.platform_data = bank;
199
200 bank->device = device;
201
202 if (of_address_to_resource(device->node, 0, &resource) != 0) {
203 dev_err(&device->dev, "Cannot access device tree\n");
204 rc = -EFAULT;
205 goto failed;
206 }
207
208 bank->size = resource.end - resource.start + 1;
209
210 if (bank->size == 0) {
211 dev_err(&device->dev, "No DDR2 memory found for %s%d\n",
212 AXON_RAM_DEVICE_NAME, axon_ram_bank_id);
213 rc = -ENODEV;
214 goto failed;
215 }
216
217 dev_info(&device->dev, "Register DDR2 memory device %s%d with %luMB\n",
218 AXON_RAM_DEVICE_NAME, axon_ram_bank_id, bank->size >> 20);
219
220 bank->ph_addr = resource.start;
221 bank->io_addr = (unsigned long) ioremap_flags(
222 bank->ph_addr, bank->size, _PAGE_NO_CACHE);
223 if (bank->io_addr == 0) {
224 dev_err(&device->dev, "ioremap() failed\n");
225 rc = -EFAULT;
226 goto failed;
227 }
228
229 bank->disk = alloc_disk(AXON_RAM_MINORS_PER_DISK);
230 if (bank->disk == NULL) {
231 dev_err(&device->dev, "Cannot register disk\n");
232 rc = -EFAULT;
233 goto failed;
234 }
235
236 bank->disk->first_minor = 0;
237 bank->disk->fops = &axon_ram_devops;
238 bank->disk->private_data = bank;
239 bank->disk->driverfs_dev = &device->dev;
240
241 sprintf(bank->disk->disk_name, "%s%d",
242 AXON_RAM_DEVICE_NAME, axon_ram_bank_id);
243 bank->disk->major = register_blkdev(0, bank->disk->disk_name);
244 if (bank->disk->major < 0) {
245 dev_err(&device->dev, "Cannot register block device\n");
246 rc = -EFAULT;
247 goto failed;
248 }
249
250 bank->disk->queue = blk_alloc_queue(GFP_KERNEL);
251 if (bank->disk->queue == NULL) {
252 dev_err(&device->dev, "Cannot register disk queue\n");
253 rc = -EFAULT;
254 goto failed;
255 }
256
257 set_capacity(bank->disk, bank->size >> AXON_RAM_SECTOR_SHIFT);
258 blk_queue_make_request(bank->disk->queue, axon_ram_make_request);
259 blk_queue_hardsect_size(bank->disk->queue, AXON_RAM_SECTOR_SIZE);
260 add_disk(bank->disk);
261
262 bank->irq_correctable = irq_of_parse_and_map(device->node, 0);
263 bank->irq_uncorrectable = irq_of_parse_and_map(device->node, 1);
264 if ((bank->irq_correctable <= 0) || (bank->irq_uncorrectable <= 0)) {
265 dev_err(&device->dev, "Cannot access ECC interrupt ID\n");
266 rc = -EFAULT;
267 goto failed;
268 }
269
270 rc = request_irq(bank->irq_correctable, axon_ram_irq_handler,
271 AXON_RAM_IRQ_FLAGS, bank->disk->disk_name, device);
272 if (rc != 0) {
273 dev_err(&device->dev, "Cannot register ECC interrupt handler\n");
274 bank->irq_correctable = bank->irq_uncorrectable = 0;
275 rc = -EFAULT;
276 goto failed;
277 }
278
279 rc = request_irq(bank->irq_uncorrectable, axon_ram_irq_handler,
280 AXON_RAM_IRQ_FLAGS, bank->disk->disk_name, device);
281 if (rc != 0) {
282 dev_err(&device->dev, "Cannot register ECC interrupt handler\n");
283 bank->irq_uncorrectable = 0;
284 rc = -EFAULT;
285 goto failed;
286 }
287
288 rc = device_create_file(&device->dev, &dev_attr_ecc);
289 if (rc != 0) {
290 dev_err(&device->dev, "Cannot create sysfs file\n");
291 rc = -EFAULT;
292 goto failed;
293 }
294
295 return 0;
296
297failed:
298 if (bank != NULL) {
299 if (bank->irq_uncorrectable > 0)
300 free_irq(bank->irq_uncorrectable, device);
301 if (bank->irq_correctable > 0)
302 free_irq(bank->irq_correctable, device);
303 if (bank->disk != NULL) {
304 if (bank->disk->queue != NULL)
305 blk_cleanup_queue(bank->disk->queue);
306 if (bank->disk->major > 0)
307 unregister_blkdev(bank->disk->major,
308 bank->disk->disk_name);
309 del_gendisk(bank->disk);
310 }
311 device->dev.platform_data = NULL;
312 if (bank->io_addr != 0)
313 iounmap((void __iomem *) bank->io_addr);
314 kfree(bank);
315 }
316
317 return rc;
318}
319
320/**
321 * axon_ram_remove - remove() method for platform driver
322 * @device: see of_platform_driver method
323 */
324static int
325axon_ram_remove(struct of_device *device)
326{
327 struct axon_ram_bank *bank = device->dev.platform_data;
328
329 BUG_ON(!bank || !bank->disk);
330
331 device_remove_file(&device->dev, &dev_attr_ecc);
332 free_irq(bank->irq_uncorrectable, device);
333 free_irq(bank->irq_correctable, device);
334 blk_cleanup_queue(bank->disk->queue);
335 unregister_blkdev(bank->disk->major, bank->disk->disk_name);
336 del_gendisk(bank->disk);
337 iounmap((void __iomem *) bank->io_addr);
338 kfree(bank);
339
340 return 0;
341}
342
343static struct of_device_id axon_ram_device_id[] = {
344 {
345 .type = "dma-memory"
346 },
347 {}
348};
349
350static struct of_platform_driver axon_ram_driver = {
351 .owner = THIS_MODULE,
352 .name = AXON_RAM_MODULE_NAME,
353 .match_table = axon_ram_device_id,
354 .probe = axon_ram_probe,
355 .remove = axon_ram_remove
356};
357
358/**
359 * axon_ram_init
360 */
361static int __init
362axon_ram_init(void)
363{
364 return of_register_platform_driver(&axon_ram_driver);
365}
366
367/**
368 * axon_ram_exit
369 */
370static void __exit
371axon_ram_exit(void)
372{
373 of_unregister_platform_driver(&axon_ram_driver);
374}
375
376module_init(axon_ram_init);
377module_exit(axon_ram_exit);
378
379MODULE_LICENSE("GPL");
380MODULE_AUTHOR("Maxim Shchetynin <maxim@de.ibm.com>");
381MODULE_DESCRIPTION("Axon DDR2 RAM device driver for IBM Cell BE");