1a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller/* flash.c: Allow mmap access to the OBP Flash, for OBP updates.
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fcntl.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/poll.h>
12a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann#include <linux/mutex.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
148a73709ecc6a972c94e6ff9c0cc639f8f38b9151Horst H. von Brand#include <linux/mm.h>
15a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller#include <linux/of.h>
16a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller#include <linux/of_device.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/pgtable.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/upa.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmannstatic DEFINE_MUTEX(flash_mutex);
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEFINE_SPINLOCK(flash_lock);
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct {
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long read_base;	/* Physical read address */
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long write_base;	/* Physical write address */
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long read_size;	/* Size of read area */
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long write_size;	/* Size of write area */
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long busy;		/* In use? */
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} flash;
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define FLASH_MINOR	152
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsflash_mmap(struct file *file, struct vm_area_struct *vma)
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long addr;
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long size;
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&flash_lock);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (flash.read_base == flash.write_base) {
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		addr = flash.read_base;
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size = flash.read_size;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((vma->vm_flags & VM_READ) &&
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (vma->vm_flags & VM_WRITE)) {
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_unlock(&flash_lock);
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (vma->vm_flags & VM_READ) {
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			addr = flash.read_base;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size = flash.read_size;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else if (vma->vm_flags & VM_WRITE) {
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			addr = flash.write_base;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size = flash.write_size;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_unlock(&flash_lock);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENXIO;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&flash_lock);
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((vma->vm_pgoff << PAGE_SHIFT) > size)
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	addr = vma->vm_pgoff + (addr >> PAGE_SHIFT);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size)
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT));
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7114778d9072e53d2171f66ffd9657daff41acfaedDavid S. Miller	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot))
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EAGAIN;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic long long
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsflash_llseek(struct file *file, long long offset, int origin)
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
82a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann	mutex_lock(&flash_mutex);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (origin) {
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0:
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			file->f_pos = offset;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 1:
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			file->f_pos += offset;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (file->f_pos > flash.read_size)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				file->f_pos = flash.read_size;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 2:
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			file->f_pos = flash.read_size;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
96a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann			mutex_unlock(&flash_mutex);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
99a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann	mutex_unlock(&flash_mutex);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return file->f_pos;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsflash_read(struct file * file, char __user * buf,
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   size_t count, loff_t *ppos)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
107be94bbb5db4de0f3a2a5405511ea3ebea261f2c8Jan Blunck	loff_t p = *ppos;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
109be94bbb5db4de0f3a2a5405511ea3ebea261f2c8Jan Blunck
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count > flash.read_size - p)
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count = flash.read_size - p;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < count; i++) {
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u8 data = upa_readb(flash.read_base + p + i);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(data, buf))
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf++;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
120be94bbb5db4de0f3a2a5405511ea3ebea261f2c8Jan Blunck	*ppos += count;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsflash_open(struct inode *inode, struct file *file)
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
127a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann	mutex_lock(&flash_mutex);
12878abb6ac919cee123e632d833a42d0312ccb2b0dArnd Bergmann	if (test_and_set_bit(0, (void *)&flash.busy) != 0) {
129a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann		mutex_unlock(&flash_mutex);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
13178abb6ac919cee123e632d833a42d0312ccb2b0dArnd Bergmann	}
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
133a3108ca2323dec0f6321c3f7706cdaed51f694eaArnd Bergmann	mutex_unlock(&flash_mutex);
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsflash_release(struct inode *inode, struct file *file)
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&flash_lock);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flash.busy = 0;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&flash_lock);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14700977a59b951207d38380c75f03a36829950265cArjan van de Venstatic const struct file_operations flash_fops = {
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* no write to the Flash, use mmap
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * and play flash dependent tricks.
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner =	THIS_MODULE,
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek =	flash_llseek,
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read =		flash_read,
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.mmap =		flash_mmap,
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =		flash_open,
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release =	flash_release,
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161082a2004db27e16ee9a5b1234e6ab219ea29d693Greg Kroah-Hartmanstatic int flash_probe(struct platform_device *op)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16361c7a080a5a061c976988fd4b844dfb468dda255Grant Likely	struct device_node *dp = op->dev.of_node;
164a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	struct device_node *parent;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
166a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	parent = dp->parent;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
168a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	if (strcmp(parent->name, "sbus") &&
169a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	    strcmp(parent->name, "sbi") &&
170a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	    strcmp(parent->name, "ebus"))
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
172a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller
173a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	flash.read_base = op->resource[0].start;
174a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	flash.read_size = resource_size(&op->resource[0]);
175a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	if (op->resource[1].flags) {
176a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller		flash.write_base = op->resource[1].start;
177a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller		flash.write_size = resource_size(&op->resource[1]);
178a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	} else {
179a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller		flash.write_base = op->resource[0].start;
180a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller		flash.write_size = resource_size(&op->resource[0]);
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
182a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	flash.busy = 0;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
184a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	printk(KERN_INFO "%s: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n",
18561c7a080a5a061c976988fd4b844dfb468dda255Grant Likely	       op->dev.of_node->full_name,
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       flash.read_base, flash.read_size,
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       flash.write_base, flash.write_size);
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
189a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	return misc_register(&flash_dev);
190a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller}
191a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller
192082a2004db27e16ee9a5b1234e6ab219ea29d693Greg Kroah-Hartmanstatic int flash_remove(struct platform_device *op)
193a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller{
194a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	misc_deregister(&flash_dev);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
199fd098316ef533e8441576f020ead4beab93154ceDavid S. Millerstatic const struct of_device_id flash_match[] = {
200a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	{
201a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller		.name = "flashprom",
202a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	},
203a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	{},
204a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller};
205a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. MillerMODULE_DEVICE_TABLE(of, flash_match);
206a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller
2074ebb24f707187196937607c60810d42f7112d7aaGrant Likelystatic struct platform_driver flash_driver = {
2084018294b53d1dae026880e45f174c1cc63b5d435Grant Likely	.driver = {
2094018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.name = "flash",
2104018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.owner = THIS_MODULE,
2114018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.of_match_table = flash_match,
2124018294b53d1dae026880e45f174c1cc63b5d435Grant Likely	},
213a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller	.probe		= flash_probe,
214082a2004db27e16ee9a5b1234e6ab219ea29d693Greg Kroah-Hartman	.remove		= flash_remove,
215a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller};
216a9540d34229c0ec1fceb9a9d1e41ea45d016044dDavid S. Miller
217dbf2b92d54e73e4a2524b90d29bd498ecc4aa593Axel Linmodule_platform_driver(flash_driver);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
220