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