ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / mtd / maps / octagon-5066.c
1 // $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 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 #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 spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED;
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 __u8 oct5066_read8(struct map_info *map, unsigned long ofs)
66 {
67         __u8 ret;
68         spin_lock(&oct5066_spin);
69         oct5066_page(map, ofs);
70         ret = readb(iomapadr + (ofs & WINDOW_MASK));
71         spin_unlock(&oct5066_spin);
72         return ret;
73 }
74
75 static __u16 oct5066_read16(struct map_info *map, unsigned long ofs)
76 {
77         __u16 ret;
78         spin_lock(&oct5066_spin);
79         oct5066_page(map, ofs);
80         ret = readw(iomapadr + (ofs & WINDOW_MASK));
81         spin_unlock(&oct5066_spin);
82         return ret;
83 }
84
85 static __u32 oct5066_read32(struct map_info *map, unsigned long ofs)
86 {
87         __u32 ret;
88         spin_lock(&oct5066_spin);
89         oct5066_page(map, ofs);
90         ret = readl(iomapadr + (ofs & WINDOW_MASK));
91         spin_unlock(&oct5066_spin);
92         return ret;
93 }
94
95 static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
96 {
97         while(len) {
98                 unsigned long thislen = len;
99                 if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
100                         thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
101                 
102                 spin_lock(&oct5066_spin);
103                 oct5066_page(map, from);
104                 memcpy_fromio(to, iomapadr + from, thislen);
105                 spin_unlock(&oct5066_spin);
106                 to += thislen;
107                 from += thislen;
108                 len -= thislen;
109         }
110 }
111
112 static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr)
113 {
114         spin_lock(&oct5066_spin);
115         oct5066_page(map, adr);
116         writeb(d, iomapadr + (adr & WINDOW_MASK));
117         spin_unlock(&oct5066_spin);
118 }
119
120 static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr)
121 {
122         spin_lock(&oct5066_spin);
123         oct5066_page(map, adr);
124         writew(d, iomapadr + (adr & WINDOW_MASK));
125         spin_unlock(&oct5066_spin);
126 }
127
128 static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr)
129 {
130         spin_lock(&oct5066_spin);
131         oct5066_page(map, adr);
132         writel(d, iomapadr + (adr & WINDOW_MASK));
133         spin_unlock(&oct5066_spin);
134 }
135
136 static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
137 {
138         while(len) {
139                 unsigned long thislen = len;
140                 if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
141                         thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
142                 
143                 spin_lock(&oct5066_spin);
144                 oct5066_page(map, to);
145                 memcpy_toio(iomapadr + to, from, thislen);
146                 spin_unlock(&oct5066_spin);
147                 to += thislen;
148                 from += thislen;
149                 len -= thislen;
150         }
151 }
152
153 static struct map_info oct5066_map[2] = {
154         {
155                 .name = "Octagon 5066 Socket",
156                 .phys = NO_XIP,
157                 .size = 512 * 1024,
158                 .buswidth = 1,
159                 .read8 = oct5066_read8,
160                 .read16 = oct5066_read16,
161                 .read32 = oct5066_read32,
162                 .copy_from = oct5066_copy_from,
163                 .write8 = oct5066_write8,
164                 .write16 = oct5066_write16,
165                 .write32 = oct5066_write32,
166                 .copy_to = oct5066_copy_to,
167                 .map_priv_1 = 1<<6
168         },
169         {
170                 .name = "Octagon 5066 Internal Flash",
171                 .phys = NO_XIP,
172                 .size = 2 * 1024 * 1024,
173                 .buswidth = 1,
174                 .read8 = oct5066_read8,
175                 .read16 = oct5066_read16,
176                 .read32 = oct5066_read32,
177                 .copy_from = oct5066_copy_from,
178                 .write8 = oct5066_write8,
179                 .write16 = oct5066_write16,
180                 .write32 = oct5066_write32,
181                 .copy_to = oct5066_copy_to,
182                 .map_priv_1 = 2<<6
183         }
184 };
185
186 static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
187
188 // OctProbe - Sense if this is an octagon card
189 // ---------------------------------------------------------------------
190 /* Perform a simple validity test, we map the window select SSD0 and
191    change pages while monitoring the window. A change in the window, 
192    controlled by the PAGE_IO port is a functioning 5066 board. This will
193    fail if the thing in the socket is set to a uniform value. */
194 static int __init OctProbe(void)
195 {
196    unsigned int Base = (1 << 6);
197    unsigned long I;
198    unsigned long Values[10];
199    for (I = 0; I != 20; I++)
200    {
201       outb(Base + (I%10),PAGE_IO);
202       if (I < 10)
203       {
204          // Record the value and check for uniqueness
205          Values[I%10] = readl(iomapadr);
206          if (I > 0 && Values[I%10] == Values[0])
207             return -EAGAIN;
208       }      
209       else
210       {
211          // Make sure we get the same values on the second pass
212          if (Values[I%10] != readl(iomapadr))
213             return -EAGAIN;
214       }      
215    }
216    return 0;
217 }
218
219 void cleanup_oct5066(void)
220 {
221         int i;
222         for (i=0; i<2; i++) {
223                 if (oct5066_mtd[i]) {
224                         del_mtd_device(oct5066_mtd[i]);
225                         map_destroy(oct5066_mtd[i]);
226                 }
227         }
228         iounmap((void *)iomapadr);
229         release_region(PAGE_IO, 1);
230 }
231
232 int __init init_oct5066(void)
233 {
234         int i;
235         int ret = 0;
236
237         // Do an autoprobe sequence
238         if (!request_region(PAGE_IO,1,"Octagon SSD")) {
239                 printk(KERN_NOTICE "5066: Page Register in Use\n");
240                 return -EAGAIN;
241         }
242         iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
243         if (!iomapadr) {
244                 printk(KERN_NOTICE "Failed to ioremap memory region\n");
245                 ret = -EIO;
246                 goto out_rel;
247         }
248         if (OctProbe() != 0) {
249                 printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
250                 iounmap((void *)iomapadr);
251                 ret = -EAGAIN;
252                 goto out_unmap;
253         }
254         
255         // Print out our little header..
256         printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
257                WINDOW_START+WINDOW_LENGTH);
258         
259         for (i=0; i<2; i++) {
260                 oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]);
261                 if (!oct5066_mtd[i])
262                         oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
263                 if (!oct5066_mtd[i])
264                         oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]);
265                 if (!oct5066_mtd[i])
266                         oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
267                 if (oct5066_mtd[i]) {
268                         oct5066_mtd[i]->owner = THIS_MODULE;
269                         add_mtd_device(oct5066_mtd[i]);
270                 }
271         }
272         
273         if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
274                 cleanup_oct5066();
275                 return -ENXIO;
276         }         
277
278         return 0;
279
280  out_unmap:
281         iounmap((void *)iomapadr);
282  out_rel:
283         release_region(PAGE_IO, 1);
284         return ret;
285 }
286
287 module_init(init_oct5066);
288 module_exit(cleanup_oct5066);
289
290 MODULE_LICENSE("GPL");
291 MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>");
292 MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");