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