diff options
47 files changed, 2439 insertions, 36 deletions
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi new file mode 100644 index 000000000000..cb84de791b5a --- /dev/null +++ b/arch/arm/boot/dts/at91sam9n12.dtsi @@ -0,0 +1,221 @@ +/* + * at91sam9n12.dtsi - Device Tree include file for AT91SAM9N12 SoC + * + * Copyright (C) 2012 Atmel, + * 2012 Hong Xu <hong.xu@atmel.com> + * + * Licensed under GPLv2 or later. + */ + +/include/ "skeleton.dtsi" + +/ { + model = "Atmel AT91SAM9N12 SoC"; + compatible = "atmel,at91sam9n12"; + interrupt-parent = <&aic>; + + aliases { + serial0 = &dbgu; + serial1 = &usart0; + serial2 = &usart1; + serial3 = &usart2; + serial4 = &usart3; + gpio0 = &pioA; + gpio1 = &pioB; + gpio2 = &pioC; + gpio3 = &pioD; + tcb0 = &tcb0; + tcb1 = &tcb1; + }; + cpus { + cpu@0 { + compatible = "arm,arm926ejs"; + }; + }; + + memory { + reg = <0x20000000 0x10000000>; + }; + + ahb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + apb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + aic: interrupt-controller@fffff000 { + #interrupt-cells = <2>; + compatible = "atmel,at91rm9200-aic"; + interrupt-controller; + reg = <0xfffff000 0x200>; + }; + + ramc0: ramc@ffffe800 { + compatible = "atmel,at91sam9g45-ddramc"; + reg = <0xffffe800 0x200>; + }; + + pmc: pmc@fffffc00 { + compatible = "atmel,at91rm9200-pmc"; + reg = <0xfffffc00 0x100>; + }; + + rstc@fffffe00 { + compatible = "atmel,at91sam9g45-rstc"; + reg = <0xfffffe00 0x10>; + }; + + pit: timer@fffffe30 { + compatible = "atmel,at91sam9260-pit"; + reg = <0xfffffe30 0xf>; + interrupts = <1 4>; + }; + + shdwc@fffffe10 { + compatible = "atmel,at91sam9x5-shdwc"; + reg = <0xfffffe10 0x10>; + }; + + tcb0: timer@f8008000 { + compatible = "atmel,at91sam9x5-tcb"; + reg = <0xf8008000 0x100>; + interrupts = <17 4>; + }; + + tcb1: timer@f800c000 { + compatible = "atmel,at91sam9x5-tcb"; + reg = <0xf800c000 0x100>; + interrupts = <17 4>; + }; + + dma: dma-controller@ffffec00 { + compatible = "atmel,at91sam9g45-dma"; + reg = <0xffffec00 0x200>; + interrupts = <20 4>; + }; + + pioA: gpio@fffff400 { + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffff400 0x100>; + interrupts = <2 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioB: gpio@fffff600 { + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffff600 0x100>; + interrupts = <2 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioC: gpio@fffff800 { + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffff800 0x100>; + interrupts = <3 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioD: gpio@fffffa00 { + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffffa00 0x100>; + interrupts = <3 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + dbgu: serial@fffff200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xfffff200 0x200>; + interrupts = <1 4>; + status = "disabled"; + }; + + usart0: serial@f801c000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xf801c000 0x4000>; + interrupts = <5 4>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + + usart1: serial@f8020000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xf8020000 0x4000>; + interrupts = <6 4>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + + usart2: serial@f8024000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xf8024000 0x4000>; + interrupts = <7 4>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + + usart3: serial@f8028000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xf8028000 0x4000>; + interrupts = <8 4>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + }; + + nand0: nand@40000000 { + compatible = "atmel,at91rm9200-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = < 0x40000000 0x10000000 + 0xffffe000 0x00000600 + 0xffffe600 0x00000200 + 0x00100000 0x00100000 + >; + atmel,nand-addr-offset = <21>; + atmel,nand-cmd-offset = <22>; + gpios = <&pioD 5 0 + &pioD 4 0 + 0 + >; + status = "disabled"; + }; + + usb0: ohci@00500000 { + compatible = "atmel,at91rm9200-ohci", "usb-ohci"; + reg = <0x00500000 0x00100000>; + interrupts = <22 4>; + status = "disabled"; + }; + }; + + i2c@0 { + compatible = "i2c-gpio"; + gpios = <&pioA 30 0 /* sda */ + &pioA 31 0 /* scl */ + >; + i2c-gpio,sda-open-drain; + i2c-gpio,scl-open-drain; + i2c-gpio,delay-us = <2>; /* ~100 kHz */ + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; +}; diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts new file mode 100644 index 000000000000..f4e43e38f3a1 --- /dev/null +++ b/arch/arm/boot/dts/at91sam9n12ek.dts @@ -0,0 +1,84 @@ +/* + * at91sam9n12ek.dts - Device Tree file for AT91SAM9N12-EK board + * + * Copyright (C) 2012 Atmel, + * 2012 Hong Xu <hong.xu@atmel.com> + * + * Licensed under GPLv2 or later. + */ +/dts-v1/; +/include/ "at91sam9n12.dtsi" + +/ { + model = "Atmel AT91SAM9N12-EK"; + compatible = "atmel,at91sam9n12ek", "atmel,at91sam9n12", "atmel,at91sam9"; + + chosen { + bootargs = "mem=128M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; + }; + + memory { + reg = <0x20000000 0x10000000>; + }; + + clocks { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + main_clock: clock@0 { + compatible = "atmel,osc", "fixed-clock"; + clock-frequency = <16000000>; + }; + }; + + ahb { + apb { + dbgu: serial@fffff200 { + status = "okay"; + }; + }; + + nand0: nand@40000000 { + nand-bus-width = <8>; + nand-ecc-mode = "soft"; + nand-on-flash-bbt; + status = "okay"; + }; + }; + + leds { + compatible = "gpio-leds"; + + d8 { + label = "d8"; + gpios = <&pioB 4 1>; + linux,default-trigger = "mmc0"; + }; + + d9 { + label = "d6"; + gpios = <&pioB 5 1>; + linux,default-trigger = "nand-disk"; + }; + + d10 { + label = "d7"; + gpios = <&pioB 6 0>; + linux,default-trigger = "heartbeat"; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + + enter { + label = "Enter"; + gpios = <&pioB 4 1>; + linux,code = <28>; + gpio-key,wakeup; + }; + }; +}; diff --git a/arch/arm/boot/dts/emev2-kzm9d.dts b/arch/arm/boot/dts/emev2-kzm9d.dts new file mode 100644 index 000000000000..297e3baba71c --- /dev/null +++ b/arch/arm/boot/dts/emev2-kzm9d.dts @@ -0,0 +1,26 @@ +/* + * Device Tree Source for the KZM9D board + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ +/dts-v1/; + +/include/ "emev2.dtsi" + +/ { + model = "EMEV2 KZM9D Board"; + compatible = "renesas,kzm9d", "renesas,emev2"; + + memory { + device_type = "memory"; + reg = <0x40000000 0x8000000>; + }; + + chosen { + bootargs = "console=ttyS1,115200n81"; + }; +}; diff --git a/arch/arm/boot/dts/emev2.dtsi b/arch/arm/boot/dts/emev2.dtsi new file mode 100644 index 000000000000..eb504a6c0f4a --- /dev/null +++ b/arch/arm/boot/dts/emev2.dtsi @@ -0,0 +1,63 @@ +/* + * Device Tree Source for the EMEV2 SoC + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/include/ "skeleton.dtsi" + +/ { + compatible = "renesas,emev2"; + interrupt-parent = <&gic>; + + cpus { + cpu@0 { + compatible = "arm,cortex-a9"; + }; + cpu@1 { + compatible = "arm,cortex-a9"; + }; + }; + + gic: interrupt-controller@e0020000 { + compatible = "arm,cortex-a9-gic"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0xe0028000 0x1000>, + <0xe0020000 0x0100>; + }; + + sti@e0180000 { + compatible = "renesas,em-sti"; + reg = <0xe0180000 0x54>; + interrupts = <0 125 0>; + }; + + uart@e1020000 { + compatible = "renesas,em-uart"; + reg = <0xe1020000 0x38>; + interrupts = <0 8 0>; + }; + + uart@e1030000 { + compatible = "renesas,em-uart"; + reg = <0xe1030000 0x38>; + interrupts = <0 9 0>; + }; + + uart@e1040000 { + compatible = "renesas,em-uart"; + reg = <0xe1040000 0x38>; + interrupts = <0 10 0>; + }; + + uart@e1050000 { + compatible = "renesas,em-uart"; + reg = <0xe1050000 0x38>; + interrupts = <0 11 0>; + }; +}; diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 98a42f3472d5..19505c0a3f01 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -91,6 +91,14 @@ config SOC_AT91SAM9X5 This support covers AT91SAM9G15, AT91SAM9G25, AT91SAM9X25, AT91SAM9G35 and AT91SAM9X35. +config SOC_AT91SAM9N12 + bool "AT91SAM9N12 family" + select SOC_AT91SAM9 + select HAVE_AT91_DBGU0 + select HAVE_FB_ATMEL + help + Select this if you are using Atmel's AT91SAM9N12 SoC. + choice prompt "Atmel AT91 Processor Devices for non DT boards" diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 79d0f60af0b2..3bb7a51efc9d 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SOC_AT91SAM9260) += at91sam9260.o obj-$(CONFIG_SOC_AT91SAM9261) += at91sam9261.o obj-$(CONFIG_SOC_AT91SAM9263) += at91sam9263.o obj-$(CONFIG_SOC_AT91SAM9G45) += at91sam9g45.o +obj-$(CONFIG_SOC_AT91SAM9N12) += at91sam9n12.o obj-$(CONFIG_SOC_AT91SAM9X5) += at91sam9x5.o obj-$(CONFIG_SOC_AT91SAM9RL) += at91sam9rl.o diff --git a/arch/arm/mach-at91/Makefile.boot b/arch/arm/mach-at91/Makefile.boot index c03417ddbf0c..9e84fe4f2aaa 100644 --- a/arch/arm/mach-at91/Makefile.boot +++ b/arch/arm/mach-at91/Makefile.boot @@ -30,5 +30,7 @@ dtb-$(CONFIG_MACH_AT91SAM_DT) += tny_a9g20.dtb dtb-$(CONFIG_MACH_AT91SAM_DT) += usb_a9g20.dtb # sam9g45 dtb-$(CONFIG_MACH_AT91SAM_DT) += at91sam9m10g45ek.dtb +# sam9n12 +dtb-$(CONFIG_MACH_AT91SAM_DT) += at91sam9n12ek.dtb # sam9x5 dtb-$(CONFIG_MACH_AT91SAM_DT) += at91sam9g25ek.dtb diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c new file mode 100644 index 000000000000..08494664ab78 --- /dev/null +++ b/arch/arm/mach-at91/at91sam9n12.c @@ -0,0 +1,233 @@ +/* + * SoC specific setup code for the AT91SAM9N12 + * + * Copyright (C) 2012 Atmel Corporation. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> + +#include <asm/irq.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <mach/at91sam9n12.h> +#include <mach/at91_pmc.h> +#include <mach/cpu.h> +#include <mach/board.h> + +#include "soc.h" +#include "generic.h" +#include "clock.h" +#include "sam9_smc.h" + +/* -------------------------------------------------------------------- + * Clocks + * -------------------------------------------------------------------- */ + +/* + * The peripheral clocks. + */ +static struct clk pioAB_clk = { + .name = "pioAB_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_PIOAB, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk pioCD_clk = { + .name = "pioCD_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_PIOCD, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart0_clk = { + .name = "usart0_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_USART0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart1_clk = { + .name = "usart1_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_USART1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart2_clk = { + .name = "usart2_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_USART2, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart3_clk = { + .name = "usart3_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_USART3, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk twi0_clk = { + .name = "twi0_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_TWI0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk twi1_clk = { + .name = "twi1_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_TWI1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk mmc_clk = { + .name = "mci_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_MCI, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk spi0_clk = { + .name = "spi0_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_SPI0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk spi1_clk = { + .name = "spi1_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_SPI1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk uart0_clk = { + .name = "uart0_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_UART0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk uart1_clk = { + .name = "uart1_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_UART1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tcb_clk = { + .name = "tcb_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_TCB, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk pwm_clk = { + .name = "pwm_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_PWM, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk adc_clk = { + .name = "adc_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_ADC, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk dma_clk = { + .name = "dma_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_DMA, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk uhp_clk = { + .name = "uhp", + .pmc_mask = 1 << AT91SAM9N12_ID_UHP, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk udp_clk = { + .name = "udp_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_UDP, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk lcdc_clk = { + .name = "lcdc_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_LCDC, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk ssc_clk = { + .name = "ssc_clk", + .pmc_mask = 1 << AT91SAM9N12_ID_SSC, + .type = CLK_TYPE_PERIPHERAL, +}; + +static struct clk *periph_clocks[] __initdata = { + &pioAB_clk, + &pioCD_clk, + &usart0_clk, + &usart1_clk, + &usart2_clk, + &usart3_clk, + &twi0_clk, + &twi1_clk, + &mmc_clk, + &spi0_clk, + &spi1_clk, + &lcdc_clk, + &uart0_clk, + &uart1_clk, + &tcb_clk, + &pwm_clk, + &adc_clk, + &dma_clk, + &uhp_clk, + &udp_clk, + &ssc_clk, +}; + +static struct clk_lookup periph_clocks_lookups[] = { + /* lookup table for DT entries */ + CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck), + CLKDEV_CON_DEV_ID("usart", "f801c000.serial", &usart0_clk), + CLKDEV_CON_DEV_ID("usart", "f8020000.serial", &usart1_clk), + CLKDEV_CON_DEV_ID("usart", "f8024000.serial", &usart2_clk), + CLKDEV_CON_DEV_ID("usart", "f8028000.serial", &usart3_clk), + CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb_clk), + CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb_clk), + CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma_clk), + CLKDEV_CON_ID("pioA", &pioAB_clk), + CLKDEV_CON_ID("pioB", &pioAB_clk), + CLKDEV_CON_ID("pioC", &pioCD_clk), + CLKDEV_CON_ID("pioD", &pioCD_clk), + /* additional fake clock for macb_hclk */ + CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &uhp_clk), + CLKDEV_CON_DEV_ID("ohci_clk", "500000.ohci", &uhp_clk), +}; + +/* + * The two programmable clocks. + * You must configure pin multiplexing to bring these signals out. + */ +static struct clk pck0 = { + .name = "pck0", + .pmc_mask = AT91_PMC_PCK0, + .type = CLK_TYPE_PROGRAMMABLE, + .id = 0, +}; +static struct clk pck1 = { + .name = "pck1", + .pmc_mask = AT91_PMC_PCK1, + .type = CLK_TYPE_PROGRAMMABLE, + .id = 1, +}; + +static void __init at91sam9n12_register_clocks(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(periph_clocks); i++) + clk_register(periph_clocks[i]); + clk_register(&pck0); + clk_register(&pck1); + + clkdev_add_table(periph_clocks_lookups, + ARRAY_SIZE(periph_clocks_lookups)); + +} + +/* -------------------------------------------------------------------- + * AT91SAM9N12 processor initialization + * -------------------------------------------------------------------- */ + +static void __init at91sam9n12_map_io(void) +{ + at91_init_sram(0, AT91SAM9N12_SRAM_BASE, AT91SAM9N12_SRAM_SIZE); +} + +void __init at91sam9n12_initialize(void) +{ + at91_extern_irq = (1 << AT91SAM9N12_ID_IRQ0); + + /* Register GPIO subsystem (using DT) */ + at91_gpio_init(NULL, 0); +} + +struct at91_init_soc __initdata at91sam9n12_soc = { + .map_io = at91sam9n12_map_io, + .register_clocks = at91sam9n12_register_clocks, + .init = at91sam9n12_initialize, +}; diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c index 6b692824c988..de2ec6b8fea7 100644 --- a/arch/arm/mach-at91/clock.c +++ b/arch/arm/mach-at91/clock.c @@ -58,13 +58,15 @@ EXPORT_SYMBOL_GPL(at91_pmc_base); #define cpu_has_800M_plla() ( cpu_is_at91sam9g20() \ || cpu_is_at91sam9g45() \ - || cpu_is_at91sam9x5()) + || cpu_is_at91sam9x5() \ + || cpu_is_at91sam9n12()) #define cpu_has_300M_plla() (cpu_is_at91sam9g10()) #define cpu_has_pllb() (!(cpu_is_at91sam9rl() \ || cpu_is_at91sam9g45() \ - || cpu_is_at91sam9x5())) + || cpu_is_at91sam9x5() \ + || cpu_is_at91sam9n12())) #define cpu_has_upll() (cpu_is_at91sam9g45() \ || cpu_is_at91sam9x5()) @@ -78,12 +80,15 @@ EXPORT_SYMBOL_GPL(at91_pmc_base); || cpu_is_at91sam9x5())) #define cpu_has_plladiv2() (cpu_is_at91sam9g45() \ - || cpu_is_at91sam9x5()) + || cpu_is_at91sam9x5() \ + || cpu_is_at91sam9n12()) #define cpu_has_mdiv3() (cpu_is_at91sam9g45() \ - || cpu_is_at91sam9x5()) + || cpu_is_at91sam9x5() \ + || cpu_is_at91sam9n12()) -#define cpu_has_alt_prescaler() (cpu_is_at91sam9x5()) +#define cpu_has_alt_prescaler() (cpu_is_at91sam9x5() \ + || cpu_is_at91sam9n12()) static LIST_HEAD(clocks); static DEFINE_SPINLOCK(clk_lock); diff --git a/arch/arm/mach-at91/include/mach/at91sam9n12.h b/arch/arm/mach-at91/include/mach/at91sam9n12.h new file mode 100644 index 000000000000..d374b87c0459 --- /dev/null +++ b/arch/arm/mach-at91/include/mach/at91sam9n12.h @@ -0,0 +1,60 @@ +/* + * SoC specific header file for the AT91SAM9N12 + * + * Copyright (C) 2012 Atmel Corporation + * + * Common definitions, based on AT91SAM9N12 SoC datasheet + * + * Licensed under GPLv2 or later + */ + +#ifndef _AT91SAM9N12_H_ +#define _AT91SAM9N12_H_ + +/* + * Peripheral identifiers/interrupts. + */ +#define AT91SAM9N12_ID_PIOAB 2 /* Parallel I/O Controller A and B */ +#define AT91SAM9N12_ID_PIOCD 3 /* Parallel I/O Controller C and D */ +#define AT91SAM9N12_ID_FUSE 4 /* FUSE Controller */ +#define AT91SAM9N12_ID_USART0 5 /* USART 0 */ +#define AT91SAM9N12_ID_USART1 6 /* USART 1 */ +#define AT91SAM9N12_ID_USART2 7 /* USART 2 */ +#define AT91SAM9N12_ID_USART3 8 /* USART 3 */ +#define AT91SAM9N12_ID_TWI0 9 /* Two-Wire Interface 0 */ +#define AT91SAM9N12_ID_TWI1 10 /* Two-Wire Interface 1 */ +#define AT91SAM9N12_ID_MCI 12 /* High Speed Multimedia Card Interface */ +#define AT91SAM9N12_ID_SPI0 13 /* Serial Peripheral Interface 0 */ +#define AT91SAM9N12_ID_SPI1 14 /* Serial Peripheral Interface 1 */ +#define AT91SAM9N12_ID_UART0 15 /* UART 0 */ +#define AT91SAM9N12_ID_UART1 16 /* UART 1 */ +#define AT91SAM9N12_ID_TCB 17 /* Timer Counter 0, 1, 2, 3, 4 and 5 */ +#define AT91SAM9N12_ID_PWM 18 /* Pulse Width Modulation Controller */ +#define AT91SAM9N12_ID_ADC 19 /* ADC Controller */ +#define AT91SAM9N12_ID_DMA 20 /* DMA Controller */ +#define AT91SAM9N12_ID_UHP 22 /* USB Host High Speed */ +#define AT91SAM9N12_ID_UDP 23 /* USB Device High Speed */ +#define AT91SAM9N12_ID_LCDC 25 /* LCD Controller */ +#define AT91SAM9N12_ID_ISI 25 /* Image Sensor Interface */ +#define AT91SAM9N12_ID_SSC 28 /* Synchronous Serial Controller */ +#define AT91SAM9N12_ID_TRNG 30 /* TRNG */ +#define AT91SAM9N12_ID_IRQ0 31 /* Advanced Interrupt Controller */ + +/* + * User Peripheral physical base addresses. + */ +#define AT91SAM9N12_BASE_USART0 0xf801c000 +#define AT91SAM9N12_BASE_USART1 0xf8020000 +#define AT91SAM9N12_BASE_USART2 0xf8024000 +#define AT91SAM9N12_BASE_USART3 0xf8028000 + +/* + * Internal Memory. + */ +#define AT91SAM9N12_SRAM_BASE 0x00300000 /* Internal SRAM base address */ +#define AT91SAM9N12_SRAM_SIZE SZ_32K /* Internal SRAM size (32Kb) */ + +#define AT91SAM9N12_ROM_BASE 0x00100000 /* Internal ROM base address */ +#define AT91SAM9N12_ROM_SIZE SZ_128K /* Internal ROM size (128Kb) */ + +#endif diff --git a/arch/arm/mach-at91/include/mach/at91sam9n12_matrix.h b/arch/arm/mach-at91/include/mach/at91sam9n12_matrix.h new file mode 100644 index 000000000000..40060cd62fa9 --- /dev/null +++ b/arch/arm/mach-at91/include/mach/at91sam9n12_matrix.h @@ -0,0 +1,53 @@ +/* + * Matrix-centric header file for the AT91SAM9N12 + * + * Copyright (C) 2012 Atmel Corporation. + * + * Only EBI related registers. + * Write Protect register definitions may be useful. + * + * Licensed under GPLv2 or later. + */ + +#ifndef _AT91SAM9N12_MATRIX_H_ +#define _AT91SAM9N12_MATRIX_H_ + +#define AT91_MATRIX_EBICSA (AT91_MATRIX + 0x118) /* EBI Chip Select Assignment Register */ +#define AT91_MATRIX_EBI_CS1A (1 << 1) /* Chip Select 1 Assignment */ +#define AT91_MATRIX_EBI_CS1A_SMC (0 << 1) +#define AT91_MATRIX_EBI_CS1A_SDRAMC (1 << 1) +#define AT91_MATRIX_EBI_CS3A (1 << 3) /* Chip Select 3 Assignment */ +#define AT91_MATRIX_EBI_CS3A_SMC (0 << 3) +#define AT91_MATRIX_EBI_CS3A_SMC_NANDFLASH (1 << 3) +#define AT91_MATRIX_EBI_DBPUC (1 << 8) /* Data Bus Pull-up Configuration */ +#define AT91_MATRIX_EBI_DBPU_ON (0 << 8) +#define AT91_MATRIX_EBI_DBPU_OFF (1 << 8) +#define AT91_MATRIX_EBI_VDDIOMSEL (1 << 16) /* Memory voltage selection */ +#define AT91_MATRIX_EBI_VDDIOMSEL_1_8V (0 << 16) +#define AT91_MATRIX_EBI_VDDIOMSEL_3_3V (1 << 16) +#define AT91_MATRIX_EBI_EBI_IOSR (1 << 17) /* EBI I/O slew rate selection */ +#define AT91_MATRIX_EBI_EBI_IOSR_REDUCED (0 << 17) +#define AT91_MATRIX_EBI_EBI_IOSR_NORMAL (1 << 17) +#define AT91_MATRIX_EBI_DDR_IOSR (1 << 18) /* DDR2 dedicated port I/O slew rate selection */ +#define AT91_MATRIX_EBI_DDR_IOSR_REDUCED (0 << 18) +#define AT91_MATRIX_EBI_DDR_IOSR_NORMAL (1 << 18) +#define AT91_MATRIX_NFD0_SELECT (1 << 24) /* NAND Flash Data Bus Selection */ +#define AT91_MATRIX_NFD0_ON_D0 (0 << 24) +#define AT91_MATRIX_NFD0_ON_D16 (1 << 24) +#define AT91_MATRIX_DDR_MP_EN (1 << 25) /* DDR Multi-port Enable */ +#define AT91_MATRIX_MP_OFF (0 << 25) +#define AT91_MATRIX_MP_ON (1 << 25) + +#define AT91_MATRIX_WPMR (AT91_MATRIX + 0x1E4) /* Write Protect Mode Register */ +#define AT91_MATRIX_WPMR_WPEN (1 << 0) /* Write Protect ENable */ +#define AT91_MATRIX_WPMR_WP_WPDIS (0 << 0) +#define AT91_MATRIX_WPMR_WP_WPEN (1 << 0) +#define AT91_MATRIX_WPMR_WPKEY (0xFFFFFF << 8) /* Write Protect KEY */ + +#define AT91_MATRIX_WPSR (AT91_MATRIX + 0x1E8) /* Write Protect Status Register */ +#define AT91_MATRIX_WPSR_WPVS (1 << 0) /* Write Protect Violation Status */ +#define AT91_MATRIX_WPSR_NO_WPV (0 << 0) +#define AT91_MATRIX_WPSR_WPV (1 << 0) +#define AT91_MATRIX_WPSR_WPVSRC (0xFFFF << 8) /* Write Protect Violation Source */ + +#endif diff --git a/arch/arm/mach-at91/include/mach/cpu.h b/arch/arm/mach-at91/include/mach/cpu.h index 73d2fd209ce4..b6504c19d55c 100644 --- a/arch/arm/mach-at91/include/mach/cpu.h +++ b/arch/arm/mach-at91/include/mach/cpu.h @@ -25,6 +25,7 @@ #define ARCH_ID_AT91SAM9G45MRL 0x819b05a2 /* aka 9G45-ES2 & non ES lots */ #define ARCH_ID_AT91SAM9G45ES 0x819b05a1 /* 9G45-ES (Engineering Sample) */ #define ARCH_ID_AT91SAM9X5 0x819a05a0 +#define ARCH_ID_AT91SAM9N12 0x819a07a0 #define ARCH_ID_AT91SAM9XE128 0x329973a0 #define ARCH_ID_AT91SAM9XE256 0x329a93a0 @@ -71,6 +72,9 @@ enum at91_soc_type { /* SAM9X5 */ AT91_SOC_SAM9X5, + /* SAM9N12 */ + AT91_SOC_SAM9N12, + /* Unknown type */ AT91_SOC_NONE }; @@ -177,6 +181,12 @@ static inline int at91_soc_is_detected(void) #define cpu_is_at91sam9x25() (0) #endif +#ifdef CONFIG_SOC_AT91SAM9N12 +#define cpu_is_at91sam9n12() (at91_soc_initdata.type == AT91_SOC_SAM9N12) +#else +#define cpu_is_at91sam9n12() (0) +#endif + /* * Since this is ARM, we will never run on any AVR32 CPU. But these * definitions may reduce clutter in common drivers. diff --git a/arch/arm/mach-at91/include/mach/hardware.h b/arch/arm/mach-at91/include/mach/hardware.h index ef5786299c60..09242b67d277 100644 --- a/arch/arm/mach-at91/include/mach/hardware.h +++ b/arch/arm/mach-at91/include/mach/hardware.h @@ -32,6 +32,7 @@ #include <mach/at91sam9rl.h> #include <mach/at91sam9g45.h> #include <mach/at91sam9x5.h> +#include <mach/at91sam9n12.h> /* * On all at91 except rm9200 and x40 have the System Controller starts diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index f44a2e7272e3..944bffb08991 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -143,6 +143,11 @@ static void __init soc_detect(u32 dbgu_base) at91_soc_initdata.type = AT91_SOC_SAM9X5; at91_boot_soc = at91sam9x5_soc; break; + + case ARCH_ID_AT91SAM9N12: + at91_soc_initdata.type = AT91_SOC_SAM9N12; + at91_boot_soc = at91sam9n12_soc; + break; } /* at91sam9g10 */ @@ -210,6 +215,7 @@ static const char *soc_name[] = { [AT91_SOC_SAM9G45] = "at91sam9g45", [AT91_SOC_SAM9RL] = "at91sam9rl", [AT91_SOC_SAM9X5] = "at91sam9x5", + [AT91_SOC_SAM9N12] = "at91sam9n12", [AT91_SOC_NONE] = "Unknown" }; diff --git a/arch/arm/mach-at91/soc.h b/arch/arm/mach-at91/soc.h index 683dddfd8b13..a9cfeb153719 100644 --- a/arch/arm/mach-at91/soc.h +++ b/arch/arm/mach-at91/soc.h @@ -20,6 +20,7 @@ extern struct at91_init_soc at91sam9263_soc; extern struct at91_init_soc at91sam9g45_soc; extern struct at91_init_soc at91sam9rl_soc; extern struct at91_init_soc at91sam9x5_soc; +extern struct at91_init_soc at91sam9n12_soc; static inline int at91_soc_is_enabled(void) { @@ -53,3 +54,7 @@ static inline int at91_soc_is_enabled(void) #if !defined(CONFIG_SOC_AT91SAM9X5) #define at91sam9x5_soc at91_boot_soc #endif + +#if !defined(CONFIG_SOC_AT91SAM9N12) +#define at91sam9n12_soc at91_boot_soc +#endif diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 34560cab45d9..7dcf08ee979d 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -41,6 +41,12 @@ config ARCH_R8A7779 select ARM_GIC select ARCH_WANT_OPTIONAL_GPIOLIB +config ARCH_EMEV2 + bool "Emma Mobile EV2" + select CPU_V7 + select ARM_GIC + select ARCH_WANT_OPTIONAL_GPIOLIB + comment "SH-Mobile Board Type" config MACH_G3EVM @@ -98,6 +104,11 @@ config MACH_MARZEN depends on ARCH_R8A7779 select ARCH_REQUIRE_GPIOLIB +config MACH_KZM9D + bool "KZM9D board" + depends on ARCH_EMEV2 + select USE_OF + comment "SH-Mobile System Configuration" config CPU_HAS_INTEVT diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index e7c2590b75d9..c795335931c9 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -12,12 +12,14 @@ obj-$(CONFIG_ARCH_SH7372) += setup-sh7372.o clock-sh7372.o intc-sh7372.o obj-$(CONFIG_ARCH_SH73A0) += setup-sh73a0.o clock-sh73a0.o intc-sh73a0.o obj-$(CONFIG_ARCH_R8A7740) += setup-r8a7740.o clock-r8a7740.o intc-r8a7740.o obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o clock-r8a7779.o intc-r8a7779.o +obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o clock-emev2.o # SMP objects smp-y := platsmp.o headsmp.o smp-$(CONFIG_HOTPLUG_CPU) += hotplug.o smp-$(CONFIG_ARCH_SH73A0) += smp-sh73a0.o smp-$(CONFIG_ARCH_R8A7779) += smp-r8a7779.o +smp-$(CONFIG_ARCH_EMEV2) += smp-emev2.o # Pinmux setup pfc-y := @@ -49,6 +51,7 @@ obj-$(CONFIG_MACH_MACKEREL) += board-mackerel.o obj-$(CONFIG_MACH_KOTA2) += board-kota2.o obj-$(CONFIG_MACH_BONITO) += board-bonito.o obj-$(CONFIG_MACH_MARZEN) += board-marzen.o +obj-$(CONFIG_MACH_KZM9D) += board-kzm9d.o # Framework support obj-$(CONFIG_SMP) += $(smp-y) diff --git a/arch/arm/mach-shmobile/board-kzm9d.c b/arch/arm/mach-shmobile/board-kzm9d.c new file mode 100644 index 000000000000..7bc5e7d39f9b --- /dev/null +++ b/arch/arm/mach-shmobile/board-kzm9d.c @@ -0,0 +1,85 @@ +/* + * kzm9d board support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Magnus Damm + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/smsc911x.h> +#include <mach/common.h> +#include <mach/emev2.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/hardware/gic.h> + +/* Ether */ +static struct resource smsc911x_resources[] = { + [0] = { + .start = 0x20000000, + .end = 0x2000ffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = EMEV2_GPIO_IRQ(1), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH, + }, +}; + +static struct smsc911x_platform_config smsc911x_platdata = { + .flags = SMSC911X_USE_32BIT, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, +}; + +static struct platform_device smsc91x_device = { + .name = "smsc911x", + .id = 0, + .dev = { + .platform_data = &smsc911x_platdata, + }, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, +}; + +static struct platform_device *kzm9d_devices[] __initdata = { + &smsc91x_device, +}; + +void __init kzm9d_add_standard_devices(void) +{ + emev2_add_standard_devices(); + + platform_add_devices(kzm9d_devices, ARRAY_SIZE(kzm9d_devices)); +} + +static const char *kzm9d_boards_compat_dt[] __initdata = { + "renesas,kzm9d", + NULL, +}; + +DT_MACHINE_START(KZM9D_DT, "kzm9d") + .map_io = emev2_map_io, + .init_early = emev2_add_early_devices, + .nr_irqs = NR_IRQS_LEGACY, + .init_irq = emev2_init_irq, + .handle_irq = gic_handle_irq, + .init_machine = kzm9d_add_standard_devices, + .timer = &shmobile_timer, + .dt_compat = kzm9d_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/clock-emev2.c b/arch/arm/mach-shmobile/clock-emev2.c new file mode 100644 index 000000000000..4710f1847bb7 --- /dev/null +++ b/arch/arm/mach-shmobile/clock-emev2.c @@ -0,0 +1,249 @@ +/* + * Emma Mobile EV2 clock framework support + * + * Copyright (C) 2012 Magnus Damm + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/sh_clk.h> +#include <linux/clkdev.h> +#include <mach/common.h> + +#define EMEV2_SMU_BASE 0xe0110000 + +/* EMEV2 SMU registers */ +#define USIAU0_RSTCTRL 0x094 +#define USIBU1_RSTCTRL 0x0ac +#define USIBU2_RSTCTRL 0x0b0 +#define USIBU3_RSTCTRL 0x0b4 +#define STI_RSTCTRL 0x124 +#define USIAU0GCLKCTRL 0x4a0 +#define USIBU1GCLKCTRL 0x4b8 +#define USIBU2GCLKCTRL 0x4bc +#define USIBU3GCLKCTRL 0x04c0 +#define STIGCLKCTRL 0x528 +#define USIAU0SCLKDIV 0x61c +#define USIB2SCLKDIV 0x65c +#define USIB3SCLKDIV 0x660 +#define STI_CLKSEL 0x688 +#define SMU_GENERAL_REG0 0x7c0 + +/* not pretty, but hey */ +static void __iomem *smu_base; + +static void emev2_smu_write(unsigned long value, int offs) +{ + BUG_ON(!smu_base || (offs >= PAGE_SIZE)); + iowrite32(value, smu_base + offs); +} + +void emev2_set_boot_vector(unsigned long value) +{ + emev2_smu_write(value, SMU_GENERAL_REG0); +} + +static struct clk_mapping smu_mapping = { + .phys = EMEV2_SMU_BASE, + .len = PAGE_SIZE, +}; + +/* Fixed 32 KHz root clock from C32K pin */ +static struct clk c32k_clk = { + .rate = 32768, + .mapping = &smu_mapping, +}; + +/* PLL3 multiplies C32K with 7000 */ +static unsigned long pll3_recalc(struct clk *clk) +{ + return clk->parent->rate * 7000; +} + +static struct sh_clk_ops pll3_clk_ops = { + .recalc = pll3_recalc, +}; + +static struct clk pll3_clk = { + .ops = &pll3_clk_ops, + .parent = &c32k_clk, +}; + +static struct clk *main_clks[] = { + &c32k_clk, + &pll3_clk, +}; + +enum { SCLKDIV_USIAU0, SCLKDIV_USIBU2, SCLKDIV_USIBU1, SCLKDIV_USIBU3, + SCLKDIV_NR }; + +#define SCLKDIV(_reg, _shift) \ +{ \ + .parent = &pll3_clk, \ + .enable_reg = IOMEM(EMEV2_SMU_BASE + (_reg)), \ + .enable_bit = _shift, \ +} + +static struct clk sclkdiv_clks[SCLKDIV_NR] = { + [SCLKDIV_USIAU0] = SCLKDIV(USIAU0SCLKDIV, 0), + [SCLKDIV_USIBU2] = SCLKDIV(USIB2SCLKDIV, 16), + [SCLKDIV_USIBU1] = SCLKDIV(USIB2SCLKDIV, 0), + [SCLKDIV_USIBU3] = SCLKDIV(USIB3SCLKDIV, 0), +}; + +enum { GCLK_USIAU0_SCLK, GCLK_USIBU1_SCLK, GCLK_USIBU2_SCLK, GCLK_USIBU3_SCLK, + GCLK_STI_SCLK, + GCLK_NR }; + +#define GCLK_SCLK(_parent, _reg) \ +{ \ + .parent = _parent, \ + .enable_reg = IOMEM(EMEV2_SMU_BASE + (_reg)), \ + .enable_bit = 1, /* SCLK_GCC */ \ +} + +static struct clk gclk_clks[GCLK_NR] = { + [GCLK_USIAU0_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIAU0], + USIAU0GCLKCTRL), + [GCLK_USIBU1_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU1], + USIBU1GCLKCTRL), + [GCLK_USIBU2_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU2], + USIBU2GCLKCTRL), + [GCLK_USIBU3_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU3], + USIBU3GCLKCTRL), + [GCLK_STI_SCLK] = GCLK_SCLK(&c32k_clk, STIGCLKCTRL), +}; + +static int emev2_gclk_enable(struct clk *clk) +{ + iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit), + clk->mapped_reg); + return 0; +} + +static void emev2_gclk_disable(struct clk *clk) +{ + iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit), + clk->mapped_reg); +} + +static struct sh_clk_ops emev2_gclk_clk_ops = { + .enable = emev2_gclk_enable, + .disable = emev2_gclk_disable, + .recalc = followparent_recalc, +}; + +static int __init emev2_gclk_register(struct clk *clks, int nr) +{ + struct clk *clkp; + int ret = 0; + int k; + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + clkp->ops = &emev2_gclk_clk_ops; + ret |= clk_register(clkp); + } + + return ret; +} + +static unsigned long emev2_sclkdiv_recalc(struct clk *clk) +{ + unsigned int sclk_div; + + sclk_div = (ioread32(clk->mapped_reg) >> clk->enable_bit) & 0xff; + + return clk->parent->rate / (sclk_div + 1); +} + +static struct sh_clk_ops emev2_sclkdiv_clk_ops = { + .recalc = emev2_sclkdiv_recalc, +}; + +static int __init emev2_sclkdiv_register(struct clk *clks, int nr) +{ + struct clk *clkp; + int ret = 0; + int k; + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + clkp->ops = &emev2_sclkdiv_clk_ops; + ret |= clk_register(clkp); + } + + return ret; +} + +static struct clk_lookup lookups[] = { + CLKDEV_DEV_ID("serial8250-em.0", &gclk_clks[GCLK_USIAU0_SCLK]), + CLKDEV_DEV_ID("e1020000.uart", &gclk_clks[GCLK_USIAU0_SCLK]), + CLKDEV_DEV_ID("serial8250-em.1", &gclk_clks[GCLK_USIBU1_SCLK]), + CLKDEV_DEV_ID("e1030000.uart", &gclk_clks[GCLK_USIBU1_SCLK]), + CLKDEV_DEV_ID("serial8250-em.2", &gclk_clks[GCLK_USIBU2_SCLK]), + CLKDEV_DEV_ID("e1040000.uart", &gclk_clks[GCLK_USIBU2_SCLK]), + CLKDEV_DEV_ID("serial8250-em.3", &gclk_clks[GCLK_USIBU3_SCLK]), + CLKDEV_DEV_ID("e1050000.uart", &gclk_clks[GCLK_USIBU3_SCLK]), + CLKDEV_DEV_ID("em_sti.0", &gclk_clks[GCLK_STI_SCLK]), + CLKDEV_DEV_ID("e0180000.sti", &gclk_clks[GCLK_STI_SCLK]), +}; + +void __init emev2_clock_init(void) +{ + int k, ret = 0; + static int is_setup; + + /* yuck, this is ugly as hell, but the non-smp case of clocks + * code is now designed to rely on ioremap() instead of static + * entity maps. in the case of smp we need access to the SMU + * register earlier than ioremap() is actually working without + * any static maps. to enable SMP in ugly but with dynamic + * mappings we have to call emev2_clock_init() from different + * places depending on UP and SMP... + */ + if (is_setup++) + return; + + smu_base = ioremap(EMEV2_SMU_BASE, PAGE_SIZE); + BUG_ON(!smu_base); + + /* setup STI timer to run on 37.768 kHz and deassert reset */ + emev2_smu_write(0, STI_CLKSEL); + emev2_smu_write(1, STI_RSTCTRL); + + /* deassert reset for UART0->UART3 */ + emev2_smu_write(2, USIAU0_RSTCTRL); + emev2_smu_write(2, USIBU1_RSTCTRL); + emev2_smu_write(2, USIBU2_RSTCTRL); + emev2_smu_write(2, USIBU3_RSTCTRL); + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + if (!ret) + ret = emev2_sclkdiv_register(sclkdiv_clks, SCLKDIV_NR); + + if (!ret) + ret = emev2_gclk_register(gclk_clks, GCLK_NR); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + shmobile_clk_init(); + else + panic("failed to setup emev2 clocks\n"); +} diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c index 99c4d743a99c..81b54a6af20f 100644 --- a/arch/arm/mach-shmobile/clock-r8a7740.c +++ b/arch/arm/mach-shmobile/clock-r8a7740.c @@ -47,6 +47,7 @@ #define PLLC01CR 0xe6150028 #define SUBCKCR 0xe6150080 +#define USBCKCR 0xe615008c #define MSTPSR0 0xe6150030 #define MSTPSR1 0xe6150038 @@ -181,6 +182,95 @@ static struct clk pllc1_div2_clk = { .parent = &pllc1_clk, }; +/* USB clock */ +static struct clk *usb24s_parents[] = { + [0] = &system_clk, + [1] = &extal2_clk +}; + +static int usb24s_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(USBCKCR) & ~(1 << 8), USBCKCR); + + return 0; +} + +static void usb24s_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(USBCKCR) | (1 << 8), USBCKCR); +} + +static int usb24s_set_parent(struct clk *clk, struct clk *parent) +{ + int i, ret; + u32 val; + + if (!clk->parent_table || !clk->parent_num) + return -EINVAL; + + /* Search the parent */ + for (i = 0; i < clk->parent_num; i++) + if (clk->parent_table[i] == parent) + break; + + if (i == clk->parent_num) + return -ENODEV; + + ret = clk_reparent(clk, parent); + if (ret < 0) + return ret; + + val = __raw_readl(USBCKCR); + val &= ~(1 << 7); + val |= i << 7; + __raw_writel(val, USBCKCR); + + return 0; +} + +static struct sh_clk_ops usb24s_clk_ops = { + .recalc = followparent_recalc, + .enable = usb24s_enable, + .disable = usb24s_disable, + .set_parent = usb24s_set_parent, +}; + +static struct clk usb24s_clk = { + .ops = &usb24s_clk_ops, + .parent_table = usb24s_parents, + .parent_num = ARRAY_SIZE(usb24s_parents), + .parent = &system_clk, +}; + +static unsigned long usb24_recalc(struct clk *clk) +{ + return clk->parent->rate / + ((__raw_readl(USBCKCR) & (1 << 6)) ? 1 : 2); +}; + +static int usb24_set_rate(struct clk *clk, unsigned long rate) +{ + u32 val; + + /* closer to which ? parent->rate or parent->rate/2 */ + val = __raw_readl(USBCKCR); + val &= ~(1 << 6); + val |= (rate > (clk->parent->rate / 4) * 3) << 6; + __raw_writel(val, USBCKCR); + + return 0; +} + +static struct sh_clk_ops usb24_clk_ops = { + .recalc = usb24_recalc, + .set_rate = usb24_set_rate, +}; + +static struct clk usb24_clk = { + .ops = &usb24_clk_ops, + .parent = &usb24s_clk, +}; + struct clk *main_clks[] = { &extalr_clk, &extal1_clk, @@ -196,6 +286,8 @@ struct clk *main_clks[] = { &pllc0_clk, &pllc1_clk, &pllc1_div2_clk, + &usb24s_clk, + &usb24_clk, }; static void div4_kick(struct clk *clk) @@ -223,7 +315,7 @@ static struct clk_div4_table div4_table = { enum { DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_HP, - DIV4_HPP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP, + DIV4_HPP, DIV4_USBP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP, DIV4_NR }; @@ -234,6 +326,7 @@ struct clk div4_clks[DIV4_NR] = { [DIV4_M1] = SH_CLK_DIV4(&pllc1_clk, FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT), [DIV4_HP] = SH_CLK_DIV4(&pllc1_clk, FRQCRB, 4, 0x6fff, 0), [DIV4_HPP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 20, 0x6fff, 0), + [DIV4_USBP] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 16, 0x6fff, 0), [DIV4_S] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 12, 0x6fff, 0), [DIV4_ZB] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 8, 0x6fff, 0), [DIV4_M3] = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 4, 0x6fff, 0), @@ -257,7 +350,10 @@ enum { MSTP222, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, - MSTP329, MSTP323, + MSTP329, MSTP328, MSTP323, MSTP320, + MSTP314, MSTP313, MSTP312, + + MSTP416, MSTP415, MSTP407, MSTP406, MSTP_NR }; @@ -280,7 +376,17 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP200] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 0, 0), /* SCIFA4 */ [MSTP329] = SH_CLK_MSTP32(&r_clk, SMSTPCR3, 29, 0), /* CMT10 */ + [MSTP328] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 28, 0), /* FSI */ [MSTP323] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */ + [MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 20, 0), /* USBF */ + [MSTP314] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 14, 0), /* SDHI0 */ + [MSTP313] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */ + [MSTP312] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */ + + [MSTP416] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 16, 0), /* USBHOST */ + [MSTP415] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 15, 0), /* SDHI2 */ + [MSTP407] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 7, 0), /* USB-Func */ + [MSTP406] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR4, 6, 0), /* USB Phy */ }; static struct clk_lookup lookups[] = { @@ -299,6 +405,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("pllc0_clk", &pllc0_clk), CLKDEV_CON_ID("pllc1_clk", &pllc1_clk), CLKDEV_CON_ID("pllc1_div2_clk", &pllc1_div2_clk), + CLKDEV_CON_ID("usb24s", &usb24s_clk), /* DIV4 clocks */ CLKDEV_CON_ID("i_clk", &div4_clks[DIV4_I]), @@ -334,7 +441,21 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP230]), CLKDEV_DEV_ID("sh_cmt.10", &mstp_clks[MSTP329]), + CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]), CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]), + CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP320]), + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), + CLKDEV_DEV_ID("sh_mmcif", &mstp_clks[MSTP312]), + + CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP415]), + + /* ICK */ + CLKDEV_ICK_ID("host", "renesas_usbhs", &mstp_clks[MSTP416]), + CLKDEV_ICK_ID("func", "renesas_usbhs", &mstp_clks[MSTP407]), + CLKDEV_ICK_ID("phy", "renesas_usbhs", &mstp_clks[MSTP406]), + CLKDEV_ICK_ID("pci", "renesas_usbhs", &div4_clks[DIV4_USBP]), + CLKDEV_ICK_ID("usb24", "renesas_usbhs", &usb24_clk), }; void __init r8a7740_clock_init(u8 md_ck) diff --git a/arch/arm/mach-shmobile/include/mach/emev2.h b/arch/arm/mach-shmobile/include/mach/emev2.h new file mode 100644 index 000000000000..e6b0c1bf4b7e --- /dev/null +++ b/arch/arm/mach-shmobile/include/mach/emev2.h @@ -0,0 +1,19 @@ +#ifndef __ASM_EMEV2_H__ +#define __ASM_EMEV2_H__ + +extern void emev2_map_io(void); +extern void emev2_init_irq(void); +extern void emev2_add_early_devices(void); +extern void emev2_add_standard_devices(void); +extern void emev2_clock_init(void); +extern void emev2_set_boot_vector(unsigned long value); +extern unsigned int emev2_get_core_count(void); +extern int emev2_platform_cpu_kill(unsigned int cpu); +extern void emev2_secondary_init(unsigned int cpu); +extern int emev2_boot_secondary(unsigned int cpu); +extern void emev2_smp_prepare_cpus(void); + +#define EMEV2_GPIO_BASE 200 +#define EMEV2_GPIO_IRQ(n) (EMEV2_GPIO_BASE + (n)) + +#endif /* __ASM_EMEV2_H__ */ diff --git a/arch/arm/mach-shmobile/include/mach/sh7372.h b/arch/arm/mach-shmobile/include/mach/sh7372.h index 8254ab86f6cd..915d0093da08 100644 --- a/arch/arm/mach-shmobile/include/mach/sh7372.h +++ b/arch/arm/mach-shmobile/include/mach/sh7372.h @@ -457,6 +457,8 @@ enum { SHDMA_SLAVE_SDHI1_TX, SHDMA_SLAVE_SDHI2_RX, SHDMA_SLAVE_SDHI2_TX, + SHDMA_SLAVE_FSIA_RX, + SHDMA_SLAVE_FSIA_TX, SHDMA_SLAVE_MMCIF_RX, SHDMA_SLAVE_MMCIF_TX, SHDMA_SLAVE_USB0_TX, diff --git a/arch/arm/mach-shmobile/include/mach/sh73a0.h b/arch/arm/mach-shmobile/include/mach/sh73a0.h index cad57578ceed..9b3598e4f105 100644 --- a/arch/arm/mach-shmobile/include/mach/sh73a0.h +++ b/arch/arm/mach-shmobile/include/mach/sh73a0.h @@ -515,8 +515,36 @@ enum { SHDMA_SLAVE_MMCIF_RX, }; -/* PINT interrupts are located at Linux IRQ 800 and up */ -#define SH73A0_PINT0_IRQ(irq) ((irq) + 800) -#define SH73A0_PINT1_IRQ(irq) ((irq) + 832) +/* + * SH73A0 IRQ LOCATION TABLE + * + * 416 ----------------------------------------- + * IRQ0-IRQ15 + * 431 ----------------------------------------- + * ... + * 448 ----------------------------------------- + * sh73a0-intcs + * sh73a0-intca-irq-pins + * 680 ----------------------------------------- + * ... + * 700 ----------------------------------------- + * sh73a0-pint0 + * 731 ----------------------------------------- + * 732 ----------------------------------------- + * sh73a0-pint1 + * 739 ----------------------------------------- + * ... + * 800 ----------------------------------------- + * IRQ16-IRQ31 + * 815 ----------------------------------------- + * ... + * 928 ----------------------------------------- + * sh73a0-intca-irq-pins + * 943 ----------------------------------------- + */ + +/* PINT interrupts are located at Linux IRQ 700 and up */ +#define SH73A0_PINT0_IRQ(irq) ((irq) + 700) +#define SH73A0_PINT1_IRQ(irq) ((irq) + 732) #endif /* __ASM_SH73A0_H__ */ diff --git a/arch/arm/mach-shmobile/pfc-r8a7740.c b/arch/arm/mach-shmobile/pfc-r8a7740.c index a4fff6950b03..670fe1869dbc 100644 --- a/arch/arm/mach-shmobile/pfc-r8a7740.c +++ b/arch/arm/mach-shmobile/pfc-r8a7740.c @@ -22,6 +22,7 @@ #include <linux/kernel.h> #include <linux/gpio.h> #include <mach/r8a7740.h> +#include <mach/irqs.h> #define CPU_ALL_PORT(fn, pfx, sfx) \ PORT_10(fn, pfx, sfx), PORT_90(fn, pfx, sfx), \ @@ -2527,6 +2528,41 @@ static struct pinmux_data_reg pinmux_data_regs[] = { { }, }; +static struct pinmux_irq pinmux_irqs[] = { + PINMUX_IRQ(evt2irq(0x0200), PORT2_FN0, PORT13_FN0), /* IRQ0A */ + PINMUX_IRQ(evt2irq(0x0220), PORT20_FN0), /* IRQ1A */ + PINMUX_IRQ(evt2irq(0x0240), PORT11_FN0, PORT12_FN0), /* IRQ2A */ + PINMUX_IRQ(evt2irq(0x0260), PORT10_FN0, PORT14_FN0), /* IRQ3A */ + PINMUX_IRQ(evt2irq(0x0280), PORT15_FN0, PORT172_FN0), /* IRQ4A */ + PINMUX_IRQ(evt2irq(0x02A0), PORT0_FN0, PORT1_FN0), /* IRQ5A */ + PINMUX_IRQ(evt2irq(0x02C0), PORT121_FN0, PORT173_FN0), /* IRQ6A */ + PINMUX_IRQ(evt2irq(0x02E0), PORT120_FN0, PORT209_FN0), /* IRQ7A */ + PINMUX_IRQ(evt2irq(0x0300), PORT119_FN0), /* IRQ8A */ + PINMUX_IRQ(evt2irq(0x0320), PORT118_FN0, PORT210_FN0), /* IRQ9A */ + PINMUX_IRQ(evt2irq(0x0340), PORT19_FN0), /* IRQ10A */ + PINMUX_IRQ(evt2irq(0x0360), PORT104_FN0), /* IRQ11A */ + PINMUX_IRQ(evt2irq(0x0380), PORT42_FN0, PORT97_FN0), /* IRQ12A */ + PINMUX_IRQ(evt2irq(0x03A0), PORT64_FN0, PORT98_FN0), /* IRQ13A */ + PINMUX_IRQ(evt2irq(0x03C0), PORT63_FN0, PORT99_FN0), /* IRQ14A */ + PINMUX_IRQ(evt2irq(0x03E0), PORT62_FN0, PORT100_FN0), /* IRQ15A */ + PINMUX_IRQ(evt2irq(0x3200), PORT68_FN0, PORT211_FN0), /* IRQ16A */ + PINMUX_IRQ(evt2irq(0x3220), PORT69_FN0), /* IRQ17A */ + PINMUX_IRQ(evt2irq(0x3240), PORT70_FN0), /* IRQ18A */ + PINMUX_IRQ(evt2irq(0x3260), PORT71_FN0), /* IRQ19A */ + PINMUX_IRQ(evt2irq(0x3280), PORT67_FN0), /* IRQ20A */ + PINMUX_IRQ(evt2irq(0x32A0), PORT202_FN0), /* IRQ21A */ + PINMUX_IRQ(evt2irq(0x32C0), PORT95_FN0), /* IRQ22A */ + PINMUX_IRQ(evt2irq(0x32E0), PORT96_FN0), /* IRQ23A */ + PINMUX_IRQ(evt2irq(0x3300), PORT180_FN0), /* IRQ24A */ + PINMUX_IRQ(evt2irq(0x3320), PORT38_FN0), /* IRQ25A */ + PINMUX_IRQ(evt2irq(0x3340), PORT58_FN0, PORT81_FN0), /* IRQ26A */ + PINMUX_IRQ(evt2irq(0x3360), PORT57_FN0, PORT168_FN0), /* IRQ27A */ + PINMUX_IRQ(evt2irq(0x3380), PORT56_FN0, PORT169_FN0), /* IRQ28A */ + PINMUX_IRQ(evt2irq(0x33A0), PORT50_FN0, PORT170_FN0), /* IRQ29A */ + PINMUX_IRQ(evt2irq(0x33C0), PORT49_FN0, PORT171_FN0), /* IRQ30A */ + PINMUX_IRQ(evt2irq(0x33E0), PORT41_FN0, PORT167_FN0), /* IRQ31A */ +}; + static struct pinmux_info r8a7740_pinmux_info = { .name = "r8a7740_pfc", .reserved_id = PINMUX_RESERVED, @@ -2554,6 +2590,9 @@ static struct pinmux_info r8a7740_pinmux_info = { .gpio_data = pinmux_data, .gpio_data_size = ARRAY_SIZE(pinmux_data), + + .gpio_irq = pinmux_irqs, + .gpio_irq_size = ARRAY_SIZE(pinmux_irqs), }; void r8a7740_pinmux_init(void) diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c index 45fa3924c6a1..7006cdc8b8ca 100644 --- a/arch/arm/mach-shmobile/platsmp.c +++ b/arch/arm/mach-shmobile/platsmp.c @@ -16,12 +16,15 @@ #include <linux/device.h> #include <linux/smp.h> #include <linux/io.h> +#include <linux/of.h> #include <asm/hardware/gic.h> #include <asm/mach-types.h> #include <mach/common.h> +#include <mach/emev2.h> #define is_sh73a0() (machine_is_ag5evm() || machine_is_kota2()) #define is_r8a7779() machine_is_marzen() +#define is_emev2() of_machine_is_compatible("renesas,emev2") static unsigned int __init shmobile_smp_get_core_count(void) { @@ -31,6 +34,9 @@ static unsigned int __init shmobile_smp_get_core_count(void) if (is_r8a7779()) return r8a7779_get_core_count(); + if (is_emev2()) + return emev2_get_core_count(); + return 1; } @@ -41,6 +47,9 @@ static void __init shmobile_smp_prepare_cpus(void) if (is_r8a7779()) r8a7779_smp_prepare_cpus(); + + if (is_emev2()) + emev2_smp_prepare_cpus(); } int shmobile_platform_cpu_kill(unsigned int cpu) @@ -48,6 +57,9 @@ int shmobile_platform_cpu_kill(unsigned int cpu) if (is_r8a7779()) return r8a7779_platform_cpu_kill(cpu); + if (is_emev2()) + return emev2_platform_cpu_kill(cpu); + return 1; } @@ -60,6 +72,9 @@ void __cpuinit platform_secondary_init(unsigned int cpu) if (is_r8a7779()) r8a7779_secondary_init(cpu); + + if (is_emev2()) + emev2_secondary_init(cpu); } int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) @@ -70,6 +85,9 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) if (is_r8a7779()) return r8a7779_boot_secondary(cpu); + if (is_emev2()) + return emev2_boot_secondary(cpu); + return -ENOSYS; } diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c new file mode 100644 index 000000000000..dae9aa68bb09 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-emev2.c @@ -0,0 +1,452 @@ +/* + * Emma Mobile EV2 processor support + * + * Copyright (C) 2012 Magnus Damm + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/platform_data/gpio-em.h> +#include <linux/of_platform.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/of_irq.h> +#include <mach/hardware.h> +#include <mach/common.h> +#include <mach/emev2.h> +#include <mach/irqs.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/time.h> +#include <asm/hardware/gic.h> + +static struct map_desc emev2_io_desc[] __initdata = { +#ifdef CONFIG_SMP + /* 128K entity map for 0xe0100000 (SMU) */ + { + .virtual = 0xe0100000, + .pfn = __phys_to_pfn(0xe0100000), + .length = SZ_128K, + .type = MT_DEVICE + }, + /* 2M mapping for SCU + L2 controller */ + { + .virtual = 0xf0000000, + .pfn = __phys_to_pfn(0x1e000000), + .length = SZ_2M, + .type = MT_DEVICE + }, +#endif +}; + +void __init emev2_map_io(void) +{ + iotable_init(emev2_io_desc, ARRAY_SIZE(emev2_io_desc)); +} + +/* UART */ +static struct resource uart0_resources[] = { + [0] = { + .start = 0xe1020000, + .end = 0xe1020037, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 40, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device uart0_device = { + .name = "serial8250-em", + .id = 0, + .num_resources = ARRAY_SIZE(uart0_resources), + .resource = uart0_resources, +}; + +static struct resource uart1_resources[] = { + [0] = { + .start = 0xe1030000, + .end = 0xe1030037, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 41, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device uart1_device = { + .name = "serial8250-em", + .id = 1, + .num_resources = ARRAY_SIZE(uart1_resources), + .resource = uart1_resources, +}; + +static struct resource uart2_resources[] = { + [0] = { + .start = 0xe1040000, + .end = 0xe1040037, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 42, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device uart2_device = { + .name = "serial8250-em", + .id = 2, + .num_resources = ARRAY_SIZE(uart2_resources), + .resource = uart2_resources, +}; + +static struct resource uart3_resources[] = { + [0] = { + .start = 0xe1050000, + .end = 0xe1050037, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 43, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device uart3_device = { + .name = "serial8250-em", + .id = 3, + .num_resources = ARRAY_SIZE(uart3_resources), + .resource = uart3_resources, +}; + +/* STI */ +static struct resource sti_resources[] = { + [0] = { + .name = "STI", + .start = 0xe0180000, + .end = 0xe0180053, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 157, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sti_device = { + .name = "em_sti", + .id = 0, + .resource = sti_resources, + .num_resources = ARRAY_SIZE(sti_resources), +}; + + +/* GIO */ +static struct gpio_em_config gio0_config = { + .gpio_base = 0, + .irq_base = EMEV2_GPIO_IRQ(0), + .number_of_pins = 32, +}; + +static struct resource gio0_resources[] = { + [0] = { + .name = "GIO_000", + .start = 0xe0050000, + .end = 0xe005002b, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "GIO_000", + .start = 0xe0050040, + .end = 0xe005005f, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = 99, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = 100, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device gio0_device = { + .name = "em_gio", + .id = 0, + .resource = gio0_resources, + .num_resources = ARRAY_SIZE(gio0_resources), + .dev = { + .platform_data = &gio0_config, + }, +}; + +static struct gpio_em_config gio1_config = { + .gpio_base = 32, + .irq_base = EMEV2_GPIO_IRQ(32), + .number_of_pins = 32, +}; + +static struct resource gio1_resources[] = { + [0] = { + .name = "GIO_032", + .start = 0xe0050080, + .end = 0xe00500ab, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "GIO_032", + .start = 0xe00500c0, + .end = 0xe00500df, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = 101, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = 102, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device gio1_device = { + .name = "em_gio", + .id = 1, + .resource = gio1_resources, + .num_resources = ARRAY_SIZE(gio1_resources), + .dev = { + .platform_data = &gio1_config, + }, +}; + +static struct gpio_em_config gio2_config = { + .gpio_base = 64, + .irq_base = EMEV2_GPIO_IRQ(64), + .number_of_pins = 32, +}; + +static struct resource gio2_resources[] = { + [0] = { + .name = "GIO_064", + .start = 0xe0050100, + .end = 0xe005012b, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "GIO_064", + .start = 0xe0050140, + .end = 0xe005015f, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = 103, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = 104, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device gio2_device = { + .name = "em_gio", + .id = 2, + .resource = gio2_resources, + .num_resources = ARRAY_SIZE(gio2_resources), + .dev = { + .platform_data = &gio2_config, + }, +}; + +static struct gpio_em_config gio3_config = { + .gpio_base = 96, + .irq_base = EMEV2_GPIO_IRQ(96), + .number_of_pins = 32, +}; + +static struct resource gio3_resources[] = { + [0] = { + .name = "GIO_096", + .start = 0xe0050100, + .end = 0xe005012b, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "GIO_096", + .start = 0xe0050140, + .end = 0xe005015f, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = 105, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = 106, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device gio3_device = { + .name = "em_gio", + .id = 3, + .resource = gio3_resources, + .num_resources = ARRAY_SIZE(gio3_resources), + .dev = { + .platform_data = &gio3_config, + }, +}; + +static struct gpio_em_config gio4_config = { + .gpio_base = 128, + .irq_base = EMEV2_GPIO_IRQ(128), + .number_of_pins = 31, +}; + +static struct resource gio4_resources[] = { + [0] = { + .name = "GIO_128", + .start = 0xe0050200, + .end = 0xe005022b, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "GIO_128", + .start = 0xe0050240, + .end = 0xe005025f, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = 107, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = 108, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device gio4_device = { + .name = "em_gio", + .id = 4, + .resource = gio4_resources, + .num_resources = ARRAY_SIZE(gio4_resources), + .dev = { + .platform_data = &gio4_config, + }, +}; + +static struct platform_device *emev2_early_devices[] __initdata = { + &uart0_device, + &uart1_device, + &uart2_device, + &uart3_device, +}; + +static struct platform_device *emev2_late_devices[] __initdata = { + &sti_device, + &gio0_device, + &gio1_device, + &gio2_device, + &gio3_device, + &gio4_device, +}; + +void __init emev2_add_standard_devices(void) +{ + emev2_clock_init(); + + platform_add_devices(emev2_early_devices, + ARRAY_SIZE(emev2_early_devices)); + + platform_add_devices(emev2_late_devices, + ARRAY_SIZE(emev2_late_devices)); +} + +void __init emev2_init_delay(void) +{ + shmobile_setup_delay(533, 1, 3); /* Cortex-A9 @ 533MHz */ +} + +void __init emev2_add_early_devices(void) +{ + emev2_init_delay(); + + early_platform_add_devices(emev2_early_devices, + ARRAY_SIZE(emev2_early_devices)); + + /* setup early console here as well */ + shmobile_setup_console(); +} + +void __init emev2_init_irq(void) +{ + void __iomem *gic_dist_base; + void __iomem *gic_cpu_base; + + /* Static mappings, never released */ + gic_dist_base = ioremap(0xe0028000, PAGE_SIZE); + gic_cpu_base = ioremap(0xe0020000, PAGE_SIZE); + BUG_ON(!gic_dist_base || !gic_cpu_base); + + /* Use GIC to handle interrupts */ + gic_init(0, 29, gic_dist_base, gic_cpu_base); +} + +#ifdef CONFIG_USE_OF +static const struct of_dev_auxdata emev2_auxdata_lookup[] __initconst = { + { } +}; + +void __init emev2_add_standard_devices_dt(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, + emev2_auxdata_lookup, NULL); +} + +static const struct of_device_id emev2_dt_irq_match[] = { + { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, + {}, +}; + +static const char *emev2_boards_compat_dt[] __initdata = { + "renesas,emev2", + NULL, +}; + +void __init emev2_init_irq_dt(void) +{ + of_irq_init(emev2_dt_irq_match); +} + +DT_MACHINE_START(EMEV2_DT, "Generic Emma Mobile EV2 (Flattened Device Tree)") + .init_early = emev2_init_delay, + .nr_irqs = NR_IRQS_LEGACY, + .init_irq = emev2_init_irq_dt, + .handle_irq = gic_handle_irq, + .init_machine = emev2_add_standard_devices_dt, + .timer = &shmobile_timer, + .dt_compat = emev2_boards_compat_dt, +MACHINE_END + +#endif /* CONFIG_USE_OF */ diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c index 14edb5cffa7f..34a1548dbda6 100644 --- a/arch/arm/mach-shmobile/setup-r8a7740.c +++ b/arch/arm/mach-shmobile/setup-r8a7740.c @@ -350,19 +350,19 @@ static void r8a7740_i2c_workaround(struct platform_device *pdev) i2c_write(reg, ICSTART, i2c_read(reg, ICSTART) | 0x10); i2c_read(reg, ICSTART); /* dummy read */ - mdelay(100); + udelay(10); i2c_write(reg, ICCR, 0x01); - i2c_read(reg, ICCR); i2c_write(reg, ICSTART, 0x00); - i2c_read(reg, ICSTART); + + udelay(10); i2c_write(reg, ICCR, 0x10); - mdelay(100); + udelay(10); i2c_write(reg, ICCR, 0x00); - mdelay(100); + udelay(10); i2c_write(reg, ICCR, 0x10); - mdelay(100); + udelay(10); iounmap(reg); } diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c index 4c7fece5ef92..6a4bd582c028 100644 --- a/arch/arm/mach-shmobile/setup-sh7372.c +++ b/arch/arm/mach-shmobile/setup-sh7372.c @@ -462,6 +462,16 @@ static const struct sh_dmae_slave_config sh7372_dmae_slaves[] = { .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT), .mid_rid = 0xce, }, { + .slave_id = SHDMA_SLAVE_FSIA_TX, + .addr = 0xfe1f0024, + .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xb1, + }, { + .slave_id = SHDMA_SLAVE_FSIA_RX, + .addr = 0xfe1f0020, + .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xb2, + }, { .slave_id = SHDMA_SLAVE_MMCIF_TX, .addr = 0xe6bd0034, .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT), diff --git a/arch/arm/mach-shmobile/smp-emev2.c b/arch/arm/mach-shmobile/smp-emev2.c new file mode 100644 index 000000000000..6a35c4a31e6c --- /dev/null +++ b/arch/arm/mach-shmobile/smp-emev2.c @@ -0,0 +1,97 @@ +/* + * SMP support for Emma Mobile EV2 + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Magnus Damm + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <mach/common.h> +#include <mach/emev2.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include <asm/hardware/gic.h> +#include <asm/cacheflush.h> + +#define EMEV2_SCU_BASE 0x1e000000 + +static DEFINE_SPINLOCK(scu_lock); +static void __iomem *scu_base; + +static void modify_scu_cpu_psr(unsigned long set, unsigned long clr) +{ + unsigned long tmp; + + /* we assume this code is running on a different cpu + * than the one that is changing coherency setting */ + spin_lock(&scu_lock); + tmp = readl(scu_base + 8); + tmp &= ~clr; + tmp |= set; + writel(tmp, scu_base + 8); + spin_unlock(&scu_lock); + +} + +unsigned int __init emev2_get_core_count(void) +{ + if (!scu_base) { + scu_base = ioremap(EMEV2_SCU_BASE, PAGE_SIZE); + emev2_clock_init(); /* need ioremapped SMU */ + } + + WARN_ON_ONCE(!scu_base); + + return scu_base ? scu_get_core_count(scu_base) : 1; +} + +int emev2_platform_cpu_kill(unsigned int cpu) +{ + return 0; /* not supported yet */ +} + +void __cpuinit emev2_secondary_init(unsigned int cpu) +{ + gic_secondary_init(0); +} + +int __cpuinit emev2_boot_secondary(unsigned int cpu) +{ + cpu = cpu_logical_map(cpu); + + /* enable cache coherency */ + modify_scu_cpu_psr(0, 3 << (cpu * 8)); + + /* Tell ROM loader about our vector (in headsmp.S) */ + emev2_set_boot_vector(__pa(shmobile_secondary_vector)); + + gic_raise_softirq(cpumask_of(cpu), 1); + return 0; +} + +void __init emev2_smp_prepare_cpus(void) +{ + int cpu = cpu_logical_map(0); + + scu_enable(scu_base); + + /* enable cache coherency on CPU0 */ + modify_scu_cpu_psr(0, 3 << (cpu * 8)); +} diff --git a/arch/arm/mach-shmobile/timer.c b/arch/arm/mach-shmobile/timer.c index cba39d866687..a68919727e24 100644 --- a/arch/arm/mach-shmobile/timer.c +++ b/arch/arm/mach-shmobile/timer.c @@ -36,7 +36,8 @@ void __init shmobile_setup_delay(unsigned int max_cpu_core_mhz, unsigned int value = (1000000 * mult) / (HZ * div); - lpj_fine = max_cpu_core_mhz * value; + if (!preset_lpj) + preset_lpj = max_cpu_core_mhz * value; } static void __init shmobile_late_time_init(void) diff --git a/arch/arm/mach-ux500/board-mop500-uib.c b/arch/arm/mach-ux500/board-mop500-uib.c index 5af36aa56c08..b29a788f498c 100644 --- a/arch/arm/mach-ux500/board-mop500-uib.c +++ b/arch/arm/mach-ux500/board-mop500-uib.c @@ -102,7 +102,7 @@ static int __init mop500_uib_init(void) struct i2c_adapter *i2c0; int ret; - if (!cpu_is_u8500()) + if (!cpu_is_u8500_family()) return -ENODEV; if (uib) { diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index df91344aa2db..dc12394295d5 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -36,7 +36,9 @@ static int __init ux500_l2x0_unlock(void) static int __init ux500_l2x0_init(void) { - if (cpu_is_u8500()) + u32 aux_val = 0x3e000000; + + if (cpu_is_u8500_family()) l2x0_base = __io_address(U8500_L2CC_BASE); else ux500_unknown_soc(); @@ -44,11 +46,19 @@ static int __init ux500_l2x0_init(void) /* Unlock before init */ ux500_l2x0_unlock(); + /* DB9540's L2 has 128KB way size */ + if (cpu_is_u9540()) + /* 128KB way size */ + aux_val |= (0x4 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); + else + /* 64KB way size */ + aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); + /* 64KB way size, 8 way associativity, force WA */ if (of_have_populated_dt()) - l2x0_of_init(0x3e060000, 0xc0000fff); + l2x0_of_init(aux_val, 0xc0000fff); else - l2x0_init(l2x0_base, 0x3e060000, 0xc0000fff); + l2x0_init(l2x0_base, aux_val, 0xc0000fff); /* * We can't disable l2 as we are in non secure mode, currently diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c index 9feb6bc7f20e..063f3dbd45a9 100644 --- a/arch/arm/mach-ux500/clock.c +++ b/arch/arm/mach-ux500/clock.c @@ -149,7 +149,7 @@ static unsigned long clk_mtu_get_rate(struct clk *clk) unsigned long mturate; unsigned long retclk; - if (cpu_is_u8500()) + if (cpu_is_u8500_family()) addr = __io_address(U8500_PRCMU_BASE); else ux500_unknown_soc(); diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index f44a12ccddf3..76a7503a11a2 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -34,8 +34,8 @@ static struct map_desc u8500_uart_io_desc[] __initdata = { __IO_DEV_DESC(U8500_UART0_BASE, SZ_4K), __IO_DEV_DESC(U8500_UART2_BASE, SZ_4K), }; - -static struct map_desc u8500_io_desc[] __initdata = { +/* U8500 and U9540 common io_desc */ +static struct map_desc u8500_common_io_desc[] __initdata = { /* SCU base also covers GIC CPU BASE and TWD with its 4K page */ __IO_DEV_DESC(U8500_SCU_BASE, SZ_4K), __IO_DEV_DESC(U8500_GIC_DIST_BASE, SZ_4K), @@ -49,12 +49,23 @@ static struct map_desc u8500_io_desc[] __initdata = { __IO_DEV_DESC(U8500_CLKRST5_BASE, SZ_4K), __IO_DEV_DESC(U8500_CLKRST6_BASE, SZ_4K), - __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO0_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO1_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO2_BASE, SZ_4K), __IO_DEV_DESC(U8500_GPIO3_BASE, SZ_4K), +}; + +/* U8500 IO map specific description */ +static struct map_desc u8500_io_desc[] __initdata = { + __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K), __IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K), + +}; + +/* U9540 IO map specific description */ +static struct map_desc u9540_io_desc[] __initdata = { + __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K + SZ_8K), + __IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K + SZ_8K), }; void __init u8500_map_io(void) @@ -66,7 +77,12 @@ void __init u8500_map_io(void) ux500_map_io(); - iotable_init(u8500_io_desc, ARRAY_SIZE(u8500_io_desc)); + iotable_init(u8500_common_io_desc, ARRAY_SIZE(u8500_common_io_desc)); + + if (cpu_is_u9540()) + iotable_init(u9540_io_desc, ARRAY_SIZE(u9540_io_desc)); + else + iotable_init(u8500_io_desc, ARRAY_SIZE(u8500_io_desc)); _PRCMU_BASE = __io_address(U8500_PRCMU_BASE); } diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 4b4e59b30d81..0982279f51f3 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -39,7 +39,7 @@ void __init ux500_init_irq(void) void __iomem *dist_base; void __iomem *cpu_base; - if (cpu_is_u8500()) { + if (cpu_is_u8500_family()) { dist_base = __io_address(U8500_GIC_DIST_BASE); cpu_base = __io_address(U8500_GIC_CPU_BASE); } else @@ -56,7 +56,7 @@ void __init ux500_init_irq(void) * Init clocks here so that they are available for system timer * initialization. */ - if (cpu_is_u8500()) + if (cpu_is_u8500_family()) db8500_prcmu_early_init(); clk_init(); } diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c index 15a0f63b2e2b..d1579920139f 100644 --- a/arch/arm/mach-ux500/id.c +++ b/arch/arm/mach-ux500/id.c @@ -23,7 +23,7 @@ static unsigned int ux500_read_asicid(phys_addr_t addr) { phys_addr_t base = addr & ~0xfff; struct map_desc desc = { - .virtual = IO_ADDRESS(base), + .virtual = UX500_VIRT_ROM, .pfn = __phys_to_pfn(base), .length = SZ_16K, .type = MT_DEVICE, @@ -35,7 +35,7 @@ static unsigned int ux500_read_asicid(phys_addr_t addr) local_flush_tlb_all(); flush_cache_all(); - return readl(__io_address(addr)); + return readl(IOMEM(UX500_VIRT_ROM + (addr & 0xfff))); } static void ux500_print_soc_info(unsigned int asicid) @@ -67,6 +67,7 @@ static unsigned int partnumber(unsigned int asicid) * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0 * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2 * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0 + * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx */ void __init ux500_map_io(void) @@ -91,6 +92,10 @@ void __init ux500_map_io(void) /* DB5500v1 */ addr = 0x9001FFF4; break; + + case 0x413fc090: /* DB9540 */ + addr = 0xFFFFDBF4; + break; } if (addr) diff --git a/arch/arm/mach-ux500/include/mach/db8500-regs.h b/arch/arm/mach-ux500/include/mach/db8500-regs.h index 9ec20b96d8f2..1530d493879d 100644 --- a/arch/arm/mach-ux500/include/mach/db8500-regs.h +++ b/arch/arm/mach-ux500/include/mach/db8500-regs.h @@ -41,6 +41,10 @@ /* ASIC ID is at 0xbf4 offset within this region */ #define U8500_ASIC_ID_BASE 0x9001D000 +#define U9540_BOOT_ROM_BASE 0xFFFE0000 +/* ASIC ID is at 0xbf4 offset within this region */ +#define U9540_ASIC_ID_BASE 0xFFFFD000 + #define U8500_PER6_BASE 0xa03c0000 #define U8500_PER7_BASE 0xa03d0000 #define U8500_PER5_BASE 0xa03e0000 @@ -96,7 +100,9 @@ #define U8500_SCR_BASE (U8500_PER4_BASE + 0x05000) #define U8500_DMC_BASE (U8500_PER4_BASE + 0x06000) #define U8500_PRCMU_BASE (U8500_PER4_BASE + 0x07000) +#define U9540_DMC1_BASE (U8500_PER4_BASE + 0x0A000) #define U8500_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x68000) +#define U9540_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x6A000) #define U8500_PRCMU_TCPM_BASE (U8500_PER4_BASE + 0x60000) #define U8500_PRCMU_TIMER_3_BASE (U8500_PER4_BASE + 0x07338) #define U8500_PRCMU_TIMER_4_BASE (U8500_PER4_BASE + 0x07450) diff --git a/arch/arm/mach-ux500/include/mach/hardware.h b/arch/arm/mach-ux500/include/mach/hardware.h index cf6fac3d1eeb..808c1d6601c5 100644 --- a/arch/arm/mach-ux500/include/mach/hardware.h +++ b/arch/arm/mach-ux500/include/mach/hardware.h @@ -17,6 +17,8 @@ */ #define U8500_IO_VIRTUAL 0xf0000000 #define U8500_IO_PHYSICAL 0xa0000000 +/* This is where we map in the ROM to check ASIC IDs */ +#define UX500_VIRT_ROM 0xf0000000 /* This macro is used in assembly, so no cast */ #define IO_ADDRESS(x) \ @@ -24,6 +26,7 @@ /* typesafe io address */ #define __io_address(n) IOMEM(IO_ADDRESS(n)) + /* Used by some plat-nomadik code */ #define io_p2v(n) __io_address(n) diff --git a/arch/arm/mach-ux500/include/mach/id.h b/arch/arm/mach-ux500/include/mach/id.h index 833d6a6edc9b..c6e2db9e9e51 100644 --- a/arch/arm/mach-ux500/include/mach/id.h +++ b/arch/arm/mach-ux500/include/mach/id.h @@ -41,6 +41,16 @@ static inline bool __attribute_const__ cpu_is_u8500(void) return dbx500_partnumber() == 0x8500; } +static inline bool __attribute_const__ cpu_is_u9540(void) +{ + return dbx500_partnumber() == 0x9540; +} + +static inline bool cpu_is_u8500_family(void) +{ + return cpu_is_u8500() || cpu_is_u9540(); +} + static inline bool __attribute_const__ cpu_is_u5500(void) { return dbx500_partnumber() == 0x5500; @@ -111,7 +121,12 @@ static inline bool cpu_is_u8500v21(void) static inline bool cpu_is_u8500v20_or_later(void) { - return cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11(); + /* + * U9540 has so much in common with U8500 that is is considered a + * U8500 variant. + */ + return cpu_is_u9540() || + (cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11()); } static inline bool ux500_is_svp(void) diff --git a/arch/arm/mach-ux500/include/mach/irqs.h b/arch/arm/mach-ux500/include/mach/irqs.h index d06dcf6208fa..e8928548b6a3 100644 --- a/arch/arm/mach-ux500/include/mach/irqs.h +++ b/arch/arm/mach-ux500/include/mach/irqs.h @@ -24,7 +24,7 @@ */ #define IRQ_MTU0 (IRQ_SHPI_START + 4) -#define DBX500_NR_INTERNAL_IRQS 160 +#define DBX500_NR_INTERNAL_IRQS 166 /* After chip-specific IRQ numbers we have the GPIO ones */ #define NOMADIK_NR_GPIO 288 diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index e8cd51aa61e4..da1d5ad5bd45 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -48,7 +48,7 @@ static void write_pen_release(int val) static void __iomem *scu_base_addr(void) { - if (cpu_is_u8500()) + if (cpu_is_u8500_family()) return __io_address(U8500_SCU_BASE); else ux500_unknown_soc(); @@ -118,7 +118,7 @@ static void __init wakeup_secondary(void) { void __iomem *backupram; - if (cpu_is_u8500()) + if (cpu_is_u8500_family()) backupram = __io_address(U8500_BACKUPRAM0_BASE); else ux500_unknown_soc(); diff --git a/arch/arm/mach-ux500/timer.c b/arch/arm/mach-ux500/timer.c index bd8e110cbcc2..741e71feca78 100644 --- a/arch/arm/mach-ux500/timer.c +++ b/arch/arm/mach-ux500/timer.c @@ -54,7 +54,7 @@ static void __init ux500_timer_init(void) void __iomem *tmp_base; struct device_node *np; - if (cpu_is_u8500()) { + if (cpu_is_u8500_family()) { mtu_timer_base = __io_address(U8500_MTU0_BASE); prcmu_timer_base = __io_address(U8500_PRCMU_TIMER_4_BASE); } else { diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index 0bf1b8910eeb..74b830b635a6 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -161,7 +161,7 @@ static struct cpufreq_driver db8500_cpufreq_driver = { static int __init db8500_cpufreq_register(void) { - if (!cpu_is_u8500v20_or_later()) + if (!cpu_is_u8500_family()) return -ENODEV; pr_info("cpufreq for DB8500 started\n"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e03653d69357..eb80ba300452 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -91,6 +91,12 @@ config GPIO_IT8761E help Say yes here to support GPIO functionality of IT8761E super I/O chip. +config GPIO_EM + tristate "Emma Mobile GPIO" + depends on ARM + help + Say yes here to support GPIO on Renesas Emma Mobile SoCs. + config GPIO_EP93XX def_bool y depends on ARCH_EP93XX diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 007f54bd0081..3f1f829260bb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o +obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c new file mode 100644 index 000000000000..150d9768811d --- /dev/null +++ b/drivers/gpio/gpio-em.c @@ -0,0 +1,418 @@ +/* + * Emma Mobile GPIO Support - GIO + * + * Copyright (C) 2012 Magnus Damm + * + * 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 + * the Free Software Foundation; either version 2 of the License + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_data/gpio-em.h> + +struct em_gio_priv { + void __iomem *base0; + void __iomem *base1; + unsigned int irq_base; + spinlock_t sense_lock; + struct platform_device *pdev; + struct gpio_chip gpio_chip; + struct irq_chip irq_chip; + struct irq_domain *irq_domain; +}; + +#define GIO_E1 0x00 +#define GIO_E0 0x04 +#define GIO_EM 0x04 +#define GIO_OL 0x08 +#define GIO_OH 0x0c +#define GIO_I 0x10 +#define GIO_IIA 0x14 +#define GIO_IEN 0x18 +#define GIO_IDS 0x1c +#define GIO_IIM 0x1c +#define GIO_RAW 0x20 +#define GIO_MST 0x24 +#define GIO_IIR 0x28 + +#define GIO_IDT0 0x40 +#define GIO_IDT1 0x44 +#define GIO_IDT2 0x48 +#define GIO_IDT3 0x4c +#define GIO_RAWBL 0x50 +#define GIO_RAWBH 0x54 +#define GIO_IRBL 0x58 +#define GIO_IRBH 0x5c + +#define GIO_IDT(n) (GIO_IDT0 + ((n) * 4)) + +static inline unsigned long em_gio_read(struct em_gio_priv *p, int offs) +{ + if (offs < GIO_IDT0) + return ioread32(p->base0 + offs); + else + return ioread32(p->base1 + (offs - GIO_IDT0)); +} + +static inline void em_gio_write(struct em_gio_priv *p, int offs, + unsigned long value) +{ + if (offs < GIO_IDT0) + iowrite32(value, p->base0 + offs); + else + iowrite32(value, p->base1 + (offs - GIO_IDT0)); +} + +static inline struct em_gio_priv *irq_to_priv(struct irq_data *d) +{ + struct irq_chip *chip = irq_data_get_irq_chip(d); + return container_of(chip, struct em_gio_priv, irq_chip); +} + +static void em_gio_irq_disable(struct irq_data *d) +{ + struct em_gio_priv *p = irq_to_priv(d); + + em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d))); +} + +static void em_gio_irq_enable(struct irq_data *d) +{ + struct em_gio_priv *p = irq_to_priv(d); + + em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d))); +} + +#define GIO_ASYNC(x) (x + 8) + +static unsigned char em_gio_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_EDGE_RISING] = GIO_ASYNC(0x00), + [IRQ_TYPE_EDGE_FALLING] = GIO_ASYNC(0x01), + [IRQ_TYPE_LEVEL_HIGH] = GIO_ASYNC(0x02), + [IRQ_TYPE_LEVEL_LOW] = GIO_ASYNC(0x03), + [IRQ_TYPE_EDGE_BOTH] = GIO_ASYNC(0x04), +}; + +static int em_gio_irq_set_type(struct irq_data *d, unsigned int type) +{ + unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK]; + struct em_gio_priv *p = irq_to_priv(d); + unsigned int reg, offset, shift; + unsigned long flags; + unsigned long tmp; + + if (!value) + return -EINVAL; + + offset = irqd_to_hwirq(d); + + pr_debug("gio: sense irq = %d, mode = %d\n", offset, value); + + /* 8 x 4 bit fields in 4 IDT registers */ + reg = GIO_IDT(offset >> 3); + shift = (offset & 0x07) << 4; + + spin_lock_irqsave(&p->sense_lock, flags); + + /* disable the interrupt in IIA */ + tmp = em_gio_read(p, GIO_IIA); + tmp &= ~BIT(offset); + em_gio_write(p, GIO_IIA, tmp); + + /* change the sense setting in IDT */ + tmp = em_gio_read(p, reg); + tmp &= ~(0xf << shift); + tmp |= value << shift; + em_gio_write(p, reg, tmp); + + /* clear pending interrupts */ + em_gio_write(p, GIO_IIR, BIT(offset)); + + /* enable the interrupt in IIA */ + tmp = em_gio_read(p, GIO_IIA); + tmp |= BIT(offset); + em_gio_write(p, GIO_IIA, tmp); + + spin_unlock_irqrestore(&p->sense_lock, flags); + + return 0; +} + +static irqreturn_t em_gio_irq_handler(int irq, void *dev_id) +{ + struct em_gio_priv *p = dev_id; + unsigned long pending; + unsigned int offset, irqs_handled = 0; + + while ((pending = em_gio_read(p, GIO_MST))) { + offset = __ffs(pending); + em_gio_write(p, GIO_IIR, BIT(offset)); + generic_handle_irq(irq_find_mapping(p->irq_domain, offset)); + irqs_handled++; + } + + return irqs_handled ? IRQ_HANDLED : IRQ_NONE; +} + +static inline struct em_gio_priv *gpio_to_priv(struct gpio_chip *chip) +{ + return container_of(chip, struct em_gio_priv, gpio_chip); +} + +static int em_gio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + em_gio_write(gpio_to_priv(chip), GIO_E0, BIT(offset)); + return 0; +} + +static int em_gio_get(struct gpio_chip *chip, unsigned offset) +{ + return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & BIT(offset)); +} + +static void __em_gio_set(struct gpio_chip *chip, unsigned int reg, + unsigned shift, int value) +{ + /* upper 16 bits contains mask and lower 16 actual value */ + em_gio_write(gpio_to_priv(chip), reg, + (1 << (shift + 16)) | (value << shift)); +} + +static void em_gio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + /* output is split into two registers */ + if (offset < 16) + __em_gio_set(chip, GIO_OL, offset, value); + else + __em_gio_set(chip, GIO_OH, offset - 16, value); +} + +static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + /* write GPIO value to output before selecting output mode of pin */ + em_gio_set(chip, offset, value); + em_gio_write(gpio_to_priv(chip), GIO_E1, BIT(offset)); + return 0; +} + +static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return irq_find_mapping(gpio_to_priv(chip)->irq_domain, offset); +} + +static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct em_gio_priv *p = h->host_data; + + pr_debug("gio: map hw irq = %d, virq = %d\n", (int)hw, virq); + + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); /* kill me now */ + return 0; +} + +static struct irq_domain_ops em_gio_irq_domain_ops = { + .map = em_gio_irq_domain_map, +}; + +static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p) +{ + struct platform_device *pdev = p->pdev; + struct gpio_em_config *pdata = pdev->dev.platform_data; + + p->irq_base = irq_alloc_descs(pdata->irq_base, 0, + pdata->number_of_pins, numa_node_id()); + if (IS_ERR_VALUE(p->irq_base)) { + dev_err(&pdev->dev, "cannot get irq_desc\n"); + return -ENXIO; + } + pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n", + pdata->gpio_base, pdata->number_of_pins, p->irq_base); + + p->irq_domain = irq_domain_add_legacy(pdev->dev.of_node, + pdata->number_of_pins, + p->irq_base, 0, + &em_gio_irq_domain_ops, p); + if (!p->irq_domain) { + irq_free_descs(p->irq_base, pdata->number_of_pins); + return -ENXIO; + } + + return 0; +} + +static void __devexit em_gio_irq_domain_cleanup(struct em_gio_priv *p) +{ + struct gpio_em_config *pdata = p->pdev->dev.platform_data; + + irq_free_descs(p->irq_base, pdata->number_of_pins); + /* FIXME: irq domain wants to be freed! */ +} + +static int __devinit em_gio_probe(struct platform_device *pdev) +{ + struct gpio_em_config *pdata = pdev->dev.platform_data; + struct em_gio_priv *p; + struct resource *io[2], *irq[2]; + struct gpio_chip *gpio_chip; + struct irq_chip *irq_chip; + const char *name = dev_name(&pdev->dev); + int ret; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + ret = -ENOMEM; + goto err0; + } + + p->pdev = pdev; + platform_set_drvdata(pdev, p); + spin_lock_init(&p->sense_lock); + + io[0] = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io[1] = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + + if (!io[0] || !io[1] || !irq[0] || !irq[1] || !pdata) { + dev_err(&pdev->dev, "missing IRQ, IOMEM or configuration\n"); + ret = -EINVAL; + goto err1; + } + + p->base0 = ioremap_nocache(io[0]->start, resource_size(io[0])); + if (!p->base0) { + dev_err(&pdev->dev, "failed to remap low I/O memory\n"); + ret = -ENXIO; + goto err1; + } + + p->base1 = ioremap_nocache(io[1]->start, resource_size(io[1])); + if (!p->base1) { + dev_err(&pdev->dev, "failed to remap high I/O memory\n"); + ret = -ENXIO; + goto err2; + } + + gpio_chip = &p->gpio_chip; + gpio_chip->direction_input = em_gio_direction_input; + gpio_chip->get = em_gio_get; + gpio_chip->direction_output = em_gio_direction_output; + gpio_chip->set = em_gio_set; + gpio_chip->to_irq = em_gio_to_irq; + gpio_chip->label = name; + gpio_chip->owner = THIS_MODULE; + gpio_chip->base = pdata->gpio_base; + gpio_chip->ngpio = pdata->number_of_pins; + + irq_chip = &p->irq_chip; + irq_chip->name = name; + irq_chip->irq_mask = em_gio_irq_disable; + irq_chip->irq_unmask = em_gio_irq_enable; + irq_chip->irq_enable = em_gio_irq_enable; + irq_chip->irq_disable = em_gio_irq_disable; + irq_chip->irq_set_type = em_gio_irq_set_type; + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; + + ret = em_gio_irq_domain_init(p); + if (ret) { + dev_err(&pdev->dev, "cannot initialize irq domain\n"); + goto err3; + } + + if (request_irq(irq[0]->start, em_gio_irq_handler, 0, name, p)) { + dev_err(&pdev->dev, "failed to request low IRQ\n"); + ret = -ENOENT; + goto err4; + } + + if (request_irq(irq[1]->start, em_gio_irq_handler, 0, name, p)) { + dev_err(&pdev->dev, "failed to request high IRQ\n"); + ret = -ENOENT; + goto err5; + } + + ret = gpiochip_add(gpio_chip); + if (ret) { + dev_err(&pdev->dev, "failed to add GPIO controller\n"); + goto err6; + } + return 0; + +err6: + free_irq(irq[1]->start, pdev); +err5: + free_irq(irq[0]->start, pdev); +err4: + em_gio_irq_domain_cleanup(p); +err3: + iounmap(p->base1); +err2: + iounmap(p->base0); +err1: + kfree(p); +err0: + return ret; +} + +static int __devexit em_gio_remove(struct platform_device *pdev) +{ + struct em_gio_priv *p = platform_get_drvdata(pdev); + struct resource *irq[2]; + int ret; + + ret = gpiochip_remove(&p->gpio_chip); + if (ret) + return ret; + + irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + + free_irq(irq[1]->start, pdev); + free_irq(irq[0]->start, pdev); + em_gio_irq_domain_cleanup(p); + iounmap(p->base1); + iounmap(p->base0); + kfree(p); + return 0; +} + +static struct platform_driver em_gio_device_driver = { + .probe = em_gio_probe, + .remove = __devexit_p(em_gio_remove), + .driver = { + .name = "em_gio", + } +}; + +module_platform_driver(em_gio_device_driver); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas Emma Mobile GIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/gpio-em.h b/include/linux/platform_data/gpio-em.h new file mode 100644 index 000000000000..573edfb046c4 --- /dev/null +++ b/include/linux/platform_data/gpio-em.h @@ -0,0 +1,10 @@ +#ifndef __GPIO_EM_H__ +#define __GPIO_EM_H__ + +struct gpio_em_config { + unsigned int gpio_base; + unsigned int irq_base; + unsigned int number_of_pins; +}; + +#endif /* __GPIO_EM_H__ */ |