/* * Copyright 2015 Freescale Semiconductor, Inc. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include "../common/qixis.h" #include "ls2085aqds_qixis.h" #ifdef CONFIG_FSL_MC_ENET /* - In LS2085A there are only 16 SERDES lanes, spread across 2 SERDES banks. * Bank 1 -> Lanes A, B, C, D, E, F, G, H * Bank 2 -> Lanes A,B, C, D, E, F, G, H */ /* Mapping of 16 SERDES lanes to LS2085A QDS board slots. A value of '0' here * means that the mapping must be determined dynamically, or that the lane * maps to something other than a board slot. */ static u8 lane_to_slot_fsm2[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* On the Vitesse VSC8234XHG SGMII riser card there are 4 SGMII PHYs * housed. */ static int riser_phy_addr[] = { SGMII_CARD_PORT1_PHY_ADDR, SGMII_CARD_PORT2_PHY_ADDR, SGMII_CARD_PORT3_PHY_ADDR, SGMII_CARD_PORT4_PHY_ADDR, }; /* Slot2 does not have EMI connections */ #define EMI_NONE 0xFFFFFFFF #define EMI1_SLOT1 0 #define EMI1_SLOT2 1 #define EMI1_SLOT3 2 #define EMI1_SLOT4 3 #define EMI1_SLOT5 4 #define EMI1_SLOT6 5 #define EMI2 6 #define SFP_TX 0 static const char * const mdio_names[] = { "LS2085A_QDS_MDIO0", "LS2085A_QDS_MDIO1", "LS2085A_QDS_MDIO2", "LS2085A_QDS_MDIO3", "LS2085A_QDS_MDIO4", "LS2085A_QDS_MDIO5", DEFAULT_WRIOP_MDIO2_NAME, }; struct ls2085a_qds_mdio { u8 muxval; struct mii_dev *realbus; }; static const char *ls2085a_qds_mdio_name_for_muxval(u8 muxval) { return mdio_names[muxval]; } struct mii_dev *mii_dev_for_muxval(u8 muxval) { struct mii_dev *bus; const char *name = ls2085a_qds_mdio_name_for_muxval(muxval); if (!name) { printf("No bus for muxval %x\n", muxval); return NULL; } bus = miiphy_get_dev_by_name(name); if (!bus) { printf("No bus by name %s\n", name); return NULL; } return bus; } static void ls2085a_qds_enable_SFP_TX(u8 muxval) { u8 brdcfg9; brdcfg9 = QIXIS_READ(brdcfg[9]); brdcfg9 &= ~BRDCFG9_SFPTX_MASK; brdcfg9 |= (muxval << BRDCFG9_SFPTX_SHIFT); QIXIS_WRITE(brdcfg[9], brdcfg9); } static void ls2085a_qds_mux_mdio(u8 muxval) { u8 brdcfg4; if (muxval <= 5) { brdcfg4 = QIXIS_READ(brdcfg[4]); brdcfg4 &= ~BRDCFG4_EMISEL_MASK; brdcfg4 |= (muxval << BRDCFG4_EMISEL_SHIFT); QIXIS_WRITE(brdcfg[4], brdcfg4); } } static int ls2085a_qds_mdio_read(struct mii_dev *bus, int addr, int devad, int regnum) { struct ls2085a_qds_mdio *priv = bus->priv; ls2085a_qds_mux_mdio(priv->muxval); return priv->realbus->read(priv->realbus, addr, devad, regnum); } static int ls2085a_qds_mdio_write(struct mii_dev *bus, int addr, int devad, int regnum, u16 value) { struct ls2085a_qds_mdio *priv = bus->priv; ls2085a_qds_mux_mdio(priv->muxval); return priv->realbus->write(priv->realbus, addr, devad, regnum, value); } static int ls2085a_qds_mdio_reset(struct mii_dev *bus) { struct ls2085a_qds_mdio *priv = bus->priv; return priv->realbus->reset(priv->realbus); } static int ls2085a_qds_mdio_init(char *realbusname, u8 muxval) { struct ls2085a_qds_mdio *pmdio; struct mii_dev *bus = mdio_alloc(); if (!bus) { printf("Failed to allocate ls2085a_qds MDIO bus\n"); return -1; } pmdio = malloc(sizeof(*pmdio)); if (!pmdio) { printf("Failed to allocate ls2085a_qds private data\n"); free(bus); return -1; } bus->read = ls2085a_qds_mdio_read; bus->write = ls2085a_qds_mdio_write; bus->reset = ls2085a_qds_mdio_reset; sprintf(bus->name, ls2085a_qds_mdio_name_for_muxval(muxval)); pmdio->realbus = miiphy_get_dev_by_name(realbusname); if (!pmdio->realbus) { printf("No bus with name %s\n", realbusname); free(bus); free(pmdio); return -1; } pmdio->muxval = muxval; bus->priv = pmdio; return mdio_register(bus); } /* * Initialize the dpmac_info array. * */ static void initialize_dpmac_to_slot(void) { struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR; int serdes1_prtcl = (in_le32(&gur->rcwsr[28]) & FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_MASK) >> FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_SHIFT; int serdes2_prtcl = (in_le32(&gur->rcwsr[28]) & FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_MASK) >> FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_SHIFT; switch (serdes1_prtcl) { case 0x2A: printf("qds: WRIOP: Supported SerDes Protocol 0x%02x\n", serdes1_prtcl); break; default: printf("qds: WRIOP: Unsupported SerDes Protocol 0x%02x\n", serdes1_prtcl); break; } switch (serdes2_prtcl) { case 0x07: case 0x08: case 0x49: printf("qds: WRIOP: Supported SerDes Protocol 0x%02x\n", serdes2_prtcl); lane_to_slot_fsm2[0] = EMI1_SLOT4; lane_to_slot_fsm2[1] = EMI1_SLOT4; lane_to_slot_fsm2[2] = EMI1_SLOT4; lane_to_slot_fsm2[3] = EMI1_SLOT4; /* No MDIO physical connection */ lane_to_slot_fsm2[4] = EMI1_SLOT6; lane_to_slot_fsm2[5] = EMI1_SLOT6; lane_to_slot_fsm2[6] = EMI1_SLOT6; lane_to_slot_fsm2[7] = EMI1_SLOT6; break; default: printf("qds: WRIOP: Unsupported SerDes Protocol 0x%02x\n", serdes2_prtcl); break; } } void ls2085a_handle_phy_interface_sgmii(int dpmac_id) { int lane, slot; struct mii_dev *bus; struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR; int serdes1_prtcl = (in_le32(&gur->rcwsr[28]) & FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_MASK) >> FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_SHIFT; int serdes2_prtcl = (in_le32(&gur->rcwsr[28]) & FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_MASK) >> FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_SHIFT; switch (serdes1_prtcl) { } switch (serdes2_prtcl) { case 0x07: case 0x08: case 0x49: lane = serdes_get_first_lane(FSL_SRDS_2, SGMII9 + (dpmac_id - 9)); slot = lane_to_slot_fsm2[lane]; switch (++slot) { case 1: break; case 3: break; case 4: /* Slot housing a SGMII riser card? */ wriop_set_phy_address(dpmac_id, riser_phy_addr[dpmac_id - 9]); dpmac_info[dpmac_id].board_mux = EMI1_SLOT4; bus = mii_dev_for_muxval(EMI1_SLOT4); wriop_set_mdio(dpmac_id, bus); dpmac_info[dpmac_id].phydev = phy_connect( dpmac_info[dpmac_id].bus, dpmac_info[dpmac_id].phy_addr, NULL, dpmac_info[dpmac_id].enet_if); phy_config(dpmac_info[dpmac_id].phydev); break; case 5: break; case 6: /* Slot housing a SGMII riser card? */ wriop_set_phy_address(dpmac_id, riser_phy_addr[dpmac_id - 13]); dpmac_info[dpmac_id].board_mux = EMI1_SLOT6; bus = mii_dev_for_muxval(EMI1_SLOT6); wriop_set_mdio(dpmac_id, bus); break; } break; default: printf("qds: WRIOP: Unsupported SerDes Protocol 0x%02x\n", serdes2_prtcl); break; } } void ls2085a_handle_phy_interface_xsgmii(int i) { struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR; int serdes1_prtcl = (in_le32(&gur->rcwsr[28]) & FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_MASK) >> FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_SHIFT; switch (serdes1_prtcl) { case 0x2A: /* * XFI does not need a PHY to work, but to avoid U-boot use * default PHY address which is zero to a MAC when it found * a MAC has no PHY address, we give a PHY address to XFI * MAC, and should not use a real XAUI PHY address, since * MDIO can access it successfully, and then MDIO thinks * the XAUI card is used for the XFI MAC, which will cause * error. */ wriop_set_phy_address(i, i + 4); ls2085a_qds_enable_SFP_TX(SFP_TX); break; default: printf("qds: WRIOP: Unsupported SerDes Protocol 0x%02x\n", serdes1_prtcl); break; } } #endif int board_eth_init(bd_t *bis) { int error; #ifdef CONFIG_FSL_MC_ENET struct memac_mdio_info *memac_mdio0_info; struct memac_mdio_info *memac_mdio1_info; unsigned int i; initialize_dpmac_to_slot(); memac_mdio0_info = (struct memac_mdio_info *)malloc( sizeof(struct memac_mdio_info)); memac_mdio0_info->regs = (struct memac_mdio_controller *) CONFIG_SYS_FSL_WRIOP1_MDIO1; memac_mdio0_info->name = DEFAULT_WRIOP_MDIO1_NAME; /* Register the real MDIO1 bus */ fm_memac_mdio_init(bis, memac_mdio0_info); memac_mdio1_info = (struct memac_mdio_info *)malloc( sizeof(struct memac_mdio_info)); memac_mdio1_info->regs = (struct memac_mdio_controller *) CONFIG_SYS_FSL_WRIOP1_MDIO2; memac_mdio1_info->name = DEFAULT_WRIOP_MDIO2_NAME; /* Register the real MDIO2 bus */ fm_memac_mdio_init(bis, memac_mdio1_info); /* Register the muxing front-ends to the MDIO buses */ ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO1_NAME, EMI1_SLOT1); ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO1_NAME, EMI1_SLOT2); ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO1_NAME, EMI1_SLOT3); ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO1_NAME, EMI1_SLOT4); ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO1_NAME, EMI1_SLOT5); ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO1_NAME, EMI1_SLOT6); ls2085a_qds_mdio_init(DEFAULT_WRIOP_MDIO2_NAME, EMI2); for (i = WRIOP1_DPMAC1; i < NUM_WRIOP_PORTS; i++) { switch (wriop_get_enet_if(i)) { case PHY_INTERFACE_MODE_QSGMII: break; case PHY_INTERFACE_MODE_SGMII: ls2085a_handle_phy_interface_sgmii(i); break; case PHY_INTERFACE_MODE_XGMII: ls2085a_handle_phy_interface_xsgmii(i); break; default: break; } } error = cpu_eth_init(bis); #endif error = pci_eth_init(bis); return error; }