Merge git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm
[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(bsr_class, NULL, cur->bsr_dev,
233                                                 cur, cur->bsr_name);
234                 if (!cur->bsr_device) {
235                         printk(KERN_ERR "device_create failed for %s\n",
236                                cur->bsr_name);
237                         cdev_del(&cur->bsr_cdev);
238                         goto out_err;
239                 }
240         }
241
242         return 0;
243
244  out_err:
245
246         bsr_cleanup_devs();
247         return -ENODEV;
248 }
249
250 static int __init bsr_init(void)
251 {
252         struct device_node *np;
253         dev_t bsr_dev = MKDEV(bsr_major, 0);
254         int ret = -ENODEV;
255         int result;
256
257         np = of_find_compatible_node(NULL, "ibm,bsr", "ibm,bsr");
258         if (!np)
259                 goto out_err;
260
261         bsr_class = class_create(THIS_MODULE, "bsr");
262         if (IS_ERR(bsr_class)) {
263                 printk(KERN_ERR "class_create() failed for bsr_class\n");
264                 goto out_err_1;
265         }
266         bsr_class->dev_attrs = bsr_dev_attrs;
267
268         result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
269         bsr_major = MAJOR(bsr_dev);
270         if (result < 0) {
271                 printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
272                 goto out_err_2;
273         }
274
275         if ((ret = bsr_create_devs(np)) < 0)
276                 goto out_err_3;
277
278         of_node_put(np);
279
280         return 0;
281
282  out_err_3:
283         unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
284
285  out_err_2:
286         class_destroy(bsr_class);
287
288  out_err_1:
289         of_node_put(np);
290
291  out_err:
292
293         return ret;
294 }
295
296 static void __exit  bsr_exit(void)
297 {
298
299         bsr_cleanup_devs();
300
301         if (bsr_class)
302                 class_destroy(bsr_class);
303
304         if (bsr_major)
305                 unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
306 }
307
308 module_init(bsr_init);
309 module_exit(bsr_exit);
310 MODULE_LICENSE("GPL");
311 MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");