s5pc110: added ext2 filesystem for onenand device.
[kernel/u-boot.git] / common / cmd_onenand_ext2.c
1 /*
2  * ext2 filesystem for onenand device.
3  *
4  * Copyright(C) 2009 Samsung Electronics
5  * InKi Dae <inki.dae@samsung.com>
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 as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307 USA
21  */
22
23 #include <config.h>
24 #include <common.h>
25 #include <version.h>
26 #include <stdarg.h>
27 #include <malloc.h>
28 #include <lcd.h>
29 #include <linux/types.h>
30 #include <asm/io.h>
31 #include <asm/global_data.h>
32
33 #include <onenand_ext2.h>
34
35 #define IMAGE_BASE              (0x380000)
36 #define IMAGE_SIZE              (8 * 1024 * 1024)
37 #define ONENAND_READ_SIZE       (4 * 1024)
38 #define FRAMEBUFFER_SIZE        (480 * 800 * 4)
39
40 #define EXT2_BLOCK_UNIT         (1024)
41 #define DATA_BLOCK_UNIT         (512)
42 #define INODE_TABLE_ENTRY_SIZE  (128)
43
44 extern vidinfo_t panel_info;
45 extern int onenand_read(ulong off, char *buf, unsigned int *out_size);
46
47 static char *ext2_buf = NULL;
48
49 /* it indicates block size of ext2 filesystem formatted. */
50 unsigned int block_size_of_fs = 0;
51
52 unsigned int inode_block_size = 0;
53
54 struct ext2_dirent g_dirent;
55
56 /* set system memory region stored with onenand region. */
57 static unsigned int allocate_ext2_buf(void)
58 {
59         int addr = 0;
60         unsigned int fb_size = 0;
61
62         addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE -1);
63         fb_size = panel_info.vl_col * panel_info.vl_row *
64                 panel_info.vl_bpix / 8;
65         addr -= IMAGE_SIZE + fb_size;
66
67         return addr;
68 }
69
70 /* load onenand region into system memory. */
71 static void onenand_read_to_ram(void)
72 {
73         unsigned int i, out_size;
74         char *buf = NULL;
75
76         ext2_buf = (char *) allocate_ext2_buf();
77         buf = malloc(ONENAND_READ_SIZE);
78
79         for (i = 0; i < IMAGE_SIZE ; i+=ONENAND_READ_SIZE) {
80                 onenand_read(IMAGE_BASE + i, buf, &out_size);
81                 memcpy(ext2_buf + i, buf, ONENAND_READ_SIZE);
82         }
83
84         if (buf)
85                 free(buf);
86 }
87
88 static char *get_sblock(struct ext2_sblock *sblock)
89 {
90         char *buf_sblock = NULL;
91
92         if (ext2_buf == NULL) {
93                 printf("ext2_buf is NULL.\n");
94                 return NULL;
95         }
96
97         buf_sblock = ext2_buf + EXT2_BLOCK_UNIT;
98
99         /* get super block. */
100         memcpy(sblock, buf_sblock, sizeof(struct ext2_sblock));
101
102         printf("total_inodes = %d, block_size = %d\n", sblock->total_inodes,
103                 sblock->log2_block_size);
104
105         return buf_sblock;
106 }
107
108 static char *get_group_dec(struct ext2_block_group *group)
109 {
110         char *buf_group = NULL;
111
112         if (ext2_buf == NULL) {
113                 printf("ext2_buf is NULL.\n");
114                 return NULL;
115         }
116
117         /* get group descriptor. */
118         buf_group = ext2_buf + EXT2_BLOCK_UNIT * 2;
119         memcpy(group, buf_group, sizeof(struct ext2_block_group));
120
121         printf("block_id = %d, inode_id = %d, inode_table_id = %d\n",
122                 group->block_id, group->inode_id, group->inode_table_id);
123
124         return buf_group;
125 }
126
127 static char *get_root_inode_entry(struct ext2_inode *inode,
128         struct ext2_block_group *group)
129 {
130         char *buf_root_dir = NULL;
131         unsigned int inode_table_location;
132
133         if (ext2_buf == NULL) {
134                 printf("ext2_buf is NULL.\n");
135                 return NULL;
136         }
137
138         if (group == NULL) {
139                 printf("group is NULL.\n");
140                 return NULL;
141         }
142
143         /* get location of inode table. */
144         inode_table_location = group->inode_table_id * EXT2_BLOCK_UNIT;
145
146         printf("inode table location = %d\n", inode_table_location);
147
148         /* 
149          * get inode entry of root directory.
150          *
151          * inode table has 128byte unit per entry and
152          * root inode is placed in second entry.
153          */
154         buf_root_dir = ext2_buf + inode_table_location + INODE_TABLE_ENTRY_SIZE;
155         memcpy(inode, buf_root_dir, sizeof(struct ext2_inode));
156
157         /* get inode block size and used to find the end of directory entry. */
158         inode_block_size = inode->blockcnt * DATA_BLOCK_UNIT;
159
160         printf("dir_blocks = %x, size = %d, blockcnt = %d\n", inode->b.blocks.dir_blocks[0],
161                 inode->size, inode->blockcnt);
162
163         return buf_root_dir;
164 }
165
166 static char *get_root_dir_entry(struct ext2_dirent *dirent, struct ext2_inode *inode)
167 {
168         char *buf_root_dir = NULL;
169
170         if (ext2_buf == NULL) {
171                 printf("ext2_buf is NULL.\n");
172                 return NULL;
173         }
174
175         if (inode == NULL) {
176                 printf("inode is NULL.\n");
177                 return NULL;
178         }
179
180         /* get block number stored with data of inode. */
181         buf_root_dir = ext2_buf + inode->b.blocks.dir_blocks[0] * EXT2_BLOCK_UNIT;
182         memcpy(dirent, buf_root_dir, sizeof(struct ext2_dirent));
183
184         printf("first entry name of root directory is name = %s\n",
185                 dirent->name);
186
187         return buf_root_dir;
188 }
189
190 /* initialize ext2 filesystem and return pointer of root directory entry. */
191 static char *mount_ext2fs(void)
192 {
193         struct ext2_sblock sblock;
194         struct ext2_block_group group;
195         struct ext2_inode inode;
196         struct ext2_dirent dirent;
197
198         unsigned int need_block = 0;
199         char *buf = NULL;
200
201         buf = get_sblock(&sblock);
202         if (buf == NULL) {
203                 printf("sblock is NULL.\n");
204                 return NULL;
205         }
206
207         /* in case that log2_block_size is 0, block_size is 1024 and 2048 for 1. */
208         block_size_of_fs = (sblock.log2_block_size + 1) * EXT2_BLOCK_UNIT;
209         printf("block size of filesystem = %d\n", block_size_of_fs);
210
211         /* get block size for inode table. */
212         need_block = sblock.total_inodes * INODE_TABLE_ENTRY_SIZE /
213                 EXT2_BLOCK_UNIT;
214
215         printf("need_block for inode table = %d\n", need_block);
216
217         buf = get_group_dec(&group);
218         if (buf == NULL) {
219                 printf("group_dec is NULL.\n");
220                 return NULL;
221         }
222
223         buf = get_root_inode_entry(&inode, &group);
224         if (buf == NULL) {
225                 printf("root_inode_entry is NULL.\n");
226                 return NULL;
227         }
228
229         buf = get_root_dir_entry(&dirent, &inode);
230         if (buf == NULL) {
231                 printf("root_dir_entry is NULL.\n");
232                 return NULL;
233         }
234
235         return buf;
236
237         /* get data block. */
238         //block_addr = inode_table_location + EXT2_BLOCK_UNIT * need_block;
239         //printf("data block location = %d\n", block_addr);
240         //buf_data_block = ext2_buf + block_addr;
241 }
242
243 static int get_dir_entry(unsigned int *inode, struct ext2_dirent *dirent)
244 {
245         static unsigned int tmp_pt = NULL;
246         static unsigned first = 1;
247
248         if (first) {
249                 tmp_pt = *inode;
250                 first = 0;
251         }
252
253         if ((*inode - tmp_pt) >= inode_block_size)
254                 return -1;
255
256         memcpy(dirent, (char *) *inode, sizeof(struct ext2_dirent));
257         *inode += dirent->direntlen;
258
259         return 0;
260 }
261
262 static void ls_ext2(unsigned int inode)
263 {
264         struct ext2_dirent dirent;
265         int ret;
266
267         ret = get_dir_entry(&inode, &dirent);
268         if (ret < 0) {
269                 printf("failed to get directory entry.\n");
270                 return;
271         }
272
273         printf("inode = %d, name = %s\n", dirent.inode, dirent.name);
274
275         do {
276                 ret = get_dir_entry(&inode, &dirent);
277                 printf("inode = %d, name = %s\n", dirent.inode, dirent.name);
278         } while (ret == 0);
279 }
280
281 void test_onenand_ext2(void)
282 {
283         unsigned int inode;
284
285         onenand_read_to_ram();
286
287         inode = (unsigned int) mount_ext2fs();
288
289         ls_ext2(inode);
290 }