ps3flash.c 10 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2 3 4 5 6 7 8 9 10
/*
 * PS3 FLASH ROM Storage Driver
 *
 * Copyright (C) 2007 Sony Computer Entertainment Inc.
 * Copyright 2007 Sony Corp.
 */

#include <linux/fs.h>
#include <linux/miscdevice.h>
11
#include <linux/slab.h>
12
#include <linux/uaccess.h>
13
#include <linux/module.h>
14 15 16 17 18 19 20 21 22 23 24 25

#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 */
26 27 28
	u64 chunk_sectors;
	int tag;		/* Start sector of buffer, -1 if invalid */
	bool dirty;
29 30 31 32
};

static struct ps3_storage_device *ps3flash_dev;

33
static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
34
				       u64 start_sector, int write)
35
{
36 37 38
	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,
39 40
					     write);
	if (res) {
41
		dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
42 43 44
			__LINE__, write ? "write" : "read", res);
		return -EIO;
	}
45
	return 0;
46 47
}

48
static int ps3flash_writeback(struct ps3_storage_device *dev)
49
{
50 51
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
	int res;
52

53 54 55
	if (!priv->dirty || priv->tag < 0)
		return 0;

56
	res = ps3flash_read_write_sectors(dev, priv->tag, 1);
57 58
	if (res)
		return res;
59

60 61
	priv->dirty = false;
	return 0;
62 63
}

64
static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
65
{
66 67 68
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
	int res;

69
	if (start_sector == priv->tag)
70 71 72 73 74 75 76 77
		return 0;

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

	priv->tag = -1;

78
	res = ps3flash_read_write_sectors(dev, start_sector, 0);
79 80 81
	if (res)
		return res;

82
	priv->tag = start_sector;
83
	return 0;
84 85 86 87 88
}

static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
{
	struct ps3_storage_device *dev = ps3flash_dev;
89 90
	return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
			dev->regions[dev->region_idx].size*dev->blk_size);
91 92
}

93 94
static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
			     size_t count, loff_t *pos)
95 96
{
	struct ps3_storage_device *dev = ps3flash_dev;
97
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
98
	u64 size, sector, offset;
99
	int res;
100
	size_t remaining, n;
101
	const void *src;
102 103

	dev_dbg(&dev->sbd.core,
104 105
		"%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
		__func__, __LINE__, count, *pos, userbuf, kernelbuf);
106 107 108 109 110 111 112 113 114 115 116 117

	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;
	}

118
	sector = *pos / dev->bounce_size * priv->chunk_sectors;
119
	offset = *pos % dev->bounce_size;
120 121 122

	remaining = count;
	do {
123 124
		n = min_t(u64, remaining, dev->bounce_size - offset);
		src = dev->bounce_buf + offset;
125

126 127
		mutex_lock(&priv->mutex);

128
		res = ps3flash_fetch(dev, sector);
129
		if (res)
130 131 132
			goto fail;

		dev_dbg(&dev->sbd.core,
133 134 135 136
			"%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)) {
137
				res = -EFAULT;
138 139 140 141 142 143 144
				goto fail;
			}
			userbuf += n;
		}
		if (kernelbuf) {
			memcpy(kernelbuf, src, n);
			kernelbuf += n;
145 146 147 148 149 150
		}

		mutex_unlock(&priv->mutex);

		*pos += n;
		remaining -= n;
151
		sector += priv->chunk_sectors;
152 153 154 155 156 157
		offset = 0;
	} while (remaining > 0);

	return count;

fail:
158 159
	mutex_unlock(&priv->mutex);
	return res;
160 161
}

162 163
static ssize_t ps3flash_write(const char __user *userbuf,
			      const void *kernelbuf, size_t count, loff_t *pos)
164 165
{
	struct ps3_storage_device *dev = ps3flash_dev;
166
	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
167 168
	u64 size, sector, offset;
	int res = 0;
169
	size_t remaining, n;
170
	void *dst;
171 172

	dev_dbg(&dev->sbd.core,
173 174
		"%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
		__func__, __LINE__, count, *pos, userbuf, kernelbuf);
175 176 177 178 179 180 181 182 183 184 185 186

	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;
	}

187
	sector = *pos / dev->bounce_size * priv->chunk_sectors;
188 189 190 191
	offset = *pos % dev->bounce_size;

	remaining = count;
	do {
192
		n = min_t(u64, remaining, dev->bounce_size - offset);
193
		dst = dev->bounce_buf + offset;
194

195 196
		mutex_lock(&priv->mutex);

197
		if (n != dev->bounce_size)
198
			res = ps3flash_fetch(dev, sector);
199 200 201 202
		else if (sector != priv->tag)
			res = ps3flash_writeback(dev);
		if (res)
			goto fail;
203 204

		dev_dbg(&dev->sbd.core,
205 206 207 208 209 210 211 212 213 214 215 216
			"%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;
217 218
		}

219 220
		priv->tag = sector;
		priv->dirty = true;
221 222 223 224 225

		mutex_unlock(&priv->mutex);

		*pos += n;
		remaining -= n;
226
		sector += priv->chunk_sectors;
227 228 229 230 231 232 233 234 235 236
		offset = 0;
	} while (remaining > 0);

	return count;

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

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
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)
{
257 258 259 260 261 262 263 264 265 266 267 268 269
	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;
270 271
}

272 273 274 275 276
static int ps3flash_flush(struct file *file, fl_owner_t id)
{
	return ps3flash_writeback(ps3flash_dev);
}

277
static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)
278
{
Al Viro's avatar
Al Viro committed
279
	struct inode *inode = file_inode(file);
280
	int err;
Al Viro's avatar
Al Viro committed
281
	inode_lock(inode);
282
	err = ps3flash_writeback(ps3flash_dev);
Al Viro's avatar
Al Viro committed
283
	inode_unlock(inode);
284
	return err;
285
}
286 287 288 289 290 291 292 293 294 295 296

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,
297
			"%s:%u: tag mismatch, got %llx, expected %llx\n",
298 299 300
			__func__, __LINE__, tag, dev->tag);

	if (res) {
301
		dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
302 303 304 305 306 307 308 309 310 311 312
			__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,
313 314
	.read	= ps3flash_user_read,
	.write	= ps3flash_user_write,
315 316
	.flush	= ps3flash_flush,
	.fsync	= ps3flash_fsync,
317 318 319 320 321
};

static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
	.read	= ps3flash_kernel_read,
	.write	= ps3flash_kernel_write,
322 323 324 325 326 327 328 329
};

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

Bill Pemberton's avatar
Bill Pemberton committed
330
static int ps3flash_probe(struct ps3_system_bus_device *_dev)
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
{
	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;
	}

370
	ps3_system_bus_set_drvdata(&dev->sbd, priv);
371
	mutex_init(&priv->mutex);
372
	priv->tag = -1;
373 374 375

	dev->bounce_size = ps3flash_bounce_buffer.size;
	dev->bounce_buf = ps3flash_bounce_buffer.address;
376
	priv->chunk_sectors = dev->bounce_size / dev->blk_size;
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391

	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);
392 393

	ps3_os_area_flash_register(&ps3flash_kernel_ops);
394 395 396 397 398 399
	return 0;

fail_teardown:
	ps3stor_teardown(dev);
fail_free_priv:
	kfree(priv);
400
	ps3_system_bus_set_drvdata(&dev->sbd, NULL);
401 402 403 404 405 406 407 408 409
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);

410
	ps3_os_area_flash_register(NULL);
411 412
	misc_deregister(&ps3flash_misc);
	ps3stor_teardown(dev);
413 414
	kfree(ps3_system_bus_get_drvdata(&dev->sbd));
	ps3_system_bus_set_drvdata(&dev->sbd, NULL);
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
	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);