Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / drivers / mtd / bcm47xxpart.c
1 /*
2  * BCM47XX MTD partitioning
3  *
4  * Copyright © 2012 Rafał Miłecki <zajec5@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/slab.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/mtd/partitions.h>
17
18 /*
19  * NAND flash on Netgear R6250 was verified to contain 15 partitions.
20  * This will result in allocating too big array for some old devices, but the
21  * memory will be freed soon anyway (see mtd_device_parse_register).
22  */
23 #define BCM47XXPART_MAX_PARTS           20
24
25 /*
26  * Amount of bytes we read when analyzing each block of flash memory.
27  * Set it big enough to allow detecting partition and reading important data.
28  */
29 #define BCM47XXPART_BYTES_TO_READ       0x4e8
30
31 /* Magics */
32 #define BOARD_DATA_MAGIC                0x5246504D      /* MPFR */
33 #define BOARD_DATA_MAGIC2               0xBD0D0BBD
34 #define CFE_MAGIC                       0x43464531      /* 1EFC */
35 #define FACTORY_MAGIC                   0x59544346      /* FCTY */
36 #define NVRAM_HEADER                    0x48534C46      /* FLSH */
37 #define POT_MAGIC1                      0x54544f50      /* POTT */
38 #define POT_MAGIC2                      0x504f          /* OP */
39 #define ML_MAGIC1                       0x39685a42
40 #define ML_MAGIC2                       0x26594131
41 #define TRX_MAGIC                       0x30524448
42 #define SQSH_MAGIC                      0x71736873      /* shsq */
43
44 struct trx_header {
45         uint32_t magic;
46         uint32_t length;
47         uint32_t crc32;
48         uint16_t flags;
49         uint16_t version;
50         uint32_t offset[3];
51 } __packed;
52
53 static void bcm47xxpart_add_part(struct mtd_partition *part, char *name,
54                                  u64 offset, uint32_t mask_flags)
55 {
56         part->name = name;
57         part->offset = offset;
58         part->mask_flags = mask_flags;
59 }
60
61 static int bcm47xxpart_parse(struct mtd_info *master,
62                              struct mtd_partition **pparts,
63                              struct mtd_part_parser_data *data)
64 {
65         struct mtd_partition *parts;
66         uint8_t i, curr_part = 0;
67         uint32_t *buf;
68         size_t bytes_read;
69         uint32_t offset;
70         uint32_t blocksize = master->erasesize;
71         struct trx_header *trx;
72         int trx_part = -1;
73         int last_trx_part = -1;
74         int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
75
76         if (blocksize <= 0x10000)
77                 blocksize = 0x10000;
78
79         /* Alloc */
80         parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
81                         GFP_KERNEL);
82         if (!parts)
83                 return -ENOMEM;
84
85         buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL);
86         if (!buf) {
87                 kfree(parts);
88                 return -ENOMEM;
89         }
90
91         /* Parse block by block looking for magics */
92         for (offset = 0; offset <= master->size - blocksize;
93              offset += blocksize) {
94                 /* Nothing more in higher memory */
95                 if (offset >= 0x2000000)
96                         break;
97
98                 if (curr_part >= BCM47XXPART_MAX_PARTS) {
99                         pr_warn("Reached maximum number of partitions, scanning stopped!\n");
100                         break;
101                 }
102
103                 /* Read beginning of the block */
104                 if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
105                              &bytes_read, (uint8_t *)buf) < 0) {
106                         pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
107                                offset);
108                         continue;
109                 }
110
111                 /* Magic or small NVRAM at 0x400 */
112                 if ((buf[0x4e0 / 4] == CFE_MAGIC && buf[0x4e4 / 4] == CFE_MAGIC) ||
113                     (buf[0x400 / 4] == NVRAM_HEADER)) {
114                         bcm47xxpart_add_part(&parts[curr_part++], "boot",
115                                              offset, MTD_WRITEABLE);
116                         continue;
117                 }
118
119                 /*
120                  * board_data starts with board_id which differs across boards,
121                  * but we can use 'MPFR' (hopefully) magic at 0x100
122                  */
123                 if (buf[0x100 / 4] == BOARD_DATA_MAGIC) {
124                         bcm47xxpart_add_part(&parts[curr_part++], "board_data",
125                                              offset, MTD_WRITEABLE);
126                         continue;
127                 }
128
129                 /* Found on Huawei E970 */
130                 if (buf[0x000 / 4] == FACTORY_MAGIC) {
131                         bcm47xxpart_add_part(&parts[curr_part++], "factory",
132                                              offset, MTD_WRITEABLE);
133                         continue;
134                 }
135
136                 /* POT(TOP) */
137                 if (buf[0x000 / 4] == POT_MAGIC1 &&
138                     (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) {
139                         bcm47xxpart_add_part(&parts[curr_part++], "POT", offset,
140                                              MTD_WRITEABLE);
141                         continue;
142                 }
143
144                 /* ML */
145                 if (buf[0x010 / 4] == ML_MAGIC1 &&
146                     buf[0x014 / 4] == ML_MAGIC2) {
147                         bcm47xxpart_add_part(&parts[curr_part++], "ML", offset,
148                                              MTD_WRITEABLE);
149                         continue;
150                 }
151
152                 /* TRX */
153                 if (buf[0x000 / 4] == TRX_MAGIC) {
154                         if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
155                                 pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
156                                 break;
157                         }
158
159                         trx = (struct trx_header *)buf;
160
161                         trx_part = curr_part;
162                         bcm47xxpart_add_part(&parts[curr_part++], "firmware",
163                                              offset, 0);
164
165                         i = 0;
166                         /* We have LZMA loader if offset[2] points to sth */
167                         if (trx->offset[2]) {
168                                 bcm47xxpart_add_part(&parts[curr_part++],
169                                                      "loader",
170                                                      offset + trx->offset[i],
171                                                      0);
172                                 i++;
173                         }
174
175                         if (trx->offset[i]) {
176                                 bcm47xxpart_add_part(&parts[curr_part++],
177                                                      "linux",
178                                                      offset + trx->offset[i],
179                                                      0);
180                                 i++;
181                         }
182
183                         /*
184                          * Pure rootfs size is known and can be calculated as:
185                          * trx->length - trx->offset[i]. We don't fill it as
186                          * we want to have jffs2 (overlay) in the same mtd.
187                          */
188                         if (trx->offset[i]) {
189                                 bcm47xxpart_add_part(&parts[curr_part++],
190                                                      "rootfs",
191                                                      offset + trx->offset[i],
192                                                      0);
193                                 i++;
194                         }
195
196                         last_trx_part = curr_part - 1;
197
198                         /*
199                          * We have whole TRX scanned, skip to the next part. Use
200                          * roundown (not roundup), as the loop will increase
201                          * offset in next step.
202                          */
203                         offset = rounddown(offset + trx->length, blocksize);
204                         continue;
205                 }
206
207                 /* Squashfs on devices not using TRX */
208                 if (buf[0x000 / 4] == SQSH_MAGIC) {
209                         bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
210                                              offset, 0);
211                         continue;
212                 }
213
214                 /*
215                  * New (ARM?) devices may have NVRAM in some middle block. Last
216                  * block will be checked later, so skip it.
217                  */
218                 if (offset != master->size - blocksize &&
219                     buf[0x000 / 4] == NVRAM_HEADER) {
220                         bcm47xxpart_add_part(&parts[curr_part++], "nvram",
221                                              offset, 0);
222                         continue;
223                 }
224
225                 /* Read middle of the block */
226                 if (mtd_read(master, offset + 0x8000, 0x4,
227                              &bytes_read, (uint8_t *)buf) < 0) {
228                         pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
229                                offset);
230                         continue;
231                 }
232
233                 /* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */
234                 if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) {
235                         bcm47xxpart_add_part(&parts[curr_part++], "board_data",
236                                              offset, MTD_WRITEABLE);
237                         continue;
238                 }
239         }
240
241         /* Look for NVRAM at the end of the last block. */
242         for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
243                 if (curr_part >= BCM47XXPART_MAX_PARTS) {
244                         pr_warn("Reached maximum number of partitions, scanning stopped!\n");
245                         break;
246                 }
247
248                 offset = master->size - possible_nvram_sizes[i];
249                 if (mtd_read(master, offset, 0x4, &bytes_read,
250                              (uint8_t *)buf) < 0) {
251                         pr_err("mtd_read error while reading at offset 0x%X!\n",
252                                offset);
253                         continue;
254                 }
255
256                 /* Standard NVRAM */
257                 if (buf[0] == NVRAM_HEADER) {
258                         bcm47xxpart_add_part(&parts[curr_part++], "nvram",
259                                              master->size - blocksize, 0);
260                         break;
261                 }
262         }
263
264         kfree(buf);
265
266         /*
267          * Assume that partitions end at the beginning of the one they are
268          * followed by.
269          */
270         for (i = 0; i < curr_part; i++) {
271                 u64 next_part_offset = (i < curr_part - 1) ?
272                                        parts[i + 1].offset : master->size;
273
274                 parts[i].size = next_part_offset - parts[i].offset;
275                 if (i == last_trx_part && trx_part >= 0)
276                         parts[trx_part].size = next_part_offset -
277                                                parts[trx_part].offset;
278         }
279
280         *pparts = parts;
281         return curr_part;
282 };
283
284 static struct mtd_part_parser bcm47xxpart_mtd_parser = {
285         .owner = THIS_MODULE,
286         .parse_fn = bcm47xxpart_parse,
287         .name = "bcm47xxpart",
288 };
289
290 static int __init bcm47xxpart_init(void)
291 {
292         register_mtd_parser(&bcm47xxpart_mtd_parser);
293         return 0;
294 }
295
296 static void __exit bcm47xxpart_exit(void)
297 {
298         deregister_mtd_parser(&bcm47xxpart_mtd_parser);
299 }
300
301 module_init(bcm47xxpart_init);
302 module_exit(bcm47xxpart_exit);
303
304 MODULE_LICENSE("GPL");
305 MODULE_DESCRIPTION("MTD partitioning for BCM47XX flash memories");