File: /usr/src/linux/drivers/sbus/char/flash.c

1     /* $Id: flash.c,v 1.23 2001/03/02 06:32:40 davem Exp $
2      * flash.c: Allow mmap access to the OBP Flash, for OBP updates.
3      *
4      * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
5      */
6     
7     #include <linux/config.h>
8     #include <linux/module.h>
9     #include <linux/types.h>
10     #include <linux/errno.h>
11     #include <linux/miscdevice.h>
12     #include <linux/slab.h>
13     #include <linux/fcntl.h>
14     #include <linux/poll.h>
15     #include <linux/init.h>
16     #include <linux/smp_lock.h>
17     #include <linux/spinlock.h>
18     
19     #include <asm/system.h>
20     #include <asm/uaccess.h>
21     #include <asm/pgtable.h>
22     #include <asm/io.h>
23     #include <asm/sbus.h>
24     #include <asm/ebus.h>
25     
26     static spinlock_t flash_lock = SPIN_LOCK_UNLOCKED;
27     static struct {
28     	unsigned long read_base;	/* Physical read address */
29     	unsigned long write_base;	/* Physical write address */
30     	unsigned long read_size;	/* Size of read area */
31     	unsigned long write_size;	/* Size of write area */
32     	unsigned long busy;		/* In use? */
33     } flash;
34     
35     #define FLASH_MINOR	152
36     
37     static int
38     flash_mmap(struct file *file, struct vm_area_struct *vma)
39     {
40     	unsigned long addr;
41     	unsigned long size;
42     
43     	spin_lock(&flash_lock);
44     	if (flash.read_base == flash.write_base) {
45     		addr = flash.read_base;
46     		size = flash.read_size;
47     	} else {
48     		if ((vma->vm_flags & VM_READ) &&
49     		    (vma->vm_flags & VM_WRITE)) {
50     			spin_unlock(&flash_lock);
51     			return -EINVAL;
52     		}
53     		if (vma->vm_flags & VM_READ) {
54     			addr = flash.read_base;
55     			size = flash.read_size;
56     		} else if (vma->vm_flags & VM_WRITE) {
57     			addr = flash.write_base;
58     			size = flash.write_size;
59     		} else {
60     			spin_unlock(&flash_lock);
61     			return -ENXIO;
62     		}
63     	}
64     	spin_unlock(&flash_lock);
65     
66     	if ((vma->vm_pgoff << PAGE_SHIFT) > size)
67     		return -ENXIO;
68     	addr += (vma->vm_pgoff << PAGE_SHIFT);
69     
70     	if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size)
71     		size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT));
72     
73     	pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE);
74     	pgprot_val(vma->vm_page_prot) |= _PAGE_E;
75     	vma->vm_flags |= (VM_SHM | VM_LOCKED);
76     
77     	if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot))
78     		return -EAGAIN;
79     		
80     	return 0;
81     }
82     
83     static long long
84     flash_llseek(struct file *file, long long offset, int origin)
85     {
86     	switch (origin) {
87     		case 0:
88     			file->f_pos = offset;
89     			break;
90     		case 1:
91     			file->f_pos += offset;
92     			if (file->f_pos > flash.read_size)
93     				file->f_pos = flash.read_size;
94     			break;
95     		case 2:
96     			file->f_pos = flash.read_size;
97     			break;
98     		default:
99     			return -EINVAL;
100     	}
101     	return file->f_pos;
102     }
103     
104     static ssize_t
105     flash_read(struct file * file, char * buf,
106     	   size_t count, loff_t *ppos)
107     {
108     	unsigned long p = file->f_pos;
109     	int i;
110     	
111     	if (count > flash.read_size - p)
112     		count = flash.read_size - p;
113     
114     	for (i = 0; i < count; i++) {
115     		u8 data = readb(flash.read_base + p + i);
116     		if (put_user(data, buf))
117     			return -EFAULT;
118     		buf++;
119     	}
120     
121     	file->f_pos += count;
122     	return count;
123     }
124     
125     static int
126     flash_open(struct inode *inode, struct file *file)
127     {
128     	if (test_and_set_bit(0, (void *)&flash.busy) != 0)
129     		return -EBUSY;
130     
131     	return 0;
132     }
133     
134     static int
135     flash_release(struct inode *inode, struct file *file)
136     {
137     	spin_lock(&flash_lock);
138     	flash.busy = 0;
139     	spin_unlock(&flash_lock);
140     
141     	return 0;
142     }
143     
144     static struct file_operations flash_fops = {
145     	/* no write to the Flash, use mmap
146     	 * and play flash dependent tricks.
147     	 */
148     	owner:		THIS_MODULE,
149     	llseek:		flash_llseek,
150     	read:		flash_read,
151     	mmap:		flash_mmap,
152     	open:		flash_open,
153     	release:	flash_release,
154     };
155     
156     static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
157     
158     EXPORT_NO_SYMBOLS;
159     
160     static int __init flash_init(void)
161     {
162     	struct sbus_bus *sbus;
163     	struct sbus_dev *sdev = 0;
164     	struct linux_ebus *ebus;
165     	struct linux_ebus_device *edev = 0;
166     	struct linux_prom_registers regs[2];
167     	int len, err, nregs;
168     
169     	for_all_sbusdev(sdev, sbus) {
170     		if (!strcmp(sdev->prom_name, "flashprom")) {
171     			if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) {
172     				flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) |
173     					(((unsigned long)sdev->reg_addrs[0].which_io)<<32UL);
174     				flash.read_size = sdev->reg_addrs[0].reg_size;
175     				flash.write_base = flash.read_base;
176     				flash.write_size = flash.read_size;
177     			} else {
178     				flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) |
179     					(((unsigned long)sdev->reg_addrs[0].which_io)<<32UL);
180     				flash.read_size = sdev->reg_addrs[0].reg_size;
181     				flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) |
182     					(((unsigned long)sdev->reg_addrs[1].which_io)<<32UL);
183     				flash.write_size = sdev->reg_addrs[1].reg_size;
184     			}
185     			flash.busy = 0;
186     			break;
187     		}
188     	}
189     	if (!sdev) {
190     #ifdef CONFIG_PCI
191     		for_each_ebus(ebus) {
192     			for_each_ebusdev(edev, ebus) {
193     				if (!strcmp(edev->prom_name, "flashprom"))
194     					goto ebus_done;
195     			}
196     		}
197     	ebus_done:
198     		if (!edev)
199     			return -ENODEV;
200     
201     		len = prom_getproperty(edev->prom_node, "reg", (void *)regs, sizeof(regs));
202     		if ((len % sizeof(regs[0])) != 0) {
203     			printk("flash: Strange reg property size %d\n", len);
204     			return -ENODEV;
205     		}
206     
207     		nregs = len / sizeof(regs[0]);
208     
209     		flash.read_base = edev->resource[0].start;
210     		flash.read_size = regs[0].reg_size;
211     
212     		if (nregs == 1) {
213     			flash.write_base = edev->resource[0].start;
214     			flash.write_size = regs[0].reg_size;
215     		} else if (nregs == 2) {
216     			flash.write_base = edev->resource[1].start;
217     			flash.write_size = regs[1].reg_size;
218     		} else {
219     			printk("flash: Strange number of regs %d\n", nregs);
220     			return -ENODEV;
221     		}
222     
223     		flash.busy = 0;
224     
225     #else
226     		return -ENODEV;
227     #endif
228     	}
229     
230     	printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n",
231     	       flash.read_base, flash.read_size,
232     	       flash.write_base, flash.write_size);
233     
234     	err = misc_register(&flash_dev);
235     	if (err) {
236     		printk(KERN_ERR "flash: unable to get misc minor\n");
237     		return err;
238     	}
239     
240     	return 0;
241     }
242     
243     static void __exit flash_cleanup(void)
244     {
245     	misc_deregister(&flash_dev);
246     }
247     
248     module_init(flash_init);
249     module_exit(flash_cleanup);
250