Merge branch 'for-next/marvell'
Conflicts: arch/arm/boards/Makefile arch/arm/dts/Makefile
This commit is contained in:
commit
07afe7d0e0
|
@ -83,6 +83,7 @@ config ARCH_MVEBU
|
|||
bool "Marvell EBU platforms"
|
||||
select COMMON_CLK
|
||||
select CLKDEV_LOOKUP
|
||||
select GPIOLIB
|
||||
select HAS_DEBUG_LL
|
||||
|
||||
config ARCH_MXS
|
||||
|
|
|
@ -91,6 +91,7 @@ obj-$(CONFIG_MACH_TX53) += karo-tx53/
|
|||
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/
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
obj-y += board.o
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright
|
||||
* (C) 2013 Jason Cooper <jason@lakedaemon.net>
|
||||
*
|
||||
* 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 */
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -8,6 +8,7 @@ dtb-$(CONFIG_ARCH_IMX6) += imx6q-gk802.dtb \
|
|||
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
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
|
@ -5,6 +5,7 @@ config ARCH_TEXT_BASE
|
|||
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 @@ config BOARDINFO
|
|||
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 @@ choice
|
|||
config MACH_GLOBALSCALE_GURUPLUG
|
||||
bool "Guruplug"
|
||||
|
||||
config MACH_USI_TOPKICK
|
||||
bool "Topkick"
|
||||
|
||||
endchoice
|
||||
|
||||
endif # ARCH_KIRKWOOD
|
||||
|
|
|
@ -91,7 +91,7 @@ static int armada_xp_init_clocks(void)
|
|||
{
|
||||
/* 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 @@ static int armada_370_xp_init_soc(void)
|
|||
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 @@ static int armada_370_xp_init_soc(void)
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -114,7 +114,7 @@ static int dove_init_clocks(void)
|
|||
}
|
||||
|
||||
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 @@ static int dove_init_soc(void)
|
|||
|
||||
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 @@ static int dove_init_soc(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
postcore_initcall(dove_init_soc);
|
||||
core_initcall(dove_init_soc);
|
||||
|
||||
void __noreturn reset_cpu(unsigned long addr)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef __ASM_ARCH_GPIO_H
|
||||
#define __ASM_ARCH_GPIO_H
|
||||
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
#endif /* __ASM_ARCH_GPIO_H */
|
|
@ -78,7 +78,7 @@ static int kirkwood_init_clocks(void)
|
|||
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 @@ static int kirkwood_init_soc(void)
|
|||
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 @@ static int kirkwood_init_soc(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
postcore_initcall(kirkwood_init_soc);
|
||||
core_initcall(kirkwood_init_soc);
|
||||
|
||||
void __noreturn reset_cpu(unsigned long addr)
|
||||
{
|
||||
|
|
|
@ -87,7 +87,7 @@ static int do_of_node(int argc, char *argv[])
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
of_free(node);
|
||||
of_delete_node(node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -86,7 +86,7 @@ static int do_oftree(int argc, char *argv[])
|
|||
struct device_node *root = of_get_root_node();
|
||||
|
||||
if (root)
|
||||
of_free(root);
|
||||
of_delete_node(root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ static int do_oftree(int argc, char *argv[])
|
|||
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) {
|
||||
|
|
|
@ -57,8 +57,7 @@ static int mvebu_timer_probe(struct device_d *dev)
|
|||
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 @@ static int mvebu_timer_probe(struct device_d *dev)
|
|||
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)
|
||||
|
|
|
@ -49,7 +49,7 @@ static int orion_timer_probe(struct device_d *dev)
|
|||
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 @@ static int orion_timer_probe(struct device_d *dev)
|
|||
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)
|
||||
|
|
|
@ -30,6 +30,14 @@ config GPIO_GENERIC_PLATFORM
|
|||
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
|
||||
|
|
|
@ -4,6 +4,7 @@ obj-$(CONFIG_GPIO_BCM2835) += gpio-bcm2835.o
|
|||
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
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Marvell Orion/MVEBU SoC GPIO driver
|
||||
*
|
||||
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||
*
|
||||
* 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 <common.h>
|
||||
#include <errno.h>
|
||||
#include <gpio.h>
|
||||
#include <init.h>
|
||||
#include <io.h>
|
||||
#include <malloc.h>
|
||||
|
||||
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);
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* address.c - address related devicetree functions
|
||||
*
|
||||
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, 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 <common.h>
|
||||
#include <of.h>
|
||||
#include <of_address.h>
|
||||
|
||||
/* 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);
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
#include <common.h>
|
||||
#include <of.h>
|
||||
#include <of_address.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <init.h>
|
||||
|
@ -72,6 +73,9 @@ struct device_node *root_node;
|
|||
|
||||
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 @@ int of_n_size_cells(struct device_node *np)
|
|||
}
|
||||
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 @@ const char *of_alias_get(struct device_node *np)
|
|||
}
|
||||
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 @@ int of_set_property(struct device_node *np, const char *name, const void *val, i
|
|||
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 @@ const struct of_device_id of_default_bus_match_table[] = {
|
|||
|
||||
int of_probe(void)
|
||||
{
|
||||
struct device_node *memory, *n;
|
||||
struct device_node *memory;
|
||||
|
||||
if(!root_node)
|
||||
return -ENODEV;
|
||||
|
@ -1891,8 +1613,7 @@ int of_probe(void)
|
|||
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 @@ out:
|
|||
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;
|
||||
|
|
|
@ -211,7 +211,7 @@ struct device_node *of_unflatten_dtb(struct device_node *root, void *infdt)
|
|||
}
|
||||
}
|
||||
err:
|
||||
of_free(root);
|
||||
of_delete_node(root);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#define DEBUG
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <of.h>
|
||||
#include <gpio.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <of.h>
|
||||
#include <of_gpio.h>
|
||||
#include <gpio.h>
|
||||
|
||||
/**
|
||||
* 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);
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* platform.c - bus/device related devicetree functions
|
||||
*
|
||||
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, 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 <common.h>
|
||||
#include <malloc.h>
|
||||
#include <of.h>
|
||||
#include <of_address.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
/**
|
||||
* 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);
|
|
@ -38,6 +38,10 @@ config DRIVER_SPI_MXS
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <gpio.h>
|
||||
#include <of_gpio.h>
|
||||
#include <mach/spi.h>
|
||||
#include <mach/generic.h>
|
||||
#include <linux/clk.h>
|
||||
|
|
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* Marvell MVEBU SoC SPI controller
|
||||
* compatible with Dove, Kirkwood, MV78x00, Armada 370/XP
|
||||
*
|
||||
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||
*
|
||||
* 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 <common.h>
|
||||
#include <driver.h>
|
||||
#include <errno.h>
|
||||
#include <init.h>
|
||||
#include <io.h>
|
||||
#include <malloc.h>
|
||||
#include <spi/spi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#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);
|
33
include/of.h
33
include/of.h
|
@ -30,9 +30,6 @@ struct device_node {
|
|||
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 @@ struct of_reserve_map *of_get_reserve_map(void);
|
|||
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 @@ static inline void of_write_number(void *__cell, u64 val, int size)
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ extern struct device_node *of_new_node(struct device_node *parent,
|
|||
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 int of_modalias_node(struct device_node *node, char *modalias, int len);
|
|||
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 @@ static inline struct device_node *of_create_node(struct device_node *root,
|
|||
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 @@ static inline int of_modalias_node(struct device_node *node, char *modalias,
|
|||
{
|
||||
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) \
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef __OF_ADDRESS_H
|
||||
#define __OF_ADDRESS_H
|
||||
|
||||
#include <common.h>
|
||||
#include <of.h>
|
||||
|
||||
#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 */
|
|
@ -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
|
Loading…
Reference in New Issue