axonram.c 9.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*
 * (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>
45 46 47
#include <linux/of_device.h>
#include <linux/of_platform.h>

48 49 50 51 52 53 54 55 56 57 58 59
#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

60 61
static int azfs_major, azfs_minor;

62 63 64
struct axon_ram_bank {
	struct of_device	*device;
	struct gendisk		*disk;
65
	unsigned int		irq_id;
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	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 of_device *device = dev;
	struct axon_ram_bank *bank = device->dev.platform_data;

	BUG_ON(!bank);

98 99 100
	dev_err(&device->dev, "Correctable memory error occured\n");
	bank->ecc_counter++;
	return IRQ_HANDLED;
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

/**
 * 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)) {
A
Al Viro 已提交
123
			bio_io_error(bio);
124 125 126 127 128 129 130 131 132 133 134 135 136
			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;
	}
A
Al Viro 已提交
137
	bio_endio(bio, 0);
138 139 140 141 142 143 144 145 146 147

	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,
148
		       void **kaddr, unsigned long *pfn)
149 150 151 152
{
	struct axon_ram_bank *bank = device->bd_disk->private_data;
	loff_t offset;

153 154 155 156
	offset = sector;
	if (device->bd_part != NULL)
		offset += device->bd_part->start_sect;
	offset <<= AXON_RAM_SECTOR_SHIFT;
157 158 159 160 161
	if (offset >= bank->size) {
		dev_err(&bank->device->dev, "Access outside of address space\n");
		return -ERANGE;
	}

162 163
	*kaddr = (void *)(bank->ph_addr + offset);
	*pfn = virt_to_phys(kaddr) >> PAGE_SHIFT;
164 165 166 167

	return 0;
}

168
static const struct block_device_operations axon_ram_devops = {
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	.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_flags(
			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;
	}

235 236
	bank->disk->major = azfs_major;
	bank->disk->first_minor = azfs_minor;
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
	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->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);
253
	blk_queue_logical_block_size(bank->disk->queue, AXON_RAM_SECTOR_SIZE);
254 255
	add_disk(bank->disk);

256 257
	bank->irq_id = irq_of_parse_and_map(device->node, 0);
	if (bank->irq_id == NO_IRQ) {
258 259 260 261 262
		dev_err(&device->dev, "Cannot access ECC interrupt ID\n");
		rc = -EFAULT;
		goto failed;
	}

263
	rc = request_irq(bank->irq_id, axon_ram_irq_handler,
264 265 266
			AXON_RAM_IRQ_FLAGS, bank->disk->disk_name, device);
	if (rc != 0) {
		dev_err(&device->dev, "Cannot register ECC interrupt handler\n");
267
		bank->irq_id = NO_IRQ;
268 269 270 271 272 273 274 275 276 277 278
		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;
	}

279 280
	azfs_minor += bank->disk->minors;

281 282 283 284
	return 0;

failed:
	if (bank != NULL) {
285 286
		if (bank->irq_id != NO_IRQ)
			free_irq(bank->irq_id, device);
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
		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);
314
	free_irq(bank->irq_id, device);
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
	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 = {
	.match_table	= axon_ram_device_id,
	.probe		= axon_ram_probe,
332 333 334 335 336
	.remove		= axon_ram_remove,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= AXON_RAM_MODULE_NAME,
	},
337 338 339 340 341 342 343 344
};

/**
 * axon_ram_init
 */
static int __init
axon_ram_init(void)
{
345 346 347 348 349 350 351 352
	azfs_major = register_blkdev(azfs_major, AXON_RAM_DEVICE_NAME);
	if (azfs_major < 0) {
		printk(KERN_ERR "%s cannot become block device major number\n",
				AXON_RAM_MODULE_NAME);
		return -EFAULT;
	}
	azfs_minor = 0;

353 354 355 356 357 358 359 360 361 362
	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);
363
	unregister_blkdev(azfs_major, AXON_RAM_DEVICE_NAME);
364 365 366 367 368 369 370 371
}

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");