Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-block.git] / drivers / char / bsr.c
CommitLineData
1a59d1b8 1// SPDX-License-Identifier: GPL-2.0-or-later
fe9e8d53
SR
2/* IBM POWER Barrier Synchronization Register Driver
3 *
4 * Copyright IBM Corporation 2008
5 *
6 * Author: Sonny Rao <sonnyrao@us.ibm.com>
fe9e8d53
SR
7 */
8
9#include <linux/kernel.h>
10#include <linux/of.h>
5af50730 11#include <linux/of_address.h>
fe9e8d53
SR
12#include <linux/of_device.h>
13#include <linux/of_platform.h>
22ae782f 14#include <linux/fs.h>
fe9e8d53
SR
15#include <linux/module.h>
16#include <linux/cdev.h>
17#include <linux/list.h>
18#include <linux/mm.h>
5a0e3ad6 19#include <linux/slab.h>
04a85d12 20#include <asm/pgtable.h>
fe9e8d53
SR
21#include <asm/io.h>
22
23/*
24 This driver exposes a special register which can be used for fast
25 synchronization across a large SMP machine. The hardware is exposed
26 as an array of bytes where each process will write to one of the bytes to
27 indicate it has finished the current stage and this update is broadcast to
28 all processors without having to bounce a cacheline between them. In
29 POWER5 and POWER6 there is one of these registers per SMP, but it is
30 presented in two forms; first, it is given as a whole and then as a number
31 of smaller registers which alias to parts of the single whole register.
32 This can potentially allow multiple groups of processes to each have their
33 own private synchronization device.
34
35 Note that this hardware *must* be written to using *only* single byte writes.
36 It may be read using 1, 2, 4, or 8 byte loads which must be aligned since
37 this region is treated as cache-inhibited processes should also use a
38 full sync before and after writing to the BSR to ensure all stores and
39 the BSR update have made it to all chips in the system
40*/
41
42/* This is arbitrary number, up to Power6 it's been 17 or fewer */
43#define BSR_MAX_DEVS (32)
44
45struct bsr_dev {
46 u64 bsr_addr; /* Real address */
47 u64 bsr_len; /* length of mem region we can map */
48 unsigned bsr_bytes; /* size of the BSR reg itself */
49 unsigned bsr_stride; /* interval at which BSR repeats in the page */
50 unsigned bsr_type; /* maps to enum below */
51 unsigned bsr_num; /* bsr id number for its type */
52 int bsr_minor;
53
a0e2f9f4
SR
54 struct list_head bsr_list;
55
fe9e8d53
SR
56 dev_t bsr_dev;
57 struct cdev bsr_cdev;
58 struct device *bsr_device;
59 char bsr_name[32];
60
61};
62
a0e2f9f4
SR
63static unsigned total_bsr_devs;
64static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
fe9e8d53
SR
65static struct class *bsr_class;
66static int bsr_major;
67
68enum {
e4031d52
SR
69 BSR_8 = 0,
70 BSR_16 = 1,
71 BSR_64 = 2,
72 BSR_128 = 3,
73 BSR_4096 = 4,
74 BSR_UNKNOWN = 5,
75 BSR_MAX = 6,
fe9e8d53
SR
76};
77
78static unsigned bsr_types[BSR_MAX];
79
80static ssize_t
81bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
82{
83 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
84 return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
85}
62e13505 86static DEVICE_ATTR_RO(bsr_size);
fe9e8d53
SR
87
88static ssize_t
89bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
90{
91 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
92 return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
93}
62e13505 94static DEVICE_ATTR_RO(bsr_stride);
fe9e8d53
SR
95
96static ssize_t
62e13505 97bsr_length_show(struct device *dev, struct device_attribute *attr, char *buf)
fe9e8d53
SR
98{
99 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
1901515c 100 return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
fe9e8d53 101}
62e13505 102static DEVICE_ATTR_RO(bsr_length);
fe9e8d53 103
62e13505
GKH
104static struct attribute *bsr_dev_attrs[] = {
105 &dev_attr_bsr_size.attr,
106 &dev_attr_bsr_stride.attr,
107 &dev_attr_bsr_length.attr,
108 NULL,
fe9e8d53 109};
62e13505 110ATTRIBUTE_GROUPS(bsr_dev);
fe9e8d53
SR
111
112static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
113{
114 unsigned long size = vma->vm_end - vma->vm_start;
115 struct bsr_dev *dev = filp->private_data;
04a85d12 116 int ret;
fe9e8d53 117
fe9e8d53
SR
118 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
119
04a85d12
SR
120 /* check for the case of a small BSR device and map one 4k page for it*/
121 if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
122 ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
123 vma->vm_page_prot);
124 else if (size <= dev->bsr_len)
125 ret = io_remap_pfn_range(vma, vma->vm_start,
126 dev->bsr_addr >> PAGE_SHIFT,
127 size, vma->vm_page_prot);
128 else
129 return -EINVAL;
130
131 if (ret)
fe9e8d53
SR
132 return -EAGAIN;
133
134 return 0;
135}
136
687685a3 137static int bsr_open(struct inode *inode, struct file *filp)
fe9e8d53
SR
138{
139 struct cdev *cdev = inode->i_cdev;
140 struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev);
141
142 filp->private_data = dev;
143 return 0;
144}
145
27157a70 146static const struct file_operations bsr_fops = {
fe9e8d53
SR
147 .owner = THIS_MODULE,
148 .mmap = bsr_mmap,
149 .open = bsr_open,
6038f373 150 .llseek = noop_llseek,
fe9e8d53
SR
151};
152
153static void bsr_cleanup_devs(void)
154{
a0e2f9f4
SR
155 struct bsr_dev *cur, *n;
156
157 list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
fe9e8d53
SR
158 if (cur->bsr_device) {
159 cdev_del(&cur->bsr_cdev);
160 device_del(cur->bsr_device);
161 }
a0e2f9f4
SR
162 list_del(&cur->bsr_list);
163 kfree(cur);
fe9e8d53 164 }
fe9e8d53
SR
165}
166
a0e2f9f4 167static int bsr_add_node(struct device_node *bn)
fe9e8d53 168{
a0e2f9f4 169 int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
fe9e8d53
SR
170 const u32 *bsr_stride;
171 const u32 *bsr_bytes;
172 unsigned i;
a0e2f9f4 173 int ret = -ENODEV;
fe9e8d53
SR
174
175 bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
176 bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
177
178 if (!bsr_stride || !bsr_bytes ||
179 (bsr_stride_len != bsr_bytes_len)) {
180 printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
a0e2f9f4 181 return ret;
fe9e8d53
SR
182 }
183
184 num_bsr_devs = bsr_bytes_len / sizeof(u32);
185
fe9e8d53 186 for (i = 0 ; i < num_bsr_devs; i++) {
a0e2f9f4
SR
187 struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
188 GFP_KERNEL);
fe9e8d53
SR
189 struct resource res;
190 int result;
191
a0e2f9f4
SR
192 if (!cur) {
193 printk(KERN_ERR "Unable to alloc bsr dev\n");
194 ret = -ENOMEM;
195 goto out_err;
196 }
197
fe9e8d53
SR
198 result = of_address_to_resource(bn, i, &res);
199 if (result < 0) {
a0e2f9f4
SR
200 printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
201 kfree(cur);
202 continue;
fe9e8d53
SR
203 }
204
a0e2f9f4 205 cur->bsr_minor = i + total_bsr_devs;
fe9e8d53 206 cur->bsr_addr = res.start;
28f65c11 207 cur->bsr_len = resource_size(&res);
fe9e8d53
SR
208 cur->bsr_bytes = bsr_bytes[i];
209 cur->bsr_stride = bsr_stride[i];
a0e2f9f4 210 cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
fe9e8d53 211
04a85d12
SR
212 /* if we have a bsr_len of > 4k and less then PAGE_SIZE (64k pages) */
213 /* we can only map 4k of it, so only advertise the 4k in sysfs */
214 if (cur->bsr_len > 4096 && cur->bsr_len < PAGE_SIZE)
215 cur->bsr_len = 4096;
216
fe9e8d53
SR
217 switch(cur->bsr_bytes) {
218 case 8:
219 cur->bsr_type = BSR_8;
220 break;
221 case 16:
222 cur->bsr_type = BSR_16;
223 break;
224 case 64:
225 cur->bsr_type = BSR_64;
226 break;
227 case 128:
228 cur->bsr_type = BSR_128;
229 break;
e4031d52
SR
230 case 4096:
231 cur->bsr_type = BSR_4096;
232 break;
fe9e8d53
SR
233 default:
234 cur->bsr_type = BSR_UNKNOWN;
fe9e8d53
SR
235 }
236
237 cur->bsr_num = bsr_types[cur->bsr_type];
fe9e8d53
SR
238 snprintf(cur->bsr_name, 32, "bsr%d_%d",
239 cur->bsr_bytes, cur->bsr_num);
240
241 cdev_init(&cur->bsr_cdev, &bsr_fops);
242 result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
a0e2f9f4
SR
243 if (result) {
244 kfree(cur);
fe9e8d53 245 goto out_err;
a0e2f9f4 246 }
fe9e8d53 247
03457cd4 248 cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
4171ee9e 249 cur, "%s", cur->bsr_name);
30c96ce5 250 if (IS_ERR(cur->bsr_device)) {
fe9e8d53
SR
251 printk(KERN_ERR "device_create failed for %s\n",
252 cur->bsr_name);
253 cdev_del(&cur->bsr_cdev);
a0e2f9f4 254 kfree(cur);
fe9e8d53
SR
255 goto out_err;
256 }
a0e2f9f4
SR
257
258 bsr_types[cur->bsr_type] = cur->bsr_num + 1;
259 list_add_tail(&cur->bsr_list, &bsr_devs);
fe9e8d53
SR
260 }
261
a0e2f9f4
SR
262 total_bsr_devs += num_bsr_devs;
263
fe9e8d53
SR
264 return 0;
265
266 out_err:
267
268 bsr_cleanup_devs();
a0e2f9f4
SR
269 return ret;
270}
271
272static int bsr_create_devs(struct device_node *bn)
273{
274 int ret;
275
276 while (bn) {
277 ret = bsr_add_node(bn);
278 if (ret) {
279 of_node_put(bn);
280 return ret;
281 }
282 bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
283 }
284 return 0;
fe9e8d53
SR
285}
286
287static int __init bsr_init(void)
288{
289 struct device_node *np;
cce36444 290 dev_t bsr_dev;
fe9e8d53 291 int ret = -ENODEV;
fe9e8d53 292
a0e2f9f4 293 np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
fe9e8d53
SR
294 if (!np)
295 goto out_err;
296
297 bsr_class = class_create(THIS_MODULE, "bsr");
298 if (IS_ERR(bsr_class)) {
299 printk(KERN_ERR "class_create() failed for bsr_class\n");
8397c76a 300 ret = PTR_ERR(bsr_class);
fe9e8d53
SR
301 goto out_err_1;
302 }
62e13505 303 bsr_class->dev_groups = bsr_dev_groups;
fe9e8d53 304
8397c76a 305 ret = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
fe9e8d53 306 bsr_major = MAJOR(bsr_dev);
8397c76a 307 if (ret < 0) {
fe9e8d53
SR
308 printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
309 goto out_err_2;
310 }
311
7256d7f4
NKP
312 ret = bsr_create_devs(np);
313 if (ret < 0) {
a0e2f9f4 314 np = NULL;
fe9e8d53 315 goto out_err_3;
a0e2f9f4 316 }
fe9e8d53
SR
317
318 return 0;
319
320 out_err_3:
321 unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
322
323 out_err_2:
324 class_destroy(bsr_class);
325
326 out_err_1:
327 of_node_put(np);
328
329 out_err:
330
331 return ret;
332}
333
334static void __exit bsr_exit(void)
335{
336
337 bsr_cleanup_devs();
338
339 if (bsr_class)
340 class_destroy(bsr_class);
341
342 if (bsr_major)
343 unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
344}
345
346module_init(bsr_init);
347module_exit(bsr_exit);
348MODULE_LICENSE("GPL");
349MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");