powerpc: Add driver for Barrier Synchronization Register
[safe/jmp/linux-2.6] / drivers / char / bsr.c
1 /* IBM POWER Barrier Synchronization Register Driver
2  *
3  * Copyright IBM Corporation 2008
4  *
5  * Author: Sonny Rao <sonnyrao@us.ibm.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20  */
21
22 #include <linux/kernel.h>
23 #include <linux/of.h>
24 #include <linux/of_device.h>
25 #include <linux/of_platform.h>
26 #include <linux/module.h>
27 #include <linux/cdev.h>
28 #include <linux/list.h>
29 #include <linux/mm.h>
30 #include <asm/io.h>
31
32 /*
33  This driver exposes a special register which can be used for fast
34  synchronization across a large SMP machine.  The hardware is exposed
35  as an array of bytes where each process will write to one of the bytes to
36  indicate it has finished the current stage and this update is broadcast to
37  all processors without having to bounce a cacheline between them. In
38  POWER5 and POWER6 there is one of these registers per SMP,  but it is
39  presented in two forms; first, it is given as a whole and then as a number
40  of smaller registers which alias to parts of the single whole register.
41  This can potentially allow multiple groups of processes to each have their
42  own private synchronization device.
43
44  Note that this hardware *must* be written to using *only* single byte writes.
45  It may be read using 1, 2, 4, or 8 byte loads which must be aligned since
46  this region is treated as cache-inhibited  processes should also use a
47  full sync before and after writing to the BSR to ensure all stores and
48  the BSR update have made it to all chips in the system
49 */
50
51 /* This is arbitrary number, up to Power6 it's been 17 or fewer  */
52 #define BSR_MAX_DEVS (32)
53
54 struct bsr_dev {
55         u64      bsr_addr;     /* Real address */
56         u64      bsr_len;      /* length of mem region we can map */
57         unsigned bsr_bytes;    /* size of the BSR reg itself */
58         unsigned bsr_stride;   /* interval at which BSR repeats in the page */
59         unsigned bsr_type;     /* maps to enum below */
60         unsigned bsr_num;      /* bsr id number for its type */
61         int      bsr_minor;
62
63         dev_t    bsr_dev;
64         struct cdev bsr_cdev;
65         struct device *bsr_device;
66         char     bsr_name[32];
67
68 };
69
70 static unsigned num_bsr_devs;
71 static struct bsr_dev *bsr_devs;
72 static struct class *bsr_class;
73 static int bsr_major;
74
75 enum {
76         BSR_8   = 0,
77         BSR_16  = 1,
78         BSR_64  = 2,
79         BSR_128 = 3,
80         BSR_UNKNOWN = 4,
81         BSR_MAX = 5,
82 };
83
84 static unsigned bsr_types[BSR_MAX];
85
86 static ssize_t
87 bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
88 {
89         struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
90         return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
91 }
92
93 static ssize_t
94 bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
95 {
96         struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
97         return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
98 }
99
100 static ssize_t
101 bsr_len_show(struct device *dev, struct device_attribute *attr, char *buf)
102 {
103         struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
104         return sprintf(buf, "%lu\n", bsr_dev->bsr_len);
105 }
106
107 static struct device_attribute bsr_dev_attrs[] = {
108         __ATTR(bsr_size, S_IRUGO, bsr_size_show, NULL),
109         __ATTR(bsr_stride, S_IRUGO, bsr_stride_show, NULL),
110         __ATTR(bsr_length, S_IRUGO, bsr_len_show, NULL),
111         __ATTR_NULL
112 };
113
114 static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
115 {
116         unsigned long size   = vma->vm_end - vma->vm_start;
117         struct bsr_dev *dev = filp->private_data;
118
119         if (size > dev->bsr_len || (size & (PAGE_SIZE-1)))
120                 return -EINVAL;
121
122         vma->vm_flags |= (VM_IO | VM_DONTEXPAND);
123         vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
124
125         if (io_remap_pfn_range(vma, vma->vm_start, dev->bsr_addr >> PAGE_SHIFT,
126                                size, vma->vm_page_prot))
127                 return -EAGAIN;
128
129         return 0;
130 }
131
132 static int bsr_open(struct inode * inode, struct file * filp)
133 {
134         struct cdev *cdev = inode->i_cdev;
135         struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev);
136
137         filp->private_data = dev;
138         return 0;
139 }
140
141 const static struct file_operations bsr_fops = {
142         .owner = THIS_MODULE,
143         .mmap  = bsr_mmap,
144         .open  = bsr_open,
145 };
146
147 static void bsr_cleanup_devs(void)
148 {
149         int i;
150         for (i=0 ; i < num_bsr_devs; i++) {
151                 struct bsr_dev *cur = bsr_devs + i;
152                 if (cur->bsr_device) {
153                         cdev_del(&cur->bsr_cdev);
154                         device_del(cur->bsr_device);
155                 }
156         }
157
158         kfree(bsr_devs);
159 }
160
161 static int bsr_create_devs(struct device_node *bn)
162 {
163         int bsr_stride_len, bsr_bytes_len;
164         const u32 *bsr_stride;
165         const u32 *bsr_bytes;
166         unsigned i;
167
168         bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
169         bsr_bytes  = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
170
171         if (!bsr_stride || !bsr_bytes ||
172             (bsr_stride_len != bsr_bytes_len)) {
173                 printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
174                 return -ENODEV;
175         }
176
177         num_bsr_devs = bsr_bytes_len / sizeof(u32);
178
179         /* only a warning, its informational since we'll fail and exit */
180         WARN_ON(num_bsr_devs > BSR_MAX_DEVS);
181
182         bsr_devs = kzalloc(sizeof(struct bsr_dev) * num_bsr_devs, GFP_KERNEL);
183         if (!bsr_devs)
184                 return -ENOMEM;
185
186         for (i = 0 ; i < num_bsr_devs; i++) {
187                 struct bsr_dev *cur = bsr_devs + i;
188                 struct resource res;
189                 int result;
190
191                 result = of_address_to_resource(bn, i, &res);
192                 if (result < 0) {
193                         printk(KERN_ERR "bsr of-node has invalid reg property\n");
194                         goto out_err;
195                 }
196
197                 cur->bsr_minor  = i;
198                 cur->bsr_addr   = res.start;
199                 cur->bsr_len    = res.end - res.start + 1;
200                 cur->bsr_bytes  = bsr_bytes[i];
201                 cur->bsr_stride = bsr_stride[i];
202                 cur->bsr_dev    = MKDEV(bsr_major, i);
203
204                 switch(cur->bsr_bytes) {
205                 case 8:
206                         cur->bsr_type = BSR_8;
207                         break;
208                 case 16:
209                         cur->bsr_type = BSR_16;
210                         break;
211                 case 64:
212                         cur->bsr_type = BSR_64;
213                         break;
214                 case 128:
215                         cur->bsr_type = BSR_128;
216                         break;
217                 default:
218                         cur->bsr_type = BSR_UNKNOWN;
219                         printk(KERN_INFO "unknown BSR size %d\n",cur->bsr_bytes);
220                 }
221
222                 cur->bsr_num = bsr_types[cur->bsr_type];
223                 bsr_types[cur->bsr_type] = cur->bsr_num + 1;
224                 snprintf(cur->bsr_name, 32, "bsr%d_%d",
225                          cur->bsr_bytes, cur->bsr_num);
226
227                 cdev_init(&cur->bsr_cdev, &bsr_fops);
228                 result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
229                 if (result)
230                         goto out_err;
231
232                 cur->bsr_device = device_create_drvdata(bsr_class, NULL,
233                                                         cur->bsr_dev,
234                                                         cur, cur->bsr_name);
235                 if (!cur->bsr_device) {
236                         printk(KERN_ERR "device_create failed for %s\n",
237                                cur->bsr_name);
238                         cdev_del(&cur->bsr_cdev);
239                         goto out_err;
240                 }
241         }
242
243         return 0;
244
245  out_err:
246
247         bsr_cleanup_devs();
248         return -ENODEV;
249 }
250
251 static int __init bsr_init(void)
252 {
253         struct device_node *np;
254         dev_t bsr_dev = MKDEV(bsr_major, 0);
255         int ret = -ENODEV;
256         int result;
257
258         np = of_find_compatible_node(NULL, "ibm,bsr", "ibm,bsr");
259         if (!np)
260                 goto out_err;
261
262         bsr_class = class_create(THIS_MODULE, "bsr");
263         if (IS_ERR(bsr_class)) {
264                 printk(KERN_ERR "class_create() failed for bsr_class\n");
265                 goto out_err_1;
266         }
267         bsr_class->dev_attrs = bsr_dev_attrs;
268
269         result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
270         bsr_major = MAJOR(bsr_dev);
271         if (result < 0) {
272                 printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
273                 goto out_err_2;
274         }
275
276         if ((ret = bsr_create_devs(np)) < 0)
277                 goto out_err_3;
278
279         of_node_put(np);
280
281         return 0;
282
283  out_err_3:
284         unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
285
286  out_err_2:
287         class_destroy(bsr_class);
288
289  out_err_1:
290         of_node_put(np);
291
292  out_err:
293
294         return ret;
295 }
296
297 static void __exit  bsr_exit(void)
298 {
299
300         bsr_cleanup_devs();
301
302         if (bsr_class)
303                 class_destroy(bsr_class);
304
305         if (bsr_major)
306                 unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
307 }
308
309 module_init(bsr_init);
310 module_exit(bsr_exit);
311 MODULE_LICENSE("GPL");
312 MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");