[PATCH net-next v3 6/9] Altera TSE: Add main and header file for Altera Ethernet Driver
From: Vince Bridgers <hidden>
Date: 2014-03-11 22:43:08
Also in:
netdev
Subsystem:
altera triple speed ethernet driver, networking drivers, the rest · Maintainers:
Boon Khai Ng, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
This patch adds the main driver and header file for the Altera Triple
Speed Ethernet driver.
Signed-off-by: Vince Bridgers <redacted>
---
V3: - Reorder commits
- Use of_get_mac_address instead of of_get_property
- Change supplemental unicast address and hash filter property
names and types.
- Added check for failure from of_phy_connect and connect_local_phy
- Correct code to find mdio node child node
V2: - Address all comments from initial RFC (thanks Florian!)
- Moved asm/cacheflush.h to bottom of header includes
- Remove barriers since DMA APIs ensure memory consistency
- Add max-frame-size property
- Use accessor function of_get_phy_mode instead of custom function
- Remove custom function to access devicetree, use of APIs
- Deprecate use of Altera devicetree properties where standard
ones exist
- Remove use of netif_carrier_off - not necessary where it was used
- Move probing of phy to the probe routine instead of open
- use ether_addr_copy instead of memcpy
---
drivers/net/ethernet/altera/altera_tse.h | 486 ++++++++
drivers/net/ethernet/altera/altera_tse_main.c | 1548 +++++++++++++++++++++++++
2 files changed, 2034 insertions(+)
create mode 100644 drivers/net/ethernet/altera/altera_tse.h
create mode 100644 drivers/net/ethernet/altera/altera_tse_main.c
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
new file mode 100644
index 0000000..f0e39b1
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse.h@@ -0,0 +1,486 @@ +/* Altera Triple-Speed Ethernet MAC driver + * Copyright (C) 2008-2014 Altera Corporation. All rights reserved + * + * Contributors: + * Dalon Westergreen + * Thomas Chou + * Ian Abbott + * Yuriy Kozlov + * Tobias Klauser + * Andriy Smolskyy + * Roman Bulgakov + * Dmytro Mytarchuk + * Matthew Gerlach + * + * Original driver contributed by SLS. + * Major updates contributed by GlobalLogic + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ALTERA_TSE_H__ +#define __ALTERA_TSE_H__ + +#define ALTERA_TSE_RESOURCE_NAME "altera_tse" + +#include <linux/bitops.h> +#include <linux/if_vlan.h> +#include <linux/list.h> +#include <linux/netdevice.h> +#include <linux/phy.h> + +#define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000 +#define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in + * bytes + */ +/* Rx FIFO default settings */ +#define ALTERA_TSE_RX_SECTION_EMPTY 16 +#define ALTERA_TSE_RX_SECTION_FULL 0 +#define ALTERA_TSE_RX_ALMOST_EMPTY 8 +#define ALTERA_TSE_RX_ALMOST_FULL 8 + +/* Tx FIFO default settings */ +#define ALTERA_TSE_TX_SECTION_EMPTY 16 +#define ALTERA_TSE_TX_SECTION_FULL 0 +#define ALTERA_TSE_TX_ALMOST_EMPTY 8 +#define ALTERA_TSE_TX_ALMOST_FULL 3 + +/* MAC function configuration default settings */ +#define ALTERA_TSE_TX_IPG_LENGTH 12 + +#define GET_BIT_VALUE(v, bit) (((v) >> (bit)) & 0x1) + +/* MAC Command_Config Register Bit Definitions + */ +#define MAC_CMDCFG_TX_ENA BIT(0) +#define MAC_CMDCFG_RX_ENA BIT(1) +#define MAC_CMDCFG_XON_GEN BIT(2) +#define MAC_CMDCFG_ETH_SPEED BIT(3) +#define MAC_CMDCFG_PROMIS_EN BIT(4) +#define MAC_CMDCFG_PAD_EN BIT(5) +#define MAC_CMDCFG_CRC_FWD BIT(6) +#define MAC_CMDCFG_PAUSE_FWD BIT(7) +#define MAC_CMDCFG_PAUSE_IGNORE BIT(8) +#define MAC_CMDCFG_TX_ADDR_INS BIT(9) +#define MAC_CMDCFG_HD_ENA BIT(10) +#define MAC_CMDCFG_EXCESS_COL BIT(11) +#define MAC_CMDCFG_LATE_COL BIT(12) +#define MAC_CMDCFG_SW_RESET BIT(13) +#define MAC_CMDCFG_MHASH_SEL BIT(14) +#define MAC_CMDCFG_LOOP_ENA BIT(15) +#define MAC_CMDCFG_TX_ADDR_SEL(v) (((v) & 0x7) << 16) +#define MAC_CMDCFG_MAGIC_ENA BIT(19) +#define MAC_CMDCFG_SLEEP BIT(20) +#define MAC_CMDCFG_WAKEUP BIT(21) +#define MAC_CMDCFG_XOFF_GEN BIT(22) +#define MAC_CMDCFG_CNTL_FRM_ENA BIT(23) +#define MAC_CMDCFG_NO_LGTH_CHECK BIT(24) +#define MAC_CMDCFG_ENA_10 BIT(25) +#define MAC_CMDCFG_RX_ERR_DISC BIT(26) +#define MAC_CMDCFG_DISABLE_READ_TIMEOUT BIT(27) +#define MAC_CMDCFG_CNT_RESET BIT(31) + +#define MAC_CMDCFG_TX_ENA_GET(v) GET_BIT_VALUE(v, 0) +#define MAC_CMDCFG_RX_ENA_GET(v) GET_BIT_VALUE(v, 1) +#define MAC_CMDCFG_XON_GEN_GET(v) GET_BIT_VALUE(v, 2) +#define MAC_CMDCFG_ETH_SPEED_GET(v) GET_BIT_VALUE(v, 3) +#define MAC_CMDCFG_PROMIS_EN_GET(v) GET_BIT_VALUE(v, 4) +#define MAC_CMDCFG_PAD_EN_GET(v) GET_BIT_VALUE(v, 5) +#define MAC_CMDCFG_CRC_FWD_GET(v) GET_BIT_VALUE(v, 6) +#define MAC_CMDCFG_PAUSE_FWD_GET(v) GET_BIT_VALUE(v, 7) +#define MAC_CMDCFG_PAUSE_IGNORE_GET(v) GET_BIT_VALUE(v, 8) +#define MAC_CMDCFG_TX_ADDR_INS_GET(v) GET_BIT_VALUE(v, 9) +#define MAC_CMDCFG_HD_ENA_GET(v) GET_BIT_VALUE(v, 10) +#define MAC_CMDCFG_EXCESS_COL_GET(v) GET_BIT_VALUE(v, 11) +#define MAC_CMDCFG_LATE_COL_GET(v) GET_BIT_VALUE(v, 12) +#define MAC_CMDCFG_SW_RESET_GET(v) GET_BIT_VALUE(v, 13) +#define MAC_CMDCFG_MHASH_SEL_GET(v) GET_BIT_VALUE(v, 14) +#define MAC_CMDCFG_LOOP_ENA_GET(v) GET_BIT_VALUE(v, 15) +#define MAC_CMDCFG_TX_ADDR_SEL_GET(v) (((v) >> 16) & 0x7) +#define MAC_CMDCFG_MAGIC_ENA_GET(v) GET_BIT_VALUE(v, 19) +#define MAC_CMDCFG_SLEEP_GET(v) GET_BIT_VALUE(v, 20) +#define MAC_CMDCFG_WAKEUP_GET(v) GET_BIT_VALUE(v, 21) +#define MAC_CMDCFG_XOFF_GEN_GET(v) GET_BIT_VALUE(v, 22) +#define MAC_CMDCFG_CNTL_FRM_ENA_GET(v) GET_BIT_VALUE(v, 23) +#define MAC_CMDCFG_NO_LGTH_CHECK_GET(v) GET_BIT_VALUE(v, 24) +#define MAC_CMDCFG_ENA_10_GET(v) GET_BIT_VALUE(v, 25) +#define MAC_CMDCFG_RX_ERR_DISC_GET(v) GET_BIT_VALUE(v, 26) +#define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27) +#define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31) + +/* MDIO registers within MAC register Space + */ +struct altera_tse_mdio { + unsigned int control; /* PHY device operation control register */ + unsigned int status; /* PHY device operation status register */ + unsigned int phy_id1; /* Bits 31:16 of PHY identifier */ + unsigned int phy_id2; /* Bits 15:0 of PHY identifier */ + unsigned int auto_negotiation_advertisement; /* Auto-negotiation + * advertisement + * register + */ + unsigned int remote_partner_base_page_ability; + + unsigned int reg6; + unsigned int reg7; + unsigned int reg8; + unsigned int reg9; + unsigned int rega; + unsigned int regb; + unsigned int regc; + unsigned int regd; + unsigned int rege; + unsigned int regf; + unsigned int reg10; + unsigned int reg11; + unsigned int reg12; + unsigned int reg13; + unsigned int reg14; + unsigned int reg15; + unsigned int reg16; + unsigned int reg17; + unsigned int reg18; + unsigned int reg19; + unsigned int reg1a; + unsigned int reg1b; + unsigned int reg1c; + unsigned int reg1d; + unsigned int reg1e; + unsigned int reg1f; +}; + +/* MAC register Space. Note that some of these registers may or may not be + * present depending upon options chosen by the user when the core was + * configured and built. Please consult the Altera Triple Speed Ethernet User + * Guide for details. + */ +struct altera_tse_mac { + /* Bits 15:0: MegaCore function revision (0x0800). Bit 31:16: Customer + * specific revision + */ + unsigned int megacore_revision; + /* Provides a memory location for user applications to test the device + * memory operation. + */ + unsigned int scratch_pad; + /* The host processor uses this register to control and configure the + * MAC block + */ + unsigned int command_config; + /* 32-bit primary MAC address word 0 bits 0 to 31 of the primary + * MAC address + */ + unsigned int mac_addr_0; + /* 32-bit primary MAC address word 1 bits 32 to 47 of the primary + * MAC address + */ + unsigned int mac_addr_1; + /* 14-bit maximum frame length. The MAC receive logic */ + unsigned int frm_length; + /* The pause quanta is used in each pause frame sent to a remote + * Ethernet device, in increments of 512 Ethernet bit times + */ + unsigned int pause_quanta; + /* 12-bit receive FIFO section-empty threshold */ + unsigned int rx_section_empty; + /* 12-bit receive FIFO section-full threshold */ + unsigned int rx_section_full; + /* 12-bit transmit FIFO section-empty threshold */ + unsigned int tx_section_empty; + /* 12-bit transmit FIFO section-full threshold */ + unsigned int tx_section_full; + /* 12-bit receive FIFO almost-empty threshold */ + unsigned int rx_almost_empty; + /* 12-bit receive FIFO almost-full threshold */ + unsigned int rx_almost_full; + /* 12-bit transmit FIFO almost-empty threshold */ + unsigned int tx_almost_empty; + /* 12-bit transmit FIFO almost-full threshold */ + unsigned int tx_almost_full; + /* MDIO address of PHY Device 0. Bits 0 to 4 hold a 5-bit PHY address */ + unsigned int mdio_phy0_addr; + /* MDIO address of PHY Device 1. Bits 0 to 4 hold a 5-bit PHY address */ + unsigned int mdio_phy1_addr; + + /* Bit[15:0]—16-bit holdoff quanta */ + unsigned int holdoff_quant; + + /* only if 100/1000 BaseX PCS, reserved otherwise */ + unsigned int reserved1[5]; + + /* Minimum IPG between consecutive transmit frame in terms of bytes */ + unsigned int tx_ipg_length; + + /* IEEE 802.3 oEntity Managed Object Support */ + + /* The MAC addresses */ + unsigned int mac_id_1; + unsigned int mac_id_2; + + /* Number of frames transmitted without error including pause frames */ + unsigned int frames_transmitted_ok; + /* Number of frames received without error including pause frames */ + unsigned int frames_received_ok; + /* Number of frames received with a CRC error */ + unsigned int frames_check_sequence_errors; + /* Frame received with an alignment error */ + unsigned int alignment_errors; + /* Sum of payload and padding octets of frames transmitted without + * error + */ + unsigned int octets_transmitted_ok; + /* Sum of payload and padding octets of frames received without error */ + unsigned int octets_received_ok; + + /* IEEE 802.3 oPausedEntity Managed Object Support */ + + /* Number of transmitted pause frames */ + unsigned int tx_pause_mac_ctrl_frames; + /* Number of Received pause frames */ + unsigned int rx_pause_mac_ctrl_frames; + + /* IETF MIB (MIB-II) Object Support */ + + /* Number of frames received with error */ + unsigned int if_in_errors; + /* Number of frames transmitted with error */ + unsigned int if_out_errors; + /* Number of valid received unicast frames */ + unsigned int if_in_ucast_pkts; + /* Number of valid received multicasts frames (without pause) */ + unsigned int if_in_multicast_pkts; + /* Number of valid received broadcast frames */ + unsigned int if_in_broadcast_pkts; + unsigned int if_out_discards; + /* The number of valid unicast frames transmitted */ + unsigned int if_out_ucast_pkts; + /* The number of valid multicast frames transmitted, + * excluding pause frames + */ + unsigned int if_out_multicast_pkts; + unsigned int if_out_broadcast_pkts; + + /* IETF RMON MIB Object Support */ + + /* Counts the number of dropped packets due to internal errors + * of the MAC client. + */ + unsigned int ether_stats_drop_events; + /* Total number of bytes received. Good and bad frames. */ + unsigned int ether_stats_octets; + /* Total number of packets received. Counts good and bad packets. */ + unsigned int ether_stats_pkts; + /* Number of packets received with less than 64 bytes. */ + unsigned int ether_stats_undersize_pkts; + /* The number of frames received that are longer than the + * value configured in the frm_length register + */ + unsigned int ether_stats_oversize_pkts; + /* Number of received packet with 64 bytes */ + unsigned int ether_stats_pkts_64_octets; + /* Frames (good and bad) with 65 to 127 bytes */ + unsigned int ether_stats_pkts_65to127_octets; + /* Frames (good and bad) with 128 to 255 bytes */ + unsigned int ether_stats_pkts_128to255_octets; + /* Frames (good and bad) with 256 to 511 bytes */ + unsigned int ether_stats_pkts_256to511_octets; + /* Frames (good and bad) with 512 to 1023 bytes */ + unsigned int ether_stats_pkts_512to1023_octets; + /* Frames (good and bad) with 1024 to 1518 bytes */ + unsigned int ether_stats_pkts_1024to1518_octets; + + /* Any frame length from 1519 to the maximum length configured in the + * frm_length register, if it is greater than 1518 + */ + unsigned int ether_stats_pkts_1519tox_octets; + /* Too long frames with CRC error */ + unsigned int ether_stats_jabbers; + /* Too short frames with CRC error */ + unsigned int ether_stats_fragments; + + unsigned int reserved2; + + /* FIFO control register */ + unsigned int tx_cmd_stat; + unsigned int rx_cmd_stat; + + /* Extended Statistics Counters */ + unsigned int msb_octets_transmitted_ok; + unsigned int msb_octets_received_ok; + unsigned int msb_ether_stats_octets; + + unsigned int reserved3; + + /* Multicast address resolution table, mapped in the controller address + * space + */ + unsigned int hash_table[64]; + + /* Registers 0 to 31 within PHY device 0/1 connected to the MDIO PHY + * management interface + */ + struct altera_tse_mdio mdio_phy0; + struct altera_tse_mdio mdio_phy1; + + /* 4 Supplemental MAC Addresses */ + unsigned int supp_mac_addr_0_0; + unsigned int supp_mac_addr_0_1; + unsigned int supp_mac_addr_1_0; + unsigned int supp_mac_addr_1_1; + unsigned int supp_mac_addr_2_0; + unsigned int supp_mac_addr_2_1; + unsigned int supp_mac_addr_3_0; + unsigned int supp_mac_addr_3_1; + + unsigned int reserved4[8]; + + /* IEEE 1588v2 Feature */ + unsigned int tx_period; + unsigned int tx_adjust_fns; + unsigned int tx_adjust_ns; + unsigned int rx_period; + unsigned int rx_adjust_fns; + unsigned int rx_adjust_ns; + + unsigned int reserved5[42]; +}; + +/* Transmit and Receive Command Registers Bit Definitions + */ +#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC BIT(17) +#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 BIT(18) +#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 BIT(25) + +/* Wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer + */ +struct tse_buffer { + struct list_head lh; + struct sk_buff *skb; + dma_addr_t dma_addr; + unsigned int len; + int mapped_as_page; +}; + +struct altera_tse_private; + +#define ALTERA_DTYPE_SGDMA 1 +#define ALTERA_DTYPE_MSGDMA 2 + +/* standard DMA interface for SGDMA and MSGDMA */ +struct altera_dmaops { + int altera_dtype; + int dmamask; + void (*reset_dma)(struct altera_tse_private *); + void (*enable_txirq)(struct altera_tse_private *); + void (*enable_rxirq)(struct altera_tse_private *); + void (*disable_txirq)(struct altera_tse_private *); + void (*disable_rxirq)(struct altera_tse_private *); + void (*clear_txirq)(struct altera_tse_private *); + void (*clear_rxirq)(struct altera_tse_private *); + int (*tx_buffer)(struct altera_tse_private *, struct tse_buffer *); + u32 (*tx_completions)(struct altera_tse_private *); + int (*add_rx_desc)(struct altera_tse_private *, struct tse_buffer *); + u32 (*get_rx_status)(struct altera_tse_private *); + int (*init_dma)(struct altera_tse_private *); + void (*uninit_dma)(struct altera_tse_private *); +}; + +/* This structure is private to each device. + */ +struct altera_tse_private { + struct net_device *dev; + struct device *device; + struct napi_struct napi; + + /* MAC address space */ + struct altera_tse_mac *mac_dev; + + /* TSE Revision */ + u32 revision; + + /* mSGDMA Rx Dispatcher address space */ + void __iomem *rx_dma_csr; + void __iomem *rx_dma_desc; + void __iomem *rx_dma_resp; + + /* mSGDMA Tx Dispatcher address space */ + void __iomem *tx_dma_csr; + void __iomem *tx_dma_desc; + + /* Rx buffers queue */ + struct tse_buffer *rx_ring; + unsigned int rx_cons; + unsigned int rx_prod; + unsigned int rx_ring_size; + unsigned int rx_dma_buf_sz; + + /* Tx ring buffer */ + struct tse_buffer *tx_ring; + unsigned int tx_prod; + unsigned int tx_cons; + unsigned int tx_ring_size; + + /* Interrupts */ + unsigned int tx_irq; + unsigned int rx_irq; + + /* RX/TX MAC FIFO configs */ + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; + unsigned int max_mtu; + + /* Hash filter settings */ + unsigned int hash_filter; + unsigned int added_unicast; + + /* Descriptor memory info for managing SGDMA */ + unsigned int txdescmem; + unsigned int rxdescmem; + dma_addr_t rxdescmem_busaddr; + dma_addr_t txdescmem_busaddr; + unsigned int txctrlreg; + unsigned int rxctrlreg; + dma_addr_t rxdescphys; + dma_addr_t txdescphys; + + struct list_head txlisthd; + struct list_head rxlisthd; + + /* MAC command_config register protection */ + spinlock_t mac_cfg_lock; + /* Tx path protection */ + spinlock_t tx_lock; + /* Rx DMA & interrupt control protection */ + spinlock_t rxdma_irq_lock; + + /* PHY */ + int phy_addr; /* PHY's MDIO address, -1 for autodetection */ + phy_interface_t phy_iface; + struct mii_bus *mdio; + struct phy_device *phydev; + int oldspeed; + int oldduplex; + int oldlink; + + /* ethtool msglvl option */ + u32 msg_enable; + + struct altera_dmaops *dmaops; +}; + +/* Function prototypes + */ +void altera_tse_set_ethtool_ops(struct net_device *); + +#endif /* __ALTERA_TSE_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
new file mode 100644
index 0000000..4a5944f
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_tse_main.c@@ -0,0 +1,1548 @@ +/* Altera Triple-Speed Ethernet MAC driver + * Copyright (C) 2008-2014 Altera Corporation. All rights reserved + * + * Contributors: + * Dalon Westergreen + * Thomas Chou + * Ian Abbott + * Yuriy Kozlov + * Tobias Klauser + * Andriy Smolskyy + * Roman Bulgakov + * Dmytro Mytarchuk + * Matthew Gerlach + * + * Original driver contributed by SLS. + * Major updates contributed by GlobalLogic + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/atomic.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of_device.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/of_platform.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> +#include <asm/cacheflush.h> + +#include "altera_utils.h" +#include "altera_tse.h" +#include "altera_sgdma.h" +#include "altera_msgdma.h" + +static atomic_t instance_count = ATOMIC_INIT(~0); +/* Module parameters */ +static int debug = -1; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)"); + +static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_IFUP | + NETIF_MSG_IFDOWN); + +#define RX_DESCRIPTORS 64 +static int dma_rx_num = RX_DESCRIPTORS; +module_param(dma_rx_num, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dma_rx_num, "Number of descriptors in the RX list"); + +#define TX_DESCRIPTORS 64 +static int dma_tx_num = TX_DESCRIPTORS; +module_param(dma_tx_num, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dma_tx_num, "Number of descriptors in the TX list"); + + +#define POLL_PHY (-1) + +/* Make sure DMA buffer size is larger than the max frame size + * plus some alignment offset and a VLAN header. If the max frame size is + * 1518, a VLAN header would be additional 4 bytes and additional + * headroom for alignment is 2 bytes, 2048 is just fine. + */ +#define ALTERA_RXDMABUFFER_SIZE 2048 + +/* Allow network stack to resume queueing packets after we've + * finished transmitting at least 1/4 of the packets in the queue. + */ +#define TSE_TX_THRESH(x) (x->tx_ring_size / 4) + +#define TXQUEUESTOP_THRESHHOLD 2 + +static struct of_device_id altera_tse_ids[]; + +static inline u32 tse_tx_avail(struct altera_tse_private *priv) +{ + return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1; +} + +/* MDIO specific functions + */ +static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct altera_tse_mac *mac = (struct altera_tse_mac *)bus->priv; + unsigned int *mdio_regs = (unsigned int *)&mac->mdio_phy0; + u32 data; + + /* set MDIO address */ + iowrite32((mii_id & 0x1f), &mac->mdio_phy0_addr); + + /* get the data */ + data = ioread32(&mdio_regs[regnum]) & 0xffff; + return data; +} + +static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum, + u16 value) +{ + struct altera_tse_mac *mac = (struct altera_tse_mac *)bus->priv; + unsigned int *mdio_regs = (unsigned int *)&mac->mdio_phy0; + + /* set MDIO address */ + iowrite32((mii_id & 0x1f), &mac->mdio_phy0_addr); + + /* write the data */ + iowrite32((u32) value, &mdio_regs[regnum]); + return 0; +} + +static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) +{ + struct altera_tse_private *priv = netdev_priv(dev); + int ret; + int i; + struct device_node *mdio_node = NULL; + struct mii_bus *mdio = NULL; + struct device_node *child_node = NULL; + + for_each_child_of_node(priv->device->of_node, child_node) { + if (of_device_is_compatible(child_node, "altr,tse-mdio")) { + mdio_node = child_node; + break; + } + } + + if (mdio_node) { + dev_dbg(priv->device, "FOUND MDIO subnode\n"); + } else { + dev_dbg(priv->device, "NO MDIO subnode\n"); + return 0; + } + + mdio = mdiobus_alloc(); + if (mdio == NULL) { + dev_err(priv->device, "Error allocating MDIO bus\n"); + return -ENOMEM; + } + + mdio->name = ALTERA_TSE_RESOURCE_NAME; + mdio->read = &altera_tse_mdio_read; + mdio->write = &altera_tse_mdio_write; + snprintf(mdio->id, MII_BUS_ID_SIZE, "%s-%u", mdio->name, id); + + mdio->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); + if (mdio->irq == NULL) { + dev_err(priv->device, "%s: Cannot allocate memory\n", __func__); + ret = -ENOMEM; + goto out_free_mdio; + } + for (i = 0; i < PHY_MAX_ADDR; i++) + mdio->irq[i] = PHY_POLL; + + mdio->priv = priv->mac_dev; + mdio->parent = priv->device; + + ret = of_mdiobus_register(mdio, mdio_node); + if (ret != 0) { + dev_err(priv->device, "Cannot register MDIO bus %s\n", + mdio->id); + goto out_free_mdio_irq; + } + + if (netif_msg_drv(priv)) + dev_info(priv->device, "MDIO bus %s: created\n", mdio->id); + + priv->mdio = mdio; + return 0; +out_free_mdio_irq: + kfree(mdio->irq); +out_free_mdio: + mdiobus_free(mdio); + mdio = NULL; + return ret; +} + +static void altera_tse_mdio_destroy(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + + if (priv->mdio == NULL) + return; + + if (netif_msg_drv(priv)) + dev_info(priv->device, "MDIO bus %s: removed\n", + priv->mdio->id); + + mdiobus_unregister(priv->mdio); + kfree(priv->mdio->irq); + mdiobus_free(priv->mdio); + priv->mdio = NULL; +} + +static int tse_init_rx_buffer(struct altera_tse_private *priv, + struct tse_buffer *rxbuffer, int len) +{ + rxbuffer->skb = netdev_alloc_skb_ip_align(priv->dev, len); + if (!rxbuffer->skb) { + dev_err(priv->device, "%s: Rx init fails; skb is NULL\n", + __func__); + return -ENOMEM; + } + + rxbuffer->dma_addr = dma_map_single(priv->device, rxbuffer->skb->data, + len, + DMA_FROM_DEVICE); + + if (dma_mapping_error(priv->device, rxbuffer->dma_addr)) { + dev_err(priv->device, "%s: DMA mapping error\n", __func__); + dev_kfree_skb_any(rxbuffer->skb); + return -EINVAL; + } + rxbuffer->len = len; + return 0; +} + +static void tse_free_rx_buffer(struct altera_tse_private *priv, + struct tse_buffer *rxbuffer) +{ + struct sk_buff *skb = rxbuffer->skb; + dma_addr_t dma_addr = rxbuffer->dma_addr; + + if (skb != NULL) { + if (dma_addr) + dma_unmap_single(priv->device, dma_addr, + rxbuffer->len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + rxbuffer->skb = NULL; + rxbuffer->dma_addr = 0; + } +} + +/* Unmap and free Tx buffer resources + */ +static void tse_free_tx_buffer(struct altera_tse_private *priv, + struct tse_buffer *buffer) +{ + if (buffer->dma_addr) { + if (buffer->mapped_as_page) + dma_unmap_page(priv->device, buffer->dma_addr, + buffer->len, DMA_TO_DEVICE); + else + dma_unmap_single(priv->device, buffer->dma_addr, + buffer->len, DMA_TO_DEVICE); + buffer->dma_addr = 0; + } + if (buffer->skb) { + dev_kfree_skb_any(buffer->skb); + buffer->skb = NULL; + } +} + +static int alloc_init_skbufs(struct altera_tse_private *priv) +{ + unsigned int rx_descs = priv->rx_ring_size; + unsigned int tx_descs = priv->tx_ring_size; + int ret = -ENOMEM; + int i; + + /* Create Rx ring buffer */ + priv->rx_ring = kcalloc(rx_descs, sizeof(struct tse_buffer), + GFP_KERNEL); + if (!priv->rx_ring) + goto err_rx_ring; + + /* Create Tx ring buffer */ + priv->tx_ring = kcalloc(tx_descs, sizeof(struct tse_buffer), + GFP_KERNEL); + if (!priv->tx_ring) + goto err_tx_ring; + + priv->tx_cons = 0; + priv->tx_prod = 0; + + /* Init Rx ring */ + for (i = 0; i < rx_descs; i++) { + ret = tse_init_rx_buffer(priv, &priv->rx_ring[i], + priv->rx_dma_buf_sz); + if (ret) + goto err_init_rx_buffers; + } + + priv->rx_cons = 0; + priv->rx_prod = 0; + + return 0; +err_init_rx_buffers: + while (--i >= 0) + tse_free_rx_buffer(priv, &priv->rx_ring[i]); + kfree(priv->tx_ring); +err_tx_ring: + kfree(priv->rx_ring); +err_rx_ring: + return ret; +} + +static void free_skbufs(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + unsigned int rx_descs = priv->rx_ring_size; + unsigned int tx_descs = priv->tx_ring_size; + int i; + + /* Release the DMA TX/RX socket buffers */ + for (i = 0; i < rx_descs; i++) + tse_free_rx_buffer(priv, &priv->rx_ring[i]); + for (i = 0; i < tx_descs; i++) + tse_free_tx_buffer(priv, &priv->tx_ring[i]); + + + kfree(priv->tx_ring); +} + +/* Reallocate the skb for the reception process + */ +static inline void tse_rx_refill(struct altera_tse_private *priv) +{ + unsigned int rxsize = priv->rx_ring_size; + unsigned int entry; + int ret; + + for (; priv->rx_cons - priv->rx_prod > 0; + priv->rx_prod++) { + entry = priv->rx_prod % rxsize; + if (likely(priv->rx_ring[entry].skb == NULL)) { + ret = tse_init_rx_buffer(priv, &priv->rx_ring[entry], + priv->rx_dma_buf_sz); + if (unlikely(ret != 0)) { + dev_err(priv->device, + "%s: Cannot allocate skb\n", __func__); + break; + } + priv->dmaops->add_rx_desc(priv, &priv->rx_ring[entry]); + if (netif_msg_rx_status(priv)) + dev_dbg(priv->device, "\trefill entry #%d\n", + entry); + } + } +} + +/* Pull out the VLAN tag and fix up the packet + */ +static inline void tse_rx_vlan(struct net_device *dev, struct sk_buff *skb) +{ + struct ethhdr *eth_hdr; + u16 vid; + if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) && + !__vlan_get_tag(skb, &vid)) { + eth_hdr = (struct ethhdr *)skb->data; + memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2); + skb_pull(skb, VLAN_HLEN); + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); + } +} + +/* Receive a packet: retrieve and pass over to upper levels + */ +static int tse_rx(struct altera_tse_private *priv, int limit) +{ + unsigned int count = 0; + unsigned int next_entry; + struct sk_buff *skb; + unsigned int entry = priv->rx_cons % priv->rx_ring_size; + u32 rxstatus; + u16 pktlength; + u16 pktstatus; + + while ((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) { + pktstatus = rxstatus >> 16; + pktlength = rxstatus & 0xffff; + + if ((pktstatus & 0xFF) || (pktlength == 0)) + netdev_err(priv->dev, + "RCV pktstatus %08X pktlength %08X\n", + pktstatus, pktlength); + + count++; + next_entry = (++priv->rx_cons) % priv->rx_ring_size; + + skb = priv->rx_ring[entry].skb; + if (unlikely(!skb)) { + dev_err(priv->device, + "%s: Inconsistent Rx descriptor chain\n", + __func__); + priv->dev->stats.rx_dropped++; + break; + } + priv->rx_ring[entry].skb = NULL; + + skb_put(skb, pktlength); + + /* make cache consistent with receive packet buffer */ + dma_sync_single_for_cpu(priv->device, + priv->rx_ring[entry].dma_addr, + priv->rx_ring[entry].len, + DMA_FROM_DEVICE); + + dma_unmap_single(priv->device, priv->rx_ring[entry].dma_addr, + priv->rx_ring[entry].len, DMA_FROM_DEVICE); + + if (netif_msg_pktdata(priv)) { + dev_info(priv->device, "frame received %d bytes\n", + pktlength); + print_hex_dump(KERN_ERR, "data: ", DUMP_PREFIX_OFFSET, + 16, 1, skb->data, pktlength, true); + } + + tse_rx_vlan(priv->dev, skb); + + skb->protocol = eth_type_trans(skb, priv->dev); + skb_checksum_none_assert(skb); + + napi_gro_receive(&priv->napi, skb); + + priv->dev->stats.rx_packets++; + priv->dev->stats.rx_bytes += pktlength; + + entry = next_entry; + } + + tse_rx_refill(priv); + return count; +} + +/* Reclaim resources after transmission completes + */ +static int tse_tx_complete(struct altera_tse_private *priv) +{ + unsigned int txsize = priv->tx_ring_size; + u32 ready; + unsigned int entry; + struct tse_buffer *tx_buff; + int txcomplete = 0; + + spin_lock(&priv->tx_lock); + + ready = priv->dmaops->tx_completions(priv); + + /* Free sent buffers */ + while (ready && (priv->tx_cons != priv->tx_prod)) { + entry = priv->tx_cons % txsize; + tx_buff = &priv->tx_ring[entry]; + + if (netif_msg_tx_done(priv)) + dev_dbg(priv->device, "%s: curr %d, dirty %d\n", + __func__, priv->tx_prod, priv->tx_cons); + + if (likely(tx_buff->skb)) + priv->dev->stats.tx_packets++; + + tse_free_tx_buffer(priv, tx_buff); + priv->tx_cons++; + + txcomplete++; + ready--; + } + + if (unlikely(netif_queue_stopped(priv->dev) && + tse_tx_avail(priv) > TSE_TX_THRESH(priv))) { + netif_tx_lock(priv->dev); + if (netif_queue_stopped(priv->dev) && + tse_tx_avail(priv) > TSE_TX_THRESH(priv)) { + if (netif_msg_tx_done(priv)) + dev_dbg(priv->device, "%s: restart transmit\n", + __func__); + netif_wake_queue(priv->dev); + } + netif_tx_unlock(priv->dev); + } + + spin_unlock(&priv->tx_lock); + return txcomplete; +} + +/* NAPI polling function + */ +static int tse_poll(struct napi_struct *napi, int budget) +{ + struct altera_tse_private *priv = + container_of(napi, struct altera_tse_private, napi); + int rxcomplete = 0; + int txcomplete = 0; + unsigned long int flags; + + txcomplete = tse_tx_complete(priv); + + rxcomplete = tse_rx(priv, budget); + + if (rxcomplete >= budget || txcomplete > 0) + return rxcomplete; + + napi_gro_flush(napi, false); + __napi_complete(napi); + + dev_dbg(priv->device, + "NAPI Complete, did %d packets with budget %d\n", + txcomplete+rxcomplete, budget); + + spin_lock_irqsave(&priv->rxdma_irq_lock, flags); + priv->dmaops->enable_rxirq(priv); + priv->dmaops->enable_txirq(priv); + spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); + return rxcomplete + txcomplete; +} + +/* DMA TX & RX FIFO interrupt routing + */ +static irqreturn_t altera_isr(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct altera_tse_private *priv; + unsigned long int flags; + + + if (unlikely(!dev)) { + pr_err("%s: invalid dev pointer\n", __func__); + return IRQ_NONE; + } + priv = netdev_priv(dev); + + /* turn off desc irqs and enable napi rx */ + spin_lock_irqsave(&priv->rxdma_irq_lock, flags); + + if (likely(napi_schedule_prep(&priv->napi))) { + priv->dmaops->disable_rxirq(priv); + priv->dmaops->disable_txirq(priv); + __napi_schedule(&priv->napi); + } + + /* reset IRQs */ + priv->dmaops->clear_rxirq(priv); + priv->dmaops->clear_txirq(priv); + + spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); + + return IRQ_HANDLED; +} + +/* Transmit a packet (called by the kernel). Dispatches + * either the SGDMA method for transmitting or the + * MSGDMA method, assumes no scatter/gather support, + * implying an assumption that there's only one + * physically contiguous fragment starting at + * skb->data, for length of skb_headlen(skb). + */ +static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + unsigned int txsize = priv->tx_ring_size; + unsigned int entry; + struct tse_buffer *buffer = NULL; + int nfrags = skb_shinfo(skb)->nr_frags; + unsigned int nopaged_len = skb_headlen(skb); + enum netdev_tx ret = NETDEV_TX_OK; + dma_addr_t dma_addr; + int txcomplete = 0; + + spin_lock_bh(&priv->tx_lock); + + if (unlikely(tse_tx_avail(priv) < nfrags + 1)) { + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); + /* This is a hard error, log it. */ + dev_err(priv->device, + "%s: Tx list full when queue awake\n", + __func__); + } + ret = NETDEV_TX_BUSY; + goto out; + } + + /* Map the first skb fragment */ + entry = priv->tx_prod % txsize; + buffer = &priv->tx_ring[entry]; + + dma_addr = dma_map_single(priv->device, skb->data, nopaged_len, + DMA_TO_DEVICE); + if (dma_mapping_error(priv->device, dma_addr)) { + dev_err(priv->device, "%s: DMA mapping error\n", __func__); + ret = NETDEV_TX_OK; + goto out; + } + + buffer->skb = skb; + buffer->dma_addr = dma_addr; + buffer->len = nopaged_len; + + /* Push data out of the cache hierarchy into main memory */ + dma_sync_single_for_device(priv->device, buffer->dma_addr, + buffer->len, DMA_TO_DEVICE); + + txcomplete = priv->dmaops->tx_buffer(priv, buffer); + + priv->tx_prod++; + dev->stats.tx_bytes += skb->len; + + if (unlikely(tse_tx_avail(priv) <= TXQUEUESTOP_THRESHHOLD)) { + if (netif_msg_hw(priv)) + dev_dbg(priv->device, "%s: stop transmitted packets\n", + __func__); + netif_stop_queue(dev); + } + +out: + spin_unlock_bh(&priv->tx_lock); + + return ret; +} + +/* Called every time the controller might need to be made + * aware of new link state. The PHY code conveys this + * information through variables in the phydev structure, and this + * function converts those variables into the appropriate + * register values, and can bring down the device if needed. + */ +static void altera_tse_adjust_link(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct phy_device *phydev = priv->phydev; + int new_state = 0; + + /* only change config if there is a link */ + spin_lock(&priv->mac_cfg_lock); + if (phydev->link) { + /* Read old config */ + u32 cfg_reg = ioread32(&priv->mac_dev->command_config); + + /* Check duplex */ + if (phydev->duplex != priv->oldduplex) { + new_state = 1; + if (!(phydev->duplex)) + cfg_reg |= MAC_CMDCFG_HD_ENA; + else + cfg_reg &= ~MAC_CMDCFG_HD_ENA; + + dev_dbg(priv->device, "%s: Link duplex = 0x%x\n", + dev->name, phydev->duplex); + + priv->oldduplex = phydev->duplex; + } + + /* Check speed */ + if (phydev->speed != priv->oldspeed) { + new_state = 1; + switch (phydev->speed) { + case 1000: + cfg_reg |= MAC_CMDCFG_ETH_SPEED; + cfg_reg &= ~MAC_CMDCFG_ENA_10; + break; + case 100: + cfg_reg &= ~MAC_CMDCFG_ETH_SPEED; + cfg_reg &= ~MAC_CMDCFG_ENA_10; + break; + case 10: + cfg_reg &= ~MAC_CMDCFG_ETH_SPEED; + cfg_reg |= MAC_CMDCFG_ENA_10; + break; + default: + if (netif_msg_link(priv)) + netdev_warn(dev, "Speed (%d) is not 10/100/1000!\n", + phydev->speed); + break; + } + priv->oldspeed = phydev->speed; + } + iowrite32(cfg_reg, &priv->mac_dev->command_config); + + if (!priv->oldlink) { + new_state = 1; + priv->oldlink = 1; + } + } else if (priv->oldlink) { + new_state = 1; + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; + } + + if (new_state && netif_msg_link(priv)) + phy_print_status(phydev); + + spin_unlock(&priv->mac_cfg_lock); +} +static struct phy_device *connect_local_phy(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct phy_device *phydev = NULL; + char phy_id_fmt[MII_BUS_ID_SIZE + 3]; + int ret; + + if (priv->phy_addr != POLL_PHY) { + snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, + priv->mdio->id, priv->phy_addr); + + dev_dbg(priv->device, "trying to attach to %s\n", phy_id_fmt); + + phydev = phy_connect(dev, phy_id_fmt, &altera_tse_adjust_link, + priv->phy_iface); + if (IS_ERR(phydev)) + netdev_err(dev, "Could not attach to PHY\n"); + + } else { + phydev = phy_find_first(priv->mdio); + if (phydev == NULL) { + netdev_err(dev, "No PHY found\n"); + return phydev; + } + + ret = phy_connect_direct(dev, phydev, &altera_tse_adjust_link, + priv->phy_iface); + if (ret != 0) { + netdev_err(dev, "Could not attach to PHY\n"); + phydev = NULL; + } + } + return phydev; +} + +/* Initialize driver's PHY state, and attach to the PHY + */ +static int init_phy(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct phy_device *phydev; + struct device_node *phynode; + + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; + + phynode = of_parse_phandle(priv->device->of_node, "phy-handle", 0); + + if (!phynode) { + netdev_dbg(dev, "no phy-handle found\n"); + if (!priv->mdio) { + netdev_err(dev, + "No phy-handle nor local mdio specified\n"); + return -ENODEV; + } + phydev = connect_local_phy(dev); + } else { + netdev_dbg(dev, "phy-handle found\n"); + phydev = of_phy_connect(dev, phynode, + &altera_tse_adjust_link, 0, priv->phy_iface); + } + + if (!phydev) { + netdev_err(dev, "Could not find the PHY\n"); + return -ENODEV; + } + + /* Stop Advertising 1000BASE Capability if interface is not GMII + * Note: Checkpatch throws CHECKs for the camel case defines below, + * it's ok to ignore. + */ + if ((priv->phy_iface == PHY_INTERFACE_MODE_MII) || + (priv->phy_iface == PHY_INTERFACE_MODE_RMII)) + phydev->advertising &= ~(SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full); + + /* Broken HW is sometimes missing the pull-up resistor on the + * MDIO line, which results in reads to non-existent devices returning + * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent + * device as well. + * Note: phydev->phy_id is the result of reading the UID PHY registers. + */ + if (phydev->phy_id == 0) { + netdev_err(dev, "Bad PHY UID 0x%08x\n", phydev->phy_id); + phy_disconnect(phydev); + return -ENODEV; + } + + dev_dbg(priv->device, "attached to PHY %d UID 0x%08x Link = %d\n", + phydev->addr, phydev->phy_id, phydev->link); + + priv->phydev = phydev; + return 0; +} + +static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr) +{ + struct altera_tse_mac *mac = priv->mac_dev; + u32 msb; + u32 lsb; + + msb = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + lsb = ((addr[5] << 8) | addr[4]) & 0xffff; + + /* Set primary MAC address */ + iowrite32(msb, &mac->mac_addr_0); + iowrite32(lsb, &mac->mac_addr_1); +} + +/* MAC software reset. + * When reset is triggered, the MAC function completes the current + * transmission or reception, and subsequently disables the transmit and + * receive logic, flushes the receive FIFO buffer, and resets the statistics + * counters. + */ +static int reset_mac(struct altera_tse_private *priv) +{ + void __iomem *cmd_cfg_reg = &priv->mac_dev->command_config; + int counter; + u32 dat; + + dat = ioread32(cmd_cfg_reg); + dat &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA); + dat |= MAC_CMDCFG_SW_RESET | MAC_CMDCFG_CNT_RESET; + iowrite32(dat, cmd_cfg_reg); + + counter = 0; + while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) { + if (tse_bit_is_clear(cmd_cfg_reg, MAC_CMDCFG_SW_RESET)) + break; + udelay(1); + } + + if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) { + if (netif_msg_drv(priv)) + dev_warn(priv->device, + "TSE MAC resetting bit never cleared!\n"); + dat = ioread32(cmd_cfg_reg); + dat &= ~MAC_CMDCFG_SW_RESET; + iowrite32(dat, cmd_cfg_reg); + return -1; + } + return 0; +} + +/* Initialize MAC core registers +*/ +static int init_mac(struct altera_tse_private *priv) +{ + struct altera_tse_mac *mac = priv->mac_dev; + unsigned int cmd = 0; + u32 frm_length; + + /* Setup Rx FIFO */ + iowrite32(priv->rx_fifo_depth - ALTERA_TSE_RX_SECTION_EMPTY, + &mac->rx_section_empty); + iowrite32(ALTERA_TSE_RX_SECTION_FULL, &mac->rx_section_full); + iowrite32(ALTERA_TSE_RX_ALMOST_EMPTY, &mac->rx_almost_empty); + iowrite32(ALTERA_TSE_RX_ALMOST_FULL, &mac->rx_almost_full); + + /* Setup Tx FIFO */ + iowrite32(priv->tx_fifo_depth - ALTERA_TSE_TX_SECTION_EMPTY, + &mac->tx_section_empty); + iowrite32(ALTERA_TSE_TX_SECTION_FULL, &mac->tx_section_full); + iowrite32(ALTERA_TSE_TX_ALMOST_EMPTY, &mac->tx_almost_empty); + iowrite32(ALTERA_TSE_TX_ALMOST_FULL, &mac->tx_almost_full); + + /* MAC Address Configuration */ + tse_update_mac_addr(priv, priv->dev->dev_addr); + + /* MAC Function Configuration */ + frm_length = ETH_HLEN + priv->dev->mtu + ETH_FCS_LEN; + iowrite32(frm_length, &mac->frm_length); + iowrite32(ALTERA_TSE_TX_IPG_LENGTH, &mac->tx_ipg_length); + + /* Disable RX/TX shift 16 for alignment of all received frames on 16-bit + * start address + */ + tse_clear_bit(&mac->rx_cmd_stat, ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16); + tse_clear_bit(&mac->tx_cmd_stat, ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 | + ALTERA_TSE_TX_CMD_STAT_OMIT_CRC); + + /* Set the MAC options */ + cmd = ioread32(&mac->command_config); + cmd |= MAC_CMDCFG_PAD_EN; /* Padding Removal on Receive */ + cmd &= ~MAC_CMDCFG_CRC_FWD; /* CRC Removal */ + cmd |= MAC_CMDCFG_RX_ERR_DISC; /* Automatically discard frames + * with CRC errors + */ + cmd |= MAC_CMDCFG_CNTL_FRM_ENA; + cmd &= ~MAC_CMDCFG_TX_ENA; + cmd &= ~MAC_CMDCFG_RX_ENA; + iowrite32(cmd, &mac->command_config); + + if (netif_msg_hw(priv)) + dev_dbg(priv->device, + "MAC post-initialization: CMD_CONFIG = 0x%08x\n", cmd); + + return 0; +} + +/* Start/stop MAC transmission logic + */ +static void tse_set_mac(struct altera_tse_private *priv, bool enable) +{ + struct altera_tse_mac *mac = priv->mac_dev; + u32 value = ioread32(&mac->command_config); + + if (enable) + value |= MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA; + else + value &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA); + + iowrite32(value, &mac->command_config); +} + +/* Change the MTU + */ +static int tse_change_mtu(struct net_device *dev, int new_mtu) +{ + struct altera_tse_private *priv = netdev_priv(dev); + unsigned int max_mtu = priv->max_mtu; + unsigned int min_mtu = ETH_ZLEN + ETH_FCS_LEN; + + if (netif_running(dev)) { + dev_err(priv->device, "must be stopped to change its MTU\n"); + return -EBUSY; + } + + if ((new_mtu < min_mtu) || (new_mtu > max_mtu)) { + netdev_err(dev, "invalid MTU, max MTU is: %u\n", max_mtu); + return -EINVAL; + } + + dev->mtu = new_mtu; + netdev_update_features(dev); + + return 0; +} + +static void altera_tse_set_mcfilter(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct altera_tse_mac *mac = (struct altera_tse_mac *)priv->mac_dev; + int i; + struct netdev_hw_addr *ha; + + /* clear the hash filter */ + for (i = 0; i < 64; i++) + iowrite32(0, &(mac->hash_table[i])); + + netdev_for_each_mc_addr(ha, dev) { + unsigned int hash = 0; + int mac_octet; + + for (mac_octet = 5; mac_octet >= 0; mac_octet--) { + unsigned char xor_bit = 0; + unsigned char octet = ha->addr[mac_octet]; + unsigned int bitshift; + + for (bitshift = 0; bitshift < 8; bitshift++) + xor_bit ^= ((octet >> bitshift) & 0x01); + + hash = (hash << 1) | xor_bit; + } + iowrite32(1, &(mac->hash_table[hash])); + } +} + + +static void altera_tse_set_mcfilterall(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct altera_tse_mac *mac = (struct altera_tse_mac *)priv->mac_dev; + int i; + + /* set the hash filter */ + for (i = 0; i < 64; i++) + iowrite32(1, &(mac->hash_table[i])); +} + +/* Set or clear the multicast filter for this adaptor + */ +static void tse_set_rx_mode_hashfilter(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct altera_tse_mac *mac = priv->mac_dev; + + spin_lock(&priv->mac_cfg_lock); + + if (dev->flags & IFF_PROMISC) + tse_set_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN); + + if (dev->flags & IFF_ALLMULTI) + altera_tse_set_mcfilterall(dev); + else + altera_tse_set_mcfilter(dev); + + spin_unlock(&priv->mac_cfg_lock); +} + +/* Set or clear the multicast filter for this adaptor + */ +static void tse_set_rx_mode(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + struct altera_tse_mac *mac = priv->mac_dev; + + spin_lock(&priv->mac_cfg_lock); + + if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || + !netdev_mc_empty(dev) || !netdev_uc_empty(dev)) + tse_set_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN); + else + tse_clear_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN); + + spin_unlock(&priv->mac_cfg_lock); +} + +/* Open and initialize the interface + */ +static int tse_open(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + int ret = 0; + int i; + unsigned long int flags; + + /* Reset and configure TSE MAC and probe associated PHY */ + ret = priv->dmaops->init_dma(priv); + if (ret != 0) { + netdev_err(dev, "Cannot initialize DMA\n"); + goto phy_error; + } + + if (netif_msg_ifup(priv)) + dev_warn(priv->device, "device MAC address %pM\n", + dev->dev_addr); + + if ((priv->revision < 0xd00) || (priv->revision > 0xe00)) + dev_warn(priv->device, "TSE revision %x\n", priv->revision); + + spin_lock(&priv->mac_cfg_lock); + ret = reset_mac(priv); + if (ret) + netdev_err(dev, "Cannot reset MAC core (error: %d)\n", ret); + + ret = init_mac(priv); + spin_unlock(&priv->mac_cfg_lock); + if (ret) { + netdev_err(dev, "Cannot init MAC core (error: %d)\n", ret); + goto alloc_skbuf_error; + } + + priv->dmaops->reset_dma(priv); + + /* Create and initialize the TX/RX descriptors chains. */ + priv->rx_ring_size = dma_rx_num; + priv->tx_ring_size = dma_tx_num; + ret = alloc_init_skbufs(priv); + if (ret) { + netdev_err(dev, "DMA descriptors initialization failed\n"); + goto alloc_skbuf_error; + } + + + /* Register RX interrupt */ + ret = request_irq(priv->rx_irq, altera_isr, IRQF_SHARED, + dev->name, dev); + if (ret) { + netdev_err(dev, "Unable to register RX interrupt %d\n", + priv->rx_irq); + goto init_error; + } + + /* Register TX interrupt */ + ret = request_irq(priv->tx_irq, altera_isr, IRQF_SHARED, + dev->name, dev); + if (ret) { + netdev_err(dev, "Unable to register TX interrupt %d\n", + priv->tx_irq); + goto tx_request_irq_error; + } + + /* Enable DMA interrupts */ + spin_lock_irqsave(&priv->rxdma_irq_lock, flags); + priv->dmaops->enable_rxirq(priv); + priv->dmaops->enable_txirq(priv); + + /* Setup RX descriptor chain */ + for (i = 0; i < priv->rx_ring_size; i++) + priv->dmaops->add_rx_desc(priv, &priv->rx_ring[i]); + + spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); + + /* Start MAC Rx/Tx */ + spin_lock(&priv->mac_cfg_lock); + tse_set_mac(priv, true); + spin_unlock(&priv->mac_cfg_lock); + + if (priv->phydev) + phy_start(priv->phydev); + + napi_enable(&priv->napi); + netif_start_queue(dev); + + return 0; + +tx_request_irq_error: + free_irq(priv->rx_irq, dev); +init_error: + free_skbufs(dev); +alloc_skbuf_error: + if (priv->phydev) { + phy_disconnect(priv->phydev); + priv->phydev = NULL; + } +phy_error: + return ret; +} + +/* Stop TSE MAC interface and put the device in an inactive state + */ +static int tse_shutdown(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + int ret; + unsigned long int flags; + + /* Stop and disconnect the PHY */ + if (priv->phydev) { + phy_stop(priv->phydev); + phy_disconnect(priv->phydev); + priv->phydev = NULL; + } + + netif_stop_queue(dev); + napi_disable(&priv->napi); + + /* Disable DMA interrupts */ + spin_lock_irqsave(&priv->rxdma_irq_lock, flags); + priv->dmaops->disable_rxirq(priv); + priv->dmaops->disable_txirq(priv); + spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); + + /* Free the IRQ lines */ + free_irq(priv->rx_irq, dev); + free_irq(priv->tx_irq, dev); + + /* disable and reset the MAC, empties fifo */ + spin_lock(&priv->mac_cfg_lock); + spin_lock(&priv->tx_lock); + + ret = reset_mac(priv); + if (ret) + netdev_err(dev, "Cannot reset MAC core (error: %d)\n", ret); + priv->dmaops->reset_dma(priv); + free_skbufs(dev); + + spin_unlock(&priv->tx_lock); + spin_unlock(&priv->mac_cfg_lock); + + priv->dmaops->uninit_dma(priv); + + return 0; +} + +static struct net_device_ops altera_tse_netdev_ops = { + .ndo_open = tse_open, + .ndo_stop = tse_shutdown, + .ndo_start_xmit = tse_start_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_set_rx_mode = tse_set_rx_mode, + .ndo_change_mtu = tse_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + + +static int request_and_map(struct platform_device *pdev, const char *name, + struct resource **res, void __iomem **ptr) +{ + struct resource *region; + struct device *device = &pdev->dev; + + *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (*res == NULL) { + dev_err(device, "resource %s not defined\n", name); + return -ENODEV; + } + + region = devm_request_mem_region(device, (*res)->start, + resource_size(*res), dev_name(device)); + if (region == NULL) { + dev_err(device, "unable to request %s\n", name); + return -EBUSY; + } + + *ptr = devm_ioremap_nocache(device, region->start, + resource_size(region)); + if (*ptr == NULL) { + dev_err(device, "ioremap_nocache of %s failed!", name); + return -ENOMEM; + } + + return 0; +} + +/* Probe Altera TSE MAC device + */ +static int altera_tse_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + int ret = -ENODEV; + struct resource *control_port; + struct resource *dma_res; + struct altera_tse_private *priv; + const unsigned char *macaddr; + struct device_node *np = pdev->dev.of_node; + unsigned int descmap; + const struct of_device_id *of_id = NULL; + + ndev = alloc_etherdev(sizeof(struct altera_tse_private)); + if (!ndev) { + dev_err(&pdev->dev, "Could not allocate network device\n"); + return -ENODEV; + } + + SET_NETDEV_DEV(ndev, &pdev->dev); + + priv = netdev_priv(ndev); + priv->device = &pdev->dev; + priv->dev = ndev; + priv->msg_enable = netif_msg_init(debug, default_msg_level); + + of_id = of_match_device(altera_tse_ids, &pdev->dev); + + if (of_id) + priv->dmaops = (struct altera_dmaops *)of_id->data; + + + if (priv->dmaops && + priv->dmaops->altera_dtype == ALTERA_DTYPE_SGDMA) { + ret = request_and_map(pdev, "s1", &dma_res, + (void *)&descmap); + if (ret) + goto out_free; + + priv->tx_dma_desc = (void *)descmap; + priv->txdescmem = resource_size(dma_res)/2; + priv->txdescmem_busaddr = dma_res->start; + + priv->rx_dma_desc = (void *)(descmap+priv->txdescmem); + priv->rxdescmem = resource_size(dma_res)/2; + priv->rxdescmem_busaddr = dma_res->start + priv->txdescmem; + + if (upper_32_bits(priv->rxdescmem_busaddr)) { + dev_dbg(priv->device, + "SGDMA bus addresses greater than 32-bits\n"); + goto out_free; + } + if (upper_32_bits(priv->txdescmem_busaddr)) { + dev_dbg(priv->device, + "SGDMA bus addresses greater than 32-bits\n"); + goto out_free; + } + } else if (priv->dmaops && + priv->dmaops->altera_dtype == ALTERA_DTYPE_MSGDMA) { + ret = request_and_map(pdev, "rx_resp", &dma_res, + (void *)&priv->rx_dma_resp); + if (ret) + goto out_free; + + ret = request_and_map(pdev, "tx_desc", &dma_res, + (void *)&priv->tx_dma_desc); + if (ret) + goto out_free; + + priv->txdescmem = resource_size(dma_res); + priv->txdescmem_busaddr = dma_res->start; + + ret = request_and_map(pdev, "rx_desc", &dma_res, + (void *)&priv->rx_dma_desc); + if (ret) + goto out_free; + + priv->rxdescmem = resource_size(dma_res); + priv->rxdescmem_busaddr = dma_res->start; + + } else { + goto out_free; + } + + if (!dma_set_mask(priv->device, DMA_BIT_MASK(priv->dmaops->dmamask))) + dma_set_coherent_mask(priv->device, + DMA_BIT_MASK(priv->dmaops->dmamask)); + else if (!dma_set_mask(priv->device, DMA_BIT_MASK(32))) + dma_set_coherent_mask(priv->device, DMA_BIT_MASK(32)); + else + goto out_free; + + /* MAC address space */ + ret = request_and_map(pdev, "control_port", &control_port, + (void *)&priv->mac_dev); + if (ret) + goto out_free; + + /* xSGDMA Rx Dispatcher address space */ + ret = request_and_map(pdev, "rx_csr", &dma_res, + (void *)&priv->rx_dma_csr); + if (ret) + goto out_free; + + + /* xSGDMA Tx Dispatcher address space */ + ret = request_and_map(pdev, "tx_csr", &dma_res, + (void *)&priv->tx_dma_csr); + if (ret) + goto out_free; + + + /* Rx IRQ */ + priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq"); + if (priv->rx_irq == -ENXIO) { + dev_err(&pdev->dev, "cannot obtain Rx IRQ\n"); + ret = -ENXIO; + goto out_free; + } + + /* Tx IRQ */ + priv->tx_irq = platform_get_irq_byname(pdev, "tx_irq"); + if (priv->tx_irq == -ENXIO) { + dev_err(&pdev->dev, "cannot obtain Tx IRQ\n"); + ret = -ENXIO; + goto out_free; + } + + /* get FIFO depths from device tree */ + if (of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", + &priv->rx_fifo_depth)) { + dev_err(&pdev->dev, "cannot obtain rx-fifo-depth\n"); + ret = -ENXIO; + goto out_free; + } + + if (of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", + &priv->rx_fifo_depth)) { + dev_err(&pdev->dev, "cannot obtain tx-fifo-depth\n"); + ret = -ENXIO; + goto out_free; + } + + /* get hash filter settings for this instance */ + priv->hash_filter = + of_property_read_bool(pdev->dev.of_node, + "altr,has-hash-multicast-filter"); + + /* get supplemental address settings for this instance */ + priv->added_unicast = + of_property_read_bool(pdev->dev.of_node, + "altr,has-supplementary-unicast"); + + /* Max MTU is 1500, ETH_DATA_LEN */ + priv->max_mtu = ETH_DATA_LEN; + + /* Get the max mtu from the device tree. Note that the + * "max-frame-size" parameter is actually max mtu. Definition + * in the ePAPR v1.1 spec and usage differ, so go with usage. + */ + of_property_read_u32(pdev->dev.of_node, "max-frame-size", + &priv->max_mtu); + + /* The DMA buffer size already accounts for an alignment bias + * to avoid unaligned access exceptions for the NIOS processor, + */ + priv->rx_dma_buf_sz = ALTERA_RXDMABUFFER_SIZE; + + /* get default MAC address from device tree */ + macaddr = of_get_mac_address(pdev->dev.of_node); + if (macaddr) + ether_addr_copy(ndev->dev_addr, macaddr); + else + eth_hw_addr_random(ndev); + + priv->phy_iface = of_get_phy_mode(np); + + /* try to get PHY address from device tree, use PHY autodetection if + * no valid address is given + */ + if (of_property_read_u32(pdev->dev.of_node, "phy-addr", + &priv->phy_addr)) { + priv->phy_addr = POLL_PHY; + } + + if (!((priv->phy_addr == POLL_PHY) || + ((priv->phy_addr >= 0) && (priv->phy_addr < PHY_MAX_ADDR)))) { + dev_err(&pdev->dev, "invalid phy-addr specified %d\n", + priv->phy_addr); + goto out_free; + } + + /* Create/attach to MDIO bus */ + ret = altera_tse_mdio_create(ndev, + atomic_add_return(1, &instance_count)); + + if (ret) + goto out_free; + + /* initialize netdev */ + ether_setup(ndev); + ndev->mem_start = control_port->start; + ndev->mem_end = control_port->end; + ndev->netdev_ops = &altera_tse_netdev_ops; + altera_tse_set_ethtool_ops(ndev); + + altera_tse_netdev_ops.ndo_set_rx_mode = tse_set_rx_mode; + + if (priv->hash_filter) + altera_tse_netdev_ops.ndo_set_rx_mode = + tse_set_rx_mode_hashfilter; + + /* Scatter/gather IO is not supported, + * so it is turned off + */ + ndev->hw_features &= ~NETIF_F_SG; + ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA; + + /* VLAN offloading of tagging, stripping and filtering is not + * supported by hardware, but driver will accommodate the + * extra 4-byte VLAN tag for processing by upper layers + */ + ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; + + /* setup NAPI interface */ + netif_napi_add(ndev, &priv->napi, tse_poll, NAPI_POLL_WEIGHT); + + spin_lock_init(&priv->mac_cfg_lock); + spin_lock_init(&priv->tx_lock); + spin_lock_init(&priv->rxdma_irq_lock); + + ret = register_netdev(ndev); + if (ret) { + dev_err(&pdev->dev, "failed to register TSE net device\n"); + goto out_free_mdio; + } + + platform_set_drvdata(pdev, ndev); + + priv->revision = ioread32(&priv->mac_dev->megacore_revision); + + if (netif_msg_probe(priv)) + dev_info(&pdev->dev, "Altera TSE MAC version %d.%d at 0x%08lx irq %d/%d\n", + (priv->revision >> 8) & 0xff, + priv->revision & 0xff, + (unsigned long) control_port->start, priv->rx_irq, + priv->tx_irq); + + ret = init_phy(ndev); + if (ret != 0) { + netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret); + goto out_free_mdio; + } + return 0; + +out_free_mdio: + altera_tse_mdio_destroy(ndev); +out_free: + free_netdev(ndev); + return ret; +} + +/* Remove Altera TSE MAC device + */ +static int altera_tse_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + altera_tse_mdio_destroy(ndev); + unregister_netdev(ndev); + free_netdev(ndev); + + return 0; +} + +struct altera_dmaops altera_dtype_sgdma = { + .altera_dtype = ALTERA_DTYPE_SGDMA, + .dmamask = 32, + .reset_dma = sgdma_reset, + .enable_txirq = sgdma_enable_txirq, + .enable_rxirq = sgdma_enable_rxirq, + .disable_txirq = sgdma_disable_txirq, + .disable_rxirq = sgdma_disable_rxirq, + .clear_txirq = sgdma_clear_txirq, + .clear_rxirq = sgdma_clear_rxirq, + .tx_buffer = sgdma_tx_buffer, + .tx_completions = sgdma_tx_completions, + .add_rx_desc = sgdma_add_rx_desc, + .get_rx_status = sgdma_rx_status, + .init_dma = sgdma_initialize, + .uninit_dma = sgdma_uninitialize, +}; + +struct altera_dmaops altera_dtype_msgdma = { + .altera_dtype = ALTERA_DTYPE_MSGDMA, + .dmamask = 64, + .reset_dma = msgdma_reset, + .enable_txirq = msgdma_enable_txirq, + .enable_rxirq = msgdma_enable_rxirq, + .disable_txirq = msgdma_disable_txirq, + .disable_rxirq = msgdma_disable_rxirq, + .clear_txirq = msgdma_clear_txirq, + .clear_rxirq = msgdma_clear_rxirq, + .tx_buffer = msgdma_tx_buffer, + .tx_completions = msgdma_tx_completions, + .add_rx_desc = msgdma_add_rx_desc, + .get_rx_status = msgdma_rx_status, + .init_dma = msgdma_initialize, + .uninit_dma = msgdma_uninitialize, +}; + +static struct of_device_id altera_tse_ids[] = { + { .compatible = "altr,tse-msgdma-1.0", .data = &altera_dtype_msgdma, }, + { .compatible = "altr,tse-1.0", .data = &altera_dtype_sgdma, }, + { .compatible = "ALTR,tse-1.0", .data = &altera_dtype_sgdma, }, + {}, +}; +MODULE_DEVICE_TABLE(of, altera_tse_ids); + +static struct platform_driver altera_tse_driver = { + .probe = altera_tse_probe, + .remove = altera_tse_remove, + .suspend = NULL, + .resume = NULL, + .driver = { + .name = ALTERA_TSE_RESOURCE_NAME, + .owner = THIS_MODULE, + .of_match_table = altera_tse_ids, + }, +}; + +module_platform_driver(altera_tse_driver); + +MODULE_AUTHOR("Altera Corporation"); +MODULE_DESCRIPTION("Altera Triple Speed Ethernet MAC driver"); +MODULE_LICENSE("GPL v2");
--
1.7.9.5