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