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"
|
bool "Marvell EBU platforms"
|
||||||
select COMMON_CLK
|
select COMMON_CLK
|
||||||
select CLKDEV_LOOKUP
|
select CLKDEV_LOOKUP
|
||||||
|
select GPIOLIB
|
||||||
select HAS_DEBUG_LL
|
select HAS_DEBUG_LL
|
||||||
|
|
||||||
config ARCH_MXS
|
config ARCH_MXS
|
||||||
|
|
|
@ -91,6 +91,7 @@ obj-$(CONFIG_MACH_TX53) += karo-tx53/
|
||||||
obj-$(CONFIG_MACH_USB_A9260) += usb-a926x/
|
obj-$(CONFIG_MACH_USB_A9260) += usb-a926x/
|
||||||
obj-$(CONFIG_MACH_USB_A9263) += usb-a926x/
|
obj-$(CONFIG_MACH_USB_A9263) += usb-a926x/
|
||||||
obj-$(CONFIG_MACH_USB_A9G20) += usb-a926x/
|
obj-$(CONFIG_MACH_USB_A9G20) += usb-a926x/
|
||||||
|
obj-$(CONFIG_MACH_USI_TOPKICK) += usi-topkick/
|
||||||
obj-$(CONFIG_MACH_VERSATILEPB) += versatile/
|
obj-$(CONFIG_MACH_VERSATILEPB) += versatile/
|
||||||
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
|
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
|
||||||
obj-$(CONFIG_MACH_ZEDBOARD) += avnet-zedboard/
|
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_MVEBU=y
|
||||||
CONFIG_ARCH_DOVE=y
|
CONFIG_ARCH_DOVE=y
|
||||||
CONFIG_AEABI=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_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_LOADY=y
|
||||||
CONFIG_CMD_LOADS=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_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_CLK=y
|
||||||
|
CONFIG_CMD_DETECT=y
|
||||||
|
CONFIG_CMD_WD=y
|
||||||
|
CONFIG_OFDEVICE=y
|
||||||
CONFIG_DRIVER_SERIAL_NS16550=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 \
|
imx6dl-mba6x.dtb \
|
||||||
imx6q-mba6x.dtb \
|
imx6q-mba6x.dtb \
|
||||||
imx6q-phytec-pbab01.dtb
|
imx6q-phytec-pbab01.dtb
|
||||||
|
dtb-$(CONFIG_ARCH_MVEBU) += dove-cubox.dtb
|
||||||
|
|
||||||
BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB_NAME))
|
BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB_NAME))
|
||||||
obj-$(CONFIG_BUILTIN_DTB) += $(BUILTIN_DTB).dtb.o
|
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_PLATHOME_OPENBLOCKS_AX3
|
||||||
default 0x2000000 if MACH_GLOBALSCALE_MIRABOX
|
default 0x2000000 if MACH_GLOBALSCALE_MIRABOX
|
||||||
default 0x2000000 if MACH_GLOBALSCALE_GURUPLUG
|
default 0x2000000 if MACH_GLOBALSCALE_GURUPLUG
|
||||||
|
default 0x2000000 if MACH_USI_TOPKICK
|
||||||
default 0x2000000 if MACH_MARVELL_ARMADA_XP_GP
|
default 0x2000000 if MACH_MARVELL_ARMADA_XP_GP
|
||||||
default 0x2000000 if MACH_SOLIDRUN_CUBOX
|
default 0x2000000 if MACH_SOLIDRUN_CUBOX
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ config BOARDINFO
|
||||||
default "Globalscale Guruplug" if MACH_GLOBALSCALE_GURUPLUG
|
default "Globalscale Guruplug" if MACH_GLOBALSCALE_GURUPLUG
|
||||||
default "Marvell Armada XP GP" if MACH_MARVELL_ARMADA_XP_GP
|
default "Marvell Armada XP GP" if MACH_MARVELL_ARMADA_XP_GP
|
||||||
default "SolidRun CuBox" if MACH_SOLIDRUN_CUBOX
|
default "SolidRun CuBox" if MACH_SOLIDRUN_CUBOX
|
||||||
|
default "USI Topkick" if MACH_USI_TOPKICK
|
||||||
|
|
||||||
choice
|
choice
|
||||||
prompt "Marvell EBU Processor"
|
prompt "Marvell EBU Processor"
|
||||||
|
@ -103,6 +105,9 @@ choice
|
||||||
config MACH_GLOBALSCALE_GURUPLUG
|
config MACH_GLOBALSCALE_GURUPLUG
|
||||||
bool "Guruplug"
|
bool "Guruplug"
|
||||||
|
|
||||||
|
config MACH_USI_TOPKICK
|
||||||
|
bool "Topkick"
|
||||||
|
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
endif # ARCH_KIRKWOOD
|
endif # ARCH_KIRKWOOD
|
||||||
|
|
|
@ -91,7 +91,7 @@ static int armada_xp_init_clocks(void)
|
||||||
{
|
{
|
||||||
/* On Armada XP, the TCLK frequency is always 250 Mhz */
|
/* On Armada XP, the TCLK frequency is always 250 Mhz */
|
||||||
tclk = clk_fixed("tclk", 250000000);
|
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()
|
#define armada_370_xp_init_clocks() armada_xp_init_clocks()
|
||||||
#endif
|
#endif
|
||||||
|
@ -101,6 +101,7 @@ static int armada_370_xp_init_soc(void)
|
||||||
unsigned long phys_base, phys_size;
|
unsigned long phys_base, phys_size;
|
||||||
|
|
||||||
armada_370_xp_init_clocks();
|
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,
|
add_generic_device("mvebu-timer", DEVICE_ID_SINGLE, NULL,
|
||||||
(unsigned int)ARMADA_370_XP_TIMER_BASE, 0x30,
|
(unsigned int)ARMADA_370_XP_TIMER_BASE, 0x30,
|
||||||
IORESOURCE_MEM, NULL);
|
IORESOURCE_MEM, NULL);
|
||||||
|
@ -109,7 +110,7 @@ static int armada_370_xp_init_soc(void)
|
||||||
armada_370_xp_add_uart();
|
armada_370_xp_add_uart();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
postcore_initcall(armada_370_xp_init_soc);
|
core_initcall(armada_370_xp_init_soc);
|
||||||
|
|
||||||
void __noreturn reset_cpu(unsigned long addr)
|
void __noreturn reset_cpu(unsigned long addr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -114,7 +114,7 @@ static int dove_init_clocks(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
tclk = clk_fixed("tclk", rate);
|
tclk = clk_fixed("tclk", rate);
|
||||||
return clk_register_clkdev(tclk, NULL, "orion-timer");
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dove_init_soc(void)
|
static int dove_init_soc(void)
|
||||||
|
@ -123,6 +123,9 @@ static int dove_init_soc(void)
|
||||||
|
|
||||||
dove_remap_mc_regs();
|
dove_remap_mc_regs();
|
||||||
dove_init_clocks();
|
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,
|
add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL,
|
||||||
(unsigned int)DOVE_TIMER_BASE, 0x30,
|
(unsigned int)DOVE_TIMER_BASE, 0x30,
|
||||||
IORESOURCE_MEM, NULL);
|
IORESOURCE_MEM, NULL);
|
||||||
|
@ -132,7 +135,7 @@ static int dove_init_soc(void)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
postcore_initcall(dove_init_soc);
|
core_initcall(dove_init_soc);
|
||||||
|
|
||||||
void __noreturn reset_cpu(unsigned long addr)
|
void __noreturn reset_cpu(unsigned long addr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,9 @@
|
||||||
#define DOVE_UART_BASE (DOVE_INT_REGS_BASE + 0x12000)
|
#define DOVE_UART_BASE (DOVE_INT_REGS_BASE + 0x12000)
|
||||||
#define DOVE_UARTn_BASE(n) (DOVE_UART_BASE + ((n) * 0x100))
|
#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 DOVE_BRIDGE_BASE (DOVE_INT_REGS_BASE + 0x20000)
|
||||||
#define INT_REGS_BASE_MAP 0x080
|
#define INT_REGS_BASE_MAP 0x080
|
||||||
#define BRIDGE_RSTOUT_MASK 0x108
|
#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;
|
rate = 200000000;
|
||||||
|
|
||||||
tclk = clk_fixed("tclk", rate);
|
tclk = clk_fixed("tclk", rate);
|
||||||
return clk_register_clkdev(tclk, NULL, "orion-timer");
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kirkwood_init_soc(void)
|
static int kirkwood_init_soc(void)
|
||||||
|
@ -86,6 +86,7 @@ static int kirkwood_init_soc(void)
|
||||||
unsigned long phys_base, phys_size;
|
unsigned long phys_base, phys_size;
|
||||||
|
|
||||||
kirkwood_init_clocks();
|
kirkwood_init_clocks();
|
||||||
|
clkdev_add_physbase(tclk, (unsigned int)KIRKWOOD_TIMER_BASE, NULL);
|
||||||
add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL,
|
add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL,
|
||||||
(unsigned int)KIRKWOOD_TIMER_BASE, 0x30,
|
(unsigned int)KIRKWOOD_TIMER_BASE, 0x30,
|
||||||
IORESOURCE_MEM, NULL);
|
IORESOURCE_MEM, NULL);
|
||||||
|
@ -95,7 +96,7 @@ static int kirkwood_init_soc(void)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
postcore_initcall(kirkwood_init_soc);
|
core_initcall(kirkwood_init_soc);
|
||||||
|
|
||||||
void __noreturn reset_cpu(unsigned long addr)
|
void __noreturn reset_cpu(unsigned long addr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -87,7 +87,7 @@ static int do_of_node(int argc, char *argv[])
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
of_free(node);
|
of_delete_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -86,7 +86,7 @@ static int do_oftree(int argc, char *argv[])
|
||||||
struct device_node *root = of_get_root_node();
|
struct device_node *root = of_get_root_node();
|
||||||
|
|
||||||
if (root)
|
if (root)
|
||||||
of_free(root);
|
of_delete_node(root);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ static int do_oftree(int argc, char *argv[])
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
of_print_nodes(root, 0);
|
of_print_nodes(root, 0);
|
||||||
of_free(root);
|
of_delete_node(root);
|
||||||
} else {
|
} else {
|
||||||
struct device_node *n = of_find_node_by_path(node);
|
struct device_node *n = of_find_node_by_path(node);
|
||||||
if (!n) {
|
if (!n) {
|
||||||
|
|
|
@ -57,8 +57,7 @@ static int mvebu_timer_probe(struct device_d *dev)
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
timer_base = dev_request_mem_region(dev, 0);
|
timer_base = dev_request_mem_region(dev, 0);
|
||||||
|
tclk = clk_get(dev, NULL);
|
||||||
tclk = clk_get(dev, "tclk");
|
|
||||||
|
|
||||||
val = __raw_readl(timer_base + TIMER_CTRL_OFF);
|
val = __raw_readl(timer_base + TIMER_CTRL_OFF);
|
||||||
val &= ~TIMER0_25MHZ;
|
val &= ~TIMER0_25MHZ;
|
||||||
|
@ -78,9 +77,15 @@ static int mvebu_timer_probe(struct device_d *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct of_device_id mvebu_timer_dt_ids[] = {
|
||||||
|
{ .compatible = "marvell,armada-370-xp-timer", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
static struct driver_d mvebu_timer_driver = {
|
static struct driver_d mvebu_timer_driver = {
|
||||||
.name = "mvebu-timer",
|
.name = "mvebu-timer",
|
||||||
.probe = mvebu_timer_probe,
|
.probe = mvebu_timer_probe,
|
||||||
|
.of_compatible = DRV_OF_COMPAT(mvebu_timer_dt_ids),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mvebu_timer_init(void)
|
static int mvebu_timer_init(void)
|
||||||
|
|
|
@ -49,7 +49,7 @@ static int orion_timer_probe(struct device_d *dev)
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
|
|
||||||
timer_base = dev_request_mem_region(dev, 0);
|
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 */
|
/* setup TIMER0 as free-running clock source */
|
||||||
__raw_writel(~0, timer_base + TIMER0_VAL);
|
__raw_writel(~0, timer_base + TIMER0_VAL);
|
||||||
|
@ -64,9 +64,15 @@ static int orion_timer_probe(struct device_d *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct of_device_id orion_timer_dt_ids[] = {
|
||||||
|
{ .compatible = "marvell,orion-timer", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
static struct driver_d orion_timer_driver = {
|
static struct driver_d orion_timer_driver = {
|
||||||
.name = "orion-timer",
|
.name = "orion-timer",
|
||||||
.probe = orion_timer_probe,
|
.probe = orion_timer_probe,
|
||||||
|
.of_compatible = DRV_OF_COMPAT(orion_timer_dt_ids),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int orion_timer_init(void)
|
static int orion_timer_init(void)
|
||||||
|
|
|
@ -30,6 +30,14 @@ config GPIO_GENERIC_PLATFORM
|
||||||
config GPIO_IMX
|
config GPIO_IMX
|
||||||
def_bool ARCH_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
|
config GPIO_PL061
|
||||||
bool "PrimeCell PL061 GPIO support"
|
bool "PrimeCell PL061 GPIO support"
|
||||||
depends on ARM_AMBA
|
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_CLPS711X) += gpio-clps711x.o
|
||||||
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
|
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
|
||||||
obj-$(CONFIG_GPIO_IMX) += gpio-imx.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_PL061) += gpio-pl061.o
|
||||||
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
|
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
|
||||||
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.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_OFTREE_MEM_GENERIC) += mem_generic.o
|
||||||
obj-$(CONFIG_GPIOLIB) += gpio.o
|
obj-$(CONFIG_GPIOLIB) += of_gpio.o
|
||||||
obj-y += partition.o
|
obj-y += partition.o
|
||||||
obj-y += of_net.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 <common.h>
|
||||||
#include <of.h>
|
#include <of.h>
|
||||||
|
#include <of_address.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
|
@ -72,6 +73,9 @@ struct device_node *root_node;
|
||||||
|
|
||||||
struct device_node *of_aliases;
|
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)
|
int of_n_addr_cells(struct device_node *np)
|
||||||
{
|
{
|
||||||
const __be32 *ip;
|
const __be32 *ip;
|
||||||
|
@ -104,21 +108,6 @@ int of_n_size_cells(struct device_node *np)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(of_n_size_cells);
|
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,
|
struct property *of_find_property(const struct device_node *np,
|
||||||
const char *name, int *lenp)
|
const char *name, int *lenp)
|
||||||
{
|
{
|
||||||
|
@ -251,31 +240,6 @@ const char *of_alias_get(struct device_node *np)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_alias_get);
|
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
|
* of_find_node_by_phandle - Find a node given a phandle
|
||||||
* @handle: phandle of the node to find
|
* @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;
|
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 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;
|
const char *device_type;
|
||||||
|
struct resource res;
|
||||||
|
int n = 0, ret;
|
||||||
|
|
||||||
ret = of_property_read_string(node, "device_type", &device_type);
|
ret = of_property_read_string(node, "device_type", &device_type);
|
||||||
if (ret)
|
if (ret || of_node_cmp(device_type, "memory"))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if (of_node_cmp(device_type, "memory"))
|
while (!of_address_to_resource(node, n, &res)) {
|
||||||
return -ENXIO;
|
if (!resource_size(&res))
|
||||||
|
|
||||||
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)
|
|
||||||
continue;
|
continue;
|
||||||
|
of_add_memory_bank(node, dump, n,
|
||||||
of_add_memory_bank(node, dump, r, base, size);
|
res.start, resource_size(&res));
|
||||||
|
n++;
|
||||||
r++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
static void __of_parse_phandles(struct device_node *node)
|
||||||
{
|
{
|
||||||
struct device_node *n;
|
struct device_node *n;
|
||||||
|
@ -1877,7 +1599,7 @@ const struct of_device_id of_default_bus_match_table[] = {
|
||||||
|
|
||||||
int of_probe(void)
|
int of_probe(void)
|
||||||
{
|
{
|
||||||
struct device_node *memory, *n;
|
struct device_node *memory;
|
||||||
|
|
||||||
if(!root_node)
|
if(!root_node)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1891,8 +1613,7 @@ int of_probe(void)
|
||||||
if (memory)
|
if (memory)
|
||||||
of_add_memory(memory, false);
|
of_add_memory(memory, false);
|
||||||
|
|
||||||
list_for_each_entry(n, &root_node->children, parent_list)
|
of_platform_populate(root_node, of_default_bus_match_table, NULL);
|
||||||
__of_probe(n, of_default_bus_match_table, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1941,6 +1662,38 @@ out:
|
||||||
return dn;
|
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)
|
int of_device_is_stdout_path(struct device_d *dev)
|
||||||
{
|
{
|
||||||
struct device_node *dn;
|
struct device_node *dn;
|
||||||
|
|
|
@ -211,7 +211,7 @@ struct device_node *of_unflatten_dtb(struct device_node *root, void *infdt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err:
|
err:
|
||||||
of_free(root);
|
of_delete_node(root);
|
||||||
|
|
||||||
return ERR_PTR(ret);
|
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 ARCH_IMX23 || ARCH_IMX28
|
||||||
depends on SPI
|
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
|
config DRIVER_SPI_OMAP3
|
||||||
bool "OMAP3 McSPI Master driver"
|
bool "OMAP3 McSPI Master driver"
|
||||||
depends on ARCH_OMAP3 || ARCH_AM33XX
|
depends on ARCH_OMAP3 || ARCH_AM33XX
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
obj-$(CONFIG_SPI) += spi.o
|
obj-$(CONFIG_SPI) += spi.o
|
||||||
obj-$(CONFIG_DRIVER_SPI_IMX) += imx_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_MXS) += mxs_spi.o
|
||||||
obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o
|
obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o
|
||||||
obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o
|
obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <gpio.h>
|
#include <gpio.h>
|
||||||
|
#include <of_gpio.h>
|
||||||
#include <mach/spi.h>
|
#include <mach/spi.h>
|
||||||
#include <mach/generic.h>
|
#include <mach/generic.h>
|
||||||
#include <linux/clk.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 children;
|
||||||
struct list_head parent_list;
|
struct list_head parent_list;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct resource *resource;
|
|
||||||
int num_resource;
|
|
||||||
struct device_d *device;
|
|
||||||
struct list_head phandles;
|
struct list_head phandles;
|
||||||
phandle phandle;
|
phandle phandle;
|
||||||
};
|
};
|
||||||
|
@ -61,6 +58,7 @@ struct of_reserve_map *of_get_reserve_map(void);
|
||||||
void of_clean_reserve_map(void);
|
void of_clean_reserve_map(void);
|
||||||
void fdt_add_reserve_map(void *fdt);
|
void fdt_add_reserve_map(void *fdt);
|
||||||
|
|
||||||
|
struct device_d;
|
||||||
struct driver_d;
|
struct driver_d;
|
||||||
|
|
||||||
int of_fix_tree(struct device_node *);
|
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_property(const void *data, int len);
|
||||||
void of_print_cmdline(struct device_node *root);
|
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);
|
void of_print_nodes(struct device_node *node, int indent);
|
||||||
int of_probe(void);
|
int of_probe(void);
|
||||||
int of_parse_dtb(struct fdt_header *fdt);
|
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);
|
const char *name);
|
||||||
extern struct device_node *of_create_node(struct device_node *root,
|
extern struct device_node *of_create_node(struct device_node *root,
|
||||||
const char *path);
|
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_machine_is_compatible(const char *compat);
|
||||||
extern int of_device_is_compatible(const struct device_node *device,
|
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 struct device_node *of_get_root_node(void);
|
||||||
extern int of_set_root_node(struct device_node *node);
|
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_parse_partitions(struct cdev *cdev, struct device_node *node);
|
||||||
int of_device_is_stdout_path(struct device_d *dev);
|
int of_device_is_stdout_path(struct device_d *dev);
|
||||||
const char *of_get_model(void);
|
const char *of_get_model(void);
|
||||||
|
@ -513,7 +508,7 @@ static inline struct device_node *of_create_node(struct device_node *root,
|
||||||
return NULL;
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
#define for_each_node_by_name(dn, name) \
|
#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