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