1 /* $Id: flash.c,v 1.25 2001/12/21 04:56:16 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/module.h> 8 #include <linux/types.h> 9 #include <linux/errno.h> 10 #include <linux/miscdevice.h> 11 #include <linux/slab.h> 12 #include <linux/fcntl.h> 13 #include <linux/poll.h> 14 #include <linux/init.h> 15 #include <linux/smp_lock.h> 16 #include <linux/spinlock.h> 17 18 #include <asm/system.h> 19 #include <asm/uaccess.h> 20 #include <asm/pgtable.h> 21 #include <asm/io.h> 22 #include <asm/sbus.h> 23 #include <asm/ebus.h> 24 #include <asm/upa.h> 25 26 static DEFINE_SPINLOCK(flash_lock); 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 + (addr >> 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 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 74 75 if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot)) 76 return -EAGAIN; 77 78 return 0; 79 } 80 81 static long long 82 flash_llseek(struct file *file, long long offset, int origin) 83 { 84 lock_kernel(); 85 switch (origin) { 86 case 0: 87 file->f_pos = offset; 88 break; 89 case 1: 90 file->f_pos += offset; 91 if (file->f_pos > flash.read_size) 92 file->f_pos = flash.read_size; 93 break; 94 case 2: 95 file->f_pos = flash.read_size; 96 break; 97 default: 98 unlock_kernel(); 99 return -EINVAL; 100 } 101 unlock_kernel(); 102 return file->f_pos; 103 } 104 105 static ssize_t 106 flash_read(struct file * file, char __user * buf, 107 size_t count, loff_t *ppos) 108 { 109 unsigned long p = file->f_pos; 110 int i; 111 112 if (count > flash.read_size - p) 113 count = flash.read_size - p; 114 115 for (i = 0; i < count; i++) { 116 u8 data = upa_readb(flash.read_base + p + i); 117 if (put_user(data, buf)) 118 return -EFAULT; 119 buf++; 120 } 121 122 file->f_pos += count; 123 return count; 124 } 125 126 static int 127 flash_open(struct inode *inode, struct file *file) 128 { 129 if (test_and_set_bit(0, (void *)&flash.busy) != 0) 130 return -EBUSY; 131 132 return 0; 133 } 134 135 static int 136 flash_release(struct inode *inode, struct file *file) 137 { 138 spin_lock(&flash_lock); 139 flash.busy = 0; 140 spin_unlock(&flash_lock); 141 142 return 0; 143 } 144 145 static struct file_operations flash_fops = { 146 /* no write to the Flash, use mmap 147 * and play flash dependent tricks. 148 */ 149 .owner = THIS_MODULE, 150 .llseek = flash_llseek, 151 .read = flash_read, 152 .mmap = flash_mmap, 153 .open = flash_open, 154 .release = flash_release, 155 }; 156 157 static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; 158 159 static int __init flash_init(void) 160 { 161 struct sbus_bus *sbus; 162 struct sbus_dev *sdev = NULL; 163 #ifdef CONFIG_PCI 164 struct linux_ebus *ebus; 165 struct linux_ebus_device *edev = NULL; 166 struct linux_prom_registers regs[2]; 167 int len, nregs; 168 #endif 169 int err; 170 171 for_all_sbusdev(sdev, sbus) { 172 if (!strcmp(sdev->prom_name, "flashprom")) { 173 if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) { 174 flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | 175 (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); 176 flash.read_size = sdev->reg_addrs[0].reg_size; 177 flash.write_base = flash.read_base; 178 flash.write_size = flash.read_size; 179 } else { 180 flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | 181 (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); 182 flash.read_size = sdev->reg_addrs[0].reg_size; 183 flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) | 184 (((unsigned long)sdev->reg_addrs[1].which_io)<<32UL); 185 flash.write_size = sdev->reg_addrs[1].reg_size; 186 } 187 flash.busy = 0; 188 break; 189 } 190 } 191 if (!sdev) { 192 #ifdef CONFIG_PCI 193 struct linux_prom_registers *ebus_regs; 194 195 for_each_ebus(ebus) { 196 for_each_ebusdev(edev, ebus) { 197 if (!strcmp(edev->prom_node->name, "flashprom")) 198 goto ebus_done; 199 } 200 } 201 ebus_done: 202 if (!edev) 203 return -ENODEV; 204 205 ebus_regs = of_get_property(edev->prom_node, "reg", &len); 206 if (!ebus_regs || (len % sizeof(regs[0])) != 0) { 207 printk("flash: Strange reg property size %d\n", len); 208 return -ENODEV; 209 } 210 211 nregs = len / sizeof(ebus_regs[0]); 212 213 flash.read_base = edev->resource[0].start; 214 flash.read_size = ebus_regs[0].reg_size; 215 216 if (nregs == 1) { 217 flash.write_base = edev->resource[0].start; 218 flash.write_size = ebus_regs[0].reg_size; 219 } else if (nregs == 2) { 220 flash.write_base = edev->resource[1].start; 221 flash.write_size = ebus_regs[1].reg_size; 222 } else { 223 printk("flash: Strange number of regs %d\n", nregs); 224 return -ENODEV; 225 } 226 227 flash.busy = 0; 228 229 #else 230 return -ENODEV; 231 #endif 232 } 233 234 printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n", 235 flash.read_base, flash.read_size, 236 flash.write_base, flash.write_size); 237 238 err = misc_register(&flash_dev); 239 if (err) { 240 printk(KERN_ERR "flash: unable to get misc minor\n"); 241 return err; 242 } 243 244 return 0; 245 } 246 247 static void __exit flash_cleanup(void) 248 { 249 misc_deregister(&flash_dev); 250 } 251 252 module_init(flash_init); 253 module_exit(flash_cleanup); 254 MODULE_LICENSE("GPL"); 255