xref: /linux/drivers/char/bsr.c (revision 6ee738610f41b59733f63718f0bdbcba7d3a3f12)
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/pgtable.h>
31 #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 
55 struct 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 
64 	struct list_head bsr_list;
65 
66 	dev_t    bsr_dev;
67 	struct cdev bsr_cdev;
68 	struct device *bsr_device;
69 	char     bsr_name[32];
70 
71 };
72 
73 static unsigned total_bsr_devs;
74 static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
75 static struct class *bsr_class;
76 static int bsr_major;
77 
78 enum {
79 	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,
86 };
87 
88 static unsigned bsr_types[BSR_MAX];
89 
90 static ssize_t
91 bsr_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 
97 static ssize_t
98 bsr_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 
104 static ssize_t
105 bsr_len_show(struct device *dev, struct device_attribute *attr, char *buf)
106 {
107 	struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
108 	return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
109 }
110 
111 static 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 
118 static 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;
122 	int ret;
123 
124 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
125 
126 	/* 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)
138 		return -EAGAIN;
139 
140 	return 0;
141 }
142 
143 static 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 
152 static const struct file_operations bsr_fops = {
153 	.owner = THIS_MODULE,
154 	.mmap  = bsr_mmap,
155 	.open  = bsr_open,
156 };
157 
158 static void bsr_cleanup_devs(void)
159 {
160 	struct bsr_dev *cur, *n;
161 
162 	list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
163 		if (cur->bsr_device) {
164 			cdev_del(&cur->bsr_cdev);
165 			device_del(cur->bsr_device);
166 		}
167 		list_del(&cur->bsr_list);
168 		kfree(cur);
169 	}
170 }
171 
172 static int bsr_add_node(struct device_node *bn)
173 {
174 	int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
175 	const u32 *bsr_stride;
176 	const u32 *bsr_bytes;
177 	unsigned i;
178 	int ret = -ENODEV;
179 
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");
186 		return ret;
187 	}
188 
189 	num_bsr_devs = bsr_bytes_len / sizeof(u32);
190 
191 	for (i = 0 ; i < num_bsr_devs; i++) {
192 		struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
193 					      GFP_KERNEL);
194 		struct resource res;
195 		int result;
196 
197 		if (!cur) {
198 			printk(KERN_ERR "Unable to alloc bsr dev\n");
199 			ret = -ENOMEM;
200 			goto out_err;
201 		}
202 
203 		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;
211 		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];
215 		cur->bsr_dev    = MKDEV(bsr_major, i + total_bsr_devs);
216 
217 		/* 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 
222 		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;
235 		case 4096:
236 			cur->bsr_type = BSR_4096;
237 			break;
238 		default:
239 			cur->bsr_type = BSR_UNKNOWN;
240 		}
241 
242 		cur->bsr_num = bsr_types[cur->bsr_type];
243 		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);
248 		if (result) {
249 			kfree(cur);
250 			goto out_err;
251 		}
252 
253 		cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
254 						cur, cur->bsr_name);
255 		if (!cur->bsr_device) {
256 			printk(KERN_ERR "device_create failed for %s\n",
257 			       cur->bsr_name);
258 			cdev_del(&cur->bsr_cdev);
259 			kfree(cur);
260 			goto out_err;
261 		}
262 
263 		bsr_types[cur->bsr_type] = cur->bsr_num + 1;
264 		list_add_tail(&cur->bsr_list, &bsr_devs);
265 	}
266 
267 	total_bsr_devs += num_bsr_devs;
268 
269 	return 0;
270 
271  out_err:
272 
273 	bsr_cleanup_devs();
274 	return ret;
275 }
276 
277 static 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;
290 }
291 
292 static 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 
299 	np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
300 	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 
317 	if ((ret = bsr_create_devs(np)) < 0) {
318 		np = NULL;
319 		goto out_err_3;
320 	}
321 
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 
338 static 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 
350 module_init(bsr_init);
351 module_exit(bsr_exit);
352 MODULE_LICENSE("GPL");
353 MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");
354