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>"); | ||
