linux/drivers/mtd/maps/octagon-5066.c
<<
>>
Prefs
   1/* ######################################################################
   2
   3   Octagon 5066 MTD Driver.
   4
   5   The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It
   6   comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that
   7   is replacable by flash. Both units are mapped through a multiplexer
   8   into a 32k memory window at 0xe8000. The control register for the
   9   multiplexing unit is located at IO 0x208 with a bit map of
  10     0-5 Page Selection in 32k increments
  11     6-7 Device selection:
  12        00 SSD off
  13        01 SSD 0 (Socket)
  14        10 SSD 1 (Flash chip)
  15        11 undefined
  16
  17   On each SSD, the first 128k is reserved for use by the bios
  18   (actually it IS the bios..) This only matters if you are booting off the
  19   flash, you must not put a file system starting there.
  20
  21   The driver tries to do a detection algorithm to guess what sort of devices
  22   are plugged into the sockets.
  23
  24   ##################################################################### */
  25
  26#include <linux/module.h>
  27#include <linux/ioport.h>
  28#include <linux/init.h>
  29#include <asm/io.h>
  30
  31#include <linux/mtd/map.h>
  32#include <linux/mtd/mtd.h>
  33
  34#define WINDOW_START 0xe8000
  35#define WINDOW_LENGTH 0x8000
  36#define WINDOW_SHIFT 27
  37#define WINDOW_MASK 0x7FFF
  38#define PAGE_IO 0x208
  39
  40static volatile char page_n_dev = 0;
  41static unsigned long iomapadr;
  42static DEFINE_SPINLOCK(oct5066_spin);
  43
  44/*
  45 * We use map_priv_1 to identify which device we are.
  46 */
  47
  48static void __oct5066_page(struct map_info *map, __u8 byte)
  49{
  50        outb(byte,PAGE_IO);
  51        page_n_dev = byte;
  52}
  53
  54static inline void oct5066_page(struct map_info *map, unsigned long ofs)
  55{
  56        __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT);
  57
  58        if (page_n_dev != byte)
  59                __oct5066_page(map, byte);
  60}
  61
  62
  63static map_word oct5066_read8(struct map_info *map, unsigned long ofs)
  64{
  65        map_word ret;
  66        spin_lock(&oct5066_spin);
  67        oct5066_page(map, ofs);
  68        ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
  69        spin_unlock(&oct5066_spin);
  70        return ret;
  71}
  72
  73static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
  74{
  75        while(len) {
  76                unsigned long thislen = len;
  77                if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
  78                        thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
  79
  80                spin_lock(&oct5066_spin);
  81                oct5066_page(map, from);
  82                memcpy_fromio(to, iomapadr + from, thislen);
  83                spin_unlock(&oct5066_spin);
  84                to += thislen;
  85                from += thislen;
  86                len -= thislen;
  87        }
  88}
  89
  90static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr)
  91{
  92        spin_lock(&oct5066_spin);
  93        oct5066_page(map, adr);
  94        writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
  95        spin_unlock(&oct5066_spin);
  96}
  97
  98static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
  99{
 100        while(len) {
 101                unsigned long thislen = len;
 102                if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
 103                        thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
 104
 105                spin_lock(&oct5066_spin);
 106                oct5066_page(map, to);
 107                memcpy_toio(iomapadr + to, from, thislen);
 108                spin_unlock(&oct5066_spin);
 109                to += thislen;
 110                from += thislen;
 111                len -= thislen;
 112        }
 113}
 114
 115static struct map_info oct5066_map[2] = {
 116        {
 117                .name = "Octagon 5066 Socket",
 118                .phys = NO_XIP,
 119                .size = 512 * 1024,
 120                .bankwidth = 1,
 121                .read = oct5066_read8,
 122                .copy_from = oct5066_copy_from,
 123                .write = oct5066_write8,
 124                .copy_to = oct5066_copy_to,
 125                .map_priv_1 = 1<<6
 126        },
 127        {
 128                .name = "Octagon 5066 Internal Flash",
 129                .phys = NO_XIP,
 130                .size = 2 * 1024 * 1024,
 131                .bankwidth = 1,
 132                .read = oct5066_read8,
 133                .copy_from = oct5066_copy_from,
 134                .write = oct5066_write8,
 135                .copy_to = oct5066_copy_to,
 136                .map_priv_1 = 2<<6
 137        }
 138};
 139
 140static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
 141
 142// OctProbe - Sense if this is an octagon card
 143// ---------------------------------------------------------------------
 144/* Perform a simple validity test, we map the window select SSD0 and
 145   change pages while monitoring the window. A change in the window,
 146   controlled by the PAGE_IO port is a functioning 5066 board. This will
 147   fail if the thing in the socket is set to a uniform value. */
 148static int __init OctProbe(void)
 149{
 150   unsigned int Base = (1 << 6);
 151   unsigned long I;
 152   unsigned long Values[10];
 153   for (I = 0; I != 20; I++)
 154   {
 155      outb(Base + (I%10),PAGE_IO);
 156      if (I < 10)
 157      {
 158         // Record the value and check for uniqueness
 159         Values[I%10] = readl(iomapadr);
 160         if (I > 0 && Values[I%10] == Values[0])
 161            return -EAGAIN;
 162      }
 163      else
 164      {
 165         // Make sure we get the same values on the second pass
 166         if (Values[I%10] != readl(iomapadr))
 167            return -EAGAIN;
 168      }
 169   }
 170   return 0;
 171}
 172
 173void cleanup_oct5066(void)
 174{
 175        int i;
 176        for (i=0; i<2; i++) {
 177                if (oct5066_mtd[i]) {
 178                        mtd_device_unregister(oct5066_mtd[i]);
 179                        map_destroy(oct5066_mtd[i]);
 180                }
 181        }
 182        iounmap((void *)iomapadr);
 183        release_region(PAGE_IO, 1);
 184}
 185
 186static int __init init_oct5066(void)
 187{
 188        int i;
 189        int ret = 0;
 190
 191        // Do an autoprobe sequence
 192        if (!request_region(PAGE_IO,1,"Octagon SSD")) {
 193                printk(KERN_NOTICE "5066: Page Register in Use\n");
 194                return -EAGAIN;
 195        }
 196        iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
 197        if (!iomapadr) {
 198                printk(KERN_NOTICE "Failed to ioremap memory region\n");
 199                ret = -EIO;
 200                goto out_rel;
 201        }
 202        if (OctProbe() != 0) {
 203                printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
 204                iounmap((void *)iomapadr);
 205                ret = -EAGAIN;
 206                goto out_unmap;
 207        }
 208
 209        // Print out our little header..
 210        printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
 211               WINDOW_START+WINDOW_LENGTH);
 212
 213        for (i=0; i<2; i++) {
 214                oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]);
 215                if (!oct5066_mtd[i])
 216                        oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
 217                if (!oct5066_mtd[i])
 218                        oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]);
 219                if (!oct5066_mtd[i])
 220                        oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
 221                if (oct5066_mtd[i]) {
 222                        oct5066_mtd[i]->owner = THIS_MODULE;
 223                        mtd_device_register(oct5066_mtd[i], NULL, 0);
 224                }
 225        }
 226
 227        if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
 228                cleanup_oct5066();
 229                return -ENXIO;
 230        }
 231
 232        return 0;
 233
 234 out_unmap:
 235        iounmap((void *)iomapadr);
 236 out_rel:
 237        release_region(PAGE_IO, 1);
 238        return ret;
 239}
 240
 241module_init(init_oct5066);
 242module_exit(cleanup_oct5066);
 243
 244MODULE_LICENSE("GPL");
 245MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>");
 246MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");
 247