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