Merge branch 'for-rmk' of git://git.marvell.com/orion
[linux-2.6] / drivers / mtd / maps / octagon-5066.c
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
41 static volatile char page_n_dev = 0;
42 static unsigned long iomapadr;
43 static DEFINE_SPINLOCK(oct5066_spin);
44
45 /*
46  * We use map_priv_1 to identify which device we are.
47  */
48
49 static void __oct5066_page(struct map_info *map, __u8 byte)
50 {
51         outb(byte,PAGE_IO);
52         page_n_dev = byte;
53 }
54
55 static 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
64 static 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
74 static 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
91 static 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
99 static 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
116 static 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
141 static 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. */
149 static 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
174 void 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
187 static 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
242 module_init(init_oct5066);
243 module_exit(cleanup_oct5066);
244
245 MODULE_LICENSE("GPL");
246 MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>");
247 MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");