Rtwo/kernel/motorola/sm8550/drivers/net/usb/ax88179a_772d.c
2025-09-30 19:22:48 -05:00

1231 lines
30 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
* Copyright (c) 2022 ASIX Electronic Corporation 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 as published by the Free Software
* Foundation, either version 2 of the License, or (at your option) any later
* version.
*
* 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
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
******************************************************************************/
#include "ax_main.h"
#include "ax88179a_772d.h"
struct _ax_buikin_setting AX88179A_BULKIN_SIZE[] = {
{5, 0x7B, 0x00, 0x18, 0x0F}, //1G, SS
{5, 0xC0, 0x02, 0x06, 0x0F}, //1G, HS
{7, 0xF0, 0x00, 0x0C, 0x0F}, //100M, Full, SS
{6, 0x00, 0x00, 0x06, 0x0F}, //100M, Half, SS
{5, 0xC0, 0x04, 0x06, 0x0F}, //100M, Full, HS
{7, 0xC0, 0x04, 0x06, 0x0F}, //100M, Half, HS
{7, 0x00, 0, 0x03, 0x3F}, //FS
};
static int ax88179a_chk_eee(struct ax_device *axdev)
{
struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
mii_ethtool_gset(&axdev->mii, &ecmd);
if (ecmd.speed == SPEED_1000) {
int eee_lp, eee_cap, eee_adv;
u32 lp, cap, adv;
eee_cap = ax_mmd_read(axdev->netdev,
MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
if (eee_cap < 0)
goto exit;
eee_cap &= ~MDIO_EEE_100TX;
cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap);
if (!cap)
goto exit;
eee_lp = ax_mmd_read(axdev->netdev,
MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
if (eee_lp < 0)
goto exit;
eee_adv = ax_mmd_read(axdev->netdev,
MDIO_MMD_AN, MDIO_AN_EEE_ADV);
if (eee_adv < 0)
goto exit;
adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
if (!(lp & adv & SUPPORTED_1000baseT_Full))
goto exit;
axdev->eee_active = 1;
return true;
}
exit:
axdev->eee_active = 0;
return false;
}
static int ax88179a_ethtool_get_eee(struct ax_device *axdev,
struct ethtool_eee *data)
{
int val;
val = ax_mmd_read(axdev->netdev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
if (val < 0)
return val;
val &= ~MDIO_EEE_100TX;
data->supported = mmd_eee_cap_to_ethtool_sup_t(val);
val = ax_mmd_read(axdev->netdev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
if (val < 0)
return val;
data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
val = ax_mmd_read(axdev->netdev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
if (val < 0)
return val;
data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
return 0;
}
static int ax88179a_get_eee(struct net_device *net, struct ethtool_eee *edata)
{
struct ax_device *axdev = netdev_priv(net);
edata->eee_enabled = axdev->eee_enabled;
edata->eee_active = axdev->eee_active;
return ax88179a_ethtool_get_eee(axdev, edata);
}
static void ax88179a_eee_setting(struct ax_device *axdev, bool enable)
{
ax_write_cmd(axdev, AX88179_GPHY_CTRL, AX_GPHY_EEE_CTRL,
enable, 0, NULL);
}
static int ax88179a_set_eee(struct net_device *net, struct ethtool_eee *edata)
{
struct ax_device *axdev = netdev_priv(net);
if (edata->advertised & MDIO_EEE_100TX)
return -EOPNOTSUPP;
axdev->eee_enabled = edata->eee_enabled;
ax88179a_eee_setting(axdev, axdev->eee_enabled);
if (axdev->eee_enabled) {
axdev->eee_enabled = ax88179a_chk_eee(axdev);
if (!axdev->eee_enabled) {
ax88179a_eee_setting(axdev, false);
return -EOPNOTSUPP;
}
}
mii_nway_restart(&axdev->mii);
return 0;
}
static int ax88179a_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *coalesce,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct ax_device *axdev = netdev_priv(netdev);
coalesce->rx_coalesce_usecs = axdev->coalesce;
return 0;
}
static u16 ax88179a_usec_to_bin_timer(struct ax_device *axdev)
{
u16 speed_multiple;
switch (axdev->link_info.eth_speed) {
case ETHER_LINK_10:
speed_multiple = 100;
break;
case ETHER_LINK_100:
speed_multiple = 10;
break;
case ETHER_LINK_1000:
default:
speed_multiple = 1;
break;
}
return (axdev->coalesce * US_TO_NS * speed_multiple) /
AX88179A_BIN_TIMER_UINT;
}
static u32 ax88179a_bin_timer_to_usec(struct ax_device *axdev, u16 timer)
{
u16 speed_multiple;
switch (axdev->link_info.eth_speed) {
case ETHER_LINK_10:
speed_multiple = 100;
break;
case ETHER_LINK_100:
speed_multiple = 10;
break;
case ETHER_LINK_1000:
default:
speed_multiple = 1;
break;
}
return (AX88179A_BIN_TIMER_UINT * timer) / (US_TO_NS * speed_multiple);
}
static int ax88179a_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *coalesce,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct ax_device *axdev = netdev_priv(netdev);
u16 timer;
int ret = 0;
ret = usb_autopm_get_interface(axdev->intf);
if (ret < 0)
return ret;
axdev->coalesce = coalesce->rx_coalesce_usecs;
timer = ax88179a_usec_to_bin_timer(axdev);
if (timer > 0) {
timer &= 0x7FFF;
ax_write_cmd(axdev, AX_ACCESS_MAC, AX_RX_BULKIN_QTIMR_LOW, 2, 2, &timer);
}
usb_autopm_put_interface(axdev->intf);
return ret;
}
const struct ethtool_ops ax88179a_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
.get_drvinfo = ax_get_drvinfo,
.get_link_ksettings = ax_get_link_ksettings,
.set_link_ksettings = ax_set_link_ksettings,
.get_link = ethtool_op_get_link,
.get_msglevel = ax_get_msglevel,
.set_msglevel = ax_set_msglevel,
.get_wol = ax_get_wol,
.set_wol = ax_set_wol,
.get_eee = ax88179a_get_eee,
.set_eee = ax88179a_set_eee,
.get_coalesce = ax88179a_get_coalesce,
.set_coalesce = ax88179a_set_coalesce,
.get_strings = ax_get_strings,
.get_sset_count = ax_get_sset_count,
.get_ethtool_stats = ax_get_ethtool_stats,
.get_pauseparam = ax_get_pauseparam,
.set_pauseparam = ax_set_pauseparam,
.get_regs_len = ax_get_regs_len,
.get_regs = ax_get_regs,
.get_ts_info = ethtool_op_get_ts_info,
};
void ax88179a_get_fw_version(struct ax_device *axdev)
{
int i;
for (i = 0; i < 3; i++) {
if (ax_read_cmd(axdev, AX88179A_ACCESS_BL, (0xFD + i),
1, 1, &axdev->fw_version[i], 1) < 0)
axdev->fw_version[i] = ~0;
}
if (ax_read_cmd(axdev, AX88179A_ACCESS_BL, AX88179A_SW_REVERSION,
1, 1, &axdev->fw_version[3], 0) < 0)
axdev->fw_version[3] = ~0;
else
axdev->fw_version[3] &= 0xF;
}
int ax88179a_signature(struct ax_device *axdev, struct _ax_ioctl_command *info)
{
strscpy(info->sig, AX88179A_SIGNATURE, sizeof(info->sig));
return 0;
}
int ax88179a_read_version(struct ax_device *axdev,
struct _ax_ioctl_command *info)
{
unsigned char temp[16] = {0};
snprintf(temp, sizeof(temp), "v%d.%d.%d.%d", axdev->fw_version[0],
axdev->fw_version[1], axdev->fw_version[2], axdev->fw_version[3]);
memcpy(&info->version.version, temp, 16);
return 0;
}
int ax88179a_write_flash(struct ax_device *axdev,
struct _ax_ioctl_command *info)
{
int i, ret;
u8 *buf = NULL;
u8 *block;
buf = kzalloc(256, GFP_KERNEL);
if (!buf)
return -ENOMEM;
block = buf;
ret = ax_write_cmd(axdev, AX88179A_FLASH_WEN, 0, 0, 0, NULL);
if (ret < 0) {
netdev_err(axdev->netdev, "Flash write enable failed");
info->flash.status = -ERR_FALSH_WRITE_EN;
goto out;
}
for (i = info->flash.offset;
i < (info->flash.length + info->flash.offset);
i += 256) {
if (copy_from_user(block,
(void __user *)&info->flash.buf[i], 256)) {
ret = -EFAULT;
goto out;
}
ret = ax_write_cmd(axdev, AX88179A_FLASH_WRITE,
(u16)((i >> 16) & 0xFFFF),
(u16)(i & 0xFFFF), 256, block);
if (ret < 0) {
info->flash.status = -ERR_FALSH_WRITE;
goto out;
}
}
ret = ax_write_cmd(axdev, AX88179A_FLASH_WDIS, 0, 0, 0, NULL);
if (ret < 0) {
netdev_err(axdev->netdev, "Flash write disable failed");
info->flash.status = -ERR_FALSH_WRITE_DIS;
return ret;
}
out:
kfree(buf);
return ret;
}
int ax88179a_read_flash(struct ax_device *axdev, struct _ax_ioctl_command *info)
{
int i, ret = 0;
void *buf = NULL;
u8 *block;
buf = kzalloc(256, GFP_KERNEL);
if (!buf)
return -ENOMEM;
block = buf;
for (i = info->flash.offset;
i < (info->flash.length + info->flash.offset);
i += 256) {
ret = ax_read_cmd(axdev, AX88179A_FLASH_READ,
(u16)((i >> 16) & 0xFFFF),
(u16)(i & 0xFFFF), 256, block, 0);
if (ret < 0) {
info->flash.status = -ERR_FALSH_READ;
break;
}
if (copy_to_user((void __user *)&info->flash.buf[i],
block, 256)) {
ret = -EFAULT;
break;
}
}
kfree(buf);
return ret;
}
int ax88179a_program_efuse(struct ax_device *axdev,
struct _ax_ioctl_command *info)
{
int ret = 0;
u16 offset = (u16)(info->flash.offset * 16);
u8 buf[20] = {0};
ret = copy_from_user(buf, (void __user *)info->flash.buf, 20);
if (ret)
return ret;
ret = ax_write_cmd(axdev, AX_ACCESS_EFUSE, offset, 0, 20, buf);
if (ret < 0) {
info->flash.status = -ERR_EFUSE_WRITE;
return ret;
}
return ret;
}
int ax88179a_dump_efuse(struct ax_device *axdev, struct _ax_ioctl_command *info)
{
int ret = 0;
u16 offset = (u16)(info->flash.offset * 16);
u8 buf[20] = {0};
ret = ax_read_cmd(axdev, AX_ACCESS_EFUSE, offset, 0, 20, buf, 0);
if (ret < 0) {
info->flash.status = -ERR_EFUSE_READ;
return ret;
}
ret = copy_to_user((void __user *)info->flash.buf, buf, 20);
return ret;
}
int ax88179a_boot_to_rom(struct ax_device *axdev,
struct _ax_ioctl_command *info)
{
usb_control_msg(axdev->udev, usb_sndctrlpipe(axdev->udev, 0),
AX88179A_BOOT_TO_ROM,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x5A5A, 0xA5A5, NULL, 0, 1);
return 0;
}
int ax88179a_erase_flash(struct ax_device *axdev,
struct _ax_ioctl_command *info)
{
int ret = 0;
ret = ax_write_cmd(axdev, AX88179A_FLASH_WEN, 0, 0, 0, NULL);
if (ret < 0) {
netdev_err(axdev->netdev, "Flash write enable failed");
info->flash.status = -ERR_FALSH_WRITE_EN;
return ret;
}
ret = usb_control_msg(axdev->udev, usb_sndctrlpipe(axdev->udev, 0),
AX88179A_FLASH_EARSE_ALL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, 0, NULL, 0, 300000);
if (ret < 0) {
netdev_err(axdev->netdev, "Flash erase all failed");
info->flash.status = -ERR_FALSH_ERASE_ALL;
return ret;
}
ret = ax_write_cmd(axdev, AX88179A_FLASH_WDIS, 0, 0, 0, NULL);
if (ret < 0) {
netdev_err(axdev->netdev, "Flash write disable failed");
info->flash.status = -ERR_FALSH_WRITE_DIS;
return ret;
}
return 0;
}
int ax88179a_sw_reset(struct ax_device *axdev, struct _ax_ioctl_command *info)
{
void *buf = NULL;
buf = kzalloc(sizeof(u32), GFP_KERNEL);
if (!buf)
return -ENOMEM;
*((u32 *)buf) = 1;
usb_control_msg(axdev->udev, usb_sndctrlpipe(axdev->udev, 0), 0x10,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x18E8, 0x000F, buf, 4, 10);
kfree(buf);
return 0;
}
IOCTRL_TABLE ax88179a_tbl[] = {
ax88179a_signature,
ax_usb_command,
ax88179a_read_version,
ax88179a_write_flash,
ax88179a_boot_to_rom,
ax88179a_erase_flash,
ax88179a_sw_reset,
ax88179a_read_flash,
ax88179a_program_efuse,
ax88179a_dump_efuse,
};
int ax88179a_siocdevprivate(struct net_device *netdev, struct ifreq *rq,
void __user *udata, int cmd)
{
struct ax_device *axdev = netdev_priv(netdev);
struct _ax_ioctl_command info;
void __user *uptr = (void __user *)rq->ifr_data;
int ret = 0;
switch (cmd) {
case AX_PRIVATE:
if (copy_from_user(&info, uptr,
sizeof(struct _ax_ioctl_command))) {
netdev_err(netdev, "copy_from_user, return -EFAULT");
return -EFAULT;
}
ret = (*ax88179a_tbl[info.ioctl_cmd])(axdev, &info);
if (ret < 0) {
netdev_err(netdev, "ax88179a_tbl, return %d", ret);
return ret;
}
if (copy_to_user(uptr, &info,
sizeof(struct _ax_ioctl_command))) {
netdev_err(netdev, "copy_to_user, return -EFAULT");
return -EFAULT;
}
break;
default:
ret = -EOPNOTSUPP;
}
return ret;
}
int ax88179a_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
{
struct ax_device *axdev = netdev_priv(netdev);
switch (cmd) {
}
return generic_mii_ioctl(&axdev->mii, if_mii(rq), cmd, NULL);
}
static int ax88179a_autodetach(struct ax_device *axdev)
{
u16 value = ((axdev->autodetach) ? 1 : 0) | AX88179A_AUTODETACH_DELAY;
return ax_write_cmd(axdev, AX88179A_AUTODETACH, value, 0, 0, NULL);
}
static bool ax88179a_check_phy_power(struct ax_device *axdev)
{
u8 reg8 = 0;
int ret = 0;
ret = ax_read_cmd_nopm(axdev, AX88179A_PHY_POWER, 0, 0, 1, &reg8, 1);
if (ret < 0)
return false;
return (reg8 & AX_PHY_POWER);
}
static int ax88179a_set_phy_power(struct ax_device *axdev, bool on)
{
u8 reg8;
int ret;
reg8 = (on) ? AX_PHY_POWER : 0;
ret = ax_write_cmd_nopm(axdev, AX88179A_PHY_POWER, 0, 0, 1, &reg8);
if (ret < 0)
return ret;
msleep(250);
return 0;
}
static int ax88179a_bind(struct ax_device *axdev)
{
struct net_device *netdev = axdev->netdev;
u16 wvalue = 0;
int ret;
ax88179a_get_fw_version(axdev);
ax_print_version(axdev, AX_DRIVER_STRING_179A_772D);
ret = ax_write_cmd(axdev, AX_FW_MODE, AX_FW_MODE_179A, wvalue, 0, NULL);
if (ret < 0)
return ret;
ret = ax_write_cmd(axdev, AX_RELOAD_FLASH_EFUSE, 0, 0, 0, NULL);
if (ret < 0)
return ret;
netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_TX;
netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_RXALL;
netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
NETIF_F_IPV6_CSUM;
axdev->tx_casecade_size = TX_CASECADES_SIZE;
axdev->gso_max_size = AX_GSO_DEFAULT_SIZE;
axdev->mii.supports_gmii = true;
axdev->mii.dev = netdev;
axdev->mii.mdio_read = ax_mdio_read;
axdev->mii.mdio_write = ax_mdio_write;
axdev->mii.phy_id_mask = 0x3F;
axdev->mii.reg_num_mask = 0x1F;
axdev->mii.phy_id = AX88179A_PHY_ID;
netif_set_gso_max_size(netdev, axdev->gso_max_size);
axdev->bin_setting.custom = false;
axdev->tx_align_len = 8;
axdev->coalesce = 0;
netdev->ethtool_ops = &ax88179a_ethtool_ops;
axdev->netdev->netdev_ops = &ax88179a_netdev_ops;
return ret;
}
static void ax88179a_unbind(struct ax_device *axdev)
{
}
static int ax88179a_stop(struct ax_device *axdev)
{
u16 reg16;
reg16 = AX_RX_CTL_STOP;
ax_write_cmd(axdev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
ax88179a_set_phy_power(axdev, false);
return 0;
}
void ax88179a_set_multicast(struct net_device *netdev)
{
struct ax_device *axdev = netdev_priv(netdev);
u8 *m_filter = axdev->m_filter;
int mc_count = 0;
if (!test_bit(AX_ENABLE, &axdev->flags))
return;
mc_count = netdev_mc_count(netdev);
axdev->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB);
if (netdev->flags & IFF_PROMISC) {
axdev->rxctl |= AX_RX_CTL_PRO;
} else if (netdev->flags & IFF_ALLMULTI || mc_count > AX_MAX_MCAST) {
axdev->rxctl |= AX_RX_CTL_AMALL;
} else if (netdev_mc_empty(netdev)) {
} else {
u32 crc_bits;
struct netdev_hw_addr *ha = NULL;
memset(m_filter, 0, AX_MCAST_FILTER_SIZE);
netdev_for_each_mc_addr(ha, netdev) {
crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
*(m_filter + (crc_bits >> 3)) |= 1 << (crc_bits & 7);
}
ax_write_cmd_async(axdev, AX_ACCESS_MAC, AX_MULTI_FILTER_ARRY,
AX_MCAST_FILTER_SIZE, AX_MCAST_FILTER_SIZE, m_filter);
axdev->rxctl |= AX_RX_CTL_AM;
}
ax_write_cmd_async(axdev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &axdev->rxctl);
}
static int ax88179a_hw_init(struct ax_device *axdev)
{
u16 reg16;
u8 reg8;
int ret;
ret = ax88179a_set_phy_power(axdev, true);
if (ret < 0)
return ret;
msleep(250);
ret = ax88179a_autodetach(axdev);
if (ret < 0)
return ret;
reg8 = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = AX_MAC_EFF_EN;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL,
1, 1, &reg8);
if (ret < 0)
return ret;
reg16 = 0;
ret = ax_write_cmd_async(axdev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &reg16);
if (ret < 0)
return ret;
reg8 = 0x04;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = 0x10;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = 0;
if (axdev->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
reg8 |= AX_VLAN_CONTROL_VFE;
if (axdev->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
reg8 |= AX_VLAN_CONTROL_VSO;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = 0xff;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = 0;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 1, &reg8);
if (ret < 0)
return ret;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 1, &reg8);
if (ret < 0)
return ret;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 1, &reg8);
if (ret < 0)
return ret;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 1, &reg8);
if (ret < 0)
return ret;
reg8 = AX_TXHDR_CKSUM_EN;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 1, &reg8);
if (ret < 0)
return ret;
reg16 = AX_RX_CTL_START | AX_RX_CTL_AP | AX_RX_CTL_AMALL |
AX_RX_CTL_AB | AX_RX_CTL_DROPCRCERR;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_RX_CTL, 1, 1, &reg16);
if (ret < 0)
return ret;
reg8 = 0;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 1, &reg8);
if (ret < 0)
return ret;
ret = ax_read_cmd(axdev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, &reg8, 1);
if (ret < 0)
return ret;
reg8 &= 0xE0;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, &reg8);
if (ret < 0)
return ret;
ret = ax_read_cmd(axdev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16, 2);
if (ret < 0)
return ret;
reg16 &= ~AX_MEDIUM_GIGAMODE;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
if (ret < 0)
return ret;
reg16 |= AX_MEDIUM_GIGAMODE;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
if (ret < 0)
return ret;
reg8 = 0;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 1, &reg8);
if (ret < 0)
return ret;
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_CDC_ECM_CTRL, 1, 1, &reg8);
if (ret < 0)
return ret;
ax_set_tx_qlen(axdev);
return ret;
}
static int ax88179a_get_ether_link(struct ax_device *axdev)
{
struct ax_link_info *link_info = &axdev->link_info;
struct ethtool_link_ksettings cmd;
mii_ethtool_get_link_ksettings(&axdev->mii, &cmd);
switch (cmd.base.speed) {
case SPEED_1000:
link_info->eth_speed = ETHER_LINK_1000;
break;
case SPEED_100:
link_info->eth_speed = ETHER_LINK_100;
break;
case SPEED_10:
default:
link_info->eth_speed = ETHER_LINK_10;
break;
}
link_info->full_duplex = cmd.base.duplex;
return 0;
}
static int ax88179a_set_bulkin_setting(struct ax_device *axdev)
{
struct ax_link_info *link_info = &axdev->link_info;
u8 link_sts;
int index = 0, ret;
ret = ax_read_cmd_nopm(axdev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts, 0);
if (ret < 0)
return ret;
switch (link_info->eth_speed) {
case ETHER_LINK_1000:
if (link_sts & AX_USB_SS)
index = 0;
else if (link_sts & AX_USB_HS)
index = 1;
break;
case ETHER_LINK_100:
if (link_sts & AX_USB_SS)
index = 2;
else if (link_sts & AX_USB_HS)
index = 4;
if (!link_info->full_duplex)
index++;
break;
case ETHER_LINK_10:
index = 6;
break;
case ETHER_LINK_NONE:
default:
index = 0;
break;
}
if (axdev->coalesce == 0) {
u16 timer = *((u16 *)&AX88179A_BULKIN_SIZE[index].timer_l);
axdev->coalesce = ax88179a_bin_timer_to_usec(axdev, timer);
} else {
u16 timer = ax88179a_usec_to_bin_timer(axdev);
memcpy(&AX88179A_BULKIN_SIZE[index].timer_l, &timer, 2);
}
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL,
5, 5, &AX88179A_BULKIN_SIZE[index]);
if (ret < 0)
return ret;
return 0;
}
static int ax88179a_link_setting(struct ax_device *axdev)
{
struct ax_link_info *link_info = &axdev->link_info;
struct ethtool_pauseparam pause;
u16 medium_mode, reg16;
u8 reg8[3];
int ret;
reg16 = AX_RX_CTL_STOP;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &reg16);
if (ret < 0)
return ret;
reg8[0] = 0;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 1, reg8);
if (ret < 0)
return ret;
reg8[0] = 0xA5;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX88179A_MAC_CDC_DELAY_TX, 1, 1, reg8);
if (ret < 0)
return ret;
reg8[0] = 0x10;
reg8[1] = 0x04;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 2, 2, reg8);
if (ret < 0)
return ret;
memset(&pause, 0, sizeof(pause));
ax_get_pauseparam(axdev->netdev, &pause);
medium_mode = AX_MEDIUM_RECEIVE_EN;
if (pause.rx_pause)
medium_mode |= AX_MEDIUM_RXFLOW_CTRLEN;
if (pause.tx_pause) {
medium_mode |= AX_MEDIUM_TXFLOW_CTRLEN;
reg8[0] = 0x28 | AX_NEW_PAUSE_EN;
} else {
reg8[0] = 0;
}
ret = ax_write_cmd(axdev, AX_ACCESS_MAC, AX88179A_NEW_PAUSE_CTRL,
1, 1, reg8);
if (ret < 0)
return ret;
switch (link_info->eth_speed) {
case ETHER_LINK_1000:
case ETHER_LINK_100:
reg8[0] = 0x78;
reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
reg8[2] = 0;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC,
AX88178A_MAC_RX_STATUS_CDC, 3, 3, reg8);
if (ret < 0)
return ret;
reg8[0] = 0x40;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC,
AX88179A_MAC_RX_DATA_CDC_CNT,
1, 1, reg8);
if (ret < 0)
return ret;
if (link_info->eth_speed == ETHER_LINK_1000)
medium_mode |= AX_MEDIUM_GIGAMODE;
break;
case ETHER_LINK_10:
reg8[0] = 0xFA;
reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
reg8[2] = 0xFF;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC,
AX88178A_MAC_RX_STATUS_CDC, 3, 3, reg8);
if (ret < 0)
return ret;
reg8[0] = 0xFA;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC,
AX88179A_MAC_RX_DATA_CDC_CNT,
1, 1, reg8);
if (ret < 0)
return ret;
break;
default:
break;
}
reg8[0] = 0;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX88179A_BFM_DATA,
1, 1, reg8);
if (ret < 0)
return ret;
ret = ax88179a_set_bulkin_setting(axdev);
if (ret < 0)
return ret;
if (link_info->full_duplex)
medium_mode |= AX_MEDIUM_FULL_DUPLEX;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
2, 2, &medium_mode);
if (ret < 0)
return ret;
axdev->rxctl |= AX_RX_CTL_START | AX_RX_CTL_AB;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX_RX_CTL,
2, 2, &axdev->rxctl);
if (ret < 0)
return ret;
reg8[0] = AX_MAC_RX_PATH_READY | AX_MAC_TX_PATH_READY;
ret = ax_write_cmd_nopm(axdev, AX_ACCESS_MAC, AX88179A_MAC_PATH,
1, 1, reg8);
if (ret < 0)
return ret;
return 0;
}
static int ax88179a_link_reset(struct ax_device *axdev)
{
int ret;
ret = ax88179a_get_ether_link(axdev);
if (ret < 0)
return ret;
ret = ax88179a_link_setting(axdev);
if (ret < 0)
return ret;
axdev->eee_enabled = ax88179a_chk_eee(axdev);
ax88179a_eee_setting(axdev, axdev->eee_enabled);
return 0;
}
inline void ax88179a_rx_checksum(struct sk_buff *skb, void *pkt_hdr)
{
struct _179a_rx_pkt_header *hdr = (struct _179a_rx_pkt_header *)pkt_hdr;
skb->ip_summed = CHECKSUM_NONE;
if (hdr->L4_err || hdr->L3_err)
return;
if (hdr->L4_pkt_type == AX_RXHDR_L4_TYPE_TCP || hdr->L4_pkt_type == AX_RXHDR_L4_TYPE_UDP)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
static void ax88179a_rx_fixup(struct ax_device *axdev, struct rx_desc *desc,
int *work_done, int budget)
{
struct napi_struct *napi = &axdev->napi;
struct net_device *netdev = axdev->netdev;
struct net_device_stats *stats = ax_get_stats(netdev);
struct _179a_rx_pkt_header *pkt_hdr;
struct _179a_rx_header *rx_header;
const u32 actual_length = desc->urb->actual_length;
u8 *rx_data;
u32 aa = 0, rx_hdroffset = 0;
u16 pkt_count = 0;
rx_header = (struct _179a_rx_header *)
(((u8 *)desc->head) + actual_length - AX88179A_RX_HEADER_SIZE);
le64_to_cpus(rx_header);
rx_hdroffset = rx_header->hdr_off;
pkt_count = rx_header->pkt_cnt;
aa = (actual_length - ((pkt_count + 1) * 8));
if ((aa != rx_hdroffset && ((aa - rx_hdroffset) % 16) != 0) ||
rx_hdroffset >= desc->urb->actual_length ||
pkt_count == 0) {
desc->urb->actual_length = 0;
stats->rx_length_errors++;
return;
}
pkt_hdr = (struct _179a_rx_pkt_header *)
(((u8 *)desc->head) + rx_header->hdr_off);
rx_data = desc->head;
while (pkt_count--) {
struct sk_buff *skb;
u32 pkt_len = 0;
le64_to_cpus(pkt_hdr);
if (!pkt_hdr->rx_ok) {
stats->rx_crc_errors++;
if (!(netdev->features & NETIF_F_RXALL))
goto find_next_rx;
}
if (pkt_hdr->drop) {
stats->rx_dropped++;
if (!(netdev->features & NETIF_F_RXALL))
goto find_next_rx;
}
pkt_len = (u32)(pkt_hdr->length & 0x7FFF);
skb = napi_alloc_skb(napi, pkt_len);
if (!skb) {
stats->rx_dropped++;
goto find_next_rx;
}
skb_put(skb, pkt_len);
memcpy(skb->data, rx_data, pkt_len);
ax88179a_rx_checksum(skb, pkt_hdr);
skb->truesize = skb->len + sizeof(struct sk_buff);
if (pkt_hdr->vlan_ind)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
pkt_hdr->vlan_tag & VLAN_VID_MASK);
skb->protocol = eth_type_trans(skb, netdev);
if (*work_done < budget) {
napi_gro_receive(napi, skb);
*work_done += 1;
stats->rx_packets++;
stats->rx_bytes += pkt_len;
} else {
__skb_queue_tail(&axdev->rx_queue, skb);
}
find_next_rx:
rx_data += (pkt_len + 7) & 0x7FFF8;
pkt_hdr++;
}
}
static int ax88179a_tx_fixup(struct ax_device *axdev, struct tx_desc *desc)
{
struct sk_buff_head skb_head, *tx_queue;
struct net_device_stats *stats = &axdev->netdev->stats;
int remain, ret;
int endpoint = 3;
u8 *tx_data;
tx_queue = axdev->tx_queue;
__skb_queue_head_init(&skb_head);
spin_lock(&tx_queue->lock);
skb_queue_splice_init(tx_queue, &skb_head);
spin_unlock(&tx_queue->lock);
tx_data = desc->head;
desc->skb_num = 0;
desc->skb_len = 0;
remain = axdev->tx_casecade_size;
while (remain >= (ETH_ZLEN + AX88179A_TX_HEADER_SIZE)) {
struct sk_buff *skb;
struct _179a_tx_pkt_header *tx_hdr;
u16 tci = 0;
skb = __skb_dequeue(&skb_head);
if (!skb)
break;
if ((skb->len + AX88179A_TX_HEADER_SIZE) > remain &&
(skb_shinfo(skb)->gso_size == 0)) {
__skb_queue_head(&skb_head, skb);
break;
}
tx_hdr = (struct _179a_tx_pkt_header *)tx_data;
memset(tx_hdr, 0, AX88179A_TX_HEADER_SIZE);
tx_hdr->length = (skb->len & 0x1FFFFF);
tx_hdr->checksum = AX88179A_TX_HERDER_CHKSUM(tx_hdr->length);
tx_hdr->max_seg_size = skb_shinfo(skb)->gso_size;
if ((axdev->netdev->features & NETIF_F_HW_VLAN_CTAG_TX) &&
(vlan_get_tag(skb, &tci) >= 0)) {
tx_hdr->vlan_tag = 1;
tx_hdr->vlan_info = tci;
}
cpu_to_le64s(tx_hdr);
tx_data += AX88179A_TX_HEADER_SIZE;
if (skb_copy_bits(skb, 0, tx_data, skb->len) < 0) {
stats->tx_dropped += skb_shinfo(skb)->gso_segs ?: 1;
dev_kfree_skb_any(skb);
continue;
}
tx_data = __tx_buf_align((void *)(tx_data + skb->len),
axdev->tx_align_len);
desc->skb_len += skb->len;
desc->skb_num += skb_shinfo(skb)->gso_segs ?: 1;
dev_kfree_skb_any(skb);
if (tx_hdr->max_seg_size)
break;
remain = axdev->tx_casecade_size -
(int)((void *)tx_data - desc->head);
}
if (!skb_queue_empty(&skb_head)) {
spin_lock(&tx_queue->lock);
skb_queue_splice(&skb_head, tx_queue);
spin_unlock(&tx_queue->lock);
}
netif_tx_lock(axdev->netdev);
if (netif_queue_stopped(axdev->netdev) &&
skb_queue_len(tx_queue) < axdev->tx_qlen) {
netif_wake_queue(axdev->netdev);
}
netif_tx_unlock(axdev->netdev);
ret = usb_autopm_get_interface_async(axdev->intf);
if (ret < 0)
return ret;
usb_fill_bulk_urb(desc->urb, axdev->udev,
usb_sndbulkpipe(axdev->udev, endpoint),
desc->head, (int)(tx_data - (u8 *)desc->head),
(usb_complete_t)ax_write_bulk_callback, desc);
ret = usb_submit_urb(desc->urb, GFP_ATOMIC);
if (ret < 0)
usb_autopm_put_interface_async(axdev->intf);
return 0;
}
static int ax88179a_system_suspend(struct ax_device *axdev)
{
return 0;
}
static int ax88179a_system_resume(struct ax_device *axdev)
{
int ret;
if (!ax88179a_check_phy_power(axdev))
ax88179a_set_phy_power(axdev, true);
ret = ax_write_cmd_nopm(axdev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
if (ret < 0)
return ret;
axdev->driver_info->hw_init(axdev);
return 0;
}
const struct driver_info ax88179a_info = {
.bind = ax88179a_bind,
.unbind = ax88179a_unbind,
.hw_init = ax88179a_hw_init,
.stop = ax88179a_stop,
.link_reset = ax88179a_link_reset,
.rx_fixup = ax88179a_rx_fixup,
.tx_fixup = ax88179a_tx_fixup,
.system_suspend = ax88179a_system_suspend,
.system_resume = ax88179a_system_resume,
.napi_weight = AX88179A_NAPI_WEIGHT,
.buf_rx_size = AX88179A_BUF_RX_SIZE,
};
const struct driver_info ax88772d_info = {
.bind = ax88179a_bind,
.unbind = ax88179a_unbind,
.hw_init = ax88179a_hw_init,
.stop = ax88179a_stop,
.link_reset = ax88179a_link_reset,
.rx_fixup = ax88179a_rx_fixup,
.tx_fixup = ax88179a_tx_fixup,
.system_suspend = ax88179a_system_suspend,
.system_resume = ax88179a_system_resume,
.napi_weight = AX88179A_NAPI_WEIGHT,
.buf_rx_size = AX88179A_BUF_RX_SIZE,
};