brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 23871cd Raw
446 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PS3 FLASH ROM Storage Driver4 *5 * Copyright (C) 2007 Sony Computer Entertainment Inc.6 * Copyright 2007 Sony Corp.7 */8 9#include <linux/fs.h>10#include <linux/miscdevice.h>11#include <linux/slab.h>12#include <linux/uaccess.h>13#include <linux/module.h>14 15#include <asm/lv1call.h>16#include <asm/ps3stor.h>17 18 19#define DEVICE_NAME		"ps3flash"20 21#define FLASH_BLOCK_SIZE	(256*1024)22 23 24struct ps3flash_private {25	struct mutex mutex;	/* Bounce buffer mutex */26	u64 chunk_sectors;27	int tag;		/* Start sector of buffer, -1 if invalid */28	bool dirty;29};30 31static struct ps3_storage_device *ps3flash_dev;32 33static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,34				       u64 start_sector, int write)35{36	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);37	u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,38					     start_sector, priv->chunk_sectors,39					     write);40	if (res) {41		dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,42			__LINE__, write ? "write" : "read", res);43		return -EIO;44	}45	return 0;46}47 48static int ps3flash_writeback(struct ps3_storage_device *dev)49{50	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);51	int res;52 53	if (!priv->dirty || priv->tag < 0)54		return 0;55 56	res = ps3flash_read_write_sectors(dev, priv->tag, 1);57	if (res)58		return res;59 60	priv->dirty = false;61	return 0;62}63 64static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)65{66	struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);67	int res;68 69	if (start_sector == priv->tag)70		return 0;71 72	res = ps3flash_writeback(dev);73	if (res)74		return res;75 76	priv->tag = -1;77 78	res = ps3flash_read_write_sectors(dev, start_sector, 0);79	if (res)80		return res;81 82	priv->tag = start_sector;83	return 0;84}85 86static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)87{88	struct ps3_storage_device *dev = ps3flash_dev;89	return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,90			dev->regions[dev->region_idx].size*dev->blk_size);91}92 93static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,94			     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		"%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",105		__func__, __LINE__, count, *pos, userbuf, kernelbuf);106 107	size = dev->regions[dev->region_idx].size*dev->blk_size;108	if (*pos >= size || !count)109		return 0;110 111	if (*pos + count > size) {112		dev_dbg(&dev->sbd.core,113			"%s:%u Truncating count from %zu to %llu\n", __func__,114			__LINE__, count, size - *pos);115		count = size - *pos;116	}117 118	sector = *pos / dev->bounce_size * priv->chunk_sectors;119	offset = *pos % dev->bounce_size;120 121	remaining = count;122	do {123		n = min_t(u64, remaining, dev->bounce_size - offset);124		src = dev->bounce_buf + offset;125 126		mutex_lock(&priv->mutex);127 128		res = ps3flash_fetch(dev, sector);129		if (res)130			goto fail;131 132		dev_dbg(&dev->sbd.core,133			"%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n",134			__func__, __LINE__, n, src, userbuf, kernelbuf);135		if (userbuf) {136			if (copy_to_user(userbuf, src, n)) {137				res = -EFAULT;138				goto fail;139			}140			userbuf += n;141		}142		if (kernelbuf) {143			memcpy(kernelbuf, src, n);144			kernelbuf += n;145		}146 147		mutex_unlock(&priv->mutex);148 149		*pos += n;150		remaining -= n;151		sector += priv->chunk_sectors;152		offset = 0;153	} while (remaining > 0);154 155	return count;156 157fail:158	mutex_unlock(&priv->mutex);159	return res;160}161 162static ssize_t ps3flash_write(const char __user *userbuf,163			      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	u64 size, sector, offset;168	int res = 0;169	size_t remaining, n;170	void *dst;171 172	dev_dbg(&dev->sbd.core,173		"%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",174		__func__, __LINE__, count, *pos, userbuf, kernelbuf);175 176	size = dev->regions[dev->region_idx].size*dev->blk_size;177	if (*pos >= size || !count)178		return 0;179 180	if (*pos + count > size) {181		dev_dbg(&dev->sbd.core,182			"%s:%u Truncating count from %zu to %llu\n", __func__,183			__LINE__, count, size - *pos);184		count = size - *pos;185	}186 187	sector = *pos / dev->bounce_size * priv->chunk_sectors;188	offset = *pos % dev->bounce_size;189 190	remaining = count;191	do {192		n = min_t(u64, remaining, dev->bounce_size - offset);193		dst = dev->bounce_buf + offset;194 195		mutex_lock(&priv->mutex);196 197		if (n != dev->bounce_size)198			res = ps3flash_fetch(dev, sector);199		else if (sector != priv->tag)200			res = ps3flash_writeback(dev);201		if (res)202			goto fail;203 204		dev_dbg(&dev->sbd.core,205			"%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n",206			__func__, __LINE__, n, userbuf, kernelbuf, dst);207		if (userbuf) {208			if (copy_from_user(dst, userbuf, n)) {209				res = -EFAULT;210				goto fail;211			}212			userbuf += n;213		}214		if (kernelbuf) {215			memcpy(dst, kernelbuf, n);216			kernelbuf += n;217		}218 219		priv->tag = sector;220		priv->dirty = true;221 222		mutex_unlock(&priv->mutex);223 224		*pos += n;225		remaining -= n;226		sector += priv->chunk_sectors;227		offset = 0;228	} while (remaining > 0);229 230	return count;231 232fail:233	mutex_unlock(&priv->mutex);234	return res;235}236 237static ssize_t ps3flash_user_read(struct file *file, char __user *buf,238				  size_t count, loff_t *pos)239{240	return ps3flash_read(buf, NULL, count, pos);241}242 243static ssize_t ps3flash_user_write(struct file *file, const char __user *buf,244				   size_t count, loff_t *pos)245{246	return ps3flash_write(buf, NULL, count, pos);247}248 249static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)250{251	return ps3flash_read(NULL, buf, count, &pos);252}253 254static ssize_t ps3flash_kernel_write(const void *buf, size_t count,255				     loff_t pos)256{257	ssize_t res;258	int wb;259 260	res = ps3flash_write(NULL, buf, count, &pos);261	if (res < 0)262		return res;263 264	/* Make kernel writes synchronous */265	wb = ps3flash_writeback(ps3flash_dev);266	if (wb)267		return wb;268 269	return res;270}271 272static int ps3flash_flush(struct file *file, fl_owner_t id)273{274	return ps3flash_writeback(ps3flash_dev);275}276 277static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)278{279	struct inode *inode = file_inode(file);280	int err;281	inode_lock(inode);282	err = ps3flash_writeback(ps3flash_dev);283	inode_unlock(inode);284	return err;285}286 287static irqreturn_t ps3flash_interrupt(int irq, void *data)288{289	struct ps3_storage_device *dev = data;290	int res;291	u64 tag, status;292 293	res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);294 295	if (tag != dev->tag)296		dev_err(&dev->sbd.core,297			"%s:%u: tag mismatch, got %llx, expected %llx\n",298			__func__, __LINE__, tag, dev->tag);299 300	if (res) {301		dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",302			__func__, __LINE__, res, status);303	} else {304		dev->lv1_status = status;305		complete(&dev->done);306	}307	return IRQ_HANDLED;308}309 310static const struct file_operations ps3flash_fops = {311	.owner	= THIS_MODULE,312	.llseek	= ps3flash_llseek,313	.read	= ps3flash_user_read,314	.write	= ps3flash_user_write,315	.flush	= ps3flash_flush,316	.fsync	= ps3flash_fsync,317};318 319static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {320	.read	= ps3flash_kernel_read,321	.write	= ps3flash_kernel_write,322};323 324static struct miscdevice ps3flash_misc = {325	.minor	= MISC_DYNAMIC_MINOR,326	.name	= DEVICE_NAME,327	.fops	= &ps3flash_fops,328};329 330static int ps3flash_probe(struct ps3_system_bus_device *_dev)331{332	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);333	struct ps3flash_private *priv;334	int error;335	unsigned long tmp;336 337	tmp = dev->regions[dev->region_idx].start*dev->blk_size;338	if (tmp % FLASH_BLOCK_SIZE) {339		dev_err(&dev->sbd.core,340			"%s:%u region start %lu is not aligned\n", __func__,341			__LINE__, tmp);342		return -EINVAL;343	}344	tmp = dev->regions[dev->region_idx].size*dev->blk_size;345	if (tmp % FLASH_BLOCK_SIZE) {346		dev_err(&dev->sbd.core,347			"%s:%u region size %lu is not aligned\n", __func__,348			__LINE__, tmp);349		return -EINVAL;350	}351 352	/* use static buffer, kmalloc cannot allocate 256 KiB */353	if (!ps3flash_bounce_buffer.address)354		return -ENODEV;355 356	if (ps3flash_dev) {357		dev_err(&dev->sbd.core,358			"Only one FLASH device is supported\n");359		return -EBUSY;360	}361 362	ps3flash_dev = dev;363 364	priv = kzalloc(sizeof(*priv), GFP_KERNEL);365	if (!priv) {366		error = -ENOMEM;367		goto fail;368	}369 370	ps3_system_bus_set_drvdata(&dev->sbd, priv);371	mutex_init(&priv->mutex);372	priv->tag = -1;373 374	dev->bounce_size = ps3flash_bounce_buffer.size;375	dev->bounce_buf = ps3flash_bounce_buffer.address;376	priv->chunk_sectors = dev->bounce_size / dev->blk_size;377 378	error = ps3stor_setup(dev, ps3flash_interrupt);379	if (error)380		goto fail_free_priv;381 382	ps3flash_misc.parent = &dev->sbd.core;383	error = misc_register(&ps3flash_misc);384	if (error) {385		dev_err(&dev->sbd.core, "%s:%u: misc_register failed %d\n",386			__func__, __LINE__, error);387		goto fail_teardown;388	}389 390	dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",391		 __func__, __LINE__, ps3flash_misc.minor);392 393	ps3_os_area_flash_register(&ps3flash_kernel_ops);394	return 0;395 396fail_teardown:397	ps3stor_teardown(dev);398fail_free_priv:399	kfree(priv);400	ps3_system_bus_set_drvdata(&dev->sbd, NULL);401fail:402	ps3flash_dev = NULL;403	return error;404}405 406static void ps3flash_remove(struct ps3_system_bus_device *_dev)407{408	struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);409 410	ps3_os_area_flash_register(NULL);411	misc_deregister(&ps3flash_misc);412	ps3stor_teardown(dev);413	kfree(ps3_system_bus_get_drvdata(&dev->sbd));414	ps3_system_bus_set_drvdata(&dev->sbd, NULL);415	ps3flash_dev = NULL;416}417 418 419static struct ps3_system_bus_driver ps3flash = {420	.match_id	= PS3_MATCH_ID_STOR_FLASH,421	.core.name	= DEVICE_NAME,422	.core.owner	= THIS_MODULE,423	.probe		= ps3flash_probe,424	.remove		= ps3flash_remove,425	.shutdown	= ps3flash_remove,426};427 428 429static int __init ps3flash_init(void)430{431	return ps3_system_bus_driver_register(&ps3flash);432}433 434static void __exit ps3flash_exit(void)435{436	ps3_system_bus_driver_unregister(&ps3flash);437}438 439module_init(ps3flash_init);440module_exit(ps3flash_exit);441 442MODULE_LICENSE("GPL");443MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver");444MODULE_AUTHOR("Sony Corporation");445MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);446