1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/module.h>
24#include <linux/types.h>
25#include <linux/kernel.h>
26#include <linux/init.h>
27#include <linux/slab.h>
28
29#include <linux/mtd/mtd.h>
30#include <linux/mtd/map.h>
31#include <linux/mtd/partitions.h>
32
33#include <asm/io.h>
34
35#define FLASH_ADDR 0x40000000
36#define FLASH_SIZE 0x00800000
37#define FLASH_BANK_MAX 4
38
39
40struct mtd_part_def
41{
42 int nums;
43 unsigned char *type;
44 struct mtd_partition* mtd_part;
45};
46
47
48static struct mtd_info* mtd_banks[FLASH_BANK_MAX];
49static struct map_info* map_banks[FLASH_BANK_MAX];
50static struct mtd_part_def part_banks[FLASH_BANK_MAX];
51static unsigned long num_banks;
52static void __iomem *start_scan_addr;
53
54
55
56
57
58
59
60
61
62
63
64
65#ifdef CONFIG_MTD_PARTITIONS
66
67static unsigned long tqm8xxl_max_flash_size = 0x00800000;
68
69
70
71
72static struct mtd_partition tqm8xxl_partitions[] = {
73 {
74 .name = "ppcboot",
75 .offset = 0x00000000,
76 .size = 0x00020000,
77 .mask_flags = MTD_WRITEABLE,
78 },
79 {
80 .name = "kernel",
81 .offset = 0x00020000,
82 .size = 0x000e0000,
83 .mask_flags = MTD_WRITEABLE,
84 },
85 {
86 .name = "user",
87 .offset = 0x00100000,
88 .size = 0x00100000,
89 },
90 {
91 .name = "initrd",
92 .offset = 0x00200000,
93 .size = 0x00200000,
94 }
95};
96
97static struct mtd_partition tqm8xxl_fs_partitions[] = {
98 {
99 .name = "cramfs",
100 .offset = 0x00000000,
101 .size = 0x00200000,
102 },
103 {
104 .name = "jffs",
105 .offset = 0x00200000,
106 .size = 0x00200000,
107
108 }
109};
110#endif
111
112static int __init init_tqm_mtd(void)
113{
114 int idx = 0, ret = 0;
115 unsigned long flash_addr, flash_size, mtd_size = 0;
116
117 bd_t *bd = (bd_t *)__res;
118
119 flash_addr = bd->bi_flashstart;
120 flash_size = bd->bi_flashsize;
121
122
123 start_scan_addr = ioremap(flash_addr, flash_size);
124 if (!start_scan_addr) {
125 printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __func__, flash_addr);
126 return -EIO;
127 }
128
129 for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
130 if(mtd_size >= flash_size)
131 break;
132
133 printk(KERN_INFO "%s: chip probing count %d\n", __func__, idx);
134
135 map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
136 if(map_banks[idx] == NULL) {
137 ret = -ENOMEM;
138
139 goto error_mem;
140 }
141
142 map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
143
144 if (!map_banks[idx]->name) {
145 ret = -ENOMEM;
146
147 goto error_mem;
148 }
149 sprintf(map_banks[idx]->name, "TQM8xxL%d", idx);
150
151 map_banks[idx]->size = flash_size;
152 map_banks[idx]->bankwidth = 4;
153
154 simple_map_init(map_banks[idx]);
155
156 map_banks[idx]->virt = start_scan_addr;
157 map_banks[idx]->phys = flash_addr;
158
159
160
161
162
163
164
165
166 if (idx && mtd_banks[idx-1]) {
167 map_banks[idx]->virt += mtd_banks[idx-1]->size;
168 map_banks[idx]->phys += mtd_banks[idx-1]->size;
169 }
170
171
172 mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
173
174 if (mtd_banks[idx]) {
175 mtd_banks[idx]->owner = THIS_MODULE;
176 mtd_size += mtd_banks[idx]->size;
177 num_banks++;
178
179 printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __func__, num_banks,
180 mtd_banks[idx]->name, mtd_banks[idx]->size);
181 }
182 }
183
184
185 if (!num_banks) {
186 printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n");
187 ret = -ENXIO;
188 goto error_mem;
189 }
190
191#ifdef CONFIG_MTD_PARTITIONS
192
193
194
195 part_banks[0].mtd_part = tqm8xxl_partitions;
196 part_banks[0].type = "Static image";
197 part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions);
198
199 part_banks[1].mtd_part = tqm8xxl_fs_partitions;
200 part_banks[1].type = "Static file system";
201 part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions);
202
203 for(idx = 0; idx < num_banks ; idx++) {
204 if (part_banks[idx].nums == 0) {
205 printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx);
206 add_mtd_device(mtd_banks[idx]);
207 } else {
208 printk(KERN_NOTICE "TQM flash%d: Using %s partition definition\n",
209 idx, part_banks[idx].type);
210 add_mtd_partitions(mtd_banks[idx], part_banks[idx].mtd_part,
211 part_banks[idx].nums);
212 }
213 }
214#else
215 printk(KERN_NOTICE "TQM flash: registering %d whole flash banks at once\n", num_banks);
216 for(idx = 0 ; idx < num_banks ; idx++)
217 add_mtd_device(mtd_banks[idx]);
218#endif
219 return 0;
220error_mem:
221 for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
222 if(map_banks[idx] != NULL) {
223 kfree(map_banks[idx]->name);
224 map_banks[idx]->name = NULL;
225 kfree(map_banks[idx]);
226 map_banks[idx] = NULL;
227 }
228 }
229error:
230 iounmap(start_scan_addr);
231 return ret;
232}
233
234static void __exit cleanup_tqm_mtd(void)
235{
236 unsigned int idx = 0;
237 for(idx = 0 ; idx < num_banks ; idx++) {
238
239 if (mtd_banks[idx]) {
240 del_mtd_partitions(mtd_banks[idx]);
241 map_destroy(mtd_banks[idx]);
242 }
243
244 kfree(map_banks[idx]->name);
245 kfree(map_banks[idx]);
246 }
247
248 if (start_scan_addr) {
249 iounmap(start_scan_addr);
250 start_scan_addr = 0;
251 }
252}
253
254module_init(init_tqm_mtd);
255module_exit(cleanup_tqm_mtd);
256
257MODULE_LICENSE("GPL");
258MODULE_AUTHOR("Kirk Lee <kirk@hpc.ee.ntu.edu.tw>");
259MODULE_DESCRIPTION("MTD map driver for TQM8xxL boards");
260