diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e7d8cdd..bd10591 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -83,6 +83,7 @@ bool "Marvell EBU platforms" select COMMON_CLK select CLKDEV_LOOKUP + select GPIOLIB select HAS_DEBUG_LL config ARCH_MXS diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile index 4267307..2e924fd 100644 --- a/arch/arm/boards/Makefile +++ b/arch/arm/boards/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_MACH_USB_A9260) += usb-a926x/ obj-$(CONFIG_MACH_USB_A9263) += usb-a926x/ obj-$(CONFIG_MACH_USB_A9G20) += usb-a926x/ +obj-$(CONFIG_MACH_USI_TOPKICK) += usi-topkick/ obj-$(CONFIG_MACH_VERSATILEPB) += versatile/ obj-$(CONFIG_MACH_VEXPRESS) += vexpress/ obj-$(CONFIG_MACH_ZEDBOARD) += avnet-zedboard/ diff --git a/arch/arm/boards/usi-topkick/Makefile b/arch/arm/boards/usi-topkick/Makefile new file mode 100644 index 0000000..dcfc293 --- /dev/null +++ b/arch/arm/boards/usi-topkick/Makefile @@ -0,0 +1 @@ +obj-y += board.o diff --git a/arch/arm/boards/usi-topkick/board.c b/arch/arm/boards/usi-topkick/board.c new file mode 100644 index 0000000..7dbe0aa --- /dev/null +++ b/arch/arm/boards/usi-topkick/board.c @@ -0,0 +1,17 @@ +/* + * Copyright + * (C) 2013 Jason Cooper + * + * 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, or (at your option) any later version. + * + * 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. + * + */ + +/* empty */ diff --git a/arch/arm/boards/usi-topkick/kwbimage.cfg b/arch/arm/boards/usi-topkick/kwbimage.cfg new file mode 100644 index 0000000..db590ca --- /dev/null +++ b/arch/arm/boards/usi-topkick/kwbimage.cfg @@ -0,0 +1,35 @@ +VERSION 0 +BOOT_FROM nand +NAND_ECCMODE default +NAND_PAGESZ 00000800 +DATA FFD100e0 1b1b1b9b +DATA FFD01400 4301503E +DATA FFD01404 B9843000 +DATA FFD01408 33137773 +DATA FFD0140C 16000C3A +DATA FFD01410 06000000 +DATA FFD01414 00000000 +DATA FFD01418 00000000 +DATA FFD0141C 00000672 +DATA FFD01420 00000004 +DATA FFD01424 0000F14F +DATA FFD01428 000D6C70 +DATA FFD0147C 00006071 +DATA FFD01504 1FFFFFF1 +DATA FFD01508 10000000 +DATA FFD0150C 0FFFFFF4 +DATA FFD01514 00000000 +DATA FFD0151C 00000000 +DATA FFD01494 00120000 +DATA FFD01498 00000000 +DATA FFD0149C 0000E803 +DATA FFD015D0 00000630 +DATA FFD015D4 00000046 +DATA FFD015D8 00000008 +DATA FFD015DC 00000000 +DATA FFD015E0 00000023 +DATA FFD015E4 00203C18 +DATA FFD01620 00680000 +DATA FFD01480 00000001 +DATA FFD20134 66666666 +DATA FFD20138 66666666 diff --git a/arch/arm/configs/solidrun_cubox_defconfig b/arch/arm/configs/solidrun_cubox_defconfig index 1a27d81..61195ae 100644 --- a/arch/arm/configs/solidrun_cubox_defconfig +++ b/arch/arm/configs/solidrun_cubox_defconfig @@ -1,9 +1,81 @@ +CONFIG_BUILTIN_DTB=y +CONFIG_BUILTIN_DTB_NAME="dove-cubox" CONFIG_ARCH_MVEBU=y CONFIG_ARCH_DOVE=y CONFIG_AEABI=y +CONFIG_CMD_ARM_MMUINFO=y +CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y +CONFIG_LONGHELP=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y CONFIG_DEBUG_LL=y +CONFIG_CONSOLE_ACTIVATE_NONE=y +CONFIG_CMD_EDIT=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_MSLEEP=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_PRINTENV=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_LET=y +CONFIG_CMD_TIME=y +CONFIG_CMD_GLOBAL=y +CONFIG_CMD_AUTOMOUNT=y +CONFIG_CMD_BASENAME=y +CONFIG_CMD_DIRNAME=y +CONFIG_CMD_LN=y +CONFIG_CMD_READLINK=y +CONFIG_CMD_FILETYPE=y +CONFIG_CMD_ECHO_E=y CONFIG_CMD_LOADY=y CONFIG_CMD_LOADS=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_IOMEM=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_SHA1SUM=y +CONFIG_CMD_SHA256SUM=y +CONFIG_CMD_SHA224SUM=y +CONFIG_CMD_BOOTM_SHOW_TYPE=y +CONFIG_CMD_BOOTM_VERBOSE=y +CONFIG_CMD_BOOTM_INITRD=y +CONFIG_CMD_BOOTM_OFTREE=y +CONFIG_CMD_BOOTM_OFTREE_UIMAGE=y +CONFIG_CMD_UIMAGE=y CONFIG_CMD_RESET=y +CONFIG_CMD_GO=y +CONFIG_CMD_OFTREE=y +CONFIG_CMD_OF_PROPERTY=y +CONFIG_CMD_OF_NODE=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_PARTITION=y +CONFIG_CMD_UNCOMPRESS=y +CONFIG_CMD_I2C=y +CONFIG_CMD_SPI=y +CONFIG_CMD_LED=y +CONFIG_CMD_LED_TRIGGER=y CONFIG_CMD_CLK=y +CONFIG_CMD_DETECT=y +CONFIG_CMD_WD=y +CONFIG_OFDEVICE=y CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_DRIVER_SPI_MVEBU=y +CONFIG_I2C=y +CONFIG_MTD=y +CONFIG_MTD_M25P80=y +CONFIG_DISK_AHCI=y +CONFIG_USB=y +CONFIG_USB_EHCI=y +CONFIG_USB_STORAGE=y +CONFIG_MCI=y +CONFIG_MCI_STARTUP=y +CONFIG_MCI_MMC_BOOT_PARTITIONS=y +CONFIG_LED=y +CONFIG_LED_TRIGGERS=y +CONFIG_WATCHDOG=y +CONFIG_FS_CRAMFS=y +CONFIG_FS_EXT4=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_FAT_LFN=y +CONFIG_BZLIB=y +CONFIG_LZO_DECOMPRESS=y diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 76c9296..77645ff 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -8,6 +8,7 @@ imx6dl-mba6x.dtb \ imx6q-mba6x.dtb \ imx6q-phytec-pbab01.dtb +dtb-$(CONFIG_ARCH_MVEBU) += dove-cubox.dtb BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB_NAME)) obj-$(CONFIG_BUILTIN_DTB) += $(BUILTIN_DTB).dtb.o diff --git a/arch/arm/dts/dove-cubox.dts b/arch/arm/dts/dove-cubox.dts new file mode 100644 index 0000000..dd575eb --- /dev/null +++ b/arch/arm/dts/dove-cubox.dts @@ -0,0 +1,155 @@ +/dts-v1/; + +/include/ "dove.dtsi" + +/ { + compatible = "solidrun,cubox", "marvell,dove"; + model = "SolidRun CuBox"; + + memory { + device_type = "memory"; + reg = <0x00000000 0x3f000000>; + }; + + chosen { + bootargs = "console=ttyS0,115200n8 earlyprintk"; + linux,stdoutpath = &uart0; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-0 = <&pmx_gpio_18>; + pinctrl-names = "default"; + + power { + label = "Power"; + gpios = <&gpio0 18 1>; + linux,default-trigger = "default-on"; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + usb_power: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "USB Power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + regulator-always-on; + regulator-boot-on; + gpio = <&gpio0 1 0>; + }; + }; + + clocks { + /* 25MHz reference crystal */ + ref25: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + }; + + video-card { + compatible = "marvell,dove-video-card"; + reg = <0x3f000000 0x1000000>; + marvell,external-encoder = <&tda19988>; + }; +}; + +&uart0 { status = "okay"; }; +&sata0 { status = "okay"; }; + +&lcd0 { + status = "okay"; + clocks = <&si5351 0>; +}; + +&i2c0 { + status = "okay"; + clock-frequency = <400000>; + + si5351: clock-generator { + compatible = "silabs,si5351a-msop"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + + /* connect xtal input to 25MHz reference */ + clocks = <&ref25>; + + /* connect xtal input as source of pll0 and pll1 */ + silabs,pll-source = <0 0>, <1 0>; + + clkout0 { + reg = <0>; + silabs,drive-strength = <8>; + silabs,multisynth-source = <0>; + silabs,clock-source = <0>; + silabs,pll-master; + }; + + clkout1 { + reg = <1>; + silabs,drive-strength = <8>; + silabs,multisynth-source = <1>; + silabs,clock-source = <0>; + silabs,pll-master; + }; + + clkout2 { + reg = <2>; + silabs,multisynth-source = <1>; + silabs,clock-source = <0>; + }; + }; + + tda19988: hdmi-encoder@70 { + compatible = "nxp,tda1998x"; + reg = <0x70>; + }; +}; + +&sdio0 { + status = "okay"; + bus-width = <4>; + /* sdio0 card detect is connected to wrong pin on CuBox */ + cd-gpios = <&gpio0 12 1>; +}; + +&spi0 { + status = "okay"; + + /* spi0.0: 4M Flash Winbond W25Q32BV */ + spi-flash@0 { + compatible = "winbond,w25q32", "m25p80"; + spi-max-frequency = <20000000>; + reg = <0>; + }; +}; + +&pinctrl { + pinctrl-0 = <&pmx_gpio_1 &pmx_gpio_12>; + pinctrl-names = "default"; + + pmx_gpio_1: pmx-gpio-1 { + marvell,pins = "mpp1"; + marvell,function = "gpio"; + }; + + pmx_gpio_12: pmx-gpio-12 { + marvell,pins = "mpp12"; + marvell,function = "gpio"; + }; + + pmx_gpio_18: pmx-gpio-18 { + marvell,pins = "mpp18"; + marvell,function = "gpio"; + }; +}; diff --git a/arch/arm/dts/dove.dtsi b/arch/arm/dts/dove.dtsi new file mode 100644 index 0000000..4ee8db0 --- /dev/null +++ b/arch/arm/dts/dove.dtsi @@ -0,0 +1,285 @@ +/include/ "skeleton.dtsi" + +/ { + compatible = "marvell,dove"; + model = "Marvell Armada 88AP510 SoC"; + #address-cells = <1>; + #size-cells = <1>; + + aliases { + gpio0 = &gpio0; + gpio1 = &gpio1; + gpio2 = &gpio2; + }; + + soc@f1000000 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&intc>; + + ranges = <0xc8000000 0xc8000000 0x0100000 /* CESA SRAM 1M */ + 0xe0000000 0xe0000000 0x8000000 /* PCIe0 Mem 128M */ + 0xe8000000 0xe8000000 0x8000000 /* PCIe1 Mem 128M */ + 0xf0000000 0xf0000000 0x0100000 /* ScratchPad 1M */ + 0x00000000 0xf1000000 0x1000000 /* SB/NB regs 16M */ + 0xf2000000 0xf2000000 0x0100000 /* PCIe0 I/O 1M */ + 0xf2100000 0xf2100000 0x0100000 /* PCIe0 I/O 1M */ + 0xf8000000 0xf8000000 0x8000000>; /* BootROM 128M */ + + l2: l2-cache { + compatible = "marvell,tauros2-cache"; + marvell,tauros2-cache-features = <0>; + }; + + timer: timer@20300 { + compatible = "marvell,orion-timer"; + reg = <0x20300 0x30>; + }; + + intc: interrupt-controller@20204 { + compatible = "marvell,orion-intc"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x20204 0x04>, <0x20214 0x04>; + }; + + core_clk: core-clocks@d0214 { + compatible = "marvell,dove-core-clock"; + reg = <0xd0214 0x4>; + #clock-cells = <1>; + }; + + gate_clk: clock-gating-control@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + clocks = <&core_clk 0>; + #clock-cells = <1>; + }; + + thermal: thermal@d001c { + compatible = "marvell,dove-thermal"; + reg = <0xd001c 0x0c>, <0xd005c 0x08>; + }; + + uart0: serial@12000 { + compatible = "ns16550a"; + reg = <0x12000 0x20>; + reg-shift = <2>; + interrupts = <7>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + uart1: serial@12100 { + compatible = "ns16550a"; + reg = <0x12100 0x20>; + reg-shift = <2>; + interrupts = <8>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + uart2: serial@12200 { + compatible = "ns16550a"; + reg = <0x12000 0x20>; + reg-shift = <2>; + interrupts = <9>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + uart3: serial@12300 { + compatible = "ns16550a"; + reg = <0x12100 0x20>; + reg-shift = <2>; + interrupts = <10>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + gpio0: gpio@d0400 { + compatible = "marvell,orion-gpio"; + #gpio-cells = <2>; + gpio-controller; + reg = <0xd0400 0x20>; + ngpios = <32>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <12>, <13>, <14>, <60>; + }; + + gpio1: gpio@d0420 { + compatible = "marvell,orion-gpio"; + #gpio-cells = <2>; + gpio-controller; + reg = <0xd0420 0x20>; + ngpios = <32>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <61>; + }; + + gpio2: gpio@e8400 { + compatible = "marvell,orion-gpio"; + #gpio-cells = <2>; + gpio-controller; + reg = <0xe8400 0x0c>; + ngpios = <8>; + }; + + pinctrl: pinctrl@d0200 { + compatible = "marvell,dove-pinctrl"; + reg = <0xd0200 0x10>; + clocks = <&gate_clk 22>; + }; + + spi0: spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + interrupts = <6>; + reg = <0x10600 0x28>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + spi1: spi@14600 { + compatible = "marvell,dove-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + interrupts = <5>; + reg = <0x14600 0x28>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + i2c0: i2c@11000 { + compatible = "marvell,mv64xxx-i2c"; + reg = <0x11000 0x20>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <11>; + clock-frequency = <400000>; + timeout-ms = <1000>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + ehci0: usb-host@50000 { + compatible = "marvell,orion-ehci"; + reg = <0x50000 0x1000>; + interrupts = <24>; + clocks = <&gate_clk 0>; + status = "okay"; + }; + + ehci1: usb-host@51000 { + compatible = "marvell,orion-ehci"; + reg = <0x51000 0x1000>; + interrupts = <25>; + clocks = <&gate_clk 1>; + status = "okay"; + }; + + sdio0: sdio@92000 { + compatible = "marvell,dove-sdhci"; + reg = <0x92000 0x100>; + interrupts = <35>, <37>; + clocks = <&gate_clk 8>; + status = "disabled"; + }; + + sdio1: sdio@90000 { + compatible = "marvell,dove-sdhci"; + reg = <0x90000 0x100>; + interrupts = <36>, <38>; + clocks = <&gate_clk 9>; + status = "disabled"; + }; + + sata0: sata@a0000 { + compatible = "marvell,orion-sata"; + reg = <0xa0000 0x2400>; + interrupts = <62>; + clocks = <&gate_clk 3>; + nr-ports = <1>; + status = "disabled"; + }; + + rtc@d8500 { + compatible = "marvell,orion-rtc"; + reg = <0xd8500 0x20>; + }; + + crypto: crypto@30000 { + compatible = "marvell,orion-crypto"; + reg = <0x30000 0x10000>, + <0xc8000000 0x800>; + reg-names = "regs", "sram"; + interrupts = <31>; + clocks = <&gate_clk 15>; + status = "okay"; + }; + + xor0: dma-engine@60800 { + compatible = "marvell,orion-xor"; + reg = <0x60800 0x100 + 0x60a00 0x100>; + clocks = <&gate_clk 23>; + status = "okay"; + + channel0 { + interrupts = <39>; + dmacap,memcpy; + dmacap,xor; + }; + + channel1 { + interrupts = <40>; + dmacap,memset; + dmacap,memcpy; + dmacap,xor; + }; + }; + + xor1: dma-engine@60900 { + compatible = "marvell,orion-xor"; + reg = <0x60900 0x100 + 0x60b00 0x100>; + clocks = <&gate_clk 24>; + status = "okay"; + + channel0 { + interrupts = <42>; + dmacap,memcpy; + dmacap,xor; + }; + + channel1 { + interrupts = <43>; + dmacap,memset; + dmacap,memcpy; + dmacap,xor; + }; + }; + + lcd0: lcd-controller@820000 { + compatible = "marvell,dove-lcd"; + reg = <0x820000 0x200>; + interrupts = <47>; + clocks = <0>; + status = "disabled"; + }; + + lcd1: lcd-controller@810000 { + compatible = "marvell,dove-lcd"; + reg = <0x810000 0x200>; + interrupts = <46>; + clocks = <0>; + status = "disabled"; + }; + }; +}; diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 11e4550..7fb7d04 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -5,6 +5,7 @@ default 0x2000000 if MACH_PLATHOME_OPENBLOCKS_AX3 default 0x2000000 if MACH_GLOBALSCALE_MIRABOX default 0x2000000 if MACH_GLOBALSCALE_GURUPLUG + default 0x2000000 if MACH_USI_TOPKICK default 0x2000000 if MACH_MARVELL_ARMADA_XP_GP default 0x2000000 if MACH_SOLIDRUN_CUBOX @@ -14,6 +15,7 @@ default "Globalscale Guruplug" if MACH_GLOBALSCALE_GURUPLUG default "Marvell Armada XP GP" if MACH_MARVELL_ARMADA_XP_GP default "SolidRun CuBox" if MACH_SOLIDRUN_CUBOX + default "USI Topkick" if MACH_USI_TOPKICK choice prompt "Marvell EBU Processor" @@ -103,6 +105,9 @@ config MACH_GLOBALSCALE_GURUPLUG bool "Guruplug" +config MACH_USI_TOPKICK + bool "Topkick" + endchoice endif # ARCH_KIRKWOOD diff --git a/arch/arm/mach-mvebu/armada-370-xp.c b/arch/arm/mach-mvebu/armada-370-xp.c index 2cdc3b0..24720d5 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.c +++ b/arch/arm/mach-mvebu/armada-370-xp.c @@ -91,7 +91,7 @@ { /* On Armada XP, the TCLK frequency is always 250 Mhz */ tclk = clk_fixed("tclk", 250000000); - return clk_register_clkdev(tclk, NULL, "mvebu-timer"); + return 0; } #define armada_370_xp_init_clocks() armada_xp_init_clocks() #endif @@ -101,6 +101,7 @@ unsigned long phys_base, phys_size; armada_370_xp_init_clocks(); + clkdev_add_physbase(tclk, (unsigned int)ARMADA_370_XP_TIMER_BASE, NULL); add_generic_device("mvebu-timer", DEVICE_ID_SINGLE, NULL, (unsigned int)ARMADA_370_XP_TIMER_BASE, 0x30, IORESOURCE_MEM, NULL); @@ -109,7 +110,7 @@ armada_370_xp_add_uart(); return 0; } -postcore_initcall(armada_370_xp_init_soc); +core_initcall(armada_370_xp_init_soc); void __noreturn reset_cpu(unsigned long addr) { diff --git a/arch/arm/mach-mvebu/dove.c b/arch/arm/mach-mvebu/dove.c index 6e8e113..16ee116 100644 --- a/arch/arm/mach-mvebu/dove.c +++ b/arch/arm/mach-mvebu/dove.c @@ -114,7 +114,7 @@ } tclk = clk_fixed("tclk", rate); - return clk_register_clkdev(tclk, NULL, "orion-timer"); + return 0; } static int dove_init_soc(void) @@ -123,6 +123,9 @@ dove_remap_mc_regs(); dove_init_clocks(); + clkdev_add_physbase(tclk, (unsigned int)DOVE_TIMER_BASE, NULL); + clkdev_add_physbase(tclk, (unsigned int)DOVE_SPI0_BASE, NULL); + clkdev_add_physbase(tclk, (unsigned int)DOVE_SPI1_BASE, NULL); add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL, (unsigned int)DOVE_TIMER_BASE, 0x30, IORESOURCE_MEM, NULL); @@ -132,7 +135,7 @@ return 0; } -postcore_initcall(dove_init_soc); +core_initcall(dove_init_soc); void __noreturn reset_cpu(unsigned long addr) { diff --git a/arch/arm/mach-mvebu/include/mach/dove-regs.h b/arch/arm/mach-mvebu/include/mach/dove-regs.h index 519457e..8b4319b 100644 --- a/arch/arm/mach-mvebu/include/mach/dove-regs.h +++ b/arch/arm/mach-mvebu/include/mach/dove-regs.h @@ -33,6 +33,9 @@ #define DOVE_UART_BASE (DOVE_INT_REGS_BASE + 0x12000) #define DOVE_UARTn_BASE(n) (DOVE_UART_BASE + ((n) * 0x100)) +#define DOVE_SPI0_BASE (DOVE_INT_REGS_BASE + 0x10600) +#define DOVE_SPI1_BASE (DOVE_INT_REGS_BASE + 0x14600) + #define DOVE_BRIDGE_BASE (DOVE_INT_REGS_BASE + 0x20000) #define INT_REGS_BASE_MAP 0x080 #define BRIDGE_RSTOUT_MASK 0x108 diff --git a/arch/arm/mach-mvebu/include/mach/gpio.h b/arch/arm/mach-mvebu/include/mach/gpio.h new file mode 100644 index 0000000..fee693e --- /dev/null +++ b/arch/arm/mach-mvebu/include/mach/gpio.h @@ -0,0 +1,6 @@ +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#include + +#endif /* __ASM_ARCH_GPIO_H */ diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index 3e16f41..2ec175a 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c @@ -78,7 +78,7 @@ rate = 200000000; tclk = clk_fixed("tclk", rate); - return clk_register_clkdev(tclk, NULL, "orion-timer"); + return 0; } static int kirkwood_init_soc(void) @@ -86,6 +86,7 @@ unsigned long phys_base, phys_size; kirkwood_init_clocks(); + clkdev_add_physbase(tclk, (unsigned int)KIRKWOOD_TIMER_BASE, NULL); add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL, (unsigned int)KIRKWOOD_TIMER_BASE, 0x30, IORESOURCE_MEM, NULL); @@ -95,7 +96,7 @@ return 0; } -postcore_initcall(kirkwood_init_soc); +core_initcall(kirkwood_init_soc); void __noreturn reset_cpu(unsigned long addr) { diff --git a/commands/of_node.c b/commands/of_node.c index e60ef66..b1894b1 100644 --- a/commands/of_node.c +++ b/commands/of_node.c @@ -87,7 +87,7 @@ return -ENOENT; } - of_free(node); + of_delete_node(node); } return 0; diff --git a/commands/oftree.c b/commands/oftree.c index 9149517..00e54dc 100644 --- a/commands/oftree.c +++ b/commands/oftree.c @@ -86,7 +86,7 @@ struct device_node *root = of_get_root_node(); if (root) - of_free(root); + of_delete_node(root); return 0; } @@ -162,7 +162,7 @@ goto out; } of_print_nodes(root, 0); - of_free(root); + of_delete_node(root); } else { struct device_node *n = of_find_node_by_path(node); if (!n) { diff --git a/drivers/clocksource/mvebu.c b/drivers/clocksource/mvebu.c index 8bedd99..e0c85ed 100644 --- a/drivers/clocksource/mvebu.c +++ b/drivers/clocksource/mvebu.c @@ -57,8 +57,7 @@ u32 val; timer_base = dev_request_mem_region(dev, 0); - - tclk = clk_get(dev, "tclk"); + tclk = clk_get(dev, NULL); val = __raw_readl(timer_base + TIMER_CTRL_OFF); val &= ~TIMER0_25MHZ; @@ -78,9 +77,15 @@ return 0; } +static struct of_device_id mvebu_timer_dt_ids[] = { + { .compatible = "marvell,armada-370-xp-timer", }, + { } +}; + static struct driver_d mvebu_timer_driver = { .name = "mvebu-timer", .probe = mvebu_timer_probe, + .of_compatible = DRV_OF_COMPAT(mvebu_timer_dt_ids), }; static int mvebu_timer_init(void) diff --git a/drivers/clocksource/orion.c b/drivers/clocksource/orion.c index e3db845..46932f1 100644 --- a/drivers/clocksource/orion.c +++ b/drivers/clocksource/orion.c @@ -49,7 +49,7 @@ uint32_t val; timer_base = dev_request_mem_region(dev, 0); - tclk = clk_get(dev, "tclk"); + tclk = clk_get(dev, NULL); /* setup TIMER0 as free-running clock source */ __raw_writel(~0, timer_base + TIMER0_VAL); @@ -64,9 +64,15 @@ return 0; } +static struct of_device_id orion_timer_dt_ids[] = { + { .compatible = "marvell,orion-timer", }, + { } +}; + static struct driver_d orion_timer_driver = { .name = "orion-timer", .probe = orion_timer_probe, + .of_compatible = DRV_OF_COMPAT(orion_timer_dt_ids), }; static int orion_timer_init(void) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e976db4..d5ac532 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -30,6 +30,14 @@ config GPIO_IMX def_bool ARCH_IMX +config GPIO_ORION + bool "GPIO support for Marvell Orion/MVEBU SoCs" + depends on ARCH_MVEBU + help + Say yes here to add the driver for the GPIO controller + found on Marvell Orion and MVEBU SoCs (Armada 370/XP, + Dove, Kirkwood, MV78x00, Orion5x). + config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c1ec5bf..adb668f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_IMX) += gpio-imx.o +obj-$(CONFIG_GPIO_ORION) += gpio-orion.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o diff --git a/drivers/gpio/gpio-orion.c b/drivers/gpio/gpio-orion.c new file mode 100644 index 0000000..855763e --- /dev/null +++ b/drivers/gpio/gpio-orion.c @@ -0,0 +1,132 @@ +/* + * Marvell Orion/MVEBU SoC GPIO driver + * + * Sebastian Hesselbarth + * + * 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, or + * (at your option) any later version. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +struct orion_gpio_regs { + u32 data_o; + u32 data_o_en; + u32 blink; + u32 data_i_pol; + u32 data_i; + u32 irq_cause; + u32 irq_mask; + u32 irq_level_mask; +}; + +struct orion_gpio_chip { + struct gpio_chip chip; + struct orion_gpio_regs __iomem *regs; +}; + +static int orion_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct orion_gpio_chip *gpio = + container_of(chip, struct orion_gpio_chip, chip); + writel(readl(&gpio->regs->data_o_en) | BIT(off), + &gpio->regs->data_o_en); + return 0; +} + +static int orion_gpio_direction_output( + struct gpio_chip *chip, unsigned off, int value) +{ + struct orion_gpio_chip *gpio = + container_of(chip, struct orion_gpio_chip, chip); + gpio->chip.ops->set(chip, off, value); + writel(readl(&gpio->regs->data_o_en) & ~BIT(off), + &gpio->regs->data_o_en); + return 0; +} + +static int orion_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct orion_gpio_chip *gpio = + container_of(chip, struct orion_gpio_chip, chip); + return (readl(&gpio->regs->data_i) & BIT(off)) ? 1 : 0; +} + +static void orion_gpio_set_value( + struct gpio_chip *chip, unsigned off, int value) +{ + struct orion_gpio_chip *gpio = + container_of(chip, struct orion_gpio_chip, chip); + u32 val; + + val = readl(&gpio->regs->data_o); + if (value) + val |= BIT(off); + else + val &= ~BIT(off); + writel(val, &gpio->regs->data_o); +} + +static struct gpio_ops orion_gpio_ops = { + .direction_input = orion_gpio_direction_input, + .direction_output = orion_gpio_direction_output, + .get = orion_gpio_get_value, + .set = orion_gpio_set_value, +}; + +static int orion_gpio_probe(struct device_d *dev) +{ + struct orion_gpio_chip *gpio; + + dev->id = of_alias_get_id(dev->device_node, "gpio"); + if (dev->id < 0) + return dev->id; + + gpio = xzalloc(sizeof(*gpio)); + gpio->regs = dev_request_mem_region(dev, 0); + if (!gpio->regs) { + free(gpio); + return -EINVAL; + } + gpio->chip.dev = dev; + gpio->chip.ops = &orion_gpio_ops; + gpio->chip.base = dev->id * 32; + gpio->chip.ngpio = 32; + of_property_read_u32(dev->device_node, "ngpios", &gpio->chip.ngpio); + + gpiochip_add(&gpio->chip); + + dev_dbg(dev, "probed gpio%d with base %d\n", dev->id, gpio->chip.base); + + return 0; +} + +static struct of_device_id orion_gpio_dt_ids[] = { + { .compatible = "marvell,orion-gpio", }, + { } +}; + +static struct driver_d orion_gpio_driver = { + .name = "orion-gpio", + .probe = orion_gpio_probe, + .of_compatible = DRV_OF_COMPAT(orion_gpio_dt_ids), +}; + +static int orion_gpio_init(void) +{ + return platform_driver_register(&orion_gpio_driver); +} +postcore_initcall(orion_gpio_init); diff --git a/drivers/of/Makefile b/drivers/of/Makefile index c81bbec..e7d0733 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,5 @@ -obj-y += base.o fdt.o +obj-y += address.o base.o fdt.o platform.o obj-$(CONFIG_OFTREE_MEM_GENERIC) += mem_generic.o -obj-$(CONFIG_GPIOLIB) += gpio.o +obj-$(CONFIG_GPIOLIB) += of_gpio.o obj-y += partition.o obj-y += of_net.o diff --git a/drivers/of/address.c b/drivers/of/address.c new file mode 100644 index 0000000..4cacdb1 --- /dev/null +++ b/drivers/of/address.c @@ -0,0 +1,437 @@ +/* + * address.c - address related devicetree functions + * + * Copyright (c) 2012 Sascha Hauer , Pengutronix + * + * based on Linux devicetree support + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ +#include +#include +#include + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) +#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void of_dump_addr(const char *s, const __be32 *addr, int na) +{ + printk(KERN_DEBUG "%s", s); + while (na--) + printk(" %08x", be32_to_cpu(*(addr++))); + printk("\n"); +} +#else +static void of_dump_addr(const char *s, const __be32 *addr, int na) { } +#endif + +/* Callbacks for bus specific translators */ +struct of_bus { + const char *name; + const char *addresses; + int (*match)(struct device_node *parent); + void (*count_cells)(struct device_node *child, + int *addrc, int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, + int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); + unsigned int (*get_flags)(const __be32 *addr); +}; + +/* + * Default translator (generic bus) + */ + +static void of_bus_default_count_cells(struct device_node *dev, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = of_n_addr_cells(dev); + if (sizec) + *sizec = of_n_size_cells(dev); +} + +static u64 of_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_number(range, na); + s = of_read_number(range + na + pna, ns); + da = of_read_number(addr, na); + + pr_debug("OF: default map, cp=%llx, s=%llx, da=%llx\n", + (unsigned long long)cp, (unsigned long long)s, + (unsigned long long)da); + + /* + * If the number of address cells is larger than 2 we assume the + * mapping doesn't specify a physical address. Rather, the address + * specifies an identifier that must match exactly. + */ + if (na > 2 && memcmp(range, addr, na * 4) != 0) + return OF_BAD_ADDR; + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = of_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_be32(a >> 32); + addr[na - 1] = cpu_to_be32(a & 0xffffffffu); + + return 0; +} + +static unsigned int of_bus_default_get_flags(const __be32 *addr) +{ + return IORESOURCE_MEM; +} + +/* + * Array of bus specific translators + */ + +static struct of_bus of_busses[] = { + /* Default */ + { + .name = "default", + .addresses = "reg", + .match = NULL, + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, + .get_flags = of_bus_default_get_flags, + }, +}; + +static struct of_bus *of_match_bus(struct device_node *np) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(of_busses); i++) + if (!of_busses[i].match || of_busses[i].match(np)) + return &of_busses[i]; + BUG(); + return NULL; +} + +static int of_translate_one(struct device_node *parent, struct of_bus *bus, + struct of_bus *pbus, __be32 *addr, + int na, int ns, int pna, const char *rprop) +{ + const __be32 *ranges; + unsigned int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + /* Normally, an absence of a "ranges" property means we are + * crossing a non-translatable boundary, and thus the addresses + * below the current not cannot be converted to CPU physical ones. + * Unfortunately, while this is very clear in the spec, it's not + * what Apple understood, and they do have things like /uni-n or + * /ht nodes with no "ranges" property and a lot of perfectly + * useable mapped devices below them. Thus we treat the absence of + * "ranges" as equivalent to an empty "ranges" property which means + * a 1:1 translation at that level. It's up to the caller not to try + * to translate addresses that aren't supposed to be translated in + * the first place. --BenH. + * + * As far as we know, this damage only exists on Apple machines, so + * This code is only enabled on powerpc. --gcl + */ + ranges = of_get_property(parent, rprop, &rlen); +#if !defined(CONFIG_PPC) + if (ranges == NULL) { + pr_err("OF: no ranges; cannot translate\n"); + return 1; + } +#endif /* !defined(CONFIG_PPC) */ + if (ranges == NULL || rlen == 0) { + offset = of_read_number(addr, na); + memset(addr, 0, pna * 4); + pr_debug("OF: empty ranges; 1:1 translation\n"); + goto finish; + } + + pr_debug("OF: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + pr_debug("OF: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("OF: parent translation for:", addr, pna); + pr_debug("OF: with offset: %llx\n", (unsigned long long)offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +static u64 __of_translate_address(struct device_node *dev, + const __be32 *in_addr, const char *rprop) +{ + struct device_node *parent = NULL; + struct of_bus *bus, *pbus; + __be32 addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + pr_debug("OF: ** translation for device %s **\n", dev->full_name); + + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + return OF_BAD_ADDR; + bus = of_match_bus(parent); + + /* Count address cells & copy address locally */ + bus->count_cells(dev, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printk(KERN_ERR "prom_parse: Bad cell count for %s\n", + dev->full_name); + return OF_BAD_ADDR; + } + memcpy(addr, in_addr, na * 4); + + pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", + bus->name, na, ns, parent->full_name); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + dev = parent; + parent = of_get_parent(dev); + + /* If root, we have finished */ + if (parent == NULL) { + pr_debug("OF: reached root node\n"); + result = of_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = of_match_bus(parent); + pbus->count_cells(dev, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printk(KERN_ERR "prom_parse: Bad cell count for %s\n", + dev->full_name); + break; + } + + pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", + pbus->name, pna, pns, parent->full_name); + + /* Apply bus translation */ + if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("OF: one level translation:", addr, na); + } + + return result; +} + +u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) +{ + return __of_translate_address(dev, in_addr, "ranges"); +} +EXPORT_SYMBOL(of_translate_address); + +u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) +{ + return __of_translate_address(dev, in_addr, "dma-ranges"); +} +EXPORT_SYMBOL(of_translate_dma_address); + +bool of_can_translate_address(struct device_node *dev) +{ + struct device_node *parent; + struct of_bus *bus; + int na, ns; + + parent = of_get_parent(dev); + if (parent == NULL) + return false; + + bus = of_match_bus(parent); + bus->count_cells(dev, &na, &ns); + + return OF_CHECK_COUNTS(na, ns); +} +EXPORT_SYMBOL(of_can_translate_address); + +const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, + unsigned int *flags) +{ + const __be32 *prop; + unsigned int psize; + struct device_node *parent; + struct of_bus *bus; + int onesize, i, na, ns; + + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + return NULL; + bus = of_match_bus(parent); + bus->count_cells(dev, &na, &ns); + if (!OF_CHECK_ADDR_COUNT(na)) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = of_get_property(dev, bus->addresses, &psize); + if (prop == NULL) + return NULL; + psize /= 4; + + onesize = na + ns; + for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) + if (i == index) { + if (size) + *size = of_read_number(prop + na, ns); + if (flags) + *flags = bus->get_flags(prop); + return prop; + } + return NULL; +} +EXPORT_SYMBOL(of_get_address); + +static int __of_address_to_resource(struct device_node *dev, + const __be32 *addrp, u64 size, unsigned int flags, + const char *name, struct resource *r) +{ + u64 taddr; + + if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + return -EINVAL; + taddr = of_translate_address(dev, addrp); + if (taddr == OF_BAD_ADDR) + return -EINVAL; + memset(r, 0, sizeof(struct resource)); + if (flags & IORESOURCE_IO) { + unsigned long port; + port = pci_address_to_pio(taddr); + if (port == (unsigned long)-1) + return -EINVAL; + r->start = port; + r->end = port + size - 1; + } else { + r->start = taddr; + r->end = taddr + size - 1; + } + r->flags = flags; + r->name = name ? name : dev->full_name; + + return 0; +} + +/** + * of_address_to_resource - Translate device tree address and return as resource + * + * Note that if your address is a PIO address, the conversion will fail if + * the physical address can't be internally converted to an IO token with + * pci_address_to_pio(), that is because it's either called to early or it + * can't be matched to any host bridge IO space + */ +int of_address_to_resource(struct device_node *dev, int index, + struct resource *r) +{ + const __be32 *addrp; + u64 size; + unsigned int flags; + const char *name = NULL; + + addrp = of_get_address(dev, index, &size, &flags); + if (addrp == NULL) + return -EINVAL; + + /* Get optional "reg-names" property to add a name to a resource */ + of_property_read_string_index(dev, "reg-names", index, &name); + + return __of_address_to_resource(dev, addrp, size, flags, name, r); +} +EXPORT_SYMBOL_GPL(of_address_to_resource); + +struct device_node *of_find_matching_node_by_address(struct device_node *from, + const struct of_device_id *matches, + u64 base_address) +{ + struct device_node *dn = of_find_matching_node(from, matches); + struct resource res; + + while (dn) { + if (of_address_to_resource(dn, 0, &res)) + continue; + if (res.start == base_address) + return dn; + dn = of_find_matching_node(dn, matches); + } + + return NULL; +} + +/** + * of_iomap - Maps the memory mapped IO for a given device_node + * @device: the device whose io range will be mapped + * @index: index of the io range + * + * Returns a pointer to the mapped memory + */ +void __iomem *of_iomap(struct device_node *np, int index) +{ + struct resource res; + + if (of_address_to_resource(np, index, &res)) + return NULL; + + return IOMEM(res.start); +} +EXPORT_SYMBOL(of_iomap); diff --git a/drivers/of/base.c b/drivers/of/base.c index 881ac3b..3b8dd0c 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -19,6 +19,7 @@ */ #include #include +#include #include #include #include @@ -72,6 +73,9 @@ struct device_node *of_aliases; +#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1 +#define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 1 + int of_n_addr_cells(struct device_node *np) { const __be32 *ip; @@ -104,21 +108,6 @@ } EXPORT_SYMBOL(of_n_size_cells); -static void of_bus_default_count_cells(struct device_node *dev, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = of_n_addr_cells(dev); - if (sizec) - *sizec = of_n_size_cells(dev); -} - -static void of_bus_count_cells(struct device_node *dev, - int *addrc, int *sizec) -{ - of_bus_default_count_cells(dev, addrc, sizec); -} - struct property *of_find_property(const struct device_node *np, const char *name, int *lenp) { @@ -251,31 +240,6 @@ } EXPORT_SYMBOL_GPL(of_alias_get); -u64 of_translate_address(struct device_node *node, const __be32 *in_addr) -{ - struct property *p; - u64 addr = be32_to_cpu(*in_addr); - - while (1) { - int na, nc; - - if (!node->parent) - return addr; - - node = node->parent; - p = of_find_property(node, "ranges", NULL); - if (!p && node->parent) - return OF_BAD_ADDR; - of_bus_count_cells(node, &na, &nc); - if (na != 1 || nc != 1) { - printk("%s: #size-cells != 1 or #address-cells != 1 " - "currently not supported\n", node->name); - return OF_BAD_ADDR; - } - } -} -EXPORT_SYMBOL(of_translate_address); - /* * of_find_node_by_phandle - Find a node given a phandle * @handle: phandle of the node to find @@ -1580,269 +1544,27 @@ return 0; } -static struct device_d *add_of_amba_device(struct device_node *node) -{ - struct amba_device *dev; - char *name, *at; - - dev = xzalloc(sizeof(*dev)); - - name = xstrdup(node->name); - at = strchr(name, '@'); - if (at) { - *at = 0; - snprintf(dev->dev.name, MAX_DRIVER_NAME, "%s.%s", at + 1, name); - } else { - strncpy(dev->dev.name, node->name, MAX_DRIVER_NAME); - } - - dev->dev.id = DEVICE_ID_SINGLE; - memcpy(&dev->res, &node->resource[0], sizeof(struct resource)); - dev->dev.resource = node->resource; - dev->dev.num_resources = 1; - dev->dev.device_node = node; - node->device = &dev->dev; - - of_property_read_u32(node, "arm,primecell-periphid", &dev->periphid); - - debug("register device 0x%08x\n", node->resource[0].start); - - amba_device_add(dev); - - free(name); - - return &dev->dev; -} - -static struct device_d *add_of_platform_device(struct device_node *node, - struct device_d *parent) -{ - struct device_d *dev; - char *name, *at; - - dev = xzalloc(sizeof(*dev)); - - dev->parent = parent; - - name = xstrdup(node->name); - at = strchr(name, '@'); - if (at) { - *at = 0; - snprintf(dev->name, MAX_DRIVER_NAME, "%s.%s", at + 1, name); - } else { - strncpy(dev->name, node->name, MAX_DRIVER_NAME); - } - - dev->id = DEVICE_ID_SINGLE; - dev->resource = node->resource; - dev->num_resources = node->num_resource; - dev->device_node = node; - node->device = dev; - - debug("register device 0x%08x\n", node->resource[0].start); - - platform_device_register(dev); - - free(name); - - return dev; -} - -static struct device_d *add_of_device(struct device_node *node, - struct device_d *parent) -{ - const struct property *cp; - - if (!of_device_is_available(node)) - return NULL; - - cp = of_get_property(node, "compatible", NULL); - if (!cp) - return NULL; - - if (IS_ENABLED(CONFIG_ARM_AMBA) && - of_device_is_compatible(node, "arm,primecell") == 1) - return add_of_amba_device(node); - else - return add_of_platform_device(node, parent); -} -EXPORT_SYMBOL(add_of_device); - -static u64 dt_mem_next_cell(int s, const __be32 **cellp) -{ - const __be32 *p = *cellp; - - *cellp = p + s; - return of_read_number(p, s); -} - int of_add_memory(struct device_node *node, bool dump) { - int na, nc; - const __be32 *reg, *endp; - int len, r = 0, ret; const char *device_type; + struct resource res; + int n = 0, ret; ret = of_property_read_string(node, "device_type", &device_type); - if (ret) + if (ret || of_node_cmp(device_type, "memory")) return -ENXIO; - if (of_node_cmp(device_type, "memory")) - return -ENXIO; - - of_bus_count_cells(node, &na, &nc); - - reg = of_get_property(node, "reg", &len); - if (!reg) - return -EINVAL; - - endp = reg + (len / sizeof(__be32)); - - while ((endp - reg) >= (na + nc)) { - u64 base, size; - - base = dt_mem_next_cell(na, ®); - size = dt_mem_next_cell(nc, ®); - - if (size == 0) + while (!of_address_to_resource(node, n, &res)) { + if (!resource_size(&res)) continue; - - of_add_memory_bank(node, dump, r, base, size); - - r++; + of_add_memory_bank(node, dump, n, + res.start, resource_size(&res)); + n++; } return 0; } -static struct device_d *add_of_device_resource(struct device_node *node, - struct device_d *parent) -{ - u64 address = 0, size; - struct resource *res, *resp; - struct device_d *dev; - const __be32 *endp, *reg; - const char *resname; - int na, nc, n_resources; - int ret, len, index; - - reg = of_get_property(node, "reg", &len); - if (!reg) - return add_of_device(node, parent); - - of_bus_count_cells(node, &na, &nc); - - n_resources = (len / sizeof(__be32)) / (na + nc); - - res = resp = xzalloc(sizeof(*res) * n_resources); - - endp = reg + (len / sizeof(__be32)); - - index = 0; - - while ((endp - reg) >= (na + nc)) { - address = of_translate_address(node, reg); - if (address == OF_BAD_ADDR) { - ret = -EINVAL; - goto err_free; - } - - reg += na; - size = dt_mem_next_cell(nc, ®); - - resp->start = address; - resp->end = address + size - 1; - resname = NULL; - of_property_read_string_index(node, "reg-names", index, &resname); - if (resname) - resp->name = xstrdup(resname); - resp->flags = IORESOURCE_MEM; - resp++; - index++; - } - - /* - * A device may already be registered as platform_device. - * Instead of registering the same device again, just - * add this node to the existing device. - */ - for_each_device(dev) { - if (!dev->resource) - continue; - if (dev->resource->start == res->start && - dev->resource->end == res->end) { - debug("connecting %s to %s\n", node->name, dev_name(dev)); - node->device = dev; - dev->device_node = node; - node->resource = dev->resource; - ret = 0; - goto err_free; - } - } - - node->resource = res; - node->num_resource = n_resources; - - return add_of_device(node, parent); - -err_free: - free(res); - - return NULL; -} - -void of_free(struct device_node *node) -{ - struct device_node *n, *nt; - struct property *p, *pt; - - if (!node) - return; - - list_for_each_entry_safe(p, pt, &node->properties, list) - of_delete_property(p); - - list_for_each_entry_safe(n, nt, &node->children, parent_list) { - of_free(n); - } - - if (node->parent) { - list_del(&node->parent_list); - list_del(&node->list); - } - - if (node->device) - node->device->device_node = NULL; - else - free(node->resource); - - free(node->name); - free(node->full_name); - free(node); - - if (node == root_node) - of_set_root_node(NULL); -} - -static void __of_probe(struct device_node *node, - const struct of_device_id *matches, - struct device_d *parent) -{ - struct device_node *n; - struct device_d *dev; - - if (node->device) - return; - - dev = add_of_device_resource(node, parent); - - if (!of_match_node(matches, node)) - return; - - list_for_each_entry(n, &node->children, parent_list) - __of_probe(n, matches, dev); -} - static void __of_parse_phandles(struct device_node *node) { struct device_node *n; @@ -1877,7 +1599,7 @@ int of_probe(void) { - struct device_node *memory, *n; + struct device_node *memory; if(!root_node) return -ENODEV; @@ -1891,8 +1613,7 @@ if (memory) of_add_memory(memory, false); - list_for_each_entry(n, &root_node->children, parent_list) - __of_probe(n, of_default_bus_match_table, NULL); + of_platform_populate(root_node, of_default_bus_match_table, NULL); return 0; } @@ -1941,6 +1662,38 @@ return dn; } +void of_delete_node(struct device_node *node) +{ + struct device_node *n, *nt; + struct property *p, *pt; + struct device_d *dev; + + if (!node) + return; + + list_for_each_entry_safe(p, pt, &node->properties, list) + of_delete_property(p); + + list_for_each_entry_safe(n, nt, &node->children, parent_list) + of_delete_node(n); + + if (node->parent) { + list_del(&node->parent_list); + list_del(&node->list); + } + + dev = of_find_device_by_node(node); + if (dev) + dev->device_node = NULL; + + free(node->name); + free(node->full_name); + free(node); + + if (node == root_node) + of_set_root_node(NULL); +} + int of_device_is_stdout_path(struct device_d *dev) { struct device_node *dn; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index afaa4e0..76d6bb1 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -211,7 +211,7 @@ } } err: - of_free(root); + of_delete_node(root); return ERR_PTR(ret); } diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c deleted file mode 100644 index 41e91ec..0000000 --- a/drivers/of/gpio.c +++ /dev/null @@ -1,26 +0,0 @@ -#define DEBUG - -#include -#include -#include -#include - -int of_get_named_gpio(struct device_node *np, - const char *propname, int index) -{ - int ret; - struct of_phandle_args out_args; - - ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", - index, &out_args); - if (ret) { - pr_debug("%s: can't parse gpios property: %d\n", __func__, ret); - return -EINVAL; - } - - ret = gpio_get_num(out_args.np->device, out_args.args[0]); - if (ret < 0) - return ret; - - return ret; -} diff --git a/drivers/of/of_gpio.c b/drivers/of/of_gpio.c new file mode 100644 index 0000000..3afdf0d --- /dev/null +++ b/drivers/of/of_gpio.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +/** + * of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API + * @np: device node to get GPIO from + * @propname: property name containing gpio specifier(s) + * @index: index of the GPIO + * @flags: a flags pointer to fill in + * + * Returns GPIO number to use with GPIO API, or one of the errno value on the + * error condition. If @flags is not NULL the function also fills in flags for + * the GPIO. + */ +int of_get_named_gpio_flags(struct device_node *np, const char *propname, + int index, enum of_gpio_flags *flags) +{ + struct of_phandle_args out_args; + struct device_d *dev; + int ret; + + ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", + index, &out_args); + if (ret) { + pr_err("%s: cannot parse %s property: %d\n", + __func__, propname, ret); + return ret; + } + + dev = of_find_device_by_node(out_args.np); + if (!dev) { + pr_err("%s: unable to find device of node %s: %d\n", + __func__, out_args.np->full_name, ret); + return ret; + } + + ret = gpio_get_num(dev, out_args.args[0]); + if (ret < 0) { + pr_err("%s: unable to get gpio num of device %s: %d\n", + __func__, dev_name(dev), ret); + return ret; + } + + if (flags) + *flags = out_args.args[1]; + + return ret; +} +EXPORT_SYMBOL(of_get_named_gpio_flags); diff --git a/drivers/of/platform.c b/drivers/of/platform.c new file mode 100644 index 0000000..e75a69b --- /dev/null +++ b/drivers/of/platform.c @@ -0,0 +1,321 @@ +/* + * platform.c - bus/device related devicetree functions + * + * Copyright (c) 2012 Sascha Hauer , Pengutronix + * + * based on Linux devicetree support + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ +#include +#include +#include +#include +#include + +/** + * of_find_device_by_node - Find the platform_device associated with a node + * @np: Pointer to device tree node + * + * Returns platform_device pointer, or NULL if not found + */ +struct device_d *of_find_device_by_node(struct device_node *np) +{ + struct device_d *dev; + for_each_device(dev) + if (dev->device_node == np) + return dev; + return NULL; +} +EXPORT_SYMBOL(of_find_device_by_node); + +/** + * of_device_make_bus_id - Use the device node data to assign a unique name + * @dev: pointer to device structure that is linked to a device tree node + * + * This routine will first try using either the dcr-reg or the reg property + * value to derive a unique name. As a last resort it will use the node + * name followed by a unique number. + */ +static void of_device_make_bus_id(struct device_d *dev) +{ + static int bus_no_reg_magic; + struct device_node *np = dev->device_node; + const __be32 *reg, *addrp; + u64 addr; + char *name, *at; + + name = xstrdup(np->name); + at = strchr(name, '@'); + if (at) + *at = '\0'; + +#ifdef CONFIG_PPC_DCR + /* + * If it's a DCR based device, use 'd' for native DCRs + * and 'D' for MMIO DCRs. + */ + reg = of_get_property(np, "dcr-reg", NULL); + if (reg) { +#ifdef CONFIG_PPC_DCR_NATIVE + snprintf(dev->name, MAX_DRIVER_NAME, "d%x.%s", *reg, name); +#else /* CONFIG_PPC_DCR_NATIVE */ + u64 addr = of_translate_dcr_address(np, *reg, NULL); + if (addr != OF_BAD_ADDR) { + snprintf(dev->name, MAX_DRIVER_NAME, "D%llx.%s", + (unsigned long long)addr, name); + free(name); + return; + } +#endif /* !CONFIG_PPC_DCR_NATIVE */ + } +#endif /* CONFIG_PPC_DCR */ + + /* + * For MMIO, get the physical address + */ + reg = of_get_property(np, "reg", NULL); + if (reg) { + if (of_can_translate_address(np)) { + addr = of_translate_address(np, reg); + } else { + addrp = of_get_address(np, 0, NULL, NULL); + if (addrp) + addr = of_read_number(addrp, 1); + else + addr = OF_BAD_ADDR; + } + if (addr != OF_BAD_ADDR) { + snprintf(dev->name, MAX_DRIVER_NAME, "%llx.%s", + (unsigned long long)addr, name); + free(name); + return; + } + } + + /* + * No BusID, use the node name and add a globally incremented counter + */ + snprintf(dev->name, MAX_DRIVER_NAME, "%s.%d", name, bus_no_reg_magic++); + free(name); +} + +/** + * of_platform_device_create - Alloc, initialize and register an of_device + * @np: pointer to node to create device for + * @parent: device model parent device. + * + * Returns pointer to created platform device, or NULL if a device was not + * registered. Unavailable devices will not get registered. + */ +static struct device_d *of_platform_device_create(struct device_node *np, + struct device_d *parent) +{ + struct device_d *dev; + struct resource *res = NULL, temp_res; + int i, j, ret, num_reg = 0, match; + + if (!of_device_is_available(np)) + return NULL; + + /* count the io resources */ + if (of_can_translate_address(np)) + while (of_address_to_resource(np, num_reg, &temp_res) == 0) + num_reg++; + + /* Populate the resource table */ + if (num_reg) { + res = xzalloc(sizeof(*res) * num_reg); + for (i = 0; i < num_reg; i++) { + ret = of_address_to_resource(np, i, &res[i]); + if (ret) { + free(res); + return NULL; + } + } + + /* + * A device may already be registered as platform_device. + * Instead of registering the same device again, just + * add this node to the existing device. + */ + for_each_device(dev) { + if (!dev->resource) + continue; + + for (i = 0, match = 0; i < num_reg; i++) + for (j = 0; j < dev->num_resources; j++) + if (dev->resource[j].start == + res[i].start && + dev->resource[j].end == + res[i].end) { + match++; + break; + } + + /* check if all address resources match */ + if (match == num_reg) { + debug("connecting %s to %s\n", + np->name, dev_name(dev)); + dev->device_node = np; + free(res); + return dev; + } + } + } + + debug("register device 0x%08x\n", + (num_reg) ? dev->resource[0].start : (-1)); + + /* setup generic device info */ + dev = xzalloc(sizeof(*dev)); + dev->id = DEVICE_ID_SINGLE; + dev->device_node = np; + dev->parent = parent; + dev->resource = res; + dev->num_resources = num_reg; + of_device_make_bus_id(dev); + + ret = platform_device_register(dev); + if (!ret) + return dev; + + free(dev); + if (num_reg) + free(res); + return NULL; +} + +#ifdef CONFIG_ARM_AMBA +static struct device_d *of_amba_device_create(struct device_node *np) +{ + struct amba_device *dev; + int ret; + + debug("Creating amba device %s\n", np->full_name); + + if (!of_device_is_available(np)) + return NULL; + + dev = xzalloc(sizeof(*dev)); + + /* setup generic device info */ + dev->dev.id = DEVICE_ID_SINGLE; + dev->dev.device_node = np; + of_device_make_bus_id(&dev->dev); + + ret = of_address_to_resource(np, 0, &dev->res); + if (ret) + goto amba_err_free; + + dev->dev.resource = &dev->res; + dev->dev.num_resources = 1; + + /* Allow the HW Peripheral ID to be overridden */ + of_property_read_u32(np, "arm,primecell-periphid", &dev->periphid); + + debug("register device 0x%08x\n", dev->dev.resource[0].start); + + ret = amba_device_add(dev); + if (ret) + goto amba_err_free; + + return &dev->dev; + +amba_err_free: + free(dev); + return NULL; +} +#else /* CONFIG_ARM_AMBA */ +static inline struct amba_device *of_amba_device_create(struct device_node *np) +{ + return NULL; +} +#endif /* CONFIG_ARM_AMBA */ + +/** + * of_platform_bus_create() - Create a device for a node and its children. + * @bus: device node of the bus to instantiate + * @matches: match table for bus nodes + * @parent: parent for new device, or NULL for top level. + * + * Creates a platform_device for the provided device_node, and optionally + * recursively create devices for all the child nodes. + */ +static int of_platform_bus_create(struct device_node *bus, + const struct of_device_id *matches, + struct device_d *parent) +{ + struct device_node *child; + struct device_d *dev; + int rc = 0; + + /* Make sure it has a compatible property */ + if (!of_get_property(bus, "compatible", NULL)) { + pr_debug("%s() - skipping %s, no compatible prop\n", + __func__, bus->full_name); + return 0; + } + + if (of_device_is_compatible(bus, "arm,primecell")) { + of_amba_device_create(bus); + return 0; + } + + dev = of_platform_device_create(bus, parent); + if (!dev || !of_match_node(matches, bus)) + return 0; + + for_each_child_of_node(bus, child) { + pr_debug(" create child: %s\n", child->full_name); + rc = of_platform_bus_create(child, matches, dev); + if (rc) + break; + } + return rc; +} + +/** + * of_platform_populate() - Populate platform_devices from device tree data + * @root: parent of the first level to probe or NULL for the root of the tree + * @matches: match table, NULL to use the default + * @parent: parent to hook devices from, NULL for toplevel + * + * This function walks the device tree given by @root node and creates devices + * from nodes. It requires all device nodes to have a 'compatible' property, + * and it is suitable for creating devices which are children of the root + * node. + * + * Returns 0 on success, < 0 on failure. + */ +int of_platform_populate(struct device_node *root, + const struct of_device_id *matches, + struct device_d *parent) +{ + struct device_node *child; + int rc = 0; + + if (!root) + root = of_find_node_by_path("/"); + if (!root) + return -EINVAL; + + for_each_child_of_node(root, child) { + rc = of_platform_bus_create(child, matches, parent); + if (rc) + break; + } + + return rc; +} +EXPORT_SYMBOL_GPL(of_platform_populate); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c279c21..422693c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -38,6 +38,10 @@ depends on ARCH_IMX23 || ARCH_IMX28 depends on SPI +config DRIVER_SPI_MVEBU + bool "Marvell MVEBU SoC SPI master driver" + depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_DOVE || ARCH_KIRKWOOD + config DRIVER_SPI_OMAP3 bool "OMAP3 McSPI Master driver" depends on ARCH_OMAP3 || ARCH_AM33XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 642b7ec..1036f8f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_SPI) += spi.o obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o +obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c index b749337..6f942bf 100644 --- a/drivers/spi/imx_spi.c +++ b/drivers/spi/imx_spi.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/spi/mvebu_spi.c b/drivers/spi/mvebu_spi.c new file mode 100644 index 0000000..7aaa9fe --- /dev/null +++ b/drivers/spi/mvebu_spi.c @@ -0,0 +1,382 @@ +/* + * Marvell MVEBU SoC SPI controller + * compatible with Dove, Kirkwood, MV78x00, Armada 370/XP + * + * Sebastian Hesselbarth + * + * 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, or (at your option) any later version. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_IF_CTRL 0x00 +#define IF_CS_NUM(x) ((x) << 2) +#define IF_CS_NUM_MASK IF_CS_NUM(7) +#define IF_READ_READY BIT(1) +#define IF_CS_ENABLE BIT(0) +#define SPI_IF_CONFIG 0x04 +#define IF_CLK_DIV(x) ((x) << 11) +#define IF_CLK_DIV_MASK (0x7 << 11) +#define IF_FAST_READ BIT(10) +#define IF_ADDRESS_LEN_4BYTE (3 << 8) +#define IF_ADDRESS_LEN_3BYTE (2 << 8) +#define IF_ADDRESS_LEN_2BYTE (1 << 8) +#define IF_ADDRESS_LEN_1BYTE (0 << 8) +#define IF_CLK_PRESCALE_POW8 BIT(7) +#define IF_CLK_PRESCALE_POW4 BIT(6) +#define IF_TRANSFER_2BYTE BIT(5) +#define IF_CLK_PRESCALE_POW2 BIT(4) +#define IF_CLK_PRESCALE(x) ((x) & 0x0f) +#define IF_CLK_PRE_PRESCALE(x) (((((x) & 0xc) << 1) | ((x) & 0x1)) << 4) +#define IF_CLK_PRESCALE_MASK (IF_CLK_PRESCALE(7) | IF_CLK_PRE_PRESCALE(7)) +#define SPI_DATA_OUT 0x08 +#define SPI_DATA_IN 0x0c +#define SPI_INT_CAUSE 0x10 +#define SPI_INT_MASK 0x14 +#define INT_READ_READY BIT(0) + +#define SPI_SPI_MAX_CS 8 + +struct mvebu_spi { + struct spi_master master; + void __iomem *base; + struct clk *clk; + bool data16; + int (*set_baudrate)(struct mvebu_spi *p, u32 speed); +}; + +#define priv_from_spi_device(s) \ + container_of(s->master, struct mvebu_spi, master); + +static inline int mvebu_spi_set_cs(struct mvebu_spi *p, u8 cs, u8 mode, bool en) +{ + u32 val; + + /* + * Only Armada 370/XP support up to 8 CS signals, for the + * others this register bits are read-only + */ + if (cs > SPI_SPI_MAX_CS) + return -EINVAL; + + if (mode & SPI_CS_HIGH) + en = !en; + + val = IF_CS_NUM(cs); + if (en) + val |= IF_CS_ENABLE; + + writel(val, p->base + SPI_IF_CTRL); + + return 0; +} + +static int mvebu_spi_set_transfer_size(struct mvebu_spi *p, int size) +{ + u32 val; + + if (size != 8 && size != 16) + return -EINVAL; + + p->data16 = (size == 16); + + val = readl(p->base + SPI_IF_CONFIG) & ~IF_TRANSFER_2BYTE; + if (p->data16) + val |= IF_TRANSFER_2BYTE; + writel(val, p->base + SPI_IF_CONFIG); + + return 0; +} + +static int mvebu_spi_set_baudrate(struct mvebu_spi *p, u32 speed) +{ + u32 pscl, val; + + /* standard prescaler values: 1,2,4,6,...,30 */ + pscl = DIV_ROUND_UP(clk_get_rate(p->clk), speed); + pscl = roundup(pscl, 2); + + dev_dbg(p->master.dev, "%s: clk = %lu, speed = %u, pscl = %d\n", + __func__, clk_get_rate(p->clk), speed, pscl); + + if (pscl > 30) + return -EINVAL; + + val = readl(p->base + SPI_IF_CONFIG) & ~(IF_CLK_PRESCALE_MASK); + val |= IF_CLK_PRESCALE_POW2 | IF_CLK_PRESCALE(pscl/2); + writel(val, p->base + SPI_IF_CONFIG); + + return 0; +} + +#if defined(CONFIG_ARCH_ARMADA_370) || defined(CONFIG_ARCH_ARMADA_XP) +static int armada_370_xp_spi_set_baudrate(struct mvebu_spi *p, u32 speed) +{ + u32 pscl, pdiv, rate, val; + + /* prescaler values: 1,2,3,...,15 */ + pscl = DIV_ROUND_UP(clk_get_rate(p->clk), speed); + + /* additional prescaler divider: 1, 2, 4, 8, 16, 32, 64, 128 */ + pdiv = 0; rate = pscl; + while (rate > 15 && pdiv <= 7) { + rate /= 2; + pdiv++; + } + + dev_dbg(p->master.dev, "%s: clk = %lu, speed = %u, pscl = %d, pdiv = %d\n", + __func__, clk_get_rate(p->clk), speed, pscl, pdiv); + + if (rate > 15 || pdiv > 7) + return -EINVAL; + + val = readl(p->base + SPI_IF_CONFIG) & ~(IF_CLK_PRESCALE_MASK); + val |= IF_CLK_PRE_PRESCALE(pdiv) | IF_CLK_PRESCALE(pscl); + writel(val, p->base + SPI_IF_CONFIG); + + return 0; +} +#endif + +#if defined(CONFIG_ARCH_DOVE) +static int dove_spi_set_baudrate(struct mvebu_spi *p, u32 speed) +{ + u32 pscl, sdiv, rate, val; + + /* prescaler values: 1,2,3,...,15 and 1,2,4,6,...,30 */ + pscl = DIV_ROUND_UP(clk_get_rate(p->clk), speed); + if (pscl > 15) + pscl = roundup(pscl, 2); + + /* additional sclk divider: 1, 2, 4, 8, 16 */ + sdiv = 0; rate = pscl; + while (rate > 30 && sdiv <= 4) { + rate /= 2; + sdiv++; + } + + dev_dbg(p->master.dev, "%s: clk = %lu, speed = %u, pscl = %d, sdiv = %d\n", + __func__, clk_get_rate(p->clk), speed, pscl, sdiv); + + if (rate > 30 || sdiv > 4) + return -EINVAL; + + val = readl(p->base + SPI_IF_CONFIG) & + ~(IF_CLK_DIV_MASK | IF_CLK_PRESCALE_MASK); + + val |= IF_CLK_DIV(sdiv); + if (pscl > 15) + val |= IF_CLK_PRESCALE_POW2 | IF_CLK_PRESCALE(pscl/2); + else + val |= IF_CLK_PRESCALE(pscl); + writel(val, p->base + SPI_IF_CONFIG); + + return 0; +} +#endif + +static int mvebu_spi_set_mode(struct mvebu_spi *p, u8 mode) +{ + /* + * From public datasheets of Orion SoCs, it is unclear + * if the SPI controller supports setting CPOL/CPHA. + * Dove has an SCK_INV but as with the other SoCs, it + * is tagged with "Must be 1". + * + * For now, we just bail out if device requests any + * other mode than SPI_MODE0. + */ + + if ((mode & (SPI_CPOL|SPI_CPHA)) == SPI_MODE_0) + return 0; + + pr_err("%s: unsupported SPI mode %02x\n", __func__, mode); + + return -EINVAL; +} + +static int mvebu_spi_setup(struct spi_device *spi) +{ + int ret; + struct mvebu_spi *priv = priv_from_spi_device(spi); + + dev_dbg(&spi->dev, "%s: mode %02x, bits_per_word = %d, speed = %d\n", + __func__, spi->mode, spi->bits_per_word, spi->max_speed_hz); + + ret = mvebu_spi_set_cs(priv, spi->chip_select, spi->mode, false); + if (ret) + return ret; + ret = mvebu_spi_set_mode(priv, spi->mode); + if (ret) + return ret; + ret = mvebu_spi_set_transfer_size(priv, spi->bits_per_word); + if (ret) + return ret; + + return priv->set_baudrate(priv, spi->max_speed_hz); +} + +static inline int mvebu_spi_wait_for_read_ready(struct mvebu_spi *p) +{ + int timeout = 100; + while ((readl(p->base + SPI_IF_CTRL) & IF_READ_READY) == 0 && + timeout--) + udelay(1); + if (timeout < 0) + return -EIO; + return 0; +} + +static int mvebu_spi_do_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + const u8 *txdata = t->tx_buf; + u8 *rxdata = t->rx_buf; + int ret = 0, n, inc; + struct mvebu_spi *priv = priv_from_spi_device(spi); + + if (t->bits_per_word) + ret = mvebu_spi_set_transfer_size(priv, spi->bits_per_word); + if (ret) + return ret; + + if (t->speed_hz) + ret = priv->set_baudrate(priv, t->speed_hz); + if (ret) + return ret; + + inc = (priv->data16) ? 2 : 1; + for (n = 0; n < t->len; n += inc) { + u32 data = 0; + + if (txdata) + data = *txdata++; + if (txdata && priv->data16) + data |= (*txdata++ << 8); + + writel(data, priv->base + SPI_DATA_OUT); + + ret = mvebu_spi_wait_for_read_ready(priv); + if (ret) { + dev_err(&spi->dev, "timeout reading from device %s\n", + dev_name(&spi->dev)); + return ret; + } + + data = readl(priv->base + SPI_DATA_IN); + + if (rxdata) + *rxdata++ = (data & 0xff); + if (rxdata && priv->data16) + *rxdata++ = (data >> 8) & 0xff; + } + + return 0; +} + +static int mvebu_spi_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct spi_transfer *t; + int ret; + struct mvebu_spi *priv = priv_from_spi_device(spi); + + ret = mvebu_spi_set_cs(priv, spi->chip_select, spi->mode, true); + if (ret) + return ret; + + msg->actual_length = 0; + + list_for_each_entry(t, &msg->transfers, transfer_list) { + ret = mvebu_spi_do_transfer(spi, t); + if (ret) + break; + msg->actual_length += t->len; + } + + ret = mvebu_spi_set_cs(priv, spi->chip_select, spi->mode, false); + if (ret) + return ret; + + return ret; +} + +static struct of_device_id mvebu_spi_dt_ids[] = { + { .compatible = "marvell,orion-spi", + .data = (unsigned long)&mvebu_spi_set_baudrate }, +#if defined(CONFIG_ARCH_ARMADA_370) || defined(CONFIG_ARCH_ARMADA_XP) + { .compatible = "marvell,armada-370-xp-spi", + .data = (unsigned long)&armada_370_xp_spi_set_baudrate }, +#endif +#if defined(CONFIG_ARCH_DOVE) + { .compatible = "marvell,dove-spi", + .data = (unsigned long)&dove_spi_set_baudrate }, +#endif + { } +}; + +static int mvebu_spi_probe(struct device_d *dev) +{ + struct spi_master *master; + struct mvebu_spi *priv; + const struct of_device_id *match; + int ret = 0; + + match = of_match_node(mvebu_spi_dt_ids, dev->device_node); + if (!match) + return -EINVAL; + + priv = xzalloc(sizeof(*priv)); + priv->base = dev_request_mem_region(dev, 0); + if (!priv->base) { + ret = -EINVAL; + goto err_free; + } + priv->set_baudrate = (void *)match->data; + priv->clk = clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto err_free; + } + + master = &priv->master; + master->dev = dev; + master->bus_num = dev->id; + master->setup = mvebu_spi_setup; + master->transfer = mvebu_spi_transfer; + master->num_chipselect = 1; + + if (dev->device_node) + spi_of_register_slaves(master, dev->device_node); + + ret = spi_register_master(master); + if (!ret) + return 0; + +err_free: + free(priv); + + return ret; +} + +static struct driver_d mvebu_spi_driver = { + .name = "mvebu-spi", + .probe = mvebu_spi_probe, + .of_compatible = DRV_OF_COMPAT(mvebu_spi_dt_ids), +}; +device_platform_driver(mvebu_spi_driver); diff --git a/include/of.h b/include/of.h index b392ca9..ebe8e39 100644 --- a/include/of.h +++ b/include/of.h @@ -30,9 +30,6 @@ struct list_head children; struct list_head parent_list; struct list_head list; - struct resource *resource; - int num_resource; - struct device_d *device; struct list_head phandles; phandle phandle; }; @@ -61,6 +58,7 @@ void of_clean_reserve_map(void); void fdt_add_reserve_map(void *fdt); +struct device_d; struct driver_d; int of_fix_tree(struct device_node *); @@ -95,17 +93,9 @@ } } -int of_get_named_gpio(struct device_node *np, - const char *propname, int index); - void of_print_property(const void *data, int len); void of_print_cmdline(struct device_node *root); -u64 of_translate_address(struct device_node *node, const __be32 *in_addr); - -#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1 -#define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 1 - void of_print_nodes(struct device_node *node, int indent); int of_probe(void); int of_parse_dtb(struct fdt_header *fdt); @@ -149,7 +139,7 @@ const char *name); extern struct device_node *of_create_node(struct device_node *root, const char *path); -extern void of_free(struct device_node *node); +extern void of_delete_node(struct device_node *node); extern int of_machine_is_compatible(const char *compat); extern int of_device_is_compatible(const struct device_node *device, @@ -226,6 +216,11 @@ extern struct device_node *of_get_root_node(void); extern int of_set_root_node(struct device_node *node); +extern int of_platform_populate(struct device_node *root, + const struct of_device_id *matches, + struct device_d *parent); +extern struct device_d *of_find_device_by_node(struct device_node *np); + int of_parse_partitions(struct cdev *cdev, struct device_node *node); int of_device_is_stdout_path(struct device_d *dev); const char *of_get_model(void); @@ -513,7 +508,7 @@ return NULL; } -static inline void of_free(struct device_node *node) +static inline void of_delete_node(struct device_node *node) { } @@ -552,6 +547,18 @@ { return -ENOSYS; } + +static inline int of_platform_populate(struct device_node *root, + const struct of_device_id *matches, + struct device_d *parent) +{ + return -ENOSYS; +} + +static inline struct device_d *of_find_device_by_node(struct device_node *np) +{ + return NULL; +} #endif #define for_each_node_by_name(dn, name) \ diff --git a/include/of_address.h b/include/of_address.h new file mode 100644 index 0000000..9022ab7 --- /dev/null +++ b/include/of_address.h @@ -0,0 +1,72 @@ +#ifndef __OF_ADDRESS_H +#define __OF_ADDRESS_H + +#include +#include + +#ifndef pci_address_to_pio +static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; } +#endif + +#ifdef CONFIG_OFTREE + +extern u64 of_translate_address(struct device_node *dev, + const __be32 *in_addr); +extern u64 of_translate_dma_address(struct device_node *dev, + const __be32 *in_addr); +extern bool of_can_translate_address(struct device_node *dev); +extern const __be32 *of_get_address(struct device_node *dev, int index, + u64 *size, unsigned int *flags); +extern int of_address_to_resource(struct device_node *dev, int index, + struct resource *r); +extern struct device_node *of_find_matching_node_by_address( + struct device_node *from, const struct of_device_id *matches, + u64 base_address); +extern void __iomem *of_iomap(struct device_node *np, int index); + +#else /* CONFIG_OFTREE */ + +static inline u64 of_translate_address(struct device_node *dev, + const __be32 *in_addr) +{ + return OF_BAD_ADDR; +} + +static inline u64 of_translate_dma_address(struct device_node *dev, + const __be32 *in_addr) +{ + return OF_BAD_ADDR; +} + +static inline bool of_can_translate_address(struct device_node *dev) +{ + return false; +} + +static inline const __be32 *of_get_address(struct device_node *dev, int index, + u64 *size, unsigned int *flags) +{ + return 0; +} + +static inline int of_address_to_resource(struct device_node *dev, int index, + struct resource *r) +{ + return -ENOSYS; +} + +static inline struct device_node *of_find_matching_node_by_address( + struct device_node *from, const struct of_device_id *matches, + u64 base_address) +{ + return NULL; +} + +static inline void __iomem *of_iomap(struct device_node *np, int index) +{ + return NULL; +} + +#endif /* CONFIG_OFTREE */ + +#endif /* __OF_ADDRESS_H */ diff --git a/include/of_gpio.h b/include/of_gpio.h new file mode 100644 index 0000000..50536a8 --- /dev/null +++ b/include/of_gpio.h @@ -0,0 +1,44 @@ +/* + * OF helpers for the GPIO API + * + * based on Linux OF_GPIO API + * + * 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, or + * (at your option) any later version. + */ + +#ifndef __OF_GPIO_H +#define __OF_GPIO_H + +/* + * This is Linux-specific flags. By default controllers' and Linux' mapping + * match, but GPIO controllers are free to translate their own flags to + * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended. + */ +enum of_gpio_flags { + OF_GPIO_ACTIVE_LOW = 0x1, +}; + +#ifdef CONFIG_OFTREE +extern int of_get_named_gpio_flags(struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags); + +#else /* CONFIG_OFTREE */ + +static inline int of_get_named_gpio_flags(struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags) +{ + return -ENOSYS; +} + +#endif /* CONFIG_OFTREE */ + +static inline int of_get_named_gpio(struct device_node *np, + const char *list_name, int index) +{ + return of_get_named_gpio_flags(np, list_name, index, NULL); +} + +#endif