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/slab.h>
  28#include <linux/ioport.h>
  29#include <linux/init.h>
  30#include <asm/io.h>
  31
  32#include <linux/mtd/map.h>
  33#include <linux/mtd/mtd.h>
  34
  35#define WINDOW_START 0xe8000
  36#define WINDOW_LENGTH 0x8000
  37#define WINDOW_SHIFT 27
  38#define WINDOW_MASK 0x7FFF
  39#define PAGE_IO 0x208
  40
  41static volatile char page_n_dev = 0;
  42static unsigned long iomapadr;
  43static DEFINE_SPINLOCK(oct5066_spin);
  44
  45/*
  46 * We use map_priv_1 to identify which device we are.
  47 */
  48
  49static void __oct5066_page(struct map_info *map, __u8 byte)
  50{
  51        outb(byte,PAGE_IO);
  52        page_n_dev = byte;
  53}
  54
  55static inline void oct5066_page(struct map_info *map, unsigned long ofs)
  56{
  57        __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT);
  58
  59        if (page_n_dev != byte)
  60                __oct5066_page(map, byte);
  61}
  62
  63
  64static map_word oct5066_read8(struct map_info *map, unsigned long ofs)
  65{
  66        map_word ret;
  67        spin_lock(&oct5066_spin);
  68        oct5066_page(map, ofs);
  69        ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
  70        spin_unlock(&oct5066_spin);
  71        return ret;
  72}
  73
  74static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
  75{
  76        while(len) {
  77                unsigned long thislen = len;
  78                if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
  79                        thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
  80
  81                spin_lock(&oct5066_spin);
  82                oct5066_page(map, from);
  83                memcpy_fromio(to, iomapadr + from, thislen);
  84                spin_unlock(&oct5066_spin);
  85                to += thislen;
  86                from += thislen;
  87                len -= thislen;
  88        }
  89}
  90
  91static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr)
  92{
  93        spin_lock(&oct5066_spin);
  94        oct5066_page(map, adr);
  95        writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
  96        spin_unlock(&oct5066_spin);
  97}
  98
  99static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 100{
 101        while(len) {
 102                unsigned long thislen = len;
 103                if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
 104                        thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
 105
 106                spin_lock(&oct5066_spin);
 107                oct5066_page(map, to);
 108                memcpy_toio(iomapadr + to, from, thislen);
 109                spin_unlock(&oct5066_spin);
 110                to += thislen;
 111                from += thislen;
 112                len -= thislen;
 113        }
 114}
 115
 116static struct map_info oct5066_map[2] = {
 117        {
 118                .name = "Octagon 5066 Socket",
 119                .phys = NO_XIP,
 120                .size = 512 * 1024,
 121                .bankwidth = 1,
 122                .read = oct5066_read8,
 123                .copy_from = oct5066_copy_from,
 124                .write = oct5066_write8,
 125                .copy_to = oct5066_copy_to,
 126                .map_priv_1 = 1<<6
 127        },
 128        {
 129                .name = "Octagon 5066 Internal Flash",
 130                .phys = NO_XIP,
 131                .size = 2 * 1024 * 1024,
 132                .bankwidth = 1,
 133                .read = oct5066_read8,
 134                .copy_from = oct5066_copy_from,
 135                .write = oct5066_write8,
 136                .copy_to = oct5066_copy_to,
 137                .map_priv_1 = 2<<6
 138        }
 139};
 140
 141static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
 142
 143// OctProbe - Sense if this is an octagon card
 144// ---------------------------------------------------------------------
 145/* Perform a simple validity test, we map the window select SSD0 and
 146   change pages while monitoring the window. A change in the window,
 147   controlled by the PAGE_IO port is a functioning 5066 board. This will
 148   fail if the thing in the socket is set to a uniform value. */
 149static int __init OctProbe(void)
 150{
 151   unsigned int Base = (1 << 6);
 152   unsigned long I;
 153   unsigned long Values[10];
 154   for (I = 0; I != 20; I++)
 155   {
 156      outb(Base + (I%10),PAGE_IO);
 157      if (I < 10)
 158      {
 159         // Record the value and check for uniqueness
 160         Values[I%10] = readl(iomapadr);
 161         if (I > 0 && Values[I%10] == Values[0])
 162            return -EAGAIN;
 163      }
 164      else
 165      {
 166         // Make sure we get the same values on the second pass
 167         if (Values[I%10] != readl(iomapadr))
 168            return -EAGAIN;
 169      }
 170   }
 171   return 0;
 172}
 173
 174void cleanup_oct5066(void)
 175{
 176        int i;
 177        for (i=0; i<2; i++) {
 178                if (oct5066_mtd[i]) {
 179                        del_mtd_device(oct5066_mtd[i]);
 180                        map_destroy(oct5066_mtd[i]);
 181                }
 182        }
 183        iounmap((void *)iomapadr);
 184        release_region(PAGE_IO, 1);
 185}
 186
 187static int __init init_oct5066(void)
 188{
 189        int i;
 190        int ret = 0;
 191
 192        // Do an autoprobe sequence
 193        if (!request_region(PAGE_IO,1,"Octagon SSD")) {
 194                printk(KERN_NOTICE "5066: Page Register in Use\n");
 195                return -EAGAIN;
 196        }
 197        iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
 198        if (!iomapadr) {
 199                printk(KERN_NOTICE "Failed to ioremap memory region\n");
 200                ret = -EIO;
 201                goto out_rel;
 202        }
 203        if (OctProbe() != 0) {
 204                printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
 205                iounmap((void *)iomapadr);
 206                ret = -EAGAIN;
 207                goto out_unmap;
 208        }
 209
 210        // Print out our little header..
 211        printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
 212               WINDOW_START+WINDOW_LENGTH);
 213
 214        for (i=0; i<2; i++) {
 215                oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]);
 216                if (!oct5066_mtd[i])
 217                        oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
 218                if (!oct5066_mtd[i])
 219                        oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]);
 220                if (!oct5066_mtd[i])
 221                        oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
 222                if (oct5066_mtd[i]) {
 223                        oct5066_mtd[i]->owner = THIS_MODULE;
 224                        add_mtd_device(oct5066_mtd[i]);
 225                }
 226        }
 227
 228        if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
 229                cleanup_oct5066();
 230                return -ENXIO;
 231        }
 232
 233        return 0;
 234
 235 out_unmap:
 236        iounmap((void *)iomapadr);
 237 out_rel:
 238        release_region(PAGE_IO, 1);
 239        return ret;
 240}
 241
 242module_init(init_oct5066);
 243module_exit(cleanup_oct5066);
 244
 245MODULE_LICENSE("GPL");
 246MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>");
 247MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");
 248
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.