#if defined(CONFIG_BCM_KF_MTD_BCMNAND) /* * * drivers/mtd/brcmnand/bcm7xxx-nand.c * <:copyright-BRCM:2011:DUAL/GPL:standard Copyright (c) 2011 Broadcom All Rights Reserved This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation (the "GPL"). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> */ #include #include #include #include #include #include #include #include #include #include "brcmnand_priv.h" #include #include #define PRINTK(...) //#define PRINTK printk #define DRIVER_NAME "brcmnand" #define DRIVER_INFO "Broadcom NAND controller" extern int setup_mtd_parts(struct mtd_info* mtd); static int brcmnanddrv_probe(struct platform_device *pdev); static int brcmnanddrv_remove(struct platform_device *pdev); static struct platform_driver brcmnand_platform_driver = { .probe = brcmnanddrv_probe, .remove = brcmnanddrv_remove, .driver = { .name = DRIVER_NAME, }, }; static struct resource brcmnand_resources[] = { [0] = { .name = DRIVER_NAME, .flags = IORESOURCE_MEM, }, }; static struct brcmnand_info { struct mtd_info mtd; struct brcmnand_chip brcmnand; int nr_parts; struct mtd_partition* parts; } *gNandInfo[NUM_NAND_CS]; int gNandCS[NAND_MAX_CS]; /* Number of NAND chips, only applicable to v1.0+ NAND controller */ int gNumNand = 0; int gClearBBT = 0; char gClearCET = 0; uint32_t gNandTiming1[NAND_MAX_CS], gNandTiming2[NAND_MAX_CS]; uint32_t gAccControl[NAND_MAX_CS], gNandConfig[NAND_MAX_CS]; static unsigned long t1[NAND_MAX_CS] = { 0 }; static int nt1 = 0; static unsigned long t2[NAND_MAX_CS] = { 0 }; static int nt2 = 0; static unsigned long acc[NAND_MAX_CS] = { 0 }; static int nacc = 0; static unsigned long nandcfg[NAND_MAX_CS] = { 0 }; static int ncfg = 0; static void* gPageBuffer = NULL; static int brcmnanddrv_probe(struct platform_device *pdev) { static int csi = 0; // Index into dev/nandInfo array int cs = 0; // Chip Select int err = 0; struct brcmnand_info* info = NULL; static struct brcmnand_ctrl* ctrl = (struct brcmnand_ctrl*)0; if (!gPageBuffer && (gPageBuffer = kmalloc(sizeof(struct brcmnand_buffers), GFP_KERNEL)) == NULL) { return -ENOMEM; } if ( (ctrl = kmalloc(sizeof(struct brcmnand_ctrl), GFP_KERNEL)) != NULL) { memset(ctrl, 0, sizeof(struct brcmnand_ctrl)); ctrl->state = FL_READY; init_waitqueue_head(&ctrl->wq); spin_lock_init(&ctrl->chip_lock); if ((info = kmalloc(sizeof(struct brcmnand_info), GFP_KERNEL)) != NULL) { gNandInfo[csi] = info; memset(info, 0, sizeof(struct brcmnand_info)); info->brcmnand.ctrl = ctrl; info->brcmnand.ctrl->numchips = gNumNand = 1; info->brcmnand.csi = csi; /* For now all devices share the same buffer */ info->brcmnand.ctrl->buffers = (struct brcmnand_buffers*)gPageBuffer; info->brcmnand.ctrl->numchips = gNumNand; info->brcmnand.chip_shift = 0; // Only 1 chip info->brcmnand.priv = &info->mtd; info->mtd.name = dev_name(&pdev->dev); info->mtd.priv = &info->brcmnand; info->mtd.owner = THIS_MODULE; /* Enable the following for a flash based bad block table */ info->brcmnand.options |= NAND_BBT_USE_FLASH; /* Each chip now will have its own BBT (per mtd handle) */ if (brcmnand_scan(&info->mtd, cs, gNumNand) == 0) { PRINTK("Master size=%08llx\n", info->mtd.size); setup_mtd_parts(&info->mtd); dev_set_drvdata(&pdev->dev, info); }else err = -ENXIO; }else err = -ENOMEM; }else err = -ENOMEM; if (err) { if (gPageBuffer) { kfree(gPageBuffer); gPageBuffer = NULL; } if (ctrl) { kfree(ctrl); ctrl = NULL; } if (info) { kfree(info); info = NULL; } } return err; } static int brcmnanddrv_remove(struct platform_device *pdev) { struct brcmnand_info *info = dev_get_drvdata(&pdev->dev); dev_set_drvdata(&pdev->dev, NULL); if (info) { mtd_device_unregister(&info->mtd); brcmnand_release(&info->mtd); kfree(gPageBuffer); kfree(info); } return 0; } static int __init brcmnanddrv_init(void) { int ret = 0; int csi; int ncsi; char cmd[32] = "\0"; struct platform_device *pdev; if (flash_get_flash_type() != FLASH_IFC_NAND) return -ENODEV; kerSysBlParmsGetStr(NAND_COMMAND_NAME, cmd, sizeof(cmd)); PRINTK("%s: brcmnanddrv_init - NANDCMD='%s'\n", __FUNCTION__, cmd); if (cmd[0]) { if (strcmp(cmd, "rescan") == 0) gClearBBT = 1; else if (strcmp(cmd, "showbbt") == 0) gClearBBT = 2; else if (strcmp(cmd, "eraseall") == 0) gClearBBT = 8; else if (strcmp(cmd, "erase") == 0) gClearBBT = 7; else if (strcmp(cmd, "clearbbt") == 0) gClearBBT = 9; else if (strcmp(cmd, "showcet") == 0) gClearCET = 1; else if (strcmp(cmd, "resetcet") == 0) gClearCET = 2; else if (strcmp(cmd, "disablecet") == 0) gClearCET = 3; else printk(KERN_WARNING "%s: unknown command '%s'\n", __FUNCTION__, cmd); } for (csi = 0; csi < NAND_MAX_CS; csi++) { gNandTiming1[csi] = 0; gNandTiming2[csi] = 0; gAccControl[csi] = 0; gNandConfig[csi] = 0; } if (nacc == 1) PRINTK("%s: nacc=%d, gAccControl[0]=%08lx, gNandConfig[0]=%08lx\n", __FUNCTION__, nacc, acc[0], nandcfg[0]); if (nacc > 1) PRINTK("%s: nacc=%d, gAccControl[1]=%08lx, gNandConfig[1]=%08lx\n", __FUNCTION__, nacc, acc[1], nandcfg[1]); for (csi = 0; csi < nacc; csi++) gAccControl[csi] = acc[csi]; for (csi = 0; csi < ncfg; csi++) gNandConfig[csi] = nandcfg[csi]; ncsi = max(nt1, nt2); for (csi = 0; csi < ncsi; csi++) { if (nt1 && csi < nt1) gNandTiming1[csi] = t1[csi]; if (nt2 && csi < nt2) gNandTiming2[csi] = t2[csi]; } printk(KERN_INFO DRIVER_INFO " (BrcmNand Controller)\n"); if ( (pdev = platform_device_alloc(DRIVER_NAME, 0)) != NULL ) { platform_device_add(pdev); platform_device_put(pdev); ret = platform_driver_register(&brcmnand_platform_driver); brcmnand_resources[0].start = BPHYSADDR(BCHP_NAND_REG_START); brcmnand_resources[0].end = BPHYSADDR(BCHP_NAND_REG_END) + 3; if (ret >= 0) request_resource(&iomem_resource, &brcmnand_resources[0]); else printk("brcmnanddrv_init: driver_register failed, err=%d\n", ret); }else ret = -ENODEV; return ret; } static void __exit brcmnanddrv_exit(void) { release_resource(&brcmnand_resources[0]); platform_driver_unregister(&brcmnand_platform_driver); } module_init(brcmnanddrv_init); module_exit(brcmnanddrv_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ton Truong "); MODULE_DESCRIPTION("Broadcom NAND flash driver"); #endif //CONFIG_BCM_KF_MTD_BCMNAND