xref: /openbmc/linux/drivers/char/bsr.c (revision 9ac8d3fb)
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>");
312