blob: 877d9be6b4a40cbc4fb5e8ee68a6cace6ca71bc9 [file] [log] [blame]
/*
* 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");