diff options
30 files changed, 1744 insertions, 185 deletions
@@ -166,27 +166,6 @@ Directory Hierarchy: Software Configuration: ======================= -Configuration is usually done using C preprocessor defines; the -rationale behind that is to avoid dead code whenever possible. - -There are two classes of configuration variables: - -* Configuration _OPTIONS_: - These are selectable by the user and have names beginning with - "CONFIG_". - -* Configuration _SETTINGS_: - These depend on the hardware etc. and should not be meddled with if - you don't know what you're doing; they have names beginning with - "CONFIG_SYS_". - -Previously, all configuration was done by hand, which involved creating -symbolic links and editing configuration files manually. More recently, -U-Boot has added the Kbuild infrastructure used by the Linux kernel, -allowing you to use the "make menuconfig" command to configure your -build. - - Selection of Processor Architecture and Board Type: --------------------------------------------------- diff --git a/arch/arm/dts/armada-370-xp.dtsi b/arch/arm/dts/armada-370-xp.dtsi index 50fc0be9f7..4cd168c957 100644 --- a/arch/arm/dts/armada-370-xp.dtsi +++ b/arch/arm/dts/armada-370-xp.dtsi @@ -246,7 +246,7 @@ }; nand@d0000 { - compatible = "marvell,armada370-nand"; + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/dts/armada-375.dtsi b/arch/arm/dts/armada-375.dtsi index ff0ad7a9c7..fcb9245fd4 100644 --- a/arch/arm/dts/armada-375.dtsi +++ b/arch/arm/dts/armada-375.dtsi @@ -541,7 +541,7 @@ }; nand@d0000 { - compatible = "marvell,armada370-nand"; + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/dts/armada-385-turris-omnia-u-boot.dtsi b/arch/arm/dts/armada-385-turris-omnia-u-boot.dtsi index 5a22cc64a1..ec12298fd4 100644 --- a/arch/arm/dts/armada-385-turris-omnia-u-boot.dtsi +++ b/arch/arm/dts/armada-385-turris-omnia-u-boot.dtsi @@ -28,11 +28,8 @@ i2c@5 { u-boot,dm-pre-reloc; - /* ATSHA204A at address 0x64 */ crypto@64 { u-boot,dm-pre-reloc; - compatible = "atmel,atsha204a"; - reg = <0x64>; }; }; }; @@ -42,7 +39,7 @@ &spi0 { u-boot,dm-pre-reloc; - spi-nor@0 { + flash@0 { u-boot,dm-pre-reloc; partitions { diff --git a/arch/arm/dts/armada-385-turris-omnia.dts b/arch/arm/dts/armada-385-turris-omnia.dts index 7f1478edfd..0be55f8bf6 100644 --- a/arch/arm/dts/armada-385-turris-omnia.dts +++ b/arch/arm/dts/armada-385-turris-omnia.dts @@ -1,43 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) /* * Device Tree file for the Turris Omnia * * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org> * Copyright (C) 2016 Tomas Hlavacek <tmshlvkc@gmail.com> * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) 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. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf */ @@ -45,6 +12,7 @@ #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/input/input.h> +#include <dt-bindings/leds/common.h> #include "armada-385.dtsi" / { @@ -70,7 +38,8 @@ ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000 MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000 MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000 - MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>; + MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000 + MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>; internal-regs { @@ -108,19 +77,48 @@ pcie@1,0 { /* Port 0, Lane 0 */ status = "okay"; + slot-power-limit-milliwatt = <10000>; }; pcie@2,0 { /* Port 1, Lane 0 */ status = "okay"; + slot-power-limit-milliwatt = <10000>; }; pcie@3,0 { /* Port 2, Lane 0 */ status = "okay"; + slot-power-limit-milliwatt = <10000>; }; }; }; + + sfp: sfp { + compatible = "sff,sfp"; + i2c-bus = <&sfp_i2c>; + tx-fault-gpios = <&pcawan 0 GPIO_ACTIVE_HIGH>; + tx-disable-gpios = <&pcawan 1 GPIO_ACTIVE_HIGH>; + rate-select0-gpios = <&pcawan 2 GPIO_ACTIVE_HIGH>; + los-gpios = <&pcawan 3 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&pcawan 4 GPIO_ACTIVE_LOW>; + maximum-power-milliwatt = <3000>; + + /* + * For now this has to be enabled at boot time by U-Boot when + * a SFP module is present. Read more in the comment in the + * eth2 node below. + */ + status = "disabled"; + }; +}; + +&bm { + status = "okay"; +}; + +&bm_bppi { + status = "okay"; }; /* Connected to 88E6176 switch, port 6 */ @@ -129,6 +127,9 @@ pinctrl-0 = <&ge0_rgmii_pins>; status = "okay"; phy-mode = "rgmii"; + buffer-manager = <&bm>; + bm,pool-long = <0>; + bm,pool-short = <3>; fixed-link { speed = <1000>; @@ -142,6 +143,9 @@ pinctrl-0 = <&ge1_rgmii_pins>; status = "okay"; phy-mode = "rgmii"; + buffer-manager = <&bm>; + bm,pool-long = <1>; + bm,pool-short = <3>; fixed-link { speed = <1000>; @@ -151,9 +155,23 @@ /* WAN port */ ð2 { + /* + * eth2 is connected via a multiplexor to both the SFP cage and to + * ethernet-phy@1. The multiplexor switches the signal to SFP cage when + * a SFP module is present, as determined by the mode-def0 GPIO. + * + * Until kernel supports this configuration properly, in case SFP module + * is present, U-Boot has to enable the sfp node above, remove phy + * handle and add managed = "in-band-status" property. + */ status = "okay"; phy-mode = "sgmii"; - phy = <&phy1>; + phy-handle = <&phy1>; + phys = <&comphy5 2>; + sfp = <&sfp>; + buffer-manager = <&bm>; + bm,pool-long = <2>; + bm,pool-short = <3>; }; &i2c0 { @@ -166,18 +184,128 @@ #address-cells = <1>; #size-cells = <0>; reg = <0x70>; - status = "okay"; i2c@0 { #address-cells = <1>; #size-cells = <0>; reg = <0>; - /* STM32F0 command interface at address 0x2a */ - /* leds device (in STM32F0) at address 0x2b */ + /* MCU command i2c API */ + mcu: mcu@2a { + compatible = "cznic,turris-omnia-mcu"; + reg = <0x2a>; + gpio-controller; + #gpio-cells = <3>; + }; + + led-controller@2b { + compatible = "cznic,turris-omnia-leds"; + reg = <0x2b>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + /* + * LEDs are controlled by MCU (STM32F0) at + * address 0x2b. + * + * LED functions are not stable yet: + * - there are 3 LEDs connected via MCU to PCIe + * ports. One of these ports supports mSATA. + * There is no mSATA nor PCIe function. + * For now we use LED_FUNCTION_WLAN, since + * in most cases users have wifi cards in + * these slots + * - there are 2 LEDs dedicated for user: A and + * B. Again there is no such function defined. + * For now we use LED_FUNCTION_INDICATOR + */ + + multi-led@0 { + reg = <0x0>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <2>; + }; + + multi-led@1 { + reg = <0x1>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <1>; + }; + + multi-led@2 { + reg = <0x2>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_WLAN; + function-enumerator = <3>; + }; + + multi-led@3 { + reg = <0x3>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_WLAN; + function-enumerator = <2>; + }; + + multi-led@4 { + reg = <0x4>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_WLAN; + function-enumerator = <1>; + }; + + multi-led@5 { + reg = <0x5>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_WAN; + }; + + multi-led@6 { + reg = <0x6>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_LAN; + function-enumerator = <4>; + }; + + multi-led@7 { + reg = <0x7>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_LAN; + function-enumerator = <3>; + }; + + multi-led@8 { + reg = <0x8>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_LAN; + function-enumerator = <2>; + }; + + multi-led@9 { + reg = <0x9>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + }; + + multi-led@a { + reg = <0xa>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_LAN; + function-enumerator = <0>; + }; + + multi-led@b { + reg = <0xb>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_POWER; + }; + }; eeprom@54 { - compatible = "at,24c64"; + compatible = "atmel,24c64"; reg = <0x54>; /* The EEPROM contains data for bootloader. @@ -216,7 +344,7 @@ /* routed to PCIe2 connector (CN62A) */ }; - i2c@4 { + sfp_i2c: i2c@4 { #address-cells = <1>; #size-cells = <0>; reg = <4>; @@ -229,7 +357,11 @@ #size-cells = <0>; reg = <5>; - /* ATSHA204A at address 0x64 */ + /* ATSHA204A-MAHDA-T crypto module */ + crypto@64 { + compatible = "atmel,atsha204a"; + reg = <0x64>; + }; }; i2c@6 { @@ -271,23 +403,29 @@ pinctrl-0 = <&mdio_pins>; status = "okay"; - phy1: phy@1 { - status = "okay"; - compatible = "ethernet-phy-id0141.0DD1", "ethernet-phy-ieee802.3-c22"; + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; reg = <1>; + marvell,reg-init = <3 18 0 0x4985>, + <3 16 0xfff0 0x0001>; /* irq is connected to &pcawan pin 7 */ }; /* Switch MV88E6176 at address 0x10 */ switch@10 { + pinctrl-names = "default"; + pinctrl-0 = <&swint_pins>; compatible = "marvell,mv88e6085"; #address-cells = <1>; #size-cells = <0>; - dsa,member = <0 0>; + dsa,member = <0 0>; reg = <0x10>; + interrupt-parent = <&gpio1>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + ports { #address-cells = <1>; #size-cells = <0>; @@ -340,12 +478,17 @@ marvell,function = "gpio"; }; + swint_pins: swint-pins { + marvell,pins = "mpp45"; + marvell,function = "gpio"; + }; + spi0cs0_pins: spi0cs0-pins { marvell,pins = "mpp25"; marvell,function = "spi0"; }; - spi0cs1_pins: spi0cs1-pins { + spi0cs2_pins: spi0cs2-pins { marvell,pins = "mpp26"; marvell,function = "spi0"; }; @@ -356,7 +499,7 @@ pinctrl-0 = <&spi0_pins &spi0cs0_pins>; status = "okay"; - spi-nor@0 { + flash@0 { compatible = "spansion,s25fl164k", "jedec,spi-nor"; #address-cells = <1>; #size-cells = <1>; @@ -380,7 +523,7 @@ }; }; - /* MISO, MOSI, SCLK and CS1 are routed to pin header CN11 */ + /* MISO, MOSI, SCLK and CS2 are routed to pin header CN11 */ }; &uart0 { diff --git a/arch/arm/dts/armada-385.dtsi b/arch/arm/dts/armada-385.dtsi index 581a7d9bea..48072fc7fd 100644 --- a/arch/arm/dts/armada-385.dtsi +++ b/arch/arm/dts/armada-385.dtsi @@ -69,17 +69,26 @@ reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 0x81000000 0 0 0x81000000 0x1 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, + <0 0 0 3 &pcie1_intc 2>, + <0 0 0 4 &pcie1_intc 3>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; resets = <&systemc 0 0>; status = "disabled"; + pcie1_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */ @@ -89,17 +98,26 @@ reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 0x81000000 0 0 0x81000000 0x2 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie2_intc 0>, + <0 0 0 2 &pcie2_intc 1>, + <0 0 0 3 &pcie2_intc 2>, + <0 0 0 4 &pcie2_intc 3>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; resets = <&systemc 0 1>; status = "disabled"; + pcie2_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* x1 port */ @@ -109,17 +127,26 @@ reg = <0x1800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 0x81000000 0 0 0x81000000 0x3 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie3_intc 0>, + <0 0 0 2 &pcie3_intc 1>, + <0 0 0 3 &pcie3_intc 2>, + <0 0 0 4 &pcie3_intc 3>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; resets = <&systemc 0 2>; status = "disabled"; + pcie3_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; /* @@ -132,17 +159,26 @@ reg = <0x2000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + interrupt-names = "intx"; + interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; #interrupt-cells = <1>; ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 0x81000000 0 0 0x81000000 0x4 0 1 0>; bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie4_intc 0>, + <0 0 0 2 &pcie4_intc 1>, + <0 0 0 3 &pcie4_intc 2>, + <0 0 0 4 &pcie4_intc 3>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; resets = <&systemc 0 3>; status = "disabled"; + pcie4_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; }; }; }; diff --git a/arch/arm/dts/armada-38x.dtsi b/arch/arm/dts/armada-38x.dtsi index 061bd78520..3e970097c8 100644 --- a/arch/arm/dts/armada-38x.dtsi +++ b/arch/arm/dts/armada-38x.dtsi @@ -9,13 +9,15 @@ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> */ -#include "skeleton.dtsi" #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/irq.h> #define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) / { + #address-cells = <1>; + #size-cells = <1>; + model = "Marvell Armada 38x family SoC"; compatible = "marvell,armada380"; @@ -103,6 +105,11 @@ #size-cells = <1>; ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; + sdramc: sdramc@1400 { + compatible = "marvell,armada-xp-sdram-controller"; + reg = <0x1400 0x500>; + }; + L2: cache-controller@8000 { compatible = "arm,pl310-cache"; reg = <0x8000 0x1000>; @@ -148,7 +155,6 @@ #address-cells = <1>; #size-cells = <0>; interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>; - timeout-ms = <1000>; clocks = <&coreclk 0>; status = "disabled"; }; @@ -159,13 +165,12 @@ #address-cells = <1>; #size-cells = <0>; interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>; - timeout-ms = <1000>; clocks = <&coreclk 0>; status = "disabled"; }; uart0: serial@12000 { - compatible = "snps,dw-apb-uart"; + compatible = "marvell,armada-38x-uart", "ns16550a"; reg = <0x12000 0x100>; reg-shift = <2>; interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>; @@ -175,7 +180,7 @@ }; uart1: serial@12100 { - compatible = "snps,dw-apb-uart"; + compatible = "marvell,armada-38x-uart", "ns16550a"; reg = <0x12100 0x100>; reg-shift = <2>; interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>; @@ -295,6 +300,7 @@ reg-names = "gpio", "pwm"; ngpios = <32>; gpio-controller; + gpio-ranges = <&pinctrl 0 0 32>; #gpio-cells = <2>; #pwm-cells = <2>; interrupt-controller; @@ -313,6 +319,7 @@ reg-names = "gpio", "pwm"; ngpios = <28>; gpio-controller; + gpio-ranges = <&pinctrl 0 32 28>; #gpio-cells = <2>; #pwm-cells = <2>; interrupt-controller; @@ -338,6 +345,44 @@ #clock-cells = <1>; }; + comphy: phy@18300 { + compatible = "marvell,armada-380-comphy"; + reg-names = "comphy", "conf"; + reg = <0x18300 0x100>, <0x18460 4>; + #address-cells = <1>; + #size-cells = <0>; + + comphy0: phy@0 { + reg = <0>; + #phy-cells = <1>; + }; + + comphy1: phy@1 { + reg = <1>; + #phy-cells = <1>; + }; + + comphy2: phy@2 { + reg = <2>; + #phy-cells = <1>; + }; + + comphy3: phy@3 { + reg = <3>; + #phy-cells = <1>; + }; + + comphy4: phy@4 { + reg = <4>; + #phy-cells = <1>; + }; + + comphy5: phy@5 { + reg = <5>; + #phy-cells = <1>; + }; + }; + coreclk: mvebu-sar@18600 { compatible = "marvell,armada-380-core-clock"; reg = <0x18600 0x04>; @@ -379,6 +424,8 @@ reg = <0x20300 0x34>, <0x20704 0x4>, <0x18260 0x4>; clocks = <&coreclk 2>, <&refclk>; clock-names = "nbclk", "fixed"; + interrupts-extended = <&gic GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>, + <&gic GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>; }; cpurst: cpurst@20800 { @@ -555,7 +602,7 @@ }; nand_controller: nand-controller@d0000 { - compatible = "marvell,armada370-nand","marvell,mvebu-pxa3xx-nand"; + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/dts/armada-xp-db-xc3-24g4xg-u-boot.dtsi b/arch/arm/dts/armada-xp-db-xc3-24g4xg-u-boot.dtsi index 90cad85506..fdbe168a87 100644 --- a/arch/arm/dts/armada-xp-db-xc3-24g4xg-u-boot.dtsi +++ b/arch/arm/dts/armada-xp-db-xc3-24g4xg-u-boot.dtsi @@ -5,7 +5,7 @@ }; &nand_controller { - compatible="marvell,mvebu-pxa3xx-nand"; + compatible="marvell,armada370-nand-controller"; status = "okay"; label = "pxa3xx_nand-0"; nand-rb = <0>; diff --git a/board/CZ.NIC/turris_omnia/MAINTAINERS b/board/CZ.NIC/turris_omnia/MAINTAINERS index 8258f4fc5b..8bff97cc27 100644 --- a/board/CZ.NIC/turris_omnia/MAINTAINERS +++ b/board/CZ.NIC/turris_omnia/MAINTAINERS @@ -6,4 +6,5 @@ F: arch/arm/dts/armada-385-turris-omnia*.dts* F: board/CZ.NIC/turris_atsha_otp.* F: board/CZ.NIC/turris_omnia/ F: configs/turris_omnia_defconfig +F: drivers/gpio/turris_omnia_mcu.c F: include/configs/turris_omnia.h diff --git a/cmd/mvebu/Kconfig b/cmd/mvebu/Kconfig index 39963db82c..120397d6d4 100644 --- a/cmd/mvebu/Kconfig +++ b/cmd/mvebu/Kconfig @@ -4,6 +4,7 @@ depends on ARCH_MVEBU config CMD_MVEBU_BUBT bool "bubt" select SHA256 if ARMADA_3700 + select SHA512 if ARMADA_3700 help bubt - Burn a u-boot image to flash For details about bubt command please see the documentation diff --git a/cmd/mvebu/bubt.c b/cmd/mvebu/bubt.c index 1362c03bce..ffa05bc201 100644 --- a/cmd/mvebu/bubt.c +++ b/cmd/mvebu/bubt.c @@ -26,6 +26,7 @@ #endif #include <u-boot/sha1.h> #include <u-boot/sha256.h> +#include <u-boot/sha512.h> #if defined(CONFIG_ARMADA_8K) #define MAIN_HDR_MAGIC 0xB105B002 @@ -56,6 +57,21 @@ struct mvebu_image_header { #define IMAGE_VERSION_3_6_0 0x030600 #define IMAGE_VERSION_3_5_0 0x030500 +struct tim_boot_flash_sign { + unsigned int id; + const char *name; +}; + +struct tim_boot_flash_sign tim_boot_flash_signs[] = { + { 0x454d4d08, "mmc" }, + { 0x454d4d0b, "mmc" }, + { 0x5350490a, "spi" }, + { 0x5350491a, "nand" }, + { 0x55415223, "uart" }, + { 0x53415432, "sata" }, + {}, +}; + struct common_tim_data { u32 version; u32 identifier; @@ -83,7 +99,7 @@ struct mvebu_image_info { u32 encrypt_start_offset; u32 encrypt_size; }; -#endif +#elif defined(CONFIG_ARMADA_32BIT) /* Structure of the main header, version 1 (Armada 370/XP/375/38x/39x) */ struct a38x_main_hdr_v1 { @@ -123,6 +139,8 @@ struct a38x_boot_mode a38x_boot_modes[] = { {}, }; +#endif + struct bubt_dev { char name[8]; size_t (*read)(const char *file_name); @@ -438,11 +456,14 @@ static int is_usb_active(void) #ifdef CONFIG_CMD_NET static size_t tftp_read_file(const char *file_name) { + int ret; + /* * update global variable image_load_addr before tftp file from network */ image_load_addr = get_load_addr(); - return net_loop(TFTPGET); + ret = net_loop(TFTPGET); + return ret > 0 ? ret : 0; } static int is_tftp_active(void) @@ -546,8 +567,10 @@ static int check_image_header(void) int image_num; u8 hash_160_output[SHA1_SUM_LEN]; u8 hash_256_output[SHA256_SUM_LEN]; + u8 hash_512_output[SHA512_SUM_LEN]; sha1_context hash1_text; sha256_context hash256_text; + sha512_context hash512_text; u8 *hash_output; u32 hash_algorithm_id; u32 image_size_to_hash; @@ -617,6 +640,12 @@ static int check_image_header(void) sha256_finish(&hash256_text, hash_256_output); hash_output = hash_256_output; break; + case SHA512_SUM_LEN: + sha512_starts(&hash512_text); + sha512_update(&hash512_text, buff, image_size_to_hash); + sha512_finish(&hash512_text, hash_512_output); + hash_output = hash_512_output; + break; default: printf("Error: Unsupported hash_algorithm_id = %d\n", hash_algorithm_id); @@ -635,7 +664,7 @@ static int check_image_header(void) return 0; } -#elif defined(CONFIG_ARMADA_38X) +#elif defined(CONFIG_ARMADA_32BIT) static size_t a38x_header_size(const struct a38x_main_hdr_v1 *h) { if (h->version == 1) @@ -691,34 +720,39 @@ static int check_image_header(void) static int bubt_check_boot_mode(const struct bubt_dev *dst) { - if (IS_ENABLED(CONFIG_ARMADA_38X)) { - int mode; - const struct a38x_main_hdr_v1 *hdr = - (struct a38x_main_hdr_v1 *)get_load_addr(); - - for (mode = 0; mode < ARRAY_SIZE(a38x_boot_modes); mode++) { - if (strcmp(a38x_boot_modes[mode].name, dst->name) == 0) - break; - } - - if (a38x_boot_modes[mode].id == hdr->blockid) - return 0; +#if defined(CONFIG_ARMADA_3700) || defined(CONFIG_ARMADA_32BIT) + int mode; +#if defined(CONFIG_ARMADA_3700) + const struct tim_boot_flash_sign *boot_modes = tim_boot_flash_signs; + const struct common_tim_data *hdr = + (struct common_tim_data *)get_load_addr(); + u32 id = hdr->boot_flash_sign; +#elif defined(CONFIG_ARMADA_32BIT) + const struct a38x_boot_mode *boot_modes = a38x_boot_modes; + const struct a38x_main_hdr_v1 *hdr = + (struct a38x_main_hdr_v1 *)get_load_addr(); + u32 id = hdr->blockid; +#endif - for (int i = 0; i < ARRAY_SIZE(a38x_boot_modes); i++) { - if (a38x_boot_modes[i].id == hdr->blockid) { - printf("Error: A38x image meant to be booted from " - "\"%s\", not \"%s\"!\n", - a38x_boot_modes[i].name, dst->name); - return -ENOEXEC; - } - } + for (mode = 0; boot_modes[mode].name; mode++) { + if (boot_modes[mode].id == id) + break; + } - printf("Error: unknown boot device in A38x image header: " - "0x%x\n", hdr->blockid); + if (!boot_modes[mode].name) { + printf("Error: unknown boot device in image header: 0x%x\n", id); return -ENOEXEC; - } else { - return 0; } + + if (strcmp(boot_modes[mode].name, dst->name) == 0) + return 0; + + printf("Error: image meant to be booted from \"%s\", not \"%s\"!\n", + boot_modes[mode].name, dst->name); + return -ENOEXEC; +#else + return 0; +#endif } static int bubt_verify(const struct bubt_dev *dst) @@ -836,11 +870,11 @@ int do_bubt_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) dst = find_bubt_dev(dst_dev_name); if (!dst) { printf("Error: Unknown destination \"%s\"\n", dst_dev_name); - return -EINVAL; + return 1; } if (!bubt_is_dev_active(dst)) - return -ENODEV; + return 1; /* Figure out the source device */ src = find_bubt_dev(src_dev_name); @@ -857,15 +891,15 @@ int do_bubt_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) image_size = bubt_read_file(src); if (!image_size) - return -EIO; + return 1; err = bubt_verify(dst); if (err) - return err; + return 1; err = bubt_write_file(dst, image_size); if (err) - return err; + return 1; return 0; } diff --git a/configs/turris_omnia_defconfig b/configs/turris_omnia_defconfig index 66c4959180..ff16825fb4 100644 --- a/configs/turris_omnia_defconfig +++ b/configs/turris_omnia_defconfig @@ -80,7 +80,6 @@ CONFIG_NETCONSOLE=y CONFIG_SPL_OF_TRANSLATE=y CONFIG_AHCI_PCI=y CONFIG_AHCI_MVEBU=y -# CONFIG_MVEBU_GPIO is not set CONFIG_DM_PCA953X=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_MV=y @@ -99,6 +98,8 @@ CONFIG_MVMDIO=y CONFIG_NVME_PCI=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_ARMADA_38X=y CONFIG_DM_RTC=y CONFIG_RTC_ARMADA38X=y CONFIG_SCSI=y diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 73741ceb6a..7c41e3f1b6 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -13,6 +13,7 @@ General designprinciples process release_cycle + system_configuration Implementation -------------- diff --git a/doc/develop/system_configuration.rst b/doc/develop/system_configuration.rst new file mode 100644 index 0000000000..52e4e1df15 --- /dev/null +++ b/doc/develop/system_configuration.rst @@ -0,0 +1,132 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +System configuration +==================== + +There are a number of different aspects to configuring U-Boot to build and then +run on a given platform or set of platforms. Broadly speaking, some aspects of +the world can be configured at run time and others must be done at build time. +In general run time configuration is preferred over build time configuration. +But when making these decisions, we also need to consider if we're talking about +a feature that could be useful to virtually every platform or something specific +to a single hardware platform. The resulting image size is also another +important consideration. Finally, run time configuration has additional overhead +both in terms of resource requirements and wall clock time. All of this means +that care must be taken when writing new code to select the most appropriate +configuration mechanism. + +When adding new features to U-Boot, be they a new subsystem or SoC support or +new platform for an existing supported SoC, the preferred configuration order +is: + +#. Hardware based run time configuration. Examples of this include reading + processor specific registers, or a set of board specific GPIOs or an EEPROM + with a known format to it. These are the cases where we either cannot or + should not be relying on device tree checks. We use this for cases such as + optimized boot time or starting with a generic device tree and then enabling + or disabling features as we boot. + +#. Making use of our Kconfig infrastructure and C preprocessor macros that have + the prefix ``CONFIG``. This is the primary method of build time + configuration. This is generally the best fit for when we want to enable or + disable some sort of feature, such as the SoC or network support. The + ``CONFIG`` prefix for C preprocessor macros is strictly reserved for Kconfig + usage only. + +#. Making use of the :doc:`device tree <devicetree/control>` to determine at + run time how to configure a feature that we have enabled via Kconfig. For + example, we would use Kconfig to enable an I2C chip driver, but use the device + tree to know where the I2C chip resides in memory and other details we need + in order to configure the bus. + +#. Making use of C header files directly and defining C preprocessor macros that + have the ``CFG`` prefix. While the ``CFG`` prefix is reserved for this build + time configuration mechanism, the usage is ad hoc. This is to be used when the + previously mentioned mechanisms are not possible, or for legacy code that has + not been converted. + +Dynamic run time configuration methods. +--------------------------------------- + +Details of hardware specific run time configuration methods are found within the +documentation for a given processor family or board. + +Details of how to use run time configuration based on :doc:`driver model +<driver-model/index>` are covered in that documentation section. + +Static build time configuration methods +--------------------------------------- + +There are two mechanisms used to control the build time configuration of U-Boot. +One is utilizing Kconfig and ``CONFIG`` prefixed macros and the other is ad hoc +usage of ``CFG`` prefixed macros. Both of these are used when it is either not +possible or not practical to make a run time determination about some +functionality of the hardware or a required software feature or similar. Each of +these has their own places where they are better suited than the other for use. + +The `Kconfig language +<https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html>`_ is well +documented and used in a number of projects, including the Linux kernel. We +implement this with the Kconfig files found throughout our sources. This +mechanism is the preferred way of exposing new configuration options as there +are a number of ways for both users and system integrators to manage and change +these options. Some common examples here are to enable a specific command within +U-Boot or even a whole subsystem such as NAND flash or network connectivity. + +The ``CFG`` mechanism is implemented directly as C preprocessor values or +macros, depending on what they are in turn describing. While we have some +functionality that is very reasonable to expose to the end user to enable or +disable we have other places where we need to describe things such as register +locations or values, memory map ranges and so on. When practical, we should be +getting these values from the device tree. However, there are cases where this +is either not practical due to when we need the information and may not have a +device tree yet or due to legacy reasons code has not been rewritten. + +When to use each mechanism +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While there are some cases where it should be fairly obvious where to use each +mechanism, as for example a command would done via Kconfig, a new I2C driver +should use Kconfig and be configured via driver model and a header of values +generated by an external tool should be ``CFG``, there will be cases where it's +less clear and one needs to take care when implementing it. In general, +configuration *options* should be done in Kconfig and configuration *settings* +should done in driver model or ``CFG``. Let us discuss things to keep in mind +when picking the appropriate mechanism. + +A thing to keep in mind is that we have a strong preference for using Kconfig as +the primary build time configuration mechanism. Options expressed this way let +us easily express dependencies and abstractions. In addition, given that many +projects use this mechanism means it has a broad set of tooling and existing +knowledge base. + +Consider the example of a SHA256 hardware acceleration engine. This would be a +feature of the SoC and so something to not ask the user if it exists, but we +would want to have our generic framework for such engines be optionally +available and depend on knowing we have this engine on a given hardware +platform. Expressing this should be done as a hidden Kconfig symbol that is +``select``'ed by the SoC symbol which would in turn be ``select``'ed by the +board option, which is user visible. Hardware features that are either present +or not present should be expressed in Kconfig and in a similar manner, features +which will always have a constant value such as "this SoC always has 4 cores and +4 threads per core" should be as well. + +This brings us to differentiating between a configuration *setting* versus a +hardware feature. To build on the previous example, while we may know the number +of cores and threads, it's possible that within a given family of SoCs the base +addresses of peripherals has changed, but the register offsets within have not. +The preference in this case is to get our information from the device tree and +perform run time configuration. However, this is not always practical and in +those cases we instead rely on the ``CFG`` mechanism. While it would be possible +to use Kconfig in this case, it would result in using calculated rather than +constructed values, resulting in less clear code. Consider the example of a set +of register values for a memory controller. Defining this as a series of logical +ORs and shifts based on other defines is more clear than the Kconfig entry that +set the calculated value alone. + +When it has been determined that the practical solution is to utilize the +``CFG`` mechanism, the next decision is where to place these settings. It is +strongly encouraged to place these in the architecture header files, if they are +generic to a given SoC, or under the board directory if board specific. Placing +them under the board.h file in the *include/configs/* directory should be seen +as a last resort. diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt index 61a72db3c7..c71280b63b 100644 --- a/doc/uImage.FIT/signature.txt +++ b/doc/uImage.FIT/signature.txt @@ -382,6 +382,32 @@ verified later even if the FIT has been signed with other keys in the meantime. +Details +------- +The signature node contains a property ('hashed-nodes') which lists all the +nodes that the signature was made over. The image is walked in order and each +tag processed as follows: +- DTB_BEGIN_NODE: The tag and the following name are included in the signature + if the node or its parent are present in 'hashed-nodes' +- DTB_END_NODE: The tag is included in the signature if the node or its parent + are present in 'hashed-nodes' +- DTB_PROPERTY: The tag, the length word, the offset in the string table, and + the data are all included if the current node is present in 'hashed-nodes' + and the property name is not 'data'. +- DTB_END: The tag is always included in the signature. +- DTB_NOP: The tag is included in the signature if the current node is present + in 'hashed-nodes' + +In addition, the signature contains a property 'hashed-strings' which contains +the offset and length in the string table of the strings that are to be +included in the signature (this is done last). + +IMPORTANT: To verify the signature outside u-boot, it is vital to not only +calculate the hash of the image and verify the signature with that, but also to +calculate the hashes of the kernel, fdt, and ramdisk images and check those +match the hash values in the corresponding 'hash*' subnodes. + + Verification ------------ FITs are verified when loaded. After the configuration is selected a list diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst index 83543f63f6..561076bac9 100644 --- a/doc/usage/environment.rst +++ b/doc/usage/environment.rst @@ -404,6 +404,56 @@ device tree blob fdtfile fdt_addr_r fdt_addr ramdisk ramdiskfile ramdisk_addr_r ramdisk_addr ================= ============== ================ ============== +When setting the RAM addresses for `kernel_addr_r`, `fdt_addr_r` and +`ramdisk_addr_r` there are several types of constraints to keep in mind. The +one type of constraint is payload requirement. For example, a device tree MUST +be loaded at an 8-byte aligned address as that is what the specification +requires. In a similar manner, the operating system may define restrictions on +where in memory space payloads can be. This is documented for example in Linux, +with both the `Booting ARM Linux`_ and `Booting AArch64 Linux`_ documents. +Finally, there are practical constraints. We do not know the size of a given +payload a user will use but each payload must not overlap or it will corrupt +the other payload. A similar problem can happen when a payload ends up being in +the OS BSS area. For these reasons we need to ensure our default values here +are both unlikely to lead to failure to boot and sufficiently explained so that +they can be optimized for boot time or adjusted for smaller memory +configurations. + +On different architectures we will have different constraints. It is important +that we follow whatever documented requirements are available to best ensure +forward compatibility. What follows are examples to highlight how to provide +reasonable default values in different cases. + +Texas Instruments OMAP2PLUS (ARMv7) example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On these families of processors we are on a 32bit ARMv7 core. As booting some +form of Linux is our most common payload we will also keep in mind the +documented requirements for booting that Linux provides. These values are also +known to be fine for booting a number of other operating systems (or their +loaders). In this example we define the following variables and values:: + + loadaddr=0x82000000 + kernel_addr_r=${loadaddr} + fdt_addr_r=0x88000000 + ramdisk_addr_r=0x88080000 + bootm_size=0x10000000 + +The first thing to keep in mind is that DRAM starts at 0x80000000. We set a +32MiB buffer from the start of memory as our default load address and set +``kernel_addr_r`` to that. This is because the Linux ``zImage`` decompressor +will typically then be able to avoid doing a relocation itself. It also MUST be +within the first 128MiB of memory. The next value is we set ``fdt_addr_r`` to +be at 128MiB offset from the start of memory. This location is suggested by the +kernel documentation and is exceedingly unlikely to be overwritten by the +kernel itself given other architectural constraints. We then allow for the +device tree to be up to 512KiB in size before placing the ramdisk in memory. We +then say that everything should be within the first 256MiB of memory so that +U-Boot can relocate things as needed to ensure proper alignment. We pick 256MiB +as our value here because we know there are very few platforms on in this +family with less memory. It could be as high as 768MiB and still ensure that +everything would be visible to the kernel, but again we go with what we assume +is the safest assumption. Automatically updated variables ------------------------------- @@ -472,3 +522,6 @@ Implementation -------------- See :doc:`../develop/environment` for internal development details. + +.. _`Booting ARM Linux`: https://www.kernel.org/doc/html/latest/arm/booting.html +.. _`Booting AArch64 Linux`: https://www.kernel.org/doc/html/latest/arm64/booting.html diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index aaa152fae7..82a8bca270 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO 8-bit gpo expander, all gpo lines are controlled by writing value into data register. +config TURRIS_OMNIA_MCU + bool "Turris Omnia MCU GPIO driver" + depends on DM_GPIO + default y if TARGET_TURRIS_OMNIA + help + Support for GPIOs on MCU connected to Turris Omnia via i2c. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d7552762d0..219f37e0e4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o +obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU) += turris_omnia_mcu.o diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 76f47027a3..138801850d 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -133,7 +133,7 @@ static int rcar_gpio_get_function(struct udevice *dev, unsigned offset) static int rcar_gpio_request(struct udevice *dev, unsigned offset, const char *label) { - return pinctrl_gpio_request(dev, offset); + return pinctrl_gpio_request(dev, offset, label); } static int rcar_gpio_free(struct udevice *dev, unsigned offset) diff --git a/drivers/gpio/mvebu_gpio.c b/drivers/gpio/mvebu_gpio.c index 4c1c68ee19..f706a6dfa4 100644 --- a/drivers/gpio/mvebu_gpio.c +++ b/drivers/gpio/mvebu_gpio.c @@ -5,6 +5,7 @@ #include <common.h> #include <dm.h> +#include <dm/pinctrl.h> #include <asm/gpio.h> #include <asm/io.h> #include <errno.h> @@ -22,7 +23,7 @@ struct mvebu_gpio_regs { struct mvebu_gpio_priv { struct mvebu_gpio_regs *regs; - char name[2]; + char name[sizeof("mvebuX_")]; }; static int mvebu_gpio_direction_input(struct udevice *dev, unsigned int gpio) @@ -91,14 +92,18 @@ static int mvebu_gpio_probe(struct udevice *dev) struct mvebu_gpio_priv *priv = dev_get_priv(dev); priv->regs = dev_read_addr_ptr(dev); - uc_priv->gpio_count = MVEBU_GPIOS_PER_BANK; - priv->name[0] = 'A' + dev_seq(dev); + uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", MVEBU_GPIOS_PER_BANK); + sprintf(priv->name, "mvebu%d_", dev_seq(dev)); uc_priv->bank_name = priv->name; return 0; } static const struct dm_gpio_ops mvebu_gpio_ops = { +#if CONFIG_IS_ENABLED(PINCTRL_ARMADA_38X) + .request = pinctrl_gpio_request, + .rfree = pinctrl_gpio_free, +#endif .direction_input = mvebu_gpio_direction_input, .direction_output = mvebu_gpio_direction_output, .get_function = mvebu_gpio_get_function, diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c new file mode 100644 index 0000000000..3e5d74e62c --- /dev/null +++ b/drivers/gpio/turris_omnia_mcu.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0+ +// (C) 2022 Pali Rohár <pali@kernel.org> + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <asm/gpio.h> +#include <linux/log2.h> + +enum commands_e { + CMD_GET_STATUS_WORD = 0x01, + CMD_GENERAL_CONTROL = 0x02, + + /* available if STS_FEATURES_SUPPORTED bit set in status word */ + CMD_GET_FEATURES = 0x10, + + /* available if FEAT_EXT_CMDS bit is set in features */ + CMD_GET_EXT_STATUS_DWORD = 0x11, + CMD_EXT_CONTROL = 0x12, + CMD_GET_EXT_CONTROL_STATUS = 0x13, +}; + +/* CMD_GET_STATUS_WORD */ +enum sts_word_e { + STS_MCU_TYPE_MASK = GENMASK(1, 0), + STS_MCU_TYPE_STM32 = 0, + STS_MCU_TYPE_GD32 = 1, + STS_MCU_TYPE_MKL = 2, + STS_FEATURES_SUPPORTED = BIT(2), + STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3), + STS_CARD_DET = BIT(4), + STS_MSATA_IND = BIT(5), + STS_USB30_OVC = BIT(6), + STS_USB31_OVC = BIT(7), + STS_USB30_PWRON = BIT(8), + STS_USB31_PWRON = BIT(9), + STS_ENABLE_4V5 = BIT(10), + STS_BUTTON_MODE = BIT(11), + STS_BUTTON_PRESSED = BIT(12), + STS_BUTTON_COUNTER_MASK = GENMASK(15, 13) +}; + +/* CMD_GENERAL_CONTROL */ +enum ctl_byte_e { + CTL_LIGHT_RST = BIT(0), + CTL_HARD_RST = BIT(1), + /*CTL_RESERVED = BIT(2),*/ + CTL_USB30_PWRON = BIT(3), + CTL_USB31_PWRON = BIT(4), + CTL_ENABLE_4V5 = BIT(5), + CTL_BUTTON_MODE = BIT(6), + CTL_BOOTLOADER = BIT(7) +}; + +/* CMD_GET_FEATURES */ +enum features_e { + FEAT_EXT_CMDS = BIT(1), +}; + +struct turris_omnia_mcu_info { + u16 features; +}; + +static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset) +{ + struct turris_omnia_mcu_info *info = dev_get_plat(dev); + + switch (offset) { + /* bank 0 */ + case 0 ... 15: + switch (offset) { + case ilog2(STS_USB30_PWRON): + case ilog2(STS_USB31_PWRON): + case ilog2(STS_ENABLE_4V5): + case ilog2(STS_BUTTON_MODE): + return GPIOF_OUTPUT; + default: + return GPIOF_INPUT; + } + + /* bank 1 - supported only when FEAT_EXT_CMDS is set */ + case (16 + 0) ... (16 + 31): + if (!(info->features & FEAT_EXT_CMDS)) + return -EINVAL; + return GPIOF_INPUT; + + /* bank 2 - supported only when FEAT_EXT_CMDS is set */ + case (16 + 32 + 0) ... (16 + 32 + 15): + if (!(info->features & FEAT_EXT_CMDS)) + return -EINVAL; + return GPIOF_OUTPUT; + + default: + return -EINVAL; + } +} + +static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset) +{ + struct turris_omnia_mcu_info *info = dev_get_plat(dev); + u8 val16[2]; + u8 val32[4]; + int ret; + + switch (offset) { + /* bank 0 */ + case 0 ... 15: + ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2); + if (ret) + return ret; + return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1; + + /* bank 1 - supported only when FEAT_EXT_CMDS is set */ + case (16 + 0) ... (16 + 31): + if (!(info->features & FEAT_EXT_CMDS)) + return -EINVAL; + ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4); + if (ret) + return ret; + return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) | + ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1; + + /* bank 2 - supported only when FEAT_EXT_CMDS is set */ + case (16 + 32 + 0) ... (16 + 32 + 15): + if (!(info->features & FEAT_EXT_CMDS)) + return -EINVAL; + ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2); + if (ret) + return ret; + return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1; + + default: + return -EINVAL; + } +} + +static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value) +{ + struct turris_omnia_mcu_info *info = dev_get_plat(dev); + u8 val[2]; + int ret; + u8 reg; + + switch (offset) { + /* bank 0 */ + case ilog2(STS_USB30_PWRON): + reg = CMD_GENERAL_CONTROL; + val[1] = CTL_USB30_PWRON; + break; + case ilog2(STS_USB31_PWRON): + reg = CMD_GENERAL_CONTROL; + val[1] = CTL_USB31_PWRON; + break; + case ilog2(STS_ENABLE_4V5): + reg = CMD_GENERAL_CONTROL; + val[1] = CTL_ENABLE_4V5; + break; + case ilog2(STS_BUTTON_MODE): + reg = CMD_GENERAL_CONTROL; + val[1] = CTL_BUTTON_MODE; + break; + + /* bank 2 - supported only when FEAT_EXT_CMDS is set */ + case (16 + 32 + 0) ... (16 + 32 + 15): + if (!(info->features & FEAT_EXT_CMDS)) + return -EINVAL; + reg = CMD_EXT_CONTROL; + val[1] = BIT(offset - 16 - 32); + break; + + default: + return -EINVAL; + } + + val[0] = value ? val[1] : 0; + + ret = dm_i2c_write(dev, reg, val, 2); + if (ret) + return ret; + + return 0; +} + +static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset) +{ + int ret; + + ret = turris_omnia_mcu_get_function(dev, offset); + if (ret < 0) + return ret; + else if (ret != GPIOF_INPUT) + return -EOPNOTSUPP; + + return 0; +} + +static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value) +{ + int ret; + + ret = turris_omnia_mcu_get_function(dev, offset); + if (ret < 0) + return ret; + else if (ret != GPIOF_OUTPUT) + return -EOPNOTSUPP; + + return turris_omnia_mcu_set_value(dev, offset, value); +} + +static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc, + struct ofnode_phandle_args *args) +{ + uint bank, gpio, flags, offset; + int ret; + + if (args->args_count != 3) + return -EINVAL; + + bank = args->args[0]; + gpio = args->args[1]; + flags = args->args[2]; + + switch (bank) { + case 0: + if (gpio >= 16) + return -EINVAL; + offset = gpio; + break; + case 1: + if (gpio >= 32) + return -EINVAL; + offset = 16 + gpio; + break; + case 2: + if (gpio >= 16) + return -EINVAL; + offset = 16 + 32 + gpio; + break; + default: + return -EINVAL; + } + + ret = turris_omnia_mcu_get_function(dev, offset); + if (ret < 0) + return ret; + + desc->offset = offset; + desc->flags = gpio_flags_xlate(flags); + + return 0; +} + +static const struct dm_gpio_ops turris_omnia_mcu_ops = { + .direction_input = turris_omnia_mcu_direction_input, + .direction_output = turris_omnia_mcu_direction_output, + .get_value = turris_omnia_mcu_get_value, + .set_value = turris_omnia_mcu_set_value, + .get_function = turris_omnia_mcu_get_function, + .xlate = turris_omnia_mcu_xlate, +}; + +static int turris_omnia_mcu_probe(struct udevice *dev) +{ + struct turris_omnia_mcu_info *info = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + u16 status; + u8 val[2]; + int ret; + + ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2); + if (ret) { + printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret); + return ret; + } + + status = ((u16)val[1] << 8) | val[0]; + + if (status & STS_FEATURES_SUPPORTED) { + ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2); + if (ret) { + printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret); + return ret; + } + info->features = ((u16)val[1] << 8) | val[0]; + } + + uc_priv->bank_name = "mcu_"; + + if (info->features & FEAT_EXT_CMDS) + uc_priv->gpio_count = 16 + 32 + 16; + else + uc_priv->gpio_count = 16; + + return 0; +} + +static const struct udevice_id turris_omnia_mcu_ids[] = { + { .compatible = "cznic,turris-omnia-mcu" }, + { } +}; + +U_BOOT_DRIVER(turris_omnia_mcu) = { + .name = "turris-omnia-mcu", + .id = UCLASS_GPIO, + .ops = &turris_omnia_mcu_ops, + .probe = turris_omnia_mcu_probe, + .plat_auto = sizeof(struct turris_omnia_mcu_info), + .of_match = turris_omnia_mcu_ids, +}; diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c index 20d1aee7b3..9c29e8a6c2 100644 --- a/drivers/mtd/nand/raw/pxa3xx_nand.c +++ b/drivers/mtd/nand/raw/pxa3xx_nand.c @@ -429,7 +429,7 @@ static struct nand_ecclayout ecc_layout_8KB_bch8bit = { static const struct udevice_id pxa3xx_nand_dt_ids[] = { { - .compatible = "marvell,mvebu-pxa3xx-nand", + .compatible = "marvell,armada370-nand-controller", .data = PXA3XX_NAND_VARIANT_ARMADA370, }, { diff --git a/drivers/pinctrl/mvebu/Kconfig b/drivers/pinctrl/mvebu/Kconfig index 07d4f3e216..574fb4dfb0 100644 --- a/drivers/pinctrl/mvebu/Kconfig +++ b/drivers/pinctrl/mvebu/Kconfig @@ -1,5 +1,12 @@ if ARCH_MVEBU +config PINCTRL_ARMADA_38X + depends on ARMADA_38X && PINCTRL_FULL + bool "Armada 38x pin control driver" + help + Support pin multiplexing and pin configuration control on + Marvell's Armada-38x SoC. + config PINCTRL_ARMADA_37XX depends on ARMADA_3700 && PINCTRL_FULL bool "Armada 37xx pin control driver" diff --git a/drivers/pinctrl/mvebu/Makefile b/drivers/pinctrl/mvebu/Makefile index c2df96bf5b..15303d83a7 100644 --- a/drivers/pinctrl/mvebu/Makefile +++ b/drivers/pinctrl/mvebu/Makefile @@ -4,5 +4,6 @@ # # https://spdx.org/licenses +obj-$(CONFIG_PINCTRL_ARMADA_38X) += pinctrl-armada-38x.o obj-$(CONFIG_PINCTRL_ARMADA_37XX) += pinctrl-armada-37xx.o obj-$(CONFIG_PINCTRL_ARMADA_8K) += pinctrl-mvebu.o diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index e76ef153e6..bb7a76baed 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -65,7 +65,6 @@ DECLARE_GLOBAL_DATA_PTR; * belonging to the group * @npins: Number of pins included in the second optional range * @funcs: A list of pinmux functions that can be selected for this group. - * @pins: List of the pins included in the group */ struct armada_37xx_pin_group { const char *name; @@ -76,7 +75,6 @@ struct armada_37xx_pin_group { unsigned int extra_pin; unsigned int extra_npins; const char *funcs[NB_FUNCS]; - unsigned int *pins; }; struct armada_37xx_pin_data { @@ -97,8 +95,6 @@ struct armada_37xx_pinctrl { const struct armada_37xx_pin_data *data; struct udevice *dev; struct pinctrl_dev *pctl_dev; - struct armada_37xx_pin_group *groups; - unsigned int ngroups; struct armada_37xx_pmx_func *funcs; unsigned int nfuncs; }; @@ -113,6 +109,16 @@ struct armada_37xx_pinctrl { .funcs = {_func1, _func2} \ } +#define PIN_GRP_GPIO_0(_name, _start, _nr) \ + { \ + .name = _name, \ + .start_pin = _start, \ + .npins = _nr, \ + .reg_mask = 0, \ + .val = {0}, \ + .funcs = {"gpio"} \ + } + #define PIN_GRP_GPIO(_name, _start, _nr, _mask, _func1) \ { \ .name = _name, \ @@ -170,6 +176,7 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { "pwm", "led"), PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"), PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"), + PIN_GRP_GPIO_0("gpio1_5", 5, 1), PIN_GRP_GPIO("i2c2", 2, 2, BIT(9), "i2c"), PIN_GRP_GPIO("i2c1", 0, 2, BIT(10), "i2c"), PIN_GRP_GPIO("spi_cs1", 17, 1, BIT(12), "spi"), @@ -186,10 +193,13 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { PIN_GRP_GPIO("usb32_drvvbus0", 0, 1, BIT(0), "drvbus"), PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"), + PIN_GRP_GPIO_0("gpio2_2", 2, 1), PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"), PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"), PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"), - PIN_GRP_GPIO("pcie1", 3, 3, BIT(5) | BIT(9) | BIT(10), "pcie"), + PIN_GRP_GPIO("pcie1", 3, 1, BIT(5), "pcie"), /* this actually controls "pcie1_reset" */ + PIN_GRP_GPIO("pcie1_clkreq", 4, 1, BIT(9), "pcie"), + PIN_GRP_GPIO("pcie1_wakeup", 5, 1, BIT(10), "pcie"), PIN_GRP_GPIO("ptp", 20, 3, BIT(11) | BIT(12) | BIT(13), "ptp"), PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"), PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"), @@ -197,14 +207,14 @@ static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { "mii", "mii_err"), }; -const struct armada_37xx_pin_data armada_37xx_pin_nb = { +static const struct armada_37xx_pin_data armada_37xx_pin_nb = { .nr_pins = 36, .name = "GPIO1", .groups = armada_37xx_nb_groups, .ngroups = ARRAY_SIZE(armada_37xx_nb_groups), }; -const struct armada_37xx_pin_data armada_37xx_pin_sb = { +static const struct armada_37xx_pin_data armada_37xx_pin_sb = { .nr_pins = 30, .name = "GPIO2", .groups = armada_37xx_sb_groups, @@ -237,7 +247,7 @@ static int armada_37xx_pmx_get_groups_count(struct udevice *dev) { struct armada_37xx_pinctrl *info = dev_get_priv(dev); - return info->ngroups; + return info->data->ngroups; } static const char *armada_37xx_pmx_dummy_name = "_dummy"; @@ -247,10 +257,10 @@ static const char *armada_37xx_pmx_get_group_name(struct udevice *dev, { struct armada_37xx_pinctrl *info = dev_get_priv(dev); - if (!info->groups[selector].name) + if (!info->data->groups[selector].name) return armada_37xx_pmx_dummy_name; - return info->groups[selector].name; + return info->data->groups[selector].name; } static int armada_37xx_pmx_get_funcs_count(struct udevice *dev) @@ -270,12 +280,13 @@ static const char *armada_37xx_pmx_get_func_name(struct udevice *dev, static int armada_37xx_pmx_set_by_name(struct udevice *dev, const char *name, - struct armada_37xx_pin_group *grp) + struct armada_37xx_pin_group *grp, + bool warn_on_change) { struct armada_37xx_pinctrl *info = dev_get_priv(dev); unsigned int reg = SELECTION; unsigned int mask = grp->reg_mask; - int func, val; + int func, val, old_func; dev_dbg(info->dev, "enable function %s group %s\n", name, grp->name); @@ -287,6 +298,18 @@ static int armada_37xx_pmx_set_by_name(struct udevice *dev, val = grp->val[func]; + if (warn_on_change && val != (readl(info->base + reg) & mask)) { + for (old_func = 0; (old_func < NB_FUNCS) && grp->funcs[old_func]; old_func++) { + if (grp->val[old_func] == val) + break; + } + dev_warn(info->dev, "Warning: Changing MPPs %u-%u function from %s to %s...\n", + grp->start_pin, grp->start_pin + grp->npins - 1, + ((old_func < NB_FUNCS && grp->funcs[old_func]) ? + grp->funcs[old_func] : "unknown"), + name); + } + clrsetbits_le32(info->base + reg, mask, val); return 0; @@ -297,10 +320,112 @@ static int armada_37xx_pmx_group_set(struct udevice *dev, unsigned func_selector) { struct armada_37xx_pinctrl *info = dev_get_priv(dev); - struct armada_37xx_pin_group *grp = &info->groups[group_selector]; + struct armada_37xx_pin_group *grp = &info->data->groups[group_selector]; const char *name = info->funcs[func_selector].name; - return armada_37xx_pmx_set_by_name(dev, name, grp); + return armada_37xx_pmx_set_by_name(dev, name, grp, false); +} + +static int armada_37xx_pmx_gpio_request_enable(struct udevice *dev, unsigned int selector) +{ + struct armada_37xx_pinctrl *info = dev_get_priv(dev); + int ret = -ENOTSUPP; + int n; + + /* Find all groups where is requested selector pin and set each group to gpio function */ + for (n = 0; n < info->data->ngroups; n++) { + struct armada_37xx_pin_group *grp = &info->data->groups[n]; + + if ((selector >= grp->start_pin && selector < grp->start_pin + grp->npins) || + (selector >= grp->extra_pin && selector < grp->extra_pin + grp->extra_npins)) { + ret = armada_37xx_pmx_set_by_name(dev, "gpio", grp, true); + if (ret) + return ret; + } + } + + return ret; +} + +static int armada_37xx_pmx_gpio_disable_free(struct udevice *dev, unsigned int selector) +{ + /* nothing to do */ + return 0; +} + +static int armada_37xx_pmx_get_pins_count(struct udevice *dev) +{ + struct armada_37xx_pinctrl *info = dev_get_priv(dev); + + return info->data->nr_pins; +} + +static const char *armada_37xx_pmx_get_pin_name(struct udevice *dev, unsigned int selector) +{ + struct armada_37xx_pinctrl *info = dev_get_priv(dev); + static char buf[sizeof("MPPx_XX")]; + + sprintf(buf, "MPP%c_%u", info->data->name[4], selector); + return buf; +} + +static int armada_37xx_pmx_get_pin_muxing(struct udevice *dev, unsigned int selector, + char *buf, int size) +{ + struct armada_37xx_pinctrl *info = dev_get_priv(dev); + int n; + + /* + * First check if selected pin is in some extra pin group. + * Function in extra pin group is active only when it is not gpio. + */ + for (n = 0; n < info->data->ngroups; n++) { + struct armada_37xx_pin_group *grp = &info->data->groups[n]; + + if (selector >= grp->extra_pin && selector < grp->extra_pin + grp->extra_npins) { + unsigned int reg = SELECTION; + unsigned int mask = grp->reg_mask; + int f, val; + + val = (readl(info->base + reg) & mask); + + for (f = 0; f < NB_FUNCS && grp->funcs[f]; f++) { + if (grp->val[f] == val) { + if (strcmp(grp->funcs[f], "gpio") != 0) { + strlcpy(buf, grp->funcs[f], size); + return 0; + } + break; + } + } + } + } + + /* If pin is not active in some extra pin group then check regular groups. */ + for (n = 0; n < info->data->ngroups; n++) { + struct armada_37xx_pin_group *grp = &info->data->groups[n]; + + if (selector >= grp->start_pin && selector < grp->start_pin + grp->npins) { + unsigned int reg = SELECTION; + unsigned int mask = grp->reg_mask; + int f, val; + + val = (readl(info->base + reg) & mask); + + for (f = 0; f < NB_FUNCS && grp->funcs[f]; f++) { + if (grp->val[f] == val) { + strlcpy(buf, grp->funcs[f], size); + return 0; + } + } + + strlcpy(buf, "unknown", size); + return 0; + } + } + + strlcpy(buf, "unknown", size); + return 0; } /** @@ -352,21 +477,9 @@ static int armada_37xx_fill_group(struct armada_37xx_pinctrl *info) { int n, num = 0, funcsize = info->data->nr_pins; - for (n = 0; n < info->ngroups; n++) { - struct armada_37xx_pin_group *grp = &info->groups[n]; - int i, j, f; - - grp->pins = devm_kzalloc(info->dev, - (grp->npins + grp->extra_npins) * - sizeof(*grp->pins), GFP_KERNEL); - if (!grp->pins) - return -ENOMEM; - - for (i = 0; i < grp->npins; i++) - grp->pins[i] = grp->start_pin + i; - - for (j = 0; j < grp->extra_npins; j++) - grp->pins[i+j] = grp->extra_pin + j; + for (n = 0; n < info->data->ngroups; n++) { + struct armada_37xx_pin_group *grp = &info->data->groups[n]; + int f; for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) { int ret; @@ -416,8 +529,8 @@ static int armada_37xx_fill_func(struct armada_37xx_pinctrl *info) groups = funcs[n].groups; - for (g = 0; g < info->ngroups; g++) { - struct armada_37xx_pin_group *gp = &info->groups[g]; + for (g = 0; g < info->data->ngroups; g++) { + struct armada_37xx_pin_group *gp = &info->data->groups[g]; int f; for (f = 0; (f < NB_FUNCS) && gp->funcs[f]; f++) { @@ -522,6 +635,8 @@ static int armada_37xx_gpio_probe(struct udevice *dev) } static const struct dm_gpio_ops armada_37xx_gpio_ops = { + .request = pinctrl_gpio_request, + .rfree = pinctrl_gpio_free, .set_value = armada_37xx_gpio_set, .get_value = armada_37xx_gpio_get, .get_function = armada_37xx_gpio_get_direction, @@ -574,16 +689,21 @@ static int armada_37xx_gpiochip_register(struct udevice *parent, return 0; } -const struct pinctrl_ops armada_37xx_pinctrl_ops = { +static const struct pinctrl_ops armada_37xx_pinctrl_ops = { + .get_pins_count = armada_37xx_pmx_get_pins_count, + .get_pin_name = armada_37xx_pmx_get_pin_name, + .get_pin_muxing = armada_37xx_pmx_get_pin_muxing, .get_groups_count = armada_37xx_pmx_get_groups_count, .get_group_name = armada_37xx_pmx_get_group_name, .get_functions_count = armada_37xx_pmx_get_funcs_count, .get_function_name = armada_37xx_pmx_get_func_name, .pinmux_group_set = armada_37xx_pmx_group_set, + .gpio_request_enable = armada_37xx_pmx_gpio_request_enable, + .gpio_disable_free = armada_37xx_pmx_gpio_disable_free, .set_state = pinctrl_generic_set_state, }; -int armada_37xx_pinctrl_probe(struct udevice *dev) +static int armada_37xx_pinctrl_probe(struct udevice *dev) { struct armada_37xx_pinctrl *info = dev_get_priv(dev); const struct armada_37xx_pin_data *pin_data; @@ -598,9 +718,6 @@ int armada_37xx_pinctrl_probe(struct udevice *dev) return -ENODEV; } - info->groups = pin_data->groups; - info->ngroups = pin_data->ngroups; - /* * we allocate functions for number of pins and hope there are * fewer unique functions than pins available diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-38x.c b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c new file mode 100644 index 0000000000..252151f3e5 --- /dev/null +++ b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// (C) 2022 Pali Rohár <pali@kernel.org> + +#include <common.h> +#include <config.h> +#include <dm.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dm/pinctrl.h> +#include <dm/root.h> +#include <errno.h> +#include <asm/io.h> + +struct mvebu_mpp_ctrl_setting { + const char *name; + const char *subname; + u8 val; + u8 variant; +}; + +struct mvebu_mpp_mode { + const char *name; + size_t nsettings; + struct mvebu_mpp_ctrl_setting *settings; +}; + +#define MPP_MODE(_name, ...) \ + { \ + .name = _name, \ + .nsettings = ARRAY_SIZE(( \ + (struct mvebu_mpp_ctrl_setting[]) \ + { __VA_ARGS__ })), \ + .settings = (struct mvebu_mpp_ctrl_setting[]){ \ + __VA_ARGS__ }, \ + } + +#define MPP_VAR_FUNCTION(_val, _name, _subname, _mask) \ + { \ + .val = _val, \ + .name = _name, \ + .subname = _subname, \ + .variant = _mask, \ + } + +#define MVEBU_MPPS_PER_REG 8 +#define MVEBU_MPP_BITS 4 +#define MVEBU_MPP_MASK 0xf + +enum { + V_88F6810 = BIT(0), + V_88F6820 = BIT(1), + V_88F6828 = BIT(2), + V_88F6810_PLUS = (V_88F6810 | V_88F6820 | V_88F6828), + V_88F6820_PLUS = (V_88F6820 | V_88F6828), +}; + +static struct mvebu_mpp_mode armada_38x_mpp_modes[] = { + MPP_MODE("mpp0", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua0", "rxd", V_88F6810_PLUS)), + MPP_MODE("mpp1", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua0", "txd", V_88F6810_PLUS)), + MPP_MODE("mpp2", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c0", "sck", V_88F6810_PLUS)), + MPP_MODE("mpp3", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c0", "sda", V_88F6810_PLUS)), + MPP_MODE("mpp4", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge", "mdc", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6810_PLUS)), + MPP_MODE("mpp5", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge", "mdio", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6810_PLUS)), + MPP_MODE("mpp6", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txclkout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge0", "crs", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs3", V_88F6810_PLUS)), + MPP_MODE("mpp7", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad9", V_88F6810_PLUS)), + MPP_MODE("mpp8", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad10", V_88F6810_PLUS)), + MPP_MODE("mpp9", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad11", V_88F6810_PLUS)), + MPP_MODE("mpp10", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad12", V_88F6810_PLUS)), + MPP_MODE("mpp11", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad13", V_88F6810_PLUS)), + MPP_MODE("mpp12", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad14", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "pcie3", "clkreq", V_88F6810_PLUS)), + MPP_MODE("mpp13", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "clkreq", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad15", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "pcie2", "clkreq", V_88F6810_PLUS)), + MPP_MODE("mpp14", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "dram", "vttctrl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "we1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "pcie3", "clkreq", V_88F6810_PLUS)), + MPP_MODE("mpp15", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdc slave", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "mosi", V_88F6810_PLUS)), + MPP_MODE("mpp16", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdio slave", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "dram", "deccerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "miso", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "pcie0", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "pcie1", "clkreq", V_88F6820_PLUS)), + MPP_MODE("mpp17", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "sata0", "prsnt", V_88F6810_PLUS)), + MPP_MODE("mpp18", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "trig", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs0", V_88F6810_PLUS)), + MPP_MODE("mpp19", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "col", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "evreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ge0", "txerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6810_PLUS)), + MPP_MODE("mpp20", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6810_PLUS)), + MPP_MODE("mpp21", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "cmd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "bootcs", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "sata1", "prsnt", V_88F6810_PLUS)), + MPP_MODE("mpp22", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "mosi", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad0", V_88F6810_PLUS)), + MPP_MODE("mpp23", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad2", V_88F6810_PLUS)), + MPP_MODE("mpp24", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "miso", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d4", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ready", V_88F6810_PLUS)), + MPP_MODE("mpp25", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d5", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs0", V_88F6810_PLUS)), + MPP_MODE("mpp26", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "i2c1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d6", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs1", V_88F6810_PLUS)), + MPP_MODE("mpp27", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txclkout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "i2c1", "sda", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d7", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs2", V_88F6810_PLUS)), + MPP_MODE("mpp28", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad5", V_88F6810_PLUS)), + MPP_MODE("mpp29", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ale0", V_88F6810_PLUS)), + MPP_MODE("mpp30", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "oe", V_88F6810_PLUS)), + MPP_MODE("mpp31", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ale1", V_88F6810_PLUS)), + MPP_MODE("mpp32", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "we0", V_88F6810_PLUS)), + MPP_MODE("mpp33", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "dram", "deccerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad3", V_88F6810_PLUS)), + MPP_MODE("mpp34", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad1", V_88F6810_PLUS)), + MPP_MODE("mpp35", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk_out1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a1", V_88F6810_PLUS)), + MPP_MODE("mpp36", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ptp", "trig", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a0", V_88F6810_PLUS)), + MPP_MODE("mpp37", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad8", V_88F6810_PLUS)), + MPP_MODE("mpp38", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ptp", "evreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ref", "clk_out0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad4", V_88F6810_PLUS)), + MPP_MODE("mpp39", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a2", V_88F6810_PLUS)), + MPP_MODE("mpp40", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c1", "sda", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad6", V_88F6810_PLUS)), + MPP_MODE("mpp41", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "burst/last", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "nand", "rb0", V_88F6810_PLUS)), + MPP_MODE("mpp42", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad7", V_88F6810_PLUS)), + MPP_MODE("mpp43", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "dram", "vttctrl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "dram", "deccerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "clkout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "nand", "rb1", V_88F6810_PLUS)), + MPP_MODE("mpp44", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "sata2", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(4, "sata3", "prsnt", V_88F6828)), + MPP_MODE("mpp45", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk_out0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6810_PLUS)), + MPP_MODE("mpp46", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk_out1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6810_PLUS)), + MPP_MODE("mpp47", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "sata2", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(5, "sata3", "prsnt", V_88F6828)), + MPP_MODE("mpp48", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "dram", "vttctrl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "pclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "mclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d4", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "pcie0", "clkreq", V_88F6810_PLUS)), + MPP_MODE("mpp49", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata2", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(2, "sata3", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(3, "tdm", "fsync", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "lrclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d5", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "pcie1", "clkreq", V_88F6820_PLUS)), + MPP_MODE("mpp50", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "drx", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "extclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "cmd", V_88F6810_PLUS)), + MPP_MODE("mpp51", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "dtx", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "sdo", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dram", "deccerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ptp", "trig", V_88F6810_PLUS)), + MPP_MODE("mpp52", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "int", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "sdi", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d6", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ptp", "clk", V_88F6810_PLUS)), + MPP_MODE("mpp53", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "rst", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "bclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d7", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ptp", "evreq", V_88F6810_PLUS)), + MPP_MODE("mpp54", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "ge0", "txerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d3", V_88F6810_PLUS)), + MPP_MODE("mpp55", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdio", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "clkreq", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6810_PLUS)), + MPP_MODE("mpp56", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdc", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "dram", "deccerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "mosi", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6810_PLUS)), + MPP_MODE("mpp57", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6810_PLUS)), + MPP_MODE("mpp58", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie1", "clkreq", V_88F6820_PLUS), + MPP_VAR_FUNCTION(2, "i2c1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie2", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "miso", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6810_PLUS)), + MPP_MODE("mpp59", + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "i2c1", "sda", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d2", V_88F6810_PLUS)), +}; + +static const char * const armada_38x_mpp_function_names[] = { + "gpio", /* make gpio always as function 0 */ + + "audio", + "dev", + "dram", + "ge", + "ge0", + "ge1", + "i2c0", + "i2c1", + "nand", + "pcie0", + "pcie1", + "pcie2", + "pcie3", + "ptp", + "ref", + "sata0", + "sata1", + "sata2", + "sata3", + "sd0", + "spi0", + "spi1", + "tdm", + "ua0", + "ua1", +}; + +struct armada_38x_pinctrl { + void __iomem *base; + u8 variant; +}; + +static int armada_38x_pinctrl_get_pins_count(struct udevice *dev) +{ + return ARRAY_SIZE(armada_38x_mpp_modes); +} + +static const char *armada_38x_pinctrl_get_pin_name(struct udevice *dev, unsigned int selector) +{ + return armada_38x_mpp_modes[selector].name; +} + +static int armada_38x_pinctrl_get_functions_count(struct udevice *dev) +{ + return ARRAY_SIZE(armada_38x_mpp_function_names); +} + +static const char *armada_38x_pinctrl_get_function_name(struct udevice *dev, unsigned int selector) +{ + return armada_38x_mpp_function_names[selector]; +} + +static int armada_38x_pinctrl_get_pin_muxing(struct udevice *dev, unsigned int selector, + char *buf, int size) +{ + struct armada_38x_pinctrl *info = dev_get_priv(dev); + unsigned int off = (selector / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned int shift = (selector % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + const char *func_name = NULL; + const char *sub_name = NULL; + unsigned long config; + int i; + + config = (readl(info->base + off) >> shift) & MVEBU_MPP_MASK; + + for (i = 0; i < armada_38x_mpp_modes[selector].nsettings; i++) { + if (armada_38x_mpp_modes[selector].settings[i].val == config) + break; + } + + if (i < armada_38x_mpp_modes[selector].nsettings) { + func_name = armada_38x_mpp_modes[selector].settings[i].name; + sub_name = armada_38x_mpp_modes[selector].settings[i].subname; + } + + snprintf(buf, size, "%s%s%s", + func_name ? func_name : "unknown", + sub_name ? "_" : "", + sub_name ? sub_name : ""); + return 0; +} + +static int armada_38x_pinctrl_pinmux_set(struct udevice *dev, unsigned int pin_selector, + unsigned int func_selector) +{ + struct armada_38x_pinctrl *info = dev_get_priv(dev); + unsigned int off = (pin_selector / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned int shift = (pin_selector % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + const char *func_name = armada_38x_mpp_function_names[func_selector]; + unsigned long config, reg; + int i; + + for (i = 0; i < armada_38x_mpp_modes[pin_selector].nsettings; i++) { + if (strcmp(armada_38x_mpp_modes[pin_selector].settings[i].name, func_name) == 0) + break; + } + + if (i >= armada_38x_mpp_modes[pin_selector].nsettings) + return -EINVAL; + + if (!(info->variant & armada_38x_mpp_modes[pin_selector].settings[i].variant)) + return -EINVAL; + + reg = readl(info->base + off) & ~(MVEBU_MPP_MASK << shift); + config = armada_38x_mpp_modes[pin_selector].settings[i].val; + writel(reg | (config << shift), info->base + off); + + return 0; +} + +static int armada_38x_pinctrl_gpio_request_enable(struct udevice *dev, unsigned int selector) +{ + char buf[20]; + + armada_38x_pinctrl_get_pin_muxing(dev, selector, buf, sizeof(buf)); + if (strcmp(buf, "gpio") != 0) + printf("Warning: Changing mpp%u function from %s to gpio...\n", selector, buf); + + return armada_38x_pinctrl_pinmux_set(dev, selector, 0); /* gpio is always function 0 */ +} + +static int armada_38x_pinctrl_gpio_disable_free(struct udevice *dev, unsigned int selector) +{ + /* nothing to do */ + return 0; +} + +static int armada_38x_pinctrl_set_state(struct udevice *dev, struct udevice *config) +{ + return pinctrl_generic_set_state_prefix(dev, config, "marvell,"); +} + +static int armada_38x_pinctrl_probe(struct udevice *dev) +{ + struct armada_38x_pinctrl *info = dev_get_priv(dev); + + info->variant = (u8)dev_get_driver_data(dev); + info->base = dev_read_addr_ptr(dev); + + if (!info->base) + return -EINVAL; + + return 0; +} + +struct pinctrl_ops armada_37xx_pinctrl_ops = { + .get_pins_count = armada_38x_pinctrl_get_pins_count, + .get_pin_name = armada_38x_pinctrl_get_pin_name, + .get_functions_count = armada_38x_pinctrl_get_functions_count, + .get_function_name = armada_38x_pinctrl_get_function_name, + .get_pin_muxing = armada_38x_pinctrl_get_pin_muxing, + .pinmux_set = armada_38x_pinctrl_pinmux_set, + .gpio_request_enable = armada_38x_pinctrl_gpio_request_enable, + .gpio_disable_free = armada_38x_pinctrl_gpio_disable_free, + .set_state = armada_38x_pinctrl_set_state, +}; + +static const struct udevice_id armada_38x_pinctrl_of_match[] = { + { + .compatible = "marvell,mv88f6810-pinctrl", + .data = V_88F6810, + }, + { + .compatible = "marvell,mv88f6820-pinctrl", + .data = V_88F6820, + }, + { + .compatible = "marvell,mv88f6828-pinctrl", + .data = V_88F6828, + }, + { }, +}; + +U_BOOT_DRIVER(armada_38x_pinctrl) = { + .name = "armada-38x-pinctrl", + .id = UCLASS_PINCTRL, + .of_match = of_match_ptr(armada_38x_pinctrl_of_match), + .probe = armada_38x_pinctrl_probe, + .priv_auto = sizeof(struct armada_38x_pinctrl), + .ops = &armada_37xx_pinctrl_ops, +}; diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index 536c6aff96..fd49a97b5b 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -52,7 +52,9 @@ void mvebu_pinctl_emmc_set_mux(struct udevice *dev, u32 pin, u32 func) EMMC_PHY_CTRL_SDPHY_EN); } } else if (!fdt_node_check_compatible(blob, node, - "marvell,armada-8k-cpm-pinctrl")) { + "marvell,armada-8k-cpm-pinctrl") || + !fdt_node_check_compatible(blob, node, + "marvell,armada-7k-pinctrl")) { if ((pin == CP110_EMMC_CLK_PIN_ID) && (func == CP110_EMMC_CLK_FUNC)) { clrbits_le32(priv->base_reg + CP_EMMC_PHY_CTRL_REG, diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c index 3c8e24088c..ec21d4ff83 100644 --- a/drivers/pinctrl/pinctrl-generic.c +++ b/drivers/pinctrl/pinctrl-generic.c @@ -234,6 +234,24 @@ enum pinmux_subnode_type { PST_PINMUX, }; +static const char *alloc_name_with_prefix(const char *name, const char *prefix) +{ + if (prefix) { + char *name_with_prefix = malloc(strlen(prefix) + sizeof("pins")); + if (name_with_prefix) + sprintf(name_with_prefix, "%s%s", prefix, name); + return name_with_prefix; + } else { + return name; + } +} + +static void free_name_with_prefix(const char *name_with_prefix, const char *prefix) +{ + if (prefix) + free((char *)name_with_prefix); +} + /** * pinctrl_generic_set_state_one() - set state for a certain pin/group * Apply all pin multiplexing and pin configurations specified by @config @@ -248,9 +266,11 @@ enum pinmux_subnode_type { */ static int pinctrl_generic_set_state_one(struct udevice *dev, struct udevice *config, + const char *prefix, enum pinmux_subnode_type subnode_type, unsigned selector) { + const char *function_propname; const char *propname; const void *value; struct ofprop property; @@ -259,18 +279,26 @@ static int pinctrl_generic_set_state_one(struct udevice *dev, assert(subnode_type != PST_NONE); + function_propname = alloc_name_with_prefix("function", prefix); + if (!function_propname) + return -ENOMEM; + dev_for_each_property(property, config) { value = dev_read_prop_by_prop(&property, &propname, &len); - if (!value) + if (!value) { + free_name_with_prefix(function_propname, prefix); return -EINVAL; + } /* pinmux subnodes already have their muxing set */ if (subnode_type != PST_PINMUX && - !strcmp(propname, "function")) { + !strcmp(propname, function_propname)) { func_selector = pinmux_func_name_to_selector(dev, value); - if (func_selector < 0) + if (func_selector < 0) { + free_name_with_prefix(function_propname, prefix); return func_selector; + } ret = pinmux_enable_setting(dev, subnode_type == PST_GROUP, selector, @@ -291,10 +319,13 @@ static int pinctrl_generic_set_state_one(struct udevice *dev, selector, param, arg); } - if (ret) + if (ret) { + free_name_with_prefix(function_propname, prefix); return ret; + } } + free_name_with_prefix(function_propname, prefix); return 0; } @@ -309,20 +340,34 @@ static int pinctrl_generic_set_state_one(struct udevice *dev, */ static enum pinmux_subnode_type pinctrl_generic_get_subnode_type(struct udevice *dev, struct udevice *config, + const char *prefix, int *count) { const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + const char *propname; - *count = dev_read_string_count(config, "pins"); + propname = alloc_name_with_prefix("pins", prefix); + if (!propname) + return -ENOMEM; + *count = dev_read_string_count(config, propname); + free_name_with_prefix(propname, prefix); if (*count >= 0) return PST_PIN; - *count = dev_read_string_count(config, "groups"); + propname = alloc_name_with_prefix("groups", prefix); + if (!propname) + return -ENOMEM; + *count = dev_read_string_count(config, propname); + free_name_with_prefix(propname, prefix); if (*count >= 0) return PST_GROUP; if (ops->pinmux_property_set) { - *count = dev_read_size(config, "pinmux"); + propname = alloc_name_with_prefix("pinmux", prefix); + if (!propname) + return -ENOMEM; + *count = dev_read_size(config, propname); + free_name_with_prefix(propname, prefix); if (*count >= 0 && !(*count % sizeof(u32))) { *count /= sizeof(u32); return PST_PINMUX; @@ -338,23 +383,30 @@ static enum pinmux_subnode_type pinctrl_generic_get_subnode_type(struct udevice * * @dev: pin controller device * @config: pseudo device pointing to config node + * @prefix: device tree property prefix (e.g. vendor specific) * @return: 0 on success, or negative error code on failure */ static int pinctrl_generic_set_state_subnode(struct udevice *dev, - struct udevice *config) + struct udevice *config, + const char *prefix) { enum pinmux_subnode_type subnode_type; + const char *propname; const char *name; int count, selector, i, ret, scratch; const u32 *pinmux_groups = NULL; /* prevent use-uninitialized warning */ - subnode_type = pinctrl_generic_get_subnode_type(dev, config, &count); + subnode_type = pinctrl_generic_get_subnode_type(dev, config, prefix, &count); debug("%s(%s, %s): count=%d\n", __func__, dev->name, config->name, count); if (subnode_type == PST_PINMUX) { - pinmux_groups = dev_read_prop(config, "pinmux", &scratch); + propname = alloc_name_with_prefix("pinmux", prefix); + if (!propname) + return -ENOMEM; + pinmux_groups = dev_read_prop(config, propname, &scratch); + free_name_with_prefix(propname, prefix); if (!pinmux_groups) return -EINVAL; } @@ -362,13 +414,21 @@ static int pinctrl_generic_set_state_subnode(struct udevice *dev, for (i = 0; i < count; i++) { switch (subnode_type) { case PST_PIN: - ret = dev_read_string_index(config, "pins", i, &name); + propname = alloc_name_with_prefix("pins", prefix); + if (!propname) + return -ENOMEM; + ret = dev_read_string_index(config, propname, i, &name); + free_name_with_prefix(propname, prefix); if (ret) return ret; selector = pinctrl_pin_name_to_selector(dev, name); break; case PST_GROUP: - ret = dev_read_string_index(config, "groups", i, &name); + propname = alloc_name_with_prefix("groups", prefix); + if (!propname) + return -ENOMEM; + ret = dev_read_string_index(config, propname, i, &name); + free_name_with_prefix(propname, prefix); if (ret) return ret; selector = pinctrl_group_name_to_selector(dev, name); @@ -390,8 +450,8 @@ static int pinctrl_generic_set_state_subnode(struct udevice *dev, if (selector < 0) return selector; - ret = pinctrl_generic_set_state_one(dev, config, subnode_type, - selector); + ret = pinctrl_generic_set_state_one(dev, config, prefix, + subnode_type, selector); if (ret) return ret; } @@ -399,22 +459,28 @@ static int pinctrl_generic_set_state_subnode(struct udevice *dev, return 0; } -int pinctrl_generic_set_state(struct udevice *dev, struct udevice *config) +int pinctrl_generic_set_state_prefix(struct udevice *dev, struct udevice *config, + const char *prefix) { struct udevice *child; int ret; - ret = pinctrl_generic_set_state_subnode(dev, config); + ret = pinctrl_generic_set_state_subnode(dev, config, prefix); if (ret) return ret; for (device_find_first_child(config, &child); child; device_find_next_child(&child)) { - ret = pinctrl_generic_set_state_subnode(dev, child); + ret = pinctrl_generic_set_state_subnode(dev, child, prefix); if (ret) return ret; } return 0; } + +int pinctrl_generic_set_state(struct udevice *dev, struct udevice *config) +{ + return pinctrl_generic_set_state_prefix(dev, config, NULL); +} diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index 38ce2b5e0a..ce2d5ddf6d 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -222,9 +222,10 @@ pinctrl_gpio_get_pinctrl_and_offset(struct udevice *dev, unsigned offset, * * @dev: GPIO peripheral device * @offset: the GPIO pin offset from the GPIO controller + * @label: the GPIO pin label * @return: 0 on success, or negative error code on failure */ -int pinctrl_gpio_request(struct udevice *dev, unsigned offset) +int pinctrl_gpio_request(struct udevice *dev, unsigned offset, const char *label) { const struct pinctrl_ops *ops; struct udevice *pctldev; diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h index a09b242fd9..e3e50afeaf 100644 --- a/include/dm/pinctrl.h +++ b/include/dm/pinctrl.h @@ -491,6 +491,8 @@ enum pin_config_param { * Return: 0 on success, or negative error code on failure */ int pinctrl_generic_set_state(struct udevice *pctldev, struct udevice *config); +int pinctrl_generic_set_state_prefix(struct udevice *pctldev, struct udevice *config, + const char *prefix); #else static inline int pinctrl_generic_set_state(struct udevice *pctldev, struct udevice *config) @@ -609,10 +611,11 @@ int pinctrl_get_pin_name(struct udevice *dev, int selector, char *buf, * pinctrl_gpio_request() - Request a single pin to be used as GPIO * @dev: GPIO peripheral device * @offset: GPIO pin offset from the GPIO controller + * @label: GPIO label * * Return: 0 on success, or negative error code on failure */ -int pinctrl_gpio_request(struct udevice *dev, unsigned offset); +int pinctrl_gpio_request(struct udevice *dev, unsigned offset, const char *label); /** * pinctrl_gpio_free() - Free a single pin used as GPIO |