blob: 6d452dcdfe34756656e2014739ccdabeb9d29064 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* ######################################################################
2
3 Tempustech VMAX SBC301 MTD Driver.
Thomas Gleixner69f34c92005-11-07 11:15:40 +00004
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 The VMAx 301 is a SBC based on . It
6 comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
Thomas Gleixner69f34c92005-11-07 11:15:40 +00007 more flash. Each unit has it's own 8k mapping into a settable region
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 (0xD8000). There are two 8k mappings for each MTD, the first is always set
9 to the lower 8k of the device the second is paged. Writing a 16 bit page
10 value to anywhere in the first 8k will cause the second 8k to page around.
11
Thomas Gleixner69f34c92005-11-07 11:15:40 +000012 To boot the device a bios extension must be installed into the first 8k
13 of flash that is smart enough to copy itself down, page in the rest of
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 itself and begin executing.
Thomas Gleixner69f34c92005-11-07 11:15:40 +000015
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 ##################################################################### */
17
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/ioport.h>
21#include <linux/init.h>
22#include <linux/spinlock.h>
23#include <asm/io.h>
24
25#include <linux/mtd/map.h>
26#include <linux/mtd/mtd.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
Thomas Gleixner69f34c92005-11-07 11:15:40 +000037 the extra indirection from having one of the map->map_priv
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 fields pointing to yet another private struct.
39*/
40static DEFINE_SPINLOCK(vmax301_spin);
41
42static 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
48static 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
56static map_word vmax301_read8(struct map_info *map, unsigned long ofs)
57{
58 map_word ret;
59 spin_lock(&vmax301_spin);
60 vmax301_page(map, ofs);
61 ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
62 spin_unlock(&vmax301_spin);
63 return ret;
64}
65
66static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
67{
68 while(len) {
69 unsigned long thislen = len;
70 if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
71 thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
72 spin_lock(&vmax301_spin);
73 vmax301_page(map, from);
74 memcpy_fromio(to, map->map_priv_2 + from, thislen);
75 spin_unlock(&vmax301_spin);
76 to += thislen;
77 from += thislen;
78 len -= thislen;
79 }
80}
81
82static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr)
83{
84 spin_lock(&vmax301_spin);
85 vmax301_page(map, adr);
86 writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK));
87 spin_unlock(&vmax301_spin);
88}
89
90static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
91{
92 while(len) {
93 unsigned long thislen = len;
94 if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
95 thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
96
97 spin_lock(&vmax301_spin);
98 vmax301_page(map, to);
99 memcpy_toio(map->map_priv_2 + to, from, thislen);
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000100 spin_unlock(&vmax301_spin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 to += thislen;
102 from += thislen;
103 len -= thislen;
104 }
105}
106
107static struct map_info vmax_map[2] = {
108 {
109 .name = "VMAX301 Internal Flash",
110 .phys = NO_XIP,
111 .size = 3*2*1024*1024,
112 .bankwidth = 1,
113 .read = vmax301_read8,
114 .copy_from = vmax301_copy_from,
115 .write = vmax301_write8,
116 .copy_to = vmax301_copy_to,
117 .map_priv_1 = WINDOW_START + WINDOW_LENGTH,
118 .map_priv_2 = 0xFFFFFFFF
119 },
120 {
121 .name = "VMAX301 Socket",
122 .phys = NO_XIP,
123 .size = 0,
124 .bankwidth = 1,
125 .read = vmax301_read8,
126 .copy_from = vmax301_copy_from,
127 .write = vmax301_write8,
128 .copy_to = vmax301_copy_to,
129 .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
130 .map_priv_2 = 0xFFFFFFFF
131 }
132};
133
134static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
135
136static void __exit cleanup_vmax301(void)
137{
138 int i;
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 for (i=0; i<2; i++) {
141 if (vmax_mtd[i]) {
142 del_mtd_device(vmax_mtd[i]);
143 map_destroy(vmax_mtd[i]);
144 }
145 }
146 iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
147}
148
Dmitri Vorobiev1aed1652008-11-25 02:55:10 +0200149static int __init init_vmax301(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
151 int i;
152 unsigned long iomapadr;
153 // Print out our little header..
154 printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
155 WINDOW_START+4*WINDOW_LENGTH);
156
157 iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
158 if (!iomapadr) {
159 printk("Failed to ioremap memory region\n");
160 return -EIO;
161 }
162 /* Put the address in the map's private data area.
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000163 We store the actual MTD IO address rather than the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 address of the first half, because it's used more
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000165 often.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 */
167 vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
168 vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000169
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 for (i=0; i<2; i++) {
171 vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
172 if (!vmax_mtd[i])
173 vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
174 if (!vmax_mtd[i])
175 vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
176 if (!vmax_mtd[i])
177 vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
178 if (vmax_mtd[i]) {
179 vmax_mtd[i]->owner = THIS_MODULE;
180 add_mtd_device(vmax_mtd[i]);
181 }
182 }
183
Petri T. Koistinenb5ac5d72006-03-31 02:29:54 -0800184 if (!vmax_mtd[0] && !vmax_mtd[1]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 iounmap((void *)iomapadr);
186 return -ENXIO;
187 }
188
189 return 0;
190}
191
192module_init(init_vmax301);
193module_exit(cleanup_vmax301);
194
195MODULE_LICENSE("GPL");
196MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
197MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");