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