linux/drivers/sbus/char/flash.c
<<
>>
Prefs
   1/* flash.c: Allow mmap access to the OBP Flash, for OBP updates.
   2 *
   3 * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
   4 */
   5
   6#include <linux/module.h>
   7#include <linux/types.h>
   8#include <linux/errno.h>
   9#include <linux/miscdevice.h>
  10#include <linux/fcntl.h>
  11#include <linux/poll.h>
  12#include <linux/mutex.h>
  13#include <linux/spinlock.h>
  14#include <linux/mm.h>
  15#include <linux/of.h>
  16#include <linux/of_device.h>
  17
  18#include <linux/uaccess.h>
  19#include <asm/pgtable.h>
  20#include <asm/io.h>
  21#include <asm/upa.h>
  22
  23static DEFINE_MUTEX(flash_mutex);
  24static DEFINE_SPINLOCK(flash_lock);
  25static struct {
  26        unsigned long read_base;        /* Physical read address */
  27        unsigned long write_base;       /* Physical write address */
  28        unsigned long read_size;        /* Size of read area */
  29        unsigned long write_size;       /* Size of write area */
  30        unsigned long busy;             /* In use? */
  31} flash;
  32
  33#define FLASH_MINOR     152
  34
  35static int
  36flash_mmap(struct file *file, struct vm_area_struct *vma)
  37{
  38        unsigned long addr;
  39        unsigned long size;
  40
  41        spin_lock(&flash_lock);
  42        if (flash.read_base == flash.write_base) {
  43                addr = flash.read_base;
  44                size = flash.read_size;
  45        } else {
  46                if ((vma->vm_flags & VM_READ) &&
  47                    (vma->vm_flags & VM_WRITE)) {
  48                        spin_unlock(&flash_lock);
  49                        return -EINVAL;
  50                }
  51                if (vma->vm_flags & VM_READ) {
  52                        addr = flash.read_base;
  53                        size = flash.read_size;
  54                } else if (vma->vm_flags & VM_WRITE) {
  55                        addr = flash.write_base;
  56                        size = flash.write_size;
  57                } else {
  58                        spin_unlock(&flash_lock);
  59                        return -ENXIO;
  60                }
  61        }
  62        spin_unlock(&flash_lock);
  63
  64        if ((vma->vm_pgoff << PAGE_SHIFT) > size)
  65                return -ENXIO;
  66        addr = vma->vm_pgoff + (addr >> PAGE_SHIFT);
  67
  68        if (vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT)) > size)
  69                size = vma->vm_end - (vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT));
  70
  71        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
  72
  73        if (io_remap_pfn_range(vma, vma->vm_start, addr, size, vma->vm_page_prot))
  74                return -EAGAIN;
  75                
  76        return 0;
  77}
  78
  79static long long
  80flash_llseek(struct file *file, long long offset, int origin)
  81{
  82        mutex_lock(&flash_mutex);
  83        switch (origin) {
  84                case 0:
  85                        file->f_pos = offset;
  86                        break;
  87                case 1:
  88                        file->f_pos += offset;
  89                        if (file->f_pos > flash.read_size)
  90                                file->f_pos = flash.read_size;
  91                        break;
  92                case 2:
  93                        file->f_pos = flash.read_size;
  94                        break;
  95                default:
  96                        mutex_unlock(&flash_mutex);
  97                        return -EINVAL;
  98        }
  99        mutex_unlock(&flash_mutex);
 100        return file->f_pos;
 101}
 102
 103static ssize_t
 104flash_read(struct file * file, char __user * buf,
 105           size_t count, loff_t *ppos)
 106{
 107        loff_t p = *ppos;
 108        int i;
 109
 110        if (count > flash.read_size - p)
 111                count = flash.read_size - p;
 112
 113        for (i = 0; i < count; i++) {
 114                u8 data = upa_readb(flash.read_base + p + i);
 115                if (put_user(data, buf))
 116                        return -EFAULT;
 117                buf++;
 118        }
 119
 120        *ppos += count;
 121        return count;
 122}
 123
 124static int
 125flash_open(struct inode *inode, struct file *file)
 126{
 127        mutex_lock(&flash_mutex);
 128        if (test_and_set_bit(0, (void *)&flash.busy) != 0) {
 129                mutex_unlock(&flash_mutex);
 130                return -EBUSY;
 131        }
 132
 133        mutex_unlock(&flash_mutex);
 134        return 0;
 135}
 136
 137static int
 138flash_release(struct inode *inode, struct file *file)
 139{
 140        spin_lock(&flash_lock);
 141        flash.busy = 0;
 142        spin_unlock(&flash_lock);
 143
 144        return 0;
 145}
 146
 147static const struct file_operations flash_fops = {
 148        /* no write to the Flash, use mmap
 149         * and play flash dependent tricks.
 150         */
 151        .owner =        THIS_MODULE,
 152        .llseek =       flash_llseek,
 153        .read =         flash_read,
 154        .mmap =         flash_mmap,
 155        .open =         flash_open,
 156        .release =      flash_release,
 157};
 158
 159static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
 160
 161static int flash_probe(struct platform_device *op)
 162{
 163        struct device_node *dp = op->dev.of_node;
 164        struct device_node *parent;
 165
 166        parent = dp->parent;
 167
 168        if (strcmp(parent->name, "sbus") &&
 169            strcmp(parent->name, "sbi") &&
 170            strcmp(parent->name, "ebus"))
 171                return -ENODEV;
 172
 173        flash.read_base = op->resource[0].start;
 174        flash.read_size = resource_size(&op->resource[0]);
 175        if (op->resource[1].flags) {
 176                flash.write_base = op->resource[1].start;
 177                flash.write_size = resource_size(&op->resource[1]);
 178        } else {
 179                flash.write_base = op->resource[0].start;
 180                flash.write_size = resource_size(&op->resource[0]);
 181        }
 182        flash.busy = 0;
 183
 184        printk(KERN_INFO "%pOF: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n",
 185               op->dev.of_node,
 186               flash.read_base, flash.read_size,
 187               flash.write_base, flash.write_size);
 188
 189        return misc_register(&flash_dev);
 190}
 191
 192static int flash_remove(struct platform_device *op)
 193{
 194        misc_deregister(&flash_dev);
 195
 196        return 0;
 197}
 198
 199static const struct of_device_id flash_match[] = {
 200        {
 201                .name = "flashprom",
 202        },
 203        {},
 204};
 205MODULE_DEVICE_TABLE(of, flash_match);
 206
 207static struct platform_driver flash_driver = {
 208        .driver = {
 209                .name = "flash",
 210                .of_match_table = flash_match,
 211        },
 212        .probe          = flash_probe,
 213        .remove         = flash_remove,
 214};
 215
 216module_platform_driver(flash_driver);
 217
 218MODULE_LICENSE("GPL");
 219