diff options
Diffstat (limited to 'drivers/net/ethernet/microchip')
-rw-r--r-- | drivers/net/ethernet/microchip/Kconfig | 56 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/Makefile | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/enc28j60.c | 1685 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/enc28j60_hw.h | 310 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/encx24j600-regmap.c | 524 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/encx24j600.c | 1152 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/encx24j600_hw.h | 438 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/lan743x_ethtool.c | 724 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/lan743x_ethtool.h | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/lan743x_main.c | 3068 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/lan743x_main.h | 827 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/lan743x_ptp.c | 1159 | ||||
-rw-r--r-- | drivers/net/ethernet/microchip/lan743x_ptp.h | 74 |
13 files changed, 10037 insertions, 0 deletions
diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig new file mode 100644 index 000000000..16bd3f44d --- /dev/null +++ b/drivers/net/ethernet/microchip/Kconfig @@ -0,0 +1,56 @@ +# +# Microchip network device configuration +# + +config NET_VENDOR_MICROCHIP + bool "Microchip devices" + default y + depends on SPI + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Microchip cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_MICROCHIP + +config ENC28J60 + tristate "ENC28J60 support" + depends on SPI + select CRC32 + ---help--- + Support for the Microchip EN28J60 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called enc28j60. + +config ENC28J60_WRITEVERIFY + bool "Enable write verify" + depends on ENC28J60 + ---help--- + Enable the verify after the buffer write useful for debugging purpose. + If unsure, say N. + +config ENCX24J600 + tristate "ENCX24J600 support" + depends on SPI + ---help--- + Support for the Microchip ENC424J600/624J600 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called encx24j600. + +config LAN743X + tristate "LAN743x support" + depends on PCI + select PHYLIB + select CRC16 + ---help--- + Support for the Microchip LAN743x PCI Express Gigabit Ethernet chip + + To compile this driver as a module, choose M here. The module will be + called lan743x. + +endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile new file mode 100644 index 000000000..538926d2b --- /dev/null +++ b/drivers/net/ethernet/microchip/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Microchip network device drivers. +# + +obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o +obj-$(CONFIG_LAN743X) += lan743x.o + +lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c new file mode 100644 index 000000000..f6ecfa778 --- /dev/null +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -0,0 +1,1685 @@ +/* + * Microchip ENC28J60 ethernet driver (MAC + PHY) + * + * Copyright (C) 2007 Eurek srl + * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com> + * based on enc28j60.c written by David Anders for 2.4 kernel version + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * $Id: enc28j60.c,v 1.22 2007/12/20 10:47:01 claudio Exp $ + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/tcp.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/of_net.h> + +#include "enc28j60_hw.h" + +#define DRV_NAME "enc28j60" +#define DRV_VERSION "1.02" + +#define SPI_OPLEN 1 + +#define ENC28J60_MSG_DEFAULT \ + (NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK) + +/* Buffer size required for the largest SPI transfer (i.e., reading a + * frame). */ +#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN) + +#define TX_TIMEOUT (4 * HZ) + +/* Max TX retries in case of collision as suggested by errata datasheet */ +#define MAX_TX_RETRYCOUNT 16 + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; + +/* Driver local data */ +struct enc28j60_net { + struct net_device *netdev; + struct spi_device *spi; + struct mutex lock; + struct sk_buff *tx_skb; + struct work_struct tx_work; + struct work_struct irq_work; + struct work_struct setrx_work; + struct work_struct restart_work; + u8 bank; /* current register bank selected */ + u16 next_pk_ptr; /* next packet pointer within FIFO */ + u16 max_pk_counter; /* statistics: max packet counter */ + u16 tx_retry_count; + bool hw_enable; + bool full_duplex; + int rxfilter; + u32 msg_enable; + u8 spi_transfer_buf[SPI_TRANSFER_BUF_LEN]; +}; + +/* use ethtool to change the level for any given device */ +static struct { + u32 msg_enable; +} debug = { -1 }; + +/* + * SPI read buffer + * wait for the SPI transfer and copy received data to destination + */ +static int +spi_read_buf(struct enc28j60_net *priv, int len, u8 *data) +{ + u8 *rx_buf = priv->spi_transfer_buf + 4; + u8 *tx_buf = priv->spi_transfer_buf; + struct spi_transfer tx = { + .tx_buf = tx_buf, + .len = SPI_OPLEN, + }; + struct spi_transfer rx = { + .rx_buf = rx_buf, + .len = len, + }; + struct spi_message msg; + int ret; + + tx_buf[0] = ENC28J60_READ_BUF_MEM; + + spi_message_init(&msg); + spi_message_add_tail(&tx, &msg); + spi_message_add_tail(&rx, &msg); + + ret = spi_sync(priv->spi, &msg); + if (ret == 0) { + memcpy(data, rx_buf, len); + ret = msg.status; + } + if (ret && netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n", + __func__, ret); + + return ret; +} + +/* + * SPI write buffer + */ +static int spi_write_buf(struct enc28j60_net *priv, int len, + const u8 *data) +{ + int ret; + + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0) + ret = -EINVAL; + else { + priv->spi_transfer_buf[0] = ENC28J60_WRITE_BUF_MEM; + memcpy(&priv->spi_transfer_buf[1], data, len); + ret = spi_write(priv->spi, priv->spi_transfer_buf, len + 1); + if (ret && netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n", + __func__, ret); + } + return ret; +} + +/* + * basic SPI read operation + */ +static u8 spi_read_op(struct enc28j60_net *priv, u8 op, + u8 addr) +{ + u8 tx_buf[2]; + u8 rx_buf[4]; + u8 val = 0; + int ret; + int slen = SPI_OPLEN; + + /* do dummy read if needed */ + if (addr & SPRD_MASK) + slen++; + + tx_buf[0] = op | (addr & ADDR_MASK); + ret = spi_write_then_read(priv->spi, tx_buf, 1, rx_buf, slen); + if (ret) + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n", + __func__, ret); + else + val = rx_buf[slen - 1]; + + return val; +} + +/* + * basic SPI write operation + */ +static int spi_write_op(struct enc28j60_net *priv, u8 op, + u8 addr, u8 val) +{ + int ret; + + priv->spi_transfer_buf[0] = op | (addr & ADDR_MASK); + priv->spi_transfer_buf[1] = val; + ret = spi_write(priv->spi, priv->spi_transfer_buf, 2); + if (ret && netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n", + __func__, ret); + return ret; +} + +static void enc28j60_soft_reset(struct enc28j60_net *priv) +{ + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + spi_write_op(priv, ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + /* Errata workaround #1, CLKRDY check is unreliable, + * delay at least 1 mS instead */ + udelay(2000); +} + +/* + * select the current register bank if necessary + */ +static void enc28j60_set_bank(struct enc28j60_net *priv, u8 addr) +{ + u8 b = (addr & BANK_MASK) >> 5; + + /* These registers (EIE, EIR, ESTAT, ECON2, ECON1) + * are present in all banks, no need to switch bank + */ + if (addr >= EIE && addr <= ECON1) + return; + + /* Clear or set each bank selection bit as needed */ + if ((b & ECON1_BSEL0) != (priv->bank & ECON1_BSEL0)) { + if (b & ECON1_BSEL0) + spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1, + ECON1_BSEL0); + else + spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1, + ECON1_BSEL0); + } + if ((b & ECON1_BSEL1) != (priv->bank & ECON1_BSEL1)) { + if (b & ECON1_BSEL1) + spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1, + ECON1_BSEL1); + else + spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1, + ECON1_BSEL1); + } + priv->bank = b; +} + +/* + * Register access routines through the SPI bus. + * Every register access comes in two flavours: + * - nolock_xxx: caller needs to invoke mutex_lock, usually to access + * atomically more than one register + * - locked_xxx: caller doesn't need to invoke mutex_lock, single access + * + * Some registers can be accessed through the bit field clear and + * bit field set to avoid a read modify write cycle. + */ + +/* + * Register bit field Set + */ +static void nolock_reg_bfset(struct enc28j60_net *priv, + u8 addr, u8 mask) +{ + enc28j60_set_bank(priv, addr); + spi_write_op(priv, ENC28J60_BIT_FIELD_SET, addr, mask); +} + +static void locked_reg_bfset(struct enc28j60_net *priv, + u8 addr, u8 mask) +{ + mutex_lock(&priv->lock); + nolock_reg_bfset(priv, addr, mask); + mutex_unlock(&priv->lock); +} + +/* + * Register bit field Clear + */ +static void nolock_reg_bfclr(struct enc28j60_net *priv, + u8 addr, u8 mask) +{ + enc28j60_set_bank(priv, addr); + spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, addr, mask); +} + +static void locked_reg_bfclr(struct enc28j60_net *priv, + u8 addr, u8 mask) +{ + mutex_lock(&priv->lock); + nolock_reg_bfclr(priv, addr, mask); + mutex_unlock(&priv->lock); +} + +/* + * Register byte read + */ +static int nolock_regb_read(struct enc28j60_net *priv, + u8 address) +{ + enc28j60_set_bank(priv, address); + return spi_read_op(priv, ENC28J60_READ_CTRL_REG, address); +} + +static int locked_regb_read(struct enc28j60_net *priv, + u8 address) +{ + int ret; + + mutex_lock(&priv->lock); + ret = nolock_regb_read(priv, address); + mutex_unlock(&priv->lock); + + return ret; +} + +/* + * Register word read + */ +static int nolock_regw_read(struct enc28j60_net *priv, + u8 address) +{ + int rl, rh; + + enc28j60_set_bank(priv, address); + rl = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address); + rh = spi_read_op(priv, ENC28J60_READ_CTRL_REG, address + 1); + + return (rh << 8) | rl; +} + +static int locked_regw_read(struct enc28j60_net *priv, + u8 address) +{ + int ret; + + mutex_lock(&priv->lock); + ret = nolock_regw_read(priv, address); + mutex_unlock(&priv->lock); + + return ret; +} + +/* + * Register byte write + */ +static void nolock_regb_write(struct enc28j60_net *priv, + u8 address, u8 data) +{ + enc28j60_set_bank(priv, address); + spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address, data); +} + +static void locked_regb_write(struct enc28j60_net *priv, + u8 address, u8 data) +{ + mutex_lock(&priv->lock); + nolock_regb_write(priv, address, data); + mutex_unlock(&priv->lock); +} + +/* + * Register word write + */ +static void nolock_regw_write(struct enc28j60_net *priv, + u8 address, u16 data) +{ + enc28j60_set_bank(priv, address); + spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address, (u8) data); + spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, address + 1, + (u8) (data >> 8)); +} + +static void locked_regw_write(struct enc28j60_net *priv, + u8 address, u16 data) +{ + mutex_lock(&priv->lock); + nolock_regw_write(priv, address, data); + mutex_unlock(&priv->lock); +} + +/* + * Buffer memory read + * Select the starting address and execute a SPI buffer read + */ +static void enc28j60_mem_read(struct enc28j60_net *priv, + u16 addr, int len, u8 *data) +{ + mutex_lock(&priv->lock); + nolock_regw_write(priv, ERDPTL, addr); +#ifdef CONFIG_ENC28J60_WRITEVERIFY + if (netif_msg_drv(priv)) { + u16 reg; + reg = nolock_regw_read(priv, ERDPTL); + if (reg != addr) + printk(KERN_DEBUG DRV_NAME ": %s() error writing ERDPT " + "(0x%04x - 0x%04x)\n", __func__, reg, addr); + } +#endif + spi_read_buf(priv, len, data); + mutex_unlock(&priv->lock); +} + +/* + * Write packet to enc28j60 TX buffer memory + */ +static void +enc28j60_packet_write(struct enc28j60_net *priv, int len, const u8 *data) +{ + mutex_lock(&priv->lock); + /* Set the write pointer to start of transmit buffer area */ + nolock_regw_write(priv, EWRPTL, TXSTART_INIT); +#ifdef CONFIG_ENC28J60_WRITEVERIFY + if (netif_msg_drv(priv)) { + u16 reg; + reg = nolock_regw_read(priv, EWRPTL); + if (reg != TXSTART_INIT) + printk(KERN_DEBUG DRV_NAME + ": %s() ERWPT:0x%04x != 0x%04x\n", + __func__, reg, TXSTART_INIT); + } +#endif + /* Set the TXND pointer to correspond to the packet size given */ + nolock_regw_write(priv, ETXNDL, TXSTART_INIT + len); + /* write per-packet control byte */ + spi_write_op(priv, ENC28J60_WRITE_BUF_MEM, 0, 0x00); + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME + ": %s() after control byte ERWPT:0x%04x\n", + __func__, nolock_regw_read(priv, EWRPTL)); + /* copy the packet into the transmit buffer */ + spi_write_buf(priv, len, data); + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME + ": %s() after write packet ERWPT:0x%04x, len=%d\n", + __func__, nolock_regw_read(priv, EWRPTL), len); + mutex_unlock(&priv->lock); +} + +static unsigned long msec20_to_jiffies; + +static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val) +{ + unsigned long timeout = jiffies + msec20_to_jiffies; + + /* 20 msec timeout read */ + while ((nolock_regb_read(priv, reg) & mask) != val) { + if (time_after(jiffies, timeout)) { + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, + "reg %02x ready timeout!\n", reg); + return -ETIMEDOUT; + } + cpu_relax(); + } + return 0; +} + +/* + * Wait until the PHY operation is complete. + */ +static int wait_phy_ready(struct enc28j60_net *priv) +{ + return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1; +} + +/* + * PHY register read + * PHY registers are not accessed directly, but through the MII + */ +static u16 enc28j60_phy_read(struct enc28j60_net *priv, u8 address) +{ + u16 ret; + + mutex_lock(&priv->lock); + /* set the PHY register address */ + nolock_regb_write(priv, MIREGADR, address); + /* start the register read operation */ + nolock_regb_write(priv, MICMD, MICMD_MIIRD); + /* wait until the PHY read completes */ + wait_phy_ready(priv); + /* quit reading */ + nolock_regb_write(priv, MICMD, 0x00); + /* return the data */ + ret = nolock_regw_read(priv, MIRDL); + mutex_unlock(&priv->lock); + + return ret; +} + +static int enc28j60_phy_write(struct enc28j60_net *priv, u8 address, u16 data) +{ + int ret; + + mutex_lock(&priv->lock); + /* set the PHY register address */ + nolock_regb_write(priv, MIREGADR, address); + /* write the PHY data */ + nolock_regw_write(priv, MIWRL, data); + /* wait until the PHY write completes and return */ + ret = wait_phy_ready(priv); + mutex_unlock(&priv->lock); + + return ret; +} + +/* + * Program the hardware MAC address from dev->dev_addr. + */ +static int enc28j60_set_hw_macaddr(struct net_device *ndev) +{ + int ret; + struct enc28j60_net *priv = netdev_priv(ndev); + + mutex_lock(&priv->lock); + if (!priv->hw_enable) { + if (netif_msg_drv(priv)) + printk(KERN_INFO DRV_NAME + ": %s: Setting MAC address to %pM\n", + ndev->name, ndev->dev_addr); + /* NOTE: MAC address in ENC28J60 is byte-backward */ + nolock_regb_write(priv, MAADR5, ndev->dev_addr[0]); + nolock_regb_write(priv, MAADR4, ndev->dev_addr[1]); + nolock_regb_write(priv, MAADR3, ndev->dev_addr[2]); + nolock_regb_write(priv, MAADR2, ndev->dev_addr[3]); + nolock_regb_write(priv, MAADR1, ndev->dev_addr[4]); + nolock_regb_write(priv, MAADR0, ndev->dev_addr[5]); + ret = 0; + } else { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME + ": %s() Hardware must be disabled to set " + "Mac address\n", __func__); + ret = -EBUSY; + } + mutex_unlock(&priv->lock); + return ret; +} + +/* + * Store the new hardware address in dev->dev_addr, and update the MAC. + */ +static int enc28j60_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return enc28j60_set_hw_macaddr(dev); +} + +/* + * Debug routine to dump useful register contents + */ +static void enc28j60_dump_regs(struct enc28j60_net *priv, const char *msg) +{ + mutex_lock(&priv->lock); + printk(KERN_DEBUG DRV_NAME " %s\n" + "HwRevID: 0x%02x\n" + "Cntrl: ECON1 ECON2 ESTAT EIR EIE\n" + " 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n" + "MAC : MACON1 MACON3 MACON4\n" + " 0x%02x 0x%02x 0x%02x\n" + "Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\n" + " 0x%04x 0x%04x 0x%04x 0x%04x " + "0x%02x 0x%02x 0x%04x\n" + "Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\n" + " 0x%04x 0x%04x 0x%02x 0x%02x 0x%02x\n", + msg, nolock_regb_read(priv, EREVID), + nolock_regb_read(priv, ECON1), nolock_regb_read(priv, ECON2), + nolock_regb_read(priv, ESTAT), nolock_regb_read(priv, EIR), + nolock_regb_read(priv, EIE), nolock_regb_read(priv, MACON1), + nolock_regb_read(priv, MACON3), nolock_regb_read(priv, MACON4), + nolock_regw_read(priv, ERXSTL), nolock_regw_read(priv, ERXNDL), + nolock_regw_read(priv, ERXWRPTL), + nolock_regw_read(priv, ERXRDPTL), + nolock_regb_read(priv, ERXFCON), + nolock_regb_read(priv, EPKTCNT), + nolock_regw_read(priv, MAMXFLL), nolock_regw_read(priv, ETXSTL), + nolock_regw_read(priv, ETXNDL), + nolock_regb_read(priv, MACLCON1), + nolock_regb_read(priv, MACLCON2), + nolock_regb_read(priv, MAPHSUP)); + mutex_unlock(&priv->lock); +} + +/* + * ERXRDPT need to be set always at odd addresses, refer to errata datasheet + */ +static u16 erxrdpt_workaround(u16 next_packet_ptr, u16 start, u16 end) +{ + u16 erxrdpt; + + if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end)) + erxrdpt = end; + else + erxrdpt = next_packet_ptr - 1; + + return erxrdpt; +} + +/* + * Calculate wrap around when reading beyond the end of the RX buffer + */ +static u16 rx_packet_start(u16 ptr) +{ + if (ptr + RSV_SIZE > RXEND_INIT) + return (ptr + RSV_SIZE) - (RXEND_INIT - RXSTART_INIT + 1); + else + return ptr + RSV_SIZE; +} + +static void nolock_rxfifo_init(struct enc28j60_net *priv, u16 start, u16 end) +{ + u16 erxrdpt; + + if (start > 0x1FFF || end > 0x1FFF || start > end) { + if (netif_msg_drv(priv)) + printk(KERN_ERR DRV_NAME ": %s(%d, %d) RXFIFO " + "bad parameters!\n", __func__, start, end); + return; + } + /* set receive buffer start + end */ + priv->next_pk_ptr = start; + nolock_regw_write(priv, ERXSTL, start); + erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end); + nolock_regw_write(priv, ERXRDPTL, erxrdpt); + nolock_regw_write(priv, ERXNDL, end); +} + +static void nolock_txfifo_init(struct enc28j60_net *priv, u16 start, u16 end) +{ + if (start > 0x1FFF || end > 0x1FFF || start > end) { + if (netif_msg_drv(priv)) + printk(KERN_ERR DRV_NAME ": %s(%d, %d) TXFIFO " + "bad parameters!\n", __func__, start, end); + return; + } + /* set transmit buffer start + end */ + nolock_regw_write(priv, ETXSTL, start); + nolock_regw_write(priv, ETXNDL, end); +} + +/* + * Low power mode shrinks power consumption about 100x, so we'd like + * the chip to be in that mode whenever it's inactive. (However, we + * can't stay in lowpower mode during suspend with WOL active.) + */ +static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low) +{ + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, "%s power...\n", + is_low ? "low" : "high"); + + mutex_lock(&priv->lock); + if (is_low) { + nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); + poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0); + poll_ready(priv, ECON1, ECON1_TXRTS, 0); + /* ECON2_VRPS was set during initialization */ + nolock_reg_bfset(priv, ECON2, ECON2_PWRSV); + } else { + nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV); + poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY); + /* caller sets ECON1_RXEN */ + } + mutex_unlock(&priv->lock); +} + +static int enc28j60_hw_init(struct enc28j60_net *priv) +{ + u8 reg; + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() - %s\n", __func__, + priv->full_duplex ? "FullDuplex" : "HalfDuplex"); + + mutex_lock(&priv->lock); + /* first reset the chip */ + enc28j60_soft_reset(priv); + /* Clear ECON1 */ + spi_write_op(priv, ENC28J60_WRITE_CTRL_REG, ECON1, 0x00); + priv->bank = 0; + priv->hw_enable = false; + priv->tx_retry_count = 0; + priv->max_pk_counter = 0; + priv->rxfilter = RXFILTER_NORMAL; + /* enable address auto increment and voltage regulator powersave */ + nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS); + + nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT); + nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT); + mutex_unlock(&priv->lock); + + /* + * Check the RevID. + * If it's 0x00 or 0xFF probably the enc28j60 is not mounted or + * damaged + */ + reg = locked_regb_read(priv, EREVID); + if (netif_msg_drv(priv)) + printk(KERN_INFO DRV_NAME ": chip RevID: 0x%02x\n", reg); + if (reg == 0x00 || reg == 0xff) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() Invalid RevId %d\n", + __func__, reg); + return 0; + } + + /* default filter mode: (unicast OR broadcast) AND crc valid */ + locked_regb_write(priv, ERXFCON, + ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN); + + /* enable MAC receive */ + locked_regb_write(priv, MACON1, + MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); + /* enable automatic padding and CRC operations */ + if (priv->full_duplex) { + locked_regb_write(priv, MACON3, + MACON3_PADCFG0 | MACON3_TXCRCEN | + MACON3_FRMLNEN | MACON3_FULDPX); + /* set inter-frame gap (non-back-to-back) */ + locked_regb_write(priv, MAIPGL, 0x12); + /* set inter-frame gap (back-to-back) */ + locked_regb_write(priv, MABBIPG, 0x15); + } else { + locked_regb_write(priv, MACON3, + MACON3_PADCFG0 | MACON3_TXCRCEN | + MACON3_FRMLNEN); + locked_regb_write(priv, MACON4, 1 << 6); /* DEFER bit */ + /* set inter-frame gap (non-back-to-back) */ + locked_regw_write(priv, MAIPGL, 0x0C12); + /* set inter-frame gap (back-to-back) */ + locked_regb_write(priv, MABBIPG, 0x12); + } + /* + * MACLCON1 (default) + * MACLCON2 (default) + * Set the maximum packet size which the controller will accept + */ + locked_regw_write(priv, MAMXFLL, MAX_FRAMELEN); + + /* Configure LEDs */ + if (!enc28j60_phy_write(priv, PHLCON, ENC28J60_LAMPS_MODE)) + return 0; + + if (priv->full_duplex) { + if (!enc28j60_phy_write(priv, PHCON1, PHCON1_PDPXMD)) + return 0; + if (!enc28j60_phy_write(priv, PHCON2, 0x00)) + return 0; + } else { + if (!enc28j60_phy_write(priv, PHCON1, 0x00)) + return 0; + if (!enc28j60_phy_write(priv, PHCON2, PHCON2_HDLDIS)) + return 0; + } + if (netif_msg_hw(priv)) + enc28j60_dump_regs(priv, "Hw initialized."); + + return 1; +} + +static void enc28j60_hw_enable(struct enc28j60_net *priv) +{ + /* enable interrupts */ + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n", + __func__); + + enc28j60_phy_write(priv, PHIE, PHIE_PGEIE | PHIE_PLNKIE); + + mutex_lock(&priv->lock); + nolock_reg_bfclr(priv, EIR, EIR_DMAIF | EIR_LINKIF | + EIR_TXIF | EIR_TXERIF | EIR_RXERIF | EIR_PKTIF); + nolock_regb_write(priv, EIE, EIE_INTIE | EIE_PKTIE | EIE_LINKIE | + EIE_TXIE | EIE_TXERIE | EIE_RXERIE); + + /* enable receive logic */ + nolock_reg_bfset(priv, ECON1, ECON1_RXEN); + priv->hw_enable = true; + mutex_unlock(&priv->lock); +} + +static void enc28j60_hw_disable(struct enc28j60_net *priv) +{ + mutex_lock(&priv->lock); + /* disable interrutps and packet reception */ + nolock_regb_write(priv, EIE, 0x00); + nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); + priv->hw_enable = false; + mutex_unlock(&priv->lock); +} + +static int +enc28j60_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex) +{ + struct enc28j60_net *priv = netdev_priv(ndev); + int ret = 0; + + if (!priv->hw_enable) { + /* link is in low power mode now; duplex setting + * will take effect on next enc28j60_hw_init(). + */ + if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) + priv->full_duplex = (duplex == DUPLEX_FULL); + else { + if (netif_msg_link(priv)) + dev_warn(&ndev->dev, + "unsupported link setting\n"); + ret = -EOPNOTSUPP; + } + } else { + if (netif_msg_link(priv)) + dev_warn(&ndev->dev, "Warning: hw must be disabled " + "to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +/* + * Read the Transmit Status Vector + */ +static void enc28j60_read_tsv(struct enc28j60_net *priv, u8 tsv[TSV_SIZE]) +{ + int endptr; + + endptr = locked_regw_read(priv, ETXNDL); + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": reading TSV at addr:0x%04x\n", + endptr + 1); + enc28j60_mem_read(priv, endptr + 1, TSV_SIZE, tsv); +} + +static void enc28j60_dump_tsv(struct enc28j60_net *priv, const char *msg, + u8 tsv[TSV_SIZE]) +{ + u16 tmp1, tmp2; + + printk(KERN_DEBUG DRV_NAME ": %s - TSV:\n", msg); + tmp1 = tsv[1]; + tmp1 <<= 8; + tmp1 |= tsv[0]; + + tmp2 = tsv[5]; + tmp2 <<= 8; + tmp2 |= tsv[4]; + + printk(KERN_DEBUG DRV_NAME ": ByteCount: %d, CollisionCount: %d," + " TotByteOnWire: %d\n", tmp1, tsv[2] & 0x0f, tmp2); + printk(KERN_DEBUG DRV_NAME ": TxDone: %d, CRCErr:%d, LenChkErr: %d," + " LenOutOfRange: %d\n", TSV_GETBIT(tsv, TSV_TXDONE), + TSV_GETBIT(tsv, TSV_TXCRCERROR), + TSV_GETBIT(tsv, TSV_TXLENCHKERROR), + TSV_GETBIT(tsv, TSV_TXLENOUTOFRANGE)); + printk(KERN_DEBUG DRV_NAME ": Multicast: %d, Broadcast: %d, " + "PacketDefer: %d, ExDefer: %d\n", + TSV_GETBIT(tsv, TSV_TXMULTICAST), + TSV_GETBIT(tsv, TSV_TXBROADCAST), + TSV_GETBIT(tsv, TSV_TXPACKETDEFER), + TSV_GETBIT(tsv, TSV_TXEXDEFER)); + printk(KERN_DEBUG DRV_NAME ": ExCollision: %d, LateCollision: %d, " + "Giant: %d, Underrun: %d\n", + TSV_GETBIT(tsv, TSV_TXEXCOLLISION), + TSV_GETBIT(tsv, TSV_TXLATECOLLISION), + TSV_GETBIT(tsv, TSV_TXGIANT), TSV_GETBIT(tsv, TSV_TXUNDERRUN)); + printk(KERN_DEBUG DRV_NAME ": ControlFrame: %d, PauseFrame: %d, " + "BackPressApp: %d, VLanTagFrame: %d\n", + TSV_GETBIT(tsv, TSV_TXCONTROLFRAME), + TSV_GETBIT(tsv, TSV_TXPAUSEFRAME), + TSV_GETBIT(tsv, TSV_BACKPRESSUREAPP), + TSV_GETBIT(tsv, TSV_TXVLANTAGFRAME)); +} + +/* + * Receive Status vector + */ +static void enc28j60_dump_rsv(struct enc28j60_net *priv, const char *msg, + u16 pk_ptr, int len, u16 sts) +{ + printk(KERN_DEBUG DRV_NAME ": %s - NextPk: 0x%04x - RSV:\n", + msg, pk_ptr); + printk(KERN_DEBUG DRV_NAME ": ByteCount: %d, DribbleNibble: %d\n", len, + RSV_GETBIT(sts, RSV_DRIBBLENIBBLE)); + printk(KERN_DEBUG DRV_NAME ": RxOK: %d, CRCErr:%d, LenChkErr: %d," + " LenOutOfRange: %d\n", RSV_GETBIT(sts, RSV_RXOK), + RSV_GETBIT(sts, RSV_CRCERROR), + RSV_GETBIT(sts, RSV_LENCHECKERR), + RSV_GETBIT(sts, RSV_LENOUTOFRANGE)); + printk(KERN_DEBUG DRV_NAME ": Multicast: %d, Broadcast: %d, " + "LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(sts, RSV_RXMULTICAST), + RSV_GETBIT(sts, RSV_RXBROADCAST), + RSV_GETBIT(sts, RSV_RXLONGEVDROPEV), + RSV_GETBIT(sts, RSV_CARRIEREV)); + printk(KERN_DEBUG DRV_NAME ": ControlFrame: %d, PauseFrame: %d," + " UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(sts, RSV_RXCONTROLFRAME), + RSV_GETBIT(sts, RSV_RXPAUSEFRAME), + RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(sts, RSV_RXTYPEVLAN)); +} + +static void dump_packet(const char *msg, int len, const char *data) +{ + printk(KERN_DEBUG DRV_NAME ": %s - packet len:%d\n", msg, len); + print_hex_dump(KERN_DEBUG, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1, + data, len, true); +} + +/* + * Hardware receive function. + * Read the buffer memory, update the FIFO pointer to free the buffer, + * check the status vector and decrement the packet counter. + */ +static void enc28j60_hw_rx(struct net_device *ndev) +{ + struct enc28j60_net *priv = netdev_priv(ndev); + struct sk_buff *skb = NULL; + u16 erxrdpt, next_packet, rxstat; + u8 rsv[RSV_SIZE]; + int len; + + if (netif_msg_rx_status(priv)) + printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n", + priv->next_pk_ptr); + + if (unlikely(priv->next_pk_ptr > RXEND_INIT)) { + if (netif_msg_rx_err(priv)) + dev_err(&ndev->dev, + "%s() Invalid packet address!! 0x%04x\n", + __func__, priv->next_pk_ptr); + /* packet address corrupted: reset RX logic */ + mutex_lock(&priv->lock); + nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); + nolock_reg_bfset(priv, ECON1, ECON1_RXRST); + nolock_reg_bfclr(priv, ECON1, ECON1_RXRST); + nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT); + nolock_reg_bfclr(priv, EIR, EIR_RXERIF); + nolock_reg_bfset(priv, ECON1, ECON1_RXEN); + mutex_unlock(&priv->lock); + ndev->stats.rx_errors++; + return; + } + /* Read next packet pointer and rx status vector */ + enc28j60_mem_read(priv, priv->next_pk_ptr, sizeof(rsv), rsv); + + next_packet = rsv[1]; + next_packet <<= 8; + next_packet |= rsv[0]; + + len = rsv[3]; + len <<= 8; + len |= rsv[2]; + + rxstat = rsv[5]; + rxstat <<= 8; + rxstat |= rsv[4]; + + if (netif_msg_rx_status(priv)) + enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat); + + if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) { + if (netif_msg_rx_err(priv)) + dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat); + ndev->stats.rx_errors++; + if (RSV_GETBIT(rxstat, RSV_CRCERROR)) + ndev->stats.rx_crc_errors++; + if (RSV_GETBIT(rxstat, RSV_LENCHECKERR)) + ndev->stats.rx_frame_errors++; + if (len > MAX_FRAMELEN) + ndev->stats.rx_over_errors++; + } else { + skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN); + if (!skb) { + if (netif_msg_rx_err(priv)) + dev_err(&ndev->dev, + "out of memory for Rx'd frame\n"); + ndev->stats.rx_dropped++; + } else { + skb_reserve(skb, NET_IP_ALIGN); + /* copy the packet from the receive buffer */ + enc28j60_mem_read(priv, + rx_packet_start(priv->next_pk_ptr), + len, skb_put(skb, len)); + if (netif_msg_pktdata(priv)) + dump_packet(__func__, skb->len, skb->data); + skb->protocol = eth_type_trans(skb, ndev); + /* update statistics */ + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + netif_rx_ni(skb); + } + } + /* + * Move the RX read pointer to the start of the next + * received packet. + * This frees the memory we just read out + */ + erxrdpt = erxrdpt_workaround(next_packet, RXSTART_INIT, RXEND_INIT); + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n", + __func__, erxrdpt); + + mutex_lock(&priv->lock); + nolock_regw_write(priv, ERXRDPTL, erxrdpt); +#ifdef CONFIG_ENC28J60_WRITEVERIFY + if (netif_msg_drv(priv)) { + u16 reg; + reg = nolock_regw_read(priv, ERXRDPTL); + if (reg != erxrdpt) + printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT verify " + "error (0x%04x - 0x%04x)\n", __func__, + reg, erxrdpt); + } +#endif + priv->next_pk_ptr = next_packet; + /* we are done with this packet, decrement the packet counter */ + nolock_reg_bfset(priv, ECON2, ECON2_PKTDEC); + mutex_unlock(&priv->lock); +} + +/* + * Calculate free space in RxFIFO + */ +static int enc28j60_get_free_rxfifo(struct enc28j60_net *priv) +{ + int epkcnt, erxst, erxnd, erxwr, erxrd; + int free_space; + + mutex_lock(&priv->lock); + epkcnt = nolock_regb_read(priv, EPKTCNT); + if (epkcnt >= 255) + free_space = -1; + else { + erxst = nolock_regw_read(priv, ERXSTL); + erxnd = nolock_regw_read(priv, ERXNDL); + erxwr = nolock_regw_read(priv, ERXWRPTL); + erxrd = nolock_regw_read(priv, ERXRDPTL); + + if (erxwr > erxrd) + free_space = (erxnd - erxst) - (erxwr - erxrd); + else if (erxwr == erxrd) + free_space = (erxnd - erxst); + else + free_space = erxrd - erxwr - 1; + } + mutex_unlock(&priv->lock); + if (netif_msg_rx_status(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() free_space = %d\n", + __func__, free_space); + return free_space; +} + +/* + * Access the PHY to determine link status + */ +static void enc28j60_check_link_status(struct net_device *ndev) +{ + struct enc28j60_net *priv = netdev_priv(ndev); + u16 reg; + int duplex; + + reg = enc28j60_phy_read(priv, PHSTAT2); + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() PHSTAT1: %04x, " + "PHSTAT2: %04x\n", __func__, + enc28j60_phy_read(priv, PHSTAT1), reg); + duplex = reg & PHSTAT2_DPXSTAT; + + if (reg & PHSTAT2_LSTAT) { + netif_carrier_on(ndev); + if (netif_msg_ifup(priv)) + dev_info(&ndev->dev, "link up - %s\n", + duplex ? "Full duplex" : "Half duplex"); + } else { + if (netif_msg_ifdown(priv)) + dev_info(&ndev->dev, "link down\n"); + netif_carrier_off(ndev); + } +} + +static void enc28j60_tx_clear(struct net_device *ndev, bool err) +{ + struct enc28j60_net *priv = netdev_priv(ndev); + + if (err) + ndev->stats.tx_errors++; + else + ndev->stats.tx_packets++; + + if (priv->tx_skb) { + if (!err) + ndev->stats.tx_bytes += priv->tx_skb->len; + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + } + locked_reg_bfclr(priv, ECON1, ECON1_TXRTS); + netif_wake_queue(ndev); +} + +/* + * RX handler + * ignore PKTIF because is unreliable! (look at the errata datasheet) + * check EPKTCNT is the suggested workaround. + * We don't need to clear interrupt flag, automatically done when + * enc28j60_hw_rx() decrements the packet counter. + * Returns how many packet processed. + */ +static int enc28j60_rx_interrupt(struct net_device *ndev) +{ + struct enc28j60_net *priv = netdev_priv(ndev); + int pk_counter, ret; + + pk_counter = locked_regb_read(priv, EPKTCNT); + if (pk_counter && netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter); + if (pk_counter > priv->max_pk_counter) { + /* update statistics */ + priv->max_pk_counter = pk_counter; + if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1) + printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n", + priv->max_pk_counter); + } + ret = pk_counter; + while (pk_counter-- > 0) + enc28j60_hw_rx(ndev); + + return ret; +} + +static void enc28j60_irq_work_handler(struct work_struct *work) +{ + struct enc28j60_net *priv = + container_of(work, struct enc28j60_net, irq_work); + struct net_device *ndev = priv->netdev; + int intflags, loop; + + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + /* disable further interrupts */ + locked_reg_bfclr(priv, EIE, EIE_INTIE); + + do { + loop = 0; + intflags = locked_regb_read(priv, EIR); + /* DMA interrupt handler (not currently used) */ + if ((intflags & EIR_DMAIF) != 0) { + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME + ": intDMA(%d)\n", loop); + locked_reg_bfclr(priv, EIR, EIR_DMAIF); + } + /* LINK changed handler */ + if ((intflags & EIR_LINKIF) != 0) { + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME + ": intLINK(%d)\n", loop); + enc28j60_check_link_status(ndev); + /* read PHIR to clear the flag */ + enc28j60_phy_read(priv, PHIR); + } + /* TX complete handler */ + if (((intflags & EIR_TXIF) != 0) && + ((intflags & EIR_TXERIF) == 0)) { + bool err = false; + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME + ": intTX(%d)\n", loop); + priv->tx_retry_count = 0; + if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT) { + if (netif_msg_tx_err(priv)) + dev_err(&ndev->dev, + "Tx Error (aborted)\n"); + err = true; + } + if (netif_msg_tx_done(priv)) { + u8 tsv[TSV_SIZE]; + enc28j60_read_tsv(priv, tsv); + enc28j60_dump_tsv(priv, "Tx Done", tsv); + } + enc28j60_tx_clear(ndev, err); + locked_reg_bfclr(priv, EIR, EIR_TXIF); + } + /* TX Error handler */ + if ((intflags & EIR_TXERIF) != 0) { + u8 tsv[TSV_SIZE]; + + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME + ": intTXErr(%d)\n", loop); + locked_reg_bfclr(priv, ECON1, ECON1_TXRTS); + enc28j60_read_tsv(priv, tsv); + if (netif_msg_tx_err(priv)) + enc28j60_dump_tsv(priv, "Tx Error", tsv); + /* Reset TX logic */ + mutex_lock(&priv->lock); + nolock_reg_bfset(priv, ECON1, ECON1_TXRST); + nolock_reg_bfclr(priv, ECON1, ECON1_TXRST); + nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT); + mutex_unlock(&priv->lock); + /* Transmit Late collision check for retransmit */ + if (TSV_GETBIT(tsv, TSV_TXLATECOLLISION)) { + if (netif_msg_tx_err(priv)) + printk(KERN_DEBUG DRV_NAME + ": LateCollision TXErr (%d)\n", + priv->tx_retry_count); + if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT) + locked_reg_bfset(priv, ECON1, + ECON1_TXRTS); + else + enc28j60_tx_clear(ndev, true); + } else + enc28j60_tx_clear(ndev, true); + locked_reg_bfclr(priv, EIR, EIR_TXERIF | EIR_TXIF); + } + /* RX Error handler */ + if ((intflags & EIR_RXERIF) != 0) { + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME + ": intRXErr(%d)\n", loop); + /* Check free FIFO space to flag RX overrun */ + if (enc28j60_get_free_rxfifo(priv) <= 0) { + if (netif_msg_rx_err(priv)) + printk(KERN_DEBUG DRV_NAME + ": RX Overrun\n"); + ndev->stats.rx_dropped++; + } + locked_reg_bfclr(priv, EIR, EIR_RXERIF); + } + /* RX handler */ + if (enc28j60_rx_interrupt(ndev)) + loop++; + } while (loop); + + /* re-enable interrupts */ + locked_reg_bfset(priv, EIE, EIE_INTIE); + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__); +} + +/* + * Hardware transmit function. + * Fill the buffer memory and send the contents of the transmit buffer + * onto the network + */ +static void enc28j60_hw_tx(struct enc28j60_net *priv) +{ + BUG_ON(!priv->tx_skb); + + if (netif_msg_tx_queued(priv)) + printk(KERN_DEBUG DRV_NAME + ": Tx Packet Len:%d\n", priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet(__func__, + priv->tx_skb->len, priv->tx_skb->data); + enc28j60_packet_write(priv, priv->tx_skb->len, priv->tx_skb->data); + +#ifdef CONFIG_ENC28J60_WRITEVERIFY + /* readback and verify written data */ + if (netif_msg_drv(priv)) { + int test_len, k; + u8 test_buf[64]; /* limit the test to the first 64 bytes */ + int okflag; + + test_len = priv->tx_skb->len; + if (test_len > sizeof(test_buf)) + test_len = sizeof(test_buf); + + /* + 1 to skip control byte */ + enc28j60_mem_read(priv, TXSTART_INIT + 1, test_len, test_buf); + okflag = 1; + for (k = 0; k < test_len; k++) { + if (priv->tx_skb->data[k] != test_buf[k]) { + printk(KERN_DEBUG DRV_NAME + ": Error, %d location differ: " + "0x%02x-0x%02x\n", k, + priv->tx_skb->data[k], test_buf[k]); + okflag = 0; + } + } + if (!okflag) + printk(KERN_DEBUG DRV_NAME ": Tx write buffer, " + "verify ERROR!\n"); + } +#endif + /* set TX request flag */ + locked_reg_bfset(priv, ECON1, ECON1_TXRTS); +} + +static netdev_tx_t enc28j60_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct enc28j60_net *priv = netdev_priv(dev); + + if (netif_msg_tx_queued(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + /* If some error occurs while trying to transmit this + * packet, you should return '1' from this function. + * In such a case you _may not_ do anything to the + * SKB, it is still owned by the network queueing + * layer when an error is returned. This means you + * may not modify any SKB fields, you may not free + * the SKB, etc. + */ + netif_stop_queue(dev); + + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + schedule_work(&priv->tx_work); + + return NETDEV_TX_OK; +} + +static void enc28j60_tx_work_handler(struct work_struct *work) +{ + struct enc28j60_net *priv = + container_of(work, struct enc28j60_net, tx_work); + + /* actual delivery of data */ + enc28j60_hw_tx(priv); +} + +static irqreturn_t enc28j60_irq(int irq, void *dev_id) +{ + struct enc28j60_net *priv = dev_id; + + /* + * Can't do anything in interrupt context because we need to + * block (spi_sync() is blocking) so fire of the interrupt + * handling workqueue. + * Remember that we access enc28j60 registers through SPI bus + * via spi_sync() call. + */ + schedule_work(&priv->irq_work); + + return IRQ_HANDLED; +} + +static void enc28j60_tx_timeout(struct net_device *ndev) +{ + struct enc28j60_net *priv = netdev_priv(ndev); + + if (netif_msg_timer(priv)) + dev_err(&ndev->dev, DRV_NAME " tx timeout\n"); + + ndev->stats.tx_errors++; + /* can't restart safely under softirq */ + schedule_work(&priv->restart_work); +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int enc28j60_net_open(struct net_device *dev) +{ + struct enc28j60_net *priv = netdev_priv(dev); + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + if (!is_valid_ether_addr(dev->dev_addr)) { + if (netif_msg_ifup(priv)) + dev_err(&dev->dev, "invalid MAC address %pM\n", + dev->dev_addr); + return -EADDRNOTAVAIL; + } + /* Reset the hardware here (and take it out of low power mode) */ + enc28j60_lowpower(priv, false); + enc28j60_hw_disable(priv); + if (!enc28j60_hw_init(priv)) { + if (netif_msg_ifup(priv)) + dev_err(&dev->dev, "hw_reset() failed\n"); + return -EINVAL; + } + /* Update the MAC address (in case user has changed it) */ + enc28j60_set_hw_macaddr(dev); + /* Enable interrupts */ + enc28j60_hw_enable(priv); + /* check link status */ + enc28j60_check_link_status(dev); + /* We are now ready to accept transmit requests from + * the queueing layer of the networking. + */ + netif_start_queue(dev); + + return 0; +} + +/* The inverse routine to net_open(). */ +static int enc28j60_net_close(struct net_device *dev) +{ + struct enc28j60_net *priv = netdev_priv(dev); + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + enc28j60_hw_disable(priv); + enc28j60_lowpower(priv, true); + netif_stop_queue(dev); + + return 0; +} + +/* + * Set or clear the multicast filter for this adapter + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, filter out multicast packets + * num_addrs > 0 Multicast mode, receive normal and MC packets + */ +static void enc28j60_set_multicast_list(struct net_device *dev) +{ + struct enc28j60_net *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + if (netif_msg_link(priv)) + dev_info(&dev->dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + if (netif_msg_link(priv)) + dev_info(&dev->dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + if (netif_msg_link(priv)) + dev_info(&dev->dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + schedule_work(&priv->setrx_work); +} + +static void enc28j60_setrx_work_handler(struct work_struct *work) +{ + struct enc28j60_net *priv = + container_of(work, struct enc28j60_net, setrx_work); + + if (priv->rxfilter == RXFILTER_PROMISC) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": promiscuous mode\n"); + locked_regb_write(priv, ERXFCON, 0x00); + } else if (priv->rxfilter == RXFILTER_MULTI) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": multicast mode\n"); + locked_regb_write(priv, ERXFCON, + ERXFCON_UCEN | ERXFCON_CRCEN | + ERXFCON_BCEN | ERXFCON_MCEN); + } else { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": normal mode\n"); + locked_regb_write(priv, ERXFCON, + ERXFCON_UCEN | ERXFCON_CRCEN | + ERXFCON_BCEN); + } +} + +static void enc28j60_restart_work_handler(struct work_struct *work) +{ + struct enc28j60_net *priv = + container_of(work, struct enc28j60_net, restart_work); + struct net_device *ndev = priv->netdev; + int ret; + + rtnl_lock(); + if (netif_running(ndev)) { + enc28j60_net_close(ndev); + ret = enc28j60_net_open(ndev); + if (unlikely(ret)) { + dev_info(&ndev->dev, " could not restart %d\n", ret); + dev_close(ndev); + } + } + rtnl_unlock(); +} + +/* ......................... ETHTOOL SUPPORT ........................... */ + +static void +enc28j60_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, + dev_name(dev->dev.parent), sizeof(info->bus_info)); +} + +static int +enc28j60_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct enc28j60_net *priv = netdev_priv(dev); + + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + + cmd->base.speed = SPEED_10; + cmd->base.duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->base.port = PORT_TP; + cmd->base.autoneg = AUTONEG_DISABLE; + + return 0; +} + +static int +enc28j60_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + return enc28j60_setlink(dev, cmd->base.autoneg, + cmd->base.speed, cmd->base.duplex); +} + +static u32 enc28j60_get_msglevel(struct net_device *dev) +{ + struct enc28j60_net *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void enc28j60_set_msglevel(struct net_device *dev, u32 val) +{ + struct enc28j60_net *priv = netdev_priv(dev); + priv->msg_enable = val; +} + +static const struct ethtool_ops enc28j60_ethtool_ops = { + .get_drvinfo = enc28j60_get_drvinfo, + .get_msglevel = enc28j60_get_msglevel, + .set_msglevel = enc28j60_set_msglevel, + .get_link_ksettings = enc28j60_get_link_ksettings, + .set_link_ksettings = enc28j60_set_link_ksettings, +}; + +static int enc28j60_chipset_init(struct net_device *dev) +{ + struct enc28j60_net *priv = netdev_priv(dev); + + return enc28j60_hw_init(priv); +} + +static const struct net_device_ops enc28j60_netdev_ops = { + .ndo_open = enc28j60_net_open, + .ndo_stop = enc28j60_net_close, + .ndo_start_xmit = enc28j60_send_packet, + .ndo_set_rx_mode = enc28j60_set_multicast_list, + .ndo_set_mac_address = enc28j60_set_mac_address, + .ndo_tx_timeout = enc28j60_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int enc28j60_probe(struct spi_device *spi) +{ + struct net_device *dev; + struct enc28j60_net *priv; + const void *macaddr; + int ret = 0; + + if (netif_msg_drv(&debug)) + dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n", + DRV_VERSION); + + dev = alloc_etherdev(sizeof(struct enc28j60_net)); + if (!dev) { + ret = -ENOMEM; + goto error_alloc; + } + priv = netdev_priv(dev); + + priv->netdev = dev; /* priv to netdev reference */ + priv->spi = spi; /* priv to spi reference */ + priv->msg_enable = netif_msg_init(debug.msg_enable, + ENC28J60_MSG_DEFAULT); + mutex_init(&priv->lock); + INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler); + INIT_WORK(&priv->setrx_work, enc28j60_setrx_work_handler); + INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler); + INIT_WORK(&priv->restart_work, enc28j60_restart_work_handler); + spi_set_drvdata(spi, priv); /* spi to priv reference */ + SET_NETDEV_DEV(dev, &spi->dev); + + if (!enc28j60_chipset_init(dev)) { + if (netif_msg_probe(priv)) + dev_info(&spi->dev, DRV_NAME " chip not found\n"); + ret = -EIO; + goto error_irq; + } + + macaddr = of_get_mac_address(spi->dev.of_node); + if (macaddr) + ether_addr_copy(dev->dev_addr, macaddr); + else + eth_hw_addr_random(dev); + enc28j60_set_hw_macaddr(dev); + + /* Board setup must set the relevant edge trigger type; + * level triggers won't currently work. + */ + ret = request_irq(spi->irq, enc28j60_irq, 0, DRV_NAME, priv); + if (ret < 0) { + if (netif_msg_probe(priv)) + dev_err(&spi->dev, DRV_NAME ": request irq %d failed " + "(ret = %d)\n", spi->irq, ret); + goto error_irq; + } + + dev->if_port = IF_PORT_10BASET; + dev->irq = spi->irq; + dev->netdev_ops = &enc28j60_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + dev->ethtool_ops = &enc28j60_ethtool_ops; + + enc28j60_lowpower(priv, true); + + ret = register_netdev(dev); + if (ret) { + if (netif_msg_probe(priv)) + dev_err(&spi->dev, "register netdev " DRV_NAME + " failed (ret = %d)\n", ret); + goto error_register; + } + dev_info(&dev->dev, DRV_NAME " driver registered\n"); + + return 0; + +error_register: + free_irq(spi->irq, priv); +error_irq: + free_netdev(dev); +error_alloc: + return ret; +} + +static int enc28j60_remove(struct spi_device *spi) +{ + struct enc28j60_net *priv = spi_get_drvdata(spi); + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": remove\n"); + + unregister_netdev(priv->netdev); + free_irq(spi->irq, priv); + free_netdev(priv->netdev); + + return 0; +} + +static const struct of_device_id enc28j60_dt_ids[] = { + { .compatible = "microchip,enc28j60" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, enc28j60_dt_ids); + +static struct spi_driver enc28j60_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = enc28j60_dt_ids, + }, + .probe = enc28j60_probe, + .remove = enc28j60_remove, +}; + +static int __init enc28j60_init(void) +{ + msec20_to_jiffies = msecs_to_jiffies(20); + + return spi_register_driver(&enc28j60_driver); +} + +module_init(enc28j60_init); + +static void __exit enc28j60_exit(void) +{ + spi_unregister_driver(&enc28j60_driver); +} + +module_exit(enc28j60_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>"); +MODULE_LICENSE("GPL"); +module_param_named(debug, debug.msg_enable, int, 0); +MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/enc28j60_hw.h b/drivers/net/ethernet/microchip/enc28j60_hw.h new file mode 100644 index 000000000..da4ab1725 --- /dev/null +++ b/drivers/net/ethernet/microchip/enc28j60_hw.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * enc28j60_hw.h: EDTP FrameThrower style enc28j60 registers + * + * $Id: enc28j60_hw.h,v 1.9 2007/12/14 11:59:16 claudio Exp $ + */ + +#ifndef _ENC28J60_HW_H +#define _ENC28J60_HW_H + +/* + * ENC28J60 Control Registers + * Control register definitions are a combination of address, + * bank number, and Ethernet/MAC/PHY indicator bits. + * - Register address (bits 0-4) + * - Bank number (bits 5-6) + * - MAC/MII indicator (bit 7) + */ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define SPRD_MASK 0x80 +/* All-bank registers */ +#define EIE 0x1B +#define EIR 0x1C +#define ESTAT 0x1D +#define ECON2 0x1E +#define ECON1 0x1F +/* Bank 0 registers */ +#define ERDPTL (0x00|0x00) +#define ERDPTH (0x01|0x00) +#define EWRPTL (0x02|0x00) +#define EWRPTH (0x03|0x00) +#define ETXSTL (0x04|0x00) +#define ETXSTH (0x05|0x00) +#define ETXNDL (0x06|0x00) +#define ETXNDH (0x07|0x00) +#define ERXSTL (0x08|0x00) +#define ERXSTH (0x09|0x00) +#define ERXNDL (0x0A|0x00) +#define ERXNDH (0x0B|0x00) +#define ERXRDPTL (0x0C|0x00) +#define ERXRDPTH (0x0D|0x00) +#define ERXWRPTL (0x0E|0x00) +#define ERXWRPTH (0x0F|0x00) +#define EDMASTL (0x10|0x00) +#define EDMASTH (0x11|0x00) +#define EDMANDL (0x12|0x00) +#define EDMANDH (0x13|0x00) +#define EDMADSTL (0x14|0x00) +#define EDMADSTH (0x15|0x00) +#define EDMACSL (0x16|0x00) +#define EDMACSH (0x17|0x00) +/* Bank 1 registers */ +#define EHT0 (0x00|0x20) +#define EHT1 (0x01|0x20) +#define EHT2 (0x02|0x20) +#define EHT3 (0x03|0x20) +#define EHT4 (0x04|0x20) +#define EHT5 (0x05|0x20) +#define EHT6 (0x06|0x20) +#define EHT7 (0x07|0x20) +#define EPMM0 (0x08|0x20) +#define EPMM1 (0x09|0x20) +#define EPMM2 (0x0A|0x20) +#define EPMM3 (0x0B|0x20) +#define EPMM4 (0x0C|0x20) +#define EPMM5 (0x0D|0x20) +#define EPMM6 (0x0E|0x20) +#define EPMM7 (0x0F|0x20) +#define EPMCSL (0x10|0x20) +#define EPMCSH (0x11|0x20) +#define EPMOL (0x14|0x20) +#define EPMOH (0x15|0x20) +#define EWOLIE (0x16|0x20) +#define EWOLIR (0x17|0x20) +#define ERXFCON (0x18|0x20) +#define EPKTCNT (0x19|0x20) +/* Bank 2 registers */ +#define MACON1 (0x00|0x40|SPRD_MASK) +/* #define MACON2 (0x01|0x40|SPRD_MASK) */ +#define MACON3 (0x02|0x40|SPRD_MASK) +#define MACON4 (0x03|0x40|SPRD_MASK) +#define MABBIPG (0x04|0x40|SPRD_MASK) +#define MAIPGL (0x06|0x40|SPRD_MASK) +#define MAIPGH (0x07|0x40|SPRD_MASK) +#define MACLCON1 (0x08|0x40|SPRD_MASK) +#define MACLCON2 (0x09|0x40|SPRD_MASK) +#define MAMXFLL (0x0A|0x40|SPRD_MASK) +#define MAMXFLH (0x0B|0x40|SPRD_MASK) +#define MAPHSUP (0x0D|0x40|SPRD_MASK) +#define MICON (0x11|0x40|SPRD_MASK) +#define MICMD (0x12|0x40|SPRD_MASK) +#define MIREGADR (0x14|0x40|SPRD_MASK) +#define MIWRL (0x16|0x40|SPRD_MASK) +#define MIWRH (0x17|0x40|SPRD_MASK) +#define MIRDL (0x18|0x40|SPRD_MASK) +#define MIRDH (0x19|0x40|SPRD_MASK) +/* Bank 3 registers */ +#define MAADR1 (0x00|0x60|SPRD_MASK) +#define MAADR0 (0x01|0x60|SPRD_MASK) +#define MAADR3 (0x02|0x60|SPRD_MASK) +#define MAADR2 (0x03|0x60|SPRD_MASK) +#define MAADR5 (0x04|0x60|SPRD_MASK) +#define MAADR4 (0x05|0x60|SPRD_MASK) +#define EBSTSD (0x06|0x60) +#define EBSTCON (0x07|0x60) +#define EBSTCSL (0x08|0x60) +#define EBSTCSH (0x09|0x60) +#define MISTAT (0x0A|0x60|SPRD_MASK) +#define EREVID (0x12|0x60) +#define ECOCON (0x15|0x60) +#define EFLOCON (0x17|0x60) +#define EPAUSL (0x18|0x60) +#define EPAUSH (0x19|0x60) +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHHID1 0x02 +#define PHHID2 0x03 +#define PHCON2 0x10 +#define PHSTAT2 0x11 +#define PHIE 0x12 +#define PHIR 0x13 +#define PHLCON 0x14 + +/* ENC28J60 EIE Register Bit Definitions */ +#define EIE_INTIE 0x80 +#define EIE_PKTIE 0x40 +#define EIE_DMAIE 0x20 +#define EIE_LINKIE 0x10 +#define EIE_TXIE 0x08 +/* #define EIE_WOLIE 0x04 (reserved) */ +#define EIE_TXERIE 0x02 +#define EIE_RXERIE 0x01 +/* ENC28J60 EIR Register Bit Definitions */ +#define EIR_PKTIF 0x40 +#define EIR_DMAIF 0x20 +#define EIR_LINKIF 0x10 +#define EIR_TXIF 0x08 +/* #define EIR_WOLIF 0x04 (reserved) */ +#define EIR_TXERIF 0x02 +#define EIR_RXERIF 0x01 +/* ENC28J60 ESTAT Register Bit Definitions */ +#define ESTAT_INT 0x80 +#define ESTAT_LATECOL 0x10 +#define ESTAT_RXBUSY 0x04 +#define ESTAT_TXABRT 0x02 +#define ESTAT_CLKRDY 0x01 +/* ENC28J60 ECON2 Register Bit Definitions */ +#define ECON2_AUTOINC 0x80 +#define ECON2_PKTDEC 0x40 +#define ECON2_PWRSV 0x20 +#define ECON2_VRPS 0x08 +/* ENC28J60 ECON1 Register Bit Definitions */ +#define ECON1_TXRST 0x80 +#define ECON1_RXRST 0x40 +#define ECON1_DMAST 0x20 +#define ECON1_CSUMEN 0x10 +#define ECON1_TXRTS 0x08 +#define ECON1_RXEN 0x04 +#define ECON1_BSEL1 0x02 +#define ECON1_BSEL0 0x01 +/* ENC28J60 MACON1 Register Bit Definitions */ +#define MACON1_LOOPBK 0x10 +#define MACON1_TXPAUS 0x08 +#define MACON1_RXPAUS 0x04 +#define MACON1_PASSALL 0x02 +#define MACON1_MARXEN 0x01 +/* ENC28J60 MACON2 Register Bit Definitions */ +#define MACON2_MARST 0x80 +#define MACON2_RNDRST 0x40 +#define MACON2_MARXRST 0x08 +#define MACON2_RFUNRST 0x04 +#define MACON2_MATXRST 0x02 +#define MACON2_TFUNRST 0x01 +/* ENC28J60 MACON3 Register Bit Definitions */ +#define MACON3_PADCFG2 0x80 +#define MACON3_PADCFG1 0x40 +#define MACON3_PADCFG0 0x20 +#define MACON3_TXCRCEN 0x10 +#define MACON3_PHDRLEN 0x08 +#define MACON3_HFRMLEN 0x04 +#define MACON3_FRMLNEN 0x02 +#define MACON3_FULDPX 0x01 +/* ENC28J60 MICMD Register Bit Definitions */ +#define MICMD_MIISCAN 0x02 +#define MICMD_MIIRD 0x01 +/* ENC28J60 MISTAT Register Bit Definitions */ +#define MISTAT_NVALID 0x04 +#define MISTAT_SCAN 0x02 +#define MISTAT_BUSY 0x01 +/* ENC28J60 ERXFCON Register Bit Definitions */ +#define ERXFCON_UCEN 0x80 +#define ERXFCON_ANDOR 0x40 +#define ERXFCON_CRCEN 0x20 +#define ERXFCON_PMEN 0x10 +#define ERXFCON_MPEN 0x08 +#define ERXFCON_HTEN 0x04 +#define ERXFCON_MCEN 0x02 +#define ERXFCON_BCEN 0x01 + +/* ENC28J60 PHY PHCON1 Register Bit Definitions */ +#define PHCON1_PRST 0x8000 +#define PHCON1_PLOOPBK 0x4000 +#define PHCON1_PPWRSV 0x0800 +#define PHCON1_PDPXMD 0x0100 +/* ENC28J60 PHY PHSTAT1 Register Bit Definitions */ +#define PHSTAT1_PFDPX 0x1000 +#define PHSTAT1_PHDPX 0x0800 +#define PHSTAT1_LLSTAT 0x0004 +#define PHSTAT1_JBSTAT 0x0002 +/* ENC28J60 PHY PHSTAT2 Register Bit Definitions */ +#define PHSTAT2_TXSTAT (1 << 13) +#define PHSTAT2_RXSTAT (1 << 12) +#define PHSTAT2_COLSTAT (1 << 11) +#define PHSTAT2_LSTAT (1 << 10) +#define PHSTAT2_DPXSTAT (1 << 9) +#define PHSTAT2_PLRITY (1 << 5) +/* ENC28J60 PHY PHCON2 Register Bit Definitions */ +#define PHCON2_FRCLINK 0x4000 +#define PHCON2_TXDIS 0x2000 +#define PHCON2_JABBER 0x0400 +#define PHCON2_HDLDIS 0x0100 +/* ENC28J60 PHY PHIE Register Bit Definitions */ +#define PHIE_PLNKIE (1 << 4) +#define PHIE_PGEIE (1 << 1) +/* ENC28J60 PHY PHIR Register Bit Definitions */ +#define PHIR_PLNKIF (1 << 4) +#define PHIR_PGEIF (1 << 1) + +/* ENC28J60 Packet Control Byte Bit Definitions */ +#define PKTCTRL_PHUGEEN 0x08 +#define PKTCTRL_PPADEN 0x04 +#define PKTCTRL_PCRCEN 0x02 +#define PKTCTRL_POVERRIDE 0x01 + +/* ENC28J60 Transmit Status Vector */ +#define TSV_TXBYTECNT 0 +#define TSV_TXCOLLISIONCNT 16 +#define TSV_TXCRCERROR 20 +#define TSV_TXLENCHKERROR 21 +#define TSV_TXLENOUTOFRANGE 22 +#define TSV_TXDONE 23 +#define TSV_TXMULTICAST 24 +#define TSV_TXBROADCAST 25 +#define TSV_TXPACKETDEFER 26 +#define TSV_TXEXDEFER 27 +#define TSV_TXEXCOLLISION 28 +#define TSV_TXLATECOLLISION 29 +#define TSV_TXGIANT 30 +#define TSV_TXUNDERRUN 31 +#define TSV_TOTBYTETXONWIRE 32 +#define TSV_TXCONTROLFRAME 48 +#define TSV_TXPAUSEFRAME 49 +#define TSV_BACKPRESSUREAPP 50 +#define TSV_TXVLANTAGFRAME 51 + +#define TSV_SIZE 7 +#define TSV_BYTEOF(x) ((x) / 8) +#define TSV_BITMASK(x) (1 << ((x) % 8)) +#define TSV_GETBIT(x, y) (((x)[TSV_BYTEOF(y)] & TSV_BITMASK(y)) ? 1 : 0) + +/* ENC28J60 Receive Status Vector */ +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_SIZE 6 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + + +/* SPI operation codes */ +#define ENC28J60_READ_CTRL_REG 0x00 +#define ENC28J60_READ_BUF_MEM 0x3A +#define ENC28J60_WRITE_CTRL_REG 0x40 +#define ENC28J60_WRITE_BUF_MEM 0x7A +#define ENC28J60_BIT_FIELD_SET 0x80 +#define ENC28J60_BIT_FIELD_CLR 0xA0 +#define ENC28J60_SOFT_RESET 0xFF + + +/* buffer boundaries applied to internal 8K ram + * entire available packet buffer space is allocated. + * Give TX buffer space for one full ethernet frame (~1500 bytes) + * receive buffer gets the rest */ +#define TXSTART_INIT 0x1A00 +#define TXEND_INIT 0x1FFF + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ +#define RXSTART_INIT 0x0000 +#define RXEND_INIT 0x19FF + +/* maximum ethernet frame length */ +#define MAX_FRAMELEN 1518 + +/* Preferred half duplex: LEDA: Link status LEDB: Rx/Tx activity */ +#define ENC28J60_LAMPS_MODE 0x3476 + +#endif diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c new file mode 100644 index 000000000..46181559d --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600-regmap.c @@ -0,0 +1,524 @@ +/** + * Register map access API - ENCX24J600 support + * + * Copyright 2015 Gridpoint + * + * Author: Jon Ringle <jringle@gridpoint.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "encx24j600_hw.h" + +static inline bool is_bits_set(int value, int mask) +{ + return (value & mask) == mask; +} + +static int encx24j600_switch_bank(struct encx24j600_context *ctx, + int bank) +{ + int ret = 0; + int bank_opcode = BANK_SELECT(bank); + + ret = spi_write(ctx->spi, &bank_opcode, 1); + if (ret == 0) + ctx->bank = bank; + + return ret; +} + +static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode, + const void *buf, size_t len) +{ + struct spi_message m; + struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, }, + { .tx_buf = buf, .len = len }, }; + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + return spi_sync(ctx->spi, &m); +} + +static void regmap_lock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + + mutex_lock(&ctx->mutex); +} + +static void regmap_unlock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + + mutex_unlock(&ctx->mutex); +} + +static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = RCRU; + int ret = 0; + int i = 0; + u8 tx_buf[2]; + + if (reg < 0x80) { + cmd = RCRCODE | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = RGPRDPT; break; + case EGPWRPT: + cmd = RGPWRPT; break; + case ERXRDPT: + cmd = RRXRDPT; break; + case ERXWRPT: + cmd = RRXWRPT; break; + case EUDARDPT: + cmd = RUDARDPT; break; + case EUDAWRPT: + cmd = RUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + tx_buf[i++] = cmd; + if (cmd == RCRU) + tx_buf[i++] = reg; + + ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len); + + return ret; +} + +static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx, + u8 reg, u8 *val, size_t len, + u8 unbanked_cmd, u8 banked_code) +{ + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = unbanked_cmd; + struct spi_message m; + struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), }, + { .tx_buf = ®, .len = sizeof(reg), }, + { .tx_buf = val, .len = len }, }; + + if (reg < 0x80) { + int ret = 0; + + cmd = banked_code | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = WGPRDPT; break; + case EGPWRPT: + cmd = WGPWRPT; break; + case ERXRDPT: + cmd = WRXRDPT; break; + case ERXWRPT: + cmd = WRXWRPT; break; + case EUDARDPT: + cmd = WUDARDPT; break; + case EUDAWRPT: + cmd = WUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + + if (cmd == unbanked_cmd) { + t[1].tx_buf = ® + spi_message_add_tail(&t[1], &m); + } + + spi_message_add_tail(&t[2], &m); + return spi_sync(ctx->spi, &m); +} + +static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + + return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE); +} + +static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE); +} + +static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE); +} + +static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg, + unsigned int mask, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + + int ret = 0; + unsigned int set_mask = mask & val; + unsigned int clr_mask = mask & ~val; + + if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80) + return -EINVAL; + + if (set_mask & 0xff) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask); + + set_mask = (set_mask & 0xff00) >> 8; + + if ((set_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask); + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask); + + clr_mask = (clr_mask & 0xff00) >> 8; + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask); + + return ret; +} + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg < 0xc0) + return encx24j600_cmdn(ctx, reg, data, count); + + /* SPI 1-byte command. Ignore data */ + return spi_write(ctx->spi, ®, 1); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write); + +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg == RBSEL && count > 1) + count = 1; + + return spi_write_then_read(ctx->spi, ®, sizeof(reg), data, count); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read); + +static int regmap_encx24j600_write(void *context, const void *data, + size_t len) +{ + u8 *dout = (u8 *)data; + u8 reg = dout[0]; + ++dout; + --len; + + if (reg > 0xa0) + return regmap_encx24j600_spi_write(context, reg, dout, len); + + if (len > 2) + return -EINVAL; + + return regmap_encx24j600_sfr_write(context, reg, dout, len); +} + +static int regmap_encx24j600_read(void *context, + const void *reg_buf, size_t reg_size, + void *val, size_t val_size) +{ + u8 reg = *(const u8 *)reg_buf; + + if (reg_size != 1) { + pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size); + return -EINVAL; + } + + if (reg > 0xa0) + return regmap_encx24j600_spi_read(context, reg, val, val_size); + + if (val_size > 2) { + pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size); + return -EINVAL; + } + + return regmap_encx24j600_sfr_read(context, reg, val, val_size); +} + +static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x36) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x66)) || + ((reg >= 0x68) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + (reg == 0xc8)) + return true; + else + return false; +} + +static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x12) || + ((reg >= 0x14) && (reg < 0x1a)) || + ((reg >= 0x1c) && (reg < 0x36)) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x68)) || + ((reg >= 0x6c) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + ((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ERXHEAD: + case EDMACS: + case ETXSTAT: + case ETXWIRE: + case ECON1: /* Can be modified via single byte cmds */ + case ECON2: /* Can be modified via single byte cmds */ + case ESTAT: + case EIR: /* Can be modified via single byte cmds */ + case MIRD: + case MISTAT: + return true; + default: + break; + } + + return false; +} + +static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg) +{ + /* single byte cmds are precious */ + if (((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, MIIRD); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, 0); + if (unlikely(ret)) + goto err_out; + + ret = regmap_read(ctx->regmap, MIRD, val); + +err_out: + if (ret) + pr_err("%s: error %d reading reg %02x\n", __func__, ret, + reg & PHREG_MASK); + + return ret; +} + +static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MIWR, val); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + +err_out: + if (ret) + pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret, + reg & PHREG_MASK, val); + + return ret; +} + +static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHSTAT1: + case PHANA: + case PHANLPA: + case PHANE: + case PHCON2: + case PHSTAT2: + case PHSTAT3: + return true; + default: + return false; + } +} + +static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHCON2: + case PHANA: + return true; + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + default: + return false; + } +} + +static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + case PHCON2: + return true; + default: + return false; + } +} + +static struct regmap_config regcfg = { + .name = "reg", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xee, + .reg_stride = 2, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_regmap_readable, + .writeable_reg = encx24j600_regmap_writeable, + .volatile_reg = encx24j600_regmap_volatile, + .precious_reg = encx24j600_regmap_precious, + .lock = regmap_lock_mutex, + .unlock = regmap_unlock_mutex, +}; + +static struct regmap_bus regmap_encx24j600 = { + .write = regmap_encx24j600_write, + .read = regmap_encx24j600_read, + .reg_update_bits = regmap_encx24j600_reg_update_bits, +}; + +static struct regmap_config phycfg = { + .name = "phy", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x1f, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_phymap_readable, + .writeable_reg = encx24j600_phymap_writeable, + .volatile_reg = encx24j600_phymap_volatile, +}; + +static struct regmap_bus phymap_encx24j600 = { + .reg_write = regmap_encx24j600_phy_reg_write, + .reg_read = regmap_encx24j600_phy_reg_read, +}; + +int devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx) +{ + mutex_init(&ctx->mutex); + regcfg.lock_arg = ctx; + ctx->regmap = devm_regmap_init(dev, ®map_encx24j600, ctx, ®cfg); + if (IS_ERR(ctx->regmap)) + return PTR_ERR(ctx->regmap); + ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg); + if (IS_ERR(ctx->phymap)) + return PTR_ERR(ctx->phymap); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c new file mode 100644 index 000000000..ff45326eb --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -0,0 +1,1152 @@ +/** + * Microchip ENCX24J600 ethernet driver + * + * Copyright (C) 2015 Gridpoint + * Author: Jon Ringle <jringle@gridpoint.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/skbuff.h> +#include <linux/spi/spi.h> + +#include "encx24j600_hw.h" + +#define DRV_NAME "encx24j600" +#define DRV_VERSION "1.0" + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0000); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/* SRAM memory layout: + * + * 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM + * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM + */ +#define ENC_TX_BUF_START 0x0000U +#define ENC_RX_BUF_START 0x0600U +#define ENC_RX_BUF_END 0x5fffU +#define ENC_SRAM_SIZE 0x6000U + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; + +struct encx24j600_priv { + struct net_device *ndev; + struct mutex lock; /* device access lock */ + struct encx24j600_context ctx; + struct sk_buff *tx_skb; + struct task_struct *kworker_task; + struct kthread_worker kworker; + struct kthread_work tx_work; + struct kthread_work setrx_work; + u16 next_packet; + bool hw_enabled; + bool full_duplex; + bool autoneg; + u16 speed; + int rxfilter; + u32 msg_enable; +}; + +static void dump_packet(const char *msg, int len, const char *data) +{ + pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len); + print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len); +} + +static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + + netdev_info(dev, "RX packet Len:%d\n", rsv->len); + netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg, + rsv->next_packet); + netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXOK), + RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE)); + netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_CRCERROR), + RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR), + RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE)); + netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST), + RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST), + RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV), + RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV)); + netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN)); +} + +static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.regmap, reg, &val); + + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, reg, val); + + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg, + u16 mask, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val); + + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n", + __func__, ret, reg, val, mask); +} + +static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.phymap, reg, &val); + + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.phymap, reg, val); + + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, 0); +} + +static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, mask); +} + +static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, cmd, 0); + + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n", + __func__, ret, cmd); +} + +static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data, + size_t count) +{ + int ret; + + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg, + const u8 *data, size_t count) +{ + int ret; + + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static void encx24j600_update_phcon1(struct encx24j600_priv *priv) +{ + u16 phcon1 = encx24j600_read_phy(priv, PHCON1); + + if (priv->autoneg == AUTONEG_ENABLE) { + phcon1 |= ANEN | RENEG; + } else { + phcon1 &= ~ANEN; + if (priv->speed == SPEED_100) + phcon1 |= SPD100; + else + phcon1 &= ~SPD100; + + if (priv->full_duplex) + phcon1 |= PFULDPX; + else + phcon1 &= ~PFULDPX; + } + encx24j600_write_phy(priv, PHCON1, phcon1); +} + +/* Waits for autonegotiation to complete. */ +static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + u16 phstat1; + u16 estat; + int ret = 0; + + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + while ((phstat1 & ANDONE) == 0) { + if (time_after(jiffies, timeout)) { + u16 phstat3; + + netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n"); + + priv->autoneg = AUTONEG_DISABLE; + phstat3 = encx24j600_read_phy(priv, PHSTAT3); + priv->speed = (phstat3 & PHY3SPD100) + ? SPEED_100 : SPEED_10; + priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0; + encx24j600_update_phcon1(priv); + netif_notice(priv, drv, dev, "Using parallel detection: %s/%s", + priv->speed == SPEED_100 ? "100" : "10", + priv->full_duplex ? "Full" : "Half"); + + return -ETIMEDOUT; + } + cpu_relax(); + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + } + + estat = encx24j600_read_reg(priv, ESTAT); + if (estat & PHYDPX) { + encx24j600_set_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x15); + } else { + encx24j600_clr_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x12); + /* Max retransmittions attempt */ + encx24j600_write_reg(priv, MACLCON, 0x370f); + } + + return ret; +} + +/* Access the PHY to determine link status */ +static void encx24j600_check_link_status(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + u16 estat; + + estat = encx24j600_read_reg(priv, ESTAT); + + if (estat & PHYLNK) { + if (priv->autoneg == AUTONEG_ENABLE) + encx24j600_wait_for_autoneg(priv); + + netif_carrier_on(dev); + netif_info(priv, ifup, dev, "link up\n"); + } else { + netif_info(priv, ifdown, dev, "link down\n"); + + /* Re-enable autoneg since we won't know what we might be + * connected to when the link is brought back up again. + */ + priv->autoneg = AUTONEG_ENABLE; + priv->full_duplex = true; + priv->speed = SPEED_100; + netif_carrier_off(dev); + } +} + +static void encx24j600_int_link_handler(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + + netif_dbg(priv, intr, dev, "%s", __func__); + encx24j600_check_link_status(priv); + encx24j600_clr_bits(priv, EIR, LINKIF); +} + +static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err) +{ + struct net_device *dev = priv->ndev; + + if (!priv->tx_skb) { + BUG(); + return; + } + + mutex_lock(&priv->lock); + + if (err) + dev->stats.tx_errors++; + else + dev->stats.tx_packets++; + + dev->stats.tx_bytes += priv->tx_skb->len; + + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : ""); + + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + + netif_wake_queue(dev); + + mutex_unlock(&priv->lock); +} + +static int encx24j600_receive_packet(struct encx24j600_priv *priv, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN); + + if (!skb) { + pr_err_ratelimited("RX: OOM: packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len); + + if (netif_msg_pktdata(priv)) + dump_packet("RX", skb->len, skb->data); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_COMPLETE; + + /* Maintain stats */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += rsv->len; + + netif_rx(skb); + + return 0; +} + +static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count) +{ + struct net_device *dev = priv->ndev; + + while (packet_count--) { + struct rsv rsv; + u16 newrxtail; + + encx24j600_write_reg(priv, ERXRDPT, priv->next_packet); + encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv)); + + if (netif_msg_rx_status(priv)) + encx24j600_dump_rsv(priv, __func__, &rsv); + + if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) || + (rsv.len > MAX_FRAMELEN)) { + netif_err(priv, rx_err, dev, "RX Error %04x\n", + rsv.rxstat); + dev->stats.rx_errors++; + + if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR)) + dev->stats.rx_crc_errors++; + if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR)) + dev->stats.rx_frame_errors++; + if (rsv.len > MAX_FRAMELEN) + dev->stats.rx_over_errors++; + } else { + encx24j600_receive_packet(priv, &rsv); + } + + priv->next_packet = rsv.next_packet; + + newrxtail = priv->next_packet - 2; + if (newrxtail == ENC_RX_BUF_START) + newrxtail = SRAM_SIZE - 2; + + encx24j600_cmd(priv, SETPKTDEC); + encx24j600_write_reg(priv, ERXTAIL, newrxtail); + } +} + +static irqreturn_t encx24j600_isr(int irq, void *dev_id) +{ + struct encx24j600_priv *priv = dev_id; + struct net_device *dev = priv->ndev; + int eir; + + /* Clear interrupts */ + encx24j600_cmd(priv, CLREIE); + + eir = encx24j600_read_reg(priv, EIR); + + if (eir & LINKIF) + encx24j600_int_link_handler(priv); + + if (eir & TXIF) + encx24j600_tx_complete(priv, false); + + if (eir & TXABTIF) + encx24j600_tx_complete(priv, true); + + if (eir & RXABTIF) { + if (eir & PCFULIF) { + /* Packet counter is full */ + netif_err(priv, rx_err, dev, "Packet counter full\n"); + } + dev->stats.rx_dropped++; + encx24j600_clr_bits(priv, EIR, RXABTIF); + } + + if (eir & PKTIF) { + u8 packet_count; + + mutex_lock(&priv->lock); + + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + while (packet_count) { + encx24j600_rx_packets(priv, packet_count); + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + } + + mutex_unlock(&priv->lock); + } + + /* Enable interrupts */ + encx24j600_cmd(priv, SETEIE); + + return IRQ_HANDLED; +} + +static int encx24j600_soft_reset(struct encx24j600_priv *priv) +{ + int ret = 0; + int timeout; + u16 eudast; + + /* Write and verify a test value to EUDAST */ + regcache_cache_bypass(priv->ctx.regmap, true); + timeout = 10; + do { + encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL); + eudast = encx24j600_read_reg(priv, EUDAST); + usleep_range(25, 100); + } while ((eudast != EUDAST_TEST_VAL) && --timeout); + regcache_cache_bypass(priv->ctx.regmap, false); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Wait for CLKRDY to become set */ + timeout = 10; + while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout) + usleep_range(25, 100); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Issue a System Reset command */ + encx24j600_cmd(priv, SETETHRST); + usleep_range(25, 100); + + /* Confirm that EUDAST has 0000h after system reset */ + if (encx24j600_read_reg(priv, EUDAST) != 0) { + ret = -EINVAL; + goto err_out; + } + + /* Wait for PHY register and status bits to become available */ + usleep_range(256, 1000); + +err_out: + return ret; +} + +static int encx24j600_hw_reset(struct encx24j600_priv *priv) +{ + int ret; + + mutex_lock(&priv->lock); + ret = encx24j600_soft_reset(priv); + mutex_unlock(&priv->lock); + + return ret; +} + +static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv) +{ + encx24j600_set_bits(priv, ECON2, TXRST); + encx24j600_clr_bits(priv, ECON2, TXRST); +} + +static void encx24j600_hw_init_tx(struct encx24j600_priv *priv) +{ + /* Reset TX */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + /* Write the Tx Buffer pointer */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); +} + +static void encx24j600_hw_init_rx(struct encx24j600_priv *priv) +{ + encx24j600_cmd(priv, DISABLERX); + + /* Set up RX packet start address in the SRAM */ + encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START); + + /* Preload the RX Data pointer to the beginning of the RX area */ + encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START); + + priv->next_packet = ENC_RX_BUF_START; + + /* Set up RX end address in the SRAM */ + encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2); + + /* Reset the user data pointers */ + encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE); + encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1); + + /* Set Max Frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); +} + +static void encx24j600_dump_config(struct encx24j600_priv *priv, + const char *msg) +{ + pr_info(DRV_NAME ": %s\n", msg); + + /* CHIP configuration */ + pr_info(DRV_NAME " ECON1: %04X\n", encx24j600_read_reg(priv, ECON1)); + pr_info(DRV_NAME " ECON2: %04X\n", encx24j600_read_reg(priv, ECON2)); + pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv, + ERXFCON)); + pr_info(DRV_NAME " ESTAT: %04X\n", encx24j600_read_reg(priv, ESTAT)); + pr_info(DRV_NAME " EIR: %04X\n", encx24j600_read_reg(priv, EIR)); + pr_info(DRV_NAME " EIDLED: %04X\n", encx24j600_read_reg(priv, EIDLED)); + + /* MAC layer configuration */ + pr_info(DRV_NAME " MACON1: %04X\n", encx24j600_read_reg(priv, MACON1)); + pr_info(DRV_NAME " MACON2: %04X\n", encx24j600_read_reg(priv, MACON2)); + pr_info(DRV_NAME " MAIPG: %04X\n", encx24j600_read_reg(priv, MAIPG)); + pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv, + MACLCON)); + pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv, + MABBIPG)); + + /* PHY configuation */ + pr_info(DRV_NAME " PHCON1: %04X\n", encx24j600_read_phy(priv, PHCON1)); + pr_info(DRV_NAME " PHCON2: %04X\n", encx24j600_read_phy(priv, PHCON2)); + pr_info(DRV_NAME " PHANA: %04X\n", encx24j600_read_phy(priv, PHANA)); + pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv, + PHANLPA)); + pr_info(DRV_NAME " PHANE: %04X\n", encx24j600_read_phy(priv, PHANE)); + pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv, + PHSTAT1)); + pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv, + PHSTAT2)); + pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv, + PHSTAT3)); +} + +static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) +{ + switch (priv->rxfilter) { + case RXFILTER_PROMISC: + encx24j600_set_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN); + break; + case RXFILTER_MULTI: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN); + break; + case RXFILTER_NORMAL: + default: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN); + break; + } +} + +static int encx24j600_hw_init(struct encx24j600_priv *priv) +{ + int ret = 0; + u16 macon2; + + priv->hw_enabled = false; + + /* PHY Leds: link status, + * LEDA: Link State + collision events + * LEDB: Link State + transmit/receive events + */ + encx24j600_update_reg(priv, EIDLED, 0xff00, 0xcb00); + + /* Loopback disabled */ + encx24j600_write_reg(priv, MACON1, 0x9); + + /* interpacket gap value */ + encx24j600_write_reg(priv, MAIPG, 0x0c12); + + /* Write the auto negotiation pattern */ + encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT); + + encx24j600_update_phcon1(priv); + encx24j600_check_link_status(priv); + + macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER; + if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex) + macon2 |= FULDPX; + + encx24j600_set_bits(priv, MACON2, macon2); + + priv->rxfilter = RXFILTER_NORMAL; + encx24j600_set_rxfilter_mode(priv); + + /* Program the Maximum frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); + + /* Init Tx pointers */ + encx24j600_hw_init_tx(priv); + + /* Init Rx pointers */ + encx24j600_hw_init_rx(priv); + + if (netif_msg_hw(priv)) + encx24j600_dump_config(priv, "Hw is initialized"); + + return ret; +} + +static void encx24j600_hw_enable(struct encx24j600_priv *priv) +{ + /* Clear the interrupt flags in case was set */ + encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF | + PKTIF | LINKIF)); + + /* Enable the interrupts */ + encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE | + PKTIE | LINKIE | INTIE)); + + /* Enable RX */ + encx24j600_cmd(priv, ENABLERX); + + priv->hw_enabled = true; +} + +static void encx24j600_hw_disable(struct encx24j600_priv *priv) +{ + /* Disable all interrupts */ + encx24j600_write_reg(priv, EIE, 0); + + /* Disable RX */ + encx24j600_cmd(priv, DISABLERX); + + priv->hw_enabled = false; +} + +static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed, + u8 duplex) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->hw_enabled) { + /* link is in low power mode now; duplex setting + * will take effect on next encx24j600_hw_init() + */ + if (speed == SPEED_10 || speed == SPEED_100) { + priv->autoneg = (autoneg == AUTONEG_ENABLE); + priv->full_duplex = (duplex == DUPLEX_FULL); + priv->speed = (speed == SPEED_100); + } else { + netif_warn(priv, link, dev, "unsupported link speed setting\n"); + /*speeds other than SPEED_10 and SPEED_100 */ + /*are not supported by chip */ + ret = -EOPNOTSUPP; + } + } else { + netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv, + unsigned char *ethaddr) +{ + unsigned short val; + + val = encx24j600_read_reg(priv, MAADR1); + + ethaddr[0] = val & 0x00ff; + ethaddr[1] = (val & 0xff00) >> 8; + + val = encx24j600_read_reg(priv, MAADR2); + + ethaddr[2] = val & 0x00ffU; + ethaddr[3] = (val & 0xff00U) >> 8; + + val = encx24j600_read_reg(priv, MAADR3); + + ethaddr[4] = val & 0x00ffU; + ethaddr[5] = (val & 0xff00U) >> 8; +} + +/* Program the hardware MAC address from dev->dev_addr.*/ +static int encx24j600_set_hw_macaddr(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + if (priv->hw_enabled) { + netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n"); + return -EBUSY; + } + + mutex_lock(&priv->lock); + + netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n", + dev->name, dev->dev_addr); + + encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] | + dev->dev_addr[5] << 8)); + encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] | + dev->dev_addr[3] << 8)); + encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] | + dev->dev_addr[1] << 8)); + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ +static int encx24j600_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return encx24j600_set_hw_macaddr(dev); +} + +static int encx24j600_open(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRV_NAME, priv); + if (unlikely(ret < 0)) { + netdev_err(dev, "request irq %d failed (ret = %d)\n", + priv->ctx.spi->irq, ret); + return ret; + } + + encx24j600_hw_disable(priv); + encx24j600_hw_init(priv); + encx24j600_hw_enable(priv); + netif_start_queue(dev); + + return 0; +} + +static int encx24j600_stop(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + free_irq(priv->ctx.spi->irq, priv); + return 0; +} + +static void encx24j600_setrx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, setrx_work); + + mutex_lock(&priv->lock); + encx24j600_set_rxfilter_mode(priv); + mutex_unlock(&priv->lock); +} + +static void encx24j600_set_multicast_list(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + netif_dbg(priv, link, dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + netif_dbg(priv, link, dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + netif_dbg(priv, link, dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + kthread_queue_work(&priv->kworker, &priv->setrx_work); +} + +static void encx24j600_hw_tx(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + + netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n", + priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data); + + if (encx24j600_read_reg(priv, EIR) & TXABTIF) + /* Last transmition aborted due to error. Reset TX interface */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF); + + /* Set the data pointer to the TX buffer address in the SRAM */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); + + /* Copy the packet into the SRAM */ + encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data, + priv->tx_skb->len); + + /* Program the Tx buffer start pointer */ + encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START); + + /* Program the packet length */ + encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len); + + /* Start the transmission */ + encx24j600_cmd(priv, SETTXRTS); +} + +static void encx24j600_tx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, tx_work); + + mutex_lock(&priv->lock); + encx24j600_hw_tx(priv); + mutex_unlock(&priv->lock); +} + +static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + /* save the timestamp */ + netif_trans_update(dev); + + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + + kthread_queue_work(&priv->kworker, &priv->tx_work); + + return NETDEV_TX_OK; +} + +/* Deal with a transmit timeout */ +static void encx24j600_tx_timeout(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", + jiffies, jiffies - dev_trans_start(dev)); + + dev->stats.tx_errors++; + netif_wake_queue(dev); +} + +static int encx24j600_get_regs_len(struct net_device *dev) +{ + return SFR_REG_COUNT; +} + +static void encx24j600_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *p) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + u16 *buff = p; + u8 reg; + + regs->version = 1; + mutex_lock(&priv->lock); + for (reg = 0; reg < SFR_REG_COUNT; reg += 2) { + unsigned int val = 0; + /* ignore errors for unreadable registers */ + regmap_read(priv->ctx.regmap, reg, &val); + buff[reg] = val & 0xffff; + } + mutex_unlock(&priv->lock); +} + +static void encx24j600_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); +} + +static int encx24j600_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + u32 supported; + + supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + + cmd->base.speed = priv->speed; + cmd->base.duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->base.port = PORT_TP; + cmd->base.autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int +encx24j600_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + return encx24j600_setlink(dev, cmd->base.autoneg, + cmd->base.speed, cmd->base.duplex); +} + +static u32 encx24j600_get_msglevel(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + return priv->msg_enable; +} + +static void encx24j600_set_msglevel(struct net_device *dev, u32 val) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + priv->msg_enable = val; +} + +static const struct ethtool_ops encx24j600_ethtool_ops = { + .get_drvinfo = encx24j600_get_drvinfo, + .get_msglevel = encx24j600_get_msglevel, + .set_msglevel = encx24j600_set_msglevel, + .get_regs_len = encx24j600_get_regs_len, + .get_regs = encx24j600_get_regs, + .get_link_ksettings = encx24j600_get_link_ksettings, + .set_link_ksettings = encx24j600_set_link_ksettings, +}; + +static const struct net_device_ops encx24j600_netdev_ops = { + .ndo_open = encx24j600_open, + .ndo_stop = encx24j600_stop, + .ndo_start_xmit = encx24j600_tx, + .ndo_set_rx_mode = encx24j600_set_multicast_list, + .ndo_set_mac_address = encx24j600_set_mac_address, + .ndo_tx_timeout = encx24j600_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int encx24j600_spi_probe(struct spi_device *spi) +{ + int ret; + + struct net_device *ndev; + struct encx24j600_priv *priv; + u16 eidled; + + ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); + + if (!ndev) { + ret = -ENOMEM; + goto error_out; + } + + priv = netdev_priv(ndev); + spi_set_drvdata(spi, priv); + dev_set_drvdata(&spi->dev, priv); + SET_NETDEV_DEV(ndev, &spi->dev); + + priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + priv->ndev = ndev; + + /* Default configuration PHY configuration */ + priv->full_duplex = true; + priv->autoneg = AUTONEG_ENABLE; + priv->speed = SPEED_100; + + priv->ctx.spi = spi; + ndev->irq = spi->irq; + ndev->netdev_ops = &encx24j600_netdev_ops; + + ret = devm_regmap_init_encx24j600(&spi->dev, &priv->ctx); + if (ret) + goto out_free; + + mutex_init(&priv->lock); + + /* Reset device and check if it is connected */ + if (encx24j600_hw_reset(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": Chip is not detected\n"); + ret = -EIO; + goto out_free; + } + + /* Initialize the device HW to the consistent state */ + if (encx24j600_hw_init(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": HW initialization error\n"); + ret = -EIO; + goto out_free; + } + + kthread_init_worker(&priv->kworker); + kthread_init_work(&priv->tx_work, encx24j600_tx_proc); + kthread_init_work(&priv->setrx_work, encx24j600_setrx_proc); + + priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker, + "encx24j600"); + + if (IS_ERR(priv->kworker_task)) { + ret = PTR_ERR(priv->kworker_task); + goto out_free; + } + + /* Get the MAC address from the chip */ + encx24j600_hw_get_macaddr(priv, ndev->dev_addr); + + ndev->ethtool_ops = &encx24j600_ethtool_ops; + + ret = register_netdev(ndev); + if (unlikely(ret)) { + netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n", + ret); + goto out_stop; + } + + eidled = encx24j600_read_reg(priv, EIDLED); + if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { + ret = -EINVAL; + goto out_unregister; + } + + netif_info(priv, probe, ndev, "Silicon rev ID: 0x%02x\n", + (eidled & REVID_MASK) >> REVID_SHIFT); + + netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); + + return ret; + +out_unregister: + unregister_netdev(priv->ndev); +out_stop: + kthread_stop(priv->kworker_task); +out_free: + free_netdev(ndev); + +error_out: + return ret; +} + +static int encx24j600_spi_remove(struct spi_device *spi) +{ + struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev); + + unregister_netdev(priv->ndev); + kthread_stop(priv->kworker_task); + + free_netdev(priv->ndev); + + return 0; +} + +static const struct spi_device_id encx24j600_spi_id_table[] = { + { .name = "encx24j600" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, encx24j600_spi_id_table); + +static struct spi_driver encx24j600_spi_net_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .bus = &spi_bus_type, + }, + .probe = encx24j600_spi_probe, + .remove = encx24j600_spi_remove, + .id_table = encx24j600_spi_id_table, +}; + +static int __init encx24j600_init(void) +{ + return spi_register_driver(&encx24j600_spi_net_driver); +} +module_init(encx24j600_init); + +static void encx24j600_exit(void) +{ + spi_unregister_driver(&encx24j600_spi_net_driver); +} +module_exit(encx24j600_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h new file mode 100644 index 000000000..711147a15 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600_hw.h @@ -0,0 +1,438 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * encx24j600_hw.h: Register definitions + * + */ + +#ifndef _ENCX24J600_HW_H +#define _ENCX24J600_HW_H + +struct encx24j600_context { + struct spi_device *spi; + struct regmap *regmap; + struct regmap *phymap; + struct mutex mutex; /* mutex to protect access to regmap */ + int bank; +}; + +int devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx); + +/* Single-byte instructions */ +#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1)) +#define B0SEL 0xC0 /* Bank 0 Select */ +#define B1SEL 0xC2 /* Bank 1 Select */ +#define B2SEL 0xC4 /* Bank 2 Select */ +#define B3SEL 0xC6 /* Bank 3 Select */ +#define SETETHRST 0xCA /* System Reset */ +#define FCDISABLE 0xE0 /* Flow Control Disable */ +#define FCSINGLE 0xE2 /* Flow Control Single */ +#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ +#define FCCLEAR 0xE6 /* Flow Control Clear */ +#define SETPKTDEC 0xCC /* Decrement Packet Counter */ +#define DMASTOP 0xD2 /* DMA Stop */ +#define DMACKSUM 0xD8 /* DMA Start Checksum */ +#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ +#define DMACOPY 0xDC /* DMA Start Copy */ +#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ +#define SETTXRTS 0xD4 /* Request Packet Transmission */ +#define ENABLERX 0xE8 /* Enable RX */ +#define DISABLERX 0xEA /* Disable RX */ +#define SETEIE 0xEC /* Enable Interrupts */ +#define CLREIE 0xEE /* Disable Interrupts */ + +/* Two byte instructions */ +#define RBSEL 0xC8 /* Read Bank Select */ + +/* Three byte instructions */ +#define WGPRDPT 0x60 /* Write EGPRDPT */ +#define RGPRDPT 0x62 /* Read EGPRDPT */ +#define WRXRDPT 0x64 /* Write ERXRDPT */ +#define RRXRDPT 0x66 /* Read ERXRDPT */ +#define WUDARDPT 0x68 /* Write EUDARDPT */ +#define RUDARDPT 0x6A /* Read EUDARDPT */ +#define WGPWRPT 0x6C /* Write EGPWRPT */ +#define RGPWRPT 0x6E /* Read EGPWRPT */ +#define WRXWRPT 0x70 /* Write ERXWRPT */ +#define RRXWRPT 0x72 /* Read ERXWRPT */ +#define WUDAWRPT 0x74 /* Write EUDAWRPT */ +#define RUDAWRPT 0x76 /* Read EUDAWRPT */ + +/* n byte instructions */ +#define RCRCODE 0x00 +#define WCRCODE 0x40 +#define BFSCODE 0x80 +#define BFCCODE 0xA0 +#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */ +#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */ +#define RCRU 0x20 /* Read Control Register Unbanked */ +#define WCRU 0x22 /* Write Control Register Unbanked */ +#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */ +#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */ +#define BFSU 0x24 /* Bit Field Set Unbanked */ +#define BFCU 0x26 /* Bit Field Clear Unbanked */ +#define RGPDATA 0x28 /* Read EGPDATA */ +#define WGPDATA 0x2A /* Write EGPDATA */ +#define RRXDATA 0x2C /* Read ERXDATA */ +#define WRXDATA 0x2E /* Write ERXDATA */ +#define RUDADATA 0x30 /* Read EUDADATA */ +#define WUDADATA 0x32 /* Write EUDADATA */ + +#define SFR_REG_COUNT 0xA0 + +/* ENC424J600 Control Registers + * Control register definitions are a combination of address + * and bank number + * - Register address (bits 0-4) + * - Bank number (bits 5-6) + */ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define BANK_SHIFT 5 + +/* All-bank registers */ +#define EUDAST 0x16 +#define EUDAND 0x18 +#define ESTAT 0x1A +#define EIR 0x1C +#define ECON1 0x1E + +/* Bank 0 registers */ +#define ETXST (0x00 | 0x00) +#define ETXLEN (0x02 | 0x00) +#define ERXST (0x04 | 0x00) +#define ERXTAIL (0x06 | 0x00) +#define ERXHEAD (0x08 | 0x00) +#define EDMAST (0x0A | 0x00) +#define EDMALEN (0x0C | 0x00) +#define EDMADST (0x0E | 0x00) +#define EDMACS (0x10 | 0x00) +#define ETXSTAT (0x12 | 0x00) +#define ETXWIRE (0x14 | 0x00) + +/* Bank 1 registers */ +#define EHT1 (0x00 | 0x20) +#define EHT2 (0x02 | 0x20) +#define EHT3 (0x04 | 0x20) +#define EHT4 (0x06 | 0x20) +#define EPMM1 (0x08 | 0x20) +#define EPMM2 (0x0A | 0x20) +#define EPMM3 (0x0C | 0x20) +#define EPMM4 (0x0E | 0x20) +#define EPMCS (0x10 | 0x20) +#define EPMO (0x12 | 0x20) +#define ERXFCON (0x14 | 0x20) + +/* Bank 2 registers */ +#define MACON1 (0x00 | 0x40) +#define MACON2 (0x02 | 0x40) +#define MABBIPG (0x04 | 0x40) +#define MAIPG (0x06 | 0x40) +#define MACLCON (0x08 | 0x40) +#define MAMXFL (0x0A | 0x40) +#define MICMD (0x12 | 0x40) +#define MIREGADR (0x14 | 0x40) + +/* Bank 3 registers */ +#define MAADR3 (0x00 | 0x60) +#define MAADR2 (0x02 | 0x60) +#define MAADR1 (0x04 | 0x60) +#define MIWR (0x06 | 0x60) +#define MIRD (0x08 | 0x60) +#define MISTAT (0x0A | 0x60) +#define EPAUS (0x0C | 0x60) +#define ECON2 (0x0E | 0x60) +#define ERXWM (0x10 | 0x60) +#define EIE (0x12 | 0x60) +#define EIDLED (0x14 | 0x60) + +/* Unbanked registers */ +#define EGPDATA (0x00 | 0x80) +#define ERXDATA (0x02 | 0x80) +#define EUDADATA (0x04 | 0x80) +#define EGPRDPT (0x06 | 0x80) +#define EGPWRPT (0x08 | 0x80) +#define ERXRDPT (0x0A | 0x80) +#define ERXWRPT (0x0C | 0x80) +#define EUDARDPT (0x0E | 0x80) +#define EUDAWRPT (0x10 | 0x80) + + +/* Register bit definitions */ +/* ESTAT */ +#define INT (1 << 15) +#define FCIDLE (1 << 14) +#define RXBUSY (1 << 13) +#define CLKRDY (1 << 12) +#define PHYDPX (1 << 10) +#define PHYLNK (1 << 8) + +/* EIR */ +#define CRYPTEN (1 << 15) +#define MODEXIF (1 << 14) +#define HASHIF (1 << 13) +#define AESIF (1 << 12) +#define LINKIF (1 << 11) +#define PKTIF (1 << 6) +#define DMAIF (1 << 5) +#define TXIF (1 << 3) +#define TXABTIF (1 << 2) +#define RXABTIF (1 << 1) +#define PCFULIF (1 << 0) + +/* ECON1 */ +#define MODEXST (1 << 15) +#define HASHEN (1 << 14) +#define HASHOP (1 << 13) +#define HASHLST (1 << 12) +#define AESST (1 << 11) +#define AESOP1 (1 << 10) +#define AESOP0 (1 << 9) +#define PKTDEC (1 << 8) +#define FCOP1 (1 << 7) +#define FCOP0 (1 << 6) +#define DMAST (1 << 5) +#define DMACPY (1 << 4) +#define DMACSSD (1 << 3) +#define DMANOCS (1 << 2) +#define TXRTS (1 << 1) +#define RXEN (1 << 0) + +/* ETXSTAT */ +#define LATECOL (1 << 10) +#define MAXCOL (1 << 9) +#define EXDEFER (1 << 8) +#define ETXSTATL_DEFER (1 << 7) +#define CRCBAD (1 << 4) +#define COLCNT_MASK 0xF + +/* ERXFCON */ +#define HTEN (1 << 15) +#define MPEN (1 << 14) +#define NOTPM (1 << 12) +#define PMEN3 (1 << 11) +#define PMEN2 (1 << 10) +#define PMEN1 (1 << 9) +#define PMEN0 (1 << 8) +#define CRCEEN (1 << 7) +#define CRCEN (1 << 6) +#define RUNTEEN (1 << 5) +#define RUNTEN (1 << 4) +#define UCEN (1 << 3) +#define NOTMEEN (1 << 2) +#define MCEN (1 << 1) +#define BCEN (1 << 0) + +/* MACON1 */ +#define LOOPBK (1 << 4) +#define RXPAUS (1 << 2) +#define PASSALL (1 << 1) + +/* MACON2 */ +#define MACON2_DEFER (1 << 14) +#define BPEN (1 << 13) +#define NOBKOFF (1 << 12) +#define PADCFG2 (1 << 7) +#define PADCFG1 (1 << 6) +#define PADCFG0 (1 << 5) +#define TXCRCEN (1 << 4) +#define PHDREN (1 << 3) +#define HFRMEN (1 << 2) +#define MACON2_RSV1 (1 << 1) +#define FULDPX (1 << 0) + +/* MAIPG */ +/* value of the high byte is given by the reserved bits, + * value of the low byte is recomended setting of the + * IPG parameter. + */ +#define MAIPGH_VAL 0x0C +#define MAIPGL_VAL 0x12 + +/* MIREGADRH */ +#define MIREGADR_VAL (1 << 8) + +/* MIREGADRL */ +#define PHREG_MASK 0x1F + +/* MICMD */ +#define MIISCAN (1 << 1) +#define MIIRD (1 << 0) + +/* MISTAT */ +#define NVALID (1 << 2) +#define SCAN (1 << 1) +#define BUSY (1 << 0) + +/* ECON2 */ +#define ETHEN (1 << 15) +#define STRCH (1 << 14) +#define TXMAC (1 << 13) +#define SHA1MD5 (1 << 12) +#define COCON3 (1 << 11) +#define COCON2 (1 << 10) +#define COCON1 (1 << 9) +#define COCON0 (1 << 8) +#define AUTOFC (1 << 7) +#define TXRST (1 << 6) +#define RXRST (1 << 5) +#define ETHRST (1 << 4) +#define MODLEN1 (1 << 3) +#define MODLEN0 (1 << 2) +#define AESLEN1 (1 << 1) +#define AESLEN0 (1 << 0) + +/* EIE */ +#define INTIE (1 << 15) +#define MODEXIE (1 << 14) +#define HASHIE (1 << 13) +#define AESIE (1 << 12) +#define LINKIE (1 << 11) +#define PKTIE (1 << 6) +#define DMAIE (1 << 5) +#define TXIE (1 << 3) +#define TXABTIE (1 << 2) +#define RXABTIE (1 << 1) +#define PCFULIE (1 << 0) + +/* EIDLED */ +#define LACFG3 (1 << 15) +#define LACFG2 (1 << 14) +#define LACFG1 (1 << 13) +#define LACFG0 (1 << 12) +#define LBCFG3 (1 << 11) +#define LBCFG2 (1 << 10) +#define LBCFG1 (1 << 9) +#define LBCFG0 (1 << 8) +#define DEVID_SHIFT 5 +#define DEVID_MASK (0x7 << DEVID_SHIFT) +#define REVID_SHIFT 0 +#define REVID_MASK (0x1F << REVID_SHIFT) + +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHANA 0x04 +#define PHANLPA 0x05 +#define PHANE 0x06 +#define PHCON2 0x11 +#define PHSTAT2 0x1B +#define PHSTAT3 0x1F + +/* PHCON1 */ +#define PRST (1 << 15) +#define PLOOPBK (1 << 14) +#define SPD100 (1 << 13) +#define ANEN (1 << 12) +#define PSLEEP (1 << 11) +#define RENEG (1 << 9) +#define PFULDPX (1 << 8) + +/* PHSTAT1 */ +#define FULL100 (1 << 14) +#define HALF100 (1 << 13) +#define FULL10 (1 << 12) +#define HALF10 (1 << 11) +#define ANDONE (1 << 5) +#define LRFAULT (1 << 4) +#define ANABLE (1 << 3) +#define LLSTAT (1 << 2) +#define EXTREGS (1 << 0) + +/* PHSTAT2 */ +#define PLRITY (1 << 4) + +/* PHSTAT3 */ +#define PHY3SPD100 (1 << 3) +#define PHY3DPX (1 << 4) +#define SPDDPX_SHIFT 2 +#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT) + +/* PHANA */ +/* Default value for PHY initialization*/ +#define PHANA_DEFAULT 0x05E1 + +/* PHANE */ +#define PDFLT (1 << 4) +#define LPARCD (1 << 1) +#define LPANABL (1 << 0) + +#define EUDAST_TEST_VAL 0x1234 + +#define TSV_SIZE 7 + +#define ENCX24J600_DEV_ID 0x1 + +/* Configuration */ + +/* Led is on when the link is present and driven low + * temporarily when packet is TX'd or RX'd + */ +#define LED_A_SETTINGS 0xC + +/* Led is on if the link is in 100 Mbps mode */ +#define LED_B_SETTINGS 0x8 + +/* maximum ethernet frame length + * Currently not used as a limit anywhere + * (we're using the "huge frame enable" feature of + * enc424j600). + */ +#define MAX_FRAMELEN 1518 + +/* Size in bytes of the receive buffer in enc424j600. + * Must be word aligned (even). + */ +#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) + +/* Start of the general purpose area in sram */ +#define SRAM_GP_START 0x0 + +/* SRAM size */ +#define SRAM_SIZE 0x6000 + +/* Start of the receive buffer */ +#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) + +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_RUNTFILTERMATCH 31 +#define RSV_NOTMEFILTERMATCH 32 +#define RSV_HASHFILTERMATCH 33 +#define RSV_MAGICPKTFILTERMATCH 34 +#define RSV_PTRNMTCHFILTERMATCH 35 +#define RSV_UNICASTFILTERMATCH 36 + +#define RSV_SIZE 8 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + +struct rsv { + u16 next_packet; + u16 len; + u32 rxstat; +}; + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ + +#define RXSTART_INIT ERXST_VAL +#define RXEND_INIT 0x5FFF + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count); +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count); + + +#endif diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c new file mode 100644 index 000000000..190c22cdc --- /dev/null +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -0,0 +1,724 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2018 Microchip Technology Inc. */ + +#include <linux/netdevice.h> +#include "lan743x_main.h" +#include "lan743x_ethtool.h" +#include <linux/net_tstamp.h> +#include <linux/pci.h> +#include <linux/phy.h> + +/* eeprom */ +#define LAN743X_EEPROM_MAGIC (0x74A5) +#define LAN743X_OTP_MAGIC (0x74F3) +#define EEPROM_INDICATOR_1 (0xA5) +#define EEPROM_INDICATOR_2 (0xAA) +#define EEPROM_MAC_OFFSET (0x01) +#define MAX_EEPROM_SIZE 512 +#define OTP_INDICATOR_1 (0xF3) +#define OTP_INDICATOR_2 (0xF7) + +static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset, + u32 length, u8 *data) +{ + unsigned long timeout; + u32 buf; + int i; + + buf = lan743x_csr_read(adapter, OTP_PWR_DN); + + if (buf & OTP_PWR_DN_PWRDN_N_) { + /* clear it and wait to be cleared */ + lan743x_csr_write(adapter, OTP_PWR_DN, 0); + + timeout = jiffies + HZ; + do { + udelay(1); + buf = lan743x_csr_read(adapter, OTP_PWR_DN); + if (time_after(jiffies, timeout)) { + netif_warn(adapter, drv, adapter->netdev, + "timeout on OTP_PWR_DN completion\n"); + return -EIO; + } + } while (buf & OTP_PWR_DN_PWRDN_N_); + } + + /* set to BYTE program mode */ + lan743x_csr_write(adapter, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_); + + for (i = 0; i < length; i++) { + lan743x_csr_write(adapter, OTP_ADDR1, + ((offset + i) >> 8) & + OTP_ADDR1_15_11_MASK_); + lan743x_csr_write(adapter, OTP_ADDR2, + ((offset + i) & + OTP_ADDR2_10_3_MASK_)); + lan743x_csr_write(adapter, OTP_PRGM_DATA, data[i]); + lan743x_csr_write(adapter, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_); + lan743x_csr_write(adapter, OTP_CMD_GO, OTP_CMD_GO_GO_); + + timeout = jiffies + HZ; + do { + udelay(1); + buf = lan743x_csr_read(adapter, OTP_STATUS); + if (time_after(jiffies, timeout)) { + netif_warn(adapter, drv, adapter->netdev, + "Timeout on OTP_STATUS completion\n"); + return -EIO; + } + } while (buf & OTP_STATUS_BUSY_); + } + + return 0; +} + +static int lan743x_eeprom_wait(struct lan743x_adapter *adapter) +{ + unsigned long start_time = jiffies; + u32 val; + + do { + val = lan743x_csr_read(adapter, E2P_CMD); + + if (!(val & E2P_CMD_EPC_BUSY_) || + (val & E2P_CMD_EPC_TIMEOUT_)) + break; + usleep_range(40, 100); + } while (!time_after(jiffies, start_time + HZ)); + + if (val & (E2P_CMD_EPC_TIMEOUT_ | E2P_CMD_EPC_BUSY_)) { + netif_warn(adapter, drv, adapter->netdev, + "EEPROM read operation timeout\n"); + return -EIO; + } + + return 0; +} + +static int lan743x_eeprom_confirm_not_busy(struct lan743x_adapter *adapter) +{ + unsigned long start_time = jiffies; + u32 val; + + do { + val = lan743x_csr_read(adapter, E2P_CMD); + + if (!(val & E2P_CMD_EPC_BUSY_)) + return 0; + + usleep_range(40, 100); + } while (!time_after(jiffies, start_time + HZ)); + + netif_warn(adapter, drv, adapter->netdev, "EEPROM is busy\n"); + return -EIO; +} + +static int lan743x_eeprom_read(struct lan743x_adapter *adapter, + u32 offset, u32 length, u8 *data) +{ + int retval; + u32 val; + int i; + + retval = lan743x_eeprom_confirm_not_busy(adapter); + if (retval) + return retval; + + for (i = 0; i < length; i++) { + val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_; + val |= (offset & E2P_CMD_EPC_ADDR_MASK_); + lan743x_csr_write(adapter, E2P_CMD, val); + + retval = lan743x_eeprom_wait(adapter); + if (retval < 0) + return retval; + + val = lan743x_csr_read(adapter, E2P_DATA); + data[i] = val & 0xFF; + offset++; + } + + return 0; +} + +static int lan743x_eeprom_write(struct lan743x_adapter *adapter, + u32 offset, u32 length, u8 *data) +{ + int retval; + u32 val; + int i; + + retval = lan743x_eeprom_confirm_not_busy(adapter); + if (retval) + return retval; + + /* Issue write/erase enable command */ + val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_; + lan743x_csr_write(adapter, E2P_CMD, val); + + retval = lan743x_eeprom_wait(adapter); + if (retval < 0) + return retval; + + for (i = 0; i < length; i++) { + /* Fill data register */ + val = data[i]; + lan743x_csr_write(adapter, E2P_DATA, val); + + /* Send "write" command */ + val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_; + val |= (offset & E2P_CMD_EPC_ADDR_MASK_); + lan743x_csr_write(adapter, E2P_CMD, val); + + retval = lan743x_eeprom_wait(adapter); + if (retval < 0) + return retval; + + offset++; + } + + return 0; +} + +static void lan743x_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->bus_info, + pci_name(adapter->pdev), sizeof(info->bus_info)); +} + +static u32 lan743x_ethtool_get_msglevel(struct net_device *netdev) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + return adapter->msg_enable; +} + +static void lan743x_ethtool_set_msglevel(struct net_device *netdev, + u32 msglevel) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + adapter->msg_enable = msglevel; +} + +static int lan743x_ethtool_get_eeprom_len(struct net_device *netdev) +{ + return MAX_EEPROM_SIZE; +} + +static int lan743x_ethtool_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + return lan743x_eeprom_read(adapter, ee->offset, ee->len, data); +} + +static int lan743x_ethtool_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + int ret = -EINVAL; + + if (ee->magic == LAN743X_EEPROM_MAGIC) + ret = lan743x_eeprom_write(adapter, ee->offset, ee->len, + data); + /* Beware! OTP is One Time Programming ONLY! + * So do some strict condition check before messing up + */ + else if ((ee->magic == LAN743X_OTP_MAGIC) && + (ee->offset == 0) && + (ee->len == MAX_EEPROM_SIZE) && + (data[0] == OTP_INDICATOR_1)) + ret = lan743x_otp_write(adapter, ee->offset, ee->len, data); + + return ret; +} + +static const char lan743x_set0_hw_cnt_strings[][ETH_GSTRING_LEN] = { + "RX FCS Errors", + "RX Alignment Errors", + "Rx Fragment Errors", + "RX Jabber Errors", + "RX Undersize Frame Errors", + "RX Oversize Frame Errors", + "RX Dropped Frames", + "RX Unicast Byte Count", + "RX Broadcast Byte Count", + "RX Multicast Byte Count", + "RX Unicast Frames", + "RX Broadcast Frames", + "RX Multicast Frames", + "RX Pause Frames", + "RX 64 Byte Frames", + "RX 65 - 127 Byte Frames", + "RX 128 - 255 Byte Frames", + "RX 256 - 511 Bytes Frames", + "RX 512 - 1023 Byte Frames", + "RX 1024 - 1518 Byte Frames", + "RX Greater 1518 Byte Frames", +}; + +static const char lan743x_set1_sw_cnt_strings[][ETH_GSTRING_LEN] = { + "RX Queue 0 Frames", + "RX Queue 1 Frames", + "RX Queue 2 Frames", + "RX Queue 3 Frames", +}; + +static const char lan743x_set2_hw_cnt_strings[][ETH_GSTRING_LEN] = { + "RX Total Frames", + "EEE RX LPI Transitions", + "EEE RX LPI Time", + "RX Counter Rollover Status", + "TX FCS Errors", + "TX Excess Deferral Errors", + "TX Carrier Errors", + "TX Bad Byte Count", + "TX Single Collisions", + "TX Multiple Collisions", + "TX Excessive Collision", + "TX Late Collisions", + "TX Unicast Byte Count", + "TX Broadcast Byte Count", + "TX Multicast Byte Count", + "TX Unicast Frames", + "TX Broadcast Frames", + "TX Multicast Frames", + "TX Pause Frames", + "TX 64 Byte Frames", + "TX 65 - 127 Byte Frames", + "TX 128 - 255 Byte Frames", + "TX 256 - 511 Bytes Frames", + "TX 512 - 1023 Byte Frames", + "TX 1024 - 1518 Byte Frames", + "TX Greater 1518 Byte Frames", + "TX Total Frames", + "EEE TX LPI Transitions", + "EEE TX LPI Time", + "TX Counter Rollover Status", +}; + +static const u32 lan743x_set0_hw_cnt_addr[] = { + STAT_RX_FCS_ERRORS, + STAT_RX_ALIGNMENT_ERRORS, + STAT_RX_FRAGMENT_ERRORS, + STAT_RX_JABBER_ERRORS, + STAT_RX_UNDERSIZE_FRAME_ERRORS, + STAT_RX_OVERSIZE_FRAME_ERRORS, + STAT_RX_DROPPED_FRAMES, + STAT_RX_UNICAST_BYTE_COUNT, + STAT_RX_BROADCAST_BYTE_COUNT, + STAT_RX_MULTICAST_BYTE_COUNT, + STAT_RX_UNICAST_FRAMES, + STAT_RX_BROADCAST_FRAMES, + STAT_RX_MULTICAST_FRAMES, + STAT_RX_PAUSE_FRAMES, + STAT_RX_64_BYTE_FRAMES, + STAT_RX_65_127_BYTE_FRAMES, + STAT_RX_128_255_BYTE_FRAMES, + STAT_RX_256_511_BYTES_FRAMES, + STAT_RX_512_1023_BYTE_FRAMES, + STAT_RX_1024_1518_BYTE_FRAMES, + STAT_RX_GREATER_1518_BYTE_FRAMES, +}; + +static const u32 lan743x_set2_hw_cnt_addr[] = { + STAT_RX_TOTAL_FRAMES, + STAT_EEE_RX_LPI_TRANSITIONS, + STAT_EEE_RX_LPI_TIME, + STAT_RX_COUNTER_ROLLOVER_STATUS, + STAT_TX_FCS_ERRORS, + STAT_TX_EXCESS_DEFERRAL_ERRORS, + STAT_TX_CARRIER_ERRORS, + STAT_TX_BAD_BYTE_COUNT, + STAT_TX_SINGLE_COLLISIONS, + STAT_TX_MULTIPLE_COLLISIONS, + STAT_TX_EXCESSIVE_COLLISION, + STAT_TX_LATE_COLLISIONS, + STAT_TX_UNICAST_BYTE_COUNT, + STAT_TX_BROADCAST_BYTE_COUNT, + STAT_TX_MULTICAST_BYTE_COUNT, + STAT_TX_UNICAST_FRAMES, + STAT_TX_BROADCAST_FRAMES, + STAT_TX_MULTICAST_FRAMES, + STAT_TX_PAUSE_FRAMES, + STAT_TX_64_BYTE_FRAMES, + STAT_TX_65_127_BYTE_FRAMES, + STAT_TX_128_255_BYTE_FRAMES, + STAT_TX_256_511_BYTES_FRAMES, + STAT_TX_512_1023_BYTE_FRAMES, + STAT_TX_1024_1518_BYTE_FRAMES, + STAT_TX_GREATER_1518_BYTE_FRAMES, + STAT_TX_TOTAL_FRAMES, + STAT_EEE_TX_LPI_TRANSITIONS, + STAT_EEE_TX_LPI_TIME, + STAT_TX_COUNTER_ROLLOVER_STATUS +}; + +static void lan743x_ethtool_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(data, lan743x_set0_hw_cnt_strings, + sizeof(lan743x_set0_hw_cnt_strings)); + memcpy(&data[sizeof(lan743x_set0_hw_cnt_strings)], + lan743x_set1_sw_cnt_strings, + sizeof(lan743x_set1_sw_cnt_strings)); + memcpy(&data[sizeof(lan743x_set0_hw_cnt_strings) + + sizeof(lan743x_set1_sw_cnt_strings)], + lan743x_set2_hw_cnt_strings, + sizeof(lan743x_set2_hw_cnt_strings)); + break; + } +} + +static void lan743x_ethtool_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + int data_index = 0; + u32 buf; + int i; + + for (i = 0; i < ARRAY_SIZE(lan743x_set0_hw_cnt_addr); i++) { + buf = lan743x_csr_read(adapter, lan743x_set0_hw_cnt_addr[i]); + data[data_index++] = (u64)buf; + } + for (i = 0; i < ARRAY_SIZE(adapter->rx); i++) + data[data_index++] = (u64)(adapter->rx[i].frame_count); + for (i = 0; i < ARRAY_SIZE(lan743x_set2_hw_cnt_addr); i++) { + buf = lan743x_csr_read(adapter, lan743x_set2_hw_cnt_addr[i]); + data[data_index++] = (u64)buf; + } +} + +static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + { + int ret; + + ret = ARRAY_SIZE(lan743x_set0_hw_cnt_strings); + ret += ARRAY_SIZE(lan743x_set1_sw_cnt_strings); + ret += ARRAY_SIZE(lan743x_set2_hw_cnt_strings); + return ret; + } + default: + return -EOPNOTSUPP; + } +} + +static int lan743x_ethtool_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *rxnfc, + u32 *rule_locs) +{ + switch (rxnfc->cmd) { + case ETHTOOL_GRXFH: + rxnfc->data = 0; + switch (rxnfc->flow_type) { + case TCP_V4_FLOW:case UDP_V4_FLOW: + case TCP_V6_FLOW:case UDP_V6_FLOW: + rxnfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fall through */ + case IPV4_FLOW: case IPV6_FLOW: + rxnfc->data |= RXH_IP_SRC | RXH_IP_DST; + return 0; + } + break; + case ETHTOOL_GRXRINGS: + rxnfc->data = LAN743X_USED_RX_CHANNELS; + return 0; + } + return -EOPNOTSUPP; +} + +static u32 lan743x_ethtool_get_rxfh_key_size(struct net_device *netdev) +{ + return 40; +} + +static u32 lan743x_ethtool_get_rxfh_indir_size(struct net_device *netdev) +{ + return 128; +} + +static int lan743x_ethtool_get_rxfh(struct net_device *netdev, + u32 *indir, u8 *key, u8 *hfunc) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + if (indir) { + int dw_index; + int byte_index = 0; + + for (dw_index = 0; dw_index < 32; dw_index++) { + u32 four_entries = + lan743x_csr_read(adapter, RFE_INDX(dw_index)); + + byte_index = dw_index << 2; + indir[byte_index + 0] = + ((four_entries >> 0) & 0x000000FF); + indir[byte_index + 1] = + ((four_entries >> 8) & 0x000000FF); + indir[byte_index + 2] = + ((four_entries >> 16) & 0x000000FF); + indir[byte_index + 3] = + ((four_entries >> 24) & 0x000000FF); + } + } + if (key) { + int dword_index; + int byte_index = 0; + + for (dword_index = 0; dword_index < 10; dword_index++) { + u32 four_entries = + lan743x_csr_read(adapter, + RFE_HASH_KEY(dword_index)); + + byte_index = dword_index << 2; + key[byte_index + 0] = + ((four_entries >> 0) & 0x000000FF); + key[byte_index + 1] = + ((four_entries >> 8) & 0x000000FF); + key[byte_index + 2] = + ((four_entries >> 16) & 0x000000FF); + key[byte_index + 3] = + ((four_entries >> 24) & 0x000000FF); + } + } + if (hfunc) + (*hfunc) = ETH_RSS_HASH_TOP; + return 0; +} + +static int lan743x_ethtool_set_rxfh(struct net_device *netdev, + const u32 *indir, const u8 *key, + const u8 hfunc) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + + if (indir) { + u32 indir_value = 0; + int dword_index = 0; + int byte_index = 0; + + for (dword_index = 0; dword_index < 32; dword_index++) { + byte_index = dword_index << 2; + indir_value = + (((indir[byte_index + 0] & 0x000000FF) << 0) | + ((indir[byte_index + 1] & 0x000000FF) << 8) | + ((indir[byte_index + 2] & 0x000000FF) << 16) | + ((indir[byte_index + 3] & 0x000000FF) << 24)); + lan743x_csr_write(adapter, RFE_INDX(dword_index), + indir_value); + } + } + if (key) { + int dword_index = 0; + int byte_index = 0; + u32 key_value = 0; + + for (dword_index = 0; dword_index < 10; dword_index++) { + byte_index = dword_index << 2; + key_value = + ((((u32)(key[byte_index + 0])) << 0) | + (((u32)(key[byte_index + 1])) << 8) | + (((u32)(key[byte_index + 2])) << 16) | + (((u32)(key[byte_index + 3])) << 24)); + lan743x_csr_write(adapter, RFE_HASH_KEY(dword_index), + key_value); + } + } + return 0; +} + +static int lan743x_ethtool_get_ts_info(struct net_device *netdev, + struct ethtool_ts_info *ts_info) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + if (adapter->ptp.ptp_clock) + ts_info->phc_index = ptp_clock_index(adapter->ptp.ptp_clock); + else + ts_info->phc_index = -1; + + ts_info->tx_types = BIT(HWTSTAMP_TX_OFF) | + BIT(HWTSTAMP_TX_ON) | + BIT(HWTSTAMP_TX_ONESTEP_SYNC); + ts_info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_ALL); + return 0; +} + +static int lan743x_ethtool_get_eee(struct net_device *netdev, + struct ethtool_eee *eee) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + struct phy_device *phydev = netdev->phydev; + u32 buf; + int ret; + + if (!phydev) + return -EIO; + if (!phydev->drv) { + netif_err(adapter, drv, adapter->netdev, + "Missing PHY Driver\n"); + return -EIO; + } + + ret = phy_ethtool_get_eee(phydev, eee); + if (ret < 0) + return ret; + + buf = lan743x_csr_read(adapter, MAC_CR); + if (buf & MAC_CR_EEE_EN_) { + eee->eee_enabled = true; + eee->eee_active = !!(eee->advertised & eee->lp_advertised); + eee->tx_lpi_enabled = true; + /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */ + buf = lan743x_csr_read(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT); + eee->tx_lpi_timer = buf; + } else { + eee->eee_enabled = false; + eee->eee_active = false; + eee->tx_lpi_enabled = false; + eee->tx_lpi_timer = 0; + } + + return 0; +} + +static int lan743x_ethtool_set_eee(struct net_device *netdev, + struct ethtool_eee *eee) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + struct phy_device *phydev = NULL; + u32 buf = 0; + int ret = 0; + + if (!netdev) + return -EINVAL; + adapter = netdev_priv(netdev); + if (!adapter) + return -EINVAL; + phydev = netdev->phydev; + if (!phydev) + return -EIO; + if (!phydev->drv) { + netif_err(adapter, drv, adapter->netdev, + "Missing PHY Driver\n"); + return -EIO; + } + + if (eee->eee_enabled) { + ret = phy_init_eee(phydev, 0); + if (ret) { + netif_err(adapter, drv, adapter->netdev, + "EEE initialization failed\n"); + return ret; + } + + buf = (u32)eee->tx_lpi_timer; + lan743x_csr_write(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT, buf); + + buf = lan743x_csr_read(adapter, MAC_CR); + buf |= MAC_CR_EEE_EN_; + lan743x_csr_write(adapter, MAC_CR, buf); + } else { + buf = lan743x_csr_read(adapter, MAC_CR); + buf &= ~MAC_CR_EEE_EN_; + lan743x_csr_write(adapter, MAC_CR, buf); + } + + return phy_ethtool_set_eee(phydev, eee); +} + +#ifdef CONFIG_PM +static void lan743x_ethtool_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + wol->supported = 0; + wol->wolopts = 0; + + if (netdev->phydev) + phy_ethtool_get_wol(netdev->phydev, wol); + + wol->supported |= WAKE_BCAST | WAKE_UCAST | WAKE_MCAST | + WAKE_MAGIC | WAKE_PHY | WAKE_ARP; + + wol->wolopts |= adapter->wolopts; +} + +static int lan743x_ethtool_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + adapter->wolopts = 0; + if (wol->wolopts & WAKE_UCAST) + adapter->wolopts |= WAKE_UCAST; + if (wol->wolopts & WAKE_MCAST) + adapter->wolopts |= WAKE_MCAST; + if (wol->wolopts & WAKE_BCAST) + adapter->wolopts |= WAKE_BCAST; + if (wol->wolopts & WAKE_MAGIC) + adapter->wolopts |= WAKE_MAGIC; + if (wol->wolopts & WAKE_PHY) + adapter->wolopts |= WAKE_PHY; + if (wol->wolopts & WAKE_ARP) + adapter->wolopts |= WAKE_ARP; + + device_set_wakeup_enable(&adapter->pdev->dev, (bool)wol->wolopts); + + return netdev->phydev ? phy_ethtool_set_wol(netdev->phydev, wol) + : -ENETDOWN; +} +#endif /* CONFIG_PM */ + +const struct ethtool_ops lan743x_ethtool_ops = { + .get_drvinfo = lan743x_ethtool_get_drvinfo, + .get_msglevel = lan743x_ethtool_get_msglevel, + .set_msglevel = lan743x_ethtool_set_msglevel, + .get_link = ethtool_op_get_link, + + .get_eeprom_len = lan743x_ethtool_get_eeprom_len, + .get_eeprom = lan743x_ethtool_get_eeprom, + .set_eeprom = lan743x_ethtool_set_eeprom, + .get_strings = lan743x_ethtool_get_strings, + .get_ethtool_stats = lan743x_ethtool_get_ethtool_stats, + .get_sset_count = lan743x_ethtool_get_sset_count, + .get_rxnfc = lan743x_ethtool_get_rxnfc, + .get_rxfh_key_size = lan743x_ethtool_get_rxfh_key_size, + .get_rxfh_indir_size = lan743x_ethtool_get_rxfh_indir_size, + .get_rxfh = lan743x_ethtool_get_rxfh, + .set_rxfh = lan743x_ethtool_set_rxfh, + .get_ts_info = lan743x_ethtool_get_ts_info, + .get_eee = lan743x_ethtool_get_eee, + .set_eee = lan743x_ethtool_set_eee, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, +#ifdef CONFIG_PM + .get_wol = lan743x_ethtool_get_wol, + .set_wol = lan743x_ethtool_set_wol, +#endif +}; diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.h b/drivers/net/ethernet/microchip/lan743x_ethtool.h new file mode 100644 index 000000000..d0d11a777 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2018 Microchip Technology Inc. */ + +#ifndef _LAN743X_ETHTOOL_H +#define _LAN743X_ETHTOOL_H + +#include "linux/ethtool.h" + +extern const struct ethtool_ops lan743x_ethtool_ops; + +#endif /* _LAN743X_ETHTOOL_H */ diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c new file mode 100644 index 000000000..e734bc5e3 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -0,0 +1,3068 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2018 Microchip Technology Inc. */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/crc32.h> +#include <linux/microchipphy.h> +#include <linux/net_tstamp.h> +#include <linux/phy.h> +#include <linux/rtnetlink.h> +#include <linux/iopoll.h> +#include <linux/crc16.h> +#include "lan743x_main.h" +#include "lan743x_ethtool.h" + +static void lan743x_pci_cleanup(struct lan743x_adapter *adapter) +{ + pci_release_selected_regions(adapter->pdev, + pci_select_bars(adapter->pdev, + IORESOURCE_MEM)); + pci_disable_device(adapter->pdev); +} + +static int lan743x_pci_init(struct lan743x_adapter *adapter, + struct pci_dev *pdev) +{ + unsigned long bars = 0; + int ret; + + adapter->pdev = pdev; + ret = pci_enable_device_mem(pdev); + if (ret) + goto return_error; + + netif_info(adapter, probe, adapter->netdev, + "PCI: Vendor ID = 0x%04X, Device ID = 0x%04X\n", + pdev->vendor, pdev->device); + bars = pci_select_bars(pdev, IORESOURCE_MEM); + if (!test_bit(0, &bars)) + goto disable_device; + + ret = pci_request_selected_regions(pdev, bars, DRIVER_NAME); + if (ret) + goto disable_device; + + pci_set_master(pdev); + return 0; + +disable_device: + pci_disable_device(adapter->pdev); + +return_error: + return ret; +} + +u32 lan743x_csr_read(struct lan743x_adapter *adapter, int offset) +{ + return ioread32(&adapter->csr.csr_address[offset]); +} + +void lan743x_csr_write(struct lan743x_adapter *adapter, int offset, + u32 data) +{ + iowrite32(data, &adapter->csr.csr_address[offset]); +} + +#define LAN743X_CSR_READ_OP(offset) lan743x_csr_read(adapter, offset) + +static int lan743x_csr_light_reset(struct lan743x_adapter *adapter) +{ + u32 data; + + data = lan743x_csr_read(adapter, HW_CFG); + data |= HW_CFG_LRST_; + lan743x_csr_write(adapter, HW_CFG, data); + + return readx_poll_timeout(LAN743X_CSR_READ_OP, HW_CFG, data, + !(data & HW_CFG_LRST_), 100000, 10000000); +} + +static int lan743x_csr_wait_for_bit(struct lan743x_adapter *adapter, + int offset, u32 bit_mask, + int target_value, int usleep_min, + int usleep_max, int count) +{ + u32 data; + + return readx_poll_timeout(LAN743X_CSR_READ_OP, offset, data, + target_value == ((data & bit_mask) ? 1 : 0), + usleep_max, usleep_min * count); +} + +static int lan743x_csr_init(struct lan743x_adapter *adapter) +{ + struct lan743x_csr *csr = &adapter->csr; + resource_size_t bar_start, bar_length; + int result; + + bar_start = pci_resource_start(adapter->pdev, 0); + bar_length = pci_resource_len(adapter->pdev, 0); + csr->csr_address = devm_ioremap(&adapter->pdev->dev, + bar_start, bar_length); + if (!csr->csr_address) { + result = -ENOMEM; + goto clean_up; + } + + csr->id_rev = lan743x_csr_read(adapter, ID_REV); + csr->fpga_rev = lan743x_csr_read(adapter, FPGA_REV); + netif_info(adapter, probe, adapter->netdev, + "ID_REV = 0x%08X, FPGA_REV = %d.%d\n", + csr->id_rev, FPGA_REV_GET_MAJOR_(csr->fpga_rev), + FPGA_REV_GET_MINOR_(csr->fpga_rev)); + if (!ID_REV_IS_VALID_CHIP_ID_(csr->id_rev)) { + result = -ENODEV; + goto clean_up; + } + + csr->flags = LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR; + switch (csr->id_rev & ID_REV_CHIP_REV_MASK_) { + case ID_REV_CHIP_REV_A0_: + csr->flags |= LAN743X_CSR_FLAG_IS_A0; + csr->flags &= ~LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR; + break; + case ID_REV_CHIP_REV_B0_: + csr->flags |= LAN743X_CSR_FLAG_IS_B0; + break; + } + + result = lan743x_csr_light_reset(adapter); + if (result) + goto clean_up; + return 0; +clean_up: + return result; +} + +static void lan743x_intr_software_isr(void *context) +{ + struct lan743x_adapter *adapter = context; + struct lan743x_intr *intr = &adapter->intr; + u32 int_sts; + + int_sts = lan743x_csr_read(adapter, INT_STS); + if (int_sts & INT_BIT_SW_GP_) { + /* disable the interrupt to prevent repeated re-triggering */ + lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_SW_GP_); + intr->software_isr_flag = 1; + } +} + +static void lan743x_tx_isr(void *context, u32 int_sts, u32 flags) +{ + struct lan743x_tx *tx = context; + struct lan743x_adapter *adapter = tx->adapter; + bool enable_flag = true; + + lan743x_csr_read(adapter, INT_EN_SET); + if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR) { + lan743x_csr_write(adapter, INT_EN_CLR, + INT_BIT_DMA_TX_(tx->channel_number)); + } + + if (int_sts & INT_BIT_DMA_TX_(tx->channel_number)) { + u32 ioc_bit = DMAC_INT_BIT_TX_IOC_(tx->channel_number); + u32 dmac_int_sts; + u32 dmac_int_en; + + if (flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ) + dmac_int_sts = lan743x_csr_read(adapter, DMAC_INT_STS); + else + dmac_int_sts = ioc_bit; + if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK) + dmac_int_en = lan743x_csr_read(adapter, + DMAC_INT_EN_SET); + else + dmac_int_en = ioc_bit; + + dmac_int_en &= ioc_bit; + dmac_int_sts &= dmac_int_en; + if (dmac_int_sts & ioc_bit) { + napi_schedule(&tx->napi); + enable_flag = false;/* poll func will enable later */ + } + } + + if (enable_flag) + /* enable isr */ + lan743x_csr_write(adapter, INT_EN_SET, + INT_BIT_DMA_TX_(tx->channel_number)); +} + +static void lan743x_rx_isr(void *context, u32 int_sts, u32 flags) +{ + struct lan743x_rx *rx = context; + struct lan743x_adapter *adapter = rx->adapter; + bool enable_flag = true; + + if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR) { + lan743x_csr_write(adapter, INT_EN_CLR, + INT_BIT_DMA_RX_(rx->channel_number)); + } + + if (int_sts & INT_BIT_DMA_RX_(rx->channel_number)) { + u32 rx_frame_bit = DMAC_INT_BIT_RXFRM_(rx->channel_number); + u32 dmac_int_sts; + u32 dmac_int_en; + + if (flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ) + dmac_int_sts = lan743x_csr_read(adapter, DMAC_INT_STS); + else + dmac_int_sts = rx_frame_bit; + if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK) + dmac_int_en = lan743x_csr_read(adapter, + DMAC_INT_EN_SET); + else + dmac_int_en = rx_frame_bit; + + dmac_int_en &= rx_frame_bit; + dmac_int_sts &= dmac_int_en; + if (dmac_int_sts & rx_frame_bit) { + napi_schedule(&rx->napi); + enable_flag = false;/* poll funct will enable later */ + } + } + + if (enable_flag) { + /* enable isr */ + lan743x_csr_write(adapter, INT_EN_SET, + INT_BIT_DMA_RX_(rx->channel_number)); + } +} + +static void lan743x_intr_shared_isr(void *context, u32 int_sts, u32 flags) +{ + struct lan743x_adapter *adapter = context; + unsigned int channel; + + if (int_sts & INT_BIT_ALL_RX_) { + for (channel = 0; channel < LAN743X_USED_RX_CHANNELS; + channel++) { + u32 int_bit = INT_BIT_DMA_RX_(channel); + + if (int_sts & int_bit) { + lan743x_rx_isr(&adapter->rx[channel], + int_bit, flags); + int_sts &= ~int_bit; + } + } + } + if (int_sts & INT_BIT_ALL_TX_) { + for (channel = 0; channel < LAN743X_USED_TX_CHANNELS; + channel++) { + u32 int_bit = INT_BIT_DMA_TX_(channel); + + if (int_sts & int_bit) { + lan743x_tx_isr(&adapter->tx[channel], + int_bit, flags); + int_sts &= ~int_bit; + } + } + } + if (int_sts & INT_BIT_ALL_OTHER_) { + if (int_sts & INT_BIT_SW_GP_) { + lan743x_intr_software_isr(adapter); + int_sts &= ~INT_BIT_SW_GP_; + } + if (int_sts & INT_BIT_1588_) { + lan743x_ptp_isr(adapter); + int_sts &= ~INT_BIT_1588_; + } + } + if (int_sts) + lan743x_csr_write(adapter, INT_EN_CLR, int_sts); +} + +static irqreturn_t lan743x_intr_entry_isr(int irq, void *ptr) +{ + struct lan743x_vector *vector = ptr; + struct lan743x_adapter *adapter = vector->adapter; + irqreturn_t result = IRQ_NONE; + u32 int_enables; + u32 int_sts; + + if (vector->flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ) { + int_sts = lan743x_csr_read(adapter, INT_STS); + } else if (vector->flags & + (LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C)) { + int_sts = lan743x_csr_read(adapter, INT_STS_R2C); + } else { + /* use mask as implied status */ + int_sts = vector->int_mask | INT_BIT_MAS_; + } + + if (!(int_sts & INT_BIT_MAS_)) + goto irq_done; + + if (vector->flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR) + /* disable vector interrupt */ + lan743x_csr_write(adapter, + INT_VEC_EN_CLR, + INT_VEC_EN_(vector->vector_index)); + + if (vector->flags & LAN743X_VECTOR_FLAG_MASTER_ENABLE_CLEAR) + /* disable master interrupt */ + lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_MAS_); + + if (vector->flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK) { + int_enables = lan743x_csr_read(adapter, INT_EN_SET); + } else { + /* use vector mask as implied enable mask */ + int_enables = vector->int_mask; + } + + int_sts &= int_enables; + int_sts &= vector->int_mask; + if (int_sts) { + if (vector->handler) { + vector->handler(vector->context, + int_sts, vector->flags); + } else { + /* disable interrupts on this vector */ + lan743x_csr_write(adapter, INT_EN_CLR, + vector->int_mask); + } + result = IRQ_HANDLED; + } + + if (vector->flags & LAN743X_VECTOR_FLAG_MASTER_ENABLE_SET) + /* enable master interrupt */ + lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_MAS_); + + if (vector->flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET) + /* enable vector interrupt */ + lan743x_csr_write(adapter, + INT_VEC_EN_SET, + INT_VEC_EN_(vector->vector_index)); +irq_done: + return result; +} + +static int lan743x_intr_test_isr(struct lan743x_adapter *adapter) +{ + struct lan743x_intr *intr = &adapter->intr; + int result = -ENODEV; + int timeout = 10; + + intr->software_isr_flag = 0; + + /* enable interrupt */ + lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_SW_GP_); + + /* activate interrupt here */ + lan743x_csr_write(adapter, INT_SET, INT_BIT_SW_GP_); + while ((timeout > 0) && (!(intr->software_isr_flag))) { + usleep_range(1000, 20000); + timeout--; + } + + if (intr->software_isr_flag) + result = 0; + + /* disable interrupts */ + lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_SW_GP_); + return result; +} + +static int lan743x_intr_register_isr(struct lan743x_adapter *adapter, + int vector_index, u32 flags, + u32 int_mask, + lan743x_vector_handler handler, + void *context) +{ + struct lan743x_vector *vector = &adapter->intr.vector_list + [vector_index]; + int ret; + + vector->adapter = adapter; + vector->flags = flags; + vector->vector_index = vector_index; + vector->int_mask = int_mask; + vector->handler = handler; + vector->context = context; + + ret = request_irq(vector->irq, + lan743x_intr_entry_isr, + (flags & LAN743X_VECTOR_FLAG_IRQ_SHARED) ? + IRQF_SHARED : 0, DRIVER_NAME, vector); + if (ret) { + vector->handler = NULL; + vector->context = NULL; + vector->int_mask = 0; + vector->flags = 0; + } + return ret; +} + +static void lan743x_intr_unregister_isr(struct lan743x_adapter *adapter, + int vector_index) +{ + struct lan743x_vector *vector = &adapter->intr.vector_list + [vector_index]; + + free_irq(vector->irq, vector); + vector->handler = NULL; + vector->context = NULL; + vector->int_mask = 0; + vector->flags = 0; +} + +static u32 lan743x_intr_get_vector_flags(struct lan743x_adapter *adapter, + u32 int_mask) +{ + int index; + + for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) { + if (adapter->intr.vector_list[index].int_mask & int_mask) + return adapter->intr.vector_list[index].flags; + } + return 0; +} + +static void lan743x_intr_close(struct lan743x_adapter *adapter) +{ + struct lan743x_intr *intr = &adapter->intr; + int index = 0; + + lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_MAS_); + lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0x000000FF); + + for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) { + if (intr->flags & INTR_FLAG_IRQ_REQUESTED(index)) { + lan743x_intr_unregister_isr(adapter, index); + intr->flags &= ~INTR_FLAG_IRQ_REQUESTED(index); + } + } + + if (intr->flags & INTR_FLAG_MSI_ENABLED) { + pci_disable_msi(adapter->pdev); + intr->flags &= ~INTR_FLAG_MSI_ENABLED; + } + + if (intr->flags & INTR_FLAG_MSIX_ENABLED) { + pci_disable_msix(adapter->pdev); + intr->flags &= ~INTR_FLAG_MSIX_ENABLED; + } +} + +static int lan743x_intr_open(struct lan743x_adapter *adapter) +{ + struct msix_entry msix_entries[LAN743X_MAX_VECTOR_COUNT]; + struct lan743x_intr *intr = &adapter->intr; + u32 int_vec_en_auto_clr = 0; + u32 int_vec_map0 = 0; + u32 int_vec_map1 = 0; + int ret = -ENODEV; + int index = 0; + u32 flags = 0; + + intr->number_of_vectors = 0; + + /* Try to set up MSIX interrupts */ + memset(&msix_entries[0], 0, + sizeof(struct msix_entry) * LAN743X_MAX_VECTOR_COUNT); + for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) + msix_entries[index].entry = index; + ret = pci_enable_msix_range(adapter->pdev, + msix_entries, 1, + 1 + LAN743X_USED_TX_CHANNELS + + LAN743X_USED_RX_CHANNELS); + + if (ret > 0) { + intr->flags |= INTR_FLAG_MSIX_ENABLED; + intr->number_of_vectors = ret; + intr->using_vectors = true; + for (index = 0; index < intr->number_of_vectors; index++) + intr->vector_list[index].irq = msix_entries + [index].vector; + netif_info(adapter, ifup, adapter->netdev, + "using MSIX interrupts, number of vectors = %d\n", + intr->number_of_vectors); + } + + /* If MSIX failed try to setup using MSI interrupts */ + if (!intr->number_of_vectors) { + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) { + if (!pci_enable_msi(adapter->pdev)) { + intr->flags |= INTR_FLAG_MSI_ENABLED; + intr->number_of_vectors = 1; + intr->using_vectors = true; + intr->vector_list[0].irq = + adapter->pdev->irq; + netif_info(adapter, ifup, adapter->netdev, + "using MSI interrupts, number of vectors = %d\n", + intr->number_of_vectors); + } + } + } + + /* If MSIX, and MSI failed, setup using legacy interrupt */ + if (!intr->number_of_vectors) { + intr->number_of_vectors = 1; + intr->using_vectors = false; + intr->vector_list[0].irq = intr->irq; + netif_info(adapter, ifup, adapter->netdev, + "using legacy interrupts\n"); + } + + /* At this point we must have at least one irq */ + lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0xFFFFFFFF); + + /* map all interrupts to vector 0 */ + lan743x_csr_write(adapter, INT_VEC_MAP0, 0x00000000); + lan743x_csr_write(adapter, INT_VEC_MAP1, 0x00000000); + lan743x_csr_write(adapter, INT_VEC_MAP2, 0x00000000); + flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ | + LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR; + + if (intr->using_vectors) { + flags |= LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR | + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET; + } else { + flags |= LAN743X_VECTOR_FLAG_MASTER_ENABLE_CLEAR | + LAN743X_VECTOR_FLAG_MASTER_ENABLE_SET | + LAN743X_VECTOR_FLAG_IRQ_SHARED; + } + + if (adapter->csr.flags & LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) { + flags &= ~LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ; + flags &= ~LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C; + flags &= ~LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR; + flags &= ~LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK; + flags |= LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C; + flags |= LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C; + } + + ret = lan743x_intr_register_isr(adapter, 0, flags, + INT_BIT_ALL_RX_ | INT_BIT_ALL_TX_ | + INT_BIT_ALL_OTHER_, + lan743x_intr_shared_isr, adapter); + if (ret) + goto clean_up; + intr->flags |= INTR_FLAG_IRQ_REQUESTED(0); + + if (intr->using_vectors) + lan743x_csr_write(adapter, INT_VEC_EN_SET, + INT_VEC_EN_(0)); + + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) { + lan743x_csr_write(adapter, INT_MOD_CFG0, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG1, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG2, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG3, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG4, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG5, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG6, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_CFG7, LAN743X_INT_MOD); + lan743x_csr_write(adapter, INT_MOD_MAP0, 0x00005432); + lan743x_csr_write(adapter, INT_MOD_MAP1, 0x00000001); + lan743x_csr_write(adapter, INT_MOD_MAP2, 0x00FFFFFF); + } + + /* enable interrupts */ + lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_MAS_); + ret = lan743x_intr_test_isr(adapter); + if (ret) + goto clean_up; + + if (intr->number_of_vectors > 1) { + int number_of_tx_vectors = intr->number_of_vectors - 1; + + if (number_of_tx_vectors > LAN743X_USED_TX_CHANNELS) + number_of_tx_vectors = LAN743X_USED_TX_CHANNELS; + flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ | + LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR | + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR | + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET; + + if (adapter->csr.flags & + LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) { + flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR | + LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR; + } + + for (index = 0; index < number_of_tx_vectors; index++) { + u32 int_bit = INT_BIT_DMA_TX_(index); + int vector = index + 1; + + /* map TX interrupt to vector */ + int_vec_map1 |= INT_VEC_MAP1_TX_VEC_(index, vector); + lan743x_csr_write(adapter, INT_VEC_MAP1, int_vec_map1); + + /* Remove TX interrupt from shared mask */ + intr->vector_list[0].int_mask &= ~int_bit; + ret = lan743x_intr_register_isr(adapter, vector, flags, + int_bit, lan743x_tx_isr, + &adapter->tx[index]); + if (ret) + goto clean_up; + intr->flags |= INTR_FLAG_IRQ_REQUESTED(vector); + if (!(flags & + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET)) + lan743x_csr_write(adapter, INT_VEC_EN_SET, + INT_VEC_EN_(vector)); + } + } + if ((intr->number_of_vectors - LAN743X_USED_TX_CHANNELS) > 1) { + int number_of_rx_vectors = intr->number_of_vectors - + LAN743X_USED_TX_CHANNELS - 1; + + if (number_of_rx_vectors > LAN743X_USED_RX_CHANNELS) + number_of_rx_vectors = LAN743X_USED_RX_CHANNELS; + + flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ | + LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR | + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR | + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET; + + if (adapter->csr.flags & + LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) { + flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR | + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET | + LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR | + LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR; + } + for (index = 0; index < number_of_rx_vectors; index++) { + int vector = index + 1 + LAN743X_USED_TX_CHANNELS; + u32 int_bit = INT_BIT_DMA_RX_(index); + + /* map RX interrupt to vector */ + int_vec_map0 |= INT_VEC_MAP0_RX_VEC_(index, vector); + lan743x_csr_write(adapter, INT_VEC_MAP0, int_vec_map0); + if (flags & + LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR) { + int_vec_en_auto_clr |= INT_VEC_EN_(vector); + lan743x_csr_write(adapter, INT_VEC_EN_AUTO_CLR, + int_vec_en_auto_clr); + } + + /* Remove RX interrupt from shared mask */ + intr->vector_list[0].int_mask &= ~int_bit; + ret = lan743x_intr_register_isr(adapter, vector, flags, + int_bit, lan743x_rx_isr, + &adapter->rx[index]); + if (ret) + goto clean_up; + intr->flags |= INTR_FLAG_IRQ_REQUESTED(vector); + + lan743x_csr_write(adapter, INT_VEC_EN_SET, + INT_VEC_EN_(vector)); + } + } + return 0; + +clean_up: + lan743x_intr_close(adapter); + return ret; +} + +static int lan743x_dp_write(struct lan743x_adapter *adapter, + u32 select, u32 addr, u32 length, u32 *buf) +{ + u32 dp_sel; + int i; + + if (lan743x_csr_wait_for_bit(adapter, DP_SEL, DP_SEL_DPRDY_, + 1, 40, 100, 100)) + return -EIO; + dp_sel = lan743x_csr_read(adapter, DP_SEL); + dp_sel &= ~DP_SEL_MASK_; + dp_sel |= select; + lan743x_csr_write(adapter, DP_SEL, dp_sel); + + for (i = 0; i < length; i++) { + lan743x_csr_write(adapter, DP_ADDR, addr + i); + lan743x_csr_write(adapter, DP_DATA_0, buf[i]); + lan743x_csr_write(adapter, DP_CMD, DP_CMD_WRITE_); + if (lan743x_csr_wait_for_bit(adapter, DP_SEL, DP_SEL_DPRDY_, + 1, 40, 100, 100)) + return -EIO; + } + + return 0; +} + +static u32 lan743x_mac_mii_access(u16 id, u16 index, int read) +{ + u32 ret; + + ret = (id << MAC_MII_ACC_PHY_ADDR_SHIFT_) & + MAC_MII_ACC_PHY_ADDR_MASK_; + ret |= (index << MAC_MII_ACC_MIIRINDA_SHIFT_) & + MAC_MII_ACC_MIIRINDA_MASK_; + + if (read) + ret |= MAC_MII_ACC_MII_READ_; + else + ret |= MAC_MII_ACC_MII_WRITE_; + ret |= MAC_MII_ACC_MII_BUSY_; + + return ret; +} + +static int lan743x_mac_mii_wait_till_not_busy(struct lan743x_adapter *adapter) +{ + u32 data; + + return readx_poll_timeout(LAN743X_CSR_READ_OP, MAC_MII_ACC, data, + !(data & MAC_MII_ACC_MII_BUSY_), 0, 1000000); +} + +static int lan743x_mdiobus_read(struct mii_bus *bus, int phy_id, int index) +{ + struct lan743x_adapter *adapter = bus->priv; + u32 val, mii_access; + int ret; + + /* comfirm MII not busy */ + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + + /* set the address, index & direction (read from PHY) */ + mii_access = lan743x_mac_mii_access(phy_id, index, MAC_MII_READ); + lan743x_csr_write(adapter, MAC_MII_ACC, mii_access); + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + + val = lan743x_csr_read(adapter, MAC_MII_DATA); + return (int)(val & 0xFFFF); +} + +static int lan743x_mdiobus_write(struct mii_bus *bus, + int phy_id, int index, u16 regval) +{ + struct lan743x_adapter *adapter = bus->priv; + u32 val, mii_access; + int ret; + + /* confirm MII not busy */ + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + if (ret < 0) + return ret; + val = (u32)regval; + lan743x_csr_write(adapter, MAC_MII_DATA, val); + + /* set the address, index & direction (write to PHY) */ + mii_access = lan743x_mac_mii_access(phy_id, index, MAC_MII_WRITE); + lan743x_csr_write(adapter, MAC_MII_ACC, mii_access); + ret = lan743x_mac_mii_wait_till_not_busy(adapter); + return ret; +} + +static void lan743x_mac_set_address(struct lan743x_adapter *adapter, + u8 *addr) +{ + u32 addr_lo, addr_hi; + + addr_lo = addr[0] | + addr[1] << 8 | + addr[2] << 16 | + addr[3] << 24; + addr_hi = addr[4] | + addr[5] << 8; + lan743x_csr_write(adapter, MAC_RX_ADDRL, addr_lo); + lan743x_csr_write(adapter, MAC_RX_ADDRH, addr_hi); + + ether_addr_copy(adapter->mac_address, addr); + netif_info(adapter, drv, adapter->netdev, + "MAC address set to %pM\n", addr); +} + +static int lan743x_mac_init(struct lan743x_adapter *adapter) +{ + bool mac_address_valid = true; + struct net_device *netdev; + u32 mac_addr_hi = 0; + u32 mac_addr_lo = 0; + u32 data; + + netdev = adapter->netdev; + + /* setup auto duplex, and speed detection */ + data = lan743x_csr_read(adapter, MAC_CR); + data |= MAC_CR_ADD_ | MAC_CR_ASD_; + data |= MAC_CR_CNTR_RST_; + lan743x_csr_write(adapter, MAC_CR, data); + + mac_addr_hi = lan743x_csr_read(adapter, MAC_RX_ADDRH); + mac_addr_lo = lan743x_csr_read(adapter, MAC_RX_ADDRL); + adapter->mac_address[0] = mac_addr_lo & 0xFF; + adapter->mac_address[1] = (mac_addr_lo >> 8) & 0xFF; + adapter->mac_address[2] = (mac_addr_lo >> 16) & 0xFF; + adapter->mac_address[3] = (mac_addr_lo >> 24) & 0xFF; + adapter->mac_address[4] = mac_addr_hi & 0xFF; + adapter->mac_address[5] = (mac_addr_hi >> 8) & 0xFF; + + if (((mac_addr_hi & 0x0000FFFF) == 0x0000FFFF) && + mac_addr_lo == 0xFFFFFFFF) { + mac_address_valid = false; + } else if (!is_valid_ether_addr(adapter->mac_address)) { + mac_address_valid = false; + } + + if (!mac_address_valid) + eth_random_addr(adapter->mac_address); + lan743x_mac_set_address(adapter, adapter->mac_address); + ether_addr_copy(netdev->dev_addr, adapter->mac_address); + return 0; +} + +static int lan743x_mac_open(struct lan743x_adapter *adapter) +{ + int ret = 0; + u32 temp; + + temp = lan743x_csr_read(adapter, MAC_RX); + lan743x_csr_write(adapter, MAC_RX, temp | MAC_RX_RXEN_); + temp = lan743x_csr_read(adapter, MAC_TX); + lan743x_csr_write(adapter, MAC_TX, temp | MAC_TX_TXEN_); + return ret; +} + +static void lan743x_mac_close(struct lan743x_adapter *adapter) +{ + u32 temp; + + temp = lan743x_csr_read(adapter, MAC_TX); + temp &= ~MAC_TX_TXEN_; + lan743x_csr_write(adapter, MAC_TX, temp); + lan743x_csr_wait_for_bit(adapter, MAC_TX, MAC_TX_TXD_, + 1, 1000, 20000, 100); + + temp = lan743x_csr_read(adapter, MAC_RX); + temp &= ~MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, temp); + lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_, + 1, 1000, 20000, 100); +} + +static void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, + bool tx_enable, bool rx_enable) +{ + u32 flow_setting = 0; + + /* set maximum pause time because when fifo space frees + * up a zero value pause frame will be sent to release the pause + */ + flow_setting = MAC_FLOW_CR_FCPT_MASK_; + if (tx_enable) + flow_setting |= MAC_FLOW_CR_TX_FCEN_; + if (rx_enable) + flow_setting |= MAC_FLOW_CR_RX_FCEN_; + lan743x_csr_write(adapter, MAC_FLOW, flow_setting); +} + +static int lan743x_mac_set_mtu(struct lan743x_adapter *adapter, int new_mtu) +{ + int enabled = 0; + u32 mac_rx = 0; + + mac_rx = lan743x_csr_read(adapter, MAC_RX); + if (mac_rx & MAC_RX_RXEN_) { + enabled = 1; + if (mac_rx & MAC_RX_RXD_) { + lan743x_csr_write(adapter, MAC_RX, mac_rx); + mac_rx &= ~MAC_RX_RXD_; + } + mac_rx &= ~MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_, + 1, 1000, 20000, 100); + lan743x_csr_write(adapter, MAC_RX, mac_rx | MAC_RX_RXD_); + } + + mac_rx &= ~(MAC_RX_MAX_SIZE_MASK_); + mac_rx |= (((new_mtu + ETH_HLEN + 4) << MAC_RX_MAX_SIZE_SHIFT_) & + MAC_RX_MAX_SIZE_MASK_); + lan743x_csr_write(adapter, MAC_RX, mac_rx); + + if (enabled) { + mac_rx |= MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + } + return 0; +} + +/* PHY */ +static int lan743x_phy_reset(struct lan743x_adapter *adapter) +{ + u32 data; + + /* Only called with in probe, and before mdiobus_register */ + + data = lan743x_csr_read(adapter, PMT_CTL); + data |= PMT_CTL_ETH_PHY_RST_; + lan743x_csr_write(adapter, PMT_CTL, data); + + return readx_poll_timeout(LAN743X_CSR_READ_OP, PMT_CTL, data, + (!(data & PMT_CTL_ETH_PHY_RST_) && + (data & PMT_CTL_READY_)), + 50000, 1000000); +} + +static void lan743x_phy_update_flowcontrol(struct lan743x_adapter *adapter, + u8 duplex, u16 local_adv, + u16 remote_adv) +{ + struct lan743x_phy *phy = &adapter->phy; + u8 cap; + + if (phy->fc_autoneg) + cap = mii_resolve_flowctrl_fdx(local_adv, remote_adv); + else + cap = phy->fc_request_control; + + lan743x_mac_flow_ctrl_set_enables(adapter, + cap & FLOW_CTRL_TX, + cap & FLOW_CTRL_RX); +} + +static int lan743x_phy_init(struct lan743x_adapter *adapter) +{ + return lan743x_phy_reset(adapter); +} + +static void lan743x_phy_link_status_change(struct net_device *netdev) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + struct phy_device *phydev = netdev->phydev; + + phy_print_status(phydev); + if (phydev->state == PHY_RUNNING) { + struct ethtool_link_ksettings ksettings; + int remote_advertisement = 0; + int local_advertisement = 0; + + memset(&ksettings, 0, sizeof(ksettings)); + phy_ethtool_get_link_ksettings(netdev, &ksettings); + local_advertisement = + ethtool_adv_to_mii_adv_t(phydev->advertising); + remote_advertisement = + ethtool_adv_to_mii_adv_t(phydev->lp_advertising); + + lan743x_phy_update_flowcontrol(adapter, + ksettings.base.duplex, + local_advertisement, + remote_advertisement); + lan743x_ptp_update_latency(adapter, ksettings.base.speed); + } +} + +static void lan743x_phy_close(struct lan743x_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + phy_stop(netdev->phydev); + phy_disconnect(netdev->phydev); + netdev->phydev = NULL; +} + +static int lan743x_phy_open(struct lan743x_adapter *adapter) +{ + struct lan743x_phy *phy = &adapter->phy; + struct phy_device *phydev; + struct net_device *netdev; + int ret = -EIO; + u32 mii_adv; + + netdev = adapter->netdev; + phydev = phy_find_first(adapter->mdiobus); + if (!phydev) + goto return_error; + + ret = phy_connect_direct(netdev, phydev, + lan743x_phy_link_status_change, + PHY_INTERFACE_MODE_GMII); + if (ret) + goto return_error; + + /* MAC doesn't support 1000T Half */ + phydev->supported &= ~SUPPORTED_1000baseT_Half; + + /* support both flow controls */ + phy->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX); + phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); + mii_adv = (u32)mii_advertise_flowctrl(phy->fc_request_control); + phydev->advertising |= mii_adv_to_ethtool_adv_t(mii_adv); + phy->fc_autoneg = phydev->autoneg; + + phy_start(phydev); + phy_start_aneg(phydev); + return 0; + +return_error: + return ret; +} + +static void lan743x_rfe_open(struct lan743x_adapter *adapter) +{ + lan743x_csr_write(adapter, RFE_RSS_CFG, + RFE_RSS_CFG_UDP_IPV6_EX_ | + RFE_RSS_CFG_TCP_IPV6_EX_ | + RFE_RSS_CFG_IPV6_EX_ | + RFE_RSS_CFG_UDP_IPV6_ | + RFE_RSS_CFG_TCP_IPV6_ | + RFE_RSS_CFG_IPV6_ | + RFE_RSS_CFG_UDP_IPV4_ | + RFE_RSS_CFG_TCP_IPV4_ | + RFE_RSS_CFG_IPV4_ | + RFE_RSS_CFG_VALID_HASH_BITS_ | + RFE_RSS_CFG_RSS_QUEUE_ENABLE_ | + RFE_RSS_CFG_RSS_HASH_STORE_ | + RFE_RSS_CFG_RSS_ENABLE_); +} + +static void lan743x_rfe_update_mac_address(struct lan743x_adapter *adapter) +{ + u8 *mac_addr; + u32 mac_addr_hi = 0; + u32 mac_addr_lo = 0; + + /* Add mac address to perfect Filter */ + mac_addr = adapter->mac_address; + mac_addr_lo = ((((u32)(mac_addr[0])) << 0) | + (((u32)(mac_addr[1])) << 8) | + (((u32)(mac_addr[2])) << 16) | + (((u32)(mac_addr[3])) << 24)); + mac_addr_hi = ((((u32)(mac_addr[4])) << 0) | + (((u32)(mac_addr[5])) << 8)); + + lan743x_csr_write(adapter, RFE_ADDR_FILT_LO(0), mac_addr_lo); + lan743x_csr_write(adapter, RFE_ADDR_FILT_HI(0), + mac_addr_hi | RFE_ADDR_FILT_HI_VALID_); +} + +static void lan743x_rfe_set_multicast(struct lan743x_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + u32 hash_table[DP_SEL_VHF_HASH_LEN]; + u32 rfctl; + u32 data; + + rfctl = lan743x_csr_read(adapter, RFE_CTL); + rfctl &= ~(RFE_CTL_AU_ | RFE_CTL_AM_ | + RFE_CTL_DA_PERFECT_ | RFE_CTL_MCAST_HASH_); + rfctl |= RFE_CTL_AB_; + if (netdev->flags & IFF_PROMISC) { + rfctl |= RFE_CTL_AM_ | RFE_CTL_AU_; + } else { + if (netdev->flags & IFF_ALLMULTI) + rfctl |= RFE_CTL_AM_; + } + + memset(hash_table, 0, DP_SEL_VHF_HASH_LEN * sizeof(u32)); + if (netdev_mc_count(netdev)) { + struct netdev_hw_addr *ha; + int i; + + rfctl |= RFE_CTL_DA_PERFECT_; + i = 1; + netdev_for_each_mc_addr(ha, netdev) { + /* set first 32 into Perfect Filter */ + if (i < 33) { + lan743x_csr_write(adapter, + RFE_ADDR_FILT_HI(i), 0); + data = ha->addr[3]; + data = ha->addr[2] | (data << 8); + data = ha->addr[1] | (data << 8); + data = ha->addr[0] | (data << 8); + lan743x_csr_write(adapter, + RFE_ADDR_FILT_LO(i), data); + data = ha->addr[5]; + data = ha->addr[4] | (data << 8); + data |= RFE_ADDR_FILT_HI_VALID_; + lan743x_csr_write(adapter, + RFE_ADDR_FILT_HI(i), data); + } else { + u32 bitnum = (ether_crc(ETH_ALEN, ha->addr) >> + 23) & 0x1FF; + hash_table[bitnum / 32] |= (1 << (bitnum % 32)); + rfctl |= RFE_CTL_MCAST_HASH_; + } + i++; + } + } + + lan743x_dp_write(adapter, DP_SEL_RFE_RAM, + DP_SEL_VHF_VLAN_LEN, + DP_SEL_VHF_HASH_LEN, hash_table); + lan743x_csr_write(adapter, RFE_CTL, rfctl); +} + +static int lan743x_dmac_init(struct lan743x_adapter *adapter) +{ + u32 data = 0; + + lan743x_csr_write(adapter, DMAC_CMD, DMAC_CMD_SWR_); + lan743x_csr_wait_for_bit(adapter, DMAC_CMD, DMAC_CMD_SWR_, + 0, 1000, 20000, 100); + switch (DEFAULT_DMA_DESCRIPTOR_SPACING) { + case DMA_DESCRIPTOR_SPACING_16: + data = DMAC_CFG_MAX_DSPACE_16_; + break; + case DMA_DESCRIPTOR_SPACING_32: + data = DMAC_CFG_MAX_DSPACE_32_; + break; + case DMA_DESCRIPTOR_SPACING_64: + data = DMAC_CFG_MAX_DSPACE_64_; + break; + case DMA_DESCRIPTOR_SPACING_128: + data = DMAC_CFG_MAX_DSPACE_128_; + break; + default: + return -EPERM; + } + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) + data |= DMAC_CFG_COAL_EN_; + data |= DMAC_CFG_CH_ARB_SEL_RX_HIGH_; + data |= DMAC_CFG_MAX_READ_REQ_SET_(6); + lan743x_csr_write(adapter, DMAC_CFG, data); + data = DMAC_COAL_CFG_TIMER_LIMIT_SET_(1); + data |= DMAC_COAL_CFG_TIMER_TX_START_; + data |= DMAC_COAL_CFG_FLUSH_INTS_; + data |= DMAC_COAL_CFG_INT_EXIT_COAL_; + data |= DMAC_COAL_CFG_CSR_EXIT_COAL_; + data |= DMAC_COAL_CFG_TX_THRES_SET_(0x0A); + data |= DMAC_COAL_CFG_RX_THRES_SET_(0x0C); + lan743x_csr_write(adapter, DMAC_COAL_CFG, data); + data = DMAC_OBFF_TX_THRES_SET_(0x08); + data |= DMAC_OBFF_RX_THRES_SET_(0x0A); + lan743x_csr_write(adapter, DMAC_OBFF_CFG, data); + return 0; +} + +static int lan743x_dmac_tx_get_state(struct lan743x_adapter *adapter, + int tx_channel) +{ + u32 dmac_cmd = 0; + + dmac_cmd = lan743x_csr_read(adapter, DMAC_CMD); + return DMAC_CHANNEL_STATE_SET((dmac_cmd & + DMAC_CMD_START_T_(tx_channel)), + (dmac_cmd & + DMAC_CMD_STOP_T_(tx_channel))); +} + +static int lan743x_dmac_tx_wait_till_stopped(struct lan743x_adapter *adapter, + int tx_channel) +{ + int timeout = 100; + int result = 0; + + while (timeout && + ((result = lan743x_dmac_tx_get_state(adapter, tx_channel)) == + DMAC_CHANNEL_STATE_STOP_PENDING)) { + usleep_range(1000, 20000); + timeout--; + } + if (result == DMAC_CHANNEL_STATE_STOP_PENDING) + result = -ENODEV; + return result; +} + +static int lan743x_dmac_rx_get_state(struct lan743x_adapter *adapter, + int rx_channel) +{ + u32 dmac_cmd = 0; + + dmac_cmd = lan743x_csr_read(adapter, DMAC_CMD); + return DMAC_CHANNEL_STATE_SET((dmac_cmd & + DMAC_CMD_START_R_(rx_channel)), + (dmac_cmd & + DMAC_CMD_STOP_R_(rx_channel))); +} + +static int lan743x_dmac_rx_wait_till_stopped(struct lan743x_adapter *adapter, + int rx_channel) +{ + int timeout = 100; + int result = 0; + + while (timeout && + ((result = lan743x_dmac_rx_get_state(adapter, rx_channel)) == + DMAC_CHANNEL_STATE_STOP_PENDING)) { + usleep_range(1000, 20000); + timeout--; + } + if (result == DMAC_CHANNEL_STATE_STOP_PENDING) + result = -ENODEV; + return result; +} + +static void lan743x_tx_release_desc(struct lan743x_tx *tx, + int descriptor_index, bool cleanup) +{ + struct lan743x_tx_buffer_info *buffer_info = NULL; + struct lan743x_tx_descriptor *descriptor = NULL; + u32 descriptor_type = 0; + bool ignore_sync; + + descriptor = &tx->ring_cpu_ptr[descriptor_index]; + buffer_info = &tx->buffer_info[descriptor_index]; + if (!(buffer_info->flags & TX_BUFFER_INFO_FLAG_ACTIVE)) + goto done; + + descriptor_type = (descriptor->data0) & + TX_DESC_DATA0_DTYPE_MASK_; + if (descriptor_type == TX_DESC_DATA0_DTYPE_DATA_) + goto clean_up_data_descriptor; + else + goto clear_active; + +clean_up_data_descriptor: + if (buffer_info->dma_ptr) { + if (buffer_info->flags & + TX_BUFFER_INFO_FLAG_SKB_FRAGMENT) { + dma_unmap_page(&tx->adapter->pdev->dev, + buffer_info->dma_ptr, + buffer_info->buffer_length, + DMA_TO_DEVICE); + } else { + dma_unmap_single(&tx->adapter->pdev->dev, + buffer_info->dma_ptr, + buffer_info->buffer_length, + DMA_TO_DEVICE); + } + buffer_info->dma_ptr = 0; + buffer_info->buffer_length = 0; + } + if (!buffer_info->skb) + goto clear_active; + + if (!(buffer_info->flags & TX_BUFFER_INFO_FLAG_TIMESTAMP_REQUESTED)) { + dev_kfree_skb_any(buffer_info->skb); + goto clear_skb; + } + + if (cleanup) { + lan743x_ptp_unrequest_tx_timestamp(tx->adapter); + dev_kfree_skb_any(buffer_info->skb); + } else { + ignore_sync = (buffer_info->flags & + TX_BUFFER_INFO_FLAG_IGNORE_SYNC) != 0; + lan743x_ptp_tx_timestamp_skb(tx->adapter, + buffer_info->skb, ignore_sync); + } + +clear_skb: + buffer_info->skb = NULL; + +clear_active: + buffer_info->flags &= ~TX_BUFFER_INFO_FLAG_ACTIVE; + +done: + memset(buffer_info, 0, sizeof(*buffer_info)); + memset(descriptor, 0, sizeof(*descriptor)); +} + +static int lan743x_tx_next_index(struct lan743x_tx *tx, int index) +{ + return ((++index) % tx->ring_size); +} + +static void lan743x_tx_release_completed_descriptors(struct lan743x_tx *tx) +{ + while ((*tx->head_cpu_ptr) != (tx->last_head)) { + lan743x_tx_release_desc(tx, tx->last_head, false); + tx->last_head = lan743x_tx_next_index(tx, tx->last_head); + } +} + +static void lan743x_tx_release_all_descriptors(struct lan743x_tx *tx) +{ + u32 original_head = 0; + + original_head = tx->last_head; + do { + lan743x_tx_release_desc(tx, tx->last_head, true); + tx->last_head = lan743x_tx_next_index(tx, tx->last_head); + } while (tx->last_head != original_head); + memset(tx->ring_cpu_ptr, 0, + sizeof(*tx->ring_cpu_ptr) * (tx->ring_size)); + memset(tx->buffer_info, 0, + sizeof(*tx->buffer_info) * (tx->ring_size)); +} + +static int lan743x_tx_get_desc_cnt(struct lan743x_tx *tx, + struct sk_buff *skb) +{ + int result = 1; /* 1 for the main skb buffer */ + int nr_frags = 0; + + if (skb_is_gso(skb)) + result++; /* requires an extension descriptor */ + nr_frags = skb_shinfo(skb)->nr_frags; + result += nr_frags; /* 1 for each fragment buffer */ + return result; +} + +static int lan743x_tx_get_avail_desc(struct lan743x_tx *tx) +{ + int last_head = tx->last_head; + int last_tail = tx->last_tail; + + if (last_tail >= last_head) + return tx->ring_size - last_tail + last_head - 1; + else + return last_head - last_tail - 1; +} + +void lan743x_tx_set_timestamping_mode(struct lan743x_tx *tx, + bool enable_timestamping, + bool enable_onestep_sync) +{ + if (enable_timestamping) + tx->ts_flags |= TX_TS_FLAG_TIMESTAMPING_ENABLED; + else + tx->ts_flags &= ~TX_TS_FLAG_TIMESTAMPING_ENABLED; + if (enable_onestep_sync) + tx->ts_flags |= TX_TS_FLAG_ONE_STEP_SYNC; + else + tx->ts_flags &= ~TX_TS_FLAG_ONE_STEP_SYNC; +} + +static int lan743x_tx_frame_start(struct lan743x_tx *tx, + unsigned char *first_buffer, + unsigned int first_buffer_length, + unsigned int frame_length, + bool time_stamp, + bool check_sum) +{ + /* called only from within lan743x_tx_xmit_frame. + * assuming tx->ring_lock has already been acquired. + */ + struct lan743x_tx_descriptor *tx_descriptor = NULL; + struct lan743x_tx_buffer_info *buffer_info = NULL; + struct lan743x_adapter *adapter = tx->adapter; + struct device *dev = &adapter->pdev->dev; + dma_addr_t dma_ptr; + + tx->frame_flags |= TX_FRAME_FLAG_IN_PROGRESS; + tx->frame_first = tx->last_tail; + tx->frame_tail = tx->frame_first; + + tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail]; + buffer_info = &tx->buffer_info[tx->frame_tail]; + dma_ptr = dma_map_single(dev, first_buffer, first_buffer_length, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma_ptr)) + return -ENOMEM; + + tx_descriptor->data1 = DMA_ADDR_LOW32(dma_ptr); + tx_descriptor->data2 = DMA_ADDR_HIGH32(dma_ptr); + tx_descriptor->data3 = (frame_length << 16) & + TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_; + + buffer_info->skb = NULL; + buffer_info->dma_ptr = dma_ptr; + buffer_info->buffer_length = first_buffer_length; + buffer_info->flags |= TX_BUFFER_INFO_FLAG_ACTIVE; + + tx->frame_data0 = (first_buffer_length & + TX_DESC_DATA0_BUF_LENGTH_MASK_) | + TX_DESC_DATA0_DTYPE_DATA_ | + TX_DESC_DATA0_FS_ | + TX_DESC_DATA0_FCS_; + if (time_stamp) + tx->frame_data0 |= TX_DESC_DATA0_TSE_; + + if (check_sum) + tx->frame_data0 |= TX_DESC_DATA0_ICE_ | + TX_DESC_DATA0_IPE_ | + TX_DESC_DATA0_TPE_; + + /* data0 will be programmed in one of other frame assembler functions */ + return 0; +} + +static void lan743x_tx_frame_add_lso(struct lan743x_tx *tx, + unsigned int frame_length, + int nr_frags) +{ + /* called only from within lan743x_tx_xmit_frame. + * assuming tx->ring_lock has already been acquired. + */ + struct lan743x_tx_descriptor *tx_descriptor = NULL; + struct lan743x_tx_buffer_info *buffer_info = NULL; + + /* wrap up previous descriptor */ + tx->frame_data0 |= TX_DESC_DATA0_EXT_; + if (nr_frags <= 0) { + tx->frame_data0 |= TX_DESC_DATA0_LS_; + tx->frame_data0 |= TX_DESC_DATA0_IOC_; + } + tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail]; + tx_descriptor->data0 = tx->frame_data0; + + /* move to next descriptor */ + tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail); + tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail]; + buffer_info = &tx->buffer_info[tx->frame_tail]; + + /* add extension descriptor */ + tx_descriptor->data1 = 0; + tx_descriptor->data2 = 0; + tx_descriptor->data3 = 0; + + buffer_info->skb = NULL; + buffer_info->dma_ptr = 0; + buffer_info->buffer_length = 0; + buffer_info->flags |= TX_BUFFER_INFO_FLAG_ACTIVE; + + tx->frame_data0 = (frame_length & TX_DESC_DATA0_EXT_PAY_LENGTH_MASK_) | + TX_DESC_DATA0_DTYPE_EXT_ | + TX_DESC_DATA0_EXT_LSO_; + + /* data0 will be programmed in one of other frame assembler functions */ +} + +static int lan743x_tx_frame_add_fragment(struct lan743x_tx *tx, + const struct skb_frag_struct *fragment, + unsigned int frame_length) +{ + /* called only from within lan743x_tx_xmit_frame + * assuming tx->ring_lock has already been acquired + */ + struct lan743x_tx_descriptor *tx_descriptor = NULL; + struct lan743x_tx_buffer_info *buffer_info = NULL; + struct lan743x_adapter *adapter = tx->adapter; + struct device *dev = &adapter->pdev->dev; + unsigned int fragment_length = 0; + dma_addr_t dma_ptr; + + fragment_length = skb_frag_size(fragment); + if (!fragment_length) + return 0; + + /* wrap up previous descriptor */ + tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail]; + tx_descriptor->data0 = tx->frame_data0; + + /* move to next descriptor */ + tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail); + tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail]; + buffer_info = &tx->buffer_info[tx->frame_tail]; + dma_ptr = skb_frag_dma_map(dev, fragment, + 0, fragment_length, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma_ptr)) { + int desc_index; + + /* cleanup all previously setup descriptors */ + desc_index = tx->frame_first; + while (desc_index != tx->frame_tail) { + lan743x_tx_release_desc(tx, desc_index, true); + desc_index = lan743x_tx_next_index(tx, desc_index); + } + dma_wmb(); + tx->frame_flags &= ~TX_FRAME_FLAG_IN_PROGRESS; + tx->frame_first = 0; + tx->frame_data0 = 0; + tx->frame_tail = 0; + return -ENOMEM; + } + + tx_descriptor->data1 = DMA_ADDR_LOW32(dma_ptr); + tx_descriptor->data2 = DMA_ADDR_HIGH32(dma_ptr); + tx_descriptor->data3 = (frame_length << 16) & + TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_; + + buffer_info->skb = NULL; + buffer_info->dma_ptr = dma_ptr; + buffer_info->buffer_length = fragment_length; + buffer_info->flags |= TX_BUFFER_INFO_FLAG_ACTIVE; + buffer_info->flags |= TX_BUFFER_INFO_FLAG_SKB_FRAGMENT; + + tx->frame_data0 = (fragment_length & TX_DESC_DATA0_BUF_LENGTH_MASK_) | + TX_DESC_DATA0_DTYPE_DATA_ | + TX_DESC_DATA0_FCS_; + + /* data0 will be programmed in one of other frame assembler functions */ + return 0; +} + +static void lan743x_tx_frame_end(struct lan743x_tx *tx, + struct sk_buff *skb, + bool time_stamp, + bool ignore_sync) +{ + /* called only from within lan743x_tx_xmit_frame + * assuming tx->ring_lock has already been acquired + */ + struct lan743x_tx_descriptor *tx_descriptor = NULL; + struct lan743x_tx_buffer_info *buffer_info = NULL; + struct lan743x_adapter *adapter = tx->adapter; + u32 tx_tail_flags = 0; + + /* wrap up previous descriptor */ + if ((tx->frame_data0 & TX_DESC_DATA0_DTYPE_MASK_) == + TX_DESC_DATA0_DTYPE_DATA_) { + tx->frame_data0 |= TX_DESC_DATA0_LS_; + tx->frame_data0 |= TX_DESC_DATA0_IOC_; + } + + tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail]; + buffer_info = &tx->buffer_info[tx->frame_tail]; + buffer_info->skb = skb; + if (time_stamp) + buffer_info->flags |= TX_BUFFER_INFO_FLAG_TIMESTAMP_REQUESTED; + if (ignore_sync) + buffer_info->flags |= TX_BUFFER_INFO_FLAG_IGNORE_SYNC; + + tx_descriptor->data0 = tx->frame_data0; + tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail); + tx->last_tail = tx->frame_tail; + + dma_wmb(); + + if (tx->vector_flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET) + tx_tail_flags |= TX_TAIL_SET_TOP_INT_VEC_EN_; + if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET) + tx_tail_flags |= TX_TAIL_SET_DMAC_INT_EN_ | + TX_TAIL_SET_TOP_INT_EN_; + + lan743x_csr_write(adapter, TX_TAIL(tx->channel_number), + tx_tail_flags | tx->frame_tail); + tx->frame_flags &= ~TX_FRAME_FLAG_IN_PROGRESS; +} + +static netdev_tx_t lan743x_tx_xmit_frame(struct lan743x_tx *tx, + struct sk_buff *skb) +{ + int required_number_of_descriptors = 0; + unsigned int start_frame_length = 0; + unsigned int frame_length = 0; + unsigned int head_length = 0; + unsigned long irq_flags = 0; + bool do_timestamp = false; + bool ignore_sync = false; + int nr_frags = 0; + bool gso = false; + int j; + + required_number_of_descriptors = lan743x_tx_get_desc_cnt(tx, skb); + + spin_lock_irqsave(&tx->ring_lock, irq_flags); + if (required_number_of_descriptors > + lan743x_tx_get_avail_desc(tx)) { + if (required_number_of_descriptors > (tx->ring_size - 1)) { + dev_kfree_skb_irq(skb); + } else { + /* save to overflow buffer */ + tx->overflow_skb = skb; + netif_stop_queue(tx->adapter->netdev); + } + goto unlock; + } + + /* space available, transmit skb */ + if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + (tx->ts_flags & TX_TS_FLAG_TIMESTAMPING_ENABLED) && + (lan743x_ptp_request_tx_timestamp(tx->adapter))) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + do_timestamp = true; + if (tx->ts_flags & TX_TS_FLAG_ONE_STEP_SYNC) + ignore_sync = true; + } + head_length = skb_headlen(skb); + frame_length = skb_pagelen(skb); + nr_frags = skb_shinfo(skb)->nr_frags; + start_frame_length = frame_length; + gso = skb_is_gso(skb); + if (gso) { + start_frame_length = max(skb_shinfo(skb)->gso_size, + (unsigned short)8); + } + + if (lan743x_tx_frame_start(tx, + skb->data, head_length, + start_frame_length, + do_timestamp, + skb->ip_summed == CHECKSUM_PARTIAL)) { + dev_kfree_skb_irq(skb); + goto unlock; + } + + if (gso) + lan743x_tx_frame_add_lso(tx, frame_length, nr_frags); + + if (nr_frags <= 0) + goto finish; + + for (j = 0; j < nr_frags; j++) { + const struct skb_frag_struct *frag; + + frag = &(skb_shinfo(skb)->frags[j]); + if (lan743x_tx_frame_add_fragment(tx, frag, frame_length)) { + /* upon error no need to call + * lan743x_tx_frame_end + * frame assembler clean up was performed inside + * lan743x_tx_frame_add_fragment + */ + dev_kfree_skb_irq(skb); + goto unlock; + } + } + +finish: + lan743x_tx_frame_end(tx, skb, do_timestamp, ignore_sync); + +unlock: + spin_unlock_irqrestore(&tx->ring_lock, irq_flags); + return NETDEV_TX_OK; +} + +static int lan743x_tx_napi_poll(struct napi_struct *napi, int weight) +{ + struct lan743x_tx *tx = container_of(napi, struct lan743x_tx, napi); + struct lan743x_adapter *adapter = tx->adapter; + bool start_transmitter = false; + unsigned long irq_flags = 0; + u32 ioc_bit = 0; + + ioc_bit = DMAC_INT_BIT_TX_IOC_(tx->channel_number); + lan743x_csr_read(adapter, DMAC_INT_STS); + if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C) + lan743x_csr_write(adapter, DMAC_INT_STS, ioc_bit); + spin_lock_irqsave(&tx->ring_lock, irq_flags); + + /* clean up tx ring */ + lan743x_tx_release_completed_descriptors(tx); + if (netif_queue_stopped(adapter->netdev)) { + if (tx->overflow_skb) { + if (lan743x_tx_get_desc_cnt(tx, tx->overflow_skb) <= + lan743x_tx_get_avail_desc(tx)) + start_transmitter = true; + } else { + netif_wake_queue(adapter->netdev); + } + } + spin_unlock_irqrestore(&tx->ring_lock, irq_flags); + + if (start_transmitter) { + /* space is now available, transmit overflow skb */ + lan743x_tx_xmit_frame(tx, tx->overflow_skb); + tx->overflow_skb = NULL; + netif_wake_queue(adapter->netdev); + } + + if (!napi_complete(napi)) + goto done; + + /* enable isr */ + lan743x_csr_write(adapter, INT_EN_SET, + INT_BIT_DMA_TX_(tx->channel_number)); + lan743x_csr_read(adapter, INT_STS); + +done: + return 0; +} + +static void lan743x_tx_ring_cleanup(struct lan743x_tx *tx) +{ + if (tx->head_cpu_ptr) { + pci_free_consistent(tx->adapter->pdev, + sizeof(*tx->head_cpu_ptr), + (void *)(tx->head_cpu_ptr), + tx->head_dma_ptr); + tx->head_cpu_ptr = NULL; + tx->head_dma_ptr = 0; + } + kfree(tx->buffer_info); + tx->buffer_info = NULL; + + if (tx->ring_cpu_ptr) { + pci_free_consistent(tx->adapter->pdev, + tx->ring_allocation_size, + tx->ring_cpu_ptr, + tx->ring_dma_ptr); + tx->ring_allocation_size = 0; + tx->ring_cpu_ptr = NULL; + tx->ring_dma_ptr = 0; + } + tx->ring_size = 0; +} + +static int lan743x_tx_ring_init(struct lan743x_tx *tx) +{ + size_t ring_allocation_size = 0; + void *cpu_ptr = NULL; + dma_addr_t dma_ptr; + int ret = -ENOMEM; + + tx->ring_size = LAN743X_TX_RING_SIZE; + if (tx->ring_size & ~TX_CFG_B_TX_RING_LEN_MASK_) { + ret = -EINVAL; + goto cleanup; + } + if (dma_set_mask_and_coherent(&tx->adapter->pdev->dev, + DMA_BIT_MASK(64))) { + if (dma_set_mask_and_coherent(&tx->adapter->pdev->dev, + DMA_BIT_MASK(32))) { + dev_warn(&tx->adapter->pdev->dev, + "lan743x_: No suitable DMA available\n"); + ret = -ENOMEM; + goto cleanup; + } + } + ring_allocation_size = ALIGN(tx->ring_size * + sizeof(struct lan743x_tx_descriptor), + PAGE_SIZE); + dma_ptr = 0; + cpu_ptr = pci_zalloc_consistent(tx->adapter->pdev, + ring_allocation_size, &dma_ptr); + if (!cpu_ptr) { + ret = -ENOMEM; + goto cleanup; + } + + tx->ring_allocation_size = ring_allocation_size; + tx->ring_cpu_ptr = (struct lan743x_tx_descriptor *)cpu_ptr; + tx->ring_dma_ptr = dma_ptr; + + cpu_ptr = kcalloc(tx->ring_size, sizeof(*tx->buffer_info), GFP_KERNEL); + if (!cpu_ptr) { + ret = -ENOMEM; + goto cleanup; + } + tx->buffer_info = (struct lan743x_tx_buffer_info *)cpu_ptr; + dma_ptr = 0; + cpu_ptr = pci_zalloc_consistent(tx->adapter->pdev, + sizeof(*tx->head_cpu_ptr), &dma_ptr); + if (!cpu_ptr) { + ret = -ENOMEM; + goto cleanup; + } + + tx->head_cpu_ptr = cpu_ptr; + tx->head_dma_ptr = dma_ptr; + if (tx->head_dma_ptr & 0x3) { + ret = -ENOMEM; + goto cleanup; + } + + return 0; + +cleanup: + lan743x_tx_ring_cleanup(tx); + return ret; +} + +static void lan743x_tx_close(struct lan743x_tx *tx) +{ + struct lan743x_adapter *adapter = tx->adapter; + + lan743x_csr_write(adapter, + DMAC_CMD, + DMAC_CMD_STOP_T_(tx->channel_number)); + lan743x_dmac_tx_wait_till_stopped(adapter, tx->channel_number); + + lan743x_csr_write(adapter, + DMAC_INT_EN_CLR, + DMAC_INT_BIT_TX_IOC_(tx->channel_number)); + lan743x_csr_write(adapter, INT_EN_CLR, + INT_BIT_DMA_TX_(tx->channel_number)); + napi_disable(&tx->napi); + netif_napi_del(&tx->napi); + + lan743x_csr_write(adapter, FCT_TX_CTL, + FCT_TX_CTL_DIS_(tx->channel_number)); + lan743x_csr_wait_for_bit(adapter, FCT_TX_CTL, + FCT_TX_CTL_EN_(tx->channel_number), + 0, 1000, 20000, 100); + + lan743x_tx_release_all_descriptors(tx); + + if (tx->overflow_skb) { + dev_kfree_skb(tx->overflow_skb); + tx->overflow_skb = NULL; + } + + lan743x_tx_ring_cleanup(tx); +} + +static int lan743x_tx_open(struct lan743x_tx *tx) +{ + struct lan743x_adapter *adapter = NULL; + u32 data = 0; + int ret; + + adapter = tx->adapter; + ret = lan743x_tx_ring_init(tx); + if (ret) + return ret; + + /* initialize fifo */ + lan743x_csr_write(adapter, FCT_TX_CTL, + FCT_TX_CTL_RESET_(tx->channel_number)); + lan743x_csr_wait_for_bit(adapter, FCT_TX_CTL, + FCT_TX_CTL_RESET_(tx->channel_number), + 0, 1000, 20000, 100); + + /* enable fifo */ + lan743x_csr_write(adapter, FCT_TX_CTL, + FCT_TX_CTL_EN_(tx->channel_number)); + + /* reset tx channel */ + lan743x_csr_write(adapter, DMAC_CMD, + DMAC_CMD_TX_SWR_(tx->channel_number)); + lan743x_csr_wait_for_bit(adapter, DMAC_CMD, + DMAC_CMD_TX_SWR_(tx->channel_number), + 0, 1000, 20000, 100); + + /* Write TX_BASE_ADDR */ + lan743x_csr_write(adapter, + TX_BASE_ADDRH(tx->channel_number), + DMA_ADDR_HIGH32(tx->ring_dma_ptr)); + lan743x_csr_write(adapter, + TX_BASE_ADDRL(tx->channel_number), + DMA_ADDR_LOW32(tx->ring_dma_ptr)); + + /* Write TX_CFG_B */ + data = lan743x_csr_read(adapter, TX_CFG_B(tx->channel_number)); + data &= ~TX_CFG_B_TX_RING_LEN_MASK_; + data |= ((tx->ring_size) & TX_CFG_B_TX_RING_LEN_MASK_); + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) + data |= TX_CFG_B_TDMABL_512_; + lan743x_csr_write(adapter, TX_CFG_B(tx->channel_number), data); + + /* Write TX_CFG_A */ + data = TX_CFG_A_TX_TMR_HPWB_SEL_IOC_ | TX_CFG_A_TX_HP_WB_EN_; + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) { + data |= TX_CFG_A_TX_HP_WB_ON_INT_TMR_; + data |= TX_CFG_A_TX_PF_THRES_SET_(0x10); + data |= TX_CFG_A_TX_PF_PRI_THRES_SET_(0x04); + data |= TX_CFG_A_TX_HP_WB_THRES_SET_(0x07); + } + lan743x_csr_write(adapter, TX_CFG_A(tx->channel_number), data); + + /* Write TX_HEAD_WRITEBACK_ADDR */ + lan743x_csr_write(adapter, + TX_HEAD_WRITEBACK_ADDRH(tx->channel_number), + DMA_ADDR_HIGH32(tx->head_dma_ptr)); + lan743x_csr_write(adapter, + TX_HEAD_WRITEBACK_ADDRL(tx->channel_number), + DMA_ADDR_LOW32(tx->head_dma_ptr)); + + /* set last head */ + tx->last_head = lan743x_csr_read(adapter, TX_HEAD(tx->channel_number)); + + /* write TX_TAIL */ + tx->last_tail = 0; + lan743x_csr_write(adapter, TX_TAIL(tx->channel_number), + (u32)(tx->last_tail)); + tx->vector_flags = lan743x_intr_get_vector_flags(adapter, + INT_BIT_DMA_TX_ + (tx->channel_number)); + netif_tx_napi_add(adapter->netdev, + &tx->napi, lan743x_tx_napi_poll, + tx->ring_size - 1); + napi_enable(&tx->napi); + + data = 0; + if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR) + data |= TX_CFG_C_TX_TOP_INT_EN_AUTO_CLR_; + if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR) + data |= TX_CFG_C_TX_DMA_INT_STS_AUTO_CLR_; + if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C) + data |= TX_CFG_C_TX_INT_STS_R2C_MODE_MASK_; + if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C) + data |= TX_CFG_C_TX_INT_EN_R2C_; + lan743x_csr_write(adapter, TX_CFG_C(tx->channel_number), data); + + if (!(tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET)) + lan743x_csr_write(adapter, INT_EN_SET, + INT_BIT_DMA_TX_(tx->channel_number)); + lan743x_csr_write(adapter, DMAC_INT_EN_SET, + DMAC_INT_BIT_TX_IOC_(tx->channel_number)); + + /* start dmac channel */ + lan743x_csr_write(adapter, DMAC_CMD, + DMAC_CMD_START_T_(tx->channel_number)); + return 0; +} + +static int lan743x_rx_next_index(struct lan743x_rx *rx, int index) +{ + return ((++index) % rx->ring_size); +} + +static struct sk_buff *lan743x_rx_allocate_skb(struct lan743x_rx *rx) +{ + int length = 0; + + length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING); + return __netdev_alloc_skb(rx->adapter->netdev, + length, GFP_ATOMIC | GFP_DMA); +} + +static int lan743x_rx_init_ring_element(struct lan743x_rx *rx, int index, + struct sk_buff *skb) +{ + struct lan743x_rx_buffer_info *buffer_info; + struct lan743x_rx_descriptor *descriptor; + int length = 0; + + length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING); + descriptor = &rx->ring_cpu_ptr[index]; + buffer_info = &rx->buffer_info[index]; + buffer_info->skb = skb; + if (!(buffer_info->skb)) + return -ENOMEM; + buffer_info->dma_ptr = dma_map_single(&rx->adapter->pdev->dev, + buffer_info->skb->data, + length, + DMA_FROM_DEVICE); + if (dma_mapping_error(&rx->adapter->pdev->dev, + buffer_info->dma_ptr)) { + buffer_info->dma_ptr = 0; + return -ENOMEM; + } + + buffer_info->buffer_length = length; + descriptor->data1 = DMA_ADDR_LOW32(buffer_info->dma_ptr); + descriptor->data2 = DMA_ADDR_HIGH32(buffer_info->dma_ptr); + descriptor->data3 = 0; + descriptor->data0 = (RX_DESC_DATA0_OWN_ | + (length & RX_DESC_DATA0_BUF_LENGTH_MASK_)); + skb_reserve(buffer_info->skb, RX_HEAD_PADDING); + + return 0; +} + +static void lan743x_rx_reuse_ring_element(struct lan743x_rx *rx, int index) +{ + struct lan743x_rx_buffer_info *buffer_info; + struct lan743x_rx_descriptor *descriptor; + + descriptor = &rx->ring_cpu_ptr[index]; + buffer_info = &rx->buffer_info[index]; + + descriptor->data1 = DMA_ADDR_LOW32(buffer_info->dma_ptr); + descriptor->data2 = DMA_ADDR_HIGH32(buffer_info->dma_ptr); + descriptor->data3 = 0; + descriptor->data0 = (RX_DESC_DATA0_OWN_ | + ((buffer_info->buffer_length) & + RX_DESC_DATA0_BUF_LENGTH_MASK_)); +} + +static void lan743x_rx_release_ring_element(struct lan743x_rx *rx, int index) +{ + struct lan743x_rx_buffer_info *buffer_info; + struct lan743x_rx_descriptor *descriptor; + + descriptor = &rx->ring_cpu_ptr[index]; + buffer_info = &rx->buffer_info[index]; + + memset(descriptor, 0, sizeof(*descriptor)); + + if (buffer_info->dma_ptr) { + dma_unmap_single(&rx->adapter->pdev->dev, + buffer_info->dma_ptr, + buffer_info->buffer_length, + DMA_FROM_DEVICE); + buffer_info->dma_ptr = 0; + } + + if (buffer_info->skb) { + dev_kfree_skb(buffer_info->skb); + buffer_info->skb = NULL; + } + + memset(buffer_info, 0, sizeof(*buffer_info)); +} + +static int lan743x_rx_process_packet(struct lan743x_rx *rx) +{ + struct skb_shared_hwtstamps *hwtstamps = NULL; + int result = RX_PROCESS_RESULT_NOTHING_TO_DO; + struct lan743x_rx_buffer_info *buffer_info; + struct lan743x_rx_descriptor *descriptor; + int current_head_index = -1; + int extension_index = -1; + int first_index = -1; + int last_index = -1; + + current_head_index = *rx->head_cpu_ptr; + if (current_head_index < 0 || current_head_index >= rx->ring_size) + goto done; + + if (rx->last_head < 0 || rx->last_head >= rx->ring_size) + goto done; + + if (rx->last_head != current_head_index) { + descriptor = &rx->ring_cpu_ptr[rx->last_head]; + if (descriptor->data0 & RX_DESC_DATA0_OWN_) + goto done; + + if (!(descriptor->data0 & RX_DESC_DATA0_FS_)) + goto done; + + first_index = rx->last_head; + if (descriptor->data0 & RX_DESC_DATA0_LS_) { + last_index = rx->last_head; + } else { + int index; + + index = lan743x_rx_next_index(rx, first_index); + while (index != current_head_index) { + descriptor = &rx->ring_cpu_ptr[index]; + if (descriptor->data0 & RX_DESC_DATA0_OWN_) + goto done; + + if (descriptor->data0 & RX_DESC_DATA0_LS_) { + last_index = index; + break; + } + index = lan743x_rx_next_index(rx, index); + } + } + if (last_index >= 0) { + descriptor = &rx->ring_cpu_ptr[last_index]; + if (descriptor->data0 & RX_DESC_DATA0_EXT_) { + /* extension is expected to follow */ + int index = lan743x_rx_next_index(rx, + last_index); + if (index != current_head_index) { + descriptor = &rx->ring_cpu_ptr[index]; + if (descriptor->data0 & + RX_DESC_DATA0_OWN_) { + goto done; + } + if (descriptor->data0 & + RX_DESC_DATA0_EXT_) { + extension_index = index; + } else { + goto done; + } + } else { + /* extension is not yet available */ + /* prevent processing of this packet */ + first_index = -1; + last_index = -1; + } + } + } + } + if (first_index >= 0 && last_index >= 0) { + int real_last_index = last_index; + struct sk_buff *skb = NULL; + u32 ts_sec = 0; + u32 ts_nsec = 0; + + /* packet is available */ + if (first_index == last_index) { + /* single buffer packet */ + struct sk_buff *new_skb = NULL; + int packet_length; + + new_skb = lan743x_rx_allocate_skb(rx); + if (!new_skb) { + /* failed to allocate next skb. + * Memory is very low. + * Drop this packet and reuse buffer. + */ + lan743x_rx_reuse_ring_element(rx, first_index); + goto process_extension; + } + + buffer_info = &rx->buffer_info[first_index]; + skb = buffer_info->skb; + descriptor = &rx->ring_cpu_ptr[first_index]; + + /* unmap from dma */ + if (buffer_info->dma_ptr) { + dma_unmap_single(&rx->adapter->pdev->dev, + buffer_info->dma_ptr, + buffer_info->buffer_length, + DMA_FROM_DEVICE); + buffer_info->dma_ptr = 0; + buffer_info->buffer_length = 0; + } + buffer_info->skb = NULL; + packet_length = RX_DESC_DATA0_FRAME_LENGTH_GET_ + (descriptor->data0); + skb_put(skb, packet_length - 4); + skb->protocol = eth_type_trans(skb, + rx->adapter->netdev); + lan743x_rx_init_ring_element(rx, first_index, new_skb); + } else { + int index = first_index; + + /* multi buffer packet not supported */ + /* this should not happen since + * buffers are allocated to be at least jumbo size + */ + + /* clean up buffers */ + if (first_index <= last_index) { + while ((index >= first_index) && + (index <= last_index)) { + lan743x_rx_reuse_ring_element(rx, + index); + index = lan743x_rx_next_index(rx, + index); + } + } else { + while ((index >= first_index) || + (index <= last_index)) { + lan743x_rx_reuse_ring_element(rx, + index); + index = lan743x_rx_next_index(rx, + index); + } + } + } + +process_extension: + if (extension_index >= 0) { + descriptor = &rx->ring_cpu_ptr[extension_index]; + buffer_info = &rx->buffer_info[extension_index]; + + ts_sec = descriptor->data1; + ts_nsec = (descriptor->data2 & + RX_DESC_DATA2_TS_NS_MASK_); + lan743x_rx_reuse_ring_element(rx, extension_index); + real_last_index = extension_index; + } + + if (!skb) { + result = RX_PROCESS_RESULT_PACKET_DROPPED; + goto move_forward; + } + + if (extension_index < 0) + goto pass_packet_to_os; + hwtstamps = skb_hwtstamps(skb); + if (hwtstamps) + hwtstamps->hwtstamp = ktime_set(ts_sec, ts_nsec); + +pass_packet_to_os: + /* pass packet to OS */ + napi_gro_receive(&rx->napi, skb); + result = RX_PROCESS_RESULT_PACKET_RECEIVED; + +move_forward: + /* push tail and head forward */ + rx->last_tail = real_last_index; + rx->last_head = lan743x_rx_next_index(rx, real_last_index); + } +done: + return result; +} + +static int lan743x_rx_napi_poll(struct napi_struct *napi, int weight) +{ + struct lan743x_rx *rx = container_of(napi, struct lan743x_rx, napi); + struct lan743x_adapter *adapter = rx->adapter; + u32 rx_tail_flags = 0; + int count; + + if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C) { + /* clear int status bit before reading packet */ + lan743x_csr_write(adapter, DMAC_INT_STS, + DMAC_INT_BIT_RXFRM_(rx->channel_number)); + } + count = 0; + while (count < weight) { + int rx_process_result = -1; + + rx_process_result = lan743x_rx_process_packet(rx); + if (rx_process_result == RX_PROCESS_RESULT_PACKET_RECEIVED) { + count++; + } else if (rx_process_result == + RX_PROCESS_RESULT_NOTHING_TO_DO) { + break; + } else if (rx_process_result == + RX_PROCESS_RESULT_PACKET_DROPPED) { + continue; + } + } + rx->frame_count += count; + if (count == weight) + goto done; + + if (!napi_complete_done(napi, count)) + goto done; + + if (rx->vector_flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET) + rx_tail_flags |= RX_TAIL_SET_TOP_INT_VEC_EN_; + if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET) { + rx_tail_flags |= RX_TAIL_SET_TOP_INT_EN_; + } else { + lan743x_csr_write(adapter, INT_EN_SET, + INT_BIT_DMA_RX_(rx->channel_number)); + } + + /* update RX_TAIL */ + lan743x_csr_write(adapter, RX_TAIL(rx->channel_number), + rx_tail_flags | rx->last_tail); +done: + return count; +} + +static void lan743x_rx_ring_cleanup(struct lan743x_rx *rx) +{ + if (rx->buffer_info && rx->ring_cpu_ptr) { + int index; + + for (index = 0; index < rx->ring_size; index++) + lan743x_rx_release_ring_element(rx, index); + } + + if (rx->head_cpu_ptr) { + pci_free_consistent(rx->adapter->pdev, + sizeof(*rx->head_cpu_ptr), + rx->head_cpu_ptr, + rx->head_dma_ptr); + rx->head_cpu_ptr = NULL; + rx->head_dma_ptr = 0; + } + + kfree(rx->buffer_info); + rx->buffer_info = NULL; + + if (rx->ring_cpu_ptr) { + pci_free_consistent(rx->adapter->pdev, + rx->ring_allocation_size, + rx->ring_cpu_ptr, + rx->ring_dma_ptr); + rx->ring_allocation_size = 0; + rx->ring_cpu_ptr = NULL; + rx->ring_dma_ptr = 0; + } + + rx->ring_size = 0; + rx->last_head = 0; +} + +static int lan743x_rx_ring_init(struct lan743x_rx *rx) +{ + size_t ring_allocation_size = 0; + dma_addr_t dma_ptr = 0; + void *cpu_ptr = NULL; + int ret = -ENOMEM; + int index = 0; + + rx->ring_size = LAN743X_RX_RING_SIZE; + if (rx->ring_size <= 1) { + ret = -EINVAL; + goto cleanup; + } + if (rx->ring_size & ~RX_CFG_B_RX_RING_LEN_MASK_) { + ret = -EINVAL; + goto cleanup; + } + if (dma_set_mask_and_coherent(&rx->adapter->pdev->dev, + DMA_BIT_MASK(64))) { + if (dma_set_mask_and_coherent(&rx->adapter->pdev->dev, + DMA_BIT_MASK(32))) { + dev_warn(&rx->adapter->pdev->dev, + "lan743x_: No suitable DMA available\n"); + ret = -ENOMEM; + goto cleanup; + } + } + ring_allocation_size = ALIGN(rx->ring_size * + sizeof(struct lan743x_rx_descriptor), + PAGE_SIZE); + dma_ptr = 0; + cpu_ptr = pci_zalloc_consistent(rx->adapter->pdev, + ring_allocation_size, &dma_ptr); + if (!cpu_ptr) { + ret = -ENOMEM; + goto cleanup; + } + rx->ring_allocation_size = ring_allocation_size; + rx->ring_cpu_ptr = (struct lan743x_rx_descriptor *)cpu_ptr; + rx->ring_dma_ptr = dma_ptr; + + cpu_ptr = kcalloc(rx->ring_size, sizeof(*rx->buffer_info), + GFP_KERNEL); + if (!cpu_ptr) { + ret = -ENOMEM; + goto cleanup; + } + rx->buffer_info = (struct lan743x_rx_buffer_info *)cpu_ptr; + dma_ptr = 0; + cpu_ptr = pci_zalloc_consistent(rx->adapter->pdev, + sizeof(*rx->head_cpu_ptr), &dma_ptr); + if (!cpu_ptr) { + ret = -ENOMEM; + goto cleanup; + } + + rx->head_cpu_ptr = cpu_ptr; + rx->head_dma_ptr = dma_ptr; + if (rx->head_dma_ptr & 0x3) { + ret = -ENOMEM; + goto cleanup; + } + + rx->last_head = 0; + for (index = 0; index < rx->ring_size; index++) { + struct sk_buff *new_skb = lan743x_rx_allocate_skb(rx); + + ret = lan743x_rx_init_ring_element(rx, index, new_skb); + if (ret) + goto cleanup; + } + return 0; + +cleanup: + lan743x_rx_ring_cleanup(rx); + return ret; +} + +static void lan743x_rx_close(struct lan743x_rx *rx) +{ + struct lan743x_adapter *adapter = rx->adapter; + + lan743x_csr_write(adapter, FCT_RX_CTL, + FCT_RX_CTL_DIS_(rx->channel_number)); + lan743x_csr_wait_for_bit(adapter, FCT_RX_CTL, + FCT_RX_CTL_EN_(rx->channel_number), + 0, 1000, 20000, 100); + + lan743x_csr_write(adapter, DMAC_CMD, + DMAC_CMD_STOP_R_(rx->channel_number)); + lan743x_dmac_rx_wait_till_stopped(adapter, rx->channel_number); + + lan743x_csr_write(adapter, DMAC_INT_EN_CLR, + DMAC_INT_BIT_RXFRM_(rx->channel_number)); + lan743x_csr_write(adapter, INT_EN_CLR, + INT_BIT_DMA_RX_(rx->channel_number)); + napi_disable(&rx->napi); + + netif_napi_del(&rx->napi); + + lan743x_rx_ring_cleanup(rx); +} + +static int lan743x_rx_open(struct lan743x_rx *rx) +{ + struct lan743x_adapter *adapter = rx->adapter; + u32 data = 0; + int ret; + + rx->frame_count = 0; + ret = lan743x_rx_ring_init(rx); + if (ret) + goto return_error; + + netif_napi_add(adapter->netdev, + &rx->napi, lan743x_rx_napi_poll, + rx->ring_size - 1); + + lan743x_csr_write(adapter, DMAC_CMD, + DMAC_CMD_RX_SWR_(rx->channel_number)); + lan743x_csr_wait_for_bit(adapter, DMAC_CMD, + DMAC_CMD_RX_SWR_(rx->channel_number), + 0, 1000, 20000, 100); + + /* set ring base address */ + lan743x_csr_write(adapter, + RX_BASE_ADDRH(rx->channel_number), + DMA_ADDR_HIGH32(rx->ring_dma_ptr)); + lan743x_csr_write(adapter, + RX_BASE_ADDRL(rx->channel_number), + DMA_ADDR_LOW32(rx->ring_dma_ptr)); + + /* set rx write back address */ + lan743x_csr_write(adapter, + RX_HEAD_WRITEBACK_ADDRH(rx->channel_number), + DMA_ADDR_HIGH32(rx->head_dma_ptr)); + lan743x_csr_write(adapter, + RX_HEAD_WRITEBACK_ADDRL(rx->channel_number), + DMA_ADDR_LOW32(rx->head_dma_ptr)); + data = RX_CFG_A_RX_HP_WB_EN_; + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) { + data |= (RX_CFG_A_RX_WB_ON_INT_TMR_ | + RX_CFG_A_RX_WB_THRES_SET_(0x7) | + RX_CFG_A_RX_PF_THRES_SET_(16) | + RX_CFG_A_RX_PF_PRI_THRES_SET_(4)); + } + + /* set RX_CFG_A */ + lan743x_csr_write(adapter, + RX_CFG_A(rx->channel_number), data); + + /* set RX_CFG_B */ + data = lan743x_csr_read(adapter, RX_CFG_B(rx->channel_number)); + data &= ~RX_CFG_B_RX_PAD_MASK_; + if (!RX_HEAD_PADDING) + data |= RX_CFG_B_RX_PAD_0_; + else + data |= RX_CFG_B_RX_PAD_2_; + data &= ~RX_CFG_B_RX_RING_LEN_MASK_; + data |= ((rx->ring_size) & RX_CFG_B_RX_RING_LEN_MASK_); + data |= RX_CFG_B_TS_ALL_RX_; + if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) + data |= RX_CFG_B_RDMABL_512_; + + lan743x_csr_write(adapter, RX_CFG_B(rx->channel_number), data); + rx->vector_flags = lan743x_intr_get_vector_flags(adapter, + INT_BIT_DMA_RX_ + (rx->channel_number)); + + /* set RX_CFG_C */ + data = 0; + if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR) + data |= RX_CFG_C_RX_TOP_INT_EN_AUTO_CLR_; + if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR) + data |= RX_CFG_C_RX_DMA_INT_STS_AUTO_CLR_; + if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C) + data |= RX_CFG_C_RX_INT_STS_R2C_MODE_MASK_; + if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C) + data |= RX_CFG_C_RX_INT_EN_R2C_; + lan743x_csr_write(adapter, RX_CFG_C(rx->channel_number), data); + + rx->last_tail = ((u32)(rx->ring_size - 1)); + lan743x_csr_write(adapter, RX_TAIL(rx->channel_number), + rx->last_tail); + rx->last_head = lan743x_csr_read(adapter, RX_HEAD(rx->channel_number)); + if (rx->last_head) { + ret = -EIO; + goto napi_delete; + } + + napi_enable(&rx->napi); + + lan743x_csr_write(adapter, INT_EN_SET, + INT_BIT_DMA_RX_(rx->channel_number)); + lan743x_csr_write(adapter, DMAC_INT_STS, + DMAC_INT_BIT_RXFRM_(rx->channel_number)); + lan743x_csr_write(adapter, DMAC_INT_EN_SET, + DMAC_INT_BIT_RXFRM_(rx->channel_number)); + lan743x_csr_write(adapter, DMAC_CMD, + DMAC_CMD_START_R_(rx->channel_number)); + + /* initialize fifo */ + lan743x_csr_write(adapter, FCT_RX_CTL, + FCT_RX_CTL_RESET_(rx->channel_number)); + lan743x_csr_wait_for_bit(adapter, FCT_RX_CTL, + FCT_RX_CTL_RESET_(rx->channel_number), + 0, 1000, 20000, 100); + lan743x_csr_write(adapter, FCT_FLOW(rx->channel_number), + FCT_FLOW_CTL_REQ_EN_ | + FCT_FLOW_CTL_ON_THRESHOLD_SET_(0x2A) | + FCT_FLOW_CTL_OFF_THRESHOLD_SET_(0xA)); + + /* enable fifo */ + lan743x_csr_write(adapter, FCT_RX_CTL, + FCT_RX_CTL_EN_(rx->channel_number)); + return 0; + +napi_delete: + netif_napi_del(&rx->napi); + lan743x_rx_ring_cleanup(rx); + +return_error: + return ret; +} + +static int lan743x_netdev_close(struct net_device *netdev) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + int index; + + lan743x_tx_close(&adapter->tx[0]); + + for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) + lan743x_rx_close(&adapter->rx[index]); + + lan743x_ptp_close(adapter); + + lan743x_phy_close(adapter); + + lan743x_mac_close(adapter); + + lan743x_intr_close(adapter); + + return 0; +} + +static int lan743x_netdev_open(struct net_device *netdev) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + int index; + int ret; + + ret = lan743x_intr_open(adapter); + if (ret) + goto return_error; + + ret = lan743x_mac_open(adapter); + if (ret) + goto close_intr; + + ret = lan743x_phy_open(adapter); + if (ret) + goto close_mac; + + ret = lan743x_ptp_open(adapter); + if (ret) + goto close_phy; + + lan743x_rfe_open(adapter); + + for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) { + ret = lan743x_rx_open(&adapter->rx[index]); + if (ret) + goto close_rx; + } + + ret = lan743x_tx_open(&adapter->tx[0]); + if (ret) + goto close_rx; + + return 0; + +close_rx: + for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) { + if (adapter->rx[index].ring_cpu_ptr) + lan743x_rx_close(&adapter->rx[index]); + } + lan743x_ptp_close(adapter); + +close_phy: + lan743x_phy_close(adapter); + +close_mac: + lan743x_mac_close(adapter); + +close_intr: + lan743x_intr_close(adapter); + +return_error: + netif_warn(adapter, ifup, adapter->netdev, + "Error opening LAN743x\n"); + return ret; +} + +static netdev_tx_t lan743x_netdev_xmit_frame(struct sk_buff *skb, + struct net_device *netdev) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + return lan743x_tx_xmit_frame(&adapter->tx[0], skb); +} + +static int lan743x_netdev_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + if (!netif_running(netdev)) + return -EINVAL; + if (cmd == SIOCSHWTSTAMP) + return lan743x_ptp_ioctl(netdev, ifr, cmd); + return phy_mii_ioctl(netdev->phydev, ifr, cmd); +} + +static void lan743x_netdev_set_multicast(struct net_device *netdev) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + lan743x_rfe_set_multicast(adapter); +} + +static int lan743x_netdev_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + int ret = 0; + + ret = lan743x_mac_set_mtu(adapter, new_mtu); + if (!ret) + netdev->mtu = new_mtu; + return ret; +} + +static void lan743x_netdev_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + stats->rx_packets = lan743x_csr_read(adapter, STAT_RX_TOTAL_FRAMES); + stats->tx_packets = lan743x_csr_read(adapter, STAT_TX_TOTAL_FRAMES); + stats->rx_bytes = lan743x_csr_read(adapter, + STAT_RX_UNICAST_BYTE_COUNT) + + lan743x_csr_read(adapter, + STAT_RX_BROADCAST_BYTE_COUNT) + + lan743x_csr_read(adapter, + STAT_RX_MULTICAST_BYTE_COUNT); + stats->tx_bytes = lan743x_csr_read(adapter, + STAT_TX_UNICAST_BYTE_COUNT) + + lan743x_csr_read(adapter, + STAT_TX_BROADCAST_BYTE_COUNT) + + lan743x_csr_read(adapter, + STAT_TX_MULTICAST_BYTE_COUNT); + stats->rx_errors = lan743x_csr_read(adapter, STAT_RX_FCS_ERRORS) + + lan743x_csr_read(adapter, + STAT_RX_ALIGNMENT_ERRORS) + + lan743x_csr_read(adapter, STAT_RX_JABBER_ERRORS) + + lan743x_csr_read(adapter, + STAT_RX_UNDERSIZE_FRAME_ERRORS) + + lan743x_csr_read(adapter, + STAT_RX_OVERSIZE_FRAME_ERRORS); + stats->tx_errors = lan743x_csr_read(adapter, STAT_TX_FCS_ERRORS) + + lan743x_csr_read(adapter, + STAT_TX_EXCESS_DEFERRAL_ERRORS) + + lan743x_csr_read(adapter, STAT_TX_CARRIER_ERRORS); + stats->rx_dropped = lan743x_csr_read(adapter, + STAT_RX_DROPPED_FRAMES); + stats->tx_dropped = lan743x_csr_read(adapter, + STAT_TX_EXCESSIVE_COLLISION); + stats->multicast = lan743x_csr_read(adapter, + STAT_RX_MULTICAST_FRAMES) + + lan743x_csr_read(adapter, + STAT_TX_MULTICAST_FRAMES); + stats->collisions = lan743x_csr_read(adapter, + STAT_TX_SINGLE_COLLISIONS) + + lan743x_csr_read(adapter, + STAT_TX_MULTIPLE_COLLISIONS) + + lan743x_csr_read(adapter, + STAT_TX_LATE_COLLISIONS); +} + +static int lan743x_netdev_set_mac_address(struct net_device *netdev, + void *addr) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + struct sockaddr *sock_addr = addr; + int ret; + + ret = eth_prepare_mac_addr_change(netdev, sock_addr); + if (ret) + return ret; + ether_addr_copy(netdev->dev_addr, sock_addr->sa_data); + lan743x_mac_set_address(adapter, sock_addr->sa_data); + lan743x_rfe_update_mac_address(adapter); + return 0; +} + +static const struct net_device_ops lan743x_netdev_ops = { + .ndo_open = lan743x_netdev_open, + .ndo_stop = lan743x_netdev_close, + .ndo_start_xmit = lan743x_netdev_xmit_frame, + .ndo_do_ioctl = lan743x_netdev_ioctl, + .ndo_set_rx_mode = lan743x_netdev_set_multicast, + .ndo_change_mtu = lan743x_netdev_change_mtu, + .ndo_get_stats64 = lan743x_netdev_get_stats64, + .ndo_set_mac_address = lan743x_netdev_set_mac_address, +}; + +static void lan743x_hardware_cleanup(struct lan743x_adapter *adapter) +{ + lan743x_csr_write(adapter, INT_EN_CLR, 0xFFFFFFFF); +} + +static void lan743x_mdiobus_cleanup(struct lan743x_adapter *adapter) +{ + mdiobus_unregister(adapter->mdiobus); +} + +static void lan743x_full_cleanup(struct lan743x_adapter *adapter) +{ + unregister_netdev(adapter->netdev); + + lan743x_mdiobus_cleanup(adapter); + lan743x_hardware_cleanup(adapter); + lan743x_pci_cleanup(adapter); +} + +static int lan743x_hardware_init(struct lan743x_adapter *adapter, + struct pci_dev *pdev) +{ + struct lan743x_tx *tx; + int index; + int ret; + + adapter->intr.irq = adapter->pdev->irq; + lan743x_csr_write(adapter, INT_EN_CLR, 0xFFFFFFFF); + + ret = lan743x_gpio_init(adapter); + if (ret) + return ret; + + ret = lan743x_mac_init(adapter); + if (ret) + return ret; + + ret = lan743x_phy_init(adapter); + if (ret) + return ret; + + ret = lan743x_ptp_init(adapter); + if (ret) + return ret; + + lan743x_rfe_update_mac_address(adapter); + + ret = lan743x_dmac_init(adapter); + if (ret) + return ret; + + for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) { + adapter->rx[index].adapter = adapter; + adapter->rx[index].channel_number = index; + } + + tx = &adapter->tx[0]; + tx->adapter = adapter; + tx->channel_number = 0; + spin_lock_init(&tx->ring_lock); + return 0; +} + +static int lan743x_mdiobus_init(struct lan743x_adapter *adapter) +{ + int ret; + + adapter->mdiobus = devm_mdiobus_alloc(&adapter->pdev->dev); + if (!(adapter->mdiobus)) { + ret = -ENOMEM; + goto return_error; + } + + adapter->mdiobus->priv = (void *)adapter; + adapter->mdiobus->read = lan743x_mdiobus_read; + adapter->mdiobus->write = lan743x_mdiobus_write; + adapter->mdiobus->name = "lan743x-mdiobus"; + snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE, + "pci-%s", pci_name(adapter->pdev)); + + if ((adapter->csr.id_rev & ID_REV_ID_MASK_) == ID_REV_ID_LAN7430_) + /* LAN7430 uses internal phy at address 1 */ + adapter->mdiobus->phy_mask = ~(u32)BIT(1); + + /* register mdiobus */ + ret = mdiobus_register(adapter->mdiobus); + if (ret < 0) + goto return_error; + return 0; + +return_error: + return ret; +} + +/* lan743x_pcidev_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @id: entry in lan743x_pci_tbl + * + * Returns 0 on success, negative on failure + * + * initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int lan743x_pcidev_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct lan743x_adapter *adapter = NULL; + struct net_device *netdev = NULL; + int ret = -ENODEV; + + netdev = devm_alloc_etherdev(&pdev->dev, + sizeof(struct lan743x_adapter)); + if (!netdev) + goto return_error; + + SET_NETDEV_DEV(netdev, &pdev->dev); + pci_set_drvdata(pdev, netdev); + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_IFUP | + NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED; + netdev->max_mtu = LAN743X_MAX_FRAME_SIZE; + + ret = lan743x_pci_init(adapter, pdev); + if (ret) + goto return_error; + + ret = lan743x_csr_init(adapter); + if (ret) + goto cleanup_pci; + + ret = lan743x_hardware_init(adapter, pdev); + if (ret) + goto cleanup_pci; + + ret = lan743x_mdiobus_init(adapter); + if (ret) + goto cleanup_hardware; + + adapter->netdev->netdev_ops = &lan743x_netdev_ops; + adapter->netdev->ethtool_ops = &lan743x_ethtool_ops; + adapter->netdev->features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM; + adapter->netdev->hw_features = adapter->netdev->features; + + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + + ret = register_netdev(adapter->netdev); + if (ret < 0) + goto cleanup_mdiobus; + return 0; + +cleanup_mdiobus: + lan743x_mdiobus_cleanup(adapter); + +cleanup_hardware: + lan743x_hardware_cleanup(adapter); + +cleanup_pci: + lan743x_pci_cleanup(adapter); + +return_error: + pr_warn("Initialization failed\n"); + return ret; +} + +/** + * lan743x_pcidev_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * this is called by the PCI subsystem to alert the driver + * that it should release a PCI device. This could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void lan743x_pcidev_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + + lan743x_full_cleanup(adapter); +} + +static void lan743x_pcidev_shutdown(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + + rtnl_lock(); + netif_device_detach(netdev); + + /* close netdev when netdev is at running state. + * For instance, it is true when system goes to sleep by pm-suspend + * However, it is false when system goes to sleep by suspend GUI menu + */ + if (netif_running(netdev)) + lan743x_netdev_close(netdev); + rtnl_unlock(); + +#ifdef CONFIG_PM + pci_save_state(pdev); +#endif + + /* clean up lan743x portion */ + lan743x_hardware_cleanup(adapter); +} + +#ifdef CONFIG_PM_SLEEP +static u16 lan743x_pm_wakeframe_crc16(const u8 *buf, int len) +{ + return bitrev16(crc16(0xFFFF, buf, len)); +} + +static void lan743x_pm_set_wol(struct lan743x_adapter *adapter) +{ + const u8 ipv4_multicast[3] = { 0x01, 0x00, 0x5E }; + const u8 ipv6_multicast[3] = { 0x33, 0x33 }; + const u8 arp_type[2] = { 0x08, 0x06 }; + int mask_index; + u32 pmtctl; + u32 wucsr; + u32 macrx; + u16 crc; + + for (mask_index = 0; mask_index < MAC_NUM_OF_WUF_CFG; mask_index++) + lan743x_csr_write(adapter, MAC_WUF_CFG(mask_index), 0); + + /* clear wake settings */ + pmtctl = lan743x_csr_read(adapter, PMT_CTL); + pmtctl |= PMT_CTL_WUPS_MASK_; + pmtctl &= ~(PMT_CTL_GPIO_WAKEUP_EN_ | PMT_CTL_EEE_WAKEUP_EN_ | + PMT_CTL_WOL_EN_ | PMT_CTL_MAC_D3_RX_CLK_OVR_ | + PMT_CTL_RX_FCT_RFE_D3_CLK_OVR_ | PMT_CTL_ETH_PHY_WAKE_EN_); + + macrx = lan743x_csr_read(adapter, MAC_RX); + + wucsr = 0; + mask_index = 0; + + pmtctl |= PMT_CTL_ETH_PHY_D3_COLD_OVR_ | PMT_CTL_ETH_PHY_D3_OVR_; + + if (adapter->wolopts & WAKE_PHY) { + pmtctl |= PMT_CTL_ETH_PHY_EDPD_PLL_CTL_; + pmtctl |= PMT_CTL_ETH_PHY_WAKE_EN_; + } + if (adapter->wolopts & WAKE_MAGIC) { + wucsr |= MAC_WUCSR_MPEN_; + macrx |= MAC_RX_RXEN_; + pmtctl |= PMT_CTL_WOL_EN_ | PMT_CTL_MAC_D3_RX_CLK_OVR_; + } + if (adapter->wolopts & WAKE_UCAST) { + wucsr |= MAC_WUCSR_RFE_WAKE_EN_ | MAC_WUCSR_PFDA_EN_; + macrx |= MAC_RX_RXEN_; + pmtctl |= PMT_CTL_WOL_EN_ | PMT_CTL_MAC_D3_RX_CLK_OVR_; + pmtctl |= PMT_CTL_RX_FCT_RFE_D3_CLK_OVR_; + } + if (adapter->wolopts & WAKE_BCAST) { + wucsr |= MAC_WUCSR_RFE_WAKE_EN_ | MAC_WUCSR_BCST_EN_; + macrx |= MAC_RX_RXEN_; + pmtctl |= PMT_CTL_WOL_EN_ | PMT_CTL_MAC_D3_RX_CLK_OVR_; + pmtctl |= PMT_CTL_RX_FCT_RFE_D3_CLK_OVR_; + } + if (adapter->wolopts & WAKE_MCAST) { + /* IPv4 multicast */ + crc = lan743x_pm_wakeframe_crc16(ipv4_multicast, 3); + lan743x_csr_write(adapter, MAC_WUF_CFG(mask_index), + MAC_WUF_CFG_EN_ | MAC_WUF_CFG_TYPE_MCAST_ | + (0 << MAC_WUF_CFG_OFFSET_SHIFT_) | + (crc & MAC_WUF_CFG_CRC16_MASK_)); + lan743x_csr_write(adapter, MAC_WUF_MASK0(mask_index), 7); + lan743x_csr_write(adapter, MAC_WUF_MASK1(mask_index), 0); + lan743x_csr_write(adapter, MAC_WUF_MASK2(mask_index), 0); + lan743x_csr_write(adapter, MAC_WUF_MASK3(mask_index), 0); + mask_index++; + + /* IPv6 multicast */ + crc = lan743x_pm_wakeframe_crc16(ipv6_multicast, 2); + lan743x_csr_write(adapter, MAC_WUF_CFG(mask_index), + MAC_WUF_CFG_EN_ | MAC_WUF_CFG_TYPE_MCAST_ | + (0 << MAC_WUF_CFG_OFFSET_SHIFT_) | + (crc & MAC_WUF_CFG_CRC16_MASK_)); + lan743x_csr_write(adapter, MAC_WUF_MASK0(mask_index), 3); + lan743x_csr_write(adapter, MAC_WUF_MASK1(mask_index), 0); + lan743x_csr_write(adapter, MAC_WUF_MASK2(mask_index), 0); + lan743x_csr_write(adapter, MAC_WUF_MASK3(mask_index), 0); + mask_index++; + + wucsr |= MAC_WUCSR_RFE_WAKE_EN_ | MAC_WUCSR_WAKE_EN_; + macrx |= MAC_RX_RXEN_; + pmtctl |= PMT_CTL_WOL_EN_ | PMT_CTL_MAC_D3_RX_CLK_OVR_; + pmtctl |= PMT_CTL_RX_FCT_RFE_D3_CLK_OVR_; + } + if (adapter->wolopts & WAKE_ARP) { + /* set MAC_WUF_CFG & WUF_MASK + * for packettype (offset 12,13) = ARP (0x0806) + */ + crc = lan743x_pm_wakeframe_crc16(arp_type, 2); + lan743x_csr_write(adapter, MAC_WUF_CFG(mask_index), + MAC_WUF_CFG_EN_ | MAC_WUF_CFG_TYPE_ALL_ | + (0 << MAC_WUF_CFG_OFFSET_SHIFT_) | + (crc & MAC_WUF_CFG_CRC16_MASK_)); + lan743x_csr_write(adapter, MAC_WUF_MASK0(mask_index), 0x3000); + lan743x_csr_write(adapter, MAC_WUF_MASK1(mask_index), 0); + lan743x_csr_write(adapter, MAC_WUF_MASK2(mask_index), 0); + lan743x_csr_write(adapter, MAC_WUF_MASK3(mask_index), 0); + mask_index++; + + wucsr |= MAC_WUCSR_RFE_WAKE_EN_ | MAC_WUCSR_WAKE_EN_; + macrx |= MAC_RX_RXEN_; + pmtctl |= PMT_CTL_WOL_EN_ | PMT_CTL_MAC_D3_RX_CLK_OVR_; + pmtctl |= PMT_CTL_RX_FCT_RFE_D3_CLK_OVR_; + } + + lan743x_csr_write(adapter, MAC_WUCSR, wucsr); + lan743x_csr_write(adapter, PMT_CTL, pmtctl); + lan743x_csr_write(adapter, MAC_RX, macrx); +} + +static int lan743x_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *netdev = pci_get_drvdata(pdev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + int ret; + + lan743x_pcidev_shutdown(pdev); + + /* clear all wakes */ + lan743x_csr_write(adapter, MAC_WUCSR, 0); + lan743x_csr_write(adapter, MAC_WUCSR2, 0); + lan743x_csr_write(adapter, MAC_WK_SRC, 0xFFFFFFFF); + + if (adapter->wolopts) + lan743x_pm_set_wol(adapter); + + /* Host sets PME_En, put D3hot */ + ret = pci_prepare_to_sleep(pdev); + + return 0; +} + +static int lan743x_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *netdev = pci_get_drvdata(pdev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_save_state(pdev); + + ret = lan743x_hardware_init(adapter, pdev); + if (ret) { + netif_err(adapter, probe, adapter->netdev, + "lan743x_hardware_init returned %d\n", ret); + lan743x_pci_cleanup(adapter); + return ret; + } + + /* open netdev when netdev is at running state while resume. + * For instance, it is true when system wakesup after pm-suspend + * However, it is false when system wakes up after suspend GUI menu + */ + if (netif_running(netdev)) + lan743x_netdev_open(netdev); + + netif_device_attach(netdev); + + return 0; +} + +static const struct dev_pm_ops lan743x_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(lan743x_pm_suspend, lan743x_pm_resume) +}; +#endif /* CONFIG_PM_SLEEP */ + +static const struct pci_device_id lan743x_pcidev_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7430) }, + { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7431) }, + { 0, } +}; + +static struct pci_driver lan743x_pcidev_driver = { + .name = DRIVER_NAME, + .id_table = lan743x_pcidev_tbl, + .probe = lan743x_pcidev_probe, + .remove = lan743x_pcidev_remove, +#ifdef CONFIG_PM_SLEEP + .driver.pm = &lan743x_pm_ops, +#endif + .shutdown = lan743x_pcidev_shutdown, +}; + +module_pci_driver(lan743x_pcidev_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h new file mode 100644 index 000000000..77273be2d --- /dev/null +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -0,0 +1,827 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2018 Microchip Technology Inc. */ + +#ifndef _LAN743X_H +#define _LAN743X_H + +#include "lan743x_ptp.h" + +#define DRIVER_AUTHOR "Bryan Whitehead <Bryan.Whitehead@microchip.com>" +#define DRIVER_DESC "LAN743x PCIe Gigabit Ethernet Driver" +#define DRIVER_NAME "lan743x" + +/* Register Definitions */ +#define ID_REV (0x00) +#define ID_REV_ID_MASK_ (0xFFFF0000) +#define ID_REV_ID_LAN7430_ (0x74300000) +#define ID_REV_ID_LAN7431_ (0x74310000) +#define ID_REV_IS_VALID_CHIP_ID_(id_rev) \ + (((id_rev) & 0xFFF00000) == 0x74300000) +#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) +#define ID_REV_CHIP_REV_A0_ (0x00000000) +#define ID_REV_CHIP_REV_B0_ (0x00000010) + +#define FPGA_REV (0x04) +#define FPGA_REV_GET_MINOR_(fpga_rev) (((fpga_rev) >> 8) & 0x000000FF) +#define FPGA_REV_GET_MAJOR_(fpga_rev) ((fpga_rev) & 0x000000FF) + +#define HW_CFG (0x010) +#define HW_CFG_LRST_ BIT(1) + +#define PMT_CTL (0x014) +#define PMT_CTL_ETH_PHY_D3_COLD_OVR_ BIT(27) +#define PMT_CTL_MAC_D3_RX_CLK_OVR_ BIT(25) +#define PMT_CTL_ETH_PHY_EDPD_PLL_CTL_ BIT(24) +#define PMT_CTL_ETH_PHY_D3_OVR_ BIT(23) +#define PMT_CTL_RX_FCT_RFE_D3_CLK_OVR_ BIT(18) +#define PMT_CTL_GPIO_WAKEUP_EN_ BIT(15) +#define PMT_CTL_EEE_WAKEUP_EN_ BIT(13) +#define PMT_CTL_READY_ BIT(7) +#define PMT_CTL_ETH_PHY_RST_ BIT(4) +#define PMT_CTL_WOL_EN_ BIT(3) +#define PMT_CTL_ETH_PHY_WAKE_EN_ BIT(2) +#define PMT_CTL_WUPS_MASK_ (0x00000003) + +#define DP_SEL (0x024) +#define DP_SEL_DPRDY_ BIT(31) +#define DP_SEL_MASK_ (0x0000001F) +#define DP_SEL_RFE_RAM (0x00000001) + +#define DP_SEL_VHF_HASH_LEN (16) +#define DP_SEL_VHF_VLAN_LEN (128) + +#define DP_CMD (0x028) +#define DP_CMD_WRITE_ (0x00000001) + +#define DP_ADDR (0x02C) + +#define DP_DATA_0 (0x030) + +#define E2P_CMD (0x040) +#define E2P_CMD_EPC_BUSY_ BIT(31) +#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000) +#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000) +#define E2P_CMD_EPC_CMD_READ_ (0x00000000) +#define E2P_CMD_EPC_TIMEOUT_ BIT(10) +#define E2P_CMD_EPC_ADDR_MASK_ (0x000001FF) + +#define E2P_DATA (0x044) + +#define GPIO_CFG0 (0x050) +#define GPIO_CFG0_GPIO_DIR_BIT_(bit) BIT(16 + (bit)) +#define GPIO_CFG0_GPIO_DATA_BIT_(bit) BIT(0 + (bit)) + +#define GPIO_CFG1 (0x054) +#define GPIO_CFG1_GPIOEN_BIT_(bit) BIT(16 + (bit)) +#define GPIO_CFG1_GPIOBUF_BIT_(bit) BIT(0 + (bit)) + +#define GPIO_CFG2 (0x058) +#define GPIO_CFG2_1588_POL_BIT_(bit) BIT(0 + (bit)) + +#define GPIO_CFG3 (0x05C) +#define GPIO_CFG3_1588_CH_SEL_BIT_(bit) BIT(16 + (bit)) +#define GPIO_CFG3_1588_OE_BIT_(bit) BIT(0 + (bit)) + +#define FCT_RX_CTL (0xAC) +#define FCT_RX_CTL_EN_(channel) BIT(28 + (channel)) +#define FCT_RX_CTL_DIS_(channel) BIT(24 + (channel)) +#define FCT_RX_CTL_RESET_(channel) BIT(20 + (channel)) + +#define FCT_TX_CTL (0xC4) +#define FCT_TX_CTL_EN_(channel) BIT(28 + (channel)) +#define FCT_TX_CTL_DIS_(channel) BIT(24 + (channel)) +#define FCT_TX_CTL_RESET_(channel) BIT(20 + (channel)) + +#define FCT_FLOW(rx_channel) (0xE0 + ((rx_channel) << 2)) +#define FCT_FLOW_CTL_OFF_THRESHOLD_ (0x00007F00) +#define FCT_FLOW_CTL_OFF_THRESHOLD_SET_(value) \ + ((value << 8) & FCT_FLOW_CTL_OFF_THRESHOLD_) +#define FCT_FLOW_CTL_REQ_EN_ BIT(7) +#define FCT_FLOW_CTL_ON_THRESHOLD_ (0x0000007F) +#define FCT_FLOW_CTL_ON_THRESHOLD_SET_(value) \ + ((value << 0) & FCT_FLOW_CTL_ON_THRESHOLD_) + +#define MAC_CR (0x100) +#define MAC_CR_EEE_EN_ BIT(17) +#define MAC_CR_ADD_ BIT(12) +#define MAC_CR_ASD_ BIT(11) +#define MAC_CR_CNTR_RST_ BIT(5) +#define MAC_CR_RST_ BIT(0) + +#define MAC_RX (0x104) +#define MAC_RX_MAX_SIZE_SHIFT_ (16) +#define MAC_RX_MAX_SIZE_MASK_ (0x3FFF0000) +#define MAC_RX_RXD_ BIT(1) +#define MAC_RX_RXEN_ BIT(0) + +#define MAC_TX (0x108) +#define MAC_TX_TXD_ BIT(1) +#define MAC_TX_TXEN_ BIT(0) + +#define MAC_FLOW (0x10C) +#define MAC_FLOW_CR_TX_FCEN_ BIT(30) +#define MAC_FLOW_CR_RX_FCEN_ BIT(29) +#define MAC_FLOW_CR_FCPT_MASK_ (0x0000FFFF) + +#define MAC_RX_ADDRH (0x118) + +#define MAC_RX_ADDRL (0x11C) + +#define MAC_MII_ACC (0x120) +#define MAC_MII_ACC_PHY_ADDR_SHIFT_ (11) +#define MAC_MII_ACC_PHY_ADDR_MASK_ (0x0000F800) +#define MAC_MII_ACC_MIIRINDA_SHIFT_ (6) +#define MAC_MII_ACC_MIIRINDA_MASK_ (0x000007C0) +#define MAC_MII_ACC_MII_READ_ (0x00000000) +#define MAC_MII_ACC_MII_WRITE_ (0x00000002) +#define MAC_MII_ACC_MII_BUSY_ BIT(0) + +#define MAC_MII_DATA (0x124) + +#define MAC_EEE_TX_LPI_REQ_DLY_CNT (0x130) + +#define MAC_WUCSR (0x140) +#define MAC_WUCSR_RFE_WAKE_EN_ BIT(14) +#define MAC_WUCSR_PFDA_EN_ BIT(3) +#define MAC_WUCSR_WAKE_EN_ BIT(2) +#define MAC_WUCSR_MPEN_ BIT(1) +#define MAC_WUCSR_BCST_EN_ BIT(0) + +#define MAC_WK_SRC (0x144) + +#define MAC_WUF_CFG0 (0x150) +#define MAC_NUM_OF_WUF_CFG (32) +#define MAC_WUF_CFG_BEGIN (MAC_WUF_CFG0) +#define MAC_WUF_CFG(index) (MAC_WUF_CFG_BEGIN + (4 * (index))) +#define MAC_WUF_CFG_EN_ BIT(31) +#define MAC_WUF_CFG_TYPE_MCAST_ (0x02000000) +#define MAC_WUF_CFG_TYPE_ALL_ (0x01000000) +#define MAC_WUF_CFG_OFFSET_SHIFT_ (16) +#define MAC_WUF_CFG_CRC16_MASK_ (0x0000FFFF) + +#define MAC_WUF_MASK0_0 (0x200) +#define MAC_WUF_MASK0_1 (0x204) +#define MAC_WUF_MASK0_2 (0x208) +#define MAC_WUF_MASK0_3 (0x20C) +#define MAC_WUF_MASK0_BEGIN (MAC_WUF_MASK0_0) +#define MAC_WUF_MASK1_BEGIN (MAC_WUF_MASK0_1) +#define MAC_WUF_MASK2_BEGIN (MAC_WUF_MASK0_2) +#define MAC_WUF_MASK3_BEGIN (MAC_WUF_MASK0_3) +#define MAC_WUF_MASK0(index) (MAC_WUF_MASK0_BEGIN + (0x10 * (index))) +#define MAC_WUF_MASK1(index) (MAC_WUF_MASK1_BEGIN + (0x10 * (index))) +#define MAC_WUF_MASK2(index) (MAC_WUF_MASK2_BEGIN + (0x10 * (index))) +#define MAC_WUF_MASK3(index) (MAC_WUF_MASK3_BEGIN + (0x10 * (index))) + +/* offset 0x400 - 0x500, x may range from 0 to 32, for a total of 33 entries */ +#define RFE_ADDR_FILT_HI(x) (0x400 + (8 * (x))) +#define RFE_ADDR_FILT_HI_VALID_ BIT(31) + +/* offset 0x404 - 0x504, x may range from 0 to 32, for a total of 33 entries */ +#define RFE_ADDR_FILT_LO(x) (0x404 + (8 * (x))) + +#define RFE_CTL (0x508) +#define RFE_CTL_AB_ BIT(10) +#define RFE_CTL_AM_ BIT(9) +#define RFE_CTL_AU_ BIT(8) +#define RFE_CTL_MCAST_HASH_ BIT(3) +#define RFE_CTL_DA_PERFECT_ BIT(1) + +#define RFE_RSS_CFG (0x554) +#define RFE_RSS_CFG_UDP_IPV6_EX_ BIT(16) +#define RFE_RSS_CFG_TCP_IPV6_EX_ BIT(15) +#define RFE_RSS_CFG_IPV6_EX_ BIT(14) +#define RFE_RSS_CFG_UDP_IPV6_ BIT(13) +#define RFE_RSS_CFG_TCP_IPV6_ BIT(12) +#define RFE_RSS_CFG_IPV6_ BIT(11) +#define RFE_RSS_CFG_UDP_IPV4_ BIT(10) +#define RFE_RSS_CFG_TCP_IPV4_ BIT(9) +#define RFE_RSS_CFG_IPV4_ BIT(8) +#define RFE_RSS_CFG_VALID_HASH_BITS_ (0x000000E0) +#define RFE_RSS_CFG_RSS_QUEUE_ENABLE_ BIT(2) +#define RFE_RSS_CFG_RSS_HASH_STORE_ BIT(1) +#define RFE_RSS_CFG_RSS_ENABLE_ BIT(0) + +#define RFE_HASH_KEY(index) (0x558 + (index << 2)) + +#define RFE_INDX(index) (0x580 + (index << 2)) + +#define MAC_WUCSR2 (0x600) + +#define INT_STS (0x780) +#define INT_BIT_DMA_RX_(channel) BIT(24 + (channel)) +#define INT_BIT_ALL_RX_ (0x0F000000) +#define INT_BIT_DMA_TX_(channel) BIT(16 + (channel)) +#define INT_BIT_ALL_TX_ (0x000F0000) +#define INT_BIT_SW_GP_ BIT(9) +#define INT_BIT_1588_ BIT(7) +#define INT_BIT_ALL_OTHER_ (INT_BIT_SW_GP_ | INT_BIT_1588_) +#define INT_BIT_MAS_ BIT(0) + +#define INT_SET (0x784) + +#define INT_EN_SET (0x788) + +#define INT_EN_CLR (0x78C) + +#define INT_STS_R2C (0x790) + +#define INT_VEC_EN_SET (0x794) +#define INT_VEC_EN_CLR (0x798) +#define INT_VEC_EN_AUTO_CLR (0x79C) +#define INT_VEC_EN_(vector_index) BIT(0 + vector_index) + +#define INT_VEC_MAP0 (0x7A0) +#define INT_VEC_MAP0_RX_VEC_(channel, vector) \ + (((u32)(vector)) << ((channel) << 2)) + +#define INT_VEC_MAP1 (0x7A4) +#define INT_VEC_MAP1_TX_VEC_(channel, vector) \ + (((u32)(vector)) << ((channel) << 2)) + +#define INT_VEC_MAP2 (0x7A8) + +#define INT_MOD_MAP0 (0x7B0) + +#define INT_MOD_MAP1 (0x7B4) + +#define INT_MOD_MAP2 (0x7B8) + +#define INT_MOD_CFG0 (0x7C0) +#define INT_MOD_CFG1 (0x7C4) +#define INT_MOD_CFG2 (0x7C8) +#define INT_MOD_CFG3 (0x7CC) +#define INT_MOD_CFG4 (0x7D0) +#define INT_MOD_CFG5 (0x7D4) +#define INT_MOD_CFG6 (0x7D8) +#define INT_MOD_CFG7 (0x7DC) + +#define PTP_CMD_CTL (0x0A00) +#define PTP_CMD_CTL_PTP_CLK_STP_NSEC_ BIT(6) +#define PTP_CMD_CTL_PTP_CLOCK_STEP_SEC_ BIT(5) +#define PTP_CMD_CTL_PTP_CLOCK_LOAD_ BIT(4) +#define PTP_CMD_CTL_PTP_CLOCK_READ_ BIT(3) +#define PTP_CMD_CTL_PTP_ENABLE_ BIT(2) +#define PTP_CMD_CTL_PTP_DISABLE_ BIT(1) +#define PTP_CMD_CTL_PTP_RESET_ BIT(0) +#define PTP_GENERAL_CONFIG (0x0A04) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_(channel) \ + (0x7 << (1 + ((channel) << 2))) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_100NS_ (0) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_10US_ (1) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_100US_ (2) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_1MS_ (3) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_10MS_ (4) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_ (5) +#define PTP_GENERAL_CONFIG_CLOCK_EVENT_X_SET_(channel, value) \ + (((value) & 0x7) << (1 + ((channel) << 2))) +#define PTP_GENERAL_CONFIG_RELOAD_ADD_X_(channel) (BIT((channel) << 2)) + +#define PTP_INT_STS (0x0A08) +#define PTP_INT_EN_SET (0x0A0C) +#define PTP_INT_EN_CLR (0x0A10) +#define PTP_INT_BIT_TX_SWTS_ERR_ BIT(13) +#define PTP_INT_BIT_TX_TS_ BIT(12) +#define PTP_INT_BIT_TIMER_B_ BIT(1) +#define PTP_INT_BIT_TIMER_A_ BIT(0) + +#define PTP_CLOCK_SEC (0x0A14) +#define PTP_CLOCK_NS (0x0A18) +#define PTP_CLOCK_SUBNS (0x0A1C) +#define PTP_CLOCK_RATE_ADJ (0x0A20) +#define PTP_CLOCK_RATE_ADJ_DIR_ BIT(31) +#define PTP_CLOCK_STEP_ADJ (0x0A2C) +#define PTP_CLOCK_STEP_ADJ_DIR_ BIT(31) +#define PTP_CLOCK_STEP_ADJ_VALUE_MASK_ (0x3FFFFFFF) +#define PTP_CLOCK_TARGET_SEC_X(channel) (0x0A30 + ((channel) << 4)) +#define PTP_CLOCK_TARGET_NS_X(channel) (0x0A34 + ((channel) << 4)) +#define PTP_CLOCK_TARGET_RELOAD_SEC_X(channel) (0x0A38 + ((channel) << 4)) +#define PTP_CLOCK_TARGET_RELOAD_NS_X(channel) (0x0A3C + ((channel) << 4)) +#define PTP_LATENCY (0x0A5C) +#define PTP_LATENCY_TX_SET_(tx_latency) (((u32)(tx_latency)) << 16) +#define PTP_LATENCY_RX_SET_(rx_latency) \ + (((u32)(rx_latency)) & 0x0000FFFF) +#define PTP_CAP_INFO (0x0A60) +#define PTP_CAP_INFO_TX_TS_CNT_GET_(reg_val) (((reg_val) & 0x00000070) >> 4) + +#define PTP_TX_MOD (0x0AA4) +#define PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_ (0x10000000) + +#define PTP_TX_MOD2 (0x0AA8) +#define PTP_TX_MOD2_TX_PTP_CLR_UDPV4_CHKSUM_ (0x00000001) + +#define PTP_TX_EGRESS_SEC (0x0AAC) +#define PTP_TX_EGRESS_NS (0x0AB0) +#define PTP_TX_EGRESS_NS_CAPTURE_CAUSE_MASK_ (0xC0000000) +#define PTP_TX_EGRESS_NS_CAPTURE_CAUSE_AUTO_ (0x00000000) +#define PTP_TX_EGRESS_NS_CAPTURE_CAUSE_SW_ (0x40000000) +#define PTP_TX_EGRESS_NS_TS_NS_MASK_ (0x3FFFFFFF) + +#define PTP_TX_MSG_HEADER (0x0AB4) +#define PTP_TX_MSG_HEADER_MSG_TYPE_ (0x000F0000) +#define PTP_TX_MSG_HEADER_MSG_TYPE_SYNC_ (0x00000000) + +#define DMAC_CFG (0xC00) +#define DMAC_CFG_COAL_EN_ BIT(16) +#define DMAC_CFG_CH_ARB_SEL_RX_HIGH_ (0x00000000) +#define DMAC_CFG_MAX_READ_REQ_MASK_ (0x00000070) +#define DMAC_CFG_MAX_READ_REQ_SET_(val) \ + ((((u32)(val)) << 4) & DMAC_CFG_MAX_READ_REQ_MASK_) +#define DMAC_CFG_MAX_DSPACE_16_ (0x00000000) +#define DMAC_CFG_MAX_DSPACE_32_ (0x00000001) +#define DMAC_CFG_MAX_DSPACE_64_ BIT(1) +#define DMAC_CFG_MAX_DSPACE_128_ (0x00000003) + +#define DMAC_COAL_CFG (0xC04) +#define DMAC_COAL_CFG_TIMER_LIMIT_MASK_ (0xFFF00000) +#define DMAC_COAL_CFG_TIMER_LIMIT_SET_(val) \ + ((((u32)(val)) << 20) & DMAC_COAL_CFG_TIMER_LIMIT_MASK_) +#define DMAC_COAL_CFG_TIMER_TX_START_ BIT(19) +#define DMAC_COAL_CFG_FLUSH_INTS_ BIT(18) +#define DMAC_COAL_CFG_INT_EXIT_COAL_ BIT(17) +#define DMAC_COAL_CFG_CSR_EXIT_COAL_ BIT(16) +#define DMAC_COAL_CFG_TX_THRES_MASK_ (0x0000FF00) +#define DMAC_COAL_CFG_TX_THRES_SET_(val) \ + ((((u32)(val)) << 8) & DMAC_COAL_CFG_TX_THRES_MASK_) +#define DMAC_COAL_CFG_RX_THRES_MASK_ (0x000000FF) +#define DMAC_COAL_CFG_RX_THRES_SET_(val) \ + (((u32)(val)) & DMAC_COAL_CFG_RX_THRES_MASK_) + +#define DMAC_OBFF_CFG (0xC08) +#define DMAC_OBFF_TX_THRES_MASK_ (0x0000FF00) +#define DMAC_OBFF_TX_THRES_SET_(val) \ + ((((u32)(val)) << 8) & DMAC_OBFF_TX_THRES_MASK_) +#define DMAC_OBFF_RX_THRES_MASK_ (0x000000FF) +#define DMAC_OBFF_RX_THRES_SET_(val) \ + (((u32)(val)) & DMAC_OBFF_RX_THRES_MASK_) + +#define DMAC_CMD (0xC0C) +#define DMAC_CMD_SWR_ BIT(31) +#define DMAC_CMD_TX_SWR_(channel) BIT(24 + (channel)) +#define DMAC_CMD_START_T_(channel) BIT(20 + (channel)) +#define DMAC_CMD_STOP_T_(channel) BIT(16 + (channel)) +#define DMAC_CMD_RX_SWR_(channel) BIT(8 + (channel)) +#define DMAC_CMD_START_R_(channel) BIT(4 + (channel)) +#define DMAC_CMD_STOP_R_(channel) BIT(0 + (channel)) + +#define DMAC_INT_STS (0xC10) +#define DMAC_INT_EN_SET (0xC14) +#define DMAC_INT_EN_CLR (0xC18) +#define DMAC_INT_BIT_RXFRM_(channel) BIT(16 + (channel)) +#define DMAC_INT_BIT_TX_IOC_(channel) BIT(0 + (channel)) + +#define RX_CFG_A(channel) (0xC40 + ((channel) << 6)) +#define RX_CFG_A_RX_WB_ON_INT_TMR_ BIT(30) +#define RX_CFG_A_RX_WB_THRES_MASK_ (0x1F000000) +#define RX_CFG_A_RX_WB_THRES_SET_(val) \ + ((((u32)(val)) << 24) & RX_CFG_A_RX_WB_THRES_MASK_) +#define RX_CFG_A_RX_PF_THRES_MASK_ (0x001F0000) +#define RX_CFG_A_RX_PF_THRES_SET_(val) \ + ((((u32)(val)) << 16) & RX_CFG_A_RX_PF_THRES_MASK_) +#define RX_CFG_A_RX_PF_PRI_THRES_MASK_ (0x00001F00) +#define RX_CFG_A_RX_PF_PRI_THRES_SET_(val) \ + ((((u32)(val)) << 8) & RX_CFG_A_RX_PF_PRI_THRES_MASK_) +#define RX_CFG_A_RX_HP_WB_EN_ BIT(5) + +#define RX_CFG_B(channel) (0xC44 + ((channel) << 6)) +#define RX_CFG_B_TS_ALL_RX_ BIT(29) +#define RX_CFG_B_RX_PAD_MASK_ (0x03000000) +#define RX_CFG_B_RX_PAD_0_ (0x00000000) +#define RX_CFG_B_RX_PAD_2_ (0x02000000) +#define RX_CFG_B_RDMABL_512_ (0x00040000) +#define RX_CFG_B_RX_RING_LEN_MASK_ (0x0000FFFF) + +#define RX_BASE_ADDRH(channel) (0xC48 + ((channel) << 6)) + +#define RX_BASE_ADDRL(channel) (0xC4C + ((channel) << 6)) + +#define RX_HEAD_WRITEBACK_ADDRH(channel) (0xC50 + ((channel) << 6)) + +#define RX_HEAD_WRITEBACK_ADDRL(channel) (0xC54 + ((channel) << 6)) + +#define RX_HEAD(channel) (0xC58 + ((channel) << 6)) + +#define RX_TAIL(channel) (0xC5C + ((channel) << 6)) +#define RX_TAIL_SET_TOP_INT_EN_ BIT(30) +#define RX_TAIL_SET_TOP_INT_VEC_EN_ BIT(29) + +#define RX_CFG_C(channel) (0xC64 + ((channel) << 6)) +#define RX_CFG_C_RX_TOP_INT_EN_AUTO_CLR_ BIT(6) +#define RX_CFG_C_RX_INT_EN_R2C_ BIT(4) +#define RX_CFG_C_RX_DMA_INT_STS_AUTO_CLR_ BIT(3) +#define RX_CFG_C_RX_INT_STS_R2C_MODE_MASK_ (0x00000007) + +#define TX_CFG_A(channel) (0xD40 + ((channel) << 6)) +#define TX_CFG_A_TX_HP_WB_ON_INT_TMR_ BIT(30) +#define TX_CFG_A_TX_TMR_HPWB_SEL_IOC_ (0x10000000) +#define TX_CFG_A_TX_PF_THRES_MASK_ (0x001F0000) +#define TX_CFG_A_TX_PF_THRES_SET_(value) \ + ((((u32)(value)) << 16) & TX_CFG_A_TX_PF_THRES_MASK_) +#define TX_CFG_A_TX_PF_PRI_THRES_MASK_ (0x00001F00) +#define TX_CFG_A_TX_PF_PRI_THRES_SET_(value) \ + ((((u32)(value)) << 8) & TX_CFG_A_TX_PF_PRI_THRES_MASK_) +#define TX_CFG_A_TX_HP_WB_EN_ BIT(5) +#define TX_CFG_A_TX_HP_WB_THRES_MASK_ (0x0000000F) +#define TX_CFG_A_TX_HP_WB_THRES_SET_(value) \ + (((u32)(value)) & TX_CFG_A_TX_HP_WB_THRES_MASK_) + +#define TX_CFG_B(channel) (0xD44 + ((channel) << 6)) +#define TX_CFG_B_TDMABL_512_ (0x00040000) +#define TX_CFG_B_TX_RING_LEN_MASK_ (0x0000FFFF) + +#define TX_BASE_ADDRH(channel) (0xD48 + ((channel) << 6)) + +#define TX_BASE_ADDRL(channel) (0xD4C + ((channel) << 6)) + +#define TX_HEAD_WRITEBACK_ADDRH(channel) (0xD50 + ((channel) << 6)) + +#define TX_HEAD_WRITEBACK_ADDRL(channel) (0xD54 + ((channel) << 6)) + +#define TX_HEAD(channel) (0xD58 + ((channel) << 6)) + +#define TX_TAIL(channel) (0xD5C + ((channel) << 6)) +#define TX_TAIL_SET_DMAC_INT_EN_ BIT(31) +#define TX_TAIL_SET_TOP_INT_EN_ BIT(30) +#define TX_TAIL_SET_TOP_INT_VEC_EN_ BIT(29) + +#define TX_CFG_C(channel) (0xD64 + ((channel) << 6)) +#define TX_CFG_C_TX_TOP_INT_EN_AUTO_CLR_ BIT(6) +#define TX_CFG_C_TX_DMA_INT_EN_AUTO_CLR_ BIT(5) +#define TX_CFG_C_TX_INT_EN_R2C_ BIT(4) +#define TX_CFG_C_TX_DMA_INT_STS_AUTO_CLR_ BIT(3) +#define TX_CFG_C_TX_INT_STS_R2C_MODE_MASK_ (0x00000007) + +#define OTP_PWR_DN (0x1000) +#define OTP_PWR_DN_PWRDN_N_ BIT(0) + +#define OTP_ADDR1 (0x1004) +#define OTP_ADDR1_15_11_MASK_ (0x1F) + +#define OTP_ADDR2 (0x1008) +#define OTP_ADDR2_10_3_MASK_ (0xFF) + +#define OTP_PRGM_DATA (0x1010) + +#define OTP_PRGM_MODE (0x1014) +#define OTP_PRGM_MODE_BYTE_ BIT(0) + +#define OTP_TST_CMD (0x1024) +#define OTP_TST_CMD_PRGVRFY_ BIT(3) + +#define OTP_CMD_GO (0x1028) +#define OTP_CMD_GO_GO_ BIT(0) + +#define OTP_STATUS (0x1030) +#define OTP_STATUS_BUSY_ BIT(0) + +/* MAC statistics registers */ +#define STAT_RX_FCS_ERRORS (0x1200) +#define STAT_RX_ALIGNMENT_ERRORS (0x1204) +#define STAT_RX_FRAGMENT_ERRORS (0x1208) +#define STAT_RX_JABBER_ERRORS (0x120C) +#define STAT_RX_UNDERSIZE_FRAME_ERRORS (0x1210) +#define STAT_RX_OVERSIZE_FRAME_ERRORS (0x1214) +#define STAT_RX_DROPPED_FRAMES (0x1218) +#define STAT_RX_UNICAST_BYTE_COUNT (0x121C) +#define STAT_RX_BROADCAST_BYTE_COUNT (0x1220) +#define STAT_RX_MULTICAST_BYTE_COUNT (0x1224) +#define STAT_RX_UNICAST_FRAMES (0x1228) +#define STAT_RX_BROADCAST_FRAMES (0x122C) +#define STAT_RX_MULTICAST_FRAMES (0x1230) +#define STAT_RX_PAUSE_FRAMES (0x1234) +#define STAT_RX_64_BYTE_FRAMES (0x1238) +#define STAT_RX_65_127_BYTE_FRAMES (0x123C) +#define STAT_RX_128_255_BYTE_FRAMES (0x1240) +#define STAT_RX_256_511_BYTES_FRAMES (0x1244) +#define STAT_RX_512_1023_BYTE_FRAMES (0x1248) +#define STAT_RX_1024_1518_BYTE_FRAMES (0x124C) +#define STAT_RX_GREATER_1518_BYTE_FRAMES (0x1250) +#define STAT_RX_TOTAL_FRAMES (0x1254) +#define STAT_EEE_RX_LPI_TRANSITIONS (0x1258) +#define STAT_EEE_RX_LPI_TIME (0x125C) +#define STAT_RX_COUNTER_ROLLOVER_STATUS (0x127C) + +#define STAT_TX_FCS_ERRORS (0x1280) +#define STAT_TX_EXCESS_DEFERRAL_ERRORS (0x1284) +#define STAT_TX_CARRIER_ERRORS (0x1288) +#define STAT_TX_BAD_BYTE_COUNT (0x128C) +#define STAT_TX_SINGLE_COLLISIONS (0x1290) +#define STAT_TX_MULTIPLE_COLLISIONS (0x1294) +#define STAT_TX_EXCESSIVE_COLLISION (0x1298) +#define STAT_TX_LATE_COLLISIONS (0x129C) +#define STAT_TX_UNICAST_BYTE_COUNT (0x12A0) +#define STAT_TX_BROADCAST_BYTE_COUNT (0x12A4) +#define STAT_TX_MULTICAST_BYTE_COUNT (0x12A8) +#define STAT_TX_UNICAST_FRAMES (0x12AC) +#define STAT_TX_BROADCAST_FRAMES (0x12B0) +#define STAT_TX_MULTICAST_FRAMES (0x12B4) +#define STAT_TX_PAUSE_FRAMES (0x12B8) +#define STAT_TX_64_BYTE_FRAMES (0x12BC) +#define STAT_TX_65_127_BYTE_FRAMES (0x12C0) +#define STAT_TX_128_255_BYTE_FRAMES (0x12C4) +#define STAT_TX_256_511_BYTES_FRAMES (0x12C8) +#define STAT_TX_512_1023_BYTE_FRAMES (0x12CC) +#define STAT_TX_1024_1518_BYTE_FRAMES (0x12D0) +#define STAT_TX_GREATER_1518_BYTE_FRAMES (0x12D4) +#define STAT_TX_TOTAL_FRAMES (0x12D8) +#define STAT_EEE_TX_LPI_TRANSITIONS (0x12DC) +#define STAT_EEE_TX_LPI_TIME (0x12E0) +#define STAT_TX_COUNTER_ROLLOVER_STATUS (0x12FC) + +/* End of Register definitions */ + +#define LAN743X_MAX_RX_CHANNELS (4) +#define LAN743X_MAX_TX_CHANNELS (1) +struct lan743x_adapter; + +#define LAN743X_USED_RX_CHANNELS (4) +#define LAN743X_USED_TX_CHANNELS (1) +#define LAN743X_INT_MOD (400) + +#if (LAN743X_USED_RX_CHANNELS > LAN743X_MAX_RX_CHANNELS) +#error Invalid LAN743X_USED_RX_CHANNELS +#endif +#if (LAN743X_USED_TX_CHANNELS > LAN743X_MAX_TX_CHANNELS) +#error Invalid LAN743X_USED_TX_CHANNELS +#endif + +/* PCI */ +/* SMSC acquired EFAR late 1990's, MCHP acquired SMSC 2012 */ +#define PCI_VENDOR_ID_SMSC PCI_VENDOR_ID_EFAR +#define PCI_DEVICE_ID_SMSC_LAN7430 (0x7430) +#define PCI_DEVICE_ID_SMSC_LAN7431 (0x7431) + +#define PCI_CONFIG_LENGTH (0x1000) + +/* CSR */ +#define CSR_LENGTH (0x2000) + +#define LAN743X_CSR_FLAG_IS_A0 BIT(0) +#define LAN743X_CSR_FLAG_IS_B0 BIT(1) +#define LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR BIT(8) + +struct lan743x_csr { + u32 flags; + u8 __iomem *csr_address; + u32 id_rev; + u32 fpga_rev; +}; + +/* INTERRUPTS */ +typedef void(*lan743x_vector_handler)(void *context, u32 int_sts, u32 flags); + +#define LAN743X_VECTOR_FLAG_IRQ_SHARED BIT(0) +#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ BIT(1) +#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C BIT(2) +#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C BIT(3) +#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK BIT(4) +#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR BIT(5) +#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C BIT(6) +#define LAN743X_VECTOR_FLAG_MASTER_ENABLE_CLEAR BIT(7) +#define LAN743X_VECTOR_FLAG_MASTER_ENABLE_SET BIT(8) +#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR BIT(9) +#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET BIT(10) +#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR BIT(11) +#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET BIT(12) +#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR BIT(13) +#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET BIT(14) +#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR BIT(15) + +struct lan743x_vector { + int irq; + u32 flags; + struct lan743x_adapter *adapter; + int vector_index; + u32 int_mask; + lan743x_vector_handler handler; + void *context; +}; + +#define LAN743X_MAX_VECTOR_COUNT (8) + +struct lan743x_intr { + int flags; + + unsigned int irq; + + struct lan743x_vector vector_list[LAN743X_MAX_VECTOR_COUNT]; + int number_of_vectors; + bool using_vectors; + + int software_isr_flag; +}; + +#define LAN743X_MAX_FRAME_SIZE (9 * 1024) + +/* PHY */ +struct lan743x_phy { + bool fc_autoneg; + u8 fc_request_control; +}; + +/* TX */ +struct lan743x_tx_descriptor; +struct lan743x_tx_buffer_info; + +#define GPIO_QUEUE_STARTED (0) +#define GPIO_TX_FUNCTION (1) +#define GPIO_TX_COMPLETION (2) +#define GPIO_TX_FRAGMENT (3) + +#define TX_FRAME_FLAG_IN_PROGRESS BIT(0) + +#define TX_TS_FLAG_TIMESTAMPING_ENABLED BIT(0) +#define TX_TS_FLAG_ONE_STEP_SYNC BIT(1) + +struct lan743x_tx { + struct lan743x_adapter *adapter; + u32 ts_flags; + u32 vector_flags; + int channel_number; + + int ring_size; + size_t ring_allocation_size; + struct lan743x_tx_descriptor *ring_cpu_ptr; + dma_addr_t ring_dma_ptr; + /* ring_lock: used to prevent concurrent access to tx ring */ + spinlock_t ring_lock; + u32 frame_flags; + u32 frame_first; + u32 frame_data0; + u32 frame_tail; + + struct lan743x_tx_buffer_info *buffer_info; + + u32 *head_cpu_ptr; + dma_addr_t head_dma_ptr; + int last_head; + int last_tail; + + struct napi_struct napi; + + struct sk_buff *overflow_skb; +}; + +void lan743x_tx_set_timestamping_mode(struct lan743x_tx *tx, + bool enable_timestamping, + bool enable_onestep_sync); + +/* RX */ +struct lan743x_rx_descriptor; +struct lan743x_rx_buffer_info; + +struct lan743x_rx { + struct lan743x_adapter *adapter; + u32 vector_flags; + int channel_number; + + int ring_size; + size_t ring_allocation_size; + struct lan743x_rx_descriptor *ring_cpu_ptr; + dma_addr_t ring_dma_ptr; + + struct lan743x_rx_buffer_info *buffer_info; + + u32 *head_cpu_ptr; + dma_addr_t head_dma_ptr; + u32 last_head; + u32 last_tail; + + struct napi_struct napi; + + u32 frame_count; +}; + +struct lan743x_adapter { + struct net_device *netdev; + struct mii_bus *mdiobus; + int msg_enable; +#ifdef CONFIG_PM + u32 wolopts; +#endif + struct pci_dev *pdev; + struct lan743x_csr csr; + struct lan743x_intr intr; + + struct lan743x_gpio gpio; + struct lan743x_ptp ptp; + + u8 mac_address[ETH_ALEN]; + + struct lan743x_phy phy; + struct lan743x_tx tx[LAN743X_MAX_TX_CHANNELS]; + struct lan743x_rx rx[LAN743X_MAX_RX_CHANNELS]; +}; + +#define LAN743X_COMPONENT_FLAG_RX(channel) BIT(20 + (channel)) + +#define INTR_FLAG_IRQ_REQUESTED(vector_index) BIT(0 + vector_index) +#define INTR_FLAG_MSI_ENABLED BIT(8) +#define INTR_FLAG_MSIX_ENABLED BIT(9) + +#define MAC_MII_READ 1 +#define MAC_MII_WRITE 0 + +#define PHY_FLAG_OPENED BIT(0) +#define PHY_FLAG_ATTACHED BIT(1) + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +#define DMA_ADDR_HIGH32(dma_addr) ((u32)(((dma_addr) >> 32) & 0xFFFFFFFF)) +#else +#define DMA_ADDR_HIGH32(dma_addr) ((u32)(0)) +#endif +#define DMA_ADDR_LOW32(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF)) +#define DMA_DESCRIPTOR_SPACING_16 (16) +#define DMA_DESCRIPTOR_SPACING_32 (32) +#define DMA_DESCRIPTOR_SPACING_64 (64) +#define DMA_DESCRIPTOR_SPACING_128 (128) +#define DEFAULT_DMA_DESCRIPTOR_SPACING (L1_CACHE_BYTES) + +#define DMAC_CHANNEL_STATE_SET(start_bit, stop_bit) \ + (((start_bit) ? 2 : 0) | ((stop_bit) ? 1 : 0)) +#define DMAC_CHANNEL_STATE_INITIAL DMAC_CHANNEL_STATE_SET(0, 0) +#define DMAC_CHANNEL_STATE_STARTED DMAC_CHANNEL_STATE_SET(1, 0) +#define DMAC_CHANNEL_STATE_STOP_PENDING DMAC_CHANNEL_STATE_SET(1, 1) +#define DMAC_CHANNEL_STATE_STOPPED DMAC_CHANNEL_STATE_SET(0, 1) + +/* TX Descriptor bits */ +#define TX_DESC_DATA0_DTYPE_MASK_ (0xC0000000) +#define TX_DESC_DATA0_DTYPE_DATA_ (0x00000000) +#define TX_DESC_DATA0_DTYPE_EXT_ (0x40000000) +#define TX_DESC_DATA0_FS_ (0x20000000) +#define TX_DESC_DATA0_LS_ (0x10000000) +#define TX_DESC_DATA0_EXT_ (0x08000000) +#define TX_DESC_DATA0_IOC_ (0x04000000) +#define TX_DESC_DATA0_ICE_ (0x00400000) +#define TX_DESC_DATA0_IPE_ (0x00200000) +#define TX_DESC_DATA0_TPE_ (0x00100000) +#define TX_DESC_DATA0_FCS_ (0x00020000) +#define TX_DESC_DATA0_TSE_ (0x00010000) +#define TX_DESC_DATA0_BUF_LENGTH_MASK_ (0x0000FFFF) +#define TX_DESC_DATA0_EXT_LSO_ (0x00200000) +#define TX_DESC_DATA0_EXT_PAY_LENGTH_MASK_ (0x000FFFFF) +#define TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_ (0x3FFF0000) + +struct lan743x_tx_descriptor { + u32 data0; + u32 data1; + u32 data2; + u32 data3; +} __aligned(DEFAULT_DMA_DESCRIPTOR_SPACING); + +#define TX_BUFFER_INFO_FLAG_ACTIVE BIT(0) +#define TX_BUFFER_INFO_FLAG_TIMESTAMP_REQUESTED BIT(1) +#define TX_BUFFER_INFO_FLAG_IGNORE_SYNC BIT(2) +#define TX_BUFFER_INFO_FLAG_SKB_FRAGMENT BIT(3) +struct lan743x_tx_buffer_info { + int flags; + struct sk_buff *skb; + dma_addr_t dma_ptr; + unsigned int buffer_length; +}; + +#define LAN743X_TX_RING_SIZE (50) + +/* OWN bit is set. ie, Descs are owned by RX DMAC */ +#define RX_DESC_DATA0_OWN_ (0x00008000) +/* OWN bit is clear. ie, Descs are owned by host */ +#define RX_DESC_DATA0_FS_ (0x80000000) +#define RX_DESC_DATA0_LS_ (0x40000000) +#define RX_DESC_DATA0_FRAME_LENGTH_MASK_ (0x3FFF0000) +#define RX_DESC_DATA0_FRAME_LENGTH_GET_(data0) \ + (((data0) & RX_DESC_DATA0_FRAME_LENGTH_MASK_) >> 16) +#define RX_DESC_DATA0_EXT_ (0x00004000) +#define RX_DESC_DATA0_BUF_LENGTH_MASK_ (0x00003FFF) +#define RX_DESC_DATA2_TS_NS_MASK_ (0x3FFFFFFF) + +#if ((NET_IP_ALIGN != 0) && (NET_IP_ALIGN != 2)) +#error NET_IP_ALIGN must be 0 or 2 +#endif + +#define RX_HEAD_PADDING NET_IP_ALIGN + +struct lan743x_rx_descriptor { + u32 data0; + u32 data1; + u32 data2; + u32 data3; +} __aligned(DEFAULT_DMA_DESCRIPTOR_SPACING); + +#define RX_BUFFER_INFO_FLAG_ACTIVE BIT(0) +struct lan743x_rx_buffer_info { + int flags; + struct sk_buff *skb; + + dma_addr_t dma_ptr; + unsigned int buffer_length; +}; + +#define LAN743X_RX_RING_SIZE (65) + +#define RX_PROCESS_RESULT_NOTHING_TO_DO (0) +#define RX_PROCESS_RESULT_PACKET_RECEIVED (1) +#define RX_PROCESS_RESULT_PACKET_DROPPED (2) + +u32 lan743x_csr_read(struct lan743x_adapter *adapter, int offset); +void lan743x_csr_write(struct lan743x_adapter *adapter, int offset, u32 data); + +#endif /* _LAN743X_H */ diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c new file mode 100644 index 000000000..ccdf9123f --- /dev/null +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -0,0 +1,1159 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2018 Microchip Technology Inc. */ + +#include <linux/netdevice.h> +#include "lan743x_main.h" + +#include <linux/ptp_clock_kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/net_tstamp.h> + +#include "lan743x_ptp.h" + +#define LAN743X_NUMBER_OF_GPIO (12) +#define LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB (31249999) +#define LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM (2047999934) + +static bool lan743x_ptp_is_enabled(struct lan743x_adapter *adapter); +static void lan743x_ptp_enable(struct lan743x_adapter *adapter); +static void lan743x_ptp_disable(struct lan743x_adapter *adapter); +static void lan743x_ptp_reset(struct lan743x_adapter *adapter); +static void lan743x_ptp_clock_set(struct lan743x_adapter *adapter, + u32 seconds, u32 nano_seconds, + u32 sub_nano_seconds); + +int lan743x_gpio_init(struct lan743x_adapter *adapter) +{ + struct lan743x_gpio *gpio = &adapter->gpio; + + spin_lock_init(&gpio->gpio_lock); + + gpio->gpio_cfg0 = 0; /* set all direction to input, data = 0 */ + gpio->gpio_cfg1 = 0x0FFF0000;/* disable all gpio, set to open drain */ + gpio->gpio_cfg2 = 0;/* set all to 1588 low polarity level */ + gpio->gpio_cfg3 = 0;/* disable all 1588 output */ + lan743x_csr_write(adapter, GPIO_CFG0, gpio->gpio_cfg0); + lan743x_csr_write(adapter, GPIO_CFG1, gpio->gpio_cfg1); + lan743x_csr_write(adapter, GPIO_CFG2, gpio->gpio_cfg2); + lan743x_csr_write(adapter, GPIO_CFG3, gpio->gpio_cfg3); + + return 0; +} + +static void lan743x_ptp_wait_till_cmd_done(struct lan743x_adapter *adapter, + u32 bit_mask) +{ + int timeout = 1000; + u32 data = 0; + + while (timeout && + (data = (lan743x_csr_read(adapter, PTP_CMD_CTL) & + bit_mask))) { + usleep_range(1000, 20000); + timeout--; + } + if (data) { + netif_err(adapter, drv, adapter->netdev, + "timeout waiting for cmd to be done, cmd = 0x%08X\n", + bit_mask); + } +} + +static void lan743x_ptp_tx_ts_enqueue_ts(struct lan743x_adapter *adapter, + u32 seconds, u32 nano_seconds, + u32 header) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + spin_lock_bh(&ptp->tx_ts_lock); + if (ptp->tx_ts_queue_size < LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS) { + ptp->tx_ts_seconds_queue[ptp->tx_ts_queue_size] = seconds; + ptp->tx_ts_nseconds_queue[ptp->tx_ts_queue_size] = nano_seconds; + ptp->tx_ts_header_queue[ptp->tx_ts_queue_size] = header; + ptp->tx_ts_queue_size++; + } else { + netif_err(adapter, drv, adapter->netdev, + "tx ts queue overflow\n"); + } + spin_unlock_bh(&ptp->tx_ts_lock); +} + +static void lan743x_ptp_tx_ts_complete(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + struct skb_shared_hwtstamps tstamps; + u32 header, nseconds, seconds; + bool ignore_sync = false; + struct sk_buff *skb; + int c, i; + + spin_lock_bh(&ptp->tx_ts_lock); + c = ptp->tx_ts_skb_queue_size; + + if (c > ptp->tx_ts_queue_size) + c = ptp->tx_ts_queue_size; + if (c <= 0) + goto done; + + for (i = 0; i < c; i++) { + ignore_sync = ((ptp->tx_ts_ignore_sync_queue & + BIT(i)) != 0); + skb = ptp->tx_ts_skb_queue[i]; + nseconds = ptp->tx_ts_nseconds_queue[i]; + seconds = ptp->tx_ts_seconds_queue[i]; + header = ptp->tx_ts_header_queue[i]; + + memset(&tstamps, 0, sizeof(tstamps)); + tstamps.hwtstamp = ktime_set(seconds, nseconds); + if (!ignore_sync || + ((header & PTP_TX_MSG_HEADER_MSG_TYPE_) != + PTP_TX_MSG_HEADER_MSG_TYPE_SYNC_)) + skb_tstamp_tx(skb, &tstamps); + + dev_kfree_skb(skb); + + ptp->tx_ts_skb_queue[i] = NULL; + ptp->tx_ts_seconds_queue[i] = 0; + ptp->tx_ts_nseconds_queue[i] = 0; + ptp->tx_ts_header_queue[i] = 0; + } + + /* shift queue */ + ptp->tx_ts_ignore_sync_queue >>= c; + for (i = c; i < LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS; i++) { + ptp->tx_ts_skb_queue[i - c] = ptp->tx_ts_skb_queue[i]; + ptp->tx_ts_seconds_queue[i - c] = ptp->tx_ts_seconds_queue[i]; + ptp->tx_ts_nseconds_queue[i - c] = ptp->tx_ts_nseconds_queue[i]; + ptp->tx_ts_header_queue[i - c] = ptp->tx_ts_header_queue[i]; + + ptp->tx_ts_skb_queue[i] = NULL; + ptp->tx_ts_seconds_queue[i] = 0; + ptp->tx_ts_nseconds_queue[i] = 0; + ptp->tx_ts_header_queue[i] = 0; + } + ptp->tx_ts_skb_queue_size -= c; + ptp->tx_ts_queue_size -= c; +done: + ptp->pending_tx_timestamps -= c; + spin_unlock_bh(&ptp->tx_ts_lock); +} + +static int lan743x_ptp_reserve_event_ch(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + int result = -ENODEV; + int index = 0; + + mutex_lock(&ptp->command_lock); + for (index = 0; index < LAN743X_PTP_NUMBER_OF_EVENT_CHANNELS; index++) { + if (!(test_bit(index, &ptp->used_event_ch))) { + ptp->used_event_ch |= BIT(index); + result = index; + break; + } + } + mutex_unlock(&ptp->command_lock); + return result; +} + +static void lan743x_ptp_release_event_ch(struct lan743x_adapter *adapter, + int event_channel) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + if (test_bit(event_channel, &ptp->used_event_ch)) { + ptp->used_event_ch &= ~BIT(event_channel); + } else { + netif_warn(adapter, drv, adapter->netdev, + "attempted release on a not used event_channel = %d\n", + event_channel); + } + mutex_unlock(&ptp->command_lock); +} + +static void lan743x_ptp_clock_get(struct lan743x_adapter *adapter, + u32 *seconds, u32 *nano_seconds, + u32 *sub_nano_seconds); +static void lan743x_ptp_clock_step(struct lan743x_adapter *adapter, + s64 time_step_ns); + +static int lan743x_gpio_rsrv_ptp_out(struct lan743x_adapter *adapter, + int bit, int ptp_channel) +{ + struct lan743x_gpio *gpio = &adapter->gpio; + unsigned long irq_flags = 0; + int bit_mask = BIT(bit); + int ret = -EBUSY; + + spin_lock_irqsave(&gpio->gpio_lock, irq_flags); + + if (!(gpio->used_bits & bit_mask)) { + gpio->used_bits |= bit_mask; + gpio->output_bits |= bit_mask; + gpio->ptp_bits |= bit_mask; + + /* set as output, and zero initial value */ + gpio->gpio_cfg0 |= GPIO_CFG0_GPIO_DIR_BIT_(bit); + gpio->gpio_cfg0 &= ~GPIO_CFG0_GPIO_DATA_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG0, gpio->gpio_cfg0); + + /* enable gpio, and set buffer type to push pull */ + gpio->gpio_cfg1 &= ~GPIO_CFG1_GPIOEN_BIT_(bit); + gpio->gpio_cfg1 |= GPIO_CFG1_GPIOBUF_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG1, gpio->gpio_cfg1); + + /* set 1588 polarity to high */ + gpio->gpio_cfg2 |= GPIO_CFG2_1588_POL_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG2, gpio->gpio_cfg2); + + if (!ptp_channel) { + /* use channel A */ + gpio->gpio_cfg3 &= ~GPIO_CFG3_1588_CH_SEL_BIT_(bit); + } else { + /* use channel B */ + gpio->gpio_cfg3 |= GPIO_CFG3_1588_CH_SEL_BIT_(bit); + } + gpio->gpio_cfg3 |= GPIO_CFG3_1588_OE_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG3, gpio->gpio_cfg3); + + ret = bit; + } + spin_unlock_irqrestore(&gpio->gpio_lock, irq_flags); + return ret; +} + +static void lan743x_gpio_release(struct lan743x_adapter *adapter, int bit) +{ + struct lan743x_gpio *gpio = &adapter->gpio; + unsigned long irq_flags = 0; + int bit_mask = BIT(bit); + + spin_lock_irqsave(&gpio->gpio_lock, irq_flags); + if (gpio->used_bits & bit_mask) { + gpio->used_bits &= ~bit_mask; + if (gpio->output_bits & bit_mask) { + gpio->output_bits &= ~bit_mask; + + if (gpio->ptp_bits & bit_mask) { + gpio->ptp_bits &= ~bit_mask; + /* disable ptp output */ + gpio->gpio_cfg3 &= ~GPIO_CFG3_1588_OE_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG3, + gpio->gpio_cfg3); + } + /* release gpio output */ + + /* disable gpio */ + gpio->gpio_cfg1 |= GPIO_CFG1_GPIOEN_BIT_(bit); + gpio->gpio_cfg1 &= ~GPIO_CFG1_GPIOBUF_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG1, gpio->gpio_cfg1); + + /* reset back to input */ + gpio->gpio_cfg0 &= ~GPIO_CFG0_GPIO_DIR_BIT_(bit); + gpio->gpio_cfg0 &= ~GPIO_CFG0_GPIO_DATA_BIT_(bit); + lan743x_csr_write(adapter, GPIO_CFG0, gpio->gpio_cfg0); + } + } + spin_unlock_irqrestore(&gpio->gpio_lock, irq_flags); +} + +static int lan743x_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + u32 lan743x_rate_adj = 0; + bool positive = true; + u64 u64_delta = 0; + + if ((scaled_ppm < (-LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM)) || + scaled_ppm > LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM) { + return -EINVAL; + } + if (scaled_ppm > 0) { + u64_delta = (u64)scaled_ppm; + positive = true; + } else { + u64_delta = (u64)(-scaled_ppm); + positive = false; + } + u64_delta = (u64_delta << 19); + lan743x_rate_adj = div_u64(u64_delta, 1000000); + + if (positive) + lan743x_rate_adj |= PTP_CLOCK_RATE_ADJ_DIR_; + + lan743x_csr_write(adapter, PTP_CLOCK_RATE_ADJ, + lan743x_rate_adj); + + return 0; +} + +static int lan743x_ptpci_adjfreq(struct ptp_clock_info *ptpci, s32 delta_ppb) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + u32 lan743x_rate_adj = 0; + bool positive = true; + u32 u32_delta = 0; + u64 u64_delta = 0; + + if ((delta_ppb < (-LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB)) || + delta_ppb > LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB) { + return -EINVAL; + } + if (delta_ppb > 0) { + u32_delta = (u32)delta_ppb; + positive = true; + } else { + u32_delta = (u32)(-delta_ppb); + positive = false; + } + u64_delta = (((u64)u32_delta) << 35); + lan743x_rate_adj = div_u64(u64_delta, 1000000000); + + if (positive) + lan743x_rate_adj |= PTP_CLOCK_RATE_ADJ_DIR_; + + lan743x_csr_write(adapter, PTP_CLOCK_RATE_ADJ, + lan743x_rate_adj); + + return 0; +} + +static int lan743x_ptpci_adjtime(struct ptp_clock_info *ptpci, s64 delta) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + + lan743x_ptp_clock_step(adapter, delta); + + return 0; +} + +static int lan743x_ptpci_gettime64(struct ptp_clock_info *ptpci, + struct timespec64 *ts) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + u32 nano_seconds = 0; + u32 seconds = 0; + + lan743x_ptp_clock_get(adapter, &seconds, &nano_seconds, NULL); + ts->tv_sec = seconds; + ts->tv_nsec = nano_seconds; + + return 0; +} + +static int lan743x_ptpci_settime64(struct ptp_clock_info *ptpci, + const struct timespec64 *ts) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + u32 nano_seconds = 0; + u32 seconds = 0; + + if (ts) { + if (ts->tv_sec > 0xFFFFFFFFLL || + ts->tv_sec < 0) { + netif_warn(adapter, drv, adapter->netdev, + "ts->tv_sec out of range, %lld\n", + ts->tv_sec); + return -ERANGE; + } + if (ts->tv_nsec >= 1000000000L || + ts->tv_nsec < 0) { + netif_warn(adapter, drv, adapter->netdev, + "ts->tv_nsec out of range, %ld\n", + ts->tv_nsec); + return -ERANGE; + } + seconds = ts->tv_sec; + nano_seconds = ts->tv_nsec; + lan743x_ptp_clock_set(adapter, seconds, nano_seconds, 0); + } else { + netif_warn(adapter, drv, adapter->netdev, "ts == NULL\n"); + return -EINVAL; + } + + return 0; +} + +static void lan743x_ptp_perout_off(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + u32 general_config = 0; + + if (ptp->perout_gpio_bit >= 0) { + lan743x_gpio_release(adapter, ptp->perout_gpio_bit); + ptp->perout_gpio_bit = -1; + } + + if (ptp->perout_event_ch >= 0) { + /* set target to far in the future, effectively disabling it */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_SEC_X(ptp->perout_event_ch), + 0xFFFF0000); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_NS_X(ptp->perout_event_ch), + 0); + + general_config = lan743x_csr_read(adapter, PTP_GENERAL_CONFIG); + general_config |= PTP_GENERAL_CONFIG_RELOAD_ADD_X_ + (ptp->perout_event_ch); + lan743x_csr_write(adapter, PTP_GENERAL_CONFIG, general_config); + lan743x_ptp_release_event_ch(adapter, ptp->perout_event_ch); + ptp->perout_event_ch = -1; + } +} + +static int lan743x_ptp_perout(struct lan743x_adapter *adapter, int on, + struct ptp_perout_request *perout) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + u32 period_sec = 0, period_nsec = 0; + u32 start_sec = 0, start_nsec = 0; + u32 general_config = 0; + int pulse_width = 0; + int perout_bit = 0; + + if (!on) { + lan743x_ptp_perout_off(adapter); + return 0; + } + + if (ptp->perout_event_ch >= 0 || + ptp->perout_gpio_bit >= 0) { + /* already on, turn off first */ + lan743x_ptp_perout_off(adapter); + } + + ptp->perout_event_ch = lan743x_ptp_reserve_event_ch(adapter); + if (ptp->perout_event_ch < 0) { + netif_warn(adapter, drv, adapter->netdev, + "Failed to reserve event channel for PEROUT\n"); + goto failed; + } + + switch (adapter->csr.id_rev & ID_REV_ID_MASK_) { + case ID_REV_ID_LAN7430_: + perout_bit = 2;/* GPIO 2 is preferred on EVB LAN7430 */ + break; + case ID_REV_ID_LAN7431_: + perout_bit = 4;/* GPIO 4 is preferred on EVB LAN7431 */ + break; + } + + ptp->perout_gpio_bit = lan743x_gpio_rsrv_ptp_out(adapter, + perout_bit, + ptp->perout_event_ch); + + if (ptp->perout_gpio_bit < 0) { + netif_warn(adapter, drv, adapter->netdev, + "Failed to reserve gpio %d for PEROUT\n", + perout_bit); + goto failed; + } + + start_sec = perout->start.sec; + start_sec += perout->start.nsec / 1000000000; + start_nsec = perout->start.nsec % 1000000000; + + period_sec = perout->period.sec; + period_sec += perout->period.nsec / 1000000000; + period_nsec = perout->period.nsec % 1000000000; + + if (period_sec == 0) { + if (period_nsec >= 400000000) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_; + } else if (period_nsec >= 20000000) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_10MS_; + } else if (period_nsec >= 2000000) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_1MS_; + } else if (period_nsec >= 200000) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_100US_; + } else if (period_nsec >= 20000) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_10US_; + } else if (period_nsec >= 200) { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_100NS_; + } else { + netif_warn(adapter, drv, adapter->netdev, + "perout period too small, minimum is 200nS\n"); + goto failed; + } + } else { + pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_; + } + + /* turn off by setting target far in future */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_SEC_X(ptp->perout_event_ch), + 0xFFFF0000); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_NS_X(ptp->perout_event_ch), 0); + + /* Configure to pulse every period */ + general_config = lan743x_csr_read(adapter, PTP_GENERAL_CONFIG); + general_config &= ~(PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_ + (ptp->perout_event_ch)); + general_config |= PTP_GENERAL_CONFIG_CLOCK_EVENT_X_SET_ + (ptp->perout_event_ch, pulse_width); + general_config &= ~PTP_GENERAL_CONFIG_RELOAD_ADD_X_ + (ptp->perout_event_ch); + lan743x_csr_write(adapter, PTP_GENERAL_CONFIG, general_config); + + /* set the reload to one toggle cycle */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_RELOAD_SEC_X(ptp->perout_event_ch), + period_sec); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_RELOAD_NS_X(ptp->perout_event_ch), + period_nsec); + + /* set the start time */ + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_SEC_X(ptp->perout_event_ch), + start_sec); + lan743x_csr_write(adapter, + PTP_CLOCK_TARGET_NS_X(ptp->perout_event_ch), + start_nsec); + + return 0; + +failed: + lan743x_ptp_perout_off(adapter); + return -ENODEV; +} + +static int lan743x_ptpci_enable(struct ptp_clock_info *ptpci, + struct ptp_clock_request *request, int on) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + + if (request) { + switch (request->type) { + case PTP_CLK_REQ_EXTTS: + return -EINVAL; + case PTP_CLK_REQ_PEROUT: + if (request->perout.index == 0) + return lan743x_ptp_perout(adapter, on, + &request->perout); + return -EINVAL; + case PTP_CLK_REQ_PPS: + return -EINVAL; + default: + netif_err(adapter, drv, adapter->netdev, + "request->type == %d, Unknown\n", + request->type); + break; + } + } else { + netif_err(adapter, drv, adapter->netdev, "request == NULL\n"); + } + return 0; +} + +static long lan743x_ptpci_do_aux_work(struct ptp_clock_info *ptpci) +{ + struct lan743x_ptp *ptp = + container_of(ptpci, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(ptp, struct lan743x_adapter, ptp); + u32 cap_info, cause, header, nsec, seconds; + bool new_timestamp_available = false; + int count = 0; + + while ((count < 100) && + (lan743x_csr_read(adapter, PTP_INT_STS) & PTP_INT_BIT_TX_TS_)) { + count++; + cap_info = lan743x_csr_read(adapter, PTP_CAP_INFO); + + if (PTP_CAP_INFO_TX_TS_CNT_GET_(cap_info) > 0) { + seconds = lan743x_csr_read(adapter, + PTP_TX_EGRESS_SEC); + nsec = lan743x_csr_read(adapter, PTP_TX_EGRESS_NS); + cause = (nsec & + PTP_TX_EGRESS_NS_CAPTURE_CAUSE_MASK_); + header = lan743x_csr_read(adapter, + PTP_TX_MSG_HEADER); + + if (cause == PTP_TX_EGRESS_NS_CAPTURE_CAUSE_SW_) { + nsec &= PTP_TX_EGRESS_NS_TS_NS_MASK_; + lan743x_ptp_tx_ts_enqueue_ts(adapter, + seconds, nsec, + header); + new_timestamp_available = true; + } else if (cause == + PTP_TX_EGRESS_NS_CAPTURE_CAUSE_AUTO_) { + netif_err(adapter, drv, adapter->netdev, + "Auto capture cause not supported\n"); + } else { + netif_warn(adapter, drv, adapter->netdev, + "unknown tx timestamp capture cause\n"); + } + } else { + netif_warn(adapter, drv, adapter->netdev, + "TX TS INT but no TX TS CNT\n"); + } + lan743x_csr_write(adapter, PTP_INT_STS, PTP_INT_BIT_TX_TS_); + } + + if (new_timestamp_available) + lan743x_ptp_tx_ts_complete(adapter); + + lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_1588_); + + return -1; +} + +static void lan743x_ptp_clock_get(struct lan743x_adapter *adapter, + u32 *seconds, u32 *nano_seconds, + u32 *sub_nano_seconds) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + + lan743x_csr_write(adapter, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_); + lan743x_ptp_wait_till_cmd_done(adapter, PTP_CMD_CTL_PTP_CLOCK_READ_); + + if (seconds) + (*seconds) = lan743x_csr_read(adapter, PTP_CLOCK_SEC); + + if (nano_seconds) + (*nano_seconds) = lan743x_csr_read(adapter, PTP_CLOCK_NS); + + if (sub_nano_seconds) + (*sub_nano_seconds) = + lan743x_csr_read(adapter, PTP_CLOCK_SUBNS); + + mutex_unlock(&ptp->command_lock); +} + +static void lan743x_ptp_clock_step(struct lan743x_adapter *adapter, + s64 time_step_ns) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + u32 nano_seconds_step = 0; + u64 abs_time_step_ns = 0; + u32 unsigned_seconds = 0; + u32 nano_seconds = 0; + u32 remainder = 0; + s32 seconds = 0; + + if (time_step_ns > 15000000000LL) { + /* convert to clock set */ + lan743x_ptp_clock_get(adapter, &unsigned_seconds, + &nano_seconds, NULL); + unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL, + &remainder); + nano_seconds += remainder; + if (nano_seconds >= 1000000000) { + unsigned_seconds++; + nano_seconds -= 1000000000; + } + lan743x_ptp_clock_set(adapter, unsigned_seconds, + nano_seconds, 0); + return; + } else if (time_step_ns < -15000000000LL) { + /* convert to clock set */ + time_step_ns = -time_step_ns; + + lan743x_ptp_clock_get(adapter, &unsigned_seconds, + &nano_seconds, NULL); + unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL, + &remainder); + nano_seconds_step = remainder; + if (nano_seconds < nano_seconds_step) { + unsigned_seconds--; + nano_seconds += 1000000000; + } + nano_seconds -= nano_seconds_step; + lan743x_ptp_clock_set(adapter, unsigned_seconds, + nano_seconds, 0); + return; + } + + /* do clock step */ + if (time_step_ns >= 0) { + abs_time_step_ns = (u64)(time_step_ns); + seconds = (s32)div_u64_rem(abs_time_step_ns, 1000000000, + &remainder); + nano_seconds = (u32)remainder; + } else { + abs_time_step_ns = (u64)(-time_step_ns); + seconds = -((s32)div_u64_rem(abs_time_step_ns, 1000000000, + &remainder)); + nano_seconds = (u32)remainder; + if (nano_seconds > 0) { + /* subtracting nano seconds is not allowed + * convert to subtracting from seconds, + * and adding to nanoseconds + */ + seconds--; + nano_seconds = (1000000000 - nano_seconds); + } + } + + if (nano_seconds > 0) { + /* add 8 ns to cover the likely normal increment */ + nano_seconds += 8; + } + + if (nano_seconds >= 1000000000) { + /* carry into seconds */ + seconds++; + nano_seconds -= 1000000000; + } + + while (seconds) { + mutex_lock(&ptp->command_lock); + if (seconds > 0) { + u32 adjustment_value = (u32)seconds; + + if (adjustment_value > 0xF) + adjustment_value = 0xF; + lan743x_csr_write(adapter, PTP_CLOCK_STEP_ADJ, + PTP_CLOCK_STEP_ADJ_DIR_ | + adjustment_value); + seconds -= ((s32)adjustment_value); + } else { + u32 adjustment_value = (u32)(-seconds); + + if (adjustment_value > 0xF) + adjustment_value = 0xF; + lan743x_csr_write(adapter, PTP_CLOCK_STEP_ADJ, + adjustment_value); + seconds += ((s32)adjustment_value); + } + lan743x_csr_write(adapter, PTP_CMD_CTL, + PTP_CMD_CTL_PTP_CLOCK_STEP_SEC_); + lan743x_ptp_wait_till_cmd_done(adapter, + PTP_CMD_CTL_PTP_CLOCK_STEP_SEC_); + mutex_unlock(&ptp->command_lock); + } + if (nano_seconds) { + mutex_lock(&ptp->command_lock); + lan743x_csr_write(adapter, PTP_CLOCK_STEP_ADJ, + PTP_CLOCK_STEP_ADJ_DIR_ | + (nano_seconds & + PTP_CLOCK_STEP_ADJ_VALUE_MASK_)); + lan743x_csr_write(adapter, PTP_CMD_CTL, + PTP_CMD_CTL_PTP_CLK_STP_NSEC_); + lan743x_ptp_wait_till_cmd_done(adapter, + PTP_CMD_CTL_PTP_CLK_STP_NSEC_); + mutex_unlock(&ptp->command_lock); + } +} + +void lan743x_ptp_isr(void *context) +{ + struct lan743x_adapter *adapter = (struct lan743x_adapter *)context; + struct lan743x_ptp *ptp = NULL; + int enable_flag = 1; + u32 ptp_int_sts = 0; + + ptp = &adapter->ptp; + + lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_1588_); + + ptp_int_sts = lan743x_csr_read(adapter, PTP_INT_STS); + ptp_int_sts &= lan743x_csr_read(adapter, PTP_INT_EN_SET); + + if (ptp_int_sts & PTP_INT_BIT_TX_TS_) { + ptp_schedule_worker(ptp->ptp_clock, 0); + enable_flag = 0;/* tasklet will re-enable later */ + } + if (ptp_int_sts & PTP_INT_BIT_TX_SWTS_ERR_) { + netif_err(adapter, drv, adapter->netdev, + "PTP TX Software Timestamp Error\n"); + /* clear int status bit */ + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_BIT_TX_SWTS_ERR_); + } + if (ptp_int_sts & PTP_INT_BIT_TIMER_B_) { + /* clear int status bit */ + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_BIT_TIMER_B_); + } + if (ptp_int_sts & PTP_INT_BIT_TIMER_A_) { + /* clear int status bit */ + lan743x_csr_write(adapter, PTP_INT_STS, + PTP_INT_BIT_TIMER_A_); + } + + if (enable_flag) { + /* re-enable isr */ + lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_1588_); + } +} + +static void lan743x_ptp_tx_ts_enqueue_skb(struct lan743x_adapter *adapter, + struct sk_buff *skb, bool ignore_sync) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + spin_lock_bh(&ptp->tx_ts_lock); + if (ptp->tx_ts_skb_queue_size < LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS) { + ptp->tx_ts_skb_queue[ptp->tx_ts_skb_queue_size] = skb; + if (ignore_sync) + ptp->tx_ts_ignore_sync_queue |= + BIT(ptp->tx_ts_skb_queue_size); + ptp->tx_ts_skb_queue_size++; + } else { + /* this should never happen, so long as the tx channel + * calls and honors the result from + * lan743x_ptp_request_tx_timestamp + */ + netif_err(adapter, drv, adapter->netdev, + "tx ts skb queue overflow\n"); + dev_kfree_skb(skb); + } + spin_unlock_bh(&ptp->tx_ts_lock); +} + +static void lan743x_ptp_sync_to_system_clock(struct lan743x_adapter *adapter) +{ + struct timespec64 ts; + + ktime_get_clocktai_ts64(&ts); + + lan743x_ptp_clock_set(adapter, ts.tv_sec, ts.tv_nsec, 0); +} + +void lan743x_ptp_update_latency(struct lan743x_adapter *adapter, + u32 link_speed) +{ + switch (link_speed) { + case 10: + lan743x_csr_write(adapter, PTP_LATENCY, + PTP_LATENCY_TX_SET_(0) | + PTP_LATENCY_RX_SET_(0)); + break; + case 100: + lan743x_csr_write(adapter, PTP_LATENCY, + PTP_LATENCY_TX_SET_(181) | + PTP_LATENCY_RX_SET_(594)); + break; + case 1000: + lan743x_csr_write(adapter, PTP_LATENCY, + PTP_LATENCY_TX_SET_(30) | + PTP_LATENCY_RX_SET_(525)); + break; + } +} + +int lan743x_ptp_init(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_init(&ptp->command_lock); + spin_lock_init(&ptp->tx_ts_lock); + ptp->used_event_ch = 0; + ptp->perout_event_ch = -1; + ptp->perout_gpio_bit = -1; + return 0; +} + +int lan743x_ptp_open(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + int ret = -ENODEV; + u32 temp; + + lan743x_ptp_reset(adapter); + lan743x_ptp_sync_to_system_clock(adapter); + temp = lan743x_csr_read(adapter, PTP_TX_MOD2); + temp |= PTP_TX_MOD2_TX_PTP_CLR_UDPV4_CHKSUM_; + lan743x_csr_write(adapter, PTP_TX_MOD2, temp); + lan743x_ptp_enable(adapter); + lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_1588_); + lan743x_csr_write(adapter, PTP_INT_EN_SET, + PTP_INT_BIT_TX_SWTS_ERR_ | PTP_INT_BIT_TX_TS_); + ptp->flags |= PTP_FLAG_ISR_ENABLED; + + if (!IS_ENABLED(CONFIG_PTP_1588_CLOCK)) + return 0; + + snprintf(ptp->pin_config[0].name, 32, "lan743x_ptp_pin_0"); + ptp->pin_config[0].index = 0; + ptp->pin_config[0].func = PTP_PF_PEROUT; + ptp->pin_config[0].chan = 0; + + ptp->ptp_clock_info.owner = THIS_MODULE; + snprintf(ptp->ptp_clock_info.name, 16, "%pm", + adapter->netdev->dev_addr); + ptp->ptp_clock_info.max_adj = LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB; + ptp->ptp_clock_info.n_alarm = 0; + ptp->ptp_clock_info.n_ext_ts = 0; + ptp->ptp_clock_info.n_per_out = 1; + ptp->ptp_clock_info.n_pins = 0; + ptp->ptp_clock_info.pps = 0; + ptp->ptp_clock_info.pin_config = NULL; + ptp->ptp_clock_info.adjfine = lan743x_ptpci_adjfine; + ptp->ptp_clock_info.adjfreq = lan743x_ptpci_adjfreq; + ptp->ptp_clock_info.adjtime = lan743x_ptpci_adjtime; + ptp->ptp_clock_info.gettime64 = lan743x_ptpci_gettime64; + ptp->ptp_clock_info.getcrosststamp = NULL; + ptp->ptp_clock_info.settime64 = lan743x_ptpci_settime64; + ptp->ptp_clock_info.enable = lan743x_ptpci_enable; + ptp->ptp_clock_info.do_aux_work = lan743x_ptpci_do_aux_work; + ptp->ptp_clock_info.verify = NULL; + + ptp->ptp_clock = ptp_clock_register(&ptp->ptp_clock_info, + &adapter->pdev->dev); + + if (IS_ERR(ptp->ptp_clock)) { + netif_err(adapter, ifup, adapter->netdev, + "ptp_clock_register failed\n"); + goto done; + } + ptp->flags |= PTP_FLAG_PTP_CLOCK_REGISTERED; + netif_info(adapter, ifup, adapter->netdev, + "successfully registered ptp clock\n"); + + return 0; +done: + lan743x_ptp_close(adapter); + return ret; +} + +void lan743x_ptp_close(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + int index; + + if (IS_ENABLED(CONFIG_PTP_1588_CLOCK) && + ptp->flags & PTP_FLAG_PTP_CLOCK_REGISTERED) { + ptp_clock_unregister(ptp->ptp_clock); + ptp->ptp_clock = NULL; + ptp->flags &= ~PTP_FLAG_PTP_CLOCK_REGISTERED; + netif_info(adapter, drv, adapter->netdev, + "ptp clock unregister\n"); + } + + if (ptp->flags & PTP_FLAG_ISR_ENABLED) { + lan743x_csr_write(adapter, PTP_INT_EN_CLR, + PTP_INT_BIT_TX_SWTS_ERR_ | + PTP_INT_BIT_TX_TS_); + lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_1588_); + ptp->flags &= ~PTP_FLAG_ISR_ENABLED; + } + + /* clean up pending timestamp requests */ + lan743x_ptp_tx_ts_complete(adapter); + spin_lock_bh(&ptp->tx_ts_lock); + for (index = 0; + index < LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS; + index++) { + struct sk_buff *skb = ptp->tx_ts_skb_queue[index]; + + if (skb) + dev_kfree_skb(skb); + ptp->tx_ts_skb_queue[index] = NULL; + ptp->tx_ts_seconds_queue[index] = 0; + ptp->tx_ts_nseconds_queue[index] = 0; + } + ptp->tx_ts_skb_queue_size = 0; + ptp->tx_ts_queue_size = 0; + ptp->pending_tx_timestamps = 0; + spin_unlock_bh(&ptp->tx_ts_lock); + + lan743x_ptp_disable(adapter); +} + +void lan743x_ptp_set_sync_ts_insert(struct lan743x_adapter *adapter, + bool ts_insert_enable) +{ + u32 ptp_tx_mod = lan743x_csr_read(adapter, PTP_TX_MOD); + + if (ts_insert_enable) + ptp_tx_mod |= PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_; + else + ptp_tx_mod &= ~PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_; + + lan743x_csr_write(adapter, PTP_TX_MOD, ptp_tx_mod); +} + +static bool lan743x_ptp_is_enabled(struct lan743x_adapter *adapter) +{ + if (lan743x_csr_read(adapter, PTP_CMD_CTL) & PTP_CMD_CTL_PTP_ENABLE_) + return true; + return false; +} + +static void lan743x_ptp_enable(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + + if (lan743x_ptp_is_enabled(adapter)) { + netif_warn(adapter, drv, adapter->netdev, + "PTP already enabled\n"); + goto done; + } + lan743x_csr_write(adapter, PTP_CMD_CTL, PTP_CMD_CTL_PTP_ENABLE_); +done: + mutex_unlock(&ptp->command_lock); +} + +static void lan743x_ptp_disable(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + if (!lan743x_ptp_is_enabled(adapter)) { + netif_warn(adapter, drv, adapter->netdev, + "PTP already disabled\n"); + goto done; + } + lan743x_csr_write(adapter, PTP_CMD_CTL, PTP_CMD_CTL_PTP_DISABLE_); + lan743x_ptp_wait_till_cmd_done(adapter, PTP_CMD_CTL_PTP_ENABLE_); +done: + mutex_unlock(&ptp->command_lock); +} + +static void lan743x_ptp_reset(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + + if (lan743x_ptp_is_enabled(adapter)) { + netif_err(adapter, drv, adapter->netdev, + "Attempting reset while enabled\n"); + goto done; + } + + lan743x_csr_write(adapter, PTP_CMD_CTL, PTP_CMD_CTL_PTP_RESET_); + lan743x_ptp_wait_till_cmd_done(adapter, PTP_CMD_CTL_PTP_RESET_); +done: + mutex_unlock(&ptp->command_lock); +} + +static void lan743x_ptp_clock_set(struct lan743x_adapter *adapter, + u32 seconds, u32 nano_seconds, + u32 sub_nano_seconds) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + mutex_lock(&ptp->command_lock); + + lan743x_csr_write(adapter, PTP_CLOCK_SEC, seconds); + lan743x_csr_write(adapter, PTP_CLOCK_NS, nano_seconds); + lan743x_csr_write(adapter, PTP_CLOCK_SUBNS, sub_nano_seconds); + + lan743x_csr_write(adapter, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_); + lan743x_ptp_wait_till_cmd_done(adapter, PTP_CMD_CTL_PTP_CLOCK_LOAD_); + mutex_unlock(&ptp->command_lock); +} + +bool lan743x_ptp_request_tx_timestamp(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + bool result = false; + + spin_lock_bh(&ptp->tx_ts_lock); + if (ptp->pending_tx_timestamps < LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS) { + /* request granted */ + ptp->pending_tx_timestamps++; + result = true; + } + spin_unlock_bh(&ptp->tx_ts_lock); + return result; +} + +void lan743x_ptp_unrequest_tx_timestamp(struct lan743x_adapter *adapter) +{ + struct lan743x_ptp *ptp = &adapter->ptp; + + spin_lock_bh(&ptp->tx_ts_lock); + if (ptp->pending_tx_timestamps > 0) + ptp->pending_tx_timestamps--; + else + netif_err(adapter, drv, adapter->netdev, + "unrequest failed, pending_tx_timestamps==0\n"); + spin_unlock_bh(&ptp->tx_ts_lock); +} + +void lan743x_ptp_tx_timestamp_skb(struct lan743x_adapter *adapter, + struct sk_buff *skb, bool ignore_sync) +{ + lan743x_ptp_tx_ts_enqueue_skb(adapter, skb, ignore_sync); + + lan743x_ptp_tx_ts_complete(adapter); +} + +int lan743x_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + struct hwtstamp_config config; + int ret = 0; + int index; + + if (!ifr) { + netif_err(adapter, drv, adapter->netdev, + "SIOCSHWTSTAMP, ifr == NULL\n"); + return -EINVAL; + } + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + if (config.flags) { + netif_warn(adapter, drv, adapter->netdev, + "ignoring hwtstamp_config.flags == 0x%08X, expected 0\n", + config.flags); + } + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + for (index = 0; index < LAN743X_MAX_TX_CHANNELS; + index++) + lan743x_tx_set_timestamping_mode(&adapter->tx[index], + false, false); + lan743x_ptp_set_sync_ts_insert(adapter, false); + break; + case HWTSTAMP_TX_ON: + for (index = 0; index < LAN743X_MAX_TX_CHANNELS; + index++) + lan743x_tx_set_timestamping_mode(&adapter->tx[index], + true, false); + lan743x_ptp_set_sync_ts_insert(adapter, false); + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + for (index = 0; index < LAN743X_MAX_TX_CHANNELS; + index++) + lan743x_tx_set_timestamping_mode(&adapter->tx[index], + true, true); + + lan743x_ptp_set_sync_ts_insert(adapter, true); + break; + default: + netif_warn(adapter, drv, adapter->netdev, + " tx_type = %d, UNKNOWN\n", config.tx_type); + ret = -EINVAL; + break; + } + + if (!ret) + return copy_to_user(ifr->ifr_data, &config, + sizeof(config)) ? -EFAULT : 0; + return ret; +} diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.h b/drivers/net/ethernet/microchip/lan743x_ptp.h new file mode 100644 index 000000000..5fc1b3cd5 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan743x_ptp.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2018 Microchip Technology Inc. */ + +#ifndef _LAN743X_PTP_H +#define _LAN743X_PTP_H + +#include "linux/ptp_clock_kernel.h" +#include "linux/netdevice.h" + +struct lan743x_adapter; + +/* GPIO */ +struct lan743x_gpio { + /* gpio_lock: used to prevent concurrent access to gpio settings */ + spinlock_t gpio_lock; + + int used_bits; + int output_bits; + int ptp_bits; + u32 gpio_cfg0; + u32 gpio_cfg1; + u32 gpio_cfg2; + u32 gpio_cfg3; +}; + +int lan743x_gpio_init(struct lan743x_adapter *adapter); + +void lan743x_ptp_isr(void *context); +bool lan743x_ptp_request_tx_timestamp(struct lan743x_adapter *adapter); +void lan743x_ptp_unrequest_tx_timestamp(struct lan743x_adapter *adapter); +void lan743x_ptp_tx_timestamp_skb(struct lan743x_adapter *adapter, + struct sk_buff *skb, bool ignore_sync); +int lan743x_ptp_init(struct lan743x_adapter *adapter); +int lan743x_ptp_open(struct lan743x_adapter *adapter); +void lan743x_ptp_close(struct lan743x_adapter *adapter); +void lan743x_ptp_update_latency(struct lan743x_adapter *adapter, + u32 link_speed); + +int lan743x_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); + +#define LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS (4) + +#define PTP_FLAG_PTP_CLOCK_REGISTERED BIT(1) +#define PTP_FLAG_ISR_ENABLED BIT(2) + +struct lan743x_ptp { + int flags; + + /* command_lock: used to prevent concurrent ptp commands */ + struct mutex command_lock; + + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + struct ptp_pin_desc pin_config[1]; + +#define LAN743X_PTP_NUMBER_OF_EVENT_CHANNELS (2) + unsigned long used_event_ch; + + int perout_event_ch; + int perout_gpio_bit; + + /* tx_ts_lock: used to prevent concurrent access to timestamp arrays */ + spinlock_t tx_ts_lock; + int pending_tx_timestamps; + struct sk_buff *tx_ts_skb_queue[LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS]; + unsigned int tx_ts_ignore_sync_queue; + int tx_ts_skb_queue_size; + u32 tx_ts_seconds_queue[LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS]; + u32 tx_ts_nseconds_queue[LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS]; + u32 tx_ts_header_queue[LAN743X_PTP_NUMBER_OF_TX_TIMESTAMPS]; + int tx_ts_queue_size; +}; + +#endif /* _LAN743X_PTP_H */ |