18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* atlx.c -- common functions for Attansic network drivers
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
58c2ecf20Sopenharmony_ci * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
68c2ecf20Sopenharmony_ci * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
78c2ecf20Sopenharmony_ci * Copyright(c) 2007 Atheros Corporation. All rights reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Derived from Intel e1000 driver
108c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* Including this file like a header is a temporary hack, I promise. -- CHS */
148c2ecf20Sopenharmony_ci#ifndef ATLX_C
158c2ecf20Sopenharmony_ci#define ATLX_C
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/if.h>
218c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
228c2ecf20Sopenharmony_ci#include <linux/socket.h>
238c2ecf20Sopenharmony_ci#include <linux/sockios.h>
248c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
258c2ecf20Sopenharmony_ci#include <linux/string.h>
268c2ecf20Sopenharmony_ci#include <linux/types.h>
278c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "atlx.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic s32 atlx_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data);
328c2ecf20Sopenharmony_cistatic u32 atlx_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr);
338c2ecf20Sopenharmony_cistatic void atlx_set_mac_addr(struct atl1_hw *hw);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct atlx_spi_flash_dev flash_table[] = {
368c2ecf20Sopenharmony_ci/*	MFR_NAME  WRSR  READ  PRGM  WREN  WRDI  RDSR  RDID  SEC_ERS CHIP_ERS */
378c2ecf20Sopenharmony_ci	{"Atmel", 0x00, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52,   0x62},
388c2ecf20Sopenharmony_ci	{"SST",   0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20,   0x60},
398c2ecf20Sopenharmony_ci	{"ST",    0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8,   0xC7},
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int atlx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	switch (cmd) {
458c2ecf20Sopenharmony_ci	case SIOCGMIIPHY:
468c2ecf20Sopenharmony_ci	case SIOCGMIIREG:
478c2ecf20Sopenharmony_ci	case SIOCSMIIREG:
488c2ecf20Sopenharmony_ci		return atlx_mii_ioctl(netdev, ifr, cmd);
498c2ecf20Sopenharmony_ci	default:
508c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/**
558c2ecf20Sopenharmony_ci * atlx_set_mac - Change the Ethernet Address of the NIC
568c2ecf20Sopenharmony_ci * @netdev: network interface device structure
578c2ecf20Sopenharmony_ci * @p: pointer to an address structure
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Returns 0 on success, negative on failure
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic int atlx_set_mac(struct net_device *netdev, void *p)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct atlx_adapter *adapter = netdev_priv(netdev);
648c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (netif_running(netdev))
678c2ecf20Sopenharmony_ci		return -EBUSY;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
708c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
738c2ecf20Sopenharmony_ci	memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	atlx_set_mac_addr(&adapter->hw);
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void atlx_check_for_link(struct atlx_adapter *adapter)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
828c2ecf20Sopenharmony_ci	u16 phy_data = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	spin_lock(&adapter->lock);
858c2ecf20Sopenharmony_ci	adapter->phy_timer_pending = false;
868c2ecf20Sopenharmony_ci	atlx_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
878c2ecf20Sopenharmony_ci	atlx_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data);
888c2ecf20Sopenharmony_ci	spin_unlock(&adapter->lock);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* notify upper layer link down ASAP */
918c2ecf20Sopenharmony_ci	if (!(phy_data & BMSR_LSTATUS)) {
928c2ecf20Sopenharmony_ci		/* Link Down */
938c2ecf20Sopenharmony_ci		if (netif_carrier_ok(netdev)) {
948c2ecf20Sopenharmony_ci			/* old link state: Up */
958c2ecf20Sopenharmony_ci			dev_info(&adapter->pdev->dev, "%s link is down\n",
968c2ecf20Sopenharmony_ci				netdev->name);
978c2ecf20Sopenharmony_ci			adapter->link_speed = SPEED_0;
988c2ecf20Sopenharmony_ci			netif_carrier_off(netdev);
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	schedule_work(&adapter->link_chg_task);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/**
1058c2ecf20Sopenharmony_ci * atlx_set_multi - Multicast and Promiscuous mode set
1068c2ecf20Sopenharmony_ci * @netdev: network interface device structure
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * The set_multi entry point is called whenever the multicast address
1098c2ecf20Sopenharmony_ci * list or the network interface flags are updated.  This routine is
1108c2ecf20Sopenharmony_ci * responsible for configuring the hardware for proper multicast,
1118c2ecf20Sopenharmony_ci * promiscuous mode, and all-multi behavior.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic void atlx_set_multi(struct net_device *netdev)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct atlx_adapter *adapter = netdev_priv(netdev);
1168c2ecf20Sopenharmony_ci	struct atlx_hw *hw = &adapter->hw;
1178c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
1188c2ecf20Sopenharmony_ci	u32 rctl;
1198c2ecf20Sopenharmony_ci	u32 hash_value;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Check for Promiscuous and All Multicast modes */
1228c2ecf20Sopenharmony_ci	rctl = ioread32(hw->hw_addr + REG_MAC_CTRL);
1238c2ecf20Sopenharmony_ci	if (netdev->flags & IFF_PROMISC)
1248c2ecf20Sopenharmony_ci		rctl |= MAC_CTRL_PROMIS_EN;
1258c2ecf20Sopenharmony_ci	else if (netdev->flags & IFF_ALLMULTI) {
1268c2ecf20Sopenharmony_ci		rctl |= MAC_CTRL_MC_ALL_EN;
1278c2ecf20Sopenharmony_ci		rctl &= ~MAC_CTRL_PROMIS_EN;
1288c2ecf20Sopenharmony_ci	} else
1298c2ecf20Sopenharmony_ci		rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	iowrite32(rctl, hw->hw_addr + REG_MAC_CTRL);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* clear the old settings from the multicast hash table */
1348c2ecf20Sopenharmony_ci	iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE);
1358c2ecf20Sopenharmony_ci	iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2));
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* compute mc addresses' hash value ,and put it into hash table */
1388c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, netdev) {
1398c2ecf20Sopenharmony_ci		hash_value = atlx_hash_mc_addr(hw, ha->addr);
1408c2ecf20Sopenharmony_ci		atlx_hash_set(hw, hash_value);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic inline void atlx_imr_set(struct atlx_adapter *adapter,
1458c2ecf20Sopenharmony_ci				unsigned int imr)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	iowrite32(imr, adapter->hw.hw_addr + REG_IMR);
1488c2ecf20Sopenharmony_ci	ioread32(adapter->hw.hw_addr + REG_IMR);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * atlx_irq_enable - Enable default interrupt generation settings
1538c2ecf20Sopenharmony_ci * @adapter: board private structure
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic void atlx_irq_enable(struct atlx_adapter *adapter)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	atlx_imr_set(adapter, IMR_NORMAL_MASK);
1588c2ecf20Sopenharmony_ci	adapter->int_enabled = true;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci * atlx_irq_disable - Mask off interrupt generation on the NIC
1638c2ecf20Sopenharmony_ci * @adapter: board private structure
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic void atlx_irq_disable(struct atlx_adapter *adapter)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	adapter->int_enabled = false;
1688c2ecf20Sopenharmony_ci	atlx_imr_set(adapter, 0);
1698c2ecf20Sopenharmony_ci	synchronize_irq(adapter->pdev->irq);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void atlx_clear_phy_int(struct atlx_adapter *adapter)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	u16 phy_data;
1758c2ecf20Sopenharmony_ci	unsigned long flags;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adapter->lock, flags);
1788c2ecf20Sopenharmony_ci	atlx_read_phy_reg(&adapter->hw, 19, &phy_data);
1798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adapter->lock, flags);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * atlx_tx_timeout - Respond to a Tx Hang
1848c2ecf20Sopenharmony_ci * @netdev: network interface device structure
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_cistatic void atlx_tx_timeout(struct net_device *netdev, unsigned int txqueue)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct atlx_adapter *adapter = netdev_priv(netdev);
1898c2ecf20Sopenharmony_ci	/* Do the reset outside of interrupt context */
1908c2ecf20Sopenharmony_ci	schedule_work(&adapter->reset_dev_task);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci * atlx_link_chg_task - deal with link change event Out of interrupt context
1958c2ecf20Sopenharmony_ci */
1968c2ecf20Sopenharmony_cistatic void atlx_link_chg_task(struct work_struct *work)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct atlx_adapter *adapter;
1998c2ecf20Sopenharmony_ci	unsigned long flags;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	adapter = container_of(work, struct atlx_adapter, link_chg_task);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adapter->lock, flags);
2048c2ecf20Sopenharmony_ci	atlx_check_link(adapter);
2058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adapter->lock, flags);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void __atlx_vlan_mode(netdev_features_t features, u32 *ctrl)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX) {
2118c2ecf20Sopenharmony_ci		/* enable VLAN tag insert/strip */
2128c2ecf20Sopenharmony_ci		*ctrl |= MAC_CTRL_RMV_VLAN;
2138c2ecf20Sopenharmony_ci	} else {
2148c2ecf20Sopenharmony_ci		/* disable VLAN tag insert/strip */
2158c2ecf20Sopenharmony_ci		*ctrl &= ~MAC_CTRL_RMV_VLAN;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void atlx_vlan_mode(struct net_device *netdev,
2208c2ecf20Sopenharmony_ci	netdev_features_t features)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct atlx_adapter *adapter = netdev_priv(netdev);
2238c2ecf20Sopenharmony_ci	unsigned long flags;
2248c2ecf20Sopenharmony_ci	u32 ctrl;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adapter->lock, flags);
2278c2ecf20Sopenharmony_ci	/* atlx_irq_disable(adapter); FIXME: confirm/remove */
2288c2ecf20Sopenharmony_ci	ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL);
2298c2ecf20Sopenharmony_ci	__atlx_vlan_mode(features, &ctrl);
2308c2ecf20Sopenharmony_ci	iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL);
2318c2ecf20Sopenharmony_ci	/* atlx_irq_enable(adapter); FIXME */
2328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adapter->lock, flags);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic void atlx_restore_vlan(struct atlx_adapter *adapter)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	atlx_vlan_mode(adapter->netdev, adapter->netdev->features);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic netdev_features_t atlx_fix_features(struct net_device *netdev,
2418c2ecf20Sopenharmony_ci	netdev_features_t features)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	/*
2448c2ecf20Sopenharmony_ci	 * Since there is no support for separate rx/tx vlan accel
2458c2ecf20Sopenharmony_ci	 * enable/disable make sure tx flag is always in same state as rx.
2468c2ecf20Sopenharmony_ci	 */
2478c2ecf20Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
2488c2ecf20Sopenharmony_ci		features |= NETIF_F_HW_VLAN_CTAG_TX;
2498c2ecf20Sopenharmony_ci	else
2508c2ecf20Sopenharmony_ci		features &= ~NETIF_F_HW_VLAN_CTAG_TX;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return features;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int atlx_set_features(struct net_device *netdev,
2568c2ecf20Sopenharmony_ci	netdev_features_t features)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	netdev_features_t changed = netdev->features ^ features;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
2618c2ecf20Sopenharmony_ci		atlx_vlan_mode(netdev, features);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return 0;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci#endif /* ATLX_C */
267