blob: b3e48739543551e6f506fcb362739262073e22e3 [file] [log] [blame]
Thomas Gleixner69f34c92005-11-07 11:15:40 +00001// $Id: vmax301.c,v 1.32 2005/11/07 11:14:29 gleixner Exp $
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/* ######################################################################
3
4 Tempustech VMAX SBC301 MTD Driver.
Thomas Gleixner69f34c92005-11-07 11:15:40 +00005
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 The VMAx 301 is a SBC based on . It
7 comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
Thomas Gleixner69f34c92005-11-07 11:15:40 +00008 more flash. Each unit has it's own 8k mapping into a settable region
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 (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
Thomas Gleixner69f34c92005-11-07 11:15:40 +000013 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
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 itself and begin executing.
Thomas Gleixner69f34c92005-11-07 11:15:40 +000016
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 ##################################################################### */
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#include <linux/mtd/mtd.h>
28
29
30#define WINDOW_START 0xd8000
31#define WINDOW_LENGTH 0x2000
32#define WINDOW_SHIFT 25
33#define WINDOW_MASK 0x1FFF
34
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
Thomas Gleixner69f34c92005-11-07 11:15:40 +000038 the extra indirection from having one of the map->map_priv
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 fields pointing to yet another private struct.
40*/
41static DEFINE_SPINLOCK(vmax301_spin);
42
43static void __vmax301_page(struct map_info *map, unsigned long page)
44{
45 writew(page, map->map_priv_2 - WINDOW_LENGTH);
46 map->map_priv_1 = page;
47}
48
49static inline void vmax301_page(struct map_info *map,
50 unsigned long ofs)
51{
52 unsigned long page = (ofs >> WINDOW_SHIFT);
53 if (map->map_priv_1 != page)
54 __vmax301_page(map, page);
55}
56
57static map_word vmax301_read8(struct map_info *map, unsigned long ofs)
58{
59 map_word ret;
60 spin_lock(&vmax301_spin);
61 vmax301_page(map, ofs);
62 ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
63 spin_unlock(&vmax301_spin);
64 return ret;
65}
66
67static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
68{
69 while(len) {
70 unsigned long thislen = len;
71 if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
72 thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
73 spin_lock(&vmax301_spin);
74 vmax301_page(map, from);
75 memcpy_fromio(to, map->map_priv_2 + from, thislen);
76 spin_unlock(&vmax301_spin);
77 to += thislen;
78 from += thislen;
79 len -= thislen;
80 }
81}
82
83static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr)
84{
85 spin_lock(&vmax301_spin);
86 vmax301_page(map, adr);
87 writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK));
88 spin_unlock(&vmax301_spin);
89}
90
91static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
92{
93 while(len) {
94 unsigned long thislen = len;
95 if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
96 thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
97
98 spin_lock(&vmax301_spin);
99 vmax301_page(map, to);
100 memcpy_toio(map->map_priv_2 + to, from, thislen);
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000101 spin_unlock(&vmax301_spin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 to += thislen;
103 from += thislen;
104 len -= thislen;
105 }
106}
107
108static struct map_info vmax_map[2] = {
109 {
110 .name = "VMAX301 Internal Flash",
111 .phys = NO_XIP,
112 .size = 3*2*1024*1024,
113 .bankwidth = 1,
114 .read = vmax301_read8,
115 .copy_from = vmax301_copy_from,
116 .write = vmax301_write8,
117 .copy_to = vmax301_copy_to,
118 .map_priv_1 = WINDOW_START + WINDOW_LENGTH,
119 .map_priv_2 = 0xFFFFFFFF
120 },
121 {
122 .name = "VMAX301 Socket",
123 .phys = NO_XIP,
124 .size = 0,
125 .bankwidth = 1,
126 .read = vmax301_read8,
127 .copy_from = vmax301_copy_from,
128 .write = vmax301_write8,
129 .copy_to = vmax301_copy_to,
130 .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
131 .map_priv_2 = 0xFFFFFFFF
132 }
133};
134
135static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
136
137static void __exit cleanup_vmax301(void)
138{
139 int i;
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000140
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 for (i=0; i<2; i++) {
142 if (vmax_mtd[i]) {
143 del_mtd_device(vmax_mtd[i]);
144 map_destroy(vmax_mtd[i]);
145 }
146 }
147 iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
148}
149
150int __init init_vmax301(void)
151{
152 int i;
153 unsigned long iomapadr;
154 // Print out our little header..
155 printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
156 WINDOW_START+4*WINDOW_LENGTH);
157
158 iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
159 if (!iomapadr) {
160 printk("Failed to ioremap memory region\n");
161 return -EIO;
162 }
163 /* Put the address in the map's private data area.
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000164 We store the actual MTD IO address rather than the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 address of the first half, because it's used more
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000166 often.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 */
168 vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
169 vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 for (i=0; i<2; i++) {
172 vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
173 if (!vmax_mtd[i])
174 vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
175 if (!vmax_mtd[i])
176 vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
177 if (!vmax_mtd[i])
178 vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
179 if (vmax_mtd[i]) {
180 vmax_mtd[i]->owner = THIS_MODULE;
181 add_mtd_device(vmax_mtd[i]);
182 }
183 }
184
Petri T. Koistinenb5ac5d72006-03-31 02:29:54 -0800185 if (!vmax_mtd[0] && !vmax_mtd[1]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 iounmap((void *)iomapadr);
187 return -ENXIO;
188 }
189
190 return 0;
191}
192
193module_init(init_vmax301);
194module_exit(cleanup_vmax301);
195
196MODULE_LICENSE("GPL");
197MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
198MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");