9
0
Fork 0

Merge branch 'for-next/marvell'

Conflicts:
	arch/arm/boards/Makefile
	arch/arm/dts/Makefile
This commit is contained in:
Sascha Hauer 2013-08-05 12:49:55 +02:00
commit 07afe7d0e0
36 changed files with 2134 additions and 347 deletions

View File

@ -83,6 +83,7 @@ config ARCH_MVEBU
bool "Marvell EBU platforms"
select COMMON_CLK
select CLKDEV_LOOKUP
select GPIOLIB
select HAS_DEBUG_LL
config ARCH_MXS

View File

@ -91,6 +91,7 @@ obj-$(CONFIG_MACH_TX53) += karo-tx53/
obj-$(CONFIG_MACH_USB_A9260) += usb-a926x/
obj-$(CONFIG_MACH_USB_A9263) += usb-a926x/
obj-$(CONFIG_MACH_USB_A9G20) += usb-a926x/
obj-$(CONFIG_MACH_USI_TOPKICK) += usi-topkick/
obj-$(CONFIG_MACH_VERSATILEPB) += versatile/
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
obj-$(CONFIG_MACH_ZEDBOARD) += avnet-zedboard/

View File

@ -0,0 +1 @@
obj-y += board.o

View File

@ -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 */

View File

@ -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

View File

@ -1,9 +1,81 @@
CONFIG_BUILTIN_DTB=y
CONFIG_BUILTIN_DTB_NAME="dove-cubox"
CONFIG_ARCH_MVEBU=y
CONFIG_ARCH_DOVE=y
CONFIG_AEABI=y
CONFIG_CMD_ARM_MMUINFO=y
CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
CONFIG_LONGHELP=y
CONFIG_CMDLINE_EDITING=y
CONFIG_AUTO_COMPLETE=y
CONFIG_DEBUG_LL=y
CONFIG_CONSOLE_ACTIVATE_NONE=y
CONFIG_CMD_EDIT=y
CONFIG_CMD_SLEEP=y
CONFIG_CMD_MSLEEP=y
CONFIG_CMD_SAVEENV=y
CONFIG_CMD_EXPORT=y
CONFIG_CMD_PRINTENV=y
CONFIG_CMD_READLINE=y
CONFIG_CMD_LET=y
CONFIG_CMD_TIME=y
CONFIG_CMD_GLOBAL=y
CONFIG_CMD_AUTOMOUNT=y
CONFIG_CMD_BASENAME=y
CONFIG_CMD_DIRNAME=y
CONFIG_CMD_LN=y
CONFIG_CMD_READLINK=y
CONFIG_CMD_FILETYPE=y
CONFIG_CMD_ECHO_E=y
CONFIG_CMD_LOADY=y
CONFIG_CMD_LOADS=y
CONFIG_CMD_MEMINFO=y
CONFIG_CMD_IOMEM=y
CONFIG_CMD_MD5SUM=y
CONFIG_CMD_SHA1SUM=y
CONFIG_CMD_SHA256SUM=y
CONFIG_CMD_SHA224SUM=y
CONFIG_CMD_BOOTM_SHOW_TYPE=y
CONFIG_CMD_BOOTM_VERBOSE=y
CONFIG_CMD_BOOTM_INITRD=y
CONFIG_CMD_BOOTM_OFTREE=y
CONFIG_CMD_BOOTM_OFTREE_UIMAGE=y
CONFIG_CMD_UIMAGE=y
CONFIG_CMD_RESET=y
CONFIG_CMD_GO=y
CONFIG_CMD_OFTREE=y
CONFIG_CMD_OF_PROPERTY=y
CONFIG_CMD_OF_NODE=y
CONFIG_CMD_TIMEOUT=y
CONFIG_CMD_PARTITION=y
CONFIG_CMD_UNCOMPRESS=y
CONFIG_CMD_I2C=y
CONFIG_CMD_SPI=y
CONFIG_CMD_LED=y
CONFIG_CMD_LED_TRIGGER=y
CONFIG_CMD_CLK=y
CONFIG_CMD_DETECT=y
CONFIG_CMD_WD=y
CONFIG_OFDEVICE=y
CONFIG_DRIVER_SERIAL_NS16550=y
CONFIG_DRIVER_SPI_MVEBU=y
CONFIG_I2C=y
CONFIG_MTD=y
CONFIG_MTD_M25P80=y
CONFIG_DISK_AHCI=y
CONFIG_USB=y
CONFIG_USB_EHCI=y
CONFIG_USB_STORAGE=y
CONFIG_MCI=y
CONFIG_MCI_STARTUP=y
CONFIG_MCI_MMC_BOOT_PARTITIONS=y
CONFIG_LED=y
CONFIG_LED_TRIGGERS=y
CONFIG_WATCHDOG=y
CONFIG_FS_CRAMFS=y
CONFIG_FS_EXT4=y
CONFIG_FS_FAT=y
CONFIG_FS_FAT_WRITE=y
CONFIG_FS_FAT_LFN=y
CONFIG_BZLIB=y
CONFIG_LZO_DECOMPRESS=y

View File

@ -8,6 +8,7 @@ dtb-$(CONFIG_ARCH_IMX6) += imx6q-gk802.dtb \
imx6dl-mba6x.dtb \
imx6q-mba6x.dtb \
imx6q-phytec-pbab01.dtb
dtb-$(CONFIG_ARCH_MVEBU) += dove-cubox.dtb
BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB_NAME))
obj-$(CONFIG_BUILTIN_DTB) += $(BUILTIN_DTB).dtb.o

155
arch/arm/dts/dove-cubox.dts Normal file
View File

@ -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";
};
};

285
arch/arm/dts/dove.dtsi Normal file
View File

@ -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";
};
};
};

View File

@ -5,6 +5,7 @@ config ARCH_TEXT_BASE
default 0x2000000 if MACH_PLATHOME_OPENBLOCKS_AX3
default 0x2000000 if MACH_GLOBALSCALE_MIRABOX
default 0x2000000 if MACH_GLOBALSCALE_GURUPLUG
default 0x2000000 if MACH_USI_TOPKICK
default 0x2000000 if MACH_MARVELL_ARMADA_XP_GP
default 0x2000000 if MACH_SOLIDRUN_CUBOX
@ -14,6 +15,7 @@ config BOARDINFO
default "Globalscale Guruplug" if MACH_GLOBALSCALE_GURUPLUG
default "Marvell Armada XP GP" if MACH_MARVELL_ARMADA_XP_GP
default "SolidRun CuBox" if MACH_SOLIDRUN_CUBOX
default "USI Topkick" if MACH_USI_TOPKICK
choice
prompt "Marvell EBU Processor"
@ -103,6 +105,9 @@ choice
config MACH_GLOBALSCALE_GURUPLUG
bool "Guruplug"
config MACH_USI_TOPKICK
bool "Topkick"
endchoice
endif # ARCH_KIRKWOOD

View File

@ -91,7 +91,7 @@ static int armada_xp_init_clocks(void)
{
/* On Armada XP, the TCLK frequency is always 250 Mhz */
tclk = clk_fixed("tclk", 250000000);
return clk_register_clkdev(tclk, NULL, "mvebu-timer");
return 0;
}
#define armada_370_xp_init_clocks() armada_xp_init_clocks()
#endif
@ -101,6 +101,7 @@ static int armada_370_xp_init_soc(void)
unsigned long phys_base, phys_size;
armada_370_xp_init_clocks();
clkdev_add_physbase(tclk, (unsigned int)ARMADA_370_XP_TIMER_BASE, NULL);
add_generic_device("mvebu-timer", DEVICE_ID_SINGLE, NULL,
(unsigned int)ARMADA_370_XP_TIMER_BASE, 0x30,
IORESOURCE_MEM, NULL);
@ -109,7 +110,7 @@ static int armada_370_xp_init_soc(void)
armada_370_xp_add_uart();
return 0;
}
postcore_initcall(armada_370_xp_init_soc);
core_initcall(armada_370_xp_init_soc);
void __noreturn reset_cpu(unsigned long addr)
{

View File

@ -114,7 +114,7 @@ static int dove_init_clocks(void)
}
tclk = clk_fixed("tclk", rate);
return clk_register_clkdev(tclk, NULL, "orion-timer");
return 0;
}
static int dove_init_soc(void)
@ -123,6 +123,9 @@ static int dove_init_soc(void)
dove_remap_mc_regs();
dove_init_clocks();
clkdev_add_physbase(tclk, (unsigned int)DOVE_TIMER_BASE, NULL);
clkdev_add_physbase(tclk, (unsigned int)DOVE_SPI0_BASE, NULL);
clkdev_add_physbase(tclk, (unsigned int)DOVE_SPI1_BASE, NULL);
add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL,
(unsigned int)DOVE_TIMER_BASE, 0x30,
IORESOURCE_MEM, NULL);
@ -132,7 +135,7 @@ static int dove_init_soc(void)
return 0;
}
postcore_initcall(dove_init_soc);
core_initcall(dove_init_soc);
void __noreturn reset_cpu(unsigned long addr)
{

View File

@ -33,6 +33,9 @@
#define DOVE_UART_BASE (DOVE_INT_REGS_BASE + 0x12000)
#define DOVE_UARTn_BASE(n) (DOVE_UART_BASE + ((n) * 0x100))
#define DOVE_SPI0_BASE (DOVE_INT_REGS_BASE + 0x10600)
#define DOVE_SPI1_BASE (DOVE_INT_REGS_BASE + 0x14600)
#define DOVE_BRIDGE_BASE (DOVE_INT_REGS_BASE + 0x20000)
#define INT_REGS_BASE_MAP 0x080
#define BRIDGE_RSTOUT_MASK 0x108

View File

@ -0,0 +1,6 @@
#ifndef __ASM_ARCH_GPIO_H
#define __ASM_ARCH_GPIO_H
#include <asm-generic/gpio.h>
#endif /* __ASM_ARCH_GPIO_H */

View File

@ -78,7 +78,7 @@ static int kirkwood_init_clocks(void)
rate = 200000000;
tclk = clk_fixed("tclk", rate);
return clk_register_clkdev(tclk, NULL, "orion-timer");
return 0;
}
static int kirkwood_init_soc(void)
@ -86,6 +86,7 @@ static int kirkwood_init_soc(void)
unsigned long phys_base, phys_size;
kirkwood_init_clocks();
clkdev_add_physbase(tclk, (unsigned int)KIRKWOOD_TIMER_BASE, NULL);
add_generic_device("orion-timer", DEVICE_ID_SINGLE, NULL,
(unsigned int)KIRKWOOD_TIMER_BASE, 0x30,
IORESOURCE_MEM, NULL);
@ -95,7 +96,7 @@ static int kirkwood_init_soc(void)
return 0;
}
postcore_initcall(kirkwood_init_soc);
core_initcall(kirkwood_init_soc);
void __noreturn reset_cpu(unsigned long addr)
{

View File

@ -87,7 +87,7 @@ static int do_of_node(int argc, char *argv[])
return -ENOENT;
}
of_free(node);
of_delete_node(node);
}
return 0;

View File

@ -86,7 +86,7 @@ static int do_oftree(int argc, char *argv[])
struct device_node *root = of_get_root_node();
if (root)
of_free(root);
of_delete_node(root);
return 0;
}
@ -162,7 +162,7 @@ static int do_oftree(int argc, char *argv[])
goto out;
}
of_print_nodes(root, 0);
of_free(root);
of_delete_node(root);
} else {
struct device_node *n = of_find_node_by_path(node);
if (!n) {

View File

@ -57,8 +57,7 @@ static int mvebu_timer_probe(struct device_d *dev)
u32 val;
timer_base = dev_request_mem_region(dev, 0);
tclk = clk_get(dev, "tclk");
tclk = clk_get(dev, NULL);
val = __raw_readl(timer_base + TIMER_CTRL_OFF);
val &= ~TIMER0_25MHZ;
@ -78,9 +77,15 @@ static int mvebu_timer_probe(struct device_d *dev)
return 0;
}
static struct of_device_id mvebu_timer_dt_ids[] = {
{ .compatible = "marvell,armada-370-xp-timer", },
{ }
};
static struct driver_d mvebu_timer_driver = {
.name = "mvebu-timer",
.probe = mvebu_timer_probe,
.of_compatible = DRV_OF_COMPAT(mvebu_timer_dt_ids),
};
static int mvebu_timer_init(void)

View File

@ -49,7 +49,7 @@ static int orion_timer_probe(struct device_d *dev)
uint32_t val;
timer_base = dev_request_mem_region(dev, 0);
tclk = clk_get(dev, "tclk");
tclk = clk_get(dev, NULL);
/* setup TIMER0 as free-running clock source */
__raw_writel(~0, timer_base + TIMER0_VAL);
@ -64,9 +64,15 @@ static int orion_timer_probe(struct device_d *dev)
return 0;
}
static struct of_device_id orion_timer_dt_ids[] = {
{ .compatible = "marvell,orion-timer", },
{ }
};
static struct driver_d orion_timer_driver = {
.name = "orion-timer",
.probe = orion_timer_probe,
.of_compatible = DRV_OF_COMPAT(orion_timer_dt_ids),
};
static int orion_timer_init(void)

View File

@ -30,6 +30,14 @@ config GPIO_GENERIC_PLATFORM
config GPIO_IMX
def_bool ARCH_IMX
config GPIO_ORION
bool "GPIO support for Marvell Orion/MVEBU SoCs"
depends on ARCH_MVEBU
help
Say yes here to add the driver for the GPIO controller
found on Marvell Orion and MVEBU SoCs (Armada 370/XP,
Dove, Kirkwood, MV78x00, Orion5x).
config GPIO_PL061
bool "PrimeCell PL061 GPIO support"
depends on ARM_AMBA

View File

@ -4,6 +4,7 @@ obj-$(CONFIG_GPIO_BCM2835) += gpio-bcm2835.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_IMX) += gpio-imx.o
obj-$(CONFIG_GPIO_ORION) += gpio-orion.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o

132
drivers/gpio/gpio-orion.c Normal file
View File

@ -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);

View File

@ -1,5 +1,5 @@
obj-y += base.o fdt.o
obj-y += address.o base.o fdt.o platform.o
obj-$(CONFIG_OFTREE_MEM_GENERIC) += mem_generic.o
obj-$(CONFIG_GPIOLIB) += gpio.o
obj-$(CONFIG_GPIOLIB) += of_gpio.o
obj-y += partition.o
obj-y += of_net.o

437
drivers/of/address.c Normal file
View File

@ -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);

View File

@ -19,6 +19,7 @@
*/
#include <common.h>
#include <of.h>
#include <of_address.h>
#include <errno.h>
#include <malloc.h>
#include <init.h>
@ -72,6 +73,9 @@ struct device_node *root_node;
struct device_node *of_aliases;
#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
#define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 1
int of_n_addr_cells(struct device_node *np)
{
const __be32 *ip;
@ -104,21 +108,6 @@ int of_n_size_cells(struct device_node *np)
}
EXPORT_SYMBOL(of_n_size_cells);
static void of_bus_default_count_cells(struct device_node *dev,
int *addrc, int *sizec)
{
if (addrc)
*addrc = of_n_addr_cells(dev);
if (sizec)
*sizec = of_n_size_cells(dev);
}
static void of_bus_count_cells(struct device_node *dev,
int *addrc, int *sizec)
{
of_bus_default_count_cells(dev, addrc, sizec);
}
struct property *of_find_property(const struct device_node *np,
const char *name, int *lenp)
{
@ -251,31 +240,6 @@ const char *of_alias_get(struct device_node *np)
}
EXPORT_SYMBOL_GPL(of_alias_get);
u64 of_translate_address(struct device_node *node, const __be32 *in_addr)
{
struct property *p;
u64 addr = be32_to_cpu(*in_addr);
while (1) {
int na, nc;
if (!node->parent)
return addr;
node = node->parent;
p = of_find_property(node, "ranges", NULL);
if (!p && node->parent)
return OF_BAD_ADDR;
of_bus_count_cells(node, &na, &nc);
if (na != 1 || nc != 1) {
printk("%s: #size-cells != 1 or #address-cells != 1 "
"currently not supported\n", node->name);
return OF_BAD_ADDR;
}
}
}
EXPORT_SYMBOL(of_translate_address);
/*
* of_find_node_by_phandle - Find a node given a phandle
* @handle: phandle of the node to find
@ -1580,269 +1544,27 @@ int of_set_property(struct device_node *np, const char *name, const void *val, i
return 0;
}
static struct device_d *add_of_amba_device(struct device_node *node)
{
struct amba_device *dev;
char *name, *at;
dev = xzalloc(sizeof(*dev));
name = xstrdup(node->name);
at = strchr(name, '@');
if (at) {
*at = 0;
snprintf(dev->dev.name, MAX_DRIVER_NAME, "%s.%s", at + 1, name);
} else {
strncpy(dev->dev.name, node->name, MAX_DRIVER_NAME);
}
dev->dev.id = DEVICE_ID_SINGLE;
memcpy(&dev->res, &node->resource[0], sizeof(struct resource));
dev->dev.resource = node->resource;
dev->dev.num_resources = 1;
dev->dev.device_node = node;
node->device = &dev->dev;
of_property_read_u32(node, "arm,primecell-periphid", &dev->periphid);
debug("register device 0x%08x\n", node->resource[0].start);
amba_device_add(dev);
free(name);
return &dev->dev;
}
static struct device_d *add_of_platform_device(struct device_node *node,
struct device_d *parent)
{
struct device_d *dev;
char *name, *at;
dev = xzalloc(sizeof(*dev));
dev->parent = parent;
name = xstrdup(node->name);
at = strchr(name, '@');
if (at) {
*at = 0;
snprintf(dev->name, MAX_DRIVER_NAME, "%s.%s", at + 1, name);
} else {
strncpy(dev->name, node->name, MAX_DRIVER_NAME);
}
dev->id = DEVICE_ID_SINGLE;
dev->resource = node->resource;
dev->num_resources = node->num_resource;
dev->device_node = node;
node->device = dev;
debug("register device 0x%08x\n", node->resource[0].start);
platform_device_register(dev);
free(name);
return dev;
}
static struct device_d *add_of_device(struct device_node *node,
struct device_d *parent)
{
const struct property *cp;
if (!of_device_is_available(node))
return NULL;
cp = of_get_property(node, "compatible", NULL);
if (!cp)
return NULL;
if (IS_ENABLED(CONFIG_ARM_AMBA) &&
of_device_is_compatible(node, "arm,primecell") == 1)
return add_of_amba_device(node);
else
return add_of_platform_device(node, parent);
}
EXPORT_SYMBOL(add_of_device);
static u64 dt_mem_next_cell(int s, const __be32 **cellp)
{
const __be32 *p = *cellp;
*cellp = p + s;
return of_read_number(p, s);
}
int of_add_memory(struct device_node *node, bool dump)
{
int na, nc;
const __be32 *reg, *endp;
int len, r = 0, ret;
const char *device_type;
struct resource res;
int n = 0, ret;
ret = of_property_read_string(node, "device_type", &device_type);
if (ret)
if (ret || of_node_cmp(device_type, "memory"))
return -ENXIO;
if (of_node_cmp(device_type, "memory"))
return -ENXIO;
of_bus_count_cells(node, &na, &nc);
reg = of_get_property(node, "reg", &len);
if (!reg)
return -EINVAL;
endp = reg + (len / sizeof(__be32));
while ((endp - reg) >= (na + nc)) {
u64 base, size;
base = dt_mem_next_cell(na, &reg);
size = dt_mem_next_cell(nc, &reg);
if (size == 0)
while (!of_address_to_resource(node, n, &res)) {
if (!resource_size(&res))
continue;
of_add_memory_bank(node, dump, r, base, size);
r++;
of_add_memory_bank(node, dump, n,
res.start, resource_size(&res));
n++;
}
return 0;
}
static struct device_d *add_of_device_resource(struct device_node *node,
struct device_d *parent)
{
u64 address = 0, size;
struct resource *res, *resp;
struct device_d *dev;
const __be32 *endp, *reg;
const char *resname;
int na, nc, n_resources;
int ret, len, index;
reg = of_get_property(node, "reg", &len);
if (!reg)
return add_of_device(node, parent);
of_bus_count_cells(node, &na, &nc);
n_resources = (len / sizeof(__be32)) / (na + nc);
res = resp = xzalloc(sizeof(*res) * n_resources);
endp = reg + (len / sizeof(__be32));
index = 0;
while ((endp - reg) >= (na + nc)) {
address = of_translate_address(node, reg);
if (address == OF_BAD_ADDR) {
ret = -EINVAL;
goto err_free;
}
reg += na;
size = dt_mem_next_cell(nc, &reg);
resp->start = address;
resp->end = address + size - 1;
resname = NULL;
of_property_read_string_index(node, "reg-names", index, &resname);
if (resname)
resp->name = xstrdup(resname);
resp->flags = IORESOURCE_MEM;
resp++;
index++;
}
/*
* A device may already be registered as platform_device.
* Instead of registering the same device again, just
* add this node to the existing device.
*/
for_each_device(dev) {
if (!dev->resource)
continue;
if (dev->resource->start == res->start &&
dev->resource->end == res->end) {
debug("connecting %s to %s\n", node->name, dev_name(dev));
node->device = dev;
dev->device_node = node;
node->resource = dev->resource;
ret = 0;
goto err_free;
}
}
node->resource = res;
node->num_resource = n_resources;
return add_of_device(node, parent);
err_free:
free(res);
return NULL;
}
void of_free(struct device_node *node)
{
struct device_node *n, *nt;
struct property *p, *pt;
if (!node)
return;
list_for_each_entry_safe(p, pt, &node->properties, list)
of_delete_property(p);
list_for_each_entry_safe(n, nt, &node->children, parent_list) {
of_free(n);
}
if (node->parent) {
list_del(&node->parent_list);
list_del(&node->list);
}
if (node->device)
node->device->device_node = NULL;
else
free(node->resource);
free(node->name);
free(node->full_name);
free(node);
if (node == root_node)
of_set_root_node(NULL);
}
static void __of_probe(struct device_node *node,
const struct of_device_id *matches,
struct device_d *parent)
{
struct device_node *n;
struct device_d *dev;
if (node->device)
return;
dev = add_of_device_resource(node, parent);
if (!of_match_node(matches, node))
return;
list_for_each_entry(n, &node->children, parent_list)
__of_probe(n, matches, dev);
}
static void __of_parse_phandles(struct device_node *node)
{
struct device_node *n;
@ -1877,7 +1599,7 @@ const struct of_device_id of_default_bus_match_table[] = {
int of_probe(void)
{
struct device_node *memory, *n;
struct device_node *memory;
if(!root_node)
return -ENODEV;
@ -1891,8 +1613,7 @@ int of_probe(void)
if (memory)
of_add_memory(memory, false);
list_for_each_entry(n, &root_node->children, parent_list)
__of_probe(n, of_default_bus_match_table, NULL);
of_platform_populate(root_node, of_default_bus_match_table, NULL);
return 0;
}
@ -1941,6 +1662,38 @@ out:
return dn;
}
void of_delete_node(struct device_node *node)
{
struct device_node *n, *nt;
struct property *p, *pt;
struct device_d *dev;
if (!node)
return;
list_for_each_entry_safe(p, pt, &node->properties, list)
of_delete_property(p);
list_for_each_entry_safe(n, nt, &node->children, parent_list)
of_delete_node(n);
if (node->parent) {
list_del(&node->parent_list);
list_del(&node->list);
}
dev = of_find_device_by_node(node);
if (dev)
dev->device_node = NULL;
free(node->name);
free(node->full_name);
free(node);
if (node == root_node)
of_set_root_node(NULL);
}
int of_device_is_stdout_path(struct device_d *dev)
{
struct device_node *dn;

View File

@ -211,7 +211,7 @@ struct device_node *of_unflatten_dtb(struct device_node *root, void *infdt)
}
}
err:
of_free(root);
of_delete_node(root);
return ERR_PTR(ret);
}

View File

@ -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;
}

52
drivers/of/of_gpio.c Normal file
View File

@ -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);

321
drivers/of/platform.c Normal file
View File

@ -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);

View File

@ -38,6 +38,10 @@ config DRIVER_SPI_MXS
depends on ARCH_IMX23 || ARCH_IMX28
depends on SPI
config DRIVER_SPI_MVEBU
bool "Marvell MVEBU SoC SPI master driver"
depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_DOVE || ARCH_KIRKWOOD
config DRIVER_SPI_OMAP3
bool "OMAP3 McSPI Master driver"
depends on ARCH_OMAP3 || ARCH_AM33XX

View File

@ -1,5 +1,6 @@
obj-$(CONFIG_SPI) += spi.o
obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o
obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o
obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o
obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o

View File

@ -23,6 +23,7 @@
#include <errno.h>
#include <malloc.h>
#include <gpio.h>
#include <of_gpio.h>
#include <mach/spi.h>
#include <mach/generic.h>
#include <linux/clk.h>

382
drivers/spi/mvebu_spi.c Normal file
View File

@ -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);

View File

@ -30,9 +30,6 @@ struct device_node {
struct list_head children;
struct list_head parent_list;
struct list_head list;
struct resource *resource;
int num_resource;
struct device_d *device;
struct list_head phandles;
phandle phandle;
};
@ -61,6 +58,7 @@ struct of_reserve_map *of_get_reserve_map(void);
void of_clean_reserve_map(void);
void fdt_add_reserve_map(void *fdt);
struct device_d;
struct driver_d;
int of_fix_tree(struct device_node *);
@ -95,17 +93,9 @@ static inline void of_write_number(void *__cell, u64 val, int size)
}
}
int of_get_named_gpio(struct device_node *np,
const char *propname, int index);
void of_print_property(const void *data, int len);
void of_print_cmdline(struct device_node *root);
u64 of_translate_address(struct device_node *node, const __be32 *in_addr);
#define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
#define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 1
void of_print_nodes(struct device_node *node, int indent);
int of_probe(void);
int of_parse_dtb(struct fdt_header *fdt);
@ -149,7 +139,7 @@ extern struct device_node *of_new_node(struct device_node *parent,
const char *name);
extern struct device_node *of_create_node(struct device_node *root,
const char *path);
extern void of_free(struct device_node *node);
extern void of_delete_node(struct device_node *node);
extern int of_machine_is_compatible(const char *compat);
extern int of_device_is_compatible(const struct device_node *device,
@ -226,6 +216,11 @@ extern int of_modalias_node(struct device_node *node, char *modalias, int len);
extern struct device_node *of_get_root_node(void);
extern int of_set_root_node(struct device_node *node);
extern int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
struct device_d *parent);
extern struct device_d *of_find_device_by_node(struct device_node *np);
int of_parse_partitions(struct cdev *cdev, struct device_node *node);
int of_device_is_stdout_path(struct device_d *dev);
const char *of_get_model(void);
@ -513,7 +508,7 @@ static inline struct device_node *of_create_node(struct device_node *root,
return NULL;
}
static inline void of_free(struct device_node *node)
static inline void of_delete_node(struct device_node *node)
{
}
@ -552,6 +547,18 @@ static inline int of_modalias_node(struct device_node *node, char *modalias,
{
return -ENOSYS;
}
static inline int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
struct device_d *parent)
{
return -ENOSYS;
}
static inline struct device_d *of_find_device_by_node(struct device_node *np)
{
return NULL;
}
#endif
#define for_each_node_by_name(dn, name) \

72
include/of_address.h Normal file
View File

@ -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 */

44
include/of_gpio.h Normal file
View File

@ -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