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