linux/drivers/char/bsr.c
<<
>>
Prefs
   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/pgtable.h>
  31#include <asm/io.h>
  32
  33/*
  34 This driver exposes a special register which can be used for fast
  35 synchronization across a large SMP machine.  The hardware is exposed
  36 as an array of bytes where each process will write to one of the bytes to
  37 indicate it has finished the current stage and this update is broadcast to
  38 all processors without having to bounce a cacheline between them. In
  39 POWER5 and POWER6 there is one of these registers per SMP,  but it is
  40 presented in two forms; first, it is given as a whole and then as a number
  41 of smaller registers which alias to parts of the single whole register.
  42 This can potentially allow multiple groups of processes to each have their
  43 own private synchronization device.
  44
  45 Note that this hardware *must* be written to using *only* single byte writes.
  46 It may be read using 1, 2, 4, or 8 byte loads which must be aligned since
  47 this region is treated as cache-inhibited  processes should also use a
  48 full sync before and after writing to the BSR to ensure all stores and
  49 the BSR update have made it to all chips in the system
  50*/
  51
  52/* This is arbitrary number, up to Power6 it's been 17 or fewer  */
  53#define BSR_MAX_DEVS (32)
  54
  55struct bsr_dev {
  56        u64      bsr_addr;     /* Real address */
  57        u64      bsr_len;      /* length of mem region we can map */
  58        unsigned bsr_bytes;    /* size of the BSR reg itself */
  59        unsigned bsr_stride;   /* interval at which BSR repeats in the page */
  60        unsigned bsr_type;     /* maps to enum below */
  61        unsigned bsr_num;      /* bsr id number for its type */
  62        int      bsr_minor;
  63
  64        struct list_head bsr_list;
  65
  66        dev_t    bsr_dev;
  67        struct cdev bsr_cdev;
  68        struct device *bsr_device;
  69        char     bsr_name[32];
  70
  71};
  72
  73static unsigned total_bsr_devs;
  74static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
  75static struct class *bsr_class;
  76static int bsr_major;
  77
  78enum {
  79        BSR_8    = 0,
  80        BSR_16   = 1,
  81        BSR_64   = 2,
  82        BSR_128  = 3,
  83        BSR_4096 = 4,
  84        BSR_UNKNOWN = 5,
  85        BSR_MAX  = 6,
  86};
  87
  88static unsigned bsr_types[BSR_MAX];
  89
  90static ssize_t
  91bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
  92{
  93        struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
  94        return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
  95}
  96
  97static ssize_t
  98bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
  99{
 100        struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
 101        return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
 102}
 103
 104static ssize_t
 105bsr_len_show(struct device *dev, struct device_attribute *attr, char *buf)
 106{
 107        struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
 108        return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
 109}
 110
 111static struct device_attribute bsr_dev_attrs[] = {
 112        __ATTR(bsr_size, S_IRUGO, bsr_size_show, NULL),
 113        __ATTR(bsr_stride, S_IRUGO, bsr_stride_show, NULL),
 114        __ATTR(bsr_length, S_IRUGO, bsr_len_show, NULL),
 115        __ATTR_NULL
 116};
 117
 118static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
 119{
 120        unsigned long size   = vma->vm_end - vma->vm_start;
 121        struct bsr_dev *dev = filp->private_data;
 122        int ret;
 123
 124        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 125
 126        /* check for the case of a small BSR device and map one 4k page for it*/
 127        if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
 128                ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
 129                                   vma->vm_page_prot);
 130        else if (size <= dev->bsr_len)
 131                ret = io_remap_pfn_range(vma, vma->vm_start,
 132                                         dev->bsr_addr >> PAGE_SHIFT,
 133                                         size, vma->vm_page_prot);
 134        else
 135                return -EINVAL;
 136
 137        if (ret)
 138                return -EAGAIN;
 139
 140        return 0;
 141}
 142
 143static int bsr_open(struct inode * inode, struct file * filp)
 144{
 145        struct cdev *cdev = inode->i_cdev;
 146        struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev);
 147
 148        filp->private_data = dev;
 149        return 0;
 150}
 151
 152static const struct file_operations bsr_fops = {
 153        .owner = THIS_MODULE,
 154        .mmap  = bsr_mmap,
 155        .open  = bsr_open,
 156};
 157
 158static void bsr_cleanup_devs(void)
 159{
 160        struct bsr_dev *cur, *n;
 161
 162        list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
 163                if (cur->bsr_device) {
 164                        cdev_del(&cur->bsr_cdev);
 165                        device_del(cur->bsr_device);
 166                }
 167                list_del(&cur->bsr_list);
 168                kfree(cur);
 169        }
 170}
 171
 172static int bsr_add_node(struct device_node *bn)
 173{
 174        int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
 175        const u32 *bsr_stride;
 176        const u32 *bsr_bytes;
 177        unsigned i;
 178        int ret = -ENODEV;
 179
 180        bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
 181        bsr_bytes  = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
 182
 183        if (!bsr_stride || !bsr_bytes ||
 184            (bsr_stride_len != bsr_bytes_len)) {
 185                printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
 186                return ret;
 187        }
 188
 189        num_bsr_devs = bsr_bytes_len / sizeof(u32);
 190
 191        for (i = 0 ; i < num_bsr_devs; i++) {
 192                struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
 193                                              GFP_KERNEL);
 194                struct resource res;
 195                int result;
 196
 197                if (!cur) {
 198                        printk(KERN_ERR "Unable to alloc bsr dev\n");
 199                        ret = -ENOMEM;
 200                        goto out_err;
 201                }
 202
 203                result = of_address_to_resource(bn, i, &res);
 204                if (result < 0) {
 205                        printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
 206                        kfree(cur);
 207                        continue;
 208                }
 209
 210                cur->bsr_minor  = i + total_bsr_devs;
 211                cur->bsr_addr   = res.start;
 212                cur->bsr_len    = res.end - res.start + 1;
 213                cur->bsr_bytes  = bsr_bytes[i];
 214                cur->bsr_stride = bsr_stride[i];
 215                cur->bsr_dev    = MKDEV(bsr_major, i + total_bsr_devs);
 216
 217                /* if we have a bsr_len of > 4k and less then PAGE_SIZE (64k pages) */
 218                /* we can only map 4k of it, so only advertise the 4k in sysfs */
 219                if (cur->bsr_len > 4096 && cur->bsr_len < PAGE_SIZE)
 220                        cur->bsr_len = 4096;
 221
 222                switch(cur->bsr_bytes) {
 223                case 8:
 224                        cur->bsr_type = BSR_8;
 225                        break;
 226                case 16:
 227                        cur->bsr_type = BSR_16;
 228                        break;
 229                case 64:
 230                        cur->bsr_type = BSR_64;
 231                        break;
 232                case 128:
 233                        cur->bsr_type = BSR_128;
 234                        break;
 235                case 4096:
 236                        cur->bsr_type = BSR_4096;
 237                        break;
 238                default:
 239                        cur->bsr_type = BSR_UNKNOWN;
 240                }
 241
 242                cur->bsr_num = bsr_types[cur->bsr_type];
 243                snprintf(cur->bsr_name, 32, "bsr%d_%d",
 244                         cur->bsr_bytes, cur->bsr_num);
 245
 246                cdev_init(&cur->bsr_cdev, &bsr_fops);
 247                result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
 248                if (result) {
 249                        kfree(cur);
 250                        goto out_err;
 251                }
 252
 253                cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
 254                                                cur, cur->bsr_name);
 255                if (!cur->bsr_device) {
 256                        printk(KERN_ERR "device_create failed for %s\n",
 257                               cur->bsr_name);
 258                        cdev_del(&cur->bsr_cdev);
 259                        kfree(cur);
 260                        goto out_err;
 261                }
 262
 263                bsr_types[cur->bsr_type] = cur->bsr_num + 1;
 264                list_add_tail(&cur->bsr_list, &bsr_devs);
 265        }
 266
 267        total_bsr_devs += num_bsr_devs;
 268
 269        return 0;
 270
 271 out_err:
 272
 273        bsr_cleanup_devs();
 274        return ret;
 275}
 276
 277static int bsr_create_devs(struct device_node *bn)
 278{
 279        int ret;
 280
 281        while (bn) {
 282                ret = bsr_add_node(bn);
 283                if (ret) {
 284                        of_node_put(bn);
 285                        return ret;
 286                }
 287                bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
 288        }
 289        return 0;
 290}
 291
 292static int __init bsr_init(void)
 293{
 294        struct device_node *np;
 295        dev_t bsr_dev = MKDEV(bsr_major, 0);
 296        int ret = -ENODEV;
 297        int result;
 298
 299        np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
 300        if (!np)
 301                goto out_err;
 302
 303        bsr_class = class_create(THIS_MODULE, "bsr");
 304        if (IS_ERR(bsr_class)) {
 305                printk(KERN_ERR "class_create() failed for bsr_class\n");
 306                goto out_err_1;
 307        }
 308        bsr_class->dev_attrs = bsr_dev_attrs;
 309
 310        result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
 311        bsr_major = MAJOR(bsr_dev);
 312        if (result < 0) {
 313                printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
 314                goto out_err_2;
 315        }
 316
 317        if ((ret = bsr_create_devs(np)) < 0) {
 318                np = NULL;
 319                goto out_err_3;
 320        }
 321
 322        return 0;
 323
 324 out_err_3:
 325        unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
 326
 327 out_err_2:
 328        class_destroy(bsr_class);
 329
 330 out_err_1:
 331        of_node_put(np);
 332
 333 out_err:
 334
 335        return ret;
 336}
 337
 338static void __exit  bsr_exit(void)
 339{
 340
 341        bsr_cleanup_devs();
 342
 343        if (bsr_class)
 344                class_destroy(bsr_class);
 345
 346        if (bsr_major)
 347                unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
 348}
 349
 350module_init(bsr_init);
 351module_exit(bsr_exit);
 352MODULE_LICENSE("GPL");
 353MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");
 354