1// $Id: vmax301.c,v 1.1.1.1 2008/10/15 03:26:35 james26_jang 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*/
40static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED;
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 __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
66static __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
76static __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
86static 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
102static 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
110static 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
118static 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
126static 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
143static 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
176static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
177
178static 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
191int __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
234module_init(init_vmax301);
235module_exit(cleanup_vmax301);
236
237MODULE_LICENSE("GPL");
238MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
239MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");
240