1 // SPDX-License-Identifier: GPL-2.0-only
2 /* flash.c: Allow mmap access to the OBP Flash, for OBP updates.
4 * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
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>
17 #include <linux/of_device.h>
19 #include <linux/uaccess.h>
23 static DEFINE_MUTEX(flash_mutex
);
24 static DEFINE_SPINLOCK(flash_lock
);
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? */
34 flash_mmap(struct file
*file
, struct vm_area_struct
*vma
)
39 spin_lock(&flash_lock
);
40 if (flash
.read_base
== flash
.write_base
) {
41 addr
= flash
.read_base
;
42 size
= flash
.read_size
;
44 if ((vma
->vm_flags
& VM_READ
) &&
45 (vma
->vm_flags
& VM_WRITE
)) {
46 spin_unlock(&flash_lock
);
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
;
56 spin_unlock(&flash_lock
);
60 spin_unlock(&flash_lock
);
62 if ((vma
->vm_pgoff
<< PAGE_SHIFT
) > size
)
64 addr
= vma
->vm_pgoff
+ (addr
>> PAGE_SHIFT
);
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
));
69 vma
->vm_page_prot
= pgprot_noncached(vma
->vm_page_prot
);
71 if (io_remap_pfn_range(vma
, vma
->vm_start
, addr
, size
, vma
->vm_page_prot
))
78 flash_llseek(struct file
*file
, long long offset
, int origin
)
80 mutex_lock(&flash_mutex
);
86 file
->f_pos
+= offset
;
87 if (file
->f_pos
> flash
.read_size
)
88 file
->f_pos
= flash
.read_size
;
91 file
->f_pos
= flash
.read_size
;
94 mutex_unlock(&flash_mutex
);
97 mutex_unlock(&flash_mutex
);
102 flash_read(struct file
* file
, char __user
* buf
,
103 size_t count
, loff_t
*ppos
)
108 if (count
> flash
.read_size
- p
)
109 count
= flash
.read_size
- p
;
111 for (i
= 0; i
< count
; i
++) {
112 u8 data
= upa_readb(flash
.read_base
+ p
+ i
);
113 if (put_user(data
, buf
))
123 flash_open(struct inode
*inode
, struct file
*file
)
125 mutex_lock(&flash_mutex
);
126 if (test_and_set_bit(0, (void *)&flash
.busy
) != 0) {
127 mutex_unlock(&flash_mutex
);
131 mutex_unlock(&flash_mutex
);
136 flash_release(struct inode
*inode
, struct file
*file
)
138 spin_lock(&flash_lock
);
140 spin_unlock(&flash_lock
);
145 static const struct file_operations flash_fops
= {
146 /* no write to the Flash, use mmap
147 * and play flash dependent tricks.
149 .owner
= THIS_MODULE
,
150 .llseek
= flash_llseek
,
154 .release
= flash_release
,
157 static struct miscdevice flash_dev
= { SBUS_FLASH_MINOR
, "flash", &flash_fops
};
159 static int flash_probe(struct platform_device
*op
)
161 struct device_node
*dp
= op
->dev
.of_node
;
162 struct device_node
*parent
;
166 if (!of_node_name_eq(parent
, "sbus") &&
167 !of_node_name_eq(parent
, "sbi") &&
168 !of_node_name_eq(parent
, "ebus"))
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]);
177 flash
.write_base
= op
->resource
[0].start
;
178 flash
.write_size
= resource_size(&op
->resource
[0]);
182 printk(KERN_INFO
"%pOF: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n",
184 flash
.read_base
, flash
.read_size
,
185 flash
.write_base
, flash
.write_size
);
187 return misc_register(&flash_dev
);
190 static int flash_remove(struct platform_device
*op
)
192 misc_deregister(&flash_dev
);
197 static const struct of_device_id flash_match
[] = {
203 MODULE_DEVICE_TABLE(of
, flash_match
);
205 static struct platform_driver flash_driver
= {
208 .of_match_table
= flash_match
,
210 .probe
= flash_probe
,
211 .remove
= flash_remove
,
214 module_platform_driver(flash_driver
);
216 MODULE_LICENSE("GPL");