| /* |
| * Copyright (c) 2012 - 2013, 2016-2017 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 <miiphy.h> |
| #include <linux/compat.h> |
| #include <fdtdec.h> |
| #include <asm/arch-ipq806x/clk.h> |
| #include <asm/arch-ipq806x/ipq_gmac.h> |
| #include <asm/arch-ipq806x/msm_ipq806x_gmac.h> |
| #include <asm/arch-qca-common/gpio.h> |
| #include <dt-bindings/qcom/gpio-ipq806x.h> |
| |
| #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)) |
| |
| uchar ipq_def_enetaddr[6] = {0x00, 0x03, 0x7F, 0xAB, 0xBD, 0xDA}; |
| |
| static struct ipq_eth_dev *ipq_gmac_macs[CONFIG_IPQ_NO_MACS]; |
| static int (*ipq_switch_init)(ipq_gmac_board_cfg_t *cfg); |
| static struct ipq_forced_mode get_params; |
| static struct bitbang_nodes *bb_nodes[CONFIG_IPQ_NO_MACS]; |
| static void ipq_gmac_mii_clk_init(struct ipq_eth_dev *priv, uint clk_div, |
| ipq_gmac_board_cfg_t *gmac_cfg); |
| |
| extern ipq_gmac_board_cfg_t gmac_cfg[]; |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| void ipq_register_switch(int(*sw_init)(ipq_gmac_board_cfg_t *cfg)) |
| { |
| ipq_switch_init = sw_init; |
| } |
| |
| static void config_auto_neg(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| u8 phy_addr; |
| |
| if (priv->forced_params->is_forced) { |
| if (priv->forced_params->miiwrite_done) { |
| phy_addr = priv->forced_params->phy_addr; |
| if (priv->forced_params->speed == SPEED_10M) { |
| miiphy_write(priv->phy_name, phy_addr, |
| PHY_CONTROL_REG, FORCE_RATE_10); |
| } else if (priv->forced_params->speed == SPEED_100M) { |
| miiphy_write(priv->phy_name, phy_addr, |
| PHY_CONTROL_REG, FORCE_RATE_100); |
| } else if (priv->forced_params->speed == SPEED_1000M) { |
| miiphy_write(priv->phy_name, phy_addr, |
| PHY_CONTROL_REG, AUTO_NEG_ENABLE); |
| } |
| priv->forced_params->miiwrite_done = 0; |
| mdelay(200); |
| } |
| } else { |
| miiphy_write(priv->phy_name, priv->phy_address[0], |
| PHY_CONTROL_REG, AUTO_NEG_ENABLE); |
| } |
| } |
| |
| static int ipq_phy_link_status(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| int port_status; |
| ushort phy_status; |
| uint i; |
| |
| udelay(1000); |
| |
| for (i = 0; i < priv->no_of_phys; i++) { |
| |
| miiphy_read(priv->phy_name, priv->phy_address[i], |
| PHY_SPECIFIC_STATUS_REG, &phy_status); |
| |
| port_status = ((phy_status & Mii_phy_status_link_up) >> |
| (MII_PHY_STAT_SHIFT)); |
| if (port_status == 1) |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| static void get_phy_speed_duplexity(struct eth_device *dev) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| uint phy_status; |
| uint start; |
| const uint timeout = 2000; |
| |
| start = get_timer(0); |
| while (get_timer(start) < timeout) { |
| |
| phy_status = readl(QSGMII_REG_BASE + |
| PCS_QSGMII_MAC_STAT); |
| |
| if (PCS_QSGMII_MAC_LINK(phy_status, priv->mac_unit)) { |
| |
| priv->speed = |
| PCS_QSGMII_MAC_SPEED(phy_status, |
| priv->mac_unit); |
| |
| priv->duplex = |
| PCS_QSGMII_MAC_DUPLEX(phy_status, |
| priv->mac_unit); |
| |
| if (priv->duplex) |
| ipq_info("Full duplex link\n"); |
| else |
| ipq_info("Half duplex link\n"); |
| |
| ipq_info("Link %x up, Phy_status = %x\n", |
| priv->mac_unit,phy_status); |
| |
| break; |
| } |
| |
| udelay(10); |
| } |
| } |
| |
| 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 ipq_mac_cfg; |
| |
| if (priv->mac_unit > GMAC_UNIT1) { |
| ipq_mac_cfg = (priv->mac_ps | FULL_DUPLEX_ENABLE); |
| } else { |
| ipq_mac_cfg = (GMII_PORT_SELECT | FULL_DUPLEX_ENABLE); |
| } |
| |
| ipq_mac_cfg |= (FRAME_BURST_ENABLE | TX_ENABLE | RX_ENABLE); |
| |
| writel(ipq_mac_cfg, &mac_reg->conf); |
| } |
| |
| 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 int ipq_eth_update(struct eth_device *dev, bd_t *this) |
| { |
| struct ipq_eth_dev *priv = dev->priv; |
| uint clk_div_val; |
| uint phy_status; |
| uint cur_speed; |
| uint cur_duplex; |
| |
| phy_status = readl(QSGMII_REG_BASE + |
| PCS_QSGMII_MAC_STAT); |
| |
| if (PCS_QSGMII_MAC_LINK(phy_status, priv->mac_unit)) { |
| cur_speed = PCS_QSGMII_MAC_SPEED(phy_status, |
| priv->mac_unit); |
| cur_duplex = PCS_QSGMII_MAC_DUPLEX(phy_status, |
| priv->mac_unit); |
| |
| if (cur_speed != priv->speed || cur_duplex != priv->duplex) { |
| ipq_info("Link %x status changed\n", priv->mac_unit); |
| if (priv->duplex) |
| ipq_info("Full duplex link\n"); |
| else |
| ipq_info("Half duplex link\n"); |
| |
| ipq_info("Link %x up, Phy_status = %x\n", |
| priv->mac_unit, phy_status); |
| |
| switch (cur_speed) { |
| case SPEED_1000M: |
| ipq_info("Port:%d speed 1000Mbps\n", |
| priv->mac_unit); |
| priv->mac_ps = GMII_PORT_SELECT; |
| clk_div_val = (CLK_DIV_SGMII_1000M - 1); |
| break; |
| |
| case SPEED_100M: |
| ipq_info("Port:%d speed 100Mbps\n", |
| priv->mac_unit); |
| priv->mac_ps = MII_PORT_SELECT; |
| clk_div_val = (CLK_DIV_SGMII_100M - 1); |
| break; |
| |
| case SPEED_10M: |
| ipq_info("Port:%d speed 10Mbps\n", |
| priv->mac_unit); |
| priv->mac_ps = MII_PORT_SELECT; |
| clk_div_val = (CLK_DIV_SGMII_10M - 1); |
| break; |
| |
| default: |
| ipq_info("Port speed unknown\n"); |
| return -1; |
| } |
| |
| priv->speed = cur_speed; |
| priv->duplex = cur_duplex; |
| |
| ipq_gmac_mii_clk_init(priv, clk_div_val, |
| priv->gmac_board_cfg); |
| } |
| } else { |
| return -1; |
| } |
| |
| 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 (!(priv->forced_params->is_forced && (priv->mac_unit == GMAC_UNIT2 || |
| priv->mac_unit == GMAC_UNIT3))) { |
| if (ipq_phy_link_status(dev) != 0) { |
| ipq_info("Mac%x unit failed\n", priv->mac_unit); |
| return -1; |
| } |
| |
| if (priv->gmac_board_cfg->mac_conn_to_phy) { |
| /* Check the current speed and duplex mode and change |
| the MAC settings according to it */ |
| if (ipq_eth_update(dev, this) != 0) { |
| ipq_info("Mac%x settings update failed\n", |
| priv->mac_unit); |
| return -1; |
| } |
| } |
| } |
| |
| priv->next_rx = 0; |
| priv->next_tx = 0; |
| |
| ipq_mac_reset(dev); |
| |
| if ((priv->mac_unit == GMAC_UNIT2) || (priv->mac_unit == GMAC_UNIT3)) |
| config_auto_neg(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 void |
| gmac_sgmii_clk_init(uint mac_unit, uint clk_div, ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| uint gmac_ctl_val; |
| uint nss_eth_clk_gate_val; |
| |
| gmac_ctl_val = (NSS_ETH_GMAC_PHY_INTF_SEL | |
| NSS_ETH_GMAC_PHY_IFG_LIMIT | |
| NSS_ETH_GMAC_PHY_IFG); |
| |
| |
| nss_eth_clk_gate_val = (GMACn_GMII_RX_CLK(mac_unit) | |
| GMACn_GMII_TX_CLK(mac_unit) | |
| GMACn_PTP_CLK(mac_unit)); |
| |
| writel(gmac_ctl_val, (NSS_REG_BASE + NSS_GMACn_CTL(mac_unit))); |
| |
| if (gmac_cfg->phy == PHY_INTERFACE_MODE_QSGMII) { |
| nss_eth_clk_gate_val = GMACn_GMII_RX_CLK(mac_unit) | |
| GMACn_GMII_TX_CLK(mac_unit); |
| clrbits_le32((NSS_REG_BASE + NSS_ETH_CLK_SRC_CTL), |
| (1 << mac_unit)); |
| writel(NSS_QSGMII_CLK_CTL_CLR_MSK, |
| (NSS_REG_BASE + NSS_QSGMII_CLK_CTL)); |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_DIV0), |
| GMACn_CLK_DIV(mac_unit, 1)); |
| } |
| |
| switch (mac_unit) { |
| case GMAC_UNIT0: |
| case GMAC_UNIT1: |
| setbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| PCS_CHn_FORCE_SPEED(mac_unit)); |
| clrbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| PCS_CHn_SPEED_MASK(mac_unit)); |
| setbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| PCS_CHn_SPEED(mac_unit, |
| PCS_CH_SPEED_1000)); |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_GATE_CTL), |
| nss_eth_clk_gate_val); |
| break; |
| case GMAC_UNIT2: |
| case GMAC_UNIT3: |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_SRC_CTL), |
| (1 << mac_unit)); |
| if (gmac_cfg->mac_conn_to_phy) { |
| |
| setbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| (PCS_CHn_SPEED_FORCE_OUTSIDE(mac_unit) | |
| PCS_DEBUG_SELECT)); |
| |
| |
| if (clk_div == 0) { |
| clrbits_le32((NSS_REG_BASE + |
| NSS_ETH_CLK_DIV0), |
| (NSS_ETH_CLK_DIV( |
| NSS_ETH_CLK_DIV_MASK, |
| mac_unit))); |
| } else { |
| clrsetbits_le32((NSS_REG_BASE + |
| NSS_ETH_CLK_DIV0), |
| (NSS_ETH_CLK_DIV( |
| NSS_ETH_CLK_DIV_MASK, |
| mac_unit)), |
| (NSS_ETH_CLK_DIV(clk_div, |
| mac_unit))); |
| } |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_GATE_CTL), |
| nss_eth_clk_gate_val); |
| } else { |
| /* this part of code forces the speed of MAC 2 to |
| * 1000Mbps disabling the Autoneg in case |
| * of AP148/DB147 since it is connected to switch |
| */ |
| setbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| PCS_CHn_FORCE_SPEED(mac_unit)); |
| |
| clrbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| PCS_CHn_SPEED_MASK(mac_unit)); |
| |
| setbits_le32((QSGMII_REG_BASE + PCS_ALL_CH_CTL), |
| PCS_CHn_SPEED(mac_unit, |
| PCS_CH_SPEED_1000)); |
| |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_GATE_CTL), |
| nss_eth_clk_gate_val); |
| } |
| break; |
| } |
| } |
| |
| static void ipq_gmac_mii_clk_init(struct ipq_eth_dev *priv, uint clk_div, |
| ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| u32 nss_gmac_ctl_val; |
| u32 nss_eth_clk_gate_ctl_val; |
| int gmac_idx = priv->mac_unit; |
| u32 interface = priv->interface; |
| |
| switch (interface) { |
| case PHY_INTERFACE_MODE_RGMII: |
| nss_gmac_ctl_val = (GMAC_PHY_RGMII | GMAC_IFG | |
| GMAC_IFG_LIMIT(GMAC_IFG)); |
| nss_eth_clk_gate_ctl_val = |
| (GMACn_RGMII_RX_CLK(gmac_idx) | |
| GMACn_RGMII_TX_CLK(gmac_idx) | |
| GMACn_PTP_CLK(gmac_idx)); |
| setbits_le32((NSS_REG_BASE + NSS_GMACn_CTL(gmac_idx)), |
| nss_gmac_ctl_val); |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_GATE_CTL), |
| nss_eth_clk_gate_ctl_val); |
| setbits_le32((NSS_REG_BASE + NSS_ETH_CLK_SRC_CTL), |
| (0x1 << gmac_idx)); |
| writel((NSS_ETH_CLK_DIV(1, gmac_idx)), |
| (NSS_REG_BASE + NSS_ETH_CLK_DIV0)); |
| break; |
| case PHY_INTERFACE_MODE_SGMII: |
| case PHY_INTERFACE_MODE_QSGMII: |
| gmac_sgmii_clk_init(gmac_idx, clk_div, gmac_cfg); |
| break; |
| default : |
| ipq_info(" default : no rgmii sgmii for gmac %d \n", gmac_idx); |
| return; |
| } |
| } |
| |
| int ipq_gmac_init(ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| static int sw_init_done = 0; |
| struct eth_device *dev[CONFIG_IPQ_NO_MACS]; |
| uint clk_div_val; |
| uchar enet_addr[CONFIG_IPQ_NO_MACS * 6]; |
| uchar *mac_addr; |
| char ethaddr[32] = "ethaddr"; |
| char mac[64]; |
| int i, phy_idx; |
| int ret; |
| int gmac_gpio_node = 0, ar8033_gpio_node = 0, offset = 0; |
| memset(enet_addr, 0, sizeof(enet_addr)); |
| |
| /* 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 failed; |
| |
| ipq_gmac_macs[i] = malloc(sizeof(struct ipq_eth_dev)); |
| if (ipq_gmac_macs[i] == NULL) |
| goto 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]; |
| |
| snprintf(dev[i]->name, sizeof(dev[i]->name), "eth%d", 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] = gmac_cfg->unit & 0xff; |
| } else { |
| memcpy(&dev[i]->enetaddr[0], &enet_addr[i * 6], 6); |
| |
| /* |
| * Populate the environment with these MAC addresses. |
| * U-Boot uses these to patch the 'local-mac-address' |
| * dts entry for the ethernet entries, which in turn |
| * will be picked up by the HLOS driver |
| */ |
| snprintf(mac, sizeof(mac), "%x:%x:%x:%x:%x:%x", |
| dev[i]->enetaddr[0], dev[i]->enetaddr[1], |
| dev[i]->enetaddr[2], dev[i]->enetaddr[3], |
| dev[i]->enetaddr[4], dev[i]->enetaddr[5]); |
| |
| setenv(ethaddr, mac); |
| |
| } |
| |
| 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(ethaddr, sizeof(ethaddr), "eth%daddr", (i + 1)); |
| |
| 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]->interface = gmac_cfg->phy; |
| ipq_gmac_macs[i]->phy_address = gmac_cfg->phy_addr.addr; |
| ipq_gmac_macs[i]->no_of_phys = gmac_cfg->phy_addr.count; |
| ipq_gmac_macs[i]->gmac_board_cfg = gmac_cfg; |
| |
| if (get_params.gmac_port == gmac_cfg->unit) { |
| ipq_gmac_macs[i]->forced_params = &get_params; |
| } |
| /* tx/rx Descriptor initialization */ |
| if (ipq_gmac_tx_rx_desc_ring(dev[i]->priv) == -1) |
| goto failed; |
| |
| if ((gmac_cfg->unit == GMAC_UNIT2 || |
| gmac_cfg->unit == GMAC_UNIT3) && |
| (gmac_cfg->mac_conn_to_phy)) { |
| if (ipq_gmac_macs[i]->forced_params->is_forced) { |
| ipq_gmac_macs[i]->speed = ipq_gmac_macs[i]->forced_params->speed; |
| } else { |
| get_phy_speed_duplexity(dev[i]); |
| } |
| switch (ipq_gmac_macs[i]->speed) { |
| case SPEED_1000M: |
| ipq_info("Port:%d speed 1000Mbps\n", |
| gmac_cfg->unit); |
| ipq_gmac_macs[i]->mac_ps = GMII_PORT_SELECT; |
| clk_div_val = (CLK_DIV_SGMII_1000M - 1); |
| break; |
| case SPEED_100M: |
| ipq_info("Port:%d speed 100Mbps\n", |
| gmac_cfg->unit); |
| ipq_gmac_macs[i]->mac_ps = MII_PORT_SELECT; |
| clk_div_val = (CLK_DIV_SGMII_100M - 1); |
| break; |
| case SPEED_10M: |
| ipq_info("Port:%d speed 10Mbps\n", |
| gmac_cfg->unit); |
| ipq_gmac_macs[i]->mac_ps = MII_PORT_SELECT; |
| clk_div_val = (CLK_DIV_SGMII_10M - 1); |
| break; |
| default: |
| ipq_info("Port speed unknown\n"); |
| goto failed; |
| } |
| } else { |
| /* Force it to zero for GMAC 0 & 1 */ |
| clk_div_val = 0; |
| } |
| |
| ipq_gmac_mii_clk_init(ipq_gmac_macs[i], clk_div_val, gmac_cfg); |
| |
| strlcpy((char *)ipq_gmac_macs[i]->phy_name, gmac_cfg->phy_name, |
| sizeof(ipq_gmac_macs[i]->phy_name)); |
| bb_nodes[i] = malloc(sizeof(struct bitbang_nodes)); |
| if (bb_nodes[i] == NULL) |
| goto failed; |
| memset(bb_nodes[i], 0, sizeof(struct bitbang_nodes)); |
| |
| gmac_gpio_node = fdt_path_offset(gd->fdt_blob, "gmac_gpio"); |
| if (gmac_gpio_node >= 0) { |
| offset = fdt_first_subnode(gd->fdt_blob, gmac_gpio_node); |
| bb_nodes[i]->mdio = fdtdec_get_uint(gd->fdt_blob, offset, "gpio", 0); |
| |
| offset = fdt_next_subnode(gd->fdt_blob, offset); |
| bb_nodes[i]->mdc = fdtdec_get_uint(gd->fdt_blob, offset, "gpio", 0); |
| bb_miiphy_buses[i].priv = bb_nodes[i]; |
| strncpy(bb_miiphy_buses[i].name, gmac_cfg->phy_name, |
| sizeof(bb_miiphy_buses[i].name)); |
| miiphy_register(bb_miiphy_buses[i].name, bb_miiphy_read, bb_miiphy_write); |
| for (phy_idx = 0; phy_idx < ipq_gmac_macs[i]->no_of_phys; phy_idx++) { |
| miiphy_write(bb_miiphy_buses[i].name, |
| ipq_gmac_macs[i]->phy_address[phy_idx], PHY_CONTROL_REG, |
| BMCR_RESET | AUTO_NEG_ENABLE); |
| mdelay(100); |
| } |
| } |
| |
| eth_register(dev[i]); |
| |
| if (!sw_init_done && ipq_switch_init) { |
| if (ipq_switch_init(gmac_cfg) == 0) { |
| sw_init_done = 1; |
| } else { |
| ipq_info("Switch inits failed\n"); |
| goto failed; |
| } |
| } |
| } |
| |
| ar8033_gpio_node = fdt_path_offset(gd->fdt_blob, "/ar8033_gpio"); |
| |
| if (ar8033_gpio_node >= 0) { |
| bb_nodes[i] = malloc(sizeof(struct bitbang_nodes)); |
| memset(bb_nodes[i], 0, sizeof(struct bitbang_nodes)); |
| |
| offset = fdt_first_subnode(gd->fdt_blob, ar8033_gpio_node); |
| bb_nodes[i]->mdio = fdtdec_get_uint(gd->fdt_blob, offset, "gpio", 0); |
| |
| offset = fdt_next_subnode(gd->fdt_blob, offset); |
| bb_nodes[i]->mdc = fdtdec_get_uint(gd->fdt_blob, offset, "gpio", 0); |
| |
| bb_miiphy_buses[i].priv = bb_nodes[i]; |
| strlcpy(bb_miiphy_buses[i].name, "8033", |
| sizeof(bb_miiphy_buses[i].name)); |
| miiphy_register(bb_miiphy_buses[i].name, bb_miiphy_read, bb_miiphy_write); |
| } |
| |
| /* set the mac address in environment for unconfigured GMAC */ |
| if (ret >= 0) { |
| for (; i < CONFIG_IPQ_NO_MACS; i++) { |
| mac_addr = &enet_addr[i * 6]; |
| if (is_valid_ethaddr(mac_addr)) { |
| /* |
| * U-Boot uses these to patch the 'local-mac-address' |
| * dts entry for the ethernet entries, which in turn |
| * will be picked up by the HLOS driver |
| */ |
| sprintf(mac, "%x:%x:%x:%x:%x:%x", |
| mac_addr[0], mac_addr[1], |
| mac_addr[2], mac_addr[3], |
| mac_addr[4], mac_addr[5]); |
| setenv(ethaddr, mac); |
| } |
| sprintf(ethaddr, "eth%daddr", (i + 1)); |
| } |
| } |
| |
| return 0; |
| |
| failed: |
| for (i = 0; i < CONFIG_IPQ_NO_MACS; i++) { |
| if (bb_nodes[i]) |
| free(bb_nodes[i]); |
| if (dev[i]) { |
| eth_unregister(dev[i]); |
| free(dev[i]); |
| } |
| if (ipq_gmac_macs[i]) |
| free(ipq_gmac_macs[i]); |
| } |
| |
| return -ENOMEM; |
| } |
| |
| |
| |
| static void ipq_gmac_core_reset(ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| for (; gmac_cfg_is_valid(gmac_cfg); gmac_cfg++) { |
| writel(0, GMAC_CORE_RESET(gmac_cfg->unit)); |
| if (gmac_cfg->is_macsec) |
| writel(0, GMACSEC_CORE_RESET(gmac_cfg->unit)); |
| } |
| |
| writel(0, (void *)GMAC_AHB_RESET); |
| } |
| |
| void ipq_gmac_common_init(ipq_gmac_board_cfg_t *gmac_cfg) |
| { |
| uint pcs_qsgmii_ctl_val; |
| uint pcs_mode_ctl_val; |
| uint i; |
| ipq_gmac_board_cfg_t *gmac_tmp_cfg = gmac_cfg; |
| |
| pcs_mode_ctl_val = (PCS_CHn_ANEG_EN(GMAC_UNIT1) | |
| PCS_CHn_ANEG_EN(GMAC_UNIT2) | |
| PCS_CHn_ANEG_EN(GMAC_UNIT3) | |
| PCS_CHn_ANEG_EN(GMAC_UNIT0) | |
| PCS_SGMII_MAC); |
| |
| pcs_qsgmii_ctl_val = (PCS_QSGMII_ATHR_CSCO_AUTONEG | |
| PCS_QSGMII_SW_VER_1_7 | |
| PCS_QSGMII_SHORT_THRESH | |
| PCS_QSGMII_SHORT_LATENCY | |
| PCS_QSGMII_DEPTH_THRESH(1) | |
| PCS_CHn_SERDES_SN_DETECT(0) | |
| PCS_CHn_SERDES_SN_DETECT(1) | |
| PCS_CHn_SERDES_SN_DETECT(2) | |
| PCS_CHn_SERDES_SN_DETECT(3) | |
| PCS_CHn_SERDES_SN_DETECT_2(0) | |
| PCS_CHn_SERDES_SN_DETECT_2(1) | |
| PCS_CHn_SERDES_SN_DETECT_2(2) | |
| PCS_CHn_SERDES_SN_DETECT_2(3)); |
| |
| for (i = 0; gmac_cfg_is_valid(gmac_tmp_cfg); gmac_tmp_cfg++, i++) { |
| switch(gmac_tmp_cfg->phy) { |
| case PHY_INTERFACE_MODE_SGMII: |
| writel(QSGMII_PHY_MODE_SGMII, |
| (QSGMII_REG_BASE + QSGMII_PHY_MODE_CTL)); |
| writel(PCS_QSGMII_MODE_SGMII, |
| (QSGMII_REG_BASE + PCS_QSGMII_SGMII_MODE)); |
| break; |
| case PHY_INTERFACE_MODE_QSGMII: |
| pcs_mode_ctl_val = (PCS_SGMII_MAC); |
| writel(QSGMII_PHY_MODE_QSGMII, |
| (QSGMII_REG_BASE + QSGMII_PHY_MODE_CTL)); |
| writel(PCS_QSGMII_MODE_QSGMII, |
| (QSGMII_REG_BASE + PCS_QSGMII_SGMII_MODE)); |
| clrbits_le32((QSGMII_REG_BASE + QSGMII_PHY_QSGMII_CTL), |
| QSGMII_TX_SLC_CTL(3)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| writel(MACSEC_BYPASS_EXT_EN, (NSS_REG_BASE + NSS_MACSEC_CTL)); |
| writel(pcs_mode_ctl_val, (QSGMII_REG_BASE + NSS_PCS_MODE_CTL)); |
| writel(pcs_qsgmii_ctl_val, (QSGMII_REG_BASE + PCS_QSGMII_CTL)); |
| |
| /* |
| * MDIO lines for all the MACs are connected through MAC0. |
| * Regardless of MAC 0 being used or not, it has to be pulled |
| * out of reset. Else, MDIO writes to configure other MACs |
| * will fail. |
| */ |
| writel(0, GMAC_CORE_RESET(0)); |
| |
| /* |
| * Pull out of reset the MACs that are applicable to the |
| * current board. |
| */ |
| ipq_gmac_core_reset(gmac_cfg); |
| } |
| |
| static int ipq_eth_bb_init(struct bb_miiphy_bus *bus) |
| { |
| return 0; |
| } |
| |
| static int ipq_eth_bb_mdio_active(struct bb_miiphy_bus *bus) |
| { |
| struct bitbang_nodes *bb_node = bus->priv; |
| struct qca_gpio_config gmac_gpio_config = {0}; |
| |
| gmac_gpio_config.gpio = bb_node->mdio; |
| gmac_gpio_config.func = 0; |
| gmac_gpio_config.out = GPIO_OUTPUT; |
| gmac_gpio_config.pull = GPIO_NO_PULL; |
| gmac_gpio_config.drvstr = GPIO_8MA; |
| gmac_gpio_config.oe = 1; |
| |
| gpio_tlmm_config(&gmac_gpio_config); |
| |
| gmac_gpio_config.gpio = bb_node->mdc; |
| |
| gpio_tlmm_config(&gmac_gpio_config); |
| |
| return 0; |
| } |
| |
| static int ipq_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) |
| { |
| struct bitbang_nodes *bb_node = bus->priv; |
| struct qca_gpio_config gmac_gpio_config = {0}; |
| |
| gmac_gpio_config.gpio = bb_node->mdio; |
| gmac_gpio_config.func = 0; |
| gmac_gpio_config.out = GPIO_INPUT; |
| gmac_gpio_config.pull = GPIO_NO_PULL; |
| gmac_gpio_config.drvstr = GPIO_8MA; |
| gmac_gpio_config.oe = 0; |
| |
| gpio_tlmm_config(&gmac_gpio_config); |
| |
| |
| gmac_gpio_config.gpio = bb_node->mdc; |
| gmac_gpio_config.out = GPIO_OUTPUT; |
| gmac_gpio_config.oe = 1; |
| |
| gpio_tlmm_config(&gmac_gpio_config); |
| return 0; |
| } |
| |
| static int ipq_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) |
| { |
| struct bitbang_nodes *bb_node = bus->priv; |
| |
| gpio_set_value(bb_node->mdio, v); |
| |
| return 0; |
| } |
| |
| static int ipq_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) |
| { |
| struct bitbang_nodes *bb_node = bus->priv; |
| |
| *v = gpio_get_value(bb_node->mdio); |
| |
| return 0; |
| } |
| |
| static int ipq_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) |
| { |
| struct bitbang_nodes *bb_node = bus->priv; |
| |
| gpio_set_value(bb_node->mdc, v); |
| |
| return 0; |
| } |
| |
| static int ipq_eth_bb_delay(struct bb_miiphy_bus *bus) |
| { |
| ndelay(350); |
| |
| return 0; |
| } |
| |
| struct bb_miiphy_bus bb_miiphy_buses[] = { |
| { |
| .init = ipq_eth_bb_init, |
| .mdio_active = ipq_eth_bb_mdio_active, |
| .mdio_tristate = ipq_eth_bb_mdio_tristate, |
| .set_mdio = ipq_eth_bb_set_mdio, |
| .get_mdio = ipq_eth_bb_get_mdio, |
| .set_mdc = ipq_eth_bb_set_mdc, |
| .delay = ipq_eth_bb_delay, |
| }, |
| { |
| .init = ipq_eth_bb_init, |
| .mdio_active = ipq_eth_bb_mdio_active, |
| .mdio_tristate = ipq_eth_bb_mdio_tristate, |
| .set_mdio = ipq_eth_bb_set_mdio, |
| .get_mdio = ipq_eth_bb_get_mdio, |
| .set_mdc = ipq_eth_bb_set_mdc, |
| .delay = ipq_eth_bb_delay, |
| }, |
| { |
| .init = ipq_eth_bb_init, |
| .mdio_active = ipq_eth_bb_mdio_active, |
| .mdio_tristate = ipq_eth_bb_mdio_tristate, |
| .set_mdio = ipq_eth_bb_set_mdio, |
| .get_mdio = ipq_eth_bb_get_mdio, |
| .set_mdc = ipq_eth_bb_set_mdc, |
| .delay = ipq_eth_bb_delay, |
| }, |
| { |
| .init = ipq_eth_bb_init, |
| .mdio_active = ipq_eth_bb_mdio_active, |
| .mdio_tristate = ipq_eth_bb_mdio_tristate, |
| .set_mdio = ipq_eth_bb_set_mdio, |
| .get_mdio = ipq_eth_bb_get_mdio, |
| .set_mdc = ipq_eth_bb_set_mdc, |
| .delay = ipq_eth_bb_delay, |
| }, |
| }; |
| int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); |
| |
| static int ipq_eth_unregister(void) |
| { |
| int i; |
| struct eth_device *dev; |
| |
| for (i = 0; i < CONFIG_IPQ_NO_MACS; i++) { |
| if (bb_nodes[i]) |
| free(bb_nodes[i]); |
| if (ipq_gmac_macs[i]) { |
| dev = ipq_gmac_macs[i]->dev; |
| eth_unregister(dev); |
| } |
| if (ipq_gmac_macs[i]) |
| free(ipq_gmac_macs[i]); |
| } |
| |
| return 0; |
| } |
| |
| static int do_force_eth_speed(cmd_tbl_t *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| int status; |
| int i; |
| int j; |
| int phyaddrfound = 0; |
| int phy_addr; |
| |
| if (argc != 3) |
| return CMD_RET_USAGE; |
| |
| ipq_gmac_board_cfg_t *gmac_tmp_cfg = gmac_cfg; |
| |
| if (strict_strtoul(argv[1], 16, (unsigned long *)&phy_addr) < 0) { |
| ipq_info("Invalid Phy addr configured\n"); |
| return CMD_RET_USAGE; |
| } |
| get_params.phy_addr = phy_addr; |
| for (i = 0; gmac_cfg_is_valid(gmac_tmp_cfg); gmac_tmp_cfg++, i++) { |
| for (j = 0; j < gmac_tmp_cfg->phy_addr.count; j++) { |
| if (gmac_tmp_cfg->phy_addr.addr[j] == get_params.phy_addr) { |
| get_params.gmac_port = gmac_tmp_cfg->unit; |
| phyaddrfound = 1; |
| break; |
| } |
| } |
| } |
| if (phyaddrfound == 0) { |
| ipq_info("Invalid Phy addr configured\n"); |
| return CMD_RET_USAGE; |
| } |
| |
| if (strcmp(argv[2], "10") == 0) { |
| get_params.speed = SPEED_10M; |
| } else if (strcmp(argv[2], "100") == 0) { |
| get_params.speed = SPEED_100M; |
| } else if (strcmp(argv[2], "autoneg") == 0) { |
| get_params.speed = SPEED_1000M; |
| } else { |
| ipq_info("Invalid speed settings configured\n"); |
| return CMD_RET_USAGE; |
| } |
| |
| get_params.is_forced = 1; |
| get_params.miiwrite_done = 1; |
| ipq_eth_unregister(); |
| status = ipq_gmac_init(gmac_cfg); |
| |
| return status; |
| } |
| |
| U_BOOT_CMD(ethspeed, 3, 0, do_force_eth_speed, |
| "Force ethernet speed to 10/100/autoneg", |
| "ethspeed {phy addr} {10|100|autoneg} - Force ethernet speed to 10/100/autoneg\n"); |
| |