1 // $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $
2 /* ######################################################################
4 Tempustech VMAX SBC301 MTD Driver.
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.
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.
17 ##################################################################### */
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>
26 #include <linux/mtd/map.h>
27 #include <linux/mtd/mtd.h>
30 #define WINDOW_START 0xd8000
31 #define WINDOW_LENGTH 0x2000
32 #define WINDOW_SHIFT 25
33 #define WINDOW_MASK 0x1FFF
35 /* Actually we could use two spinlocks, but we'd have to have
36 more private space in the struct map_info. We lose a little
37 performance like this, but we'd probably lose more by having
38 the extra indirection from having one of the map->map_priv
39 fields pointing to yet another private struct.
41 static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED;
43 static void __vmax301_page(struct map_info *map, unsigned long page)
45 writew(page, map->map_priv_2 - WINDOW_LENGTH);
46 map->map_priv_1 = page;
49 static inline void vmax301_page(struct map_info *map,
52 unsigned long page = (ofs >> WINDOW_SHIFT);
53 if (map->map_priv_1 != page)
54 __vmax301_page(map, page);
57 static __u8 vmax301_read8(struct map_info *map, unsigned long ofs)
60 spin_lock(&vmax301_spin);
61 vmax301_page(map, ofs);
62 ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
63 spin_unlock(&vmax301_spin);
67 static __u16 vmax301_read16(struct map_info *map, unsigned long ofs)
70 spin_lock(&vmax301_spin);
71 vmax301_page(map, ofs);
72 ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK));
73 spin_unlock(&vmax301_spin);
77 static __u32 vmax301_read32(struct map_info *map, unsigned long ofs)
80 spin_lock(&vmax301_spin);
81 vmax301_page(map, ofs);
82 ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK));
83 spin_unlock(&vmax301_spin);
87 static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
90 unsigned long thislen = len;
91 if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
92 thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
93 spin_lock(&vmax301_spin);
94 vmax301_page(map, from);
95 memcpy_fromio(to, map->map_priv_2 + from, thislen);
96 spin_unlock(&vmax301_spin);
103 static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr)
105 spin_lock(&vmax301_spin);
106 vmax301_page(map, adr);
107 writeb(d, map->map_priv_2 + (adr & WINDOW_MASK));
108 spin_unlock(&vmax301_spin);
111 static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr)
113 spin_lock(&vmax301_spin);
114 vmax301_page(map, adr);
115 writew(d, map->map_priv_2 + (adr & WINDOW_MASK));
116 spin_unlock(&vmax301_spin);
119 static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr)
121 spin_lock(&vmax301_spin);
122 vmax301_page(map, adr);
123 writel(d, map->map_priv_2 + (adr & WINDOW_MASK));
124 spin_unlock(&vmax301_spin);
127 static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
130 unsigned long thislen = len;
131 if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
132 thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
134 spin_lock(&vmax301_spin);
135 vmax301_page(map, to);
136 memcpy_toio(map->map_priv_2 + to, from, thislen);
137 spin_unlock(&vmax301_spin);
144 static struct map_info vmax_map[2] = {
146 .name = "VMAX301 Internal Flash",
148 .size = 3*2*1024*1024,
150 .read8 = vmax301_read8,
151 .read16 = vmax301_read16,
152 .read32 = vmax301_read32,
153 .copy_from = vmax301_copy_from,
154 .write8 = vmax301_write8,
155 .write16 = vmax301_write16,
156 .write32 = vmax301_write32,
157 .copy_to = vmax301_copy_to,
158 .map_priv_1 = WINDOW_START + WINDOW_LENGTH,
159 .map_priv_2 = 0xFFFFFFFF
162 .name = "VMAX301 Socket",
166 .read8 = vmax301_read8,
167 .read16 = vmax301_read16,
168 .read32 = vmax301_read32,
169 .copy_from = vmax301_copy_from,
170 .write8 = vmax301_write8,
171 .write16 = vmax301_write16,
172 .write32 = vmax301_write32,
173 .copy_to = vmax301_copy_to,
174 .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
175 .map_priv_2 = 0xFFFFFFFF
179 static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
181 static void __exit cleanup_vmax301(void)
185 for (i=0; i<2; i++) {
187 del_mtd_device(vmax_mtd[i]);
188 map_destroy(vmax_mtd[i]);
191 iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
194 int __init init_vmax301(void)
197 unsigned long iomapadr;
198 // Print out our little header..
199 printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
200 WINDOW_START+4*WINDOW_LENGTH);
202 iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
204 printk("Failed to ioremap memory region\n");
207 /* Put the address in the map's private data area.
208 We store the actual MTD IO address rather than the
209 address of the first half, because it's used more
212 vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
213 vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
215 for (i=0; i<2; i++) {
216 vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
218 vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
220 vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
222 vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
224 vmax_mtd[i]->owner = THIS_MODULE;
225 add_mtd_device(vmax_mtd[i]);
229 if (!vmax_mtd[1] && !vmax_mtd[2]) {
230 iounmap((void *)iomapadr);
237 module_init(init_vmax301);
238 module_exit(cleanup_vmax301);
240 MODULE_LICENSE("GPL");
241 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
242 MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");