From: Haavard Skinnemoen Date: Tue, 26 Sep 2006 06:32:16 +0000 (-0700) Subject: [PATCH] AVR32 MTD: Static Memory Controller driver X-Git-Tag: v2.6.19-rc1~1139 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc157b75960f1f33566074e820342690216629b9;p=linux-2.6 [PATCH] AVR32 MTD: Static Memory Controller driver This patchset adds the necessary drivers and infrastructure to access the external flash on the ATSTK1000 board through the MTD subsystem. With this stuff in place, it will be possible to use a jffs2 filesystem stored in the external flash as a root filesystem. It might also be possible to update the boot loader if you drop the write protection of partition 0. As suggested by David Woodhouse, I reworked the patches to use the physmap driver instead of introducing a separate mapping driver for the ATSTK1000. I've also cleaned up the hsmc header by removing useless comments and converting spaces to tabs (my headerfile generator needs some work.) Unfortunately, I couldn't unlock the flash in fixup_use_atmel_lock because the erase regions hadn't been set up yet, so I had to do it from cfi_amdstd_setup instead. This patch: This adds a simple API for configuring the static memory controller along with an implementation for the Atmel HSMC. Signed-off-by: Haavard Skinnemoen Cc: David Woodhouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index 4b10853eb6..f62eb69155 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,2 +1,2 @@ -obj-y += at32ap.o clock.o pio.o intc.o extint.o +obj-y += at32ap.o clock.o pio.o intc.o extint.o hsmc.o obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index e8c6893a1c..37982b6039 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c @@ -450,6 +450,13 @@ static struct clk hramc_clk = { .users = 1, }; +static struct resource smc0_resource[] = { + PBMEM(0xfff03400), +}; +DEFINE_DEV(smc, 0); +DEV_CLK(pclk, smc0, pbb, 13); +DEV_CLK(mck, smc0, hsb, 0); + static struct platform_device pdc_device = { .name = "pdc", .id = 0, @@ -503,6 +510,7 @@ void __init at32_add_system_devices(void) platform_device_register(&at32_sm_device); platform_device_register(&at32_intc0_device); + platform_device_register(&smc0_device); platform_device_register(&pdc_device); platform_device_register(&pio0_device); @@ -796,6 +804,8 @@ struct clk *at32_clock_list[] = { &at32_intc0_pclk, &ebi_clk, &hramc_clk, + &smc0_pclk, + &smc0_mck, &pdc_hclk, &pdc_pclk, &pico_clk, diff --git a/arch/avr32/mach-at32ap/hsmc.c b/arch/avr32/mach-at32ap/hsmc.c new file mode 100644 index 0000000000..7691721928 --- /dev/null +++ b/arch/avr32/mach-at32ap/hsmc.c @@ -0,0 +1,164 @@ +/* + * Static Memory Controller for AT32 chips + * + * Copyright (C) 2006 Atmel Corporation + * + * 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. + */ +#define DEBUG +#include +#include +#include +#include +#include + +#include +#include + +#include "hsmc.h" + +#define NR_CHIP_SELECTS 6 + +struct hsmc { + void __iomem *regs; + struct clk *pclk; + struct clk *mck; +}; + +static struct hsmc *hsmc; + +int smc_set_configuration(int cs, const struct smc_config *config) +{ + unsigned long mul; + unsigned long offset; + u32 setup, pulse, cycle, mode; + + if (!hsmc) + return -ENODEV; + if (cs >= NR_CHIP_SELECTS) + return -EINVAL; + + /* + * cycles = x / T = x * f + * = ((x * 1000000000) * ((f * 65536) / 1000000000)) / 65536 + * = ((x * 1000000000) * (((f / 10000) * 65536) / 100000)) / 65536 + */ + mul = (clk_get_rate(hsmc->mck) / 10000) << 16; + mul /= 100000; + +#define ns2cyc(x) ((((x) * mul) + 65535) >> 16) + + setup = (HSMC_BF(NWE_SETUP, ns2cyc(config->nwe_setup)) + | HSMC_BF(NCS_WR_SETUP, ns2cyc(config->ncs_write_setup)) + | HSMC_BF(NRD_SETUP, ns2cyc(config->nrd_setup)) + | HSMC_BF(NCS_RD_SETUP, ns2cyc(config->ncs_read_setup))); + pulse = (HSMC_BF(NWE_PULSE, ns2cyc(config->nwe_pulse)) + | HSMC_BF(NCS_WR_PULSE, ns2cyc(config->ncs_write_pulse)) + | HSMC_BF(NRD_PULSE, ns2cyc(config->nrd_pulse)) + | HSMC_BF(NCS_RD_PULSE, ns2cyc(config->ncs_read_pulse))); + cycle = (HSMC_BF(NWE_CYCLE, ns2cyc(config->write_cycle)) + | HSMC_BF(NRD_CYCLE, ns2cyc(config->read_cycle))); + + switch (config->bus_width) { + case 1: + mode = HSMC_BF(DBW, HSMC_DBW_8_BITS); + break; + case 2: + mode = HSMC_BF(DBW, HSMC_DBW_16_BITS); + break; + case 4: + mode = HSMC_BF(DBW, HSMC_DBW_32_BITS); + break; + default: + return -EINVAL; + } + + if (config->nrd_controlled) + mode |= HSMC_BIT(READ_MODE); + if (config->nwe_controlled) + mode |= HSMC_BIT(WRITE_MODE); + if (config->byte_write) + mode |= HSMC_BIT(BAT); + + pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n", + cs, setup, pulse, cycle, mode); + + offset = cs * 0x10; + hsmc_writel(hsmc, SETUP0 + offset, setup); + hsmc_writel(hsmc, PULSE0 + offset, pulse); + hsmc_writel(hsmc, CYCLE0 + offset, cycle); + hsmc_writel(hsmc, MODE0 + offset, mode); + hsmc_readl(hsmc, MODE0); /* I/O barrier */ + + return 0; +} +EXPORT_SYMBOL(smc_set_configuration); + +static int hsmc_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct clk *pclk, *mck; + int ret; + + if (hsmc) + return -EBUSY; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) + return PTR_ERR(pclk); + mck = clk_get(&pdev->dev, "mck"); + if (IS_ERR(mck)) { + ret = PTR_ERR(mck); + goto out_put_pclk; + } + + ret = -ENOMEM; + hsmc = kzalloc(sizeof(struct hsmc), GFP_KERNEL); + if (!hsmc) + goto out_put_clocks; + + clk_enable(pclk); + clk_enable(mck); + + hsmc->pclk = pclk; + hsmc->mck = mck; + hsmc->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!hsmc->regs) + goto out_disable_clocks; + + dev_info(&pdev->dev, "Atmel Static Memory Controller at 0x%08lx\n", + (unsigned long)regs->start); + + platform_set_drvdata(pdev, hsmc); + + return 0; + +out_disable_clocks: + clk_disable(mck); + clk_disable(pclk); + kfree(hsmc); +out_put_clocks: + clk_put(mck); +out_put_pclk: + clk_put(pclk); + hsmc = NULL; + return ret; +} + +static struct platform_driver hsmc_driver = { + .probe = hsmc_probe, + .driver = { + .name = "smc", + }, +}; + +static int __init hsmc_init(void) +{ + return platform_driver_register(&hsmc_driver); +} +arch_initcall(hsmc_init); diff --git a/arch/avr32/mach-at32ap/hsmc.h b/arch/avr32/mach-at32ap/hsmc.h new file mode 100644 index 0000000000..5681276faf --- /dev/null +++ b/arch/avr32/mach-at32ap/hsmc.h @@ -0,0 +1,127 @@ +/* + * Register definitions for Atmel Static Memory Controller (SMC) + * + * Copyright (C) 2006 Atmel Corporation + * + * 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. + */ +#ifndef __ASM_AVR32_HSMC_H__ +#define __ASM_AVR32_HSMC_H__ + +/* HSMC register offsets */ +#define HSMC_SETUP0 0x0000 +#define HSMC_PULSE0 0x0004 +#define HSMC_CYCLE0 0x0008 +#define HSMC_MODE0 0x000c +#define HSMC_SETUP1 0x0010 +#define HSMC_PULSE1 0x0014 +#define HSMC_CYCLE1 0x0018 +#define HSMC_MODE1 0x001c +#define HSMC_SETUP2 0x0020 +#define HSMC_PULSE2 0x0024 +#define HSMC_CYCLE2 0x0028 +#define HSMC_MODE2 0x002c +#define HSMC_SETUP3 0x0030 +#define HSMC_PULSE3 0x0034 +#define HSMC_CYCLE3 0x0038 +#define HSMC_MODE3 0x003c +#define HSMC_SETUP4 0x0040 +#define HSMC_PULSE4 0x0044 +#define HSMC_CYCLE4 0x0048 +#define HSMC_MODE4 0x004c +#define HSMC_SETUP5 0x0050 +#define HSMC_PULSE5 0x0054 +#define HSMC_CYCLE5 0x0058 +#define HSMC_MODE5 0x005c + +/* Bitfields in SETUP0 */ +#define HSMC_NWE_SETUP_OFFSET 0 +#define HSMC_NWE_SETUP_SIZE 6 +#define HSMC_NCS_WR_SETUP_OFFSET 8 +#define HSMC_NCS_WR_SETUP_SIZE 6 +#define HSMC_NRD_SETUP_OFFSET 16 +#define HSMC_NRD_SETUP_SIZE 6 +#define HSMC_NCS_RD_SETUP_OFFSET 24 +#define HSMC_NCS_RD_SETUP_SIZE 6 + +/* Bitfields in PULSE0 */ +#define HSMC_NWE_PULSE_OFFSET 0 +#define HSMC_NWE_PULSE_SIZE 7 +#define HSMC_NCS_WR_PULSE_OFFSET 8 +#define HSMC_NCS_WR_PULSE_SIZE 7 +#define HSMC_NRD_PULSE_OFFSET 16 +#define HSMC_NRD_PULSE_SIZE 7 +#define HSMC_NCS_RD_PULSE_OFFSET 24 +#define HSMC_NCS_RD_PULSE_SIZE 7 + +/* Bitfields in CYCLE0 */ +#define HSMC_NWE_CYCLE_OFFSET 0 +#define HSMC_NWE_CYCLE_SIZE 9 +#define HSMC_NRD_CYCLE_OFFSET 16 +#define HSMC_NRD_CYCLE_SIZE 9 + +/* Bitfields in MODE0 */ +#define HSMC_READ_MODE_OFFSET 0 +#define HSMC_READ_MODE_SIZE 1 +#define HSMC_WRITE_MODE_OFFSET 1 +#define HSMC_WRITE_MODE_SIZE 1 +#define HSMC_EXNW_MODE_OFFSET 4 +#define HSMC_EXNW_MODE_SIZE 2 +#define HSMC_BAT_OFFSET 8 +#define HSMC_BAT_SIZE 1 +#define HSMC_DBW_OFFSET 12 +#define HSMC_DBW_SIZE 2 +#define HSMC_TDF_CYCLES_OFFSET 16 +#define HSMC_TDF_CYCLES_SIZE 4 +#define HSMC_TDF_MODE_OFFSET 20 +#define HSMC_TDF_MODE_SIZE 1 +#define HSMC_PMEN_OFFSET 24 +#define HSMC_PMEN_SIZE 1 +#define HSMC_PS_OFFSET 28 +#define HSMC_PS_SIZE 2 + +/* Constants for READ_MODE */ +#define HSMC_READ_MODE_NCS_CONTROLLED 0 +#define HSMC_READ_MODE_NRD_CONTROLLED 1 + +/* Constants for WRITE_MODE */ +#define HSMC_WRITE_MODE_NCS_CONTROLLED 0 +#define HSMC_WRITE_MODE_NWE_CONTROLLED 1 + +/* Constants for EXNW_MODE */ +#define HSMC_EXNW_MODE_DISABLED 0 +#define HSMC_EXNW_MODE_RESERVED 1 +#define HSMC_EXNW_MODE_FROZEN 2 +#define HSMC_EXNW_MODE_READY 3 + +/* Constants for BAT */ +#define HSMC_BAT_BYTE_SELECT 0 +#define HSMC_BAT_BYTE_WRITE 1 + +/* Constants for DBW */ +#define HSMC_DBW_8_BITS 0 +#define HSMC_DBW_16_BITS 1 +#define HSMC_DBW_32_BITS 2 + +/* Bit manipulation macros */ +#define HSMC_BIT(name) \ + (1 << HSMC_##name##_OFFSET) +#define HSMC_BF(name,value) \ + (((value) & ((1 << HSMC_##name##_SIZE) - 1)) \ + << HSMC_##name##_OFFSET) +#define HSMC_BFEXT(name,value) \ + (((value) >> HSMC_##name##_OFFSET) \ + & ((1 << HSMC_##name##_SIZE) - 1)) +#define HSMC_BFINS(name,value,old) \ + (((old) & ~(((1 << HSMC_##name##_SIZE) - 1) \ + << HSMC_##name##_OFFSET)) | HSMC_BF(name,value)) + +/* Register access macros */ +#define hsmc_readl(port,reg) \ + readl((port)->regs + HSMC_##reg) +#define hsmc_writel(port,reg,value) \ + writel((value), (port)->regs + HSMC_##reg) + +#endif /* __ASM_AVR32_HSMC_H__ */ diff --git a/include/asm-avr32/arch-at32ap/smc.h b/include/asm-avr32/arch-at32ap/smc.h new file mode 100644 index 0000000000..3732b32830 --- /dev/null +++ b/include/asm-avr32/arch-at32ap/smc.h @@ -0,0 +1,60 @@ +/* + * Static Memory Controller for AT32 chips + * + * Copyright (C) 2006 Atmel Corporation + * + * Inspired by the OMAP2 General-Purpose Memory Controller interface + * + * 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. + */ +#ifndef __ARCH_AT32AP_SMC_H +#define __ARCH_AT32AP_SMC_H + +/* + * All timing parameters are in nanoseconds. + */ +struct smc_config { + /* Delay from address valid to assertion of given strobe */ + u16 ncs_read_setup; + u16 nrd_setup; + u16 ncs_write_setup; + u16 nwe_setup; + + /* Pulse length of given strobe */ + u16 ncs_read_pulse; + u16 nrd_pulse; + u16 ncs_write_pulse; + u16 nwe_pulse; + + /* Total cycle length of given operation */ + u16 read_cycle; + u16 write_cycle; + + /* Bus width in bytes */ + u8 bus_width; + + /* + * 0: Data is sampled on rising edge of NCS + * 1: Data is sampled on rising edge of NRD + */ + unsigned int nrd_controlled:1; + + /* + * 0: Data is driven on falling edge of NCS + * 1: Data is driven on falling edge of NWR + */ + unsigned int nwe_controlled:1; + + /* + * 0: Byte select access type + * 1: Byte write access type + */ + unsigned int byte_write:1; +}; + +extern int smc_set_configuration(int cs, const struct smc_config *config); +extern struct smc_config *smc_get_configuration(int cs); + +#endif /* __ARCH_AT32AP_SMC_H */