linuxOS_D21X/source/linux-5.10/drivers/net/ethernet/artinchip/aicmac_mac.c
2024-11-29 16:33:21 +08:00

312 lines
8.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 ArtInChip Technology Co., Ltd.
* Author: Keliang Liu <keliang.liu@artinchip.com>
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
#include <linux/netdevice.h>
#include "aicmac.h"
#include "aicmac_util.h"
#include "aicmac_mac.h"
#include "aicmac_dma.h"
#include "aicmac_platform.h"
#include "aicmac_gmac_reg.h"
#include "aicmac_dma_reg.h"
static void aicmac_mac_validate(struct phylink_config *config,
unsigned long *supported,
struct phylink_link_state *state)
{
struct aicmac_priv *priv = netdev_priv(to_net_dev(config->dev));
__ETHTOOL_DECLARE_LINK_MODE_MASK(mac_supported) = { 0, };
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = {
0,
};
int tx_cnt = priv->plat->tx_queues_to_use;
int max_speed = priv->plat->mac_data->max_speed;
phylink_set(mac_supported, 10baseT_Half);
phylink_set(mac_supported, 10baseT_Full);
phylink_set(mac_supported, 100baseT_Half);
phylink_set(mac_supported, 100baseT_Full);
phylink_set(mac_supported, 1000baseT_Half);
phylink_set(mac_supported, 1000baseT_Full);
phylink_set(mac_supported, 1000baseKX_Full);
phylink_set(mac_supported, Autoneg);
phylink_set(mac_supported, Pause);
phylink_set(mac_supported, Asym_Pause);
phylink_set_port_modes(mac_supported);
/* Cut down 1G if asked to */
if ((max_speed > 0) && (max_speed < 1000)) {
phylink_set(mask, 1000baseT_Full);
phylink_set(mask, 1000baseX_Full);
}
/* Half-Duplex can only work with single queue */
if (tx_cnt > 1) {
phylink_set(mask, 10baseT_Half);
phylink_set(mask, 100baseT_Half);
phylink_set(mask, 1000baseT_Half);
}
bitmap_and(supported, supported, mac_supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_andnot(supported, supported, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_and(state->advertising, state->advertising, mac_supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_andnot(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
static void aicmac_mac_config_interface(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
struct aicmac_priv *priv = netdev_priv(to_net_dev(config->dev));
u32 ctrl;
ctrl = aicmac_mac_reg_get_config(priv->resource->ioaddr);
ctrl &= ~priv->plat->mac_data->link.speed_mask;
if (state->interface == PHY_INTERFACE_MODE_USXGMII) {
switch (state->speed) {
case SPEED_10000:
ctrl |= priv->plat->mac_data->link.xgmii.speed10000;
break;
case SPEED_5000:
ctrl |= priv->plat->mac_data->link.xgmii.speed5000;
break;
case SPEED_2500:
ctrl |= priv->plat->mac_data->link.xgmii.speed2500;
break;
default:
return;
}
} else {
switch (state->speed) {
case SPEED_2500:
ctrl |= priv->plat->mac_data->link.speed2500;
break;
case SPEED_1000:
ctrl |= priv->plat->mac_data->link.speed1000;
break;
case SPEED_100:
ctrl |= priv->plat->mac_data->link.speed100;
break;
case SPEED_10:
ctrl |= priv->plat->mac_data->link.speed10;
break;
default:
return;
}
}
if (!state->duplex)
ctrl &= ~priv->plat->mac_data->link.duplex;
else
ctrl |= priv->plat->mac_data->link.duplex;
if (state->pause)
aicmac_mac_reg_flow_ctrl(priv->plat->mac_data, state->duplex,
FLOW_AUTO, PAUSE_TIME,
priv->plat->tx_queues_to_use);
aicmac_mac_reg_set_config(priv->resource->ioaddr, ctrl);
aicmac_mac_reg_enable_mac(priv->resource->ioaddr, true);
}
static void aicmac_mac_link_down(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
struct aicmac_priv *priv = netdev_priv(to_net_dev(config->dev));
aicmac_mac_reg_enable_mac(priv->resource->ioaddr, false);
priv->link_state = LINK_STATE_DOWN;
}
static void aicmac_mac_link_up(struct phylink_config *config,
struct phy_device *phy, unsigned int mode,
phy_interface_t interface, int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct aicmac_priv *priv = netdev_priv(to_net_dev(config->dev));
u32 ctrl;
ctrl = readl(priv->resource->ioaddr + GMAC_BASIC_CONFIG);
ctrl &= ~priv->plat->mac_data->link.speed_mask;
switch (speed) {
case SPEED_2500:
ctrl |= priv->plat->mac_data->link.speed2500;
break;
case SPEED_1000:
ctrl |= priv->plat->mac_data->link.speed1000;
break;
case SPEED_100:
ctrl |= priv->plat->mac_data->link.speed100;
break;
case SPEED_10:
ctrl |= priv->plat->mac_data->link.speed10;
break;
default:
return;
}
priv->plat->mac_data->mac_port_sel_speed = speed;
if (!duplex)
ctrl &= ~priv->plat->mac_data->link.duplex;
else
ctrl |= priv->plat->mac_data->link.duplex;
/* Flow Control operation */
if (tx_pause && rx_pause)
aicmac_mac_reg_flow_ctrl(priv->plat->mac_data, duplex,
FLOW_AUTO, PAUSE_TIME,
priv->plat->tx_queues_to_use);
writel(ctrl, priv->resource->ioaddr + GMAC_BASIC_CONFIG);
aicmac_mac_reg_enable_mac(priv->resource->ioaddr, true);
priv->link_state = LINK_STATE_UP;
#ifdef CONFIG_ARTINCHIP_GMAC_DEBUG
aicmac_print_reg("linkup", priv->resource->ioaddr, AICMAC_GMAC_REGS_NUM);
#endif
}
static void aicmac_mac_pcs_get_state(struct phylink_config *config,
struct phylink_link_state *state)
{
state->link = 0;
}
static void aicmac_mac_an_restart(struct phylink_config *config)
{
pr_warn("%s called with unsupported!\n", __func__);
}
static const struct phylink_mac_ops aicmac_phylink_mac_ops = {
.validate = aicmac_mac_validate,
.mac_pcs_get_state = aicmac_mac_pcs_get_state,
.mac_config = aicmac_mac_config_interface,
.mac_an_restart = aicmac_mac_an_restart,
.mac_link_down = aicmac_mac_link_down,
.mac_link_up = aicmac_mac_link_up,
};
struct aicmac_mac_data *aicmac_mac_init_data(struct platform_device *pdev,
struct device_node *np)
{
struct aicmac_mac_data *mac_data = devm_kzalloc(&pdev->dev,
sizeof(struct aicmac_mac_data), GFP_KERNEL);
if (of_get_phy_mode(np, &mac_data->mac_interface))
mac_data->mac_interface = PHY_INTERFACE_MODE_MII;
mac_data->mac_port_sel_speed =
aicmac_interface_to_speed(mac_data->mac_interface);
mac_data->max_speed = mac_data->mac_port_sel_speed;
mac_data->ps = mac_data->mac_port_sel_speed;
if (of_property_read_u32(np, "max-speed", &mac_data->max_speed))
mac_data->max_speed = 1000;
mac_data->maxmtu = 9000;
mac_data->riwt_off = 1;
mac_data->bugged_jumbo = 1;
mac_data->en_tx_lpi_clockgating = false;
mac_data->multicast_filter_bins = 64;
mac_data->unicast_filter_entries = 8;
mac_data->mcast_bits_log2 = ilog2(mac_data->multicast_filter_bins);
;
mac_data->pcs = 0;
mac_data->pmt = 0;
mac_data->link.duplex = GMAC_BASIC_CONFIG_DM;
mac_data->link.speed10 = GMAC_BASIC_CONFIG_PC_10M;
mac_data->link.speed100 = GMAC_BASIC_CONFIG_PC_100M;
mac_data->link.speed1000 = GMAC_BASIC_CONFIG_PC_1000M;
mac_data->link.speed_mask = GMAC_BASIC_CONFIG_PC_MASK;
mac_data->phylink_mac_ops = &aicmac_phylink_mac_ops;
pr_info("%s mac_interface:%d max_speed:%d\n", __func__, mac_data->mac_interface,
mac_data->max_speed);
return mac_data;
}
int aicmac_mac_ip_init(struct aicmac_priv *priv)
{
struct aicmac_platform_data *plat = priv->plat;
plat->hw_cap.hw_cap_support = 1;
plat->hw_cap.mbps_10_100 = 1;
plat->hw_cap.mbps_1000 = 1;
plat->hw_cap.half_duplex = 1;
plat->hw_cap.hash_filter = 1;
plat->hw_cap.multi_addr = 1;
plat->hw_cap.pcs = 0;
plat->hw_cap.sma_mdio = 1;
plat->hw_cap.pmt_remote_wake_up = 0;
plat->hw_cap.pmt_magic_frame = 0;
/* MMC */
plat->hw_cap.rmon = 0;
/* IEEE 1588-2002 */
plat->hw_cap.time_stamp = 0;
/* IEEE 1588-2008 */
plat->hw_cap.atime_stamp = 1;
/* 802.3az - Energy-Efficient Ethernet (EEE) */
plat->hw_cap.eee = 0;
plat->hw_cap.av = 1;
/* TX and RX csum */
plat->hw_cap.tx_coe = AICMAC_RX_COE_TYPE2;
plat->hw_cap.rx_coe = AICMAC_RX_COE_TYPE2;
plat->hw_cap.rxfifo_over_2048 = 0;
/* TX and RX number of channels */
plat->hw_cap.number_rx_channel = 1;
plat->hw_cap.number_tx_channel = 1;
/* Alternate (enhanced) DESC mode */
plat->hw_cap.enh_desc = 1;
return 0;
}
int aicmac_mac_init(void *priv_ptr)
{
struct aicmac_priv *priv = priv_ptr;
struct aicmac_mac_data *mac_data = priv->plat->mac_data;
int ret = 0;
/* Initialize HW Interface */
ret = aicmac_mac_ip_init(priv);
if (ret)
return ret;
mac_data->pmt = priv->plat->hw_cap.pmt_remote_wake_up;
if (priv->plat->hw_cap.hash_tb_sz) {
mac_data->multicast_filter_bins =
(BIT(priv->plat->hw_cap.hash_tb_sz) << 5);
mac_data->mcast_bits_log2 =
ilog2(mac_data->multicast_filter_bins);
}
return 0;
}