Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/*
  2 * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved.
  3 *
  4 * Derived from Intel e1000 driver
  5 * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
  6 *
  7 * This program is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU General Public License as published by the Free
  9 * Software Foundation; either version 2 of the License, or (at your option)
 10 * any later version.
 11 *
 12 * This program is distributed in the hope that it will be useful, but WITHOUT
 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 15 * more details.
 16 *
 17 * You should have received a copy of the GNU General Public License along with
 18 * this program; if not, write to the Free Software Foundation, Inc., 59
 19 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 20 *
 21 */
 22
 23#include <linux/netdevice.h>
 24#include <linux/ethtool.h>
 25#include <linux/slab.h>
 26
 27#include "atl1c.h"
 28
 29static int atl1c_get_settings(struct net_device *netdev,
 30			      struct ethtool_cmd *ecmd)
 31{
 32	struct atl1c_adapter *adapter = netdev_priv(netdev);
 33	struct atl1c_hw *hw = &adapter->hw;
 34
 35	ecmd->supported = (SUPPORTED_10baseT_Half  |
 36			   SUPPORTED_10baseT_Full  |
 37			   SUPPORTED_100baseT_Half |
 38			   SUPPORTED_100baseT_Full |
 39			   SUPPORTED_Autoneg       |
 40			   SUPPORTED_TP);
 41	if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M)
 42		ecmd->supported |= SUPPORTED_1000baseT_Full;
 43
 44	ecmd->advertising = ADVERTISED_TP;
 45
 46	ecmd->advertising |= hw->autoneg_advertised;
 47
 48	ecmd->port = PORT_TP;
 49	ecmd->phy_address = 0;
 50	ecmd->transceiver = XCVR_INTERNAL;
 51
 52	if (adapter->link_speed != SPEED_0) {
 53		ethtool_cmd_speed_set(ecmd, adapter->link_speed);
 54		if (adapter->link_duplex == FULL_DUPLEX)
 55			ecmd->duplex = DUPLEX_FULL;
 56		else
 57			ecmd->duplex = DUPLEX_HALF;
 58	} else {
 59		ethtool_cmd_speed_set(ecmd, -1);
 60		ecmd->duplex = -1;
 61	}
 62
 63	ecmd->autoneg = AUTONEG_ENABLE;
 64	return 0;
 65}
 66
 67static int atl1c_set_settings(struct net_device *netdev,
 68			      struct ethtool_cmd *ecmd)
 69{
 70	struct atl1c_adapter *adapter = netdev_priv(netdev);
 71	struct atl1c_hw *hw = &adapter->hw;
 72	u16  autoneg_advertised;
 73
 74	while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
 75		msleep(1);
 76
 77	if (ecmd->autoneg == AUTONEG_ENABLE) {
 78		autoneg_advertised = ADVERTISED_Autoneg;
 79	} else {
 80		u32 speed = ethtool_cmd_speed(ecmd);
 81		if (speed == SPEED_1000) {
 82			if (ecmd->duplex != DUPLEX_FULL) {
 83				if (netif_msg_link(adapter))
 84					dev_warn(&adapter->pdev->dev,
 85						"1000M half is invalid\n");
 86				clear_bit(__AT_RESETTING, &adapter->flags);
 87				return -EINVAL;
 88			}
 89			autoneg_advertised = ADVERTISED_1000baseT_Full;
 90		} else if (speed == SPEED_100) {
 91			if (ecmd->duplex == DUPLEX_FULL)
 92				autoneg_advertised = ADVERTISED_100baseT_Full;
 93			else
 94				autoneg_advertised = ADVERTISED_100baseT_Half;
 95		} else {
 96			if (ecmd->duplex == DUPLEX_FULL)
 97				autoneg_advertised = ADVERTISED_10baseT_Full;
 98			else
 99				autoneg_advertised = ADVERTISED_10baseT_Half;
100		}
101	}
102
103	if (hw->autoneg_advertised != autoneg_advertised) {
104		hw->autoneg_advertised = autoneg_advertised;
105		if (atl1c_restart_autoneg(hw) != 0) {
106			if (netif_msg_link(adapter))
107				dev_warn(&adapter->pdev->dev,
108					"ethtool speed/duplex setting failed\n");
109			clear_bit(__AT_RESETTING, &adapter->flags);
110			return -EINVAL;
111		}
112	}
113	clear_bit(__AT_RESETTING, &adapter->flags);
114	return 0;
115}
116
117static u32 atl1c_get_msglevel(struct net_device *netdev)
118{
119	struct atl1c_adapter *adapter = netdev_priv(netdev);
120	return adapter->msg_enable;
121}
122
123static void atl1c_set_msglevel(struct net_device *netdev, u32 data)
124{
125	struct atl1c_adapter *adapter = netdev_priv(netdev);
126	adapter->msg_enable = data;
127}
128
129static int atl1c_get_regs_len(struct net_device *netdev)
130{
131	return AT_REGS_LEN;
132}
133
134static void atl1c_get_regs(struct net_device *netdev,
135			   struct ethtool_regs *regs, void *p)
136{
137	struct atl1c_adapter *adapter = netdev_priv(netdev);
138	struct atl1c_hw *hw = &adapter->hw;
139	u32 *regs_buff = p;
140	u16 phy_data;
141
142	memset(p, 0, AT_REGS_LEN);
143
144	regs->version = 0;
145	AT_READ_REG(hw, REG_VPD_CAP, 		  p++);
146	AT_READ_REG(hw, REG_PM_CTRL, 		  p++);
147	AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL,  p++);
148	AT_READ_REG(hw, REG_TWSI_CTRL, 		  p++);
149	AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL,   p++);
150	AT_READ_REG(hw, REG_MASTER_CTRL, 	  p++);
151	AT_READ_REG(hw, REG_MANUAL_TIMER_INIT,    p++);
152	AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);
153	AT_READ_REG(hw, REG_GPHY_CTRL, 		  p++);
154	AT_READ_REG(hw, REG_LINK_CTRL, 		  p++);
155	AT_READ_REG(hw, REG_IDLE_STATUS, 	  p++);
156	AT_READ_REG(hw, REG_MDIO_CTRL, 		  p++);
157	AT_READ_REG(hw, REG_SERDES_LOCK, 	  p++);
158	AT_READ_REG(hw, REG_MAC_CTRL, 		  p++);
159	AT_READ_REG(hw, REG_MAC_IPG_IFG, 	  p++);
160	AT_READ_REG(hw, REG_MAC_STA_ADDR, 	  p++);
161	AT_READ_REG(hw, REG_MAC_STA_ADDR+4, 	  p++);
162	AT_READ_REG(hw, REG_RX_HASH_TABLE, 	  p++);
163	AT_READ_REG(hw, REG_RX_HASH_TABLE+4, 	  p++);
164	AT_READ_REG(hw, REG_RXQ_CTRL, 		  p++);
165	AT_READ_REG(hw, REG_TXQ_CTRL, 		  p++);
166	AT_READ_REG(hw, REG_MTU, 		  p++);
167	AT_READ_REG(hw, REG_WOL_CTRL, 		  p++);
168
169	atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);
170	regs_buff[73] =	(u32) phy_data;
171	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
172	regs_buff[74] = (u32) phy_data;
173}
174
175static int atl1c_get_eeprom_len(struct net_device *netdev)
176{
177	struct atl1c_adapter *adapter = netdev_priv(netdev);
178
179	if (atl1c_check_eeprom_exist(&adapter->hw))
180		return AT_EEPROM_LEN;
181	else
182		return 0;
183}
184
185static int atl1c_get_eeprom(struct net_device *netdev,
186		struct ethtool_eeprom *eeprom, u8 *bytes)
187{
188	struct atl1c_adapter *adapter = netdev_priv(netdev);
189	struct atl1c_hw *hw = &adapter->hw;
190	u32 *eeprom_buff;
191	int first_dword, last_dword;
192	int ret_val = 0;
193	int i;
194
195	if (eeprom->len == 0)
196		return -EINVAL;
197
198	if (!atl1c_check_eeprom_exist(hw)) /* not exist */
199		return -EINVAL;
200
201	eeprom->magic = adapter->pdev->vendor |
202			(adapter->pdev->device << 16);
203
204	first_dword = eeprom->offset >> 2;
205	last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
206
207	eeprom_buff = kmalloc(sizeof(u32) *
208			(last_dword - first_dword + 1), GFP_KERNEL);
209	if (eeprom_buff == NULL)
210		return -ENOMEM;
211
212	for (i = first_dword; i < last_dword; i++) {
213		if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
214			kfree(eeprom_buff);
215			return -EIO;
216		}
217	}
218
219	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
220			eeprom->len);
221	kfree(eeprom_buff);
222
223	return ret_val;
224	return 0;
225}
226
227static void atl1c_get_drvinfo(struct net_device *netdev,
228		struct ethtool_drvinfo *drvinfo)
229{
230	struct atl1c_adapter *adapter = netdev_priv(netdev);
231
232	strlcpy(drvinfo->driver,  atl1c_driver_name, sizeof(drvinfo->driver));
233	strlcpy(drvinfo->version, atl1c_driver_version,
234		sizeof(drvinfo->version));
235	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
236	strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
237		sizeof(drvinfo->bus_info));
238	drvinfo->n_stats = 0;
239	drvinfo->testinfo_len = 0;
240	drvinfo->regdump_len = atl1c_get_regs_len(netdev);
241	drvinfo->eedump_len = atl1c_get_eeprom_len(netdev);
242}
243
244static void atl1c_get_wol(struct net_device *netdev,
245			  struct ethtool_wolinfo *wol)
246{
247	struct atl1c_adapter *adapter = netdev_priv(netdev);
248
249	wol->supported = WAKE_MAGIC | WAKE_PHY;
250	wol->wolopts = 0;
251
252	if (adapter->wol & AT_WUFC_EX)
253		wol->wolopts |= WAKE_UCAST;
254	if (adapter->wol & AT_WUFC_MC)
255		wol->wolopts |= WAKE_MCAST;
256	if (adapter->wol & AT_WUFC_BC)
257		wol->wolopts |= WAKE_BCAST;
258	if (adapter->wol & AT_WUFC_MAG)
259		wol->wolopts |= WAKE_MAGIC;
260	if (adapter->wol & AT_WUFC_LNKC)
261		wol->wolopts |= WAKE_PHY;
262}
263
264static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
265{
266	struct atl1c_adapter *adapter = netdev_priv(netdev);
267
268	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
269			    WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
270		return -EOPNOTSUPP;
271	/* these settings will always override what we currently have */
272	adapter->wol = 0;
273
274	if (wol->wolopts & WAKE_MAGIC)
275		adapter->wol |= AT_WUFC_MAG;
276	if (wol->wolopts & WAKE_PHY)
277		adapter->wol |= AT_WUFC_LNKC;
278
279	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
280
281	return 0;
282}
283
284static int atl1c_nway_reset(struct net_device *netdev)
285{
286	struct atl1c_adapter *adapter = netdev_priv(netdev);
287	if (netif_running(netdev))
288		atl1c_reinit_locked(adapter);
289	return 0;
290}
291
292static const struct ethtool_ops atl1c_ethtool_ops = {
293	.get_settings           = atl1c_get_settings,
294	.set_settings           = atl1c_set_settings,
295	.get_drvinfo            = atl1c_get_drvinfo,
296	.get_regs_len           = atl1c_get_regs_len,
297	.get_regs               = atl1c_get_regs,
298	.get_wol                = atl1c_get_wol,
299	.set_wol                = atl1c_set_wol,
300	.get_msglevel           = atl1c_get_msglevel,
301	.set_msglevel           = atl1c_set_msglevel,
302	.nway_reset             = atl1c_nway_reset,
303	.get_link               = ethtool_op_get_link,
304	.get_eeprom_len         = atl1c_get_eeprom_len,
305	.get_eeprom             = atl1c_get_eeprom,
306};
307
308void atl1c_set_ethtool_ops(struct net_device *netdev)
309{
310	SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
311}