kernel: modify mtd related patches for 3.14
[openwrt.git] / target / linux / adm8668 / files / drivers / mtd / maps / adm8668.c
1 /*
2  * Infineon/ADMTek ADM8668 (WildPass) partition parser support
3  *
4  * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us>
5  * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org>
6  *
7  * original functions for finding root filesystem from Mike Baker
8  *
9  * This program is free software; you can redistribute  it and/or modify it
10  * under  the terms of  the GNU General  Public License as published by the
11  * Free Software Foundation;  either version 2 of the  License, or (at your
12  * option) any later version.
13  *
14  * THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
15  * WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
17  * NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
20  * USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21  * ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * You should have received a copy of the  GNU General Public License along
26  * with this program; if not, write  to the Free Software Foundation, Inc.,
27  * 675 Mass Ave, Cambridge, MA 02139, USA.
28  *
29  *
30  * Copyright 2004, Broadcom Corporation
31  * All Rights Reserved.
32  *
33  * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
34  * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
35  * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
36  * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
37  */
38
39 #include <linux/module.h>
40 #include <linux/types.h>
41 #include <linux/kernel.h>
42 #include <linux/sched.h>
43 #include <linux/wait.h>
44 #include <linux/mtd/mtd.h>
45 #include <linux/mtd/partitions.h>
46 #include <linux/vmalloc.h>
47 #include <linux/slab.h>
48 #include <linux/crc32.h>
49 #include <linux/magic.h>
50
51 #define PFX     "adm8668-part: "
52
53 /* first a little bit about the headers i need.. */
54
55 /* just interested in part of the full struct */
56 struct squashfs_super_block {
57         __le32  s_magic;
58         __le32  pad0[9];        /* it's not really padding */
59         __le64  bytes_used;
60 };
61
62 #define IH_MAGIC        0x56190527      /* Image Magic Number           */
63 struct uboot_header {
64         uint32_t        ih_magic;       /* Image Header Magic Number    */
65         uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
66         uint32_t        ih_time;        /* Image Creation Timestamp     */
67         uint32_t        ih_size;        /* Image Data Size              */
68         uint32_t        ih_load;        /* Data  Load  Address          */
69         uint32_t        ih_ep;          /* Entry Point Address          */
70         uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
71         uint8_t         ih_os;          /* Operating System             */
72         uint8_t         ih_arch;        /* CPU architecture             */
73         uint8_t         ih_type;        /* Image Type                   */
74         uint8_t         ih_comp;        /* Compression Type             */
75         char            ih_name[32];    /* image name */
76 };
77
78 /* in case i wanna change stuff later, and to clarify the math section... */
79 #define PART_LINUX      0
80 #define PART_ROOTFS     1
81 #define PART_UBOOT_ENV  2
82 #define NR_PARTS        3
83
84 static int adm8668_parse_partitions(struct mtd_info *master,
85                                     struct mtd_partition **pparts,
86                                     struct mtd_part_parser_data *data)
87 {
88         int ret;
89         struct uboot_header uhdr;
90         int off, blocksize;
91         size_t len, linux_len;
92         struct squashfs_super_block shdr;
93         struct erase_info erase_info;
94         struct mtd_partition *adm8668_parts;
95
96         memset(&erase_info, 0, sizeof(erase_info));
97
98         blocksize = master->erasesize;
99
100         if (blocksize < 0x10000)
101                 blocksize = 0x10000;
102
103         adm8668_parts = kzalloc(sizeof(*adm8668_parts) * NR_PARTS, GFP_KERNEL);
104         if (!adm8668_parts)
105                 return -ENOMEM;
106
107         adm8668_parts[PART_LINUX].name = kstrdup("linux", GFP_KERNEL);
108         adm8668_parts[PART_LINUX].offset = 0x40000;
109         adm8668_parts[PART_LINUX].size = master->size - 0x40000;
110         adm8668_parts[PART_ROOTFS].name = kstrdup("rootfs", GFP_KERNEL);
111         adm8668_parts[PART_ROOTFS].offset = 0xe0000;
112         adm8668_parts[PART_ROOTFS].size = 0x140000;
113         adm8668_parts[PART_UBOOT_ENV].name = kstrdup("uboot_env", GFP_KERNEL);
114         adm8668_parts[PART_UBOOT_ENV].offset = 0x20000;
115         adm8668_parts[PART_UBOOT_ENV].size = 0x20000;
116
117         /* now find squashfs */
118         memset(&shdr, 0xe5, sizeof(shdr));
119
120         for (off = 0x40000; off < master->size; off += blocksize) {
121                 /*
122                  * Read into buffer
123                  */
124                 if (mtd_read(master, off, sizeof(shdr), &len, (char *)&shdr) ||
125                     len != sizeof(shdr))
126                         continue;
127
128                 if (shdr.s_magic == SQUASHFS_MAGIC) {
129                         uint32_t fs_size = (uint32_t)shdr.bytes_used;
130
131                         pr_info(PFX "filesystem type: squashfs, size=%dkB\n",
132                                 fs_size >> 10);
133
134                         /*
135                          * Update rootfs based on the superblock info, and
136                          * stretch to end of MTD. rootfs_split will split it
137                          */
138                         adm8668_parts[PART_ROOTFS].offset = off;
139                         adm8668_parts[PART_ROOTFS].size = master->size -
140                                 adm8668_parts[PART_ROOTFS].offset;
141
142                         /*
143                          * kernel ends where rootfs starts
144                          * but we'll keep it full-length for upgrades
145                          */
146                         linux_len = adm8668_parts[PART_LINUX + 1].offset -
147                                 adm8668_parts[PART_LINUX].offset;
148
149                         adm8668_parts[PART_LINUX].size = master->size -
150                                 adm8668_parts[PART_LINUX].offset;
151                         goto found;
152                 }
153         }
154
155         pr_err(PFX "could't find root filesystem\n");
156         return NR_PARTS;
157
158 found:
159         if (mtd_read(master, adm8668_parts[PART_LINUX].offset, sizeof(uhdr), &len, (char *)&uhdr) ||
160             len != sizeof(uhdr)) {
161                 pr_err(PFX "failed to read u-boot header\n");
162                 return NR_PARTS;
163         }
164
165         if (uhdr.ih_magic != IH_MAGIC) {
166                 pr_info(PFX "invalid u-boot magic detected?!?!\n");
167                 return NR_PARTS;
168         }
169
170         if (be32_to_cpu(uhdr.ih_size) != (linux_len - sizeof(uhdr))) {
171                 u32 data;
172                 size_t data_len = 0;
173                 unsigned char *block;
174                 unsigned int offset;
175
176                 offset = adm8668_parts[PART_LINUX].offset +
177                          sizeof(struct uboot_header);
178
179                 pr_info(PFX "Updating U-boot image:\n");
180                 pr_info(PFX "  old: [size: %8d crc32: 0x%08x]\n",
181                         be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc));
182
183                 if (mtd_read(master, offset, sizeof(data), &data_len, (char *)&data)) {
184                         pr_err(PFX "failed to read data\n");
185                         goto out;
186                 }
187
188                 /* Update the data length & crc32 */
189                 uhdr.ih_size = cpu_to_be32(linux_len - sizeof(uhdr));
190                 uhdr.ih_dcrc = crc32_le(~0, (char *)&data, linux_len - sizeof(uhdr)) ^ (~0);
191                 uhdr.ih_dcrc = cpu_to_be32(uhdr.ih_dcrc);
192
193                 pr_info(PFX "  new: [size: %8d crc32: 0x%08x]\n",
194                         be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc));
195
196                 /* update header's crc... */
197                 uhdr.ih_hcrc = 0;
198                 uhdr.ih_hcrc = crc32_le(~0, (unsigned char *)&uhdr,
199                                 sizeof(uhdr)) ^ (~0);
200                 uhdr.ih_hcrc = cpu_to_be32(uhdr.ih_hcrc);
201
202                 /* read first eraseblock from the image */
203                 block = vmalloc(master->erasesize);
204                 if (!block)
205                         return -ENOMEM;
206
207                 if (mtd_read(master, adm8668_parts[PART_LINUX].offset, master->erasesize, &len, block)
208                                 || len != master->erasesize) {
209                         pr_err(PFX "error copying first eraseblock\n");
210                         return 0;
211                 }
212
213                 /* Write updated header to the flash */
214                 memcpy(block, &uhdr, sizeof(uhdr));
215                 if (master->unlock)
216                         master->unlock(master, off, master->erasesize);
217
218                 erase_info.mtd = master;
219                 erase_info.addr = (uint64_t)adm8668_parts[PART_LINUX].offset;
220                 erase_info.len = master->erasesize;
221                 ret = mtd_erase(master, &erase_info);
222                 if (!ret) {
223                         if (mtd_write(master, adm8668_parts[PART_LINUX].offset, master->erasesize,
224                                         &len, block))
225                                 pr_err(PFX "write failed");
226                 } else
227                         pr_err(PFX "erase failed");
228
229                 mtd_sync(master);
230 out:
231                 if (block)
232                         vfree(block);
233                 pr_info(PFX "done\n");
234         }
235
236         *pparts = adm8668_parts;
237
238         return NR_PARTS;
239 }
240
241 static struct mtd_part_parser adm8668_parser = {
242         .owner  = THIS_MODULE,
243         .parse_fn = adm8668_parse_partitions,
244         .name = "adm8668part",
245 };
246
247 static int __init adm8668_parser_init(void)
248 {
249         register_mtd_parser(&adm8668_parser);
250
251         return 0;
252 }
253
254 static void __exit adm8668_parser_exit(void)
255 {
256         deregister_mtd_parser(&adm8668_parser);
257 }
258
259 module_init(adm8668_parser_init);
260 module_exit(adm8668_parser_exit);
261
262 MODULE_LICENSE("GPL");
263 MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>");
264 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
265 MODULE_DESCRIPTION("MTD partition parser for ADM8668");