diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 09:32:42 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 09:32:42 -0700 |
commit | 9f639269ed1522c7d69c54cc8b80ab8ee53fcb10 (patch) | |
tree | bf3e38862b6e4e4c416a6e075b614f2c4befe3e9 | |
parent | b324c67d4800e59171f48d9ddab6cbfb59110482 (diff) | |
parent | 0804dcb2afdcf014947ee98264041765f580d43f (diff) |
Merge tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull support for new arm SoCs from Olof Johansson:
"Three new system-on-chip models are supported: the st-ericsson u9540
in ux500, the sam9n12 in at91 and the emma ev2 in shmobile.
Emma is a little bit special because it is completely unrelated to the
classic shmobile models, but the new Renesas rmobile SoCs are a
combination of things from both Emma and shmobile, so it was decided
to have them all live in one directory.
This also contains updates to existing shmobile soc code as well as
some related board changes due to dependencies."
* tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (23 commits)
mach-shmobile: Use DT_MACHINE for KZM9D V3
mach-shmobile: Emma Mobile EV2 DT support V3
mach-shmobile: KZM9D board Ethernet support V3
mach-shmobile: Emma Mobile EV2 GPIO support V3
mach-shmobile: Emma Mobile EV2 SMP support V3
mach-shmobile: KZM9D board support V3
mach-shmobile: Emma Mobile EV2 SoC base support V3
gpio: Emma Mobile GPIO driver V2
ARM: mach-shmobile: sh73a0: fixup PINT/IRQ16-IRQ31 irq number conflict
ARM: mach-shmobile: clock-r8a7740: use followparent_recalc on usb24s
ARM: mach-shmobile: clock-r8a7740: add MMCIF clock
ARM: mach-shmobile: clock-r8a7740: add SDHI clock
ARM: mach-shmobile: clock-r8a7740: add USB clock
ARM: mach-shmobile: clock-r8a7740: add FSI clock
ARM: mach-shmobile: r8a7740: cleanup I2C workaround method
ARM: mach-shmobile: r8a7740: add gpio_irq support
ARM: mach-shmobile: sh7372: Add FSI DMAEngine support
ARM / mach-shmobile: Use preset_lpj with calibrate_delay()
ARM: ux500: ioremap differences for DB9540
ARM: ux500: core U9540 support
...
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__ */ |