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