diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-30 08:31:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-30 08:31:35 -0700 |
commit | 79346507ad48895f41b438fa562b1965721f36b9 (patch) | |
tree | 5c115ce87f1fbc0b530f30db56cecf824e9f6e05 /arch | |
parent | 706d4b12f8d7edd28d7e879a77235472da393edb (diff) | |
parent | 40847437f15221b5822ba70550e8b9fcccfb9bb3 (diff) | |
download | talos-obmc-linux-79346507ad48895f41b438fa562b1965721f36b9.tar.gz talos-obmc-linux-79346507ad48895f41b438fa562b1965721f36b9.zip |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (82 commits)
mtd: fix build error in m25p80.c
mtd: Remove redundant mutex from mtd_blkdevs.c
MTD: Fix wrong check register_blkdev return value
Revert "mtd: cleanup Kconfig dependencies"
mtd: cfi_cmdset_0002: make sector erase command variable
mtd: cfi_cmdset_0002: add CFI detection for SST 38VF640x chips
mtd: cfi_util: add support for switching SST 39VF640xB chips into QRY mode
mtd: cfi_cmdset_0001: use defined value of P_ID_INTEL_PERFORMANCE instead of hardcoded one
block2mtd: dubious assignment
P4080/mtd: Fix the freescale lbc issue with 36bit mode
P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
mtd: phram: use KBUILD_MODNAME
mtd: OneNAND: S5PC110: Fix double call suspend & resume function
mtd: nand: fix MTD_MODE_RAW writes
jffs2: use kmemdup
mtd: sm_ftl: cosmetic, use bool when possible
mtd: r852: remove useless pci powerup/down from suspend/resume routines
mtd: blktrans: fix a race vs kthread_stop
mtd: blktrans: kill BKL
mtd: allow to unload the mtdtrans module if its block devices aren't open
...
Fix up trivial whitespace-introduced conflict in drivers/mtd/mtdchar.c
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-u300/clock.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-u300/core.c | 47 | ||||
-rw-r--r-- | arch/arm/mach-u300/include/mach/u300-regs.h | 2 | ||||
-rw-r--r-- | arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | 18 | ||||
-rw-r--r-- | arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h | 97 | ||||
-rw-r--r-- | arch/powerpc/Kconfig | 7 | ||||
-rw-r--r-- | arch/powerpc/include/asm/fsl_lbc.h | 34 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_lbc.c | 244 |
8 files changed, 397 insertions, 58 deletions
diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c index 60acf9e708ae..7458fc6df5c6 100644 --- a/arch/arm/mach-u300/clock.c +++ b/arch/arm/mach-u300/clock.c @@ -66,7 +66,7 @@ static DEFINE_SPINLOCK(syscon_resetreg_lock); * AMBA bus * | * +- CPU - * +- NANDIF NAND Flash interface + * +- FSMC NANDIF NAND Flash interface * +- SEMI Shared Memory interface * +- ISP Image Signal Processor (U335 only) * +- CDS (U335 only) @@ -726,7 +726,7 @@ static struct clk cpu_clk = { }; static struct clk nandif_clk = { - .name = "NANDIF", + .name = "FSMC", .parent = &amba_clk, .hw_ctrld = false, .reset = true, @@ -1259,7 +1259,7 @@ static struct clk_lookup lookups[] = { /* Connected directly to the AMBA bus */ DEF_LOOKUP("amba", &amba_clk), DEF_LOOKUP("cpu", &cpu_clk), - DEF_LOOKUP("fsmc", &nandif_clk), + DEF_LOOKUP("fsmc-nand", &nandif_clk), DEF_LOOKUP("semi", &semi_clk), #ifdef CONFIG_MACH_U300_BS335 DEF_LOOKUP("isp", &isp_clk), diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index ea41c236be0f..aa53ee22438f 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -21,7 +21,8 @@ #include <linux/gpio.h> #include <linux/clk.h> #include <linux/err.h> -#include <mach/coh901318.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/fsmc.h> #include <asm/types.h> #include <asm/setup.h> @@ -30,6 +31,7 @@ #include <asm/mach/map.h> #include <asm/mach/irq.h> +#include <mach/coh901318.h> #include <mach/hardware.h> #include <mach/syscon.h> #include <mach/dma_channels.h> @@ -285,6 +287,13 @@ static struct resource rtc_resources[] = { */ static struct resource fsmc_resources[] = { { + .name = "nand_data", + .start = U300_NAND_CS0_PHYS_BASE, + .end = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "fsmc_regs", .start = U300_NAND_IF_PHYS_BASE, .end = U300_NAND_IF_PHYS_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, @@ -1429,11 +1438,39 @@ static struct platform_device rtc_device = { .resource = rtc_resources, }; -static struct platform_device fsmc_device = { - .name = "nandif", +static struct mtd_partition u300_partitions[] = { + { + .name = "bootrecords", + .offset = 0, + .size = SZ_128K, + }, + { + .name = "free", + .offset = SZ_128K, + .size = 8064 * SZ_1K, + }, + { + .name = "platform", + .offset = 8192 * SZ_1K, + .size = 253952 * SZ_1K, + }, +}; + +static struct fsmc_nand_platform_data nand_platform_data = { + .partitions = u300_partitions, + .nr_partitions = ARRAY_SIZE(u300_partitions), + .options = NAND_SKIP_BBTSCAN, + .width = FSMC_NAND_BW8, +}; + +static struct platform_device nand_device = { + .name = "fsmc-nand", .id = -1, - .num_resources = ARRAY_SIZE(fsmc_resources), .resource = fsmc_resources, + .num_resources = ARRAY_SIZE(fsmc_resources), + .dev = { + .platform_data = &nand_platform_data, + }, }; static struct platform_device ave_device = { @@ -1465,7 +1502,7 @@ static struct platform_device *platform_devs[] __initdata = { &keypad_device, &rtc_device, &gpio_device, - &fsmc_device, + &nand_device, &wdog_device, &ave_device }; diff --git a/arch/arm/mach-u300/include/mach/u300-regs.h b/arch/arm/mach-u300/include/mach/u300-regs.h index 56721a0cd2af..8b85df4c8d8f 100644 --- a/arch/arm/mach-u300/include/mach/u300-regs.h +++ b/arch/arm/mach-u300/include/mach/u300-regs.h @@ -20,11 +20,9 @@ /* NAND Flash CS0 */ #define U300_NAND_CS0_PHYS_BASE 0x80000000 -#define U300_NAND_CS0_VIRT_BASE 0xff040000 /* NFIF */ #define U300_NAND_IF_PHYS_BASE 0x9f800000 -#define U300_NAND_IF_VIRT_BASE 0xff030000 /* AHB Peripherals */ #define U300_AHB_PER_PHYS_BASE 0xa0000000 diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h index 3478eae32d8a..01a8448e471c 100644 --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h @@ -30,15 +30,15 @@ struct pxa3xx_nand_cmdset { }; struct pxa3xx_nand_flash { - const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ - const struct pxa3xx_nand_cmdset *cmdset; - - uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */ - uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ - uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ - uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */ - uint32_t num_blocks; /* Number of physical blocks in Flash */ - uint32_t chip_id; + uint32_t chip_id; + unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */ + unsigned int page_size; /* Page size in bytes (PAGE_SZ) */ + unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ + unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ + unsigned int num_blocks; /* Number of physical blocks in Flash */ + + struct pxa3xx_nand_cmdset *cmdset; /* NAND command set */ + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ }; struct pxa3xx_nand_platform_data { diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h b/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h new file mode 100644 index 000000000000..5325084d5c48 --- /dev/null +++ b/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h @@ -0,0 +1,97 @@ +#ifndef __BCM963XX_TAG_H +#define __BCM963XX_TAG_H + +#define TAGVER_LEN 4 /* Length of Tag Version */ +#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */ +#define SIG1_LEN 20 /* Company Signature 1 Length */ +#define SIG2_LEN 14 /* Company Signature 2 Lenght */ +#define BOARDID_LEN 16 /* Length of BoardId */ +#define ENDIANFLAG_LEN 2 /* Endian Flag Length */ +#define CHIPID_LEN 6 /* Chip Id Length */ +#define IMAGE_LEN 10 /* Length of Length Field */ +#define ADDRESS_LEN 12 /* Length of Address field */ +#define DUALFLAG_LEN 2 /* Dual Image flag Length */ +#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */ +#define RSASIG_LEN 20 /* Length of RSA Signature in tag */ +#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */ +#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */ +#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */ +#define CRC_LEN 4 /* Length of CRC in bytes */ +#define ALTTAGINFO_LEN 54 /* Alternate length for vendor information; Pirelli */ + +#define NUM_PIRELLI 2 +#define IMAGETAG_CRC_START 0xFFFFFFFF + +#define PIRELLI_BOARDS { \ + "AGPF-S0", \ + "DWV-S0", \ +} + +/* + * The broadcom firmware assumes the rootfs starts the image, + * therefore uses the rootfs start (flash_image_address) + * to determine where to flash the image. Since we have the kernel first + * we have to give it the kernel address, but the crc uses the length + * associated with this address (root_length), which is added to the kernel + * length (kernel_length) to determine the length of image to flash and thus + * needs to be rootfs + deadcode (jffs2 EOF marker) +*/ + +struct bcm_tag { + /* 0-3: Version of the image tag */ + char tag_version[TAGVER_LEN]; + /* 4-23: Company Line 1 */ + char sig_1[SIG1_LEN]; + /* 24-37: Company Line 2 */ + char sig_2[SIG2_LEN]; + /* 38-43: Chip this image is for */ + char chip_id[CHIPID_LEN]; + /* 44-59: Board name */ + char board_id[BOARDID_LEN]; + /* 60-61: Map endianness -- 1 BE 0 LE */ + char big_endian[ENDIANFLAG_LEN]; + /* 62-71: Total length of image */ + char total_length[IMAGE_LEN]; + /* 72-83: Address in memory of CFE */ + char cfe__address[ADDRESS_LEN]; + /* 84-93: Size of CFE */ + char cfe_length[IMAGE_LEN]; + /* 94-105: Address in memory of image start + * (kernel for OpenWRT, rootfs for stock firmware) + */ + char flash_image_start[ADDRESS_LEN]; + /* 106-115: Size of rootfs */ + char root_length[IMAGE_LEN]; + /* 116-127: Address in memory of kernel */ + char kernel_address[ADDRESS_LEN]; + /* 128-137: Size of kernel */ + char kernel_length[IMAGE_LEN]; + /* 138-139: Unused at the moment */ + char dual_image[DUALFLAG_LEN]; + /* 140-141: Unused at the moment */ + char inactive_flag[INACTIVEFLAG_LEN]; + /* 142-161: RSA Signature (not used; some vendors may use this) */ + char rsa_signature[RSASIG_LEN]; + /* 162-191: Compilation and related information (not used in OpenWrt) */ + char information1[TAGINFO1_LEN]; + /* 192-195: Version flash layout */ + char flash_layout_ver[FLASHLAYOUTVER_LEN]; + /* 196-199: kernel+rootfs CRC32 */ + char fskernel_crc[CRC_LEN]; + /* 200-215: Unused except on Alice Gate where is is information */ + char information2[TAGINFO2_LEN]; + /* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */ + char image_crc[CRC_LEN]; + /* 220-223: CRC32 of rootfs partition */ + char rootfs_crc[CRC_LEN]; + /* 224-227: CRC32 of kernel partition */ + char kernel_crc[CRC_LEN]; + /* 228-235: Unused at present */ + char reserved1[8]; + /* 236-239: CRC32 of header excluding tagVersion */ + char header_crc[CRC_LEN]; + /* 240-255: Unused at present */ + char reserved2[16]; +}; + +#endif /* __BCM63XX_TAG_H */ diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c7e40b37aa65..b6447190e1a2 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -682,9 +682,12 @@ config 4xx_SOC bool config FSL_LBC - bool + bool "Freescale Local Bus support" + depends on FSL_SOC help - Freescale Localbus support + Enables reporting of errors from the Freescale local bus + controller. Also contains some common code used by + drivers for specific local bus peripherals. config FSL_GTM bool diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h index 1b5a21041f9b..5c1bf3466749 100644 --- a/arch/powerpc/include/asm/fsl_lbc.h +++ b/arch/powerpc/include/asm/fsl_lbc.h @@ -1,9 +1,10 @@ /* Freescale Local Bus Controller * - * Copyright (c) 2006-2007 Freescale Semiconductor + * Copyright © 2006-2007, 2010 Freescale Semiconductor * * Authors: Nick Spence <nick.spence@freescale.com>, * Scott Wood <scottwood@freescale.com> + * Jack Lan <jack.lan@freescale.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +27,8 @@ #include <linux/compiler.h> #include <linux/types.h> #include <linux/io.h> +#include <linux/device.h> +#include <linux/spinlock.h> struct fsl_lbc_bank { __be32 br; /**< Base Register */ @@ -125,13 +128,23 @@ struct fsl_lbc_regs { #define LTESR_ATMW 0x00800000 #define LTESR_ATMR 0x00400000 #define LTESR_CS 0x00080000 +#define LTESR_UPM 0x00000002 #define LTESR_CC 0x00000001 #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) +#define LTESR_MASK (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \ + | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \ + | LTESR_CC) +#define LTESR_CLEAR 0xFFFFFFFF +#define LTECCR_CLEAR 0xFFFFFFFF +#define LTESR_STATUS LTESR_MASK +#define LTEIR_ENABLE LTESR_MASK +#define LTEDR_ENABLE 0x00000000 __be32 ltedr; /**< Transfer Error Disable Register */ __be32 lteir; /**< Transfer Error Interrupt Register */ __be32 lteatr; /**< Transfer Error Attributes Register */ __be32 ltear; /**< Transfer Error Address Register */ - u8 res6[0xC]; + __be32 lteccr; /**< Transfer Error ECC Register */ + u8 res6[0x8]; __be32 lbcr; /**< Configuration Register */ #define LBCR_LDIS 0x80000000 #define LBCR_LDIS_SHIFT 31 @@ -235,6 +248,7 @@ struct fsl_upm { int width; }; +extern u32 fsl_lbc_addr(phys_addr_t addr_base); extern int fsl_lbc_find(phys_addr_t addr_base); extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm); @@ -265,7 +279,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm) cpu_relax(); } +/* overview of the fsl lbc controller */ + +struct fsl_lbc_ctrl { + /* device info */ + struct device *dev; + struct fsl_lbc_regs __iomem *regs; + int irq; + wait_queue_head_t irq_wait; + spinlock_t lock; + void *nand; + + /* status read from LTESR by irq handler */ + unsigned int irq_status; +}; + extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar); +extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; #endif /* __ASM_FSL_LBC_H */ diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c index dceb8d1a843d..4fcb5a4e60dd 100644 --- a/arch/powerpc/sysdev/fsl_lbc.c +++ b/arch/powerpc/sysdev/fsl_lbc.c @@ -1,9 +1,12 @@ /* * Freescale LBC and UPM routines. * - * Copyright (c) 2007-2008 MontaVista Software, Inc. + * Copyright © 2007-2008 MontaVista Software, Inc. + * Copyright © 2010 Freescale Semiconductor * * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * Author: Jack Lan <Jack.Lan@freescale.com> + * Author: Roy Zang <tie-fei.zang@freescale.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,39 +22,37 @@ #include <linux/types.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/mod_devicetable.h> #include <asm/prom.h> #include <asm/fsl_lbc.h> static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock); -static struct fsl_lbc_regs __iomem *fsl_lbc_regs; +struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; +EXPORT_SYMBOL(fsl_lbc_ctrl_dev); -static char __initdata *compat_lbc[] = { - "fsl,pq2-localbus", - "fsl,pq2pro-localbus", - "fsl,pq3-localbus", - "fsl,elbc", -}; - -static int __init fsl_lbc_init(void) +/** + * fsl_lbc_addr - convert the base address + * @addr_base: base address of the memory bank + * + * This function converts a base address of lbc into the right format for the + * BR register. If the SOC has eLBC then it returns 32bit physical address + * else it convers a 34bit local bus physical address to correct format of + * 32bit address for BR register (Example: MPC8641). + */ +u32 fsl_lbc_addr(phys_addr_t addr_base) { - struct device_node *lbus; - int i; + struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node; + u32 addr = addr_base & 0xffff8000; - for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) { - lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]); - if (lbus) - goto found; - } - return -ENODEV; + if (of_device_is_compatible(np, "fsl,elbc")) + return addr; -found: - fsl_lbc_regs = of_iomap(lbus, 0); - of_node_put(lbus); - if (!fsl_lbc_regs) - return -ENOMEM; - return 0; + return addr | ((addr_base & 0x300000000ull) >> 19); } -arch_initcall(fsl_lbc_init); +EXPORT_SYMBOL(fsl_lbc_addr); /** * fsl_lbc_find - find Localbus bank @@ -65,15 +66,17 @@ arch_initcall(fsl_lbc_init); int fsl_lbc_find(phys_addr_t addr_base) { int i; + struct fsl_lbc_regs __iomem *lbc; - if (!fsl_lbc_regs) + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) return -ENODEV; - for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) { - __be32 br = in_be32(&fsl_lbc_regs->bank[i].br); - __be32 or = in_be32(&fsl_lbc_regs->bank[i].or); + lbc = fsl_lbc_ctrl_dev->regs; + for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) { + __be32 br = in_be32(&lbc->bank[i].br); + __be32 or = in_be32(&lbc->bank[i].or); - if (br & BR_V && (br & or & BR_BA) == addr_base) + if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base)) return i; } @@ -94,22 +97,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm) { int bank; __be32 br; + struct fsl_lbc_regs __iomem *lbc; bank = fsl_lbc_find(addr_base); if (bank < 0) return bank; - br = in_be32(&fsl_lbc_regs->bank[bank].br); + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) + return -ENODEV; + + lbc = fsl_lbc_ctrl_dev->regs; + br = in_be32(&lbc->bank[bank].br); switch (br & BR_MSEL) { case BR_MS_UPMA: - upm->mxmr = &fsl_lbc_regs->mamr; + upm->mxmr = &lbc->mamr; break; case BR_MS_UPMB: - upm->mxmr = &fsl_lbc_regs->mbmr; + upm->mxmr = &lbc->mbmr; break; case BR_MS_UPMC: - upm->mxmr = &fsl_lbc_regs->mcmr; + upm->mxmr = &lbc->mcmr; break; default: return -EINVAL; @@ -148,9 +156,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) int ret = 0; unsigned long flags; + if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) + return -ENODEV; + spin_lock_irqsave(&fsl_lbc_lock, flags); - out_be32(&fsl_lbc_regs->mar, mar); + out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar); switch (upm->width) { case 8: @@ -172,3 +183,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) return ret; } EXPORT_SYMBOL(fsl_upm_run_pattern); + +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl) +{ + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + + /* clear event registers */ + setbits32(&lbc->ltesr, LTESR_CLEAR); + out_be32(&lbc->lteatr, 0); + out_be32(&lbc->ltear, 0); + out_be32(&lbc->lteccr, LTECCR_CLEAR); + out_be32(&lbc->ltedr, LTEDR_ENABLE); + + /* Enable interrupts for any detected events */ + out_be32(&lbc->lteir, LTEIR_ENABLE); + + return 0; +} + +/* + * NOTE: This interrupt is used to report localbus events of various kinds, + * such as transaction errors on the chipselects. + */ + +static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data) +{ + struct fsl_lbc_ctrl *ctrl = data; + struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + u32 status; + + status = in_be32(&lbc->ltesr); + if (!status) + return IRQ_NONE; + + out_be32(&lbc->ltesr, LTESR_CLEAR); + out_be32(&lbc->lteatr, 0); + out_be32(&lbc->ltear, 0); + ctrl->irq_status = status; + + if (status & LTESR_BM) + dev_err(ctrl->dev, "Local bus monitor time-out: " + "LTESR 0x%08X\n", status); + if (status & LTESR_WP) + dev_err(ctrl->dev, "Write protect error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_ATMW) + dev_err(ctrl->dev, "Atomic write error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_ATMR) + dev_err(ctrl->dev, "Atomic read error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_CS) + dev_err(ctrl->dev, "Chip select error: " + "LTESR 0x%08X\n", status); + if (status & LTESR_UPM) + ; + if (status & LTESR_FCT) { + dev_err(ctrl->dev, "FCM command time-out: " + "LTESR 0x%08X\n", status); + smp_wmb(); + wake_up(&ctrl->irq_wait); + } + if (status & LTESR_PAR) { + dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: " + "LTESR 0x%08X\n", status); + smp_wmb(); + wake_up(&ctrl->irq_wait); + } + if (status & LTESR_CC) { + smp_wmb(); + wake_up(&ctrl->irq_wait); + } + if (status & ~LTESR_MASK) + dev_err(ctrl->dev, "Unknown error: " + "LTESR 0x%08X\n", status); + return IRQ_HANDLED; +} + +/* + * fsl_lbc_ctrl_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code allocates all of + * the resources needed for the controller only. The + * resources for the NAND banks themselves are allocated + * in the chip probe function. +*/ + +static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev) +{ + int ret; + + if (!dev->dev.of_node) { + dev_err(&dev->dev, "Device OF-Node is NULL"); + return -EFAULT; + } + + fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL); + if (!fsl_lbc_ctrl_dev) + return -ENOMEM; + + dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev); + + spin_lock_init(&fsl_lbc_ctrl_dev->lock); + init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait); + + fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); + if (!fsl_lbc_ctrl_dev->regs) { + dev_err(&dev->dev, "failed to get memory region\n"); + ret = -ENODEV; + goto err; + } + + fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); + if (fsl_lbc_ctrl_dev->irq == NO_IRQ) { + dev_err(&dev->dev, "failed to get irq resource\n"); + ret = -ENODEV; + goto err; + } + + fsl_lbc_ctrl_dev->dev = &dev->dev; + + ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev); + if (ret < 0) + goto err; + + ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0, + "fsl-lbc", fsl_lbc_ctrl_dev); + if (ret != 0) { + dev_err(&dev->dev, "failed to install irq (%d)\n", + fsl_lbc_ctrl_dev->irq); + ret = fsl_lbc_ctrl_dev->irq; + goto err; + } + + return 0; + +err: + iounmap(fsl_lbc_ctrl_dev->regs); + kfree(fsl_lbc_ctrl_dev); + return ret; +} + +static const struct of_device_id fsl_lbc_match[] = { + { .compatible = "fsl,elbc", }, + { .compatible = "fsl,pq3-localbus", }, + { .compatible = "fsl,pq2-localbus", }, + { .compatible = "fsl,pq2pro-localbus", }, + {}, +}; + +static struct platform_driver fsl_lbc_ctrl_driver = { + .driver = { + .name = "fsl-lbc", + .of_match_table = fsl_lbc_match, + }, + .probe = fsl_lbc_ctrl_probe, +}; + +static int __init fsl_lbc_init(void) +{ + return platform_driver_register(&fsl_lbc_ctrl_driver); +} +module_init(fsl_lbc_init); |