2009-10-15 05:13:45 +07:00
|
|
|
/*******************************************************************************
|
|
|
|
This is the driver for the ST MAC 10/100/1000 on-chip Ethernet controllers.
|
|
|
|
ST Ethernet IPs are built around a Synopsys IP Core.
|
|
|
|
|
2011-10-18 07:01:24 +07:00
|
|
|
Copyright(C) 2007-2011 STMicroelectronics Ltd
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
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, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
|
|
|
|
The full GNU General Public License is included in this distribution in
|
|
|
|
the file called "COPYING".
|
|
|
|
|
|
|
|
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
|
|
|
|
|
|
|
|
Documentation available at:
|
|
|
|
http://www.stlinux.com
|
|
|
|
Support available at:
|
|
|
|
https://bugzilla.stlinux.com/
|
|
|
|
*******************************************************************************/
|
|
|
|
|
2012-07-31 04:39:41 +07:00
|
|
|
#include <linux/clk.h>
|
2009-10-15 05:13:45 +07:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <linux/tcp.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/crc32.h>
|
|
|
|
#include <linux/mii.h>
|
2011-08-16 13:29:00 +07:00
|
|
|
#include <linux/if.h>
|
2009-10-15 05:13:45 +07:00
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2011-05-23 03:47:17 +07:00
|
|
|
#include <linux/prefetch.h>
|
2014-01-16 17:52:52 +07:00
|
|
|
#include <linux/pinctrl/consumer.h>
|
2014-11-04 21:49:33 +07:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
2011-09-02 04:51:39 +07:00
|
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <linux/seq_file.h>
|
2014-11-04 21:49:33 +07:00
|
|
|
#endif /* CONFIG_DEBUG_FS */
|
2013-03-26 11:43:10 +07:00
|
|
|
#include <linux/net_tstamp.h>
|
|
|
|
#include "stmmac_ptp.h"
|
2011-10-18 07:01:24 +07:00
|
|
|
#include "stmmac.h"
|
2014-01-17 20:24:41 +07:00
|
|
|
#include <linux/reset.h>
|
2015-05-28 01:02:47 +07:00
|
|
|
#include <linux/of_mdio.h>
|
2015-12-14 10:32:01 +07:00
|
|
|
#include "dwmac1000.h"
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
|
2016-04-01 16:37:34 +07:00
|
|
|
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
/* Module parameters */
|
2013-04-08 09:10:00 +07:00
|
|
|
#define TX_TIMEO 5000
|
2009-10-15 05:13:45 +07:00
|
|
|
static int watchdog = TX_TIMEO;
|
|
|
|
module_param(watchdog, int, S_IRUGO | S_IWUSR);
|
2013-04-08 09:10:00 +07:00
|
|
|
MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds (default 5s)");
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
static int debug = -1;
|
2009-10-15 05:13:45 +07:00
|
|
|
module_param(debug, int, S_IRUGO | S_IWUSR);
|
2013-04-08 09:10:00 +07:00
|
|
|
MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-12-31 01:38:57 +07:00
|
|
|
static int phyaddr = -1;
|
2009-10-15 05:13:45 +07:00
|
|
|
module_param(phyaddr, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(phyaddr, "Physical device address");
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
#define STMMAC_TX_THRESH (DMA_TX_SIZE / 4)
|
2016-02-29 20:27:42 +07:00
|
|
|
#define STMMAC_RX_THRESH (DMA_RX_SIZE / 4)
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
static int flow_ctrl = FLOW_OFF;
|
|
|
|
module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off]");
|
|
|
|
|
|
|
|
static int pause = PAUSE_TIME;
|
|
|
|
module_param(pause, int, S_IRUGO | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(pause, "Flow Control Pause Time");
|
|
|
|
|
|
|
|
#define TC_DEFAULT 64
|
|
|
|
static int tc = TC_DEFAULT;
|
|
|
|
module_param(tc, int, S_IRUGO | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(tc, "DMA threshold control value");
|
|
|
|
|
2014-03-10 19:40:32 +07:00
|
|
|
#define DEFAULT_BUFSIZE 1536
|
|
|
|
static int buf_sz = DEFAULT_BUFSIZE;
|
2009-10-15 05:13:45 +07:00
|
|
|
module_param(buf_sz, int, S_IRUGO | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(buf_sz, "DMA buffer size");
|
|
|
|
|
2016-02-29 20:27:41 +07:00
|
|
|
#define STMMAC_RX_COPYBREAK 256
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
|
|
|
|
NETIF_MSG_LINK | NETIF_MSG_IFUP |
|
|
|
|
NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
#define STMMAC_DEFAULT_LPI_TIMER 1000
|
|
|
|
static int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
|
|
|
|
module_param(eee_timer, int, S_IRUGO | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
|
2013-06-18 12:03:23 +07:00
|
|
|
#define STMMAC_LPI_T(x) (jiffies + msecs_to_jiffies(x))
|
2012-06-28 04:14:37 +07:00
|
|
|
|
2013-03-26 11:43:05 +07:00
|
|
|
/* By default the driver will use the ring mode to manage tx and rx descriptors
|
|
|
|
* but passing this value so user can force to use the chain instead of the ring
|
|
|
|
*/
|
|
|
|
static unsigned int chain_mode;
|
|
|
|
module_param(chain_mode, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
|
|
|
|
|
2014-11-04 21:49:33 +07:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
2011-12-21 10:58:19 +07:00
|
|
|
static int stmmac_init_fs(struct net_device *dev);
|
2015-05-23 09:03:29 +07:00
|
|
|
static void stmmac_exit_fs(struct net_device *dev);
|
2011-12-21 10:58:19 +07:00
|
|
|
#endif
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
|
|
|
* stmmac_verify_args - verify the driver parameters.
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: it checks the driver parameters and set a default in case of
|
|
|
|
* errors.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static void stmmac_verify_args(void)
|
|
|
|
{
|
|
|
|
if (unlikely(watchdog < 0))
|
|
|
|
watchdog = TX_TIMEO;
|
2014-03-10 19:40:32 +07:00
|
|
|
if (unlikely((buf_sz < DEFAULT_BUFSIZE) || (buf_sz > BUF_SIZE_16KiB)))
|
|
|
|
buf_sz = DEFAULT_BUFSIZE;
|
2009-10-15 05:13:45 +07:00
|
|
|
if (unlikely(flow_ctrl > 1))
|
|
|
|
flow_ctrl = FLOW_AUTO;
|
|
|
|
else if (likely(flow_ctrl < 0))
|
|
|
|
flow_ctrl = FLOW_OFF;
|
|
|
|
if (unlikely((pause < 0) || (pause > 0xffff)))
|
|
|
|
pause = PAUSE_TIME;
|
2012-06-28 04:14:37 +07:00
|
|
|
if (eee_timer < 0)
|
|
|
|
eee_timer = STMMAC_DEFAULT_LPI_TIMER;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
|
|
|
* stmmac_clk_csr_set - dynamically set the MDC clock
|
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: this is to dynamically set the MDC clock according to the csr
|
|
|
|
* clock input.
|
|
|
|
* Note:
|
|
|
|
* If a specific clk_csr value is passed from the platform
|
|
|
|
* this means that the CSR Clock Range selection cannot be
|
|
|
|
* changed at run-time and it is fixed (as reported in the driver
|
|
|
|
* documentation). Viceversa the driver will try to set the MDC
|
|
|
|
* clock dynamically according to the actual clock input.
|
|
|
|
*/
|
2012-04-04 11:33:27 +07:00
|
|
|
static void stmmac_clk_csr_set(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
u32 clk_rate;
|
|
|
|
|
|
|
|
clk_rate = clk_get_rate(priv->stmmac_clk);
|
|
|
|
|
|
|
|
/* Platform provided default clk_csr would be assumed valid
|
2013-04-08 09:10:01 +07:00
|
|
|
* for all other cases except for the below mentioned ones.
|
|
|
|
* For values higher than the IEEE 802.3 specified frequency
|
|
|
|
* we can not estimate the proper divider as it is not known
|
|
|
|
* the frequency of clk_csr_i. So we do not change the default
|
|
|
|
* divider.
|
|
|
|
*/
|
2012-04-04 11:33:27 +07:00
|
|
|
if (!(priv->clk_csr & MAC_CSR_H_FRQ_MASK)) {
|
|
|
|
if (clk_rate < CSR_F_35M)
|
|
|
|
priv->clk_csr = STMMAC_CSR_20_35M;
|
|
|
|
else if ((clk_rate >= CSR_F_35M) && (clk_rate < CSR_F_60M))
|
|
|
|
priv->clk_csr = STMMAC_CSR_35_60M;
|
|
|
|
else if ((clk_rate >= CSR_F_60M) && (clk_rate < CSR_F_100M))
|
|
|
|
priv->clk_csr = STMMAC_CSR_60_100M;
|
|
|
|
else if ((clk_rate >= CSR_F_100M) && (clk_rate < CSR_F_150M))
|
|
|
|
priv->clk_csr = STMMAC_CSR_100_150M;
|
|
|
|
else if ((clk_rate >= CSR_F_150M) && (clk_rate < CSR_F_250M))
|
|
|
|
priv->clk_csr = STMMAC_CSR_150_250M;
|
2015-12-14 10:32:01 +07:00
|
|
|
else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M))
|
2012-04-04 11:33:27 +07:00
|
|
|
priv->clk_csr = STMMAC_CSR_250_300M;
|
2013-04-08 09:10:01 +07:00
|
|
|
}
|
2012-04-04 11:33:27 +07:00
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
static void print_pkt(unsigned char *buf, int len)
|
|
|
|
{
|
2014-11-07 21:53:12 +07:00
|
|
|
pr_debug("len = %d byte, buf addr: 0x%p\n", len, buf);
|
|
|
|
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline u32 stmmac_tx_avail(struct stmmac_priv *priv)
|
|
|
|
{
|
2016-02-29 20:27:30 +07:00
|
|
|
unsigned avail;
|
|
|
|
|
|
|
|
if (priv->dirty_tx > priv->cur_tx)
|
|
|
|
avail = priv->dirty_tx - priv->cur_tx - 1;
|
|
|
|
else
|
|
|
|
avail = DMA_TX_SIZE - priv->cur_tx + priv->dirty_tx - 1;
|
|
|
|
|
|
|
|
return avail;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
unsigned dirty;
|
|
|
|
|
|
|
|
if (priv->dirty_rx <= priv->cur_rx)
|
|
|
|
dirty = priv->cur_rx - priv->dirty_rx;
|
|
|
|
else
|
|
|
|
dirty = DMA_RX_SIZE - priv->dirty_rx + priv->cur_rx;
|
|
|
|
|
|
|
|
return dirty;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_hw_fix_mac_speed - callback for speed selection
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: on some platforms (e.g. ST), some HW system configuraton
|
|
|
|
* registers have to be set according to the link speed negotiated.
|
2010-11-24 09:37:58 +07:00
|
|
|
*/
|
|
|
|
static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
struct phy_device *phydev = priv->phydev;
|
|
|
|
|
|
|
|
if (likely(priv->plat->fix_mac_speed))
|
2013-04-08 09:10:01 +07:00
|
|
|
priv->plat->fix_mac_speed(priv->plat->bsp_priv, phydev->speed);
|
2010-11-24 09:37:58 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_enable_eee_mode - check and enter in LPI mode
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: this function is to verify and enter in LPI mode in case of
|
|
|
|
* EEE.
|
2013-04-08 09:10:00 +07:00
|
|
|
*/
|
2012-06-28 04:14:37 +07:00
|
|
|
static void stmmac_enable_eee_mode(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
/* Check and enter in LPI mode */
|
|
|
|
if ((priv->dirty_tx == priv->cur_tx) &&
|
|
|
|
(priv->tx_path_in_lpi_mode == false))
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->set_eee_mode(priv->hw);
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_disable_eee_mode - disable and exit from LPI mode
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: this function is to exit and disable EEE in case of
|
|
|
|
* LPI state is true. This is called by the xmit.
|
|
|
|
*/
|
2012-06-28 04:14:37 +07:00
|
|
|
void stmmac_disable_eee_mode(struct stmmac_priv *priv)
|
|
|
|
{
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->reset_eee_mode(priv->hw);
|
2012-06-28 04:14:37 +07:00
|
|
|
del_timer_sync(&priv->eee_ctrl_timer);
|
|
|
|
priv->tx_path_in_lpi_mode = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_eee_ctrl_timer - EEE TX SW timer.
|
2012-06-28 04:14:37 +07:00
|
|
|
* @arg : data hook
|
|
|
|
* Description:
|
2013-04-08 09:10:00 +07:00
|
|
|
* if there is no data transfer and if we are not in LPI state,
|
2012-06-28 04:14:37 +07:00
|
|
|
* then MAC Transmitter can be moved to LPI state.
|
|
|
|
*/
|
|
|
|
static void stmmac_eee_ctrl_timer(unsigned long arg)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = (struct stmmac_priv *)arg;
|
|
|
|
|
|
|
|
stmmac_enable_eee_mode(priv);
|
2013-06-18 12:03:23 +07:00
|
|
|
mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer));
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_eee_init - init EEE
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2012-06-28 04:14:37 +07:00
|
|
|
* Description:
|
2014-11-18 15:47:01 +07:00
|
|
|
* if the GMAC supports the EEE (from the HW cap reg) and the phy device
|
|
|
|
* can also manage EEE, this function enable the LPI state and start related
|
|
|
|
* timer.
|
2012-06-28 04:14:37 +07:00
|
|
|
*/
|
|
|
|
bool stmmac_eee_init(struct stmmac_priv *priv)
|
|
|
|
{
|
2014-11-04 23:08:08 +07:00
|
|
|
unsigned long flags;
|
2012-06-28 04:14:37 +07:00
|
|
|
bool ret = false;
|
|
|
|
|
2013-06-18 12:03:23 +07:00
|
|
|
/* Using PCS we cannot dial with the phy registers at this stage
|
|
|
|
* so we do not support extra feature like EEE.
|
|
|
|
*/
|
2016-06-24 20:16:25 +07:00
|
|
|
if ((priv->hw->pcs == STMMAC_PCS_RGMII) ||
|
|
|
|
(priv->hw->pcs == STMMAC_PCS_TBI) ||
|
|
|
|
(priv->hw->pcs == STMMAC_PCS_RTBI))
|
2013-06-18 12:03:23 +07:00
|
|
|
goto out;
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
/* MAC core supports the EEE feature. */
|
|
|
|
if (priv->dma_cap.eee) {
|
2014-03-10 19:40:31 +07:00
|
|
|
int tx_lpi_timer = priv->tx_lpi_timer;
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
/* Check if the PHY supports EEE */
|
2014-03-10 19:40:31 +07:00
|
|
|
if (phy_init_eee(priv->phydev, 1)) {
|
|
|
|
/* To manage at run-time if the EEE cannot be supported
|
|
|
|
* anymore (for example because the lp caps have been
|
|
|
|
* changed).
|
|
|
|
* In that case the driver disable own timers.
|
|
|
|
*/
|
2014-11-04 23:08:08 +07:00
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
2014-03-10 19:40:31 +07:00
|
|
|
if (priv->eee_active) {
|
|
|
|
pr_debug("stmmac: disable EEE\n");
|
|
|
|
del_timer_sync(&priv->eee_ctrl_timer);
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->set_eee_timer(priv->hw, 0,
|
2014-03-10 19:40:31 +07:00
|
|
|
tx_lpi_timer);
|
|
|
|
}
|
|
|
|
priv->eee_active = 0;
|
2014-11-04 23:08:08 +07:00
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
2012-06-28 04:14:37 +07:00
|
|
|
goto out;
|
2014-03-10 19:40:31 +07:00
|
|
|
}
|
|
|
|
/* Activate the EEE and start timers */
|
2014-11-04 23:08:08 +07:00
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
2013-06-18 12:03:23 +07:00
|
|
|
if (!priv->eee_active) {
|
|
|
|
priv->eee_active = 1;
|
2015-02-28 01:42:34 +07:00
|
|
|
setup_timer(&priv->eee_ctrl_timer,
|
|
|
|
stmmac_eee_ctrl_timer,
|
|
|
|
(unsigned long)priv);
|
|
|
|
mod_timer(&priv->eee_ctrl_timer,
|
|
|
|
STMMAC_LPI_T(eee_timer));
|
2013-06-18 12:03:23 +07:00
|
|
|
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->set_eee_timer(priv->hw,
|
2013-06-18 12:03:23 +07:00
|
|
|
STMMAC_DEFAULT_LIT_LS,
|
2014-03-10 19:40:31 +07:00
|
|
|
tx_lpi_timer);
|
2014-08-28 13:11:44 +07:00
|
|
|
}
|
|
|
|
/* Set HW EEE according to the speed */
|
|
|
|
priv->hw->mac->set_eee_pls(priv->hw, priv->phydev->link);
|
2012-06-28 04:14:37 +07:00
|
|
|
|
|
|
|
ret = true;
|
2014-11-04 23:08:08 +07:00
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
|
|
|
|
pr_debug("stmmac: Energy-Efficient Ethernet initialized\n");
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-18 15:47:01 +07:00
|
|
|
/* stmmac_get_tx_hwtstamp - get HW TX timestamps
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2013-03-26 11:43:10 +07:00
|
|
|
* @entry : descriptor index to be used.
|
|
|
|
* @skb : the socket buffer
|
|
|
|
* Description :
|
|
|
|
* This function will read timestamp from the descriptor & pass it to stack.
|
|
|
|
* and also perform some sanity checks.
|
|
|
|
*/
|
|
|
|
static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
|
2013-04-08 09:10:01 +07:00
|
|
|
unsigned int entry, struct sk_buff *skb)
|
2013-03-26 11:43:10 +07:00
|
|
|
{
|
|
|
|
struct skb_shared_hwtstamps shhwtstamp;
|
|
|
|
u64 ns;
|
|
|
|
void *desc = NULL;
|
|
|
|
|
|
|
|
if (!priv->hwts_tx_en)
|
|
|
|
return;
|
|
|
|
|
2013-04-08 09:10:01 +07:00
|
|
|
/* exit if skb doesn't support hw tstamp */
|
2014-01-17 22:47:59 +07:00
|
|
|
if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
|
2013-03-26 11:43:10 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (priv->adv_ts)
|
|
|
|
desc = (priv->dma_etx + entry);
|
|
|
|
else
|
|
|
|
desc = (priv->dma_tx + entry);
|
|
|
|
|
|
|
|
/* check tx tstamp status */
|
|
|
|
if (!priv->hw->desc->get_tx_timestamp_status((struct dma_desc *)desc))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* get the valid tstamp */
|
|
|
|
ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts);
|
|
|
|
|
|
|
|
memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
|
|
|
|
shhwtstamp.hwtstamp = ns_to_ktime(ns);
|
|
|
|
/* pass tstamp to stack */
|
|
|
|
skb_tstamp_tx(skb, &shhwtstamp);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-18 15:47:01 +07:00
|
|
|
/* stmmac_get_rx_hwtstamp - get HW RX timestamps
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2013-03-26 11:43:10 +07:00
|
|
|
* @entry : descriptor index to be used.
|
|
|
|
* @skb : the socket buffer
|
|
|
|
* Description :
|
|
|
|
* This function will read received packet's timestamp from the descriptor
|
|
|
|
* and pass it to stack. It also perform some sanity checks.
|
|
|
|
*/
|
|
|
|
static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv,
|
2013-04-08 09:10:01 +07:00
|
|
|
unsigned int entry, struct sk_buff *skb)
|
2013-03-26 11:43:10 +07:00
|
|
|
{
|
|
|
|
struct skb_shared_hwtstamps *shhwtstamp = NULL;
|
|
|
|
u64 ns;
|
|
|
|
void *desc = NULL;
|
|
|
|
|
|
|
|
if (!priv->hwts_rx_en)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (priv->adv_ts)
|
|
|
|
desc = (priv->dma_erx + entry);
|
|
|
|
else
|
|
|
|
desc = (priv->dma_rx + entry);
|
|
|
|
|
2013-04-08 09:10:01 +07:00
|
|
|
/* exit if rx tstamp is not valid */
|
2013-03-26 11:43:10 +07:00
|
|
|
if (!priv->hw->desc->get_rx_timestamp_status(desc, priv->adv_ts))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* get valid tstamp */
|
|
|
|
ns = priv->hw->desc->get_timestamp(desc, priv->adv_ts);
|
|
|
|
shhwtstamp = skb_hwtstamps(skb);
|
|
|
|
memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
|
|
|
|
shhwtstamp->hwtstamp = ns_to_ktime(ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_hwtstamp_ioctl - control hardware timestamping.
|
|
|
|
* @dev: device pointer.
|
|
|
|
* @ifr: An IOCTL specefic structure, that can contain a pointer to
|
|
|
|
* a proprietary structure used to pass information to the driver.
|
|
|
|
* Description:
|
|
|
|
* This function configures the MAC to enable/disable both outgoing(TX)
|
|
|
|
* and incoming(RX) packets time stamping based on user input.
|
|
|
|
* Return Value:
|
|
|
|
* 0 on success and an appropriate -ve integer on failure.
|
|
|
|
*/
|
|
|
|
static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
struct hwtstamp_config config;
|
2015-09-30 18:26:32 +07:00
|
|
|
struct timespec64 now;
|
2013-03-26 11:43:10 +07:00
|
|
|
u64 temp = 0;
|
|
|
|
u32 ptp_v2 = 0;
|
|
|
|
u32 tstamp_all = 0;
|
|
|
|
u32 ptp_over_ipv4_udp = 0;
|
|
|
|
u32 ptp_over_ipv6_udp = 0;
|
|
|
|
u32 ptp_over_ethernet = 0;
|
|
|
|
u32 snap_type_sel = 0;
|
|
|
|
u32 ts_master_en = 0;
|
|
|
|
u32 ts_event_en = 0;
|
|
|
|
u32 value = 0;
|
2015-12-14 10:32:01 +07:00
|
|
|
u32 sec_inc;
|
2013-03-26 11:43:10 +07:00
|
|
|
|
|
|
|
if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
|
|
|
|
netdev_alert(priv->dev, "No support for HW time stamping\n");
|
|
|
|
priv->hwts_tx_en = 0;
|
|
|
|
priv->hwts_rx_en = 0;
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_from_user(&config, ifr->ifr_data,
|
2013-04-08 09:10:01 +07:00
|
|
|
sizeof(struct hwtstamp_config)))
|
2013-03-26 11:43:10 +07:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
pr_debug("%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
|
|
|
|
__func__, config.flags, config.tx_type, config.rx_filter);
|
|
|
|
|
|
|
|
/* reserved for future extensions */
|
|
|
|
if (config.flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-14 07:43:41 +07:00
|
|
|
if (config.tx_type != HWTSTAMP_TX_OFF &&
|
|
|
|
config.tx_type != HWTSTAMP_TX_ON)
|
2013-03-26 11:43:10 +07:00
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
if (priv->adv_ts) {
|
|
|
|
switch (config.rx_filter) {
|
|
|
|
case HWTSTAMP_FILTER_NONE:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* time stamp no incoming packet at all */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_NONE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v1, UDP, any kind of event packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
|
|
|
|
/* take time stamp for all event messages */
|
|
|
|
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v1, UDP, Sync packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
|
|
|
|
/* take time stamp for SYNC messages only */
|
|
|
|
ts_event_en = PTP_TCR_TSEVNTENA;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v1, UDP, Delay_req packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
|
|
|
|
/* take time stamp for Delay_Req messages only */
|
|
|
|
ts_master_en = PTP_TCR_TSMSTRENA;
|
|
|
|
ts_event_en = PTP_TCR_TSEVNTENA;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v2, UDP, any kind of event packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
|
|
|
|
ptp_v2 = PTP_TCR_TSVER2ENA;
|
|
|
|
/* take time stamp for all event messages */
|
|
|
|
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v2, UDP, Sync packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
|
|
|
|
ptp_v2 = PTP_TCR_TSVER2ENA;
|
|
|
|
/* take time stamp for SYNC messages only */
|
|
|
|
ts_event_en = PTP_TCR_TSEVNTENA;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v2, UDP, Delay_req packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
|
|
|
|
ptp_v2 = PTP_TCR_TSVER2ENA;
|
|
|
|
/* take time stamp for Delay_Req messages only */
|
|
|
|
ts_master_en = PTP_TCR_TSMSTRENA;
|
|
|
|
ts_event_en = PTP_TCR_TSEVNTENA;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v2/802.AS1 any layer, any kind of event packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
|
|
|
ptp_v2 = PTP_TCR_TSVER2ENA;
|
|
|
|
/* take time stamp for all event messages */
|
|
|
|
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
ptp_over_ethernet = PTP_TCR_TSIPENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v2/802.AS1, any layer, Sync packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
|
|
|
|
ptp_v2 = PTP_TCR_TSVER2ENA;
|
|
|
|
/* take time stamp for SYNC messages only */
|
|
|
|
ts_event_en = PTP_TCR_TSEVNTENA;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
ptp_over_ethernet = PTP_TCR_TSIPENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* PTP v2/802.AS1, any layer, Delay_req packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
|
|
|
|
ptp_v2 = PTP_TCR_TSVER2ENA;
|
|
|
|
/* take time stamp for Delay_Req messages only */
|
|
|
|
ts_master_en = PTP_TCR_TSMSTRENA;
|
|
|
|
ts_event_en = PTP_TCR_TSEVNTENA;
|
|
|
|
|
|
|
|
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
|
|
|
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
|
|
|
ptp_over_ethernet = PTP_TCR_TSIPENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HWTSTAMP_FILTER_ALL:
|
2013-04-08 09:10:01 +07:00
|
|
|
/* time stamp any incoming packet */
|
2013-03-26 11:43:10 +07:00
|
|
|
config.rx_filter = HWTSTAMP_FILTER_ALL;
|
|
|
|
tstamp_all = PTP_TCR_TSENALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (config.rx_filter) {
|
|
|
|
case HWTSTAMP_FILTER_NONE:
|
|
|
|
config.rx_filter = HWTSTAMP_FILTER_NONE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* PTP v1, UDP, any kind of event packet */
|
|
|
|
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
|
2013-11-14 07:43:41 +07:00
|
|
|
priv->hwts_tx_en = config.tx_type == HWTSTAMP_TX_ON;
|
2013-03-26 11:43:10 +07:00
|
|
|
|
|
|
|
if (!priv->hwts_tx_en && !priv->hwts_rx_en)
|
|
|
|
priv->hw->ptp->config_hw_tstamping(priv->ioaddr, 0);
|
|
|
|
else {
|
|
|
|
value = (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR |
|
2013-04-08 09:10:01 +07:00
|
|
|
tstamp_all | ptp_v2 | ptp_over_ethernet |
|
|
|
|
ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en |
|
|
|
|
ts_master_en | snap_type_sel);
|
2013-03-26 11:43:10 +07:00
|
|
|
priv->hw->ptp->config_hw_tstamping(priv->ioaddr, value);
|
|
|
|
|
|
|
|
/* program Sub Second Increment reg */
|
2015-12-14 10:32:01 +07:00
|
|
|
sec_inc = priv->hw->ptp->config_sub_second_increment(
|
|
|
|
priv->ioaddr, priv->clk_ptp_rate);
|
|
|
|
temp = div_u64(1000000000ULL, sec_inc);
|
2013-03-26 11:43:10 +07:00
|
|
|
|
|
|
|
/* calculate default added value:
|
|
|
|
* formula is :
|
|
|
|
* addend = (2^32)/freq_div_ratio;
|
2015-12-14 10:32:01 +07:00
|
|
|
* where, freq_div_ratio = 1e9ns/sec_inc
|
2013-03-26 11:43:10 +07:00
|
|
|
*/
|
2015-12-14 10:32:01 +07:00
|
|
|
temp = (u64)(temp << 32);
|
2014-08-27 15:37:49 +07:00
|
|
|
priv->default_addend = div_u64(temp, priv->clk_ptp_rate);
|
2013-03-26 11:43:10 +07:00
|
|
|
priv->hw->ptp->config_addend(priv->ioaddr,
|
|
|
|
priv->default_addend);
|
|
|
|
|
|
|
|
/* initialize system time */
|
2015-09-30 18:26:32 +07:00
|
|
|
ktime_get_real_ts64(&now);
|
|
|
|
|
|
|
|
/* lower 32 bits of tv_sec are safe until y2106 */
|
|
|
|
priv->hw->ptp->init_systime(priv->ioaddr, (u32)now.tv_sec,
|
2013-03-26 11:43:10 +07:00
|
|
|
now.tv_nsec);
|
|
|
|
}
|
|
|
|
|
|
|
|
return copy_to_user(ifr->ifr_data, &config,
|
|
|
|
sizeof(struct hwtstamp_config)) ? -EFAULT : 0;
|
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_init_ptp - init PTP
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: this is to verify if the HW supports the PTPv1 or PTPv2.
|
2013-04-08 09:10:00 +07:00
|
|
|
* This is done by looking at the HW cap. register.
|
2014-11-18 15:47:01 +07:00
|
|
|
* This function also registers the ptp driver.
|
2013-04-08 09:10:00 +07:00
|
|
|
*/
|
2013-03-26 11:43:11 +07:00
|
|
|
static int stmmac_init_ptp(struct stmmac_priv *priv)
|
2013-03-26 11:43:10 +07:00
|
|
|
{
|
2013-03-26 11:43:11 +07:00
|
|
|
if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2014-08-27 15:37:49 +07:00
|
|
|
/* Fall-back to main clock in case of no PTP ref is passed */
|
|
|
|
priv->clk_ptp_ref = devm_clk_get(priv->device, "clk_ptp_ref");
|
|
|
|
if (IS_ERR(priv->clk_ptp_ref)) {
|
|
|
|
priv->clk_ptp_rate = clk_get_rate(priv->stmmac_clk);
|
|
|
|
priv->clk_ptp_ref = NULL;
|
2016-10-12 20:42:03 +07:00
|
|
|
netdev_dbg(priv->dev, "PTP uses main clock\n");
|
2014-08-27 15:37:49 +07:00
|
|
|
} else {
|
|
|
|
clk_prepare_enable(priv->clk_ptp_ref);
|
|
|
|
priv->clk_ptp_rate = clk_get_rate(priv->clk_ptp_ref);
|
2016-10-12 20:42:03 +07:00
|
|
|
netdev_dbg(priv->dev, "PTP rate %d\n", priv->clk_ptp_rate);
|
2014-08-27 15:37:49 +07:00
|
|
|
}
|
|
|
|
|
2013-12-21 00:19:34 +07:00
|
|
|
priv->adv_ts = 0;
|
2016-10-12 20:42:03 +07:00
|
|
|
/* Check if adv_ts can be enabled for dwmac 4.x core */
|
|
|
|
if (priv->plat->has_gmac4 && priv->dma_cap.atime_stamp)
|
|
|
|
priv->adv_ts = 1;
|
|
|
|
/* Dwmac 3.x core with extend_desc can support adv_ts */
|
|
|
|
else if (priv->extend_desc && priv->dma_cap.atime_stamp)
|
2013-12-21 00:19:34 +07:00
|
|
|
priv->adv_ts = 1;
|
|
|
|
|
2016-10-12 20:42:03 +07:00
|
|
|
if (priv->dma_cap.time_stamp)
|
|
|
|
netdev_info(priv->dev, "IEEE 1588-2002 Timestamp supported\n");
|
2013-12-21 00:19:34 +07:00
|
|
|
|
2016-10-12 20:42:03 +07:00
|
|
|
if (priv->adv_ts)
|
|
|
|
netdev_info(priv->dev,
|
|
|
|
"IEEE 1588-2008 Advanced Timestamp supported\n");
|
2013-03-26 11:43:10 +07:00
|
|
|
|
|
|
|
priv->hw->ptp = &stmmac_ptp;
|
|
|
|
priv->hwts_tx_en = 0;
|
|
|
|
priv->hwts_rx_en = 0;
|
2013-03-26 11:43:11 +07:00
|
|
|
|
2016-10-19 14:06:41 +07:00
|
|
|
stmmac_ptp_register(priv);
|
|
|
|
|
|
|
|
return 0;
|
2013-03-26 11:43:11 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void stmmac_release_ptp(struct stmmac_priv *priv)
|
|
|
|
{
|
2014-08-27 15:37:49 +07:00
|
|
|
if (priv->clk_ptp_ref)
|
|
|
|
clk_disable_unprepare(priv->clk_ptp_ref);
|
2013-03-26 11:43:11 +07:00
|
|
|
stmmac_ptp_unregister(priv);
|
2013-03-26 11:43:10 +07:00
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_adjust_link - adjusts the link parameters
|
2009-10-15 05:13:45 +07:00
|
|
|
* @dev: net device structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: this is the helper called by the physical abstraction layer
|
|
|
|
* drivers to communicate the phy link status. According the speed and duplex
|
|
|
|
* this driver can invoke registered glue-logic as well.
|
|
|
|
* It also invoke the eee initialization because it could happen when switch
|
|
|
|
* on different networks (that are eee capable).
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static void stmmac_adjust_link(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
struct phy_device *phydev = priv->phydev;
|
|
|
|
unsigned long flags;
|
|
|
|
int new_state = 0;
|
|
|
|
unsigned int fc = priv->flow_ctrl, pause_time = priv->pause;
|
|
|
|
|
|
|
|
if (phydev == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
2012-06-28 04:14:37 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (phydev->link) {
|
2010-08-24 03:40:42 +07:00
|
|
|
u32 ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
/* Now we make sure that we can be in full duplex mode.
|
|
|
|
* If not, we operate in half-duplex mode. */
|
|
|
|
if (phydev->duplex != priv->oldduplex) {
|
|
|
|
new_state = 1;
|
|
|
|
if (!(phydev->duplex))
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl &= ~priv->hw->link.duplex;
|
2009-10-15 05:13:45 +07:00
|
|
|
else
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl |= priv->hw->link.duplex;
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->oldduplex = phydev->duplex;
|
|
|
|
}
|
|
|
|
/* Flow Control operation */
|
|
|
|
if (phydev->pause)
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->flow_ctrl(priv->hw, phydev->duplex,
|
2010-01-07 06:07:17 +07:00
|
|
|
fc, pause_time);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
if (phydev->speed != priv->speed) {
|
|
|
|
new_state = 1;
|
|
|
|
switch (phydev->speed) {
|
|
|
|
case 1000:
|
2016-04-01 16:37:34 +07:00
|
|
|
if (likely((priv->plat->has_gmac) ||
|
|
|
|
(priv->plat->has_gmac4)))
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl &= ~priv->hw->link.port;
|
2013-04-08 09:10:01 +07:00
|
|
|
stmmac_hw_fix_mac_speed(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
break;
|
|
|
|
case 100:
|
|
|
|
case 10:
|
2016-04-01 16:37:34 +07:00
|
|
|
if (likely((priv->plat->has_gmac) ||
|
|
|
|
(priv->plat->has_gmac4))) {
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl |= priv->hw->link.port;
|
2009-10-15 05:13:45 +07:00
|
|
|
if (phydev->speed == SPEED_100) {
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl |= priv->hw->link.speed;
|
2009-10-15 05:13:45 +07:00
|
|
|
} else {
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl &= ~(priv->hw->link.speed);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
} else {
|
2010-01-07 06:07:17 +07:00
|
|
|
ctrl &= ~priv->hw->link.port;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2010-11-24 09:37:58 +07:00
|
|
|
stmmac_hw_fix_mac_speed(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (netif_msg_link(priv))
|
2013-04-08 09:10:01 +07:00
|
|
|
pr_warn("%s: Speed (%d) not 10/100\n",
|
|
|
|
dev->name, phydev->speed);
|
2009-10-15 05:13:45 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->speed = phydev->speed;
|
|
|
|
}
|
|
|
|
|
2010-08-24 03:40:42 +07:00
|
|
|
writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
if (!priv->oldlink) {
|
|
|
|
new_state = 1;
|
|
|
|
priv->oldlink = 1;
|
|
|
|
}
|
|
|
|
} else if (priv->oldlink) {
|
|
|
|
new_state = 1;
|
|
|
|
priv->oldlink = 0;
|
|
|
|
priv->speed = 0;
|
|
|
|
priv->oldduplex = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_state && netif_msg_link(priv))
|
|
|
|
phy_print_status(phydev);
|
|
|
|
|
2014-11-04 23:08:08 +07:00
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
|
2016-04-05 13:46:57 +07:00
|
|
|
if (phydev->is_pseudo_fixed_link)
|
|
|
|
/* Stop PHY layer to call the hook to adjust the link in case
|
|
|
|
* of a switch is attached to the stmmac driver.
|
|
|
|
*/
|
|
|
|
phydev->irq = PHY_IGNORE_INTERRUPT;
|
|
|
|
else
|
|
|
|
/* At this stage, init the EEE if supported.
|
|
|
|
* Never called in case of fixed_link.
|
|
|
|
*/
|
|
|
|
priv->eee_enabled = stmmac_eee_init(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_check_pcs_mode - verify if RGMII/SGMII is supported
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: this is to verify if the HW supports the PCS.
|
|
|
|
* Physical Coding Sublayer (PCS) interface that can be used when the MAC is
|
|
|
|
* configured for the TBI, RTBI, or SGMII PHY interface.
|
|
|
|
*/
|
2013-03-26 11:43:08 +07:00
|
|
|
static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int interface = priv->plat->interface;
|
|
|
|
|
|
|
|
if (priv->dma_cap.pcs) {
|
2013-06-28 14:35:31 +07:00
|
|
|
if ((interface == PHY_INTERFACE_MODE_RGMII) ||
|
|
|
|
(interface == PHY_INTERFACE_MODE_RGMII_ID) ||
|
|
|
|
(interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
|
|
|
|
(interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
|
2013-03-26 11:43:08 +07:00
|
|
|
pr_debug("STMMAC: PCS RGMII support enable\n");
|
2016-06-24 20:16:25 +07:00
|
|
|
priv->hw->pcs = STMMAC_PCS_RGMII;
|
2013-06-28 14:35:31 +07:00
|
|
|
} else if (interface == PHY_INTERFACE_MODE_SGMII) {
|
2013-03-26 11:43:08 +07:00
|
|
|
pr_debug("STMMAC: PCS SGMII support enable\n");
|
2016-06-24 20:16:25 +07:00
|
|
|
priv->hw->pcs = STMMAC_PCS_SGMII;
|
2013-03-26 11:43:08 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
|
|
|
* stmmac_init_phy - PHY initialization
|
|
|
|
* @dev: net device structure
|
|
|
|
* Description: it initializes the driver's PHY state, and attaches the PHY
|
|
|
|
* to the mac driver.
|
|
|
|
* Return value:
|
|
|
|
* 0 on success
|
|
|
|
*/
|
|
|
|
static int stmmac_init_phy(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
struct phy_device *phydev;
|
2012-06-28 04:14:37 +07:00
|
|
|
char phy_id_fmt[MII_BUS_ID_SIZE + 3];
|
2010-01-07 06:07:11 +07:00
|
|
|
char bus_id[MII_BUS_ID_SIZE];
|
2011-10-18 07:01:18 +07:00
|
|
|
int interface = priv->plat->interface;
|
2014-01-16 17:51:43 +07:00
|
|
|
int max_speed = priv->plat->max_speed;
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->oldlink = 0;
|
|
|
|
priv->speed = 0;
|
|
|
|
priv->oldduplex = -1;
|
|
|
|
|
2015-05-28 01:02:47 +07:00
|
|
|
if (priv->plat->phy_node) {
|
|
|
|
phydev = of_phy_connect(dev, priv->plat->phy_node,
|
|
|
|
&stmmac_adjust_link, 0, interface);
|
|
|
|
} else {
|
2016-04-01 14:07:16 +07:00
|
|
|
snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
|
|
|
|
priv->plat->bus_id);
|
2015-05-28 01:02:47 +07:00
|
|
|
|
|
|
|
snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
|
|
priv->plat->phy_addr);
|
|
|
|
pr_debug("stmmac_init_phy: trying to attach to %s\n",
|
|
|
|
phy_id_fmt);
|
|
|
|
|
|
|
|
phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link,
|
|
|
|
interface);
|
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2015-09-09 22:01:08 +07:00
|
|
|
if (IS_ERR_OR_NULL(phydev)) {
|
2009-10-15 05:13:45 +07:00
|
|
|
pr_err("%s: Could not attach to PHY\n", dev->name);
|
2015-09-09 22:01:08 +07:00
|
|
|
if (!phydev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
return PTR_ERR(phydev);
|
|
|
|
}
|
|
|
|
|
2011-10-18 07:01:18 +07:00
|
|
|
/* Stop Advertising 1000BASE Capability if interface is not GMII */
|
2011-11-17 04:57:59 +07:00
|
|
|
if ((interface == PHY_INTERFACE_MODE_MII) ||
|
2014-01-16 17:51:43 +07:00
|
|
|
(interface == PHY_INTERFACE_MODE_RMII) ||
|
2014-08-25 18:31:16 +07:00
|
|
|
(max_speed < 1000 && max_speed > 0))
|
2011-11-17 04:57:59 +07:00
|
|
|
phydev->advertising &= ~(SUPPORTED_1000baseT_Half |
|
|
|
|
SUPPORTED_1000baseT_Full);
|
2011-10-18 07:01:18 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2015-05-28 01:02:48 +07:00
|
|
|
if (!priv->plat->phy_node && phydev->phy_id == 0) {
|
2009-10-15 05:13:45 +07:00
|
|
|
phy_disconnect(phydev);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2016-02-29 20:27:39 +07:00
|
|
|
|
net: stmmac: Fix lack of link transition for fixed PHYs
Commit 52f95bbfcf72 ("stmmac: fix adjust link call in case of a switch
is attached") added some logic to avoid polling the fixed PHY and
therefore invoking the adjust_link callback more than once, since this
is a fixed PHY and link events won't be generated.
This works fine the first time, because we start with phydev->irq =
PHY_POLL, so we call adjust_link, then we set phydev->irq =
PHY_IGNORE_INTERRUPT and we stop polling the PHY.
Now, if we called ndo_close(), which calls both phy_stop() and does an
explicit netif_carrier_off(), we end up with a link down. Upon calling
ndo_open() again, despite starting the PHY state machine, we have
PHY_IGNORE_INTERRUPT set, and we generate no link event at all, so the
link is permanently down.
Fixes: 52f95bbfcf72 ("stmmac: fix adjust link call in case of a switch is attached")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-11-14 08:50:35 +07:00
|
|
|
/* stmmac_adjust_link will change this to PHY_IGNORE_INTERRUPT to avoid
|
|
|
|
* subsequent PHY polling, make sure we force a link transition if
|
|
|
|
* we have a UP/DOWN/UP transition
|
|
|
|
*/
|
|
|
|
if (phydev->is_pseudo_fixed_link)
|
|
|
|
phydev->irq = PHY_POLL;
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
pr_debug("stmmac_init_phy: %s: attached to PHY (UID 0x%x)"
|
2011-07-20 07:05:23 +07:00
|
|
|
" Link = %d\n", dev->name, phydev->phy_id, phydev->link);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
priv->phydev = phydev;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
static void stmmac_display_rings(struct stmmac_priv *priv)
|
|
|
|
{
|
2016-04-01 16:37:26 +07:00
|
|
|
void *head_rx, *head_tx;
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc) {
|
2016-04-01 16:37:26 +07:00
|
|
|
head_rx = (void *)priv->dma_erx;
|
|
|
|
head_tx = (void *)priv->dma_etx;
|
2013-03-26 11:43:06 +07:00
|
|
|
} else {
|
2016-04-01 16:37:26 +07:00
|
|
|
head_rx = (void *)priv->dma_rx;
|
|
|
|
head_tx = (void *)priv->dma_tx;
|
2013-03-26 11:43:06 +07:00
|
|
|
}
|
2016-04-01 16:37:26 +07:00
|
|
|
|
|
|
|
/* Display Rx ring */
|
|
|
|
priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true);
|
|
|
|
/* Display Tx ring */
|
|
|
|
priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false);
|
2013-03-26 11:43:06 +07:00
|
|
|
}
|
|
|
|
|
2011-10-18 07:01:24 +07:00
|
|
|
static int stmmac_set_bfsize(int mtu, int bufsize)
|
|
|
|
{
|
|
|
|
int ret = bufsize;
|
|
|
|
|
|
|
|
if (mtu >= BUF_SIZE_4KiB)
|
|
|
|
ret = BUF_SIZE_8KiB;
|
|
|
|
else if (mtu >= BUF_SIZE_2KiB)
|
|
|
|
ret = BUF_SIZE_4KiB;
|
2014-03-10 19:40:32 +07:00
|
|
|
else if (mtu > DEFAULT_BUFSIZE)
|
2011-10-18 07:01:24 +07:00
|
|
|
ret = BUF_SIZE_2KiB;
|
|
|
|
else
|
2014-03-10 19:40:32 +07:00
|
|
|
ret = DEFAULT_BUFSIZE;
|
2011-10-18 07:01:24 +07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_clear_descriptors - clear descriptors
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: this function is called to clear the tx and rx descriptors
|
|
|
|
* in case of both basic and extended descriptors are used.
|
|
|
|
*/
|
2013-03-26 11:43:06 +07:00
|
|
|
static void stmmac_clear_descriptors(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Clear the Rx/Tx descriptors */
|
2016-02-29 20:27:30 +07:00
|
|
|
for (i = 0; i < DMA_RX_SIZE; i++)
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
|
|
|
priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic,
|
|
|
|
priv->use_riwt, priv->mode,
|
2016-02-29 20:27:30 +07:00
|
|
|
(i == DMA_RX_SIZE - 1));
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
priv->hw->desc->init_rx_desc(&priv->dma_rx[i],
|
|
|
|
priv->use_riwt, priv->mode,
|
2016-02-29 20:27:30 +07:00
|
|
|
(i == DMA_RX_SIZE - 1));
|
|
|
|
for (i = 0; i < DMA_TX_SIZE; i++)
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
|
|
|
priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic,
|
|
|
|
priv->mode,
|
2016-02-29 20:27:30 +07:00
|
|
|
(i == DMA_TX_SIZE - 1));
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
priv->hw->desc->init_tx_desc(&priv->dma_tx[i],
|
|
|
|
priv->mode,
|
2016-02-29 20:27:30 +07:00
|
|
|
(i == DMA_TX_SIZE - 1));
|
2013-03-26 11:43:06 +07:00
|
|
|
}
|
|
|
|
|
2014-11-18 15:47:01 +07:00
|
|
|
/**
|
|
|
|
* stmmac_init_rx_buffers - init the RX descriptor buffer.
|
|
|
|
* @priv: driver private structure
|
|
|
|
* @p: descriptor pointer
|
|
|
|
* @i: descriptor index
|
|
|
|
* @flags: gfp flag.
|
|
|
|
* Description: this function is called to allocate a receive buffer, perform
|
|
|
|
* the DMA mapping and init the descriptor.
|
|
|
|
*/
|
2013-03-26 11:43:06 +07:00
|
|
|
static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
|
2014-11-04 23:08:09 +07:00
|
|
|
int i, gfp_t flags)
|
2013-03-26 11:43:06 +07:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2015-05-20 13:34:40 +07:00
|
|
|
skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags);
|
2013-08-09 19:02:08 +07:00
|
|
|
if (!skb) {
|
2013-03-26 11:43:06 +07:00
|
|
|
pr_err("%s: Rx init fails; skb is NULL\n", __func__);
|
2013-08-09 19:02:08 +07:00
|
|
|
return -ENOMEM;
|
2013-03-26 11:43:06 +07:00
|
|
|
}
|
|
|
|
priv->rx_skbuff[i] = skb;
|
|
|
|
priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
|
|
|
|
priv->dma_buf_sz,
|
|
|
|
DMA_FROM_DEVICE);
|
2013-08-09 19:02:08 +07:00
|
|
|
if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) {
|
|
|
|
pr_err("%s: DMA mapping error\n", __func__);
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-03-26 11:43:06 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00)
|
|
|
|
p->des0 = priv->rx_skbuff_dma[i];
|
|
|
|
else
|
|
|
|
p->des2 = priv->rx_skbuff_dma[i];
|
2013-03-26 11:43:06 +07:00
|
|
|
|
2014-03-10 19:40:33 +07:00
|
|
|
if ((priv->hw->mode->init_desc3) &&
|
2013-03-26 11:43:06 +07:00
|
|
|
(priv->dma_buf_sz == BUF_SIZE_16KiB))
|
2014-03-10 19:40:33 +07:00
|
|
|
priv->hw->mode->init_desc3(p);
|
2013-03-26 11:43:06 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-09 19:02:08 +07:00
|
|
|
static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i)
|
|
|
|
{
|
|
|
|
if (priv->rx_skbuff[i]) {
|
|
|
|
dma_unmap_single(priv->device, priv->rx_skbuff_dma[i],
|
|
|
|
priv->dma_buf_sz, DMA_FROM_DEVICE);
|
|
|
|
dev_kfree_skb_any(priv->rx_skbuff[i]);
|
|
|
|
}
|
|
|
|
priv->rx_skbuff[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
|
|
|
* init_dma_desc_rings - init the RX/TX descriptor rings
|
|
|
|
* @dev: net device structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* @flags: gfp flag.
|
|
|
|
* Description: this function initializes the DMA RX/TX descriptors
|
2011-10-18 07:01:24 +07:00
|
|
|
* and allocates the socket buffers. It suppors the chained and ring
|
|
|
|
* modes.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
2014-11-04 23:08:09 +07:00
|
|
|
static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
2013-03-26 11:43:05 +07:00
|
|
|
unsigned int bfsize = 0;
|
2013-08-09 19:02:08 +07:00
|
|
|
int ret = -ENOMEM;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-03-10 19:40:33 +07:00
|
|
|
if (priv->hw->mode->set_16kib_bfsize)
|
|
|
|
bfsize = priv->hw->mode->set_16kib_bfsize(dev->mtu);
|
2011-10-18 07:01:24 +07:00
|
|
|
|
2013-03-26 11:43:05 +07:00
|
|
|
if (bfsize < BUF_SIZE_16KiB)
|
2011-10-18 07:01:24 +07:00
|
|
|
bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-01-20 18:39:01 +07:00
|
|
|
priv->dma_buf_sz = bfsize;
|
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_probe(priv)) {
|
2013-03-26 11:43:06 +07:00
|
|
|
pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__,
|
|
|
|
(u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
/* RX INITIALIZATION */
|
|
|
|
pr_debug("\tSKB addresses:\nskb\t\tskb data\tdma data\n");
|
|
|
|
}
|
2016-02-29 20:27:30 +07:00
|
|
|
for (i = 0; i < DMA_RX_SIZE; i++) {
|
2013-03-26 11:43:06 +07:00
|
|
|
struct dma_desc *p;
|
|
|
|
if (priv->extend_desc)
|
|
|
|
p = &((priv->dma_erx + i)->basic);
|
|
|
|
else
|
|
|
|
p = priv->dma_rx + i;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-11-04 23:08:09 +07:00
|
|
|
ret = stmmac_init_rx_buffers(priv, p, i, flags);
|
2013-08-09 19:02:08 +07:00
|
|
|
if (ret)
|
|
|
|
goto err_init_rx_buffers;
|
2011-10-18 07:01:24 +07:00
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_probe(priv))
|
|
|
|
pr_debug("[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],
|
|
|
|
priv->rx_skbuff[i]->data,
|
|
|
|
(unsigned int)priv->rx_skbuff_dma[i]);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
priv->cur_rx = 0;
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->dirty_rx = (unsigned int)(i - DMA_RX_SIZE);
|
2009-10-15 05:13:45 +07:00
|
|
|
buf_sz = bfsize;
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
/* Setup the chained descriptor addresses */
|
|
|
|
if (priv->mode == STMMAC_CHAIN_MODE) {
|
|
|
|
if (priv->extend_desc) {
|
2014-03-10 19:40:33 +07:00
|
|
|
priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy,
|
2016-02-29 20:27:30 +07:00
|
|
|
DMA_RX_SIZE, 1);
|
2014-03-10 19:40:33 +07:00
|
|
|
priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy,
|
2016-02-29 20:27:30 +07:00
|
|
|
DMA_TX_SIZE, 1);
|
2013-03-26 11:43:06 +07:00
|
|
|
} else {
|
2014-03-10 19:40:33 +07:00
|
|
|
priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy,
|
2016-02-29 20:27:30 +07:00
|
|
|
DMA_RX_SIZE, 0);
|
2014-03-10 19:40:33 +07:00
|
|
|
priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy,
|
2016-02-29 20:27:30 +07:00
|
|
|
DMA_TX_SIZE, 0);
|
2013-03-26 11:43:06 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/* TX INITIALIZATION */
|
2016-02-29 20:27:30 +07:00
|
|
|
for (i = 0; i < DMA_TX_SIZE; i++) {
|
2013-03-26 11:43:06 +07:00
|
|
|
struct dma_desc *p;
|
|
|
|
if (priv->extend_desc)
|
|
|
|
p = &((priv->dma_etx + i)->basic);
|
|
|
|
else
|
|
|
|
p = priv->dma_tx + i;
|
2016-04-01 16:37:34 +07:00
|
|
|
|
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00) {
|
|
|
|
p->des0 = 0;
|
|
|
|
p->des1 = 0;
|
|
|
|
p->des2 = 0;
|
|
|
|
p->des3 = 0;
|
|
|
|
} else {
|
|
|
|
p->des2 = 0;
|
|
|
|
}
|
|
|
|
|
2014-08-27 16:27:00 +07:00
|
|
|
priv->tx_skbuff_dma[i].buf = 0;
|
|
|
|
priv->tx_skbuff_dma[i].map_as_page = false;
|
2016-02-29 20:27:31 +07:00
|
|
|
priv->tx_skbuff_dma[i].len = 0;
|
2016-02-29 20:27:32 +07:00
|
|
|
priv->tx_skbuff_dma[i].last_segment = false;
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->tx_skbuff[i] = NULL;
|
|
|
|
}
|
2011-10-18 07:01:24 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->dirty_tx = 0;
|
|
|
|
priv->cur_tx = 0;
|
2015-01-22 01:07:27 +07:00
|
|
|
netdev_reset_queue(priv->dev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
stmmac_clear_descriptors(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (netif_msg_hw(priv))
|
|
|
|
stmmac_display_rings(priv);
|
2013-08-09 19:02:08 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err_init_rx_buffers:
|
|
|
|
while (--i >= 0)
|
|
|
|
stmmac_free_rx_buffers(priv, i);
|
|
|
|
return ret;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dma_free_rx_skbufs(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
for (i = 0; i < DMA_RX_SIZE; i++)
|
2013-08-09 19:02:08 +07:00
|
|
|
stmmac_free_rx_buffers(priv, i);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dma_free_tx_skbufs(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
for (i = 0; i < DMA_TX_SIZE; i++) {
|
2014-01-17 22:47:59 +07:00
|
|
|
struct dma_desc *p;
|
|
|
|
|
|
|
|
if (priv->extend_desc)
|
|
|
|
p = &((priv->dma_etx + i)->basic);
|
|
|
|
else
|
|
|
|
p = priv->dma_tx + i;
|
|
|
|
|
2014-08-27 16:27:00 +07:00
|
|
|
if (priv->tx_skbuff_dma[i].buf) {
|
|
|
|
if (priv->tx_skbuff_dma[i].map_as_page)
|
|
|
|
dma_unmap_page(priv->device,
|
|
|
|
priv->tx_skbuff_dma[i].buf,
|
2016-02-29 20:27:31 +07:00
|
|
|
priv->tx_skbuff_dma[i].len,
|
2014-08-27 16:27:00 +07:00
|
|
|
DMA_TO_DEVICE);
|
|
|
|
else
|
|
|
|
dma_unmap_single(priv->device,
|
|
|
|
priv->tx_skbuff_dma[i].buf,
|
2016-02-29 20:27:31 +07:00
|
|
|
priv->tx_skbuff_dma[i].len,
|
2014-08-27 16:27:00 +07:00
|
|
|
DMA_TO_DEVICE);
|
2014-01-17 22:47:59 +07:00
|
|
|
}
|
2013-03-26 11:43:06 +07:00
|
|
|
|
2014-01-17 22:47:59 +07:00
|
|
|
if (priv->tx_skbuff[i] != NULL) {
|
2009-10-15 05:13:45 +07:00
|
|
|
dev_kfree_skb_any(priv->tx_skbuff[i]);
|
|
|
|
priv->tx_skbuff[i] = NULL;
|
2014-08-27 16:27:00 +07:00
|
|
|
priv->tx_skbuff_dma[i].buf = 0;
|
|
|
|
priv->tx_skbuff_dma[i].map_as_page = false;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-18 15:47:01 +07:00
|
|
|
/**
|
|
|
|
* alloc_dma_desc_resources - alloc TX/RX resources.
|
|
|
|
* @priv: private structure
|
|
|
|
* Description: according to which descriptor can be used (extend or basic)
|
|
|
|
* this function allocates the resources for TX and RX paths. In case of
|
|
|
|
* reception, for example, it pre-allocated the RX socket buffer in order to
|
|
|
|
* allow zero-copy mechanism.
|
|
|
|
*/
|
2014-01-16 17:52:06 +07:00
|
|
|
static int alloc_dma_desc_resources(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, sizeof(dma_addr_t),
|
2014-01-16 17:52:06 +07:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!priv->rx_skbuff_dma)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->rx_skbuff = kmalloc_array(DMA_RX_SIZE, sizeof(struct sk_buff *),
|
2014-01-16 17:52:06 +07:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!priv->rx_skbuff)
|
|
|
|
goto err_rx_skbuff;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE,
|
2014-08-27 16:27:00 +07:00
|
|
|
sizeof(*priv->tx_skbuff_dma),
|
2014-01-16 17:52:06 +07:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!priv->tx_skbuff_dma)
|
|
|
|
goto err_tx_skbuff_dma;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->tx_skbuff = kmalloc_array(DMA_TX_SIZE, sizeof(struct sk_buff *),
|
2014-01-16 17:52:06 +07:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!priv->tx_skbuff)
|
|
|
|
goto err_tx_skbuff;
|
|
|
|
|
|
|
|
if (priv->extend_desc) {
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE *
|
2015-06-24 15:47:41 +07:00
|
|
|
sizeof(struct
|
|
|
|
dma_extended_desc),
|
|
|
|
&priv->dma_rx_phy,
|
|
|
|
GFP_KERNEL);
|
2014-01-16 17:52:06 +07:00
|
|
|
if (!priv->dma_erx)
|
|
|
|
goto err_dma;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->dma_etx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE *
|
2015-06-24 15:47:41 +07:00
|
|
|
sizeof(struct
|
|
|
|
dma_extended_desc),
|
|
|
|
&priv->dma_tx_phy,
|
|
|
|
GFP_KERNEL);
|
2014-01-16 17:52:06 +07:00
|
|
|
if (!priv->dma_etx) {
|
2016-02-29 20:27:30 +07:00
|
|
|
dma_free_coherent(priv->device, DMA_RX_SIZE *
|
2015-06-24 15:47:41 +07:00
|
|
|
sizeof(struct dma_extended_desc),
|
|
|
|
priv->dma_erx, priv->dma_rx_phy);
|
2014-01-16 17:52:06 +07:00
|
|
|
goto err_dma;
|
|
|
|
}
|
|
|
|
} else {
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE *
|
2015-06-24 15:47:41 +07:00
|
|
|
sizeof(struct dma_desc),
|
|
|
|
&priv->dma_rx_phy,
|
|
|
|
GFP_KERNEL);
|
2014-01-16 17:52:06 +07:00
|
|
|
if (!priv->dma_rx)
|
|
|
|
goto err_dma;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE *
|
2015-06-24 15:47:41 +07:00
|
|
|
sizeof(struct dma_desc),
|
|
|
|
&priv->dma_tx_phy,
|
|
|
|
GFP_KERNEL);
|
2014-01-16 17:52:06 +07:00
|
|
|
if (!priv->dma_tx) {
|
2016-02-29 20:27:30 +07:00
|
|
|
dma_free_coherent(priv->device, DMA_RX_SIZE *
|
2015-06-24 15:47:41 +07:00
|
|
|
sizeof(struct dma_desc),
|
|
|
|
priv->dma_rx, priv->dma_rx_phy);
|
2014-01-16 17:52:06 +07:00
|
|
|
goto err_dma;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_dma:
|
|
|
|
kfree(priv->tx_skbuff);
|
|
|
|
err_tx_skbuff:
|
|
|
|
kfree(priv->tx_skbuff_dma);
|
|
|
|
err_tx_skbuff_dma:
|
|
|
|
kfree(priv->rx_skbuff);
|
|
|
|
err_rx_skbuff:
|
|
|
|
kfree(priv->rx_skbuff_dma);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
static void free_dma_desc_resources(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
/* Release the DMA TX/RX socket buffers */
|
|
|
|
dma_free_rx_skbufs(priv);
|
|
|
|
dma_free_tx_skbufs(priv);
|
|
|
|
|
2013-04-08 09:10:01 +07:00
|
|
|
/* Free DMA regions of consistent memory previously allocated */
|
2013-03-26 11:43:06 +07:00
|
|
|
if (!priv->extend_desc) {
|
|
|
|
dma_free_coherent(priv->device,
|
2016-02-29 20:27:30 +07:00
|
|
|
DMA_TX_SIZE * sizeof(struct dma_desc),
|
2013-03-26 11:43:06 +07:00
|
|
|
priv->dma_tx, priv->dma_tx_phy);
|
|
|
|
dma_free_coherent(priv->device,
|
2016-02-29 20:27:30 +07:00
|
|
|
DMA_RX_SIZE * sizeof(struct dma_desc),
|
2013-03-26 11:43:06 +07:00
|
|
|
priv->dma_rx, priv->dma_rx_phy);
|
|
|
|
} else {
|
2016-02-29 20:27:30 +07:00
|
|
|
dma_free_coherent(priv->device, DMA_TX_SIZE *
|
2013-03-26 11:43:06 +07:00
|
|
|
sizeof(struct dma_extended_desc),
|
|
|
|
priv->dma_etx, priv->dma_tx_phy);
|
2016-02-29 20:27:30 +07:00
|
|
|
dma_free_coherent(priv->device, DMA_RX_SIZE *
|
2013-03-26 11:43:06 +07:00
|
|
|
sizeof(struct dma_extended_desc),
|
|
|
|
priv->dma_erx, priv->dma_rx_phy);
|
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
kfree(priv->rx_skbuff_dma);
|
|
|
|
kfree(priv->rx_skbuff);
|
2013-03-26 11:43:09 +07:00
|
|
|
kfree(priv->tx_skbuff_dma);
|
2009-10-15 05:13:45 +07:00
|
|
|
kfree(priv->tx_skbuff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_dma_operation_mode - HW DMA operation mode
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: it is used for configuring the DMA operation mode register in
|
|
|
|
* order to program the tx/rx DMA thresholds or Store-And-Forward mode.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
|
|
|
|
{
|
2015-04-15 23:17:42 +07:00
|
|
|
int rxfifosz = priv->plat->rx_fifo_size;
|
|
|
|
|
2013-08-28 17:55:39 +07:00
|
|
|
if (priv->plat->force_thresh_dma_mode)
|
2015-04-15 23:17:42 +07:00
|
|
|
priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz);
|
2013-08-28 17:55:39 +07:00
|
|
|
else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) {
|
2011-07-18 03:54:09 +07:00
|
|
|
/*
|
|
|
|
* In case of GMAC, SF mode can be enabled
|
|
|
|
* to perform the TX COE in HW. This depends on:
|
2010-09-17 10:23:40 +07:00
|
|
|
* 1) TX COE if actually supported
|
|
|
|
* 2) There is no bugged Jumbo frame support
|
|
|
|
* that needs to not insert csum in the TDES.
|
|
|
|
*/
|
2015-04-15 23:17:42 +07:00
|
|
|
priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE,
|
|
|
|
rxfifosz);
|
2015-01-30 12:49:32 +07:00
|
|
|
priv->xstats.threshold = SF_DMA_MODE;
|
2010-09-17 10:23:40 +07:00
|
|
|
} else
|
2015-04-15 23:17:42 +07:00
|
|
|
priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE,
|
|
|
|
rxfifosz);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_tx_clean - to manage the transmission completion
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: it reclaims the transmit resources after transmission completes.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
2012-11-26 06:10:42 +07:00
|
|
|
static void stmmac_tx_clean(struct stmmac_priv *priv)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
2015-01-22 01:07:27 +07:00
|
|
|
unsigned int bytes_compl = 0, pkts_compl = 0;
|
2016-02-29 20:27:30 +07:00
|
|
|
unsigned int entry = priv->dirty_tx;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2011-10-18 07:01:19 +07:00
|
|
|
spin_lock(&priv->tx_lock);
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
priv->xstats.tx_clean++;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
while (entry != priv->cur_tx) {
|
2009-10-15 05:13:45 +07:00
|
|
|
struct sk_buff *skb = priv->tx_skbuff[entry];
|
2013-03-26 11:43:06 +07:00
|
|
|
struct dma_desc *p;
|
2016-02-29 20:27:36 +07:00
|
|
|
int status;
|
2013-03-26 11:43:06 +07:00
|
|
|
|
|
|
|
if (priv->extend_desc)
|
2013-04-08 09:10:01 +07:00
|
|
|
p = (struct dma_desc *)(priv->dma_etx + entry);
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
p = priv->dma_tx + entry;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-02-29 20:27:36 +07:00
|
|
|
status = priv->hw->desc->tx_status(&priv->dev->stats,
|
2013-04-08 09:10:01 +07:00
|
|
|
&priv->xstats, p,
|
|
|
|
priv->ioaddr);
|
2016-02-29 20:27:36 +07:00
|
|
|
/* Check if the descriptor is owned by the DMA */
|
|
|
|
if (unlikely(status & tx_dma_own))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Just consider the last segment and ...*/
|
|
|
|
if (likely(!(status & tx_not_ls))) {
|
|
|
|
/* ... verify the status error condition */
|
|
|
|
if (unlikely(status & tx_err)) {
|
|
|
|
priv->dev->stats.tx_errors++;
|
|
|
|
} else {
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->dev->stats.tx_packets++;
|
|
|
|
priv->xstats.tx_pkt_n++;
|
2016-02-29 20:27:36 +07:00
|
|
|
}
|
2013-03-26 11:43:10 +07:00
|
|
|
stmmac_get_tx_hwtstamp(priv, entry, skb);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2014-08-27 16:27:00 +07:00
|
|
|
if (likely(priv->tx_skbuff_dma[entry].buf)) {
|
|
|
|
if (priv->tx_skbuff_dma[entry].map_as_page)
|
|
|
|
dma_unmap_page(priv->device,
|
|
|
|
priv->tx_skbuff_dma[entry].buf,
|
2016-02-29 20:27:31 +07:00
|
|
|
priv->tx_skbuff_dma[entry].len,
|
2014-08-27 16:27:00 +07:00
|
|
|
DMA_TO_DEVICE);
|
|
|
|
else
|
|
|
|
dma_unmap_single(priv->device,
|
|
|
|
priv->tx_skbuff_dma[entry].buf,
|
2016-02-29 20:27:31 +07:00
|
|
|
priv->tx_skbuff_dma[entry].len,
|
2014-08-27 16:27:00 +07:00
|
|
|
DMA_TO_DEVICE);
|
|
|
|
priv->tx_skbuff_dma[entry].buf = 0;
|
2016-04-01 16:37:34 +07:00
|
|
|
priv->tx_skbuff_dma[entry].len = 0;
|
2014-08-27 16:27:00 +07:00
|
|
|
priv->tx_skbuff_dma[entry].map_as_page = false;
|
2013-03-26 11:43:09 +07:00
|
|
|
}
|
2016-04-01 16:37:34 +07:00
|
|
|
|
|
|
|
if (priv->hw->mode->clean_desc3)
|
|
|
|
priv->hw->mode->clean_desc3(priv, p);
|
|
|
|
|
2016-02-29 20:27:32 +07:00
|
|
|
priv->tx_skbuff_dma[entry].last_segment = false;
|
2016-02-29 20:27:33 +07:00
|
|
|
priv->tx_skbuff_dma[entry].is_jumbo = false;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
if (likely(skb != NULL)) {
|
2015-01-22 01:07:27 +07:00
|
|
|
pkts_compl++;
|
|
|
|
bytes_compl += skb->len;
|
2014-03-16 08:11:09 +07:00
|
|
|
dev_consume_skb_any(skb);
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->tx_skbuff[entry] = NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-26 11:43:05 +07:00
|
|
|
priv->hw->desc->release_tx_desc(p, priv->mode);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2016-02-29 20:27:37 +07:00
|
|
|
priv->dirty_tx = entry;
|
2015-01-22 01:07:27 +07:00
|
|
|
|
|
|
|
netdev_completed_queue(priv->dev, pkts_compl, bytes_compl);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (unlikely(netif_queue_stopped(priv->dev) &&
|
2016-02-29 20:27:30 +07:00
|
|
|
stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) {
|
2009-10-15 05:13:45 +07:00
|
|
|
netif_tx_lock(priv->dev);
|
|
|
|
if (netif_queue_stopped(priv->dev) &&
|
2016-02-29 20:27:30 +07:00
|
|
|
stmmac_tx_avail(priv) > STMMAC_TX_THRESH) {
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_tx_done(priv))
|
|
|
|
pr_debug("%s: restart transmit\n", __func__);
|
2009-10-15 05:13:45 +07:00
|
|
|
netif_wake_queue(priv->dev);
|
|
|
|
}
|
|
|
|
netif_tx_unlock(priv->dev);
|
|
|
|
}
|
2012-06-28 04:14:37 +07:00
|
|
|
|
|
|
|
if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) {
|
|
|
|
stmmac_enable_eee_mode(priv);
|
2013-06-18 12:03:23 +07:00
|
|
|
mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer));
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
2011-10-18 07:01:19 +07:00
|
|
|
spin_unlock(&priv->tx_lock);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
static inline void stmmac_enable_dma_irq(struct stmmac_priv *priv)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
2012-11-26 06:10:41 +07:00
|
|
|
priv->hw->dma->enable_dma_irq(priv->ioaddr);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
2012-11-26 06:10:41 +07:00
|
|
|
priv->hw->dma->disable_dma_irq(priv->ioaddr);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_tx_err - to manage the tx error
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2009-10-15 05:13:45 +07:00
|
|
|
* Description: it cleans the descriptors and restarts the transmission
|
2014-11-18 15:47:01 +07:00
|
|
|
* in case of transmission errors.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static void stmmac_tx_err(struct stmmac_priv *priv)
|
|
|
|
{
|
2013-03-26 11:43:06 +07:00
|
|
|
int i;
|
2009-10-15 05:13:45 +07:00
|
|
|
netif_stop_queue(priv->dev);
|
|
|
|
|
2010-08-24 03:40:42 +07:00
|
|
|
priv->hw->dma->stop_tx(priv->ioaddr);
|
2009-10-15 05:13:45 +07:00
|
|
|
dma_free_tx_skbufs(priv);
|
2016-02-29 20:27:30 +07:00
|
|
|
for (i = 0; i < DMA_TX_SIZE; i++)
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
|
|
|
priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic,
|
|
|
|
priv->mode,
|
2016-02-29 20:27:30 +07:00
|
|
|
(i == DMA_TX_SIZE - 1));
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
priv->hw->desc->init_tx_desc(&priv->dma_tx[i],
|
|
|
|
priv->mode,
|
2016-02-29 20:27:30 +07:00
|
|
|
(i == DMA_TX_SIZE - 1));
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->dirty_tx = 0;
|
|
|
|
priv->cur_tx = 0;
|
2015-01-22 01:07:27 +07:00
|
|
|
netdev_reset_queue(priv->dev);
|
2010-08-24 03:40:42 +07:00
|
|
|
priv->hw->dma->start_tx(priv->ioaddr);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
priv->dev->stats.tx_errors++;
|
|
|
|
netif_wake_queue(priv->dev);
|
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_dma_interrupt - DMA ISR
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: this is the DMA ISR. It is called by the main ISR.
|
2014-11-18 15:47:01 +07:00
|
|
|
* It calls the dwmac dma routine and schedule poll method in case of some
|
|
|
|
* work can be done.
|
2013-04-08 09:10:00 +07:00
|
|
|
*/
|
2010-01-07 06:07:18 +07:00
|
|
|
static void stmmac_dma_interrupt(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int status;
|
2015-04-15 23:17:42 +07:00
|
|
|
int rxfifosz = priv->plat->rx_fifo_size;
|
2010-01-07 06:07:18 +07:00
|
|
|
|
2010-08-24 03:40:42 +07:00
|
|
|
status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats);
|
2012-11-26 06:10:42 +07:00
|
|
|
if (likely((status & handle_rx)) || (status & handle_tx)) {
|
|
|
|
if (likely(napi_schedule_prep(&priv->napi))) {
|
|
|
|
stmmac_disable_dma_irq(priv);
|
|
|
|
__napi_schedule(&priv->napi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (unlikely(status & tx_hard_error_bump_tc)) {
|
2010-01-07 06:07:18 +07:00
|
|
|
/* Try to bump up the dma threshold on this failure */
|
2015-01-30 12:49:32 +07:00
|
|
|
if (unlikely(priv->xstats.threshold != SF_DMA_MODE) &&
|
|
|
|
(tc <= 256)) {
|
2010-01-07 06:07:18 +07:00
|
|
|
tc += 64;
|
2015-01-22 13:55:56 +07:00
|
|
|
if (priv->plat->force_thresh_dma_mode)
|
2015-04-15 23:17:42 +07:00
|
|
|
priv->hw->dma->dma_mode(priv->ioaddr, tc, tc,
|
|
|
|
rxfifosz);
|
2015-01-22 13:55:56 +07:00
|
|
|
else
|
|
|
|
priv->hw->dma->dma_mode(priv->ioaddr, tc,
|
2015-04-15 23:17:42 +07:00
|
|
|
SF_DMA_MODE, rxfifosz);
|
2010-01-07 06:07:18 +07:00
|
|
|
priv->xstats.threshold = tc;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2010-01-07 06:07:18 +07:00
|
|
|
} else if (unlikely(status == tx_hard_error))
|
|
|
|
stmmac_tx_err(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
|
|
|
* stmmac_mmc_setup: setup the Mac Management Counters (MMC)
|
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: this masks the MMC irq, in fact, the counters are managed in SW.
|
|
|
|
*/
|
2011-09-02 04:51:38 +07:00
|
|
|
static void stmmac_mmc_setup(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
unsigned int mode = MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET |
|
2016-04-01 16:37:32 +07:00
|
|
|
MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
|
2011-09-02 04:51:38 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00)
|
|
|
|
priv->mmcaddr = priv->ioaddr + MMC_GMAC4_OFFSET;
|
|
|
|
else
|
|
|
|
priv->mmcaddr = priv->ioaddr + MMC_GMAC3_X_OFFSET;
|
2016-04-01 16:37:32 +07:00
|
|
|
|
|
|
|
dwmac_mmc_intr_all_mask(priv->mmcaddr);
|
2011-11-18 12:00:20 +07:00
|
|
|
|
|
|
|
if (priv->dma_cap.rmon) {
|
2016-04-01 16:37:32 +07:00
|
|
|
dwmac_mmc_ctrl(priv->mmcaddr, mode);
|
2011-11-18 12:00:20 +07:00
|
|
|
memset(&priv->mmc, 0, sizeof(struct stmmac_counters));
|
|
|
|
} else
|
2012-01-10 08:47:51 +07:00
|
|
|
pr_info(" No MAC Management Counters available\n");
|
2011-09-02 04:51:38 +07:00
|
|
|
}
|
|
|
|
|
2011-11-17 04:58:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_selec_desc_mode - to select among: normal/alternate/extend descriptors
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description: select the Enhanced/Alternate or Normal descriptors.
|
2014-11-18 15:47:01 +07:00
|
|
|
* In case of Enhanced/Alternate, it checks if the extended descriptors are
|
|
|
|
* supported by the HW capability register.
|
2012-06-05 02:22:55 +07:00
|
|
|
*/
|
2011-11-17 04:58:00 +07:00
|
|
|
static void stmmac_selec_desc_mode(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
if (priv->plat->enh_desc) {
|
|
|
|
pr_info(" Enhanced/Alternate descriptors\n");
|
2013-03-26 11:43:06 +07:00
|
|
|
|
|
|
|
/* GMAC older than 3.50 has no extended descriptors */
|
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_3_50) {
|
|
|
|
pr_info("\tEnabled extended descriptors\n");
|
|
|
|
priv->extend_desc = 1;
|
|
|
|
} else
|
|
|
|
pr_warn("Extended descriptors not supported\n");
|
|
|
|
|
2011-11-17 04:58:00 +07:00
|
|
|
priv->hw->desc = &enh_desc_ops;
|
|
|
|
} else {
|
|
|
|
pr_info(" Normal descriptors\n");
|
|
|
|
priv->hw->desc = &ndesc_ops;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_get_hw_features - get MAC capabilities from the HW cap. register.
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2011-11-17 04:58:00 +07:00
|
|
|
* Description:
|
|
|
|
* new GMAC chip generations have a new register to indicate the
|
|
|
|
* presence of the optional feature/functions.
|
|
|
|
* This can be also used to override the value passed through the
|
|
|
|
* platform and necessary for old MAC10/100 and GMAC chips.
|
2011-09-02 04:51:41 +07:00
|
|
|
*/
|
|
|
|
static int stmmac_get_hw_features(struct stmmac_priv *priv)
|
|
|
|
{
|
2016-04-01 16:37:25 +07:00
|
|
|
u32 ret = 0;
|
2011-10-27 02:43:09 +07:00
|
|
|
|
2011-10-27 02:43:07 +07:00
|
|
|
if (priv->hw->dma->get_hw_feature) {
|
2016-04-01 16:37:25 +07:00
|
|
|
priv->hw->dma->get_hw_feature(priv->ioaddr,
|
|
|
|
&priv->dma_cap);
|
|
|
|
ret = 1;
|
2011-11-17 04:58:00 +07:00
|
|
|
}
|
2011-09-02 04:51:41 +07:00
|
|
|
|
2016-04-01 16:37:25 +07:00
|
|
|
return ret;
|
2011-09-02 04:51:41 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_check_ether_addr - check if the MAC addr is valid
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description:
|
|
|
|
* it is to verify if the MAC address is valid, in case of failures it
|
|
|
|
* generates a random MAC address
|
|
|
|
*/
|
2011-12-21 10:58:19 +07:00
|
|
|
static void stmmac_check_ether_addr(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
if (!is_valid_ether_addr(priv->dev->dev_addr)) {
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->get_umac_addr(priv->hw,
|
2011-12-21 10:58:19 +07:00
|
|
|
priv->dev->dev_addr, 0);
|
2013-04-08 09:10:01 +07:00
|
|
|
if (!is_valid_ether_addr(priv->dev->dev_addr))
|
2012-02-15 13:45:39 +07:00
|
|
|
eth_hw_addr_random(priv->dev);
|
2014-01-26 21:50:44 +07:00
|
|
|
pr_info("%s: device MAC address %pM\n", priv->dev->name,
|
|
|
|
priv->dev->dev_addr);
|
2011-12-21 10:58:19 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_init_dma_engine - DMA init.
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description:
|
|
|
|
* It inits the DMA invoking the specific MAC/GMAC callback.
|
|
|
|
* Some DMA parameters can be passed from the platform;
|
|
|
|
* in case of these are not passed a default is kept for the MAC or GMAC.
|
|
|
|
*/
|
2012-04-19 02:48:21 +07:00
|
|
|
static int stmmac_init_dma_engine(struct stmmac_priv *priv)
|
|
|
|
{
|
2016-02-29 20:27:28 +07:00
|
|
|
int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, aal = 0;
|
2012-05-14 05:18:42 +07:00
|
|
|
int mixed_burst = 0;
|
2013-03-26 11:43:06 +07:00
|
|
|
int atds = 0;
|
2016-02-29 20:27:27 +07:00
|
|
|
int ret = 0;
|
2012-04-19 02:48:21 +07:00
|
|
|
|
|
|
|
if (priv->plat->dma_cfg) {
|
|
|
|
pbl = priv->plat->dma_cfg->pbl;
|
|
|
|
fixed_burst = priv->plat->dma_cfg->fixed_burst;
|
2012-05-14 05:18:42 +07:00
|
|
|
mixed_burst = priv->plat->dma_cfg->mixed_burst;
|
2016-02-29 20:27:28 +07:00
|
|
|
aal = priv->plat->dma_cfg->aal;
|
2012-04-19 02:48:21 +07:00
|
|
|
}
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
|
|
|
|
atds = 1;
|
|
|
|
|
2016-02-29 20:27:27 +07:00
|
|
|
ret = priv->hw->dma->reset(priv->ioaddr);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(priv->device, "Failed to reset the dma\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst,
|
2016-02-29 20:27:28 +07:00
|
|
|
aal, priv->dma_tx_phy, priv->dma_rx_phy, atds);
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00) {
|
|
|
|
priv->rx_tail_addr = priv->dma_rx_phy +
|
|
|
|
(DMA_RX_SIZE * sizeof(struct dma_desc));
|
|
|
|
priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, priv->rx_tail_addr,
|
|
|
|
STMMAC_CHAN0);
|
|
|
|
|
|
|
|
priv->tx_tail_addr = priv->dma_tx_phy +
|
|
|
|
(DMA_TX_SIZE * sizeof(struct dma_desc));
|
|
|
|
priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr,
|
|
|
|
STMMAC_CHAN0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->plat->axi && priv->hw->dma->axi)
|
2016-02-29 20:27:28 +07:00
|
|
|
priv->hw->dma->axi(priv->ioaddr, priv->plat->axi);
|
|
|
|
|
2016-02-29 20:27:27 +07:00
|
|
|
return ret;
|
2012-04-19 02:48:21 +07:00
|
|
|
}
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_tx_timer - mitigation sw timer for tx.
|
2012-11-26 06:10:42 +07:00
|
|
|
* @data: data pointer
|
|
|
|
* Description:
|
|
|
|
* This is the timer handler to directly invoke the stmmac_tx_clean.
|
|
|
|
*/
|
|
|
|
static void stmmac_tx_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = (struct stmmac_priv *)data;
|
|
|
|
|
|
|
|
stmmac_tx_clean(priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_init_tx_coalesce - init tx mitigation options.
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2012-11-26 06:10:42 +07:00
|
|
|
* Description:
|
|
|
|
* This inits the transmit coalesce parameters: i.e. timer rate,
|
|
|
|
* timer handler and default threshold used for enabling the
|
|
|
|
* interrupt on completion bit.
|
|
|
|
*/
|
|
|
|
static void stmmac_init_tx_coalesce(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
priv->tx_coal_frames = STMMAC_TX_FRAMES;
|
|
|
|
priv->tx_coal_timer = STMMAC_COAL_TX_TIMER;
|
|
|
|
init_timer(&priv->txtimer);
|
|
|
|
priv->txtimer.expires = STMMAC_COAL_TIMER(priv->tx_coal_timer);
|
|
|
|
priv->txtimer.data = (unsigned long)priv;
|
|
|
|
priv->txtimer.function = stmmac_tx_timer;
|
|
|
|
add_timer(&priv->txtimer);
|
|
|
|
}
|
|
|
|
|
2014-01-16 17:52:14 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_hw_setup - setup mac in a usable state.
|
2014-01-16 17:52:14 +07:00
|
|
|
* @dev : pointer to the device structure.
|
|
|
|
* Description:
|
2014-11-18 15:47:01 +07:00
|
|
|
* this is the main function to setup the HW in a usable state because the
|
|
|
|
* dma engine is reset, the core registers are configured (e.g. AXI,
|
|
|
|
* Checksum features, timers). The DMA is ready to start receiving and
|
|
|
|
* transmitting.
|
2014-01-16 17:52:14 +07:00
|
|
|
* Return value:
|
|
|
|
* 0 on success and an appropriate (-)ve integer as defined in errno.h
|
|
|
|
* file on failure.
|
|
|
|
*/
|
2014-12-19 21:38:18 +07:00
|
|
|
static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
|
2014-01-16 17:52:14 +07:00
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* DMA initialization and SW reset */
|
|
|
|
ret = stmmac_init_dma_engine(priv);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("%s: DMA engine initialization failed\n", __func__);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the MAC addr into the HW */
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->set_umac_addr(priv->hw, dev->dev_addr, 0);
|
2014-01-16 17:52:14 +07:00
|
|
|
|
|
|
|
/* If required, perform hw setup of the bus. */
|
|
|
|
if (priv->plat->bus_setup)
|
|
|
|
priv->plat->bus_setup(priv->ioaddr);
|
|
|
|
|
2016-06-24 20:16:26 +07:00
|
|
|
/* PS and related bits will be programmed according to the speed */
|
|
|
|
if (priv->hw->pcs) {
|
|
|
|
int speed = priv->plat->mac_port_sel_speed;
|
|
|
|
|
|
|
|
if ((speed == SPEED_10) || (speed == SPEED_100) ||
|
|
|
|
(speed == SPEED_1000)) {
|
|
|
|
priv->hw->ps = speed;
|
|
|
|
} else {
|
|
|
|
dev_warn(priv->device, "invalid port speed\n");
|
|
|
|
priv->hw->ps = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-16 17:52:14 +07:00
|
|
|
/* Initialize the MAC Core */
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->core_init(priv->hw, dev->mtu);
|
2014-01-16 17:52:14 +07:00
|
|
|
|
2014-08-25 19:56:18 +07:00
|
|
|
ret = priv->hw->mac->rx_ipc(priv->hw);
|
|
|
|
if (!ret) {
|
|
|
|
pr_warn(" RX IPC Checksum Offload disabled\n");
|
|
|
|
priv->plat->rx_coe = STMMAC_RX_COE_NONE;
|
2014-09-01 14:17:52 +07:00
|
|
|
priv->hw->rx_csum = 0;
|
2014-08-25 19:56:18 +07:00
|
|
|
}
|
|
|
|
|
2014-01-16 17:52:14 +07:00
|
|
|
/* Enable the MAC Rx/Tx */
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00)
|
|
|
|
stmmac_dwmac4_set_mac(priv->ioaddr, true);
|
|
|
|
else
|
|
|
|
stmmac_set_mac(priv->ioaddr, true);
|
2014-01-16 17:52:14 +07:00
|
|
|
|
|
|
|
/* Set the HW DMA mode and the COE */
|
|
|
|
stmmac_dma_operation_mode(priv);
|
|
|
|
|
|
|
|
stmmac_mmc_setup(priv);
|
|
|
|
|
2014-12-19 21:38:18 +07:00
|
|
|
if (init_ptp) {
|
|
|
|
ret = stmmac_init_ptp(priv);
|
2016-10-12 20:42:04 +07:00
|
|
|
if (ret)
|
2016-10-19 14:06:41 +07:00
|
|
|
netdev_warn(priv->dev, "fail to init PTP.\n");
|
2014-12-19 21:38:18 +07:00
|
|
|
}
|
2014-01-16 17:52:14 +07:00
|
|
|
|
2014-11-04 21:49:33 +07:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
2014-01-16 17:52:14 +07:00
|
|
|
ret = stmmac_init_fs(dev);
|
|
|
|
if (ret < 0)
|
|
|
|
pr_warn("%s: failed debugFS registration\n", __func__);
|
|
|
|
#endif
|
|
|
|
/* Start the ball rolling... */
|
|
|
|
pr_debug("%s: DMA RX/TX processes started...\n", dev->name);
|
|
|
|
priv->hw->dma->start_tx(priv->ioaddr);
|
|
|
|
priv->hw->dma->start_rx(priv->ioaddr);
|
|
|
|
|
|
|
|
/* Dump DMA/MAC registers */
|
|
|
|
if (netif_msg_hw(priv)) {
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->dump_regs(priv->hw);
|
2014-01-16 17:52:14 +07:00
|
|
|
priv->hw->dma->dump_regs(priv->ioaddr);
|
|
|
|
}
|
|
|
|
priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS;
|
|
|
|
|
|
|
|
if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) {
|
|
|
|
priv->rx_riwt = MAX_DMA_RIWT;
|
|
|
|
priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT);
|
|
|
|
}
|
|
|
|
|
2016-06-24 20:16:25 +07:00
|
|
|
if (priv->hw->pcs && priv->hw->mac->pcs_ctrl_ane)
|
2016-06-24 20:16:26 +07:00
|
|
|
priv->hw->mac->pcs_ctrl_ane(priv->hw, 1, priv->hw->ps, 0);
|
2014-01-16 17:52:14 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/* set TX ring length */
|
|
|
|
if (priv->hw->dma->set_tx_ring_len)
|
|
|
|
priv->hw->dma->set_tx_ring_len(priv->ioaddr,
|
|
|
|
(DMA_TX_SIZE - 1));
|
|
|
|
/* set RX ring length */
|
|
|
|
if (priv->hw->dma->set_rx_ring_len)
|
|
|
|
priv->hw->dma->set_rx_ring_len(priv->ioaddr,
|
|
|
|
(DMA_RX_SIZE - 1));
|
|
|
|
/* Enable TSO */
|
|
|
|
if (priv->tso)
|
|
|
|
priv->hw->dma->enable_tso(priv->ioaddr, 1, STMMAC_CHAN0);
|
|
|
|
|
2014-01-16 17:52:14 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
|
|
|
* stmmac_open - open entry point of the driver
|
|
|
|
* @dev : pointer to the device structure.
|
|
|
|
* Description:
|
|
|
|
* This function is the open entry point of the driver.
|
|
|
|
* Return value:
|
|
|
|
* 0 on success and an appropriate (-)ve integer as defined in errno.h
|
|
|
|
* file on failure.
|
|
|
|
*/
|
|
|
|
static int stmmac_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
int ret;
|
|
|
|
|
2012-04-19 02:48:20 +07:00
|
|
|
stmmac_check_ether_addr(priv);
|
|
|
|
|
2016-06-24 20:16:25 +07:00
|
|
|
if (priv->hw->pcs != STMMAC_PCS_RGMII &&
|
|
|
|
priv->hw->pcs != STMMAC_PCS_TBI &&
|
|
|
|
priv->hw->pcs != STMMAC_PCS_RTBI) {
|
2013-03-26 11:43:08 +07:00
|
|
|
ret = stmmac_init_phy(dev);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("%s: Cannot attach to PHY (error: %d)\n",
|
|
|
|
__func__, ret);
|
2014-05-20 16:38:18 +07:00
|
|
|
return ret;
|
2013-03-26 11:43:08 +07:00
|
|
|
}
|
2011-04-11 06:16:45 +07:00
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-01-16 17:52:14 +07:00
|
|
|
/* Extra statistics */
|
|
|
|
memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
|
|
|
|
priv->xstats.threshold = tc;
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
|
2016-02-29 20:27:41 +07:00
|
|
|
priv->rx_copybreak = STMMAC_RX_COPYBREAK;
|
2013-08-09 19:02:08 +07:00
|
|
|
|
2014-02-22 19:09:03 +07:00
|
|
|
ret = alloc_dma_desc_resources(priv);
|
2014-01-16 17:52:06 +07:00
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("%s: DMA descriptors allocation failed\n", __func__);
|
|
|
|
goto dma_desc_error;
|
|
|
|
}
|
|
|
|
|
2014-11-04 23:08:09 +07:00
|
|
|
ret = init_dma_desc_rings(dev, GFP_KERNEL);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("%s: DMA descriptors initialization failed\n", __func__);
|
|
|
|
goto init_error;
|
|
|
|
}
|
|
|
|
|
2014-12-19 21:38:18 +07:00
|
|
|
ret = stmmac_hw_setup(dev, true);
|
2013-08-09 19:02:08 +07:00
|
|
|
if (ret < 0) {
|
2014-01-16 17:52:14 +07:00
|
|
|
pr_err("%s: Hw setup failed\n", __func__);
|
2013-07-04 11:18:07 +07:00
|
|
|
goto init_error;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2014-11-04 23:08:09 +07:00
|
|
|
stmmac_init_tx_coalesce(priv);
|
|
|
|
|
2014-01-16 17:52:14 +07:00
|
|
|
if (priv->phydev)
|
|
|
|
phy_start(priv->phydev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2011-04-11 06:16:45 +07:00
|
|
|
/* Request the IRQ lines */
|
|
|
|
ret = request_irq(dev->irq, stmmac_interrupt,
|
2013-04-08 09:10:01 +07:00
|
|
|
IRQF_SHARED, dev->name, dev);
|
2011-04-11 06:16:45 +07:00
|
|
|
if (unlikely(ret < 0)) {
|
|
|
|
pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
|
|
|
|
__func__, dev->irq, ret);
|
2013-07-04 11:18:07 +07:00
|
|
|
goto init_error;
|
2011-04-11 06:16:45 +07:00
|
|
|
}
|
|
|
|
|
2012-02-15 07:10:38 +07:00
|
|
|
/* Request the Wake IRQ in case of another line is used for WoL */
|
|
|
|
if (priv->wol_irq != dev->irq) {
|
|
|
|
ret = request_irq(priv->wol_irq, stmmac_interrupt,
|
|
|
|
IRQF_SHARED, dev->name, dev);
|
|
|
|
if (unlikely(ret < 0)) {
|
2013-04-08 09:10:01 +07:00
|
|
|
pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n",
|
|
|
|
__func__, priv->wol_irq, ret);
|
2013-07-04 11:18:07 +07:00
|
|
|
goto wolirq_error;
|
2012-02-15 07:10:38 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
/* Request the IRQ lines */
|
2014-05-29 21:31:40 +07:00
|
|
|
if (priv->lpi_irq > 0) {
|
2012-06-28 04:14:37 +07:00
|
|
|
ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED,
|
|
|
|
dev->name, dev);
|
|
|
|
if (unlikely(ret < 0)) {
|
|
|
|
pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n",
|
|
|
|
__func__, priv->lpi_irq, ret);
|
2013-07-04 11:18:07 +07:00
|
|
|
goto lpiirq_error;
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
napi_enable(&priv->napi);
|
|
|
|
netif_start_queue(dev);
|
2011-04-11 06:16:45 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
return 0;
|
2011-04-11 06:16:45 +07:00
|
|
|
|
2013-07-04 11:18:07 +07:00
|
|
|
lpiirq_error:
|
2012-06-28 04:14:37 +07:00
|
|
|
if (priv->wol_irq != dev->irq)
|
|
|
|
free_irq(priv->wol_irq, dev);
|
2013-07-04 11:18:07 +07:00
|
|
|
wolirq_error:
|
2012-02-15 07:10:38 +07:00
|
|
|
free_irq(dev->irq, dev);
|
|
|
|
|
2013-07-04 11:18:07 +07:00
|
|
|
init_error:
|
|
|
|
free_dma_desc_resources(priv);
|
2013-08-09 19:02:08 +07:00
|
|
|
dma_desc_error:
|
2011-04-11 06:16:45 +07:00
|
|
|
if (priv->phydev)
|
|
|
|
phy_disconnect(priv->phydev);
|
2012-04-19 02:48:20 +07:00
|
|
|
|
2011-04-11 06:16:45 +07:00
|
|
|
return ret;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_release - close entry point of the driver
|
|
|
|
* @dev : device pointer.
|
|
|
|
* Description:
|
|
|
|
* This is the stop entry point of the driver.
|
|
|
|
*/
|
|
|
|
static int stmmac_release(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
if (priv->eee_enabled)
|
|
|
|
del_timer_sync(&priv->eee_ctrl_timer);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/* 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);
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
del_timer_sync(&priv->txtimer);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/* Free the IRQ lines */
|
|
|
|
free_irq(dev->irq, dev);
|
2012-02-15 07:10:38 +07:00
|
|
|
if (priv->wol_irq != dev->irq)
|
|
|
|
free_irq(priv->wol_irq, dev);
|
2014-05-29 21:31:40 +07:00
|
|
|
if (priv->lpi_irq > 0)
|
2012-06-28 04:14:37 +07:00
|
|
|
free_irq(priv->lpi_irq, dev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
/* Stop TX/RX DMA and clear the descriptors */
|
2010-08-24 03:40:42 +07:00
|
|
|
priv->hw->dma->stop_tx(priv->ioaddr);
|
|
|
|
priv->hw->dma->stop_rx(priv->ioaddr);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
/* Release and free the Rx/Tx resources */
|
|
|
|
free_dma_desc_resources(priv);
|
|
|
|
|
2010-10-26 01:58:14 +07:00
|
|
|
/* Disable the MAC Rx/Tx */
|
2011-12-21 10:58:19 +07:00
|
|
|
stmmac_set_mac(priv->ioaddr, false);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
|
2014-11-04 21:49:33 +07:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
2015-05-23 09:03:29 +07:00
|
|
|
stmmac_exit_fs(dev);
|
2011-12-21 10:58:19 +07:00
|
|
|
#endif
|
|
|
|
|
2013-03-26 11:43:11 +07:00
|
|
|
stmmac_release_ptp(priv);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/**
|
|
|
|
* stmmac_tso_allocator - close entry point of the driver
|
|
|
|
* @priv: driver private structure
|
|
|
|
* @des: buffer start address
|
|
|
|
* @total_len: total length to fill in descriptors
|
|
|
|
* @last_segmant: condition for the last descriptor
|
|
|
|
* Description:
|
|
|
|
* This function fills descriptor and request new descriptors according to
|
|
|
|
* buffer length to fill
|
|
|
|
*/
|
|
|
|
static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des,
|
|
|
|
int total_len, bool last_segment)
|
|
|
|
{
|
|
|
|
struct dma_desc *desc;
|
|
|
|
int tmp_len;
|
|
|
|
u32 buff_size;
|
|
|
|
|
|
|
|
tmp_len = total_len;
|
|
|
|
|
|
|
|
while (tmp_len > 0) {
|
|
|
|
priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE);
|
|
|
|
desc = priv->dma_tx + priv->cur_tx;
|
|
|
|
|
|
|
|
desc->des0 = des + (total_len - tmp_len);
|
|
|
|
buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ?
|
|
|
|
TSO_MAX_BUFF_SIZE : tmp_len;
|
|
|
|
|
|
|
|
priv->hw->desc->prepare_tso_tx_desc(desc, 0, buff_size,
|
|
|
|
0, 1,
|
|
|
|
(last_segment) && (buff_size < TSO_MAX_BUFF_SIZE),
|
|
|
|
0, 0);
|
|
|
|
|
|
|
|
tmp_len -= TSO_MAX_BUFF_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO)
|
|
|
|
* @skb : the socket buffer
|
|
|
|
* @dev : device pointer
|
|
|
|
* Description: this is the transmit function that is called on TSO frames
|
|
|
|
* (support available on GMAC4 and newer chips).
|
|
|
|
* Diagram below show the ring programming in case of TSO frames:
|
|
|
|
*
|
|
|
|
* First Descriptor
|
|
|
|
* --------
|
|
|
|
* | DES0 |---> buffer1 = L2/L3/L4 header
|
|
|
|
* | DES1 |---> TCP Payload (can continue on next descr...)
|
|
|
|
* | DES2 |---> buffer 1 and 2 len
|
|
|
|
* | DES3 |---> must set TSE, TCP hdr len-> [22:19]. TCP payload len [17:0]
|
|
|
|
* --------
|
|
|
|
* |
|
|
|
|
* ...
|
|
|
|
* |
|
|
|
|
* --------
|
|
|
|
* | DES0 | --| Split TCP Payload on Buffers 1 and 2
|
|
|
|
* | DES1 | --|
|
|
|
|
* | DES2 | --> buffer 1 and 2 len
|
|
|
|
* | DES3 |
|
|
|
|
* --------
|
|
|
|
*
|
|
|
|
* mss is fixed when enable tso, so w/o programming the TDES3 ctx field.
|
|
|
|
*/
|
|
|
|
static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
|
|
|
u32 pay_len, mss;
|
|
|
|
int tmp_pay_len = 0;
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
int nfrags = skb_shinfo(skb)->nr_frags;
|
|
|
|
unsigned int first_entry, des;
|
|
|
|
struct dma_desc *desc, *first, *mss_desc = NULL;
|
|
|
|
u8 proto_hdr_len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
spin_lock(&priv->tx_lock);
|
|
|
|
|
|
|
|
/* Compute header lengths */
|
|
|
|
proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
|
|
|
|
|
|
|
|
/* Desc availability based on threshold should be enough safe */
|
|
|
|
if (unlikely(stmmac_tx_avail(priv) <
|
|
|
|
(((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) {
|
|
|
|
if (!netif_queue_stopped(dev)) {
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
/* This is a hard error, log it. */
|
|
|
|
pr_err("%s: Tx Ring full when queue awake\n", __func__);
|
|
|
|
}
|
|
|
|
spin_unlock(&priv->tx_lock);
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */
|
|
|
|
|
|
|
|
mss = skb_shinfo(skb)->gso_size;
|
|
|
|
|
|
|
|
/* set new MSS value if needed */
|
|
|
|
if (mss != priv->mss) {
|
|
|
|
mss_desc = priv->dma_tx + priv->cur_tx;
|
|
|
|
priv->hw->desc->set_mss(mss_desc, mss);
|
|
|
|
priv->mss = mss;
|
|
|
|
priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netif_msg_tx_queued(priv)) {
|
|
|
|
pr_info("%s: tcphdrlen %d, hdr_len %d, pay_len %d, mss %d\n",
|
|
|
|
__func__, tcp_hdrlen(skb), proto_hdr_len, pay_len, mss);
|
|
|
|
pr_info("\tskb->len %d, skb->data_len %d\n", skb->len,
|
|
|
|
skb->data_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
first_entry = priv->cur_tx;
|
|
|
|
|
|
|
|
desc = priv->dma_tx + first_entry;
|
|
|
|
first = desc;
|
|
|
|
|
|
|
|
/* first descriptor: fill Headers on Buf1 */
|
|
|
|
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
if (dma_mapping_error(priv->device, des))
|
|
|
|
goto dma_map_err;
|
|
|
|
|
|
|
|
priv->tx_skbuff_dma[first_entry].buf = des;
|
|
|
|
priv->tx_skbuff_dma[first_entry].len = skb_headlen(skb);
|
|
|
|
priv->tx_skbuff[first_entry] = skb;
|
|
|
|
|
|
|
|
first->des0 = des;
|
|
|
|
|
|
|
|
/* Fill start of payload in buff2 of first descriptor */
|
|
|
|
if (pay_len)
|
|
|
|
first->des1 = des + proto_hdr_len;
|
|
|
|
|
|
|
|
/* If needed take extra descriptors to fill the remaining payload */
|
|
|
|
tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE;
|
|
|
|
|
|
|
|
stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0));
|
|
|
|
|
|
|
|
/* Prepare fragments */
|
|
|
|
for (i = 0; i < nfrags; i++) {
|
|
|
|
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
|
|
|
|
|
|
|
|
des = skb_frag_dma_map(priv->device, frag, 0,
|
|
|
|
skb_frag_size(frag),
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
|
|
|
|
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
|
|
|
|
(i == nfrags - 1));
|
|
|
|
|
|
|
|
priv->tx_skbuff_dma[priv->cur_tx].buf = des;
|
|
|
|
priv->tx_skbuff_dma[priv->cur_tx].len = skb_frag_size(frag);
|
|
|
|
priv->tx_skbuff[priv->cur_tx] = NULL;
|
|
|
|
priv->tx_skbuff_dma[priv->cur_tx].map_as_page = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->tx_skbuff_dma[priv->cur_tx].last_segment = true;
|
|
|
|
|
|
|
|
priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE);
|
|
|
|
|
|
|
|
if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) {
|
|
|
|
if (netif_msg_hw(priv))
|
|
|
|
pr_debug("%s: stop transmitted packets\n", __func__);
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->stats.tx_bytes += skb->len;
|
|
|
|
priv->xstats.tx_tso_frames++;
|
|
|
|
priv->xstats.tx_tso_nfrags += nfrags;
|
|
|
|
|
|
|
|
/* Manage tx mitigation */
|
|
|
|
priv->tx_count_frames += nfrags + 1;
|
|
|
|
if (likely(priv->tx_coal_frames > priv->tx_count_frames)) {
|
|
|
|
mod_timer(&priv->txtimer,
|
|
|
|
STMMAC_COAL_TIMER(priv->tx_coal_timer));
|
|
|
|
} else {
|
|
|
|
priv->tx_count_frames = 0;
|
|
|
|
priv->hw->desc->set_tx_ic(desc);
|
|
|
|
priv->xstats.tx_set_ic_bit++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!priv->hwts_tx_en)
|
|
|
|
skb_tx_timestamp(skb);
|
|
|
|
|
|
|
|
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
|
|
|
|
priv->hwts_tx_en)) {
|
|
|
|
/* declare that device is doing timestamping */
|
|
|
|
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
|
|
|
priv->hw->desc->enable_tx_timestamp(first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Complete the first descriptor before granting the DMA */
|
|
|
|
priv->hw->desc->prepare_tso_tx_desc(first, 1,
|
|
|
|
proto_hdr_len,
|
|
|
|
pay_len,
|
|
|
|
1, priv->tx_skbuff_dma[first_entry].last_segment,
|
|
|
|
tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len));
|
|
|
|
|
|
|
|
/* If context desc is used to change MSS */
|
|
|
|
if (mss_desc)
|
|
|
|
priv->hw->desc->set_tx_owner(mss_desc);
|
|
|
|
|
|
|
|
/* The own bit must be the latest setting done when prepare the
|
|
|
|
* descriptor and then barrier is needed to make sure that
|
|
|
|
* all is coherent before granting the DMA engine.
|
|
|
|
*/
|
|
|
|
smp_wmb();
|
|
|
|
|
|
|
|
if (netif_msg_pktdata(priv)) {
|
|
|
|
pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n",
|
|
|
|
__func__, priv->cur_tx, priv->dirty_tx, first_entry,
|
|
|
|
priv->cur_tx, first, nfrags);
|
|
|
|
|
|
|
|
priv->hw->desc->display_ring((void *)priv->dma_tx, DMA_TX_SIZE,
|
|
|
|
0);
|
|
|
|
|
|
|
|
pr_info(">>> frame to be transmitted: ");
|
|
|
|
print_pkt(skb->data, skb_headlen(skb));
|
|
|
|
}
|
|
|
|
|
|
|
|
netdev_sent_queue(dev, skb->len);
|
|
|
|
|
|
|
|
priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr,
|
|
|
|
STMMAC_CHAN0);
|
|
|
|
|
|
|
|
spin_unlock(&priv->tx_lock);
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
|
|
|
|
dma_map_err:
|
|
|
|
spin_unlock(&priv->tx_lock);
|
|
|
|
dev_err(priv->device, "Tx dma map failed\n");
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
priv->dev->stats.tx_dropped++;
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_xmit - Tx entry point of the driver
|
2009-10-15 05:13:45 +07:00
|
|
|
* @skb : the socket buffer
|
|
|
|
* @dev : device pointer
|
2013-04-08 09:10:00 +07:00
|
|
|
* Description : this is the tx entry point of the driver.
|
|
|
|
* It programs the chain or the ring and supports oversized frames
|
|
|
|
* and SG feature.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
2016-02-29 20:27:38 +07:00
|
|
|
unsigned int nopaged_len = skb_headlen(skb);
|
2013-03-26 11:43:05 +07:00
|
|
|
int i, csum_insertion = 0, is_jumbo = 0;
|
2009-10-15 05:13:45 +07:00
|
|
|
int nfrags = skb_shinfo(skb)->nr_frags;
|
2016-02-29 20:27:38 +07:00
|
|
|
unsigned int entry, first_entry;
|
2009-10-15 05:13:45 +07:00
|
|
|
struct dma_desc *desc, *first;
|
2016-02-29 20:27:38 +07:00
|
|
|
unsigned int enh_desc;
|
2016-04-01 16:37:34 +07:00
|
|
|
unsigned int des;
|
|
|
|
|
|
|
|
/* Manage oversized TCP frames for GMAC4 device */
|
|
|
|
if (skb_is_gso(skb) && priv->tso) {
|
|
|
|
if (ip_hdr(skb)->protocol == IPPROTO_TCP)
|
|
|
|
return stmmac_tso_xmit(skb, dev);
|
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-11-04 23:08:05 +07:00
|
|
|
spin_lock(&priv->tx_lock);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) {
|
2014-11-04 23:08:05 +07:00
|
|
|
spin_unlock(&priv->tx_lock);
|
2009-10-15 05:13:45 +07:00
|
|
|
if (!netif_queue_stopped(dev)) {
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
/* This is a hard error, log it. */
|
2013-04-08 09:10:01 +07:00
|
|
|
pr_err("%s: Tx Ring full when queue awake\n", __func__);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
if (priv->tx_path_in_lpi_mode)
|
|
|
|
stmmac_disable_eee_mode(priv);
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
entry = priv->cur_tx;
|
2016-02-29 20:27:38 +07:00
|
|
|
first_entry = entry;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2011-04-09 09:46:55 +07:00
|
|
|
csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
if (likely(priv->extend_desc))
|
2013-04-08 09:10:01 +07:00
|
|
|
desc = (struct dma_desc *)(priv->dma_etx + entry);
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
desc = priv->dma_tx + entry;
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
first = desc;
|
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
priv->tx_skbuff[first_entry] = skb;
|
|
|
|
|
|
|
|
enh_desc = priv->plat->enh_desc;
|
2013-03-26 11:43:05 +07:00
|
|
|
/* To program the descriptors according to the size of the frame */
|
2014-03-10 19:40:33 +07:00
|
|
|
if (enh_desc)
|
|
|
|
is_jumbo = priv->hw->mode->is_jumbo_frm(skb->len, enh_desc);
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (unlikely(is_jumbo) && likely(priv->synopsys_id <
|
|
|
|
DWMAC_CORE_4_00)) {
|
2014-03-10 19:40:33 +07:00
|
|
|
entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion);
|
2014-08-27 16:27:00 +07:00
|
|
|
if (unlikely(entry < 0))
|
|
|
|
goto dma_map_err;
|
2014-03-10 19:40:33 +07:00
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
for (i = 0; i < nfrags; i++) {
|
2011-10-19 04:00:24 +07:00
|
|
|
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
|
|
|
|
int len = skb_frag_size(frag);
|
2016-02-29 20:27:35 +07:00
|
|
|
bool last_segment = (i == (nfrags - 1));
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
|
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
if (likely(priv->extend_desc))
|
2013-04-08 09:10:01 +07:00
|
|
|
desc = (struct dma_desc *)(priv->dma_etx + entry);
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
desc = priv->dma_tx + entry;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
des = skb_frag_dma_map(priv->device, frag, 0, len,
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
if (dma_mapping_error(priv->device, des))
|
2014-08-27 16:27:00 +07:00
|
|
|
goto dma_map_err; /* should reuse desc w/o issues */
|
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
priv->tx_skbuff[entry] = NULL;
|
2016-04-01 16:37:34 +07:00
|
|
|
|
|
|
|
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) {
|
|
|
|
desc->des0 = des;
|
|
|
|
priv->tx_skbuff_dma[entry].buf = desc->des0;
|
|
|
|
} else {
|
|
|
|
desc->des2 = des;
|
|
|
|
priv->tx_skbuff_dma[entry].buf = desc->des2;
|
|
|
|
}
|
|
|
|
|
2014-08-27 16:27:00 +07:00
|
|
|
priv->tx_skbuff_dma[entry].map_as_page = true;
|
2016-02-29 20:27:31 +07:00
|
|
|
priv->tx_skbuff_dma[entry].len = len;
|
2016-02-29 20:27:38 +07:00
|
|
|
priv->tx_skbuff_dma[entry].last_segment = last_segment;
|
|
|
|
|
|
|
|
/* Prepare the descriptor and set the own bit too */
|
2013-03-26 11:43:05 +07:00
|
|
|
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion,
|
2016-02-29 20:27:35 +07:00
|
|
|
priv->mode, 1, last_segment);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
|
|
|
|
|
|
|
|
priv->cur_tx = entry;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
if (netif_msg_pktdata(priv)) {
|
2016-04-01 16:37:26 +07:00
|
|
|
void *tx_head;
|
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
pr_debug("%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d",
|
|
|
|
__func__, priv->cur_tx, priv->dirty_tx, first_entry,
|
|
|
|
entry, first, nfrags);
|
2013-07-02 19:12:36 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
2016-04-01 16:37:26 +07:00
|
|
|
tx_head = (void *)priv->dma_etx;
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
2016-04-01 16:37:26 +07:00
|
|
|
tx_head = (void *)priv->dma_tx;
|
|
|
|
|
|
|
|
priv->hw->desc->display_ring(tx_head, DMA_TX_SIZE, false);
|
2013-03-26 11:43:06 +07:00
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
pr_debug(">>> frame to be transmitted: ");
|
2009-10-15 05:13:45 +07:00
|
|
|
print_pkt(skb->data, skb->len);
|
|
|
|
}
|
2016-02-29 20:27:38 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) {
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_hw(priv))
|
|
|
|
pr_debug("%s: stop transmitted packets\n", __func__);
|
2009-10-15 05:13:45 +07:00
|
|
|
netif_stop_queue(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->stats.tx_bytes += skb->len;
|
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
/* According to the coalesce parameter the IC bit for the latest
|
|
|
|
* segment is reset and the timer re-started to clean the tx status.
|
|
|
|
* This approach takes care about the fragments: desc is the first
|
|
|
|
* element in case of no SG.
|
|
|
|
*/
|
|
|
|
priv->tx_count_frames += nfrags + 1;
|
|
|
|
if (likely(priv->tx_coal_frames > priv->tx_count_frames)) {
|
|
|
|
mod_timer(&priv->txtimer,
|
|
|
|
STMMAC_COAL_TIMER(priv->tx_coal_timer));
|
|
|
|
} else {
|
|
|
|
priv->tx_count_frames = 0;
|
|
|
|
priv->hw->desc->set_tx_ic(desc);
|
|
|
|
priv->xstats.tx_set_ic_bit++;
|
2013-03-26 11:43:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!priv->hwts_tx_en)
|
|
|
|
skb_tx_timestamp(skb);
|
2011-06-12 09:19:06 +07:00
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
/* Ready to fill the first descriptor and set the OWN bit w/o any
|
|
|
|
* problems because all the descriptors are actually ready to be
|
|
|
|
* passed to the DMA engine.
|
|
|
|
*/
|
|
|
|
if (likely(!is_jumbo)) {
|
|
|
|
bool last_segment = (nfrags == 0);
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
des = dma_map_single(priv->device, skb->data,
|
|
|
|
nopaged_len, DMA_TO_DEVICE);
|
|
|
|
if (dma_mapping_error(priv->device, des))
|
2016-02-29 20:27:38 +07:00
|
|
|
goto dma_map_err;
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) {
|
|
|
|
first->des0 = des;
|
|
|
|
priv->tx_skbuff_dma[first_entry].buf = first->des0;
|
|
|
|
} else {
|
|
|
|
first->des2 = des;
|
|
|
|
priv->tx_skbuff_dma[first_entry].buf = first->des2;
|
|
|
|
}
|
|
|
|
|
2016-02-29 20:27:38 +07:00
|
|
|
priv->tx_skbuff_dma[first_entry].len = nopaged_len;
|
|
|
|
priv->tx_skbuff_dma[first_entry].last_segment = last_segment;
|
|
|
|
|
|
|
|
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
|
|
|
|
priv->hwts_tx_en)) {
|
|
|
|
/* declare that device is doing timestamping */
|
|
|
|
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
|
|
|
priv->hw->desc->enable_tx_timestamp(first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare the first descriptor setting the OWN bit too */
|
|
|
|
priv->hw->desc->prepare_tx_desc(first, 1, nopaged_len,
|
|
|
|
csum_insertion, priv->mode, 1,
|
|
|
|
last_segment);
|
|
|
|
|
|
|
|
/* The own bit must be the latest setting done when prepare the
|
|
|
|
* descriptor and then barrier is needed to make sure that
|
|
|
|
* all is coherent before granting the DMA engine.
|
|
|
|
*/
|
|
|
|
smp_wmb();
|
|
|
|
}
|
|
|
|
|
2015-01-22 01:07:27 +07:00
|
|
|
netdev_sent_queue(dev, skb->len);
|
2016-04-01 16:37:34 +07:00
|
|
|
|
|
|
|
if (priv->synopsys_id < DWMAC_CORE_4_00)
|
|
|
|
priv->hw->dma->enable_dma_transmission(priv->ioaddr);
|
|
|
|
else
|
|
|
|
priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr,
|
|
|
|
STMMAC_CHAN0);
|
2011-06-19 10:31:43 +07:00
|
|
|
|
2011-10-18 07:01:19 +07:00
|
|
|
spin_unlock(&priv->tx_lock);
|
2014-08-27 16:27:00 +07:00
|
|
|
return NETDEV_TX_OK;
|
2011-10-18 07:01:19 +07:00
|
|
|
|
2014-08-27 16:27:00 +07:00
|
|
|
dma_map_err:
|
2014-11-04 23:08:06 +07:00
|
|
|
spin_unlock(&priv->tx_lock);
|
2014-08-27 16:27:00 +07:00
|
|
|
dev_err(priv->device, "Tx dma map failed\n");
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
priv->dev->stats.tx_dropped++;
|
2009-10-15 05:13:45 +07:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2014-01-15 02:42:05 +07:00
|
|
|
static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ethhdr *ehdr;
|
|
|
|
u16 vlanid;
|
|
|
|
|
|
|
|
if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) ==
|
|
|
|
NETIF_F_HW_VLAN_CTAG_RX &&
|
|
|
|
!__vlan_get_tag(skb, &vlanid)) {
|
|
|
|
/* pop the vlan tag */
|
|
|
|
ehdr = (struct ethhdr *)skb->data;
|
|
|
|
memmove(skb->data + VLAN_HLEN, ehdr, ETH_ALEN * 2);
|
|
|
|
skb_pull(skb, VLAN_HLEN);
|
|
|
|
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlanid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-29 20:27:42 +07:00
|
|
|
static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
if (priv->rx_zeroc_thresh < STMMAC_RX_THRESH)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_rx_refill - refill used skb preallocated buffers
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* Description : this is to reallocate the skb for the reception process
|
|
|
|
* that is based on zero-copy.
|
|
|
|
*/
|
2009-10-15 05:13:45 +07:00
|
|
|
static inline void stmmac_rx_refill(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
int bfsize = priv->dma_buf_sz;
|
2016-02-29 20:27:30 +07:00
|
|
|
unsigned int entry = priv->dirty_rx;
|
|
|
|
int dirty = stmmac_rx_dirty(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
while (dirty-- > 0) {
|
2013-03-26 11:43:06 +07:00
|
|
|
struct dma_desc *p;
|
|
|
|
|
|
|
|
if (priv->extend_desc)
|
2013-04-08 09:10:01 +07:00
|
|
|
p = (struct dma_desc *)(priv->dma_erx + entry);
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
|
|
|
p = priv->dma_rx + entry;
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (likely(priv->rx_skbuff[entry] == NULL)) {
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2012-10-05 13:23:55 +07:00
|
|
|
skb = netdev_alloc_skb_ip_align(priv->dev, bfsize);
|
2016-02-29 20:27:42 +07:00
|
|
|
if (unlikely(!skb)) {
|
|
|
|
/* so for a while no zero-copy! */
|
|
|
|
priv->rx_zeroc_thresh = STMMAC_RX_THRESH;
|
|
|
|
if (unlikely(net_ratelimit()))
|
|
|
|
dev_err(priv->device,
|
|
|
|
"fail to alloc skb entry %d\n",
|
|
|
|
entry);
|
2009-10-15 05:13:45 +07:00
|
|
|
break;
|
2016-02-29 20:27:42 +07:00
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
priv->rx_skbuff[entry] = skb;
|
|
|
|
priv->rx_skbuff_dma[entry] =
|
|
|
|
dma_map_single(priv->device, skb->data, bfsize,
|
|
|
|
DMA_FROM_DEVICE);
|
2014-08-27 16:27:00 +07:00
|
|
|
if (dma_mapping_error(priv->device,
|
|
|
|
priv->rx_skbuff_dma[entry])) {
|
|
|
|
dev_err(priv->device, "Rx dma map failed\n");
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
break;
|
|
|
|
}
|
2011-10-18 07:01:24 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) {
|
|
|
|
p->des0 = priv->rx_skbuff_dma[entry];
|
|
|
|
p->des1 = 0;
|
|
|
|
} else {
|
|
|
|
p->des2 = priv->rx_skbuff_dma[entry];
|
|
|
|
}
|
|
|
|
if (priv->hw->mode->refill_desc3)
|
|
|
|
priv->hw->mode->refill_desc3(priv, p);
|
2011-10-18 07:01:24 +07:00
|
|
|
|
2016-02-29 20:27:42 +07:00
|
|
|
if (priv->rx_zeroc_thresh > 0)
|
|
|
|
priv->rx_zeroc_thresh--;
|
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_rx_status(priv))
|
|
|
|
pr_debug("\trefill entry #%d\n", entry);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2011-07-18 03:54:08 +07:00
|
|
|
wmb();
|
2016-04-01 16:37:34 +07:00
|
|
|
|
|
|
|
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
|
|
|
|
priv->hw->desc->init_rx_desc(p, priv->use_riwt, 0, 0);
|
|
|
|
else
|
|
|
|
priv->hw->desc->set_rx_owner(p);
|
|
|
|
|
2012-07-09 04:14:45 +07:00
|
|
|
wmb();
|
2016-02-29 20:27:30 +07:00
|
|
|
|
|
|
|
entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->dirty_rx = entry;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
2014-11-18 15:47:01 +07:00
|
|
|
* stmmac_rx - manage the receive process
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
|
|
|
* @limit: napi bugget.
|
|
|
|
* Description : this the function called by the napi poll method.
|
|
|
|
* It gets all the frames inside the ring.
|
|
|
|
*/
|
2009-10-15 05:13:45 +07:00
|
|
|
static int stmmac_rx(struct stmmac_priv *priv, int limit)
|
|
|
|
{
|
2016-02-29 20:27:30 +07:00
|
|
|
unsigned int entry = priv->cur_rx;
|
2009-10-15 05:13:45 +07:00
|
|
|
unsigned int next_entry;
|
|
|
|
unsigned int count = 0;
|
2014-09-01 14:17:52 +07:00
|
|
|
int coe = priv->hw->rx_csum;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_rx_status(priv)) {
|
2016-04-01 16:37:26 +07:00
|
|
|
void *rx_head;
|
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
pr_debug("%s: descriptor ring:\n", __func__);
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
2016-04-01 16:37:26 +07:00
|
|
|
rx_head = (void *)priv->dma_erx;
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
2016-04-01 16:37:26 +07:00
|
|
|
rx_head = (void *)priv->dma_rx;
|
|
|
|
|
|
|
|
priv->hw->desc->display_ring(rx_head, DMA_RX_SIZE, true);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2013-03-26 11:43:06 +07:00
|
|
|
while (count < limit) {
|
2009-10-15 05:13:45 +07:00
|
|
|
int status;
|
2013-04-08 09:10:03 +07:00
|
|
|
struct dma_desc *p;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
2013-04-08 09:10:01 +07:00
|
|
|
p = (struct dma_desc *)(priv->dma_erx + entry);
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
2013-04-08 09:10:01 +07:00
|
|
|
p = priv->dma_rx + entry;
|
2013-03-26 11:43:06 +07:00
|
|
|
|
2016-02-29 20:27:34 +07:00
|
|
|
/* read the status of the incoming frame */
|
|
|
|
status = priv->hw->desc->rx_status(&priv->dev->stats,
|
|
|
|
&priv->xstats, p);
|
|
|
|
/* check if managed by the DMA otherwise go ahead */
|
|
|
|
if (unlikely(status & dma_own))
|
2009-10-15 05:13:45 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
2016-02-29 20:27:30 +07:00
|
|
|
priv->cur_rx = STMMAC_GET_ENTRY(priv->cur_rx, DMA_RX_SIZE);
|
|
|
|
next_entry = priv->cur_rx;
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc)
|
2013-04-08 09:10:03 +07:00
|
|
|
prefetch(priv->dma_erx + next_entry);
|
2013-03-26 11:43:06 +07:00
|
|
|
else
|
2013-04-08 09:10:03 +07:00
|
|
|
prefetch(priv->dma_rx + next_entry);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status))
|
|
|
|
priv->hw->desc->rx_extended_status(&priv->dev->stats,
|
|
|
|
&priv->xstats,
|
|
|
|
priv->dma_erx +
|
|
|
|
entry);
|
2013-03-26 11:43:10 +07:00
|
|
|
if (unlikely(status == discard_frame)) {
|
2009-10-15 05:13:45 +07:00
|
|
|
priv->dev->stats.rx_errors++;
|
2013-03-26 11:43:10 +07:00
|
|
|
if (priv->hwts_rx_en && !priv->extend_desc) {
|
|
|
|
/* DESC2 & DESC3 will be overwitten by device
|
|
|
|
* with timestamp value, hence reinitialize
|
|
|
|
* them in stmmac_rx_refill() function so that
|
|
|
|
* device can reuse it.
|
|
|
|
*/
|
|
|
|
priv->rx_skbuff[entry] = NULL;
|
|
|
|
dma_unmap_single(priv->device,
|
2013-04-08 09:10:01 +07:00
|
|
|
priv->rx_skbuff_dma[entry],
|
|
|
|
priv->dma_buf_sz,
|
|
|
|
DMA_FROM_DEVICE);
|
2013-03-26 11:43:10 +07:00
|
|
|
}
|
|
|
|
} else {
|
2009-10-15 05:13:45 +07:00
|
|
|
struct sk_buff *skb;
|
2010-07-27 07:09:47 +07:00
|
|
|
int frame_len;
|
2016-04-01 16:37:34 +07:00
|
|
|
unsigned int des;
|
|
|
|
|
|
|
|
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
|
|
|
|
des = p->des0;
|
|
|
|
else
|
|
|
|
des = p->des2;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-04-08 09:10:01 +07:00
|
|
|
frame_len = priv->hw->desc->get_rx_frame_len(p, coe);
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/* If frame length is greather than skb buffer size
|
|
|
|
* (preallocated during init) then the packet is
|
|
|
|
* ignored
|
|
|
|
*/
|
2015-11-26 14:35:45 +07:00
|
|
|
if (frame_len > priv->dma_buf_sz) {
|
2016-04-01 16:37:34 +07:00
|
|
|
pr_err("%s: len %d larger than size (%d)\n",
|
|
|
|
priv->dev->name, frame_len,
|
|
|
|
priv->dma_buf_sz);
|
2015-11-26 14:35:45 +07:00
|
|
|
priv->dev->stats.rx_length_errors++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-07-27 07:09:47 +07:00
|
|
|
/* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3
|
2013-04-08 09:10:01 +07:00
|
|
|
* Type frames (LLC/LLC-SNAP)
|
|
|
|
*/
|
2010-07-27 07:09:47 +07:00
|
|
|
if (unlikely(status != llc_snap))
|
|
|
|
frame_len -= ETH_FCS_LEN;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2013-07-02 19:12:36 +07:00
|
|
|
if (netif_msg_rx_status(priv)) {
|
2009-10-15 05:13:45 +07:00
|
|
|
pr_debug("\tdesc: %p [entry %d] buff=0x%x\n",
|
2016-04-01 16:37:34 +07:00
|
|
|
p, entry, des);
|
2013-07-02 19:12:36 +07:00
|
|
|
if (frame_len > ETH_FRAME_LEN)
|
|
|
|
pr_debug("\tframe size %d, COE: %d\n",
|
|
|
|
frame_len, status);
|
|
|
|
}
|
2016-02-29 20:27:41 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/* The zero-copy is always used for all the sizes
|
|
|
|
* in case of GMAC4 because it needs
|
|
|
|
* to refill the used descriptors, always.
|
|
|
|
*/
|
|
|
|
if (unlikely(!priv->plat->has_gmac4 &&
|
|
|
|
((frame_len < priv->rx_copybreak) ||
|
|
|
|
stmmac_rx_threshold_count(priv)))) {
|
2016-02-29 20:27:41 +07:00
|
|
|
skb = netdev_alloc_skb_ip_align(priv->dev,
|
|
|
|
frame_len);
|
|
|
|
if (unlikely(!skb)) {
|
|
|
|
if (net_ratelimit())
|
|
|
|
dev_warn(priv->device,
|
|
|
|
"packet dropped\n");
|
|
|
|
priv->dev->stats.rx_dropped++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_sync_single_for_cpu(priv->device,
|
|
|
|
priv->rx_skbuff_dma
|
|
|
|
[entry], frame_len,
|
|
|
|
DMA_FROM_DEVICE);
|
|
|
|
skb_copy_to_linear_data(skb,
|
|
|
|
priv->
|
|
|
|
rx_skbuff[entry]->data,
|
|
|
|
frame_len);
|
|
|
|
|
|
|
|
skb_put(skb, frame_len);
|
|
|
|
dma_sync_single_for_device(priv->device,
|
|
|
|
priv->rx_skbuff_dma
|
|
|
|
[entry], frame_len,
|
|
|
|
DMA_FROM_DEVICE);
|
|
|
|
} else {
|
|
|
|
skb = priv->rx_skbuff[entry];
|
|
|
|
if (unlikely(!skb)) {
|
|
|
|
pr_err("%s: Inconsistent Rx chain\n",
|
|
|
|
priv->dev->name);
|
|
|
|
priv->dev->stats.rx_dropped++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prefetch(skb->data - NET_IP_ALIGN);
|
|
|
|
priv->rx_skbuff[entry] = NULL;
|
2016-02-29 20:27:42 +07:00
|
|
|
priv->rx_zeroc_thresh++;
|
2016-02-29 20:27:41 +07:00
|
|
|
|
|
|
|
skb_put(skb, frame_len);
|
|
|
|
dma_unmap_single(priv->device,
|
|
|
|
priv->rx_skbuff_dma[entry],
|
|
|
|
priv->dma_buf_sz,
|
|
|
|
DMA_FROM_DEVICE);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2013-03-26 11:43:10 +07:00
|
|
|
stmmac_get_rx_hwtstamp(priv, entry, skb);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (netif_msg_pktdata(priv)) {
|
2013-07-02 19:12:36 +07:00
|
|
|
pr_debug("frame received (%dbytes)", frame_len);
|
2009-10-15 05:13:45 +07:00
|
|
|
print_pkt(skb->data, frame_len);
|
|
|
|
}
|
2013-07-02 19:12:36 +07:00
|
|
|
|
2014-01-15 02:42:05 +07:00
|
|
|
stmmac_rx_vlan(priv->dev, skb);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
skb->protocol = eth_type_trans(skb, priv->dev);
|
|
|
|
|
2013-04-08 09:10:01 +07:00
|
|
|
if (unlikely(!coe))
|
2010-09-03 03:07:41 +07:00
|
|
|
skb_checksum_none_assert(skb);
|
2012-11-26 06:10:43 +07:00
|
|
|
else
|
2009-10-15 05:13:45 +07:00
|
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
2012-11-26 06:10:43 +07:00
|
|
|
|
|
|
|
napi_gro_receive(&priv->napi, skb);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
priv->dev->stats.rx_packets++;
|
|
|
|
priv->dev->stats.rx_bytes += frame_len;
|
|
|
|
}
|
|
|
|
entry = next_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
stmmac_rx_refill(priv);
|
|
|
|
|
|
|
|
priv->xstats.rx_pkt_n += count;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_poll - stmmac poll method (NAPI)
|
|
|
|
* @napi : pointer to the napi structure.
|
|
|
|
* @budget : maximum number of packets that the current CPU can receive from
|
|
|
|
* all interfaces.
|
|
|
|
* Description :
|
2012-11-26 06:10:42 +07:00
|
|
|
* To look at the incoming frames and clear the tx resources.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static int stmmac_poll(struct napi_struct *napi, int budget)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi);
|
|
|
|
int work_done = 0;
|
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
priv->xstats.napi_poll++;
|
|
|
|
stmmac_tx_clean(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2012-11-26 06:10:42 +07:00
|
|
|
work_done = stmmac_rx(priv, budget);
|
2009-10-15 05:13:45 +07:00
|
|
|
if (work_done < budget) {
|
|
|
|
napi_complete(napi);
|
2012-11-26 06:10:42 +07:00
|
|
|
stmmac_enable_dma_irq(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
return work_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_tx_timeout
|
|
|
|
* @dev : Pointer to net device structure
|
|
|
|
* Description: this function is called when a packet transmission fails to
|
2012-11-26 06:10:41 +07:00
|
|
|
* complete within a reasonable time. The driver will mark the error in the
|
2009-10-15 05:13:45 +07:00
|
|
|
* netdev structure and arrange for the device to be reset to a sane state
|
|
|
|
* in order to transmit a new packet.
|
|
|
|
*/
|
|
|
|
static void stmmac_tx_timeout(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
|
|
|
/* Clear Tx resources and restart transmitting again */
|
|
|
|
stmmac_tx_err(priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-08-16 13:29:00 +07:00
|
|
|
* stmmac_set_rx_mode - entry point for multicast addressing
|
2009-10-15 05:13:45 +07:00
|
|
|
* @dev : pointer to the device structure
|
|
|
|
* Description:
|
|
|
|
* This function is a driver entry point which gets called by the kernel
|
|
|
|
* whenever multicast addresses must be enabled/disabled.
|
|
|
|
* Return value:
|
|
|
|
* void.
|
|
|
|
*/
|
2011-08-16 13:29:00 +07:00
|
|
|
static void stmmac_set_rx_mode(struct net_device *dev)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
2014-08-01 03:49:17 +07:00
|
|
|
priv->hw->mac->set_filter(priv->hw, dev);
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_change_mtu - entry point to change MTU size for the device.
|
|
|
|
* @dev : device pointer.
|
|
|
|
* @new_mtu : the new MTU size for the device.
|
|
|
|
* Description: the Maximum Transfer Unit (MTU) is used by the network layer
|
|
|
|
* to drive packet transmission. Ethernet has an MTU of 1500 octets
|
|
|
|
* (ETH_DATA_LEN). This value can be changed with ifconfig.
|
|
|
|
* Return value:
|
|
|
|
* 0 on success and an appropriate (-)ve integer as defined in errno.h
|
|
|
|
* file on failure.
|
|
|
|
*/
|
|
|
|
static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
int max_mtu;
|
|
|
|
|
|
|
|
if (netif_running(dev)) {
|
|
|
|
pr_err("%s: must be stopped to change its MTU\n", dev->name);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
|
2009-10-15 05:13:45 +07:00
|
|
|
max_mtu = JUMBO_LEN;
|
|
|
|
else
|
2011-10-18 08:39:55 +07:00
|
|
|
max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-01-20 18:39:01 +07:00
|
|
|
if (priv->plat->maxmtu < max_mtu)
|
|
|
|
max_mtu = priv->plat->maxmtu;
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if ((new_mtu < 46) || (new_mtu > max_mtu)) {
|
|
|
|
pr_err("%s: invalid MTU, max MTU is: %d\n", dev->name, max_mtu);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-04-09 09:46:55 +07:00
|
|
|
dev->mtu = new_mtu;
|
2016-04-01 16:37:34 +07:00
|
|
|
|
2011-04-09 09:46:55 +07:00
|
|
|
netdev_update_features(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-15 22:29:55 +07:00
|
|
|
static netdev_features_t stmmac_fix_features(struct net_device *dev,
|
2013-04-08 09:10:01 +07:00
|
|
|
netdev_features_t features)
|
2011-04-09 09:46:55 +07:00
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
2012-04-04 11:33:21 +07:00
|
|
|
if (priv->plat->rx_coe == STMMAC_RX_COE_NONE)
|
2011-04-09 09:46:55 +07:00
|
|
|
features &= ~NETIF_F_RXCSUM;
|
2014-09-01 14:17:52 +07:00
|
|
|
|
2011-04-09 09:46:55 +07:00
|
|
|
if (!priv->plat->tx_coe)
|
2015-12-15 02:19:43 +07:00
|
|
|
features &= ~NETIF_F_CSUM_MASK;
|
2011-04-09 09:46:55 +07:00
|
|
|
|
2010-09-17 10:23:40 +07:00
|
|
|
/* Some GMAC devices have a bugged Jumbo frame support that
|
|
|
|
* needs to have the Tx COE disabled for oversized frames
|
|
|
|
* (due to limited buffer sizes). In this case we disable
|
2013-04-08 09:10:01 +07:00
|
|
|
* the TX csum insertionin the TDES and not use SF.
|
|
|
|
*/
|
2011-04-09 09:46:55 +07:00
|
|
|
if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN))
|
2015-12-15 02:19:43 +07:00
|
|
|
features &= ~NETIF_F_CSUM_MASK;
|
2010-09-17 10:23:40 +07:00
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/* Disable tso if asked by ethtool */
|
|
|
|
if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) {
|
|
|
|
if (features & NETIF_F_TSO)
|
|
|
|
priv->tso = true;
|
|
|
|
else
|
|
|
|
priv->tso = false;
|
|
|
|
}
|
|
|
|
|
2011-04-09 09:46:55 +07:00
|
|
|
return features;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2014-09-01 14:17:52 +07:00
|
|
|
static int stmmac_set_features(struct net_device *netdev,
|
|
|
|
netdev_features_t features)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(netdev);
|
|
|
|
|
|
|
|
/* Keep the COE Type in case of csum is supporting */
|
|
|
|
if (features & NETIF_F_RXCSUM)
|
|
|
|
priv->hw->rx_csum = priv->plat->rx_coe;
|
|
|
|
else
|
|
|
|
priv->hw->rx_csum = 0;
|
|
|
|
/* No check needed because rx_coe has been set before and it will be
|
|
|
|
* fixed in case of issue.
|
|
|
|
*/
|
|
|
|
priv->hw->mac->rx_ipc(priv->hw);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-08 09:10:00 +07:00
|
|
|
/**
|
|
|
|
* stmmac_interrupt - main ISR
|
|
|
|
* @irq: interrupt number.
|
|
|
|
* @dev_id: to pass the net device pointer.
|
|
|
|
* Description: this is the main driver interrupt service routine.
|
2014-11-18 15:47:01 +07:00
|
|
|
* It can call:
|
|
|
|
* o DMA service routine (to manage incoming frame reception and transmission
|
|
|
|
* status)
|
|
|
|
* o Core interrupts to manage: remote wake-up, management counter, LPI
|
|
|
|
* interrupts.
|
2013-04-08 09:10:00 +07:00
|
|
|
*/
|
2009-10-15 05:13:45 +07:00
|
|
|
static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct net_device *dev = (struct net_device *)dev_id;
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
2014-01-16 17:53:00 +07:00
|
|
|
if (priv->irq_wake)
|
|
|
|
pm_wakeup_event(priv->device, 0);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
if (unlikely(!dev)) {
|
|
|
|
pr_err("%s: invalid dev pointer\n", __func__);
|
|
|
|
return IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
/* To handle GMAC own interrupts */
|
2016-04-01 16:37:34 +07:00
|
|
|
if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
|
2014-08-01 03:49:13 +07:00
|
|
|
int status = priv->hw->mac->host_irq_status(priv->hw,
|
2013-03-26 11:43:07 +07:00
|
|
|
&priv->xstats);
|
2012-06-28 04:14:37 +07:00
|
|
|
if (unlikely(status)) {
|
|
|
|
/* For LPI we need to save the tx status */
|
2013-03-26 11:43:07 +07:00
|
|
|
if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE)
|
2012-06-28 04:14:37 +07:00
|
|
|
priv->tx_path_in_lpi_mode = true;
|
2013-03-26 11:43:07 +07:00
|
|
|
if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE)
|
2012-06-28 04:14:37 +07:00
|
|
|
priv->tx_path_in_lpi_mode = false;
|
2016-07-01 02:46:16 +07:00
|
|
|
if (status & CORE_IRQ_MTL_RX_OVERFLOW && priv->hw->dma->set_rx_tail_ptr)
|
2016-04-01 16:37:34 +07:00
|
|
|
priv->hw->dma->set_rx_tail_ptr(priv->ioaddr,
|
|
|
|
priv->rx_tail_addr,
|
|
|
|
STMMAC_CHAN0);
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
2016-06-24 20:16:24 +07:00
|
|
|
|
|
|
|
/* PCS link status */
|
2016-06-24 20:16:25 +07:00
|
|
|
if (priv->hw->pcs) {
|
2016-06-24 20:16:24 +07:00
|
|
|
if (priv->xstats.pcs_link)
|
|
|
|
netif_carrier_on(dev);
|
|
|
|
else
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
}
|
2012-06-28 04:14:37 +07:00
|
|
|
}
|
2010-01-07 06:07:18 +07:00
|
|
|
|
2012-06-28 04:14:37 +07:00
|
|
|
/* To handle DMA interrupts */
|
2010-01-07 06:07:18 +07:00
|
|
|
stmmac_dma_interrupt(priv);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
/* Polling receive - used by NETCONSOLE and other diagnostic tools
|
2013-04-08 09:10:01 +07:00
|
|
|
* to allow network I/O with interrupts disabled.
|
|
|
|
*/
|
2009-10-15 05:13:45 +07:00
|
|
|
static void stmmac_poll_controller(struct net_device *dev)
|
|
|
|
{
|
|
|
|
disable_irq(dev->irq);
|
|
|
|
stmmac_interrupt(dev->irq, dev);
|
|
|
|
enable_irq(dev->irq);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_ioctl - Entry point for the Ioctl
|
|
|
|
* @dev: Device pointer.
|
|
|
|
* @rq: An IOCTL specefic structure, that can contain a pointer to
|
|
|
|
* a proprietary structure used to pass information to the driver.
|
|
|
|
* @cmd: IOCTL command
|
|
|
|
* Description:
|
2013-04-08 09:10:00 +07:00
|
|
|
* Currently it supports the phy_mii_ioctl(...) and HW time stamping.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
|
|
|
static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
|
|
{
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
2013-03-26 11:43:10 +07:00
|
|
|
int ret = -EOPNOTSUPP;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
if (!netif_running(dev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-03-26 11:43:10 +07:00
|
|
|
switch (cmd) {
|
|
|
|
case SIOCGMIIPHY:
|
|
|
|
case SIOCGMIIREG:
|
|
|
|
case SIOCSMIIREG:
|
|
|
|
if (!priv->phydev)
|
|
|
|
return -EINVAL;
|
|
|
|
ret = phy_mii_ioctl(priv->phydev, rq, cmd);
|
|
|
|
break;
|
|
|
|
case SIOCSHWTSTAMP:
|
|
|
|
ret = stmmac_hwtstamp_ioctl(dev, rq);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-07-17 15:48:55 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-04 21:49:33 +07:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
2011-09-02 04:51:39 +07:00
|
|
|
static struct dentry *stmmac_fs_dir;
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
static void sysfs_display_ring(void *head, int size, int extend_desc,
|
2013-04-08 09:10:01 +07:00
|
|
|
struct seq_file *seq)
|
2011-09-02 04:51:39 +07:00
|
|
|
{
|
|
|
|
int i;
|
2013-04-08 09:10:01 +07:00
|
|
|
struct dma_extended_desc *ep = (struct dma_extended_desc *)head;
|
|
|
|
struct dma_desc *p = (struct dma_desc *)head;
|
2011-09-02 04:51:39 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
u64 x;
|
|
|
|
if (extend_desc) {
|
|
|
|
x = *(u64 *) ep;
|
|
|
|
seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
|
2013-04-08 09:10:01 +07:00
|
|
|
i, (unsigned int)virt_to_phys(ep),
|
2016-04-01 16:37:34 +07:00
|
|
|
ep->basic.des0, ep->basic.des1,
|
2013-03-26 11:43:06 +07:00
|
|
|
ep->basic.des2, ep->basic.des3);
|
|
|
|
ep++;
|
|
|
|
} else {
|
|
|
|
x = *(u64 *) p;
|
|
|
|
seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n",
|
2013-04-08 09:10:01 +07:00
|
|
|
i, (unsigned int)virt_to_phys(ep),
|
2016-04-01 16:37:34 +07:00
|
|
|
p->des0, p->des1, p->des2, p->des3);
|
2013-03-26 11:43:06 +07:00
|
|
|
p++;
|
|
|
|
}
|
2011-09-02 04:51:39 +07:00
|
|
|
seq_printf(seq, "\n");
|
|
|
|
}
|
2013-03-26 11:43:06 +07:00
|
|
|
}
|
2011-09-02 04:51:39 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
struct net_device *dev = seq->private;
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
2011-09-02 04:51:39 +07:00
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
if (priv->extend_desc) {
|
|
|
|
seq_printf(seq, "Extended RX descriptor ring:\n");
|
2016-02-29 20:27:30 +07:00
|
|
|
sysfs_display_ring((void *)priv->dma_erx, DMA_RX_SIZE, 1, seq);
|
2013-03-26 11:43:06 +07:00
|
|
|
seq_printf(seq, "Extended TX descriptor ring:\n");
|
2016-02-29 20:27:30 +07:00
|
|
|
sysfs_display_ring((void *)priv->dma_etx, DMA_TX_SIZE, 1, seq);
|
2013-03-26 11:43:06 +07:00
|
|
|
} else {
|
|
|
|
seq_printf(seq, "RX descriptor ring:\n");
|
2016-02-29 20:27:30 +07:00
|
|
|
sysfs_display_ring((void *)priv->dma_rx, DMA_RX_SIZE, 0, seq);
|
2013-03-26 11:43:06 +07:00
|
|
|
seq_printf(seq, "TX descriptor ring:\n");
|
2016-02-29 20:27:30 +07:00
|
|
|
sysfs_display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0, seq);
|
2011-09-02 04:51:39 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int stmmac_sysfs_ring_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, stmmac_sysfs_ring_read, inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations stmmac_rings_status_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = stmmac_sysfs_ring_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
2012-05-20 20:55:30 +07:00
|
|
|
.release = single_release,
|
2011-09-02 04:51:39 +07:00
|
|
|
};
|
|
|
|
|
2011-09-02 04:51:41 +07:00
|
|
|
static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
struct net_device *dev = seq->private;
|
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
2011-11-17 04:58:00 +07:00
|
|
|
if (!priv->hw_cap_support) {
|
2011-09-02 04:51:41 +07:00
|
|
|
seq_printf(seq, "DMA HW features not supported\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
seq_printf(seq, "==============================\n");
|
|
|
|
seq_printf(seq, "\tDMA HW features\n");
|
|
|
|
seq_printf(seq, "==============================\n");
|
|
|
|
|
|
|
|
seq_printf(seq, "\t10/100 Mbps %s\n",
|
|
|
|
(priv->dma_cap.mbps_10_100) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\t1000 Mbps %s\n",
|
|
|
|
(priv->dma_cap.mbps_1000) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tHalf duple %s\n",
|
|
|
|
(priv->dma_cap.half_duplex) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tHash Filter: %s\n",
|
|
|
|
(priv->dma_cap.hash_filter) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tMultiple MAC address registers: %s\n",
|
|
|
|
(priv->dma_cap.multi_addr) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tPCS (TBI/SGMII/RTBI PHY interfatces): %s\n",
|
|
|
|
(priv->dma_cap.pcs) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tSMA (MDIO) Interface: %s\n",
|
|
|
|
(priv->dma_cap.sma_mdio) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tPMT Remote wake up: %s\n",
|
|
|
|
(priv->dma_cap.pmt_remote_wake_up) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tPMT Magic Frame: %s\n",
|
|
|
|
(priv->dma_cap.pmt_magic_frame) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tRMON module: %s\n",
|
|
|
|
(priv->dma_cap.rmon) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tIEEE 1588-2002 Time Stamp: %s\n",
|
|
|
|
(priv->dma_cap.time_stamp) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tIEEE 1588-2008 Advanced Time Stamp:%s\n",
|
|
|
|
(priv->dma_cap.atime_stamp) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\t802.3az - Energy-Efficient Ethernet (EEE) %s\n",
|
|
|
|
(priv->dma_cap.eee) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tAV features: %s\n", (priv->dma_cap.av) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tChecksum Offload in TX: %s\n",
|
|
|
|
(priv->dma_cap.tx_coe) ? "Y" : "N");
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00) {
|
|
|
|
seq_printf(seq, "\tIP Checksum Offload in RX: %s\n",
|
|
|
|
(priv->dma_cap.rx_coe) ? "Y" : "N");
|
|
|
|
} else {
|
|
|
|
seq_printf(seq, "\tIP Checksum Offload (type1) in RX: %s\n",
|
|
|
|
(priv->dma_cap.rx_coe_type1) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tIP Checksum Offload (type2) in RX: %s\n",
|
|
|
|
(priv->dma_cap.rx_coe_type2) ? "Y" : "N");
|
|
|
|
}
|
2011-09-02 04:51:41 +07:00
|
|
|
seq_printf(seq, "\tRXFIFO > 2048bytes: %s\n",
|
|
|
|
(priv->dma_cap.rxfifo_over_2048) ? "Y" : "N");
|
|
|
|
seq_printf(seq, "\tNumber of Additional RX channel: %d\n",
|
|
|
|
priv->dma_cap.number_rx_channel);
|
|
|
|
seq_printf(seq, "\tNumber of Additional TX channel: %d\n",
|
|
|
|
priv->dma_cap.number_tx_channel);
|
|
|
|
seq_printf(seq, "\tEnhanced descriptors: %s\n",
|
|
|
|
(priv->dma_cap.enh_desc) ? "Y" : "N");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int stmmac_sysfs_dma_cap_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, stmmac_sysfs_dma_cap_read, inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations stmmac_dma_cap_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = stmmac_sysfs_dma_cap_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
2012-05-20 20:55:30 +07:00
|
|
|
.release = single_release,
|
2011-09-02 04:51:41 +07:00
|
|
|
};
|
|
|
|
|
2011-09-02 04:51:39 +07:00
|
|
|
static int stmmac_init_fs(struct net_device *dev)
|
|
|
|
{
|
2015-05-23 09:03:29 +07:00
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
|
|
|
/* Create per netdev entries */
|
|
|
|
priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir);
|
2011-09-02 04:51:39 +07:00
|
|
|
|
2015-05-23 09:03:29 +07:00
|
|
|
if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) {
|
|
|
|
pr_err("ERROR %s/%s, debugfs create directory failed\n",
|
|
|
|
STMMAC_RESOURCE_NAME, dev->name);
|
2011-09-02 04:51:39 +07:00
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Entry to report DMA RX/TX rings */
|
2015-05-23 09:03:29 +07:00
|
|
|
priv->dbgfs_rings_status =
|
|
|
|
debugfs_create_file("descriptors_status", S_IRUGO,
|
|
|
|
priv->dbgfs_dir, dev,
|
|
|
|
&stmmac_rings_status_fops);
|
2011-09-02 04:51:39 +07:00
|
|
|
|
2015-05-23 09:03:29 +07:00
|
|
|
if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) {
|
2011-09-02 04:51:39 +07:00
|
|
|
pr_info("ERROR creating stmmac ring debugfs file\n");
|
2015-05-23 09:03:29 +07:00
|
|
|
debugfs_remove_recursive(priv->dbgfs_dir);
|
2011-09-02 04:51:39 +07:00
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2011-09-02 04:51:41 +07:00
|
|
|
/* Entry to report the DMA HW features */
|
2015-05-23 09:03:29 +07:00
|
|
|
priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
|
|
|
|
priv->dbgfs_dir,
|
|
|
|
dev, &stmmac_dma_cap_fops);
|
2011-09-02 04:51:41 +07:00
|
|
|
|
2015-05-23 09:03:29 +07:00
|
|
|
if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
|
2011-09-02 04:51:41 +07:00
|
|
|
pr_info("ERROR creating stmmac MMC debugfs file\n");
|
2015-05-23 09:03:29 +07:00
|
|
|
debugfs_remove_recursive(priv->dbgfs_dir);
|
2011-09-02 04:51:41 +07:00
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2011-09-02 04:51:39 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-23 09:03:29 +07:00
|
|
|
static void stmmac_exit_fs(struct net_device *dev)
|
2011-09-02 04:51:39 +07:00
|
|
|
{
|
2015-05-23 09:03:29 +07:00
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
|
|
|
debugfs_remove_recursive(priv->dbgfs_dir);
|
2011-09-02 04:51:39 +07:00
|
|
|
}
|
2014-11-04 21:49:33 +07:00
|
|
|
#endif /* CONFIG_DEBUG_FS */
|
2011-09-02 04:51:39 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
static const struct net_device_ops stmmac_netdev_ops = {
|
|
|
|
.ndo_open = stmmac_open,
|
|
|
|
.ndo_start_xmit = stmmac_xmit,
|
|
|
|
.ndo_stop = stmmac_release,
|
|
|
|
.ndo_change_mtu = stmmac_change_mtu,
|
2011-04-09 09:46:55 +07:00
|
|
|
.ndo_fix_features = stmmac_fix_features,
|
2014-09-01 14:17:52 +07:00
|
|
|
.ndo_set_features = stmmac_set_features,
|
2011-08-16 13:29:00 +07:00
|
|
|
.ndo_set_rx_mode = stmmac_set_rx_mode,
|
2009-10-15 05:13:45 +07:00
|
|
|
.ndo_tx_timeout = stmmac_tx_timeout,
|
|
|
|
.ndo_do_ioctl = stmmac_ioctl,
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = stmmac_poll_controller,
|
|
|
|
#endif
|
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
};
|
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
/**
|
|
|
|
* stmmac_hw_init - Init the MAC device
|
2013-04-08 09:10:00 +07:00
|
|
|
* @priv: driver private structure
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: this function is to configure the MAC device according to
|
|
|
|
* some platform parameters or the HW capability register. It prepares the
|
|
|
|
* driver to use either ring or chain modes and to setup either enhanced or
|
|
|
|
* normal descriptors.
|
2012-02-15 07:10:39 +07:00
|
|
|
*/
|
|
|
|
static int stmmac_hw_init(struct stmmac_priv *priv)
|
|
|
|
{
|
|
|
|
struct mac_device_info *mac;
|
|
|
|
|
|
|
|
/* Identify the MAC HW device */
|
2012-04-04 05:13:01 +07:00
|
|
|
if (priv->plat->has_gmac) {
|
|
|
|
priv->dev->priv_flags |= IFF_UNICAST_FLT;
|
2014-08-01 03:49:17 +07:00
|
|
|
mac = dwmac1000_setup(priv->ioaddr,
|
|
|
|
priv->plat->multicast_filter_bins,
|
2016-04-01 16:37:27 +07:00
|
|
|
priv->plat->unicast_filter_entries,
|
|
|
|
&priv->synopsys_id);
|
2016-04-01 16:37:34 +07:00
|
|
|
} else if (priv->plat->has_gmac4) {
|
|
|
|
priv->dev->priv_flags |= IFF_UNICAST_FLT;
|
|
|
|
mac = dwmac4_setup(priv->ioaddr,
|
|
|
|
priv->plat->multicast_filter_bins,
|
|
|
|
priv->plat->unicast_filter_entries,
|
|
|
|
&priv->synopsys_id);
|
2012-04-04 05:13:01 +07:00
|
|
|
} else {
|
2016-04-01 16:37:27 +07:00
|
|
|
mac = dwmac100_setup(priv->ioaddr, &priv->synopsys_id);
|
2012-04-04 05:13:01 +07:00
|
|
|
}
|
2012-02-15 07:10:39 +07:00
|
|
|
if (!mac)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
priv->hw = mac;
|
|
|
|
|
2013-03-26 11:43:05 +07:00
|
|
|
/* To use the chained or ring mode */
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00) {
|
|
|
|
priv->hw->mode = &dwmac4_ring_mode_ops;
|
2013-03-26 11:43:05 +07:00
|
|
|
} else {
|
2016-04-01 16:37:34 +07:00
|
|
|
if (chain_mode) {
|
|
|
|
priv->hw->mode = &chain_mode_ops;
|
|
|
|
pr_info(" Chain mode enabled\n");
|
|
|
|
priv->mode = STMMAC_CHAIN_MODE;
|
|
|
|
} else {
|
|
|
|
priv->hw->mode = &ring_mode_ops;
|
|
|
|
pr_info(" Ring mode enabled\n");
|
|
|
|
priv->mode = STMMAC_RING_MODE;
|
|
|
|
}
|
2013-03-26 11:43:05 +07:00
|
|
|
}
|
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
/* Get the HW capability (new GMAC newer than 3.50a) */
|
|
|
|
priv->hw_cap_support = stmmac_get_hw_features(priv);
|
|
|
|
if (priv->hw_cap_support) {
|
|
|
|
pr_info(" DMA HW capability register supported");
|
|
|
|
|
|
|
|
/* We can override some gmac/dma configuration fields: e.g.
|
|
|
|
* enh_desc, tx_coe (e.g. that are passed through the
|
|
|
|
* platform) with the values from the HW capability
|
|
|
|
* register (if supported).
|
|
|
|
*/
|
|
|
|
priv->plat->enh_desc = priv->dma_cap.enh_desc;
|
|
|
|
priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up;
|
2016-06-24 20:16:25 +07:00
|
|
|
priv->hw->pmt = priv->plat->pmt;
|
2012-04-04 11:33:21 +07:00
|
|
|
|
2016-05-16 22:41:07 +07:00
|
|
|
/* TXCOE doesn't work in thresh DMA mode */
|
|
|
|
if (priv->plat->force_thresh_dma_mode)
|
|
|
|
priv->plat->tx_coe = 0;
|
|
|
|
else
|
|
|
|
priv->plat->tx_coe = priv->dma_cap.tx_coe;
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/* In case of GMAC4 rx_coe is from HW cap register. */
|
|
|
|
priv->plat->rx_coe = priv->dma_cap.rx_coe;
|
2012-04-04 11:33:21 +07:00
|
|
|
|
|
|
|
if (priv->dma_cap.rx_coe_type2)
|
|
|
|
priv->plat->rx_coe = STMMAC_RX_COE_TYPE2;
|
|
|
|
else if (priv->dma_cap.rx_coe_type1)
|
|
|
|
priv->plat->rx_coe = STMMAC_RX_COE_TYPE1;
|
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
} else
|
|
|
|
pr_info(" No HW DMA feature register supported");
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
/* To use alternate (extended), normal or GMAC4 descriptor structures */
|
|
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00)
|
|
|
|
priv->hw->desc = &dwmac4_desc_ops;
|
|
|
|
else
|
|
|
|
stmmac_selec_desc_mode(priv);
|
2013-06-28 14:35:32 +07:00
|
|
|
|
2014-09-01 14:17:52 +07:00
|
|
|
if (priv->plat->rx_coe) {
|
|
|
|
priv->hw->rx_csum = priv->plat->rx_coe;
|
2016-04-01 16:37:34 +07:00
|
|
|
pr_info(" RX Checksum Offload Engine supported\n");
|
|
|
|
if (priv->synopsys_id < DWMAC_CORE_4_00)
|
|
|
|
pr_info("\tCOE Type %d\n", priv->hw->rx_csum);
|
2014-09-01 14:17:52 +07:00
|
|
|
}
|
2012-02-15 07:10:39 +07:00
|
|
|
if (priv->plat->tx_coe)
|
|
|
|
pr_info(" TX Checksum insertion supported\n");
|
|
|
|
|
|
|
|
if (priv->plat->pmt) {
|
|
|
|
pr_info(" Wake-Up On Lan supported\n");
|
|
|
|
device_set_wakeup_capable(priv->device, 1);
|
|
|
|
}
|
|
|
|
|
2016-04-01 16:37:34 +07:00
|
|
|
if (priv->dma_cap.tsoen)
|
|
|
|
pr_info(" TSO supported\n");
|
|
|
|
|
2013-03-26 11:43:06 +07:00
|
|
|
return 0;
|
2012-02-15 07:10:39 +07:00
|
|
|
}
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
/**
|
2011-12-21 10:58:19 +07:00
|
|
|
* stmmac_dvr_probe
|
|
|
|
* @device: device pointer
|
2012-06-05 02:22:55 +07:00
|
|
|
* @plat_dat: platform data pointer
|
2015-05-21 01:03:07 +07:00
|
|
|
* @res: stmmac resource pointer
|
2011-12-21 10:58:19 +07:00
|
|
|
* Description: this is the main probe function used to
|
|
|
|
* call the alloc_etherdev, allocate the priv structure.
|
2015-01-27 23:38:03 +07:00
|
|
|
* Return:
|
2015-05-21 01:03:08 +07:00
|
|
|
* returns 0 on success, otherwise errno.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
2015-05-21 01:03:08 +07:00
|
|
|
int stmmac_dvr_probe(struct device *device,
|
|
|
|
struct plat_stmmacenet_data *plat_dat,
|
|
|
|
struct stmmac_resources *res)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2011-12-21 10:58:19 +07:00
|
|
|
struct net_device *ndev = NULL;
|
|
|
|
struct stmmac_priv *priv;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2011-12-21 10:58:19 +07:00
|
|
|
ndev = alloc_etherdev(sizeof(struct stmmac_priv));
|
2012-01-29 20:47:52 +07:00
|
|
|
if (!ndev)
|
2015-05-21 01:03:08 +07:00
|
|
|
return -ENOMEM;
|
2011-12-21 10:58:19 +07:00
|
|
|
|
|
|
|
SET_NETDEV_DEV(ndev, device);
|
|
|
|
|
|
|
|
priv = netdev_priv(ndev);
|
|
|
|
priv->device = device;
|
|
|
|
priv->dev = ndev;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2011-12-21 10:58:19 +07:00
|
|
|
stmmac_set_ethtool_ops(ndev);
|
2012-02-15 07:10:39 +07:00
|
|
|
priv->pause = pause;
|
|
|
|
priv->plat = plat_dat;
|
2015-05-21 01:03:07 +07:00
|
|
|
priv->ioaddr = res->addr;
|
|
|
|
priv->dev->base_addr = (unsigned long)res->addr;
|
|
|
|
|
|
|
|
priv->dev->irq = res->irq;
|
|
|
|
priv->wol_irq = res->wol_irq;
|
|
|
|
priv->lpi_irq = res->lpi_irq;
|
|
|
|
|
|
|
|
if (res->mac)
|
|
|
|
memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN);
|
2012-02-15 07:10:39 +07:00
|
|
|
|
2015-07-18 04:48:17 +07:00
|
|
|
dev_set_drvdata(device, priv->dev);
|
2015-05-21 01:03:06 +07:00
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
/* Verify driver arguments */
|
|
|
|
stmmac_verify_args();
|
2011-12-21 10:58:19 +07:00
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
/* Override with kernel parameters if supplied XXX CRS XXX
|
2013-04-08 09:10:01 +07:00
|
|
|
* this needs to have multiple instances
|
|
|
|
*/
|
2012-02-15 07:10:39 +07:00
|
|
|
if ((phyaddr >= 0) && (phyaddr <= 31))
|
|
|
|
priv->plat->phy_addr = phyaddr;
|
|
|
|
|
2014-01-17 20:24:40 +07:00
|
|
|
priv->stmmac_clk = devm_clk_get(priv->device, STMMAC_RESOURCE_NAME);
|
|
|
|
if (IS_ERR(priv->stmmac_clk)) {
|
|
|
|
dev_warn(priv->device, "%s: warning: cannot get CSR clock\n",
|
|
|
|
__func__);
|
2014-09-26 20:42:55 +07:00
|
|
|
/* If failed to obtain stmmac_clk and specific clk_csr value
|
|
|
|
* is NOT passed from the platform, probe fail.
|
|
|
|
*/
|
|
|
|
if (!priv->plat->clk_csr) {
|
|
|
|
ret = PTR_ERR(priv->stmmac_clk);
|
|
|
|
goto error_clk_get;
|
|
|
|
} else {
|
|
|
|
priv->stmmac_clk = NULL;
|
|
|
|
}
|
2014-01-17 20:24:40 +07:00
|
|
|
}
|
|
|
|
clk_prepare_enable(priv->stmmac_clk);
|
|
|
|
|
2015-04-08 03:38:45 +07:00
|
|
|
priv->pclk = devm_clk_get(priv->device, "pclk");
|
|
|
|
if (IS_ERR(priv->pclk)) {
|
|
|
|
if (PTR_ERR(priv->pclk) == -EPROBE_DEFER) {
|
|
|
|
ret = -EPROBE_DEFER;
|
|
|
|
goto error_pclk_get;
|
|
|
|
}
|
|
|
|
priv->pclk = NULL;
|
|
|
|
}
|
|
|
|
clk_prepare_enable(priv->pclk);
|
|
|
|
|
2014-01-17 20:24:41 +07:00
|
|
|
priv->stmmac_rst = devm_reset_control_get(priv->device,
|
|
|
|
STMMAC_RESOURCE_NAME);
|
|
|
|
if (IS_ERR(priv->stmmac_rst)) {
|
|
|
|
if (PTR_ERR(priv->stmmac_rst) == -EPROBE_DEFER) {
|
|
|
|
ret = -EPROBE_DEFER;
|
|
|
|
goto error_hw_init;
|
|
|
|
}
|
|
|
|
dev_info(priv->device, "no reset control found\n");
|
|
|
|
priv->stmmac_rst = NULL;
|
|
|
|
}
|
|
|
|
if (priv->stmmac_rst)
|
|
|
|
reset_control_deassert(priv->stmmac_rst);
|
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
/* Init MAC and get the capabilities */
|
2013-03-26 11:43:06 +07:00
|
|
|
ret = stmmac_hw_init(priv);
|
|
|
|
if (ret)
|
2014-01-17 20:24:40 +07:00
|
|
|
goto error_hw_init;
|
2012-02-15 07:10:39 +07:00
|
|
|
|
|
|
|
ndev->netdev_ops = &stmmac_netdev_ops;
|
2011-12-21 10:58:19 +07:00
|
|
|
|
2012-02-15 07:10:39 +07:00
|
|
|
ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
|
|
|
|
NETIF_F_RXCSUM;
|
2016-04-01 16:37:34 +07:00
|
|
|
|
|
|
|
if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) {
|
|
|
|
ndev->hw_features |= NETIF_F_TSO;
|
|
|
|
priv->tso = true;
|
|
|
|
pr_info(" TSO feature enabled\n");
|
|
|
|
}
|
2011-12-21 10:58:19 +07:00
|
|
|
ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
|
|
|
|
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
|
2009-10-15 05:13:45 +07:00
|
|
|
#ifdef STMMAC_VLAN_TAG_USED
|
|
|
|
/* Both mac100 and gmac support receive VLAN tag detection */
|
2013-04-19 09:04:27 +07:00
|
|
|
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
|
2009-10-15 05:13:45 +07:00
|
|
|
#endif
|
|
|
|
priv->msg_enable = netif_msg_init(debug, default_msg_level);
|
|
|
|
|
|
|
|
if (flow_ctrl)
|
|
|
|
priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */
|
|
|
|
|
2012-11-26 06:10:43 +07:00
|
|
|
/* Rx Watchdog is available in the COREs newer than the 3.40.
|
|
|
|
* In some case, for example on bugged HW this feature
|
|
|
|
* has to be disable and this can be done by passing the
|
|
|
|
* riwt_off field from the platform.
|
|
|
|
*/
|
|
|
|
if ((priv->synopsys_id >= DWMAC_CORE_3_50) && (!priv->plat->riwt_off)) {
|
|
|
|
priv->use_riwt = 1;
|
|
|
|
pr_info(" Enable RX Mitigation via HW Watchdog Timer\n");
|
|
|
|
}
|
|
|
|
|
2011-12-21 10:58:19 +07:00
|
|
|
netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2010-11-30 05:52:52 +07:00
|
|
|
spin_lock_init(&priv->lock);
|
2011-10-18 07:01:19 +07:00
|
|
|
spin_lock_init(&priv->tx_lock);
|
2010-11-30 05:52:52 +07:00
|
|
|
|
2011-12-21 10:58:19 +07:00
|
|
|
ret = register_netdev(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
if (ret) {
|
2012-02-15 07:10:39 +07:00
|
|
|
pr_err("%s: ERROR %i registering the device\n", __func__, ret);
|
2012-07-31 04:39:41 +07:00
|
|
|
goto error_netdev_register;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
2012-04-04 11:33:27 +07:00
|
|
|
/* If a specific clk_csr value is passed from the platform
|
|
|
|
* this means that the CSR Clock Range selection cannot be
|
|
|
|
* changed at run-time and it is fixed. Viceversa the driver'll try to
|
|
|
|
* set the MDC clock dynamically according to the csr actual
|
|
|
|
* clock input.
|
|
|
|
*/
|
|
|
|
if (!priv->plat->clk_csr)
|
|
|
|
stmmac_clk_csr_set(priv);
|
|
|
|
else
|
|
|
|
priv->clk_csr = priv->plat->clk_csr;
|
|
|
|
|
2013-03-26 11:43:08 +07:00
|
|
|
stmmac_check_pcs_mode(priv);
|
|
|
|
|
2016-06-24 20:16:25 +07:00
|
|
|
if (priv->hw->pcs != STMMAC_PCS_RGMII &&
|
|
|
|
priv->hw->pcs != STMMAC_PCS_TBI &&
|
|
|
|
priv->hw->pcs != STMMAC_PCS_RTBI) {
|
2013-03-26 11:43:08 +07:00
|
|
|
/* MDIO bus Registration */
|
|
|
|
ret = stmmac_mdio_register(ndev);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_debug("%s: MDIO bus (id: %d) registration failed",
|
|
|
|
__func__, priv->plat->bus_id);
|
|
|
|
goto error_mdio_register;
|
|
|
|
}
|
2012-04-19 02:48:20 +07:00
|
|
|
}
|
|
|
|
|
2015-05-21 01:03:08 +07:00
|
|
|
return 0;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2012-07-31 04:39:41 +07:00
|
|
|
error_mdio_register:
|
2010-12-21 04:34:56 +07:00
|
|
|
unregister_netdev(ndev);
|
2012-07-31 04:39:41 +07:00
|
|
|
error_netdev_register:
|
|
|
|
netif_napi_del(&priv->napi);
|
2014-01-17 20:24:40 +07:00
|
|
|
error_hw_init:
|
2015-04-08 03:38:45 +07:00
|
|
|
clk_disable_unprepare(priv->pclk);
|
|
|
|
error_pclk_get:
|
2014-01-17 20:24:40 +07:00
|
|
|
clk_disable_unprepare(priv->stmmac_clk);
|
|
|
|
error_clk_get:
|
2010-12-21 04:34:56 +07:00
|
|
|
free_netdev(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2015-05-21 01:03:08 +07:00
|
|
|
return ret;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
2014-11-10 17:38:59 +07:00
|
|
|
EXPORT_SYMBOL_GPL(stmmac_dvr_probe);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* stmmac_dvr_remove
|
2016-05-02 03:58:19 +07:00
|
|
|
* @dev: device pointer
|
2009-10-15 05:13:45 +07:00
|
|
|
* Description: this function resets the TX/RX processes, disables the MAC RX/TX
|
2011-12-21 10:58:19 +07:00
|
|
|
* changes the link status, releases the DMA descriptor rings.
|
2009-10-15 05:13:45 +07:00
|
|
|
*/
|
2016-05-02 03:58:19 +07:00
|
|
|
int stmmac_dvr_remove(struct device *dev)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
2016-05-02 03:58:19 +07:00
|
|
|
struct net_device *ndev = dev_get_drvdata(dev);
|
2010-01-07 06:07:18 +07:00
|
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
pr_info("%s:\n\tremoving driver", __func__);
|
|
|
|
|
2010-08-24 03:40:42 +07:00
|
|
|
priv->hw->dma->stop_rx(priv->ioaddr);
|
|
|
|
priv->hw->dma->stop_tx(priv->ioaddr);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2011-12-21 10:58:19 +07:00
|
|
|
stmmac_set_mac(priv->ioaddr, false);
|
2009-10-15 05:13:45 +07:00
|
|
|
netif_carrier_off(ndev);
|
|
|
|
unregister_netdev(ndev);
|
2016-08-01 14:02:42 +07:00
|
|
|
of_node_put(priv->plat->phy_node);
|
2014-01-17 20:24:41 +07:00
|
|
|
if (priv->stmmac_rst)
|
|
|
|
reset_control_assert(priv->stmmac_rst);
|
2015-04-08 03:38:45 +07:00
|
|
|
clk_disable_unprepare(priv->pclk);
|
2014-01-17 20:24:40 +07:00
|
|
|
clk_disable_unprepare(priv->stmmac_clk);
|
2016-06-24 20:16:25 +07:00
|
|
|
if (priv->hw->pcs != STMMAC_PCS_RGMII &&
|
|
|
|
priv->hw->pcs != STMMAC_PCS_TBI &&
|
|
|
|
priv->hw->pcs != STMMAC_PCS_RTBI)
|
2015-04-16 23:56:03 +07:00
|
|
|
stmmac_mdio_unregister(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
free_netdev(ndev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-11-10 17:38:59 +07:00
|
|
|
EXPORT_SYMBOL_GPL(stmmac_dvr_remove);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-11-18 15:47:01 +07:00
|
|
|
/**
|
|
|
|
* stmmac_suspend - suspend callback
|
2016-05-02 03:58:19 +07:00
|
|
|
* @dev: device pointer
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: this is the function to suspend the device and it is called
|
|
|
|
* by the platform driver to stop the network queue, release the resources,
|
|
|
|
* program the PMT register (for WoL), clean and release driver resources.
|
|
|
|
*/
|
2016-05-02 03:58:19 +07:00
|
|
|
int stmmac_suspend(struct device *dev)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
2016-05-02 03:58:19 +07:00
|
|
|
struct net_device *ndev = dev_get_drvdata(dev);
|
2010-11-24 09:38:11 +07:00
|
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
2012-05-14 05:18:43 +07:00
|
|
|
unsigned long flags;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
if (!ndev || !netif_running(ndev))
|
2009-10-15 05:13:45 +07:00
|
|
|
return 0;
|
|
|
|
|
2011-11-17 04:58:02 +07:00
|
|
|
if (priv->phydev)
|
|
|
|
phy_stop(priv->phydev);
|
|
|
|
|
2012-05-14 05:18:43 +07:00
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
netif_device_detach(ndev);
|
|
|
|
netif_stop_queue(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
napi_disable(&priv->napi);
|
|
|
|
|
|
|
|
/* Stop TX/RX DMA */
|
|
|
|
priv->hw->dma->stop_tx(priv->ioaddr);
|
|
|
|
priv->hw->dma->stop_rx(priv->ioaddr);
|
2013-03-26 11:43:06 +07:00
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
/* Enable Power down mode by programming the PMT regs */
|
2014-01-16 17:53:00 +07:00
|
|
|
if (device_may_wakeup(priv->device)) {
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->pmt(priv->hw, priv->wolopts);
|
2014-01-16 17:53:00 +07:00
|
|
|
priv->irq_wake = 1;
|
|
|
|
} else {
|
2011-12-21 10:58:19 +07:00
|
|
|
stmmac_set_mac(priv->ioaddr, false);
|
2014-01-16 17:52:52 +07:00
|
|
|
pinctrl_pm_select_sleep_state(priv->device);
|
2012-04-04 11:33:25 +07:00
|
|
|
/* Disable clock in case of PWM is off */
|
2015-04-08 03:38:45 +07:00
|
|
|
clk_disable(priv->pclk);
|
2014-11-04 23:08:09 +07:00
|
|
|
clk_disable(priv->stmmac_clk);
|
2012-04-04 11:33:25 +07:00
|
|
|
}
|
2012-05-14 05:18:43 +07:00
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
2014-07-29 02:07:58 +07:00
|
|
|
|
|
|
|
priv->oldlink = 0;
|
|
|
|
priv->speed = 0;
|
|
|
|
priv->oldduplex = -1;
|
2009-10-15 05:13:45 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-10 17:38:59 +07:00
|
|
|
EXPORT_SYMBOL_GPL(stmmac_suspend);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2014-11-18 15:47:01 +07:00
|
|
|
/**
|
|
|
|
* stmmac_resume - resume callback
|
2016-05-02 03:58:19 +07:00
|
|
|
* @dev: device pointer
|
2014-11-18 15:47:01 +07:00
|
|
|
* Description: when resume this function is invoked to setup the DMA and CORE
|
|
|
|
* in a usable state.
|
|
|
|
*/
|
2016-05-02 03:58:19 +07:00
|
|
|
int stmmac_resume(struct device *dev)
|
2009-10-15 05:13:45 +07:00
|
|
|
{
|
2016-05-02 03:58:19 +07:00
|
|
|
struct net_device *ndev = dev_get_drvdata(dev);
|
2010-11-24 09:38:11 +07:00
|
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
2012-05-14 05:18:43 +07:00
|
|
|
unsigned long flags;
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
if (!netif_running(ndev))
|
2009-10-15 05:13:45 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Power Down bit, into the PM register, is cleared
|
|
|
|
* automatically as soon as a magic packet or a Wake-up frame
|
|
|
|
* is received. Anyway, it's better to manually clear
|
|
|
|
* this bit because it can generate problems while resuming
|
2013-04-08 09:10:01 +07:00
|
|
|
* from another devices (e.g. serial console).
|
|
|
|
*/
|
2014-01-16 17:52:35 +07:00
|
|
|
if (device_may_wakeup(priv->device)) {
|
2016-06-01 22:53:48 +07:00
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
2014-08-01 03:49:13 +07:00
|
|
|
priv->hw->mac->pmt(priv->hw, 0);
|
2016-06-01 22:53:48 +07:00
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
2014-01-16 17:53:00 +07:00
|
|
|
priv->irq_wake = 0;
|
2014-01-16 17:52:35 +07:00
|
|
|
} else {
|
2014-01-16 17:52:52 +07:00
|
|
|
pinctrl_pm_select_default_state(priv->device);
|
2012-04-04 11:33:25 +07:00
|
|
|
/* enable the clk prevously disabled */
|
2014-11-04 23:08:09 +07:00
|
|
|
clk_enable(priv->stmmac_clk);
|
2015-04-08 03:38:45 +07:00
|
|
|
clk_enable(priv->pclk);
|
2014-01-16 17:52:35 +07:00
|
|
|
/* reset the phy so that it's ready */
|
|
|
|
if (priv->mii)
|
|
|
|
stmmac_mdio_reset(priv->mii);
|
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
netif_device_attach(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2016-06-01 22:53:48 +07:00
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
|
|
|
2015-12-04 13:21:06 +07:00
|
|
|
priv->cur_rx = 0;
|
|
|
|
priv->dirty_rx = 0;
|
|
|
|
priv->dirty_tx = 0;
|
|
|
|
priv->cur_tx = 0;
|
2016-04-01 16:37:34 +07:00
|
|
|
/* reset private mss value to force mss context settings at
|
|
|
|
* next tso xmit (only used for gmac4).
|
|
|
|
*/
|
|
|
|
priv->mss = 0;
|
|
|
|
|
2015-12-04 13:21:06 +07:00
|
|
|
stmmac_clear_descriptors(priv);
|
|
|
|
|
2014-12-19 21:38:18 +07:00
|
|
|
stmmac_hw_setup(ndev, false);
|
2014-11-04 23:08:09 +07:00
|
|
|
stmmac_init_tx_coalesce(priv);
|
2015-11-26 14:35:41 +07:00
|
|
|
stmmac_set_rx_mode(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
|
|
|
napi_enable(&priv->napi);
|
|
|
|
|
2010-11-24 09:38:11 +07:00
|
|
|
netif_start_queue(ndev);
|
2009-10-15 05:13:45 +07:00
|
|
|
|
2012-05-14 05:18:43 +07:00
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
2011-11-17 04:58:02 +07:00
|
|
|
|
|
|
|
if (priv->phydev)
|
|
|
|
phy_start(priv->phydev);
|
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-10 17:38:59 +07:00
|
|
|
EXPORT_SYMBOL_GPL(stmmac_resume);
|
2012-06-05 02:22:57 +07:00
|
|
|
|
2009-10-15 05:13:45 +07:00
|
|
|
#ifndef MODULE
|
|
|
|
static int __init stmmac_cmdline_opt(char *str)
|
|
|
|
{
|
|
|
|
char *opt;
|
|
|
|
|
|
|
|
if (!str || !*str)
|
|
|
|
return -EINVAL;
|
|
|
|
while ((opt = strsep(&str, ",")) != NULL) {
|
2011-07-20 07:05:22 +07:00
|
|
|
if (!strncmp(opt, "debug:", 6)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 6, 0, &debug))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
|
|
|
} else if (!strncmp(opt, "phyaddr:", 8)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 8, 0, &phyaddr))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
|
|
|
} else if (!strncmp(opt, "buf_sz:", 7)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 7, 0, &buf_sz))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
|
|
|
} else if (!strncmp(opt, "tc:", 3)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 3, 0, &tc))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
|
|
|
} else if (!strncmp(opt, "watchdog:", 9)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 9, 0, &watchdog))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
|
|
|
} else if (!strncmp(opt, "flow_ctrl:", 10)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 10, 0, &flow_ctrl))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
|
|
|
} else if (!strncmp(opt, "pause:", 6)) {
|
2012-06-28 04:14:35 +07:00
|
|
|
if (kstrtoint(opt + 6, 0, &pause))
|
2011-07-20 07:05:22 +07:00
|
|
|
goto err;
|
2013-02-15 06:00:13 +07:00
|
|
|
} else if (!strncmp(opt, "eee_timer:", 10)) {
|
2012-06-28 04:14:37 +07:00
|
|
|
if (kstrtoint(opt + 10, 0, &eee_timer))
|
|
|
|
goto err;
|
2013-03-26 11:43:05 +07:00
|
|
|
} else if (!strncmp(opt, "chain_mode:", 11)) {
|
|
|
|
if (kstrtoint(opt + 11, 0, &chain_mode))
|
|
|
|
goto err;
|
2011-07-20 07:05:22 +07:00
|
|
|
}
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
return 0;
|
2011-07-20 07:05:22 +07:00
|
|
|
|
|
|
|
err:
|
|
|
|
pr_err("%s: ERROR broken module parameter conversion", __func__);
|
|
|
|
return -EINVAL;
|
2009-10-15 05:13:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
__setup("stmmaceth=", stmmac_cmdline_opt);
|
2013-04-08 09:10:01 +07:00
|
|
|
#endif /* MODULE */
|
2011-12-24 02:21:20 +07:00
|
|
|
|
2015-05-23 09:03:29 +07:00
|
|
|
static int __init stmmac_init(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
/* Create debugfs main directory if it doesn't exist yet */
|
|
|
|
if (!stmmac_fs_dir) {
|
|
|
|
stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
|
|
|
|
|
|
if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
|
|
pr_err("ERROR %s, debugfs create directory failed\n",
|
|
|
|
STMMAC_RESOURCE_NAME);
|
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit stmmac_exit(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
debugfs_remove_recursive(stmmac_fs_dir);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(stmmac_init)
|
|
|
|
module_exit(stmmac_exit)
|
|
|
|
|
2011-12-24 02:21:20 +07:00
|
|
|
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver");
|
|
|
|
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
|
|
|
MODULE_LICENSE("GPL");
|