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 *filp, loff_t offset, int origin)
 277{
 278        struct srom_dev *srom = filp->private_data;
 279
 280        if (mutex_lock_interruptible(&srom->lock))
 281                return -ERESTARTSYS;
 282
 283        switch (origin) {
 284        case SEEK_END:
 285                offset += srom->total_size;
 286                break;
 287        case SEEK_CUR:
 288                offset += filp->f_pos;
 289                break;
 290        }
 291
 292        if (offset < 0 || offset > srom->total_size) {
 293                offset = -EINVAL;
 294        } else {
 295                filp->f_pos = offset;
 296                filp->f_version = 0;
 297        }
 298
 299        mutex_unlock(&srom->lock);
 300
 301        return offset;
 302}
 303
 304static ssize_t total_show(struct device *dev,
 305                          struct device_attribute *attr, char *buf)
 306{
 307        struct srom_dev *srom = dev_get_drvdata(dev);
 308        return sprintf(buf, "%u\n", srom->total_size);
 309}
 310
 311static ssize_t sector_show(struct device *dev,
 312                           struct device_attribute *attr, char *buf)
 313{
 314        struct srom_dev *srom = dev_get_drvdata(dev);
 315        return sprintf(buf, "%u\n", srom->sector_size);
 316}
 317
 318static ssize_t page_show(struct device *dev,
 319                         struct device_attribute *attr, char *buf)
 320{
 321        struct srom_dev *srom = dev_get_drvdata(dev);
 322        return sprintf(buf, "%u\n", srom->page_size);
 323}
 324
 325static struct device_attribute srom_dev_attrs[] = {
 326        __ATTR(total_size, S_IRUGO, total_show, NULL),
 327        __ATTR(sector_size, S_IRUGO, sector_show, NULL),
 328        __ATTR(page_size, S_IRUGO, page_show, NULL),
 329        __ATTR_NULL
 330};
 331
 332static char *srom_devnode(struct device *dev, umode_t *mode)
 333{
 334        *mode = S_IRUGO | S_IWUSR;
 335        return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
 336}
 337
 338/*
 339 * The fops
 340 */
 341static const struct file_operations srom_fops = {
 342        .owner =     THIS_MODULE,
 343        .llseek =    srom_llseek,
 344        .read =      srom_read,
 345        .write =     srom_write,
 346        .open =      srom_open,
 347        .release =   srom_release,
 348};
 349
 350/**
 351 * srom_setup_minor() - Initialize per-minor information.
 352 * @srom: Per-device SROM state.
 353 * @index: Device to set up.
 354 */
 355static int srom_setup_minor(struct srom_dev *srom, int index)
 356{
 357        struct device *dev;
 358        int devhdl = srom->hv_devhdl;
 359
 360        mutex_init(&srom->lock);
 361
 362        if (_srom_read(devhdl, &srom->total_size,
 363                       SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0)
 364                return -EIO;
 365        if (_srom_read(devhdl, &srom->sector_size,
 366                       SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0)
 367                return -EIO;
 368        if (_srom_read(devhdl, &srom->page_size,
 369                       SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
 370                return -EIO;
 371
 372        dev = device_create(srom_class, &platform_bus,
 373                            MKDEV(srom_major, index), srom, "%d", index);
 374        return PTR_RET(dev);
 375}
 376
 377/** srom_init() - Initialize the driver's module. */
 378static int srom_init(void)
 379{
 380        int result, i;
 381        dev_t dev = MKDEV(srom_major, 0);
 382
 383        /*
 384         * Start with a plausible number of partitions; the krealloc() call
 385         * below will yield about log(srom_devs) additional allocations.
 386         */
 387        srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
 388
 389        /* Discover the number of srom partitions. */
 390        for (i = 0; ; i++) {
 391                int devhdl;
 392                char buf[20];
 393                struct srom_dev *new_srom_devices =
 394                        krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
 395                                 GFP_KERNEL | __GFP_ZERO);
 396                if (!new_srom_devices) {
 397                        result = -ENOMEM;
 398                        goto fail_mem;
 399                }
 400                srom_devices = new_srom_devices;
 401                sprintf(buf, "srom/0/%d", i);
 402                devhdl = hv_dev_open((HV_VirtAddr)buf, 0);
 403                if (devhdl < 0) {
 404                        if (devhdl != HV_ENODEV)
 405                                pr_notice("srom/%d: hv_dev_open failed: %d.\n",
 406                                          i, devhdl);
 407                        break;
 408                }
 409                srom_devices[i].hv_devhdl = devhdl;
 410        }
 411        srom_devs = i;
 412
 413        /* Bail out early if we have no partitions at all. */
 414        if (srom_devs == 0) {
 415                result = -ENODEV;
 416                goto fail_mem;
 417        }
 418
 419        /* Register our major, and accept a dynamic number. */
 420        if (srom_major)
 421                result = register_chrdev_region(dev, srom_devs, "srom");
 422        else {
 423                result = alloc_chrdev_region(&dev, 0, srom_devs, "srom");
 424                srom_major = MAJOR(dev);
 425        }
 426        if (result < 0)
 427                goto fail_mem;
 428
 429        /* Register a character device. */
 430        cdev_init(&srom_cdev, &srom_fops);
 431        srom_cdev.owner = THIS_MODULE;
 432        srom_cdev.ops = &srom_fops;
 433        result = cdev_add(&srom_cdev, dev, srom_devs);
 434        if (result < 0)
 435                goto fail_chrdev;
 436
 437        /* Create a sysfs class. */
 438        srom_class = class_create(THIS_MODULE, "srom");
 439        if (IS_ERR(srom_class)) {
 440                result = PTR_ERR(srom_class);
 441                goto fail_cdev;
 442        }
 443        srom_class->dev_attrs = srom_dev_attrs;
 444        srom_class->devnode = srom_devnode;
 445
 446        /* Do per-partition initialization */
 447        for (i = 0; i < srom_devs; i++) {
 448                result = srom_setup_minor(srom_devices + i, i);
 449                if (result < 0)
 450                        goto fail_class;
 451        }
 452
 453        return 0;
 454
 455fail_class:
 456        for (i = 0; i < srom_devs; i++)
 457                device_destroy(srom_class, MKDEV(srom_major, i));
 458        class_destroy(srom_class);
 459fail_cdev:
 460        cdev_del(&srom_cdev);
 461fail_chrdev:
 462        unregister_chrdev_region(dev, srom_devs);
 463fail_mem:
 464        kfree(srom_devices);
 465        return result;
 466}
 467
 468/** srom_cleanup() - Clean up the driver's module. */
 469static void srom_cleanup(void)
 470{
 471        int i;
 472        for (i = 0; i < srom_devs; i++)
 473                device_destroy(srom_class, MKDEV(srom_major, i));
 474        class_destroy(srom_class);
 475        cdev_del(&srom_cdev);
 476        unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs);
 477        kfree(srom_devices);
 478}
 479
 480module_init(srom_init);
 481module_exit(srom_cleanup);
 482
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.