From 27144edc9f13029eca10988e8836d2131031ab5f Mon Sep 17 00:00:00 2001 From: Thomas Langer Date: Sun, 20 May 2012 15:46:19 +0200 Subject: [PATCH] --- yaml --- r: 319641 b: refs/heads/master c: 6cd3c7e2b1dc1e3cc28ffcef074d0b8182b6e501 h: refs/heads/master i: 319639: 75f5c5df0d2da8b196d3031d9a65fc92edcf9116 v: v3 --- [refs] | 2 +- trunk/arch/mips/Kconfig | 1 - trunk/arch/mips/boot/compressed/Makefile | 4 - trunk/arch/mips/boot/compressed/uart-16550.c | 5 - .../include/asm/mach-jz4740/jz4740_nand.h | 4 - trunk/arch/mips/jz4740/board-qi_lb60.c | 1 - trunk/arch/mips/jz4740/platform.c | 20 +- trunk/arch/mips/jz4740/reset.c | 49 +- trunk/drivers/mtd/nand/jz4740_nand.c | 228 ++------- trunk/drivers/spi/Kconfig | 9 + trunk/drivers/spi/Makefile | 1 + trunk/drivers/spi/spi-falcon.c | 469 ++++++++++++++++++ 12 files changed, 522 insertions(+), 271 deletions(-) create mode 100644 trunk/drivers/spi/spi-falcon.c diff --git a/[refs] b/[refs] index 8f9b9b4c63ce..51ca190ae162 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: f9c9affc5799b9243d57b86fefda0dffbce3a32e +refs/heads/master: 6cd3c7e2b1dc1e3cc28ffcef074d0b8182b6e501 diff --git a/trunk/arch/mips/Kconfig b/trunk/arch/mips/Kconfig index debdb6e83409..b3e10fdd3898 100644 --- a/trunk/arch/mips/Kconfig +++ b/trunk/arch/mips/Kconfig @@ -209,7 +209,6 @@ config MACH_JZ4740 select SYS_HAS_CPU_MIPS32_R1 select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN - select SYS_SUPPORTS_ZBOOT_UART16550 select DMA_NONCOHERENT select IRQ_CPU select GENERIC_GPIO diff --git a/trunk/arch/mips/boot/compressed/Makefile b/trunk/arch/mips/boot/compressed/Makefile index c2a3fb0ffc87..5042d51b0512 100644 --- a/trunk/arch/mips/boot/compressed/Makefile +++ b/trunk/arch/mips/boot/compressed/Makefile @@ -58,12 +58,8 @@ $(obj)/piggy.o: $(obj)/dummy.o $(obj)/vmlinux.bin.z FORCE # Calculate the load address of the compressed kernel image hostprogs-y := calc_vmlinuz_load_addr -ifeq ($(CONFIG_MACH_JZ4740),y) -VMLINUZ_LOAD_ADDRESS := 0x80600000 -else VMLINUZ_LOAD_ADDRESS = $(shell $(obj)/calc_vmlinuz_load_addr \ $(obj)/vmlinux.bin $(VMLINUX_LOAD_ADDRESS)) -endif vmlinuzobjs-y += $(obj)/piggy.o diff --git a/trunk/arch/mips/boot/compressed/uart-16550.c b/trunk/arch/mips/boot/compressed/uart-16550.c index 1c7b739b6a1d..c9caaf4fbf60 100644 --- a/trunk/arch/mips/boot/compressed/uart-16550.c +++ b/trunk/arch/mips/boot/compressed/uart-16550.c @@ -18,11 +18,6 @@ #define PORT(offset) (CKSEG1ADDR(AR7_REGS_UART0) + (4 * offset)) #endif -#ifdef CONFIG_MACH_JZ4740 -#define UART0_BASE 0xB0030000 -#define PORT(offset) (UART0_BASE + (4 * offset)) -#endif - #ifndef PORT #error please define the serial port address for your own machine #endif diff --git a/trunk/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/trunk/arch/mips/include/asm/mach-jz4740/jz4740_nand.h index 986982db7c38..bb5b9a4e29c8 100644 --- a/trunk/arch/mips/include/asm/mach-jz4740/jz4740_nand.h +++ b/trunk/arch/mips/include/asm/mach-jz4740/jz4740_nand.h @@ -19,8 +19,6 @@ #include #include -#define JZ_NAND_NUM_BANKS 4 - struct jz_nand_platform_data { int num_partitions; struct mtd_partition *partitions; @@ -29,8 +27,6 @@ struct jz_nand_platform_data { unsigned int busy_gpio; - unsigned char banks[JZ_NAND_NUM_BANKS]; - void (*ident_callback)(struct platform_device *, struct nand_chip *, struct mtd_partition **, int *num_partitions); }; diff --git a/trunk/arch/mips/jz4740/board-qi_lb60.c b/trunk/arch/mips/jz4740/board-qi_lb60.c index 9a3d9de4d04e..9a91fe9de696 100644 --- a/trunk/arch/mips/jz4740/board-qi_lb60.c +++ b/trunk/arch/mips/jz4740/board-qi_lb60.c @@ -140,7 +140,6 @@ static void qi_lb60_nand_ident(struct platform_device *pdev, static struct jz_nand_platform_data qi_lb60_nand_pdata = { .ident_callback = qi_lb60_nand_ident, .busy_gpio = 94, - .banks = { 1 }, }; /* Keyboard*/ diff --git a/trunk/arch/mips/jz4740/platform.c b/trunk/arch/mips/jz4740/platform.c index e342ed4cbd43..10929e2bc6d8 100644 --- a/trunk/arch/mips/jz4740/platform.c +++ b/trunk/arch/mips/jz4740/platform.c @@ -157,29 +157,11 @@ static struct resource jz4740_nand_resources[] = { .flags = IORESOURCE_MEM, }, { - .name = "bank1", + .name = "bank", .start = 0x18000000, .end = 0x180C0000 - 1, .flags = IORESOURCE_MEM, }, - { - .name = "bank2", - .start = 0x14000000, - .end = 0x140C0000 - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "bank3", - .start = 0x0C000000, - .end = 0x0C0C0000 - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "bank4", - .start = 0x08000000, - .end = 0x080C0000 - 1, - .flags = IORESOURCE_MEM, - }, }; struct platform_device jz4740_nand_device = { diff --git a/trunk/arch/mips/jz4740/reset.c b/trunk/arch/mips/jz4740/reset.c index 6c0da5afcf17..5f1fb95c0d0d 100644 --- a/trunk/arch/mips/jz4740/reset.c +++ b/trunk/arch/mips/jz4740/reset.c @@ -21,9 +21,6 @@ #include #include -#include "reset.h" -#include "clock.h" - static void jz4740_halt(void) { while (1) { @@ -56,57 +53,21 @@ static void jz4740_restart(char *command) jz4740_halt(); } -#define JZ_REG_RTC_CTRL 0x00 -#define JZ_REG_RTC_HIBERNATE 0x20 -#define JZ_REG_RTC_WAKEUP_FILTER 0x24 -#define JZ_REG_RTC_RESET_COUNTER 0x28 +#define JZ_REG_RTC_CTRL 0x00 +#define JZ_REG_RTC_HIBERNATE 0x20 -#define JZ_RTC_CTRL_WRDY BIT(7) -#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 -#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 +#define JZ_RTC_CTRL_WRDY BIT(7) -static inline void jz4740_rtc_wait_ready(void __iomem *rtc_base) +static void jz4740_power_off(void) { + void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x24); uint32_t ctrl; do { ctrl = readl(rtc_base + JZ_REG_RTC_CTRL); } while (!(ctrl & JZ_RTC_CTRL_WRDY)); -} -static void jz4740_power_off(void) -{ - void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x38); - unsigned long wakeup_filter_ticks; - unsigned long reset_counter_ticks; - - /* - * Set minimum wakeup pin assertion time: 100 ms. - * Range is 0 to 2 sec if RTC is clocked at 32 kHz. - */ - wakeup_filter_ticks = (100 * jz4740_clock_bdata.rtc_rate) / 1000; - if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK) - wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; - else - wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK; - jz4740_rtc_wait_ready(rtc_base); - writel(wakeup_filter_ticks, rtc_base + JZ_REG_RTC_WAKEUP_FILTER); - - /* - * Set reset pin low-level assertion time after wakeup: 60 ms. - * Range is 0 to 125 ms if RTC is clocked at 32 kHz. - */ - reset_counter_ticks = (60 * jz4740_clock_bdata.rtc_rate) / 1000; - if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK) - reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK; - else - reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK; - jz4740_rtc_wait_ready(rtc_base); - writel(reset_counter_ticks, rtc_base + JZ_REG_RTC_RESET_COUNTER); - - jz4740_rtc_wait_ready(rtc_base); writel(1, rtc_base + JZ_REG_RTC_HIBERNATE); - jz4740_halt(); } diff --git a/trunk/drivers/mtd/nand/jz4740_nand.c b/trunk/drivers/mtd/nand/jz4740_nand.c index 100b6775e175..a6fa884ae49b 100644 --- a/trunk/drivers/mtd/nand/jz4740_nand.c +++ b/trunk/drivers/mtd/nand/jz4740_nand.c @@ -52,10 +52,9 @@ #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) -#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa -#define JZ_NAND_MEM_CMD_OFFSET 0x08000 #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 +#define JZ_NAND_MEM_CMD_OFFSET 0x08000 struct jz_nand { struct mtd_info mtd; @@ -63,11 +62,8 @@ struct jz_nand { void __iomem *base; struct resource *mem; - unsigned char banks[JZ_NAND_NUM_BANKS]; - void __iomem *bank_base[JZ_NAND_NUM_BANKS]; - struct resource *bank_mem[JZ_NAND_NUM_BANKS]; - - int selected_bank; + void __iomem *bank_base; + struct resource *bank_mem; struct jz_nand_platform_data *pdata; bool is_reading; @@ -78,50 +74,26 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) return container_of(mtd, struct jz_nand, mtd); } -static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct jz_nand *nand = mtd_to_jz_nand(mtd); - struct nand_chip *chip = mtd->priv; - uint32_t ctrl; - int banknr; - - ctrl = readl(nand->base + JZ_REG_NAND_CTRL); - ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; - - if (chipnr == -1) { - banknr = -1; - } else { - banknr = nand->banks[chipnr] - 1; - chip->IO_ADDR_R = nand->bank_base[banknr]; - chip->IO_ADDR_W = nand->bank_base[banknr]; - } - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - - nand->selected_bank = banknr; -} - static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct jz_nand *nand = mtd_to_jz_nand(mtd); struct nand_chip *chip = mtd->priv; uint32_t reg; - void __iomem *bank_base = nand->bank_base[nand->selected_bank]; - - BUG_ON(nand->selected_bank < 0); if (ctrl & NAND_CTRL_CHANGE) { BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); if (ctrl & NAND_ALE) - bank_base += JZ_NAND_MEM_ADDR_OFFSET; + chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; else if (ctrl & NAND_CLE) - bank_base += JZ_NAND_MEM_CMD_OFFSET; - chip->IO_ADDR_W = bank_base; + chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; + else + chip->IO_ADDR_W = nand->bank_base; reg = readl(nand->base + JZ_REG_NAND_CTRL); if (ctrl & NAND_NCE) - reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); + reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); else - reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); writel(reg, nand->base + JZ_REG_NAND_CTRL); } if (dat != NAND_CMD_NONE) @@ -280,7 +252,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, } static int jz_nand_ioremap_resource(struct platform_device *pdev, - const char *name, struct resource **res, void *__iomem *base) + const char *name, struct resource **res, void __iomem **base) { int ret; @@ -316,90 +288,6 @@ static int jz_nand_ioremap_resource(struct platform_device *pdev, return ret; } -static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) -{ - iounmap(base); - release_mem_region(res->start, resource_size(res)); -} - -static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { - int ret; - int gpio; - char gpio_name[9]; - char res_name[6]; - uint32_t ctrl; - struct mtd_info *mtd = &nand->mtd; - struct nand_chip *chip = &nand->chip; - - /* Request GPIO port. */ - gpio = JZ_GPIO_MEM_CS0 + bank - 1; - sprintf(gpio_name, "NAND CS%d", bank); - ret = gpio_request(gpio, gpio_name); - if (ret) { - dev_warn(&pdev->dev, - "Failed to request %s gpio %d: %d\n", - gpio_name, gpio, ret); - goto notfound_gpio; - } - - /* Request I/O resource. */ - sprintf(res_name, "bank%d", bank); - ret = jz_nand_ioremap_resource(pdev, res_name, - &nand->bank_mem[bank - 1], - &nand->bank_base[bank - 1]); - if (ret) - goto notfound_resource; - - /* Enable chip in bank. */ - jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); - ctrl = readl(nand->base + JZ_REG_NAND_CTRL); - ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - - if (chipnr == 0) { - /* Detect first chip. */ - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - goto notfound_id; - - /* Retrieve the IDs from the first chip. */ - chip->select_chip(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - *nand_maf_id = chip->read_byte(mtd); - *nand_dev_id = chip->read_byte(mtd); - } else { - /* Detect additional chip. */ - chip->select_chip(mtd, chipnr); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - if (*nand_maf_id != chip->read_byte(mtd) - || *nand_dev_id != chip->read_byte(mtd)) { - ret = -ENODEV; - goto notfound_id; - } - - /* Update size of the MTD. */ - chip->numchips++; - mtd->size += chip->chipsize; - } - - dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); - return 0; - -notfound_id: - dev_info(&pdev->dev, "No chip found on bank %i\n", bank); - ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); -notfound_resource: - gpio_free(gpio); -notfound_gpio: - return ret; -} - static int __devinit jz_nand_probe(struct platform_device *pdev) { int ret; @@ -407,8 +295,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) struct nand_chip *chip; struct mtd_info *mtd; struct jz_nand_platform_data *pdata = pdev->dev.platform_data; - size_t chipnr, bank_idx; - uint8_t nand_maf_id = 0, nand_dev_id = 0; nand = kzalloc(sizeof(*nand), GFP_KERNEL); if (!nand) { @@ -419,6 +305,10 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); if (ret) goto err_free; + ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, + &nand->bank_base); + if (ret) + goto err_iounmap_mmio; if (pdata && gpio_is_valid(pdata->busy_gpio)) { ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); @@ -426,7 +316,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request busy gpio %d: %d\n", pdata->busy_gpio, ret); - goto err_iounmap_mmio; + goto err_iounmap_mem; } } @@ -449,51 +339,22 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->chip_delay = 50; chip->cmd_ctrl = jz_nand_cmd_ctrl; - chip->select_chip = jz_nand_select_chip; if (pdata && gpio_is_valid(pdata->busy_gpio)) chip->dev_ready = jz_nand_dev_ready; + chip->IO_ADDR_R = nand->bank_base; + chip->IO_ADDR_W = nand->bank_base; + nand->pdata = pdata; platform_set_drvdata(pdev, nand); - /* We are going to autodetect NAND chips in the banks specified in the - * platform data. Although nand_scan_ident() can detect multiple chips, - * it requires those chips to be numbered consecuitively, which is not - * always the case for external memory banks. And a fixed chip-to-bank - * mapping is not practical either, since for example Dingoo units - * produced at different times have NAND chips in different banks. - */ - chipnr = 0; - for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { - unsigned char bank; - - /* If there is no platform data, look for NAND in bank 1, - * which is the most likely bank since it is the only one - * that can be booted from. - */ - bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; - if (bank == 0) - break; - if (bank > JZ_NAND_NUM_BANKS) { - dev_warn(&pdev->dev, - "Skipping non-existing bank: %d\n", bank); - continue; - } - /* The detection routine will directly or indirectly call - * jz_nand_select_chip(), so nand->banks has to contain the - * bank we're checking. - */ - nand->banks[chipnr] = bank; - if (jz_nand_detect_bank(pdev, nand, bank, chipnr, - &nand_maf_id, &nand_dev_id) == 0) - chipnr++; - else - nand->banks[chipnr] = 0; - } - if (chipnr == 0) { - dev_err(&pdev->dev, "No NAND chips found\n"); - goto err_gpio_busy; + writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; } if (pdata && pdata->ident_callback) { @@ -503,8 +364,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) ret = nand_scan_tail(mtd); if (ret) { - dev_err(&pdev->dev, "Failed to scan NAND\n"); - goto err_unclaim_banks; + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; } ret = mtd_device_parse_register(mtd, NULL, NULL, @@ -521,21 +382,14 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) return 0; err_nand_release: - nand_release(mtd); -err_unclaim_banks: - while (chipnr--) { - unsigned char bank = nand->banks[chipnr]; - gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - } - writel(0, nand->base + JZ_REG_NAND_CTRL); -err_gpio_busy: - if (pdata && gpio_is_valid(pdata->busy_gpio)) - gpio_free(pdata->busy_gpio); + nand_release(&nand->mtd); +err_gpio_free: platform_set_drvdata(pdev, NULL); + gpio_free(pdata->busy_gpio); +err_iounmap_mem: + iounmap(nand->bank_base); err_iounmap_mmio: - jz_nand_iounmap_resource(nand->mem, nand->base); + iounmap(nand->base); err_free: kfree(nand); return ret; @@ -544,26 +398,16 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) static int __devexit jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; - size_t i; nand_release(&nand->mtd); /* Deassert and disable all chips */ writel(0, nand->base + JZ_REG_NAND_CTRL); - for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { - unsigned char bank = nand->banks[i]; - if (bank != 0) { - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); - } - } - if (pdata && gpio_is_valid(pdata->busy_gpio)) - gpio_free(pdata->busy_gpio); - - jz_nand_iounmap_resource(nand->mem, nand->base); + iounmap(nand->bank_base); + release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); + iounmap(nand->base); + release_mem_region(nand->mem->start, resource_size(nand->mem)); platform_set_drvdata(pdev, NULL); kfree(nand); diff --git a/trunk/drivers/spi/Kconfig b/trunk/drivers/spi/Kconfig index cd2fe350e724..b18abf31fd08 100644 --- a/trunk/drivers/spi/Kconfig +++ b/trunk/drivers/spi/Kconfig @@ -144,6 +144,15 @@ config SPI_EP93XX This enables using the Cirrus EP93xx SPI controller in master mode. +config SPI_FALCON + tristate "Falcon SPI controller support" + depends on SOC_FALCON + help + The external bus unit (EBU) found on the FALC-ON SoC has SPI + emulation that is designed for serial flash access. This driver + has only been tested with m25p80 type chips. The hardware has no + support for other types of SPI peripherals. + config SPI_GPIO tristate "GPIO-based bitbanging SPI Master" depends on GENERIC_GPIO diff --git a/trunk/drivers/spi/Makefile b/trunk/drivers/spi/Makefile index 9d75d2198ff5..b5cbab2b5ab0 100644 --- a/trunk/drivers/spi/Makefile +++ b/trunk/drivers/spi/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o +obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o diff --git a/trunk/drivers/spi/spi-falcon.c b/trunk/drivers/spi/spi-falcon.c new file mode 100644 index 000000000000..8f6aa735a24c --- /dev/null +++ b/trunk/drivers/spi/spi-falcon.c @@ -0,0 +1,469 @@ +/* + * 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. + * + * Copyright (C) 2012 Thomas Langer + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "sflash-falcon" + +#define FALCON_SPI_XFER_BEGIN (1 << 0) +#define FALCON_SPI_XFER_END (1 << 1) + +/* Bus Read Configuration Register0 */ +#define BUSRCON0 0x00000010 +/* Bus Write Configuration Register0 */ +#define BUSWCON0 0x00000018 +/* Serial Flash Configuration Register */ +#define SFCON 0x00000080 +/* Serial Flash Time Register */ +#define SFTIME 0x00000084 +/* Serial Flash Status Register */ +#define SFSTAT 0x00000088 +/* Serial Flash Command Register */ +#define SFCMD 0x0000008C +/* Serial Flash Address Register */ +#define SFADDR 0x00000090 +/* Serial Flash Data Register */ +#define SFDATA 0x00000094 +/* Serial Flash I/O Control Register */ +#define SFIO 0x00000098 +/* EBU Clock Control Register */ +#define EBUCC 0x000000C4 + +/* Dummy Phase Length */ +#define SFCMD_DUMLEN_OFFSET 16 +#define SFCMD_DUMLEN_MASK 0x000F0000 +/* Chip Select */ +#define SFCMD_CS_OFFSET 24 +#define SFCMD_CS_MASK 0x07000000 +/* field offset */ +#define SFCMD_ALEN_OFFSET 20 +#define SFCMD_ALEN_MASK 0x00700000 +/* SCK Rise-edge Position */ +#define SFTIME_SCKR_POS_OFFSET 8 +#define SFTIME_SCKR_POS_MASK 0x00000F00 +/* SCK Period */ +#define SFTIME_SCK_PER_OFFSET 0 +#define SFTIME_SCK_PER_MASK 0x0000000F +/* SCK Fall-edge Position */ +#define SFTIME_SCKF_POS_OFFSET 12 +#define SFTIME_SCKF_POS_MASK 0x0000F000 +/* Device Size */ +#define SFCON_DEV_SIZE_A23_0 0x03000000 +#define SFCON_DEV_SIZE_MASK 0x0F000000 +/* Read Data Position */ +#define SFTIME_RD_POS_MASK 0x000F0000 +/* Data Output */ +#define SFIO_UNUSED_WD_MASK 0x0000000F +/* Command Opcode mask */ +#define SFCMD_OPC_MASK 0x000000FF +/* dlen bytes of data to write */ +#define SFCMD_DIR_WRITE 0x00000100 +/* Data Length offset */ +#define SFCMD_DLEN_OFFSET 9 +/* Command Error */ +#define SFSTAT_CMD_ERR 0x20000000 +/* Access Command Pending */ +#define SFSTAT_CMD_PEND 0x00400000 +/* Frequency set to 100MHz. */ +#define EBUCC_EBUDIV_SELF100 0x00000001 +/* Serial Flash */ +#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000 +/* 8-bit multiplexed */ +#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000 +/* Serial Flash */ +#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000 +/* Chip Select after opcode */ +#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000 + +#define CLOCK_100M 100000000 +#define CLOCK_50M 50000000 + +struct falcon_sflash { + u32 sfcmd; /* for caching of opcode, direction, ... */ + struct spi_master *master; +}; + +int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, + unsigned long flags) +{ + struct device *dev = &spi->dev; + struct falcon_sflash *priv = spi_master_get_devdata(spi->master); + const u8 *txp = t->tx_buf; + u8 *rxp = t->rx_buf; + unsigned int bytelen = ((8 * t->len + 7) / 8); + unsigned int len, alen, dumlen; + u32 val; + enum { + state_init, + state_command_prepare, + state_write, + state_read, + state_disable_cs, + state_end + } state = state_init; + + do { + switch (state) { + case state_init: /* detect phase of upper layer sequence */ + { + /* initial write ? */ + if (flags & FALCON_SPI_XFER_BEGIN) { + if (!txp) { + dev_err(dev, + "BEGIN without tx data!\n"); + return -ENODATA; + } + /* + * Prepare the parts of the sfcmd register, + * which should not change during a sequence! + * Only exception are the length fields, + * especially alen and dumlen. + */ + + priv->sfcmd = ((spi->chip_select + << SFCMD_CS_OFFSET) + & SFCMD_CS_MASK); + priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED; + priv->sfcmd |= *txp; + txp++; + bytelen--; + if (bytelen) { + /* + * more data: + * maybe address and/or dummy + */ + state = state_command_prepare; + break; + } else { + dev_dbg(dev, "write cmd %02X\n", + priv->sfcmd & SFCMD_OPC_MASK); + } + } + /* continued write ? */ + if (txp && bytelen) { + state = state_write; + break; + } + /* read data? */ + if (rxp && bytelen) { + state = state_read; + break; + } + /* end of sequence? */ + if (flags & FALCON_SPI_XFER_END) + state = state_disable_cs; + else + state = state_end; + break; + } + /* collect tx data for address and dummy phase */ + case state_command_prepare: + { + /* txp is valid, already checked */ + val = 0; + alen = 0; + dumlen = 0; + while (bytelen > 0) { + if (alen < 3) { + val = (val << 8) | (*txp++); + alen++; + } else if ((dumlen < 15) && (*txp == 0)) { + /* + * assume dummy bytes are set to 0 + * from upper layer + */ + dumlen++; + txp++; + } else { + break; + } + bytelen--; + } + priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK); + priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) | + (dumlen << SFCMD_DUMLEN_OFFSET); + if (alen > 0) + ltq_ebu_w32(val, SFADDR); + + dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n", + priv->sfcmd & SFCMD_OPC_MASK, + alen, val, dumlen); + + if (bytelen > 0) { + /* continue with write */ + state = state_write; + } else if (flags & FALCON_SPI_XFER_END) { + /* end of sequence? */ + state = state_disable_cs; + } else { + /* + * go to end and expect another + * call (read or write) + */ + state = state_end; + } + break; + } + case state_write: + { + /* txp still valid */ + priv->sfcmd |= SFCMD_DIR_WRITE; + len = 0; + val = 0; + do { + if (bytelen--) + val |= (*txp++) << (8 * len++); + if ((flags & FALCON_SPI_XFER_END) + && (bytelen == 0)) { + priv->sfcmd &= + ~SFCMD_KEEP_CS_KEEP_SELECTED; + } + if ((len == 4) || (bytelen == 0)) { + ltq_ebu_w32(val, SFDATA); + ltq_ebu_w32(priv->sfcmd + | (len<sfcmd &= ~(SFCMD_ALEN_MASK + | SFCMD_DUMLEN_MASK); + } + } while (bytelen); + state = state_end; + break; + } + case state_read: + { + /* read data */ + priv->sfcmd &= ~SFCMD_DIR_WRITE; + do { + if ((flags & FALCON_SPI_XFER_END) + && (bytelen <= 4)) { + priv->sfcmd &= + ~SFCMD_KEEP_CS_KEEP_SELECTED; + } + len = (bytelen > 4) ? 4 : bytelen; + bytelen -= len; + ltq_ebu_w32(priv->sfcmd + | (len << SFCMD_DLEN_OFFSET), SFCMD); + priv->sfcmd &= ~(SFCMD_ALEN_MASK + | SFCMD_DUMLEN_MASK); + do { + val = ltq_ebu_r32(SFSTAT); + if (val & SFSTAT_CMD_ERR) { + /* reset error status */ + dev_err(dev, "SFSTAT: CMD_ERR"); + dev_err(dev, " (%x)\n", val); + ltq_ebu_w32(SFSTAT_CMD_ERR, + SFSTAT); + return -EBADE; + } + } while (val & SFSTAT_CMD_PEND); + val = ltq_ebu_r32(SFDATA); + do { + *rxp = (val & 0xFF); + rxp++; + val >>= 8; + len--; + } while (len); + } while (bytelen); + state = state_end; + break; + } + case state_disable_cs: + { + priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED; + ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET), + SFCMD); + val = ltq_ebu_r32(SFSTAT); + if (val & SFSTAT_CMD_ERR) { + /* reset error status */ + dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val); + ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT); + return -EBADE; + } + state = state_end; + break; + } + case state_end: + break; + } + } while (state != state_end); + + return 0; +} + +static int falcon_sflash_setup(struct spi_device *spi) +{ + unsigned int i; + unsigned long flags; + + if (spi->chip_select > 0) + return -ENODEV; + + spin_lock_irqsave(&ebu_lock, flags); + + if (spi->max_speed_hz >= CLOCK_100M) { + /* set EBU clock to 100 MHz */ + ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC); + i = 1; /* divider */ + } else { + /* set EBU clock to 50 MHz */ + ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC); + + /* search for suitable divider */ + for (i = 1; i < 7; i++) { + if (CLOCK_50M / i <= spi->max_speed_hz) + break; + } + } + + /* setup period of serial clock */ + ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK + | SFTIME_SCKR_POS_MASK + | SFTIME_SCK_PER_MASK, + (i << SFTIME_SCKR_POS_OFFSET) + | (i << (SFTIME_SCK_PER_OFFSET + 1)), + SFTIME); + + /* + * set some bits of unused_wd, to not trigger HOLD/WP + * signals on non QUAD flashes + */ + ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO); + + ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX, + BUSRCON0); + ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0); + /* set address wrap around to maximum for 24-bit addresses */ + ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON); + + spin_unlock_irqrestore(&ebu_lock, flags); + + return 0; +} + +static int falcon_sflash_prepare_xfer(struct spi_master *master) +{ + return 0; +} + +static int falcon_sflash_unprepare_xfer(struct spi_master *master) +{ + return 0; +} + +static int falcon_sflash_xfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct falcon_sflash *priv = spi_master_get_devdata(master); + struct spi_transfer *t; + unsigned long spi_flags; + unsigned long flags; + int ret = 0; + + priv->sfcmd = 0; + m->actual_length = 0; + + spi_flags = FALCON_SPI_XFER_BEGIN; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (list_is_last(&t->transfer_list, &m->transfers)) + spi_flags |= FALCON_SPI_XFER_END; + + spin_lock_irqsave(&ebu_lock, flags); + ret = falcon_sflash_xfer(m->spi, t, spi_flags); + spin_unlock_irqrestore(&ebu_lock, flags); + + if (ret) + break; + + m->actual_length += t->len; + + WARN_ON(t->delay_usecs || t->cs_change); + spi_flags = 0; + } + + m->status = ret; + m->complete(m->context); + + return 0; +} + +static int __devinit falcon_sflash_probe(struct platform_device *pdev) +{ + struct falcon_sflash *priv; + struct spi_master *master; + int ret; + + if (ltq_boot_select() != BS_SPI) { + dev_err(&pdev->dev, "invalid bootstrap options\n"); + return -ENODEV; + } + + master = spi_alloc_master(&pdev->dev, sizeof(*priv)); + if (!master) + return -ENOMEM; + + priv = spi_master_get_devdata(master); + priv->master = master; + + master->mode_bits = SPI_MODE_3; + master->num_chipselect = 1; + master->bus_num = -1; + master->setup = falcon_sflash_setup; + master->prepare_transfer_hardware = falcon_sflash_prepare_xfer; + master->transfer_one_message = falcon_sflash_xfer_one; + master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer; + master->dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, priv); + + ret = spi_register_master(master); + if (ret) + spi_master_put(master); + return ret; +} + +static int __devexit falcon_sflash_remove(struct platform_device *pdev) +{ + struct falcon_sflash *priv = platform_get_drvdata(pdev); + + spi_unregister_master(priv->master); + + return 0; +} + +static const struct of_device_id falcon_sflash_match[] = { + { .compatible = "lantiq,sflash-falcon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, falcon_sflash_match); + +static struct platform_driver falcon_sflash_driver = { + .probe = falcon_sflash_probe, + .remove = __devexit_p(falcon_sflash_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = falcon_sflash_match, + } +}; + +module_platform_driver(falcon_sflash_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver");