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 #include <linux/mm.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 #include <asm/upa.h> 26 27 static DEFINE_SPINLOCK(flash_lock); 28 static struct { 29 unsigned long read_base; /* Physical read address */ 30 unsigned long write_base; /* Physical write address */ 31 unsigned long read_size; /* Size of read area */ 32 unsigned long write_size; /* Size of write area */ 33 unsigned long busy; /* In use? */ 34 } flash; 35 36 #define FLASH_MINOR 152 37 38 static int 39 flash_mmap(struct file *file, struct vm_area_struct *vma) 40 { 41 unsigned long addr; 42 unsigned long size; 43 44 spin_lock(&flash_lock); 45 if (flash.read_base == flash.write_base) { 46 addr = flash.read_base; 47 size = flash.read_size; 48 } else { 49 if ((vma->vm_flags & VM_READ) && 50 (vma->vm_flags & VM_WRITE)) { 51 spin_unlock(&flash_lock); 52 return -EINVAL; 53 } 54 if (vma->vm_flags & VM_READ) { 55 addr = flash.read_base; 56 size = flash.read_size; 57 } else if (vma->vm_flags & VM_WRITE) { 58 addr = flash.write_base; 59 size = flash.write_size; 60 } else { 61 spin_unlock(&flash_lock); 62 return -ENXIO; 63 } 64 } 65 spin_unlock(&flash_lock); 66 67 if ((vma->vm_pgoff << PAGE_SHIFT) > size) 68 return -ENXIO; 69 addr = vma->vm_pgoff + (addr >> PAGE_SHIFT); 70 71 if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size) 72 size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)); 73 74 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 75 76 if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot)) 77 return -EAGAIN; 78 79 return 0; 80 } 81 82 static long long 83 flash_llseek(struct file *file, long long offset, int origin) 84 { 85 lock_kernel(); 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 unlock_kernel(); 100 return -EINVAL; 101 } 102 unlock_kernel(); 103 return file->f_pos; 104 } 105 106 static ssize_t 107 flash_read(struct file * file, char __user * buf, 108 size_t count, loff_t *ppos) 109 { 110 unsigned long p = file->f_pos; 111 int i; 112 113 if (count > flash.read_size - p) 114 count = flash.read_size - p; 115 116 for (i = 0; i < count; i++) { 117 u8 data = upa_readb(flash.read_base + p + i); 118 if (put_user(data, buf)) 119 return -EFAULT; 120 buf++; 121 } 122 123 file->f_pos += count; 124 return count; 125 } 126 127 static int 128 flash_open(struct inode *inode, struct file *file) 129 { 130 lock_kernel(); 131 if (test_and_set_bit(0, (void *)&flash.busy) != 0) { 132 unlock_kernel(); 133 return -EBUSY; 134 } 135 136 unlock_kernel(); 137 return 0; 138 } 139 140 static int 141 flash_release(struct inode *inode, struct file *file) 142 { 143 spin_lock(&flash_lock); 144 flash.busy = 0; 145 spin_unlock(&flash_lock); 146 147 return 0; 148 } 149 150 static const struct file_operations flash_fops = { 151 /* no write to the Flash, use mmap 152 * and play flash dependent tricks. 153 */ 154 .owner = THIS_MODULE, 155 .llseek = flash_llseek, 156 .read = flash_read, 157 .mmap = flash_mmap, 158 .open = flash_open, 159 .release = flash_release, 160 }; 161 162 static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; 163 164 static int __init flash_init(void) 165 { 166 struct sbus_bus *sbus; 167 struct sbus_dev *sdev = NULL; 168 #ifdef CONFIG_PCI 169 struct linux_ebus *ebus; 170 struct linux_ebus_device *edev = NULL; 171 struct linux_prom_registers regs[2]; 172 int len, nregs; 173 #endif 174 int err; 175 176 for_all_sbusdev(sdev, sbus) { 177 if (!strcmp(sdev->prom_name, "flashprom")) { 178 if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) { 179 flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | 180 (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); 181 flash.read_size = sdev->reg_addrs[0].reg_size; 182 flash.write_base = flash.read_base; 183 flash.write_size = flash.read_size; 184 } else { 185 flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | 186 (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); 187 flash.read_size = sdev->reg_addrs[0].reg_size; 188 flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) | 189 (((unsigned long)sdev->reg_addrs[1].which_io)<<32UL); 190 flash.write_size = sdev->reg_addrs[1].reg_size; 191 } 192 flash.busy = 0; 193 break; 194 } 195 } 196 if (!sdev) { 197 #ifdef CONFIG_PCI 198 const struct linux_prom_registers *ebus_regs; 199 200 for_each_ebus(ebus) { 201 for_each_ebusdev(edev, ebus) { 202 if (!strcmp(edev->prom_node->name, "flashprom")) 203 goto ebus_done; 204 } 205 } 206 ebus_done: 207 if (!edev) 208 return -ENODEV; 209 210 ebus_regs = of_get_property(edev->prom_node, "reg", &len); 211 if (!ebus_regs || (len % sizeof(regs[0])) != 0) { 212 printk("flash: Strange reg property size %d\n", len); 213 return -ENODEV; 214 } 215 216 nregs = len / sizeof(ebus_regs[0]); 217 218 flash.read_base = edev->resource[0].start; 219 flash.read_size = ebus_regs[0].reg_size; 220 221 if (nregs == 1) { 222 flash.write_base = edev->resource[0].start; 223 flash.write_size = ebus_regs[0].reg_size; 224 } else if (nregs == 2) { 225 flash.write_base = edev->resource[1].start; 226 flash.write_size = ebus_regs[1].reg_size; 227 } else { 228 printk("flash: Strange number of regs %d\n", nregs); 229 return -ENODEV; 230 } 231 232 flash.busy = 0; 233 234 #else 235 return -ENODEV; 236 #endif 237 } 238 239 printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n", 240 flash.read_base, flash.read_size, 241 flash.write_base, flash.write_size); 242 243 err = misc_register(&flash_dev); 244 if (err) { 245 printk(KERN_ERR "flash: unable to get misc minor\n"); 246 return err; 247 } 248 249 return 0; 250 } 251 252 static void __exit flash_cleanup(void) 253 { 254 misc_deregister(&flash_dev); 255 } 256 257 module_init(flash_init); 258 module_exit(flash_cleanup); 259 MODULE_LICENSE("GPL"); 260