Merge remote-tracking branch 'linux-2.6.32.y/master' into develop
[firefly-linux-kernel-4.4.55.git] / drivers / mtd / rknand / rknand_base_ko.c
1 /*\r
2  *  linux/drivers/mtd/rknand/rknand_base.c\r
3  *\r
4  *  Copyright (C) 2005-2009 Fuzhou Rockchip Electronics\r
5  *  ZYF <zyf@rock-chips.com>\r
6  *\r
7  *   \r
8  */\r
9 \r
10 #include <linux/module.h>\r
11 #include <linux/sched.h>\r
12 #include <linux/delay.h>\r
13 #include <linux/init.h>\r
14 #include <linux/slab.h>\r
15 #include <linux/platform_device.h>\r
16 #include <linux/mtd/mtd.h>\r
17 #include <linux/mtd/partitions.h>\r
18 #include <linux/mutex.h>\r
19 #include <linux/kthread.h>\r
20 #include <linux/reboot.h>\r
21 #include <asm/io.h>\r
22 #include <asm/mach/flash.h>\r
23 //#include "api_flash.h"\r
24 #include "rknand_base.h"\r
25 \r
26 \r
27 #define DRIVER_NAME     "rk29xxnand"\r
28 const char rknand_base_version[] = "rknand_base.c version: 4.26 20110610";\r
29 #define NAND_DEBUG_LEVEL0 0\r
30 #define NAND_DEBUG_LEVEL1 1\r
31 #define NAND_DEBUG_LEVEL2 2\r
32 #define NAND_DEBUG_LEVEL3 3\r
33 \r
34 int g_num_partitions = 0;\r
35 unsigned long SysImageWriteEndAdd = 0;\r
36 struct mtd_info         rknand_mtd;  \r
37 struct mtd_partition *rknand_parts;\r
38 struct rknand_info * gpNandInfo;\r
39 \r
40 #ifdef CONFIG_MTD_NAND_RK29XX_DEBUG\r
41 static int s_debug = CONFIG_MTD_NAND_RK29XX_DEBUG_VERBOSE;\r
42 #undef NAND_DEBUG\r
43 #define NAND_DEBUG(n, format, arg...) \\r
44         if (n <= s_debug) {      \\r
45                 printk(format,##arg); \\r
46         }\r
47 #else\r
48 #undef NAND_DEBUG\r
49 #define NAND_DEBUG(n, arg...)\r
50 static const int s_debug = 0;\r
51 #endif\r
52 \r
53 #include <linux/proc_fs.h>\r
54 #include <linux/version.h>\r
55 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26))\r
56 #define NANDPROC_ROOT  (&proc_root)\r
57 #else\r
58 #define NANDPROC_ROOT  NULL\r
59 #endif\r
60 \r
61 #define MAX_BUFFER_SIZE     (long)(2048 * 8) //sector\r
62 long grknand_buf[MAX_BUFFER_SIZE * 512/4] __attribute__((aligned(4096)));\r
63 \r
64 static struct proc_dir_entry *my_proc_entry;\r
65 extern int rkNand_proc_ftlread(char *page);\r
66 extern int rkNand_proc_bufread(char *page);\r
67 static int rkNand_proc_read(char *page,\r
68                            char **start,\r
69                            off_t offset, int count, int *eof, void *data)\r
70 {\r
71         char *buf = page;\r
72         int step = offset;\r
73         *(int *)start = 1;\r
74         if(step == 0)\r
75         {\r
76         buf += sprintf(buf, "%s\n", rknand_base_version);\r
77         if(gpNandInfo->proc_ftlread)\r
78             buf += gpNandInfo->proc_ftlread(buf);\r
79         if(gpNandInfo->proc_bufread)\r
80             buf += gpNandInfo->proc_bufread(buf);\r
81     }\r
82         return buf - page < count ? buf - page : count;\r
83 }\r
84 \r
85 static void rk28nand_create_procfs(void)\r
86 {\r
87     /* Install the proc_fs entry */\r
88     my_proc_entry = create_proc_entry("rk29xxnand",\r
89                            S_IRUGO | S_IFREG,\r
90                            NANDPROC_ROOT);\r
91 \r
92     if (my_proc_entry) {\r
93         my_proc_entry->write_proc = NULL;\r
94         my_proc_entry->read_proc = rkNand_proc_read;\r
95         my_proc_entry->data = NULL;\r
96     } \r
97 }\r
98 \r
99 void printk_write_log(long lba,int len, const u_char *pbuf)\r
100 {\r
101     char debug_buf[100];\r
102     int i;\r
103     for(i=0;i<len;i++)\r
104     {\r
105         sprintf(debug_buf,"%lx :",lba+i);\r
106         print_hex_dump(KERN_WARNING, debug_buf, DUMP_PREFIX_NONE, 16,4, &pbuf[512*i], 8, 0);\r
107     }\r
108 }\r
109 \r
110 static int rk28xxnand_read(struct mtd_info *mtd, loff_t from, size_t len,\r
111         size_t *retlen, u_char *buf)\r
112 {\r
113         int ret = 0;\r
114         int sector = len>>9;\r
115         int LBA = (int)(from>>9);\r
116     //printk("rk28xxnand_read: from=%x,len=%x,\n",(int)from,len);\r
117     if(sector && gpNandInfo->ftl_read)\r
118     {\r
119                 ret = gpNandInfo->ftl_read(LBA, sector, buf);\r
120     }\r
121         *retlen = len;\r
122         return 0;//ret;\r
123 }\r
124 \r
125 static int rk28xxnand_write(struct mtd_info *mtd, loff_t from, size_t len,\r
126         size_t *retlen, const u_char *buf)\r
127 {\r
128         int ret = 0;\r
129         int sector = len>>9;\r
130         int LBA = (int)(from>>9);\r
131         //printk("*");\r
132     //printk(KERN_NOTICE "write: from=%lx,len=%x\n",(int)LBA,sector);\r
133     //printk_write_log(LBA,sector,buf);\r
134         if(sector && gpNandInfo->ftl_write)// cmy\r
135         {\r
136                 if(LBA < SysImageWriteEndAdd)//0x4E000)\r
137                 {\r
138                         NAND_DEBUG(NAND_DEBUG_LEVEL0,">>> FtlWriteImage: LBA=0x%08X  sector=%d\n",LBA, sector);\r
139             ret = gpNandInfo->ftl_write(LBA, sector, (void *)buf,1);\r
140         }\r
141                 else\r
142         {\r
143             ret = gpNandInfo->ftl_write(LBA, sector, (void *)buf,0);\r
144         }\r
145         }\r
146         *retlen = len;\r
147         return 0;\r
148 }\r
149 \r
150 static int rk28xxnand_erase(struct mtd_info *mtd, struct erase_info *instr)\r
151 {\r
152         int ret = 0;\r
153     if (instr->callback)\r
154                 instr->callback(instr);\r
155         return ret;\r
156 }\r
157 \r
158 static void rk28xxnand_sync(struct mtd_info *mtd)\r
159 {\r
160         NAND_DEBUG(NAND_DEBUG_LEVEL0,"rk28xxnand_sync: \n");\r
161     if(gpNandInfo->ftl_sync)\r
162         gpNandInfo->ftl_sync();\r
163 }\r
164 \r
165 extern void FtlWriteCacheEn(int);\r
166 static int rk28xxnand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)\r
167 {\r
168         int sector = len >> 9;\r
169         int LBA = (int)(to >> 9);\r
170 \r
171         if (sector && gpNandInfo->ftl_write_panic) {\r
172             if(gpNandInfo->ftl_cache_en)\r
173                     gpNandInfo->ftl_cache_en(0);\r
174                 gpNandInfo->ftl_write_panic(LBA, sector, (void *)buf);\r
175             if(gpNandInfo->ftl_cache_en)\r
176                     gpNandInfo->ftl_cache_en(1);\r
177         }\r
178         *retlen = len;\r
179         return 0;\r
180 }\r
181 \r
182 \r
183 int GetIdBlockSysData(char * buf, int Sector)\r
184 {\r
185     if(gpNandInfo->GetIdBlockSysData)\r
186            return( gpNandInfo->GetIdBlockSysData( buf,  Sector));\r
187     return 0;\r
188 }\r
189 \r
190 char GetSNSectorInfo(char * pbuf)\r
191 {\r
192     if(gpNandInfo->GetSNSectorInfo)\r
193            return( gpNandInfo->GetSNSectorInfo( pbuf));\r
194     return 0;\r
195 }\r
196 \r
197 char GetChipSectorInfo(char * pbuf)\r
198 {\r
199     if(gpNandInfo->GetChipSectorInfo)\r
200            return( gpNandInfo->GetChipSectorInfo( pbuf));\r
201     return 0;\r
202 }\r
203 \r
204 static int rk28xxnand_block_isbad(struct mtd_info *mtd, loff_t ofs)\r
205 {\r
206         return 0;\r
207 }\r
208 \r
209 static int rk28xxnand_block_markbad(struct mtd_info *mtd, loff_t ofs)\r
210 {\r
211         return 0;\r
212 }\r
213 \r
214 static int rk28xxnand_init(struct rknand_info *nand_info)\r
215 {\r
216         struct mtd_info    *mtd = &rknand_mtd;\r
217         struct rknand_chip *rknand = &nand_info->rknand;  \r
218 \r
219         rknand->state = FL_READY;\r
220         rknand->rknand_schedule_enable = 1;\r
221         rknand->pFlashCallBack = NULL;\r
222         init_waitqueue_head(&rknand->wq);\r
223 \r
224     mtd->oobsize = 0;\r
225     mtd->oobavail = 0;\r
226     mtd->ecclayout = 0;\r
227     mtd->erasesize = 32*0x200;\r
228     mtd->writesize = 8*0x200;\r
229 \r
230         // Fill in remaining MTD driver data \r
231         mtd->type = MTD_NANDFLASH;\r
232         mtd->flags = (MTD_WRITEABLE|MTD_NO_ERASE);//\r
233         mtd->erase = rk28xxnand_erase;\r
234         mtd->point = NULL;\r
235         mtd->unpoint = NULL;\r
236         mtd->read = rk28xxnand_read;\r
237         mtd->write = rk28xxnand_write;\r
238         mtd->read_oob = NULL;\r
239         mtd->write_oob = NULL;\r
240         mtd->panic_write = rk28xxnand_panic_write;\r
241 \r
242         mtd->sync = rk28xxnand_sync;\r
243         mtd->lock = NULL;\r
244         mtd->unlock = NULL;\r
245         mtd->suspend = NULL;\r
246         mtd->resume = NULL;\r
247         mtd->block_isbad = rk28xxnand_block_isbad;\r
248         mtd->block_markbad = rk28xxnand_block_markbad;\r
249         mtd->owner = THIS_MODULE;\r
250     return 0;\r
251 }\r
252 \r
253 \r
254 /*\r
255  * CMY: Ôö¼ÓÁ˶ÔÃüÁîÐзÖÇøÐÅÏ¢µÄÖ§³Ö\r
256  *              ÈôcmdlineÓÐÌṩ·ÖÇøÐÅÏ¢£¬ÔòʹÓÃcmdlineµÄ·ÖÇøÐÅÏ¢½øÐзÖÇø\r
257  *              ÈôcmdlineûÓÐÌṩ·ÖÇøÐÅÏ¢£¬ÔòʹÓÃĬÈϵķÖÇøÐÅÏ¢(rk28_partition_info)½øÐзÖÇø\r
258  */\r
259 \r
260 #ifdef CONFIG_MTD_CMDLINE_PARTS\r
261 const char *part_probes[] = { "cmdlinepart", NULL }; \r
262 #endif \r
263 \r
264 static int rk29xxnand_add_partitions(struct rknand_info *nand_info)\r
265 {\r
266 #ifdef CONFIG_MTD_CMDLINE_PARTS\r
267     int num_partitions = 0; \r
268 \r
269         // ´ÓÃüÁîÐнâÎö·ÖÇøµÄÐÅÏ¢\r
270     num_partitions = parse_mtd_partitions(&(rknand_mtd), part_probes, &rknand_parts, 0); \r
271     NAND_DEBUG(NAND_DEBUG_LEVEL0,"num_partitions = %d\n",num_partitions);\r
272     if(num_partitions > 0) { \r
273         int i;\r
274         for (i = 0; i < num_partitions; i++) \r
275         {\r
276             rknand_parts[i].offset *= 0x200;\r
277             rknand_parts[i].size   *=0x200;\r
278         }\r
279         rknand_parts[num_partitions - 1].size = rknand_mtd.size - rknand_parts[num_partitions - 1].offset;\r
280         \r
281                 g_num_partitions = num_partitions;\r
282                 return add_mtd_partitions(&(rknand_mtd), rknand_parts, num_partitions);\r
283     } \r
284 #endif \r
285         return 0;\r
286 }\r
287 \r
288 int add_rknand_device(struct rknand_info * prknand_Info)\r
289 {\r
290     struct mtd_partition *parts;\r
291     int i;\r
292     NAND_DEBUG(NAND_DEBUG_LEVEL0,"add_rknand_device: \n");\r
293     \r
294     rknand_mtd.size = (uint64_t)gpNandInfo->nandCapacity*0x200;\r
295     \r
296     rk29xxnand_add_partitions(prknand_Info);\r
297  \r
298     parts = rknand_parts;\r
299     for(i=0;i<g_num_partitions;i++)\r
300     {\r
301         //printk(">>> part[%d]: name=%s offset=0x%012llx\n", i, parts[i].name, parts[i].offset);\r
302         if(strcmp(parts[i].name,"backup") == 0)\r
303         {\r
304             SysImageWriteEndAdd = (unsigned long)(parts[i].offset + parts[i].size)>>9;//sector\r
305             //printk(">>> SysImageWriteEndAdd=0x%lx\n", SysImageWriteEndAdd);\r
306             break;\r
307         }\r
308     }\r
309 \r
310     gpNandInfo->SysImageWriteEndAdd = SysImageWriteEndAdd;\r
311     return 0;\r
312 }\r
313 \r
314 int get_rknand_device(struct rknand_info ** prknand_Info)\r
315 {\r
316     *prknand_Info = gpNandInfo;\r
317     return 0;    \r
318 }\r
319 \r
320 EXPORT_SYMBOL(get_rknand_device);\r
321 \r
322 static int rknand_probe(struct platform_device *pdev)\r
323 {\r
324         struct rknand_info *nand_info;\r
325         int err = 0;\r
326         NAND_DEBUG(NAND_DEBUG_LEVEL0,"rk28xxnand_probe: \n");\r
327         gpNandInfo = kzalloc(sizeof(struct rknand_info), GFP_KERNEL);\r
328         if (!gpNandInfo)\r
329                 return -ENOMEM;\r
330     \r
331     nand_info = gpNandInfo;\r
332 \r
333     memset(gpNandInfo,0,sizeof(struct rknand_info));\r
334 \r
335     gpNandInfo->bufSize = MAX_BUFFER_SIZE * 512;\r
336     gpNandInfo->pbuf = grknand_buf;\r
337    \r
338         rknand_mtd.name = dev_name(&pdev->dev);\r
339         rknand_mtd.priv = &nand_info->rknand;\r
340         rknand_mtd.owner = THIS_MODULE;\r
341     \r
342         if(rk28xxnand_init(nand_info))\r
343         {\r
344                 err = -ENXIO;\r
345                 goto  exit_free;\r
346         }\r
347         \r
348         nand_info->add_rknand_device = add_rknand_device;\r
349         nand_info->get_rknand_device = get_rknand_device;\r
350 \r
351         rk28nand_create_procfs();\r
352         return 0;\r
353 \r
354 exit_free:\r
355         if(nand_info)\r
356         kfree(nand_info);\r
357 \r
358         return err;\r
359 }\r
360 \r
361 static int rknand_suspend(struct platform_device *pdev, pm_message_t state)\r
362 {\r
363     gpNandInfo->rknand.rknand_schedule_enable = 0;\r
364         NAND_DEBUG(NAND_DEBUG_LEVEL0,"rknand_suspend: \n");\r
365         return 0;\r
366 }\r
367 \r
368 static int rknand_resume(struct platform_device *pdev)\r
369 {\r
370     gpNandInfo->rknand.rknand_schedule_enable = 1;\r
371         NAND_DEBUG(NAND_DEBUG_LEVEL0,"rknand_resume: \n");\r
372         return 0;\r
373 }\r
374 \r
375 void rknand_shutdown(struct platform_device *pdev)\r
376 {\r
377     printk("rknand_shutdown...\n");\r
378     gpNandInfo->rknand.rknand_schedule_enable = 0;\r
379     if(gpNandInfo->rknand_buffer_shutdown)\r
380         gpNandInfo->rknand_buffer_shutdown();    \r
381 }\r
382 \r
383 static struct platform_driver rknand_driver = {\r
384         .probe          = rknand_probe,\r
385         .suspend        = rknand_suspend,\r
386         .resume         = rknand_resume,\r
387         .shutdown   = rknand_shutdown,\r
388         .driver         = {\r
389                 .name   = DRIVER_NAME,\r
390                 .owner  = THIS_MODULE,\r
391         },\r
392 };\r
393 \r
394 \r
395 MODULE_ALIAS(DRIVER_NAME);\r
396 \r
397 static int __init rknand_init(void)\r
398 {\r
399         int ret;\r
400         NAND_DEBUG(NAND_DEBUG_LEVEL0,"rknand_init: \n");\r
401         ret = platform_driver_register(&rknand_driver);\r
402         NAND_DEBUG(NAND_DEBUG_LEVEL0,"platform_driver_register:ret = %x \n",ret);\r
403         return ret;\r
404 }\r
405 \r
406 static void __exit rknand_exit(void)\r
407 {\r
408     platform_driver_unregister(&rknand_driver);\r
409 }\r
410 \r
411 module_init(rknand_init);\r
412 module_exit(rknand_exit);\r
413 \r
414 MODULE_LICENSE("GPL");\r
415 MODULE_AUTHOR("ZYF <zyf@rock-chips.com>");\r
416 MODULE_DESCRIPTION("rknand driver.");\r
417 \r
418 \r