brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · 0bce122 Raw
296 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved.4 *5 * Derived from Intel e1000 driver6 * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.7 */8 9#include <linux/netdevice.h>10#include <linux/ethtool.h>11#include <linux/slab.h>12 13#include "atl1c.h"14 15static int atl1c_get_link_ksettings(struct net_device *netdev,16				    struct ethtool_link_ksettings *cmd)17{18	struct atl1c_adapter *adapter = netdev_priv(netdev);19	struct atl1c_hw *hw = &adapter->hw;20	u32 supported, advertising;21 22	supported = (SUPPORTED_10baseT_Half  |23			   SUPPORTED_10baseT_Full  |24			   SUPPORTED_100baseT_Half |25			   SUPPORTED_100baseT_Full |26			   SUPPORTED_Autoneg       |27			   SUPPORTED_TP);28	if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M)29		supported |= SUPPORTED_1000baseT_Full;30 31	advertising = ADVERTISED_TP;32 33	advertising |= hw->autoneg_advertised;34 35	cmd->base.port = PORT_TP;36	cmd->base.phy_address = 0;37 38	if (adapter->link_speed != SPEED_0) {39		cmd->base.speed = adapter->link_speed;40		if (adapter->link_duplex == FULL_DUPLEX)41			cmd->base.duplex = DUPLEX_FULL;42		else43			cmd->base.duplex = DUPLEX_HALF;44	} else {45		cmd->base.speed = SPEED_UNKNOWN;46		cmd->base.duplex = DUPLEX_UNKNOWN;47	}48 49	cmd->base.autoneg = AUTONEG_ENABLE;50 51	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,52						supported);53	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,54						advertising);55 56	return 0;57}58 59static int atl1c_set_link_ksettings(struct net_device *netdev,60				    const struct ethtool_link_ksettings *cmd)61{62	struct atl1c_adapter *adapter = netdev_priv(netdev);63	struct atl1c_hw *hw = &adapter->hw;64	u16  autoneg_advertised;65 66	while (test_and_set_bit(__AT_RESETTING, &adapter->flags))67		msleep(1);68 69	if (cmd->base.autoneg == AUTONEG_ENABLE) {70		autoneg_advertised = ADVERTISED_Autoneg;71	} else {72		u32 speed = cmd->base.speed;73		if (speed == SPEED_1000) {74			if (cmd->base.duplex != DUPLEX_FULL) {75				if (netif_msg_link(adapter))76					dev_warn(&adapter->pdev->dev,77						"1000M half is invalid\n");78				clear_bit(__AT_RESETTING, &adapter->flags);79				return -EINVAL;80			}81			autoneg_advertised = ADVERTISED_1000baseT_Full;82		} else if (speed == SPEED_100) {83			if (cmd->base.duplex == DUPLEX_FULL)84				autoneg_advertised = ADVERTISED_100baseT_Full;85			else86				autoneg_advertised = ADVERTISED_100baseT_Half;87		} else {88			if (cmd->base.duplex == DUPLEX_FULL)89				autoneg_advertised = ADVERTISED_10baseT_Full;90			else91				autoneg_advertised = ADVERTISED_10baseT_Half;92		}93	}94 95	if (hw->autoneg_advertised != autoneg_advertised) {96		hw->autoneg_advertised = autoneg_advertised;97		if (atl1c_restart_autoneg(hw) != 0) {98			if (netif_msg_link(adapter))99				dev_warn(&adapter->pdev->dev,100					"ethtool speed/duplex setting failed\n");101			clear_bit(__AT_RESETTING, &adapter->flags);102			return -EINVAL;103		}104	}105	clear_bit(__AT_RESETTING, &adapter->flags);106	return 0;107}108 109static u32 atl1c_get_msglevel(struct net_device *netdev)110{111	struct atl1c_adapter *adapter = netdev_priv(netdev);112	return adapter->msg_enable;113}114 115static void atl1c_set_msglevel(struct net_device *netdev, u32 data)116{117	struct atl1c_adapter *adapter = netdev_priv(netdev);118	adapter->msg_enable = data;119}120 121static int atl1c_get_regs_len(struct net_device *netdev)122{123	return AT_REGS_LEN;124}125 126static void atl1c_get_regs(struct net_device *netdev,127			   struct ethtool_regs *regs, void *p)128{129	struct atl1c_adapter *adapter = netdev_priv(netdev);130	struct atl1c_hw *hw = &adapter->hw;131	u32 *regs_buff = p;132	u16 phy_data;133 134	memset(p, 0, AT_REGS_LEN);135 136	regs->version = 1;137	AT_READ_REG(hw, REG_PM_CTRL, 		  p++);138	AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL,  p++);139	AT_READ_REG(hw, REG_TWSI_CTRL, 		  p++);140	AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL,   p++);141	AT_READ_REG(hw, REG_MASTER_CTRL, 	  p++);142	AT_READ_REG(hw, REG_MANUAL_TIMER_INIT,    p++);143	AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);144	AT_READ_REG(hw, REG_GPHY_CTRL, 		  p++);145	AT_READ_REG(hw, REG_LINK_CTRL, 		  p++);146	AT_READ_REG(hw, REG_IDLE_STATUS, 	  p++);147	AT_READ_REG(hw, REG_MDIO_CTRL, 		  p++);148	AT_READ_REG(hw, REG_SERDES,		  p++);149	AT_READ_REG(hw, REG_MAC_CTRL, 		  p++);150	AT_READ_REG(hw, REG_MAC_IPG_IFG, 	  p++);151	AT_READ_REG(hw, REG_MAC_STA_ADDR, 	  p++);152	AT_READ_REG(hw, REG_MAC_STA_ADDR+4, 	  p++);153	AT_READ_REG(hw, REG_RX_HASH_TABLE, 	  p++);154	AT_READ_REG(hw, REG_RX_HASH_TABLE+4, 	  p++);155	AT_READ_REG(hw, REG_RXQ_CTRL, 		  p++);156	AT_READ_REG(hw, REG_TXQ_CTRL, 		  p++);157	AT_READ_REG(hw, REG_MTU, 		  p++);158	AT_READ_REG(hw, REG_WOL_CTRL, 		  p++);159 160	atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);161	regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data;162	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);163	regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data;164}165 166static int atl1c_get_eeprom_len(struct net_device *netdev)167{168	struct atl1c_adapter *adapter = netdev_priv(netdev);169 170	if (atl1c_check_eeprom_exist(&adapter->hw))171		return AT_EEPROM_LEN;172	else173		return 0;174}175 176static int atl1c_get_eeprom(struct net_device *netdev,177		struct ethtool_eeprom *eeprom, u8 *bytes)178{179	struct atl1c_adapter *adapter = netdev_priv(netdev);180	struct atl1c_hw *hw = &adapter->hw;181	u32 *eeprom_buff;182	int first_dword, last_dword;183	int ret_val = 0;184	int i;185 186	if (eeprom->len == 0)187		return -EINVAL;188 189	if (!atl1c_check_eeprom_exist(hw)) /* not exist */190		return -EINVAL;191 192	eeprom->magic = adapter->pdev->vendor |193			(adapter->pdev->device << 16);194 195	first_dword = eeprom->offset >> 2;196	last_dword = (eeprom->offset + eeprom->len - 1) >> 2;197 198	eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32),199				    GFP_KERNEL);200	if (eeprom_buff == NULL)201		return -ENOMEM;202 203	for (i = first_dword; i < last_dword; i++) {204		if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {205			kfree(eeprom_buff);206			return -EIO;207		}208	}209 210	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),211			eeprom->len);212	kfree(eeprom_buff);213 214	return ret_val;215	return 0;216}217 218static void atl1c_get_drvinfo(struct net_device *netdev,219		struct ethtool_drvinfo *drvinfo)220{221	struct atl1c_adapter *adapter = netdev_priv(netdev);222 223	strscpy(drvinfo->driver,  atl1c_driver_name, sizeof(drvinfo->driver));224	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),225		sizeof(drvinfo->bus_info));226}227 228static void atl1c_get_wol(struct net_device *netdev,229			  struct ethtool_wolinfo *wol)230{231	struct atl1c_adapter *adapter = netdev_priv(netdev);232 233	wol->supported = WAKE_MAGIC | WAKE_PHY;234	wol->wolopts = 0;235 236	if (adapter->wol & AT_WUFC_EX)237		wol->wolopts |= WAKE_UCAST;238	if (adapter->wol & AT_WUFC_MC)239		wol->wolopts |= WAKE_MCAST;240	if (adapter->wol & AT_WUFC_BC)241		wol->wolopts |= WAKE_BCAST;242	if (adapter->wol & AT_WUFC_MAG)243		wol->wolopts |= WAKE_MAGIC;244	if (adapter->wol & AT_WUFC_LNKC)245		wol->wolopts |= WAKE_PHY;246}247 248static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)249{250	struct atl1c_adapter *adapter = netdev_priv(netdev);251 252	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |253			    WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))254		return -EOPNOTSUPP;255	/* these settings will always override what we currently have */256	adapter->wol = 0;257 258	if (wol->wolopts & WAKE_MAGIC)259		adapter->wol |= AT_WUFC_MAG;260	if (wol->wolopts & WAKE_PHY)261		adapter->wol |= AT_WUFC_LNKC;262 263	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);264 265	return 0;266}267 268static int atl1c_nway_reset(struct net_device *netdev)269{270	struct atl1c_adapter *adapter = netdev_priv(netdev);271	if (netif_running(netdev))272		atl1c_reinit_locked(adapter);273	return 0;274}275 276static const struct ethtool_ops atl1c_ethtool_ops = {277	.get_drvinfo            = atl1c_get_drvinfo,278	.get_regs_len           = atl1c_get_regs_len,279	.get_regs               = atl1c_get_regs,280	.get_wol                = atl1c_get_wol,281	.set_wol                = atl1c_set_wol,282	.get_msglevel           = atl1c_get_msglevel,283	.set_msglevel           = atl1c_set_msglevel,284	.nway_reset             = atl1c_nway_reset,285	.get_link               = ethtool_op_get_link,286	.get_eeprom_len         = atl1c_get_eeprom_len,287	.get_eeprom             = atl1c_get_eeprom,288	.get_link_ksettings     = atl1c_get_link_ksettings,289	.set_link_ksettings     = atl1c_set_link_ksettings,290};291 292void atl1c_set_ethtool_ops(struct net_device *netdev)293{294	netdev->ethtool_ops = &atl1c_ethtool_ops;295}296