e732334e92b127e40a19511775b794dc32f2bcb6
[oweals/openwrt.git] / target / linux / rdc / files / drivers / mtd / maps / bifferboard-flash.c
1 /* Flash mapping for Bifferboard, (c) bifferos@yahoo.co.uk 
2  * Works with 1, 4 and 8MB flash versions
3  */
4
5 #include <linux/delay.h>
6 #include <linux/mtd/mtd.h>
7 #include <linux/mtd/map.h>
8 #include <linux/mtd/partitions.h>
9
10 #define DRV "bifferboard-flash: "
11
12 static struct mtd_info *bb_mtd = NULL;
13
14 #define SIZE_BIFFBOOT 0x10000
15 #define SIZE_SECTOR   0x10000
16 #define SIZE_CONFIG   0x02000
17 #define SIZE_1MB 0x00100000
18 #define SIZE_4MB 0x00400000
19 #define SIZE_8MB 0x00800000
20
21 #define BASE_1MB 0xfff00000
22 #define BASE_4MB 0xffc00000
23 #define BASE_8MB 0xff800000
24
25 #define OFFS_KERNEL 0x6000
26 #define OFFS_CONFIG 0x4000
27
28 /*
29  * Flash detection code.  This is needed because the cfi_probe doesn't probe
30  * for the size correctly.  You actually have to know the flash size before you
31  * call it, or so it seems.  It does work correctly if you map the entire flash
32  * chip, but unsure of the implications for mapping more mem than exists.
33  */
34
35 static unsigned char ReadFlash(void* base, u32 addr)
36 {
37         unsigned char val = *(volatile unsigned char *)(base+addr);  
38         udelay(1);
39         return val;
40 }
41
42 static void WriteFlash(void* base, u32 addr, unsigned short data)
43 {
44         *(volatile unsigned short *)(base+addr) = data;
45 }
46
47 static DEFINE_SPINLOCK(flash_lock);
48
49 static u32 bb_detect(void)
50 {
51         u32 ret = 0;  
52         ulong flags;
53   
54         /* for detection, map in just the 1MB device, 1st 64k is enough */
55         void* base = ioremap_nocache(BASE_1MB, 0x10000);
56     
57         if (!base)
58         {
59                 pr_err(DRV "Failed to map flash for probing\n");
60                 return 0;
61         }
62   
63         spin_lock_irqsave(&flash_lock, flags);  
64   
65         /* put flash in auto-detect mode */
66         WriteFlash(base, 0xaaaa, 0xaaaa);
67         WriteFlash(base, 0x5554, 0x5555);
68         WriteFlash(base, 0xaaaa, 0x9090);
69
70         /* Read the auto-config data - 4 values in total */
71         ret = ReadFlash(base, 0x0000);  ret <<= 8;
72         ret |= ReadFlash(base, 0x0200);  ret <<= 8;
73         ret |= ReadFlash(base, 0x0003);  ret <<= 8;
74         ret |= ReadFlash(base, 0x0002);
75   
76         /* exit the autodetect state */
77         WriteFlash(base, 0x0000, 0xf0f0);
78   
79         spin_unlock_irqrestore(&flash_lock, flags);
80   
81         /* unmap it, it'll be re-mapped based on the detection */
82         iounmap(base);
83   
84         return ret;
85 }
86
87
88 static struct map_info bb_map;
89
90 /* Update the map, using the detected flash signature. */
91 static int bb_adjust_map(unsigned long sig, struct map_info* m)
92 {
93         m->bankwidth = 2;
94         switch (sig)
95         {
96                 case 0x7f1c225b :
97                         m->name = "ENLV800B";
98                         m->phys = BASE_1MB;
99                         m->size = SIZE_1MB;
100                         break;
101                 case 0x7f1c22f9 :
102                         m->name = "ENLV320B";
103                         m->phys = BASE_4MB;
104                         m->size = SIZE_4MB;
105                         break;
106                 case 0x7f1c22cb :
107                         m->name = "ENLV640B";
108                         m->phys = BASE_8MB;
109                         m->size = SIZE_8MB;
110                         break;
111                 default:
112                         return -ENXIO;
113         }
114         return 0;
115 }
116
117
118 /* adjust partition sizes, prior to adding the partition
119  * Values not specified by defines here are subject to replacement based on 
120  * the real flash size. (0x00000000 being the exception, of course) */
121
122 static struct mtd_partition bb_parts[] = 
123 {
124 /* 0 */ { name: "kernel",   offset: 0x00000000,         size: 0x000fa000 },
125 /* 1 */ { name: "rootfs",   offset: MTDPART_OFS_APPEND, size: 0x002F0000 },
126 /* 2 */ { name: "biffboot", offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL, mask_flags: MTD_WRITEABLE },
127 };
128
129
130 /* returns the count of partitions to be used */
131 static ulong bb_adjust_partitions(struct map_info* m)
132 {
133         /* Read the kernel offset value, 1036 bytes into the config block. */
134         u16 km_units = *((u16*)(m->virt + OFFS_CONFIG + 1036));
135         u32 kernelmax = 0x200000;  /* Biffboot 2.7 or earlier default */
136         
137         /* special-case 1MB devices, because there are fewer partitions */
138         if (m->size == SIZE_1MB)
139         {
140                 bb_parts[0].size   = SIZE_1MB - SIZE_BIFFBOOT;  /* kernel */
141                 bb_parts[1].name   = "biffboot";                /* biffboot */
142                 bb_parts[1].offset = MTDPART_OFS_APPEND;        /* biffboot */
143                 bb_parts[1].size   = MTDPART_SIZ_FULL;          /* biffboot */
144                 bb_parts[1].mask_flags = MTD_WRITEABLE;
145                 return 2;   /* because there's no rootfs now */
146         }
147         
148         /* sanity check */
149         if (km_units > ((m->size-SIZE_BIFFBOOT)/SIZE_SECTOR))
150         {
151                 pr_err(DRV "config block has invalid kernelmax\n");
152                 return 0;
153         }
154         
155         kernelmax = km_units * SIZE_SECTOR;
156         
157         /* Kernel */
158         bb_parts[0].size = kernelmax;
159         
160         /* rootfs */
161         bb_parts[1].size   = m->size - kernelmax - SIZE_BIFFBOOT;
162                 
163         return 3;  /* 3 partitions */
164 }
165
166
167 static int __init init_bb_map(void)
168 {
169         int ret;
170         ulong signature = bb_detect();
171         ulong part_len;
172         
173         if (!signature)
174         {
175                 pr_err(DRV "Undetected flash chip");
176                 return -ENXIO;
177         }
178         
179         ret = bb_adjust_map(signature, &bb_map);
180         
181         if (ret)
182         {
183                 pr_err(DRV "Unrecognised flash chip (signature: 0x%lx)\n", signature);
184                 return ret;
185         }
186         
187         bb_map.virt = ioremap_nocache(bb_map.phys, bb_map.size);
188         if (!bb_map.virt) 
189         {
190                 pr_err(DRV "ioremap\n");
191                 return -EIO;
192         }
193         
194         bb_mtd = do_map_probe("cfi_probe", &bb_map);
195         if (!bb_mtd)
196         {
197                 /* cfi_probe fails here for 1MB devices */
198                 pr_err(DRV "cfi_probe\n");
199                 ret = -ENXIO;
200                 goto err;
201         }
202
203         part_len = bb_adjust_partitions(&bb_map);
204         if (!part_len)
205         {
206                 pr_err(DRV "no partitions established");
207                 ret = -ENXIO;
208                 goto err2;
209         }
210         
211         bb_mtd->owner = THIS_MODULE;
212         ret = add_mtd_partitions(bb_mtd, bb_parts, part_len);
213         if (ret) 
214         {
215                 pr_err(DRV "add_mtd_partitions\n");
216                 ret = -ENXIO;
217                 goto err2;
218         }
219         
220         return 0;
221         
222 err2:
223         map_destroy(bb_mtd);
224 err:
225         iounmap(bb_map.virt);
226
227         return ret;
228 }
229
230 static void __exit exit_bb_map(void)
231 {
232         del_mtd_partitions(bb_mtd);
233         map_destroy(bb_mtd);    
234         iounmap(bb_map.virt);
235 }
236
237 module_init(init_bb_map);
238 module_exit(exit_bb_map);
239
240 MODULE_LICENSE("GPL");
241 MODULE_AUTHOR("Bifferos <bifferos@yahoo.co.uk>");
242 MODULE_DESCRIPTION("MTD map driver for Bifferboard");
243