blob: 5e68de73eabc9b94fce43255806994f5a8f9baaa [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/ioport.h>
20#include <linux/init.h>
21#include <linux/spinlock.h>
22#include <asm/io.h>
23
24#include <linux/mtd/map.h>
25#include <linux/mtd/mtd.h>
26
27
28#define WINDOW_START 0xd8000
29#define WINDOW_LENGTH 0x2000
30#define WINDOW_SHIFT 25
31#define WINDOW_MASK 0x1FFF
32
33/* Actually we could use two spinlocks, but we'd have to have
34 more private space in the struct map_info. We lose a little
35 performance like this, but we'd probably lose more by having
Thomas Gleixner69f34c92005-11-07 11:15:40 +000036 the extra indirection from having one of the map->map_priv
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 fields pointing to yet another private struct.
38*/
39static DEFINE_SPINLOCK(vmax301_spin);
40
41static void __vmax301_page(struct map_info *map, unsigned long page)
42{
43 writew(page, map->map_priv_2 - WINDOW_LENGTH);
44 map->map_priv_1 = page;
45}
46
47static inline void vmax301_page(struct map_info *map,
48 unsigned long ofs)
49{
50 unsigned long page = (ofs >> WINDOW_SHIFT);
51 if (map->map_priv_1 != page)
52 __vmax301_page(map, page);
53}
54
55static map_word vmax301_read8(struct map_info *map, unsigned long ofs)
56{
57 map_word ret;
58 spin_lock(&vmax301_spin);
59 vmax301_page(map, ofs);
60 ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
61 spin_unlock(&vmax301_spin);
62 return ret;
63}
64
65static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
66{
67 while(len) {
68 unsigned long thislen = len;
69 if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
70 thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
71 spin_lock(&vmax301_spin);
72 vmax301_page(map, from);
73 memcpy_fromio(to, map->map_priv_2 + from, thislen);
74 spin_unlock(&vmax301_spin);
75 to += thislen;
76 from += thislen;
77 len -= thislen;
78 }
79}
80
81static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr)
82{
83 spin_lock(&vmax301_spin);
84 vmax301_page(map, adr);
85 writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK));
86 spin_unlock(&vmax301_spin);
87}
88
89static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
90{
91 while(len) {
92 unsigned long thislen = len;
93 if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
94 thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
95
96 spin_lock(&vmax301_spin);
97 vmax301_page(map, to);
98 memcpy_toio(map->map_priv_2 + to, from, thislen);
Thomas Gleixner69f34c92005-11-07 11:15:40 +000099 spin_unlock(&vmax301_spin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 to += thislen;
101 from += thislen;
102 len -= thislen;
103 }
104}
105
106static struct map_info vmax_map[2] = {
107 {
108 .name = "VMAX301 Internal Flash",
109 .phys = NO_XIP,
110 .size = 3*2*1024*1024,
111 .bankwidth = 1,
112 .read = vmax301_read8,
113 .copy_from = vmax301_copy_from,
114 .write = vmax301_write8,
115 .copy_to = vmax301_copy_to,
116 .map_priv_1 = WINDOW_START + WINDOW_LENGTH,
117 .map_priv_2 = 0xFFFFFFFF
118 },
119 {
120 .name = "VMAX301 Socket",
121 .phys = NO_XIP,
122 .size = 0,
123 .bankwidth = 1,
124 .read = vmax301_read8,
125 .copy_from = vmax301_copy_from,
126 .write = vmax301_write8,
127 .copy_to = vmax301_copy_to,
128 .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
129 .map_priv_2 = 0xFFFFFFFF
130 }
131};
132
133static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
134
135static void __exit cleanup_vmax301(void)
136{
137 int i;
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 for (i=0; i<2; i++) {
140 if (vmax_mtd[i]) {
Jamie Ilesee0e87b2011-05-23 10:23:40 +0100141 mtd_device_unregister(vmax_mtd[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 map_destroy(vmax_mtd[i]);
143 }
144 }
145 iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
146}
147
Dmitri Vorobiev1aed1652008-11-25 02:55:10 +0200148static int __init init_vmax301(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
150 int i;
151 unsigned long iomapadr;
152 // Print out our little header..
153 printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
154 WINDOW_START+4*WINDOW_LENGTH);
155
156 iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
157 if (!iomapadr) {
158 printk("Failed to ioremap memory region\n");
159 return -EIO;
160 }
161 /* Put the address in the map's private data area.
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000162 We store the actual MTD IO address rather than the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 address of the first half, because it's used more
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000164 often.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 */
166 vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
167 vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
Thomas Gleixner69f34c92005-11-07 11:15:40 +0000168
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 for (i=0; i<2; i++) {
170 vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
171 if (!vmax_mtd[i])
172 vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
173 if (!vmax_mtd[i])
174 vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
175 if (!vmax_mtd[i])
176 vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
177 if (vmax_mtd[i]) {
178 vmax_mtd[i]->owner = THIS_MODULE;
Jamie Ilesee0e87b2011-05-23 10:23:40 +0100179 mtd_device_register(vmax_mtd[i], NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 }
181 }
182
Petri T. Koistinenb5ac5d72006-03-31 02:29:54 -0800183 if (!vmax_mtd[0] && !vmax_mtd[1]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 iounmap((void *)iomapadr);
185 return -ENXIO;
186 }
187
188 return 0;
189}
190
191module_init(init_vmax301);
192module_exit(cleanup_vmax301);
193
194MODULE_LICENSE("GPL");
195MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
196MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");