File: /usr/src/linux/drivers/mtd/maps/octagon-5066.c

1     // $Id: octagon-5066.c,v 1.17 2001/06/02 14:30:44 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     
41     static volatile char page_n_dev = 0;
42     static unsigned long iomapadr;
43     static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED;
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 __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     
74     static __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     
84     static __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     
94     static 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     
111     static 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     
119     static 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     
127     static 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     
135     static 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     
152     static 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     
183     static 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. */
191     static 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     
216     #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
217     #define init_oct5066 init_module
218     #define cleanup_oct5066 cleanup_module
219     #endif
220     
221     void cleanup_oct5066(void)
222     {
223     	int i;
224     	for (i=0; i<2; i++) {
225     		if (oct5066_mtd[i]) {
226     			del_mtd_device(oct5066_mtd[i]);
227     			map_destroy(oct5066_mtd[i]);
228     		}
229     	}
230     	iounmap((void *)iomapadr);
231     	release_region(PAGE_IO,1);
232     }
233     
234     int __init init_oct5066(void)
235     {
236     	int i;
237     	
238     	// Do an autoprobe sequence
239     	if (check_region(PAGE_IO,1) != 0)
240     		{
241     			printk("5066: Page Register in Use\n");
242     			return -EAGAIN;
243     		}
244     	iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
245     	if (!iomapadr) {
246     		printk("Failed to ioremap memory region\n");
247     		return -EIO;
248     	}
249     	if (OctProbe() != 0)
250     		{
251     			printk("5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
252     			iounmap((void *)iomapadr);
253     			return -EAGAIN;
254     		}
255     	
256     	request_region(PAGE_IO,1,"Octagon SSD");
257     	
258     	// Print out our little header..
259     	printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
260     	       WINDOW_START+WINDOW_LENGTH);
261     	
262     	for (i=0; i<2; i++) {
263     		oct5066_mtd[i] = do_map_probe("cfi", &oct5066_map[i]);
264     		if (!oct5066_mtd[i])
265     			oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
266     		if (!oct5066_mtd[i])
267     			oct5066_mtd[i] = do_map_probe("ram", &oct5066_map[i]);
268     		if (!oct5066_mtd[i])
269     			oct5066_mtd[i] = do_map_probe("rom", &oct5066_map[i]);
270     		if (oct5066_mtd[i]) {
271     			oct5066_mtd[i]->module = THIS_MODULE;
272     			add_mtd_device(oct5066_mtd[i]);
273     		}
274     	}
275     	
276     	if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
277     		cleanup_oct5066();
278     		return -ENXIO;
279     	}	  
280     	
281     	return 0;
282     }
283     
284     module_init(init_oct5066);
285     module_exit(cleanup_oct5066);
286