linux-old/drivers/mtd/maps/octagon-5066.c
<<
>>
Prefs
   1// $Id: octagon-5066.c,v 1.20 2003/01/07 17:21:55 dwmw2 Exp $
   2/* ######################################################################
   3
   4   Octagon 5066 MTD Driver. 
   5  
   6   The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It
   7   comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that
   8   is replacable by flash. Both units are mapped through a multiplexer
   9   into a 32k memory window at 0xe8000. The control register for the 
  10   multiplexing unit is located at IO 0x208 with a bit map of
  11     0-5 Page Selection in 32k increments
  12     6-7 Device selection:
  13        00 SSD off
  14        01 SSD 0 (Socket)
  15        10 SSD 1 (Flash chip)
  16        11 undefined
  17  
  18   On each SSD, the first 128k is reserved for use by the bios
  19   (actually it IS the bios..) This only matters if you are booting off the 
  20   flash, you must not put a file system starting there.
  21   
  22   The driver tries to do a detection algorithm to guess what sort of devices
  23   are plugged into the sockets.
  24   
  25   ##################################################################### */
  26
  27#include <linux/module.h>
  28#include <linux/slab.h>
  29#include <linux/ioport.h>
  30#include <linux/init.h>
  31#include <asm/io.h>
  32
  33#include <linux/mtd/map.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 spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED;
  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 __u8 oct5066_read8(struct map_info *map, unsigned long ofs)
  65{
  66        __u8 ret;
  67        spin_lock(&oct5066_spin);
  68        oct5066_page(map, ofs);
  69        ret = readb(iomapadr + (ofs & WINDOW_MASK));
  70        spin_unlock(&oct5066_spin);
  71        return ret;
  72}
  73
  74static __u16 oct5066_read16(struct map_info *map, unsigned long ofs)
  75{
  76        __u16 ret;
  77        spin_lock(&oct5066_spin);
  78        oct5066_page(map, ofs);
  79        ret = readw(iomapadr + (ofs & WINDOW_MASK));
  80        spin_unlock(&oct5066_spin);
  81        return ret;
  82}
  83
  84static __u32 oct5066_read32(struct map_info *map, unsigned long ofs)
  85{
  86        __u32 ret;
  87        spin_lock(&oct5066_spin);
  88        oct5066_page(map, ofs);
  89        ret = readl(iomapadr + (ofs & WINDOW_MASK));
  90        spin_unlock(&oct5066_spin);
  91        return ret;
  92}
  93
  94static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
  95{
  96        while(len) {
  97                unsigned long thislen = len;
  98                if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
  99                        thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
 100                
 101                spin_lock(&oct5066_spin);
 102                oct5066_page(map, from);
 103                memcpy_fromio(to, iomapadr + from, thislen);
 104                spin_unlock(&oct5066_spin);
 105                to += thislen;
 106                from += thislen;
 107                len -= thislen;
 108        }
 109}
 110
 111static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr)
 112{
 113        spin_lock(&oct5066_spin);
 114        oct5066_page(map, adr);
 115        writeb(d, iomapadr + (adr & WINDOW_MASK));
 116        spin_unlock(&oct5066_spin);
 117}
 118
 119static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr)
 120{
 121        spin_lock(&oct5066_spin);
 122        oct5066_page(map, adr);
 123        writew(d, iomapadr + (adr & WINDOW_MASK));
 124        spin_unlock(&oct5066_spin);
 125}
 126
 127static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr)
 128{
 129        spin_lock(&oct5066_spin);
 130        oct5066_page(map, adr);
 131        writel(d, iomapadr + (adr & WINDOW_MASK));
 132        spin_unlock(&oct5066_spin);
 133}
 134
 135static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 136{
 137        while(len) {
 138                unsigned long thislen = len;
 139                if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
 140                        thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
 141                
 142                spin_lock(&oct5066_spin);
 143                oct5066_page(map, to);
 144                memcpy_toio(iomapadr + to, from, thislen);
 145                spin_unlock(&oct5066_spin);
 146                to += thislen;
 147                from += thislen;
 148                len -= thislen;
 149        }
 150}
 151
 152static struct map_info oct5066_map[2] = {
 153        {
 154                name: "Octagon 5066 Socket",
 155                size: 512 * 1024,
 156                buswidth: 1,
 157                read8: oct5066_read8,
 158                read16: oct5066_read16,
 159                read32: oct5066_read32,
 160                copy_from: oct5066_copy_from,
 161                write8: oct5066_write8,
 162                write16: oct5066_write16,
 163                write32: oct5066_write32,
 164                copy_to: oct5066_copy_to,
 165                map_priv_1: 1<<6
 166        },
 167        {
 168                name: "Octagon 5066 Internal Flash",
 169                size: 2 * 1024 * 1024,
 170                buswidth: 1,
 171                read8: oct5066_read8,
 172                read16: oct5066_read16,
 173                read32: oct5066_read32,
 174                copy_from: oct5066_copy_from,
 175                write8: oct5066_write8,
 176                write16: oct5066_write16,
 177                write32: oct5066_write32,
 178                copy_to: oct5066_copy_to,
 179                map_priv_1: 2<<6
 180        }
 181};
 182
 183static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
 184
 185// OctProbe - Sense if this is an octagon card
 186// ---------------------------------------------------------------------
 187/* Perform a simple validity test, we map the window select SSD0 and
 188   change pages while monitoring the window. A change in the window, 
 189   controlled by the PAGE_IO port is a functioning 5066 board. This will
 190   fail if the thing in the socket is set to a uniform value. */
 191static int __init OctProbe(void)
 192{
 193   unsigned int Base = (1 << 6);
 194   unsigned long I;
 195   unsigned long Values[10];
 196   for (I = 0; I != 20; I++)
 197   {
 198      outb(Base + (I%10),PAGE_IO);
 199      if (I < 10)
 200      {
 201         // Record the value and check for uniqueness
 202         Values[I%10] = readl(iomapadr);
 203         if (I > 0 && Values[I%10] == Values[0])
 204            return -EAGAIN;
 205      }      
 206      else
 207      {
 208         // Make sure we get the same values on the second pass
 209         if (Values[I%10] != readl(iomapadr))
 210            return -EAGAIN;
 211      }      
 212   }
 213   return 0;
 214}
 215
 216void cleanup_oct5066(void)
 217{
 218        int i;
 219        for (i=0; i<2; i++) {
 220                if (oct5066_mtd[i]) {
 221                        del_mtd_device(oct5066_mtd[i]);
 222                        map_destroy(oct5066_mtd[i]);
 223                }
 224        }
 225        iounmap((void *)iomapadr);
 226        release_region(PAGE_IO, 1);
 227}
 228
 229int __init init_oct5066(void)
 230{
 231        int i;
 232        int ret = 0;
 233
 234        // Do an autoprobe sequence
 235        if (!request_region(PAGE_IO,1,"Octagon SSD")) {
 236                printk(KERN_NOTICE "5066: Page Register in Use\n");
 237                return -EAGAIN;
 238        }
 239        iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
 240        if (!iomapadr) {
 241                printk(KERN_NOTICE "Failed to ioremap memory region\n");
 242                ret = -EIO;
 243                goto out_rel;
 244        }
 245        if (OctProbe() != 0) {
 246                printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
 247                ret = -EAGAIN;
 248                goto out_unmap;
 249        }
 250        
 251        // Print out our little header..
 252        printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
 253               WINDOW_START+WINDOW_LENGTH);
 254        
 255        for (i=0; i<2; i++) {
 256                oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]);
 257                if (!oct5066_mtd[i])
 258                        oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
 259                if (!oct5066_mtd[i])
 260                        oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]);
 261                if (!oct5066_mtd[i])
 262                        oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
 263                if (oct5066_mtd[i]) {
 264                        oct5066_mtd[i]->module = THIS_MODULE;
 265                        add_mtd_device(oct5066_mtd[i]);
 266                }
 267        }
 268        
 269        if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
 270                cleanup_oct5066();
 271                return -ENXIO;
 272        }         
 273
 274        return 0;
 275
 276 out_unmap:
 277        iounmap((void *)iomapadr);
 278 out_rel:
 279        release_region(PAGE_IO, 1);
 280        return ret;
 281}
 282
 283module_init(init_oct5066);
 284module_exit(cleanup_oct5066);
 285
 286MODULE_LICENSE("GPL");
 287MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>");
 288MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");
 289