ps3flash.c 10.9 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
/*
 * PS3 FLASH ROM Storage Driver
 *
 * Copyright (C) 2007 Sony Computer Entertainment Inc.
 * Copyright 2007 Sony Corp.
 *
 * 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; version 2 of the License.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <linux/fs.h>
#include <linux/miscdevice.h>
23
#include <linux/slab.h>
24
#include <linux/uaccess.h>
25
#include <linux/module.h>
26 27 28 29 30 31 32 33 34 35 36 37

#include <asm/lv1call.h>
#include <asm/ps3stor.h>


#define DEVICE_NAME		"ps3flash"

#define FLASH_BLOCK_SIZE	(256*1024)


struct ps3flash_private {
	struct mutex mutex;	/* Bounce buffer mutex */
38 39 40
	u64 chunk_sectors;
	int tag;		/* Start sector of buffer, -1 if invalid */
	bool dirty;
41 42 43 44
};

static struct ps3_storage_device *ps3flash_dev;

45
static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
46
				       u64 start_sector, int write)
47
{
48 49 50
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
	u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
					     start_sector, priv->chunk_sectors,
51 52
					     write);
	if (res) {
53
		dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
54 55 56
			__LINE__, write ? "write" : "read", res);
		return -EIO;
	}
57
	return 0;
58 59
}

60
static int ps3flash_writeback(struct ps3_storage_device *dev)
61
{
62 63
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
	int res;
64

65 66 67
	if (!priv->dirty || priv->tag < 0)
		return 0;

68
	res = ps3flash_read_write_sectors(dev, priv->tag, 1);
69 70
	if (res)
		return res;
71

72 73
	priv->dirty = false;
	return 0;
74 75
}

76
static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
77
{
78 79 80
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
	int res;

81
	if (start_sector == priv->tag)
82 83 84 85 86 87 88 89
		return 0;

	res = ps3flash_writeback(dev);
	if (res)
		return res;

	priv->tag = -1;

90
	res = ps3flash_read_write_sectors(dev, start_sector, 0);
91 92 93
	if (res)
		return res;

94
	priv->tag = start_sector;
95
	return 0;
96 97 98 99 100 101 102 103 104
}

static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
{
	struct ps3_storage_device *dev = ps3flash_dev;
	loff_t res;

	mutex_lock(&file->f_mapping->host->i_mutex);
	switch (origin) {
105 106
	case 0:
		break;
107 108 109 110 111 112
	case 1:
		offset += file->f_pos;
		break;
	case 2:
		offset += dev->regions[dev->region_idx].size*dev->blk_size;
		break;
113 114
	default:
		offset = -1;
115 116 117 118 119 120 121 122 123 124 125 126 127 128
	}
	if (offset < 0) {
		res = -EINVAL;
		goto out;
	}

	file->f_pos = offset;
	res = file->f_pos;

out:
	mutex_unlock(&file->f_mapping->host->i_mutex);
	return res;
}

129 130
static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
			     size_t count, loff_t *pos)
131 132
{
	struct ps3_storage_device *dev = ps3flash_dev;
133
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
134
	u64 size, sector, offset;
135
	int res;
136
	size_t remaining, n;
137
	const void *src;
138 139

	dev_dbg(&dev->sbd.core,
140 141
		"%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
		__func__, __LINE__, count, *pos, userbuf, kernelbuf);
142 143 144 145 146 147 148 149 150 151 152 153

	size = dev->regions[dev->region_idx].size*dev->blk_size;
	if (*pos >= size || !count)
		return 0;

	if (*pos + count > size) {
		dev_dbg(&dev->sbd.core,
			"%s:%u Truncating count from %zu to %llu\n", __func__,
			__LINE__, count, size - *pos);
		count = size - *pos;
	}

154
	sector = *pos / dev->bounce_size * priv->chunk_sectors;
155
	offset = *pos % dev->bounce_size;
156 157 158

	remaining = count;
	do {
159 160
		n = min_t(u64, remaining, dev->bounce_size - offset);
		src = dev->bounce_buf + offset;
161

162 163
		mutex_lock(&priv->mutex);

164
		res = ps3flash_fetch(dev, sector);
165
		if (res)
166 167 168
			goto fail;

		dev_dbg(&dev->sbd.core,
169 170 171 172
			"%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n",
			__func__, __LINE__, n, src, userbuf, kernelbuf);
		if (userbuf) {
			if (copy_to_user(userbuf, src, n)) {
173
				res = -EFAULT;
174 175 176 177 178 179 180
				goto fail;
			}
			userbuf += n;
		}
		if (kernelbuf) {
			memcpy(kernelbuf, src, n);
			kernelbuf += n;
181 182 183 184 185 186
		}

		mutex_unlock(&priv->mutex);

		*pos += n;
		remaining -= n;
187
		sector += priv->chunk_sectors;
188 189 190 191 192 193
		offset = 0;
	} while (remaining > 0);

	return count;

fail:
194 195
	mutex_unlock(&priv->mutex);
	return res;
196 197
}

198 199
static ssize_t ps3flash_write(const char __user *userbuf,
			      const void *kernelbuf, size_t count, loff_t *pos)
200 201
{
	struct ps3_storage_device *dev = ps3flash_dev;
202
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
203 204
	u64 size, sector, offset;
	int res = 0;
205
	size_t remaining, n;
206
	void *dst;
207 208

	dev_dbg(&dev->sbd.core,
209 210
		"%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
		__func__, __LINE__, count, *pos, userbuf, kernelbuf);
211 212 213 214 215 216 217 218 219 220 221 222

	size = dev->regions[dev->region_idx].size*dev->blk_size;
	if (*pos >= size || !count)
		return 0;

	if (*pos + count > size) {
		dev_dbg(&dev->sbd.core,
			"%s:%u Truncating count from %zu to %llu\n", __func__,
			__LINE__, count, size - *pos);
		count = size - *pos;
	}

223
	sector = *pos / dev->bounce_size * priv->chunk_sectors;
224 225 226 227
	offset = *pos % dev->bounce_size;

	remaining = count;
	do {
228
		n = min_t(u64, remaining, dev->bounce_size - offset);
229
		dst = dev->bounce_buf + offset;
230

231 232
		mutex_lock(&priv->mutex);

233
		if (n != dev->bounce_size)
234
			res = ps3flash_fetch(dev, sector);
235 236 237 238
		else if (sector != priv->tag)
			res = ps3flash_writeback(dev);
		if (res)
			goto fail;
239 240

		dev_dbg(&dev->sbd.core,
241 242 243 244 245 246 247 248 249 250 251 252
			"%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n",
			__func__, __LINE__, n, userbuf, kernelbuf, dst);
		if (userbuf) {
			if (copy_from_user(dst, userbuf, n)) {
				res = -EFAULT;
				goto fail;
			}
			userbuf += n;
		}
		if (kernelbuf) {
			memcpy(dst, kernelbuf, n);
			kernelbuf += n;
253 254
		}

255 256
		priv->tag = sector;
		priv->dirty = true;
257 258 259 260 261

		mutex_unlock(&priv->mutex);

		*pos += n;
		remaining -= n;
262
		sector += priv->chunk_sectors;
263 264 265 266 267 268 269 270 271 272
		offset = 0;
	} while (remaining > 0);

	return count;

fail:
	mutex_unlock(&priv->mutex);
	return res;
}

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
static ssize_t ps3flash_user_read(struct file *file, char __user *buf,
				  size_t count, loff_t *pos)
{
	return ps3flash_read(buf, NULL, count, pos);
}

static ssize_t ps3flash_user_write(struct file *file, const char __user *buf,
				   size_t count, loff_t *pos)
{
	return ps3flash_write(buf, NULL, count, pos);
}

static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)
{
	return ps3flash_read(NULL, buf, count, &pos);
}

static ssize_t ps3flash_kernel_write(const void *buf, size_t count,
				     loff_t pos)
{
293 294 295 296 297 298 299 300 301 302 303 304 305
	ssize_t res;
	int wb;

	res = ps3flash_write(NULL, buf, count, &pos);
	if (res < 0)
		return res;

	/* Make kernel writes synchronous */
	wb = ps3flash_writeback(ps3flash_dev);
	if (wb)
		return wb;

	return res;
306 307
}

308 309 310 311 312
static int ps3flash_flush(struct file *file, fl_owner_t id)
{
	return ps3flash_writeback(ps3flash_dev);
}

313
static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)
314
{
Al Viro's avatar
Al Viro committed
315
	struct inode *inode = file_inode(file);
316 317 318 319 320
	int err;
	mutex_lock(&inode->i_mutex);
	err = ps3flash_writeback(ps3flash_dev);
	mutex_unlock(&inode->i_mutex);
	return err;
321
}
322 323 324 325 326 327 328 329 330 331 332

static irqreturn_t ps3flash_interrupt(int irq, void *data)
{
	struct ps3_storage_device *dev = data;
	int res;
	u64 tag, status;

	res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);

	if (tag != dev->tag)
		dev_err(&dev->sbd.core,
333
			"%s:%u: tag mismatch, got %llx, expected %llx\n",
334 335 336
			__func__, __LINE__, tag, dev->tag);

	if (res) {
337
		dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
338 339 340 341 342 343 344 345 346 347 348
			__func__, __LINE__, res, status);
	} else {
		dev->lv1_status = status;
		complete(&dev->done);
	}
	return IRQ_HANDLED;
}

static const struct file_operations ps3flash_fops = {
	.owner	= THIS_MODULE,
	.llseek	= ps3flash_llseek,
349 350
	.read	= ps3flash_user_read,
	.write	= ps3flash_user_write,
351 352
	.flush	= ps3flash_flush,
	.fsync	= ps3flash_fsync,
353 354 355 356 357
};

static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
	.read	= ps3flash_kernel_read,
	.write	= ps3flash_kernel_write,
358 359 360 361 362 363 364 365
};

static struct miscdevice ps3flash_misc = {
	.minor	= MISC_DYNAMIC_MINOR,
	.name	= DEVICE_NAME,
	.fops	= &ps3flash_fops,
};

366
static int ps3flash_probe(struct ps3_system_bus_device *_dev)
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
{
	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
	struct ps3flash_private *priv;
	int error;
	unsigned long tmp;

	tmp = dev->regions[dev->region_idx].start*dev->blk_size;
	if (tmp % FLASH_BLOCK_SIZE) {
		dev_err(&dev->sbd.core,
			"%s:%u region start %lu is not aligned\n", __func__,
			__LINE__, tmp);
		return -EINVAL;
	}
	tmp = dev->regions[dev->region_idx].size*dev->blk_size;
	if (tmp % FLASH_BLOCK_SIZE) {
		dev_err(&dev->sbd.core,
			"%s:%u region size %lu is not aligned\n", __func__,
			__LINE__, tmp);
		return -EINVAL;
	}

	/* use static buffer, kmalloc cannot allocate 256 KiB */
	if (!ps3flash_bounce_buffer.address)
		return -ENODEV;

	if (ps3flash_dev) {
		dev_err(&dev->sbd.core,
			"Only one FLASH device is supported\n");
		return -EBUSY;
	}

	ps3flash_dev = dev;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto fail;
	}

406
	ps3_system_bus_set_drvdata(&dev->sbd, priv);
407
	mutex_init(&priv->mutex);
408
	priv->tag = -1;
409 410 411

	dev->bounce_size = ps3flash_bounce_buffer.size;
	dev->bounce_buf = ps3flash_bounce_buffer.address;
412
	priv->chunk_sectors = dev->bounce_size / dev->blk_size;
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427

	error = ps3stor_setup(dev, ps3flash_interrupt);
	if (error)
		goto fail_free_priv;

	ps3flash_misc.parent = &dev->sbd.core;
	error = misc_register(&ps3flash_misc);
	if (error) {
		dev_err(&dev->sbd.core, "%s:%u: misc_register failed %d\n",
			__func__, __LINE__, error);
		goto fail_teardown;
	}

	dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",
		 __func__, __LINE__, ps3flash_misc.minor);
428 429

	ps3_os_area_flash_register(&ps3flash_kernel_ops);
430 431 432 433 434 435
	return 0;

fail_teardown:
	ps3stor_teardown(dev);
fail_free_priv:
	kfree(priv);
436
	ps3_system_bus_set_drvdata(&dev->sbd, NULL);
437 438 439 440 441 442 443 444 445
fail:
	ps3flash_dev = NULL;
	return error;
}

static int ps3flash_remove(struct ps3_system_bus_device *_dev)
{
	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);

446
	ps3_os_area_flash_register(NULL);
447 448
	misc_deregister(&ps3flash_misc);
	ps3stor_teardown(dev);
449 450
	kfree(ps3_system_bus_get_drvdata(&dev->sbd));
	ps3_system_bus_set_drvdata(&dev->sbd, NULL);
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	ps3flash_dev = NULL;
	return 0;
}


static struct ps3_system_bus_driver ps3flash = {
	.match_id	= PS3_MATCH_ID_STOR_FLASH,
	.core.name	= DEVICE_NAME,
	.core.owner	= THIS_MODULE,
	.probe		= ps3flash_probe,
	.remove		= ps3flash_remove,
	.shutdown	= ps3flash_remove,
};


static int __init ps3flash_init(void)
{
	return ps3_system_bus_driver_register(&ps3flash);
}

static void __exit ps3flash_exit(void)
{
	ps3_system_bus_driver_unregister(&ps3flash);
}

module_init(ps3flash_init);
module_exit(ps3flash_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver");
MODULE_AUTHOR("Sony Corporation");
MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);