diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9467e0d65..21f90121c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -87,6 +87,7 @@ config ARCH_MVEBU select CLKDEV_LOOKUP select GPIOLIB select HAS_DEBUG_LL + select MVEBU_MBUS select OFTREE config ARCH_MXS diff --git a/arch/arm/boards/solidrun-cubox/lowlevel.c b/arch/arm/boards/solidrun-cubox/lowlevel.c index 1fcecb5b7..4b35731e2 100644 --- a/arch/arm/boards/solidrun-cubox/lowlevel.c +++ b/arch/arm/boards/solidrun-cubox/lowlevel.c @@ -21,7 +21,7 @@ #include #include -extern char __dtb_dove_cubox_start[]; +extern char __dtb_dove_cubox_bb_start[]; ENTRY_FUNCTION(start_solidrun_cubox, r0, r1, r2) { @@ -29,7 +29,7 @@ ENTRY_FUNCTION(start_solidrun_cubox, r0, r1, r2) arm_cpu_lowlevel_init(); - fdt = (uint32_t)__dtb_dove_cubox_start - get_runtime_offset(); + fdt = (uint32_t)__dtb_dove_cubox_bb_start - get_runtime_offset(); mvebu_barebox_entry(fdt); } diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 823549901..141709309 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -26,7 +26,7 @@ dtb-$(CONFIG_ARCH_IMX6) += imx6q-gk802.dtb \ imx6q-nitrogen6x.dtb \ imx6dl-nitrogen6x.dtb \ imx6q-udoo.dtb -dtb-$(CONFIG_ARCH_MVEBU) += dove-cubox.dtb +dtb-$(CONFIG_ARCH_MVEBU) += dove-cubox-bb.dtb dtb-$(CONFIG_ARCH_SOCFPGA) += socfpga_cyclone5_sockit.dtb \ socfpga_cyclone5_socrates.dtb dtb-$(CONFIG_ARCH_TEGRA) += \ @@ -45,7 +45,7 @@ pbl-$(CONFIG_MACH_DFI_FS700_M60) += imx6q-dfi-fs700-m60-6q.dtb.o imx6dl-dfi-fs70 pbl-$(CONFIG_MACH_PCM051) += am335x-phytec-phycore.dtb.o pbl-$(CONFIG_MACH_PHYTEC_PFLA02) += imx6q-phytec-pbab01.dtb.o pbl-$(CONFIG_MACH_REALQ7) += imx6q-dmo-edmqmx6.dtb.o -pbl-$(CONFIG_MACH_SOLIDRUN_CUBOX) += dove-cubox.dtb.o +pbl-$(CONFIG_MACH_SOLIDRUN_CUBOX) += dove-cubox-bb.dtb.o pbl-$(CONFIG_MACH_GK802) += imx6q-gk802.dtb.o pbl-$(CONFIG_MACH_TORADEX_COLIBRI_T20_IRIS) += tegra20-colibri-iris.dtb.o pbl-$(CONFIG_MACH_TOSHIBA_AC100) += tegra20-paz00.dtb.o diff --git a/arch/arm/dts/dove-cubox-bb.dts b/arch/arm/dts/dove-cubox-bb.dts new file mode 100644 index 000000000..3d1e5b4fa --- /dev/null +++ b/arch/arm/dts/dove-cubox-bb.dts @@ -0,0 +1,18 @@ +/* + * Barebox specific DT overlay for SolidRun CuBox + * Sebastian Hesselbarth + */ + +#include "dove-cubox.dts" + +/ { + chosen { + linux,stdout-path = &uart0; + }; + + leds { + power { + barebox,default-trigger = "heartbeat"; + }; + }; +}; diff --git a/arch/arm/dts/dove-cubox.dts b/arch/arm/dts/dove-cubox.dts index 61f38fe5a..7a70f4ca5 100644 --- a/arch/arm/dts/dove-cubox.dts +++ b/arch/arm/dts/dove-cubox.dts @@ -1,19 +1,18 @@ /dts-v1/; -/include/ "dove.dtsi" +#include "dove.dtsi" / { - compatible = "solidrun,cubox", "marvell,dove"; model = "SolidRun CuBox"; + compatible = "solidrun,cubox", "marvell,dove"; memory { device_type = "memory"; - reg = <0x00000000 0x3f000000>; + reg = <0x00000000 0x40000000>; }; chosen { bootargs = "console=ttyS0,115200n8 earlyprintk"; - linux,stdout-path = &uart0; }; leds { @@ -24,7 +23,7 @@ power { label = "Power"; gpios = <&gpio0 18 1>; - linux,default-trigger = "default-on"; + default-state = "keep"; }; }; @@ -43,6 +42,8 @@ regulator-always-on; regulator-boot-on; gpio = <&gpio0 1 0>; + pinctrl-0 = <&pmx_gpio_1>; + pinctrl-names = "default"; }; }; @@ -55,24 +56,27 @@ }; }; - video-card { - compatible = "marvell,dove-video-card"; - reg = <0x3f000000 0x1000000>; - marvell,external-encoder = <&tda19988>; + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio0 19 1>; + pinctrl-0 = <&pmx_gpio_19>; + pinctrl-names = "default"; }; }; &uart0 { status = "okay"; }; &sata0 { status = "okay"; }; +&mdio { status = "okay"; }; +ð { status = "okay"; }; -&lcd0 { - status = "okay"; - clocks = <&si5351 0>; +ðphy { + compatible = "marvell,88e1310"; + reg = <1>; }; &i2c0 { status = "okay"; - clock-frequency = <400000>; + clock-frequency = <100000>; si5351: clock-generator { compatible = "silabs,si5351a-msop"; @@ -95,32 +99,21 @@ silabs,pll-master; }; - clkout1 { - reg = <1>; + clkout2 { + reg = <2>; 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>; + pinctrl-0 = <&pmx_sdio0 &pmx_gpio_12>; }; &spi0 { @@ -128,28 +121,16 @@ /* spi0.0: 4M Flash Winbond W25Q32BV */ spi-flash@0 { - compatible = "winbond,w25q32", "m25p80"; + compatible = "st,w25q32"; spi-max-frequency = <20000000>; reg = <0>; }; }; -&pinctrl { - pinctrl-0 = <&pmx_gpio_1 &pmx_gpio_12>; +&audio1 { + status = "okay"; + clocks = <&gate_clk 13>, <&si5351 2>; + clock-names = "internal", "extclk"; + pinctrl-0 = <&pmx_audio1_i2s1_spdifo &pmx_audio1_extclk>; pinctrl-names = "default"; - - pmx_gpio_1: pmx-gpio-1 { - marvell,pins = "mpp1"; - marvell,function = "gpio"; - }; - - pmx_gpio_12: pmx-gpio-12 { - marvell,pins = "mpp12"; - marvell,function = "gpio"; - }; - - pmx_gpio_18: pmx-gpio-18 { - marvell,pins = "mpp18"; - marvell,function = "gpio"; - }; }; diff --git a/arch/arm/dts/dove.dtsi b/arch/arm/dts/dove.dtsi index 32fbf28dd..2b76524f4 100644 --- a/arch/arm/dts/dove.dtsi +++ b/arch/arm/dts/dove.dtsi @@ -1,10 +1,11 @@ /include/ "skeleton.dtsi" +#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) + / { compatible = "marvell,dove"; model = "Marvell Armada 88AP510 SoC"; - #address-cells = <1>; - #size-cells = <1>; + interrupt-parent = <&intc>; aliases { gpio0 = &gpio0; @@ -12,275 +13,614 @@ gpio2 = &gpio2; }; - soc@f1000000 { - compatible = "simple-bus"; + cpus { #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "marvell,pj4a", "marvell,sheeva-v7"; + device_type = "cpu"; + next-level-cache = <&l2>; + reg = <0>; + }; + }; + + l2: l2-cache { + compatible = "marvell,tauros2-cache"; + marvell,tauros2-cache-features = <0>; + }; + + mbus { + compatible = "marvell,dove-mbus", "marvell,mbus", "simple-bus"; + #address-cells = <2>; #size-cells = <1>; - interrupt-parent = <&intc>; + controller = <&mbusc>; + pcie-mem-aperture = <0xe0000000 0x10000000>; /* 256M MEM space */ + pcie-io-aperture = <0xf2000000 0x00200000>; /* 2M I/O space */ - 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 */ + ranges = ; /* PMU SRAM 1M */ - l2: l2-cache { - compatible = "marvell,tauros2-cache"; - marvell,tauros2-cache-features = <0>; - }; - - timer: timer@20300 { - compatible = "marvell,orion-timer"; - reg = <0x20300 0x30>; - clocks = <&core_clk 0>; - }; - - 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>; + pcie: pcie-controller { + compatible = "marvell,dove-pcie"; status = "disabled"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + + msi-parent = <&intc>; + bus-range = <0x00 0xff>; + + ranges = <0x82000000 0x0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x2000 + 0x82000000 0x0 0x80000 MBUS_ID(0xf0, 0x01) 0x80000 0 0x2000 + 0x82000000 0x1 0x0 MBUS_ID(0x04, 0xe8) 0 1 0 /* Port 0.0 Mem */ + 0x81000000 0x1 0x0 MBUS_ID(0x04, 0xe0) 0 1 0 /* Port 0.0 I/O */ + 0x82000000 0x2 0x0 MBUS_ID(0x08, 0xe8) 0 1 0 /* Port 1.0 Mem */ + 0x81000000 0x2 0x0 MBUS_ID(0x08, 0xe0) 0 1 0>; /* Port 1.0 I/O */ + + pcie-port@0 { + device_type = "pci"; + status = "disabled"; + assigned-addresses = <0x82000800 0 0x40000 0 0x2000>; + reg = <0x0800 0 0 0 0>; + clocks = <&gate_clk 4>; + marvell,pcie-port = <0>; + + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &intc 16>; + }; + + pcie-port@1 { + device_type = "pci"; + status = "disabled"; + assigned-addresses = <0x82002800 0 0x80000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + clocks = <&gate_clk 5>; + marvell,pcie-port = <1>; + + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &intc 18>; + }; }; - 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"; + internal-regs { + compatible = "simple-bus"; #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - interrupts = <6>; - reg = <0x10600 0x28>; - clocks = <&core_clk 0>; - status = "disabled"; - }; + #size-cells = <1>; + ranges = <0x00000000 MBUS_ID(0xf0, 0x01) 0 0x0100000 /* MBUS regs 1M */ + 0x00800000 MBUS_ID(0xf0, 0x02) 0 0x1000000 /* AXI regs 16M */ + 0xffffe000 MBUS_ID(0x03, 0x01) 0 0x0000800 /* CESA SRAM 2k */ + 0xfffff000 MBUS_ID(0x0d, 0x00) 0 0x0000800>; /* PMU SRAM 2k */ - 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; + spi0: spi-ctrl@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + interrupts = <6>; + reg = <0x10600 0x28>; + clocks = <&core_clk 0>; + pinctrl-0 = <&pmx_spi0>; + pinctrl-names = "default"; + status = "disabled"; }; - 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; + i2c0: i2c-ctrl@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"; }; - channel1 { - interrupts = <43>; - dmacap,memset; - dmacap,memcpy; - dmacap,xor; + uart0: serial@12000 { + compatible = "ns16550a"; + reg = <0x12000 0x100>; + reg-shift = <2>; + interrupts = <7>; + clocks = <&core_clk 0>; + status = "disabled"; }; - }; - lcd0: lcd-controller@820000 { - compatible = "marvell,dove-lcd"; - reg = <0x820000 0x200>; - interrupts = <47>; - clocks = <0>; - status = "disabled"; - }; + uart1: serial@12100 { + compatible = "ns16550a"; + reg = <0x12100 0x100>; + reg-shift = <2>; + interrupts = <8>; + clocks = <&core_clk 0>; + pinctrl-0 = <&pmx_uart1>; + pinctrl-names = "default"; + status = "disabled"; + }; - lcd1: lcd-controller@810000 { - compatible = "marvell,dove-lcd"; - reg = <0x810000 0x200>; - interrupts = <46>; - clocks = <0>; - status = "disabled"; + uart2: serial@12200 { + compatible = "ns16550a"; + reg = <0x12000 0x100>; + reg-shift = <2>; + interrupts = <9>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + uart3: serial@12300 { + compatible = "ns16550a"; + reg = <0x12100 0x100>; + reg-shift = <2>; + interrupts = <10>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + spi1: spi-ctrl@14600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + interrupts = <5>; + reg = <0x14600 0x28>; + clocks = <&core_clk 0>; + status = "disabled"; + }; + + mbusc: mbus-ctrl@20000 { + compatible = "marvell,mbus-controller"; + reg = <0x20000 0x80>, <0x800100 0x8>; + }; + + bridge_intc: bridge-interrupt-ctrl@20110 { + compatible = "marvell,orion-bridge-intc"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x20110 0x8>; + interrupts = <0>; + marvell,#interrupts = <5>; + }; + + intc: main-interrupt-ctrl@20200 { + compatible = "marvell,orion-intc"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x20200 0x10>, <0x20210 0x10>; + }; + + timer: timer@20300 { + compatible = "marvell,orion-timer"; + reg = <0x20300 0x20>; + interrupt-parent = <&bridge_intc>; + interrupts = <1>, <2>; + clocks = <&core_clk 0>; + }; + + crypto: crypto-engine@30000 { + compatible = "marvell,orion-crypto"; + reg = <0x30000 0x10000>, + <0xffffe000 0x800>; + reg-names = "regs", "sram"; + interrupts = <31>; + clocks = <&gate_clk 15>; + status = "okay"; + }; + + 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"; + }; + + 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,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,memcpy; + dmacap,xor; + }; + }; + + sdio1: sdio-host@90000 { + compatible = "marvell,dove-sdhci"; + reg = <0x90000 0x100>; + interrupts = <36>, <38>; + clocks = <&gate_clk 9>; + pinctrl-0 = <&pmx_sdio1>; + pinctrl-names = "default"; + status = "disabled"; + }; + + eth: ethernet-ctrl@72000 { + compatible = "marvell,orion-eth"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x72000 0x4000>; + clocks = <&gate_clk 2>; + marvell,tx-checksum-limit = <1600>; + status = "disabled"; + + ethernet-port@0 { + compatible = "marvell,orion-eth-port"; + reg = <0>; + interrupts = <29>; + /* overwrite MAC address in bootloader */ + local-mac-address = [00 00 00 00 00 00]; + phy-handle = <ðphy>; + }; + }; + + mdio: mdio-bus@72004 { + compatible = "marvell,orion-mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x72004 0x84>; + interrupts = <30>; + clocks = <&gate_clk 2>; + status = "disabled"; + + ethphy: ethernet-phy { + /* set phy address in board file */ + }; + }; + + sdio0: sdio-host@92000 { + compatible = "marvell,dove-sdhci"; + reg = <0x92000 0x100>; + interrupts = <35>, <37>; + clocks = <&gate_clk 8>; + pinctrl-0 = <&pmx_sdio0>; + pinctrl-names = "default"; + status = "disabled"; + }; + + sata0: sata-host@a0000 { + compatible = "marvell,orion-sata"; + reg = <0xa0000 0x2400>; + interrupts = <62>; + clocks = <&gate_clk 3>; + phys = <&sata_phy0>; + phy-names = "port0"; + nr-ports = <1>; + status = "disabled"; + }; + + sata_phy0: sata-phy@a2000 { + compatible = "marvell,mvebu-sata-phy"; + reg = <0xa2000 0x0334>; + clocks = <&gate_clk 3>; + clock-names = "sata"; + #phy-cells = <0>; + status = "ok"; + }; + + audio0: audio-controller@b0000 { + compatible = "marvell,dove-audio"; + reg = <0xb0000 0x2210>; + interrupts = <19>, <20>; + clocks = <&gate_clk 12>; + clock-names = "internal"; + status = "disabled"; + }; + + audio1: audio-controller@b4000 { + compatible = "marvell,dove-audio"; + reg = <0xb4000 0x2210>; + interrupts = <21>, <22>; + clocks = <&gate_clk 13>; + clock-names = "internal"; + status = "disabled"; + }; + + thermal: thermal-diode@d001c { + compatible = "marvell,dove-thermal"; + reg = <0xd001c 0x0c>, <0xd005c 0x08>; + }; + + gate_clk: clock-gating-ctrl@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + clocks = <&core_clk 0>; + #clock-cells = <1>; + }; + + pmu_intc: pmu-interrupt-ctrl@d0050 { + compatible = "marvell,dove-pmu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0xd0050 0x8>; + interrupts = <33>; + marvell,#interrupts = <7>; + }; + + pinctrl: pin-ctrl@d0200 { + compatible = "marvell,dove-pinctrl"; + reg = <0xd0200 0x10>; + clocks = <&gate_clk 22>; + + pmx_gpio_0: pmx-gpio-0 { + marvell,pins = "mpp0"; + marvell,function = "gpio"; + }; + + pmx_gpio_1: pmx-gpio-1 { + marvell,pins = "mpp1"; + marvell,function = "gpio"; + }; + + pmx_gpio_2: pmx-gpio-2 { + marvell,pins = "mpp2"; + marvell,function = "gpio"; + }; + + pmx_gpio_3: pmx-gpio-3 { + marvell,pins = "mpp3"; + marvell,function = "gpio"; + }; + + pmx_gpio_4: pmx-gpio-4 { + marvell,pins = "mpp4"; + marvell,function = "gpio"; + }; + + pmx_gpio_5: pmx-gpio-5 { + marvell,pins = "mpp5"; + marvell,function = "gpio"; + }; + + pmx_gpio_6: pmx-gpio-6 { + marvell,pins = "mpp6"; + marvell,function = "gpio"; + }; + + pmx_gpio_7: pmx-gpio-7 { + marvell,pins = "mpp7"; + marvell,function = "gpio"; + }; + + pmx_gpio_8: pmx-gpio-8 { + marvell,pins = "mpp8"; + marvell,function = "gpio"; + }; + + pmx_gpio_9: pmx-gpio-9 { + marvell,pins = "mpp9"; + marvell,function = "gpio"; + }; + + pmx_gpio_10: pmx-gpio-10 { + marvell,pins = "mpp10"; + marvell,function = "gpio"; + }; + + pmx_gpio_11: pmx-gpio-11 { + marvell,pins = "mpp11"; + marvell,function = "gpio"; + }; + + pmx_gpio_12: pmx-gpio-12 { + marvell,pins = "mpp12"; + marvell,function = "gpio"; + }; + + pmx_gpio_13: pmx-gpio-13 { + marvell,pins = "mpp13"; + marvell,function = "gpio"; + }; + + pmx_audio1_extclk: pmx-audio1-extclk { + marvell,pins = "mpp13"; + marvell,function = "audio1"; + }; + + pmx_gpio_14: pmx-gpio-14 { + marvell,pins = "mpp14"; + marvell,function = "gpio"; + }; + + pmx_gpio_15: pmx-gpio-15 { + marvell,pins = "mpp15"; + marvell,function = "gpio"; + }; + + pmx_gpio_16: pmx-gpio-16 { + marvell,pins = "mpp16"; + marvell,function = "gpio"; + }; + + pmx_gpio_17: pmx-gpio-17 { + marvell,pins = "mpp17"; + marvell,function = "gpio"; + }; + + pmx_gpio_18: pmx-gpio-18 { + marvell,pins = "mpp18"; + marvell,function = "gpio"; + }; + + pmx_gpio_19: pmx-gpio-19 { + marvell,pins = "mpp19"; + marvell,function = "gpio"; + }; + + pmx_gpio_20: pmx-gpio-20 { + marvell,pins = "mpp20"; + marvell,function = "gpio"; + }; + + pmx_gpio_21: pmx-gpio-21 { + marvell,pins = "mpp21"; + marvell,function = "gpio"; + }; + + pmx_camera: pmx-camera { + marvell,pins = "mpp_camera"; + marvell,function = "camera"; + }; + + pmx_camera_gpio: pmx-camera-gpio { + marvell,pins = "mpp_camera"; + marvell,function = "gpio"; + }; + + pmx_sdio0: pmx-sdio0 { + marvell,pins = "mpp_sdio0"; + marvell,function = "sdio0"; + }; + + pmx_sdio0_gpio: pmx-sdio0-gpio { + marvell,pins = "mpp_sdio0"; + marvell,function = "gpio"; + }; + + pmx_sdio1: pmx-sdio1 { + marvell,pins = "mpp_sdio1"; + marvell,function = "sdio1"; + }; + + pmx_sdio1_gpio: pmx-sdio1-gpio { + marvell,pins = "mpp_sdio1"; + marvell,function = "gpio"; + }; + + pmx_audio1_gpio: pmx-audio1-gpio { + marvell,pins = "mpp_audio1"; + marvell,function = "gpio"; + }; + + pmx_audio1_i2s1_spdifo: pmx-audio1-i2s1-spdifo { + marvell,pins = "mpp_audio1"; + marvell,function = "i2s1/spdifo"; + }; + + pmx_spi0: pmx-spi0 { + marvell,pins = "mpp_spi0"; + marvell,function = "spi0"; + }; + + pmx_spi0_gpio: pmx-spi0-gpio { + marvell,pins = "mpp_spi0"; + marvell,function = "gpio"; + }; + + pmx_uart1: pmx-uart1 { + marvell,pins = "mpp_uart1"; + marvell,function = "uart1"; + }; + + pmx_uart1_gpio: pmx-uart1-gpio { + marvell,pins = "mpp_uart1"; + marvell,function = "gpio"; + }; + + pmx_nand: pmx-nand { + marvell,pins = "mpp_nand"; + marvell,function = "nand"; + }; + + pmx_nand_gpo: pmx-nand-gpo { + marvell,pins = "mpp_nand"; + marvell,function = "gpo"; + }; + }; + + core_clk: core-clocks@d0214 { + compatible = "marvell,dove-core-clock"; + reg = <0xd0214 0x4>; + #clock-cells = <1>; + }; + + gpio0: gpio-ctrl@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-ctrl@d0420 { + compatible = "marvell,orion-gpio"; + #gpio-cells = <2>; + gpio-controller; + reg = <0xd0420 0x20>; + ngpios = <32>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <61>; + }; + + rtc: real-time-clock@d8500 { + compatible = "marvell,orion-rtc"; + reg = <0xd8500 0x20>; + interrupt-parent = <&pmu_intc>; + interrupts = <5>; + }; + + gpio2: gpio-ctrl@e8400 { + compatible = "marvell,orion-gpio"; + #gpio-cells = <2>; + gpio-controller; + reg = <0xe8400 0x0c>; + ngpios = <8>; + }; }; }; }; diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index b0d3c1fe9..202df5976 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -1,13 +1,20 @@ menu "Bus devices" -config IMX_WEIM - depends on ARCH_IMX - bool "i.MX WEIM driver" - config BUS_OMAP_GPMC depends on ARCH_OMAP depends on OFDEVICE depends on OMAP_GPMC bool "TI OMAP/AM33xx GPMC support" +config IMX_WEIM + depends on ARCH_IMX + bool "i.MX WEIM driver" + +config MVEBU_MBUS + bool + depends on ARCH_MVEBU + help + Driver needed for the MBus configuration on Marvell EBU SoCs + (Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP). + endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index f1c5ac73c..4b7aa888a 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -1,2 +1,3 @@ -obj-$(CONFIG_IMX_WEIM) += imx-weim.o -obj-$(CONFIG_BUS_OMAP_GPMC) += omap-gpmc.o +obj-$(CONFIG_BUS_OMAP_GPMC) += omap-gpmc.o +obj-$(CONFIG_IMX_WEIM) += imx-weim.o +obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c new file mode 100644 index 000000000..11e3777a6 --- /dev/null +++ b/drivers/bus/mvebu-mbus.c @@ -0,0 +1,743 @@ +/* + * Address map functions for Marvell EBU SoCs (Kirkwood, Armada + * 370/XP, Dove, Orion5x and MV78xx0) + * + * Sebastian Hesselbarth + * + * based on mbus driver from Linux + * (C) Copyright 2008 Marvell Semiconductor + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The Marvell EBU SoCs have a configurable physical address space: + * the physical address at which certain devices (PCIe, NOR, NAND, + * etc.) sit can be configured. The configuration takes place through + * two sets of registers: + * + * - One to configure the access of the CPU to the devices. Depending + * on the families, there are between 8 and 20 configurable windows, + * each can be use to create a physical memory window that maps to a + * specific device. Devices are identified by a tuple (target, + * attribute). + * + * - One to configure the access to the CPU to the SDRAM. There are + * either 2 (for Dove) or 4 (for other families) windows to map the + * SDRAM into the physical address space. + * + * This driver: + * + * - Reads out the SDRAM address decoding windows at initialization + * time, and fills the mbus_dram_info structure with these + * informations. The exported function mv_mbus_dram_info() allow + * device drivers to get those informations related to the SDRAM + * address decoding windows. This is because devices also have their + * own windows (configured through registers that are part of each + * device register space), and therefore the drivers for Marvell + * devices have to configure those device -> SDRAM windows to ensure + * that DMA works properly. + * + * - Provides an API for platform code or device drivers to + * dynamically add or remove address decoding windows for the CPU -> + * device accesses. This API is mvebu_mbus_add_window_by_id(), + * mvebu_mbus_add_window_remap_by_id() and + * mvebu_mbus_del_window(). + * + * - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to + * see the list of CPU -> SDRAM windows and their configuration + * (file 'sdram') and the list of CPU -> devices windows and their + * configuration (file 'devices'). + */ + +#include +#include +#include +#include +#include +#include + +/* DDR target is the same on all platforms */ +#define TARGET_DDR 0 + +/* CPU Address Decode Windows registers */ +#define WIN_CTRL_OFF 0x0000 +#define WIN_CTRL_ENABLE BIT(0) +#define WIN_CTRL_TGT_MASK 0xf0 +#define WIN_CTRL_TGT_SHIFT 4 +#define WIN_CTRL_ATTR_MASK 0xff00 +#define WIN_CTRL_ATTR_SHIFT 8 +#define WIN_CTRL_SIZE_MASK 0xffff0000 +#define WIN_CTRL_SIZE_SHIFT 16 +#define WIN_BASE_OFF 0x0004 +#define WIN_BASE_LOW 0xffff0000 +#define WIN_BASE_HIGH 0xf +#define WIN_REMAP_LO_OFF 0x0008 +#define WIN_REMAP_LOW 0xffff0000 +#define WIN_REMAP_HI_OFF 0x000c + +#define ATTR_HW_COHERENCY (0x1 << 4) + +#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3)) +#define DDR_BASE_CS_HIGH_MASK 0xf +#define DDR_BASE_CS_LOW_MASK 0xff000000 +#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3)) +#define DDR_SIZE_ENABLED BIT(0) +#define DDR_SIZE_CS_MASK 0x1c +#define DDR_SIZE_CS_SHIFT 2 +#define DDR_SIZE_MASK 0xff000000 + +#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) + +struct mvebu_mbus_state; + +struct mvebu_mbus_soc_data { + unsigned int num_wins; + unsigned int num_remappable_wins; + unsigned int (*win_cfg_offset)(const int win); + void (*setup_cpu_target)(struct mvebu_mbus_state *s); +}; + +struct mvebu_mbus_state { + struct device_d *dev; + void __iomem *mbuswins_base; + void __iomem *sdramwins_base; + struct dentry *debugfs_root; + struct dentry *debugfs_sdram; + struct dentry *debugfs_devs; + struct resource pcie_mem_aperture; + struct resource pcie_io_aperture; + const struct mvebu_mbus_soc_data *soc; + int hw_io_coherency; +}; + +static struct mvebu_mbus_state mbus_state; +static struct mbus_dram_target_info mbus_dram_info; + +/* + * Functions to manipulate the address decoding windows + */ + +static void mvebu_mbus_read_window(struct mvebu_mbus_state *mbus, + int win, int *enabled, u64 *base, + u32 *size, u8 *target, u8 *attr, + u64 *remap) +{ + void __iomem *addr = mbus->mbuswins_base + + mbus->soc->win_cfg_offset(win); + u32 basereg = readl(addr + WIN_BASE_OFF); + u32 ctrlreg = readl(addr + WIN_CTRL_OFF); + + if (!(ctrlreg & WIN_CTRL_ENABLE)) { + *enabled = 0; + return; + } + + *enabled = 1; + *base = ((u64)basereg & WIN_BASE_HIGH) << 32; + *base |= (basereg & WIN_BASE_LOW); + *size = (ctrlreg | ~WIN_CTRL_SIZE_MASK) + 1; + + if (target) + *target = (ctrlreg & WIN_CTRL_TGT_MASK) >> WIN_CTRL_TGT_SHIFT; + + if (attr) + *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT; + + if (remap) { + if (win < mbus->soc->num_remappable_wins) { + u32 remap_low = readl(addr + WIN_REMAP_LO_OFF); + u32 remap_hi = readl(addr + WIN_REMAP_HI_OFF); + *remap = ((u64)remap_hi << 32) | remap_low; + } else + *remap = 0; + } +} + +static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus, + int win) +{ + void __iomem *addr; + + addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win); + + writel(0, addr + WIN_BASE_OFF); + writel(0, addr + WIN_CTRL_OFF); + if (win < mbus->soc->num_remappable_wins) { + writel(0, addr + WIN_REMAP_LO_OFF); + writel(0, addr + WIN_REMAP_HI_OFF); + } +} + +/* Checks whether the given window number is available */ +static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus, + const int win) +{ + void __iomem *addr = mbus->mbuswins_base + + mbus->soc->win_cfg_offset(win); + u32 ctrl = readl(addr + WIN_CTRL_OFF); + return !(ctrl & WIN_CTRL_ENABLE); +} + +/* + * Checks whether the given (base, base+size) area doesn't overlap an + * existing region + */ +static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus, + phys_addr_t base, size_t size, + u8 target, u8 attr) +{ + u64 end = (u64)base + size; + int win; + + for (win = 0; win < mbus->soc->num_wins; win++) { + u64 wbase, wend; + u32 wsize; + u8 wtarget, wattr; + int enabled; + + mvebu_mbus_read_window(mbus, win, + &enabled, &wbase, &wsize, + &wtarget, &wattr, NULL); + + if (!enabled) + continue; + + wend = wbase + wsize; + + /* + * Check if the current window overlaps with the + * proposed physical range + */ + if ((u64)base < wend && end > wbase) + return 0; + + /* + * Check if target/attribute conflicts + */ + if (target == wtarget && attr == wattr) + return 0; + } + + return 1; +} + +static int mvebu_mbus_find_window(struct mvebu_mbus_state *mbus, + phys_addr_t base, size_t size) +{ + int win; + + for (win = 0; win < mbus->soc->num_wins; win++) { + u64 wbase; + u32 wsize; + int enabled; + + mvebu_mbus_read_window(mbus, win, + &enabled, &wbase, &wsize, + NULL, NULL, NULL); + + if (!enabled) + continue; + + if (base == wbase && size == wsize) + return win; + } + + return -ENODEV; +} + +static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus, + int win, phys_addr_t base, size_t size, + phys_addr_t remap, u8 target, + u8 attr) +{ + void __iomem *addr = mbus->mbuswins_base + + mbus->soc->win_cfg_offset(win); + u32 ctrl, remap_addr; + + ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | + (attr << WIN_CTRL_ATTR_SHIFT) | + (target << WIN_CTRL_TGT_SHIFT) | + WIN_CTRL_ENABLE; + + writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF); + writel(ctrl, addr + WIN_CTRL_OFF); + if (win < mbus->soc->num_remappable_wins) { + if (remap == MVEBU_MBUS_NO_REMAP) + remap_addr = base; + else + remap_addr = remap; + writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF); + writel(0, addr + WIN_REMAP_HI_OFF); + } + + return 0; +} + +static int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus, + phys_addr_t base, size_t size, + phys_addr_t remap, u8 target, + u8 attr) +{ + int win; + + if (remap == MVEBU_MBUS_NO_REMAP) { + for (win = mbus->soc->num_remappable_wins; + win < mbus->soc->num_wins; win++) + if (mvebu_mbus_window_is_free(mbus, win)) + return mvebu_mbus_setup_window(mbus, win, base, + size, remap, + target, attr); + } + + + for (win = 0; win < mbus->soc->num_wins; win++) + if (mvebu_mbus_window_is_free(mbus, win)) + return mvebu_mbus_setup_window(mbus, win, base, size, + remap, target, attr); + + return -ENOMEM; +} + +/* + * SoC-specific functions and definitions + */ + +static unsigned int armada_370_xp_mbus_win_offset(int win) +{ + /* The register layout is a bit annoying and the below code + * tries to cope with it. + * - At offset 0x0, there are the registers for the first 8 + * windows, with 4 registers of 32 bits per window (ctrl, + * base, remap low, remap high) + * - Then at offset 0x80, there is a hole of 0x10 bytes for + * the internal registers base address and internal units + * sync barrier register. + * - Then at offset 0x90, there the registers for 12 + * windows, with only 2 registers of 32 bits per window + * (ctrl, base). + */ + if (win < 8) + return win << 4; + else + return 0x90 + ((win - 8) << 3); +} + +static unsigned int orion5x_mbus_win_offset(int win) +{ + return win << 4; +} + +static unsigned int mv78xx0_mbus_win_offset(int win) +{ + if (win < 8) + return win << 4; + else + return 0x900 + ((win - 8) << 4); +} + +static void mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) +{ + int i; + int cs; + + mbus_dram_info.mbus_dram_target_id = TARGET_DDR; + + for (i = 0, cs = 0; i < 4; i++) { + u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); + u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); + + /* + * We only take care of entries for which the chip + * select is enabled, and that don't have high base + * address bits set (devices can only access the first + * 32 bits of the memory). + */ + if ((size & DDR_SIZE_ENABLED) && + !(base & DDR_BASE_CS_HIGH_MASK)) { + struct mbus_dram_window *w; + + w = &mbus_dram_info.cs[cs++]; + w->cs_index = i; + w->mbus_attr = 0xf & ~(1 << i); + if (mbus->hw_io_coherency) + w->mbus_attr |= ATTR_HW_COHERENCY; + w->base = base & DDR_BASE_CS_LOW_MASK; + w->size = (size | ~DDR_SIZE_MASK) + 1; + } + } + mbus_dram_info.num_cs = cs; +} + +static void mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) +{ + int i; + int cs; + + mbus_dram_info.mbus_dram_target_id = TARGET_DDR; + + for (i = 0, cs = 0; i < 2; i++) { + u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); + + /* + * Chip select enabled? + */ + if (map & 1) { + struct mbus_dram_window *w; + + w = &mbus_dram_info.cs[cs++]; + w->cs_index = i; + w->mbus_attr = 0; /* CS address decoding done inside */ + /* the DDR controller, no need to */ + /* provide attributes */ + w->base = map & 0xff800000; + w->size = 0x100000 << (((map & 0x000f0000) >> 16) - 4); + } + } + + mbus_dram_info.num_cs = cs; +} + +static const struct mvebu_mbus_soc_data +armada_370_xp_mbus_data __maybe_unused = { + .num_wins = 20, + .num_remappable_wins = 8, + .win_cfg_offset = armada_370_xp_mbus_win_offset, + .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, +}; + +static const struct mvebu_mbus_soc_data +dove_mbus_data __maybe_unused = { + .num_wins = 8, + .num_remappable_wins = 4, + .win_cfg_offset = orion5x_mbus_win_offset, + .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, +}; + +static const struct mvebu_mbus_soc_data +kirkwood_mbus_data __maybe_unused = { + .num_wins = 8, + .num_remappable_wins = 4, + .win_cfg_offset = orion5x_mbus_win_offset, + .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, +}; + +/* + * Some variants of Orion5x have 4 remappable windows, some other have + * only two of them. + */ +static const struct mvebu_mbus_soc_data +orion5x_4win_mbus_data __maybe_unused = { + .num_wins = 8, + .num_remappable_wins = 4, + .win_cfg_offset = orion5x_mbus_win_offset, + .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, +}; + +static const struct mvebu_mbus_soc_data +orion5x_2win_mbus_data__maybe_unused = { + .num_wins = 8, + .num_remappable_wins = 2, + .win_cfg_offset = orion5x_mbus_win_offset, + .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, +}; + +static const struct mvebu_mbus_soc_data +mv78xx0_mbus_data __maybe_unused = { + .num_wins = 14, + .num_remappable_wins = 8, + .win_cfg_offset = mv78xx0_mbus_win_offset, + .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, +}; + +static struct of_device_id mvebu_mbus_dt_ids[] = { +#if defined(CONFIG_ARCH_ARMADA_370) || defined(CONFIG_ARCH_ARMADA_XP) + { .compatible = "marvell,armada370-mbus", + .data = (u32)&armada_370_xp_mbus_data, }, + { .compatible = "marvell,armadaxp-mbus", + .data = (u32)&armada_370_xp_mbus_data, }, +#endif +#if defined(CONFIG_ARCH_DOVE) + { .compatible = "marvell,dove-mbus", + .data = (u32)&dove_mbus_data, }, +#endif +#if defined(CONFIG_ARCH_KIRKWOOD) + { .compatible = "marvell,kirkwood-mbus", + .data = (u32)&kirkwood_mbus_data, }, +#endif +#if defined(CONFIG_ARCH_ORION5X) + { .compatible = "marvell,orion5x-88f5281-mbus", + .data = (u32)&orion5x_4win_mbus_data, }, + { .compatible = "marvell,orion5x-88f5182-mbus", + .data = (u32)&orion5x_2win_mbus_data, }, + { .compatible = "marvell,orion5x-88f5181-mbus", + .data = (u32)&orion5x_2win_mbus_data, }, + { .compatible = "marvell,orion5x-88f6183-mbus", + .data = (u32)&orion5x_4win_mbus_data, }, +#endif +#if defined(CONFIG_ARCH_MV78XX0) + { .compatible = "marvell,mv78xx0-mbus", + .data = (u32)&mv78xx0_mbus_data, }, +#endif + { }, +}; + +/* + * Public API of the driver + */ +const struct mbus_dram_target_info *mvebu_mbus_dram_info(void) +{ + return &mbus_dram_info; +} + +int mvebu_mbus_add_window_remap_by_id(unsigned int target, + unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) +{ + struct mvebu_mbus_state *s = &mbus_state; + + if (!mvebu_mbus_window_conflicts(s, base, size, target, attribute)) { + dev_err(s->dev, "cannot add window '%x:%x', conflicts with another window\n", + target, attribute); + return -EINVAL; + } + + return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); +} + +int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size) +{ + return mvebu_mbus_add_window_remap_by_id(target, attribute, base, + size, MVEBU_MBUS_NO_REMAP); +} + +int mvebu_mbus_del_window(phys_addr_t base, size_t size) +{ + int win; + + win = mvebu_mbus_find_window(&mbus_state, base, size); + if (win < 0) + return win; + + mvebu_mbus_disable_window(&mbus_state, win); + return 0; +} + +void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) +{ + if (!res) + return; + *res = mbus_state.pcie_mem_aperture; +} + +void mvebu_mbus_get_pcie_io_aperture(struct resource *res) +{ + if (!res) + return; + *res = mbus_state.pcie_io_aperture; +} + +/* + * The window IDs in the ranges DT property have the following format: + * - bits 28 to 31: MBus custom field + * - bits 24 to 27: window target ID + * - bits 16 to 23: window attribute ID + * - bits 0 to 15: unused + */ +#define CUSTOM(id) (((id) & 0xF0000000) >> 24) +#define TARGET(id) (((id) & 0x0F000000) >> 24) +#define ATTR(id) (((id) & 0x00FF0000) >> 16) + +static int mbus_dt_setup_win(struct mvebu_mbus_state *mbus, + u32 base, u32 size, u8 target, u8 attr) +{ + if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) { + dev_err(mbus->dev, "cannot add window '%04x:%04x', conflicts with another window\n", + target, attr); + return -EBUSY; + } + + if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP, + target, attr)) { + dev_err(mbus->dev, "cannot add window '%04x:%04x', too many windows\n", + target, attr); + return -ENOMEM; + } + return 0; +} + +static int mbus_parse_ranges(struct mvebu_mbus_state *mbus, int *addr_cells, + int *c_addr_cells, int *c_size_cells, + int *cell_count, const __be32 **ranges_start, + const __be32 **ranges_end) +{ + struct device_node *node = mbus->dev->device_node; + const __be32 *prop; + int ranges_len, tuple_len; + + /* Allow a node with no 'ranges' property */ + *ranges_start = of_get_property(node, "ranges", &ranges_len); + if (*ranges_start == NULL) { + *addr_cells = *c_addr_cells = *c_size_cells = *cell_count = 0; + *ranges_start = *ranges_end = NULL; + return 0; + } + *ranges_end = *ranges_start + ranges_len / sizeof(__be32); + + *addr_cells = of_n_addr_cells(node); + + prop = of_get_property(node, "#address-cells", NULL); + *c_addr_cells = be32_to_cpup(prop); + + prop = of_get_property(node, "#size-cells", NULL); + *c_size_cells = be32_to_cpup(prop); + + *cell_count = *addr_cells + *c_addr_cells + *c_size_cells; + tuple_len = (*cell_count) * sizeof(__be32); + + if (ranges_len % tuple_len) { + dev_warn(mbus->dev, "malformed ranges entry '%s'\n", + node->name); + return -EINVAL; + } + return 0; +} + +static int mbus_dt_setup(struct mvebu_mbus_state *mbus) +{ + int addr_cells, c_addr_cells, c_size_cells; + int i, ret, cell_count; + const __be32 *r, *ranges_start, *ranges_end; + + ret = mbus_parse_ranges(mbus, &addr_cells, &c_addr_cells, + &c_size_cells, &cell_count, + &ranges_start, &ranges_end); + if (ret < 0) + return ret; + + for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) { + u32 windowid, base, size; + u8 target, attr; + + /* + * An entry with a non-zero custom field do not + * correspond to a static window, so skip it. + */ + windowid = of_read_number(r, 1); + if (CUSTOM(windowid)) + continue; + + target = TARGET(windowid); + attr = ATTR(windowid); + + base = of_read_number(r + c_addr_cells, addr_cells); + size = of_read_number(r + c_addr_cells + addr_cells, + c_size_cells); + ret = mbus_dt_setup_win(mbus, base, size, target, attr); + if (ret < 0) + return ret; + } + return 0; +} + +static void mvebu_mbus_get_pcie_resources(struct device_node *np, + struct resource *mem, struct resource *io) +{ + u32 reg[2]; + int ret; + + /* + * These are optional, so we make sure that resource_size(x) will + * return 0. + */ + memset(mem, 0, sizeof(struct resource)); + mem->end = -1; + memset(io, 0, sizeof(struct resource)); + io->end = -1; + + ret = of_property_read_u32_array(np, "pcie-mem-aperture", + reg, ARRAY_SIZE(reg)); + if (!ret) { + mem->start = reg[0]; + mem->end = mem->start + reg[1]; + mem->flags = IORESOURCE_MEM; + } + + ret = of_property_read_u32_array(np, "pcie-io-aperture", + reg, ARRAY_SIZE(reg)); + if (!ret) { + io->start = reg[0]; + io->end = io->start + reg[1]; + io->flags = IORESOURCE_IO; + } +} + +static int mvebu_mbus_probe(struct device_d *dev) +{ + struct device_node *np, *controller; + const struct of_device_id *match; + const __be32 *prop; + int win; + + mbus_state.dev = dev; + + np = of_find_matching_node_and_match(NULL, mvebu_mbus_dt_ids, &match); + if (!np) { + dev_err(dev, "could not find a matching SoC family\n"); + return -ENODEV; + } + mbus_state.soc = (struct mvebu_mbus_soc_data *)match->data; + + prop = of_get_property(np, "controller", NULL); + if (!prop) { + dev_err(dev, "required 'controller' property missing\n"); + return -EINVAL; + } + + controller = of_find_node_by_phandle(be32_to_cpup(prop)); + if (!controller) { + dev_err(dev, "could not find an 'mbus-controller' node\n"); + return -ENODEV; + } + + mbus_state.mbuswins_base = of_iomap(controller, 0); + if (!mbus_state.mbuswins_base) { + dev_err(dev, "cannot get MBUS register address\n"); + return -ENOMEM; + } + + mbus_state.sdramwins_base = of_iomap(controller, 1); + if (!mbus_state.sdramwins_base) { + dev_err(dev, "cannot get SDRAM register address\n"); + return -ENOMEM; + } + + /* Get optional pcie-{mem,io}-aperture properties */ + mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, + &mbus_state.pcie_io_aperture); + + if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric")) + mbus_state.hw_io_coherency = 1; + + for (win = 0; win < mbus_state.soc->num_wins; win++) + mvebu_mbus_disable_window(&mbus_state, win); + + mbus_state.soc->setup_cpu_target(&mbus_state); + + /* Setup statically declared windows in the DT */ + return mbus_dt_setup(&mbus_state); +} + +static struct driver_d mvebu_mbus_driver = { + .probe = mvebu_mbus_probe, + .name = "mvebu-mbus", + .of_compatible = DRV_OF_COMPAT(mvebu_mbus_dt_ids), +}; + +static int mvebu_mbus_init(void) +{ + return platform_driver_register(&mvebu_mbus_driver); +} +postcore_initcall(mvebu_mbus_init); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index bc341c139..fd9685988 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1,32 +1,30 @@ +config ARCH_HAS_FEC_IMX + bool + +config HAS_AT91_ETHER + bool + config HAS_CS8900 bool +config HAS_DESIGNWARE_ETH + bool + config HAS_DM9000 bool config HAS_MACB bool -config HAS_AT91_ETHER - bool - config HAS_NETX_ETHER bool -config HAS_DESIGNWARE_ETH - bool - -config ARCH_HAS_FEC_IMX - bool - config PHYLIB bool menu "Network drivers" depends on NET -source "drivers/net/phy/Kconfig" - config DRIVER_NET_AR231X bool "AR231X Ethernet support" depends on MACH_MIPS_AR231X @@ -34,6 +32,11 @@ config DRIVER_NET_AR231X help Support for the AR231x/531x ethernet controller +config DRIVER_NET_AT91_ETHER + bool "at91 ethernet driver" + depends on HAS_AT91_ETHER + select PHYLIB + config DRIVER_NET_CALXEDA_XGMAC bool "Calxeda xgmac" @@ -46,6 +49,79 @@ config DRIVER_NET_CPSW depends on ARCH_OMAP select PHYLIB +config DRIVER_NET_DAVINCI_EMAC + bool "TI Davinci/OMAP EMAC ethernet driver" + depends on ARCH_OMAP3 + select PHYLIB + +config DRIVER_NET_DESIGNWARE + bool "Designware Universal MAC ethernet driver" + select PHYLIB + help + This option enables support for the Synopsys + Designware Core Univesal MAC 10M/100M/1G ethernet IP. + +config DRIVER_NET_DM9K + bool "Davicom dm9k[E|A|B] ethernet driver" + depends on HAS_DM9000 + select PHYLIB + +config DRIVER_NET_EP93XX + bool "EP93xx Ethernet driver" + depends on ARCH_EP93XX + select PHYLIB + +config DRIVER_NET_ETHOC + bool "OpenCores ethernet MAC driver" + help + This option enables support for the OpenCores 10/100 Mbps + Ethernet MAC core. + +config DRIVER_NET_FEC_IMX + bool "i.MX FEC Ethernet driver" + depends on ARCH_HAS_FEC_IMX + select PHYLIB + +config DRIVER_NET_GIANFAR + bool "Gianfar Ethernet" + depends on ARCH_MPC85XX + select PHYLIB + +config DRIVER_NET_KS8851_MLL + bool "ks8851 mll ethernet driver" + select PHYLIB + help + This option enables support for the Micrel KS8851 MLL + ethernet chip. + +config DRIVER_NET_MACB + bool "macb Ethernet driver" + depends on HAS_MACB + select PHYLIB + +config DRIVER_NET_MICREL + depends on SPI + bool "Micrel KSZ8864RMN Ethernet Switch driver" + help + This option enables support for enabling the Micrel + KSZ8864RMN Ethernet Switch over SPI. + +config DRIVER_NET_MPC5200 + bool "MPC5200 Ethernet driver" + depends on ARCH_MPC5200 + select PHYLIB + +config DRIVER_NET_NETX + bool "Hilscher Netx ethernet driver" + depends on HAS_NETX_ETHER + select PHYLIB + +config DRIVER_NET_ORION + bool "Marvell Orion SoC Ethernet" + depends on ARCH_MVEBU + select PHYLIB + select MDIO_MVEBU + config DRIVER_NET_SMC911X bool "smc911x ethernet driver" select PHYLIB @@ -60,46 +136,6 @@ config DRIVER_NET_SMC91111 This option enables support for the SMSC LAN91C111 ethernet chip. -config DRIVER_NET_DAVINCI_EMAC - bool "TI Davinci/OMAP EMAC ethernet driver" - depends on ARCH_OMAP3 - select PHYLIB - -config DRIVER_NET_DM9K - bool "Davicom dm9k[E|A|B] ethernet driver" - depends on HAS_DM9000 - select PHYLIB - -config DRIVER_NET_NETX - bool "Hilscher Netx ethernet driver" - depends on HAS_NETX_ETHER - select PHYLIB - -config DRIVER_NET_AT91_ETHER - bool "at91 ethernet driver" - depends on HAS_AT91_ETHER - select PHYLIB - -config DRIVER_NET_MPC5200 - bool "MPC5200 Ethernet driver" - depends on ARCH_MPC5200 - select PHYLIB - -config DRIVER_NET_FEC_IMX - bool "i.MX FEC Ethernet driver" - depends on ARCH_HAS_FEC_IMX - select PHYLIB - -config DRIVER_NET_EP93XX - bool "EP93xx Ethernet driver" - depends on ARCH_EP93XX - select PHYLIB - -config DRIVER_NET_MACB - bool "macb Ethernet driver" - depends on HAS_MACB - select PHYLIB - config DRIVER_NET_TAP bool "tap Ethernet driver" depends on LINUX @@ -120,39 +156,8 @@ config TSE_USE_DEDICATED_DESC_MEM reserved with a malloc but directly mapped to the memory address (defined in config.h) -config DRIVER_NET_KS8851_MLL - bool "ks8851 mll ethernet driver" - select PHYLIB - help - This option enables support for the Micrel KS8851 MLL - ethernet chip. - -config DRIVER_NET_DESIGNWARE - bool "Designware Universal MAC ethernet driver" - select PHYLIB - help - This option enables support for the Synopsys - Designware Core Univesal MAC 10M/100M/1G ethernet IP. - -config DRIVER_NET_GIANFAR - bool "Gianfar Ethernet" - depends on ARCH_MPC85XX - select PHYLIB - -config DRIVER_NET_ETHOC - bool "OpenCores ethernet MAC driver" - help - This option enables support for the OpenCores 10/100 Mbps - Ethernet MAC core. - +source "drivers/net/phy/Kconfig" source "drivers/net/usb/Kconfig" -config DRIVER_NET_MICREL - depends on SPI - bool "Micrel KSZ8864RMN Ethernet Switch driver" - help - This option enables support for enabling the Micrel - KSZ8864RMN Ethernet Switch over SPI. - endmenu diff --git a/drivers/net/Makefile b/drivers/net/Makefile index ffb40ae56..c1c455993 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -1,23 +1,25 @@ +obj-$(CONFIG_PHYLIB) += phy/ +obj-$(CONFIG_NET_USB) += usb/ + obj-$(CONFIG_DRIVER_NET_AR231X) += ar231x.o +obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o obj-$(CONFIG_DRIVER_NET_CALXEDA_XGMAC) += xgmac.o obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o obj-$(CONFIG_DRIVER_NET_CPSW) += cpsw.o +obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC) += davinci_emac.o +obj-$(CONFIG_DRIVER_NET_DESIGNWARE) += designware.o +obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o +obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o +obj-$(CONFIG_DRIVER_NET_ETHOC) += ethoc.o +obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o +obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o +obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o +obj-$(CONFIG_DRIVER_NET_MACB) += macb.o +obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o +obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o +obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o +obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o -obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC) += davinci_emac.o -obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o -obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o -obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o -obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o -obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o -obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o -obj-$(CONFIG_DRIVER_NET_MACB) += macb.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o -obj-$(CONFIG_PHYLIB) += phy/ -obj-$(CONFIG_NET_USB) += usb/ obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o -obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o -obj-$(CONFIG_DRIVER_NET_DESIGNWARE) += designware.o -obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o -obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o -obj-$(CONFIG_DRIVER_NET_ETHOC) += ethoc.o diff --git a/drivers/net/orion-gbe.c b/drivers/net/orion-gbe.c new file mode 100644 index 000000000..00f5e543c --- /dev/null +++ b/drivers/net/orion-gbe.c @@ -0,0 +1,541 @@ +/* + * (C) Copyright 2014 + * Pengutronix, Michael Grzeschik + * Sebastian Hesselbarth + * + * based on kirkwood_egiga driver from u-boot + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * based on - Driver for MV64360X ethernet ports + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "orion-gbe.h" + +struct rxdesc { + u32 cmd_sts; /* Descriptor command status */ + u16 buf_size; /* Buffer size */ + u16 byte_cnt; /* Descriptor buffer byte count */ + void *buf_ptr; /* Descriptor buffer pointer */ + struct rxdesc *nxtdesc; /* Next descriptor pointer */ +}; + +struct txdesc { + u32 cmd_sts; /* Descriptor command status */ + u16 l4i_chk; /* CPU provided TCP Checksum */ + u16 byte_cnt; /* Descriptor buffer byte count */ + void *buf_ptr; /* Descriptor buffer ptr */ + struct txdesc *nxtdesc; /* Next descriptor ptr */ +}; + +struct port_priv { + struct eth_device edev; + void __iomem *regs; + struct device_node *np; + int portno; + struct txdesc *txdesc; + struct rxdesc *rxdesc; + struct rxdesc *current_rxdesc; + u8 *rxbuf; +}; + +struct orion_gbe { + void __iomem *regs; + struct clk *clk; + struct port_priv *ports; + int num_ports; +}; + +#define UTXQ 0 /* Used Tx queue number */ +#define URXQ 0 /* Used Rx queue number */ +#define RX_RING_SIZE 4 +#define TRANSFER_TIMEOUT (10 * MSECOND) + +#define NR_ADDR_WINS 6 /* number of address windows */ +#define NR_HIGH_ADDR_WINS 4 /* number of high address windows */ + +#define ACCEPT_MAC_ADDR 0 +#define REJECT_MAC_ADDR 1 + +/* setup DRAM access windows provided by mbus */ +static void eunit_set_dram_access(struct orion_gbe *gbe) +{ + const struct mbus_dram_target_info *dram = mvebu_mbus_dram_info(); + u32 bare = ~0, epap = 0, reg; + int n; + + for (n = 0; n < NR_ADDR_WINS; n++) { + if (n >= dram->num_cs) + continue; + + /* enable BAR */ + bare &= ~BIT(n); + /* set port access protect to R/W */ + epap |= ACCESS_FULL << (n * 2); + + /* configure Base Address and Size */ + reg = ((dram->cs[n].size / SZ_64K) - 1) << 16; + writel(reg, gbe->regs + EUNIT_S(n)); + + reg = dram->cs[n].base & 0xffff0000; + reg |= dram->cs[n].mbus_attr << 8; + reg |= dram->mbus_dram_target_id; + writel(reg, gbe->regs + EUNIT_BA(n)); + if (n < NR_HIGH_ADDR_WINS) + writel(0, gbe->regs + EUNIT_HA(n)); + } + + writel(epap, gbe->regs + EUNIT_PAP); + writel(bare, gbe->regs + EUNIT_BARE); +} + +/* clear entries in unicast, special multicast, and other multicast tables */ +static void port_clear_mac_tables(struct port_priv *port) +{ + int n; + + /* clear unicast tables (DFUTn) */ + for (n = 0; n < 4; n++) + writel(0, port->regs + PORT_DFUT(n)); + + /* clear special (DFSMTn) and other (DFOMTn) multicast tables */ + for (n = 0; n < 64; n++) { + writel(0, port->regs + PORT_DFSMT(n)); + writel(0, port->regs + PORT_DFOMT(n)); + } +} + +/* + * set the port unicast address table + * + * This function adds/removes MAC addresses from the port unicast + * address table. + * + * Locate the proper entry in the Unicast table for the specified MAC + * nibble and set its properties according to function parameters. + * + * @nibble Unicast MAC address, last nibble + * @reject 0 = Accept, 1 = Reject MAC address + */ +static void port_set_unicast_filter(struct port_priv *port, + u8 nibble, int reject) +{ + u8 table, entry, shift; + u32 reg; + + /* Locate the Unicast table entry by nibble */ + nibble &= 0xf; + table = nibble / 4; + entry = nibble % 4; + shift = (DFT_ENTRY_SIZE * entry); + + reg = readl(port->regs + PORT_DFUT(table)); + reg &= DFT_ENTRY_MASK << shift; + if (!reject) + reg |= (DFT_PASS | (URXQ << DFT_QUEUE_SHIFT)) << shift; + writel(reg, port->regs + PORT_DFUT(table)); +} + +/* initialize rx descriptor ring */ +static void port_init_rxdesc_ring(struct port_priv *port) +{ + struct rxdesc *rxdesc, *nxtdesc; + void *rxbuf; + int n; + + /* initialize aligned rx descriptor ring-buffer */ + rxdesc = port->rxdesc; + rxbuf = port->rxbuf; + for (n = 0; n < RX_RING_SIZE; n++) { + nxtdesc = ((void *)rxdesc) + ALIGN(sizeof(*port->rxdesc), 16); + + rxdesc->cmd_sts = RXDESC_OWNED_BY_DMA; + rxdesc->buf_size = ALIGN(PKTSIZE, 8); + rxdesc->byte_cnt = 0; + rxdesc->buf_ptr = rxbuf; + if (n == RX_RING_SIZE-1) + rxdesc->nxtdesc = port->rxdesc; + else + rxdesc->nxtdesc = nxtdesc; + + rxbuf += ALIGN(PKTSIZE, 8); + + rxdesc = nxtdesc; + } + + port->current_rxdesc = port->rxdesc; +} + +/* stop a queue and check for termination */ +static void port_stop_queue(void __iomem *ctrl) +{ + u32 reg = readl(ctrl); + + if (!(reg & 0xff)) + return; + + /* stop active channels only */ + writel((reg << 8), ctrl); + /* wait for all queues to terminate */ + while (readl(ctrl) & 0xff) + ; +} + +static void port_stop(struct port_priv *port) +{ + /* stop all queues */ + port_stop_queue(port->regs + PORT_TQC); + port_stop_queue(port->regs + PORT_RQC); + /* disable port, release reset */ + writel(readl(port->regs + PORT_SC0) & ~PORT_ENABLE, + port->regs + PORT_SC0); + writel(readl(port->regs + PORT_SC1) & ~PORT_RESET, + port->regs + PORT_SC1); + /* clear and mask all interrupts */ + writel(0, port->regs + PORT_IC); + writel(0, port->regs + PORT_IM); + writel(0, port->regs + PORT_EIC); + writel(0, port->regs + PORT_EIM); +} + +static void port_halt(struct eth_device *edev) +{ + struct port_priv *port = edev->priv; + + port_stop(port); +} + +static int port_send(struct eth_device *edev, void *data, int len) +{ + struct port_priv *port = edev->priv; + struct txdesc *txdesc = port->txdesc; + u32 cmd_sts; + int ret; + + /* flush transmit data */ + dma_flush_range((unsigned long)data, (unsigned long)data+len); + + txdesc->cmd_sts = TXDESC_OWNED_BY_DMA; + txdesc->cmd_sts |= TXDESC_FIRST | TXDESC_LAST; + txdesc->cmd_sts |= TXDESC_ZERO_PADDING | TXDESC_GEN_CRC; + txdesc->buf_ptr = data; + txdesc->byte_cnt = len; + + /* assign tx descriptor and issue send command */ + writel((u32)txdesc, port->regs + PORT_TCQDP(UTXQ)); + writel(BIT(UTXQ), port->regs + PORT_TQC); + + /* wait for packet transmit completion */ + ret = wait_on_timeout(TRANSFER_TIMEOUT, + (readl(&txdesc->cmd_sts) & TXDESC_OWNED_BY_DMA) == 0); + if (ret) { + dev_err(&edev->dev, "transmit timeout\n"); + return ret; + } + + cmd_sts = readl(&txdesc->cmd_sts); + if ((cmd_sts & TXDESC_LAST) && (cmd_sts & TXDESC_ERROR)) { + dev_err(&edev->dev, "transmit error %d\n", + (cmd_sts & TXDESC_ERROR_MASK) >> TXDESC_ERROR_SHIFT); + return ret; + } + + return 0; +} + +static int port_recv(struct eth_device *edev) +{ + struct port_priv *port = edev->priv; + struct rxdesc *rxdesc = port->current_rxdesc; + u32 cmd_sts; + int ret = 0; + + /* wait for received packet */ + if (readl(&rxdesc->cmd_sts) & RXDESC_OWNED_BY_DMA) + return 0; + + /* drop malicious packets */ + cmd_sts = readl(&rxdesc->cmd_sts); + if ((cmd_sts & (RXDESC_FIRST | RXDESC_LAST)) != + (RXDESC_FIRST | RXDESC_LAST)) { + dev_err(&edev->dev, "rx packet spread on multiple descriptors\n"); + ret = -EIO; + goto recv_err; + } + + if (cmd_sts & RXDESC_ERROR) { + dev_err(&edev->dev, "receive error %d\n", + (cmd_sts & RXDESC_ERROR_MASK) >> RXDESC_ERROR_SHIFT); + ret = -EIO; + goto recv_err; + } + + /* invalidate current receive buffer */ + dma_inv_range((unsigned long)rxdesc->buf_ptr, + (unsigned long)rxdesc->buf_ptr + + ALIGN(PKTSIZE, 8)); + + /* received packet is padded with two null bytes */ + net_receive(rxdesc->buf_ptr + 0x2, rxdesc->byte_cnt - 0x2); + ret = 0; + +recv_err: + /* reset this and get next rx descriptor*/ + rxdesc->byte_cnt = 0; + rxdesc->buf_size = ALIGN(PKTSIZE, 8); + rxdesc->cmd_sts = RXDESC_OWNED_BY_DMA; + writel((u32)rxdesc->nxtdesc, &port->current_rxdesc); + + return ret; +} + +static int port_set_ethaddr(struct eth_device *edev, unsigned char *mac) +{ + struct port_priv *port = edev->priv; + u32 mac_h = (mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]; + u32 mac_l = (mac[4] << 8) | mac[5]; + + port_clear_mac_tables(port); + + writel(mac_l, port->regs + PORT_MACAL); + writel(mac_h, port->regs + PORT_MACAH); + + /* accept frames for this address */ + port_set_unicast_filter(port, mac[5], ACCEPT_MAC_ADDR); + + return 0; +} + +static int port_get_ethaddr(struct eth_device *edev, unsigned char *mac) +{ + struct port_priv *port = edev->priv; + u32 reg; + + reg = readl(port->regs + PORT_MACAH); + mac[0] = (u8)(reg >> 24) & 0xff; + mac[1] = (u8)(reg >> 16) & 0xff; + mac[2] = (u8)(reg >> 8) & 0xff; + mac[3] = (u8)reg & 0xff; + + reg = readl(port->regs + PORT_MACAL); + mac[4] = (u8)(reg >> 8) & 0xff; + mac[5] = (u8)reg & 0xff; + + return 0; +} + +static int port_open(struct eth_device *edev) +{ + struct port_priv *port = edev->priv; + + /* enable receive queue */ + writel(BIT(URXQ), port->regs + PORT_RQC); + + return 0; +} + +static void port_adjust_link(struct eth_device *edev) +{ + struct port_priv *port = edev->priv; + struct phy_device *phy = edev->phydev; + u32 reg; + + /* disable port */ + reg = readl(port->regs + PORT_SC0); + reg &= ~PORT_ENABLE; + writel(reg, port->regs + PORT_SC0); + + /* setup and enable port */ + reg &= ~(SET_SPEED_MASK | SET_FULL_DUPLEX | SET_FLOWCTRL_ENABLE); + if (phy->speed == SPEED_1000) + reg |= SET_SPEED_1000; + else if (phy->speed == SPEED_100) + reg |= SET_SPEED_100; + else if (phy->speed == SPEED_10) + reg |= SET_SPEED_10; + if (phy->duplex) + reg |= SET_FULL_DUPLEX; + if (phy->pause) + reg |= SET_FLOWCTRL_ENABLE; + reg |= FORCE_LINK_PASS | FORCE_NO_LINK_FAIL | PORT_ENABLE; + + writel(reg, port->regs + PORT_SC0); +} + +static int port_probe(struct device_d *parent, struct port_priv *port) +{ + struct device_node *phynp; + phy_interface_t intf = PHY_INTERFACE_MODE_RGMII; + u32 reg; + int ret; + + /* assume port0 but warn on missing port reg property */ + if (of_property_read_u32(port->np, "reg", &port->portno)) + dev_warn(parent, "port node is missing reg property\n"); + + phynp = of_parse_phandle(port->np, "phy-handle", 0); + if (phynp) { + ret = of_get_phy_mode(port->np); + if (ret > 0) + intf = ret; + } + + port->regs = dev_get_mem_region(parent, 0) + PORTn_REGS(port->portno); + + /* allocate rx/tx descriptors and buffers */ + port->txdesc = dma_alloc_coherent(ALIGN(sizeof(*port->txdesc), 16)); + port->rxdesc = dma_alloc_coherent(RX_RING_SIZE * + ALIGN(sizeof(*port->rxdesc), 16)); + port->rxbuf = dma_alloc(RX_RING_SIZE * ALIGN(PKTSIZE, 8)); + + port_stop(port); + port_init_rxdesc_ring(port); + + /* disable port bandwidth limitation */ + writel(~0, port->regs + PORT_TQTBCNT(UTXQ)); + writel(~0, port->regs + PORT_TQTBC(UTXQ)); + writel(0, port->regs + PORT_MTU); + /* assign initial rx descriptor */ + writel((u32)port->current_rxdesc, port->regs + PORT_CRDP(URXQ)); + /* setup SDMA with maximum burst and no swap */ + reg = RX_BURST_SIZE_16 | RX_BLM_NO_SWAP | + TX_BURST_SIZE_16 | TX_BLM_NO_SWAP; + writel(reg, port->regs + PORT_SDC); + + /* port configuration */ + reg = DEFAULT_RXQ(URXQ) | DEFAULT_ARPQ(URXQ); + writel(reg, port->regs + PORT_C); + writel(0, port->regs + PORT_CX); + + reg = SC0_RESERVED | MRU_1518; + reg |= DISABLE_ANEG_DUPLEX | DISABLE_ANEG_FLOWCTRL | DISABLE_ANEG_SPEED; + writel(reg, port->regs + PORT_SC0); + + reg = SC1_RESERVED; + reg |= DEFAULT_COL_LIMIT | COL_ON_BACKPRESS | INBAND_ANEG_BYPASS; + if (intf == PHY_INTERFACE_MODE_RGMII) + reg |= RGMII_ENABLE; + writel(reg, port->regs + PORT_SC1); + + /* register eth device */ + port->edev.priv = port; + port->edev.open = port_open; + port->edev.send = port_send; + port->edev.recv = port_recv; + port->edev.halt = port_halt; + port->edev.set_ethaddr = port_set_ethaddr; + port->edev.get_ethaddr = port_get_ethaddr; + port->edev.parent = parent; + + ret = eth_register(&port->edev); + if (ret) + return ret; + + /* attach phy device */ + if (phynp) { + ret = of_phy_device_connect(&port->edev, phynp, + port_adjust_link, 0, intf); + if (ret) + return ret; + } + + return 0; +} + +static int orion_gbe_probe(struct device_d *dev) +{ + struct orion_gbe *gbe; + struct port_priv *ppriv; + struct device_node *pnp; + int ret; + + gbe = xzalloc(sizeof(*gbe)); + dev->priv = gbe; + + gbe->regs = dev_get_mem_region(dev, 0); + gbe->clk = clk_get(dev, 0); + if (!IS_ERR(gbe->clk)) + clk_enable(gbe->clk); + + eunit_set_dram_access(gbe); + + /* + * Orion SoCs only have one port per controller, but the + * IP itself supports more than one port per controller. + * Although untested, the driver should also be able to + * deal with multi-port controllers. + */ + for_each_child_of_node(dev->device_node, pnp) + gbe->num_ports++; + + gbe->ports = xzalloc(gbe->num_ports * sizeof(*gbe->ports)); + + ppriv = gbe->ports; + for_each_child_of_node(dev->device_node, pnp) { + ppriv->np = pnp; + + ret = port_probe(dev, ppriv); + if (ret) + return ret; + + ppriv++; + } + + return 0; +} + +static void orion_gbe_remove(struct device_d *dev) +{ + struct orion_gbe *gbe = dev->priv; + int n; + + for (n = 0; n < gbe->num_ports; n++) + port_halt(&gbe->ports[n].edev); + + /* disable all address windows */ + writel(~0, gbe->regs + EUNIT_BARE); + + if (!IS_ERR(gbe->clk)) + clk_disable(gbe->clk); +} + +static struct of_device_id orion_gbe_dt_ids[] = { + { .compatible = "marvell,orion-eth", }, + { .compatible = "marvell,kirkwood-eth", }, + { } +}; + +static struct driver_d orion_gbe_driver = { + .name = "orion-gbe", + .probe = orion_gbe_probe, + .remove = orion_gbe_remove, + .of_compatible = DRV_OF_COMPAT(orion_gbe_dt_ids), +}; +device_platform_driver(orion_gbe_driver); diff --git a/drivers/net/orion-gbe.h b/drivers/net/orion-gbe.h new file mode 100644 index 000000000..e5b18b1eb --- /dev/null +++ b/drivers/net/orion-gbe.h @@ -0,0 +1,236 @@ +/* + * (C) Copyright 2014 + * Pengutronix, Michael Grzeschik + * Sebastian Hesselbarth + * + * based on kirkwood_egiga driver from u-boot + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + * + * based on - Driver for MV64360X ethernet ports + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __ORION_GBE_ +#define __ORION_GBE_ + +/* Ethernet Unit Base Address */ +#define EUNIT_BA(x) (0x200 + (x) * 0x8) +/* Ethernet Unit Size */ +#define EUNIT_S(x) (0x204 + (x) * 0x8) +/* Ethernet Unit High Address */ +#define EUNIT_HA(x) (0x280 + (x) * 0x4) +/* Ethernet Unit Base Address Enable */ +#define EUNIT_BARE 0x290 +/* Ethernet Unit Port Access Protect */ +#define EUNIT_PAP 0x294 +#define NO_ACCESS 0 +#define ACCESS_READ_ONLY 1 +#define ACCESS_FULL 3 + +/* Port Registers Offset */ +#define PORTn_REGS(x) (0x400 + (x) * 0x400) +/* Port Configuration */ +#define PORT_C 0x000 +#define PROMISCUOUS_MODE BIT(0) +#define DEFAULT_RXQ(x) ((x) << 1) +#define DEFAULT_ARPQ(x) ((x) << 4) +#define BCAST_OTHER_REJECT BIT(7) +#define BCAST_IP_REJECT BIT(8) +#define BCAST_ARP_REJECT BIT(9) +#define AUTO_SET_NO_TX_ERR BIT(12) +#define TCPQ_CAPTURE_ENABLE BIT(14) +#define UDPQ_CAPTURE_ENABLE BIT(15) +#define DEFAULT_TCPQ(x) ((x) << 16) +#define DEFAULT_UDPQ(x) ((x) << 19) +#define DEFAULT_BPDUQ(x) ((x) << 22) +#define RX_TCP_CHKSUM_HEADER BIT(25) +/* Port Configuration Extended */ +#define PORT_CX 0x004 +#define TX_CRC_GEN_DISABLE BIT(3) +#define BPDUQ_CAPTURE_ENABLE BIT(0) +/* Port MAC Address High */ +#define PORT_MACAL 0x014 +/* Port MAC Address Low */ +#define PORT_MACAH 0x018 +/* Port SDMA Configuration */ +#define PORT_SDC 0x01c +#define TX_BURST_SIZE_1 (0 << 22) +#define TX_BURST_SIZE_2 (1 << 22) +#define TX_BURST_SIZE_4 (2 << 22) +#define TX_BURST_SIZE_8 (3 << 22) +#define TX_BURST_SIZE_16 (4 << 22) +#define TX_BLM_SWAP (0 << 5) +#define TX_BLM_NO_SWAP (1 << 5) +#define RX_BLM_SWAP (0 << 4) +#define RX_BLM_NO_SWAP (1 << 4) +#define RX_BURST_SIZE_1 (0 << 1) +#define RX_BURST_SIZE_2 (1 << 1) +#define RX_BURST_SIZE_4 (2 << 1) +#define RX_BURST_SIZE_8 (3 << 1) +#define RX_BURST_SIZE_16 (4 << 1) +/* Port Serial Control 0 */ +#define PORT_SC0 0x03c +#define SC0_RESERVED BIT(9) +#define PORT_ENABLE BIT(0) +#define FORCE_LINK_PASS BIT(1) +#define DISABLE_ANEG_DUPLEX BIT(2) +#define DISABLE_ANEG_FLOWCTRL BIT(3) +#define ADVERTISE_PAUSE BIT(4) +#define FORCE_FLOWCTRL_OFF (0 << 5) +#define FORCE_FLOWCTRL_ON (1 << 5) +#define FORCE_FLOWCTRL_MASK (3 << 5) +#define FORCE_BACKPRESS_NO_JAM (0 << 7) +#define FORCE_BACKPRESS_JAM (1 << 7) +#define FORCE_BACKPRESS_MASK (3 << 7) +#define FORCE_NO_LINK_FAIL BIT(10) +#define DISABLE_ANEG_SPEED BIT(13) +#define ADVERTISE_DTE BIT(14) +#define MII_PHY_MODE BIT(15) +#define MII_SRC_SYNCHRONOUS BIT(16) +#define MRU_1518 (0 << 17) +#define MRU_1522 (1 << 17) +#define MRU_1552 (2 << 17) +#define MRU_9022 (3 << 17) +#define MRU_9192 (4 << 17) +#define MRU_9700 (5 << 17) +#define MRU_MASK (7 << 17) +#define SET_FULL_DUPLEX BIT(21) +#define SET_FLOWCTRL_ENABLE BIT(22) +#define SET_SPEED_1000 (1 << 23) +#define SET_SPEED_10 (0 << 23) +#define SET_SPEED_100 (2 << 23) +#define SET_SPEED_MASK (3 << 23) +/* Port Status 0 */ +#define PORT_S0 0x044 +/* Port Trasmit Queue Command */ +#define PORT_TQC 0x048 +/* Port Serial Control 1 */ +#define PORT_SC1 0x04c +#define SC1_RESERVED (0x2 << 9) +#define LOOPBACK_PCS BIT(1) +#define RGMII_ENABLE BIT(3) +#define PORT_RESET BIT(4) +#define CLK125_BYPASS BIT(5) +#define INBAND_ANEG BIT(6) +#define INBAND_ANEG_BYPASS BIT(7) +#define INBAND_ANEG_RESTART BIT(8) +#define LIMIT_TO_1000BASEX BIT(11) +#define COL_ON_BACKPRESS BIT(15) +#define COL_LIMIT(x) (((x) & 0xfff) << 16) +#define DEFAULT_COL_LIMIT COL_LIMIT(0x23) +#define COL_ON_BACKPRESS BIT(15) +#define EN_MII_ODD_PREAMBLE BIT(22) +/* Port Status 1 */ +#define PORT_S1 0x050 +/* Port Interrupt Cause */ +#define PORT_IC 0x060 +/* Port Interrupt Mask */ +#define PORT_IM 0x068 +#define INT_SUM BIT(31) +#define TX_END BIT(19) +#define RXQ_ERR (15 << 11) +#define RX_ERR BIT(10) +#define TXQ_ERR (15 << 2) +#define EXTENDED_INT BIT(1) +#define RX_RETURN BIT(0) +/* Port Extended Interrupt Cause */ +#define PORT_EIC 0x064 +/* Port Extended Interrupt Mask */ +#define PORT_EIM 0x06c +#define EXTENDED_INT_SUM BIT(31) +#define PRBS_ERR BIT(25) +#define INTERNAL_ADDR_ERR BIT(23) +#define LINK_CHANGE BIT(20) +#define TX_UNDERRUN BIT(19) +#define RX_OVERRUN BIT(18) +#define PHY_CHANGE BIT(16) +#define TX_ERR BIT(8) +#define TX_RETURN BIT(0) +/* Port Maximum Transmit Unit */ +#define PORT_MTU 0x0e8 +/* Port Current Receive Descriptor Pointer */ +#define PORT_CRDP(x) (0x20c + (x) * 0x10) +/* Port Receive Queue Command */ +#define PORT_RQC 0x280 +/* Port Transmit Current Queue Descriptor Pointer */ +#define PORT_TCQDP(x) (0x2c0 + (x) * 0x04) +/* Port Transmit Queue Token Bucket Counter */ +#define PORT_TQTBCNT(x) (0x300 + (x) * 0x10) +/* Port Transmit Queue Token Bucket Configuration */ +#define PORT_TQTBC(x) (0x304 + (x) * 0x10) + +#define PORT_DFSMT(x) (0x1000 + ((x) * 0x04)) +#define PORT_DFOMT(x) (0x1100 + ((x) * 0x04)) +#define PORT_DFUT(x) (0x1200 + ((x) * 0x04)) + +#define DFT_ENTRY_MASK 0xff +#define DFT_ENTRY_SIZE 8 +#define DFT_QUEUE_SHIFT 1 +#define DFT_PASS BIT(0) + +#define RXDESC_ERROR BIT(0) +#define RXDESC_ERROR_CRC (0 << 1) +#define RXDESC_ERROR_OVERRUN (1 << 1) +#define RXDESC_ERROR_MAXLEN (2 << 1) +#define RXDESC_ERROR_RESOURCE (3 << 1) +#define RXDESC_ERROR_MASK (3 << 1) +#define RXDESC_ERROR_SHIFT 1 +#define RXDESC_L4_CHECKSUM(x) (((x) & (0xffff << 3)) >> 3) +#define RXDESC_VLAN_TAGGED BIT(19) +#define RXDESC_BDPU BIT(20) +#define RXDESC_FRAME_TCP (0 << 21) +#define RXDESC_FRAME_UDP (1 << 21) +#define RXDESC_FRAME_OTHER (2 << 21) +#define RXDESC_FRAME_MASK (3 << 21) +#define RXDESC_L2_IS_ETHERNET BIT(23) +#define RXDESC_L4_IS_IPV4 BIT(24) +#define RXDESC_L4_HEADER_OK BIT(25) +#define RXDESC_LAST BIT(26) +#define RXDESC_FIRST BIT(27) +#define RXDESC_UNKNOWN_DEST BIT(28) +#define RXDESC_ENABLE_IRQ BIT(29) +#define RXDESC_L4_CHECKSUM_OK BIT(30) +#define RXDESC_OWNED_BY_DMA BIT(31) + +#define RXDESC_BYTECOUNT_FRAG BIT(2) + +#define TXDESC_ERROR BIT(0) +#define TXDESC_ERROR_LATE_COLL (0 << 1) +#define TXDESC_ERROR_UNDERRUN (1 << 1) +#define TXDESC_ERROR_RET_LIMIT (2 << 1) +#define TXDESC_ERROR_MASK (3 << 1) +#define TXDESC_ERROR_SHIFT 1 +#define TXDESC_LCC_SNAP BIT(9) +#define TXDESC_L4_CHK_FIRST BIT(10) +#define TXDESC_IPV4_HEADER_LEN(x) (((x) & 0xf) << 11) +#define TXDESC_VLAN_TAGGED BIT(15) +#define TXDESC_FRAME_TCP (0 << 16) +#define TXDESC_FRAME_UDP (1 << 16) +#define TXDESC_GEN_FRAME_CHECKSUM BIT(17) +#define TXDESC_GEN_IPV4_CHECKSUM BIT(18) +#define TXDESC_ZERO_PADDING BIT(19) +#define TXDESC_LAST BIT(20) +#define TXDESC_FIRST BIT(21) +#define TXDESC_GEN_CRC BIT(22) /* Orion5x only */ +#define TXDESC_ENABLE_IRQ BIT(23) +#define TXDESC_NO_AUTO_RETURN BIT(30) +#define TXDESC_OWNED_BY_DMA BIT(31) + +#endif diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 260774e26..02e1e83c0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -33,6 +33,14 @@ config SMSC_PHY ---help--- Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs +comment "MII bus device drivers" + +config MDIO_MVEBU + bool "Driver for MVEBU SoC MDIO bus" + depends on ARCH_MVEBU + ---help--- + Driver for the MDIO bus found on Marvell EBU SoCs. + endif endmenu diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a00cc76ef..7f8277f1d 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_SMSC_PHY) += smsc.o + +obj-$(CONFIG_MDIO_MVEBU) += mdio-mvebu.o diff --git a/drivers/net/phy/mdio-mvebu.c b/drivers/net/phy/mdio-mvebu.c new file mode 100644 index 000000000..f8b492a24 --- /dev/null +++ b/drivers/net/phy/mdio-mvebu.c @@ -0,0 +1,152 @@ +/* + * Marvell MVEBU SoC MDIO interface driver + * + * (C) Copyright 2014 + * Pengutronix, Michael Grzeschik + * Sebastian Hesselbarth + * + * based on mvmdio driver from Linux + * Copyright (C) 2012 Marvell + * Thomas Petazzoni + * + * Since the MDIO interface of Marvell network interfaces is shared + * between all network interfaces, having a single driver allows to + * handle concurrent accesses properly (you may have four Ethernet + * ports, but they in fact share the same SMI interface to access + * the MDIO bus). This driver is currently used by the mvneta and + * mv643xx_eth drivers. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMI_DATA_SHIFT 0 +#define SMI_PHY_ADDR_SHIFT 16 +#define SMI_PHY_REG_SHIFT 21 +#define SMI_READ_OPERATION BIT(26) +#define SMI_WRITE_OPERATION 0 +#define SMI_READ_VALID BIT(27) +#define SMI_BUSY BIT(28) +#define ERR_INT_CAUSE 0x007C +#define ERR_INT_SMI_DONE BIT(4) +#define ERR_INT_MASK BIT(7) + +struct mdio_priv { + struct mii_bus miibus; + void __iomem *regs; + struct clk *clk; +}; + +#define SMI_POLL_TIMEOUT (10 * MSECOND) + +static int mvebu_mdio_wait_ready(struct mdio_priv *priv) +{ + int ret = wait_on_timeout(SMI_POLL_TIMEOUT, + (readl(priv->regs) & SMI_BUSY) == 0); + + if (ret) + dev_err(&priv->miibus.dev, "timeout, SMI busy for too long\n"); + + return ret; +} + +static int mvebu_mdio_read(struct mii_bus *bus, int addr, int reg) +{ + struct mdio_priv *priv = bus->priv; + u32 smi; + int ret; + + ret = mvebu_mdio_wait_ready(priv); + if (ret) + return ret; + + smi = SMI_READ_OPERATION; + smi |= (addr << SMI_PHY_ADDR_SHIFT) | (reg << SMI_PHY_REG_SHIFT); + writel(smi, priv->regs); + + ret = mvebu_mdio_wait_ready(priv); + if (ret) + return ret; + + smi = readl(priv->regs); + if ((smi & SMI_READ_VALID) == 0) { + dev_err(&bus->dev, "SMI bus read not valid\n"); + return -ENODEV; + } + + return smi & 0xFFFF; +} + +static int mvebu_mdio_write(struct mii_bus *bus, int addr, int reg, u16 data) +{ + struct mdio_priv *priv = bus->priv; + u32 smi; + int ret; + + ret = mvebu_mdio_wait_ready(priv); + if (ret) + return ret; + + smi = SMI_WRITE_OPERATION; + smi |= (addr << SMI_PHY_ADDR_SHIFT) | (reg << SMI_PHY_REG_SHIFT); + smi |= data << SMI_DATA_SHIFT; + writel(smi, priv->regs); + + return 0; +} + +static int mvebu_mdio_probe(struct device_d *dev) +{ + struct mdio_priv *priv; + + priv = xzalloc(sizeof(*priv)); + dev->priv = priv; + + priv->regs = dev_get_mem_region(dev, 0); + if (!priv->regs) + return -ENOMEM; + + priv->clk = clk_get(dev, NULL); + if (!IS_ERR(priv->clk)) + clk_enable(priv->clk); + + priv->miibus.priv = priv; + priv->miibus.parent = dev; + priv->miibus.read = mvebu_mdio_read; + priv->miibus.write = mvebu_mdio_write; + + return mdiobus_register(&priv->miibus); +} + +static void mvebu_mdio_remove(struct device_d *dev) +{ + struct mdio_priv *priv = dev->priv; + + mdiobus_unregister(&priv->miibus); + + if (!IS_ERR(priv->clk)) + clk_disable(priv->clk); +} + +static struct of_device_id mvebu_mdio_dt_ids[] = { + { .compatible = "marvell,orion-mdio" }, + { } +}; + +static struct driver_d mvebu_mdio_driver = { + .name = "mvebu-mdio", + .probe = mvebu_mdio_probe, + .remove = mvebu_mdio_remove, + .of_compatible = DRV_OF_COMPAT(mvebu_mdio_dt_ids), +}; +device_platform_driver(mvebu_mdio_driver); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index faa5c26c2..879939d4a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -331,6 +331,37 @@ fail: return ret; } +#if defined(CONFIG_OFTREE) +int of_phy_device_connect(struct eth_device *edev, struct device_node *phy_np, + void (*adjust_link) (struct eth_device *edev), + u32 flags, phy_interface_t interface) +{ + struct device_node *bus_np; + struct mii_bus *miibus; + int phy_addr = -ENODEV; + + if (!phy_np) + return -EINVAL; + + of_property_read_u32(phy_np, "reg", &phy_addr); + + bus_np = of_get_parent(phy_np); + if (!bus_np) + return -ENODEV; + + for_each_mii_bus(miibus) { + if (miibus->parent && miibus->parent->device_node == bus_np) + return phy_device_connect(edev, miibus, phy_addr, + adjust_link, flags, interface); + } + + dev_err(&edev->dev, "unable to mdio bus for phy %s\n", + phy_np->full_name); + + return -ENODEV; +} +#endif + /* Generic PHY support and helper functions */ /** diff --git a/include/linux/mbus.h b/include/linux/mbus.h new file mode 100644 index 000000000..578ff3314 --- /dev/null +++ b/include/linux/mbus.h @@ -0,0 +1,61 @@ +/* + * Marvell MBUS common definitions. + * + * Copyright (C) 2008 Marvell Semiconductor + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __LINUX_MBUS_H +#define __LINUX_MBUS_H + +struct resource; + +struct mbus_dram_target_info { + /* + * The 4-bit MBUS target ID of the DRAM controller. + */ + u8 mbus_dram_target_id; + + /* + * The base address, size, and MBUS attribute ID for each + * of the possible DRAM chip selects. Peripherals are + * required to support at least 4 decode windows. + */ + int num_cs; + struct mbus_dram_window { + u8 cs_index; + u8 mbus_attr; + u32 base; + u32 size; + } cs[4]; +}; + +/* Flags for PCI/PCIe address decoding regions */ +#define MVEBU_MBUS_PCI_IO 0x1 +#define MVEBU_MBUS_PCI_MEM 0x2 +#define MVEBU_MBUS_PCI_WA 0x3 + +/* + * Magic value that explicits that we don't need a remapping-capable + * address decoding window. + */ +#define MVEBU_MBUS_NO_REMAP (0xffffffff) + +/* Maximum size of a mbus window name */ +#define MVEBU_MBUS_MAX_WINNAME_SZ 32 + +const struct mbus_dram_target_info *mvebu_mbus_dram_info(void); +void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); +void mvebu_mbus_get_pcie_io_aperture(struct resource *res); +int mvebu_mbus_add_window_remap_by_id(unsigned int target, + unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap); +int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size); +int mvebu_mbus_del_window(phys_addr_t base, size_t size); + +#endif /* __LINUX_MBUS_H */ diff --git a/include/linux/phy.h b/include/linux/phy.h index c8980b019..9567c43e3 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -280,6 +280,20 @@ int phy_device_connect(struct eth_device *dev, struct mii_bus *bus, int addr, void (*adjust_link) (struct eth_device *edev), u32 flags, phy_interface_t interface); +#if defined(CONFIG_OFTREE) +int of_phy_device_connect(struct eth_device *edev, struct device_node *phy_np, + void (*adjust_link) (struct eth_device *edev), + u32 flags, phy_interface_t interface); +#else +static inline int of_phy_device_connect(struct eth_device *edev, + struct device_node *phy_np, + void (*adjust_link) (struct eth_device *edev), + u32 flags, phy_interface_t interface) +{ + return -ENOSYS; +} +#endif + int phy_update_status(struct phy_device *phydev); int phy_wait_aneg_done(struct phy_device *phydev); diff --git a/scripts/kwbimage.c b/scripts/kwbimage.c index cd8745609..f8abeb178 100644 --- a/scripts/kwbimage.c +++ b/scripts/kwbimage.c @@ -701,18 +701,18 @@ static int image_create_payload(void *payload_start, size_t payloadsz, if (ret < 0) { fprintf(stderr, "Cannot stat payload file %s\n", payload_filename); + fclose(payload); return ret; } ret = fread(payload_start, s.st_size, 1, payload); + fclose(payload); if (ret != 1) { fprintf(stderr, "Cannot read payload file %s\n", payload_filename); return -1; } - fclose(payload); - *payload_checksum = image_checksum32(payload_start, payloadsz); return 0; } @@ -1343,6 +1343,7 @@ static int image_create(const char *input, const char *output, rewind(fcfg); ret = image_create_config_parse(fcfg, image_cfg, &cfgn); + fclose(fcfg); if (ret) { free(image_cfg); return -1;