diff options
-rw-r--r-- | arch/powerpc/configs/pseries_defconfig | 1 | ||||
-rw-r--r-- | drivers/char/Kconfig | 8 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/bsr.c | 312 |
4 files changed, 322 insertions, 0 deletions
diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index adaa05fb0478..fe6ffa683d78 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig | |||
@@ -946,6 +946,7 @@ CONFIG_HVC_DRIVER=y | |||
946 | CONFIG_HVC_CONSOLE=y | 946 | CONFIG_HVC_CONSOLE=y |
947 | CONFIG_HVC_RTAS=y | 947 | CONFIG_HVC_RTAS=y |
948 | CONFIG_HVCS=m | 948 | CONFIG_HVCS=m |
949 | CONFIG_IBM_BSR=m | ||
949 | # CONFIG_IPMI_HANDLER is not set | 950 | # CONFIG_IPMI_HANDLER is not set |
950 | # CONFIG_HW_RANDOM is not set | 951 | # CONFIG_HW_RANDOM is not set |
951 | CONFIG_GEN_RTC=y | 952 | CONFIG_GEN_RTC=y |
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 2d854bb9373e..650e6b44ce65 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
@@ -649,6 +649,14 @@ config HVCS | |||
649 | which will also be compiled when this driver is built as a | 649 | which will also be compiled when this driver is built as a |
650 | module. | 650 | module. |
651 | 651 | ||
652 | config IBM_BSR | ||
653 | tristate "IBM POWER Barrier Synchronization Register support" | ||
654 | depends on PPC_PSERIES | ||
655 | help | ||
656 | This devices exposes a hardware mechanism for fast synchronization | ||
657 | of threads across a large system which avoids bouncing a cacheline | ||
658 | between several cores on a system | ||
659 | |||
652 | source "drivers/char/ipmi/Kconfig" | 660 | source "drivers/char/ipmi/Kconfig" |
653 | 661 | ||
654 | config DS1620 | 662 | config DS1620 |
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 4c1c584e9eb6..d38ac5030763 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile | |||
@@ -57,6 +57,7 @@ obj-$(CONFIG_MMTIMER) += mmtimer.o | |||
57 | obj-$(CONFIG_VIOCONS) += viocons.o | 57 | obj-$(CONFIG_VIOCONS) += viocons.o |
58 | obj-$(CONFIG_VIOTAPE) += viotape.o | 58 | obj-$(CONFIG_VIOTAPE) += viotape.o |
59 | obj-$(CONFIG_HVCS) += hvcs.o | 59 | obj-$(CONFIG_HVCS) += hvcs.o |
60 | obj-$(CONFIG_IBM_BSR) += bsr.o | ||
60 | obj-$(CONFIG_SGI_MBCS) += mbcs.o | 61 | obj-$(CONFIG_SGI_MBCS) += mbcs.o |
61 | obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o | 62 | obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o |
62 | obj-$(CONFIG_BFIN_OTP) += bfin-otp.o | 63 | obj-$(CONFIG_BFIN_OTP) += bfin-otp.o |
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c new file mode 100644 index 000000000000..b650b4e48e50 --- /dev/null +++ b/drivers/char/bsr.c | |||
@@ -0,0 +1,312 @@ | |||
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_drvdata(bsr_class, NULL, | ||
233 | cur->bsr_dev, | ||
234 | cur, cur->bsr_name); | ||
235 | if (!cur->bsr_device) { | ||
236 | printk(KERN_ERR "device_create failed for %s\n", | ||
237 | cur->bsr_name); | ||
238 | cdev_del(&cur->bsr_cdev); | ||
239 | goto out_err; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | return 0; | ||
244 | |||
245 | out_err: | ||
246 | |||
247 | bsr_cleanup_devs(); | ||
248 | return -ENODEV; | ||
249 | } | ||
250 | |||
251 | static int __init bsr_init(void) | ||
252 | { | ||
253 | struct device_node *np; | ||
254 | dev_t bsr_dev = MKDEV(bsr_major, 0); | ||
255 | int ret = -ENODEV; | ||
256 | int result; | ||
257 | |||
258 | np = of_find_compatible_node(NULL, "ibm,bsr", "ibm,bsr"); | ||
259 | if (!np) | ||
260 | goto out_err; | ||
261 | |||
262 | bsr_class = class_create(THIS_MODULE, "bsr"); | ||
263 | if (IS_ERR(bsr_class)) { | ||
264 | printk(KERN_ERR "class_create() failed for bsr_class\n"); | ||
265 | goto out_err_1; | ||
266 | } | ||
267 | bsr_class->dev_attrs = bsr_dev_attrs; | ||
268 | |||
269 | result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr"); | ||
270 | bsr_major = MAJOR(bsr_dev); | ||
271 | if (result < 0) { | ||
272 | printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n"); | ||
273 | goto out_err_2; | ||
274 | } | ||
275 | |||
276 | if ((ret = bsr_create_devs(np)) < 0) | ||
277 | goto out_err_3; | ||
278 | |||
279 | of_node_put(np); | ||
280 | |||
281 | return 0; | ||
282 | |||
283 | out_err_3: | ||
284 | unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS); | ||
285 | |||
286 | out_err_2: | ||
287 | class_destroy(bsr_class); | ||
288 | |||
289 | out_err_1: | ||
290 | of_node_put(np); | ||
291 | |||
292 | out_err: | ||
293 | |||
294 | return ret; | ||
295 | } | ||
296 | |||
297 | static void __exit bsr_exit(void) | ||
298 | { | ||
299 | |||
300 | bsr_cleanup_devs(); | ||
301 | |||
302 | if (bsr_class) | ||
303 | class_destroy(bsr_class); | ||
304 | |||
305 | if (bsr_major) | ||
306 | unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS); | ||
307 | } | ||
308 | |||
309 | module_init(bsr_init); | ||
310 | module_exit(bsr_exit); | ||
311 | MODULE_LICENSE("GPL"); | ||
312 | MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>"); | ||