diff options
32 files changed, 1506 insertions, 625 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 355d214522..a946af816f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -123,6 +123,8 @@ config SANDBOX imply DM_SOUND imply PCI_SANDBOX_EP imply PCH + imply PHYLIB + imply DM_MDIO config SH bool "SuperH architecture" diff --git a/arch/arm/dts/k3-am65.dtsi b/arch/arm/dts/k3-am65.dtsi index 3d4bf369d0..9d1ed49753 100644 --- a/arch/arm/dts/k3-am65.dtsi +++ b/arch/arm/dts/k3-am65.dtsi @@ -64,6 +64,7 @@ /* MCUSS Range */ <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>, <0x00 0x40200000 0x00 0x40200000 0x00 0x00900100>, + <0x00 0x40f00000 0x00 0x40f00000 0x00 0x00020000>, <0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>, <0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>, <0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>, @@ -75,6 +76,7 @@ #size-cells = <2>; ranges = <0x00 0x28380000 0x00 0x28380000 0x00 0x03880000>, /* MCU NAVSS*/ <0x00 0x40200000 0x00 0x40200000 0x00 0x00900100>, /* First peripheral window */ + <0x00 0x40f00000 0x00 0x40f00000 0x00 0x00020000>, /* CTRL_MMR0 */ <0x00 0x42040000 0x00 0x42040000 0x00 0x03ac2400>, /* WKUP */ <0x00 0x45100000 0x00 0x45100000 0x00 0x00c24000>, /* MMRs, remaining NAVSS */ <0x00 0x46000000 0x00 0x46000000 0x00 0x00200000>, /* CPSW */ diff --git a/arch/arm/dts/k3-am654-base-board-u-boot.dtsi b/arch/arm/dts/k3-am654-base-board-u-boot.dtsi index f5c8253831..844a5cd96a 100644 --- a/arch/arm/dts/k3-am654-base-board-u-boot.dtsi +++ b/arch/arm/dts/k3-am654-base-board-u-boot.dtsi @@ -5,6 +5,7 @@ #include <dt-bindings/pinctrl/k3.h> #include <dt-bindings/dma/k3-udma.h> +#include <dt-bindings/net/ti-dp83867.h> / { chosen { @@ -13,6 +14,7 @@ aliases { serial2 = &main_uart0; + ethernet0 = &cpsw_port1; }; }; @@ -110,6 +112,116 @@ dma-coherent; }; }; + + mcu_conf: scm_conf@40f00000 { + compatible = "syscon"; + reg = <0x0 0x40f00000 0x0 0x20000>; + }; + + mcu_cpsw: cpsw_nuss@046000000 { + compatible = "ti,am654-cpsw-nuss"; + #address-cells = <2>; + #size-cells = <2>; + reg = <0x0 0x46000000 0x0 0x200000>; + reg-names = "cpsw_nuss"; + ranges; + dma-coherent; + clocks = <&k3_clks 5 10>; + clock-names = "fck"; + power-domains = <&k3_pds 5>; + ti,psil-base = <0x7000>; + + dmas = <&mcu_udmap &mcu_cpsw 0 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 1 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 2 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 3 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 4 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 5 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 6 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 7 UDMA_DIR_TX>, + <&mcu_udmap &mcu_cpsw 0 UDMA_DIR_RX>; + dma-names = "tx0", "tx1", "tx2", "tx3", + "tx4", "tx5", "tx6", "tx7", + "rx"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + host: host@0 { + reg = <0>; + ti,label = "host"; + }; + + cpsw_port1: port@1 { + reg = <1>; + ti,mac-only; + ti,label = "port1"; + ti,syscon-efuse = <&mcu_conf 0x200>; + }; + }; + + davinci_mdio: mdio { + #address-cells = <1>; + #size-cells = <0>; + bus_freq = <1000000>; + }; + + ti,psil-config0 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config1 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config2 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config3 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config4 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config5 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config6 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + + ti,psil-config7 { + linux,udma-mode = <UDMA_PKT_MODE>; + statictr-type = <PSIL_STATIC_TR_NONE>; + ti,needs-epib; + ti,psd-size = <16>; + }; + }; }; &cbass_wakeup { @@ -189,6 +301,32 @@ u-boot,dm-spl; }; +&wkup_pmx0 { + mcu_cpsw_pins_default: mcu_cpsw_pins_default { + pinctrl-single,pins = < + AM65X_WKUP_IOPAD(0x0058, PIN_OUTPUT, 0) /* (N4) MCU_RGMII1_TX_CTL */ + AM65X_WKUP_IOPAD(0x005c, PIN_INPUT, 0) /* (N5) MCU_RGMII1_RX_CTL */ + AM65X_WKUP_IOPAD(0x0060, PIN_OUTPUT, 0) /* (M2) MCU_RGMII1_TD3 */ + AM65X_WKUP_IOPAD(0x0064, PIN_OUTPUT, 0) /* (M3) MCU_RGMII1_TD2 */ + AM65X_WKUP_IOPAD(0x0068, PIN_OUTPUT, 0) /* (M4) MCU_RGMII1_TD1 */ + AM65X_WKUP_IOPAD(0x006c, PIN_OUTPUT, 0) /* (M5) MCU_RGMII1_TD0 */ + AM65X_WKUP_IOPAD(0x0078, PIN_INPUT, 0) /* (L2) MCU_RGMII1_RD3 */ + AM65X_WKUP_IOPAD(0x007c, PIN_INPUT, 0) /* (L5) MCU_RGMII1_RD2 */ + AM65X_WKUP_IOPAD(0x0080, PIN_INPUT, 0) /* (M6) MCU_RGMII1_RD1 */ + AM65X_WKUP_IOPAD(0x0084, PIN_INPUT, 0) /* (L6) MCU_RGMII1_RD0 */ + AM65X_WKUP_IOPAD(0x0070, PIN_INPUT, 0) /* (N1) MCU_RGMII1_TXC */ + AM65X_WKUP_IOPAD(0x0074, PIN_INPUT, 0) /* (M1) MCU_RGMII1_RXC */ + >; + }; + + mcu_mdio_pins_default: mcu_mdio1_pins_default { + pinctrl-single,pins = < + AM65X_WKUP_IOPAD(0x008c, PIN_OUTPUT, 0) /* (L1) MCU_MDIO0_MDC */ + AM65X_WKUP_IOPAD(0x0088, PIN_INPUT, 0) /* (L4) MCU_MDIO0_MDIO */ + >; + }; +}; + &main_uart0 { u-boot,dm-spl; pinctrl-names = "default"; @@ -212,3 +350,35 @@ pinctrl-0 = <&main_mmc1_pins_default>; sdhci-caps-mask = <0x7 0x0>; }; + +&mcu_cpsw { + pinctrl-names = "default"; + pinctrl-0 = <&mcu_cpsw_pins_default &mcu_mdio_pins_default>; +}; + +&davinci_mdio { + phy0: ethernet-phy@0 { + reg = <0>; + /* TODO: phy reset: TCA9555RTWR(i2c:0x21)[p04].GPIO_MCU_RGMII_RSTN */ + ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>; + ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>; + ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>; + }; +}; + +&cpsw_port1 { + phy-mode = "rgmii-id"; + phy-handle = <&phy0>; +}; + +&mcu_cpsw { + reg = <0x0 0x46000000 0x0 0x200000>, + <0x0 0x40f00200 0x0 0x2>; + reg-names = "cpsw_nuss", "mac_efuse"; + + cpsw-phy-sel@40f04040 { + compatible = "ti,am654-cpsw-phy-sel"; + reg= <0x0 0x40f04040 0x0 0x4>; + reg-names = "gmii-sel"; + }; +}; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8147d9781e..531c1afc97 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -823,6 +823,10 @@ dmas = <&dma 0>, <&dma 1>, <&dma 2>; dma-names = "m2m", "tx0", "rx0"; }; + + mdio-test { + compatible = "sandbox,mdio"; + }; }; #include "sandbox_pmic.dtsi" diff --git a/cmd/mdio.c b/cmd/mdio.c index 5e219f699d..a6fa9266d0 100644 --- a/cmd/mdio.c +++ b/cmd/mdio.c @@ -203,6 +203,11 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc < 2) return CMD_RET_USAGE; +#ifdef CONFIG_DM_MDIO + /* probe DM MII device before any operation so they are all accesible */ + dm_mdio_probe_devices(); +#endif + /* * We use the last specified parameters, unless new ones are * entered. @@ -12,25 +12,11 @@ #include <command.h> #include <miiphy.h> -typedef struct _MII_reg_desc_t { - ushort regno; - char * name; -} MII_reg_desc_t; - -static const MII_reg_desc_t reg_0_5_desc_tbl[] = { - { MII_BMCR, "PHY control register" }, - { MII_BMSR, "PHY status register" }, - { MII_PHYSID1, "PHY ID 1 register" }, - { MII_PHYSID2, "PHY ID 2 register" }, - { MII_ADVERTISE, "Autonegotiation advertisement register" }, - { MII_LPA, "Autonegotiation partner abilities register" }, -}; - typedef struct _MII_field_desc_t { ushort hi; ushort lo; ushort mask; - char * name; + const char *name; } MII_field_desc_t; static const MII_field_desc_t reg_0_desc_tbl[] = { @@ -87,7 +73,7 @@ static const MII_field_desc_t reg_4_desc_tbl[] = { { 7, 7, 0x01, "100BASE-TX able" }, { 6, 6, 0x01, "10BASE-T full duplex able" }, { 5, 5, 0x01, "10BASE-T able" }, - { 4, 0, 0x1f, "xxx to do" }, + { 4, 0, 0x1f, "selector" }, }; static const MII_field_desc_t reg_5_desc_tbl[] = { @@ -102,50 +88,91 @@ static const MII_field_desc_t reg_5_desc_tbl[] = { { 7, 7, 0x01, "100BASE-TX able" }, { 6, 6, 0x01, "10BASE-T full duplex able" }, { 5, 5, 0x01, "10BASE-T able" }, - { 4, 0, 0x1f, "xxx to do" }, + { 4, 0, 0x1f, "partner selector" }, }; -typedef struct _MII_field_desc_and_len_t { + +static const MII_field_desc_t reg_9_desc_tbl[] = { + { 15, 13, 0x07, "test mode" }, + { 12, 12, 0x01, "manual master/slave enable" }, + { 11, 11, 0x01, "manual master/slave value" }, + { 10, 10, 0x01, "multi/single port" }, + { 9, 9, 0x01, "1000BASE-T full duplex able" }, + { 8, 8, 0x01, "1000BASE-T half duplex able" }, + { 7, 7, 0x01, "automatic TDR on link down" }, + { 6, 6, 0x7f, "(reserved)" }, +}; + +static const MII_field_desc_t reg_10_desc_tbl[] = { + { 15, 15, 0x01, "master/slave config fault" }, + { 14, 14, 0x01, "master/slave config result" }, + { 13, 13, 0x01, "local receiver status OK" }, + { 12, 12, 0x01, "remote receiver status OK" }, + { 11, 11, 0x01, "1000BASE-T full duplex able" }, + { 10, 10, 0x01, "1000BASE-T half duplex able" }, + { 9, 8, 0x03, "(reserved)" }, + { 7, 0, 0xff, "1000BASE-T idle error counter"}, +}; + +typedef struct _MII_reg_desc_t { + ushort regno; const MII_field_desc_t *pdesc; ushort len; -} MII_field_desc_and_len_t; - -static const MII_field_desc_and_len_t desc_and_len_tbl[] = { - { reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl) }, - { reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl) }, - { reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl) }, - { reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl) }, - { reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl) }, - { reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl) }, + const char *name; +} MII_reg_desc_t; + +static const MII_reg_desc_t mii_reg_desc_tbl[] = { + { MII_BMCR, reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl), + "PHY control register" }, + { MII_BMSR, reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl), + "PHY status register" }, + { MII_PHYSID1, reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl), + "PHY ID 1 register" }, + { MII_PHYSID2, reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl), + "PHY ID 2 register" }, + { MII_ADVERTISE, reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl), + "Autonegotiation advertisement register" }, + { MII_LPA, reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl), + "Autonegotiation partner abilities register" }, + { MII_CTRL1000, reg_9_desc_tbl, ARRAY_SIZE(reg_9_desc_tbl), + "1000BASE-T control register" }, + { MII_STAT1000, reg_10_desc_tbl, ARRAY_SIZE(reg_10_desc_tbl), + "1000BASE-T status register" }, }; static void dump_reg( ushort regval, - const MII_reg_desc_t *prd, - const MII_field_desc_and_len_t *pdl); - -static int special_field( - ushort regno, - const MII_field_desc_t *pdesc, - ushort regval); - -static void MII_dump_0_to_5( - ushort regvals[6], - uchar reglo, - uchar reghi) + const MII_reg_desc_t *prd); + +static bool special_field(ushort regno, const MII_field_desc_t *pdesc, + ushort regval); + +static void MII_dump(const ushort *regvals, uchar reglo, uchar reghi) { ulong i; - for (i = 0; i < 6; i++) { - if ((reglo <= i) && (i <= reghi)) - dump_reg(regvals[i], ®_0_5_desc_tbl[i], - &desc_and_len_tbl[i]); + for (i = 0; i < ARRAY_SIZE(mii_reg_desc_tbl); i++) { + const uchar reg = mii_reg_desc_tbl[i].regno; + + if (reg >= reglo && reg <= reghi) + dump_reg(regvals[reg - reglo], &mii_reg_desc_tbl[i]); } } +/* Print out field position, value, name */ +static void dump_field(const MII_field_desc_t *pdesc, ushort regval) +{ + if (pdesc->hi == pdesc->lo) + printf("%2u ", pdesc->lo); + else + printf("%2u-%2u", pdesc->hi, pdesc->lo); + + printf(" = %5u %s", (regval >> pdesc->lo) & pdesc->mask, + pdesc->name); +} + static void dump_reg( ushort regval, - const MII_reg_desc_t *prd, - const MII_field_desc_and_len_t *pdl) + const MII_reg_desc_t *prd) { ulong i; ushort mask_in_place; @@ -154,8 +181,8 @@ static void dump_reg( printf("%u. (%04hx) -- %s --\n", prd->regno, regval, prd->name); - for (i = 0; i < pdl->len; i++) { - pdesc = &pdl->pdesc[i]; + for (i = 0; i < prd->len; i++) { + pdesc = &prd->pdesc[i]; mask_in_place = pdesc->mask << pdesc->lo; @@ -164,17 +191,8 @@ static void dump_reg( regval & mask_in_place, prd->regno); - if (special_field(prd->regno, pdesc, regval)) { - } - else { - if (pdesc->hi == pdesc->lo) - printf("%2u ", pdesc->lo); - else - printf("%2u-%2u", pdesc->hi, pdesc->lo); - printf(" = %5u %s", - (regval & mask_in_place) >> pdesc->lo, - pdesc->name); - } + if (!special_field(prd->regno, pdesc, regval)) + dump_field(pdesc, regval); printf("\n"); } @@ -190,11 +208,11 @@ static void dump_reg( ** 5.4-0 */ -static int special_field( - ushort regno, - const MII_field_desc_t *pdesc, - ushort regval) +static bool special_field(ushort regno, const MII_field_desc_t *pdesc, + ushort regval) { + const ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; + if ((regno == MII_BMCR) && (pdesc->lo == 6)) { ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100); printf("%2u,%2u = b%u%u speed selection = %s Mbps", @@ -208,34 +226,26 @@ static int special_field( } else if ((regno == MII_BMCR) && (pdesc->lo == 8)) { - printf("%2u = %5u duplex = %s", - pdesc->lo, - (regval >> pdesc->lo) & 1, - ((regval >> pdesc->lo) & 1) ? "full" : "half"); + dump_field(pdesc, regval); + printf(" = %s", ((regval >> pdesc->lo) & 1) ? "full" : "half"); return 1; } else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) { - ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; - printf("%2u-%2u = %5u selector = %s", - pdesc->hi, pdesc->lo, sel_bits, - sel_bits == PHY_ANLPAR_PSB_802_3 ? - "IEEE 802.3" : - sel_bits == PHY_ANLPAR_PSB_802_9 ? - "IEEE 802.9 ISLAN-16T" : - "???"); + dump_field(pdesc, regval); + printf(" = %s", + sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : "???"); return 1; } else if ((regno == MII_LPA) && (pdesc->lo == 0)) { - ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; - printf("%2u-%2u = %u selector = %s", - pdesc->hi, pdesc->lo, sel_bits, - sel_bits == PHY_ANLPAR_PSB_802_3 ? - "IEEE 802.3" : - sel_bits == PHY_ANLPAR_PSB_802_9 ? - "IEEE 802.9 ISLAN-16T" : - "???"); + dump_field(pdesc, regval); + printf(" = %s", + sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : "???"); return 1; } @@ -406,17 +416,16 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } } } else if (strncmp(op, "du", 2) == 0) { - ushort regs[6]; + ushort regs[MII_STAT1000 + 1]; /* Last reg is 0x0a */ int ok = 1; - if ((reglo > 5) || (reghi > 5)) { - printf( - "The MII dump command only formats the " - "standard MII registers, 0-5.\n"); + if (reglo > MII_STAT1000 || reghi > MII_STAT1000) { + printf("The MII dump command only formats the standard MII registers, 0-5, 9-a.\n"); return 1; } for (addr = addrlo; addr <= addrhi; addr++) { - for (reg = reglo; reg < reghi + 1; reg++) { - if (miiphy_read(devname, addr, reg, ®s[reg]) != 0) { + for (reg = reglo; reg <= reghi; reg++) { + if (miiphy_read(devname, addr, reg, + ®s[reg - reglo]) != 0) { ok = 0; printf( "Error reading from the PHY addr=%02x reg=%02x\n", @@ -425,7 +434,7 @@ static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } } if (ok) - MII_dump_0_to_5(regs, reglo, reghi); + MII_dump(regs, reglo, reghi); printf("\n"); } } else if (strncmp(op, "de", 2) == 0) { diff --git a/configs/am65x_evm_a53_defconfig b/configs/am65x_evm_a53_defconfig index 41cf0100fa..b940af3f56 100644 --- a/configs/am65x_evm_a53_defconfig +++ b/configs/am65x_evm_a53_defconfig @@ -44,6 +44,7 @@ CONFIG_SPL_MULTI_DTB_FIT_NO_COMPRESSION=y CONFIG_ENV_IS_IN_FAT=y CONFIG_ENV_FAT_INTERFACE="mmc" CONFIG_ENV_FAT_DEVICE_AND_PART="1:1" +CONFIG_NET_RANDOM_ETHADDR=y CONFIG_DM=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y @@ -58,6 +59,11 @@ CONFIG_K3_SEC_PROXY=y CONFIG_DM_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_K3_ARASAN=y +CONFIG_PHY_TI=y +CONFIG_PHY_FIXED=y +CONFIG_DM_ETH=y +CONFIG_TI_AM65_CPSW_NUSS=y +CONFIG_PHY=y CONFIG_PINCTRL=y # CONFIG_PINCTRL_GENERIC is not set CONFIG_SPL_PINCTRL=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e6a4fdf30e..635f8d72c2 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -11,6 +11,29 @@ config DM_ETH This is currently implemented in net/eth-uclass.c Look in include/net.h for details. +config DM_MDIO + bool "Enable Driver Model for MDIO devices" + depends on DM_ETH && PHYLIB + help + Enable driver model for MDIO devices + + Adds UCLASS_MDIO DM class supporting MDIO buses that are probed as + stand-alone devices. Useful in particular for systems that support + DM_ETH and have a stand-alone MDIO hardware block shared by multiple + Ethernet interfaces. + This is currently implemented in net/mdio-uclass.c + Look in include/miiphy.h for details. + +config MDIO_SANDBOX + depends on DM_MDIO && SANDBOX + default y + bool "Sandbox: Mocked MDIO driver" + help + This driver implements dummy read/write/reset MDIO functions mimicking + a bus with a single PHY. + + This driver is used in for testing in test/dm/mdio.c + menuconfig NETDEVICES bool "Network device support" depends on NET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 8d02a37896..40038427db 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,3 +77,4 @@ obj-y += ti/ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o +obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o diff --git a/drivers/net/macb.c b/drivers/net/macb.c index c5560a7111..a7eddd647d 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -613,10 +613,12 @@ static int macb_phy_init(struct macb_device *macb, const char *name) /* First check for GMAC and that it is GiB capable */ if (gem_is_gigabit_capable(macb)) { - lpa = macb_mdio_read(macb, MII_STAT1000); + lpa = macb_mdio_read(macb, MII_LPA); - if (lpa & (LPA_1000FULL | LPA_1000HALF)) { - duplex = ((lpa & LPA_1000FULL) ? 1 : 0); + if (lpa & (LPA_1000FULL | LPA_1000HALF | LPA_1000XFULL | + LPA_1000XHALF)) { + duplex = ((lpa & (LPA_1000FULL | LPA_1000XFULL)) ? + 1 : 0); printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n", name, diff --git a/drivers/net/mdio_sandbox.c b/drivers/net/mdio_sandbox.c new file mode 100644 index 0000000000..07515e078c --- /dev/null +++ b/drivers/net/mdio_sandbox.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <dm.h> +#include <errno.h> +#include <miiphy.h> + +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG 0 + +struct mdio_sandbox_priv { + int enabled; + u16 reg; +}; + +static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + if (!priv->enabled) + return -ENODEV; + + if (addr != SANDBOX_PHY_ADDR) + return -ENODEV; + if (devad != MDIO_DEVAD_NONE) + return -ENODEV; + if (reg != SANDBOX_PHY_REG) + return -ENODEV; + + return priv->reg; +} + +static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg, + u16 val) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + if (!priv->enabled) + return -ENODEV; + + if (addr != SANDBOX_PHY_ADDR) + return -ENODEV; + if (devad != MDIO_DEVAD_NONE) + return -ENODEV; + if (reg != SANDBOX_PHY_REG) + return -ENODEV; + + priv->reg = val; + + return 0; +} + +static int mdio_sandbox_reset(struct udevice *dev) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + priv->reg = 0; + + return 0; +} + +static const struct mdio_ops mdio_sandbox_ops = { + .read = mdio_sandbox_read, + .write = mdio_sandbox_write, + .reset = mdio_sandbox_reset, +}; + +static int mdio_sandbox_probe(struct udevice *dev) +{ + struct mdio_sandbox_priv *priv = dev_get_priv(dev); + + priv->enabled = 1; + + return 0; +} + +static const struct udevice_id mdio_sandbox_ids[] = { + { .compatible = "sandbox,mdio" }, + { } +}; + +U_BOOT_DRIVER(mdio_sandbox) = { + .name = "mdio_sandbox", + .id = UCLASS_MDIO, + .of_match = mdio_sandbox_ids, + .probe = mdio_sandbox_probe, + .ops = &mdio_sandbox_ops, + .priv_auto_alloc_size = sizeof(struct mdio_sandbox_priv), +}; diff --git a/drivers/net/mscc_eswitch/Makefile b/drivers/net/mscc_eswitch/Makefile index 02f39a76bb..d583fe9fc4 100644 --- a/drivers/net/mscc_eswitch/Makefile +++ b/drivers/net/mscc_eswitch/Makefile @@ -1,6 +1,6 @@ -obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o -obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o -obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o -obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o -obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o +obj-$(CONFIG_MSCC_OCELOT_SWITCH) += ocelot_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o +obj-$(CONFIG_MSCC_LUTON_SWITCH) += luton_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o +obj-$(CONFIG_MSCC_JR2_SWITCH) += jr2_switch.o mscc_xfer.o mscc_miim.o +obj-$(CONFIG_MSCC_SERVALT_SWITCH) += servalt_switch.o mscc_xfer.o mscc_miim.o +obj-$(CONFIG_MSCC_SERVAL_SWITCH) += serval_switch.o mscc_xfer.o mscc_mac_table.o mscc_miim.o diff --git a/drivers/net/mscc_eswitch/jr2_switch.c b/drivers/net/mscc_eswitch/jr2_switch.c index 60d408f1c7..665517775e 100644 --- a/drivers/net/mscc_eswitch/jr2_switch.c +++ b/drivers/net/mscc_eswitch/jr2_switch.c @@ -17,20 +17,7 @@ #include <dt-bindings/mscc/jr2_data.h> #include "mscc_xfer.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_SCAN BIT(0) -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x3 << 16) +#include "mscc_miim.h" #define ANA_AC_RAM_CTRL_RAM_INIT 0x94358 #define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x94370 @@ -279,13 +266,6 @@ struct jr2_private { struct jr2_phy_port_t ports[MAX_PORT]; }; -struct jr2_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long jr2_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -294,99 +274,9 @@ static const unsigned long jr2_regs_qs[] = { [MSCC_QS_INJ_CTRL] = 0x34, }; -static struct jr2_miim_dev miim[JR2_MIIM_BUS_COUNT]; +static struct mscc_miim_dev miim[JR2_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct jr2_miim_dev *miim) -{ - unsigned long deadline; - u32 val; - - deadline = timer_get_us() + 250000; - - do { - val = readl(miim->regs + GCB_MIIM_MII_STATUS); - } while (timer_get_us() <= deadline && (val & GCB_MIIM_STAT_BUSY)); - - if (val & GCB_MIIM_STAT_BUSY) - return -ETIMEDOUT; - - return 0; -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct jr2_miim_dev *miim = (struct jr2_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct jr2_miim_dev *miim = (struct jr2_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - - out: - return ret; -} - -static struct mii_dev *jr2_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void jr2_cpu_capture_setup(struct jr2_private *priv) { /* ASM: No preamble and IFH prefix on CPU injected frames */ @@ -973,7 +863,7 @@ static int jr2_probe(struct udevice *dev) } /* Initialize miim buses */ - memset(&miim, 0x0, sizeof(struct jr2_miim_dev) * JR2_MIIM_BUS_COUNT); + memset(&miim, 0x0, sizeof(struct mscc_miim_dev) * JR2_MIIM_BUS_COUNT); /* iterate all the ports and find out on which bus they are */ i = 0; @@ -1008,7 +898,8 @@ static int jr2_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - jr2_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/luton_switch.c b/drivers/net/mscc_eswitch/luton_switch.c index 94852b06e7..dffe81d873 100644 --- a/drivers/net/mscc_eswitch/luton_switch.c +++ b/drivers/net/mscc_eswitch/luton_switch.c @@ -17,18 +17,7 @@ #include "mscc_xfer.h" #include "mscc_mac_table.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x2 << 16) +#include "mscc_miim.h" #define ANA_PORT_VLAN_CFG(x) (0x00 + 0x80 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) @@ -189,13 +178,6 @@ struct luton_private { struct luton_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long luton_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x18, [MSCC_QS_XTR_FLUSH] = 0x28, @@ -213,84 +195,6 @@ static const unsigned long luton_regs_ana_table[] = { static struct mscc_miim_dev miim[LUTON_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - out: - return ret; -} - -static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void luton_stop(struct udevice *dev) { struct luton_private *priv = dev_get_priv(dev); @@ -760,7 +664,8 @@ static int luton_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - serval_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/mscc_miim.c b/drivers/net/mscc_eswitch/mscc_miim.c index 419dcc1dd6..d8ee8df47b 100644 --- a/drivers/net/mscc_eswitch/mscc_miim.c +++ b/drivers/net/mscc_eswitch/mscc_miim.c @@ -72,3 +72,31 @@ int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, out: return ret; } + +struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count, + phys_addr_t miim_base, + unsigned long miim_size) +{ + struct mii_dev *bus; + + bus = mdio_alloc(); + + if (!bus) + return NULL; + + *miim_count += 1; + sprintf(bus->name, "miim-bus%d", *miim_count); + + miim[*miim_count].regs = ioremap(miim_base, miim_size); + miim[*miim_count].miim_base = miim_base; + miim[*miim_count].miim_size = miim_size; + bus->priv = &miim[*miim_count]; + bus->read = mscc_miim_read; + bus->write = mscc_miim_write; + + if (mdio_register(bus)) + return NULL; + + miim[*miim_count].bus = bus; + return bus; +} diff --git a/drivers/net/mscc_eswitch/mscc_miim.h b/drivers/net/mscc_eswitch/mscc_miim.h index 0e5d5e3c81..feb1f40ae5 100644 --- a/drivers/net/mscc_eswitch/mscc_miim.h +++ b/drivers/net/mscc_eswitch/mscc_miim.h @@ -3,10 +3,22 @@ * Copyright (c) 2018 Microsemi Corporation */ +#ifndef _MSCC_MIIM_H_ +#define _MSCC_MIIM_H_ + struct mscc_miim_dev { void __iomem *regs; - void __iomem *phy_regs; + phys_addr_t miim_base; + unsigned long miim_size; + struct mii_dev *bus; }; int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg); int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, u16 val); + +struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count, + phys_addr_t miim_base, + unsigned long miim_size); + + +#endif /* _MSCC_MIIM_H_ */ diff --git a/drivers/net/mscc_eswitch/ocelot_switch.c b/drivers/net/mscc_eswitch/ocelot_switch.c index 5c7e6961be..0ba84ab78a 100644 --- a/drivers/net/mscc_eswitch/ocelot_switch.c +++ b/drivers/net/mscc_eswitch/ocelot_switch.c @@ -17,6 +17,7 @@ #include "mscc_xfer.h" #include "mscc_mac_table.h" +#include "mscc_miim.h" #define PHY_CFG 0x0 #define PHY_CFG_ENA 0xF @@ -25,20 +26,6 @@ #define PHY_STAT 0x4 #define PHY_STAT_SUPERVISOR_COMPLETE BIT(0) -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_SCAN BIT(0) -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_SINGLE_SCAN BIT(3) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x3 << 16) - #define ANA_PORT_VLAN_CFG(x) (0x7000 + 0x100 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) #define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18) @@ -173,13 +160,6 @@ struct ocelot_private { struct ocelot_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static struct mscc_miim_dev miim[OCELOT_MIIM_BUS_COUNT]; static int miim_count = -1; @@ -209,85 +189,6 @@ static void mscc_phy_reset(void) } } -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - out: - return ret; -} - -static struct mii_dev *ocelot_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - __weak void mscc_switch_reset(void) { } @@ -682,7 +583,8 @@ static int ocelot_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - ocelot_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/serval_switch.c b/drivers/net/mscc_eswitch/serval_switch.c index 2559f5d0cd..1a21360a96 100644 --- a/drivers/net/mscc_eswitch/serval_switch.c +++ b/drivers/net/mscc_eswitch/serval_switch.c @@ -17,18 +17,7 @@ #include "mscc_xfer.h" #include "mscc_mac_table.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x2 << 16) +#include "mscc_miim.h" #define ANA_PORT_VLAN_CFG(x) (0xc000 + 0x100 * (x)) #define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) @@ -156,13 +145,6 @@ struct serval_private { struct serval_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long serval_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -180,84 +162,6 @@ static const unsigned long serval_regs_ana_table[] = { static struct mscc_miim_dev miim[SERVAL_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; - out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - out: - return ret; -} - -static struct mii_dev *serval_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void serval_cpu_capture_setup(struct serval_private *priv) { int i; @@ -356,8 +260,6 @@ static void serdes_write(void __iomem *base, u32 addr) do { data = readl(base + HSIO_MCB_SERDES1G_CFG); } while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT); - - mdelay(100); } static void serdes1g_setup(void __iomem *base, uint32_t addr, @@ -636,7 +538,8 @@ static int serval_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - serval_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/mscc_eswitch/servalt_switch.c b/drivers/net/mscc_eswitch/servalt_switch.c index 995c62309d..d20ec49d56 100644 --- a/drivers/net/mscc_eswitch/servalt_switch.c +++ b/drivers/net/mscc_eswitch/servalt_switch.c @@ -16,18 +16,7 @@ #include <wait_bit.h> #include "mscc_xfer.h" - -#define GCB_MIIM_MII_STATUS 0x0 -#define GCB_MIIM_STAT_BUSY BIT(3) -#define GCB_MIIM_MII_CMD 0x8 -#define GCB_MIIM_MII_CMD_OPR_WRITE BIT(1) -#define GCB_MIIM_MII_CMD_OPR_READ BIT(2) -#define GCB_MIIM_MII_CMD_WRDATA(x) ((x) << 4) -#define GCB_MIIM_MII_CMD_REGAD(x) ((x) << 20) -#define GCB_MIIM_MII_CMD_PHYAD(x) ((x) << 25) -#define GCB_MIIM_MII_CMD_VLD BIT(31) -#define GCB_MIIM_DATA 0xC -#define GCB_MIIM_DATA_ERROR (0x3 << 16) +#include "mscc_miim.h" #define PHY_CFG 0x0 #define PHY_CFG_ENA 0x3 @@ -134,13 +123,6 @@ struct servalt_private { struct servalt_phy_port_t ports[MAX_PORT]; }; -struct mscc_miim_dev { - void __iomem *regs; - phys_addr_t miim_base; - unsigned long miim_size; - struct mii_dev *bus; -}; - static const unsigned long servalt_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, @@ -152,85 +134,6 @@ static const unsigned long servalt_regs_qs[] = { static struct mscc_miim_dev miim[SERVALT_MIIM_BUS_COUNT]; static int miim_count = -1; -static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) -{ - return wait_for_bit_le32(miim->regs + GCB_MIIM_MII_STATUS, - GCB_MIIM_STAT_BUSY, false, 250, false); -} - -static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - u32 val; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_OPR_READ, - miim->regs + GCB_MIIM_MII_CMD); - - ret = mscc_miim_wait_ready(miim); - if (ret) - goto out; - - val = readl(miim->regs + GCB_MIIM_DATA); - if (val & GCB_MIIM_DATA_ERROR) { - ret = -EIO; - goto out; - } - - ret = val & 0xFFFF; -out: - return ret; -} - -static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val) -{ - struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; - int ret; - - ret = mscc_miim_wait_ready(miim); - if (ret < 0) - goto out; - - writel(GCB_MIIM_MII_CMD_VLD | GCB_MIIM_MII_CMD_PHYAD(addr) | - GCB_MIIM_MII_CMD_REGAD(reg) | GCB_MIIM_MII_CMD_WRDATA(val) | - GCB_MIIM_MII_CMD_OPR_WRITE, miim->regs + GCB_MIIM_MII_CMD); - -out: - return ret; -} - -static struct mii_dev *servalt_mdiobus_init(phys_addr_t miim_base, - unsigned long miim_size) -{ - struct mii_dev *bus; - - bus = mdio_alloc(); - if (!bus) - return NULL; - - ++miim_count; - sprintf(bus->name, "miim-bus%d", miim_count); - - miim[miim_count].regs = ioremap(miim_base, miim_size); - miim[miim_count].miim_base = miim_base; - miim[miim_count].miim_size = miim_size; - bus->priv = &miim[miim_count]; - bus->read = mscc_miim_read; - bus->write = mscc_miim_write; - - if (mdio_register(bus)) - return NULL; - - miim[miim_count].bus = bus; - return bus; -} - static void mscc_phy_reset(void) { writel(0, BASE_DEVCPU_GCB + GCB_PHY_CFG + PHY_CFG); @@ -564,7 +467,8 @@ static int servalt_probe(struct udevice *dev) /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = - servalt_mdiobus_init(addr_base, addr_size); + mscc_mdiobus_init(miim, &miim_count, addr_base, + addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index a04a118f90..ec81dab3f6 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -176,8 +176,13 @@ void cs4340_upload_firmware(struct phy_device *phydev) printf("MMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); +#ifdef CONFIG_BLK + (void)blk_dread(mmc_get_blk_desc(mmc), blk, cnt, + addr); +#else (void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt, addr); +#endif } #endif diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index 25f1332ca9..7509936465 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -103,7 +103,7 @@ struct dp83867_private { int io_impedance; bool rxctrl_strap_quirk; int port_mirroring; - int clk_output_sel; + unsigned int clk_output_sel; }; static int dp83867_config_port_mirroring(struct phy_device *phydev) @@ -136,17 +136,11 @@ static int dp83867_of_init(struct phy_device *phydev) ofnode node; u16 val; - /* Optional configuration */ - node = phy_get_ofnode(phydev); if (!ofnode_valid(node)) return -EINVAL; - /* - * Keep the default value if ti,clk-output-sel is not set - * or to high - */ - + /* Keep the default value if ti,clk-output-sel is not set */ dp83867->clk_output_sel = ofnode_read_u32_default(node, "ti,clk-output-sel", DP83867_CLK_O_SEL_REF_CLK); @@ -162,14 +156,14 @@ static int dp83867_of_init(struct phy_device *phydev) dp83867->rxctrl_strap_quirk = true; dp83867->rx_id_delay = ofnode_read_u32_default(node, "ti,rx-internal-delay", - -1); + DEFAULT_RX_ID_DELAY); dp83867->tx_id_delay = ofnode_read_u32_default(node, "ti,tx-internal-delay", - -1); + DEFAULT_TX_ID_DELAY); dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth", - -1); + DEFAULT_FIFO_DEPTH); if (ofnode_read_bool(node, "enet-phy-lane-swap")) dp83867->port_mirroring = DP83867_PORT_MIRRORING_EN; diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig index 82bc9f5d03..ecf642de10 100644 --- a/drivers/net/ti/Kconfig +++ b/drivers/net/ti/Kconfig @@ -18,3 +18,11 @@ config DRIVER_TI_KEYSTONE_NET bool "TI Keystone 2 Ethernet" help This driver supports the TI Keystone 2 Ethernet subsystem + +config TI_AM65_CPSW_NUSS + bool "TI K3 AM65x MCU CPSW Nuss Ethernet controller driver" + depends on ARCH_K3 + select PHYLIB + help + This driver supports TI K3 MCU CPSW Nuss Ethernet controller + in Texas Instruments K3 AM65x SoCs. diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile index ee3e4eb5d6..8d3808bb4b 100644 --- a/drivers/net/ti/Makefile +++ b/drivers/net/ti/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o cpsw_mdio.o +obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o cpsw_mdio.o diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c new file mode 100644 index 0000000000..e11fbdeed3 --- /dev/null +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver + * + * Copyright (C) 2019, Texas Instruments, Incorporated + * + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <clk.h> +#include <dm.h> +#include <dm/lists.h> +#include <dma-uclass.h> +#include <dm/of_access.h> +#include <miiphy.h> +#include <net.h> +#include <phy.h> +#include <power-domain.h> +#include <linux/soc/ti/ti-udma.h> + +#include "cpsw_mdio.h" + +#define AM65_CPSW_CPSWNU_MAX_PORTS 2 + +#define AM65_CPSW_SS_BASE 0x0 +#define AM65_CPSW_SGMII_BASE 0x100 +#define AM65_CPSW_MDIO_BASE 0xf00 +#define AM65_CPSW_XGMII_BASE 0x2100 +#define AM65_CPSW_CPSW_NU_BASE 0x20000 +#define AM65_CPSW_CPSW_NU_ALE_BASE 0x1e000 + +#define AM65_CPSW_CPSW_NU_PORTS_OFFSET 0x1000 +#define AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET 0x330 + +#define AM65_CPSW_MDIO_BUS_FREQ_DEF 1000000 + +#define AM65_CPSW_CTL_REG 0x4 +#define AM65_CPSW_STAT_PORT_EN_REG 0x14 +#define AM65_CPSW_PTYPE_REG 0x18 + +#define AM65_CPSW_CTL_REG_P0_ENABLE BIT(2) +#define AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE BIT(13) +#define AM65_CPSW_CTL_REG_P0_RX_PAD BIT(14) + +#define AM65_CPSW_P0_FLOW_ID_REG 0x8 +#define AM65_CPSW_PN_RX_MAXLEN_REG 0x24 +#define AM65_CPSW_PN_REG_SA_L 0x308 +#define AM65_CPSW_PN_REG_SA_H 0x30c + +#define AM65_CPSW_ALE_CTL_REG 0x8 +#define AM65_CPSW_ALE_CTL_REG_ENABLE BIT(31) +#define AM65_CPSW_ALE_CTL_REG_RESET_TBL BIT(30) +#define AM65_CPSW_ALE_CTL_REG_BYPASS BIT(4) +#define AM65_CPSW_ALE_PN_CTL_REG(x) (0x40 + (x) * 4) +#define AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD 0x3 +#define AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY BIT(11) + +#define AM65_CPSW_MACSL_CTL_REG 0x0 +#define AM65_CPSW_MACSL_CTL_REG_IFCTL_A BIT(15) +#define AM65_CPSW_MACSL_CTL_REG_GIG BIT(7) +#define AM65_CPSW_MACSL_CTL_REG_GMII_EN BIT(5) +#define AM65_CPSW_MACSL_CTL_REG_LOOPBACK BIT(1) +#define AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX BIT(0) +#define AM65_CPSW_MACSL_RESET_REG 0x8 +#define AM65_CPSW_MACSL_RESET_REG_RESET BIT(0) +#define AM65_CPSW_MACSL_STATUS_REG 0x4 +#define AM65_CPSW_MACSL_RESET_REG_PN_IDLE BIT(31) +#define AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE BIT(30) +#define AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE BIT(29) +#define AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE BIT(28) +#define AM65_CPSW_MACSL_RESET_REG_IDLE_MASK \ + (AM65_CPSW_MACSL_RESET_REG_PN_IDLE | \ + AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE | \ + AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE | \ + AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE) + +#define AM65_CPSW_CPPI_PKT_TYPE 0x7 + +struct am65_cpsw_port { + fdt_addr_t port_base; + fdt_addr_t macsl_base; + bool disabled; + u32 mac_control; +}; + +struct am65_cpsw_common { + struct udevice *dev; + fdt_addr_t ss_base; + fdt_addr_t cpsw_base; + fdt_addr_t mdio_base; + fdt_addr_t ale_base; + fdt_addr_t gmii_sel; + fdt_addr_t mac_efuse; + + struct clk fclk; + struct power_domain pwrdmn; + + u32 port_num; + struct am65_cpsw_port ports[AM65_CPSW_CPSWNU_MAX_PORTS]; + u32 rflow_id_base; + + struct mii_dev *bus; + u32 bus_freq; + + struct dma dma_tx; + struct dma dma_rx; + u32 rx_next; + u32 rx_pend; + bool started; +}; + +struct am65_cpsw_priv { + struct udevice *dev; + struct am65_cpsw_common *cpsw_common; + u32 port_id; + + struct phy_device *phydev; + bool has_phy; + ofnode phy_node; + u32 phy_addr; +}; + +#ifdef PKTSIZE_ALIGN +#define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN +#else +#define UDMA_RX_BUF_SIZE ALIGN(1522, ARCH_DMA_MINALIGN) +#endif + +#ifdef PKTBUFSRX +#define UDMA_RX_DESC_NUM PKTBUFSRX +#else +#define UDMA_RX_DESC_NUM 4 +#endif + +#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ + ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) + +static void am65_cpsw_set_sl_mac(struct am65_cpsw_port *slave, + unsigned char *addr) +{ + writel(mac_hi(addr), + slave->port_base + AM65_CPSW_PN_REG_SA_H); + writel(mac_lo(addr), + slave->port_base + AM65_CPSW_PN_REG_SA_L); +} + +int am65_cpsw_macsl_reset(struct am65_cpsw_port *slave) +{ + u32 i = 100; + + /* Set the soft reset bit */ + writel(AM65_CPSW_MACSL_RESET_REG_RESET, + slave->macsl_base + AM65_CPSW_MACSL_RESET_REG); + + while ((readl(slave->macsl_base + AM65_CPSW_MACSL_RESET_REG) & + AM65_CPSW_MACSL_RESET_REG_RESET) && i--) + cpu_relax(); + + /* Timeout on the reset */ + return i; +} + +static int am65_cpsw_macsl_wait_for_idle(struct am65_cpsw_port *slave) +{ + u32 i = 100; + + while ((readl(slave->macsl_base + AM65_CPSW_MACSL_STATUS_REG) & + AM65_CPSW_MACSL_RESET_REG_IDLE_MASK) && i--) + cpu_relax(); + + return i; +} + +static int am65_cpsw_update_link(struct am65_cpsw_priv *priv) +{ + struct am65_cpsw_common *common = priv->cpsw_common; + struct am65_cpsw_port *port = &common->ports[priv->port_id]; + struct phy_device *phy = priv->phydev; + u32 mac_control = 0; + + if (phy->link) { /* link up */ + mac_control = /*AM65_CPSW_MACSL_CTL_REG_LOOPBACK |*/ + AM65_CPSW_MACSL_CTL_REG_GMII_EN; + if (phy->speed == 1000) + mac_control |= AM65_CPSW_MACSL_CTL_REG_GIG; + if (phy->duplex == DUPLEX_FULL) + mac_control |= AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX; + if (phy->speed == 100) + mac_control |= AM65_CPSW_MACSL_CTL_REG_IFCTL_A; + } + + if (mac_control == port->mac_control) + goto out; + + if (mac_control) { + printf("link up on port %d, speed %d, %s duplex\n", + priv->port_id, phy->speed, + (phy->duplex == DUPLEX_FULL) ? "full" : "half"); + } else { + printf("link down on port %d\n", priv->port_id); + } + + writel(mac_control, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); + port->mac_control = mac_control; + +out: + return phy->link; +} + +#define AM65_GMII_SEL_MODE_MII 0 +#define AM65_GMII_SEL_MODE_RMII 1 +#define AM65_GMII_SEL_MODE_RGMII 2 + +#define AM65_GMII_SEL_RGMII_IDMODE BIT(4) + +static void am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv, + phy_interface_t phy_mode, int slave) +{ + struct am65_cpsw_common *common = priv->cpsw_common; + u32 reg; + u32 mode = 0; + bool rgmii_id = false; + + reg = readl(common->gmii_sel); + + dev_dbg(common->dev, "old gmii_sel: %08x\n", reg); + + switch (phy_mode) { + case PHY_INTERFACE_MODE_RMII: + mode = AM65_GMII_SEL_MODE_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + mode = AM65_GMII_SEL_MODE_RGMII; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + mode = AM65_GMII_SEL_MODE_RGMII; + rgmii_id = true; + break; + + default: + dev_warn(common->dev, + "Unsupported PHY mode: %u. Defaulting to MII.\n", + phy_mode); + /* fallthrough */ + case PHY_INTERFACE_MODE_MII: + mode = AM65_GMII_SEL_MODE_MII; + break; + }; + + if (rgmii_id) + mode |= AM65_GMII_SEL_RGMII_IDMODE; + + reg = mode; + dev_dbg(common->dev, "gmii_sel PHY mode: %u, new gmii_sel: %08x\n", + phy_mode, reg); + writel(reg, common->gmii_sel); + + reg = readl(common->gmii_sel); + if (reg != mode) + dev_err(common->dev, + "gmii_sel PHY mode NOT SET!: requested: %08x, gmii_sel: %08x\n", + mode, reg); +} + +static int am65_cpsw_start(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct am65_cpsw_port *port = &common->ports[priv->port_id]; + struct am65_cpsw_port *port0 = &common->ports[0]; + int ret, i; + + ret = power_domain_on(&common->pwrdmn); + if (ret) { + dev_err(dev, "power_domain_on() failed %d\n", ret); + goto out; + } + + ret = clk_enable(&common->fclk); + if (ret) { + dev_err(dev, "clk enabled failed %d\n", ret); + goto err_off_pwrdm; + } + + common->rx_next = 0; + common->rx_pend = 0; + ret = dma_get_by_name(common->dev, "tx0", &common->dma_tx); + if (ret) { + dev_err(dev, "TX dma get failed %d\n", ret); + goto err_off_clk; + } + ret = dma_get_by_name(common->dev, "rx", &common->dma_rx); + if (ret) { + dev_err(dev, "RX dma get failed %d\n", ret); + goto err_free_tx; + } + + for (i = 0; i < UDMA_RX_DESC_NUM; i++) { + ret = dma_prepare_rcv_buf(&common->dma_rx, + net_rx_packets[i], + UDMA_RX_BUF_SIZE); + if (ret) { + dev_err(dev, "RX dma add buf failed %d\n", ret); + goto err_free_tx; + } + } + + ret = dma_enable(&common->dma_tx); + if (ret) { + dev_err(dev, "TX dma_enable failed %d\n", ret); + goto err_free_rx; + } + ret = dma_enable(&common->dma_rx); + if (ret) { + dev_err(dev, "RX dma_enable failed %d\n", ret); + goto err_dis_tx; + } + + /* Control register */ + writel(AM65_CPSW_CTL_REG_P0_ENABLE | + AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE | + AM65_CPSW_CTL_REG_P0_RX_PAD, + common->cpsw_base + AM65_CPSW_CTL_REG); + + /* disable priority elevation */ + writel(0, common->cpsw_base + AM65_CPSW_PTYPE_REG); + + /* enable statistics */ + writel(BIT(0) | BIT(priv->port_id), + common->cpsw_base + AM65_CPSW_STAT_PORT_EN_REG); + + /* Port 0 length register */ + writel(PKTSIZE_ALIGN, port0->port_base + AM65_CPSW_PN_RX_MAXLEN_REG); + + /* set base flow_id */ + writel(common->rflow_id_base, + port0->port_base + AM65_CPSW_P0_FLOW_ID_REG); + + /* Reset and enable the ALE */ + writel(AM65_CPSW_ALE_CTL_REG_ENABLE | AM65_CPSW_ALE_CTL_REG_RESET_TBL | + AM65_CPSW_ALE_CTL_REG_BYPASS, + common->ale_base + AM65_CPSW_ALE_CTL_REG); + + /* port 0 put into forward mode */ + writel(AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD, + common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); + + /* PORT x configuration */ + + /* Port x Max length register */ + writel(PKTSIZE_ALIGN, port->port_base + AM65_CPSW_PN_RX_MAXLEN_REG); + + /* Port x set mac */ + am65_cpsw_set_sl_mac(port, pdata->enetaddr); + + /* Port x ALE: mac_only, Forwarding */ + writel(AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY | + AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD, + common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); + + port->mac_control = 0; + if (!am65_cpsw_macsl_reset(port)) { + dev_err(dev, "mac_sl reset failed\n"); + ret = -EFAULT; + goto err_dis_rx; + } + + ret = phy_startup(priv->phydev); + if (ret) { + dev_err(dev, "phy_startup failed\n"); + goto err_dis_rx; + } + + ret = am65_cpsw_update_link(priv); + if (!ret) { + ret = -ENODEV; + goto err_phy_shutdown; + } + + common->started = true; + + return 0; + +err_phy_shutdown: + phy_shutdown(priv->phydev); +err_dis_rx: + /* disable ports */ + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); + if (!am65_cpsw_macsl_wait_for_idle(port)) + dev_err(dev, "mac_sl idle timeout\n"); + writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); + writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG); + writel(0, common->cpsw_base + AM65_CPSW_CTL_REG); + + dma_disable(&common->dma_rx); +err_dis_tx: + dma_disable(&common->dma_tx); +err_free_rx: + dma_free(&common->dma_rx); +err_free_tx: + dma_free(&common->dma_tx); +err_off_clk: + clk_disable(&common->fclk); +err_off_pwrdm: + power_domain_off(&common->pwrdmn); +out: + dev_err(dev, "%s end error\n", __func__); + + return ret; +} + +static int am65_cpsw_send(struct udevice *dev, void *packet, int length) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct ti_udma_drv_packet_data packet_data; + int ret; + + packet_data.pkt_type = AM65_CPSW_CPPI_PKT_TYPE; + packet_data.dest_tag = priv->port_id; + ret = dma_send(&common->dma_tx, packet, length, &packet_data); + if (ret) { + dev_err(dev, "TX dma_send failed %d\n", ret); + return ret; + } + + return 0; +} + +static int am65_cpsw_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + + /* try to receive a new packet */ + return dma_receive(&common->dma_rx, (void **)packetp, NULL); +} + +static int am65_cpsw_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + int ret; + + if (length > 0) { + u32 pkt = common->rx_next % UDMA_RX_DESC_NUM; + + ret = dma_prepare_rcv_buf(&common->dma_rx, + net_rx_packets[pkt], + UDMA_RX_BUF_SIZE); + if (ret) + dev_err(dev, "RX dma free_pkt failed %d\n", ret); + common->rx_next++; + } + + return 0; +} + +static void am65_cpsw_stop(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct am65_cpsw_port *port = &common->ports[priv->port_id]; + + if (!common->started) + return; + + phy_shutdown(priv->phydev); + + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); + writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); + if (!am65_cpsw_macsl_wait_for_idle(port)) + dev_err(dev, "mac_sl idle timeout\n"); + writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); + writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG); + writel(0, common->cpsw_base + AM65_CPSW_CTL_REG); + + dma_disable(&common->dma_tx); + dma_free(&common->dma_tx); + + dma_disable(&common->dma_rx); + dma_free(&common->dma_rx); + + common->started = false; +} + +static int am65_cpsw_read_rom_hwaddr(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *common = priv->cpsw_common; + struct eth_pdata *pdata = dev_get_platdata(dev); + u32 mac_hi, mac_lo; + + if (common->mac_efuse == FDT_ADDR_T_NONE) + return -1; + + mac_lo = readl(common->mac_efuse); + mac_hi = readl(common->mac_efuse + 4); + pdata->enetaddr[0] = (mac_hi >> 8) & 0xff; + pdata->enetaddr[1] = mac_hi & 0xff; + pdata->enetaddr[2] = (mac_lo >> 24) & 0xff; + pdata->enetaddr[3] = (mac_lo >> 16) & 0xff; + pdata->enetaddr[4] = (mac_lo >> 8) & 0xff; + pdata->enetaddr[5] = mac_lo & 0xff; + + return 0; +} + +static const struct eth_ops am65_cpsw_ops = { + .start = am65_cpsw_start, + .send = am65_cpsw_send, + .recv = am65_cpsw_recv, + .free_pkt = am65_cpsw_free_pkt, + .stop = am65_cpsw_stop, + .read_rom_hwaddr = am65_cpsw_read_rom_hwaddr, +}; + +static int am65_cpsw_mdio_init(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *cpsw_common = priv->cpsw_common; + + if (!priv->has_phy || cpsw_common->bus) + return 0; + + cpsw_common->bus = cpsw_mdio_init(dev->name, + cpsw_common->mdio_base, + cpsw_common->bus_freq, + clk_get_rate(&cpsw_common->fclk)); + if (!cpsw_common->bus) + return -EFAULT; + + return 0; +} + +static int am65_cpsw_phy_init(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct am65_cpsw_common *cpsw_common = priv->cpsw_common; + struct eth_pdata *pdata = dev_get_platdata(dev); + struct phy_device *phydev; + u32 supported = PHY_GBIT_FEATURES; + int ret; + + phydev = phy_connect(cpsw_common->bus, + priv->phy_addr, + priv->dev, + pdata->phy_interface); + + if (!phydev) { + dev_err(dev, "phy_connect() failed\n"); + return -ENODEV; + } + + phydev->supported &= supported; + if (pdata->max_speed) { + ret = phy_set_supported(phydev, pdata->max_speed); + if (ret) + return ret; + } + phydev->advertising = phydev->supported; + + if (ofnode_valid(priv->phy_node)) + phydev->node = priv->phy_node; + + priv->phydev = phydev; + ret = phy_config(phydev); + if (ret < 0) + pr_err("phy_config() failed: %d", ret); + + return ret; +} + +static int am65_cpsw_ofdata_parse_phy(struct udevice *dev, ofnode port_np) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args out_args; + const char *phy_mode; + int ret = 0; + + phy_mode = ofnode_read_string(port_np, "phy-mode"); + if (phy_mode) { + pdata->phy_interface = + phy_get_interface_by_name(phy_mode); + if (pdata->phy_interface == -1) { + dev_err(dev, "Invalid PHY mode '%s', port %u\n", + phy_mode, priv->port_id); + ret = -EINVAL; + goto out; + } + } + + ofnode_read_u32(port_np, "max-speed", (u32 *)&pdata->max_speed); + if (pdata->max_speed) + dev_err(dev, "Port %u speed froced to %uMbit\n", + priv->port_id, pdata->max_speed); + + priv->has_phy = true; + ret = ofnode_parse_phandle_with_args(port_np, "phy-handle", + NULL, 0, 0, &out_args); + if (ret) { + dev_err(dev, "can't parse phy-handle port %u (%d)\n", + priv->port_id, ret); + priv->has_phy = false; + ret = 0; + } + + priv->phy_node = out_args.node; + if (priv->has_phy) { + ret = ofnode_read_u32(priv->phy_node, "reg", &priv->phy_addr); + if (ret) { + dev_err(dev, "failed to get phy_addr port %u (%d)\n", + priv->port_id, ret); + goto out; + } + } + +out: + return ret; +} + +static int am65_cpsw_probe_cpsw(struct udevice *dev) +{ + struct am65_cpsw_priv *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + struct am65_cpsw_common *cpsw_common; + ofnode ports_np, node; + int ret, i; + + priv->dev = dev; + + cpsw_common = calloc(1, sizeof(*priv->cpsw_common)); + if (!cpsw_common) + return -ENOMEM; + priv->cpsw_common = cpsw_common; + + cpsw_common->dev = dev; + cpsw_common->ss_base = dev_read_addr(dev); + if (cpsw_common->ss_base == FDT_ADDR_T_NONE) + return -EINVAL; + cpsw_common->mac_efuse = devfdt_get_addr_name(dev, "mac_efuse"); + /* no err check - optional */ + + ret = power_domain_get_by_index(dev, &cpsw_common->pwrdmn, 0); + if (ret) { + dev_err(dev, "failed to get pwrdmn: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "fck", &cpsw_common->fclk); + if (ret) { + power_domain_free(&cpsw_common->pwrdmn); + dev_err(dev, "failed to get clock %d\n", ret); + return ret; + } + + cpsw_common->cpsw_base = cpsw_common->ss_base + AM65_CPSW_CPSW_NU_BASE; + cpsw_common->ale_base = cpsw_common->cpsw_base + + AM65_CPSW_CPSW_NU_ALE_BASE; + cpsw_common->mdio_base = cpsw_common->ss_base + AM65_CPSW_MDIO_BASE; + + cpsw_common->rflow_id_base = 0; + cpsw_common->rflow_id_base = + dev_read_u32_default(dev, "ti,rx-flow-id-base", + cpsw_common->rflow_id_base); + + ports_np = dev_read_subnode(dev, "ports"); + if (!ofnode_valid(ports_np)) { + ret = -ENOENT; + goto out; + } + + ofnode_for_each_subnode(node, ports_np) { + const char *node_name; + u32 port_id; + bool disabled; + + node_name = ofnode_get_name(node); + + disabled = !ofnode_is_available(node); + + ret = ofnode_read_u32(node, "reg", &port_id); + if (ret) { + dev_err(dev, "%s: failed to get port_id (%d)\n", + node_name, ret); + goto out; + } + + if (port_id >= AM65_CPSW_CPSWNU_MAX_PORTS) { + dev_err(dev, "%s: invalid port_id (%d)\n", + node_name, port_id); + ret = -EINVAL; + goto out; + } + cpsw_common->port_num++; + + if (!port_id) + continue; + + priv->port_id = port_id; + cpsw_common->ports[port_id].disabled = disabled; + if (disabled) + continue; + + ret = am65_cpsw_ofdata_parse_phy(dev, node); + if (ret) + goto out; + } + + for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) { + struct am65_cpsw_port *port = &cpsw_common->ports[i]; + + port->port_base = cpsw_common->cpsw_base + + AM65_CPSW_CPSW_NU_PORTS_OFFSET + + (i * AM65_CPSW_CPSW_NU_PORTS_OFFSET); + port->macsl_base = port->port_base + + AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET; + } + + node = dev_read_subnode(dev, "cpsw-phy-sel"); + if (!ofnode_valid(node)) { + dev_err(dev, "can't find cpsw-phy-sel\n"); + ret = -ENOENT; + goto out; + } + + cpsw_common->gmii_sel = ofnode_get_addr(node); + if (cpsw_common->gmii_sel == FDT_ADDR_T_NONE) { + dev_err(dev, "failed to get gmii_sel base\n"); + goto out; + } + + node = dev_read_subnode(dev, "mdio"); + if (!ofnode_valid(node)) { + dev_err(dev, "can't find mdio\n"); + ret = -ENOENT; + goto out; + } + + cpsw_common->bus_freq = + dev_read_u32_default(dev, "bus_freq", + AM65_CPSW_MDIO_BUS_FREQ_DEF); + + am65_cpsw_gmii_sel_k3(priv, pdata->phy_interface, priv->port_id); + + ret = am65_cpsw_mdio_init(dev); + if (ret) + goto out; + + ret = am65_cpsw_phy_init(dev); + if (ret) + goto out; + + dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X Ports:%u rflow_id_base:%u mdio_freq:%u\n", + readl(cpsw_common->ss_base), + readl(cpsw_common->cpsw_base), + readl(cpsw_common->ale_base), + cpsw_common->port_num, + cpsw_common->rflow_id_base, + cpsw_common->bus_freq); + +out: + clk_free(&cpsw_common->fclk); + power_domain_free(&cpsw_common->pwrdmn); + return ret; +} + +static const struct udevice_id am65_cpsw_nuss_ids[] = { + { .compatible = "ti,am654-cpsw-nuss" }, + { } +}; + +U_BOOT_DRIVER(am65_cpsw_nuss_slave) = { + .name = "am65_cpsw_nuss_slave", + .id = UCLASS_ETH, + .of_match = am65_cpsw_nuss_ids, + .probe = am65_cpsw_probe_cpsw, + .ops = &am65_cpsw_ops, + .priv_auto_alloc_size = sizeof(struct am65_cpsw_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/net/ti/cpsw_mdio.c b/drivers/net/ti/cpsw_mdio.c index 70f547e6d7..6e8f652011 100644 --- a/drivers/net/ti/cpsw_mdio.c +++ b/drivers/net/ti/cpsw_mdio.c @@ -125,7 +125,7 @@ u32 cpsw_mdio_get_alive(struct mii_dev *bus) return val & GENMASK(15, 0); } -struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base, +struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base, u32 bus_freq, int fck_freq) { struct cpsw_mdio *cpsw_mdio; @@ -144,7 +144,7 @@ struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base, return NULL; } - cpsw_mdio->regs = (struct cpsw_mdio_regs *)mdio_base; + cpsw_mdio->regs = (struct cpsw_mdio_regs *)(uintptr_t)mdio_base; if (!bus_freq || !fck_freq) cpsw_mdio->div = CPSW_MDIO_DIV_DEF; diff --git a/drivers/net/ti/cpsw_mdio.h b/drivers/net/ti/cpsw_mdio.h index 4a76d4e5c5..dbf4a2dcac 100644 --- a/drivers/net/ti/cpsw_mdio.h +++ b/drivers/net/ti/cpsw_mdio.h @@ -10,7 +10,7 @@ struct cpsw_mdio; -struct mii_dev *cpsw_mdio_init(const char *name, u32 mdio_base, +struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base, u32 bus_freq, int fck_freq); void cpsw_mdio_free(struct mii_dev *bus); u32 cpsw_mdio_get_alive(struct mii_dev *bus); diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 5056a084d2..f9300a64ce 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -58,6 +58,7 @@ enum uclass_id { UCLASS_LPC, /* x86 'low pin count' interface */ UCLASS_MAILBOX, /* Mailbox controller */ UCLASS_MASS_STORAGE, /* Mass storage device */ + UCLASS_MDIO, /* MDIO bus */ UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ diff --git a/include/miiphy.h b/include/miiphy.h index f11763affd..e6dd441983 100644 --- a/include/miiphy.h +++ b/include/miiphy.h @@ -118,4 +118,53 @@ int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg, #define ESTATUS_1000XF 0x8000 #define ESTATUS_1000XH 0x4000 +#ifdef CONFIG_DM_MDIO + +/** + * struct mdio_perdev_priv - Per-device class data for MDIO DM + * + * @mii_bus: Supporting MII legacy bus + */ +struct mdio_perdev_priv { + struct mii_dev *mii_bus; +}; + +/** + * struct mdio_ops - MDIO bus operations + * + * @read: Read from a PHY register + * @write: Write to a PHY register + * @reset: Reset the MDIO bus, NULL if not supported + */ +struct mdio_ops { + int (*read)(struct udevice *mdio_dev, int addr, int devad, int reg); + int (*write)(struct udevice *mdio_dev, int addr, int devad, int reg, + u16 val); + int (*reset)(struct udevice *mdio_dev); +}; + +#define mdio_get_ops(dev) ((struct mdio_ops *)(dev)->driver->ops) + +/** + * dm_mdio_probe_devices - Call probe on all MII devices, currently used for + * MDIO console commands. + */ +void dm_mdio_probe_devices(void); + +/** + * dm_mdio_phy_connect - Wrapper over phy_connect for DM MDIO + * + * @dev: mdio dev + * @addr: PHY address on MDIO bus + * @ethdev: ethernet device to connect to the PHY + * @interface: MAC-PHY protocol + * + * @return pointer to phy_device, or 0 on error + */ +struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr, + struct udevice *ethdev, + phy_interface_t interface); + +#endif + #endif diff --git a/net/Makefile b/net/Makefile index ce36362168..6251ff3991 100644 --- a/net/Makefile +++ b/net/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_NET) += eth-uclass.o else obj-$(CONFIG_NET) += eth_legacy.o endif +obj-$(CONFIG_DM_MDIO) += mdio-uclass.o obj-$(CONFIG_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_NET) += net.o diff --git a/net/mdio-uclass.c b/net/mdio-uclass.c new file mode 100644 index 0000000000..36a404ff44 --- /dev/null +++ b/net/mdio-uclass.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <common.h> +#include <dm.h> +#include <miiphy.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +void dm_mdio_probe_devices(void) +{ + struct udevice *it; + struct uclass *uc; + + uclass_get(UCLASS_MDIO, &uc); + uclass_foreach_dev(it, uc) { + device_probe(it); + } +} + +static int dm_mdio_post_bind(struct udevice *dev) +{ + /* + * MDIO command doesn't like spaces in names, don't allow them to keep + * it happy + */ + if (strchr(dev->name, ' ')) { + debug("\nError: MDIO device name \"%s\" has a space!\n", + dev->name); + return -EINVAL; + } + + return 0; +} + +/* + * Following read/write/reset functions are registered with legacy MII code. + * These are called for PHY operations by upper layers and we further call the + * DM MDIO driver functions. + */ +static int mdio_read(struct mii_dev *mii_bus, int addr, int devad, int reg) +{ + struct udevice *dev = mii_bus->priv; + + return mdio_get_ops(dev)->read(dev, addr, devad, reg); +} + +static int mdio_write(struct mii_dev *mii_bus, int addr, int devad, int reg, + u16 val) +{ + struct udevice *dev = mii_bus->priv; + + return mdio_get_ops(dev)->write(dev, addr, devad, reg, val); +} + +static int mdio_reset(struct mii_dev *mii_bus) +{ + struct udevice *dev = mii_bus->priv; + + if (mdio_get_ops(dev)->reset) + return mdio_get_ops(dev)->reset(dev); + else + return 0; +} + +static int dm_mdio_post_probe(struct udevice *dev) +{ + struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev); + + pdata->mii_bus = mdio_alloc(); + pdata->mii_bus->read = mdio_read; + pdata->mii_bus->write = mdio_write; + pdata->mii_bus->reset = mdio_reset; + pdata->mii_bus->priv = dev; + strncpy(pdata->mii_bus->name, dev->name, MDIO_NAME_LEN); + + return mdio_register(pdata->mii_bus); +} + +static int dm_mdio_pre_remove(struct udevice *dev) +{ + struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev); + struct mdio_ops *ops = mdio_get_ops(dev); + + if (ops->reset) + ops->reset(dev); + mdio_unregister(pdata->mii_bus); + mdio_free(pdata->mii_bus); + + return 0; +} + +struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr, + struct udevice *ethdev, + phy_interface_t interface) +{ + struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev); + + if (device_probe(dev)) + return 0; + + return phy_connect(pdata->mii_bus, addr, ethdev, interface); +} + +UCLASS_DRIVER(mdio) = { + .id = UCLASS_MDIO, + .name = "mdio", + .post_bind = dm_mdio_post_bind, + .post_probe = dm_mdio_post_probe, + .pre_remove = dm_mdio_pre_remove, + .per_device_auto_alloc_size = sizeof(struct mdio_perdev_priv), +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index 420bf81154..6a36cc0a32 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -62,4 +62,5 @@ obj-$(CONFIG_SOUND) += sound.o obj-$(CONFIG_TEE) += tee.o obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o obj-$(CONFIG_DMA) += dma.o +obj-$(CONFIG_DM_MDIO) += mdio.o endif diff --git a/test/dm/mdio.c b/test/dm/mdio.c new file mode 100644 index 0000000000..5b66255f7d --- /dev/null +++ b/test/dm/mdio.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <misc.h> +#include <test/ut.h> +#include <miiphy.h> + +/* macros copied over from mdio_sandbox.c */ +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG 0 + +#define TEST_REG_VALUE 0xabcd + +static int dm_test_mdio(struct unit_test_state *uts) +{ + struct uclass *uc; + struct udevice *dev; + struct mdio_ops *ops; + u16 reg; + + ut_assertok(uclass_get(UCLASS_MDIO, &uc)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &dev)); + + ops = mdio_get_ops(dev); + ut_assertnonnull(ops); + ut_assertnonnull(ops->read); + ut_assertnonnull(ops->write); + + ut_assertok(ops->write(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG, TEST_REG_VALUE)); + reg = ops->read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG); + ut_asserteq(reg, TEST_REG_VALUE); + + ut_assert(ops->read(dev, SANDBOX_PHY_ADDR + 1, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG) != 0); + + ut_assertok(ops->reset(dev)); + reg = ops->read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG); + ut_asserteq(reg, 0); + + return 0; +} + +DM_TEST(dm_test_mdio, DM_TESTF_SCAN_FDT); |