| /* |
| * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <common.h> |
| #include <net.h> |
| #include <asm-generic/errno.h> |
| #include <asm/io.h> |
| #include <malloc.h> |
| #include <phy.h> |
| #include <net.h> |
| #include <asm/global_data.h> |
| #include "ipq_phy.h" |
| #include <asm/arch-ipq5018/ipq5018_gmac.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| #define ipq_info printf |
| #define ipq_dbg printf |
| #define DESC_SIZE (sizeof(ipq_gmac_desc_t)) |
| #define DESC_FLUSH_SIZE (((DESC_SIZE + (CONFIG_SYS_CACHELINE_SIZE - 1)) \ |
| / CONFIG_SYS_CACHELINE_SIZE) * \ |
| (CONFIG_SYS_CACHELINE_SIZE)) |
| |
| static struct ipq_eth_dev *ipq_gmac_macs[IPQ5018_GMAC_PORT]; |
| |
| uchar ipq_def_enetaddr[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; |
| phy_info_t *phy_info[IPQ5018_PHY_MAX] = {0}; |
| |
| extern int ipq_mdio_read(int mii_id, int regnum, ushort *data); |
| extern int ipq_mdio_write(int mii_id, int regnum, u16 value); |
| extern int ipq5018_mdio_write(int mii_id, int regnum, u16 value); |
| extern int ipq5018_mdio_read(int mii_id, int regnum, ushort *data); |
| extern int ipq_qca8033_phy_init(struct phy_ops **ops, u32 phy_id); |
| extern int ipq_qca8081_phy_init(struct phy_ops **ops, u32 phy_id); |
| extern int ipq_gephy_phy_init(struct phy_ops **ops, u32 phy_id); |
| extern int ipq_sw_mdio_init(const char *); |
| extern int ipq5018_sw_mdio_init(const char *); |
| extern void ppe_uniphy_mode_set(uint32_t mode); |
| extern int ipq_athrs17_init(ipq_gmac_board_cfg_t *gmac_cfg); |
| |
| static int ipq_eth_wr_macaddr(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_mac_regs *mac_p = (struct eth_mac_regs *)priv->mac_regs_p; |
| u32 macid_lo, macid_hi; |
| u8 *mac_id = &dev->enetaddr[0]; |
| |
| macid_lo = mac_id[0] + (mac_id[1] << 8) + |
| (mac_id[2] << 16) + (mac_id[3] << 24); |
| macid_hi = mac_id[4] + (mac_id[5] << 8); |
| |
| writel(macid_hi, &mac_p->macaddr0hi); |
| writel(macid_lo, &mac_p->macaddr0lo); |
| |
| return 0; |
| } |
| |
| static void ipq_mac_reset(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_dma_regs *dma_reg = (struct eth_dma_regs *)priv->dma_regs_p; |
| u32 val; |
| |
| writel(DMAMAC_SRST, &dma_reg->busmode); |
| do { |
| udelay(10); |
| val = readl(&dma_reg->busmode); |
| } while (val & DMAMAC_SRST); |
| |
| } |
| |
| static void ipq_eth_mac_cfg(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_mac_regs *mac_reg = (struct eth_mac_regs *)priv->mac_regs_p; |
| uint speed = 0; |
| uint ipq_mac_cfg = 0; |
| uint ipq_mac_framefilter = 0; |
| |
| ipq_mac_framefilter = PROMISCUOUS_MODE_ON; |
| |
| if (priv->mac_unit) { |
| if (priv->phy_type == QCA8081_1_1_PHY || priv->phy_type == QCA8033_PHY) |
| speed = priv->speed; |
| |
| ipq_mac_cfg |= (FRAME_BURST_ENABLE | JUMBO_FRAME_ENABLE | JABBER_DISABLE | |
| TX_ENABLE | RX_ENABLE | FULL_DUPLEX_ENABLE | speed); |
| |
| writel(ipq_mac_cfg, &mac_reg->conf); |
| } else { |
| ipq_mac_cfg |= (priv->speed | FULL_DUPLEX_ENABLE | FRAME_BURST_ENABLE | |
| TX_ENABLE | RX_ENABLE); |
| writel(ipq_mac_cfg, &mac_reg->conf); |
| } |
| |
| writel(ipq_mac_framefilter, &mac_reg->framefilt); |
| |
| } |
| |
| static void ipq_eth_dma_cfg(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_dma_regs *dma_reg = (struct eth_dma_regs *)priv->dma_regs_p; |
| uint ipq_dma_bus_mode; |
| uint ipq_dma_op_mode; |
| |
| ipq_dma_op_mode = DmaStoreAndForward | DmaRxThreshCtrl128 | |
| DmaTxSecondFrame; |
| ipq_dma_bus_mode = DmaFixedBurstEnable | DmaBurstLength16 | |
| DmaDescriptorSkip0 | DmaDescriptor8Words | |
| DmaArbitPr; |
| |
| writel(ipq_dma_bus_mode, &dma_reg->busmode); |
| writel(ipq_dma_op_mode, &dma_reg->opmode); |
| } |
| |
| static void ipq_eth_flw_cntl_cfg(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_mac_regs *mac_reg = (struct eth_mac_regs *)priv->mac_regs_p; |
| struct eth_dma_regs *dma_reg = (struct eth_dma_regs *)priv->dma_regs_p; |
| uint ipq_dma_flw_cntl; |
| uint ipq_mac_flw_cntl; |
| |
| ipq_dma_flw_cntl = DmaRxFlowCtrlAct3K | DmaRxFlowCtrlDeact4K | |
| DmaEnHwFlowCtrl; |
| ipq_mac_flw_cntl = GmacRxFlowControl | GmacTxFlowControl | 0xFFFF0000; |
| |
| setbits_le32(&dma_reg->opmode, ipq_dma_flw_cntl); |
| setbits_le32(&mac_reg->flowcontrol, ipq_mac_flw_cntl); |
| } |
| |
| static int ipq_gmac_alloc_fifo(int ndesc, ipq_gmac_desc_t **fifo) |
| { |
| int i; |
| void *addr; |
| |
| addr = memalign((CONFIG_SYS_CACHELINE_SIZE), |
| (ndesc * DESC_FLUSH_SIZE)); |
| |
| for (i = 0; i < ndesc; i++) { |
| fifo[i] = (ipq_gmac_desc_t *)((unsigned long)addr + |
| (i * DESC_FLUSH_SIZE)); |
| if (fifo[i] == NULL) { |
| ipq_info("Can't allocate desc fifos\n"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static int ipq_gmac_rx_desc_setup(struct ipq_eth_dev *priv) |
| { |
| struct eth_dma_regs *dma_reg = (struct eth_dma_regs *)priv->dma_regs_p; |
| ipq_gmac_desc_t *rxdesc; |
| int i; |
| |
| for (i = 0; i < NO_OF_RX_DESC; i++) { |
| rxdesc = priv->desc_rx[i]; |
| rxdesc->length |= ((ETH_MAX_FRAME_LEN << DescSize1Shift) & |
| DescSize1Mask); |
| rxdesc->buffer1 = virt_to_phys(net_rx_packets[i]); |
| rxdesc->data1 = (unsigned long)priv->desc_rx[(i + 1) % |
| NO_OF_RX_DESC]; |
| |
| rxdesc->extstatus = 0; |
| rxdesc->reserved1 = 0; |
| rxdesc->timestamplow = 0; |
| rxdesc->timestamphigh = 0; |
| rxdesc->status = DescOwnByDma; |
| |
| |
| flush_dcache_range((unsigned long)rxdesc, |
| (unsigned long)rxdesc + DESC_SIZE); |
| |
| } |
| /* Assign Descriptor base address to dmadesclist addr reg */ |
| writel((uint)priv->desc_rx[0], &dma_reg->rxdesclistaddr); |
| |
| return 0; |
| } |
| |
| static int ipq_gmac_tx_rx_desc_ring(struct ipq_eth_dev *priv) |
| { |
| int i; |
| ipq_gmac_desc_t *desc; |
| |
| if (ipq_gmac_alloc_fifo(NO_OF_TX_DESC, priv->desc_tx)) |
| return -1; |
| |
| for (i = 0; i < NO_OF_TX_DESC; i++) { |
| desc = priv->desc_tx[i]; |
| memset(desc, 0, DESC_SIZE); |
| |
| desc->status = |
| (i == (NO_OF_TX_DESC - 1)) ? TxDescEndOfRing : 0; |
| |
| desc->status |= TxDescChain; |
| |
| desc->data1 = (unsigned long)priv->desc_tx[(i + 1) % |
| NO_OF_TX_DESC ]; |
| |
| flush_dcache_range((unsigned long)desc, |
| (unsigned long)desc + DESC_SIZE); |
| |
| } |
| |
| if (ipq_gmac_alloc_fifo(NO_OF_RX_DESC, priv->desc_rx)) |
| return -1; |
| |
| for (i = 0; i < NO_OF_RX_DESC; i++) { |
| desc = priv->desc_rx[i]; |
| memset(desc, 0, DESC_SIZE); |
| desc->length = |
| (i == (NO_OF_RX_DESC - 1)) ? RxDescEndOfRing : 0; |
| desc->length |= RxDescChain; |
| |
| desc->data1 = (unsigned long)priv->desc_rx[(i + 1) % |
| NO_OF_RX_DESC]; |
| |
| flush_dcache_range((unsigned long)desc, |
| (unsigned long)desc + DESC_SIZE); |
| |
| } |
| |
| priv->next_tx = 0; |
| priv->next_rx = 0; |
| |
| return 0; |
| } |
| |
| static inline void ipq_gmac_give_to_dma(ipq_gmac_desc_t *fr) |
| { |
| fr->status |= DescOwnByDma; |
| } |
| |
| static inline u32 ipq_gmac_owned_by_dma(ipq_gmac_desc_t *fr) |
| { |
| return (fr->status & DescOwnByDma); |
| } |
| |
| static inline u32 ipq_gmac_is_desc_empty(ipq_gmac_desc_t *fr) |
| { |
| return ((fr->length & DescSize1Mask) == 0); |
| } |
| |
| static void ipq5018_gmac0_speed_clock_set(int speed_clock1, |
| int speed_clock2, int gmacid) |
| { |
| int iTxRx; |
| uint32_t reg_value; |
| /* |
| * iTxRx indicates Tx and RX register |
| * 0 = Rx and 1 = Tx |
| */ |
| for (iTxRx = 0; iTxRx < 2; ++iTxRx){ |
| /* gcc port first clock divider */ |
| reg_value = 0; |
| reg_value = readl(GCC_GMAC0_RX_CFG_RCGR + |
| (iTxRx * 8) + (gmacid * 0x10)); |
| reg_value &= ~0x1f; |
| reg_value |= speed_clock1; |
| writel(reg_value, GCC_GMAC0_RX_CFG_RCGR + |
| (iTxRx * 8) + (gmacid * 0x10)); |
| /* gcc port second clock divider */ |
| reg_value = 0; |
| reg_value = readl(GCC_GMAC0_RX_MISC + |
| (iTxRx * 4) + (gmacid * 0x10)); |
| reg_value &= ~0xf; |
| reg_value |= speed_clock2; |
| writel(reg_value, GCC_GMAC0_RX_MISC + |
| (iTxRx * 4) + (gmacid * 0x10)); |
| /* update above clock configuration */ |
| reg_value = 0; |
| reg_value = readl(GCC_GMAC0_RX_CMD_RCGR + |
| (iTxRx * 8) + (gmacid * 0x10)); |
| reg_value &= ~0x1; |
| reg_value |= 0x1; |
| writel(reg_value, GCC_GMAC0_RX_CMD_RCGR + |
| (iTxRx * 8) + (gmacid * 0x10)); |
| } |
| } |
| |
| static void ipq5018_enable_gephy(void) |
| { |
| uint32_t reg_val; |
| |
| reg_val = readl(GCC_GEPHY_RX_CBCR); |
| reg_val |= GCC_CBCR_CLK_ENABLE; |
| writel(reg_val, GCC_GEPHY_RX_CBCR); |
| mdelay(20); |
| |
| reg_val = readl(GCC_GEPHY_TX_CBCR); |
| reg_val |= GCC_CBCR_CLK_ENABLE; |
| writel(reg_val, GCC_GEPHY_TX_CBCR); |
| mdelay(20); |
| } |
| |
| static int ipq5018_s17c_Link_Update(struct ipq_eth_dev *priv) |
| { |
| uint16_t phy_data; |
| int status = 1; |
| int i; |
| |
| for(i = 0; |
| i < priv->gmac_board_cfg->switch_port_count; ++i){ |
| phy_data = ipq_mdio_read( |
| priv->gmac_board_cfg->switch_port_phy_address[i], |
| 0x11, |
| NULL); |
| |
| if (phy_data == 0x50) |
| continue; |
| |
| /* Atleast one port should be link up*/ |
| if (phy_data & LINK_UP) |
| status = 0; |
| |
| printf("Port%d %s ", i + 1, LINK(phy_data)); |
| |
| switch(SPEED(phy_data)){ |
| case SPEED_1000M: |
| printf("Speed :1000M "); |
| break; |
| case SPEED_100M: |
| printf("Speed :100M "); |
| break; |
| default: |
| printf("Speed :10M "); |
| } |
| |
| printf ("%s \n", DUPLEX(phy_data)); |
| } |
| return status; |
| } |
| |
| static int ipq5018_phy_link_update(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| u8 status = 1; |
| struct phy_ops *phy_get_ops; |
| fal_port_speed_t speed; |
| fal_port_duplex_t duplex; |
| char *lstatus[] = {"up", "Down"}; |
| char *dp[] = {"Half", "Full"}; |
| int speed_clock1 = 0, speed_clock2 = 0; |
| int mode = PORT_WRAPPER_SGMII0_RGMII4; |
| |
| phy_get_ops = priv->ops; |
| |
| if (priv->ipq_swith) { |
| speed_clock1 = 1; |
| speed_clock2 = 0; |
| status = ipq5018_s17c_Link_Update(priv); |
| } |
| |
| if (phy_get_ops != NULL && |
| phy_get_ops->phy_get_link_status != NULL && |
| phy_get_ops->phy_get_speed != NULL && |
| phy_get_ops->phy_get_duplex != NULL){ |
| |
| status = phy_get_ops->phy_get_link_status(priv->mac_unit, |
| priv->phy_address); |
| phy_get_ops->phy_get_speed(priv->mac_unit, |
| priv->phy_address, &speed); |
| phy_get_ops->phy_get_duplex(priv->mac_unit, |
| priv->phy_address, &duplex); |
| |
| switch (speed) { |
| case FAL_SPEED_10: |
| speed_clock1 = 9; |
| speed_clock2 = 9; |
| priv->speed = MII_PORT_SELECT; |
| printf ("eth%d %s Speed :%d %s duplex\n", |
| priv->mac_unit, |
| lstatus[status], speed, |
| dp[duplex]); |
| break; |
| case FAL_SPEED_100: |
| priv->speed = MII_PORT_SELECT | FES_PORT_SPEED; |
| speed_clock1 = 9; |
| speed_clock2 = 0; |
| printf ("eth%d %s Speed :%d %s duplex\n", |
| priv->mac_unit, |
| lstatus[status], speed, |
| dp[duplex]); |
| break; |
| case FAL_SPEED_1000: |
| priv->speed = SGMII_PORT_SELECT; |
| speed_clock1 = 1; |
| speed_clock2 = 0; |
| printf ("eth%d %s Speed :%d %s duplex\n", |
| priv->mac_unit, |
| lstatus[status], speed, |
| dp[duplex]); |
| break; |
| case FAL_SPEED_2500: |
| priv->speed = SGMII_PORT_SELECT; |
| mode = PORT_WRAPPER_SGMII_PLUS; |
| speed_clock1 = 1; |
| speed_clock2 = 0; |
| printf ("eth%d %s Speed :%d %s duplex\n", |
| priv->mac_unit, |
| lstatus[status], speed, |
| dp[duplex]); |
| break; |
| default: |
| printf("Unknown speed\n"); |
| break; |
| } |
| } |
| |
| if (status) { |
| /* No PHY link is alive */ |
| if (priv->ipq_swith == 0 && phy_get_ops == NULL) |
| printf("Link status/Get speed/Get duplex not mapped\n"); |
| return -1; |
| } |
| |
| if (priv->mac_unit){ |
| ppe_uniphy_mode_set(mode); |
| } else { |
| ipq5018_enable_gephy(); |
| } |
| |
| ipq5018_gmac0_speed_clock_set(speed_clock1, speed_clock2, priv->mac_unit); |
| |
| return 0; |
| } |
| |
| int ipq_eth_init(struct eth_device *dev, bd_t *this) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_dma_regs *dma_reg = (struct eth_dma_regs *)priv->dma_regs_p; |
| u32 data; |
| |
| if(ipq5018_phy_link_update(dev) < 0) { |
| return -1; |
| } |
| |
| priv->next_rx = 0; |
| priv->next_tx = 0; |
| |
| ipq_mac_reset(dev); |
| ipq_eth_wr_macaddr(dev); |
| |
| /* DMA, MAC configuration for Synopsys GMAC */ |
| ipq_eth_dma_cfg(dev); |
| ipq_eth_mac_cfg(dev); |
| ipq_eth_flw_cntl_cfg(dev); |
| |
| /* clear all pending interrupts if any */ |
| data = readl(&dma_reg->status); |
| writel(data, &dma_reg->status); |
| |
| /* Setup Rx fifos and assign base address to */ |
| ipq_gmac_rx_desc_setup(priv); |
| |
| writel((uint)priv->desc_tx[0], &dma_reg->txdesclistaddr); |
| setbits_le32(&dma_reg->opmode, (RXSTART)); |
| setbits_le32(&dma_reg->opmode, (TXSTART)); |
| |
| return 1; |
| } |
| |
| static int ipq_eth_send(struct eth_device *dev, void *packet, int length) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_dma_regs *dma_p = (struct eth_dma_regs *)priv->dma_regs_p; |
| ipq_gmac_desc_t *txdesc = priv->desc_tx[priv->next_tx]; |
| int i; |
| |
| invalidate_dcache_range((unsigned long)txdesc, |
| (unsigned long)txdesc + DESC_FLUSH_SIZE); |
| |
| /* Check if the dma descriptor is still owned by DMA */ |
| if (ipq_gmac_owned_by_dma(txdesc)) { |
| ipq_info("BUG: Tx descriptor is owned by DMA %p\n", txdesc); |
| return NETDEV_TX_BUSY; |
| } |
| |
| txdesc->length |= ((length <<DescSize1Shift) & DescSize1Mask); |
| txdesc->status |= (DescTxFirst | DescTxLast | DescTxIntEnable); |
| txdesc->buffer1 = virt_to_phys(packet); |
| ipq_gmac_give_to_dma(txdesc); |
| |
| flush_dcache_range((unsigned long)txdesc, |
| (unsigned long)txdesc + DESC_SIZE); |
| |
| flush_dcache_range((unsigned long)(txdesc->buffer1), |
| (unsigned long)(txdesc->buffer1) + PKTSIZE_ALIGN); |
| |
| /* Start the transmission */ |
| writel(POLL_DATA, &dma_p->txpolldemand); |
| |
| for (i = 0; i < MAX_WAIT; i++) { |
| |
| udelay(10); |
| |
| invalidate_dcache_range((unsigned long)txdesc, |
| (unsigned long)txdesc + DESC_FLUSH_SIZE); |
| |
| if (!ipq_gmac_owned_by_dma(txdesc)) |
| break; |
| } |
| |
| if (i == MAX_WAIT) { |
| ipq_info("Tx Timed out\n"); |
| } |
| |
| /* reset the descriptors */ |
| txdesc->status = (priv->next_tx == (NO_OF_TX_DESC - 1)) ? |
| TxDescEndOfRing : 0; |
| txdesc->status |= TxDescChain; |
| txdesc->length = 0; |
| txdesc->buffer1 = 0; |
| |
| priv->next_tx = (priv->next_tx + 1) % NO_OF_TX_DESC; |
| |
| txdesc->data1 = (unsigned long)priv->desc_tx[priv->next_tx]; |
| |
| |
| flush_dcache_range((unsigned long)txdesc, |
| (unsigned long)txdesc + DESC_SIZE); |
| |
| return 0; |
| } |
| |
| static int ipq_eth_recv(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| struct eth_dma_regs *dma_p = (struct eth_dma_regs *)priv->dma_regs_p; |
| int length = 0; |
| ipq_gmac_desc_t *rxdesc = priv->desc_rx[priv->next_rx]; |
| uint status; |
| |
| invalidate_dcache_range((unsigned long)(priv->desc_rx[0]), |
| (unsigned long)(priv->desc_rx[NO_OF_RX_DESC - 1]) + |
| DESC_FLUSH_SIZE); |
| |
| for (rxdesc = priv->desc_rx[priv->next_rx]; |
| !ipq_gmac_owned_by_dma(rxdesc); |
| rxdesc = priv->desc_rx[priv->next_rx]) { |
| |
| status = rxdesc->status; |
| length = ((status & DescFrameLengthMask) >> |
| DescFrameLengthShift); |
| |
| invalidate_dcache_range( |
| (unsigned long)(net_rx_packets[priv->next_rx]), |
| (unsigned long)(net_rx_packets[priv->next_rx]) + |
| PKTSIZE_ALIGN); |
| net_process_received_packet(net_rx_packets[priv->next_rx], length - 4); |
| |
| rxdesc->length = ((ETH_MAX_FRAME_LEN << DescSize1Shift) & |
| DescSize1Mask); |
| |
| rxdesc->length |= (priv->next_rx == (NO_OF_RX_DESC - 1)) ? |
| RxDescEndOfRing : 0; |
| rxdesc->length |= RxDescChain; |
| |
| rxdesc->buffer1 = virt_to_phys(net_rx_packets[priv->next_rx]); |
| |
| priv->next_rx = (priv->next_rx + 1) % NO_OF_RX_DESC; |
| |
| rxdesc->data1 = (unsigned long)priv->desc_rx[priv->next_rx]; |
| |
| rxdesc->extstatus = 0; |
| rxdesc->reserved1 = 0; |
| rxdesc->timestamplow = 0; |
| rxdesc->timestamphigh = 0; |
| rxdesc->status = DescOwnByDma; |
| |
| flush_dcache_range((unsigned long)rxdesc, |
| (unsigned long)rxdesc + DESC_SIZE); |
| |
| writel(POLL_DATA, &dma_p->rxpolldemand); |
| } |
| |
| return length; |
| } |
| |
| static void ipq_eth_halt(struct eth_device *dev) |
| { |
| if (dev->state != ETH_STATE_ACTIVE) |
| return; |
| /* reset the mac */ |
| ipq_mac_reset(dev); |
| } |
| |
| static int QCA8337_switch_init(ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| int port; |
| |
| for (port = 0; |
| port < gmac_cfg->switch_port_count; |
| ++port) { |
| u32 phy_val; |
| /* phy powerdown */ |
| ipq_mdio_write( |
| gmac_cfg->switch_port_phy_address[port], |
| 0x0, |
| 0x0800 |
| ); |
| phy_val = ipq_mdio_read( |
| gmac_cfg->switch_port_phy_address[port], |
| 0x3d, |
| NULL |
| ); |
| phy_val &= ~0x0040; |
| ipq_mdio_write( |
| gmac_cfg->switch_port_phy_address[port], |
| 0x3d, |
| phy_val |
| ); |
| /* |
| * PHY will stop the tx clock for a while when link is down |
| * en_anychange debug port 0xb bit13 = 0 //speed up link down tx_clk |
| * sel_rst_80us debug port 0xb bit10 = 0 //speed up speed mode change to 2'b10 tx_clk |
| */ |
| phy_val = ipq_mdio_read( |
| gmac_cfg->switch_port_phy_address[port], |
| 0xb, |
| NULL |
| ); |
| phy_val &= ~0x2400; |
| ipq_mdio_write( |
| gmac_cfg->switch_port_phy_address[port], |
| 0xb, |
| phy_val |
| ); |
| mdelay(100); |
| } |
| if (ipq_athrs17_init(gmac_cfg) != 0){ |
| printf("QCA_8337 switch init failed \n"); |
| return 0; |
| } |
| for (port = 0; |
| port < gmac_cfg->switch_port_count; |
| ++port) { |
| ipq_mdio_write( |
| gmac_cfg->switch_port_phy_address[port], |
| MII_ADVERTISE, |
| ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM |
| ); |
| /* phy reg 0x9, b10,1 = Prefer multi-port device (master) */ |
| ipq_mdio_write( |
| gmac_cfg->switch_port_phy_address[port], |
| MII_CTRL1000, |
| (0x0400|ADVERTISE_1000FULL) |
| ); |
| ipq_mdio_write( |
| gmac_cfg->switch_port_phy_address[port], |
| MII_BMCR, |
| BMCR_RESET | BMCR_ANENABLE |
| ); |
| mdelay(100); |
| } |
| return 1; |
| } |
| |
| static void gephy_mdac_edac_config(ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| uint16_t phy_data; |
| uint32_t phy_dac = PHY_DAC(0x10); |
| uint32_t C45_id = QCA808X_REG_C45_ADDRESS(MPGE_PHY_MMD1_NUM, |
| MPGE_PHY_MMD1_DAC); |
| /*set mdac value*/ |
| phy_data = ipq5018_mdio_read( |
| gmac_cfg->phy_addr, |
| C45_id, |
| NULL |
| ); |
| phy_data &= ~(MPGE_PHY_MMD1_DAC_MASK); |
| ipq5018_mdio_write( |
| gmac_cfg->phy_addr, |
| C45_id, |
| (phy_data | phy_dac) |
| ); |
| mdelay(1); |
| /*set edac value*/ |
| phy_data = ipq5018_mdio_read( |
| gmac_cfg->phy_addr, |
| MPGE_PHY_DEBUG_EDAC, |
| NULL |
| ); |
| phy_data &= ~(MPGE_PHY_MMD1_DAC_MASK); |
| ipq5018_mdio_write( |
| gmac_cfg->phy_addr, |
| MPGE_PHY_DEBUG_EDAC, |
| (phy_data | phy_dac) |
| ); |
| mdelay(1); |
| } |
| |
| static void mdio_init(void) |
| { |
| if(ipq5018_sw_mdio_init("IPQ MDIO0")) |
| printf("MDIO Failed to init for GMAC0\n"); |
| |
| if(ipq_sw_mdio_init("IPQ MDIO1")) |
| printf("MDIO Failed to init for GMAC1\n"); |
| } |
| |
| int ipq_gmac_init(ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| struct eth_device *dev[CONFIG_IPQ_NO_MACS]; |
| uchar enet_addr[CONFIG_IPQ_NO_MACS * 6]; |
| int i; |
| uint32_t phy_chip_id, phy_chip_id1, phy_chip_id2; |
| int ret; |
| memset(enet_addr, 0, sizeof(enet_addr)); |
| |
| /* Mdio init */ |
| mdio_init(); |
| |
| /* Getting the MAC address from ART partition */ |
| ret = get_eth_mac_address(enet_addr, CONFIG_IPQ_NO_MACS); |
| |
| for (i = 0; gmac_cfg_is_valid(gmac_cfg); gmac_cfg++, i++) { |
| |
| dev[i] = malloc(sizeof(struct eth_device)); |
| if (dev[i] == NULL) |
| goto init_failed; |
| |
| ipq_gmac_macs[i] = malloc(sizeof(struct ipq_eth_dev)); |
| if (ipq_gmac_macs[i] == NULL) |
| goto init_failed; |
| |
| memset(dev[i], 0, sizeof(struct eth_device)); |
| memset(ipq_gmac_macs[i], 0, sizeof(struct ipq_eth_dev)); |
| |
| dev[i]->iobase = gmac_cfg->base; |
| dev[i]->init = ipq_eth_init; |
| dev[i]->halt = ipq_eth_halt; |
| dev[i]->recv = ipq_eth_recv; |
| dev[i]->send = ipq_eth_send; |
| dev[i]->write_hwaddr = ipq_eth_wr_macaddr; |
| dev[i]->priv = (void *) ipq_gmac_macs[i]; |
| /* |
| * Setting the Default MAC address |
| * if the MAC read from ART partition is invalid |
| */ |
| if ((ret < 0) || |
| (!is_valid_ethaddr(&enet_addr[i * 6]))) { |
| memcpy(&dev[i]->enetaddr[0], ipq_def_enetaddr, 6); |
| dev[i]->enetaddr[5] = dev[i]->enetaddr[5] + i; |
| } else { |
| memcpy(&dev[i]->enetaddr[0], &enet_addr[i * 6], 6); |
| } |
| |
| ipq_info("MAC%x addr:%x:%x:%x:%x:%x:%x\n", |
| gmac_cfg->unit, dev[i]->enetaddr[0], |
| dev[i]->enetaddr[1], |
| dev[i]->enetaddr[2], |
| dev[i]->enetaddr[3], |
| dev[i]->enetaddr[4], |
| dev[i]->enetaddr[5]); |
| |
| snprintf(dev[i]->name, sizeof(dev[i]->name), "eth%d", i); |
| |
| ipq_gmac_macs[i]->dev = dev[i]; |
| ipq_gmac_macs[i]->mac_unit = gmac_cfg->unit; |
| ipq_gmac_macs[i]->mac_regs_p = |
| (struct eth_mac_regs *)(gmac_cfg->base); |
| ipq_gmac_macs[i]->dma_regs_p = |
| (struct eth_dma_regs *)(gmac_cfg->base + DW_DMA_BASE_OFFSET); |
| ipq_gmac_macs[i]->phy_address = gmac_cfg->phy_addr; |
| ipq_gmac_macs[i]->gmac_board_cfg = gmac_cfg; |
| ipq_gmac_macs[i]->interface = gmac_cfg->phy_interface_mode; |
| ipq_gmac_macs[i]->phy_type = gmac_cfg->phy_type; |
| ipq_gmac_macs[i]->phy_external_link = gmac_cfg->phy_external_link; |
| |
| snprintf((char *)ipq_gmac_macs[i]->phy_name, |
| sizeof(ipq_gmac_macs[i]->phy_name), "IPQ MDIO%d", i); |
| |
| if (gmac_cfg->unit){ |
| phy_chip_id1 = ipq_mdio_read( |
| ipq_gmac_macs[i]->phy_address, |
| QCA_PHY_ID1, |
| NULL); |
| phy_chip_id2 = ipq_mdio_read( |
| ipq_gmac_macs[i]->phy_address, |
| QCA_PHY_ID2, |
| NULL); |
| phy_chip_id = (phy_chip_id1 << 16) | phy_chip_id2; |
| } else { |
| phy_chip_id1 = ipq5018_mdio_read( |
| ipq_gmac_macs[i]->phy_address, |
| QCA_PHY_ID1, |
| NULL); |
| phy_chip_id2 = ipq5018_mdio_read( |
| ipq_gmac_macs[i]->phy_address, |
| QCA_PHY_ID2, |
| NULL); |
| phy_chip_id = (phy_chip_id1 << 16) | phy_chip_id2; |
| } |
| switch(phy_chip_id) { |
| #ifdef CONFIG_QCA8081_PHY |
| /* NAPA PHY For GMAC1 */ |
| case QCA8081_PHY: |
| case QCA8081_1_1_PHY: |
| ipq_gmac_macs[i]->phy_type = QCA8081_1_1_PHY; |
| ipq_qca8081_phy_init( |
| &ipq_gmac_macs[i]->ops, |
| ipq_gmac_macs[i]->phy_address); |
| break; |
| #endif |
| /* Internel GEPHY only for GMAC0 */ |
| case GEPHY: |
| ipq_gmac_macs[i]->phy_type = GEPHY; |
| ipq_gephy_phy_init( |
| &ipq_gmac_macs[i]->ops, |
| ipq_gmac_macs[i]->phy_address); |
| if(ipq_gmac_macs[i]->phy_external_link) |
| gephy_mdac_edac_config(gmac_cfg); |
| break; |
| #ifdef CONFIG_QCA8033_PHY |
| /* 1G PHY */ |
| case QCA8033_PHY: |
| ipq_gmac_macs[i]->phy_type = QCA8033_PHY; |
| ipq_qca8033_phy_init( |
| &ipq_gmac_macs[i]->ops, |
| ipq_gmac_macs[i]->phy_address); |
| break; |
| #endif |
| case QCA_8337: |
| if(gmac_cfg->ipq_swith){ |
| ipq_gmac_macs[i]->ipq_swith = |
| QCA8337_switch_init(gmac_cfg); |
| } |
| break; |
| default: |
| printf("GMAC%d:Invalid PHY ID \n", i); |
| break; |
| } |
| /* Initialize 8337 switch */ |
| if (gmac_cfg->ipq_swith && |
| ipq_gmac_macs[i]->phy_external_link && |
| !ipq_gmac_macs[i]->ipq_swith){ |
| ipq_gmac_macs[i]->ipq_swith = |
| QCA8337_switch_init(gmac_cfg); |
| } |
| /* Tx/Rx Descriptor initialization */ |
| if (ipq_gmac_tx_rx_desc_ring(dev[i]->priv) == -1) |
| goto init_failed; |
| |
| eth_register(dev[i]); |
| } |
| return 0; |
| |
| init_failed: |
| for (i = 0; i < IPQ5018_GMAC_PORT; i++) { |
| if (dev[i]) { |
| free(dev[i]); |
| } |
| if (ipq_gmac_macs[i]) |
| free(ipq_gmac_macs[i]); |
| } |
| |
| return -ENOMEM; |
| } |
| |