11da177e4SLinus Torvalds /* $Id: flash.c,v 1.25 2001/12/21 04:56:16 davem Exp $ 21da177e4SLinus Torvalds * flash.c: Allow mmap access to the OBP Flash, for OBP updates. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds #include <linux/config.h> 81da177e4SLinus Torvalds #include <linux/module.h> 91da177e4SLinus Torvalds #include <linux/types.h> 101da177e4SLinus Torvalds #include <linux/errno.h> 111da177e4SLinus Torvalds #include <linux/miscdevice.h> 121da177e4SLinus Torvalds #include <linux/slab.h> 131da177e4SLinus Torvalds #include <linux/fcntl.h> 141da177e4SLinus Torvalds #include <linux/poll.h> 151da177e4SLinus Torvalds #include <linux/init.h> 161da177e4SLinus Torvalds #include <linux/smp_lock.h> 171da177e4SLinus Torvalds #include <linux/spinlock.h> 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds #include <asm/system.h> 201da177e4SLinus Torvalds #include <asm/uaccess.h> 211da177e4SLinus Torvalds #include <asm/pgtable.h> 221da177e4SLinus Torvalds #include <asm/io.h> 231da177e4SLinus Torvalds #include <asm/sbus.h> 241da177e4SLinus Torvalds #include <asm/ebus.h> 251da177e4SLinus Torvalds #include <asm/upa.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds static DEFINE_SPINLOCK(flash_lock); 281da177e4SLinus Torvalds static struct { 291da177e4SLinus Torvalds unsigned long read_base; /* Physical read address */ 301da177e4SLinus Torvalds unsigned long write_base; /* Physical write address */ 311da177e4SLinus Torvalds unsigned long read_size; /* Size of read area */ 321da177e4SLinus Torvalds unsigned long write_size; /* Size of write area */ 331da177e4SLinus Torvalds unsigned long busy; /* In use? */ 341da177e4SLinus Torvalds } flash; 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds #define FLASH_MINOR 152 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds static int 391da177e4SLinus Torvalds flash_mmap(struct file *file, struct vm_area_struct *vma) 401da177e4SLinus Torvalds { 411da177e4SLinus Torvalds unsigned long addr; 421da177e4SLinus Torvalds unsigned long size; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds spin_lock(&flash_lock); 451da177e4SLinus Torvalds if (flash.read_base == flash.write_base) { 461da177e4SLinus Torvalds addr = flash.read_base; 471da177e4SLinus Torvalds size = flash.read_size; 481da177e4SLinus Torvalds } else { 491da177e4SLinus Torvalds if ((vma->vm_flags & VM_READ) && 501da177e4SLinus Torvalds (vma->vm_flags & VM_WRITE)) { 511da177e4SLinus Torvalds spin_unlock(&flash_lock); 521da177e4SLinus Torvalds return -EINVAL; 531da177e4SLinus Torvalds } 541da177e4SLinus Torvalds if (vma->vm_flags & VM_READ) { 551da177e4SLinus Torvalds addr = flash.read_base; 561da177e4SLinus Torvalds size = flash.read_size; 571da177e4SLinus Torvalds } else if (vma->vm_flags & VM_WRITE) { 581da177e4SLinus Torvalds addr = flash.write_base; 591da177e4SLinus Torvalds size = flash.write_size; 601da177e4SLinus Torvalds } else { 611da177e4SLinus Torvalds spin_unlock(&flash_lock); 621da177e4SLinus Torvalds return -ENXIO; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds spin_unlock(&flash_lock); 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds if ((vma->vm_pgoff << PAGE_SHIFT) > size) 681da177e4SLinus Torvalds return -ENXIO; 691da177e4SLinus Torvalds addr = vma->vm_pgoff + (addr >> PAGE_SHIFT); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size) 721da177e4SLinus Torvalds size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)); 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds vma->vm_flags |= (VM_SHM | VM_LOCKED); 7514778d90SDavid S. Miller vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot)) 781da177e4SLinus Torvalds return -EAGAIN; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds return 0; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static long long 841da177e4SLinus Torvalds flash_llseek(struct file *file, long long offset, int origin) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds lock_kernel(); 871da177e4SLinus Torvalds switch (origin) { 881da177e4SLinus Torvalds case 0: 891da177e4SLinus Torvalds file->f_pos = offset; 901da177e4SLinus Torvalds break; 911da177e4SLinus Torvalds case 1: 921da177e4SLinus Torvalds file->f_pos += offset; 931da177e4SLinus Torvalds if (file->f_pos > flash.read_size) 941da177e4SLinus Torvalds file->f_pos = flash.read_size; 951da177e4SLinus Torvalds break; 961da177e4SLinus Torvalds case 2: 971da177e4SLinus Torvalds file->f_pos = flash.read_size; 981da177e4SLinus Torvalds break; 991da177e4SLinus Torvalds default: 1001da177e4SLinus Torvalds unlock_kernel(); 1011da177e4SLinus Torvalds return -EINVAL; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds unlock_kernel(); 1041da177e4SLinus Torvalds return file->f_pos; 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds static ssize_t 1081da177e4SLinus Torvalds flash_read(struct file * file, char __user * buf, 1091da177e4SLinus Torvalds size_t count, loff_t *ppos) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds unsigned long p = file->f_pos; 1121da177e4SLinus Torvalds int i; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds if (count > flash.read_size - p) 1151da177e4SLinus Torvalds count = flash.read_size - p; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds for (i = 0; i < count; i++) { 1181da177e4SLinus Torvalds u8 data = upa_readb(flash.read_base + p + i); 1191da177e4SLinus Torvalds if (put_user(data, buf)) 1201da177e4SLinus Torvalds return -EFAULT; 1211da177e4SLinus Torvalds buf++; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds file->f_pos += count; 1251da177e4SLinus Torvalds return count; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds static int 1291da177e4SLinus Torvalds flash_open(struct inode *inode, struct file *file) 1301da177e4SLinus Torvalds { 1311da177e4SLinus Torvalds if (test_and_set_bit(0, (void *)&flash.busy) != 0) 1321da177e4SLinus Torvalds return -EBUSY; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds return 0; 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds static int 1381da177e4SLinus Torvalds flash_release(struct inode *inode, struct file *file) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds spin_lock(&flash_lock); 1411da177e4SLinus Torvalds flash.busy = 0; 1421da177e4SLinus Torvalds spin_unlock(&flash_lock); 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds return 0; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static struct file_operations flash_fops = { 1481da177e4SLinus Torvalds /* no write to the Flash, use mmap 1491da177e4SLinus Torvalds * and play flash dependent tricks. 1501da177e4SLinus Torvalds */ 1511da177e4SLinus Torvalds .owner = THIS_MODULE, 1521da177e4SLinus Torvalds .llseek = flash_llseek, 1531da177e4SLinus Torvalds .read = flash_read, 1541da177e4SLinus Torvalds .mmap = flash_mmap, 1551da177e4SLinus Torvalds .open = flash_open, 1561da177e4SLinus Torvalds .release = flash_release, 1571da177e4SLinus Torvalds }; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static int __init flash_init(void) 1621da177e4SLinus Torvalds { 1631da177e4SLinus Torvalds struct sbus_bus *sbus; 1641da177e4SLinus Torvalds struct sbus_dev *sdev = NULL; 1651da177e4SLinus Torvalds #ifdef CONFIG_PCI 1661da177e4SLinus Torvalds struct linux_ebus *ebus; 1671da177e4SLinus Torvalds struct linux_ebus_device *edev = NULL; 1681da177e4SLinus Torvalds struct linux_prom_registers regs[2]; 1691da177e4SLinus Torvalds int len, nregs; 1701da177e4SLinus Torvalds #endif 1711da177e4SLinus Torvalds int err; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds for_all_sbusdev(sdev, sbus) { 1741da177e4SLinus Torvalds if (!strcmp(sdev->prom_name, "flashprom")) { 1751da177e4SLinus Torvalds if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) { 1761da177e4SLinus Torvalds flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | 1771da177e4SLinus Torvalds (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); 1781da177e4SLinus Torvalds flash.read_size = sdev->reg_addrs[0].reg_size; 1791da177e4SLinus Torvalds flash.write_base = flash.read_base; 1801da177e4SLinus Torvalds flash.write_size = flash.read_size; 1811da177e4SLinus Torvalds } else { 1821da177e4SLinus Torvalds flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | 1831da177e4SLinus Torvalds (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); 1841da177e4SLinus Torvalds flash.read_size = sdev->reg_addrs[0].reg_size; 1851da177e4SLinus Torvalds flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) | 1861da177e4SLinus Torvalds (((unsigned long)sdev->reg_addrs[1].which_io)<<32UL); 1871da177e4SLinus Torvalds flash.write_size = sdev->reg_addrs[1].reg_size; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds flash.busy = 0; 1901da177e4SLinus Torvalds break; 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds if (!sdev) { 1941da177e4SLinus Torvalds #ifdef CONFIG_PCI 195690c8fd3SDavid S. Miller struct linux_prom_registers *ebus_regs; 196690c8fd3SDavid S. Miller 1971da177e4SLinus Torvalds for_each_ebus(ebus) { 1981da177e4SLinus Torvalds for_each_ebusdev(edev, ebus) { 199690c8fd3SDavid S. Miller if (!strcmp(edev->prom_node->name, "flashprom")) 2001da177e4SLinus Torvalds goto ebus_done; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds ebus_done: 2041da177e4SLinus Torvalds if (!edev) 2051da177e4SLinus Torvalds return -ENODEV; 2061da177e4SLinus Torvalds 207690c8fd3SDavid S. Miller ebus_regs = of_get_property(edev->prom_node, "reg", &len); 208690c8fd3SDavid S. Miller if (!ebus_regs || (len % sizeof(regs[0])) != 0) { 2091da177e4SLinus Torvalds printk("flash: Strange reg property size %d\n", len); 2101da177e4SLinus Torvalds return -ENODEV; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 213690c8fd3SDavid S. Miller nregs = len / sizeof(ebus_regs[0]); 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds flash.read_base = edev->resource[0].start; 216690c8fd3SDavid S. Miller flash.read_size = ebus_regs[0].reg_size; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds if (nregs == 1) { 2191da177e4SLinus Torvalds flash.write_base = edev->resource[0].start; 220690c8fd3SDavid S. Miller flash.write_size = ebus_regs[0].reg_size; 2211da177e4SLinus Torvalds } else if (nregs == 2) { 2221da177e4SLinus Torvalds flash.write_base = edev->resource[1].start; 223690c8fd3SDavid S. Miller flash.write_size = ebus_regs[1].reg_size; 2241da177e4SLinus Torvalds } else { 2251da177e4SLinus Torvalds printk("flash: Strange number of regs %d\n", nregs); 2261da177e4SLinus Torvalds return -ENODEV; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds flash.busy = 0; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds #else 2321da177e4SLinus Torvalds return -ENODEV; 2331da177e4SLinus Torvalds #endif 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n", 2371da177e4SLinus Torvalds flash.read_base, flash.read_size, 2381da177e4SLinus Torvalds flash.write_base, flash.write_size); 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds err = misc_register(&flash_dev); 2411da177e4SLinus Torvalds if (err) { 2421da177e4SLinus Torvalds printk(KERN_ERR "flash: unable to get misc minor\n"); 2431da177e4SLinus Torvalds return err; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds return 0; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds static void __exit flash_cleanup(void) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds misc_deregister(&flash_dev); 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds module_init(flash_init); 2551da177e4SLinus Torvalds module_exit(flash_cleanup); 2561da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 257