blob: 567e49b31ef79f2243d96c065b4cd44e2e06054c [file] [log] [blame]
/*
* 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;
}