Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
octagon-5066.c
Go to the documentation of this file.
1 /* ######################################################################
2 
3  Octagon 5066 MTD Driver.
4 
5  The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It
6  comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that
7  is replacable by flash. Both units are mapped through a multiplexer
8  into a 32k memory window at 0xe8000. The control register for the
9  multiplexing unit is located at IO 0x208 with a bit map of
10  0-5 Page Selection in 32k increments
11  6-7 Device selection:
12  00 SSD off
13  01 SSD 0 (Socket)
14  10 SSD 1 (Flash chip)
15  11 undefined
16 
17  On each SSD, the first 128k is reserved for use by the bios
18  (actually it IS the bios..) This only matters if you are booting off the
19  flash, you must not put a file system starting there.
20 
21  The driver tries to do a detection algorithm to guess what sort of devices
22  are plugged into the sockets.
23 
24  ##################################################################### */
25 
26 #include <linux/module.h>
27 #include <linux/ioport.h>
28 #include <linux/init.h>
29 #include <asm/io.h>
30 
31 #include <linux/mtd/map.h>
32 #include <linux/mtd/mtd.h>
33 
34 #define WINDOW_START 0xe8000
35 #define WINDOW_LENGTH 0x8000
36 #define WINDOW_SHIFT 27
37 #define WINDOW_MASK 0x7FFF
38 #define PAGE_IO 0x208
39 
40 static volatile char page_n_dev = 0;
41 static unsigned long iomapadr;
42 static DEFINE_SPINLOCK(oct5066_spin);
43 
44 /*
45  * We use map_priv_1 to identify which device we are.
46  */
47 
48 static void __oct5066_page(struct map_info *map, __u8 byte)
49 {
50  outb(byte,PAGE_IO);
51  page_n_dev = byte;
52 }
53 
54 static inline void oct5066_page(struct map_info *map, unsigned long ofs)
55 {
56  __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT);
57 
58  if (page_n_dev != byte)
59  __oct5066_page(map, byte);
60 }
61 
62 
63 static map_word oct5066_read8(struct map_info *map, unsigned long ofs)
64 {
65  map_word ret;
66  spin_lock(&oct5066_spin);
67  oct5066_page(map, ofs);
68  ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
69  spin_unlock(&oct5066_spin);
70  return ret;
71 }
72 
73 static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
74 {
75  while(len) {
76  unsigned long thislen = len;
77  if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
78  thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
79 
80  spin_lock(&oct5066_spin);
81  oct5066_page(map, from);
82  memcpy_fromio(to, iomapadr + from, thislen);
83  spin_unlock(&oct5066_spin);
84  to += thislen;
85  from += thislen;
86  len -= thislen;
87  }
88 }
89 
90 static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr)
91 {
92  spin_lock(&oct5066_spin);
93  oct5066_page(map, adr);
94  writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
95  spin_unlock(&oct5066_spin);
96 }
97 
98 static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
99 {
100  while(len) {
101  unsigned long thislen = len;
102  if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
103  thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
104 
105  spin_lock(&oct5066_spin);
106  oct5066_page(map, to);
107  memcpy_toio(iomapadr + to, from, thislen);
108  spin_unlock(&oct5066_spin);
109  to += thislen;
110  from += thislen;
111  len -= thislen;
112  }
113 }
114 
115 static struct map_info oct5066_map[2] = {
116  {
117  .name = "Octagon 5066 Socket",
118  .phys = NO_XIP,
119  .size = 512 * 1024,
120  .bankwidth = 1,
121  .read = oct5066_read8,
122  .copy_from = oct5066_copy_from,
123  .write = oct5066_write8,
124  .copy_to = oct5066_copy_to,
125  .map_priv_1 = 1<<6
126  },
127  {
128  .name = "Octagon 5066 Internal Flash",
129  .phys = NO_XIP,
130  .size = 2 * 1024 * 1024,
131  .bankwidth = 1,
132  .read = oct5066_read8,
133  .copy_from = oct5066_copy_from,
134  .write = oct5066_write8,
135  .copy_to = oct5066_copy_to,
136  .map_priv_1 = 2<<6
137  }
138 };
139 
140 static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
141 
142 // OctProbe - Sense if this is an octagon card
143 // ---------------------------------------------------------------------
144 /* Perform a simple validity test, we map the window select SSD0 and
145  change pages while monitoring the window. A change in the window,
146  controlled by the PAGE_IO port is a functioning 5066 board. This will
147  fail if the thing in the socket is set to a uniform value. */
148 static int __init OctProbe(void)
149 {
150  unsigned int Base = (1 << 6);
151  unsigned long I;
152  unsigned long Values[10];
153  for (I = 0; I != 20; I++)
154  {
155  outb(Base + (I%10),PAGE_IO);
156  if (I < 10)
157  {
158  // Record the value and check for uniqueness
159  Values[I%10] = readl(iomapadr);
160  if (I > 0 && Values[I%10] == Values[0])
161  return -EAGAIN;
162  }
163  else
164  {
165  // Make sure we get the same values on the second pass
166  if (Values[I%10] != readl(iomapadr))
167  return -EAGAIN;
168  }
169  }
170  return 0;
171 }
172 
173 void cleanup_oct5066(void)
174 {
175  int i;
176  for (i=0; i<2; i++) {
177  if (oct5066_mtd[i]) {
178  mtd_device_unregister(oct5066_mtd[i]);
179  map_destroy(oct5066_mtd[i]);
180  }
181  }
182  iounmap((void *)iomapadr);
184 }
185 
186 static int __init init_oct5066(void)
187 {
188  int i;
189  int ret = 0;
190 
191  // Do an autoprobe sequence
192  if (!request_region(PAGE_IO,1,"Octagon SSD")) {
193  printk(KERN_NOTICE "5066: Page Register in Use\n");
194  return -EAGAIN;
195  }
196  iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
197  if (!iomapadr) {
198  printk(KERN_NOTICE "Failed to ioremap memory region\n");
199  ret = -EIO;
200  goto out_rel;
201  }
202  if (OctProbe() != 0) {
203  printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
204  iounmap((void *)iomapadr);
205  ret = -EAGAIN;
206  goto out_unmap;
207  }
208 
209  // Print out our little header..
210  printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
212 
213  for (i=0; i<2; i++) {
214  oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]);
215  if (!oct5066_mtd[i])
216  oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
217  if (!oct5066_mtd[i])
218  oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]);
219  if (!oct5066_mtd[i])
220  oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
221  if (oct5066_mtd[i]) {
222  oct5066_mtd[i]->owner = THIS_MODULE;
223  mtd_device_register(oct5066_mtd[i], NULL, 0);
224  }
225  }
226 
227  if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
228  cleanup_oct5066();
229  return -ENXIO;
230  }
231 
232  return 0;
233 
234  out_unmap:
235  iounmap((void *)iomapadr);
236  out_rel:
238  return ret;
239 }
240 
241 module_init(init_oct5066);
243 
244 MODULE_LICENSE("GPL");
245 MODULE_AUTHOR("Jason Gunthorpe <[email protected]>, David Woodhouse <[email protected]>");
246 MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer");