blob: c02db01f736ea46bc536190910a2f4a9e336bfbb [file] [log] [blame]
Sonny Raofe9e8d52008-07-08 15:45:11 +10001/* 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>
Sonny Rao04a85d12009-06-18 15:13:04 +000030#include <asm/pgtable.h>
Sonny Raofe9e8d52008-07-08 15:45:11 +100031#include <asm/io.h>
32
33/*
34 This driver exposes a special register which can be used for fast
35 synchronization across a large SMP machine. The hardware is exposed
36 as an array of bytes where each process will write to one of the bytes to
37 indicate it has finished the current stage and this update is broadcast to
38 all processors without having to bounce a cacheline between them. In
39 POWER5 and POWER6 there is one of these registers per SMP, but it is
40 presented in two forms; first, it is given as a whole and then as a number
41 of smaller registers which alias to parts of the single whole register.
42 This can potentially allow multiple groups of processes to each have their
43 own private synchronization device.
44
45 Note that this hardware *must* be written to using *only* single byte writes.
46 It may be read using 1, 2, 4, or 8 byte loads which must be aligned since
47 this region is treated as cache-inhibited processes should also use a
48 full sync before and after writing to the BSR to ensure all stores and
49 the BSR update have made it to all chips in the system
50*/
51
52/* This is arbitrary number, up to Power6 it's been 17 or fewer */
53#define BSR_MAX_DEVS (32)
54
55struct bsr_dev {
56 u64 bsr_addr; /* Real address */
57 u64 bsr_len; /* length of mem region we can map */
58 unsigned bsr_bytes; /* size of the BSR reg itself */
59 unsigned bsr_stride; /* interval at which BSR repeats in the page */
60 unsigned bsr_type; /* maps to enum below */
61 unsigned bsr_num; /* bsr id number for its type */
62 int bsr_minor;
63
Sonny Raoa0e2f9f2008-11-09 14:15:11 +000064 struct list_head bsr_list;
65
Sonny Raofe9e8d52008-07-08 15:45:11 +100066 dev_t bsr_dev;
67 struct cdev bsr_cdev;
68 struct device *bsr_device;
69 char bsr_name[32];
70
71};
72
Sonny Raoa0e2f9f2008-11-09 14:15:11 +000073static unsigned total_bsr_devs;
74static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
Sonny Raofe9e8d52008-07-08 15:45:11 +100075static struct class *bsr_class;
76static int bsr_major;
77
78enum {
Sonny Raoe4031d52009-06-18 15:14:36 +000079 BSR_8 = 0,
80 BSR_16 = 1,
81 BSR_64 = 2,
82 BSR_128 = 3,
83 BSR_4096 = 4,
84 BSR_UNKNOWN = 5,
85 BSR_MAX = 6,
Sonny Raofe9e8d52008-07-08 15:45:11 +100086};
87
88static unsigned bsr_types[BSR_MAX];
89
90static ssize_t
91bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
92{
93 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
94 return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
95}
96
97static ssize_t
98bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
99{
100 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
101 return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
102}
103
104static ssize_t
105bsr_len_show(struct device *dev, struct device_attribute *attr, char *buf)
106{
107 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
Stephen Rothwell19015152009-01-06 14:28:48 +0000108 return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000109}
110
111static struct device_attribute bsr_dev_attrs[] = {
112 __ATTR(bsr_size, S_IRUGO, bsr_size_show, NULL),
113 __ATTR(bsr_stride, S_IRUGO, bsr_stride_show, NULL),
114 __ATTR(bsr_length, S_IRUGO, bsr_len_show, NULL),
115 __ATTR_NULL
116};
117
118static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
119{
120 unsigned long size = vma->vm_end - vma->vm_start;
121 struct bsr_dev *dev = filp->private_data;
Sonny Rao04a85d12009-06-18 15:13:04 +0000122 int ret;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000123
Sonny Raofe9e8d52008-07-08 15:45:11 +1000124 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
125
Sonny Rao04a85d12009-06-18 15:13:04 +0000126 /* check for the case of a small BSR device and map one 4k page for it*/
127 if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
128 ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
129 vma->vm_page_prot);
130 else if (size <= dev->bsr_len)
131 ret = io_remap_pfn_range(vma, vma->vm_start,
132 dev->bsr_addr >> PAGE_SHIFT,
133 size, vma->vm_page_prot);
134 else
135 return -EINVAL;
136
137 if (ret)
Sonny Raofe9e8d52008-07-08 15:45:11 +1000138 return -EAGAIN;
139
140 return 0;
141}
142
143static int bsr_open(struct inode * inode, struct file * filp)
144{
145 struct cdev *cdev = inode->i_cdev;
146 struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev);
147
148 filp->private_data = dev;
149 return 0;
150}
151
Tobias Klauser27157a72009-02-09 22:01:08 +0100152static const struct file_operations bsr_fops = {
Sonny Raofe9e8d52008-07-08 15:45:11 +1000153 .owner = THIS_MODULE,
154 .mmap = bsr_mmap,
155 .open = bsr_open,
156};
157
158static void bsr_cleanup_devs(void)
159{
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000160 struct bsr_dev *cur, *n;
161
162 list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
Sonny Raofe9e8d52008-07-08 15:45:11 +1000163 if (cur->bsr_device) {
164 cdev_del(&cur->bsr_cdev);
165 device_del(cur->bsr_device);
166 }
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000167 list_del(&cur->bsr_list);
168 kfree(cur);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000169 }
Sonny Raofe9e8d52008-07-08 15:45:11 +1000170}
171
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000172static int bsr_add_node(struct device_node *bn)
Sonny Raofe9e8d52008-07-08 15:45:11 +1000173{
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000174 int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000175 const u32 *bsr_stride;
176 const u32 *bsr_bytes;
177 unsigned i;
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000178 int ret = -ENODEV;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000179
180 bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
181 bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
182
183 if (!bsr_stride || !bsr_bytes ||
184 (bsr_stride_len != bsr_bytes_len)) {
185 printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000186 return ret;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000187 }
188
189 num_bsr_devs = bsr_bytes_len / sizeof(u32);
190
Sonny Raofe9e8d52008-07-08 15:45:11 +1000191 for (i = 0 ; i < num_bsr_devs; i++) {
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000192 struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
193 GFP_KERNEL);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000194 struct resource res;
195 int result;
196
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000197 if (!cur) {
198 printk(KERN_ERR "Unable to alloc bsr dev\n");
199 ret = -ENOMEM;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000200 goto out_err;
201 }
202
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000203 result = of_address_to_resource(bn, i, &res);
204 if (result < 0) {
205 printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
206 kfree(cur);
207 continue;
208 }
209
210 cur->bsr_minor = i + total_bsr_devs;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000211 cur->bsr_addr = res.start;
212 cur->bsr_len = res.end - res.start + 1;
213 cur->bsr_bytes = bsr_bytes[i];
214 cur->bsr_stride = bsr_stride[i];
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000215 cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000216
Sonny Rao04a85d12009-06-18 15:13:04 +0000217 /* if we have a bsr_len of > 4k and less then PAGE_SIZE (64k pages) */
218 /* we can only map 4k of it, so only advertise the 4k in sysfs */
219 if (cur->bsr_len > 4096 && cur->bsr_len < PAGE_SIZE)
220 cur->bsr_len = 4096;
221
Sonny Raofe9e8d52008-07-08 15:45:11 +1000222 switch(cur->bsr_bytes) {
223 case 8:
224 cur->bsr_type = BSR_8;
225 break;
226 case 16:
227 cur->bsr_type = BSR_16;
228 break;
229 case 64:
230 cur->bsr_type = BSR_64;
231 break;
232 case 128:
233 cur->bsr_type = BSR_128;
234 break;
Sonny Raoe4031d52009-06-18 15:14:36 +0000235 case 4096:
236 cur->bsr_type = BSR_4096;
237 break;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000238 default:
239 cur->bsr_type = BSR_UNKNOWN;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000240 }
241
242 cur->bsr_num = bsr_types[cur->bsr_type];
Sonny Raofe9e8d52008-07-08 15:45:11 +1000243 snprintf(cur->bsr_name, 32, "bsr%d_%d",
244 cur->bsr_bytes, cur->bsr_num);
245
246 cdev_init(&cur->bsr_cdev, &bsr_fops);
247 result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000248 if (result) {
249 kfree(cur);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000250 goto out_err;
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000251 }
Sonny Raofe9e8d52008-07-08 15:45:11 +1000252
Greg Kroah-Hartman03457cd2008-07-21 20:03:34 -0700253 cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
254 cur, cur->bsr_name);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000255 if (!cur->bsr_device) {
256 printk(KERN_ERR "device_create failed for %s\n",
257 cur->bsr_name);
258 cdev_del(&cur->bsr_cdev);
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000259 kfree(cur);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000260 goto out_err;
261 }
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000262
263 bsr_types[cur->bsr_type] = cur->bsr_num + 1;
264 list_add_tail(&cur->bsr_list, &bsr_devs);
Sonny Raofe9e8d52008-07-08 15:45:11 +1000265 }
266
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000267 total_bsr_devs += num_bsr_devs;
268
Sonny Raofe9e8d52008-07-08 15:45:11 +1000269 return 0;
270
271 out_err:
272
273 bsr_cleanup_devs();
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000274 return ret;
275}
276
277static int bsr_create_devs(struct device_node *bn)
278{
279 int ret;
280
281 while (bn) {
282 ret = bsr_add_node(bn);
283 if (ret) {
284 of_node_put(bn);
285 return ret;
286 }
287 bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
288 }
289 return 0;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000290}
291
292static int __init bsr_init(void)
293{
294 struct device_node *np;
295 dev_t bsr_dev = MKDEV(bsr_major, 0);
296 int ret = -ENODEV;
297 int result;
298
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000299 np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
Sonny Raofe9e8d52008-07-08 15:45:11 +1000300 if (!np)
301 goto out_err;
302
303 bsr_class = class_create(THIS_MODULE, "bsr");
304 if (IS_ERR(bsr_class)) {
305 printk(KERN_ERR "class_create() failed for bsr_class\n");
306 goto out_err_1;
307 }
308 bsr_class->dev_attrs = bsr_dev_attrs;
309
310 result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
311 bsr_major = MAJOR(bsr_dev);
312 if (result < 0) {
313 printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
314 goto out_err_2;
315 }
316
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000317 if ((ret = bsr_create_devs(np)) < 0) {
318 np = NULL;
Sonny Raofe9e8d52008-07-08 15:45:11 +1000319 goto out_err_3;
Sonny Raoa0e2f9f2008-11-09 14:15:11 +0000320 }
Sonny Raofe9e8d52008-07-08 15:45:11 +1000321
322 return 0;
323
324 out_err_3:
325 unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
326
327 out_err_2:
328 class_destroy(bsr_class);
329
330 out_err_1:
331 of_node_put(np);
332
333 out_err:
334
335 return ret;
336}
337
338static void __exit bsr_exit(void)
339{
340
341 bsr_cleanup_devs();
342
343 if (bsr_class)
344 class_destroy(bsr_class);
345
346 if (bsr_major)
347 unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
348}
349
350module_init(bsr_init);
351module_exit(bsr_exit);
352MODULE_LICENSE("GPL");
353MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");