1/* $Id: flash.c,v 1.1.1.1 2008/10/15 03:26:47 james26_jang 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 26static spinlock_t flash_lock = SPIN_LOCK_UNLOCKED; 27static 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 37static int 38flash_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 83static long long 84flash_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 104static ssize_t 105flash_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 125static int 126flash_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 134static int 135flash_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 144static 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 156static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; 157 158EXPORT_NO_SYMBOLS; 159 160static 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 243static void __exit flash_cleanup(void) 244{ 245 misc_deregister(&flash_dev); 246} 247 248module_init(flash_init); 249module_exit(flash_cleanup); 250MODULE_LICENSE("GPL"); 251