1 // $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $
2 /* ######################################################################
3 
4    Tempustech VMAX SBC301 MTD Driver.
5 
6    The VMAx 301 is a SBC based on . It
7    comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
8    more flash. Each unit has it's own 8k mapping into a settable region
9    (0xD8000). There are two 8k mappings for each MTD, the first is always set
10    to the lower 8k of the device the second is paged. Writing a 16 bit page
11    value to anywhere in the first 8k will cause the second 8k to page around.
12 
13    To boot the device a bios extension must be installed into the first 8k
14    of flash that is smart enough to copy itself down, page in the rest of
15    itself and begin executing.
16 
17    ##################################################################### */
18 
19 #include <linux/module.h>
20 #include <linux/slab.h>
21 #include <linux/ioport.h>
22 #include <linux/init.h>
23 #include <linux/spinlock.h>
24 #include <asm/io.h>
25 
26 #include <linux/mtd/map.h>
27 
28 
29 #define WINDOW_START 0xd8000
30 #define WINDOW_LENGTH 0x2000
31 #define WINDOW_SHIFT 25
32 #define WINDOW_MASK 0x1FFF
33 
34 /* Actually we could use two spinlocks, but we'd have to have
35    more private space in the struct map_info. We lose a little
36    performance like this, but we'd probably lose more by having
37    the extra indirection from having one of the map->map_priv
38    fields pointing to yet another private struct.
39 */
40 static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED;
41 
__vmax301_page(struct map_info * map,unsigned long page)42 static void __vmax301_page(struct map_info *map, unsigned long page)
43 {
44 	writew(page, map->map_priv_2 - WINDOW_LENGTH);
45 	map->map_priv_1 = page;
46 }
47 
vmax301_page(struct map_info * map,unsigned long ofs)48 static inline void vmax301_page(struct map_info *map,
49 				  unsigned long ofs)
50 {
51 	unsigned long page = (ofs >> WINDOW_SHIFT);
52 	if (map->map_priv_1 != page)
53 		__vmax301_page(map, page);
54 }
55 
vmax301_read8(struct map_info * map,unsigned long ofs)56 static __u8 vmax301_read8(struct map_info *map, unsigned long ofs)
57 {
58 	__u8 ret;
59 	spin_lock(&vmax301_spin);
60 	vmax301_page(map, ofs);
61 	ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
62 	spin_unlock(&vmax301_spin);
63 	return ret;
64 }
65 
vmax301_read16(struct map_info * map,unsigned long ofs)66 static __u16 vmax301_read16(struct map_info *map, unsigned long ofs)
67 {
68 	__u16 ret;
69 	spin_lock(&vmax301_spin);
70 	vmax301_page(map, ofs);
71 	ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK));
72 	spin_unlock(&vmax301_spin);
73 	return ret;
74 }
75 
vmax301_read32(struct map_info * map,unsigned long ofs)76 static __u32 vmax301_read32(struct map_info *map, unsigned long ofs)
77 {
78 	__u32 ret;
79 	spin_lock(&vmax301_spin);
80 	vmax301_page(map, ofs);
81 	ret =  readl(map->map_priv_2 + (ofs & WINDOW_MASK));
82 	spin_unlock(&vmax301_spin);
83 	return ret;
84 }
85 
vmax301_copy_from(struct map_info * map,void * to,unsigned long from,ssize_t len)86 static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
87 {
88 	while(len) {
89 		unsigned long thislen = len;
90 		if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
91 			thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
92 		spin_lock(&vmax301_spin);
93 		vmax301_page(map, from);
94 		memcpy_fromio(to, map->map_priv_2 + from, thislen);
95 		spin_unlock(&vmax301_spin);
96 		to += thislen;
97 		from += thislen;
98 		len -= thislen;
99 	}
100 }
101 
vmax301_write8(struct map_info * map,__u8 d,unsigned long adr)102 static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr)
103 {
104 	spin_lock(&vmax301_spin);
105 	vmax301_page(map, adr);
106 	writeb(d, map->map_priv_2 + (adr & WINDOW_MASK));
107 	spin_unlock(&vmax301_spin);
108 }
109 
vmax301_write16(struct map_info * map,__u16 d,unsigned long adr)110 static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr)
111 {
112 	spin_lock(&vmax301_spin);
113 	vmax301_page(map, adr);
114 	writew(d, map->map_priv_2 + (adr & WINDOW_MASK));
115 	spin_unlock(&vmax301_spin);
116 }
117 
vmax301_write32(struct map_info * map,__u32 d,unsigned long adr)118 static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr)
119 {
120 	spin_lock(&vmax301_spin);
121 	vmax301_page(map, adr);
122 	writel(d, map->map_priv_2 + (adr & WINDOW_MASK));
123 	spin_unlock(&vmax301_spin);
124 }
125 
vmax301_copy_to(struct map_info * map,unsigned long to,const void * from,ssize_t len)126 static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
127 {
128 	while(len) {
129 		unsigned long thislen = len;
130 		if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
131 			thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
132 
133 		spin_lock(&vmax301_spin);
134 		vmax301_page(map, to);
135 		memcpy_toio(map->map_priv_2 + to, from, thislen);
136 		spin_unlock(&vmax301_spin);
137 		to += thislen;
138 		from += thislen;
139 		len -= thislen;
140 	}
141 }
142 
143 static struct map_info vmax_map[2] = {
144 	{
145 		name: "VMAX301 Internal Flash",
146 		size: 3*2*1024*1024,
147 		buswidth: 1,
148 		read8: vmax301_read8,
149 		read16: vmax301_read16,
150 		read32: vmax301_read32,
151 		copy_from: vmax301_copy_from,
152 		write8: vmax301_write8,
153 		write16: vmax301_write16,
154 		write32: vmax301_write32,
155 		copy_to: vmax301_copy_to,
156 		map_priv_1: WINDOW_START + WINDOW_LENGTH,
157 		map_priv_2: 0xFFFFFFFF
158 	},
159 	{
160 		name: "VMAX301 Socket",
161 		size: 0,
162 		buswidth: 1,
163 		read8: vmax301_read8,
164 		read16: vmax301_read16,
165 		read32: vmax301_read32,
166 		copy_from: vmax301_copy_from,
167 		write8: vmax301_write8,
168 		write16: vmax301_write16,
169 		write32: vmax301_write32,
170 		copy_to: vmax301_copy_to,
171 		map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
172 		map_priv_2: 0xFFFFFFFF
173 	}
174 };
175 
176 static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
177 
cleanup_vmax301(void)178 static void __exit cleanup_vmax301(void)
179 {
180 	int i;
181 
182 	for (i=0; i<2; i++) {
183 		if (vmax_mtd[i]) {
184 			del_mtd_device(vmax_mtd[i]);
185 			map_destroy(vmax_mtd[i]);
186 		}
187 	}
188 	iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
189 }
190 
init_vmax301(void)191 int __init init_vmax301(void)
192 {
193 	int i;
194 	unsigned long iomapadr;
195 	// Print out our little header..
196 	printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
197 	       WINDOW_START+4*WINDOW_LENGTH);
198 
199 	iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
200 	if (!iomapadr) {
201 		printk("Failed to ioremap memory region\n");
202 		return -EIO;
203 	}
204 	/* Put the address in the map's private data area.
205 	   We store the actual MTD IO address rather than the
206 	   address of the first half, because it's used more
207 	   often.
208 	*/
209 	vmax_map[0].map_priv_1 = iomapadr + WINDOW_START;
210 	vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START);
211 
212 	for (i=0; i<2; i++) {
213 		vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
214 		if (!vmax_mtd[i])
215 			vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
216 		if (!vmax_mtd[i])
217 			vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
218 		if (!vmax_mtd[i])
219 			vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
220 		if (vmax_mtd[i]) {
221 			vmax_mtd[i]->module = THIS_MODULE;
222 			add_mtd_device(vmax_mtd[i]);
223 		}
224 	}
225 
226 	if (!vmax_mtd[1] && !vmax_mtd[2]) {
227 		iounmap((void *)iomapadr);
228 		return -ENXIO;
229 	}
230 
231 	return 0;
232 }
233 
234 module_init(init_vmax301);
235 module_exit(cleanup_vmax301);
236 
237 MODULE_LICENSE("GPL");
238 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
239 MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");
240