diff options
| -rw-r--r-- | arch/tile/include/hv/drv_srom_intf.h | 41 | ||||
| -rw-r--r-- | drivers/char/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/char/Makefile | 2 | ||||
| -rw-r--r-- | drivers/char/tile-srom.c | 481 |
4 files changed, 535 insertions, 0 deletions
diff --git a/arch/tile/include/hv/drv_srom_intf.h b/arch/tile/include/hv/drv_srom_intf.h new file mode 100644 index 000000000000..6395faa6d9e6 --- /dev/null +++ b/arch/tile/include/hv/drv_srom_intf.h | |||
| @@ -0,0 +1,41 @@ | |||
| 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 | |||
| 15 | /** | ||
| 16 | * @file drv_srom_intf.h | ||
| 17 | * Interface definitions for the SPI Flash ROM driver. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef _SYS_HV_INCLUDE_DRV_SROM_INTF_H | ||
| 21 | #define _SYS_HV_INCLUDE_DRV_SROM_INTF_H | ||
| 22 | |||
| 23 | /** Read this offset to get the total device size. */ | ||
| 24 | #define SROM_TOTAL_SIZE_OFF 0xF0000000 | ||
| 25 | |||
| 26 | /** Read this offset to get the device sector size. */ | ||
| 27 | #define SROM_SECTOR_SIZE_OFF 0xF0000004 | ||
| 28 | |||
| 29 | /** Read this offset to get the device page size. */ | ||
| 30 | #define SROM_PAGE_SIZE_OFF 0xF0000008 | ||
| 31 | |||
| 32 | /** Write this offset to flush any pending writes. */ | ||
| 33 | #define SROM_FLUSH_OFF 0xF1000000 | ||
| 34 | |||
| 35 | /** Write this offset, plus the byte offset of the start of a sector, to | ||
| 36 | * erase a sector. Any write data is ignored, but there must be at least | ||
| 37 | * one byte of write data. Only applies when the driver is in MTD mode. | ||
| 38 | */ | ||
| 39 | #define SROM_ERASE_OFF 0xF2000000 | ||
| 40 | |||
| 41 | #endif /* _SYS_HV_INCLUDE_DRV_SROM_INTF_H */ | ||
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 49502bc5360a..423fd56bf612 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
| @@ -616,5 +616,16 @@ config MSM_SMD_PKT | |||
| 616 | Enables userspace clients to read and write to some packet SMD | 616 | Enables userspace clients to read and write to some packet SMD |
| 617 | ports via device interface for MSM chipset. | 617 | ports via device interface for MSM chipset. |
| 618 | 618 | ||
| 619 | config TILE_SROM | ||
| 620 | bool "Character-device access via hypervisor to the Tilera SPI ROM" | ||
| 621 | depends on TILE | ||
| 622 | default y | ||
| 623 | ---help--- | ||
| 624 | This device provides character-level read-write access | ||
| 625 | to the SROM, typically via the "0", "1", and "2" devices | ||
| 626 | in /dev/srom/. The Tilera hypervisor makes the flash | ||
| 627 | device appear much like a simple EEPROM, and knows | ||
| 628 | how to partition a single ROM for multiple purposes. | ||
| 629 | |||
| 619 | endmenu | 630 | endmenu |
| 620 | 631 | ||
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 7a00672bd85d..32762ba769c2 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile | |||
| @@ -63,3 +63,5 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o | |||
| 63 | 63 | ||
| 64 | obj-$(CONFIG_JS_RTC) += js-rtc.o | 64 | obj-$(CONFIG_JS_RTC) += js-rtc.o |
| 65 | js-rtc-y = rtc.o | 65 | js-rtc-y = rtc.o |
| 66 | |||
| 67 | obj-$(CONFIG_TILE_SROM) += tile-srom.o | ||
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c new file mode 100644 index 000000000000..cf3ee008dca2 --- /dev/null +++ b/drivers/char/tile-srom.c | |||
| @@ -0,0 +1,481 @@ | |||
| 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 | |||
| 65 | struct 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 | |||
| 73 | static int srom_major; /* Dynamic major by default */ | ||
| 74 | module_param(srom_major, int, 0); | ||
| 75 | MODULE_AUTHOR("Tilera Corporation"); | ||
| 76 | MODULE_LICENSE("GPL"); | ||
| 77 | |||
| 78 | static int srom_devs; /* Number of SROM partitions */ | ||
| 79 | static struct cdev srom_cdev; | ||
| 80 | static struct class *srom_class; | ||
| 81 | static struct srom_dev *srom_devices; | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Handle calling the hypervisor and managing EAGAIN/EBUSY. | ||
| 85 | */ | ||
| 86 | |||
| 87 | static 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 | |||
| 107 | static 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 | } | ||
