CFE-ready mtd driver, needs fixing though, update kernel config
[librecmc/librecmc.git] / target / linux / adm5120-2.6 / files / drivers / mtd / maps / adm5120_mtd.c
1 /*
2  *      Flash device on the ADM5120 board
3  *
4  *      Copyright Florian Fainelli <florian@openwrt.org>
5  *   
6  *
7  *      This program is free software; you can redistribute it and/or
8  *      modify it under the terms of the GNU General Public License version
9  *      2 as published by the Free Software Foundation.
10  */
11
12 #include <linux/autoconf.h>
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/mtd/map.h>
17 #include <linux/mtd/partitions.h>
18 #include <linux/vmalloc.h>
19
20
21 #define FLASH_PHYS_ADDR         0x1FC00000
22 #define FLASH_SIZE              0x400000
23
24 #define IMAGE_LEN               10                      /* Length of Length Field */
25 #define ADDRESS_LEN             12                      /* Length of Address field */
26 #define EXTENDED_SIZE           0xBFC00000              /* Extended flash address */
27 #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
28  
29
30 static struct mtd_info *adm5120_mtd;
31 static struct mtd_partition *parsed_parts;
32
33 static struct map_info adm5120_mtd_map = {
34         .name = "adm5120",
35         .size = FLASH_SIZE,
36         .bankwidth = 2,
37         .phys = FLASH_PHYS_ADDR,
38 };
39
40 static int parse_cfe_partitions( struct mtd_info *master, struct mtd_partition **pparts)
41 {
42         int nrparts = 2, curpart = 0; // CFE and NVRAM always present.
43         struct adm5120_cfe_map {
44                 unsigned char tagVersion[4];                            // Version of the image tag
45                 unsigned char sig_1[20];                                // Company Line 1
46                 unsigned char sig_2[14];                                // Company Line 2
47                 unsigned char chipid[6];                                        // Chip this image is for
48                 unsigned char boardid[16];                              // Board name
49                 unsigned char bigEndian[2];                             // Map endianness -- 1 BE 0 LE
50                 unsigned char totalLength[IMAGE_LEN];           //Total length of image
51                 unsigned char cfeAddress[ADDRESS_LEN];  // Address in memory of CFE
52                 unsigned char cfeLength[IMAGE_LEN];             // Size of CFE
53                 unsigned char rootAddress[ADDRESS_LEN];         // Address in memory of rootfs
54                 unsigned char rootLength[IMAGE_LEN];            // Size of rootfs
55                 unsigned char kernelAddress[ADDRESS_LEN];       // Address in memory of kernel
56                 unsigned char kernelLength[IMAGE_LEN];  // Size of kernel
57                 unsigned char dualImage[2];                             // Unused at present
58                 unsigned char inactiveFlag[2];                  // Unused at present
59                 unsigned char reserved1[74];                            // Reserved area not in use
60                 unsigned char imageCRC[4];                              // CRC32 of images
61                 unsigned char reserved2[16];                            // Unused at present
62                 unsigned char headerCRC[4];                             // CRC32 of header excluding tagVersion
63                 unsigned char reserved3[16];                            // Unused at present
64         } *buf;
65         struct mtd_partition *parts;
66         int ret;
67         size_t retlen;
68         unsigned int rootfsaddr, kerneladdr, spareaddr;
69         unsigned int rootfslen, kernellen, sparelen, totallen;
70         int namelen = 0;
71         int i;
72         // Allocate memory for buffer
73         buf = vmalloc(sizeof(struct adm5120_cfe_map));
74
75         if (!buf)
76                 return -ENOMEM;
77
78         // Get the tag
79         ret = master->read(master,master->erasesize,sizeof(struct adm5120_cfe_map), &retlen, (void *)buf);
80
81         if (retlen != sizeof(struct adm5120_cfe_map)){
82                 vfree(buf);
83                 return -EIO;
84         };
85
86         printk("adm5120: CFE boot tag found with version %s and board type %s.\n",buf->tagVersion,buf->boardid);
87         // Get the values and calculate
88         sscanf(buf->rootAddress,"%u", &rootfsaddr);
89         rootfsaddr = rootfsaddr - EXTENDED_SIZE;
90         sscanf(buf->rootLength, "%u", &rootfslen);
91         sscanf(buf->kernelAddress, "%u", &kerneladdr);
92         kerneladdr = kerneladdr - EXTENDED_SIZE;
93         sscanf(buf->kernelLength, "%u", &kernellen);
94         sscanf(buf->totalLength, "%u", &totallen);
95         spareaddr = ROUNDUP(totallen,master->erasesize) + master->erasesize;
96         sparelen = master->size - spareaddr - master->erasesize;
97         // Determine number of partitions
98         namelen = 8;
99         if (rootfslen > 0){
100                 nrparts++;
101                 namelen =+ 6;
102         };
103
104         if (kernellen > 0){
105                 nrparts++;
106                 namelen =+ 6;
107         };
108         if (sparelen > 0){
109                 nrparts++;
110                 namelen =+ 6;
111         };
112         // Ask kernel for more memory.
113         parts = kmalloc(sizeof(*parts)*nrparts+10*nrparts, GFP_KERNEL);
114         if (!parts){
115                 vfree(buf);
116                 return -ENOMEM;
117         };
118         memset(parts,0,sizeof(*parts)*nrparts+10*nrparts);
119         // Start building partition list
120         parts[curpart].name = "CFE";
121         parts[curpart].offset = 0;
122         parts[curpart].size = master->erasesize;
123         curpart++;
124         if (kernellen > 0) {
125                 parts[curpart].name = "Kernel";
126                 parts[curpart].offset = kerneladdr;
127                 parts[curpart].size = kernellen;
128                 curpart++;
129         };
130         if (rootfslen > 0) {
131                 parts[curpart].name = "Rootfs";
132                 parts[curpart].offset = rootfsaddr;
133                 parts[curpart].size = rootfslen;
134                 curpart++;
135         };
136         if (sparelen > 0){
137                 parts[curpart].name = "OpenWrt";
138                 parts[curpart].offset = spareaddr;
139                 parts[curpart].size = sparelen;
140                 curpart++;
141         };
142         parts[curpart].name = "NVRAM";
143         parts[curpart].offset = master->size - master->erasesize;
144         parts[curpart].size = master->erasesize;
145         for (i = 0; i < nrparts; i++) {
146                 printk("adm5120: Partition %d is %s offset %x and length %x\n", i, parts[i].name, parts[i].offset, parts[i].size);
147         }
148         *pparts = parts;
149         vfree(buf);
150         return nrparts;
151 };
152
153 static int adm5120_detect_cfe(struct mtd_info *master)
154 {
155         int idoffset = 0x4e0;
156 #ifdef CONFIG_CPU_BIG_ENDIAN
157         static char idstring[8] = "CFE1CFE1";
158 #else
159         static char idstring[8] = "1EFC1EFC";
160 #endif
161         char buf[8];
162         int ret;
163         size_t retlen;
164
165         ret = master->read(master, idoffset, 8, &retlen, (void *)buf);
166         printk("adm5120: Read Signature value of %s\n", buf);
167         return strcmp(idstring,buf);
168 }
169
170 static int __init adm5120_mtd_init(void)
171 {
172         printk(KERN_INFO "ADM5120 board flash (0x%08x at 0x%08x)\n", FLASH_SIZE,
173             FLASH_PHYS_ADDR);
174
175         adm5120_mtd_map.virt = ioremap(FLASH_PHYS_ADDR, FLASH_SIZE);
176
177         if (!adm5120_mtd_map.virt) {
178                 printk("ADM5120: failed to ioremap\n");
179                 return -EIO;
180         }
181                 
182         simple_map_init(&adm5120_mtd_map);
183
184         adm5120_mtd = do_map_probe("cfi_probe", &adm5120_mtd_map);
185
186         if (adm5120_mtd) {
187                 adm5120_mtd->owner = THIS_MODULE;
188                 
189                 if (adm5120_detect_cfe(adm5120_mtd) == 0)
190                 {
191                         int parsed_nr_parts = 0;
192                         char * part_type;
193
194                         printk("adm5120: CFE bootloader detected\n");
195                         
196                         if (parsed_nr_parts == 0) {
197                                 int ret = parse_cfe_partitions(adm5120_mtd, &parsed_parts);
198                                 
199                                 if (ret > 0) {
200                                         part_type = "CFE";
201                                         parsed_nr_parts = ret;
202                                 }
203                         }
204                         add_mtd_partitions(adm5120_mtd, parsed_parts, parsed_nr_parts);
205                         return 0;
206                 } else {
207                         add_mtd_device(adm5120_mtd);
208                 }
209                 return 0;
210         }
211         iounmap(adm5120_mtd);
212         return -ENXIO;
213 }
214
215 static void __exit adm5120_mtd_exit(void)
216 {
217         if (adm5120_mtd) {
218                 del_mtd_device(adm5120_mtd);
219                 map_destroy(adm5120_mtd);
220         }
221
222         if (adm5120_mtd_map.virt) {
223                 iounmap(adm5120_mtd_map.virt);
224                 adm5120_mtd_map.virt = 0;
225         }
226         
227 }
228
229 module_init(adm5120_mtd_init);
230 module_exit(adm5120_mtd_exit);
231
232 MODULE_LICENSE("GPL");
233 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
234 MODULE_DESCRIPTION("MTD map driver for ADM5120 boards");