// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
/* TSN endpoint Ethernet MAC driver
*
* The TSN endpoint Ethernet MAC is a FPGA based network device for real-time
* communication. It is designed for endpoints within TSN (Time Sensitive
* Networking) networks; e.g., for PLCs in the industrial automation case.
*
* It supports multiple TX/RX queue pairs. The first TX/RX queue pair is used
* by the driver.
*
* More information can be found here:
* - www.embedded-experts.at/tsn
* - www.engleder-embedded.com
*/
#include "tsnep.h"
#include "tsnep_hw.h"
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/interrupt.h>
#include <linux/etherdevice.h>
#include <linux/phy.h>
#include <linux/iopoll.h>
#define RX_SKB_LENGTH (round_up(TSNEP_RX_INLINE_METADATA_SIZE + ETH_HLEN + \
TSNEP_MAX_FRAME_SIZE + ETH_FCS_LEN, 4))
#define RX_SKB_RESERVE ((16 - TSNEP_RX_INLINE_METADATA_SIZE) + NET_IP_ALIGN)
#define RX_SKB_ALLOC_LENGTH (RX_SKB_RESERVE + RX_SKB_LENGTH)
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
#define DMA_ADDR_HIGH(dma_addr) ((u32)(((dma_addr) >> 32) & 0xFFFFFFFF))
#else
#define DMA_ADDR_HIGH(dma_addr) ((u32)(0))
#endif
#define DMA_ADDR_LOW(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF))
static void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask)
{
iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
}
static void tsnep_disable_irq(struct tsnep_adapter *adapter, u32 mask)
{
mask |= ECM_INT_DISABLE;
iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
}
static irqreturn_t tsnep_irq(int irq, void *arg)
{
struct tsnep_adapter *adapter = arg;
u32 active = ioread32(adapter->addr + ECM_INT_ACTIVE);
/* acknowledge interrupt */
if (active != 0)
iowrite32(active, adapter->addr + ECM_INT_ACKNOWLEDGE);
/* handle link interrupt */
if ((active & ECM_INT_LINK) != 0) {
if (adapter->netdev->phydev)
phy_mac_interrupt(adapter->netdev->phydev);
}
/* handle TX/RX queue 0 interrupt */
if ((active & adapter->queue[0].irq_mask) != 0) {
if (adapter->netdev) {
tsnep_disable_irq(adapter, adapter->queue[0].irq_mask);
napi_schedule(&adapter->queue[0].napi);
}
}
return IRQ_HANDLED;
}
static int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
{
struct tsnep_adapter *adapter = bus->priv;
u32 md;
int retval;
if (regnum & MII_ADDR_C45)
return -EOPNOTSUPP;
md = ECM_MD_READ;
if (!adapter->suppress_preamble)
md