11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2fe9e8d53SSonny Rao /* IBM POWER Barrier Synchronization Register Driver
3fe9e8d53SSonny Rao *
4fe9e8d53SSonny Rao * Copyright IBM Corporation 2008
5fe9e8d53SSonny Rao *
6fe9e8d53SSonny Rao * Author: Sonny Rao <sonnyrao@us.ibm.com>
7fe9e8d53SSonny Rao */
8fe9e8d53SSonny Rao
9*89f6fc9cSRob Herring #include <linux/device.h>
10fe9e8d53SSonny Rao #include <linux/kernel.h>
11fe9e8d53SSonny Rao #include <linux/of.h>
125af50730SRob Herring #include <linux/of_address.h>
1322ae782fSGrant Likely #include <linux/fs.h>
14fe9e8d53SSonny Rao #include <linux/module.h>
15fe9e8d53SSonny Rao #include <linux/cdev.h>
16fe9e8d53SSonny Rao #include <linux/list.h>
17fe9e8d53SSonny Rao #include <linux/mm.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
19fe9e8d53SSonny Rao #include <asm/io.h>
20fe9e8d53SSonny Rao
21fe9e8d53SSonny Rao /*
22fe9e8d53SSonny Rao This driver exposes a special register which can be used for fast
23fe9e8d53SSonny Rao synchronization across a large SMP machine. The hardware is exposed
24fe9e8d53SSonny Rao as an array of bytes where each process will write to one of the bytes to
25fe9e8d53SSonny Rao indicate it has finished the current stage and this update is broadcast to
26fe9e8d53SSonny Rao all processors without having to bounce a cacheline between them. In
27fe9e8d53SSonny Rao POWER5 and POWER6 there is one of these registers per SMP, but it is
28fe9e8d53SSonny Rao presented in two forms; first, it is given as a whole and then as a number
29fe9e8d53SSonny Rao of smaller registers which alias to parts of the single whole register.
30fe9e8d53SSonny Rao This can potentially allow multiple groups of processes to each have their
31fe9e8d53SSonny Rao own private synchronization device.
32fe9e8d53SSonny Rao
33fe9e8d53SSonny Rao Note that this hardware *must* be written to using *only* single byte writes.
34fe9e8d53SSonny Rao It may be read using 1, 2, 4, or 8 byte loads which must be aligned since
35fe9e8d53SSonny Rao this region is treated as cache-inhibited processes should also use a
36fe9e8d53SSonny Rao full sync before and after writing to the BSR to ensure all stores and
37fe9e8d53SSonny Rao the BSR update have made it to all chips in the system
38fe9e8d53SSonny Rao */
39fe9e8d53SSonny Rao
40fe9e8d53SSonny Rao /* This is arbitrary number, up to Power6 it's been 17 or fewer */
41fe9e8d53SSonny Rao #define BSR_MAX_DEVS (32)
42fe9e8d53SSonny Rao
43fe9e8d53SSonny Rao struct bsr_dev {
44fe9e8d53SSonny Rao u64 bsr_addr; /* Real address */
45fe9e8d53SSonny Rao u64 bsr_len; /* length of mem region we can map */
46fe9e8d53SSonny Rao unsigned bsr_bytes; /* size of the BSR reg itself */
47fe9e8d53SSonny Rao unsigned bsr_stride; /* interval at which BSR repeats in the page */
48fe9e8d53SSonny Rao unsigned bsr_type; /* maps to enum below */
49fe9e8d53SSonny Rao unsigned bsr_num; /* bsr id number for its type */
50fe9e8d53SSonny Rao int bsr_minor;
51fe9e8d53SSonny Rao
52a0e2f9f4SSonny Rao struct list_head bsr_list;
53a0e2f9f4SSonny Rao
54fe9e8d53SSonny Rao dev_t bsr_dev;
55fe9e8d53SSonny Rao struct cdev bsr_cdev;
56fe9e8d53SSonny Rao struct device *bsr_device;
57fe9e8d53SSonny Rao char bsr_name[32];
58fe9e8d53SSonny Rao
59fe9e8d53SSonny Rao };
60fe9e8d53SSonny Rao
61a0e2f9f4SSonny Rao static unsigned total_bsr_devs;
62aef3125dSCai Huoqing static LIST_HEAD(bsr_devs);
63fe9e8d53SSonny Rao static int bsr_major;
64fe9e8d53SSonny Rao
65fe9e8d53SSonny Rao enum {
66fe9e8d53SSonny Rao BSR_8 = 0,
67fe9e8d53SSonny Rao BSR_16 = 1,
68fe9e8d53SSonny Rao BSR_64 = 2,
69fe9e8d53SSonny Rao BSR_128 = 3,
70e4031d52SSonny Rao BSR_4096 = 4,
71e4031d52SSonny Rao BSR_UNKNOWN = 5,
72e4031d52SSonny Rao BSR_MAX = 6,
73fe9e8d53SSonny Rao };
74fe9e8d53SSonny Rao
75fe9e8d53SSonny Rao static unsigned bsr_types[BSR_MAX];
76fe9e8d53SSonny Rao
77fe9e8d53SSonny Rao static ssize_t
bsr_size_show(struct device * dev,struct device_attribute * attr,char * buf)78fe9e8d53SSonny Rao bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
79fe9e8d53SSonny Rao {
80fe9e8d53SSonny Rao struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
81fe9e8d53SSonny Rao return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
82fe9e8d53SSonny Rao }
8362e13505SGreg Kroah-Hartman static DEVICE_ATTR_RO(bsr_size);
84fe9e8d53SSonny Rao
85fe9e8d53SSonny Rao static ssize_t
bsr_stride_show(struct device * dev,struct device_attribute * attr,char * buf)86fe9e8d53SSonny Rao bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
87fe9e8d53SSonny Rao {
88fe9e8d53SSonny Rao struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
89fe9e8d53SSonny Rao return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
90fe9e8d53SSonny Rao }
9162e13505SGreg Kroah-Hartman static DEVICE_ATTR_RO(bsr_stride);
92fe9e8d53SSonny Rao
93fe9e8d53SSonny Rao static ssize_t
bsr_length_show(struct device * dev,struct device_attribute * attr,char * buf)9462e13505SGreg Kroah-Hartman bsr_length_show(struct device *dev, struct device_attribute *attr, char *buf)
95fe9e8d53SSonny Rao {
96fe9e8d53SSonny Rao struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
971901515cSStephen Rothwell return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
98fe9e8d53SSonny Rao }
9962e13505SGreg Kroah-Hartman static DEVICE_ATTR_RO(bsr_length);
100fe9e8d53SSonny Rao
10162e13505SGreg Kroah-Hartman static struct attribute *bsr_dev_attrs[] = {
10262e13505SGreg Kroah-Hartman &dev_attr_bsr_size.attr,
10362e13505SGreg Kroah-Hartman &dev_attr_bsr_stride.attr,
10462e13505SGreg Kroah-Hartman &dev_attr_bsr_length.attr,
10562e13505SGreg Kroah-Hartman NULL,
106fe9e8d53SSonny Rao };
10762e13505SGreg Kroah-Hartman ATTRIBUTE_GROUPS(bsr_dev);
108fe9e8d53SSonny Rao
109e55ce9fdSIvan Orlov static const struct class bsr_class = {
110e55ce9fdSIvan Orlov .name = "bsr",
111e55ce9fdSIvan Orlov .dev_groups = bsr_dev_groups,
112e55ce9fdSIvan Orlov };
113e55ce9fdSIvan Orlov
bsr_mmap(struct file * filp,struct vm_area_struct * vma)114fe9e8d53SSonny Rao static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
115fe9e8d53SSonny Rao {
116fe9e8d53SSonny Rao unsigned long size = vma->vm_end - vma->vm_start;
117fe9e8d53SSonny Rao struct bsr_dev *dev = filp->private_data;
11804a85d12SSonny Rao int ret;
119fe9e8d53SSonny Rao
120fe9e8d53SSonny Rao vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
121fe9e8d53SSonny Rao
12204a85d12SSonny Rao /* check for the case of a small BSR device and map one 4k page for it*/
12304a85d12SSonny Rao if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
12404a85d12SSonny Rao ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
12504a85d12SSonny Rao vma->vm_page_prot);
12604a85d12SSonny Rao else if (size <= dev->bsr_len)
12704a85d12SSonny Rao ret = io_remap_pfn_range(vma, vma->vm_start,
12804a85d12SSonny Rao dev->bsr_addr >> PAGE_SHIFT,
12904a85d12SSonny Rao size, vma->vm_page_prot);
13004a85d12SSonny Rao else
13104a85d12SSonny Rao return -EINVAL;
13204a85d12SSonny Rao
13304a85d12SSonny Rao if (ret)
134fe9e8d53SSonny Rao return -EAGAIN;
135fe9e8d53SSonny Rao
136fe9e8d53SSonny Rao return 0;
137fe9e8d53SSonny Rao }
138fe9e8d53SSonny Rao
bsr_open(struct inode * inode,struct file * filp)139fe9e8d53SSonny Rao static int bsr_open(struct inode *inode, struct file *filp)
140fe9e8d53SSonny Rao {
141fe9e8d53SSonny Rao struct cdev *cdev = inode->i_cdev;
142fe9e8d53SSonny Rao struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev);
143fe9e8d53SSonny Rao
144fe9e8d53SSonny Rao filp->private_data = dev;
145fe9e8d53SSonny Rao return 0;
146fe9e8d53SSonny Rao }
147fe9e8d53SSonny Rao
14827157a70STobias Klauser static const struct file_operations bsr_fops = {
149fe9e8d53SSonny Rao .owner = THIS_MODULE,
150fe9e8d53SSonny Rao .mmap = bsr_mmap,
151fe9e8d53SSonny Rao .open = bsr_open,
1526038f373SArnd Bergmann .llseek = noop_llseek,
153fe9e8d53SSonny Rao };
154fe9e8d53SSonny Rao
bsr_cleanup_devs(void)155fe9e8d53SSonny Rao static void bsr_cleanup_devs(void)
156fe9e8d53SSonny Rao {
157a0e2f9f4SSonny Rao struct bsr_dev *cur, *n;
158a0e2f9f4SSonny Rao
159a0e2f9f4SSonny Rao list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
160fe9e8d53SSonny Rao if (cur->bsr_device) {
161fe9e8d53SSonny Rao cdev_del(&cur->bsr_cdev);
162fe9e8d53SSonny Rao device_del(cur->bsr_device);
163fe9e8d53SSonny Rao }
164a0e2f9f4SSonny Rao list_del(&cur->bsr_list);
165a0e2f9f4SSonny Rao kfree(cur);
166a0e2f9f4SSonny Rao }
167fe9e8d53SSonny Rao }
168fe9e8d53SSonny Rao
bsr_add_node(struct device_node * bn)169a0e2f9f4SSonny Rao static int bsr_add_node(struct device_node *bn)
170fe9e8d53SSonny Rao {
171a0e2f9f4SSonny Rao int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
172fe9e8d53SSonny Rao const u32 *bsr_stride;
173fe9e8d53SSonny Rao const u32 *bsr_bytes;
174fe9e8d53SSonny Rao unsigned i;
175a0e2f9f4SSonny Rao int ret = -ENODEV;
176fe9e8d53SSonny Rao
177fe9e8d53SSonny Rao bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
178fe9e8d53SSonny Rao bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
179fe9e8d53SSonny Rao
180fe9e8d53SSonny Rao if (!bsr_stride || !bsr_bytes ||
181fe9e8d53SSonny Rao (bsr_stride_len != bsr_bytes_len)) {
182fe9e8d53SSonny Rao printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
183a0e2f9f4SSonny Rao return ret;
184fe9e8d53SSonny Rao }
185fe9e8d53SSonny Rao
186fe9e8d53SSonny Rao num_bsr_devs = bsr_bytes_len / sizeof(u32);
187fe9e8d53SSonny Rao
188fe9e8d53SSonny Rao for (i = 0 ; i < num_bsr_devs; i++) {
189a0e2f9f4SSonny Rao struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
190a0e2f9f4SSonny Rao GFP_KERNEL);
191fe9e8d53SSonny Rao struct resource res;
192fe9e8d53SSonny Rao int result;
193fe9e8d53SSonny Rao
194a0e2f9f4SSonny Rao if (!cur) {
195a0e2f9f4SSonny Rao printk(KERN_ERR "Unable to alloc bsr dev\n");
196a0e2f9f4SSonny Rao ret = -ENOMEM;
197fe9e8d53SSonny Rao goto out_err;
198fe9e8d53SSonny Rao }
199fe9e8d53SSonny Rao
200a0e2f9f4SSonny Rao result = of_address_to_resource(bn, i, &res);
201a0e2f9f4SSonny Rao if (result < 0) {
202a0e2f9f4SSonny Rao printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
203a0e2f9f4SSonny Rao kfree(cur);
204a0e2f9f4SSonny Rao continue;
205a0e2f9f4SSonny Rao }
206a0e2f9f4SSonny Rao
207a0e2f9f4SSonny Rao cur->bsr_minor = i + total_bsr_devs;
208fe9e8d53SSonny Rao cur->bsr_addr = res.start;
20928f65c11SJoe Perches cur->bsr_len = resource_size(&res);
210fe9e8d53SSonny Rao cur->bsr_bytes = bsr_bytes[i];
211fe9e8d53SSonny Rao cur->bsr_stride = bsr_stride[i];
212a0e2f9f4SSonny Rao cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
213fe9e8d53SSonny Rao
21404a85d12SSonny Rao /* if we have a bsr_len of > 4k and less then PAGE_SIZE (64k pages) */
21504a85d12SSonny Rao /* we can only map 4k of it, so only advertise the 4k in sysfs */
21604a85d12SSonny Rao if (cur->bsr_len > 4096 && cur->bsr_len < PAGE_SIZE)
21704a85d12SSonny Rao cur->bsr_len = 4096;
21804a85d12SSonny Rao
219fe9e8d53SSonny Rao switch(cur->bsr_bytes) {
220fe9e8d53SSonny Rao case 8:
221fe9e8d53SSonny Rao cur->bsr_type = BSR_8;
222fe9e8d53SSonny Rao break;
223fe9e8d53SSonny Rao case 16:
224fe9e8d53SSonny Rao cur->bsr_type = BSR_16;
225fe9e8d53SSonny Rao break;
226fe9e8d53SSonny Rao case 64:
227fe9e8d53SSonny Rao cur->bsr_type = BSR_64;
228fe9e8d53SSonny Rao break;
229fe9e8d53SSonny Rao case 128:
230fe9e8d53SSonny Rao cur->bsr_type = BSR_128;
231fe9e8d53SSonny Rao break;
232e4031d52SSonny Rao case 4096:
233e4031d52SSonny Rao cur->bsr_type = BSR_4096;
234e4031d52SSonny Rao break;
235fe9e8d53SSonny Rao default:
236fe9e8d53SSonny Rao cur->bsr_type = BSR_UNKNOWN;
237fe9e8d53SSonny Rao }
238fe9e8d53SSonny Rao
239fe9e8d53SSonny Rao cur->bsr_num = bsr_types[cur->bsr_type];
240fe9e8d53SSonny Rao snprintf(cur->bsr_name, 32, "bsr%d_%d",
241fe9e8d53SSonny Rao cur->bsr_bytes, cur->bsr_num);
242fe9e8d53SSonny Rao
243fe9e8d53SSonny Rao cdev_init(&cur->bsr_cdev, &bsr_fops);
244fe9e8d53SSonny Rao result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
245a0e2f9f4SSonny Rao if (result) {
246a0e2f9f4SSonny Rao kfree(cur);
247fe9e8d53SSonny Rao goto out_err;
248a0e2f9f4SSonny Rao }
249fe9e8d53SSonny Rao
250e55ce9fdSIvan Orlov cur->bsr_device = device_create(&bsr_class, NULL, cur->bsr_dev,
2514171ee9eSKees Cook cur, "%s", cur->bsr_name);
25230c96ce5SJani Nikula if (IS_ERR(cur->bsr_device)) {
253fe9e8d53SSonny Rao printk(KERN_ERR "device_create failed for %s\n",
254fe9e8d53SSonny Rao cur->bsr_name);
255fe9e8d53SSonny Rao cdev_del(&cur->bsr_cdev);
256a0e2f9f4SSonny Rao kfree(cur);
257fe9e8d53SSonny Rao goto out_err;
258fe9e8d53SSonny Rao }
259a0e2f9f4SSonny Rao
260a0e2f9f4SSonny Rao bsr_types[cur->bsr_type] = cur->bsr_num + 1;
261a0e2f9f4SSonny Rao list_add_tail(&cur->bsr_list, &bsr_devs);
262fe9e8d53SSonny Rao }
263fe9e8d53SSonny Rao
264a0e2f9f4SSonny Rao total_bsr_devs += num_bsr_devs;
265a0e2f9f4SSonny Rao
266fe9e8d53SSonny Rao return 0;
267fe9e8d53SSonny Rao
268fe9e8d53SSonny Rao out_err:
269fe9e8d53SSonny Rao
270fe9e8d53SSonny Rao bsr_cleanup_devs();
271a0e2f9f4SSonny Rao return ret;
272a0e2f9f4SSonny Rao }
273a0e2f9f4SSonny Rao
bsr_create_devs(struct device_node * bn)274a0e2f9f4SSonny Rao static int bsr_create_devs(struct device_node *bn)
275a0e2f9f4SSonny Rao {
276a0e2f9f4SSonny Rao int ret;
277a0e2f9f4SSonny Rao
278a0e2f9f4SSonny Rao while (bn) {
279a0e2f9f4SSonny Rao ret = bsr_add_node(bn);
280a0e2f9f4SSonny Rao if (ret) {
281a0e2f9f4SSonny Rao of_node_put(bn);
282a0e2f9f4SSonny Rao return ret;
283a0e2f9f4SSonny Rao }
284a0e2f9f4SSonny Rao bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
285a0e2f9f4SSonny Rao }
286a0e2f9f4SSonny Rao return 0;
287fe9e8d53SSonny Rao }
288fe9e8d53SSonny Rao
bsr_init(void)289fe9e8d53SSonny Rao static int __init bsr_init(void)
290fe9e8d53SSonny Rao {
291fe9e8d53SSonny Rao struct device_node *np;
292cce36444SRobert P. J. Day dev_t bsr_dev;
293fe9e8d53SSonny Rao int ret = -ENODEV;
294fe9e8d53SSonny Rao
295a0e2f9f4SSonny Rao np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
296fe9e8d53SSonny Rao if (!np)
297fe9e8d53SSonny Rao goto out_err;
298fe9e8d53SSonny Rao
299e55ce9fdSIvan Orlov ret = class_register(&bsr_class);
300adfdaf81SGreg Kroah-Hartman if (ret)
301fe9e8d53SSonny Rao goto out_err_1;
302fe9e8d53SSonny Rao
3038397c76aSDevendra Naga ret = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
304fe9e8d53SSonny Rao bsr_major = MAJOR(bsr_dev);
3058397c76aSDevendra Naga if (ret < 0) {
306fe9e8d53SSonny Rao printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
307fe9e8d53SSonny Rao goto out_err_2;
308fe9e8d53SSonny Rao }
309fe9e8d53SSonny Rao
3107256d7f4SNaveen Kumar Parna ret = bsr_create_devs(np);
3117256d7f4SNaveen Kumar Parna if (ret < 0) {
312a0e2f9f4SSonny Rao np = NULL;
313fe9e8d53SSonny Rao goto out_err_3;
314a0e2f9f4SSonny Rao }
315fe9e8d53SSonny Rao
316fe9e8d53SSonny Rao return 0;
317fe9e8d53SSonny Rao
318fe9e8d53SSonny Rao out_err_3:
319fe9e8d53SSonny Rao unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
320fe9e8d53SSonny Rao
321fe9e8d53SSonny Rao out_err_2:
322e55ce9fdSIvan Orlov class_unregister(&bsr_class);
323fe9e8d53SSonny Rao
324fe9e8d53SSonny Rao out_err_1:
325fe9e8d53SSonny Rao of_node_put(np);
326fe9e8d53SSonny Rao
327fe9e8d53SSonny Rao out_err:
328fe9e8d53SSonny Rao
329fe9e8d53SSonny Rao return ret;
330fe9e8d53SSonny Rao }
331fe9e8d53SSonny Rao
bsr_exit(void)332fe9e8d53SSonny Rao static void __exit bsr_exit(void)
333fe9e8d53SSonny Rao {
334fe9e8d53SSonny Rao
335fe9e8d53SSonny Rao bsr_cleanup_devs();
336fe9e8d53SSonny Rao
337e55ce9fdSIvan Orlov class_unregister(&bsr_class);
338fe9e8d53SSonny Rao
339fe9e8d53SSonny Rao if (bsr_major)
340fe9e8d53SSonny Rao unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
341fe9e8d53SSonny Rao }
342fe9e8d53SSonny Rao
343fe9e8d53SSonny Rao module_init(bsr_init);
344fe9e8d53SSonny Rao module_exit(bsr_exit);
345fe9e8d53SSonny Rao MODULE_LICENSE("GPL");
346fe9e8d53SSonny Rao MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");
347