Files
linux-nv-oot/drivers/net/ethernet/marvell/oak/oak.c
Jon Hunter b5ec750ebb marvell: oak: Fix build for Linux v5.14
The functions eth_hw_addr_set() and dev_addr_mod() do not exist for
Linux v5.14 and so compiling the marvell oak driver fails for Linux
v5.14. Fix this by calling memcpy() instead of eth_hw_addr_set() and
dev_addr_mod() for Linux v5.14. Note that both eth_hw_addr_set() and
dev_addr_mod() internally call memcpy() and so this is equivalent.

Bug 3820317

Change-Id: I4b49e031383adf62a55f1d01e8de2fcc0ad47862
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2875022
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2023-03-24 17:20:19 -07:00

704 lines
20 KiB
C

/*
*
* If you received this File from Marvell, you may opt to use, redistribute and/or
* modify this File in accordance with the terms and conditions of the General
* Public License Version 2, June 1991 (the "GPL License"), a copy of which is
* available along with the File in the license.txt file or by writing to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
* on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
* DISCLAIMED. The GPL License provides additional details about this warranty
* disclaimer.
*
*/
#include <linux/version.h>
#include "oak.h"
/* private function prototypes */
static int oak_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id);
static void oak_remove(struct pci_dev *pdev);
static void oak_read_set_mac_address(struct pci_dev *pdev);
/* Oak driver name, version, and copyright */
const char oak_driver_name[] = OAK_DRIVER_NAME;
const char oak_driver_version[] = OAK_DRIVER_VERSION;
static const char oak_driver_string[] = OAK_DRIVER_STRING;
static const char oak_driver_copyright[] = OAK_DRIVER_COPYRIGHT;
/* Oak PCI device ID structure */
static struct pci_device_id oak_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x1000)},
{PCI_DEVICE(0x11AB, 0x0000)}, /* FPGA board */
{PCI_DEVICE(0x11AB, 0xABCD)}, /* FPGA board */
{PCI_DEVICE(0x11AB, 0x0f13)},
{PCI_DEVICE(0x11AB, 0x0a72)}, /* Oak */
{0,} /* Terminate the table */
};
#ifdef CONFIG_PM_SLEEP
/* Device Power Management (DPM) support */
static const struct dev_pm_ops oak_dpm_ops = {
.suspend = oak_dpm_suspend,
.resume = oak_dpm_resume,
};
#endif
/* PCIe - interface structure */
static struct pci_driver oak_driver = {
.name = oak_driver_name,
.id_table = oak_pci_tbl,
.probe = oak_probe,
.remove = oak_remove,
#ifdef CONFIG_PM_SLEEP
.driver.pm = &oak_dpm_ops,
#endif
};
/* Oak ethtool operation structure */
static const struct ethtool_ops oak_ethtool_ops = {
.get_drvinfo = oak_ethtool_get_drvinfo,
.get_ethtool_stats = oak_ethtool_get_stats,
.get_strings = oak_ethtool_get_strings,
.get_sset_count = oak_ethtool_get_sscnt,
.get_link = ethtool_op_get_link,
.get_msglevel = oak_dbg_get_level,
.set_msglevel = oak_dbg_set_level,
.get_link_ksettings = oak_ethtool_get_link_ksettings,
};
/* Oak netdevice operation structure */
static const struct net_device_ops oak_netdev_ops = {
.ndo_open = oak_net_open,
.ndo_stop = oak_net_close,
.ndo_start_xmit = oak_net_xmit_frame,
.ndo_do_ioctl = oak_net_ioctl,
.ndo_set_mac_address = oak_net_set_mac_addr,
.ndo_select_queue = oak_net_select_queue,
.ndo_change_mtu = oak_net_esu_set_mtu,
.ndo_validate_addr = eth_validate_addr,
};
/* global variable declaration */
u32 debug = 0;
u32 txs = 2048;
u32 rxs = 2048;
int chan = MAX_NUM_OF_CHANNELS;
int rto = 100;
int mhdr;
u32 port_speed = 10;
int napi_wt = NAPI_POLL_WEIGHT;
/* software level defination */
#define SOFTWARE_INIT 10
#define HARDWARE_INIT 20
#define SOFTWARE_STARTED 40
/* Name : oak_init_module
* Returns : int
* Parameters:
* Description : This function is the entry point for the driver registration.
*/
int oak_init_module(void)
{
s32 retval = 0;
pr_info("%s - (%s) version %s\n",
oak_driver_string, oak_driver_name, oak_driver_version);
pr_info("%s\n", oak_driver_copyright);
/* Register Oak PCI driver with the linux kernel
* PCI device drivers call pci_register_driver() during their
* initialization with a pointer to a structure describing the
* driver (struct pci_driver)
*/
retval = pci_register_driver(&oak_driver);
return retval;
}
/* Name : exit_module
* Returns : void
* Parameters:
* Description : This function is the exit point for the driver.
*/
void oak_exit_module(void)
{
/* Unregister Oak PCI driver from linux kernel */
pci_unregister_driver(&oak_driver);
}
/* Name : start_software
* Returns : int
* Parameters: struct pci_dev * pdev
* Description: This function registers the oak device to napi
*/
static int oak_start_software(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
int retval = 0;
netdev->ethtool_ops = &oak_ethtool_ops;
oak_net_add_napi(netdev);
/* register netdev will take a completed network device structure and
* add it to the kernel interfaces
*/
retval = register_netdev(netdev);
return retval;
}
/* Name : release_software
* Returns : void
* Parameters: struct pci_dev * pdev
* Description : This function release the Ethernet interface.
*/
static void oak_release_software(struct pci_dev *pdev)
{
int retval;
struct net_device *netdev = pci_get_drvdata(pdev);
oak_t *oak;
oak = netdev_priv(netdev);
#ifdef CONFIG_PM
/* Set the PCI device power state to D3hot */
retval = pci_set_power_state(pdev, PCI_D3hot);
if (retval == 0)
pr_info("oak: Device power state D%d\n", pdev->current_state);
else
pr_err("oak: Failed to set the device power state err: %d\n",
retval);
/* Remove sysfs entry */
oak_dpm_remove_sysfs(oak);
#endif
oakdbg(debug, PROBE, "pdev=%p ndev=%p", pdev, pci_get_drvdata(pdev));
free_netdev(netdev);
}
/* Name : oak_init_software
* Returns : int
* Parameters : struct pci_dev *pdev
* Description : This function initializes the PCIe interface and network device
*/
static int oak_init_software(struct pci_dev *pdev)
{
struct net_device *netdev = NULL;
oak_t *oak;
int retval = 0;
/* Allocates and sets up an Ethernet device
* Fill in the fields of the device structure with Ethernet-generic
* values. Basically does everything except registering the device
*/
netdev = alloc_etherdev_mq(sizeof(oak_t), chan);
if (netdev) {
/* Set the sysfs physical device reference for the network
* logical device if set prior to registration will cause a
* symlink during initialization.
*/
SET_NETDEV_DEV(netdev, &pdev->dev);
/* Set private driver data pointer for a pci_dev */
pci_set_drvdata(pdev, netdev);
oak = netdev_priv(netdev);
oak->device = &pdev->dev;
oak->netdev = netdev;
oak->pdev = pdev;
#ifdef CONFIG_PM
/* Create sysfs entry for D0, D1, D2 and D3 states testing */
oak_dpm_create_sysfs(oak);
#endif
/* Register network device operations */
netdev->netdev_ops = &oak_netdev_ops;
/* Set hardware's checksum Offload capabilities */
netdev->features = oak_chksum_get_config();
/* Set the min and max MTU size */
oak_set_mtu_config(netdev);
spin_lock_init(&oak->lock);
/* Assign random MAC address */
eth_hw_addr_random(netdev);
} else {
/* If software initialization fails then we need to release the
* device and free allocated structure with retnval as ENOMEM
*/
oak_release_software(pdev);
retval = -ENOMEM;
}
oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d",
pdev, pci_get_drvdata(pdev), retval);
return retval;
}
/* Name : oak_probe
* Returns : int
* Parameters : struct pci_dev *pdev, const struct pci_device_id *dev_id
* Description : This function probe the device and call initialization
* functions
*/
static int oak_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
{
int retval = 0;
int err = 0;
oak_t *adapter = NULL;
#ifdef CONFIG_PM
/* Set PCI device power state to D0 */
err = pci_set_power_state(pdev, PCI_D0);
if (err == 0)
pr_info("oak: Device power state D%d\n", pdev->current_state);
else
pr_err("oak: Failed to set the device power state err: %d\n",
err);
#endif
/* Initialize the oak software */
err = oak_init_software(pdev);
if (err == 0) {
struct net_device *netdev = pci_get_drvdata(pdev);
adapter = netdev_priv(netdev);
/* Set level as software initialization */
adapter->level = SOFTWARE_INIT;
/* Initialize the Oak hardware */
err = oak_init_hardware(pdev);
if (err == 0) {
/* Read MAC from firmware registers and set to the
* device address register.
*/
oak_read_set_mac_address(pdev);
err = oak_start_software(pdev);
if (err == 0)
/* Set level as driver software started */
adapter->level = SOFTWARE_STARTED;
else
retval = err;
} else {
retval = err;
}
} else {
retval = err;
}
if (err == 0) {
retval = err;
if (adapter->sw_base)
pr_info("%s[%d] - ESU register access is supported",
oak_driver_name, pdev->devfn);
} else {
oak_remove(pdev);
}
oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d", pdev,
pci_get_drvdata(pdev), err);
return retval;
}
/* Name : oak_get_msix_resources
* Returns : int
* Parameters : struct pci_dev * pdev
* Description : This function allocates the MSIX resources.
*/
static int oak_get_msix_resources(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
oak_t *adapter = netdev_priv(dev);
u32 num_irqs;
u32 num_cpus = num_online_cpus();
u32 i;
int cnt;
int retval = 0;
num_irqs = sizeof(adapter->gicu.msi_vec) / sizeof(struct msix_entry);
/* Return the number of device's MSI-X table entries */
cnt = pci_msix_vec_count(pdev);
if (cnt <= 0) {
retval = -EFAULT;
} else {
if (cnt <= num_irqs)
num_irqs = cnt;
if (num_irqs > num_cpus)
num_irqs = num_cpus;
for (i = 0; i < num_irqs; i++) {
adapter->gicu.msi_vec[i].vector = 0;
adapter->gicu.msi_vec[i].entry = i;
}
#ifdef OAK_MSIX_LEGACY
/* Configure device's MSI-X capability structure - Setup the
* MSI-X capability structure of device function with a
* maximum possible number of interrupts in the range between
* minvec and maxvec upon its software driver call to request
* for MSI-X mode enabled on its hardware device function.
* It returns a negative errno if an error occurs.
* If it succeeds, it returns the actual number of interrupts
* allocated and indicates the successful configuration of
* MSI-X capability structure with new allocated MSI-X
* interrupts.
*/
retval = pci_enable_msix_range(pdev, adapter->gicu.msi_vec,
num_irqs, num_irqs);
#else
/* Which allocates up to max_vecs interrupt vectors for a PCI
* device.
*/
retval = pci_alloc_irq_vectors(pdev, num_irqs, num_irqs,
PCI_IRQ_ALL_TYPES);
if (retval) {
pr_info("int vec count %d\n", retval);
num_irqs = retval;
}
#endif
if (retval >= 0)
retval = 0;
adapter->gicu.num_ldg = num_irqs;
oakdbg(debug, PROBE, "pdev=%p ndev=%p num_irqs=%d/%d retval=%d",
pdev, dev, num_irqs, cnt, retval);
}
return retval;
}
/* Name : release_hardware
* Returns : void
* Parameters: struct pci_dev * pdev
* Description : This function de initializes the hardware and
* release the resources.
*/
void oak_release_hardware(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
oak_t *adapter = netdev_priv(dev);
int err = 0;
if (adapter->gicu.num_ldg > 0)
pci_disable_msix(pdev);
/* Release reserved PCI I/O and memory resources */
pci_release_regions(pdev);
pci_disable_device(pdev);
oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d",
pdev, pci_get_drvdata(pdev), err);
}
/* Name : oak_init_map_config
* Returns : int
* Parameters : struct pci_dev *pdev
* Description : This function create a virtual mapping cookie for a PCI BAR
*/
static int oak_init_map_config(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
oak_t *adapter = netdev_priv(dev);
u32 mem_flags;
int retval = 0;
/* This function returns the flags associated with the PCI resource.
* Resource flags are used to define some features of the individual
* resource. For PCI resources associated with PCI I/O regions, the
* information is extracted from the base address registers, but can
* come from elsewhere for resources not associated with PCI devices.
*/
mem_flags = pci_resource_flags(pdev, 2);
if ((mem_flags & IORESOURCE_MEM) == 0)
retval = -EINVAL;
else
adapter->sw_base = pci_iomap(pdev, 2, 0);
oakdbg(debug, PROBE,
"Device found: dom=%d bus=%d dev=%d fun=%d reg-addr=%p",
pci_domain_nr(pdev->bus), pdev->bus->number,
PCI_SLOT(pdev->devfn), pdev->devfn, adapter->um_base);
return retval;
}
/* Name : oak_init_read_write_config
* Returns : int
* Parameters : struct pci_dev *pdev
* Description : This function read and write into config space.
*/
static int oak_init_read_write_config(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
oak_t *adapter = netdev_priv(dev);
u32 v0, v1;
u16 devctl;
int retval = 0;
/* Create virtual mapping the PCI BAR configuration space before doing
* read or write into configuration space
*/
retval = oak_init_map_config(pdev);
if (retval != 0)
pr_err("PCI config space mapping failed %d\n", retval);
/* After the driver has detected the device, it usually needs to read
* from or write to the three address spaces: memory, port, and
* configuration. In particular, accessing the configuration space is
* vital to the driver, because it is the only way it can find out
* where the device is mapped in memory and in the I/O space.
*/
pci_read_config_dword(pdev, 0x10, &v0);
pci_read_config_dword(pdev, 0x14, &v1);
v0 &= 0xfffffff0U;
v0 |= 1U;
pci_write_config_dword(pdev, 0x944, v1);
pci_write_config_dword(pdev, 0x940, v0);
pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &devctl);
/* Calculate and store TX max burst size */
adapter->rrs =
(u16)(1U << (((devctl & PCI_EXP_DEVCTL_READRQ) >> 12) + 6));
if (retval == 0)
retval = oak_get_msix_resources(pdev);
return retval;
}
/* Name : oak_init_pci_config
* Returns : int
* Parameters : struct pci_dev *pdev
* Description : This function initialize oak pci config space
*/
static int oak_init_pci_config(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
oak_t *adapter = netdev_priv(dev);
int err = 0;
err = pci_request_regions(pdev, OAK_DRIVER_NAME);
if (err == 0) {
/* Enables bus-mastering for device */
pci_set_master(pdev);
/* Save the PCI configuration space */
pci_save_state(pdev);
/* create a virtual mapping cookie for a PCI BAR. Using
* this function you will get a __iomem address to your
* device BAR. You can access it using ioread*() and
* iowrite*(). These functions hide the details if this
* is a MMIO or PIO address space and will just do what
* you expect from them in the correct way.
* void __iomem * pci_iomap(struct pci_dev * dev,
* int bar, unsigned long, maxlen);
* maxlen specifies the maximum length to map. If you
* want to get access to the complete BAR without
* checking for its length first, pass 0 here.
*/
adapter->um_base = pci_iomap(pdev, 0, 0);
if (!adapter->um_base)
err = -ENOMEM;
else
err = oak_init_read_write_config(pdev);
}
return err;
}
/* Name : oak_init_hardware
* Returns : int
* Parameters : struct pci_dev * pdev
* Description : This function initializes oak hardware
*/
int oak_init_hardware(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
oak_t *adapter = netdev_priv(dev);
int retval = 0;
u32 mem_flags;
/* Initialize device before it's used by a driver */
retval = pci_enable_device(pdev);
if (retval == 0) {
/* This function returns the flags associated with this
* resource. Resource flags are used to define some features
* of the individual resource. For PCI resources associated with
* PCI I/O regions, the information is extracted from the base
* address registers, but can come from elsewhere for resources
* not associated with PCI devices
*/
mem_flags = pci_resource_flags(pdev, 0);
if ((mem_flags & IORESOURCE_MEM) == 0) {
retval = -EINVAL;
} else {
pci_read_config_dword(pdev, PCI_CLASS_REVISION,
&adapter->pci_class_revision);
adapter->pci_class_revision &= 0x0000000FU;
if (adapter->pci_class_revision > OAK_REVISION_B0) {
retval = -EINVAL;
} else {
retval =
dma_set_mask_and_coherent(&pdev->dev,
DMA_BIT_MASK(64));
}
if (retval == 0)
retval = oak_init_pci_config(pdev);
}
} else {
pr_err("PCI enable device failed %d\n", retval);
}
oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d", pdev,
pci_get_drvdata(pdev), retval);
return retval;
}
/* Name : oak_set_mtu_config
* Returns : void
* Parameters: struct net_device *netdev
* Description: This function sets the min and max MTU size in the linux netdev.
*/
void oak_set_mtu_config(struct net_device *netdev)
{
netdev->min_mtu = ETH_MIN_MTU;
netdev->max_mtu = OAK_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN);
}
/* Name : oak_read_set_mac_address
* Returns : void
* Parameters : struct net_device *netdev
* Description : During startup, firmware writes its own MAC address into the
* EPU_DATA2 and EPU_DATA3 registers. When loaded, this function obtains MAC
* address from EPU_DATA registers and increments the NIC specific part of the
* MAC address by 1.
* The MAC address consists of two parts:
* Octet 1-3: Organizationally Unique Identifier (OUI)
* Octet 4-6: Network Interface Controller (NIC) specific part.
* The increment shall be done for the NIC specific part only.
*/
static void oak_read_set_mac_address(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
oak_t *np = netdev_priv(netdev);
/* Octet 1-3 from EPU_DATA2 */
u32 mac_oui;
/* Octet 4-6 from EPU_DATA3 */
u32 mac_nic;
u32 mac_update;
u32 oui_be;
unsigned char device_mac[sizeof(uint64_t)];
char nic_mac[ETH_ALEN];
bool rc;
/* Read MAC address from EPU_DATA2 and EPU_DATA3 registers */
mac_oui = oak_unimac_io_read_32(np, OAK_EPU_DATA2);
mac_nic = oak_unimac_io_read_32(np, OAK_EPU_DATA3);
pr_debug("Device MAC address OUI (EPU_DATA2) : 0x%x\n",
cpu_to_be32(mac_oui));
pr_debug("Device MAC address NIC (EPU_DATA3) : 0x%x\n",
cpu_to_be32(mac_nic));
/* Copy Oak device MAC address and print */
memcpy((void *)&device_mac, (void *)&mac_oui, sizeof(mac_oui));
memcpy((void *)&device_mac + 4, (void *)&mac_nic, sizeof(mac_nic));
rc = is_valid_ether_addr(device_mac);
if (rc != 0) {
/* Copy the OUI into network device structure */
oui_be = (__force u32)cpu_to_be32(mac_oui);
nic_mac[0] = (char)((oui_be & 0xff000000U) >> 24);
nic_mac[1] = (char)((oui_be & 0x00ff0000U) >> 16);
nic_mac[2] = (char)((oui_be & 0x0000ff00U) >> 8);
/* To construct MAC address we need to takeout one bye from
* EPU_DATA2 and two bytes from EPU_DATA3 register
* As an example: MAC address is 11:22:33:44:55:66 set by
* firmware then Data read from register will give below
* little endian output
* EPU_DATA2 - 0x44332211
* EPU_DATA3 - 0x6655
* Hence it is required to convert to endian using cpu_to_be32
* EPU_DATA2 - 0x11223344
* EPU_DATA3 - 0x55660000
* To construct NIC part as 0x445566
*/
mac_update = (__force u32)cpu_to_be32(mac_nic) >> 16;
mac_update = ((__force u32)cpu_to_be32(mac_oui) & 0xFFU) << 16 |
mac_update;
/* Update the NIC part by one */
mac_update = mac_update + 1;
/* Copy updated NIC to netdev layer */
nic_mac[3] = (char)((mac_update & 0xff0000U) >> 16);
nic_mac[4] = (char)((mac_update & 0x00ff00U) >> 8);
nic_mac[5] = (char)(mac_update & 0x0000ffU);
/* Validate locally constructed NIC MAC address and
* Copy to mac_address member of oak_t if its valid.
*/
rc = is_valid_ether_addr(nic_mac);
if (rc != 0) {
pr_info("Device MAC address : %pM\n", device_mac);
ether_addr_copy(np->mac_address, nic_mac);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
eth_hw_addr_set(netdev, nic_mac);
#else
memcpy(netdev->dev_addr, nic_mac, ETH_ALEN);
#endif
pr_info("MAC address of NIC : %pM\n", nic_mac);
}
}
}
/* Name : stop_software
* Returns : void
* Parameters: struct pci_dev * pdev
* Description: This function unregisters the oak driver from netdev
*/
static void oak_stop_software(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
oak_net_del_napi(netdev);
/* This function shuts down a device interface and removes it from
* the kernel tables.
*/
unregister_netdev(netdev);
}
/* Name : oak_remove
* Returns : void
* Parameters : struct pci_dev * pdev
* Description : This function remove the device from kernel
*/
static void oak_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
oak_t *adapter = NULL;
if (netdev)
adapter = netdev_priv(netdev);
if (adapter) {
if (adapter->level >= SOFTWARE_STARTED)
oak_stop_software(pdev);
if (adapter->level >= HARDWARE_INIT)
oak_release_hardware(pdev);
if (adapter->level >= SOFTWARE_INIT)
oak_release_software(pdev);
}
oakdbg(debug, PROBE, "pdev=%p ndev=%p", pdev, pci_get_drvdata(pdev));
#ifndef OAK_MSIX_LEGACY
/* Free previously allocated IRQs for a device */
pci_free_irq_vectors(pdev);
#endif
}