linux/drivers/char/tile-srom.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 *
  14 * SPI Flash ROM driver
  15 *
  16 * This source code is derived from code provided in "Linux Device
  17 * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and
  18 * Greg Kroah-Hartman, published by O'Reilly Media, Inc.
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/moduleparam.h>
  23#include <linux/init.h>
  24#include <linux/kernel.h>       /* printk() */
  25#include <linux/slab.h>         /* kmalloc() */
  26#include <linux/fs.h>           /* everything... */
  27#include <linux/errno.h>        /* error codes */
  28#include <linux/types.h>        /* size_t */
  29#include <linux/proc_fs.h>
  30#include <linux/fcntl.h>        /* O_ACCMODE */
  31#include <linux/aio.h>
  32#include <linux/pagemap.h>
  33#include <linux/hugetlb.h>
  34#include <linux/uaccess.h>
  35#include <linux/platform_device.h>
  36#include <hv/hypervisor.h>
  37#include <linux/ioctl.h>
  38#include <linux/cdev.h>
  39#include <linux/delay.h>
  40#include <hv/drv_srom_intf.h>
  41
  42/*
  43 * Size of our hypervisor I/O requests.  We break up large transfers
  44 * so that we don't spend large uninterrupted spans of time in the
  45 * hypervisor.  Erasing an SROM sector takes a significant fraction of
  46 * a second, so if we allowed the user to, say, do one I/O to write the
  47 * entire ROM, we'd get soft lockup timeouts, or worse.
  48 */
  49#define SROM_CHUNK_SIZE ((size_t)4096)
  50
  51/*
  52 * When hypervisor is busy (e.g. erasing), poll the status periodically.
  53 */
  54
  55/*
  56 * Interval to poll the state in msec
  57 */
  58#define SROM_WAIT_TRY_INTERVAL 20
  59
  60/*
  61 * Maximum times to poll the state
  62 */
  63#define SROM_MAX_WAIT_TRY_TIMES 1000
  64
  65struct srom_dev {
  66        int hv_devhdl;                  /* Handle for hypervisor device */
  67        u32 total_size;                 /* Size of this device */
  68        u32 sector_size;                /* Size of a sector */
  69        u32 page_size;                  /* Size of a page */
  70        struct mutex lock;              /* Allow only one accessor at a time */
  71};
  72
  73static int srom_major;                  /* Dynamic major by default */
  74module_param(srom_major, int, 0);
  75MODULE_AUTHOR("Tilera Corporation");
  76MODULE_LICENSE("GPL");
  77
  78static int srom_devs;                   /* Number of SROM partitions */
  79static struct cdev srom_cdev;
  80static struct class *srom_class;
  81static struct srom_dev *srom_devices;
  82
  83/*
  84 * Handle calling the hypervisor and managing EAGAIN/EBUSY.
  85 */
  86
  87static ssize_t _srom_read(int hv_devhdl, void *buf,
  88                          loff_t off, size_t count)
  89{
  90        int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
  91        for (;;) {
  92                retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf,
  93                                      count, off);
  94                if (retval >= 0)
  95                        return retval;
  96                if (retval == HV_EAGAIN)
  97                        continue;
  98                if (retval == HV_EBUSY && --retries > 0) {
  99                        msleep(SROM_WAIT_TRY_INTERVAL);
 100                        continue;
 101                }
 102                pr_err("_srom_read: error %d\n", retval);
 103                return -EIO;
 104        }
 105}
 106
 107static ssize_t _srom_write(int hv_devhdl, const void *buf,
 108                           loff_t off, size_t count)
 109{
 110        int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
 111        for (;;) {
 112                retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf,
 113                                       count, off);
 114                if (retval >= 0)
 115                        return retval;
 116                if (retval == HV_EAGAIN)
 117                        continue;
 118                if (retval == HV_EBUSY && --retries > 0) {
 119                        msleep(SROM_WAIT_TRY_INTERVAL);
 120                        continue;
 121                }
 122                pr_err("_srom_write: error %d\n", retval);
 123                return -EIO;
 124        }
 125}
 126
 127/**
 128 * srom_open() - Device open routine.
 129 * @inode: Inode for this device.
 130 * @filp: File for this specific open of the device.
 131 *
 132 * Returns zero, or an error code.
 133 */
 134static int srom_open(struct inode *inode, struct file *filp)
 135{
 136        filp->private_data = &srom_devices[iminor(inode)];
 137        return 0;
 138}
 139
 140
 141/**
 142 * srom_release() - Device release routine.
 143 * @inode: Inode for this device.
 144 * @filp: File for this specific open of the device.
 145 *
 146 * Returns zero, or an error code.
 147 */
 148static int srom_release(struct inode *inode, struct file *filp)
 149{
 150        struct srom_dev *srom = filp->private_data;
 151        char dummy;
 152
 153        /* Make sure we've flushed anything written to the ROM. */
 154        mutex_lock(&srom->lock);
 155        if (srom->hv_devhdl >= 0)
 156                _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1);
 157        mutex_unlock(&srom->lock);
 158
 159        filp->private_data = NULL;
 160
 161        return 0;
 162}
 163
 164
 165/**
 166 * srom_read() - Read data from the device.
 167 * @filp: File for this specific open of the device.
 168 * @buf: User's data buffer.
 169 * @count: Number of bytes requested.
 170 * @f_pos: File position.
 171 *
 172 * Returns number of bytes read, or an error code.
 173 */
 174static ssize_t srom_read(struct file *filp, char __user *buf,
 175                         size_t count, loff_t *f_pos)
 176{
 177        int retval = 0;
 178        void *kernbuf;
 179        struct srom_dev *srom = filp->private_data;
 180
 181        kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
 182        if (!kernbuf)
 183                return -ENOMEM;
 184
 185        if (mutex_lock_interruptible(&srom->lock)) {
 186                retval = -ERESTARTSYS;
 187                kfree(kernbuf);
 188                return retval;
 189        }
 190
 191        while (count) {
 192                int hv_retval;
 193                int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
 194
 195                hv_retval = _srom_read(srom->hv_devhdl, kernbuf,
 196                                       *f_pos, bytes_this_pass);
 197                if (hv_retval <= 0) {
 198                        if (retval == 0)
 199                                retval = hv_retval;
 200                        break;
 201                }
 202
 203                if (copy_to_user(buf, kernbuf, hv_retval) != 0) {
 204                        retval = -EFAULT;
 205                        break;
 206                }
 207
 208                retval += hv_retval;
 209                *f_pos += hv_retval;
 210                buf += hv_retval;
 211                count -= hv_retval;
 212        }
 213
 214        mutex_unlock(&srom->lock);
 215        kfree(kernbuf);
 216
 217        return retval;
 218}
 219
 220/**
 221 * srom_write() - Write data to the device.
 222 * @filp: File for this specific open of the device.
 223 * @buf: User's data buffer.
 224 * @count: Number of bytes requested.
 225 * @f_pos: File position.
 226 *
 227 * Returns number of bytes written, or an error code.
 228 */
 229static ssize_t srom_write(struct file *filp, const char __user *buf,
 230                          size_t count, loff_t *f_pos)
 231{
 232        int retval = 0;
 233        void *kernbuf;
 234        struct srom_dev *srom = filp->private_data;
 235
 236        kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
 237        if (!kernbuf)
 238                return -ENOMEM;
 239
 240        if (mutex_lock_interruptible(&srom->lock)) {
 241                retval = -ERESTARTSYS;
 242                kfree(kernbuf);
 243                return retval;
 244        }
 245
 246        while (count) {
 247                int hv_retval;
 248                int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
 249
 250                if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) {
 251                        retval = -EFAULT;
 252                        break;
 253                }
 254
 255                hv_retval = _srom_write(srom->hv_devhdl, kernbuf,
 256                                        *f_pos, bytes_this_pass);
 257                if (hv_retval <= 0) {
 258                        if (retval == 0)
 259                                retval = hv_retval;
 260                        break;
 261                }
 262
 263                retval += hv_retval;
 264                *f_pos += hv_retval;
 265                buf += hv_retval;
 266                count -= hv_retval;
 267        }
 268
 269        mutex_unlock(&srom->lock);
 270        kfree(kernbuf);
 271
 272        return retval;
 273}
 274
 275/* Provide our own implementation so we can use srom->total_size. */
 276loff_t srom_llseek(struct file *file, loff_t offset, int origin)
 277{
 278        struct srom_dev *srom = file->private_data;
 279        return fixed_size_llseek(file, offset, origin, srom->total_size);
 280}
 281
 282static ssize_t total_size_show(struct device *dev,
 283                               struct device_attribute *attr, char *buf)
 284{
 285        struct srom_dev *srom = dev_get_drvdata(dev);
 286        return sprintf(buf, "%u\n", srom->total_size);
 287}
 288static DEVICE_ATTR_RO(total_size);
 289
 290static ssize_t sector_size_show(struct device *dev,
 291                                struct device_attribute *attr, char *buf)
 292{
 293        struct srom_dev *srom = dev_get_drvdata(dev);
 294        return sprintf(buf, "%u\n", srom->sector_size);
 295}
 296static DEVICE_ATTR_RO(sector_size);
 297
 298static ssize_t page_size_show(struct device *dev,
 299                              struct device_attribute *attr, char *buf)
 300{
 301        struct srom_dev *srom = dev_get_drvdata(dev);
 302        return sprintf(buf, "%u\n", srom->page_size);
 303}
 304static DEVICE_ATTR_RO(page_size);
 305
 306static struct attribute *srom_dev_attrs[] = {
 307        &dev_attr_total_size.attr,
 308        &dev_attr_sector_size.attr,
 309        &dev_attr_page_size.attr,
 310        NULL,
 311};
 312ATTRIBUTE_GROUPS(srom_dev);
 313
 314static char *srom_devnode(struct device *dev, umode_t *mode)
 315{
 316        *mode = S_IRUGO | S_IWUSR;
 317        return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
 318}
 319
 320/*
 321 * The fops
 322 */
 323static const struct file_operations srom_fops = {
 324        .owner =     THIS_MODULE,
 325        .llseek =    srom_llseek,
 326        .read =      srom_read,
 327        .write =     srom_write,
 328        .open =      srom_open,
 329        .release =   srom_release,
 330};
 331
 332/**
 333 * srom_setup_minor() - Initialize per-minor information.
 334 * @srom: Per-device SROM state.
 335 * @index: Device to set up.
 336 */
 337static int srom_setup_minor(struct srom_dev *srom, int index)
 338{
 339        struct device *dev;
 340        int devhdl = srom->hv_devhdl;
 341
 342        mutex_init(&srom->lock);
 343
 344        if (_srom_read(devhdl, &srom->total_size,
 345                       SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0)
 346                return -EIO;
 347        if (_srom_read(devhdl, &srom->sector_size,
 348                       SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0)
 349                return -EIO;
 350        if (_srom_read(devhdl, &srom->page_size,
 351                       SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
 352                return -EIO;
 353
 354        dev = device_create(srom_class, &platform_bus,
 355                            MKDEV(srom_major, index), srom, "%d", index);
 356        return PTR_ERR_OR_ZERO(dev);
 357}
 358
 359/** srom_init() - Initialize the driver's module. */
 360static int srom_init(void)
 361{
 362        int result, i;
 363        dev_t dev = MKDEV(srom_major, 0);
 364
 365        /*
 366         * Start with a plausible number of partitions; the krealloc() call
 367         * below will yield about log(srom_devs) additional allocations.
 368         */
 369        srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
 370
 371        /* Discover the number of srom partitions. */
 372        for (i = 0; ; i++) {
 373                int devhdl;
 374                char buf[20];
 375                struct srom_dev *new_srom_devices =
 376                        krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
 377                                 GFP_KERNEL | __GFP_ZERO);
 378                if (!new_srom_devices) {
 379                        result = -ENOMEM;
 380                        goto fail_mem;
 381                }
 382                srom_devices = new_srom_devices;
 383                sprintf(buf, "srom/0/%d", i);
 384                devhdl = hv_dev_open((HV_VirtAddr)buf, 0);
 385                if (devhdl < 0) {
 386                        if (devhdl != HV_ENODEV)
 387                                pr_notice("srom/%d: hv_dev_open failed: %d.\n",
 388                                          i, devhdl);
 389                        break;
 390                }
 391                srom_devices[i].hv_devhdl = devhdl;
 392        }
 393        srom_devs = i;
 394
 395        /* Bail out early if we have no partitions at all. */
 396        if (srom_devs == 0) {
 397                result = -ENODEV;
 398                goto fail_mem;
 399        }
 400
 401        /* Register our major, and accept a dynamic number. */
 402        if (srom_major)
 403                result = register_chrdev_region(dev, srom_devs, "srom");
 404        else {
 405                result = alloc_chrdev_region(&dev, 0, srom_devs, "srom");
 406                srom_major = MAJOR(dev);
 407        }
 408        if (result < 0)
 409                goto fail_mem;
 410
 411        /* Register a character device. */
 412        cdev_init(&srom_cdev, &srom_fops);
 413        srom_cdev.owner = THIS_MODULE;
 414        srom_cdev.ops = &srom_fops;
 415        result = cdev_add(&srom_cdev, dev, srom_devs);
 416        if (result < 0)
 417                goto fail_chrdev;
 418
 419        /* Create a sysfs class. */
 420        srom_class = class_create(THIS_MODULE, "srom");
 421        if (IS_ERR(srom_class)) {
 422                result = PTR_ERR(srom_class);
 423                goto fail_cdev;
 424        }
 425        srom_class->dev_groups = srom_dev_groups;
 426        srom_class->devnode = srom_devnode;
 427
 428        /* Do per-partition initialization */
 429        for (i = 0; i < srom_devs; i++) {
 430                result = srom_setup_minor(srom_devices + i, i);
 431                if (result < 0)
 432                        goto fail_class;
 433        }
 434
 435        return 0;
 436
 437fail_class:
 438        for (i = 0; i < srom_devs; i++)
 439                device_destroy(srom_class, MKDEV(srom_major, i));
 440        class_destroy(srom_class);
 441fail_cdev:
 442        cdev_del(&srom_cdev);
 443fail_chrdev:
 444        unregister_chrdev_region(dev, srom_devs);
 445fail_mem:
 446        kfree(srom_devices);
 447        return result;
 448}
 449
 450/** srom_cleanup() - Clean up the driver's module. */
 451static void srom_cleanup(void)
 452{
 453        int i;
 454        for (i = 0; i < srom_devs; i++)
 455                device_destroy(srom_class, MKDEV(srom_major, i));
 456        class_destroy(srom_class);
 457        cdev_del(&srom_cdev);
 458        unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs);
 459        kfree(srom_devices);
 460}
 461
 462module_init(srom_init);
 463module_exit(srom_cleanup);
 464