1a9540d34SDavid S. Miller /* flash.c: Allow mmap access to the OBP Flash, for OBP updates. 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) 41da177e4SLinus Torvalds */ 51da177e4SLinus Torvalds 61da177e4SLinus Torvalds #include <linux/module.h> 71da177e4SLinus Torvalds #include <linux/types.h> 81da177e4SLinus Torvalds #include <linux/errno.h> 91da177e4SLinus Torvalds #include <linux/miscdevice.h> 101da177e4SLinus Torvalds #include <linux/slab.h> 111da177e4SLinus Torvalds #include <linux/fcntl.h> 121da177e4SLinus Torvalds #include <linux/poll.h> 131da177e4SLinus Torvalds #include <linux/init.h> 141da177e4SLinus Torvalds #include <linux/smp_lock.h> 151da177e4SLinus Torvalds #include <linux/spinlock.h> 168a73709eSHorst H. von Brand #include <linux/mm.h> 17a9540d34SDavid S. Miller #include <linux/of.h> 18a9540d34SDavid S. Miller #include <linux/of_device.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include <asm/system.h> 211da177e4SLinus Torvalds #include <asm/uaccess.h> 221da177e4SLinus Torvalds #include <asm/pgtable.h> 231da177e4SLinus Torvalds #include <asm/io.h> 241da177e4SLinus Torvalds #include <asm/upa.h> 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds static DEFINE_SPINLOCK(flash_lock); 271da177e4SLinus Torvalds static struct { 281da177e4SLinus Torvalds unsigned long read_base; /* Physical read address */ 291da177e4SLinus Torvalds unsigned long write_base; /* Physical write address */ 301da177e4SLinus Torvalds unsigned long read_size; /* Size of read area */ 311da177e4SLinus Torvalds unsigned long write_size; /* Size of write area */ 321da177e4SLinus Torvalds unsigned long busy; /* In use? */ 331da177e4SLinus Torvalds } flash; 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds #define FLASH_MINOR 152 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds static int 381da177e4SLinus Torvalds flash_mmap(struct file *file, struct vm_area_struct *vma) 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds unsigned long addr; 411da177e4SLinus Torvalds unsigned long size; 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds spin_lock(&flash_lock); 441da177e4SLinus Torvalds if (flash.read_base == flash.write_base) { 451da177e4SLinus Torvalds addr = flash.read_base; 461da177e4SLinus Torvalds size = flash.read_size; 471da177e4SLinus Torvalds } else { 481da177e4SLinus Torvalds if ((vma->vm_flags & VM_READ) && 491da177e4SLinus Torvalds (vma->vm_flags & VM_WRITE)) { 501da177e4SLinus Torvalds spin_unlock(&flash_lock); 511da177e4SLinus Torvalds return -EINVAL; 521da177e4SLinus Torvalds } 531da177e4SLinus Torvalds if (vma->vm_flags & VM_READ) { 541da177e4SLinus Torvalds addr = flash.read_base; 551da177e4SLinus Torvalds size = flash.read_size; 561da177e4SLinus Torvalds } else if (vma->vm_flags & VM_WRITE) { 571da177e4SLinus Torvalds addr = flash.write_base; 581da177e4SLinus Torvalds size = flash.write_size; 591da177e4SLinus Torvalds } else { 601da177e4SLinus Torvalds spin_unlock(&flash_lock); 611da177e4SLinus Torvalds return -ENXIO; 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds spin_unlock(&flash_lock); 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds if ((vma->vm_pgoff << PAGE_SHIFT) > size) 671da177e4SLinus Torvalds return -ENXIO; 681da177e4SLinus Torvalds addr = vma->vm_pgoff + (addr >> PAGE_SHIFT); 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size) 711da177e4SLinus Torvalds size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)); 721da177e4SLinus Torvalds 7314778d90SDavid S. Miller vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot)) 761da177e4SLinus Torvalds return -EAGAIN; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds return 0; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds static long long 821da177e4SLinus Torvalds flash_llseek(struct file *file, long long offset, int origin) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds lock_kernel(); 851da177e4SLinus Torvalds switch (origin) { 861da177e4SLinus Torvalds case 0: 871da177e4SLinus Torvalds file->f_pos = offset; 881da177e4SLinus Torvalds break; 891da177e4SLinus Torvalds case 1: 901da177e4SLinus Torvalds file->f_pos += offset; 911da177e4SLinus Torvalds if (file->f_pos > flash.read_size) 921da177e4SLinus Torvalds file->f_pos = flash.read_size; 931da177e4SLinus Torvalds break; 941da177e4SLinus Torvalds case 2: 951da177e4SLinus Torvalds file->f_pos = flash.read_size; 961da177e4SLinus Torvalds break; 971da177e4SLinus Torvalds default: 981da177e4SLinus Torvalds unlock_kernel(); 991da177e4SLinus Torvalds return -EINVAL; 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds unlock_kernel(); 1021da177e4SLinus Torvalds return file->f_pos; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds static ssize_t 1061da177e4SLinus Torvalds flash_read(struct file * file, char __user * buf, 1071da177e4SLinus Torvalds size_t count, loff_t *ppos) 1081da177e4SLinus Torvalds { 1091da177e4SLinus Torvalds unsigned long p = file->f_pos; 1101da177e4SLinus Torvalds int i; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds if (count > flash.read_size - p) 1131da177e4SLinus Torvalds count = flash.read_size - p; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds for (i = 0; i < count; i++) { 1161da177e4SLinus Torvalds u8 data = upa_readb(flash.read_base + p + i); 1171da177e4SLinus Torvalds if (put_user(data, buf)) 1181da177e4SLinus Torvalds return -EFAULT; 1191da177e4SLinus Torvalds buf++; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds file->f_pos += count; 1231da177e4SLinus Torvalds return count; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds static int 1271da177e4SLinus Torvalds flash_open(struct inode *inode, struct file *file) 1281da177e4SLinus Torvalds { 12978abb6acSArnd Bergmann lock_kernel(); 13078abb6acSArnd Bergmann if (test_and_set_bit(0, (void *)&flash.busy) != 0) { 13178abb6acSArnd Bergmann unlock_kernel(); 1321da177e4SLinus Torvalds return -EBUSY; 13378abb6acSArnd Bergmann } 1341da177e4SLinus Torvalds 13578abb6acSArnd Bergmann unlock_kernel(); 1361da177e4SLinus Torvalds return 0; 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds static int 1401da177e4SLinus Torvalds flash_release(struct inode *inode, struct file *file) 1411da177e4SLinus Torvalds { 1421da177e4SLinus Torvalds spin_lock(&flash_lock); 1431da177e4SLinus Torvalds flash.busy = 0; 1441da177e4SLinus Torvalds spin_unlock(&flash_lock); 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds return 0; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds 14900977a59SArjan van de Ven static const struct file_operations flash_fops = { 1501da177e4SLinus Torvalds /* no write to the Flash, use mmap 1511da177e4SLinus Torvalds * and play flash dependent tricks. 1521da177e4SLinus Torvalds */ 1531da177e4SLinus Torvalds .owner = THIS_MODULE, 1541da177e4SLinus Torvalds .llseek = flash_llseek, 1551da177e4SLinus Torvalds .read = flash_read, 1561da177e4SLinus Torvalds .mmap = flash_mmap, 1571da177e4SLinus Torvalds .open = flash_open, 1581da177e4SLinus Torvalds .release = flash_release, 1591da177e4SLinus Torvalds }; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; 1621da177e4SLinus Torvalds 163a9540d34SDavid S. Miller static int __devinit flash_probe(struct of_device *op, 164a9540d34SDavid S. Miller const struct of_device_id *match) 1651da177e4SLinus Torvalds { 166a9540d34SDavid S. Miller struct device_node *dp = op->node; 167a9540d34SDavid S. Miller struct device_node *parent; 1681da177e4SLinus Torvalds 169a9540d34SDavid S. Miller parent = dp->parent; 170a9540d34SDavid S. Miller 171a9540d34SDavid S. Miller if (strcmp(parent->name, "sbus") && 172a9540d34SDavid S. Miller strcmp(parent->name, "sbi") && 173a9540d34SDavid S. Miller strcmp(parent->name, "ebus")) 174a9540d34SDavid S. Miller return -ENODEV; 175a9540d34SDavid S. Miller 176a9540d34SDavid S. Miller flash.read_base = op->resource[0].start; 177a9540d34SDavid S. Miller flash.read_size = resource_size(&op->resource[0]); 178a9540d34SDavid S. Miller if (op->resource[1].flags) { 179a9540d34SDavid S. Miller flash.write_base = op->resource[1].start; 180a9540d34SDavid S. Miller flash.write_size = resource_size(&op->resource[1]); 1811da177e4SLinus Torvalds } else { 182a9540d34SDavid S. Miller flash.write_base = op->resource[0].start; 183a9540d34SDavid S. Miller flash.write_size = resource_size(&op->resource[0]); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds flash.busy = 0; 186690c8fd3SDavid S. Miller 187a9540d34SDavid S. Miller printk(KERN_INFO "%s: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n", 188a9540d34SDavid S. Miller op->node->full_name, 1891da177e4SLinus Torvalds flash.read_base, flash.read_size, 1901da177e4SLinus Torvalds flash.write_base, flash.write_size); 1911da177e4SLinus Torvalds 192a9540d34SDavid S. Miller return misc_register(&flash_dev); 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 195a9540d34SDavid S. Miller static int __devexit flash_remove(struct of_device *op) 196a9540d34SDavid S. Miller { 197a9540d34SDavid S. Miller misc_deregister(&flash_dev); 198a9540d34SDavid S. Miller 1991da177e4SLinus Torvalds return 0; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 202fd098316SDavid S. Miller static const struct of_device_id flash_match[] = { 203a9540d34SDavid S. Miller { 204a9540d34SDavid S. Miller .name = "flashprom", 205a9540d34SDavid S. Miller }, 206a9540d34SDavid S. Miller {}, 207a9540d34SDavid S. Miller }; 208a9540d34SDavid S. Miller MODULE_DEVICE_TABLE(of, flash_match); 209a9540d34SDavid S. Miller 210a9540d34SDavid S. Miller static struct of_platform_driver flash_driver = { 211a9540d34SDavid S. Miller .name = "flash", 212a9540d34SDavid S. Miller .match_table = flash_match, 213a9540d34SDavid S. Miller .probe = flash_probe, 214a9540d34SDavid S. Miller .remove = __devexit_p(flash_remove), 215a9540d34SDavid S. Miller }; 216a9540d34SDavid S. Miller 217a9540d34SDavid S. Miller static int __init flash_init(void) 218a9540d34SDavid S. Miller { 219a9540d34SDavid S. Miller return of_register_driver(&flash_driver, &of_bus_type); 220a9540d34SDavid S. Miller } 221a9540d34SDavid S. Miller 2221da177e4SLinus Torvalds static void __exit flash_cleanup(void) 2231da177e4SLinus Torvalds { 224a9540d34SDavid S. Miller of_unregister_driver(&flash_driver); 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds module_init(flash_init); 2281da177e4SLinus Torvalds module_exit(flash_cleanup); 2291da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 230