// SPDX-License-Identifier: GPL-2.0-only
/*
* This is the driver for the GMAC on-chip Ethernet controller for ST SoCs.
* DWC Ether MAC version 4.00 has been used for developing this code.
*
* This only implements the mac core functions for this chip.
*
* Copyright (C) 2015 STMicroelectronics Ltd
*
* Author: Alexandre Torgue <alexandre.torgue@st.com>
*/
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
#include <linux/io.h>
#include <net/dsa.h>
#include "stmmac.h"
#include "stmmac_pcs.h"
#include "dwmac4.h"
#include "dwmac5.h"
static void dwmac4_core_init(struct mac_device_info *hw,
struct net_device *dev)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_CONFIG);
value |= GMAC_CORE_INIT;
if (hw->ps) {
value |= GMAC_CONFIG_TE;
value &= hw->link.speed_mask;
switch (hw->ps) {
case SPEED_1000:
value |= hw->link.speed1000;
break;
case SPEED_100:
value |= hw->link.speed100;
break;
case SPEED_10:
value |= hw->link.speed10;
break;
}
}
writel(value, ioaddr + GMAC_CONFIG);
/* Enable GMAC interrupts */
value = GMAC_INT_DEFAULT_ENABLE;
if (hw->pcs)
value |= GMAC_PCS_IRQ_DEFAULT;
writel(value, ioaddr + GMAC_INT_EN);
}
static void dwmac4_rx_queue_enable(struct mac_device_info *hw,
u8 mode, u32 queue)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_RXQ_CTRL0);
value &= GMAC_RX_QUEUE_CLEAR(queue);
if (mode == MTL_QUEUE_AVB)
value |= GMAC_RX_AV_QUEUE_ENABLE(queue);
else if (mode == MTL_QUEUE_DCB)
value |= GMAC_RX_DCB_QUEUE_ENABLE(queue);
writel(value, ioaddr + GMAC_RXQ_CTRL0);
}
static void dwmac4_rx_queue_priority(struct mac_device_info *hw,
u32 prio, u32 queue)
{
void __iomem *ioaddr = hw->pcsr;
u32 base_register;
u32 value;
base_register = (queue < 4) ? GMAC_RXQ_CTRL2 : GMAC_RXQ_CTRL3;
if (queue >= 4)
queue -= 4;
value = readl(ioaddr + base_register);
value &= ~GMAC_RXQCTRL_PSRQX_MASK(queue);
value |= (prio << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) &
GMAC_RXQCTRL_PSRQX_MASK(queue);
writel(value, ioaddr + base_register);
}
static void dwmac4_tx_queue_priority(struct mac_device_info *hw,
u32 prio, u32 queue)
{
void __iomem *ioaddr = hw->pcsr;
u32 base_register;
u32 value;
base_register = (queue < 4) ? GMAC_TXQ_PRTY_MAP0 : GMAC_TXQ_PRTY_MAP1;
if (queue >= 4)
queue -= 4;
value = readl(ioaddr + base_register);
value &= ~GMAC_TXQCTRL_PSTQX_MASK(queue);
value |= (prio << GMAC_TXQCTRL_PSTQX_SHIFT(queue)) &
GMAC_TXQCTRL_PSTQX_MASK(queue);
writel(value, ioaddr + base_register);
}
static void dwmac4_rx_queue_routing(struct mac_device_info *hw,
u8 packet, u32 queue)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
static const struct stmmac_rx_routing route_possibilities[] = {
{ GMAC_RXQCTRL_AVCPQ_MASK, GMAC_RXQCTRL_AVCPQ_SHIFT },
{ GMAC_RXQCTRL_PTPQ_MASK, GMAC_RXQCTRL_PTPQ_SHIFT },
{ GMAC_RXQCTRL_DCBCPQ_MASK, GMAC_RXQCTRL_DCBCPQ_SHIFT },
{ GMAC_RXQCTRL_UPQ_MASK, GMAC_RXQCTRL_UPQ_SHIFT },
{ GMAC_RXQCTRL_MCBCQ_MASK, GMAC_RXQCTRL_MCBCQ_SHIFT },
};
value = readl(ioaddr + GMAC_RXQ_CTRL1);
/* routing configuration */
value &= ~route_possibilities[packet - 1].reg_mask;
value |= (queue << route_possibilities[packet-1].reg_shift) &
route_possibilities[packet - 1].reg_mask;
/* some packets require extra ops */
if (packet == PACKET_AVCPQ) {
value &= ~GMAC_RXQCTRL_TACPQE;
value |= 0x1 << GMAC_RXQCTRL_TACPQE_SHIFT;
} else if (packet == PACKET_MCBCQ) {
value &= ~GMAC_RXQCTRL_MCBCQEN;
value |= 0x1 << GMAC_RXQCTRL_MCBCQEN_SHIFT;
}
writel(value, ioaddr + GMAC_RXQ_CTRL1);
}
static void dwmac4_prog_mtl_rx_algorithms(struct mac_device_info *hw,
u32 rx_alg)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + MTL_OPERATION_MODE);
value &= ~MTL_OPERATION_RAA;
switch (rx_alg) {
case MTL_RX_ALGORITHM_SP:
value |= MTL_OPERATION_RAA_SP;
break;
case MTL_RX_ALGORITHM_WSP:
value |= MTL_OPERATION_RAA_WSP;
break;